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.
|
|
@@ -675,7 +675,7 @@ class FormLabelAssociation {
|
|
|
675
675
|
}
|
|
676
676
|
}
|
|
677
677
|
|
|
678
|
-
var cssContent$
|
|
678
|
+
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}}";
|
|
679
679
|
|
|
680
680
|
(() => {
|
|
681
681
|
let _classDecorators = [element('snice-input', { formAssociated: true, delegatesFocus: true })];
|
|
@@ -1141,7 +1141,7 @@ var cssContent$k = ":host([hidden]){display:none}:host{display:inline-block;widt
|
|
|
1141
1141
|
`;
|
|
1142
1142
|
}
|
|
1143
1143
|
styles() {
|
|
1144
|
-
return css /*css*/ `${cssContent$
|
|
1144
|
+
return css /*css*/ `${cssContent$l}`;
|
|
1145
1145
|
}
|
|
1146
1146
|
init() {
|
|
1147
1147
|
// Preserve a value assigned before custom-element upgrade. The own data
|
|
@@ -1610,7 +1610,7 @@ var cssContent$k = ":host([hidden]){display:none}:host{display:inline-block;widt
|
|
|
1610
1610
|
return _classThis;
|
|
1611
1611
|
})();
|
|
1612
1612
|
|
|
1613
|
-
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}}";
|
|
1613
|
+
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}}";
|
|
1614
1614
|
|
|
1615
1615
|
(() => {
|
|
1616
1616
|
let _classDecorators = [element('snice-select', { formAssociated: true, delegatesFocus: true })];
|
|
@@ -1953,14 +1953,11 @@ var cssContent$j = ":host([hidden]){display:none}:host{display:inline-block;font
|
|
|
1953
1953
|
get interactionDisabled() {
|
|
1954
1954
|
return this.disabled || this.formDisabled || this.loading;
|
|
1955
1955
|
}
|
|
1956
|
-
/**
|
|
1956
|
+
/** The documented option list: child `<snice-option>` elements win over the `options` array */
|
|
1957
1957
|
get mergedOptions() {
|
|
1958
|
-
//
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
const childValues = new Set(this.childOptions.map(o => o.value));
|
|
1962
|
-
return [...this.childOptions, ...this.options.filter(o => !childValues.has(o.value))];
|
|
1963
|
-
}
|
|
1958
|
+
// "Children take precedence over `options` array" — when children are
|
|
1959
|
+
// present they are the list, so a later `options` assignment cannot
|
|
1960
|
+
// append to or reorder what the author declared in markup.
|
|
1964
1961
|
return this.childOptions.length > 0 ? this.childOptions : this.options;
|
|
1965
1962
|
}
|
|
1966
1963
|
/**
|
|
@@ -2171,7 +2168,7 @@ var cssContent$j = ":host([hidden]){display:none}:host{display:inline-block;font
|
|
|
2171
2168
|
option.setAttribute('data-value', opt.value);
|
|
2172
2169
|
option.setAttribute('role', 'option');
|
|
2173
2170
|
option.setAttribute('aria-selected', String(isSelected));
|
|
2174
|
-
option.setAttribute('aria-disabled', String(opt.disabled));
|
|
2171
|
+
option.setAttribute('aria-disabled', String(Boolean(opt.disabled)));
|
|
2175
2172
|
option.setAttribute('part', 'option');
|
|
2176
2173
|
const check = document.createElement('span');
|
|
2177
2174
|
check.className = 'select-option-check';
|
|
@@ -2191,7 +2188,7 @@ var cssContent$j = ":host([hidden]){display:none}:host{display:inline-block;font
|
|
|
2191
2188
|
return fragment;
|
|
2192
2189
|
}
|
|
2193
2190
|
styles() {
|
|
2194
|
-
return css `${cssContent$
|
|
2191
|
+
return css `${cssContent$k}`;
|
|
2195
2192
|
}
|
|
2196
2193
|
init() {
|
|
2197
2194
|
// Read options from child snice-option elements
|
|
@@ -3051,7 +3048,7 @@ var cssContent$j = ":host([hidden]){display:none}:host{display:inline-block;font
|
|
|
3051
3048
|
return _classThis;
|
|
3052
3049
|
})();
|
|
3053
3050
|
|
|
3054
|
-
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}}";
|
|
3051
|
+
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}}";
|
|
3055
3052
|
|
|
3056
3053
|
(() => {
|
|
3057
3054
|
let _classDecorators = [element('snice-button', { formAssociated: true, delegatesFocus: true })];
|
|
@@ -3314,7 +3311,7 @@ var cssContent$i = ":host([hidden]){display:none}:host{display:inline-block;posi
|
|
|
3314
3311
|
this.formDisabled = disabled;
|
|
3315
3312
|
}
|
|
3316
3313
|
styles() {
|
|
3317
|
-
return css /*css*/ `${cssContent$
|
|
3314
|
+
return css /*css*/ `${cssContent$j}`;
|
|
3318
3315
|
}
|
|
3319
3316
|
// Keep these methods for backwards compatibility if needed
|
|
3320
3317
|
setLoading(loading) {
|
|
@@ -3351,7 +3348,7 @@ var cssContent$i = ":host([hidden]){display:none}:host{display:inline-block;posi
|
|
|
3351
3348
|
return _classThis;
|
|
3352
3349
|
})();
|
|
3353
3350
|
|
|
3354
|
-
var cssContent$
|
|
3351
|
+
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}}";
|
|
3355
3352
|
|
|
3356
3353
|
(() => {
|
|
3357
3354
|
let _classDecorators = [element('snice-checkbox', { formAssociated: true, delegatesFocus: true })];
|
|
@@ -3767,7 +3764,7 @@ var cssContent$h = ":host([hidden]){display:none}:host{display:inline-flex;align
|
|
|
3767
3764
|
};
|
|
3768
3765
|
}
|
|
3769
3766
|
styles() {
|
|
3770
|
-
return css /*css*/ `${cssContent$
|
|
3767
|
+
return css /*css*/ `${cssContent$i}`;
|
|
3771
3768
|
}
|
|
3772
3769
|
// Public API
|
|
3773
3770
|
focus() {
|
|
@@ -3829,7 +3826,7 @@ var cssContent$h = ":host([hidden]){display:none}:host{display:inline-flex;align
|
|
|
3829
3826
|
return _classThis;
|
|
3830
3827
|
})();
|
|
3831
3828
|
|
|
3832
|
-
var cssContent$
|
|
3829
|
+
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}}";
|
|
3833
3830
|
|
|
3834
3831
|
(() => {
|
|
3835
3832
|
let _classDecorators = [element('snice-modal')];
|
|
@@ -4002,7 +3999,7 @@ var cssContent$g = ":host{--modal-z-index:1000;--modal-backdrop-bg:var(--snice-m
|
|
|
4002
3999
|
`;
|
|
4003
4000
|
}
|
|
4004
4001
|
styles() {
|
|
4005
|
-
return css /*css*/ `${cssContent$
|
|
4002
|
+
return css /*css*/ `${cssContent$h}`;
|
|
4006
4003
|
}
|
|
4007
4004
|
init() {
|
|
4008
4005
|
// Make sure modal state is properly initialized
|
|
@@ -4136,7 +4133,7 @@ var cssContent$g = ":host{--modal-z-index:1000;--modal-backdrop-bg:var(--snice-m
|
|
|
4136
4133
|
return _classThis;
|
|
4137
4134
|
})();
|
|
4138
4135
|
|
|
4139
|
-
var cssContent$
|
|
4136
|
+
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}}";
|
|
4140
4137
|
|
|
4141
4138
|
(() => {
|
|
4142
4139
|
let _classDecorators = [element('snice-empty-state')];
|
|
@@ -4231,7 +4228,7 @@ var cssContent$f = ":host([hidden]){display:none}:host{display:block;font-family
|
|
|
4231
4228
|
`;
|
|
4232
4229
|
}
|
|
4233
4230
|
styles() {
|
|
4234
|
-
return css /*css*/ `${cssContent$
|
|
4231
|
+
return css /*css*/ `${cssContent$g}`;
|
|
4235
4232
|
}
|
|
4236
4233
|
handleActionClick(e) {
|
|
4237
4234
|
if (!this.actionHref) {
|
|
@@ -4256,6 +4253,265 @@ var cssContent$f = ":host([hidden]){display:none}:host{display:block;font-family
|
|
|
4256
4253
|
return _classThis;
|
|
4257
4254
|
})();
|
|
4258
4255
|
|
|
4256
|
+
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}}";
|
|
4257
|
+
|
|
4258
|
+
(() => {
|
|
4259
|
+
let _classDecorators = [element('snice-progress')];
|
|
4260
|
+
let _classDescriptor;
|
|
4261
|
+
let _classExtraInitializers = [];
|
|
4262
|
+
let _classThis;
|
|
4263
|
+
let _classSuper = HTMLElement;
|
|
4264
|
+
let _instanceExtraInitializers = [];
|
|
4265
|
+
let _value_decorators;
|
|
4266
|
+
let _value_initializers = [];
|
|
4267
|
+
let _value_extraInitializers = [];
|
|
4268
|
+
let _max_decorators;
|
|
4269
|
+
let _max_initializers = [];
|
|
4270
|
+
let _max_extraInitializers = [];
|
|
4271
|
+
let _variant_decorators;
|
|
4272
|
+
let _variant_initializers = [];
|
|
4273
|
+
let _variant_extraInitializers = [];
|
|
4274
|
+
let _size_decorators;
|
|
4275
|
+
let _size_initializers = [];
|
|
4276
|
+
let _size_extraInitializers = [];
|
|
4277
|
+
let _color_decorators;
|
|
4278
|
+
let _color_initializers = [];
|
|
4279
|
+
let _color_extraInitializers = [];
|
|
4280
|
+
let _indeterminate_decorators;
|
|
4281
|
+
let _indeterminate_initializers = [];
|
|
4282
|
+
let _indeterminate_extraInitializers = [];
|
|
4283
|
+
let _showLabel_decorators;
|
|
4284
|
+
let _showLabel_initializers = [];
|
|
4285
|
+
let _showLabel_extraInitializers = [];
|
|
4286
|
+
let _label_decorators;
|
|
4287
|
+
let _label_initializers = [];
|
|
4288
|
+
let _label_extraInitializers = [];
|
|
4289
|
+
let _striped_decorators;
|
|
4290
|
+
let _striped_initializers = [];
|
|
4291
|
+
let _striped_extraInitializers = [];
|
|
4292
|
+
let _animated_decorators;
|
|
4293
|
+
let _animated_initializers = [];
|
|
4294
|
+
let _animated_extraInitializers = [];
|
|
4295
|
+
let _thickness_decorators;
|
|
4296
|
+
let _thickness_initializers = [];
|
|
4297
|
+
let _thickness_extraInitializers = [];
|
|
4298
|
+
let _progressBar_decorators;
|
|
4299
|
+
let _progressBar_initializers = [];
|
|
4300
|
+
let _progressBar_extraInitializers = [];
|
|
4301
|
+
let _circleBar_decorators;
|
|
4302
|
+
let _circleBar_initializers = [];
|
|
4303
|
+
let _circleBar_extraInitializers = [];
|
|
4304
|
+
let _labelElement_decorators;
|
|
4305
|
+
let _labelElement_initializers = [];
|
|
4306
|
+
let _labelElement_extraInitializers = [];
|
|
4307
|
+
let _circleLabelElement_decorators;
|
|
4308
|
+
let _circleLabelElement_initializers = [];
|
|
4309
|
+
let _circleLabelElement_extraInitializers = [];
|
|
4310
|
+
let _render_decorators;
|
|
4311
|
+
let _styles_decorators;
|
|
4312
|
+
let _handleProgressChange_decorators;
|
|
4313
|
+
let _handleThicknessChange_decorators;
|
|
4314
|
+
let _handleColorChange_decorators;
|
|
4315
|
+
(class extends _classSuper {
|
|
4316
|
+
static { _classThis = this; }
|
|
4317
|
+
constructor() {
|
|
4318
|
+
super(...arguments);
|
|
4319
|
+
this.value = (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _value_initializers, 0));
|
|
4320
|
+
this.max = (__runInitializers(this, _value_extraInitializers), __runInitializers(this, _max_initializers, 100));
|
|
4321
|
+
this.variant = (__runInitializers(this, _max_extraInitializers), __runInitializers(this, _variant_initializers, 'linear'));
|
|
4322
|
+
this.size = (__runInitializers(this, _variant_extraInitializers), __runInitializers(this, _size_initializers, 'medium'));
|
|
4323
|
+
this.color = (__runInitializers(this, _size_extraInitializers), __runInitializers(this, _color_initializers, 'primary'));
|
|
4324
|
+
this.indeterminate = (__runInitializers(this, _color_extraInitializers), __runInitializers(this, _indeterminate_initializers, false));
|
|
4325
|
+
this.showLabel = (__runInitializers(this, _indeterminate_extraInitializers), __runInitializers(this, _showLabel_initializers, false));
|
|
4326
|
+
this.label = (__runInitializers(this, _showLabel_extraInitializers), __runInitializers(this, _label_initializers, ''));
|
|
4327
|
+
this.striped = (__runInitializers(this, _label_extraInitializers), __runInitializers(this, _striped_initializers, false));
|
|
4328
|
+
this.animated = (__runInitializers(this, _striped_extraInitializers), __runInitializers(this, _animated_initializers, false));
|
|
4329
|
+
this.thickness = (__runInitializers(this, _animated_extraInitializers), __runInitializers(this, _thickness_initializers, 4));
|
|
4330
|
+
this.progressBar = (__runInitializers(this, _thickness_extraInitializers), __runInitializers(this, _progressBar_initializers, void 0));
|
|
4331
|
+
this.circleBar = (__runInitializers(this, _progressBar_extraInitializers), __runInitializers(this, _circleBar_initializers, void 0));
|
|
4332
|
+
this.labelElement = (__runInitializers(this, _circleBar_extraInitializers), __runInitializers(this, _labelElement_initializers, void 0));
|
|
4333
|
+
this.circleLabelElement = (__runInitializers(this, _labelElement_extraInitializers), __runInitializers(this, _circleLabelElement_initializers, void 0));
|
|
4334
|
+
this.radius = (__runInitializers(this, _circleLabelElement_extraInitializers), 0);
|
|
4335
|
+
this.circumference = 0;
|
|
4336
|
+
}
|
|
4337
|
+
static {
|
|
4338
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
4339
|
+
_value_decorators = [property({ type: Number, })];
|
|
4340
|
+
_max_decorators = [property({ type: Number, })];
|
|
4341
|
+
_variant_decorators = [property({})];
|
|
4342
|
+
_size_decorators = [property({})];
|
|
4343
|
+
_color_decorators = [property({})];
|
|
4344
|
+
_indeterminate_decorators = [property({ type: Boolean, })];
|
|
4345
|
+
_showLabel_decorators = [property({ type: Boolean, attribute: 'show-label', })];
|
|
4346
|
+
_label_decorators = [property({})];
|
|
4347
|
+
_striped_decorators = [property({ type: Boolean, })];
|
|
4348
|
+
_animated_decorators = [property({ type: Boolean, })];
|
|
4349
|
+
_thickness_decorators = [property({ type: Number, })];
|
|
4350
|
+
_progressBar_decorators = [query('.progress__bar')];
|
|
4351
|
+
_circleBar_decorators = [query('.progress__circle-bar')];
|
|
4352
|
+
_labelElement_decorators = [query('.progress__label')];
|
|
4353
|
+
_circleLabelElement_decorators = [query('.progress__circle-label')];
|
|
4354
|
+
_render_decorators = [render()];
|
|
4355
|
+
_styles_decorators = [styles()];
|
|
4356
|
+
_handleProgressChange_decorators = [watch('value', 'max', 'indeterminate', { immediate: false })];
|
|
4357
|
+
_handleThicknessChange_decorators = [watch('thickness')];
|
|
4358
|
+
_handleColorChange_decorators = [watch('color')];
|
|
4359
|
+
__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);
|
|
4360
|
+
__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);
|
|
4361
|
+
__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);
|
|
4362
|
+
__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);
|
|
4363
|
+
__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);
|
|
4364
|
+
__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);
|
|
4365
|
+
__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);
|
|
4366
|
+
__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);
|
|
4367
|
+
__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);
|
|
4368
|
+
__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);
|
|
4369
|
+
__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);
|
|
4370
|
+
__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);
|
|
4371
|
+
__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);
|
|
4372
|
+
__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);
|
|
4373
|
+
__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);
|
|
4374
|
+
__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);
|
|
4375
|
+
__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);
|
|
4376
|
+
__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);
|
|
4377
|
+
__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);
|
|
4378
|
+
__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);
|
|
4379
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
4380
|
+
_classThis = _classDescriptor.value;
|
|
4381
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
4382
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
4383
|
+
}
|
|
4384
|
+
render() {
|
|
4385
|
+
if (this.variant === 'circular') {
|
|
4386
|
+
return this.renderCircular();
|
|
4387
|
+
}
|
|
4388
|
+
return this.renderLinear();
|
|
4389
|
+
}
|
|
4390
|
+
styles() {
|
|
4391
|
+
return css `${cssContent$f}`;
|
|
4392
|
+
}
|
|
4393
|
+
renderLinear() {
|
|
4394
|
+
const percentage = this.getPercentage();
|
|
4395
|
+
const labelText = this.label || `${Math.round(percentage)}%`;
|
|
4396
|
+
const progressClass = `progress progress--linear${this.showLabel ? ' progress--with-label' : ''}`;
|
|
4397
|
+
const barStyle = this.indeterminate ? '' : `width: ${percentage}%`;
|
|
4398
|
+
return html /*html*/ `
|
|
4399
|
+
<div part="base" class="${progressClass}"
|
|
4400
|
+
role="progressbar"
|
|
4401
|
+
aria-valuenow="${this.value}"
|
|
4402
|
+
aria-valuemin="0"
|
|
4403
|
+
aria-valuemax="${this.max}"
|
|
4404
|
+
aria-label="${labelText}">
|
|
4405
|
+
<div part="bar" class="progress__bar" style="${barStyle}">
|
|
4406
|
+
<if ${this.showLabel}>
|
|
4407
|
+
<span part="label" class="progress__label">${labelText}</span>
|
|
4408
|
+
</if>
|
|
4409
|
+
</div>
|
|
4410
|
+
</div>
|
|
4411
|
+
`;
|
|
4412
|
+
}
|
|
4413
|
+
renderCircular() {
|
|
4414
|
+
const size = this.getSizeValue();
|
|
4415
|
+
const strokeWidth = this.thickness;
|
|
4416
|
+
const radius = (size - strokeWidth * 2) / 2;
|
|
4417
|
+
const circumference = radius * 2 * Math.PI;
|
|
4418
|
+
const percentage = this.getPercentage();
|
|
4419
|
+
const offset = this.indeterminate ? 0 : circumference - (percentage / 100) * circumference;
|
|
4420
|
+
const labelText = this.label || `${Math.round(percentage)}%`;
|
|
4421
|
+
this.radius = radius;
|
|
4422
|
+
this.circumference = circumference;
|
|
4423
|
+
const viewBox = `0 0 ${size} ${size}`;
|
|
4424
|
+
const centerX = size / 2;
|
|
4425
|
+
const centerY = size / 2;
|
|
4426
|
+
return html /*html*/ `
|
|
4427
|
+
<div part="base" class="progress progress--circular"
|
|
4428
|
+
role="progressbar"
|
|
4429
|
+
aria-valuenow="${this.value}"
|
|
4430
|
+
aria-valuemin="0"
|
|
4431
|
+
aria-valuemax="${this.max}"
|
|
4432
|
+
aria-label="${labelText}">
|
|
4433
|
+
<svg part="circle" class="progress__circle" viewBox="${viewBox}">
|
|
4434
|
+
<circle
|
|
4435
|
+
part="circle-bg"
|
|
4436
|
+
class="progress__circle-bg"
|
|
4437
|
+
cx="${centerX}"
|
|
4438
|
+
cy="${centerY}"
|
|
4439
|
+
r="${radius}"
|
|
4440
|
+
/>
|
|
4441
|
+
<circle
|
|
4442
|
+
part="circle-bar"
|
|
4443
|
+
class="progress__circle-bar"
|
|
4444
|
+
cx="${centerX}"
|
|
4445
|
+
cy="${centerY}"
|
|
4446
|
+
r="${radius}"
|
|
4447
|
+
stroke-dasharray="${circumference}"
|
|
4448
|
+
stroke-dashoffset="${offset}"
|
|
4449
|
+
/>
|
|
4450
|
+
</svg>
|
|
4451
|
+
<if ${this.showLabel && !this.indeterminate}>
|
|
4452
|
+
<div part="label" class="progress__circle-label">${labelText}</div>
|
|
4453
|
+
</if>
|
|
4454
|
+
</div>
|
|
4455
|
+
`;
|
|
4456
|
+
}
|
|
4457
|
+
handleProgressChange() {
|
|
4458
|
+
this.dispatchProgressChange();
|
|
4459
|
+
}
|
|
4460
|
+
handleThicknessChange() {
|
|
4461
|
+
if (this.variant === 'circular') {
|
|
4462
|
+
this.style.setProperty('--progress-spinner-stroke', `${this.thickness}px`);
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
handleColorChange() {
|
|
4466
|
+
const semanticColors = ['primary', 'success', 'warning', 'error', 'info'];
|
|
4467
|
+
if (!semanticColors.includes(this.color)) {
|
|
4468
|
+
// Custom color value - apply directly via CSS variable
|
|
4469
|
+
this.style.setProperty('--progress-color', this.color);
|
|
4470
|
+
}
|
|
4471
|
+
else {
|
|
4472
|
+
// Semantic color - remove inline style, let CSS handle it
|
|
4473
|
+
this.style.removeProperty('--progress-color');
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
getPercentage() {
|
|
4477
|
+
if (this.indeterminate)
|
|
4478
|
+
return 0;
|
|
4479
|
+
if (this.max === 0)
|
|
4480
|
+
return 0;
|
|
4481
|
+
return Math.min(100, Math.max(0, (this.value / this.max) * 100));
|
|
4482
|
+
}
|
|
4483
|
+
setProgress(value, max) {
|
|
4484
|
+
this.value = value;
|
|
4485
|
+
if (max !== undefined) {
|
|
4486
|
+
this.max = max;
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4489
|
+
getSizeValue() {
|
|
4490
|
+
switch (this.size) {
|
|
4491
|
+
case 'small': return 24;
|
|
4492
|
+
case 'large': return 56;
|
|
4493
|
+
case 'xl': return 80;
|
|
4494
|
+
case 'xxl': return 120;
|
|
4495
|
+
case 'xxxl': return 160;
|
|
4496
|
+
default: return 40;
|
|
4497
|
+
}
|
|
4498
|
+
}
|
|
4499
|
+
dispatchProgressChange() {
|
|
4500
|
+
this.dispatchEvent(new CustomEvent('progress-change', {
|
|
4501
|
+
bubbles: true,
|
|
4502
|
+
composed: true,
|
|
4503
|
+
detail: {
|
|
4504
|
+
value: this.value,
|
|
4505
|
+
max: this.max,
|
|
4506
|
+
percentage: this.getPercentage(),
|
|
4507
|
+
indeterminate: this.indeterminate
|
|
4508
|
+
}
|
|
4509
|
+
}));
|
|
4510
|
+
}
|
|
4511
|
+
});
|
|
4512
|
+
return _classThis;
|
|
4513
|
+
})();
|
|
4514
|
+
|
|
4259
4515
|
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}}";
|
|
4260
4516
|
|
|
4261
4517
|
const presentationProperties = [
|
|
@@ -4266,6 +4522,37 @@ const presentationProperties = [
|
|
|
4266
4522
|
'fontSize',
|
|
4267
4523
|
'textDecoration',
|
|
4268
4524
|
];
|
|
4525
|
+
/** The alignment a column type renders with when the column declares none. */
|
|
4526
|
+
function defaultCellAlign(type) {
|
|
4527
|
+
if (['number', 'currency', 'percent', 'percentage', 'duration', 'filesize',
|
|
4528
|
+
'accounting', 'scientific', 'fraction'].includes(type || ''))
|
|
4529
|
+
return 'right';
|
|
4530
|
+
if (['boolean', 'rating', 'image'].includes(type || ''))
|
|
4531
|
+
return 'center';
|
|
4532
|
+
return 'left';
|
|
4533
|
+
}
|
|
4534
|
+
/**
|
|
4535
|
+
* The value a typed cell holds when the row has nothing to show — the row field
|
|
4536
|
+
* is null, or the row never carried the field at all.
|
|
4537
|
+
*
|
|
4538
|
+
* Each typed cell owns its value semantics: `false` for boolean, `0` for
|
|
4539
|
+
* rating/progress/duration/filesize, `null` for JSON, `''` for the text-shaped
|
|
4540
|
+
* rest. Handing every empty value the same `''` instead lets the cell's typed
|
|
4541
|
+
* attribute converter re-read that empty string as a value of the wrong
|
|
4542
|
+
* meaning — `''` on a Boolean property is an attribute that is *present*, i.e.
|
|
4543
|
+
* `true`, and `''` on an Object property parses to `{}`.
|
|
4544
|
+
*/
|
|
4545
|
+
function emptyCellValue(type) {
|
|
4546
|
+
switch (type) {
|
|
4547
|
+
case 'boolean': return false;
|
|
4548
|
+
case 'rating':
|
|
4549
|
+
case 'progress':
|
|
4550
|
+
case 'duration':
|
|
4551
|
+
case 'filesize': return 0;
|
|
4552
|
+
case 'json': return null;
|
|
4553
|
+
default: return '';
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4269
4556
|
const installed = new WeakMap();
|
|
4270
4557
|
const appliedClasses = new WeakMap();
|
|
4271
4558
|
/**
|
|
@@ -4279,10 +4566,9 @@ const appliedClasses = new WeakMap();
|
|
|
4279
4566
|
* fallback, so `formatter` wins. `formatterOverride` says the cell does NOT
|
|
4280
4567
|
* apply `column.formatter` itself, so this module owns both formatters for it.
|
|
4281
4568
|
*
|
|
4282
|
-
*
|
|
4283
|
-
*
|
|
4284
|
-
*
|
|
4285
|
-
* renders structure rather than text, still shows the formatted string.
|
|
4569
|
+
* Table-rendered cells never arrive here with a formatter: `createCellElement`
|
|
4570
|
+
* resolves the pipeline once and hands the text cell the formatted string. This
|
|
4571
|
+
* path serves the declarative and standalone cells, which own a full column.
|
|
4286
4572
|
*/
|
|
4287
4573
|
function installCellPresentation(host, formatterOverride = false) {
|
|
4288
4574
|
if (installed.has(host))
|
|
@@ -4335,12 +4621,8 @@ function installCellPresentation(host, formatterOverride = false) {
|
|
|
4335
4621
|
const formatter = column.formatter
|
|
4336
4622
|
? (formatterOverride ? column.formatter : null)
|
|
4337
4623
|
: column.valueFormatter;
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
const display = host.hasAttribute('formatted')
|
|
4341
|
-
? (host.getAttribute('value') ?? '')
|
|
4342
|
-
: (formatter ? String(formatter(host.value, host.rowData) ?? '') : null);
|
|
4343
|
-
if (display !== null) {
|
|
4624
|
+
if (formatter) {
|
|
4625
|
+
const display = String(formatter(host.value, host.rowData) ?? '');
|
|
4344
4626
|
const content = host.shadowRoot?.querySelector('[part~="content"]');
|
|
4345
4627
|
if (content && (content.textContent !== display || content.childElementCount > 0)) {
|
|
4346
4628
|
content.textContent = display;
|
|
@@ -4361,6 +4643,82 @@ function installCellPresentation(host, formatterOverride = false) {
|
|
|
4361
4643
|
queueMicrotask(sync);
|
|
4362
4644
|
}
|
|
4363
4645
|
|
|
4646
|
+
/**
|
|
4647
|
+
* Date parsing for the table's cell layer.
|
|
4648
|
+
*
|
|
4649
|
+
* A column of type `date` usually carries calendar dates: `'2026-03-15'` names
|
|
4650
|
+
* a day, not an instant. `new Date('2026-03-15')` nevertheless resolves the
|
|
4651
|
+
* ISO date-only forms as UTC midnight (ECMA-262 21.4.3.2), so every consumer
|
|
4652
|
+
* in a negative-offset zone formatted the day BEFORE the authored one, while
|
|
4653
|
+
* an `<input type="date">` editor holding the same string showed the authored
|
|
4654
|
+
* day. Display, export, and filtering must all agree with the string the
|
|
4655
|
+
* application wrote.
|
|
4656
|
+
*
|
|
4657
|
+
* The rule: a value that carries NO time is a calendar day and is read at
|
|
4658
|
+
* local midnight; anything that carries a time (or an offset, or is already a
|
|
4659
|
+
* `Date`/epoch number) is an instant and is left to the platform.
|
|
4660
|
+
*/
|
|
4661
|
+
/** ISO calendar dates with no time part: `YYYY`, `YYYY-MM`, `YYYY-MM-DD`. */
|
|
4662
|
+
const DATE_ONLY = /^(\d{4})(?:-(\d{2}))?(?:-(\d{2}))?$/;
|
|
4663
|
+
/**
|
|
4664
|
+
* Parse a cell value into a `Date`, or `null` when there is nothing to show.
|
|
4665
|
+
*
|
|
4666
|
+
* `null` covers the empty cases (`null`, `undefined`, `''`) and unparseable
|
|
4667
|
+
* input alike, so callers fall back to the raw value rather than rendering
|
|
4668
|
+
* "Invalid Date" or the epoch.
|
|
4669
|
+
*/
|
|
4670
|
+
function parseCellDate(value) {
|
|
4671
|
+
if (value === null || value === undefined || value === '')
|
|
4672
|
+
return null;
|
|
4673
|
+
if (value instanceof Date) {
|
|
4674
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
4675
|
+
}
|
|
4676
|
+
if (typeof value === 'number') {
|
|
4677
|
+
const fromEpoch = new Date(value);
|
|
4678
|
+
return Number.isNaN(fromEpoch.getTime()) ? null : fromEpoch;
|
|
4679
|
+
}
|
|
4680
|
+
if (typeof value === 'string') {
|
|
4681
|
+
const trimmed = value.trim();
|
|
4682
|
+
if (trimmed === '')
|
|
4683
|
+
return null;
|
|
4684
|
+
const calendarDay = DATE_ONLY.exec(trimmed);
|
|
4685
|
+
if (calendarDay) {
|
|
4686
|
+
const year = Number(calendarDay[1]);
|
|
4687
|
+
const month = calendarDay[2] === undefined ? 1 : Number(calendarDay[2]);
|
|
4688
|
+
const day = calendarDay[3] === undefined ? 1 : Number(calendarDay[3]);
|
|
4689
|
+
// Local midnight: the same calendar day in every timezone.
|
|
4690
|
+
const local = new Date(year, month - 1, day);
|
|
4691
|
+
// Years 0-99 mean themselves, not 1900+n.
|
|
4692
|
+
if (year >= 0 && year <= 99)
|
|
4693
|
+
local.setFullYear(year, month - 1, day);
|
|
4694
|
+
if (Number.isNaN(local.getTime()))
|
|
4695
|
+
return null;
|
|
4696
|
+
// `new Date(2026, 12, 1)` rolls over to 2027 instead of rejecting, so a
|
|
4697
|
+
// typo like '2026-13-01' or '2026-02-30' would render as a confident but
|
|
4698
|
+
// WRONG day. The platform calls those strings invalid; so do we, and the
|
|
4699
|
+
// caller falls back to showing the raw value.
|
|
4700
|
+
if (local.getFullYear() !== year ||
|
|
4701
|
+
local.getMonth() !== month - 1 ||
|
|
4702
|
+
local.getDate() !== day) {
|
|
4703
|
+
return null;
|
|
4704
|
+
}
|
|
4705
|
+
return local;
|
|
4706
|
+
}
|
|
4707
|
+
const parsed = new Date(trimmed);
|
|
4708
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
4709
|
+
}
|
|
4710
|
+
const coerced = new Date(value);
|
|
4711
|
+
return Number.isNaN(coerced.getTime()) ? null : coerced;
|
|
4712
|
+
}
|
|
4713
|
+
/**
|
|
4714
|
+
* Midnight on the calendar day a value falls on, in local time. Used by
|
|
4715
|
+
* day-granularity comparisons (today/past/future classification) so an
|
|
4716
|
+
* instant is never bumped across a day boundary by the UTC offset.
|
|
4717
|
+
*/
|
|
4718
|
+
function startOfLocalDay(date) {
|
|
4719
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
4720
|
+
}
|
|
4721
|
+
|
|
4364
4722
|
(() => {
|
|
4365
4723
|
let _classDecorators = [element('snice-cell')];
|
|
4366
4724
|
let _classDescriptor;
|
|
@@ -4447,7 +4805,14 @@ function installCellPresentation(host, formatterOverride = false) {
|
|
|
4447
4805
|
this.applyConditionalFormatting();
|
|
4448
4806
|
}
|
|
4449
4807
|
applyAlignment() {
|
|
4450
|
-
|
|
4808
|
+
// snice-cell.css already encodes each type's default alignment
|
|
4809
|
+
// (:host([type="accounting"]) and friends are right-aligned, boolean/rating
|
|
4810
|
+
// centred). Writing an inline text-align unconditionally OVERRODE that, so a
|
|
4811
|
+
// standalone accounting figure sat left-aligned beside a right-aligned
|
|
4812
|
+
// currency cell — and the accounting format's alignment padding meant
|
|
4813
|
+
// nothing. Only an alignment the caller actually declared wins.
|
|
4814
|
+
const declared = this.getAttribute('align');
|
|
4815
|
+
this.style.textAlign = declared ?? '';
|
|
4451
4816
|
}
|
|
4452
4817
|
applyConditionalFormatting() {
|
|
4453
4818
|
if (!this.column)
|
|
@@ -4578,24 +4943,60 @@ function installCellPresentation(host, formatterOverride = false) {
|
|
|
4578
4943
|
return String(this.value);
|
|
4579
4944
|
return `${num.toFixed(format.decimals ?? 2)}%`;
|
|
4580
4945
|
}
|
|
4946
|
+
/**
|
|
4947
|
+
* Accounting notation is currency notation: symbol, grouped digits, and
|
|
4948
|
+
* negatives in parentheses instead of a minus sign. It shares its defaults
|
|
4949
|
+
* with `formatCurrency()` on purpose — an `accounting` cell and a `currency`
|
|
4950
|
+
* cell showing the same amount must not disagree about what money looks
|
|
4951
|
+
* like. `numberFormat` still steers decimals/grouping (its historic role
|
|
4952
|
+
* here), `currencyFormat` steers the symbol and locale, and an explicit
|
|
4953
|
+
* `numberFormat.prefix` opts out of the symbol entirely for ledgers that
|
|
4954
|
+
* label their own columns.
|
|
4955
|
+
*/
|
|
4581
4956
|
formatAccounting() {
|
|
4582
4957
|
const format = this.column.numberFormat || {};
|
|
4958
|
+
const currency = this.column.currencyFormat || {};
|
|
4583
4959
|
const num = Number(this.value);
|
|
4584
4960
|
if (isNaN(num))
|
|
4585
4961
|
return String(this.value);
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4962
|
+
const decimals = format.decimals ?? currency.decimals ?? 2;
|
|
4963
|
+
const useGrouping = format.thousandsSeparator ?? currency.thousandsSeparator ?? true;
|
|
4964
|
+
const magnitude = Math.abs(num);
|
|
4965
|
+
const options = {
|
|
4966
|
+
useGrouping,
|
|
4967
|
+
minimumFractionDigits: decimals,
|
|
4968
|
+
maximumFractionDigits: decimals,
|
|
4969
|
+
};
|
|
4970
|
+
// An author-supplied prefix/suffix is the caller naming the unit; adding a
|
|
4971
|
+
// currency symbol on top would print it twice.
|
|
4972
|
+
const symbolIsCallers = !!format.prefix || !!format.suffix;
|
|
4973
|
+
if (!symbolIsCallers) {
|
|
4974
|
+
options.style = 'currency';
|
|
4975
|
+
options.currency = currency.currency || 'USD';
|
|
4976
|
+
options.currencyDisplay = currency.currencyDisplay || currency.display || 'symbol';
|
|
4977
|
+
}
|
|
4978
|
+
let formatted;
|
|
4979
|
+
try {
|
|
4980
|
+
formatted = new Intl.NumberFormat(currency.locale || 'en-US', options).format(magnitude);
|
|
4594
4981
|
}
|
|
4595
|
-
|
|
4596
|
-
formatted =
|
|
4982
|
+
catch {
|
|
4983
|
+
formatted = magnitude.toFixed(decimals);
|
|
4984
|
+
if (useGrouping) {
|
|
4985
|
+
const parts = formatted.split('.');
|
|
4986
|
+
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
4987
|
+
formatted = parts.join('.');
|
|
4988
|
+
}
|
|
4989
|
+
if (!symbolIsCallers)
|
|
4990
|
+
formatted = `$${formatted}`;
|
|
4597
4991
|
}
|
|
4598
|
-
|
|
4992
|
+
if (format.prefix)
|
|
4993
|
+
formatted = format.prefix + formatted;
|
|
4994
|
+
if (format.suffix)
|
|
4995
|
+
formatted = formatted + format.suffix;
|
|
4996
|
+
// The trailing space is the format's whole reason for existing: it keeps a
|
|
4997
|
+
// positive row's last digit under the ")" of the row above in a
|
|
4998
|
+
// right-aligned column. A normal space collapses in HTML, so pad with NBSP.
|
|
4999
|
+
return num < 0 ? `(${formatted})` : `${formatted}\u00a0`;
|
|
4599
5000
|
}
|
|
4600
5001
|
formatScientific() {
|
|
4601
5002
|
const format = this.column.numberFormat || {};
|
|
@@ -4637,8 +5038,10 @@ function installCellPresentation(host, formatterOverride = false) {
|
|
|
4637
5038
|
}
|
|
4638
5039
|
formatDate() {
|
|
4639
5040
|
const format = this.column.dateFormat || {};
|
|
4640
|
-
|
|
4641
|
-
|
|
5041
|
+
// Calendar days are read at local midnight (see table-date.ts) so this
|
|
5042
|
+
// generic cell shows the same day as `snice-cell-date` and as the editor.
|
|
5043
|
+
const date = parseCellDate(this.value);
|
|
5044
|
+
if (!date)
|
|
4642
5045
|
return String(this.value);
|
|
4643
5046
|
if (format.customFormat) {
|
|
4644
5047
|
// Simple custom format implementation
|
|
@@ -5277,7 +5680,7 @@ function installCellPresentation(host, formatterOverride = false) {
|
|
|
5277
5680
|
_styles_decorators = [styles()];
|
|
5278
5681
|
_init_decorators = [ready()];
|
|
5279
5682
|
_updateAlignment_decorators = [watch('align')];
|
|
5280
|
-
_updateContent_decorators = [watch('value', 'column')];
|
|
5683
|
+
_updateContent_decorators = [watch('value', 'column', 'dateFormat', 'customFormat', 'locale', 'relativeTime', 'showTime')];
|
|
5281
5684
|
__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);
|
|
5282
5685
|
__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);
|
|
5283
5686
|
__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);
|
|
@@ -5321,6 +5724,9 @@ function installCellPresentation(host, formatterOverride = false) {
|
|
|
5321
5724
|
updateAlignment() {
|
|
5322
5725
|
this.applyAlignment();
|
|
5323
5726
|
}
|
|
5727
|
+
// Every input the formatter reads, not just the value: a late
|
|
5728
|
+
// `custom-format` / `locale` / `show-time` change must repaint the day it
|
|
5729
|
+
// describes, or the cell keeps showing the format it happened to be born with.
|
|
5324
5730
|
updateContent() {
|
|
5325
5731
|
if (this.contentElement) {
|
|
5326
5732
|
const formattedValue = this.formatDateValue();
|
|
@@ -5337,8 +5743,11 @@ function installCellPresentation(host, formatterOverride = false) {
|
|
|
5337
5743
|
if (this.column.formatter) {
|
|
5338
5744
|
return this.column.formatter(this.value, this.rowData);
|
|
5339
5745
|
}
|
|
5340
|
-
|
|
5341
|
-
|
|
5746
|
+
// A date-only value names a calendar day, so it is read at LOCAL midnight
|
|
5747
|
+
// (see table-date.ts). Parsing it with `new Date` would place it at UTC
|
|
5748
|
+
// midnight and render the previous day for every negative UTC offset.
|
|
5749
|
+
const date = parseCellDate(this.value);
|
|
5750
|
+
if (!date) {
|
|
5342
5751
|
return String(this.value);
|
|
5343
5752
|
}
|
|
5344
5753
|
// Show relative time if enabled
|
|
@@ -5425,13 +5834,18 @@ function installCellPresentation(host, formatterOverride = false) {
|
|
|
5425
5834
|
}
|
|
5426
5835
|
}
|
|
5427
5836
|
getDateStyles() {
|
|
5428
|
-
const date =
|
|
5429
|
-
const now = new Date();
|
|
5430
|
-
const isToday = date.toDateString() === now.toDateString();
|
|
5431
|
-
const isPast = date < now;
|
|
5432
|
-
const isFuture = date > now;
|
|
5837
|
+
const date = parseCellDate(this.value);
|
|
5433
5838
|
// Apply CSS classes instead of direct styles
|
|
5434
5839
|
this.classList.remove('date--today', 'date--past', 'date--future');
|
|
5840
|
+
if (!date)
|
|
5841
|
+
return '';
|
|
5842
|
+
// Day granularity: a calendar day is today, past, or future as a whole —
|
|
5843
|
+
// the wall-clock time of day never decides which.
|
|
5844
|
+
const day = startOfLocalDay(date).getTime();
|
|
5845
|
+
const today = startOfLocalDay(new Date()).getTime();
|
|
5846
|
+
const isToday = day === today;
|
|
5847
|
+
const isPast = day < today;
|
|
5848
|
+
const isFuture = day > today;
|
|
5435
5849
|
if (isToday) {
|
|
5436
5850
|
this.classList.add('date--today');
|
|
5437
5851
|
}
|
|
@@ -7126,7 +7540,7 @@ var cssContent$b = ":host([hidden]){display:none}:host{color:var(--snice-color-t
|
|
|
7126
7540
|
return _classThis;
|
|
7127
7541
|
})();
|
|
7128
7542
|
|
|
7129
|
-
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))}}";
|
|
7543
|
+
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))}}";
|
|
7130
7544
|
|
|
7131
7545
|
(() => {
|
|
7132
7546
|
let _classDecorators = [element('snice-cell-status')];
|
|
@@ -7266,7 +7680,7 @@ var cssContent$a = ":host([hidden]){display:none}:host{color:var(--snice-color-t
|
|
|
7266
7680
|
return _classThis;
|
|
7267
7681
|
})();
|
|
7268
7682
|
|
|
7269
|
-
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}}";
|
|
7683
|
+
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}}";
|
|
7270
7684
|
|
|
7271
7685
|
(() => {
|
|
7272
7686
|
let _classDecorators = [element('snice-cell-actions')];
|
|
@@ -7404,7 +7818,7 @@ var cssContent$9 = ":host([hidden]){display:none}:host{color:var(--snice-color-t
|
|
|
7404
7818
|
return _classThis;
|
|
7405
7819
|
})();
|
|
7406
7820
|
|
|
7407
|
-
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))}}";
|
|
7821
|
+
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))}}";
|
|
7408
7822
|
|
|
7409
7823
|
(() => {
|
|
7410
7824
|
let _classDecorators = [element('snice-cell-link')];
|
|
@@ -7544,7 +7958,7 @@ var cssContent$8 = ":host([hidden]){display:none}:host{color:var(--snice-color-t
|
|
|
7544
7958
|
return _classThis;
|
|
7545
7959
|
})();
|
|
7546
7960
|
|
|
7547
|
-
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}}";
|
|
7961
|
+
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}}";
|
|
7548
7962
|
|
|
7549
7963
|
(() => {
|
|
7550
7964
|
let _classDecorators = [element('snice-cell-email')];
|
|
@@ -7665,7 +8079,7 @@ var cssContent$7 = ":host([hidden]){display:none}:host{color:var(--snice-color-t
|
|
|
7665
8079
|
return _classThis;
|
|
7666
8080
|
})();
|
|
7667
8081
|
|
|
7668
|
-
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}}";
|
|
8082
|
+
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}}";
|
|
7669
8083
|
|
|
7670
8084
|
(() => {
|
|
7671
8085
|
let _classDecorators = [element('snice-cell-phone')];
|
|
@@ -7814,7 +8228,7 @@ var cssContent$6 = ":host([hidden]){display:none}:host{color:var(--snice-color-t
|
|
|
7814
8228
|
return _classThis;
|
|
7815
8229
|
})();
|
|
7816
8230
|
|
|
7817
|
-
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}}";
|
|
8231
|
+
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}}";
|
|
7818
8232
|
|
|
7819
8233
|
(() => {
|
|
7820
8234
|
let _classDecorators = [element('snice-cell-color')];
|
|
@@ -7998,7 +8412,7 @@ var cssContent$5 = ":host([hidden]){display:none}:host{color:var(--snice-color-t
|
|
|
7998
8412
|
return _classThis;
|
|
7999
8413
|
})();
|
|
8000
8414
|
|
|
8001
|
-
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}}";
|
|
8415
|
+
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}}";
|
|
8002
8416
|
|
|
8003
8417
|
(() => {
|
|
8004
8418
|
let _classDecorators = [element('snice-cell-location')];
|
|
@@ -8181,7 +8595,7 @@ var cssContent$4 = ":host([hidden]){display:none}:host{color:var(--snice-color-t
|
|
|
8181
8595
|
return _classThis;
|
|
8182
8596
|
})();
|
|
8183
8597
|
|
|
8184
|
-
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}}";
|
|
8598
|
+
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}}";
|
|
8185
8599
|
|
|
8186
8600
|
(() => {
|
|
8187
8601
|
let _classDecorators = [element('snice-cell-json')];
|
|
@@ -8602,7 +9016,7 @@ function escHtml(s) {
|
|
|
8602
9016
|
return _classThis;
|
|
8603
9017
|
})();
|
|
8604
9018
|
|
|
8605
|
-
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}}";
|
|
9019
|
+
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}}";
|
|
8606
9020
|
|
|
8607
9021
|
(() => {
|
|
8608
9022
|
let _classDecorators = [element('snice-cell-image')];
|
|
@@ -9306,7 +9720,7 @@ var cssContent$2 = ":host([hidden]){display:none}:host{color:var(--snice-color-t
|
|
|
9306
9720
|
return _classThis;
|
|
9307
9721
|
})();
|
|
9308
9722
|
|
|
9309
|
-
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-
|
|
9723
|
+
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}}";
|
|
9310
9724
|
|
|
9311
9725
|
(() => {
|
|
9312
9726
|
let _classDecorators = [element('snice-row')];
|
|
@@ -9559,19 +9973,12 @@ var cssContent$1 = ":host([hidden]){display:none}:host{display:block;width:100%;
|
|
|
9559
9973
|
<div class="cell cell--${column.type}" part="cell" style="${this.getCellStyles(column)}">
|
|
9560
9974
|
${unsafeHTML(`<${cellComponent}
|
|
9561
9975
|
type="${column.type}"
|
|
9562
|
-
align="${column.align ||
|
|
9976
|
+
align="${column.align || defaultCellAlign(column.type)}"
|
|
9563
9977
|
data-column-index="${columnIndex}"
|
|
9564
9978
|
></${cellComponent}>`)}
|
|
9565
9979
|
</div>
|
|
9566
9980
|
`;
|
|
9567
9981
|
}
|
|
9568
|
-
getDefaultAlign(type) {
|
|
9569
|
-
if (['number', 'currency', 'percent', 'percentage', 'duration', 'filesize', 'accounting', 'scientific', 'fraction'].includes(type || ''))
|
|
9570
|
-
return 'right';
|
|
9571
|
-
if (['boolean', 'rating', 'image'].includes(type || ''))
|
|
9572
|
-
return 'center';
|
|
9573
|
-
return 'left';
|
|
9574
|
-
}
|
|
9575
9982
|
applyCellPresentation(wrapper, column, value) {
|
|
9576
9983
|
if (!wrapper)
|
|
9577
9984
|
return;
|
|
@@ -9804,11 +10211,24 @@ class TableVirtualizer {
|
|
|
9804
10211
|
window.removeEventListener('resize', this.scrollHandler);
|
|
9805
10212
|
cancelAnimationFrame(this.rafId);
|
|
9806
10213
|
}
|
|
9807
|
-
|
|
10214
|
+
/**
|
|
10215
|
+
* Publish a new row count. `repaint` false updates the model only — the
|
|
10216
|
+
* pending re-render is still armed (lastStartIndex is reset), so the caller's
|
|
10217
|
+
* own paint (scrollToIndex / refresh) performs it. Callers that resolve a
|
|
10218
|
+
* display index and then scroll MUST use that form: painting here runs
|
|
10219
|
+
* `afterRender`, and a host whose afterRender restores keyboard focus would
|
|
10220
|
+
* re-enter this method from inside its own update.
|
|
10221
|
+
*/
|
|
10222
|
+
setTotalRows(total, repaint = true) {
|
|
9808
10223
|
this.options.totalRows = total;
|
|
9809
10224
|
this.invalidateOffsets();
|
|
9810
10225
|
this.lastStartIndex = -1; // Force re-render
|
|
9811
|
-
|
|
10226
|
+
if (repaint)
|
|
10227
|
+
this.update();
|
|
10228
|
+
}
|
|
10229
|
+
/** Row count the virtualizer is currently windowing over. */
|
|
10230
|
+
getTotalRows() {
|
|
10231
|
+
return this.options.totalRows ?? 0;
|
|
9812
10232
|
}
|
|
9813
10233
|
/**
|
|
9814
10234
|
* Force a full re-render of the visible range.
|
|
@@ -9819,11 +10239,30 @@ class TableVirtualizer {
|
|
|
9819
10239
|
this.lastEndIndex = -1;
|
|
9820
10240
|
this.update();
|
|
9821
10241
|
}
|
|
10242
|
+
/**
|
|
10243
|
+
* The element that scrolls RIGHT NOW. A structural re-render of the host
|
|
10244
|
+
* swaps the node captured at `attach()`; keep the listener and every lookup
|
|
10245
|
+
* (notably the `<tbody>` we paint into) on the live one.
|
|
10246
|
+
*/
|
|
10247
|
+
resolveContainer() {
|
|
10248
|
+
const live = this.options.resolveScrollContainer?.() ?? null;
|
|
10249
|
+
const current = this.options.scrollContainer;
|
|
10250
|
+
if (live && live !== current) {
|
|
10251
|
+
current?.removeEventListener('scroll', this.scrollHandler);
|
|
10252
|
+
live.addEventListener('scroll', this.scrollHandler, { passive: true });
|
|
10253
|
+
this.options.scrollContainer = live;
|
|
10254
|
+
// The new container's tbody is empty — the range we believed painted is
|
|
10255
|
+
// not on screen anymore.
|
|
10256
|
+
this.lastStartIndex = -1;
|
|
10257
|
+
this.lastEndIndex = -1;
|
|
10258
|
+
}
|
|
10259
|
+
return this.options.scrollContainer ?? null;
|
|
10260
|
+
}
|
|
9822
10261
|
/**
|
|
9823
10262
|
* Calculate visible range and render if changed.
|
|
9824
10263
|
*/
|
|
9825
10264
|
update() {
|
|
9826
|
-
if (!this.enabled || !this.
|
|
10265
|
+
if (!this.enabled || !this.resolveContainer())
|
|
9827
10266
|
return;
|
|
9828
10267
|
const { bufferPx, totalRows, scrollContainer, renderRange } = this.options;
|
|
9829
10268
|
const scrollTop = scrollContainer.scrollTop;
|
|
@@ -9837,11 +10276,12 @@ class TableVirtualizer {
|
|
|
9837
10276
|
// Filtering, pagination, or collapsing a group can shrink the flattened
|
|
9838
10277
|
// model while the container is scrolled near the old end. Clamp before
|
|
9839
10278
|
// deriving indices so startIndex never sits beyond totalRows and blanks the
|
|
9840
|
-
// body.
|
|
9841
|
-
//
|
|
10279
|
+
// body. Only an offset that overruns the whole content is written back to
|
|
10280
|
+
// the element — a browser already clamps its own scrollTop, and rewriting a
|
|
10281
|
+
// deliberate offset (scrollToRow) would undo the caller's request.
|
|
9842
10282
|
const maxScrollTop = Math.max(0, totalHeight - viewportHeight);
|
|
9843
10283
|
const effectiveScrollTop = Math.min(Math.max(0, scrollTop), maxScrollTop);
|
|
9844
|
-
if (scrollTop
|
|
10284
|
+
if (scrollTop > totalHeight || scrollTop < 0)
|
|
9845
10285
|
scrollContainer.scrollTop = effectiveScrollTop;
|
|
9846
10286
|
// Calculate visible range with buffer
|
|
9847
10287
|
const startPx = Math.max(0, effectiveScrollTop - bufferPx);
|
|
@@ -9851,75 +10291,89 @@ class TableVirtualizer {
|
|
|
9851
10291
|
// Skip if range hasn't changed
|
|
9852
10292
|
if (startIndex === this.lastStartIndex && endIndex === this.lastEndIndex)
|
|
9853
10293
|
return;
|
|
9854
|
-
|
|
9855
|
-
|
|
10294
|
+
// Nothing to paint into yet (the host template has not committed its table,
|
|
10295
|
+
// or a structural branch flip is mid-flight). Return WITHOUT recording the
|
|
10296
|
+
// range: a dropped paint that looks like a completed one makes the guard
|
|
10297
|
+
// above suppress every retry, and then only a viewport-height change (a
|
|
10298
|
+
// window resize) ever repaints the rows.
|
|
10299
|
+
const tbody = scrollContainer.querySelector('tbody');
|
|
10300
|
+
if (!tbody)
|
|
10301
|
+
return;
|
|
9856
10302
|
// Update spacers
|
|
9857
10303
|
const topHeight = offsets[startIndex] || 0;
|
|
9858
10304
|
const bottomHeight = Math.max(0, totalHeight - (offsets[endIndex] || totalHeight));
|
|
9859
10305
|
this.topSpacer.style.height = `${topHeight}px`;
|
|
9860
10306
|
this.bottomSpacer.style.height = `${bottomHeight}px`;
|
|
10307
|
+
// Span the columns that exist, never more (see resolveColumnSpan).
|
|
10308
|
+
const columnSpan = Math.max(1, Math.round(this.options.resolveColumnSpan?.() ?? 1));
|
|
9861
10309
|
// Render the visible range
|
|
9862
10310
|
const fragment = renderRange(startIndex, endIndex);
|
|
9863
10311
|
// Build the tbody content
|
|
9864
|
-
|
|
9865
|
-
|
|
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
|
-
|
|
10312
|
+
tbody.innerHTML = '';
|
|
10313
|
+
// Pinned-top rows render outside (above) the windowed range so they are
|
|
10314
|
+
// always present. Rebuilt every window recompute since tbody is wiped.
|
|
10315
|
+
const pinnedTop = this.options.renderPinnedTop?.();
|
|
10316
|
+
if (pinnedTop)
|
|
10317
|
+
tbody.appendChild(pinnedTop);
|
|
10318
|
+
if (topHeight > 0) {
|
|
10319
|
+
const topTd = document.createElement('td');
|
|
10320
|
+
topTd.colSpan = columnSpan;
|
|
10321
|
+
topTd.style.height = `${topHeight}px`;
|
|
10322
|
+
topTd.style.padding = '0';
|
|
10323
|
+
topTd.style.border = 'none';
|
|
10324
|
+
this.topSpacer.innerHTML = '';
|
|
10325
|
+
this.topSpacer.appendChild(topTd);
|
|
10326
|
+
tbody.appendChild(this.topSpacer);
|
|
10327
|
+
}
|
|
10328
|
+
tbody.appendChild(fragment);
|
|
10329
|
+
// Nothing to window over: hand the body back its empty/loading/error row
|
|
10330
|
+
// instead of leaving the user staring at a blank frame.
|
|
10331
|
+
if (totalRows === 0) {
|
|
10332
|
+
const emptyBody = this.options.renderEmptyBody?.();
|
|
10333
|
+
if (emptyBody)
|
|
10334
|
+
tbody.appendChild(emptyBody);
|
|
10335
|
+
}
|
|
10336
|
+
if (bottomHeight > 0) {
|
|
10337
|
+
const bottomTd = document.createElement('td');
|
|
10338
|
+
bottomTd.colSpan = columnSpan;
|
|
10339
|
+
bottomTd.style.height = `${bottomHeight}px`;
|
|
10340
|
+
bottomTd.style.padding = '0';
|
|
10341
|
+
bottomTd.style.border = 'none';
|
|
10342
|
+
this.bottomSpacer.innerHTML = '';
|
|
10343
|
+
this.bottomSpacer.appendChild(bottomTd);
|
|
10344
|
+
tbody.appendChild(this.bottomSpacer);
|
|
10345
|
+
}
|
|
10346
|
+
// Pinned-bottom rows render outside (below) the windowed range.
|
|
10347
|
+
const pinnedBottom = this.options.renderPinnedBottom?.();
|
|
10348
|
+
if (pinnedBottom)
|
|
10349
|
+
tbody.appendChild(pinnedBottom);
|
|
10350
|
+
// The window is on screen — only NOW does this range count as painted.
|
|
10351
|
+
// Committed before afterRender so a re-entrant update() from that hook
|
|
10352
|
+
// (keyboard focus restoration scrolls) sees the range it is looking at.
|
|
10353
|
+
this.lastStartIndex = startIndex;
|
|
10354
|
+
this.lastEndIndex = endIndex;
|
|
10355
|
+
this.options.afterRender?.();
|
|
9899
10356
|
}
|
|
9900
10357
|
/** Get the currently visible row index range */
|
|
9901
10358
|
getVisibleRange() {
|
|
9902
10359
|
return { start: this.lastStartIndex, end: this.lastEndIndex };
|
|
9903
10360
|
}
|
|
9904
|
-
/** Scroll to bring a specific row into view */
|
|
9905
|
-
scrollToRow(index) {
|
|
9906
|
-
if (!this.options.scrollContainer)
|
|
9907
|
-
return;
|
|
9908
|
-
const top = this.offsetForIndex(index);
|
|
9909
|
-
this.options.scrollContainer.scrollTop = top;
|
|
9910
|
-
}
|
|
9911
10361
|
/**
|
|
9912
|
-
* Scroll a
|
|
9913
|
-
*
|
|
9914
|
-
*
|
|
9915
|
-
*
|
|
10362
|
+
* Scroll a row into view AND synchronously render the window around it.
|
|
10363
|
+
* A programmatic `scrollTop` assignment fires no scroll event, so the window
|
|
10364
|
+
* has to be recomputed here — otherwise the viewport lands on bare spacer and
|
|
10365
|
+
* the requested row is never in the DOM.
|
|
9916
10366
|
*/
|
|
9917
|
-
|
|
10367
|
+
scrollToRow(index) {
|
|
9918
10368
|
if (!this.options.scrollContainer)
|
|
9919
10369
|
return;
|
|
9920
10370
|
this.options.scrollContainer.scrollTop = this.offsetForIndex(index);
|
|
9921
10371
|
this.refresh();
|
|
9922
10372
|
}
|
|
10373
|
+
/** Scroll to a logical (flattened display) row index. */
|
|
10374
|
+
scrollToIndex(index) {
|
|
10375
|
+
this.scrollToRow(index);
|
|
10376
|
+
}
|
|
9923
10377
|
/** Get current scroll position */
|
|
9924
10378
|
getScrollPosition() {
|
|
9925
10379
|
const el = this.options.scrollContainer;
|
|
@@ -9989,6 +10443,13 @@ class TableVirtualizer {
|
|
|
9989
10443
|
* Column management for snice-table.
|
|
9990
10444
|
* Handles: flex width, resize, auto-size, visibility, ordering, pinning, groups.
|
|
9991
10445
|
*/
|
|
10446
|
+
/** Padding + border a cell spends before any content fits inside it. */
|
|
10447
|
+
function horizontalCellChrome(cell) {
|
|
10448
|
+
const style = getComputedStyle(cell);
|
|
10449
|
+
const px = (value) => Number.parseFloat(value) || 0;
|
|
10450
|
+
return px(style.paddingLeft) + px(style.paddingRight)
|
|
10451
|
+
+ px(style.borderLeftWidth) + px(style.borderRightWidth);
|
|
10452
|
+
}
|
|
9992
10453
|
class TableColumnManager {
|
|
9993
10454
|
constructor() {
|
|
9994
10455
|
this.states = new Map();
|
|
@@ -9998,27 +10459,110 @@ class TableColumnManager {
|
|
|
9998
10459
|
this.resizingKey = null;
|
|
9999
10460
|
this.resizeOverlay = null;
|
|
10000
10461
|
this.tableElement = null;
|
|
10462
|
+
this.fitMode = 'scroll';
|
|
10463
|
+
/**
|
|
10464
|
+
* The content budget the last `squish` fit shared out. A drag-resize needs it
|
|
10465
|
+
* to know how much width the columns beside the dragged one have to give up
|
|
10466
|
+
* (or take back) for the total to stay on the frame.
|
|
10467
|
+
*/
|
|
10468
|
+
this.squishBudget = null;
|
|
10469
|
+
}
|
|
10470
|
+
/** Fallback content width for a column nobody sized. */
|
|
10471
|
+
static { this.DEFAULT_WIDTH = 150; }
|
|
10472
|
+
/**
|
|
10473
|
+
* The floor `squish` relaxes `minWidth` down to. Not zero: a column narrower
|
|
10474
|
+
* than this shows no glyph at all, only its ellipsis, and stops being a
|
|
10475
|
+
* column the reader can identify. A frame too narrow even for this is past
|
|
10476
|
+
* what the mode can honour — see `fitVisibleColumns`.
|
|
10477
|
+
*/
|
|
10478
|
+
static { this.SQUISH_MIN_WIDTH = 24; }
|
|
10479
|
+
/**
|
|
10480
|
+
* Which fitting policy `fitVisibleColumns` (and a drag-resize) follows.
|
|
10481
|
+
* Owned by the table's `columnFit` property; see the `ColumnFit` docs.
|
|
10482
|
+
*/
|
|
10483
|
+
setFitMode(mode) {
|
|
10484
|
+
if (mode === this.fitMode)
|
|
10485
|
+
return;
|
|
10486
|
+
this.fitMode = mode;
|
|
10487
|
+
// The budget belongs to the squish fit that recorded it.
|
|
10488
|
+
if (mode !== 'squish')
|
|
10489
|
+
this.squishBudget = null;
|
|
10490
|
+
}
|
|
10491
|
+
getFitMode() {
|
|
10492
|
+
return this.fitMode;
|
|
10001
10493
|
}
|
|
10002
|
-
/**
|
|
10494
|
+
/**
|
|
10495
|
+
* Routine resync, run on every render: make sure a state exists for each
|
|
10496
|
+
* declared column and refresh the flags that are pure declaration (min/max,
|
|
10497
|
+
* resizable, reorderable, hideable, pinnable, flex).
|
|
10498
|
+
*
|
|
10499
|
+
* Deliberately PRESERVES the interaction-owned members of an existing state —
|
|
10500
|
+
* width, visible, pinned, order — because renderBody() calls this on every
|
|
10501
|
+
* paint, and re-applying the declaration here would undo a drag-resize, a
|
|
10502
|
+
* hidden column or a user reorder on the very next render. Re-applying the
|
|
10503
|
+
* declaration is applyConfiguration()'s job, and only a `columns` assignment
|
|
10504
|
+
* calls that.
|
|
10505
|
+
*/
|
|
10003
10506
|
initialize(columns, tableEl) {
|
|
10004
10507
|
this.tableElement = tableEl;
|
|
10005
10508
|
columns.forEach((col, index) => {
|
|
10006
10509
|
const existing = this.states.get(col.key);
|
|
10007
|
-
this.states.set(col.key,
|
|
10008
|
-
|
|
10009
|
-
|
|
10010
|
-
|
|
10011
|
-
|
|
10012
|
-
|
|
10013
|
-
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10017
|
-
|
|
10018
|
-
|
|
10019
|
-
|
|
10020
|
-
|
|
10510
|
+
this.states.set(col.key, this.buildState(col, index, existing));
|
|
10511
|
+
});
|
|
10512
|
+
}
|
|
10513
|
+
/**
|
|
10514
|
+
* A `columns` assignment (or setColumns()) delivered a NEW CONFIGURATION.
|
|
10515
|
+
*
|
|
10516
|
+
* The declaration is the source of truth here: painted order, `pinned`,
|
|
10517
|
+
* `width` and visibility all come from the array just assigned, and states for
|
|
10518
|
+
* columns the new array no longer declares are DROPPED. Without this the first
|
|
10519
|
+
* configuration the table ever saw won permanently — a re-ordered array kept
|
|
10520
|
+
* the original order, a newly declared pin or width was ignored, and a column
|
|
10521
|
+
* hidden through setColumnVisible() could never be brought back
|
|
10522
|
+
* (MATRIX-columns-2/3/4/6).
|
|
10523
|
+
*
|
|
10524
|
+
* The cost is that the assignment also discards user interaction state (a
|
|
10525
|
+
* drag-resize, a manual reorder, a hidden column) for the columns it
|
|
10526
|
+
* re-declares. That is the intended reading of the contract, and it applies to
|
|
10527
|
+
* EVERY assignment: `columns` declares `hasChanged: () => true`, so handing
|
|
10528
|
+
* back the array the table already holds — after mutating it, or unchanged —
|
|
10529
|
+
* re-applies the declaration exactly like a fresh array would. Mutating the
|
|
10530
|
+
* published definitions is therefore NOT an escape hatch; the only way to keep
|
|
10531
|
+
* user column state is to not assign `columns` at all and drive the change
|
|
10532
|
+
* through the column APIs instead (setColumnVisible / moveColumn / pinColumn /
|
|
10533
|
+
* unpinColumn), which mutate the state map in place. Renders triggered by this
|
|
10534
|
+
* go through the table's coalescing queue, so re-applying costs no extra paint.
|
|
10535
|
+
*/
|
|
10536
|
+
applyConfiguration(columns, tableEl) {
|
|
10537
|
+
this.tableElement = tableEl;
|
|
10538
|
+
const next = new Map();
|
|
10539
|
+
columns.forEach((col, index) => {
|
|
10540
|
+
next.set(col.key, this.buildState(col, index));
|
|
10021
10541
|
});
|
|
10542
|
+
this.states = next;
|
|
10543
|
+
}
|
|
10544
|
+
/**
|
|
10545
|
+
* One place that turns a column definition into a state. `existing` supplies
|
|
10546
|
+
* the interaction-owned members on the resync path; omitting it yields the
|
|
10547
|
+
* pure declared state applyConfiguration() wants.
|
|
10548
|
+
*/
|
|
10549
|
+
buildState(col, index, existing) {
|
|
10550
|
+
const declared = this.parseWidth(col.width);
|
|
10551
|
+
return {
|
|
10552
|
+
key: col.key,
|
|
10553
|
+
width: existing?.width ?? declared ?? TableColumnManager.DEFAULT_WIDTH,
|
|
10554
|
+
flex: col.flex,
|
|
10555
|
+
minWidth: col.minWidth ?? 50,
|
|
10556
|
+
maxWidth: col.maxWidth ?? Infinity,
|
|
10557
|
+
visible: existing?.visible ?? true,
|
|
10558
|
+
pinned: existing?.pinned ?? (col.pinned || false),
|
|
10559
|
+
order: existing?.order ?? index,
|
|
10560
|
+
resizable: col.resizable !== false,
|
|
10561
|
+
reorderable: col.reorderable !== false,
|
|
10562
|
+
hideable: col.hideable !== false,
|
|
10563
|
+
pinnable: col.pinnable !== false,
|
|
10564
|
+
authored: existing?.authored ?? declared !== undefined,
|
|
10565
|
+
};
|
|
10022
10566
|
}
|
|
10023
10567
|
parseWidth(width) {
|
|
10024
10568
|
if (!width)
|
|
@@ -10074,48 +10618,222 @@ class TableColumnManager {
|
|
|
10074
10618
|
}
|
|
10075
10619
|
return widths;
|
|
10076
10620
|
}
|
|
10077
|
-
/**
|
|
10078
|
-
|
|
10079
|
-
|
|
10080
|
-
|
|
10081
|
-
|
|
10082
|
-
|
|
10083
|
-
|
|
10621
|
+
/**
|
|
10622
|
+
* Fit the columns nobody sized into `availableWidth` (the frame's content
|
|
10623
|
+
* box), reserving `chromePerColumn` for each column's own padding + border.
|
|
10624
|
+
*
|
|
10625
|
+
* A column width is a CONTENT width — the rendered column is that plus its
|
|
10626
|
+
* chrome — so the budget has to come off the top or the fitted table lands a
|
|
10627
|
+
* few pixels wider than the frame it was fitted to.
|
|
10628
|
+
*
|
|
10629
|
+
* Declared and user-chosen widths are spent first and never rewritten: a
|
|
10630
|
+
* table that asked for `width: '80'` keeps 80 whatever the frame does. What
|
|
10631
|
+
* is left is split evenly and clamped to each column's [minWidth, maxWidth],
|
|
10632
|
+
* so a frame too narrow for the minimums overflows into the frame's own
|
|
10633
|
+
* scroller instead of collapsing the columns to nothing.
|
|
10634
|
+
*
|
|
10635
|
+
* Returns whether any width actually moved, so callers can skip a repaint.
|
|
10636
|
+
*
|
|
10637
|
+
* Under `column-fit="squish"` the frame stops being a suggestion: see
|
|
10638
|
+
* `squishVisibleColumns`.
|
|
10639
|
+
*/
|
|
10640
|
+
fitVisibleColumns(availableWidth, chromePerColumn = 0) {
|
|
10641
|
+
const visible = this.getVisibleColumns();
|
|
10642
|
+
// A layoutless (or not-yet-laid-out) frame reports 0. Fitting to that would
|
|
10643
|
+
// slam every column to its minimum for a measurement that means nothing.
|
|
10644
|
+
if (visible.length === 0 || !(availableWidth > 0))
|
|
10645
|
+
return false;
|
|
10646
|
+
const budget = availableWidth - visible.length * chromePerColumn;
|
|
10647
|
+
if (this.fitMode === 'squish') {
|
|
10648
|
+
this.squishBudget = budget;
|
|
10649
|
+
return this.squishVisibleColumns(visible, budget);
|
|
10650
|
+
}
|
|
10651
|
+
const auto = visible.filter((state) => !state.authored);
|
|
10652
|
+
if (auto.length === 0)
|
|
10653
|
+
return false;
|
|
10654
|
+
let remaining = budget;
|
|
10655
|
+
for (const state of visible) {
|
|
10656
|
+
if (state.authored)
|
|
10657
|
+
remaining -= state.width;
|
|
10658
|
+
}
|
|
10659
|
+
const share = Math.floor(remaining / auto.length);
|
|
10660
|
+
let changed = false;
|
|
10661
|
+
for (const state of auto) {
|
|
10662
|
+
const next = Math.max(state.minWidth, Math.min(state.maxWidth, share));
|
|
10663
|
+
if (next !== state.width) {
|
|
10664
|
+
state.width = next;
|
|
10665
|
+
changed = true;
|
|
10084
10666
|
}
|
|
10085
|
-
}
|
|
10667
|
+
}
|
|
10668
|
+
return changed;
|
|
10086
10669
|
}
|
|
10087
|
-
|
|
10088
|
-
|
|
10089
|
-
|
|
10090
|
-
|
|
10091
|
-
|
|
10092
|
-
|
|
10093
|
-
|
|
10094
|
-
|
|
10095
|
-
|
|
10096
|
-
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
|
|
10100
|
-
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
|
|
10104
|
-
|
|
10670
|
+
/**
|
|
10671
|
+
* The `squish` policy: the columns add up to the frame, whatever that costs.
|
|
10672
|
+
*
|
|
10673
|
+
* `minWidth` is a readability preference, and this mode has already decided
|
|
10674
|
+
* the frame outranks it, so the only floor left is SQUISH_MIN_WIDTH. Two
|
|
10675
|
+
* cases:
|
|
10676
|
+
*
|
|
10677
|
+
* - There is an unsized column left to absorb the difference, and the
|
|
10678
|
+
* authored widths still leave room for it. Those are honoured exactly (a
|
|
10679
|
+
* column that asked for 80, or that the user dragged to 80, keeps 80) and
|
|
10680
|
+
* the rest is split evenly between the unsized ones.
|
|
10681
|
+
*
|
|
10682
|
+
* - Every column is authored, or the authored widths alone already overflow
|
|
10683
|
+
* the frame. There is nothing left to give, so `scroll`'s escape hatch
|
|
10684
|
+
* (overflow into the frame's scroller) is exactly what this mode exists to
|
|
10685
|
+
* prevent: every column is scaled by the same factor instead. Scaling is a
|
|
10686
|
+
* fixed point — the second pass computes a factor of 1 — so this settles
|
|
10687
|
+
* on the first render rather than creeping on every repaint.
|
|
10688
|
+
*
|
|
10689
|
+
* Either way the remainder from integer division is handed back to the widest
|
|
10690
|
+
* columns that are free to take it, so the widths sum to the budget EXACTLY
|
|
10691
|
+
* and the rightmost column edge lands on the frame's inner edge instead of a
|
|
10692
|
+
* few pixels short of it.
|
|
10693
|
+
*/
|
|
10694
|
+
squishVisibleColumns(visible, budget) {
|
|
10695
|
+
const floor = TableColumnManager.SQUISH_MIN_WIDTH;
|
|
10696
|
+
const auto = visible.filter((state) => !state.authored);
|
|
10697
|
+
// What the frame has left once the authored widths are paid for.
|
|
10698
|
+
const spare = budget - visible.reduce((spent, state) => spent + (state.authored ? state.width : 0), 0);
|
|
10699
|
+
const next = new Map();
|
|
10700
|
+
if (auto.length > 0 && spare >= auto.length * floor) {
|
|
10701
|
+
const share = Math.floor(spare / auto.length);
|
|
10702
|
+
for (const state of visible) {
|
|
10703
|
+
next.set(state.key, state.authored
|
|
10704
|
+
? state.width
|
|
10705
|
+
: Math.max(floor, Math.min(state.maxWidth, share)));
|
|
10706
|
+
}
|
|
10707
|
+
}
|
|
10708
|
+
else {
|
|
10709
|
+
const current = visible.reduce((sum, state) => sum + state.width, 0) || 1;
|
|
10710
|
+
const scale = budget / current;
|
|
10711
|
+
for (const state of visible) {
|
|
10712
|
+
next.set(state.key, Math.max(floor, Math.floor(state.width * scale)));
|
|
10713
|
+
}
|
|
10714
|
+
}
|
|
10715
|
+
// Hand the leftover pixels to the widest column that is free to grow: it
|
|
10716
|
+
// absorbs a few pixels without reading as a different column, and a
|
|
10717
|
+
// narrower one might be sitting on its floor or its maxWidth.
|
|
10718
|
+
let slack = budget - Array.from(next.values()).reduce((sum, w) => sum + w, 0);
|
|
10719
|
+
if (slack > 0) {
|
|
10720
|
+
const takers = visible
|
|
10721
|
+
.filter((state) => (next.get(state.key) ?? 0) < state.maxWidth)
|
|
10722
|
+
.sort((a, b) => (next.get(b.key) ?? 0) - (next.get(a.key) ?? 0));
|
|
10723
|
+
for (const state of takers) {
|
|
10724
|
+
if (slack <= 0)
|
|
10725
|
+
break;
|
|
10726
|
+
const room = Math.min(slack, state.maxWidth - (next.get(state.key) ?? 0));
|
|
10727
|
+
next.set(state.key, (next.get(state.key) ?? 0) + room);
|
|
10728
|
+
slack -= room;
|
|
10729
|
+
}
|
|
10730
|
+
}
|
|
10731
|
+
let changed = false;
|
|
10732
|
+
for (const state of visible) {
|
|
10733
|
+
const width = next.get(state.key);
|
|
10734
|
+
if (width !== state.width) {
|
|
10735
|
+
state.width = width;
|
|
10736
|
+
changed = true;
|
|
10737
|
+
}
|
|
10738
|
+
}
|
|
10739
|
+
return changed;
|
|
10740
|
+
}
|
|
10741
|
+
/**
|
|
10742
|
+
* A drag-resize under `squish` moved one column; the others pay for it.
|
|
10743
|
+
*
|
|
10744
|
+
* The dragged column is the anchor — it keeps the width the pointer asked
|
|
10745
|
+
* for, capped so the remaining columns can still sit on their floor — and the
|
|
10746
|
+
* rest are scaled to fill exactly what is left of the frame. Without this a
|
|
10747
|
+
* drag would simply push the trailing column out of a frame that is not
|
|
10748
|
+
* allowed to scroll.
|
|
10749
|
+
*/
|
|
10750
|
+
rebalanceSquish(anchorKey) {
|
|
10751
|
+
const budget = this.squishBudget;
|
|
10752
|
+
const anchor = this.states.get(anchorKey);
|
|
10753
|
+
if (budget === null || !anchor)
|
|
10754
|
+
return;
|
|
10755
|
+
const others = this.getVisibleColumns().filter((state) => state.key !== anchorKey);
|
|
10756
|
+
if (others.length === 0)
|
|
10757
|
+
return;
|
|
10758
|
+
const floor = TableColumnManager.SQUISH_MIN_WIDTH;
|
|
10759
|
+
anchor.width = Math.max(floor, Math.min(anchor.width, budget - others.length * floor));
|
|
10760
|
+
const remaining = budget - anchor.width;
|
|
10761
|
+
const current = others.reduce((sum, state) => sum + state.width, 0) || 1;
|
|
10762
|
+
const scale = remaining / current;
|
|
10763
|
+
for (const state of others) {
|
|
10764
|
+
state.width = Math.max(floor, Math.floor(state.width * scale));
|
|
10765
|
+
}
|
|
10766
|
+
let slack = remaining - others.reduce((sum, state) => sum + state.width, 0);
|
|
10767
|
+
for (const state of [...others].sort((a, b) => b.width - a.width)) {
|
|
10768
|
+
if (slack <= 0)
|
|
10769
|
+
break;
|
|
10770
|
+
const room = Math.min(slack, state.maxWidth - state.width);
|
|
10771
|
+
state.width += room;
|
|
10772
|
+
slack -= room;
|
|
10773
|
+
}
|
|
10774
|
+
}
|
|
10775
|
+
/** Push the current widths onto the painted header and body cells. */
|
|
10776
|
+
writeWidths(keys) {
|
|
10777
|
+
const root = this.tableElement?.shadowRoot;
|
|
10778
|
+
if (!root)
|
|
10779
|
+
return;
|
|
10780
|
+
for (const key of keys) {
|
|
10781
|
+
const width = this.states.get(key)?.width;
|
|
10782
|
+
if (width === undefined)
|
|
10783
|
+
continue;
|
|
10784
|
+
const th = root.querySelector(`th[data-key="${key}"]`);
|
|
10785
|
+
if (th)
|
|
10786
|
+
th.style.width = `${width}px`;
|
|
10787
|
+
const tds = root.querySelectorAll(`td[data-key="${key}"]`);
|
|
10788
|
+
tds.forEach((td) => { td.style.width = `${width}px`; });
|
|
10789
|
+
}
|
|
10790
|
+
}
|
|
10791
|
+
/** Apply computed widths to <col> or <th> elements */
|
|
10792
|
+
applyWidths(headerRow, widths) {
|
|
10793
|
+
const ths = headerRow.querySelectorAll('th[data-key]');
|
|
10794
|
+
ths.forEach(th => {
|
|
10795
|
+
const key = th.getAttribute('data-key');
|
|
10796
|
+
if (key && widths.has(key)) {
|
|
10797
|
+
th.style.width = `${widths.get(key)}px`;
|
|
10798
|
+
}
|
|
10799
|
+
});
|
|
10800
|
+
}
|
|
10801
|
+
// ── Resize ──
|
|
10802
|
+
/** Start column resize. Call from mousedown on resize handle. */
|
|
10803
|
+
startResize(columnKey, startX) {
|
|
10804
|
+
const state = this.states.get(columnKey);
|
|
10805
|
+
if (!state || !state.resizable)
|
|
10806
|
+
return;
|
|
10807
|
+
this.resizingKey = columnKey;
|
|
10808
|
+
this.resizeStartX = startX;
|
|
10809
|
+
this.resizeStartWidth = state.width;
|
|
10810
|
+
// Create overlay to capture mouse events
|
|
10811
|
+
this.resizeOverlay = document.createElement('div');
|
|
10812
|
+
this.resizeOverlay.style.cssText = 'position:fixed;inset:0;z-index:99999;cursor:col-resize;';
|
|
10813
|
+
document.body.appendChild(this.resizeOverlay);
|
|
10814
|
+
const onMove = (e) => {
|
|
10815
|
+
e.preventDefault();
|
|
10816
|
+
const delta = e.clientX - this.resizeStartX;
|
|
10817
|
+
// Squish relaxes minWidth for the fit, so it has to relax it for the drag
|
|
10818
|
+
// too — otherwise a column the fit already squished below its minimum
|
|
10819
|
+
// would jump back up to it the moment its edge was touched.
|
|
10820
|
+
const squish = this.fitMode === 'squish';
|
|
10821
|
+
const min = squish ? TableColumnManager.SQUISH_MIN_WIDTH : state.minWidth;
|
|
10822
|
+
state.width = Math.max(min, Math.min(state.maxWidth, this.resizeStartWidth + delta));
|
|
10105
10823
|
// Remove flex if manually resized
|
|
10106
10824
|
state.flex = undefined;
|
|
10107
|
-
//
|
|
10108
|
-
|
|
10109
|
-
|
|
10110
|
-
|
|
10111
|
-
|
|
10112
|
-
|
|
10113
|
-
|
|
10114
|
-
|
|
10115
|
-
|
|
10825
|
+
// The user chose this width — the container fit must leave it alone.
|
|
10826
|
+
state.authored = true;
|
|
10827
|
+
// Squish cannot let the drag push the table past the frame: the columns
|
|
10828
|
+
// beside this one give up (or take back) the difference.
|
|
10829
|
+
if (squish)
|
|
10830
|
+
this.rebalanceSquish(columnKey);
|
|
10831
|
+
this.writeWidths(squish
|
|
10832
|
+
? this.getVisibleColumns().map((column) => column.key)
|
|
10833
|
+
: [columnKey]);
|
|
10116
10834
|
// Dispatch resize event
|
|
10117
10835
|
this.tableElement?.dispatchEvent(new CustomEvent('column-resize', {
|
|
10118
|
-
detail: { key: columnKey, width:
|
|
10836
|
+
detail: { key: columnKey, width: state.width },
|
|
10119
10837
|
bubbles: true, composed: true
|
|
10120
10838
|
}));
|
|
10121
10839
|
};
|
|
@@ -10135,24 +10853,81 @@ class TableColumnManager {
|
|
|
10135
10853
|
}
|
|
10136
10854
|
/** Auto-size a column to fit its content */
|
|
10137
10855
|
autoSizeColumn(columnKey, tbody) {
|
|
10138
|
-
|
|
10139
|
-
if (!state)
|
|
10140
|
-
return;
|
|
10141
|
-
// Measure header
|
|
10142
|
-
const th = this.tableElement?.shadowRoot?.querySelector(`th[data-key="${columnKey}"]`);
|
|
10143
|
-
let maxWidth = th ? th.scrollWidth : 0;
|
|
10144
|
-
// Measure body cells
|
|
10145
|
-
const cells = tbody.querySelectorAll(`td[data-key="${columnKey}"]`);
|
|
10146
|
-
cells.forEach(cell => {
|
|
10147
|
-
maxWidth = Math.max(maxWidth, cell.scrollWidth);
|
|
10148
|
-
});
|
|
10149
|
-
state.width = Math.max(state.minWidth, Math.min(state.maxWidth, maxWidth + 16)); // 16px padding
|
|
10150
|
-
state.flex = undefined;
|
|
10856
|
+
this.autoSizeColumns([columnKey], tbody);
|
|
10151
10857
|
}
|
|
10152
10858
|
/** Auto-size all columns */
|
|
10153
10859
|
autoSizeAll(tbody) {
|
|
10154
|
-
|
|
10155
|
-
|
|
10860
|
+
this.autoSizeColumns(Array.from(this.states.keys()), tbody);
|
|
10861
|
+
}
|
|
10862
|
+
/** Slack added to a measured content width so the last glyph never sits on
|
|
10863
|
+
* the clip edge (and a sortable header keeps room for its chevron). */
|
|
10864
|
+
static { this.AUTO_SIZE_SLACK = 4; }
|
|
10865
|
+
/**
|
|
10866
|
+
* Measure the columns' content and give each the width it actually needs.
|
|
10867
|
+
*
|
|
10868
|
+
* A table cell's `scrollWidth` is NOT its content width: for content that
|
|
10869
|
+
* fits, it reports the padding box — the width the column already has. The
|
|
10870
|
+
* previous implementation took that maximum and added 16px, so every
|
|
10871
|
+
* "auto-size" pass could only GROW the column, by a chrome's worth each
|
|
10872
|
+
* time, until the table outgrew its own frame. Once it did, the right-pinned
|
|
10873
|
+
* column (`position: sticky`) parked over the columns beside it, and the
|
|
10874
|
+
* columns behind it read as deleted.
|
|
10875
|
+
*
|
|
10876
|
+
* The width a column NEEDS is a layout question, so ask layout: drop the
|
|
10877
|
+
* table into content-driven sizing (`table-layout: auto` at `max-content`)
|
|
10878
|
+
* with the measured columns' imposed widths removed, and read back the width
|
|
10879
|
+
* each header settles at. `max-content` also means no wrapping, so a long
|
|
10880
|
+
* cell reports its full single-line extent even though the painted column
|
|
10881
|
+
* ellipsises it. Everything is restored in the same task, before paint.
|
|
10882
|
+
*/
|
|
10883
|
+
autoSizeColumns(columnKeys, tbody) {
|
|
10884
|
+
const root = this.tableElement?.shadowRoot ?? null;
|
|
10885
|
+
const tableEl = (root?.querySelector('table')
|
|
10886
|
+
?? tbody.closest('table'));
|
|
10887
|
+
const targets = columnKeys
|
|
10888
|
+
.map((key) => this.states.get(key))
|
|
10889
|
+
.filter((state) => !!state);
|
|
10890
|
+
if (!tableEl || targets.length === 0)
|
|
10891
|
+
return;
|
|
10892
|
+
const restore = [];
|
|
10893
|
+
const push = (el, property) => {
|
|
10894
|
+
const previous = el.style[property];
|
|
10895
|
+
restore.push(() => { el.style[property] = previous; });
|
|
10896
|
+
};
|
|
10897
|
+
push(tableEl, 'tableLayout');
|
|
10898
|
+
push(tableEl, 'width');
|
|
10899
|
+
tableEl.style.tableLayout = 'auto';
|
|
10900
|
+
tableEl.style.width = 'max-content';
|
|
10901
|
+
const headers = new Map();
|
|
10902
|
+
for (const state of targets) {
|
|
10903
|
+
const header = root?.querySelector(`th[data-key="${state.key}"]`);
|
|
10904
|
+
if (!header)
|
|
10905
|
+
continue;
|
|
10906
|
+
headers.set(state.key, header);
|
|
10907
|
+
// An imposed width is still a preferred width under auto layout, so it
|
|
10908
|
+
// has to come off before the column can report what it wants.
|
|
10909
|
+
push(header, 'width');
|
|
10910
|
+
header.style.width = 'auto';
|
|
10911
|
+
for (const cell of Array.from(tbody.querySelectorAll(`td[data-key="${state.key}"]`))) {
|
|
10912
|
+
push(cell, 'width');
|
|
10913
|
+
cell.style.width = 'auto';
|
|
10914
|
+
}
|
|
10915
|
+
}
|
|
10916
|
+
const widths = new Map();
|
|
10917
|
+
for (const [key, header] of headers) {
|
|
10918
|
+
widths.set(key, header.getBoundingClientRect().width - horizontalCellChrome(header));
|
|
10919
|
+
}
|
|
10920
|
+
for (const undo of restore)
|
|
10921
|
+
undo();
|
|
10922
|
+
for (const state of targets) {
|
|
10923
|
+
const content = widths.get(state.key) ?? 0;
|
|
10924
|
+
// Nothing measurable — a layoutless DOM, or a column with no rendered
|
|
10925
|
+
// header. Leave the width alone rather than slamming it to the minimum.
|
|
10926
|
+
if (content <= 0)
|
|
10927
|
+
continue;
|
|
10928
|
+
state.width = Math.max(state.minWidth, Math.min(state.maxWidth, Math.ceil(content) + TableColumnManager.AUTO_SIZE_SLACK));
|
|
10929
|
+
state.flex = undefined;
|
|
10930
|
+
state.authored = true;
|
|
10156
10931
|
}
|
|
10157
10932
|
}
|
|
10158
10933
|
// ── Visibility ──
|
|
@@ -10473,25 +11248,45 @@ class TableFilterEngine {
|
|
|
10473
11248
|
return Number(cellValue) < Number(filterValue);
|
|
10474
11249
|
case 'lte':
|
|
10475
11250
|
return Number(cellValue) <= Number(filterValue);
|
|
10476
|
-
// Date
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
|
|
11251
|
+
// Date. Both sides cross the cell layer's parser, so a `2026-03-15`
|
|
11252
|
+
// bound means that calendar day locally — the same day the cell paints
|
|
11253
|
+
// — instead of UTC midnight, which shifted every comparison by a day
|
|
11254
|
+
// for negative UTC offsets and split a date-only bound from a
|
|
11255
|
+
// timestamped cell value on the same day.
|
|
11256
|
+
case 'is':
|
|
10482
11257
|
case 'isNot': {
|
|
10483
|
-
const
|
|
10484
|
-
const
|
|
10485
|
-
|
|
11258
|
+
const cellDay = this.calendarDay(cellValue);
|
|
11259
|
+
const filterDay = this.calendarDay(filterValue);
|
|
11260
|
+
// An unparseable value is on no day: it never "is" the filtered day,
|
|
11261
|
+
// and it is therefore always "isNot" it.
|
|
11262
|
+
const sameDay = cellDay !== null && filterDay !== null && cellDay === filterDay;
|
|
11263
|
+
return filter.operator === 'is' ? sameDay : !sameDay;
|
|
10486
11264
|
}
|
|
10487
11265
|
case 'before':
|
|
10488
|
-
return new Date(cellValue) < new Date(filterValue);
|
|
10489
11266
|
case 'onOrBefore':
|
|
10490
|
-
return new Date(cellValue) <= new Date(filterValue);
|
|
10491
11267
|
case 'after':
|
|
10492
|
-
|
|
10493
|
-
|
|
10494
|
-
|
|
11268
|
+
case 'onOrAfter': {
|
|
11269
|
+
const cell = parseCellDate(cellValue);
|
|
11270
|
+
const bound = parseCellDate(filterValue);
|
|
11271
|
+
if (!cell || !bound)
|
|
11272
|
+
return false;
|
|
11273
|
+
const a = cell.getTime();
|
|
11274
|
+
// A date-only bound names a whole DAY, not the instant it starts at, so
|
|
11275
|
+
// the day's two edges bracket it. Comparing everything against its
|
|
11276
|
+
// opening instant made the four operators disagree with `is`: a cell at
|
|
11277
|
+
// 09:00 on the bound day "is" that day, yet counted as `after` it and
|
|
11278
|
+
// fell outside `onOrBefore` it. A bound that carries its own time is an
|
|
11279
|
+
// instant, and both edges collapse onto it.
|
|
11280
|
+
const dayStart = bound.getTime();
|
|
11281
|
+
const dayEnd = this.boundEnd(filterValue, bound);
|
|
11282
|
+
if (filter.operator === 'before')
|
|
11283
|
+
return a < dayStart;
|
|
11284
|
+
if (filter.operator === 'onOrBefore')
|
|
11285
|
+
return a <= dayEnd;
|
|
11286
|
+
if (filter.operator === 'after')
|
|
11287
|
+
return a > dayEnd;
|
|
11288
|
+
return a >= dayStart;
|
|
11289
|
+
}
|
|
10495
11290
|
// Boolean
|
|
10496
11291
|
case 'isTrue':
|
|
10497
11292
|
return cellValue === true || cellValue === 'true' || cellValue === 1;
|
|
@@ -10501,6 +11296,28 @@ class TableFilterEngine {
|
|
|
10501
11296
|
return true;
|
|
10502
11297
|
}
|
|
10503
11298
|
}
|
|
11299
|
+
/**
|
|
11300
|
+
* The local calendar day a value falls on, as a `YYYY-M-D` stamp, or `null`
|
|
11301
|
+
* when the value names no day at all.
|
|
11302
|
+
*/
|
|
11303
|
+
calendarDay(value) {
|
|
11304
|
+
const date = parseCellDate(value);
|
|
11305
|
+
if (!date)
|
|
11306
|
+
return null;
|
|
11307
|
+
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
|
|
11308
|
+
}
|
|
11309
|
+
/** True when the raw bound named a calendar day rather than an instant. */
|
|
11310
|
+
isDateOnlyBound(raw) {
|
|
11311
|
+
return typeof raw === 'string' && /^\d{4}(?:-\d{2}){0,2}$/.test(raw.trim());
|
|
11312
|
+
}
|
|
11313
|
+
/** Last instant covered by a bound — the day's end when it named a day. */
|
|
11314
|
+
boundEnd(raw, parsed) {
|
|
11315
|
+
if (!this.isDateOnlyBound(raw))
|
|
11316
|
+
return parsed.getTime();
|
|
11317
|
+
const end = new Date(parsed);
|
|
11318
|
+
end.setDate(end.getDate() + 1);
|
|
11319
|
+
return end.getTime() - 1;
|
|
11320
|
+
}
|
|
10504
11321
|
/** Serialize filter model for server-side requests */
|
|
10505
11322
|
toServerParams() {
|
|
10506
11323
|
return {
|
|
@@ -10883,7 +11700,7 @@ class TableKeyboard {
|
|
|
10883
11700
|
return;
|
|
10884
11701
|
table.setAttribute('role', 'grid');
|
|
10885
11702
|
table.setAttribute('tabindex', '0');
|
|
10886
|
-
table.setAttribute('aria-rowcount', String(this.ariaRowCount +
|
|
11703
|
+
table.setAttribute('aria-rowcount', String(this.ariaRowCount + this.headerRowCount));
|
|
10887
11704
|
table.setAttribute('aria-colcount', String(this.totalColCount));
|
|
10888
11705
|
}
|
|
10889
11706
|
/**
|
|
@@ -10917,6 +11734,16 @@ class TableKeyboard {
|
|
|
10917
11734
|
get ariaRowOffset() {
|
|
10918
11735
|
return Math.max(0, this.resolveBound(this.options.ariaRowOffset));
|
|
10919
11736
|
}
|
|
11737
|
+
/**
|
|
11738
|
+
* Rows the header contributes to `aria-rowcount`, and the offset every body
|
|
11739
|
+
* row's `aria-rowindex` starts from. Normally 1 — but list mode paints a
|
|
11740
|
+
* card per row and leaves the thead empty, and claiming a header row that
|
|
11741
|
+
* does not exist would count one row too many and start the body at index 2
|
|
11742
|
+
* with nothing at index 1.
|
|
11743
|
+
*/
|
|
11744
|
+
get headerRowCount() {
|
|
11745
|
+
return this.getTable()?.querySelector('thead tr') ? 1 : 0;
|
|
11746
|
+
}
|
|
10920
11747
|
/** Resolve a body row by its logical data-index (not its DOM position). */
|
|
10921
11748
|
getBodyRow(index) {
|
|
10922
11749
|
if (this.options.getRowElement)
|
|
@@ -10931,7 +11758,8 @@ class TableKeyboard {
|
|
|
10931
11758
|
const table = this.getTable();
|
|
10932
11759
|
if (!table)
|
|
10933
11760
|
return;
|
|
10934
|
-
|
|
11761
|
+
const headerRows = this.headerRowCount;
|
|
11762
|
+
table.setAttribute('aria-rowcount', String(this.ariaRowCount + headerRows));
|
|
10935
11763
|
table.setAttribute('aria-colcount', String(this.totalColCount));
|
|
10936
11764
|
// Header row
|
|
10937
11765
|
const headerRow = table.querySelector('thead tr.column-header-row')
|
|
@@ -10963,7 +11791,7 @@ class TableKeyboard {
|
|
|
10963
11791
|
const bodyRows = table.querySelectorAll('tbody tr:not(.virtual-spacer):not(.table-fill-row)');
|
|
10964
11792
|
bodyRows.forEach((row, i) => {
|
|
10965
11793
|
row.setAttribute('role', 'row');
|
|
10966
|
-
row.setAttribute('aria-rowindex', String(this.ariaRowOffset + i +
|
|
11794
|
+
row.setAttribute('aria-rowindex', String(this.ariaRowOffset + i + 1 + headerRows)); // 1-based, after the header
|
|
10967
11795
|
const isSelected = row.getAttribute('data-selected') === 'true';
|
|
10968
11796
|
if (isSelected)
|
|
10969
11797
|
row.setAttribute('aria-selected', 'true');
|
|
@@ -11379,8 +12207,10 @@ class TableExport {
|
|
|
11379
12207
|
return `${num.toFixed(column.percentageFormat?.decimals ?? 2)}%`;
|
|
11380
12208
|
}
|
|
11381
12209
|
case 'date': {
|
|
11382
|
-
|
|
11383
|
-
|
|
12210
|
+
// Exported dates must read as the cell reads them: a calendar day is
|
|
12211
|
+
// local midnight, never UTC midnight (see table-date.ts).
|
|
12212
|
+
const date = parseCellDate(value);
|
|
12213
|
+
if (!date)
|
|
11384
12214
|
return this.rawValue(value);
|
|
11385
12215
|
const format = column.dateFormat || {};
|
|
11386
12216
|
const dateStyle = format.format === 'custom' ? undefined : (format.format || 'short');
|
|
@@ -12284,10 +13114,13 @@ class TableTreeData {
|
|
|
12284
13114
|
this.flatRows = [];
|
|
12285
13115
|
this.enabled = false;
|
|
12286
13116
|
this.defaultDepth = 0;
|
|
13117
|
+
/** Whether the default expansion depth has been seeded (first data only). */
|
|
13118
|
+
this.defaultsApplied = false;
|
|
12287
13119
|
}
|
|
12288
13120
|
attach(options) {
|
|
12289
13121
|
this.options = options;
|
|
12290
13122
|
this.enabled = true;
|
|
13123
|
+
this.defaultsApplied = false;
|
|
12291
13124
|
// Pre-expand to default depth
|
|
12292
13125
|
if (options.defaultExpansionDepth !== undefined && options.defaultExpansionDepth >= 0) {
|
|
12293
13126
|
this.defaultDepth = options.defaultExpansionDepth;
|
|
@@ -12308,13 +13141,27 @@ class TableTreeData {
|
|
|
12308
13141
|
for (const row of data) {
|
|
12309
13142
|
const path = this.options.getPath(row);
|
|
12310
13143
|
const key = path.join('/');
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
13144
|
+
// A real row may already exist as a gap node generated for a descendant
|
|
13145
|
+
// delivered earlier; upgrade it in place so the children it collected
|
|
13146
|
+
// survive.
|
|
13147
|
+
const existing = nodeMap.get(key);
|
|
13148
|
+
if (existing) {
|
|
13149
|
+
existing.data = row;
|
|
13150
|
+
existing.depth = path.length - 1;
|
|
13151
|
+
existing.isGap = false;
|
|
13152
|
+
}
|
|
13153
|
+
else {
|
|
13154
|
+
nodeMap.set(key, {
|
|
13155
|
+
data: row,
|
|
13156
|
+
children: new Set(),
|
|
13157
|
+
depth: path.length - 1,
|
|
13158
|
+
isGap: false,
|
|
13159
|
+
});
|
|
13160
|
+
}
|
|
13161
|
+
// Ensure all ancestors exist (gap nodes), and register each one as a
|
|
13162
|
+
// child of its own parent: a generated node with no link upward leaves
|
|
13163
|
+
// its parent reporting hasChildren === false, so flatten() never
|
|
13164
|
+
// descends and the whole subtree — real rows included — disappears.
|
|
12318
13165
|
for (let i = 1; i < path.length; i++) {
|
|
12319
13166
|
const ancestorPath = path.slice(0, i);
|
|
12320
13167
|
const ancestorKey = ancestorPath.join('/');
|
|
@@ -12326,6 +13173,11 @@ class TableTreeData {
|
|
|
12326
13173
|
isGap: true,
|
|
12327
13174
|
});
|
|
12328
13175
|
}
|
|
13176
|
+
if (i > 1) {
|
|
13177
|
+
const ancestorParent = nodeMap.get(path.slice(0, i - 1).join('/'));
|
|
13178
|
+
if (ancestorParent)
|
|
13179
|
+
ancestorParent.children.add(ancestorKey);
|
|
13180
|
+
}
|
|
12329
13181
|
}
|
|
12330
13182
|
// Register as child of parent
|
|
12331
13183
|
if (path.length > 1) {
|
|
@@ -12342,13 +13194,17 @@ class TableTreeData {
|
|
|
12342
13194
|
rootKeys.push(key);
|
|
12343
13195
|
}
|
|
12344
13196
|
rootKeys.sort();
|
|
12345
|
-
// Auto-expand to default depth on first process
|
|
12346
|
-
|
|
13197
|
+
// Auto-expand to default depth on the first process that sees rows.
|
|
13198
|
+
// `defaultExpansionDepth` describes the STARTING expansion only: keying
|
|
13199
|
+
// this off an empty expansion set re-seeded it on every render, so
|
|
13200
|
+
// collapsing the last expanded node (or collapseAll()) was silently undone.
|
|
13201
|
+
if (!this.defaultsApplied && this.defaultDepth > 0 && nodeMap.size > 0) {
|
|
12347
13202
|
for (const [key, node] of nodeMap) {
|
|
12348
13203
|
if (node.depth < this.defaultDepth && node.children.size > 0) {
|
|
12349
13204
|
this.expandedKeys.add(key);
|
|
12350
13205
|
}
|
|
12351
13206
|
}
|
|
13207
|
+
this.defaultsApplied = true;
|
|
12352
13208
|
}
|
|
12353
13209
|
// Flatten tree respecting expand/collapse
|
|
12354
13210
|
this.flatRows = [];
|
|
@@ -13102,6 +13958,11 @@ class TableColumnDnD {
|
|
|
13102
13958
|
}
|
|
13103
13959
|
}
|
|
13104
13960
|
|
|
13961
|
+
/**
|
|
13962
|
+
* The one string both loading affordances use — the zero-row message row shows
|
|
13963
|
+
* it, the refetch overlay announces it. Kept together so they can never drift.
|
|
13964
|
+
*/
|
|
13965
|
+
const LOADING_LABEL = 'Loading…';
|
|
13105
13966
|
(() => {
|
|
13106
13967
|
let _classDecorators = [element('snice-table')];
|
|
13107
13968
|
let _classDescriptor;
|
|
@@ -13196,6 +14057,9 @@ class TableColumnDnD {
|
|
|
13196
14057
|
let _density_decorators;
|
|
13197
14058
|
let _density_initializers = [];
|
|
13198
14059
|
let _density_extraInitializers = [];
|
|
14060
|
+
let _columnFit_decorators;
|
|
14061
|
+
let _columnFit_initializers = [];
|
|
14062
|
+
let _columnFit_extraInitializers = [];
|
|
13199
14063
|
let _headerFilters_decorators;
|
|
13200
14064
|
let _headerFilters_initializers = [];
|
|
13201
14065
|
let _headerFilters_extraInitializers = [];
|
|
@@ -13271,6 +14135,7 @@ class TableColumnDnD {
|
|
|
13271
14135
|
let _handleColumnReorderChange_decorators;
|
|
13272
14136
|
let _handleLazyLoadChange_decorators;
|
|
13273
14137
|
let _handleColumnMenuChange_decorators;
|
|
14138
|
+
let _handleColumnFitChange_decorators;
|
|
13274
14139
|
let _dispatchPageChange_decorators;
|
|
13275
14140
|
let _dispatchRowSelectionChanged_decorators;
|
|
13276
14141
|
let _dispatchRowClicked_decorators;
|
|
@@ -13308,8 +14173,8 @@ class TableColumnDnD {
|
|
|
13308
14173
|
_totalItems_decorators = [property({ type: Number, attribute: 'total-items' })];
|
|
13309
14174
|
_pageSizes_decorators = [property({ type: Array, attribute: false })];
|
|
13310
14175
|
_searchDebounce_decorators = [property({ type: Number, attribute: 'search-debounce' })];
|
|
13311
|
-
_columns_decorators = [property({ attribute: false })];
|
|
13312
|
-
_data_decorators = [property({ attribute: false })];
|
|
14176
|
+
_columns_decorators = [property({ attribute: false, hasChanged: () => true })];
|
|
14177
|
+
_data_decorators = [property({ attribute: false, hasChanged: () => true })];
|
|
13313
14178
|
_mode_decorators = [property()];
|
|
13314
14179
|
_currentSort_decorators = [property({ type: Array, attribute: false })];
|
|
13315
14180
|
_selector_decorators = [property({ type: String, attribute: 'selector' })];
|
|
@@ -13322,6 +14187,7 @@ class TableColumnDnD {
|
|
|
13322
14187
|
_editable_decorators = [property({ type: Boolean, attribute: 'editable' })];
|
|
13323
14188
|
_editMode_decorators = [property({ attribute: 'edit-mode' })];
|
|
13324
14189
|
_density_decorators = [property({ attribute: 'density' })];
|
|
14190
|
+
_columnFit_decorators = [property({ attribute: 'column-fit' })];
|
|
13325
14191
|
_headerFilters_decorators = [property({ type: Boolean, attribute: 'header-filters' })];
|
|
13326
14192
|
_quickFilter_decorators = [property({ type: Boolean, attribute: 'quick-filter' })];
|
|
13327
14193
|
_rowReorder_decorators = [property({ type: Boolean, attribute: 'row-reorder' })];
|
|
@@ -13369,6 +14235,7 @@ class TableColumnDnD {
|
|
|
13369
14235
|
_handleColumnReorderChange_decorators = [watch('columnReorder', { immediate: false })];
|
|
13370
14236
|
_handleLazyLoadChange_decorators = [watch('lazyLoad', 'lazyLoadThreshold', { immediate: false })];
|
|
13371
14237
|
_handleColumnMenuChange_decorators = [watch('columnMenu', { immediate: false })];
|
|
14238
|
+
_handleColumnFitChange_decorators = [watch('columnFit', { immediate: false })];
|
|
13372
14239
|
_dispatchPageChange_decorators = [dispatch('page-change', { bubbles: true, composed: true })];
|
|
13373
14240
|
_dispatchRowSelectionChanged_decorators = [dispatch('table-row-selection-changed', { bubbles: true, composed: true })];
|
|
13374
14241
|
_dispatchRowClicked_decorators = [dispatch('row-clicked', { bubbles: true, composed: true })];
|
|
@@ -13418,6 +14285,7 @@ class TableColumnDnD {
|
|
|
13418
14285
|
__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);
|
|
13419
14286
|
__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);
|
|
13420
14287
|
__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);
|
|
14288
|
+
__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);
|
|
13421
14289
|
__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);
|
|
13422
14290
|
__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);
|
|
13423
14291
|
__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);
|
|
@@ -13462,6 +14330,7 @@ class TableColumnDnD {
|
|
|
13462
14330
|
__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);
|
|
13463
14331
|
__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);
|
|
13464
14332
|
__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);
|
|
14333
|
+
__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);
|
|
13465
14334
|
__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);
|
|
13466
14335
|
__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);
|
|
13467
14336
|
__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);
|
|
@@ -13505,6 +14374,11 @@ class TableColumnDnD {
|
|
|
13505
14374
|
// Wait for next frame to ensure DOM is updated
|
|
13506
14375
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
13507
14376
|
this.renderHeader();
|
|
14377
|
+
// The body render the columns assignment queued above can have been
|
|
14378
|
+
// consumed against a not-yet-committed <tbody>. Repainting the header
|
|
14379
|
+
// without the rows is what leaves a remote table showing headings over an
|
|
14380
|
+
// empty frame, so this pass owns both.
|
|
14381
|
+
this.renderBody();
|
|
13508
14382
|
this.renderControls();
|
|
13509
14383
|
return config;
|
|
13510
14384
|
}
|
|
@@ -13534,8 +14408,7 @@ class TableColumnDnD {
|
|
|
13534
14408
|
if (response.totalItems !== undefined) {
|
|
13535
14409
|
this.totalItems = response.totalItems;
|
|
13536
14410
|
}
|
|
13537
|
-
this.
|
|
13538
|
-
this.classList.remove('table--error');
|
|
14411
|
+
this.clearLoadError();
|
|
13539
14412
|
this.loading = false;
|
|
13540
14413
|
// Wait for next frame to ensure DOM is updated
|
|
13541
14414
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
@@ -13554,14 +14427,49 @@ class TableColumnDnD {
|
|
|
13554
14427
|
this.loading = false;
|
|
13555
14428
|
// Wait for next frame to ensure DOM is updated
|
|
13556
14429
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
14430
|
+
// Same guard the success path applies above: a newer request can settle
|
|
14431
|
+
// (and paint) inside this frame, and this failure's repaint would then be
|
|
14432
|
+
// a second full body rebuild owed to nobody.
|
|
14433
|
+
if (seq !== this.dataRequestSeq)
|
|
14434
|
+
return;
|
|
13557
14435
|
this.renderBody();
|
|
13558
14436
|
}
|
|
13559
14437
|
}
|
|
14438
|
+
/**
|
|
14439
|
+
* Requeue a body render whose target had not been committed yet. A frame is
|
|
14440
|
+
* the right granularity — the shadow template commits between frames — and
|
|
14441
|
+
* the paint is dropped again (silently, as before) once the retries run out.
|
|
14442
|
+
*
|
|
14443
|
+
* The retry CHAINS: a template that needs more than one frame to commit is
|
|
14444
|
+
* the whole reason this exists, so a retry that still finds no <tbody> spends
|
|
14445
|
+
* one more of the budget rather than giving the frame back and quitting.
|
|
14446
|
+
*/
|
|
14447
|
+
rearmBodyRender() {
|
|
14448
|
+
if (this.bodyRenderRetryQueued || this.bodyRenderRetries >= 3)
|
|
14449
|
+
return;
|
|
14450
|
+
this.bodyRenderRetryQueued = true;
|
|
14451
|
+
this.bodyRenderRetries++;
|
|
14452
|
+
requestAnimationFrame(() => {
|
|
14453
|
+
this.bodyRenderRetryQueued = false;
|
|
14454
|
+
if (this.tbody)
|
|
14455
|
+
this.renderBody();
|
|
14456
|
+
else
|
|
14457
|
+
this.rearmBodyRender();
|
|
14458
|
+
});
|
|
14459
|
+
}
|
|
13560
14460
|
scheduleRender(what) {
|
|
13561
14461
|
if (what === 'header' || what === 'both')
|
|
13562
14462
|
this.pendingHeaderRender = true;
|
|
13563
|
-
if (what === 'body' || what === 'both')
|
|
14463
|
+
if (what === 'body' || what === 'both') {
|
|
13564
14464
|
this.pendingBodyRender = true;
|
|
14465
|
+
// A NEWLY requested paint gets a fresh budget. The bound exists to stop
|
|
14466
|
+
// one render from spinning against a template that structurally has no
|
|
14467
|
+
// <tbody> (the slotted-rows branch), not to disqualify the table forever:
|
|
14468
|
+
// without this reset, three misses during the slotted phase silently
|
|
14469
|
+
// cancel every body render that follows a later flip to the native table.
|
|
14470
|
+
if (!this.bodyRenderRetryQueued)
|
|
14471
|
+
this.bodyRenderRetries = 0;
|
|
14472
|
+
}
|
|
13565
14473
|
if (this.renderQueued)
|
|
13566
14474
|
return;
|
|
13567
14475
|
this.renderQueued = true;
|
|
@@ -13576,14 +14484,28 @@ class TableColumnDnD {
|
|
|
13576
14484
|
const doBody = this.pendingBodyRender;
|
|
13577
14485
|
this.pendingHeaderRender = false;
|
|
13578
14486
|
this.pendingBodyRender = false;
|
|
13579
|
-
|
|
13580
|
-
|
|
14487
|
+
try {
|
|
14488
|
+
if (doHeader)
|
|
14489
|
+
this.renderHeader();
|
|
14490
|
+
}
|
|
14491
|
+
catch (error) {
|
|
14492
|
+
// Both flags are already consumed. Letting a header failure escape
|
|
14493
|
+
// would cancel the body paint queued alongside it and leave the rows
|
|
14494
|
+
// permanently stale — the exact "header fine, rows blank" symptom.
|
|
14495
|
+
console.error('Error rendering table header:', error);
|
|
14496
|
+
}
|
|
13581
14497
|
if (doBody)
|
|
13582
14498
|
this.renderBody();
|
|
13583
14499
|
});
|
|
13584
14500
|
});
|
|
13585
14501
|
}
|
|
13586
14502
|
debouncedDataRequest() {
|
|
14503
|
+
// The supersede decision belongs to the moment the NEW request is decided
|
|
14504
|
+
// on, not to the moment its timeout fires. Bumping here closes the 150ms
|
|
14505
|
+
// window in which an already-obsolete in-flight response still satisfied
|
|
14506
|
+
// `seq === this.dataRequestSeq` and painted rows for the previous sort /
|
|
14507
|
+
// filter / page under a header that already advertises the new one.
|
|
14508
|
+
this.dataRequestSeq++;
|
|
13587
14509
|
// Set loading immediately for instant feedback
|
|
13588
14510
|
if (!this.loading) {
|
|
13589
14511
|
this.loading = true;
|
|
@@ -13671,6 +14593,72 @@ class TableColumnDnD {
|
|
|
13671
14593
|
/* no border or radius — handled by .table-frame */
|
|
13672
14594
|
}
|
|
13673
14595
|
|
|
14596
|
+
/* column-fit="squish": the frame's width is the constraint.
|
|
14597
|
+
*
|
|
14598
|
+
* The arithmetic (syncColumnWidthsToFrame → fitVisibleColumns) already
|
|
14599
|
+
* lands the columns on the frame's content width, and fixed layout hands
|
|
14600
|
+
* any rounding slack back to the columns, so the rightmost edge sits on
|
|
14601
|
+
* the frame's inner edge. These rules are the guarantee behind that:
|
|
14602
|
+
* hiding the horizontal overflow means a stray pixel — a sub-pixel
|
|
14603
|
+
* border, a browser rounding a percentage the other way — can never turn
|
|
14604
|
+
* into a scrollbar the mode promised would not appear. Vertical scrolling
|
|
14605
|
+
* is untouched. */
|
|
14606
|
+
:host([column-fit="squish"]) .table-frame {
|
|
14607
|
+
overflow-x: hidden;
|
|
14608
|
+
}
|
|
14609
|
+
|
|
14610
|
+
:host([column-fit="squish"]) table {
|
|
14611
|
+
max-width: 100%;
|
|
14612
|
+
}
|
|
14613
|
+
|
|
14614
|
+
/* Squished columns are narrower than their content by design, so the
|
|
14615
|
+
* content has to end in an ellipsis rather than wrap the row taller (or
|
|
14616
|
+
* push a word past the clip edge). */
|
|
14617
|
+
:host([column-fit="squish"]) th,
|
|
14618
|
+
:host([column-fit="squish"]) td {
|
|
14619
|
+
white-space: nowrap;
|
|
14620
|
+
}
|
|
14621
|
+
|
|
14622
|
+
:host([column-fit="squish"]) td > snice-cell-text,
|
|
14623
|
+
:host([column-fit="squish"]) td > .custom-cell,
|
|
14624
|
+
:host([column-fit="squish"]) td > .tree-label {
|
|
14625
|
+
display: block;
|
|
14626
|
+
overflow: hidden;
|
|
14627
|
+
text-overflow: ellipsis;
|
|
14628
|
+
}
|
|
14629
|
+
|
|
14630
|
+
/* The typed cells (email, status, link, phone, location, json, colour,
|
|
14631
|
+
* image, actions) each declare a 100px floor on their host AND on their
|
|
14632
|
+
* inner .cell-content. That floor is a sensible default at natural width
|
|
14633
|
+
* and fatal here: a squished column is routinely narrower than 100px, the
|
|
14634
|
+
* cell refuses to shrink to it, and the td's own overflow: hidden then
|
|
14635
|
+
* chops the label mid-glyph — no ellipsis, because the element that owns
|
|
14636
|
+
* the ellipsis never got small enough to trigger it.
|
|
14637
|
+
*
|
|
14638
|
+
* .cell-content lives in the cell's shadow root, so no descendant
|
|
14639
|
+
* selector from here can reach it. A custom property can: it inherits
|
|
14640
|
+
* across the boundary, the cells read it with the same 100px as the
|
|
14641
|
+
* fallback, and nothing outside a squished td sees any change. */
|
|
14642
|
+
:host([column-fit="squish"]) td {
|
|
14643
|
+
--snice-table-cell-min-width: 0;
|
|
14644
|
+
}
|
|
14645
|
+
|
|
14646
|
+
/* The header label is the table's own markup and needs the same promise:
|
|
14647
|
+
* "Department" clipped to "Departme" reads as a different word. A
|
|
14648
|
+
* sortable header puts the label in a flex row beside the sort
|
|
14649
|
+
* indicator, and a flex item only shrinks below its content once it is
|
|
14650
|
+
* allowed to — hence min-width: 0 alongside the ellipsis. A plain header
|
|
14651
|
+
* is bare text in the th, which ellipsises on the th itself. */
|
|
14652
|
+
:host([column-fit="squish"]) th {
|
|
14653
|
+
text-overflow: ellipsis;
|
|
14654
|
+
}
|
|
14655
|
+
|
|
14656
|
+
:host([column-fit="squish"]) th .sort-header > span {
|
|
14657
|
+
overflow: hidden;
|
|
14658
|
+
text-overflow: ellipsis;
|
|
14659
|
+
min-width: 0;
|
|
14660
|
+
}
|
|
14661
|
+
|
|
13674
14662
|
/* Super-header (slotted area above column headers) */
|
|
13675
14663
|
.table-superheader {
|
|
13676
14664
|
background: var(--snice-color-surface, rgb(255 255 255));
|
|
@@ -13793,9 +14781,13 @@ class TableColumnDnD {
|
|
|
13793
14781
|
background-color: var(--snice-color-surface-container-low, rgb(245 245 245));
|
|
13794
14782
|
}
|
|
13795
14783
|
|
|
13796
|
-
/* Row styling
|
|
14784
|
+
/* Row styling. The zebra tint is an ALPHA overlay, not a surface step:
|
|
14785
|
+
surface-container-low sits ~2 luminance points from the body surface
|
|
14786
|
+
on dark themes, so striped rendered as no striping at all there.
|
|
14787
|
+
An overlay also composites over pinned/tinted cells instead of
|
|
14788
|
+
replacing their color. */
|
|
13797
14789
|
:host([striped]) tbody tr:nth-child(even) {
|
|
13798
|
-
background-color: var(--snice-color-
|
|
14790
|
+
background-color: var(--snice-table-stripe-bg, var(--snice-color-overlay-stripe, hsl(0 0% 0% / 0.02)));
|
|
13799
14791
|
}
|
|
13800
14792
|
|
|
13801
14793
|
:host([hoverable]) tbody tr:hover {
|
|
@@ -14059,12 +15051,14 @@ class TableColumnDnD {
|
|
|
14059
15051
|
justify-content: center;
|
|
14060
15052
|
}
|
|
14061
15053
|
|
|
14062
|
-
/* Loading fade
|
|
14063
|
-
|
|
15054
|
+
/* Loading fade. Scoped to ROWS, never the whole tbody: with no rows the
|
|
15055
|
+
only thing in there is the loading indicator, and dimming that halves
|
|
15056
|
+
the contrast of the one element whose entire job is to be seen. */
|
|
15057
|
+
tbody > tr {
|
|
14064
15058
|
transition: opacity var(--snice-transition-normal, 250ms);
|
|
14065
15059
|
}
|
|
14066
15060
|
|
|
14067
|
-
:host([loading]) tbody {
|
|
15061
|
+
:host([loading]) tbody > tr:not(.table-message-row) {
|
|
14068
15062
|
opacity: 0.5;
|
|
14069
15063
|
}
|
|
14070
15064
|
|
|
@@ -14079,6 +15073,33 @@ class TableColumnDnD {
|
|
|
14079
15073
|
z-index: 2;
|
|
14080
15074
|
}
|
|
14081
15075
|
|
|
15076
|
+
/* Zero-row loading: spinner + the word, centred in the empty body. */
|
|
15077
|
+
.table-loading-state {
|
|
15078
|
+
display: flex;
|
|
15079
|
+
flex-direction: column;
|
|
15080
|
+
align-items: center;
|
|
15081
|
+
justify-content: center;
|
|
15082
|
+
gap: var(--snice-spacing-xs, 0.5rem);
|
|
15083
|
+
color: var(--snice-color-text-secondary, rgb(82 82 82));
|
|
15084
|
+
}
|
|
15085
|
+
|
|
15086
|
+
.table-loading-label {
|
|
15087
|
+
font-size: var(--snice-font-size-sm, 0.875rem);
|
|
15088
|
+
line-height: 1;
|
|
15089
|
+
}
|
|
15090
|
+
|
|
15091
|
+
/* snice-progress draws its circular track at 20% alpha, which disappears
|
|
15092
|
+
entirely on a dark surface — the ring the field report saw was a
|
|
15093
|
+
near-black hairline on near-black. Both table spinners opt out and use
|
|
15094
|
+
the border token at full strength, so the track reads in either
|
|
15095
|
+
theme and the primary arc always has something to travel around. */
|
|
15096
|
+
.table-loading-state snice-progress,
|
|
15097
|
+
.table-loading-overlay snice-progress {
|
|
15098
|
+
--progress-track-opacity: 1;
|
|
15099
|
+
--progress-bg: var(--snice-color-border, rgb(226 226 226));
|
|
15100
|
+
--progress-color: var(--snice-color-primary, rgb(37 99 235));
|
|
15101
|
+
}
|
|
15102
|
+
|
|
14082
15103
|
.no-data {
|
|
14083
15104
|
text-align: center;
|
|
14084
15105
|
padding: var(--snice-spacing-lg, 1.5rem);
|
|
@@ -14220,10 +15241,58 @@ class TableColumnDnD {
|
|
|
14220
15241
|
background: var(--snice-color-primary, rgb(37 99 235));
|
|
14221
15242
|
}
|
|
14222
15243
|
|
|
14223
|
-
/* Pinned
|
|
15244
|
+
/* Pinned columns.
|
|
15245
|
+
|
|
15246
|
+
A frozen column must be opaque — scrolled cells pass underneath it —
|
|
15247
|
+
and a header keeps the header's own surface rather than borrowing the
|
|
15248
|
+
body's. The frozen EDGE (the inner-most pinned column on each side) is
|
|
15249
|
+
what the reader actually needs to see: a heavier divider plus a soft
|
|
15250
|
+
shadow cast INTO the scrolling region, mirrored per side. Between two
|
|
15251
|
+
columns pinned to the same side there is no boundary to mark, so they
|
|
15252
|
+
keep the ordinary cell border.
|
|
15253
|
+
|
|
15254
|
+
The shadow is a border-token tint, not black: a black drop shadow is
|
|
15255
|
+
invisible against a dark surface, which is exactly how pinning came to
|
|
15256
|
+
have no affordance at all in the dark theme. */
|
|
14224
15257
|
.pinned-cell {
|
|
14225
15258
|
background: var(--snice-color-surface, rgb(255 255 255));
|
|
14226
|
-
|
|
15259
|
+
}
|
|
15260
|
+
|
|
15261
|
+
th.pinned-cell {
|
|
15262
|
+
background: var(--snice-color-surface-container-low, rgb(245 245 245));
|
|
15263
|
+
}
|
|
15264
|
+
|
|
15265
|
+
.pinned-cell--left.pinned-cell--edge {
|
|
15266
|
+
border-right: 2px solid var(--snice-color-border, rgb(226 226 226));
|
|
15267
|
+
box-shadow: 6px 0 8px -6px var(--snice-color-border, rgb(226 226 226));
|
|
15268
|
+
}
|
|
15269
|
+
|
|
15270
|
+
.pinned-cell--right.pinned-cell--edge {
|
|
15271
|
+
border-left: 2px solid var(--snice-color-border, rgb(226 226 226));
|
|
15272
|
+
box-shadow: -6px 0 8px -6px var(--snice-color-border, rgb(226 226 226));
|
|
15273
|
+
}
|
|
15274
|
+
|
|
15275
|
+
/* A right-pinned header is often the last cell in the row, where the
|
|
15276
|
+
shared rule above strips the right border. Its divider is on the left,
|
|
15277
|
+
so nothing to restore — but the edge border must survive that rule. */
|
|
15278
|
+
th:last-child.pinned-cell--right.pinned-cell--edge,
|
|
15279
|
+
td:last-child.pinned-cell--right.pinned-cell--edge {
|
|
15280
|
+
border-right: none;
|
|
15281
|
+
}
|
|
15282
|
+
|
|
15283
|
+
/* Pin marker: the only cue when the table is wide enough not to scroll. */
|
|
15284
|
+
.pin-indicator {
|
|
15285
|
+
display: inline-flex;
|
|
15286
|
+
align-items: center;
|
|
15287
|
+
margin-left: var(--snice-spacing-2xs, 0.25rem);
|
|
15288
|
+
color: var(--snice-color-text-secondary, rgb(82 82 82));
|
|
15289
|
+
opacity: 0.65;
|
|
15290
|
+
vertical-align: middle;
|
|
15291
|
+
pointer-events: none;
|
|
15292
|
+
}
|
|
15293
|
+
|
|
15294
|
+
.pin-indicator--right {
|
|
15295
|
+
transform: scaleX(-1);
|
|
14227
15296
|
}
|
|
14228
15297
|
|
|
14229
15298
|
/* Density: compact */
|
|
@@ -14646,7 +15715,7 @@ class TableColumnDnD {
|
|
|
14646
15715
|
}
|
|
14647
15716
|
|
|
14648
15717
|
:host([striped]) .snice-table--slotted .table-body::slotted(snice-row:nth-child(even)) {
|
|
14649
|
-
background-color: var(--snice-color-
|
|
15718
|
+
background-color: var(--snice-table-stripe-bg, var(--snice-color-overlay-stripe, hsl(0 0% 0% / 0.02)));
|
|
14650
15719
|
}
|
|
14651
15720
|
|
|
14652
15721
|
:host([hoverable]) .snice-table--slotted .table-body::slotted(snice-row:hover) {
|
|
@@ -14752,9 +15821,18 @@ class TableColumnDnD {
|
|
|
14752
15821
|
}
|
|
14753
15822
|
container.appendChild(controlsDiv);
|
|
14754
15823
|
}
|
|
14755
|
-
// Re-derive the
|
|
14756
|
-
// fullscreen toggled)
|
|
15824
|
+
// Re-derive the frame-dependent layout whenever the frame's box changes
|
|
15825
|
+
// (host resized, fullscreen toggled): the columns share its width, and the
|
|
15826
|
+
// leftover space below the rows changes with its height.
|
|
14757
15827
|
handleFrameResize() {
|
|
15828
|
+
// Only when the width actually moved. Fitting rewrites header widths, which
|
|
15829
|
+
// can change row heights, which can flip the frame's vertical scrollbar and
|
|
15830
|
+
// call us straight back — the guard stops that becoming a cycle.
|
|
15831
|
+
const width = this.shadowRoot?.querySelector('.table-frame')?.clientWidth ?? 0;
|
|
15832
|
+
if (width > 0 && width !== this.lastFittedFrameWidth) {
|
|
15833
|
+
this.lastFittedFrameWidth = width;
|
|
15834
|
+
this.syncColumnWidthsToFrame();
|
|
15835
|
+
}
|
|
14758
15836
|
this.updateFillRow();
|
|
14759
15837
|
}
|
|
14760
15838
|
async initialize() {
|
|
@@ -14857,7 +15935,18 @@ class TableColumnDnD {
|
|
|
14857
15935
|
}
|
|
14858
15936
|
}
|
|
14859
15937
|
this.data.splice(toIndex, 0, item);
|
|
14860
|
-
|
|
15938
|
+
// Reordering the SAME rows is not a delivery: a failed reload keeps its
|
|
15939
|
+
// error until data actually arrives, so a drag must not dismiss it.
|
|
15940
|
+
// Everything else the data watcher does (row index, detail prep, render)
|
|
15941
|
+
// is still wanted here — hence a narrow flag rather than
|
|
15942
|
+
// `settingSortedData`, which skips all of it.
|
|
15943
|
+
this.reorderingRows = true;
|
|
15944
|
+
try {
|
|
15945
|
+
this.data = [...this.data];
|
|
15946
|
+
}
|
|
15947
|
+
finally {
|
|
15948
|
+
this.reorderingRows = false;
|
|
15949
|
+
}
|
|
14861
15950
|
this.selectedRows = selectedRows
|
|
14862
15951
|
.map((row) => this.indexOfRow(row))
|
|
14863
15952
|
.filter((index) => index >= 0);
|
|
@@ -14971,7 +16060,17 @@ class TableColumnDnD {
|
|
|
14971
16060
|
this.renderHeader(); // Re-render header to show/hide sort indicators
|
|
14972
16061
|
}
|
|
14973
16062
|
handleSelectableChange() {
|
|
14974
|
-
this.render(); //
|
|
16063
|
+
this.render(); // host template (the structural mode can change)
|
|
16064
|
+
// The checkbox tool column is structural: like the sibling selection-mode
|
|
16065
|
+
// watcher, a post-mount toggle has to rebuild the header (select-all th)
|
|
16066
|
+
// AND every row (row checkbox) — the host template render leaves the
|
|
16067
|
+
// imperatively built thead/tbody untouched, so without this the two
|
|
16068
|
+
// disagree and every data cell sits one column off its header. Rows are
|
|
16069
|
+
// never recycled stale across the toggle: computeStructuralSig folds in
|
|
16070
|
+
// `sel=`. Skipped before the first render — the initial paint reads
|
|
16071
|
+
// `selectable` itself.
|
|
16072
|
+
if (this.thead)
|
|
16073
|
+
this.scheduleRender('both');
|
|
14975
16074
|
queueMicrotask(() => this.syncSlottedSelectionState());
|
|
14976
16075
|
}
|
|
14977
16076
|
// C1: reactive columns. A new column set can change cell types/formatting
|
|
@@ -14980,8 +16079,12 @@ class TableColumnDnD {
|
|
|
14980
16079
|
// construction is deferred to the coalescing queue (setter-stack landmine).
|
|
14981
16080
|
handleColumnsAssignment() {
|
|
14982
16081
|
this.renderedRows = new Map();
|
|
16082
|
+
// A new array is a NEW CONFIGURATION: its order / pinned / width / declared
|
|
16083
|
+
// set replace whatever the column model is holding, and states for columns
|
|
16084
|
+
// it no longer declares are dropped. initialize() is the render-path resync
|
|
16085
|
+
// that must NOT do this (it would undo a drag-resize on every paint).
|
|
14983
16086
|
if (this.columns.length > 0)
|
|
14984
|
-
this.columnManager.
|
|
16087
|
+
this.columnManager.applyConfiguration(this.columns, this);
|
|
14985
16088
|
// F: a column set change can add/remove an `aggregate` — resync the grouping
|
|
14986
16089
|
// model's aggregator list and drop its stale flattened snapshot.
|
|
14987
16090
|
this.grouping.setColumns(this.columns);
|
|
@@ -15054,9 +16157,21 @@ class TableColumnDnD {
|
|
|
15054
16157
|
// and the row-index map, then renders the body. The internal sort path
|
|
15055
16158
|
// (sortLocalData) reassigns this.data with a re-sorted view of the SAME rows
|
|
15056
16159
|
// and owns its own snapshot/render — settingSortedData tells us to skip.
|
|
15057
|
-
handleDataAssignment() {
|
|
16160
|
+
handleDataAssignment(oldVal) {
|
|
15058
16161
|
if (this.settingSortedData)
|
|
15059
16162
|
return;
|
|
16163
|
+
// A load error describes the LAST delivery ATTEMPT, so a dataset arriving
|
|
16164
|
+
// from any source supersedes it — including the app falling back to local
|
|
16165
|
+
// rows (or to none) after the remote load failed. Clearing it only on the
|
|
16166
|
+
// remote success path left `loadError` and the `table--error` host class
|
|
16167
|
+
// alive forever, so the next zero-row render showed the stale ⚠️ where the
|
|
16168
|
+
// empty state belonged.
|
|
16169
|
+
//
|
|
16170
|
+
// A REORDER is not an arrival: the row-reorder handler republishes the same
|
|
16171
|
+
// rows in a new array, and dragging a row must not dismiss the ⚠️ the
|
|
16172
|
+
// contract says survives until data actually arrives.
|
|
16173
|
+
if (!this.reorderingRows)
|
|
16174
|
+
this.clearLoadError();
|
|
15060
16175
|
this.unsortedData = [...this.data];
|
|
15061
16176
|
// C3: rows delivered while a client-side sort is active must land in that
|
|
15062
16177
|
// sorted order — the currentSort watcher only fires when the sort itself
|
|
@@ -15065,11 +16180,25 @@ class TableColumnDnD {
|
|
|
15065
16180
|
if (this.mode !== 'remote' && this.currentSort.length > 0)
|
|
15066
16181
|
this.applyLocalSortOrder();
|
|
15067
16182
|
this.rebuildRowIndex();
|
|
16183
|
+
// Re-delivering the SAME rows in a different order must not move the
|
|
16184
|
+
// selection to whatever row inherited the index (no-op for a new dataset).
|
|
16185
|
+
if (oldVal)
|
|
16186
|
+
this.remapSelectionAfterReorder(oldVal);
|
|
15068
16187
|
this.masterDetail.prepare(this.data, row => this.rowValueSignature(row));
|
|
15069
16188
|
if (this.settingDataImperative)
|
|
15070
16189
|
return; // imperative setData(): caller renders
|
|
15071
16190
|
this.scheduleRender('body');
|
|
15072
16191
|
}
|
|
16192
|
+
/**
|
|
16193
|
+
* Drop the failed-load state: the ⚠️ message row's text and the `table--error`
|
|
16194
|
+
* host class always move together, so they are cleared together and from one
|
|
16195
|
+
* place. Idempotent, and deliberately does NOT render — every caller is either
|
|
16196
|
+
* mid-delivery (the data watcher renders) or already renders itself.
|
|
16197
|
+
*/
|
|
16198
|
+
clearLoadError() {
|
|
16199
|
+
this.loadError = null;
|
|
16200
|
+
this.classList.remove('table--error');
|
|
16201
|
+
}
|
|
15073
16202
|
handleDataChange() {
|
|
15074
16203
|
this.renderBody();
|
|
15075
16204
|
this.updateLoadingOverlay();
|
|
@@ -15089,11 +16218,12 @@ class TableColumnDnD {
|
|
|
15089
16218
|
overlay = document.createElement('div');
|
|
15090
16219
|
overlay.className = 'table-loading-overlay';
|
|
15091
16220
|
overlay.setAttribute('part', 'loading-overlay');
|
|
15092
|
-
|
|
15093
|
-
|
|
15094
|
-
|
|
15095
|
-
|
|
15096
|
-
overlay.
|
|
16221
|
+
// The overlay sits ON TOP of readable rows, so it carries its label as
|
|
16222
|
+
// an aria-label rather than visible text — but it must still announce.
|
|
16223
|
+
overlay.setAttribute('role', 'status');
|
|
16224
|
+
overlay.setAttribute('aria-live', 'polite');
|
|
16225
|
+
overlay.setAttribute('aria-label', LOADING_LABEL);
|
|
16226
|
+
overlay.appendChild(this.createLoadingSpinner('medium'));
|
|
15097
16227
|
frame.appendChild(overlay);
|
|
15098
16228
|
}
|
|
15099
16229
|
else if (!show && overlay) {
|
|
@@ -15156,11 +16286,26 @@ class TableColumnDnD {
|
|
|
15156
16286
|
this.sortLocalData();
|
|
15157
16287
|
}
|
|
15158
16288
|
handleCurrentPageChange() {
|
|
16289
|
+
// A clamp written by the display model itself is not a navigation — see
|
|
16290
|
+
// clampCurrentPage().
|
|
16291
|
+
if (this.clampingPage)
|
|
16292
|
+
return;
|
|
15159
16293
|
if (this.paginationMode === 'server' && this.mode === 'remote')
|
|
15160
16294
|
this.debouncedDataRequest();
|
|
15161
16295
|
else
|
|
15162
16296
|
this.scheduleRender('body');
|
|
15163
16297
|
}
|
|
16298
|
+
clampCurrentPage(page) {
|
|
16299
|
+
if (page === this.currentPage)
|
|
16300
|
+
return;
|
|
16301
|
+
this.clampingPage = true;
|
|
16302
|
+
try {
|
|
16303
|
+
this.currentPage = page;
|
|
16304
|
+
}
|
|
16305
|
+
finally {
|
|
16306
|
+
this.clampingPage = false;
|
|
16307
|
+
}
|
|
16308
|
+
}
|
|
15164
16309
|
handlePageSizeChange() {
|
|
15165
16310
|
this.currentPage = 1;
|
|
15166
16311
|
if (this.paginationMode === 'server' && this.mode === 'remote')
|
|
@@ -15176,7 +16321,9 @@ class TableColumnDnD {
|
|
|
15176
16321
|
}
|
|
15177
16322
|
handleListChange() {
|
|
15178
16323
|
this.renderedRows = new Map();
|
|
15179
|
-
|
|
16324
|
+
// The header is part of the mode: entering list mode with a renderer drops
|
|
16325
|
+
// the columnar header, and leaving it must bring the header back.
|
|
16326
|
+
this.scheduleRender('both');
|
|
15180
16327
|
}
|
|
15181
16328
|
handleEditableChange() {
|
|
15182
16329
|
if (this.editable)
|
|
@@ -15234,6 +16381,17 @@ class TableColumnDnD {
|
|
|
15234
16381
|
this.initColumnMenu();
|
|
15235
16382
|
this.scheduleRender('header');
|
|
15236
16383
|
}
|
|
16384
|
+
/**
|
|
16385
|
+
* Switching fit policy re-decides every unsized column's width, so the frame
|
|
16386
|
+
* has to be re-fitted from scratch: the cached width would otherwise make the
|
|
16387
|
+
* resize observer's "did the frame move?" guard swallow the very next pass.
|
|
16388
|
+
*/
|
|
16389
|
+
handleColumnFitChange() {
|
|
16390
|
+
this.columnManager.setFitMode(this.columnFit);
|
|
16391
|
+
this.lastFittedFrameWidth = 0;
|
|
16392
|
+
this.syncColumnWidthsToFrame();
|
|
16393
|
+
this.scheduleRender('header');
|
|
16394
|
+
}
|
|
15237
16395
|
/** Column definitions in their actual painted order. Column state owns
|
|
15238
16396
|
* visibility/order; pinned columns are partitioned to their physical edges
|
|
15239
16397
|
* so right-pinned cells do not overlap later unpinned columns. */
|
|
@@ -15250,9 +16408,124 @@ class TableColumnDnD {
|
|
|
15250
16408
|
.map((state) => byKey.get(state.key))
|
|
15251
16409
|
.filter((column) => !!column);
|
|
15252
16410
|
}
|
|
16411
|
+
/**
|
|
16412
|
+
* Cells one full-width row spans: every visible data column plus the tool
|
|
16413
|
+
* columns that precede them. Group, aggregate, detail, empty-state, and
|
|
16414
|
+
* virtual-spacer rows all measure themselves against this — a row that spans
|
|
16415
|
+
* MORE than the table has invents columns, and `table-layout: fixed` then
|
|
16416
|
+
* spends the frame's leftover width on the phantoms.
|
|
16417
|
+
*/
|
|
16418
|
+
getTotalColumnSpan() {
|
|
16419
|
+
const managed = this.columnManager.getAllStates();
|
|
16420
|
+
const dataColumns = managed.length > 0
|
|
16421
|
+
? this.columnManager.getVisibleColumns().length
|
|
16422
|
+
: this.columns.length;
|
|
16423
|
+
return dataColumns
|
|
16424
|
+
+ (this.hasSelectionColumn() ? 1 : 0)
|
|
16425
|
+
+ (this.masterDetail.isEnabled() ? 1 : 0)
|
|
16426
|
+
+ (this.rowReorder && this.rowDnD.isEnabled() ? 1 : 0);
|
|
16427
|
+
}
|
|
16428
|
+
/**
|
|
16429
|
+
* Re-fit the columns nobody sized to the frame they are painted in.
|
|
16430
|
+
*
|
|
16431
|
+
* A column width is a CONTENT width, so the frame's budget has to be reduced
|
|
16432
|
+
* by each column's own padding + border and by the fixed tool columns before
|
|
16433
|
+
* it can be shared out. Both are read off the live header — the previous
|
|
16434
|
+
* render's, on the first pass — rather than guessed, so the fitted table
|
|
16435
|
+
* lands on the frame's width instead of a few pixels past it.
|
|
16436
|
+
*
|
|
16437
|
+
* Widths are written straight onto the header cells: under fixed layout the
|
|
16438
|
+
* first row owns the column grid, so no body repaint is needed and this can
|
|
16439
|
+
* run inside a resize observer without a render cascade.
|
|
16440
|
+
*/
|
|
16441
|
+
syncColumnWidthsToFrame() {
|
|
16442
|
+
// The policy is the table's, the arithmetic is the manager's: hand it over
|
|
16443
|
+
// before every fit so a `columnFit` change is honoured on the next pass
|
|
16444
|
+
// whether it arrived as a property, an attribute, or an upgrade.
|
|
16445
|
+
this.columnManager.setFitMode(this.columnFit);
|
|
16446
|
+
const frame = this.shadowRoot?.querySelector('.table-frame');
|
|
16447
|
+
const headerRow = this.shadowRoot?.querySelector('thead tr.column-header-row');
|
|
16448
|
+
if (!frame || !headerRow)
|
|
16449
|
+
return false;
|
|
16450
|
+
const available = frame.clientWidth;
|
|
16451
|
+
// Layoutless DOM (or a display:none host): every measurement is 0 and
|
|
16452
|
+
// fitting to it would slam the columns to their minimums for nothing.
|
|
16453
|
+
if (!(available > 0))
|
|
16454
|
+
return false;
|
|
16455
|
+
const dataCells = Array.from(headerRow.querySelectorAll('th[data-key]'));
|
|
16456
|
+
// An active list renderer drops the columnar header entirely; there is no
|
|
16457
|
+
// column geometry to fit and no cell to measure the chrome from.
|
|
16458
|
+
if (dataCells.length === 0)
|
|
16459
|
+
return false;
|
|
16460
|
+
let reserved = 0;
|
|
16461
|
+
for (const cell of Array.from(headerRow.children)) {
|
|
16462
|
+
if (!cell.hasAttribute('data-key'))
|
|
16463
|
+
reserved += cell.getBoundingClientRect().width;
|
|
16464
|
+
}
|
|
16465
|
+
const chrome = horizontalCellChrome(dataCells[0]);
|
|
16466
|
+
if (!this.columnManager.fitVisibleColumns(available - reserved, chrome))
|
|
16467
|
+
return false;
|
|
16468
|
+
for (const cell of dataCells) {
|
|
16469
|
+
const state = this.columnManager.getState(cell.getAttribute('data-key') || '');
|
|
16470
|
+
if (state)
|
|
16471
|
+
cell.style.width = `${state.width}px`;
|
|
16472
|
+
}
|
|
16473
|
+
return true;
|
|
16474
|
+
}
|
|
16475
|
+
/**
|
|
16476
|
+
* Sticky geometry plus the visible affordance for a pinned cell.
|
|
16477
|
+
*
|
|
16478
|
+
* Freezing a column is only communicated if the reader can see where the
|
|
16479
|
+
* frozen region ends, so the inner-most pinned column on each side carries
|
|
16480
|
+
* the divider (`--edge`); the header additionally carries a pin marker, which
|
|
16481
|
+
* is the only cue when the table happens to be wide enough not to scroll.
|
|
16482
|
+
*/
|
|
16483
|
+
applyPinnedCell(cell, key, state, zIndex) {
|
|
16484
|
+
if (state.pinned !== 'left' && state.pinned !== 'right')
|
|
16485
|
+
return;
|
|
16486
|
+
const side = state.pinned;
|
|
16487
|
+
const columns = side === 'left'
|
|
16488
|
+
? this.columnManager.getPinnedLeft()
|
|
16489
|
+
: this.columnManager.getPinnedRight();
|
|
16490
|
+
const offsets = side === 'left'
|
|
16491
|
+
? this.columnManager.getPinnedLeftOffsets()
|
|
16492
|
+
: this.columnManager.getPinnedRightOffsets();
|
|
16493
|
+
// The edge of the frozen region: the last left-pinned column, the first
|
|
16494
|
+
// right-pinned one. Inner boundaries between two pinned columns are just
|
|
16495
|
+
// ordinary cell borders.
|
|
16496
|
+
const edgeKey = side === 'left' ? columns[columns.length - 1]?.key : columns[0]?.key;
|
|
16497
|
+
cell.classList.add('pinned-cell', `pinned-cell--${side}`);
|
|
16498
|
+
if (key === edgeKey)
|
|
16499
|
+
cell.classList.add('pinned-cell--edge');
|
|
16500
|
+
cell.style.position = 'sticky';
|
|
16501
|
+
cell.style[side] = `${offsets.get(key) ?? 0}px`;
|
|
16502
|
+
cell.style.zIndex = zIndex;
|
|
16503
|
+
}
|
|
16504
|
+
/** The pin marker a frozen header carries, so pinning reads at any width. */
|
|
16505
|
+
static pinIndicator(side) {
|
|
16506
|
+
return `<span class="pin-indicator pin-indicator--${side}" aria-hidden="true">`
|
|
16507
|
+
+ `<svg width="10" height="10" viewBox="0 0 16 16" fill="currentColor">`
|
|
16508
|
+
+ `<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"/>`
|
|
16509
|
+
+ `</svg></span>`;
|
|
16510
|
+
}
|
|
16511
|
+
/**
|
|
16512
|
+
* List mode is only "list mode" once a renderer is installed: `list` alone is
|
|
16513
|
+
* a border treatment and still paints normal columnar cells. With a renderer
|
|
16514
|
+
* every row collapses to one full-width cell, so there is no column geometry
|
|
16515
|
+
* left for a header to label.
|
|
16516
|
+
*/
|
|
16517
|
+
usesListRowRenderer() {
|
|
16518
|
+
return !!(this.list && this.listRenderer);
|
|
16519
|
+
}
|
|
15253
16520
|
renderHeader() {
|
|
15254
16521
|
if (!this.thead)
|
|
15255
16522
|
return;
|
|
16523
|
+
// A card/directory list has no columns, so a columnar header would promise
|
|
16524
|
+
// fields that align with nothing. Suppress the data-column headers (and the
|
|
16525
|
+
// equally columnar group and header-filter rows) while a list renderer is
|
|
16526
|
+
// active; the tool columns below still have real body cells to head, so
|
|
16527
|
+
// select-all is kept rather than lost as collateral.
|
|
16528
|
+
const listRows = this.usesListRowRenderer();
|
|
15256
16529
|
const headerRow = document.createElement('tr');
|
|
15257
16530
|
headerRow.className = 'column-header-row';
|
|
15258
16531
|
// Tool column headers — must match createRow order
|
|
@@ -15290,7 +16563,7 @@ class TableColumnDnD {
|
|
|
15290
16563
|
}
|
|
15291
16564
|
headerRow.appendChild(selectCell);
|
|
15292
16565
|
}
|
|
15293
|
-
const visibleColumns = this.getVisibleColumnDefinitions();
|
|
16566
|
+
const visibleColumns = listRows ? [] : this.getVisibleColumnDefinitions();
|
|
15294
16567
|
visibleColumns.forEach(column => {
|
|
15295
16568
|
const th = document.createElement('th');
|
|
15296
16569
|
th.setAttribute('data-key', column.key);
|
|
@@ -15307,23 +16580,10 @@ class TableColumnDnD {
|
|
|
15307
16580
|
}
|
|
15308
16581
|
// Apply column width
|
|
15309
16582
|
const state = this.columnManager.getState(column.key);
|
|
16583
|
+
const pinnedSide = state?.pinned === 'left' || state?.pinned === 'right' ? state.pinned : null;
|
|
15310
16584
|
if (state) {
|
|
15311
16585
|
th.style.width = `${state.width}px`;
|
|
15312
|
-
|
|
15313
|
-
if (state.pinned === 'left') {
|
|
15314
|
-
const offsets = this.columnManager.getPinnedLeftOffsets();
|
|
15315
|
-
th.classList.add('pinned-cell');
|
|
15316
|
-
th.style.position = 'sticky';
|
|
15317
|
-
th.style.left = `${offsets.get(column.key) ?? 0}px`;
|
|
15318
|
-
th.style.zIndex = '2';
|
|
15319
|
-
}
|
|
15320
|
-
else if (state.pinned === 'right') {
|
|
15321
|
-
const offsets = this.columnManager.getPinnedRightOffsets();
|
|
15322
|
-
th.classList.add('pinned-cell');
|
|
15323
|
-
th.style.position = 'sticky';
|
|
15324
|
-
th.style.right = `${offsets.get(column.key) ?? 0}px`;
|
|
15325
|
-
th.style.zIndex = '2';
|
|
15326
|
-
}
|
|
16586
|
+
this.applyPinnedCell(th, column.key, state, '2');
|
|
15327
16587
|
}
|
|
15328
16588
|
if (this.sortable && column.sortable !== false) {
|
|
15329
16589
|
th.classList.add('sortable');
|
|
@@ -15331,11 +16591,22 @@ class TableColumnDnD {
|
|
|
15331
16591
|
// A role=button must be focusable and named, or the keyboard cannot
|
|
15332
16592
|
// sort at all. Matches the declarative header in snice-header.ts.
|
|
15333
16593
|
th.setAttribute('tabindex', '0');
|
|
15334
|
-
|
|
16594
|
+
// Frozen columns say so: the divider is a visual cue only.
|
|
16595
|
+
th.setAttribute('aria-label', pinnedSide
|
|
16596
|
+
? `Sort by ${column.label}, pinned ${pinnedSide}`
|
|
16597
|
+
: `Sort by ${column.label}`);
|
|
15335
16598
|
th.innerHTML = this.renderSortableHeader(column);
|
|
15336
16599
|
}
|
|
15337
16600
|
else {
|
|
15338
16601
|
th.textContent = column.label;
|
|
16602
|
+
if (pinnedSide)
|
|
16603
|
+
th.setAttribute('aria-label', `${column.label}, pinned ${pinnedSide}`);
|
|
16604
|
+
}
|
|
16605
|
+
if (pinnedSide) {
|
|
16606
|
+
// Inside the label, not after it: `.sort-header` is a full-width flex
|
|
16607
|
+
// row, so a sibling would be pushed onto a second line.
|
|
16608
|
+
const label = th.querySelector('.sort-header > span') ?? th;
|
|
16609
|
+
label.insertAdjacentHTML('beforeend', SniceTable.pinIndicator(pinnedSide));
|
|
15339
16610
|
}
|
|
15340
16611
|
// Filter indicator
|
|
15341
16612
|
if (this.filterEngine.hasColumnFilter(column.key)) {
|
|
@@ -15388,7 +16659,7 @@ class TableColumnDnD {
|
|
|
15388
16659
|
});
|
|
15389
16660
|
this.thead.innerHTML = '';
|
|
15390
16661
|
// Column groups header row (if any)
|
|
15391
|
-
const groups = this.columnManager.getColumnGroups();
|
|
16662
|
+
const groups = listRows ? [] : this.columnManager.getColumnGroups();
|
|
15392
16663
|
if (groups.length > 0) {
|
|
15393
16664
|
const groupRow = document.createElement('tr');
|
|
15394
16665
|
groupRow.className = 'column-group-row';
|
|
@@ -15410,9 +16681,12 @@ class TableColumnDnD {
|
|
|
15410
16681
|
groupRow.appendChild(groupedHeaders.content);
|
|
15411
16682
|
this.thead.appendChild(groupRow);
|
|
15412
16683
|
}
|
|
15413
|
-
|
|
16684
|
+
// A list-mode header row with no tool columns has nothing to show; leaving
|
|
16685
|
+
// it out keeps the thead empty so it paints no strip above the cards.
|
|
16686
|
+
if (!listRows || headerRow.children.length > 0)
|
|
16687
|
+
this.thead.appendChild(headerRow);
|
|
15414
16688
|
// Header filter row (if enabled)
|
|
15415
|
-
if (this.headerFilters) {
|
|
16689
|
+
if (this.headerFilters && !listRows) {
|
|
15416
16690
|
const filterRow = document.createElement('tr');
|
|
15417
16691
|
filterRow.className = 'header-filter-row';
|
|
15418
16692
|
if (this.rowReorder && this.rowDnD.isEnabled()) {
|
|
@@ -15449,6 +16723,11 @@ class TableColumnDnD {
|
|
|
15449
16723
|
});
|
|
15450
16724
|
this.thead.appendChild(filterRow);
|
|
15451
16725
|
}
|
|
16726
|
+
// The header is in the DOM, so its real chrome and tool-column widths are
|
|
16727
|
+
// measurable: fit the unsized columns to the frame before the browser
|
|
16728
|
+
// paints. Widths land on the header cells that were just created, so this
|
|
16729
|
+
// never re-enters renderHeader.
|
|
16730
|
+
this.syncColumnWidthsToFrame();
|
|
15452
16731
|
}
|
|
15453
16732
|
renderSortableHeader(column) {
|
|
15454
16733
|
const sortItem = this.currentSort.find(s => s.column === column.key);
|
|
@@ -15480,9 +16759,90 @@ class TableColumnDnD {
|
|
|
15480
16759
|
</div>
|
|
15481
16760
|
`;
|
|
15482
16761
|
}
|
|
16762
|
+
/**
|
|
16763
|
+
* The single message row a zero-row body shows: loading spinner, load error,
|
|
16764
|
+
* or the empty state (the `empty-state` slot cloned fresh, else the default
|
|
16765
|
+
* placeholder). Returns null when there is data to render. Shared by the
|
|
16766
|
+
* ordinary and virtualized paths — the virtualizer wipes the tbody itself, so
|
|
16767
|
+
* it asks for this row rather than leaving a virtualized empty table blank.
|
|
16768
|
+
*/
|
|
16769
|
+
/**
|
|
16770
|
+
* The spinner both loading affordances draw. `snice-progress` paints its
|
|
16771
|
+
* track at 20% alpha, which vanishes against a dark surface — the table pins
|
|
16772
|
+
* `--progress-track-opacity` in its own stylesheet so the ring reads on any
|
|
16773
|
+
* theme; the indicator arc keeps the primary color.
|
|
16774
|
+
*/
|
|
16775
|
+
createLoadingSpinner(size) {
|
|
16776
|
+
const spinner = document.createElement('snice-progress');
|
|
16777
|
+
spinner.setAttribute('variant', 'circular');
|
|
16778
|
+
spinner.setAttribute('indeterminate', '');
|
|
16779
|
+
spinner.setAttribute('size', size);
|
|
16780
|
+
return spinner;
|
|
16781
|
+
}
|
|
16782
|
+
createEmptyBodyRow() {
|
|
16783
|
+
if (this.data.length > 0 || this.columns.length === 0)
|
|
16784
|
+
return null;
|
|
16785
|
+
const toolCols = (this.hasSelectionColumn() ? 1 : 0)
|
|
16786
|
+
+ (this.masterDetail.isEnabled() ? 1 : 0)
|
|
16787
|
+
+ (this.rowReorder && this.rowDnD.isEnabled() ? 1 : 0);
|
|
16788
|
+
const tr = document.createElement('tr');
|
|
16789
|
+
tr.className = 'table-message-row';
|
|
16790
|
+
const td = document.createElement('td');
|
|
16791
|
+
td.colSpan = this.columns.length + toolCols;
|
|
16792
|
+
td.className = 'no-data';
|
|
16793
|
+
tr.appendChild(td);
|
|
16794
|
+
if (this.loading) {
|
|
16795
|
+
// A bare ring in an otherwise empty rectangle reads as a rendering
|
|
16796
|
+
// artefact, not as "we are fetching your rows": at `small` it is 24px of
|
|
16797
|
+
// low-contrast stroke with nothing naming it. The zero-row body has the
|
|
16798
|
+
// space for a full-size spinner AND the word, and the pair is what makes
|
|
16799
|
+
// the state legible — visually and to a screen reader.
|
|
16800
|
+
const status = document.createElement('div');
|
|
16801
|
+
status.className = 'table-loading-state';
|
|
16802
|
+
status.setAttribute('role', 'status');
|
|
16803
|
+
status.setAttribute('aria-live', 'polite');
|
|
16804
|
+
status.appendChild(this.createLoadingSpinner('medium'));
|
|
16805
|
+
const label = document.createElement('span');
|
|
16806
|
+
label.className = 'table-loading-label';
|
|
16807
|
+
label.textContent = LOADING_LABEL;
|
|
16808
|
+
status.appendChild(label);
|
|
16809
|
+
td.appendChild(status);
|
|
16810
|
+
}
|
|
16811
|
+
else if (this.loadError) {
|
|
16812
|
+
// Surfaces a failed remote load instead of silently falling through to
|
|
16813
|
+
// the generic "No data" empty state.
|
|
16814
|
+
const errorDiv = document.createElement('div');
|
|
16815
|
+
errorDiv.className = 'table-error-message';
|
|
16816
|
+
errorDiv.textContent = `⚠️ ${this.loadError}`;
|
|
16817
|
+
td.appendChild(errorDiv);
|
|
16818
|
+
}
|
|
16819
|
+
else {
|
|
16820
|
+
const slotted = this.querySelector('[slot="empty-state"]');
|
|
16821
|
+
if (slotted) {
|
|
16822
|
+
td.appendChild(slotted.cloneNode(true));
|
|
16823
|
+
}
|
|
16824
|
+
else {
|
|
16825
|
+
const empty = document.createElement('snice-empty-state');
|
|
16826
|
+
empty.setAttribute('size', 'small');
|
|
16827
|
+
empty.setAttribute('icon', '📭');
|
|
16828
|
+
empty.setAttribute('title', 'No data');
|
|
16829
|
+
empty.setAttribute('description', 'No records to display.');
|
|
16830
|
+
td.appendChild(empty);
|
|
16831
|
+
}
|
|
16832
|
+
}
|
|
16833
|
+
return tr;
|
|
16834
|
+
}
|
|
15483
16835
|
renderBody() {
|
|
15484
|
-
|
|
16836
|
+
// The queue flush consumed `pendingBodyRender` before calling us, so a body
|
|
16837
|
+
// render that arrives while the shadow template has not committed its
|
|
16838
|
+
// <tbody> (a `table/config` response landing before @ready, a structural
|
|
16839
|
+
// branch flip mid-flight) would simply vanish — the header repaints and the
|
|
16840
|
+
// rows stay stale forever. Re-arm it for the next frame instead.
|
|
16841
|
+
if (!this.tbody) {
|
|
16842
|
+
this.rearmBodyRender();
|
|
15485
16843
|
return;
|
|
16844
|
+
}
|
|
16845
|
+
this.bodyRenderRetries = 0;
|
|
15486
16846
|
// Update column manager when columns change
|
|
15487
16847
|
if (this.columns.length > 0) {
|
|
15488
16848
|
this.columnManager.initialize(this.columns, this);
|
|
@@ -15514,8 +16874,7 @@ class TableColumnDnD {
|
|
|
15514
16874
|
// NOT the raw filtered data — otherwise the virtualizer windows the wrong
|
|
15515
16875
|
// dataset and expand/collapse silently no-ops.
|
|
15516
16876
|
if (this.virtualize && this.virtualizer.isEnabled()) {
|
|
15517
|
-
this.
|
|
15518
|
-
this.virtualizer.setTotalRows(this.virtualRowsSnapshot.length);
|
|
16877
|
+
this.syncVirtualRows();
|
|
15519
16878
|
// The virtualizer's afterRender hook re-establishes grid ARIA/focus on
|
|
15520
16879
|
// the freshly inserted window.
|
|
15521
16880
|
// Still render pagination
|
|
@@ -15524,64 +16883,15 @@ class TableColumnDnD {
|
|
|
15524
16883
|
this.updateSelectAllState();
|
|
15525
16884
|
return;
|
|
15526
16885
|
}
|
|
15527
|
-
|
|
16886
|
+
const emptyBodyRow = this.createEmptyBodyRow();
|
|
16887
|
+
if (emptyBodyRow) {
|
|
15528
16888
|
// Empty / loading / error states replace the body with a single message
|
|
15529
16889
|
// row. Reset the recycler map so a later data render starts from a clean
|
|
15530
16890
|
// slate (no stale reuse candidates pointing at removed rows).
|
|
15531
16891
|
this.tbody.innerHTML = '';
|
|
15532
16892
|
this.renderedRows = new Map();
|
|
15533
|
-
|
|
15534
|
-
|
|
15535
|
-
+ (this.rowReorder && this.rowDnD.isEnabled() ? 1 : 0);
|
|
15536
|
-
const colSpan = this.columns.length + toolCols;
|
|
15537
|
-
if (this.loading) {
|
|
15538
|
-
// Show loading spinner
|
|
15539
|
-
const tr = document.createElement('tr');
|
|
15540
|
-
const td = document.createElement('td');
|
|
15541
|
-
td.colSpan = colSpan;
|
|
15542
|
-
td.className = 'no-data';
|
|
15543
|
-
td.innerHTML = '<snice-progress variant="circular" indeterminate size="small"></snice-progress>';
|
|
15544
|
-
tr.appendChild(td);
|
|
15545
|
-
this.tbody.appendChild(tr);
|
|
15546
|
-
return;
|
|
15547
|
-
}
|
|
15548
|
-
else if (this.loadError) {
|
|
15549
|
-
// Show error state — surfaces a failed remote load instead of
|
|
15550
|
-
// silently falling through to the generic "No data" empty state.
|
|
15551
|
-
const tr = document.createElement('tr');
|
|
15552
|
-
const td = document.createElement('td');
|
|
15553
|
-
td.colSpan = colSpan;
|
|
15554
|
-
td.className = 'no-data';
|
|
15555
|
-
const errorDiv = document.createElement('div');
|
|
15556
|
-
errorDiv.className = 'table-error-message';
|
|
15557
|
-
errorDiv.textContent = `⚠️ ${this.loadError}`;
|
|
15558
|
-
td.appendChild(errorDiv);
|
|
15559
|
-
tr.appendChild(td);
|
|
15560
|
-
this.tbody.appendChild(tr);
|
|
15561
|
-
return;
|
|
15562
|
-
}
|
|
15563
|
-
else {
|
|
15564
|
-
// Show empty state
|
|
15565
|
-
const tr = document.createElement('tr');
|
|
15566
|
-
const td = document.createElement('td');
|
|
15567
|
-
td.colSpan = colSpan;
|
|
15568
|
-
td.className = 'no-data';
|
|
15569
|
-
const slotted = this.querySelector('[slot="empty-state"]');
|
|
15570
|
-
if (slotted) {
|
|
15571
|
-
td.appendChild(slotted.cloneNode(true));
|
|
15572
|
-
}
|
|
15573
|
-
else {
|
|
15574
|
-
const empty = document.createElement('snice-empty-state');
|
|
15575
|
-
empty.setAttribute('size', 'small');
|
|
15576
|
-
empty.setAttribute('icon', '📭');
|
|
15577
|
-
empty.setAttribute('title', 'No data');
|
|
15578
|
-
empty.setAttribute('description', 'No records to display.');
|
|
15579
|
-
td.appendChild(empty);
|
|
15580
|
-
}
|
|
15581
|
-
tr.appendChild(td);
|
|
15582
|
-
this.tbody.appendChild(tr);
|
|
15583
|
-
return;
|
|
15584
|
-
}
|
|
16893
|
+
this.tbody.appendChild(emptyBodyRow);
|
|
16894
|
+
return;
|
|
15585
16895
|
}
|
|
15586
16896
|
// Apply client-side filters (cached snapshot — see getFilteredData).
|
|
15587
16897
|
const filteredData = this.getFilteredData();
|
|
@@ -15593,15 +16903,23 @@ class TableColumnDnD {
|
|
|
15593
16903
|
if (this.pagination && this.paginationMode === 'client'
|
|
15594
16904
|
&& !this.usesGroupingDisplayModel() && !this.treeData.isEnabled()) {
|
|
15595
16905
|
this.totalItems = filteredData.length;
|
|
15596
|
-
|
|
16906
|
+
// Re-clamp before slicing. A shorter re-delivery (or a filter) can leave
|
|
16907
|
+
// `currentPage` past the new last page; slicing at the stale offset lands
|
|
16908
|
+
// off the end and renders an EMPTY body while the pager still advertises
|
|
16909
|
+
// the tail rows — blank until the user clicks a page button. Same
|
|
16910
|
+
// [1, totalPages] clamp goToPage() applies, and the same one the grouping
|
|
16911
|
+
// and tree display models already do before their own slice.
|
|
16912
|
+
// An empty set is left alone: a render before the rows arrive must not
|
|
16913
|
+
// rewrite the host's declared starting page.
|
|
16914
|
+
const totalPages = filteredData.length > 0
|
|
16915
|
+
? Math.ceil(filteredData.length / this.pageSize)
|
|
16916
|
+
: this.currentPage;
|
|
16917
|
+
const page = Math.max(1, Math.min(this.currentPage, totalPages));
|
|
16918
|
+
this.clampCurrentPage(page);
|
|
16919
|
+
const startIndex = (page - 1) * this.pageSize;
|
|
15597
16920
|
displayData = filteredData.slice(startIndex, startIndex + this.pageSize);
|
|
15598
16921
|
}
|
|
15599
|
-
const
|
|
15600
|
-
const managedColumns = this.columnManager.getAllStates();
|
|
15601
|
-
const visibleColumnCount = managedColumns.length > 0
|
|
15602
|
-
? this.columnManager.getVisibleColumns().length
|
|
15603
|
-
: this.columns.length;
|
|
15604
|
-
const totalColSpan = visibleColumnCount + extraCols;
|
|
16922
|
+
const totalColSpan = this.getTotalColumnSpan();
|
|
15605
16923
|
const structuralSig = this.computeStructuralSig();
|
|
15606
16924
|
// Task B: describe the desired body as an ordered list of keyed entries,
|
|
15607
16925
|
// then reconcile the tbody to match. Rows whose object + signature are
|
|
@@ -15697,6 +17015,19 @@ class TableColumnDnD {
|
|
|
15697
17015
|
restampIndex: index,
|
|
15698
17016
|
create: () => this.createRow(rowData, index, treeRow),
|
|
15699
17017
|
});
|
|
17018
|
+
// Tree data must compose with master-detail exactly like the ordinary
|
|
17019
|
+
// and grouping paths: createRow renders the toggle for every tree row,
|
|
17020
|
+
// so omitting this entry left that control inert. Generated gap nodes
|
|
17021
|
+
// (index -1) carry no data row and therefore no panel.
|
|
17022
|
+
if (index >= 0 && this.masterDetail.isEnabled() && this.masterDetail.isExpanded(index)) {
|
|
17023
|
+
entries.push({
|
|
17024
|
+
key: `__detail_${index}`,
|
|
17025
|
+
sig: 'detail',
|
|
17026
|
+
alwaysRebuild: true,
|
|
17027
|
+
create: () => this.masterDetail.createDetailRow(rowData, index, totalColSpan) ??
|
|
17028
|
+
document.createElement('tr'),
|
|
17029
|
+
});
|
|
17030
|
+
}
|
|
15700
17031
|
});
|
|
15701
17032
|
}
|
|
15702
17033
|
else {
|
|
@@ -15754,6 +17085,20 @@ class TableColumnDnD {
|
|
|
15754
17085
|
* data row, so column widths, pinning, and alignment track the table
|
|
15755
17086
|
* engine natively. Removed entirely when content fills (or overflows) the
|
|
15756
17087
|
* frame, restoring the plain last-row styling.
|
|
17088
|
+
*
|
|
17089
|
+
* INVARIANT — the filler may only consume space the frame ALREADY has; space
|
|
17090
|
+
* that exists only BECAUSE of the filler must be given back. The filler is
|
|
17091
|
+
* table content, and `@observe('resize', '.table-frame')` re-derives it from
|
|
17092
|
+
* the frame's box, so on a host whose height is content-driven (the
|
|
17093
|
+
* documented `:host { height: 100% }` resolving against an auto or
|
|
17094
|
+
* max-content container — a stretched grid/flex item, a plain block parent)
|
|
17095
|
+
* the filler is an input to the very measurement that produced it: the frame
|
|
17096
|
+
* grows, the observer fires, the leftover computes positive again, and the
|
|
17097
|
+
* table inflates by one slack per animation frame without bound. No static
|
|
17098
|
+
* check distinguishes a content-driven frame from a genuinely sized one, so
|
|
17099
|
+
* this measures instead: apply the filler, then confirm the frame kept its
|
|
17100
|
+
* height. Any growth is self-inflicted and is subtracted from the filler;
|
|
17101
|
+
* once nothing honest is left, the frame had no free space to fill at all.
|
|
15757
17102
|
*/
|
|
15758
17103
|
updateFillRow() {
|
|
15759
17104
|
const frame = this.shadowRoot?.querySelector('.table-frame');
|
|
@@ -15768,22 +17113,45 @@ class TableColumnDnD {
|
|
|
15768
17113
|
const lastRow = rows[rows.length - 1];
|
|
15769
17114
|
if (!lastRow)
|
|
15770
17115
|
return;
|
|
15771
|
-
|
|
17116
|
+
// Baseline: the frame's height with no filler of ours in the box. Every
|
|
17117
|
+
// later reading is compared against it, so the correction below can never
|
|
17118
|
+
// hand back more than the filler itself created.
|
|
17119
|
+
const baseline = frame.clientHeight;
|
|
17120
|
+
let leftover = Math.floor(baseline - tableEl.offsetHeight);
|
|
15772
17121
|
if (leftover <= 1)
|
|
15773
17122
|
return;
|
|
15774
17123
|
const fill = lastRow.cloneNode(true);
|
|
15775
17124
|
fill.className = 'table-fill-row';
|
|
15776
|
-
|
|
17125
|
+
// `part` goes with the rest of the row identity: the filler is not "each
|
|
17126
|
+
// native body row", so a consumer's ::part(row)/::part(cell) rule must not
|
|
17127
|
+
// paint a phantom row across it.
|
|
17128
|
+
for (const attr of ['data-index', 'data-selected', 'role', 'aria-rowindex', 'aria-selected', 'tabindex', 'id', 'part']) {
|
|
15777
17129
|
fill.removeAttribute(attr);
|
|
15778
17130
|
}
|
|
15779
17131
|
fill.setAttribute('aria-hidden', 'true');
|
|
15780
17132
|
fill.querySelectorAll('td').forEach(td => {
|
|
15781
17133
|
td.replaceChildren();
|
|
15782
|
-
for (const attr of ['role', 'aria-colindex', 'tabindex', 'aria-selected'])
|
|
17134
|
+
for (const attr of ['role', 'aria-colindex', 'tabindex', 'aria-selected', 'part'])
|
|
15783
17135
|
td.removeAttribute(attr);
|
|
15784
17136
|
});
|
|
15785
17137
|
fill.style.height = `${leftover}px`;
|
|
15786
17138
|
tbody.appendChild(fill);
|
|
17139
|
+
// Feedback check. Reading `clientHeight` flushes layout, so each pass sees
|
|
17140
|
+
// the frame as it stands WITH the filler in it. A frame that kept its
|
|
17141
|
+
// baseline is genuinely sized and the filler stays; growth means the frame
|
|
17142
|
+
// is (partly) sized by its own content, so give exactly that back. Two
|
|
17143
|
+
// corrections settle every real case — a fully self-made leftover reaches
|
|
17144
|
+
// zero on the first — and the bounded loop guarantees termination.
|
|
17145
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
17146
|
+
const selfMade = frame.clientHeight - baseline;
|
|
17147
|
+
if (selfMade <= 0)
|
|
17148
|
+
return;
|
|
17149
|
+
leftover -= selfMade;
|
|
17150
|
+
if (leftover <= 1)
|
|
17151
|
+
break;
|
|
17152
|
+
fill.style.height = `${leftover}px`;
|
|
17153
|
+
}
|
|
17154
|
+
fill.remove();
|
|
15787
17155
|
}
|
|
15788
17156
|
// ── Task B: render-path reconciler ──────────────────────────────────────
|
|
15789
17157
|
//
|
|
@@ -15941,7 +17309,7 @@ class TableColumnDnD {
|
|
|
15941
17309
|
restampRowIndex(el, index) {
|
|
15942
17310
|
el.setAttribute('data-index', String(index));
|
|
15943
17311
|
const isSelected = this.selectedRows.includes(index);
|
|
15944
|
-
|
|
17312
|
+
this.stampRowSelection(el, isSelected);
|
|
15945
17313
|
const checkbox = el.querySelector('snice-checkbox.row-select');
|
|
15946
17314
|
if (checkbox) {
|
|
15947
17315
|
checkbox.setAttribute('data-row-index', String(index));
|
|
@@ -16128,24 +17496,30 @@ class TableColumnDnD {
|
|
|
16128
17496
|
span.textContent = rendered == null ? '' : String(rendered);
|
|
16129
17497
|
return span;
|
|
16130
17498
|
}
|
|
16131
|
-
|
|
17499
|
+
// A declared display formatter resolves the whole pipeline here — the
|
|
17500
|
+
// documented precedence is `formatter` first, `valueFormatter` as the
|
|
17501
|
+
// fallback — and its output is TEXT, so the column renders through the text
|
|
17502
|
+
// cell: a rating/progress/link/boolean cell cannot carry a display string
|
|
17503
|
+
// (its value channel is typed) and renders structure rather than text. This
|
|
17504
|
+
// is the rule the declarative <snice-row> path already uses.
|
|
17505
|
+
const displayText = this.cellDisplayText(column, value, row);
|
|
17506
|
+
const tagName = displayText === null ? this.getCellTagName(column.type) : 'snice-cell-text';
|
|
16132
17507
|
const el = document.createElement(tagName);
|
|
16133
17508
|
// Base attributes. Only force `align` if the column definition specifies
|
|
16134
17509
|
// one; otherwise let the cell pick its type-appropriate default (numbers
|
|
16135
17510
|
// right, rating/boolean center, text left). Hard-coding 'left' here fought
|
|
16136
|
-
// the cell's own type-based text-align and made number values drift
|
|
17511
|
+
// the cell's own type-based text-align and made number values drift — and a
|
|
17512
|
+
// formatted column keeps its TYPE's default, not the text cell's.
|
|
16137
17513
|
el.setAttribute('type', column.type || 'text');
|
|
16138
17514
|
if (column.align)
|
|
16139
17515
|
el.setAttribute('align', column.align);
|
|
17516
|
+
else if (displayText !== null)
|
|
17517
|
+
el.setAttribute('align', defaultCellAlign(column.type));
|
|
16140
17518
|
el.setAttribute('in-table', 'true');
|
|
16141
|
-
//
|
|
16142
|
-
// the
|
|
16143
|
-
//
|
|
16144
|
-
// cell
|
|
16145
|
-
// without the formatters, which keeps it from formatting again and tells
|
|
16146
|
-
// the shared presentation contract to paint the text over any type-specific
|
|
16147
|
-
// rendering (a rating/progress/link cell cannot render display text).
|
|
16148
|
-
const displayText = this.cellDisplayText(column, value, row);
|
|
17519
|
+
// The cell's value IS its display value: with a formatter declared the cell
|
|
17520
|
+
// receives the formatted text and a column with the formatters removed, so
|
|
17521
|
+
// nothing formats twice and the `value` channel a caller reads is what the
|
|
17522
|
+
// cell shows.
|
|
16149
17523
|
const cellColumn = displayText === null
|
|
16150
17524
|
? column
|
|
16151
17525
|
: { ...column, formatter: undefined, valueFormatter: undefined };
|
|
@@ -16153,7 +17527,6 @@ class TableColumnDnD {
|
|
|
16153
17527
|
// JSON, action, and tag cells without a lossy attribute conversion. Keep a
|
|
16154
17528
|
// primitive attribute for declarative inspection and CSS selectors.
|
|
16155
17529
|
if (displayText !== null) {
|
|
16156
|
-
el.setAttribute('formatted', '');
|
|
16157
17530
|
el.value = displayText;
|
|
16158
17531
|
el.setAttribute('value', displayText);
|
|
16159
17532
|
}
|
|
@@ -16163,9 +17536,12 @@ class TableColumnDnD {
|
|
|
16163
17536
|
el.data = value;
|
|
16164
17537
|
}
|
|
16165
17538
|
else {
|
|
16166
|
-
|
|
16167
|
-
|
|
16168
|
-
|
|
17539
|
+
// An empty row value (null, or a field the row never carried) falls back
|
|
17540
|
+
// to the typed cell's own documented empty value, never to a blanket ''.
|
|
17541
|
+
const resolved = value ?? emptyCellValue(column.type);
|
|
17542
|
+
el.value = resolved;
|
|
17543
|
+
if (resolved !== null && typeof resolved !== 'object') {
|
|
17544
|
+
el.setAttribute('value', String(resolved));
|
|
16169
17545
|
}
|
|
16170
17546
|
}
|
|
16171
17547
|
// Column definition as property (for formatter access etc.)
|
|
@@ -16382,16 +17758,36 @@ class TableColumnDnD {
|
|
|
16382
17758
|
updateRowSelectionState() {
|
|
16383
17759
|
if (!this.tbody)
|
|
16384
17760
|
return;
|
|
16385
|
-
|
|
16386
|
-
|
|
17761
|
+
// Key off each row's own `data-index`, never its DOM position: group
|
|
17762
|
+
// headers, aggregate footers, virtual spacers, and the inert fill row share
|
|
17763
|
+
// the tbody, so a positional walk hands a raw-data index to a non-data row
|
|
17764
|
+
// (and drifts every real row after it — on page 2 of a client-paged table
|
|
17765
|
+
// the offsets never lined up at all).
|
|
17766
|
+
const rows = this.tbody.querySelectorAll('tr[data-index]');
|
|
17767
|
+
rows.forEach(row => {
|
|
17768
|
+
const index = Number(row.getAttribute('data-index'));
|
|
16387
17769
|
const isSelected = this.selectedRows.includes(index);
|
|
16388
|
-
|
|
17770
|
+
this.stampRowSelection(row, isSelected);
|
|
16389
17771
|
const checkbox = row.querySelector('snice-checkbox.row-select');
|
|
16390
17772
|
if (checkbox) {
|
|
16391
17773
|
checkbox.checked = isSelected;
|
|
16392
17774
|
}
|
|
16393
17775
|
});
|
|
16394
17776
|
}
|
|
17777
|
+
/**
|
|
17778
|
+
* The two selection reflections a row carries, written together: `data-selected`
|
|
17779
|
+
* drives the CSS, `aria-selected` is the documented a11y contract ("selected
|
|
17780
|
+
* data rows expose aria-selected"). The keyboard module stamps ARIA only on a
|
|
17781
|
+
* full body render, so the delta paths below have to carry it themselves —
|
|
17782
|
+
* otherwise a screen reader never hears an interaction-driven selection.
|
|
17783
|
+
*/
|
|
17784
|
+
stampRowSelection(row, isSelected) {
|
|
17785
|
+
row.setAttribute('data-selected', String(isSelected));
|
|
17786
|
+
if (isSelected)
|
|
17787
|
+
row.setAttribute('aria-selected', 'true');
|
|
17788
|
+
else
|
|
17789
|
+
row.removeAttribute('aria-selected');
|
|
17790
|
+
}
|
|
16395
17791
|
// A2: reflect one row's selection into the DOM (located by data-index). The
|
|
16396
17792
|
// reconciler (Task B) re-stamps data-index on moved rows, so this stays
|
|
16397
17793
|
// correct after reorder. No-op if the row isn't in the current window.
|
|
@@ -16401,7 +17797,7 @@ class TableColumnDnD {
|
|
|
16401
17797
|
const row = this.tbody.querySelector(`tr[data-index="${index}"]`);
|
|
16402
17798
|
if (!row)
|
|
16403
17799
|
return;
|
|
16404
|
-
|
|
17800
|
+
this.stampRowSelection(row, isSelected);
|
|
16405
17801
|
const checkbox = row.querySelector('snice-checkbox.row-select');
|
|
16406
17802
|
if (checkbox)
|
|
16407
17803
|
checkbox.checked = isSelected;
|
|
@@ -16502,14 +17898,54 @@ class TableColumnDnD {
|
|
|
16502
17898
|
this.settingSortedData = false;
|
|
16503
17899
|
}
|
|
16504
17900
|
sortLocalData() {
|
|
17901
|
+
const previous = this.data;
|
|
16505
17902
|
this.applyLocalSortOrder();
|
|
16506
17903
|
// Sort replaced this.data (new order + reference) — resync the row-index
|
|
16507
17904
|
// map and drop the stale filtered snapshot before re-rendering. Render is
|
|
16508
17905
|
// queued (not synchronous): sortLocalData is reachable from the currentSort
|
|
16509
17906
|
// setter stack via handleControlledSort, and cells must never construct there.
|
|
16510
17907
|
this.rebuildRowIndex();
|
|
17908
|
+
this.remapSelectionAfterReorder(previous);
|
|
16511
17909
|
this.scheduleRender('body');
|
|
16512
17910
|
}
|
|
17911
|
+
/**
|
|
17912
|
+
* Selection is anchored to the ROW, not to its position. `selectedRows` holds
|
|
17913
|
+
* indices into `data`, so any reorder of that array — a local sort, or a
|
|
17914
|
+
* re-delivery of the same row objects in another order — has to re-resolve
|
|
17915
|
+
* them; otherwise the indices keep pointing at whatever row now occupies the
|
|
17916
|
+
* slot and the user's checkmark silently jumps to a different row (the same
|
|
17917
|
+
* remap the row-reorder/DnD path performs).
|
|
17918
|
+
*
|
|
17919
|
+
* Only a permutation is remapped: same length, same identities, no duplicates.
|
|
17920
|
+
* A genuinely new dataset keeps the documented raw-index semantics untouched,
|
|
17921
|
+
* and rows carrying no identity (duplicate objects) have nothing to anchor to.
|
|
17922
|
+
* Call AFTER rebuildRowIndex() — the remap reads the fresh row-index map.
|
|
17923
|
+
*/
|
|
17924
|
+
remapSelectionAfterReorder(previous) {
|
|
17925
|
+
if (this.selectedRows.length === 0)
|
|
17926
|
+
return;
|
|
17927
|
+
if (previous === this.data || previous.length !== this.data.length)
|
|
17928
|
+
return;
|
|
17929
|
+
const identities = new Set(this.data);
|
|
17930
|
+
if (identities.size !== this.data.length)
|
|
17931
|
+
return;
|
|
17932
|
+
// BOTH sides have to be duplicate-free for "same length + subset" to mean
|
|
17933
|
+
// permutation: previous=[A,A] against data=[A,B] satisfies the subset test
|
|
17934
|
+
// while actually being a NEW dataset, and remapping it would move the
|
|
17935
|
+
// selection off the row the raw-index contract points at.
|
|
17936
|
+
if (new Set(previous).size !== previous.length)
|
|
17937
|
+
return;
|
|
17938
|
+
for (const row of previous)
|
|
17939
|
+
if (!identities.has(row))
|
|
17940
|
+
return;
|
|
17941
|
+
const remapped = this.selectedRows
|
|
17942
|
+
.map((index) => this.indexOfRow(previous[index]))
|
|
17943
|
+
.filter((index) => index >= 0);
|
|
17944
|
+
if (remapped.length === this.selectedRows.length
|
|
17945
|
+
&& remapped.every((index, i) => index === this.selectedRows[i]))
|
|
17946
|
+
return;
|
|
17947
|
+
this.selectedRows = remapped;
|
|
17948
|
+
}
|
|
16513
17949
|
dispatchRowSelectionChanged(rowIndex, selected) {
|
|
16514
17950
|
return {
|
|
16515
17951
|
selectedRows: this.selectedRows,
|
|
@@ -16629,20 +18065,37 @@ class TableColumnDnD {
|
|
|
16629
18065
|
const scrollContainer = this.getScrollContainer();
|
|
16630
18066
|
if (!scrollContainer)
|
|
16631
18067
|
return;
|
|
16632
|
-
this.
|
|
18068
|
+
this.syncVirtualRows();
|
|
16633
18069
|
this.virtualizer.attach({
|
|
16634
18070
|
rowHeight: (displayIndex) => this.getVirtualRowHeight(displayIndex),
|
|
16635
18071
|
bufferPx: this.virtualBuffer,
|
|
16636
18072
|
totalRows: this.virtualRowsSnapshot.length,
|
|
16637
18073
|
scrollContainer,
|
|
18074
|
+
// The host template swaps its structural branch (slotted rows ⇄ native
|
|
18075
|
+
// table) without re-attaching. Re-resolving keeps the window painting into
|
|
18076
|
+
// the LIVE tbody instead of the detached branch's orphaned one.
|
|
18077
|
+
resolveScrollContainer: () => this.getScrollContainer(),
|
|
16638
18078
|
renderRange: (start, end) => this.renderRowRange(start, end),
|
|
18079
|
+
// Spacers span the real grid. A spacer wider than the table invents
|
|
18080
|
+
// columns for fixed layout to spend the frame's spare width on.
|
|
18081
|
+
resolveColumnSpan: () => this.getTotalColumnSpan(),
|
|
16639
18082
|
// Pinned rows live outside the windowed range so they are always present,
|
|
16640
18083
|
// exactly like the non-virtual path (renderBody).
|
|
16641
18084
|
renderPinnedTop: () => this.renderPinnedRows(this.pinnedTopRows, 'top'),
|
|
16642
18085
|
renderPinnedBottom: () => this.renderPinnedRows(this.pinnedBottomRows, 'bottom'),
|
|
18086
|
+
// A zero-row window still owes the user the empty/loading/error row the
|
|
18087
|
+
// ordinary path renders.
|
|
18088
|
+
renderEmptyBody: () => this.createEmptyBodyRow(),
|
|
16643
18089
|
// Scroll-driven windows bypass renderBody; restore grid roles, logical
|
|
16644
18090
|
// row indices, and roving focus after every inserted range.
|
|
16645
|
-
afterRender: () =>
|
|
18091
|
+
afterRender: () => {
|
|
18092
|
+
this.keyboard.refresh();
|
|
18093
|
+
// Every window rebuilds the tbody from scratch, taking any filler with
|
|
18094
|
+
// it. Virtualization is not exempt from the fill-row contract, and
|
|
18095
|
+
// renderBody()'s own tail call never runs on the virtual path, so the
|
|
18096
|
+
// filler is re-evaluated here instead.
|
|
18097
|
+
this.updateFillRow();
|
|
18098
|
+
},
|
|
16646
18099
|
});
|
|
16647
18100
|
}
|
|
16648
18101
|
getVirtualRowHeight(displayIndex) {
|
|
@@ -16661,6 +18114,30 @@ class TableColumnDnD {
|
|
|
16661
18114
|
* virtualized); otherwise it is the filtered data. Each descriptor carries
|
|
16662
18115
|
* the logical index used for `data-index`, selection, and detail lookups.
|
|
16663
18116
|
*/
|
|
18117
|
+
/**
|
|
18118
|
+
* Re-derive the windowed row model and hand it to the virtualizer.
|
|
18119
|
+
*
|
|
18120
|
+
* `virtualRowsSnapshot` is the ONLY state the paint pipeline caches, and
|
|
18121
|
+
* `renderRowRange` paints from it. Any caller that resolves a display index
|
|
18122
|
+
* against a FRESH `getVirtualRows()` and then asks the virtualizer to paint
|
|
18123
|
+
* (scrollToRow, the keyboard ensure-visible hook) must republish that model
|
|
18124
|
+
* first, or it scrolls to an offset computed in one list and paints rows from
|
|
18125
|
+
* another — the requested row never appears. Returns the fresh model so the
|
|
18126
|
+
* caller indexes exactly what will be painted.
|
|
18127
|
+
*
|
|
18128
|
+
* `paint` false leaves the repaint to the caller (scrollToIndex refreshes the
|
|
18129
|
+
* window itself). Those callers must not paint from here: the virtualizer's
|
|
18130
|
+
* afterRender hook re-runs keyboard.refresh(), whose ensure-visible callback
|
|
18131
|
+
* lands back in ensureKeyboardRowRendered — an unbounded recursion.
|
|
18132
|
+
*/
|
|
18133
|
+
syncVirtualRows(paint = true) {
|
|
18134
|
+
this.virtualRowsSnapshot = this.getVirtualRows();
|
|
18135
|
+
// Not yet attached during setupVirtualization; attach() takes the count.
|
|
18136
|
+
if (this.virtualizer.isEnabled()) {
|
|
18137
|
+
this.virtualizer.setTotalRows(this.virtualRowsSnapshot.length, paint);
|
|
18138
|
+
}
|
|
18139
|
+
return this.virtualRowsSnapshot;
|
|
18140
|
+
}
|
|
16664
18141
|
getVirtualRows() {
|
|
16665
18142
|
const filtered = this.getFilteredData();
|
|
16666
18143
|
// F: the flattened group+row+aggregate list is the virtual model when
|
|
@@ -16683,14 +18160,7 @@ class TableColumnDnD {
|
|
|
16683
18160
|
const rows = this.virtualRowsSnapshot.length > 0
|
|
16684
18161
|
? this.virtualRowsSnapshot
|
|
16685
18162
|
: this.getVirtualRows();
|
|
16686
|
-
const
|
|
16687
|
-
(this.masterDetail.isEnabled() ? 1 : 0) +
|
|
16688
|
-
(this.rowReorder && this.rowDnD.isEnabled() ? 1 : 0);
|
|
16689
|
-
const managedColumns = this.columnManager.getAllStates();
|
|
16690
|
-
const visibleColumnCount = managedColumns.length > 0
|
|
16691
|
-
? this.columnManager.getVisibleColumns().length
|
|
16692
|
-
: this.columns.length;
|
|
16693
|
-
const totalColSpan = visibleColumnCount + extraCols;
|
|
18163
|
+
const totalColSpan = this.getTotalColumnSpan();
|
|
16694
18164
|
const structuralSig = this.computeStructuralSig();
|
|
16695
18165
|
// Task B: recycle rows that stay in the window across a scroll shift. A row
|
|
16696
18166
|
// still visible after scrolling keeps its data-object key and signature, so
|
|
@@ -16736,9 +18206,10 @@ class TableColumnDnD {
|
|
|
16736
18206
|
// Master-detail: render the expanded detail row in-window. The detail
|
|
16737
18207
|
// A fixed detailHeight participates in virtual spacer math through
|
|
16738
18208
|
// getVirtualRowHeight. Auto-height content is still rendered in-window
|
|
16739
|
-
// and contributes through normal table layout. Tree rows
|
|
16740
|
-
// detail
|
|
16741
|
-
|
|
18209
|
+
// and contributes through normal table layout. Tree rows compose with
|
|
18210
|
+
// master-detail like any other row; only generated gap nodes (index -1)
|
|
18211
|
+
// have no data row to describe.
|
|
18212
|
+
if (dataIndex >= 0 && this.masterDetail.isEnabled() && this.masterDetail.isExpanded(dataIndex)) {
|
|
16742
18213
|
const detailRow = this.masterDetail.createDetailRow(data, dataIndex, totalColSpan);
|
|
16743
18214
|
if (detailRow)
|
|
16744
18215
|
fragment.appendChild(detailRow);
|
|
@@ -16764,7 +18235,8 @@ class TableColumnDnD {
|
|
|
16764
18235
|
// data row rather than an unrelated header/subtotal.
|
|
16765
18236
|
if (this.usesGroupingDisplayModel() || this.treeData.isEnabled()) {
|
|
16766
18237
|
const row = this.data[index];
|
|
16767
|
-
|
|
18238
|
+
// Resolve against the model the virtualizer is about to paint from.
|
|
18239
|
+
const displayIndex = this.syncVirtualRows(false).findIndex((entry) => entry.data === row);
|
|
16768
18240
|
if (displayIndex >= 0)
|
|
16769
18241
|
this.virtualizer.scrollToIndex(displayIndex);
|
|
16770
18242
|
return;
|
|
@@ -16773,6 +18245,14 @@ class TableColumnDnD {
|
|
|
16773
18245
|
}
|
|
16774
18246
|
/** Rows currently reachable by grid keyboard navigation, in display order. */
|
|
16775
18247
|
getKeyboardColumnCount() {
|
|
18248
|
+
// List mode paints tool cells plus ONE full-width cell per row, and its
|
|
18249
|
+
// header no longer mirrors the body — count the body shape directly.
|
|
18250
|
+
if (this.usesListRowRenderer()) {
|
|
18251
|
+
return 1
|
|
18252
|
+
+ (this.rowReorder && this.rowDnD.isEnabled() ? 1 : 0)
|
|
18253
|
+
+ (this.masterDetail.isEnabled() ? 1 : 0)
|
|
18254
|
+
+ (this.hasSelectionColumn() ? 1 : 0);
|
|
18255
|
+
}
|
|
16776
18256
|
const rendered = this.shadowRoot?.querySelectorAll('thead tr.column-header-row > th').length ?? 0;
|
|
16777
18257
|
if (rendered > 0)
|
|
16778
18258
|
return rendered;
|
|
@@ -16828,7 +18308,9 @@ class TableColumnDnD {
|
|
|
16828
18308
|
const row = this.getKeyboardRows()[rowIndex];
|
|
16829
18309
|
if (row === undefined)
|
|
16830
18310
|
return;
|
|
16831
|
-
|
|
18311
|
+
// The window is painted from `virtualRowsSnapshot`; republish it so the
|
|
18312
|
+
// offset we scroll to and the rows that land there come from one model.
|
|
18313
|
+
const displayIndex = this.syncVirtualRows(false).findIndex((entry) => entry.data === row);
|
|
16832
18314
|
if (displayIndex >= 0)
|
|
16833
18315
|
this.virtualizer.scrollToIndex(displayIndex);
|
|
16834
18316
|
}
|
|
@@ -16956,8 +18438,7 @@ class TableColumnDnD {
|
|
|
16956
18438
|
return items;
|
|
16957
18439
|
const totalPages = Math.max(1, Math.ceil(items.length / this.pageSize));
|
|
16958
18440
|
const page = Math.max(1, Math.min(this.currentPage, totalPages));
|
|
16959
|
-
|
|
16960
|
-
this.currentPage = page;
|
|
18441
|
+
this.clampCurrentPage(page);
|
|
16961
18442
|
const start = (page - 1) * this.pageSize;
|
|
16962
18443
|
return items.slice(start, start + this.pageSize);
|
|
16963
18444
|
}
|
|
@@ -16977,8 +18458,7 @@ class TableColumnDnD {
|
|
|
16977
18458
|
return rows;
|
|
16978
18459
|
const totalPages = Math.max(1, Math.ceil(rows.length / this.pageSize));
|
|
16979
18460
|
const page = Math.max(1, Math.min(this.currentPage, totalPages));
|
|
16980
|
-
|
|
16981
|
-
this.currentPage = page;
|
|
18461
|
+
this.clampCurrentPage(page);
|
|
16982
18462
|
const start = (page - 1) * this.pageSize;
|
|
16983
18463
|
return rows.slice(start, start + this.pageSize);
|
|
16984
18464
|
}
|
|
@@ -17040,16 +18520,21 @@ class TableColumnDnD {
|
|
|
17040
18520
|
this.renderBody();
|
|
17041
18521
|
this.dispatchColumnPinChange(key, false);
|
|
17042
18522
|
}
|
|
18523
|
+
// A measured width is the column's width, not the header's: the body has to
|
|
18524
|
+
// be repainted with it too, or the th and its tds carry different numbers
|
|
18525
|
+
// and the next body-only re-render paints the stale one.
|
|
17043
18526
|
autoSizeColumn(key) {
|
|
17044
18527
|
if (this.tbody) {
|
|
17045
18528
|
this.columnManager.autoSizeColumn(key, this.tbody);
|
|
17046
18529
|
this.renderHeader();
|
|
18530
|
+
this.renderBody();
|
|
17047
18531
|
}
|
|
17048
18532
|
}
|
|
17049
18533
|
autoSizeAllColumns() {
|
|
17050
18534
|
if (this.tbody) {
|
|
17051
18535
|
this.columnManager.autoSizeAll(this.tbody);
|
|
17052
18536
|
this.renderHeader();
|
|
18537
|
+
this.renderBody();
|
|
17053
18538
|
}
|
|
17054
18539
|
}
|
|
17055
18540
|
moveColumn(key, toIndex) {
|
|
@@ -17309,8 +18794,7 @@ class TableColumnDnD {
|
|
|
17309
18794
|
this.masterDetail.onHeightChange = () => {
|
|
17310
18795
|
if (!this.virtualize || !this.virtualizer.isEnabled())
|
|
17311
18796
|
return;
|
|
17312
|
-
this.
|
|
17313
|
-
this.virtualizer.setTotalRows(this.virtualRowsSnapshot.length);
|
|
18797
|
+
this.syncVirtualRows();
|
|
17314
18798
|
};
|
|
17315
18799
|
this.masterDetail.prepare(this.data);
|
|
17316
18800
|
this.renderHeader();
|
|
@@ -17537,6 +19021,9 @@ class TableColumnDnD {
|
|
|
17537
19021
|
// The row recycler cannot infer that a callback identity changed from
|
|
17538
19022
|
// row data alone. Force fresh rows so list markup replaces table cells.
|
|
17539
19023
|
this.renderedRows = new Map();
|
|
19024
|
+
// Installing a renderer removes the column geometry the header describes;
|
|
19025
|
+
// clearing it back to null restores columnar rows AND their header.
|
|
19026
|
+
this.renderHeader();
|
|
17540
19027
|
this.renderBody();
|
|
17541
19028
|
}
|
|
17542
19029
|
setListViewRenderer(fn) {
|
|
@@ -17555,7 +19042,7 @@ class TableColumnDnD {
|
|
|
17555
19042
|
tr.style.height = `${this.rowHeight}px`;
|
|
17556
19043
|
}
|
|
17557
19044
|
const isSelected = this.selectedRows.includes(index);
|
|
17558
|
-
|
|
19045
|
+
this.stampRowSelection(tr, isSelected);
|
|
17559
19046
|
// Row DnD handle
|
|
17560
19047
|
if (this.rowReorder && this.rowDnD.isEnabled()) {
|
|
17561
19048
|
tr.appendChild(this.rowDnD.createDragHandle());
|
|
@@ -17626,7 +19113,18 @@ class TableColumnDnD {
|
|
|
17626
19113
|
const toggle = this.treeData.createToggle(treeRow);
|
|
17627
19114
|
td.appendChild(toggle);
|
|
17628
19115
|
const textSpan = document.createElement('span');
|
|
17629
|
-
textSpan.
|
|
19116
|
+
textSpan.className = 'tree-label';
|
|
19117
|
+
// The group cell keeps its plain label next to the indent/chevron, but
|
|
19118
|
+
// it is still a cell: renderCell owns it outright, and otherwise the
|
|
19119
|
+
// documented display pipeline (formatter, then valueFormatter) decides
|
|
19120
|
+
// the text. Only a column with neither shows the raw working value.
|
|
19121
|
+
if (typeof column.renderCell === 'function') {
|
|
19122
|
+
textSpan.appendChild(this.createCellElement(column, value, rowData));
|
|
19123
|
+
}
|
|
19124
|
+
else {
|
|
19125
|
+
textSpan.textContent = this.cellDisplayText(column, value, rowData)
|
|
19126
|
+
?? (value == null ? '' : String(value));
|
|
19127
|
+
}
|
|
17630
19128
|
// Clicking the text label also toggles expand/collapse
|
|
17631
19129
|
if (treeRow.hasChildren) {
|
|
17632
19130
|
textSpan.style.cursor = 'pointer';
|
|
@@ -17645,21 +19143,7 @@ class TableColumnDnD {
|
|
|
17645
19143
|
const state = this.columnManager.getState(column.key);
|
|
17646
19144
|
if (state) {
|
|
17647
19145
|
td.style.width = `${state.width}px`;
|
|
17648
|
-
|
|
17649
|
-
if (state.pinned === 'left') {
|
|
17650
|
-
const offsets = this.columnManager.getPinnedLeftOffsets();
|
|
17651
|
-
td.classList.add('pinned-cell');
|
|
17652
|
-
td.style.position = 'sticky';
|
|
17653
|
-
td.style.left = `${offsets.get(column.key) ?? 0}px`;
|
|
17654
|
-
td.style.zIndex = '1';
|
|
17655
|
-
}
|
|
17656
|
-
else if (state.pinned === 'right') {
|
|
17657
|
-
const offsets = this.columnManager.getPinnedRightOffsets();
|
|
17658
|
-
td.classList.add('pinned-cell');
|
|
17659
|
-
td.style.position = 'sticky';
|
|
17660
|
-
td.style.right = `${offsets.get(column.key) ?? 0}px`;
|
|
17661
|
-
td.style.zIndex = '1';
|
|
17662
|
-
}
|
|
19146
|
+
this.applyPinnedCell(td, column.key, state, '1');
|
|
17663
19147
|
}
|
|
17664
19148
|
tr.appendChild(td);
|
|
17665
19149
|
});
|
|
@@ -17823,20 +19307,7 @@ class TableColumnDnD {
|
|
|
17823
19307
|
const state = this.columnManager.getState(column.key);
|
|
17824
19308
|
if (state) {
|
|
17825
19309
|
td.style.width = `${state.width}px`;
|
|
17826
|
-
|
|
17827
|
-
const offsets = this.columnManager.getPinnedLeftOffsets();
|
|
17828
|
-
td.classList.add('pinned-cell');
|
|
17829
|
-
td.style.position = 'sticky';
|
|
17830
|
-
td.style.left = `${offsets.get(column.key) ?? 0}px`;
|
|
17831
|
-
td.style.zIndex = '1';
|
|
17832
|
-
}
|
|
17833
|
-
else if (state.pinned === 'right') {
|
|
17834
|
-
const offsets = this.columnManager.getPinnedRightOffsets();
|
|
17835
|
-
td.classList.add('pinned-cell');
|
|
17836
|
-
td.style.position = 'sticky';
|
|
17837
|
-
td.style.right = `${offsets.get(column.key) ?? 0}px`;
|
|
17838
|
-
td.style.zIndex = '1';
|
|
17839
|
-
}
|
|
19310
|
+
this.applyPinnedCell(td, column.key, state, '1');
|
|
17840
19311
|
}
|
|
17841
19312
|
tr.appendChild(td);
|
|
17842
19313
|
});
|
|
@@ -18017,6 +19488,26 @@ class TableColumnDnD {
|
|
|
18017
19488
|
// C1: reactive, no attribute reflection. A post-mount assignment (or the
|
|
18018
19489
|
// setData/setColumns aliases) re-renders through the microtask-coalesced
|
|
18019
19490
|
// queue — see handleColumnsAssignment / handleDataAssignment.
|
|
19491
|
+
//
|
|
19492
|
+
// `hasChanged: () => true` — ASSIGNMENT IS THE SIGNAL, NOT IDENTITY.
|
|
19493
|
+
// The core dirty-check (notEqual in packages/core/src/utils.ts, applied by the
|
|
19494
|
+
// @property setter in element.ts) is identity-based and stays that way for
|
|
19495
|
+
// every other property: that is the right default for reactivity in general.
|
|
19496
|
+
// But `data` and `columns` are bulk payloads that callers routinely mutate in
|
|
19497
|
+
// place and then publish by re-assigning the SAME array reference
|
|
19498
|
+
// (`rows.push(row); table.data = rows`). The documented contract is that
|
|
19499
|
+
// assigning `data`/`columns` rerenders, so these two properties opt OUT of the
|
|
19500
|
+
// identity gate locally rather than weakening it globally.
|
|
19501
|
+
//
|
|
19502
|
+
// Tradeoff — double-render cost: a genuinely redundant assignment
|
|
19503
|
+
// (`table.data = table.data` with nothing changed) now costs a render that the
|
|
19504
|
+
// identity gate used to swallow, and a caller who assigns both `columns` and
|
|
19505
|
+
// `data` pays two schedule calls instead of one. It is bounded: both watchers
|
|
19506
|
+
// go through scheduleRender(), which coalesces a burst within a tick into a
|
|
19507
|
+
// single microtask flush, so the worst case is one extra body paint per tick,
|
|
19508
|
+
// never per assignment. Deep-equality diffing was rejected as the alternative
|
|
19509
|
+
// — it is O(rows x columns) on every assignment, i.e. a cost paid by the
|
|
19510
|
+
// common (genuinely-changed) path to save the rare redundant one.
|
|
18020
19511
|
this.columns = (__runInitializers(this, _searchDebounce_extraInitializers), __runInitializers(this, _columns_initializers, []));
|
|
18021
19512
|
this.data = (__runInitializers(this, _columns_extraInitializers), __runInitializers(this, _data_initializers, []));
|
|
18022
19513
|
/**
|
|
@@ -18037,7 +19528,8 @@ class TableColumnDnD {
|
|
|
18037
19528
|
this.selector = __runInitializers(this, _selector_initializers, '');
|
|
18038
19529
|
this.selectorOptions = (__runInitializers(this, _selector_extraInitializers), __runInitializers(this, _selectorOptions_initializers, []));
|
|
18039
19530
|
this.loading = (__runInitializers(this, _selectorOptions_extraInitializers), __runInitializers(this, _loading_initializers, false));
|
|
18040
|
-
// Set when a remote-mode load fails; cleared
|
|
19531
|
+
// Set when a remote-mode load fails; cleared by clearLoadError() as soon as
|
|
19532
|
+
// ANY fresh dataset arrives (see there).
|
|
18041
19533
|
// Not a @property — renderBody() reads it directly, no attribute needed.
|
|
18042
19534
|
this.loadError = (__runInitializers(this, _loading_extraInitializers), null);
|
|
18043
19535
|
this.virtualize = __runInitializers(this, _virtualize_initializers, false);
|
|
@@ -18047,7 +19539,16 @@ class TableColumnDnD {
|
|
|
18047
19539
|
this.editable = (__runInitializers(this, _columnResize_extraInitializers), __runInitializers(this, _editable_initializers, false));
|
|
18048
19540
|
this.editMode = (__runInitializers(this, _editable_extraInitializers), __runInitializers(this, _editMode_initializers, 'cell'));
|
|
18049
19541
|
this.density = (__runInitializers(this, _editMode_extraInitializers), __runInitializers(this, _density_initializers, 'standard'));
|
|
18050
|
-
|
|
19542
|
+
/**
|
|
19543
|
+
* How the columns relate to the frame's width.
|
|
19544
|
+
*
|
|
19545
|
+
* `scroll` keeps every column at or above its `minWidth` and lets the frame
|
|
19546
|
+
* scroll horizontally when they no longer fit. `squish` makes the frame the
|
|
19547
|
+
* hard constraint instead: minimums relax, content ellipsises, and the table
|
|
19548
|
+
* never scrolls sideways. See the `ColumnFit` docs in snice-table.types.ts.
|
|
19549
|
+
*/
|
|
19550
|
+
this.columnFit = (__runInitializers(this, _density_extraInitializers), __runInitializers(this, _columnFit_initializers, 'scroll'));
|
|
19551
|
+
this.headerFilters = (__runInitializers(this, _columnFit_extraInitializers), __runInitializers(this, _headerFilters_initializers, false));
|
|
18051
19552
|
this.quickFilter = (__runInitializers(this, _headerFilters_extraInitializers), __runInitializers(this, _quickFilter_initializers, false));
|
|
18052
19553
|
this.rowReorder = (__runInitializers(this, _quickFilter_extraInitializers), __runInitializers(this, _rowReorder_initializers, false));
|
|
18053
19554
|
this.columnReorder = (__runInitializers(this, _rowReorder_extraInitializers), __runInitializers(this, _columnReorder_initializers, false));
|
|
@@ -18099,7 +19600,9 @@ class TableColumnDnD {
|
|
|
18099
19600
|
this.table = __runInitializers(this, _table_initializers, void 0);
|
|
18100
19601
|
this.thead = (__runInitializers(this, _table_extraInitializers), __runInitializers(this, _thead_initializers, void 0));
|
|
18101
19602
|
this.tbody = (__runInitializers(this, _thead_extraInitializers), __runInitializers(this, _tbody_initializers, void 0));
|
|
18102
|
-
|
|
19603
|
+
/** Frame width the columns were last fitted to — see handleFrameResize. */
|
|
19604
|
+
this.lastFittedFrameWidth = (__runInitializers(this, _tbody_extraInitializers), 0);
|
|
19605
|
+
this.unsortedData = [];
|
|
18103
19606
|
this.dataRequestTimeout = null;
|
|
18104
19607
|
// A1: row-object → its index in `this.data`. Replaces the O(n) `data.indexOf`
|
|
18105
19608
|
// that ran per row on every selection toggle (was O(n²) for select-all).
|
|
@@ -18131,6 +19634,10 @@ class TableColumnDnD {
|
|
|
18131
19634
|
// C1: set while the imperative setData() runs so the data @watch resyncs the
|
|
18132
19635
|
// model but skips the auto-render (the caller drives the body fill).
|
|
18133
19636
|
this.settingDataImperative = false;
|
|
19637
|
+
// Set while the row-reorder handler re-publishes `this.data` as a new array
|
|
19638
|
+
// of the SAME rows. Tells the data @watch that no fresh dataset arrived, so a
|
|
19639
|
+
// pending load error must survive the drag.
|
|
19640
|
+
this.reorderingRows = false;
|
|
18134
19641
|
// C1/C2: microtask-coalesced render queue. Reactive assignments schedule a
|
|
18135
19642
|
// render here instead of calling renderHeader/renderBody synchronously —
|
|
18136
19643
|
// MANDATORY, because happy-dom crashes constructing cell elements inside a
|
|
@@ -18138,6 +19645,32 @@ class TableColumnDnD {
|
|
|
18138
19645
|
this.renderQueued = false;
|
|
18139
19646
|
this.pendingHeaderRender = false;
|
|
18140
19647
|
this.pendingBodyRender = false;
|
|
19648
|
+
// Re-arm bookkeeping for a body render that found no <tbody> to paint into.
|
|
19649
|
+
// Bounded: the slotted-rows template legitimately has no <tbody>, and an
|
|
19650
|
+
// unbounded retry there would spin every frame.
|
|
19651
|
+
this.bodyRenderRetries = 0;
|
|
19652
|
+
this.bodyRenderRetryQueued = false;
|
|
19653
|
+
/**
|
|
19654
|
+
* Write a page number the display model just derived (a re-clamp after the
|
|
19655
|
+
* row set shortened), WITHOUT re-entering the render scheduler.
|
|
19656
|
+
*
|
|
19657
|
+
* The three clamp sites all sit inside a display-model computation whose
|
|
19658
|
+
* caller paints immediately afterwards with the clamped page — the ordinary
|
|
19659
|
+
* body path slices with it and then renders the pager, the grouping and tree
|
|
19660
|
+
* models return the clamped slice. Letting the @watch fire here queued a
|
|
19661
|
+
* second, byte-identical body pass per delivery — the measured cost, and what
|
|
19662
|
+
* the regression test pins. Suppressing the write's watcher makes the clamp
|
|
19663
|
+
* cost zero extra passes and makes an oscillating clamp structurally unable
|
|
19664
|
+
* to loop.
|
|
19665
|
+
*
|
|
19666
|
+
* All three sites are reachable only under `paginationMode === 'client'`, so
|
|
19667
|
+
* the watcher's server branch (debouncedDataRequest, whose `loading = true`
|
|
19668
|
+
* re-enters renderBody SYNCHRONOUSLY through the loading watcher — from the
|
|
19669
|
+
* middle of a render) is not reachable from a clamp today. It is the reason
|
|
19670
|
+
* to keep this guard rather than to drop the watcher suppression if a fourth
|
|
19671
|
+
* clamp site ever lands outside that guard.
|
|
19672
|
+
*/
|
|
19673
|
+
this.clampingPage = false;
|
|
18141
19674
|
/**
|
|
18142
19675
|
* A structural fingerprint of everything `createRow` reads that is NOT
|
|
18143
19676
|
* per-row data: visible columns + their widths/pinning/offsets, tool columns,
|