@livenetworks/ashlar 1.3.2

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 (232) hide show
  1. package/README.md +177 -0
  2. package/js/COMPONENTS.md +1102 -0
  3. package/js/index.js +41 -0
  4. package/js/ln-accordion/README.md +137 -0
  5. package/js/ln-accordion/ln-accordion.js +1 -0
  6. package/js/ln-accordion/src/ln-accordion.js +41 -0
  7. package/js/ln-ajax/README.md +91 -0
  8. package/js/ln-ajax/ln-ajax.js +1 -0
  9. package/js/ln-ajax/src/ln-ajax.js +277 -0
  10. package/js/ln-api-connector/README.md +150 -0
  11. package/js/ln-api-connector/ln-api-connector.js +1 -0
  12. package/js/ln-api-connector/src/ln-api-connector.js +265 -0
  13. package/js/ln-autoresize/README.md +80 -0
  14. package/js/ln-autoresize/ln-autoresize.js +1 -0
  15. package/js/ln-autoresize/src/ln-autoresize.js +47 -0
  16. package/js/ln-autosave/README.md +92 -0
  17. package/js/ln-autosave/ln-autosave.js +1 -0
  18. package/js/ln-autosave/src/ln-autosave.js +147 -0
  19. package/js/ln-circular-progress/README.md +161 -0
  20. package/js/ln-circular-progress/ln-circular-progress.js +1 -0
  21. package/js/ln-circular-progress/src/ln-circular-progress.js +133 -0
  22. package/js/ln-confirm/README.md +86 -0
  23. package/js/ln-confirm/_ln-confirm.scss +13 -0
  24. package/js/ln-confirm/ln-confirm.js +1 -0
  25. package/js/ln-confirm/src/ln-confirm.js +131 -0
  26. package/js/ln-core/crypto.js +83 -0
  27. package/js/ln-core/helpers.js +411 -0
  28. package/js/ln-core/index.js +5 -0
  29. package/js/ln-core/persist.js +71 -0
  30. package/js/ln-core/positioning.js +207 -0
  31. package/js/ln-core/reactive.js +74 -0
  32. package/js/ln-couchdb-connector/README.md +156 -0
  33. package/js/ln-couchdb-connector/ln-couchdb-connector.js +1 -0
  34. package/js/ln-couchdb-connector/src/ln-couchdb-connector.js +348 -0
  35. package/js/ln-data-coordinator/README.md +165 -0
  36. package/js/ln-data-coordinator/ln-data-coordinator.js +1 -0
  37. package/js/ln-data-coordinator/src/ln-data-coordinator.js +249 -0
  38. package/js/ln-data-store/README.md +94 -0
  39. package/js/ln-data-store/ln-data-store.js +1 -0
  40. package/js/ln-data-store/src/ln-data-store.js +699 -0
  41. package/js/ln-data-table/README.md +110 -0
  42. package/js/ln-data-table/ln-data-table.js +1 -0
  43. package/js/ln-data-table/ln-data-table.scss +10 -0
  44. package/js/ln-data-table/src/ln-data-table.js +1103 -0
  45. package/js/ln-date/README.md +151 -0
  46. package/js/ln-date/ln-date.js +1 -0
  47. package/js/ln-date/src/ln-date.js +442 -0
  48. package/js/ln-dropdown/README.md +117 -0
  49. package/js/ln-dropdown/ln-dropdown.js +1 -0
  50. package/js/ln-dropdown/ln-dropdown.scss +15 -0
  51. package/js/ln-dropdown/src/ln-dropdown.js +174 -0
  52. package/js/ln-external-links/README.md +341 -0
  53. package/js/ln-external-links/ln-external-links.js +1 -0
  54. package/js/ln-external-links/src/ln-external-links.js +116 -0
  55. package/js/ln-filter/README.md +99 -0
  56. package/js/ln-filter/ln-filter.js +1 -0
  57. package/js/ln-filter/ln-filter.scss +7 -0
  58. package/js/ln-filter/src/ln-filter.js +404 -0
  59. package/js/ln-form/README.md +101 -0
  60. package/js/ln-form/ln-form.js +1 -0
  61. package/js/ln-form/src/ln-form.js +199 -0
  62. package/js/ln-http/README.md +89 -0
  63. package/js/ln-http/ln-http.js +1 -0
  64. package/js/ln-http/src/ln-http.js +219 -0
  65. package/js/ln-icons/README.md +88 -0
  66. package/js/ln-icons/ln-icons.js +1 -0
  67. package/js/ln-icons/src/ln-icons.js +169 -0
  68. package/js/ln-link/README.md +303 -0
  69. package/js/ln-link/ln-link.js +1 -0
  70. package/js/ln-link/src/ln-link.js +196 -0
  71. package/js/ln-modal/README.md +154 -0
  72. package/js/ln-modal/ln-modal.js +1 -0
  73. package/js/ln-modal/ln-modal.scss +11 -0
  74. package/js/ln-modal/src/ln-modal.js +201 -0
  75. package/js/ln-nav/README.md +70 -0
  76. package/js/ln-nav/ln-nav.js +1 -0
  77. package/js/ln-nav/src/ln-nav.js +177 -0
  78. package/js/ln-number/README.md +122 -0
  79. package/js/ln-number/ln-number.js +1 -0
  80. package/js/ln-number/src/ln-number.js +302 -0
  81. package/js/ln-popover/README.md +127 -0
  82. package/js/ln-popover/ln-popover.js +1 -0
  83. package/js/ln-popover/src/ln-popover.js +288 -0
  84. package/js/ln-progress/README.md +442 -0
  85. package/js/ln-progress/ln-progress.js +1 -0
  86. package/js/ln-progress/src/ln-progress.js +150 -0
  87. package/js/ln-search/README.md +83 -0
  88. package/js/ln-search/ln-search.js +1 -0
  89. package/js/ln-search/ln-search.scss +7 -0
  90. package/js/ln-search/src/ln-search.js +114 -0
  91. package/js/ln-sortable/README.md +95 -0
  92. package/js/ln-sortable/ln-sortable.js +1 -0
  93. package/js/ln-sortable/src/ln-sortable.js +203 -0
  94. package/js/ln-table/README.md +101 -0
  95. package/js/ln-table/ln-table-sort.js +1 -0
  96. package/js/ln-table/ln-table.js +1 -0
  97. package/js/ln-table/ln-table.scss +11 -0
  98. package/js/ln-table/src/ln-table-sort.js +168 -0
  99. package/js/ln-table/src/ln-table.js +473 -0
  100. package/js/ln-tabs/README.md +137 -0
  101. package/js/ln-tabs/ln-tabs.js +1 -0
  102. package/js/ln-tabs/src/ln-tabs.js +171 -0
  103. package/js/ln-time/README.md +81 -0
  104. package/js/ln-time/ln-time.js +1 -0
  105. package/js/ln-time/src/ln-time.js +192 -0
  106. package/js/ln-toast/README.md +122 -0
  107. package/js/ln-toast/ln-toast.js +15 -0
  108. package/js/ln-toast/src/ln-toast.js +210 -0
  109. package/js/ln-toast/template.html +14 -0
  110. package/js/ln-toggle/README.md +137 -0
  111. package/js/ln-toggle/ln-toggle.js +1 -0
  112. package/js/ln-toggle/src/ln-toggle.js +139 -0
  113. package/js/ln-tooltip/README.md +58 -0
  114. package/js/ln-tooltip/ln-tooltip.js +1 -0
  115. package/js/ln-tooltip/ln-tooltip.scss +9 -0
  116. package/js/ln-tooltip/src/ln-tooltip.js +169 -0
  117. package/js/ln-translations/README.md +96 -0
  118. package/js/ln-translations/ln-translations.js +1 -0
  119. package/js/ln-translations/src/ln-translations.js +275 -0
  120. package/js/ln-upload/README.md +180 -0
  121. package/js/ln-upload/ln-upload.js +1 -0
  122. package/js/ln-upload/ln-upload.scss +20 -0
  123. package/js/ln-upload/src/ln-upload.js +407 -0
  124. package/js/ln-validate/README.md +108 -0
  125. package/js/ln-validate/ln-validate.js +1 -0
  126. package/js/ln-validate/src/ln-validate.js +160 -0
  127. package/package.json +55 -0
  128. package/scss/base/_global.scss +83 -0
  129. package/scss/base/_reset.scss +17 -0
  130. package/scss/base/_typography.scss +125 -0
  131. package/scss/components/_accordion.scss +34 -0
  132. package/scss/components/_ajax.scss +15 -0
  133. package/scss/components/_alert.scss +5 -0
  134. package/scss/components/_app-shell.scss +15 -0
  135. package/scss/components/_avatar.scss +6 -0
  136. package/scss/components/_breadcrumbs.scss +33 -0
  137. package/scss/components/_button.scss +20 -0
  138. package/scss/components/_card.scss +10 -0
  139. package/scss/components/_chip.scss +5 -0
  140. package/scss/components/_circular-progress.scss +29 -0
  141. package/scss/components/_confirm.scss +5 -0
  142. package/scss/components/_data-table.scss +83 -0
  143. package/scss/components/_dropdown.scss +25 -0
  144. package/scss/components/_empty-state.scss +22 -0
  145. package/scss/components/_form.scss +100 -0
  146. package/scss/components/_layout.scss +8 -0
  147. package/scss/components/_link.scss +11 -0
  148. package/scss/components/_ln-table.scss +60 -0
  149. package/scss/components/_loader.scss +6 -0
  150. package/scss/components/_modal.scss +20 -0
  151. package/scss/components/_nav.scss +9 -0
  152. package/scss/components/_page-header.scss +10 -0
  153. package/scss/components/_popover.scss +10 -0
  154. package/scss/components/_progress.scss +17 -0
  155. package/scss/components/_prose.scss +5 -0
  156. package/scss/components/_scrollbar.scss +32 -0
  157. package/scss/components/_sections.scss +12 -0
  158. package/scss/components/_sidebar.scss +5 -0
  159. package/scss/components/_stat-card.scss +5 -0
  160. package/scss/components/_status-badge.scss +4 -0
  161. package/scss/components/_stepper.scss +5 -0
  162. package/scss/components/_table.scss +19 -0
  163. package/scss/components/_tabs.scss +21 -0
  164. package/scss/components/_timeline.scss +14 -0
  165. package/scss/components/_toast.scss +41 -0
  166. package/scss/components/_toggle.scss +81 -0
  167. package/scss/components/_tooltip.scss +18 -0
  168. package/scss/components/_translations.scss +111 -0
  169. package/scss/components/_upload.scss +51 -0
  170. package/scss/config/_breakpoints.scss +72 -0
  171. package/scss/config/_density.scss +117 -0
  172. package/scss/config/_icons.scss +37 -0
  173. package/scss/config/_mixins.scss +13 -0
  174. package/scss/config/_theme.scss +216 -0
  175. package/scss/config/_tokens.scss +419 -0
  176. package/scss/config/mixins/_accordion.scss +52 -0
  177. package/scss/config/mixins/_ajax.scss +39 -0
  178. package/scss/config/mixins/_alert.scss +82 -0
  179. package/scss/config/mixins/_app-shell.scss +312 -0
  180. package/scss/config/mixins/_avatar.scss +109 -0
  181. package/scss/config/mixins/_borders.scss +36 -0
  182. package/scss/config/mixins/_breadcrumbs.scss +72 -0
  183. package/scss/config/mixins/_breakpoints.scss +62 -0
  184. package/scss/config/mixins/_btn.scss +179 -0
  185. package/scss/config/mixins/_card.scss +338 -0
  186. package/scss/config/mixins/_chip.scss +66 -0
  187. package/scss/config/mixins/_circular-progress.scss +71 -0
  188. package/scss/config/mixins/_collapsible.scss +24 -0
  189. package/scss/config/mixins/_colors.scss +46 -0
  190. package/scss/config/mixins/_confirm.scss +31 -0
  191. package/scss/config/mixins/_data-table.scss +346 -0
  192. package/scss/config/mixins/_display.scss +32 -0
  193. package/scss/config/mixins/_dropdown.scss +143 -0
  194. package/scss/config/mixins/_empty-state.scss +30 -0
  195. package/scss/config/mixins/_focus.scss +55 -0
  196. package/scss/config/mixins/_footer.scss +42 -0
  197. package/scss/config/mixins/_form.scss +601 -0
  198. package/scss/config/mixins/_index.scss +58 -0
  199. package/scss/config/mixins/_interaction.scss +15 -0
  200. package/scss/config/mixins/_kbd.scss +22 -0
  201. package/scss/config/mixins/_layout.scss +117 -0
  202. package/scss/config/mixins/_link.scss +55 -0
  203. package/scss/config/mixins/_ln-table.scss +420 -0
  204. package/scss/config/mixins/_loader.scss +26 -0
  205. package/scss/config/mixins/_modal.scss +66 -0
  206. package/scss/config/mixins/_motion.scss +19 -0
  207. package/scss/config/mixins/_nav.scss +273 -0
  208. package/scss/config/mixins/_page-header.scss +69 -0
  209. package/scss/config/mixins/_popover.scss +25 -0
  210. package/scss/config/mixins/_position.scss +32 -0
  211. package/scss/config/mixins/_progress.scss +56 -0
  212. package/scss/config/mixins/_prose.scss +127 -0
  213. package/scss/config/mixins/_shadows.scss +8 -0
  214. package/scss/config/mixins/_sidebar.scss +95 -0
  215. package/scss/config/mixins/_sizing.scss +6 -0
  216. package/scss/config/mixins/_spacing.scss +19 -0
  217. package/scss/config/mixins/_stat-card.scss +68 -0
  218. package/scss/config/mixins/_status-badge.scss +83 -0
  219. package/scss/config/mixins/_stepper.scss +78 -0
  220. package/scss/config/mixins/_table.scss +215 -0
  221. package/scss/config/mixins/_tabs.scss +64 -0
  222. package/scss/config/mixins/_timeline.scss +69 -0
  223. package/scss/config/mixins/_toast.scss +148 -0
  224. package/scss/config/mixins/_tooltip.scss +111 -0
  225. package/scss/config/mixins/_transitions.scss +10 -0
  226. package/scss/config/mixins/_translations.scss +124 -0
  227. package/scss/config/mixins/_typography.scss +57 -0
  228. package/scss/config/mixins/_upload.scss +168 -0
  229. package/scss/ln-ashlar.scss +62 -0
  230. package/scss/tabler-icons.txt +5039 -0
  231. package/scss/utilities/_animations.scss +83 -0
  232. package/scss/utilities/_utilities.scss +49 -0
@@ -0,0 +1,147 @@
1
+ import { dispatch, dispatchCancelable, serializeForm, populateForm, registerComponent } from '../../ln-core';
2
+
3
+ (function () {
4
+ const DOM_SELECTOR = 'data-ln-autosave';
5
+ const DOM_ATTRIBUTE = 'lnAutosave';
6
+ const CLEAR_SELECTOR = 'data-ln-autosave-clear';
7
+ const DEBOUNCE_SELECTOR = 'data-ln-autosave-debounce-input';
8
+ const STORAGE_PREFIX = 'ln-autosave:';
9
+ const DEFAULT_DEBOUNCE_MS = 1000;
10
+
11
+ if (window[DOM_ATTRIBUTE] !== undefined) return;
12
+
13
+ // ─── Component ─────────────────────────────────────────────
14
+
15
+ function _component(form) {
16
+ const key = _getStorageKey(form);
17
+ if (!key) {
18
+ console.warn('ln-autosave: form needs an id or data-ln-autosave value', form);
19
+ return;
20
+ }
21
+
22
+ this.dom = form;
23
+ this.key = key;
24
+
25
+ let inputTimer = null;
26
+
27
+ // Closure-scoped helpers (NOT on prototype — internal only)
28
+ function _save() {
29
+ const data = serializeForm(form);
30
+ try { localStorage.setItem(key, JSON.stringify(data)); }
31
+ catch (e) { return; }
32
+ dispatch(form, 'ln-autosave:saved', { target: form, data: data });
33
+ }
34
+
35
+ function _restore() {
36
+ let raw;
37
+ try { raw = localStorage.getItem(key); } catch (e) { return; }
38
+ if (!raw) return;
39
+ let data;
40
+ try { data = JSON.parse(raw); } catch (e) { return; }
41
+ const before = dispatchCancelable(form, 'ln-autosave:before-restore', { target: form, data: data });
42
+ if (before.defaultPrevented) return;
43
+ const restored = populateForm(form, data);
44
+ for (let k = 0; k < restored.length; k++) {
45
+ restored[k].dispatchEvent(new Event('input', { bubbles: true }));
46
+ restored[k].dispatchEvent(new Event('change', { bubbles: true }));
47
+ }
48
+ dispatch(form, 'ln-autosave:restored', { target: form, data: data });
49
+ }
50
+
51
+ function _clear() {
52
+ try { localStorage.removeItem(key); } catch (e) { return; }
53
+ dispatch(form, 'ln-autosave:cleared', { target: form });
54
+ }
55
+
56
+ // Listener handlers (held on `this` for symmetric removal in destroy)
57
+ this._onFocusout = function (e) {
58
+ const el = e.target;
59
+ if (_isFormField(el) && el.name) _save();
60
+ };
61
+
62
+ this._onChange = function (e) {
63
+ const el = e.target;
64
+ if (_isFormField(el) && el.name) _save();
65
+ };
66
+
67
+ this._onSubmit = function () { _clear(); };
68
+
69
+ this._onReset = function () { _clear(); };
70
+
71
+ this._onClearClick = function (e) {
72
+ const btn = e.target.closest('[' + CLEAR_SELECTOR + ']');
73
+ if (btn) _clear();
74
+ };
75
+
76
+ form.addEventListener('focusout', this._onFocusout);
77
+ form.addEventListener('change', this._onChange);
78
+ form.addEventListener('submit', this._onSubmit);
79
+ form.addEventListener('reset', this._onReset);
80
+ form.addEventListener('click', this._onClearClick);
81
+
82
+ // Optional debounced input listener (opt-in via data-ln-autosave-debounce-input)
83
+ const debounceMs = _resolveDebounceMs(form);
84
+ if (debounceMs > 0) {
85
+ this._onInput = function (e) {
86
+ const el = e.target;
87
+ if (!_isFormField(el) || !el.name) return;
88
+ if (inputTimer !== null) clearTimeout(inputTimer);
89
+ inputTimer = setTimeout(_save, debounceMs);
90
+ };
91
+ form.addEventListener('input', this._onInput);
92
+ }
93
+
94
+ // Thunk so destroy() can reach the closure-scoped inputTimer
95
+ this._getInputTimer = function () { return inputTimer; };
96
+
97
+ _restore();
98
+
99
+ return this;
100
+ }
101
+
102
+ _component.prototype.destroy = function () {
103
+ if (!this.dom[DOM_ATTRIBUTE]) return;
104
+ this.dom.removeEventListener('focusout', this._onFocusout);
105
+ this.dom.removeEventListener('change', this._onChange);
106
+ this.dom.removeEventListener('submit', this._onSubmit);
107
+ this.dom.removeEventListener('reset', this._onReset);
108
+ this.dom.removeEventListener('click', this._onClearClick);
109
+ if (this._onInput) {
110
+ this.dom.removeEventListener('input', this._onInput);
111
+ const t = this._getInputTimer();
112
+ if (t !== null) clearTimeout(t);
113
+ }
114
+ dispatch(this.dom, 'ln-autosave:destroyed', { target: this.dom });
115
+ delete this.dom[DOM_ATTRIBUTE];
116
+ };
117
+
118
+ // ─── Helpers ───────────────────────────────────────────────
119
+
120
+ function _getStorageKey(form) {
121
+ const value = form.getAttribute(DOM_SELECTOR);
122
+ const identifier = value || form.id;
123
+ if (!identifier) return null;
124
+ return STORAGE_PREFIX + window.location.pathname + ':' + identifier;
125
+ }
126
+
127
+ function _isFormField(el) {
128
+ const tag = el.tagName;
129
+ return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';
130
+ }
131
+
132
+ function _resolveDebounceMs(form) {
133
+ if (!form.hasAttribute(DEBOUNCE_SELECTOR)) return 0;
134
+ const raw = form.getAttribute(DEBOUNCE_SELECTOR);
135
+ if (raw === '' || raw === null) return DEFAULT_DEBOUNCE_MS;
136
+ const ms = parseInt(raw, 10);
137
+ if (isNaN(ms) || ms < 0) {
138
+ console.warn('ln-autosave: invalid debounce value, using default', form);
139
+ return DEFAULT_DEBOUNCE_MS;
140
+ }
141
+ return ms;
142
+ }
143
+
144
+ // ─── Init ──────────────────────────────────────────────────
145
+
146
+ registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _component, 'ln-autosave');
147
+ })();
@@ -0,0 +1,161 @@
1
+ # ln-circular-progress
2
+
3
+ > Attribute-driven SVG ring renderer. Set `data-ln-circular-progress="75"` on an
4
+ > empty host; the component builds the SVG, watches the attribute, and redraws on every change. The attribute IS the state — no imperative setter.
5
+
6
+ ## Quick start
7
+
8
+ ```html
9
+ <div data-ln-circular-progress="75" class="success"></div>
10
+ ```
11
+
12
+ One attribute on an empty host element. The constructor creates an `<svg>` with a
13
+ track circle and fill circle, creates a `<strong>` label, and appends both to the
14
+ host. The SVG fills the host's dimensions via CSS (`width: 100%`, `height: 100%`),
15
+ so size comes from the host — or from a size class (`.sm`, `.lg`, `.xl`).
16
+
17
+ The host element must be empty at init — the constructor `appendChild`s the SVG and label; pre-existing children stay in the DOM and overlap the ring.
18
+
19
+ ## Attributes
20
+
21
+ | Attribute | On | Description |
22
+ |---|---|---|
23
+ | `data-ln-circular-progress="N"` | host element | Current value. Creates the instance. Number; negative or above-max values are allowed — the rendered percentage clamps to 0–100, but the attribute is left as-written. Empty / non-numeric strings render as 0%. |
24
+ | `data-ln-circular-progress-max="N"` | host element | Maximum value. Default `100`. Used as denominator in `(value / max) × 100`. `max="0"` forces 0% (avoids divide-by-zero). |
25
+ | `data-ln-circular-progress-label="text"` | host element | Custom centre label, replaces the auto-computed percentage. Read inside `_render`, but **NOT in the MutationObserver filter** — mutating this attribute alone does not trigger a re-render. Update the label alongside a value or max write. |
26
+
27
+ `data-ln-circular-progress-initialized=""` is set by the constructor and removed by `destroy()`. It is an internal marker — do not read it from project code; use `el.lnCircularProgress` instead.
28
+
29
+ **Size variants** — apply a class to the host: `.sm` (2.5 rem), default (4 rem), `.lg` (6 rem), `.xl` (8 rem). Each rebinds the label font-size.
30
+
31
+ **Colour variants** — `.success`, `.warning`, `.error` override the fill stroke via `scss/components/_circular-progress.scss`. The default fill reads `--color-accent` and follows the active theme.
32
+
33
+ ## Events
34
+
35
+ | Event | Bubbles | Cancelable | `detail` |
36
+ |---|---|---|---|
37
+ | `ln-circular-progress:change` | yes | no | `{ target, value, max, percentage }` |
38
+
39
+ `detail.value` is the raw `parseFloat` of the attribute — unclamped. `detail.percentage` is clamped 0–100. A value of 150 on a max-100 ring produces `value: 150, percentage: 100`. Read `detail.value` to detect overshoot; read `detail.percentage` to match the visible arc.
40
+
41
+ `detail.target` is the host element — useful when a single document-level listener handles multiple rings:
42
+
43
+ ```js
44
+ document.addEventListener('ln-circular-progress:change', function (e) {
45
+ const { target, percentage } = e.detail;
46
+ if (percentage >= 100) {
47
+ target.classList.add('done');
48
+ }
49
+ });
50
+ ```
51
+
52
+ The event fires on the initial render at construction, as well as on every subsequent value or max attribute change. A document-level listener sees a `:change` for every ring on the page during init.
53
+
54
+ ## Accessibility
55
+
56
+ The component sets `aria-hidden="true"` on the constructed `<svg>` — screen readers skip the SVG element. The component does **not** write `role`, `aria-valuenow`, `aria-valuemin`, or `aria-valuemax` on the host. Consumer owns the accessibility tree:
57
+
58
+ ```html
59
+ <div data-ln-circular-progress="75"
60
+ role="progressbar"
61
+ aria-valuenow="75"
62
+ aria-valuemin="0"
63
+ aria-valuemax="100"
64
+ aria-label="Upload progress"
65
+ class="success">
66
+ </div>
67
+ ```
68
+
69
+ Keep `aria-valuenow` in sync when writing `data-ln-circular-progress` — the component does not do this automatically.
70
+
71
+ ## API
72
+
73
+ `el.lnCircularProgress` on a constructed host element:
74
+
75
+ | Property | Type | Description |
76
+ |---|---|---|
77
+ | `dom` | `HTMLElement` | Back-reference to the host element |
78
+ | `svg` | `SVGSVGElement` | The constructed `<svg>` |
79
+ | `trackCircle` | `SVGCircleElement` | Background circle (stroke = `--color-border`) |
80
+ | `progressCircle` | `SVGCircleElement` | Fill circle — `stroke-dashoffset` carries the progress |
81
+ | `labelEl` | `HTMLElement` | The `<strong>` element holding the percentage or custom label text |
82
+ | `destroy()` | method | Disconnects the attribute observer, removes the SVG and label from DOM, removes `data-ln-circular-progress-initialized`, deletes `el.lnCircularProgress`. Leaves the value attribute, colour class, and size class in place — setting the value attribute again re-instantiates. |
83
+
84
+ `window.lnCircularProgress(root)` re-runs the init scan over `root`. The shared `registerComponent` observer already covers AJAX inserts; call this manually only for Shadow DOM roots or foreign documents the observer cannot reach.
85
+
86
+ ## Examples
87
+
88
+ ### Minimal
89
+
90
+ ```html
91
+ <div data-ln-circular-progress="75" class="success"></div>
92
+ ```
93
+
94
+ 75% green ring with the default 4 rem diameter. The centre label reads "75%" automatically. No JS required.
95
+
96
+ ### Reactive — button-driven
97
+
98
+ ```html
99
+ <div id="ring" data-ln-circular-progress="0" class="lg"></div>
100
+ <button id="advance">+10%</button>
101
+ ```
102
+
103
+ ```js
104
+ let pct = 0;
105
+ document.getElementById('advance').addEventListener('click', function () {
106
+ pct = Math.min(100, pct + 10);
107
+ document.getElementById('ring').setAttribute('data-ln-circular-progress', pct);
108
+ });
109
+ ```
110
+
111
+ Each attribute write triggers a re-render and a `:change` event. The CSS
112
+ transition animates the arc from the previous offset to the new one.
113
+
114
+ ### Custom max + label
115
+
116
+ ```html
117
+ <!-- 7 out of 10 — arc fills to 70%, label shows the count -->
118
+ <div data-ln-circular-progress="7"
119
+ data-ln-circular-progress-max="10"
120
+ data-ln-circular-progress-label="7/10"
121
+ class="lg success">
122
+ </div>
123
+ ```
124
+
125
+ `max="10"` shifts the denominator so 7 maps to 70% of the arc. `label="7/10"` overrides the centre text — without it, the centre would read "70%". Both attributes are usually set together for non-percentage scales.
126
+
127
+ ## What it does NOT do
128
+
129
+ - No indeterminate / spinner mode. An empty value renders 0% (empty track). For an indeterminate spinner use `@mixin loader`.
130
+ - No automatic ARIA. Consumer adds `role="progressbar"` and keeps `aria-valuenow` in sync.
131
+ - No debounce on rapid attribute writes — each write triggers a synchronous render and a `:change` event.
132
+ - No `setValue()` / `setMax()` / imperative API. `el.setAttribute('data-ln-circular-progress', n)` is the canonical update path.
133
+
134
+ ## Integration and Source Files
135
+
136
+ This component can be loaded either as part of the unified Ashlar bundle or as a standalone zero-dependency script.
137
+
138
+ ### 1. In-Bundle (Standard Integration)
139
+ Include the main Ashlar bundle in your HTML. This bundle registers all Ashlar components, including `ln-circular-progress`:
140
+ ```html
141
+ <script src="dist/ln-ashlar.iife.js" defer></script>
142
+ ```
143
+
144
+ ### 2. Standalone (Zero-Dependency IIFE)
145
+ If you only need circular progress indicators without the rest of the Ashlar library, load the component's standalone IIFE script:
146
+ ```html
147
+ <script src="js/ln-circular-progress/ln-circular-progress.js" defer></script>
148
+ ```
149
+
150
+ ### 3. Source & Reference
151
+ * **Active Development Source**: [js/ln-circular-progress/src/ln-circular-progress.js](file:///c:/laragon/www/ln-ashlar/js/ln-circular-progress/src/ln-circular-progress.js) — The source of truth containing the ES module implementation.
152
+ * **Compiled Standalone Release**: [js/ln-circular-progress/ln-circular-progress.js](file:///c:/laragon/www/ln-ashlar/js/ln-circular-progress/ln-circular-progress.js) — The compiled IIFE distribution file.
153
+
154
+ ## See also
155
+
156
+ - `@mixin circular-progress` (`scss/config/mixins/_circular-progress.scss`) — SVG layout, fill/track stroke tokens, motion-safe transition, size variant mixins.
157
+ - `scss/components/_circular-progress.scss` — default selector application and the `.success` / `.warning` / `.error` colour variant rules.
158
+ - [`ln-progress`](../ln-progress/README.md) — linear sibling. Same contract shape (`data-ln-progress` + `-max`); supports parent-track shared max.
159
+ - [`docs/js/circular-progress.md`](../../docs/js/circular-progress.md) — architecture reference: SVG geometry constants, render flow, MutationObserver filter, performance notes.
160
+ - `@mixin loader` (`scss/config/mixins/_loader.scss`) — indeterminate spinner for operations with no known fraction.
161
+ - [`docs/architecture/data-flow.md`](../../docs/architecture/data-flow.md) — passive renderer pattern used by this component.
@@ -0,0 +1 @@
1
+ (function(){"use strict";function C(e,t,l){e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:l||{}}))}function m(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){m(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}function b(e,t,l,o){if(e.nodeType!==1)return;const c=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",h=Array.from(e.querySelectorAll(c));e.matches&&e.matches(c)&&h.push(e);for(const s of h)s[l]||(s[l]=new o(s))}function w(e,t,l,o,u={}){const c=u.extraAttributes||[],h=u.onAttributeChange||null,s=u.onInit||null;function f(p){const d=p||document.body;b(d,e,t,l),s&&s(d)}return m(function(){const p=new MutationObserver(function(n){for(let i=0;i<n.length;i++){const r=n[i];if(r.type==="childList")for(let a=0;a<r.addedNodes.length;a++){const g=r.addedNodes[a];g.nodeType===1&&(b(g,e,t,l),s&&s(g))}else r.type==="attributes"&&(h&&r.target[t]?h(r.target,r.attributeName):(b(r.target,e,t,l),s&&s(r.target)))}});let d=[];if(e.indexOf("[")!==-1){const n=/\[([\w-]+)/g;let i;for(;(i=n.exec(e))!==null;)d.push(i[1])}else d.push(e);p.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:d.concat(c)})},o),window[t]=f,document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){f(document.body)}):f(document.body),f}const v={};function y(e,t){v[e]=t}function E(e){return v[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=y,window.lnCore.getDataMapper=E),(function(){const e="data-ln-circular-progress",t="lnCircularProgress";if(window[t]!==void 0)return;const l="http://www.w3.org/2000/svg",o=36,u=16,c=2*Math.PI*u;function h(n){return this.dom=n,this.svg=null,this.trackCircle=null,this.progressCircle=null,this.labelEl=null,this._attrObserver=null,f.call(this),d.call(this),p.call(this),n.setAttribute("data-ln-circular-progress-initialized",""),this}h.prototype.destroy=function(){this.dom[t]&&(this._attrObserver&&this._attrObserver.disconnect(),this.svg&&this.svg.remove(),this.labelEl&&this.labelEl.remove(),this.dom.removeAttribute("data-ln-circular-progress-initialized"),delete this.dom[t])};function s(n,i){const r=document.createElementNS(l,n);for(const a in i)r.setAttribute(a,i[a]);return r}function f(){this.svg=s("svg",{viewBox:"0 0 "+o+" "+o,"aria-hidden":"true"}),this.trackCircle=s("circle",{cx:o/2,cy:o/2,r:u,fill:"none","stroke-width":"3"}),this.trackCircle.classList.add("ln-circular-progress__track"),this.progressCircle=s("circle",{cx:o/2,cy:o/2,r:u,fill:"none","stroke-width":"3","stroke-linecap":"round","stroke-dasharray":c,"stroke-dashoffset":c,transform:"rotate(-90 "+o/2+" "+o/2+")"}),this.progressCircle.classList.add("ln-circular-progress__fill"),this.svg.appendChild(this.trackCircle),this.svg.appendChild(this.progressCircle),this.labelEl=document.createElement("strong"),this.labelEl.classList.add("ln-circular-progress__label"),this.dom.appendChild(this.svg),this.dom.appendChild(this.labelEl)}function p(){const n=this,i=new MutationObserver(function(r){for(const a of r)(a.attributeName==="data-ln-circular-progress"||a.attributeName==="data-ln-circular-progress-max")&&d.call(n)});i.observe(this.dom,{attributes:!0,attributeFilter:["data-ln-circular-progress","data-ln-circular-progress-max"]}),this._attrObserver=i}function d(){const n=parseFloat(this.dom.getAttribute("data-ln-circular-progress"))||0,i=parseFloat(this.dom.getAttribute("data-ln-circular-progress-max"))||100;let r=i>0?n/i*100:0;r<0&&(r=0),r>100&&(r=100);const a=c-r/100*c;this.progressCircle.setAttribute("stroke-dashoffset",a);const g=this.dom.getAttribute("data-ln-circular-progress-label");this.labelEl.textContent=g!==null?g:Math.round(r)+"%",C(this.dom,"ln-circular-progress:change",{target:this.dom,value:n,max:i,percentage:r})}w(e,t,h,"ln-circular-progress")})()})();
@@ -0,0 +1,133 @@
1
+ import { dispatch, registerComponent } from '../../ln-core';
2
+
3
+ (function () {
4
+ const DOM_SELECTOR = 'data-ln-circular-progress';
5
+ const DOM_ATTRIBUTE = 'lnCircularProgress';
6
+
7
+ if (window[DOM_ATTRIBUTE] !== undefined) return;
8
+
9
+ const SVG_NS = 'http://www.w3.org/2000/svg';
10
+ const VIEW_SIZE = 36;
11
+ const RADIUS = 16;
12
+ const CIRCUMFERENCE = 2 * Math.PI * RADIUS;
13
+
14
+ function _constructor(dom) {
15
+ this.dom = dom;
16
+ this.svg = null;
17
+ this.trackCircle = null;
18
+ this.progressCircle = null;
19
+ this.labelEl = null;
20
+ this._attrObserver = null;
21
+ _buildSvg.call(this);
22
+ _render.call(this);
23
+ _listenValues.call(this);
24
+ dom.setAttribute('data-ln-circular-progress-initialized', '');
25
+ return this;
26
+ }
27
+
28
+ _constructor.prototype.destroy = function () {
29
+ if (!this.dom[DOM_ATTRIBUTE]) return;
30
+ if (this._attrObserver) {
31
+ this._attrObserver.disconnect();
32
+ }
33
+ if (this.svg) {
34
+ this.svg.remove();
35
+ }
36
+ if (this.labelEl) {
37
+ this.labelEl.remove();
38
+ }
39
+ this.dom.removeAttribute('data-ln-circular-progress-initialized');
40
+ delete this.dom[DOM_ATTRIBUTE];
41
+ };
42
+
43
+ function _createSvgElement(tag, attrs) {
44
+ const el = document.createElementNS(SVG_NS, tag);
45
+ for (const key in attrs) {
46
+ el.setAttribute(key, attrs[key]);
47
+ }
48
+ return el;
49
+ }
50
+
51
+ function _buildSvg() {
52
+ this.svg = _createSvgElement('svg', {
53
+ viewBox: '0 0 ' + VIEW_SIZE + ' ' + VIEW_SIZE,
54
+ 'aria-hidden': 'true'
55
+ });
56
+
57
+ this.trackCircle = _createSvgElement('circle', {
58
+ cx: VIEW_SIZE / 2,
59
+ cy: VIEW_SIZE / 2,
60
+ r: RADIUS,
61
+ fill: 'none',
62
+ 'stroke-width': '3'
63
+ });
64
+ this.trackCircle.classList.add('ln-circular-progress__track');
65
+
66
+ this.progressCircle = _createSvgElement('circle', {
67
+ cx: VIEW_SIZE / 2,
68
+ cy: VIEW_SIZE / 2,
69
+ r: RADIUS,
70
+ fill: 'none',
71
+ 'stroke-width': '3',
72
+ 'stroke-linecap': 'round',
73
+ 'stroke-dasharray': CIRCUMFERENCE,
74
+ 'stroke-dashoffset': CIRCUMFERENCE,
75
+ transform: 'rotate(-90 ' + (VIEW_SIZE / 2) + ' ' + (VIEW_SIZE / 2) + ')'
76
+ });
77
+ this.progressCircle.classList.add('ln-circular-progress__fill');
78
+
79
+ this.svg.appendChild(this.trackCircle);
80
+ this.svg.appendChild(this.progressCircle);
81
+
82
+ this.labelEl = document.createElement('strong');
83
+ this.labelEl.classList.add('ln-circular-progress__label');
84
+
85
+ this.dom.appendChild(this.svg);
86
+ this.dom.appendChild(this.labelEl);
87
+ }
88
+
89
+ function _listenValues() {
90
+ const self = this;
91
+ const observer = new MutationObserver(function (mutations) {
92
+ for (const mutation of mutations) {
93
+ if (mutation.attributeName === 'data-ln-circular-progress' ||
94
+ mutation.attributeName === 'data-ln-circular-progress-max') {
95
+ _render.call(self);
96
+ }
97
+ }
98
+ });
99
+
100
+ observer.observe(this.dom, {
101
+ attributes: true,
102
+ attributeFilter: ['data-ln-circular-progress', 'data-ln-circular-progress-max']
103
+ });
104
+
105
+ this._attrObserver = observer;
106
+ }
107
+
108
+ function _render() {
109
+ const value = parseFloat(this.dom.getAttribute('data-ln-circular-progress')) || 0;
110
+ const max = parseFloat(this.dom.getAttribute('data-ln-circular-progress-max')) || 100;
111
+ let percentage = (max > 0) ? (value / max) * 100 : 0;
112
+
113
+ if (percentage < 0) percentage = 0;
114
+ if (percentage > 100) percentage = 100;
115
+
116
+ const offset = CIRCUMFERENCE - (percentage / 100) * CIRCUMFERENCE;
117
+ this.progressCircle.setAttribute('stroke-dashoffset', offset);
118
+
119
+ const label = this.dom.getAttribute('data-ln-circular-progress-label');
120
+ this.labelEl.textContent = label !== null ? label : Math.round(percentage) + '%';
121
+
122
+ dispatch(this.dom, 'ln-circular-progress:change', {
123
+ target: this.dom,
124
+ value: value,
125
+ max: max,
126
+ percentage: percentage
127
+ });
128
+ }
129
+
130
+ // ─── Init ──────────────────────────────────────────────────
131
+
132
+ registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _constructor, 'ln-circular-progress');
133
+ })();
@@ -0,0 +1,86 @@
1
+ # ln-confirm
2
+
3
+ A zero-dependency, ultra-lightweight **Interaction Gate Primitive** (~109 lines of JavaScript) that creates a self-contained, two-click confirmation checkpoint on standard buttons. It intercepts accidental clicks, morphs in-place to reveal confirmation prompts, and passes the second click directly to native platform events.
4
+
5
+ ---
6
+
7
+ ## 🧭 Philosophy & Architecture
8
+
9
+ 1. **In-Place Morphing:** Instead of launching heavy, separate dialogs or using frozen, unstyleable `window.confirm()` scripts, confirmation lives **directly on the target button**. It preserves structural styling while using CSS mixins to display temporary labels or icon tooltips.
10
+ 2. **Platform Event Release:** `ln-confirm` does not implement a custom `accept` handler. The acceptance is the standard platform `click` event. On the second click, the component steps out of the way, allowing form submissions (`type="submit"`), links (`href`), or custom AJAX click listeners to execute natively.
11
+ 3. **Graceful Auto-Revert:** The gate is timed. When armed, a countdown timer is scheduled. If a second click does not arrive within the window, the button cleanly reverts to its idle text or icon state.
12
+
13
+ ---
14
+
15
+ ## 📦 Minimal Blueprint
16
+
17
+ ### Text-Button Mode
18
+
19
+ ```html
20
+ <button data-ln-confirm="Are you sure you want to delete?">Delete account</button>
21
+ ```
22
+
23
+ On click, the text replaces with the confirmation prompt in-place, and the button accent shifts to error red.
24
+
25
+ ### Icon-Only Mode
26
+
27
+ ```html
28
+ <button aria-label="Delete item" data-ln-confirm="Confirm delete?">
29
+ <svg class="ln-icon"><use href="#ln-trash"></use></svg>
30
+ </button>
31
+ ```
32
+
33
+ On click, the trash icon swaps to `#ln-check`, a tooltip bubble is displayed above, and the prompt is announced for screen-readers.
34
+
35
+ ---
36
+
37
+ ## 🛠️ Declarative API Contract
38
+
39
+ ### HTML Attributes
40
+
41
+ | Attribute | Elements | Description |
42
+ | :--- | :--- | :--- |
43
+ | `data-ln-confirm="Prompt"` | `<button>`, `<a>` | Action gate marker. Empty value defaults to `"Confirm?"`. |
44
+ | `data-ln-confirm-timeout="3"` | `<button>`, `<a>` | Auto-revert delay in seconds (default `3`). |
45
+ | `data-confirming="true"` | `<button>` (auto) | Managed state. Set during active confirmation; acts as public CSS hook. |
46
+ | `data-tooltip-text="Prompt"` | `<button>` (auto) | Managed state. Displays tooltip bubble in icon-only mode. |
47
+
48
+ ### JS API
49
+
50
+ Access the confirmation instance directly via the `lnConfirm` property on the button element:
51
+
52
+ ```javascript
53
+ const button = document.getElementById('delete-btn');
54
+
55
+ // 1. Check if armed (Boolean getter)
56
+ if (button.lnConfirm.confirming) { ... }
57
+
58
+ // 2. Disarm immediately, restore visual states, and unbind listeners
59
+ button.lnConfirm.destroy();
60
+ ```
61
+
62
+ ---
63
+
64
+ ## ⚡ DOM Events
65
+
66
+ ### Emitted
67
+
68
+ `ln-confirm` dispatches exactly one event during its lifecycle:
69
+
70
+ | Event | Bubbles | Payload | Description |
71
+ | :--- | :--- | :--- | :--- |
72
+ | `ln-confirm:waiting` | Yes | `{ target }` | Fires on first click, right after the button arms. Useful for telemetry. |
73
+
74
+ *Note: There is no `accept` event. Code execution on accept belongs on standard browser `click` or form `submit` handlers.*
75
+
76
+ ---
77
+
78
+ ## ⚠️ Common Pitfalls
79
+
80
+ - **Listening for an `:accept` event:** There is no `ln-confirm:accept`. Destructive logic belongs in the button's native click handler or form submit listener — which are automatically gated.
81
+ - **Double-Click Protection:** Once the second click executes the target action, the button returns to idle. If your custom AJAX handler takes time, the user can click again and execute duplicate actions. Disable the button on submit:
82
+ ```javascript
83
+ form.addEventListener('submit', () => submitButton.disabled = true);
84
+ ```
85
+ - **GET Request Navigation (`<a>`):** While the script works on links, performing destructive actions via HTTP GET is a security risk (crawlers and pre-fetchers can trigger deletes). Always use `<form method="POST">` with submit buttons instead.
86
+ - **Attachment Order:** `ln-confirm` intercepts first clicks via `stopImmediatePropagation()`. If custom click handlers are bound *before* the bundle loads, they will run on the first click anyway. Ensure scripts are defer-loaded.
@@ -0,0 +1,13 @@
1
+ // ── JS state: confirm mode color override ──
2
+ // When data-confirming is set (by JS), override --color-primary to error
3
+ // so the button turns red. Tooltip-mode buttons handle color differently.
4
+ [data-confirming]:not(.ln-confirm-tooltip) {
5
+ --color-primary: var(--color-error);
6
+ }
7
+
8
+ // ── Overflow escape: tooltip inside clipped containers ──
9
+ // [data-ln-table] uses overflow:clip which clips the ::after tooltip.
10
+ // Temporarily allow overflow when a confirm is active inside.
11
+ [data-ln-table]:has([data-confirming]) {
12
+ overflow: visible;
13
+ }
@@ -0,0 +1 @@
1
+ (function(){"use strict";function A(e,t,s){e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:s||{}}))}function p(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){p(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}function h(e,t,s,u){if(e.nodeType!==1)return;const i=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",n=Array.from(e.querySelectorAll(i));e.matches&&e.matches(i)&&n.push(e);for(const o of n)o[s]||(o[s]=new u(o))}function v(e,t,s,u,r={}){const i=r.extraAttributes||[],n=r.onAttributeChange||null,o=r.onInit||null;function c(m){const l=m||document.body;h(l,e,t,s),o&&o(l)}return p(function(){const m=new MutationObserver(function(f){for(let d=0;d<f.length;d++){const a=f[d];if(a.type==="childList")for(let g=0;g<a.addedNodes.length;g++){const b=a.addedNodes[g];b.nodeType===1&&(h(b,e,t,s),o&&o(b))}else a.type==="attributes"&&(n&&a.target[t]?n(a.target,a.attributeName):(h(a.target,e,t,s),o&&o(a.target)))}});let l=[];if(e.indexOf("[")!==-1){const f=/\[([\w-]+)/g;let d;for(;(d=f.exec(e))!==null;)l.push(d[1])}else l.push(e);m.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:l.concat(i)})},u),window[t]=c,document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){c(document.body)}):c(document.body),c}const T={};function y(e,t){T[e]=t}function C(e){return T[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=y,window.lnCore.getDataMapper=C),(function(){const e="data-ln-confirm",t="lnConfirm",s="data-ln-confirm-timeout";if(window[t]!==void 0)return;function r(i){this.dom=i,this.confirming=!1,this.originalText=i.textContent.trim(),this.confirmText=i.getAttribute(e)||"Confirm?",this.revertTimer=null,this._submitted=!1;const n=this;return this._onClick=function(o){if(!n.confirming)o.preventDefault(),o.stopImmediatePropagation(),n._enterConfirm();else{if(n._submitted)return;n._submitted=!0,n._reset()}},i.addEventListener("click",this._onClick),this}r.prototype._getTimeout=function(){const i=parseFloat(this.dom.getAttribute(s));return isNaN(i)||i<=0?3:i},r.prototype._enterConfirm=function(){this.confirming=!0,this.dom.setAttribute("data-confirming","true");var i=this.dom.querySelector("svg.ln-icon use");i&&this.originalText===""?(this.isIconButton=!0,this.originalIconHref=i.getAttribute("href"),i.setAttribute("href","#ln-check"),this.dom.classList.add("ln-confirm-tooltip"),this.dom.setAttribute("data-tooltip-text",this.confirmText),this.originalAriaLabel=this.dom.getAttribute("aria-label"),this.dom.setAttribute("aria-label",this.confirmText),this.alertNode=document.createElement("span"),this.alertNode.className="sr-only",this.alertNode.setAttribute("role","alert"),this.alertNode.textContent=this.confirmText,this.dom.appendChild(this.alertNode)):this.dom.textContent=this.confirmText,this._startTimer(),A(this.dom,"ln-confirm:waiting",{target:this.dom})},r.prototype._startTimer=function(){this.revertTimer&&clearTimeout(this.revertTimer);const i=this,n=this._getTimeout()*1e3;this.revertTimer=setTimeout(function(){i._reset()},n)},r.prototype._reset=function(){if(this._submitted=!1,this.confirming=!1,this.dom.removeAttribute("data-confirming"),this.isIconButton){var i=this.dom.querySelector("svg.ln-icon use");i&&this.originalIconHref&&i.setAttribute("href",this.originalIconHref),this.dom.classList.remove("ln-confirm-tooltip"),this.dom.removeAttribute("data-tooltip-text"),this.originalAriaLabel!==null&&this.originalAriaLabel!==void 0?this.dom.setAttribute("aria-label",this.originalAriaLabel):this.dom.removeAttribute("aria-label"),this.originalAriaLabel=null,this.alertNode&&this.alertNode.parentNode===this.dom&&this.dom.removeChild(this.alertNode),this.alertNode=null,this.isIconButton=!1,this.originalIconHref=null}else this.dom.textContent=this.originalText;this.revertTimer&&(clearTimeout(this.revertTimer),this.revertTimer=null)},r.prototype.destroy=function(){this.dom[t]&&(this._reset(),this.dom.removeEventListener("click",this._onClick),delete this.dom[t])},v(e,t,r,"ln-confirm")})()})();