v-dict 2.0.3 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,12 @@
5
5
  * @Description:
6
6
  -->
7
7
 
8
- # Vue3 Dict Manager
8
+ # Vue3 & React Dict Manager
9
+
10
+ ## 目录
11
+
12
+ - [Vue](#vue)
13
+ - [React](#react)
9
14
 
10
15
  ## Installation
11
16
 
@@ -13,7 +18,7 @@
13
18
  npm i v-dict
14
19
  ```
15
20
 
16
- ## Examples
21
+ ## Vue
17
22
 
18
23
  ### dict.ts
19
24
 
@@ -147,3 +152,85 @@ onMounted(async () => {
147
152
  })
148
153
  </script>
149
154
  ```
155
+
156
+ ## React
157
+
158
+ ### dict.ts
159
+
160
+ ```ts
161
+ import { createDictManager, defineDictData } from 'v-dict/react'
162
+
163
+ export const dm = createDictManager({
164
+ fetch: (code) =>
165
+ Promise.resolve([
166
+ { label: 'xx', value: 'xx' },
167
+ { label: 'xx', value: 'xx' }
168
+ ]),
169
+ extra: ({ map }) => {
170
+ return {
171
+ getLabel: (value: string) => map[value]?.label
172
+ }
173
+ }
174
+ })
175
+
176
+ export const useStatusDict = dm.defineDict('STATUS', {
177
+ data: defineDictData({
178
+ ENABLED: {
179
+ label: 'Enabled',
180
+ color: 'green'
181
+ },
182
+ DISABLED: {
183
+ label: 'Disabled',
184
+ color: 'red'
185
+ }
186
+ })
187
+ })
188
+
189
+ export const useRemoteStatusDict = dm.defineDict('REMOTE_STATUS', {
190
+ remote: true,
191
+ fetch: (code) =>
192
+ Promise.resolve([
193
+ { label: 'Enabled', value: 'ENABLED', color: 'green' },
194
+ { label: 'Disabled', value: 'DISABLED', color: 'red' }
195
+ ]),
196
+ extra: ({ map }) => {
197
+ return {
198
+ getItemDetail: (value: string) => map[value]
199
+ }
200
+ }
201
+ })
202
+
203
+ // clear one dict
204
+ // dm.clear('REMOTE_STATUS')
205
+
206
+ // clear all dicts
207
+ // dm.clear()
208
+ ```
209
+
210
+ ### xx.tsx
211
+
212
+ ```tsx
213
+ import { useEffect } from 'react'
214
+ import { useRemoteStatusDict } from './dict'
215
+
216
+ export default function Demo() {
217
+ const statusDict = useRemoteStatusDict({
218
+ // same as above
219
+ clone: false,
220
+ // same as above
221
+ immediate: true,
222
+ // same as above
223
+ refresh: false
224
+ })
225
+
226
+ return (
227
+ <div>
228
+ <div>{E.ENABLED}</div>
229
+ <div>{map[E.ENABLED]?.label}</div>
230
+ <div>{statusDict.getLabel(E.ENABLED)}</div>
231
+ <div>{statusDict.getItem(E.DISABLED)?.color}</div>
232
+ <div>{list.length}</div>
233
+ </div>
234
+ )
235
+ }
236
+ ```
@@ -0,0 +1,8 @@
1
+ export type Resolve<T> = (value: T | PromiseLike<T>) => void;
2
+ export type Reject = (reason?: any) => void;
3
+ export type CreatePromiseReturn<T> = Promise<T> & {
4
+ resolve: Resolve<T>;
5
+ reject: Reject;
6
+ isPending: boolean;
7
+ };
8
+ export declare function createPromise<T = any>(executor?: (resolve: Resolve<T>, reject: Reject) => void): CreatePromiseReturn<T>;
@@ -0,0 +1 @@
1
+ export * from './vue';
@@ -143,10 +143,11 @@ function createDictManager(createDictManagerOptions = {}) {
143
143
  const listenersMap = /* @__PURE__ */ Object.create(null);
144
144
  function emitChange(code) {
145
145
  var _a;
146
- const listeners = code ? (_a = listenersMap[code]) != null ? _a : [] : Object.values(listenersMap).flat();
147
- for (let listener of listeners) {
148
- listener();
149
- }
146
+ const listeners = code ? (_a = listenersMap[code]) != null ? _a : /* @__PURE__ */ new Set() : Object.values(listenersMap).reduce((acc, set) => {
147
+ set.forEach((listener) => acc.add(listener));
148
+ return acc;
149
+ }, /* @__PURE__ */ new Set());
150
+ listeners.forEach((listener) => listener());
150
151
  }
151
152
  function clear(code) {
152
153
  var _a;
@@ -197,9 +198,10 @@ function createDictManager(createDictManagerOptions = {}) {
197
198
  }
198
199
  function subscribeMap(listener) {
199
200
  var _a;
200
- listenersMap[code] = [...(_a = listenersMap[code]) != null ? _a : [], listener];
201
+ (_a = listenersMap[code]) != null ? _a : listenersMap[code] = /* @__PURE__ */ new Set();
202
+ listenersMap[code].add(listener);
201
203
  return () => {
202
- listenersMap[code] = listenersMap[code].filter((l) => l !== listener);
204
+ listenersMap[code].delete(listener);
203
205
  };
204
206
  }
205
207
  function getMapSnapshot() {
@@ -239,15 +241,16 @@ function createDictManager(createDictManagerOptions = {}) {
239
241
  const loadPromiseRef = react.useRef(
240
242
  !clone ? globalLoadPromise : createPromise()
241
243
  );
242
- const map = react.useSyncExternalStore(subscribeMap, getMapSnapshot);
244
+ const globalMap = react.useSyncExternalStore(subscribeMap, getMapSnapshot);
243
245
  const [clonedMap, setClonedMap] = react.useState(/* @__PURE__ */ new Map());
244
- const state = react.useMemo(
245
- () => createStateFromMap(!clone ? map : clonedMap),
246
- [clone, map, clonedMap]
247
- );
246
+ const map = !clone ? globalMap : clonedMap;
247
+ const state = react.useMemo(() => createStateFromMap(map), [map]);
248
248
  const load = react.useCallback((options) => {
249
249
  const oldLoadPromise = loadPromiseRef.current;
250
250
  loadPromiseRef.current = createPromise();
251
+ if (!clone) {
252
+ globalLoadPromise = loadPromiseRef.current;
253
+ }
251
254
  loadDict({ ...mergedOptions, ...options }).then((map2) => {
252
255
  if (!clone) {
253
256
  maps[code] = map2;
@@ -268,9 +271,12 @@ function createDictManager(createDictManagerOptions = {}) {
268
271
  }
269
272
  setClonedMap(/* @__PURE__ */ new Map());
270
273
  }, []);
271
- const getItem = react.useCallback((value) => {
272
- return value !== null && value !== void 0 ? state.map[value] : null;
273
- }, [state]);
274
+ const getItem = react.useCallback(
275
+ (value) => {
276
+ return value !== null && value !== void 0 ? state.map[value] : null;
277
+ },
278
+ [state]
279
+ );
274
280
  react.useEffect(() => {
275
281
  if (!remote || immediate) {
276
282
  if (clone) {
@@ -278,6 +284,9 @@ function createDictManager(createDictManagerOptions = {}) {
278
284
  } else {
279
285
  if (!globalLoadPromise) {
280
286
  globalLoadPromise = createPromise();
287
+ if (!clone) {
288
+ loadPromiseRef.current = globalLoadPromise;
289
+ }
281
290
  load();
282
291
  } else {
283
292
  globalLoadPromise.then(() => {
@@ -293,6 +302,9 @@ function createDictManager(createDictManagerOptions = {}) {
293
302
  } else {
294
303
  if (!globalLoadPromise) {
295
304
  globalLoadPromise = createPromise();
305
+ if (!clone) {
306
+ loadPromiseRef.current = globalLoadPromise;
307
+ }
296
308
  globalLoadPromise.resolve(void 0);
297
309
  }
298
310
  }
@@ -0,0 +1,3 @@
1
+ export type * from '../types';
2
+ export * from './react-dict-manager';
3
+ export { defineDictData } from '../util';
@@ -14,10 +14,11 @@ function createDictManager(createDictManagerOptions = {}) {
14
14
  const listenersMap = /* @__PURE__ */ Object.create(null);
15
15
  function emitChange(code) {
16
16
  var _a;
17
- const listeners = code ? (_a = listenersMap[code]) != null ? _a : [] : Object.values(listenersMap).flat();
18
- for (let listener of listeners) {
19
- listener();
20
- }
17
+ const listeners = code ? (_a = listenersMap[code]) != null ? _a : /* @__PURE__ */ new Set() : Object.values(listenersMap).reduce((acc, set) => {
18
+ set.forEach((listener) => acc.add(listener));
19
+ return acc;
20
+ }, /* @__PURE__ */ new Set());
21
+ listeners.forEach((listener) => listener());
21
22
  }
22
23
  function clear(code) {
23
24
  var _a;
@@ -68,9 +69,10 @@ function createDictManager(createDictManagerOptions = {}) {
68
69
  }
69
70
  function subscribeMap(listener) {
70
71
  var _a;
71
- listenersMap[code] = [...(_a = listenersMap[code]) != null ? _a : [], listener];
72
+ (_a = listenersMap[code]) != null ? _a : listenersMap[code] = /* @__PURE__ */ new Set();
73
+ listenersMap[code].add(listener);
72
74
  return () => {
73
- listenersMap[code] = listenersMap[code].filter((l) => l !== listener);
75
+ listenersMap[code].delete(listener);
74
76
  };
75
77
  }
76
78
  function getMapSnapshot() {
@@ -110,15 +112,16 @@ function createDictManager(createDictManagerOptions = {}) {
110
112
  const loadPromiseRef = useRef(
111
113
  !clone ? globalLoadPromise : createPromise()
112
114
  );
113
- const map = useSyncExternalStore(subscribeMap, getMapSnapshot);
115
+ const globalMap = useSyncExternalStore(subscribeMap, getMapSnapshot);
114
116
  const [clonedMap, setClonedMap] = useState(/* @__PURE__ */ new Map());
115
- const state = useMemo(
116
- () => createStateFromMap(!clone ? map : clonedMap),
117
- [clone, map, clonedMap]
118
- );
117
+ const map = !clone ? globalMap : clonedMap;
118
+ const state = useMemo(() => createStateFromMap(map), [map]);
119
119
  const load = useCallback((options) => {
120
120
  const oldLoadPromise = loadPromiseRef.current;
121
121
  loadPromiseRef.current = createPromise();
122
+ if (!clone) {
123
+ globalLoadPromise = loadPromiseRef.current;
124
+ }
122
125
  loadDict({ ...mergedOptions, ...options }).then((map2) => {
123
126
  if (!clone) {
124
127
  maps[code] = map2;
@@ -139,9 +142,12 @@ function createDictManager(createDictManagerOptions = {}) {
139
142
  }
140
143
  setClonedMap(/* @__PURE__ */ new Map());
141
144
  }, []);
142
- const getItem = useCallback((value) => {
143
- return value !== null && value !== void 0 ? state.map[value] : null;
144
- }, [state]);
145
+ const getItem = useCallback(
146
+ (value) => {
147
+ return value !== null && value !== void 0 ? state.map[value] : null;
148
+ },
149
+ [state]
150
+ );
145
151
  useEffect(() => {
146
152
  if (!remote || immediate) {
147
153
  if (clone) {
@@ -149,6 +155,9 @@ function createDictManager(createDictManagerOptions = {}) {
149
155
  } else {
150
156
  if (!globalLoadPromise) {
151
157
  globalLoadPromise = createPromise();
158
+ if (!clone) {
159
+ loadPromiseRef.current = globalLoadPromise;
160
+ }
152
161
  load();
153
162
  } else {
154
163
  globalLoadPromise.then(() => {
@@ -164,6 +173,9 @@ function createDictManager(createDictManagerOptions = {}) {
164
173
  } else {
165
174
  if (!globalLoadPromise) {
166
175
  globalLoadPromise = createPromise();
176
+ if (!clone) {
177
+ loadPromiseRef.current = globalLoadPromise;
178
+ }
167
179
  globalLoadPromise.resolve(void 0);
168
180
  }
169
181
  }
@@ -0,0 +1,6 @@
1
+ import type { CreateDictManagerOptions, DefineDict, DictMap, ExtraGetter, Fetch, Recordable } from '../types';
2
+ export declare function createDictManager<E extends ExtraGetter, F extends Fetch>(createDictManagerOptions?: CreateDictManagerOptions<E, F>): {
3
+ defineDict: DefineDict<E, F>;
4
+ clear: (code?: string) => void;
5
+ maps: Recordable<DictMap>;
6
+ };
@@ -0,0 +1,69 @@
1
+ import type { createPromise } from '../create-promise';
2
+ import type { Merge, MergeValues } from './merge';
3
+ import type { AnyFn, MaybeGetter, MaybePromise, Nil, OptionalRequired, Recordable, Simplify } from './tool';
4
+ export type DictValue = number | string;
5
+ export type DictItem = {
6
+ label: string;
7
+ value: DictValue;
8
+ };
9
+ export type DictItemRecord = DictItem & Recordable;
10
+ export type DictMap = Map<DictValue, DictItemRecord>;
11
+ export type LoadPromise = ReturnType<typeof createPromise<void>>;
12
+ export type Dict<K extends PropertyKey = PropertyKey, I extends Recordable = DictItem, O extends Recordable = Recordable> = {
13
+ list: I[];
14
+ E: {
15
+ [X in K]: X;
16
+ };
17
+ map: {
18
+ [X in K]: I;
19
+ };
20
+ loadPromise: LoadPromise;
21
+ load: (options?: O) => LoadPromise;
22
+ clear: () => void;
23
+ getItem: (value?: I['value'] | Nil) => I | Nil;
24
+ };
25
+ export type Fetch = (code: string, options: Recordable) => MaybePromise<DictItemRecord[]>;
26
+ type FetchOptions<F extends Fetch> = Parameters<F>[1] extends infer T ? T extends Nil ? {} : T : {};
27
+ export type ExtraGetter<D extends Dict<string> = Dict<string>> = (dict: D) => Recordable;
28
+ export interface CreateDictManagerOptions<E extends ExtraGetter, F extends Fetch> {
29
+ fetch?: F;
30
+ extra?: E;
31
+ transformer?: (value: DictValue) => DictValue;
32
+ itemTransformer?: (item: DictItemRecord) => any;
33
+ }
34
+ export type UseDictOptions = {
35
+ clone?: boolean;
36
+ immediate?: boolean;
37
+ refresh?: boolean;
38
+ } & Recordable;
39
+ type Options<F extends Fetch> = FetchOptions<F> & UseDictOptions;
40
+ type CreateDict<D extends Recordable<Recordable>, F extends Fetch> = Dict<keyof D, Simplify<Merge<[DictItem, MergeValues<D>]> extends infer Item ? Item extends never ? DictItem : Item extends Recordable ? OptionalRequired<Item, 'label' | 'value'> : DictItem : DictItem>, Simplify<Options<F>>>;
41
+ type _UseDict<E extends Recordable, D extends Recordable<Recordable>, F extends Fetch> = (options?: Simplify<Options<F>>) => CreateDict<D, F> & E;
42
+ export type UseDict<E extends Recordable, D extends Recordable<Recordable>, F extends Fetch> = _UseDict<E, D, F> & {
43
+ extend: (extendCode: string, extendOptions?: {
44
+ pickValues?: Simplify<keyof D>[];
45
+ omitValues?: Simplify<keyof D>[];
46
+ }) => UseDict<E, D, F>;
47
+ };
48
+ type Data<R extends boolean> = Recordable<(R extends true ? {
49
+ label?: string;
50
+ } : {
51
+ label: string;
52
+ }) & Recordable>;
53
+ export interface DefineDict<ME extends ExtraGetter, MF extends Fetch> {
54
+ <R extends boolean, F extends Fetch, D extends Data<R>, E extends ExtraGetter>(code: string, options?: MaybeGetter<{
55
+ remote?: R;
56
+ fetch?: F;
57
+ data?: D;
58
+ extra?: E;
59
+ transformer?: (value: DictValue) => DictValue;
60
+ itemTransformer?: (item: DictItemRecord) => any;
61
+ }>): UseDict<ReturnType<ME> & ReturnType<E>, D, F extends undefined ? MF : F>;
62
+ }
63
+ export type VDictItem<T extends AnyFn> = ReturnType<T> extends {
64
+ list: Array<infer R>;
65
+ } ? R : never;
66
+ export type VDictUnionValue<T extends AnyFn> = ReturnType<T> extends {
67
+ E: infer R;
68
+ } ? keyof R : never;
69
+ export {};
@@ -0,0 +1,3 @@
1
+ export * from './dict';
2
+ export * from './merge';
3
+ export * from './tool';
@@ -0,0 +1,37 @@
1
+ type TupleUnionKeys<T> = T extends any ? keyof T : never;
2
+ type TupleAllKeys<T extends any[]> = TupleUnionKeys<T[number]>;
3
+ type TupleGetUnionType<T extends any[], K extends PropertyKey> = T extends [
4
+ infer First,
5
+ ...infer Rest
6
+ ] ? (K extends keyof First ? First[K] : undefined) | TupleGetUnionType<Rest, K> : never;
7
+ type Widen<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;
8
+ type IsRequiredProperty<T, K extends keyof T> = {} extends Pick<T, K> ? false : true;
9
+ type IsAllRequired<T extends any[], K> = T extends [infer First, ...infer Rest] ? (K extends keyof First ? IsRequiredProperty<First, K> : false) extends true ? IsAllRequired<Rest, K> : false : true;
10
+ type TupleRequiredKeys<T extends any[]> = {
11
+ [K in TupleAllKeys<T>]: IsAllRequired<T, K> extends true ? K : never;
12
+ }[TupleAllKeys<T>];
13
+ export type Merge<T extends any[]> = {
14
+ [K in Exclude<TupleAllKeys<T>, TupleRequiredKeys<T>>]?: Widen<TupleGetUnionType<T, K>>;
15
+ } & {
16
+ [K in TupleRequiredKeys<T>]: Widen<TupleGetUnionType<T, K>>;
17
+ } extends infer O ? {
18
+ [P in keyof O]: O[P];
19
+ } : never;
20
+ type UnionKeys<T> = T extends any ? keyof T : never;
21
+ type UnionValueOf<T, K extends PropertyKey> = T extends any ? K extends keyof T ? T[K] : undefined : never;
22
+ type UnionIsAlwaysPresent<T, K extends PropertyKey> = [T] extends [infer U] ? U extends any ? K extends keyof U ? true : false : never : never;
23
+ type UnionRequiredKeys<T> = {
24
+ [K in UnionKeys<T>]: UnionIsAlwaysPresent<T, K> extends true ? K : never;
25
+ }[UnionKeys<T>];
26
+ type UnionOptionalKeys<T> = Exclude<UnionKeys<T>, UnionRequiredKeys<T>>;
27
+ export type MergeUnion<T> = {
28
+ [K in UnionOptionalKeys<T>]?: Widen<UnionValueOf<T, K>>;
29
+ } & {
30
+ [K in UnionRequiredKeys<T>]: Widen<UnionValueOf<T, K>>;
31
+ } extends infer O ? {
32
+ [P in keyof O]: O[P];
33
+ } : never;
34
+ export type MergeValues<T> = MergeUnion<{
35
+ [K in keyof T]: T[K];
36
+ }[keyof T]>;
37
+ export {};
@@ -0,0 +1,11 @@
1
+ export type Recordable<T = any> = Record<string, T>;
2
+ export type Nil = undefined | null;
3
+ export type MaybePromise<T> = T | Promise<T>;
4
+ export type Simplify<T> = {
5
+ [KeyType in keyof T]: T[KeyType];
6
+ } & {};
7
+ export type OptionalRequired<T, K extends keyof T> = Partial<T> & Required<Pick<T, K>>;
8
+ export type Getter<T> = () => T;
9
+ export type MaybeGetter<T> = T | Getter<T>;
10
+ export type AnyFn<Return = any, Args extends unknown[] = any[]> = (...args: Args) => Return;
11
+ export type PlainObject<T = any> = Record<PropertyKey, any>;
package/dist/util.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ import type { AnyFn, DictItemRecord, DictMap, DictValue, Merge, PlainObject, Recordable } from './types';
2
+ export declare const warn: (msg: string) => void;
3
+ export declare function isFunction(fn: unknown): fn is AnyFn;
4
+ export declare function clearObj(obj: Recordable): void;
5
+ export declare function mapToObj(map: DictMap, { obj, itemTransformer }?: {
6
+ obj?: Recordable<DictItemRecord>;
7
+ itemTransformer?: (item: DictItemRecord) => any;
8
+ }): Recordable<DictItemRecord>;
9
+ export declare function mapToList(map: DictMap, { list, itemTransformer }?: {
10
+ list?: DictItemRecord[];
11
+ itemTransformer?: (item: DictItemRecord) => any;
12
+ }): DictItemRecord[];
13
+ type MapOptions = {
14
+ map?: DictMap;
15
+ pickValues?: DictValue[];
16
+ omitValues?: DictValue[];
17
+ transformer?: (value: DictValue) => DictValue;
18
+ };
19
+ export declare function toMap(data: Recordable<DictItemRecord> | DictItemRecord[], { map, pickValues, omitValues, transformer }?: MapOptions): DictMap;
20
+ export declare const defineDictData: <T>(data: T) => T;
21
+ export declare function isPlainObject(obj: any): obj is PlainObject;
22
+ export declare function merge<T extends PlainObject, S extends PlainObject[]>(target: T, ...sources: S): Merge<[T, ...S]>;
23
+ export declare function cloneDeep<T>(value: T): T;
24
+ export {};
@@ -0,0 +1,3 @@
1
+ export type * from '../types';
2
+ export * from './vue-dict-manager';
3
+ export { defineDictData } from '../util';
@@ -0,0 +1,12 @@
1
+ import type { CreateDictManagerOptions, DefineDict, DictValue, ExtraGetter, Fetch } from '../types';
2
+ export declare function createDictManager<E extends ExtraGetter, F extends Fetch>(createDictManagerOptions?: CreateDictManagerOptions<E, F>): {
3
+ defineDict: DefineDict<E, F>;
4
+ clear: (code?: string) => void;
5
+ maps: {
6
+ readonly [x: string]: ReadonlyMap<DictValue, {
7
+ readonly [x: string]: any;
8
+ readonly label: string;
9
+ readonly value: DictValue;
10
+ }>;
11
+ };
12
+ };
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "v-dict",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "type": "module",
5
5
  "description": "Vue3 & React Dict Manager",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/rrryyi/v-dict"
9
9
  },
10
- "main": "dist/index.js",
11
- "module": "dist/index.mjs",
10
+ "main": "dist/index.cjs",
11
+ "module": "dist/index.js",
12
12
  "typings": "dist/index.d.ts",
13
13
  "exports": {
14
14
  ".": {
@@ -39,11 +39,9 @@
39
39
  "vue",
40
40
  "react",
41
41
  "dict",
42
- "dictionary",
43
- "vue3",
44
- "react-dict"
42
+ "dictionary"
45
43
  ],
46
- "author": "",
44
+ "author": "rrryyi",
47
45
  "license": "MIT",
48
46
  "dependencies": {},
49
47
  "peerDependencies": {