@idem.agency/form-builder 0.0.14 → 0.0.15

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.mjs CHANGED
@@ -3,72 +3,6 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import clsx from "clsx";
4
4
  import { z } from "zod";
5
5
 
6
- //#region src/shared/model/store/store.ts
7
- function createStore(reducer, initialState) {
8
- let state = initialState;
9
- const listeners = /* @__PURE__ */ new Set();
10
- return {
11
- getState() {
12
- return state;
13
- },
14
- dispatch(action) {
15
- state = reducer(state, action);
16
- listeners.forEach((l) => l());
17
- },
18
- subscribe(listener) {
19
- listeners.add(listener);
20
- return () => {
21
- listeners.delete(listener);
22
- };
23
- },
24
- subscribeSelector(selector, listener) {
25
- let prev = selector(state);
26
- return this.subscribe(() => {
27
- const next = selector(state);
28
- if (next !== prev) {
29
- prev = next;
30
- listener(next);
31
- }
32
- });
33
- }
34
- };
35
- }
36
-
37
- //#endregion
38
- //#region src/shared/model/store/createStoreContext.tsx
39
- function createStoreContext(reducer, defaultState) {
40
- const StoreContext = createContext(null);
41
- const Provider = ({ children, initialState }) => {
42
- const storeRef = useRef(createStore(reducer, initialState ?? defaultState));
43
- return /* @__PURE__ */ jsx(StoreContext.Provider, {
44
- value: storeRef.current,
45
- children
46
- });
47
- };
48
- function useStore(selector) {
49
- const store = useContext(StoreContext);
50
- if (!store) throw new Error("StoreProvider missing");
51
- return useSyncExternalStore(store.subscribe, () => selector(store.getState()), () => selector(store.getState()));
52
- }
53
- function useDispatch() {
54
- const store = useContext(StoreContext);
55
- if (!store) throw new Error("StoreProvider missing");
56
- return store.dispatch;
57
- }
58
- function useStoreInstance() {
59
- const store = useContext(StoreContext);
60
- if (!store) throw new Error("StoreProvider missing");
61
- return store;
62
- }
63
- return {
64
- Provider,
65
- useStore,
66
- useDispatch,
67
- useStoreInstance
68
- };
69
- }
70
-
71
- //#endregion
72
6
  //#region src/shared/utils.ts
73
7
  function updateNestedValue(obj, path, value) {
74
8
  if (!path) return value;
@@ -93,43 +27,6 @@ function getNestedValue(obj, path) {
93
27
  return path.split(".").reduce((acc, key) => acc && acc[key] !== void 0 ? acc[key] : void 0, obj);
94
28
  }
95
29
 
96
- //#endregion
97
- //#region src/shared/model/store/index.tsx
98
- function reducer(state, action) {
99
- const newData = { ...state };
100
- switch (action.type) {
101
- case "setValue":
102
- newData.formData = updateNestedValue(newData.formData, action.path, action.value);
103
- break;
104
- case "setFieldValue":
105
- newData.formData = updateNestedValue(newData.formData, action.path, action.value);
106
- newData.errors = updateNestedValue(newData.errors, action.path, action?.errors?.length ? action.errors : null);
107
- break;
108
- case "setError":
109
- newData.errors = updateNestedValue(newData.errors, action.path, action.value);
110
- break;
111
- case "reset":
112
- newData.formData = {};
113
- newData.errors = {};
114
- break;
115
- case "setErrors":
116
- newData.errors = { ...action.errors };
117
- break;
118
- }
119
- return newData;
120
- }
121
- const { Provider, useStore: useFormStore, useDispatch: useFormDispatch, useStoreInstance: useFormStoreInstance } = createStoreContext(reducer, {
122
- formData: {},
123
- errors: {}
124
- });
125
- const FormStoreProvider = ({ children, formData }) => /* @__PURE__ */ jsx(Provider, {
126
- initialState: {
127
- formData: formData ?? {},
128
- errors: {}
129
- },
130
- children
131
- });
132
-
133
30
  //#endregion
134
31
  //#region src/plugins/validation/rules/confirm.ts
135
32
  const confirm = {
@@ -215,47 +112,276 @@ var Validation = class {
215
112
  };
216
113
 
217
114
  //#endregion
218
- //#region src/plugins/validation/provider.tsx
219
- function createValidationProvider() {
220
- const Context = createContext(null);
221
- const Provider = ({ validator = {
222
- onSubmit: false,
223
- rules: []
224
- }, children }) => {
225
- const core = useMemo(() => new Validation(validator.onSubmit ?? false, validator.rules ?? []), [validator]);
226
- return /* @__PURE__ */ jsx(Context.Provider, {
227
- value: core,
228
- children
229
- });
115
+ //#region src/plugins/validation/index.ts
116
+ function createValidationPlugin(config) {
117
+ const engine = new Validation(config?.onSubmit ?? false, config?.rules ?? []);
118
+ return {
119
+ name: "validation",
120
+ install(ctx) {
121
+ ctx.events.tap("field:register", ({ path, field }) => {
122
+ engine.registerField(path, field.validation ?? []);
123
+ });
124
+ ctx.pipeline.use("field:change", (data, next) => {
125
+ const errors = engine.validate(data.field.validation ?? [], data.value, data.formData);
126
+ return next({
127
+ ...data,
128
+ errors
129
+ });
130
+ });
131
+ ctx.pipeline.use("form:validate", (data, next) => {
132
+ const errors = engine.validateAll(data.formData);
133
+ return next({
134
+ ...data,
135
+ errors
136
+ });
137
+ });
138
+ }
230
139
  };
231
- const useValidate = (rules) => {
232
- const core = useContext(Context);
233
- if (!core) throw new Error("ValidationProvider missing");
234
- return (data, formData) => {
235
- return core.validate(rules ?? [], data, formData);
236
- };
140
+ }
141
+
142
+ //#endregion
143
+ //#region src/plugins/visibility/core.ts
144
+ var VisibilityCore = class VisibilityCore {
145
+ registerFields = {};
146
+ registerField(path, rule) {
147
+ this.registerFields[path] = rule;
148
+ }
149
+ static isGroupRule(i) {
150
+ return "rules" in i;
151
+ }
152
+ static isInOperator(i) {
153
+ return i.operator == "in";
154
+ }
155
+ static isEqualOperand(i) {
156
+ return i.operator == "=";
157
+ }
158
+ static checkGroup(groupRule, formData) {
159
+ const logic = groupRule.logic;
160
+ const items = [];
161
+ groupRule.rules.forEach((rule) => {
162
+ if (this.isGroupRule(rule)) items.push(this.checkGroup(rule, formData));
163
+ else {
164
+ const value = getNestedValue(formData, rule.field);
165
+ if (this.isInOperator(rule)) items.push(rule.value.indexOf(value) !== -1);
166
+ else if (this.isEqualOperand(rule)) items.push(rule.value == value);
167
+ }
168
+ });
169
+ if (logic == "or") return items.indexOf(true) != -1;
170
+ else return items.indexOf(false) == -1;
171
+ }
172
+ isView(path, data) {
173
+ const rule = this.checkInRule(path);
174
+ if (!this.checkInRule(path)) return true;
175
+ return VisibilityCore.checkGroup(rule, data);
176
+ }
177
+ checkInRule(path) {
178
+ return this.registerFields[path];
179
+ }
180
+ };
181
+
182
+ //#endregion
183
+ //#region src/plugins/visibility/index.ts
184
+ function createVisibilityPlugin() {
185
+ const engine = new VisibilityCore();
186
+ return {
187
+ name: "visibility",
188
+ install(ctx) {
189
+ ctx.events.tap("field:register", ({ path, field }) => {
190
+ if (field.visibility) engine.registerField(path, field.visibility);
191
+ });
192
+ ctx.pipeline.useSync("field:visible", (data, next) => {
193
+ const visible = engine.isView(data.path, data.formData);
194
+ return next({
195
+ ...data,
196
+ visible
197
+ });
198
+ });
199
+ }
237
200
  };
238
- const useRegister = (currentFieldPath, field) => {
239
- const core = useContext(Context);
240
- if (!core) throw new Error("ValidationProvider missing");
241
- core.registerField(currentFieldPath, field.validation ?? []);
242
- return useValidate(field.validation);
201
+ }
202
+
203
+ //#endregion
204
+ //#region src/entity/inputs/ui/input/index.tsx
205
+ function isTextFieldConfig(field) {
206
+ return field.type === "text" || field.type === "email" || field.type === "password";
207
+ }
208
+ const TextField = ({ field, value, errors, onChange }) => {
209
+ if (!isTextFieldConfig(field)) {
210
+ console.warn(`TextField received an invalid field config for type: ${field.type}`);
211
+ return null;
212
+ }
213
+ const id = useId();
214
+ return /* @__PURE__ */ jsxs("div", {
215
+ style: { marginBottom: "15px" },
216
+ children: [
217
+ /* @__PURE__ */ jsxs("label", {
218
+ htmlFor: id,
219
+ children: [field.label, ":"]
220
+ }),
221
+ /* @__PURE__ */ jsx("input", {
222
+ type: field.type,
223
+ id,
224
+ name: field.name,
225
+ placeholder: field.placeholder,
226
+ value: value || "",
227
+ onChange: (e) => onChange(e.target.value),
228
+ style: { borderColor: errors ? "red" : "#ccc" }
229
+ }),
230
+ errors && /* @__PURE__ */ jsx("p", {
231
+ style: {
232
+ color: "red",
233
+ fontSize: "0.8em"
234
+ },
235
+ children: Object.values(errors).join(", ")
236
+ })
237
+ ]
238
+ });
239
+ };
240
+
241
+ //#endregion
242
+ //#region src/shared/model/store/store.ts
243
+ function createStore(reducer, initialState) {
244
+ let state = initialState;
245
+ const listeners = /* @__PURE__ */ new Set();
246
+ return {
247
+ getState() {
248
+ return state;
249
+ },
250
+ dispatch(action) {
251
+ state = reducer(state, action);
252
+ listeners.forEach((l) => l());
253
+ },
254
+ subscribe(listener) {
255
+ listeners.add(listener);
256
+ return () => {
257
+ listeners.delete(listener);
258
+ };
259
+ },
260
+ subscribeSelector(selector, listener) {
261
+ let prev = selector(state);
262
+ return this.subscribe(() => {
263
+ const next = selector(state);
264
+ if (next !== prev) {
265
+ prev = next;
266
+ listener(next);
267
+ }
268
+ });
269
+ }
243
270
  };
244
- const useSubmitValidation = () => {
245
- const core = useContext(Context);
246
- if (!core) throw new Error("ValidationProvider missing");
247
- return (formData) => core.validateAll(formData);
271
+ }
272
+
273
+ //#endregion
274
+ //#region src/shared/model/store/createStoreContext.tsx
275
+ function createStoreContext(reducer, defaultState) {
276
+ const StoreContext = createContext(null);
277
+ const Provider = ({ children, initialState }) => {
278
+ const storeRef = useRef(createStore(reducer, initialState ?? defaultState));
279
+ return /* @__PURE__ */ jsx(StoreContext.Provider, {
280
+ value: storeRef.current,
281
+ children
282
+ });
248
283
  };
284
+ function useStore(selector) {
285
+ const store = useContext(StoreContext);
286
+ if (!store) throw new Error("StoreProvider missing");
287
+ return useSyncExternalStore(store.subscribe, () => selector(store.getState()), () => selector(store.getState()));
288
+ }
289
+ function useDispatch() {
290
+ const store = useContext(StoreContext);
291
+ if (!store) throw new Error("StoreProvider missing");
292
+ return store.dispatch;
293
+ }
294
+ function useStoreInstance() {
295
+ const store = useContext(StoreContext);
296
+ if (!store) throw new Error("StoreProvider missing");
297
+ return store;
298
+ }
249
299
  return {
250
300
  Provider,
251
- useRegister,
252
- useSubmitValidation
301
+ useStore,
302
+ useDispatch,
303
+ useStoreInstance
253
304
  };
254
305
  }
255
306
 
256
307
  //#endregion
257
- //#region src/plugins/validation/index.ts
258
- const { Provider: ValidationProvider, useRegister, useSubmitValidation } = createValidationProvider();
308
+ //#region src/shared/model/plugins/context.tsx
309
+ const PluginSystemContext = createContext(null);
310
+ function usePluginManager() {
311
+ const ctx = useContext(PluginSystemContext);
312
+ if (!ctx) throw new Error("PluginSystemContext not found");
313
+ return ctx;
314
+ }
315
+
316
+ //#endregion
317
+ //#region src/shared/model/store/index.tsx
318
+ function reducer(state, action) {
319
+ const newData = { ...state };
320
+ switch (action.type) {
321
+ case "setValue":
322
+ newData.formData = updateNestedValue(newData.formData, action.path, action.value);
323
+ break;
324
+ case "setFieldValue":
325
+ newData.formData = updateNestedValue(newData.formData, action.path, action.value);
326
+ newData.errors = updateNestedValue(newData.errors, action.path, action?.errors?.length ? action.errors : null);
327
+ break;
328
+ case "setError":
329
+ newData.errors = updateNestedValue(newData.errors, action.path, action.value);
330
+ break;
331
+ case "reset":
332
+ newData.formData = {};
333
+ newData.errors = {};
334
+ break;
335
+ case "setErrors":
336
+ newData.errors = { ...action.errors };
337
+ break;
338
+ }
339
+ return newData;
340
+ }
341
+ function composeMiddleware(middlewares, baseDispatch, getState) {
342
+ if (middlewares.length === 0) return baseDispatch;
343
+ return middlewares.reduceRight((next, m) => (action) => m(action, next, getState), baseDispatch);
344
+ }
345
+ const { Provider, useStore: useFormStore, useDispatch: _useRawDispatch, useStoreInstance: useFormStoreInstance } = createStoreContext(reducer, {
346
+ formData: {},
347
+ errors: {}
348
+ });
349
+ const DispatchContext = createContext(null);
350
+ const DispatchEnhancer = ({ children }) => {
351
+ const pluginManager = useContext(PluginSystemContext);
352
+ const store = useFormStoreInstance();
353
+ const rawDispatch = _useRawDispatch();
354
+ const middlewares = pluginManager ? pluginManager.getMiddleware() : [];
355
+ const dispatch = useMemo(() => composeMiddleware(middlewares, rawDispatch, store.getState), []);
356
+ return /* @__PURE__ */ jsx(DispatchContext.Provider, {
357
+ value: dispatch,
358
+ children
359
+ });
360
+ };
361
+ const FormStoreProvider = ({ children, formData }) => /* @__PURE__ */ jsx(Provider, {
362
+ initialState: {
363
+ formData: formData ?? {},
364
+ errors: {}
365
+ },
366
+ children: /* @__PURE__ */ jsx(DispatchEnhancer, { children })
367
+ });
368
+ function useFormDispatch() {
369
+ const ctx = useContext(DispatchContext);
370
+ if (!ctx) throw new Error("FormStoreProvider missing");
371
+ return ctx;
372
+ }
373
+
374
+ //#endregion
375
+ //#region src/shared/model/plugins/hooks.ts
376
+ function useCallEvents() {
377
+ return usePluginManager().callEvents;
378
+ }
379
+ function useRunPipeline() {
380
+ return usePluginManager().runPipeline;
381
+ }
382
+ function useRunPipelineSync() {
383
+ return usePluginManager().runPipelineSync;
384
+ }
259
385
 
260
386
  //#endregion
261
387
  //#region src/entity/dynamicBuilder/element.tsx
@@ -263,23 +389,51 @@ const DynamicBuilderElement = (props) => {
263
389
  const Element = props.element;
264
390
  const field = props.field;
265
391
  const currentFieldPath = props.path ? `${props.path}.${field.name}` : field.name;
266
- const value = useFormStore(((s) => getNestedValue(s.formData, currentFieldPath)));
267
- const errors = useFormStore(((s) => s.errors[currentFieldPath]));
392
+ const value = useFormStore((s) => getNestedValue(s.formData, currentFieldPath));
393
+ const errors = useFormStore((s) => s.errors[currentFieldPath]);
268
394
  const dispatch = useFormDispatch();
269
395
  const store = useFormStoreInstance();
270
- const validator = useRegister(currentFieldPath, field);
396
+ const callEvents = useCallEvents();
397
+ const runPipeline = useRunPipeline();
398
+ const runPipelineSync = useRunPipelineSync();
399
+ useEffect(() => {
400
+ callEvents("field:register", {
401
+ path: currentFieldPath,
402
+ field
403
+ });
404
+ }, [currentFieldPath]);
405
+ if (!useFormStore((s) => runPipelineSync("field:visible", {
406
+ visible: true,
407
+ field,
408
+ path: currentFieldPath,
409
+ formData: s.formData
410
+ }).visible)) return null;
271
411
  return /* @__PURE__ */ jsx(Element, {
272
412
  field: { ...field },
273
413
  path: currentFieldPath,
274
414
  value,
275
415
  errors,
276
- onChange: (value) => {
416
+ onChange: async (value) => {
277
417
  const data = store.getState().formData;
418
+ const { value: finalValue, errors } = await runPipeline("field:change", {
419
+ value,
420
+ field,
421
+ path: currentFieldPath,
422
+ formData: data,
423
+ errors: []
424
+ });
278
425
  dispatch({
279
426
  type: "setFieldValue",
280
427
  path: currentFieldPath,
281
- value,
282
- errors: validator(value, data)
428
+ value: finalValue,
429
+ errors
430
+ });
431
+ await callEvents("field:change", {
432
+ value: finalValue,
433
+ prevValue: value,
434
+ field,
435
+ path: currentFieldPath,
436
+ formData: store.getState().formData
283
437
  });
284
438
  }
285
439
  });
@@ -345,26 +499,57 @@ const DynamicBuilder = (props) => {
345
499
  });
346
500
  };
347
501
 
502
+ //#endregion
503
+ //#region src/entity/inputs/ui/group/index.tsx
504
+ function isGroupConfig(field) {
505
+ return Array.isArray(field.fields);
506
+ }
507
+ const FormGroup = ({ field, path }) => {
508
+ if (!isGroupConfig(field)) return null;
509
+ const Builder = useBuilder()(field.fields, path);
510
+ const className = (field.variant ?? "col") == "col" ? "flex-col" : "flex-row";
511
+ return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", { children: field.label }), /* @__PURE__ */ jsx("div", {
512
+ className: clsx(className, "flex"),
513
+ children: Builder
514
+ })] });
515
+ };
516
+
348
517
  //#endregion
349
518
  //#region src/widgets/form/form.tsx
350
519
  const Form = forwardRef((props, ref) => {
351
520
  const dispatch = useFormDispatch();
352
521
  const store = useFormStoreInstance();
353
- const changeRef = useRef(props.onChange);
522
+ const callEvents = useCallEvents();
523
+ const runPipeline = useRunPipeline();
354
524
  useEffect(() => {
355
- return store.subscribeSelector((s) => s.formData, (formData) => changeRef.current?.(formData));
356
- }, [store]);
357
- const validation = useSubmitValidation();
358
- const submitHdl = useCallback(() => {
525
+ return store.subscribeSelector((s) => s.formData, (formData) => props.onChange ? props.onChange?.(formData) : null);
526
+ }, [props.onChange]);
527
+ const submitHdl = useCallback(async () => {
359
528
  if (props.onSubmit) {
360
- const errors = validation(store.getState().formData);
361
- if (Object.keys(errors).length == 0) props.onSubmit(store.getState().formData);
529
+ const currentFormData = store.getState().formData;
530
+ await callEvents("form:submit:before", { formData: currentFormData });
531
+ const { formData: transformed } = await runPipeline("form:submit", { formData: currentFormData });
532
+ const { errors } = await runPipeline("form:validate", {
533
+ formData: transformed,
534
+ errors: {}
535
+ });
536
+ if (Object.keys(errors).length === 0) props.onSubmit(transformed);
362
537
  else dispatch({
363
538
  type: "setErrors",
364
539
  errors
365
540
  });
541
+ await callEvents("form:submit:after", {
542
+ formData: transformed,
543
+ errors
544
+ });
366
545
  }
367
- }, [props.onSubmit, validation]);
546
+ }, [
547
+ props.onSubmit,
548
+ callEvents,
549
+ runPipeline,
550
+ store,
551
+ dispatch
552
+ ]);
368
553
  useImperativeHandle(ref, () => ({
369
554
  reset: () => dispatch({ type: "reset" }),
370
555
  submit: () => submitHdl(),
@@ -375,9 +560,9 @@ const Form = forwardRef((props, ref) => {
375
560
  store
376
561
  ]);
377
562
  return /* @__PURE__ */ jsxs("form", {
378
- onSubmit: (e) => {
563
+ onSubmit: async (e) => {
379
564
  e.preventDefault();
380
- submitHdl();
565
+ await submitHdl();
381
566
  },
382
567
  className: props?.className,
383
568
  children: [
@@ -394,14 +579,204 @@ const Form = forwardRef((props, ref) => {
394
579
  });
395
580
  });
396
581
 
582
+ //#endregion
583
+ //#region src/shared/model/plugins/HookRegistry.ts
584
+ var HookRegistry = class {
585
+ handlers = /* @__PURE__ */ new Map();
586
+ tap(hook, handler) {
587
+ if (!this.handlers.has(hook)) this.handlers.set(hook, /* @__PURE__ */ new Set());
588
+ this.handlers.get(hook).add(handler);
589
+ return () => {
590
+ this.handlers.get(hook)?.delete(handler);
591
+ };
592
+ }
593
+ async call(hook, data) {
594
+ const set = this.handlers.get(hook);
595
+ if (!set) return;
596
+ await Promise.all([...set].map((h) => h(data)));
597
+ }
598
+ };
599
+
600
+ //#endregion
601
+ //#region src/shared/model/plugins/PipelineRegistry.ts
602
+ var PipelineRegistry = class {
603
+ stages = /* @__PURE__ */ new Map();
604
+ syncStages = /* @__PURE__ */ new Map();
605
+ use(pipeline, stage, priority = 10) {
606
+ if (!this.stages.has(pipeline)) this.stages.set(pipeline, []);
607
+ const arr = this.stages.get(pipeline);
608
+ const entry = {
609
+ stage,
610
+ priority
611
+ };
612
+ arr.push(entry);
613
+ arr.sort((a, b) => b.priority - a.priority);
614
+ return () => {
615
+ const i = arr.indexOf(entry);
616
+ if (i >= 0) arr.splice(i, 1);
617
+ };
618
+ }
619
+ useSync(pipeline, stage, priority = 10) {
620
+ if (!this.syncStages.has(pipeline)) this.syncStages.set(pipeline, []);
621
+ const arr = this.syncStages.get(pipeline);
622
+ const entry = {
623
+ stage,
624
+ priority
625
+ };
626
+ arr.push(entry);
627
+ arr.sort((a, b) => b.priority - a.priority);
628
+ return () => {
629
+ const i = arr.indexOf(entry);
630
+ if (i >= 0) arr.splice(i, 1);
631
+ };
632
+ }
633
+ async run(name, initialData) {
634
+ const arr = this.stages.get(name) ?? [];
635
+ const compose = (index) => (data) => {
636
+ if (index >= arr.length) return Promise.resolve(data);
637
+ return arr[index].stage(data, compose(index + 1));
638
+ };
639
+ return compose(0)(initialData);
640
+ }
641
+ runSync(name, initialData) {
642
+ const arr = this.syncStages.get(name) ?? [];
643
+ const compose = (index) => (data) => {
644
+ if (index >= arr.length) return data;
645
+ return arr[index].stage(data, compose(index + 1));
646
+ };
647
+ return compose(0)(initialData);
648
+ }
649
+ };
650
+
651
+ //#endregion
652
+ //#region src/shared/model/plugins/EventBus.ts
653
+ var EventBus = class {
654
+ listeners = /* @__PURE__ */ new Map();
655
+ on(event, handler) {
656
+ if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
657
+ this.listeners.get(event).add(handler);
658
+ return () => this.listeners.get(event)?.delete(handler);
659
+ }
660
+ emit(event, data) {
661
+ this.listeners.get(event)?.forEach((h) => h(data));
662
+ }
663
+ };
664
+
665
+ //#endregion
666
+ //#region src/shared/model/plugins/MiddlewareRegistry.ts
667
+ var MiddlewareRegistry = class {
668
+ middlewares = [];
669
+ use(middleware) {
670
+ this.middlewares.push(middleware);
671
+ return () => {
672
+ const i = this.middlewares.indexOf(middleware);
673
+ if (i >= 0) this.middlewares.splice(i, 1);
674
+ };
675
+ }
676
+ getAll() {
677
+ return [...this.middlewares];
678
+ }
679
+ };
680
+
681
+ //#endregion
682
+ //#region src/shared/model/plugins/FieldRegistry.ts
683
+ var FieldRegistry = class {
684
+ components = /* @__PURE__ */ new Map();
685
+ register(type, component) {
686
+ this.components.set(type, component);
687
+ return () => this.components.delete(type);
688
+ }
689
+ getAll() {
690
+ return Object.fromEntries(this.components);
691
+ }
692
+ };
693
+
694
+ //#endregion
695
+ //#region src/shared/model/plugins/PluginManager.ts
696
+ var PluginManager = class {
697
+ eventRegistry = new HookRegistry();
698
+ pipelineRegistry = new PipelineRegistry();
699
+ eventBus = new EventBus();
700
+ middlewareRegistry = new MiddlewareRegistry();
701
+ fieldRegistry = new FieldRegistry();
702
+ unsubscribers = [];
703
+ installed = false;
704
+ constructor(plugins) {
705
+ this.plugins = plugins;
706
+ }
707
+ install() {
708
+ if (this.installed) return;
709
+ const context = {
710
+ events: { tap: (hook, handler) => {
711
+ const unsub = this.eventRegistry.tap(hook, handler);
712
+ this.unsubscribers.push(unsub);
713
+ return unsub;
714
+ } },
715
+ pipeline: {
716
+ use: (pipeline, stage, priority) => {
717
+ const unsub = this.pipelineRegistry.use(pipeline, stage, priority);
718
+ this.unsubscribers.push(unsub);
719
+ return unsub;
720
+ },
721
+ useSync: (pipeline, stage, priority) => {
722
+ const unsub = this.pipelineRegistry.useSync(pipeline, stage, priority);
723
+ this.unsubscribers.push(unsub);
724
+ return unsub;
725
+ }
726
+ },
727
+ eventBus: {
728
+ on: (event, handler) => {
729
+ const unsub = this.eventBus.on(event, handler);
730
+ this.unsubscribers.push(unsub);
731
+ return unsub;
732
+ },
733
+ emit: (event, data) => this.eventBus.emit(event, data)
734
+ },
735
+ fields: { register: (type, component) => {
736
+ const unsub = this.fieldRegistry.register(type, component);
737
+ this.unsubscribers.push(unsub);
738
+ return unsub;
739
+ } },
740
+ middleware: { use: (middleware) => {
741
+ const unsub = this.middlewareRegistry.use(middleware);
742
+ this.unsubscribers.push(unsub);
743
+ return unsub;
744
+ } }
745
+ };
746
+ this.installed = true;
747
+ for (const plugin of this.plugins) plugin.install(context);
748
+ }
749
+ uninstall() {
750
+ if (!this.installed) return;
751
+ this.installed = false;
752
+ [...this.unsubscribers].reverse().forEach((fn) => fn());
753
+ this.unsubscribers = [];
754
+ [...this.plugins].reverse().forEach((plugin) => plugin.uninstall?.());
755
+ }
756
+ callEvents = this.eventRegistry.call.bind(this.eventRegistry);
757
+ runPipeline = this.pipelineRegistry.run.bind(this.pipelineRegistry);
758
+ runPipelineSync = this.pipelineRegistry.runSync.bind(this.pipelineRegistry);
759
+ getMiddleware = this.middlewareRegistry.getAll.bind(this.middlewareRegistry);
760
+ getFields = this.fieldRegistry.getAll.bind(this.fieldRegistry);
761
+ };
762
+
397
763
  //#endregion
398
764
  //#region src/app/index.tsx
399
765
  const FormBuilder = forwardRef((props, ref) => {
400
766
  const { formData, ...innerProps } = props;
401
- return /* @__PURE__ */ jsx(FormStoreProvider, {
402
- formData,
403
- children: /* @__PURE__ */ jsx(ValidationProvider, {
404
- validator: props.validator,
767
+ const pluginManager = useMemo(() => {
768
+ const pm = new PluginManager([createValidationPlugin(props.validator), ...props.plugins ?? []]);
769
+ pm.install();
770
+ return pm;
771
+ }, []);
772
+ useEffect(() => {
773
+ pluginManager.install();
774
+ return () => pluginManager.uninstall();
775
+ }, [pluginManager]);
776
+ return /* @__PURE__ */ jsx(PluginSystemContext.Provider, {
777
+ value: pluginManager,
778
+ children: /* @__PURE__ */ jsx(FormStoreProvider, {
779
+ formData,
405
780
  children: /* @__PURE__ */ jsx(Form, {
406
781
  ...innerProps,
407
782
  ref
@@ -410,59 +785,6 @@ const FormBuilder = forwardRef((props, ref) => {
410
785
  });
411
786
  });
412
787
 
413
- //#endregion
414
- //#region src/entity/inputs/ui/input/index.tsx
415
- function isTextFieldConfig(field) {
416
- return field.type === "text" || field.type === "email" || field.type === "password";
417
- }
418
- const TextField = ({ field, value, errors, onChange }) => {
419
- if (!isTextFieldConfig(field)) {
420
- console.warn(`TextField received an invalid field config for type: ${field.type}`);
421
- return null;
422
- }
423
- const id = useId();
424
- return /* @__PURE__ */ jsxs("div", {
425
- style: { marginBottom: "15px" },
426
- children: [
427
- /* @__PURE__ */ jsxs("label", {
428
- htmlFor: id,
429
- children: [field.label, ":"]
430
- }),
431
- /* @__PURE__ */ jsx("input", {
432
- type: field.type,
433
- id,
434
- name: field.name,
435
- placeholder: field.placeholder,
436
- value: value || "",
437
- onChange: (e) => onChange(e.target.value),
438
- style: { borderColor: errors ? "red" : "#ccc" }
439
- }),
440
- errors && /* @__PURE__ */ jsx("p", {
441
- style: {
442
- color: "red",
443
- fontSize: "0.8em"
444
- },
445
- children: Object.values(errors).join(", ")
446
- })
447
- ]
448
- });
449
- };
450
-
451
- //#endregion
452
- //#region src/entity/inputs/ui/group/index.tsx
453
- function isGroupConfig(field) {
454
- return Array.isArray(field.fields);
455
- }
456
- const FormGroup = ({ field, path }) => {
457
- if (!isGroupConfig(field)) return null;
458
- const Builder = useBuilder()(field.fields, path);
459
- const className = (field.variant ?? "col") == "col" ? "flex-col" : "flex-row";
460
- return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", { children: field.label }), /* @__PURE__ */ jsx("div", {
461
- className: clsx(className, "flex"),
462
- children: Builder
463
- })] });
464
- };
465
-
466
788
  //#endregion
467
789
  //#region src/shared/model/index.ts
468
790
  const fieldShema = z.object({
@@ -472,5 +794,16 @@ const fieldShema = z.object({
472
794
  });
473
795
 
474
796
  //#endregion
475
- export { FormBuilder, FormGroup, TextField, fieldShema };
797
+ //#region src/index.ts
798
+ const plugins = {
799
+ createValidationPlugin,
800
+ createVisibilityPlugin
801
+ };
802
+ const inputs = {
803
+ FormGroup,
804
+ TextField
805
+ };
806
+
807
+ //#endregion
808
+ export { FormBuilder, fieldShema, inputs, plugins };
476
809
  //# sourceMappingURL=index.mjs.map