observer-form 0.1.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.
@@ -0,0 +1,2 @@
1
+ import type { CleanupManager, SubscriberStore } from '../types/form';
2
+ export declare function createCleanupManager(store: SubscriberStore): CleanupManager;
@@ -0,0 +1,4 @@
1
+ import type { CleanupManager, Notifier } from '../types/form';
2
+ export declare function createFieldRegistry(state: Record<string, any>, notifier: Notifier, cleanup: CleanupManager): {
3
+ registerField: (inputField: HTMLInputElement) => void;
4
+ };
@@ -0,0 +1,3 @@
1
+ import type { FormApi, FormOptions } from '../types/form';
2
+ declare function createForm(props: FormOptions): FormApi;
3
+ export default createForm;
@@ -0,0 +1,2 @@
1
+ import type { Notifier, SubscriberStore } from '../types/form';
2
+ export declare function createNotifier(store: SubscriberStore): Notifier;
@@ -0,0 +1,2 @@
1
+ import type { SubscriberStore } from '../types/form';
2
+ export declare function createSubscriberStore(): SubscriberStore;
@@ -0,0 +1 @@
1
+ export { default as createForm } from './core/form';
@@ -0,0 +1,114 @@
1
+ //#region src/core/subscriberStore.ts
2
+ function e() {
3
+ let e = /* @__PURE__ */ new Map();
4
+ function t(t, n) {
5
+ e.has(t) || e.set(t, /* @__PURE__ */ new Set()), e.get(t).add(n);
6
+ }
7
+ function n(t, n) {
8
+ let r = e.get(t);
9
+ r && (r.delete(n), r.size === 0 && e.delete(t));
10
+ }
11
+ return {
12
+ subscribe: t,
13
+ unsubscribe: n,
14
+ get: (t) => e.get(t),
15
+ has: (t) => e.has(t),
16
+ clear: () => e.clear(),
17
+ delete: (t) => e.delete(t),
18
+ get size() {
19
+ return e.size;
20
+ }
21
+ };
22
+ }
23
+ //#endregion
24
+ //#region src/core/notifier.ts
25
+ function t(e) {
26
+ function t(t) {
27
+ if (e.size === 0) return;
28
+ let n = e.get(t.name);
29
+ if (n) for (let e of n) e.update(t);
30
+ }
31
+ return { notify: t };
32
+ }
33
+ //#endregion
34
+ //#region src/core/cleanupManager.ts
35
+ function n(e) {
36
+ let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map(), r = null;
37
+ function i(e) {
38
+ let n = new AbortController();
39
+ return t.set(e, n), n.signal;
40
+ }
41
+ function a(r) {
42
+ t.get(r)?.abort(), t.delete(r), n.get(r)?.disconnect(), n.delete(r), e.delete(r);
43
+ }
44
+ function o(e, t, r) {
45
+ let i = new MutationObserver(() => {
46
+ t.isConnected || a(e);
47
+ });
48
+ i.observe(r, {
49
+ childList: !0,
50
+ subtree: !0
51
+ }), n.set(e, i);
52
+ }
53
+ function s() {
54
+ for (let e of t.values()) e.abort();
55
+ for (let e of n.values()) e.disconnect();
56
+ t.clear(), n.clear(), e.clear(), r &&= (r.disconnect(), null);
57
+ }
58
+ function c(e) {
59
+ r || e.parentNode && (r = new MutationObserver(() => {
60
+ e.isConnected || s();
61
+ }), r.observe(e.parentNode, { childList: !0 }));
62
+ }
63
+ return {
64
+ createFieldController: i,
65
+ observeField: o,
66
+ observeForm: c,
67
+ cleanupField: a,
68
+ teardown: s,
69
+ get hasFormObserver() {
70
+ return r !== null;
71
+ }
72
+ };
73
+ }
74
+ //#endregion
75
+ //#region src/core/fieldRegistry.ts
76
+ function r(e, t, n) {
77
+ function r(r) {
78
+ let i = r.name;
79
+ e[i] = r.value;
80
+ let a = n.createFieldController(i);
81
+ r.addEventListener("input", (n) => {
82
+ n.stopPropagation();
83
+ let a = r.value;
84
+ e[i] = a, t.notify({
85
+ name: i,
86
+ value: a
87
+ });
88
+ }, { signal: a });
89
+ let o = r.closest("form") || r.parentNode;
90
+ if (o && n.observeField(i, r, o), !n.hasFormObserver) {
91
+ let e = r.closest("form");
92
+ e?.parentNode && n.observeForm(e);
93
+ }
94
+ }
95
+ return { registerField: r };
96
+ }
97
+ //#endregion
98
+ //#region src/core/form.ts
99
+ function i(i) {
100
+ if (!i?.config) throw Error("Config is required");
101
+ if (!i?.onSubmit) throw Error("onSubmit is required");
102
+ let a = { ...i.initialValues }, o = e(), s = t(o), c = r(a, s, n(o));
103
+ return {
104
+ state: a,
105
+ subscribe: o.subscribe,
106
+ unsubscribe: o.unsubscribe,
107
+ notify: s.notify,
108
+ registerField: c.registerField
109
+ };
110
+ }
111
+ //#endregion
112
+ export { i as createForm };
113
+
114
+ //# sourceMappingURL=observer-form-index.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer-form-index.es.js","names":[],"sources":["../src/core/subscriberStore.ts","../src/core/notifier.ts","../src/core/cleanupManager.ts","../src/core/fieldRegistry.ts","../src/core/form.ts"],"sourcesContent":["import type { Observer, SubscriberStore } from '../types/form';\n\nexport function createSubscriberStore(): SubscriberStore {\n const subscribers = new Map<string, Set<Observer>>();\n\n function subscribe(fieldName: string, observer: Observer) {\n if (!subscribers.has(fieldName)) {\n subscribers.set(fieldName, new Set());\n }\n subscribers.get(fieldName)!.add(observer);\n }\n\n function unsubscribe(fieldName: string, observer: Observer) {\n const observers = subscribers.get(fieldName);\n if (!observers) return;\n observers.delete(observer);\n if (observers.size === 0) {\n subscribers.delete(fieldName);\n }\n }\n\n return {\n subscribe,\n unsubscribe,\n get: (fieldName) => subscribers.get(fieldName),\n has: (fieldName) => subscribers.has(fieldName),\n clear: () => subscribers.clear(),\n delete: (fieldName) => subscribers.delete(fieldName),\n get size() {\n return subscribers.size;\n },\n };\n}\n","import type { NotifyData, Notifier, SubscriberStore } from '../types/form';\n\nexport function createNotifier(store: SubscriberStore): Notifier {\n function notify(data: NotifyData) {\n if (store.size === 0) return;\n const observers = store.get(data.name);\n if (!observers) return;\n for (const sub of observers) sub.update(data);\n }\n\n return { notify };\n}\n","import type { CleanupManager, SubscriberStore } from '../types/form';\n\nexport function createCleanupManager(store: SubscriberStore): CleanupManager {\n const fieldControllers = new Map<string, AbortController>();\n const fieldObservers = new Map<string, MutationObserver>();\n let formObserver: MutationObserver | null = null;\n\n function createFieldController(fieldName: string): AbortSignal {\n const controller = new AbortController();\n fieldControllers.set(fieldName, controller);\n return controller.signal;\n }\n\n function cleanupField(fieldName: string) {\n fieldControllers.get(fieldName)?.abort();\n fieldControllers.delete(fieldName);\n\n fieldObservers.get(fieldName)?.disconnect();\n fieldObservers.delete(fieldName);\n\n store.delete(fieldName);\n }\n\n function observeField(\n fieldName: string,\n inputField: HTMLInputElement,\n ancestor: Element,\n ) {\n const mo = new MutationObserver(() => {\n if (!inputField.isConnected) {\n cleanupField(fieldName);\n }\n });\n mo.observe(ancestor, { childList: true, subtree: true });\n fieldObservers.set(fieldName, mo);\n }\n\n function teardown() {\n for (const controller of fieldControllers.values()) {\n controller.abort();\n }\n for (const observer of fieldObservers.values()) {\n observer.disconnect();\n }\n fieldControllers.clear();\n fieldObservers.clear();\n store.clear();\n\n if (formObserver) {\n formObserver.disconnect();\n formObserver = null;\n }\n }\n\n function observeForm(formElement: HTMLFormElement) {\n if (formObserver) return;\n if (!formElement.parentNode) return;\n\n formObserver = new MutationObserver(() => {\n if (!formElement.isConnected) {\n teardown();\n }\n });\n formObserver.observe(formElement.parentNode, { childList: true });\n }\n\n return {\n createFieldController,\n observeField,\n observeForm,\n cleanupField,\n teardown,\n get hasFormObserver() {\n return formObserver !== null;\n },\n };\n}\n","import type { CleanupManager, Notifier } from '../types/form';\n\nexport function createFieldRegistry(\n state: Record<string, any>,\n notifier: Notifier,\n cleanup: CleanupManager,\n) {\n function registerField(inputField: HTMLInputElement) {\n const fieldName = inputField.name;\n state[fieldName] = inputField.value;\n\n const signal = cleanup.createFieldController(fieldName);\n\n inputField.addEventListener(\n 'input',\n (e) => {\n e.stopPropagation();\n const val = inputField.value;\n state[fieldName] = val;\n notifier.notify({ name: fieldName, value: val });\n },\n { signal },\n );\n\n const ancestor = inputField.closest('form') || inputField.parentNode;\n if (ancestor) {\n cleanup.observeField(fieldName, inputField, ancestor as Element);\n }\n\n if (!cleanup.hasFormObserver) {\n const form = inputField.closest('form');\n if (form?.parentNode) {\n cleanup.observeForm(form);\n }\n }\n }\n\n return { registerField };\n}\n","import type { FormApi, FormOptions } from '../types/form';\nimport { createSubscriberStore } from './subscriberStore';\nimport { createNotifier } from './notifier';\nimport { createCleanupManager } from './cleanupManager';\nimport { createFieldRegistry } from './fieldRegistry';\n\nfunction createForm(props: FormOptions): FormApi {\n if (!props?.config) {\n throw new Error('Config is required');\n }\n\n if (!props?.onSubmit) {\n throw new Error('onSubmit is required');\n }\n\n const state = { ...props.initialValues };\n const store = createSubscriberStore();\n const notifier = createNotifier(store);\n const cleanup = createCleanupManager(store);\n const registry = createFieldRegistry(state, notifier, cleanup);\n\n return {\n state,\n subscribe: store.subscribe,\n unsubscribe: store.unsubscribe,\n notify: notifier.notify,\n registerField: registry.registerField,\n };\n}\n\nexport default createForm;\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "observer-form",
3
+ "version": "0.1.0",
4
+ "description": "Lightweight observer-pattern form library",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "./dist/observer-form-index.es.js",
8
+ "module": "./dist/observer-form-index.es.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/observer-form-index.es.js",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "files": ["dist"],
17
+ "scripts": {
18
+ "build": "vite build && tsc -p tsconfig.build.json",
19
+ "prepublishOnly": "pnpm build"
20
+ },
21
+ "keywords": ["form", "observer", "lightweight"],
22
+ "license": "MIT",
23
+ "author": "AliLtRP",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/AliLtRP/form.git"
27
+ },
28
+ "devDependencies": {
29
+ "typescript": "~6.0.2",
30
+ "vite": "^8.0.10"
31
+ }
32
+ }