@livenetworks/ashlar 1.5.1 → 1.6.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 (217) hide show
  1. package/README.md +189 -177
  2. package/js/COMPONENTS.md +55 -49
  3. package/js/index.js +22 -5
  4. package/js/ln-accordion/README.md +186 -137
  5. package/js/ln-accordion/ln-accordion.js +1 -1
  6. package/js/ln-ajax/README.md +73 -4
  7. package/js/ln-ajax/ln-ajax.js +1 -1
  8. package/js/ln-ajax/src/ln-ajax.js +13 -13
  9. package/js/ln-api-connector/README.md +261 -150
  10. package/js/ln-api-connector/ln-api-connector.js +1 -1
  11. package/js/ln-api-connector/src/ln-api-connector.js +368 -265
  12. package/js/ln-api-queue/README.md +219 -0
  13. package/js/ln-api-queue/ln-api-queue.js +1 -0
  14. package/js/ln-api-queue/src/ln-api-queue.js +320 -0
  15. package/js/ln-api-queue/src/queue-storage.js +455 -0
  16. package/js/ln-autoresize/README.md +47 -0
  17. package/js/ln-autoresize/ln-autoresize.js +1 -1
  18. package/js/ln-autosave/README.md +33 -1
  19. package/js/ln-autosave/ln-autosave.js +1 -1
  20. package/js/ln-autosave/src/ln-autosave.js +4 -4
  21. package/js/ln-circular-progress/README.md +27 -13
  22. package/js/ln-circular-progress/ln-circular-progress.js +1 -1
  23. package/js/ln-circular-progress/src/ln-circular-progress.js +18 -5
  24. package/js/ln-confirm/README.md +162 -86
  25. package/js/ln-confirm/_ln-confirm.scss +11 -0
  26. package/js/ln-confirm/ln-confirm.js +1 -1
  27. package/js/ln-confirm/src/ln-confirm.js +77 -41
  28. package/js/ln-core/README.md +896 -0
  29. package/js/ln-core/hash.js +92 -0
  30. package/js/ln-core/helpers.js +337 -8
  31. package/js/ln-core/index.js +4 -2
  32. package/js/ln-core/persist.js +29 -3
  33. package/js/ln-core/positioning.js +1 -35
  34. package/js/ln-core/window-cache.js +213 -0
  35. package/js/ln-couchdb-connector/README.md +177 -156
  36. package/js/ln-couchdb-connector/ln-couchdb-connector.js +1 -1
  37. package/js/ln-couchdb-connector/src/ln-couchdb-connector.js +398 -348
  38. package/js/ln-data-coordinator/README.md +536 -165
  39. package/js/ln-data-coordinator/ln-data-coordinator.js +1 -1
  40. package/js/ln-data-coordinator/src/data-read-policy.js +18 -0
  41. package/js/ln-data-coordinator/src/ln-data-coordinator.js +1042 -249
  42. package/js/ln-data-coordinator/src/mutation-receipts.js +46 -0
  43. package/js/ln-data-store/README.md +241 -94
  44. package/js/ln-data-store/ln-data-store.js +1 -1
  45. package/js/ln-data-store/src/ln-data-store.js +694 -699
  46. package/js/ln-date/README.md +39 -1
  47. package/js/ln-date/ln-date-dev.scss +14 -0
  48. package/js/ln-date/ln-date.js +1 -1
  49. package/js/ln-date/src/ln-date.js +236 -178
  50. package/js/ln-debug/README.md +58 -0
  51. package/js/ln-debug/ln-debug.js +1 -0
  52. package/js/ln-debug/src/ln-debug.js +19 -0
  53. package/js/ln-dropdown/README.md +31 -8
  54. package/js/ln-dropdown/ln-dropdown.js +1 -1
  55. package/js/ln-dropdown/src/ln-dropdown.js +30 -13
  56. package/js/ln-editor/README.md +274 -0
  57. package/js/ln-editor/ln-editor.js +1 -0
  58. package/js/ln-editor/ln-editor.scss +43 -0
  59. package/js/ln-editor/src/ln-editor.js +619 -0
  60. package/js/ln-external-links/README.md +44 -5
  61. package/js/ln-external-links/ln-external-links.js +1 -1
  62. package/js/ln-fill/README.md +199 -0
  63. package/js/ln-fill/ln-fill.js +1 -0
  64. package/js/ln-fill/src/ln-fill.js +87 -0
  65. package/js/ln-filter/README.md +92 -2
  66. package/js/ln-filter/ln-filter-dev.scss +8 -0
  67. package/js/ln-filter/ln-filter.js +1 -1
  68. package/js/ln-filter/src/ln-filter.js +22 -7
  69. package/js/ln-form/README.md +212 -49
  70. package/js/ln-form/ln-form.js +1 -1
  71. package/js/ln-form/src/ln-form.js +99 -199
  72. package/js/ln-http/README.md +125 -89
  73. package/js/ln-http/ln-http.js +1 -1
  74. package/js/ln-http/src/ln-http.js +32 -13
  75. package/js/ln-icon/README.md +130 -0
  76. package/js/ln-icon/ln-icon.js +1 -0
  77. package/js/{ln-icons/src/ln-icons.js → ln-icon/src/ln-icon.js} +20 -15
  78. package/js/ln-link/README.md +47 -9
  79. package/js/ln-link/ln-link.js +1 -1
  80. package/js/ln-link/src/ln-link.js +22 -6
  81. package/js/ln-list/README.md +129 -0
  82. package/js/ln-list/ln-list.js +1 -0
  83. package/js/ln-list/ln-list.scss +28 -0
  84. package/js/ln-list/src/ln-list.js +1216 -0
  85. package/js/ln-modal/README.md +185 -154
  86. package/js/ln-modal/ln-modal-dev.scss +13 -0
  87. package/js/ln-modal/ln-modal.js +1 -1
  88. package/js/ln-modal/ln-modal.scss +28 -4
  89. package/js/ln-modal/src/ln-modal.js +137 -201
  90. package/js/ln-modal-coordinator/README.md +99 -0
  91. package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +8 -0
  92. package/js/ln-modal-coordinator/ln-modal-coordinator.js +1 -0
  93. package/js/ln-modal-coordinator/src/ln-modal-coordinator.js +345 -0
  94. package/js/ln-nav/README.md +46 -3
  95. package/js/ln-nav/ln-nav.js +1 -1
  96. package/js/ln-nav/src/ln-nav.js +92 -127
  97. package/js/ln-number/README.md +28 -1
  98. package/js/ln-number/ln-number-dev.scss +13 -0
  99. package/js/ln-number/ln-number.js +1 -1
  100. package/js/ln-number/src/ln-number.js +443 -350
  101. package/js/ln-options/README.md +66 -0
  102. package/js/ln-options/ln-options.js +1 -0
  103. package/js/ln-options/src/ln-options.js +74 -0
  104. package/js/ln-popover/README.md +40 -5
  105. package/js/ln-popover/ln-popover.js +1 -1
  106. package/js/ln-popover/src/ln-popover.js +38 -11
  107. package/js/ln-progress/README.md +413 -442
  108. package/js/ln-progress/ln-progress.js +1 -1
  109. package/js/ln-progress/src/ln-progress.js +98 -150
  110. package/js/ln-router/README.md +432 -0
  111. package/js/ln-router/ln-router.js +1 -0
  112. package/js/ln-router/src/ln-router.js +539 -0
  113. package/js/ln-search/README.md +158 -83
  114. package/js/ln-search/ln-search-dev.scss +8 -0
  115. package/js/ln-search/ln-search.js +1 -1
  116. package/js/ln-search/src/ln-search.js +12 -9
  117. package/js/ln-slug/README.md +110 -0
  118. package/js/ln-slug/ln-slug.js +1 -0
  119. package/js/ln-slug/src/ln-slug.js +74 -0
  120. package/js/ln-sort/README.md +170 -0
  121. package/js/ln-sort/ln-sort-dev.scss +8 -0
  122. package/js/ln-sort/ln-sort.js +1 -0
  123. package/js/ln-sort/src/ln-sort.js +178 -0
  124. package/js/ln-sortable/README.md +133 -95
  125. package/js/ln-sortable/ln-sortable.js +1 -1
  126. package/js/ln-stat/README.md +55 -0
  127. package/js/ln-stat/ln-stat.js +1 -0
  128. package/js/ln-stat/src/ln-stat.js +55 -0
  129. package/js/ln-table/README.md +285 -101
  130. package/js/ln-table/ln-table-dev.scss +17 -0
  131. package/js/ln-table/ln-table.js +1 -1
  132. package/js/ln-table/ln-table.scss +1 -1
  133. package/js/ln-table/src/ln-table.js +1493 -473
  134. package/js/ln-table-coordinator/README.md +73 -0
  135. package/js/ln-table-coordinator/ln-table-coordinator.js +1 -0
  136. package/js/ln-table-coordinator/src/ln-table-coordinator.js +160 -0
  137. package/js/ln-tabs/README.md +221 -137
  138. package/js/ln-tabs/ln-tabs-dev.scss +18 -0
  139. package/js/ln-tabs/ln-tabs.js +1 -1
  140. package/js/ln-tabs/src/ln-tabs.js +41 -24
  141. package/js/ln-time/README.md +14 -0
  142. package/js/ln-time/ln-time.js +1 -1
  143. package/js/ln-time/src/ln-time.js +223 -192
  144. package/js/ln-toast/README.md +178 -122
  145. package/js/ln-toast/ln-toast.js +1 -15
  146. package/js/ln-toast/src/ln-toast.js +232 -210
  147. package/js/ln-toast/template.html +16 -12
  148. package/js/ln-toggle/README.md +61 -2
  149. package/js/ln-toggle/ln-toggle-dev.scss +13 -0
  150. package/js/ln-toggle/ln-toggle.js +1 -1
  151. package/js/ln-toggle/src/ln-toggle.js +56 -43
  152. package/js/ln-tooltip/README.md +95 -58
  153. package/js/ln-tooltip/ln-tooltip-dev.scss +9 -0
  154. package/js/ln-tooltip/ln-tooltip.js +1 -1
  155. package/js/ln-tooltip/src/ln-tooltip.js +45 -6
  156. package/js/ln-translations/README.md +118 -96
  157. package/js/ln-translations/ln-translations.js +1 -1
  158. package/js/ln-translations/src/ln-translations.js +10 -5
  159. package/js/ln-upload/README.md +85 -10
  160. package/js/ln-upload/ln-upload.js +1 -1
  161. package/js/ln-upload/src/ln-upload.js +26 -10
  162. package/js/ln-validate/README.md +72 -0
  163. package/js/ln-validate/ln-validate-dev.scss +15 -0
  164. package/js/ln-validate/ln-validate.js +1 -1
  165. package/js/ln-validate/src/ln-validate.js +64 -0
  166. package/package.json +61 -56
  167. package/scss/base/_global.scss +2 -2
  168. package/scss/base/_typography.scss +1 -2
  169. package/scss/components/_date.scss +1 -0
  170. package/scss/components/_dropdown.scss +0 -5
  171. package/scss/components/_editor.scss +5 -0
  172. package/scss/components/_filter.scss +26 -25
  173. package/scss/components/_form.scss +7 -0
  174. package/scss/components/_ln-table.scss +13 -3
  175. package/scss/components/_sort.scss +10 -0
  176. package/scss/components/_table.scss +36 -23
  177. package/scss/components/_toast.scss +68 -41
  178. package/scss/components/_toggle.scss +16 -0
  179. package/scss/config/_density.scss +205 -117
  180. package/scss/config/_icons.scss +10 -9
  181. package/scss/config/_tokens.scss +69 -47
  182. package/scss/config/mixins/_accordion.scss +2 -7
  183. package/scss/config/mixins/_alert.scss +4 -4
  184. package/scss/config/mixins/_breadcrumbs.scss +2 -2
  185. package/scss/config/mixins/_btn.scss +1 -1
  186. package/scss/config/mixins/_card.scss +89 -14
  187. package/scss/config/mixins/_collapsible.scss +3 -0
  188. package/scss/config/mixins/_colors.scss +1 -1
  189. package/scss/config/mixins/_diagnostics.scss +11 -0
  190. package/scss/config/mixins/_display.scss +1 -0
  191. package/scss/config/mixins/_dropdown.scss +3 -5
  192. package/scss/config/mixins/_editor.scss +124 -0
  193. package/scss/config/mixins/_empty-state.scss +7 -0
  194. package/scss/config/mixins/_filter.scss +3 -5
  195. package/scss/config/mixins/_footer.scss +1 -2
  196. package/scss/config/mixins/_form.scss +207 -21
  197. package/scss/config/mixins/_index.scss +2 -0
  198. package/scss/config/mixins/_ln-table.scss +41 -2
  199. package/scss/config/mixins/_modal.scss +149 -66
  200. package/scss/config/mixins/_nav.scss +3 -4
  201. package/scss/config/mixins/_popover.scss +39 -0
  202. package/scss/config/mixins/_sidebar.scss +1 -1
  203. package/scss/config/mixins/_sort.scss +42 -0
  204. package/scss/config/mixins/_table.scss +85 -60
  205. package/scss/config/mixins/_toast.scss +179 -152
  206. package/scss/config/mixins/_tooltip.scss +17 -7
  207. package/scss/config/mixins/_typography.scss +4 -2
  208. package/scss/ln-ashlar-dev.scss +27 -9
  209. package/scss/ln-ashlar.scss +7 -1
  210. package/js/ln-data-table/README.md +0 -110
  211. package/js/ln-data-table/ln-data-table.js +0 -1
  212. package/js/ln-data-table/ln-data-table.scss +0 -10
  213. package/js/ln-data-table/src/ln-data-table.js +0 -1087
  214. package/js/ln-icons/README.md +0 -88
  215. package/js/ln-icons/ln-icons.js +0 -1
  216. package/js/ln-table/ln-table-sort.js +0 -1
  217. package/js/ln-table/src/ln-table-sort.js +0 -156
@@ -1,4 +1,4 @@
1
- import { guardBody } from '../../ln-core';
1
+ import { registerComponent, dispatch, dispatchCancelable } from '../../ln-core';
2
2
 
3
3
  (function () {
4
4
  const DOM_SELECTOR = 'data-ln-nav';
@@ -6,8 +6,6 @@ import { guardBody } from '../../ln-core';
6
6
 
7
7
  if (window[DOM_ATTRIBUTE] !== undefined) return;
8
8
 
9
- const navInstances = new WeakMap();
10
-
11
9
  // ─── pushState singleton patch ──────────────────────────────
12
10
  const _pushStateCallbacks = [];
13
11
 
@@ -20,158 +18,125 @@ import { guardBody } from '../../ln-core';
20
18
  history._lnNavPatched = true;
21
19
  }
22
20
 
23
- // ─── Constructor ───────────────────────────────────────────
24
-
25
- function constructor(navElement) {
26
- if (!navElement.hasAttribute(DOM_SELECTOR)) return;
27
- if (navInstances.has(navElement)) return;
21
+ // ─── Component ─────────────────────────────────────────────
28
22
 
29
- const activeClass = navElement.getAttribute(DOM_SELECTOR);
30
- if (!activeClass) return;
23
+ function _component(dom) {
24
+ this.dom = dom;
25
+ this.activeClass = dom.getAttribute(DOM_SELECTOR) || 'active';
26
+ this.exact = dom.hasAttribute('data-ln-nav-exact');
31
27
 
32
- const instance = _initializeNav(navElement, activeClass);
33
- navInstances.set(navElement, instance);
34
- navElement[DOM_ATTRIBUTE] = instance;
35
- }
28
+ this.updateHandler = () => this.update();
36
29
 
37
- function _initializeNav(navElement, activeClass) {
38
- let links = Array.from(navElement.querySelectorAll('a'));
30
+ // Listen to navigation events
31
+ window.addEventListener('popstate', this.updateHandler);
32
+ _pushStateCallbacks.push(this.updateHandler);
39
33
 
40
- _updateActiveState(links, activeClass, window.location.pathname);
34
+ // Observe DOM changes inside this nav element
35
+ this.observer = new MutationObserver(() => this.update());
36
+ this.observer.observe(dom, { childList: true, subtree: true });
41
37
 
42
- const updateHandler = function () {
43
- links = Array.from(navElement.querySelectorAll('a'));
44
- _updateActiveState(links, activeClass, window.location.pathname);
45
- };
38
+ // Perform initial highlight
39
+ this.update();
46
40
 
47
- window.addEventListener('popstate', updateHandler);
48
- _pushStateCallbacks.push(updateHandler);
49
-
50
- const observer = new MutationObserver(function (mutations) {
51
- for (const mutation of mutations) {
52
- if (mutation.type === 'childList') {
53
- for (const node of mutation.addedNodes) {
54
- if (node.nodeType === 1) {
55
- if (node.tagName === 'A') {
56
- links.push(node);
57
- _updateActiveState([node], activeClass, window.location.pathname);
58
- } else if (node.querySelectorAll) {
59
- const newLinks = Array.from(node.querySelectorAll('a'));
60
- links = links.concat(newLinks);
61
- _updateActiveState(newLinks, activeClass, window.location.pathname);
62
- }
63
- }
64
- }
65
-
66
- for (const node of mutation.removedNodes) {
67
- if (node.nodeType === 1) {
68
- if (node.tagName === 'A') {
69
- links = links.filter(function (link) { return link !== node; });
70
- } else if (node.querySelectorAll) {
71
- const removedLinks = Array.from(node.querySelectorAll('a'));
72
- links = links.filter(function (link) {
73
- return !removedLinks.includes(link);
74
- });
75
- }
76
- }
77
- }
78
- }
79
- }
80
- });
81
-
82
- observer.observe(navElement, { childList: true, subtree: true });
83
-
84
- return {
85
- navElement: navElement,
86
- activeClass: activeClass,
87
- observer: observer,
88
- updateHandler: updateHandler,
89
- destroy: function () {
90
- observer.disconnect();
91
- window.removeEventListener('popstate', updateHandler);
92
- const idx = _pushStateCallbacks.indexOf(updateHandler);
93
- if (idx !== -1) _pushStateCallbacks.splice(idx, 1);
94
- navInstances.delete(navElement);
95
- delete navElement[DOM_ATTRIBUTE];
96
- }
97
- };
41
+ return this;
98
42
  }
99
43
 
100
- function _normalizeUrl(url) {
101
- try {
102
- const urlObj = new URL(url, window.location.href);
103
- return urlObj.pathname.replace(/\/$/, '') || '/';
104
- } catch (e) {
105
- return url.replace(/\/$/, '') || '/';
106
- }
107
- }
44
+ _component.prototype.update = function () {
45
+ if (!this.activeClass) return;
46
+ const before = dispatchCancelable(this.dom, 'ln-nav:before-update', { target: this.dom });
47
+ if (before.defaultPrevented) return;
108
48
 
109
- function _updateActiveState(links, activeClass, currentPath) {
49
+ const links = Array.from(this.dom.querySelectorAll('a'));
50
+ const currentPath = window.location.pathname;
110
51
  const normalizedCurrent = _normalizeUrl(currentPath);
111
52
 
112
53
  for (const link of links) {
113
54
  const href = link.getAttribute('href');
114
- if (!href) continue;
115
-
116
- const normalizedHref = _normalizeUrl(href);
55
+ if (!href || href === '#' || href.startsWith('#') || href.startsWith('javascript:') || href.startsWith('mailto:') || href.startsWith('tel:')) {
56
+ link.classList.remove(this.activeClass);
57
+ link.removeAttribute('aria-current');
58
+ continue;
59
+ }
117
60
 
118
- link.classList.remove(activeClass);
61
+ // Exclude external links
62
+ if (link.hostname && link.hostname !== window.location.hostname) {
63
+ link.classList.remove(this.activeClass);
64
+ link.removeAttribute('aria-current');
65
+ continue;
66
+ }
119
67
 
68
+ const normalizedHref = _normalizeUrl(href);
120
69
  const isExact = normalizedHref === normalizedCurrent;
121
- const isParent = normalizedHref !== '/' && normalizedCurrent.startsWith(normalizedHref + '/');
70
+ const isParent = !this.exact && normalizedHref !== '/' && normalizedCurrent.startsWith(normalizedHref + '/');
122
71
 
123
72
  if (isExact || isParent) {
124
- link.classList.add(activeClass);
73
+ link.classList.add(this.activeClass);
74
+ link.setAttribute('aria-current', 'page');
75
+ } else {
76
+ link.classList.remove(this.activeClass);
77
+ link.removeAttribute('aria-current');
125
78
  }
126
79
  }
127
- }
128
80
 
129
- // ─── Global DOM Observer ───────────────────────────────────
130
-
131
- function _domObserver() {
132
- guardBody(function () {
133
- const observer = new MutationObserver(function (mutations) {
134
- for (const mutation of mutations) {
135
- if (mutation.type === 'childList') {
136
- for (const node of mutation.addedNodes) {
137
- if (node.nodeType === 1) {
138
- if (node.hasAttribute && node.hasAttribute(DOM_SELECTOR)) {
139
- constructor(node);
140
- }
141
- if (node.querySelectorAll) {
142
- for (const el of node.querySelectorAll('[' + DOM_SELECTOR + ']')) {
143
- constructor(el);
144
- }
145
- }
146
- }
147
- }
148
- } else if (mutation.type === 'attributes') {
149
- if (mutation.target.hasAttribute && mutation.target.hasAttribute(DOM_SELECTOR)) {
150
- constructor(mutation.target);
151
- }
152
- }
153
- }
154
- });
81
+ dispatch(this.dom, 'ln-nav:update', { target: this.dom });
82
+ };
83
+
84
+ _component.prototype.destroy = function () {
85
+ if (!this.dom[DOM_ATTRIBUTE]) return;
86
+ if (this.observer) {
87
+ this.observer.disconnect();
88
+ }
89
+ window.removeEventListener('popstate', this.updateHandler);
90
+ const idx = _pushStateCallbacks.indexOf(this.updateHandler);
91
+ if (idx !== -1) {
92
+ _pushStateCallbacks.splice(idx, 1);
93
+ }
94
+ dispatch(this.dom, 'ln-nav:destroyed', { target: this.dom });
95
+ delete this.dom[DOM_ATTRIBUTE];
96
+ };
155
97
 
156
- observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: [DOM_SELECTOR] });
157
- }, 'ln-nav');
98
+ // ─── Helper Functions ──────────────────────────────────────
99
+
100
+ function _normalizeUrl(url) {
101
+ try {
102
+ const urlObj = new URL(url, window.location.href);
103
+ return urlObj.pathname.replace(/\/$/, '') || '/';
104
+ } catch (e) {
105
+ return url.replace(/\/$/, '') || '/';
106
+ }
158
107
  }
159
108
 
160
- // ─── Init ──────────────────────────────────────────────────
109
+ // ─── Attribute Sync ────────────────────────────────────────
161
110
 
162
- window[DOM_ATTRIBUTE] = constructor;
111
+ function _syncAttribute(el, attrName) {
112
+ const instance = el[DOM_ATTRIBUTE];
113
+ if (!instance) return;
163
114
 
164
- function _initializeAll() {
165
- for (const el of document.querySelectorAll('[' + DOM_SELECTOR + ']')) {
166
- constructor(el);
115
+ if (attrName === DOM_SELECTOR) {
116
+ if (!el.hasAttribute(DOM_SELECTOR)) {
117
+ instance.destroy();
118
+ return;
119
+ }
120
+ const oldClass = instance.activeClass;
121
+ const newClass = el.getAttribute(DOM_SELECTOR) || 'active';
122
+ if (oldClass !== newClass) {
123
+ const links = el.querySelectorAll('a');
124
+ for (const link of links) {
125
+ if (oldClass) link.classList.remove(oldClass);
126
+ }
127
+ instance.activeClass = newClass;
128
+ }
129
+ } else if (attrName === 'data-ln-nav-exact') {
130
+ instance.exact = el.hasAttribute('data-ln-nav-exact');
167
131
  }
132
+
133
+ instance.update();
168
134
  }
169
135
 
170
- _domObserver();
136
+ // ─── Init ──────────────────────────────────────────────────
171
137
 
172
- if (document.readyState === 'loading') {
173
- document.addEventListener('DOMContentLoaded', _initializeAll);
174
- } else {
175
- _initializeAll();
176
- }
138
+ registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _component, 'ln-nav', {
139
+ extraAttributes: ['data-ln-nav-exact'],
140
+ onAttributeChange: _syncAttribute
141
+ });
177
142
  })();
@@ -8,7 +8,12 @@ Real-time locale-aware number formatting for input fields.
8
8
  <input type="number" name="amount" data-ln-number>
9
9
  ```
10
10
 
11
- The component creates a hidden input that holds the raw numeric value for form submission and formats the visible input with locale-aware thousand separators. See [`docs/js/number.md`](../../docs/js/number.md#html) for the resulting DOM.
11
+ The component creates a hidden input that holds the raw numeric value for form submission and formats the visible input with locale-aware thousand separators. After initialization:
12
+
13
+ ```html
14
+ <input type="text" inputmode="decimal" data-ln-number>
15
+ <input type="hidden" name="amount" value="1234567">
16
+ ```
12
17
 
13
18
  ## Loading & Source Files
14
19
 
@@ -120,3 +125,25 @@ on the visible input and works as expected:
120
125
  Works automatically. `serializeForm()` reads the hidden input (which has the
121
126
  `name`). `populateForm()` sets the hidden input's value, which triggers the
122
127
  display update.
128
+
129
+ ---
130
+
131
+ ## 🔧 Internals
132
+
133
+ Source: `js/ln-number/ln-number.js`. Each `[data-ln-number]` gets a `_component` instance at `element.lnNumber`, holding `dom` (visible input), `_hidden` (hidden input), and bound `_onInput`/`_onPaste` handlers.
134
+
135
+ ### Formatter cache
136
+
137
+ One `Intl.NumberFormat` instance per unique locale, cached at module level (`_formatters[locale]`), storing the formatter plus `groupSep`/`decimalSep` extracted once via `formatToParts()`. Separate cache keys (`locale + '|d' + max`, `locale + '|u' + n`) exist for decimal-fixed and user-decimal-preserving formatters. Reused across all instances sharing a locale — `Intl.NumberFormat` construction is the expensive part, not the format call.
138
+
139
+ ### Input flow
140
+
141
+ On every `input` event: empty value clears the hidden input and dispatches; a bare `-` (user starting a negative number) is left alone. Otherwise: cursor position is saved as a digit-count left of `selectionStart` (not a raw index — formatting inserts/removes separators, so digit-count is the only stable anchor). The typed string is parsed (strip `groupSep`, swap `decimalSep` for `.`); a trailing decimal point or trailing zeros after the decimal point short-circuit to a hidden-only update (so `"12."` doesn't get reformatted mid-type and eat the point the user just typed). Otherwise the decimal limit is enforced (truncated, not rounded), the value is formatted via `Intl.NumberFormat`, `dom.value` is set, the cursor is restored by walking the formatted string until the saved digit-count is reached, and `hidden.value` is set to the raw numeric string. `ln-number:input` dispatches last.
142
+
143
+ ### Hidden input value interceptor
144
+
145
+ `hidden.value` is wrapped via `Object.defineProperty` so programmatic sets (e.g. `populateForm()` writing `hidden.value = "1234"`) route through the same parse → format → display path as user input. Internal code writes through `_setHiddenRaw()`, which calls the prototype's original setter directly — bypassing the custom setter avoids the interceptor re-triggering itself (infinite loop).
146
+
147
+ ### Auto-init
148
+
149
+ A single `document.body` `MutationObserver` watches `childList` (subtree — new `[data-ln-number]` elements auto-initialize via `findElements`) and `attributes` on `data-ln-number` (attribute added to an existing element initializes it in place).
@@ -0,0 +1,13 @@
1
+ @use '../../scss/config/mixins' as *;
2
+
3
+ [data-ln-debug] {
4
+ // 1. Number element misuse: data-ln-number belongs on <input>, <span>, <time>, or <td>
5
+ [data-ln-number]:not(input, span, time, td, p, div, b, strong, data) {
6
+ @include dev-inline-error("data-ln-number belongs on <input>, <span>, <time>, or <td>");
7
+ }
8
+
9
+ // 2. Number input missing ID: input[data-ln-number] must have an id for proper labeling/association
10
+ input[data-ln-number]:not([id]) {
11
+ @include dev-inline-error("data-ln-number is missing required 'id' attribute");
12
+ }
13
+ }
@@ -1 +1 @@
1
- (function(){"use strict";function v(s,i,u){s.dispatchEvent(new CustomEvent(i,{bubbles:!0,detail:u||{}}))}function S(s,i){if(!document.body){document.addEventListener("DOMContentLoaded",function(){S(s,i)}),console.warn("["+i+'] Script loaded before <body> — add "defer" to your <script> tag');return}s()}function x(s,i,u,p){if(s.nodeType!==1)return;const y=i.indexOf("[")!==-1||i.indexOf(".")!==-1||i.indexOf("#")!==-1?i:"["+i+"]",m=Array.from(s.querySelectorAll(y));s.matches&&s.matches(y)&&m.push(s);for(const h of m)h[u]||(h[u]=new p(h))}function E(s){const i=s.closest("[lang]");return(i?i.lang:null)||navigator.language}function O(s,i,{get:u,set:p}){Object.defineProperty(s,"value",{get:function(){return u?u.call(this):i.get.call(this)},set:function(g){p?p.call(this,g,y=>i.set.call(this,y)):i.set.call(this,g)},configurable:!0})}function I(s,i,u,p,g={}){const y=g.extraAttributes||[],m=g.onAttributeChange||null,h=g.onInit||null;function t(a){const e=a||document.body;x(e,s,i,u),h&&h(e)}return S(function(){const a=new MutationObserver(function(c){for(let r=0;r<c.length;r++){const n=c[r];if(n.type==="childList"){for(let o=0;o<n.addedNodes.length;o++){const l=n.addedNodes[o];l.nodeType===1&&(x(l,s,i,u),h&&h(l))}for(let o=0;o<n.removedNodes.length;o++){const l=n.removedNodes[o];if(l.nodeType===1){const _=s.indexOf("[")!==-1||s.indexOf(".")!==-1||s.indexOf("#")!==-1?s:"["+s+"]",F=Array.from(l.querySelectorAll(_));l.matches&&l.matches(_)&&F.push(l);for(let b=0;b<F.length;b++){const D=F[b];if(!document.contains(D)){const N=D[i];N&&typeof N.destroy=="function"&&N.destroy()}}}}}else n.type==="attributes"&&(m&&n.target[i]?m(n.target,n.attributeName):(x(n.target,s,i,u),h&&h(n.target)))}});let e=[];if(s.indexOf("[")!==-1){const c=/\[([\w-]+)/g;let r;for(;(r=c.exec(s))!==null;)e.push(r[1])}else e.push(s);a.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:e.concat(y)})},p),window[i]=t,document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){t(document.body)}):t(document.body),t}const A={};function C(s,i){A[s]=i}function L(s){return A[s]||{ingress:i=>i,egress:i=>i}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=C,window.lnCore.getDataMapper=L),(function(){const s="data-ln-number",i="lnNumber";if(window[i]!==void 0)return;const u={},p=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,"value");function g(t){if(!u[t]){const a=new Intl.NumberFormat(t,{useGrouping:!0}),e=a.formatToParts(1234.5);let c="",r=".";for(let n=0;n<e.length;n++)e[n].type==="group"&&(c=e[n].value),e[n].type==="decimal"&&(r=e[n].value);u[t]={fmt:a,groupSep:c,decimalSep:r}}return u[t]}function y(t,a,e){if(e!==null){const c=parseInt(e,10),r=t+"|d"+c;return u[r]||(u[r]=new Intl.NumberFormat(t,{useGrouping:!0,minimumFractionDigits:0,maximumFractionDigits:c})),u[r].format(a)}return g(t).fmt.format(a)}function m(t){if(t.tagName!=="INPUT")return console.warn("[ln-number] Can only be applied to <input>, got:",t.tagName),this;if(t[i])return t[i];t[i]=this,this.dom=t;const a=document.createElement("input");a.type="hidden",a.name=t.name,t.removeAttribute("name"),t.type="text",t.setAttribute("inputmode","decimal"),t.insertAdjacentElement("afterend",a),this._hidden=a;const e=this;Object.defineProperty(a,"value",{get:function(){return p.get.call(a)},set:function(r){if(p.set.call(a,r),r!==""&&!isNaN(parseFloat(r))){const n=parseFloat(r);e._displayFormatted(n),v(e.dom,"ln-number:input",{value:n,formatted:e.dom.value}),e.dom.dispatchEvent(new Event("input",{bubbles:!0}))}else r===""&&(e.dom.value="",v(e.dom,"ln-number:input",{value:NaN,formatted:""}),e.dom.dispatchEvent(new Event("input",{bubbles:!0})))}}),O(t,p,{get:function(){return p.get.call(t)},set:function(r,n){if(e._isFormatting){n(r);return}if(r===""){n(""),e._setHiddenRaw(""),v(t,"ln-number:input",{value:NaN,formatted:""}),t.dispatchEvent(new Event("input",{bubbles:!0}));return}let o=typeof r=="number"?r:parseFloat(String(r).replace(/[^\d.-]/g,""));if(isNaN(o))n(String(r)),e._setHiddenRaw(""),v(t,"ln-number:input",{value:NaN,formatted:String(r)}),t.dispatchEvent(new Event("input",{bubbles:!0}));else{e._setHiddenRaw(o);const l=y(E(t),o,t.getAttribute("data-ln-number-decimals"));n(l),v(t,"ln-number:input",{value:o,formatted:l}),t.dispatchEvent(new Event("input",{bubbles:!0}))}}}),this._onInput=function(){e._handleInput()},t.addEventListener("input",this._onInput),this._onPaste=function(r){r.preventDefault();const n=(r.clipboardData||window.clipboardData).getData("text"),o=g(E(t)),l=o.decimalSep.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");let f=n.replace(new RegExp("[^0-9\\-"+l+".]","g"),"");o.groupSep&&(f=f.split(o.groupSep).join("")),o.decimalSep!=="."&&(f=f.replace(o.decimalSep,"."));const _=parseFloat(f);isNaN(_)?(t.value="",e._hidden.value=""):e.value=_},t.addEventListener("paste",this._onPaste);const c=t.value;if(c!==""){const r=parseFloat(c);isNaN(r)||(this._displayFormatted(r),p.set.call(a,String(r)),v(t,"ln-number:input",{value:r,formatted:t.value}),t.dispatchEvent(new Event("input",{bubbles:!0})))}return this}m.prototype._handleInput=function(){const t=this.dom,a=g(E(t)),e=t.value;if(e===""){this._hidden.value="",v(t,"ln-number:input",{value:NaN,formatted:""});return}if(e==="-"){this._hidden.value="";return}const c=t.selectionStart;let r=0;for(let d=0;d<c;d++)/[0-9]/.test(e[d])&&r++;let n=e;if(a.groupSep&&(n=n.split(a.groupSep).join("")),n=n.replace(a.decimalSep,"."),e.endsWith(a.decimalSep)||e.endsWith(".")){const d=n.replace(/\.$/,""),w=parseFloat(d);isNaN(w)||this._setHiddenRaw(w);return}const o=n.indexOf(".");if(o!==-1&&n.slice(o+1).endsWith("0")){const w=parseFloat(n);isNaN(w)||this._setHiddenRaw(w);return}const l=t.getAttribute("data-ln-number-decimals");if(l!==null&&o!==-1){const d=parseInt(l,10);n.slice(o+1).length>d&&(n=n.slice(0,o+1+d))}const f=parseFloat(n);if(isNaN(f))return;const _=t.getAttribute("data-ln-number-min"),F=t.getAttribute("data-ln-number-max");if(_!==null&&f<parseFloat(_)||F!==null&&f>parseFloat(F))return;let b;if(l!==null)b=y(E(t),f,l);else{const d=o!==-1?n.slice(o+1).length:0;if(d>0){const w=E(t)+"|u"+d;u[w]||(u[w]=new Intl.NumberFormat(E(t),{useGrouping:!0,minimumFractionDigits:d,maximumFractionDigits:d})),b=u[w].format(f)}else b=a.fmt.format(f)}t.value=b;let D=r,N=0;for(let d=0;d<b.length&&D>0;d++)N=d+1,/[0-9]/.test(b[d])&&D--;D>0&&(N=b.length),t.setSelectionRange(N,N),this._setHiddenRaw(f),v(t,"ln-number:input",{value:f,formatted:b})},m.prototype._setHiddenRaw=function(t){p.set.call(this._hidden,String(t))},m.prototype._displayFormatted=function(t){this._isFormatting=!0,this.dom.value=y(E(this.dom),t,this.dom.getAttribute("data-ln-number-decimals")),this._isFormatting=!1},Object.defineProperty(m.prototype,"value",{get:function(){const t=this._hidden.value;return t===""?NaN:parseFloat(t)},set:function(t){if(typeof t!="number"||isNaN(t)){this.dom.value="",this._setHiddenRaw(""),this.dom.dispatchEvent(new Event("input",{bubbles:!0}));return}this._displayFormatted(t),this._setHiddenRaw(t),v(this.dom,"ln-number:input",{value:t,formatted:this.dom.value}),this.dom.dispatchEvent(new Event("input",{bubbles:!0}))}}),Object.defineProperty(m.prototype,"formatted",{get:function(){return this.dom.value}}),m.prototype.destroy=function(){this.dom[i]&&(this.dom.removeEventListener("input",this._onInput),this.dom.removeEventListener("paste",this._onPaste),this.dom.name=this._hidden.name,this.dom.type="number",this.dom.removeAttribute("inputmode"),this._hidden.remove(),v(this.dom,"ln-number:destroyed",{target:this.dom}),delete this.dom[i])};function h(){new MutationObserver(function(){const t=document.querySelectorAll("["+s+"]");for(let a=0;a<t.length;a++){const e=t[a][i];e&&!isNaN(e.value)&&e._displayFormatted(e.value)}}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"]})}I(s,i,m,"ln-number"),h()})()})();
1
+ (function(){"use strict";if(typeof window<"u"){const e=console.warn;console.warn=function(...n){typeof n[0]=="string"&&(n[0].startsWith("[ln-")||n[0].startsWith("[lnCore"))&&!(document.documentElement.hasAttribute("data-ln-debug")||document.body&&document.body.hasAttribute("data-ln-debug"))||e.apply(console,n)}}const v={};function H(e,n){v[e]||(v[e]=document.querySelector('[data-ln-template="'+e+'"]'));const s=v[e];return s?s.content.cloneNode(!0):(console.warn("["+(n||"ln-core")+'] Template "'+e+'" not found'),null)}function S(e,n,s){e.dispatchEvent(new CustomEvent(n,{bubbles:!0,detail:s||{}}))}function R(e,n){if(!e||!n)return e;const s=e.querySelectorAll("[data-ln-field]");for(let l=0;l<s.length;l++){const a=s[l],d=a.getAttribute("data-ln-field");n[d]!=null&&(a.textContent=n[d])}const u=e.querySelectorAll("[data-ln-attr]");for(let l=0;l<u.length;l++){const a=u[l],d=a.getAttribute("data-ln-attr").split(",");for(let t=0;t<d.length;t++){const i=d[t].trim().split(":");if(i.length!==2)continue;const r=i[0].trim(),m=i[1].trim();n[m]!=null&&a.setAttribute(r,n[m])}}const p=e.querySelectorAll("[data-ln-show]");for(let l=0;l<p.length;l++){const a=p[l],d=a.getAttribute("data-ln-show");d in n&&a.classList.toggle("hidden",!n[d])}const c=e.querySelectorAll("[data-ln-class]");for(let l=0;l<c.length;l++){const a=c[l],d=a.getAttribute("data-ln-class").split(",");for(let t=0;t<d.length;t++){const i=d[t].trim().split(":");if(i.length!==2)continue;const r=i[0].trim(),m=i[1].trim();m in n&&a.classList.toggle(r,!!n[m])}}return e}function P(e,n){e.matches&&e.matches("[data-ln-form], [data-ln-fillable]")&&e.dispatchEvent(new CustomEvent("ln-fill",{detail:n??null,bubbles:!0}));const s=e.querySelectorAll("[data-ln-form], [data-ln-fillable]");for(let u=0;u<s.length;u++)s[u].dispatchEvent(new CustomEvent("ln-fill",{detail:n??null,bubbles:!0}));return e}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._fillBound||(window.lnCore._fillBound=!0,document.addEventListener("ln-fill",function(e){if(!(!e.target.matches||!e.target.matches("[data-ln-fillable]")))if(e.detail)R(e.target,e.detail);else{const n=e.target.querySelectorAll("[data-ln-field]");for(let s=0;s<n.length;s++)n[s].textContent=""}})));function L(e,n){if(!e||!n)return e;const s=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;s.nextNode();){const c=s.currentNode;c.textContent.indexOf("{{")!==-1&&(c.textContent=c.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(l,a){return n[a]!==void 0?n[a]:""}))}const u=function(c,l){return n[l]!==void 0?n[l]:""},p=Array.from(e.querySelectorAll("*"));e.nodeType===1&&p.push(e);for(let c=0;c<p.length;c++){const l=p[c],a=l.attributes;for(let d=0;d<a.length;d++){const t=a[d];t.value.indexOf("{{")!==-1&&l.setAttribute(t.name,t.value.replace(/\{\{\s*(\w+)\s*\}\}/g,u))}}return e}function M(e,n,s,u,p,c){const l={};for(let d=0;d<e.children.length;d++){const t=e.children[d],i=t.getAttribute("data-ln-key");i&&(l[i]=t)}const a=document.createDocumentFragment();for(let d=0;d<n.length;d++){const t=n[d],i=String(u(t));let r=l[i];if(r)p(r,t,d);else{const m=H(s,c);if(!m||(L(m,t),r=m.firstElementChild,!r))continue;r.setAttribute("data-ln-key",i),p(r,t,d)}a.appendChild(r)}e.textContent="",e.appendChild(a)}function O(e,n){if(!document.body){document.addEventListener("DOMContentLoaded",function(){O(e,n)}),console.warn("["+n+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}function D(e,n,s,u){if(e.nodeType!==1)return;const c=n.indexOf("[")!==-1||n.indexOf(".")!==-1||n.indexOf("#")!==-1?n:"["+n+"]",l=Array.from(e.querySelectorAll(c));e.matches&&e.matches(c)&&l.push(e);for(const a of l)a[s]||(a[s]=new u(a))}const F={mk:"mk-MK",de:"de-DE",fr:"fr-FR",es:"es-ES",it:"it-IT",nl:"nl-NL",pt:"pt-PT",ru:"ru-RU",bg:"bg-BG",hr:"hr-HR",sr:"sr-RS",sq:"sq-AL",el:"el-GR",en:"en-US"};function w(e){const n=e?e.closest("[lang]"):null,s=(n?n.getAttribute("lang")||n.lang:null)||(document.documentElement?document.documentElement.getAttribute("lang")||document.documentElement.lang:null)||navigator.language;if(!s)return"en-US";const u=s.trim().toLowerCase();return u.indexOf("-")===-1&&F[u]?F[u]:s}function V(e,n,{get:s,set:u}){Object.defineProperty(e,"value",{get:function(){return s?s.call(this):n.get.call(this)},set:function(p){u?u.call(this,p,c=>n.set.call(this,c)):n.set.call(this,p)},configurable:!0})}function W(e,n,s,u,p={}){const c=p.extraAttributes||[],l=p.onAttributeChange||null,a=p.onInit||null;function d(t){const i=t||document.body;D(i,e,n,s),a&&a(i)}return O(function(){const t=new MutationObserver(function(r){for(let m=0;m<r.length;m++){const o=r[m];if(o.type==="childList"){for(let f=0;f<o.addedNodes.length;f++){const h=o.addedNodes[f];h.nodeType===1&&(D(h,e,n,s),a&&a(h))}for(let f=0;f<o.removedNodes.length;f++){const h=o.removedNodes[f];if(h.nodeType===1){const b=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]",x=Array.from(h.querySelectorAll(b));h.matches&&h.matches(b)&&x.push(h);for(let E=0;E<x.length;E++){const y=x[E];if(!document.contains(y)){const A=y[n];A&&typeof A.destroy=="function"&&A.destroy()}}}}}else o.type==="attributes"&&(l&&o.target[n]?l(o.target,o.attributeName):(D(o.target,e,n,s),a&&a(o.target)))}});let i=[];if(e.indexOf("[")!==-1){const r=/\[([\w-]+)/g;let m;for(;(m=r.exec(e))!==null;)i.push(m[1])}else i.push(e);t.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:i.concat(c)})},u),window[n]=d,document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){d(document.body)}):d(document.body),d}const k={};function B(e,n){k[e]=n}function G(e){return k[e]||{ingress:n=>n,egress:n=>n}}const I={};function j(e,n){if(!e||typeof n!="object")return;const s=e.toLowerCase().split("-")[0];I[s]=n}function U(e){if(!e)return null;const n=e.toLowerCase().split("-")[0];return I[n]||null}j("mk",{monthsLong:["јануари","февруари","март","април","мај","јуни","јули","август","септември","октомври","ноември","декември"],monthsShort:["јан","фев","мар","апр","мај","јун","јул","авг","септ","окт","ноем","дек"],daysLong:["недела","понеделник","вторник","среда","четврток","петок","сабота"],daysShort:["нед","пон","вт","ср","чет","пет","саб"]}),typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=B,window.lnCore.getDataMapper=G,window.lnCore.registerLocaleFallback=j,window.lnCore.getLocaleFallback=U,window.lnCore.fillTemplate=L,window.lnCore.fill=R,window.lnCore.lnFill=P,window.lnCore.renderList=M);function q(e){return(e||"").replace(/^#/,"")}function T(e){const n=e===void 0?location.hash:e,s={},u=q(n);if(!u)return s;const p=u.split("&");for(let c=0;c<p.length;c++){const l=p[c];if(!l)continue;const a=l.indexOf(":"),d=a>-1?l.slice(0,a):l,t=a>-1?l.slice(a+1):"";if(d)try{s[d]=decodeURIComponent(t)}catch{s[d]=t}}return s}function K(e){if(!e)return null;const n=T();return e in n?n[e]:null}function $(e,n){if(!e)return;const s=T();n==null?delete s[e]:s[e]=String(n);const p=Object.keys(s).map(function(c){const l=s[c];return l===""?c:c+":"+encodeURIComponent(l)}).join("&");q(location.hash)!==p&&(location.hash=p)}function X(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=T,window.lnCore.hashGet=K,window.lnCore.hashSet=$,window.lnCore.hashLinkClick=X),(function(){const e="data-ln-number",n="lnNumber";if(window[n]!==void 0)return;const s={},u=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,"value");function p(t){if(!s[t]){const i=new Intl.NumberFormat(t,{useGrouping:!0}),r=i.formatToParts(1234.5);let m="",o=".";for(let f=0;f<r.length;f++)r[f].type==="group"&&(m=r[f].value),r[f].type==="decimal"&&(o=r[f].value);s[t]={fmt:i,groupSep:m,decimalSep:o}}return s[t]}function c(t,i,r){if(r!==null){const m=parseInt(r,10),o=t+"|d"+m;return s[o]||(s[o]=new Intl.NumberFormat(t,{useGrouping:!0,minimumFractionDigits:0,maximumFractionDigits:m})),s[o].format(i)}return p(t).fmt.format(i)}function l(t){if(t[n])return t[n];if(t[n]=this,this.dom=t,t.tagName!=="INPUT")return this.isTextElement=!0,this._initTextElement(),this;const i=document.createElement("input");i.type="hidden",i.name=t.name,t.removeAttribute("name"),t.hasAttribute("data-ln-fill-as")&&i.setAttribute("data-ln-fill-as",t.getAttribute("data-ln-fill-as")),t.type="text",t.setAttribute("inputmode","decimal"),t.insertAdjacentElement("afterend",i),this._hidden=i;const r=this;Object.defineProperty(i,"value",{get:function(){return u.get.call(i)},set:function(o){u.set.call(i,o),o!==""&&!isNaN(parseFloat(o))?r._setDisplayRaw(c(w(r.dom),parseFloat(o),r.dom.getAttribute("data-ln-number-decimals"))):r._setDisplayRaw(""),r.dom.dispatchEvent(new Event("input",{bubbles:!0}))}}),V(t,u,{get:function(){return u.get.call(t)},set:function(o){if(o===""){r._setDisplayRaw(""),r._setHiddenRaw(""),t.dispatchEvent(new Event("input",{bubbles:!0}));return}const f=typeof o=="number"?o:parseFloat(String(o).replace(/[^\d.-]/g,""));isNaN(f)?(r._setDisplayRaw(String(o)),r._setHiddenRaw("")):(r._setHiddenRaw(f),r._setDisplayRaw(c(w(t),f,t.getAttribute("data-ln-number-decimals")))),t.dispatchEvent(new Event("input",{bubbles:!0}))}}),this._onInput=function(){r._handleInput()},t.addEventListener("input",this._onInput),this._onPaste=function(o){o.preventDefault();const f=(o.clipboardData||window.clipboardData).getData("text"),h=p(w(t)),C=h.decimalSep.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");let b=f.replace(new RegExp("[^0-9\\-"+C+".]","g"),"");h.groupSep&&(b=b.split(h.groupSep).join("")),h.decimalSep!=="."&&(b=b.replace(h.decimalSep,"."));const x=parseFloat(b);r.value=isNaN(x)?NaN:x},t.addEventListener("paste",this._onPaste);const m=t.value;if(m!==""){const o=parseFloat(m);isNaN(o)||(this._setHiddenRaw(o),this._setDisplayRaw(c(w(t),o,t.getAttribute("data-ln-number-decimals"))),t.dispatchEvent(new Event("input",{bubbles:!0})))}return this}function a(t){if(typeof t=="number")return isNaN(t)?null:t;if(!t||typeof t!="string")return null;let i=t.trim();if(i==="")return null;i=i.replace(/[\s\u00A0$€£]/g,""),i.indexOf(",")!==-1&&i.indexOf(".")!==-1?i.indexOf(".")<i.indexOf(",")?i=i.replace(/\./g,"").replace(",","."):i=i.replace(/,/g,""):i.indexOf(",")!==-1&&(i=i.replace(",",".")),i=i.replace(/[^\d.-]/g,"");const r=parseFloat(i);return isNaN(r)?null:r}l.prototype._initTextElement=function(){const t=this.dom;let i=t.getAttribute("data-ln-value"),r=t.getAttribute("data-ln-number"),m=null;i!==null&&i!==""?m=i:r!==null&&r!==""&&r!=="true"?m=r:m=t.textContent.trim();const o=a(m);o!==null?(this._rawValue=o,t.hasAttribute("data-ln-value")||t.setAttribute("data-ln-value",String(o)),this._formatTextContent()):this._rawValue=null},l.prototype._formatTextContent=function(){if(this._rawValue!==null&&!isNaN(this._rawValue)){const t=this.dom.getAttribute("data-ln-number-decimals");this.dom.textContent=c(w(this.dom),this._rawValue,t)}},l.prototype._handleInput=function(){const t=this.dom,i=p(w(t)),r=u.get.call(t);if(r===""){this._setHiddenRaw(""),S(t,"ln-number:input",{value:NaN,formatted:""});return}if(r==="-"){this._setHiddenRaw("");return}const m=t.selectionStart;let o=0;for(let g=0;g<m;g++)/[0-9]/.test(r[g])&&o++;let f=r;if(i.groupSep&&(f=f.split(i.groupSep).join("")),f=f.replace(i.decimalSep,"."),r.endsWith(i.decimalSep)||r.endsWith(".")){const g=f.replace(/\.$/,""),_=parseFloat(g);isNaN(_)||this._setHiddenRaw(_);return}const h=f.indexOf(".");if(h!==-1&&f.slice(h+1).endsWith("0")){const _=parseFloat(f);isNaN(_)||this._setHiddenRaw(_);return}const C=t.getAttribute("data-ln-number-decimals");if(C!==null&&h!==-1){const g=parseInt(C,10);f.slice(h+1).length>g&&(f=f.slice(0,h+1+g))}const b=parseFloat(f);if(isNaN(b))return;const x=t.getAttribute("data-ln-number-min"),E=t.getAttribute("data-ln-number-max");if(x!==null&&b<parseFloat(x)||E!==null&&b>parseFloat(E))return;let y;if(C!==null)y=c(w(t),b,C);else{const g=h!==-1?f.slice(h+1).length:0;if(g>0){const _=w(t)+"|u"+g;s[_]||(s[_]=new Intl.NumberFormat(w(t),{useGrouping:!0,minimumFractionDigits:g,maximumFractionDigits:g})),y=s[_].format(b)}else y=i.fmt.format(b)}this._setDisplayRaw(y);let A=o,N=0;for(let g=0;g<y.length&&A>0;g++)N=g+1,/[0-9]/.test(y[g])&&A--;A>0&&(N=y.length),t.setSelectionRange(N,N),this._setHiddenRaw(b),S(t,"ln-number:input",{value:b,formatted:y})},l.prototype._setHiddenRaw=function(t){this._hidden&&u.set.call(this._hidden,String(t))},l.prototype._setDisplayRaw=function(t){this.isTextElement?this.dom.textContent=String(t):u.set.call(this.dom,String(t))},l.prototype._displayFormatted=function(t){this.isTextElement?this._formatTextContent():this._setDisplayRaw(c(w(this.dom),t,this.dom.getAttribute("data-ln-number-decimals")))},Object.defineProperty(l.prototype,"value",{get:function(){if(this.isTextElement)return this._rawValue;const t=u.get.call(this._hidden);return t===""?NaN:parseFloat(t)},set:function(t){if(this.isTextElement){typeof t!="number"||isNaN(t)?(this._rawValue=null,this.dom.textContent=""):(this._rawValue=t,this.dom.setAttribute("data-ln-value",String(t)),this._formatTextContent());return}if(typeof t!="number"||isNaN(t)){this._setDisplayRaw(""),this._setHiddenRaw(""),this.dom.dispatchEvent(new Event("input",{bubbles:!0}));return}this._setHiddenRaw(t),this._setDisplayRaw(c(w(this.dom),t,this.dom.getAttribute("data-ln-number-decimals"))),this.dom.dispatchEvent(new Event("input",{bubbles:!0}))}}),Object.defineProperty(l.prototype,"formatted",{get:function(){return this.isTextElement?this.dom.textContent:u.get.call(this.dom)}}),l.prototype.destroy=function(){this.dom[n]&&(this.isTextElement||(this.dom.removeEventListener("input",this._onInput),this.dom.removeEventListener("paste",this._onPaste),this._hidden&&(this.dom.name=this._hidden.name,this._hidden.remove()),this.dom.type="number",this.dom.removeAttribute("inputmode")),S(this.dom,"ln-number:destroyed",{target:this.dom}),delete this.dom[n])};function d(){new MutationObserver(function(){const t=document.querySelectorAll("["+e+"]");for(let i=0;i<t.length;i++){const r=t[i][n];r&&(r.isTextElement?r._formatTextContent():isNaN(r.value)||r._displayFormatted(r.value))}}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})}W(e,n,l,"ln-number",{extraAttributes:["data-ln-value","data-ln-number-decimals","data-ln-number-min","data-ln-number-max","lang"],onAttributeChange:function(t){const i=t[n];i&&(i.isTextElement?i._initTextElement():isNaN(i.value)||i._displayFormatted(i.value))}}),d()})()})();