@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.
- package/README.md +189 -177
- package/js/COMPONENTS.md +55 -49
- package/js/index.js +22 -5
- package/js/ln-accordion/README.md +186 -137
- package/js/ln-accordion/ln-accordion.js +1 -1
- package/js/ln-ajax/README.md +73 -4
- package/js/ln-ajax/ln-ajax.js +1 -1
- package/js/ln-ajax/src/ln-ajax.js +13 -13
- package/js/ln-api-connector/README.md +261 -150
- package/js/ln-api-connector/ln-api-connector.js +1 -1
- package/js/ln-api-connector/src/ln-api-connector.js +368 -265
- package/js/ln-api-queue/README.md +219 -0
- package/js/ln-api-queue/ln-api-queue.js +1 -0
- package/js/ln-api-queue/src/ln-api-queue.js +320 -0
- package/js/ln-api-queue/src/queue-storage.js +455 -0
- package/js/ln-autoresize/README.md +47 -0
- package/js/ln-autoresize/ln-autoresize.js +1 -1
- package/js/ln-autosave/README.md +33 -1
- package/js/ln-autosave/ln-autosave.js +1 -1
- package/js/ln-autosave/src/ln-autosave.js +4 -4
- package/js/ln-circular-progress/README.md +27 -13
- package/js/ln-circular-progress/ln-circular-progress.js +1 -1
- package/js/ln-circular-progress/src/ln-circular-progress.js +18 -5
- package/js/ln-confirm/README.md +162 -86
- package/js/ln-confirm/_ln-confirm.scss +11 -0
- package/js/ln-confirm/ln-confirm.js +1 -1
- package/js/ln-confirm/src/ln-confirm.js +77 -41
- package/js/ln-core/README.md +896 -0
- package/js/ln-core/hash.js +92 -0
- package/js/ln-core/helpers.js +337 -8
- package/js/ln-core/index.js +4 -2
- package/js/ln-core/persist.js +29 -3
- package/js/ln-core/positioning.js +1 -35
- package/js/ln-core/window-cache.js +213 -0
- package/js/ln-couchdb-connector/README.md +177 -156
- package/js/ln-couchdb-connector/ln-couchdb-connector.js +1 -1
- package/js/ln-couchdb-connector/src/ln-couchdb-connector.js +398 -348
- package/js/ln-data-coordinator/README.md +536 -165
- package/js/ln-data-coordinator/ln-data-coordinator.js +1 -1
- package/js/ln-data-coordinator/src/data-read-policy.js +18 -0
- package/js/ln-data-coordinator/src/ln-data-coordinator.js +1042 -249
- package/js/ln-data-coordinator/src/mutation-receipts.js +46 -0
- package/js/ln-data-store/README.md +241 -94
- package/js/ln-data-store/ln-data-store.js +1 -1
- package/js/ln-data-store/src/ln-data-store.js +694 -699
- package/js/ln-date/README.md +39 -1
- package/js/ln-date/ln-date-dev.scss +14 -0
- package/js/ln-date/ln-date.js +1 -1
- package/js/ln-date/src/ln-date.js +236 -178
- package/js/ln-debug/README.md +58 -0
- package/js/ln-debug/ln-debug.js +1 -0
- package/js/ln-debug/src/ln-debug.js +19 -0
- package/js/ln-dropdown/README.md +31 -8
- package/js/ln-dropdown/ln-dropdown.js +1 -1
- package/js/ln-dropdown/src/ln-dropdown.js +30 -13
- package/js/ln-editor/README.md +274 -0
- package/js/ln-editor/ln-editor.js +1 -0
- package/js/ln-editor/ln-editor.scss +43 -0
- package/js/ln-editor/src/ln-editor.js +619 -0
- package/js/ln-external-links/README.md +44 -5
- package/js/ln-external-links/ln-external-links.js +1 -1
- package/js/ln-fill/README.md +199 -0
- package/js/ln-fill/ln-fill.js +1 -0
- package/js/ln-fill/src/ln-fill.js +87 -0
- package/js/ln-filter/README.md +92 -2
- package/js/ln-filter/ln-filter-dev.scss +8 -0
- package/js/ln-filter/ln-filter.js +1 -1
- package/js/ln-filter/src/ln-filter.js +22 -7
- package/js/ln-form/README.md +212 -49
- package/js/ln-form/ln-form.js +1 -1
- package/js/ln-form/src/ln-form.js +99 -199
- package/js/ln-http/README.md +125 -89
- package/js/ln-http/ln-http.js +1 -1
- package/js/ln-http/src/ln-http.js +32 -13
- package/js/ln-icon/README.md +130 -0
- package/js/ln-icon/ln-icon.js +1 -0
- package/js/{ln-icons/src/ln-icons.js → ln-icon/src/ln-icon.js} +20 -15
- package/js/ln-link/README.md +47 -9
- package/js/ln-link/ln-link.js +1 -1
- package/js/ln-link/src/ln-link.js +22 -6
- package/js/ln-list/README.md +129 -0
- package/js/ln-list/ln-list.js +1 -0
- package/js/ln-list/ln-list.scss +28 -0
- package/js/ln-list/src/ln-list.js +1216 -0
- package/js/ln-modal/README.md +185 -154
- package/js/ln-modal/ln-modal-dev.scss +13 -0
- package/js/ln-modal/ln-modal.js +1 -1
- package/js/ln-modal/ln-modal.scss +28 -4
- package/js/ln-modal/src/ln-modal.js +137 -201
- package/js/ln-modal-coordinator/README.md +99 -0
- package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +8 -0
- package/js/ln-modal-coordinator/ln-modal-coordinator.js +1 -0
- package/js/ln-modal-coordinator/src/ln-modal-coordinator.js +345 -0
- package/js/ln-nav/README.md +46 -3
- package/js/ln-nav/ln-nav.js +1 -1
- package/js/ln-nav/src/ln-nav.js +92 -127
- package/js/ln-number/README.md +28 -1
- package/js/ln-number/ln-number-dev.scss +13 -0
- package/js/ln-number/ln-number.js +1 -1
- package/js/ln-number/src/ln-number.js +443 -350
- package/js/ln-options/README.md +66 -0
- package/js/ln-options/ln-options.js +1 -0
- package/js/ln-options/src/ln-options.js +74 -0
- package/js/ln-popover/README.md +40 -5
- package/js/ln-popover/ln-popover.js +1 -1
- package/js/ln-popover/src/ln-popover.js +38 -11
- package/js/ln-progress/README.md +413 -442
- package/js/ln-progress/ln-progress.js +1 -1
- package/js/ln-progress/src/ln-progress.js +98 -150
- package/js/ln-router/README.md +432 -0
- package/js/ln-router/ln-router.js +1 -0
- package/js/ln-router/src/ln-router.js +539 -0
- package/js/ln-search/README.md +158 -83
- package/js/ln-search/ln-search-dev.scss +8 -0
- package/js/ln-search/ln-search.js +1 -1
- package/js/ln-search/src/ln-search.js +12 -9
- package/js/ln-slug/README.md +110 -0
- package/js/ln-slug/ln-slug.js +1 -0
- package/js/ln-slug/src/ln-slug.js +74 -0
- package/js/ln-sort/README.md +170 -0
- package/js/ln-sort/ln-sort-dev.scss +8 -0
- package/js/ln-sort/ln-sort.js +1 -0
- package/js/ln-sort/src/ln-sort.js +178 -0
- package/js/ln-sortable/README.md +133 -95
- package/js/ln-sortable/ln-sortable.js +1 -1
- package/js/ln-stat/README.md +55 -0
- package/js/ln-stat/ln-stat.js +1 -0
- package/js/ln-stat/src/ln-stat.js +55 -0
- package/js/ln-table/README.md +285 -101
- package/js/ln-table/ln-table-dev.scss +17 -0
- package/js/ln-table/ln-table.js +1 -1
- package/js/ln-table/ln-table.scss +1 -1
- package/js/ln-table/src/ln-table.js +1493 -473
- package/js/ln-table-coordinator/README.md +73 -0
- package/js/ln-table-coordinator/ln-table-coordinator.js +1 -0
- package/js/ln-table-coordinator/src/ln-table-coordinator.js +160 -0
- package/js/ln-tabs/README.md +221 -137
- package/js/ln-tabs/ln-tabs-dev.scss +18 -0
- package/js/ln-tabs/ln-tabs.js +1 -1
- package/js/ln-tabs/src/ln-tabs.js +41 -24
- package/js/ln-time/README.md +14 -0
- package/js/ln-time/ln-time.js +1 -1
- package/js/ln-time/src/ln-time.js +223 -192
- package/js/ln-toast/README.md +178 -122
- package/js/ln-toast/ln-toast.js +1 -15
- package/js/ln-toast/src/ln-toast.js +232 -210
- package/js/ln-toast/template.html +16 -12
- package/js/ln-toggle/README.md +61 -2
- package/js/ln-toggle/ln-toggle-dev.scss +13 -0
- package/js/ln-toggle/ln-toggle.js +1 -1
- package/js/ln-toggle/src/ln-toggle.js +56 -43
- package/js/ln-tooltip/README.md +95 -58
- package/js/ln-tooltip/ln-tooltip-dev.scss +9 -0
- package/js/ln-tooltip/ln-tooltip.js +1 -1
- package/js/ln-tooltip/src/ln-tooltip.js +45 -6
- package/js/ln-translations/README.md +118 -96
- package/js/ln-translations/ln-translations.js +1 -1
- package/js/ln-translations/src/ln-translations.js +10 -5
- package/js/ln-upload/README.md +85 -10
- package/js/ln-upload/ln-upload.js +1 -1
- package/js/ln-upload/src/ln-upload.js +26 -10
- package/js/ln-validate/README.md +72 -0
- package/js/ln-validate/ln-validate-dev.scss +15 -0
- package/js/ln-validate/ln-validate.js +1 -1
- package/js/ln-validate/src/ln-validate.js +64 -0
- package/package.json +61 -56
- package/scss/base/_global.scss +2 -2
- package/scss/base/_typography.scss +1 -2
- package/scss/components/_date.scss +1 -0
- package/scss/components/_dropdown.scss +0 -5
- package/scss/components/_editor.scss +5 -0
- package/scss/components/_filter.scss +26 -25
- package/scss/components/_form.scss +7 -0
- package/scss/components/_ln-table.scss +13 -3
- package/scss/components/_sort.scss +10 -0
- package/scss/components/_table.scss +36 -23
- package/scss/components/_toast.scss +68 -41
- package/scss/components/_toggle.scss +16 -0
- package/scss/config/_density.scss +205 -117
- package/scss/config/_icons.scss +10 -9
- package/scss/config/_tokens.scss +69 -47
- package/scss/config/mixins/_accordion.scss +2 -7
- package/scss/config/mixins/_alert.scss +4 -4
- package/scss/config/mixins/_breadcrumbs.scss +2 -2
- package/scss/config/mixins/_btn.scss +1 -1
- package/scss/config/mixins/_card.scss +89 -14
- package/scss/config/mixins/_collapsible.scss +3 -0
- package/scss/config/mixins/_colors.scss +1 -1
- package/scss/config/mixins/_diagnostics.scss +11 -0
- package/scss/config/mixins/_display.scss +1 -0
- package/scss/config/mixins/_dropdown.scss +3 -5
- package/scss/config/mixins/_editor.scss +124 -0
- package/scss/config/mixins/_empty-state.scss +7 -0
- package/scss/config/mixins/_filter.scss +3 -5
- package/scss/config/mixins/_footer.scss +1 -2
- package/scss/config/mixins/_form.scss +207 -21
- package/scss/config/mixins/_index.scss +2 -0
- package/scss/config/mixins/_ln-table.scss +41 -2
- package/scss/config/mixins/_modal.scss +149 -66
- package/scss/config/mixins/_nav.scss +3 -4
- package/scss/config/mixins/_popover.scss +39 -0
- package/scss/config/mixins/_sidebar.scss +1 -1
- package/scss/config/mixins/_sort.scss +42 -0
- package/scss/config/mixins/_table.scss +85 -60
- package/scss/config/mixins/_toast.scss +179 -152
- package/scss/config/mixins/_tooltip.scss +17 -7
- package/scss/config/mixins/_typography.scss +4 -2
- package/scss/ln-ashlar-dev.scss +27 -9
- package/scss/ln-ashlar.scss +7 -1
- package/js/ln-data-table/README.md +0 -110
- package/js/ln-data-table/ln-data-table.js +0 -1
- package/js/ln-data-table/ln-data-table.scss +0 -10
- package/js/ln-data-table/src/ln-data-table.js +0 -1087
- package/js/ln-icons/README.md +0 -88
- package/js/ln-icons/ln-icons.js +0 -1
- package/js/ln-table/ln-table-sort.js +0 -1
- package/js/ln-table/src/ln-table-sort.js +0 -156
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dispatch, getLocale, registerComponent, interceptValueProperty } from '../../ln-core';
|
|
1
|
+
import { dispatch, getLocale, registerComponent, interceptValueProperty, getLocaleFallback, registerLocaleFallback, buildDict } from '../../ln-core';
|
|
2
2
|
|
|
3
3
|
(function () {
|
|
4
4
|
const DOM_SELECTOR = 'data-ln-date';
|
|
@@ -50,11 +50,30 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
50
50
|
const hours = date.getHours();
|
|
51
51
|
const minutes = date.getMinutes();
|
|
52
52
|
|
|
53
|
+
let mmmmVal, mmmVal;
|
|
54
|
+
const fallback = getLocaleFallback(locale);
|
|
55
|
+
const langPrefix = (locale || '').toLowerCase().split('-')[0];
|
|
56
|
+
const formatter = _getFormatter(locale, { month: 'long' });
|
|
57
|
+
const resolvedLocale = formatter.resolvedOptions().locale.toLowerCase().split('-')[0];
|
|
58
|
+
const isFallbackNeeded = fallback && resolvedLocale !== langPrefix;
|
|
59
|
+
|
|
60
|
+
if (isFallbackNeeded && fallback.monthsLong) {
|
|
61
|
+
mmmmVal = fallback.monthsLong[month];
|
|
62
|
+
} else {
|
|
63
|
+
mmmmVal = _getFormatter(locale, { month: 'long' }).format(date);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (isFallbackNeeded && fallback.monthsShort) {
|
|
67
|
+
mmmVal = fallback.monthsShort[month];
|
|
68
|
+
} else {
|
|
69
|
+
mmmVal = _getFormatter(locale, { month: 'short' }).format(date);
|
|
70
|
+
}
|
|
71
|
+
|
|
53
72
|
const tokens = {
|
|
54
73
|
'yyyy': String(year),
|
|
55
74
|
'yy': String(year).slice(-2),
|
|
56
|
-
'MMMM':
|
|
57
|
-
'MMM':
|
|
75
|
+
'MMMM': mmmmVal,
|
|
76
|
+
'MMM': mmmVal,
|
|
58
77
|
'MM': String(month + 1).padStart(2, '0'),
|
|
59
78
|
'M': String(month + 1),
|
|
60
79
|
'dd': String(day).padStart(2, '0'),
|
|
@@ -71,29 +90,158 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
71
90
|
function _formatDate(date, format, locale) {
|
|
72
91
|
const intlOptions = _getIntlOptions(format);
|
|
73
92
|
if (intlOptions) {
|
|
74
|
-
|
|
93
|
+
const formatter = _getFormatter(locale, intlOptions);
|
|
94
|
+
const langPrefix = (locale || '').toLowerCase().split('-')[0];
|
|
95
|
+
const resolvedLocale = formatter.resolvedOptions().locale.toLowerCase().split('-')[0];
|
|
96
|
+
const fallback = getLocaleFallback(locale);
|
|
97
|
+
|
|
98
|
+
if (fallback && resolvedLocale !== langPrefix) {
|
|
99
|
+
return _formatCustom(date, 'dd.MM.yyyy', locale);
|
|
100
|
+
}
|
|
101
|
+
return formatter.format(date);
|
|
75
102
|
}
|
|
76
103
|
return _formatCustom(date, format, locale);
|
|
77
104
|
}
|
|
78
105
|
|
|
106
|
+
// ─── Component Helpers ────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
function _toISO(date) {
|
|
109
|
+
if (!date) return '';
|
|
110
|
+
const y = date.getFullYear();
|
|
111
|
+
const m = String(date.getMonth() + 1).padStart(2, '0');
|
|
112
|
+
const d = String(date.getDate()).padStart(2, '0');
|
|
113
|
+
return y + '-' + m + '-' + d;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function _notifyChange(self, iso, date) {
|
|
117
|
+
dispatch(self.dom, 'ln-date:change', {
|
|
118
|
+
value: iso,
|
|
119
|
+
formatted: self.dom.value,
|
|
120
|
+
date: date
|
|
121
|
+
});
|
|
122
|
+
self.dom.dispatchEvent(new Event('change', { bubbles: true }));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function _updateState(self, iso, date, formattedText) {
|
|
126
|
+
self._setHiddenRaw(iso);
|
|
127
|
+
_inputValueDesc.set.call(self._picker, iso);
|
|
128
|
+
self._lastISO = iso;
|
|
129
|
+
if (formattedText !== undefined) {
|
|
130
|
+
self._isFormatting = true;
|
|
131
|
+
self.dom.value = formattedText;
|
|
132
|
+
self._isFormatting = false;
|
|
133
|
+
} else if (date) {
|
|
134
|
+
self._displayFormatted(date);
|
|
135
|
+
}
|
|
136
|
+
_notifyChange(self, iso, date);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function _clearState(self) {
|
|
140
|
+
self._setHiddenRaw('');
|
|
141
|
+
_inputValueDesc.set.call(self._picker, '');
|
|
142
|
+
self._isFormatting = true;
|
|
143
|
+
self.dom.value = '';
|
|
144
|
+
self._isFormatting = false;
|
|
145
|
+
self._lastISO = '';
|
|
146
|
+
_notifyChange(self, '', null);
|
|
147
|
+
}
|
|
148
|
+
|
|
79
149
|
// ─── Component ────────────────────────────────────────────
|
|
80
150
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
151
|
+
// ─── Component ────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
_component.prototype._initTextElement = function () {
|
|
154
|
+
const dom = this.dom;
|
|
155
|
+
let valAttr = dom.getAttribute('data-ln-value');
|
|
156
|
+
let dateAttr = dom.getAttribute('data-ln-date');
|
|
157
|
+
let datetimeAttr = dom.getAttribute('datetime');
|
|
158
|
+
|
|
159
|
+
let candidate = null;
|
|
160
|
+
if (valAttr !== null && valAttr !== '') {
|
|
161
|
+
candidate = valAttr;
|
|
162
|
+
} else if (datetimeAttr !== null && datetimeAttr !== '') {
|
|
163
|
+
candidate = datetimeAttr;
|
|
164
|
+
} else if (dateAttr !== null && dateAttr !== '' && dateAttr !== 'true' && !KEYWORD_RE.test(dateAttr)) {
|
|
165
|
+
candidate = dateAttr;
|
|
166
|
+
} else {
|
|
167
|
+
candidate = dom.textContent.trim();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
let date = _parseISO(candidate) || _parseTyped(candidate);
|
|
171
|
+
if (!date && candidate) {
|
|
172
|
+
if (!isNaN(candidate)) {
|
|
173
|
+
const num = Number(candidate);
|
|
174
|
+
date = new Date(num > 1e11 ? num : num * 1000);
|
|
175
|
+
} else {
|
|
176
|
+
date = new Date(candidate);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (date && !isNaN(date.getTime())) {
|
|
180
|
+
const iso = _toISO(date);
|
|
181
|
+
this._rawValue = iso;
|
|
182
|
+
if (!dom.hasAttribute('data-ln-value')) {
|
|
183
|
+
dom.setAttribute('data-ln-value', iso);
|
|
184
|
+
}
|
|
185
|
+
this._formatTextContent();
|
|
186
|
+
} else {
|
|
187
|
+
this._rawValue = null;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
_component.prototype._formatTextContent = function () {
|
|
192
|
+
if (this._rawValue) {
|
|
193
|
+
const date = _parseISO(this._rawValue);
|
|
194
|
+
if (date) {
|
|
195
|
+
const formatAttr = this.dom.getAttribute('data-ln-date-format');
|
|
196
|
+
let format = formatAttr;
|
|
197
|
+
if (!format) {
|
|
198
|
+
const dateAttr = this.dom.getAttribute('data-ln-date');
|
|
199
|
+
if (dateAttr && KEYWORD_RE.test(dateAttr)) {
|
|
200
|
+
format = dateAttr;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const locale = getLocale(this.dom);
|
|
204
|
+
this.dom.textContent = _formatDate(date, format || 'medium', locale);
|
|
205
|
+
}
|
|
85
206
|
}
|
|
207
|
+
};
|
|
86
208
|
|
|
209
|
+
function _component(dom) {
|
|
87
210
|
if (dom[DOM_ATTRIBUTE]) return dom[DOM_ATTRIBUTE];
|
|
88
211
|
dom[DOM_ATTRIBUTE] = this;
|
|
89
212
|
|
|
90
213
|
this.dom = dom;
|
|
214
|
+
|
|
215
|
+
if (dom.tagName !== 'INPUT') {
|
|
216
|
+
this.isTextElement = true;
|
|
217
|
+
this._initTextElement();
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
this.isTextElement = false;
|
|
91
222
|
const self = this;
|
|
92
223
|
|
|
93
224
|
// ── Read initial state ──────────────────────────────
|
|
94
|
-
const initialValue = dom.value;
|
|
225
|
+
const initialValue = dom.value;
|
|
95
226
|
const name = dom.name;
|
|
96
227
|
|
|
228
|
+
// Check for declarative HTML dictionary nearby
|
|
229
|
+
const container = dom.closest('.form-element, form') || dom.parentNode;
|
|
230
|
+
const dictEls = container.querySelectorAll('[data-ln-date-dict]');
|
|
231
|
+
for (let i = 0; i < dictEls.length; i++) {
|
|
232
|
+
const lang = dictEls[i].getAttribute('data-ln-date-dict');
|
|
233
|
+
if (lang) {
|
|
234
|
+
const dictData = buildDict(dictEls[i], 'data-ln-date-dict-key');
|
|
235
|
+
if (dictData['months-long']) {
|
|
236
|
+
dictData.monthsLong = dictData['months-long'].split(',').map(s => s.trim());
|
|
237
|
+
}
|
|
238
|
+
if (dictData['months-short']) {
|
|
239
|
+
dictData.monthsShort = dictData['months-short'].split(',').map(s => s.trim());
|
|
240
|
+
}
|
|
241
|
+
registerLocaleFallback(lang, dictData);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
97
245
|
// ── Wrap field: replace dom with <span data-ln-date-field> and move dom inside ──
|
|
98
246
|
const wrapper = document.createElement('span');
|
|
99
247
|
wrapper.setAttribute('data-ln-date-field', '');
|
|
@@ -106,6 +254,9 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
106
254
|
hidden.type = 'hidden';
|
|
107
255
|
hidden.name = name;
|
|
108
256
|
dom.removeAttribute('name');
|
|
257
|
+
if (dom.hasAttribute('data-ln-fill-as')) {
|
|
258
|
+
hidden.setAttribute('data-ln-fill-as', dom.getAttribute('data-ln-fill-as'));
|
|
259
|
+
}
|
|
109
260
|
dom.insertAdjacentElement('afterend', hidden);
|
|
110
261
|
this._hidden = hidden;
|
|
111
262
|
|
|
@@ -123,13 +274,13 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
123
274
|
// ── Create calendar button ──────────────────────────
|
|
124
275
|
const btn = document.createElement('button');
|
|
125
276
|
btn.type = 'button';
|
|
126
|
-
btn.setAttribute('aria-label', 'Open date picker');
|
|
127
|
-
btn.innerHTML = '<svg class="ln-icon" aria-hidden="true"><use href="#ln-calendar"></use></svg>';
|
|
277
|
+
btn.setAttribute('aria-label', dom.getAttribute('data-ln-date-label') || 'Open date picker');
|
|
278
|
+
btn.innerHTML = '<svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-calendar"></use></svg>';
|
|
128
279
|
picker.insertAdjacentElement('afterend', btn);
|
|
129
280
|
this._btn = btn;
|
|
130
281
|
this._lastISO = '';
|
|
131
282
|
|
|
132
|
-
// ── Intercept programmatic value sets on hidden input
|
|
283
|
+
// ── Intercept programmatic value sets on hidden input ──
|
|
133
284
|
Object.defineProperty(hidden, 'value', {
|
|
134
285
|
get: function () {
|
|
135
286
|
return _inputValueDesc.get.call(hidden);
|
|
@@ -138,27 +289,9 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
138
289
|
_inputValueDesc.set.call(hidden, val);
|
|
139
290
|
if (val && val !== '') {
|
|
140
291
|
const date = _parseISO(val);
|
|
141
|
-
if (date)
|
|
142
|
-
self._displayFormatted(date);
|
|
143
|
-
_inputValueDesc.set.call(picker, val);
|
|
144
|
-
self._lastISO = val;
|
|
145
|
-
dispatch(self.dom, 'ln-date:change', {
|
|
146
|
-
value: val,
|
|
147
|
-
formatted: self.dom.value,
|
|
148
|
-
date: date
|
|
149
|
-
});
|
|
150
|
-
self.dom.dispatchEvent(new Event('change', { bubbles: true }));
|
|
151
|
-
}
|
|
292
|
+
if (date) _updateState(self, val, date);
|
|
152
293
|
} else if (val === '') {
|
|
153
|
-
self
|
|
154
|
-
_inputValueDesc.set.call(picker, '');
|
|
155
|
-
self._lastISO = '';
|
|
156
|
-
dispatch(self.dom, 'ln-date:change', {
|
|
157
|
-
value: '',
|
|
158
|
-
formatted: '',
|
|
159
|
-
date: null
|
|
160
|
-
});
|
|
161
|
-
self.dom.dispatchEvent(new Event('change', { bubbles: true }));
|
|
294
|
+
_clearState(self);
|
|
162
295
|
}
|
|
163
296
|
}
|
|
164
297
|
});
|
|
@@ -175,84 +308,34 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
175
308
|
}
|
|
176
309
|
if (!val || val === '') {
|
|
177
310
|
originalSet('');
|
|
178
|
-
self
|
|
179
|
-
_inputValueDesc.set.call(self._picker, '');
|
|
180
|
-
self._lastISO = '';
|
|
181
|
-
dispatch(dom, 'ln-date:change', {
|
|
182
|
-
value: '',
|
|
183
|
-
formatted: '',
|
|
184
|
-
date: null
|
|
185
|
-
});
|
|
186
|
-
dom.dispatchEvent(new Event('change', { bubbles: true }));
|
|
311
|
+
_clearState(self);
|
|
187
312
|
return;
|
|
188
313
|
}
|
|
189
314
|
|
|
190
|
-
|
|
191
|
-
let date = _parseISO(val);
|
|
192
|
-
if (!date) {
|
|
193
|
-
date = _parseTyped(val);
|
|
194
|
-
}
|
|
315
|
+
const date = _parseISO(val) || _parseTyped(val);
|
|
195
316
|
|
|
196
317
|
if (date) {
|
|
197
|
-
const
|
|
198
|
-
const m = String(date.getMonth() + 1).padStart(2, '0');
|
|
199
|
-
const d = String(date.getDate()).padStart(2, '0');
|
|
200
|
-
const iso = y + '-' + m + '-' + d;
|
|
201
|
-
|
|
202
|
-
self._setHiddenRaw(iso);
|
|
203
|
-
_inputValueDesc.set.call(self._picker, iso);
|
|
204
|
-
self._lastISO = iso;
|
|
205
|
-
|
|
318
|
+
const iso = _toISO(date);
|
|
206
319
|
const format = dom.getAttribute(DOM_SELECTOR) || '';
|
|
207
320
|
const locale = getLocale(dom);
|
|
208
321
|
const formatted = _formatDate(date, format, locale);
|
|
209
322
|
originalSet(formatted);
|
|
210
|
-
|
|
211
|
-
dispatch(dom, 'ln-date:change', {
|
|
212
|
-
value: iso,
|
|
213
|
-
formatted: formatted,
|
|
214
|
-
date: date
|
|
215
|
-
});
|
|
216
|
-
dom.dispatchEvent(new Event('change', { bubbles: true }));
|
|
323
|
+
_updateState(self, iso, date, formatted);
|
|
217
324
|
} else {
|
|
218
325
|
originalSet(String(val));
|
|
219
|
-
self
|
|
220
|
-
_inputValueDesc.set.call(self._picker, '');
|
|
221
|
-
self._lastISO = '';
|
|
222
|
-
dispatch(dom, 'ln-date:change', {
|
|
223
|
-
value: '',
|
|
224
|
-
formatted: String(val),
|
|
225
|
-
date: null
|
|
226
|
-
});
|
|
227
|
-
dom.dispatchEvent(new Event('change', { bubbles: true }));
|
|
326
|
+
_clearState(self);
|
|
228
327
|
}
|
|
229
328
|
}
|
|
230
329
|
});
|
|
231
330
|
|
|
232
331
|
// ── Bind events ─────────────────────────────────────
|
|
233
332
|
this._onPickerChange = function () {
|
|
234
|
-
const val = picker.value;
|
|
333
|
+
const val = picker.value;
|
|
235
334
|
if (val) {
|
|
236
335
|
const date = _parseISO(val);
|
|
237
|
-
if (date)
|
|
238
|
-
self._setHiddenRaw(val);
|
|
239
|
-
self._displayFormatted(date);
|
|
240
|
-
self._lastISO = val;
|
|
241
|
-
dispatch(self.dom, 'ln-date:change', {
|
|
242
|
-
value: val,
|
|
243
|
-
formatted: self.dom.value,
|
|
244
|
-
date: date
|
|
245
|
-
});
|
|
246
|
-
}
|
|
336
|
+
if (date) _updateState(self, val, date);
|
|
247
337
|
} else {
|
|
248
|
-
self
|
|
249
|
-
self.dom.value = '';
|
|
250
|
-
self._lastISO = '';
|
|
251
|
-
dispatch(self.dom, 'ln-date:change', {
|
|
252
|
-
value: '',
|
|
253
|
-
formatted: '',
|
|
254
|
-
date: null
|
|
255
|
-
});
|
|
338
|
+
_clearState(self);
|
|
256
339
|
}
|
|
257
340
|
};
|
|
258
341
|
picker.addEventListener('change', this._onPickerChange);
|
|
@@ -260,51 +343,25 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
260
343
|
this._onBlur = function () {
|
|
261
344
|
const typed = self.dom.value.trim();
|
|
262
345
|
|
|
263
|
-
// Empty input — clear if there was a value
|
|
264
346
|
if (typed === '') {
|
|
265
|
-
if (self._lastISO !== '')
|
|
266
|
-
self._setHiddenRaw('');
|
|
267
|
-
_inputValueDesc.set.call(self._picker, '');
|
|
268
|
-
self.dom.value = '';
|
|
269
|
-
self._lastISO = '';
|
|
270
|
-
dispatch(self.dom, 'ln-date:change', {
|
|
271
|
-
value: '',
|
|
272
|
-
formatted: '',
|
|
273
|
-
date: null
|
|
274
|
-
});
|
|
275
|
-
}
|
|
347
|
+
if (self._lastISO !== '') _clearState(self);
|
|
276
348
|
return;
|
|
277
349
|
}
|
|
278
350
|
|
|
279
|
-
// Check if text is unchanged from current formatted display
|
|
280
351
|
if (self._lastISO) {
|
|
281
352
|
const currentDate = _parseISO(self._lastISO);
|
|
282
353
|
if (currentDate) {
|
|
283
354
|
const format = self.dom.getAttribute(DOM_SELECTOR) || '';
|
|
284
355
|
const locale = getLocale(self.dom);
|
|
285
|
-
|
|
286
|
-
if (typed === currentFormatted) return;
|
|
356
|
+
if (typed === _formatDate(currentDate, format, locale)) return;
|
|
287
357
|
}
|
|
288
358
|
}
|
|
289
359
|
|
|
290
|
-
// Try to parse the typed value
|
|
291
360
|
const parsed = _parseTyped(typed);
|
|
292
361
|
if (parsed) {
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
const d = String(parsed.getDate()).padStart(2, '0');
|
|
296
|
-
const iso = y + '-' + m + '-' + d;
|
|
297
|
-
self._setHiddenRaw(iso);
|
|
298
|
-
_inputValueDesc.set.call(self._picker, iso);
|
|
299
|
-
self._displayFormatted(parsed);
|
|
300
|
-
self._lastISO = iso;
|
|
301
|
-
dispatch(self.dom, 'ln-date:change', {
|
|
302
|
-
value: iso,
|
|
303
|
-
formatted: self.dom.value,
|
|
304
|
-
date: parsed
|
|
305
|
-
});
|
|
362
|
+
const iso = _toISO(parsed);
|
|
363
|
+
_updateState(self, iso, parsed);
|
|
306
364
|
} else {
|
|
307
|
-
// Invalid input — revert to previous display
|
|
308
365
|
if (self._lastISO) {
|
|
309
366
|
const prevDate = _parseISO(self._lastISO);
|
|
310
367
|
if (prevDate) self._displayFormatted(prevDate);
|
|
@@ -323,27 +380,15 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
323
380
|
// ── Handle pre-filled value ─────────────────────────
|
|
324
381
|
if (initialValue && initialValue !== '') {
|
|
325
382
|
const date = _parseISO(initialValue);
|
|
326
|
-
if (date)
|
|
327
|
-
this._setHiddenRaw(initialValue);
|
|
328
|
-
_inputValueDesc.set.call(picker, initialValue);
|
|
329
|
-
this._displayFormatted(date);
|
|
330
|
-
this._lastISO = initialValue;
|
|
331
|
-
dispatch(dom, 'ln-date:change', {
|
|
332
|
-
value: initialValue,
|
|
333
|
-
formatted: dom.value,
|
|
334
|
-
date: date
|
|
335
|
-
});
|
|
336
|
-
dom.dispatchEvent(new Event('change', { bubbles: true }));
|
|
337
|
-
}
|
|
383
|
+
if (date) _updateState(self, initialValue, date);
|
|
338
384
|
}
|
|
339
385
|
|
|
340
386
|
return this;
|
|
341
387
|
}
|
|
342
388
|
|
|
343
|
-
// ─── Helpers
|
|
389
|
+
// ─── Parsing Helpers ──────────────────────────────────────
|
|
344
390
|
|
|
345
391
|
function _parseISO(str) {
|
|
346
|
-
// Parse YYYY-MM-DD (and optionally YYYY-MM-DDTHH:mm)
|
|
347
392
|
if (!str || typeof str !== 'string') return null;
|
|
348
393
|
const parts = str.split('T');
|
|
349
394
|
const dateParts = parts[0].split('-');
|
|
@@ -359,7 +404,6 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
359
404
|
min = parseInt(timeParts[1], 10) || 0;
|
|
360
405
|
}
|
|
361
406
|
const date = new Date(y, m, d, h, min);
|
|
362
|
-
// Validate the date is real (e.g., Feb 30 would roll over)
|
|
363
407
|
if (date.getFullYear() !== y || date.getMonth() !== m || date.getDate() !== d) return null;
|
|
364
408
|
return date;
|
|
365
409
|
}
|
|
@@ -367,19 +411,15 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
367
411
|
function _parseTyped(str) {
|
|
368
412
|
if (!str || typeof str !== 'string') return null;
|
|
369
413
|
str = str.trim();
|
|
370
|
-
if (str.length < 6) return null;
|
|
414
|
+
if (str.length < 6) return null;
|
|
371
415
|
|
|
372
|
-
// Detect separator
|
|
373
416
|
let sep, parts;
|
|
374
417
|
if (str.indexOf('.') !== -1) {
|
|
375
|
-
sep = '.';
|
|
376
|
-
parts = str.split('.');
|
|
418
|
+
sep = '.'; parts = str.split('.');
|
|
377
419
|
} else if (str.indexOf('/') !== -1) {
|
|
378
|
-
sep = '/';
|
|
379
|
-
parts = str.split('/');
|
|
420
|
+
sep = '/'; parts = str.split('/');
|
|
380
421
|
} else if (str.indexOf('-') !== -1) {
|
|
381
|
-
sep = '-';
|
|
382
|
-
parts = str.split('-');
|
|
422
|
+
sep = '-'; parts = str.split('-');
|
|
383
423
|
} else {
|
|
384
424
|
return null;
|
|
385
425
|
}
|
|
@@ -393,15 +433,11 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
393
433
|
}
|
|
394
434
|
|
|
395
435
|
let day, month, year;
|
|
396
|
-
|
|
397
436
|
if (sep === '.') {
|
|
398
|
-
// dd.MM.yyyy (European)
|
|
399
437
|
day = nums[0]; month = nums[1]; year = nums[2];
|
|
400
438
|
} else if (sep === '/') {
|
|
401
|
-
// MM/dd/yyyy (US)
|
|
402
439
|
month = nums[0]; day = nums[1]; year = nums[2];
|
|
403
440
|
} else {
|
|
404
|
-
// dash: yyyy-MM-dd if first part is 4 digits, else dd-MM-yyyy
|
|
405
441
|
if (parts[0].length === 4) {
|
|
406
442
|
year = nums[0]; month = nums[1]; day = nums[2];
|
|
407
443
|
} else {
|
|
@@ -409,12 +445,10 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
409
445
|
}
|
|
410
446
|
}
|
|
411
447
|
|
|
412
|
-
// Two-digit year pivot
|
|
413
448
|
if (year < 100) {
|
|
414
449
|
year += (year < 50) ? 2000 : 1900;
|
|
415
450
|
}
|
|
416
451
|
|
|
417
|
-
// Validate via Date constructor roundtrip
|
|
418
452
|
const date = new Date(year, month - 1, day);
|
|
419
453
|
if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
|
|
420
454
|
return null;
|
|
@@ -427,7 +461,6 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
427
461
|
try {
|
|
428
462
|
this._picker.showPicker();
|
|
429
463
|
} catch (e) {
|
|
430
|
-
// showPicker() can throw if not triggered by user gesture
|
|
431
464
|
this._picker.click();
|
|
432
465
|
}
|
|
433
466
|
} else {
|
|
@@ -451,27 +484,34 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
451
484
|
|
|
452
485
|
Object.defineProperty(_component.prototype, 'value', {
|
|
453
486
|
get: function () {
|
|
487
|
+
if (this.isTextElement) {
|
|
488
|
+
return this._rawValue || '';
|
|
489
|
+
}
|
|
454
490
|
return _inputValueDesc.get.call(this._hidden);
|
|
455
491
|
},
|
|
456
492
|
set: function (isoStr) {
|
|
493
|
+
if (this.isTextElement) {
|
|
494
|
+
if (!isoStr || isoStr === '') {
|
|
495
|
+
this._rawValue = null;
|
|
496
|
+
this.dom.removeAttribute('data-ln-value');
|
|
497
|
+
this.dom.textContent = '';
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const date = _parseISO(isoStr) || _parseTyped(isoStr);
|
|
501
|
+
if (!date) return;
|
|
502
|
+
const iso = _toISO(date);
|
|
503
|
+
this._rawValue = iso;
|
|
504
|
+
this.dom.setAttribute('data-ln-value', iso);
|
|
505
|
+
this._formatTextContent();
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
457
508
|
if (!isoStr || isoStr === '') {
|
|
458
|
-
this
|
|
459
|
-
_inputValueDesc.set.call(this._picker, '');
|
|
460
|
-
this.dom.value = '';
|
|
461
|
-
this._lastISO = '';
|
|
509
|
+
_clearState(this);
|
|
462
510
|
return;
|
|
463
511
|
}
|
|
464
512
|
const date = _parseISO(isoStr);
|
|
465
513
|
if (!date) return;
|
|
466
|
-
this
|
|
467
|
-
_inputValueDesc.set.call(this._picker, isoStr);
|
|
468
|
-
this._displayFormatted(date);
|
|
469
|
-
this._lastISO = isoStr;
|
|
470
|
-
dispatch(this.dom, 'ln-date:change', {
|
|
471
|
-
value: isoStr,
|
|
472
|
-
formatted: this.dom.value,
|
|
473
|
-
date: date
|
|
474
|
-
});
|
|
514
|
+
_updateState(this, isoStr, date);
|
|
475
515
|
}
|
|
476
516
|
});
|
|
477
517
|
|
|
@@ -486,38 +526,40 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
486
526
|
this.value = '';
|
|
487
527
|
return;
|
|
488
528
|
}
|
|
489
|
-
|
|
490
|
-
const m = String(dateObj.getMonth() + 1).padStart(2, '0');
|
|
491
|
-
const d = String(dateObj.getDate()).padStart(2, '0');
|
|
492
|
-
this.value = y + '-' + m + '-' + d;
|
|
529
|
+
this.value = _toISO(dateObj);
|
|
493
530
|
}
|
|
494
531
|
});
|
|
495
532
|
|
|
496
533
|
Object.defineProperty(_component.prototype, 'formatted', {
|
|
497
534
|
get: function () {
|
|
535
|
+
if (this.isTextElement) {
|
|
536
|
+
return this.dom.textContent;
|
|
537
|
+
}
|
|
498
538
|
return this.dom.value;
|
|
499
539
|
}
|
|
500
540
|
});
|
|
501
541
|
|
|
502
542
|
_component.prototype.destroy = function () {
|
|
503
543
|
if (!this.dom[DOM_ATTRIBUTE]) return;
|
|
544
|
+
if (this.isTextElement) {
|
|
545
|
+
dispatch(this.dom, 'ln-date:destroyed', { target: this.dom });
|
|
546
|
+
delete this.dom[DOM_ATTRIBUTE];
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
504
549
|
this._picker.removeEventListener('change', this._onPickerChange);
|
|
505
550
|
this.dom.removeEventListener('blur', this._onBlur);
|
|
506
551
|
this._btn.removeEventListener('click', this._onBtnClick);
|
|
507
|
-
// Restore original input
|
|
508
|
-
this.dom.name = this._hidden.name;
|
|
509
|
-
this.dom.type = 'date';
|
|
510
552
|
const isoVal = this.value;
|
|
511
|
-
// Remove created elements
|
|
512
553
|
this._hidden.remove();
|
|
513
554
|
this._picker.remove();
|
|
514
555
|
this._btn.remove();
|
|
515
|
-
// Unwrap: move dom back out and drop the wrapper
|
|
516
556
|
if (this._wrapper && this._wrapper.parentNode) {
|
|
517
557
|
this._wrapper.parentNode.insertBefore(this.dom, this._wrapper);
|
|
518
558
|
this._wrapper.remove();
|
|
519
559
|
}
|
|
520
|
-
|
|
560
|
+
delete this.dom.value;
|
|
561
|
+
this.dom.name = this._hidden.name;
|
|
562
|
+
this.dom.type = 'date';
|
|
521
563
|
if (isoVal) this.dom.value = isoVal;
|
|
522
564
|
dispatch(this.dom, 'ln-date:destroyed', { target: this.dom });
|
|
523
565
|
delete this.dom[DOM_ATTRIBUTE];
|
|
@@ -530,16 +572,32 @@ import { dispatch, getLocale, registerComponent, interceptValueProperty } from '
|
|
|
530
572
|
const els = document.querySelectorAll('[' + DOM_SELECTOR + ']');
|
|
531
573
|
for (let i = 0; i < els.length; i++) {
|
|
532
574
|
const inst = els[i][DOM_ATTRIBUTE];
|
|
533
|
-
if (inst
|
|
534
|
-
|
|
535
|
-
|
|
575
|
+
if (inst) {
|
|
576
|
+
if (inst.isTextElement) {
|
|
577
|
+
inst._formatTextContent();
|
|
578
|
+
} else if (inst.value) {
|
|
579
|
+
const date = _parseISO(inst.value);
|
|
580
|
+
if (date) inst._displayFormatted(date);
|
|
581
|
+
}
|
|
536
582
|
}
|
|
537
583
|
}
|
|
538
|
-
}).observe(document.documentElement, { attributes: true, attributeFilter: ['lang'] });
|
|
584
|
+
}).observe(document.documentElement, { attributes: true, attributeFilter: ['lang'], subtree: true });
|
|
539
585
|
}
|
|
540
586
|
|
|
541
587
|
// ─── Init ─────────────────────────────────────────────────
|
|
542
588
|
|
|
543
|
-
registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _component, 'ln-date'
|
|
589
|
+
registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _component, 'ln-date', {
|
|
590
|
+
extraAttributes: ['data-ln-date-format', 'data-ln-date-locale', 'data-ln-value', 'datetime', 'lang'],
|
|
591
|
+
onAttributeChange: function (el) {
|
|
592
|
+
const inst = el[DOM_ATTRIBUTE];
|
|
593
|
+
if (!inst) return;
|
|
594
|
+
if (inst.isTextElement) {
|
|
595
|
+
inst._initTextElement();
|
|
596
|
+
} else if (inst.value) {
|
|
597
|
+
const date = _parseISO(inst.value);
|
|
598
|
+
if (date) inst._displayFormatted(date);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
});
|
|
544
602
|
_localeObserver();
|
|
545
603
|
})();
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# ln-debug
|
|
2
|
+
|
|
3
|
+
> The diagnostic component of `ln-ashlar` that enables verbose developer warnings in console logs during development.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Philosophy
|
|
8
|
+
|
|
9
|
+
In production, libraries should remain silent and not pollute the browser console with developer-oriented warnings (such as template mismatch warnings or missing configuration parameters).
|
|
10
|
+
|
|
11
|
+
The `ln-debug` component solves this by acting as a global switch:
|
|
12
|
+
- When active, all core validation warnings (prefixed with `[ln-` or `[lnCore`) are enabled.
|
|
13
|
+
- When inactive, these developer warnings are safely suppressed, maintaining a clean production log.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 2. Minimal Blueprint
|
|
18
|
+
|
|
19
|
+
Add the `data-ln-debug` attribute to either the `<html>` or `<body>` element (or any element in the document) to enable debug logs:
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<!DOCTYPE html>
|
|
23
|
+
<html lang="en" data-ln-debug>
|
|
24
|
+
<head>
|
|
25
|
+
<script src="dist/ln-ashlar.iife.js" defer></script>
|
|
26
|
+
</head>
|
|
27
|
+
<body>
|
|
28
|
+
<!-- Warnings will be visible in the developer console -->
|
|
29
|
+
</body>
|
|
30
|
+
</html>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 3. DOM & State Management
|
|
36
|
+
|
|
37
|
+
This component lives entirely at the level of the DOM. The single source of truth is the presence of the `data-ln-debug` attribute on `<html>` or `<body>`.
|
|
38
|
+
|
|
39
|
+
### Checking the State via JS
|
|
40
|
+
|
|
41
|
+
To check if debug mode is active in your scripts, query the DOM directly:
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
const isDebug = document.documentElement.hasAttribute('data-ln-debug') ||
|
|
45
|
+
(document.body && document.body.hasAttribute('data-ln-debug'));
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Toggling the State
|
|
49
|
+
|
|
50
|
+
To toggle debug mode at runtime, add or remove the attribute from the DOM:
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
// Enable
|
|
54
|
+
document.documentElement.setAttribute('data-ln-debug', '');
|
|
55
|
+
|
|
56
|
+
// Disable
|
|
57
|
+
document.documentElement.removeAttribute('data-ln-debug');
|
|
58
|
+
```
|