@hairy/react-lib 1.23.0 → 1.25.0

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/dist/index.cjs CHANGED
@@ -38,12 +38,14 @@ __export(index_exports, {
38
38
  Switch: () => Switch,
39
39
  Then: () => Then,
40
40
  Trans: () => Trans,
41
+ Trigger: () => Trigger,
41
42
  Unless: () => Unless,
42
43
  cls: () => cls,
43
44
  defineAsyncStore: () => defineAsyncStore,
44
45
  defineStore: () => defineStore,
45
46
  proxyWithPersistant: () => proxyWithPersistant,
46
- ref: () => ref,
47
+ ref: () => ref2,
48
+ track: () => track,
47
49
  useAsyncCallback: () => useAsyncCallback,
48
50
  useAsyncState: () => useAsyncState,
49
51
  useDebounce: () => useDebounce,
@@ -92,6 +94,51 @@ cls.append = function(value, newClass) {
92
94
  return value ? `${value} ${newClass}` : newClass;
93
95
  };
94
96
 
97
+ // ../util-core/src/util/noop.ts
98
+ var noop = () => {
99
+ };
100
+
101
+ // ../util-core/src/util/deferred.ts
102
+ var Deferred = class extends Promise {
103
+ resolve;
104
+ reject;
105
+ constructor(executor = noop) {
106
+ let _resolve, _reject;
107
+ super((resolve_, reject_) => {
108
+ _resolve = resolve_;
109
+ _reject = reject_;
110
+ return executor(resolve_, reject_);
111
+ });
112
+ this.resolve = (value) => {
113
+ _resolve(value);
114
+ return this;
115
+ };
116
+ this.reject = (reason) => {
117
+ _reject(reason);
118
+ return this;
119
+ };
120
+ }
121
+ };
122
+
123
+ // ../util-core/src/util/json.ts
124
+ function jsonTryParse(text) {
125
+ try {
126
+ return JSON.parse(text || "");
127
+ } catch {
128
+ return void 0;
129
+ }
130
+ }
131
+
132
+ // src/utils/track.ts
133
+ var import_valtio = require("valtio");
134
+ function track(fn, ...args) {
135
+ const deferred = (0, import_valtio.ref)(new Deferred());
136
+ const exposer = { fn, args, deferred, id: ++Trigger.id };
137
+ Trigger.tasks.set(exposer.id, exposer);
138
+ deferred.then(() => Trigger.tasks.delete(exposer.id));
139
+ return deferred;
140
+ }
141
+
95
142
  // src/utils/wrapper.ts
96
143
  var import_react = require("react");
97
144
  function wrapper(tag, props, children) {
@@ -203,9 +250,36 @@ function renderNodes(tokens, values) {
203
250
  });
204
251
  }
205
252
 
206
- // src/hooks/ref.ts
253
+ // src/components/utils/Trigger.ts
207
254
  var import_react7 = require("react");
208
- function ref(value) {
255
+ var import_valtio2 = require("valtio");
256
+ var import_utils8 = require("valtio/utils");
257
+ var pendingTasks = (0, import_utils8.proxyMap)();
258
+ function createTracker(exposer) {
259
+ const Component = () => {
260
+ try {
261
+ exposer.deferred.resolve(exposer.fn(...exposer.args));
262
+ } catch (error) {
263
+ exposer.deferred.reject(error);
264
+ }
265
+ return null;
266
+ };
267
+ Component.key = exposer.id;
268
+ return Component;
269
+ }
270
+ function renderTracker(Tracker) {
271
+ return (0, import_react7.createElement)(Tracker, { key: Tracker.key });
272
+ }
273
+ function Trigger() {
274
+ const values = [...(0, import_valtio2.useSnapshot)(pendingTasks).values()];
275
+ return values.map(createTracker).map(renderTracker);
276
+ }
277
+ Trigger.id = 0;
278
+ Trigger.tasks = pendingTasks;
279
+
280
+ // src/hooks/ref.ts
281
+ var import_react8 = require("react");
282
+ function ref2(value) {
209
283
  function define(value2) {
210
284
  if (typeof value2 === "function")
211
285
  return (prev) => define(value2(prev.value));
@@ -215,14 +289,14 @@ function ref(value) {
215
289
  enumerable: true
216
290
  });
217
291
  }
218
- const [ref2, set] = (0, import_react7.useState)(define(value));
219
- return ref2;
292
+ const [ref3, set] = (0, import_react8.useState)(define(value));
293
+ return ref3;
220
294
  }
221
295
 
222
296
  // src/hooks/useAsyncCallback.ts
223
- var import_react8 = require("react");
297
+ var import_react9 = require("react");
224
298
  function useAsyncCallback(fun) {
225
- const [state, set] = (0, import_react8.useState)({ loading: false });
299
+ const [state, set] = (0, import_react9.useState)({ loading: false });
226
300
  async function execute(...args) {
227
301
  set({ loading: true });
228
302
  return fun(...args).then((value) => {
@@ -237,11 +311,11 @@ function useAsyncCallback(fun) {
237
311
  }
238
312
 
239
313
  // src/hooks/useAsyncState.ts
240
- var import_react9 = require("react");
314
+ var import_react10 = require("react");
241
315
  function useAsyncState(fun, deps = [], options) {
242
- const [value, set] = (0, import_react9.useState)(options?.initial);
316
+ const [value, set] = (0, import_react10.useState)(options?.initial);
243
317
  const [loading, execute, error] = useAsyncCallback(async (...args) => fun(...args).then(set));
244
- (0, import_react9.useEffect)(
318
+ (0, import_react10.useEffect)(
245
319
  () => {
246
320
  execute();
247
321
  },
@@ -251,10 +325,10 @@ function useAsyncState(fun, deps = [], options) {
251
325
  }
252
326
 
253
327
  // src/hooks/useDebounce.ts
254
- var import_react10 = require("react");
328
+ var import_react11 = require("react");
255
329
  function useDebounce(value, delay) {
256
- const [debouncedValue, setDebouncedValue] = (0, import_react10.useState)(value);
257
- (0, import_react10.useEffect)(() => {
330
+ const [debouncedValue, setDebouncedValue] = (0, import_react11.useState)(value);
331
+ (0, import_react11.useEffect)(() => {
258
332
  const handler = setTimeout(() => setDebouncedValue(value), delay);
259
333
  return () => clearTimeout(handler);
260
334
  }, [value, delay]);
@@ -263,14 +337,14 @@ function useDebounce(value, delay) {
263
337
 
264
338
  // src/hooks/useEventBus.ts
265
339
  var import_mitt = __toESM(require("mitt"), 1);
266
- var import_react11 = require("react");
340
+ var import_react12 = require("react");
267
341
  var emitter = (0, import_mitt.default)();
268
342
  function useEventBus(key) {
269
- const onRef = (0, import_react11.useRef)();
343
+ const onRef = (0, import_react12.useRef)();
270
344
  function on(listener) {
271
345
  emitter.on(key, listener);
272
346
  onRef.current = listener;
273
- (0, import_react11.useEffect)(() => {
347
+ (0, import_react12.useEffect)(() => {
274
348
  if (!onRef.current)
275
349
  return;
276
350
  emitter.off(key, onRef.current);
@@ -293,9 +367,9 @@ function useEventBus(key) {
293
367
  }
294
368
 
295
369
  // ../../node_modules/.pnpm/react-use@17.6.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/react-use/esm/useEffectOnce.js
296
- var import_react12 = require("react");
370
+ var import_react13 = require("react");
297
371
  var useEffectOnce = function(effect) {
298
- (0, import_react12.useEffect)(effect, []);
372
+ (0, import_react13.useEffect)(effect, []);
299
373
  };
300
374
  var useEffectOnce_default = useEffectOnce;
301
375
 
@@ -331,23 +405,23 @@ function fetchRequestIntercept(intercept) {
331
405
  }
332
406
 
333
407
  // src/hooks/useMounted.ts
334
- var import_react13 = require("react");
408
+ var import_react14 = require("react");
335
409
  function useMounted() {
336
- const [mounted, setMounted] = (0, import_react13.useState)(false);
337
- (0, import_react13.useEffect)(() => setMounted(true), []);
410
+ const [mounted, setMounted] = (0, import_react14.useState)(false);
411
+ (0, import_react14.useEffect)(() => setMounted(true), []);
338
412
  return mounted;
339
413
  }
340
414
 
341
415
  // src/hooks/useWatch.ts
342
- var import_react14 = require("react");
416
+ var import_react15 = require("react");
343
417
  function useWatch(source, callback, options = {}) {
344
- const firstUpdate = (0, import_react14.useRef)(false);
345
- const then = (0, import_react14.useRef)();
346
- const deps = (0, import_react14.useMemo)(
418
+ const firstUpdate = (0, import_react15.useRef)(false);
419
+ const then = (0, import_react15.useRef)();
420
+ const deps = (0, import_react15.useMemo)(
347
421
  () => Array.isArray(source) ? source : [source],
348
422
  [source]
349
423
  );
350
- (0, import_react14.useEffect)(() => {
424
+ (0, import_react15.useEffect)(() => {
351
425
  if (!firstUpdate.current)
352
426
  recordFirst();
353
427
  else
@@ -369,23 +443,14 @@ function useWhenever(source, cb, options) {
369
443
  }
370
444
 
371
445
  // src/storage/defineAsyncStore.ts
372
- var import_utils8 = require("valtio/utils");
446
+ var import_utils10 = require("valtio/utils");
373
447
 
374
448
  // src/storage/defineStore.ts
375
- var import_react15 = require("react");
376
- var import_valtio2 = require("valtio");
377
-
378
- // ../util-core/src/util/json.ts
379
- function jsonTryParse(text) {
380
- try {
381
- return JSON.parse(text || "");
382
- } catch {
383
- return void 0;
384
- }
385
- }
449
+ var import_react16 = require("react");
450
+ var import_valtio4 = require("valtio");
386
451
 
387
452
  // src/storage/persistant.ts
388
- var import_valtio = require("valtio");
453
+ var import_valtio3 = require("valtio");
389
454
  function proxyWithPersistant(keyOrOptions, initialObject) {
390
455
  let options;
391
456
  if (typeof keyOrOptions === "string") {
@@ -395,8 +460,8 @@ function proxyWithPersistant(keyOrOptions, initialObject) {
395
460
  }
396
461
  const storage = options.storage || (typeof localStorage !== "undefined" ? localStorage : void 0);
397
462
  typeof keyOrOptions === "string" && (keyOrOptions = { id: keyOrOptions });
398
- const state = (0, import_valtio.proxy)(jsonTryParse(storage?.getItem(options.id)) || initialObject);
399
- (0, import_valtio.subscribe)(state, () => {
463
+ const state = (0, import_valtio3.proxy)(jsonTryParse(storage?.getItem(options.id)) || initialObject);
464
+ (0, import_valtio3.subscribe)(state, () => {
400
465
  const pick = options.pick || Object.keys(state);
401
466
  const statePick = {};
402
467
  for (const key of pick)
@@ -415,18 +480,18 @@ function defineStore(store, options = {}) {
415
480
  status.finished = false;
416
481
  status.loading = false;
417
482
  status.error = null;
418
- const $status = (0, import_valtio2.proxy)(status);
419
- const $state = options.persist ? proxyWithPersistant(options.persist, state) : (0, import_valtio2.proxy)(state);
483
+ const $status = (0, import_valtio4.proxy)(status);
484
+ const $state = options.persist ? proxyWithPersistant(options.persist, state) : (0, import_valtio4.proxy)(state);
420
485
  const $actions = {};
421
486
  const $getters = {};
422
487
  setupActions($state, actions, $actions, $status);
423
488
  setupGetters(state, $state, getters, $getters);
424
489
  setupStatus($actions, $status);
425
490
  function $subscribe(listener) {
426
- return (0, import_valtio2.subscribe)($state, () => listener($state));
491
+ return (0, import_valtio4.subscribe)($state, () => listener($state));
427
492
  }
428
493
  $subscribe.status = function(listener) {
429
- return (0, import_valtio2.subscribe)($status, () => listener($status));
494
+ return (0, import_valtio4.subscribe)($status, () => listener($status));
430
495
  };
431
496
  function $patch(patch) {
432
497
  if (typeof patch === "function")
@@ -435,10 +500,10 @@ function defineStore(store, options = {}) {
435
500
  Object.assign($state, patch);
436
501
  }
437
502
  function $signal(fn) {
438
- return (0, import_react15.createElement)(() => fn((0, import_valtio2.useSnapshot)($state)));
503
+ return (0, import_react16.createElement)(() => fn((0, import_valtio4.useSnapshot)($state)));
439
504
  }
440
505
  $signal.status = function(fn) {
441
- return (0, import_react15.createElement)(() => fn((0, import_valtio2.useSnapshot)($status)));
506
+ return (0, import_react16.createElement)(() => fn((0, import_valtio4.useSnapshot)($status)));
442
507
  };
443
508
  return {
444
509
  $subscribe,
@@ -451,7 +516,7 @@ function defineStore(store, options = {}) {
451
516
  ...$actions
452
517
  };
453
518
  }
454
- function track(action, status) {
519
+ function track2(action, status) {
455
520
  let loadings = 0;
456
521
  const tracking = () => loadings++ === 0 && (status.loading = true);
457
522
  const done = () => !--loadings && (status.loading = false);
@@ -478,7 +543,7 @@ function track(action, status) {
478
543
  function setupActions($state, actions, $actions, $status) {
479
544
  for (const key in actions) {
480
545
  $status[key] = { finished: false, loading: false, error: null };
481
- $actions[key] = track(actions[key].bind($state), $status[key]);
546
+ $actions[key] = track2(actions[key].bind($state), $status[key]);
482
547
  }
483
548
  }
484
549
  function setupGetters(state, $state, getters, $getters) {
@@ -529,7 +594,7 @@ function defineAsyncStore(fetch, options = {}) {
529
594
  },
530
595
  { persist: options.persist ? { id: options.persist, pick: ["value"] } : void 0 }
531
596
  );
532
- (0, import_utils8.watch)((get) => {
597
+ (0, import_utils10.watch)((get) => {
533
598
  const status = get(store.$status.fetch);
534
599
  store.$state.error = status.error;
535
600
  store.$state.loading = status.loading;
@@ -540,15 +605,15 @@ function defineAsyncStore(fetch, options = {}) {
540
605
  }
541
606
 
542
607
  // src/storage/useStatus.tsx
543
- var import_valtio3 = require("valtio");
608
+ var import_valtio5 = require("valtio");
544
609
  function useStatus(store) {
545
- return (0, import_valtio3.useSnapshot)(store.$status);
610
+ return (0, import_valtio5.useSnapshot)(store.$status);
546
611
  }
547
612
 
548
613
  // src/storage/useStore.ts
549
- var import_valtio4 = require("valtio");
614
+ var import_valtio6 = require("valtio");
550
615
  function useStore(store) {
551
- return (0, import_valtio4.useSnapshot)(store.$state);
616
+ return (0, import_valtio6.useSnapshot)(store.$state);
552
617
  }
553
618
  // Annotate the CommonJS export names for ESM import in node:
554
619
  0 && (module.exports = {
@@ -560,12 +625,14 @@ function useStore(store) {
560
625
  Switch,
561
626
  Then,
562
627
  Trans,
628
+ Trigger,
563
629
  Unless,
564
630
  cls,
565
631
  defineAsyncStore,
566
632
  defineStore,
567
633
  proxyWithPersistant,
568
634
  ref,
635
+ track,
569
636
  useAsyncCallback,
570
637
  useAsyncState,
571
638
  useDebounce,
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BooleanLike, PromiseFn, PromiseType, AnyFn } from '@hairy/utils';
1
+ import { AnyFn, BooleanLike, Deferred, PromiseFn, PromiseType } from '@hairy/utils';
2
2
  import * as react from 'react';
3
3
  import { JSX, ReactNode, PropsWithChildren, ReactElement, FC, ComponentClass, SetStateAction, DetailedHTMLProps, HTMLAttributes } from 'react';
4
4
  import * as valtio from 'valtio';
@@ -19,6 +19,18 @@ declare namespace cls {
19
19
  var append: (value: any, newClass: any) => any;
20
20
  }
21
21
 
22
+ /**
23
+ * @requires `Trigger` component to be mounted in the tree.
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * // Obtain externally
28
+ * import { track } from '@hairy/lib-react'
29
+ * const context = await track(() => useContext(YourContext))
30
+ * console.log(context) // { ... }
31
+ */
32
+ declare function track<T extends AnyFn>(fn: T, ...args: Parameters<T>): Promise<ReturnType<T>>;
33
+
22
34
  type WrapperTag = keyof JSX.IntrinsicElements | Function;
23
35
  type WrapperProps<Kag extends keyof JSX.IntrinsicElements | React.FC | unknown> = {
24
36
  tag?: Kag;
@@ -79,6 +91,40 @@ interface TransProps {
79
91
  }
80
92
  declare function Trans({ i18nKey, ...additionalProps }: TransProps): ReactNode[];
81
93
 
94
+ interface Exposer {
95
+ deferred: Deferred<any>;
96
+ args: any[];
97
+ fn: AnyFn;
98
+ id: number;
99
+ }
100
+ /**
101
+ * @example
102
+ * ```tsx
103
+ * import { Trigger } from '@hairy/lib-react'
104
+ *
105
+ * // Use triggers to capture context
106
+ * function App() {
107
+ * return (
108
+ * <YourContext.Provider>
109
+ * <Trigger />
110
+ * </YourContext.Provider>
111
+ * )
112
+ * }
113
+ *
114
+ * // Obtain externally
115
+ * import { track } from '@hairy/lib-react'
116
+ * const context = await track(() => useContext(YourContext))
117
+ * console.log(context) // { ... }
118
+ * ```
119
+ */
120
+ declare function Trigger(): ReactNode[];
121
+ declare namespace Trigger {
122
+ var id: number;
123
+ var tasks: Map<number, Exposer> & {
124
+ $$valtioSnapshot: Omit<Map<number, Exposer>, "set" | "delete" | "clear">;
125
+ };
126
+ }
127
+
82
128
  interface Ref<S> {
83
129
  set value(action: SetStateAction<S>);
84
130
  get value(): S;
@@ -258,4 +304,4 @@ declare function useStore<S extends object, A extends Actions<S>, G extends Gett
258
304
 
259
305
  type PropsWithDetailedHTML<T = HTMLDivElement> = DetailedHTMLProps<HTMLAttributes<T>, T>;
260
306
 
261
- export { type Argument, type ArgumentArray, type AsyncStoreOptions, Case, type CaseProps, Default, type DefaultProps, Else, type ElseProps, type EventBusListener, type FetchRequestInterceptCallback, type FetchResponseInterceptCallback, If, type IfProps, type InjectComponent, Injector, type InjectorProps, type Mapping, type PersistantOptions, type PropsWithDetailedHTML, type ReadonlyArgumentArray, type Ref, Switch, type SwitchProps, Then, type ThenProps, Trans, type TransProps, Unless, type UnlessProps, type UseAsyncStateOptions, type Value, type WatchCallback, type WatchOptions, type WrapperProps, type WrapperTag, cls, defineAsyncStore, defineStore, proxyWithPersistant, ref, useAsyncCallback, useAsyncState, useDebounce, useEventBus, useFetchRequestIntercept, useFetchResponseIntercept, useMounted, useStatus, useStore, useWatch, useWhenever, wrapper };
307
+ export { type Argument, type ArgumentArray, type AsyncStoreOptions, Case, type CaseProps, Default, type DefaultProps, Else, type ElseProps, type EventBusListener, type Exposer, type FetchRequestInterceptCallback, type FetchResponseInterceptCallback, If, type IfProps, type InjectComponent, Injector, type InjectorProps, type Mapping, type PersistantOptions, type PropsWithDetailedHTML, type ReadonlyArgumentArray, type Ref, Switch, type SwitchProps, Then, type ThenProps, Trans, type TransProps, Trigger, Unless, type UnlessProps, type UseAsyncStateOptions, type Value, type WatchCallback, type WatchOptions, type WrapperProps, type WrapperTag, cls, defineAsyncStore, defineStore, proxyWithPersistant, ref, track, useAsyncCallback, useAsyncState, useDebounce, useEventBus, useFetchRequestIntercept, useFetchResponseIntercept, useMounted, useStatus, useStore, useWatch, useWhenever, wrapper };