@idem.agency/form-builder 0.0.14 → 0.0.16

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