cogsbox-state 0.5.476 → 0.5.478

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/src/plugins.ts CHANGED
@@ -1,14 +1,71 @@
1
1
  import { z } from 'zod';
2
2
  import type React from 'react';
3
3
  import { StateObject, UpdateTypeDetail } from './CogsState';
4
- import { getGlobalStore } from './store';
4
+ import {
5
+ getGlobalStore,
6
+ getAllFieldElements,
7
+ setAllFieldsDisabled,
8
+ } from './store';
5
9
  import { ClientActivityEvent } from './pluginStore';
10
+ import { RefObject } from 'react';
6
11
 
7
12
  type Prettify<T> = { [K in keyof T]: T[K] } & {};
8
13
 
14
+ export type ChainMethodTarget =
15
+ | 'any'
16
+ | 'array'
17
+ | 'object'
18
+ | 'primitive'
19
+ | 'boolean';
20
+
21
+ export type ChainMethodContext<TOptions = any, THookReturn = any> = {
22
+ stateKey: string;
23
+ path: string[];
24
+ pluginName: string;
25
+ options: TOptions | undefined;
26
+ hookData?: THookReturn;
27
+ $get: () => any;
28
+ $update: (payload: any) => { synced: () => void };
29
+ $applyOperation: (operation: any, metaData?: Record<string, any>) => void;
30
+ getFieldMetaData: () => any;
31
+ setFieldMetaData: (data: Record<string, any>) => void;
32
+ removeFieldMetaData: () => void;
33
+ getFieldRefs: () => RefObject<any>[];
34
+ getFieldElements: () => HTMLElement[];
35
+ setFieldDisabled: (disabled: boolean) => void;
36
+ };
37
+
38
+ export type ChainMethodHandler = (
39
+ ctx: ChainMethodContext<any, any>,
40
+ ...args: any[]
41
+ ) => any;
42
+
43
+ export type ChainMethodDefinition<
44
+ THandler extends ChainMethodHandler = ChainMethodHandler,
45
+ > = {
46
+ target: ChainMethodTarget;
47
+ pathPattern?: string[];
48
+ handler: THandler;
49
+ };
50
+
51
+ export type ChainMethodDefinitions = Record<string, ChainMethodDefinition<any>>;
52
+
53
+ type ChainMethodCallable<THandler> = THandler extends (
54
+ ctx: any,
55
+ ...args: infer TArgs
56
+ ) => infer TReturn
57
+ ? (...args: TArgs) => TReturn
58
+ : never;
59
+
60
+ export type ChainMethodCallables<TMethods> = {
61
+ [K in keyof TMethods]: TMethods[K] extends ChainMethodDefinition<infer TFn>
62
+ ? ChainMethodCallable<TFn>
63
+ : never;
64
+ };
65
+
9
66
  export type KeyedTypes<TMap extends Record<string, any>> = {
10
67
  __key: 'keyed';
11
- map: { [K in keyof TMap]: TMap[K] };
68
+ map: TMap;
12
69
  };
13
70
 
14
71
  export const keyedSchema = <TMap extends Record<string, any>>() =>
@@ -58,6 +115,9 @@ type ScopedMetadataMethods<TFieldMetaData> = {
58
115
  getFieldMetaData: () => TFieldMetaData | undefined;
59
116
  setFieldMetaData: (data: Partial<TFieldMetaData>) => void;
60
117
  removeFieldMetaData: () => void;
118
+ getFieldRefs: () => RefObject<any>[];
119
+ getFieldElements: () => HTMLElement[];
120
+ setFieldDisabled: (disabled: boolean) => void;
61
121
  };
62
122
 
63
123
  // These are the existing global methods that still require a path.
@@ -65,6 +125,11 @@ type GlobalMetadataMethods<TFieldMetaData> = {
65
125
  getFieldMetaData: (path: string[]) => TFieldMetaData | undefined;
66
126
  setFieldMetaData: (path: string[], data: Partial<TFieldMetaData>) => void;
67
127
  removeFieldMetaData: (path: string[]) => void;
128
+ getFieldRefs: (path: string[]) => RefObject<any>[];
129
+ getFieldElements: (path: string[]) => HTMLElement[];
130
+ setFieldDisabled: (path: string[], disabled: boolean) => void;
131
+ getAllFieldElements: () => HTMLElement[];
132
+ setAllFieldsDisabled: (disabled: boolean) => void;
68
133
  };
69
134
  // Simplified: All params use the same TFieldMetaData type
70
135
  export type UseHookParams<
@@ -193,9 +258,12 @@ export type CogsPlugin<
193
258
  THookReturn,
194
259
  TPluginMetaData,
195
260
  TFieldMetaData,
261
+ TChainMethods extends ChainMethodDefinitions = {},
196
262
  > = {
197
263
  name: TName;
198
264
 
265
+ initialState?: () => Record<string, unknown>;
266
+
199
267
  useHook?: (
200
268
  params: UseHookParams<TOptions, TPluginMetaData, TFieldMetaData, any>
201
269
  ) => THookReturn;
@@ -239,6 +307,8 @@ export type CogsPlugin<
239
307
  any
240
308
  >
241
309
  ) => React.ReactNode;
310
+
311
+ chainMethods?: TChainMethods;
242
312
  };
243
313
 
244
314
  // Metadata helpers
@@ -276,6 +346,45 @@ export function createMetadataContext<TPluginMetaData, TFieldMetaData>(
276
346
  getGlobalStore
277
347
  .getState()
278
348
  .removePluginMetaData(stateKey, path, pluginName),
349
+ getFieldRefs: (path: string[]): RefObject<any>[] => {
350
+ const meta = getGlobalStore.getState().getShadowMetadata(stateKey, path);
351
+ if (!meta?.clientActivityState?.elements) return [];
352
+ const refs: RefObject<any>[] = [];
353
+ meta.clientActivityState.elements.forEach((entry: any) => {
354
+ if (entry.domRef?.current) refs.push(entry.domRef);
355
+ });
356
+ return refs;
357
+ },
358
+
359
+ getFieldElements: (path: string[]): HTMLElement[] => {
360
+ const meta = getGlobalStore.getState().getShadowMetadata(stateKey, path);
361
+ if (!meta?.clientActivityState?.elements) return [];
362
+ const elements: HTMLElement[] = [];
363
+ meta.clientActivityState.elements.forEach((entry: any) => {
364
+ if (entry.domRef?.current) elements.push(entry.domRef.current);
365
+ });
366
+ return elements;
367
+ },
368
+
369
+ setFieldDisabled: (path: string[], disabled: boolean) => {
370
+ const meta = getGlobalStore.getState().getShadowMetadata(stateKey, path);
371
+ if (!meta?.clientActivityState?.elements) return;
372
+ meta.clientActivityState.elements.forEach((entry: any) => {
373
+ const el = entry.domRef?.current;
374
+ if (!el) return;
375
+ if ('disabled' in el) el.disabled = disabled;
376
+ else {
377
+ el.style.pointerEvents = disabled ? 'none' : '';
378
+ el.setAttribute('aria-disabled', String(disabled));
379
+ }
380
+ });
381
+ },
382
+ getAllFieldElements: (): HTMLElement[] => {
383
+ return getAllFieldElements(stateKey);
384
+ },
385
+
386
+ setAllFieldsDisabled: (disabled: boolean): void =>
387
+ setAllFieldsDisabled(stateKey, disabled),
279
388
  };
280
389
  }
281
390
 
@@ -290,19 +399,54 @@ export function createScopedMetadataContext<TPluginMetaData, TFieldMetaData>(
290
399
  );
291
400
 
292
401
  return {
293
- // Return the global methods for plugin metadata
294
402
  ...globalContext,
295
- // Override the field methods with new, path-scoped versions
296
403
  getFieldMetaData: (): TFieldMetaData | undefined =>
297
404
  globalContext.getFieldMetaData(path),
298
-
299
405
  setFieldMetaData: (data: Partial<TFieldMetaData>) =>
300
406
  globalContext.setFieldMetaData(path, data),
301
-
302
407
  removeFieldMetaData: () => globalContext.removeFieldMetaData(path),
408
+
409
+ // NEW: Direct access to the DOM refs for this field
410
+ getFieldRefs: (): RefObject<any>[] => {
411
+ const meta = getGlobalStore.getState().getShadowMetadata(stateKey, path);
412
+ if (!meta?.clientActivityState?.elements) return [];
413
+ const refs: RefObject<any>[] = [];
414
+ meta.clientActivityState.elements.forEach((entry: any) => {
415
+ if (entry.domRef?.current) {
416
+ refs.push(entry.domRef);
417
+ }
418
+ });
419
+ return refs;
420
+ },
421
+
422
+ getFieldElements: (): HTMLElement[] => {
423
+ const meta = getGlobalStore.getState().getShadowMetadata(stateKey, path);
424
+ if (!meta?.clientActivityState?.elements) return [];
425
+ const elements: HTMLElement[] = [];
426
+ meta.clientActivityState.elements.forEach((entry: any) => {
427
+ if (entry.domRef?.current) {
428
+ elements.push(entry.domRef.current);
429
+ }
430
+ });
431
+ return elements;
432
+ },
433
+
434
+ setFieldDisabled: (disabled: boolean) => {
435
+ const meta = getGlobalStore.getState().getShadowMetadata(stateKey, path);
436
+ if (!meta?.clientActivityState?.elements) return;
437
+ meta.clientActivityState.elements.forEach((entry: any) => {
438
+ const el = entry.domRef?.current;
439
+ if (!el) return;
440
+ if ('disabled' in el) {
441
+ el.disabled = disabled;
442
+ } else {
443
+ el.style.pointerEvents = disabled ? 'none' : '';
444
+ el.setAttribute('aria-disabled', String(disabled));
445
+ }
446
+ });
447
+ },
303
448
  };
304
449
  }
305
-
306
450
  // Add this type export - derives from what createScopedMetadataContext returns
307
451
  export type ScopedMetadataContext<TFieldMetaData = any> = {
308
452
  getFieldMetaData: () => TFieldMetaData | undefined;
@@ -323,13 +467,88 @@ type ZodObjOutput<T extends z.ZodObject<any>> = {
323
467
  type OutputOf<T extends z.ZodTypeAny> =
324
468
  T extends z.ZodObject<any> ? Prettify<ZodObjOutput<T>> : z.output<T>;
325
469
 
470
+ type MethodFactory = <THandler extends ChainMethodHandler>(
471
+ handler: THandler
472
+ ) => ChainMethodDefinition<THandler>;
473
+
474
+ type PathMethodFactory = MethodFactory & {
475
+ array: MethodFactory;
476
+ object: MethodFactory;
477
+ primitive: MethodFactory;
478
+ boolean: MethodFactory;
479
+ field: MethodFactory;
480
+ };
481
+
482
+ type MethodsBuilderParams = {
483
+ path: (selector: (state: any) => any) => PathMethodFactory;
484
+ array: MethodFactory;
485
+ object: MethodFactory;
486
+ primitive: MethodFactory;
487
+ boolean: MethodFactory;
488
+ field: MethodFactory;
489
+ };
490
+
491
+ const createMethodDefinition =
492
+ (target: ChainMethodTarget, pathPattern?: string[]): MethodFactory =>
493
+ (handler) => ({
494
+ target,
495
+ pathPattern,
496
+ handler,
497
+ });
498
+
499
+ const createPathRecorder = () => {
500
+ const path: string[] = [];
501
+
502
+ const recorder = new Proxy(
503
+ {},
504
+ {
505
+ get(_target, prop) {
506
+ if (typeof prop !== 'string') return recorder;
507
+ if (prop === 'then') return undefined;
508
+ path.push(prop === '$' ? '*' : prop);
509
+ return recorder;
510
+ },
511
+ }
512
+ );
513
+
514
+ return { recorder, path };
515
+ };
516
+
517
+ const createMethodsBuilderParams = (): MethodsBuilderParams => {
518
+ const path = (selector: (state: any) => any): PathMethodFactory => {
519
+ const { recorder, path: pathPattern } = createPathRecorder();
520
+ selector(recorder);
521
+
522
+ const base = createMethodDefinition(
523
+ 'any',
524
+ pathPattern
525
+ ) as PathMethodFactory;
526
+ base.array = createMethodDefinition('array', pathPattern);
527
+ base.object = createMethodDefinition('object', pathPattern);
528
+ base.primitive = createMethodDefinition('primitive', pathPattern);
529
+ base.boolean = createMethodDefinition('boolean', pathPattern);
530
+ base.field = createMethodDefinition('any', pathPattern);
531
+
532
+ return base;
533
+ };
534
+
535
+ return {
536
+ path,
537
+ array: createMethodDefinition('array'),
538
+ object: createMethodDefinition('object'),
539
+ primitive: createMethodDefinition('primitive'),
540
+ boolean: createMethodDefinition('boolean'),
541
+ field: createMethodDefinition('any'),
542
+ };
543
+ };
544
+
326
545
  export function createPluginContext<
327
- O extends z.ZodTypeAny,
546
+ O extends z.ZodTypeAny | undefined = undefined,
328
547
  PM extends z.ZodTypeAny | undefined = undefined,
329
548
  FM extends z.ZodTypeAny | undefined = undefined,
330
- >(schemas: { options: O; pluginMetaData?: PM; fieldMetaData?: FM }) {
549
+ >(schemas?: { options?: O; pluginMetaData?: PM; fieldMetaData?: FM }) {
331
550
  // Crucial: compute from the generic params, not from an object-indexed optional type
332
- type Options = OutputOf<O>;
551
+ type Options = O extends z.ZodTypeAny ? OutputOf<O> : undefined;
333
552
  type PluginMetaData = PM extends z.ZodTypeAny ? OutputOf<PM> : unknown;
334
553
  type FieldMetaData = FM extends z.ZodTypeAny ? OutputOf<FM> : unknown;
335
554
 
@@ -361,34 +580,51 @@ export function createPluginContext<
361
580
  >
362
581
  ) => void;
363
582
 
364
- type Plugin<THookReturn> = Prettify<
365
- CogsPlugin<TName, Options, THookReturn, PluginMetaData, FieldMetaData>
583
+ type Plugin<
584
+ THookReturn,
585
+ TChainMethods extends ChainMethodDefinitions = {},
586
+ > = Prettify<
587
+ CogsPlugin<
588
+ TName,
589
+ Options,
590
+ THookReturn,
591
+ PluginMetaData,
592
+ FieldMetaData,
593
+ TChainMethods
594
+ >
366
595
  >;
367
596
 
368
- const createPluginObject = <THookReturn = never>(
597
+ const createPluginObject = <
598
+ THookReturn = never,
599
+ TChainMethods extends ChainMethodDefinitions = {},
600
+ >(
369
601
  hookFn?: (
370
602
  params: UseHookParams<Options, PluginMetaData, FieldMetaData>
371
603
  ) => THookReturn,
372
604
  transformFn?: TransformFn<THookReturn>,
373
605
  updateHandler?: UpdateFn<THookReturn>,
374
- formUpdateHandler?: FormUpdateFn<THookReturn>
375
- ): Plugin<THookReturn> => {
606
+ formUpdateHandler?: FormUpdateFn<THookReturn>,
607
+ chainMethods?: TChainMethods
608
+ ): Plugin<THookReturn, TChainMethods> => {
376
609
  return {
377
610
  name,
378
611
  useHook: hookFn as any,
379
612
  transformState: transformFn as any,
380
613
  onUpdate: updateHandler as any,
381
614
  onFormUpdate: formUpdateHandler as any,
615
+ chainMethods,
382
616
  };
383
617
  };
384
618
 
385
619
  type BuildRet<
386
620
  THookReturn,
621
+ TChainMethods extends ChainMethodDefinitions,
387
622
  HasTransform extends boolean,
388
623
  HasUpdate extends boolean,
389
624
  HasFormUpdate extends boolean,
625
+ HasMethods extends boolean,
390
626
  HasWrapper extends boolean,
391
- > = Plugin<THookReturn> &
627
+ > = Plugin<THookReturn, TChainMethods> &
392
628
  (HasTransform extends true
393
629
  ? {}
394
630
  : {
@@ -396,9 +632,11 @@ export function createPluginContext<
396
632
  fn: TransformFn<THookReturn>
397
633
  ): BuildRet<
398
634
  THookReturn,
635
+ TChainMethods,
399
636
  true,
400
637
  HasUpdate,
401
638
  HasFormUpdate,
639
+ HasMethods,
402
640
  HasWrapper
403
641
  >;
404
642
  }) &
@@ -409,9 +647,11 @@ export function createPluginContext<
409
647
  fn: UpdateFn<THookReturn>
410
648
  ): BuildRet<
411
649
  THookReturn,
650
+ TChainMethods,
412
651
  HasTransform,
413
652
  true,
414
653
  HasFormUpdate,
654
+ HasMethods,
415
655
  HasWrapper
416
656
  >;
417
657
  }) &
@@ -420,14 +660,39 @@ export function createPluginContext<
420
660
  : {
421
661
  onFormUpdate(
422
662
  fn: FormUpdateFn<THookReturn>
423
- ): BuildRet<THookReturn, HasTransform, HasUpdate, true, HasWrapper>;
663
+ ): BuildRet<
664
+ THookReturn,
665
+ TChainMethods,
666
+ HasTransform,
667
+ HasUpdate,
668
+ true,
669
+ HasMethods,
670
+ HasWrapper
671
+ >;
672
+ }) &
673
+ (HasMethods extends true
674
+ ? {}
675
+ : {
676
+ methods<TNextMethods extends ChainMethodDefinitions>(
677
+ fn: (helpers: MethodsBuilderParams) => TNextMethods
678
+ ): BuildRet<
679
+ THookReturn,
680
+ TNextMethods,
681
+ HasTransform,
682
+ HasUpdate,
683
+ HasFormUpdate,
684
+ true,
685
+ HasWrapper
686
+ >;
424
687
  });
425
688
 
426
689
  function createBuilder<
427
690
  THookReturn = never,
691
+ TChainMethods extends ChainMethodDefinitions = {},
428
692
  HasTransform extends boolean = false,
429
693
  HasUpdate extends boolean = false,
430
694
  HasFormUpdate extends boolean = false,
695
+ HasMethods extends boolean = false,
431
696
  HasWrapper extends boolean = false,
432
697
  >(
433
698
  hookFn?: (
@@ -435,27 +700,33 @@ export function createPluginContext<
435
700
  ) => THookReturn,
436
701
  transformFn?: TransformFn<THookReturn>,
437
702
  updateHandler?: UpdateFn<THookReturn>,
438
- formUpdateHandler?: FormUpdateFn<THookReturn>
703
+ formUpdateHandler?: FormUpdateFn<THookReturn>,
704
+ chainMethods?: TChainMethods
439
705
  ): BuildRet<
440
706
  THookReturn,
707
+ TChainMethods,
441
708
  HasTransform,
442
709
  HasUpdate,
443
710
  HasFormUpdate,
711
+ HasMethods,
444
712
  HasWrapper
445
713
  > {
446
- const plugin = createPluginObject<THookReturn>(
714
+ const plugin = createPluginObject<THookReturn, TChainMethods>(
447
715
  hookFn,
448
716
  transformFn,
449
717
  updateHandler,
450
- formUpdateHandler
718
+ formUpdateHandler,
719
+ chainMethods
451
720
  );
452
721
 
453
722
  const methods = {} as Partial<
454
723
  BuildRet<
455
724
  THookReturn,
725
+ TChainMethods,
456
726
  HasTransform,
457
727
  HasUpdate,
458
728
  HasFormUpdate,
729
+ HasMethods,
459
730
  HasWrapper
460
731
  >
461
732
  >;
@@ -464,58 +735,97 @@ export function createPluginContext<
464
735
  (methods as any).transformState = (fn: TransformFn<THookReturn>) =>
465
736
  createBuilder<
466
737
  THookReturn,
738
+ TChainMethods,
467
739
  true,
468
740
  HasUpdate,
469
741
  HasFormUpdate,
742
+ HasMethods,
470
743
  HasWrapper
471
- >(hookFn, fn, updateHandler, formUpdateHandler);
744
+ >(hookFn, fn, updateHandler, formUpdateHandler, chainMethods);
472
745
  }
473
746
  if (!updateHandler) {
474
747
  (methods as any).onUpdate = (fn: UpdateFn<THookReturn>) =>
475
748
  createBuilder<
476
749
  THookReturn,
750
+ TChainMethods,
477
751
  HasTransform,
478
752
  true,
479
753
  HasFormUpdate,
754
+ HasMethods,
480
755
  HasWrapper
481
- >(hookFn, transformFn, fn, formUpdateHandler);
756
+ >(hookFn, transformFn, fn, formUpdateHandler, chainMethods);
482
757
  }
483
758
  if (!formUpdateHandler) {
484
759
  (methods as any).onFormUpdate = (fn: FormUpdateFn<THookReturn>) =>
485
- createBuilder<THookReturn, HasTransform, HasUpdate, true, HasWrapper>(
760
+ createBuilder<
761
+ THookReturn,
762
+ TChainMethods,
763
+ HasTransform,
764
+ HasUpdate,
765
+ true,
766
+ HasMethods,
767
+ HasWrapper
768
+ >(hookFn, transformFn, updateHandler, fn, chainMethods);
769
+ }
770
+ if (!chainMethods) {
771
+ (methods as any).methods = <
772
+ TNextMethods extends ChainMethodDefinitions,
773
+ >(
774
+ fn: (helpers: MethodsBuilderParams) => TNextMethods
775
+ ) =>
776
+ createBuilder<
777
+ THookReturn,
778
+ TNextMethods,
779
+ HasTransform,
780
+ HasUpdate,
781
+ HasFormUpdate,
782
+ true,
783
+ HasWrapper
784
+ >(
486
785
  hookFn,
487
786
  transformFn,
488
787
  updateHandler,
489
- fn
788
+ formUpdateHandler,
789
+ fn(createMethodsBuilderParams())
490
790
  );
491
791
  }
492
792
 
493
793
  return Object.assign(plugin, methods) as BuildRet<
494
794
  THookReturn,
795
+ TChainMethods,
495
796
  HasTransform,
496
797
  HasUpdate,
497
798
  HasFormUpdate,
799
+ HasMethods,
498
800
  HasWrapper
499
801
  >;
500
802
  }
501
803
 
502
804
  const start = Object.assign(
503
- createBuilder<never, false, false, false, false>(),
805
+ createBuilder<never, {}, false, false, false, false, false>(),
504
806
  {
505
807
  useHook<THookReturn>(
506
808
  hookFn: (
507
809
  params: UseHookParams<Options, PluginMetaData, FieldMetaData>
508
810
  ) => THookReturn
509
811
  ) {
510
- return createBuilder<THookReturn, false, false, false, false>(hookFn);
812
+ return createBuilder<
813
+ THookReturn,
814
+ {},
815
+ false,
816
+ false,
817
+ false,
818
+ false,
819
+ false
820
+ >(hookFn);
511
821
  },
512
822
  }
513
- ) as BuildRet<never, false, false, false, false> & {
823
+ ) as BuildRet<never, {}, false, false, false, false, false> & {
514
824
  useHook<THookReturn>(
515
825
  hookFn: (
516
826
  params: UseHookParams<Options, PluginMetaData, FieldMetaData>
517
827
  ) => THookReturn
518
- ): BuildRet<THookReturn, false, false, false, false>;
828
+ ): BuildRet<THookReturn, {}, false, false, false, false, false>;
519
829
  };
520
830
 
521
831
  return start;