discourse/app/assets/javascripts/select-kit/addon/components/multi-select.js
Joffrey JAFFEUX cb59681d86
DEV: select-kit third major update with focus on accessibility (#13303)
Major changes included:
- better support for screen readers
- trapping focus in modals
- better tabbing order in composer
- alerts on no content found/number of items found
- better autofocus in modals
- mini-tag-chooser is now a multi-select component
- each multi-select-component will now display selection on one row
2021-08-23 10:44:19 +02:00

192 lines
5.0 KiB
JavaScript

import SelectKitComponent from "select-kit/components/select-kit";
import { computed } from "@ember/object";
import { isPresent } from "@ember/utils";
import { next } from "@ember/runloop";
import layout from "select-kit/templates/components/multi-select";
import { makeArray } from "discourse-common/lib/helpers";
export default SelectKitComponent.extend({
pluginApiIdentifiers: ["multi-select"],
layout,
classNames: ["multi-select"],
multiSelect: true,
selectKitOptions: {
none: "select_kit.default_header_text",
clearable: true,
filterable: true,
filterIcon: null,
closeOnChange: false,
autoInsertNoneItem: false,
headerComponent: "multi-select/multi-select-header",
autoFilterable: true,
caretDownIcon: "caretIcon",
caretUpIcon: "caretIcon",
},
caretIcon: computed("value.[]", function () {
const maximum = this.selectKit.options.maximum;
return maximum && makeArray(this.value).length >= parseInt(maximum, 10)
? null
: "plus";
}),
search(filter) {
return this._super(filter).filter(
(content) => !makeArray(this.selectedContent).includes(content)
);
},
append(values) {
const existingItems = values
.map((value) => {
const defaultItem = this.defaultItem(value, value);
const existingItem =
this.findValue(this.mainCollection, defaultItem) ||
this.findName(this.mainCollection, defaultItem);
if (!existingItem) {
if (this.validateCreate(value, this.content)) {
return value;
}
} else if (this.validateSelect(existingItem)) {
return this.getValue(existingItem);
}
})
.filter(Boolean);
const newValues = makeArray(this.value).concat(existingItems);
const newContent = makeArray(this.selectedContent).concat(
makeArray(existingItems)
);
this.selectKit.change(newValues, newContent);
},
deselect(item) {
this.clearErrors();
const newContent = this.selectedContent.filter(
(content) => this.getValue(item) !== this.getValue(content)
);
this.selectKit.change(
this.valueProperty ? newContent.mapBy(this.valueProperty) : newContent,
newContent
);
},
select(value, item) {
if (this.selectKit.hasSelection && this.selectKit.options.maximum === 1) {
this.selectKit.deselectByValue(
this.getValue(this.selectedContent.firstObject)
);
next(() => {
this.selectKit.select(value, item);
});
return;
}
if (!isPresent(value)) {
if (!this.validateSelect(this.selectKit.highlighted)) {
return;
}
this.selectKit.change(
makeArray(this.value).concat(
makeArray(this.getValue(this.selectKit.highlighted))
),
makeArray(this.selectedContent).concat(
makeArray(this.selectKit.highlighted)
)
);
} else {
const existingItem = this.findValue(
this.mainCollection,
this.selectKit.valueProperty ? item : value
);
if (existingItem) {
if (!this.validateSelect(item)) {
return;
}
}
const newValues = makeArray(this.value).concat(makeArray(value));
const newContent = makeArray(this.selectedContent).concat(
makeArray(item)
);
this.selectKit.change(
[...new Set(newValues)],
newContent.length
? newContent
: makeArray(this.defaultItem(value, value))
);
}
},
selectedContent: computed("value.[]", "content.[]", function () {
const value = makeArray(this.value).map((v) =>
this.selectKit.options.castInteger && this._isNumeric(v) ? Number(v) : v
);
if (value.length) {
let content = [];
value.forEach((v) => {
if (this.selectKit.valueProperty) {
const c = makeArray(this.content).findBy(
this.selectKit.valueProperty,
v
);
if (c) {
content.push(c);
}
} else {
if (makeArray(this.content).includes(v)) {
content.push(v);
}
}
});
return this.selectKit.modifySelection(content);
}
return null;
}),
_onKeydown(event) {
if (
event.code === "Enter" &&
event.target.classList.contains("selected-name")
) {
event.stopPropagation();
this.selectKit.deselectByValue(event.target.dataset.value);
return false;
}
if (event.code === "Backspace") {
event.stopPropagation();
const input = this.getFilterInput();
if (input && input.value.length === 0) {
const selected = this.element.querySelectorAll(
".select-kit-header .choice.select-kit-selected-name"
);
if (selected.length) {
const lastSelected = selected[selected.length - 1];
if (lastSelected) {
if (lastSelected === document.activeElement) {
this.deselect(this.selectedContent.lastObject);
} else {
lastSelected.focus();
}
}
}
}
}
return true;
},
});