snice 7.8.0 → 7.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/adapters/react/SniceProvider.js +1 -1
- package/adapters/react/SniceRouter.js +1 -1
- package/adapters/react/matchRoute.js +1 -1
- package/adapters/react/table.d.ts +1 -0
- package/adapters/react/table.d.ts.map +1 -1
- package/adapters/react/table.js +1 -1
- package/adapters/react/table.js.map +1 -1
- package/adapters/react/table.tsx +2 -1
- package/adapters/react/useRequestHandler.js +1 -1
- package/bin/analyzer-contracts.js +17 -1
- package/custom-elements.json +57 -3
- package/dist/cdn/accordion/snice-accordion.js +1 -1
- package/dist/cdn/action-bar/snice-action-bar.js +1 -1
- package/dist/cdn/activity-feed/snice-activity-feed.js +1 -1
- package/dist/cdn/alert/snice-alert.js +1 -1
- package/dist/cdn/app-tiles/snice-app-tiles.js +1 -1
- package/dist/cdn/approval-flow/snice-approval-flow.js +1 -1
- package/dist/cdn/audio-recorder/snice-audio-recorder.js +1 -1
- package/dist/cdn/availability/snice-availability.js +1 -1
- package/dist/cdn/avatar/snice-avatar.js +1 -1
- package/dist/cdn/avatar-group/snice-avatar-group.js +1 -1
- package/dist/cdn/badge/snice-badge.js +1 -1
- package/dist/cdn/banner/snice-banner.js +1 -1
- package/dist/cdn/binpack/snice-binpack.js +1 -1
- package/dist/cdn/book/snice-book.js +1 -1
- package/dist/cdn/booking/snice-booking.js +1 -1
- package/dist/cdn/breadcrumbs/snice-breadcrumbs.js +1 -1
- package/dist/cdn/button/snice-button.js +1 -1
- package/dist/cdn/calendar/README.md +2 -2
- package/dist/cdn/calendar/snice-calendar.js +537 -44
- package/dist/cdn/calendar/snice-calendar.js.map +1 -1
- package/dist/cdn/calendar/snice-calendar.min.js +2 -2
- package/dist/cdn/calendar/snice-calendar.min.js.map +1 -1
- package/dist/cdn/camera/snice-camera.js +1 -1
- package/dist/cdn/camera-annotate/snice-camera-annotate.js +1 -1
- package/dist/cdn/candlestick/snice-candlestick.js +1 -1
- package/dist/cdn/card/snice-card.js +1 -1
- package/dist/cdn/carousel/snice-carousel.js +1 -1
- package/dist/cdn/cart/snice-cart.js +1 -1
- package/dist/cdn/chart/snice-chart.js +1 -1
- package/dist/cdn/chat/snice-chat.js +1 -1
- package/dist/cdn/checkbox/snice-checkbox.js +1 -1
- package/dist/cdn/chip/snice-chip.js +1 -1
- package/dist/cdn/code-block/README.md +2 -2
- package/dist/cdn/code-block/snice-code-block.js +177 -167
- package/dist/cdn/code-block/snice-code-block.js.map +1 -1
- package/dist/cdn/code-block/snice-code-block.min.js +2 -2
- package/dist/cdn/code-block/snice-code-block.min.js.map +1 -1
- package/dist/cdn/color-display/snice-color-display.js +1 -1
- package/dist/cdn/color-picker/snice-color-picker.js +1 -1
- package/dist/cdn/command-palette/README.md +1 -1
- package/dist/cdn/command-palette/snice-command-palette.js +2 -2
- package/dist/cdn/command-palette/snice-command-palette.js.map +1 -1
- package/dist/cdn/command-palette/snice-command-palette.min.js +1 -1
- package/dist/cdn/command-palette/snice-command-palette.min.js.map +1 -1
- package/dist/cdn/comments/snice-comments.js +1 -1
- package/dist/cdn/countdown/snice-countdown.js +1 -1
- package/dist/cdn/cropper/snice-cropper.js +1 -1
- package/dist/cdn/data-card/snice-data-card.js +1 -1
- package/dist/cdn/date-picker/snice-date-picker.js +1 -1
- package/dist/cdn/date-range-picker/snice-date-range-picker.js +1 -1
- package/dist/cdn/date-time-picker/snice-date-time-picker.js +1 -1
- package/dist/cdn/diff/snice-diff.js +1 -1
- package/dist/cdn/divider/snice-divider.js +1 -1
- package/dist/cdn/doc/snice-doc.js +1 -1
- package/dist/cdn/draw/snice-draw.js +1 -1
- package/dist/cdn/drawer/snice-drawer.js +1 -1
- package/dist/cdn/empty-state/snice-empty-state.js +1 -1
- package/dist/cdn/estimate/snice-estimate.js +1 -1
- package/dist/cdn/file-gallery/snice-file-gallery.js +1 -1
- package/dist/cdn/file-upload/snice-file-upload.js +1 -1
- package/dist/cdn/flip-card/snice-flip-card.js +1 -1
- package/dist/cdn/flow/snice-flow.js +1 -1
- package/dist/cdn/form-layout/snice-form-layout.js +1 -1
- package/dist/cdn/funnel/snice-funnel.js +1 -1
- package/dist/cdn/gantt/snice-gantt.js +1 -1
- package/dist/cdn/gauge/snice-gauge.js +1 -1
- package/dist/cdn/grid/snice-grid.js +1 -1
- package/dist/cdn/heatmap/snice-heatmap.js +1 -1
- package/dist/cdn/image/snice-image.js +1 -1
- package/dist/cdn/input/snice-input.js +1 -1
- package/dist/cdn/invoice/snice-invoice.js +1 -1
- package/dist/cdn/kanban/snice-kanban.js +1 -1
- package/dist/cdn/key-value/snice-key-value.js +1 -1
- package/dist/cdn/kpi/snice-kpi.js +1 -1
- package/dist/cdn/layout/snice-layout.js +1 -1
- package/dist/cdn/leaderboard/snice-leaderboard.js +1 -1
- package/dist/cdn/link/snice-link.js +1 -1
- package/dist/cdn/link-preview/snice-link-preview.js +1 -1
- package/dist/cdn/list/snice-list.js +1 -1
- package/dist/cdn/location/snice-location.js +1 -1
- package/dist/cdn/login/snice-login.js +1 -1
- package/dist/cdn/map/snice-map.js +1 -1
- package/dist/cdn/markdown/snice-markdown.js +1 -1
- package/dist/cdn/masonry/snice-masonry.js +1 -1
- package/dist/cdn/menu/snice-menu.js +1 -1
- package/dist/cdn/message-strip/snice-message-strip.js +1 -1
- package/dist/cdn/modal/snice-modal.js +1 -1
- package/dist/cdn/music-player/snice-music-player.js +1 -1
- package/dist/cdn/nav/snice-nav.js +1 -1
- package/dist/cdn/network-graph/snice-network-graph.js +1 -1
- package/dist/cdn/notification-center/snice-notification-center.js +1 -1
- package/dist/cdn/order-tracker/snice-order-tracker.js +1 -1
- package/dist/cdn/org-chart/snice-org-chart.js +1 -1
- package/dist/cdn/pagination/snice-pagination.js +1 -1
- package/dist/cdn/paint/snice-paint.js +1 -1
- package/dist/cdn/pdf-viewer/snice-pdf-viewer.js +1 -1
- package/dist/cdn/permission-matrix/snice-permission-matrix.js +1 -1
- package/dist/cdn/podcast-player/snice-podcast-player.js +1 -1
- package/dist/cdn/popover/snice-popover.js +1 -1
- package/dist/cdn/pricing-table/snice-pricing-table.js +1 -1
- package/dist/cdn/product-card/snice-product-card.js +1 -1
- package/dist/cdn/progress/snice-progress.js +2 -2
- package/dist/cdn/progress/snice-progress.js.map +1 -1
- package/dist/cdn/progress/snice-progress.min.js +1 -1
- package/dist/cdn/progress/snice-progress.min.js.map +1 -1
- package/dist/cdn/progress-ring/snice-progress-ring.js +1 -1
- package/dist/cdn/qr-code/README.md +2 -2
- package/dist/cdn/qr-code/snice-qr-code.js +19 -3
- package/dist/cdn/qr-code/snice-qr-code.js.map +1 -1
- package/dist/cdn/qr-code/snice-qr-code.min.js +2 -2
- package/dist/cdn/qr-code/snice-qr-code.min.js.map +1 -1
- package/dist/cdn/qr-reader/snice-qr-reader.js +1 -1
- package/dist/cdn/radio/snice-radio.js +1 -1
- package/dist/cdn/range-slider/snice-range-slider.js +1 -1
- package/dist/cdn/rating/snice-rating.js +1 -1
- package/dist/cdn/receipt/snice-receipt.js +5 -2
- package/dist/cdn/receipt/snice-receipt.js.map +1 -1
- package/dist/cdn/receipt/snice-receipt.min.js +1 -1
- package/dist/cdn/receipt/snice-receipt.min.js.map +1 -1
- package/dist/cdn/recipe/snice-recipe.js +1 -1
- package/dist/cdn/runtime/snice-runtime.esm.js +4 -4
- package/dist/cdn/runtime/snice-runtime.esm.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.esm.min.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.js +4 -4
- package/dist/cdn/runtime/snice-runtime.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.min.js.map +1 -1
- package/dist/cdn/sankey/snice-sankey.js +1 -1
- package/dist/cdn/segmented-control/snice-segmented-control.js +1 -1
- package/dist/cdn/select/README.md +2 -2
- package/dist/cdn/select/snice-select.js +6 -9
- package/dist/cdn/select/snice-select.js.map +1 -1
- package/dist/cdn/select/snice-select.min.js +2 -2
- package/dist/cdn/select/snice-select.min.js.map +1 -1
- package/dist/cdn/skeleton/snice-skeleton.js +1 -1
- package/dist/cdn/slider/snice-slider.js +1 -1
- package/dist/cdn/sortable/snice-sortable.js +1 -1
- package/dist/cdn/sparkline/snice-sparkline.js +1 -1
- package/dist/cdn/spinner/snice-spinner.js +1 -1
- package/dist/cdn/split-button/snice-split-button.js +1 -1
- package/dist/cdn/split-pane/snice-split-pane.js +1 -1
- package/dist/cdn/spotlight/snice-spotlight.js +1 -1
- package/dist/cdn/stat-group/snice-stat-group.js +1 -1
- package/dist/cdn/step-input/snice-step-input.js +1 -1
- package/dist/cdn/stepper/snice-stepper.js +1 -1
- package/dist/cdn/switch/snice-switch.js +1 -1
- package/dist/cdn/table/README.md +2 -2
- package/dist/cdn/table/snice-table.esm.js +1940 -407
- package/dist/cdn/table/snice-table.esm.js.map +1 -1
- package/dist/cdn/table/snice-table.esm.min.js +242 -53
- package/dist/cdn/table/snice-table.esm.min.js.map +1 -1
- package/dist/cdn/table/snice-table.js +1940 -407
- package/dist/cdn/table/snice-table.js.map +1 -1
- package/dist/cdn/table/snice-table.min.js +234 -45
- package/dist/cdn/table/snice-table.min.js.map +1 -1
- package/dist/cdn/tabs/snice-tabs.js +1 -1
- package/dist/cdn/tag/snice-tag.js +1 -1
- package/dist/cdn/tag-input/snice-tag-input.js +1 -1
- package/dist/cdn/terminal/snice-terminal.js +1 -1
- package/dist/cdn/testimonial/snice-testimonial.js +1 -1
- package/dist/cdn/textarea/snice-textarea.js +1 -1
- package/dist/cdn/time-picker/snice-time-picker.js +1 -1
- package/dist/cdn/time-range-picker/snice-time-range-picker.js +1 -1
- package/dist/cdn/timeline/snice-timeline.js +1 -1
- package/dist/cdn/timer/snice-timer.js +1 -1
- package/dist/cdn/toast/snice-toast.js +1 -1
- package/dist/cdn/tooltip/snice-tooltip.js +1 -1
- package/dist/cdn/tree/snice-tree.js +1 -1
- package/dist/cdn/treemap/snice-treemap.js +1 -1
- package/dist/cdn/user-card/snice-user-card.js +1 -1
- package/dist/cdn/video-player/snice-video-player.js +1 -1
- package/dist/cdn/virtual-scroller/snice-virtual-scroller.js +1 -1
- package/dist/cdn/waterfall/snice-waterfall.js +1 -1
- package/dist/cdn/weather/snice-weather.js +1 -1
- package/dist/cdn/work-order/snice-work-order.js +1 -1
- package/dist/components/calendar/snice-calendar.d.ts +187 -5
- package/dist/components/calendar/snice-calendar.js +537 -44
- package/dist/components/calendar/snice-calendar.js.map +1 -1
- package/dist/components/calendar/snice-calendar.types.d.ts +2 -1
- package/dist/components/code-block/formatter.d.ts +6 -0
- package/dist/components/code-block/formatter.js +174 -164
- package/dist/components/code-block/formatter.js.map +1 -1
- package/dist/components/code-block/grammars/snice.json +1 -1
- package/dist/components/code-block/grammars/typescript.json +1 -1
- package/dist/components/command-palette/snice-command-palette.js +1 -1
- package/dist/components/command-palette/snice-command-palette.js.map +1 -1
- package/dist/components/progress/snice-progress.js +1 -1
- package/dist/components/progress/snice-progress.js.map +1 -1
- package/dist/components/qr-code/qrcode.d.ts +1 -1
- package/dist/components/qr-code/qrcode.js +18 -2
- package/dist/components/qr-code/qrcode.js.map +1 -1
- package/dist/components/receipt/snice-receipt.js +4 -1
- package/dist/components/receipt/snice-receipt.js.map +1 -1
- package/dist/components/select/snice-select.d.ts +1 -1
- package/dist/components/select/snice-select.js +5 -8
- package/dist/components/select/snice-select.js.map +1 -1
- package/dist/components/table/snice-cell-actions.js +1 -1
- package/dist/components/table/snice-cell-actions.js.map +1 -1
- package/dist/components/table/snice-cell-color.js +1 -1
- package/dist/components/table/snice-cell-color.js.map +1 -1
- package/dist/components/table/snice-cell-date.js +20 -8
- package/dist/components/table/snice-cell-date.js.map +1 -1
- package/dist/components/table/snice-cell-email.js +1 -1
- package/dist/components/table/snice-cell-email.js.map +1 -1
- package/dist/components/table/snice-cell-image.js +1 -1
- package/dist/components/table/snice-cell-image.js.map +1 -1
- package/dist/components/table/snice-cell-json.js +1 -1
- package/dist/components/table/snice-cell-json.js.map +1 -1
- package/dist/components/table/snice-cell-link.js +1 -1
- package/dist/components/table/snice-cell-link.js.map +1 -1
- package/dist/components/table/snice-cell-location.js +1 -1
- package/dist/components/table/snice-cell-location.js.map +1 -1
- package/dist/components/table/snice-cell-phone.js +1 -1
- package/dist/components/table/snice-cell-phone.js.map +1 -1
- package/dist/components/table/snice-cell-status.js +1 -1
- package/dist/components/table/snice-cell-status.js.map +1 -1
- package/dist/components/table/snice-cell.d.ts +10 -0
- package/dist/components/table/snice-cell.js +59 -13
- package/dist/components/table/snice-cell.js.map +1 -1
- package/dist/components/table/snice-row.d.ts +0 -1
- package/dist/components/table/snice-row.js +4 -10
- package/dist/components/table/snice-row.js.map +1 -1
- package/dist/components/table/snice-table.d.ts +170 -2
- package/dist/components/table/snice-table.js +868 -189
- package/dist/components/table/snice-table.js.map +1 -1
- package/dist/components/table/snice-table.types.d.ts +17 -0
- package/dist/components/table/table-cell-presentation.d.ts +17 -4
- package/dist/components/table/table-cell-presentation.js +37 -11
- package/dist/components/table/table-cell-presentation.js.map +1 -1
- package/dist/components/table/table-column-manager.d.ts +153 -2
- package/dist/components/table/table-column-manager.js +364 -43
- package/dist/components/table/table-column-manager.js.map +1 -1
- package/dist/components/table/table-date.d.ts +29 -0
- package/dist/components/table/table-date.js +78 -0
- package/dist/components/table/table-date.js.map +1 -0
- package/dist/components/table/table-export.js +6 -2
- package/dist/components/table/table-export.js.map +1 -1
- package/dist/components/table/table-filter-engine.d.ts +9 -0
- package/dist/components/table/table-filter-engine.js +58 -14
- package/dist/components/table/table-filter-engine.js.map +1 -1
- package/dist/components/table/table-keyboard.d.ts +8 -0
- package/dist/components/table/table-keyboard.js +14 -3
- package/dist/components/table/table-keyboard.js.map +1 -1
- package/dist/components/table/table-toolbar.js +1 -0
- package/dist/components/table/table-toolbar.js.map +1 -1
- package/dist/components/table/table-tree-data.d.ts +2 -0
- package/dist/components/table/table-tree-data.js +35 -9
- package/dist/components/table/table-tree-data.js.map +1 -1
- package/dist/components/table/table-virtualizer.d.ts +44 -7
- package/dist/components/table/table-virtualizer.js +101 -54
- package/dist/components/table/table-virtualizer.js.map +1 -1
- package/dist/components/theme/theme.css +9 -0
- package/dist/index.cjs +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.iife.js +1 -1
- package/dist/react/SniceProvider.js +1 -1
- package/dist/react/SniceRouter.js +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/matchRoute.js +1 -1
- package/dist/react/useRequestHandler.js +1 -1
- package/dist/symbols.cjs +1 -1
- package/dist/symbols.esm.js +1 -1
- package/dist/testing-dom.cjs +1 -1
- package/dist/testing-dom.esm.js +1 -1
- package/dist/transitions.cjs +1 -1
- package/dist/transitions.esm.js +1 -1
- package/docs/ai/DEVELOPMENT.md +93 -2
- package/docs/ai/components/calendar.md +113 -10
- package/docs/ai/components/code-block.md +4 -0
- package/docs/ai/components/doc.md +1 -1
- package/docs/ai/components/progress.md +1 -0
- package/docs/ai/components/receipt.md +1 -1
- package/docs/ai/components/table.md +29 -30
- package/docs/ai/components/theme.md +1 -0
- package/docs/ai/events.md +30 -0
- package/docs/ai/theme.md +1 -0
- package/docs/components/calendar.md +171 -11
- package/docs/components/code-block.md +2 -0
- package/docs/components/doc.md +1 -1
- package/docs/components/progress.md +13 -0
- package/docs/components/receipt.md +1 -0
- package/docs/components/table.md +274 -15
- package/docs/components/theme.md +5 -0
- package/docs/events.md +49 -0
- package/docs/plans/2026-03-06-date-range-picker-plan.md +4 -4
- package/docs/plans/2026-03-07-table-mui-parity-design.md +1 -1
- package/docs/plans/2026-03-09-action-bar-plan.md +11 -11
- package/docs/superpowers/specs/2026-07-09-table-incredible-design.md +1 -1
- package/docs/superpowers/specs/2026-07-09-table-phase0-implementation.md +2 -2
- package/package.json +3 -1
- package/vscode.html-custom-data.json +13 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* snice v7.
|
|
2
|
+
* snice v7.8.0
|
|
3
3
|
* A decorator-driven web component library with routing, controllers, daemons, and 130+ UI components. For better coding-agent results, run npx snice init-ai.
|
|
4
4
|
* (c) 2024
|
|
5
5
|
* Released under the MIT License.
|
|
@@ -677,7 +677,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
677
677
|
}
|
|
678
678
|
}
|
|
679
679
|
|
|
680
|
-
var cssContent$
|
|
680
|
+
var cssContent$l = ":host([hidden]){display:none}:host{display:inline-block;width:100%;max-width:100%;box-sizing:border-box;font-family:var(--snice-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif);color-scheme:light dark}.input-wrapper{position:relative;width:100%;max-width:100%;height:100%;box-sizing:border-box}:host([align]) .input-wrapper{display:flex;flex-direction:column}:host([align=top]) .input-wrapper{justify-content:flex-start}:host([align=center]) .input-wrapper{justify-content:center}:host([align=bottom]) .input-wrapper{justify-content:flex-end}.input-container{position:relative;display:flex;align-items:center;width:100%;max-width:100%;box-sizing:border-box;transition:all var(--snice-transition-fast, 150ms) ease}:host([stretch]) .input-wrapper{display:flex;flex-direction:column}:host([stretch]) .input-container{flex:1}:host([stretch]) .input{height:100%}.input{flex:1;min-width:0;box-sizing:border-box;font-family:inherit;font-size:var(--snice-font-size-md, 1rem);background:var(--snice-color-surface-container-lowest,rgb(248 247 245));color:var(--snice-color-text,rgb(23 23 23));border:1px solid var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-md,.25rem);transition:border-color var(--snice-transition-fast, 150ms) var(--snice-ease-out, ease),box-shadow var(--snice-transition-fast, 150ms) var(--snice-ease-out, ease),background-color var(--snice-transition-fast, 150ms) var(--snice-ease-out, ease);outline:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}.input:focus-visible{border-color:var(--snice-color-primary,rgb(37 99 235));box-shadow:0 0 0 var(--snice-focus-ring-width,2px) var(--snice-focus-ring-color,rgb(59 130 246 / .5))}.input:hover:not(:focus):not(:disabled){border-color:var(--snice-color-border-hover,rgb(204 204 204))}.input:disabled{background:var(--snice-color-surface-container-low,rgb(250 250 250));color:var(--snice-color-text-secondary,rgb(82 82 82));cursor:not-allowed;opacity:.6}.input::placeholder{color:var(--snice-color-text-secondary,rgb(82 82 82));opacity:.7}.input--small{padding:.375rem .625rem;font-size:var(--snice-font-size-sm, .875rem);min-height:2rem}.input--medium{padding:.5rem .75rem;font-size:var(--snice-font-size-md, 1rem);min-height:2.5rem}.input--large{padding:.625rem .875rem;font-size:var(--snice-font-size-lg, 1.125rem);min-height:3rem}.input--outlined{background:var(--snice-color-surface-container-lowest,rgb(248 247 245));border:1px solid var(--snice-color-border,rgb(226 226 226))}.input--filled{background:var(--snice-color-surface-container-low,rgb(250 250 250));border:1px solid transparent;border-bottom-color:var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-md,.25rem) var(--snice-border-radius-md,.25rem) 0 0}.input--filled:focus-visible{background:var(--snice-color-surface-hover,rgb(245 245 245));border-bottom-color:var(--snice-color-primary,rgb(37 99 235))}.input--underlined{background:0 0;border:none;border-bottom:1px solid var(--snice-color-border,rgb(226 226 226));border-radius:0;padding-left:0;padding-right:0;box-shadow:none}.input-container:has(.input--underlined){position:relative}.input-container:has(.input--underlined)::after{content:'';position:absolute;left:50%;right:50%;bottom:-1px;height:2px;background:var(--snice-color-primary,rgb(37 99 235));transition:left .2s var(--snice-ease-out, ease),right .2s var(--snice-ease-out, ease);pointer-events:none}.input-container:has(.input--underlined:focus-visible)::after{left:0;right:0}.input--underlined:focus-visible{border-bottom-color:var(--snice-color-border,rgb(226 226 226));box-shadow:none}.input--invalid{border-color:var(--snice-color-danger,rgb(220 38 38))}.input--invalid:focus-visible{border-color:var(--snice-color-danger,rgb(220 38 38));box-shadow:0 0 0 var(--snice-focus-ring-width,2px) var(--snice-color-danger-alpha,rgb(220 38 38 / .4)),var(--snice-shadow-sm,0 1px 2px 0 rgb(0 0 0 / .05))}.input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:.7}.input::-webkit-calendar-picker-indicator:hover{opacity:1}.input--with-prefix-icon{padding-left:2.5rem}.input--clearable,.input--with-suffix-icon{padding-right:2.5rem}.input--with-suffix-icon.input--clearable{padding-right:4rem}.label{display:block;margin-bottom:.25rem;font-size:var(--snice-font-size-sm, .875rem);font-weight:var(--snice-font-weight-medium,500);line-height:var(--snice-line-height-normal, 1.5);color:var(--snice-color-text,rgb(23 23 23));text-align:left}:host([label-align=center]) .label{text-align:center}:host([label-align=right]) .label{text-align:right}.label--required::after{content:' *';color:var(--snice-color-danger,rgb(220 38 38))}.error-text,.helper-text{display:block;margin-top:.25rem;font-size:var(--snice-font-size-xs, .75rem)}.helper-text{color:var(--snice-color-text-secondary,rgb(82 82 82))}.error-text{color:var(--snice-color-danger,rgb(220 38 38))}.icon-slot{position:absolute;top:50%;transform:translateY(-50%);display:flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;color:var(--snice-color-text-secondary,rgb(82 82 82));pointer-events:none;opacity:.7}.icon-slot:empty{display:none}.icon-slot--prefix{left:.625rem}.icon-slot--suffix{right:.625rem}.icon{display:flex;align-items:center;justify-content:center;overflow:hidden;width:1.5rem;height:1.5rem;font-size:1.25rem;line-height:1;color:var(--snice-color-text-secondary,rgb(82 82 82));pointer-events:none}.icon-slot ::slotted(*){display:flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;font-size:1.25rem;line-height:1}.icon-slot ::slotted(svg){width:1.5rem;height:1.5rem}.icon-slot ::slotted(img){width:1.5rem;height:1.5rem;object-fit:contain}.clear-button{position:absolute;right:.5rem;display:flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;padding:.25rem;background:0 0;border:none;border-radius:var(--snice-border-radius-sm,.125rem);color:var(--snice-color-text-secondary,rgb(82 82 82));cursor:pointer;transition:all var(--snice-transition-fast, 150ms) ease;opacity:0;visibility:hidden}.clear-button:hover{background:var(--snice-color-surface-hover,rgb(245 245 245));color:var(--snice-color-text,rgb(23 23 23))}.input-container:hover .clear-button--visible,.input:focus-visible~.clear-button--visible{opacity:1;visibility:visible}.clear-button--with-suffix{right:2rem}.password-toggle{position:absolute;right:.5rem;display:flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;padding:.25rem;background:0 0;border:none;border-radius:var(--snice-border-radius-sm,.125rem);color:var(--snice-color-text-secondary,rgb(82 82 82));cursor:pointer;transition:all var(--snice-transition-fast, 150ms) ease}.password-toggle:hover{background:var(--snice-color-surface-hover,rgb(245 245 245));color:var(--snice-color-text,rgb(23 23 23))}.input[type=number]::-webkit-inner-spin-button,.input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.input[type=number]{-moz-appearance:textfield}.input[type=search]::-webkit-search-cancel-button,.input[type=search]::-webkit-search-decoration,.input[type=search]::-webkit-search-results-button,.input[type=search]::-webkit-search-results-decoration{-webkit-appearance:none}.input-container:focus-within{z-index:1}.input--loading{cursor:wait}.spinner{position:absolute;right:.75rem;width:1em;height:1em;pointer-events:none}.input--with-suffix-icon .spinner{right:2.5rem}.spinner::after{content:'';display:block;width:100%;height:100%;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:.6s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){*{transition-duration:0s!important;animation-duration:0s!important}}";
|
|
681
681
|
|
|
682
682
|
(() => {
|
|
683
683
|
let _classDecorators = [snice.element('snice-input', { formAssociated: true, delegatesFocus: true })];
|
|
@@ -1143,7 +1143,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
1143
1143
|
`;
|
|
1144
1144
|
}
|
|
1145
1145
|
styles() {
|
|
1146
|
-
return snice.css /*css*/ `${cssContent$
|
|
1146
|
+
return snice.css /*css*/ `${cssContent$l}`;
|
|
1147
1147
|
}
|
|
1148
1148
|
init() {
|
|
1149
1149
|
// Preserve a value assigned before custom-element upgrade. The own data
|
|
@@ -1612,7 +1612,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
1612
1612
|
return _classThis;
|
|
1613
1613
|
})();
|
|
1614
1614
|
|
|
1615
|
-
var cssContent$j = ":host([hidden]){display:none}:host{display:inline-block;font-family:var(--snice-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif);position:relative;width:100%;max-width:18.75rem}[hidden]{display:none!important}img[src=\"\"]{display:none}.select-wrapper{position:relative;width:100%}.select-label{display:block;margin-bottom:.25rem;font-size:var(--snice-font-size-sm, .875rem);font-weight:var(--snice-font-weight-medium,500);line-height:var(--snice-line-height-normal, 1.5);color:var(--snice-color-text,rgb(23 23 23));text-align:left}.select-label--required::after{content:' *';color:var(--snice-color-danger,rgb(220 38 38))}.select-error-text,.select-helper-text{display:block;margin-top:var(--snice-spacing-2xs,.25rem);font-size:var(--snice-font-size-xs, .75rem);font-weight:var(--snice-font-weight-normal,400);line-height:var(--snice-line-height-normal, 1.5)}.select-helper-text{color:var(--snice-color-text-secondary,rgb(82 82 82))}.select-error-text{color:var(--snice-color-danger,rgb(220 38 38))}.select-trigger{box-sizing:border-box;min-width:0;width:100%;display:flex;align-items:center;justify-content:space-between;background:var(--snice-color-surface-container-lowest,rgb(248 247 245));border:1px solid var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-md,.25rem);cursor:pointer;transition:all var(--snice-transition-fast, 150ms) ease;position:relative;text-align:left;font-family:inherit;color:var(--snice-color-text,rgb(23 23 23))}.select-trigger--small{padding:.375rem 2rem .375rem .625rem;font-size:var(--snice-font-size-sm, .875rem);min-height:2rem}.select-trigger--medium{padding:.5rem 2.25rem .5rem .75rem;font-size:var(--snice-font-size-md, 1rem);min-height:var(--snice-select-min-height,2.5rem)}.select-trigger--large{padding:.625rem 2.5rem .625rem .875rem;font-size:var(--snice-font-size-lg, 1.125rem);min-height:3rem}.select-trigger:hover:not(.select-trigger--disabled){border-color:var(--snice-color-border-hover,rgb(180 180 180))}.select-trigger:focus-visible{outline:2px solid var(--snice-color-primary,rgb(37 99 235));outline-offset:2px;border-color:var(--snice-color-primary,rgb(37 99 235))}.select-trigger--open{border-color:var(--snice-color-primary,rgb(37 99 235))}.select-trigger--disabled{background:var(--snice-color-surface-container-low,rgb(245 245 245));cursor:not-allowed;opacity:.6}.select-trigger--readonly{background:var(--snice-color-surface-container-low,rgb(245 245 245));cursor:default}.select-trigger--invalid{border-color:var(--snice-color-danger,rgb(220 38 38))}.select-placeholder{color:var(--snice-color-text-secondary,rgb(82 82 82))}.select-value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select-value--single{display:flex;align-items:center;gap:.5rem}.select-value-icon{width:1rem;height:1rem;object-fit:contain;flex-shrink:0}.select-value--multiple{display:flex;gap:.25rem;flex-wrap:wrap}.select-tag{display:inline-flex;align-items:center;gap:.25rem;padding:.125rem .375rem;background:var(--snice-color-primary-subtle,rgb(219 234 254));color:var(--snice-color-primary,rgb(37 99 235));border-radius:var(--snice-border-radius-sm,.125rem);font-size:var(--snice-font-size-xs, .75rem)}.select-tag-icon{width:.75rem;height:.75rem;object-fit:contain;flex-shrink:0}.select-tag-remove{cursor:pointer;padding:0;background:0 0;border:none;color:inherit;font-size:.75rem;line-height:1}.select-tag-remove:hover{opacity:.7}.select-icons{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);display:flex;align-items:center;gap:.25rem;pointer-events:none;z-index:1}.select-clear{padding:.125rem;background:0 0;border:none;cursor:pointer;color:var(--snice-color-text-secondary,rgb(82 82 82));display:flex;align-items:center;justify-content:center;border-radius:var(--snice-border-radius-sm,.125rem);pointer-events:auto}.select-clear:hover{background:var(--snice-color-surface-container-low,rgb(245 245 245));color:var(--snice-color-text,rgb(23 23 23))}.select-arrow{transition:transform var(--snice-transition-fast, 150ms) ease;color:var(--snice-color-text-secondary,rgb(82 82 82));pointer-events:none}.select-arrow--open{transform:rotate(180deg)}.select-dropdown{position:absolute;top:100%;margin-top:.25rem;left:0;right:0;background:var(--snice-color-surface-container-high,rgb(252 251 249));border:1px solid var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-md,.25rem);box-shadow:var(--snice-shadow-lg,0 10px 15px -3px rgb(0 0 0 / .1),0 4px 6px -4px rgb(0 0 0 / .1));z-index:1000;opacity:0;transform:translateY(-8px) scaleY(.95);transform-origin:top;pointer-events:none;transition:opacity var(--snice-transition-fast, 150ms) ease,transform var(--snice-transition-fast, 150ms) ease;display:flex;flex-direction:column;overflow:hidden}.select-dropdown--open{opacity:1;transform:translateY(0) scaleY(1);pointer-events:auto}.select-search{padding:.5rem;border-bottom:1px solid var(--snice-color-border,rgb(226 226 226));flex-shrink:0}.select-search-input{width:100%;padding:.375rem .625rem;background:var(--snice-color-surface-container-lowest,rgb(248 247 245));border:1px solid var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-sm,.125rem);font-size:var(--snice-font-size-sm, .875rem);font-family:inherit;color:var(--snice-color-text,rgb(23 23 23));box-sizing:border-box}.select-search-input:focus-visible{outline:0;border-color:var(--snice-color-primary,rgb(37 99 235))}.select-options{max-height:12.5rem;overflow-y:auto;overflow-x:hidden;padding:.25rem;scrollbar-gutter:stable;scrollbar-width:thin;scrollbar-color:var(--snice-color-border,rgb(226 226 226)) var(--snice-color-surface-container-low,rgb(245 245 245))}.select-options::-webkit-scrollbar-track{background:var(--snice-color-surface-container-low,rgb(245 245 245));border-radius:var(--snice-border-radius-md,.25rem);margin:.25rem 0}.select-options::-webkit-scrollbar-thumb{background:var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-md,.25rem);border:2px solid var(--snice-color-surface,rgb(255 255 255))}.select-option{padding:.5rem .75rem;cursor:pointer;border-radius:var(--snice-border-radius-sm,.125rem);transition:background var(--snice-transition-fast, 150ms) ease;display:flex;align-items:center;gap:.5rem}.select-option:hover:not(.select-option--disabled){background:var(--snice-color-surface-container-low,rgb(245 245 245))}.select-option--selected{background:var(--snice-color-primary-subtle,rgb(219 234 254));color:var(--snice-color-primary,rgb(37 99 235));font-weight:var(--snice-font-weight-medium,500)}.select-option--focused{background:var(--snice-color-surface-container-low,rgb(245 245 245));outline:2px solid var(--snice-color-primary,rgb(37 99 235));outline-offset:-2px}.select-option--disabled{opacity:.5;cursor:not-allowed}.select-option-icon{width:1rem;height:1rem;object-fit:contain;flex-shrink:0}.select-option-check{width:1rem;height:1rem;display:flex;align-items:center;justify-content:center;flex-shrink:0}.select-option-label{flex:1}.select-no-options{padding:1rem;text-align:center;color:var(--snice-color-text-secondary,rgb(82 82 82));font-size:var(--snice-font-size-sm, .875rem)}.select-native{position:absolute;opacity:0;pointer-events:none;width:0;height:0}.select-options::-webkit-scrollbar{width:6px}.select-options::-webkit-scrollbar-track{background:var(--snice-color-surface-container-low,rgb(245 245 245));border-radius:3px}.select-options::-webkit-scrollbar-thumb{background:var(--snice-color-border,rgb(226 226 226));border-radius:3px}.select-options::-webkit-scrollbar-thumb:hover{background:var(--snice-color-border-hover,rgb(180 180 180))}.select-trigger--loading{cursor:wait;opacity:.7}.select-spinner{display:inline-block;width:.875rem;height:.875rem;margin-right:.25rem}.select-spinner::after{content:'';display:block;width:100%;height:100%;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:.6s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}.select-freetext-input{flex:1;border:none;outline:0;background:0 0;font-family:inherit;font-size:inherit;color:var(--snice-color-text,rgb(23 23 23));padding:0;min-width:0;width:100%}.select-freetext-input::placeholder{color:var(--snice-color-text-secondary,rgb(82 82 82))}.select-editable-container{position:relative;display:flex;align-items:center;width:100%}.select-editable-input{width:100%;box-sizing:border-box;font-family:inherit;background:var(--snice-color-surface-container-lowest,rgb(248 247 245));color:var(--snice-color-text,rgb(23 23 23));border:1px solid var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-md,.25rem);transition:all var(--snice-transition-fast, 150ms) ease;outline:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}.select-editable-input--small{padding:.375rem 2rem .375rem .625rem;font-size:var(--snice-font-size-sm, .875rem);min-height:2rem}.select-editable-input--medium{padding:.5rem 2.25rem .5rem .75rem;font-size:var(--snice-font-size-md, 1rem);min-height:2.5rem}.select-editable-input--large{padding:.625rem 2.5rem .625rem .875rem;font-size:var(--snice-font-size-lg, 1.125rem);min-height:3rem}.select-editable-input:hover:not(:disabled){border-color:var(--snice-color-border-hover,rgb(180 180 180))}.select-editable-input:focus-visible{border-color:var(--snice-color-primary,rgb(37 99 235));box-shadow:0 0 0 var(--snice-focus-ring-width,2px) var(--snice-focus-ring-color,rgb(59 130 246 / .5))}.select-editable-input--open{border-color:var(--snice-color-primary,rgb(37 99 235))}.select-editable-input--invalid{border-color:var(--snice-color-danger,rgb(220 38 38))}.select-editable-input:disabled{background:var(--snice-color-surface-container-low,rgb(245 245 245));color:var(--snice-color-text-secondary,rgb(82 82 82));cursor:not-allowed;opacity:.6}.select-editable-input:read-only{background:var(--snice-color-surface-container-low,rgb(245 245 245));cursor:default}.select-editable-input::placeholder{color:var(--snice-color-text-secondary,rgb(82 82 82));opacity:.7}.select-editable-arrow{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);display:flex;align-items:center;justify-content:center;color:var(--snice-color-text-secondary,rgb(82 82 82));pointer-events:none;transition:transform var(--snice-transition-fast, 150ms) ease}.select-editable-arrow--open{transform:translateY(-50%) rotate(180deg)}.select-loading-indicator{display:flex;align-items:center;justify-content:center;gap:.5rem;padding:.75rem 1rem;color:var(--snice-color-text-secondary,rgb(82 82 82));font-size:var(--snice-font-size-sm, .875rem)}.select-loading-spinner{display:inline-block;width:1rem;height:1rem;flex-shrink:0}.select-loading-spinner::after{content:'';display:block;width:100%;height:100%;border:2px solid var(--snice-color-border,rgb(226 226 226));border-top-color:var(--snice-color-primary,rgb(37 99 235));border-radius:50%;animation:.6s linear infinite spin}@media (prefers-reduced-motion:reduce){*{transition-duration:0s!important;animation-duration:0s!important}}";
|
|
1615
|
+
var cssContent$k = ":host([hidden]){display:none}:host{display:inline-block;font-family:var(--snice-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif);position:relative;width:100%;max-width:18.75rem}[hidden]{display:none!important}img[src=\"\"]{display:none}.select-wrapper{position:relative;width:100%}.select-label{display:block;margin-bottom:.25rem;font-size:var(--snice-font-size-sm, .875rem);font-weight:var(--snice-font-weight-medium,500);line-height:var(--snice-line-height-normal, 1.5);color:var(--snice-color-text,rgb(23 23 23));text-align:left}.select-label--required::after{content:' *';color:var(--snice-color-danger,rgb(220 38 38))}.select-error-text,.select-helper-text{display:block;margin-top:var(--snice-spacing-2xs,.25rem);font-size:var(--snice-font-size-xs, .75rem);font-weight:var(--snice-font-weight-normal,400);line-height:var(--snice-line-height-normal, 1.5)}.select-helper-text{color:var(--snice-color-text-secondary,rgb(82 82 82))}.select-error-text{color:var(--snice-color-danger,rgb(220 38 38))}.select-trigger{box-sizing:border-box;min-width:0;width:100%;display:flex;align-items:center;justify-content:space-between;background:var(--snice-color-surface-container-lowest,rgb(248 247 245));border:1px solid var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-md,.25rem);cursor:pointer;transition:all var(--snice-transition-fast, 150ms) ease;position:relative;text-align:left;font-family:inherit;color:var(--snice-color-text,rgb(23 23 23))}.select-trigger--small{padding:.375rem 2rem .375rem .625rem;font-size:var(--snice-font-size-sm, .875rem);min-height:2rem}.select-trigger--medium{padding:.5rem 2.25rem .5rem .75rem;font-size:var(--snice-font-size-md, 1rem);min-height:var(--snice-select-min-height,2.5rem)}.select-trigger--large{padding:.625rem 2.5rem .625rem .875rem;font-size:var(--snice-font-size-lg, 1.125rem);min-height:3rem}.select-trigger:hover:not(.select-trigger--disabled){border-color:var(--snice-color-border-hover,rgb(180 180 180))}.select-trigger:focus-visible{outline:2px solid var(--snice-color-primary,rgb(37 99 235));outline-offset:2px;border-color:var(--snice-color-primary,rgb(37 99 235))}.select-trigger--open{border-color:var(--snice-color-primary,rgb(37 99 235))}.select-trigger--disabled{background:var(--snice-color-surface-container-low,rgb(245 245 245));cursor:not-allowed;opacity:.6}.select-trigger--readonly{background:var(--snice-color-surface-container-low,rgb(245 245 245));cursor:default}.select-trigger--invalid{border-color:var(--snice-color-danger,rgb(220 38 38))}.select-placeholder{color:var(--snice-color-text-secondary,rgb(82 82 82))}.select-value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select-value--single{display:flex;align-items:center;gap:.5rem}.select-value-icon{width:1rem;height:1rem;object-fit:contain;flex-shrink:0}.select-value--multiple{display:flex;gap:.25rem;flex-wrap:wrap}.select-tag{display:inline-flex;align-items:center;gap:.25rem;padding:.125rem .375rem;background:var(--snice-color-primary-subtle,rgb(219 234 254));color:var(--snice-color-primary,rgb(37 99 235));border-radius:var(--snice-border-radius-sm,.125rem);font-size:var(--snice-font-size-xs, .75rem)}.select-tag-icon{width:.75rem;height:.75rem;object-fit:contain;flex-shrink:0}.select-tag-remove{cursor:pointer;padding:0;background:0 0;border:none;color:inherit;font-size:.75rem;line-height:1}.select-tag-remove:hover{opacity:.7}.select-icons{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);display:flex;align-items:center;gap:.25rem;pointer-events:none;z-index:1}.select-clear{padding:.125rem;background:0 0;border:none;cursor:pointer;color:var(--snice-color-text-secondary,rgb(82 82 82));display:flex;align-items:center;justify-content:center;border-radius:var(--snice-border-radius-sm,.125rem);pointer-events:auto}.select-clear:hover{background:var(--snice-color-surface-container-low,rgb(245 245 245));color:var(--snice-color-text,rgb(23 23 23))}.select-arrow{transition:transform var(--snice-transition-fast, 150ms) ease;color:var(--snice-color-text-secondary,rgb(82 82 82));pointer-events:none}.select-arrow--open{transform:rotate(180deg)}.select-dropdown{position:absolute;top:100%;margin-top:.25rem;left:0;right:0;background:var(--snice-color-surface-container-high,rgb(252 251 249));border:1px solid var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-md,.25rem);box-shadow:var(--snice-shadow-lg,0 10px 15px -3px rgb(0 0 0 / .1),0 4px 6px -4px rgb(0 0 0 / .1));z-index:1000;opacity:0;transform:translateY(-8px) scaleY(.95);transform-origin:top;pointer-events:none;transition:opacity var(--snice-transition-fast, 150ms) ease,transform var(--snice-transition-fast, 150ms) ease;display:flex;flex-direction:column;overflow:hidden}.select-dropdown--open{opacity:1;transform:translateY(0) scaleY(1);pointer-events:auto}.select-search{padding:.5rem;border-bottom:1px solid var(--snice-color-border,rgb(226 226 226));flex-shrink:0}.select-search-input{width:100%;padding:.375rem .625rem;background:var(--snice-color-surface-container-lowest,rgb(248 247 245));border:1px solid var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-sm,.125rem);font-size:var(--snice-font-size-sm, .875rem);font-family:inherit;color:var(--snice-color-text,rgb(23 23 23));box-sizing:border-box}.select-search-input:focus-visible{outline:0;border-color:var(--snice-color-primary,rgb(37 99 235))}.select-options{max-height:12.5rem;overflow-y:auto;overflow-x:hidden;padding:.25rem;scrollbar-gutter:stable;scrollbar-width:thin;scrollbar-color:var(--snice-color-border,rgb(226 226 226)) var(--snice-color-surface-container-low,rgb(245 245 245))}.select-options::-webkit-scrollbar-track{background:var(--snice-color-surface-container-low,rgb(245 245 245));border-radius:var(--snice-border-radius-md,.25rem);margin:.25rem 0}.select-options::-webkit-scrollbar-thumb{background:var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-md,.25rem);border:2px solid var(--snice-color-surface,rgb(255 255 255))}.select-option{padding:.5rem .75rem;cursor:pointer;border-radius:var(--snice-border-radius-sm,.125rem);transition:background var(--snice-transition-fast, 150ms) ease;display:flex;align-items:center;gap:.5rem}.select-option:hover:not(.select-option--disabled){background:var(--snice-color-surface-container-low,rgb(245 245 245))}.select-option--selected{background:var(--snice-color-primary-subtle,rgb(219 234 254));color:var(--snice-color-primary,rgb(37 99 235));font-weight:var(--snice-font-weight-medium,500)}.select-option--focused{background:var(--snice-color-surface-container-low,rgb(245 245 245));outline:2px solid var(--snice-color-primary,rgb(37 99 235));outline-offset:-2px}.select-option--disabled{opacity:.5;cursor:not-allowed}.select-option-icon{width:1rem;height:1rem;object-fit:contain;flex-shrink:0}.select-option-check{width:1rem;height:1rem;display:flex;align-items:center;justify-content:center;flex-shrink:0}.select-option-label{flex:1}.select-no-options{padding:1rem;text-align:center;color:var(--snice-color-text-secondary,rgb(82 82 82));font-size:var(--snice-font-size-sm, .875rem)}.select-native{position:absolute;opacity:0;pointer-events:none;width:0;height:0}.select-options::-webkit-scrollbar{width:6px}.select-options::-webkit-scrollbar-track{background:var(--snice-color-surface-container-low,rgb(245 245 245));border-radius:3px}.select-options::-webkit-scrollbar-thumb{background:var(--snice-color-border,rgb(226 226 226));border-radius:3px}.select-options::-webkit-scrollbar-thumb:hover{background:var(--snice-color-border-hover,rgb(180 180 180))}.select-trigger--loading{cursor:wait;opacity:.7}.select-spinner{display:inline-block;width:.875rem;height:.875rem;margin-right:.25rem}.select-spinner::after{content:'';display:block;width:100%;height:100%;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:.6s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}.select-freetext-input{flex:1;border:none;outline:0;background:0 0;font-family:inherit;font-size:inherit;color:var(--snice-color-text,rgb(23 23 23));padding:0;min-width:0;width:100%}.select-freetext-input::placeholder{color:var(--snice-color-text-secondary,rgb(82 82 82))}.select-editable-container{position:relative;display:flex;align-items:center;width:100%}.select-editable-input{width:100%;box-sizing:border-box;font-family:inherit;background:var(--snice-color-surface-container-lowest,rgb(248 247 245));color:var(--snice-color-text,rgb(23 23 23));border:1px solid var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-md,.25rem);transition:all var(--snice-transition-fast, 150ms) ease;outline:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}.select-editable-input--small{padding:.375rem 2rem .375rem .625rem;font-size:var(--snice-font-size-sm, .875rem);min-height:2rem}.select-editable-input--medium{padding:.5rem 2.25rem .5rem .75rem;font-size:var(--snice-font-size-md, 1rem);min-height:2.5rem}.select-editable-input--large{padding:.625rem 2.5rem .625rem .875rem;font-size:var(--snice-font-size-lg, 1.125rem);min-height:3rem}.select-editable-input:hover:not(:disabled){border-color:var(--snice-color-border-hover,rgb(180 180 180))}.select-editable-input:focus-visible{border-color:var(--snice-color-primary,rgb(37 99 235));box-shadow:0 0 0 var(--snice-focus-ring-width,2px) var(--snice-focus-ring-color,rgb(59 130 246 / .5))}.select-editable-input--open{border-color:var(--snice-color-primary,rgb(37 99 235))}.select-editable-input--invalid{border-color:var(--snice-color-danger,rgb(220 38 38))}.select-editable-input:disabled{background:var(--snice-color-surface-container-low,rgb(245 245 245));color:var(--snice-color-text-secondary,rgb(82 82 82));cursor:not-allowed;opacity:.6}.select-editable-input:read-only{background:var(--snice-color-surface-container-low,rgb(245 245 245));cursor:default}.select-editable-input::placeholder{color:var(--snice-color-text-secondary,rgb(82 82 82));opacity:.7}.select-editable-arrow{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);display:flex;align-items:center;justify-content:center;color:var(--snice-color-text-secondary,rgb(82 82 82));pointer-events:none;transition:transform var(--snice-transition-fast, 150ms) ease}.select-editable-arrow--open{transform:translateY(-50%) rotate(180deg)}.select-loading-indicator{display:flex;align-items:center;justify-content:center;gap:.5rem;padding:.75rem 1rem;color:var(--snice-color-text-secondary,rgb(82 82 82));font-size:var(--snice-font-size-sm, .875rem)}.select-loading-spinner{display:inline-block;width:1rem;height:1rem;flex-shrink:0}.select-loading-spinner::after{content:'';display:block;width:100%;height:100%;border:2px solid var(--snice-color-border,rgb(226 226 226));border-top-color:var(--snice-color-primary,rgb(37 99 235));border-radius:50%;animation:.6s linear infinite spin}@media (prefers-reduced-motion:reduce){*{transition-duration:0s!important;animation-duration:0s!important}}";
|
|
1616
1616
|
|
|
1617
1617
|
(() => {
|
|
1618
1618
|
let _classDecorators = [snice.element('snice-select', { formAssociated: true, delegatesFocus: true })];
|
|
@@ -1955,14 +1955,11 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
1955
1955
|
get interactionDisabled() {
|
|
1956
1956
|
return this.disabled || this.formDisabled || this.loading;
|
|
1957
1957
|
}
|
|
1958
|
-
/**
|
|
1958
|
+
/** The documented option list: child `<snice-option>` elements win over the `options` array */
|
|
1959
1959
|
get mergedOptions() {
|
|
1960
|
-
//
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
const childValues = new Set(this.childOptions.map(o => o.value));
|
|
1964
|
-
return [...this.childOptions, ...this.options.filter(o => !childValues.has(o.value))];
|
|
1965
|
-
}
|
|
1960
|
+
// "Children take precedence over `options` array" — when children are
|
|
1961
|
+
// present they are the list, so a later `options` assignment cannot
|
|
1962
|
+
// append to or reorder what the author declared in markup.
|
|
1966
1963
|
return this.childOptions.length > 0 ? this.childOptions : this.options;
|
|
1967
1964
|
}
|
|
1968
1965
|
/**
|
|
@@ -2173,7 +2170,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
2173
2170
|
option.setAttribute('data-value', opt.value);
|
|
2174
2171
|
option.setAttribute('role', 'option');
|
|
2175
2172
|
option.setAttribute('aria-selected', String(isSelected));
|
|
2176
|
-
option.setAttribute('aria-disabled', String(opt.disabled));
|
|
2173
|
+
option.setAttribute('aria-disabled', String(Boolean(opt.disabled)));
|
|
2177
2174
|
option.setAttribute('part', 'option');
|
|
2178
2175
|
const check = document.createElement('span');
|
|
2179
2176
|
check.className = 'select-option-check';
|
|
@@ -2193,7 +2190,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
2193
2190
|
return fragment;
|
|
2194
2191
|
}
|
|
2195
2192
|
styles() {
|
|
2196
|
-
return snice.css `${cssContent$
|
|
2193
|
+
return snice.css `${cssContent$k}`;
|
|
2197
2194
|
}
|
|
2198
2195
|
init() {
|
|
2199
2196
|
// Read options from child snice-option elements
|
|
@@ -3053,7 +3050,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
3053
3050
|
return _classThis;
|
|
3054
3051
|
})();
|
|
3055
3052
|
|
|
3056
|
-
var cssContent$i = ":host([hidden]){display:none}:host{display:inline-block;position:relative;width:auto;cursor:pointer}:host([disabled]){cursor:not-allowed}:host([justify-text=start]) .button{justify-content:flex-start}:host([justify-text=end]) .button{justify-content:flex-end}:host([justify-text=center]) .button{justify-content:center}.button{display:inline-flex;align-items:center;justify-content:center;width:100%;border:1px solid;font-family:var(--snice-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif);font-weight:var(--snice-font-weight-medium,500);text-decoration:none;user-select:none;white-space:nowrap;vertical-align:middle;padding:0;box-shadow:var(--snice-shadow-xs,0 1px 2px 0 rgb(0 0 0 / .03));transition:color var(--snice-transition-fast, 150ms) var(--snice-ease-out, cubic-bezier(.22,1,.36,1)),background-color var(--snice-transition-fast, 150ms) var(--snice-ease-out, cubic-bezier(.22,1,.36,1)),border-color var(--snice-transition-fast, 150ms) var(--snice-ease-out, cubic-bezier(.22,1,.36,1)),box-shadow var(--snice-transition-fast, 150ms) var(--snice-ease-out, cubic-bezier(.22,1,.36,1)),transform 180ms var(--snice-ease-spring, cubic-bezier(.34,1.56,.64,1));cursor:inherit;position:relative}.button::before{content:'';position:absolute;inset:0;border-radius:inherit;background:var(--snice-gradient-convex,none);pointer-events:none}.button:active:not(.button--disabled){transition:color 50ms var(--snice-ease-out, ease),background-color 50ms var(--snice-ease-out, ease),border-color 50ms var(--snice-ease-out, ease),box-shadow 50ms var(--snice-ease-out, ease),transform 80ms var(--snice-ease-out, ease);transform:translateY(0) scale(.97)}.button:focus-visible{outline:0;box-shadow:0 0 0 var(--snice-focus-ring-width,2px) var(--snice-focus-ring-color,rgb(59 130 246 / .5))}.button--disabled{opacity:.5;cursor:not-allowed;box-shadow:none}.button--small{font-size:var(--snice-font-size-sm, .875rem);height:2rem;padding:0 var(--snice-spacing-sm,.75rem);border-radius:var(--snice-border-radius-md,.25rem)}.button--medium{font-size:var(--snice-font-size-md, 1rem);height:2.5rem;padding:0 var(--snice-spacing-md,1rem);border-radius:var(--snice-border-radius-md,.25rem)}.button--large{font-size:var(--snice-font-size-lg, 1.125rem);height:3rem;padding:0 var(--snice-spacing-lg,1.5rem);border-radius:var(--snice-border-radius-md,.25rem)}.button--default{background-color:var(--snice-color-surface,rgb(255 255 255));color:var(--snice-color-text,rgb(23 23 23));border-color:var(--snice-color-border,rgb(226 226 226))}.button--default:hover:not(.button--disabled){background-color:var(--snice-color-surface-container-low,rgb(250 250 250));border-color:var(--snice-color-border-hover,rgb(204 204 204));box-shadow:var(--snice-shadow-sm,0 1px 2px 0 rgb(0 0 0 / .05));transform:translateY(-1px)}.button--default:active:not(.button--disabled){background-color:var(--snice-color-surface-container,rgb(248 248 248));border-color:var(--snice-color-border-hover,rgb(204 204 204));box-shadow:var(--snice-shadow-inset-sm,inset 0 1px 2px rgb(0 0 0 / .05));transform:translateY(0) scale(.97)}.button--primary{background-color:var(--snice-color-primary,rgb(37 99 235));color:var(--snice-color-text-inverse,rgb(250 250 250));border-color:var(--snice-color-primary,rgb(37 99 235))}.button--primary:hover:not(.button--disabled){background-color:var(--snice-color-primary-hover,rgb(29 78 216));border-color:var(--snice-color-primary-hover,rgb(29 78 216));box-shadow:var(--snice-shadow-sm,0 1px 2px 0 rgb(0 0 0 / .05));transform:translateY(-1px)}.button--primary:active:not(.button--disabled){background-color:color-mix(in srgb,var(--snice-color-primary,rgb(37 99 235)) 85%,#fff);border-color:color-mix(in srgb,var(--snice-color-primary,rgb(37 99 235)) 85%,#fff);box-shadow:var(--snice-shadow-inset-sm,inset 0 1px 2px rgb(0 0 0 / .05));transform:translateY(0) scale(.97)}.button--success{background-color:var(--snice-color-success,rgb(22 163 74));color:var(--snice-color-text-inverse,rgb(250 250 250));border-color:var(--snice-color-success,rgb(22 163 74))}.button--success:hover:not(.button--disabled){background-color:var(--snice-color-success-hover,rgb(21 128 61));border-color:var(--snice-color-success-hover,rgb(21 128 61));box-shadow:var(--snice-shadow-sm,0 1px 2px 0 rgb(0 0 0 / .05));transform:translateY(-1px)}.button--success:active:not(.button--disabled){background-color:color-mix(in srgb,var(--snice-color-success,rgb(22 163 74)) 85%,#fff);border-color:color-mix(in srgb,var(--snice-color-success,rgb(22 163 74)) 85%,#fff);box-shadow:var(--snice-shadow-inset-sm,inset 0 1px 2px rgb(0 0 0 / .05));transform:translateY(0) scale(.97)}.button--warning{background-color:var(--snice-color-warning,rgb(202 138 4));color:var(--button-warning-text,rgb(69 26 3));border-color:var(--snice-color-warning,rgb(202 138 4))}.button--warning:hover:not(.button--disabled){background-color:var(--snice-color-warning-hover,rgb(161 98 7));border-color:var(--snice-color-warning-hover,rgb(161 98 7));box-shadow:var(--snice-shadow-sm,0 1px 2px 0 rgb(0 0 0 / .05));transform:translateY(-1px)}.button--warning:active:not(.button--disabled){background-color:color-mix(in srgb,var(--snice-color-warning,rgb(202 138 4)) 85%,#fff);border-color:color-mix(in srgb,var(--snice-color-warning,rgb(202 138 4)) 85%,#fff);box-shadow:var(--snice-shadow-inset-sm,inset 0 1px 2px rgb(0 0 0 / .05));transform:translateY(0) scale(.97)}.button--danger{background-color:var(--snice-color-danger,rgb(220 38 38));color:var(--snice-color-text-inverse,rgb(250 250 250));border-color:var(--snice-color-danger,rgb(220 38 38))}.button--danger:hover:not(.button--disabled){background-color:var(--snice-color-danger-hover,rgb(185 28 28));border-color:var(--snice-color-danger-hover,rgb(185 28 28));box-shadow:var(--snice-shadow-sm,0 1px 2px 0 rgb(0 0 0 / .05));transform:translateY(-1px)}.button--danger:active:not(.button--disabled){background-color:color-mix(in srgb,var(--snice-color-danger,rgb(220 38 38)) 85%,#fff);border-color:color-mix(in srgb,var(--snice-color-danger,rgb(220 38 38)) 85%,#fff);box-shadow:var(--snice-shadow-inset-sm,inset 0 1px 2px rgb(0 0 0 / .05));transform:translateY(0) scale(.97)}.button--text{background-color:transparent;color:var(--snice-color-primary,rgb(37 99 235));border-color:transparent;box-shadow:none}.button--text::before{display:none}.button--text:hover:not(.button--disabled){background-color:var(--snice-color-primary-alpha,rgb(37 99 235 / .1));color:var(--snice-color-primary-hover,rgb(29 78 216));box-shadow:none;transform:none}.button--text:active:not(.button--disabled){background-color:var(--snice-color-primary-alpha-strong,rgb(37 99 235 / .15));box-shadow:none;transform:none}.button--outline{box-shadow:none}.button--outline::before{display:none}.button--outline.button--default{background-color:transparent;color:var(--snice-color-text,rgb(23 23 23))}.button--outline.button--primary{background-color:transparent;color:var(--snice-color-primary,rgb(37 99 235))}.button--outline.button--success{background-color:transparent;color:var(--snice-color-success,rgb(22 163 74))}.button--outline.button--warning{background-color:transparent;color:var(--snice-color-warning,rgb(202 138 4))}.button--outline.button--danger{background-color:transparent;color:var(--snice-color-danger,rgb(220 38 38))}.button--outline.button--default:hover:not(.button--disabled){background-color:var(--snice-color-surface-hover,rgb(245 245 245));box-shadow:none;transform:none}.button--outline.button--primary:hover:not(.button--disabled){background-color:var(--snice-color-primary-subtle,rgb(239 246 255));color:var(--snice-color-primary-hover,rgb(29 78 216));box-shadow:none;transform:none}.button--outline.button--success:hover:not(.button--disabled){background-color:var(--snice-color-success-subtle,rgb(220 252 231));color:var(--snice-color-success-hover,rgb(21 128 61));box-shadow:none;transform:none}.button--outline.button--warning:hover:not(.button--disabled){background-color:var(--snice-color-warning-subtle,rgb(254 249 195));color:var(--snice-color-warning-hover,rgb(161 98 7));box-shadow:none;transform:none}.button--outline.button--danger:hover:not(.button--disabled){background-color:var(--snice-color-danger-subtle,rgb(254 226 226));color:var(--snice-color-danger-hover,rgb(185 28 28));box-shadow:none;transform:none}.button--outline:active:not(.button--disabled){background-color:var(--snice-color-primary-subtle-hover,var(--snice-color-surface-active,rgb(235 235 235)));box-shadow:none;transform:none}.button--pill{border-radius:var(--snice-border-radius-pill,9999px)}.button--circle{border-radius:var(--snice-border-radius-circle,50%);width:var(--size);padding:0}.button--small.button--circle{--size:2rem}.button--medium.button--circle{--size:2.5rem}.button--large.button--circle{--size:3rem}.button--loading{position:relative;cursor:wait}.button--loading .label{visibility:hidden}.spinner{display:none;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:1em;height:1em}.button--loading .spinner{display:block}.spinner::after{content:'';display:block;width:100%;height:100%;box-sizing:border-box;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:.6s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}.icon-slot{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:1.25em;height:1.25em;line-height:1}.icon-slot:empty{display:none}.icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:1.25em;height:1.25em;line-height:1}.icon img{width:100%;height:100%;object-fit:contain}.icon svg{width:100%;height:100%}.icon-slot ::slotted(*){display:flex;align-items:center;justify-content:center;font-size:1.25em;line-height:1}.icon-slot ::slotted(svg){width:1.25em;height:1.25em}.icon-slot ::slotted(img){width:1.25em;height:1.25em;object-fit:contain}.button--icon-start .icon,.button--icon-start .icon-slot{margin-right:.5em}.button--icon-end .icon,.button--icon-end .icon-slot{margin-left:.5em}.button--circle .icon,.button--circle .icon-slot{margin:0;transform:none;width:auto;height:auto}.button--circle .icon svg{width:1.25em;height:1.25em}.button--circle .icon-slot ::slotted(*){font-size:1.25em;line-height:1;display:flex;align-items:center;justify-content:center}.button--circle .icon-slot ::slotted(svg){width:1.25em;height:1.25em}@media (prefers-reduced-motion:reduce){.icon-slot,.spinner,button,button:active{transition-duration:0s!important;animation-duration:0s!important}button:active{transform:none}}";
|
|
3053
|
+
var cssContent$j = ":host([hidden]){display:none}:host{display:inline-block;position:relative;width:auto;cursor:pointer}:host([disabled]){cursor:not-allowed}:host([justify-text=start]) .button{justify-content:flex-start}:host([justify-text=end]) .button{justify-content:flex-end}:host([justify-text=center]) .button{justify-content:center}.button{display:inline-flex;align-items:center;justify-content:center;width:100%;border:1px solid;font-family:var(--snice-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif);font-weight:var(--snice-font-weight-medium,500);text-decoration:none;user-select:none;white-space:nowrap;vertical-align:middle;padding:0;box-shadow:var(--snice-shadow-xs,0 1px 2px 0 rgb(0 0 0 / .03));transition:color var(--snice-transition-fast, 150ms) var(--snice-ease-out, cubic-bezier(.22,1,.36,1)),background-color var(--snice-transition-fast, 150ms) var(--snice-ease-out, cubic-bezier(.22,1,.36,1)),border-color var(--snice-transition-fast, 150ms) var(--snice-ease-out, cubic-bezier(.22,1,.36,1)),box-shadow var(--snice-transition-fast, 150ms) var(--snice-ease-out, cubic-bezier(.22,1,.36,1)),transform 180ms var(--snice-ease-spring, cubic-bezier(.34,1.56,.64,1));cursor:inherit;position:relative}.button::before{content:'';position:absolute;inset:0;border-radius:inherit;background:var(--snice-gradient-convex,none);pointer-events:none}.button:active:not(.button--disabled){transition:color 50ms var(--snice-ease-out, ease),background-color 50ms var(--snice-ease-out, ease),border-color 50ms var(--snice-ease-out, ease),box-shadow 50ms var(--snice-ease-out, ease),transform 80ms var(--snice-ease-out, ease);transform:translateY(0) scale(.97)}.button:focus-visible{outline:0;box-shadow:0 0 0 var(--snice-focus-ring-width,2px) var(--snice-focus-ring-color,rgb(59 130 246 / .5))}.button--disabled{opacity:.5;cursor:not-allowed;box-shadow:none}.button--small{font-size:var(--snice-font-size-sm, .875rem);height:2rem;padding:0 var(--snice-spacing-sm,.75rem);border-radius:var(--snice-border-radius-md,.25rem)}.button--medium{font-size:var(--snice-font-size-md, 1rem);height:2.5rem;padding:0 var(--snice-spacing-md,1rem);border-radius:var(--snice-border-radius-md,.25rem)}.button--large{font-size:var(--snice-font-size-lg, 1.125rem);height:3rem;padding:0 var(--snice-spacing-lg,1.5rem);border-radius:var(--snice-border-radius-md,.25rem)}.button--default{background-color:var(--snice-color-surface,rgb(255 255 255));color:var(--snice-color-text,rgb(23 23 23));border-color:var(--snice-color-border,rgb(226 226 226))}.button--default:hover:not(.button--disabled){background-color:var(--snice-color-surface-container-low,rgb(250 250 250));border-color:var(--snice-color-border-hover,rgb(204 204 204));box-shadow:var(--snice-shadow-sm,0 1px 2px 0 rgb(0 0 0 / .05));transform:translateY(-1px)}.button--default:active:not(.button--disabled){background-color:var(--snice-color-surface-container,rgb(248 248 248));border-color:var(--snice-color-border-hover,rgb(204 204 204));box-shadow:var(--snice-shadow-inset-sm,inset 0 1px 2px rgb(0 0 0 / .05));transform:translateY(0) scale(.97)}.button--primary{background-color:var(--snice-color-primary,rgb(37 99 235));color:var(--snice-color-text-inverse,rgb(250 250 250));border-color:var(--snice-color-primary,rgb(37 99 235))}.button--primary:hover:not(.button--disabled){background-color:var(--snice-color-primary-hover,rgb(29 78 216));border-color:var(--snice-color-primary-hover,rgb(29 78 216));box-shadow:var(--snice-shadow-sm,0 1px 2px 0 rgb(0 0 0 / .05));transform:translateY(-1px)}.button--primary:active:not(.button--disabled){background-color:color-mix(in srgb,var(--snice-color-primary,rgb(37 99 235)) 85%,#fff);border-color:color-mix(in srgb,var(--snice-color-primary,rgb(37 99 235)) 85%,#fff);box-shadow:var(--snice-shadow-inset-sm,inset 0 1px 2px rgb(0 0 0 / .05));transform:translateY(0) scale(.97)}.button--success{background-color:var(--snice-color-success,rgb(22 163 74));color:var(--snice-color-text-inverse,rgb(250 250 250));border-color:var(--snice-color-success,rgb(22 163 74))}.button--success:hover:not(.button--disabled){background-color:var(--snice-color-success-hover,rgb(21 128 61));border-color:var(--snice-color-success-hover,rgb(21 128 61));box-shadow:var(--snice-shadow-sm,0 1px 2px 0 rgb(0 0 0 / .05));transform:translateY(-1px)}.button--success:active:not(.button--disabled){background-color:color-mix(in srgb,var(--snice-color-success,rgb(22 163 74)) 85%,#fff);border-color:color-mix(in srgb,var(--snice-color-success,rgb(22 163 74)) 85%,#fff);box-shadow:var(--snice-shadow-inset-sm,inset 0 1px 2px rgb(0 0 0 / .05));transform:translateY(0) scale(.97)}.button--warning{background-color:var(--snice-color-warning,rgb(202 138 4));color:var(--button-warning-text,rgb(69 26 3));border-color:var(--snice-color-warning,rgb(202 138 4))}.button--warning:hover:not(.button--disabled){background-color:var(--snice-color-warning-hover,rgb(161 98 7));border-color:var(--snice-color-warning-hover,rgb(161 98 7));box-shadow:var(--snice-shadow-sm,0 1px 2px 0 rgb(0 0 0 / .05));transform:translateY(-1px)}.button--warning:active:not(.button--disabled){background-color:color-mix(in srgb,var(--snice-color-warning,rgb(202 138 4)) 85%,#fff);border-color:color-mix(in srgb,var(--snice-color-warning,rgb(202 138 4)) 85%,#fff);box-shadow:var(--snice-shadow-inset-sm,inset 0 1px 2px rgb(0 0 0 / .05));transform:translateY(0) scale(.97)}.button--danger{background-color:var(--snice-color-danger,rgb(220 38 38));color:var(--snice-color-text-inverse,rgb(250 250 250));border-color:var(--snice-color-danger,rgb(220 38 38))}.button--danger:hover:not(.button--disabled){background-color:var(--snice-color-danger-hover,rgb(185 28 28));border-color:var(--snice-color-danger-hover,rgb(185 28 28));box-shadow:var(--snice-shadow-sm,0 1px 2px 0 rgb(0 0 0 / .05));transform:translateY(-1px)}.button--danger:active:not(.button--disabled){background-color:color-mix(in srgb,var(--snice-color-danger,rgb(220 38 38)) 85%,#fff);border-color:color-mix(in srgb,var(--snice-color-danger,rgb(220 38 38)) 85%,#fff);box-shadow:var(--snice-shadow-inset-sm,inset 0 1px 2px rgb(0 0 0 / .05));transform:translateY(0) scale(.97)}.button--text{background-color:transparent;color:var(--snice-color-primary,rgb(37 99 235));border-color:transparent;box-shadow:none}.button--text::before{display:none}.button--text:hover:not(.button--disabled){background-color:var(--snice-color-primary-alpha,rgb(37 99 235 / .1));color:var(--snice-color-primary-hover,rgb(29 78 216));box-shadow:none;transform:none}.button--text:active:not(.button--disabled){background-color:var(--snice-color-primary-alpha-strong,rgb(37 99 235 / .15));box-shadow:none;transform:none}.button--outline{box-shadow:none}.button--outline::before{display:none}.button--outline.button--default{background-color:transparent;color:var(--snice-color-text,rgb(23 23 23))}.button--outline.button--primary{background-color:transparent;color:var(--snice-color-primary,rgb(37 99 235))}.button--outline.button--success{background-color:transparent;color:var(--snice-color-success,rgb(22 163 74))}.button--outline.button--warning{background-color:transparent;color:var(--snice-color-warning,rgb(202 138 4))}.button--outline.button--danger{background-color:transparent;color:var(--snice-color-danger,rgb(220 38 38))}.button--outline.button--default:hover:not(.button--disabled){background-color:var(--snice-color-surface-hover,rgb(245 245 245));box-shadow:none;transform:none}.button--outline.button--primary:hover:not(.button--disabled){background-color:var(--snice-color-primary-subtle,rgb(239 246 255));color:var(--snice-color-primary-hover,rgb(29 78 216));box-shadow:none;transform:none}.button--outline.button--success:hover:not(.button--disabled){background-color:var(--snice-color-success-subtle,rgb(220 252 231));color:var(--snice-color-success-hover,rgb(21 128 61));box-shadow:none;transform:none}.button--outline.button--warning:hover:not(.button--disabled){background-color:var(--snice-color-warning-subtle,rgb(254 249 195));color:var(--snice-color-warning-hover,rgb(161 98 7));box-shadow:none;transform:none}.button--outline.button--danger:hover:not(.button--disabled){background-color:var(--snice-color-danger-subtle,rgb(254 226 226));color:var(--snice-color-danger-hover,rgb(185 28 28));box-shadow:none;transform:none}.button--outline:active:not(.button--disabled){background-color:var(--snice-color-primary-subtle-hover,var(--snice-color-surface-active,rgb(235 235 235)));box-shadow:none;transform:none}.button--pill{border-radius:var(--snice-border-radius-pill,9999px)}.button--circle{border-radius:var(--snice-border-radius-circle,50%);width:var(--size);padding:0}.button--small.button--circle{--size:2rem}.button--medium.button--circle{--size:2.5rem}.button--large.button--circle{--size:3rem}.button--loading{position:relative;cursor:wait}.button--loading .label{visibility:hidden}.spinner{display:none;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:1em;height:1em}.button--loading .spinner{display:block}.spinner::after{content:'';display:block;width:100%;height:100%;box-sizing:border-box;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:.6s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}.icon-slot{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:1.25em;height:1.25em;line-height:1}.icon-slot:empty{display:none}.icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:1.25em;height:1.25em;line-height:1}.icon img{width:100%;height:100%;object-fit:contain}.icon svg{width:100%;height:100%}.icon-slot ::slotted(*){display:flex;align-items:center;justify-content:center;font-size:1.25em;line-height:1}.icon-slot ::slotted(svg){width:1.25em;height:1.25em}.icon-slot ::slotted(img){width:1.25em;height:1.25em;object-fit:contain}.button--icon-start .icon,.button--icon-start .icon-slot{margin-right:.5em}.button--icon-end .icon,.button--icon-end .icon-slot{margin-left:.5em}.button--circle .icon,.button--circle .icon-slot{margin:0;transform:none;width:auto;height:auto}.button--circle .icon svg{width:1.25em;height:1.25em}.button--circle .icon-slot ::slotted(*){font-size:1.25em;line-height:1;display:flex;align-items:center;justify-content:center}.button--circle .icon-slot ::slotted(svg){width:1.25em;height:1.25em}@media (prefers-reduced-motion:reduce){.icon-slot,.spinner,button,button:active{transition-duration:0s!important;animation-duration:0s!important}button:active{transform:none}}";
|
|
3057
3054
|
|
|
3058
3055
|
(() => {
|
|
3059
3056
|
let _classDecorators = [snice.element('snice-button', { formAssociated: true, delegatesFocus: true })];
|
|
@@ -3316,7 +3313,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
3316
3313
|
this.formDisabled = disabled;
|
|
3317
3314
|
}
|
|
3318
3315
|
styles() {
|
|
3319
|
-
return snice.css /*css*/ `${cssContent$
|
|
3316
|
+
return snice.css /*css*/ `${cssContent$j}`;
|
|
3320
3317
|
}
|
|
3321
3318
|
// Keep these methods for backwards compatibility if needed
|
|
3322
3319
|
setLoading(loading) {
|
|
@@ -3353,7 +3350,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
3353
3350
|
return _classThis;
|
|
3354
3351
|
})();
|
|
3355
3352
|
|
|
3356
|
-
var cssContent$
|
|
3353
|
+
var cssContent$i = ":host([hidden]){display:none}:host{display:inline-flex;align-items:center;align-self:flex-end;min-height:2.5rem;font-family:var(--snice-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif)}:host([size=small]){min-height:2rem;align-self:center}:host([size=large]){min-height:3rem;align-self:center}:host([compact]){min-height:0;align-self:center}.checkbox-wrapper{display:inline-flex;align-items:center;position:relative;cursor:pointer;user-select:none;min-height:1.25rem}.checkbox-wrapper--disabled{cursor:not-allowed;opacity:.6}.checkbox-input{position:absolute;opacity:0;left:0;top:0;width:100%;height:100%;margin:0;cursor:pointer;z-index:1;pointer-events:all}.checkbox{position:relative;display:inline-flex;align-items:center;justify-content:center;background:var(--snice-color-surface-container-lowest,rgb(248 247 245));border:2px solid var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-sm,.125rem);transition:all var(--snice-transition-fast, 150ms) ease}.checkbox--small{width:1rem;height:1rem}.checkbox--medium{width:1.25rem;height:1.25rem}.checkbox--large{width:1.5rem;height:1.5rem}.checkbox-input:checked~.checkbox{background:var(--snice-color-primary,rgb(37 99 235));border-color:var(--snice-color-primary,rgb(37 99 235))}.checkbox-input:focus-visible~.checkbox{box-shadow:0 0 0 3px var(--snice-focus-ring-color,rgb(59 130 246 / .5));outline:2px solid var(--snice-color-primary,rgb(37 99 235));outline-offset:2px}.checkbox-input:disabled~.checkbox{background:var(--snice-color-surface-container-low,rgb(250 250 250));border-color:var(--snice-color-border,rgb(226 226 226))}.checkbox-input:disabled:checked~.checkbox{background:var(--snice-color-text-secondary,rgb(82 82 82));border-color:var(--snice-color-text-secondary,rgb(82 82 82))}.checkbox--invalid{border-color:var(--snice-color-danger,rgb(220 38 38))}.checkbox-input:checked~.checkbox--invalid{background:var(--snice-color-danger,rgb(220 38 38));border-color:var(--snice-color-danger,rgb(220 38 38))}.checkbox-icon{position:absolute;display:none;color:var(--snice-color-text-inverse,rgb(250 250 250))}.checkbox-input:checked~.checkbox .checkbox-icon--check{display:block}.checkbox--indeterminate .checkbox-icon--check{display:none!important}.checkbox--indeterminate .checkbox-icon--indeterminate{display:block!important}.checkbox--small .checkbox-icon{width:.75rem;height:.75rem}.checkbox--medium .checkbox-icon{width:.875rem;height:.875rem}.checkbox--large .checkbox-icon{width:1.125rem;height:1.125rem}.checkbox-label{margin-left:.5rem;color:var(--snice-color-text,rgb(23 23 23));cursor:pointer}.checkbox-label--small{font-size:var(--snice-font-size-sm, .875rem)}.checkbox-label--medium{font-size:var(--snice-font-size-md, 1rem)}.checkbox-label--large{font-size:var(--snice-font-size-lg, 1.125rem)}.checkbox-wrapper--disabled .checkbox-label{color:var(--snice-color-text-secondary,rgb(82 82 82));cursor:not-allowed}.checkbox-label--required::after{content:' *';color:var(--snice-color-danger,rgb(220 38 38))}.checkbox-wrapper:not(.checkbox-wrapper--disabled):hover .checkbox{border-color:var(--snice-color-primary,rgb(37 99 235))}.checkbox-wrapper:not(.checkbox-wrapper--disabled):hover .checkbox-input:checked~.checkbox{background:var(--snice-color-primary-hover,rgb(29 78 216));border-color:var(--snice-color-primary-hover,rgb(29 78 216))}.checkbox-wrapper--loading{cursor:wait;opacity:.7}.checkbox--loading{cursor:wait}.checkbox-spinner{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;width:100%;height:100%;pointer-events:none}.checkbox-spinner::after{content:'';display:block;width:.75em;height:.75em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:.6s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){*{transition-duration:0s!important;animation-duration:0s!important}}";
|
|
3357
3354
|
|
|
3358
3355
|
(() => {
|
|
3359
3356
|
let _classDecorators = [snice.element('snice-checkbox', { formAssociated: true, delegatesFocus: true })];
|
|
@@ -3769,7 +3766,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
3769
3766
|
};
|
|
3770
3767
|
}
|
|
3771
3768
|
styles() {
|
|
3772
|
-
return snice.css /*css*/ `${cssContent$
|
|
3769
|
+
return snice.css /*css*/ `${cssContent$i}`;
|
|
3773
3770
|
}
|
|
3774
3771
|
// Public API
|
|
3775
3772
|
focus() {
|
|
@@ -3831,7 +3828,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
3831
3828
|
return _classThis;
|
|
3832
3829
|
})();
|
|
3833
3830
|
|
|
3834
|
-
var cssContent$
|
|
3831
|
+
var cssContent$h = ":host{--modal-z-index:1000;--modal-backdrop-bg:var(--snice-modal-backdrop, rgb(0 0 0 / 0.6));--modal-backdrop-blur:6px;--modal-panel-bg:var(--snice-color-surface-container-highest, var(--snice-color-surface, rgb(255 255 255)));--modal-panel-shadow:var(--snice-shadow-2xl, 0 25px 50px -12px rgb(0 0 0 / 0.25));--modal-border-radius:var(--snice-border-radius-lg, 8px);--modal-padding:var(--snice-spacing-lg, 1.5rem);--modal-transition-duration:200ms}.modal{position:fixed;top:0;left:0;right:0;bottom:0;z-index:var(--modal-z-index);display:flex;align-items:center;justify-content:center;padding:var(--modal-padding);pointer-events:none;opacity:0;visibility:hidden}.modal--open{pointer-events:auto;opacity:1;visibility:visible}.modal__backdrop{position:absolute;top:0;left:0;right:0;bottom:0;background:var(--modal-backdrop-bg);backdrop-filter:blur(var(--modal-backdrop-blur));-webkit-backdrop-filter:blur(var(--modal-backdrop-blur));opacity:0;transition:opacity 180ms var(--snice-ease-out, ease)}.modal--open .modal__backdrop{opacity:1}.modal__panel{position:relative;background:var(--modal-panel-bg);border-radius:var(--modal-border-radius);box-shadow:var(--modal-panel-shadow);display:flex;flex-direction:column;max-height:calc(100vh - 2 * var(--modal-padding));opacity:0;transform:scale(.96) translateY(1rem);transition:opacity 260ms var(--snice-ease-out, ease) 80ms,transform 320ms var(--snice-ease-spring, cubic-bezier(.34,1.56,.64,1)) 80ms;z-index:1}.modal--open .modal__panel{opacity:1;transform:scale(1) translateY(0)}.modal--open .modal__panel>*{animation:snice-modal-content-in 240ms var(--snice-ease-out,ease) backwards}.modal--open .modal__header{animation-delay:140ms}.modal--open .modal__body{animation-delay:180ms}.modal--open .modal__footer{animation-delay:220ms}@keyframes snice-modal-content-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.modal__panel--small{width:100%;max-width:25rem}.modal__panel--medium{width:100%;max-width:37.5rem}.modal__panel--large{width:100%;max-width:56.25rem}.modal__panel--fullscreen{width:calc(100vw - 2 * var(--modal-padding));height:calc(100vh - 2 * var(--modal-padding));max-width:none}.modal__header{font-family:var(--snice-font-family-display, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif);display:flex;align-items:center;justify-content:space-between;padding:var(--modal-padding);padding-bottom:var(--snice-spacing-sm,.5rem)}.modal__header--empty{font-family:var(--snice-font-family-display, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif);display:none}::slotted([slot=header]){flex:1;margin:0;font-size:var(--snice-font-size-lg, 1.125rem);font-weight:var(--snice-font-weight-semibold,600);color:var(--snice-color-text,rgb(23 23 23))}.modal__close{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;padding:0;margin-left:auto;background:0 0;border:none;border-radius:var(--snice-border-radius-md,6px);color:var(--snice-color-text-secondary,rgb(82 82 82));cursor:pointer;transition:background-color var(--snice-transition-fast, 150ms) ease,color var(--snice-transition-fast, 150ms) ease}.modal__close:hover{background:var(--snice-color-surface-hover,rgb(241 241 241));color:var(--snice-color-text,rgb(23 23 23))}.modal__close:focus-visible{outline:2px solid var(--snice-color-primary,rgb(37 99 235));outline-offset:2px}.modal__close svg{width:1.125rem;height:1.125rem}.modal__body{flex:1;padding:var(--modal-padding);overflow-y:auto;color:var(--snice-color-text,rgb(23 23 23))}.modal__footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--snice-spacing-sm,.5rem);padding:var(--modal-padding);padding-top:var(--snice-spacing-sm,.5rem)}.modal__footer--empty{display:none}.modal__header--empty+.modal__body{font-family:var(--snice-font-family-display, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif);padding-top:var(--modal-padding)}.modal__body:has(+ .modal__footer--empty){padding-bottom:var(--modal-padding)}@keyframes slideUp{from{opacity:0;transform:translateY(6.25rem)}to{opacity:1;transform:translateY(0)}}@keyframes fadeOut{from{opacity:1}to{opacity:0}}@media (max-width:640px){.modal{padding:0}.modal__panel:not(.modal__panel--fullscreen){width:100%;height:100%;max-width:none;max-height:none;border-radius:0}}@media (prefers-reduced-motion:reduce){*{transition-duration:0s!important;animation-duration:0s!important}}";
|
|
3835
3832
|
|
|
3836
3833
|
(() => {
|
|
3837
3834
|
let _classDecorators = [snice.element('snice-modal')];
|
|
@@ -4004,7 +4001,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
4004
4001
|
`;
|
|
4005
4002
|
}
|
|
4006
4003
|
styles() {
|
|
4007
|
-
return snice.css /*css*/ `${cssContent$
|
|
4004
|
+
return snice.css /*css*/ `${cssContent$h}`;
|
|
4008
4005
|
}
|
|
4009
4006
|
init() {
|
|
4010
4007
|
// Make sure modal state is properly initialized
|
|
@@ -4138,7 +4135,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
4138
4135
|
return _classThis;
|
|
4139
4136
|
})();
|
|
4140
4137
|
|
|
4141
|
-
var cssContent$
|
|
4138
|
+
var cssContent$g = ":host([hidden]){display:none}:host{display:block;font-family:var(--snice-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif)}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:var(--snice-spacing-2xl,3rem) var(--snice-spacing-xl,2rem);color:var(--snice-color-text-secondary,rgb(82 82 82));animation:snice-empty-in 360ms var(--snice-ease-out,ease) backwards}@keyframes snice-empty-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.empty-state--small{padding:var(--snice-spacing-xl,2rem) var(--snice-spacing-lg,1.5rem)}.empty-state--medium{padding:var(--snice-spacing-2xl,3rem) var(--snice-spacing-xl,2rem)}.empty-state--large{padding:var(--snice-spacing-3xl,4rem) var(--snice-spacing-2xl,2.5rem)}.empty-state__icon-wrapper{display:flex;align-items:center;justify-content:center;margin-bottom:var(--snice-spacing-lg,1.5rem);position:relative;width:calc(var(--snice-font-size-4xl,4rem) * 1.8);height:calc(var(--snice-font-size-4xl,4rem) * 1.8)}.empty-state__icon-wrapper::before{content:'';position:absolute;inset:0;border-radius:50%;border:1px dashed color-mix(in srgb,var(--snice-color-border,#ddd) 70%,transparent);animation:18s linear infinite snice-empty-halo}@keyframes snice-empty-halo{to{transform:rotate(360deg)}}.empty-state__icon{font-size:var(--snice-font-size-4xl, 4rem);opacity:.5;line-height:1}.empty-state__icon svg{width:1em;height:1em;display:block}.empty-state__icon-wrapper ::slotted(*){display:flex;align-items:center;justify-content:center;font-size:var(--snice-font-size-4xl, 4rem);opacity:.5;line-height:1}.empty-state__icon-wrapper ::slotted(svg){width:var(--snice-font-size-4xl,4rem);height:var(--snice-font-size-4xl,4rem)}.empty-state__icon-wrapper ::slotted(img){width:var(--snice-font-size-4xl,4rem);height:var(--snice-font-size-4xl,4rem);object-fit:contain}.empty-state--small .empty-state__icon-wrapper{margin-bottom:var(--snice-spacing-md,1rem);width:calc(var(--snice-font-size-3xl,3rem) * 1.8);height:calc(var(--snice-font-size-3xl,3rem) * 1.8)}.empty-state--small .empty-state__icon{font-size:var(--snice-font-size-3xl, 3rem)}.empty-state--small .empty-state__icon-wrapper ::slotted(*){font-size:var(--snice-font-size-3xl, 3rem)}.empty-state--small .empty-state__icon-wrapper ::slotted(img),.empty-state--small .empty-state__icon-wrapper ::slotted(svg){width:var(--snice-font-size-3xl,3rem);height:var(--snice-font-size-3xl,3rem)}.empty-state--large .empty-state__icon-wrapper{margin-bottom:var(--snice-spacing-xl,2rem);width:calc(var(--snice-font-size-3xl,5rem) * 1.8);height:calc(var(--snice-font-size-3xl,5rem) * 1.8)}.empty-state--large .empty-state__icon{font-size:var(--snice-font-size-3xl, 5rem)}.empty-state--large .empty-state__icon-wrapper ::slotted(*){font-size:var(--snice-font-size-3xl, 5rem)}.empty-state--large .empty-state__icon-wrapper ::slotted(img),.empty-state--large .empty-state__icon-wrapper ::slotted(svg){width:var(--snice-font-size-3xl,5rem);height:var(--snice-font-size-3xl,5rem)}.empty-state__title{font-family:var(--snice-font-family-display, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif);font-size:var(--snice-heading-lg, 1.25rem);font-weight:var(--snice-font-weight-semibold,600);color:var(--snice-color-text,rgb(23 23 23));margin:0 0 var(--snice-spacing-xs,.5rem) 0}.empty-state--small .empty-state__title{font-family:var(--snice-font-family-display, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif);font-size:var(--snice-heading-md, 1.125rem);margin-bottom:var(--snice-spacing-2xs,.375rem)}.empty-state--large .empty-state__title{font-family:var(--snice-font-family-display, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif);font-size:var(--snice-heading-xl, 1.5rem);margin-bottom:var(--snice-spacing-sm,.75rem)}.empty-state__description{font-size:var(--snice-font-size-sm, .875rem);color:var(--snice-color-text-secondary,rgb(82 82 82));margin:0 0 var(--snice-spacing-lg,1.5rem) 0;max-width:32rem;line-height:var(--snice-line-height-normal, 1.5)}.empty-state--small .empty-state__description{font-size:var(--snice-font-size-xs, .8125rem);margin-bottom:var(--snice-spacing-md,1rem)}.empty-state--large .empty-state__description{font-size:var(--snice-font-size-md, 1rem);margin-bottom:var(--snice-spacing-xl,2rem)}.empty-state__action{display:inline-flex;align-items:center;justify-content:center;padding:var(--snice-spacing-xs,.5rem) var(--snice-spacing-md,1rem);font-size:var(--snice-font-size-sm, .875rem);font-weight:var(--snice-font-weight-medium,500);color:var(--snice-color-text-inverse,rgb(250 250 250));background:var(--snice-color-primary,rgb(37 99 235));border:none;border-radius:var(--snice-border-radius-md,.375rem);text-decoration:none;cursor:pointer;transition:background var(--snice-transition-fast, 150ms)}.empty-state__action:hover{background:var(--snice-color-primary-hover,rgb(29 78 216))}.empty-state--small .empty-state__action{padding:var(--snice-spacing-2xs,.375rem) var(--snice-spacing-sm,.75rem);font-size:var(--snice-font-size-xs, .8125rem)}.empty-state--large .empty-state__action{padding:var(--snice-spacing-sm,.625rem) var(--snice-spacing-lg,1.25rem);font-size:var(--snice-font-size-md, 1rem)}.empty-state__extra ::slotted(*){margin-top:var(--snice-spacing-md,1rem)}@media (prefers-reduced-motion:reduce){*{transition-duration:0s!important;animation-duration:0s!important}}";
|
|
4142
4139
|
|
|
4143
4140
|
(() => {
|
|
4144
4141
|
let _classDecorators = [snice.element('snice-empty-state')];
|
|
@@ -4233,7 +4230,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
4233
4230
|
`;
|
|
4234
4231
|
}
|
|
4235
4232
|
styles() {
|
|
4236
|
-
return snice.css /*css*/ `${cssContent$
|
|
4233
|
+
return snice.css /*css*/ `${cssContent$g}`;
|
|
4237
4234
|
}
|
|
4238
4235
|
handleActionClick(e) {
|
|
4239
4236
|
if (!this.actionHref) {
|
|
@@ -4258,6 +4255,265 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
4258
4255
|
return _classThis;
|
|
4259
4256
|
})();
|
|
4260
4257
|
|
|
4258
|
+
var cssContent$f = ":host([hidden]){display:none}:host{--progress-height:0.5rem;--progress-radius:4px;--progress-bg:var(--snice-color-border, rgb(226 226 226));--progress-color:var(--snice-color-primary, rgb(37 99 235));--progress-animation-duration:1.5s;display:inline-block;width:100%}:host([variant=circular]){width:auto}.progress{position:relative;width:100%;background:var(--progress-bg);border-radius:var(--progress-radius);overflow:hidden}.progress--linear{height:var(--progress-height)}.progress__bar{height:100%;background:var(--progress-color);border-radius:var(--progress-radius);transition:width .3s;position:relative;overflow:hidden}:host([size=small]){--progress-height:0.25rem;--progress-spinner-size:1.5rem;--progress-spinner-stroke:3px}:host([size=medium]){--progress-height:0.5rem;--progress-spinner-size:2.5rem;--progress-spinner-stroke:4px}:host([size=large]){--progress-height:0.75rem;--progress-spinner-size:3.5rem;--progress-spinner-stroke:5px}:host([size=xl]){--progress-height:1rem;--progress-spinner-size:5rem;--progress-spinner-stroke:6px}:host([size=xxl]){--progress-height:1.25rem;--progress-spinner-size:7.5rem;--progress-spinner-stroke:8px}:host([size=xxxl]){--progress-height:1.5rem;--progress-spinner-size:10rem;--progress-spinner-stroke:10px}:host([color=primary]){--progress-color:var(--snice-color-primary, rgb(37 99 235))}:host([color=success]){--progress-color:var(--snice-color-success, rgb(22 163 74))}:host([color=warning]){--progress-color:var(--snice-color-warning, rgb(217 119 6))}:host([color=error]){--progress-color:var(--snice-color-danger, rgb(220 38 38))}:host([color=info]){--progress-color:var(--snice-color-primary, rgb(37 99 235))}:host([striped]) .progress__bar::after{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background-image:linear-gradient(45deg,rgb(255 255 255 / .15) 25%,transparent 25%,transparent 50%,rgb(255 255 255 / .15) 50%,rgb(255 255 255 / .15) 75%,transparent 75%,transparent);background-size:1rem 1rem}:host([animated]) .progress__bar::after{animation:progress-stripes var(--progress-animation-duration) linear infinite}@keyframes progress-stripes{from{background-position:1rem 0}to{background-position:0 0}}:host([indeterminate]) .progress__bar{width:30%!important;animation:progress-indeterminate var(--progress-animation-duration) ease-in-out infinite}@keyframes progress-indeterminate{0%{transform:translateX(-100%)}100%{transform:translateX(400%)}}.progress__label{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:.75rem;font-weight:var(--snice-font-weight-semibold,600);color:var(--snice-color-text-inverse,rgb(255 255 255));white-space:nowrap;z-index:1}.progress--with-label{height:calc(var(--progress-height) + .5rem);min-height:1.25rem}.progress--circular{display:inline-block;width:var(--progress-spinner-size,2.5rem);height:var(--progress-spinner-size,2.5rem);position:relative;background:0 0}.progress__circle{transform:rotate(-90deg);width:100%;height:100%;background:0 0;shape-rendering:geometricPrecision}.progress__circle-bar,.progress__circle-bg{fill:none;stroke-width:var(--progress-spinner-stroke,4px);shape-rendering:geometricPrecision;vector-effect:non-scaling-stroke}.progress__circle-bg{stroke:var(--progress-bg);opacity:var(--progress-track-opacity, .2)}.progress__circle-bar{stroke:var(--progress-color);stroke-linecap:round;transition:stroke-dashoffset .3s}:host([variant=circular][indeterminate]) .progress__circle{animation:2s linear infinite progress-rotate}:host([variant=circular][indeterminate]) .progress__circle-bar{animation:1.5s ease-in-out infinite progress-dash}@keyframes progress-rotate{100%{transform:rotate(270deg)}}@keyframes progress-dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}100%{stroke-dasharray:90,150;stroke-dashoffset:-124}}.progress__circle-label{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:.625rem;font-weight:var(--snice-font-weight-semibold,600);color:var(--progress-color);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}:host([size=small]) .progress__circle-label{font-size:.375rem}:host([size=medium]) .progress__circle-label{font-size:.625rem}:host([size=large]) .progress__circle-label{font-size:1rem}:host([size=xl]) .progress__circle-label{font-size:1.25rem}:host([size=xxl]) .progress__circle-label{font-size:1.75rem}:host([size=xxxl]) .progress__circle-label{font-size:2.25rem}@media (prefers-reduced-motion:reduce){*{transition-duration:0s!important;animation-duration:0s!important}}";
|
|
4259
|
+
|
|
4260
|
+
(() => {
|
|
4261
|
+
let _classDecorators = [snice.element('snice-progress')];
|
|
4262
|
+
let _classDescriptor;
|
|
4263
|
+
let _classExtraInitializers = [];
|
|
4264
|
+
let _classThis;
|
|
4265
|
+
let _classSuper = HTMLElement;
|
|
4266
|
+
let _instanceExtraInitializers = [];
|
|
4267
|
+
let _value_decorators;
|
|
4268
|
+
let _value_initializers = [];
|
|
4269
|
+
let _value_extraInitializers = [];
|
|
4270
|
+
let _max_decorators;
|
|
4271
|
+
let _max_initializers = [];
|
|
4272
|
+
let _max_extraInitializers = [];
|
|
4273
|
+
let _variant_decorators;
|
|
4274
|
+
let _variant_initializers = [];
|
|
4275
|
+
let _variant_extraInitializers = [];
|
|
4276
|
+
let _size_decorators;
|
|
4277
|
+
let _size_initializers = [];
|
|
4278
|
+
let _size_extraInitializers = [];
|
|
4279
|
+
let _color_decorators;
|
|
4280
|
+
let _color_initializers = [];
|
|
4281
|
+
let _color_extraInitializers = [];
|
|
4282
|
+
let _indeterminate_decorators;
|
|
4283
|
+
let _indeterminate_initializers = [];
|
|
4284
|
+
let _indeterminate_extraInitializers = [];
|
|
4285
|
+
let _showLabel_decorators;
|
|
4286
|
+
let _showLabel_initializers = [];
|
|
4287
|
+
let _showLabel_extraInitializers = [];
|
|
4288
|
+
let _label_decorators;
|
|
4289
|
+
let _label_initializers = [];
|
|
4290
|
+
let _label_extraInitializers = [];
|
|
4291
|
+
let _striped_decorators;
|
|
4292
|
+
let _striped_initializers = [];
|
|
4293
|
+
let _striped_extraInitializers = [];
|
|
4294
|
+
let _animated_decorators;
|
|
4295
|
+
let _animated_initializers = [];
|
|
4296
|
+
let _animated_extraInitializers = [];
|
|
4297
|
+
let _thickness_decorators;
|
|
4298
|
+
let _thickness_initializers = [];
|
|
4299
|
+
let _thickness_extraInitializers = [];
|
|
4300
|
+
let _progressBar_decorators;
|
|
4301
|
+
let _progressBar_initializers = [];
|
|
4302
|
+
let _progressBar_extraInitializers = [];
|
|
4303
|
+
let _circleBar_decorators;
|
|
4304
|
+
let _circleBar_initializers = [];
|
|
4305
|
+
let _circleBar_extraInitializers = [];
|
|
4306
|
+
let _labelElement_decorators;
|
|
4307
|
+
let _labelElement_initializers = [];
|
|
4308
|
+
let _labelElement_extraInitializers = [];
|
|
4309
|
+
let _circleLabelElement_decorators;
|
|
4310
|
+
let _circleLabelElement_initializers = [];
|
|
4311
|
+
let _circleLabelElement_extraInitializers = [];
|
|
4312
|
+
let _render_decorators;
|
|
4313
|
+
let _styles_decorators;
|
|
4314
|
+
let _handleProgressChange_decorators;
|
|
4315
|
+
let _handleThicknessChange_decorators;
|
|
4316
|
+
let _handleColorChange_decorators;
|
|
4317
|
+
(class extends _classSuper {
|
|
4318
|
+
static { _classThis = this; }
|
|
4319
|
+
constructor() {
|
|
4320
|
+
super(...arguments);
|
|
4321
|
+
this.value = (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _value_initializers, 0));
|
|
4322
|
+
this.max = (__runInitializers(this, _value_extraInitializers), __runInitializers(this, _max_initializers, 100));
|
|
4323
|
+
this.variant = (__runInitializers(this, _max_extraInitializers), __runInitializers(this, _variant_initializers, 'linear'));
|
|
4324
|
+
this.size = (__runInitializers(this, _variant_extraInitializers), __runInitializers(this, _size_initializers, 'medium'));
|
|
4325
|
+
this.color = (__runInitializers(this, _size_extraInitializers), __runInitializers(this, _color_initializers, 'primary'));
|
|
4326
|
+
this.indeterminate = (__runInitializers(this, _color_extraInitializers), __runInitializers(this, _indeterminate_initializers, false));
|
|
4327
|
+
this.showLabel = (__runInitializers(this, _indeterminate_extraInitializers), __runInitializers(this, _showLabel_initializers, false));
|
|
4328
|
+
this.label = (__runInitializers(this, _showLabel_extraInitializers), __runInitializers(this, _label_initializers, ''));
|
|
4329
|
+
this.striped = (__runInitializers(this, _label_extraInitializers), __runInitializers(this, _striped_initializers, false));
|
|
4330
|
+
this.animated = (__runInitializers(this, _striped_extraInitializers), __runInitializers(this, _animated_initializers, false));
|
|
4331
|
+
this.thickness = (__runInitializers(this, _animated_extraInitializers), __runInitializers(this, _thickness_initializers, 4));
|
|
4332
|
+
this.progressBar = (__runInitializers(this, _thickness_extraInitializers), __runInitializers(this, _progressBar_initializers, void 0));
|
|
4333
|
+
this.circleBar = (__runInitializers(this, _progressBar_extraInitializers), __runInitializers(this, _circleBar_initializers, void 0));
|
|
4334
|
+
this.labelElement = (__runInitializers(this, _circleBar_extraInitializers), __runInitializers(this, _labelElement_initializers, void 0));
|
|
4335
|
+
this.circleLabelElement = (__runInitializers(this, _labelElement_extraInitializers), __runInitializers(this, _circleLabelElement_initializers, void 0));
|
|
4336
|
+
this.radius = (__runInitializers(this, _circleLabelElement_extraInitializers), 0);
|
|
4337
|
+
this.circumference = 0;
|
|
4338
|
+
}
|
|
4339
|
+
static {
|
|
4340
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
4341
|
+
_value_decorators = [snice.property({ type: Number, })];
|
|
4342
|
+
_max_decorators = [snice.property({ type: Number, })];
|
|
4343
|
+
_variant_decorators = [snice.property({})];
|
|
4344
|
+
_size_decorators = [snice.property({})];
|
|
4345
|
+
_color_decorators = [snice.property({})];
|
|
4346
|
+
_indeterminate_decorators = [snice.property({ type: Boolean, })];
|
|
4347
|
+
_showLabel_decorators = [snice.property({ type: Boolean, attribute: 'show-label', })];
|
|
4348
|
+
_label_decorators = [snice.property({})];
|
|
4349
|
+
_striped_decorators = [snice.property({ type: Boolean, })];
|
|
4350
|
+
_animated_decorators = [snice.property({ type: Boolean, })];
|
|
4351
|
+
_thickness_decorators = [snice.property({ type: Number, })];
|
|
4352
|
+
_progressBar_decorators = [snice.query('.progress__bar')];
|
|
4353
|
+
_circleBar_decorators = [snice.query('.progress__circle-bar')];
|
|
4354
|
+
_labelElement_decorators = [snice.query('.progress__label')];
|
|
4355
|
+
_circleLabelElement_decorators = [snice.query('.progress__circle-label')];
|
|
4356
|
+
_render_decorators = [snice.render()];
|
|
4357
|
+
_styles_decorators = [snice.styles()];
|
|
4358
|
+
_handleProgressChange_decorators = [snice.watch('value', 'max', 'indeterminate', { immediate: false })];
|
|
4359
|
+
_handleThicknessChange_decorators = [snice.watch('thickness')];
|
|
4360
|
+
_handleColorChange_decorators = [snice.watch('color')];
|
|
4361
|
+
__esDecorate(this, null, _render_decorators, { kind: "method", name: "render", static: false, private: false, access: { has: obj => "render" in obj, get: obj => obj.render }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
4362
|
+
__esDecorate(this, null, _styles_decorators, { kind: "method", name: "styles", static: false, private: false, access: { has: obj => "styles" in obj, get: obj => obj.styles }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
4363
|
+
__esDecorate(this, null, _handleProgressChange_decorators, { kind: "method", name: "handleProgressChange", static: false, private: false, access: { has: obj => "handleProgressChange" in obj, get: obj => obj.handleProgressChange }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
4364
|
+
__esDecorate(this, null, _handleThicknessChange_decorators, { kind: "method", name: "handleThicknessChange", static: false, private: false, access: { has: obj => "handleThicknessChange" in obj, get: obj => obj.handleThicknessChange }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
4365
|
+
__esDecorate(this, null, _handleColorChange_decorators, { kind: "method", name: "handleColorChange", static: false, private: false, access: { has: obj => "handleColorChange" in obj, get: obj => obj.handleColorChange }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
4366
|
+
__esDecorate(null, null, _value_decorators, { kind: "field", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
|
|
4367
|
+
__esDecorate(null, null, _max_decorators, { kind: "field", name: "max", static: false, private: false, access: { has: obj => "max" in obj, get: obj => obj.max, set: (obj, value) => { obj.max = value; } }, metadata: _metadata }, _max_initializers, _max_extraInitializers);
|
|
4368
|
+
__esDecorate(null, null, _variant_decorators, { kind: "field", name: "variant", static: false, private: false, access: { has: obj => "variant" in obj, get: obj => obj.variant, set: (obj, value) => { obj.variant = value; } }, metadata: _metadata }, _variant_initializers, _variant_extraInitializers);
|
|
4369
|
+
__esDecorate(null, null, _size_decorators, { kind: "field", name: "size", static: false, private: false, access: { has: obj => "size" in obj, get: obj => obj.size, set: (obj, value) => { obj.size = value; } }, metadata: _metadata }, _size_initializers, _size_extraInitializers);
|
|
4370
|
+
__esDecorate(null, null, _color_decorators, { kind: "field", name: "color", static: false, private: false, access: { has: obj => "color" in obj, get: obj => obj.color, set: (obj, value) => { obj.color = value; } }, metadata: _metadata }, _color_initializers, _color_extraInitializers);
|
|
4371
|
+
__esDecorate(null, null, _indeterminate_decorators, { kind: "field", name: "indeterminate", static: false, private: false, access: { has: obj => "indeterminate" in obj, get: obj => obj.indeterminate, set: (obj, value) => { obj.indeterminate = value; } }, metadata: _metadata }, _indeterminate_initializers, _indeterminate_extraInitializers);
|
|
4372
|
+
__esDecorate(null, null, _showLabel_decorators, { kind: "field", name: "showLabel", static: false, private: false, access: { has: obj => "showLabel" in obj, get: obj => obj.showLabel, set: (obj, value) => { obj.showLabel = value; } }, metadata: _metadata }, _showLabel_initializers, _showLabel_extraInitializers);
|
|
4373
|
+
__esDecorate(null, null, _label_decorators, { kind: "field", name: "label", static: false, private: false, access: { has: obj => "label" in obj, get: obj => obj.label, set: (obj, value) => { obj.label = value; } }, metadata: _metadata }, _label_initializers, _label_extraInitializers);
|
|
4374
|
+
__esDecorate(null, null, _striped_decorators, { kind: "field", name: "striped", static: false, private: false, access: { has: obj => "striped" in obj, get: obj => obj.striped, set: (obj, value) => { obj.striped = value; } }, metadata: _metadata }, _striped_initializers, _striped_extraInitializers);
|
|
4375
|
+
__esDecorate(null, null, _animated_decorators, { kind: "field", name: "animated", static: false, private: false, access: { has: obj => "animated" in obj, get: obj => obj.animated, set: (obj, value) => { obj.animated = value; } }, metadata: _metadata }, _animated_initializers, _animated_extraInitializers);
|
|
4376
|
+
__esDecorate(null, null, _thickness_decorators, { kind: "field", name: "thickness", static: false, private: false, access: { has: obj => "thickness" in obj, get: obj => obj.thickness, set: (obj, value) => { obj.thickness = value; } }, metadata: _metadata }, _thickness_initializers, _thickness_extraInitializers);
|
|
4377
|
+
__esDecorate(null, null, _progressBar_decorators, { kind: "field", name: "progressBar", static: false, private: false, access: { has: obj => "progressBar" in obj, get: obj => obj.progressBar, set: (obj, value) => { obj.progressBar = value; } }, metadata: _metadata }, _progressBar_initializers, _progressBar_extraInitializers);
|
|
4378
|
+
__esDecorate(null, null, _circleBar_decorators, { kind: "field", name: "circleBar", static: false, private: false, access: { has: obj => "circleBar" in obj, get: obj => obj.circleBar, set: (obj, value) => { obj.circleBar = value; } }, metadata: _metadata }, _circleBar_initializers, _circleBar_extraInitializers);
|
|
4379
|
+
__esDecorate(null, null, _labelElement_decorators, { kind: "field", name: "labelElement", static: false, private: false, access: { has: obj => "labelElement" in obj, get: obj => obj.labelElement, set: (obj, value) => { obj.labelElement = value; } }, metadata: _metadata }, _labelElement_initializers, _labelElement_extraInitializers);
|
|
4380
|
+
__esDecorate(null, null, _circleLabelElement_decorators, { kind: "field", name: "circleLabelElement", static: false, private: false, access: { has: obj => "circleLabelElement" in obj, get: obj => obj.circleLabelElement, set: (obj, value) => { obj.circleLabelElement = value; } }, metadata: _metadata }, _circleLabelElement_initializers, _circleLabelElement_extraInitializers);
|
|
4381
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
4382
|
+
_classThis = _classDescriptor.value;
|
|
4383
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
4384
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
4385
|
+
}
|
|
4386
|
+
render() {
|
|
4387
|
+
if (this.variant === 'circular') {
|
|
4388
|
+
return this.renderCircular();
|
|
4389
|
+
}
|
|
4390
|
+
return this.renderLinear();
|
|
4391
|
+
}
|
|
4392
|
+
styles() {
|
|
4393
|
+
return snice.css `${cssContent$f}`;
|
|
4394
|
+
}
|
|
4395
|
+
renderLinear() {
|
|
4396
|
+
const percentage = this.getPercentage();
|
|
4397
|
+
const labelText = this.label || `${Math.round(percentage)}%`;
|
|
4398
|
+
const progressClass = `progress progress--linear${this.showLabel ? ' progress--with-label' : ''}`;
|
|
4399
|
+
const barStyle = this.indeterminate ? '' : `width: ${percentage}%`;
|
|
4400
|
+
return snice.html /*html*/ `
|
|
4401
|
+
<div part="base" class="${progressClass}"
|
|
4402
|
+
role="progressbar"
|
|
4403
|
+
aria-valuenow="${this.value}"
|
|
4404
|
+
aria-valuemin="0"
|
|
4405
|
+
aria-valuemax="${this.max}"
|
|
4406
|
+
aria-label="${labelText}">
|
|
4407
|
+
<div part="bar" class="progress__bar" style="${barStyle}">
|
|
4408
|
+
<if ${this.showLabel}>
|
|
4409
|
+
<span part="label" class="progress__label">${labelText}</span>
|
|
4410
|
+
</if>
|
|
4411
|
+
</div>
|
|
4412
|
+
</div>
|
|
4413
|
+
`;
|
|
4414
|
+
}
|
|
4415
|
+
renderCircular() {
|
|
4416
|
+
const size = this.getSizeValue();
|
|
4417
|
+
const strokeWidth = this.thickness;
|
|
4418
|
+
const radius = (size - strokeWidth * 2) / 2;
|
|
4419
|
+
const circumference = radius * 2 * Math.PI;
|
|
4420
|
+
const percentage = this.getPercentage();
|
|
4421
|
+
const offset = this.indeterminate ? 0 : circumference - (percentage / 100) * circumference;
|
|
4422
|
+
const labelText = this.label || `${Math.round(percentage)}%`;
|
|
4423
|
+
this.radius = radius;
|
|
4424
|
+
this.circumference = circumference;
|
|
4425
|
+
const viewBox = `0 0 ${size} ${size}`;
|
|
4426
|
+
const centerX = size / 2;
|
|
4427
|
+
const centerY = size / 2;
|
|
4428
|
+
return snice.html /*html*/ `
|
|
4429
|
+
<div part="base" class="progress progress--circular"
|
|
4430
|
+
role="progressbar"
|
|
4431
|
+
aria-valuenow="${this.value}"
|
|
4432
|
+
aria-valuemin="0"
|
|
4433
|
+
aria-valuemax="${this.max}"
|
|
4434
|
+
aria-label="${labelText}">
|
|
4435
|
+
<svg part="circle" class="progress__circle" viewBox="${viewBox}">
|
|
4436
|
+
<circle
|
|
4437
|
+
part="circle-bg"
|
|
4438
|
+
class="progress__circle-bg"
|
|
4439
|
+
cx="${centerX}"
|
|
4440
|
+
cy="${centerY}"
|
|
4441
|
+
r="${radius}"
|
|
4442
|
+
/>
|
|
4443
|
+
<circle
|
|
4444
|
+
part="circle-bar"
|
|
4445
|
+
class="progress__circle-bar"
|
|
4446
|
+
cx="${centerX}"
|
|
4447
|
+
cy="${centerY}"
|
|
4448
|
+
r="${radius}"
|
|
4449
|
+
stroke-dasharray="${circumference}"
|
|
4450
|
+
stroke-dashoffset="${offset}"
|
|
4451
|
+
/>
|
|
4452
|
+
</svg>
|
|
4453
|
+
<if ${this.showLabel && !this.indeterminate}>
|
|
4454
|
+
<div part="label" class="progress__circle-label">${labelText}</div>
|
|
4455
|
+
</if>
|
|
4456
|
+
</div>
|
|
4457
|
+
`;
|
|
4458
|
+
}
|
|
4459
|
+
handleProgressChange() {
|
|
4460
|
+
this.dispatchProgressChange();
|
|
4461
|
+
}
|
|
4462
|
+
handleThicknessChange() {
|
|
4463
|
+
if (this.variant === 'circular') {
|
|
4464
|
+
this.style.setProperty('--progress-spinner-stroke', `${this.thickness}px`);
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
4467
|
+
handleColorChange() {
|
|
4468
|
+
const semanticColors = ['primary', 'success', 'warning', 'error', 'info'];
|
|
4469
|
+
if (!semanticColors.includes(this.color)) {
|
|
4470
|
+
// Custom color value - apply directly via CSS variable
|
|
4471
|
+
this.style.setProperty('--progress-color', this.color);
|
|
4472
|
+
}
|
|
4473
|
+
else {
|
|
4474
|
+
// Semantic color - remove inline style, let CSS handle it
|
|
4475
|
+
this.style.removeProperty('--progress-color');
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4478
|
+
getPercentage() {
|
|
4479
|
+
if (this.indeterminate)
|
|
4480
|
+
return 0;
|
|
4481
|
+
if (this.max === 0)
|
|
4482
|
+
return 0;
|
|
4483
|
+
return Math.min(100, Math.max(0, (this.value / this.max) * 100));
|
|
4484
|
+
}
|
|
4485
|
+
setProgress(value, max) {
|
|
4486
|
+
this.value = value;
|
|
4487
|
+
if (max !== undefined) {
|
|
4488
|
+
this.max = max;
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4491
|
+
getSizeValue() {
|
|
4492
|
+
switch (this.size) {
|
|
4493
|
+
case 'small': return 24;
|
|
4494
|
+
case 'large': return 56;
|
|
4495
|
+
case 'xl': return 80;
|
|
4496
|
+
case 'xxl': return 120;
|
|
4497
|
+
case 'xxxl': return 160;
|
|
4498
|
+
default: return 40;
|
|
4499
|
+
}
|
|
4500
|
+
}
|
|
4501
|
+
dispatchProgressChange() {
|
|
4502
|
+
this.dispatchEvent(new CustomEvent('progress-change', {
|
|
4503
|
+
bubbles: true,
|
|
4504
|
+
composed: true,
|
|
4505
|
+
detail: {
|
|
4506
|
+
value: this.value,
|
|
4507
|
+
max: this.max,
|
|
4508
|
+
percentage: this.getPercentage(),
|
|
4509
|
+
indeterminate: this.indeterminate
|
|
4510
|
+
}
|
|
4511
|
+
}));
|
|
4512
|
+
}
|
|
4513
|
+
});
|
|
4514
|
+
return _classThis;
|
|
4515
|
+
})();
|
|
4516
|
+
|
|
4261
4517
|
var cssContent$e = ":host([hidden]){display:none}:host{display:block;width:100%;font-size:.875rem;line-height:1.25rem;color:var(--snice-color-text,rgb(23 23 23))}.cell-content{width:100%;min-height:1.25rem;word-wrap:break-word}:host([align=left]){text-align:left}:host([align=center]){text-align:center}:host([align=right]){text-align:right}:host([type=accounting]),:host([type=currency]),:host([type=fraction]),:host([type=number]),:host([type=percent]),:host([type=scientific]){font-variant-numeric:tabular-nums;text-align:right}:host([type=date]){font-variant-numeric:tabular-nums}:host([type=boolean]){text-align:center;font-size:1rem}.boolean--true{color:var(--snice-color-success,rgb(22 163 74))}.boolean--false{color:var(--snice-color-danger,rgb(220 38 38))}:host([type=rating]){text-align:center}:host([type=sparkline]){text-align:center}.cell-content--text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cell-content--text[data-multiline=true]{white-space:pre-wrap;word-wrap:break-word;overflow-wrap:break-word}.cell-content--date,.cell-content--number{font-feature-settings:'tnum'}:host([type=boolean]) .cell-content{display:flex;align-items:center;justify-content:center}:host([type=rating]) .cell-content{display:flex;align-items:center;justify-content:center;gap:.125rem;container-type:inline-size}:host([type=rating]) snice-rating{--rating-size:clamp(0.55rem, 16cqi, 1rem)}.cell-content--progress{width:100%;min-width:4rem}.cell-content--sparkline{display:flex;align-items:center;justify-content:center;min-width:3rem}:host(.positive){color:var(--snice-color-success,rgb(22 163 74))}:host(.negative){color:var(--snice-color-danger,rgb(220 38 38))}:host(.warning){color:var(--snice-color-warning,rgb(234 179 8));background-color:rgb(254 249 195 / .3)}:host(.danger){color:var(--snice-color-danger,rgb(220 38 38));background-color:rgb(254 226 226 / .3)}:host(.success){color:var(--snice-color-success,rgb(22 163 74));background-color:rgb(220 252 231 / .3)}:host(.info){color:var(--snice-color-primary,rgb(37 99 235));background-color:rgb(219 234 254 / .3)}:host(.date--today){font-weight:var(--snice-font-weight-semibold,600);color:var(--snice-color-primary,rgb(37 99 235))}:host(.date--past){color:var(--snice-color-text-secondary,rgb(82 82 82))}:host(.date--future){color:var(--snice-color-success,rgb(22 163 74))}:host(.number--negative.number--negative-red){color:var(--snice-color-danger,rgb(220 38 38))}:host(.number--positive.number--highlighted){color:var(--snice-color-success,rgb(22 163 74));font-weight:var(--snice-font-weight-semibold,600)}:host(.number--zero){color:var(--snice-color-text-secondary,rgb(82 82 82))}:host([truncate]) .cell-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host([wrap]) .cell-content{white-space:pre-wrap;word-wrap:break-word;overflow-wrap:break-word}:host(:not([value])) .cell-content,:host([value=\"\"]) .cell-content{color:var(--snice-color-text-secondary,rgb(82 82 82))}:host(.loading){position:relative;overflow:hidden}:host(.loading) .cell-content{opacity:.6}:host(.loading)::after{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(90deg,transparent,var(--snice-color-surface,rgb(255 255 255 / .6)),transparent);animation:1.5s infinite cell-shimmer}@keyframes cell-shimmer{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}}:host(:focus-within){outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:-2px}.cell-content button,.cell-content input,.cell-content select{font-size:inherit;line-height:inherit}.cell-content a{color:var(--snice-color-primary,rgb(37 99 235));text-decoration:none}.cell-content a:hover{text-decoration:underline}@media (max-width:768px){:host{font-size:.75rem;line-height:1rem}:host([type=boolean]){font-size:.875rem}}@media (prefers-reduced-motion:reduce){:host(.loading)::after{animation:none}}@media (prefers-contrast:high){:host(.positive){background-color:rgb(220 252 231 / .3)}:host(.negative){background-color:rgb(254 226 226 / .3)}.cell-content a{text-decoration:underline}}@media print{:host{font-size:.75rem}:host(.loading)::after{display:none}.cell-content a{text-decoration:underline}}";
|
|
4262
4518
|
|
|
4263
4519
|
const presentationProperties = [
|
|
@@ -4268,6 +4524,37 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
4268
4524
|
'fontSize',
|
|
4269
4525
|
'textDecoration',
|
|
4270
4526
|
];
|
|
4527
|
+
/** The alignment a column type renders with when the column declares none. */
|
|
4528
|
+
function defaultCellAlign(type) {
|
|
4529
|
+
if (['number', 'currency', 'percent', 'percentage', 'duration', 'filesize',
|
|
4530
|
+
'accounting', 'scientific', 'fraction'].includes(type || ''))
|
|
4531
|
+
return 'right';
|
|
4532
|
+
if (['boolean', 'rating', 'image'].includes(type || ''))
|
|
4533
|
+
return 'center';
|
|
4534
|
+
return 'left';
|
|
4535
|
+
}
|
|
4536
|
+
/**
|
|
4537
|
+
* The value a typed cell holds when the row has nothing to show — the row field
|
|
4538
|
+
* is null, or the row never carried the field at all.
|
|
4539
|
+
*
|
|
4540
|
+
* Each typed cell owns its value semantics: `false` for boolean, `0` for
|
|
4541
|
+
* rating/progress/duration/filesize, `null` for JSON, `''` for the text-shaped
|
|
4542
|
+
* rest. Handing every empty value the same `''` instead lets the cell's typed
|
|
4543
|
+
* attribute converter re-read that empty string as a value of the wrong
|
|
4544
|
+
* meaning — `''` on a Boolean property is an attribute that is *present*, i.e.
|
|
4545
|
+
* `true`, and `''` on an Object property parses to `{}`.
|
|
4546
|
+
*/
|
|
4547
|
+
function emptyCellValue(type) {
|
|
4548
|
+
switch (type) {
|
|
4549
|
+
case 'boolean': return false;
|
|
4550
|
+
case 'rating':
|
|
4551
|
+
case 'progress':
|
|
4552
|
+
case 'duration':
|
|
4553
|
+
case 'filesize': return 0;
|
|
4554
|
+
case 'json': return null;
|
|
4555
|
+
default: return '';
|
|
4556
|
+
}
|
|
4557
|
+
}
|
|
4271
4558
|
const installed = new WeakMap();
|
|
4272
4559
|
const appliedClasses = new WeakMap();
|
|
4273
4560
|
/**
|
|
@@ -4281,10 +4568,9 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
4281
4568
|
* fallback, so `formatter` wins. `formatterOverride` says the cell does NOT
|
|
4282
4569
|
* apply `column.formatter` itself, so this module owns both formatters for it.
|
|
4283
4570
|
*
|
|
4284
|
-
*
|
|
4285
|
-
*
|
|
4286
|
-
*
|
|
4287
|
-
* renders structure rather than text, still shows the formatted string.
|
|
4571
|
+
* Table-rendered cells never arrive here with a formatter: `createCellElement`
|
|
4572
|
+
* resolves the pipeline once and hands the text cell the formatted string. This
|
|
4573
|
+
* path serves the declarative and standalone cells, which own a full column.
|
|
4288
4574
|
*/
|
|
4289
4575
|
function installCellPresentation(host, formatterOverride = false) {
|
|
4290
4576
|
if (installed.has(host))
|
|
@@ -4337,12 +4623,8 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
4337
4623
|
const formatter = column.formatter
|
|
4338
4624
|
? (formatterOverride ? column.formatter : null)
|
|
4339
4625
|
: column.valueFormatter;
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
const display = host.hasAttribute('formatted')
|
|
4343
|
-
? (host.getAttribute('value') ?? '')
|
|
4344
|
-
: (formatter ? String(formatter(host.value, host.rowData) ?? '') : null);
|
|
4345
|
-
if (display !== null) {
|
|
4626
|
+
if (formatter) {
|
|
4627
|
+
const display = String(formatter(host.value, host.rowData) ?? '');
|
|
4346
4628
|
const content = host.shadowRoot?.querySelector('[part~="content"]');
|
|
4347
4629
|
if (content && (content.textContent !== display || content.childElementCount > 0)) {
|
|
4348
4630
|
content.textContent = display;
|
|
@@ -4363,6 +4645,82 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
4363
4645
|
queueMicrotask(sync);
|
|
4364
4646
|
}
|
|
4365
4647
|
|
|
4648
|
+
/**
|
|
4649
|
+
* Date parsing for the table's cell layer.
|
|
4650
|
+
*
|
|
4651
|
+
* A column of type `date` usually carries calendar dates: `'2026-03-15'` names
|
|
4652
|
+
* a day, not an instant. `new Date('2026-03-15')` nevertheless resolves the
|
|
4653
|
+
* ISO date-only forms as UTC midnight (ECMA-262 21.4.3.2), so every consumer
|
|
4654
|
+
* in a negative-offset zone formatted the day BEFORE the authored one, while
|
|
4655
|
+
* an `<input type="date">` editor holding the same string showed the authored
|
|
4656
|
+
* day. Display, export, and filtering must all agree with the string the
|
|
4657
|
+
* application wrote.
|
|
4658
|
+
*
|
|
4659
|
+
* The rule: a value that carries NO time is a calendar day and is read at
|
|
4660
|
+
* local midnight; anything that carries a time (or an offset, or is already a
|
|
4661
|
+
* `Date`/epoch number) is an instant and is left to the platform.
|
|
4662
|
+
*/
|
|
4663
|
+
/** ISO calendar dates with no time part: `YYYY`, `YYYY-MM`, `YYYY-MM-DD`. */
|
|
4664
|
+
const DATE_ONLY = /^(\d{4})(?:-(\d{2}))?(?:-(\d{2}))?$/;
|
|
4665
|
+
/**
|
|
4666
|
+
* Parse a cell value into a `Date`, or `null` when there is nothing to show.
|
|
4667
|
+
*
|
|
4668
|
+
* `null` covers the empty cases (`null`, `undefined`, `''`) and unparseable
|
|
4669
|
+
* input alike, so callers fall back to the raw value rather than rendering
|
|
4670
|
+
* "Invalid Date" or the epoch.
|
|
4671
|
+
*/
|
|
4672
|
+
function parseCellDate(value) {
|
|
4673
|
+
if (value === null || value === undefined || value === '')
|
|
4674
|
+
return null;
|
|
4675
|
+
if (value instanceof Date) {
|
|
4676
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
4677
|
+
}
|
|
4678
|
+
if (typeof value === 'number') {
|
|
4679
|
+
const fromEpoch = new Date(value);
|
|
4680
|
+
return Number.isNaN(fromEpoch.getTime()) ? null : fromEpoch;
|
|
4681
|
+
}
|
|
4682
|
+
if (typeof value === 'string') {
|
|
4683
|
+
const trimmed = value.trim();
|
|
4684
|
+
if (trimmed === '')
|
|
4685
|
+
return null;
|
|
4686
|
+
const calendarDay = DATE_ONLY.exec(trimmed);
|
|
4687
|
+
if (calendarDay) {
|
|
4688
|
+
const year = Number(calendarDay[1]);
|
|
4689
|
+
const month = calendarDay[2] === undefined ? 1 : Number(calendarDay[2]);
|
|
4690
|
+
const day = calendarDay[3] === undefined ? 1 : Number(calendarDay[3]);
|
|
4691
|
+
// Local midnight: the same calendar day in every timezone.
|
|
4692
|
+
const local = new Date(year, month - 1, day);
|
|
4693
|
+
// Years 0-99 mean themselves, not 1900+n.
|
|
4694
|
+
if (year >= 0 && year <= 99)
|
|
4695
|
+
local.setFullYear(year, month - 1, day);
|
|
4696
|
+
if (Number.isNaN(local.getTime()))
|
|
4697
|
+
return null;
|
|
4698
|
+
// `new Date(2026, 12, 1)` rolls over to 2027 instead of rejecting, so a
|
|
4699
|
+
// typo like '2026-13-01' or '2026-02-30' would render as a confident but
|
|
4700
|
+
// WRONG day. The platform calls those strings invalid; so do we, and the
|
|
4701
|
+
// caller falls back to showing the raw value.
|
|
4702
|
+
if (local.getFullYear() !== year ||
|
|
4703
|
+
local.getMonth() !== month - 1 ||
|
|
4704
|
+
local.getDate() !== day) {
|
|
4705
|
+
return null;
|
|
4706
|
+
}
|
|
4707
|
+
return local;
|
|
4708
|
+
}
|
|
4709
|
+
const parsed = new Date(trimmed);
|
|
4710
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
4711
|
+
}
|
|
4712
|
+
const coerced = new Date(value);
|
|
4713
|
+
return Number.isNaN(coerced.getTime()) ? null : coerced;
|
|
4714
|
+
}
|
|
4715
|
+
/**
|
|
4716
|
+
* Midnight on the calendar day a value falls on, in local time. Used by
|
|
4717
|
+
* day-granularity comparisons (today/past/future classification) so an
|
|
4718
|
+
* instant is never bumped across a day boundary by the UTC offset.
|
|
4719
|
+
*/
|
|
4720
|
+
function startOfLocalDay(date) {
|
|
4721
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
4722
|
+
}
|
|
4723
|
+
|
|
4366
4724
|
(() => {
|
|
4367
4725
|
let _classDecorators = [snice.element('snice-cell')];
|
|
4368
4726
|
let _classDescriptor;
|
|
@@ -4449,7 +4807,14 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
4449
4807
|
this.applyConditionalFormatting();
|
|
4450
4808
|
}
|
|
4451
4809
|
applyAlignment() {
|
|
4452
|
-
|
|
4810
|
+
// snice-cell.css already encodes each type's default alignment
|
|
4811
|
+
// (:host([type="accounting"]) and friends are right-aligned, boolean/rating
|
|
4812
|
+
// centred). Writing an inline text-align unconditionally OVERRODE that, so a
|
|
4813
|
+
// standalone accounting figure sat left-aligned beside a right-aligned
|
|
4814
|
+
// currency cell — and the accounting format's alignment padding meant
|
|
4815
|
+
// nothing. Only an alignment the caller actually declared wins.
|
|
4816
|
+
const declared = this.getAttribute('align');
|
|
4817
|
+
this.style.textAlign = declared ?? '';
|
|
4453
4818
|
}
|
|
4454
4819
|
applyConditionalFormatting() {
|
|
4455
4820
|
if (!this.column)
|
|
@@ -4580,24 +4945,60 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
4580
4945
|
return String(this.value);
|
|
4581
4946
|
return `${num.toFixed(format.decimals ?? 2)}%`;
|
|
4582
4947
|
}
|
|
4948
|
+
/**
|
|
4949
|
+
* Accounting notation is currency notation: symbol, grouped digits, and
|
|
4950
|
+
* negatives in parentheses instead of a minus sign. It shares its defaults
|
|
4951
|
+
* with `formatCurrency()` on purpose — an `accounting` cell and a `currency`
|
|
4952
|
+
* cell showing the same amount must not disagree about what money looks
|
|
4953
|
+
* like. `numberFormat` still steers decimals/grouping (its historic role
|
|
4954
|
+
* here), `currencyFormat` steers the symbol and locale, and an explicit
|
|
4955
|
+
* `numberFormat.prefix` opts out of the symbol entirely for ledgers that
|
|
4956
|
+
* label their own columns.
|
|
4957
|
+
*/
|
|
4583
4958
|
formatAccounting() {
|
|
4584
4959
|
const format = this.column.numberFormat || {};
|
|
4960
|
+
const currency = this.column.currencyFormat || {};
|
|
4585
4961
|
const num = Number(this.value);
|
|
4586
4962
|
if (isNaN(num))
|
|
4587
4963
|
return String(this.value);
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4964
|
+
const decimals = format.decimals ?? currency.decimals ?? 2;
|
|
4965
|
+
const useGrouping = format.thousandsSeparator ?? currency.thousandsSeparator ?? true;
|
|
4966
|
+
const magnitude = Math.abs(num);
|
|
4967
|
+
const options = {
|
|
4968
|
+
useGrouping,
|
|
4969
|
+
minimumFractionDigits: decimals,
|
|
4970
|
+
maximumFractionDigits: decimals,
|
|
4971
|
+
};
|
|
4972
|
+
// An author-supplied prefix/suffix is the caller naming the unit; adding a
|
|
4973
|
+
// currency symbol on top would print it twice.
|
|
4974
|
+
const symbolIsCallers = !!format.prefix || !!format.suffix;
|
|
4975
|
+
if (!symbolIsCallers) {
|
|
4976
|
+
options.style = 'currency';
|
|
4977
|
+
options.currency = currency.currency || 'USD';
|
|
4978
|
+
options.currencyDisplay = currency.currencyDisplay || currency.display || 'symbol';
|
|
4979
|
+
}
|
|
4980
|
+
let formatted;
|
|
4981
|
+
try {
|
|
4982
|
+
formatted = new Intl.NumberFormat(currency.locale || 'en-US', options).format(magnitude);
|
|
4596
4983
|
}
|
|
4597
|
-
|
|
4598
|
-
formatted =
|
|
4984
|
+
catch {
|
|
4985
|
+
formatted = magnitude.toFixed(decimals);
|
|
4986
|
+
if (useGrouping) {
|
|
4987
|
+
const parts = formatted.split('.');
|
|
4988
|
+
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
4989
|
+
formatted = parts.join('.');
|
|
4990
|
+
}
|
|
4991
|
+
if (!symbolIsCallers)
|
|
4992
|
+
formatted = `$${formatted}`;
|
|
4599
4993
|
}
|
|
4600
|
-
|
|
4994
|
+
if (format.prefix)
|
|
4995
|
+
formatted = format.prefix + formatted;
|
|
4996
|
+
if (format.suffix)
|
|
4997
|
+
formatted = formatted + format.suffix;
|
|
4998
|
+
// The trailing space is the format's whole reason for existing: it keeps a
|
|
4999
|
+
// positive row's last digit under the ")" of the row above in a
|
|
5000
|
+
// right-aligned column. A normal space collapses in HTML, so pad with NBSP.
|
|
5001
|
+
return num < 0 ? `(${formatted})` : `${formatted}\u00a0`;
|
|
4601
5002
|
}
|
|
4602
5003
|
formatScientific() {
|
|
4603
5004
|
const format = this.column.numberFormat || {};
|
|
@@ -4639,8 +5040,10 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
4639
5040
|
}
|
|
4640
5041
|
formatDate() {
|
|
4641
5042
|
const format = this.column.dateFormat || {};
|
|
4642
|
-
|
|
4643
|
-
|
|
5043
|
+
// Calendar days are read at local midnight (see table-date.ts) so this
|
|
5044
|
+
// generic cell shows the same day as `snice-cell-date` and as the editor.
|
|
5045
|
+
const date = parseCellDate(this.value);
|
|
5046
|
+
if (!date)
|
|
4644
5047
|
return String(this.value);
|
|
4645
5048
|
if (format.customFormat) {
|
|
4646
5049
|
// Simple custom format implementation
|
|
@@ -5279,7 +5682,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
5279
5682
|
_styles_decorators = [snice.styles()];
|
|
5280
5683
|
_init_decorators = [snice.ready()];
|
|
5281
5684
|
_updateAlignment_decorators = [snice.watch('align')];
|
|
5282
|
-
_updateContent_decorators = [snice.watch('value', 'column')];
|
|
5685
|
+
_updateContent_decorators = [snice.watch('value', 'column', 'dateFormat', 'customFormat', 'locale', 'relativeTime', 'showTime')];
|
|
5283
5686
|
__esDecorate(this, null, _render_decorators, { kind: "method", name: "render", static: false, private: false, access: { has: obj => "render" in obj, get: obj => obj.render }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
5284
5687
|
__esDecorate(this, null, _styles_decorators, { kind: "method", name: "styles", static: false, private: false, access: { has: obj => "styles" in obj, get: obj => obj.styles }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
5285
5688
|
__esDecorate(this, null, _init_decorators, { kind: "method", name: "init", static: false, private: false, access: { has: obj => "init" in obj, get: obj => obj.init }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
@@ -5323,6 +5726,9 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
5323
5726
|
updateAlignment() {
|
|
5324
5727
|
this.applyAlignment();
|
|
5325
5728
|
}
|
|
5729
|
+
// Every input the formatter reads, not just the value: a late
|
|
5730
|
+
// `custom-format` / `locale` / `show-time` change must repaint the day it
|
|
5731
|
+
// describes, or the cell keeps showing the format it happened to be born with.
|
|
5326
5732
|
updateContent() {
|
|
5327
5733
|
if (this.contentElement) {
|
|
5328
5734
|
const formattedValue = this.formatDateValue();
|
|
@@ -5339,8 +5745,11 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
5339
5745
|
if (this.column.formatter) {
|
|
5340
5746
|
return this.column.formatter(this.value, this.rowData);
|
|
5341
5747
|
}
|
|
5342
|
-
|
|
5343
|
-
|
|
5748
|
+
// A date-only value names a calendar day, so it is read at LOCAL midnight
|
|
5749
|
+
// (see table-date.ts). Parsing it with `new Date` would place it at UTC
|
|
5750
|
+
// midnight and render the previous day for every negative UTC offset.
|
|
5751
|
+
const date = parseCellDate(this.value);
|
|
5752
|
+
if (!date) {
|
|
5344
5753
|
return String(this.value);
|
|
5345
5754
|
}
|
|
5346
5755
|
// Show relative time if enabled
|
|
@@ -5427,13 +5836,18 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
5427
5836
|
}
|
|
5428
5837
|
}
|
|
5429
5838
|
getDateStyles() {
|
|
5430
|
-
const date =
|
|
5431
|
-
const now = new Date();
|
|
5432
|
-
const isToday = date.toDateString() === now.toDateString();
|
|
5433
|
-
const isPast = date < now;
|
|
5434
|
-
const isFuture = date > now;
|
|
5839
|
+
const date = parseCellDate(this.value);
|
|
5435
5840
|
// Apply CSS classes instead of direct styles
|
|
5436
5841
|
this.classList.remove('date--today', 'date--past', 'date--future');
|
|
5842
|
+
if (!date)
|
|
5843
|
+
return '';
|
|
5844
|
+
// Day granularity: a calendar day is today, past, or future as a whole —
|
|
5845
|
+
// the wall-clock time of day never decides which.
|
|
5846
|
+
const day = startOfLocalDay(date).getTime();
|
|
5847
|
+
const today = startOfLocalDay(new Date()).getTime();
|
|
5848
|
+
const isToday = day === today;
|
|
5849
|
+
const isPast = day < today;
|
|
5850
|
+
const isFuture = day > today;
|
|
5437
5851
|
if (isToday) {
|
|
5438
5852
|
this.classList.add('date--today');
|
|
5439
5853
|
}
|
|
@@ -7128,7 +7542,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
7128
7542
|
return _classThis;
|
|
7129
7543
|
})();
|
|
7130
7544
|
|
|
7131
|
-
var cssContent$a = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;height:2.5rem;min-width:6.25rem;flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:6.25rem;min-height:1.25rem;display:flex;align-items:center;gap:.5rem}.status-dot{width:.5rem;height:.5rem;border-radius:50%;flex-shrink:0;position:relative}.status-dot--online{background-color:var(--snice-color-success,rgb(22 163 74));box-shadow:0 0 0 2px rgb(22 163 74 / .2)}.status-dot--offline{background-color:rgb(156 163 175);box-shadow:0 0 0 2px rgb(107 114 128 / .2)}.status-dot--busy{background-color:var(--snice-color-danger,rgb(220 38 38));box-shadow:0 0 0 2px rgb(220 38 38 / .2)}.status-dot--away{background-color:var(--snice-color-warning,rgb(234 179 8));box-shadow:0 0 0 2px rgb(234 179 8 / .2)}.status-dot--custom{background-color:var(--snice-color-primary,rgb(37 99 235));box-shadow:0 0 0 2px rgb(37 99 235 / .2)}.status-dot--online::after{content:'';position:absolute;top:-2px;left:-2px;right:-2px;bottom:-2px;border-radius:50%;border:2px solid var(--snice-color-success,rgb(22 163 74));animation:2s ease-in-out infinite status-pulse;opacity:0}@keyframes status-pulse{0%{transform:scale(1);opacity:1}100%{transform:scale(1.5);opacity:0}}.status-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.status-label--online{color:var(--snice-color-success,rgb(22 163 74))}.status-label--offline{color:var(--snice-color-text-secondary,rgb(82 82 82))}.status-label--busy{color:var(--snice-color-danger,rgb(220 38 38))}.status-label--away{color:var(--snice-color-warning,rgb(234 179 8))}.status-label--custom{color:var(--snice-color-text,rgb(23 23 23))}:host([align=left]) .cell-content{justify-content:flex-start}:host([align=center]) .cell-content{justify-content:center}:host([align=right]) .cell-content{justify-content:flex-end}@media (max-width:768px){:host{color:var(--snice-color-text,rgb(23 23 23));font-size:.75rem;line-height:1rem}.status-dot{width:.375rem;height:.375rem}}@media (prefers-reduced-motion:reduce){.status-dot--online::after{animation:none}}@media print{.status-dot{border:1px solid var(--snice-color-text,rgb(23 23 23))}.status-dot--online{background-color:var(--snice-color-surface,rgb(255 255 255));border-color:var(--snice-color-success,rgb(22 163 74))}.status-dot--offline{background-color:var(--snice-color-surface,rgb(255 255 255));border-color:var(--snice-color-neutral,rgb(107 114 128))}.status-dot--busy{background-color:var(--snice-color-surface,rgb(255 255 255));border-color:var(--snice-color-danger,rgb(220 38 38))}.status-dot--away{background-color:var(--snice-color-surface,rgb(255 255 255));border-color:var(--snice-color-warning,rgb(234 179 8))}}";
|
|
7545
|
+
var cssContent$a = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;height:2.5rem;min-width:var(--snice-table-cell-min-width,6.25rem);flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:var(--snice-table-cell-min-width,6.25rem);min-height:1.25rem;display:flex;align-items:center;gap:.5rem}.status-dot{width:.5rem;height:.5rem;border-radius:50%;flex-shrink:0;position:relative}.status-dot--online{background-color:var(--snice-color-success,rgb(22 163 74));box-shadow:0 0 0 2px rgb(22 163 74 / .2)}.status-dot--offline{background-color:rgb(156 163 175);box-shadow:0 0 0 2px rgb(107 114 128 / .2)}.status-dot--busy{background-color:var(--snice-color-danger,rgb(220 38 38));box-shadow:0 0 0 2px rgb(220 38 38 / .2)}.status-dot--away{background-color:var(--snice-color-warning,rgb(234 179 8));box-shadow:0 0 0 2px rgb(234 179 8 / .2)}.status-dot--custom{background-color:var(--snice-color-primary,rgb(37 99 235));box-shadow:0 0 0 2px rgb(37 99 235 / .2)}.status-dot--online::after{content:'';position:absolute;top:-2px;left:-2px;right:-2px;bottom:-2px;border-radius:50%;border:2px solid var(--snice-color-success,rgb(22 163 74));animation:2s ease-in-out infinite status-pulse;opacity:0}@keyframes status-pulse{0%{transform:scale(1);opacity:1}100%{transform:scale(1.5);opacity:0}}.status-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.status-label--online{color:var(--snice-color-success,rgb(22 163 74))}.status-label--offline{color:var(--snice-color-text-secondary,rgb(82 82 82))}.status-label--busy{color:var(--snice-color-danger,rgb(220 38 38))}.status-label--away{color:var(--snice-color-warning,rgb(234 179 8))}.status-label--custom{color:var(--snice-color-text,rgb(23 23 23))}:host([align=left]) .cell-content{justify-content:flex-start}:host([align=center]) .cell-content{justify-content:center}:host([align=right]) .cell-content{justify-content:flex-end}@media (max-width:768px){:host{color:var(--snice-color-text,rgb(23 23 23));font-size:.75rem;line-height:1rem}.status-dot{width:.375rem;height:.375rem}}@media (prefers-reduced-motion:reduce){.status-dot--online::after{animation:none}}@media print{.status-dot{border:1px solid var(--snice-color-text,rgb(23 23 23))}.status-dot--online{background-color:var(--snice-color-surface,rgb(255 255 255));border-color:var(--snice-color-success,rgb(22 163 74))}.status-dot--offline{background-color:var(--snice-color-surface,rgb(255 255 255));border-color:var(--snice-color-neutral,rgb(107 114 128))}.status-dot--busy{background-color:var(--snice-color-surface,rgb(255 255 255));border-color:var(--snice-color-danger,rgb(220 38 38))}.status-dot--away{background-color:var(--snice-color-surface,rgb(255 255 255));border-color:var(--snice-color-warning,rgb(234 179 8))}}";
|
|
7132
7546
|
|
|
7133
7547
|
(() => {
|
|
7134
7548
|
let _classDecorators = [snice.element('snice-cell-status')];
|
|
@@ -7268,7 +7682,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
7268
7682
|
return _classThis;
|
|
7269
7683
|
})();
|
|
7270
7684
|
|
|
7271
|
-
var cssContent$9 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;min-width:6.25rem;flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:6.25rem;min-height:1.25rem}.actions-container{display:inline-flex;gap:.5rem;align-items:center}.action-button{display:inline-flex;align-items:center;gap:.25rem;padding:.25rem .5rem;border:1px solid transparent;border-radius:.25rem;background:0 0;cursor:pointer;transition:.2s;font-size:.875rem;line-height:1}.action-button:hover{background:var(--snice-color-surface-hover,rgb(243 244 246))}.action-button:focus-visible{outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:2px}.action-button:active{transform:scale(.95)}.action-button:disabled{opacity:.5;cursor:not-allowed}.action-button:disabled:hover{background:0 0}.action-button--primary{color:var(--snice-color-primary,rgb(37 99 235))}.action-button--primary:hover:not(:disabled){background:rgb(37 99 235 / .1)}.action-button--secondary{color:var(--snice-color-text,rgb(23 23 23))}.action-button--danger{color:var(--snice-color-danger,rgb(220 38 38))}.action-button--danger:hover:not(:disabled){background:rgb(220 38 38 / .1)}.action-button--success{color:var(--snice-color-success,rgb(22 163 74))}.action-button--success:hover:not(:disabled){background:rgb(22 163 74 / .1)}.action-icon{display:inline-flex;align-items:center;font-size:1rem}img.action-icon{width:1rem;height:1rem;object-fit:contain}.action-icon svg{width:1rem;height:1rem}.action-label{font-weight:var(--snice-font-weight-medium,500)}:host([align=left]){justify-content:flex-start}:host([align=center]){justify-content:center}:host([align=right]){justify-content:flex-end}@media (max-width:768px){.action-button{padding:.25rem}.action-label{display:none}}@media print{.actions-container{display:none}}";
|
|
7685
|
+
var cssContent$9 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;min-width:var(--snice-table-cell-min-width,6.25rem);flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:var(--snice-table-cell-min-width,6.25rem);min-height:1.25rem}.actions-container{display:inline-flex;gap:.5rem;align-items:center}.action-button{display:inline-flex;align-items:center;gap:.25rem;padding:.25rem .5rem;border:1px solid transparent;border-radius:.25rem;background:0 0;cursor:pointer;transition:.2s;font-size:.875rem;line-height:1}.action-button:hover{background:var(--snice-color-surface-hover,rgb(243 244 246))}.action-button:focus-visible{outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:2px}.action-button:active{transform:scale(.95)}.action-button:disabled{opacity:.5;cursor:not-allowed}.action-button:disabled:hover{background:0 0}.action-button--primary{color:var(--snice-color-primary,rgb(37 99 235))}.action-button--primary:hover:not(:disabled){background:rgb(37 99 235 / .1)}.action-button--secondary{color:var(--snice-color-text,rgb(23 23 23))}.action-button--danger{color:var(--snice-color-danger,rgb(220 38 38))}.action-button--danger:hover:not(:disabled){background:rgb(220 38 38 / .1)}.action-button--success{color:var(--snice-color-success,rgb(22 163 74))}.action-button--success:hover:not(:disabled){background:rgb(22 163 74 / .1)}.action-icon{display:inline-flex;align-items:center;font-size:1rem}img.action-icon{width:1rem;height:1rem;object-fit:contain}.action-icon svg{width:1rem;height:1rem}.action-label{font-weight:var(--snice-font-weight-medium,500)}:host([align=left]){justify-content:flex-start}:host([align=center]){justify-content:center}:host([align=right]){justify-content:flex-end}@media (max-width:768px){.action-button{padding:.25rem}.action-label{display:none}}@media print{.actions-container{display:none}}";
|
|
7272
7686
|
|
|
7273
7687
|
(() => {
|
|
7274
7688
|
let _classDecorators = [snice.element('snice-cell-actions')];
|
|
@@ -7406,7 +7820,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
7406
7820
|
return _classThis;
|
|
7407
7821
|
})();
|
|
7408
7822
|
|
|
7409
|
-
var cssContent$8 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;min-width:6.25rem;flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:6.25rem;min-height:1.25rem}.cell-link{display:inline-flex;align-items:center;gap:.25rem;color:var(--snice-color-primary,rgb(37 99 235));text-decoration:none;transition:color .2s}.cell-link:hover{color:var(--snice-color-primary-hover,rgb(29 78 216));text-decoration:underline}.cell-link:focus-visible{outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:2px;border-radius:var(--snice-border-radius-sm,.125rem)}.cell-link:visited{color:var(--snice-color-text-secondary,rgb(107 114 128))}.link-icon{display:inline-flex;align-items:center;font-size:1rem}img.link-icon{width:1rem;height:1rem;object-fit:contain}.link-icon svg{width:1rem;height:1rem}.link-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.external-icon{display:inline-flex;align-items:center;font-size:.75rem;opacity:.7}:host([align=left]){text-align:left;justify-content:flex-start}:host([align=center]){text-align:center;justify-content:center}:host([align=right]){text-align:right;justify-content:flex-end}@media (max-width:768px){:host{color:var(--snice-color-text,rgb(23 23 23));font-size:.75rem;line-height:1rem}}@media print{.cell-link{text-decoration:underline}.cell-link::after{content:\" (\" attr(href) \")\";font-size:.75rem;color:var(--snice-color-text-secondary,rgb(82 82 82))}}";
|
|
7823
|
+
var cssContent$8 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;min-width:var(--snice-table-cell-min-width,6.25rem);flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:var(--snice-table-cell-min-width,6.25rem);min-height:1.25rem}.cell-link{display:inline-flex;align-items:center;gap:.25rem;color:var(--snice-color-primary,rgb(37 99 235));text-decoration:none;transition:color .2s}.cell-link:hover{color:var(--snice-color-primary-hover,rgb(29 78 216));text-decoration:underline}.cell-link:focus-visible{outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:2px;border-radius:var(--snice-border-radius-sm,.125rem)}.cell-link:visited{color:var(--snice-color-text-secondary,rgb(107 114 128))}.link-icon{display:inline-flex;align-items:center;font-size:1rem}img.link-icon{width:1rem;height:1rem;object-fit:contain}.link-icon svg{width:1rem;height:1rem}.link-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.external-icon{display:inline-flex;align-items:center;font-size:.75rem;opacity:.7}:host([align=left]){text-align:left;justify-content:flex-start}:host([align=center]){text-align:center;justify-content:center}:host([align=right]){text-align:right;justify-content:flex-end}@media (max-width:768px){:host{color:var(--snice-color-text,rgb(23 23 23));font-size:.75rem;line-height:1rem}}@media print{.cell-link{text-decoration:underline}.cell-link::after{content:\" (\" attr(href) \")\";font-size:.75rem;color:var(--snice-color-text-secondary,rgb(82 82 82))}}";
|
|
7410
7824
|
|
|
7411
7825
|
(() => {
|
|
7412
7826
|
let _classDecorators = [snice.element('snice-cell-link')];
|
|
@@ -7546,7 +7960,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
7546
7960
|
return _classThis;
|
|
7547
7961
|
})();
|
|
7548
7962
|
|
|
7549
|
-
var cssContent$7 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;min-width:6.25rem;flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:6.25rem;min-height:1.25rem;display:flex;align-items:center;gap:.375rem}.email-icon{display:inline-flex;align-items:center;font-size:.875rem;opacity:.7;flex-shrink:0}.email-link{color:var(--snice-color-primary,rgb(37 99 235));text-decoration:none;transition:color .2s;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.email-link:hover{color:var(--snice-color-primary-hover,rgb(29 78 216));text-decoration:underline}.email-link:focus-visible{outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:2px;border-radius:var(--snice-border-radius-sm,.125rem)}.email-link:visited{color:var(--snice-color-text-secondary,rgb(107 114 128))}.email-empty{color:var(--snice-color-text-secondary,rgb(82 82 82));font-style:italic}:host([align=left]) .cell-content{justify-content:flex-start}:host([align=center]) .cell-content{justify-content:center}:host([align=right]) .cell-content{justify-content:flex-end}@media (max-width:768px){:host{color:var(--snice-color-text,rgb(23 23 23));font-size:.75rem;line-height:1rem}.email-icon{font-size:.75rem}}@media print{.email-link{text-decoration:underline}.email-link::after{content:\" (\" attr(href) \")\";font-size:.75rem;color:var(--snice-color-text-secondary,rgb(82 82 82))}.email-icon{display:none}}";
|
|
7963
|
+
var cssContent$7 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;min-width:var(--snice-table-cell-min-width,6.25rem);flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:var(--snice-table-cell-min-width,6.25rem);min-height:1.25rem;display:flex;align-items:center;gap:.375rem}.email-icon{display:inline-flex;align-items:center;font-size:.875rem;opacity:.7;flex-shrink:0}.email-link{color:var(--snice-color-primary,rgb(37 99 235));text-decoration:none;transition:color .2s;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.email-link:hover{color:var(--snice-color-primary-hover,rgb(29 78 216));text-decoration:underline}.email-link:focus-visible{outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:2px;border-radius:var(--snice-border-radius-sm,.125rem)}.email-link:visited{color:var(--snice-color-text-secondary,rgb(107 114 128))}.email-empty{color:var(--snice-color-text-secondary,rgb(82 82 82));font-style:italic}:host([align=left]) .cell-content{justify-content:flex-start}:host([align=center]) .cell-content{justify-content:center}:host([align=right]) .cell-content{justify-content:flex-end}@media (max-width:768px){:host{color:var(--snice-color-text,rgb(23 23 23));font-size:.75rem;line-height:1rem}.email-icon{font-size:.75rem}}@media print{.email-link{text-decoration:underline}.email-link::after{content:\" (\" attr(href) \")\";font-size:.75rem;color:var(--snice-color-text-secondary,rgb(82 82 82))}.email-icon{display:none}}";
|
|
7550
7964
|
|
|
7551
7965
|
(() => {
|
|
7552
7966
|
let _classDecorators = [snice.element('snice-cell-email')];
|
|
@@ -7667,7 +8081,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
7667
8081
|
return _classThis;
|
|
7668
8082
|
})();
|
|
7669
8083
|
|
|
7670
|
-
var cssContent$6 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;height:2.5rem;min-width:6.25rem;flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:6.25rem;min-height:1.25rem;display:flex;align-items:center;gap:.375rem}.phone-icon{display:inline-flex;align-items:center;font-size:.875rem;opacity:.7;flex-shrink:0}.phone-link{color:var(--snice-color-primary,rgb(37 99 235));text-decoration:none;transition:color .2s;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-variant-numeric:tabular-nums}.phone-link:hover{color:var(--snice-color-primary-hover,rgb(29 78 216));text-decoration:underline}.phone-link:focus-visible{outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:2px;border-radius:var(--snice-border-radius-sm,.125rem)}.phone-link:visited{color:var(--snice-color-text-secondary,rgb(107 114 128))}.phone-empty{color:var(--snice-color-text-secondary,rgb(82 82 82));font-style:italic}:host([align=left]) .cell-content{justify-content:flex-start}:host([align=center]) .cell-content{justify-content:center}:host([align=right]) .cell-content{justify-content:flex-end}@media (max-width:768px){:host{color:var(--snice-color-text,rgb(23 23 23));font-size:.75rem;line-height:1rem}.phone-icon{font-size:.75rem}}@media print{.phone-link{text-decoration:underline}.phone-link::after{content:\" (\" attr(href) \")\";font-size:.75rem;color:var(--snice-color-text-secondary,rgb(82 82 82))}.phone-icon{display:none}}";
|
|
8084
|
+
var cssContent$6 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;height:2.5rem;min-width:var(--snice-table-cell-min-width,6.25rem);flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:var(--snice-table-cell-min-width,6.25rem);min-height:1.25rem;display:flex;align-items:center;gap:.375rem}.phone-icon{display:inline-flex;align-items:center;font-size:.875rem;opacity:.7;flex-shrink:0}.phone-link{color:var(--snice-color-primary,rgb(37 99 235));text-decoration:none;transition:color .2s;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-variant-numeric:tabular-nums}.phone-link:hover{color:var(--snice-color-primary-hover,rgb(29 78 216));text-decoration:underline}.phone-link:focus-visible{outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:2px;border-radius:var(--snice-border-radius-sm,.125rem)}.phone-link:visited{color:var(--snice-color-text-secondary,rgb(107 114 128))}.phone-empty{color:var(--snice-color-text-secondary,rgb(82 82 82));font-style:italic}:host([align=left]) .cell-content{justify-content:flex-start}:host([align=center]) .cell-content{justify-content:center}:host([align=right]) .cell-content{justify-content:flex-end}@media (max-width:768px){:host{color:var(--snice-color-text,rgb(23 23 23));font-size:.75rem;line-height:1rem}.phone-icon{font-size:.75rem}}@media print{.phone-link{text-decoration:underline}.phone-link::after{content:\" (\" attr(href) \")\";font-size:.75rem;color:var(--snice-color-text-secondary,rgb(82 82 82))}.phone-icon{display:none}}";
|
|
7671
8085
|
|
|
7672
8086
|
(() => {
|
|
7673
8087
|
let _classDecorators = [snice.element('snice-cell-phone')];
|
|
@@ -7816,7 +8230,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
7816
8230
|
return _classThis;
|
|
7817
8231
|
})();
|
|
7818
8232
|
|
|
7819
|
-
var cssContent$5 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;min-width:6.25rem;flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:6.25rem;min-height:1.25rem;display:flex;align-items:center;gap:.5rem}.color-swatch{border:1px solid var(--snice-color-border,rgb(226 226 226));border-radius:.25rem;flex-shrink:0;box-shadow:var(--snice-shadow-xs,0 1px 2px 0 rgb(0 0 0 / .03))}.color-swatch--small{width:1rem;height:1rem}.color-swatch--medium{width:1.5rem;height:1.5rem}.color-swatch--large{width:2rem;height:2rem}.color-text{font-family:monospace;font-size:.875rem;color:var(--snice-color-text,rgb(23 23 23));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host([align=left]) .cell-content{justify-content:flex-start}:host([align=center]) .cell-content{justify-content:center}:host([align=right]) .cell-content{justify-content:flex-end}@media (max-width:768px){:host{color:var(--snice-color-text,rgb(23 23 23));font-size:.75rem;line-height:1rem}.color-swatch--small{width:.875rem;height:.875rem}.color-swatch--medium{width:1.25rem;height:1.25rem}.color-swatch--large{width:1.75rem;height:1.75rem}.color-text{font-size:.75rem}}@media print{.color-swatch{border:2px solid var(--snice-color-text,rgb(23 23 23))}.color-text{font-size:.75rem}}";
|
|
8233
|
+
var cssContent$5 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;min-width:var(--snice-table-cell-min-width,6.25rem);flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:var(--snice-table-cell-min-width,6.25rem);min-height:1.25rem;display:flex;align-items:center;gap:.5rem}.color-swatch{border:1px solid var(--snice-color-border,rgb(226 226 226));border-radius:.25rem;flex-shrink:0;box-shadow:var(--snice-shadow-xs,0 1px 2px 0 rgb(0 0 0 / .03))}.color-swatch--small{width:1rem;height:1rem}.color-swatch--medium{width:1.5rem;height:1.5rem}.color-swatch--large{width:2rem;height:2rem}.color-text{font-family:monospace;font-size:.875rem;color:var(--snice-color-text,rgb(23 23 23));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host([align=left]) .cell-content{justify-content:flex-start}:host([align=center]) .cell-content{justify-content:center}:host([align=right]) .cell-content{justify-content:flex-end}@media (max-width:768px){:host{color:var(--snice-color-text,rgb(23 23 23));font-size:.75rem;line-height:1rem}.color-swatch--small{width:.875rem;height:.875rem}.color-swatch--medium{width:1.25rem;height:1.25rem}.color-swatch--large{width:1.75rem;height:1.75rem}.color-text{font-size:.75rem}}@media print{.color-swatch{border:2px solid var(--snice-color-text,rgb(23 23 23))}.color-text{font-size:.75rem}}";
|
|
7820
8234
|
|
|
7821
8235
|
(() => {
|
|
7822
8236
|
let _classDecorators = [snice.element('snice-cell-color')];
|
|
@@ -8000,7 +8414,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
8000
8414
|
return _classThis;
|
|
8001
8415
|
})();
|
|
8002
8416
|
|
|
8003
|
-
var cssContent$4 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;height:2.5rem;min-width:6.25rem;flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:6.25rem;min-height:1.25rem;display:flex;align-items:center;gap:.375rem}.location-icon{display:inline-flex;align-items:center;font-size:.875rem;opacity:.7;flex-shrink:0}.location-link{color:var(--snice-color-primary,rgb(37 99 235));text-decoration:none;transition:color .2s;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.location-link:hover{color:var(--snice-color-primary-hover,rgb(29 78 216));text-decoration:underline}.location-link:focus-visible{outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:2px;border-radius:var(--snice-border-radius-sm,.125rem)}.location-link:visited{color:var(--snice-color-text-secondary,rgb(107 114 128))}.location-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--snice-color-text,rgb(23 23 23))}.location-empty{color:var(--snice-color-text-secondary,rgb(82 82 82));font-style:italic}:host([align=left]) .cell-content{justify-content:flex-start}:host([align=center]) .cell-content{justify-content:center}:host([align=right]) .cell-content{justify-content:flex-end}@media (max-width:768px){:host{color:var(--snice-color-text,rgb(23 23 23));font-size:.75rem;line-height:1rem}.location-icon{font-size:.75rem}}@media print{.location-link{text-decoration:underline}.location-link::after{content:\" (\" attr(href) \")\";font-size:.75rem;color:var(--snice-color-text-secondary,rgb(82 82 82))}.location-icon{display:none}}";
|
|
8417
|
+
var cssContent$4 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;height:2.5rem;min-width:var(--snice-table-cell-min-width,6.25rem);flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:var(--snice-table-cell-min-width,6.25rem);min-height:1.25rem;display:flex;align-items:center;gap:.375rem}.location-icon{display:inline-flex;align-items:center;font-size:.875rem;opacity:.7;flex-shrink:0}.location-link{color:var(--snice-color-primary,rgb(37 99 235));text-decoration:none;transition:color .2s;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.location-link:hover{color:var(--snice-color-primary-hover,rgb(29 78 216));text-decoration:underline}.location-link:focus-visible{outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:2px;border-radius:var(--snice-border-radius-sm,.125rem)}.location-link:visited{color:var(--snice-color-text-secondary,rgb(107 114 128))}.location-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--snice-color-text,rgb(23 23 23))}.location-empty{color:var(--snice-color-text-secondary,rgb(82 82 82));font-style:italic}:host([align=left]) .cell-content{justify-content:flex-start}:host([align=center]) .cell-content{justify-content:center}:host([align=right]) .cell-content{justify-content:flex-end}@media (max-width:768px){:host{color:var(--snice-color-text,rgb(23 23 23));font-size:.75rem;line-height:1rem}.location-icon{font-size:.75rem}}@media print{.location-link{text-decoration:underline}.location-link::after{content:\" (\" attr(href) \")\";font-size:.75rem;color:var(--snice-color-text-secondary,rgb(82 82 82))}.location-icon{display:none}}";
|
|
8004
8418
|
|
|
8005
8419
|
(() => {
|
|
8006
8420
|
let _classDecorators = [snice.element('snice-cell-location')];
|
|
@@ -8183,7 +8597,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
8183
8597
|
return _classThis;
|
|
8184
8598
|
})();
|
|
8185
8599
|
|
|
8186
|
-
var cssContent$3 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;height:auto;min-width:6.25rem;flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:6.25rem;min-height:1.25rem;display:flex;align-items:flex-start;gap:.5rem;padding:.25rem 0}.json-toggle{background:0 0;border:none;color:var(--snice-color-text-secondary,rgb(82 82 82));cursor:pointer;padding:.125rem .25rem;font-size:.75rem;line-height:1;border-radius:.25rem;transition:background-color .2s;flex-shrink:0;display:inline-flex;align-items:center;justify-content:center}.json-toggle svg{width:.75rem;height:.75rem}.json-toggle:hover{background-color:var(--snice-color-surface-hover,rgb(245 245 245))}.json-toggle:focus-visible{outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:2px}.json-viewer{font-family:'Courier New',Consolas,monospace;font-size:.8125rem;overflow:hidden;flex:1}.json-viewer--collapsed{white-space:nowrap;text-overflow:ellipsis}.json-viewer--expanded{white-space:pre-wrap;word-break:break-word}.json-preview{color:var(--snice-color-text-secondary,rgb(82 82 82));font-style:italic}.json-object{padding-left:1rem}.json-line{margin:.125rem 0}.json-key{color:var(--snice-color-primary,rgb(37 99 235));font-weight:var(--snice-font-weight-medium,500);margin-right:.25rem}.json-string{color:var(--snice-color-success,rgb(22 163 74))}.json-number{color:var(--snice-color-primary,rgb(59 130 246))}.json-boolean{color:var(--snice-color-warning,rgb(234 179 8))}.json-empty,.json-null,.json-undefined{color:var(--snice-color-text-secondary,rgb(82 82 82));font-style:italic}:host([align=left]) .cell-content{justify-content:flex-start}:host([align=center]) .cell-content{justify-content:center}:host([align=right]) .cell-content{justify-content:flex-end}@media (max-width:768px){:host{color:var(--snice-color-text,rgb(23 23 23));font-size:.75rem;line-height:1rem}.json-viewer{font-size:.75rem}.json-toggle{font-size:.625rem}}@media print{.json-toggle{display:none}.json-viewer--collapsed{white-space:normal}.json-viewer{font-size:.625rem}}";
|
|
8600
|
+
var cssContent$3 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;height:auto;min-width:var(--snice-table-cell-min-width,6.25rem);flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:var(--snice-table-cell-min-width,6.25rem);min-height:1.25rem;display:flex;align-items:flex-start;gap:.5rem;padding:.25rem 0}.json-toggle{background:0 0;border:none;color:var(--snice-color-text-secondary,rgb(82 82 82));cursor:pointer;padding:.125rem .25rem;font-size:.75rem;line-height:1;border-radius:.25rem;transition:background-color .2s;flex-shrink:0;display:inline-flex;align-items:center;justify-content:center}.json-toggle svg{width:.75rem;height:.75rem}.json-toggle:hover{background-color:var(--snice-color-surface-hover,rgb(245 245 245))}.json-toggle:focus-visible{outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:2px}.json-viewer{font-family:'Courier New',Consolas,monospace;font-size:.8125rem;overflow:hidden;flex:1}.json-viewer--collapsed{white-space:nowrap;text-overflow:ellipsis}.json-viewer--expanded{white-space:pre-wrap;word-break:break-word}.json-preview{color:var(--snice-color-text-secondary,rgb(82 82 82));font-style:italic}.json-object{padding-left:1rem}.json-line{margin:.125rem 0}.json-key{color:var(--snice-color-primary,rgb(37 99 235));font-weight:var(--snice-font-weight-medium,500);margin-right:.25rem}.json-string{color:var(--snice-color-success,rgb(22 163 74))}.json-number{color:var(--snice-color-primary,rgb(59 130 246))}.json-boolean{color:var(--snice-color-warning,rgb(234 179 8))}.json-empty,.json-null,.json-undefined{color:var(--snice-color-text-secondary,rgb(82 82 82));font-style:italic}:host([align=left]) .cell-content{justify-content:flex-start}:host([align=center]) .cell-content{justify-content:center}:host([align=right]) .cell-content{justify-content:flex-end}@media (max-width:768px){:host{color:var(--snice-color-text,rgb(23 23 23));font-size:.75rem;line-height:1rem}.json-viewer{font-size:.75rem}.json-toggle{font-size:.625rem}}@media print{.json-toggle{display:none}.json-viewer--collapsed{white-space:normal}.json-viewer{font-size:.625rem}}";
|
|
8187
8601
|
|
|
8188
8602
|
(() => {
|
|
8189
8603
|
let _classDecorators = [snice.element('snice-cell-json')];
|
|
@@ -8604,7 +9018,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
8604
9018
|
return _classThis;
|
|
8605
9019
|
})();
|
|
8606
9020
|
|
|
8607
|
-
var cssContent$2 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;min-width:6.25rem;flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:6.25rem;min-height:1.25rem;display:flex;align-items:center}.cell-image{display:block;object-fit:cover;background-color:rgb(243 244 246)}.image--small{width:1.5rem;height:1.5rem}.image--medium{width:2rem;height:2rem}.image--large{width:3rem;height:3rem}.image--rounded{border-radius:.25rem}.image--square{border-radius:0}.image--circle{border-radius:50%}.cell-image--placeholder{background:linear-gradient(135deg,rgb(229 231 235) 0,rgb(209 213 219) 100%);border:1px solid rgb(209 213 219);position:relative}.cell-image__placeholder-icon{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:inline-flex;align-items:center;justify-content:center;color:var(--snice-color-text-tertiary,rgb(115 115 115));opacity:.55}.cell-image__placeholder-icon svg{width:50%;max-width:1.25rem;height:auto}:host([align=left]) .cell-content{justify-content:flex-start}:host([align=center]) .cell-content{justify-content:center}:host([align=right]) .cell-content{justify-content:flex-end}:host(.loading) .cell-image{opacity:.6;animation:1.5s ease-in-out infinite image-pulse}@keyframes image-pulse{0%,100%{opacity:.6}50%{opacity:.3}}@media (max-width:768px){.image--small{width:1.25rem;height:1.25rem}.image--medium{width:1.75rem;height:1.75rem}.image--large{width:2.5rem;height:2.5rem}}@media (prefers-reduced-motion:reduce){:host(.loading) .cell-image{animation:none}}@media print{.cell-image{max-width:2rem;max-height:2rem}}";
|
|
9021
|
+
var cssContent$2 = ":host([hidden]){display:none}:host{color:var(--snice-color-text,rgb(23 23 23));display:flex;align-items:center;min-height:2.5rem;min-width:var(--snice-table-cell-min-width,6.25rem);flex:1;font-size:.875rem;line-height:1.25rem}.cell-content{width:100%;min-width:var(--snice-table-cell-min-width,6.25rem);min-height:1.25rem;display:flex;align-items:center}.cell-image{display:block;object-fit:cover;background-color:rgb(243 244 246)}.image--small{width:1.5rem;height:1.5rem}.image--medium{width:2rem;height:2rem}.image--large{width:3rem;height:3rem}.image--rounded{border-radius:.25rem}.image--square{border-radius:0}.image--circle{border-radius:50%}.cell-image--placeholder{background:linear-gradient(135deg,rgb(229 231 235) 0,rgb(209 213 219) 100%);border:1px solid rgb(209 213 219);position:relative}.cell-image__placeholder-icon{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:inline-flex;align-items:center;justify-content:center;color:var(--snice-color-text-tertiary,rgb(115 115 115));opacity:.55}.cell-image__placeholder-icon svg{width:50%;max-width:1.25rem;height:auto}:host([align=left]) .cell-content{justify-content:flex-start}:host([align=center]) .cell-content{justify-content:center}:host([align=right]) .cell-content{justify-content:flex-end}:host(.loading) .cell-image{opacity:.6;animation:1.5s ease-in-out infinite image-pulse}@keyframes image-pulse{0%,100%{opacity:.6}50%{opacity:.3}}@media (max-width:768px){.image--small{width:1.25rem;height:1.25rem}.image--medium{width:1.75rem;height:1.75rem}.image--large{width:2.5rem;height:2.5rem}}@media (prefers-reduced-motion:reduce){:host(.loading) .cell-image{animation:none}}@media print{.cell-image{max-width:2rem;max-height:2rem}}";
|
|
8608
9022
|
|
|
8609
9023
|
(() => {
|
|
8610
9024
|
let _classDecorators = [snice.element('snice-cell-image')];
|
|
@@ -9308,7 +9722,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
9308
9722
|
return _classThis;
|
|
9309
9723
|
})();
|
|
9310
9724
|
|
|
9311
|
-
var cssContent$1 = ":host([hidden]){display:none}:host{display:block;width:100%;min-height:2.5rem}.row-container{display:flex;align-items:stretch;width:100%;min-height:2.5rem;background:0 0;color:var(--snice-color-text,rgb(23 23 23));border-bottom:1px solid var(--snice-color-border,rgb(226 226 226));transition:background-color .15s}.cell{display:flex;align-items:center;border-right:1px solid var(--snice-color-border,rgb(226 226 226));min-height:2.5rem;min-width:6.25rem;flex:1;word-wrap:break-word;overflow:hidden;color:var(--snice-color-text,rgb(23 23 23))}.cell:last-child{border-right:none}.cell--checkbox{width:3rem;min-width:3rem;max-width:3rem;padding:.75rem;justify-content:center}.row-checkbox{width:1rem;height:1rem;cursor:pointer;margin:0;display:block;accent-color:var(--snice-color-primary,rgb(37 99 235))}.cells-container{display:flex;flex:1;align-items:stretch}.cell--text{text-align:left}.cell--accounting,.cell--currency,.cell--fraction,.cell--number,.cell--percent,.cell--scientific{text-align:right;font-variant-numeric:tabular-nums}.cell--date{min-width:8rem;font-variant-numeric:tabular-nums}.cell--boolean{text-align:center;font-size:1rem}.cell--rating{text-align:center;min-width:6rem}.cell--progress{min-width:8rem}.cell--sparkline{text-align:center;min-width:6rem}.cell--duration,.cell--filesize{text-align:right;font-variant-numeric:tabular-nums}:host(.row--hoverable) .row-container:hover{background-color:var(--snice-color-surface-hover,rgb(243 244 246))}:host(.row--clickable) .row-container{cursor:pointer}:host(.row--clickable) .row-container:hover{background-color:var(--snice-color-surface-hover,rgb(243 244 246))}:host(.row--selected) .row-container{background-color:var(--snice-color-primary-subtle,rgb(239 246 255));box-shadow:inset 2px 0 0 0 var(--snice-color-primary,rgb(37 99 235))}:host(.row--selected.row--hoverable) .row-container:hover{background-color:var(--snice-color-primary-subtle-hover,var(--snice-color-primary-subtle,rgb(219 234 254)))}:host(.row--highlighted) .row-container{background-color:var(--snice-color-warning-subtle,rgb(254 249 195))!important;animation:2s ease-out row-highlight-fade}@keyframes row-highlight-fade{0%{background-color:var(--snice-color-warning-subtle-hover,rgb(254 252 232))}100%{background-color:var(--snice-color-warning-subtle,rgb(254 249 195))}}:host(:focus) .row-container{outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:-2px}:host(.row--selectable){tabindex:0}:host(.row--striped) .row-container{background-color:var(--snice-color-
|
|
9725
|
+
var cssContent$1 = ":host([hidden]){display:none}:host{display:block;width:100%;min-height:2.5rem}.row-container{display:flex;align-items:stretch;width:100%;min-height:2.5rem;background:0 0;color:var(--snice-color-text,rgb(23 23 23));border-bottom:1px solid var(--snice-color-border,rgb(226 226 226));transition:background-color .15s}.cell{display:flex;align-items:center;border-right:1px solid var(--snice-color-border,rgb(226 226 226));min-height:2.5rem;min-width:6.25rem;flex:1;word-wrap:break-word;overflow:hidden;color:var(--snice-color-text,rgb(23 23 23))}.cell:last-child{border-right:none}.cell--checkbox{width:3rem;min-width:3rem;max-width:3rem;padding:.75rem;justify-content:center}.row-checkbox{width:1rem;height:1rem;cursor:pointer;margin:0;display:block;accent-color:var(--snice-color-primary,rgb(37 99 235))}.cells-container{display:flex;flex:1;align-items:stretch}.cell--text{text-align:left}.cell--accounting,.cell--currency,.cell--fraction,.cell--number,.cell--percent,.cell--scientific{text-align:right;font-variant-numeric:tabular-nums}.cell--date{min-width:8rem;font-variant-numeric:tabular-nums}.cell--boolean{text-align:center;font-size:1rem}.cell--rating{text-align:center;min-width:6rem}.cell--progress{min-width:8rem}.cell--sparkline{text-align:center;min-width:6rem}.cell--duration,.cell--filesize{text-align:right;font-variant-numeric:tabular-nums}:host(.row--hoverable) .row-container:hover{background-color:var(--snice-color-surface-hover,rgb(243 244 246))}:host(.row--clickable) .row-container{cursor:pointer}:host(.row--clickable) .row-container:hover{background-color:var(--snice-color-surface-hover,rgb(243 244 246))}:host(.row--selected) .row-container{background-color:var(--snice-color-primary-subtle,rgb(239 246 255));box-shadow:inset 2px 0 0 0 var(--snice-color-primary,rgb(37 99 235))}:host(.row--selected.row--hoverable) .row-container:hover{background-color:var(--snice-color-primary-subtle-hover,var(--snice-color-primary-subtle,rgb(219 234 254)))}:host(.row--highlighted) .row-container{background-color:var(--snice-color-warning-subtle,rgb(254 249 195))!important;animation:2s ease-out row-highlight-fade}@keyframes row-highlight-fade{0%{background-color:var(--snice-color-warning-subtle-hover,rgb(254 252 232))}100%{background-color:var(--snice-color-warning-subtle,rgb(254 249 195))}}:host(:focus) .row-container{outline:var(--snice-focus-ring-width,2px) solid var(--snice-focus-ring-color,rgb(37 99 235 / .5));outline-offset:-2px}:host(.row--selectable){tabindex:0}:host(.row--striped) .row-container{background-color:var(--snice-table-stripe-bg,var(--snice-color-overlay-stripe,hsl(0 0% 0% / .02)))}:host(.row--bordered) .row-container{border-bottom:1px solid var(--snice-color-border,rgb(226 226 226))}:host(.row--small) .cell{padding:.5rem .75rem;min-height:2rem;font-size:.75rem}:host(.row--small) .row-container{min-height:2rem}:host(.row--large) .cell{padding:1rem 1.25rem;min-height:3.5rem;font-size:1rem}:host(.row--large) .row-container{min-height:3.5rem}@media (max-width:768px){.cell{padding:.5rem .75rem;font-size:.875rem}.cell--checkbox{width:2.5rem;min-width:2.5rem;max-width:2.5rem;padding:.5rem}.row-checkbox{width:.875rem;height:.875rem}}:host(.row--loading) .row-container{opacity:.6;pointer-events:none}:host(.row--loading) .row-container::after{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(90deg,transparent,var(--snice-color-surface,rgb(255 255 255 / .6)),transparent);animation:1.5s infinite loading-shimmer}@keyframes loading-shimmer{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}}@media (prefers-reduced-motion:reduce){.row-container{transition:none}:host(.row--highlighted) .row-container{animation:none;background-color:var(--snice-color-warning-subtle,rgb(254 249 195))!important}:host(.row--loading) .row-container::after{animation:none}}@media (prefers-contrast:high){.cell{border-right:1px solid}.row-container{border-bottom:1px solid}:host(.row--selected) .row-container{border:2px solid}}";
|
|
9312
9726
|
|
|
9313
9727
|
(() => {
|
|
9314
9728
|
let _classDecorators = [snice.element('snice-row')];
|
|
@@ -9561,19 +9975,12 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
9561
9975
|
<div class="cell cell--${column.type}" part="cell" style="${this.getCellStyles(column)}">
|
|
9562
9976
|
${snice.unsafeHTML(`<${cellComponent}
|
|
9563
9977
|
type="${column.type}"
|
|
9564
|
-
align="${column.align ||
|
|
9978
|
+
align="${column.align || defaultCellAlign(column.type)}"
|
|
9565
9979
|
data-column-index="${columnIndex}"
|
|
9566
9980
|
></${cellComponent}>`)}
|
|
9567
9981
|
</div>
|
|
9568
9982
|
`;
|
|
9569
9983
|
}
|
|
9570
|
-
getDefaultAlign(type) {
|
|
9571
|
-
if (['number', 'currency', 'percent', 'percentage', 'duration', 'filesize', 'accounting', 'scientific', 'fraction'].includes(type || ''))
|
|
9572
|
-
return 'right';
|
|
9573
|
-
if (['boolean', 'rating', 'image'].includes(type || ''))
|
|
9574
|
-
return 'center';
|
|
9575
|
-
return 'left';
|
|
9576
|
-
}
|
|
9577
9984
|
applyCellPresentation(wrapper, column, value) {
|
|
9578
9985
|
if (!wrapper)
|
|
9579
9986
|
return;
|
|
@@ -9806,11 +10213,24 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
9806
10213
|
window.removeEventListener('resize', this.scrollHandler);
|
|
9807
10214
|
cancelAnimationFrame(this.rafId);
|
|
9808
10215
|
}
|
|
9809
|
-
|
|
10216
|
+
/**
|
|
10217
|
+
* Publish a new row count. `repaint` false updates the model only — the
|
|
10218
|
+
* pending re-render is still armed (lastStartIndex is reset), so the caller's
|
|
10219
|
+
* own paint (scrollToIndex / refresh) performs it. Callers that resolve a
|
|
10220
|
+
* display index and then scroll MUST use that form: painting here runs
|
|
10221
|
+
* `afterRender`, and a host whose afterRender restores keyboard focus would
|
|
10222
|
+
* re-enter this method from inside its own update.
|
|
10223
|
+
*/
|
|
10224
|
+
setTotalRows(total, repaint = true) {
|
|
9810
10225
|
this.options.totalRows = total;
|
|
9811
10226
|
this.invalidateOffsets();
|
|
9812
10227
|
this.lastStartIndex = -1; // Force re-render
|
|
9813
|
-
|
|
10228
|
+
if (repaint)
|
|
10229
|
+
this.update();
|
|
10230
|
+
}
|
|
10231
|
+
/** Row count the virtualizer is currently windowing over. */
|
|
10232
|
+
getTotalRows() {
|
|
10233
|
+
return this.options.totalRows ?? 0;
|
|
9814
10234
|
}
|
|
9815
10235
|
/**
|
|
9816
10236
|
* Force a full re-render of the visible range.
|
|
@@ -9821,11 +10241,30 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
9821
10241
|
this.lastEndIndex = -1;
|
|
9822
10242
|
this.update();
|
|
9823
10243
|
}
|
|
10244
|
+
/**
|
|
10245
|
+
* The element that scrolls RIGHT NOW. A structural re-render of the host
|
|
10246
|
+
* swaps the node captured at `attach()`; keep the listener and every lookup
|
|
10247
|
+
* (notably the `<tbody>` we paint into) on the live one.
|
|
10248
|
+
*/
|
|
10249
|
+
resolveContainer() {
|
|
10250
|
+
const live = this.options.resolveScrollContainer?.() ?? null;
|
|
10251
|
+
const current = this.options.scrollContainer;
|
|
10252
|
+
if (live && live !== current) {
|
|
10253
|
+
current?.removeEventListener('scroll', this.scrollHandler);
|
|
10254
|
+
live.addEventListener('scroll', this.scrollHandler, { passive: true });
|
|
10255
|
+
this.options.scrollContainer = live;
|
|
10256
|
+
// The new container's tbody is empty — the range we believed painted is
|
|
10257
|
+
// not on screen anymore.
|
|
10258
|
+
this.lastStartIndex = -1;
|
|
10259
|
+
this.lastEndIndex = -1;
|
|
10260
|
+
}
|
|
10261
|
+
return this.options.scrollContainer ?? null;
|
|
10262
|
+
}
|
|
9824
10263
|
/**
|
|
9825
10264
|
* Calculate visible range and render if changed.
|
|
9826
10265
|
*/
|
|
9827
10266
|
update() {
|
|
9828
|
-
if (!this.enabled || !this.
|
|
10267
|
+
if (!this.enabled || !this.resolveContainer())
|
|
9829
10268
|
return;
|
|
9830
10269
|
const { bufferPx, totalRows, scrollContainer, renderRange } = this.options;
|
|
9831
10270
|
const scrollTop = scrollContainer.scrollTop;
|
|
@@ -9839,11 +10278,12 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
9839
10278
|
// Filtering, pagination, or collapsing a group can shrink the flattened
|
|
9840
10279
|
// model while the container is scrolled near the old end. Clamp before
|
|
9841
10280
|
// deriving indices so startIndex never sits beyond totalRows and blanks the
|
|
9842
|
-
// body.
|
|
9843
|
-
//
|
|
10281
|
+
// body. Only an offset that overruns the whole content is written back to
|
|
10282
|
+
// the element — a browser already clamps its own scrollTop, and rewriting a
|
|
10283
|
+
// deliberate offset (scrollToRow) would undo the caller's request.
|
|
9844
10284
|
const maxScrollTop = Math.max(0, totalHeight - viewportHeight);
|
|
9845
10285
|
const effectiveScrollTop = Math.min(Math.max(0, scrollTop), maxScrollTop);
|
|
9846
|
-
if (scrollTop
|
|
10286
|
+
if (scrollTop > totalHeight || scrollTop < 0)
|
|
9847
10287
|
scrollContainer.scrollTop = effectiveScrollTop;
|
|
9848
10288
|
// Calculate visible range with buffer
|
|
9849
10289
|
const startPx = Math.max(0, effectiveScrollTop - bufferPx);
|
|
@@ -9853,75 +10293,89 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
9853
10293
|
// Skip if range hasn't changed
|
|
9854
10294
|
if (startIndex === this.lastStartIndex && endIndex === this.lastEndIndex)
|
|
9855
10295
|
return;
|
|
9856
|
-
|
|
9857
|
-
|
|
10296
|
+
// Nothing to paint into yet (the host template has not committed its table,
|
|
10297
|
+
// or a structural branch flip is mid-flight). Return WITHOUT recording the
|
|
10298
|
+
// range: a dropped paint that looks like a completed one makes the guard
|
|
10299
|
+
// above suppress every retry, and then only a viewport-height change (a
|
|
10300
|
+
// window resize) ever repaints the rows.
|
|
10301
|
+
const tbody = scrollContainer.querySelector('tbody');
|
|
10302
|
+
if (!tbody)
|
|
10303
|
+
return;
|
|
9858
10304
|
// Update spacers
|
|
9859
10305
|
const topHeight = offsets[startIndex] || 0;
|
|
9860
10306
|
const bottomHeight = Math.max(0, totalHeight - (offsets[endIndex] || totalHeight));
|
|
9861
10307
|
this.topSpacer.style.height = `${topHeight}px`;
|
|
9862
10308
|
this.bottomSpacer.style.height = `${bottomHeight}px`;
|
|
10309
|
+
// Span the columns that exist, never more (see resolveColumnSpan).
|
|
10310
|
+
const columnSpan = Math.max(1, Math.round(this.options.resolveColumnSpan?.() ?? 1));
|
|
9863
10311
|
// Render the visible range
|
|
9864
10312
|
const fragment = renderRange(startIndex, endIndex);
|
|
9865
10313
|
// Build the tbody content
|
|
9866
|
-
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
|
|
9870
|
-
|
|
9871
|
-
|
|
9872
|
-
|
|
9873
|
-
|
|
9874
|
-
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
|
|
9886
|
-
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
|
|
9890
|
-
|
|
9891
|
-
|
|
9892
|
-
|
|
9893
|
-
|
|
9894
|
-
|
|
9895
|
-
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
10314
|
+
tbody.innerHTML = '';
|
|
10315
|
+
// Pinned-top rows render outside (above) the windowed range so they are
|
|
10316
|
+
// always present. Rebuilt every window recompute since tbody is wiped.
|
|
10317
|
+
const pinnedTop = this.options.renderPinnedTop?.();
|
|
10318
|
+
if (pinnedTop)
|
|
10319
|
+
tbody.appendChild(pinnedTop);
|
|
10320
|
+
if (topHeight > 0) {
|
|
10321
|
+
const topTd = document.createElement('td');
|
|
10322
|
+
topTd.colSpan = columnSpan;
|
|
10323
|
+
topTd.style.height = `${topHeight}px`;
|
|
10324
|
+
topTd.style.padding = '0';
|
|
10325
|
+
topTd.style.border = 'none';
|
|
10326
|
+
this.topSpacer.innerHTML = '';
|
|
10327
|
+
this.topSpacer.appendChild(topTd);
|
|
10328
|
+
tbody.appendChild(this.topSpacer);
|
|
10329
|
+
}
|
|
10330
|
+
tbody.appendChild(fragment);
|
|
10331
|
+
// Nothing to window over: hand the body back its empty/loading/error row
|
|
10332
|
+
// instead of leaving the user staring at a blank frame.
|
|
10333
|
+
if (totalRows === 0) {
|
|
10334
|
+
const emptyBody = this.options.renderEmptyBody?.();
|
|
10335
|
+
if (emptyBody)
|
|
10336
|
+
tbody.appendChild(emptyBody);
|
|
10337
|
+
}
|
|
10338
|
+
if (bottomHeight > 0) {
|
|
10339
|
+
const bottomTd = document.createElement('td');
|
|
10340
|
+
bottomTd.colSpan = columnSpan;
|
|
10341
|
+
bottomTd.style.height = `${bottomHeight}px`;
|
|
10342
|
+
bottomTd.style.padding = '0';
|
|
10343
|
+
bottomTd.style.border = 'none';
|
|
10344
|
+
this.bottomSpacer.innerHTML = '';
|
|
10345
|
+
this.bottomSpacer.appendChild(bottomTd);
|
|
10346
|
+
tbody.appendChild(this.bottomSpacer);
|
|
10347
|
+
}
|
|
10348
|
+
// Pinned-bottom rows render outside (below) the windowed range.
|
|
10349
|
+
const pinnedBottom = this.options.renderPinnedBottom?.();
|
|
10350
|
+
if (pinnedBottom)
|
|
10351
|
+
tbody.appendChild(pinnedBottom);
|
|
10352
|
+
// The window is on screen — only NOW does this range count as painted.
|
|
10353
|
+
// Committed before afterRender so a re-entrant update() from that hook
|
|
10354
|
+
// (keyboard focus restoration scrolls) sees the range it is looking at.
|
|
10355
|
+
this.lastStartIndex = startIndex;
|
|
10356
|
+
this.lastEndIndex = endIndex;
|
|
10357
|
+
this.options.afterRender?.();
|
|
9901
10358
|
}
|
|
9902
10359
|
/** Get the currently visible row index range */
|
|
9903
10360
|
getVisibleRange() {
|
|
9904
10361
|
return { start: this.lastStartIndex, end: this.lastEndIndex };
|
|
9905
10362
|
}
|
|
9906
|
-
/** Scroll to bring a specific row into view */
|
|
9907
|
-
scrollToRow(index) {
|
|
9908
|
-
if (!this.options.scrollContainer)
|
|
9909
|
-
return;
|
|
9910
|
-
const top = this.offsetForIndex(index);
|
|
9911
|
-
this.options.scrollContainer.scrollTop = top;
|
|
9912
|
-
}
|
|
9913
10363
|
/**
|
|
9914
|
-
* Scroll a
|
|
9915
|
-
*
|
|
9916
|
-
*
|
|
9917
|
-
*
|
|
10364
|
+
* Scroll a row into view AND synchronously render the window around it.
|
|
10365
|
+
* A programmatic `scrollTop` assignment fires no scroll event, so the window
|
|
10366
|
+
* has to be recomputed here — otherwise the viewport lands on bare spacer and
|
|
10367
|
+
* the requested row is never in the DOM.
|
|
9918
10368
|
*/
|
|
9919
|
-
|
|
10369
|
+
scrollToRow(index) {
|
|
9920
10370
|
if (!this.options.scrollContainer)
|
|
9921
10371
|
return;
|
|
9922
10372
|
this.options.scrollContainer.scrollTop = this.offsetForIndex(index);
|
|
9923
10373
|
this.refresh();
|
|
9924
10374
|
}
|
|
10375
|
+
/** Scroll to a logical (flattened display) row index. */
|
|
10376
|
+
scrollToIndex(index) {
|
|
10377
|
+
this.scrollToRow(index);
|
|
10378
|
+
}
|
|
9925
10379
|
/** Get current scroll position */
|
|
9926
10380
|
getScrollPosition() {
|
|
9927
10381
|
const el = this.options.scrollContainer;
|
|
@@ -9991,6 +10445,13 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
9991
10445
|
* Column management for snice-table.
|
|
9992
10446
|
* Handles: flex width, resize, auto-size, visibility, ordering, pinning, groups.
|
|
9993
10447
|
*/
|
|
10448
|
+
/** Padding + border a cell spends before any content fits inside it. */
|
|
10449
|
+
function horizontalCellChrome(cell) {
|
|
10450
|
+
const style = getComputedStyle(cell);
|
|
10451
|
+
const px = (value) => Number.parseFloat(value) || 0;
|
|
10452
|
+
return px(style.paddingLeft) + px(style.paddingRight)
|
|
10453
|
+
+ px(style.borderLeftWidth) + px(style.borderRightWidth);
|
|
10454
|
+
}
|
|
9994
10455
|
class TableColumnManager {
|
|
9995
10456
|
constructor() {
|
|
9996
10457
|
this.states = new Map();
|
|
@@ -10000,27 +10461,110 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
10000
10461
|
this.resizingKey = null;
|
|
10001
10462
|
this.resizeOverlay = null;
|
|
10002
10463
|
this.tableElement = null;
|
|
10464
|
+
this.fitMode = 'scroll';
|
|
10465
|
+
/**
|
|
10466
|
+
* The content budget the last `squish` fit shared out. A drag-resize needs it
|
|
10467
|
+
* to know how much width the columns beside the dragged one have to give up
|
|
10468
|
+
* (or take back) for the total to stay on the frame.
|
|
10469
|
+
*/
|
|
10470
|
+
this.squishBudget = null;
|
|
10471
|
+
}
|
|
10472
|
+
/** Fallback content width for a column nobody sized. */
|
|
10473
|
+
static { this.DEFAULT_WIDTH = 150; }
|
|
10474
|
+
/**
|
|
10475
|
+
* The floor `squish` relaxes `minWidth` down to. Not zero: a column narrower
|
|
10476
|
+
* than this shows no glyph at all, only its ellipsis, and stops being a
|
|
10477
|
+
* column the reader can identify. A frame too narrow even for this is past
|
|
10478
|
+
* what the mode can honour — see `fitVisibleColumns`.
|
|
10479
|
+
*/
|
|
10480
|
+
static { this.SQUISH_MIN_WIDTH = 24; }
|
|
10481
|
+
/**
|
|
10482
|
+
* Which fitting policy `fitVisibleColumns` (and a drag-resize) follows.
|
|
10483
|
+
* Owned by the table's `columnFit` property; see the `ColumnFit` docs.
|
|
10484
|
+
*/
|
|
10485
|
+
setFitMode(mode) {
|
|
10486
|
+
if (mode === this.fitMode)
|
|
10487
|
+
return;
|
|
10488
|
+
this.fitMode = mode;
|
|
10489
|
+
// The budget belongs to the squish fit that recorded it.
|
|
10490
|
+
if (mode !== 'squish')
|
|
10491
|
+
this.squishBudget = null;
|
|
10492
|
+
}
|
|
10493
|
+
getFitMode() {
|
|
10494
|
+
return this.fitMode;
|
|
10003
10495
|
}
|
|
10004
|
-
/**
|
|
10496
|
+
/**
|
|
10497
|
+
* Routine resync, run on every render: make sure a state exists for each
|
|
10498
|
+
* declared column and refresh the flags that are pure declaration (min/max,
|
|
10499
|
+
* resizable, reorderable, hideable, pinnable, flex).
|
|
10500
|
+
*
|
|
10501
|
+
* Deliberately PRESERVES the interaction-owned members of an existing state —
|
|
10502
|
+
* width, visible, pinned, order — because renderBody() calls this on every
|
|
10503
|
+
* paint, and re-applying the declaration here would undo a drag-resize, a
|
|
10504
|
+
* hidden column or a user reorder on the very next render. Re-applying the
|
|
10505
|
+
* declaration is applyConfiguration()'s job, and only a `columns` assignment
|
|
10506
|
+
* calls that.
|
|
10507
|
+
*/
|
|
10005
10508
|
initialize(columns, tableEl) {
|
|
10006
10509
|
this.tableElement = tableEl;
|
|
10007
10510
|
columns.forEach((col, index) => {
|
|
10008
10511
|
const existing = this.states.get(col.key);
|
|
10009
|
-
this.states.set(col.key,
|
|
10010
|
-
|
|
10011
|
-
|
|
10012
|
-
|
|
10013
|
-
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10017
|
-
|
|
10018
|
-
|
|
10019
|
-
|
|
10020
|
-
|
|
10021
|
-
|
|
10022
|
-
|
|
10512
|
+
this.states.set(col.key, this.buildState(col, index, existing));
|
|
10513
|
+
});
|
|
10514
|
+
}
|
|
10515
|
+
/**
|
|
10516
|
+
* A `columns` assignment (or setColumns()) delivered a NEW CONFIGURATION.
|
|
10517
|
+
*
|
|
10518
|
+
* The declaration is the source of truth here: painted order, `pinned`,
|
|
10519
|
+
* `width` and visibility all come from the array just assigned, and states for
|
|
10520
|
+
* columns the new array no longer declares are DROPPED. Without this the first
|
|
10521
|
+
* configuration the table ever saw won permanently — a re-ordered array kept
|
|
10522
|
+
* the original order, a newly declared pin or width was ignored, and a column
|
|
10523
|
+
* hidden through setColumnVisible() could never be brought back
|
|
10524
|
+
* (MATRIX-columns-2/3/4/6).
|
|
10525
|
+
*
|
|
10526
|
+
* The cost is that the assignment also discards user interaction state (a
|
|
10527
|
+
* drag-resize, a manual reorder, a hidden column) for the columns it
|
|
10528
|
+
* re-declares. That is the intended reading of the contract, and it applies to
|
|
10529
|
+
* EVERY assignment: `columns` declares `hasChanged: () => true`, so handing
|
|
10530
|
+
* back the array the table already holds — after mutating it, or unchanged —
|
|
10531
|
+
* re-applies the declaration exactly like a fresh array would. Mutating the
|
|
10532
|
+
* published definitions is therefore NOT an escape hatch; the only way to keep
|
|
10533
|
+
* user column state is to not assign `columns` at all and drive the change
|
|
10534
|
+
* through the column APIs instead (setColumnVisible / moveColumn / pinColumn /
|
|
10535
|
+
* unpinColumn), which mutate the state map in place. Renders triggered by this
|
|
10536
|
+
* go through the table's coalescing queue, so re-applying costs no extra paint.
|
|
10537
|
+
*/
|
|
10538
|
+
applyConfiguration(columns, tableEl) {
|
|
10539
|
+
this.tableElement = tableEl;
|
|
10540
|
+
const next = new Map();
|
|
10541
|
+
columns.forEach((col, index) => {
|
|
10542
|
+
next.set(col.key, this.buildState(col, index));
|
|
10023
10543
|
});
|
|
10544
|
+
this.states = next;
|
|
10545
|
+
}
|
|
10546
|
+
/**
|
|
10547
|
+
* One place that turns a column definition into a state. `existing` supplies
|
|
10548
|
+
* the interaction-owned members on the resync path; omitting it yields the
|
|
10549
|
+
* pure declared state applyConfiguration() wants.
|
|
10550
|
+
*/
|
|
10551
|
+
buildState(col, index, existing) {
|
|
10552
|
+
const declared = this.parseWidth(col.width);
|
|
10553
|
+
return {
|
|
10554
|
+
key: col.key,
|
|
10555
|
+
width: existing?.width ?? declared ?? TableColumnManager.DEFAULT_WIDTH,
|
|
10556
|
+
flex: col.flex,
|
|
10557
|
+
minWidth: col.minWidth ?? 50,
|
|
10558
|
+
maxWidth: col.maxWidth ?? Infinity,
|
|
10559
|
+
visible: existing?.visible ?? true,
|
|
10560
|
+
pinned: existing?.pinned ?? (col.pinned || false),
|
|
10561
|
+
order: existing?.order ?? index,
|
|
10562
|
+
resizable: col.resizable !== false,
|
|
10563
|
+
reorderable: col.reorderable !== false,
|
|
10564
|
+
hideable: col.hideable !== false,
|
|
10565
|
+
pinnable: col.pinnable !== false,
|
|
10566
|
+
authored: existing?.authored ?? declared !== undefined,
|
|
10567
|
+
};
|
|
10024
10568
|
}
|
|
10025
10569
|
parseWidth(width) {
|
|
10026
10570
|
if (!width)
|
|
@@ -10076,48 +10620,222 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
10076
10620
|
}
|
|
10077
10621
|
return widths;
|
|
10078
10622
|
}
|
|
10079
|
-
/**
|
|
10080
|
-
|
|
10081
|
-
|
|
10082
|
-
|
|
10083
|
-
|
|
10084
|
-
|
|
10085
|
-
|
|
10623
|
+
/**
|
|
10624
|
+
* Fit the columns nobody sized into `availableWidth` (the frame's content
|
|
10625
|
+
* box), reserving `chromePerColumn` for each column's own padding + border.
|
|
10626
|
+
*
|
|
10627
|
+
* A column width is a CONTENT width — the rendered column is that plus its
|
|
10628
|
+
* chrome — so the budget has to come off the top or the fitted table lands a
|
|
10629
|
+
* few pixels wider than the frame it was fitted to.
|
|
10630
|
+
*
|
|
10631
|
+
* Declared and user-chosen widths are spent first and never rewritten: a
|
|
10632
|
+
* table that asked for `width: '80'` keeps 80 whatever the frame does. What
|
|
10633
|
+
* is left is split evenly and clamped to each column's [minWidth, maxWidth],
|
|
10634
|
+
* so a frame too narrow for the minimums overflows into the frame's own
|
|
10635
|
+
* scroller instead of collapsing the columns to nothing.
|
|
10636
|
+
*
|
|
10637
|
+
* Returns whether any width actually moved, so callers can skip a repaint.
|
|
10638
|
+
*
|
|
10639
|
+
* Under `column-fit="squish"` the frame stops being a suggestion: see
|
|
10640
|
+
* `squishVisibleColumns`.
|
|
10641
|
+
*/
|
|
10642
|
+
fitVisibleColumns(availableWidth, chromePerColumn = 0) {
|
|
10643
|
+
const visible = this.getVisibleColumns();
|
|
10644
|
+
// A layoutless (or not-yet-laid-out) frame reports 0. Fitting to that would
|
|
10645
|
+
// slam every column to its minimum for a measurement that means nothing.
|
|
10646
|
+
if (visible.length === 0 || !(availableWidth > 0))
|
|
10647
|
+
return false;
|
|
10648
|
+
const budget = availableWidth - visible.length * chromePerColumn;
|
|
10649
|
+
if (this.fitMode === 'squish') {
|
|
10650
|
+
this.squishBudget = budget;
|
|
10651
|
+
return this.squishVisibleColumns(visible, budget);
|
|
10652
|
+
}
|
|
10653
|
+
const auto = visible.filter((state) => !state.authored);
|
|
10654
|
+
if (auto.length === 0)
|
|
10655
|
+
return false;
|
|
10656
|
+
let remaining = budget;
|
|
10657
|
+
for (const state of visible) {
|
|
10658
|
+
if (state.authored)
|
|
10659
|
+
remaining -= state.width;
|
|
10660
|
+
}
|
|
10661
|
+
const share = Math.floor(remaining / auto.length);
|
|
10662
|
+
let changed = false;
|
|
10663
|
+
for (const state of auto) {
|
|
10664
|
+
const next = Math.max(state.minWidth, Math.min(state.maxWidth, share));
|
|
10665
|
+
if (next !== state.width) {
|
|
10666
|
+
state.width = next;
|
|
10667
|
+
changed = true;
|
|
10086
10668
|
}
|
|
10087
|
-
}
|
|
10669
|
+
}
|
|
10670
|
+
return changed;
|
|
10088
10671
|
}
|
|
10089
|
-
|
|
10090
|
-
|
|
10091
|
-
|
|
10092
|
-
|
|
10093
|
-
|
|
10094
|
-
|
|
10095
|
-
|
|
10096
|
-
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
|
|
10100
|
-
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
|
|
10104
|
-
|
|
10105
|
-
|
|
10106
|
-
|
|
10672
|
+
/**
|
|
10673
|
+
* The `squish` policy: the columns add up to the frame, whatever that costs.
|
|
10674
|
+
*
|
|
10675
|
+
* `minWidth` is a readability preference, and this mode has already decided
|
|
10676
|
+
* the frame outranks it, so the only floor left is SQUISH_MIN_WIDTH. Two
|
|
10677
|
+
* cases:
|
|
10678
|
+
*
|
|
10679
|
+
* - There is an unsized column left to absorb the difference, and the
|
|
10680
|
+
* authored widths still leave room for it. Those are honoured exactly (a
|
|
10681
|
+
* column that asked for 80, or that the user dragged to 80, keeps 80) and
|
|
10682
|
+
* the rest is split evenly between the unsized ones.
|
|
10683
|
+
*
|
|
10684
|
+
* - Every column is authored, or the authored widths alone already overflow
|
|
10685
|
+
* the frame. There is nothing left to give, so `scroll`'s escape hatch
|
|
10686
|
+
* (overflow into the frame's scroller) is exactly what this mode exists to
|
|
10687
|
+
* prevent: every column is scaled by the same factor instead. Scaling is a
|
|
10688
|
+
* fixed point — the second pass computes a factor of 1 — so this settles
|
|
10689
|
+
* on the first render rather than creeping on every repaint.
|
|
10690
|
+
*
|
|
10691
|
+
* Either way the remainder from integer division is handed back to the widest
|
|
10692
|
+
* columns that are free to take it, so the widths sum to the budget EXACTLY
|
|
10693
|
+
* and the rightmost column edge lands on the frame's inner edge instead of a
|
|
10694
|
+
* few pixels short of it.
|
|
10695
|
+
*/
|
|
10696
|
+
squishVisibleColumns(visible, budget) {
|
|
10697
|
+
const floor = TableColumnManager.SQUISH_MIN_WIDTH;
|
|
10698
|
+
const auto = visible.filter((state) => !state.authored);
|
|
10699
|
+
// What the frame has left once the authored widths are paid for.
|
|
10700
|
+
const spare = budget - visible.reduce((spent, state) => spent + (state.authored ? state.width : 0), 0);
|
|
10701
|
+
const next = new Map();
|
|
10702
|
+
if (auto.length > 0 && spare >= auto.length * floor) {
|
|
10703
|
+
const share = Math.floor(spare / auto.length);
|
|
10704
|
+
for (const state of visible) {
|
|
10705
|
+
next.set(state.key, state.authored
|
|
10706
|
+
? state.width
|
|
10707
|
+
: Math.max(floor, Math.min(state.maxWidth, share)));
|
|
10708
|
+
}
|
|
10709
|
+
}
|
|
10710
|
+
else {
|
|
10711
|
+
const current = visible.reduce((sum, state) => sum + state.width, 0) || 1;
|
|
10712
|
+
const scale = budget / current;
|
|
10713
|
+
for (const state of visible) {
|
|
10714
|
+
next.set(state.key, Math.max(floor, Math.floor(state.width * scale)));
|
|
10715
|
+
}
|
|
10716
|
+
}
|
|
10717
|
+
// Hand the leftover pixels to the widest column that is free to grow: it
|
|
10718
|
+
// absorbs a few pixels without reading as a different column, and a
|
|
10719
|
+
// narrower one might be sitting on its floor or its maxWidth.
|
|
10720
|
+
let slack = budget - Array.from(next.values()).reduce((sum, w) => sum + w, 0);
|
|
10721
|
+
if (slack > 0) {
|
|
10722
|
+
const takers = visible
|
|
10723
|
+
.filter((state) => (next.get(state.key) ?? 0) < state.maxWidth)
|
|
10724
|
+
.sort((a, b) => (next.get(b.key) ?? 0) - (next.get(a.key) ?? 0));
|
|
10725
|
+
for (const state of takers) {
|
|
10726
|
+
if (slack <= 0)
|
|
10727
|
+
break;
|
|
10728
|
+
const room = Math.min(slack, state.maxWidth - (next.get(state.key) ?? 0));
|
|
10729
|
+
next.set(state.key, (next.get(state.key) ?? 0) + room);
|
|
10730
|
+
slack -= room;
|
|
10731
|
+
}
|
|
10732
|
+
}
|
|
10733
|
+
let changed = false;
|
|
10734
|
+
for (const state of visible) {
|
|
10735
|
+
const width = next.get(state.key);
|
|
10736
|
+
if (width !== state.width) {
|
|
10737
|
+
state.width = width;
|
|
10738
|
+
changed = true;
|
|
10739
|
+
}
|
|
10740
|
+
}
|
|
10741
|
+
return changed;
|
|
10742
|
+
}
|
|
10743
|
+
/**
|
|
10744
|
+
* A drag-resize under `squish` moved one column; the others pay for it.
|
|
10745
|
+
*
|
|
10746
|
+
* The dragged column is the anchor — it keeps the width the pointer asked
|
|
10747
|
+
* for, capped so the remaining columns can still sit on their floor — and the
|
|
10748
|
+
* rest are scaled to fill exactly what is left of the frame. Without this a
|
|
10749
|
+
* drag would simply push the trailing column out of a frame that is not
|
|
10750
|
+
* allowed to scroll.
|
|
10751
|
+
*/
|
|
10752
|
+
rebalanceSquish(anchorKey) {
|
|
10753
|
+
const budget = this.squishBudget;
|
|
10754
|
+
const anchor = this.states.get(anchorKey);
|
|
10755
|
+
if (budget === null || !anchor)
|
|
10756
|
+
return;
|
|
10757
|
+
const others = this.getVisibleColumns().filter((state) => state.key !== anchorKey);
|
|
10758
|
+
if (others.length === 0)
|
|
10759
|
+
return;
|
|
10760
|
+
const floor = TableColumnManager.SQUISH_MIN_WIDTH;
|
|
10761
|
+
anchor.width = Math.max(floor, Math.min(anchor.width, budget - others.length * floor));
|
|
10762
|
+
const remaining = budget - anchor.width;
|
|
10763
|
+
const current = others.reduce((sum, state) => sum + state.width, 0) || 1;
|
|
10764
|
+
const scale = remaining / current;
|
|
10765
|
+
for (const state of others) {
|
|
10766
|
+
state.width = Math.max(floor, Math.floor(state.width * scale));
|
|
10767
|
+
}
|
|
10768
|
+
let slack = remaining - others.reduce((sum, state) => sum + state.width, 0);
|
|
10769
|
+
for (const state of [...others].sort((a, b) => b.width - a.width)) {
|
|
10770
|
+
if (slack <= 0)
|
|
10771
|
+
break;
|
|
10772
|
+
const room = Math.min(slack, state.maxWidth - state.width);
|
|
10773
|
+
state.width += room;
|
|
10774
|
+
slack -= room;
|
|
10775
|
+
}
|
|
10776
|
+
}
|
|
10777
|
+
/** Push the current widths onto the painted header and body cells. */
|
|
10778
|
+
writeWidths(keys) {
|
|
10779
|
+
const root = this.tableElement?.shadowRoot;
|
|
10780
|
+
if (!root)
|
|
10781
|
+
return;
|
|
10782
|
+
for (const key of keys) {
|
|
10783
|
+
const width = this.states.get(key)?.width;
|
|
10784
|
+
if (width === undefined)
|
|
10785
|
+
continue;
|
|
10786
|
+
const th = root.querySelector(`th[data-key="${key}"]`);
|
|
10787
|
+
if (th)
|
|
10788
|
+
th.style.width = `${width}px`;
|
|
10789
|
+
const tds = root.querySelectorAll(`td[data-key="${key}"]`);
|
|
10790
|
+
tds.forEach((td) => { td.style.width = `${width}px`; });
|
|
10791
|
+
}
|
|
10792
|
+
}
|
|
10793
|
+
/** Apply computed widths to <col> or <th> elements */
|
|
10794
|
+
applyWidths(headerRow, widths) {
|
|
10795
|
+
const ths = headerRow.querySelectorAll('th[data-key]');
|
|
10796
|
+
ths.forEach(th => {
|
|
10797
|
+
const key = th.getAttribute('data-key');
|
|
10798
|
+
if (key && widths.has(key)) {
|
|
10799
|
+
th.style.width = `${widths.get(key)}px`;
|
|
10800
|
+
}
|
|
10801
|
+
});
|
|
10802
|
+
}
|
|
10803
|
+
// ── Resize ──
|
|
10804
|
+
/** Start column resize. Call from mousedown on resize handle. */
|
|
10805
|
+
startResize(columnKey, startX) {
|
|
10806
|
+
const state = this.states.get(columnKey);
|
|
10807
|
+
if (!state || !state.resizable)
|
|
10808
|
+
return;
|
|
10809
|
+
this.resizingKey = columnKey;
|
|
10810
|
+
this.resizeStartX = startX;
|
|
10811
|
+
this.resizeStartWidth = state.width;
|
|
10812
|
+
// Create overlay to capture mouse events
|
|
10813
|
+
this.resizeOverlay = document.createElement('div');
|
|
10814
|
+
this.resizeOverlay.style.cssText = 'position:fixed;inset:0;z-index:99999;cursor:col-resize;';
|
|
10815
|
+
document.body.appendChild(this.resizeOverlay);
|
|
10816
|
+
const onMove = (e) => {
|
|
10817
|
+
e.preventDefault();
|
|
10818
|
+
const delta = e.clientX - this.resizeStartX;
|
|
10819
|
+
// Squish relaxes minWidth for the fit, so it has to relax it for the drag
|
|
10820
|
+
// too — otherwise a column the fit already squished below its minimum
|
|
10821
|
+
// would jump back up to it the moment its edge was touched.
|
|
10822
|
+
const squish = this.fitMode === 'squish';
|
|
10823
|
+
const min = squish ? TableColumnManager.SQUISH_MIN_WIDTH : state.minWidth;
|
|
10824
|
+
state.width = Math.max(min, Math.min(state.maxWidth, this.resizeStartWidth + delta));
|
|
10107
10825
|
// Remove flex if manually resized
|
|
10108
10826
|
state.flex = undefined;
|
|
10109
|
-
//
|
|
10110
|
-
|
|
10111
|
-
|
|
10112
|
-
|
|
10113
|
-
|
|
10114
|
-
|
|
10115
|
-
|
|
10116
|
-
|
|
10117
|
-
|
|
10827
|
+
// The user chose this width — the container fit must leave it alone.
|
|
10828
|
+
state.authored = true;
|
|
10829
|
+
// Squish cannot let the drag push the table past the frame: the columns
|
|
10830
|
+
// beside this one give up (or take back) the difference.
|
|
10831
|
+
if (squish)
|
|
10832
|
+
this.rebalanceSquish(columnKey);
|
|
10833
|
+
this.writeWidths(squish
|
|
10834
|
+
? this.getVisibleColumns().map((column) => column.key)
|
|
10835
|
+
: [columnKey]);
|
|
10118
10836
|
// Dispatch resize event
|
|
10119
10837
|
this.tableElement?.dispatchEvent(new CustomEvent('column-resize', {
|
|
10120
|
-
detail: { key: columnKey, width:
|
|
10838
|
+
detail: { key: columnKey, width: state.width },
|
|
10121
10839
|
bubbles: true, composed: true
|
|
10122
10840
|
}));
|
|
10123
10841
|
};
|
|
@@ -10137,24 +10855,81 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
10137
10855
|
}
|
|
10138
10856
|
/** Auto-size a column to fit its content */
|
|
10139
10857
|
autoSizeColumn(columnKey, tbody) {
|
|
10140
|
-
|
|
10141
|
-
if (!state)
|
|
10142
|
-
return;
|
|
10143
|
-
// Measure header
|
|
10144
|
-
const th = this.tableElement?.shadowRoot?.querySelector(`th[data-key="${columnKey}"]`);
|
|
10145
|
-
let maxWidth = th ? th.scrollWidth : 0;
|
|
10146
|
-
// Measure body cells
|
|
10147
|
-
const cells = tbody.querySelectorAll(`td[data-key="${columnKey}"]`);
|
|
10148
|
-
cells.forEach(cell => {
|
|
10149
|
-
maxWidth = Math.max(maxWidth, cell.scrollWidth);
|
|
10150
|
-
});
|
|
10151
|
-
state.width = Math.max(state.minWidth, Math.min(state.maxWidth, maxWidth + 16)); // 16px padding
|
|
10152
|
-
state.flex = undefined;
|
|
10858
|
+
this.autoSizeColumns([columnKey], tbody);
|
|
10153
10859
|
}
|
|
10154
10860
|
/** Auto-size all columns */
|
|
10155
10861
|
autoSizeAll(tbody) {
|
|
10156
|
-
|
|
10157
|
-
|
|
10862
|
+
this.autoSizeColumns(Array.from(this.states.keys()), tbody);
|
|
10863
|
+
}
|
|
10864
|
+
/** Slack added to a measured content width so the last glyph never sits on
|
|
10865
|
+
* the clip edge (and a sortable header keeps room for its chevron). */
|
|
10866
|
+
static { this.AUTO_SIZE_SLACK = 4; }
|
|
10867
|
+
/**
|
|
10868
|
+
* Measure the columns' content and give each the width it actually needs.
|
|
10869
|
+
*
|
|
10870
|
+
* A table cell's `scrollWidth` is NOT its content width: for content that
|
|
10871
|
+
* fits, it reports the padding box — the width the column already has. The
|
|
10872
|
+
* previous implementation took that maximum and added 16px, so every
|
|
10873
|
+
* "auto-size" pass could only GROW the column, by a chrome's worth each
|
|
10874
|
+
* time, until the table outgrew its own frame. Once it did, the right-pinned
|
|
10875
|
+
* column (`position: sticky`) parked over the columns beside it, and the
|
|
10876
|
+
* columns behind it read as deleted.
|
|
10877
|
+
*
|
|
10878
|
+
* The width a column NEEDS is a layout question, so ask layout: drop the
|
|
10879
|
+
* table into content-driven sizing (`table-layout: auto` at `max-content`)
|
|
10880
|
+
* with the measured columns' imposed widths removed, and read back the width
|
|
10881
|
+
* each header settles at. `max-content` also means no wrapping, so a long
|
|
10882
|
+
* cell reports its full single-line extent even though the painted column
|
|
10883
|
+
* ellipsises it. Everything is restored in the same task, before paint.
|
|
10884
|
+
*/
|
|
10885
|
+
autoSizeColumns(columnKeys, tbody) {
|
|
10886
|
+
const root = this.tableElement?.shadowRoot ?? null;
|
|
10887
|
+
const tableEl = (root?.querySelector('table')
|
|
10888
|
+
?? tbody.closest('table'));
|
|
10889
|
+
const targets = columnKeys
|
|
10890
|
+
.map((key) => this.states.get(key))
|
|
10891
|
+
.filter((state) => !!state);
|
|
10892
|
+
if (!tableEl || targets.length === 0)
|
|
10893
|
+
return;
|
|
10894
|
+
const restore = [];
|
|
10895
|
+
const push = (el, property) => {
|
|
10896
|
+
const previous = el.style[property];
|
|
10897
|
+
restore.push(() => { el.style[property] = previous; });
|
|
10898
|
+
};
|
|
10899
|
+
push(tableEl, 'tableLayout');
|
|
10900
|
+
push(tableEl, 'width');
|
|
10901
|
+
tableEl.style.tableLayout = 'auto';
|
|
10902
|
+
tableEl.style.width = 'max-content';
|
|
10903
|
+
const headers = new Map();
|
|
10904
|
+
for (const state of targets) {
|
|
10905
|
+
const header = root?.querySelector(`th[data-key="${state.key}"]`);
|
|
10906
|
+
if (!header)
|
|
10907
|
+
continue;
|
|
10908
|
+
headers.set(state.key, header);
|
|
10909
|
+
// An imposed width is still a preferred width under auto layout, so it
|
|
10910
|
+
// has to come off before the column can report what it wants.
|
|
10911
|
+
push(header, 'width');
|
|
10912
|
+
header.style.width = 'auto';
|
|
10913
|
+
for (const cell of Array.from(tbody.querySelectorAll(`td[data-key="${state.key}"]`))) {
|
|
10914
|
+
push(cell, 'width');
|
|
10915
|
+
cell.style.width = 'auto';
|
|
10916
|
+
}
|
|
10917
|
+
}
|
|
10918
|
+
const widths = new Map();
|
|
10919
|
+
for (const [key, header] of headers) {
|
|
10920
|
+
widths.set(key, header.getBoundingClientRect().width - horizontalCellChrome(header));
|
|
10921
|
+
}
|
|
10922
|
+
for (const undo of restore)
|
|
10923
|
+
undo();
|
|
10924
|
+
for (const state of targets) {
|
|
10925
|
+
const content = widths.get(state.key) ?? 0;
|
|
10926
|
+
// Nothing measurable — a layoutless DOM, or a column with no rendered
|
|
10927
|
+
// header. Leave the width alone rather than slamming it to the minimum.
|
|
10928
|
+
if (content <= 0)
|
|
10929
|
+
continue;
|
|
10930
|
+
state.width = Math.max(state.minWidth, Math.min(state.maxWidth, Math.ceil(content) + TableColumnManager.AUTO_SIZE_SLACK));
|
|
10931
|
+
state.flex = undefined;
|
|
10932
|
+
state.authored = true;
|
|
10158
10933
|
}
|
|
10159
10934
|
}
|
|
10160
10935
|
// ── Visibility ──
|
|
@@ -10475,25 +11250,45 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
10475
11250
|
return Number(cellValue) < Number(filterValue);
|
|
10476
11251
|
case 'lte':
|
|
10477
11252
|
return Number(cellValue) <= Number(filterValue);
|
|
10478
|
-
// Date
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
11253
|
+
// Date. Both sides cross the cell layer's parser, so a `2026-03-15`
|
|
11254
|
+
// bound means that calendar day locally — the same day the cell paints
|
|
11255
|
+
// — instead of UTC midnight, which shifted every comparison by a day
|
|
11256
|
+
// for negative UTC offsets and split a date-only bound from a
|
|
11257
|
+
// timestamped cell value on the same day.
|
|
11258
|
+
case 'is':
|
|
10484
11259
|
case 'isNot': {
|
|
10485
|
-
const
|
|
10486
|
-
const
|
|
10487
|
-
|
|
11260
|
+
const cellDay = this.calendarDay(cellValue);
|
|
11261
|
+
const filterDay = this.calendarDay(filterValue);
|
|
11262
|
+
// An unparseable value is on no day: it never "is" the filtered day,
|
|
11263
|
+
// and it is therefore always "isNot" it.
|
|
11264
|
+
const sameDay = cellDay !== null && filterDay !== null && cellDay === filterDay;
|
|
11265
|
+
return filter.operator === 'is' ? sameDay : !sameDay;
|
|
10488
11266
|
}
|
|
10489
11267
|
case 'before':
|
|
10490
|
-
return new Date(cellValue) < new Date(filterValue);
|
|
10491
11268
|
case 'onOrBefore':
|
|
10492
|
-
return new Date(cellValue) <= new Date(filterValue);
|
|
10493
11269
|
case 'after':
|
|
10494
|
-
|
|
10495
|
-
|
|
10496
|
-
|
|
11270
|
+
case 'onOrAfter': {
|
|
11271
|
+
const cell = parseCellDate(cellValue);
|
|
11272
|
+
const bound = parseCellDate(filterValue);
|
|
11273
|
+
if (!cell || !bound)
|
|
11274
|
+
return false;
|
|
11275
|
+
const a = cell.getTime();
|
|
11276
|
+
// A date-only bound names a whole DAY, not the instant it starts at, so
|
|
11277
|
+
// the day's two edges bracket it. Comparing everything against its
|
|
11278
|
+
// opening instant made the four operators disagree with `is`: a cell at
|
|
11279
|
+
// 09:00 on the bound day "is" that day, yet counted as `after` it and
|
|
11280
|
+
// fell outside `onOrBefore` it. A bound that carries its own time is an
|
|
11281
|
+
// instant, and both edges collapse onto it.
|
|
11282
|
+
const dayStart = bound.getTime();
|
|
11283
|
+
const dayEnd = this.boundEnd(filterValue, bound);
|
|
11284
|
+
if (filter.operator === 'before')
|
|
11285
|
+
return a < dayStart;
|
|
11286
|
+
if (filter.operator === 'onOrBefore')
|
|
11287
|
+
return a <= dayEnd;
|
|
11288
|
+
if (filter.operator === 'after')
|
|
11289
|
+
return a > dayEnd;
|
|
11290
|
+
return a >= dayStart;
|
|
11291
|
+
}
|
|
10497
11292
|
// Boolean
|
|
10498
11293
|
case 'isTrue':
|
|
10499
11294
|
return cellValue === true || cellValue === 'true' || cellValue === 1;
|
|
@@ -10503,6 +11298,28 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
10503
11298
|
return true;
|
|
10504
11299
|
}
|
|
10505
11300
|
}
|
|
11301
|
+
/**
|
|
11302
|
+
* The local calendar day a value falls on, as a `YYYY-M-D` stamp, or `null`
|
|
11303
|
+
* when the value names no day at all.
|
|
11304
|
+
*/
|
|
11305
|
+
calendarDay(value) {
|
|
11306
|
+
const date = parseCellDate(value);
|
|
11307
|
+
if (!date)
|
|
11308
|
+
return null;
|
|
11309
|
+
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
|
|
11310
|
+
}
|
|
11311
|
+
/** True when the raw bound named a calendar day rather than an instant. */
|
|
11312
|
+
isDateOnlyBound(raw) {
|
|
11313
|
+
return typeof raw === 'string' && /^\d{4}(?:-\d{2}){0,2}$/.test(raw.trim());
|
|
11314
|
+
}
|
|
11315
|
+
/** Last instant covered by a bound — the day's end when it named a day. */
|
|
11316
|
+
boundEnd(raw, parsed) {
|
|
11317
|
+
if (!this.isDateOnlyBound(raw))
|
|
11318
|
+
return parsed.getTime();
|
|
11319
|
+
const end = new Date(parsed);
|
|
11320
|
+
end.setDate(end.getDate() + 1);
|
|
11321
|
+
return end.getTime() - 1;
|
|
11322
|
+
}
|
|
10506
11323
|
/** Serialize filter model for server-side requests */
|
|
10507
11324
|
toServerParams() {
|
|
10508
11325
|
return {
|
|
@@ -10885,7 +11702,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
10885
11702
|
return;
|
|
10886
11703
|
table.setAttribute('role', 'grid');
|
|
10887
11704
|
table.setAttribute('tabindex', '0');
|
|
10888
|
-
table.setAttribute('aria-rowcount', String(this.ariaRowCount +
|
|
11705
|
+
table.setAttribute('aria-rowcount', String(this.ariaRowCount + this.headerRowCount));
|
|
10889
11706
|
table.setAttribute('aria-colcount', String(this.totalColCount));
|
|
10890
11707
|
}
|
|
10891
11708
|
/**
|
|
@@ -10919,6 +11736,16 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
10919
11736
|
get ariaRowOffset() {
|
|
10920
11737
|
return Math.max(0, this.resolveBound(this.options.ariaRowOffset));
|
|
10921
11738
|
}
|
|
11739
|
+
/**
|
|
11740
|
+
* Rows the header contributes to `aria-rowcount`, and the offset every body
|
|
11741
|
+
* row's `aria-rowindex` starts from. Normally 1 — but list mode paints a
|
|
11742
|
+
* card per row and leaves the thead empty, and claiming a header row that
|
|
11743
|
+
* does not exist would count one row too many and start the body at index 2
|
|
11744
|
+
* with nothing at index 1.
|
|
11745
|
+
*/
|
|
11746
|
+
get headerRowCount() {
|
|
11747
|
+
return this.getTable()?.querySelector('thead tr') ? 1 : 0;
|
|
11748
|
+
}
|
|
10922
11749
|
/** Resolve a body row by its logical data-index (not its DOM position). */
|
|
10923
11750
|
getBodyRow(index) {
|
|
10924
11751
|
if (this.options.getRowElement)
|
|
@@ -10933,7 +11760,8 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
10933
11760
|
const table = this.getTable();
|
|
10934
11761
|
if (!table)
|
|
10935
11762
|
return;
|
|
10936
|
-
|
|
11763
|
+
const headerRows = this.headerRowCount;
|
|
11764
|
+
table.setAttribute('aria-rowcount', String(this.ariaRowCount + headerRows));
|
|
10937
11765
|
table.setAttribute('aria-colcount', String(this.totalColCount));
|
|
10938
11766
|
// Header row
|
|
10939
11767
|
const headerRow = table.querySelector('thead tr.column-header-row')
|
|
@@ -10965,7 +11793,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
10965
11793
|
const bodyRows = table.querySelectorAll('tbody tr:not(.virtual-spacer):not(.table-fill-row)');
|
|
10966
11794
|
bodyRows.forEach((row, i) => {
|
|
10967
11795
|
row.setAttribute('role', 'row');
|
|
10968
|
-
row.setAttribute('aria-rowindex', String(this.ariaRowOffset + i +
|
|
11796
|
+
row.setAttribute('aria-rowindex', String(this.ariaRowOffset + i + 1 + headerRows)); // 1-based, after the header
|
|
10969
11797
|
const isSelected = row.getAttribute('data-selected') === 'true';
|
|
10970
11798
|
if (isSelected)
|
|
10971
11799
|
row.setAttribute('aria-selected', 'true');
|
|
@@ -11381,8 +12209,10 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
11381
12209
|
return `${num.toFixed(column.percentageFormat?.decimals ?? 2)}%`;
|
|
11382
12210
|
}
|
|
11383
12211
|
case 'date': {
|
|
11384
|
-
|
|
11385
|
-
|
|
12212
|
+
// Exported dates must read as the cell reads them: a calendar day is
|
|
12213
|
+
// local midnight, never UTC midnight (see table-date.ts).
|
|
12214
|
+
const date = parseCellDate(value);
|
|
12215
|
+
if (!date)
|
|
11386
12216
|
return this.rawValue(value);
|
|
11387
12217
|
const format = column.dateFormat || {};
|
|
11388
12218
|
const dateStyle = format.format === 'custom' ? undefined : (format.format || 'short');
|
|
@@ -12286,10 +13116,13 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
12286
13116
|
this.flatRows = [];
|
|
12287
13117
|
this.enabled = false;
|
|
12288
13118
|
this.defaultDepth = 0;
|
|
13119
|
+
/** Whether the default expansion depth has been seeded (first data only). */
|
|
13120
|
+
this.defaultsApplied = false;
|
|
12289
13121
|
}
|
|
12290
13122
|
attach(options) {
|
|
12291
13123
|
this.options = options;
|
|
12292
13124
|
this.enabled = true;
|
|
13125
|
+
this.defaultsApplied = false;
|
|
12293
13126
|
// Pre-expand to default depth
|
|
12294
13127
|
if (options.defaultExpansionDepth !== undefined && options.defaultExpansionDepth >= 0) {
|
|
12295
13128
|
this.defaultDepth = options.defaultExpansionDepth;
|
|
@@ -12310,13 +13143,27 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
12310
13143
|
for (const row of data) {
|
|
12311
13144
|
const path = this.options.getPath(row);
|
|
12312
13145
|
const key = path.join('/');
|
|
12313
|
-
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
13146
|
+
// A real row may already exist as a gap node generated for a descendant
|
|
13147
|
+
// delivered earlier; upgrade it in place so the children it collected
|
|
13148
|
+
// survive.
|
|
13149
|
+
const existing = nodeMap.get(key);
|
|
13150
|
+
if (existing) {
|
|
13151
|
+
existing.data = row;
|
|
13152
|
+
existing.depth = path.length - 1;
|
|
13153
|
+
existing.isGap = false;
|
|
13154
|
+
}
|
|
13155
|
+
else {
|
|
13156
|
+
nodeMap.set(key, {
|
|
13157
|
+
data: row,
|
|
13158
|
+
children: new Set(),
|
|
13159
|
+
depth: path.length - 1,
|
|
13160
|
+
isGap: false,
|
|
13161
|
+
});
|
|
13162
|
+
}
|
|
13163
|
+
// Ensure all ancestors exist (gap nodes), and register each one as a
|
|
13164
|
+
// child of its own parent: a generated node with no link upward leaves
|
|
13165
|
+
// its parent reporting hasChildren === false, so flatten() never
|
|
13166
|
+
// descends and the whole subtree — real rows included — disappears.
|
|
12320
13167
|
for (let i = 1; i < path.length; i++) {
|
|
12321
13168
|
const ancestorPath = path.slice(0, i);
|
|
12322
13169
|
const ancestorKey = ancestorPath.join('/');
|
|
@@ -12328,6 +13175,11 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
12328
13175
|
isGap: true,
|
|
12329
13176
|
});
|
|
12330
13177
|
}
|
|
13178
|
+
if (i > 1) {
|
|
13179
|
+
const ancestorParent = nodeMap.get(path.slice(0, i - 1).join('/'));
|
|
13180
|
+
if (ancestorParent)
|
|
13181
|
+
ancestorParent.children.add(ancestorKey);
|
|
13182
|
+
}
|
|
12331
13183
|
}
|
|
12332
13184
|
// Register as child of parent
|
|
12333
13185
|
if (path.length > 1) {
|
|
@@ -12344,13 +13196,17 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
12344
13196
|
rootKeys.push(key);
|
|
12345
13197
|
}
|
|
12346
13198
|
rootKeys.sort();
|
|
12347
|
-
// Auto-expand to default depth on first process
|
|
12348
|
-
|
|
13199
|
+
// Auto-expand to default depth on the first process that sees rows.
|
|
13200
|
+
// `defaultExpansionDepth` describes the STARTING expansion only: keying
|
|
13201
|
+
// this off an empty expansion set re-seeded it on every render, so
|
|
13202
|
+
// collapsing the last expanded node (or collapseAll()) was silently undone.
|
|
13203
|
+
if (!this.defaultsApplied && this.defaultDepth > 0 && nodeMap.size > 0) {
|
|
12349
13204
|
for (const [key, node] of nodeMap) {
|
|
12350
13205
|
if (node.depth < this.defaultDepth && node.children.size > 0) {
|
|
12351
13206
|
this.expandedKeys.add(key);
|
|
12352
13207
|
}
|
|
12353
13208
|
}
|
|
13209
|
+
this.defaultsApplied = true;
|
|
12354
13210
|
}
|
|
12355
13211
|
// Flatten tree respecting expand/collapse
|
|
12356
13212
|
this.flatRows = [];
|
|
@@ -13104,6 +13960,11 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
13104
13960
|
}
|
|
13105
13961
|
}
|
|
13106
13962
|
|
|
13963
|
+
/**
|
|
13964
|
+
* The one string both loading affordances use — the zero-row message row shows
|
|
13965
|
+
* it, the refetch overlay announces it. Kept together so they can never drift.
|
|
13966
|
+
*/
|
|
13967
|
+
const LOADING_LABEL = 'Loading…';
|
|
13107
13968
|
(() => {
|
|
13108
13969
|
let _classDecorators = [snice.element('snice-table')];
|
|
13109
13970
|
let _classDescriptor;
|
|
@@ -13198,6 +14059,9 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
13198
14059
|
let _density_decorators;
|
|
13199
14060
|
let _density_initializers = [];
|
|
13200
14061
|
let _density_extraInitializers = [];
|
|
14062
|
+
let _columnFit_decorators;
|
|
14063
|
+
let _columnFit_initializers = [];
|
|
14064
|
+
let _columnFit_extraInitializers = [];
|
|
13201
14065
|
let _headerFilters_decorators;
|
|
13202
14066
|
let _headerFilters_initializers = [];
|
|
13203
14067
|
let _headerFilters_extraInitializers = [];
|
|
@@ -13273,6 +14137,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
13273
14137
|
let _handleColumnReorderChange_decorators;
|
|
13274
14138
|
let _handleLazyLoadChange_decorators;
|
|
13275
14139
|
let _handleColumnMenuChange_decorators;
|
|
14140
|
+
let _handleColumnFitChange_decorators;
|
|
13276
14141
|
let _dispatchPageChange_decorators;
|
|
13277
14142
|
let _dispatchRowSelectionChanged_decorators;
|
|
13278
14143
|
let _dispatchRowClicked_decorators;
|
|
@@ -13310,8 +14175,8 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
13310
14175
|
_totalItems_decorators = [snice.property({ type: Number, attribute: 'total-items' })];
|
|
13311
14176
|
_pageSizes_decorators = [snice.property({ type: Array, attribute: false })];
|
|
13312
14177
|
_searchDebounce_decorators = [snice.property({ type: Number, attribute: 'search-debounce' })];
|
|
13313
|
-
_columns_decorators = [snice.property({ attribute: false })];
|
|
13314
|
-
_data_decorators = [snice.property({ attribute: false })];
|
|
14178
|
+
_columns_decorators = [snice.property({ attribute: false, hasChanged: () => true })];
|
|
14179
|
+
_data_decorators = [snice.property({ attribute: false, hasChanged: () => true })];
|
|
13315
14180
|
_mode_decorators = [snice.property()];
|
|
13316
14181
|
_currentSort_decorators = [snice.property({ type: Array, attribute: false })];
|
|
13317
14182
|
_selector_decorators = [snice.property({ type: String, attribute: 'selector' })];
|
|
@@ -13324,6 +14189,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
13324
14189
|
_editable_decorators = [snice.property({ type: Boolean, attribute: 'editable' })];
|
|
13325
14190
|
_editMode_decorators = [snice.property({ attribute: 'edit-mode' })];
|
|
13326
14191
|
_density_decorators = [snice.property({ attribute: 'density' })];
|
|
14192
|
+
_columnFit_decorators = [snice.property({ attribute: 'column-fit' })];
|
|
13327
14193
|
_headerFilters_decorators = [snice.property({ type: Boolean, attribute: 'header-filters' })];
|
|
13328
14194
|
_quickFilter_decorators = [snice.property({ type: Boolean, attribute: 'quick-filter' })];
|
|
13329
14195
|
_rowReorder_decorators = [snice.property({ type: Boolean, attribute: 'row-reorder' })];
|
|
@@ -13371,6 +14237,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
13371
14237
|
_handleColumnReorderChange_decorators = [snice.watch('columnReorder', { immediate: false })];
|
|
13372
14238
|
_handleLazyLoadChange_decorators = [snice.watch('lazyLoad', 'lazyLoadThreshold', { immediate: false })];
|
|
13373
14239
|
_handleColumnMenuChange_decorators = [snice.watch('columnMenu', { immediate: false })];
|
|
14240
|
+
_handleColumnFitChange_decorators = [snice.watch('columnFit', { immediate: false })];
|
|
13374
14241
|
_dispatchPageChange_decorators = [snice.dispatch('page-change', { bubbles: true, composed: true })];
|
|
13375
14242
|
_dispatchRowSelectionChanged_decorators = [snice.dispatch('table-row-selection-changed', { bubbles: true, composed: true })];
|
|
13376
14243
|
_dispatchRowClicked_decorators = [snice.dispatch('row-clicked', { bubbles: true, composed: true })];
|
|
@@ -13420,6 +14287,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
13420
14287
|
__esDecorate(this, null, _handleColumnReorderChange_decorators, { kind: "method", name: "handleColumnReorderChange", static: false, private: false, access: { has: obj => "handleColumnReorderChange" in obj, get: obj => obj.handleColumnReorderChange }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
13421
14288
|
__esDecorate(this, null, _handleLazyLoadChange_decorators, { kind: "method", name: "handleLazyLoadChange", static: false, private: false, access: { has: obj => "handleLazyLoadChange" in obj, get: obj => obj.handleLazyLoadChange }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
13422
14289
|
__esDecorate(this, null, _handleColumnMenuChange_decorators, { kind: "method", name: "handleColumnMenuChange", static: false, private: false, access: { has: obj => "handleColumnMenuChange" in obj, get: obj => obj.handleColumnMenuChange }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
14290
|
+
__esDecorate(this, null, _handleColumnFitChange_decorators, { kind: "method", name: "handleColumnFitChange", static: false, private: false, access: { has: obj => "handleColumnFitChange" in obj, get: obj => obj.handleColumnFitChange }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
13423
14291
|
__esDecorate(this, null, _dispatchPageChange_decorators, { kind: "method", name: "dispatchPageChange", static: false, private: false, access: { has: obj => "dispatchPageChange" in obj, get: obj => obj.dispatchPageChange }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
13424
14292
|
__esDecorate(this, null, _dispatchRowSelectionChanged_decorators, { kind: "method", name: "dispatchRowSelectionChanged", static: false, private: false, access: { has: obj => "dispatchRowSelectionChanged" in obj, get: obj => obj.dispatchRowSelectionChanged }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
13425
14293
|
__esDecorate(this, null, _dispatchRowClicked_decorators, { kind: "method", name: "dispatchRowClicked", static: false, private: false, access: { has: obj => "dispatchRowClicked" in obj, get: obj => obj.dispatchRowClicked }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
@@ -13464,6 +14332,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
13464
14332
|
__esDecorate(null, null, _editable_decorators, { kind: "field", name: "editable", static: false, private: false, access: { has: obj => "editable" in obj, get: obj => obj.editable, set: (obj, value) => { obj.editable = value; } }, metadata: _metadata }, _editable_initializers, _editable_extraInitializers);
|
|
13465
14333
|
__esDecorate(null, null, _editMode_decorators, { kind: "field", name: "editMode", static: false, private: false, access: { has: obj => "editMode" in obj, get: obj => obj.editMode, set: (obj, value) => { obj.editMode = value; } }, metadata: _metadata }, _editMode_initializers, _editMode_extraInitializers);
|
|
13466
14334
|
__esDecorate(null, null, _density_decorators, { kind: "field", name: "density", static: false, private: false, access: { has: obj => "density" in obj, get: obj => obj.density, set: (obj, value) => { obj.density = value; } }, metadata: _metadata }, _density_initializers, _density_extraInitializers);
|
|
14335
|
+
__esDecorate(null, null, _columnFit_decorators, { kind: "field", name: "columnFit", static: false, private: false, access: { has: obj => "columnFit" in obj, get: obj => obj.columnFit, set: (obj, value) => { obj.columnFit = value; } }, metadata: _metadata }, _columnFit_initializers, _columnFit_extraInitializers);
|
|
13467
14336
|
__esDecorate(null, null, _headerFilters_decorators, { kind: "field", name: "headerFilters", static: false, private: false, access: { has: obj => "headerFilters" in obj, get: obj => obj.headerFilters, set: (obj, value) => { obj.headerFilters = value; } }, metadata: _metadata }, _headerFilters_initializers, _headerFilters_extraInitializers);
|
|
13468
14337
|
__esDecorate(null, null, _quickFilter_decorators, { kind: "field", name: "quickFilter", static: false, private: false, access: { has: obj => "quickFilter" in obj, get: obj => obj.quickFilter, set: (obj, value) => { obj.quickFilter = value; } }, metadata: _metadata }, _quickFilter_initializers, _quickFilter_extraInitializers);
|
|
13469
14338
|
__esDecorate(null, null, _rowReorder_decorators, { kind: "field", name: "rowReorder", static: false, private: false, access: { has: obj => "rowReorder" in obj, get: obj => obj.rowReorder, set: (obj, value) => { obj.rowReorder = value; } }, metadata: _metadata }, _rowReorder_initializers, _rowReorder_extraInitializers);
|
|
@@ -13507,6 +14376,11 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
13507
14376
|
// Wait for next frame to ensure DOM is updated
|
|
13508
14377
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
13509
14378
|
this.renderHeader();
|
|
14379
|
+
// The body render the columns assignment queued above can have been
|
|
14380
|
+
// consumed against a not-yet-committed <tbody>. Repainting the header
|
|
14381
|
+
// without the rows is what leaves a remote table showing headings over an
|
|
14382
|
+
// empty frame, so this pass owns both.
|
|
14383
|
+
this.renderBody();
|
|
13510
14384
|
this.renderControls();
|
|
13511
14385
|
return config;
|
|
13512
14386
|
}
|
|
@@ -13536,8 +14410,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
13536
14410
|
if (response.totalItems !== undefined) {
|
|
13537
14411
|
this.totalItems = response.totalItems;
|
|
13538
14412
|
}
|
|
13539
|
-
this.
|
|
13540
|
-
this.classList.remove('table--error');
|
|
14413
|
+
this.clearLoadError();
|
|
13541
14414
|
this.loading = false;
|
|
13542
14415
|
// Wait for next frame to ensure DOM is updated
|
|
13543
14416
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
@@ -13556,14 +14429,49 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
13556
14429
|
this.loading = false;
|
|
13557
14430
|
// Wait for next frame to ensure DOM is updated
|
|
13558
14431
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
14432
|
+
// Same guard the success path applies above: a newer request can settle
|
|
14433
|
+
// (and paint) inside this frame, and this failure's repaint would then be
|
|
14434
|
+
// a second full body rebuild owed to nobody.
|
|
14435
|
+
if (seq !== this.dataRequestSeq)
|
|
14436
|
+
return;
|
|
13559
14437
|
this.renderBody();
|
|
13560
14438
|
}
|
|
13561
14439
|
}
|
|
14440
|
+
/**
|
|
14441
|
+
* Requeue a body render whose target had not been committed yet. A frame is
|
|
14442
|
+
* the right granularity — the shadow template commits between frames — and
|
|
14443
|
+
* the paint is dropped again (silently, as before) once the retries run out.
|
|
14444
|
+
*
|
|
14445
|
+
* The retry CHAINS: a template that needs more than one frame to commit is
|
|
14446
|
+
* the whole reason this exists, so a retry that still finds no <tbody> spends
|
|
14447
|
+
* one more of the budget rather than giving the frame back and quitting.
|
|
14448
|
+
*/
|
|
14449
|
+
rearmBodyRender() {
|
|
14450
|
+
if (this.bodyRenderRetryQueued || this.bodyRenderRetries >= 3)
|
|
14451
|
+
return;
|
|
14452
|
+
this.bodyRenderRetryQueued = true;
|
|
14453
|
+
this.bodyRenderRetries++;
|
|
14454
|
+
requestAnimationFrame(() => {
|
|
14455
|
+
this.bodyRenderRetryQueued = false;
|
|
14456
|
+
if (this.tbody)
|
|
14457
|
+
this.renderBody();
|
|
14458
|
+
else
|
|
14459
|
+
this.rearmBodyRender();
|
|
14460
|
+
});
|
|
14461
|
+
}
|
|
13562
14462
|
scheduleRender(what) {
|
|
13563
14463
|
if (what === 'header' || what === 'both')
|
|
13564
14464
|
this.pendingHeaderRender = true;
|
|
13565
|
-
if (what === 'body' || what === 'both')
|
|
14465
|
+
if (what === 'body' || what === 'both') {
|
|
13566
14466
|
this.pendingBodyRender = true;
|
|
14467
|
+
// A NEWLY requested paint gets a fresh budget. The bound exists to stop
|
|
14468
|
+
// one render from spinning against a template that structurally has no
|
|
14469
|
+
// <tbody> (the slotted-rows branch), not to disqualify the table forever:
|
|
14470
|
+
// without this reset, three misses during the slotted phase silently
|
|
14471
|
+
// cancel every body render that follows a later flip to the native table.
|
|
14472
|
+
if (!this.bodyRenderRetryQueued)
|
|
14473
|
+
this.bodyRenderRetries = 0;
|
|
14474
|
+
}
|
|
13567
14475
|
if (this.renderQueued)
|
|
13568
14476
|
return;
|
|
13569
14477
|
this.renderQueued = true;
|
|
@@ -13578,14 +14486,28 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
13578
14486
|
const doBody = this.pendingBodyRender;
|
|
13579
14487
|
this.pendingHeaderRender = false;
|
|
13580
14488
|
this.pendingBodyRender = false;
|
|
13581
|
-
|
|
13582
|
-
|
|
14489
|
+
try {
|
|
14490
|
+
if (doHeader)
|
|
14491
|
+
this.renderHeader();
|
|
14492
|
+
}
|
|
14493
|
+
catch (error) {
|
|
14494
|
+
// Both flags are already consumed. Letting a header failure escape
|
|
14495
|
+
// would cancel the body paint queued alongside it and leave the rows
|
|
14496
|
+
// permanently stale — the exact "header fine, rows blank" symptom.
|
|
14497
|
+
console.error('Error rendering table header:', error);
|
|
14498
|
+
}
|
|
13583
14499
|
if (doBody)
|
|
13584
14500
|
this.renderBody();
|
|
13585
14501
|
});
|
|
13586
14502
|
});
|
|
13587
14503
|
}
|
|
13588
14504
|
debouncedDataRequest() {
|
|
14505
|
+
// The supersede decision belongs to the moment the NEW request is decided
|
|
14506
|
+
// on, not to the moment its timeout fires. Bumping here closes the 150ms
|
|
14507
|
+
// window in which an already-obsolete in-flight response still satisfied
|
|
14508
|
+
// `seq === this.dataRequestSeq` and painted rows for the previous sort /
|
|
14509
|
+
// filter / page under a header that already advertises the new one.
|
|
14510
|
+
this.dataRequestSeq++;
|
|
13589
14511
|
// Set loading immediately for instant feedback
|
|
13590
14512
|
if (!this.loading) {
|
|
13591
14513
|
this.loading = true;
|
|
@@ -13673,6 +14595,72 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
13673
14595
|
/* no border or radius — handled by .table-frame */
|
|
13674
14596
|
}
|
|
13675
14597
|
|
|
14598
|
+
/* column-fit="squish": the frame's width is the constraint.
|
|
14599
|
+
*
|
|
14600
|
+
* The arithmetic (syncColumnWidthsToFrame → fitVisibleColumns) already
|
|
14601
|
+
* lands the columns on the frame's content width, and fixed layout hands
|
|
14602
|
+
* any rounding slack back to the columns, so the rightmost edge sits on
|
|
14603
|
+
* the frame's inner edge. These rules are the guarantee behind that:
|
|
14604
|
+
* hiding the horizontal overflow means a stray pixel — a sub-pixel
|
|
14605
|
+
* border, a browser rounding a percentage the other way — can never turn
|
|
14606
|
+
* into a scrollbar the mode promised would not appear. Vertical scrolling
|
|
14607
|
+
* is untouched. */
|
|
14608
|
+
:host([column-fit="squish"]) .table-frame {
|
|
14609
|
+
overflow-x: hidden;
|
|
14610
|
+
}
|
|
14611
|
+
|
|
14612
|
+
:host([column-fit="squish"]) table {
|
|
14613
|
+
max-width: 100%;
|
|
14614
|
+
}
|
|
14615
|
+
|
|
14616
|
+
/* Squished columns are narrower than their content by design, so the
|
|
14617
|
+
* content has to end in an ellipsis rather than wrap the row taller (or
|
|
14618
|
+
* push a word past the clip edge). */
|
|
14619
|
+
:host([column-fit="squish"]) th,
|
|
14620
|
+
:host([column-fit="squish"]) td {
|
|
14621
|
+
white-space: nowrap;
|
|
14622
|
+
}
|
|
14623
|
+
|
|
14624
|
+
:host([column-fit="squish"]) td > snice-cell-text,
|
|
14625
|
+
:host([column-fit="squish"]) td > .custom-cell,
|
|
14626
|
+
:host([column-fit="squish"]) td > .tree-label {
|
|
14627
|
+
display: block;
|
|
14628
|
+
overflow: hidden;
|
|
14629
|
+
text-overflow: ellipsis;
|
|
14630
|
+
}
|
|
14631
|
+
|
|
14632
|
+
/* The typed cells (email, status, link, phone, location, json, colour,
|
|
14633
|
+
* image, actions) each declare a 100px floor on their host AND on their
|
|
14634
|
+
* inner .cell-content. That floor is a sensible default at natural width
|
|
14635
|
+
* and fatal here: a squished column is routinely narrower than 100px, the
|
|
14636
|
+
* cell refuses to shrink to it, and the td's own overflow: hidden then
|
|
14637
|
+
* chops the label mid-glyph — no ellipsis, because the element that owns
|
|
14638
|
+
* the ellipsis never got small enough to trigger it.
|
|
14639
|
+
*
|
|
14640
|
+
* .cell-content lives in the cell's shadow root, so no descendant
|
|
14641
|
+
* selector from here can reach it. A custom property can: it inherits
|
|
14642
|
+
* across the boundary, the cells read it with the same 100px as the
|
|
14643
|
+
* fallback, and nothing outside a squished td sees any change. */
|
|
14644
|
+
:host([column-fit="squish"]) td {
|
|
14645
|
+
--snice-table-cell-min-width: 0;
|
|
14646
|
+
}
|
|
14647
|
+
|
|
14648
|
+
/* The header label is the table's own markup and needs the same promise:
|
|
14649
|
+
* "Department" clipped to "Departme" reads as a different word. A
|
|
14650
|
+
* sortable header puts the label in a flex row beside the sort
|
|
14651
|
+
* indicator, and a flex item only shrinks below its content once it is
|
|
14652
|
+
* allowed to — hence min-width: 0 alongside the ellipsis. A plain header
|
|
14653
|
+
* is bare text in the th, which ellipsises on the th itself. */
|
|
14654
|
+
:host([column-fit="squish"]) th {
|
|
14655
|
+
text-overflow: ellipsis;
|
|
14656
|
+
}
|
|
14657
|
+
|
|
14658
|
+
:host([column-fit="squish"]) th .sort-header > span {
|
|
14659
|
+
overflow: hidden;
|
|
14660
|
+
text-overflow: ellipsis;
|
|
14661
|
+
min-width: 0;
|
|
14662
|
+
}
|
|
14663
|
+
|
|
13676
14664
|
/* Super-header (slotted area above column headers) */
|
|
13677
14665
|
.table-superheader {
|
|
13678
14666
|
background: var(--snice-color-surface, rgb(255 255 255));
|
|
@@ -13795,9 +14783,13 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
13795
14783
|
background-color: var(--snice-color-surface-container-low, rgb(245 245 245));
|
|
13796
14784
|
}
|
|
13797
14785
|
|
|
13798
|
-
/* Row styling
|
|
14786
|
+
/* Row styling. The zebra tint is an ALPHA overlay, not a surface step:
|
|
14787
|
+
surface-container-low sits ~2 luminance points from the body surface
|
|
14788
|
+
on dark themes, so striped rendered as no striping at all there.
|
|
14789
|
+
An overlay also composites over pinned/tinted cells instead of
|
|
14790
|
+
replacing their color. */
|
|
13799
14791
|
:host([striped]) tbody tr:nth-child(even) {
|
|
13800
|
-
background-color: var(--snice-color-
|
|
14792
|
+
background-color: var(--snice-table-stripe-bg, var(--snice-color-overlay-stripe, hsl(0 0% 0% / 0.02)));
|
|
13801
14793
|
}
|
|
13802
14794
|
|
|
13803
14795
|
:host([hoverable]) tbody tr:hover {
|
|
@@ -14061,12 +15053,14 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
14061
15053
|
justify-content: center;
|
|
14062
15054
|
}
|
|
14063
15055
|
|
|
14064
|
-
/* Loading fade
|
|
14065
|
-
|
|
15056
|
+
/* Loading fade. Scoped to ROWS, never the whole tbody: with no rows the
|
|
15057
|
+
only thing in there is the loading indicator, and dimming that halves
|
|
15058
|
+
the contrast of the one element whose entire job is to be seen. */
|
|
15059
|
+
tbody > tr {
|
|
14066
15060
|
transition: opacity var(--snice-transition-normal, 250ms);
|
|
14067
15061
|
}
|
|
14068
15062
|
|
|
14069
|
-
:host([loading]) tbody {
|
|
15063
|
+
:host([loading]) tbody > tr:not(.table-message-row) {
|
|
14070
15064
|
opacity: 0.5;
|
|
14071
15065
|
}
|
|
14072
15066
|
|
|
@@ -14081,6 +15075,33 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
14081
15075
|
z-index: 2;
|
|
14082
15076
|
}
|
|
14083
15077
|
|
|
15078
|
+
/* Zero-row loading: spinner + the word, centred in the empty body. */
|
|
15079
|
+
.table-loading-state {
|
|
15080
|
+
display: flex;
|
|
15081
|
+
flex-direction: column;
|
|
15082
|
+
align-items: center;
|
|
15083
|
+
justify-content: center;
|
|
15084
|
+
gap: var(--snice-spacing-xs, 0.5rem);
|
|
15085
|
+
color: var(--snice-color-text-secondary, rgb(82 82 82));
|
|
15086
|
+
}
|
|
15087
|
+
|
|
15088
|
+
.table-loading-label {
|
|
15089
|
+
font-size: var(--snice-font-size-sm, 0.875rem);
|
|
15090
|
+
line-height: 1;
|
|
15091
|
+
}
|
|
15092
|
+
|
|
15093
|
+
/* snice-progress draws its circular track at 20% alpha, which disappears
|
|
15094
|
+
entirely on a dark surface — the ring the field report saw was a
|
|
15095
|
+
near-black hairline on near-black. Both table spinners opt out and use
|
|
15096
|
+
the border token at full strength, so the track reads in either
|
|
15097
|
+
theme and the primary arc always has something to travel around. */
|
|
15098
|
+
.table-loading-state snice-progress,
|
|
15099
|
+
.table-loading-overlay snice-progress {
|
|
15100
|
+
--progress-track-opacity: 1;
|
|
15101
|
+
--progress-bg: var(--snice-color-border, rgb(226 226 226));
|
|
15102
|
+
--progress-color: var(--snice-color-primary, rgb(37 99 235));
|
|
15103
|
+
}
|
|
15104
|
+
|
|
14084
15105
|
.no-data {
|
|
14085
15106
|
text-align: center;
|
|
14086
15107
|
padding: var(--snice-spacing-lg, 1.5rem);
|
|
@@ -14222,10 +15243,58 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
14222
15243
|
background: var(--snice-color-primary, rgb(37 99 235));
|
|
14223
15244
|
}
|
|
14224
15245
|
|
|
14225
|
-
/* Pinned
|
|
15246
|
+
/* Pinned columns.
|
|
15247
|
+
|
|
15248
|
+
A frozen column must be opaque — scrolled cells pass underneath it —
|
|
15249
|
+
and a header keeps the header's own surface rather than borrowing the
|
|
15250
|
+
body's. The frozen EDGE (the inner-most pinned column on each side) is
|
|
15251
|
+
what the reader actually needs to see: a heavier divider plus a soft
|
|
15252
|
+
shadow cast INTO the scrolling region, mirrored per side. Between two
|
|
15253
|
+
columns pinned to the same side there is no boundary to mark, so they
|
|
15254
|
+
keep the ordinary cell border.
|
|
15255
|
+
|
|
15256
|
+
The shadow is a border-token tint, not black: a black drop shadow is
|
|
15257
|
+
invisible against a dark surface, which is exactly how pinning came to
|
|
15258
|
+
have no affordance at all in the dark theme. */
|
|
14226
15259
|
.pinned-cell {
|
|
14227
15260
|
background: var(--snice-color-surface, rgb(255 255 255));
|
|
14228
|
-
|
|
15261
|
+
}
|
|
15262
|
+
|
|
15263
|
+
th.pinned-cell {
|
|
15264
|
+
background: var(--snice-color-surface-container-low, rgb(245 245 245));
|
|
15265
|
+
}
|
|
15266
|
+
|
|
15267
|
+
.pinned-cell--left.pinned-cell--edge {
|
|
15268
|
+
border-right: 2px solid var(--snice-color-border, rgb(226 226 226));
|
|
15269
|
+
box-shadow: 6px 0 8px -6px var(--snice-color-border, rgb(226 226 226));
|
|
15270
|
+
}
|
|
15271
|
+
|
|
15272
|
+
.pinned-cell--right.pinned-cell--edge {
|
|
15273
|
+
border-left: 2px solid var(--snice-color-border, rgb(226 226 226));
|
|
15274
|
+
box-shadow: -6px 0 8px -6px var(--snice-color-border, rgb(226 226 226));
|
|
15275
|
+
}
|
|
15276
|
+
|
|
15277
|
+
/* A right-pinned header is often the last cell in the row, where the
|
|
15278
|
+
shared rule above strips the right border. Its divider is on the left,
|
|
15279
|
+
so nothing to restore — but the edge border must survive that rule. */
|
|
15280
|
+
th:last-child.pinned-cell--right.pinned-cell--edge,
|
|
15281
|
+
td:last-child.pinned-cell--right.pinned-cell--edge {
|
|
15282
|
+
border-right: none;
|
|
15283
|
+
}
|
|
15284
|
+
|
|
15285
|
+
/* Pin marker: the only cue when the table is wide enough not to scroll. */
|
|
15286
|
+
.pin-indicator {
|
|
15287
|
+
display: inline-flex;
|
|
15288
|
+
align-items: center;
|
|
15289
|
+
margin-left: var(--snice-spacing-2xs, 0.25rem);
|
|
15290
|
+
color: var(--snice-color-text-secondary, rgb(82 82 82));
|
|
15291
|
+
opacity: 0.65;
|
|
15292
|
+
vertical-align: middle;
|
|
15293
|
+
pointer-events: none;
|
|
15294
|
+
}
|
|
15295
|
+
|
|
15296
|
+
.pin-indicator--right {
|
|
15297
|
+
transform: scaleX(-1);
|
|
14229
15298
|
}
|
|
14230
15299
|
|
|
14231
15300
|
/* Density: compact */
|
|
@@ -14648,7 +15717,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
14648
15717
|
}
|
|
14649
15718
|
|
|
14650
15719
|
:host([striped]) .snice-table--slotted .table-body::slotted(snice-row:nth-child(even)) {
|
|
14651
|
-
background-color: var(--snice-color-
|
|
15720
|
+
background-color: var(--snice-table-stripe-bg, var(--snice-color-overlay-stripe, hsl(0 0% 0% / 0.02)));
|
|
14652
15721
|
}
|
|
14653
15722
|
|
|
14654
15723
|
:host([hoverable]) .snice-table--slotted .table-body::slotted(snice-row:hover) {
|
|
@@ -14754,9 +15823,18 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
14754
15823
|
}
|
|
14755
15824
|
container.appendChild(controlsDiv);
|
|
14756
15825
|
}
|
|
14757
|
-
// Re-derive the
|
|
14758
|
-
// fullscreen toggled)
|
|
15826
|
+
// Re-derive the frame-dependent layout whenever the frame's box changes
|
|
15827
|
+
// (host resized, fullscreen toggled): the columns share its width, and the
|
|
15828
|
+
// leftover space below the rows changes with its height.
|
|
14759
15829
|
handleFrameResize() {
|
|
15830
|
+
// Only when the width actually moved. Fitting rewrites header widths, which
|
|
15831
|
+
// can change row heights, which can flip the frame's vertical scrollbar and
|
|
15832
|
+
// call us straight back — the guard stops that becoming a cycle.
|
|
15833
|
+
const width = this.shadowRoot?.querySelector('.table-frame')?.clientWidth ?? 0;
|
|
15834
|
+
if (width > 0 && width !== this.lastFittedFrameWidth) {
|
|
15835
|
+
this.lastFittedFrameWidth = width;
|
|
15836
|
+
this.syncColumnWidthsToFrame();
|
|
15837
|
+
}
|
|
14760
15838
|
this.updateFillRow();
|
|
14761
15839
|
}
|
|
14762
15840
|
async initialize() {
|
|
@@ -14859,7 +15937,18 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
14859
15937
|
}
|
|
14860
15938
|
}
|
|
14861
15939
|
this.data.splice(toIndex, 0, item);
|
|
14862
|
-
|
|
15940
|
+
// Reordering the SAME rows is not a delivery: a failed reload keeps its
|
|
15941
|
+
// error until data actually arrives, so a drag must not dismiss it.
|
|
15942
|
+
// Everything else the data watcher does (row index, detail prep, render)
|
|
15943
|
+
// is still wanted here — hence a narrow flag rather than
|
|
15944
|
+
// `settingSortedData`, which skips all of it.
|
|
15945
|
+
this.reorderingRows = true;
|
|
15946
|
+
try {
|
|
15947
|
+
this.data = [...this.data];
|
|
15948
|
+
}
|
|
15949
|
+
finally {
|
|
15950
|
+
this.reorderingRows = false;
|
|
15951
|
+
}
|
|
14863
15952
|
this.selectedRows = selectedRows
|
|
14864
15953
|
.map((row) => this.indexOfRow(row))
|
|
14865
15954
|
.filter((index) => index >= 0);
|
|
@@ -14973,7 +16062,17 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
14973
16062
|
this.renderHeader(); // Re-render header to show/hide sort indicators
|
|
14974
16063
|
}
|
|
14975
16064
|
handleSelectableChange() {
|
|
14976
|
-
this.render(); //
|
|
16065
|
+
this.render(); // host template (the structural mode can change)
|
|
16066
|
+
// The checkbox tool column is structural: like the sibling selection-mode
|
|
16067
|
+
// watcher, a post-mount toggle has to rebuild the header (select-all th)
|
|
16068
|
+
// AND every row (row checkbox) — the host template render leaves the
|
|
16069
|
+
// imperatively built thead/tbody untouched, so without this the two
|
|
16070
|
+
// disagree and every data cell sits one column off its header. Rows are
|
|
16071
|
+
// never recycled stale across the toggle: computeStructuralSig folds in
|
|
16072
|
+
// `sel=`. Skipped before the first render — the initial paint reads
|
|
16073
|
+
// `selectable` itself.
|
|
16074
|
+
if (this.thead)
|
|
16075
|
+
this.scheduleRender('both');
|
|
14977
16076
|
queueMicrotask(() => this.syncSlottedSelectionState());
|
|
14978
16077
|
}
|
|
14979
16078
|
// C1: reactive columns. A new column set can change cell types/formatting
|
|
@@ -14982,8 +16081,12 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
14982
16081
|
// construction is deferred to the coalescing queue (setter-stack landmine).
|
|
14983
16082
|
handleColumnsAssignment() {
|
|
14984
16083
|
this.renderedRows = new Map();
|
|
16084
|
+
// A new array is a NEW CONFIGURATION: its order / pinned / width / declared
|
|
16085
|
+
// set replace whatever the column model is holding, and states for columns
|
|
16086
|
+
// it no longer declares are dropped. initialize() is the render-path resync
|
|
16087
|
+
// that must NOT do this (it would undo a drag-resize on every paint).
|
|
14985
16088
|
if (this.columns.length > 0)
|
|
14986
|
-
this.columnManager.
|
|
16089
|
+
this.columnManager.applyConfiguration(this.columns, this);
|
|
14987
16090
|
// F: a column set change can add/remove an `aggregate` — resync the grouping
|
|
14988
16091
|
// model's aggregator list and drop its stale flattened snapshot.
|
|
14989
16092
|
this.grouping.setColumns(this.columns);
|
|
@@ -15056,9 +16159,21 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15056
16159
|
// and the row-index map, then renders the body. The internal sort path
|
|
15057
16160
|
// (sortLocalData) reassigns this.data with a re-sorted view of the SAME rows
|
|
15058
16161
|
// and owns its own snapshot/render — settingSortedData tells us to skip.
|
|
15059
|
-
handleDataAssignment() {
|
|
16162
|
+
handleDataAssignment(oldVal) {
|
|
15060
16163
|
if (this.settingSortedData)
|
|
15061
16164
|
return;
|
|
16165
|
+
// A load error describes the LAST delivery ATTEMPT, so a dataset arriving
|
|
16166
|
+
// from any source supersedes it — including the app falling back to local
|
|
16167
|
+
// rows (or to none) after the remote load failed. Clearing it only on the
|
|
16168
|
+
// remote success path left `loadError` and the `table--error` host class
|
|
16169
|
+
// alive forever, so the next zero-row render showed the stale ⚠️ where the
|
|
16170
|
+
// empty state belonged.
|
|
16171
|
+
//
|
|
16172
|
+
// A REORDER is not an arrival: the row-reorder handler republishes the same
|
|
16173
|
+
// rows in a new array, and dragging a row must not dismiss the ⚠️ the
|
|
16174
|
+
// contract says survives until data actually arrives.
|
|
16175
|
+
if (!this.reorderingRows)
|
|
16176
|
+
this.clearLoadError();
|
|
15062
16177
|
this.unsortedData = [...this.data];
|
|
15063
16178
|
// C3: rows delivered while a client-side sort is active must land in that
|
|
15064
16179
|
// sorted order — the currentSort watcher only fires when the sort itself
|
|
@@ -15067,11 +16182,25 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15067
16182
|
if (this.mode !== 'remote' && this.currentSort.length > 0)
|
|
15068
16183
|
this.applyLocalSortOrder();
|
|
15069
16184
|
this.rebuildRowIndex();
|
|
16185
|
+
// Re-delivering the SAME rows in a different order must not move the
|
|
16186
|
+
// selection to whatever row inherited the index (no-op for a new dataset).
|
|
16187
|
+
if (oldVal)
|
|
16188
|
+
this.remapSelectionAfterReorder(oldVal);
|
|
15070
16189
|
this.masterDetail.prepare(this.data, row => this.rowValueSignature(row));
|
|
15071
16190
|
if (this.settingDataImperative)
|
|
15072
16191
|
return; // imperative setData(): caller renders
|
|
15073
16192
|
this.scheduleRender('body');
|
|
15074
16193
|
}
|
|
16194
|
+
/**
|
|
16195
|
+
* Drop the failed-load state: the ⚠️ message row's text and the `table--error`
|
|
16196
|
+
* host class always move together, so they are cleared together and from one
|
|
16197
|
+
* place. Idempotent, and deliberately does NOT render — every caller is either
|
|
16198
|
+
* mid-delivery (the data watcher renders) or already renders itself.
|
|
16199
|
+
*/
|
|
16200
|
+
clearLoadError() {
|
|
16201
|
+
this.loadError = null;
|
|
16202
|
+
this.classList.remove('table--error');
|
|
16203
|
+
}
|
|
15075
16204
|
handleDataChange() {
|
|
15076
16205
|
this.renderBody();
|
|
15077
16206
|
this.updateLoadingOverlay();
|
|
@@ -15091,11 +16220,12 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15091
16220
|
overlay = document.createElement('div');
|
|
15092
16221
|
overlay.className = 'table-loading-overlay';
|
|
15093
16222
|
overlay.setAttribute('part', 'loading-overlay');
|
|
15094
|
-
|
|
15095
|
-
|
|
15096
|
-
|
|
15097
|
-
|
|
15098
|
-
overlay.
|
|
16223
|
+
// The overlay sits ON TOP of readable rows, so it carries its label as
|
|
16224
|
+
// an aria-label rather than visible text — but it must still announce.
|
|
16225
|
+
overlay.setAttribute('role', 'status');
|
|
16226
|
+
overlay.setAttribute('aria-live', 'polite');
|
|
16227
|
+
overlay.setAttribute('aria-label', LOADING_LABEL);
|
|
16228
|
+
overlay.appendChild(this.createLoadingSpinner('medium'));
|
|
15099
16229
|
frame.appendChild(overlay);
|
|
15100
16230
|
}
|
|
15101
16231
|
else if (!show && overlay) {
|
|
@@ -15158,11 +16288,26 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15158
16288
|
this.sortLocalData();
|
|
15159
16289
|
}
|
|
15160
16290
|
handleCurrentPageChange() {
|
|
16291
|
+
// A clamp written by the display model itself is not a navigation — see
|
|
16292
|
+
// clampCurrentPage().
|
|
16293
|
+
if (this.clampingPage)
|
|
16294
|
+
return;
|
|
15161
16295
|
if (this.paginationMode === 'server' && this.mode === 'remote')
|
|
15162
16296
|
this.debouncedDataRequest();
|
|
15163
16297
|
else
|
|
15164
16298
|
this.scheduleRender('body');
|
|
15165
16299
|
}
|
|
16300
|
+
clampCurrentPage(page) {
|
|
16301
|
+
if (page === this.currentPage)
|
|
16302
|
+
return;
|
|
16303
|
+
this.clampingPage = true;
|
|
16304
|
+
try {
|
|
16305
|
+
this.currentPage = page;
|
|
16306
|
+
}
|
|
16307
|
+
finally {
|
|
16308
|
+
this.clampingPage = false;
|
|
16309
|
+
}
|
|
16310
|
+
}
|
|
15166
16311
|
handlePageSizeChange() {
|
|
15167
16312
|
this.currentPage = 1;
|
|
15168
16313
|
if (this.paginationMode === 'server' && this.mode === 'remote')
|
|
@@ -15178,7 +16323,9 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15178
16323
|
}
|
|
15179
16324
|
handleListChange() {
|
|
15180
16325
|
this.renderedRows = new Map();
|
|
15181
|
-
|
|
16326
|
+
// The header is part of the mode: entering list mode with a renderer drops
|
|
16327
|
+
// the columnar header, and leaving it must bring the header back.
|
|
16328
|
+
this.scheduleRender('both');
|
|
15182
16329
|
}
|
|
15183
16330
|
handleEditableChange() {
|
|
15184
16331
|
if (this.editable)
|
|
@@ -15236,6 +16383,17 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15236
16383
|
this.initColumnMenu();
|
|
15237
16384
|
this.scheduleRender('header');
|
|
15238
16385
|
}
|
|
16386
|
+
/**
|
|
16387
|
+
* Switching fit policy re-decides every unsized column's width, so the frame
|
|
16388
|
+
* has to be re-fitted from scratch: the cached width would otherwise make the
|
|
16389
|
+
* resize observer's "did the frame move?" guard swallow the very next pass.
|
|
16390
|
+
*/
|
|
16391
|
+
handleColumnFitChange() {
|
|
16392
|
+
this.columnManager.setFitMode(this.columnFit);
|
|
16393
|
+
this.lastFittedFrameWidth = 0;
|
|
16394
|
+
this.syncColumnWidthsToFrame();
|
|
16395
|
+
this.scheduleRender('header');
|
|
16396
|
+
}
|
|
15239
16397
|
/** Column definitions in their actual painted order. Column state owns
|
|
15240
16398
|
* visibility/order; pinned columns are partitioned to their physical edges
|
|
15241
16399
|
* so right-pinned cells do not overlap later unpinned columns. */
|
|
@@ -15252,9 +16410,124 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15252
16410
|
.map((state) => byKey.get(state.key))
|
|
15253
16411
|
.filter((column) => !!column);
|
|
15254
16412
|
}
|
|
16413
|
+
/**
|
|
16414
|
+
* Cells one full-width row spans: every visible data column plus the tool
|
|
16415
|
+
* columns that precede them. Group, aggregate, detail, empty-state, and
|
|
16416
|
+
* virtual-spacer rows all measure themselves against this — a row that spans
|
|
16417
|
+
* MORE than the table has invents columns, and `table-layout: fixed` then
|
|
16418
|
+
* spends the frame's leftover width on the phantoms.
|
|
16419
|
+
*/
|
|
16420
|
+
getTotalColumnSpan() {
|
|
16421
|
+
const managed = this.columnManager.getAllStates();
|
|
16422
|
+
const dataColumns = managed.length > 0
|
|
16423
|
+
? this.columnManager.getVisibleColumns().length
|
|
16424
|
+
: this.columns.length;
|
|
16425
|
+
return dataColumns
|
|
16426
|
+
+ (this.hasSelectionColumn() ? 1 : 0)
|
|
16427
|
+
+ (this.masterDetail.isEnabled() ? 1 : 0)
|
|
16428
|
+
+ (this.rowReorder && this.rowDnD.isEnabled() ? 1 : 0);
|
|
16429
|
+
}
|
|
16430
|
+
/**
|
|
16431
|
+
* Re-fit the columns nobody sized to the frame they are painted in.
|
|
16432
|
+
*
|
|
16433
|
+
* A column width is a CONTENT width, so the frame's budget has to be reduced
|
|
16434
|
+
* by each column's own padding + border and by the fixed tool columns before
|
|
16435
|
+
* it can be shared out. Both are read off the live header — the previous
|
|
16436
|
+
* render's, on the first pass — rather than guessed, so the fitted table
|
|
16437
|
+
* lands on the frame's width instead of a few pixels past it.
|
|
16438
|
+
*
|
|
16439
|
+
* Widths are written straight onto the header cells: under fixed layout the
|
|
16440
|
+
* first row owns the column grid, so no body repaint is needed and this can
|
|
16441
|
+
* run inside a resize observer without a render cascade.
|
|
16442
|
+
*/
|
|
16443
|
+
syncColumnWidthsToFrame() {
|
|
16444
|
+
// The policy is the table's, the arithmetic is the manager's: hand it over
|
|
16445
|
+
// before every fit so a `columnFit` change is honoured on the next pass
|
|
16446
|
+
// whether it arrived as a property, an attribute, or an upgrade.
|
|
16447
|
+
this.columnManager.setFitMode(this.columnFit);
|
|
16448
|
+
const frame = this.shadowRoot?.querySelector('.table-frame');
|
|
16449
|
+
const headerRow = this.shadowRoot?.querySelector('thead tr.column-header-row');
|
|
16450
|
+
if (!frame || !headerRow)
|
|
16451
|
+
return false;
|
|
16452
|
+
const available = frame.clientWidth;
|
|
16453
|
+
// Layoutless DOM (or a display:none host): every measurement is 0 and
|
|
16454
|
+
// fitting to it would slam the columns to their minimums for nothing.
|
|
16455
|
+
if (!(available > 0))
|
|
16456
|
+
return false;
|
|
16457
|
+
const dataCells = Array.from(headerRow.querySelectorAll('th[data-key]'));
|
|
16458
|
+
// An active list renderer drops the columnar header entirely; there is no
|
|
16459
|
+
// column geometry to fit and no cell to measure the chrome from.
|
|
16460
|
+
if (dataCells.length === 0)
|
|
16461
|
+
return false;
|
|
16462
|
+
let reserved = 0;
|
|
16463
|
+
for (const cell of Array.from(headerRow.children)) {
|
|
16464
|
+
if (!cell.hasAttribute('data-key'))
|
|
16465
|
+
reserved += cell.getBoundingClientRect().width;
|
|
16466
|
+
}
|
|
16467
|
+
const chrome = horizontalCellChrome(dataCells[0]);
|
|
16468
|
+
if (!this.columnManager.fitVisibleColumns(available - reserved, chrome))
|
|
16469
|
+
return false;
|
|
16470
|
+
for (const cell of dataCells) {
|
|
16471
|
+
const state = this.columnManager.getState(cell.getAttribute('data-key') || '');
|
|
16472
|
+
if (state)
|
|
16473
|
+
cell.style.width = `${state.width}px`;
|
|
16474
|
+
}
|
|
16475
|
+
return true;
|
|
16476
|
+
}
|
|
16477
|
+
/**
|
|
16478
|
+
* Sticky geometry plus the visible affordance for a pinned cell.
|
|
16479
|
+
*
|
|
16480
|
+
* Freezing a column is only communicated if the reader can see where the
|
|
16481
|
+
* frozen region ends, so the inner-most pinned column on each side carries
|
|
16482
|
+
* the divider (`--edge`); the header additionally carries a pin marker, which
|
|
16483
|
+
* is the only cue when the table happens to be wide enough not to scroll.
|
|
16484
|
+
*/
|
|
16485
|
+
applyPinnedCell(cell, key, state, zIndex) {
|
|
16486
|
+
if (state.pinned !== 'left' && state.pinned !== 'right')
|
|
16487
|
+
return;
|
|
16488
|
+
const side = state.pinned;
|
|
16489
|
+
const columns = side === 'left'
|
|
16490
|
+
? this.columnManager.getPinnedLeft()
|
|
16491
|
+
: this.columnManager.getPinnedRight();
|
|
16492
|
+
const offsets = side === 'left'
|
|
16493
|
+
? this.columnManager.getPinnedLeftOffsets()
|
|
16494
|
+
: this.columnManager.getPinnedRightOffsets();
|
|
16495
|
+
// The edge of the frozen region: the last left-pinned column, the first
|
|
16496
|
+
// right-pinned one. Inner boundaries between two pinned columns are just
|
|
16497
|
+
// ordinary cell borders.
|
|
16498
|
+
const edgeKey = side === 'left' ? columns[columns.length - 1]?.key : columns[0]?.key;
|
|
16499
|
+
cell.classList.add('pinned-cell', `pinned-cell--${side}`);
|
|
16500
|
+
if (key === edgeKey)
|
|
16501
|
+
cell.classList.add('pinned-cell--edge');
|
|
16502
|
+
cell.style.position = 'sticky';
|
|
16503
|
+
cell.style[side] = `${offsets.get(key) ?? 0}px`;
|
|
16504
|
+
cell.style.zIndex = zIndex;
|
|
16505
|
+
}
|
|
16506
|
+
/** The pin marker a frozen header carries, so pinning reads at any width. */
|
|
16507
|
+
static pinIndicator(side) {
|
|
16508
|
+
return `<span class="pin-indicator pin-indicator--${side}" aria-hidden="true">`
|
|
16509
|
+
+ `<svg width="10" height="10" viewBox="0 0 16 16" fill="currentColor">`
|
|
16510
|
+
+ `<path d="M9.5 1a1 1 0 0 0-.7 1.7l.3.3-3 3-2.4-.5a1 1 0 0 0-.9 1.7l3 3-3.6 3.6a.6.6 0 0 0 .8.8L6.6 12l3 3a1 1 0 0 0 1.7-.9l-.5-2.4 3-3 .3.3a1 1 0 0 0 1.4-1.4l-5.3-5.3A1 1 0 0 0 9.5 1Z"/>`
|
|
16511
|
+
+ `</svg></span>`;
|
|
16512
|
+
}
|
|
16513
|
+
/**
|
|
16514
|
+
* List mode is only "list mode" once a renderer is installed: `list` alone is
|
|
16515
|
+
* a border treatment and still paints normal columnar cells. With a renderer
|
|
16516
|
+
* every row collapses to one full-width cell, so there is no column geometry
|
|
16517
|
+
* left for a header to label.
|
|
16518
|
+
*/
|
|
16519
|
+
usesListRowRenderer() {
|
|
16520
|
+
return !!(this.list && this.listRenderer);
|
|
16521
|
+
}
|
|
15255
16522
|
renderHeader() {
|
|
15256
16523
|
if (!this.thead)
|
|
15257
16524
|
return;
|
|
16525
|
+
// A card/directory list has no columns, so a columnar header would promise
|
|
16526
|
+
// fields that align with nothing. Suppress the data-column headers (and the
|
|
16527
|
+
// equally columnar group and header-filter rows) while a list renderer is
|
|
16528
|
+
// active; the tool columns below still have real body cells to head, so
|
|
16529
|
+
// select-all is kept rather than lost as collateral.
|
|
16530
|
+
const listRows = this.usesListRowRenderer();
|
|
15258
16531
|
const headerRow = document.createElement('tr');
|
|
15259
16532
|
headerRow.className = 'column-header-row';
|
|
15260
16533
|
// Tool column headers — must match createRow order
|
|
@@ -15292,7 +16565,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15292
16565
|
}
|
|
15293
16566
|
headerRow.appendChild(selectCell);
|
|
15294
16567
|
}
|
|
15295
|
-
const visibleColumns = this.getVisibleColumnDefinitions();
|
|
16568
|
+
const visibleColumns = listRows ? [] : this.getVisibleColumnDefinitions();
|
|
15296
16569
|
visibleColumns.forEach(column => {
|
|
15297
16570
|
const th = document.createElement('th');
|
|
15298
16571
|
th.setAttribute('data-key', column.key);
|
|
@@ -15309,23 +16582,10 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15309
16582
|
}
|
|
15310
16583
|
// Apply column width
|
|
15311
16584
|
const state = this.columnManager.getState(column.key);
|
|
16585
|
+
const pinnedSide = state?.pinned === 'left' || state?.pinned === 'right' ? state.pinned : null;
|
|
15312
16586
|
if (state) {
|
|
15313
16587
|
th.style.width = `${state.width}px`;
|
|
15314
|
-
|
|
15315
|
-
if (state.pinned === 'left') {
|
|
15316
|
-
const offsets = this.columnManager.getPinnedLeftOffsets();
|
|
15317
|
-
th.classList.add('pinned-cell');
|
|
15318
|
-
th.style.position = 'sticky';
|
|
15319
|
-
th.style.left = `${offsets.get(column.key) ?? 0}px`;
|
|
15320
|
-
th.style.zIndex = '2';
|
|
15321
|
-
}
|
|
15322
|
-
else if (state.pinned === 'right') {
|
|
15323
|
-
const offsets = this.columnManager.getPinnedRightOffsets();
|
|
15324
|
-
th.classList.add('pinned-cell');
|
|
15325
|
-
th.style.position = 'sticky';
|
|
15326
|
-
th.style.right = `${offsets.get(column.key) ?? 0}px`;
|
|
15327
|
-
th.style.zIndex = '2';
|
|
15328
|
-
}
|
|
16588
|
+
this.applyPinnedCell(th, column.key, state, '2');
|
|
15329
16589
|
}
|
|
15330
16590
|
if (this.sortable && column.sortable !== false) {
|
|
15331
16591
|
th.classList.add('sortable');
|
|
@@ -15333,11 +16593,22 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15333
16593
|
// A role=button must be focusable and named, or the keyboard cannot
|
|
15334
16594
|
// sort at all. Matches the declarative header in snice-header.ts.
|
|
15335
16595
|
th.setAttribute('tabindex', '0');
|
|
15336
|
-
|
|
16596
|
+
// Frozen columns say so: the divider is a visual cue only.
|
|
16597
|
+
th.setAttribute('aria-label', pinnedSide
|
|
16598
|
+
? `Sort by ${column.label}, pinned ${pinnedSide}`
|
|
16599
|
+
: `Sort by ${column.label}`);
|
|
15337
16600
|
th.innerHTML = this.renderSortableHeader(column);
|
|
15338
16601
|
}
|
|
15339
16602
|
else {
|
|
15340
16603
|
th.textContent = column.label;
|
|
16604
|
+
if (pinnedSide)
|
|
16605
|
+
th.setAttribute('aria-label', `${column.label}, pinned ${pinnedSide}`);
|
|
16606
|
+
}
|
|
16607
|
+
if (pinnedSide) {
|
|
16608
|
+
// Inside the label, not after it: `.sort-header` is a full-width flex
|
|
16609
|
+
// row, so a sibling would be pushed onto a second line.
|
|
16610
|
+
const label = th.querySelector('.sort-header > span') ?? th;
|
|
16611
|
+
label.insertAdjacentHTML('beforeend', SniceTable.pinIndicator(pinnedSide));
|
|
15341
16612
|
}
|
|
15342
16613
|
// Filter indicator
|
|
15343
16614
|
if (this.filterEngine.hasColumnFilter(column.key)) {
|
|
@@ -15390,7 +16661,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15390
16661
|
});
|
|
15391
16662
|
this.thead.innerHTML = '';
|
|
15392
16663
|
// Column groups header row (if any)
|
|
15393
|
-
const groups = this.columnManager.getColumnGroups();
|
|
16664
|
+
const groups = listRows ? [] : this.columnManager.getColumnGroups();
|
|
15394
16665
|
if (groups.length > 0) {
|
|
15395
16666
|
const groupRow = document.createElement('tr');
|
|
15396
16667
|
groupRow.className = 'column-group-row';
|
|
@@ -15412,9 +16683,12 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15412
16683
|
groupRow.appendChild(groupedHeaders.content);
|
|
15413
16684
|
this.thead.appendChild(groupRow);
|
|
15414
16685
|
}
|
|
15415
|
-
|
|
16686
|
+
// A list-mode header row with no tool columns has nothing to show; leaving
|
|
16687
|
+
// it out keeps the thead empty so it paints no strip above the cards.
|
|
16688
|
+
if (!listRows || headerRow.children.length > 0)
|
|
16689
|
+
this.thead.appendChild(headerRow);
|
|
15416
16690
|
// Header filter row (if enabled)
|
|
15417
|
-
if (this.headerFilters) {
|
|
16691
|
+
if (this.headerFilters && !listRows) {
|
|
15418
16692
|
const filterRow = document.createElement('tr');
|
|
15419
16693
|
filterRow.className = 'header-filter-row';
|
|
15420
16694
|
if (this.rowReorder && this.rowDnD.isEnabled()) {
|
|
@@ -15451,6 +16725,11 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15451
16725
|
});
|
|
15452
16726
|
this.thead.appendChild(filterRow);
|
|
15453
16727
|
}
|
|
16728
|
+
// The header is in the DOM, so its real chrome and tool-column widths are
|
|
16729
|
+
// measurable: fit the unsized columns to the frame before the browser
|
|
16730
|
+
// paints. Widths land on the header cells that were just created, so this
|
|
16731
|
+
// never re-enters renderHeader.
|
|
16732
|
+
this.syncColumnWidthsToFrame();
|
|
15454
16733
|
}
|
|
15455
16734
|
renderSortableHeader(column) {
|
|
15456
16735
|
const sortItem = this.currentSort.find(s => s.column === column.key);
|
|
@@ -15482,9 +16761,90 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15482
16761
|
</div>
|
|
15483
16762
|
`;
|
|
15484
16763
|
}
|
|
16764
|
+
/**
|
|
16765
|
+
* The single message row a zero-row body shows: loading spinner, load error,
|
|
16766
|
+
* or the empty state (the `empty-state` slot cloned fresh, else the default
|
|
16767
|
+
* placeholder). Returns null when there is data to render. Shared by the
|
|
16768
|
+
* ordinary and virtualized paths — the virtualizer wipes the tbody itself, so
|
|
16769
|
+
* it asks for this row rather than leaving a virtualized empty table blank.
|
|
16770
|
+
*/
|
|
16771
|
+
/**
|
|
16772
|
+
* The spinner both loading affordances draw. `snice-progress` paints its
|
|
16773
|
+
* track at 20% alpha, which vanishes against a dark surface — the table pins
|
|
16774
|
+
* `--progress-track-opacity` in its own stylesheet so the ring reads on any
|
|
16775
|
+
* theme; the indicator arc keeps the primary color.
|
|
16776
|
+
*/
|
|
16777
|
+
createLoadingSpinner(size) {
|
|
16778
|
+
const spinner = document.createElement('snice-progress');
|
|
16779
|
+
spinner.setAttribute('variant', 'circular');
|
|
16780
|
+
spinner.setAttribute('indeterminate', '');
|
|
16781
|
+
spinner.setAttribute('size', size);
|
|
16782
|
+
return spinner;
|
|
16783
|
+
}
|
|
16784
|
+
createEmptyBodyRow() {
|
|
16785
|
+
if (this.data.length > 0 || this.columns.length === 0)
|
|
16786
|
+
return null;
|
|
16787
|
+
const toolCols = (this.hasSelectionColumn() ? 1 : 0)
|
|
16788
|
+
+ (this.masterDetail.isEnabled() ? 1 : 0)
|
|
16789
|
+
+ (this.rowReorder && this.rowDnD.isEnabled() ? 1 : 0);
|
|
16790
|
+
const tr = document.createElement('tr');
|
|
16791
|
+
tr.className = 'table-message-row';
|
|
16792
|
+
const td = document.createElement('td');
|
|
16793
|
+
td.colSpan = this.columns.length + toolCols;
|
|
16794
|
+
td.className = 'no-data';
|
|
16795
|
+
tr.appendChild(td);
|
|
16796
|
+
if (this.loading) {
|
|
16797
|
+
// A bare ring in an otherwise empty rectangle reads as a rendering
|
|
16798
|
+
// artefact, not as "we are fetching your rows": at `small` it is 24px of
|
|
16799
|
+
// low-contrast stroke with nothing naming it. The zero-row body has the
|
|
16800
|
+
// space for a full-size spinner AND the word, and the pair is what makes
|
|
16801
|
+
// the state legible — visually and to a screen reader.
|
|
16802
|
+
const status = document.createElement('div');
|
|
16803
|
+
status.className = 'table-loading-state';
|
|
16804
|
+
status.setAttribute('role', 'status');
|
|
16805
|
+
status.setAttribute('aria-live', 'polite');
|
|
16806
|
+
status.appendChild(this.createLoadingSpinner('medium'));
|
|
16807
|
+
const label = document.createElement('span');
|
|
16808
|
+
label.className = 'table-loading-label';
|
|
16809
|
+
label.textContent = LOADING_LABEL;
|
|
16810
|
+
status.appendChild(label);
|
|
16811
|
+
td.appendChild(status);
|
|
16812
|
+
}
|
|
16813
|
+
else if (this.loadError) {
|
|
16814
|
+
// Surfaces a failed remote load instead of silently falling through to
|
|
16815
|
+
// the generic "No data" empty state.
|
|
16816
|
+
const errorDiv = document.createElement('div');
|
|
16817
|
+
errorDiv.className = 'table-error-message';
|
|
16818
|
+
errorDiv.textContent = `⚠️ ${this.loadError}`;
|
|
16819
|
+
td.appendChild(errorDiv);
|
|
16820
|
+
}
|
|
16821
|
+
else {
|
|
16822
|
+
const slotted = this.querySelector('[slot="empty-state"]');
|
|
16823
|
+
if (slotted) {
|
|
16824
|
+
td.appendChild(slotted.cloneNode(true));
|
|
16825
|
+
}
|
|
16826
|
+
else {
|
|
16827
|
+
const empty = document.createElement('snice-empty-state');
|
|
16828
|
+
empty.setAttribute('size', 'small');
|
|
16829
|
+
empty.setAttribute('icon', '📭');
|
|
16830
|
+
empty.setAttribute('title', 'No data');
|
|
16831
|
+
empty.setAttribute('description', 'No records to display.');
|
|
16832
|
+
td.appendChild(empty);
|
|
16833
|
+
}
|
|
16834
|
+
}
|
|
16835
|
+
return tr;
|
|
16836
|
+
}
|
|
15485
16837
|
renderBody() {
|
|
15486
|
-
|
|
16838
|
+
// The queue flush consumed `pendingBodyRender` before calling us, so a body
|
|
16839
|
+
// render that arrives while the shadow template has not committed its
|
|
16840
|
+
// <tbody> (a `table/config` response landing before @ready, a structural
|
|
16841
|
+
// branch flip mid-flight) would simply vanish — the header repaints and the
|
|
16842
|
+
// rows stay stale forever. Re-arm it for the next frame instead.
|
|
16843
|
+
if (!this.tbody) {
|
|
16844
|
+
this.rearmBodyRender();
|
|
15487
16845
|
return;
|
|
16846
|
+
}
|
|
16847
|
+
this.bodyRenderRetries = 0;
|
|
15488
16848
|
// Update column manager when columns change
|
|
15489
16849
|
if (this.columns.length > 0) {
|
|
15490
16850
|
this.columnManager.initialize(this.columns, this);
|
|
@@ -15516,8 +16876,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15516
16876
|
// NOT the raw filtered data — otherwise the virtualizer windows the wrong
|
|
15517
16877
|
// dataset and expand/collapse silently no-ops.
|
|
15518
16878
|
if (this.virtualize && this.virtualizer.isEnabled()) {
|
|
15519
|
-
this.
|
|
15520
|
-
this.virtualizer.setTotalRows(this.virtualRowsSnapshot.length);
|
|
16879
|
+
this.syncVirtualRows();
|
|
15521
16880
|
// The virtualizer's afterRender hook re-establishes grid ARIA/focus on
|
|
15522
16881
|
// the freshly inserted window.
|
|
15523
16882
|
// Still render pagination
|
|
@@ -15526,64 +16885,15 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15526
16885
|
this.updateSelectAllState();
|
|
15527
16886
|
return;
|
|
15528
16887
|
}
|
|
15529
|
-
|
|
16888
|
+
const emptyBodyRow = this.createEmptyBodyRow();
|
|
16889
|
+
if (emptyBodyRow) {
|
|
15530
16890
|
// Empty / loading / error states replace the body with a single message
|
|
15531
16891
|
// row. Reset the recycler map so a later data render starts from a clean
|
|
15532
16892
|
// slate (no stale reuse candidates pointing at removed rows).
|
|
15533
16893
|
this.tbody.innerHTML = '';
|
|
15534
16894
|
this.renderedRows = new Map();
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
+ (this.rowReorder && this.rowDnD.isEnabled() ? 1 : 0);
|
|
15538
|
-
const colSpan = this.columns.length + toolCols;
|
|
15539
|
-
if (this.loading) {
|
|
15540
|
-
// Show loading spinner
|
|
15541
|
-
const tr = document.createElement('tr');
|
|
15542
|
-
const td = document.createElement('td');
|
|
15543
|
-
td.colSpan = colSpan;
|
|
15544
|
-
td.className = 'no-data';
|
|
15545
|
-
td.innerHTML = '<snice-progress variant="circular" indeterminate size="small"></snice-progress>';
|
|
15546
|
-
tr.appendChild(td);
|
|
15547
|
-
this.tbody.appendChild(tr);
|
|
15548
|
-
return;
|
|
15549
|
-
}
|
|
15550
|
-
else if (this.loadError) {
|
|
15551
|
-
// Show error state — surfaces a failed remote load instead of
|
|
15552
|
-
// silently falling through to the generic "No data" empty state.
|
|
15553
|
-
const tr = document.createElement('tr');
|
|
15554
|
-
const td = document.createElement('td');
|
|
15555
|
-
td.colSpan = colSpan;
|
|
15556
|
-
td.className = 'no-data';
|
|
15557
|
-
const errorDiv = document.createElement('div');
|
|
15558
|
-
errorDiv.className = 'table-error-message';
|
|
15559
|
-
errorDiv.textContent = `⚠️ ${this.loadError}`;
|
|
15560
|
-
td.appendChild(errorDiv);
|
|
15561
|
-
tr.appendChild(td);
|
|
15562
|
-
this.tbody.appendChild(tr);
|
|
15563
|
-
return;
|
|
15564
|
-
}
|
|
15565
|
-
else {
|
|
15566
|
-
// Show empty state
|
|
15567
|
-
const tr = document.createElement('tr');
|
|
15568
|
-
const td = document.createElement('td');
|
|
15569
|
-
td.colSpan = colSpan;
|
|
15570
|
-
td.className = 'no-data';
|
|
15571
|
-
const slotted = this.querySelector('[slot="empty-state"]');
|
|
15572
|
-
if (slotted) {
|
|
15573
|
-
td.appendChild(slotted.cloneNode(true));
|
|
15574
|
-
}
|
|
15575
|
-
else {
|
|
15576
|
-
const empty = document.createElement('snice-empty-state');
|
|
15577
|
-
empty.setAttribute('size', 'small');
|
|
15578
|
-
empty.setAttribute('icon', '📭');
|
|
15579
|
-
empty.setAttribute('title', 'No data');
|
|
15580
|
-
empty.setAttribute('description', 'No records to display.');
|
|
15581
|
-
td.appendChild(empty);
|
|
15582
|
-
}
|
|
15583
|
-
tr.appendChild(td);
|
|
15584
|
-
this.tbody.appendChild(tr);
|
|
15585
|
-
return;
|
|
15586
|
-
}
|
|
16895
|
+
this.tbody.appendChild(emptyBodyRow);
|
|
16896
|
+
return;
|
|
15587
16897
|
}
|
|
15588
16898
|
// Apply client-side filters (cached snapshot — see getFilteredData).
|
|
15589
16899
|
const filteredData = this.getFilteredData();
|
|
@@ -15595,15 +16905,23 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15595
16905
|
if (this.pagination && this.paginationMode === 'client'
|
|
15596
16906
|
&& !this.usesGroupingDisplayModel() && !this.treeData.isEnabled()) {
|
|
15597
16907
|
this.totalItems = filteredData.length;
|
|
15598
|
-
|
|
16908
|
+
// Re-clamp before slicing. A shorter re-delivery (or a filter) can leave
|
|
16909
|
+
// `currentPage` past the new last page; slicing at the stale offset lands
|
|
16910
|
+
// off the end and renders an EMPTY body while the pager still advertises
|
|
16911
|
+
// the tail rows — blank until the user clicks a page button. Same
|
|
16912
|
+
// [1, totalPages] clamp goToPage() applies, and the same one the grouping
|
|
16913
|
+
// and tree display models already do before their own slice.
|
|
16914
|
+
// An empty set is left alone: a render before the rows arrive must not
|
|
16915
|
+
// rewrite the host's declared starting page.
|
|
16916
|
+
const totalPages = filteredData.length > 0
|
|
16917
|
+
? Math.ceil(filteredData.length / this.pageSize)
|
|
16918
|
+
: this.currentPage;
|
|
16919
|
+
const page = Math.max(1, Math.min(this.currentPage, totalPages));
|
|
16920
|
+
this.clampCurrentPage(page);
|
|
16921
|
+
const startIndex = (page - 1) * this.pageSize;
|
|
15599
16922
|
displayData = filteredData.slice(startIndex, startIndex + this.pageSize);
|
|
15600
16923
|
}
|
|
15601
|
-
const
|
|
15602
|
-
const managedColumns = this.columnManager.getAllStates();
|
|
15603
|
-
const visibleColumnCount = managedColumns.length > 0
|
|
15604
|
-
? this.columnManager.getVisibleColumns().length
|
|
15605
|
-
: this.columns.length;
|
|
15606
|
-
const totalColSpan = visibleColumnCount + extraCols;
|
|
16924
|
+
const totalColSpan = this.getTotalColumnSpan();
|
|
15607
16925
|
const structuralSig = this.computeStructuralSig();
|
|
15608
16926
|
// Task B: describe the desired body as an ordered list of keyed entries,
|
|
15609
16927
|
// then reconcile the tbody to match. Rows whose object + signature are
|
|
@@ -15699,6 +17017,19 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15699
17017
|
restampIndex: index,
|
|
15700
17018
|
create: () => this.createRow(rowData, index, treeRow),
|
|
15701
17019
|
});
|
|
17020
|
+
// Tree data must compose with master-detail exactly like the ordinary
|
|
17021
|
+
// and grouping paths: createRow renders the toggle for every tree row,
|
|
17022
|
+
// so omitting this entry left that control inert. Generated gap nodes
|
|
17023
|
+
// (index -1) carry no data row and therefore no panel.
|
|
17024
|
+
if (index >= 0 && this.masterDetail.isEnabled() && this.masterDetail.isExpanded(index)) {
|
|
17025
|
+
entries.push({
|
|
17026
|
+
key: `__detail_${index}`,
|
|
17027
|
+
sig: 'detail',
|
|
17028
|
+
alwaysRebuild: true,
|
|
17029
|
+
create: () => this.masterDetail.createDetailRow(rowData, index, totalColSpan) ??
|
|
17030
|
+
document.createElement('tr'),
|
|
17031
|
+
});
|
|
17032
|
+
}
|
|
15702
17033
|
});
|
|
15703
17034
|
}
|
|
15704
17035
|
else {
|
|
@@ -15756,6 +17087,20 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15756
17087
|
* data row, so column widths, pinning, and alignment track the table
|
|
15757
17088
|
* engine natively. Removed entirely when content fills (or overflows) the
|
|
15758
17089
|
* frame, restoring the plain last-row styling.
|
|
17090
|
+
*
|
|
17091
|
+
* INVARIANT — the filler may only consume space the frame ALREADY has; space
|
|
17092
|
+
* that exists only BECAUSE of the filler must be given back. The filler is
|
|
17093
|
+
* table content, and `@observe('resize', '.table-frame')` re-derives it from
|
|
17094
|
+
* the frame's box, so on a host whose height is content-driven (the
|
|
17095
|
+
* documented `:host { height: 100% }` resolving against an auto or
|
|
17096
|
+
* max-content container — a stretched grid/flex item, a plain block parent)
|
|
17097
|
+
* the filler is an input to the very measurement that produced it: the frame
|
|
17098
|
+
* grows, the observer fires, the leftover computes positive again, and the
|
|
17099
|
+
* table inflates by one slack per animation frame without bound. No static
|
|
17100
|
+
* check distinguishes a content-driven frame from a genuinely sized one, so
|
|
17101
|
+
* this measures instead: apply the filler, then confirm the frame kept its
|
|
17102
|
+
* height. Any growth is self-inflicted and is subtracted from the filler;
|
|
17103
|
+
* once nothing honest is left, the frame had no free space to fill at all.
|
|
15759
17104
|
*/
|
|
15760
17105
|
updateFillRow() {
|
|
15761
17106
|
const frame = this.shadowRoot?.querySelector('.table-frame');
|
|
@@ -15770,22 +17115,45 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15770
17115
|
const lastRow = rows[rows.length - 1];
|
|
15771
17116
|
if (!lastRow)
|
|
15772
17117
|
return;
|
|
15773
|
-
|
|
17118
|
+
// Baseline: the frame's height with no filler of ours in the box. Every
|
|
17119
|
+
// later reading is compared against it, so the correction below can never
|
|
17120
|
+
// hand back more than the filler itself created.
|
|
17121
|
+
const baseline = frame.clientHeight;
|
|
17122
|
+
let leftover = Math.floor(baseline - tableEl.offsetHeight);
|
|
15774
17123
|
if (leftover <= 1)
|
|
15775
17124
|
return;
|
|
15776
17125
|
const fill = lastRow.cloneNode(true);
|
|
15777
17126
|
fill.className = 'table-fill-row';
|
|
15778
|
-
|
|
17127
|
+
// `part` goes with the rest of the row identity: the filler is not "each
|
|
17128
|
+
// native body row", so a consumer's ::part(row)/::part(cell) rule must not
|
|
17129
|
+
// paint a phantom row across it.
|
|
17130
|
+
for (const attr of ['data-index', 'data-selected', 'role', 'aria-rowindex', 'aria-selected', 'tabindex', 'id', 'part']) {
|
|
15779
17131
|
fill.removeAttribute(attr);
|
|
15780
17132
|
}
|
|
15781
17133
|
fill.setAttribute('aria-hidden', 'true');
|
|
15782
17134
|
fill.querySelectorAll('td').forEach(td => {
|
|
15783
17135
|
td.replaceChildren();
|
|
15784
|
-
for (const attr of ['role', 'aria-colindex', 'tabindex', 'aria-selected'])
|
|
17136
|
+
for (const attr of ['role', 'aria-colindex', 'tabindex', 'aria-selected', 'part'])
|
|
15785
17137
|
td.removeAttribute(attr);
|
|
15786
17138
|
});
|
|
15787
17139
|
fill.style.height = `${leftover}px`;
|
|
15788
17140
|
tbody.appendChild(fill);
|
|
17141
|
+
// Feedback check. Reading `clientHeight` flushes layout, so each pass sees
|
|
17142
|
+
// the frame as it stands WITH the filler in it. A frame that kept its
|
|
17143
|
+
// baseline is genuinely sized and the filler stays; growth means the frame
|
|
17144
|
+
// is (partly) sized by its own content, so give exactly that back. Two
|
|
17145
|
+
// corrections settle every real case — a fully self-made leftover reaches
|
|
17146
|
+
// zero on the first — and the bounded loop guarantees termination.
|
|
17147
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
17148
|
+
const selfMade = frame.clientHeight - baseline;
|
|
17149
|
+
if (selfMade <= 0)
|
|
17150
|
+
return;
|
|
17151
|
+
leftover -= selfMade;
|
|
17152
|
+
if (leftover <= 1)
|
|
17153
|
+
break;
|
|
17154
|
+
fill.style.height = `${leftover}px`;
|
|
17155
|
+
}
|
|
17156
|
+
fill.remove();
|
|
15789
17157
|
}
|
|
15790
17158
|
// ── Task B: render-path reconciler ──────────────────────────────────────
|
|
15791
17159
|
//
|
|
@@ -15943,7 +17311,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
15943
17311
|
restampRowIndex(el, index) {
|
|
15944
17312
|
el.setAttribute('data-index', String(index));
|
|
15945
17313
|
const isSelected = this.selectedRows.includes(index);
|
|
15946
|
-
|
|
17314
|
+
this.stampRowSelection(el, isSelected);
|
|
15947
17315
|
const checkbox = el.querySelector('snice-checkbox.row-select');
|
|
15948
17316
|
if (checkbox) {
|
|
15949
17317
|
checkbox.setAttribute('data-row-index', String(index));
|
|
@@ -16130,24 +17498,30 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16130
17498
|
span.textContent = rendered == null ? '' : String(rendered);
|
|
16131
17499
|
return span;
|
|
16132
17500
|
}
|
|
16133
|
-
|
|
17501
|
+
// A declared display formatter resolves the whole pipeline here — the
|
|
17502
|
+
// documented precedence is `formatter` first, `valueFormatter` as the
|
|
17503
|
+
// fallback — and its output is TEXT, so the column renders through the text
|
|
17504
|
+
// cell: a rating/progress/link/boolean cell cannot carry a display string
|
|
17505
|
+
// (its value channel is typed) and renders structure rather than text. This
|
|
17506
|
+
// is the rule the declarative <snice-row> path already uses.
|
|
17507
|
+
const displayText = this.cellDisplayText(column, value, row);
|
|
17508
|
+
const tagName = displayText === null ? this.getCellTagName(column.type) : 'snice-cell-text';
|
|
16134
17509
|
const el = document.createElement(tagName);
|
|
16135
17510
|
// Base attributes. Only force `align` if the column definition specifies
|
|
16136
17511
|
// one; otherwise let the cell pick its type-appropriate default (numbers
|
|
16137
17512
|
// right, rating/boolean center, text left). Hard-coding 'left' here fought
|
|
16138
|
-
// the cell's own type-based text-align and made number values drift
|
|
17513
|
+
// the cell's own type-based text-align and made number values drift — and a
|
|
17514
|
+
// formatted column keeps its TYPE's default, not the text cell's.
|
|
16139
17515
|
el.setAttribute('type', column.type || 'text');
|
|
16140
17516
|
if (column.align)
|
|
16141
17517
|
el.setAttribute('align', column.align);
|
|
17518
|
+
else if (displayText !== null)
|
|
17519
|
+
el.setAttribute('align', defaultCellAlign(column.type));
|
|
16142
17520
|
el.setAttribute('in-table', 'true');
|
|
16143
|
-
//
|
|
16144
|
-
// the
|
|
16145
|
-
//
|
|
16146
|
-
// cell
|
|
16147
|
-
// without the formatters, which keeps it from formatting again and tells
|
|
16148
|
-
// the shared presentation contract to paint the text over any type-specific
|
|
16149
|
-
// rendering (a rating/progress/link cell cannot render display text).
|
|
16150
|
-
const displayText = this.cellDisplayText(column, value, row);
|
|
17521
|
+
// The cell's value IS its display value: with a formatter declared the cell
|
|
17522
|
+
// receives the formatted text and a column with the formatters removed, so
|
|
17523
|
+
// nothing formats twice and the `value` channel a caller reads is what the
|
|
17524
|
+
// cell shows.
|
|
16151
17525
|
const cellColumn = displayText === null
|
|
16152
17526
|
? column
|
|
16153
17527
|
: { ...column, formatter: undefined, valueFormatter: undefined };
|
|
@@ -16155,7 +17529,6 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16155
17529
|
// JSON, action, and tag cells without a lossy attribute conversion. Keep a
|
|
16156
17530
|
// primitive attribute for declarative inspection and CSS selectors.
|
|
16157
17531
|
if (displayText !== null) {
|
|
16158
|
-
el.setAttribute('formatted', '');
|
|
16159
17532
|
el.value = displayText;
|
|
16160
17533
|
el.setAttribute('value', displayText);
|
|
16161
17534
|
}
|
|
@@ -16165,9 +17538,12 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16165
17538
|
el.data = value;
|
|
16166
17539
|
}
|
|
16167
17540
|
else {
|
|
16168
|
-
|
|
16169
|
-
|
|
16170
|
-
|
|
17541
|
+
// An empty row value (null, or a field the row never carried) falls back
|
|
17542
|
+
// to the typed cell's own documented empty value, never to a blanket ''.
|
|
17543
|
+
const resolved = value ?? emptyCellValue(column.type);
|
|
17544
|
+
el.value = resolved;
|
|
17545
|
+
if (resolved !== null && typeof resolved !== 'object') {
|
|
17546
|
+
el.setAttribute('value', String(resolved));
|
|
16171
17547
|
}
|
|
16172
17548
|
}
|
|
16173
17549
|
// Column definition as property (for formatter access etc.)
|
|
@@ -16384,16 +17760,36 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16384
17760
|
updateRowSelectionState() {
|
|
16385
17761
|
if (!this.tbody)
|
|
16386
17762
|
return;
|
|
16387
|
-
|
|
16388
|
-
|
|
17763
|
+
// Key off each row's own `data-index`, never its DOM position: group
|
|
17764
|
+
// headers, aggregate footers, virtual spacers, and the inert fill row share
|
|
17765
|
+
// the tbody, so a positional walk hands a raw-data index to a non-data row
|
|
17766
|
+
// (and drifts every real row after it — on page 2 of a client-paged table
|
|
17767
|
+
// the offsets never lined up at all).
|
|
17768
|
+
const rows = this.tbody.querySelectorAll('tr[data-index]');
|
|
17769
|
+
rows.forEach(row => {
|
|
17770
|
+
const index = Number(row.getAttribute('data-index'));
|
|
16389
17771
|
const isSelected = this.selectedRows.includes(index);
|
|
16390
|
-
|
|
17772
|
+
this.stampRowSelection(row, isSelected);
|
|
16391
17773
|
const checkbox = row.querySelector('snice-checkbox.row-select');
|
|
16392
17774
|
if (checkbox) {
|
|
16393
17775
|
checkbox.checked = isSelected;
|
|
16394
17776
|
}
|
|
16395
17777
|
});
|
|
16396
17778
|
}
|
|
17779
|
+
/**
|
|
17780
|
+
* The two selection reflections a row carries, written together: `data-selected`
|
|
17781
|
+
* drives the CSS, `aria-selected` is the documented a11y contract ("selected
|
|
17782
|
+
* data rows expose aria-selected"). The keyboard module stamps ARIA only on a
|
|
17783
|
+
* full body render, so the delta paths below have to carry it themselves —
|
|
17784
|
+
* otherwise a screen reader never hears an interaction-driven selection.
|
|
17785
|
+
*/
|
|
17786
|
+
stampRowSelection(row, isSelected) {
|
|
17787
|
+
row.setAttribute('data-selected', String(isSelected));
|
|
17788
|
+
if (isSelected)
|
|
17789
|
+
row.setAttribute('aria-selected', 'true');
|
|
17790
|
+
else
|
|
17791
|
+
row.removeAttribute('aria-selected');
|
|
17792
|
+
}
|
|
16397
17793
|
// A2: reflect one row's selection into the DOM (located by data-index). The
|
|
16398
17794
|
// reconciler (Task B) re-stamps data-index on moved rows, so this stays
|
|
16399
17795
|
// correct after reorder. No-op if the row isn't in the current window.
|
|
@@ -16403,7 +17799,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16403
17799
|
const row = this.tbody.querySelector(`tr[data-index="${index}"]`);
|
|
16404
17800
|
if (!row)
|
|
16405
17801
|
return;
|
|
16406
|
-
|
|
17802
|
+
this.stampRowSelection(row, isSelected);
|
|
16407
17803
|
const checkbox = row.querySelector('snice-checkbox.row-select');
|
|
16408
17804
|
if (checkbox)
|
|
16409
17805
|
checkbox.checked = isSelected;
|
|
@@ -16504,14 +17900,54 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16504
17900
|
this.settingSortedData = false;
|
|
16505
17901
|
}
|
|
16506
17902
|
sortLocalData() {
|
|
17903
|
+
const previous = this.data;
|
|
16507
17904
|
this.applyLocalSortOrder();
|
|
16508
17905
|
// Sort replaced this.data (new order + reference) — resync the row-index
|
|
16509
17906
|
// map and drop the stale filtered snapshot before re-rendering. Render is
|
|
16510
17907
|
// queued (not synchronous): sortLocalData is reachable from the currentSort
|
|
16511
17908
|
// setter stack via handleControlledSort, and cells must never construct there.
|
|
16512
17909
|
this.rebuildRowIndex();
|
|
17910
|
+
this.remapSelectionAfterReorder(previous);
|
|
16513
17911
|
this.scheduleRender('body');
|
|
16514
17912
|
}
|
|
17913
|
+
/**
|
|
17914
|
+
* Selection is anchored to the ROW, not to its position. `selectedRows` holds
|
|
17915
|
+
* indices into `data`, so any reorder of that array — a local sort, or a
|
|
17916
|
+
* re-delivery of the same row objects in another order — has to re-resolve
|
|
17917
|
+
* them; otherwise the indices keep pointing at whatever row now occupies the
|
|
17918
|
+
* slot and the user's checkmark silently jumps to a different row (the same
|
|
17919
|
+
* remap the row-reorder/DnD path performs).
|
|
17920
|
+
*
|
|
17921
|
+
* Only a permutation is remapped: same length, same identities, no duplicates.
|
|
17922
|
+
* A genuinely new dataset keeps the documented raw-index semantics untouched,
|
|
17923
|
+
* and rows carrying no identity (duplicate objects) have nothing to anchor to.
|
|
17924
|
+
* Call AFTER rebuildRowIndex() — the remap reads the fresh row-index map.
|
|
17925
|
+
*/
|
|
17926
|
+
remapSelectionAfterReorder(previous) {
|
|
17927
|
+
if (this.selectedRows.length === 0)
|
|
17928
|
+
return;
|
|
17929
|
+
if (previous === this.data || previous.length !== this.data.length)
|
|
17930
|
+
return;
|
|
17931
|
+
const identities = new Set(this.data);
|
|
17932
|
+
if (identities.size !== this.data.length)
|
|
17933
|
+
return;
|
|
17934
|
+
// BOTH sides have to be duplicate-free for "same length + subset" to mean
|
|
17935
|
+
// permutation: previous=[A,A] against data=[A,B] satisfies the subset test
|
|
17936
|
+
// while actually being a NEW dataset, and remapping it would move the
|
|
17937
|
+
// selection off the row the raw-index contract points at.
|
|
17938
|
+
if (new Set(previous).size !== previous.length)
|
|
17939
|
+
return;
|
|
17940
|
+
for (const row of previous)
|
|
17941
|
+
if (!identities.has(row))
|
|
17942
|
+
return;
|
|
17943
|
+
const remapped = this.selectedRows
|
|
17944
|
+
.map((index) => this.indexOfRow(previous[index]))
|
|
17945
|
+
.filter((index) => index >= 0);
|
|
17946
|
+
if (remapped.length === this.selectedRows.length
|
|
17947
|
+
&& remapped.every((index, i) => index === this.selectedRows[i]))
|
|
17948
|
+
return;
|
|
17949
|
+
this.selectedRows = remapped;
|
|
17950
|
+
}
|
|
16515
17951
|
dispatchRowSelectionChanged(rowIndex, selected) {
|
|
16516
17952
|
return {
|
|
16517
17953
|
selectedRows: this.selectedRows,
|
|
@@ -16631,20 +18067,37 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16631
18067
|
const scrollContainer = this.getScrollContainer();
|
|
16632
18068
|
if (!scrollContainer)
|
|
16633
18069
|
return;
|
|
16634
|
-
this.
|
|
18070
|
+
this.syncVirtualRows();
|
|
16635
18071
|
this.virtualizer.attach({
|
|
16636
18072
|
rowHeight: (displayIndex) => this.getVirtualRowHeight(displayIndex),
|
|
16637
18073
|
bufferPx: this.virtualBuffer,
|
|
16638
18074
|
totalRows: this.virtualRowsSnapshot.length,
|
|
16639
18075
|
scrollContainer,
|
|
18076
|
+
// The host template swaps its structural branch (slotted rows ⇄ native
|
|
18077
|
+
// table) without re-attaching. Re-resolving keeps the window painting into
|
|
18078
|
+
// the LIVE tbody instead of the detached branch's orphaned one.
|
|
18079
|
+
resolveScrollContainer: () => this.getScrollContainer(),
|
|
16640
18080
|
renderRange: (start, end) => this.renderRowRange(start, end),
|
|
18081
|
+
// Spacers span the real grid. A spacer wider than the table invents
|
|
18082
|
+
// columns for fixed layout to spend the frame's spare width on.
|
|
18083
|
+
resolveColumnSpan: () => this.getTotalColumnSpan(),
|
|
16641
18084
|
// Pinned rows live outside the windowed range so they are always present,
|
|
16642
18085
|
// exactly like the non-virtual path (renderBody).
|
|
16643
18086
|
renderPinnedTop: () => this.renderPinnedRows(this.pinnedTopRows, 'top'),
|
|
16644
18087
|
renderPinnedBottom: () => this.renderPinnedRows(this.pinnedBottomRows, 'bottom'),
|
|
18088
|
+
// A zero-row window still owes the user the empty/loading/error row the
|
|
18089
|
+
// ordinary path renders.
|
|
18090
|
+
renderEmptyBody: () => this.createEmptyBodyRow(),
|
|
16645
18091
|
// Scroll-driven windows bypass renderBody; restore grid roles, logical
|
|
16646
18092
|
// row indices, and roving focus after every inserted range.
|
|
16647
|
-
afterRender: () =>
|
|
18093
|
+
afterRender: () => {
|
|
18094
|
+
this.keyboard.refresh();
|
|
18095
|
+
// Every window rebuilds the tbody from scratch, taking any filler with
|
|
18096
|
+
// it. Virtualization is not exempt from the fill-row contract, and
|
|
18097
|
+
// renderBody()'s own tail call never runs on the virtual path, so the
|
|
18098
|
+
// filler is re-evaluated here instead.
|
|
18099
|
+
this.updateFillRow();
|
|
18100
|
+
},
|
|
16648
18101
|
});
|
|
16649
18102
|
}
|
|
16650
18103
|
getVirtualRowHeight(displayIndex) {
|
|
@@ -16663,6 +18116,30 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16663
18116
|
* virtualized); otherwise it is the filtered data. Each descriptor carries
|
|
16664
18117
|
* the logical index used for `data-index`, selection, and detail lookups.
|
|
16665
18118
|
*/
|
|
18119
|
+
/**
|
|
18120
|
+
* Re-derive the windowed row model and hand it to the virtualizer.
|
|
18121
|
+
*
|
|
18122
|
+
* `virtualRowsSnapshot` is the ONLY state the paint pipeline caches, and
|
|
18123
|
+
* `renderRowRange` paints from it. Any caller that resolves a display index
|
|
18124
|
+
* against a FRESH `getVirtualRows()` and then asks the virtualizer to paint
|
|
18125
|
+
* (scrollToRow, the keyboard ensure-visible hook) must republish that model
|
|
18126
|
+
* first, or it scrolls to an offset computed in one list and paints rows from
|
|
18127
|
+
* another — the requested row never appears. Returns the fresh model so the
|
|
18128
|
+
* caller indexes exactly what will be painted.
|
|
18129
|
+
*
|
|
18130
|
+
* `paint` false leaves the repaint to the caller (scrollToIndex refreshes the
|
|
18131
|
+
* window itself). Those callers must not paint from here: the virtualizer's
|
|
18132
|
+
* afterRender hook re-runs keyboard.refresh(), whose ensure-visible callback
|
|
18133
|
+
* lands back in ensureKeyboardRowRendered — an unbounded recursion.
|
|
18134
|
+
*/
|
|
18135
|
+
syncVirtualRows(paint = true) {
|
|
18136
|
+
this.virtualRowsSnapshot = this.getVirtualRows();
|
|
18137
|
+
// Not yet attached during setupVirtualization; attach() takes the count.
|
|
18138
|
+
if (this.virtualizer.isEnabled()) {
|
|
18139
|
+
this.virtualizer.setTotalRows(this.virtualRowsSnapshot.length, paint);
|
|
18140
|
+
}
|
|
18141
|
+
return this.virtualRowsSnapshot;
|
|
18142
|
+
}
|
|
16666
18143
|
getVirtualRows() {
|
|
16667
18144
|
const filtered = this.getFilteredData();
|
|
16668
18145
|
// F: the flattened group+row+aggregate list is the virtual model when
|
|
@@ -16685,14 +18162,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16685
18162
|
const rows = this.virtualRowsSnapshot.length > 0
|
|
16686
18163
|
? this.virtualRowsSnapshot
|
|
16687
18164
|
: this.getVirtualRows();
|
|
16688
|
-
const
|
|
16689
|
-
(this.masterDetail.isEnabled() ? 1 : 0) +
|
|
16690
|
-
(this.rowReorder && this.rowDnD.isEnabled() ? 1 : 0);
|
|
16691
|
-
const managedColumns = this.columnManager.getAllStates();
|
|
16692
|
-
const visibleColumnCount = managedColumns.length > 0
|
|
16693
|
-
? this.columnManager.getVisibleColumns().length
|
|
16694
|
-
: this.columns.length;
|
|
16695
|
-
const totalColSpan = visibleColumnCount + extraCols;
|
|
18165
|
+
const totalColSpan = this.getTotalColumnSpan();
|
|
16696
18166
|
const structuralSig = this.computeStructuralSig();
|
|
16697
18167
|
// Task B: recycle rows that stay in the window across a scroll shift. A row
|
|
16698
18168
|
// still visible after scrolling keeps its data-object key and signature, so
|
|
@@ -16738,9 +18208,10 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16738
18208
|
// Master-detail: render the expanded detail row in-window. The detail
|
|
16739
18209
|
// A fixed detailHeight participates in virtual spacer math through
|
|
16740
18210
|
// getVirtualRowHeight. Auto-height content is still rendered in-window
|
|
16741
|
-
// and contributes through normal table layout. Tree rows
|
|
16742
|
-
// detail
|
|
16743
|
-
|
|
18211
|
+
// and contributes through normal table layout. Tree rows compose with
|
|
18212
|
+
// master-detail like any other row; only generated gap nodes (index -1)
|
|
18213
|
+
// have no data row to describe.
|
|
18214
|
+
if (dataIndex >= 0 && this.masterDetail.isEnabled() && this.masterDetail.isExpanded(dataIndex)) {
|
|
16744
18215
|
const detailRow = this.masterDetail.createDetailRow(data, dataIndex, totalColSpan);
|
|
16745
18216
|
if (detailRow)
|
|
16746
18217
|
fragment.appendChild(detailRow);
|
|
@@ -16766,7 +18237,8 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16766
18237
|
// data row rather than an unrelated header/subtotal.
|
|
16767
18238
|
if (this.usesGroupingDisplayModel() || this.treeData.isEnabled()) {
|
|
16768
18239
|
const row = this.data[index];
|
|
16769
|
-
|
|
18240
|
+
// Resolve against the model the virtualizer is about to paint from.
|
|
18241
|
+
const displayIndex = this.syncVirtualRows(false).findIndex((entry) => entry.data === row);
|
|
16770
18242
|
if (displayIndex >= 0)
|
|
16771
18243
|
this.virtualizer.scrollToIndex(displayIndex);
|
|
16772
18244
|
return;
|
|
@@ -16775,6 +18247,14 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16775
18247
|
}
|
|
16776
18248
|
/** Rows currently reachable by grid keyboard navigation, in display order. */
|
|
16777
18249
|
getKeyboardColumnCount() {
|
|
18250
|
+
// List mode paints tool cells plus ONE full-width cell per row, and its
|
|
18251
|
+
// header no longer mirrors the body — count the body shape directly.
|
|
18252
|
+
if (this.usesListRowRenderer()) {
|
|
18253
|
+
return 1
|
|
18254
|
+
+ (this.rowReorder && this.rowDnD.isEnabled() ? 1 : 0)
|
|
18255
|
+
+ (this.masterDetail.isEnabled() ? 1 : 0)
|
|
18256
|
+
+ (this.hasSelectionColumn() ? 1 : 0);
|
|
18257
|
+
}
|
|
16778
18258
|
const rendered = this.shadowRoot?.querySelectorAll('thead tr.column-header-row > th').length ?? 0;
|
|
16779
18259
|
if (rendered > 0)
|
|
16780
18260
|
return rendered;
|
|
@@ -16830,7 +18310,9 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16830
18310
|
const row = this.getKeyboardRows()[rowIndex];
|
|
16831
18311
|
if (row === undefined)
|
|
16832
18312
|
return;
|
|
16833
|
-
|
|
18313
|
+
// The window is painted from `virtualRowsSnapshot`; republish it so the
|
|
18314
|
+
// offset we scroll to and the rows that land there come from one model.
|
|
18315
|
+
const displayIndex = this.syncVirtualRows(false).findIndex((entry) => entry.data === row);
|
|
16834
18316
|
if (displayIndex >= 0)
|
|
16835
18317
|
this.virtualizer.scrollToIndex(displayIndex);
|
|
16836
18318
|
}
|
|
@@ -16958,8 +18440,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16958
18440
|
return items;
|
|
16959
18441
|
const totalPages = Math.max(1, Math.ceil(items.length / this.pageSize));
|
|
16960
18442
|
const page = Math.max(1, Math.min(this.currentPage, totalPages));
|
|
16961
|
-
|
|
16962
|
-
this.currentPage = page;
|
|
18443
|
+
this.clampCurrentPage(page);
|
|
16963
18444
|
const start = (page - 1) * this.pageSize;
|
|
16964
18445
|
return items.slice(start, start + this.pageSize);
|
|
16965
18446
|
}
|
|
@@ -16979,8 +18460,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
16979
18460
|
return rows;
|
|
16980
18461
|
const totalPages = Math.max(1, Math.ceil(rows.length / this.pageSize));
|
|
16981
18462
|
const page = Math.max(1, Math.min(this.currentPage, totalPages));
|
|
16982
|
-
|
|
16983
|
-
this.currentPage = page;
|
|
18463
|
+
this.clampCurrentPage(page);
|
|
16984
18464
|
const start = (page - 1) * this.pageSize;
|
|
16985
18465
|
return rows.slice(start, start + this.pageSize);
|
|
16986
18466
|
}
|
|
@@ -17042,16 +18522,21 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
17042
18522
|
this.renderBody();
|
|
17043
18523
|
this.dispatchColumnPinChange(key, false);
|
|
17044
18524
|
}
|
|
18525
|
+
// A measured width is the column's width, not the header's: the body has to
|
|
18526
|
+
// be repainted with it too, or the th and its tds carry different numbers
|
|
18527
|
+
// and the next body-only re-render paints the stale one.
|
|
17045
18528
|
autoSizeColumn(key) {
|
|
17046
18529
|
if (this.tbody) {
|
|
17047
18530
|
this.columnManager.autoSizeColumn(key, this.tbody);
|
|
17048
18531
|
this.renderHeader();
|
|
18532
|
+
this.renderBody();
|
|
17049
18533
|
}
|
|
17050
18534
|
}
|
|
17051
18535
|
autoSizeAllColumns() {
|
|
17052
18536
|
if (this.tbody) {
|
|
17053
18537
|
this.columnManager.autoSizeAll(this.tbody);
|
|
17054
18538
|
this.renderHeader();
|
|
18539
|
+
this.renderBody();
|
|
17055
18540
|
}
|
|
17056
18541
|
}
|
|
17057
18542
|
moveColumn(key, toIndex) {
|
|
@@ -17311,8 +18796,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
17311
18796
|
this.masterDetail.onHeightChange = () => {
|
|
17312
18797
|
if (!this.virtualize || !this.virtualizer.isEnabled())
|
|
17313
18798
|
return;
|
|
17314
|
-
this.
|
|
17315
|
-
this.virtualizer.setTotalRows(this.virtualRowsSnapshot.length);
|
|
18799
|
+
this.syncVirtualRows();
|
|
17316
18800
|
};
|
|
17317
18801
|
this.masterDetail.prepare(this.data);
|
|
17318
18802
|
this.renderHeader();
|
|
@@ -17539,6 +19023,9 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
17539
19023
|
// The row recycler cannot infer that a callback identity changed from
|
|
17540
19024
|
// row data alone. Force fresh rows so list markup replaces table cells.
|
|
17541
19025
|
this.renderedRows = new Map();
|
|
19026
|
+
// Installing a renderer removes the column geometry the header describes;
|
|
19027
|
+
// clearing it back to null restores columnar rows AND their header.
|
|
19028
|
+
this.renderHeader();
|
|
17542
19029
|
this.renderBody();
|
|
17543
19030
|
}
|
|
17544
19031
|
setListViewRenderer(fn) {
|
|
@@ -17557,7 +19044,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
17557
19044
|
tr.style.height = `${this.rowHeight}px`;
|
|
17558
19045
|
}
|
|
17559
19046
|
const isSelected = this.selectedRows.includes(index);
|
|
17560
|
-
|
|
19047
|
+
this.stampRowSelection(tr, isSelected);
|
|
17561
19048
|
// Row DnD handle
|
|
17562
19049
|
if (this.rowReorder && this.rowDnD.isEnabled()) {
|
|
17563
19050
|
tr.appendChild(this.rowDnD.createDragHandle());
|
|
@@ -17628,7 +19115,18 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
17628
19115
|
const toggle = this.treeData.createToggle(treeRow);
|
|
17629
19116
|
td.appendChild(toggle);
|
|
17630
19117
|
const textSpan = document.createElement('span');
|
|
17631
|
-
textSpan.
|
|
19118
|
+
textSpan.className = 'tree-label';
|
|
19119
|
+
// The group cell keeps its plain label next to the indent/chevron, but
|
|
19120
|
+
// it is still a cell: renderCell owns it outright, and otherwise the
|
|
19121
|
+
// documented display pipeline (formatter, then valueFormatter) decides
|
|
19122
|
+
// the text. Only a column with neither shows the raw working value.
|
|
19123
|
+
if (typeof column.renderCell === 'function') {
|
|
19124
|
+
textSpan.appendChild(this.createCellElement(column, value, rowData));
|
|
19125
|
+
}
|
|
19126
|
+
else {
|
|
19127
|
+
textSpan.textContent = this.cellDisplayText(column, value, rowData)
|
|
19128
|
+
?? (value == null ? '' : String(value));
|
|
19129
|
+
}
|
|
17632
19130
|
// Clicking the text label also toggles expand/collapse
|
|
17633
19131
|
if (treeRow.hasChildren) {
|
|
17634
19132
|
textSpan.style.cursor = 'pointer';
|
|
@@ -17647,21 +19145,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
17647
19145
|
const state = this.columnManager.getState(column.key);
|
|
17648
19146
|
if (state) {
|
|
17649
19147
|
td.style.width = `${state.width}px`;
|
|
17650
|
-
|
|
17651
|
-
if (state.pinned === 'left') {
|
|
17652
|
-
const offsets = this.columnManager.getPinnedLeftOffsets();
|
|
17653
|
-
td.classList.add('pinned-cell');
|
|
17654
|
-
td.style.position = 'sticky';
|
|
17655
|
-
td.style.left = `${offsets.get(column.key) ?? 0}px`;
|
|
17656
|
-
td.style.zIndex = '1';
|
|
17657
|
-
}
|
|
17658
|
-
else if (state.pinned === 'right') {
|
|
17659
|
-
const offsets = this.columnManager.getPinnedRightOffsets();
|
|
17660
|
-
td.classList.add('pinned-cell');
|
|
17661
|
-
td.style.position = 'sticky';
|
|
17662
|
-
td.style.right = `${offsets.get(column.key) ?? 0}px`;
|
|
17663
|
-
td.style.zIndex = '1';
|
|
17664
|
-
}
|
|
19148
|
+
this.applyPinnedCell(td, column.key, state, '1');
|
|
17665
19149
|
}
|
|
17666
19150
|
tr.appendChild(td);
|
|
17667
19151
|
});
|
|
@@ -17825,20 +19309,7 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
17825
19309
|
const state = this.columnManager.getState(column.key);
|
|
17826
19310
|
if (state) {
|
|
17827
19311
|
td.style.width = `${state.width}px`;
|
|
17828
|
-
|
|
17829
|
-
const offsets = this.columnManager.getPinnedLeftOffsets();
|
|
17830
|
-
td.classList.add('pinned-cell');
|
|
17831
|
-
td.style.position = 'sticky';
|
|
17832
|
-
td.style.left = `${offsets.get(column.key) ?? 0}px`;
|
|
17833
|
-
td.style.zIndex = '1';
|
|
17834
|
-
}
|
|
17835
|
-
else if (state.pinned === 'right') {
|
|
17836
|
-
const offsets = this.columnManager.getPinnedRightOffsets();
|
|
17837
|
-
td.classList.add('pinned-cell');
|
|
17838
|
-
td.style.position = 'sticky';
|
|
17839
|
-
td.style.right = `${offsets.get(column.key) ?? 0}px`;
|
|
17840
|
-
td.style.zIndex = '1';
|
|
17841
|
-
}
|
|
19312
|
+
this.applyPinnedCell(td, column.key, state, '1');
|
|
17842
19313
|
}
|
|
17843
19314
|
tr.appendChild(td);
|
|
17844
19315
|
});
|
|
@@ -18019,6 +19490,26 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
18019
19490
|
// C1: reactive, no attribute reflection. A post-mount assignment (or the
|
|
18020
19491
|
// setData/setColumns aliases) re-renders through the microtask-coalesced
|
|
18021
19492
|
// queue — see handleColumnsAssignment / handleDataAssignment.
|
|
19493
|
+
//
|
|
19494
|
+
// `hasChanged: () => true` — ASSIGNMENT IS THE SIGNAL, NOT IDENTITY.
|
|
19495
|
+
// The core dirty-check (notEqual in packages/core/src/utils.ts, applied by the
|
|
19496
|
+
// @property setter in element.ts) is identity-based and stays that way for
|
|
19497
|
+
// every other property: that is the right default for reactivity in general.
|
|
19498
|
+
// But `data` and `columns` are bulk payloads that callers routinely mutate in
|
|
19499
|
+
// place and then publish by re-assigning the SAME array reference
|
|
19500
|
+
// (`rows.push(row); table.data = rows`). The documented contract is that
|
|
19501
|
+
// assigning `data`/`columns` rerenders, so these two properties opt OUT of the
|
|
19502
|
+
// identity gate locally rather than weakening it globally.
|
|
19503
|
+
//
|
|
19504
|
+
// Tradeoff — double-render cost: a genuinely redundant assignment
|
|
19505
|
+
// (`table.data = table.data` with nothing changed) now costs a render that the
|
|
19506
|
+
// identity gate used to swallow, and a caller who assigns both `columns` and
|
|
19507
|
+
// `data` pays two schedule calls instead of one. It is bounded: both watchers
|
|
19508
|
+
// go through scheduleRender(), which coalesces a burst within a tick into a
|
|
19509
|
+
// single microtask flush, so the worst case is one extra body paint per tick,
|
|
19510
|
+
// never per assignment. Deep-equality diffing was rejected as the alternative
|
|
19511
|
+
// — it is O(rows x columns) on every assignment, i.e. a cost paid by the
|
|
19512
|
+
// common (genuinely-changed) path to save the rare redundant one.
|
|
18022
19513
|
this.columns = (__runInitializers(this, _searchDebounce_extraInitializers), __runInitializers(this, _columns_initializers, []));
|
|
18023
19514
|
this.data = (__runInitializers(this, _columns_extraInitializers), __runInitializers(this, _data_initializers, []));
|
|
18024
19515
|
/**
|
|
@@ -18039,7 +19530,8 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
18039
19530
|
this.selector = __runInitializers(this, _selector_initializers, '');
|
|
18040
19531
|
this.selectorOptions = (__runInitializers(this, _selector_extraInitializers), __runInitializers(this, _selectorOptions_initializers, []));
|
|
18041
19532
|
this.loading = (__runInitializers(this, _selectorOptions_extraInitializers), __runInitializers(this, _loading_initializers, false));
|
|
18042
|
-
// Set when a remote-mode load fails; cleared
|
|
19533
|
+
// Set when a remote-mode load fails; cleared by clearLoadError() as soon as
|
|
19534
|
+
// ANY fresh dataset arrives (see there).
|
|
18043
19535
|
// Not a @property — renderBody() reads it directly, no attribute needed.
|
|
18044
19536
|
this.loadError = (__runInitializers(this, _loading_extraInitializers), null);
|
|
18045
19537
|
this.virtualize = __runInitializers(this, _virtualize_initializers, false);
|
|
@@ -18049,7 +19541,16 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
18049
19541
|
this.editable = (__runInitializers(this, _columnResize_extraInitializers), __runInitializers(this, _editable_initializers, false));
|
|
18050
19542
|
this.editMode = (__runInitializers(this, _editable_extraInitializers), __runInitializers(this, _editMode_initializers, 'cell'));
|
|
18051
19543
|
this.density = (__runInitializers(this, _editMode_extraInitializers), __runInitializers(this, _density_initializers, 'standard'));
|
|
18052
|
-
|
|
19544
|
+
/**
|
|
19545
|
+
* How the columns relate to the frame's width.
|
|
19546
|
+
*
|
|
19547
|
+
* `scroll` keeps every column at or above its `minWidth` and lets the frame
|
|
19548
|
+
* scroll horizontally when they no longer fit. `squish` makes the frame the
|
|
19549
|
+
* hard constraint instead: minimums relax, content ellipsises, and the table
|
|
19550
|
+
* never scrolls sideways. See the `ColumnFit` docs in snice-table.types.ts.
|
|
19551
|
+
*/
|
|
19552
|
+
this.columnFit = (__runInitializers(this, _density_extraInitializers), __runInitializers(this, _columnFit_initializers, 'scroll'));
|
|
19553
|
+
this.headerFilters = (__runInitializers(this, _columnFit_extraInitializers), __runInitializers(this, _headerFilters_initializers, false));
|
|
18053
19554
|
this.quickFilter = (__runInitializers(this, _headerFilters_extraInitializers), __runInitializers(this, _quickFilter_initializers, false));
|
|
18054
19555
|
this.rowReorder = (__runInitializers(this, _quickFilter_extraInitializers), __runInitializers(this, _rowReorder_initializers, false));
|
|
18055
19556
|
this.columnReorder = (__runInitializers(this, _rowReorder_extraInitializers), __runInitializers(this, _columnReorder_initializers, false));
|
|
@@ -18101,7 +19602,9 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
18101
19602
|
this.table = __runInitializers(this, _table_initializers, void 0);
|
|
18102
19603
|
this.thead = (__runInitializers(this, _table_extraInitializers), __runInitializers(this, _thead_initializers, void 0));
|
|
18103
19604
|
this.tbody = (__runInitializers(this, _thead_extraInitializers), __runInitializers(this, _tbody_initializers, void 0));
|
|
18104
|
-
|
|
19605
|
+
/** Frame width the columns were last fitted to — see handleFrameResize. */
|
|
19606
|
+
this.lastFittedFrameWidth = (__runInitializers(this, _tbody_extraInitializers), 0);
|
|
19607
|
+
this.unsortedData = [];
|
|
18105
19608
|
this.dataRequestTimeout = null;
|
|
18106
19609
|
// A1: row-object → its index in `this.data`. Replaces the O(n) `data.indexOf`
|
|
18107
19610
|
// that ran per row on every selection toggle (was O(n²) for select-all).
|
|
@@ -18133,6 +19636,10 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
18133
19636
|
// C1: set while the imperative setData() runs so the data @watch resyncs the
|
|
18134
19637
|
// model but skips the auto-render (the caller drives the body fill).
|
|
18135
19638
|
this.settingDataImperative = false;
|
|
19639
|
+
// Set while the row-reorder handler re-publishes `this.data` as a new array
|
|
19640
|
+
// of the SAME rows. Tells the data @watch that no fresh dataset arrived, so a
|
|
19641
|
+
// pending load error must survive the drag.
|
|
19642
|
+
this.reorderingRows = false;
|
|
18136
19643
|
// C1/C2: microtask-coalesced render queue. Reactive assignments schedule a
|
|
18137
19644
|
// render here instead of calling renderHeader/renderBody synchronously —
|
|
18138
19645
|
// MANDATORY, because happy-dom crashes constructing cell elements inside a
|
|
@@ -18140,6 +19647,32 @@ if(typeof globalThis.Snice==="undefined"){console.warn("[snice] snice-runtime.mi
|
|
|
18140
19647
|
this.renderQueued = false;
|
|
18141
19648
|
this.pendingHeaderRender = false;
|
|
18142
19649
|
this.pendingBodyRender = false;
|
|
19650
|
+
// Re-arm bookkeeping for a body render that found no <tbody> to paint into.
|
|
19651
|
+
// Bounded: the slotted-rows template legitimately has no <tbody>, and an
|
|
19652
|
+
// unbounded retry there would spin every frame.
|
|
19653
|
+
this.bodyRenderRetries = 0;
|
|
19654
|
+
this.bodyRenderRetryQueued = false;
|
|
19655
|
+
/**
|
|
19656
|
+
* Write a page number the display model just derived (a re-clamp after the
|
|
19657
|
+
* row set shortened), WITHOUT re-entering the render scheduler.
|
|
19658
|
+
*
|
|
19659
|
+
* The three clamp sites all sit inside a display-model computation whose
|
|
19660
|
+
* caller paints immediately afterwards with the clamped page — the ordinary
|
|
19661
|
+
* body path slices with it and then renders the pager, the grouping and tree
|
|
19662
|
+
* models return the clamped slice. Letting the @watch fire here queued a
|
|
19663
|
+
* second, byte-identical body pass per delivery — the measured cost, and what
|
|
19664
|
+
* the regression test pins. Suppressing the write's watcher makes the clamp
|
|
19665
|
+
* cost zero extra passes and makes an oscillating clamp structurally unable
|
|
19666
|
+
* to loop.
|
|
19667
|
+
*
|
|
19668
|
+
* All three sites are reachable only under `paginationMode === 'client'`, so
|
|
19669
|
+
* the watcher's server branch (debouncedDataRequest, whose `loading = true`
|
|
19670
|
+
* re-enters renderBody SYNCHRONOUSLY through the loading watcher — from the
|
|
19671
|
+
* middle of a render) is not reachable from a clamp today. It is the reason
|
|
19672
|
+
* to keep this guard rather than to drop the watcher suppression if a fourth
|
|
19673
|
+
* clamp site ever lands outside that guard.
|
|
19674
|
+
*/
|
|
19675
|
+
this.clampingPage = false;
|
|
18143
19676
|
/**
|
|
18144
19677
|
* A structural fingerprint of everything `createRow` reads that is NOT
|
|
18145
19678
|
* per-row data: visible columns + their widths/pinning/offsets, tool columns,
|