discourse/app/assets/javascripts/select-kit/components/multi-select.js.es6

374 lines
10 KiB
JavaScript

import SelectKitComponent from "select-kit/components/select-kit";
import computed from "ember-addons/ember-computed-decorators";
import { on } from "ember-addons/ember-computed-decorators";
const { get, isNone, isEmpty, makeArray, run } = Ember;
import {
applyOnSelectPluginApiCallbacks,
applyOnSelectNonePluginApiCallbacks
} from "select-kit/mixins/plugin-api";
export default SelectKitComponent.extend({
pluginApiIdentifiers: ["multi-select"],
layoutName: "select-kit/templates/components/multi-select",
classNames: "multi-select",
headerComponent: "multi-select/multi-select-header",
headerText: "select_kit.default_header_text",
allowAny: true,
allowInitialValueMutation: false,
autoFilterable: true,
selectedNameComponent: "multi-select/selected-name",
filterIcon: null,
filterComponent: "multi-select/multi-select-filter",
computedValues: null,
values: null,
init() {
this._super(...arguments);
this.set("computedValues", []);
if (isNone(this.get("values"))) {
this.set("values", []);
}
this.get("headerComponentOptions").setProperties({
selectedNameComponent: this.get("selectedNameComponent")
});
},
@on("didRender")
_setChoicesMaxWidth() {
const width = this.$body().outerWidth(false);
if (width > 0) {
this.$(".choices").css({ maxWidth: width });
}
},
@on("didUpdateAttrs", "init")
_compute() {
run.scheduleOnce("afterRender", () => {
this.willComputeAttributes();
let content = this.get("content") || [];
let asyncContent = this.get("asyncContent") || [];
content = this.willComputeContent(content);
asyncContent = this.willComputeAsyncContent(asyncContent);
let values = this._beforeWillComputeValues(this.get("values"));
content = this.computeContent(content);
asyncContent = this.computeAsyncContent(asyncContent);
content = this._beforeDidComputeContent(content);
asyncContent = this._beforeDidComputeAsyncContent(asyncContent);
values = this.willComputeValues(values);
values = this.computeValues(values);
values = this._beforeDidComputeValues(values);
this.didComputeContent(content);
this.didComputeAsyncContent(asyncContent);
this.didComputeValues(values);
this.didComputeAttributes();
});
},
@computed("filter", "shouldDisplayCreateRow")
createRowComputedContent(filter, shouldDisplayCreateRow) {
if (shouldDisplayCreateRow) {
let content = this.createContentFromInput(filter);
return this.computeContentItem(content, { created: true });
}
},
@computed("filter", "computedValues")
shouldDisplayCreateRow(filter, computedValues) {
return this._super() && !computedValues.includes(filter);
},
@computed
shouldDisplayFilter() {
return true;
},
_beforeWillComputeValues(values) {
return values.map(v => this._cast(v === "" ? null : v));
},
willComputeValues(values) {
return values;
},
computeValues(values) {
return values;
},
_beforeDidComputeValues(values) {
this.setProperties({ computedValues: values });
return values;
},
didComputeValues(values) {
return values;
},
mutateAttributes() {
run.next(() => {
if (this.get("isDestroyed") || this.get("isDestroying")) return;
this.mutateContent(this.get("computedContent"));
this.mutateValues(this.get("computedValues"));
});
},
mutateValues(computedValues) {
this.set("values", computedValues);
},
mutateContent() {},
forceValues(values) {
this.mutateValues(values);
this._compute();
},
filterComputedContent(computedContent, computedValues, filter) {
return computedContent.filter(c => {
return this._normalize(get(c, "name")).indexOf(filter) > -1;
});
},
@computed("computedAsyncContent.[]", "computedValues.[]")
filteredAsyncComputedContent(computedAsyncContent, computedValues) {
computedAsyncContent = computedAsyncContent.filter(c => {
return !computedValues.includes(get(c, "value"));
});
if (this.get("limitMatches")) {
return computedAsyncContent.slice(0, this.get("limitMatches"));
}
return computedAsyncContent;
},
@computed("computedContent.[]", "computedValues.[]", "filter")
filteredComputedContent(computedContent, computedValues, filter) {
computedContent = computedContent.filter(c => {
return !computedValues.includes(get(c, "value"));
});
if (this.get("shouldFilter")) {
computedContent = this.filterComputedContent(
computedContent,
computedValues,
this._normalize(filter)
);
}
if (this.get("limitMatches")) {
return computedContent.slice(0, this.get("limitMatches"));
}
return computedContent;
},
computeHeaderContent() {
let content = {
title: this.get("title"),
selection: this.get("selection")
};
if (this.get("noneLabel")) {
if (!this.get("hasSelection")) {
content.title = content.name = content.label = I18n.t(
this.get("noneLabel")
);
}
} else {
if (!this.get("hasReachedMinimum")) {
const key =
this.get("minimumLabel") || "select_kit.min_content_not_reached";
content.title = content.name = content.label = I18n.t(key, {
count: this.get("minimum")
});
}
}
return content;
},
@computed("filter")
templateForCreateRow() {
return rowComponent => {
return I18n.t("select_kit.create", {
content: rowComponent.get("computedContent.name")
});
};
},
validateSelect() {
return this._super() && !this.get("hasReachedMaximum");
},
@computed("computedValues.[]", "computedContent.[]")
selection(computedValues, computedContent) {
const selected = [];
computedValues.forEach(v => {
const value = computedContent.findBy("value", v);
if (value) selected.push(value);
});
return selected;
},
@computed("selection.[]")
hasSelection(selection) {
return !isEmpty(selection);
},
didPressTab(event) {
if (isEmpty(this.get("filter")) && !this.get("highlighted")) {
this.$header().focus();
this.close(event);
return true;
}
if (this.get("highlighted") && this.get("isExpanded")) {
this._destroyEvent(event);
this.focus();
this.select(this.get("highlighted"));
return false;
} else {
this.close(event);
}
return true;
},
autoHighlight() {
run.schedule("afterRender", () => {
if (!this.get("isExpanded")) return;
if (!this.get("renderedBodyOnce")) return;
if (this.get("highlighted")) return;
if (isEmpty(this.get("collectionComputedContent"))) {
if (this.get("createRowComputedContent")) {
this.highlight(this.get("createRowComputedContent"));
} else if (
this.get("noneRowComputedContent") &&
this.get("hasSelection")
) {
this.highlight(this.get("noneRowComputedContent"));
}
} else {
this.highlight(this.get("collectionComputedContent.firstObject"));
}
});
},
select(computedContentItem) {
if (
!computedContentItem ||
computedContentItem.__sk_row_type === "noneRow"
) {
applyOnSelectNonePluginApiCallbacks(
this.get("pluginApiIdentifiers"),
this
);
this._boundaryActionHandler("onSelectNone");
this.clearSelection();
return;
}
if (computedContentItem.__sk_row_type === "noopRow") {
applyOnSelectPluginApiCallbacks(
this.get("pluginApiIdentifiers"),
computedContentItem.value,
this
);
this._boundaryActionHandler("onSelect", computedContentItem.value);
return;
}
if (computedContentItem.__sk_row_type === "createRow") {
if (
!this.get("computedValues").includes(computedContentItem.value) &&
this.validateCreate(computedContentItem.value)
) {
this.willCreate(computedContentItem);
computedContentItem.__sk_row_type = null;
this.get("computedContent").pushObject(computedContentItem);
run.schedule("afterRender", () => {
this.didCreate(computedContentItem);
this._boundaryActionHandler("onCreate");
});
this.select(computedContentItem);
return;
} else {
this._boundaryActionHandler("onCreateFailure");
return;
}
}
if (this.validateSelect(computedContentItem)) {
this.willSelect(computedContentItem);
this.clearFilter();
this.setProperties({ highlighted: null });
this.get("computedValues").pushObject(computedContentItem.value);
run.next(() => this.mutateAttributes());
run.schedule("afterRender", () => {
this.didSelect(computedContentItem);
applyOnSelectPluginApiCallbacks(
this.get("pluginApiIdentifiers"),
computedContentItem.value,
this
);
this.autoHighlight();
this._boundaryActionHandler("onSelect", computedContentItem.value);
});
} else {
this._boundaryActionHandler("onSelectFailure");
}
},
deselect(rowComputedContentItems) {
this.willDeselect(rowComputedContentItems);
rowComputedContentItems = makeArray(rowComputedContentItems);
const generatedComputedContents = this._filterRemovableComputedContents(
makeArray(rowComputedContentItems)
);
this.setProperties({ highlighted: null, highlightedSelection: [] });
this.get("computedValues").removeObjects(
rowComputedContentItems.map(r => r.value)
);
this.get("computedContent").removeObjects([
...rowComputedContentItems,
...generatedComputedContents
]);
run.next(() => {
this.mutateAttributes();
run.schedule("afterRender", () => {
this.didDeselect(rowComputedContentItems);
this.autoHighlight();
if (!this.isDestroying && !this.isDestroyed) {
this._positionWrapper();
}
});
});
},
close(event) {
this.clearHighlightSelection();
this._super(event);
},
unfocus(event) {
this.clearHighlightSelection();
this._super(event);
}
});