Skip to content

Commit 4ec52b3

Browse files
committed
feat: add toSelectPlugin
1 parent b93b256 commit 4ec52b3

File tree

10 files changed

+221
-51
lines changed

10 files changed

+221
-51
lines changed

packages/plugin/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import { Enum } from 'enum-plus';
2-
import searchItemsPlugin from './searchItems';
2+
import searchItemsPlugin from './plugins/searchItems';
33

44
Enum.install(searchItemsPlugin);

packages/plugin/src/searchItems.ts renamed to packages/plugin/src/plugins/searchItems.ts

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
import type { EnumItemClass, EnumItemOptions, EnumValue, PluginFunc } from 'enum-plus';
1+
import type {
2+
EnumInit,
3+
EnumItemClass,
4+
EnumItemInit,
5+
EnumItemOptions,
6+
EnumKey,
7+
EnumValue,
8+
PluginFunc,
9+
ValueTypeFromSingleInit,
10+
} from 'enum-plus';
211

312
export interface PluginOptions {
4-
/**
5-
* **EN:** The localization function, used to localize the label of the list item. Or a function
6-
* that returns a localization function, it's useful that you can get the latest `$t` after
7-
* language is changed
8-
*
9-
* **CN:** 本地化函数,用于本地化列表项的标签,也可以是一个函数,返回一个本地化函数,这样可以在语言切换后获取最新的`$t`
10-
*/
11-
$t?: EnumItemOptions['localize'] | (() => EnumItemOptions['localize']);
1213
/**
1314
* **EN:** The name of the field used for list item labels, used to search list items. Default is
1415
* `label`.
@@ -26,9 +27,16 @@ export interface PluginOptions {
2627
}
2728

2829
const searchItemsPlugin: PluginFunc<PluginOptions> = (options, Enum) => {
29-
const { $t, labelField = 'label', ignoreCase = true } = options ?? {};
30+
const { labelField = 'label', ignoreCase = true } = options ?? {};
3031
Enum.extends({
31-
searchItems: (search?: string, item?: EnumValue | Record<string, unknown> | EnumItemClass<EnumValue>): boolean => {
32+
searchItems: <
33+
T extends EnumInit<K, V>,
34+
K extends EnumKey<T> = EnumKey<T>,
35+
V extends EnumValue = ValueTypeFromSingleInit<T[K], K>,
36+
>(
37+
search?: string,
38+
item?: V | Record<string, unknown> | EnumItemClass<EnumItemInit<V>, K, V>
39+
): boolean => {
3240
let label: string | undefined;
3341
if (
3442
typeof item === 'boolean' ||
@@ -55,8 +63,8 @@ const searchItemsPlugin: PluginFunc<PluginOptions> = (options, Enum) => {
5563
}
5664
}
5765
}
58-
if ($t) {
59-
const result = $t(label);
66+
if (Enum.localize) {
67+
const result = Enum.localize(label);
6068
if (typeof result === 'function') {
6169
// if $t is a function, call it to get the latest $t
6270
label = result(label) ?? label;
@@ -77,7 +85,11 @@ export default searchItemsPlugin;
7785

7886
declare module 'enum-plus-extend' {
7987
// eslint-disable-next-line @typescript-eslint/no-unused-vars
80-
interface EnumExtension<T, K, V> {
88+
interface EnumExtension<
89+
T extends EnumInit<K, V>,
90+
K extends EnumKey<T> = EnumKey<T>,
91+
V extends EnumValue = ValueTypeFromSingleInit<T[K], K>,
92+
> {
8193
/**
8294
* **EN:** Search for list items, returns true if the item matches the search criteria, false
8395
* otherwise. The function supports searching by specific field that is specified in options. If
@@ -108,7 +120,7 @@ declare module 'enum-plus-extend' {
108120
*
109121
* **CN:** 待搜索的列表项
110122
*/
111-
item?: EnumValue | { value?: EnumValue; label?: string } | EnumItemClass<EnumValue>
123+
item?: V | Record<string, unknown> | EnumItemClass<EnumItemInit<V>, K, V>
112124
) => boolean;
113125
}
114126
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import type {
2+
BuiltInLocaleKeys,
3+
EnumInit,
4+
EnumItemClass,
5+
EnumKey,
6+
EnumValue,
7+
IEnum,
8+
PluginFunc,
9+
ValueTypeFromSingleInit,
10+
} from 'enum-plus';
11+
import type { StandardEnumInit } from 'lib/types';
12+
13+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
14+
export type PluginOptions = Pick<ToSelectConfig<any>, 'labelField' | 'valueField'>;
15+
16+
const toSelectPlugin: PluginFunc<PluginOptions> = (options, Enum) => {
17+
const { labelField: globalLabelField, valueField: globalValueField } = options ?? {};
18+
Enum.extends({
19+
toList<FL extends string = string, FV extends string = string>(
20+
this: IEnum<StandardEnumInit<string, EnumValue>, string, EnumValue>,
21+
config: ToSelectConfig<StandardEnumInit<string, EnumValue>, FL, FV, string, EnumValue> &
22+
FirstOptionConfig<FL, FV, EnumValue> = {}
23+
): SelectItem<FL, FV, EnumValue>[] {
24+
const {
25+
firstOption = false,
26+
labelField = globalLabelField ?? 'label',
27+
valueField = globalValueField ?? 'value',
28+
} = config;
29+
const selectItems = this.items.map(
30+
(item) =>
31+
({
32+
[valueField]: item.value,
33+
[labelField]: item.label,
34+
}) as SelectItem<FL, FV, EnumValue>
35+
);
36+
if (firstOption) {
37+
if (firstOption === true) {
38+
// the first option
39+
const value = '' as EnumValue;
40+
const label = Enum.localize
41+
? Enum.localize('enum-plus.options.all' as BuiltInLocaleKeys)
42+
: 'enum-plus.options.all';
43+
return [{ [valueField]: value, [labelField]: label } as SelectItem<FL, FV, EnumValue>, ...selectItems];
44+
} else {
45+
return [
46+
{
47+
...firstOption,
48+
[labelField]: Enum.localize
49+
? Enum.localize(firstOption[labelField as FL])
50+
: firstOption[labelField as FL],
51+
},
52+
...selectItems,
53+
];
54+
}
55+
} else {
56+
return selectItems;
57+
}
58+
},
59+
});
60+
};
61+
62+
/** More options for the options method */
63+
export interface ToSelectConfig<
64+
T extends EnumInit<K, V>,
65+
OV extends string | ((item: EnumItemClass<T[K], K, V>) => string) = string,
66+
OL extends string | ((item: EnumItemClass<T[K], K, V>) => string) = string,
67+
K extends EnumKey<T> = EnumKey<T>,
68+
V extends EnumValue = ValueTypeFromSingleInit<T[K], K>,
69+
> {
70+
/**
71+
* **EN:** The name of the value field in the output object, or a function to get the field name,
72+
* default is `value`
73+
*
74+
* **CN:** 输出对象的value字段名,或者获取字段名的函数,默认为 `value`
75+
*/
76+
valueField?: OV;
77+
/**
78+
* **EN:** The name of the label field in the output object, or a function to get the field name,
79+
* default is `label`
80+
*
81+
* **CN:** 输出对象的label字段名,或者获取字段名的函数,默认为 `label`
82+
*/
83+
labelField?: OL;
84+
}
85+
86+
export interface FirstOptionConfig<FL extends string = string, FV extends string = string, V = EnumValue> {
87+
/**
88+
* **EN:** Add a default option at the top
89+
*
90+
* - `true`: the option uses the default value, `value` is `''`, `label` is `'All'`;
91+
* - `false`: the default option is not added;
92+
* - `object`: custom default option, must be an object with `key`, `value`, and `label` properties
93+
*
94+
* **CN:** 在头部添加一个默认选项
95+
*
96+
* - `true`: 选项使用默认值,`value`为`''`,`label`为`'全部'`
97+
* - `false`: 不添加默认选项
98+
* - `object`: 自定义默认选项
99+
*
100+
* @default false
101+
*/
102+
firstOption?: boolean | SelectItem<FL, FV, V>;
103+
}
104+
105+
/** Data structure of ant-design Select options */
106+
export type SelectItem<FL extends string = string, FV extends string = string, V = EnumValue> = Record<FV, V> &
107+
Record<FL, string>;
108+
109+
export default toSelectPlugin;
110+
111+
declare module 'enum-plus-extend' {
112+
interface EnumExtension<
113+
T extends EnumInit<K, V>,
114+
K extends EnumKey<T> = EnumKey<T>,
115+
V extends EnumValue = ValueTypeFromSingleInit<T[K], K>,
116+
> {
117+
toList(this: IEnum<T, K, V> & EnumExtension<T, K, V>): SelectItem<'label', 'value', V>[];
118+
toList<
119+
OV extends string | ((item: EnumItemClass<T[K], K, V>) => string) = string,
120+
OL extends string | ((item: EnumItemClass<T[K], K, V>) => string) = string,
121+
>(
122+
this: IEnum<T, K, V> & EnumExtension<T, K, V>,
123+
config: ToSelectConfig<T, OV, OL, K, V> &
124+
FirstOptionConfig<
125+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
126+
OL extends (...args: any) => any ? ReturnType<OL> : OL,
127+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
128+
OV extends (...args: any) => any ? ReturnType<OV> : OV,
129+
V
130+
>
131+
): SelectItem<
132+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
133+
OL extends (...args: any) => any ? ReturnType<OL> : OL,
134+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
135+
OV extends (...args: any) => any ? ReturnType<OV> : OV,
136+
V
137+
>[];
138+
}
139+
}

src/enum-collection.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import type {
1616
MenuItemOption,
1717
ObjectFirstOptionConfig,
1818
StandardEnumItemInit,
19-
ToSelectConfig,
19+
ToListConfig,
2020
ValueTypeFromSingleInit,
2121
} from './types';
2222
import { ENUM_COLLECTION, ITEMS, KEYS } from './utils';
@@ -124,15 +124,15 @@ export class EnumCollectionClass<
124124
return this.items.has(keyOrValue);
125125
}
126126

127-
toSelect(): EnumItemOptionData<K, V>[];
128-
toSelect(config: ToSelectConfig & BooleanFirstOptionConfig<V>): EnumItemOptionData<K | '', V | ''>[];
129-
toSelect<FK = never, FV = never>(
130-
config: ToSelectConfig & ObjectFirstOptionConfig<FK, FV>
127+
toList(): EnumItemOptionData<K, V>[];
128+
toList(config: ToListConfig & BooleanFirstOptionConfig<V>): EnumItemOptionData<K | '', V | ''>[];
129+
toList<FK = never, FV = never>(
130+
config: ToListConfig & ObjectFirstOptionConfig<FK, FV>
131131
): EnumItemOptionData<K | (FK extends never ? FV : FK), V | (FV extends never ? V : FV)>[];
132-
toSelect<FK = never, FV = never>(
133-
config?: ToSelectConfig & (BooleanFirstOptionConfig<V> | ObjectFirstOptionConfig<FK, FV>)
132+
toList<FK = never, FV = never>(
133+
config?: ToListConfig & (BooleanFirstOptionConfig<V> | ObjectFirstOptionConfig<FK, FV>)
134134
): EnumItemOptionData<K | FK, V | FV>[] {
135-
return this.items.toSelect(config as ToSelectConfig & BooleanFirstOptionConfig<V>) as EnumItemOptionData<
135+
return this.items.toList(config as ToListConfig & BooleanFirstOptionConfig<V>) as EnumItemOptionData<
136136
K | FK,
137137
V | FV
138138
>[];

src/enum-values.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type {
1313
IEnumItems,
1414
MenuItemOption,
1515
ObjectFirstOptionConfig,
16-
ToSelectConfig,
16+
ToListConfig,
1717
ValueMap,
1818
ValueTypeFromSingleInit,
1919
} from './types';
@@ -74,13 +74,13 @@ export class EnumItemsArray<
7474
return this.some((i) => i.value === keyOrValue || i.key === keyOrValue);
7575
}
7676

77-
toSelect(): EnumItemOptionData<K, V>[];
78-
toSelect(config?: ToSelectConfig & BooleanFirstOptionConfig<V>): EnumItemOptionData<K | '', V | ''>[];
79-
toSelect<FK = never, FV = never>(
80-
config: ToSelectConfig & ObjectFirstOptionConfig<FK, FV>
77+
toList(): EnumItemOptionData<K, V>[];
78+
toList(config?: ToListConfig & BooleanFirstOptionConfig<V>): EnumItemOptionData<K | '', V | ''>[];
79+
toList<FK = never, FV = never>(
80+
config: ToListConfig & ObjectFirstOptionConfig<FK, FV>
8181
): EnumItemOptionData<K | (FK extends never ? FV : FK), V | (FV extends never ? V : FV)>[];
82-
toSelect<FK = never, FV = never>(
83-
config: ToSelectConfig & (BooleanFirstOptionConfig<V> | ObjectFirstOptionConfig<FK, FV>) = {}
82+
toList<FK = never, FV = never>(
83+
config: ToListConfig & (BooleanFirstOptionConfig<V> | ObjectFirstOptionConfig<FK, FV>) = {}
8484
): EnumItemOptionData<K | FK, V | FV>[] {
8585
const { firstOption = false } = config;
8686
if (firstOption) {

src/enum.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ function getInitMapFromArray<
144144
*
145145
* **CN:** 表示增强Enum类功能的插件,通过添加新方法或属性
146146
*
147-
* @param option The options for the plugin | 插件的选项
147+
* @param options The options for the plugin | 插件的选项
148148
* @param Enum The Enum global method | Enum全局方法
149149
*/
150-
export type PluginFunc<T = unknown> = (option: T | undefined, Enum: typeof EnumAlias) => void;
150+
export type PluginFunc<T = unknown> = (options: T | undefined, Enum: typeof EnumAlias) => void;

src/types.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ export interface IEnumItems<
185185
* @see https://ant.design/components/checkbox-cn#option
186186
*/
187187
// eslint-disable-next-line @typescript-eslint/method-signature-style
188-
toSelect(): EnumItemOptionData<K, V>[];
188+
toList(): EnumItemOptionData<K, V>[];
189189
/**
190190
* **EN:** Generate an array of objects that can be bound to those `options like` of components
191191
* such as Select, Radio, and Checkbox, following the data specification of ant-design
@@ -203,7 +203,7 @@ export interface IEnumItems<
203203
* @see https://ant.design/components/checkbox-cn#option
204204
*/
205205
// eslint-disable-next-line @typescript-eslint/method-signature-style
206-
toSelect(config: ToSelectConfig & BooleanFirstOptionConfig<V>): EnumItemOptionData<K | '', V | ''>[];
206+
toList(config: ToListConfig & BooleanFirstOptionConfig<V>): EnumItemOptionData<K | '', V | ''>[];
207207
/**
208208
* **EN:** Generate an array of objects that can be bound to those `options like` of components
209209
* such as Select, Radio, and Checkbox, following the data specification of ant-design
@@ -221,8 +221,8 @@ export interface IEnumItems<
221221
* @see https://ant.design/components/checkbox-cn#option
222222
*/
223223
// eslint-disable-next-line @typescript-eslint/method-signature-style
224-
toSelect<FK, FV>(
225-
config: ToSelectConfig & ObjectFirstOptionConfig<FK, FV>
224+
toList<FK, FV>(
225+
config: ToListConfig & ObjectFirstOptionConfig<FK, FV>
226226
): EnumItemOptionData<K | (FK extends never ? FV : FK), V | (FV extends never ? V : FV)>[];
227227

228228
/**
@@ -451,7 +451,26 @@ export type EnumValue = keyof any | bigint | boolean | Date | RegExp;
451451
export type EnumKey<T> = keyof T;
452452

453453
/** More options for the options method */
454-
export type ToSelectConfig = object;
454+
export interface ToListConfig<
455+
T extends EnumInit<K, V>,
456+
K extends EnumKey<T> = EnumKey<T>,
457+
V extends EnumValue = ValueTypeFromSingleInit<T[K], K>,
458+
> {
459+
/**
460+
* **EN:** The name of the value field in the output object, or a function to get the field name,
461+
* default is `value`
462+
*
463+
* **CN:** 输出对象的value字段名,或者获取字段名的函数,默认为 `value`
464+
*/
465+
valueField?: string | ((item: EnumItemClass<T[K], K, V>[]) => string);
466+
/**
467+
* **EN:** The name of the label field in the output object, or a function to get the field name,
468+
* default is `label`
469+
*
470+
* **CN:** 输出对象的label字段名,或者获取字段名的函数,默认为 `label`
471+
*/
472+
labelField?: string | ((item: EnumItemClass<T[K], K, V>[]) => string);
473+
}
455474

456475
export interface BooleanFirstOptionConfig<V> {
457476
/**

test/test-suites/enum-collection.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const testEnumCollection = (engine: TestEngineBase) => {
4141
engine.expect(week.key(1)).toBe(week.items.key(1));
4242
engine.expect(week.has(1)).toBe(week.items.has(1));
4343
engine.expect(week.raw()).toBe(week.items.raw());
44-
engine.expect(week.toSelect()).toEqual(week.items.toSelect());
44+
engine.expect(week.toList()).toEqual(week.items.toList());
4545
engine.expect(week.toMenu()).toEqual(week.items.toMenu());
4646
engine.expect(week.toFilter()).toEqual(week.items.toFilter());
4747
engine.expect(week.toValueMap()).toEqual(week.items.toValueMap());
@@ -86,8 +86,8 @@ const testEnumCollection = (engine: TestEngineBase) => {
8686
engine.expect(strangeEnum.items.key(2)).toBe('key');
8787
engine.expect(strangeEnum.has).toBe(3);
8888
engine.expect(strangeEnum.items.has(3)).toBe(true);
89-
engine.expect(strangeEnum.toSelect).toBe(4);
90-
engine.expect(strangeEnum.items.toSelect()).toHaveLength(Object.keys(strangeEnumConfig).length);
89+
engine.expect(strangeEnum.toList).toBe(4);
90+
engine.expect(strangeEnum.items.toList()).toHaveLength(Object.keys(strangeEnumConfig).length);
9191
engine.expect(strangeEnum.toFilter).toBe(6);
9292
engine.expect(strangeEnum.items.toFilter()).toHaveLength(Object.keys(strangeEnumConfig).length);
9393
engine.expect(strangeEnum.toMenu).toBe(7);

0 commit comments

Comments
 (0)