native-document 1.0.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.
Files changed (47) hide show
  1. package/dist/native-document.dev.js +2346 -0
  2. package/dist/native-document.min.js +1 -0
  3. package/elements.js +2 -0
  4. package/index.js +11 -0
  5. package/package.json +16 -0
  6. package/readme.md +495 -0
  7. package/rollup.config.js +29 -0
  8. package/router.js +9 -0
  9. package/src/data/MemoryManager.js +60 -0
  10. package/src/data/Observable.js +162 -0
  11. package/src/data/ObservableChecker.js +24 -0
  12. package/src/data/ObservableItem.js +101 -0
  13. package/src/data/Store.js +74 -0
  14. package/src/elements/content-formatter.js +32 -0
  15. package/src/elements/control/for-each.js +110 -0
  16. package/src/elements/control/show-if.js +86 -0
  17. package/src/elements/control/switch.js +88 -0
  18. package/src/elements/description-list.js +5 -0
  19. package/src/elements/form.js +71 -0
  20. package/src/elements/html5-semantics.js +12 -0
  21. package/src/elements/img.js +45 -0
  22. package/src/elements/index.js +21 -0
  23. package/src/elements/interactive.js +7 -0
  24. package/src/elements/list.js +6 -0
  25. package/src/elements/medias.js +8 -0
  26. package/src/elements/meta-data.js +9 -0
  27. package/src/elements/table.js +14 -0
  28. package/src/errors/ArgTypesError.js +7 -0
  29. package/src/errors/NativeDocumentError.js +8 -0
  30. package/src/errors/RouterError.js +9 -0
  31. package/src/router/Route.js +102 -0
  32. package/src/router/RouteGroupHelper.js +52 -0
  33. package/src/router/Router.js +232 -0
  34. package/src/router/RouterComponent.js +37 -0
  35. package/src/router/link.js +27 -0
  36. package/src/router/modes/HashRouter.js +83 -0
  37. package/src/router/modes/HistoryRouter.js +66 -0
  38. package/src/router/modes/MemoryRouter.js +71 -0
  39. package/src/utils/args-types.js +100 -0
  40. package/src/utils/debug-manager.js +34 -0
  41. package/src/utils/helpers.js +37 -0
  42. package/src/utils/prototypes.js +16 -0
  43. package/src/utils/validator.js +96 -0
  44. package/src/wrappers/AttributesWrapper.js +94 -0
  45. package/src/wrappers/DocumentObserver.js +51 -0
  46. package/src/wrappers/HtmlElementEventsWrapper.js +77 -0
  47. package/src/wrappers/HtmlElementWrapper.js +174 -0
@@ -0,0 +1,83 @@
1
+
2
+
3
+ export default function HashRouter() {
4
+
5
+ const $history = [];
6
+ let $currentIndex = 0;
7
+
8
+ /**
9
+ *
10
+ * @param {number} delta
11
+ */
12
+ const go = (delta) => {
13
+ const index = $currentIndex + delta;
14
+ if(!$history[index]) {
15
+ return;
16
+ }
17
+ $currentIndex = index;
18
+ const { route, params, query, path } = $history[index];
19
+ setHash(path);
20
+ };
21
+
22
+ const canGoBack = function() {
23
+ return $currentIndex > 0;
24
+ };
25
+ const canGoForward = function() {
26
+ return $currentIndex < $history.length - 1;
27
+ };
28
+
29
+ /**
30
+ *
31
+ * @param {string} path
32
+ */
33
+ const setHash = (path) => {
34
+ window.location.replace(`${window.location.pathname}${window.location.search}#${path}`);
35
+ }
36
+
37
+ const getCurrentHash = () => window.location.hash.slice(1);
38
+
39
+ /**
40
+ * @param {string|{name:string,params?:Object, query?:Object }} target
41
+ */
42
+ this.push = function(target) {
43
+ const { route, params, query, path } = this.resolve(target);
44
+ if(path === getCurrentHash()) {
45
+ return;
46
+ }
47
+ $history.splice($currentIndex + 1);
48
+ $history.push({ route, params, query, path });
49
+ $currentIndex++;
50
+ setHash(path);
51
+ };
52
+ /**
53
+ *
54
+ * @param {string|{name:string,params?:Object, query?:Object }} target
55
+ */
56
+ this.replace = function(target) {
57
+ const { route, params, query, path } = this.resolve(target);
58
+ if(path === getCurrentHash()) {
59
+ return;
60
+ }
61
+ $history[$currentIndex] = { route, params, query, path };
62
+ };
63
+ this.forward = function() {
64
+ return canGoForward() && go(1);
65
+ };
66
+ this.back = function() {
67
+ return canGoBack() && go(-1);
68
+ };
69
+
70
+ /**
71
+ * @param {string} defaultPath
72
+ */
73
+ this.init = function(defaultPath) {
74
+ window.addEventListener('hashchange', () => {
75
+ const { route, params, query, path } = this.resolve(getCurrentHash());
76
+ this.handleRouteChange(route, params, query, path);
77
+ });
78
+ const { route, params, query, path } = this.resolve(defaultPath || getCurrentHash());
79
+ $history.push({ route, params, query, path });
80
+ $currentIndex = 0;
81
+ this.handleRouteChange(route, params, query, path);
82
+ }
83
+ };
@@ -0,0 +1,66 @@
1
+ import RouterError from '../../errors/RouterError';
2
+ import DebugManager from "../../utils/debug-manager.js";
3
+
4
+ export default function HistoryRouter() {
5
+
6
+ /**
7
+ *
8
+ * @param {string|{name:string,params?:Object, query?:Object }} target
9
+ */
10
+ this.push = function(target) {
11
+ try {
12
+ const { route, path, params, query } = this.resolve(target);
13
+ if(window.history.state && window.history.state.path === path) {
14
+ return;
15
+ }
16
+ window.history.pushState({ name: route.name(), params, path}, route.name() || path , path);
17
+ this.handleRouteChange(route, params, query, path);
18
+ } catch (e) {
19
+ DebugManager.error('HistoryRouter', 'Error in pushState', e);
20
+ }
21
+ };
22
+ /**
23
+ *
24
+ * @param {string|{name:string,params?:Object, query?:Object }} target
25
+ */
26
+ this.replace = function(target) {
27
+ const { route, path, params } = this.resolve(target);
28
+ try {
29
+ window.history.replaceState({ name: route.name(), params, path}, route.name() || path , path);
30
+ this.handleRouteChange(route, params, {}, path);
31
+ } catch(e) {
32
+ DebugManager.error('HistoryRouter', 'Error in replaceState', e);
33
+ }
34
+ };
35
+ this.forward = function() {
36
+ window.history.forward();
37
+ };
38
+
39
+ this.back = function() {
40
+ window.history.back();
41
+ };
42
+
43
+ /**
44
+ * @param {string} defaultPath
45
+ */
46
+ this.init = function(defaultPath) {
47
+ window.addEventListener('popstate', (event) => {
48
+ try {
49
+ if(!event.state || !event.state.path) {
50
+ return;
51
+ }
52
+ const statePath = event.state.path;
53
+ const {route, params, query, path} = this.resolve(statePath);
54
+ if(!route) {
55
+ return;
56
+ }
57
+ this.handleRouteChange(route, params, query, path);
58
+ } catch(e) {
59
+ DebugManager.error('HistoryRouter', 'Error in popstate event', e);
60
+ }
61
+ });
62
+ const { route, params, query, path } = this.resolve(defaultPath || (window.location.pathname+window.location.search));
63
+ this.handleRouteChange(route, params, query, path);
64
+ }
65
+
66
+ };
@@ -0,0 +1,71 @@
1
+
2
+ export default function MemoryRouter() {
3
+ const $history = [];
4
+ let $currentIndex = 0;
5
+
6
+ /**
7
+ *
8
+ * @param {number} delta
9
+ */
10
+ const go = (delta) => {
11
+ const index = $currentIndex + delta;
12
+ if(!$history[index]) {
13
+ return;
14
+ }
15
+ $currentIndex = index;
16
+ const { route, params, query, path } = $history[index];
17
+ this.handleRouteChange(route, params, query, path);
18
+ };
19
+
20
+ const canGoBack = function() {
21
+ return $currentIndex > 0;
22
+ };
23
+ const canGoForward = function() {
24
+ return $currentIndex < $history.length - 1;
25
+ };
26
+
27
+ /**
28
+ *
29
+ * @param {string|{name:string,params?:Object, query?:Object }} target
30
+ */
31
+ this.push = function(target) {
32
+ const { route, params, query, path} = this.resolve(target);
33
+ if($history[$currentIndex] && $history[$currentIndex].path === path) {
34
+ return;
35
+ }
36
+ $history.splice($currentIndex + 1);
37
+ $history.push({ route, params, query, path });
38
+ $currentIndex++;
39
+ this.handleRouteChange(route, params, query, path);
40
+ };
41
+
42
+ /**
43
+ *
44
+ * @param {string|{name:string,params?:Object, query?:Object }} target
45
+ */
46
+ this.replace = function(target) {
47
+ const { route, params, query, path} = this.resolve(target);
48
+ $history[$currentIndex] = { route, params, query, path };
49
+ this.handleRouteChange(route, params, query, path);
50
+ };
51
+
52
+ this.forward = function() {
53
+ return canGoForward() && go(1);
54
+ };
55
+
56
+ this.back = function() {
57
+ return canGoBack() && go(-1);
58
+ };
59
+
60
+ /**
61
+ * @param {string} defaultPath
62
+ */
63
+ this.init = function(defaultPath) {
64
+ const currentPath = defaultPath || (window.location.pathname + window.location.search);
65
+ const { route, params, query, path } = this.resolve(currentPath);
66
+ $history.push({ route, params, query, path });
67
+ $currentIndex = 0;
68
+
69
+ this.handleRouteChange(route, params, query, path);
70
+ }
71
+ };
@@ -0,0 +1,100 @@
1
+ import Validator from "./validator";
2
+ import ArgTypesError from "../errors/ArgTypesError";
3
+ import NativeDocumentError from "../errors/NativeDocumentError";
4
+
5
+ /**
6
+ *
7
+ * @type {{string: (function(*): {name: *, type: string, validate: function(*): boolean}),
8
+ * number: (function(*): {name: *, type: string, validate: function(*): boolean}),
9
+ * boolean: (function(*): {name: *, type: string, validate: function(*): boolean}),
10
+ * observable: (function(*): {name: *, type: string, validate: function(*): boolean}),
11
+ * element: (function(*): {name: *, type: string, validate: function(*): *}),
12
+ * function: (function(*): {name: *, type: string, validate: function(*): boolean}),
13
+ * object: (function(*): {name: *, type: string, validate: function(*): boolean}),
14
+ * objectNotNull: (function(*): {name: *, type: string, validate: function(*): *}),
15
+ * children: (function(*): {name: *, type: string, validate: function(*): *}),
16
+ * attributes: (function(*): {name: *, type: string, validate: function(*): *}),
17
+ * optional: (function(*): *&{optional: boolean}),
18
+ * oneOf: (function(*, ...[*]): {name: *, type: string, types: *[],
19
+ * validate: function(*): boolean})
20
+ * }}
21
+ */
22
+ export const ArgTypes = {
23
+ string: (name) => ({ name, type: 'string', validate: (v) => Validator.isString(v) }),
24
+ number: (name) => ({ name, type: 'number', validate: (v) => Validator.isNumber(v) }),
25
+ boolean: (name) => ({ name, type: 'boolean', validate: (v) => Validator.isBoolean(v) }),
26
+ observable: (name) => ({ name, type: 'observable', validate: (v) => Validator.isObservable(v) }),
27
+ element: (name) => ({ name, type: 'element', validate: (v) => Validator.isElement(v) }),
28
+ function: (name) => ({ name, type: 'function', validate: (v) => Validator.isFunction(v) }),
29
+ object: (name) => ({ name, type: 'object', validate: (v) => (Validator.isObject(v)) }),
30
+ objectNotNull: (name) => ({ name, type: 'object', validate: (v) => (Validator.isObject(v) && v !== null) }),
31
+ children: (name) => ({ name, type: 'children', validate: (v) => Validator.validateChildren(v) }),
32
+ attributes: (name) => ({ name, type: 'attributes', validate: (v) => Validator.validateAttributes(v) }),
33
+
34
+ // Optional arguments
35
+ optional: (argType) => ({ ...argType, optional: true }),
36
+
37
+ // Union types
38
+ oneOf: (name, ...argTypes) => ({
39
+ name,
40
+ type: 'oneOf',
41
+ types: argTypes,
42
+ validate: (v) => argTypes.some(type => type.validate(v))
43
+ })
44
+ };
45
+
46
+ /**
47
+ *
48
+ * @param {Array} args
49
+ * @param {Array} argSchema
50
+ * @param {string} fnName
51
+ */
52
+ const validateArgs = (args, argSchema, fnName = 'Function') => {
53
+ if (!argSchema) return;
54
+
55
+ const errors = [];
56
+
57
+ // Check the number of arguments
58
+ const requiredCount = argSchema.filter(arg => !arg.optional).length;
59
+ if (args.length < requiredCount) {
60
+ errors.push(`${fnName}: Expected at least ${requiredCount} arguments, got ${args.length}`);
61
+ }
62
+
63
+ // Validate each argument
64
+ argSchema.forEach((schema, index) => {
65
+ const position = index + 1;
66
+ const value = args[index];
67
+
68
+ if (value === undefined) {
69
+ if (!schema.optional) {
70
+ errors.push(`${fnName}: Missing required argument '${schema.name}' at position ${position}`);
71
+ }
72
+ return;
73
+ }
74
+
75
+ if (!schema.validate(value)) {
76
+ const valueTypeOf = value?.constructor?.name || typeof value;
77
+ errors.push(`${fnName}: Invalid argument '${schema.name}' at position ${position}, expected ${schema.type}, got ${valueTypeOf}`);
78
+ }
79
+ });
80
+
81
+ if (errors.length > 0) {
82
+ throw new ArgTypesError(`Argument validation failed`, errors);
83
+ }
84
+ };
85
+
86
+ /**
87
+ * @param {Function} fn
88
+ * @param {Array} argSchema
89
+ * @param {string} fnName
90
+ * @returns {Function}
91
+ */
92
+ export const withValidation = (fn, argSchema, fnName = 'Function') => {
93
+ if(!Validator.isArray(argSchema)) {
94
+ throw new NativeDocumentError('withValidation : argSchema must be an array');
95
+ }
96
+ return function(...args) {
97
+ validateArgs(args, argSchema, fn.name || fnName);
98
+ return fn.apply(this, args);
99
+ };
100
+ };
@@ -0,0 +1,34 @@
1
+ // Build configuration
2
+ const isProd = process.env.NODE_ENV === 'production';
3
+
4
+ const DebugManager = {
5
+ enabled: !isProd,
6
+
7
+ enable() {
8
+ this.enabled = true;
9
+ console.log('🔍 NativeDocument Debug Mode enabled');
10
+ },
11
+
12
+ disable() {
13
+ this.enabled = false;
14
+ },
15
+
16
+ log: isProd ? () => {} : function(category, message, data) {
17
+ if (!this.enabled) return;
18
+ console.group(`🔍 [${category}] ${message}`);
19
+ if (data) console.log(data);
20
+ console.trace();
21
+ console.groupEnd();
22
+ },
23
+
24
+ warn: isProd ? () => {} : function(category, message, data) {
25
+ if (!this.enabled) return;
26
+ console.warn(`⚠️ [${category}] ${message}`, data);
27
+ },
28
+
29
+ error: isProd ? () => {} : function(category, message, error) {
30
+ console.error(`❌ [${category}] ${message}`, error);
31
+ }
32
+ };
33
+
34
+ export default DebugManager;
@@ -0,0 +1,37 @@
1
+ /**
2
+ *
3
+ * @param {Function} fn
4
+ * @param {number} delay
5
+ * @param {{leading?:Boolean, trailing?:Boolean, debounce?:Boolean}}options
6
+ * @returns {(function(...[*]): void)|*}
7
+ */
8
+ export const throttle = function(fn, delay, options = {}) {
9
+ let timer = null;
10
+ let lastExecTime = 0;
11
+ const { leading = true, trailing = true, debounce = false } = options;
12
+
13
+ return function(...args) {
14
+ const now = Date.now();
15
+ if (debounce) {
16
+ // debounce mode: reset the timer for each call
17
+ clearTimeout(timer);
18
+ timer = setTimeout(() => fn.apply(this, args), delay);
19
+ return;
20
+ }
21
+ if (leading && now - lastExecTime >= delay) {
22
+ fn.apply(this, args);
23
+ lastExecTime = now;
24
+ }
25
+ if (trailing && !timer) {
26
+ timer = setTimeout(() => {
27
+ fn.apply(this, args);
28
+ lastExecTime = Date.now();
29
+ timer = null;
30
+ }, delay - (now - lastExecTime));
31
+ }
32
+ }
33
+ };
34
+
35
+ export const trim = function(str, char) {
36
+ return str.replace(new RegExp(`^[${char}]+|[${char}]+$`, 'g'), '');
37
+ }
@@ -0,0 +1,16 @@
1
+ import {withValidation} from "./args-types.js";
2
+
3
+
4
+ Function.prototype.args = function(...args) {
5
+ return withValidation(this, args);
6
+ };
7
+
8
+ Function.prototype.errorBoundary = function(callback) {
9
+ return (...args) => {
10
+ try {
11
+ return this.apply(this, args);
12
+ } catch(e) {
13
+ return callback(e);
14
+ }
15
+ };
16
+ }
@@ -0,0 +1,96 @@
1
+ import ObservableItem from "../data/ObservableItem";
2
+ import DebugManager from "./debug-manager";
3
+ import NativeDocumentError from "../errors/NativeDocumentError";
4
+ import ObservableChecker from "../data/ObservableChecker";
5
+
6
+ const Validator = {
7
+ isObservable(value) {
8
+ return value instanceof ObservableItem || value instanceof ObservableChecker;
9
+ },
10
+ isProxy(value) {
11
+ return value?.__isProxy__
12
+ },
13
+ isObservableChecker(value) {
14
+ return value instanceof ObservableChecker;
15
+ },
16
+ isArray(value) {
17
+ return Array.isArray(value);
18
+ },
19
+ isString(value) {
20
+ return typeof value === 'string';
21
+ },
22
+ isNumber(value) {
23
+ return typeof value === 'number';
24
+ },
25
+ isBoolean(value) {
26
+ return typeof value === 'boolean';
27
+ },
28
+ isFunction(value) {
29
+ return typeof value === 'function';
30
+ },
31
+ isObject(value) {
32
+ return typeof value === 'object';
33
+ },
34
+ isJson(value) {
35
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
36
+ },
37
+ isElement(value) {
38
+ return value instanceof HTMLElement || value instanceof DocumentFragment || value instanceof Text;
39
+ },
40
+ isFragment(value) {
41
+ return value instanceof DocumentFragment;
42
+ },
43
+ isStringOrObservable(value) {
44
+ return this.isString(value) || this.isObservable(value);
45
+ },
46
+
47
+ isValidChild(child) {
48
+ return child === null ||
49
+ this.isElement(child) ||
50
+ this.isObservable(child) ||
51
+ ['string', 'number', 'boolean'].includes(typeof child);
52
+ },
53
+ isValidChildren(children) {
54
+ if (!Array.isArray(children)) {
55
+ children = [children];
56
+ }
57
+
58
+ const invalid = children.filter(child => !this.isValidChild(child));
59
+ return invalid.length === 0;
60
+ },
61
+ validateChildren(children) {
62
+ if (!Array.isArray(children)) {
63
+ children = [children];
64
+ }
65
+
66
+ const invalid = children.filter(child => !this.isValidChild(child));
67
+ if (invalid.length > 0) {
68
+ throw new NativeDocumentError(`Invalid children detected: ${invalid.map(i => typeof i).join(', ')}`);
69
+ }
70
+
71
+ return children;
72
+ },
73
+
74
+ validateAttributes(attributes) {
75
+ if (!attributes || typeof attributes !== 'object') {
76
+ return attributes;
77
+ }
78
+
79
+ const reserved = [];
80
+ const foundReserved = Object.keys(attributes).filter(key => reserved.includes(key));
81
+
82
+ if (foundReserved.length > 0) {
83
+ DebugManager.warn('Validator', `Reserved attributes found: ${foundReserved.join(', ')}`);
84
+ }
85
+
86
+ return attributes;
87
+ },
88
+
89
+ validateEventCallback(callback) {
90
+ if (typeof callback !== 'function') {
91
+ throw new NativeDocumentError('Event callback must be a function');
92
+ }
93
+ }
94
+ };
95
+
96
+ export default Validator;
@@ -0,0 +1,94 @@
1
+ import Validator from "../utils/validator";
2
+ import NativeDocumentError from "../errors/NativeDocumentError";
3
+
4
+
5
+ /**
6
+ *
7
+ * @param {HTMLElement} element
8
+ * @param {string} className
9
+ * @param {string} value
10
+ */
11
+ const toggleClassItem = function(element, className, value) {
12
+ if(value) {
13
+ element.classList.add(className);
14
+ } else {
15
+ element.classList.remove(className);
16
+ }
17
+ }
18
+
19
+ /**
20
+ *
21
+ * @param {HTMLElement} element
22
+ * @param {Object} data
23
+ */
24
+ function bindClassAttribute(element, data) {
25
+ for(let className in data) {
26
+ const value = data[className];
27
+ if(Validator.isObservable(value)) {
28
+ toggleClassItem(element, className, value.val());
29
+ value.subscribe(newValue => toggleClassItem(element, className, newValue));
30
+ continue;
31
+ }
32
+ toggleClassItem(element, className, value);
33
+ }
34
+ }
35
+
36
+ /**
37
+ *
38
+ * @param {HTMLElement} element
39
+ * @param {Object} data
40
+ */
41
+ function bindStyleAttribute(element, data) {
42
+ for(let styleName in data) {
43
+ const value = data[styleName];
44
+ if(Validator.isObservable(value)) {
45
+ element.style[styleName] = value.val();
46
+ value.subscribe(newValue => {
47
+ element.style[styleName] = newValue;
48
+ });
49
+ continue;
50
+ }
51
+ element.style[styleName] = value;
52
+ }
53
+ }
54
+
55
+ /**
56
+ *
57
+ * @param {HTMLElement} element
58
+ * @param {Object} attributes
59
+ */
60
+ export default function AttributesWrapper(element, attributes) {
61
+
62
+ Validator.validateAttributes(attributes);
63
+
64
+ if(!Validator.isObject(attributes)) {
65
+ console.log(attributes);
66
+ throw new NativeDocumentError('Attributes must be an object');
67
+ }
68
+
69
+ for(let attributeName in attributes) {
70
+ const value = attributes[attributeName];
71
+ if(Validator.isObservable(value)) {
72
+ value.subscribe(newValue => element.setAttribute(attributeName, newValue));
73
+ element.setAttribute(attributeName, value.val());
74
+ if(attributeName === 'value') {
75
+ if(['checkbox', 'radio'].includes(element.type)) {
76
+ element.addEventListener('input', () => value.set(element.checked));
77
+ } else {
78
+ element.addEventListener('input', () => value.set(element.value));
79
+ }
80
+ }
81
+ continue;
82
+ }
83
+ if(attributeName === 'class' && Validator.isJson(value)) {
84
+ bindClassAttribute(element, value);
85
+ continue;
86
+ }
87
+ if(attributeName === 'style' && Validator.isJson(value)) {
88
+ bindStyleAttribute(element, value);
89
+ continue;
90
+ }
91
+ element.setAttribute(attributeName, value);
92
+ }
93
+ return element;
94
+ }
@@ -0,0 +1,51 @@
1
+ import {throttle} from "../utils/helpers";
2
+
3
+ const DocumentObserver = {
4
+ elements: new Map(),
5
+ observer: null,
6
+ checkMutation: throttle(function() {
7
+ for(const [element, data] of DocumentObserver.elements.entries()) {
8
+ const isCurrentlyInDom = document.body.contains(element);
9
+ if(isCurrentlyInDom && !data.inDom) {
10
+ data.inDom = true;
11
+ data.mounted.forEach(callback => callback(element));
12
+ } else if(!isCurrentlyInDom && data.inDom) {
13
+ data.inDom = false;
14
+ data.unmounted.forEach(callback => callback(element));
15
+ }
16
+ }
17
+ }, 10, { debounce: true }),
18
+ /**
19
+ *
20
+ * @param {HTMLElement} element
21
+ * @returns {{watch: (function(): Map<any, any>), disconnect: (function(): boolean), mounted: (function(*): Set<any>), unmounted: (function(*): Set<any>)}}
22
+ */
23
+ watch: function(element) {
24
+ let data = {};
25
+ if(DocumentObserver.elements.has(element)) {
26
+ data = DocumentObserver.elements.get(element);
27
+ } else {
28
+ const inDom = document.body.contains(element);
29
+ data = {
30
+ inDom,
31
+ mounted: new Set(),
32
+ unmounted: new Set(),
33
+ };
34
+ DocumentObserver.elements.set(element, data);
35
+ }
36
+
37
+ return {
38
+ watch: () => DocumentObserver.elements.set(element, data),
39
+ disconnect: () => DocumentObserver.elements.delete(element),
40
+ mounted: (callback) => data.mounted.add(callback),
41
+ unmounted: (callback) => data.unmounted.add(callback)
42
+ };
43
+ }
44
+ };
45
+
46
+ DocumentObserver.observer = new MutationObserver(DocumentObserver.checkMutation);
47
+ DocumentObserver.observer.observe(document.body, {
48
+ childList: true,
49
+ subtree: true,
50
+ });
51
+ export default DocumentObserver;