@sondor-technology/sondor-components 1.0.5 → 1.0.7

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 (226) hide show
  1. package/dist/components/alert/alert.constants.d.ts +3 -0
  2. package/dist/components/alert/alert.constants.d.ts.map +1 -0
  3. package/dist/components/alert/alert.constants.js +2 -0
  4. package/dist/components/alert/alert.constants.js.map +1 -0
  5. package/dist/components/alert/alert.d.ts +4 -3
  6. package/dist/components/alert/alert.d.ts.map +1 -1
  7. package/dist/components/alert/alert.js +21 -11
  8. package/dist/components/alert/alert.js.map +1 -0
  9. package/dist/components/badge/badge.constants.d.ts +3 -0
  10. package/dist/components/badge/badge.constants.d.ts.map +1 -0
  11. package/dist/components/badge/badge.constants.js +2 -0
  12. package/dist/components/badge/badge.constants.js.map +1 -0
  13. package/dist/components/badge/badge.d.ts +1 -1
  14. package/dist/components/badge/badge.d.ts.map +1 -1
  15. package/dist/components/badge/badge.js +3 -2
  16. package/dist/components/badge/badge.js.map +1 -0
  17. package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -1
  18. package/dist/components/breadcrumb/breadcrumb.js +2 -1
  19. package/dist/components/breadcrumb/breadcrumb.js.map +1 -0
  20. package/dist/components/button/button.constants.d.ts +7 -0
  21. package/dist/components/button/button.constants.d.ts.map +1 -0
  22. package/dist/components/button/button.constants.js +4 -0
  23. package/dist/components/button/button.constants.js.map +1 -0
  24. package/dist/components/button/button.d.ts +8 -4
  25. package/dist/components/button/button.d.ts.map +1 -1
  26. package/dist/components/button/button.js +181 -137
  27. package/dist/components/button/button.js.map +1 -0
  28. package/dist/components/button-group/button-group.d.ts +1 -0
  29. package/dist/components/button-group/button-group.d.ts.map +1 -1
  30. package/dist/components/button-group/button-group.js +17 -21
  31. package/dist/components/button-group/button-group.js.map +1 -0
  32. package/dist/components/checkbox/checkbox.constants.d.ts +3 -0
  33. package/dist/components/checkbox/checkbox.constants.d.ts.map +1 -0
  34. package/dist/components/checkbox/checkbox.constants.js +2 -0
  35. package/dist/components/checkbox/checkbox.constants.js.map +1 -0
  36. package/dist/components/checkbox/checkbox.d.ts +5 -1
  37. package/dist/components/checkbox/checkbox.d.ts.map +1 -1
  38. package/dist/components/checkbox/checkbox.js +21 -5
  39. package/dist/components/checkbox/checkbox.js.map +1 -0
  40. package/dist/components/code-block/code-block.d.ts +7 -7
  41. package/dist/components/code-block/code-block.d.ts.map +1 -1
  42. package/dist/components/code-block/code-block.js +130 -66
  43. package/dist/components/code-block/code-block.js.map +1 -0
  44. package/dist/components/content-card/content-card.d.ts.map +1 -1
  45. package/dist/components/content-card/content-card.js +10 -15
  46. package/dist/components/content-card/content-card.js.map +1 -0
  47. package/dist/components/divider/divider.constants.d.ts +5 -0
  48. package/dist/components/divider/divider.constants.d.ts.map +1 -0
  49. package/dist/components/divider/divider.constants.js +3 -0
  50. package/dist/components/divider/divider.constants.js.map +1 -0
  51. package/dist/components/divider/divider.d.ts +4 -2
  52. package/dist/components/divider/divider.d.ts.map +1 -1
  53. package/dist/components/divider/divider.js +18 -9
  54. package/dist/components/divider/divider.js.map +1 -0
  55. package/dist/components/drawer/drawer.d.ts +7 -4
  56. package/dist/components/drawer/drawer.d.ts.map +1 -1
  57. package/dist/components/drawer/drawer.js +73 -30
  58. package/dist/components/drawer/drawer.js.map +1 -0
  59. package/dist/components/field/field.d.ts +2 -2
  60. package/dist/components/field/field.d.ts.map +1 -1
  61. package/dist/components/field/field.js +8 -9
  62. package/dist/components/field/field.js.map +1 -0
  63. package/dist/components/footer/footer.d.ts +8 -1
  64. package/dist/components/footer/footer.d.ts.map +1 -1
  65. package/dist/components/footer/footer.js +42 -12
  66. package/dist/components/footer/footer.js.map +1 -0
  67. package/dist/components/form/form-step.js +1 -0
  68. package/dist/components/form/form-step.js.map +1 -0
  69. package/dist/components/form/form.d.ts +3 -0
  70. package/dist/components/form/form.d.ts.map +1 -1
  71. package/dist/components/form/form.js +109 -69
  72. package/dist/components/form/form.js.map +1 -0
  73. package/dist/components/grid/grid-row.d.ts +3 -3
  74. package/dist/components/grid/grid-row.d.ts.map +1 -1
  75. package/dist/components/grid/grid-row.js +36 -22
  76. package/dist/components/grid/grid-row.js.map +1 -0
  77. package/dist/components/grid/grid.d.ts +2 -2
  78. package/dist/components/grid/grid.d.ts.map +1 -1
  79. package/dist/components/grid/grid.js +5 -2
  80. package/dist/components/grid/grid.js.map +1 -0
  81. package/dist/components/icon/icon-paths.d.ts +161 -86
  82. package/dist/components/icon/icon-paths.d.ts.map +1 -1
  83. package/dist/components/icon/icon-paths.js +176 -106
  84. package/dist/components/icon/icon-paths.js.map +1 -0
  85. package/dist/components/icon/icon.d.ts +1 -1
  86. package/dist/components/icon/icon.d.ts.map +1 -1
  87. package/dist/components/icon/icon.js +6 -5
  88. package/dist/components/icon/icon.js.map +1 -0
  89. package/dist/components/input/input.d.ts +9 -0
  90. package/dist/components/input/input.d.ts.map +1 -1
  91. package/dist/components/input/input.js +30 -37
  92. package/dist/components/input/input.js.map +1 -0
  93. package/dist/components/link/link.d.ts +6 -6
  94. package/dist/components/link/link.d.ts.map +1 -1
  95. package/dist/components/link/link.js +42 -19
  96. package/dist/components/link/link.js.map +1 -0
  97. package/dist/components/metadata/metadata.d.ts +3 -3
  98. package/dist/components/metadata/metadata.d.ts.map +1 -1
  99. package/dist/components/metadata/metadata.js +23 -20
  100. package/dist/components/metadata/metadata.js.map +1 -0
  101. package/dist/components/modal/modal.d.ts +4 -16
  102. package/dist/components/modal/modal.d.ts.map +1 -1
  103. package/dist/components/modal/modal.js +99 -62
  104. package/dist/components/modal/modal.js.map +1 -0
  105. package/dist/components/page-layout/page-layout.d.ts +4 -3
  106. package/dist/components/page-layout/page-layout.d.ts.map +1 -1
  107. package/dist/components/page-layout/page-layout.js +95 -68
  108. package/dist/components/page-layout/page-layout.js.map +1 -0
  109. package/dist/components/radio/radio.d.ts +8 -0
  110. package/dist/components/radio/radio.d.ts.map +1 -1
  111. package/dist/components/radio/radio.js +8 -12
  112. package/dist/components/radio/radio.js.map +1 -0
  113. package/dist/components/radio-group/radio-group.d.ts +6 -1
  114. package/dist/components/radio-group/radio-group.d.ts.map +1 -1
  115. package/dist/components/radio-group/radio-group.js +44 -18
  116. package/dist/components/radio-group/radio-group.js.map +1 -0
  117. package/dist/components/router/router.d.ts +5 -7
  118. package/dist/components/router/router.d.ts.map +1 -1
  119. package/dist/components/router/router.js +54 -38
  120. package/dist/components/router/router.js.map +1 -0
  121. package/dist/components/select/select.d.ts +1 -3
  122. package/dist/components/select/select.d.ts.map +1 -1
  123. package/dist/components/select/select.js +14 -5
  124. package/dist/components/select/select.js.map +1 -0
  125. package/dist/components/skeleton/skeleton.d.ts +3 -2
  126. package/dist/components/skeleton/skeleton.d.ts.map +1 -1
  127. package/dist/components/skeleton/skeleton.js +10 -13
  128. package/dist/components/skeleton/skeleton.js.map +1 -0
  129. package/dist/components/switch/switch.d.ts +12 -0
  130. package/dist/components/switch/switch.d.ts.map +1 -1
  131. package/dist/components/switch/switch.js +28 -22
  132. package/dist/components/switch/switch.js.map +1 -0
  133. package/dist/components/table/data-table.d.ts +3 -25
  134. package/dist/components/table/data-table.d.ts.map +1 -1
  135. package/dist/components/table/data-table.js +211 -146
  136. package/dist/components/table/data-table.js.map +1 -0
  137. package/dist/components/table/table.d.ts +3 -3
  138. package/dist/components/table/table.d.ts.map +1 -1
  139. package/dist/components/table/table.js +39 -20
  140. package/dist/components/table/table.js.map +1 -0
  141. package/dist/components/tabs/tab-panel.d.ts +3 -1
  142. package/dist/components/tabs/tab-panel.d.ts.map +1 -1
  143. package/dist/components/tabs/tab-panel.js +21 -11
  144. package/dist/components/tabs/tab-panel.js.map +1 -0
  145. package/dist/components/tabs/tab.d.ts +5 -1
  146. package/dist/components/tabs/tab.d.ts.map +1 -1
  147. package/dist/components/tabs/tab.js +46 -28
  148. package/dist/components/tabs/tab.js.map +1 -0
  149. package/dist/components/tabs/tabs.d.ts +4 -0
  150. package/dist/components/tabs/tabs.d.ts.map +1 -1
  151. package/dist/components/tabs/tabs.js +76 -13
  152. package/dist/components/tabs/tabs.js.map +1 -0
  153. package/dist/components/textarea/textarea.d.ts +13 -0
  154. package/dist/components/textarea/textarea.d.ts.map +1 -1
  155. package/dist/components/textarea/textarea.js +59 -13
  156. package/dist/components/textarea/textarea.js.map +1 -0
  157. package/dist/components/theme-toggle/theme-toggle.constants.d.ts +3 -0
  158. package/dist/components/theme-toggle/theme-toggle.constants.d.ts.map +1 -0
  159. package/dist/components/theme-toggle/theme-toggle.constants.js +2 -0
  160. package/dist/components/theme-toggle/theme-toggle.constants.js.map +1 -0
  161. package/dist/components/theme-toggle/theme-toggle.d.ts +3 -2
  162. package/dist/components/theme-toggle/theme-toggle.d.ts.map +1 -1
  163. package/dist/components/theme-toggle/theme-toggle.js +46 -19
  164. package/dist/components/theme-toggle/theme-toggle.js.map +1 -0
  165. package/dist/components/vertical-nav/vertical-nav-group.d.ts +5 -2
  166. package/dist/components/vertical-nav/vertical-nav-group.d.ts.map +1 -1
  167. package/dist/components/vertical-nav/vertical-nav-group.js +50 -14
  168. package/dist/components/vertical-nav/vertical-nav-group.js.map +1 -0
  169. package/dist/components/vertical-nav/vertical-nav-item.d.ts +3 -2
  170. package/dist/components/vertical-nav/vertical-nav-item.d.ts.map +1 -1
  171. package/dist/components/vertical-nav/vertical-nav-item.js +30 -8
  172. package/dist/components/vertical-nav/vertical-nav-item.js.map +1 -0
  173. package/dist/styles/index.css +48 -17
  174. package/dist/styles/reset.js +1 -0
  175. package/dist/styles/reset.js.map +1 -0
  176. package/package.json +2 -1
  177. package/src/components/alert/alert.constants.ts +2 -0
  178. package/src/components/alert/alert.ts +24 -15
  179. package/src/components/badge/badge.constants.ts +2 -0
  180. package/src/components/badge/badge.ts +3 -4
  181. package/src/components/breadcrumb/breadcrumb.ts +2 -2
  182. package/src/components/button/button.constants.ts +8 -0
  183. package/src/components/button/button.ts +184 -144
  184. package/src/components/button-group/button-group.ts +18 -22
  185. package/src/components/checkbox/checkbox.constants.ts +2 -0
  186. package/src/components/checkbox/checkbox.ts +33 -18
  187. package/src/components/code-block/code-block.ts +151 -84
  188. package/src/components/content-card/content-card.ts +10 -16
  189. package/src/components/divider/divider.constants.ts +5 -0
  190. package/src/components/divider/divider.ts +22 -11
  191. package/src/components/drawer/drawer.ts +79 -34
  192. package/src/components/field/field.ts +9 -11
  193. package/src/components/footer/footer.ts +43 -12
  194. package/src/components/form/form.ts +121 -84
  195. package/src/components/grid/grid-row.ts +45 -32
  196. package/src/components/grid/grid.ts +7 -4
  197. package/src/components/icon/icon-paths.ts +175 -106
  198. package/src/components/icon/icon.ts +6 -6
  199. package/src/components/input/input.ts +32 -42
  200. package/src/components/link/link.ts +45 -21
  201. package/src/components/metadata/metadata.ts +26 -24
  202. package/src/components/modal/modal.ts +108 -74
  203. package/src/components/page-layout/page-layout.ts +97 -72
  204. package/src/components/radio/radio.ts +8 -13
  205. package/src/components/radio-group/radio-group.ts +45 -21
  206. package/src/components/router/router.ts +73 -50
  207. package/src/components/select/select.ts +18 -19
  208. package/src/components/skeleton/skeleton.ts +14 -14
  209. package/src/components/switch/switch.ts +31 -25
  210. package/src/components/table/data-table.ts +248 -177
  211. package/src/components/table/table.ts +52 -29
  212. package/src/components/tabs/tab-panel.ts +25 -13
  213. package/src/components/tabs/tab.ts +58 -33
  214. package/src/components/tabs/tabs.ts +77 -15
  215. package/src/components/textarea/textarea.ts +63 -15
  216. package/src/components/theme-toggle/theme-toggle.constants.ts +2 -0
  217. package/src/components/theme-toggle/theme-toggle.ts +47 -22
  218. package/src/components/vertical-nav/vertical-nav-group.ts +57 -18
  219. package/src/components/vertical-nav/vertical-nav-item.ts +35 -12
  220. package/src/styles/globals.css +27 -4
  221. package/src/styles/theme.css +38 -32
  222. package/tsconfig.json +2 -1
  223. package/dist/components/card/card.d.ts +0 -11
  224. package/dist/components/card/card.d.ts.map +0 -1
  225. package/dist/components/card/card.js +0 -54
  226. package/src/components/card/card.ts +0 -53
@@ -1,7 +1,8 @@
1
- import { LitElement, html, css, PropertyValues } from 'lit';
2
- import { customElement, property, state } from 'lit/decorators.js';
3
- import { classMap } from 'lit/directives/class-map.js';
4
- import { resetStyles } from '../../styles/reset.js';
1
+ import {LitElement, html, css, PropertyValues} from "lit";
2
+ import {customElement, property, state} from "lit/decorators.js";
3
+ import {classMap} from "lit/directives/class-map.js";
4
+ import {resetStyles} from "../../styles/reset.js";
5
+ import {ifDefined} from "lit/directives/if-defined.js";
5
6
 
6
7
  export interface DataTableColumn {
7
8
  header: string;
@@ -12,52 +13,24 @@ export interface DataTableColumn {
12
13
 
13
14
  export interface SortConfig {
14
15
  key: string;
15
- direction: 'asc' | 'desc';
16
+ direction: "asc" | "desc";
16
17
  }
17
18
 
18
- @customElement('st-data-table')
19
+ @customElement("st-data-table")
19
20
  export class StDataTable extends LitElement {
20
- /**
21
- * Array defining table headers, keys, and sortability.
22
- */
23
- @property({ type: Array }) columns: DataTableColumn[] = [];
24
-
25
- /**
26
- * Array of row data objects rendered in the table.
27
- */
28
- @property({ type: Array }) data: Record<string, any>[] = [];
29
-
30
- /**
31
- * Total number of rows available on the server (used for pagination).
32
- */
33
- @property({ type: Number, attribute: 'total-rows' }) totalRows = 0;
34
-
35
- /**
36
- * Current page number (1-indexed).
37
- */
38
- @property({ type: Number }) page = 1;
39
-
40
- /**
41
- * Number of rows per page.
42
- */
43
- @property({ type: Number, attribute: 'page-size' }) pageSize = 10;
44
-
45
- /**
46
- * Enables holding 'Shift' to sort by multiple columns sequentially.
47
- */
48
- @property({ type: Boolean, attribute: 'multi-sort' }) multiSort = false;
49
-
50
- /**
51
- * Displays loading skeleton rows and locks controls.
52
- */
53
- @property({ type: Boolean, reflect: true }) loading = false;
54
-
55
- // --- Internal State ---
21
+ @property({type: Array}) columns: DataTableColumn[] = [];
22
+ @property({type: Array}) data: Record<string, any>[] = [];
23
+ @property({type: Number, attribute: "total-rows"}) totalRows = 0;
24
+ @property({type: Number}) page = 1;
25
+ @property({type: Number, attribute: "page-size"}) pageSize = 10;
26
+ @property({type: Boolean, attribute: "multi-sort"}) multiSort = false;
27
+ @property({type: Boolean, reflect: true}) loading = false;
28
+
56
29
  @state() private internalColumns: DataTableColumn[] = [];
57
30
  @state() private sortState: SortConfig[] = [];
58
- @state() private searchQuery = '';
31
+ @state() private searchQuery = "";
59
32
  @state() private showColumnMenu = false;
60
-
33
+
61
34
  private searchTimeout?: number;
62
35
  private draggedColumnKey: string | null = null;
63
36
 
@@ -251,59 +224,60 @@ export class StDataTable extends LitElement {
251
224
  .drag-handle:hover {
252
225
  opacity: 0.7;
253
226
  }
254
- `
227
+ `,
255
228
  ];
256
229
 
257
- willUpdate(changedProperties: PropertyValues) {
258
- if (changedProperties.has('columns') && this.columns.length > 0) {
259
- if (this.internalColumns.length === 0) {
260
- this.internalColumns = [...this.columns];
261
- }
262
- }
263
- }
264
-
265
- firstUpdated() {
266
- if (this.internalColumns.length === 0 && this.columns.length > 0) {
267
- this.internalColumns = [...this.columns];
230
+ override willUpdate(changedProperties: PropertyValues) {
231
+ // 🚀 FIX: Properly sync dynamically updated columns while preserving user visibility states
232
+ if (changedProperties.has("columns") && this.columns) {
233
+ this.internalColumns = this.columns.map((newCol) => {
234
+ const existing = this.internalColumns.find((ic) => ic.key === newCol.key);
235
+ return {...newCol, hidden: existing ? existing.hidden : newCol.hidden};
236
+ });
268
237
  }
269
- this.emitChange();
270
238
  }
271
239
 
272
240
  connectedCallback() {
273
241
  super.connectedCallback();
274
- document.addEventListener('click', this.handleOutsideClick);
242
+ document.addEventListener("click", this.handleOutsideClick);
275
243
  }
276
244
 
277
245
  disconnectedCallback() {
278
- document.removeEventListener('click', this.handleOutsideClick);
246
+ document.removeEventListener("click", this.handleOutsideClick);
279
247
  super.disconnectedCallback();
280
248
  }
281
249
 
282
- private handleOutsideClick = (e: MouseEvent) => {
283
- const target = e.target as HTMLElement;
284
- if (!target.closest('.columns-toggle-wrapper')) {
250
+ private handleOutsideClick = (e: Event) => {
251
+ const path = e.composedPath();
252
+ const toggleWrapper = this.shadowRoot?.querySelector(".columns-toggle-wrapper");
253
+
254
+ // 🚀 THE FIX: Use composedPath() to pierce the Shadow DOM and check if
255
+ // the click originated from inside the wrapper element.
256
+ if (toggleWrapper && !path.includes(toggleWrapper)) {
285
257
  this.showColumnMenu = false;
286
258
  }
287
259
  };
288
260
 
289
261
  private emitChange() {
290
- this.dispatchEvent(new CustomEvent('st-data-change', {
291
- detail: {
292
- page: this.page,
293
- pageSize: this.pageSize,
294
- search: this.searchQuery,
295
- sort: this.sortState
296
- },
297
- bubbles: true,
298
- composed: true
299
- }));
262
+ this.dispatchEvent(
263
+ new CustomEvent("st-data-change", {
264
+ detail: {
265
+ page: this.page,
266
+ pageSize: this.pageSize,
267
+ search: this.searchQuery,
268
+ sort: this.sortState,
269
+ },
270
+ bubbles: true,
271
+ composed: true,
272
+ }),
273
+ );
300
274
  }
301
275
 
302
276
  private handleSearchInput(e: Event) {
303
277
  if (this.loading) return;
304
278
  const val = (e.target as HTMLInputElement).value;
305
279
  window.clearTimeout(this.searchTimeout);
306
-
280
+
307
281
  this.searchTimeout = window.setTimeout(() => {
308
282
  this.searchQuery = val;
309
283
  this.page = 1;
@@ -314,11 +288,11 @@ export class StDataTable extends LitElement {
314
288
  private handleSort(key: string, e: MouseEvent) {
315
289
  if (this.loading) return;
316
290
  const isShift = e.shiftKey && this.multiSort;
317
- const existingIndex = this.sortState.findIndex(s => s.key === key);
318
- let newDirection: 'asc' | 'desc' | null = 'asc';
291
+ const existingIndex = this.sortState.findIndex((s) => s.key === key);
292
+ let newDirection: "asc" | "desc" | null = "asc";
319
293
 
320
294
  if (existingIndex !== -1) {
321
- newDirection = this.sortState[existingIndex].direction === 'asc' ? 'desc' : null;
295
+ newDirection = this.sortState[existingIndex].direction === "asc" ? "desc" : null;
322
296
  }
323
297
 
324
298
  let newSortState = [...this.sortState];
@@ -328,14 +302,14 @@ export class StDataTable extends LitElement {
328
302
  if (existingIndex !== -1) {
329
303
  newSortState[existingIndex].direction = newDirection;
330
304
  } else {
331
- newSortState.push({ key, direction: newDirection });
305
+ newSortState.push({key, direction: newDirection});
332
306
  }
333
307
  } else {
334
308
  newSortState.splice(existingIndex, 1);
335
309
  }
336
310
  } else {
337
311
  if (newDirection) {
338
- newSortState = [{ key, direction: newDirection }];
312
+ newSortState = [{key, direction: newDirection}];
339
313
  } else {
340
314
  newSortState = [];
341
315
  }
@@ -365,41 +339,41 @@ export class StDataTable extends LitElement {
365
339
  }
366
340
 
367
341
  private toggleColumnVisibility(key: string) {
368
- this.internalColumns = this.internalColumns.map(col =>
369
- col.key === key ? { ...col, hidden: !col.hidden } : col
342
+ this.internalColumns = this.internalColumns.map((col) =>
343
+ col.key === key ? {...col, hidden: !col.hidden} : col,
370
344
  );
371
345
  }
372
346
 
373
347
  private handleDragStart(e: DragEvent, key: string) {
374
348
  this.draggedColumnKey = key;
375
349
  if (e.dataTransfer) {
376
- e.dataTransfer.effectAllowed = 'move';
377
- e.dataTransfer.setData('text/plain', key);
350
+ e.dataTransfer.effectAllowed = "move";
351
+ e.dataTransfer.setData("text/plain", key);
378
352
  }
379
353
  }
380
354
 
381
355
  private handleDragOver(e: DragEvent) {
382
356
  e.preventDefault();
383
- if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';
384
- const target = (e.target as HTMLElement).closest('th');
385
- if (target) target.classList.add('drag-over');
357
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
358
+ const target = (e.target as HTMLElement).closest("th");
359
+ if (target) target.classList.add("drag-over");
386
360
  }
387
361
 
388
362
  private handleDragLeave(e: DragEvent) {
389
- const target = (e.target as HTMLElement).closest('th');
390
- if (target) target.classList.remove('drag-over');
363
+ const target = (e.target as HTMLElement).closest("th");
364
+ if (target) target.classList.remove("drag-over");
391
365
  }
392
366
 
393
367
  private handleDrop(e: DragEvent, targetKey: string) {
394
368
  e.preventDefault();
395
- const targetEl = (e.target as HTMLElement).closest('th');
396
- if (targetEl) targetEl.classList.remove('drag-over');
369
+ const targetEl = (e.target as HTMLElement).closest("th");
370
+ if (targetEl) targetEl.classList.remove("drag-over");
397
371
 
398
372
  if (!this.draggedColumnKey || this.draggedColumnKey === targetKey) return;
399
373
 
400
374
  const newCols = [...this.internalColumns];
401
- const fromIndex = newCols.findIndex(c => c.key === this.draggedColumnKey);
402
- const toIndex = newCols.findIndex(c => c.key === targetKey);
375
+ const fromIndex = newCols.findIndex((c) => c.key === this.draggedColumnKey);
376
+ const toIndex = newCols.findIndex((c) => c.key === targetKey);
403
377
 
404
378
  const [movedCol] = newCols.splice(fromIndex, 1);
405
379
  newCols.splice(toIndex, 0, movedCol);
@@ -409,136 +383,233 @@ export class StDataTable extends LitElement {
409
383
  }
410
384
 
411
385
  private renderSortIcon(key: string) {
412
- const sortIndex = this.sortState.findIndex(s => s.key === key);
386
+ const sortIndex = this.sortState.findIndex((s) => s.key === key);
413
387
  if (sortIndex === -1) {
414
- return html`<svg class="sort-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>`;
388
+ return html`<svg
389
+ class="sort-icon"
390
+ viewBox="0 0 24 24"
391
+ fill="none"
392
+ stroke="currentColor"
393
+ stroke-width="2"
394
+ stroke-linecap="round"
395
+ stroke-linejoin="round"
396
+ >
397
+ <path d="m7 15 5 5 5-5" />
398
+ <path d="m7 9 5-5 5 5" />
399
+ </svg>`;
415
400
  }
416
401
 
417
402
  const sortConfig = this.sortState[sortIndex];
418
- const icon = sortConfig.direction === 'asc'
419
- ? html`<svg class="sort-icon active" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 15-6-6-6 6"/></svg>`
420
- : html`<svg class="sort-icon active" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>`;
403
+ const icon =
404
+ sortConfig.direction === "asc"
405
+ ? html`<svg
406
+ class="sort-icon active"
407
+ viewBox="0 0 24 24"
408
+ fill="none"
409
+ stroke="currentColor"
410
+ stroke-width="2"
411
+ stroke-linecap="round"
412
+ stroke-linejoin="round"
413
+ >
414
+ <path d="m18 15-6-6-6 6" />
415
+ </svg>`
416
+ : html`<svg
417
+ class="sort-icon active"
418
+ viewBox="0 0 24 24"
419
+ fill="none"
420
+ stroke="currentColor"
421
+ stroke-width="2"
422
+ stroke-linecap="round"
423
+ stroke-linejoin="round"
424
+ >
425
+ <path d="m6 9 6 6 6-6" />
426
+ </svg>`;
421
427
 
422
- const priority = this.sortState.length > 1 && this.multiSort
423
- ? html`<span class="sort-priority">${sortIndex + 1}</span>`
424
- : '';
428
+ const priority =
429
+ this.sortState.length > 1 && this.multiSort
430
+ ? html`<span class="sort-priority">${sortIndex + 1}</span>`
431
+ : "";
425
432
 
426
433
  return html`${icon}${priority}`;
427
434
  }
428
435
 
429
436
  render() {
430
- const visibleColumns = this.internalColumns.filter(c => !c.hidden);
437
+ const visibleColumns = this.internalColumns.filter((c) => !c.hidden);
431
438
  const skeletonRowsCount = Math.min(this.pageSize, 5);
432
439
 
433
440
  return html`
434
- <!-- Toolbar -->
435
441
  <div class="toolbar">
436
442
  <div class="search-wrapper">
437
- <svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
438
- <input
439
- type="text"
440
- class="search-input"
441
- placeholder="Search..."
443
+ <svg
444
+ class="search-icon"
445
+ aria-hidden="true"
446
+ viewBox="0 0 24 24"
447
+ fill="none"
448
+ stroke="currentColor"
449
+ stroke-width="2"
450
+ stroke-linecap="round"
451
+ stroke-linejoin="round"
452
+ >
453
+ <circle cx="11" cy="11" r="8"></circle>
454
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
455
+ </svg>
456
+ <input
457
+ type="text"
458
+ class="search-input"
459
+ placeholder="Search..."
460
+ aria-label="Search data table"
461
+ spellcheck="false"
462
+ autocapitalize="none"
442
463
  ?disabled=${this.loading}
443
464
  @input=${this.handleSearchInput}
444
- >
465
+ />
445
466
  </div>
446
467
 
447
468
  <div class="columns-toggle-wrapper">
448
- <st-button
449
- variant="secondary"
450
- size="sm"
451
- @click=${() => this.showColumnMenu = !this.showColumnMenu}
469
+ <st-button
470
+ variant="secondary"
471
+ size="sm"
472
+ aria-haspopup="menu"
473
+ aria-expanded=${this.showColumnMenu}
474
+ @click=${() => (this.showColumnMenu = !this.showColumnMenu)}
452
475
  >
453
476
  Columns
454
477
  </st-button>
455
-
456
- ${this.showColumnMenu ? html`
457
- <div class="columns-menu">
458
- ${this.internalColumns.map(col => html`
459
- <label class="column-option">
460
- <input
461
- type="checkbox"
462
- .checked=${!col.hidden}
463
- @change=${() => this.toggleColumnVisibility(col.key)}
464
- >
465
- ${col.header}
466
- </label>
467
- `)}
468
- </div>
469
- ` : ''}
478
+
479
+ ${this.showColumnMenu
480
+ ? html`
481
+ <div class="columns-menu" role="menu">
482
+ ${this.internalColumns.map(
483
+ (col) => html`
484
+ <label
485
+ class="column-option"
486
+ role="menuitemcheckbox"
487
+ aria-checked=${!col.hidden}
488
+ >
489
+ <input
490
+ type="checkbox"
491
+ .checked=${!col.hidden}
492
+ @change=${() => this.toggleColumnVisibility(col.key)}
493
+ />
494
+ ${col.header}
495
+ </label>
496
+ `,
497
+ )}
498
+ </div>
499
+ `
500
+ : ""}
470
501
  </div>
471
502
  </div>
472
503
 
473
- <!-- Table -->
474
504
  <div class="table-wrapper">
475
- <table>
505
+ <table aria-busy=${this.loading}>
476
506
  <thead>
477
507
  <tr>
478
- ${visibleColumns.map(col => html`
479
- <th
480
- draggable="true"
481
- @dragstart=${(e: DragEvent) => this.handleDragStart(e, col.key)}
482
- @dragover=${this.handleDragOver}
483
- @dragleave=${this.handleDragLeave}
484
- @drop=${(e: DragEvent) => this.handleDrop(e, col.key)}
485
- >
486
- <div class="th-content">
487
- <svg class="drag-handle" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="12" r="1"/><circle cx="9" cy="5" r="1"/><circle cx="9" cy="19" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="15" cy="5" r="1"/><circle cx="15" cy="19" r="1"/></svg>
488
-
489
- <div
490
- class=${classMap({ 'sortable-header': !!col.sortable })}
491
- @click=${(e: MouseEvent) => col.sortable && this.handleSort(col.key, e)}
492
- >
493
- ${col.header}
494
- ${col.sortable ? this.renderSortIcon(col.key) : ''}
508
+ ${visibleColumns.map((col) => {
509
+ const sortItem = this.sortState.find((s) => s.key === col.key);
510
+ const ariaSort = sortItem
511
+ ? sortItem.direction === "asc"
512
+ ? "ascending"
513
+ : "descending"
514
+ : "none";
515
+
516
+ return html`
517
+ <th
518
+ scope="col"
519
+ aria-sort=${ifDefined(col.sortable ? ariaSort : undefined)}
520
+ draggable="true"
521
+ @dragstart=${(e: DragEvent) => this.handleDragStart(e, col.key)}
522
+ @dragover=${this.handleDragOver}
523
+ @dragleave=${this.handleDragLeave}
524
+ @drop=${(e: DragEvent) => this.handleDrop(e, col.key)}
525
+ >
526
+ <div class="th-content">
527
+ <svg
528
+ class="drag-handle"
529
+ aria-hidden="true"
530
+ width="12"
531
+ height="12"
532
+ viewBox="0 0 24 24"
533
+ fill="none"
534
+ stroke="currentColor"
535
+ stroke-width="2"
536
+ stroke-linecap="round"
537
+ stroke-linejoin="round"
538
+ >
539
+ <circle cx="9" cy="12" r="1" />
540
+ <circle cx="9" cy="5" r="1" />
541
+ <circle cx="9" cy="19" r="1" />
542
+ <circle cx="15" cy="12" r="1" />
543
+ <circle cx="15" cy="5" r="1" />
544
+ <circle cx="15" cy="19" r="1" />
545
+ </svg>
546
+
547
+ <div
548
+ class=${classMap({"sortable-header": !!col.sortable})}
549
+ @click=${(e: MouseEvent) => col.sortable && this.handleSort(col.key, e)}
550
+ >
551
+ ${col.header} ${col.sortable ? this.renderSortIcon(col.key) : ""}
552
+ </div>
495
553
  </div>
496
- </div>
497
- </th>
498
- `)}
554
+ </th>
555
+ `;
556
+ })}
499
557
  </tr>
500
558
  </thead>
501
- <tbody>
502
- ${this.loading ? html`
503
- ${Array.from({ length: skeletonRowsCount }).map(() => html`
504
- <tr>
505
- ${visibleColumns.map(() => html`
506
- <td><st-skeleton variant="text" width="75%"></st-skeleton></td>
507
- `)}
508
- </tr>
509
- `)}
510
- ` : this.data.length === 0 ? html`
511
- <tr>
512
- <td colspan=${visibleColumns.length} style="text-align: center; padding: 2rem;">No results found.</td>
513
- </tr>
514
- ` : this.data.map(row => html`
515
- <tr>
516
- ${visibleColumns.map(col => html`
517
- <td>${row[col.key]}</td>
518
- `)}
519
- </tr>
520
- `)}
559
+
560
+ <tbody aria-live="polite">
561
+ ${this.loading
562
+ ? html`
563
+ ${Array.from({length: skeletonRowsCount}).map(
564
+ () => html`
565
+ <tr>
566
+ ${visibleColumns.map(
567
+ () => html`
568
+ <td><st-skeleton variant="text" width="75%"></st-skeleton></td>
569
+ `,
570
+ )}
571
+ </tr>
572
+ `,
573
+ )}
574
+ `
575
+ : this.data.length === 0
576
+ ? html`
577
+ <tr>
578
+ <td
579
+ colspan=${visibleColumns.length}
580
+ style="text-align: center; padding: 2rem;"
581
+ >
582
+ No results found.
583
+ </td>
584
+ </tr>
585
+ `
586
+ : this.data.map(
587
+ (row) => html`
588
+ <tr>
589
+ ${visibleColumns.map((col) => html`<td>${row[col.key] ?? ""}</td>`)}
590
+ </tr>
591
+ `,
592
+ )}
521
593
  </tbody>
522
594
  </table>
523
595
  </div>
524
596
 
525
- <!-- Pagination -->
526
597
  <div class="pagination">
527
598
  <div class="pagination-info">
528
599
  Page ${this.page} of ${this.totalPages} (${this.totalRows} total items)
529
600
  </div>
530
601
  <div class="pagination-controls">
531
- <st-button
532
- variant="secondary"
533
- size="sm"
602
+ <st-button
603
+ variant="secondary"
604
+ size="sm"
534
605
  ?disabled=${this.loading || this.page === 1}
535
606
  @click=${this.prevPage}
536
607
  >
537
608
  Prev
538
609
  </st-button>
539
- <st-button
540
- variant="secondary"
541
- size="sm"
610
+ <st-button
611
+ variant="secondary"
612
+ size="sm"
542
613
  ?disabled=${this.loading || this.page === this.totalPages}
543
614
  @click=${this.nextPage}
544
615
  >
@@ -552,6 +623,6 @@ export class StDataTable extends LitElement {
552
623
 
553
624
  declare global {
554
625
  interface HTMLElementTagNameMap {
555
- 'st-data-table': StDataTable;
626
+ "st-data-table": StDataTable;
556
627
  }
557
- }
628
+ }