@svgrid/grid 2.2.33 → 2.4.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 +199 -199
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +178 -139
- package/dist/GridMenus.svelte +705 -705
- package/dist/SvAutoComplete.svelte +165 -165
- package/dist/SvAvatar.svelte +75 -75
- package/dist/SvCalendar.svelte +503 -503
- package/dist/SvCarousel.svelte +141 -141
- package/dist/SvCheckBox.svelte +102 -102
- package/dist/SvCircularProgress.svelte +109 -109
- package/dist/SvColorInput.svelte +181 -181
- package/dist/SvComboBox.svelte +275 -275
- package/dist/SvContextMenu.svelte +116 -116
- package/dist/SvCountryInput.svelte +160 -160
- package/dist/SvDrawer.svelte +254 -254
- package/dist/SvDropDownList.svelte +374 -374
- package/dist/SvDurationInput.svelte +126 -126
- package/dist/SvField.svelte +293 -293
- package/dist/SvForm.svelte +437 -437
- package/dist/SvGrid.css +2814 -2804
- package/dist/SvGrid.svelte +3490 -3490
- package/dist/SvGridChart.svelte +1716 -1716
- package/dist/SvGridChartPanel.svelte +485 -485
- package/dist/SvGridChartView.svelte +70 -70
- package/dist/SvGridDropdown.svelte +728 -728
- package/dist/SvGridSelect.svelte +266 -266
- package/dist/SvGroupCell.svelte +116 -116
- package/dist/SvListBox.svelte +326 -326
- package/dist/SvMaskedInput.svelte +122 -122
- package/dist/SvMenu.svelte +124 -124
- package/dist/SvMenuList.svelte +146 -146
- package/dist/SvMultiSelect.svelte +289 -289
- package/dist/SvNumberInput.svelte +168 -168
- package/dist/SvOtpInput.svelte +158 -158
- package/dist/SvPasswordInput.svelte +151 -151
- package/dist/SvPhoneInput.svelte +133 -133
- package/dist/SvPopover.svelte +197 -197
- package/dist/SvProgress.svelte +116 -116
- package/dist/SvRadioGroup.svelte +107 -107
- package/dist/SvRating.svelte +112 -112
- package/dist/SvResult.svelte +73 -73
- package/dist/SvRichText.svelte +211 -211
- package/dist/SvRowGroupPanel.svelte +170 -170
- package/dist/SvScrollArea.svelte +61 -61
- package/dist/SvSlider.svelte +200 -200
- package/dist/SvSwitchButton.svelte +108 -108
- package/dist/SvTagsInput.svelte +112 -112
- package/dist/SvTextInput.svelte +147 -147
- package/dist/SvTimePicker.svelte +245 -245
- package/dist/SvToaster.svelte +159 -159
- package/dist/SvToggleButton.svelte +85 -85
- package/dist/SvTooltip.svelte +161 -161
- package/dist/SvTour.svelte +208 -208
- package/dist/SvTree.svelte +444 -444
- package/dist/SvTreeSelect.svelte +235 -235
- package/dist/cdn/{GridMenus-B7XhhqdN.js → GridMenus-BpVcKyUr.js} +4 -4
- package/dist/cdn/{GridMenus-BxmIvCJh.js → GridMenus-CeXL5IW4.js} +153 -150
- package/dist/cdn/{SvDateTimePicker-ClqtVIj3.js → SvDateTimePicker-DSUhIta8.js} +165 -165
- package/dist/cdn/{SvDateTimePicker-o94wfSUa.js → SvDateTimePicker-h9kPyszT.js} +572 -555
- package/dist/cdn/{SvGridChart-Cr_90pPD.js → SvGridChart-Bs2GIR2Q.js} +28 -21
- package/dist/cdn/{SvGridChart-BhnFdLd0.js → SvGridChart-CwhFz7GV.js} +2 -2
- package/dist/cdn/{SvGridChartPanel-Cmx9DHAS.js → SvGridChartPanel-1WXuStzM.js} +2 -2
- package/dist/cdn/{SvGridChartPanel-ICbxiUvK.js → SvGridChartPanel-e5_nqNP9.js} +20 -12
- package/dist/cdn/{SvGridChartView-Bx42PMvf.js → SvGridChartView-SmPW10dI.js} +3 -3
- package/dist/cdn/{SvGridChartView-B40DTJSX.js → SvGridChartView-eSPuJE6g.js} +2 -2
- package/dist/cdn/{SvGridDropdown-D0VdjeR8.js → SvGridDropdown-B1ZcLpgs.js} +1 -1
- package/dist/cdn/{SvGridDropdown-CUOdm7YA.js → SvGridDropdown-CtdbHcIS.js} +6 -5
- package/dist/cdn/{chart-T1usDN0o.js → chart-i4XcXyHJ.js} +12 -10
- package/dist/cdn/{disclose-version-DljhZohK.js → disclose-version-Dr7rEVv-.js} +989 -1081
- package/dist/cdn/{export-format-JT5pWVT8.js → export-format-Cv1Dll6k.js} +66 -245
- package/dist/cdn/{src-erjBRYfX.js → src-DbeN1ReZ.js} +9221 -8925
- package/dist/cdn/{src-ktbQVOk-.js → src-DmEQFW5_.js} +9021 -8809
- package/dist/cdn/svgrid.js +9 -9
- package/dist/cdn/svgrid.svelte-external.js +9 -9
- package/dist/chart-export.js +8 -8
- package/dist/columns.js +9 -2
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/_svelte_metadata.json +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/_metadata.json +178 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/attachments-qzRy9DX4.js +3843 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/attachments-qzRy9DX4.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/constants-RLNOXCpz.js +23 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/constants-RLNOXCpz.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/dev-DPd12uEU.js +1925 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/dev-DPd12uEU.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/events-CDC4DKuQ.js +206 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/events-CDC4DKuQ.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/flags-BD-lYX7s.js +20 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/flags-BD-lYX7s.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/hydration-D3UBU2t8.js +264 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/hydration-D3UBU2t8.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/legacy-client-B0ZUlbSQ.js +698 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/legacy-client-B0ZUlbSQ.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/package.json +3 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte.js +135 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_animate.js +57 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_animate.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_attachments.js +2 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_compiler.js +46691 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_compiler.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_easing.js +239 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_easing.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_events.js +2 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal.js +6 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_client.js +7 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_disclose-version.js +6 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_disclose-version.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_async.js +7 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_async.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_legacy.js +7 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_legacy.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_tracing.js +7 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_tracing.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_server.js +6 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_legacy.js +85 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_legacy.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_motion.js +561 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_motion.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity.js +26 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity_window.js +124 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity_window.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_server.js +2 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_store.js +91 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_store.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_transition.js +242 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_transition.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-lObbumRX.js +497 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-lObbumRX.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-mR9mAWyK.js +4929 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-mR9mAWyK.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/validate-CezNmRkM.js +192 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/validate-CezNmRkM.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/version-BHHo0WE-.js +10 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/version-BHHo0WE-.js.map +1 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/warnings-yiExAB06.js +140 -0
- package/dist/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/warnings-yiExAB06.js.map +1 -0
- package/package.json +11 -11
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +178 -139
- package/src/GridMenus.svelte +705 -705
- package/src/SvAutoComplete.svelte +165 -165
- package/src/SvAvatar.svelte +75 -75
- package/src/SvCalendar.svelte +503 -503
- package/src/SvCalendar.test.ts +226 -226
- package/src/SvCarousel.svelte +141 -141
- package/src/SvCheckBox.svelte +102 -102
- package/src/SvCircularProgress.svelte +109 -109
- package/src/SvColorInput.svelte +181 -181
- package/src/SvComboBox.svelte +275 -275
- package/src/SvContextMenu.svelte +116 -116
- package/src/SvCountryInput.svelte +160 -160
- package/src/SvDrawer.svelte +254 -254
- package/src/SvDropDownList.svelte +374 -374
- package/src/SvDurationInput.svelte +126 -126
- package/src/SvField.svelte +293 -293
- package/src/SvForm.svelte +437 -437
- package/src/SvForm.test.ts +411 -411
- package/src/SvGrid.controller.svelte.ts +3680 -3680
- package/src/SvGrid.css +2814 -2804
- package/src/SvGrid.svelte +3490 -3490
- package/src/SvGrid.types.ts +1708 -1708
- package/src/SvGridChart.svelte +1716 -1716
- package/src/SvGridChartPanel.svelte +485 -485
- package/src/SvGridChartView.svelte +70 -70
- package/src/SvGridDropdown.svelte +728 -728
- package/src/SvGridSelect.svelte +266 -266
- package/src/SvGroupCell.svelte +116 -116
- package/src/SvListBox.svelte +326 -326
- package/src/SvMaskedInput.svelte +122 -122
- package/src/SvMenu.svelte +124 -124
- package/src/SvMenu.test.ts +97 -97
- package/src/SvMenuList.svelte +146 -146
- package/src/SvMultiSelect.svelte +289 -289
- package/src/SvNumberInput.svelte +168 -168
- package/src/SvOtpInput.svelte +158 -158
- package/src/SvPasswordInput.svelte +151 -151
- package/src/SvPhoneInput.svelte +133 -133
- package/src/SvPopover.svelte +197 -197
- package/src/SvProgress.svelte +116 -116
- package/src/SvRadioGroup.svelte +107 -107
- package/src/SvRating.svelte +112 -112
- package/src/SvResult.svelte +73 -73
- package/src/SvRichText.svelte +211 -211
- package/src/SvRowGroupPanel.svelte +170 -170
- package/src/SvScrollArea.svelte +61 -61
- package/src/SvSlider.svelte +200 -200
- package/src/SvSwitchButton.svelte +108 -108
- package/src/SvTagsInput.svelte +112 -112
- package/src/SvTextInput.svelte +147 -147
- package/src/SvTimePicker.svelte +245 -245
- package/src/SvToaster.svelte +159 -159
- package/src/SvToaster.test.ts +95 -95
- package/src/SvToggleButton.svelte +85 -85
- package/src/SvTooltip.svelte +161 -161
- package/src/SvTour.svelte +208 -208
- package/src/SvTree.svelte +444 -444
- package/src/SvTreeSelect.svelte +235 -235
- package/src/a11y/dismissable.test.ts +119 -119
- package/src/a11y/dismissable.ts +114 -114
- package/src/a11y.contract.test.ts +49 -49
- package/src/a11y.test.ts +59 -59
- package/src/a11y.ts +61 -61
- package/src/ai.test.ts +502 -502
- package/src/ai.ts +1391 -1391
- package/src/build-api.coverage.test.ts +633 -633
- package/src/build-api.ts +805 -805
- package/src/builtin-editors.grid.test.ts +85 -85
- package/src/cell-formatting.ts +169 -169
- package/src/cell-render.test.ts +513 -513
- package/src/cell-render.ts +496 -496
- package/src/cell-values.ts +118 -118
- package/src/chart-export.ts +201 -201
- package/src/chart-view.svelte.ts +36 -36
- package/src/chart.ts +2295 -2295
- package/src/collaboration.test.ts +104 -104
- package/src/collaboration.ts +167 -167
- package/src/columns.ts +7 -2
- package/src/core.performance.test.ts +30 -30
- package/src/core.ts +1261 -1261
- package/src/createAutocomplete.svelte.ts +132 -132
- package/src/createCombobox.svelte.ts +191 -191
- package/src/createCountryInput.svelte.ts +157 -157
- package/src/createDropdownList.svelte.ts +168 -168
- package/src/createForm.svelte.ts +386 -386
- package/src/createGrid.svelte.ts +42 -42
- package/src/createGrid.test.ts +10 -10
- package/src/createGridState.svelte.ts +17 -17
- package/src/createListbox.svelte.ts +250 -250
- package/src/createMenu.svelte.ts +224 -224
- package/src/createPopoverSelect.svelte.ts +213 -213
- package/src/createSlider.svelte.ts +191 -191
- package/src/createTooltip.svelte.ts +144 -144
- package/src/createTree.svelte.ts +319 -319
- package/src/editing.test.ts +949 -949
- package/src/editing.ts +606 -606
- package/src/editor-contract.ts +164 -164
- package/src/editor-registry.grid.test.ts +144 -144
- package/src/editor-registry.ts +122 -122
- package/src/export-data-api.test.ts +126 -126
- package/src/export-format.test.ts +107 -107
- package/src/export-format.ts +601 -601
- package/src/filter-operators.ts +157 -157
- package/src/flex-render.ts +3 -3
- package/src/form-field.ts +127 -127
- package/src/group-display.test.ts +167 -167
- package/src/group-display.ts +200 -200
- package/src/index.ts +724 -724
- package/src/js-scroller.svelte.ts +173 -173
- package/src/keyboard-handlers.ts +270 -270
- package/src/keyboard.test.ts +59 -59
- package/src/keyboard.ts +97 -97
- package/src/list-nav.test.ts +49 -49
- package/src/list-nav.ts +29 -29
- package/src/list-option.test.ts +56 -56
- package/src/list-option.ts +172 -172
- package/src/menus.ts +567 -567
- package/src/merge-objects.ts +48 -48
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/_svelte_metadata.json +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/_metadata.json +178 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/attachments-qzRy9DX4.js +3843 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/attachments-qzRy9DX4.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/constants-RLNOXCpz.js +23 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/constants-RLNOXCpz.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/dev-DPd12uEU.js +1925 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/dev-DPd12uEU.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/events-CDC4DKuQ.js +206 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/events-CDC4DKuQ.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/flags-BD-lYX7s.js +20 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/flags-BD-lYX7s.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/hydration-D3UBU2t8.js +264 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/hydration-D3UBU2t8.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/legacy-client-B0ZUlbSQ.js +698 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/legacy-client-B0ZUlbSQ.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/package.json +3 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte.js +135 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_animate.js +57 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_animate.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_attachments.js +2 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_compiler.js +46691 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_compiler.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_easing.js +239 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_easing.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_events.js +2 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal.js +6 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_client.js +7 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_disclose-version.js +6 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_disclose-version.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_async.js +7 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_async.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_legacy.js +7 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_legacy.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_tracing.js +7 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_flags_tracing.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_internal_server.js +6 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_legacy.js +85 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_legacy.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_motion.js +561 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_motion.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity.js +26 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity_window.js +124 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_reactivity_window.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_server.js +2 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_store.js +91 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_store.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_transition.js +242 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/svelte_transition.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-lObbumRX.js +497 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-lObbumRX.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-mR9mAWyK.js +4929 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/utils-mR9mAWyK.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/validate-CezNmRkM.js +192 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/validate-CezNmRkM.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/version-BHHo0WE-.js +10 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/version-BHHo0WE-.js.map +1 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/warnings-yiExAB06.js +140 -0
- package/src/node_modules/.vite/vitest/67d8ed22ed986c5e2e948f64d892f30be166cc36/deps___vitest__/warnings-yiExAB06.js.map +1 -0
- package/src/overlays.test.ts +90 -90
- package/src/render-component.ts +28 -28
- package/src/row-resize.ts +210 -210
- package/src/scheduler-model.test.ts +562 -562
- package/src/scheduler-model.ts +872 -872
- package/src/selection.test.ts +754 -754
- package/src/selection.ts +491 -491
- package/src/server-data-source.test.ts +289 -289
- package/src/server-data-source.ts +413 -413
- package/src/sparkline.test.ts +68 -68
- package/src/sparkline.ts +169 -169
- package/src/spreadsheet.test.ts +488 -488
- package/src/spreadsheet.ts +312 -312
- package/src/static-functions.ts +11 -11
- package/src/subscribe.ts +38 -38
- package/src/svgrid-wrapper.types.ts +526 -526
- package/src/svgrid.async-editor-options.test.ts +271 -271
- package/src/svgrid.auto-row-height.test.ts +204 -204
- package/src/svgrid.behavior.test.ts +705 -705
- package/src/svgrid.charting.test.ts +534 -534
- package/src/svgrid.comments-autocomplete.test.ts +123 -123
- package/src/svgrid.context-menu.test.ts +147 -147
- package/src/svgrid.features.test.ts +157 -157
- package/src/svgrid.filter-depth.test.ts +163 -163
- package/src/svgrid.filter-menu-listbox.svelte.test.ts +322 -322
- package/src/svgrid.filter-menu-scroll.test.ts +112 -112
- package/src/svgrid.footer-pagesize.svelte.test.ts +86 -0
- package/src/svgrid.grand-total.test.ts +188 -188
- package/src/svgrid.group-display-mode.test.ts +171 -171
- package/src/svgrid.group-footers.test.ts +121 -121
- package/src/svgrid.group-pagination.test.ts +153 -153
- package/src/svgrid.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.tree-data.test.ts +186 -186
- package/src/svgrid.wrapper.test.ts +40 -40
- package/src/svgriddropdown.async-panel.svelte.test.ts +195 -195
- package/src/test-setup.ts +62 -62
- package/src/themes/index.ts +257 -257
- package/src/toast-store.svelte.ts +246 -246
- package/src/toast-store.test.ts +147 -147
- package/src/tree-row-model.test.ts +168 -168
- package/src/ui-buttons.test.ts +144 -144
- package/src/ui-inputs.test.ts +118 -118
- package/src/ui-localization.test.ts +113 -113
- package/src/ui-range.test.ts +70 -70
- package/src/ui-selection.test.ts +155 -155
- package/src/ui-tier1.test.ts +142 -142
- package/src/virtual.test.ts +88 -88
- package/src/virtualization/column-virtualizer.test.ts +27 -27
- package/src/virtualization/column-virtualizer.ts +30 -30
- package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
- package/src/virtualization/types.ts +30 -30
- package/src/virtualization/virtualizer.test.ts +47 -47
- package/src/virtualization/virtualizer.ts +296 -296
package/src/SvCalendar.svelte
CHANGED
|
@@ -1,503 +1,503 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
// Public types now live with the headless core and are re-exported here so the
|
|
3
|
-
// component's module entry point (and @svgrid/grid) keeps the same surface.
|
|
4
|
-
export type { CalendarValue, CalendarPreset, CalendarAnimation, DisplayMode } from './createCalendar.svelte'
|
|
5
|
-
</script>
|
|
6
|
-
|
|
7
|
-
<script lang="ts">
|
|
8
|
-
/**
|
|
9
|
-
* SvCalendar - a themeable, accessible month/year/decade calendar. It is a thin
|
|
10
|
-
* styled renderer over the headless `createCalendar` core (the same split as
|
|
11
|
-
* `createSvGrid` / <SvGrid>): the core owns the reactive state + interaction +
|
|
12
|
-
* ARIA (built on the framework-free ./datetime engine), while this component
|
|
13
|
-
* keeps the render-only concerns - the WAAPI view-change animation, mouse-wheel
|
|
14
|
-
* navigation, and DOM refs. All visuals come from the grid's `--sg-*` tokens.
|
|
15
|
-
*
|
|
16
|
-
* Parity target: Smart `smart-calendar` (selectionModes, min/max, restricted /
|
|
17
|
-
* important dates, week numbers, firstDayOfWeek, drill navigation, keyboard).
|
|
18
|
-
*/
|
|
19
|
-
import { untrack, type Snippet } from 'svelte'
|
|
20
|
-
import SvField from './SvField.svelte'
|
|
21
|
-
import { nextEditorId, resolveMessages, type EditorDir, type EditorSize } from './editor-contract'
|
|
22
|
-
import {
|
|
23
|
-
createCalendar,
|
|
24
|
-
type CalendarValue,
|
|
25
|
-
type CalendarPreset,
|
|
26
|
-
type CalendarAnimation,
|
|
27
|
-
type CalendarDayState,
|
|
28
|
-
type DisplayMode,
|
|
29
|
-
} from './createCalendar.svelte'
|
|
30
|
-
import type { SelectionMode } from './datetime/date-selection'
|
|
31
|
-
import type { RestrictOptions } from './datetime/date-restrict'
|
|
32
|
-
import type { DateLike } from './datetime/date-core'
|
|
33
|
-
import type { RecurrenceRule } from './recurrence'
|
|
34
|
-
|
|
35
|
-
type NameFormat = 'narrow' | 'short' | 'long'
|
|
36
|
-
|
|
37
|
-
/** User-facing strings (localizable via `messages`). */
|
|
38
|
-
type CalendarMessages = { label: string; shortcuts: string; today: string; clear: string; prev: string; next: string }
|
|
39
|
-
const DEFAULT_MESSAGES: CalendarMessages = {
|
|
40
|
-
label: 'Calendar', shortcuts: 'Shortcuts', today: 'Today', clear: 'Clear', prev: 'Previous', next: 'Next',
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
type Props = {
|
|
44
|
-
/** Selected value(s). Single Date for one/zeroOrOne, array for multi modes. */
|
|
45
|
-
value?: CalendarValue
|
|
46
|
-
/** Fires with the full selected-day list on every change. */
|
|
47
|
-
onChange?: (dates: Date[]) => void
|
|
48
|
-
/** Fires when the visible month/year/decade page changes. */
|
|
49
|
-
onNavigate?: (viewDate: Date, displayMode: DisplayMode) => void
|
|
50
|
-
selectionMode?: SelectionMode
|
|
51
|
-
min?: DateLike | null
|
|
52
|
-
max?: DateLike | null
|
|
53
|
-
/** Non-selectable dates (list or predicate). */
|
|
54
|
-
restrictedDates?: RestrictOptions['restrictedDates']
|
|
55
|
-
/** Highlighted (but still selectable) dates. */
|
|
56
|
-
importantDates?: ReadonlyArray<DateLike> | ((d: Date) => boolean) | null
|
|
57
|
-
/** 0 = Sunday .. 6 = Saturday. */
|
|
58
|
-
firstDayOfWeek?: number
|
|
59
|
-
weeks?: number
|
|
60
|
-
weekNumbers?: boolean
|
|
61
|
-
/** Number of month panels side by side. */
|
|
62
|
-
months?: number
|
|
63
|
-
hideDayNames?: boolean
|
|
64
|
-
hideOtherMonthDays?: boolean
|
|
65
|
-
dayNameFormat?: NameFormat
|
|
66
|
-
monthNameFormat?: NameFormat
|
|
67
|
-
/** Show the Today / Clear footer. */
|
|
68
|
-
footer?: boolean
|
|
69
|
-
disabled?: boolean
|
|
70
|
-
readonly?: boolean
|
|
71
|
-
locale?: string
|
|
72
|
-
name?: string
|
|
73
|
-
/** Which drill level to open on. */
|
|
74
|
-
displayMode?: DisplayMode
|
|
75
|
-
/** Animate month/year navigation + drill (opt-in). `true` = 'slide'. This is
|
|
76
|
-
* an explicit opt-in, so it plays regardless of the OS "reduce motion"
|
|
77
|
-
* setting - pass 'none' (or gate it yourself) to suppress it. */
|
|
78
|
-
animate?: boolean | CalendarAnimation
|
|
79
|
-
/** Change the visible month/year/decade with the mouse wheel over the grid. */
|
|
80
|
-
wheelNavigation?: boolean
|
|
81
|
-
/** Per-day tooltip text (shown as the native title on the day button). */
|
|
82
|
-
dateTooltip?: (date: Date) => string | null | undefined
|
|
83
|
-
/** One-click shortcuts (e.g. Today, Last 7 days) shown in a side rail. */
|
|
84
|
-
presets?: ReadonlyArray<CalendarPreset>
|
|
85
|
-
/** Text direction (rtl mirrors layout + flips the nav arrows; auto inherits). */
|
|
86
|
-
dir?: EditorDir
|
|
87
|
-
/** Override the built-in strings (group/shortcut labels, footer buttons). */
|
|
88
|
-
messages?: Partial<CalendarMessages>
|
|
89
|
-
/** Repeat pattern(s): matching days get an indicator + `recurring` day-state. */
|
|
90
|
-
recurrence?: RecurrenceRule | ReadonlyArray<RecurrenceRule> | null
|
|
91
|
-
/** Render rich content inside each day cell (events, dots, badges). Receives
|
|
92
|
-
* the date + its day-state. Switches the month grid to a taller,
|
|
93
|
-
* top-aligned layout so there's room for the content. */
|
|
94
|
-
day?: Snippet<[Date, CalendarDayState]>
|
|
95
|
-
// Optional field chrome: when any of label/hint/error is set, the calendar is
|
|
96
|
-
// wrapped in <SvField> so it gains a label/hint/error line; otherwise it
|
|
97
|
-
// renders bare (as embedded inside SvDateTimePicker's popover).
|
|
98
|
-
label?: string
|
|
99
|
-
hint?: string
|
|
100
|
-
error?: string
|
|
101
|
-
required?: boolean
|
|
102
|
-
invalid?: boolean
|
|
103
|
-
size?: EditorSize
|
|
104
|
-
id?: string
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
let {
|
|
108
|
-
value = null,
|
|
109
|
-
onChange,
|
|
110
|
-
onNavigate,
|
|
111
|
-
selectionMode = 'one',
|
|
112
|
-
min = null,
|
|
113
|
-
max = null,
|
|
114
|
-
restrictedDates = null,
|
|
115
|
-
importantDates = null,
|
|
116
|
-
firstDayOfWeek = 0,
|
|
117
|
-
weeks = 6,
|
|
118
|
-
weekNumbers = false,
|
|
119
|
-
months = 1,
|
|
120
|
-
hideDayNames = false,
|
|
121
|
-
hideOtherMonthDays = false,
|
|
122
|
-
dayNameFormat = 'short',
|
|
123
|
-
monthNameFormat = 'long',
|
|
124
|
-
footer = false,
|
|
125
|
-
disabled = false,
|
|
126
|
-
readonly = false,
|
|
127
|
-
locale = typeof navigator !== 'undefined' ? navigator.language : 'en-US',
|
|
128
|
-
name,
|
|
129
|
-
displayMode = 'month',
|
|
130
|
-
animate = false,
|
|
131
|
-
wheelNavigation = false,
|
|
132
|
-
dateTooltip,
|
|
133
|
-
presets,
|
|
134
|
-
dir,
|
|
135
|
-
messages,
|
|
136
|
-
recurrence = null,
|
|
137
|
-
day,
|
|
138
|
-
label,
|
|
139
|
-
hint,
|
|
140
|
-
error,
|
|
141
|
-
required = false,
|
|
142
|
-
invalid = false,
|
|
143
|
-
id,
|
|
144
|
-
}: Props = $props()
|
|
145
|
-
|
|
146
|
-
const calAutoId = nextEditorId('sv-cal')
|
|
147
|
-
const uid = $derived(id ?? calAutoId)
|
|
148
|
-
const hasChrome = $derived(!!(label || hint || error))
|
|
149
|
-
const M = $derived(resolveMessages(DEFAULT_MESSAGES, messages))
|
|
150
|
-
const resolvedDir = $derived(dir === 'ltr' || dir === 'rtl' ? dir : undefined)
|
|
151
|
-
// With several month panels side by side, leading/trailing days of adjacent
|
|
152
|
-
// months overlap visually, so hide them by default when months > 1.
|
|
153
|
-
const hideOther = $derived(hideOtherMonthDays || months > 1)
|
|
154
|
-
|
|
155
|
-
// The headless core owns all reactive state, selection, navigation, keyboard
|
|
156
|
-
// and ARIA. Reactive inputs are passed as getters; callbacks as closures.
|
|
157
|
-
const cal = createCalendar({
|
|
158
|
-
value: () => value,
|
|
159
|
-
onChange: (d) => onChange?.(d),
|
|
160
|
-
onNavigate: (v, m) => onNavigate?.(v, m),
|
|
161
|
-
selectionMode: () => selectionMode,
|
|
162
|
-
min: () => min,
|
|
163
|
-
max: () => max,
|
|
164
|
-
restrictedDates: () => restrictedDates,
|
|
165
|
-
importantDates: () => importantDates,
|
|
166
|
-
firstDayOfWeek: () => firstDayOfWeek,
|
|
167
|
-
weeks: () => weeks,
|
|
168
|
-
months: () => months,
|
|
169
|
-
displayMode: () => displayMode,
|
|
170
|
-
disabled: () => disabled,
|
|
171
|
-
readonly: () => readonly,
|
|
172
|
-
locale: () => locale,
|
|
173
|
-
dayNameFormat: () => dayNameFormat,
|
|
174
|
-
monthNameFormat: () => monthNameFormat,
|
|
175
|
-
dateTooltip: () => dateTooltip,
|
|
176
|
-
recurrence: () => recurrence,
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
// --- View-change animation (framework-native Web Animations API) -----------
|
|
180
|
-
// A render concern kept out of the core: we animate a wrapper element instead
|
|
181
|
-
// of re-keying the grid, so day cells stay mounted (keyboard focus +
|
|
182
|
-
// interactivity are preserved). The core exposes `navToken` (bumps on each
|
|
183
|
-
// navigation) + `navDir` (why it changed); an effect replays a short keyframe
|
|
184
|
-
// on the new content.
|
|
185
|
-
const animKind = $derived<CalendarAnimation>(animate === true ? 'slide' : animate || 'none')
|
|
186
|
-
let animEl = $state<HTMLElement | undefined>()
|
|
187
|
-
let animPrimed = false // skip the very first (mount) run
|
|
188
|
-
// Note: `animate` is an explicit opt-in, so we honor it even under the OS
|
|
189
|
-
// "reduce motion" setting. Pass animate="none" (or gate it yourself on
|
|
190
|
-
// prefers-reduced-motion) if you want it suppressed for that audience.
|
|
191
|
-
function playAnim() {
|
|
192
|
-
if (animKind === 'none' || !animEl || typeof animEl.animate !== 'function') return
|
|
193
|
-
const slide = animKind === 'slide'
|
|
194
|
-
// A month grid barely changes shape between months, so the movement has to be
|
|
195
|
-
// pronounced to read as a transition. Slide the new content a clear distance +
|
|
196
|
-
// fade; drills zoom. A mid-keyframe holds the offset briefly for legibility.
|
|
197
|
-
const D = 34
|
|
198
|
-
const navDir = cal.navDir
|
|
199
|
-
const from =
|
|
200
|
-
!slide ? { opacity: 0, transform: 'scale(0.98)' }
|
|
201
|
-
: navDir === 'next' ? { transform: `translateX(${D}px)`, opacity: 0 }
|
|
202
|
-
: navDir === 'prev' ? { transform: `translateX(-${D}px)`, opacity: 0 }
|
|
203
|
-
: navDir === 'drillDown' ? { transform: 'scale(0.85)', opacity: 0 }
|
|
204
|
-
: navDir === 'drillUp' ? { transform: 'scale(1.14)', opacity: 0 }
|
|
205
|
-
: { opacity: 0 }
|
|
206
|
-
animEl.animate([from, { transform: 'none', opacity: 1 }], {
|
|
207
|
-
duration: slide && (navDir === 'next' || navDir === 'prev') ? 300 : 220,
|
|
208
|
-
easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
|
|
209
|
-
})
|
|
210
|
-
}
|
|
211
|
-
$effect(() => {
|
|
212
|
-
cal.navToken // re-run after each navigation's DOM update
|
|
213
|
-
if (!animPrimed) { animPrimed = true; return }
|
|
214
|
-
untrack(playAnim)
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
// Mouse-wheel navigation (opt-in). Non-passive so we can prevent page scroll.
|
|
218
|
-
function wheelNav(node: HTMLElement) {
|
|
219
|
-
const onWheel = (e: WheelEvent) => {
|
|
220
|
-
if (!wheelNavigation || disabled) return
|
|
221
|
-
e.preventDefault()
|
|
222
|
-
cal.step(e.deltaY > 0 || e.deltaX > 0 ? 1 : -1)
|
|
223
|
-
}
|
|
224
|
-
node.addEventListener('wheel', onWheel, { passive: false })
|
|
225
|
-
return { destroy: () => node.removeEventListener('wheel', onWheel) }
|
|
226
|
-
}
|
|
227
|
-
</script>
|
|
228
|
-
|
|
229
|
-
{#snippet calBody()}
|
|
230
|
-
<div
|
|
231
|
-
class="sv-cal"
|
|
232
|
-
class:sv-cal--disabled={disabled}
|
|
233
|
-
class:sv-cal--invalid={invalid}
|
|
234
|
-
class:sv-cal--with-presets={presets && presets.length}
|
|
235
|
-
class:sv-cal--band={selectionMode === 'range' || selectionMode === 'week'}
|
|
236
|
-
class:sv-cal--rich={!!day}
|
|
237
|
-
id={uid}
|
|
238
|
-
role="group"
|
|
239
|
-
aria-label={label ?? M.label}
|
|
240
|
-
aria-disabled={disabled}
|
|
241
|
-
dir={resolvedDir}
|
|
242
|
-
>
|
|
243
|
-
{#if presets && presets.length}
|
|
244
|
-
<div class="sv-cal__presets" role="group" aria-label={M.shortcuts}>
|
|
245
|
-
{#each presets as p, i (i)}
|
|
246
|
-
<button class="sv-cal__preset" {...cal.presetProps(p)}>{p.label}</button>
|
|
247
|
-
{/each}
|
|
248
|
-
</div>
|
|
249
|
-
{/if}
|
|
250
|
-
|
|
251
|
-
<div class="sv-cal__main" use:wheelNav>
|
|
252
|
-
<div class="sv-cal__header">
|
|
253
|
-
<button class="sv-cal__nav" {...cal.navProps(-1)}>
|
|
254
|
-
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6" /></svg>
|
|
255
|
-
</button>
|
|
256
|
-
<button class="sv-cal__title" {...cal.titleProps()}>{cal.title}</button>
|
|
257
|
-
<button class="sv-cal__nav" {...cal.navProps(1)}>
|
|
258
|
-
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6" /></svg>
|
|
259
|
-
</button>
|
|
260
|
-
</div>
|
|
261
|
-
|
|
262
|
-
<div class="sv-cal__view" bind:this={animEl}>
|
|
263
|
-
{#if cal.mode === 'month'}
|
|
264
|
-
<div class="sv-cal__panels" {...cal.gridProps()}>
|
|
265
|
-
{#each cal.panels as panel (panel.month.getTime())}
|
|
266
|
-
<div class="sv-cal__panel">
|
|
267
|
-
{#if months > 1}<div class="sv-cal__panel-title">{panel.title}</div>{/if}
|
|
268
|
-
{#if !hideDayNames}
|
|
269
|
-
<div class="sv-cal__weekdays" role="row">
|
|
270
|
-
{#if weekNumbers}<span class="sv-cal__wk-head" aria-hidden="true">#</span>{/if}
|
|
271
|
-
{#each cal.weekdayHeaders as wh (wh.weekday)}
|
|
272
|
-
<span class="sv-cal__weekday" role="columnheader">{wh.label}</span>
|
|
273
|
-
{/each}
|
|
274
|
-
</div>
|
|
275
|
-
{/if}
|
|
276
|
-
{#each panel.matrix as week, wi (wi)}
|
|
277
|
-
<div class="sv-cal__week" role="row">
|
|
278
|
-
{#if weekNumbers}<span class="sv-cal__wk" aria-hidden="true">{week[0]?.week}</span>{/if}
|
|
279
|
-
{#each week as cell (cell.date.getTime())}
|
|
280
|
-
{@const st = cal.dayState(cell.date, panel.month)}
|
|
281
|
-
{#if st.outside && hideOther}
|
|
282
|
-
<span class="sv-cal__day sv-cal__day--empty" role="gridcell" aria-hidden="true"></span>
|
|
283
|
-
{:else}
|
|
284
|
-
<button
|
|
285
|
-
class="sv-cal__day"
|
|
286
|
-
class:is-selected={st.selected}
|
|
287
|
-
class:is-today={st.today}
|
|
288
|
-
class:is-outside={st.outside}
|
|
289
|
-
class:is-important={st.important}
|
|
290
|
-
class:is-preview={st.preview}
|
|
291
|
-
class:is-focused={st.focused}
|
|
292
|
-
class:is-recurring={st.recurring}
|
|
293
|
-
{...cal.dayProps(cell, panel.month)}
|
|
294
|
-
><span class="sv-cal__daynum">{cell.date.getDate()}</span>{#if day}<span class="sv-cal__daybody">{@render day(cell.date, st)}</span>{/if}</button>
|
|
295
|
-
{/if}
|
|
296
|
-
{/each}
|
|
297
|
-
</div>
|
|
298
|
-
{/each}
|
|
299
|
-
</div>
|
|
300
|
-
{/each}
|
|
301
|
-
</div>
|
|
302
|
-
{:else if cal.mode === 'year'}
|
|
303
|
-
<div class="sv-cal__grid-mode" {...cal.gridProps()}>
|
|
304
|
-
{#each cal.yearCells as yc (yc.index)}
|
|
305
|
-
<button
|
|
306
|
-
class="sv-cal__cell"
|
|
307
|
-
class:is-today={yc.current}
|
|
308
|
-
class:is-selected={yc.selected}
|
|
309
|
-
{...cal.monthCellProps(yc.index)}
|
|
310
|
-
>{yc.label}</button>
|
|
311
|
-
{/each}
|
|
312
|
-
</div>
|
|
313
|
-
{:else}
|
|
314
|
-
<div class="sv-cal__grid-mode" {...cal.gridProps()}>
|
|
315
|
-
{#each cal.decadeCells as dc (dc.year)}
|
|
316
|
-
<button
|
|
317
|
-
class="sv-cal__cell"
|
|
318
|
-
class:is-outside={dc.outside}
|
|
319
|
-
class:is-today={dc.current}
|
|
320
|
-
class:is-selected={dc.selected}
|
|
321
|
-
{...cal.yearCellProps(dc.year)}
|
|
322
|
-
>{dc.year}</button>
|
|
323
|
-
{/each}
|
|
324
|
-
</div>
|
|
325
|
-
{/if}
|
|
326
|
-
</div>
|
|
327
|
-
|
|
328
|
-
{#if footer}
|
|
329
|
-
<div class="sv-cal__footer">
|
|
330
|
-
<button type="button" class="sv-cal__foot-btn" onclick={cal.goToday} disabled={disabled}>{M.today}</button>
|
|
331
|
-
{#if cal.canClear}
|
|
332
|
-
<button type="button" class="sv-cal__foot-btn" onclick={cal.clearSelection} disabled={disabled}>{M.clear}</button>
|
|
333
|
-
{/if}
|
|
334
|
-
</div>
|
|
335
|
-
{/if}
|
|
336
|
-
</div>
|
|
337
|
-
|
|
338
|
-
{#if name}<input type="hidden" {name} value={cal.selectedDates.map((d) => d.toISOString()).join(',')} />{/if}
|
|
339
|
-
</div>
|
|
340
|
-
{/snippet}
|
|
341
|
-
|
|
342
|
-
{#if hasChrome}
|
|
343
|
-
<SvField id={uid} {label} {hint} {error} {required} {dir}>{@render calBody()}</SvField>
|
|
344
|
-
{:else}
|
|
345
|
-
{@render calBody()}
|
|
346
|
-
{/if}
|
|
347
|
-
|
|
348
|
-
<style>
|
|
349
|
-
.sv-cal {
|
|
350
|
-
--_accent: var(--sg-accent, #2563eb);
|
|
351
|
-
--_bg: var(--sg-bg, #fff);
|
|
352
|
-
--_fg: var(--sg-fg, #0f172a);
|
|
353
|
-
--_muted: var(--sg-muted, #64748b);
|
|
354
|
-
--_border: var(--sg-border, #e2e8f0);
|
|
355
|
-
--_hover: var(--sg-row-hover-bg, rgba(37, 99, 235, 0.08));
|
|
356
|
-
--_radius: var(--sg-radius, 8px);
|
|
357
|
-
display: inline-flex;
|
|
358
|
-
gap: 10px;
|
|
359
|
-
padding: 10px;
|
|
360
|
-
width: max-content;
|
|
361
|
-
background: var(--_bg);
|
|
362
|
-
color: var(--_fg);
|
|
363
|
-
border: 1px solid var(--_border);
|
|
364
|
-
border-radius: calc(var(--_radius) + 4px);
|
|
365
|
-
font-size: 13px;
|
|
366
|
-
user-select: none;
|
|
367
|
-
}
|
|
368
|
-
.sv-cal--disabled { opacity: 0.55; pointer-events: none; }
|
|
369
|
-
.sv-cal--invalid { border-color: var(--sg-danger, #dc2626); }
|
|
370
|
-
.sv-cal__main { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
371
|
-
/* Clip so animated content slides in from the edge (not past the border). Day
|
|
372
|
-
focus outlines use outline-offset:-2px, so they stay inside and aren't clipped. */
|
|
373
|
-
.sv-cal__view { display: block; overflow: hidden; }
|
|
374
|
-
/* Quick-shortcut rail (Today / Last 7 days / ...) shown when `presets` is set. */
|
|
375
|
-
.sv-cal__presets {
|
|
376
|
-
display: flex; flex-direction: column; gap: 2px;
|
|
377
|
-
padding-inline-end: 10px; border-inline-end: 1px solid var(--_border);
|
|
378
|
-
min-width: 118px; align-self: stretch;
|
|
379
|
-
}
|
|
380
|
-
.sv-cal__preset {
|
|
381
|
-
text-align: start; padding: 6px 10px; font: inherit; font-size: 12px; font-weight: 550;
|
|
382
|
-
background: none; border: 0; border-radius: var(--_radius); color: inherit; cursor: pointer; white-space: nowrap;
|
|
383
|
-
}
|
|
384
|
-
/* Under RTL the prev/next chevron glyphs must point the other way. */
|
|
385
|
-
.sv-cal[dir='rtl'] .sv-cal__nav svg { transform: scaleX(-1); }
|
|
386
|
-
.sv-cal__preset:hover:not(:disabled) { background: var(--_hover); color: var(--_accent); }
|
|
387
|
-
.sv-cal__preset:disabled { opacity: 0.5; cursor: default; }
|
|
388
|
-
|
|
389
|
-
.sv-cal__header { display: flex; align-items: center; gap: 4px; }
|
|
390
|
-
.sv-cal__title {
|
|
391
|
-
flex: 1; text-align: center; font-weight: 650; font-size: 13.5px;
|
|
392
|
-
background: none; border: 0; color: inherit; cursor: pointer;
|
|
393
|
-
padding: 6px 8px; border-radius: var(--_radius);
|
|
394
|
-
}
|
|
395
|
-
.sv-cal__title:hover:not(:disabled) { background: var(--_hover); }
|
|
396
|
-
.sv-cal__title:disabled { cursor: default; }
|
|
397
|
-
.sv-cal__nav {
|
|
398
|
-
display: grid; place-items: center; width: 30px; height: 30px;
|
|
399
|
-
background: none; border: 0; color: var(--_muted); cursor: pointer; border-radius: var(--_radius);
|
|
400
|
-
}
|
|
401
|
-
.sv-cal__nav:hover:not(:disabled) { background: var(--_hover); color: var(--_fg); }
|
|
402
|
-
.sv-cal__nav:disabled { opacity: 0.4; cursor: default; }
|
|
403
|
-
|
|
404
|
-
.sv-cal__panels { display: flex; gap: 14px; }
|
|
405
|
-
.sv-cal__panel-title { text-align: center; font-weight: 600; font-size: 12.5px; margin-bottom: 4px; color: var(--_muted); }
|
|
406
|
-
.sv-cal__weekdays, .sv-cal__week {
|
|
407
|
-
display: grid;
|
|
408
|
-
grid-template-columns: repeat(7, 34px);
|
|
409
|
-
}
|
|
410
|
-
.sv-cal__weekdays:has(.sv-cal__wk-head), .sv-cal__week:has(.sv-cal__wk) {
|
|
411
|
-
grid-template-columns: 26px repeat(7, 34px);
|
|
412
|
-
}
|
|
413
|
-
.sv-cal__weekday, .sv-cal__wk-head {
|
|
414
|
-
display: grid; place-items: center; height: 28px;
|
|
415
|
-
font-size: 11px; font-weight: 600; color: var(--_muted); text-transform: uppercase; letter-spacing: 0.02em;
|
|
416
|
-
}
|
|
417
|
-
.sv-cal__wk {
|
|
418
|
-
display: grid; place-items: center; height: 34px;
|
|
419
|
-
font-size: 10.5px; color: var(--_muted); opacity: 0.7;
|
|
420
|
-
}
|
|
421
|
-
.sv-cal__day {
|
|
422
|
-
display: grid; place-items: center; width: 34px; height: 34px; box-sizing: border-box;
|
|
423
|
-
background: none; border: 0; color: inherit; cursor: pointer;
|
|
424
|
-
border-radius: var(--_radius); font-size: 12.5px; position: relative;
|
|
425
|
-
}
|
|
426
|
-
.sv-cal__day--empty { visibility: hidden; }
|
|
427
|
-
/* Hover only NON-selected days with the neutral hover fill; hovering a selected
|
|
428
|
-
day keeps the accent (a bare :hover would swap in the neutral bg while the
|
|
429
|
-
text stays --sg-on-accent -> invisible on themes like shadcn). */
|
|
430
|
-
.sv-cal__day:hover:not(:disabled):not(.is-selected) { background: var(--_hover); }
|
|
431
|
-
.sv-cal__day.is-selected:hover:not(:disabled) { background: color-mix(in srgb, var(--_accent) 86%, var(--_fg)); }
|
|
432
|
-
.sv-cal__day.is-outside { color: var(--_muted); opacity: 0.55; }
|
|
433
|
-
.sv-cal__day.is-today { font-weight: 750; box-shadow: inset 0 0 0 1px var(--_accent); }
|
|
434
|
-
.sv-cal__day.is-important::after {
|
|
435
|
-
content: ''; position: absolute; bottom: 4px; left: 50%; transform: translateX(-50%);
|
|
436
|
-
width: 4px; height: 4px; border-radius: 50%; background: var(--sg-danger, #dc2626);
|
|
437
|
-
}
|
|
438
|
-
/* Repeat-pattern indicator: a small ring in the top-inline-end corner
|
|
439
|
-
(distinct from the is-important bottom dot). */
|
|
440
|
-
.sv-cal__day.is-recurring::before {
|
|
441
|
-
content: ''; position: absolute; top: 4px; inset-inline-end: 4px;
|
|
442
|
-
width: 5px; height: 5px; border-radius: 50%; pointer-events: none;
|
|
443
|
-
border: 1.5px solid color-mix(in srgb, var(--_accent) 75%, transparent);
|
|
444
|
-
}
|
|
445
|
-
.sv-cal:not(.sv-cal--rich) .sv-cal__day.is-selected.is-recurring::before { border-color: var(--sg-on-accent, #fff); }
|
|
446
|
-
|
|
447
|
-
/* --- Rich month grid: enabled by a `day` snippet. Cells fill the column width
|
|
448
|
-
and become taller / top-aligned so the snippet content (events, badges) has
|
|
449
|
-
room - turning the picker into an event-calendar surface. --- */
|
|
450
|
-
.sv-cal--rich .sv-cal__weekdays, .sv-cal--rich .sv-cal__week { grid-template-columns: repeat(7, minmax(0, 1fr)); }
|
|
451
|
-
.sv-cal--rich .sv-cal__weekdays:has(.sv-cal__wk-head), .sv-cal--rich .sv-cal__week:has(.sv-cal__wk) { grid-template-columns: 26px repeat(7, minmax(0, 1fr)); }
|
|
452
|
-
.sv-cal--rich .sv-cal__weekday { justify-items: start; padding-inline-start: 6px; }
|
|
453
|
-
.sv-cal--rich .sv-cal__day {
|
|
454
|
-
width: auto; height: auto; min-height: 90px; display: flex; flex-direction: column;
|
|
455
|
-
align-items: stretch; gap: 3px; padding: 4px 5px 6px; text-align: start; border-radius: 8px;
|
|
456
|
-
}
|
|
457
|
-
.sv-cal--rich .sv-cal__day.is-today { box-shadow: none; }
|
|
458
|
-
.sv-cal--rich .sv-cal__day.is-selected { background: color-mix(in srgb, var(--_accent) 9%, transparent); color: inherit; }
|
|
459
|
-
.sv-cal--rich .sv-cal__daynum {
|
|
460
|
-
align-self: flex-start; flex: none; width: 24px; height: 24px; display: grid; place-items: center;
|
|
461
|
-
border-radius: 50%; font-size: 12.5px; font-weight: 600;
|
|
462
|
-
}
|
|
463
|
-
.sv-cal--rich .sv-cal__day.is-today .sv-cal__daynum { background: var(--_accent); color: var(--sg-on-accent, #fff); }
|
|
464
|
-
.sv-cal--rich .sv-cal__daybody { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; overflow: hidden; }
|
|
465
|
-
.sv-cal--rich .sv-cal__day.is-important::after { display: none; }
|
|
466
|
-
.sv-cal__day.is-preview { background: color-mix(in srgb, var(--_accent) 16%, transparent); border-radius: 0; }
|
|
467
|
-
.sv-cal__day.is-selected {
|
|
468
|
-
background: var(--_accent); color: var(--sg-on-accent, #fff); font-weight: 650;
|
|
469
|
-
}
|
|
470
|
-
/* Discrete selection (single / many / ...): inset each selected pill so adjacent
|
|
471
|
-
picked days keep a visible gap. Range / week modes stay a connected band. */
|
|
472
|
-
.sv-cal:not(.sv-cal--band) .sv-cal__day.is-selected { background-clip: content-box; padding: 2px; }
|
|
473
|
-
.sv-cal__day.is-selected.is-important::after { background: var(--sg-on-accent, #fff); }
|
|
474
|
-
.sv-cal__day:disabled { color: var(--_muted); opacity: 0.35; cursor: default; text-decoration: line-through; }
|
|
475
|
-
.sv-cal__day.is-focused:focus-visible,
|
|
476
|
-
.sv-cal__day:focus-visible,
|
|
477
|
-
.sv-cal__cell:focus-visible {
|
|
478
|
-
outline: 2px solid var(--sg-focus-ring, var(--_accent)); outline-offset: -2px;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
.sv-cal__grid-mode {
|
|
482
|
-
display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px;
|
|
483
|
-
min-width: 252px;
|
|
484
|
-
}
|
|
485
|
-
.sv-cal__cell {
|
|
486
|
-
height: 44px; display: grid; place-items: center;
|
|
487
|
-
background: none; border: 0; color: inherit; cursor: pointer;
|
|
488
|
-
border-radius: var(--_radius); font-size: 12.5px;
|
|
489
|
-
}
|
|
490
|
-
.sv-cal__cell:hover:not(:disabled):not(.is-selected) { background: var(--_hover); }
|
|
491
|
-
.sv-cal__cell.is-selected:hover:not(:disabled) { background: color-mix(in srgb, var(--_accent) 86%, var(--_fg)); }
|
|
492
|
-
.sv-cal__cell.is-outside { color: var(--_muted); opacity: 0.5; }
|
|
493
|
-
.sv-cal__cell.is-today { box-shadow: inset 0 0 0 1px var(--_accent); font-weight: 700; }
|
|
494
|
-
.sv-cal__cell.is-selected { background: var(--_accent); color: var(--sg-on-accent, #fff); font-weight: 650; }
|
|
495
|
-
|
|
496
|
-
.sv-cal__footer { display: flex; justify-content: center; gap: 8px; border-top: 1px solid var(--_border); padding-top: 8px; margin-top: 2px; }
|
|
497
|
-
.sv-cal__foot-btn {
|
|
498
|
-
padding: 5px 12px; font: inherit; font-size: 12px; font-weight: 600;
|
|
499
|
-
background: none; border: 1px solid var(--_border); border-radius: var(--_radius);
|
|
500
|
-
color: var(--_accent); cursor: pointer;
|
|
501
|
-
}
|
|
502
|
-
.sv-cal__foot-btn:hover:not(:disabled) { background: var(--_hover); }
|
|
503
|
-
</style>
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
// Public types now live with the headless core and are re-exported here so the
|
|
3
|
+
// component's module entry point (and @svgrid/grid) keeps the same surface.
|
|
4
|
+
export type { CalendarValue, CalendarPreset, CalendarAnimation, DisplayMode } from './createCalendar.svelte'
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
/**
|
|
9
|
+
* SvCalendar - a themeable, accessible month/year/decade calendar. It is a thin
|
|
10
|
+
* styled renderer over the headless `createCalendar` core (the same split as
|
|
11
|
+
* `createSvGrid` / <SvGrid>): the core owns the reactive state + interaction +
|
|
12
|
+
* ARIA (built on the framework-free ./datetime engine), while this component
|
|
13
|
+
* keeps the render-only concerns - the WAAPI view-change animation, mouse-wheel
|
|
14
|
+
* navigation, and DOM refs. All visuals come from the grid's `--sg-*` tokens.
|
|
15
|
+
*
|
|
16
|
+
* Parity target: Smart `smart-calendar` (selectionModes, min/max, restricted /
|
|
17
|
+
* important dates, week numbers, firstDayOfWeek, drill navigation, keyboard).
|
|
18
|
+
*/
|
|
19
|
+
import { untrack, type Snippet } from 'svelte'
|
|
20
|
+
import SvField from './SvField.svelte'
|
|
21
|
+
import { nextEditorId, resolveMessages, type EditorDir, type EditorSize } from './editor-contract'
|
|
22
|
+
import {
|
|
23
|
+
createCalendar,
|
|
24
|
+
type CalendarValue,
|
|
25
|
+
type CalendarPreset,
|
|
26
|
+
type CalendarAnimation,
|
|
27
|
+
type CalendarDayState,
|
|
28
|
+
type DisplayMode,
|
|
29
|
+
} from './createCalendar.svelte'
|
|
30
|
+
import type { SelectionMode } from './datetime/date-selection'
|
|
31
|
+
import type { RestrictOptions } from './datetime/date-restrict'
|
|
32
|
+
import type { DateLike } from './datetime/date-core'
|
|
33
|
+
import type { RecurrenceRule } from './recurrence'
|
|
34
|
+
|
|
35
|
+
type NameFormat = 'narrow' | 'short' | 'long'
|
|
36
|
+
|
|
37
|
+
/** User-facing strings (localizable via `messages`). */
|
|
38
|
+
type CalendarMessages = { label: string; shortcuts: string; today: string; clear: string; prev: string; next: string }
|
|
39
|
+
const DEFAULT_MESSAGES: CalendarMessages = {
|
|
40
|
+
label: 'Calendar', shortcuts: 'Shortcuts', today: 'Today', clear: 'Clear', prev: 'Previous', next: 'Next',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type Props = {
|
|
44
|
+
/** Selected value(s). Single Date for one/zeroOrOne, array for multi modes. */
|
|
45
|
+
value?: CalendarValue
|
|
46
|
+
/** Fires with the full selected-day list on every change. */
|
|
47
|
+
onChange?: (dates: Date[]) => void
|
|
48
|
+
/** Fires when the visible month/year/decade page changes. */
|
|
49
|
+
onNavigate?: (viewDate: Date, displayMode: DisplayMode) => void
|
|
50
|
+
selectionMode?: SelectionMode
|
|
51
|
+
min?: DateLike | null
|
|
52
|
+
max?: DateLike | null
|
|
53
|
+
/** Non-selectable dates (list or predicate). */
|
|
54
|
+
restrictedDates?: RestrictOptions['restrictedDates']
|
|
55
|
+
/** Highlighted (but still selectable) dates. */
|
|
56
|
+
importantDates?: ReadonlyArray<DateLike> | ((d: Date) => boolean) | null
|
|
57
|
+
/** 0 = Sunday .. 6 = Saturday. */
|
|
58
|
+
firstDayOfWeek?: number
|
|
59
|
+
weeks?: number
|
|
60
|
+
weekNumbers?: boolean
|
|
61
|
+
/** Number of month panels side by side. */
|
|
62
|
+
months?: number
|
|
63
|
+
hideDayNames?: boolean
|
|
64
|
+
hideOtherMonthDays?: boolean
|
|
65
|
+
dayNameFormat?: NameFormat
|
|
66
|
+
monthNameFormat?: NameFormat
|
|
67
|
+
/** Show the Today / Clear footer. */
|
|
68
|
+
footer?: boolean
|
|
69
|
+
disabled?: boolean
|
|
70
|
+
readonly?: boolean
|
|
71
|
+
locale?: string
|
|
72
|
+
name?: string
|
|
73
|
+
/** Which drill level to open on. */
|
|
74
|
+
displayMode?: DisplayMode
|
|
75
|
+
/** Animate month/year navigation + drill (opt-in). `true` = 'slide'. This is
|
|
76
|
+
* an explicit opt-in, so it plays regardless of the OS "reduce motion"
|
|
77
|
+
* setting - pass 'none' (or gate it yourself) to suppress it. */
|
|
78
|
+
animate?: boolean | CalendarAnimation
|
|
79
|
+
/** Change the visible month/year/decade with the mouse wheel over the grid. */
|
|
80
|
+
wheelNavigation?: boolean
|
|
81
|
+
/** Per-day tooltip text (shown as the native title on the day button). */
|
|
82
|
+
dateTooltip?: (date: Date) => string | null | undefined
|
|
83
|
+
/** One-click shortcuts (e.g. Today, Last 7 days) shown in a side rail. */
|
|
84
|
+
presets?: ReadonlyArray<CalendarPreset>
|
|
85
|
+
/** Text direction (rtl mirrors layout + flips the nav arrows; auto inherits). */
|
|
86
|
+
dir?: EditorDir
|
|
87
|
+
/** Override the built-in strings (group/shortcut labels, footer buttons). */
|
|
88
|
+
messages?: Partial<CalendarMessages>
|
|
89
|
+
/** Repeat pattern(s): matching days get an indicator + `recurring` day-state. */
|
|
90
|
+
recurrence?: RecurrenceRule | ReadonlyArray<RecurrenceRule> | null
|
|
91
|
+
/** Render rich content inside each day cell (events, dots, badges). Receives
|
|
92
|
+
* the date + its day-state. Switches the month grid to a taller,
|
|
93
|
+
* top-aligned layout so there's room for the content. */
|
|
94
|
+
day?: Snippet<[Date, CalendarDayState]>
|
|
95
|
+
// Optional field chrome: when any of label/hint/error is set, the calendar is
|
|
96
|
+
// wrapped in <SvField> so it gains a label/hint/error line; otherwise it
|
|
97
|
+
// renders bare (as embedded inside SvDateTimePicker's popover).
|
|
98
|
+
label?: string
|
|
99
|
+
hint?: string
|
|
100
|
+
error?: string
|
|
101
|
+
required?: boolean
|
|
102
|
+
invalid?: boolean
|
|
103
|
+
size?: EditorSize
|
|
104
|
+
id?: string
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let {
|
|
108
|
+
value = null,
|
|
109
|
+
onChange,
|
|
110
|
+
onNavigate,
|
|
111
|
+
selectionMode = 'one',
|
|
112
|
+
min = null,
|
|
113
|
+
max = null,
|
|
114
|
+
restrictedDates = null,
|
|
115
|
+
importantDates = null,
|
|
116
|
+
firstDayOfWeek = 0,
|
|
117
|
+
weeks = 6,
|
|
118
|
+
weekNumbers = false,
|
|
119
|
+
months = 1,
|
|
120
|
+
hideDayNames = false,
|
|
121
|
+
hideOtherMonthDays = false,
|
|
122
|
+
dayNameFormat = 'short',
|
|
123
|
+
monthNameFormat = 'long',
|
|
124
|
+
footer = false,
|
|
125
|
+
disabled = false,
|
|
126
|
+
readonly = false,
|
|
127
|
+
locale = typeof navigator !== 'undefined' ? navigator.language : 'en-US',
|
|
128
|
+
name,
|
|
129
|
+
displayMode = 'month',
|
|
130
|
+
animate = false,
|
|
131
|
+
wheelNavigation = false,
|
|
132
|
+
dateTooltip,
|
|
133
|
+
presets,
|
|
134
|
+
dir,
|
|
135
|
+
messages,
|
|
136
|
+
recurrence = null,
|
|
137
|
+
day,
|
|
138
|
+
label,
|
|
139
|
+
hint,
|
|
140
|
+
error,
|
|
141
|
+
required = false,
|
|
142
|
+
invalid = false,
|
|
143
|
+
id,
|
|
144
|
+
}: Props = $props()
|
|
145
|
+
|
|
146
|
+
const calAutoId = nextEditorId('sv-cal')
|
|
147
|
+
const uid = $derived(id ?? calAutoId)
|
|
148
|
+
const hasChrome = $derived(!!(label || hint || error))
|
|
149
|
+
const M = $derived(resolveMessages(DEFAULT_MESSAGES, messages))
|
|
150
|
+
const resolvedDir = $derived(dir === 'ltr' || dir === 'rtl' ? dir : undefined)
|
|
151
|
+
// With several month panels side by side, leading/trailing days of adjacent
|
|
152
|
+
// months overlap visually, so hide them by default when months > 1.
|
|
153
|
+
const hideOther = $derived(hideOtherMonthDays || months > 1)
|
|
154
|
+
|
|
155
|
+
// The headless core owns all reactive state, selection, navigation, keyboard
|
|
156
|
+
// and ARIA. Reactive inputs are passed as getters; callbacks as closures.
|
|
157
|
+
const cal = createCalendar({
|
|
158
|
+
value: () => value,
|
|
159
|
+
onChange: (d) => onChange?.(d),
|
|
160
|
+
onNavigate: (v, m) => onNavigate?.(v, m),
|
|
161
|
+
selectionMode: () => selectionMode,
|
|
162
|
+
min: () => min,
|
|
163
|
+
max: () => max,
|
|
164
|
+
restrictedDates: () => restrictedDates,
|
|
165
|
+
importantDates: () => importantDates,
|
|
166
|
+
firstDayOfWeek: () => firstDayOfWeek,
|
|
167
|
+
weeks: () => weeks,
|
|
168
|
+
months: () => months,
|
|
169
|
+
displayMode: () => displayMode,
|
|
170
|
+
disabled: () => disabled,
|
|
171
|
+
readonly: () => readonly,
|
|
172
|
+
locale: () => locale,
|
|
173
|
+
dayNameFormat: () => dayNameFormat,
|
|
174
|
+
monthNameFormat: () => monthNameFormat,
|
|
175
|
+
dateTooltip: () => dateTooltip,
|
|
176
|
+
recurrence: () => recurrence,
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
// --- View-change animation (framework-native Web Animations API) -----------
|
|
180
|
+
// A render concern kept out of the core: we animate a wrapper element instead
|
|
181
|
+
// of re-keying the grid, so day cells stay mounted (keyboard focus +
|
|
182
|
+
// interactivity are preserved). The core exposes `navToken` (bumps on each
|
|
183
|
+
// navigation) + `navDir` (why it changed); an effect replays a short keyframe
|
|
184
|
+
// on the new content.
|
|
185
|
+
const animKind = $derived<CalendarAnimation>(animate === true ? 'slide' : animate || 'none')
|
|
186
|
+
let animEl = $state<HTMLElement | undefined>()
|
|
187
|
+
let animPrimed = false // skip the very first (mount) run
|
|
188
|
+
// Note: `animate` is an explicit opt-in, so we honor it even under the OS
|
|
189
|
+
// "reduce motion" setting. Pass animate="none" (or gate it yourself on
|
|
190
|
+
// prefers-reduced-motion) if you want it suppressed for that audience.
|
|
191
|
+
function playAnim() {
|
|
192
|
+
if (animKind === 'none' || !animEl || typeof animEl.animate !== 'function') return
|
|
193
|
+
const slide = animKind === 'slide'
|
|
194
|
+
// A month grid barely changes shape between months, so the movement has to be
|
|
195
|
+
// pronounced to read as a transition. Slide the new content a clear distance +
|
|
196
|
+
// fade; drills zoom. A mid-keyframe holds the offset briefly for legibility.
|
|
197
|
+
const D = 34
|
|
198
|
+
const navDir = cal.navDir
|
|
199
|
+
const from =
|
|
200
|
+
!slide ? { opacity: 0, transform: 'scale(0.98)' }
|
|
201
|
+
: navDir === 'next' ? { transform: `translateX(${D}px)`, opacity: 0 }
|
|
202
|
+
: navDir === 'prev' ? { transform: `translateX(-${D}px)`, opacity: 0 }
|
|
203
|
+
: navDir === 'drillDown' ? { transform: 'scale(0.85)', opacity: 0 }
|
|
204
|
+
: navDir === 'drillUp' ? { transform: 'scale(1.14)', opacity: 0 }
|
|
205
|
+
: { opacity: 0 }
|
|
206
|
+
animEl.animate([from, { transform: 'none', opacity: 1 }], {
|
|
207
|
+
duration: slide && (navDir === 'next' || navDir === 'prev') ? 300 : 220,
|
|
208
|
+
easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
$effect(() => {
|
|
212
|
+
cal.navToken // re-run after each navigation's DOM update
|
|
213
|
+
if (!animPrimed) { animPrimed = true; return }
|
|
214
|
+
untrack(playAnim)
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
// Mouse-wheel navigation (opt-in). Non-passive so we can prevent page scroll.
|
|
218
|
+
function wheelNav(node: HTMLElement) {
|
|
219
|
+
const onWheel = (e: WheelEvent) => {
|
|
220
|
+
if (!wheelNavigation || disabled) return
|
|
221
|
+
e.preventDefault()
|
|
222
|
+
cal.step(e.deltaY > 0 || e.deltaX > 0 ? 1 : -1)
|
|
223
|
+
}
|
|
224
|
+
node.addEventListener('wheel', onWheel, { passive: false })
|
|
225
|
+
return { destroy: () => node.removeEventListener('wheel', onWheel) }
|
|
226
|
+
}
|
|
227
|
+
</script>
|
|
228
|
+
|
|
229
|
+
{#snippet calBody()}
|
|
230
|
+
<div
|
|
231
|
+
class="sv-cal"
|
|
232
|
+
class:sv-cal--disabled={disabled}
|
|
233
|
+
class:sv-cal--invalid={invalid}
|
|
234
|
+
class:sv-cal--with-presets={presets && presets.length}
|
|
235
|
+
class:sv-cal--band={selectionMode === 'range' || selectionMode === 'week'}
|
|
236
|
+
class:sv-cal--rich={!!day}
|
|
237
|
+
id={uid}
|
|
238
|
+
role="group"
|
|
239
|
+
aria-label={label ?? M.label}
|
|
240
|
+
aria-disabled={disabled}
|
|
241
|
+
dir={resolvedDir}
|
|
242
|
+
>
|
|
243
|
+
{#if presets && presets.length}
|
|
244
|
+
<div class="sv-cal__presets" role="group" aria-label={M.shortcuts}>
|
|
245
|
+
{#each presets as p, i (i)}
|
|
246
|
+
<button class="sv-cal__preset" {...cal.presetProps(p)}>{p.label}</button>
|
|
247
|
+
{/each}
|
|
248
|
+
</div>
|
|
249
|
+
{/if}
|
|
250
|
+
|
|
251
|
+
<div class="sv-cal__main" use:wheelNav>
|
|
252
|
+
<div class="sv-cal__header">
|
|
253
|
+
<button class="sv-cal__nav" {...cal.navProps(-1)}>
|
|
254
|
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6" /></svg>
|
|
255
|
+
</button>
|
|
256
|
+
<button class="sv-cal__title" {...cal.titleProps()}>{cal.title}</button>
|
|
257
|
+
<button class="sv-cal__nav" {...cal.navProps(1)}>
|
|
258
|
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6" /></svg>
|
|
259
|
+
</button>
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<div class="sv-cal__view" bind:this={animEl}>
|
|
263
|
+
{#if cal.mode === 'month'}
|
|
264
|
+
<div class="sv-cal__panels" {...cal.gridProps()}>
|
|
265
|
+
{#each cal.panels as panel (panel.month.getTime())}
|
|
266
|
+
<div class="sv-cal__panel">
|
|
267
|
+
{#if months > 1}<div class="sv-cal__panel-title">{panel.title}</div>{/if}
|
|
268
|
+
{#if !hideDayNames}
|
|
269
|
+
<div class="sv-cal__weekdays" role="row">
|
|
270
|
+
{#if weekNumbers}<span class="sv-cal__wk-head" aria-hidden="true">#</span>{/if}
|
|
271
|
+
{#each cal.weekdayHeaders as wh (wh.weekday)}
|
|
272
|
+
<span class="sv-cal__weekday" role="columnheader">{wh.label}</span>
|
|
273
|
+
{/each}
|
|
274
|
+
</div>
|
|
275
|
+
{/if}
|
|
276
|
+
{#each panel.matrix as week, wi (wi)}
|
|
277
|
+
<div class="sv-cal__week" role="row">
|
|
278
|
+
{#if weekNumbers}<span class="sv-cal__wk" aria-hidden="true">{week[0]?.week}</span>{/if}
|
|
279
|
+
{#each week as cell (cell.date.getTime())}
|
|
280
|
+
{@const st = cal.dayState(cell.date, panel.month)}
|
|
281
|
+
{#if st.outside && hideOther}
|
|
282
|
+
<span class="sv-cal__day sv-cal__day--empty" role="gridcell" aria-hidden="true"></span>
|
|
283
|
+
{:else}
|
|
284
|
+
<button
|
|
285
|
+
class="sv-cal__day"
|
|
286
|
+
class:is-selected={st.selected}
|
|
287
|
+
class:is-today={st.today}
|
|
288
|
+
class:is-outside={st.outside}
|
|
289
|
+
class:is-important={st.important}
|
|
290
|
+
class:is-preview={st.preview}
|
|
291
|
+
class:is-focused={st.focused}
|
|
292
|
+
class:is-recurring={st.recurring}
|
|
293
|
+
{...cal.dayProps(cell, panel.month)}
|
|
294
|
+
><span class="sv-cal__daynum">{cell.date.getDate()}</span>{#if day}<span class="sv-cal__daybody">{@render day(cell.date, st)}</span>{/if}</button>
|
|
295
|
+
{/if}
|
|
296
|
+
{/each}
|
|
297
|
+
</div>
|
|
298
|
+
{/each}
|
|
299
|
+
</div>
|
|
300
|
+
{/each}
|
|
301
|
+
</div>
|
|
302
|
+
{:else if cal.mode === 'year'}
|
|
303
|
+
<div class="sv-cal__grid-mode" {...cal.gridProps()}>
|
|
304
|
+
{#each cal.yearCells as yc (yc.index)}
|
|
305
|
+
<button
|
|
306
|
+
class="sv-cal__cell"
|
|
307
|
+
class:is-today={yc.current}
|
|
308
|
+
class:is-selected={yc.selected}
|
|
309
|
+
{...cal.monthCellProps(yc.index)}
|
|
310
|
+
>{yc.label}</button>
|
|
311
|
+
{/each}
|
|
312
|
+
</div>
|
|
313
|
+
{:else}
|
|
314
|
+
<div class="sv-cal__grid-mode" {...cal.gridProps()}>
|
|
315
|
+
{#each cal.decadeCells as dc (dc.year)}
|
|
316
|
+
<button
|
|
317
|
+
class="sv-cal__cell"
|
|
318
|
+
class:is-outside={dc.outside}
|
|
319
|
+
class:is-today={dc.current}
|
|
320
|
+
class:is-selected={dc.selected}
|
|
321
|
+
{...cal.yearCellProps(dc.year)}
|
|
322
|
+
>{dc.year}</button>
|
|
323
|
+
{/each}
|
|
324
|
+
</div>
|
|
325
|
+
{/if}
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
{#if footer}
|
|
329
|
+
<div class="sv-cal__footer">
|
|
330
|
+
<button type="button" class="sv-cal__foot-btn" onclick={cal.goToday} disabled={disabled}>{M.today}</button>
|
|
331
|
+
{#if cal.canClear}
|
|
332
|
+
<button type="button" class="sv-cal__foot-btn" onclick={cal.clearSelection} disabled={disabled}>{M.clear}</button>
|
|
333
|
+
{/if}
|
|
334
|
+
</div>
|
|
335
|
+
{/if}
|
|
336
|
+
</div>
|
|
337
|
+
|
|
338
|
+
{#if name}<input type="hidden" {name} value={cal.selectedDates.map((d) => d.toISOString()).join(',')} />{/if}
|
|
339
|
+
</div>
|
|
340
|
+
{/snippet}
|
|
341
|
+
|
|
342
|
+
{#if hasChrome}
|
|
343
|
+
<SvField id={uid} {label} {hint} {error} {required} {dir}>{@render calBody()}</SvField>
|
|
344
|
+
{:else}
|
|
345
|
+
{@render calBody()}
|
|
346
|
+
{/if}
|
|
347
|
+
|
|
348
|
+
<style>
|
|
349
|
+
.sv-cal {
|
|
350
|
+
--_accent: var(--sg-accent, #2563eb);
|
|
351
|
+
--_bg: var(--sg-bg, #fff);
|
|
352
|
+
--_fg: var(--sg-fg, #0f172a);
|
|
353
|
+
--_muted: var(--sg-muted, #64748b);
|
|
354
|
+
--_border: var(--sg-border, #e2e8f0);
|
|
355
|
+
--_hover: var(--sg-row-hover-bg, rgba(37, 99, 235, 0.08));
|
|
356
|
+
--_radius: var(--sg-radius, 8px);
|
|
357
|
+
display: inline-flex;
|
|
358
|
+
gap: 10px;
|
|
359
|
+
padding: 10px;
|
|
360
|
+
width: max-content;
|
|
361
|
+
background: var(--_bg);
|
|
362
|
+
color: var(--_fg);
|
|
363
|
+
border: 1px solid var(--_border);
|
|
364
|
+
border-radius: calc(var(--_radius) + 4px);
|
|
365
|
+
font-size: 13px;
|
|
366
|
+
user-select: none;
|
|
367
|
+
}
|
|
368
|
+
.sv-cal--disabled { opacity: 0.55; pointer-events: none; }
|
|
369
|
+
.sv-cal--invalid { border-color: var(--sg-danger, #dc2626); }
|
|
370
|
+
.sv-cal__main { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
371
|
+
/* Clip so animated content slides in from the edge (not past the border). Day
|
|
372
|
+
focus outlines use outline-offset:-2px, so they stay inside and aren't clipped. */
|
|
373
|
+
.sv-cal__view { display: block; overflow: hidden; }
|
|
374
|
+
/* Quick-shortcut rail (Today / Last 7 days / ...) shown when `presets` is set. */
|
|
375
|
+
.sv-cal__presets {
|
|
376
|
+
display: flex; flex-direction: column; gap: 2px;
|
|
377
|
+
padding-inline-end: 10px; border-inline-end: 1px solid var(--_border);
|
|
378
|
+
min-width: 118px; align-self: stretch;
|
|
379
|
+
}
|
|
380
|
+
.sv-cal__preset {
|
|
381
|
+
text-align: start; padding: 6px 10px; font: inherit; font-size: 12px; font-weight: 550;
|
|
382
|
+
background: none; border: 0; border-radius: var(--_radius); color: inherit; cursor: pointer; white-space: nowrap;
|
|
383
|
+
}
|
|
384
|
+
/* Under RTL the prev/next chevron glyphs must point the other way. */
|
|
385
|
+
.sv-cal[dir='rtl'] .sv-cal__nav svg { transform: scaleX(-1); }
|
|
386
|
+
.sv-cal__preset:hover:not(:disabled) { background: var(--_hover); color: var(--_accent); }
|
|
387
|
+
.sv-cal__preset:disabled { opacity: 0.5; cursor: default; }
|
|
388
|
+
|
|
389
|
+
.sv-cal__header { display: flex; align-items: center; gap: 4px; }
|
|
390
|
+
.sv-cal__title {
|
|
391
|
+
flex: 1; text-align: center; font-weight: 650; font-size: 13.5px;
|
|
392
|
+
background: none; border: 0; color: inherit; cursor: pointer;
|
|
393
|
+
padding: 6px 8px; border-radius: var(--_radius);
|
|
394
|
+
}
|
|
395
|
+
.sv-cal__title:hover:not(:disabled) { background: var(--_hover); }
|
|
396
|
+
.sv-cal__title:disabled { cursor: default; }
|
|
397
|
+
.sv-cal__nav {
|
|
398
|
+
display: grid; place-items: center; width: 30px; height: 30px;
|
|
399
|
+
background: none; border: 0; color: var(--_muted); cursor: pointer; border-radius: var(--_radius);
|
|
400
|
+
}
|
|
401
|
+
.sv-cal__nav:hover:not(:disabled) { background: var(--_hover); color: var(--_fg); }
|
|
402
|
+
.sv-cal__nav:disabled { opacity: 0.4; cursor: default; }
|
|
403
|
+
|
|
404
|
+
.sv-cal__panels { display: flex; gap: 14px; }
|
|
405
|
+
.sv-cal__panel-title { text-align: center; font-weight: 600; font-size: 12.5px; margin-bottom: 4px; color: var(--_muted); }
|
|
406
|
+
.sv-cal__weekdays, .sv-cal__week {
|
|
407
|
+
display: grid;
|
|
408
|
+
grid-template-columns: repeat(7, 34px);
|
|
409
|
+
}
|
|
410
|
+
.sv-cal__weekdays:has(.sv-cal__wk-head), .sv-cal__week:has(.sv-cal__wk) {
|
|
411
|
+
grid-template-columns: 26px repeat(7, 34px);
|
|
412
|
+
}
|
|
413
|
+
.sv-cal__weekday, .sv-cal__wk-head {
|
|
414
|
+
display: grid; place-items: center; height: 28px;
|
|
415
|
+
font-size: 11px; font-weight: 600; color: var(--_muted); text-transform: uppercase; letter-spacing: 0.02em;
|
|
416
|
+
}
|
|
417
|
+
.sv-cal__wk {
|
|
418
|
+
display: grid; place-items: center; height: 34px;
|
|
419
|
+
font-size: 10.5px; color: var(--_muted); opacity: 0.7;
|
|
420
|
+
}
|
|
421
|
+
.sv-cal__day {
|
|
422
|
+
display: grid; place-items: center; width: 34px; height: 34px; box-sizing: border-box;
|
|
423
|
+
background: none; border: 0; color: inherit; cursor: pointer;
|
|
424
|
+
border-radius: var(--_radius); font-size: 12.5px; position: relative;
|
|
425
|
+
}
|
|
426
|
+
.sv-cal__day--empty { visibility: hidden; }
|
|
427
|
+
/* Hover only NON-selected days with the neutral hover fill; hovering a selected
|
|
428
|
+
day keeps the accent (a bare :hover would swap in the neutral bg while the
|
|
429
|
+
text stays --sg-on-accent -> invisible on themes like shadcn). */
|
|
430
|
+
.sv-cal__day:hover:not(:disabled):not(.is-selected) { background: var(--_hover); }
|
|
431
|
+
.sv-cal__day.is-selected:hover:not(:disabled) { background: color-mix(in srgb, var(--_accent) 86%, var(--_fg)); }
|
|
432
|
+
.sv-cal__day.is-outside { color: var(--_muted); opacity: 0.55; }
|
|
433
|
+
.sv-cal__day.is-today { font-weight: 750; box-shadow: inset 0 0 0 1px var(--_accent); }
|
|
434
|
+
.sv-cal__day.is-important::after {
|
|
435
|
+
content: ''; position: absolute; bottom: 4px; left: 50%; transform: translateX(-50%);
|
|
436
|
+
width: 4px; height: 4px; border-radius: 50%; background: var(--sg-danger, #dc2626);
|
|
437
|
+
}
|
|
438
|
+
/* Repeat-pattern indicator: a small ring in the top-inline-end corner
|
|
439
|
+
(distinct from the is-important bottom dot). */
|
|
440
|
+
.sv-cal__day.is-recurring::before {
|
|
441
|
+
content: ''; position: absolute; top: 4px; inset-inline-end: 4px;
|
|
442
|
+
width: 5px; height: 5px; border-radius: 50%; pointer-events: none;
|
|
443
|
+
border: 1.5px solid color-mix(in srgb, var(--_accent) 75%, transparent);
|
|
444
|
+
}
|
|
445
|
+
.sv-cal:not(.sv-cal--rich) .sv-cal__day.is-selected.is-recurring::before { border-color: var(--sg-on-accent, #fff); }
|
|
446
|
+
|
|
447
|
+
/* --- Rich month grid: enabled by a `day` snippet. Cells fill the column width
|
|
448
|
+
and become taller / top-aligned so the snippet content (events, badges) has
|
|
449
|
+
room - turning the picker into an event-calendar surface. --- */
|
|
450
|
+
.sv-cal--rich .sv-cal__weekdays, .sv-cal--rich .sv-cal__week { grid-template-columns: repeat(7, minmax(0, 1fr)); }
|
|
451
|
+
.sv-cal--rich .sv-cal__weekdays:has(.sv-cal__wk-head), .sv-cal--rich .sv-cal__week:has(.sv-cal__wk) { grid-template-columns: 26px repeat(7, minmax(0, 1fr)); }
|
|
452
|
+
.sv-cal--rich .sv-cal__weekday { justify-items: start; padding-inline-start: 6px; }
|
|
453
|
+
.sv-cal--rich .sv-cal__day {
|
|
454
|
+
width: auto; height: auto; min-height: 90px; display: flex; flex-direction: column;
|
|
455
|
+
align-items: stretch; gap: 3px; padding: 4px 5px 6px; text-align: start; border-radius: 8px;
|
|
456
|
+
}
|
|
457
|
+
.sv-cal--rich .sv-cal__day.is-today { box-shadow: none; }
|
|
458
|
+
.sv-cal--rich .sv-cal__day.is-selected { background: color-mix(in srgb, var(--_accent) 9%, transparent); color: inherit; }
|
|
459
|
+
.sv-cal--rich .sv-cal__daynum {
|
|
460
|
+
align-self: flex-start; flex: none; width: 24px; height: 24px; display: grid; place-items: center;
|
|
461
|
+
border-radius: 50%; font-size: 12.5px; font-weight: 600;
|
|
462
|
+
}
|
|
463
|
+
.sv-cal--rich .sv-cal__day.is-today .sv-cal__daynum { background: var(--_accent); color: var(--sg-on-accent, #fff); }
|
|
464
|
+
.sv-cal--rich .sv-cal__daybody { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; overflow: hidden; }
|
|
465
|
+
.sv-cal--rich .sv-cal__day.is-important::after { display: none; }
|
|
466
|
+
.sv-cal__day.is-preview { background: color-mix(in srgb, var(--_accent) 16%, transparent); border-radius: 0; }
|
|
467
|
+
.sv-cal__day.is-selected {
|
|
468
|
+
background: var(--_accent); color: var(--sg-on-accent, #fff); font-weight: 650;
|
|
469
|
+
}
|
|
470
|
+
/* Discrete selection (single / many / ...): inset each selected pill so adjacent
|
|
471
|
+
picked days keep a visible gap. Range / week modes stay a connected band. */
|
|
472
|
+
.sv-cal:not(.sv-cal--band) .sv-cal__day.is-selected { background-clip: content-box; padding: 2px; }
|
|
473
|
+
.sv-cal__day.is-selected.is-important::after { background: var(--sg-on-accent, #fff); }
|
|
474
|
+
.sv-cal__day:disabled { color: var(--_muted); opacity: 0.35; cursor: default; text-decoration: line-through; }
|
|
475
|
+
.sv-cal__day.is-focused:focus-visible,
|
|
476
|
+
.sv-cal__day:focus-visible,
|
|
477
|
+
.sv-cal__cell:focus-visible {
|
|
478
|
+
outline: 2px solid var(--sg-focus-ring, var(--_accent)); outline-offset: -2px;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.sv-cal__grid-mode {
|
|
482
|
+
display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px;
|
|
483
|
+
min-width: 252px;
|
|
484
|
+
}
|
|
485
|
+
.sv-cal__cell {
|
|
486
|
+
height: 44px; display: grid; place-items: center;
|
|
487
|
+
background: none; border: 0; color: inherit; cursor: pointer;
|
|
488
|
+
border-radius: var(--_radius); font-size: 12.5px;
|
|
489
|
+
}
|
|
490
|
+
.sv-cal__cell:hover:not(:disabled):not(.is-selected) { background: var(--_hover); }
|
|
491
|
+
.sv-cal__cell.is-selected:hover:not(:disabled) { background: color-mix(in srgb, var(--_accent) 86%, var(--_fg)); }
|
|
492
|
+
.sv-cal__cell.is-outside { color: var(--_muted); opacity: 0.5; }
|
|
493
|
+
.sv-cal__cell.is-today { box-shadow: inset 0 0 0 1px var(--_accent); font-weight: 700; }
|
|
494
|
+
.sv-cal__cell.is-selected { background: var(--_accent); color: var(--sg-on-accent, #fff); font-weight: 650; }
|
|
495
|
+
|
|
496
|
+
.sv-cal__footer { display: flex; justify-content: center; gap: 8px; border-top: 1px solid var(--_border); padding-top: 8px; margin-top: 2px; }
|
|
497
|
+
.sv-cal__foot-btn {
|
|
498
|
+
padding: 5px 12px; font: inherit; font-size: 12px; font-weight: 600;
|
|
499
|
+
background: none; border: 1px solid var(--_border); border-radius: var(--_radius);
|
|
500
|
+
color: var(--_accent); cursor: pointer;
|
|
501
|
+
}
|
|
502
|
+
.sv-cal__foot-btn:hover:not(:disabled) { background: var(--_hover); }
|
|
503
|
+
</style>
|