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.
- package/dist/core/cleanupManager.d.ts +2 -0
- package/dist/core/fieldRegistry.d.ts +4 -0
- package/dist/core/form.d.ts +3 -0
- package/dist/core/notifier.d.ts +2 -0
- package/dist/core/subscriberStore.d.ts +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/observer-form-index.es.js +114 -0
- package/dist/observer-form-index.es.js.map +1 -0
- package/package.json +32 -0
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|