const { get, isNone, isEmpty, isPresent } = Ember; import { on, observes } from "ember-addons/ember-computed-decorators"; import computed from "ember-addons/ember-computed-decorators"; import UtilsMixin from "select-box-kit/mixins/utils"; import DomHelpersMixin from "select-box-kit/mixins/dom-helpers"; import KeyboardMixin from "select-box-kit/mixins/keyboard"; export default Ember.Component.extend(UtilsMixin, DomHelpersMixin, KeyboardMixin, { layoutName: "select-box-kit/templates/components/select-box-kit", classNames: "select-box-kit", classNameBindings: [ "isFocused", "isExpanded", "isDisabled", "isHidden", "isAbove", "isBelow", "isLeftAligned", "isRightAligned" ], isDisabled: false, isExpanded: false, isFocused: false, isHidden: false, renderBody: false, tabindex: 0, scrollableParentSelector: ".modal-body", value: null, none: null, highlightedValue: null, noContentLabel: "select_box.no_content", valueAttribute: "id", nameProperty: "name", autoFilterable: false, filterable: false, filter: "", filterPlaceholder: "select_box.filter_placeholder", filterIcon: "search", rowComponent: "select-box-kit/select-box-kit-row", noneRowComponent: "select-box-kit/select-box-kit-none-row", createRowComponent: "select-box-kit/select-box-kit-create-row", filterComponent: "select-box-kit/select-box-kit-filter", headerComponent: "select-box-kit/select-box-kit-header", collectionComponent: "select-box-kit/select-box-kit-collection", collectionHeight: 200, verticalOffset: 0, horizontalOffset: 0, fullWidthOnMobile: false, castInteger: false, allowAny: false, init() { this._super(); if ($(window).outerWidth(false) <= 420) { this.setProperties({ filterable: false, autoFilterable: false }); } this._previousScrollParentOverflow = "auto"; }, close() { this.setProperties({ isExpanded: false, isFocused: false }); }, focus() { Ember.run.schedule("afterRender", () => this.$offscreenInput().select() ); }, blur() { Ember.run.schedule("afterRender", () => this.$offscreenInput().blur() ); }, clickOutside(event) { if ($(event.target).parents(".select-box-kit").length === 1) { this.close(); return; } if (this.get("isExpanded") === true) { this.set("isExpanded", false); this.focus(); } else { this.close(); } }, createFunction(input) { return (selectedBox) => { const formatedContent = selectedBox.formatContent(input); formatedContent.meta.generated = true; return formatedContent; }; }, filterFunction(content) { return selectBox => { const filter = selectBox.get("filter").toLowerCase(); return _.filter(content, c => { return get(c, "name").toLowerCase().indexOf(filter) > -1; }); }; }, nameForContent(content) { if (isNone(content)) { return null; } if (typeof content === "object") { return get(content, this.get("nameProperty")); } return content; }, valueForContent(content) { switch (typeof content) { case "string": return this._castInteger(content); default: return this._castInteger(get(content, this.get("valueAttribute"))); } }, formatContent(content) { return { value: this.valueForContent(content), name: this.nameForContent(content), originalContent: content, meta: { generated: false } }; }, formatContents(contents) { return contents.map(content => this.formatContent(content)); }, @computed("filter", "filterable", "autoFilterable") computedFilterable(filter, filterable, autoFilterable) { if (filterable === true) { return true; } if (filter.length > 0 && autoFilterable === true) { return true; } return false; }, @computed("computedFilterable", "filter", "allowAny") shouldDisplayCreateRow(computedFilterable, filter, allow) { return computedFilterable === true && filter.length > 0 && allow === true; }, @computed("filter", "allowAny") createRowContent(filter, allow) { if (allow === true) { return Ember.Object.create({ value: filter, name: filter }); } }, @computed("content.[]") computedContent(content) { return this.formatContents(content || []); }, @computed("value", "none", "computedContent.firstObject.value") computedValue(value, none, firstContentValue) { if (isNone(value) && isNone(none)) { return this._castInteger(firstContentValue); } return this._castInteger(value); }, @computed templateForRow() { return () => null; }, @computed templateForNoneRow() { return () => null; }, @computed templateForCreateRow() { return () => null; }, @computed("none") computedNone(none) { if (isNone(none)) { return null; } switch (typeof none) { case "string": return Ember.Object.create({ name: I18n.t(none), value: "" }); default: return this.formatContent(none); } }, @computed("computedValue", "computedContent.[]") selectedContent(computedValue, computedContent) { if (isNone(computedValue)) { return []; } return [ computedContent.findBy("value", this._castInteger(computedValue)) ]; }, @on("didRender") _configureSelectBoxDOM() { if (this.get("isExpanded") === true) { Ember.run.schedule("afterRender", () => { this.$collection().css("max-height", this.get("collectionHeight")); this._applyDirection(); this._positionWrapper(); }); } }, @on("willDestroyElement") _cleanHandlers() { $(window).off("resize.select-box-kit"); this._removeFixedPosition(); }, @on("didInsertElement") _setupResizeListener() { $(window).on("resize.select-box-kit", () => this.set("isExpanded", false) ); }, @observes("filter", "filteredContent.[]", "shouldDisplayCreateRow") _setHighlightedValue() { const filteredContent = this.get("filteredContent"); const display = this.get("shouldDisplayCreateRow"); const none = this.get("computedNone"); if (isNone(this.get("highlightedValue")) && !isEmpty(filteredContent)) { this.set("highlightedValue", get(filteredContent, "firstObject.value")); return; } if (display === true && isEmpty(filteredContent)) { this.set("highlightedValue", this.get("filter")); } else if (!isEmpty(filteredContent)) { this.set("highlightedValue", get(filteredContent, "firstObject.value")); } else if (isEmpty(filteredContent) && isPresent(none) && display === false) { this.set("highlightedValue", get(none, "value")); } }, @observes("isExpanded") _isExpandedChanged() { if (this.get("isExpanded") === true) { this._applyFixedPosition(); this.setProperties({ highlightedValue: this.get("computedValue"), renderBody: true, isFocused: true }); } else { this._removeFixedPosition(); } }, @computed("filter", "computedFilterable", "computedContent.[]", "computedValue.[]") filteredContent(filter, computedFilterable, computedContent, computedValue) { if (computedFilterable === false) { return computedContent; } return this.filterFunction(computedContent)(this, computedValue); }, @computed("scrollableParentSelector") scrollableParent(scrollableParentSelector) { return this.$().parents(scrollableParentSelector).first(); }, actions: { onToggle() { this.toggleProperty("isExpanded"); if (this.get("isExpanded") === true) { this.focus(); } }, onCreateContent(input) { const content = this.createFunction(input)(this); this.get("computedContent").pushObject(content); this.send("onSelect", content.value); }, onFilterChange(filter) { this.set("filter", filter); }, onHighlight(value) { this.set("highlightedValue", value); }, onClearSelection() { this.send("onSelect", null); }, onSelect(value) { value = this.defaultOnSelect(value); this.set("value", value); }, onDeselect() { this.defaultOnDeselect(); this.set("value", null); } }, defaultOnSelect(value) { if (value === "") { value = null; } this.setProperties({ highlightedValue: null, isExpanded: false, filter: "" }); this.focus(); return value; }, defaultOnDeselect(value) { const content = this.get("computedContent").findBy("value", value); if (!isNone(content) && get(content, "meta.generated") === true) { this.get("computedContent").removeObject(content); } }, _applyDirection() { let options = { left: "auto", bottom: "auto", top: "auto" }; const headerHeight = this.$header().outerHeight(false); const filterHeight = this.$(".select-box-kit-filter").outerHeight(false); const bodyHeight = this.$body().outerHeight(false); const windowWidth = $(window).width(); const windowHeight = $(window).height(); const boundingRect = this.get("element").getBoundingClientRect(); const offsetTop = boundingRect.top; if (this.get("fullWidthOnMobile") && windowWidth <= 420) { const margin = 10; const relativeLeft = this.$().offset().left - $(window).scrollLeft(); options.left = margin - relativeLeft; options.width = windowWidth - margin * 2; options.maxWidth = options.minWidth = "unset"; } else { const offsetLeft = boundingRect.left; const bodyWidth = this.$body().outerWidth(false); const hasRightSpace = (windowWidth - (this.get("horizontalOffset") + offsetLeft + filterHeight + bodyWidth) > 0); if (hasRightSpace) { this.setProperties({ isLeftAligned: true, isRightAligned: false }); options.left = this.get("horizontalOffset"); } else { this.setProperties({ isLeftAligned: false, isRightAligned: true }); options.right = this.get("horizontalOffset"); } } const componentHeight = this.get("verticalOffset") + bodyHeight + headerHeight; const hasBelowSpace = windowHeight - offsetTop - componentHeight > 0; if (hasBelowSpace) { this.setProperties({ isBelow: true, isAbove: false }); options.top = headerHeight + this.get("verticalOffset"); } else { this.setProperties({ isBelow: false, isAbove: true }); options.bottom = headerHeight + this.get("verticalOffset"); } this.$body().css(options); }, _applyFixedPosition() { const width = this.$().outerWidth(false); const height = this.$header().outerHeight(false); if (this.get("scrollableParent").length === 0) { return; } const $placeholder = $(`
`); this._previousScrollParentOverflow = this.get("scrollableParent").css("overflow"); this.get("scrollableParent").css({ overflow: "hidden" }); this.$() .before($placeholder.css({ display: "inline-block", width, height, "vertical-align": "middle" })) .css({ width, direction: $("html").css("direction"), position: "fixed", "margin-top": -this.get("scrollableParent").scrollTop(), "margin-left": -width }); }, _removeFixedPosition() { if (this.get("scrollableParent").length === 0) { return; } $(`.select-box-kit-fixed-placeholder-${this.elementId}`).remove(); this.$().css({ top: "auto", left: "auto", "margin-left": "auto", "margin-top": "auto", position: "relative" }); this.get("scrollableParent").css({ overflow: this._previousScrollParentOverflow }); }, _positionWrapper() { const headerHeight = this.$header().outerHeight(false); this.$(".select-box-kit-wrapper").css({ width: this.$().width(), height: headerHeight + this.$body().outerHeight(false) }); } });