mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
DEV: Convert select-kit base classes to native class syntax (#28467)
This lays the groundwork for converting SelectKit subclasses to native class syntax. This commit is designed to be entirely backwards-compatible, so it should not affect any existing subclasses. Of interest: - Any properties which are designed to be overridden by subclasses are implemented using a local `@protoProp` decorator. That means they are applied to the prototype, so that they can be overridden in subclasses by both legacy `.extend()` prototype extensions, and by modern native-class fields. - New class decorators are introduced: `@selectKitOptions` and `@pluginApiIdentifiers`. These are native class versions of the legacy `concatenatedProperties` system. This follows the pattern Ember has introduced for `@className`, `@classNameBindings`, etc.
This commit is contained in:
parent
ebbe23e4d2
commit
3e3c051164
@ -1,15 +1,15 @@
|
||||
import { computed } from "@ember/object";
|
||||
import { next } from "@ember/runloop";
|
||||
import { isPresent } from "@ember/utils";
|
||||
import { classNames } from "@ember-decorators/component";
|
||||
import { makeArray } from "discourse-common/lib/helpers";
|
||||
import SelectKitComponent from "select-kit/components/select-kit";
|
||||
import SelectKitComponent, {
|
||||
pluginApiIdentifiers,
|
||||
selectKitOptions,
|
||||
} from "select-kit/components/select-kit";
|
||||
|
||||
export default SelectKitComponent.extend({
|
||||
pluginApiIdentifiers: ["multi-select"],
|
||||
classNames: ["multi-select"],
|
||||
multiSelect: true,
|
||||
|
||||
selectKitOptions: {
|
||||
@classNames("multi-select")
|
||||
@selectKitOptions({
|
||||
none: "select_kit.default_header_text",
|
||||
clearable: true,
|
||||
filterable: true,
|
||||
@ -22,20 +22,24 @@ export default SelectKitComponent.extend({
|
||||
caretDownIcon: "caretIcon",
|
||||
caretUpIcon: "caretIcon",
|
||||
useHeaderFilter: false,
|
||||
},
|
||||
})
|
||||
@pluginApiIdentifiers(["multi-select"])
|
||||
export default class MultiSelect extends SelectKitComponent {
|
||||
multiSelect = true;
|
||||
|
||||
caretIcon: computed("value.[]", function () {
|
||||
@computed("value.[]")
|
||||
get caretIcon() {
|
||||
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)
|
||||
);
|
||||
},
|
||||
return super
|
||||
.search(filter)
|
||||
.filter((content) => !makeArray(this.selectedContent).includes(content));
|
||||
}
|
||||
|
||||
append(values) {
|
||||
const existingItems = values
|
||||
@ -60,7 +64,7 @@ export default SelectKitComponent.extend({
|
||||
);
|
||||
|
||||
this.selectKit.change(newValues, newContent);
|
||||
},
|
||||
}
|
||||
|
||||
deselect(item) {
|
||||
this.clearErrors();
|
||||
@ -73,7 +77,7 @@ export default SelectKitComponent.extend({
|
||||
this.valueProperty ? newContent.mapBy(this.valueProperty) : newContent,
|
||||
newContent
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
select(value, item) {
|
||||
if (this.selectKit.hasSelection && this.selectKit.options.maximum === 1) {
|
||||
@ -122,13 +126,10 @@ export default SelectKitComponent.extend({
|
||||
: makeArray(this.defaultItem(value, value))
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
selectedContent: computed(
|
||||
"value.[]",
|
||||
"content.[]",
|
||||
"selectKit.noneItem",
|
||||
function () {
|
||||
@computed("value.[]", "content.[]", "selectKit.noneItem")
|
||||
get selectedContent() {
|
||||
const value = makeArray(this.value).map((v) =>
|
||||
this.selectKit.options.castInteger && this._isNumeric(v) ? Number(v) : v
|
||||
);
|
||||
@ -157,7 +158,6 @@ export default SelectKitComponent.extend({
|
||||
|
||||
return null;
|
||||
}
|
||||
),
|
||||
|
||||
_onKeydown(event) {
|
||||
if (
|
||||
@ -192,5 +192,5 @@ export default SelectKitComponent.extend({
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
import Component from "@ember/component";
|
||||
import EmberObject, { computed, get } from "@ember/object";
|
||||
import { guidFor } from "@ember/object/internals";
|
||||
import Mixin from "@ember/object/mixin";
|
||||
import { bind, cancel, next, schedule, throttle } from "@ember/runloop";
|
||||
import { service } from "@ember/service";
|
||||
import { isEmpty, isNone, isPresent } from "@ember/utils";
|
||||
import {
|
||||
classNameBindings,
|
||||
classNames,
|
||||
tagName,
|
||||
} from "@ember-decorators/component";
|
||||
import { createPopper } from "@popperjs/core";
|
||||
import { Promise } from "rsvp";
|
||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
@ -13,7 +17,7 @@ import deprecated from "discourse-common/lib/deprecated";
|
||||
import { makeArray } from "discourse-common/lib/helpers";
|
||||
import { bind as bindDecorator } from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
import PluginApiMixin, {
|
||||
import {
|
||||
applyContentPluginApiCallbacks,
|
||||
applyOnChangePluginApiCallbacks,
|
||||
} from "select-kit/mixins/plugin-api";
|
||||
@ -22,49 +26,124 @@ import UtilsMixin from "select-kit/mixins/utils";
|
||||
export const MAIN_COLLECTION = "MAIN_COLLECTION";
|
||||
export const ERRORS_COLLECTION = "ERRORS_COLLECTION";
|
||||
|
||||
const EMPTY_OBJECT = Object.freeze({});
|
||||
const SELECT_KIT_OPTIONS = Mixin.create({
|
||||
concatenatedProperties: ["selectKitOptions"],
|
||||
selectKitOptions: EMPTY_OBJECT,
|
||||
});
|
||||
|
||||
function isDocumentRTL() {
|
||||
return document.documentElement.classList.contains("rtl");
|
||||
}
|
||||
|
||||
export default Component.extend(
|
||||
SELECT_KIT_OPTIONS,
|
||||
PluginApiMixin,
|
||||
UtilsMixin,
|
||||
{
|
||||
tagName: "details",
|
||||
pluginApiIdentifiers: ["select-kit"],
|
||||
classNames: ["select-kit"],
|
||||
classNameBindings: [
|
||||
/**
|
||||
* Simulates the behavior of Ember's concatenatedProperties under native class syntax
|
||||
*/
|
||||
function concatProtoProperty(target, key, value) {
|
||||
target.proto();
|
||||
target.prototype[key] = [
|
||||
...makeArray(target.prototype[key]),
|
||||
...makeArray(value),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @decorator
|
||||
*
|
||||
* Apply select-kit options to a component class
|
||||
*
|
||||
*/
|
||||
export function selectKitOptions(options) {
|
||||
return function (target) {
|
||||
concatProtoProperty(target, "selectKitOptions", options);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @decorator
|
||||
*
|
||||
* Register one or more plugin API identifiers for a component class
|
||||
*
|
||||
*/
|
||||
export function pluginApiIdentifiers(identifiers) {
|
||||
return function (target) {
|
||||
concatProtoProperty(target, "pluginIdIdentifiers", identifiers);
|
||||
};
|
||||
}
|
||||
|
||||
// Decorator which converts a field into a prototype property.
|
||||
// This allows the value to be overridden in subclasses, even if they're still
|
||||
// using the legacy Ember `.extend()` syntax.
|
||||
function protoProp(prototype, key, descriptor) {
|
||||
return {
|
||||
value: descriptor.initializer?.(),
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
};
|
||||
}
|
||||
|
||||
@tagName("details")
|
||||
@classNames("select-kit")
|
||||
@classNameBindings(
|
||||
"selectKit.isLoading:is-loading",
|
||||
"selectKit.isExpanded:is-expanded",
|
||||
"selectKit.options.disabled:is-disabled",
|
||||
"selectKit.isHidden:is-hidden",
|
||||
"selectKit.hasSelection:has-selection",
|
||||
],
|
||||
tabindex: 0,
|
||||
content: null,
|
||||
value: null,
|
||||
selectKit: null,
|
||||
mainCollection: null,
|
||||
errorsCollection: null,
|
||||
options: null,
|
||||
valueProperty: "id",
|
||||
nameProperty: "name",
|
||||
singleSelect: false,
|
||||
multiSelect: false,
|
||||
labelProperty: null,
|
||||
titleProperty: null,
|
||||
langProperty: null,
|
||||
appEvents: service(),
|
||||
"selectKit.hasSelection:has-selection"
|
||||
)
|
||||
@selectKitOptions({
|
||||
allowAny: false,
|
||||
showFullTitle: true,
|
||||
none: null,
|
||||
translatedNone: null,
|
||||
filterable: false,
|
||||
autoFilterable: "autoFilterable",
|
||||
filterIcon: "search",
|
||||
filterPlaceholder: null,
|
||||
translatedFilterPlaceholder: null,
|
||||
icon: null,
|
||||
icons: null,
|
||||
maximum: null,
|
||||
maximumLabel: null,
|
||||
minimum: null,
|
||||
autoInsertNoneItem: true,
|
||||
closeOnChange: true,
|
||||
useHeaderFilter: false,
|
||||
limitMatches: null,
|
||||
placement: isDocumentRTL() ? "bottom-end" : "bottom-start",
|
||||
verticalOffset: 3,
|
||||
filterComponent: "select-kit/select-kit-filter",
|
||||
selectedNameComponent: "selected-name",
|
||||
selectedChoiceComponent: "selected-choice",
|
||||
castInteger: false,
|
||||
focusAfterOnChange: true,
|
||||
triggerOnChangeOnTab: true,
|
||||
autofocus: false,
|
||||
placementStrategy: null,
|
||||
mobilePlacementStrategy: null,
|
||||
desktopPlacementStrategy: null,
|
||||
hiddenValues: null,
|
||||
disabled: false,
|
||||
expandedOnInsert: false,
|
||||
formName: null,
|
||||
})
|
||||
@pluginApiIdentifiers(["select-kit"])
|
||||
export default class SelectKit extends Component.extend(UtilsMixin) {
|
||||
@service appEvents;
|
||||
|
||||
singleSelect = false;
|
||||
multiSelect = false;
|
||||
|
||||
@protoProp tabindex = 0;
|
||||
@protoProp content = null;
|
||||
@protoProp value = null;
|
||||
@protoProp selectKit = null;
|
||||
@protoProp mainCollection = null;
|
||||
@protoProp errorsCollection = null;
|
||||
@protoProp options = null;
|
||||
@protoProp valueProperty = "id";
|
||||
@protoProp nameProperty = "name";
|
||||
@protoProp labelProperty = null;
|
||||
@protoProp titleProperty = null;
|
||||
@protoProp langProperty = null;
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
super.init(...arguments);
|
||||
|
||||
this._searchPromise = null;
|
||||
|
||||
@ -139,14 +218,14 @@ export default Component.extend(
|
||||
bodyElement: bind(this, this._bodyElement),
|
||||
})
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
_modifyComponentForRowWrapper(collection, item) {
|
||||
let component = this.modifyComponentForRow(collection, item);
|
||||
return component || "select-kit/select-kit-row";
|
||||
},
|
||||
}
|
||||
|
||||
modifyComponentForRow() {},
|
||||
modifyComponentForRow() {}
|
||||
|
||||
_modifyContentForCollectionWrapper(identifier) {
|
||||
let collection = this.modifyContentForCollection(identifier);
|
||||
@ -163,9 +242,9 @@ export default Component.extend(
|
||||
}
|
||||
|
||||
return collection;
|
||||
},
|
||||
}
|
||||
|
||||
modifyContentForCollection() {},
|
||||
modifyContentForCollection() {}
|
||||
|
||||
_modifyComponentForCollectionWrapper(identifier) {
|
||||
let component = this.modifyComponentForCollection(identifier);
|
||||
@ -182,18 +261,18 @@ export default Component.extend(
|
||||
}
|
||||
|
||||
return component;
|
||||
},
|
||||
}
|
||||
|
||||
modifyComponentForCollection() {},
|
||||
modifyComponentForCollection() {}
|
||||
|
||||
didUpdateAttrs() {
|
||||
this._super(...arguments);
|
||||
super.didUpdateAttrs(...arguments);
|
||||
|
||||
this.handleDeprecations();
|
||||
},
|
||||
}
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
super.didInsertElement(...arguments);
|
||||
|
||||
this.appEvents.on("keyboard-visibility-change", this, this._updatePopper);
|
||||
|
||||
@ -202,32 +281,28 @@ export default Component.extend(
|
||||
this._open();
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
click(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
},
|
||||
}
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
super.willDestroyElement(...arguments);
|
||||
|
||||
this._cancelSearch();
|
||||
|
||||
this.appEvents.off(
|
||||
"keyboard-visibility-change",
|
||||
this,
|
||||
this._updatePopper
|
||||
);
|
||||
this.appEvents.off("keyboard-visibility-change", this, this._updatePopper);
|
||||
|
||||
if (this.popper) {
|
||||
this.popper.destroy();
|
||||
this.popper = null;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
super.didReceiveAttrs(...arguments);
|
||||
|
||||
const deprecatedOptions = this._resolveDeprecatedOptions();
|
||||
const mergedOptions = Object.assign({}, ...this.selectKitOptions);
|
||||
@ -258,11 +333,7 @@ export default Component.extend(
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof value === "string" &&
|
||||
!value.includes(".") &&
|
||||
value in this
|
||||
) {
|
||||
if (typeof value === "string" && !value.includes(".") && value in this) {
|
||||
const computedValue = get(this, value);
|
||||
if (typeof computedValue !== "function") {
|
||||
this.selectKit.options.set(key, computedValue);
|
||||
@ -290,58 +361,19 @@ export default Component.extend(
|
||||
|
||||
this.set("content", this.computeContent());
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
selectKitOptions: {
|
||||
allowAny: false,
|
||||
showFullTitle: true,
|
||||
none: null,
|
||||
translatedNone: null,
|
||||
filterable: false,
|
||||
autoFilterable: "autoFilterable",
|
||||
filterIcon: "search",
|
||||
filterPlaceholder: null,
|
||||
translatedFilterPlaceholder: null,
|
||||
icon: null,
|
||||
icons: null,
|
||||
maximum: null,
|
||||
maximumLabel: null,
|
||||
minimum: null,
|
||||
autoInsertNoneItem: true,
|
||||
closeOnChange: true,
|
||||
useHeaderFilter: false,
|
||||
limitMatches: null,
|
||||
placement: isDocumentRTL() ? "bottom-end" : "bottom-start",
|
||||
verticalOffset: 3,
|
||||
filterComponent: "select-kit/select-kit-filter",
|
||||
selectedNameComponent: "selected-name",
|
||||
selectedChoiceComponent: "selected-choice",
|
||||
castInteger: false,
|
||||
focusAfterOnChange: true,
|
||||
triggerOnChangeOnTab: true,
|
||||
autofocus: false,
|
||||
placementStrategy: null,
|
||||
mobilePlacementStrategy: null,
|
||||
desktopPlacementStrategy: null,
|
||||
hiddenValues: null,
|
||||
disabled: false,
|
||||
expandedOnInsert: false,
|
||||
formName: null,
|
||||
},
|
||||
|
||||
autoFilterable: computed("content.[]", "selectKit.filter", function () {
|
||||
@computed("content.[]", "selectKit.filter")
|
||||
get autoFilterable() {
|
||||
return (
|
||||
this.selectKit.filter &&
|
||||
this.options.autoFilterable &&
|
||||
this.content.length > 15
|
||||
);
|
||||
}),
|
||||
}
|
||||
|
||||
collections: computed(
|
||||
"selectedContent.[]",
|
||||
"mainCollection.[]",
|
||||
"errorsCollection.[]",
|
||||
function () {
|
||||
@computed("selectedContent.[]", "mainCollection.[]", "errorsCollection.[]")
|
||||
get collections() {
|
||||
return this._collections.map((identifier) => {
|
||||
return {
|
||||
identifier,
|
||||
@ -349,11 +381,10 @@ export default Component.extend(
|
||||
};
|
||||
});
|
||||
}
|
||||
),
|
||||
|
||||
createContentFromInput(input) {
|
||||
return input;
|
||||
},
|
||||
}
|
||||
|
||||
validateCreate(filter, content) {
|
||||
this.clearErrors();
|
||||
@ -364,7 +395,7 @@ export default Component.extend(
|
||||
!content.map((c) => this.getValue(c)).includes(filter) &&
|
||||
!makeArray(this.value).includes(filter)
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
validateSelect() {
|
||||
this.clearErrors();
|
||||
@ -374,14 +405,13 @@ export default Component.extend(
|
||||
const maximum = this.selectKit.options.maximum;
|
||||
if (maximum && selection.length >= maximum) {
|
||||
const key =
|
||||
this.selectKit.options.maximumLabel ||
|
||||
"select_kit.max_content_reached";
|
||||
this.selectKit.options.maximumLabel || "select_kit.max_content_reached";
|
||||
this.addError(I18n.t(key, { count: maximum }));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
}
|
||||
|
||||
addError(error) {
|
||||
if (!this.errorsCollection.includes(error)) {
|
||||
@ -389,7 +419,7 @@ export default Component.extend(
|
||||
}
|
||||
|
||||
this._safeAfterRender(() => this._updatePopper());
|
||||
},
|
||||
}
|
||||
|
||||
clearErrors() {
|
||||
if (!this.element || this.isDestroyed || this.isDestroying) {
|
||||
@ -397,29 +427,29 @@ export default Component.extend(
|
||||
}
|
||||
|
||||
this.set("errorsCollection", []);
|
||||
},
|
||||
}
|
||||
|
||||
prependCollection(identifier) {
|
||||
this._collections.unshift(identifier);
|
||||
},
|
||||
}
|
||||
|
||||
appendCollection(identifier) {
|
||||
this._collections.push(identifier);
|
||||
},
|
||||
}
|
||||
|
||||
insertCollectionAtIndex(identifier, index) {
|
||||
this._collections.insertAt(index, identifier);
|
||||
},
|
||||
}
|
||||
|
||||
insertBeforeCollection(identifier, insertedIdentifier) {
|
||||
const index = this._collections.indexOf(identifier);
|
||||
this.insertCollectionAtIndex(insertedIdentifier, index - 1);
|
||||
},
|
||||
}
|
||||
|
||||
insertAfterCollection(identifier, insertedIdentifier) {
|
||||
const index = this._collections.indexOf(identifier);
|
||||
this.insertCollectionAtIndex(insertedIdentifier, index + 1);
|
||||
},
|
||||
}
|
||||
|
||||
_onInput(event) {
|
||||
this._updatePopper();
|
||||
@ -436,21 +466,18 @@ export default Component.extend(
|
||||
event.target.value,
|
||||
INPUT_DELAY
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
_debouncedInput(filter) {
|
||||
this.selectKit.set("filter", filter);
|
||||
this.triggerSearch(filter);
|
||||
},
|
||||
}
|
||||
|
||||
_onChangeWrapper(value, items) {
|
||||
this.selectKit.set("filter", null);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
if (
|
||||
!this.selectKit.valueProperty &&
|
||||
this.selectKit.noneItem === value
|
||||
) {
|
||||
if (!this.selectKit.valueProperty && this.selectKit.noneItem === value) {
|
||||
value = null;
|
||||
items = [];
|
||||
}
|
||||
@ -499,21 +526,21 @@ export default Component.extend(
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_modifyContentWrapper(content) {
|
||||
content = this.modifyContent(content);
|
||||
|
||||
return applyContentPluginApiCallbacks(content, this);
|
||||
},
|
||||
}
|
||||
|
||||
modifyContent(content) {
|
||||
return content;
|
||||
},
|
||||
}
|
||||
|
||||
_modifyNoSelectionWrapper() {
|
||||
return this.modifyNoSelection();
|
||||
},
|
||||
}
|
||||
|
||||
modifyNoSelection() {
|
||||
if (this.selectKit.options.translatedNone) {
|
||||
@ -543,39 +570,39 @@ export default Component.extend(
|
||||
}
|
||||
|
||||
return item;
|
||||
},
|
||||
}
|
||||
|
||||
_modifySelectionWrapper(item) {
|
||||
return this.modifySelection(item);
|
||||
},
|
||||
}
|
||||
|
||||
modifySelection(item) {
|
||||
return item;
|
||||
},
|
||||
}
|
||||
|
||||
_onKeydownWrapper(event) {
|
||||
return this._boundaryActionHandler("onKeydown", event);
|
||||
},
|
||||
}
|
||||
|
||||
_mainElement() {
|
||||
return document.querySelector(`#${this.selectKit.uniqueID}`);
|
||||
},
|
||||
}
|
||||
|
||||
_headerElement() {
|
||||
return this.selectKit.mainElement().querySelector("summary");
|
||||
},
|
||||
}
|
||||
|
||||
_bodyElement() {
|
||||
return this.selectKit.mainElement().querySelector(".select-kit-body");
|
||||
},
|
||||
}
|
||||
|
||||
_onHover(value, item) {
|
||||
throttle(this, this._highlight, item, 25, true);
|
||||
},
|
||||
}
|
||||
|
||||
_highlight(item) {
|
||||
this.selectKit.set("highlighted", item);
|
||||
},
|
||||
}
|
||||
|
||||
_boundaryActionHandler(actionName, ...params) {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||
@ -603,12 +630,12 @@ export default Component.extend(
|
||||
}
|
||||
|
||||
return boundaryAction;
|
||||
},
|
||||
}
|
||||
|
||||
deselect() {
|
||||
this.clearErrors();
|
||||
this.selectKit.change(null, null);
|
||||
},
|
||||
}
|
||||
|
||||
deselectByValue(value) {
|
||||
if (!value) {
|
||||
@ -617,11 +644,11 @@ export default Component.extend(
|
||||
|
||||
const item = this.itemForValue(value, this.selectedContent);
|
||||
this.deselect(item);
|
||||
},
|
||||
}
|
||||
|
||||
append() {
|
||||
// do nothing on general case
|
||||
},
|
||||
}
|
||||
|
||||
search(filter) {
|
||||
let content = this.content || [];
|
||||
@ -633,15 +660,13 @@ export default Component.extend(
|
||||
});
|
||||
}
|
||||
return content;
|
||||
},
|
||||
}
|
||||
|
||||
triggerSearch(filter) {
|
||||
this._searchPromise && cancel(this._searchPromise);
|
||||
|
||||
this._searchPromise = this._searchWrapper(
|
||||
filter || this.selectKit.filter
|
||||
);
|
||||
},
|
||||
this._searchPromise = this._searchWrapper(filter || this.selectKit.filter);
|
||||
}
|
||||
|
||||
_searchWrapper(filter) {
|
||||
if (this.isDestroyed || this.isDestroying) {
|
||||
@ -731,7 +756,7 @@ export default Component.extend(
|
||||
}
|
||||
this.set("selectKit.enterDisabled", false);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_safeAfterRender(fn) {
|
||||
next(() => {
|
||||
@ -743,7 +768,7 @@ export default Component.extend(
|
||||
fn();
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_scrollToRow(rowItem, preventScroll = true) {
|
||||
const value = this.getValue(rowItem);
|
||||
@ -758,7 +783,7 @@ export default Component.extend(
|
||||
}
|
||||
|
||||
rowContainer?.focus({ preventScroll });
|
||||
},
|
||||
}
|
||||
|
||||
_highlightLast() {
|
||||
const highlighted = this.mainCollection.objectAt(
|
||||
@ -768,7 +793,7 @@ export default Component.extend(
|
||||
this._scrollToRow(highlighted, false);
|
||||
this.set("selectKit.highlighted", highlighted);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_highlightFirst() {
|
||||
const highlighted = this.mainCollection.objectAt(0);
|
||||
@ -776,7 +801,7 @@ export default Component.extend(
|
||||
this._scrollToRow(highlighted, false);
|
||||
this.set("selectKit.highlighted", highlighted);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_highlightNext() {
|
||||
let highlightedIndex = this.mainCollection.indexOf(
|
||||
@ -801,7 +826,7 @@ export default Component.extend(
|
||||
this._scrollToRow(highlighted, false);
|
||||
this.set("selectKit.highlighted", highlighted);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_highlightPrevious() {
|
||||
let highlightedIndex = this.mainCollection.indexOf(
|
||||
@ -826,13 +851,13 @@ export default Component.extend(
|
||||
this._scrollToRow(highlighted, false);
|
||||
this.set("selectKit.highlighted", highlighted);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_deselectLast() {
|
||||
if (this.selectKit.hasSelection) {
|
||||
this.deselectByValue(this.value[this.value.length - 1]);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
select(value, item) {
|
||||
if (!isPresent(value)) {
|
||||
@ -847,26 +872,26 @@ export default Component.extend(
|
||||
|
||||
this.selectKit.change(value, item || this.defaultItem(value, value));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_onClearSelection() {
|
||||
this.selectKit.change(null, null);
|
||||
},
|
||||
}
|
||||
|
||||
_onOpenWrapper() {
|
||||
return this._boundaryActionHandler("onOpen");
|
||||
},
|
||||
}
|
||||
|
||||
_cancelSearch() {
|
||||
this._searchPromise && cancel(this._searchPromise);
|
||||
},
|
||||
}
|
||||
|
||||
_onCloseWrapper() {
|
||||
this._cancelSearch();
|
||||
this.set("selectKit.highlighted", null);
|
||||
|
||||
return this._boundaryActionHandler("onClose");
|
||||
},
|
||||
}
|
||||
|
||||
_toggle(event) {
|
||||
if (this.selectKit.isExpanded) {
|
||||
@ -874,7 +899,7 @@ export default Component.extend(
|
||||
} else {
|
||||
this._open(event);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_close(event) {
|
||||
if (!this.selectKit.isExpanded) {
|
||||
@ -897,7 +922,7 @@ export default Component.extend(
|
||||
isExpanded: false,
|
||||
filter: null,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_open(event) {
|
||||
if (this.selectKit.isExpanded) {
|
||||
@ -913,9 +938,7 @@ export default Component.extend(
|
||||
const anchor = document.querySelector(
|
||||
`#${this.selectKit.uniqueID}-header`
|
||||
);
|
||||
const popper = document.querySelector(
|
||||
`#${this.selectKit.uniqueID}-body`
|
||||
);
|
||||
const popper = document.querySelector(`#${this.selectKit.uniqueID}-body`);
|
||||
const strategy = this._computePlacementStrategy();
|
||||
|
||||
let bottomOffset = 0;
|
||||
@ -1060,7 +1083,7 @@ export default Component.extend(
|
||||
this._scrollToCurrent();
|
||||
this._updatePopper();
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_scrollToCurrent() {
|
||||
if (this.value && this.mainCollection) {
|
||||
@ -1080,7 +1103,7 @@ export default Component.extend(
|
||||
this.set("selectKit.highlighted", highlighted);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_focusFilter(forceHeader = false) {
|
||||
if (!this.selectKit.mainElement()) {
|
||||
@ -1108,7 +1131,7 @@ export default Component.extend(
|
||||
headerContainer && headerContainer.focus({ preventScroll: true });
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_focusFilterInput() {
|
||||
const input = this.getFilterInput();
|
||||
@ -1120,26 +1143,26 @@ export default Component.extend(
|
||||
input.selectionStart = input.selectionEnd = input.value.length;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
getFilterInput() {
|
||||
return document.querySelector(`#${this.selectKit.uniqueID}-filter input`);
|
||||
},
|
||||
}
|
||||
|
||||
getHeader() {
|
||||
return document.querySelector(`#${this.selectKit.uniqueID}-header`);
|
||||
},
|
||||
}
|
||||
|
||||
handleDeprecations() {
|
||||
this._deprecateValueAttribute();
|
||||
this._deprecateMutations();
|
||||
this._handleDeprecatedArgs();
|
||||
},
|
||||
}
|
||||
|
||||
@bindDecorator
|
||||
_updatePopper() {
|
||||
this.popper?.update?.();
|
||||
},
|
||||
}
|
||||
|
||||
_computePlacementStrategy() {
|
||||
let placementStrategy = this.selectKit.options.placementStrategy;
|
||||
@ -1157,7 +1180,7 @@ export default Component.extend(
|
||||
}
|
||||
|
||||
return placementStrategy;
|
||||
},
|
||||
}
|
||||
|
||||
_deprecated(text) {
|
||||
deprecated(text, {
|
||||
@ -1165,7 +1188,7 @@ export default Component.extend(
|
||||
dropFrom: "2.9.0.beta1",
|
||||
id: "discourse.select-kit",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_deprecateValueAttribute() {
|
||||
if (this.valueAttribute || this.valueAttribute === null) {
|
||||
@ -1175,7 +1198,7 @@ export default Component.extend(
|
||||
|
||||
this.set("valueProperty", this.valueAttribute);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_deprecateMutations() {
|
||||
this.actions ??= {};
|
||||
@ -1190,7 +1213,7 @@ export default Component.extend(
|
||||
this.actions.onSelect ||
|
||||
((value) => this.set("value", value));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_resolveDeprecatedOptions() {
|
||||
const migrations = {
|
||||
@ -1229,7 +1252,7 @@ export default Component.extend(
|
||||
});
|
||||
|
||||
return resolvedDeprecations;
|
||||
},
|
||||
}
|
||||
|
||||
_handleDeprecatedArgs() {
|
||||
const migrations = {
|
||||
@ -1248,6 +1271,12 @@ export default Component.extend(
|
||||
this.set(to, this.get(from));
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Keep the concatenatedProperties behavior for legacy `.extend()` subclasses
|
||||
SelectKit.prototype.concatenatedProperties = [
|
||||
...SelectKit.prototype.concatenatedProperties,
|
||||
"selectKitOptions",
|
||||
"pluginApiIdentifiers",
|
||||
];
|
||||
|
@ -1,21 +1,21 @@
|
||||
import { computed } from "@ember/object";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import SelectKitComponent from "select-kit/components/select-kit";
|
||||
import { classNames } from "@ember-decorators/component";
|
||||
import SelectKitComponent, {
|
||||
pluginApiIdentifiers,
|
||||
selectKitOptions,
|
||||
} from "select-kit/components/select-kit";
|
||||
|
||||
export default SelectKitComponent.extend({
|
||||
pluginApiIdentifiers: ["single-select"],
|
||||
classNames: ["single-select"],
|
||||
singleSelect: true,
|
||||
|
||||
selectKitOptions: {
|
||||
@classNames("single-select")
|
||||
@selectKitOptions({
|
||||
headerComponent: "select-kit/single-select-header",
|
||||
},
|
||||
})
|
||||
@pluginApiIdentifiers(["single-select"])
|
||||
export default class SingleSelect extends SelectKitComponent {
|
||||
singleSelect = true;
|
||||
|
||||
selectedContent: computed(
|
||||
"value",
|
||||
"content.[]",
|
||||
"selectKit.noneItem",
|
||||
function () {
|
||||
@computed("value", "content.[]", "selectKit.noneItem")
|
||||
get selectedContent() {
|
||||
if (!isEmpty(this.value)) {
|
||||
let content;
|
||||
|
||||
@ -42,5 +42,4 @@ export default SelectKitComponent.extend({
|
||||
return this.selectKit.noneItem;
|
||||
}
|
||||
}
|
||||
),
|
||||
});
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import Mixin from "@ember/object/mixin";
|
||||
import { isNone } from "@ember/utils";
|
||||
import { makeArray } from "discourse-common/lib/helpers";
|
||||
|
||||
@ -97,9 +96,3 @@ export function clearCallbacks() {
|
||||
_onChangeCallbacks = {};
|
||||
_replaceContentCallbacks = {};
|
||||
}
|
||||
|
||||
const EMPTY_ARRAY = Object.freeze([]);
|
||||
export default Mixin.create({
|
||||
concatenatedProperties: ["pluginApiIdentifiers"],
|
||||
pluginApiIdentifiers: EMPTY_ARRAY,
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user