@livenetworks/ashlar 2.0.7 → 2.0.9

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 (213) hide show
  1. package/LICENSE +641 -641
  2. package/components/COMPONENTS.md +1116 -1116
  3. package/components/index.js +81 -81
  4. package/components/ln-accordion/ln-accordion-dev.scss +8 -8
  5. package/components/ln-accordion/ln-accordion.js +1 -1
  6. package/components/ln-accordion/ln-accordion.schema.json +30 -30
  7. package/components/ln-ajax/ln-ajax-dev.scss +8 -8
  8. package/components/ln-ajax/ln-ajax.js +1 -1
  9. package/components/ln-ajax/ln-ajax.schema.json +30 -30
  10. package/components/ln-api-connector/ln-api-connector.js +1 -1
  11. package/components/ln-api-connector/ln-api-connector.schema.json +9 -0
  12. package/components/ln-api-connector/src/ln-api-connector.js +2 -0
  13. package/components/ln-api-queue/ln-api-queue.js +1 -1
  14. package/components/ln-api-queue/ln-api-queue.schema.json +34 -34
  15. package/components/ln-api-queue/src/ln-api-queue.js +3 -13
  16. package/components/ln-autoresize/ln-autoresize-dev.scss +8 -8
  17. package/components/ln-autoresize/ln-autoresize.js +1 -1
  18. package/components/ln-autoresize/ln-autoresize.schema.json +22 -22
  19. package/components/ln-autosave/ln-autosave-dev.scss +13 -13
  20. package/components/ln-autosave/ln-autosave.js +1 -1
  21. package/components/ln-autosave/ln-autosave.schema.json +48 -48
  22. package/components/ln-autosave/src/autosave-model.js +34 -34
  23. package/components/ln-chart/ln-chart.js +1 -1
  24. package/components/ln-chart/ln-chart.schema.json +146 -146
  25. package/components/ln-chart/src/chart-model.js +154 -154
  26. package/components/ln-circular-progress/ln-circular-progress.js +1 -1
  27. package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -42
  28. package/components/ln-confirm/ln-confirm-dev.scss +24 -24
  29. package/components/ln-confirm/ln-confirm.js +1 -1
  30. package/components/ln-confirm/ln-confirm.schema.json +66 -66
  31. package/components/ln-confirm/src/ln-confirm.js +178 -178
  32. package/components/ln-core/attrs.js +13 -0
  33. package/components/ln-core/crypto.js +286 -265
  34. package/components/ln-core/dom.js +115 -115
  35. package/components/ln-core/form.js +146 -136
  36. package/components/ln-core/helpers.js +33 -33
  37. package/components/ln-core/http.js +48 -48
  38. package/components/ln-core/index.js +1 -1
  39. package/components/ln-core/lifecycle.js +5 -0
  40. package/components/ln-core/ln-core.schema.json +3 -2
  41. package/components/ln-core/locale.js +55 -55
  42. package/components/ln-core/matching.js +78 -78
  43. package/components/ln-core/number.js +125 -125
  44. package/components/ln-core/template.js +206 -206
  45. package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -1
  46. package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -50
  47. package/components/ln-couchdb-connector/src/ln-couchdb-connector.js +1 -0
  48. package/components/ln-data-coordinator/README.md +9 -38
  49. package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -8
  50. package/components/ln-data-coordinator/ln-data-coordinator.js +1 -1
  51. package/components/ln-data-coordinator/ln-data-coordinator.schema.json +16 -16
  52. package/components/ln-data-coordinator/src/data-read-policy.js +0 -7
  53. package/components/ln-data-coordinator/src/ln-data-coordinator.js +432 -1308
  54. package/components/ln-data-store/ln-data-store-dev.scss +13 -13
  55. package/components/ln-data-store/ln-data-store.js +1 -1
  56. package/components/ln-data-store/ln-data-store.schema.json +84 -84
  57. package/components/ln-data-store/src/data-store-model.js +184 -184
  58. package/components/ln-data-store/src/ln-data-store.js +1 -1
  59. package/components/ln-date/ln-date.js +1 -1
  60. package/components/ln-date/ln-date.schema.json +92 -92
  61. package/components/ln-date/src/date-model.js +186 -186
  62. package/components/ln-debug/ln-debug.js +1 -1
  63. package/components/ln-debug/ln-debug.schema.json +28 -28
  64. package/components/ln-debug/src/console-sink.js +19 -19
  65. package/components/ln-debug/src/gate.js +95 -95
  66. package/components/ln-debug/src/generated-attributes.js +4 -2
  67. package/components/ln-dropdown/ln-dropdown-dev.scss +13 -13
  68. package/components/ln-dropdown/ln-dropdown.js +1 -1
  69. package/components/ln-dropdown/ln-dropdown.schema.json +90 -90
  70. package/components/ln-editor/ln-editor.js +1 -1
  71. package/components/ln-editor/ln-editor.schema.json +58 -58
  72. package/components/ln-external-links/ln-external-links.js +1 -1
  73. package/components/ln-external-links/ln-external-links.schema.json +18 -18
  74. package/components/ln-fill/ln-fill.js +1 -1
  75. package/components/ln-fill/ln-fill.schema.json +23 -23
  76. package/components/ln-filter/ln-filter.js +1 -1
  77. package/components/ln-filter/ln-filter.schema.json +153 -153
  78. package/components/ln-filter/src/filter-model.js +185 -185
  79. package/components/ln-form/ln-form-dev.scss +8 -8
  80. package/components/ln-form/ln-form.js +1 -1
  81. package/components/ln-form/ln-form.schema.json +6 -0
  82. package/components/ln-form/src/ln-form.js +21 -0
  83. package/components/ln-http/ln-http.js +1 -1
  84. package/components/ln-http/ln-http.schema.json +6 -6
  85. package/components/ln-icon/ln-icon.schema.json +6 -6
  86. package/components/ln-include/ln-include.js +1 -1
  87. package/components/ln-include/ln-include.schema.json +23 -23
  88. package/components/ln-key/ln-key-dev.scss +18 -18
  89. package/components/ln-key/ln-key.js +1 -1
  90. package/components/ln-key/ln-key.schema.json +56 -56
  91. package/components/ln-key/src/ln-key.js +10 -10
  92. package/components/ln-link/ln-link-dev.scss +8 -8
  93. package/components/ln-link/ln-link.js +1 -1
  94. package/components/ln-link/ln-link.schema.json +22 -22
  95. package/components/ln-list/README.md +152 -152
  96. package/components/ln-list/ln-list-dev.scss +8 -8
  97. package/components/ln-list/ln-list.js +1 -1
  98. package/components/ln-list/ln-list.schema.json +166 -166
  99. package/components/ln-modal/ln-modal.js +1 -1
  100. package/components/ln-modal/ln-modal.schema.json +65 -65
  101. package/components/ln-nav/ln-nav.js +1 -1
  102. package/components/ln-nav/ln-nav.schema.json +25 -25
  103. package/components/ln-number/ln-number.js +1 -1
  104. package/components/ln-number/ln-number.schema.json +63 -63
  105. package/components/ln-number/src/ln-number.js +26 -0
  106. package/components/ln-number/src/number-model.js +31 -31
  107. package/components/ln-obfuscator/README.md +60 -60
  108. package/components/ln-obfuscator/ln-obfuscator.js +1 -1
  109. package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -49
  110. package/components/ln-obfuscator/src/ln-obfuscator.js +159 -159
  111. package/components/ln-obfuscator/src/obfuscator-model.js +250 -250
  112. package/components/ln-options/ln-options.js +1 -1
  113. package/components/ln-options/ln-options.schema.json +6 -0
  114. package/components/ln-options/src/ln-options.js +4 -4
  115. package/components/ln-persist/README.md +151 -151
  116. package/components/ln-persist/ln-persist.js +1 -1
  117. package/components/ln-persist/ln-persist.schema.json +28 -28
  118. package/components/ln-persist/src/ln-persist.js +83 -83
  119. package/components/ln-picklist/README.md +230 -230
  120. package/components/ln-picklist/ln-picklist-dev.scss +22 -22
  121. package/components/ln-picklist/ln-picklist.js +1 -1
  122. package/components/ln-picklist/ln-picklist.schema.json +49 -49
  123. package/components/ln-picklist/src/ln-picklist.js +228 -228
  124. package/components/ln-popover/ln-popover-dev.scss +13 -13
  125. package/components/ln-popover/ln-popover.js +1 -1
  126. package/components/ln-popover/ln-popover.schema.json +82 -82
  127. package/components/ln-progress/ln-progress-dev.scss +8 -8
  128. package/components/ln-progress/ln-progress.js +1 -1
  129. package/components/ln-progress/ln-progress.schema.json +40 -40
  130. package/components/ln-router/ln-router.js +1 -1
  131. package/components/ln-router/ln-router.schema.json +76 -76
  132. package/components/ln-scroll/README.md +45 -45
  133. package/components/ln-scroll/ln-scroll.js +1 -1
  134. package/components/ln-scroll/ln-scroll.schema.json +85 -85
  135. package/components/ln-scroll/src/ln-scroll.js +150 -150
  136. package/components/ln-search/README.md +188 -188
  137. package/components/ln-search/ln-search.js +1 -1
  138. package/components/ln-search/ln-search.schema.json +103 -103
  139. package/components/ln-slug/ln-slug.js +1 -1
  140. package/components/ln-slug/ln-slug.schema.json +23 -23
  141. package/components/ln-slug/src/ln-slug.js +25 -1
  142. package/components/ln-sort/ln-sort.js +1 -1
  143. package/components/ln-sort/ln-sort.schema.json +104 -104
  144. package/components/ln-sort/src/ln-sort.js +321 -321
  145. package/components/ln-sort/src/sort-model.js +117 -117
  146. package/components/ln-sortable/ln-sortable-dev.scss +13 -13
  147. package/components/ln-sortable/ln-sortable.js +1 -1
  148. package/components/ln-sortable/ln-sortable.schema.json +36 -36
  149. package/components/ln-stat/ln-stat-dev.scss +8 -8
  150. package/components/ln-stat/ln-stat.js +1 -1
  151. package/components/ln-stat/ln-stat.schema.json +32 -32
  152. package/components/ln-stat/src/stat-model.js +28 -28
  153. package/components/ln-table/ln-table.js +1 -1
  154. package/components/ln-table/ln-table.schema.json +211 -211
  155. package/components/ln-table/src/table-model.js +98 -98
  156. package/components/ln-table-coordinator/ln-table-coordinator.js +1 -1
  157. package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -93
  158. package/components/ln-tabs/ln-tabs.js +1 -1
  159. package/components/ln-tabs/ln-tabs.schema.json +75 -75
  160. package/components/ln-tabs/src/ln-tabs.js +208 -208
  161. package/components/ln-tabs/src/tabs-model.js +82 -82
  162. package/components/ln-time/ln-time-dev.scss +13 -13
  163. package/components/ln-time/ln-time.js +1 -1
  164. package/components/ln-time/ln-time.schema.json +38 -38
  165. package/components/ln-toast/ln-toast.js +1 -1
  166. package/components/ln-toast/ln-toast.schema.json +65 -65
  167. package/components/ln-toggle/ln-toggle.js +1 -1
  168. package/components/ln-toggle/ln-toggle.schema.json +52 -52
  169. package/components/ln-tooltip/ln-tooltip.js +1 -1
  170. package/components/ln-tooltip/ln-tooltip.schema.json +86 -86
  171. package/components/ln-translations/ln-translations.js +1 -1
  172. package/components/ln-translations/ln-translations.schema.json +120 -120
  173. package/components/ln-ui-coordinator/README.md +62 -62
  174. package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -1
  175. package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +28 -28
  176. package/components/ln-ui-coordinator/src/ln-ui-coordinator.js +140 -140
  177. package/components/ln-upload/ln-upload.js +1 -1
  178. package/components/ln-upload/ln-upload.schema.json +193 -193
  179. package/components/ln-upload/src/upload-model.js +78 -78
  180. package/components/ln-validate/ln-validate.js +1 -1
  181. package/components/ln-validate/ln-validate.schema.json +50 -50
  182. package/components/ln-validate/src/validate-model.js +64 -64
  183. package/components/ln-websocket-connector/README.md +166 -166
  184. package/components/ln-websocket-connector/ln-websocket-connector.js +1 -1
  185. package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -29
  186. package/components/ln-websocket-connector/src/ln-websocket-connector.js +283 -279
  187. package/ln-ashlar.html-data.json +15 -8
  188. package/package.json +85 -84
  189. package/theme/brand.css +73 -73
  190. package/theme/components/_confirm.scss +6 -6
  191. package/theme/components/_picklist.scss +3 -3
  192. package/theme/config/_palette.scss +274 -274
  193. package/theme/config/_theme.scss +292 -292
  194. package/theme/config/_tokens.scss +285 -285
  195. package/theme/config/mixins/_index.scss +62 -62
  196. package/theme/config/mixins/_picklist.scss +66 -66
  197. package/theme/ln-ashlar-dev.scss +66 -66
  198. package/theme/ln-ashlar-landing.scss +56 -56
  199. package/theme/ln-ashlar-theme.scss +63 -63
  200. package/theme/presentation/_bento.scss +151 -151
  201. package/theme/presentation/_cta.scss +111 -111
  202. package/theme/presentation/_effects.scss +216 -216
  203. package/theme/presentation/_features.scss +108 -108
  204. package/theme/presentation/_footer.scss +129 -129
  205. package/theme/presentation/_hero.scss +158 -158
  206. package/theme/presentation/_layout.scss +170 -170
  207. package/theme/presentation/_navbar.scss +140 -140
  208. package/theme/presentation/_pricing.scss +262 -262
  209. package/theme/presentation/_stats.scss +87 -87
  210. package/theme/presentation/_testimonials.scss +124 -124
  211. package/theme/presentation/_tokens.scss +39 -39
  212. package/theme/presentation/_typography.scss +163 -163
  213. package/web-types.json +26 -12
@@ -1,48 +1,48 @@
1
- /**
2
- * Build a URL from segments, stripping duplicate slashes.
3
- */
4
- export function buildUrl(...segments) {
5
- return segments
6
- .filter(x => x !== undefined && x !== null && x !== '')
7
- .map((part, index) => {
8
- if (index === 0) return part.replace(/\/+$/, '');
9
- return part.replace(/^\/+/, '').replace(/\/+$/, '');
10
- })
11
- .filter(Boolean)
12
- .join('/');
13
- }
14
-
15
- /**
16
- * Compile standard JSON request headers.
17
- */
18
- export function getHeaders(customHeaders, auth) {
19
- return Object.assign({
20
- 'Content-Type': 'application/json',
21
- 'Accept': 'application/json'
22
- }, customHeaders, auth ? { 'Authorization': auth } : null);
23
- }
24
-
25
- /**
26
- * Safely parse headers from JSON string.
27
- */
28
- export function parseHeaders(str, componentName = 'ln-core') {
29
- try { return str ? JSON.parse(str) : {}; }
30
- catch (e) { return console.error(`[${componentName}] Invalid headers JSON:`, e), {}; }
31
- }
32
-
33
- // ─── Domain Data Mapper Registry ───────────────────────────
34
- const _dataMappers = {};
35
-
36
- export function registerDataMapper(name, mapper) {
37
- _dataMappers[name] = mapper;
38
- }
39
-
40
- export function getDataMapper(name) {
41
- return _dataMappers[name] || { ingress: r => r, egress: r => r };
42
- }
43
-
44
- if (typeof window !== 'undefined') {
45
- window.lnCore = window.lnCore || {};
46
- window.lnCore.registerDataMapper = registerDataMapper;
47
- window.lnCore.getDataMapper = getDataMapper;
48
- }
1
+ /**
2
+ * Build a URL from segments, stripping duplicate slashes.
3
+ */
4
+ export function buildUrl(...segments) {
5
+ return segments
6
+ .filter(x => x !== undefined && x !== null && x !== '')
7
+ .map((part, index) => {
8
+ if (index === 0) return part.replace(/\/+$/, '');
9
+ return part.replace(/^\/+/, '').replace(/\/+$/, '');
10
+ })
11
+ .filter(Boolean)
12
+ .join('/');
13
+ }
14
+
15
+ /**
16
+ * Compile standard JSON request headers.
17
+ */
18
+ export function getHeaders(customHeaders, auth) {
19
+ return Object.assign({
20
+ 'Content-Type': 'application/json',
21
+ 'Accept': 'application/json'
22
+ }, customHeaders, auth ? { 'Authorization': auth } : null);
23
+ }
24
+
25
+ /**
26
+ * Safely parse headers from JSON string.
27
+ */
28
+ export function parseHeaders(str, componentName = 'ln-core') {
29
+ try { return str ? JSON.parse(str) : {}; }
30
+ catch (e) { return console.error(`[${componentName}] Invalid headers JSON:`, e), {}; }
31
+ }
32
+
33
+ // ─── Domain Data Mapper Registry ───────────────────────────
34
+ const _dataMappers = {};
35
+
36
+ export function registerDataMapper(name, mapper) {
37
+ _dataMappers[name] = mapper;
38
+ }
39
+
40
+ export function getDataMapper(name) {
41
+ return _dataMappers[name] || { ingress: r => r, egress: r => r };
42
+ }
43
+
44
+ if (typeof window !== 'undefined') {
45
+ window.lnCore = window.lnCore || {};
46
+ window.lnCore.registerDataMapper = registerDataMapper;
47
+ window.lnCore.getDataMapper = getDataMapper;
48
+ }
@@ -3,7 +3,7 @@ export { reactiveState, deepReactive, createBatcher } from './reactive.js';
3
3
  export { createWindowCache } from './window-cache.js';
4
4
  export { hashParse, hashGet, hashSet, hashLinkClick, resolveHashNamespace, hashSortEncode, hashSortDecode, hashFilterEncode, hashFilterDecode } from './hash.js';
5
5
  export { computePlacement, measureHidden } from './positioning.js';
6
- export { setCryptoKey, getCryptoKey, clearCryptoKey, hasCryptoKey, deriveCryptoKey, encryptData, decryptData, uint8ToBase64, base64ToUint8 } from './crypto.js';
6
+ export { setCryptoKey, getCryptoKey, clearCryptoKey, hasCryptoKey, deriveCryptoKey, encryptData, decryptData, uint8ToBase64, base64ToUint8, uuid } from './crypto.js';
7
7
  export { calculateProgress } from './progress.js';
8
8
  export { parseDateInput, formatDateToISO } from './date.js';
9
9
  export { getSeparators, cleanNumericString, parseNumber, formatNumber } from './number.js';
@@ -385,6 +385,11 @@ export function registerComponent(selector, attribute, ComponentFn, componentTag
385
385
 
386
386
  window[attribute] = constructor;
387
387
 
388
+ if (typeof window !== 'undefined') {
389
+ window.lnCore = window.lnCore || {};
390
+ window.lnCore.registerComponent = registerComponent;
391
+ }
392
+
388
393
  function boot() {
389
394
  if (pendingCount() > 0) {
390
395
  queueBoot(function () { constructor(document.body); });
@@ -100,9 +100,10 @@
100
100
  ]
101
101
  },
102
102
  "data-ln-value": {
103
- "direction": "author",
103
+ "direction": "both",
104
104
  "sources": [
105
- "dom.js"
105
+ "dom.js",
106
+ "form.js"
106
107
  ]
107
108
  }
108
109
  }
@@ -1,55 +1,55 @@
1
- import { dispatch } from './events.js';
2
- import { guardBody } from './dom.js';
3
-
4
- // ─── Locale Detection ─────────────────────────────────────
5
-
6
- export function getLocale(el) {
7
- const langEl = el ? el.closest('[lang]') : null;
8
- const raw = (langEl ? (langEl.getAttribute('lang') || langEl.lang) : null)
9
- || (typeof document !== 'undefined' && document.documentElement ? (document.documentElement.getAttribute('lang') || document.documentElement.lang) : null)
10
- || (typeof navigator !== 'undefined' ? navigator.language : null);
11
- return raw ? raw.trim() : 'en-US';
12
- }
13
-
14
- // ─── Shared Locale-Change Broadcaster ─────────────────────
15
-
16
- export function ensureLocaleObserver() {
17
- if (typeof window === 'undefined') return;
18
- window.lnCore = window.lnCore || {};
19
- if (window.lnCore._localeObserverBound) return;
20
- window.lnCore._localeObserverBound = true;
21
-
22
- guardBody(function () {
23
- const observer = new MutationObserver(function () {
24
- dispatch(document, 'ln-core:locale-change', {});
25
- });
26
- observer.observe(document.documentElement, {
27
- attributes: true,
28
- attributeFilter: ['lang'],
29
- subtree: true
30
- });
31
- }, 'ln-core');
32
- }
33
-
34
- // ─── Locale Fallback Registry ───────────────────────────────
35
- const _localeFallbacks = {};
36
-
37
- export function registerLocaleFallback(langPrefix, dictionary) {
38
- if (!langPrefix || typeof dictionary !== 'object') return;
39
- const prefix = langPrefix.toLowerCase().split('-')[0];
40
- _localeFallbacks[prefix] = dictionary;
41
- }
42
-
43
- export function getLocaleFallback(lang) {
44
- if (!lang) return null;
45
- const prefix = lang.toLowerCase().split('-')[0];
46
- return _localeFallbacks[prefix] || null;
47
- }
48
-
49
-
50
- if (typeof window !== 'undefined') {
51
- window.lnCore = window.lnCore || {};
52
- window.lnCore.registerLocaleFallback = registerLocaleFallback;
53
- window.lnCore.getLocaleFallback = getLocaleFallback;
54
- window.lnCore.ensureLocaleObserver = ensureLocaleObserver;
55
- }
1
+ import { dispatch } from './events.js';
2
+ import { guardBody } from './dom.js';
3
+
4
+ // ─── Locale Detection ─────────────────────────────────────
5
+
6
+ export function getLocale(el) {
7
+ const langEl = el ? el.closest('[lang]') : null;
8
+ const raw = (langEl ? (langEl.getAttribute('lang') || langEl.lang) : null)
9
+ || (typeof document !== 'undefined' && document.documentElement ? (document.documentElement.getAttribute('lang') || document.documentElement.lang) : null)
10
+ || (typeof navigator !== 'undefined' ? navigator.language : null);
11
+ return raw ? raw.trim() : 'en-US';
12
+ }
13
+
14
+ // ─── Shared Locale-Change Broadcaster ─────────────────────
15
+
16
+ export function ensureLocaleObserver() {
17
+ if (typeof window === 'undefined') return;
18
+ window.lnCore = window.lnCore || {};
19
+ if (window.lnCore._localeObserverBound) return;
20
+ window.lnCore._localeObserverBound = true;
21
+
22
+ guardBody(function () {
23
+ const observer = new MutationObserver(function () {
24
+ dispatch(document, 'ln-core:locale-change', {});
25
+ });
26
+ observer.observe(document.documentElement, {
27
+ attributes: true,
28
+ attributeFilter: ['lang'],
29
+ subtree: true
30
+ });
31
+ }, 'ln-core');
32
+ }
33
+
34
+ // ─── Locale Fallback Registry ───────────────────────────────
35
+ const _localeFallbacks = {};
36
+
37
+ export function registerLocaleFallback(langPrefix, dictionary) {
38
+ if (!langPrefix || typeof dictionary !== 'object') return;
39
+ const prefix = langPrefix.toLowerCase().split('-')[0];
40
+ _localeFallbacks[prefix] = dictionary;
41
+ }
42
+
43
+ export function getLocaleFallback(lang) {
44
+ if (!lang) return null;
45
+ const prefix = lang.toLowerCase().split('-')[0];
46
+ return _localeFallbacks[prefix] || null;
47
+ }
48
+
49
+
50
+ if (typeof window !== 'undefined') {
51
+ window.lnCore = window.lnCore || {};
52
+ window.lnCore.registerLocaleFallback = registerLocaleFallback;
53
+ window.lnCore.getLocaleFallback = getLocaleFallback;
54
+ window.lnCore.ensureLocaleObserver = ensureLocaleObserver;
55
+ }
@@ -1,78 +1,78 @@
1
- /**
2
- * Normalizes a search query string for matching.
3
- * @param {unknown} value
4
- * @returns {string}
5
- */
6
- export function normalizeSearchTerm(value) {
7
- return String(value || '').trim().toLowerCase();
8
- }
9
-
10
- /**
11
- * Splits query string into individual tokens for AND-based substring matching.
12
- * @param {unknown} term
13
- * @returns {string[]}
14
- */
15
- export function tokenizeSearchQuery(term) {
16
- const normalized = normalizeSearchTerm(term);
17
- if (!normalized) return [];
18
- return normalized.split(/\s+/).filter(Boolean);
19
- }
20
-
21
- /**
22
- * Parses comma-separated field selectors forwarded to consumers.
23
- * @param {unknown} rawFields
24
- * @returns {string[]|null}
25
- */
26
- export function parseSearchFields(rawFields) {
27
- if (rawFields === null || rawFields === undefined) return null;
28
- const fields = String(rawFields)
29
- .split(',')
30
- .map(f => f.trim())
31
- .filter(Boolean);
32
- return fields.length ? fields : null;
33
- }
34
-
35
- /**
36
- * Checks if text contains all tokens (AND-matching, order-independent).
37
- * @param {string} text
38
- * @param {string[]} tokens
39
- * @returns {boolean}
40
- */
41
- export function matchesSearchTokens(text, tokens) {
42
- if (!tokens || tokens.length === 0) return true;
43
- if (!text) return false;
44
- const lower = String(text).toLowerCase();
45
- for (let i = 0; i < tokens.length; i++) {
46
- if (lower.indexOf(tokens[i]) === -1) return false;
47
- }
48
- return true;
49
- }
50
-
51
- /**
52
- * Collapses collected text parts into normalized search index text.
53
- * @param {string[]} parts
54
- * @returns {string}
55
- */
56
- export function collapseSearchParts(parts) {
57
- if (!parts || parts.length === 0) return '';
58
- return parts.join(' ').replace(/\s+/g, ' ').trim().toLowerCase();
59
- }
60
-
61
- /**
62
- * Evaluates whether a cell or attribute value matches any of the filter's values (OR logic).
63
- * @param {unknown} value
64
- * @param {string[]} filterValues
65
- * @returns {boolean}
66
- */
67
- export function matchesFilterValues(value, filterValues) {
68
- if (!filterValues || filterValues.length === 0) return true;
69
- if (value === null || value === undefined) return false;
70
-
71
- const lowerVal = String(value).trim().toLowerCase();
72
- for (let i = 0; i < filterValues.length; i++) {
73
- if (String(filterValues[i]).trim().toLowerCase() === lowerVal) {
74
- return true;
75
- }
76
- }
77
- return false;
78
- }
1
+ /**
2
+ * Normalizes a search query string for matching.
3
+ * @param {unknown} value
4
+ * @returns {string}
5
+ */
6
+ export function normalizeSearchTerm(value) {
7
+ return String(value || '').trim().toLowerCase();
8
+ }
9
+
10
+ /**
11
+ * Splits query string into individual tokens for AND-based substring matching.
12
+ * @param {unknown} term
13
+ * @returns {string[]}
14
+ */
15
+ export function tokenizeSearchQuery(term) {
16
+ const normalized = normalizeSearchTerm(term);
17
+ if (!normalized) return [];
18
+ return normalized.split(/\s+/).filter(Boolean);
19
+ }
20
+
21
+ /**
22
+ * Parses comma-separated field selectors forwarded to consumers.
23
+ * @param {unknown} rawFields
24
+ * @returns {string[]|null}
25
+ */
26
+ export function parseSearchFields(rawFields) {
27
+ if (rawFields === null || rawFields === undefined) return null;
28
+ const fields = String(rawFields)
29
+ .split(',')
30
+ .map(f => f.trim())
31
+ .filter(Boolean);
32
+ return fields.length ? fields : null;
33
+ }
34
+
35
+ /**
36
+ * Checks if text contains all tokens (AND-matching, order-independent).
37
+ * @param {string} text
38
+ * @param {string[]} tokens
39
+ * @returns {boolean}
40
+ */
41
+ export function matchesSearchTokens(text, tokens) {
42
+ if (!tokens || tokens.length === 0) return true;
43
+ if (!text) return false;
44
+ const lower = String(text).toLowerCase();
45
+ for (let i = 0; i < tokens.length; i++) {
46
+ if (lower.indexOf(tokens[i]) === -1) return false;
47
+ }
48
+ return true;
49
+ }
50
+
51
+ /**
52
+ * Collapses collected text parts into normalized search index text.
53
+ * @param {string[]} parts
54
+ * @returns {string}
55
+ */
56
+ export function collapseSearchParts(parts) {
57
+ if (!parts || parts.length === 0) return '';
58
+ return parts.join(' ').replace(/\s+/g, ' ').trim().toLowerCase();
59
+ }
60
+
61
+ /**
62
+ * Evaluates whether a cell or attribute value matches any of the filter's values (OR logic).
63
+ * @param {unknown} value
64
+ * @param {string[]} filterValues
65
+ * @returns {boolean}
66
+ */
67
+ export function matchesFilterValues(value, filterValues) {
68
+ if (!filterValues || filterValues.length === 0) return true;
69
+ if (value === null || value === undefined) return false;
70
+
71
+ const lowerVal = String(value).trim().toLowerCase();
72
+ for (let i = 0; i < filterValues.length; i++) {
73
+ if (String(filterValues[i]).trim().toLowerCase() === lowerVal) {
74
+ return true;
75
+ }
76
+ }
77
+ return false;
78
+ }
@@ -1,125 +1,125 @@
1
- const _formatterCache = {};
2
-
3
- /**
4
- * Extract group and decimal separators dynamically from Intl.NumberFormat.
5
- * Supports standard commas, dots, and unicode narrow no-break space (U+202F) for fr-FR.
6
- *
7
- * @param {string} locale BCP 47 locale tag (e.g. 'en-US', 'mk-MK', 'fr-FR')
8
- * @returns {{ groupSep: string, decimalSep: string, fmt: Intl.NumberFormat }}
9
- */
10
- export function getSeparators(locale) {
11
- const key = locale || 'default';
12
- if (!_formatterCache[key]) {
13
- const fmt = new Intl.NumberFormat(locale, { useGrouping: true });
14
- const parts = fmt.formatToParts(1234.5);
15
- let groupSep = '';
16
- let decimalSep = '.';
17
- for (let i = 0; i < parts.length; i++) {
18
- if (parts[i].type === 'group') groupSep = parts[i].value;
19
- if (parts[i].type === 'decimal') decimalSep = parts[i].value;
20
- }
21
- _formatterCache[key] = { groupSep, decimalSep, fmt };
22
- }
23
- return _formatterCache[key];
24
- }
25
-
26
- /**
27
- * Clean a numeric string by removing group separators, unicode spaces,
28
- * and normalizing the decimal separator to a period ('.').
29
- *
30
- * @param {string} raw Raw user input or string representation
31
- * @param {string} groupSep Group (thousands) separator
32
- * @param {string} decimalSep Decimal separator
33
- * @returns {string} Standardized numeric string (e.g. "1234.56" or "-10")
34
- */
35
- export function cleanNumericString(raw, groupSep, decimalSep) {
36
- if (raw == null || typeof raw !== 'string') return '';
37
- let str = raw.trim();
38
- if (str === '') return '';
39
-
40
- // Remove common currency symbols
41
- str = str.replace(/[$€£¥]/g, '');
42
-
43
- // Remove group separators if present
44
- if (groupSep) {
45
- str = str.split(groupSep).join('');
46
- }
47
-
48
- // Remove all Unicode whitespace characters (covers space, U+00A0, U+202F, etc.)
49
- str = str.replace(/\s/g, '');
50
-
51
- // Normalize decimal separator to '.'
52
- if (decimalSep && decimalSep !== '.') {
53
- str = str.replace(decimalSep, '.');
54
- }
55
-
56
- // Strip any remaining characters except digits, minus, and dot
57
- str = str.replace(/[^\d.-]/g, '');
58
-
59
- return str;
60
- }
61
-
62
- /**
63
- * Parse a localized numeric string or raw value into a valid floating-point number.
64
- *
65
- * @param {unknown} raw Raw string or number
66
- * @param {string} locale BCP 47 locale tag
67
- * @returns {number} Parsed float or NaN if invalid
68
- */
69
- export function parseNumber(raw, locale) {
70
- if (typeof raw === 'number') return isNaN(raw) ? NaN : raw;
71
- if (raw == null || typeof raw !== 'string') return NaN;
72
- const trimmed = raw.trim();
73
- if (trimmed === '' || trimmed === '-') return NaN;
74
-
75
- const info = getSeparators(locale);
76
- const cleaned = cleanNumericString(trimmed, info.groupSep, info.decimalSep);
77
- if (cleaned === '' || cleaned === '-') return NaN;
78
-
79
- const num = parseFloat(cleaned);
80
- return isNaN(num) ? NaN : num;
81
- }
82
-
83
- /**
84
- * Format a number using Intl.NumberFormat according to specified locale and options.
85
- *
86
- * @param {number} num Number to format
87
- * @param {string} [locale='default'] BCP 47 locale tag
88
- * @param {Object} [options] Formatting options
89
- * @param {number|null} [options.maxDecimals] Maximum fraction digits limit
90
- * @param {number} [options.userDecimals] Exact fraction digits to preserve during typing
91
- * @returns {string} Formatted number string
92
- */
93
- export function formatNumber(num, locale, options = {}) {
94
- if (typeof num !== 'number' || isNaN(num) || !Number.isFinite(num)) return '';
95
-
96
- const loc = locale || 'default';
97
- const maxDecimals = options.maxDecimals != null ? parseInt(options.maxDecimals, 10) : null;
98
- const userDecimals = options.userDecimals != null ? options.userDecimals : null;
99
-
100
- if (maxDecimals !== null) {
101
- const cacheKey = loc + '|max:' + maxDecimals;
102
- if (!_formatterCache[cacheKey]) {
103
- _formatterCache[cacheKey] = new Intl.NumberFormat(locale, {
104
- useGrouping: true,
105
- minimumFractionDigits: 0,
106
- maximumFractionDigits: maxDecimals
107
- });
108
- }
109
- return _formatterCache[cacheKey].format(num);
110
- }
111
-
112
- if (userDecimals !== null && userDecimals > 0) {
113
- const cacheKey = loc + '|exact:' + userDecimals;
114
- if (!_formatterCache[cacheKey]) {
115
- _formatterCache[cacheKey] = new Intl.NumberFormat(locale, {
116
- useGrouping: true,
117
- minimumFractionDigits: userDecimals,
118
- maximumFractionDigits: userDecimals
119
- });
120
- }
121
- return _formatterCache[cacheKey].format(num);
122
- }
123
-
124
- return getSeparators(locale).fmt.format(num);
125
- }
1
+ const _formatterCache = {};
2
+
3
+ /**
4
+ * Extract group and decimal separators dynamically from Intl.NumberFormat.
5
+ * Supports standard commas, dots, and unicode narrow no-break space (U+202F) for fr-FR.
6
+ *
7
+ * @param {string} locale BCP 47 locale tag (e.g. 'en-US', 'mk-MK', 'fr-FR')
8
+ * @returns {{ groupSep: string, decimalSep: string, fmt: Intl.NumberFormat }}
9
+ */
10
+ export function getSeparators(locale) {
11
+ const key = locale || 'default';
12
+ if (!_formatterCache[key]) {
13
+ const fmt = new Intl.NumberFormat(locale, { useGrouping: true });
14
+ const parts = fmt.formatToParts(1234.5);
15
+ let groupSep = '';
16
+ let decimalSep = '.';
17
+ for (let i = 0; i < parts.length; i++) {
18
+ if (parts[i].type === 'group') groupSep = parts[i].value;
19
+ if (parts[i].type === 'decimal') decimalSep = parts[i].value;
20
+ }
21
+ _formatterCache[key] = { groupSep, decimalSep, fmt };
22
+ }
23
+ return _formatterCache[key];
24
+ }
25
+
26
+ /**
27
+ * Clean a numeric string by removing group separators, unicode spaces,
28
+ * and normalizing the decimal separator to a period ('.').
29
+ *
30
+ * @param {string} raw Raw user input or string representation
31
+ * @param {string} groupSep Group (thousands) separator
32
+ * @param {string} decimalSep Decimal separator
33
+ * @returns {string} Standardized numeric string (e.g. "1234.56" or "-10")
34
+ */
35
+ export function cleanNumericString(raw, groupSep, decimalSep) {
36
+ if (raw == null || typeof raw !== 'string') return '';
37
+ let str = raw.trim();
38
+ if (str === '') return '';
39
+
40
+ // Remove common currency symbols
41
+ str = str.replace(/[$€£¥]/g, '');
42
+
43
+ // Remove group separators if present
44
+ if (groupSep) {
45
+ str = str.split(groupSep).join('');
46
+ }
47
+
48
+ // Remove all Unicode whitespace characters (covers space, U+00A0, U+202F, etc.)
49
+ str = str.replace(/\s/g, '');
50
+
51
+ // Normalize decimal separator to '.'
52
+ if (decimalSep && decimalSep !== '.') {
53
+ str = str.replace(decimalSep, '.');
54
+ }
55
+
56
+ // Strip any remaining characters except digits, minus, and dot
57
+ str = str.replace(/[^\d.-]/g, '');
58
+
59
+ return str;
60
+ }
61
+
62
+ /**
63
+ * Parse a localized numeric string or raw value into a valid floating-point number.
64
+ *
65
+ * @param {unknown} raw Raw string or number
66
+ * @param {string} locale BCP 47 locale tag
67
+ * @returns {number} Parsed float or NaN if invalid
68
+ */
69
+ export function parseNumber(raw, locale) {
70
+ if (typeof raw === 'number') return isNaN(raw) ? NaN : raw;
71
+ if (raw == null || typeof raw !== 'string') return NaN;
72
+ const trimmed = raw.trim();
73
+ if (trimmed === '' || trimmed === '-') return NaN;
74
+
75
+ const info = getSeparators(locale);
76
+ const cleaned = cleanNumericString(trimmed, info.groupSep, info.decimalSep);
77
+ if (cleaned === '' || cleaned === '-') return NaN;
78
+
79
+ const num = parseFloat(cleaned);
80
+ return isNaN(num) ? NaN : num;
81
+ }
82
+
83
+ /**
84
+ * Format a number using Intl.NumberFormat according to specified locale and options.
85
+ *
86
+ * @param {number} num Number to format
87
+ * @param {string} [locale='default'] BCP 47 locale tag
88
+ * @param {Object} [options] Formatting options
89
+ * @param {number|null} [options.maxDecimals] Maximum fraction digits limit
90
+ * @param {number} [options.userDecimals] Exact fraction digits to preserve during typing
91
+ * @returns {string} Formatted number string
92
+ */
93
+ export function formatNumber(num, locale, options = {}) {
94
+ if (typeof num !== 'number' || isNaN(num) || !Number.isFinite(num)) return '';
95
+
96
+ const loc = locale || 'default';
97
+ const maxDecimals = options.maxDecimals != null ? parseInt(options.maxDecimals, 10) : null;
98
+ const userDecimals = options.userDecimals != null ? options.userDecimals : null;
99
+
100
+ if (maxDecimals !== null) {
101
+ const cacheKey = loc + '|max:' + maxDecimals;
102
+ if (!_formatterCache[cacheKey]) {
103
+ _formatterCache[cacheKey] = new Intl.NumberFormat(locale, {
104
+ useGrouping: true,
105
+ minimumFractionDigits: 0,
106
+ maximumFractionDigits: maxDecimals
107
+ });
108
+ }
109
+ return _formatterCache[cacheKey].format(num);
110
+ }
111
+
112
+ if (userDecimals !== null && userDecimals > 0) {
113
+ const cacheKey = loc + '|exact:' + userDecimals;
114
+ if (!_formatterCache[cacheKey]) {
115
+ _formatterCache[cacheKey] = new Intl.NumberFormat(locale, {
116
+ useGrouping: true,
117
+ minimumFractionDigits: userDecimals,
118
+ maximumFractionDigits: userDecimals
119
+ });
120
+ }
121
+ return _formatterCache[cacheKey].format(num);
122
+ }
123
+
124
+ return getSeparators(locale).fmt.format(num);
125
+ }