Skip to content

Commit b754aad

Browse files
committed
feat: add toSelectPlugin
1 parent 45d1b4b commit b754aad

File tree

10 files changed

+235
-51
lines changed

10 files changed

+235
-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
@@ -17,7 +17,7 @@ import type {
1717
ObjectFirstOptionConfig,
1818
PrimitiveOf,
1919
StandardEnumItemInit,
20-
ToSelectConfig,
20+
ToListConfig,
2121
ValueTypeFromSingleInit,
2222
} from './types';
2323
import { ENUM_COLLECTION, ITEMS, KEYS } from './utils';
@@ -139,15 +139,15 @@ export class EnumCollectionClass<
139139
return this.items.has(keyOrValue);
140140
}
141141

142-
toSelect(): EnumItemOptionData<K, V>[];
143-
toSelect(config: ToSelectConfig & BooleanFirstOptionConfig<V>): EnumItemOptionData<K | '', V | ''>[];
144-
toSelect<FK = never, FV = never>(
145-
config: ToSelectConfig & ObjectFirstOptionConfig<FK, FV>
142+
toList(): EnumItemOptionData<K, V>[];
143+
toList(config: ToListConfig & BooleanFirstOptionConfig<V>): EnumItemOptionData<K | '', V | ''>[];
144+
toList<FK = never, FV = never>(
145+
config: ToListConfig & ObjectFirstOptionConfig<FK, FV>
146146
): EnumItemOptionData<K | (FK extends never ? FV : FK), V | (FV extends never ? V : FV)>[];
147-
toSelect<FK = never, FV = never>(
148-
config?: ToSelectConfig & (BooleanFirstOptionConfig<V> | ObjectFirstOptionConfig<FK, FV>)
147+
toList<FK = never, FV = never>(
148+
config?: ToListConfig & (BooleanFirstOptionConfig<V> | ObjectFirstOptionConfig<FK, FV>)
149149
): EnumItemOptionData<K | FK, V | FV>[] {
150-
return this.items.toSelect(config as ToSelectConfig & BooleanFirstOptionConfig<V>) as EnumItemOptionData<
150+
return this.items.toList(config as ToListConfig & BooleanFirstOptionConfig<V>) as EnumItemOptionData<
151151
K | FK,
152152
V | FV
153153
>[];

src/enum-values.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import type {
1515
MenuItemOption,
1616
ObjectFirstOptionConfig,
1717
PrimitiveOf,
18-
ToSelectConfig,
18+
ToListConfig,
1919
ValueMap,
2020
ValueTypeFromSingleInit,
2121
} from './types';
@@ -62,6 +62,20 @@ export class EnumItemsArray<
6262
this._raw = raw;
6363
this._options = options;
6464
}
65+
name?: string | undefined;
66+
toSelect(): EnumItemOptionData<K, V>[];
67+
toSelect(config: ToSelectConfig & BooleanFirstOptionConfig<V>): EnumItemOptionData<'' | K, '' | V>[];
68+
toSelect<FK, FV>(
69+
config: ToSelectConfig & ObjectFirstOptionConfig<FK, FV>
70+
): EnumItemOptionData<K | (FK extends never ? FV : FK), V | (FV extends never ? V : FV)>[];
71+
toSelect(
72+
config?: unknown
73+
):
74+
| EnumItemOptionData<K, V>[]
75+
| EnumItemOptionData<'' | K, '' | V>[]
76+
| EnumItemOptionData<K | (FK extends never ? FV : FK), V | (FV extends never ? V : FV)>[] {
77+
throw new Error('Method not implemented.');
78+
}
6579

6680
label<KV extends V | K | NonNullable<PrimitiveOf<V>> | NonNullable<PrimitiveOf<K>> | undefined>(
6781
keyOrValue: KV
@@ -129,13 +143,13 @@ export class EnumItemsArray<
129143
return this.some((i) => i.value === keyOrValue || i.key === keyOrValue);
130144
}
131145

132-
toSelect(): EnumItemOptionData<K, V>[];
133-
toSelect(config?: ToSelectConfig & BooleanFirstOptionConfig<V>): EnumItemOptionData<K | '', V | ''>[];
134-
toSelect<FK = never, FV = never>(
135-
config: ToSelectConfig & ObjectFirstOptionConfig<FK, FV>
146+
toList(): EnumItemOptionData<K, V>[];
147+
toList(config?: ToListConfig & BooleanFirstOptionConfig<V>): EnumItemOptionData<K | '', V | ''>[];
148+
toList<FK = never, FV = never>(
149+
config: ToListConfig & ObjectFirstOptionConfig<FK, FV>
136150
): EnumItemOptionData<K | (FK extends never ? FV : FK), V | (FV extends never ? V : FV)>[];
137-
toSelect<FK = never, FV = never>(
138-
config: ToSelectConfig & (BooleanFirstOptionConfig<V> | ObjectFirstOptionConfig<FK, FV>) = {}
151+
toList<FK = never, FV = never>(
152+
config: ToListConfig & (BooleanFirstOptionConfig<V> | ObjectFirstOptionConfig<FK, FV>) = {}
139153
): EnumItemOptionData<K | FK, V | FV>[] {
140154
const { firstOption = false } = config;
141155
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
@@ -252,7 +252,7 @@ export interface IEnumItems<
252252
*
253253
* @see https://ant.design/components/checkbox-cn#option
254254
*/
255-
toSelect(): EnumItemOptionData<K, V>[];
255+
toList(): EnumItemOptionData<K, V>[];
256256
/**
257257
* **EN:** Generate an array of objects that can be bound to those `options like` of components
258258
* such as Select, Radio, and Checkbox, following the data specification of ant-design
@@ -269,7 +269,7 @@ export interface IEnumItems<
269269
*
270270
* @see https://ant.design/components/checkbox-cn#option
271271
*/
272-
toSelect(config: ToSelectConfig & BooleanFirstOptionConfig<V>): EnumItemOptionData<K | '', V | ''>[];
272+
toList(config: ToListConfig & BooleanFirstOptionConfig<V>): EnumItemOptionData<K | '', V | ''>[];
273273
/**
274274
* **EN:** Generate an array of objects that can be bound to those `options like` of components
275275
* such as Select, Radio, and Checkbox, following the data specification of ant-design
@@ -286,8 +286,8 @@ export interface IEnumItems<
286286
*
287287
* @see https://ant.design/components/checkbox-cn#option
288288
*/
289-
toSelect<FK, FV>(
290-
config: ToSelectConfig & ObjectFirstOptionConfig<FK, FV>
289+
toList<FK, FV>(
290+
config: ToListConfig & ObjectFirstOptionConfig<FK, FV>
291291
): EnumItemOptionData<K | (FK extends never ? FV : FK), V | (FV extends never ? V : FV)>[];
292292

293293
/**
@@ -490,7 +490,26 @@ export type EnumValue = keyof any | bigint | boolean | Date | RegExp;
490490
export type EnumKey<T> = keyof T;
491491

492492
/** More options for the options method */
493-
export type ToSelectConfig = object;
493+
export interface ToListConfig<
494+
T extends EnumInit<K, V>,
495+
K extends EnumKey<T> = EnumKey<T>,
496+
V extends EnumValue = ValueTypeFromSingleInit<T[K], K>,
497+
> {
498+
/**
499+
* **EN:** The name of the value field in the output object, or a function to get the field name,
500+
* default is `value`
501+
*
502+
* **CN:** 输出对象的value字段名,或者获取字段名的函数,默认为 `value`
503+
*/
504+
valueField?: string | ((item: EnumItemClass<T[K], K, V>[]) => string);
505+
/**
506+
* **EN:** The name of the label field in the output object, or a function to get the field name,
507+
* default is `label`
508+
*
509+
* **CN:** 输出对象的label字段名,或者获取字段名的函数,默认为 `label`
510+
*/
511+
labelField?: string | ((item: EnumItemClass<T[K], K, V>[]) => string);
512+
}
494513

495514
export interface BooleanFirstOptionConfig<V> {
496515
/**

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)