mtrl 0.10.0-next.1 → 0.10.0-next.3
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 +26 -5
- package/dist/README.md +26 -5
- package/dist/components/bottom-sheet/config.js +11 -4
- package/dist/components/bottom-sheet/constants.d.ts +0 -2
- package/dist/components/bottom-sheet/constants.js +0 -1
- package/dist/components/bottom-sheet/features/state.js +39 -2
- package/dist/components/bottom-sheet/features/structure.js +5 -1
- package/dist/components/bottom-sheet/types.d.ts +15 -2
- package/dist/components/chips/features/controller.js +2 -1
- package/dist/components/datepicker/constants.d.ts +1 -0
- package/dist/components/datepicker/constants.js +1 -0
- package/dist/components/datepicker/datepicker.js +79 -10
- package/dist/components/datepicker/render.js +43 -10
- package/dist/components/datepicker/types.d.ts +4 -1
- package/dist/components/datepicker/utils.d.ts +0 -7
- package/dist/components/datepicker/utils.js +0 -9
- package/dist/components/dialog/api.js +5 -0
- package/dist/components/dialog/config.js +8 -2
- package/dist/components/dialog/features.js +73 -14
- package/dist/components/dialog/types.d.ts +11 -0
- package/dist/components/drawer/config.js +9 -2
- package/dist/components/drawer/features/items.js +2 -1
- package/dist/components/drawer/features/state.js +50 -9
- package/dist/components/drawer/types.d.ts +12 -0
- package/dist/components/extended-fab/config.js +1 -1
- package/dist/components/extended-fab/types.d.ts +3 -2
- package/dist/components/list/features/renderer.js +2 -1
- package/dist/components/menu/config.js +6 -1
- package/dist/components/menu/features/controller.js +23 -8
- package/dist/components/menu/features/keyboard.js +3 -2
- package/dist/components/menu/features/layer.d.ts +15 -0
- package/dist/components/menu/features/layer.js +16 -0
- package/dist/components/menu/features/opener.js +2 -1
- package/dist/components/menu/features/position.js +8 -6
- package/dist/components/menu/features/submenu.js +12 -4
- package/dist/components/menu/types.d.ts +12 -0
- package/dist/components/navigation-rail/navigation-rail.js +4 -3
- package/dist/components/search/api.d.ts +9 -1
- package/dist/components/search/api.js +16 -8
- package/dist/components/search/config.d.ts +17 -1
- package/dist/components/search/config.js +10 -1
- package/dist/components/search/constants.d.ts +11 -0
- package/dist/components/search/constants.js +9 -0
- package/dist/components/search/features/input.js +12 -3
- package/dist/components/search/features/states.d.ts +4 -2
- package/dist/components/search/features/states.js +98 -16
- package/dist/components/search/features/structure.d.ts +7 -20
- package/dist/components/search/features/structure.js +79 -29
- package/dist/components/search/features/suggestions.js +22 -6
- package/dist/components/search/index.d.ts +1 -1
- package/dist/components/search/types.d.ts +23 -0
- package/dist/components/segmented-button/segment.js +1 -1
- package/dist/components/select/features.js +6 -3
- package/dist/components/select/types.d.ts +7 -0
- package/dist/components/side-sheet/config.js +11 -3
- package/dist/components/side-sheet/features/state.js +38 -2
- package/dist/components/side-sheet/features/structure.js +1 -1
- package/dist/components/side-sheet/types.d.ts +12 -0
- package/dist/components/slider/features/controller.js +6 -2
- package/dist/components/slider/features/handlers.js +2 -1
- package/dist/components/slider/features/states.d.ts +0 -3
- package/dist/components/slider/features/states.js +2 -15
- package/dist/components/snackbar/api.js +28 -4
- package/dist/components/snackbar/config.d.ts +1 -0
- package/dist/components/snackbar/config.js +8 -1
- package/dist/components/snackbar/layer.d.ts +23 -0
- package/dist/components/snackbar/layer.js +85 -0
- package/dist/components/snackbar/types.d.ts +12 -0
- package/dist/components/split-button/features/menu.js +1 -0
- package/dist/components/split-button/types.d.ts +6 -0
- package/dist/components/textfield/constants.d.ts +11 -1
- package/dist/components/textfield/constants.js +5 -0
- package/dist/components/textfield/features/placement.js +45 -82
- package/dist/components/timepicker/api.js +112 -55
- package/dist/components/timepicker/config.d.ts +0 -1082
- package/dist/components/timepicker/config.js +3 -40
- package/dist/components/timepicker/constants.d.ts +21 -11
- package/dist/components/timepicker/constants.js +4 -3
- package/dist/components/timepicker/dial.d.ts +35 -0
- package/dist/components/timepicker/dial.js +175 -0
- package/dist/components/timepicker/render.js +232 -333
- package/dist/components/timepicker/timepicker.d.ts +0 -5
- package/dist/components/timepicker/timepicker.js +7 -21
- package/dist/components/timepicker/types.d.ts +42 -10
- package/dist/components/timepicker/utils.d.ts +18 -51
- package/dist/components/timepicker/utils.js +36 -86
- package/dist/components/tooltip/api.js +34 -3
- package/dist/components/tooltip/config.js +8 -1
- package/dist/components/tooltip/types.d.ts +12 -0
- package/dist/core/dom/focus.d.ts +36 -0
- package/dist/core/dom/focus.js +47 -0
- package/dist/core/dom/index.d.ts +3 -0
- package/dist/core/dom/index.js +2 -0
- package/dist/core/dom/layer.d.ts +58 -0
- package/dist/core/dom/layer.js +52 -0
- package/dist/elements/badge.d.ts +62 -0
- package/dist/elements/badge.js +33 -0
- package/dist/elements/bottom-app-bar.d.ts +46 -0
- package/dist/elements/bottom-app-bar.js +42 -0
- package/dist/elements/bottom-sheet.d.ts +121 -0
- package/dist/elements/bottom-sheet.js +95 -0
- package/dist/elements/button-group.d.ts +153 -0
- package/dist/elements/button-group.js +174 -0
- package/dist/elements/button.d.ts +5 -8
- package/dist/elements/button.js +4 -19
- package/dist/elements/card.d.ts +68 -0
- package/dist/elements/card.js +112 -0
- package/dist/elements/carousel.d.ts +123 -0
- package/dist/elements/carousel.js +117 -0
- package/dist/elements/checkbox.d.ts +92 -0
- package/dist/elements/checkbox.js +49 -0
- package/dist/elements/chips.d.ts +136 -0
- package/dist/elements/chips.js +214 -0
- package/dist/elements/css/bottom-app-bar.d.ts +1 -0
- package/dist/elements/css/bottom-app-bar.js +3 -0
- package/dist/elements/css/bottom-sheet.d.ts +1 -0
- package/dist/elements/css/bottom-sheet.js +3 -0
- package/dist/elements/css/button-group.d.ts +1 -0
- package/dist/elements/css/button-group.js +5 -0
- package/dist/elements/css/card.d.ts +1 -0
- package/dist/elements/css/card.js +4 -0
- package/dist/elements/css/carousel.d.ts +1 -0
- package/dist/elements/css/carousel.js +3 -0
- package/dist/elements/css/checkbox.d.ts +1 -0
- package/dist/elements/css/checkbox.js +3 -0
- package/dist/elements/css/chips.d.ts +1 -0
- package/dist/elements/css/chips.js +3 -0
- package/dist/elements/css/datepicker.d.ts +1 -0
- package/dist/elements/css/datepicker.js +3 -0
- package/dist/elements/css/dialog.d.ts +1 -0
- package/dist/elements/css/dialog.js +5 -0
- package/dist/elements/css/divider.d.ts +1 -0
- package/dist/elements/css/divider.js +3 -0
- package/dist/elements/css/drawer.d.ts +1 -0
- package/dist/elements/css/drawer.js +3 -0
- package/dist/elements/css/extended-fab.d.ts +1 -0
- package/dist/elements/css/extended-fab.js +3 -0
- package/dist/elements/css/fab.d.ts +1 -0
- package/dist/elements/css/fab.js +3 -0
- package/dist/elements/css/icon-button.d.ts +1 -0
- package/dist/elements/css/icon-button.js +3 -0
- package/dist/elements/css/index.js +29 -0
- package/dist/elements/css/list.d.ts +1 -0
- package/dist/elements/css/list.js +3 -0
- package/dist/elements/css/loading-indicator.d.ts +1 -0
- package/dist/elements/css/loading-indicator.js +3 -0
- package/dist/elements/css/menu.d.ts +1 -0
- package/dist/elements/css/menu.js +3 -0
- package/dist/elements/css/navigation-rail.d.ts +1 -0
- package/dist/elements/css/navigation-rail.js +3 -0
- package/dist/elements/css/radios.d.ts +1 -0
- package/dist/elements/css/radios.js +3 -0
- package/dist/elements/css/search.d.ts +1 -0
- package/dist/elements/css/search.js +3 -0
- package/dist/elements/css/select.d.ts +1 -0
- package/dist/elements/css/select.js +5 -0
- package/dist/elements/css/side-sheet.d.ts +1 -0
- package/dist/elements/css/side-sheet.js +3 -0
- package/dist/elements/css/slider.d.ts +1 -0
- package/dist/elements/css/slider.js +3 -0
- package/dist/elements/css/snackbar.d.ts +1 -0
- package/dist/elements/css/snackbar.js +5 -0
- package/dist/elements/css/split-button.d.ts +1 -0
- package/dist/elements/css/split-button.js +5 -0
- package/dist/elements/css/switch.js +1 -1
- package/dist/elements/css/textfield.d.ts +1 -0
- package/dist/elements/css/textfield.js +3 -0
- package/dist/elements/css/timepicker.d.ts +1 -0
- package/dist/elements/css/timepicker.js +3 -0
- package/dist/elements/css/tooltip.d.ts +1 -0
- package/dist/elements/css/tooltip.js +3 -0
- package/dist/elements/css/top-app-bar.d.ts +1 -0
- package/dist/elements/css/top-app-bar.js +3 -0
- package/dist/elements/datepicker.d.ts +155 -0
- package/dist/elements/datepicker.js +150 -0
- package/dist/elements/define.d.ts +38 -6
- package/dist/elements/define.js +68 -4
- package/dist/elements/dialog.d.ts +118 -0
- package/dist/elements/dialog.js +155 -0
- package/dist/elements/divider.d.ts +54 -0
- package/dist/elements/divider.js +23 -0
- package/dist/elements/drawer.d.ts +137 -0
- package/dist/elements/drawer.js +136 -0
- package/dist/elements/extended-fab.d.ts +82 -0
- package/dist/elements/extended-fab.js +32 -0
- package/dist/elements/fab.d.ts +65 -0
- package/dist/elements/fab.js +24 -0
- package/dist/elements/form-button.d.ts +29 -0
- package/dist/elements/form-button.js +19 -0
- package/dist/elements/icon-button.d.ts +104 -0
- package/dist/elements/icon-button.js +56 -0
- package/dist/elements/index.d.ts +357 -1
- package/dist/elements/index.js +134 -0
- package/dist/elements/list.d.ts +135 -0
- package/dist/elements/list.js +191 -0
- package/dist/elements/loading-indicator.d.ts +44 -0
- package/dist/elements/loading-indicator.js +26 -0
- package/dist/elements/menu.d.ts +165 -0
- package/dist/elements/menu.js +178 -0
- package/dist/elements/navigation-rail.d.ts +153 -0
- package/dist/elements/navigation-rail.js +129 -0
- package/dist/elements/progress.d.ts +90 -0
- package/dist/elements/progress.js +40 -0
- package/dist/elements/radios.d.ts +99 -0
- package/dist/elements/radios.js +126 -0
- package/dist/elements/search.d.ts +215 -0
- package/dist/elements/search.js +215 -0
- package/dist/elements/select.d.ts +134 -0
- package/dist/elements/select.js +149 -0
- package/dist/elements/sheet.d.ts +44 -0
- package/dist/elements/sheet.js +60 -0
- package/dist/elements/side-sheet.d.ts +91 -0
- package/dist/elements/side-sheet.js +63 -0
- package/dist/elements/slider.d.ts +160 -0
- package/dist/elements/slider.js +136 -0
- package/dist/elements/snackbar.d.ts +99 -0
- package/dist/elements/snackbar.js +54 -0
- package/dist/elements/split-button.d.ts +82 -0
- package/dist/elements/split-button.js +77 -0
- package/dist/elements/switch.d.ts +2 -1
- package/dist/elements/tabs.d.ts +3 -2
- package/dist/elements/tabs.js +3 -5
- package/dist/elements/textfield.d.ts +160 -0
- package/dist/elements/textfield.js +107 -0
- package/dist/elements/timepicker.d.ts +158 -0
- package/dist/elements/timepicker.js +159 -0
- package/dist/elements/tooltip.d.ts +89 -0
- package/dist/elements/tooltip.js +132 -0
- package/dist/elements/top-app-bar.d.ts +63 -0
- package/dist/elements/top-app-bar.js +94 -0
- package/dist/index.cjs +0 -0
- package/dist/package.json +1 -1
- package/dist/react/index.d.ts +71 -1
- package/dist/react/index.js +71 -1
- package/dist/solid/index.d.ts +71 -1
- package/dist/solid/index.js +71 -1
- package/dist/styles/badge.css +1 -1
- package/dist/styles/base.css +1 -1
- package/dist/styles/bottom-app-bar.css +1 -1
- package/dist/styles/bottom-sheet.css +2 -2
- package/dist/styles/button-group.css +1 -1
- package/dist/styles/button.css +1 -1
- package/dist/styles/card.css +1 -1
- package/dist/styles/carousel.css +1 -1
- package/dist/styles/checkbox.css +1 -1
- package/dist/styles/chips.css +1 -1
- package/dist/styles/datepicker.css +2 -2
- package/dist/styles/dialog.css +2 -2
- package/dist/styles/divider.css +1 -1
- package/dist/styles/drawer.css +2 -2
- package/dist/styles/extended-fab.css +1 -1
- package/dist/styles/fab.css +1 -1
- package/dist/styles/icon-button.css +1 -1
- package/dist/styles/list.css +1 -1
- package/dist/styles/loading-indicator.css +1 -1
- package/dist/styles/menu.css +2 -2
- package/dist/styles/navigation-rail.css +1 -1
- package/dist/styles/progress.css +1 -1
- package/dist/styles/radios.css +2 -2
- package/dist/styles/search.css +2 -2
- package/dist/styles/segmented-button.css +1 -1
- package/dist/styles/select.css +1 -1
- package/dist/styles/side-sheet.css +2 -2
- package/dist/styles/slider.css +1 -1
- package/dist/styles/snackbar.css +2 -2
- package/dist/styles/split-button.css +1 -1
- package/dist/styles/switch.css +2 -2
- package/dist/styles/tabs.css +1 -1
- package/dist/styles/textfield.css +2 -2
- package/dist/styles/timepicker.css +2 -2
- package/dist/styles/tooltip.css +2 -2
- package/dist/styles/top-app-bar.css +1 -1
- package/dist/styles/utilities.css +1 -1
- package/dist/styles.css +2 -2
- package/dist/svelte/Badge.svelte +20 -0
- package/dist/svelte/Badge.svelte.d.ts +5 -0
- package/dist/svelte/BottomAppBar.svelte +19 -0
- package/dist/svelte/BottomAppBar.svelte.d.ts +5 -0
- package/dist/svelte/BottomSheet.svelte +19 -0
- package/dist/svelte/BottomSheet.svelte.d.ts +5 -0
- package/dist/svelte/ButtonGroup.svelte +20 -0
- package/dist/svelte/ButtonGroup.svelte.d.ts +5 -0
- package/dist/svelte/ButtonGroupItem.svelte +11 -0
- package/dist/svelte/ButtonGroupItem.svelte.d.ts +5 -0
- package/dist/svelte/Card.svelte +19 -0
- package/dist/svelte/Card.svelte.d.ts +5 -0
- package/dist/svelte/Carousel.svelte +20 -0
- package/dist/svelte/Carousel.svelte.d.ts +5 -0
- package/dist/svelte/CarouselItem.svelte +11 -0
- package/dist/svelte/CarouselItem.svelte.d.ts +5 -0
- package/dist/svelte/Checkbox.svelte +21 -0
- package/dist/svelte/Checkbox.svelte.d.ts +5 -0
- package/dist/svelte/Chip.svelte +11 -0
- package/dist/svelte/Chip.svelte.d.ts +5 -0
- package/dist/svelte/Chips.svelte +20 -0
- package/dist/svelte/Chips.svelte.d.ts +5 -0
- package/dist/svelte/Datepicker.svelte +20 -0
- package/dist/svelte/Datepicker.svelte.d.ts +5 -0
- package/dist/svelte/Dialog.svelte +19 -0
- package/dist/svelte/Dialog.svelte.d.ts +5 -0
- package/dist/svelte/Divider.svelte +19 -0
- package/dist/svelte/Divider.svelte.d.ts +5 -0
- package/dist/svelte/Drawer.svelte +20 -0
- package/dist/svelte/Drawer.svelte.d.ts +5 -0
- package/dist/svelte/DrawerItem.svelte +11 -0
- package/dist/svelte/DrawerItem.svelte.d.ts +5 -0
- package/dist/svelte/ExtendedFab.svelte +19 -0
- package/dist/svelte/ExtendedFab.svelte.d.ts +5 -0
- package/dist/svelte/Fab.svelte +19 -0
- package/dist/svelte/Fab.svelte.d.ts +5 -0
- package/dist/svelte/IconButton.svelte +20 -0
- package/dist/svelte/IconButton.svelte.d.ts +5 -0
- package/dist/svelte/List.svelte +21 -0
- package/dist/svelte/List.svelte.d.ts +5 -0
- package/dist/svelte/ListItem.svelte +11 -0
- package/dist/svelte/ListItem.svelte.d.ts +5 -0
- package/dist/svelte/LoadingIndicator.svelte +19 -0
- package/dist/svelte/LoadingIndicator.svelte.d.ts +5 -0
- package/dist/svelte/Menu.svelte +20 -0
- package/dist/svelte/Menu.svelte.d.ts +5 -0
- package/dist/svelte/MenuItem.svelte +11 -0
- package/dist/svelte/MenuItem.svelte.d.ts +5 -0
- package/dist/svelte/NavigationRail.svelte +20 -0
- package/dist/svelte/NavigationRail.svelte.d.ts +5 -0
- package/dist/svelte/NavigationRailItem.svelte +11 -0
- package/dist/svelte/NavigationRailItem.svelte.d.ts +5 -0
- package/dist/svelte/Progress.svelte +21 -0
- package/dist/svelte/Progress.svelte.d.ts +5 -0
- package/dist/svelte/Radio.svelte +11 -0
- package/dist/svelte/Radio.svelte.d.ts +5 -0
- package/dist/svelte/Radios.svelte +20 -0
- package/dist/svelte/Radios.svelte.d.ts +5 -0
- package/dist/svelte/Search.svelte +20 -0
- package/dist/svelte/Search.svelte.d.ts +5 -0
- package/dist/svelte/SearchSuggestion.svelte +11 -0
- package/dist/svelte/SearchSuggestion.svelte.d.ts +5 -0
- package/dist/svelte/Select.svelte +20 -0
- package/dist/svelte/Select.svelte.d.ts +5 -0
- package/dist/svelte/SelectOption.svelte +11 -0
- package/dist/svelte/SelectOption.svelte.d.ts +5 -0
- package/dist/svelte/SideSheet.svelte +19 -0
- package/dist/svelte/SideSheet.svelte.d.ts +5 -0
- package/dist/svelte/Slider.svelte +21 -0
- package/dist/svelte/Slider.svelte.d.ts +5 -0
- package/dist/svelte/Snackbar.svelte +20 -0
- package/dist/svelte/Snackbar.svelte.d.ts +5 -0
- package/dist/svelte/SplitButton.svelte +19 -0
- package/dist/svelte/SplitButton.svelte.d.ts +5 -0
- package/dist/svelte/Textfield.svelte +20 -0
- package/dist/svelte/Textfield.svelte.d.ts +5 -0
- package/dist/svelte/Timepicker.svelte +20 -0
- package/dist/svelte/Timepicker.svelte.d.ts +5 -0
- package/dist/svelte/Tooltip.svelte +20 -0
- package/dist/svelte/Tooltip.svelte.d.ts +5 -0
- package/dist/svelte/TopAppBar.svelte +19 -0
- package/dist/svelte/TopAppBar.svelte.d.ts +5 -0
- package/dist/svelte/index.d.ts +41 -0
- package/dist/svelte/index.js +70 -0
- package/dist/themes/autumn.css +1 -1
- package/dist/themes/baseline.css +1 -1
- package/dist/themes/brownbeige.css +1 -1
- package/dist/themes/browngreen.css +1 -1
- package/dist/themes/desert.css +1 -1
- package/dist/themes/forest.css +1 -1
- package/dist/themes/highcontrast.css +1 -1
- package/dist/themes/legacy.css +1 -1
- package/dist/themes/material.css +1 -1
- package/dist/themes/ocean.css +1 -1
- package/dist/themes/sageivory.css +1 -1
- package/dist/themes/spring.css +1 -1
- package/dist/themes/summer.css +1 -1
- package/dist/themes/sunset.css +1 -1
- package/dist/themes/tealcaramel.css +1 -1
- package/dist/themes/winter.css +1 -1
- package/dist/vue/index.d.ts +71 -1
- package/dist/vue/index.js +71 -1
- package/package.json +1 -1
- package/src/styles/components/_bottom-sheet.scss +42 -2
- package/src/styles/components/_datepicker.scss +52 -14
- package/src/styles/components/_dialog.scss +52 -0
- package/src/styles/components/_drawer.scss +42 -0
- package/src/styles/components/_menu.scss +9 -0
- package/src/styles/components/_radios.scss +2 -3
- package/src/styles/components/_search.scss +278 -273
- package/src/styles/components/_side-sheet.scss +41 -0
- package/src/styles/components/_snackbar.scss +12 -0
- package/src/styles/components/_switch.scss +2 -2
- package/src/styles/components/_textfield.scss +122 -70
- package/src/styles/components/_timepicker.scss +264 -198
- package/src/styles/components/_tooltip.scss +11 -0
- package/dist/components/timepicker/clockdial.d.ts +0 -34
- package/dist/components/timepicker/clockdial.js +0 -276
package/README.md
CHANGED
|
@@ -136,23 +136,44 @@ Button progress and card actions load on demand; enable code splitting in your b
|
|
|
136
136
|
|
|
137
137
|
## Web components and frameworks (experimental)
|
|
138
138
|
|
|
139
|
-
From
|
|
139
|
+
From the 0.10.0 prereleases (`npm install mtrl@next`), every component also ships as a custom element, with thin adapters for React, Vue, Svelte and Solid. The API may still change before 1.0.
|
|
140
|
+
|
|
141
|
+
| Kind | Elements | Framework components (Vue: `M` prefix, e.g. `MDialog`) |
|
|
142
|
+
|------|----------|--------------------------------------------------------|
|
|
143
|
+
| Actions | `<m-button>`, `<m-icon-button>`, `<m-fab>`, `<m-extended-fab>`, `<m-split-button>`, `<m-button-group>` + `<m-button-group-item>` | `Button`, `IconButton`, `Fab`, `ExtendedFab`, `SplitButton`, `ButtonGroup`, `ButtonGroupItem` |
|
|
144
|
+
| Form controls | `<m-checkbox>`, `<m-switch>`, `<m-radios>` + `<m-radio>`, `<m-slider>`, `<m-textfield>`, `<m-select>` + `<m-select-option>`, `<m-search>` + `<m-search-suggestion>`, `<m-datepicker>`, `<m-timepicker>` | `Checkbox`, `Switch`, `Radios`, `Radio`, `Slider`, `Textfield`, `Select`, `SelectOption`, `Search`, `SearchSuggestion`, `Datepicker`, `Timepicker` |
|
|
145
|
+
| Selection | `<m-chips>` + `<m-chip>`, `<m-tabs>` + `<m-tab>`, `<m-list>` + `<m-list-item>` | `Chips`, `Chip`, `Tabs`, `Tab`, `List`, `ListItem` |
|
|
146
|
+
| Navigation | `<m-navigation-rail>` + `<m-navigation-rail-item>`, `<m-drawer>` + `<m-drawer-item>`, `<m-top-app-bar>`, `<m-bottom-app-bar>` | `NavigationRail`, `NavigationRailItem`, `Drawer`, `DrawerItem`, `TopAppBar`, `BottomAppBar` |
|
|
147
|
+
| Overlays (top layer) | `<m-menu>` + `<m-menu-item>`, `<m-dialog>`, `<m-bottom-sheet>`, `<m-side-sheet>`, `<m-tooltip>`, `<m-snackbar>` | `Menu`, `MenuItem`, `Dialog`, `BottomSheet`, `SideSheet`, `Tooltip`, `Snackbar` |
|
|
148
|
+
| Containment | `<m-card>`, `<m-carousel>` + `<m-carousel-item>` | `Card`, `Carousel`, `CarouselItem` |
|
|
149
|
+
| Indicators | `<m-progress>`, `<m-loading-indicator>`, `<m-badge>`, `<m-divider>` | `Progress`, `LoadingIndicator`, `Badge`, `Divider` |
|
|
150
|
+
|
|
151
|
+
Overlays open in the browser's top layer (`showModal()` or `popover`) from inside their own shadow root, so they keep their styles, trap focus and make the page inert where M3 says so. Form controls are form-associated: `name`, the form value, reset, validation, `<label for>` and back-navigation restore work as on native controls.
|
|
140
152
|
|
|
141
153
|
```html
|
|
142
154
|
<script type="module">
|
|
143
155
|
import 'mtrl/elements/css'; // the elements' styles
|
|
144
156
|
import { defineAll } from 'mtrl/elements';
|
|
145
|
-
defineAll(); // registers <m-button>, <m-switch>,
|
|
157
|
+
defineAll(); // registers every element (<m-button>, <m-switch>, …)
|
|
146
158
|
</script>
|
|
147
159
|
|
|
148
|
-
<
|
|
160
|
+
<form>
|
|
161
|
+
<m-textfield name="email" type="email" label="Email" required></m-textfield>
|
|
162
|
+
<m-radios name="plan" value="free" aria-label="Plan">
|
|
163
|
+
<m-radio value="free">Free</m-radio>
|
|
164
|
+
<m-radio value="pro">Pro</m-radio>
|
|
165
|
+
</m-radios>
|
|
166
|
+
<m-switch name="news" checked>Newsletter</m-switch>
|
|
167
|
+
<m-button type="submit">Sign up</m-button>
|
|
168
|
+
</form>
|
|
169
|
+
|
|
149
170
|
<m-tabs value="songs">
|
|
150
171
|
<m-tab value="songs">Songs</m-tab>
|
|
151
172
|
<m-tab value="albums">Albums</m-tab>
|
|
152
173
|
</m-tabs>
|
|
153
174
|
```
|
|
154
175
|
|
|
155
|
-
The page still loads mtrl's base styles and a theme (see [Styles](#styles)); the elements render in shadow DOM and pick up the theme's tokens. Attributes are defaults and properties the live state, as on native controls
|
|
176
|
+
The page still loads mtrl's base styles and a theme (see [Styles](#styles)); the elements render in shadow DOM and pick up the theme's tokens. Attributes are defaults and properties the live state, as on native controls: a `checked` or `value` attribute sets the state until the user or a script changes it, and `form.reset()` goes back to it. Form controls take part in forms (their host's `name`), reset, validation and back-navigation restore.
|
|
156
177
|
|
|
157
178
|
| Framework | Import | Two-way binding |
|
|
158
179
|
|-----------|--------|-----------------|
|
|
@@ -161,7 +182,7 @@ The page still loads mtrl's base styles and a theme (see [Styles](#styles)); the
|
|
|
161
182
|
| Svelte 5 | `import { Switch } from 'mtrl/svelte'` | `bind:checked` |
|
|
162
183
|
| Solid | `import { Switch } from 'mtrl/solid'` | `checked` + `onChange` |
|
|
163
184
|
|
|
164
|
-
Each framework is an optional peer dependency; mtrl installs none of them. All adapters render on the server and hydrate.
|
|
185
|
+
Each framework is an optional peer dependency; mtrl installs none of them. All adapters render on the server and hydrate. Angular apps use the elements directly, with `CUSTOM_ELEMENTS_SCHEMA`.
|
|
165
186
|
|
|
166
187
|
## Building your own components
|
|
167
188
|
|
package/dist/README.md
CHANGED
|
@@ -136,23 +136,44 @@ Button progress and card actions load on demand; enable code splitting in your b
|
|
|
136
136
|
|
|
137
137
|
## Web components and frameworks (experimental)
|
|
138
138
|
|
|
139
|
-
From
|
|
139
|
+
From the 0.10.0 prereleases (`npm install mtrl@next`), every component also ships as a custom element, with thin adapters for React, Vue, Svelte and Solid. The API may still change before 1.0.
|
|
140
|
+
|
|
141
|
+
| Kind | Elements | Framework components (Vue: `M` prefix, e.g. `MDialog`) |
|
|
142
|
+
|------|----------|--------------------------------------------------------|
|
|
143
|
+
| Actions | `<m-button>`, `<m-icon-button>`, `<m-fab>`, `<m-extended-fab>`, `<m-split-button>`, `<m-button-group>` + `<m-button-group-item>` | `Button`, `IconButton`, `Fab`, `ExtendedFab`, `SplitButton`, `ButtonGroup`, `ButtonGroupItem` |
|
|
144
|
+
| Form controls | `<m-checkbox>`, `<m-switch>`, `<m-radios>` + `<m-radio>`, `<m-slider>`, `<m-textfield>`, `<m-select>` + `<m-select-option>`, `<m-search>` + `<m-search-suggestion>`, `<m-datepicker>`, `<m-timepicker>` | `Checkbox`, `Switch`, `Radios`, `Radio`, `Slider`, `Textfield`, `Select`, `SelectOption`, `Search`, `SearchSuggestion`, `Datepicker`, `Timepicker` |
|
|
145
|
+
| Selection | `<m-chips>` + `<m-chip>`, `<m-tabs>` + `<m-tab>`, `<m-list>` + `<m-list-item>` | `Chips`, `Chip`, `Tabs`, `Tab`, `List`, `ListItem` |
|
|
146
|
+
| Navigation | `<m-navigation-rail>` + `<m-navigation-rail-item>`, `<m-drawer>` + `<m-drawer-item>`, `<m-top-app-bar>`, `<m-bottom-app-bar>` | `NavigationRail`, `NavigationRailItem`, `Drawer`, `DrawerItem`, `TopAppBar`, `BottomAppBar` |
|
|
147
|
+
| Overlays (top layer) | `<m-menu>` + `<m-menu-item>`, `<m-dialog>`, `<m-bottom-sheet>`, `<m-side-sheet>`, `<m-tooltip>`, `<m-snackbar>` | `Menu`, `MenuItem`, `Dialog`, `BottomSheet`, `SideSheet`, `Tooltip`, `Snackbar` |
|
|
148
|
+
| Containment | `<m-card>`, `<m-carousel>` + `<m-carousel-item>` | `Card`, `Carousel`, `CarouselItem` |
|
|
149
|
+
| Indicators | `<m-progress>`, `<m-loading-indicator>`, `<m-badge>`, `<m-divider>` | `Progress`, `LoadingIndicator`, `Badge`, `Divider` |
|
|
150
|
+
|
|
151
|
+
Overlays open in the browser's top layer (`showModal()` or `popover`) from inside their own shadow root, so they keep their styles, trap focus and make the page inert where M3 says so. Form controls are form-associated: `name`, the form value, reset, validation, `<label for>` and back-navigation restore work as on native controls.
|
|
140
152
|
|
|
141
153
|
```html
|
|
142
154
|
<script type="module">
|
|
143
155
|
import 'mtrl/elements/css'; // the elements' styles
|
|
144
156
|
import { defineAll } from 'mtrl/elements';
|
|
145
|
-
defineAll(); // registers <m-button>, <m-switch>,
|
|
157
|
+
defineAll(); // registers every element (<m-button>, <m-switch>, …)
|
|
146
158
|
</script>
|
|
147
159
|
|
|
148
|
-
<
|
|
160
|
+
<form>
|
|
161
|
+
<m-textfield name="email" type="email" label="Email" required></m-textfield>
|
|
162
|
+
<m-radios name="plan" value="free" aria-label="Plan">
|
|
163
|
+
<m-radio value="free">Free</m-radio>
|
|
164
|
+
<m-radio value="pro">Pro</m-radio>
|
|
165
|
+
</m-radios>
|
|
166
|
+
<m-switch name="news" checked>Newsletter</m-switch>
|
|
167
|
+
<m-button type="submit">Sign up</m-button>
|
|
168
|
+
</form>
|
|
169
|
+
|
|
149
170
|
<m-tabs value="songs">
|
|
150
171
|
<m-tab value="songs">Songs</m-tab>
|
|
151
172
|
<m-tab value="albums">Albums</m-tab>
|
|
152
173
|
</m-tabs>
|
|
153
174
|
```
|
|
154
175
|
|
|
155
|
-
The page still loads mtrl's base styles and a theme (see [Styles](#styles)); the elements render in shadow DOM and pick up the theme's tokens. Attributes are defaults and properties the live state, as on native controls
|
|
176
|
+
The page still loads mtrl's base styles and a theme (see [Styles](#styles)); the elements render in shadow DOM and pick up the theme's tokens. Attributes are defaults and properties the live state, as on native controls: a `checked` or `value` attribute sets the state until the user or a script changes it, and `form.reset()` goes back to it. Form controls take part in forms (their host's `name`), reset, validation and back-navigation restore.
|
|
156
177
|
|
|
157
178
|
| Framework | Import | Two-way binding |
|
|
158
179
|
|-----------|--------|-----------------|
|
|
@@ -161,7 +182,7 @@ The page still loads mtrl's base styles and a theme (see [Styles](#styles)); the
|
|
|
161
182
|
| Svelte 5 | `import { Switch } from 'mtrl/svelte'` | `bind:checked` |
|
|
162
183
|
| Solid | `import { Switch } from 'mtrl/solid'` | `checked` + `onChange` |
|
|
163
184
|
|
|
164
|
-
Each framework is an optional peer dependency; mtrl installs none of them. All adapters render on the server and hydrate.
|
|
185
|
+
Each framework is an optional peer dependency; mtrl installs none of them. All adapters render on the server and hydrate. Angular apps use the elements directly, with `CUSTOM_ELEMENTS_SCHEMA`.
|
|
165
186
|
|
|
166
187
|
## Building your own components
|
|
167
188
|
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import { createComponentConfig, createElementConfig, } from "../../core/config/component.js";
|
|
2
|
-
import { BOTTOM_SHEET_DEFAULTS, BOTTOM_SHEET_STATES, } from "./constants.js";
|
|
2
|
+
import { BOTTOM_SHEET_DEFAULTS, BOTTOM_SHEET_STATES, BOTTOM_SHEET_VARIANTS, } from "./constants.js";
|
|
3
|
+
import { supportsTopLayer } from "../../core/dom/layer.js";
|
|
3
4
|
export const defaultConfig = {
|
|
4
5
|
variant: BOTTOM_SHEET_DEFAULTS.VARIANT,
|
|
5
6
|
dragHandle: BOTTOM_SHEET_DEFAULTS.DRAG_HANDLE,
|
|
6
|
-
peekHeight: BOTTOM_SHEET_DEFAULTS.PEEK_HEIGHT,
|
|
7
7
|
maxWidth: BOTTOM_SHEET_DEFAULTS.MAX_WIDTH,
|
|
8
8
|
initialState: BOTTOM_SHEET_STATES.HIDDEN,
|
|
9
9
|
closeOnScrimClick: BOTTOM_SHEET_DEFAULTS.CLOSE_ON_SCRIM_CLICK,
|
|
10
10
|
closeOnEscape: BOTTOM_SHEET_DEFAULTS.CLOSE_ON_ESCAPE,
|
|
11
11
|
};
|
|
12
|
-
export const createBaseConfig = (config = {}) =>
|
|
12
|
+
export const createBaseConfig = (config = {}) => {
|
|
13
|
+
const merged = createComponentConfig(defaultConfig, config, "bottom-sheet");
|
|
14
|
+
if (merged.layer === "top" &&
|
|
15
|
+
(merged.variant !== BOTTOM_SHEET_VARIANTS.MODAL || !supportsTopLayer("modal"))) {
|
|
16
|
+
merged.layer = undefined;
|
|
17
|
+
}
|
|
18
|
+
return merged;
|
|
19
|
+
};
|
|
13
20
|
export const getElementConfig = (config) => createElementConfig(config, {
|
|
14
|
-
tag: "div",
|
|
21
|
+
tag: config.layer === "top" ? "dialog" : "div",
|
|
15
22
|
className: [config.class].filter(Boolean),
|
|
16
23
|
attributes: {
|
|
17
24
|
"aria-hidden": "true",
|
|
@@ -25,8 +25,6 @@ export declare const BOTTOM_SHEET_STATES: {
|
|
|
25
25
|
*/
|
|
26
26
|
export declare const BOTTOM_SHEET_DEFAULTS: {
|
|
27
27
|
readonly VARIANT: "modal";
|
|
28
|
-
/** BottomSheetDefaults.SheetPeekHeight */
|
|
29
|
-
readonly PEEK_HEIGHT: 56;
|
|
30
28
|
/** BottomSheetDefaults.SheetMaxWidth; wider viewports centre the sheet */
|
|
31
29
|
readonly MAX_WIDTH: 640;
|
|
32
30
|
/** A drag handle is part of the anatomy, so it is on unless asked otherwise */
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { BOTTOM_SHEET_CLASSES, BOTTOM_SHEET_EVENTS, BOTTOM_SHEET_STATES, BOTTOM_SHEET_VARIANTS, } from "../constants.js";
|
|
2
|
+
import { deepActiveElement, wrapTab } from "../../../core/dom/focus.js";
|
|
3
|
+
import { hideFromTopLayer, onTopLayerClose, showInTopLayer, } from "../../../core/dom/layer.js";
|
|
2
4
|
export const withState = (config) => (component) => {
|
|
3
5
|
const { element, getClass, structure } = component;
|
|
4
6
|
const root = getClass(BOTTOM_SHEET_CLASSES.ROOT);
|
|
5
7
|
const isModal = config.variant === BOTTOM_SHEET_VARIANTS.MODAL;
|
|
8
|
+
const top = config.layer === "top";
|
|
6
9
|
let state = config.initialState ?? BOTTOM_SHEET_STATES.HIDDEN;
|
|
7
10
|
let previouslyFocused = null;
|
|
8
11
|
const applyState = (next) => {
|
|
@@ -35,12 +38,27 @@ export const withState = (config) => (component) => {
|
|
|
35
38
|
if (config.closeOnScrimClick)
|
|
36
39
|
close();
|
|
37
40
|
};
|
|
41
|
+
const handleBackdropClick = (event) => {
|
|
42
|
+
if (event.target === element)
|
|
43
|
+
handleScrimClick();
|
|
44
|
+
};
|
|
45
|
+
const handleTab = (event) => wrapTab(element, event);
|
|
46
|
+
const handleCancel = (event) => {
|
|
47
|
+
event.preventDefault();
|
|
48
|
+
if (config.closeOnEscape)
|
|
49
|
+
close();
|
|
50
|
+
};
|
|
38
51
|
function open(to = BOTTOM_SHEET_STATES.PARTIAL) {
|
|
39
52
|
if (state === to)
|
|
40
53
|
return;
|
|
41
54
|
const wasHidden = state === BOTTOM_SHEET_STATES.HIDDEN;
|
|
42
55
|
if (wasHidden && isModal) {
|
|
43
|
-
previouslyFocused =
|
|
56
|
+
previouslyFocused = deepActiveElement();
|
|
57
|
+
}
|
|
58
|
+
if (wasHidden && top) {
|
|
59
|
+
if (!showInTopLayer(element, { kind: "modal" }))
|
|
60
|
+
element.setAttribute("open", "");
|
|
61
|
+
void element.offsetWidth;
|
|
44
62
|
}
|
|
45
63
|
setState(to);
|
|
46
64
|
if (wasHidden) {
|
|
@@ -53,6 +71,8 @@ export const withState = (config) => (component) => {
|
|
|
53
71
|
if (state === BOTTOM_SHEET_STATES.HIDDEN)
|
|
54
72
|
return;
|
|
55
73
|
setState(BOTTOM_SHEET_STATES.HIDDEN);
|
|
74
|
+
if (top)
|
|
75
|
+
hideFromTopLayer(element);
|
|
56
76
|
if (isModal && previouslyFocused?.isConnected) {
|
|
57
77
|
previouslyFocused.focus();
|
|
58
78
|
previouslyFocused = null;
|
|
@@ -60,8 +80,19 @@ export const withState = (config) => (component) => {
|
|
|
60
80
|
component.emit(BOTTOM_SHEET_EVENTS.CLOSE);
|
|
61
81
|
}
|
|
62
82
|
applyState(state);
|
|
63
|
-
|
|
83
|
+
let stopCloses = null;
|
|
84
|
+
if (top) {
|
|
85
|
+
element.addEventListener("cancel", handleCancel);
|
|
86
|
+
element.addEventListener("click", handleBackdropClick);
|
|
87
|
+
element.addEventListener("keydown", handleTab);
|
|
88
|
+
stopCloses = onTopLayerClose(element, close);
|
|
89
|
+
if (state !== BOTTOM_SHEET_STATES.HIDDEN && element.isConnected) {
|
|
90
|
+
showInTopLayer(element, { kind: "modal" });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (config.closeOnEscape) {
|
|
64
94
|
document.addEventListener("keydown", handleKeydown);
|
|
95
|
+
}
|
|
65
96
|
structure.scrim?.addEventListener("click", handleScrimClick);
|
|
66
97
|
return {
|
|
67
98
|
...component,
|
|
@@ -76,6 +107,12 @@ export const withState = (config) => (component) => {
|
|
|
76
107
|
release: () => {
|
|
77
108
|
document.removeEventListener("keydown", handleKeydown);
|
|
78
109
|
structure.scrim?.removeEventListener("click", handleScrimClick);
|
|
110
|
+
element.removeEventListener("cancel", handleCancel);
|
|
111
|
+
element.removeEventListener("click", handleBackdropClick);
|
|
112
|
+
element.removeEventListener("keydown", handleTab);
|
|
113
|
+
stopCloses?.();
|
|
114
|
+
if (top)
|
|
115
|
+
hideFromTopLayer(element);
|
|
79
116
|
},
|
|
80
117
|
},
|
|
81
118
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BOTTOM_SHEET_CLASSES, BOTTOM_SHEET_VARIANTS } from "../constants.js";
|
|
2
|
+
import { PREFIX } from "../../../core/config.js";
|
|
2
3
|
import { setHTML } from "../../../core/dom/html.js";
|
|
3
4
|
export const withStructure = (config) => (component) => {
|
|
4
5
|
const { element, getClass } = component;
|
|
@@ -6,7 +7,7 @@ export const withStructure = (config) => (component) => {
|
|
|
6
7
|
element.classList.add(getClass(BOTTOM_SHEET_CLASSES.ROOT));
|
|
7
8
|
element.classList.add(`${getClass(BOTTOM_SHEET_CLASSES.ROOT)}--${config.variant}`);
|
|
8
9
|
let scrim = null;
|
|
9
|
-
if (isModal) {
|
|
10
|
+
if (isModal && config.layer !== "top") {
|
|
10
11
|
scrim = document.createElement("div");
|
|
11
12
|
scrim.className = getClass(BOTTOM_SHEET_CLASSES.SCRIM);
|
|
12
13
|
element.appendChild(scrim);
|
|
@@ -20,6 +21,9 @@ export const withStructure = (config) => (component) => {
|
|
|
20
21
|
if (config.maxWidth) {
|
|
21
22
|
container.style.maxWidth = `${config.maxWidth}px`;
|
|
22
23
|
}
|
|
24
|
+
if (config.peekHeight) {
|
|
25
|
+
container.style.setProperty(`--${PREFIX}-bottom-sheet-peek-height`, `${config.peekHeight}px`);
|
|
26
|
+
}
|
|
23
27
|
let handle = null;
|
|
24
28
|
if (config.dragHandle) {
|
|
25
29
|
handle = document.createElement("div");
|
|
@@ -44,8 +44,9 @@ export interface BottomSheetConfig {
|
|
|
44
44
|
*/
|
|
45
45
|
dragHandle?: boolean;
|
|
46
46
|
/**
|
|
47
|
-
* Height of the partially expanded state, in pixels
|
|
48
|
-
*
|
|
47
|
+
* Height of the partially expanded state, in pixels: a sheet with more
|
|
48
|
+
* content shows this much of it until it is expanded. Unset, the partial
|
|
49
|
+
* sheet shows its content up to half the screen.
|
|
49
50
|
*/
|
|
50
51
|
peekHeight?: number;
|
|
51
52
|
/**
|
|
@@ -72,6 +73,18 @@ export interface BottomSheetConfig {
|
|
|
72
73
|
closeOnEscape?: boolean;
|
|
73
74
|
/** Where to mount the sheet. Defaults to `document.body` */
|
|
74
75
|
container?: HTMLElement;
|
|
76
|
+
/**
|
|
77
|
+
* `"top"` makes a modal sheet's root a `<dialog>` element, rendered in
|
|
78
|
+
* `container` like any sheet, and opens it with `showModal()`: in the
|
|
79
|
+
* browser's top layer, above every z-index and outside any clipping
|
|
80
|
+
* ancestor, with the rest of the page inert. The scrim is its `::backdrop`,
|
|
81
|
+
* and Escape reaches the sheet as the dialog's `cancel` event. The sheet
|
|
82
|
+
* slides as without it.
|
|
83
|
+
*
|
|
84
|
+
* A standard sheet stays in the page, and where the browser has no
|
|
85
|
+
* `showModal()` the sheet behaves as without it.
|
|
86
|
+
*/
|
|
87
|
+
layer?: "top";
|
|
75
88
|
/** Handlers registered at creation, equivalent to calling `on` for each */
|
|
76
89
|
on?: BottomSheetEventHandlers;
|
|
77
90
|
/** Extra classes for the root element */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getCleanup } from "../../../core/compose/cleanup.js";
|
|
2
2
|
import createChip from "../chip/chip.js";
|
|
3
3
|
import { CHIPS_EVENTS } from "../constants.js";
|
|
4
|
+
import { activeElementOf } from "../../../core/dom/focus.js";
|
|
4
5
|
export const withController = (config) => (component) => {
|
|
5
6
|
const eventListeners = {
|
|
6
7
|
change: [],
|
|
@@ -157,7 +158,7 @@ export const withController = (config) => (component) => {
|
|
|
157
158
|
: component.chipInstances.indexOf(chipOrIndex);
|
|
158
159
|
if (index >= 0 && index < component.chipInstances.length) {
|
|
159
160
|
const chip = component.chipInstances[index];
|
|
160
|
-
const hadFocus = chip.element.contains(
|
|
161
|
+
const hadFocus = chip.element.contains(activeElementOf(chip.element));
|
|
161
162
|
dispatchEvent(CHIPS_EVENTS.REMOVE, chip);
|
|
162
163
|
chip.element.removeEventListener("keydown", handleKeyboardNavigation);
|
|
163
164
|
chip.destroy();
|
|
@@ -101,4 +101,5 @@ export declare const DATEPICKER_ICONS: {
|
|
|
101
101
|
readonly edit: "<svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"m3 17.25 11.06-11.06 3.75 3.75L6.75 21H3v-3.75ZM20.71 7.04l-1.83 1.83-3.75-3.75 1.83-1.83a1 1 0 0 1 1.41 0l2.34 2.34a1 1 0 0 1 0 1.41Z\"/></svg>";
|
|
102
102
|
readonly previous: "<svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"m15.4 7.4-1.4-1.4-6 6 6 6 1.4-1.4-4.6-4.6Z\"/></svg>";
|
|
103
103
|
readonly next: "<svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"m8.6 7.4 1.4-1.4 6 6-6 6-1.4-1.4 4.6-4.6Z\"/></svg>";
|
|
104
|
+
readonly close: "<svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12Z\"/></svg>";
|
|
104
105
|
};
|
|
@@ -53,4 +53,5 @@ export const DATEPICKER_ICONS = {
|
|
|
53
53
|
edit: '<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><path d="m3 17.25 11.06-11.06 3.75 3.75L6.75 21H3v-3.75ZM20.71 7.04l-1.83 1.83-3.75-3.75 1.83-1.83a1 1 0 0 1 1.41 0l2.34 2.34a1 1 0 0 1 0 1.41Z"/></svg>',
|
|
54
54
|
previous: '<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><path d="m15.4 7.4-1.4-1.4-6 6 6 6 1.4-1.4-4.6-4.6Z"/></svg>',
|
|
55
55
|
next: '<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><path d="m8.6 7.4 1.4-1.4 6 6-6 6-1.4-1.4 4.6-4.6Z"/></svg>',
|
|
56
|
+
close: '<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12Z"/></svg>',
|
|
56
57
|
};
|
|
@@ -3,6 +3,7 @@ import { createBase, withElement } from "../../core/compose/component.js";
|
|
|
3
3
|
import { withEvents, withDisabled, withLifecycle } from "../../core/compose/features/index.js";
|
|
4
4
|
import { createElement } from "../../core/dom/create.js";
|
|
5
5
|
import { setHTML } from "../../core/dom/html.js";
|
|
6
|
+
import { activeElementOf, deepActiveElement } from "../../core/dom/focus.js";
|
|
6
7
|
import { DATEPICKER_ICONS } from "./constants.js";
|
|
7
8
|
import { PREFIX } from "../../core/config.js";
|
|
8
9
|
import { createBaseConfig, getContainerConfig } from "./config.js";
|
|
@@ -33,10 +34,10 @@ const createDatePicker = (config = {}) => {
|
|
|
33
34
|
const today = parseDate(new Date());
|
|
34
35
|
const state = {
|
|
35
36
|
id, prefix: PREFIX, label: settings.label || "Select date", selectedDate: null, rangeEndDate: null,
|
|
36
|
-
currentView: ["month", "year"].includes(settings.initialView ?? "") ? settings.initialView : "day",
|
|
37
|
+
currentView: settings.variant !== "fullscreen" && ["month", "year"].includes(settings.initialView ?? "") ? settings.initialView : "day",
|
|
37
38
|
currentMonth: today.getMonth(), currentYear: today.getFullYear(), focusedDate: today,
|
|
38
39
|
minDate: parseDate(settings.minDate ?? null), maxDate: parseDate(settings.maxDate ?? null),
|
|
39
|
-
dateFormat: settings.dateFormat || "MM/DD/YYYY", variant: settings.variant === "modal" || settings.variant === "modal-input" ? settings.variant : "docked",
|
|
40
|
+
dateFormat: settings.dateFormat || "MM/DD/YYYY", variant: settings.variant === "modal" || settings.variant === "modal-input" || settings.variant === "fullscreen" ? settings.variant : "docked",
|
|
40
41
|
selectionMode: settings.selectionMode === "range" ? "range" : "single", inputMode: settings.variant === "modal-input",
|
|
41
42
|
specialDates: (settings.specialDates ?? []).map(item => ({ ...item })),
|
|
42
43
|
isAllowed: date => (!state.minDate || date >= state.minDate) && (!state.maxDate || date <= state.maxDate) && !state.specialDates.some(item => { const special = parseDate(item.date); return item.disabled && special && isSameDay(date, special); }),
|
|
@@ -73,6 +74,8 @@ const createDatePicker = (config = {}) => {
|
|
|
73
74
|
dialog.id = `${id}-dialog`;
|
|
74
75
|
dialog.className = cls("calendar");
|
|
75
76
|
dialog.classList.add(base.getClass(`datepicker--${modal ? "modal" : "docked"}`));
|
|
77
|
+
if (state.variant === "fullscreen")
|
|
78
|
+
dialog.classList.add(base.getClass("datepicker--fullscreen"));
|
|
76
79
|
dialog.setAttribute("aria-modal", String(modal));
|
|
77
80
|
if (settings.animate)
|
|
78
81
|
dialog.classList.add(base.getClass("datepicker--animate"));
|
|
@@ -105,11 +108,66 @@ const createDatePicker = (config = {}) => {
|
|
|
105
108
|
const element = track();
|
|
106
109
|
if (element?.clientWidth)
|
|
107
110
|
element.scrollLeft = (rtl(element) ? -1 : 1) * element.clientWidth;
|
|
111
|
+
const years = dialog.querySelector(`.${cls("years")}`);
|
|
112
|
+
const year = years?.querySelector('[aria-pressed="true"]');
|
|
113
|
+
if (years && year)
|
|
114
|
+
years.scrollTop = year.offsetTop - (years.clientHeight - year.offsetHeight) / 2;
|
|
115
|
+
};
|
|
116
|
+
const monthIndex = (date) => date.getFullYear() * 12 + date.getMonth();
|
|
117
|
+
const fromIndex = (index) => new Date(Math.floor(index / 12), index % 12, 1);
|
|
118
|
+
const list = () => dialog.querySelector(`.${cls("list")}`);
|
|
119
|
+
const limits = () => [state.minDate ? monthIndex(state.minDate) : -Infinity, state.maxDate ? monthIndex(state.maxDate) : Infinity];
|
|
120
|
+
let shiftBy = null;
|
|
121
|
+
const ensureWindow = () => {
|
|
122
|
+
if (state.variant !== "fullscreen")
|
|
123
|
+
return;
|
|
124
|
+
const focus = state.currentYear * 12 + state.currentMonth;
|
|
125
|
+
const start = state.listStart ? monthIndex(state.listStart) : NaN;
|
|
126
|
+
if (start <= focus && focus < start + (state.listLength ?? 0))
|
|
127
|
+
return;
|
|
128
|
+
const [min, max] = limits();
|
|
129
|
+
const first = Math.max(min, focus - 12), last = Math.min(max, focus + 12);
|
|
130
|
+
state.listStart = fromIndex(first);
|
|
131
|
+
state.listLength = last - first + 1;
|
|
132
|
+
};
|
|
133
|
+
const extend = (element) => {
|
|
134
|
+
const [min, max] = limits();
|
|
135
|
+
const start = monthIndex(state.listStart), end = start + state.listLength - 1;
|
|
136
|
+
const margin = element.clientHeight;
|
|
137
|
+
if (element.scrollTop < margin && start > min) {
|
|
138
|
+
const first = Math.max(min, start - 12);
|
|
139
|
+
state.listStart = fromIndex(first);
|
|
140
|
+
state.listLength = end - first + 1;
|
|
141
|
+
shiftBy = element.scrollHeight;
|
|
142
|
+
}
|
|
143
|
+
else if (element.scrollHeight - element.scrollTop - element.clientHeight < margin && end < max) {
|
|
144
|
+
state.listLength = Math.min(max, end + 12) - start + 1;
|
|
145
|
+
}
|
|
146
|
+
else
|
|
147
|
+
return;
|
|
148
|
+
const focused = activeElementOf(dialog);
|
|
149
|
+
const active = focused instanceof HTMLElement ? focused.dataset.date : undefined;
|
|
150
|
+
render();
|
|
151
|
+
if (active)
|
|
152
|
+
dialog.querySelector(`[data-date="${active}"]`)?.focus({ preventScroll: true });
|
|
153
|
+
};
|
|
154
|
+
const reveal = () => {
|
|
155
|
+
const element = list();
|
|
156
|
+
const section = element?.querySelector(`[data-month-key="${state.currentYear}-${state.currentMonth}"]`);
|
|
157
|
+
if (element && section)
|
|
158
|
+
element.scrollTop = section.offsetTop;
|
|
108
159
|
};
|
|
109
160
|
const render = (focus = false, action) => {
|
|
110
161
|
if (!opened || destroyed)
|
|
111
162
|
return;
|
|
163
|
+
ensureWindow();
|
|
164
|
+
const previous = list();
|
|
165
|
+
const kept = previous && { top: previous.scrollTop, height: previous.scrollHeight };
|
|
112
166
|
dialog.replaceChildren(renderCalendar(state), announcement);
|
|
167
|
+
const current = list();
|
|
168
|
+
if (current && kept)
|
|
169
|
+
current.scrollTop = kept.top + (shiftBy === null ? 0 : current.scrollHeight - kept.height);
|
|
170
|
+
shiftBy = null;
|
|
113
171
|
centre();
|
|
114
172
|
announcement.textContent = new Date(state.currentYear, state.currentMonth, 1).toLocaleDateString("en-US", { month: "long", year: "numeric" });
|
|
115
173
|
if (action)
|
|
@@ -150,6 +208,7 @@ const createDatePicker = (config = {}) => {
|
|
|
150
208
|
opened = true;
|
|
151
209
|
resetDraft();
|
|
152
210
|
state.inputMode = state.variant === "modal-input";
|
|
211
|
+
state.listStart = undefined;
|
|
153
212
|
let initial = committed ?? today;
|
|
154
213
|
if (state.minDate && initial < state.minDate)
|
|
155
214
|
initial = state.minDate;
|
|
@@ -158,7 +217,8 @@ const createDatePicker = (config = {}) => {
|
|
|
158
217
|
if (!navigationRequested)
|
|
159
218
|
setDisplayDate(initial);
|
|
160
219
|
navigationRequested = false;
|
|
161
|
-
|
|
220
|
+
const opener = deepActiveElement();
|
|
221
|
+
returnFocus = opener instanceof HTMLElement && opener !== doc.body ? opener : trigger;
|
|
162
222
|
render();
|
|
163
223
|
if (modal && typeof dialog.showModal === "function" && dialog.isConnected)
|
|
164
224
|
dialog.showModal();
|
|
@@ -169,6 +229,7 @@ const createDatePicker = (config = {}) => {
|
|
|
169
229
|
if (modal)
|
|
170
230
|
unlock = lockScroll(doc);
|
|
171
231
|
centre();
|
|
232
|
+
reveal();
|
|
172
233
|
trigger.setAttribute("aria-expanded", "true");
|
|
173
234
|
focusCurrent();
|
|
174
235
|
base.emit("open", { value: getValue() });
|
|
@@ -256,7 +317,7 @@ const createDatePicker = (config = {}) => {
|
|
|
256
317
|
render(true);
|
|
257
318
|
};
|
|
258
319
|
const navigate = (amount) => {
|
|
259
|
-
const count = state.currentView === "day" ? amount :
|
|
320
|
+
const count = state.currentView === "day" ? amount : amount * 12;
|
|
260
321
|
const anchor = new Date(state.currentYear, state.currentMonth, 1);
|
|
261
322
|
anchor.setDate(Math.min(state.focusedDate.getDate(), new Date(state.currentYear, state.currentMonth + 1, 0).getDate()));
|
|
262
323
|
setDisplayDate(addMonths(anchor, count));
|
|
@@ -349,11 +410,12 @@ const createDatePicker = (config = {}) => {
|
|
|
349
410
|
if (event.key === "Tab" && modal) {
|
|
350
411
|
const elements = Array.from(dialog.querySelectorAll('button:not(:disabled):not([tabindex="-1"]), input:not(:disabled)'));
|
|
351
412
|
const first = elements[0], last = elements.at(-1);
|
|
352
|
-
|
|
413
|
+
const focused = activeElementOf(dialog);
|
|
414
|
+
if (event.shiftKey && focused === first) {
|
|
353
415
|
event.preventDefault();
|
|
354
416
|
last?.focus();
|
|
355
417
|
}
|
|
356
|
-
else if (!event.shiftKey &&
|
|
418
|
+
else if (!event.shiftKey && focused === last) {
|
|
357
419
|
event.preventDefault();
|
|
358
420
|
first?.focus();
|
|
359
421
|
}
|
|
@@ -363,10 +425,11 @@ const createDatePicker = (config = {}) => {
|
|
|
363
425
|
if (target?.dataset.date) {
|
|
364
426
|
const date = parseDate(target.dataset.date);
|
|
365
427
|
let next;
|
|
428
|
+
const ahead = rtl(dialog) ? -1 : 1;
|
|
366
429
|
if (event.key === "ArrowLeft")
|
|
367
|
-
next = addDays(date, -
|
|
430
|
+
next = addDays(date, -ahead);
|
|
368
431
|
else if (event.key === "ArrowRight")
|
|
369
|
-
next = addDays(date,
|
|
432
|
+
next = addDays(date, ahead);
|
|
370
433
|
else if (event.key === "ArrowUp")
|
|
371
434
|
next = addDays(date, -7);
|
|
372
435
|
else if (event.key === "ArrowDown")
|
|
@@ -395,7 +458,8 @@ const createDatePicker = (config = {}) => {
|
|
|
395
458
|
else if (target?.hasAttribute('data-month') || target?.hasAttribute('data-year')) {
|
|
396
459
|
const buttons = Array.from(dialog.querySelectorAll('[data-month], [data-year]'));
|
|
397
460
|
let index = buttons.indexOf(target);
|
|
398
|
-
const
|
|
461
|
+
const ahead = rtl(dialog) ? -1 : 1;
|
|
462
|
+
const delta = { ArrowLeft: -ahead, ArrowRight: ahead, ArrowUp: -3, ArrowDown: 3 }[event.key];
|
|
399
463
|
if (event.key === "Home")
|
|
400
464
|
index = 0;
|
|
401
465
|
else if (event.key === "End")
|
|
@@ -425,11 +489,16 @@ const createDatePicker = (config = {}) => {
|
|
|
425
489
|
const page = Math.round(Math.abs(element.scrollLeft) / element.clientWidth) - 1;
|
|
426
490
|
if (!page)
|
|
427
491
|
return;
|
|
428
|
-
const
|
|
492
|
+
const focused = activeElementOf(dialog);
|
|
493
|
+
const active = focused instanceof HTMLElement && dialog.contains(focused) ? focused : null;
|
|
429
494
|
navigate(page);
|
|
430
495
|
render(!!active?.dataset.date, active?.dataset.action);
|
|
431
496
|
};
|
|
432
497
|
const onScroll = (event) => {
|
|
498
|
+
if (event.type === "scroll" && event.target instanceof HTMLElement && event.target === list()) {
|
|
499
|
+
extend(event.target);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
433
502
|
if (!(event.target instanceof HTMLElement) || event.target !== track())
|
|
434
503
|
return;
|
|
435
504
|
if (event.type === "scrollend") {
|