<commit_before>import * as _ from "lodash";
import { DOM } from "aurelia-pal";
import { computedFrom, bindingMode, BindingEngine } from "aurelia-binding";
import { customElement, bindable } from "aurelia-templating";
import { autoinject } from "aurelia-dependency-injection";
import { Subscription } from "aurelia-event-aggregator";
import { Dictionary, KeyCode } from "@ssv/core";
import { LoggerFactory, ILog } from "@ssv/au-core";

import { attributeUtil, ElementFocusedController } from "../core/index";
import { SelectType, selectType, supportedSelectTypes, SelectItem, SelectGroup } from "./select.model";
import { selectConfig, SelectConfig } from "./select.config";

const PREFIX = "ssv-select";

@autoinject()
@customElement(PREFIX)
export class SelectElement {
	static id = 0;

	@bindable color: string;
	@bindable placeholder: string;
	@bindable selectedClass: string | undefined;
	@bindable autoClose: boolean;
	@bindable allowClear: boolean;
	@bindable allowFiltering: boolean;
	@bindable filterPlaceholder: string;
	@bindable type: SelectType;
	@bindable modifier: string | undefined;
	@bindable disabled: boolean | string = false;

	@bindable({
		defaultBindingMode: bindingMode.twoWay
	}) selected: any | undefined;
	@bindable options: any[] = [];
	@bindable text: string;
	@bindable value: string;
	@bindable groupby: string | undefined;

	controlId: string;
	modifiers: string | undefined;
	labelModifierClass: string | undefined;
	isOpen = false;
	arrowUpIcon: string;
	arrowDownIcon: string;
	clearIcon: string;
	filterBy: string;
	filteredGroupOptions: SelectGroup[] = [];
	noOptionsAvailableText: string;
	focusValue: string;

	@computedFrom("isOpen", "selectedItems")
	get isActive(): boolean {
		return this.isOpen || this.selectedItems.length > 0;
	}

	private logger: ILog;
	private config: SelectConfig;
	private items: SelectItem[];
	private selectedItems: SelectItem[] = [];
	private flattenedFilteredGroupOptions: SelectItem[];
	private optionsMap: Dictionary<object> = {};
	private isComplexList: boolean;

	private focusedController: ElementFocusedController;
	private focus$$: Subscription;
	private input$$: Subscription;

	constructor(
		private element: Element,
		private bindingEngine: BindingEngine,
		loggerFactory: LoggerFactory,
	) {
		this.logger = loggerFactory.get("selectElement");
		this.controlId = `${PREFIX}-${SelectElement.id++}`;
		this.focusedController = new ElementFocusedController(PREFIX, element);
		const input$ = this.bindingEngine.propertyObserver(this, "input");
		this.input$$ = input$.subscribe((inputElement: HTMLInputElement) => {
			if (!inputElement) {
				return;
			}
			inputElement.focus();
		});
	}

	bind() {
		this.presetBooleanTypes();
		this.setDefaults();
		this.modifiers = attributeUtil.generateBemStyleModifiers(this.modifier, PREFIX);
		this.selectedClass = attributeUtil.generateBemStyleModifiers(this.config.selectedClass, `${PREFIX}__item`);

		const type = this.config.type.toLowerCase();
		this.element.classList.add(`${PREFIX}--${type}`);
		this.labelModifierClass = attributeUtil.generateBemStyleModifiers(type, `${PREFIX}__label`);

		if (this.config.color) {
			this.element.classList.add(`${PREFIX}--${this.config.color.toLowerCase()}`);
		}
	}

	attached() {
		this.focusedController.init();
		this.focus$$ = this.focusedController.onFocus(() => this.toggle());
		this.element.addEventListener("keydown", this.onFocusedKeyPress.bind(this));
		DOM.addEventListener("click", this.onBodyClick.bind(this), true);
	}

	detached() {
		this.focusedController.destroy();
		this.focus$$.dispose();
		this.input$$.dispose();
		this.element.removeEventListener("keydown", this.onFocusedKeyPress);
		DOM.removeEventListener("click", this.onBodyClick, true);
	}

	selectedChanged(value: any) {
		this.onSelectedChanged(value);
	}

	optionsChanged(options: any[]) {
		this.onOptionsChanged(options);
	}

	disabledChanged(newValue: boolean) {
		if (this.disabled) {
			this.isOpen = false;
		}
		attributeUtil.setAsFlag(this.element, "disabled", newValue);
	}

	modifierChanged(newValue: string | undefined) {
		this.modifiers = attributeUtil.generateBemStyleModifiers(newValue, PREFIX);
	}

	filterOptions(searchTerm: string) {
		let filteredOptions = this.items;
		if (!searchTerm) {
			this.groupedOptions(filteredOptions);
			return;
		}

		filteredOptions = _.filter(this.items, item => {
			return _.includes(item.text.toLowerCase(), searchTerm.toLowerCase());
		});
		this.groupedOptions(filteredOptions);
	}

	onClear() {
		this.filterBy = "";
		this.isOpen = false;
		this.selected = undefined;
		this.selectedItems = [];
		this.groupedOptions(this.items);

		for (let item of this.items) {
			item.isSelected = false;
		}
	}

	onDeselect(optionValue: string) {
		this.clearMultiSelectionItem(optionValue);
	}

	toggle() {
		if (this.disabled) {
			return;
		}
		this.isOpen = !this.isOpen;
		if (this.isOpen) {
			this.setFocusValue();
		}
	}

	onBodyClick(event: Event): void {
		if (!this.isOpen && (event.target === this.element
			|| this.element.contains(event.target as Node))) {
			return;
		}
		this.isOpen = false;<commit_msg>select: fixed issue with on body click<commit_after>import * as _ from "lodash";
import { DOM } from "aurelia-pal";
import { computedFrom, bindingMode, BindingEngine } from "aurelia-binding";
import { customElement, bindable } from "aurelia-templating";
import { autoinject } from "aurelia-dependency-injection";
import { Subscription } from "aurelia-event-aggregator";
import { Dictionary, KeyCode } from "@ssv/core";
import { LoggerFactory, ILog } from "@ssv/au-core";

import { attributeUtil, ElementFocusedController } from "../core/index";
import { SelectType, selectType, supportedSelectTypes, SelectItem, SelectGroup } from "./select.model";
import { selectConfig, SelectConfig } from "./select.config";

const PREFIX = "ssv-select";

@autoinject()
@customElement(PREFIX)
export class SelectElement {
	static id = 0;

	@bindable color: string;
	@bindable placeholder: string;
	@bindable selectedClass: string | undefined;
	@bindable autoClose: boolean;
	@bindable allowClear: boolean;
	@bindable allowFiltering: boolean;
	@bindable filterPlaceholder: string;
	@bindable type: SelectType;
	@bindable modifier: string | undefined;
	@bindable disabled: boolean | string = false;

	@bindable({
		defaultBindingMode: bindingMode.twoWay
	}) selected: any | undefined;
	@bindable options: any[] = [];
	@bindable text: string;
	@bindable value: string;
	@bindable groupby: string | undefined;

	controlId: string;
	modifiers: string | undefined;
	labelModifierClass: string | undefined;
	isOpen = false;
	arrowUpIcon: string;
	arrowDownIcon: string;
	clearIcon: string;
	filterBy: string;
	filteredGroupOptions: SelectGroup[] = [];
	noOptionsAvailableText: string;
	focusValue: string;

	@computedFrom("isOpen", "selectedItems")
	get isActive(): boolean {
		return this.isOpen || this.selectedItems.length > 0;
	}

	private logger: ILog;
	private config: SelectConfig;
	private items: SelectItem[];
	private selectedItems: SelectItem[] = [];
	private flattenedFilteredGroupOptions: SelectItem[];
	private optionsMap: Dictionary<object> = {};
	private isComplexList: boolean;

	private focusedController: ElementFocusedController;
	private focus$$: Subscription;
	private input$$: Subscription;

	constructor(
		private element: Element,
		private bindingEngine: BindingEngine,
		loggerFactory: LoggerFactory,
	) {
		this.logger = loggerFactory.get("selectElement");
		this.controlId = `${PREFIX}-${SelectElement.id++}`;
		this.focusedController = new ElementFocusedController(PREFIX, element);
		const input$ = this.bindingEngine.propertyObserver(this, "input");
		this.input$$ = input$.subscribe((inputElement: HTMLInputElement) => {
			if (!inputElement) {
				return;
			}
			inputElement.focus();
		});
	}

	bind() {
		this.presetBooleanTypes();
		this.setDefaults();
		this.modifiers = attributeUtil.generateBemStyleModifiers(this.modifier, PREFIX);
		this.selectedClass = attributeUtil.generateBemStyleModifiers(this.config.selectedClass, `${PREFIX}__item`);

		const type = this.config.type.toLowerCase();
		this.element.classList.add(`${PREFIX}--${type}`);
		this.labelModifierClass = attributeUtil.generateBemStyleModifiers(type, `${PREFIX}__label`);

		if (this.config.color) {
			this.element.classList.add(`${PREFIX}--${this.config.color.toLowerCase()}`);
		}
	}

	attached() {
		this.focusedController.init();
		this.focus$$ = this.focusedController.onFocus(() => this.toggle());
		this.element.addEventListener("keydown", this.onFocusedKeyPress.bind(this));
		DOM.addEventListener("click", this.onBodyClick.bind(this), true);
	}

	detached() {
		this.focusedController.destroy();
		this.focus$$.dispose();
		this.input$$.dispose();
		this.element.removeEventListener("keydown", this.onFocusedKeyPress);
		DOM.removeEventListener("click", this.onBodyClick, true);
	}

	selectedChanged(value: any) {
		this.onSelectedChanged(value);
	}

	optionsChanged(options: any[]) {
		this.onOptionsChanged(options);
	}

	disabledChanged(newValue: boolean) {
		if (this.disabled) {
			this.isOpen = false;
		}
		attributeUtil.setAsFlag(this.element, "disabled", newValue);
	}

	modifierChanged(newValue: string | undefined) {
		this.modifiers = attributeUtil.generateBemStyleModifiers(newValue, PREFIX);
	}

	filterOptions(searchTerm: string) {
		let filteredOptions = this.items;
		if (!searchTerm) {
			this.groupedOptions(filteredOptions);
			return;
		}

		filteredOptions = _.filter(this.items, item => {
			return _.includes(item.text.toLowerCase(), searchTerm.toLowerCase());
		});
		this.groupedOptions(filteredOptions);
	}

	onClear() {
		this.filterBy = "";
		this.isOpen = false;
		this.selected = undefined;
		this.selectedItems = [];
		this.groupedOptions(this.items);

		for (let item of this.items) {
			item.isSelected = false;
		}
	}

	onDeselect(optionValue: string) {
		this.clearMultiSelectionItem(optionValue);
	}

	toggle() {
		if (this.disabled) {
			return;
		}
		this.isOpen = !this.isOpen;
		if (this.isOpen) {
			this.setFocusValue();
		}
	}

	onBodyClick(event: Event): void {
		if (!this.isOpen || event.target === this.element
			|| this.element.contains(event.target as Node)) {
			return;
		}
		this.isOpen = false;
{'fraction_non_alphanumeric': 0.1383776414451261, 'fraction_numerical': 0.00038952186191449994, 'mean_word_length': 5.205438066465256, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2618557', 'n_tokens_mistral': 3421, 'n_tokens_neox': 3195, 'n_words': 655}