@windmorph/react 0.2.1 → 0.2.2
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/dist/components/carousel/carousel-styles.d.ts +6 -15
- package/dist/components/carousel/carousel-styles.js +4 -7
- package/dist/components/carousel/carousel-styles.js.map +1 -1
- package/dist/components/chip/chip-tokens.d.ts +4 -4
- package/dist/components/date-picker/DatePicker.js +11 -4
- package/dist/components/date-picker/DatePicker.js.map +1 -1
- package/dist/components/date-picker/date-picker-styles.d.ts +3 -3
- package/dist/components/date-picker/date-picker-tokens.d.ts +1 -1
- package/dist/components/dialog/dialog-tokens.d.ts +1 -1
- package/dist/components/list/List.d.ts +9 -0
- package/dist/components/list/List.js +7 -4
- package/dist/components/list/List.js.map +1 -1
- package/dist/components/list/list-styles.js +6 -6
- package/dist/components/list/list-styles.js.map +1 -1
- package/dist/components/list/list-tokens.d.ts +1 -1
- package/dist/components/list/list-tokens.js +50 -0
- package/dist/components/list/list-tokens.js.map +1 -1
- package/dist/components/menu/menu-tokens.d.ts +1 -1
- package/dist/components/navigation/NavigationRail.js +52 -15
- package/dist/components/navigation/NavigationRail.js.map +1 -1
- package/dist/components/search/SearchBar.js +19 -34
- package/dist/components/search/SearchBar.js.map +1 -1
- package/dist/components/search/SearchField.d.ts +111 -0
- package/dist/components/search/SearchField.js +128 -0
- package/dist/components/search/SearchField.js.map +1 -0
- package/dist/components/search/search-field-frame.js +36 -0
- package/dist/components/search/search-field-frame.js.map +1 -0
- package/dist/components/search/search-styles.d.ts +58 -0
- package/dist/components/search/search-styles.js +27 -2
- package/dist/components/search/search-styles.js.map +1 -1
- package/dist/components/select/search-list.js +8 -13
- package/dist/components/select/search-list.js.map +1 -1
- package/dist/components/snackbar/snackbar-tokens.d.ts +1 -1
- package/dist/components/text-field/TextField.js +19 -5
- package/dist/components/text-field/TextField.js.map +1 -1
- package/dist/components/toolbar/toolbar-styles.js +2 -2
- package/dist/components/toolbar/toolbar-styles.js.map +1 -1
- package/dist/components/tooltip/tooltip-tokens.d.ts +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/styles/generated/components.css +5 -0
- package/dist/styles.compiled.css +1 -1
- package/package.json +1 -1
|
@@ -43,15 +43,12 @@ export declare const carouselStyles: import("tailwind-variants").TVReturnType<{
|
|
|
43
43
|
};
|
|
44
44
|
false: {};
|
|
45
45
|
};
|
|
46
|
-
/** Full-bleed items fill the window edge to edge: no corners
|
|
46
|
+
/** Full-bleed items fill the window edge to edge: no corners. */
|
|
47
47
|
fullBleed: {
|
|
48
48
|
true: {
|
|
49
49
|
root: string;
|
|
50
|
-
item: string;
|
|
51
|
-
};
|
|
52
|
-
false: {
|
|
53
|
-
item: string;
|
|
54
50
|
};
|
|
51
|
+
false: {};
|
|
55
52
|
};
|
|
56
53
|
}, {
|
|
57
54
|
root: string[];
|
|
@@ -102,15 +99,12 @@ export declare const carouselStyles: import("tailwind-variants").TVReturnType<{
|
|
|
102
99
|
};
|
|
103
100
|
false: {};
|
|
104
101
|
};
|
|
105
|
-
/** Full-bleed items fill the window edge to edge: no corners
|
|
102
|
+
/** Full-bleed items fill the window edge to edge: no corners. */
|
|
106
103
|
fullBleed: {
|
|
107
104
|
true: {
|
|
108
105
|
root: string;
|
|
109
|
-
item: string;
|
|
110
|
-
};
|
|
111
|
-
false: {
|
|
112
|
-
item: string;
|
|
113
106
|
};
|
|
107
|
+
false: {};
|
|
114
108
|
};
|
|
115
109
|
}, {
|
|
116
110
|
root: string[];
|
|
@@ -161,15 +155,12 @@ export declare const carouselStyles: import("tailwind-variants").TVReturnType<{
|
|
|
161
155
|
};
|
|
162
156
|
false: {};
|
|
163
157
|
};
|
|
164
|
-
/** Full-bleed items fill the window edge to edge: no corners
|
|
158
|
+
/** Full-bleed items fill the window edge to edge: no corners. */
|
|
165
159
|
fullBleed: {
|
|
166
160
|
true: {
|
|
167
161
|
root: string;
|
|
168
|
-
item: string;
|
|
169
|
-
};
|
|
170
|
-
false: {
|
|
171
|
-
item: string;
|
|
172
162
|
};
|
|
163
|
+
false: {};
|
|
173
164
|
};
|
|
174
165
|
}, {
|
|
175
166
|
root: string[];
|
|
@@ -13,7 +13,7 @@ const carouselStyles = tv({
|
|
|
13
13
|
"data-dragging:cursor-grabbing data-dragging:[scroll-snap-type:none] data-dragging:select-none",
|
|
14
14
|
"[&::-webkit-scrollbar]:hidden"
|
|
15
15
|
],
|
|
16
|
-
item: "flex shrink-0 rounded-(--m3-carousel-corner)",
|
|
16
|
+
item: "flex shrink-0 rounded-(--m3-carousel-corner) focus-ring-inset",
|
|
17
17
|
mask: [
|
|
18
18
|
"grid size-full overflow-hidden bg-surface [&>*]:[grid-area:1/1] [&>*]:size-full",
|
|
19
19
|
"[--m3-carousel-item-corner:var(--m3-carousel-corner)]",
|
|
@@ -65,13 +65,10 @@ const carouselStyles = tv({
|
|
|
65
65
|
true: { item: "cursor-pointer data-disabled:cursor-default" },
|
|
66
66
|
false: {}
|
|
67
67
|
},
|
|
68
|
-
/** Full-bleed items fill the window edge to edge: no corners
|
|
68
|
+
/** Full-bleed items fill the window edge to edge: no corners. */
|
|
69
69
|
fullBleed: {
|
|
70
|
-
true: {
|
|
71
|
-
|
|
72
|
-
item: "focus-ring-inset"
|
|
73
|
-
},
|
|
74
|
-
false: { item: "focus-ring" }
|
|
70
|
+
true: { root: "[--m3-carousel-corner:0px] [--m3-carousel-pressed-corner:0px]" },
|
|
71
|
+
false: {}
|
|
75
72
|
}
|
|
76
73
|
},
|
|
77
74
|
compoundVariants: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"carousel-styles.js","names":[],"sources":["../../../src/components/carousel/carousel-styles.ts"],"sourcesContent":["import { tv, type VariantProps } from '../../utils/tv';\n\n/*\n * Values follow the M3 Expressive carousel spec (2026-10-13) and Compose Material 3\n * (carousel/Carousel.kt, Keylines.kt, Strategy.kt):\n *\n * - Multi-browse and hero: 16px leading and trailing padding, 8px top and bottom, 8px\n * between items. Uncontained (and multi-aspect): 16px leading padding only. Full-screen:\n * no padding, 16px between items and no corners. Small items are 40–56px.\n * - Snapping: hero and full-screen move one item at a time (`scroll-snap-stop: always`);\n * multi-browse flings across many items and then snaps; uncontained and multi-aspect\n * scroll freely unless `snap=\"item\"`.\n * - Uncontained (and multi-aspect) items keep one size and flow past the end edge; only\n * multi-browse and hero items follow keylines.\n * - Each item's mask is filled with `surface`, the spec's container colour, so transparent\n * or still-loading content never shows what's behind the carousel.\n * - Items are masked with 28px (extra-large) corners, as Compose's samples do with\n * `maskClip(MaterialTheme.shapes.extraLarge)`; the component token\n * `--md-comp-carousel-item-container-shape` (`carousel-tokens.ts`) sets them. A\n * pressed item's corners tighten to `--m3-carousel-pressed-corner` (20px, large-increased)\n * on the fast spatial spring, the spec's \"changes the shape slightly\".\n *\n * The carousel scrolls natively. Items keep their natural slots (the large item size), and\n * a library-owned mask layer inside each is translated and clipped to its keyline on every\n * scroll frame, so the root is never transformed. The state layer is a library-owned\n * element sharing the mask's grid cell, painted over the item (nothing is positioned).\n * Under the mask, a press area follows it, widened to 48px for a small item (40–56px):\n * M3's touch target, taken from the 8px spacing, so neighbours never overlap.\n * Every class is written out in full.\n */\n\n/** Variant definitions for {@link Carousel}. */\nexport const carouselStyles = tv({\n slots: {\n root: [\n 'flex min-h-0 min-w-0',\n '[--m3-carousel-corner:var(--_md-comp-carousel-item-container-shape)]',\n '[--m3-carousel-pressed-corner:var(--md-sys-shape-corner-large-increased)]',\n ],\n scroller: [\n 'flex min-h-0 min-w-0 flex-1 outline-none [scrollbar-width:none]',\n 'data-dragging:cursor-grabbing data-dragging:[scroll-snap-type:none] data-dragging:select-none',\n '[&::-webkit-scrollbar]:hidden',\n ],\n item: 'flex shrink-0 rounded-(--m3-carousel-corner)',\n mask: [\n 'grid size-full overflow-hidden bg-surface [&>*]:[grid-area:1/1] [&>*]:size-full',\n '[--m3-carousel-item-corner:var(--m3-carousel-corner)]',\n 'data-pressed:[--m3-carousel-item-corner:var(--m3-carousel-pressed-corner)]',\n '[transition-property:--m3-carousel-item-corner]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing)]',\n 'motion-reduce:[transition-duration:0s]',\n 'data-disabled:opacity-38',\n ],\n stateLayer:\n 'pointer-events-none state-layer [--m3-state-layer-color:var(--md-sys-color-on-surface)]',\n // The item's press area, laid under the mask in the same place (a negative end margin,\n // so nothing is positioned).\n hitArea: 'h-full w-full shrink-0 -me-[100%]',\n },\n variants: {\n orientation: {\n // Overscroll is contained on the carousel's own axis only, so a swipe across it\n // (a vertical swipe on a horizontal carousel) still scrolls the page (2026-10-15).\n horizontal: {\n scroller: 'flex-row overflow-x-auto overflow-y-hidden overscroll-x-contain',\n item: 'h-full',\n },\n vertical: { scroller: 'flex-col overflow-x-hidden overflow-y-auto', item: 'w-full' },\n },\n /** `single` stops at every item, `many` lets a fling cross several, `none` doesn't snap. */\n snapping: { single: {}, many: {}, none: {} },\n /** The mask is clipped by the keylines on every scroll frame; otherwise by CSS. */\n masked: {\n // Only the mask and the press area under it take presses, not the rest of the slot, so\n // a press between items never lands on an item whose slot reaches there.\n true: {\n item: 'pointer-events-none',\n mask: 'pointer-events-auto',\n hitArea: 'pointer-events-auto',\n },\n false: { mask: '[clip-path:inset(0_round_var(--m3-carousel-item-corner))]' },\n },\n /** Multi-aspect items keep their own width, between 9:16 and 16:9 of the height. */\n aspect: {\n true: {\n item: [\n 'w-max min-w-[calc(var(--m3-carousel-cross-size)*9/16)]',\n 'max-w-[calc(var(--m3-carousel-cross-size)*16/9)]',\n ],\n mask: '[&>*]:h-full [&>*]:w-auto [&>*]:min-w-full [&>*]:max-w-full [&>*]:object-cover',\n },\n false: {},\n },\n interactive: { true: { item: 'cursor-pointer data-disabled:cursor-default' }, false: {} },\n /** Full-bleed items fill the window edge to edge: no corners
|
|
1
|
+
{"version":3,"file":"carousel-styles.js","names":[],"sources":["../../../src/components/carousel/carousel-styles.ts"],"sourcesContent":["import { tv, type VariantProps } from '../../utils/tv';\n\n/*\n * Values follow the M3 Expressive carousel spec (2026-10-13) and Compose Material 3\n * (carousel/Carousel.kt, Keylines.kt, Strategy.kt):\n *\n * - Multi-browse and hero: 16px leading and trailing padding, 8px top and bottom, 8px\n * between items. Uncontained (and multi-aspect): 16px leading padding only. Full-screen:\n * no padding, 16px between items and no corners. Small items are 40–56px.\n * - Snapping: hero and full-screen move one item at a time (`scroll-snap-stop: always`);\n * multi-browse flings across many items and then snaps; uncontained and multi-aspect\n * scroll freely unless `snap=\"item\"`.\n * - Uncontained (and multi-aspect) items keep one size and flow past the end edge; only\n * multi-browse and hero items follow keylines.\n * - Each item's mask is filled with `surface`, the spec's container colour, so transparent\n * or still-loading content never shows what's behind the carousel.\n * - Items are masked with 28px (extra-large) corners, as Compose's samples do with\n * `maskClip(MaterialTheme.shapes.extraLarge)`; the component token\n * `--md-comp-carousel-item-container-shape` (`carousel-tokens.ts`) sets them. A\n * pressed item's corners tighten to `--m3-carousel-pressed-corner` (20px, large-increased)\n * on the fast spatial spring, the spec's \"changes the shape slightly\".\n *\n * The carousel scrolls natively. Items keep their natural slots (the large item size), and\n * a library-owned mask layer inside each is translated and clipped to its keyline on every\n * scroll frame, so the root is never transformed. The state layer is a library-owned\n * element sharing the mask's grid cell, painted over the item (nothing is positioned).\n * Under the mask, a press area follows it, widened to 48px for a small item (40–56px):\n * M3's touch target, taken from the 8px spacing, so neighbours never overlap.\n * Every class is written out in full.\n */\n\n/** Variant definitions for {@link Carousel}. */\nexport const carouselStyles = tv({\n slots: {\n root: [\n 'flex min-h-0 min-w-0',\n '[--m3-carousel-corner:var(--_md-comp-carousel-item-container-shape)]',\n '[--m3-carousel-pressed-corner:var(--md-sys-shape-corner-large-increased)]',\n ],\n scroller: [\n 'flex min-h-0 min-w-0 flex-1 outline-none [scrollbar-width:none]',\n 'data-dragging:cursor-grabbing data-dragging:[scroll-snap-type:none] data-dragging:select-none',\n '[&::-webkit-scrollbar]:hidden',\n ],\n // The focus ring is drawn inside: items sit against the scroller's edges in every layout\n // (Compose keeps the padding in the keylines), so a ring outside was cut where they meet.\n item: 'flex shrink-0 rounded-(--m3-carousel-corner) focus-ring-inset',\n mask: [\n 'grid size-full overflow-hidden bg-surface [&>*]:[grid-area:1/1] [&>*]:size-full',\n '[--m3-carousel-item-corner:var(--m3-carousel-corner)]',\n 'data-pressed:[--m3-carousel-item-corner:var(--m3-carousel-pressed-corner)]',\n '[transition-property:--m3-carousel-item-corner]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing)]',\n 'motion-reduce:[transition-duration:0s]',\n 'data-disabled:opacity-38',\n ],\n stateLayer:\n 'pointer-events-none state-layer [--m3-state-layer-color:var(--md-sys-color-on-surface)]',\n // The item's press area, laid under the mask in the same place (a negative end margin,\n // so nothing is positioned).\n hitArea: 'h-full w-full shrink-0 -me-[100%]',\n },\n variants: {\n orientation: {\n // Overscroll is contained on the carousel's own axis only, so a swipe across it\n // (a vertical swipe on a horizontal carousel) still scrolls the page (2026-10-15).\n horizontal: {\n scroller: 'flex-row overflow-x-auto overflow-y-hidden overscroll-x-contain',\n item: 'h-full',\n },\n vertical: { scroller: 'flex-col overflow-x-hidden overflow-y-auto', item: 'w-full' },\n },\n /** `single` stops at every item, `many` lets a fling cross several, `none` doesn't snap. */\n snapping: { single: {}, many: {}, none: {} },\n /** The mask is clipped by the keylines on every scroll frame; otherwise by CSS. */\n masked: {\n // Only the mask and the press area under it take presses, not the rest of the slot, so\n // a press between items never lands on an item whose slot reaches there.\n true: {\n item: 'pointer-events-none',\n mask: 'pointer-events-auto',\n hitArea: 'pointer-events-auto',\n },\n false: { mask: '[clip-path:inset(0_round_var(--m3-carousel-item-corner))]' },\n },\n /** Multi-aspect items keep their own width, between 9:16 and 16:9 of the height. */\n aspect: {\n true: {\n item: [\n 'w-max min-w-[calc(var(--m3-carousel-cross-size)*9/16)]',\n 'max-w-[calc(var(--m3-carousel-cross-size)*16/9)]',\n ],\n mask: '[&>*]:h-full [&>*]:w-auto [&>*]:min-w-full [&>*]:max-w-full [&>*]:object-cover',\n },\n false: {},\n },\n interactive: { true: { item: 'cursor-pointer data-disabled:cursor-default' }, false: {} },\n /** Full-bleed items fill the window edge to edge: no corners. */\n fullBleed: {\n true: { root: '[--m3-carousel-corner:0px] [--m3-carousel-pressed-corner:0px]' },\n false: {},\n },\n },\n compoundVariants: [\n {\n snapping: ['single', 'many'],\n orientation: 'horizontal',\n class: { scroller: 'snap-x snap-mandatory', item: 'snap-start' },\n },\n {\n snapping: ['single', 'many'],\n orientation: 'vertical',\n class: { scroller: 'snap-y snap-mandatory', item: 'snap-start' },\n },\n { snapping: 'single', class: { item: 'snap-always' } },\n // A full-screen vertical carousel fills the window, so past its last item a swipe goes\n // on to the page instead of trapping it.\n { orientation: 'vertical', fullBleed: false, class: { scroller: 'overscroll-y-contain' } },\n ],\n defaultVariants: {\n orientation: 'horizontal',\n snapping: 'single',\n masked: true,\n aspect: false,\n interactive: false,\n fullBleed: false,\n },\n});\n\n/**\n * The variants of {@link carouselStyles}, for building on its styles. They are the recipe's keys, not\n * component props: their names (`hasIcon`, `isOpen`…) follow the classes and may change.\n */\nexport type CarouselStyleProps = VariantProps<typeof carouselStyles>;\n"],"mappings":";;;AAgCA,MAAa,iBAAiB,GAAG;CAC/B,OAAO;EACL,MAAM;GACJ;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;EACF;EAGA,MAAM;EACN,MAAM;GACJ;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,YACE;EAGF,SAAS;CACX;CACA,UAAU;EACR,aAAa;GAGX,YAAY;IACV,UAAU;IACV,MAAM;GACR;GACA,UAAU;IAAE,UAAU;IAA8C,MAAM;GAAS;EACrF;;EAEA,UAAU;GAAE,QAAQ,CAAC;GAAG,MAAM,CAAC;GAAG,MAAM,CAAC;EAAE;;EAE3C,QAAQ;GAGN,MAAM;IACJ,MAAM;IACN,MAAM;IACN,SAAS;GACX;GACA,OAAO,EAAE,MAAM,4DAA4D;EAC7E;;EAEA,QAAQ;GACN,MAAM;IACJ,MAAM,CACJ,0DACA,kDACF;IACA,MAAM;GACR;GACA,OAAO,CAAC;EACV;EACA,aAAa;GAAE,MAAM,EAAE,MAAM,8CAA8C;GAAG,OAAO,CAAC;EAAE;;EAExF,WAAW;GACT,MAAM,EAAE,MAAM,gEAAgE;GAC9E,OAAO,CAAC;EACV;CACF;CACA,kBAAkB;EAChB;GACE,UAAU,CAAC,UAAU,MAAM;GAC3B,aAAa;GACb,OAAO;IAAE,UAAU;IAAyB,MAAM;GAAa;EACjE;EACA;GACE,UAAU,CAAC,UAAU,MAAM;GAC3B,aAAa;GACb,OAAO;IAAE,UAAU;IAAyB,MAAM;GAAa;EACjE;EACA;GAAE,UAAU;GAAU,OAAO,EAAE,MAAM,cAAc;EAAE;EAGrD;GAAE,aAAa;GAAY,WAAW;GAAO,OAAO,EAAE,UAAU,uBAAuB;EAAE;CAC3F;CACA,iBAAiB;EACf,aAAa;EACb,UAAU;EACV,QAAQ;EACR,QAAQ;EACR,aAAa;EACb,WAAW;CACb;AACF,CAAC"}
|
|
@@ -2,12 +2,12 @@ import { ComponentTokenSet } from "../../tokens/component.js";
|
|
|
2
2
|
import "../../index.js";
|
|
3
3
|
//#region src/components/chip/chip-tokens.d.ts
|
|
4
4
|
/** The tokens of {@link AssistChip}: `--md-comp-assist-chip-*`. */
|
|
5
|
-
export declare const assistChipTokenSet: ComponentTokenSet<"
|
|
5
|
+
export declare const assistChipTokenSet: ComponentTokenSet<"container-shape" | "label-text-color" | "with-icon-icon-color" | "flat-outline-color" | "elevated-container-color">;
|
|
6
6
|
/** The tokens of {@link SuggestionChip}: `--md-comp-suggestion-chip-*`. */
|
|
7
|
-
export declare const suggestionChipTokenSet: ComponentTokenSet<"
|
|
7
|
+
export declare const suggestionChipTokenSet: ComponentTokenSet<"container-shape" | "label-text-color" | "flat-outline-color" | "elevated-container-color" | "with-leading-icon-leading-icon-color">;
|
|
8
8
|
/** The tokens of {@link FilterChip}: `--md-comp-filter-chip-*`. */
|
|
9
|
-
export declare const filterChipTokenSet: ComponentTokenSet<"selected-label-text-color" | "unselected-label-text-color" | "
|
|
9
|
+
export declare const filterChipTokenSet: ComponentTokenSet<"container-shape" | "selected-label-text-color" | "unselected-label-text-color" | "with-leading-icon-unselected-leading-icon-color" | "flat-unselected-outline-color" | "flat-selected-container-color" | "elevated-unselected-container-color">;
|
|
10
10
|
/** The tokens of {@link InputChip}: `--md-comp-input-chip-*`. */
|
|
11
|
-
export declare const inputChipTokenSet: ComponentTokenSet<"selected-container-color" | "selected-label-text-color" | "unselected-label-text-color" | "
|
|
11
|
+
export declare const inputChipTokenSet: ComponentTokenSet<"container-shape" | "selected-container-color" | "selected-label-text-color" | "unselected-label-text-color" | "unselected-outline-color">;
|
|
12
12
|
//#endregion
|
|
13
13
|
//# sourceMappingURL=chip-tokens.d.ts.map
|
|
@@ -381,11 +381,18 @@ function RangeMonthList({ state, styles, className, classNames }) {
|
|
|
381
381
|
useLayoutEffect(() => {
|
|
382
382
|
const list = listRef.current;
|
|
383
383
|
if (!list) return;
|
|
384
|
+
let opened = list.getClientRects().length > 0;
|
|
384
385
|
list.scrollTop = initialTop.current;
|
|
385
|
-
const measure = () =>
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
386
|
+
const measure = () => {
|
|
387
|
+
if (!opened && list.clientHeight > 0) {
|
|
388
|
+
opened = true;
|
|
389
|
+
list.scrollTop = initialTop.current;
|
|
390
|
+
}
|
|
391
|
+
setView({
|
|
392
|
+
top: list.scrollTop,
|
|
393
|
+
height: list.clientHeight
|
|
394
|
+
});
|
|
395
|
+
};
|
|
389
396
|
measure();
|
|
390
397
|
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
|
|
391
398
|
observer?.observe(list);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatePicker.js","names":[],"sources":["../../../src/components/date-picker/DatePicker.tsx"],"sourcesContent":["'use client';\n\nimport {\n CalendarDate,\n createCalendar,\n getLocalTimeZone,\n maxDate,\n minDate,\n startOfWeek,\n toCalendarDate,\n today,\n type DateValue,\n} from '@internationalized/date';\nimport {\n useContext,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type ReactNode,\n type Ref,\n type TransitionEvent,\n} from 'react';\nimport {\n mergeProps,\n useCalendar,\n useDateFormatter,\n useLocale,\n useRangeCalendar,\n type AriaButtonProps,\n} from 'react-aria';\nimport {\n useCalendarState,\n useRangeCalendarState,\n type CalendarState,\n type RangeCalendarState,\n} from 'react-stately';\nimport { useControlledState } from 'react-stately/useControlledState';\nimport { DomDirectionLocale } from '../../primitives/DomDirectionLocale';\nimport { useSizeTransform } from './use-size-transform';\nimport { useViewExit } from './use-view-exit';\nimport { usePresence } from '../../primitives/use-presence';\nimport { cn } from '../../utils/cn';\nimport { mergeDataTokens } from '../../utils/merge-data-tokens';\nimport {\n splitRootProps,\n type DescriptionAria,\n type WrapperRootDOMProps,\n} from '../../utils/root-props';\nimport { Button } from '../button/Button';\nimport { IconButton } from '../icon-button/IconButton';\nimport { LazyTooltip } from '../tooltip/lazy-tooltip';\nimport { TooltipTrigger } from '../tooltip/tooltip-trigger';\nimport { CalendarGrid } from './CalendarGrid';\nimport { ChoiceButton, ShortcutIconButton, useMonthSwipe, usePickerKeys } from './calendar-parts';\nimport type { Density } from '../../tokens/density';\nimport { DateInput, type DateInputLabels } from './DateInput';\nimport { datePickerStyles } from './date-picker-styles';\nimport { PickerDialogContext } from './picker-dialog-context';\nimport {\n ArrowDropDownIcon,\n CalendarIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n EditIcon,\n} from './picker-icons';\n\n/** The picker's labels, for localisation. */\nexport interface DatePickerLabels extends DateInputLabels {\n /** The header's title in calendar mode. @default \"Select date\", or \"Select dates\" for a range */\n title: string;\n /**\n * The title in text input mode (the M3 modal date input's \"Enter date\").\n * @default \"Enter date\", or \"Enter dates\" for a range\n */\n inputTitle: string;\n /** Headline when nothing is selected. @default \"Selected date\" / \"Start date – End date\" */\n noSelection: string;\n /** The previous month button. @default \"Previous month\" */\n previousMonth: string;\n /** The next month button. @default \"Next month\" */\n nextMonth: string;\n /** Tooltip of the previous-month button, naming its shortcut. @default \"Previous month (Page Up)\" */\n previousMonthTooltip: string;\n /** Tooltip of the next-month button, naming its shortcut. @default \"Next month (Page Down)\" */\n nextMonthTooltip: string;\n /** The month and year button while the days show. @default \"Switch to selecting a year\" */\n showYears: string;\n /** The month and year button while the years show. @default \"Switch to selecting a day\" */\n showDays: string;\n /** Tooltip of the month and year button, naming its shortcut. @default \"Choose a year (Shift+Y)\" */\n yearsTooltip: string;\n /** Name of the year list. @default \"Year\" */\n yearList: string;\n /** The mode toggle in calendar mode. @default \"Switch to text input mode\" */\n inputMode: string;\n /** The mode toggle in text input mode. @default \"Switch to calendar input mode\" */\n calendarMode: string;\n /** Label of the input mode's field. @default \"Date\" */\n dateLabel: string;\n /** Label of a range's start field in input mode. @default \"Start date\" */\n startDateLabel: string;\n /**\n * Label of a range's end field in input mode, and the headline's end while a range is\n * being picked (\"Mar 20 – End date\"). @default \"End date\"\n */\n endDateLabel: string;\n}\n\nexport interface DatePickerClassNames {\n /** The picker's root; `className` is merged here too. */\n root?: string;\n /** The header: title, headline and mode toggle. */\n header?: string;\n /** The header's title (\"Select date\"). */\n title?: string;\n /** The header's headline: the chosen date or range. */\n headline?: string;\n /** The calendar or the text input below the header. */\n body?: string;\n /** The weekday names above the days. */\n weekday?: string;\n /** Each day of the calendar. */\n day?: string;\n /** Each year of the year list. */\n year?: string;\n /** The month and year button above the days, which shows the year list. */\n yearButton?: string;\n /** Each month's subhead in the range picker's scrolling month list. */\n monthSubhead?: string;\n}\n\nexport interface DateRangePickerClassNames extends DatePickerClassNames {\n /**\n * The band behind a range's days: each calendar cell it spans (`data-range` is `start`,\n * `middle` or `end`), whose background paints it.\n */\n rangeBand?: string;\n}\n\n// No `aria-description` and the like: the picker's root is a plain container, and no single\n// element in it stands for the whole picker.\ninterface DatePickerCommonProps extends Omit<WrapperRootDOMProps, DescriptionAria> {\n /** Earliest date allowed; earlier days are disabled. */\n min?: DateValue;\n /** Latest date allowed; later days are disabled. */\n max?: DateValue;\n /** Returns `true` for a day that can't be picked (it shows disabled). */\n isDateUnavailable?: (date: DateValue) => boolean;\n /** Show the header (title, headline and mode toggle). @default true */\n header?: boolean;\n /** Show the calendar ↔ text input toggle. @default true */\n modeToggle?: boolean;\n /** Start in calendar or text input mode. @default \"calendar\" */\n defaultMode?: 'calendar' | 'input';\n /**\n * First and last years of the year list (and of a range's month list). Years outside\n * `min` / `max` are shown disabled. @default [1900, 2100]\n */\n yearRange?: [number, number];\n /** Words the picker shows and announces, with English defaults. */\n labels?: Partial<DatePickerLabels>;\n /**\n * Web density of the input mode's text fields, `0` to `-3`: each step takes 4px off them\n * (56px at 0, 44px at -3). The calendar keeps its size: M3 doesn't make date pickers denser,\n * since their 48px day targets are part of their accessibility. Left out, the fields take\n * the density of the nearest `ThemeProvider` or `ThemeScope`.\n * @default 0\n */\n density?: Density;\n /** The root `<div>`'s id. */\n id?: string;\n /** Ref to the root `<div>` element. */\n ref?: Ref<HTMLDivElement>;\n /** Classes for the root element, merged with `classNames.root`; consumer classes win. */\n className?: string;\n /** Inline styles for the root element. */\n style?: CSSProperties;\n /** Classes for each part, by slot. */\n classNames?: DatePickerClassNames;\n /** `data-*` attributes go to the root. */\n [data: `data-${string}`]: string | number | boolean | undefined;\n}\n\nexport type DatePickerProps = DatePickerCommonProps & {\n /** The selected date (controlled); `null` for none. */\n value?: CalendarDate | null;\n /** Initial date when uncontrolled. @default null */\n defaultValue?: CalendarDate | null;\n /** Called with the date the user picks or types. */\n onChange?: (value: CalendarDate) => void;\n};\n\nexport interface DateRange {\n start: CalendarDate;\n end: CalendarDate;\n}\n\nexport type DateRangePickerProps = Omit<DatePickerCommonProps, 'classNames'> & {\n /** Classes for each part, by slot. */\n classNames?: DateRangePickerClassNames;\n /** The selected range (controlled); `null` for none. */\n value?: DateRange | null;\n /** Initial range when uncontrolled. @default null */\n defaultValue?: DateRange | null;\n /** Called with the range once both its start and end are picked or typed. */\n onChange?: (value: DateRange) => void;\n /**\n * `scroll` lists the months one under another and scrolls vertically, as the M3 modal\n * range picker does; `paged` shows one month at a time with previous / next buttons and\n * a year list, like the date picker. @default \"scroll\"\n */\n monthLayout?: 'scroll' | 'paged';\n};\n\n/**\n * M3 modal date picker (Compose's `DatePicker`): a header with the selected date, a month\n * grid you page with the arrows, a horizontal swipe or Page Up / Down, a year list, and a\n * text input mode. Put it in a {@link PickerDialog} for the modal form; {@link DateField}\n * is the docked form. Values are `@internationalized/date` dates.\n *\n * @example\n * <DatePicker value={date} onChange={setDate} />\n */\nexport function DatePicker(props: DatePickerProps) {\n return (\n <DomDirectionLocale>\n {(directionRef) => <SingleDatePicker {...props} directionRef={directionRef} />}\n </DomDirectionLocale>\n );\n}\n\n/**\n * M3 date range picker (Compose's `DateRangePicker`): select a start and an end date on a\n * vertically scrolling list of months, or type them in input mode.\n *\n * @example\n * <DateRangePicker value={range} onChange={setRange} />\n */\nexport function DateRangePicker(props: DateRangePickerProps) {\n return (\n <DomDirectionLocale>\n {(directionRef) => <RangeDatePicker {...props} directionRef={directionRef} />}\n </DomDirectionLocale>\n );\n}\n\ntype Inner<P> = P & { directionRef: (element: HTMLElement | null) => void };\n\nfunction SingleDatePicker(props: Inner<DatePickerProps>) {\n const { locale } = useLocale();\n const confirm = useContext(PickerDialogContext);\n const [value, setValue] = useControlledState(\n props.value,\n props.defaultValue ?? null,\n props.onChange as (value: CalendarDate | null) => void,\n );\n const state = useCalendarState({\n value,\n onChange: (date) => setValue(date as CalendarDate),\n minValue: props.min,\n maxValue: props.max,\n isDateUnavailable: props.isDateUnavailable,\n locale,\n createCalendar,\n });\n const formatter = useDateFormatter({ dateStyle: 'medium' });\n const labels = props.labels ?? {};\n const headline = value\n ? formatter.format(value.toDate(getLocalTimeZone()))\n : (labels.noSelection ?? 'Selected date');\n return (\n <PickerFrame\n {...props}\n range={false}\n state={state}\n headline={headline}\n input={\n <DateInput\n label={labels.dateLabel ?? 'Date'}\n value={value}\n onCommit={(date) => date && setValue(date)}\n onEnter={(date) => date && confirm?.requestConfirm()}\n min={props.min}\n max={props.max}\n isDateUnavailable={props.isDateUnavailable}\n labels={labels}\n density={props.density}\n className=\"w-full\"\n />\n }\n />\n );\n}\n\n/** Whole months from `from` to `to`. */\nconst monthsBetween = (from: CalendarDate, to: CalendarDate) =>\n (to.year - from.year) * 12 + (to.month - from.month);\n\nfunction RangeDatePicker({ monthLayout = 'scroll', ...props }: Inner<DateRangePickerProps>) {\n const { locale } = useLocale();\n const confirm = useContext(PickerDialogContext);\n const [value, setValue] = useControlledState(\n props.value,\n props.defaultValue ?? null,\n props.onChange as (value: DateRange | null) => void,\n );\n // The scrolling list shows every month of the year range (within min / max) at once.\n const [firstYear, lastYear] = props.yearRange ?? [1900, 2100];\n const scroll = monthLayout === 'scroll';\n const listStart = maxDate(\n new CalendarDate(firstYear, 1, 1),\n props.min ? toCalendarDate(props.min) : null,\n )!;\n const listEnd = minDate(\n new CalendarDate(lastYear, 12, 31),\n props.max ? toCalendarDate(props.max) : null,\n )!;\n const state = useRangeCalendarState({\n value,\n onChange: (range) => setValue(range as DateRange),\n minValue: scroll ? listStart : props.min,\n maxValue: scroll ? listEnd : props.max,\n isDateUnavailable: props.isDateUnavailable,\n visibleDuration: scroll ? { months: monthsBetween(listStart, listEnd) + 1 } : undefined,\n locale,\n createCalendar,\n });\n const formatter = useDateFormatter({ month: 'short', day: 'numeric' });\n const labels = props.labels ?? {};\n const format = (date: CalendarDate) => formatter.format(date.toDate(getLocalTimeZone()));\n // While a new range is being picked, its first day is the start and the end is still open\n // (Compose's headline: \"Mar 20 – End date\"), not the range it replaces.\n const anchor = state.anchorDate;\n const headline = anchor\n ? `${format(anchor)} – ${labels.endDateLabel ?? 'End date'}`\n : value\n ? `${format(value.start)} – ${format(value.end)}`\n : (labels.noSelection ?? 'Start date – End date');\n const common = {\n min: props.min,\n max: props.max,\n isDateUnavailable: props.isDateUnavailable,\n labels,\n density: props.density,\n className: 'w-full',\n };\n return (\n <PickerFrame\n {...props}\n range\n scroll={scroll}\n state={state}\n headline={headline}\n input={\n <>\n <DateInput\n {...common}\n label={labels.startDateLabel ?? 'Start date'}\n value={value?.start ?? null}\n onCommit={(start) =>\n start && setValue({ start, end: value && value.end >= start ? value.end : start })\n }\n />\n <DateInput\n {...common}\n label={labels.endDateLabel ?? 'End date'}\n value={value?.end ?? null}\n onCommit={(end) =>\n end && setValue({ start: value && value.start <= end ? value.start : end, end })\n }\n onEnter={(end) => end && value && confirm?.requestConfirm()}\n />\n </>\n }\n />\n );\n}\n\nfunction PickerFrame({\n range,\n scroll = false,\n state,\n headline,\n input,\n header = true,\n modeToggle = true,\n defaultMode = 'calendar',\n yearRange = [1900, 2100],\n labels = {},\n id,\n ref,\n className,\n style,\n classNames,\n directionRef,\n ...rest\n}: Inner<Omit<DatePickerCommonProps, 'classNames'>> & {\n classNames?: DateRangePickerClassNames | undefined;\n range: boolean;\n scroll?: boolean;\n state: CalendarState | RangeCalendarState;\n headline: string;\n input: ReactNode;\n value?: unknown;\n defaultValue?: unknown;\n onChange?: unknown;\n}) {\n const { root: data } = splitRootProps(rest, { wrapper: true });\n const [mode, setMode] = useState(defaultMode);\n // Set once the mode has been switched, so only a switch animates (not the first render).\n const [switched, setSwitched] = useState(false);\n const [years, setYears] = useState(false);\n // The picker's height follows the view on the default spatial spring (Compose's\n // `SizeTransform` in its display mode animation), as the views swap.\n const { attach: attachSize, capture: captureSize } = useSizeTransform<HTMLDivElement>(mode);\n // The outgoing view fades out as it slides away (Compose: up 48px as the input comes in,\n // down by its whole height as the calendar comes back), clipped with the new one.\n const {\n attachView: attachExitView,\n attachSlot: attachExitSlot,\n capture: captureExit,\n } = useViewExit(mode);\n const styles = datePickerStyles({ range });\n const title =\n mode === 'input'\n ? (labels.inputTitle ?? (range ? 'Enter dates' : 'Enter date'))\n : (labels.title ?? (range ? 'Select dates' : 'Select date'));\n const bodyProps = {\n styles,\n years,\n setYears,\n yearRange,\n labels,\n className: classNames?.body,\n classNames,\n };\n return (\n <div\n {...data}\n id={id}\n ref={(element) => {\n attachSize(element);\n directionRef(element);\n if (typeof ref === 'function') ref(element);\n else if (ref) ref.current = element;\n }}\n style={style}\n // The modal date picker's component tokens (date-picker-tokens.ts).\n data-tokens={mergeDataTokens('md-comp-date-picker-modal', data)}\n data-years={years || undefined}\n data-mode={mode}\n className={styles.root({ class: cn(classNames?.root, className) })}\n >\n {header && (\n <div className={styles.header({ class: classNames?.header })}>\n <div className={styles.title({ class: classNames?.title })}>{title}</div>\n <div className={styles.headlineRow()}>\n <div\n aria-live=\"polite\"\n data-slot=\"headline\"\n className={styles.headline({ class: classNames?.headline })}\n >\n {headline}\n </div>\n {modeToggle && (\n <IconButton\n variant=\"standard\"\n // The picker's surface isn't dense, only its input mode's fields.\n density={0}\n icon={mode === 'calendar' ? <EditIcon /> : <CalendarIcon />}\n aria-label={\n mode === 'calendar'\n ? (labels.inputMode ?? 'Switch to text input mode')\n : (labels.calendarMode ?? 'Switch to calendar input mode')\n }\n onPress={() => {\n captureSize();\n captureExit(mode === 'calendar' ? '-48px' : '100%');\n setSwitched(true);\n setMode(mode === 'calendar' ? 'input' : 'calendar');\n }}\n />\n )}\n </div>\n </div>\n )}\n <div className={styles.modeStack()}>\n <div\n key={mode}\n ref={attachExitView}\n data-enter={switched ? mode : undefined}\n className={styles.modeView()}\n >\n {mode === 'input' ? (\n <div className={styles.input({ class: classNames?.body })}>{input}</div>\n ) : range && scroll ? (\n <RangeMonthList state={state as RangeCalendarState} {...bodyProps} />\n ) : range ? (\n <RangeCalendarBody state={state as RangeCalendarState} {...bodyProps} />\n ) : (\n <SingleCalendarBody state={state as CalendarState} {...bodyProps} />\n )}\n </div>\n <div ref={attachExitSlot} aria-hidden=\"true\" inert className={styles.modeExit()} />\n </div>\n </div>\n );\n}\n\ninterface BodyProps {\n styles: ReturnType<typeof datePickerStyles>;\n years: boolean;\n setYears: (open: boolean) => void;\n yearRange: [number, number];\n labels: Partial<DatePickerLabels>;\n className?: string | undefined;\n classNames?: DateRangePickerClassNames | undefined;\n}\n\nfunction SingleCalendarBody({ state, ...props }: BodyProps & { state: CalendarState }) {\n const { calendarProps, prevButtonProps, nextButtonProps } = useCalendar({}, state);\n return (\n <CalendarBody\n {...props}\n state={state}\n calendarProps={calendarProps}\n prevButtonProps={prevButtonProps}\n nextButtonProps={nextButtonProps}\n />\n );\n}\n\nfunction RangeCalendarBody({ state, ...props }: BodyProps & { state: RangeCalendarState }) {\n const ref = useRef<HTMLDivElement>(null);\n const { calendarProps, prevButtonProps, nextButtonProps } = useRangeCalendar({}, state, ref);\n return (\n <CalendarBody\n {...props}\n bodyRef={ref}\n state={state}\n calendarProps={calendarProps}\n prevButtonProps={prevButtonProps}\n nextButtonProps={nextButtonProps}\n />\n );\n}\n\nfunction CalendarBody({\n state,\n calendarProps,\n prevButtonProps,\n nextButtonProps,\n styles,\n years,\n setYears,\n yearRange,\n labels,\n className,\n classNames,\n bodyRef,\n}: BodyProps & {\n state: CalendarState | RangeCalendarState;\n calendarProps: HTMLAttributes<HTMLElement>;\n prevButtonProps: AriaButtonProps;\n nextButtonProps: AriaButtonProps;\n bodyRef?: Ref<HTMLDivElement>;\n}) {\n const confirm = useContext(PickerDialogContext);\n const monthFormatter = useDateFormatter({ month: 'long', year: 'numeric' });\n const monthLabel = monthFormatter.format(state.visibleRange.start.toDate(getLocalTimeZone()));\n const menuRef = useRef<HTMLButtonElement>(null);\n const [focusYears, setFocusYears] = useState(false);\n // Compose shrinks the year list and fades it out (`shrinkVertically` + `fadeOut`); the\n // first of the two transitions to end removes it.\n const yearsPresence = usePresence(years, ['clip-path', 'opacity']);\n const { swipeProps, slideKey, slideDirection } = useMonthSwipe(state);\n const onKeyDownCapture = usePickerKeys(state, {\n onConfirm: confirm?.requestConfirm,\n onMonthMenu: () => menuRef.current?.focus(),\n onYearMenu: () => {\n setFocusYears(true);\n setYears(true);\n },\n });\n return (\n <div\n {...calendarProps}\n ref={bodyRef}\n onKeyDownCapture={onKeyDownCapture}\n className={styles.body({ class: className })}\n >\n <div className={styles.nav()}>\n <TooltipTrigger>\n <Button\n ref={menuRef}\n variant=\"text\"\n className={cn('text-on-surface-variant', classNames?.yearButton)}\n aria-label={`${monthLabel}, ${years ? (labels.showDays ?? 'Switch to selecting a day') : (labels.showYears ?? 'Switch to selecting a year')}`}\n onPress={() => {\n setFocusYears(false);\n setYears(!years);\n }}\n >\n {/* The drop-down arrow trails the label here, which Button's one leading icon can't do. */}\n <span className=\"inline-flex items-center gap-[8px]\">\n <span aria-live=\"polite\">{monthLabel}</span>\n <span\n aria-hidden=\"true\"\n className=\"inline-flex size-[20px] items-center justify-center\"\n >\n <span className={styles.yearButtonIcon()}>\n <ArrowDropDownIcon />\n </span>\n </span>\n </span>\n </Button>\n <LazyTooltip>{labels.yearsTooltip ?? 'Choose a year (Shift+Y)'}</LazyTooltip>\n </TooltipTrigger>\n {!years && (\n <div className={styles.navButtons()}>\n <ShortcutIconButton\n icon={<ChevronLeftIcon />}\n label={labels.previousMonth ?? 'Previous month'}\n tooltip={labels.previousMonthTooltip ?? 'Previous month (Page Up)'}\n onPress={prevButtonProps.onPress as () => void}\n disabled={prevButtonProps.isDisabled}\n />\n <ShortcutIconButton\n icon={<ChevronRightIcon />}\n label={labels.nextMonth ?? 'Next month'}\n tooltip={labels.nextMonthTooltip ?? 'Next month (Page Down)'}\n onPress={nextButtonProps.onPress as () => void}\n disabled={nextButtonProps.isDisabled}\n />\n </div>\n )}\n </div>\n {!years && (\n <div {...swipeProps} className={styles.swipe()}>\n <div key={slideKey} data-direction={slideDirection} className={styles.slide()}>\n <CalendarGrid state={state} styles={styles} classNames={classNames} />\n </div>\n </div>\n )}\n {/* Closing, the list stays over the grid until it has shrunk and faded away. */}\n {yearsPresence.isPresent && (\n <YearList\n state={state}\n styles={styles}\n yearRange={yearRange}\n className={classNames?.year}\n label={labels.yearList ?? 'Year'}\n autoFocus={focusYears}\n exiting={yearsPresence.isExiting}\n exitProps={yearsPresence.exitProps}\n onPick={() => setYears(false)}\n />\n )}\n </div>\n );\n}\n\n/**\n * The range picker's months, one under another in a vertical scroll (M3: \"To navigate\n * across months, scroll vertically\"), under a single weekday row. Only the months in view\n * (and the focused one) are rendered; the rest are spacers of the same fixed height.\n */\nfunction RangeMonthList({\n state,\n styles,\n className,\n classNames,\n}: BodyProps & { state: RangeCalendarState }) {\n const confirm = useContext(PickerDialogContext);\n const ref = useRef<HTMLDivElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const { calendarProps } = useRangeCalendar({}, state, ref);\n const { locale } = useLocale();\n const first = state.visibleRange.start;\n const count = monthsBetween(first, state.visibleRange.end) + 1;\n const focusedIndex = monthsBetween(first, state.focusedDate);\n const [view, setView] = useState(() => ({ top: focusedIndex * MONTH_HEIGHT, height: 0 }));\n const weekdays = useDateFormatter({ weekday: 'narrow' });\n const longWeekdays = useDateFormatter({ weekday: 'long' });\n const subhead = useDateFormatter({ month: 'long', year: 'numeric' });\n const onKeyDownCapture = usePickerKeys(state, {\n onConfirm: confirm?.requestConfirm,\n onMonthMenu: () => undefined,\n onYearMenu: () => undefined,\n });\n\n // Open on the focused month (the range's start, or today), then follow the scroll.\n const initialTop = useRef(view.top);\n useLayoutEffect(() => {\n const list = listRef.current;\n if (!list) return;\n list.scrollTop = initialTop.current;\n const measure = () => setView({ top: list.scrollTop, height: list.clientHeight });\n measure();\n const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(measure);\n observer?.observe(list);\n list.addEventListener('scroll', measure, { passive: true });\n return () => {\n observer?.disconnect();\n list.removeEventListener('scroll', measure);\n };\n }, []);\n\n const height = view.height || MONTH_HEIGHT;\n let from = Math.max(0, Math.floor(view.top / MONTH_HEIGHT) - 1);\n let to = Math.min(count - 1, Math.floor((view.top + height) / MONTH_HEIGHT) + 1);\n if (state.isFocused) {\n from = Math.min(from, focusedIndex);\n to = Math.max(to, focusedIndex);\n }\n const weekStart = startOfWeek(today(getLocalTimeZone()), locale);\n\n return (\n <div\n {...mergeProps(calendarProps, { onKeyDownCapture })}\n ref={ref}\n className={styles.body({ class: cn('min-h-0 flex-1', className) })}\n >\n <div aria-hidden=\"true\" className={styles.weekdayRow()}>\n {Array.from({ length: 7 }, (_, index) => {\n const day = weekStart.add({ days: index }).toDate(getLocalTimeZone());\n return (\n <span\n key={index}\n title={longWeekdays.format(day)}\n className={styles.weekdayCell({ class: classNames?.weekday })}\n >\n {weekdays.format(day)}\n </span>\n );\n })}\n </div>\n <div ref={listRef} className={styles.months()}>\n <div style={{ height: from * MONTH_HEIGHT }} />\n {Array.from({ length: to - from + 1 }, (_, i) => {\n const month = first.add({ months: from + i });\n return (\n <div key={month.toString()} className=\"h-[336px]\">\n <div\n aria-hidden=\"true\"\n className={styles.monthSubhead({ class: classNames?.monthSubhead })}\n >\n {subhead.format(month.toDate(getLocalTimeZone()))}\n </div>\n <CalendarGrid state={state} styles={styles} month={month} classNames={classNames} />\n </div>\n );\n })}\n <div style={{ height: (count - 1 - to) * MONTH_HEIGHT }} />\n </div>\n </div>\n );\n}\n\n/** A month in the scrolling list: its 48px subhead and six 48px weeks. */\nconst MONTH_HEIGHT = 336;\n\nfunction YearList({\n state,\n styles,\n yearRange,\n className,\n label,\n autoFocus,\n exiting,\n exitProps,\n onPick,\n}: {\n state: CalendarState | RangeCalendarState;\n styles: ReturnType<typeof datePickerStyles>;\n yearRange: [number, number];\n className: string | undefined;\n label: string;\n autoFocus: boolean;\n /** Closing: drawn over the grid, out of the accessibility tree and taking no presses. */\n exiting: boolean;\n exitProps: { onTransitionEnd: (event: TransitionEvent<HTMLElement>) => void };\n onPick: () => void;\n}) {\n const current = today(getLocalTimeZone()).year;\n const focusedYear = state.focusedDate.year;\n const minYear = state.minValue?.year ?? -Infinity;\n const maxYear = state.maxValue?.year ?? Infinity;\n return (\n <div\n {...exitProps}\n role=\"radiogroup\"\n aria-label={label}\n aria-hidden={exiting || undefined}\n inert={exiting || undefined}\n data-exiting={exiting || undefined}\n className={styles.years()}\n ref={(element) => {\n if (exiting) return;\n // Open on the focused year, as Compose scrolls the year list to it.\n const selected = element?.querySelector<HTMLElement>('[aria-checked=\"true\"]');\n selected?.scrollIntoView?.({ block: 'center' });\n if (autoFocus) selected?.focus({ preventScroll: true });\n }}\n >\n {Array.from({ length: yearRange[1] - yearRange[0] + 1 }, (_, i) => yearRange[0] + i).map(\n (year) => (\n <ChoiceButton\n key={year}\n selected={year === focusedYear}\n disabled={year < minYear || year > maxYear}\n data-current={year === current || undefined}\n steps={YEAR_STEPS}\n className={styles.year({ class: className })}\n onPick={() => {\n state.setFocusedDate(state.focusedDate.set({ year }));\n onPick();\n }}\n >\n {year}\n </ChoiceButton>\n ),\n )}\n </div>\n );\n}\n\n/** Three years to a row. */\nconst YEAR_STEPS = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -3, ArrowDown: 3 };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DatePicker.js","names":[],"sources":["../../../src/components/date-picker/DatePicker.tsx"],"sourcesContent":["'use client';\n\nimport {\n CalendarDate,\n createCalendar,\n getLocalTimeZone,\n maxDate,\n minDate,\n startOfWeek,\n toCalendarDate,\n today,\n type DateValue,\n} from '@internationalized/date';\nimport {\n useContext,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type ReactNode,\n type Ref,\n type TransitionEvent,\n} from 'react';\nimport {\n mergeProps,\n useCalendar,\n useDateFormatter,\n useLocale,\n useRangeCalendar,\n type AriaButtonProps,\n} from 'react-aria';\nimport {\n useCalendarState,\n useRangeCalendarState,\n type CalendarState,\n type RangeCalendarState,\n} from 'react-stately';\nimport { useControlledState } from 'react-stately/useControlledState';\nimport { DomDirectionLocale } from '../../primitives/DomDirectionLocale';\nimport { useSizeTransform } from './use-size-transform';\nimport { useViewExit } from './use-view-exit';\nimport { usePresence } from '../../primitives/use-presence';\nimport { cn } from '../../utils/cn';\nimport { mergeDataTokens } from '../../utils/merge-data-tokens';\nimport {\n splitRootProps,\n type DescriptionAria,\n type WrapperRootDOMProps,\n} from '../../utils/root-props';\nimport { Button } from '../button/Button';\nimport { IconButton } from '../icon-button/IconButton';\nimport { LazyTooltip } from '../tooltip/lazy-tooltip';\nimport { TooltipTrigger } from '../tooltip/tooltip-trigger';\nimport { CalendarGrid } from './CalendarGrid';\nimport { ChoiceButton, ShortcutIconButton, useMonthSwipe, usePickerKeys } from './calendar-parts';\nimport type { Density } from '../../tokens/density';\nimport { DateInput, type DateInputLabels } from './DateInput';\nimport { datePickerStyles } from './date-picker-styles';\nimport { PickerDialogContext } from './picker-dialog-context';\nimport {\n ArrowDropDownIcon,\n CalendarIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n EditIcon,\n} from './picker-icons';\n\n/** The picker's labels, for localisation. */\nexport interface DatePickerLabels extends DateInputLabels {\n /** The header's title in calendar mode. @default \"Select date\", or \"Select dates\" for a range */\n title: string;\n /**\n * The title in text input mode (the M3 modal date input's \"Enter date\").\n * @default \"Enter date\", or \"Enter dates\" for a range\n */\n inputTitle: string;\n /** Headline when nothing is selected. @default \"Selected date\" / \"Start date – End date\" */\n noSelection: string;\n /** The previous month button. @default \"Previous month\" */\n previousMonth: string;\n /** The next month button. @default \"Next month\" */\n nextMonth: string;\n /** Tooltip of the previous-month button, naming its shortcut. @default \"Previous month (Page Up)\" */\n previousMonthTooltip: string;\n /** Tooltip of the next-month button, naming its shortcut. @default \"Next month (Page Down)\" */\n nextMonthTooltip: string;\n /** The month and year button while the days show. @default \"Switch to selecting a year\" */\n showYears: string;\n /** The month and year button while the years show. @default \"Switch to selecting a day\" */\n showDays: string;\n /** Tooltip of the month and year button, naming its shortcut. @default \"Choose a year (Shift+Y)\" */\n yearsTooltip: string;\n /** Name of the year list. @default \"Year\" */\n yearList: string;\n /** The mode toggle in calendar mode. @default \"Switch to text input mode\" */\n inputMode: string;\n /** The mode toggle in text input mode. @default \"Switch to calendar input mode\" */\n calendarMode: string;\n /** Label of the input mode's field. @default \"Date\" */\n dateLabel: string;\n /** Label of a range's start field in input mode. @default \"Start date\" */\n startDateLabel: string;\n /**\n * Label of a range's end field in input mode, and the headline's end while a range is\n * being picked (\"Mar 20 – End date\"). @default \"End date\"\n */\n endDateLabel: string;\n}\n\nexport interface DatePickerClassNames {\n /** The picker's root; `className` is merged here too. */\n root?: string;\n /** The header: title, headline and mode toggle. */\n header?: string;\n /** The header's title (\"Select date\"). */\n title?: string;\n /** The header's headline: the chosen date or range. */\n headline?: string;\n /** The calendar or the text input below the header. */\n body?: string;\n /** The weekday names above the days. */\n weekday?: string;\n /** Each day of the calendar. */\n day?: string;\n /** Each year of the year list. */\n year?: string;\n /** The month and year button above the days, which shows the year list. */\n yearButton?: string;\n /** Each month's subhead in the range picker's scrolling month list. */\n monthSubhead?: string;\n}\n\nexport interface DateRangePickerClassNames extends DatePickerClassNames {\n /**\n * The band behind a range's days: each calendar cell it spans (`data-range` is `start`,\n * `middle` or `end`), whose background paints it.\n */\n rangeBand?: string;\n}\n\n// No `aria-description` and the like: the picker's root is a plain container, and no single\n// element in it stands for the whole picker.\ninterface DatePickerCommonProps extends Omit<WrapperRootDOMProps, DescriptionAria> {\n /** Earliest date allowed; earlier days are disabled. */\n min?: DateValue;\n /** Latest date allowed; later days are disabled. */\n max?: DateValue;\n /** Returns `true` for a day that can't be picked (it shows disabled). */\n isDateUnavailable?: (date: DateValue) => boolean;\n /** Show the header (title, headline and mode toggle). @default true */\n header?: boolean;\n /** Show the calendar ↔ text input toggle. @default true */\n modeToggle?: boolean;\n /** Start in calendar or text input mode. @default \"calendar\" */\n defaultMode?: 'calendar' | 'input';\n /**\n * First and last years of the year list (and of a range's month list). Years outside\n * `min` / `max` are shown disabled. @default [1900, 2100]\n */\n yearRange?: [number, number];\n /** Words the picker shows and announces, with English defaults. */\n labels?: Partial<DatePickerLabels>;\n /**\n * Web density of the input mode's text fields, `0` to `-3`: each step takes 4px off them\n * (56px at 0, 44px at -3). The calendar keeps its size: M3 doesn't make date pickers denser,\n * since their 48px day targets are part of their accessibility. Left out, the fields take\n * the density of the nearest `ThemeProvider` or `ThemeScope`.\n * @default 0\n */\n density?: Density;\n /** The root `<div>`'s id. */\n id?: string;\n /** Ref to the root `<div>` element. */\n ref?: Ref<HTMLDivElement>;\n /** Classes for the root element, merged with `classNames.root`; consumer classes win. */\n className?: string;\n /** Inline styles for the root element. */\n style?: CSSProperties;\n /** Classes for each part, by slot. */\n classNames?: DatePickerClassNames;\n /** `data-*` attributes go to the root. */\n [data: `data-${string}`]: string | number | boolean | undefined;\n}\n\nexport type DatePickerProps = DatePickerCommonProps & {\n /** The selected date (controlled); `null` for none. */\n value?: CalendarDate | null;\n /** Initial date when uncontrolled. @default null */\n defaultValue?: CalendarDate | null;\n /** Called with the date the user picks or types. */\n onChange?: (value: CalendarDate) => void;\n};\n\nexport interface DateRange {\n start: CalendarDate;\n end: CalendarDate;\n}\n\nexport type DateRangePickerProps = Omit<DatePickerCommonProps, 'classNames'> & {\n /** Classes for each part, by slot. */\n classNames?: DateRangePickerClassNames;\n /** The selected range (controlled); `null` for none. */\n value?: DateRange | null;\n /** Initial range when uncontrolled. @default null */\n defaultValue?: DateRange | null;\n /** Called with the range once both its start and end are picked or typed. */\n onChange?: (value: DateRange) => void;\n /**\n * `scroll` lists the months one under another and scrolls vertically, as the M3 modal\n * range picker does; `paged` shows one month at a time with previous / next buttons and\n * a year list, like the date picker. @default \"scroll\"\n */\n monthLayout?: 'scroll' | 'paged';\n};\n\n/**\n * M3 modal date picker (Compose's `DatePicker`): a header with the selected date, a month\n * grid you page with the arrows, a horizontal swipe or Page Up / Down, a year list, and a\n * text input mode. Put it in a {@link PickerDialog} for the modal form; {@link DateField}\n * is the docked form. Values are `@internationalized/date` dates.\n *\n * @example\n * <DatePicker value={date} onChange={setDate} />\n */\nexport function DatePicker(props: DatePickerProps) {\n return (\n <DomDirectionLocale>\n {(directionRef) => <SingleDatePicker {...props} directionRef={directionRef} />}\n </DomDirectionLocale>\n );\n}\n\n/**\n * M3 date range picker (Compose's `DateRangePicker`): select a start and an end date on a\n * vertically scrolling list of months, or type them in input mode.\n *\n * @example\n * <DateRangePicker value={range} onChange={setRange} />\n */\nexport function DateRangePicker(props: DateRangePickerProps) {\n return (\n <DomDirectionLocale>\n {(directionRef) => <RangeDatePicker {...props} directionRef={directionRef} />}\n </DomDirectionLocale>\n );\n}\n\ntype Inner<P> = P & { directionRef: (element: HTMLElement | null) => void };\n\nfunction SingleDatePicker(props: Inner<DatePickerProps>) {\n const { locale } = useLocale();\n const confirm = useContext(PickerDialogContext);\n const [value, setValue] = useControlledState(\n props.value,\n props.defaultValue ?? null,\n props.onChange as (value: CalendarDate | null) => void,\n );\n const state = useCalendarState({\n value,\n onChange: (date) => setValue(date as CalendarDate),\n minValue: props.min,\n maxValue: props.max,\n isDateUnavailable: props.isDateUnavailable,\n locale,\n createCalendar,\n });\n const formatter = useDateFormatter({ dateStyle: 'medium' });\n const labels = props.labels ?? {};\n const headline = value\n ? formatter.format(value.toDate(getLocalTimeZone()))\n : (labels.noSelection ?? 'Selected date');\n return (\n <PickerFrame\n {...props}\n range={false}\n state={state}\n headline={headline}\n input={\n <DateInput\n label={labels.dateLabel ?? 'Date'}\n value={value}\n onCommit={(date) => date && setValue(date)}\n onEnter={(date) => date && confirm?.requestConfirm()}\n min={props.min}\n max={props.max}\n isDateUnavailable={props.isDateUnavailable}\n labels={labels}\n density={props.density}\n className=\"w-full\"\n />\n }\n />\n );\n}\n\n/** Whole months from `from` to `to`. */\nconst monthsBetween = (from: CalendarDate, to: CalendarDate) =>\n (to.year - from.year) * 12 + (to.month - from.month);\n\nfunction RangeDatePicker({ monthLayout = 'scroll', ...props }: Inner<DateRangePickerProps>) {\n const { locale } = useLocale();\n const confirm = useContext(PickerDialogContext);\n const [value, setValue] = useControlledState(\n props.value,\n props.defaultValue ?? null,\n props.onChange as (value: DateRange | null) => void,\n );\n // The scrolling list shows every month of the year range (within min / max) at once.\n const [firstYear, lastYear] = props.yearRange ?? [1900, 2100];\n const scroll = monthLayout === 'scroll';\n const listStart = maxDate(\n new CalendarDate(firstYear, 1, 1),\n props.min ? toCalendarDate(props.min) : null,\n )!;\n const listEnd = minDate(\n new CalendarDate(lastYear, 12, 31),\n props.max ? toCalendarDate(props.max) : null,\n )!;\n const state = useRangeCalendarState({\n value,\n onChange: (range) => setValue(range as DateRange),\n minValue: scroll ? listStart : props.min,\n maxValue: scroll ? listEnd : props.max,\n isDateUnavailable: props.isDateUnavailable,\n visibleDuration: scroll ? { months: monthsBetween(listStart, listEnd) + 1 } : undefined,\n locale,\n createCalendar,\n });\n const formatter = useDateFormatter({ month: 'short', day: 'numeric' });\n const labels = props.labels ?? {};\n const format = (date: CalendarDate) => formatter.format(date.toDate(getLocalTimeZone()));\n // While a new range is being picked, its first day is the start and the end is still open\n // (Compose's headline: \"Mar 20 – End date\"), not the range it replaces.\n const anchor = state.anchorDate;\n const headline = anchor\n ? `${format(anchor)} – ${labels.endDateLabel ?? 'End date'}`\n : value\n ? `${format(value.start)} – ${format(value.end)}`\n : (labels.noSelection ?? 'Start date – End date');\n const common = {\n min: props.min,\n max: props.max,\n isDateUnavailable: props.isDateUnavailable,\n labels,\n density: props.density,\n className: 'w-full',\n };\n return (\n <PickerFrame\n {...props}\n range\n scroll={scroll}\n state={state}\n headline={headline}\n input={\n <>\n <DateInput\n {...common}\n label={labels.startDateLabel ?? 'Start date'}\n value={value?.start ?? null}\n onCommit={(start) =>\n start && setValue({ start, end: value && value.end >= start ? value.end : start })\n }\n />\n <DateInput\n {...common}\n label={labels.endDateLabel ?? 'End date'}\n value={value?.end ?? null}\n onCommit={(end) =>\n end && setValue({ start: value && value.start <= end ? value.start : end, end })\n }\n onEnter={(end) => end && value && confirm?.requestConfirm()}\n />\n </>\n }\n />\n );\n}\n\nfunction PickerFrame({\n range,\n scroll = false,\n state,\n headline,\n input,\n header = true,\n modeToggle = true,\n defaultMode = 'calendar',\n yearRange = [1900, 2100],\n labels = {},\n id,\n ref,\n className,\n style,\n classNames,\n directionRef,\n ...rest\n}: Inner<Omit<DatePickerCommonProps, 'classNames'>> & {\n classNames?: DateRangePickerClassNames | undefined;\n range: boolean;\n scroll?: boolean;\n state: CalendarState | RangeCalendarState;\n headline: string;\n input: ReactNode;\n value?: unknown;\n defaultValue?: unknown;\n onChange?: unknown;\n}) {\n const { root: data } = splitRootProps(rest, { wrapper: true });\n const [mode, setMode] = useState(defaultMode);\n // Set once the mode has been switched, so only a switch animates (not the first render).\n const [switched, setSwitched] = useState(false);\n const [years, setYears] = useState(false);\n // The picker's height follows the view on the default spatial spring (Compose's\n // `SizeTransform` in its display mode animation), as the views swap.\n const { attach: attachSize, capture: captureSize } = useSizeTransform<HTMLDivElement>(mode);\n // The outgoing view fades out as it slides away (Compose: up 48px as the input comes in,\n // down by its whole height as the calendar comes back), clipped with the new one.\n const {\n attachView: attachExitView,\n attachSlot: attachExitSlot,\n capture: captureExit,\n } = useViewExit(mode);\n const styles = datePickerStyles({ range });\n const title =\n mode === 'input'\n ? (labels.inputTitle ?? (range ? 'Enter dates' : 'Enter date'))\n : (labels.title ?? (range ? 'Select dates' : 'Select date'));\n const bodyProps = {\n styles,\n years,\n setYears,\n yearRange,\n labels,\n className: classNames?.body,\n classNames,\n };\n return (\n <div\n {...data}\n id={id}\n ref={(element) => {\n attachSize(element);\n directionRef(element);\n if (typeof ref === 'function') ref(element);\n else if (ref) ref.current = element;\n }}\n style={style}\n // The modal date picker's component tokens (date-picker-tokens.ts).\n data-tokens={mergeDataTokens('md-comp-date-picker-modal', data)}\n data-years={years || undefined}\n data-mode={mode}\n className={styles.root({ class: cn(classNames?.root, className) })}\n >\n {header && (\n <div className={styles.header({ class: classNames?.header })}>\n <div className={styles.title({ class: classNames?.title })}>{title}</div>\n <div className={styles.headlineRow()}>\n <div\n aria-live=\"polite\"\n data-slot=\"headline\"\n className={styles.headline({ class: classNames?.headline })}\n >\n {headline}\n </div>\n {modeToggle && (\n <IconButton\n variant=\"standard\"\n // The picker's surface isn't dense, only its input mode's fields.\n density={0}\n icon={mode === 'calendar' ? <EditIcon /> : <CalendarIcon />}\n aria-label={\n mode === 'calendar'\n ? (labels.inputMode ?? 'Switch to text input mode')\n : (labels.calendarMode ?? 'Switch to calendar input mode')\n }\n onPress={() => {\n captureSize();\n captureExit(mode === 'calendar' ? '-48px' : '100%');\n setSwitched(true);\n setMode(mode === 'calendar' ? 'input' : 'calendar');\n }}\n />\n )}\n </div>\n </div>\n )}\n <div className={styles.modeStack()}>\n <div\n key={mode}\n ref={attachExitView}\n data-enter={switched ? mode : undefined}\n className={styles.modeView()}\n >\n {mode === 'input' ? (\n <div className={styles.input({ class: classNames?.body })}>{input}</div>\n ) : range && scroll ? (\n <RangeMonthList state={state as RangeCalendarState} {...bodyProps} />\n ) : range ? (\n <RangeCalendarBody state={state as RangeCalendarState} {...bodyProps} />\n ) : (\n <SingleCalendarBody state={state as CalendarState} {...bodyProps} />\n )}\n </div>\n <div ref={attachExitSlot} aria-hidden=\"true\" inert className={styles.modeExit()} />\n </div>\n </div>\n );\n}\n\ninterface BodyProps {\n styles: ReturnType<typeof datePickerStyles>;\n years: boolean;\n setYears: (open: boolean) => void;\n yearRange: [number, number];\n labels: Partial<DatePickerLabels>;\n className?: string | undefined;\n classNames?: DateRangePickerClassNames | undefined;\n}\n\nfunction SingleCalendarBody({ state, ...props }: BodyProps & { state: CalendarState }) {\n const { calendarProps, prevButtonProps, nextButtonProps } = useCalendar({}, state);\n return (\n <CalendarBody\n {...props}\n state={state}\n calendarProps={calendarProps}\n prevButtonProps={prevButtonProps}\n nextButtonProps={nextButtonProps}\n />\n );\n}\n\nfunction RangeCalendarBody({ state, ...props }: BodyProps & { state: RangeCalendarState }) {\n const ref = useRef<HTMLDivElement>(null);\n const { calendarProps, prevButtonProps, nextButtonProps } = useRangeCalendar({}, state, ref);\n return (\n <CalendarBody\n {...props}\n bodyRef={ref}\n state={state}\n calendarProps={calendarProps}\n prevButtonProps={prevButtonProps}\n nextButtonProps={nextButtonProps}\n />\n );\n}\n\nfunction CalendarBody({\n state,\n calendarProps,\n prevButtonProps,\n nextButtonProps,\n styles,\n years,\n setYears,\n yearRange,\n labels,\n className,\n classNames,\n bodyRef,\n}: BodyProps & {\n state: CalendarState | RangeCalendarState;\n calendarProps: HTMLAttributes<HTMLElement>;\n prevButtonProps: AriaButtonProps;\n nextButtonProps: AriaButtonProps;\n bodyRef?: Ref<HTMLDivElement>;\n}) {\n const confirm = useContext(PickerDialogContext);\n const monthFormatter = useDateFormatter({ month: 'long', year: 'numeric' });\n const monthLabel = monthFormatter.format(state.visibleRange.start.toDate(getLocalTimeZone()));\n const menuRef = useRef<HTMLButtonElement>(null);\n const [focusYears, setFocusYears] = useState(false);\n // Compose shrinks the year list and fades it out (`shrinkVertically` + `fadeOut`); the\n // first of the two transitions to end removes it.\n const yearsPresence = usePresence(years, ['clip-path', 'opacity']);\n const { swipeProps, slideKey, slideDirection } = useMonthSwipe(state);\n const onKeyDownCapture = usePickerKeys(state, {\n onConfirm: confirm?.requestConfirm,\n onMonthMenu: () => menuRef.current?.focus(),\n onYearMenu: () => {\n setFocusYears(true);\n setYears(true);\n },\n });\n return (\n <div\n {...calendarProps}\n ref={bodyRef}\n onKeyDownCapture={onKeyDownCapture}\n className={styles.body({ class: className })}\n >\n <div className={styles.nav()}>\n <TooltipTrigger>\n <Button\n ref={menuRef}\n variant=\"text\"\n className={cn('text-on-surface-variant', classNames?.yearButton)}\n aria-label={`${monthLabel}, ${years ? (labels.showDays ?? 'Switch to selecting a day') : (labels.showYears ?? 'Switch to selecting a year')}`}\n onPress={() => {\n setFocusYears(false);\n setYears(!years);\n }}\n >\n {/* The drop-down arrow trails the label here, which Button's one leading icon can't do. */}\n <span className=\"inline-flex items-center gap-[8px]\">\n <span aria-live=\"polite\">{monthLabel}</span>\n <span\n aria-hidden=\"true\"\n className=\"inline-flex size-[20px] items-center justify-center\"\n >\n <span className={styles.yearButtonIcon()}>\n <ArrowDropDownIcon />\n </span>\n </span>\n </span>\n </Button>\n <LazyTooltip>{labels.yearsTooltip ?? 'Choose a year (Shift+Y)'}</LazyTooltip>\n </TooltipTrigger>\n {!years && (\n <div className={styles.navButtons()}>\n <ShortcutIconButton\n icon={<ChevronLeftIcon />}\n label={labels.previousMonth ?? 'Previous month'}\n tooltip={labels.previousMonthTooltip ?? 'Previous month (Page Up)'}\n onPress={prevButtonProps.onPress as () => void}\n disabled={prevButtonProps.isDisabled}\n />\n <ShortcutIconButton\n icon={<ChevronRightIcon />}\n label={labels.nextMonth ?? 'Next month'}\n tooltip={labels.nextMonthTooltip ?? 'Next month (Page Down)'}\n onPress={nextButtonProps.onPress as () => void}\n disabled={nextButtonProps.isDisabled}\n />\n </div>\n )}\n </div>\n {!years && (\n <div {...swipeProps} className={styles.swipe()}>\n <div key={slideKey} data-direction={slideDirection} className={styles.slide()}>\n <CalendarGrid state={state} styles={styles} classNames={classNames} />\n </div>\n </div>\n )}\n {/* Closing, the list stays over the grid until it has shrunk and faded away. */}\n {yearsPresence.isPresent && (\n <YearList\n state={state}\n styles={styles}\n yearRange={yearRange}\n className={classNames?.year}\n label={labels.yearList ?? 'Year'}\n autoFocus={focusYears}\n exiting={yearsPresence.isExiting}\n exitProps={yearsPresence.exitProps}\n onPick={() => setYears(false)}\n />\n )}\n </div>\n );\n}\n\n/**\n * The range picker's months, one under another in a vertical scroll (M3: \"To navigate\n * across months, scroll vertically\"), under a single weekday row. Only the months in view\n * (and the focused one) are rendered; the rest are spacers of the same fixed height.\n */\nfunction RangeMonthList({\n state,\n styles,\n className,\n classNames,\n}: BodyProps & { state: RangeCalendarState }) {\n const confirm = useContext(PickerDialogContext);\n const ref = useRef<HTMLDivElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const { calendarProps } = useRangeCalendar({}, state, ref);\n const { locale } = useLocale();\n const first = state.visibleRange.start;\n const count = monthsBetween(first, state.visibleRange.end) + 1;\n const focusedIndex = monthsBetween(first, state.focusedDate);\n const [view, setView] = useState(() => ({ top: focusedIndex * MONTH_HEIGHT, height: 0 }));\n const weekdays = useDateFormatter({ weekday: 'narrow' });\n const longWeekdays = useDateFormatter({ weekday: 'long' });\n const subhead = useDateFormatter({ month: 'long', year: 'numeric' });\n const onKeyDownCapture = usePickerKeys(state, {\n onConfirm: confirm?.requestConfirm,\n onMonthMenu: () => undefined,\n onYearMenu: () => undefined,\n });\n\n // Open on the focused month (the range's start, or today), then follow the scroll.\n const initialTop = useRef(view.top);\n useLayoutEffect(() => {\n const list = listRef.current;\n if (!list) return;\n // A list out of sight (`display: none` above it) can't scroll, so it scrolls to the focused\n // month once it shows; it used to open on the range's first month.\n let opened = list.getClientRects().length > 0;\n list.scrollTop = initialTop.current;\n const measure = () => {\n if (!opened && list.clientHeight > 0) {\n opened = true;\n list.scrollTop = initialTop.current;\n }\n setView({ top: list.scrollTop, height: list.clientHeight });\n };\n measure();\n const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(measure);\n observer?.observe(list);\n list.addEventListener('scroll', measure, { passive: true });\n return () => {\n observer?.disconnect();\n list.removeEventListener('scroll', measure);\n };\n }, []);\n\n const height = view.height || MONTH_HEIGHT;\n let from = Math.max(0, Math.floor(view.top / MONTH_HEIGHT) - 1);\n let to = Math.min(count - 1, Math.floor((view.top + height) / MONTH_HEIGHT) + 1);\n if (state.isFocused) {\n from = Math.min(from, focusedIndex);\n to = Math.max(to, focusedIndex);\n }\n const weekStart = startOfWeek(today(getLocalTimeZone()), locale);\n\n return (\n <div\n {...mergeProps(calendarProps, { onKeyDownCapture })}\n ref={ref}\n className={styles.body({ class: cn('min-h-0 flex-1', className) })}\n >\n <div aria-hidden=\"true\" className={styles.weekdayRow()}>\n {Array.from({ length: 7 }, (_, index) => {\n const day = weekStart.add({ days: index }).toDate(getLocalTimeZone());\n return (\n <span\n key={index}\n title={longWeekdays.format(day)}\n className={styles.weekdayCell({ class: classNames?.weekday })}\n >\n {weekdays.format(day)}\n </span>\n );\n })}\n </div>\n <div ref={listRef} className={styles.months()}>\n <div style={{ height: from * MONTH_HEIGHT }} />\n {Array.from({ length: to - from + 1 }, (_, i) => {\n const month = first.add({ months: from + i });\n return (\n <div key={month.toString()} className=\"h-[336px]\">\n <div\n aria-hidden=\"true\"\n className={styles.monthSubhead({ class: classNames?.monthSubhead })}\n >\n {subhead.format(month.toDate(getLocalTimeZone()))}\n </div>\n <CalendarGrid state={state} styles={styles} month={month} classNames={classNames} />\n </div>\n );\n })}\n <div style={{ height: (count - 1 - to) * MONTH_HEIGHT }} />\n </div>\n </div>\n );\n}\n\n/** A month in the scrolling list: its 48px subhead and six 48px weeks. */\nconst MONTH_HEIGHT = 336;\n\nfunction YearList({\n state,\n styles,\n yearRange,\n className,\n label,\n autoFocus,\n exiting,\n exitProps,\n onPick,\n}: {\n state: CalendarState | RangeCalendarState;\n styles: ReturnType<typeof datePickerStyles>;\n yearRange: [number, number];\n className: string | undefined;\n label: string;\n autoFocus: boolean;\n /** Closing: drawn over the grid, out of the accessibility tree and taking no presses. */\n exiting: boolean;\n exitProps: { onTransitionEnd: (event: TransitionEvent<HTMLElement>) => void };\n onPick: () => void;\n}) {\n const current = today(getLocalTimeZone()).year;\n const focusedYear = state.focusedDate.year;\n const minYear = state.minValue?.year ?? -Infinity;\n const maxYear = state.maxValue?.year ?? Infinity;\n return (\n <div\n {...exitProps}\n role=\"radiogroup\"\n aria-label={label}\n aria-hidden={exiting || undefined}\n inert={exiting || undefined}\n data-exiting={exiting || undefined}\n className={styles.years()}\n ref={(element) => {\n if (exiting) return;\n // Open on the focused year, as Compose scrolls the year list to it.\n const selected = element?.querySelector<HTMLElement>('[aria-checked=\"true\"]');\n selected?.scrollIntoView?.({ block: 'center' });\n if (autoFocus) selected?.focus({ preventScroll: true });\n }}\n >\n {Array.from({ length: yearRange[1] - yearRange[0] + 1 }, (_, i) => yearRange[0] + i).map(\n (year) => (\n <ChoiceButton\n key={year}\n selected={year === focusedYear}\n disabled={year < minYear || year > maxYear}\n data-current={year === current || undefined}\n steps={YEAR_STEPS}\n className={styles.year({ class: className })}\n onPick={() => {\n state.setFocusedDate(state.focusedDate.set({ year }));\n onPick();\n }}\n >\n {year}\n </ChoiceButton>\n ),\n )}\n </div>\n );\n}\n\n/** Three years to a row. */\nconst YEAR_STEPS = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -3, ArrowDown: 3 };\n"],"mappings":"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"}
|
|
@@ -162,8 +162,8 @@ export declare const dockedDatePickerStyles: import("tailwind-variants").TVRetur
|
|
|
162
162
|
view?: import("tailwind-variants").ClassValue;
|
|
163
163
|
item?: import("tailwind-variants").ClassValue;
|
|
164
164
|
check?: import("tailwind-variants").ClassValue;
|
|
165
|
-
menuIcon?: import("tailwind-variants").ClassValue;
|
|
166
165
|
list?: import("tailwind-variants").ClassValue;
|
|
166
|
+
menuIcon?: import("tailwind-variants").ClassValue;
|
|
167
167
|
group?: import("tailwind-variants").ClassValue;
|
|
168
168
|
calendar?: import("tailwind-variants").ClassValue;
|
|
169
169
|
menuButton?: import("tailwind-variants").ClassValue;
|
|
@@ -178,8 +178,8 @@ export declare const dockedDatePickerStyles: import("tailwind-variants").TVRetur
|
|
|
178
178
|
view?: import("tailwind-variants").ClassValue;
|
|
179
179
|
item?: import("tailwind-variants").ClassValue;
|
|
180
180
|
check?: import("tailwind-variants").ClassValue;
|
|
181
|
-
menuIcon?: import("tailwind-variants").ClassValue;
|
|
182
181
|
list?: import("tailwind-variants").ClassValue;
|
|
182
|
+
menuIcon?: import("tailwind-variants").ClassValue;
|
|
183
183
|
group?: import("tailwind-variants").ClassValue;
|
|
184
184
|
calendar?: import("tailwind-variants").ClassValue;
|
|
185
185
|
menuButton?: import("tailwind-variants").ClassValue;
|
|
@@ -206,8 +206,8 @@ export declare const dockedDatePickerStyles: import("tailwind-variants").TVRetur
|
|
|
206
206
|
view?: import("tailwind-variants").ClassValue;
|
|
207
207
|
item?: import("tailwind-variants").ClassValue;
|
|
208
208
|
check?: import("tailwind-variants").ClassValue;
|
|
209
|
-
menuIcon?: import("tailwind-variants").ClassValue;
|
|
210
209
|
list?: import("tailwind-variants").ClassValue;
|
|
210
|
+
menuIcon?: import("tailwind-variants").ClassValue;
|
|
211
211
|
group?: import("tailwind-variants").ClassValue;
|
|
212
212
|
calendar?: import("tailwind-variants").ClassValue;
|
|
213
213
|
menuButton?: import("tailwind-variants").ClassValue;
|
|
@@ -2,6 +2,6 @@ import { ComponentTokenSet } from "../../tokens/component.js";
|
|
|
2
2
|
import "../../index.js";
|
|
3
3
|
//#region src/components/date-picker/date-picker-tokens.d.ts
|
|
4
4
|
/** The tokens of {@link DatePicker}, {@link DateRangePicker} and the date {@link PickerDialog}. */
|
|
5
|
-
export declare const datePickerTokenSet: ComponentTokenSet<"container-color" | "container-
|
|
5
|
+
export declare const datePickerTokenSet: ComponentTokenSet<"container-color" | "container-shape" | "container-elevation" | "header-headline-color" | "date-selected-container-color" | "date-selected-label-text-color" | "date-today-container-outline-color" | "date-today-label-text-color" | "range-selection-active-indicator-container-color">;
|
|
6
6
|
//#endregion
|
|
7
7
|
//# sourceMappingURL=date-picker-tokens.d.ts.map
|
|
@@ -2,6 +2,6 @@ import { ComponentTokenSet } from "../../tokens/component.js";
|
|
|
2
2
|
import "../../index.js";
|
|
3
3
|
//#region src/components/dialog/dialog-tokens.d.ts
|
|
4
4
|
/** The tokens of {@link Dialog}: `--md-comp-dialog-<part>`. */
|
|
5
|
-
export declare const dialogTokenSet: ComponentTokenSet<"container-color" | "container-
|
|
5
|
+
export declare const dialogTokenSet: ComponentTokenSet<"container-color" | "container-shape" | "supporting-text-color" | "container-elevation" | "with-icon-icon-color" | "headline-color">;
|
|
6
6
|
//#endregion
|
|
7
7
|
//# sourceMappingURL=dialog-tokens.d.ts.map
|
|
@@ -48,6 +48,15 @@ export interface ListItemProps {
|
|
|
48
48
|
defaultChecked?: boolean;
|
|
49
49
|
/** Called when a switch or checkbox item is toggled. */
|
|
50
50
|
onCheckedChange?: (checked: boolean) => void;
|
|
51
|
+
/**
|
|
52
|
+
* Whether a switch, checkbox or radio item that's on takes the selected colours and shape
|
|
53
|
+
* (`secondary-container`, 16px corners), as Compose's toggleable and selectable `ListItem`s
|
|
54
|
+
* do. `false` keeps it plain, with only its control showing the state, as a settings row.
|
|
55
|
+
* Either way the whole item stays the control: a press anywhere changes it, with the state
|
|
56
|
+
* layer and a pointer cursor.
|
|
57
|
+
* @default false for a switch, true for a checkbox or a radio button
|
|
58
|
+
*/
|
|
59
|
+
selectedWhenChecked?: boolean;
|
|
51
60
|
/** Where the switch, checkbox or radio button sits, at the start or end of the row. @default "start", or "end" for a switch */
|
|
52
61
|
controlPlacement?: ControlPlacement;
|
|
53
62
|
/** A radio list item's value, or the value a switch or checkbox item posts with its form. */
|
|
@@ -1226,19 +1226,21 @@ function RadioItem({ item, state, placement, ...view }) {
|
|
|
1226
1226
|
/**
|
|
1227
1227
|
* An item that is a switch, checkbox or radio button (Compose's toggleable and selectable
|
|
1228
1228
|
* `ListItem`s): the item is the input's `<label>`, so pressing anywhere on it changes it,
|
|
1229
|
-
* with the list item's state layer
|
|
1230
|
-
*
|
|
1229
|
+
* with the list item's state layer and shapes. The drawn control shows the state; an item
|
|
1230
|
+
* that's on also takes the selected colours and shape unless it's a switch, or it sets
|
|
1231
|
+
* `selectedWhenChecked`.
|
|
1231
1232
|
*/
|
|
1232
1233
|
function ControlItem({ item, ids, kind, inputProps, inputRef, labelProps, isSelected, isDisabled, isPressed, placement, variant, classNames, position: itemPosition }) {
|
|
1233
1234
|
const ref = useRef(null);
|
|
1234
1235
|
const hiddenInputRef = useMemo(() => mergeRefs(inputRef, preventHiddenInputScroll), [inputRef]);
|
|
1236
|
+
const looksSelected = isSelected && (item.selectedWhenChecked ?? kind !== "switch");
|
|
1235
1237
|
const { interactionProps, dataAttributes, state } = useM3Interaction({
|
|
1236
1238
|
isDisabled,
|
|
1237
1239
|
isPressed,
|
|
1238
|
-
isSelected,
|
|
1240
|
+
isSelected: looksSelected,
|
|
1239
1241
|
within: true
|
|
1240
1242
|
}, ref);
|
|
1241
|
-
const shape = isPressed && !isDisabled ||
|
|
1243
|
+
const shape = isPressed && !isDisabled || looksSelected || state.isFocusVisible ? "active" : state.isHovered && !isDisabled ? "hovered" : "rest";
|
|
1242
1244
|
const styles = itemStyles(item, variant, true, useContext(ListOptionsContext));
|
|
1243
1245
|
const controlState = {
|
|
1244
1246
|
"data-checked": isSelected || void 0,
|
|
@@ -1248,6 +1250,7 @@ function ControlItem({ item, ids, kind, inputProps, inputRef, labelProps, isSele
|
|
|
1248
1250
|
...mergeProps(labelProps, interactionProps),
|
|
1249
1251
|
...dataAttributes,
|
|
1250
1252
|
ref,
|
|
1253
|
+
"data-checked": isSelected || void 0,
|
|
1251
1254
|
"data-shape": shape,
|
|
1252
1255
|
"data-position": itemPosition,
|
|
1253
1256
|
...itemAttributes(variant),
|