snice 7.6.0 → 7.8.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 (228) hide show
  1. package/adapters/react/SniceProvider.js +1 -1
  2. package/adapters/react/SniceRouter.js +1 -1
  3. package/adapters/react/calendar.d.ts +1 -0
  4. package/adapters/react/calendar.d.ts.map +1 -1
  5. package/adapters/react/calendar.js +1 -1
  6. package/adapters/react/calendar.js.map +1 -1
  7. package/adapters/react/calendar.tsx +2 -1
  8. package/adapters/react/matchRoute.js +1 -1
  9. package/adapters/react/useRequestHandler.js +1 -1
  10. package/bin/analyzer-contracts.js +8 -2
  11. package/custom-elements.json +15 -1
  12. package/dist/cdn/accordion/snice-accordion.js +1 -1
  13. package/dist/cdn/action-bar/snice-action-bar.js +1 -1
  14. package/dist/cdn/activity-feed/snice-activity-feed.js +1 -1
  15. package/dist/cdn/alert/snice-alert.js +1 -1
  16. package/dist/cdn/app-tiles/snice-app-tiles.js +1 -1
  17. package/dist/cdn/approval-flow/snice-approval-flow.js +1 -1
  18. package/dist/cdn/audio-recorder/snice-audio-recorder.js +1 -1
  19. package/dist/cdn/availability/snice-availability.js +1 -1
  20. package/dist/cdn/avatar/snice-avatar.js +1 -1
  21. package/dist/cdn/avatar-group/snice-avatar-group.js +1 -1
  22. package/dist/cdn/badge/snice-badge.js +1 -1
  23. package/dist/cdn/banner/snice-banner.js +1 -1
  24. package/dist/cdn/binpack/snice-binpack.js +1 -1
  25. package/dist/cdn/book/snice-book.js +1 -1
  26. package/dist/cdn/booking/snice-booking.js +1 -1
  27. package/dist/cdn/breadcrumbs/snice-breadcrumbs.js +1 -1
  28. package/dist/cdn/button/snice-button.js +2 -2
  29. package/dist/cdn/button/snice-button.js.map +1 -1
  30. package/dist/cdn/button/snice-button.min.js +1 -1
  31. package/dist/cdn/button/snice-button.min.js.map +1 -1
  32. package/dist/cdn/calendar/README.md +2 -2
  33. package/dist/cdn/calendar/snice-calendar.js +26 -2
  34. package/dist/cdn/calendar/snice-calendar.js.map +1 -1
  35. package/dist/cdn/calendar/snice-calendar.min.js +2 -2
  36. package/dist/cdn/calendar/snice-calendar.min.js.map +1 -1
  37. package/dist/cdn/camera/snice-camera.js +1 -1
  38. package/dist/cdn/camera-annotate/snice-camera-annotate.js +1 -1
  39. package/dist/cdn/candlestick/snice-candlestick.js +1 -1
  40. package/dist/cdn/card/snice-card.js +1 -1
  41. package/dist/cdn/carousel/snice-carousel.js +1 -1
  42. package/dist/cdn/cart/snice-cart.js +1 -1
  43. package/dist/cdn/chart/snice-chart.js +1 -1
  44. package/dist/cdn/chat/snice-chat.js +1 -1
  45. package/dist/cdn/checkbox/snice-checkbox.js +1 -1
  46. package/dist/cdn/chip/snice-chip.js +1 -1
  47. package/dist/cdn/code-block/snice-code-block.js +1 -1
  48. package/dist/cdn/color-display/snice-color-display.js +1 -1
  49. package/dist/cdn/color-picker/snice-color-picker.js +1 -1
  50. package/dist/cdn/command-palette/snice-command-palette.js +1 -1
  51. package/dist/cdn/comments/snice-comments.js +1 -1
  52. package/dist/cdn/countdown/snice-countdown.js +1 -1
  53. package/dist/cdn/cropper/snice-cropper.js +1 -1
  54. package/dist/cdn/data-card/snice-data-card.js +1 -1
  55. package/dist/cdn/date-picker/snice-date-picker.js +1 -1
  56. package/dist/cdn/date-range-picker/snice-date-range-picker.js +1 -1
  57. package/dist/cdn/date-time-picker/snice-date-time-picker.js +1 -1
  58. package/dist/cdn/diff/snice-diff.js +1 -1
  59. package/dist/cdn/divider/snice-divider.js +1 -1
  60. package/dist/cdn/doc/snice-doc.js +1 -1
  61. package/dist/cdn/draw/snice-draw.js +1 -1
  62. package/dist/cdn/drawer/snice-drawer.js +1 -1
  63. package/dist/cdn/empty-state/snice-empty-state.js +1 -1
  64. package/dist/cdn/estimate/snice-estimate.js +1 -1
  65. package/dist/cdn/file-gallery/snice-file-gallery.js +1 -1
  66. package/dist/cdn/file-upload/snice-file-upload.js +1 -1
  67. package/dist/cdn/flip-card/snice-flip-card.js +1 -1
  68. package/dist/cdn/flow/snice-flow.js +1 -1
  69. package/dist/cdn/form-layout/snice-form-layout.js +1 -1
  70. package/dist/cdn/funnel/snice-funnel.js +1 -1
  71. package/dist/cdn/gantt/snice-gantt.js +1 -1
  72. package/dist/cdn/gauge/snice-gauge.js +1 -1
  73. package/dist/cdn/grid/snice-grid.js +1 -1
  74. package/dist/cdn/heatmap/snice-heatmap.js +1 -1
  75. package/dist/cdn/image/snice-image.js +1 -1
  76. package/dist/cdn/input/snice-input.js +1 -1
  77. package/dist/cdn/invoice/snice-invoice.js +1 -1
  78. package/dist/cdn/kanban/snice-kanban.js +1 -1
  79. package/dist/cdn/key-value/snice-key-value.js +1 -1
  80. package/dist/cdn/kpi/snice-kpi.js +1 -1
  81. package/dist/cdn/layout/snice-layout.js +1 -1
  82. package/dist/cdn/leaderboard/snice-leaderboard.js +1 -1
  83. package/dist/cdn/link/snice-link.js +1 -1
  84. package/dist/cdn/link-preview/snice-link-preview.js +1 -1
  85. package/dist/cdn/list/snice-list.js +1 -1
  86. package/dist/cdn/location/snice-location.js +1 -1
  87. package/dist/cdn/login/README.md +1 -1
  88. package/dist/cdn/login/snice-login.js +2 -2
  89. package/dist/cdn/login/snice-login.js.map +1 -1
  90. package/dist/cdn/login/snice-login.min.js +1 -1
  91. package/dist/cdn/login/snice-login.min.js.map +1 -1
  92. package/dist/cdn/map/snice-map.js +1 -1
  93. package/dist/cdn/markdown/snice-markdown.js +1 -1
  94. package/dist/cdn/masonry/snice-masonry.js +1 -1
  95. package/dist/cdn/menu/snice-menu.js +1 -1
  96. package/dist/cdn/message-strip/snice-message-strip.js +1 -1
  97. package/dist/cdn/modal/snice-modal.js +1 -1
  98. package/dist/cdn/music-player/snice-music-player.js +1 -1
  99. package/dist/cdn/nav/snice-nav.js +1 -1
  100. package/dist/cdn/network-graph/snice-network-graph.js +1 -1
  101. package/dist/cdn/notification-center/snice-notification-center.js +1 -1
  102. package/dist/cdn/order-tracker/snice-order-tracker.js +1 -1
  103. package/dist/cdn/org-chart/snice-org-chart.js +1 -1
  104. package/dist/cdn/pagination/snice-pagination.js +1 -1
  105. package/dist/cdn/paint/snice-paint.js +1 -1
  106. package/dist/cdn/pdf-viewer/snice-pdf-viewer.js +1 -1
  107. package/dist/cdn/permission-matrix/snice-permission-matrix.js +1 -1
  108. package/dist/cdn/podcast-player/snice-podcast-player.js +1 -1
  109. package/dist/cdn/popover/snice-popover.js +1 -1
  110. package/dist/cdn/pricing-table/snice-pricing-table.js +1 -1
  111. package/dist/cdn/product-card/snice-product-card.js +1 -1
  112. package/dist/cdn/progress/snice-progress.js +1 -1
  113. package/dist/cdn/progress-ring/snice-progress-ring.js +1 -1
  114. package/dist/cdn/qr-code/snice-qr-code.js +1 -1
  115. package/dist/cdn/qr-reader/snice-qr-reader.js +1 -1
  116. package/dist/cdn/radio/snice-radio.js +1 -1
  117. package/dist/cdn/range-slider/snice-range-slider.js +1 -1
  118. package/dist/cdn/rating/snice-rating.js +1 -1
  119. package/dist/cdn/receipt/snice-receipt.js +1 -1
  120. package/dist/cdn/recipe/snice-recipe.js +1 -1
  121. package/dist/cdn/runtime/README.md +2 -2
  122. package/dist/cdn/runtime/snice-runtime.esm.js +130 -31
  123. package/dist/cdn/runtime/snice-runtime.esm.js.map +1 -1
  124. package/dist/cdn/runtime/snice-runtime.esm.min.js +1 -1
  125. package/dist/cdn/runtime/snice-runtime.esm.min.js.map +1 -1
  126. package/dist/cdn/runtime/snice-runtime.js +130 -31
  127. package/dist/cdn/runtime/snice-runtime.js.map +1 -1
  128. package/dist/cdn/runtime/snice-runtime.min.js +1 -1
  129. package/dist/cdn/runtime/snice-runtime.min.js.map +1 -1
  130. package/dist/cdn/sankey/snice-sankey.js +1 -1
  131. package/dist/cdn/segmented-control/snice-segmented-control.js +1 -1
  132. package/dist/cdn/select/snice-select.js +1 -1
  133. package/dist/cdn/skeleton/snice-skeleton.js +1 -1
  134. package/dist/cdn/slider/snice-slider.js +1 -1
  135. package/dist/cdn/sortable/snice-sortable.js +1 -1
  136. package/dist/cdn/sparkline/snice-sparkline.js +1 -1
  137. package/dist/cdn/spinner/snice-spinner.js +1 -1
  138. package/dist/cdn/split-button/snice-split-button.js +1 -1
  139. package/dist/cdn/split-pane/snice-split-pane.js +1 -1
  140. package/dist/cdn/spotlight/snice-spotlight.js +1 -1
  141. package/dist/cdn/stat-group/snice-stat-group.js +1 -1
  142. package/dist/cdn/step-input/snice-step-input.js +1 -1
  143. package/dist/cdn/stepper/snice-stepper.js +1 -1
  144. package/dist/cdn/switch/snice-switch.js +1 -1
  145. package/dist/cdn/table/README.md +2 -2
  146. package/dist/cdn/table/snice-table.esm.js +227 -36
  147. package/dist/cdn/table/snice-table.esm.js.map +1 -1
  148. package/dist/cdn/table/snice-table.esm.min.js +69 -48
  149. package/dist/cdn/table/snice-table.esm.min.js.map +1 -1
  150. package/dist/cdn/table/snice-table.js +226 -35
  151. package/dist/cdn/table/snice-table.js.map +1 -1
  152. package/dist/cdn/table/snice-table.min.js +47 -26
  153. package/dist/cdn/table/snice-table.min.js.map +1 -1
  154. package/dist/cdn/tabs/snice-tabs.js +1 -1
  155. package/dist/cdn/tag/snice-tag.js +1 -1
  156. package/dist/cdn/tag-input/README.md +2 -2
  157. package/dist/cdn/tag-input/snice-tag-input.js +10 -16
  158. package/dist/cdn/tag-input/snice-tag-input.js.map +1 -1
  159. package/dist/cdn/tag-input/snice-tag-input.min.js +2 -2
  160. package/dist/cdn/tag-input/snice-tag-input.min.js.map +1 -1
  161. package/dist/cdn/terminal/snice-terminal.js +1 -1
  162. package/dist/cdn/testimonial/snice-testimonial.js +1 -1
  163. package/dist/cdn/textarea/snice-textarea.js +1 -1
  164. package/dist/cdn/time-picker/snice-time-picker.js +1 -1
  165. package/dist/cdn/time-range-picker/snice-time-range-picker.js +1 -1
  166. package/dist/cdn/timeline/snice-timeline.js +1 -1
  167. package/dist/cdn/timer/snice-timer.js +1 -1
  168. package/dist/cdn/toast/snice-toast.js +1 -1
  169. package/dist/cdn/tooltip/snice-tooltip.js +1 -1
  170. package/dist/cdn/tree/snice-tree.js +1 -1
  171. package/dist/cdn/treemap/snice-treemap.js +1 -1
  172. package/dist/cdn/user-card/snice-user-card.js +1 -1
  173. package/dist/cdn/video-player/snice-video-player.js +1 -1
  174. package/dist/cdn/virtual-scroller/snice-virtual-scroller.js +1 -1
  175. package/dist/cdn/waterfall/snice-waterfall.js +1 -1
  176. package/dist/cdn/weather/snice-weather.js +1 -1
  177. package/dist/cdn/work-order/snice-work-order.js +1 -1
  178. package/dist/components/button/snice-button.js +1 -1
  179. package/dist/components/button/snice-button.js.map +1 -1
  180. package/dist/components/calendar/snice-calendar.d.ts +2 -0
  181. package/dist/components/calendar/snice-calendar.js +25 -1
  182. package/dist/components/calendar/snice-calendar.js.map +1 -1
  183. package/dist/components/table/snice-row.d.ts +6 -0
  184. package/dist/components/table/snice-row.js +11 -3
  185. package/dist/components/table/snice-row.js.map +1 -1
  186. package/dist/components/table/snice-table.d.ts +40 -3
  187. package/dist/components/table/snice-table.js +157 -17
  188. package/dist/components/table/snice-table.js.map +1 -1
  189. package/dist/components/table/table-cell-presentation.d.ts +11 -0
  190. package/dist/components/table/table-cell-presentation.js +25 -8
  191. package/dist/components/table/table-cell-presentation.js.map +1 -1
  192. package/dist/components/table/table-export.d.ts +6 -0
  193. package/dist/components/table/table-export.js +11 -2
  194. package/dist/components/table/table-export.js.map +1 -1
  195. package/dist/components/table/table-keyboard.js +1 -1
  196. package/dist/components/table/table-keyboard.js.map +1 -1
  197. package/dist/components/table/table-master-detail.d.ts +11 -2
  198. package/dist/components/table/table-master-detail.js +20 -3
  199. package/dist/components/table/table-master-detail.js.map +1 -1
  200. package/dist/components/tag-input/snice-tag-input.d.ts +2 -2
  201. package/dist/components/tag-input/snice-tag-input.js +9 -15
  202. package/dist/components/tag-input/snice-tag-input.js.map +1 -1
  203. package/dist/index.cjs +127 -28
  204. package/dist/index.cjs.map +1 -1
  205. package/dist/index.esm.js +127 -28
  206. package/dist/index.esm.js.map +1 -1
  207. package/dist/index.iife.js +127 -28
  208. package/dist/index.iife.js.map +1 -1
  209. package/dist/react/SniceProvider.js +1 -1
  210. package/dist/react/SniceRouter.js +1 -1
  211. package/dist/react/index.js +1 -1
  212. package/dist/react/matchRoute.js +1 -1
  213. package/dist/react/useRequestHandler.js +1 -1
  214. package/dist/request-response.d.ts +24 -1
  215. package/dist/symbols.cjs +1 -1
  216. package/dist/symbols.esm.js +1 -1
  217. package/dist/testing-dom.cjs +1 -1
  218. package/dist/testing-dom.esm.js +1 -1
  219. package/dist/transitions.cjs +1 -1
  220. package/dist/transitions.esm.js +1 -1
  221. package/docs/ai/DEVELOPMENT.md +1 -1
  222. package/docs/ai/components/calendar.md +8 -0
  223. package/docs/ai/components/tag-input.md +4 -0
  224. package/docs/ai/controllers.md +22 -6
  225. package/docs/components/calendar.md +24 -0
  226. package/docs/components/tag-input.md +6 -1
  227. package/docs/controllers.md +57 -6
  228. package/package.json +1 -1
@@ -3,6 +3,17 @@ import type { ColumnDefinition } from './snice-table.types';
3
3
  * Give every standalone specialized table cell the common ColumnDefinition
4
4
  * presentation contract. The observer reapplies after Snice's differential
5
5
  * renderer changes shadow content; equality guards prevent mutation loops.
6
+ *
7
+ * Display formatters
8
+ * ------------------
9
+ * `formatter` is the column's display formatter and `valueFormatter` its
10
+ * fallback, so `formatter` wins. `formatterOverride` says the cell does NOT
11
+ * apply `column.formatter` itself, so this module owns both formatters for it.
12
+ *
13
+ * A host marked `formatted` was handed its display text as `value` by the
14
+ * table, which already resolved the pipeline. Its content is that text
15
+ * verbatim — the marker exists so a rating/progress/link/tag cell, which
16
+ * renders structure rather than text, still shows the formatted string.
6
17
  */
7
18
  export declare function installCellPresentation(host: HTMLElement & {
8
19
  column?: ColumnDefinition | null;
@@ -12,6 +12,17 @@ const appliedClasses = new WeakMap();
12
12
  * Give every standalone specialized table cell the common ColumnDefinition
13
13
  * presentation contract. The observer reapplies after Snice's differential
14
14
  * renderer changes shadow content; equality guards prevent mutation loops.
15
+ *
16
+ * Display formatters
17
+ * ------------------
18
+ * `formatter` is the column's display formatter and `valueFormatter` its
19
+ * fallback, so `formatter` wins. `formatterOverride` says the cell does NOT
20
+ * apply `column.formatter` itself, so this module owns both formatters for it.
21
+ *
22
+ * A host marked `formatted` was handed its display text as `value` by the
23
+ * table, which already resolved the pipeline. Its content is that text
24
+ * verbatim — the marker exists so a rating/progress/link/tag cell, which
25
+ * renders structure rather than text, still shows the formatted string.
15
26
  */
16
27
  function installCellPresentation(host, formatterOverride = false) {
17
28
  if (installed.has(host))
@@ -58,15 +69,21 @@ function installCellPresentation(host, formatterOverride = false) {
58
69
  ? column.tooltip(host.value, host.rowData)
59
70
  : (host.value == null ? '' : String(host.value));
60
71
  }
61
- if (formatterOverride || column.valueFormatter) {
62
- const formatter = formatterOverride
63
- ? (column.formatter || column.valueFormatter)
64
- : column.valueFormatter;
72
+ // `formatter` beats `valueFormatter`. A cell that applies `formatter`
73
+ // itself has already painted it, so the fallback must not overwrite that
74
+ // content — only a formatter-less column falls through to valueFormatter.
75
+ const formatter = column.formatter
76
+ ? (formatterOverride ? column.formatter : null)
77
+ : column.valueFormatter;
78
+ // Pre-formatted hosts read the attribute, not the property: a cell whose
79
+ // value property is typed (boolean, number) coerces the display string.
80
+ const display = host.hasAttribute('formatted')
81
+ ? (host.getAttribute('value') ?? '')
82
+ : (formatter ? String(formatter(host.value, host.rowData) ?? '') : null);
83
+ if (display !== null) {
65
84
  const content = host.shadowRoot?.querySelector('[part~="content"]');
66
- if (formatter && content) {
67
- const value = String(formatter(host.value, host.rowData) ?? '');
68
- if (content.textContent !== value || content.childElementCount > 0)
69
- content.textContent = value;
85
+ if (content && (content.textContent !== display || content.childElementCount > 0)) {
86
+ content.textContent = display;
70
87
  }
71
88
  }
72
89
  }
@@ -1 +1 @@
1
- {"version":3,"file":"table-cell-presentation.js","sources":["../../../../components/table/table-cell-presentation.ts"],"sourcesContent":["import type { ColumnDefinition, CellStyle } from './snice-table.types';\n\nconst presentationProperties: Array<keyof CellStyle> = [\n 'backgroundColor',\n 'color',\n 'fontWeight',\n 'fontStyle',\n 'fontSize',\n 'textDecoration',\n];\n\nconst installed = new WeakMap<HTMLElement, MutationObserver>();\nconst appliedClasses = new WeakMap<HTMLElement, string[]>();\n\n/**\n * Give every standalone specialized table cell the common ColumnDefinition\n * presentation contract. The observer reapplies after Snice's differential\n * renderer changes shadow content; equality guards prevent mutation loops.\n */\nexport function installCellPresentation(\n host: HTMLElement & { column?: ColumnDefinition | null; value?: any; rowData?: any },\n formatterOverride = false,\n) {\n if (installed.has(host)) return;\n let observer: MutationObserver | null = null;\n let syncing = false;\n\n const sync = () => {\n if (syncing) return;\n syncing = true;\n observer?.disconnect();\n try {\n const column = host.column;\n for (const className of appliedClasses.get(host) || []) host.classList.remove(className);\n appliedClasses.set(host, []);\n for (const property of presentationProperties) (host.style as any)[property] = '';\n host.removeAttribute('title');\n if (!column) return;\n\n const applyStyle = (style?: CellStyle) => {\n if (!style) return;\n for (const property of presentationProperties) {\n const value = style[property];\n if (value) (host.style as any)[property] = value;\n }\n };\n\n applyStyle(column.style);\n for (const rule of column.conditionalFormats || []) {\n if (!rule.condition(host.value, host.rowData)) continue;\n applyStyle(rule.style);\n if (rule.className) {\n host.classList.add(rule.className);\n appliedClasses.set(host, [rule.className]);\n }\n break;\n }\n\n if (column.tooltip) {\n host.title = typeof column.tooltip === 'function'\n ? column.tooltip(host.value, host.rowData)\n : (host.value == null ? '' : String(host.value));\n }\n\n if (formatterOverride || column.valueFormatter) {\n const formatter = formatterOverride\n ? (column.formatter || column.valueFormatter)\n : column.valueFormatter;\n const content = host.shadowRoot?.querySelector('[part~=\"content\"]') as HTMLElement | null;\n if (formatter && content) {\n const value = String(formatter(host.value, host.rowData) ?? '');\n if (content.textContent !== value || content.childElementCount > 0) content.textContent = value;\n }\n }\n } finally {\n syncing = false;\n if (observer && host.shadowRoot) {\n observer.observe(host.shadowRoot, { childList: true, subtree: true, characterData: true });\n }\n }\n };\n\n observer = new MutationObserver(sync);\n if (host.shadowRoot) observer.observe(host.shadowRoot, { childList: true, subtree: true, characterData: true });\n installed.set(host, observer);\n queueMicrotask(sync);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"table-cell-presentation.js","sources":["../../../../components/table/table-cell-presentation.ts"],"sourcesContent":["import type { ColumnDefinition, CellStyle } from './snice-table.types';\n\nconst presentationProperties: Array<keyof CellStyle> = [\n 'backgroundColor',\n 'color',\n 'fontWeight',\n 'fontStyle',\n 'fontSize',\n 'textDecoration',\n];\n\nconst installed = new WeakMap<HTMLElement, MutationObserver>();\nconst appliedClasses = new WeakMap<HTMLElement, string[]>();\n\n/**\n * Give every standalone specialized table cell the common ColumnDefinition\n * presentation contract. The observer reapplies after Snice's differential\n * renderer changes shadow content; equality guards prevent mutation loops.\n *\n * Display formatters\n * ------------------\n * `formatter` is the column's display formatter and `valueFormatter` its\n * fallback, so `formatter` wins. `formatterOverride` says the cell does NOT\n * apply `column.formatter` itself, so this module owns both formatters for it.\n *\n * A host marked `formatted` was handed its display text as `value` by the\n * table, which already resolved the pipeline. Its content is that text\n * verbatim — the marker exists so a rating/progress/link/tag cell, which\n * renders structure rather than text, still shows the formatted string.\n */\nexport function installCellPresentation(\n host: HTMLElement & { column?: ColumnDefinition | null; value?: any; rowData?: any },\n formatterOverride = false,\n) {\n if (installed.has(host)) return;\n let observer: MutationObserver | null = null;\n let syncing = false;\n\n const sync = () => {\n if (syncing) return;\n syncing = true;\n observer?.disconnect();\n try {\n const column = host.column;\n for (const className of appliedClasses.get(host) || []) host.classList.remove(className);\n appliedClasses.set(host, []);\n for (const property of presentationProperties) (host.style as any)[property] = '';\n host.removeAttribute('title');\n if (!column) return;\n\n const applyStyle = (style?: CellStyle) => {\n if (!style) return;\n for (const property of presentationProperties) {\n const value = style[property];\n if (value) (host.style as any)[property] = value;\n }\n };\n\n applyStyle(column.style);\n for (const rule of column.conditionalFormats || []) {\n if (!rule.condition(host.value, host.rowData)) continue;\n applyStyle(rule.style);\n if (rule.className) {\n host.classList.add(rule.className);\n appliedClasses.set(host, [rule.className]);\n }\n break;\n }\n\n if (column.tooltip) {\n host.title = typeof column.tooltip === 'function'\n ? column.tooltip(host.value, host.rowData)\n : (host.value == null ? '' : String(host.value));\n }\n\n // `formatter` beats `valueFormatter`. A cell that applies `formatter`\n // itself has already painted it, so the fallback must not overwrite that\n // content — only a formatter-less column falls through to valueFormatter.\n const formatter = column.formatter\n ? (formatterOverride ? column.formatter : null)\n : column.valueFormatter;\n // Pre-formatted hosts read the attribute, not the property: a cell whose\n // value property is typed (boolean, number) coerces the display string.\n const display = host.hasAttribute('formatted')\n ? (host.getAttribute('value') ?? '')\n : (formatter ? String(formatter(host.value, host.rowData) ?? '') : null);\n if (display !== null) {\n const content = host.shadowRoot?.querySelector('[part~=\"content\"]') as HTMLElement | null;\n if (content && (content.textContent !== display || content.childElementCount > 0)) {\n content.textContent = display;\n }\n }\n } finally {\n syncing = false;\n if (observer && host.shadowRoot) {\n observer.observe(host.shadowRoot, { childList: true, subtree: true, characterData: true });\n }\n }\n };\n\n observer = new MutationObserver(sync);\n if (host.shadowRoot) observer.observe(host.shadowRoot, { childList: true, subtree: true, characterData: true });\n installed.set(host, observer);\n queueMicrotask(sync);\n}\n"],"names":[],"mappings":"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"}
@@ -51,6 +51,12 @@ export declare class TableExport {
51
51
  */
52
52
  copyToClipboard(data: any[], columns: ColumnDefinition[], options?: ClipboardOptions): Promise<boolean>;
53
53
  private escapeCSV;
54
+ /**
55
+ * The column's working value for a row: `valueGetter` "runs for display,
56
+ * sort, aggregation" — and for export, which must ship what the table shows,
57
+ * not a row field the column may not even have.
58
+ */
59
+ private cellValue;
54
60
  private rawValue;
55
61
  private formatValue;
56
62
  private escapeHTML;
@@ -17,7 +17,7 @@ class TableExport {
17
17
  // Data rows
18
18
  for (const row of data) {
19
19
  const cells = exportColumns.map(c => {
20
- const value = row[c.key];
20
+ const value = this.cellValue(row, c);
21
21
  return this.escapeCSV(value == null ? '' : String(value), delimiter);
22
22
  });
23
23
  rows.push(cells.join(delimiter));
@@ -129,7 +129,7 @@ class TableExport {
129
129
  if (rows.length === 0)
130
130
  return false;
131
131
  const text = rows.map(row => columns.map(c => {
132
- const value = row[c.key];
132
+ const value = this.cellValue(row, c);
133
133
  return useFormatted ? this.formatValue(value, row, c) : this.rawValue(value);
134
134
  }).join(delimiter)).join('\n');
135
135
  try {
@@ -154,6 +154,15 @@ class TableExport {
154
154
  }
155
155
  return value;
156
156
  }
157
+ /**
158
+ * The column's working value for a row: `valueGetter` "runs for display,
159
+ * sort, aggregation" — and for export, which must ship what the table shows,
160
+ * not a row field the column may not even have.
161
+ */
162
+ cellValue(row, column) {
163
+ const raw = row?.[column.key];
164
+ return column.valueGetter ? column.valueGetter(raw, row) : raw;
165
+ }
157
166
  rawValue(value) {
158
167
  if (value == null)
159
168
  return '';
@@ -1 +1 @@
1
- {"version":3,"file":"table-export.js","sources":["../../../../components/table/table-export.ts"],"sourcesContent":["/**\n * Export and clipboard functionality for snice-table.\n * Handles: CSV export, print, clipboard copy.\n */\nimport type { ColumnDefinition } from './snice-table.types';\n\nexport interface CSVExportOptions {\n /** CSV delimiter. Default: ',' */\n delimiter?: string;\n /** Filename for download. Default: 'export.csv' */\n filename?: string;\n /** Include column headers. Default: true */\n includeHeaders?: boolean;\n /** Export only selected rows. Default: false */\n selectedOnly?: boolean;\n /** Specific column keys to export. Default: all visible */\n columns?: string[];\n /** Add UTF-8 BOM for Excel compatibility. Default: true */\n utf8BOM?: boolean;\n}\n\nexport interface PrintOptions {\n /** Hide footer in print. Default: false */\n hideFooter?: boolean;\n /** Hide toolbar in print. Default: false */\n hideToolbar?: boolean;\n /** Include selection checkboxes. Default: false */\n includeCheckboxes?: boolean;\n /** Custom page CSS. */\n pageStyles?: string;\n}\n\nexport interface ClipboardOptions {\n /** Delimiter for copied cells. Default: '\\t' */\n delimiter?: string;\n /** Use formatted values or raw. Default: true (formatted) */\n useFormatted?: boolean;\n}\n\nexport class TableExport {\n /**\n * Export data to CSV and trigger download.\n */\n exportCSV(\n data: any[],\n columns: { key: string; label: string; exportable?: boolean }[],\n options: CSVExportOptions = {}\n ) {\n const {\n delimiter = ',',\n filename = 'export.csv',\n includeHeaders = true,\n columns: columnKeys,\n utf8BOM = true,\n } = options;\n\n // Filter columns\n let exportColumns = columns.filter(c => (c as any).exportable !== false);\n if (columnKeys) {\n exportColumns = exportColumns.filter(c => columnKeys.includes(c.key));\n }\n\n const rows: string[] = [];\n\n // Header row\n if (includeHeaders) {\n rows.push(exportColumns.map(c => this.escapeCSV(c.label, delimiter)).join(delimiter));\n }\n\n // Data rows\n for (const row of data) {\n const cells = exportColumns.map(c => {\n const value = row[c.key];\n return this.escapeCSV(value == null ? '' : String(value), delimiter);\n });\n rows.push(cells.join(delimiter));\n }\n\n const csv = rows.join('\\n');\n const bom = utf8BOM ? '\\ufeff' : '';\n const blob = new Blob([bom + csv], { type: 'text/csv;charset=utf-8;' });\n\n // Download\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n link.style.display = 'none';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n }\n\n /**\n * Open browser print dialog with table content.\n */\n print(\n tableElement: HTMLElement,\n options: PrintOptions = {}\n ) {\n const {\n hideFooter = false,\n hideToolbar = false,\n includeCheckboxes = false,\n pageStyles = '',\n } = options;\n\n // Clone the table content\n const shadowRoot = tableElement.shadowRoot;\n if (!shadowRoot) return;\n\n const table = shadowRoot.querySelector('table');\n if (!table) return;\n\n const tableClone = table.cloneNode(true) as HTMLTableElement;\n\n // Shadow-root cell contents are not included by outerHTML. Flatten each\n // rendered custom cell into plain print text so the printout contains what\n // the user actually sees rather than empty custom-element tags.\n const originalCells = Array.from(table.querySelectorAll('snice-cell, [in-table], snice-checkbox'));\n const clonedCells = Array.from(tableClone.querySelectorAll('snice-cell, [in-table], snice-checkbox'));\n originalCells.forEach((original: any, index) => {\n const clone = clonedCells[index] as HTMLElement | undefined;\n if (!clone) return;\n if (original.localName === 'snice-checkbox') {\n clone.replaceWith(document.createTextNode(original.checked ? '☑' : '☐'));\n return;\n }\n const content = original.shadowRoot?.querySelector('[part~=\"content\"]');\n clone.replaceWith(document.createTextNode((content?.textContent || original.textContent || '').trim()));\n });\n\n if (!includeCheckboxes) {\n tableClone.querySelectorAll('.select-column').forEach((cell) => cell.remove());\n }\n\n const plainSection = (selector: string, className: string): string => {\n const source = shadowRoot.querySelector(selector);\n if (!source) return '';\n const clone = source.cloneNode(true) as HTMLElement;\n clone.querySelectorAll('style').forEach((style) => style.remove());\n clone.querySelectorAll('button').forEach((button) => {\n if (!button.textContent?.trim()) button.textContent = button.getAttribute('aria-label') || '';\n });\n const text = clone.textContent?.replace(/\\s+/g, ' ').trim();\n return text ? `<div class=\"${className}\">${this.escapeHTML(text)}</div>` : '';\n };\n const toolbarHTML = hideToolbar ? '' : plainSection('.table-controls-container', 'print-toolbar');\n const footerHTML = hideFooter ? '' : plainSection('.table-pagination-container', 'print-footer');\n\n const printWindow = window.open('', '_blank');\n if (!printWindow) return;\n\n const html = `\n <!DOCTYPE html>\n <html>\n <head>\n <title>Print</title>\n <style>\n body { font-family: system-ui, -apple-system, sans-serif; margin: 1rem; }\n table { width: 100%; border-collapse: collapse; }\n th, td { padding: 8px 12px; border: 1px solid #ddd; text-align: left; }\n th { background: #f5f5f5; font-weight: 600; }\n .print-toolbar, .print-footer { margin: 0 0 0.75rem; color: #444; }\n .print-footer { margin: 0.75rem 0 0; }\n @media print {\n body { margin: 0; }\n table { page-break-inside: auto; }\n tr { page-break-inside: avoid; }\n }\n ${pageStyles}\n </style>\n </head>\n <body>\n ${toolbarHTML}\n ${tableClone.outerHTML}\n ${footerHTML}\n </body>\n </html>\n `;\n\n printWindow.document.write(html);\n printWindow.document.close();\n printWindow.onload = () => {\n printWindow.print();\n printWindow.close();\n };\n }\n\n /**\n * Copy cells to clipboard as tab-separated text.\n */\n async copyToClipboard(\n data: any[],\n columns: ColumnDefinition[],\n options: ClipboardOptions = {}\n ): Promise<boolean> {\n const { delimiter = '\\t', useFormatted = true } = options;\n const rows = data;\n\n if (rows.length === 0) return false;\n\n const text = rows.map(row =>\n columns.map(c => {\n const value = row[c.key];\n return useFormatted ? this.formatValue(value, row, c) : this.rawValue(value);\n }).join(delimiter)\n ).join('\\n');\n\n try {\n await navigator.clipboard.writeText(text);\n return true;\n } catch {\n // Fallback for older browsers\n const textarea = document.createElement('textarea');\n textarea.value = text;\n textarea.style.cssText = 'position:fixed;left:-9999px;';\n document.body.appendChild(textarea);\n textarea.select();\n const success = document.execCommand('copy');\n document.body.removeChild(textarea);\n return success;\n }\n }\n\n private escapeCSV(value: string, delimiter: string): string {\n if (value.includes(delimiter) || value.includes('\"') || value.includes('\\n') || value.includes('\\r')) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n }\n\n private rawValue(value: any): string {\n if (value == null) return '';\n if (typeof value === 'object') {\n try { return JSON.stringify(value); } catch { return String(value); }\n }\n return String(value);\n }\n\n private formatValue(value: any, row: any, column: ColumnDefinition): string {\n const override = column.formatter || column.valueFormatter;\n if (override) return String(override(value, row) ?? '');\n if (value == null) return '';\n\n const num = Number(value);\n switch (column.type) {\n case 'number':\n case 'accounting': {\n if (!Number.isFinite(num)) return this.rawValue(value);\n const format = column.numberFormat || {};\n const decimals = format.decimals ?? (column.type === 'accounting' ? 2 : 0);\n const grouped = new Intl.NumberFormat('en-US', {\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n useGrouping: format.thousandsSeparator ?? false,\n }).format(Math.abs(num));\n let output = num < 0 && (format.negativeStyle === 'parentheses' || column.type === 'accounting')\n ? `(${grouped})`\n : `${num < 0 ? '-' : ''}${grouped}`;\n if (format.prefix) output = format.prefix + output;\n if (format.suffix) output += format.suffix;\n return output;\n }\n case 'currency': {\n if (!Number.isFinite(num)) return this.rawValue(value);\n const format = column.currencyFormat || {};\n const decimals = format.decimals ?? 2;\n const display = format.currencyDisplay || format.display || 'symbol';\n const output = new Intl.NumberFormat(format.locale || 'en-US', {\n style: 'currency',\n currency: format.currency || 'USD',\n currencyDisplay: display,\n useGrouping: format.thousandsSeparator ?? true,\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n }).format(Math.abs(num));\n if (num >= 0) return output;\n return format.negativeStyle === 'parentheses' ? `(${output})` : `-${output}`;\n }\n case 'percent':\n case 'percentage': {\n if (!Number.isFinite(num)) return this.rawValue(value);\n return `${num.toFixed(column.percentageFormat?.decimals ?? 2)}%`;\n }\n case 'date': {\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return this.rawValue(value);\n const format = column.dateFormat || {};\n const dateStyle = format.format === 'custom' ? undefined : (format.format || 'short');\n return new Intl.DateTimeFormat(format.locale || 'en-US', dateStyle ? { dateStyle } : {}).format(date);\n }\n case 'boolean': {\n const format = column.booleanFormat || {};\n const truthy = value === true || String(value) === String(format.trueValue ?? 'true');\n return truthy ? (format.trueSymbol || format.trueValue || 'true') : (format.falseSymbol || format.falseValue || 'false');\n }\n case 'scientific':\n return Number.isFinite(num) ? num.toExponential(column.numberFormat?.decimals ?? 2) : this.rawValue(value);\n default:\n return this.rawValue(value);\n }\n }\n\n private escapeHTML(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n }\n}\n"],"names":[],"mappings":"MAuCa,WAAW,CAAA;AACtB;;AAEG;AACH,IAAA,SAAS,CACP,IAAW,EACX,OAA+D,EAC/D,UAA4B,EAAE,EAAA;QAE9B,MAAM,EACJ,SAAS,GAAG,GAAG,EACf,QAAQ,GAAG,YAAY,EACvB,cAAc,GAAG,IAAI,EACrB,OAAO,EAAE,UAAU,EACnB,OAAO,GAAG,IAAI,GACf,GAAG,OAAO;;AAGX,QAAA,IAAI,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,IAAK,CAAS,CAAC,UAAU,KAAK,KAAK,CAAC;QACxE,IAAI,UAAU,EAAE;AACd,YAAA,aAAa,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACvE;QAEA,MAAM,IAAI,GAAa,EAAE;;QAGzB,IAAI,cAAc,EAAE;AAClB,YAAA,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACvF;;AAGA,QAAA,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE;YACtB,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,IAAG;gBAClC,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;gBACxB,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;AACtE,YAAA,CAAC,CAAC;YACF,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAClC;QAEA,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QAC3B,MAAM,GAAG,GAAG,OAAO,GAAG,QAAQ,GAAG,EAAE;AACnC,QAAA,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,yBAAyB,EAAE,CAAC;;QAGvE,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC;QACrC,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC;AACxC,QAAA,IAAI,CAAC,IAAI,GAAG,GAAG;AACf,QAAA,IAAI,CAAC,QAAQ,GAAG,QAAQ;AACxB,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM;AAC3B,QAAA,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;QAC/B,IAAI,CAAC,KAAK,EAAE;AACZ,QAAA,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;AAC/B,QAAA,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC;IAC1B;AAEA;;AAEG;AACH,IAAA,KAAK,CACH,YAAyB,EACzB,OAAA,GAAwB,EAAE,EAAA;AAE1B,QAAA,MAAM,EACJ,UAAU,GAAG,KAAK,EAClB,WAAW,GAAG,KAAK,EACnB,iBAAiB,GAAG,KAAK,EACzB,UAAU,GAAG,EAAE,GAChB,GAAG,OAAO;;AAGX,QAAA,MAAM,UAAU,GAAG,YAAY,CAAC,UAAU;AAC1C,QAAA,IAAI,CAAC,UAAU;YAAE;QAEjB,MAAM,KAAK,GAAG,UAAU,CAAC,aAAa,CAAC,OAAO,CAAC;AAC/C,QAAA,IAAI,CAAC,KAAK;YAAE;QAEZ,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,CAAqB;;;;AAK5D,QAAA,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,wCAAwC,CAAC,CAAC;AAClG,QAAA,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,wCAAwC,CAAC,CAAC;QACrG,aAAa,CAAC,OAAO,CAAC,CAAC,QAAa,EAAE,KAAK,KAAI;AAC7C,YAAA,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAA4B;AAC3D,YAAA,IAAI,CAAC,KAAK;gBAAE;AACZ,YAAA,IAAI,QAAQ,CAAC,SAAS,KAAK,gBAAgB,EAAE;gBAC3C,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;gBACxE;YACF;YACA,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,mBAAmB,CAAC;YACvE,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,OAAO,EAAE,WAAW,IAAI,QAAQ,CAAC,WAAW,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AACzG,QAAA,CAAC,CAAC;QAEF,IAAI,CAAC,iBAAiB,EAAE;AACtB,YAAA,UAAU,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;QAChF;AAEA,QAAA,MAAM,YAAY,GAAG,CAAC,QAAgB,EAAE,SAAiB,KAAY;YACnE,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,CAAC,QAAQ,CAAC;AACjD,YAAA,IAAI,CAAC,MAAM;AAAE,gBAAA,OAAO,EAAE;YACtB,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAgB;AACnD,YAAA,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;YAClE,KAAK,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,KAAI;AAClD,gBAAA,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,EAAE;oBAAE,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC,YAAY,CAAC,IAAI,EAAE;AAC/F,YAAA,CAAC,CAAC;AACF,YAAA,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE;AAC3D,YAAA,OAAO,IAAI,GAAG,CAAA,YAAA,EAAe,SAAS,KAAK,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA,MAAA,CAAQ,GAAG,EAAE;AAC/E,QAAA,CAAC;AACD,QAAA,MAAM,WAAW,GAAG,WAAW,GAAG,EAAE,GAAG,YAAY,CAAC,2BAA2B,EAAE,eAAe,CAAC;AACjG,QAAA,MAAM,UAAU,GAAG,UAAU,GAAG,EAAE,GAAG,YAAY,CAAC,6BAA6B,EAAE,cAAc,CAAC;QAEhG,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC;AAC7C,QAAA,IAAI,CAAC,WAAW;YAAE;AAElB,QAAA,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;YAiBL,UAAU;;;;UAIZ,WAAW;AACX,QAAA,EAAA,UAAU,CAAC,SAAS;UACpB,UAAU;;;KAGf;AAED,QAAA,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;AAChC,QAAA,WAAW,CAAC,QAAQ,CAAC,KAAK,EAAE;AAC5B,QAAA,WAAW,CAAC,MAAM,GAAG,MAAK;YACxB,WAAW,CAAC,KAAK,EAAE;YACnB,WAAW,CAAC,KAAK,EAAE;AACrB,QAAA,CAAC;IACH;AAEA;;AAEG;IACH,MAAM,eAAe,CACnB,IAAW,EACX,OAA2B,EAC3B,UAA4B,EAAE,EAAA;QAE9B,MAAM,EAAE,SAAS,GAAG,IAAI,EAAE,YAAY,GAAG,IAAI,EAAE,GAAG,OAAO;QACzD,MAAM,IAAI,GAAG,IAAI;AAEjB,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;AAAE,YAAA,OAAO,KAAK;AAEnC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IACvB,OAAO,CAAC,GAAG,CAAC,CAAC,IAAG;YACd,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YACxB,OAAO,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;AAC9E,QAAA,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CACnB,CAAC,IAAI,CAAC,IAAI,CAAC;AAEZ,QAAA,IAAI;YACF,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC;AACzC,YAAA,OAAO,IAAI;QACb;AAAE,QAAA,MAAM;;YAEN,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC;AACnD,YAAA,QAAQ,CAAC,KAAK,GAAG,IAAI;AACrB,YAAA,QAAQ,CAAC,KAAK,CAAC,OAAO,GAAG,8BAA8B;AACvD,YAAA,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;YACnC,QAAQ,CAAC,MAAM,EAAE;YACjB,MAAM,OAAO,GAAG,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC;AAC5C,YAAA,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;AACnC,YAAA,OAAO,OAAO;QAChB;IACF;IAEQ,SAAS,CAAC,KAAa,EAAE,SAAiB,EAAA;AAChD,QAAA,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;YACpG,OAAO,CAAA,CAAA,EAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA,CAAA,CAAG;QACzC;AACA,QAAA,OAAO,KAAK;IACd;AAEQ,IAAA,QAAQ,CAAC,KAAU,EAAA;QACzB,IAAI,KAAK,IAAI,IAAI;AAAE,YAAA,OAAO,EAAE;AAC5B,QAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AAC7B,YAAA,IAAI;AAAE,gBAAA,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;YAAE;AAAE,YAAA,MAAM;AAAE,gBAAA,OAAO,MAAM,CAAC,KAAK,CAAC;YAAE;QACtE;AACA,QAAA,OAAO,MAAM,CAAC,KAAK,CAAC;IACtB;AAEQ,IAAA,WAAW,CAAC,KAAU,EAAE,GAAQ,EAAE,MAAwB,EAAA;QAChE,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,cAAc;AAC1D,QAAA,IAAI,QAAQ;YAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;QACvD,IAAI,KAAK,IAAI,IAAI;AAAE,YAAA,OAAO,EAAE;AAE5B,QAAA,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC;AACzB,QAAA,QAAQ,MAAM,CAAC,IAAI;AACjB,YAAA,KAAK,QAAQ;YACb,KAAK,YAAY,EAAE;AACjB,gBAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;AAAE,oBAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;AACtD,gBAAA,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,IAAI,EAAE;gBACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,IAAI,KAAK,YAAY,GAAG,CAAC,GAAG,CAAC,CAAC;gBAC1E,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;AAC7C,oBAAA,qBAAqB,EAAE,QAAQ;AAC/B,oBAAA,qBAAqB,EAAE,QAAQ;AAC/B,oBAAA,WAAW,EAAE,MAAM,CAAC,kBAAkB,IAAI,KAAK;iBAChD,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACxB,gBAAA,IAAI,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,MAAM,CAAC,aAAa,KAAK,aAAa,IAAI,MAAM,CAAC,IAAI,KAAK,YAAY;sBAC3F,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA;AACb,sBAAE,CAAA,EAAG,GAAG,GAAG,CAAC,GAAG,GAAG,GAAG,EAAE,CAAA,EAAG,OAAO,EAAE;gBACrC,IAAI,MAAM,CAAC,MAAM;AAAE,oBAAA,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM;gBAClD,IAAI,MAAM,CAAC,MAAM;AAAE,oBAAA,MAAM,IAAI,MAAM,CAAC,MAAM;AAC1C,gBAAA,OAAO,MAAM;YACf;YACA,KAAK,UAAU,EAAE;AACf,gBAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;AAAE,oBAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;AACtD,gBAAA,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,IAAI,EAAE;AAC1C,gBAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,CAAC;gBACrC,MAAM,OAAO,GAAG,MAAM,CAAC,eAAe,IAAI,MAAM,CAAC,OAAO,IAAI,QAAQ;AACpE,gBAAA,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,IAAI,OAAO,EAAE;AAC7D,oBAAA,KAAK,EAAE,UAAU;AACjB,oBAAA,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,KAAK;AAClC,oBAAA,eAAe,EAAE,OAAO;AACxB,oBAAA,WAAW,EAAE,MAAM,CAAC,kBAAkB,IAAI,IAAI;AAC9C,oBAAA,qBAAqB,EAAE,QAAQ;AAC/B,oBAAA,qBAAqB,EAAE,QAAQ;iBAChC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACxB,IAAI,GAAG,IAAI,CAAC;AAAE,oBAAA,OAAO,MAAM;AAC3B,gBAAA,OAAO,MAAM,CAAC,aAAa,KAAK,aAAa,GAAG,CAAA,CAAA,EAAI,MAAM,GAAG,GAAG,CAAA,CAAA,EAAI,MAAM,EAAE;YAC9E;AACA,YAAA,KAAK,SAAS;YACd,KAAK,YAAY,EAAE;AACjB,gBAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;AAAE,oBAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;AACtD,gBAAA,OAAO,CAAA,EAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,QAAQ,IAAI,CAAC,CAAC,GAAG;YAClE;YACA,KAAK,MAAM,EAAE;AACX,gBAAA,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC;gBAC5B,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;AAAE,oBAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;AAC7D,gBAAA,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,IAAI,EAAE;gBACtC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,KAAK,QAAQ,GAAG,SAAS,IAAI,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC;AACrF,gBAAA,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,IAAI,OAAO,EAAE,SAAS,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;YACvG;YACA,KAAK,SAAS,EAAE;AACd,gBAAA,MAAM,MAAM,GAAG,MAAM,CAAC,aAAa,IAAI,EAAE;AACzC,gBAAA,MAAM,MAAM,GAAG,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC;AACrF,gBAAA,OAAO,MAAM,IAAI,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,KAAK,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,UAAU,IAAI,OAAO,CAAC;YAC1H;AACA,YAAA,KAAK,YAAY;AACf,gBAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,YAAY,EAAE,QAAQ,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;AAC5G,YAAA;AACE,gBAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;;IAEjC;AAEQ,IAAA,UAAU,CAAC,KAAa,EAAA;AAC9B,QAAA,OAAO;AACJ,aAAA,OAAO,CAAC,IAAI,EAAE,OAAO;AACrB,aAAA,OAAO,CAAC,IAAI,EAAE,MAAM;AACpB,aAAA,OAAO,CAAC,IAAI,EAAE,MAAM;AACpB,aAAA,OAAO,CAAC,IAAI,EAAE,QAAQ;AACtB,aAAA,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;IAC3B;AACD;;;;"}
1
+ {"version":3,"file":"table-export.js","sources":["../../../../components/table/table-export.ts"],"sourcesContent":["/**\n * Export and clipboard functionality for snice-table.\n * Handles: CSV export, print, clipboard copy.\n */\nimport type { ColumnDefinition } from './snice-table.types';\n\nexport interface CSVExportOptions {\n /** CSV delimiter. Default: ',' */\n delimiter?: string;\n /** Filename for download. Default: 'export.csv' */\n filename?: string;\n /** Include column headers. Default: true */\n includeHeaders?: boolean;\n /** Export only selected rows. Default: false */\n selectedOnly?: boolean;\n /** Specific column keys to export. Default: all visible */\n columns?: string[];\n /** Add UTF-8 BOM for Excel compatibility. Default: true */\n utf8BOM?: boolean;\n}\n\nexport interface PrintOptions {\n /** Hide footer in print. Default: false */\n hideFooter?: boolean;\n /** Hide toolbar in print. Default: false */\n hideToolbar?: boolean;\n /** Include selection checkboxes. Default: false */\n includeCheckboxes?: boolean;\n /** Custom page CSS. */\n pageStyles?: string;\n}\n\nexport interface ClipboardOptions {\n /** Delimiter for copied cells. Default: '\\t' */\n delimiter?: string;\n /** Use formatted values or raw. Default: true (formatted) */\n useFormatted?: boolean;\n}\n\nexport class TableExport {\n /**\n * Export data to CSV and trigger download.\n */\n exportCSV(\n data: any[],\n columns: { key: string; label: string; exportable?: boolean }[],\n options: CSVExportOptions = {}\n ) {\n const {\n delimiter = ',',\n filename = 'export.csv',\n includeHeaders = true,\n columns: columnKeys,\n utf8BOM = true,\n } = options;\n\n // Filter columns\n let exportColumns = columns.filter(c => (c as any).exportable !== false);\n if (columnKeys) {\n exportColumns = exportColumns.filter(c => columnKeys.includes(c.key));\n }\n\n const rows: string[] = [];\n\n // Header row\n if (includeHeaders) {\n rows.push(exportColumns.map(c => this.escapeCSV(c.label, delimiter)).join(delimiter));\n }\n\n // Data rows\n for (const row of data) {\n const cells = exportColumns.map(c => {\n const value = this.cellValue(row, c);\n return this.escapeCSV(value == null ? '' : String(value), delimiter);\n });\n rows.push(cells.join(delimiter));\n }\n\n const csv = rows.join('\\n');\n const bom = utf8BOM ? '\\ufeff' : '';\n const blob = new Blob([bom + csv], { type: 'text/csv;charset=utf-8;' });\n\n // Download\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n link.style.display = 'none';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n }\n\n /**\n * Open browser print dialog with table content.\n */\n print(\n tableElement: HTMLElement,\n options: PrintOptions = {}\n ) {\n const {\n hideFooter = false,\n hideToolbar = false,\n includeCheckboxes = false,\n pageStyles = '',\n } = options;\n\n // Clone the table content\n const shadowRoot = tableElement.shadowRoot;\n if (!shadowRoot) return;\n\n const table = shadowRoot.querySelector('table');\n if (!table) return;\n\n const tableClone = table.cloneNode(true) as HTMLTableElement;\n\n // Shadow-root cell contents are not included by outerHTML. Flatten each\n // rendered custom cell into plain print text so the printout contains what\n // the user actually sees rather than empty custom-element tags.\n const originalCells = Array.from(table.querySelectorAll('snice-cell, [in-table], snice-checkbox'));\n const clonedCells = Array.from(tableClone.querySelectorAll('snice-cell, [in-table], snice-checkbox'));\n originalCells.forEach((original: any, index) => {\n const clone = clonedCells[index] as HTMLElement | undefined;\n if (!clone) return;\n if (original.localName === 'snice-checkbox') {\n clone.replaceWith(document.createTextNode(original.checked ? '☑' : '☐'));\n return;\n }\n const content = original.shadowRoot?.querySelector('[part~=\"content\"]');\n clone.replaceWith(document.createTextNode((content?.textContent || original.textContent || '').trim()));\n });\n\n if (!includeCheckboxes) {\n tableClone.querySelectorAll('.select-column').forEach((cell) => cell.remove());\n }\n\n const plainSection = (selector: string, className: string): string => {\n const source = shadowRoot.querySelector(selector);\n if (!source) return '';\n const clone = source.cloneNode(true) as HTMLElement;\n clone.querySelectorAll('style').forEach((style) => style.remove());\n clone.querySelectorAll('button').forEach((button) => {\n if (!button.textContent?.trim()) button.textContent = button.getAttribute('aria-label') || '';\n });\n const text = clone.textContent?.replace(/\\s+/g, ' ').trim();\n return text ? `<div class=\"${className}\">${this.escapeHTML(text)}</div>` : '';\n };\n const toolbarHTML = hideToolbar ? '' : plainSection('.table-controls-container', 'print-toolbar');\n const footerHTML = hideFooter ? '' : plainSection('.table-pagination-container', 'print-footer');\n\n const printWindow = window.open('', '_blank');\n if (!printWindow) return;\n\n const html = `\n <!DOCTYPE html>\n <html>\n <head>\n <title>Print</title>\n <style>\n body { font-family: system-ui, -apple-system, sans-serif; margin: 1rem; }\n table { width: 100%; border-collapse: collapse; }\n th, td { padding: 8px 12px; border: 1px solid #ddd; text-align: left; }\n th { background: #f5f5f5; font-weight: 600; }\n .print-toolbar, .print-footer { margin: 0 0 0.75rem; color: #444; }\n .print-footer { margin: 0.75rem 0 0; }\n @media print {\n body { margin: 0; }\n table { page-break-inside: auto; }\n tr { page-break-inside: avoid; }\n }\n ${pageStyles}\n </style>\n </head>\n <body>\n ${toolbarHTML}\n ${tableClone.outerHTML}\n ${footerHTML}\n </body>\n </html>\n `;\n\n printWindow.document.write(html);\n printWindow.document.close();\n printWindow.onload = () => {\n printWindow.print();\n printWindow.close();\n };\n }\n\n /**\n * Copy cells to clipboard as tab-separated text.\n */\n async copyToClipboard(\n data: any[],\n columns: ColumnDefinition[],\n options: ClipboardOptions = {}\n ): Promise<boolean> {\n const { delimiter = '\\t', useFormatted = true } = options;\n const rows = data;\n\n if (rows.length === 0) return false;\n\n const text = rows.map(row =>\n columns.map(c => {\n const value = this.cellValue(row, c);\n return useFormatted ? this.formatValue(value, row, c) : this.rawValue(value);\n }).join(delimiter)\n ).join('\\n');\n\n try {\n await navigator.clipboard.writeText(text);\n return true;\n } catch {\n // Fallback for older browsers\n const textarea = document.createElement('textarea');\n textarea.value = text;\n textarea.style.cssText = 'position:fixed;left:-9999px;';\n document.body.appendChild(textarea);\n textarea.select();\n const success = document.execCommand('copy');\n document.body.removeChild(textarea);\n return success;\n }\n }\n\n private escapeCSV(value: string, delimiter: string): string {\n if (value.includes(delimiter) || value.includes('\"') || value.includes('\\n') || value.includes('\\r')) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n }\n\n /**\n * The column's working value for a row: `valueGetter` \"runs for display,\n * sort, aggregation\" — and for export, which must ship what the table shows,\n * not a row field the column may not even have.\n */\n private cellValue(row: any, column: ColumnDefinition): any {\n const raw = row?.[column.key];\n return column.valueGetter ? column.valueGetter(raw, row) : raw;\n }\n\n private rawValue(value: any): string {\n if (value == null) return '';\n if (typeof value === 'object') {\n try { return JSON.stringify(value); } catch { return String(value); }\n }\n return String(value);\n }\n\n private formatValue(value: any, row: any, column: ColumnDefinition): string {\n const override = column.formatter || column.valueFormatter;\n if (override) return String(override(value, row) ?? '');\n if (value == null) return '';\n\n const num = Number(value);\n switch (column.type) {\n case 'number':\n case 'accounting': {\n if (!Number.isFinite(num)) return this.rawValue(value);\n const format = column.numberFormat || {};\n const decimals = format.decimals ?? (column.type === 'accounting' ? 2 : 0);\n const grouped = new Intl.NumberFormat('en-US', {\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n useGrouping: format.thousandsSeparator ?? false,\n }).format(Math.abs(num));\n let output = num < 0 && (format.negativeStyle === 'parentheses' || column.type === 'accounting')\n ? `(${grouped})`\n : `${num < 0 ? '-' : ''}${grouped}`;\n if (format.prefix) output = format.prefix + output;\n if (format.suffix) output += format.suffix;\n return output;\n }\n case 'currency': {\n if (!Number.isFinite(num)) return this.rawValue(value);\n const format = column.currencyFormat || {};\n const decimals = format.decimals ?? 2;\n const display = format.currencyDisplay || format.display || 'symbol';\n const output = new Intl.NumberFormat(format.locale || 'en-US', {\n style: 'currency',\n currency: format.currency || 'USD',\n currencyDisplay: display,\n useGrouping: format.thousandsSeparator ?? true,\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n }).format(Math.abs(num));\n if (num >= 0) return output;\n return format.negativeStyle === 'parentheses' ? `(${output})` : `-${output}`;\n }\n case 'percent':\n case 'percentage': {\n if (!Number.isFinite(num)) return this.rawValue(value);\n return `${num.toFixed(column.percentageFormat?.decimals ?? 2)}%`;\n }\n case 'date': {\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return this.rawValue(value);\n const format = column.dateFormat || {};\n const dateStyle = format.format === 'custom' ? undefined : (format.format || 'short');\n return new Intl.DateTimeFormat(format.locale || 'en-US', dateStyle ? { dateStyle } : {}).format(date);\n }\n case 'boolean': {\n const format = column.booleanFormat || {};\n const truthy = value === true || String(value) === String(format.trueValue ?? 'true');\n return truthy ? (format.trueSymbol || format.trueValue || 'true') : (format.falseSymbol || format.falseValue || 'false');\n }\n case 'scientific':\n return Number.isFinite(num) ? num.toExponential(column.numberFormat?.decimals ?? 2) : this.rawValue(value);\n default:\n return this.rawValue(value);\n }\n }\n\n private escapeHTML(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n }\n}\n"],"names":[],"mappings":"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"}
@@ -122,7 +122,7 @@ class TableKeyboard {
122
122
  });
123
123
  }
124
124
  // Body rows
125
- const bodyRows = table.querySelectorAll('tbody tr:not(.virtual-spacer)');
125
+ const bodyRows = table.querySelectorAll('tbody tr:not(.virtual-spacer):not(.table-fill-row)');
126
126
  bodyRows.forEach((row, i) => {
127
127
  row.setAttribute('role', 'row');
128
128
  row.setAttribute('aria-rowindex', String(this.ariaRowOffset + i + 2)); // +2 for 1-based + header
@@ -1 +1 @@
1
- {"version":3,"file":"table-keyboard.js","sources":["../../../../components/table/table-keyboard.ts"],"sourcesContent":["/**\n * Keyboard navigation and ARIA for snice-table.\n * Full WAI-ARIA grid pattern with arrow key navigation.\n */\n\nexport interface KeyboardOptions {\n /** The shadow root containing the table */\n shadowRoot: ShadowRoot;\n /** Callback when a cell is activated (Enter) */\n onCellActivate?: (rowIndex: number, columnKey: string) => void;\n /** Callback when selection changes via keyboard */\n onSelectionToggle?: (rowIndex: number) => void;\n /** Callback for select-all (Ctrl+A) */\n onSelectAll?: () => void;\n /** Tab navigation mode */\n tabMode: 'none' | 'content' | 'header' | 'all';\n /**\n * Total data rows. A getter callback keeps bounds live as the dataset is\n * loaded / filtered / paginated; a plain number is accepted for callers that\n * re-attach on every change.\n */\n totalRows: number | (() => number);\n /** Total columns. Getter callback or plain number — see `totalRows`. */\n totalColumns: number | (() => number);\n /**\n * Total rows exposed to assistive technology. This may exceed the keyboard\n * navigation count when the body also contains non-focusable group and\n * aggregate rows. Defaults to totalRows.\n */\n ariaRows?: number | (() => number);\n /** Zero-based logical offset of the first rendered body row (virtual grids). */\n ariaRowOffset?: number | (() => number);\n /**\n * Resolve a keyboard-navigation row to its rendered <tr>. Grouping/filtering\n * can make navigation order differ from raw data-index order.\n */\n getRowElement?: (rowIndex: number) => HTMLElement | null;\n /**\n * Bring a logical (data-index) row into the rendered window before focus\n * lands on it. Under virtualization only the current window is in the DOM;\n * the host scrolls the row's range into view and renders it synchronously.\n */\n ensureRowVisible?: (rowIndex: number) => void;\n /** Whether editing is active */\n isEditing: () => boolean;\n}\n\nexport class TableKeyboard {\n private options: KeyboardOptions;\n private focusedRow = -1; // -1 = header\n /** True after the first real user keydown — no focus paint at rest. */\n private hasUserInteracted = false;\n private focusedCol = 0;\n // EventListener-typed: ShadowRoot's TS event map only knows 'slotchange',\n // so a KeyboardEvent-typed handler fails the addEventListener overloads.\n private keyHandler: EventListener;\n private attached = false;\n\n constructor() {\n this.options = {} as KeyboardOptions;\n this.keyHandler = (e: Event) => this.handleKeyDown(e as KeyboardEvent);\n }\n\n attach(options: KeyboardOptions) {\n this.options = options;\n const root = options.shadowRoot;\n if (!root) return;\n\n // Delegate keydown at the shadow root — a stable ancestor that always\n // exists (even before the first render) and survives every <table> rebuild.\n // Every keydown inside the shadow tree bubbles here, so navigation works no\n // matter when data/columns arrive or how often renderHeader/renderBody\n // replace the table structure. Binding to the inner <table> instead meant a\n // table built after @ready was never listened to, and a rebuilt table\n // silently dropped the listener.\n root.addEventListener('keydown', this.keyHandler);\n this.attached = true;\n this.syncGridRole();\n this.applyARIA();\n }\n\n detach() {\n this.options.shadowRoot?.removeEventListener('keydown', this.keyHandler);\n this.attached = false;\n }\n\n /**\n * (Re)apply the grid identity to the current <table>: role=grid, a focusable\n * tabindex, and live row/col counts. Cheap, idempotent, and safe to call\n * before the table exists (no-op). A structural rebuild that swaps the\n * <table> node drops these attributes, so they must be re-applied afterwards.\n */\n private syncGridRole() {\n const table = this.getTable();\n if (!table) return;\n\n table.setAttribute('role', 'grid');\n table.setAttribute('tabindex', '0');\n table.setAttribute('aria-rowcount', String(this.ariaRowCount + 1)); // +1 header\n table.setAttribute('aria-colcount', String(this.totalColCount));\n }\n\n /**\n * Host hook: called after renderHeader/renderBody rebuild the table. The\n * delegated keydown listener lives on the shadow root so it survives on its\n * own; this restores the grid role and the roving tabindex on the focused\n * cell, both of which a wiped thead/tbody (or a swapped <table>) would lose.\n */\n refresh() {\n if (!this.attached) return;\n\n this.syncGridRole();\n this.applyARIA();\n }\n\n private getTable(): HTMLElement | null {\n return this.options.shadowRoot?.querySelector('table') ?? null;\n }\n\n /** Resolve a bound that may be a live getter or a static number. */\n private resolveBound(v: number | (() => number) | undefined): number {\n return typeof v === 'function' ? v() : (v ?? 0);\n }\n\n private get totalRowCount(): number {\n return this.resolveBound(this.options.totalRows);\n }\n\n private get totalColCount(): number {\n return this.resolveBound(this.options.totalColumns);\n }\n\n private get ariaRowCount(): number {\n return this.resolveBound(this.options.ariaRows ?? this.options.totalRows);\n }\n\n private get ariaRowOffset(): number {\n return Math.max(0, this.resolveBound(this.options.ariaRowOffset));\n }\n\n /** Resolve a body row by its logical data-index (not its DOM position). */\n private getBodyRow(index: number): HTMLElement | null {\n if (this.options.getRowElement) return this.options.getRowElement(index);\n const table = this.getTable();\n if (!table) return null;\n return table.querySelector(`tbody tr[data-index=\"${index}\"]`);\n }\n\n /** Apply ARIA attributes to the grid */\n applyARIA() {\n const table = this.getTable();\n if (!table) return;\n\n table.setAttribute('aria-rowcount', String(this.ariaRowCount + 1)); // +1 for header\n table.setAttribute('aria-colcount', String(this.totalColCount));\n\n // Header row\n const headerRow = table.querySelector('thead tr.column-header-row')\n ?? table.querySelector('thead tr:not(.column-group-row):not(.header-filter-row)');\n if (headerRow) {\n headerRow.setAttribute('role', 'row');\n headerRow.setAttribute('aria-rowindex', '1');\n const ths = headerRow.querySelectorAll('th');\n ths.forEach((th, i) => {\n th.setAttribute('role', 'columnheader');\n th.setAttribute('aria-colindex', String(i + 1));\n\n // Sort state\n if (th.classList.contains('sortable')) {\n const sortIndicator = th.querySelector('.sort-indicator.active');\n if (sortIndicator) {\n const text = sortIndicator.textContent?.trim() || '';\n if (text.includes('▲')) th.setAttribute('aria-sort', 'ascending');\n else if (text.includes('▼')) th.setAttribute('aria-sort', 'descending');\n } else {\n th.setAttribute('aria-sort', 'none');\n }\n }\n });\n }\n\n // Body rows\n const bodyRows = table.querySelectorAll('tbody tr:not(.virtual-spacer)');\n bodyRows.forEach((row, i) => {\n row.setAttribute('role', 'row');\n row.setAttribute('aria-rowindex', String(this.ariaRowOffset + i + 2)); // +2 for 1-based + header\n\n const isSelected = row.getAttribute('data-selected') === 'true';\n if (isSelected) row.setAttribute('aria-selected', 'true');\n else row.removeAttribute('aria-selected');\n\n const cells = row.querySelectorAll('td');\n cells.forEach((cell, j) => {\n cell.setAttribute('role', 'gridcell');\n cell.setAttribute('aria-colindex', String(j + 1));\n });\n });\n\n // Set roving tabindex on focused cell\n this.updateFocusIndicator();\n }\n\n /** Set focus position */\n setFocus(row: number, col: number) {\n this.focusedRow = row;\n this.focusedCol = col;\n this.updateFocusIndicator();\n }\n\n /** Get current focus position */\n getFocus(): { row: number; col: number } {\n return { row: this.focusedRow, col: this.focusedCol };\n }\n\n private updateFocusIndicator() {\n // A grid that was never keyboard-focused must not wear focus styling —\n // refresh() runs after every renderBody, and painting the default header\n // position (focusedRow=-1) put a stuck blue ring on every table at rest.\n if (!this.hasUserInteracted) return;\n\n const table = this.getTable();\n if (!table) return;\n\n // Remove previous focus\n table.querySelectorAll('[data-grid-focus]').forEach(el => {\n el.removeAttribute('data-grid-focus');\n el.removeAttribute('tabindex');\n });\n\n // Find target cell\n let targetCell: HTMLElement | null = null;\n if (this.focusedRow === -1) {\n // Header\n const ths = table.querySelectorAll('thead th');\n targetCell = ths[this.focusedCol] as HTMLElement;\n } else {\n // Resolve by logical data-index, not DOM position: under virtualization\n // only the current window is in the DOM.\n let row = this.getBodyRow(this.focusedRow);\n if (!row) {\n // Outside the rendered window — ask the host to scroll it into view,\n // then look again.\n this.options.ensureRowVisible?.(this.focusedRow);\n row = this.getBodyRow(this.focusedRow);\n }\n if (row) {\n const cells = row.querySelectorAll('td');\n targetCell = cells[this.focusedCol] as HTMLElement;\n }\n }\n\n if (targetCell) {\n targetCell.setAttribute('data-grid-focus', 'true');\n targetCell.setAttribute('tabindex', '0');\n }\n }\n\n private handleKeyDown(e: KeyboardEvent) {\n // Don't handle when editing\n if (this.options.isEditing()) return;\n\n // Let controls inside the grid keep their native keyboard contract. This\n // includes group/tree toggle buttons (Enter/Space must click them), row\n // checkboxes, links, and form controls rendered by custom cells. Inspect\n // the composed path so controls nested in another component's shadow root\n // are recognized too.\n const interactiveSelector = 'button, a, input, select, textarea, [contenteditable=\"true\"], snice-button, snice-checkbox, snice-input, snice-select';\n const fromInteractiveControl = e.composedPath().some((node) =>\n node instanceof Element && node.matches(interactiveSelector)\n );\n if (fromInteractiveControl) return;\n\n this.hasUserInteracted = true;\n\n const totalRows = this.totalRowCount;\n const totalColumns = this.totalColCount;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n this.focusedRow = Math.min(totalRows - 1, this.focusedRow + 1);\n this.updateFocusIndicator();\n this.scrollIntoView();\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n this.focusedRow = Math.max(-1, this.focusedRow - 1);\n this.updateFocusIndicator();\n this.scrollIntoView();\n break;\n\n case 'ArrowRight':\n e.preventDefault();\n this.focusedCol = Math.min(totalColumns - 1, this.focusedCol + 1);\n this.updateFocusIndicator();\n break;\n\n case 'ArrowLeft':\n e.preventDefault();\n this.focusedCol = Math.max(0, this.focusedCol - 1);\n this.updateFocusIndicator();\n break;\n\n case 'Home':\n e.preventDefault();\n if (e.ctrlKey) {\n this.focusedRow = -1;\n this.focusedCol = 0;\n } else {\n this.focusedCol = 0;\n }\n this.updateFocusIndicator();\n this.scrollIntoView();\n break;\n\n case 'End':\n e.preventDefault();\n if (e.ctrlKey) {\n this.focusedRow = totalRows - 1;\n this.focusedCol = totalColumns - 1;\n } else {\n this.focusedCol = totalColumns - 1;\n }\n this.updateFocusIndicator();\n this.scrollIntoView();\n break;\n\n case 'PageDown':\n e.preventDefault();\n this.focusedRow = Math.min(totalRows - 1, this.focusedRow + 10);\n this.updateFocusIndicator();\n this.scrollIntoView();\n break;\n\n case 'PageUp':\n e.preventDefault();\n this.focusedRow = Math.max(0, this.focusedRow - 10);\n this.updateFocusIndicator();\n this.scrollIntoView();\n break;\n\n case ' ':\n if (this.focusedRow >= 0) {\n e.preventDefault();\n this.options.onSelectionToggle?.(this.focusedRow);\n }\n break;\n\n case 'Enter':\n if (this.focusedRow >= 0 && this.focusedCol >= 0) {\n e.preventDefault();\n const row = this.getBodyRow(this.focusedRow);\n const cells = row?.querySelectorAll('td');\n const cell = cells?.[this.focusedCol];\n const columnKey = cell?.getAttribute('data-key') || '';\n this.options.onCellActivate?.(this.focusedRow, columnKey);\n }\n break;\n\n case 'a':\n if (e.ctrlKey || e.metaKey) {\n e.preventDefault();\n this.options.onSelectAll?.();\n }\n break;\n\n case 'Escape':\n // Let this bubble up to cancel editing\n break;\n }\n }\n\n private scrollIntoView() {\n const table = this.getTable();\n if (!table) return;\n\n const focusedEl = table.querySelector('[data-grid-focus]') as HTMLElement;\n if (focusedEl) {\n // Guard: layout APIs are not implemented in some non-browser DOMs.\n if (typeof focusedEl.scrollIntoView === 'function') {\n focusedEl.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n }\n if (typeof focusedEl.focus === 'function') {\n focusedEl.focus({ preventScroll: true });\n }\n }\n }\n\n isAttached(): boolean {\n return this.attached;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"table-keyboard.js","sources":["../../../../components/table/table-keyboard.ts"],"sourcesContent":["/**\n * Keyboard navigation and ARIA for snice-table.\n * Full WAI-ARIA grid pattern with arrow key navigation.\n */\n\nexport interface KeyboardOptions {\n /** The shadow root containing the table */\n shadowRoot: ShadowRoot;\n /** Callback when a cell is activated (Enter) */\n onCellActivate?: (rowIndex: number, columnKey: string) => void;\n /** Callback when selection changes via keyboard */\n onSelectionToggle?: (rowIndex: number) => void;\n /** Callback for select-all (Ctrl+A) */\n onSelectAll?: () => void;\n /** Tab navigation mode */\n tabMode: 'none' | 'content' | 'header' | 'all';\n /**\n * Total data rows. A getter callback keeps bounds live as the dataset is\n * loaded / filtered / paginated; a plain number is accepted for callers that\n * re-attach on every change.\n */\n totalRows: number | (() => number);\n /** Total columns. Getter callback or plain number — see `totalRows`. */\n totalColumns: number | (() => number);\n /**\n * Total rows exposed to assistive technology. This may exceed the keyboard\n * navigation count when the body also contains non-focusable group and\n * aggregate rows. Defaults to totalRows.\n */\n ariaRows?: number | (() => number);\n /** Zero-based logical offset of the first rendered body row (virtual grids). */\n ariaRowOffset?: number | (() => number);\n /**\n * Resolve a keyboard-navigation row to its rendered <tr>. Grouping/filtering\n * can make navigation order differ from raw data-index order.\n */\n getRowElement?: (rowIndex: number) => HTMLElement | null;\n /**\n * Bring a logical (data-index) row into the rendered window before focus\n * lands on it. Under virtualization only the current window is in the DOM;\n * the host scrolls the row's range into view and renders it synchronously.\n */\n ensureRowVisible?: (rowIndex: number) => void;\n /** Whether editing is active */\n isEditing: () => boolean;\n}\n\nexport class TableKeyboard {\n private options: KeyboardOptions;\n private focusedRow = -1; // -1 = header\n /** True after the first real user keydown — no focus paint at rest. */\n private hasUserInteracted = false;\n private focusedCol = 0;\n // EventListener-typed: ShadowRoot's TS event map only knows 'slotchange',\n // so a KeyboardEvent-typed handler fails the addEventListener overloads.\n private keyHandler: EventListener;\n private attached = false;\n\n constructor() {\n this.options = {} as KeyboardOptions;\n this.keyHandler = (e: Event) => this.handleKeyDown(e as KeyboardEvent);\n }\n\n attach(options: KeyboardOptions) {\n this.options = options;\n const root = options.shadowRoot;\n if (!root) return;\n\n // Delegate keydown at the shadow root — a stable ancestor that always\n // exists (even before the first render) and survives every <table> rebuild.\n // Every keydown inside the shadow tree bubbles here, so navigation works no\n // matter when data/columns arrive or how often renderHeader/renderBody\n // replace the table structure. Binding to the inner <table> instead meant a\n // table built after @ready was never listened to, and a rebuilt table\n // silently dropped the listener.\n root.addEventListener('keydown', this.keyHandler);\n this.attached = true;\n this.syncGridRole();\n this.applyARIA();\n }\n\n detach() {\n this.options.shadowRoot?.removeEventListener('keydown', this.keyHandler);\n this.attached = false;\n }\n\n /**\n * (Re)apply the grid identity to the current <table>: role=grid, a focusable\n * tabindex, and live row/col counts. Cheap, idempotent, and safe to call\n * before the table exists (no-op). A structural rebuild that swaps the\n * <table> node drops these attributes, so they must be re-applied afterwards.\n */\n private syncGridRole() {\n const table = this.getTable();\n if (!table) return;\n\n table.setAttribute('role', 'grid');\n table.setAttribute('tabindex', '0');\n table.setAttribute('aria-rowcount', String(this.ariaRowCount + 1)); // +1 header\n table.setAttribute('aria-colcount', String(this.totalColCount));\n }\n\n /**\n * Host hook: called after renderHeader/renderBody rebuild the table. The\n * delegated keydown listener lives on the shadow root so it survives on its\n * own; this restores the grid role and the roving tabindex on the focused\n * cell, both of which a wiped thead/tbody (or a swapped <table>) would lose.\n */\n refresh() {\n if (!this.attached) return;\n\n this.syncGridRole();\n this.applyARIA();\n }\n\n private getTable(): HTMLElement | null {\n return this.options.shadowRoot?.querySelector('table') ?? null;\n }\n\n /** Resolve a bound that may be a live getter or a static number. */\n private resolveBound(v: number | (() => number) | undefined): number {\n return typeof v === 'function' ? v() : (v ?? 0);\n }\n\n private get totalRowCount(): number {\n return this.resolveBound(this.options.totalRows);\n }\n\n private get totalColCount(): number {\n return this.resolveBound(this.options.totalColumns);\n }\n\n private get ariaRowCount(): number {\n return this.resolveBound(this.options.ariaRows ?? this.options.totalRows);\n }\n\n private get ariaRowOffset(): number {\n return Math.max(0, this.resolveBound(this.options.ariaRowOffset));\n }\n\n /** Resolve a body row by its logical data-index (not its DOM position). */\n private getBodyRow(index: number): HTMLElement | null {\n if (this.options.getRowElement) return this.options.getRowElement(index);\n const table = this.getTable();\n if (!table) return null;\n return table.querySelector(`tbody tr[data-index=\"${index}\"]`);\n }\n\n /** Apply ARIA attributes to the grid */\n applyARIA() {\n const table = this.getTable();\n if (!table) return;\n\n table.setAttribute('aria-rowcount', String(this.ariaRowCount + 1)); // +1 for header\n table.setAttribute('aria-colcount', String(this.totalColCount));\n\n // Header row\n const headerRow = table.querySelector('thead tr.column-header-row')\n ?? table.querySelector('thead tr:not(.column-group-row):not(.header-filter-row)');\n if (headerRow) {\n headerRow.setAttribute('role', 'row');\n headerRow.setAttribute('aria-rowindex', '1');\n const ths = headerRow.querySelectorAll('th');\n ths.forEach((th, i) => {\n th.setAttribute('role', 'columnheader');\n th.setAttribute('aria-colindex', String(i + 1));\n\n // Sort state\n if (th.classList.contains('sortable')) {\n const sortIndicator = th.querySelector('.sort-indicator.active');\n if (sortIndicator) {\n const text = sortIndicator.textContent?.trim() || '';\n if (text.includes('▲')) th.setAttribute('aria-sort', 'ascending');\n else if (text.includes('▼')) th.setAttribute('aria-sort', 'descending');\n } else {\n th.setAttribute('aria-sort', 'none');\n }\n }\n });\n }\n\n // Body rows\n const bodyRows = table.querySelectorAll('tbody tr:not(.virtual-spacer):not(.table-fill-row)');\n bodyRows.forEach((row, i) => {\n row.setAttribute('role', 'row');\n row.setAttribute('aria-rowindex', String(this.ariaRowOffset + i + 2)); // +2 for 1-based + header\n\n const isSelected = row.getAttribute('data-selected') === 'true';\n if (isSelected) row.setAttribute('aria-selected', 'true');\n else row.removeAttribute('aria-selected');\n\n const cells = row.querySelectorAll('td');\n cells.forEach((cell, j) => {\n cell.setAttribute('role', 'gridcell');\n cell.setAttribute('aria-colindex', String(j + 1));\n });\n });\n\n // Set roving tabindex on focused cell\n this.updateFocusIndicator();\n }\n\n /** Set focus position */\n setFocus(row: number, col: number) {\n this.focusedRow = row;\n this.focusedCol = col;\n this.updateFocusIndicator();\n }\n\n /** Get current focus position */\n getFocus(): { row: number; col: number } {\n return { row: this.focusedRow, col: this.focusedCol };\n }\n\n private updateFocusIndicator() {\n // A grid that was never keyboard-focused must not wear focus styling —\n // refresh() runs after every renderBody, and painting the default header\n // position (focusedRow=-1) put a stuck blue ring on every table at rest.\n if (!this.hasUserInteracted) return;\n\n const table = this.getTable();\n if (!table) return;\n\n // Remove previous focus\n table.querySelectorAll('[data-grid-focus]').forEach(el => {\n el.removeAttribute('data-grid-focus');\n el.removeAttribute('tabindex');\n });\n\n // Find target cell\n let targetCell: HTMLElement | null = null;\n if (this.focusedRow === -1) {\n // Header\n const ths = table.querySelectorAll('thead th');\n targetCell = ths[this.focusedCol] as HTMLElement;\n } else {\n // Resolve by logical data-index, not DOM position: under virtualization\n // only the current window is in the DOM.\n let row = this.getBodyRow(this.focusedRow);\n if (!row) {\n // Outside the rendered window — ask the host to scroll it into view,\n // then look again.\n this.options.ensureRowVisible?.(this.focusedRow);\n row = this.getBodyRow(this.focusedRow);\n }\n if (row) {\n const cells = row.querySelectorAll('td');\n targetCell = cells[this.focusedCol] as HTMLElement;\n }\n }\n\n if (targetCell) {\n targetCell.setAttribute('data-grid-focus', 'true');\n targetCell.setAttribute('tabindex', '0');\n }\n }\n\n private handleKeyDown(e: KeyboardEvent) {\n // Don't handle when editing\n if (this.options.isEditing()) return;\n\n // Let controls inside the grid keep their native keyboard contract. This\n // includes group/tree toggle buttons (Enter/Space must click them), row\n // checkboxes, links, and form controls rendered by custom cells. Inspect\n // the composed path so controls nested in another component's shadow root\n // are recognized too.\n const interactiveSelector = 'button, a, input, select, textarea, [contenteditable=\"true\"], snice-button, snice-checkbox, snice-input, snice-select';\n const fromInteractiveControl = e.composedPath().some((node) =>\n node instanceof Element && node.matches(interactiveSelector)\n );\n if (fromInteractiveControl) return;\n\n this.hasUserInteracted = true;\n\n const totalRows = this.totalRowCount;\n const totalColumns = this.totalColCount;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n this.focusedRow = Math.min(totalRows - 1, this.focusedRow + 1);\n this.updateFocusIndicator();\n this.scrollIntoView();\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n this.focusedRow = Math.max(-1, this.focusedRow - 1);\n this.updateFocusIndicator();\n this.scrollIntoView();\n break;\n\n case 'ArrowRight':\n e.preventDefault();\n this.focusedCol = Math.min(totalColumns - 1, this.focusedCol + 1);\n this.updateFocusIndicator();\n break;\n\n case 'ArrowLeft':\n e.preventDefault();\n this.focusedCol = Math.max(0, this.focusedCol - 1);\n this.updateFocusIndicator();\n break;\n\n case 'Home':\n e.preventDefault();\n if (e.ctrlKey) {\n this.focusedRow = -1;\n this.focusedCol = 0;\n } else {\n this.focusedCol = 0;\n }\n this.updateFocusIndicator();\n this.scrollIntoView();\n break;\n\n case 'End':\n e.preventDefault();\n if (e.ctrlKey) {\n this.focusedRow = totalRows - 1;\n this.focusedCol = totalColumns - 1;\n } else {\n this.focusedCol = totalColumns - 1;\n }\n this.updateFocusIndicator();\n this.scrollIntoView();\n break;\n\n case 'PageDown':\n e.preventDefault();\n this.focusedRow = Math.min(totalRows - 1, this.focusedRow + 10);\n this.updateFocusIndicator();\n this.scrollIntoView();\n break;\n\n case 'PageUp':\n e.preventDefault();\n this.focusedRow = Math.max(0, this.focusedRow - 10);\n this.updateFocusIndicator();\n this.scrollIntoView();\n break;\n\n case ' ':\n if (this.focusedRow >= 0) {\n e.preventDefault();\n this.options.onSelectionToggle?.(this.focusedRow);\n }\n break;\n\n case 'Enter':\n if (this.focusedRow >= 0 && this.focusedCol >= 0) {\n e.preventDefault();\n const row = this.getBodyRow(this.focusedRow);\n const cells = row?.querySelectorAll('td');\n const cell = cells?.[this.focusedCol];\n const columnKey = cell?.getAttribute('data-key') || '';\n this.options.onCellActivate?.(this.focusedRow, columnKey);\n }\n break;\n\n case 'a':\n if (e.ctrlKey || e.metaKey) {\n e.preventDefault();\n this.options.onSelectAll?.();\n }\n break;\n\n case 'Escape':\n // Let this bubble up to cancel editing\n break;\n }\n }\n\n private scrollIntoView() {\n const table = this.getTable();\n if (!table) return;\n\n const focusedEl = table.querySelector('[data-grid-focus]') as HTMLElement;\n if (focusedEl) {\n // Guard: layout APIs are not implemented in some non-browser DOMs.\n if (typeof focusedEl.scrollIntoView === 'function') {\n focusedEl.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n }\n if (typeof focusedEl.focus === 'function') {\n focusedEl.focus({ preventScroll: true });\n }\n }\n }\n\n isAttached(): boolean {\n return this.attached;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -19,12 +19,21 @@ export declare class TableMasterDetail {
19
19
  private options;
20
20
  private tableElement;
21
21
  private contentCache;
22
+ /** Last value signature seen per row, so in-place mutations evict the cache. */
23
+ private fingerprints;
22
24
  private rowByIndex;
23
25
  private measuredHeights;
24
26
  onHeightChange: (() => void) | null;
25
27
  attach(tableEl: HTMLElement, options: DetailPanelOptions): void;
26
- /** Eagerly construct detail content when lazy=false. */
27
- prepare(rows: any[]): void;
28
+ /**
29
+ * Eagerly construct detail content when lazy=false.
30
+ *
31
+ * `fingerprint` (the table's per-row value signature) also evicts rows whose
32
+ * fields were mutated in place: the cache is keyed by row identity, so an
33
+ * unchanged identity would otherwise keep serving a panel built from the
34
+ * pre-mutation values.
35
+ */
36
+ prepare(rows: any[], fingerprint?: (row: any) => string): void;
28
37
  /** Check if a row is expanded */
29
38
  isExpanded(rowIndex: number): boolean;
30
39
  /** Toggle expansion of a row */
@@ -8,6 +8,8 @@ class TableMasterDetail {
8
8
  this.options = null;
9
9
  this.tableElement = null;
10
10
  this.contentCache = new Map();
11
+ /** Last value signature seen per row, so in-place mutations evict the cache. */
12
+ this.fingerprints = new Map();
11
13
  this.rowByIndex = new Map();
12
14
  this.measuredHeights = new Map();
13
15
  this.onHeightChange = null;
@@ -16,18 +18,33 @@ class TableMasterDetail {
16
18
  this.tableElement = tableEl;
17
19
  this.options = options;
18
20
  this.contentCache.clear();
21
+ this.fingerprints.clear();
19
22
  this.rowByIndex.clear();
20
23
  this.measuredHeights.clear();
21
24
  }
22
- /** Eagerly construct detail content when lazy=false. */
23
- prepare(rows) {
25
+ /**
26
+ * Eagerly construct detail content when lazy=false.
27
+ *
28
+ * `fingerprint` (the table's per-row value signature) also evicts rows whose
29
+ * fields were mutated in place: the cache is keyed by row identity, so an
30
+ * unchanged identity would otherwise keep serving a panel built from the
31
+ * pre-mutation values.
32
+ */
33
+ prepare(rows, fingerprint) {
24
34
  if (!this.options)
25
35
  return;
26
36
  this.rowByIndex = new Map(rows.map((row, index) => [index, row]));
27
37
  const currentRows = new Set(rows);
28
- for (const row of this.contentCache.keys()) {
38
+ for (const row of [...this.contentCache.keys()]) {
29
39
  if (!currentRows.has(row))
30
40
  this.contentCache.delete(row);
41
+ else if (fingerprint && this.fingerprints.get(row) !== fingerprint(row)) {
42
+ this.contentCache.delete(row);
43
+ this.measuredHeights.delete(row);
44
+ }
45
+ }
46
+ if (fingerprint) {
47
+ this.fingerprints = new Map(rows.map(row => [row, fingerprint(row)]));
31
48
  }
32
49
  for (const row of this.measuredHeights.keys()) {
33
50
  if (!currentRows.has(row))
@@ -1 +1 @@
1
- {"version":3,"file":"table-master-detail.js","sources":["../../../../components/table/table-master-detail.ts"],"sourcesContent":["/**\n * Master-detail (row expansion) for snice-table.\n * Expand a row to reveal a detail panel below it.\n */\n\nexport interface DetailPanelOptions {\n /** Render detail content for a row. Return HTML string or HTMLElement. */\n getDetailContent: (row: any, rowIndex: number) => string | HTMLElement;\n /** Detail panel height. 'auto' for content-based. Default: 'auto' */\n detailHeight?: string | number;\n /** Lazy: create on expand, destroy on collapse. Default: true */\n lazy?: boolean;\n /** Expand icon (collapsed state) */\n expandIcon?: string;\n /** Collapse icon (expanded state) */\n collapseIcon?: string;\n}\n\nexport class TableMasterDetail {\n private expandedRows: Set<number> = new Set();\n private options: DetailPanelOptions | null = null;\n private tableElement: HTMLElement | null = null;\n private contentCache = new Map<any, string | HTMLElement>();\n private rowByIndex = new Map<number, any>();\n private measuredHeights = new Map<any, number>();\n onHeightChange: (() => void) | null = null;\n\n attach(tableEl: HTMLElement, options: DetailPanelOptions) {\n this.tableElement = tableEl;\n this.options = options;\n this.contentCache.clear();\n this.rowByIndex.clear();\n this.measuredHeights.clear();\n }\n\n /** Eagerly construct detail content when lazy=false. */\n prepare(rows: any[]) {\n if (!this.options) return;\n this.rowByIndex = new Map(rows.map((row, index) => [index, row]));\n const currentRows = new Set(rows);\n for (const row of this.contentCache.keys()) {\n if (!currentRows.has(row)) this.contentCache.delete(row);\n }\n for (const row of this.measuredHeights.keys()) {\n if (!currentRows.has(row)) this.measuredHeights.delete(row);\n }\n if (this.options.lazy === false) rows.forEach((row, index) => this.getContent(row, index));\n }\n\n /** Check if a row is expanded */\n isExpanded(rowIndex: number): boolean {\n return this.expandedRows.has(rowIndex);\n }\n\n /** Toggle expansion of a row */\n toggle(rowIndex: number) {\n if (this.expandedRows.has(rowIndex)) {\n this.collapse(rowIndex);\n } else {\n this.expand(rowIndex);\n }\n }\n\n /** Expand a row */\n expand(rowIndex: number) {\n this.expandedRows.add(rowIndex);\n this.tableElement?.dispatchEvent(new CustomEvent('row-expand', {\n detail: { rowIndex },\n bubbles: true,\n composed: true,\n }));\n }\n\n /** Collapse a row */\n collapse(rowIndex: number) {\n this.expandedRows.delete(rowIndex);\n if (this.options?.lazy !== false) {\n const row = this.rowByIndex.get(rowIndex);\n if (row !== undefined) {\n this.contentCache.delete(row);\n this.measuredHeights.delete(row);\n }\n }\n this.tableElement?.dispatchEvent(new CustomEvent('row-collapse', {\n detail: { rowIndex },\n bubbles: true,\n composed: true,\n }));\n }\n\n /** Expand all rows */\n expandAll(totalRows: number) {\n for (let i = 0; i < totalRows; i++) {\n this.expandedRows.add(i);\n }\n }\n\n /** Collapse all rows */\n collapseAll() {\n this.expandedRows.clear();\n if (this.options?.lazy !== false) {\n this.contentCache.clear();\n this.measuredHeights.clear();\n }\n }\n\n /** Get set of expanded row indices */\n getExpandedRows(): Set<number> {\n return new Set(this.expandedRows);\n }\n\n /** Replace expansion indices without emitting user-action events. */\n setExpandedRows(rowIndices: Iterable<number>) {\n const next = new Set(rowIndices);\n if (this.options?.lazy !== false) {\n for (const index of this.expandedRows) {\n if (next.has(index)) continue;\n const row = this.rowByIndex.get(index);\n if (row !== undefined) {\n this.contentCache.delete(row);\n this.measuredHeights.delete(row);\n }\n }\n }\n this.expandedRows = next;\n }\n\n /** Create the expand/collapse toggle button — uses same chevron SVG as snice-accordion */\n createToggleButton(rowIndex: number): HTMLElement {\n const expanded = this.isExpanded(rowIndex);\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = `detail-toggle${expanded ? ' detail-toggle--expanded' : ''}`;\n btn.setAttribute('aria-expanded', String(expanded));\n btn.setAttribute('aria-label', expanded ? 'Collapse row' : 'Expand row');\n const customIcon = expanded ? this.options?.collapseIcon : this.options?.expandIcon;\n if (customIcon) {\n if (customIcon.trim().startsWith('<')) btn.innerHTML = customIcon;\n else btn.textContent = customIcon;\n } else {\n btn.innerHTML = `<svg class=\"detail-toggle-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M6 9l6 6 6-6\"/></svg>`;\n }\n\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n this.toggle(rowIndex);\n this.tableElement?.dispatchEvent(new CustomEvent('detail-toggle', {\n detail: { rowIndex, expanded: this.isExpanded(rowIndex) },\n bubbles: true,\n composed: true,\n }));\n });\n\n return btn;\n }\n\n /** Create the detail panel row (a <tr> with a single <td> spanning all columns) */\n createDetailRow(row: any, rowIndex: number, colSpan: number): HTMLTableRowElement | null {\n if (!this.options || !this.isExpanded(rowIndex)) return null;\n\n const tr = document.createElement('tr');\n tr.className = 'detail-row';\n tr.setAttribute('data-detail-for', String(rowIndex));\n\n const td = document.createElement('td');\n td.colSpan = colSpan;\n td.className = 'detail-cell';\n td.style.padding = '0';\n td.style.border = 'none';\n\n // Animated wrapper — same pattern as snice-accordion\n const wrapper = document.createElement('div');\n wrapper.className = 'detail-content';\n\n const inner = document.createElement('div');\n inner.className = 'detail-content-inner';\n\n const content = this.getContent(row, rowIndex);\n if (typeof content === 'string') {\n inner.innerHTML = content;\n } else if (content instanceof HTMLElement) {\n inner.appendChild(content);\n }\n\n wrapper.appendChild(inner);\n td.appendChild(wrapper);\n\n // Trigger animation after insertion\n requestAnimationFrame(() => {\n const configuredHeight = this.options?.detailHeight;\n const height = configuredHeight === undefined || configuredHeight === 'auto'\n ? `${inner.scrollHeight}px`\n : (typeof configuredHeight === 'number' ? `${configuredHeight}px` : configuredHeight);\n if (configuredHeight !== undefined && configuredHeight !== 'auto') {\n inner.style.height = height;\n inner.style.overflow = 'auto';\n }\n wrapper.style.setProperty('--detail-max-height', height);\n wrapper.classList.add('detail-content--open');\n const measuredHeight = inner.getBoundingClientRect().height || inner.scrollHeight;\n const previousHeight = this.measuredHeights.get(row);\n if (measuredHeight > 0 && measuredHeight !== previousHeight) {\n this.measuredHeights.set(row, measuredHeight);\n this.onHeightChange?.();\n }\n });\n\n tr.appendChild(td);\n return tr;\n }\n\n isEnabled(): boolean {\n return this.options !== null;\n }\n\n /** Extra height contributed by an expanded detail row. Auto/non-pixel\n * panels use the most recent browser measurement. */\n getFixedAdditionalHeight(rowIndex: number): number {\n if (!this.isExpanded(rowIndex)) return 0;\n const configured = this.options?.detailHeight;\n if (typeof configured === 'number') return Math.max(0, configured);\n if (typeof configured === 'string' && /^\\d+(?:\\.\\d+)?px$/.test(configured.trim())) {\n return Math.max(0, Number.parseFloat(configured));\n }\n const row = this.rowByIndex.get(rowIndex);\n return row === undefined ? 0 : (this.measuredHeights.get(row) || 0);\n }\n\n private getContent(row: any, rowIndex: number): string | HTMLElement {\n if (!this.options) return '';\n this.rowByIndex.set(rowIndex, row);\n if (!this.contentCache.has(row)) {\n this.contentCache.set(row, this.options.getDetailContent(row, rowIndex));\n }\n return this.contentCache.get(row)!;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"table-master-detail.js","sources":["../../../../components/table/table-master-detail.ts"],"sourcesContent":["/**\n * Master-detail (row expansion) for snice-table.\n * Expand a row to reveal a detail panel below it.\n */\n\nexport interface DetailPanelOptions {\n /** Render detail content for a row. Return HTML string or HTMLElement. */\n getDetailContent: (row: any, rowIndex: number) => string | HTMLElement;\n /** Detail panel height. 'auto' for content-based. Default: 'auto' */\n detailHeight?: string | number;\n /** Lazy: create on expand, destroy on collapse. Default: true */\n lazy?: boolean;\n /** Expand icon (collapsed state) */\n expandIcon?: string;\n /** Collapse icon (expanded state) */\n collapseIcon?: string;\n}\n\nexport class TableMasterDetail {\n private expandedRows: Set<number> = new Set();\n private options: DetailPanelOptions | null = null;\n private tableElement: HTMLElement | null = null;\n private contentCache = new Map<any, string | HTMLElement>();\n /** Last value signature seen per row, so in-place mutations evict the cache. */\n private fingerprints = new Map<any, string>();\n private rowByIndex = new Map<number, any>();\n private measuredHeights = new Map<any, number>();\n onHeightChange: (() => void) | null = null;\n\n attach(tableEl: HTMLElement, options: DetailPanelOptions) {\n this.tableElement = tableEl;\n this.options = options;\n this.contentCache.clear();\n this.fingerprints.clear();\n this.rowByIndex.clear();\n this.measuredHeights.clear();\n }\n\n /**\n * Eagerly construct detail content when lazy=false.\n *\n * `fingerprint` (the table's per-row value signature) also evicts rows whose\n * fields were mutated in place: the cache is keyed by row identity, so an\n * unchanged identity would otherwise keep serving a panel built from the\n * pre-mutation values.\n */\n prepare(rows: any[], fingerprint?: (row: any) => string) {\n if (!this.options) return;\n this.rowByIndex = new Map(rows.map((row, index) => [index, row]));\n const currentRows = new Set(rows);\n for (const row of [...this.contentCache.keys()]) {\n if (!currentRows.has(row)) this.contentCache.delete(row);\n else if (fingerprint && this.fingerprints.get(row) !== fingerprint(row)) {\n this.contentCache.delete(row);\n this.measuredHeights.delete(row);\n }\n }\n if (fingerprint) {\n this.fingerprints = new Map(rows.map(row => [row, fingerprint(row)]));\n }\n for (const row of this.measuredHeights.keys()) {\n if (!currentRows.has(row)) this.measuredHeights.delete(row);\n }\n if (this.options.lazy === false) rows.forEach((row, index) => this.getContent(row, index));\n }\n\n /** Check if a row is expanded */\n isExpanded(rowIndex: number): boolean {\n return this.expandedRows.has(rowIndex);\n }\n\n /** Toggle expansion of a row */\n toggle(rowIndex: number) {\n if (this.expandedRows.has(rowIndex)) {\n this.collapse(rowIndex);\n } else {\n this.expand(rowIndex);\n }\n }\n\n /** Expand a row */\n expand(rowIndex: number) {\n this.expandedRows.add(rowIndex);\n this.tableElement?.dispatchEvent(new CustomEvent('row-expand', {\n detail: { rowIndex },\n bubbles: true,\n composed: true,\n }));\n }\n\n /** Collapse a row */\n collapse(rowIndex: number) {\n this.expandedRows.delete(rowIndex);\n if (this.options?.lazy !== false) {\n const row = this.rowByIndex.get(rowIndex);\n if (row !== undefined) {\n this.contentCache.delete(row);\n this.measuredHeights.delete(row);\n }\n }\n this.tableElement?.dispatchEvent(new CustomEvent('row-collapse', {\n detail: { rowIndex },\n bubbles: true,\n composed: true,\n }));\n }\n\n /** Expand all rows */\n expandAll(totalRows: number) {\n for (let i = 0; i < totalRows; i++) {\n this.expandedRows.add(i);\n }\n }\n\n /** Collapse all rows */\n collapseAll() {\n this.expandedRows.clear();\n if (this.options?.lazy !== false) {\n this.contentCache.clear();\n this.measuredHeights.clear();\n }\n }\n\n /** Get set of expanded row indices */\n getExpandedRows(): Set<number> {\n return new Set(this.expandedRows);\n }\n\n /** Replace expansion indices without emitting user-action events. */\n setExpandedRows(rowIndices: Iterable<number>) {\n const next = new Set(rowIndices);\n if (this.options?.lazy !== false) {\n for (const index of this.expandedRows) {\n if (next.has(index)) continue;\n const row = this.rowByIndex.get(index);\n if (row !== undefined) {\n this.contentCache.delete(row);\n this.measuredHeights.delete(row);\n }\n }\n }\n this.expandedRows = next;\n }\n\n /** Create the expand/collapse toggle button — uses same chevron SVG as snice-accordion */\n createToggleButton(rowIndex: number): HTMLElement {\n const expanded = this.isExpanded(rowIndex);\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = `detail-toggle${expanded ? ' detail-toggle--expanded' : ''}`;\n btn.setAttribute('aria-expanded', String(expanded));\n btn.setAttribute('aria-label', expanded ? 'Collapse row' : 'Expand row');\n const customIcon = expanded ? this.options?.collapseIcon : this.options?.expandIcon;\n if (customIcon) {\n if (customIcon.trim().startsWith('<')) btn.innerHTML = customIcon;\n else btn.textContent = customIcon;\n } else {\n btn.innerHTML = `<svg class=\"detail-toggle-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M6 9l6 6 6-6\"/></svg>`;\n }\n\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n this.toggle(rowIndex);\n this.tableElement?.dispatchEvent(new CustomEvent('detail-toggle', {\n detail: { rowIndex, expanded: this.isExpanded(rowIndex) },\n bubbles: true,\n composed: true,\n }));\n });\n\n return btn;\n }\n\n /** Create the detail panel row (a <tr> with a single <td> spanning all columns) */\n createDetailRow(row: any, rowIndex: number, colSpan: number): HTMLTableRowElement | null {\n if (!this.options || !this.isExpanded(rowIndex)) return null;\n\n const tr = document.createElement('tr');\n tr.className = 'detail-row';\n tr.setAttribute('data-detail-for', String(rowIndex));\n\n const td = document.createElement('td');\n td.colSpan = colSpan;\n td.className = 'detail-cell';\n td.style.padding = '0';\n td.style.border = 'none';\n\n // Animated wrapper — same pattern as snice-accordion\n const wrapper = document.createElement('div');\n wrapper.className = 'detail-content';\n\n const inner = document.createElement('div');\n inner.className = 'detail-content-inner';\n\n const content = this.getContent(row, rowIndex);\n if (typeof content === 'string') {\n inner.innerHTML = content;\n } else if (content instanceof HTMLElement) {\n inner.appendChild(content);\n }\n\n wrapper.appendChild(inner);\n td.appendChild(wrapper);\n\n // Trigger animation after insertion\n requestAnimationFrame(() => {\n const configuredHeight = this.options?.detailHeight;\n const height = configuredHeight === undefined || configuredHeight === 'auto'\n ? `${inner.scrollHeight}px`\n : (typeof configuredHeight === 'number' ? `${configuredHeight}px` : configuredHeight);\n if (configuredHeight !== undefined && configuredHeight !== 'auto') {\n inner.style.height = height;\n inner.style.overflow = 'auto';\n }\n wrapper.style.setProperty('--detail-max-height', height);\n wrapper.classList.add('detail-content--open');\n const measuredHeight = inner.getBoundingClientRect().height || inner.scrollHeight;\n const previousHeight = this.measuredHeights.get(row);\n if (measuredHeight > 0 && measuredHeight !== previousHeight) {\n this.measuredHeights.set(row, measuredHeight);\n this.onHeightChange?.();\n }\n });\n\n tr.appendChild(td);\n return tr;\n }\n\n isEnabled(): boolean {\n return this.options !== null;\n }\n\n /** Extra height contributed by an expanded detail row. Auto/non-pixel\n * panels use the most recent browser measurement. */\n getFixedAdditionalHeight(rowIndex: number): number {\n if (!this.isExpanded(rowIndex)) return 0;\n const configured = this.options?.detailHeight;\n if (typeof configured === 'number') return Math.max(0, configured);\n if (typeof configured === 'string' && /^\\d+(?:\\.\\d+)?px$/.test(configured.trim())) {\n return Math.max(0, Number.parseFloat(configured));\n }\n const row = this.rowByIndex.get(rowIndex);\n return row === undefined ? 0 : (this.measuredHeights.get(row) || 0);\n }\n\n private getContent(row: any, rowIndex: number): string | HTMLElement {\n if (!this.options) return '';\n this.rowByIndex.set(rowIndex, row);\n if (!this.contentCache.has(row)) {\n this.contentCache.set(row, this.options.getDetailContent(row, rowIndex));\n }\n return this.contentCache.get(row)!;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -2,7 +2,6 @@ import type { SniceTagInputElement } from './snice-tag-input.types';
2
2
  export declare class SniceTagInput extends HTMLElement implements SniceTagInputElement {
3
3
  internals: ElementInternals;
4
4
  private dirtyValue;
5
- private suppressValueEvent;
6
5
  private customValidationMessage;
7
6
  private readonly labelAssociation;
8
7
  private validationInput?;
@@ -11,7 +10,8 @@ export declare class SniceTagInput extends HTMLElement implements SniceTagInputE
11
10
  private constraintInvalid;
12
11
  constructor();
13
12
  /**
14
- * Live tags. Assignments do not rewrite the authored reset default.
13
+ * Live tags. Assignments do not rewrite the authored reset default and do
14
+ * not emit `tag-change`; only tag edits do.
15
15
  * @public
16
16
  */
17
17
  get value(): string[];