@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.
- package/LICENSE +641 -641
- package/components/COMPONENTS.md +1116 -1116
- package/components/index.js +81 -81
- package/components/ln-accordion/ln-accordion-dev.scss +8 -8
- package/components/ln-accordion/ln-accordion.js +1 -1
- package/components/ln-accordion/ln-accordion.schema.json +30 -30
- package/components/ln-ajax/ln-ajax-dev.scss +8 -8
- package/components/ln-ajax/ln-ajax.js +1 -1
- package/components/ln-ajax/ln-ajax.schema.json +30 -30
- package/components/ln-api-connector/ln-api-connector.js +1 -1
- package/components/ln-api-connector/ln-api-connector.schema.json +9 -0
- package/components/ln-api-connector/src/ln-api-connector.js +2 -0
- package/components/ln-api-queue/ln-api-queue.js +1 -1
- package/components/ln-api-queue/ln-api-queue.schema.json +34 -34
- package/components/ln-api-queue/src/ln-api-queue.js +3 -13
- package/components/ln-autoresize/ln-autoresize-dev.scss +8 -8
- package/components/ln-autoresize/ln-autoresize.js +1 -1
- package/components/ln-autoresize/ln-autoresize.schema.json +22 -22
- package/components/ln-autosave/ln-autosave-dev.scss +13 -13
- package/components/ln-autosave/ln-autosave.js +1 -1
- package/components/ln-autosave/ln-autosave.schema.json +48 -48
- package/components/ln-autosave/src/autosave-model.js +34 -34
- package/components/ln-chart/ln-chart.js +1 -1
- package/components/ln-chart/ln-chart.schema.json +146 -146
- package/components/ln-chart/src/chart-model.js +154 -154
- package/components/ln-circular-progress/ln-circular-progress.js +1 -1
- package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -42
- package/components/ln-confirm/ln-confirm-dev.scss +24 -24
- package/components/ln-confirm/ln-confirm.js +1 -1
- package/components/ln-confirm/ln-confirm.schema.json +66 -66
- package/components/ln-confirm/src/ln-confirm.js +178 -178
- package/components/ln-core/attrs.js +13 -0
- package/components/ln-core/crypto.js +286 -265
- package/components/ln-core/dom.js +115 -115
- package/components/ln-core/form.js +146 -136
- package/components/ln-core/helpers.js +33 -33
- package/components/ln-core/http.js +48 -48
- package/components/ln-core/index.js +1 -1
- package/components/ln-core/lifecycle.js +5 -0
- package/components/ln-core/ln-core.schema.json +3 -2
- package/components/ln-core/locale.js +55 -55
- package/components/ln-core/matching.js +78 -78
- package/components/ln-core/number.js +125 -125
- package/components/ln-core/template.js +206 -206
- package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -1
- package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -50
- package/components/ln-couchdb-connector/src/ln-couchdb-connector.js +1 -0
- package/components/ln-data-coordinator/README.md +9 -38
- package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -8
- package/components/ln-data-coordinator/ln-data-coordinator.js +1 -1
- package/components/ln-data-coordinator/ln-data-coordinator.schema.json +16 -16
- package/components/ln-data-coordinator/src/data-read-policy.js +0 -7
- package/components/ln-data-coordinator/src/ln-data-coordinator.js +432 -1308
- package/components/ln-data-store/ln-data-store-dev.scss +13 -13
- package/components/ln-data-store/ln-data-store.js +1 -1
- package/components/ln-data-store/ln-data-store.schema.json +84 -84
- package/components/ln-data-store/src/data-store-model.js +184 -184
- package/components/ln-data-store/src/ln-data-store.js +1 -1
- package/components/ln-date/ln-date.js +1 -1
- package/components/ln-date/ln-date.schema.json +92 -92
- package/components/ln-date/src/date-model.js +186 -186
- package/components/ln-debug/ln-debug.js +1 -1
- package/components/ln-debug/ln-debug.schema.json +28 -28
- package/components/ln-debug/src/console-sink.js +19 -19
- package/components/ln-debug/src/gate.js +95 -95
- package/components/ln-debug/src/generated-attributes.js +4 -2
- package/components/ln-dropdown/ln-dropdown-dev.scss +13 -13
- package/components/ln-dropdown/ln-dropdown.js +1 -1
- package/components/ln-dropdown/ln-dropdown.schema.json +90 -90
- package/components/ln-editor/ln-editor.js +1 -1
- package/components/ln-editor/ln-editor.schema.json +58 -58
- package/components/ln-external-links/ln-external-links.js +1 -1
- package/components/ln-external-links/ln-external-links.schema.json +18 -18
- package/components/ln-fill/ln-fill.js +1 -1
- package/components/ln-fill/ln-fill.schema.json +23 -23
- package/components/ln-filter/ln-filter.js +1 -1
- package/components/ln-filter/ln-filter.schema.json +153 -153
- package/components/ln-filter/src/filter-model.js +185 -185
- package/components/ln-form/ln-form-dev.scss +8 -8
- package/components/ln-form/ln-form.js +1 -1
- package/components/ln-form/ln-form.schema.json +6 -0
- package/components/ln-form/src/ln-form.js +21 -0
- package/components/ln-http/ln-http.js +1 -1
- package/components/ln-http/ln-http.schema.json +6 -6
- package/components/ln-icon/ln-icon.schema.json +6 -6
- package/components/ln-include/ln-include.js +1 -1
- package/components/ln-include/ln-include.schema.json +23 -23
- package/components/ln-key/ln-key-dev.scss +18 -18
- package/components/ln-key/ln-key.js +1 -1
- package/components/ln-key/ln-key.schema.json +56 -56
- package/components/ln-key/src/ln-key.js +10 -10
- package/components/ln-link/ln-link-dev.scss +8 -8
- package/components/ln-link/ln-link.js +1 -1
- package/components/ln-link/ln-link.schema.json +22 -22
- package/components/ln-list/README.md +152 -152
- package/components/ln-list/ln-list-dev.scss +8 -8
- package/components/ln-list/ln-list.js +1 -1
- package/components/ln-list/ln-list.schema.json +166 -166
- package/components/ln-modal/ln-modal.js +1 -1
- package/components/ln-modal/ln-modal.schema.json +65 -65
- package/components/ln-nav/ln-nav.js +1 -1
- package/components/ln-nav/ln-nav.schema.json +25 -25
- package/components/ln-number/ln-number.js +1 -1
- package/components/ln-number/ln-number.schema.json +63 -63
- package/components/ln-number/src/ln-number.js +26 -0
- package/components/ln-number/src/number-model.js +31 -31
- package/components/ln-obfuscator/README.md +60 -60
- package/components/ln-obfuscator/ln-obfuscator.js +1 -1
- package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -49
- package/components/ln-obfuscator/src/ln-obfuscator.js +159 -159
- package/components/ln-obfuscator/src/obfuscator-model.js +250 -250
- package/components/ln-options/ln-options.js +1 -1
- package/components/ln-options/ln-options.schema.json +6 -0
- package/components/ln-options/src/ln-options.js +4 -4
- package/components/ln-persist/README.md +151 -151
- package/components/ln-persist/ln-persist.js +1 -1
- package/components/ln-persist/ln-persist.schema.json +28 -28
- package/components/ln-persist/src/ln-persist.js +83 -83
- package/components/ln-picklist/README.md +230 -230
- package/components/ln-picklist/ln-picklist-dev.scss +22 -22
- package/components/ln-picklist/ln-picklist.js +1 -1
- package/components/ln-picklist/ln-picklist.schema.json +49 -49
- package/components/ln-picklist/src/ln-picklist.js +228 -228
- package/components/ln-popover/ln-popover-dev.scss +13 -13
- package/components/ln-popover/ln-popover.js +1 -1
- package/components/ln-popover/ln-popover.schema.json +82 -82
- package/components/ln-progress/ln-progress-dev.scss +8 -8
- package/components/ln-progress/ln-progress.js +1 -1
- package/components/ln-progress/ln-progress.schema.json +40 -40
- package/components/ln-router/ln-router.js +1 -1
- package/components/ln-router/ln-router.schema.json +76 -76
- package/components/ln-scroll/README.md +45 -45
- package/components/ln-scroll/ln-scroll.js +1 -1
- package/components/ln-scroll/ln-scroll.schema.json +85 -85
- package/components/ln-scroll/src/ln-scroll.js +150 -150
- package/components/ln-search/README.md +188 -188
- package/components/ln-search/ln-search.js +1 -1
- package/components/ln-search/ln-search.schema.json +103 -103
- package/components/ln-slug/ln-slug.js +1 -1
- package/components/ln-slug/ln-slug.schema.json +23 -23
- package/components/ln-slug/src/ln-slug.js +25 -1
- package/components/ln-sort/ln-sort.js +1 -1
- package/components/ln-sort/ln-sort.schema.json +104 -104
- package/components/ln-sort/src/ln-sort.js +321 -321
- package/components/ln-sort/src/sort-model.js +117 -117
- package/components/ln-sortable/ln-sortable-dev.scss +13 -13
- package/components/ln-sortable/ln-sortable.js +1 -1
- package/components/ln-sortable/ln-sortable.schema.json +36 -36
- package/components/ln-stat/ln-stat-dev.scss +8 -8
- package/components/ln-stat/ln-stat.js +1 -1
- package/components/ln-stat/ln-stat.schema.json +32 -32
- package/components/ln-stat/src/stat-model.js +28 -28
- package/components/ln-table/ln-table.js +1 -1
- package/components/ln-table/ln-table.schema.json +211 -211
- package/components/ln-table/src/table-model.js +98 -98
- package/components/ln-table-coordinator/ln-table-coordinator.js +1 -1
- package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -93
- package/components/ln-tabs/ln-tabs.js +1 -1
- package/components/ln-tabs/ln-tabs.schema.json +75 -75
- package/components/ln-tabs/src/ln-tabs.js +208 -208
- package/components/ln-tabs/src/tabs-model.js +82 -82
- package/components/ln-time/ln-time-dev.scss +13 -13
- package/components/ln-time/ln-time.js +1 -1
- package/components/ln-time/ln-time.schema.json +38 -38
- package/components/ln-toast/ln-toast.js +1 -1
- package/components/ln-toast/ln-toast.schema.json +65 -65
- package/components/ln-toggle/ln-toggle.js +1 -1
- package/components/ln-toggle/ln-toggle.schema.json +52 -52
- package/components/ln-tooltip/ln-tooltip.js +1 -1
- package/components/ln-tooltip/ln-tooltip.schema.json +86 -86
- package/components/ln-translations/ln-translations.js +1 -1
- package/components/ln-translations/ln-translations.schema.json +120 -120
- package/components/ln-ui-coordinator/README.md +62 -62
- package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -1
- package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +28 -28
- package/components/ln-ui-coordinator/src/ln-ui-coordinator.js +140 -140
- package/components/ln-upload/ln-upload.js +1 -1
- package/components/ln-upload/ln-upload.schema.json +193 -193
- package/components/ln-upload/src/upload-model.js +78 -78
- package/components/ln-validate/ln-validate.js +1 -1
- package/components/ln-validate/ln-validate.schema.json +50 -50
- package/components/ln-validate/src/validate-model.js +64 -64
- package/components/ln-websocket-connector/README.md +166 -166
- package/components/ln-websocket-connector/ln-websocket-connector.js +1 -1
- package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -29
- package/components/ln-websocket-connector/src/ln-websocket-connector.js +283 -279
- package/ln-ashlar.html-data.json +15 -8
- package/package.json +85 -84
- package/theme/brand.css +73 -73
- package/theme/components/_confirm.scss +6 -6
- package/theme/components/_picklist.scss +3 -3
- package/theme/config/_palette.scss +274 -274
- package/theme/config/_theme.scss +292 -292
- package/theme/config/_tokens.scss +285 -285
- package/theme/config/mixins/_index.scss +62 -62
- package/theme/config/mixins/_picklist.scss +66 -66
- package/theme/ln-ashlar-dev.scss +66 -66
- package/theme/ln-ashlar-landing.scss +56 -56
- package/theme/ln-ashlar-theme.scss +63 -63
- package/theme/presentation/_bento.scss +151 -151
- package/theme/presentation/_cta.scss +111 -111
- package/theme/presentation/_effects.scss +216 -216
- package/theme/presentation/_features.scss +108 -108
- package/theme/presentation/_footer.scss +129 -129
- package/theme/presentation/_hero.scss +158 -158
- package/theme/presentation/_layout.scss +170 -170
- package/theme/presentation/_navbar.scss +140 -140
- package/theme/presentation/_pricing.scss +262 -262
- package/theme/presentation/_stats.scss +87 -87
- package/theme/presentation/_testimonials.scss +124 -124
- package/theme/presentation/_tokens.scss +39 -39
- package/theme/presentation/_typography.scss +163 -163
- package/web-types.json +26 -12
|
@@ -1,115 +1,115 @@
|
|
|
1
|
-
// ─── Guard Body ────────────────────────────────────────
|
|
2
|
-
|
|
3
|
-
export function guardBody(setupFn, componentTag) {
|
|
4
|
-
if (!document.body) {
|
|
5
|
-
document.addEventListener('DOMContentLoaded', function () {
|
|
6
|
-
guardBody(setupFn, componentTag);
|
|
7
|
-
});
|
|
8
|
-
console.warn('[' + componentTag + '] Script loaded before <body> — add "defer" to your <script> tag');
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
setupFn();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// ─── Visibility Check ─────────────────────────────────────
|
|
15
|
-
|
|
16
|
-
export function isVisible(el) {
|
|
17
|
-
return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Determines whether a mouse click event should be ignored (modified click or non-primary button).
|
|
22
|
-
* @param {MouseEvent|Object} [event]
|
|
23
|
-
* @returns {boolean}
|
|
24
|
-
*/
|
|
25
|
-
export function shouldIgnoreClick(event) {
|
|
26
|
-
if (!event) return true;
|
|
27
|
-
if (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) return true;
|
|
28
|
-
if (typeof event.button === 'number' && event.button !== 0) return true;
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Checks if a target element is an active editable field or contenteditable element.
|
|
34
|
-
* @param {Element|Object} [target]
|
|
35
|
-
* @returns {boolean}
|
|
36
|
-
*/
|
|
37
|
-
export function isEditableTarget(target) {
|
|
38
|
-
if (!target) return false;
|
|
39
|
-
if (typeof target.closest === 'function') {
|
|
40
|
-
return Boolean(target.closest('input, textarea, select, [contenteditable]:not([contenteditable="false"])'));
|
|
41
|
-
}
|
|
42
|
-
const tag = String(target.tagName || '').toLowerCase();
|
|
43
|
-
return tag === 'input' || tag === 'textarea' || tag === 'select' || Boolean(target.isContentEditable);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Checks if a target element is disabled, aria-disabled, or inside an inert container.
|
|
48
|
-
* @param {Element|Object} [target]
|
|
49
|
-
* @returns {boolean}
|
|
50
|
-
*/
|
|
51
|
-
export function isTargetDisabled(target) {
|
|
52
|
-
if (!target) return true;
|
|
53
|
-
if (target.disabled || (typeof target.getAttribute === 'function' && target.getAttribute('aria-disabled') === 'true')) {
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
if (typeof target.closest === 'function' && target.closest('[inert]')) {
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Checks if a target element is connected, enabled, not inert, visible, and optionally supports an action method.
|
|
64
|
-
* @param {Element} target
|
|
65
|
-
* @param {string} [action]
|
|
66
|
-
* @returns {boolean}
|
|
67
|
-
*/
|
|
68
|
-
export function isUsableTarget(target, action) {
|
|
69
|
-
if (!target || !document.contains(target)) return false;
|
|
70
|
-
if (isTargetDisabled(target)) return false;
|
|
71
|
-
if (action && typeof target[action] !== 'function') return false;
|
|
72
|
-
return isVisible(target);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Checks if a link click should be intercepted by client-side routing.
|
|
77
|
-
* Must be standard navigation click (left button, no modifiers), same origin,
|
|
78
|
-
* not mailto/tel, and not pure hash/empty.
|
|
79
|
-
*
|
|
80
|
-
* @param {MouseEvent} event
|
|
81
|
-
* @param {HTMLAnchorElement} anchor
|
|
82
|
-
* @returns {boolean}
|
|
83
|
-
*/
|
|
84
|
-
export function shouldInterceptLink(event, anchor) {
|
|
85
|
-
if (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey || event.button !== 0) {
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
if (!anchor) return false;
|
|
89
|
-
const href = anchor.getAttribute('href');
|
|
90
|
-
if (!href) return false;
|
|
91
|
-
if (anchor.getAttribute('target') === '_blank') return false;
|
|
92
|
-
if (anchor.hasAttribute('download')) return false;
|
|
93
|
-
if (href.startsWith('mailto:') || href.startsWith('tel:')) return false;
|
|
94
|
-
if (href === '#' || href.startsWith('#')) return false;
|
|
95
|
-
if (anchor.hostname && anchor.hostname !== window.location.hostname) return false;
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Read the raw machine value behind a formatted cell/item display.
|
|
101
|
-
* Returns the `data-ln-value` attribute if present, else trimmed textContent.
|
|
102
|
-
* Single read path for value-based sort/filter across components.
|
|
103
|
-
*/
|
|
104
|
-
export function readValue(el) {
|
|
105
|
-
if (el.hasAttribute('data-ln-value')) {
|
|
106
|
-
return el.getAttribute('data-ln-value');
|
|
107
|
-
}
|
|
108
|
-
if (el.tagName === 'TIME' && el.hasAttribute('datetime')) {
|
|
109
|
-
return el.getAttribute('datetime');
|
|
110
|
-
}
|
|
111
|
-
if (el.tagName === 'DATA' && el.hasAttribute('value')) {
|
|
112
|
-
return el.getAttribute('value');
|
|
113
|
-
}
|
|
114
|
-
return el.textContent.trim();
|
|
115
|
-
}
|
|
1
|
+
// ─── Guard Body ────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
export function guardBody(setupFn, componentTag) {
|
|
4
|
+
if (!document.body) {
|
|
5
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
6
|
+
guardBody(setupFn, componentTag);
|
|
7
|
+
});
|
|
8
|
+
console.warn('[' + componentTag + '] Script loaded before <body> — add "defer" to your <script> tag');
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
setupFn();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// ─── Visibility Check ─────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
export function isVisible(el) {
|
|
17
|
+
return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Determines whether a mouse click event should be ignored (modified click or non-primary button).
|
|
22
|
+
* @param {MouseEvent|Object} [event]
|
|
23
|
+
* @returns {boolean}
|
|
24
|
+
*/
|
|
25
|
+
export function shouldIgnoreClick(event) {
|
|
26
|
+
if (!event) return true;
|
|
27
|
+
if (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) return true;
|
|
28
|
+
if (typeof event.button === 'number' && event.button !== 0) return true;
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Checks if a target element is an active editable field or contenteditable element.
|
|
34
|
+
* @param {Element|Object} [target]
|
|
35
|
+
* @returns {boolean}
|
|
36
|
+
*/
|
|
37
|
+
export function isEditableTarget(target) {
|
|
38
|
+
if (!target) return false;
|
|
39
|
+
if (typeof target.closest === 'function') {
|
|
40
|
+
return Boolean(target.closest('input, textarea, select, [contenteditable]:not([contenteditable="false"])'));
|
|
41
|
+
}
|
|
42
|
+
const tag = String(target.tagName || '').toLowerCase();
|
|
43
|
+
return tag === 'input' || tag === 'textarea' || tag === 'select' || Boolean(target.isContentEditable);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Checks if a target element is disabled, aria-disabled, or inside an inert container.
|
|
48
|
+
* @param {Element|Object} [target]
|
|
49
|
+
* @returns {boolean}
|
|
50
|
+
*/
|
|
51
|
+
export function isTargetDisabled(target) {
|
|
52
|
+
if (!target) return true;
|
|
53
|
+
if (target.disabled || (typeof target.getAttribute === 'function' && target.getAttribute('aria-disabled') === 'true')) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (typeof target.closest === 'function' && target.closest('[inert]')) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Checks if a target element is connected, enabled, not inert, visible, and optionally supports an action method.
|
|
64
|
+
* @param {Element} target
|
|
65
|
+
* @param {string} [action]
|
|
66
|
+
* @returns {boolean}
|
|
67
|
+
*/
|
|
68
|
+
export function isUsableTarget(target, action) {
|
|
69
|
+
if (!target || !document.contains(target)) return false;
|
|
70
|
+
if (isTargetDisabled(target)) return false;
|
|
71
|
+
if (action && typeof target[action] !== 'function') return false;
|
|
72
|
+
return isVisible(target);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Checks if a link click should be intercepted by client-side routing.
|
|
77
|
+
* Must be standard navigation click (left button, no modifiers), same origin,
|
|
78
|
+
* not mailto/tel, and not pure hash/empty.
|
|
79
|
+
*
|
|
80
|
+
* @param {MouseEvent} event
|
|
81
|
+
* @param {HTMLAnchorElement} anchor
|
|
82
|
+
* @returns {boolean}
|
|
83
|
+
*/
|
|
84
|
+
export function shouldInterceptLink(event, anchor) {
|
|
85
|
+
if (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey || event.button !== 0) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (!anchor) return false;
|
|
89
|
+
const href = anchor.getAttribute('href');
|
|
90
|
+
if (!href) return false;
|
|
91
|
+
if (anchor.getAttribute('target') === '_blank') return false;
|
|
92
|
+
if (anchor.hasAttribute('download')) return false;
|
|
93
|
+
if (href.startsWith('mailto:') || href.startsWith('tel:')) return false;
|
|
94
|
+
if (href === '#' || href.startsWith('#')) return false;
|
|
95
|
+
if (anchor.hostname && anchor.hostname !== window.location.hostname) return false;
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Read the raw machine value behind a formatted cell/item display.
|
|
101
|
+
* Returns the `data-ln-value` attribute if present, else trimmed textContent.
|
|
102
|
+
* Single read path for value-based sort/filter across components.
|
|
103
|
+
*/
|
|
104
|
+
export function readValue(el) {
|
|
105
|
+
if (el.hasAttribute('data-ln-value')) {
|
|
106
|
+
return el.getAttribute('data-ln-value');
|
|
107
|
+
}
|
|
108
|
+
if (el.tagName === 'TIME' && el.hasAttribute('datetime')) {
|
|
109
|
+
return el.getAttribute('datetime');
|
|
110
|
+
}
|
|
111
|
+
if (el.tagName === 'DATA' && el.hasAttribute('value')) {
|
|
112
|
+
return el.getAttribute('value');
|
|
113
|
+
}
|
|
114
|
+
return el.textContent.trim();
|
|
115
|
+
}
|
|
@@ -1,136 +1,146 @@
|
|
|
1
|
-
// ─── Form Method Resolution ────────────────────────────────
|
|
2
|
-
|
|
3
|
-
export function resolveFormMethod(form) {
|
|
4
|
-
const methodInput = form.querySelector('input[name="_method"]');
|
|
5
|
-
const raw = (methodInput && methodInput.value !== '') ? methodInput.value : form.method;
|
|
6
|
-
return (raw || '').toUpperCase();
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
// ─── Form Serialization ───────────────────────────────────
|
|
10
|
-
|
|
11
|
-
export function serializeForm(form, opts) {
|
|
12
|
-
const typed = !!(opts && opts.typed);
|
|
13
|
-
const exclude = opts && opts.exclude;
|
|
14
|
-
const data = {};
|
|
15
|
-
const elements = form.elements;
|
|
16
|
-
|
|
17
|
-
const checkboxCounts = {};
|
|
18
|
-
if (typed) {
|
|
19
|
-
for (let i = 0; i < elements.length; i++) {
|
|
20
|
-
const el = elements[i];
|
|
21
|
-
if (el.name && el.type === 'checkbox' && !el.disabled) {
|
|
22
|
-
checkboxCounts[el.name] = (checkboxCounts[el.name] || 0) + 1;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
for (let i = 0; i < elements.length; i++) {
|
|
28
|
-
const el = elements[i];
|
|
29
|
-
if (!el.name || el.disabled || el.type === 'file' || el.type === 'submit' || el.type === 'button') continue;
|
|
30
|
-
if (exclude && el.matches && el.matches(exclude)) continue;
|
|
31
|
-
|
|
32
|
-
if (el.type === 'checkbox') {
|
|
33
|
-
if (typed && checkboxCounts[el.name] === 1) {
|
|
34
|
-
data[el.name] = el.checked;
|
|
35
|
-
} else {
|
|
36
|
-
if (!data[el.name]) data[el.name] = [];
|
|
37
|
-
if (el.checked) data[el.name].push(el.value);
|
|
38
|
-
}
|
|
39
|
-
} else if (el.type === 'radio') {
|
|
40
|
-
if (el.checked) data[el.name] = el.value;
|
|
41
|
-
} else if (el.type === 'select-multiple') {
|
|
42
|
-
data[el.name] = [];
|
|
43
|
-
for (let j = 0; j < el.options.length; j++) {
|
|
44
|
-
if (el.options[j].selected) data[el.name].push(el.options[j].value);
|
|
45
|
-
}
|
|
46
|
-
} else if (typed && el.type === 'hidden') {
|
|
47
|
-
data[el.name] = el.value;
|
|
48
|
-
} else if (typed && (el.type === 'number' || el.type === 'range')) {
|
|
49
|
-
const n = Number(el.value);
|
|
50
|
-
data[el.name] = (el.value === '' || isNaN(n)) ? null : n;
|
|
51
|
-
} else {
|
|
52
|
-
data[el.name] = el.value;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return data;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ─── Form Population ──────────────────────────────────────
|
|
60
|
-
|
|
61
|
-
function _coerceBool(value) {
|
|
62
|
-
if (typeof value !== 'string') return !!value;
|
|
63
|
-
const v = value.trim().toLowerCase();
|
|
64
|
-
return v !== 'false' && v !== '0' && v !== '' && v !== 'off' && v !== 'no';
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function populateForm(form, data) {
|
|
68
|
-
const elements = form.elements;
|
|
69
|
-
const filled = [];
|
|
70
|
-
|
|
71
|
-
const checkboxCounts = {};
|
|
72
|
-
for (let i = 0; i < elements.length; i++) {
|
|
73
|
-
const el = elements[i];
|
|
74
|
-
if (el.name && el.type === 'checkbox') {
|
|
75
|
-
checkboxCounts[el.name] = (checkboxCounts[el.name] || 0) + 1;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
for (let i = 0; i < elements.length; i++) {
|
|
80
|
-
const el = elements[i];
|
|
81
|
-
if (el.type === 'file' || el.type === 'submit' || el.type === 'button') continue;
|
|
82
|
-
|
|
83
|
-
const matchKey = el.getAttribute('data-ln-fill-as') || el.name;
|
|
84
|
-
if (!matchKey || !(matchKey in data)) continue;
|
|
85
|
-
|
|
86
|
-
const value = data[matchKey];
|
|
87
|
-
|
|
88
|
-
if (el.type === 'checkbox') {
|
|
89
|
-
if (Array.isArray(value)) {
|
|
90
|
-
el.checked = value.indexOf(el.value) !== -1;
|
|
91
|
-
} else if (checkboxCounts[el.name] > 1) {
|
|
92
|
-
const list = String(value).split(',').map(function (s) { return s.trim(); });
|
|
93
|
-
el.checked = list.indexOf(el.value) !== -1;
|
|
94
|
-
} else {
|
|
95
|
-
el.checked = _coerceBool(value);
|
|
96
|
-
}
|
|
97
|
-
filled.push(el);
|
|
98
|
-
} else if (el.type === 'radio') {
|
|
99
|
-
el.checked = el.value === String(value);
|
|
100
|
-
filled.push(el);
|
|
101
|
-
} else if (el.type === 'select-multiple') {
|
|
102
|
-
if (Array.isArray(value)) {
|
|
103
|
-
for (let j = 0; j < el.options.length; j++) {
|
|
104
|
-
el.options[j].selected = value.indexOf(el.options[j].value) !== -1;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
filled.push(el);
|
|
108
|
-
} else {
|
|
109
|
-
el.value = value;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
1
|
+
// ─── Form Method Resolution ────────────────────────────────
|
|
2
|
+
|
|
3
|
+
export function resolveFormMethod(form) {
|
|
4
|
+
const methodInput = form.querySelector('input[name="_method"]');
|
|
5
|
+
const raw = (methodInput && methodInput.value !== '') ? methodInput.value : form.method;
|
|
6
|
+
return (raw || '').toUpperCase();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// ─── Form Serialization ───────────────────────────────────
|
|
10
|
+
|
|
11
|
+
export function serializeForm(form, opts) {
|
|
12
|
+
const typed = !!(opts && opts.typed);
|
|
13
|
+
const exclude = opts && opts.exclude;
|
|
14
|
+
const data = {};
|
|
15
|
+
const elements = form.elements;
|
|
16
|
+
|
|
17
|
+
const checkboxCounts = {};
|
|
18
|
+
if (typed) {
|
|
19
|
+
for (let i = 0; i < elements.length; i++) {
|
|
20
|
+
const el = elements[i];
|
|
21
|
+
if (el.name && el.type === 'checkbox' && !el.disabled) {
|
|
22
|
+
checkboxCounts[el.name] = (checkboxCounts[el.name] || 0) + 1;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
for (let i = 0; i < elements.length; i++) {
|
|
28
|
+
const el = elements[i];
|
|
29
|
+
if (!el.name || el.disabled || el.type === 'file' || el.type === 'submit' || el.type === 'button') continue;
|
|
30
|
+
if (exclude && el.matches && el.matches(exclude)) continue;
|
|
31
|
+
|
|
32
|
+
if (el.type === 'checkbox') {
|
|
33
|
+
if (typed && checkboxCounts[el.name] === 1) {
|
|
34
|
+
data[el.name] = el.checked;
|
|
35
|
+
} else {
|
|
36
|
+
if (!data[el.name]) data[el.name] = [];
|
|
37
|
+
if (el.checked) data[el.name].push(el.value);
|
|
38
|
+
}
|
|
39
|
+
} else if (el.type === 'radio') {
|
|
40
|
+
if (el.checked) data[el.name] = el.value;
|
|
41
|
+
} else if (el.type === 'select-multiple') {
|
|
42
|
+
data[el.name] = [];
|
|
43
|
+
for (let j = 0; j < el.options.length; j++) {
|
|
44
|
+
if (el.options[j].selected) data[el.name].push(el.options[j].value);
|
|
45
|
+
}
|
|
46
|
+
} else if (typed && el.type === 'hidden') {
|
|
47
|
+
data[el.name] = el.value;
|
|
48
|
+
} else if (typed && (el.type === 'number' || el.type === 'range')) {
|
|
49
|
+
const n = Number(el.value);
|
|
50
|
+
data[el.name] = (el.value === '' || isNaN(n)) ? null : n;
|
|
51
|
+
} else {
|
|
52
|
+
data[el.name] = el.value;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return data;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ─── Form Population ──────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
function _coerceBool(value) {
|
|
62
|
+
if (typeof value !== 'string') return !!value;
|
|
63
|
+
const v = value.trim().toLowerCase();
|
|
64
|
+
return v !== 'false' && v !== '0' && v !== '' && v !== 'off' && v !== 'no';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function populateForm(form, data) {
|
|
68
|
+
const elements = form.elements;
|
|
69
|
+
const filled = [];
|
|
70
|
+
|
|
71
|
+
const checkboxCounts = {};
|
|
72
|
+
for (let i = 0; i < elements.length; i++) {
|
|
73
|
+
const el = elements[i];
|
|
74
|
+
if (el.name && el.type === 'checkbox') {
|
|
75
|
+
checkboxCounts[el.name] = (checkboxCounts[el.name] || 0) + 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
for (let i = 0; i < elements.length; i++) {
|
|
80
|
+
const el = elements[i];
|
|
81
|
+
if (el.type === 'file' || el.type === 'submit' || el.type === 'button') continue;
|
|
82
|
+
|
|
83
|
+
const matchKey = el.getAttribute('data-ln-fill-as') || el.name;
|
|
84
|
+
if (!matchKey || !(matchKey in data)) continue;
|
|
85
|
+
|
|
86
|
+
const value = data[matchKey];
|
|
87
|
+
|
|
88
|
+
if (el.type === 'checkbox') {
|
|
89
|
+
if (Array.isArray(value)) {
|
|
90
|
+
el.checked = value.indexOf(el.value) !== -1;
|
|
91
|
+
} else if (checkboxCounts[el.name] > 1) {
|
|
92
|
+
const list = String(value).split(',').map(function (s) { return s.trim(); });
|
|
93
|
+
el.checked = list.indexOf(el.value) !== -1;
|
|
94
|
+
} else {
|
|
95
|
+
el.checked = _coerceBool(value);
|
|
96
|
+
}
|
|
97
|
+
filled.push(el);
|
|
98
|
+
} else if (el.type === 'radio') {
|
|
99
|
+
el.checked = el.value === String(value);
|
|
100
|
+
filled.push(el);
|
|
101
|
+
} else if (el.type === 'select-multiple') {
|
|
102
|
+
if (Array.isArray(value)) {
|
|
103
|
+
for (let j = 0; j < el.options.length; j++) {
|
|
104
|
+
el.options[j].selected = value.indexOf(el.options[j].value) !== -1;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
filled.push(el);
|
|
108
|
+
} else {
|
|
109
|
+
el.value = value;
|
|
110
|
+
if (el.tagName === 'SELECT' && value != null) {
|
|
111
|
+
el.setAttribute('data-ln-value', value);
|
|
112
|
+
}
|
|
113
|
+
filled.push(el);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return filled;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ─── Value Property Interception ───────────────────────────
|
|
121
|
+
|
|
122
|
+
export function interceptValueProperty(dom, descriptor, { get, set }) {
|
|
123
|
+
Object.defineProperty(dom, 'value', {
|
|
124
|
+
get: function () {
|
|
125
|
+
if (get) {
|
|
126
|
+
return get.call(this);
|
|
127
|
+
}
|
|
128
|
+
return descriptor.get.call(this);
|
|
129
|
+
},
|
|
130
|
+
set: function (val) {
|
|
131
|
+
if (set) {
|
|
132
|
+
set.call(this, val, (originalVal) => descriptor.set.call(this, originalVal));
|
|
133
|
+
} else {
|
|
134
|
+
descriptor.set.call(this, val);
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
configurable: true
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (typeof window !== 'undefined') {
|
|
142
|
+
window.lnCore = window.lnCore || {};
|
|
143
|
+
window.lnCore.populateForm = populateForm;
|
|
144
|
+
window.lnCore.serializeForm = serializeForm;
|
|
145
|
+
window.lnCore.resolveFormMethod = resolveFormMethod;
|
|
146
|
+
}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
// ==========================================================================
|
|
2
|
-
// ln-core — Shared Helpers & Runtime Kernel Facade (Barrel File)
|
|
3
|
-
// ==========================================================================
|
|
4
|
-
// All primitives are modularized into cohesive single-responsibility modules.
|
|
5
|
-
// This barrel re-exports the entire public surface with 100% backward
|
|
6
|
-
// compatibility. Evaluation order preserves module initialization invariants:
|
|
7
|
-
// events.js evaluates first to ensure window.lnCore and console warning
|
|
8
|
-
// interception are established before subsequent modules load.
|
|
9
|
-
// ==========================================================================
|
|
10
|
-
|
|
11
|
-
// Events & Debug
|
|
12
|
-
export { dispatch, dispatchCancelable, requestData, setDebugSink, setPersistSink, hasActiveDebug, isDevMode } from './events.js';
|
|
13
|
-
|
|
14
|
-
// DOM & Target Predicates
|
|
15
|
-
export { guardBody, isVisible, shouldIgnoreClick, isEditableTarget, isTargetDisabled, isUsableTarget, shouldInterceptLink, readValue } from './dom.js';
|
|
16
|
-
|
|
17
|
-
// Form Utilities
|
|
18
|
-
export { resolveFormMethod, serializeForm, populateForm, interceptValueProperty } from './form.js';
|
|
19
|
-
|
|
20
|
-
// HTTP & Data Mappers
|
|
21
|
-
export { buildUrl, getHeaders, parseHeaders, registerDataMapper, getDataMapper } from './http.js';
|
|
22
|
-
|
|
23
|
-
// Locale & i18n
|
|
24
|
-
export { getLocale, ensureLocaleObserver, registerLocaleFallback, getLocaleFallback } from './locale.js';
|
|
25
|
-
|
|
26
|
-
// Templates & DOM Fill
|
|
27
|
-
export { cloneTemplate, cloneTemplateScoped, fill, lnFill, fillTemplate, renderList, buildDict } from './template.js';
|
|
28
|
-
|
|
29
|
-
// Lifecycle & Kernel Registration
|
|
30
|
-
export { findElements, holdInit, releaseInit, pendingCount, queueBoot, observeAttributes, registerComponent } from './lifecycle.js';
|
|
31
|
-
|
|
32
|
-
// Value Comparison Primitives (re-exported from compare.js for backward compatibility)
|
|
33
|
-
export { compareValues, detectValueType } from './compare.js';
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// ln-core — Shared Helpers & Runtime Kernel Facade (Barrel File)
|
|
3
|
+
// ==========================================================================
|
|
4
|
+
// All primitives are modularized into cohesive single-responsibility modules.
|
|
5
|
+
// This barrel re-exports the entire public surface with 100% backward
|
|
6
|
+
// compatibility. Evaluation order preserves module initialization invariants:
|
|
7
|
+
// events.js evaluates first to ensure window.lnCore and console warning
|
|
8
|
+
// interception are established before subsequent modules load.
|
|
9
|
+
// ==========================================================================
|
|
10
|
+
|
|
11
|
+
// Events & Debug
|
|
12
|
+
export { dispatch, dispatchCancelable, requestData, setDebugSink, setPersistSink, hasActiveDebug, isDevMode } from './events.js';
|
|
13
|
+
|
|
14
|
+
// DOM & Target Predicates
|
|
15
|
+
export { guardBody, isVisible, shouldIgnoreClick, isEditableTarget, isTargetDisabled, isUsableTarget, shouldInterceptLink, readValue } from './dom.js';
|
|
16
|
+
|
|
17
|
+
// Form Utilities
|
|
18
|
+
export { resolveFormMethod, serializeForm, populateForm, interceptValueProperty } from './form.js';
|
|
19
|
+
|
|
20
|
+
// HTTP & Data Mappers
|
|
21
|
+
export { buildUrl, getHeaders, parseHeaders, registerDataMapper, getDataMapper } from './http.js';
|
|
22
|
+
|
|
23
|
+
// Locale & i18n
|
|
24
|
+
export { getLocale, ensureLocaleObserver, registerLocaleFallback, getLocaleFallback } from './locale.js';
|
|
25
|
+
|
|
26
|
+
// Templates & DOM Fill
|
|
27
|
+
export { cloneTemplate, cloneTemplateScoped, fill, lnFill, fillTemplate, renderList, buildDict } from './template.js';
|
|
28
|
+
|
|
29
|
+
// Lifecycle & Kernel Registration
|
|
30
|
+
export { findElements, holdInit, releaseInit, pendingCount, queueBoot, observeAttributes, registerComponent } from './lifecycle.js';
|
|
31
|
+
|
|
32
|
+
// Value Comparison Primitives (re-exported from compare.js for backward compatibility)
|
|
33
|
+
export { compareValues, detectValueType } from './compare.js';
|