const { get, isNone, run, isEmpty, makeArray } = Ember; import computed from "ember-addons/ember-computed-decorators"; import UtilsMixin from "select-kit/mixins/utils"; import DomHelpersMixin from "select-kit/mixins/dom-helpers"; import EventsMixin from "select-kit/mixins/events"; import PluginApiMixin from "select-kit/mixins/plugin-api"; import { applyContentPluginApiCallbacks, applyHeaderContentPluginApiCallbacks, applyCollectionHeaderCallbacks } from "select-kit/mixins/plugin-api"; export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixin, EventsMixin, { pluginApiIdentifiers: ["select-kit"], layoutName: "select-kit/templates/components/select-kit", classNames: ["select-kit"], classNameBindings: [ "isLoading", "isFocused", "isExpanded", "isDisabled", "isHidden", "isAbove", "isBelow", "isLeftAligned", "isRightAligned", "hasSelection", "hasReachedLimit", ], isDisabled: false, isExpanded: false, isFocused: false, isHidden: false, isLoading: false, isAsync: false, renderedBodyOnce: false, renderedFilterOnce: false, tabindex: 0, none: null, highlighted: null, valueAttribute: "id", nameProperty: "name", autoFilterable: false, filterable: false, filter: "", previousFilter: "", filterPlaceholder: "select_kit.filter_placeholder", filterIcon: "search", headerIcon: null, rowComponent: "select-kit/select-kit-row", rowComponentOptions: null, noneRowComponent: "select-kit/select-kit-none-row", createRowComponent: "select-kit/select-kit-create-row", filterComponent: "select-kit/select-kit-filter", headerComponent: "select-kit/select-kit-header", headerComponentOptions: null, headerComputedContent: null, collectionHeaderComputedContent: null, collectionComponent: "select-kit/select-kit-collection", verticalOffset: 0, horizontalOffset: 0, fullWidthOnMobile: false, castInteger: false, allowAny: false, allowInitialValueMutation: false, content: null, computedContent: null, limitMatches: null, nameChanges: false, allowContentReplacement: false, collectionHeader: null, allowAutoSelectFirst: true, highlightedSelection: null, init() { this._super(); this.noneValue = "__none__"; this.set("headerComponentOptions", Ember.Object.create()); this.set("rowComponentOptions", Ember.Object.create()); this.set("computedContent", []); this.set("highlightedSelection", []); if (this.site && this.site.isMobileDevice) { this.setProperties({ filterable: isNone(this.get("filterable")) ? false : this.get("filterable"), autoFilterable: isNone(this.get("autoFilterable")) ? false : this.get("filterable") }); } if (this.get("nameChanges")) { this.addObserver(`content.@each.${this.get("nameProperty")}`, this, this._compute); } if (this.get("allowContentReplacement")) { this.addObserver(`content.[]`, this, this._compute); } if (this.get("isAsync")) { this.addObserver(`asyncContent.[]`, this, this._compute); } }, keyDown(event) { if (!isEmpty(this.get("filter"))) return true; const keyCode = event.keyCode || event.which; if (event.metaKey === true && keyCode === this.keys.A) { this.didPressSelectAll(); return false; } if (keyCode === this.keys.BACKSPACE) { this.didPressBackspace(); return false; } }, willDestroyElement() { this.removeObserver(`content.@each.${this.get("nameProperty")}`, this, this._compute); this.removeObserver(`content.[]`, this, this._compute); this.removeObserver(`asyncContent.[]`, this, this._compute); }, willComputeAttributes() {}, didComputeAttributes() {}, willComputeContent(content) { return content; }, computeContent(content) { return content; }, _beforeDidComputeContent(content) { content = applyContentPluginApiCallbacks(this.get("pluginApiIdentifiers"), content, this); const existingCreatedComputedContent = this.get("computedContent").filterBy("created", true); this.setProperties({ computedContent: content.map(c => this.computeContentItem(c)).concat(existingCreatedComputedContent) }); return content; }, didComputeContent() {}, willComputeAsyncContent(content) { return content; }, computeAsyncContent(content) { return content; }, _beforeDidComputeAsyncContent(content) { content = applyContentPluginApiCallbacks(this.get("pluginApiIdentifiers"), content, this); this.setProperties({ computedAsyncContent: content.map(c => this.computeAsyncContentItem(c)) }); return content; }, didComputeAsyncContent() {}, computeContentItem(contentItem, options) { let originalContent; options = options || {}; const name = options.name; if (typeof contentItem === "string" || typeof contentItem === "number") { originalContent = {}; originalContent[this.get("valueAttribute")] = contentItem; originalContent[this.get("nameProperty")] = name || contentItem; } else { originalContent = contentItem; } let computedContentItem = { value: this._castInteger(this.valueForContentItem(contentItem)), name: name || this._nameForContent(contentItem), locked: false, created: options.created || false, __sk_row_type: options.created ? "createRow" : null, originalContent }; return computedContentItem; }, computeAsyncContentItem(contentItem, options) { return this.computeContentItem(contentItem, options); }, @computed("isAsync", "isLoading", "filteredAsyncComputedContent.[]", "filteredComputedContent.[]") collectionComputedContent(isAsync, isLoading, filteredAsyncComputedContent, filteredComputedContent) { if (isAsync) { return isLoading ? [] : filteredAsyncComputedContent; } else { return filteredComputedContent; } }, validateCreate() { return !this.get("hasReachedLimit"); }, validateSelect() { return !this.get("hasReachedLimit"); }, @computed("limit", "selection.[]") hasReachedLimit(limit, selection) { if (!limit) return false; return selection.length >= limit; }, @computed("shouldFilter", "allowAny", "filter") shouldDisplayFilter(shouldFilter, allowAny, filter) { if (shouldFilter) return true; if (allowAny && filter.length > 0) return true; return false; }, @computed("filter", "collectionComputedContent.[]", "isLoading") noContentRow(filter, collectionComputedContent, isLoading) { if (filter.length > 0 && collectionComputedContent.length === 0 && !isLoading) { return I18n.t("select_kit.no_content"); } }, @computed("hasReachedLimit", "limit") maxContentRow(hasReachedLimit, limit) { if (hasReachedLimit) { return I18n.t("select_kit.max_content_reached", { count: limit }); } }, @computed("filter", "filterable", "autoFilterable", "renderedFilterOnce") shouldFilter(filter, filterable, autoFilterable, renderedFilterOnce) { if (renderedFilterOnce && filterable) return true; if (filterable) return true; if (autoFilterable && filter.length > 0) return true; return false; }, @computed("computedValue", "filter", "collectionComputedContent.[]", "hasReachedLimit", "isLoading") shouldDisplayCreateRow(computedValue, filter, collectionComputedContent, hasReachedLimit, isLoading) { if (isLoading || hasReachedLimit) return false; if (collectionComputedContent.map(c => c.value).includes(filter)) return false; if (this.get("allowAny") && filter.length > 0 && this.validateCreate(filter)) return true; return false; }, @computed("filter", "shouldDisplayCreateRow") createRowComputedContent(filter, shouldDisplayCreateRow) { if (shouldDisplayCreateRow) { let content = this.createContentFromInput(filter); let computedContentItem = this.computeContentItem(content, { created: true }); computedContentItem.__sk_row_type = "createRow"; return computedContentItem; } }, @computed templateForRow() { return () => null; }, @computed templateForNoneRow() { return () => null; }, @computed("filter") templateForCreateRow() { return (rowComponent) => { return I18n.t("select_kit.create", { content: rowComponent.get("computedContent.name") }); }; }, @computed("none") noneRowComputedContent(none) { if (isNone(none)) { return null; } let noneRowComputedContent; switch (typeof none) { case "string": noneRowComputedContent = this.computeContentItem(this.noneValue, { name: (I18n.t(none) || "").htmlSafe() }); break; default: noneRowComputedContent = this.computeContentItem(none); } noneRowComputedContent.__sk_row_type = "noneRow"; return noneRowComputedContent; }, createContentFromInput(input) { return input; }, highlightSelection(items) { this.propertyWillChange("highlightedSelection"); this.set("highlightedSelection", makeArray(items)); this.propertyDidChange("highlightedSelection"); }, clearHighlightSelection() { this.highlightSelection([]); }, willSelect() {}, didSelect() {}, willCreate() {}, didCreate() {}, willDeselect() {}, didDeselect() {}, clearFilter() { this.$filterInput().val(""); this.setProperties({ filter: "", previousFilter: "" }); }, startLoading() { this.set("isLoading", true); this.set("highlighted", null); this._boundaryActionHandler("onStartLoading"); }, stopLoading() { this.focus(); this.set("isLoading", false); this._boundaryActionHandler("onStopLoading"); }, @computed("selection.[]", "isExpanded", "filter", "highlightedSelection.[]") collectionHeaderComputedContent() { return applyCollectionHeaderCallbacks( this.get("pluginApiIdentifiers"), this.get("collectionHeader"), this ); }, @computed("selection.[]", "isExpanded", "headerIcon") headerComputedContent() { return applyHeaderContentPluginApiCallbacks( this.get("pluginApiIdentifiers"), this.computeHeaderContent(), this ); }, _boundaryActionHandler(actionName, ...params) { if (get(this.actions, actionName)) { run.next(() => this.send(actionName, ...params)); } else if (this.get(actionName)) { run.next(() => this.get(actionName)()); } }, highlight(computedContent) { this.set("highlighted", computedContent); this._boundaryActionHandler("onHighlight", computedContent); }, clearSelection() { this.deselect(this.get("selection")); this.focus(); }, actions: { onToggle() { this.clearHighlightSelection(); if (this.get("isExpanded")) { this.collapse(); } else { this.expand(); } }, onClickRow(computedContentItem) { this.didClickRow(computedContentItem); }, onClickSelectionItem(computedContentItem) { this.didClickSelectionItem(computedContentItem); }, onClearSelection() { this.clearSelection(); }, onMouseoverRow(computedContentItem) { this.highlight(computedContentItem); }, onFilterComputedContent(filter) { if (filter === this.get("previousFilter")) return; this.clearHighlightSelection(); this.setProperties({ highlighted: null, renderedFilterOnce: true, previousFilter: filter, filter }); this.autoHighlight(); this._boundaryActionHandler("onFilter", filter); } } });