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
|
@@ -5,18 +5,18 @@ Calendar with date selection and event support.
|
|
|
5
5
|
## Properties
|
|
6
6
|
|
|
7
7
|
```typescript
|
|
8
|
-
value: Date | string =
|
|
8
|
+
value: Date | string | null = null; // null = nothing selected (initial state); today is marked by highlightToday, not by a selection
|
|
9
9
|
view: 'month'|'week'|'day' = 'month';
|
|
10
10
|
events: CalendarEvent[] = [];
|
|
11
11
|
minDate: Date | string = ''; // attribute: min-date
|
|
12
12
|
maxDate: Date | string = ''; // attribute: max-date
|
|
13
13
|
disabledDates: (Date | string)[] = []; // JS-only; no attribute
|
|
14
14
|
highlightToday: boolean = true; // attribute: highlight-today
|
|
15
|
-
showWeekNumbers: boolean = false; // attribute: show-week-numbers
|
|
15
|
+
showWeekNumbers: boolean = false; // attribute: show-week-numbers — adds a leading week-number column (ISO-8601 when firstDayOfWeek=1, otherwise the week containing Jan 1 is week 1)
|
|
16
16
|
firstDayOfWeek: number = 0; // attribute: first-day-of-week (0=Sun, 1=Mon)
|
|
17
17
|
locale: string = 'en-US';
|
|
18
18
|
noDaySelect: boolean = false; // attribute: no-day-select — display-only: day clicks don't select/highlight or fire calendar-change; event bars stay interactive
|
|
19
|
-
cellSizing: 'square'|'stretch' = 'square'; // attribute: cell-sizing — square: cells as tall as the column is wide; stretch: rows collapse to content
|
|
19
|
+
cellSizing: 'square'|'stretch' = 'square'; // attribute: cell-sizing — square: cells as tall as the column is wide; stretch: rows collapse to content (every event lane shown). Sizes the cell, and the cell's height sets the visible event-lane count
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## Methods
|
|
@@ -33,7 +33,11 @@ cellSizing: 'square'|'stretch' = 'square'; // attribute: cell-sizing — square:
|
|
|
33
33
|
|
|
34
34
|
- `calendar-change` -> `{ value: Date, calendar: SniceCalendarElement }`
|
|
35
35
|
- `calendar-event-click` -> `{ event: CalendarEvent, calendar: SniceCalendarElement }`
|
|
36
|
-
- `calendar-more-click` -> `{ date: Date, count: number, calendar: SniceCalendarElement }` — "+N more" chip clicked; does NOT fire `calendar-change`
|
|
36
|
+
- `calendar-more-click` -> `{ date: Date, count: number, calendar: SniceCalendarElement }` — "+N more" chip clicked; does NOT fire `calendar-change`. **Cancelable**: `preventDefault()` suppresses the built-in day panel (see "+N more" default action)
|
|
37
|
+
|
|
38
|
+
## Methods (overlay)
|
|
39
|
+
|
|
40
|
+
- `closeEventPopover(options?: { returnFocus?: boolean })` - Closes the shared overlay (event popover or "+N more" day panel)
|
|
37
41
|
|
|
38
42
|
## CSS Parts
|
|
39
43
|
|
|
@@ -41,6 +45,8 @@ cellSizing: 'square'|'stretch' = 'square'; // attribute: cell-sizing — square:
|
|
|
41
45
|
- `header` - Header with title and navigation buttons
|
|
42
46
|
- `grid` - Day cells grid
|
|
43
47
|
- `more-chip` - Per-day "+N more" overflow chip
|
|
48
|
+
- `more-list` / `more-item` / `more-dot` / `more-panel-date` - Built-in "+N more" day panel internals
|
|
49
|
+
- `week-number` / `week-number-header` - Week-number column cells (`show-week-numbers`)
|
|
44
50
|
- `event-bar` / `event-avatar` / `event-tooltip` / `event-popover` - Event bar internals
|
|
45
51
|
|
|
46
52
|
## Basic Usage
|
|
@@ -58,21 +64,31 @@ calendar.events = [
|
|
|
58
64
|
];
|
|
59
65
|
// Ranged events render as continuous stripes: one bar per week row, chopped
|
|
60
66
|
// at week boundaries (squared corners on the continuing side, title repeats).
|
|
61
|
-
// Concurrent events stack into lanes (start asc, longer first on ties)
|
|
62
|
-
//
|
|
67
|
+
// Concurrent events stack into lanes (start asc, longer first on ties).
|
|
68
|
+
// Visible lanes are DERIVED PER WEEK from the day cell's height (see
|
|
69
|
+
// "Event-lane budget"); deeper stacks collapse to a per-day "+N more" chip.
|
|
63
70
|
// Chip: part="more-chip", role="button" tabindex=0 (Enter/Space), click ->
|
|
64
71
|
// calendar-more-click { date, count }; the click never falls through to day
|
|
65
|
-
// selection.
|
|
66
|
-
//
|
|
67
|
-
//
|
|
72
|
+
// selection. Built-in default action, no wiring required — see
|
|
73
|
+
// '"+N more" default action' below. Replace it with your own day/agenda view:
|
|
74
|
+
// calendar.addEventListener('calendar-more-click', (e) => {
|
|
75
|
+
// e.preventDefault(); // cancels the built-in panel
|
|
76
|
+
// showDay(e.detail.date, e.detail.count);
|
|
77
|
+
// });
|
|
68
78
|
// Bars: part="event-bar", click -> calendar-event-click.
|
|
69
79
|
// Styling: color -> background; avatar?: string | {src?,name?,alt?} ->
|
|
70
80
|
// <snice-avatar> on each bar (part="event-avatar"; name -> initials fallback);
|
|
81
|
+
// Adornment, not payload: a bar segment narrower than 4.5rem renders the
|
|
82
|
+
// title alone (class calendar__event-bar--compact, tighter padding) — the
|
|
83
|
+
// avatar would leave ~3 characters in a default-width month cell. Measured
|
|
84
|
+
// per segment, so a multi-day span keeps it; re-decided on resize.
|
|
71
85
|
// className?: string -> added to bar classes AND part list, so
|
|
72
86
|
// ::part(<className>) themes specific events from outside.
|
|
73
87
|
// Tooltips: event.tooltip?: string (static), or on the element:
|
|
74
88
|
// eventTooltip: (event) => string | Node | Promise<string | Node> // lazy/rich; wins over event.tooltip
|
|
75
|
-
// Stale async results discarded on pointer-leave. Overlay part="event-tooltip"
|
|
89
|
+
// Stale async results discarded on pointer-leave. Overlay part="event-tooltip";
|
|
90
|
+
// surface = --snice-tooltip-bg / --snice-tooltip-color (same pair as
|
|
91
|
+
// <snice-tooltip>), so it stays legible in both themes.
|
|
76
92
|
// Popovers (click-to-open, interactive; STRICT per-event opt-in):
|
|
77
93
|
// event.popover?: boolean | string | Node | (() => Node)
|
|
78
94
|
// Resolution: inline content -> element eventPopover(event) => string|Node|Promise
|
|
@@ -81,12 +97,99 @@ calendar.events = [
|
|
|
81
97
|
// to bar; loading state while pending; stale results discarded.
|
|
82
98
|
// Bars with popover: role="button" tabindex=0, Enter/Space opens.
|
|
83
99
|
// calendar-event-click still fires. closeEventPopover() closes programmatically.
|
|
100
|
+
// Overlay placement (both tooltip and popover): position: absolute inside
|
|
101
|
+
// ::part(base), NOT viewport-fixed — :host has `contain: layout style`, which
|
|
102
|
+
// makes the host the containing block for fixed descendants. Anchored to the
|
|
103
|
+
// bar (popover below / tooltip above), flipped at the viewport edge, clamped
|
|
104
|
+
// horizontally. Containment also makes the host a stacking context, so while
|
|
105
|
+
// an overlay shows the host carries a read-only `overlay-open` attribute and
|
|
106
|
+
// z-index: var(--snice-z-floating, 1000); restyle via
|
|
107
|
+
// snice-calendar[overlay-open] { z-index: ... }.
|
|
84
108
|
|
|
85
109
|
calendar.addEventListener('calendar-change', (e) => {
|
|
86
110
|
console.log('Selected:', e.detail.value);
|
|
87
111
|
});
|
|
88
112
|
```
|
|
89
113
|
|
|
114
|
+
## "+N more" Default Action
|
|
115
|
+
|
|
116
|
+
Clicking (or Enter/Space on) the chip opens the shared overlay listing that
|
|
117
|
+
day's hidden events — no listener required.
|
|
118
|
+
|
|
119
|
+
- Panel = the same `.calendar__popover` element the event popover uses:
|
|
120
|
+
`role="dialog"`, `part="event-popover"`, Escape / outside-press dismiss,
|
|
121
|
+
focus moves into the panel and returns to the chip on close.
|
|
122
|
+
- `aria-label` names the day; `part="more-panel-date"` heading + `part="more-list"`
|
|
123
|
+
(`role="list"`) of `<button part="more-item">` entries, each a
|
|
124
|
+
`part="more-dot"` colour dot (`event.color`) plus the title.
|
|
125
|
+
- Entry click -> `calendar-event-click`; if the event has `popover` content the
|
|
126
|
+
same overlay drills into it, otherwise the panel closes.
|
|
127
|
+
- Suppress with `preventDefault()` on `calendar-more-click`:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
calendar.addEventListener('calendar-more-click', (e) => {
|
|
131
|
+
e.preventDefault(); // built-in panel does not open
|
|
132
|
+
openMyDayView(e.detail.date);
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Event-Lane Budget
|
|
137
|
+
|
|
138
|
+
Visible lanes per week = how many fit the day cell's height. No flat cap.
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
lanes = max(1, floor((cellHeight - 2.125rem - chip) / 1.375rem))
|
|
142
|
+
chip = 1rem when the stack overflows (the "+N more" strip), else 0
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Cell height comes from whatever sizes the cell:
|
|
146
|
+
- `cell-sizing="square"` (default): `100cqw / 7` — the column width; wider calendar -> taller cells -> more lanes. Capped by the built-in `max-width: 37.5rem` (~5.4rem cell -> 2 lanes, 1 with the chip); lift with `::part(base) { max-width: none }`. Narrow calendars show fewer lanes than the flat 3-lane cap this replaced.
|
|
147
|
+
- Definite host height: `snice-calendar { height: 40rem }` (or a sized flex/grid parent) — the six week rows share the room under the header, so the budget grows with it.
|
|
148
|
+
- `cell-sizing="stretch"` with no imposed height: no budget — rows collapse to content and every lane is shown, no chip.
|
|
149
|
+
|
|
150
|
+
Notes:
|
|
151
|
+
- Re-derived on resize (`@observe('resize')`) — no need to re-assign `events`.
|
|
152
|
+
- Row reservation is a floor, applied only when the cell is shorter than the stack it shows: 3.5rem for one lane, 4.5rem for one lane + chip. Above that all rows stay equal; below it (square cells under ~4.5rem, i.e. calendars under ~500px wide) the busy row still grows.
|
|
153
|
+
- No layout to measure (headless DOM, `display: none`, detached) -> falls back to 3 lanes.
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
// Same events, more visible lanes:
|
|
157
|
+
calendar.style.height = '40rem';
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Sizing In A Constrained Host
|
|
161
|
+
|
|
162
|
+
The month fits its own host box — the bottom weeks are not clipped.
|
|
163
|
+
|
|
164
|
+
- Host height is **auto** (default): week rows take the square baseline
|
|
165
|
+
(`100cqw / 7`), and a busy row grows by its lane reservation. The host's
|
|
166
|
+
height is whatever that adds up to.
|
|
167
|
+
- Host height is **larger** than that: the six rows share the surplus and the
|
|
168
|
+
lane budget grows with them.
|
|
169
|
+
- Host height is **smaller** (`height`, `max-height`, `aspect-ratio`, a sized
|
|
170
|
+
flex/grid parent): the rows are capped at their equal share of the room under
|
|
171
|
+
the header, so all six weeks stay inside the box. The lane budget follows the
|
|
172
|
+
compressed row — events that no longer fit collapse into the "+N more" chip
|
|
173
|
+
instead of being drawn outside their cell, and a row too short for even one
|
|
174
|
+
lane shows the chip alone.
|
|
175
|
+
|
|
176
|
+
Limits:
|
|
177
|
+
- Measures its OWN box only. An ancestor that clips/scrolls while the calendar's
|
|
178
|
+
height stays auto (fixed wrapper + `overflow:hidden`, flex item without
|
|
179
|
+
`min-height:0`) is undetectable — constrain the calendar, not the wrapper.
|
|
180
|
+
- Row floor = the cell's own padding + rules (~17px default). Under a ~200px
|
|
181
|
+
host the grid overflows again rather than stacking week over week.
|
|
182
|
+
- `view="week"` / `view="day"` keep their own taller row minimums; not capped.
|
|
183
|
+
|
|
184
|
+
Authoring note: a month grid is a header, a weekday strip and six square-ish
|
|
185
|
+
week rows, so its natural box is TALLER than it is wide. `aspect-ratio: 1` on
|
|
186
|
+
the host is not a neutral size hint — it asks the grid to lose ~1.5 week rows.
|
|
187
|
+
Constrain the width and let the height follow:
|
|
188
|
+
|
|
189
|
+
```html
|
|
190
|
+
<snice-calendar style="min-width: 360px; max-width: 26rem; width: 100%"></snice-calendar>
|
|
191
|
+
```
|
|
192
|
+
|
|
90
193
|
## Keyboard Navigation
|
|
91
194
|
|
|
92
195
|
- Arrow keys: navigate dates
|
|
@@ -71,6 +71,10 @@ cb.code = 'const x = 1;';
|
|
|
71
71
|
|
|
72
72
|
Grammar-based: `format="pretty"` (grammars: json, typescript, css, snice)
|
|
73
73
|
|
|
74
|
+
- Preserve author line breaks and blank lines; renormalize leading indentation; space rules apply within a line
|
|
75
|
+
- Rule-driven breaks (after `;`, before `}`) fire only where no author break exists; suppressed inside `()`/`[]`; closers break only when their group is multi-line
|
|
76
|
+
- One-liners (e.g. minified JSON) still pretty-print
|
|
77
|
+
|
|
74
78
|
Imperative: `setFormatter(fn)` overrides grammar formatters
|
|
75
79
|
|
|
76
80
|
```typescript
|
|
@@ -63,7 +63,7 @@ doc.downloadAs('markdown', 'my-doc.md');
|
|
|
63
63
|
|
|
64
64
|
## Icon Sets
|
|
65
65
|
|
|
66
|
-
- `default` - Text labels and
|
|
66
|
+
- `default` - Text labels and embedded SVG icons
|
|
67
67
|
- `material` - Material Symbols Outlined (load font in document)
|
|
68
68
|
- `fontawesome` - Font Awesome 6 solid (load font in document)
|
|
69
69
|
- Fonts cascade from light DOM into shadow DOM
|
|
@@ -39,6 +39,7 @@ Circular: `base`, `circle`, `circle-bg`, `circle-bar`, `label`
|
|
|
39
39
|
--progress-radius: 4px;
|
|
40
40
|
--progress-bg: var(--snice-color-border, rgb(226 226 226));
|
|
41
41
|
--progress-color: var(--snice-color-primary, rgb(37 99 235));
|
|
42
|
+
--progress-track-opacity: 0.2; /* circular track ring; raise to 1 on dark surfaces — 0.2 of any color vanishes on near-black */
|
|
42
43
|
--progress-animation-duration: 1.5s;
|
|
43
44
|
```
|
|
44
45
|
|
|
@@ -139,5 +139,5 @@ receipt.taxes = [
|
|
|
139
139
|
- Subtotal auto-computed from items if not set
|
|
140
140
|
- Total auto-computed as subtotal + tax - discount + tip if not set
|
|
141
141
|
- `taxes` array overrides single `tax` property
|
|
142
|
-
- SKU, notes, per-item
|
|
142
|
+
- SKU, notes, per-item discount lines only in `detailed` variant; an item `discount` still reduces the subtotal in every variant
|
|
143
143
|
- Print styles included for all variants
|
|
@@ -14,13 +14,14 @@ striped:boolean=false; searchable:boolean=false; filterable:boolean=false; sorta
|
|
|
14
14
|
hoverable:boolean=true; clickable:boolean=false; list:boolean=false; loading:boolean=false; pagination:boolean=false; virtualize:boolean=false; editable:boolean=false; columnResize:boolean=false; headerFilters:boolean=false; // attrs column-resize/header-filters
|
|
15
15
|
quickFilter:boolean=false; rowReorder:boolean=false; columnReorder:boolean=false; columnMenu:boolean=false; lazyLoad:boolean=false; // attrs quick-filter/row-reorder/column-reorder/column-menu/lazy-load
|
|
16
16
|
mode: 'local'|'remote' = 'local';
|
|
17
|
-
columns:ColumnDefinition[]=[]; data:any[]=[]; currentSort:{column:string;direction:'asc'|'desc'}[]=[]; selectedRows:number[]=[]; // JS-only; selectedRows are raw-data indices
|
|
17
|
+
columns:ColumnDefinition[]=[]; data:any[]=[]; currentSort:{column:string;direction:'asc'|'desc'}[]=[]; selectedRows:number[]=[]; // JS-only; selectedRows are raw-data indices, re-resolved to the same row objects when `data` is reordered (local sort, reordered re-delivery)
|
|
18
18
|
listRenderer:((row,index)=>string|HTMLElement)|null=null; // JS-only; full-width custom rows in list mode
|
|
19
19
|
searchText:string=''; searchDebounce:number=500; // searchText JS-only; attr search-debounce
|
|
20
20
|
selectionMode:'none'|'single'|'multiple'='multiple'; selector:string=''; selectorOptions:{value:string;label:string}[]=[]; // attr selection-mode; options JS-only
|
|
21
21
|
groupBy:string|string[]=''; groupDefaults:{expanded?:boolean}={}; // JS-only
|
|
22
22
|
paginationMode:'client'|'server'='client'; pageSize:number=10; currentPage:number=1; totalItems:number=0; pageSizes:number[]=[10,25,50,100]; // attrs pagination-mode/page-size/current-page/total-items; pageSizes JS-only
|
|
23
23
|
rowHeight:number=48; virtualBuffer:number=200; editMode:'cell'|'row'='cell'; density:'compact'|'standard'|'comfortable'='standard'; // attrs row-height/virtual-buffer/edit-mode
|
|
24
|
+
columnFit:'scroll'|'squish'='scroll'; // attr column-fit
|
|
24
25
|
lazyLoadThreshold:number=200; // lazy-load-threshold, px
|
|
25
26
|
// <snice-column>; camelCase properties reflect to matching kebab-case attributes unless marked JS-only
|
|
26
27
|
key:string=''; label:string=''; type:ColumnType='text'; align:'left'|'center'|'right'='left'; width:string=''; aggregate?:Aggregator; // custom function JS-only
|
|
@@ -37,7 +38,7 @@ interface CellProps {value:any;align:'left'|'center'|'right';type:string;column:
|
|
|
37
38
|
class TextCell {multiline:boolean=false;maxLines?:number} // max-lines
|
|
38
39
|
class NumberCell {decimals:number=0;thousandsSeparator:boolean=false;prefix:string='';suffix:string='';negativeStyle:'parentheses'|'red'|'minus'='minus';highlight:boolean=false} // thousands-separator, negative-style
|
|
39
40
|
class CurrencyCell {decimals:number=2;thousandsSeparator:boolean=true;currency:string='USD';currencyDisplay:'symbol'|'code'|'name'='symbol';locale:string='en-US';negativeStyle:'parentheses'|'red'|'minus'='red';highlight:boolean=false} // thousands-separator, currencydisplay, negative-style
|
|
40
|
-
class DateCell {dateFormat:'short'|'medium'|'long'|'full'|'custom'='short';customFormat?:string;locale:string='en-US';relativeTime:boolean=false;showTime:boolean=false} // date-format, custom-format, relative-time, show-time
|
|
41
|
+
class DateCell {dateFormat:'short'|'medium'|'long'|'full'|'custom'='short';customFormat?:string;locale:string='en-US';relativeTime:boolean=false;showTime:boolean=false} // date-format, custom-format, relative-time, show-time; date-only values ('2026-03-15','2026-03','2026') are calendar days read at LOCAL midnight — same day in every timezone, in display/export/filters; values with a time (…T10:00, …Z, epoch, Date) stay instants; a date-only filter bound spans the whole day, so before/onOrBefore/after/onOrAfter agree with `is` (onOrBefore '2026-03-15' keeps '2026-03-15T09:00'); impossible days ('2026-02-30','2026-13-01') are rejected, not rolled over, and render as the raw value
|
|
41
42
|
class BooleanCell {trueValue:string='true';falseValue:string='false';useSymbols:boolean=true;trueSymbol:string='svg';falseSymbol:string='svg'} // true-value, false-value, use-symbols, true-symbol, false-symbol
|
|
42
43
|
class PercentageCell {decimals:number=2;showTrend:boolean=false;trendValue:number|null=null;colorize:boolean=true} // attrs showtrend/trendvalue (no hyphens)
|
|
43
44
|
class SparklineCell {chartType:'line'|'bar'|'area'='line';color:string='var(--snice-color-primary)';width:number=80;height:number=24;showDots:boolean=false;showBaseline:boolean=false;strokeWidth:number=1.5;minValue?:number;maxValue?:number;data:number[]=[]} // assign data in JS; color/width/height attrs unchanged; other camel names use explicit kebab attrs
|
|
@@ -63,46 +64,45 @@ interface ColumnDefinition {
|
|
|
63
64
|
}
|
|
64
65
|
type Aggregator='sum'|'avg'|'min'|'max'|'count'|((values:any[],rows:any[])=>any);
|
|
65
66
|
type NumberFormat={decimals?:number;thousandsSeparator?:boolean;prefix?:string;suffix?:string;negativeStyle?:'parentheses'|'red'|'minus'}; type DateFormat={format?:'short'|'medium'|'long'|'full'|'custom';customFormat?:string;locale?:string};
|
|
66
|
-
type BooleanFormat={trueValue?:string;falseValue?:string;useSymbols?:boolean;trueSymbol?:string;falseSymbol?:string}; type RatingFormat={max?:number;symbol?:string;emptySymbol?:string;color?:string};
|
|
67
|
-
type
|
|
68
|
-
type
|
|
69
|
-
type
|
|
70
|
-
type ActionsFormat={actions:ActionButton[]}; type ActionButton={action:string;label?:string;icon?:string;variant?:'primary'|'secondary'|'danger'|'success';title?:string;disabled?:boolean};
|
|
71
|
-
type LinkFormat={href?:string;target?:string;external?:boolean;icon?:string;text?:string}; type EmailFormat={email?:string;showIcon?:boolean;displayText?:string};
|
|
72
|
-
type ColorFormat={color?:string;size?:'small'|'medium'|'large';displayFormat?:'hex'|'rgb'|'hsl'|'name';showSwatch?:boolean;showHex?:boolean;showRgb?:boolean;swatchSize?:'small'|'medium'|'large'};
|
|
73
|
-
type CurrencyFormat={currency?:string;locale?:string;display?:'symbol'|'code'|'name';currencyDisplay?:'symbol'|'code'|'name';decimals?:number;thousandsSeparator?:boolean;negativeStyle?:'parentheses'|'red'|'minus'};
|
|
67
|
+
type BooleanFormat={trueValue?:string;falseValue?:string;useSymbols?:boolean;trueSymbol?:string;falseSymbol?:string}; type RatingFormat={max?:number;symbol?:string;emptySymbol?:string;color?:string}; type ProgressFormat={max?:number;showPercentage?:boolean;color?:string;colorize?:boolean;backgroundColor?:string;height?:string}; type SparklineFormat={type?:'line'|'bar'|'area';color?:string;width?:number;height?:number;showDots?:boolean;showBaseline?:boolean;strokeWidth?:number;minValue?:number;maxValue?:number};
|
|
68
|
+
type PercentageFormat={decimals?:number;showTrend?:boolean;trendValue?:number|null;colorize?:boolean}; type PhoneFormat={phone?:string;displayText?:string;showIcon?:boolean;format?:boolean;country?:string}; type StatusFormat={status?:string;label?:string;showDot?:boolean;variant?:'online'|'offline'|'busy'|'away'|'custom'}; type TagFormat={variant?:string};
|
|
69
|
+
type ActionsFormat={actions:ActionButton[]}; type ActionButton={action:string;label?:string;icon?:string;variant?:'primary'|'secondary'|'danger'|'success';title?:string;disabled?:boolean}; type LinkFormat={href?:string;target?:string;external?:boolean;icon?:string;text?:string}; type EmailFormat={email?:string;showIcon?:boolean;displayText?:string};
|
|
70
|
+
type ColorFormat={color?:string;size?:'small'|'medium'|'large';displayFormat?:'hex'|'rgb'|'hsl'|'name';showSwatch?:boolean;showHex?:boolean;showRgb?:boolean;swatchSize?:'small'|'medium'|'large'}; type CurrencyFormat={currency?:string;locale?:string;display?:'symbol'|'code'|'name';currencyDisplay?:'symbol'|'code'|'name';decimals?:number;thousandsSeparator?:boolean;negativeStyle?:'parentheses'|'red'|'minus'};
|
|
74
71
|
type ImageFormat={src?:string;fallback?:string;shape?:'rounded'|'square'|'circle';variant?:'rounded'|'square'|'circle';size?:'small'|'medium'|'large';alt?:string;lazy?:boolean}; type JsonFormat={maxDepth?:number;expanded?:boolean;collapsed?:boolean;showToggle?:boolean};
|
|
75
72
|
type LocationFormat={address?:string;latitude?:string|number;longitude?:string|number;showMapLink?:boolean;mapProvider?:'google'|'openstreetmap'|'apple';showIcon?:boolean;lat?:number;lng?:number};
|
|
76
73
|
type CellStyle={backgroundColor?:string;color?:string;fontWeight?:'normal'|'bold'|'lighter';fontStyle?:'normal'|'italic';fontSize?:string;textDecoration?:'none'|'underline'|'line-through'}; type ConditionalFormat={condition:(value:any,row?:any)=>boolean;style?:CellStyle;className?:string};
|
|
77
74
|
```
|
|
78
75
|
|
|
79
|
-
- `table.columns =`/`table.data =` rerender; `setData()` is non-eager, so call `renderBody()` when unpaired.
|
|
76
|
+
- `table.columns =`/`table.data =` rerender on EVERY assignment — identity is not consulted, so re-assigning the SAME array reference after mutating it in place (`rows.push(r); table.data = rows`) rerenders. Tradeoff: a truly redundant assignment costs a render (coalesced per microtask, so a burst still paints once). `setData()` is non-eager, so call `renderBody()` when unpaired. A `columns` assignment is a NEW CONFIGURATION, not a patch: the assigned array owns painted order, `pinned`, `width` and the column set (undeclared keys are dropped from the model; a re-declared key returns visible), which also discards user column state (drag-resize, manual reorder, `setColumnVisible()` hides) for the columns it re-declares — on EVERY assignment including a same-reference one, so mutating the published definitions is NOT an escape hatch; keep user column state by not re-assigning `columns` and using `setColumnVisible()`/`moveColumn()`/`pinColumn()`/`unpinColumn()` instead. A delivery under a non-empty `currentSort` renders sorted (local; remote keeps the server order). Rows recycle by identity but repaint when their own field values change, so an in-place patch + re-delivery updates that row — shallow compare, so a mutation inside a nested object value needs a new row object.
|
|
80
77
|
- `searchable` filters locally or requests remotely; `quickFilter` shows a model-backed input; `filterable` is the legacy remote selector.
|
|
81
|
-
- Currency uses `snice-cell-currency`. Formatter/valueFormatter, tooltip, base/conditional styles, all declared format aliases, object progress/JSON/sparkline values, sparkline baselines, and already-percent semantics work across Table/declarative/standalone paths.
|
|
82
|
-
- `
|
|
83
|
-
-
|
|
78
|
+
- Currency uses `snice-cell-currency`. Formatter/valueFormatter, tooltip, base/conditional styles, all declared format aliases, object progress/JSON/sparkline values, sparkline baselines, and already-percent semantics work across Table/declarative/standalone paths. Display pipeline: `valueGetter` -> `formatter` (wins) -> `valueFormatter` (fallback), on every path — cells, aggregate footers, `<snice-row>`, CSV/clipboard export; a column declaring either formatter renders through `snice-cell-text` (keeping its type's alignment) and the cell's `value` property/attribute IS the display value — except the tree group column, whose resolved label is plain text beside the indent/chevron (no typed cell, so no `value` attribute). An empty row value (null, or a field the row never carried) falls back to the typed cell's own empty value — `false` boolean, `0` rating/progress/duration/filesize, `null` JSON, `''` otherwise — so an absent boolean never reads as `true` and an absent progress never reads as `{}` (table-rendered cells; a declarative `<snice-row>` hands the cell the empty value unchanged).
|
|
79
|
+
- `virtualize` windows the body but keeps every zero-row state: loading spinner, `⚠️` load error, and the `empty-state` slot clone (or the default "No data" placeholder) all render on the virtual path too.
|
|
80
|
+
- Zero-row `loading` renders `<div class="table-loading-state" role="status" aria-live="polite">` = medium `snice-progress` + a `.table-loading-label` reading `Loading…`. With rows present the `loading-overlay` part replaces it and carries `role="status"`/`aria-label="Loading…"`. Both raise `--progress-track-opacity` so the track reads on dark surfaces.
|
|
81
|
+
- `type:'accounting'` = currency notation: symbol + grouping + parenthesised negatives, positives padded with one NBSP for column alignment. Defaults 2 decimals / grouping on / USD / en-US. `numberFormat.decimals|thousandsSeparator` and `currencyFormat.currency|locale|currencyDisplay` override; `numberFormat.prefix|suffix` replaces the symbol. `-12840.5` -> `($12,840.50)`. Generic `<snice-cell>` writes an inline `text-align` only when `align` is declared, so an undeclared cell keeps its TYPE's alignment from snice-cell.css (numeric types right, boolean/rating/sparkline centre).
|
|
82
|
+
- `rowReorder`, `columnReorder`, `lazyLoad`, and its threshold are post-mount reactive. List renderers execute in list mode. An ACTIVE `listRenderer` (needs `list` too) also suppresses the columnar header — column-group row, per-column `<th>`, and header-filter row all drop, since a card row has no column geometry; tool-column headers stay, so `selectable` keeps select-all. Clearing `listRenderer` or `list` restores the header; `list` alone (no renderer) keeps columnar cells AND its header. With no header to click, list-mode sorting runs through the toolbar (`setToolbar({showSort:true})`) or `toggleSort()`/`currentSort`. Density emits `density-change`. `selectable` and `selectionMode` are post-mount reactive too: either one rebuilds header + body, so the checkbox tool column appears/disappears in both.
|
|
83
|
+
- Selection is anchored to the row, not the position: a local sort or a re-delivery of the same row objects in another order re-resolves `selectedRows`, so `getSelectedData()` and the highlight stay on the same rows. A different row set keeps the raw-index semantics.
|
|
84
|
+
- Limits: pagination shows "Showing 1–25 of 60" or numbered `aria-label="Page N"` (no "Page X of Y"); remote has a 150ms debounce, re-requests only on `currentPage`/`currentSort`/`pageSize` (the re-request supersedes any in-flight request WHEN THE CHANGE IS MADE, not when the debounce fires — a response landing inside the window is discarded, and a superseded failure is silent: only the newest request reports `table-load-error`), and keeps rows on a failed reload (⚠️ row + empty-state only with no data). The error state (⚠️ row + `table--error` class) describes the last delivery ATTEMPT and clears on ANY arriving dataset — successful re-request, `setData()`, or a `table.data =` assignment (including `[]`, which then shows the empty state, not the stale error) — the assignment need NOT be a new array (same-reference clears it too), a drag row-reorder republishes the same rows and does NOT clear, and an already-in-flight request still reports its own failure when it settles. Local sort/search are client-side on `data` only. Client pagination re-clamps `currentPage` to the new last page when a filter or a shorter re-delivery leaves it past the end (an empty set keeps the declared page), so the body always shows the rows the summary claims.
|
|
84
85
|
|
|
85
86
|
## Methods
|
|
86
87
|
|
|
87
88
|
- `getTableConfig()` - Request/apply controller config; `getTableData()` - request/apply current rows; `setData(data)` - non-eager bulk load; `setColumns(columns)` - assign and schedule both renders
|
|
88
89
|
- `renderControls()` - Render legacy controls; `renderHeader()` - render headers/tools/filters; `renderSortableHeader(column)` - return sortable-header HTML; `renderBody()` - render display model; `renderPagination()` - render pager
|
|
89
90
|
- `createCellElement(column,value,row?)` - Build rendered cell; `getCellTagName(type)` - resolve runtime cell tag
|
|
90
|
-
- `getSelectedData()` - Resolve raw selected indices; `setSelectabilityCheck(fn)` - install row predicate; `updateRowSelectionState()` - sync rows; `updateSelectAllState()` - sync select-all
|
|
91
|
-
- `toggleSort(key,multi?)` - Cycle direction; `setSortComparator(key,fn)` - install comparator; `goToPage(page)` - clamp/navigate; `setPageSize(size)` - resize/reset page
|
|
91
|
+
- `getSelectedData()` - Resolve raw selected indices; `setSelectabilityCheck(fn)` - install row predicate; `updateRowSelectionState()` - sync rows (keyed by each row's data index, so group/aggregate/spacer/filler rows are never marked selected); `updateSelectAllState()` - sync select-all (also runs on every body render, so a new row set re-derives checked/indeterminate)
|
|
92
|
+
- `toggleSort(key,multi?)` - Cycle direction; `setSortComparator(key,fn)` - install comparator (receives valueGetter-derived values); `goToPage(page)` - clamp/navigate; `setPageSize(size)` - resize/reset page
|
|
92
93
|
- `setColumnFilter(column,operator,value)` - Set one filter; `removeColumnFilter(column)` - remove it; `setQuickFilter(text)` - global search; `setFilterModel(model)` - replace model; `getFilterModel()` - read model; `clearAllFilters()` - clear all
|
|
93
94
|
- Filter operators: text `contains/notContains/equals/notEquals/startsWith/endsWith/isEmpty/isNotEmpty`; number `eq/neq/gt/gte/lt/lte/isEmpty/isNotEmpty`; date `is/isNot/before/onOrBefore/after/onOrAfter/isEmpty/isNotEmpty`; boolean `isTrue/isFalse`
|
|
94
95
|
- `setColumnVisible(key,visible)` - Set visibility; `showAllColumns()` - show all; `hideAllColumns()` - hide hideable; `getColumnVisibility()` - read map; `pinColumn(key,side)` - pin; `unpinColumn(key)` - unpin
|
|
95
|
-
- `autoSizeColumn(key)` -
|
|
96
|
-
- `scrollToRow(index)` - Reveal data row; `scrollToColumn(key)` - reveal header; `getScrollPosition()` - read `{top,left}`; `setRowHeight(px)` - set fixed height; `setRowHeightCallback(fn)` - set rendered/virtual height
|
|
96
|
+
- `autoSizeColumn(key)` - Fit one column to its content; `autoSizeAllColumns()` - fit all. Both measure the rendered header + body under content-driven layout, so they SHRINK as well as grow, and repaint header AND body with the measured width. `moveColumn(key,index)` - move; `setColumnGroups(groups)` - add grouped headers
|
|
97
|
+
- `scrollToRow(index)` - Reveal data row (recomputes the virtual window in the same call — a programmatic scroll fires no `scroll` event, so the row is in the DOM on return); `scrollToColumn(key)` - reveal header; `getScrollPosition()` - read `{top,left}`; `setRowHeight(px)` - set fixed height; `setRowHeightCallback(fn)` - set rendered/virtual height
|
|
97
98
|
- `startEdit(row,key)` - Start cell/row edit; `commitEdit()` - validate/commit; `cancelEdit()` - cancel; `setCellEditableCheck(fn)` - install predicate. Parsers/setters receive the real row; setters may return a value or row
|
|
98
99
|
- `exportCSV(options?)` - Download filtered raw data; `printTable(options?)` - print visible text with toolbar/footer/checkbox options; `copyToClipboard(options?)` - copy formatted (default) or raw filtered data; `toggleFullscreen()` - toggle native/fallback fullscreen
|
|
99
|
-
- `setToolbar(options)` - Install search/sort/filter/export/fullscreen UI; `setListViewRenderer(fn)` - render full-width custom list rows
|
|
100
|
+
- `setToolbar(options)` - Install search/sort/filter/export/fullscreen UI, `{showSearch?,showSort?,showFilter?,showExport?,searchPlaceholder?}`; fullscreen always appears; sort/filter buttons edit the header/menu models; `setListViewRenderer(fn)` - render full-width custom list rows
|
|
100
101
|
- `setDetailPanel(options)` - Configure content/height/lazy lifecycle/icons; `expandRow(i)` - expand; `collapseRow(i)` - collapse; `toggleRowExpansion(i)` - toggle; `expandAllRows()` - expand all; `collapseAllRows()` - collapse all
|
|
101
|
-
- `setTreeData(options)` - Configure path hierarchy; `expandTreeNode(key)` - expand; `collapseTreeNode(key)` - collapse; `toggleTreeNode(key)` - toggle; `expandAllTreeNodes()` - expand all; `collapseAllTreeNodes()` - collapse all
|
|
102
|
+
- `setTreeData(options)` - Configure path hierarchy; `expandTreeNode(key)` - expand; `collapseTreeNode(key)` - collapse; `toggleTreeNode(key)` - toggle; `expandAllTreeNodes()` - expand all; `collapseAllTreeNodes()` - collapse all. Every skipped path level becomes a gap node (`['Eng','Team','Alice']` alone renders Eng+Team+Alice); `defaultExpansionDepth` seeds the STARTING expansion once, so a full collapse sticks; the group column runs the normal display pipeline (`renderCell` > `formatter` > `valueFormatter`) beside the indent/chevron; tree rows render detail panels when `setDetailPanel()` is configured (gap nodes, index `-1`, never do)
|
|
102
103
|
- `pinRowTop(row)` - Pin above; `pinRowBottom(row)` - pin below; `unpinRow(row)` - unpin same object; `clearPinnedRows()` - clear both areas
|
|
103
104
|
- `<snice-column>`: `setFormatter(fn)` - set formatter; `addConditionalFormat(rule)` - append; `removeConditionalFormat(i)` - remove; `clearConditionalFormats()` - clear; `getColumnDefinition()` - build definition
|
|
104
105
|
- `<snice-row>`: `select()` - select if allowed; `deselect()` - deselect; `focusRow()` - focus/scroll; `getCellValue(key)` - read; `setCellValue(key,value)` - update; `getCellElement(key)` - find cell; `updateCells()` - reconfigure cells; `highlight(ms?)` - temporary highlight
|
|
105
|
-
- Toolbar options: `{showSearch?,showSort?,showFilter?,showExport?,searchPlaceholder?}`; fullscreen always appears; sort/filter buttons edit the header/menu models.
|
|
106
106
|
- Remote `table/data` payload: `{search,sort,filter,selector,page?,pageSize?}`; response: `{data,totalItems?}`. Automatic initial request requires `mode="remote"`; page requests also require `pagination-mode="server"`.
|
|
107
107
|
- CSV/clipboard resolve raw selected identities before intersecting filters, so active filters cannot retarget selection.
|
|
108
108
|
|
|
@@ -121,16 +121,18 @@ type CellStyle={backgroundColor?:string;color?:string;fontWeight?:'normal'|'bold
|
|
|
121
121
|
|
|
122
122
|
## Slots
|
|
123
123
|
|
|
124
|
-
- `<snice-table>`: `columns`, `rows`, `header`, `empty-state` (cloned into the shadow body on EACH zero-row render — the slotted original is only a template); no default slot. `<snice-column>`: default inert content slot. Cells: no slots.
|
|
125
|
-
- CDN Table bundle includes column, row, and cell dependencies. Grouping/aggregation upgrades declarative rows to the native table model.
|
|
124
|
+
- `<snice-table>`: `columns`, `rows`, `header`, `empty-state` (cloned into the shadow body on EACH zero-row render, virtualized or not — the slotted original is only a template); no default slot. `<snice-column>`: default inert content slot. Cells: no slots. CDN Table bundle includes column, row, and cell dependencies. Grouping/aggregation upgrades declarative rows to the native table model.
|
|
126
125
|
|
|
127
126
|
## CSS Parts
|
|
128
127
|
|
|
129
|
-
- `<snice-table>`: `body`, `superheader`, `controls`, `toolbar`, `pagination`, `loading-overlay` (refetch spinner while `loading` with rows present), `row` (each native body row), `cell` (each native body cell), `filter-button` (column filter trigger), `sort-indicator` (sort direction); `<snice-row>`: `container`, `checkbox-cell`, `cell`; cells: `content`; actions: `action-button`; JSON: `toggle`; tag: `tag`; link/email/phone/location: `link`
|
|
128
|
+
- `<snice-table>`: `body`, `superheader`, `controls`, `toolbar`, `pagination`, `loading-overlay` (refetch spinner while `loading` with rows present), `row` (each native body row), `cell` (each native body cell — the inert filler that pads a body shorter than its host, virtualized or not, is neither and exposes no part), `filter-button` (column filter trigger), `sort-indicator` (sort direction); `<snice-row>`: `container`, `checkbox-cell`, `cell`; cells: `content`; actions: `action-button`; JSON: `toggle`; tag: `tag`; link/email/phone/location: `link`
|
|
129
|
+
- `column-fit` (default `scroll`) = the fitting policy. `scroll`: `minWidth` wins, `.table-frame` scrolls horizontally when the columns no longer fit. `squish`: the FRAME wins — `minWidth` relaxes to a 24px floor, every visible column shares the frame width, cell content AND header labels ellipsise (typed cells too: squish relaxes their 100px floor via `--snice-table-cell-min-width`, without which they clip mid-glyph), `.table-frame` gets `overflow-x:hidden`, and the rightmost column edge sits on the frame's inner edge at any width. Squish resize is allowed and REBALANCES: the dragged column keeps its width (capped so the rest can still sit on the floor) and the others scale to fill what is left. Declared widths are honoured while an unsized column can absorb the difference; when the authored widths alone overflow, every column is scaled by the same factor. Reactive — assigning `columnFit` re-fits the painted columns. Use squish for a table inside a card/tile/split pane; use scroll for a wide dataset (pinned columns only pay off when something scrolls).
|
|
130
|
+
- Column width: a column that declares no `width` SHARES the frame — grown or shrunk on every render and frame resize, floored at `minWidth` (default 50), after which the frame scrolls instead of collapsing. A declared `width`, a drag-resize, and an auto-size are authored widths and are never refitted. Pinned cells carry `pinned-cell` + `pinned-cell--left|right`; the inner-most column per side adds `pinned-cell--edge` (2px divider + shadow) and its header a `.pin-indicator` pin glyph, so pinning reads even when nothing scrolls.
|
|
131
|
+
- Height fill: the filler only consumes space the frame ALREADY has. A content-driven host (`:host{height:100%}` against an auto/max-content parent — stretched grid/flex item, plain block) would make the filler an input to its own resize trigger, so the table applies it, verifies the frame kept its height, and hands back any self-caused growth (partial filler, or none). Size the host (explicit height / flex or grid track / sized ancestor) to get a full-height grid.
|
|
130
132
|
|
|
131
133
|
## CSS Custom Properties
|
|
132
134
|
|
|
133
|
-
- `--snice-table-body-bg`, `--snice-table-group-header-bg`, `--snice-table-group-header-color`, `--snice-table-group-count-bg`, `--snice-table-group-count-color`, `--snice-table-aggregate-bg`, `--snice-table-aggregate-border-color`, `--snice-table-aggregate-color`, `--snice-table-aggregate-label-color`, `--snice-table-cell-padding`, `--snice-table-cell-border` (vertical; `none` drops grid lines), `--snice-table-row-border` (horizontal)
|
|
135
|
+
- `--snice-table-stripe-bg` (`striped` row tint; defaults to `--snice-color-overlay-stripe`, an alpha tint tuned per theme), `--snice-table-body-bg`, `--snice-table-group-header-bg`, `--snice-table-group-header-color`, `--snice-table-group-count-bg`, `--snice-table-group-count-color`, `--snice-table-aggregate-bg`, `--snice-table-aggregate-border-color`, `--snice-table-aggregate-color`, `--snice-table-aggregate-label-color`, `--snice-table-cell-padding`, `--snice-table-cell-border` (vertical; `none` drops grid lines), `--snice-table-row-border` (horizontal), `--snice-table-cell-min-width` (typed-cell width floor, default `6.25rem`; squish sets it to `0`)
|
|
134
136
|
|
|
135
137
|
## Basic Usage
|
|
136
138
|
|
|
@@ -141,10 +143,7 @@ table.data = employees;
|
|
|
141
143
|
table.setToolbar({showSearch:true,showSort:true,showFilter:true,showExport:true});
|
|
142
144
|
```
|
|
143
145
|
|
|
144
|
-
## Keyboard
|
|
146
|
+
## Keyboard & Accessibility
|
|
145
147
|
|
|
146
148
|
- Grid: arrows, Home/End, Page Up/Down; Enter edits; Space/Shift+Space toggles selection; Ctrl/Cmd+A selects filtered selectable rows. Group/tree/detail buttons: Tab then Enter/Space.
|
|
147
|
-
|
|
148
|
-
## Accessibility
|
|
149
|
-
|
|
150
|
-
- Native table uses ARIA grid roles, live counts, roving focus, `aria-sort`, and `aria-selected`; group/tree/detail controls expose labels/`aria-expanded`, and group selection supports mixed state.
|
|
149
|
+
- Native table uses ARIA grid roles, live counts, roving focus, `aria-sort`, and `aria-selected`; group/tree/detail controls expose labels/`aria-expanded`, and group selection supports mixed state. `aria-selected="true"` is written with `data-selected` on every selection path, including click/checkbox/select-all deltas — not only on a full body render.
|
|
@@ -56,6 +56,7 @@ CSS-only design token system. No component -- just import the stylesheet.
|
|
|
56
56
|
--snice-color-overlay-hover /* hsl(0 0% 0% / 0.04) — hover tint over any surface */
|
|
57
57
|
--snice-color-overlay-selected /* hsl(primary / 0.08) — selected row/item tint */
|
|
58
58
|
--snice-color-overlay-selected-hover /* hsl(primary / 0.12) — selected + hovered */
|
|
59
|
+
--snice-color-overlay-stripe /* hsl(0 0% 0%/.02) light, hsl(0 0% 100%/.11) dark — zebra row tint; dark needs the bigger step */
|
|
59
60
|
```
|
|
60
61
|
|
|
61
62
|
### Spacing
|
package/docs/ai/events.md
CHANGED
|
@@ -317,6 +317,36 @@ cancels an older timer and dispatches the new result immediately. Throttle
|
|
|
317
317
|
trailing detail is latest-wins; its deadline is last actual dispatch + the
|
|
318
318
|
newly resolved interval.
|
|
319
319
|
|
|
320
|
+
### cancelable — suppressible default actions
|
|
321
|
+
|
|
322
|
+
`cancelable` is an `EventInit` field, so `@dispatch('x', { cancelable: true })`
|
|
323
|
+
DOES produce a cancelable event and listeners can call `preventDefault()`.
|
|
324
|
+
|
|
325
|
+
But the decorated method returns the **detail**, not `dispatchEvent`'s boolean,
|
|
326
|
+
so the component cannot see whether the event was cancelled. A component with a
|
|
327
|
+
default action to suppress must dispatch by hand and read the result:
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
// ❌ Cancelable, but the component can never act on the cancellation
|
|
331
|
+
@dispatch('more-click', { cancelable: true })
|
|
332
|
+
private emitMore(date: Date) { return { date }; }
|
|
333
|
+
|
|
334
|
+
// ✅ Default action the app can cancel
|
|
335
|
+
private emitMore(date: Date): boolean {
|
|
336
|
+
return this.dispatchEvent(new CustomEvent('more-click', {
|
|
337
|
+
detail: { date }, bubbles: true, composed: true, cancelable: true,
|
|
338
|
+
}));
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private onChipClick(date: Date) {
|
|
342
|
+
if (!this.emitMore(date)) return; // preventDefault() -> app owns it
|
|
343
|
+
this.openBuiltInPanel(date);
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
In the library: `snice-link`'s `navigate`, `snice-stepper`'s `step-change`,
|
|
348
|
+
`snice-calendar`'s `calendar-more-click`.
|
|
349
|
+
|
|
320
350
|
### scope — dispatch target
|
|
321
351
|
|
|
322
352
|
Default: `this.dispatchEvent(event)` — event originates from the host element. `scope` redirects the dispatch to another target so the event behaves as if it originated there. Use with `@on({ scope })` to express cross-cutting events without bubbling.
|
package/docs/ai/theme.md
CHANGED
|
@@ -58,6 +58,7 @@ Modern syntax with alpha: `hsl(0 0% 0% / 0.15)` (not `hsla(0, 0%, 0%, 0.15)`)
|
|
|
58
58
|
--snice-color-overlay-hover: hsl(0 0% 0% / 0.04);
|
|
59
59
|
--snice-color-overlay-selected: hsl(var(--snice-color-blue-500) / 0.08);
|
|
60
60
|
--snice-color-overlay-selected-hover: hsl(var(--snice-color-blue-500) / 0.12);
|
|
61
|
+
--snice-color-overlay-stripe: hsl(0 0% 0% / 0.02); /* dark: hsl(0 0% 100% / 0.11) */
|
|
61
62
|
```
|
|
62
63
|
|
|
63
64
|
## Layers (z-index)
|