@livenetworks/ashlar 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/README.md +189 -177
  2. package/js/COMPONENTS.md +55 -49
  3. package/js/index.js +22 -5
  4. package/js/ln-accordion/README.md +186 -137
  5. package/js/ln-accordion/ln-accordion.js +1 -1
  6. package/js/ln-ajax/README.md +73 -4
  7. package/js/ln-ajax/ln-ajax.js +1 -1
  8. package/js/ln-ajax/src/ln-ajax.js +13 -13
  9. package/js/ln-api-connector/README.md +261 -150
  10. package/js/ln-api-connector/ln-api-connector.js +1 -1
  11. package/js/ln-api-connector/src/ln-api-connector.js +368 -265
  12. package/js/ln-api-queue/README.md +219 -0
  13. package/js/ln-api-queue/ln-api-queue.js +1 -0
  14. package/js/ln-api-queue/src/ln-api-queue.js +320 -0
  15. package/js/ln-api-queue/src/queue-storage.js +455 -0
  16. package/js/ln-autoresize/README.md +47 -0
  17. package/js/ln-autoresize/ln-autoresize.js +1 -1
  18. package/js/ln-autosave/README.md +33 -1
  19. package/js/ln-autosave/ln-autosave.js +1 -1
  20. package/js/ln-autosave/src/ln-autosave.js +4 -4
  21. package/js/ln-circular-progress/README.md +27 -13
  22. package/js/ln-circular-progress/ln-circular-progress.js +1 -1
  23. package/js/ln-circular-progress/src/ln-circular-progress.js +18 -5
  24. package/js/ln-confirm/README.md +162 -86
  25. package/js/ln-confirm/_ln-confirm.scss +11 -0
  26. package/js/ln-confirm/ln-confirm.js +1 -1
  27. package/js/ln-confirm/src/ln-confirm.js +77 -41
  28. package/js/ln-core/README.md +896 -0
  29. package/js/ln-core/hash.js +92 -0
  30. package/js/ln-core/helpers.js +337 -8
  31. package/js/ln-core/index.js +4 -2
  32. package/js/ln-core/persist.js +29 -3
  33. package/js/ln-core/positioning.js +1 -35
  34. package/js/ln-core/window-cache.js +213 -0
  35. package/js/ln-couchdb-connector/README.md +177 -156
  36. package/js/ln-couchdb-connector/ln-couchdb-connector.js +1 -1
  37. package/js/ln-couchdb-connector/src/ln-couchdb-connector.js +398 -348
  38. package/js/ln-data-coordinator/README.md +536 -165
  39. package/js/ln-data-coordinator/ln-data-coordinator.js +1 -1
  40. package/js/ln-data-coordinator/src/data-read-policy.js +18 -0
  41. package/js/ln-data-coordinator/src/ln-data-coordinator.js +1042 -249
  42. package/js/ln-data-coordinator/src/mutation-receipts.js +46 -0
  43. package/js/ln-data-store/README.md +241 -94
  44. package/js/ln-data-store/ln-data-store.js +1 -1
  45. package/js/ln-data-store/src/ln-data-store.js +694 -699
  46. package/js/ln-date/README.md +39 -1
  47. package/js/ln-date/ln-date-dev.scss +14 -0
  48. package/js/ln-date/ln-date.js +1 -1
  49. package/js/ln-date/src/ln-date.js +236 -178
  50. package/js/ln-debug/README.md +58 -0
  51. package/js/ln-debug/ln-debug.js +1 -0
  52. package/js/ln-debug/src/ln-debug.js +19 -0
  53. package/js/ln-dropdown/README.md +31 -8
  54. package/js/ln-dropdown/ln-dropdown.js +1 -1
  55. package/js/ln-dropdown/src/ln-dropdown.js +30 -13
  56. package/js/ln-editor/README.md +274 -0
  57. package/js/ln-editor/ln-editor.js +1 -0
  58. package/js/ln-editor/ln-editor.scss +43 -0
  59. package/js/ln-editor/src/ln-editor.js +619 -0
  60. package/js/ln-external-links/README.md +44 -5
  61. package/js/ln-external-links/ln-external-links.js +1 -1
  62. package/js/ln-fill/README.md +199 -0
  63. package/js/ln-fill/ln-fill.js +1 -0
  64. package/js/ln-fill/src/ln-fill.js +87 -0
  65. package/js/ln-filter/README.md +92 -2
  66. package/js/ln-filter/ln-filter-dev.scss +8 -0
  67. package/js/ln-filter/ln-filter.js +1 -1
  68. package/js/ln-filter/src/ln-filter.js +22 -7
  69. package/js/ln-form/README.md +212 -49
  70. package/js/ln-form/ln-form.js +1 -1
  71. package/js/ln-form/src/ln-form.js +99 -199
  72. package/js/ln-http/README.md +125 -89
  73. package/js/ln-http/ln-http.js +1 -1
  74. package/js/ln-http/src/ln-http.js +32 -13
  75. package/js/ln-icon/README.md +130 -0
  76. package/js/ln-icon/ln-icon.js +1 -0
  77. package/js/{ln-icons/src/ln-icons.js → ln-icon/src/ln-icon.js} +20 -15
  78. package/js/ln-link/README.md +47 -9
  79. package/js/ln-link/ln-link.js +1 -1
  80. package/js/ln-link/src/ln-link.js +22 -6
  81. package/js/ln-list/README.md +129 -0
  82. package/js/ln-list/ln-list.js +1 -0
  83. package/js/ln-list/ln-list.scss +28 -0
  84. package/js/ln-list/src/ln-list.js +1216 -0
  85. package/js/ln-modal/README.md +185 -154
  86. package/js/ln-modal/ln-modal-dev.scss +13 -0
  87. package/js/ln-modal/ln-modal.js +1 -1
  88. package/js/ln-modal/ln-modal.scss +28 -4
  89. package/js/ln-modal/src/ln-modal.js +137 -201
  90. package/js/ln-modal-coordinator/README.md +99 -0
  91. package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +8 -0
  92. package/js/ln-modal-coordinator/ln-modal-coordinator.js +1 -0
  93. package/js/ln-modal-coordinator/src/ln-modal-coordinator.js +345 -0
  94. package/js/ln-nav/README.md +46 -3
  95. package/js/ln-nav/ln-nav.js +1 -1
  96. package/js/ln-nav/src/ln-nav.js +92 -127
  97. package/js/ln-number/README.md +28 -1
  98. package/js/ln-number/ln-number-dev.scss +13 -0
  99. package/js/ln-number/ln-number.js +1 -1
  100. package/js/ln-number/src/ln-number.js +443 -350
  101. package/js/ln-options/README.md +66 -0
  102. package/js/ln-options/ln-options.js +1 -0
  103. package/js/ln-options/src/ln-options.js +74 -0
  104. package/js/ln-popover/README.md +40 -5
  105. package/js/ln-popover/ln-popover.js +1 -1
  106. package/js/ln-popover/src/ln-popover.js +38 -11
  107. package/js/ln-progress/README.md +413 -442
  108. package/js/ln-progress/ln-progress.js +1 -1
  109. package/js/ln-progress/src/ln-progress.js +98 -150
  110. package/js/ln-router/README.md +432 -0
  111. package/js/ln-router/ln-router.js +1 -0
  112. package/js/ln-router/src/ln-router.js +539 -0
  113. package/js/ln-search/README.md +158 -83
  114. package/js/ln-search/ln-search-dev.scss +8 -0
  115. package/js/ln-search/ln-search.js +1 -1
  116. package/js/ln-search/src/ln-search.js +12 -9
  117. package/js/ln-slug/README.md +110 -0
  118. package/js/ln-slug/ln-slug.js +1 -0
  119. package/js/ln-slug/src/ln-slug.js +74 -0
  120. package/js/ln-sort/README.md +170 -0
  121. package/js/ln-sort/ln-sort-dev.scss +8 -0
  122. package/js/ln-sort/ln-sort.js +1 -0
  123. package/js/ln-sort/src/ln-sort.js +178 -0
  124. package/js/ln-sortable/README.md +133 -95
  125. package/js/ln-sortable/ln-sortable.js +1 -1
  126. package/js/ln-stat/README.md +55 -0
  127. package/js/ln-stat/ln-stat.js +1 -0
  128. package/js/ln-stat/src/ln-stat.js +55 -0
  129. package/js/ln-table/README.md +285 -101
  130. package/js/ln-table/ln-table-dev.scss +17 -0
  131. package/js/ln-table/ln-table.js +1 -1
  132. package/js/ln-table/ln-table.scss +1 -1
  133. package/js/ln-table/src/ln-table.js +1493 -473
  134. package/js/ln-table-coordinator/README.md +73 -0
  135. package/js/ln-table-coordinator/ln-table-coordinator.js +1 -0
  136. package/js/ln-table-coordinator/src/ln-table-coordinator.js +160 -0
  137. package/js/ln-tabs/README.md +221 -137
  138. package/js/ln-tabs/ln-tabs-dev.scss +18 -0
  139. package/js/ln-tabs/ln-tabs.js +1 -1
  140. package/js/ln-tabs/src/ln-tabs.js +41 -24
  141. package/js/ln-time/README.md +14 -0
  142. package/js/ln-time/ln-time.js +1 -1
  143. package/js/ln-time/src/ln-time.js +223 -192
  144. package/js/ln-toast/README.md +178 -122
  145. package/js/ln-toast/ln-toast.js +1 -15
  146. package/js/ln-toast/src/ln-toast.js +232 -210
  147. package/js/ln-toast/template.html +16 -12
  148. package/js/ln-toggle/README.md +61 -2
  149. package/js/ln-toggle/ln-toggle-dev.scss +13 -0
  150. package/js/ln-toggle/ln-toggle.js +1 -1
  151. package/js/ln-toggle/src/ln-toggle.js +56 -43
  152. package/js/ln-tooltip/README.md +95 -58
  153. package/js/ln-tooltip/ln-tooltip-dev.scss +9 -0
  154. package/js/ln-tooltip/ln-tooltip.js +1 -1
  155. package/js/ln-tooltip/src/ln-tooltip.js +45 -6
  156. package/js/ln-translations/README.md +118 -96
  157. package/js/ln-translations/ln-translations.js +1 -1
  158. package/js/ln-translations/src/ln-translations.js +10 -5
  159. package/js/ln-upload/README.md +85 -10
  160. package/js/ln-upload/ln-upload.js +1 -1
  161. package/js/ln-upload/src/ln-upload.js +26 -10
  162. package/js/ln-validate/README.md +72 -0
  163. package/js/ln-validate/ln-validate-dev.scss +15 -0
  164. package/js/ln-validate/ln-validate.js +1 -1
  165. package/js/ln-validate/src/ln-validate.js +64 -0
  166. package/package.json +61 -56
  167. package/scss/base/_global.scss +2 -2
  168. package/scss/base/_typography.scss +1 -2
  169. package/scss/components/_date.scss +1 -0
  170. package/scss/components/_dropdown.scss +0 -5
  171. package/scss/components/_editor.scss +5 -0
  172. package/scss/components/_filter.scss +26 -25
  173. package/scss/components/_form.scss +7 -0
  174. package/scss/components/_ln-table.scss +13 -3
  175. package/scss/components/_sort.scss +10 -0
  176. package/scss/components/_table.scss +36 -23
  177. package/scss/components/_toast.scss +68 -41
  178. package/scss/components/_toggle.scss +16 -0
  179. package/scss/config/_density.scss +205 -117
  180. package/scss/config/_icons.scss +10 -9
  181. package/scss/config/_tokens.scss +69 -47
  182. package/scss/config/mixins/_accordion.scss +2 -7
  183. package/scss/config/mixins/_alert.scss +4 -4
  184. package/scss/config/mixins/_breadcrumbs.scss +2 -2
  185. package/scss/config/mixins/_btn.scss +1 -1
  186. package/scss/config/mixins/_card.scss +89 -14
  187. package/scss/config/mixins/_collapsible.scss +3 -0
  188. package/scss/config/mixins/_colors.scss +1 -1
  189. package/scss/config/mixins/_diagnostics.scss +11 -0
  190. package/scss/config/mixins/_display.scss +1 -0
  191. package/scss/config/mixins/_dropdown.scss +3 -5
  192. package/scss/config/mixins/_editor.scss +124 -0
  193. package/scss/config/mixins/_empty-state.scss +7 -0
  194. package/scss/config/mixins/_filter.scss +3 -5
  195. package/scss/config/mixins/_footer.scss +1 -2
  196. package/scss/config/mixins/_form.scss +207 -21
  197. package/scss/config/mixins/_index.scss +2 -0
  198. package/scss/config/mixins/_ln-table.scss +41 -2
  199. package/scss/config/mixins/_modal.scss +149 -66
  200. package/scss/config/mixins/_nav.scss +3 -4
  201. package/scss/config/mixins/_popover.scss +39 -0
  202. package/scss/config/mixins/_sidebar.scss +1 -1
  203. package/scss/config/mixins/_sort.scss +42 -0
  204. package/scss/config/mixins/_table.scss +85 -60
  205. package/scss/config/mixins/_toast.scss +179 -152
  206. package/scss/config/mixins/_tooltip.scss +17 -7
  207. package/scss/config/mixins/_typography.scss +4 -2
  208. package/scss/ln-ashlar-dev.scss +27 -9
  209. package/scss/ln-ashlar.scss +7 -1
  210. package/js/ln-data-table/README.md +0 -110
  211. package/js/ln-data-table/ln-data-table.js +0 -1
  212. package/js/ln-data-table/ln-data-table.scss +0 -10
  213. package/js/ln-data-table/src/ln-data-table.js +0 -1087
  214. package/js/ln-icons/README.md +0 -88
  215. package/js/ln-icons/ln-icons.js +0 -1
  216. package/js/ln-table/ln-table-sort.js +0 -1
  217. package/js/ln-table/src/ln-table-sort.js +0 -156
@@ -1,192 +1,223 @@
1
- import { registerComponent } from '../../ln-core';
2
-
3
- (function () {
4
- const DOM_SELECTOR = 'data-ln-time';
5
- const DOM_ATTRIBUTE = 'lnTime';
6
-
7
- if (window[DOM_ATTRIBUTE] !== undefined) return;
8
-
9
- // ─── Formatter Cache ──────────────────────────────────────
10
- const _formatters = {};
11
- const _relativeFormatters = {};
12
-
13
- function _getLocale(el) {
14
- return el.getAttribute('data-ln-time-locale')
15
- || document.documentElement.lang
16
- || undefined;
17
- }
18
-
19
- function _getFormatter(locale, options) {
20
- const key = (locale || '') + '|' + JSON.stringify(options);
21
- if (!_formatters[key]) {
22
- _formatters[key] = new Intl.DateTimeFormat(locale, options);
23
- }
24
- return _formatters[key];
25
- }
26
-
27
- function _getRelativeFormatter(locale) {
28
- const key = locale || '';
29
- if (!_relativeFormatters[key]) {
30
- _relativeFormatters[key] = new Intl.RelativeTimeFormat(locale, { numeric: 'auto', style: 'narrow' });
31
- }
32
- return _relativeFormatters[key];
33
- }
34
-
35
- // ─── Auto-Update Pool ─────────────────────────────────────
36
- const _relativeElements = new Set();
37
- let _intervalId = null;
38
-
39
- function _startInterval() {
40
- if (_intervalId) return;
41
- _intervalId = setInterval(_tickRelative, 60000);
42
- }
43
-
44
- function _stopInterval() {
45
- if (_intervalId) {
46
- clearInterval(_intervalId);
47
- _intervalId = null;
48
- }
49
- }
50
-
51
- function _tickRelative() {
52
- for (const instance of _relativeElements) {
53
- if (!document.body.contains(instance.dom)) {
54
- _relativeElements.delete(instance);
55
- continue;
56
- }
57
- _render(instance);
58
- }
59
- if (_relativeElements.size === 0) _stopInterval();
60
- }
61
-
62
- // ─── Formatting ───────────────────────────────────────────
63
- function _formatFull(date, locale) {
64
- return _getFormatter(locale, { dateStyle: 'long', timeStyle: 'short' }).format(date);
65
- }
66
-
67
- function _formatShort(date, locale) {
68
- const now = new Date();
69
- const options = { month: 'short', day: 'numeric' };
70
- if (date.getFullYear() !== now.getFullYear()) {
71
- options.year = 'numeric';
72
- }
73
- return _getFormatter(locale, options).format(date);
74
- }
75
-
76
- function _formatDate(date, locale) {
77
- return _getFormatter(locale, { dateStyle: 'medium' }).format(date);
78
- }
79
-
80
- function _formatTime(date, locale) {
81
- return _getFormatter(locale, { timeStyle: 'short' }).format(date);
82
- }
83
-
84
- function _formatRelative(date, locale) {
85
- const nowSec = Math.floor(Date.now() / 1000);
86
- const thenSec = Math.floor(date.getTime() / 1000);
87
- const diff = thenSec - nowSec;
88
- const absDiff = Math.abs(diff);
89
-
90
- if (absDiff < 10) return _getRelativeFormatter(locale).format(0, 'second');
91
-
92
- let unit, value;
93
- if (absDiff < 60) {
94
- unit = 'second'; value = diff;
95
- } else if (absDiff < 3600) {
96
- unit = 'minute'; value = Math.round(diff / 60);
97
- } else if (absDiff < 86400) {
98
- unit = 'hour'; value = Math.round(diff / 3600);
99
- } else if (absDiff < 604800) {
100
- unit = 'day'; value = Math.round(diff / 86400);
101
- } else if (absDiff < 2592000) {
102
- unit = 'week'; value = Math.round(diff / 604800);
103
- } else {
104
- return _formatShort(date, locale);
105
- }
106
-
107
- return _getRelativeFormatter(locale).format(value, unit);
108
- }
109
-
110
- // ─── Render ───────────────────────────────────────────────
111
- function _render(instance) {
112
- const raw = instance.dom.getAttribute('datetime');
113
- if (!raw) return;
114
-
115
- const timestamp = Number(raw);
116
- if (isNaN(timestamp)) return;
117
-
118
- const date = new Date(timestamp * 1000);
119
- const mode = instance.dom.getAttribute(DOM_SELECTOR) || 'short';
120
- const locale = _getLocale(instance.dom);
121
- let text;
122
-
123
- switch (mode) {
124
- case 'relative': text = _formatRelative(date, locale); break;
125
- case 'full': text = _formatFull(date, locale); break;
126
- case 'date': text = _formatDate(date, locale); break;
127
- case 'time': text = _formatTime(date, locale); break;
128
- default: text = _formatShort(date, locale); break;
129
- }
130
-
131
- instance.dom.textContent = text;
132
- if (mode !== 'full') {
133
- instance.dom.title = _formatFull(date, locale);
134
- }
135
- }
136
-
137
- // ─── Component ────────────────────────────────────────────
138
- function _constructor(dom) {
139
- this.dom = dom;
140
- _render(this);
141
-
142
- if (dom.getAttribute(DOM_SELECTOR) === 'relative') {
143
- _relativeElements.add(this);
144
- _startInterval();
145
- }
146
-
147
- return this;
148
- }
149
-
150
- _constructor.prototype.render = function () {
151
- _render(this);
152
- };
153
-
154
- _constructor.prototype.destroy = function () {
155
- _relativeElements.delete(this);
156
- if (_relativeElements.size === 0) _stopInterval();
157
- delete this.dom[DOM_ATTRIBUTE];
158
- };
159
-
160
- // ─── Attribute / Mutation Hooks ───────────────────────────
161
-
162
- function _onAttributeChange(el) {
163
- const instance = el[DOM_ATTRIBUTE];
164
- if (!instance) return;
165
- const mode = el.getAttribute(DOM_SELECTOR);
166
- if (mode === 'relative') {
167
- _relativeElements.add(instance);
168
- _startInterval();
169
- } else {
170
- _relativeElements.delete(instance);
171
- if (_relativeElements.size === 0) _stopInterval();
172
- }
173
- _render(instance);
174
- }
175
-
176
- function _onInit(node) {
177
- // Re-render any data-ln-time element in the mutation target subtree.
178
- // Covers `datetime` extra-attribute changes (extras don't fire onAttributeChange).
179
- if (node.nodeType !== 1) return;
180
- if (node.hasAttribute && node.hasAttribute(DOM_SELECTOR) && node[DOM_ATTRIBUTE]) {
181
- _render(node[DOM_ATTRIBUTE]);
182
- }
183
- }
184
-
185
- // ─── Registration ─────────────────────────────────────────
186
-
187
- registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _constructor, 'ln-time', {
188
- extraAttributes: ['datetime'],
189
- onAttributeChange: _onAttributeChange,
190
- onInit: _onInit
191
- });
192
- })();
1
+ import { registerComponent, getLocale, getLocaleFallback } from '../../ln-core';
2
+
3
+ (function () {
4
+ const DOM_SELECTOR = 'data-ln-time';
5
+ const DOM_ATTRIBUTE = 'lnTime';
6
+
7
+ if (window[DOM_ATTRIBUTE] !== undefined) return;
8
+
9
+ // ─── Formatter Cache ──────────────────────────────────────
10
+ const _formatters = {};
11
+ const _relativeFormatters = {};
12
+
13
+ function _resolveLocale(el) {
14
+ return el.getAttribute('data-ln-time-locale') || getLocale(el);
15
+ }
16
+
17
+ function _getFormatter(locale, options) {
18
+ const key = (locale || '') + '|' + JSON.stringify(options);
19
+ if (!_formatters[key]) {
20
+ _formatters[key] = new Intl.DateTimeFormat(locale, options);
21
+ }
22
+ return _formatters[key];
23
+ }
24
+
25
+ function _getRelativeFormatter(locale) {
26
+ const key = locale || '';
27
+ if (!_relativeFormatters[key]) {
28
+ _relativeFormatters[key] = new Intl.RelativeTimeFormat(locale, { numeric: 'auto', style: 'narrow' });
29
+ }
30
+ return _relativeFormatters[key];
31
+ }
32
+
33
+ // ─── Auto-Update Pool ─────────────────────────────────────
34
+ const _relativeElements = new Set();
35
+ let _intervalId = null;
36
+
37
+ function _startInterval() {
38
+ if (!_intervalId) _intervalId = setInterval(_tickRelative, 60000);
39
+ }
40
+
41
+ function _stopInterval() {
42
+ if (_intervalId) {
43
+ clearInterval(_intervalId);
44
+ _intervalId = null;
45
+ }
46
+ }
47
+
48
+ function _tickRelative() {
49
+ for (const instance of _relativeElements) {
50
+ if (!document.body.contains(instance.dom)) {
51
+ _relativeElements.delete(instance);
52
+ continue;
53
+ }
54
+ _render(instance);
55
+ }
56
+ if (_relativeElements.size === 0) _stopInterval();
57
+ }
58
+
59
+ // ─── Formatting ───────────────────────────────────────────
60
+ function _formatFull(date, locale) {
61
+ const fallback = getLocaleFallback(locale);
62
+ const langPrefix = (locale || '').toLowerCase().split('-')[0];
63
+ const formatter = _getFormatter(locale, { dateStyle: 'long', timeStyle: 'short' });
64
+ const resolvedLocale = formatter.resolvedOptions().locale.toLowerCase().split('-')[0];
65
+
66
+ if (fallback && resolvedLocale !== langPrefix && fallback.monthsLong) {
67
+ const month = fallback.monthsLong[date.getMonth()];
68
+ const day = date.getDate();
69
+ const year = date.getFullYear();
70
+ const hours = String(date.getHours()).padStart(2, '0');
71
+ const mins = String(date.getMinutes()).padStart(2, '0');
72
+ return `${day} ${month} ${year} во ${hours}:${mins}`;
73
+ }
74
+ return formatter.format(date);
75
+ }
76
+
77
+ function _formatShort(date, locale) {
78
+ const now = new Date();
79
+ const options = { month: 'short', day: 'numeric' };
80
+ if (date.getFullYear() !== now.getFullYear()) {
81
+ options.year = 'numeric';
82
+ }
83
+ const fallback = getLocaleFallback(locale);
84
+ const langPrefix = (locale || '').toLowerCase().split('-')[0];
85
+ const formatter = _getFormatter(locale, options);
86
+ const resolvedLocale = formatter.resolvedOptions().locale.toLowerCase().split('-')[0];
87
+
88
+ if (fallback && resolvedLocale !== langPrefix && fallback.monthsShort) {
89
+ const month = fallback.monthsShort[date.getMonth()];
90
+ const day = date.getDate();
91
+ const year = date.getFullYear() !== now.getFullYear() ? ' ' + date.getFullYear() : '';
92
+ return `${day} ${month}${year}`;
93
+ }
94
+ return formatter.format(date);
95
+ }
96
+
97
+ function _formatDate(date, locale) {
98
+ return _getFormatter(locale, { dateStyle: 'medium' }).format(date);
99
+ }
100
+
101
+ function _formatTime(date, locale) {
102
+ return _getFormatter(locale, { timeStyle: 'short' }).format(date);
103
+ }
104
+
105
+ function _formatRelative(date, locale) {
106
+ const nowSec = Math.floor(Date.now() / 1000);
107
+ const thenSec = Math.floor(date.getTime() / 1000);
108
+ const diff = thenSec - nowSec;
109
+ const absDiff = Math.abs(diff);
110
+
111
+ if (absDiff < 10) return _getRelativeFormatter(locale).format(0, 'second');
112
+
113
+ let unit, value;
114
+ if (absDiff < 60) {
115
+ unit = 'second'; value = diff;
116
+ } else if (absDiff < 3600) {
117
+ unit = 'minute'; value = Math.round(diff / 60);
118
+ } else if (absDiff < 86400) {
119
+ unit = 'hour'; value = Math.round(diff / 3600);
120
+ } else if (absDiff < 604800) {
121
+ unit = 'day'; value = Math.round(diff / 86400);
122
+ } else if (absDiff < 2592000) {
123
+ unit = 'week'; value = Math.round(diff / 604800);
124
+ } else {
125
+ return _formatShort(date, locale);
126
+ }
127
+
128
+ return _getRelativeFormatter(locale).format(value, unit);
129
+ }
130
+
131
+ // ─── Render ───────────────────────────────────────────────
132
+ function _render(instance) {
133
+ const raw = instance.dom.getAttribute('datetime');
134
+ if (!raw) return;
135
+
136
+ const timestamp = Number(raw);
137
+ if (isNaN(timestamp)) return;
138
+
139
+ const date = new Date(timestamp * 1000);
140
+ const mode = instance.dom.getAttribute(DOM_SELECTOR) || 'short';
141
+ const locale = _resolveLocale(instance.dom);
142
+ let text;
143
+
144
+ switch (mode) {
145
+ case 'relative': text = _formatRelative(date, locale); break;
146
+ case 'full': text = _formatFull(date, locale); break;
147
+ case 'date': text = _formatDate(date, locale); break;
148
+ case 'time': text = _formatTime(date, locale); break;
149
+ default: text = _formatShort(date, locale); break;
150
+ }
151
+
152
+ instance.dom.textContent = text;
153
+ if (mode !== 'full') {
154
+ instance.dom.title = _formatFull(date, locale);
155
+ }
156
+ }
157
+
158
+ // ─── Component ────────────────────────────────────────────
159
+ function _constructor(dom) {
160
+ this.dom = dom;
161
+ _render(this);
162
+
163
+ if (dom.getAttribute(DOM_SELECTOR) === 'relative') {
164
+ _relativeElements.add(this);
165
+ _startInterval();
166
+ }
167
+
168
+ return this;
169
+ }
170
+
171
+ _constructor.prototype.render = function () {
172
+ _render(this);
173
+ };
174
+
175
+ _constructor.prototype.destroy = function () {
176
+ _relativeElements.delete(this);
177
+ if (_relativeElements.size === 0) _stopInterval();
178
+ delete this.dom[DOM_ATTRIBUTE];
179
+ };
180
+
181
+ // ─── Attribute / Mutation Hooks ───────────────────────────
182
+ function _onAttributeChange(el) {
183
+ const instance = el[DOM_ATTRIBUTE];
184
+ if (!instance) return;
185
+ const mode = el.getAttribute(DOM_SELECTOR);
186
+ if (mode === 'relative') {
187
+ _relativeElements.add(instance);
188
+ _startInterval();
189
+ } else {
190
+ _relativeElements.delete(instance);
191
+ if (_relativeElements.size === 0) _stopInterval();
192
+ }
193
+ _render(instance);
194
+ }
195
+
196
+ function _onInit(node) {
197
+ if (node.nodeType !== 1) return;
198
+ if (node.hasAttribute && node.hasAttribute(DOM_SELECTOR) && node[DOM_ATTRIBUTE]) {
199
+ _render(node[DOM_ATTRIBUTE]);
200
+ }
201
+ }
202
+
203
+ // ─── Locale Observer ──────────────────────────────────────
204
+ function _localeObserver() {
205
+ new MutationObserver(function () {
206
+ const els = document.querySelectorAll('[' + DOM_SELECTOR + ']');
207
+ for (let i = 0; i < els.length; i++) {
208
+ const inst = els[i][DOM_ATTRIBUTE];
209
+ if (inst) {
210
+ _render(inst);
211
+ }
212
+ }
213
+ }).observe(document.documentElement, { attributes: true, attributeFilter: ['lang'], subtree: true });
214
+ }
215
+
216
+ // ─── Registration ─────────────────────────────────────────
217
+ registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _constructor, 'ln-time', {
218
+ extraAttributes: ['datetime', 'data-ln-time-locale', 'lang'],
219
+ onAttributeChange: _onAttributeChange,
220
+ onInit: _onInit
221
+ });
222
+ _localeObserver();
223
+ })();