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:
David Taylor 2024-08-22 09:39:03 +01:00 committed by GitHub
parent ebbe23e4d2
commit 3e3c051164
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 1276 additions and 1255 deletions

View File

@ -1,41 +1,45 @@
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,
@classNames("multi-select")
@selectKitOptions({
none: "select_kit.default_header_text",
clearable: true,
filterable: true,
filterIcon: null,
closeOnChange: false,
autoInsertNoneItem: false,
headerComponent: "multi-select/multi-select-header",
filterComponent: "multi-select/multi-select-filter",
autoFilterable: true,
caretDownIcon: "caretIcon",
caretUpIcon: "caretIcon",
useHeaderFilter: false,
})
@pluginApiIdentifiers(["multi-select"])
export default class MultiSelect extends SelectKitComponent {
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",
filterComponent: "multi-select/multi-select-filter",
autoFilterable: true,
caretDownIcon: "caretIcon",
caretUpIcon: "caretIcon",
useHeaderFilter: false,
},
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,42 +126,38 @@ export default SelectKitComponent.extend({
: makeArray(this.defaultItem(value, value))
);
}
},
}
selectedContent: computed(
"value.[]",
"content.[]",
"selectKit.noneItem",
function () {
const value = makeArray(this.value).map((v) =>
this.selectKit.options.castInteger && this._isNumeric(v) ? Number(v) : v
);
@computed("value.[]", "content.[]", "selectKit.noneItem")
get selectedContent() {
const value = makeArray(this.value).map((v) =>
this.selectKit.options.castInteger && this._isNumeric(v) ? Number(v) : v
);
if (value.length) {
let content = [];
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);
}
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;
return this.selectKit.modifySelection(content);
}
),
return null;
}
_onKeydown(event) {
if (
@ -192,5 +192,5 @@ export default SelectKitComponent.extend({
}
return true;
},
});
}
}

View File

@ -1,46 +1,45 @@
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,
@classNames("single-select")
@selectKitOptions({
headerComponent: "select-kit/single-select-header",
})
@pluginApiIdentifiers(["single-select"])
export default class SingleSelect extends SelectKitComponent {
singleSelect = true;
selectKitOptions: {
headerComponent: "select-kit/single-select-header",
},
@computed("value", "content.[]", "selectKit.noneItem")
get selectedContent() {
if (!isEmpty(this.value)) {
let content;
selectedContent: computed(
"value",
"content.[]",
"selectKit.noneItem",
function () {
if (!isEmpty(this.value)) {
let content;
const value =
this.selectKit.options.castInteger && this._isNumeric(this.value)
? Number(this.value)
: this.value;
const value =
this.selectKit.options.castInteger && this._isNumeric(this.value)
? Number(this.value)
: this.value;
if (this.selectKit.valueProperty) {
content = (this.content || []).findBy(
this.selectKit.valueProperty,
value
);
if (this.selectKit.valueProperty) {
content = (this.content || []).findBy(
this.selectKit.valueProperty,
value
);
return this.selectKit.modifySelection(
content || this.defaultItem(value, value)
);
} else {
return this.selectKit.modifySelection(
(this.content || []).filter((c) => c === value)
);
}
return this.selectKit.modifySelection(
content || this.defaultItem(value, value)
);
} else {
return this.selectKit.noneItem;
return this.selectKit.modifySelection(
(this.content || []).filter((c) => c === value)
);
}
} else {
return this.selectKit.noneItem;
}
),
});
}
}

View File

@ -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,
});