@windmorph/react 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/checkbox/checkbox-tokens.d.ts +1 -1
- package/dist/components/date-picker/date-picker-styles.d.ts +6 -6
- package/dist/components/date-picker/date-picker-styles.js +5 -5
- package/dist/components/date-picker/date-picker-styles.js.map +1 -1
- package/dist/components/dialog/dialog-styles.js +5 -5
- package/dist/components/dialog/dialog-styles.js.map +1 -1
- package/dist/components/menu/use-reveal-entering.d.ts +19 -0
- package/dist/components/navigation/NavItem.js +1 -1
- package/dist/components/navigation/NavItem.js.map +1 -1
- package/dist/components/navigation/NavigationRail.d.ts +21 -2
- package/dist/components/navigation/NavigationRail.js +18 -4
- package/dist/components/navigation/NavigationRail.js.map +1 -1
- package/dist/components/navigation/navigation-styles.d.ts +21 -0
- package/dist/components/navigation/navigation-styles.js +6 -1
- package/dist/components/navigation/navigation-styles.js.map +1 -1
- package/dist/components/select/Select.js +12 -1
- package/dist/components/select/Select.js.map +1 -1
- package/dist/components/select/search-list.js +1 -1
- package/dist/components/select/search-list.js.map +1 -1
- package/dist/components/sheet/BottomSheet.js +6 -1
- package/dist/components/sheet/BottomSheet.js.map +1 -1
- package/dist/components/sheet/sheet-styles.js +4 -4
- package/dist/components/sheet/sheet-styles.js.map +1 -1
- package/dist/components/sheet/use-keyboard-inset.js +33 -0
- package/dist/components/sheet/use-keyboard-inset.js.map +1 -0
- package/dist/components/snackbar/snackbar-tokens.d.ts +1 -1
- package/dist/components/text-field/TextField.js +9 -1
- package/dist/components/text-field/TextField.js.map +1 -1
- package/dist/components/time-picker/TimeField.d.ts +118 -0
- package/dist/components/time-picker/TimeField.js +183 -0
- package/dist/components/time-picker/TimeField.js.map +1 -0
- package/dist/components/time-picker/TimeFieldDialog.js +70 -0
- package/dist/components/time-picker/TimeFieldDialog.js.map +1 -0
- package/dist/components/time-picker/TimePicker.js +1 -4
- package/dist/components/time-picker/TimePicker.js.map +1 -1
- package/dist/components/time-picker/hour-cycle.js +10 -0
- package/dist/components/time-picker/hour-cycle.js.map +1 -0
- package/dist/components/time-picker/time-format.js +84 -0
- package/dist/components/time-picker/time-format.js.map +1 -0
- package/dist/composites/button-group/ButtonGroup.d.ts +7 -0
- package/dist/composites/button-group/ButtonGroup.js +3 -2
- package/dist/composites/button-group/ButtonGroup.js.map +1 -1
- package/dist/composites/button-group/button-group-styles.d.ts +15 -3
- package/dist/composites/button-group/button-group-styles.js +16 -1
- package/dist/composites/button-group/button-group-styles.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -2
- package/dist/primitives.d.ts +4 -1
- package/dist/primitives.js +4 -1
- package/dist/styles.compiled.css +1 -1
- package/dist/theme/ThemeProvider.d.ts +6 -0
- package/dist/theme/ThemeProvider.js +44 -11
- package/dist/theme/ThemeProvider.js.map +1 -1
- package/dist/theme/ThemeScript.d.ts +4 -1
- package/dist/theme/ThemeScript.js +4 -1
- package/dist/theme/ThemeScript.js.map +1 -1
- package/dist/theme/context.js +18 -3
- package/dist/theme/context.js.map +1 -1
- package/dist/utils/closed-by.d.ts +5 -0
- package/dist/utils/placement.d.ts +11 -0
- package/package.json +2 -2
|
@@ -2,6 +2,6 @@ import { ComponentTokenSet } from "../../tokens/component.js";
|
|
|
2
2
|
import "../../index.js";
|
|
3
3
|
//#region src/components/checkbox/checkbox-tokens.d.ts
|
|
4
4
|
/** The tokens of {@link Checkbox}: `--md-comp-checkbox-<part>`. */
|
|
5
|
-
export declare const checkboxTokenSet: ComponentTokenSet<"selected-
|
|
5
|
+
export declare const checkboxTokenSet: ComponentTokenSet<"selected-icon-color" | "selected-container-color" | "unselected-outline-color">;
|
|
6
6
|
//#endregion
|
|
7
7
|
//# sourceMappingURL=checkbox-tokens.d.ts.map
|
|
@@ -156,14 +156,14 @@ export declare const datePickerStyles: import("tailwind-variants").TVReturnType<
|
|
|
156
156
|
export declare const dockedDatePickerStyles: import("tailwind-variants").TVReturnType<{
|
|
157
157
|
[key: string]: {
|
|
158
158
|
[key: string]: import("tailwind-variants").ClassValue | {
|
|
159
|
+
panel?: import("tailwind-variants").ClassValue;
|
|
159
160
|
actions?: import("tailwind-variants").ClassValue;
|
|
160
161
|
item?: import("tailwind-variants").ClassValue;
|
|
161
162
|
header?: import("tailwind-variants").ClassValue;
|
|
162
163
|
view?: import("tailwind-variants").ClassValue;
|
|
163
|
-
panel?: import("tailwind-variants").ClassValue;
|
|
164
164
|
list?: import("tailwind-variants").ClassValue;
|
|
165
|
-
menuIcon?: import("tailwind-variants").ClassValue;
|
|
166
165
|
check?: 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;
|
|
@@ -172,14 +172,14 @@ export declare const dockedDatePickerStyles: import("tailwind-variants").TVRetur
|
|
|
172
172
|
} | {
|
|
173
173
|
[x: string]: {
|
|
174
174
|
[x: string]: import("tailwind-variants").ClassValue | {
|
|
175
|
+
panel?: import("tailwind-variants").ClassValue;
|
|
175
176
|
actions?: import("tailwind-variants").ClassValue;
|
|
176
177
|
item?: import("tailwind-variants").ClassValue;
|
|
177
178
|
header?: import("tailwind-variants").ClassValue;
|
|
178
179
|
view?: import("tailwind-variants").ClassValue;
|
|
179
|
-
panel?: import("tailwind-variants").ClassValue;
|
|
180
180
|
list?: import("tailwind-variants").ClassValue;
|
|
181
|
-
menuIcon?: import("tailwind-variants").ClassValue;
|
|
182
181
|
check?: 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;
|
|
@@ -200,14 +200,14 @@ export declare const dockedDatePickerStyles: import("tailwind-variants").TVRetur
|
|
|
200
200
|
}, undefined, {
|
|
201
201
|
[key: string]: {
|
|
202
202
|
[key: string]: import("tailwind-variants").ClassValue | {
|
|
203
|
+
panel?: import("tailwind-variants").ClassValue;
|
|
203
204
|
actions?: import("tailwind-variants").ClassValue;
|
|
204
205
|
item?: import("tailwind-variants").ClassValue;
|
|
205
206
|
header?: import("tailwind-variants").ClassValue;
|
|
206
207
|
view?: import("tailwind-variants").ClassValue;
|
|
207
|
-
panel?: import("tailwind-variants").ClassValue;
|
|
208
208
|
list?: import("tailwind-variants").ClassValue;
|
|
209
|
-
menuIcon?: import("tailwind-variants").ClassValue;
|
|
210
209
|
check?: 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;
|
|
@@ -63,7 +63,7 @@ const datePickerStyles = tv({
|
|
|
63
63
|
"relative mx-auto flex size-[40px] cursor-pointer items-center justify-center rounded-corner-full",
|
|
64
64
|
"before:absolute before:-inset-[4px] before:rounded-corner-full",
|
|
65
65
|
"text-body-large text-on-surface outline-none state-layer focus-ring",
|
|
66
|
-
"data-today:border data-in-range:text-on-secondary-container",
|
|
66
|
+
"data-today:not-data-selected:border data-in-range:not-data-selected:text-on-secondary-container",
|
|
67
67
|
"data-disabled:cursor-default data-disabled:text-on-surface/38",
|
|
68
68
|
"data-outside:invisible",
|
|
69
69
|
...forcedSelection,
|
|
@@ -83,7 +83,7 @@ const datePickerStyles = tv({
|
|
|
83
83
|
"relative flex h-[36px] w-[72px] cursor-pointer items-center justify-center rounded-corner-full",
|
|
84
84
|
"before:absolute before:-inset-y-[6px] before:inset-x-0",
|
|
85
85
|
"text-body-large text-on-surface-variant outline-none state-layer focus-ring",
|
|
86
|
-
"data-current:border data-current:border-primary data-current:text-primary",
|
|
86
|
+
"data-current:not-data-selected:border data-current:not-data-selected:border-primary data-current:not-data-selected:text-primary",
|
|
87
87
|
"data-selected:bg-primary data-selected:text-on-primary",
|
|
88
88
|
"data-disabled:cursor-default data-disabled:text-on-surface/38",
|
|
89
89
|
...forcedSelection
|
|
@@ -111,14 +111,14 @@ const datePickerStyles = tv({
|
|
|
111
111
|
true: {
|
|
112
112
|
body: "ps-0 pe-0",
|
|
113
113
|
day: [
|
|
114
|
-
"data-today:border-primary data-today:text-primary",
|
|
114
|
+
"data-today:not-data-selected:border-primary data-today:not-data-selected:text-primary",
|
|
115
115
|
"data-selected:bg-primary data-selected:text-on-primary",
|
|
116
116
|
"data-outside:visible data-outside:text-on-surface-variant"
|
|
117
117
|
]
|
|
118
118
|
},
|
|
119
119
|
false: { day: [
|
|
120
|
-
"data-today:border-[color:var(--_md-comp-date-picker-modal-date-today-container-outline-color)]",
|
|
121
|
-
"data-today:text-[color:var(--_md-comp-date-picker-modal-date-today-label-text-color)]",
|
|
120
|
+
"data-today:not-data-selected:border-[color:var(--_md-comp-date-picker-modal-date-today-container-outline-color)]",
|
|
121
|
+
"data-today:not-data-selected:text-[color:var(--_md-comp-date-picker-modal-date-today-label-text-color)]",
|
|
122
122
|
"data-selected:bg-[var(--_md-comp-date-picker-modal-date-selected-container-color)]",
|
|
123
123
|
"data-selected:text-[color:var(--_md-comp-date-picker-modal-date-selected-label-text-color)]"
|
|
124
124
|
] }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date-picker-styles.js","names":[],"sources":["../../../src/components/date-picker/date-picker-styles.ts"],"sourcesContent":["import { tv, type VariantProps } from '../../utils/tv';\n\n/*\n * Values follow the M3 date picker spec, with sizes from Compose Material 3 (DatePicker.kt,\n * DateRangePicker.kt, DatePickerDialog.kt, DatePickerModalTokens).\n *\n * At least 360px wide. Header at least 120px (128px for ranges): a `label-large` title\n * (24px start, 12px end, 16px top) and a `headline-large` headline (`title-large` for\n * ranges; 24px start, 12px end and bottom), both `on-surface-variant`, with the mode toggle\n * at the end, above an `outline-variant` divider. The body has 12px sides: a 56px month row\n * (the month / year button, `on-surface-variant`, and previous / next buttons), a 48px\n * weekday row (`body-large` `on-surface`) and six 48px week rows. Days are 40px circles\n * with a 48px touch target: `body-large` `on-surface`; selected `primary` / `on-primary`;\n * today a 1px `primary` outline with `primary` text; disabled 38%. In a range, the days\n * between are a 40px `secondary-container` band with `on-secondary-container` text. Years\n * are 72 × 36px pills with a 48px-tall target, in three columns 16px apart (selected\n * `primary`, this year outlined), in a 335px area.\n *\n * Months slide in from the side they come from on the default spatial spring (a fade only\n * with reduced motion). The range picker's month list scrolls vertically: each month has a\n * 48px `title-small` `on-surface-variant` subhead (8px below it, 24px in) over its six\n * week rows, under one weekday row.\n *\n * The headline, the picked days, today and the range band come from the modal date\n * picker's component tokens (`date-picker-tokens.ts`, `--md-comp-date-picker-modal-*`): the\n * root carries `data-tokens=\"md-comp-date-picker-modal\"`, and the classes read one private\n * variable per part. A consumer class still wins in `cn()`.\n *\n * Every class is written out in full so Tailwind can find it.\n */\n\n/**\n * Forced colours paint backgrounds as Canvas, so a picked day or year looked like any\n * other: it keeps a Highlight fill with HighlightText, opting out of the forced palette\n * (2026-10-15). `!important` beats the token colours, whose selectors are as specific.\n */\nconst forcedSelection = [\n 'forced-colors:data-selected:bg-[Highlight]! forced-colors:data-selected:text-[HighlightText]!',\n 'forced-colors:data-selected:forced-color-adjust-none',\n];\n\n/** Variant definitions for {@link DatePicker}, {@link DateRangePicker} and the docked calendar. */\nexport const datePickerStyles = tv({\n slots: {\n root: [\n 'group/picker flex w-[360px] max-w-full shrink-0 flex-col text-on-surface',\n // A full-screen picker dialog spans the window, and the month list fills its height.\n 'group-data-full-screen/picker-dialog:min-h-0 group-data-full-screen/picker-dialog:w-full group-data-full-screen/picker-dialog:flex-1',\n ],\n header: 'flex shrink-0 flex-col justify-between border-b border-outline-variant',\n title: 'ps-[24px] pe-[12px] pt-[16px] text-label-large text-on-surface-variant',\n headlineRow: 'flex items-end justify-between gap-[8px] ps-[24px] pe-[12px] pb-[12px]',\n headline: 'min-w-0 flex-1 text-[color:var(--_md-comp-date-picker-modal-header-headline-color)]',\n body: 'relative flex min-h-0 flex-col ps-[12px] pe-[12px]',\n nav: 'flex h-[56px] shrink-0 items-center justify-between',\n navButtons: 'flex items-center text-on-surface-variant',\n yearButtonIcon: [\n 'inline-flex size-[18px] [&>svg]:size-full',\n '[transition-property:rotate] [transition-duration:var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n 'group-data-[years]/picker:rotate-180',\n ],\n // Horizontal swipes change the month; vertical ones still scroll the page.\n swipe: 'touch-pan-y overflow-hidden',\n // A new month slides in from the side it comes from.\n slide: [\n 'translate-none opacity-100',\n '[transition-property:translate,opacity]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-default-duration),var(--md-sys-motion-spring-effects-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing),var(--md-sys-motion-spring-effects-default-easing)]',\n 'data-[direction=next]:starting:translate-x-[48px] data-[direction=next]:starting:opacity-0',\n 'data-[direction=previous]:starting:-translate-x-[48px] data-[direction=previous]:starting:opacity-0',\n 'rtl:data-[direction=next]:starting:-translate-x-[48px]',\n 'rtl:data-[direction=previous]:starting:translate-x-[48px]',\n 'motion-reduce:starting:translate-none',\n ],\n // Calendar ↔ text input (Compose's display mode animation, enter side): the input rises\n // from below its own height, the calendar comes down 48px, both fading in, inside a\n // clipping view. Reduced motion keeps the fade.\n // The view and the outgoing view's copy share one clipping cell; the copy's slot has no\n // height, so only the new view sizes the picker.\n modeStack:\n 'grid min-h-0 flex-1 grid-cols-[minmax(0,1fr)] grid-rows-[minmax(0,1fr)] overflow-hidden',\n modeExit: 'pointer-events-none h-0 [grid-area:1/1]',\n modeView: [\n 'flex min-h-0 flex-1 flex-col overflow-hidden [grid-area:1/1]',\n '[&>*]:[transition-property:translate,opacity]',\n '[&>*]:[transition-duration:var(--md-sys-motion-spring-spatial-default-duration),var(--md-sys-motion-spring-effects-default-duration)]',\n '[&>*]:[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing),var(--md-sys-motion-spring-effects-default-easing)]',\n 'data-[enter=input]:[&>*]:starting:translate-y-full data-[enter=input]:[&>*]:starting:opacity-0',\n 'data-[enter=calendar]:[&>*]:starting:-translate-y-[48px] data-[enter=calendar]:[&>*]:starting:opacity-0',\n 'motion-reduce:[&>*]:starting:translate-none',\n ],\n grid: 'w-full table-fixed border-collapse',\n weekday: 'h-[48px] cursor-default p-0 text-center text-body-large font-normal text-on-surface',\n weekdayRow: 'grid shrink-0 grid-cols-7',\n weekdayCell:\n 'flex h-[48px] cursor-default items-center justify-center text-body-large text-on-surface',\n cell: [\n 'h-[48px] p-0 text-center',\n // The range band: a 40px stripe behind the days between the ends.\n // 1px wider than the cell, so neighbouring bands never leave a hairline between them.\n 'bg-[length:calc(100%+1px)_40px] bg-center bg-no-repeat',\n 'data-[range=middle]:bg-[image:var(--_md-comp-date-picker-modal-range-middle)]',\n 'data-[range=start]:bg-[image:var(--_md-comp-date-picker-modal-range-to-right)]',\n 'data-[range=end]:bg-[image:var(--_md-comp-date-picker-modal-range-to-left)]',\n 'rtl:data-[range=start]:bg-[image:var(--_md-comp-date-picker-modal-range-to-left)]',\n 'rtl:data-[range=end]:bg-[image:var(--_md-comp-date-picker-modal-range-to-right)]',\n ],\n day: [\n // The 40px circle; its ::before widens the touch target to the 48px cell.\n 'relative mx-auto flex size-[40px] cursor-pointer items-center justify-center rounded-corner-full',\n 'before:absolute before:-inset-[4px] before:rounded-corner-full',\n 'text-body-large text-on-surface outline-none state-layer focus-ring',\n 'data-today:border data-in-range:text-on-secondary-container',\n 'data-disabled:cursor-default data-disabled:text-on-surface/38',\n 'data-outside:invisible',\n ...forcedSelection,\n // The range band is a background, which forced colours drop: the days between the\n // ends take a Highlight ring instead (2026-10-15).\n 'forced-colors:data-in-range:not-data-selected:border forced-colors:data-in-range:not-data-selected:border-[Highlight]',\n ],\n years: [\n 'grid h-[335px] grid-cols-3 content-start justify-items-center gap-y-[16px] overflow-y-auto overscroll-contain pt-[8px] pb-[8px]',\n // It expands down from the month row as it fades in from 60% (Compose's year picker\n // `expandVertically` + `fadeIn(initialAlpha = 0.6f)`, default effects); reduced motion\n // keeps the fade.\n '[clip-path:inset(0)] [transition-property:clip-path,opacity]',\n '[transition-duration:var(--md-sys-motion-spring-effects-default-duration)] [transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n 'starting:[clip-path:inset(0_0_100%_0)] starting:opacity-60 motion-reduce:starting:[clip-path:inset(0)]',\n // Closing, it lies over the grid below the month row, on the picker's container colour,\n // and shrinks back up to the month row as it fades out (Compose's `shrinkVertically` +\n // `fadeOut`, fast effects). Reduced motion keeps the fade.\n 'data-exiting:absolute data-exiting:inset-x-[12px] data-exiting:top-[56px] data-exiting:z-10',\n 'data-exiting:bg-[var(--_md-comp-date-picker-modal-container-color)] data-exiting:pointer-events-none',\n 'data-exiting:[clip-path:inset(0_0_100%_0)] data-exiting:opacity-0 motion-reduce:data-exiting:[clip-path:inset(0)]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)] data-exiting:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n ],\n year: [\n // The 36px pill; its ::before makes the touch target 48px tall.\n 'relative flex h-[36px] w-[72px] cursor-pointer items-center justify-center rounded-corner-full',\n 'before:absolute before:-inset-y-[6px] before:inset-x-0',\n 'text-body-large text-on-surface-variant outline-none state-layer focus-ring',\n 'data-current:border data-current:border-primary data-current:text-primary',\n 'data-selected:bg-primary data-selected:text-on-primary',\n 'data-disabled:cursor-default data-disabled:text-on-surface/38',\n ...forcedSelection,\n ],\n months: [\n 'h-[344px] min-h-0 overflow-y-auto overscroll-contain',\n 'group-data-full-screen/picker-dialog:h-auto group-data-full-screen/picker-dialog:flex-1',\n ],\n monthSubhead:\n 'flex h-[48px] items-end ps-[24px] pe-[12px] pb-[8px] text-title-small text-on-surface-variant',\n input: 'flex flex-col gap-[16px] ps-[24px] pe-[24px] pt-[16px] pb-[8px]',\n },\n variants: {\n range: {\n false: { header: 'min-h-[120px]', headline: 'text-headline-large' },\n // Full screen, the top row takes the title's space: Compose's range header is the\n // token's 128px less its 60px `HeaderHeightOffset`.\n true: {\n header: 'min-h-[128px] group-data-full-screen/picker-dialog:min-h-[68px]',\n headline: 'text-title-large',\n },\n },\n /**\n * The docked picker shows the days of the months around the shown one. Its days keep\n * their classes: the modal picker's tokens (`date-picker-tokens.ts`) colour the others.\n */\n docked: {\n true: {\n body: 'ps-0 pe-0',\n day: [\n 'data-today:border-primary data-today:text-primary',\n 'data-selected:bg-primary data-selected:text-on-primary',\n 'data-outside:visible data-outside:text-on-surface-variant',\n ],\n },\n false: {\n day: [\n 'data-today:border-[color:var(--_md-comp-date-picker-modal-date-today-container-outline-color)]',\n 'data-today:text-[color:var(--_md-comp-date-picker-modal-date-today-label-text-color)]',\n 'data-selected:bg-[var(--_md-comp-date-picker-modal-date-selected-container-color)]',\n 'data-selected:text-[color:var(--_md-comp-date-picker-modal-date-selected-label-text-color)]',\n ],\n },\n },\n },\n defaultVariants: { range: false, docked: false },\n});\n\n/*\n * The docked date picker (M3 \"Docked date picker\"): a `surface-container-high` panel with\n * 16px corners at level 3 below its date field, 360px wide with 12px sides. A 56px row\n * holds two menu buttons (month and year), each between its previous / next icon buttons;\n * the month or year menu replaces the calendar with a 48px-row list under an\n * `outline-variant` divider (the chosen row `surface-variant` with a check), and text\n * buttons end the panel.\n */\n\n/** Variant definitions for {@link DateField}'s docked calendar. */\nexport const dockedDatePickerStyles = tv({\n slots: {\n panel: [\n 'flex w-[360px] max-w-full flex-col rounded-corner-large bg-surface-container-high',\n 'ps-[12px] pe-[12px] text-on-surface shadow-elevation-3 outline-none',\n // Forced colours drop the surface and the shadow: a system-colour outline keeps the edge.\n 'forced-colors:outline-solid forced-colors:outline-1 forced-colors:outline-[CanvasText]',\n ],\n calendar: 'flex flex-col',\n header: 'flex h-[56px] shrink-0 items-center justify-between',\n group: 'flex items-center text-on-surface-variant',\n menuButton: 'min-w-0 ps-[8px] pe-[4px] text-on-surface-variant',\n menuIcon: [\n 'inline-flex size-[18px] [&>svg]:size-full',\n '[transition-property:rotate] [transition-duration:var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n 'data-open:rotate-180',\n ],\n view: 'h-[336px] min-h-0',\n list: [\n '-ms-[12px] -me-[12px] h-[336px] overflow-y-auto overscroll-contain',\n 'border-t border-outline-variant pt-[8px] pb-[8px]',\n ],\n item: [\n 'flex h-[48px] w-full cursor-pointer items-center gap-[16px] ps-[16px] pe-[24px] text-start',\n 'text-body-large text-on-surface outline-none state-layer focus-ring-inset',\n 'data-selected:bg-surface-variant',\n 'data-disabled:cursor-default data-disabled:text-on-surface/38',\n ],\n check: 'inline-flex size-[24px] shrink-0 [&>svg]:size-full',\n actions: 'flex shrink-0 items-center justify-end gap-[8px] pt-[4px] pb-[8px]',\n },\n});\n\n/**\n * The variants of {@link datePickerStyles}, 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 DatePickerStyleProps = VariantProps<typeof datePickerStyles>;\n"],"mappings":";;;;;;;AAoCA,MAAM,kBAAkB,CACtB,iGACA,sDACF;;AAGA,MAAa,mBAAmB,GAAG;CACjC,OAAO;EACL,MAAM,CACJ,4EAEA,sIACF;EACA,QAAQ;EACR,OAAO;EACP,aAAa;EACb,UAAU;EACV,MAAM;EACN,KAAK;EACL,YAAY;EACZ,gBAAgB;GACd;GACA;GACA;GACA;EACF;EAEA,OAAO;EAEP,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EAMA,WACE;EACF,UAAU;EACV,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,MAAM;EACN,SAAS;EACT,YAAY;EACZ,aACE;EACF,MAAM;GACJ;GAGA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,KAAK;GAEH;GACA;GACA;GACA;GACA;GACA;GACA,GAAG;GAGH;EACF;EACA,OAAO;GACL;GAIA;GACA;GACA;GAIA;GACA;GACA;GACA;EACF;EACA,MAAM;GAEJ;GACA;GACA;GACA;GACA;GACA;GACA,GAAG;EACL;EACA,QAAQ,CACN,wDACA,yFACF;EACA,cACE;EACF,OAAO;CACT;CACA,UAAU;EACR,OAAO;GACL,OAAO;IAAE,QAAQ;IAAiB,UAAU;GAAsB;GAGlE,MAAM;IACJ,QAAQ;IACR,UAAU;GACZ;EACF;;;;;EAKA,QAAQ;GACN,MAAM;IACJ,MAAM;IACN,KAAK;KACH;KACA;KACA;IACF;GACF;GACA,OAAO,EACL,KAAK;IACH;IACA;IACA;IACA;GACF,EACF;EACF;CACF;CACA,iBAAiB;EAAE,OAAO;EAAO,QAAQ;CAAM;AACjD,CAAC;;AAYD,MAAa,yBAAyB,GAAG,EACvC,OAAO;CACL,OAAO;EACL;EACA;EAEA;CACF;CACA,UAAU;CACV,QAAQ;CACR,OAAO;CACP,YAAY;CACZ,UAAU;EACR;EACA;EACA;EACA;CACF;CACA,MAAM;CACN,MAAM,CACJ,sEACA,mDACF;CACA,MAAM;EACJ;EACA;EACA;EACA;CACF;CACA,OAAO;CACP,SAAS;AACX,EACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"date-picker-styles.js","names":[],"sources":["../../../src/components/date-picker/date-picker-styles.ts"],"sourcesContent":["import { tv, type VariantProps } from '../../utils/tv';\n\n/*\n * Values follow the M3 date picker spec, with sizes from Compose Material 3 (DatePicker.kt,\n * DateRangePicker.kt, DatePickerDialog.kt, DatePickerModalTokens).\n *\n * At least 360px wide. Header at least 120px (128px for ranges): a `label-large` title\n * (24px start, 12px end, 16px top) and a `headline-large` headline (`title-large` for\n * ranges; 24px start, 12px end and bottom), both `on-surface-variant`, with the mode toggle\n * at the end, above an `outline-variant` divider. The body has 12px sides: a 56px month row\n * (the month / year button, `on-surface-variant`, and previous / next buttons), a 48px\n * weekday row (`body-large` `on-surface`) and six 48px week rows. Days are 40px circles\n * with a 48px touch target: `body-large` `on-surface`; selected `primary` / `on-primary`;\n * today a 1px `primary` outline with `primary` text; disabled 38%. In a range, the days\n * between are a 40px `secondary-container` band with `on-secondary-container` text. Years\n * are 72 × 36px pills with a 48px-tall target, in three columns 16px apart (selected\n * `primary`, this year outlined), in a 335px area.\n *\n * Months slide in from the side they come from on the default spatial spring (a fade only\n * with reduced motion). The range picker's month list scrolls vertically: each month has a\n * 48px `title-small` `on-surface-variant` subhead (8px below it, 24px in) over its six\n * week rows, under one weekday row.\n *\n * The headline, the picked days, today and the range band come from the modal date\n * picker's component tokens (`date-picker-tokens.ts`, `--md-comp-date-picker-modal-*`): the\n * root carries `data-tokens=\"md-comp-date-picker-modal\"`, and the classes read one private\n * variable per part. A consumer class still wins in `cn()`.\n *\n * Every class is written out in full so Tailwind can find it.\n */\n\n/**\n * Forced colours paint backgrounds as Canvas, so a picked day or year looked like any\n * other: it keeps a Highlight fill with HighlightText, opting out of the forced palette\n * (2026-10-15). `!important` beats the token colours, whose selectors are as specific.\n */\nconst forcedSelection = [\n 'forced-colors:data-selected:bg-[Highlight]! forced-colors:data-selected:text-[HighlightText]!',\n 'forced-colors:data-selected:forced-color-adjust-none',\n];\n\n/** Variant definitions for {@link DatePicker}, {@link DateRangePicker} and the docked calendar. */\nexport const datePickerStyles = tv({\n slots: {\n root: [\n 'group/picker flex w-[360px] max-w-full shrink-0 flex-col text-on-surface',\n // A full-screen picker dialog spans the window, and the month list fills its height.\n 'group-data-full-screen/picker-dialog:min-h-0 group-data-full-screen/picker-dialog:w-full group-data-full-screen/picker-dialog:flex-1',\n ],\n header: 'flex shrink-0 flex-col justify-between border-b border-outline-variant',\n title: 'ps-[24px] pe-[12px] pt-[16px] text-label-large text-on-surface-variant',\n headlineRow: 'flex items-end justify-between gap-[8px] ps-[24px] pe-[12px] pb-[12px]',\n headline: 'min-w-0 flex-1 text-[color:var(--_md-comp-date-picker-modal-header-headline-color)]',\n body: 'relative flex min-h-0 flex-col ps-[12px] pe-[12px]',\n nav: 'flex h-[56px] shrink-0 items-center justify-between',\n navButtons: 'flex items-center text-on-surface-variant',\n yearButtonIcon: [\n 'inline-flex size-[18px] [&>svg]:size-full',\n '[transition-property:rotate] [transition-duration:var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n 'group-data-[years]/picker:rotate-180',\n ],\n // Horizontal swipes change the month; vertical ones still scroll the page.\n swipe: 'touch-pan-y overflow-hidden',\n // A new month slides in from the side it comes from.\n slide: [\n 'translate-none opacity-100',\n '[transition-property:translate,opacity]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-default-duration),var(--md-sys-motion-spring-effects-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing),var(--md-sys-motion-spring-effects-default-easing)]',\n 'data-[direction=next]:starting:translate-x-[48px] data-[direction=next]:starting:opacity-0',\n 'data-[direction=previous]:starting:-translate-x-[48px] data-[direction=previous]:starting:opacity-0',\n 'rtl:data-[direction=next]:starting:-translate-x-[48px]',\n 'rtl:data-[direction=previous]:starting:translate-x-[48px]',\n 'motion-reduce:starting:translate-none',\n ],\n // Calendar ↔ text input (Compose's display mode animation, enter side): the input rises\n // from below its own height, the calendar comes down 48px, both fading in, inside a\n // clipping view. Reduced motion keeps the fade.\n // The view and the outgoing view's copy share one clipping cell; the copy's slot has no\n // height, so only the new view sizes the picker.\n modeStack:\n 'grid min-h-0 flex-1 grid-cols-[minmax(0,1fr)] grid-rows-[minmax(0,1fr)] overflow-hidden',\n modeExit: 'pointer-events-none h-0 [grid-area:1/1]',\n modeView: [\n 'flex min-h-0 flex-1 flex-col overflow-hidden [grid-area:1/1]',\n '[&>*]:[transition-property:translate,opacity]',\n '[&>*]:[transition-duration:var(--md-sys-motion-spring-spatial-default-duration),var(--md-sys-motion-spring-effects-default-duration)]',\n '[&>*]:[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing),var(--md-sys-motion-spring-effects-default-easing)]',\n 'data-[enter=input]:[&>*]:starting:translate-y-full data-[enter=input]:[&>*]:starting:opacity-0',\n 'data-[enter=calendar]:[&>*]:starting:-translate-y-[48px] data-[enter=calendar]:[&>*]:starting:opacity-0',\n 'motion-reduce:[&>*]:starting:translate-none',\n ],\n grid: 'w-full table-fixed border-collapse',\n weekday: 'h-[48px] cursor-default p-0 text-center text-body-large font-normal text-on-surface',\n weekdayRow: 'grid shrink-0 grid-cols-7',\n weekdayCell:\n 'flex h-[48px] cursor-default items-center justify-center text-body-large text-on-surface',\n cell: [\n 'h-[48px] p-0 text-center',\n // The range band: a 40px stripe behind the days between the ends.\n // 1px wider than the cell, so neighbouring bands never leave a hairline between them.\n 'bg-[length:calc(100%+1px)_40px] bg-center bg-no-repeat',\n 'data-[range=middle]:bg-[image:var(--_md-comp-date-picker-modal-range-middle)]',\n 'data-[range=start]:bg-[image:var(--_md-comp-date-picker-modal-range-to-right)]',\n 'data-[range=end]:bg-[image:var(--_md-comp-date-picker-modal-range-to-left)]',\n 'rtl:data-[range=start]:bg-[image:var(--_md-comp-date-picker-modal-range-to-left)]',\n 'rtl:data-[range=end]:bg-[image:var(--_md-comp-date-picker-modal-range-to-right)]',\n ],\n day: [\n // The 40px circle; its ::before widens the touch target to the 48px cell.\n 'relative mx-auto flex size-[40px] cursor-pointer items-center justify-center rounded-corner-full',\n 'before:absolute before:-inset-[4px] before:rounded-corner-full',\n 'text-body-large text-on-surface outline-none state-layer focus-ring',\n // Today's outline and colour, and the range's text, give way to a selected day's fill\n // and on-colour: as equal-specificity rules, the later one in the CSS used to win, so a\n // selected today was primary on primary (2026-11-07).\n 'data-today:not-data-selected:border data-in-range:not-data-selected:text-on-secondary-container',\n 'data-disabled:cursor-default data-disabled:text-on-surface/38',\n 'data-outside:invisible',\n ...forcedSelection,\n // The range band is a background, which forced colours drop: the days between the\n // ends take a Highlight ring instead (2026-10-15).\n 'forced-colors:data-in-range:not-data-selected:border forced-colors:data-in-range:not-data-selected:border-[Highlight]',\n ],\n years: [\n 'grid h-[335px] grid-cols-3 content-start justify-items-center gap-y-[16px] overflow-y-auto overscroll-contain pt-[8px] pb-[8px]',\n // It expands down from the month row as it fades in from 60% (Compose's year picker\n // `expandVertically` + `fadeIn(initialAlpha = 0.6f)`, default effects); reduced motion\n // keeps the fade.\n '[clip-path:inset(0)] [transition-property:clip-path,opacity]',\n '[transition-duration:var(--md-sys-motion-spring-effects-default-duration)] [transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n 'starting:[clip-path:inset(0_0_100%_0)] starting:opacity-60 motion-reduce:starting:[clip-path:inset(0)]',\n // Closing, it lies over the grid below the month row, on the picker's container colour,\n // and shrinks back up to the month row as it fades out (Compose's `shrinkVertically` +\n // `fadeOut`, fast effects). Reduced motion keeps the fade.\n 'data-exiting:absolute data-exiting:inset-x-[12px] data-exiting:top-[56px] data-exiting:z-10',\n 'data-exiting:bg-[var(--_md-comp-date-picker-modal-container-color)] data-exiting:pointer-events-none',\n 'data-exiting:[clip-path:inset(0_0_100%_0)] data-exiting:opacity-0 motion-reduce:data-exiting:[clip-path:inset(0)]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)] data-exiting:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n ],\n year: [\n // The 36px pill; its ::before makes the touch target 48px tall.\n 'relative flex h-[36px] w-[72px] cursor-pointer items-center justify-center rounded-corner-full',\n 'before:absolute before:-inset-y-[6px] before:inset-x-0',\n 'text-body-large text-on-surface-variant outline-none state-layer focus-ring',\n 'data-current:not-data-selected:border data-current:not-data-selected:border-primary data-current:not-data-selected:text-primary',\n 'data-selected:bg-primary data-selected:text-on-primary',\n 'data-disabled:cursor-default data-disabled:text-on-surface/38',\n ...forcedSelection,\n ],\n months: [\n 'h-[344px] min-h-0 overflow-y-auto overscroll-contain',\n 'group-data-full-screen/picker-dialog:h-auto group-data-full-screen/picker-dialog:flex-1',\n ],\n monthSubhead:\n 'flex h-[48px] items-end ps-[24px] pe-[12px] pb-[8px] text-title-small text-on-surface-variant',\n input: 'flex flex-col gap-[16px] ps-[24px] pe-[24px] pt-[16px] pb-[8px]',\n },\n variants: {\n range: {\n false: { header: 'min-h-[120px]', headline: 'text-headline-large' },\n // Full screen, the top row takes the title's space: Compose's range header is the\n // token's 128px less its 60px `HeaderHeightOffset`.\n true: {\n header: 'min-h-[128px] group-data-full-screen/picker-dialog:min-h-[68px]',\n headline: 'text-title-large',\n },\n },\n /**\n * The docked picker shows the days of the months around the shown one. Its days keep\n * their classes: the modal picker's tokens (`date-picker-tokens.ts`) colour the others.\n */\n docked: {\n true: {\n body: 'ps-0 pe-0',\n day: [\n 'data-today:not-data-selected:border-primary data-today:not-data-selected:text-primary',\n 'data-selected:bg-primary data-selected:text-on-primary',\n 'data-outside:visible data-outside:text-on-surface-variant',\n ],\n },\n false: {\n day: [\n 'data-today:not-data-selected:border-[color:var(--_md-comp-date-picker-modal-date-today-container-outline-color)]',\n 'data-today:not-data-selected:text-[color:var(--_md-comp-date-picker-modal-date-today-label-text-color)]',\n 'data-selected:bg-[var(--_md-comp-date-picker-modal-date-selected-container-color)]',\n 'data-selected:text-[color:var(--_md-comp-date-picker-modal-date-selected-label-text-color)]',\n ],\n },\n },\n },\n defaultVariants: { range: false, docked: false },\n});\n\n/*\n * The docked date picker (M3 \"Docked date picker\"): a `surface-container-high` panel with\n * 16px corners at level 3 below its date field, 360px wide with 12px sides. A 56px row\n * holds two menu buttons (month and year), each between its previous / next icon buttons;\n * the month or year menu replaces the calendar with a 48px-row list under an\n * `outline-variant` divider (the chosen row `surface-variant` with a check), and text\n * buttons end the panel.\n */\n\n/** Variant definitions for {@link DateField}'s docked calendar. */\nexport const dockedDatePickerStyles = tv({\n slots: {\n panel: [\n 'flex w-[360px] max-w-full flex-col rounded-corner-large bg-surface-container-high',\n 'ps-[12px] pe-[12px] text-on-surface shadow-elevation-3 outline-none',\n // Forced colours drop the surface and the shadow: a system-colour outline keeps the edge.\n 'forced-colors:outline-solid forced-colors:outline-1 forced-colors:outline-[CanvasText]',\n ],\n calendar: 'flex flex-col',\n header: 'flex h-[56px] shrink-0 items-center justify-between',\n group: 'flex items-center text-on-surface-variant',\n menuButton: 'min-w-0 ps-[8px] pe-[4px] text-on-surface-variant',\n menuIcon: [\n 'inline-flex size-[18px] [&>svg]:size-full',\n '[transition-property:rotate] [transition-duration:var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n 'data-open:rotate-180',\n ],\n view: 'h-[336px] min-h-0',\n list: [\n '-ms-[12px] -me-[12px] h-[336px] overflow-y-auto overscroll-contain',\n 'border-t border-outline-variant pt-[8px] pb-[8px]',\n ],\n item: [\n 'flex h-[48px] w-full cursor-pointer items-center gap-[16px] ps-[16px] pe-[24px] text-start',\n 'text-body-large text-on-surface outline-none state-layer focus-ring-inset',\n 'data-selected:bg-surface-variant',\n 'data-disabled:cursor-default data-disabled:text-on-surface/38',\n ],\n check: 'inline-flex size-[24px] shrink-0 [&>svg]:size-full',\n actions: 'flex shrink-0 items-center justify-end gap-[8px] pt-[4px] pb-[8px]',\n },\n});\n\n/**\n * The variants of {@link datePickerStyles}, 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 DatePickerStyleProps = VariantProps<typeof datePickerStyles>;\n"],"mappings":";;;;;;;AAoCA,MAAM,kBAAkB,CACtB,iGACA,sDACF;;AAGA,MAAa,mBAAmB,GAAG;CACjC,OAAO;EACL,MAAM,CACJ,4EAEA,sIACF;EACA,QAAQ;EACR,OAAO;EACP,aAAa;EACb,UAAU;EACV,MAAM;EACN,KAAK;EACL,YAAY;EACZ,gBAAgB;GACd;GACA;GACA;GACA;EACF;EAEA,OAAO;EAEP,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EAMA,WACE;EACF,UAAU;EACV,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,MAAM;EACN,SAAS;EACT,YAAY;EACZ,aACE;EACF,MAAM;GACJ;GAGA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,KAAK;GAEH;GACA;GACA;GAIA;GACA;GACA;GACA,GAAG;GAGH;EACF;EACA,OAAO;GACL;GAIA;GACA;GACA;GAIA;GACA;GACA;GACA;EACF;EACA,MAAM;GAEJ;GACA;GACA;GACA;GACA;GACA;GACA,GAAG;EACL;EACA,QAAQ,CACN,wDACA,yFACF;EACA,cACE;EACF,OAAO;CACT;CACA,UAAU;EACR,OAAO;GACL,OAAO;IAAE,QAAQ;IAAiB,UAAU;GAAsB;GAGlE,MAAM;IACJ,QAAQ;IACR,UAAU;GACZ;EACF;;;;;EAKA,QAAQ;GACN,MAAM;IACJ,MAAM;IACN,KAAK;KACH;KACA;KACA;IACF;GACF;GACA,OAAO,EACL,KAAK;IACH;IACA;IACA;IACA;GACF,EACF;EACF;CACF;CACA,iBAAiB;EAAE,OAAO;EAAO,QAAQ;CAAM;AACjD,CAAC;;AAYD,MAAa,yBAAyB,GAAG,EACvC,OAAO;CACL,OAAO;EACL;EACA;EAEA;CACF;CACA,UAAU;CACV,QAAQ;CACR,OAAO;CACP,YAAY;CACZ,UAAU;EACR;EACA;EACA;EACA;CACF;CACA,MAAM;CACN,MAAM,CACJ,sEACA,mDACF;CACA,MAAM;EACJ;EACA;EACA;EACA;CACF;CACA,OAAO;CACP,SAAS;AACX,EACF,CAAC"}
|
|
@@ -57,7 +57,7 @@ const dialogStyles = tv({
|
|
|
57
57
|
headerButton: "",
|
|
58
58
|
close: "[--md-comp-icon-button-icon-color:var(--md-sys-color-on-surface)]",
|
|
59
59
|
content: [
|
|
60
|
-
"min-h-0 flex-1 overflow-y-auto pb-[24px] text-body-medium",
|
|
60
|
+
"-mt-[8px] min-h-0 flex-1 overflow-y-auto pt-[8px] pb-[24px] text-body-medium",
|
|
61
61
|
"text-[color:var(--_md-comp-dialog-supporting-text-color)]",
|
|
62
62
|
"focus-ring"
|
|
63
63
|
],
|
|
@@ -138,7 +138,7 @@ const dialogStyles = tv({
|
|
|
138
138
|
panel: ["size-full max-h-none max-w-none rounded-none p-0 shadow-none", "bg-surface-container-high"],
|
|
139
139
|
header: "flex h-[56px] shrink-0 items-center gap-[4px] ps-[4px] pe-[12px]",
|
|
140
140
|
title: "min-w-0 flex-1 truncate ps-[4px] pb-0 text-start text-title-large",
|
|
141
|
-
content: "px-[24px] pt-[24px]",
|
|
141
|
+
content: "mt-0 px-[24px] pt-[24px]",
|
|
142
142
|
actions: "hidden"
|
|
143
143
|
},
|
|
144
144
|
compact: {
|
|
@@ -177,7 +177,7 @@ const dialogStyles = tv({
|
|
|
177
177
|
header: ["max-medium:flex max-medium:h-[56px] max-medium:shrink-0 max-medium:items-center", "max-medium:gap-[4px] max-medium:ps-[4px] max-medium:pe-[12px]"],
|
|
178
178
|
headerButton: "medium:hidden",
|
|
179
179
|
title: ["max-medium:min-w-0 max-medium:flex-1 max-medium:truncate max-medium:ps-[4px]", "max-medium:pb-0 max-medium:text-start max-medium:text-title-large"],
|
|
180
|
-
content: "max-medium:px-[24px] max-medium:pt-[24px]",
|
|
180
|
+
content: "max-medium:mt-0 max-medium:px-[24px] max-medium:pt-[24px]",
|
|
181
181
|
actions: "max-medium:hidden"
|
|
182
182
|
}
|
|
183
183
|
},
|
|
@@ -244,12 +244,12 @@ const dialogStyles = tv({
|
|
|
244
244
|
{
|
|
245
245
|
fullScreen: "never",
|
|
246
246
|
dividers: true,
|
|
247
|
-
class: { content: "-mx-[24px] mb-[24px] border-y border-outline-variant px-[24px] py-[8px]" }
|
|
247
|
+
class: { content: "mt-0 -mx-[24px] mb-[24px] border-y border-outline-variant px-[24px] py-[8px]" }
|
|
248
248
|
},
|
|
249
249
|
{
|
|
250
250
|
fullScreen: "compact",
|
|
251
251
|
dividers: true,
|
|
252
|
-
class: { content: ["medium:-mx-[24px] medium:mb-[24px] medium:border-y medium:border-outline-variant", "medium:px-[24px] medium:py-[8px]"] }
|
|
252
|
+
class: { content: ["medium:mt-0 medium:-mx-[24px] medium:mb-[24px] medium:border-y medium:border-outline-variant", "medium:px-[24px] medium:py-[8px]"] }
|
|
253
253
|
},
|
|
254
254
|
{
|
|
255
255
|
fullScreen: "never",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog-styles.js","names":[],"sources":["../../../src/components/dialog/dialog-styles.ts"],"sourcesContent":["import { tv, type VariantProps } from '../../utils/tv';\n\n/*\n * Values follow Compose Material 3 (AlertDialog.kt and DialogTokens): 280–560px wide (the\n * minimum capped by the window's 24px margins), 24px padding, 28px corners,\n * surface-container-high at elevation 3, a 24px secondary icon and the title 16px apart, the\n * title centred when there is an icon, 24px below the supporting text, 8px between actions,\n * and stacked actions with the confirming action on top. Optional dividers above and below\n * the content are 1px `outline-variant`. The scrim is `scrim` at 32% (M3).\n *\n * Motion (2026-10-14): Compose has no dialog motion of its own (the platform animates\n * windows), so the basic dialog follows the M3 dialog enter and exit as Material Web encodes\n * it, mapped onto the M3 Expressive springs (spatial for movement and size, effects for\n * opacity). Entering, the dialog slides 50px away from the window edge it sits against\n * (downward when centred), its container grows from that edge from 0 to its full size (a\n * clip on a library-owned wrapper, so text is never shown outside the container and the\n * spec's 0 start is kept), and it fades in on the fast effects spring while the scrim fades\n * in on the slow one; the headline, icon and supporting text fade in after 50ms and the\n * actions after 150ms. Leaving follows the reference's close timing exactly, on M3's\n * emphasized-accelerate easing and duration tokens: the parts fade out over 100ms, the\n * dialog slides back 50px and its container shrinks to 35% over 150ms (slow at first, so\n * it is still moving and shrinking until it is gone), the container fades out from 100ms to\n * 150ms, and the scrim fades out over 150ms. Reduced motion keeps only the\n * fades of the dialog and the scrim; `instant` turns all of it off.\n * The positioner keeps 24px from the window's edges on compact windows and the spec's 56px\n * on wider ones, and `placement` aligns the dialog in it.\n *\n * Full-screen dialogs follow the M3 dialog spec's full-screen type: the panel fills the\n * window on `surface-container-high` with no corners, elevation or padding; a 56px header\n * holds the `on-surface` close icon button, the `title-large` headline and the confirming\n * text button (with an optional `outline-variant` divider), and the content is padded 24px\n * at the top and sides. With a header action `DialogActions` are hidden; without one they\n * form a 56px bottom action bar. The panel slides up 48px and fades in on the slow springs\n * (the spec's speed for full-screen animations), or, with an `origin`, grows out of it (a\n * container transform). `fullScreen: 'compact'` applies all of this only below 600px (the M3\n * compact window class) and is a basic dialog otherwise.\n *\n * The basic dialog's container colour, corners and shadow, and the icon, title and supporting\n * text colours, come from the dialog's component tokens (`dialog-tokens.ts`,\n * `--md-comp-dialog-*`); the panel carries `data-tokens=\"md-comp-dialog\"`, and the parts read\n * one private variable each. A full-screen panel keeps its own `surface-container-high`. A\n * consumer class (`bg-surface-container`) still wins. Every class is written out in full.\n */\n\n/** The dialog root's placement, inline so it wins over consumer layout classes. */\nexport const DIALOG_ROOT_STYLE = { position: 'fixed', inset: 0 } as const;\n\n/** Variant definitions for {@link Dialog} and its parts. */\nexport const dialogStyles = tv({\n slots: {\n scrim: [\n 'fixed inset-0 z-(--md-sys-z-scrim) bg-scrim/32 opacity-100',\n '[transition-property:opacity]',\n '[transition-duration:var(--md-sys-motion-spring-effects-slow-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-slow-easing)]',\n 'starting:opacity-0',\n 'data-exiting:opacity-0',\n 'data-exiting:[transition-duration:var(--md-sys-motion-duration-short-3)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-easing-linear)]',\n ],\n // The dialog's outermost element, which takes the consumer's `className` and `style`. It\n // covers the window through an inline `position: fixed; inset: 0` (`DIALOG_ROOT_STYLE`),\n // which a consumer class can't move: a layout class such as `static` or `absolute top-24`\n // never puts the dialog in the page's flow or out of the window.\n root: 'z-(--md-sys-z-dialog)',\n positioner: 'fixed inset-0 z-(--md-sys-z-dialog) flex p-[24px] medium:p-[56px]',\n /*\n * The library-owned wrapper that moves: it slides 50px (`--m3-dialog-slide`), its clip\n * grows the container (`--m3-dialog-enter-clip` → `--m3-dialog-open-clip`, which rests\n * 32px outside the panel so the shadow shows, its corners concentric with the panel's\n * `--m3-dialog-corner`) and it fades. Leaving, the clip shrinks to 35%\n * (`--m3-dialog-exit-clip`) and the fade waits 100ms for the parts to fade first. The\n * exit uses the reference's curves rather than springs (2026-10-14): the slide and shrink\n * run 150ms on emphasized-accelerate, which never overshoots and keeps moving until the\n * fade (50ms, linear, after 100ms) has ended. A spring front-loads the motion: the\n * expressive spatial one overshot past 50px and below 35%, and the critically damped\n * effects one finished shrinking before the fade, leaving a still 35% box fading. The\n * clip's sides open from the panel's edges to the shadow's 32px together with its corner\n * radius, so the growing edge keeps the panel's corners on every frame.\n */\n motion: [\n 'flex max-h-full max-w-full',\n '[--m3-dialog-corner:var(--md-comp-dialog-container-shape,var(--md-sys-shape-corner-extra-large))]',\n '[--m3-dialog-open-clip:inset(-32px_round_calc(var(--m3-dialog-corner)_+_32px))]',\n '[--m3-dialog-enter-clip:inset(0_0_100%_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-exit-clip:inset(0_0_65%_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-slide:0_-50px] [--m3-dialog-scale-origin:center]',\n 'translate-none opacity-100 [clip-path:var(--m3-dialog-open-clip)]',\n 'starting:[translate:var(--m3-dialog-slide)] starting:opacity-0',\n 'starting:[clip-path:var(--m3-dialog-enter-clip)]',\n 'data-exiting:[translate:var(--m3-dialog-slide)] data-exiting:opacity-0',\n 'data-exiting:[clip-path:var(--m3-dialog-exit-clip)]',\n 'motion-reduce:data-exiting:translate-none',\n 'motion-reduce:data-exiting:[clip-path:var(--m3-dialog-open-clip)]',\n '[transition-property:opacity,translate,clip-path] motion-reduce:[transition-property:opacity]',\n '[transition-duration:var(--md-sys-motion-spring-effects-fast-duration),var(--md-sys-motion-spring-spatial-default-duration),var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-spatial-default-easing),var(--md-sys-motion-spring-spatial-default-easing)]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-duration-short-1),var(--md-sys-motion-duration-short-3),var(--md-sys-motion-duration-short-3)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-easing-linear),var(--md-sys-motion-easing-emphasized-accelerate),var(--md-sys-motion-easing-emphasized-accelerate)]',\n 'data-exiting:[transition-delay:var(--md-sys-motion-duration-short-2),0s,0s]',\n 'motion-reduce:data-exiting:[transition-delay:0s]',\n 'motion-reduce:data-exiting:[transition-duration:var(--md-sys-motion-duration-short-3)]',\n ],\n /*\n * Classes for the panel that stagger its parts (its children): every part fades in after\n * 50ms on the default effects spring (the headline, icon and supporting text, and any\n * custom child), and the actions (`data-slot=\"actions\"`) after 150ms on the fast one.\n * Leaving, they all fade out at once on the fast spring. Basic dialogs only (see the\n * `fullScreen` variants); reduced motion leaves the parts to the wrapper's fade.\n */\n parts: '',\n panel: [\n 'flex max-h-full min-w-[min(280px,calc(100vw-48px))] max-w-[560px] flex-col p-[24px]',\n 'rounded-[var(--_md-comp-dialog-container-shape)] bg-[var(--_md-comp-dialog-container-color)]',\n 'text-on-surface shadow-[shadow:var(--_md-comp-dialog-container-elevation)]',\n 'outline-none',\n // Forced colours drop the fill and shadow, so the panel draws its edge instead.\n 'forced-colors:outline-solid forced-colors:outline-1 forced-colors:outline-[CanvasText]',\n ],\n icon: [\n 'mx-auto inline-flex pb-[16px] [&>svg]:size-[24px]',\n 'text-[color:var(--_md-comp-dialog-with-icon-icon-color)]',\n ],\n title: [\n 'pb-[16px] text-headline-small font-brand',\n 'text-[color:var(--_md-comp-dialog-headline-color)]',\n ],\n header: '',\n headerButton: '',\n close: '[--md-comp-icon-button-icon-color:var(--md-sys-color-on-surface)]',\n content: [\n 'min-h-0 flex-1 overflow-y-auto pb-[24px] text-body-medium',\n 'text-[color:var(--_md-comp-dialog-supporting-text-color)]',\n 'focus-ring',\n ],\n // Stacked (`data-stacked`, set by `DialogActions` when they don't fit on one line), the\n // buttons are rendered confirming action first and wrap forwards instead of in reverse:\n // the same picture, sizes included, in focus order. The custom property tells it\n // whether the layout lets them stack.\n actions: [\n 'flex flex-wrap-reverse items-center justify-end gap-[8px] [--m3-dialog-actions-stack:1]',\n 'data-stacked:flex-wrap',\n ],\n },\n variants: {\n hasIcon: {\n true: { title: 'text-center' },\n false: { title: 'text-start' },\n },\n /*\n * The dialog enters from the window edge it sits against (the spec's enter and exit\n * pattern): it slides 50px away from that edge while its container grows away from it,\n * and leaves the same way back. Centred or at the top it slides down and grows downward\n * (the reference motion), at the bottom it slides up and grows upward, and at the start\n * or end it moves and grows along the x axis, mirrored in right-to-left layouts.\n */\n placement: {\n center: { positioner: 'items-center justify-center' },\n top: {\n positioner: 'items-start justify-center',\n motion: '[--m3-dialog-scale-origin:top]',\n },\n bottom: {\n positioner: 'items-end justify-center',\n motion: [\n '[--m3-dialog-enter-clip:inset(100%_0_0_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-exit-clip:inset(65%_0_0_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-slide:0_50px] [--m3-dialog-scale-origin:bottom]',\n ],\n },\n start: {\n positioner: 'items-center justify-start',\n motion: [\n '[--m3-dialog-enter-clip:inset(0_100%_0_0_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-exit-clip:inset(0_65%_0_0_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-slide:-50px_0] [--m3-dialog-scale-origin:left]',\n 'rtl:[--m3-dialog-enter-clip:inset(0_0_0_100%_round_var(--m3-dialog-corner))]',\n 'rtl:[--m3-dialog-exit-clip:inset(0_0_0_65%_round_var(--m3-dialog-corner))]',\n 'rtl:[--m3-dialog-slide:50px_0] rtl:[--m3-dialog-scale-origin:right]',\n ],\n },\n end: {\n positioner: 'items-center justify-end',\n motion: [\n '[--m3-dialog-enter-clip:inset(0_0_0_100%_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-exit-clip:inset(0_0_0_65%_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-slide:50px_0] [--m3-dialog-scale-origin:right]',\n 'rtl:[--m3-dialog-enter-clip:inset(0_100%_0_0_round_var(--m3-dialog-corner))]',\n 'rtl:[--m3-dialog-exit-clip:inset(0_65%_0_0_round_var(--m3-dialog-corner))]',\n 'rtl:[--m3-dialog-slide:-50px_0] rtl:[--m3-dialog-scale-origin:left]',\n ],\n },\n },\n fullScreen: {\n never: {\n headerButton: 'hidden',\n parts: [\n 'motion-safe:*:starting:opacity-0 motion-safe:data-exiting:*:opacity-0',\n '*:[transition-property:opacity]',\n '*:[transition-duration:var(--md-sys-motion-spring-effects-default-duration)]',\n '*:[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n '*:[transition-delay:var(--md-sys-motion-duration-short-1)]',\n '*:data-[slot=actions]:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n '*:data-[slot=actions]:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n '*:data-[slot=actions]:[transition-delay:var(--md-sys-motion-duration-short-3)]',\n 'data-exiting:*:[transition-duration:var(--md-sys-motion-duration-short-2)]',\n 'data-exiting:*:[transition-timing-function:var(--md-sys-motion-easing-linear)]',\n 'data-exiting:*:[transition-delay:0s] data-exiting:*:data-[slot=actions]:[transition-delay:0s]',\n 'data-exiting:*:data-[slot=actions]:[transition-duration:var(--md-sys-motion-duration-short-2)]',\n 'data-exiting:*:data-[slot=actions]:[transition-timing-function:var(--md-sys-motion-easing-linear)]',\n ],\n },\n /*\n * Full screen keeps its own motion: the dialog slides up 48px as it fades in on the slow\n * springs (the spec's speed for full-screen motion) with no clip, and slides back down\n * as it fades out on the fast ones, its parts fading with it.\n */\n always: {\n positioner: 'p-0 medium:p-0',\n motion: [\n 'size-full [--m3-dialog-slide:0_48px]',\n '[--m3-dialog-open-clip:none] [--m3-dialog-enter-clip:none] [--m3-dialog-exit-clip:none]',\n '[transition-duration:var(--md-sys-motion-spring-effects-slow-duration),var(--md-sys-motion-spring-spatial-slow-duration),var(--md-sys-motion-spring-spatial-slow-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-slow-easing),var(--md-sys-motion-spring-spatial-slow-easing),var(--md-sys-motion-spring-spatial-slow-easing)]',\n 'data-exiting:[transition-delay:0s]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n ],\n panel: [\n 'size-full max-h-none max-w-none rounded-none p-0 shadow-none',\n 'bg-surface-container-high',\n ],\n header: 'flex h-[56px] shrink-0 items-center gap-[4px] ps-[4px] pe-[12px]',\n title: 'min-w-0 flex-1 truncate ps-[4px] pb-0 text-start text-title-large',\n content: 'px-[24px] pt-[24px]',\n actions: 'hidden',\n },\n compact: {\n positioner: 'max-medium:p-0',\n motion: [\n 'max-medium:size-full max-medium:[--m3-dialog-slide:0_48px]',\n 'max-medium:[--m3-dialog-open-clip:none] max-medium:[--m3-dialog-enter-clip:none]',\n 'max-medium:[--m3-dialog-exit-clip:none]',\n 'max-medium:[transition-duration:var(--md-sys-motion-spring-effects-slow-duration),var(--md-sys-motion-spring-spatial-slow-duration),var(--md-sys-motion-spring-spatial-slow-duration)]',\n 'max-medium:[transition-timing-function:var(--md-sys-motion-spring-effects-slow-easing),var(--md-sys-motion-spring-spatial-slow-easing),var(--md-sys-motion-spring-spatial-slow-easing)]',\n 'max-medium:data-exiting:[transition-delay:0s]',\n 'max-medium:data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'max-medium:data-exiting:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n ],\n parts: [\n 'medium:motion-safe:*:starting:opacity-0 medium:motion-safe:data-exiting:*:opacity-0',\n 'medium:*:[transition-property:opacity]',\n 'medium:*:[transition-duration:var(--md-sys-motion-spring-effects-default-duration)]',\n 'medium:*:[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n 'medium:*:[transition-delay:var(--md-sys-motion-duration-short-1)]',\n 'medium:*:data-[slot=actions]:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'medium:*:data-[slot=actions]:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n 'medium:*:data-[slot=actions]:[transition-delay:var(--md-sys-motion-duration-short-3)]',\n 'medium:data-exiting:*:[transition-duration:var(--md-sys-motion-duration-short-2)]',\n 'medium:data-exiting:*:[transition-timing-function:var(--md-sys-motion-easing-linear)]',\n 'medium:data-exiting:*:[transition-delay:0s]',\n 'medium:data-exiting:*:data-[slot=actions]:[transition-delay:0s]',\n 'medium:data-exiting:*:data-[slot=actions]:[transition-duration:var(--md-sys-motion-duration-short-2)]',\n 'medium:data-exiting:*:data-[slot=actions]:[transition-timing-function:var(--md-sys-motion-easing-linear)]',\n ],\n panel: [\n 'max-medium:size-full max-medium:max-h-none max-medium:max-w-none',\n 'max-medium:rounded-none max-medium:p-0 max-medium:shadow-none',\n 'max-medium:bg-surface-container-high',\n ],\n header: [\n 'max-medium:flex max-medium:h-[56px] max-medium:shrink-0 max-medium:items-center',\n 'max-medium:gap-[4px] max-medium:ps-[4px] max-medium:pe-[12px]',\n ],\n headerButton: 'medium:hidden',\n title: [\n 'max-medium:min-w-0 max-medium:flex-1 max-medium:truncate max-medium:ps-[4px]',\n 'max-medium:pb-0 max-medium:text-start max-medium:text-title-large',\n ],\n content: 'max-medium:px-[24px] max-medium:pt-[24px]',\n actions: 'max-medium:hidden',\n },\n },\n /** Full screen without a header action: the actions form a 56px bottom bar. */\n bottomBar: { true: {}, false: {} },\n /** A divider under the full-screen header. */\n divider: { true: {}, false: {} },\n /** The basic dialog's optional dividers above and below `DialogContent`. */\n dividers: { true: {}, false: {} },\n /** A container transform from a trigger (`origin`) into the full-screen dialog. */\n origin: { true: {}, false: {} },\n /**\n * The basic dialog's enter and exit: `reveal`, the M3 dialog motion above, or `scale`,\n * the original pop (see the compound variants). Full-screen dialogs ignore it.\n */\n transition: { reveal: {}, scale: {} },\n /** No enter or exit motion: the dialog, its parts and the scrim appear and go at once. */\n instant: {\n true: {\n scrim: '[transition-property:none]!',\n motion: '[transition-property:none]!',\n panel: '[transition-property:none]!',\n parts: '*:[transition-property:none]!',\n },\n false: {},\n },\n },\n compoundVariants: [\n {\n fullScreen: 'always',\n bottomBar: true,\n class: {\n actions: 'flex h-[56px] shrink-0 flex-nowrap px-[24px] [--m3-dialog-actions-stack:0]',\n },\n },\n {\n fullScreen: 'compact',\n bottomBar: true,\n class: {\n actions:\n 'max-medium:flex max-medium:h-[56px] max-medium:shrink-0 max-medium:flex-nowrap max-medium:px-[24px] max-medium:[--m3-dialog-actions-stack:0]',\n },\n },\n {\n fullScreen: 'always',\n divider: true,\n class: { header: 'border-b border-outline-variant' },\n },\n {\n fullScreen: 'compact',\n divider: true,\n class: { header: 'max-medium:border-b max-medium:border-outline-variant' },\n },\n /*\n * Dividers run the panel's full width, 1px `outline-variant`, with 8px between them and\n * the content; the body keeps 24px to the actions below the bottom one.\n */\n {\n fullScreen: 'never',\n dividers: true,\n class: {\n content: '-mx-[24px] mb-[24px] border-y border-outline-variant px-[24px] py-[8px]',\n },\n },\n {\n fullScreen: 'compact',\n dividers: true,\n class: {\n content: [\n 'medium:-mx-[24px] medium:mb-[24px] medium:border-y medium:border-outline-variant',\n 'medium:px-[24px] medium:py-[8px]',\n ],\n },\n },\n /*\n * `transition=\"scale\"`, the library's original pop (an extension beyond the M3 dialog\n * motion): the scrim fades on the default effects spring, and the dialog fades in while it\n * scales up from 90% on the default springs, then fades out while it scales to 95% on the\n * fast ones. No clip, no slide, no staggered parts. It scales from the edge it sits\n * against (`--m3-dialog-scale-origin`: the centre for `center`, the top, bottom, start or\n * end edge otherwise). Reduced motion keeps only the fades. Basic dialogs only (from 600px\n * for `compact`).\n */\n {\n fullScreen: 'never',\n transition: 'scale',\n class: {\n scrim: [\n '[transition-duration:var(--md-sys-motion-spring-effects-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n ],\n motion: [\n '[--m3-dialog-slide:0_0] [--m3-dialog-open-clip:none]',\n '[--m3-dialog-enter-clip:none] [--m3-dialog-exit-clip:none]',\n 'origin-(--m3-dialog-scale-origin) scale-none starting:scale-90 data-exiting:scale-95',\n 'motion-reduce:data-exiting:scale-none',\n '[transition-property:opacity,scale] motion-reduce:[transition-property:opacity]',\n '[transition-duration:var(--md-sys-motion-spring-effects-default-duration),var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing),var(--md-sys-motion-spring-spatial-default-easing)]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration),var(--md-sys-motion-spring-spatial-fast-duration)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-spatial-fast-easing)]',\n 'data-exiting:[transition-delay:0s]',\n ],\n },\n },\n {\n fullScreen: 'compact',\n transition: 'scale',\n class: {\n scrim: [\n 'medium:[transition-duration:var(--md-sys-motion-spring-effects-default-duration)]',\n 'medium:[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n 'medium:data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'medium:data-exiting:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n ],\n motion: [\n 'medium:[--m3-dialog-slide:0_0] medium:[--m3-dialog-open-clip:none]',\n 'medium:[--m3-dialog-enter-clip:none] medium:[--m3-dialog-exit-clip:none]',\n 'medium:origin-(--m3-dialog-scale-origin) medium:scale-none',\n 'medium:starting:scale-90 medium:data-exiting:scale-95',\n 'medium:motion-reduce:data-exiting:scale-none',\n 'medium:[transition-property:opacity,scale]',\n 'medium:motion-reduce:[transition-property:opacity]',\n 'medium:[transition-duration:var(--md-sys-motion-spring-effects-default-duration),var(--md-sys-motion-spring-spatial-default-duration)]',\n 'medium:[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing),var(--md-sys-motion-spring-spatial-default-easing)]',\n 'medium:data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration),var(--md-sys-motion-spring-spatial-fast-duration)]',\n 'medium:data-exiting:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-spatial-fast-easing)]',\n 'medium:data-exiting:[transition-delay:0s]',\n ],\n },\n },\n /*\n * Container transform (the spec: \"Use a container transform pattern to transition a FAB\n * into a full-screen dialog\"): the clip starts on the origin's box and corners\n * (`--m3-dialog-origin`, measured by the dialog) and opens to the window on the slow\n * spatial spring, while the container colour moves from the origin's\n * (`--m3-dialog-origin-color`) and the content fades in. Leaving collapses back into the\n * origin on emphasized-accelerate over 250ms (2026-10-15), as the basic exit avoids\n * springs: the fast spatial spring collapsed the clip past the origin to nothing and\n * regrew it while still visible. The container stays opaque for the first 60% and fades\n * over the last 100ms, linear, ending with the collapse (2026-10-17, after Material\n * Components' container transform, whose return fades from 60% to 90%), so it is seen going\n * back into the origin. Reduced motion keeps only the fades (the dialog's and its content's).\n */\n {\n fullScreen: 'always',\n origin: true,\n class: {\n motion: [\n '[--m3-dialog-slide:0_0] [--m3-dialog-open-clip:inset(0_round_0)]',\n '[--m3-dialog-enter-clip:var(--m3-dialog-origin)] [--m3-dialog-exit-clip:var(--m3-dialog-origin)]',\n '[transition-duration:var(--md-sys-motion-spring-effects-fast-duration),var(--md-sys-motion-spring-spatial-slow-duration),var(--md-sys-motion-spring-spatial-slow-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-spatial-slow-easing),var(--md-sys-motion-spring-spatial-slow-easing)]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-duration-short-2),var(--md-sys-motion-duration-medium-1),var(--md-sys-motion-duration-medium-1)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-easing-linear),var(--md-sys-motion-easing-emphasized-accelerate),var(--md-sys-motion-easing-emphasized-accelerate)]',\n // Full screen's exit starts every transition at once; the fade waits here.\n 'data-exiting:[transition-delay:var(--md-sys-motion-duration-short-3),0s,0s]',\n 'motion-reduce:data-exiting:[transition-delay:0s]',\n ],\n panel: [\n '[transition-property:background-color] motion-reduce:[transition-property:none]',\n '[transition-duration:var(--md-sys-motion-spring-effects-slow-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-slow-easing)]',\n 'starting:bg-(--m3-dialog-origin-color) motion-safe:data-exiting:bg-(--m3-dialog-origin-color)',\n 'data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n '*:[transition-property:opacity] *:starting:opacity-0 data-exiting:*:opacity-0',\n '*:[transition-duration:var(--md-sys-motion-spring-effects-slow-duration)]',\n '*:[transition-timing-function:var(--md-sys-motion-spring-effects-slow-easing)]',\n ],\n },\n },\n {\n fullScreen: 'compact',\n origin: true,\n class: {\n motion: [\n 'max-medium:[--m3-dialog-slide:0_0] max-medium:[--m3-dialog-open-clip:inset(0_round_0)]',\n 'max-medium:[--m3-dialog-enter-clip:var(--m3-dialog-origin)]',\n 'max-medium:[--m3-dialog-exit-clip:var(--m3-dialog-origin)]',\n 'max-medium:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration),var(--md-sys-motion-spring-spatial-slow-duration),var(--md-sys-motion-spring-spatial-slow-duration)]',\n 'max-medium:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-spatial-slow-easing),var(--md-sys-motion-spring-spatial-slow-easing)]',\n 'max-medium:data-exiting:[transition-duration:var(--md-sys-motion-duration-short-2),var(--md-sys-motion-duration-medium-1),var(--md-sys-motion-duration-medium-1)]',\n 'max-medium:data-exiting:[transition-timing-function:var(--md-sys-motion-easing-linear),var(--md-sys-motion-easing-emphasized-accelerate),var(--md-sys-motion-easing-emphasized-accelerate)]',\n 'max-medium:data-exiting:[transition-delay:var(--md-sys-motion-duration-short-3),0s,0s]',\n // The breakpoint outranks the base reduced-motion exit: restate it, so the fade\n // keeps its duration and starts at once while the spatial springs are instant.\n 'max-medium:motion-reduce:data-exiting:[transition-duration:var(--md-sys-motion-duration-short-3)]',\n 'max-medium:motion-reduce:data-exiting:[transition-delay:0s]',\n ],\n panel: [\n 'max-medium:[transition-property:background-color]',\n 'max-medium:motion-reduce:[transition-property:none]',\n 'max-medium:[transition-duration:var(--md-sys-motion-spring-effects-slow-duration)]',\n 'max-medium:[transition-timing-function:var(--md-sys-motion-spring-effects-slow-easing)]',\n 'max-medium:starting:bg-(--m3-dialog-origin-color)',\n 'max-medium:motion-safe:data-exiting:bg-(--m3-dialog-origin-color)',\n 'max-medium:data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'max-medium:*:[transition-property:opacity] max-medium:*:starting:opacity-0',\n 'max-medium:data-exiting:*:opacity-0',\n 'max-medium:*:[transition-duration:var(--md-sys-motion-spring-effects-slow-duration)]',\n 'max-medium:*:[transition-timing-function:var(--md-sys-motion-spring-effects-slow-easing)]',\n ],\n },\n },\n ],\n defaultVariants: {\n dividers: false,\n origin: false,\n instant: false,\n transition: 'reveal',\n hasIcon: false,\n fullScreen: 'never',\n placement: 'center',\n bottomBar: false,\n divider: false,\n },\n});\n\n/**\n * The variants of {@link dialogStyles}, 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 DialogStyleProps = VariantProps<typeof dialogStyles>;\n"],"mappings":";;;AA6CA,MAAa,oBAAoB;CAAE,UAAU;CAAS,OAAO;AAAE;;AAG/D,MAAa,eAAe,GAAG;CAC7B,OAAO;EACL,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EAKA,MAAM;EACN,YAAY;EAeZ,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EAQA,OAAO;EACP,OAAO;GACL;GACA;GACA;GACA;GAEA;EACF;EACA,MAAM,CACJ,qDACA,0DACF;EACA,OAAO,CACL,4CACA,oDACF;EACA,QAAQ;EACR,cAAc;EACd,OAAO;EACP,SAAS;GACP;GACA;GACA;EACF;EAKA,SAAS,CACP,2FACA,wBACF;CACF;CACA,UAAU;EACR,SAAS;GACP,MAAM,EAAE,OAAO,cAAc;GAC7B,OAAO,EAAE,OAAO,aAAa;EAC/B;EAQA,WAAW;GACT,QAAQ,EAAE,YAAY,8BAA8B;GACpD,KAAK;IACH,YAAY;IACZ,QAAQ;GACV;GACA,QAAQ;IACN,YAAY;IACZ,QAAQ;KACN;KACA;KACA;IACF;GACF;GACA,OAAO;IACL,YAAY;IACZ,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;IACF;GACF;GACA,KAAK;IACH,YAAY;IACZ,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;IACF;GACF;EACF;EACA,YAAY;GACV,OAAO;IACL,cAAc;IACd,OAAO;KACL;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;GACF;GAMA,QAAQ;IACN,YAAY;IACZ,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;KACA;IACF;IACA,OAAO,CACL,gEACA,2BACF;IACA,QAAQ;IACR,OAAO;IACP,SAAS;IACT,SAAS;GACX;GACA,SAAS;IACP,YAAY;IACZ,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;IACA,OAAO;KACL;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;IACA,OAAO;KACL;KACA;KACA;IACF;IACA,QAAQ,CACN,mFACA,+DACF;IACA,cAAc;IACd,OAAO,CACL,gFACA,mEACF;IACA,SAAS;IACT,SAAS;GACX;EACF;;EAEA,WAAW;GAAE,MAAM,CAAC;GAAG,OAAO,CAAC;EAAE;;EAEjC,SAAS;GAAE,MAAM,CAAC;GAAG,OAAO,CAAC;EAAE;;EAE/B,UAAU;GAAE,MAAM,CAAC;GAAG,OAAO,CAAC;EAAE;;EAEhC,QAAQ;GAAE,MAAM,CAAC;GAAG,OAAO,CAAC;EAAE;;;;;EAK9B,YAAY;GAAE,QAAQ,CAAC;GAAG,OAAO,CAAC;EAAE;;EAEpC,SAAS;GACP,MAAM;IACJ,OAAO;IACP,QAAQ;IACR,OAAO;IACP,OAAO;GACT;GACA,OAAO,CAAC;EACV;CACF;CACA,kBAAkB;EAChB;GACE,YAAY;GACZ,WAAW;GACX,OAAO,EACL,SAAS,6EACX;EACF;EACA;GACE,YAAY;GACZ,WAAW;GACX,OAAO,EACL,SACE,+IACJ;EACF;EACA;GACE,YAAY;GACZ,SAAS;GACT,OAAO,EAAE,QAAQ,kCAAkC;EACrD;EACA;GACE,YAAY;GACZ,SAAS;GACT,OAAO,EAAE,QAAQ,wDAAwD;EAC3E;EAKA;GACE,YAAY;GACZ,UAAU;GACV,OAAO,EACL,SAAS,0EACX;EACF;EACA;GACE,YAAY;GACZ,UAAU;GACV,OAAO,EACL,SAAS,CACP,oFACA,kCACF,EACF;EACF;EAUA;GACE,YAAY;GACZ,YAAY;GACZ,OAAO;IACL,OAAO;KACL;KACA;KACA;KACA;IACF;IACA,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;GACF;EACF;EACA;GACE,YAAY;GACZ,YAAY;GACZ,OAAO;IACL,OAAO;KACL;KACA;KACA;KACA;IACF;IACA,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;GACF;EACF;EAcA;GACE,YAAY;GACZ,QAAQ;GACR,OAAO;IACL,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;KAEA;KACA;IACF;IACA,OAAO;KACL;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;GACF;EACF;EACA;GACE,YAAY;GACZ,QAAQ;GACR,OAAO;IACL,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KAGA;KACA;IACF;IACA,OAAO;KACL;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;GACF;EACF;CACF;CACA,iBAAiB;EACf,UAAU;EACV,QAAQ;EACR,SAAS;EACT,YAAY;EACZ,SAAS;EACT,YAAY;EACZ,WAAW;EACX,WAAW;EACX,SAAS;CACX;AACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"dialog-styles.js","names":[],"sources":["../../../src/components/dialog/dialog-styles.ts"],"sourcesContent":["import { tv, type VariantProps } from '../../utils/tv';\n\n/*\n * Values follow Compose Material 3 (AlertDialog.kt and DialogTokens): 280–560px wide (the\n * minimum capped by the window's 24px margins), 24px padding, 28px corners,\n * surface-container-high at elevation 3, a 24px secondary icon and the title 16px apart, the\n * title centred when there is an icon, 24px below the supporting text, 8px between actions,\n * and stacked actions with the confirming action on top. Optional dividers above and below\n * the content are 1px `outline-variant`. The scrim is `scrim` at 32% (M3).\n *\n * Motion (2026-10-14): Compose has no dialog motion of its own (the platform animates\n * windows), so the basic dialog follows the M3 dialog enter and exit as Material Web encodes\n * it, mapped onto the M3 Expressive springs (spatial for movement and size, effects for\n * opacity). Entering, the dialog slides 50px away from the window edge it sits against\n * (downward when centred), its container grows from that edge from 0 to its full size (a\n * clip on a library-owned wrapper, so text is never shown outside the container and the\n * spec's 0 start is kept), and it fades in on the fast effects spring while the scrim fades\n * in on the slow one; the headline, icon and supporting text fade in after 50ms and the\n * actions after 150ms. Leaving follows the reference's close timing exactly, on M3's\n * emphasized-accelerate easing and duration tokens: the parts fade out over 100ms, the\n * dialog slides back 50px and its container shrinks to 35% over 150ms (slow at first, so\n * it is still moving and shrinking until it is gone), the container fades out from 100ms to\n * 150ms, and the scrim fades out over 150ms. Reduced motion keeps only the\n * fades of the dialog and the scrim; `instant` turns all of it off.\n * The positioner keeps 24px from the window's edges on compact windows and the spec's 56px\n * on wider ones, and `placement` aligns the dialog in it.\n *\n * Full-screen dialogs follow the M3 dialog spec's full-screen type: the panel fills the\n * window on `surface-container-high` with no corners, elevation or padding; a 56px header\n * holds the `on-surface` close icon button, the `title-large` headline and the confirming\n * text button (with an optional `outline-variant` divider), and the content is padded 24px\n * at the top and sides. With a header action `DialogActions` are hidden; without one they\n * form a 56px bottom action bar. The panel slides up 48px and fades in on the slow springs\n * (the spec's speed for full-screen animations), or, with an `origin`, grows out of it (a\n * container transform). `fullScreen: 'compact'` applies all of this only below 600px (the M3\n * compact window class) and is a basic dialog otherwise.\n *\n * The basic dialog's container colour, corners and shadow, and the icon, title and supporting\n * text colours, come from the dialog's component tokens (`dialog-tokens.ts`,\n * `--md-comp-dialog-*`); the panel carries `data-tokens=\"md-comp-dialog\"`, and the parts read\n * one private variable each. A full-screen panel keeps its own `surface-container-high`. A\n * consumer class (`bg-surface-container`) still wins. Every class is written out in full.\n */\n\n/** The dialog root's placement, inline so it wins over consumer layout classes. */\nexport const DIALOG_ROOT_STYLE = { position: 'fixed', inset: 0 } as const;\n\n/** Variant definitions for {@link Dialog} and its parts. */\nexport const dialogStyles = tv({\n slots: {\n scrim: [\n 'fixed inset-0 z-(--md-sys-z-scrim) bg-scrim/32 opacity-100',\n '[transition-property:opacity]',\n '[transition-duration:var(--md-sys-motion-spring-effects-slow-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-slow-easing)]',\n 'starting:opacity-0',\n 'data-exiting:opacity-0',\n 'data-exiting:[transition-duration:var(--md-sys-motion-duration-short-3)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-easing-linear)]',\n ],\n // The dialog's outermost element, which takes the consumer's `className` and `style`. It\n // covers the window through an inline `position: fixed; inset: 0` (`DIALOG_ROOT_STYLE`),\n // which a consumer class can't move: a layout class such as `static` or `absolute top-24`\n // never puts the dialog in the page's flow or out of the window.\n root: 'z-(--md-sys-z-dialog)',\n positioner: 'fixed inset-0 z-(--md-sys-z-dialog) flex p-[24px] medium:p-[56px]',\n /*\n * The library-owned wrapper that moves: it slides 50px (`--m3-dialog-slide`), its clip\n * grows the container (`--m3-dialog-enter-clip` → `--m3-dialog-open-clip`, which rests\n * 32px outside the panel so the shadow shows, its corners concentric with the panel's\n * `--m3-dialog-corner`) and it fades. Leaving, the clip shrinks to 35%\n * (`--m3-dialog-exit-clip`) and the fade waits 100ms for the parts to fade first. The\n * exit uses the reference's curves rather than springs (2026-10-14): the slide and shrink\n * run 150ms on emphasized-accelerate, which never overshoots and keeps moving until the\n * fade (50ms, linear, after 100ms) has ended. A spring front-loads the motion: the\n * expressive spatial one overshot past 50px and below 35%, and the critically damped\n * effects one finished shrinking before the fade, leaving a still 35% box fading. The\n * clip's sides open from the panel's edges to the shadow's 32px together with its corner\n * radius, so the growing edge keeps the panel's corners on every frame.\n */\n motion: [\n 'flex max-h-full max-w-full',\n '[--m3-dialog-corner:var(--md-comp-dialog-container-shape,var(--md-sys-shape-corner-extra-large))]',\n '[--m3-dialog-open-clip:inset(-32px_round_calc(var(--m3-dialog-corner)_+_32px))]',\n '[--m3-dialog-enter-clip:inset(0_0_100%_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-exit-clip:inset(0_0_65%_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-slide:0_-50px] [--m3-dialog-scale-origin:center]',\n 'translate-none opacity-100 [clip-path:var(--m3-dialog-open-clip)]',\n 'starting:[translate:var(--m3-dialog-slide)] starting:opacity-0',\n 'starting:[clip-path:var(--m3-dialog-enter-clip)]',\n 'data-exiting:[translate:var(--m3-dialog-slide)] data-exiting:opacity-0',\n 'data-exiting:[clip-path:var(--m3-dialog-exit-clip)]',\n 'motion-reduce:data-exiting:translate-none',\n 'motion-reduce:data-exiting:[clip-path:var(--m3-dialog-open-clip)]',\n '[transition-property:opacity,translate,clip-path] motion-reduce:[transition-property:opacity]',\n '[transition-duration:var(--md-sys-motion-spring-effects-fast-duration),var(--md-sys-motion-spring-spatial-default-duration),var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-spatial-default-easing),var(--md-sys-motion-spring-spatial-default-easing)]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-duration-short-1),var(--md-sys-motion-duration-short-3),var(--md-sys-motion-duration-short-3)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-easing-linear),var(--md-sys-motion-easing-emphasized-accelerate),var(--md-sys-motion-easing-emphasized-accelerate)]',\n 'data-exiting:[transition-delay:var(--md-sys-motion-duration-short-2),0s,0s]',\n 'motion-reduce:data-exiting:[transition-delay:0s]',\n 'motion-reduce:data-exiting:[transition-duration:var(--md-sys-motion-duration-short-3)]',\n ],\n /*\n * Classes for the panel that stagger its parts (its children): every part fades in after\n * 50ms on the default effects spring (the headline, icon and supporting text, and any\n * custom child), and the actions (`data-slot=\"actions\"`) after 150ms on the fast one.\n * Leaving, they all fade out at once on the fast spring. Basic dialogs only (see the\n * `fullScreen` variants); reduced motion leaves the parts to the wrapper's fade.\n */\n parts: '',\n panel: [\n 'flex max-h-full min-w-[min(280px,calc(100vw-48px))] max-w-[560px] flex-col p-[24px]',\n 'rounded-[var(--_md-comp-dialog-container-shape)] bg-[var(--_md-comp-dialog-container-color)]',\n 'text-on-surface shadow-[shadow:var(--_md-comp-dialog-container-elevation)]',\n 'outline-none',\n // Forced colours drop the fill and shadow, so the panel draws its edge instead.\n 'forced-colors:outline-solid forced-colors:outline-1 forced-colors:outline-[CanvasText]',\n ],\n icon: [\n 'mx-auto inline-flex pb-[16px] [&>svg]:size-[24px]',\n 'text-[color:var(--_md-comp-dialog-with-icon-icon-color)]',\n ],\n title: [\n 'pb-[16px] text-headline-small font-brand',\n 'text-[color:var(--_md-comp-dialog-headline-color)]',\n ],\n header: '',\n headerButton: '',\n close: '[--md-comp-icon-button-icon-color:var(--md-sys-color-on-surface)]',\n content: [\n // 8px of room above the first field, for an outlined field's label, which sits across\n // its top edge and would be clipped by this scroller. The margin takes the same 8px off\n // the title's spacing, so nothing moves.\n '-mt-[8px] min-h-0 flex-1 overflow-y-auto pt-[8px] pb-[24px] text-body-medium',\n 'text-[color:var(--_md-comp-dialog-supporting-text-color)]',\n 'focus-ring',\n ],\n // Stacked (`data-stacked`, set by `DialogActions` when they don't fit on one line), the\n // buttons are rendered confirming action first and wrap forwards instead of in reverse:\n // the same picture, sizes included, in focus order. The custom property tells it\n // whether the layout lets them stack.\n actions: [\n 'flex flex-wrap-reverse items-center justify-end gap-[8px] [--m3-dialog-actions-stack:1]',\n 'data-stacked:flex-wrap',\n ],\n },\n variants: {\n hasIcon: {\n true: { title: 'text-center' },\n false: { title: 'text-start' },\n },\n /*\n * The dialog enters from the window edge it sits against (the spec's enter and exit\n * pattern): it slides 50px away from that edge while its container grows away from it,\n * and leaves the same way back. Centred or at the top it slides down and grows downward\n * (the reference motion), at the bottom it slides up and grows upward, and at the start\n * or end it moves and grows along the x axis, mirrored in right-to-left layouts.\n */\n placement: {\n center: { positioner: 'items-center justify-center' },\n top: {\n positioner: 'items-start justify-center',\n motion: '[--m3-dialog-scale-origin:top]',\n },\n bottom: {\n positioner: 'items-end justify-center',\n motion: [\n '[--m3-dialog-enter-clip:inset(100%_0_0_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-exit-clip:inset(65%_0_0_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-slide:0_50px] [--m3-dialog-scale-origin:bottom]',\n ],\n },\n start: {\n positioner: 'items-center justify-start',\n motion: [\n '[--m3-dialog-enter-clip:inset(0_100%_0_0_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-exit-clip:inset(0_65%_0_0_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-slide:-50px_0] [--m3-dialog-scale-origin:left]',\n 'rtl:[--m3-dialog-enter-clip:inset(0_0_0_100%_round_var(--m3-dialog-corner))]',\n 'rtl:[--m3-dialog-exit-clip:inset(0_0_0_65%_round_var(--m3-dialog-corner))]',\n 'rtl:[--m3-dialog-slide:50px_0] rtl:[--m3-dialog-scale-origin:right]',\n ],\n },\n end: {\n positioner: 'items-center justify-end',\n motion: [\n '[--m3-dialog-enter-clip:inset(0_0_0_100%_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-exit-clip:inset(0_0_0_65%_round_var(--m3-dialog-corner))]',\n '[--m3-dialog-slide:50px_0] [--m3-dialog-scale-origin:right]',\n 'rtl:[--m3-dialog-enter-clip:inset(0_100%_0_0_round_var(--m3-dialog-corner))]',\n 'rtl:[--m3-dialog-exit-clip:inset(0_65%_0_0_round_var(--m3-dialog-corner))]',\n 'rtl:[--m3-dialog-slide:-50px_0] rtl:[--m3-dialog-scale-origin:left]',\n ],\n },\n },\n fullScreen: {\n never: {\n headerButton: 'hidden',\n parts: [\n 'motion-safe:*:starting:opacity-0 motion-safe:data-exiting:*:opacity-0',\n '*:[transition-property:opacity]',\n '*:[transition-duration:var(--md-sys-motion-spring-effects-default-duration)]',\n '*:[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n '*:[transition-delay:var(--md-sys-motion-duration-short-1)]',\n '*:data-[slot=actions]:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n '*:data-[slot=actions]:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n '*:data-[slot=actions]:[transition-delay:var(--md-sys-motion-duration-short-3)]',\n 'data-exiting:*:[transition-duration:var(--md-sys-motion-duration-short-2)]',\n 'data-exiting:*:[transition-timing-function:var(--md-sys-motion-easing-linear)]',\n 'data-exiting:*:[transition-delay:0s] data-exiting:*:data-[slot=actions]:[transition-delay:0s]',\n 'data-exiting:*:data-[slot=actions]:[transition-duration:var(--md-sys-motion-duration-short-2)]',\n 'data-exiting:*:data-[slot=actions]:[transition-timing-function:var(--md-sys-motion-easing-linear)]',\n ],\n },\n /*\n * Full screen keeps its own motion: the dialog slides up 48px as it fades in on the slow\n * springs (the spec's speed for full-screen motion) with no clip, and slides back down\n * as it fades out on the fast ones, its parts fading with it.\n */\n always: {\n positioner: 'p-0 medium:p-0',\n motion: [\n 'size-full [--m3-dialog-slide:0_48px]',\n '[--m3-dialog-open-clip:none] [--m3-dialog-enter-clip:none] [--m3-dialog-exit-clip:none]',\n '[transition-duration:var(--md-sys-motion-spring-effects-slow-duration),var(--md-sys-motion-spring-spatial-slow-duration),var(--md-sys-motion-spring-spatial-slow-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-slow-easing),var(--md-sys-motion-spring-spatial-slow-easing),var(--md-sys-motion-spring-spatial-slow-easing)]',\n 'data-exiting:[transition-delay:0s]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n ],\n panel: [\n 'size-full max-h-none max-w-none rounded-none p-0 shadow-none',\n 'bg-surface-container-high',\n ],\n header: 'flex h-[56px] shrink-0 items-center gap-[4px] ps-[4px] pe-[12px]',\n title: 'min-w-0 flex-1 truncate ps-[4px] pb-0 text-start text-title-large',\n content: 'mt-0 px-[24px] pt-[24px]',\n actions: 'hidden',\n },\n compact: {\n positioner: 'max-medium:p-0',\n motion: [\n 'max-medium:size-full max-medium:[--m3-dialog-slide:0_48px]',\n 'max-medium:[--m3-dialog-open-clip:none] max-medium:[--m3-dialog-enter-clip:none]',\n 'max-medium:[--m3-dialog-exit-clip:none]',\n 'max-medium:[transition-duration:var(--md-sys-motion-spring-effects-slow-duration),var(--md-sys-motion-spring-spatial-slow-duration),var(--md-sys-motion-spring-spatial-slow-duration)]',\n 'max-medium:[transition-timing-function:var(--md-sys-motion-spring-effects-slow-easing),var(--md-sys-motion-spring-spatial-slow-easing),var(--md-sys-motion-spring-spatial-slow-easing)]',\n 'max-medium:data-exiting:[transition-delay:0s]',\n 'max-medium:data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'max-medium:data-exiting:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n ],\n parts: [\n 'medium:motion-safe:*:starting:opacity-0 medium:motion-safe:data-exiting:*:opacity-0',\n 'medium:*:[transition-property:opacity]',\n 'medium:*:[transition-duration:var(--md-sys-motion-spring-effects-default-duration)]',\n 'medium:*:[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n 'medium:*:[transition-delay:var(--md-sys-motion-duration-short-1)]',\n 'medium:*:data-[slot=actions]:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'medium:*:data-[slot=actions]:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n 'medium:*:data-[slot=actions]:[transition-delay:var(--md-sys-motion-duration-short-3)]',\n 'medium:data-exiting:*:[transition-duration:var(--md-sys-motion-duration-short-2)]',\n 'medium:data-exiting:*:[transition-timing-function:var(--md-sys-motion-easing-linear)]',\n 'medium:data-exiting:*:[transition-delay:0s]',\n 'medium:data-exiting:*:data-[slot=actions]:[transition-delay:0s]',\n 'medium:data-exiting:*:data-[slot=actions]:[transition-duration:var(--md-sys-motion-duration-short-2)]',\n 'medium:data-exiting:*:data-[slot=actions]:[transition-timing-function:var(--md-sys-motion-easing-linear)]',\n ],\n panel: [\n 'max-medium:size-full max-medium:max-h-none max-medium:max-w-none',\n 'max-medium:rounded-none max-medium:p-0 max-medium:shadow-none',\n 'max-medium:bg-surface-container-high',\n ],\n header: [\n 'max-medium:flex max-medium:h-[56px] max-medium:shrink-0 max-medium:items-center',\n 'max-medium:gap-[4px] max-medium:ps-[4px] max-medium:pe-[12px]',\n ],\n headerButton: 'medium:hidden',\n title: [\n 'max-medium:min-w-0 max-medium:flex-1 max-medium:truncate max-medium:ps-[4px]',\n 'max-medium:pb-0 max-medium:text-start max-medium:text-title-large',\n ],\n content: 'max-medium:mt-0 max-medium:px-[24px] max-medium:pt-[24px]',\n actions: 'max-medium:hidden',\n },\n },\n /** Full screen without a header action: the actions form a 56px bottom bar. */\n bottomBar: { true: {}, false: {} },\n /** A divider under the full-screen header. */\n divider: { true: {}, false: {} },\n /** The basic dialog's optional dividers above and below `DialogContent`. */\n dividers: { true: {}, false: {} },\n /** A container transform from a trigger (`origin`) into the full-screen dialog. */\n origin: { true: {}, false: {} },\n /**\n * The basic dialog's enter and exit: `reveal`, the M3 dialog motion above, or `scale`,\n * the original pop (see the compound variants). Full-screen dialogs ignore it.\n */\n transition: { reveal: {}, scale: {} },\n /** No enter or exit motion: the dialog, its parts and the scrim appear and go at once. */\n instant: {\n true: {\n scrim: '[transition-property:none]!',\n motion: '[transition-property:none]!',\n panel: '[transition-property:none]!',\n parts: '*:[transition-property:none]!',\n },\n false: {},\n },\n },\n compoundVariants: [\n {\n fullScreen: 'always',\n bottomBar: true,\n class: {\n actions: 'flex h-[56px] shrink-0 flex-nowrap px-[24px] [--m3-dialog-actions-stack:0]',\n },\n },\n {\n fullScreen: 'compact',\n bottomBar: true,\n class: {\n actions:\n 'max-medium:flex max-medium:h-[56px] max-medium:shrink-0 max-medium:flex-nowrap max-medium:px-[24px] max-medium:[--m3-dialog-actions-stack:0]',\n },\n },\n {\n fullScreen: 'always',\n divider: true,\n class: { header: 'border-b border-outline-variant' },\n },\n {\n fullScreen: 'compact',\n divider: true,\n class: { header: 'max-medium:border-b max-medium:border-outline-variant' },\n },\n /*\n * Dividers run the panel's full width, 1px `outline-variant`, with 8px between them and\n * the content; the body keeps 24px to the actions below the bottom one.\n */\n {\n fullScreen: 'never',\n dividers: true,\n class: {\n content: 'mt-0 -mx-[24px] mb-[24px] border-y border-outline-variant px-[24px] py-[8px]',\n },\n },\n {\n fullScreen: 'compact',\n dividers: true,\n class: {\n content: [\n 'medium:mt-0 medium:-mx-[24px] medium:mb-[24px] medium:border-y medium:border-outline-variant',\n 'medium:px-[24px] medium:py-[8px]',\n ],\n },\n },\n /*\n * `transition=\"scale\"`, the library's original pop (an extension beyond the M3 dialog\n * motion): the scrim fades on the default effects spring, and the dialog fades in while it\n * scales up from 90% on the default springs, then fades out while it scales to 95% on the\n * fast ones. No clip, no slide, no staggered parts. It scales from the edge it sits\n * against (`--m3-dialog-scale-origin`: the centre for `center`, the top, bottom, start or\n * end edge otherwise). Reduced motion keeps only the fades. Basic dialogs only (from 600px\n * for `compact`).\n */\n {\n fullScreen: 'never',\n transition: 'scale',\n class: {\n scrim: [\n '[transition-duration:var(--md-sys-motion-spring-effects-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n ],\n motion: [\n '[--m3-dialog-slide:0_0] [--m3-dialog-open-clip:none]',\n '[--m3-dialog-enter-clip:none] [--m3-dialog-exit-clip:none]',\n 'origin-(--m3-dialog-scale-origin) scale-none starting:scale-90 data-exiting:scale-95',\n 'motion-reduce:data-exiting:scale-none',\n '[transition-property:opacity,scale] motion-reduce:[transition-property:opacity]',\n '[transition-duration:var(--md-sys-motion-spring-effects-default-duration),var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing),var(--md-sys-motion-spring-spatial-default-easing)]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration),var(--md-sys-motion-spring-spatial-fast-duration)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-spatial-fast-easing)]',\n 'data-exiting:[transition-delay:0s]',\n ],\n },\n },\n {\n fullScreen: 'compact',\n transition: 'scale',\n class: {\n scrim: [\n 'medium:[transition-duration:var(--md-sys-motion-spring-effects-default-duration)]',\n 'medium:[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n 'medium:data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'medium:data-exiting:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n ],\n motion: [\n 'medium:[--m3-dialog-slide:0_0] medium:[--m3-dialog-open-clip:none]',\n 'medium:[--m3-dialog-enter-clip:none] medium:[--m3-dialog-exit-clip:none]',\n 'medium:origin-(--m3-dialog-scale-origin) medium:scale-none',\n 'medium:starting:scale-90 medium:data-exiting:scale-95',\n 'medium:motion-reduce:data-exiting:scale-none',\n 'medium:[transition-property:opacity,scale]',\n 'medium:motion-reduce:[transition-property:opacity]',\n 'medium:[transition-duration:var(--md-sys-motion-spring-effects-default-duration),var(--md-sys-motion-spring-spatial-default-duration)]',\n 'medium:[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing),var(--md-sys-motion-spring-spatial-default-easing)]',\n 'medium:data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration),var(--md-sys-motion-spring-spatial-fast-duration)]',\n 'medium:data-exiting:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-spatial-fast-easing)]',\n 'medium:data-exiting:[transition-delay:0s]',\n ],\n },\n },\n /*\n * Container transform (the spec: \"Use a container transform pattern to transition a FAB\n * into a full-screen dialog\"): the clip starts on the origin's box and corners\n * (`--m3-dialog-origin`, measured by the dialog) and opens to the window on the slow\n * spatial spring, while the container colour moves from the origin's\n * (`--m3-dialog-origin-color`) and the content fades in. Leaving collapses back into the\n * origin on emphasized-accelerate over 250ms (2026-10-15), as the basic exit avoids\n * springs: the fast spatial spring collapsed the clip past the origin to nothing and\n * regrew it while still visible. The container stays opaque for the first 60% and fades\n * over the last 100ms, linear, ending with the collapse (2026-10-17, after Material\n * Components' container transform, whose return fades from 60% to 90%), so it is seen going\n * back into the origin. Reduced motion keeps only the fades (the dialog's and its content's).\n */\n {\n fullScreen: 'always',\n origin: true,\n class: {\n motion: [\n '[--m3-dialog-slide:0_0] [--m3-dialog-open-clip:inset(0_round_0)]',\n '[--m3-dialog-enter-clip:var(--m3-dialog-origin)] [--m3-dialog-exit-clip:var(--m3-dialog-origin)]',\n '[transition-duration:var(--md-sys-motion-spring-effects-fast-duration),var(--md-sys-motion-spring-spatial-slow-duration),var(--md-sys-motion-spring-spatial-slow-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-spatial-slow-easing),var(--md-sys-motion-spring-spatial-slow-easing)]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-duration-short-2),var(--md-sys-motion-duration-medium-1),var(--md-sys-motion-duration-medium-1)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-easing-linear),var(--md-sys-motion-easing-emphasized-accelerate),var(--md-sys-motion-easing-emphasized-accelerate)]',\n // Full screen's exit starts every transition at once; the fade waits here.\n 'data-exiting:[transition-delay:var(--md-sys-motion-duration-short-3),0s,0s]',\n 'motion-reduce:data-exiting:[transition-delay:0s]',\n ],\n panel: [\n '[transition-property:background-color] motion-reduce:[transition-property:none]',\n '[transition-duration:var(--md-sys-motion-spring-effects-slow-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-slow-easing)]',\n 'starting:bg-(--m3-dialog-origin-color) motion-safe:data-exiting:bg-(--m3-dialog-origin-color)',\n 'data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n '*:[transition-property:opacity] *:starting:opacity-0 data-exiting:*:opacity-0',\n '*:[transition-duration:var(--md-sys-motion-spring-effects-slow-duration)]',\n '*:[transition-timing-function:var(--md-sys-motion-spring-effects-slow-easing)]',\n ],\n },\n },\n {\n fullScreen: 'compact',\n origin: true,\n class: {\n motion: [\n 'max-medium:[--m3-dialog-slide:0_0] max-medium:[--m3-dialog-open-clip:inset(0_round_0)]',\n 'max-medium:[--m3-dialog-enter-clip:var(--m3-dialog-origin)]',\n 'max-medium:[--m3-dialog-exit-clip:var(--m3-dialog-origin)]',\n 'max-medium:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration),var(--md-sys-motion-spring-spatial-slow-duration),var(--md-sys-motion-spring-spatial-slow-duration)]',\n 'max-medium:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-spatial-slow-easing),var(--md-sys-motion-spring-spatial-slow-easing)]',\n 'max-medium:data-exiting:[transition-duration:var(--md-sys-motion-duration-short-2),var(--md-sys-motion-duration-medium-1),var(--md-sys-motion-duration-medium-1)]',\n 'max-medium:data-exiting:[transition-timing-function:var(--md-sys-motion-easing-linear),var(--md-sys-motion-easing-emphasized-accelerate),var(--md-sys-motion-easing-emphasized-accelerate)]',\n 'max-medium:data-exiting:[transition-delay:var(--md-sys-motion-duration-short-3),0s,0s]',\n // The breakpoint outranks the base reduced-motion exit: restate it, so the fade\n // keeps its duration and starts at once while the spatial springs are instant.\n 'max-medium:motion-reduce:data-exiting:[transition-duration:var(--md-sys-motion-duration-short-3)]',\n 'max-medium:motion-reduce:data-exiting:[transition-delay:0s]',\n ],\n panel: [\n 'max-medium:[transition-property:background-color]',\n 'max-medium:motion-reduce:[transition-property:none]',\n 'max-medium:[transition-duration:var(--md-sys-motion-spring-effects-slow-duration)]',\n 'max-medium:[transition-timing-function:var(--md-sys-motion-spring-effects-slow-easing)]',\n 'max-medium:starting:bg-(--m3-dialog-origin-color)',\n 'max-medium:motion-safe:data-exiting:bg-(--m3-dialog-origin-color)',\n 'max-medium:data-exiting:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'max-medium:*:[transition-property:opacity] max-medium:*:starting:opacity-0',\n 'max-medium:data-exiting:*:opacity-0',\n 'max-medium:*:[transition-duration:var(--md-sys-motion-spring-effects-slow-duration)]',\n 'max-medium:*:[transition-timing-function:var(--md-sys-motion-spring-effects-slow-easing)]',\n ],\n },\n },\n ],\n defaultVariants: {\n dividers: false,\n origin: false,\n instant: false,\n transition: 'reveal',\n hasIcon: false,\n fullScreen: 'never',\n placement: 'center',\n bottomBar: false,\n divider: false,\n },\n});\n\n/**\n * The variants of {@link dialogStyles}, 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 DialogStyleProps = VariantProps<typeof dialogStyles>;\n"],"mappings":";;;AA6CA,MAAa,oBAAoB;CAAE,UAAU;CAAS,OAAO;AAAE;;AAG/D,MAAa,eAAe,GAAG;CAC7B,OAAO;EACL,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EAKA,MAAM;EACN,YAAY;EAeZ,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EAQA,OAAO;EACP,OAAO;GACL;GACA;GACA;GACA;GAEA;EACF;EACA,MAAM,CACJ,qDACA,0DACF;EACA,OAAO,CACL,4CACA,oDACF;EACA,QAAQ;EACR,cAAc;EACd,OAAO;EACP,SAAS;GAIP;GACA;GACA;EACF;EAKA,SAAS,CACP,2FACA,wBACF;CACF;CACA,UAAU;EACR,SAAS;GACP,MAAM,EAAE,OAAO,cAAc;GAC7B,OAAO,EAAE,OAAO,aAAa;EAC/B;EAQA,WAAW;GACT,QAAQ,EAAE,YAAY,8BAA8B;GACpD,KAAK;IACH,YAAY;IACZ,QAAQ;GACV;GACA,QAAQ;IACN,YAAY;IACZ,QAAQ;KACN;KACA;KACA;IACF;GACF;GACA,OAAO;IACL,YAAY;IACZ,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;IACF;GACF;GACA,KAAK;IACH,YAAY;IACZ,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;IACF;GACF;EACF;EACA,YAAY;GACV,OAAO;IACL,cAAc;IACd,OAAO;KACL;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;GACF;GAMA,QAAQ;IACN,YAAY;IACZ,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;KACA;IACF;IACA,OAAO,CACL,gEACA,2BACF;IACA,QAAQ;IACR,OAAO;IACP,SAAS;IACT,SAAS;GACX;GACA,SAAS;IACP,YAAY;IACZ,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;IACA,OAAO;KACL;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;IACA,OAAO;KACL;KACA;KACA;IACF;IACA,QAAQ,CACN,mFACA,+DACF;IACA,cAAc;IACd,OAAO,CACL,gFACA,mEACF;IACA,SAAS;IACT,SAAS;GACX;EACF;;EAEA,WAAW;GAAE,MAAM,CAAC;GAAG,OAAO,CAAC;EAAE;;EAEjC,SAAS;GAAE,MAAM,CAAC;GAAG,OAAO,CAAC;EAAE;;EAE/B,UAAU;GAAE,MAAM,CAAC;GAAG,OAAO,CAAC;EAAE;;EAEhC,QAAQ;GAAE,MAAM,CAAC;GAAG,OAAO,CAAC;EAAE;;;;;EAK9B,YAAY;GAAE,QAAQ,CAAC;GAAG,OAAO,CAAC;EAAE;;EAEpC,SAAS;GACP,MAAM;IACJ,OAAO;IACP,QAAQ;IACR,OAAO;IACP,OAAO;GACT;GACA,OAAO,CAAC;EACV;CACF;CACA,kBAAkB;EAChB;GACE,YAAY;GACZ,WAAW;GACX,OAAO,EACL,SAAS,6EACX;EACF;EACA;GACE,YAAY;GACZ,WAAW;GACX,OAAO,EACL,SACE,+IACJ;EACF;EACA;GACE,YAAY;GACZ,SAAS;GACT,OAAO,EAAE,QAAQ,kCAAkC;EACrD;EACA;GACE,YAAY;GACZ,SAAS;GACT,OAAO,EAAE,QAAQ,wDAAwD;EAC3E;EAKA;GACE,YAAY;GACZ,UAAU;GACV,OAAO,EACL,SAAS,+EACX;EACF;EACA;GACE,YAAY;GACZ,UAAU;GACV,OAAO,EACL,SAAS,CACP,gGACA,kCACF,EACF;EACF;EAUA;GACE,YAAY;GACZ,YAAY;GACZ,OAAO;IACL,OAAO;KACL;KACA;KACA;KACA;IACF;IACA,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;GACF;EACF;EACA;GACE,YAAY;GACZ,YAAY;GACZ,OAAO;IACL,OAAO;KACL;KACA;KACA;KACA;IACF;IACA,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;GACF;EACF;EAcA;GACE,YAAY;GACZ,QAAQ;GACR,OAAO;IACL,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;KAEA;KACA;IACF;IACA,OAAO;KACL;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;GACF;EACF;EACA;GACE,YAAY;GACZ,QAAQ;GACR,OAAO;IACL,QAAQ;KACN;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KAGA;KACA;IACF;IACA,OAAO;KACL;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF;GACF;EACF;CACF;CACA,iBAAiB;EACf,UAAU;EACV,QAAQ;EACR,SAAS;EACT,YAAY;EACZ,SAAS;EACT,YAAY;EACZ,WAAW;EACX,WAAW;EACX,SAAS;CACX;AACF,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { RefObject } from "react";
|
|
2
|
+
//#region src/components/menu/use-reveal-entering.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Holds a menu's reveal closed (`data-entering`) until React Aria has placed it, then lets
|
|
5
|
+
* it open, so it grows from the edge nearest its anchor.
|
|
6
|
+
*
|
|
7
|
+
* The reveal's closed clip depends on `data-placement` (from the top below the anchor, from
|
|
8
|
+
* the bottom above it, from the side beside it). A `@starting-style` rule is read the first
|
|
9
|
+
* time the element's style is resolved, which React Aria's measuring forces before it has
|
|
10
|
+
* set the placement, so every menu used to open from its top edge. Instead, the closed state
|
|
11
|
+
* is a class with no transition: once the placement is known (before the first paint, as
|
|
12
|
+
* React Aria places in a layout effect), the closed clip for that edge is resolved and the
|
|
13
|
+
* class removed, and the clip and opacity transition open from there.
|
|
14
|
+
*
|
|
15
|
+
* Should the placement never arrive, the menu opens on the next frame anyway.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useRevealEntering(placement: string | null | undefined, ref: RefObject<HTMLElement | null>): boolean;
|
|
18
|
+
//#endregion
|
|
19
|
+
//# sourceMappingURL=use-reveal-entering.d.ts.map
|
|
@@ -117,7 +117,7 @@ function NavItem({ layout, enterLabel, lastIndicatorRef, icon, selectedIcon, chi
|
|
|
117
117
|
if (stateLayerRef.current && !action.disabled && (event.key === "Enter" || event.key === " ")) setRippleOrigin(stateLayerRef.current);
|
|
118
118
|
},
|
|
119
119
|
"data-nav-item": "",
|
|
120
|
-
"aria-current": selected ? "page" : void 0,
|
|
120
|
+
"aria-current": action["aria-current"] ?? (selected ? "page" : void 0),
|
|
121
121
|
"aria-describedby": [action["aria-describedby"], badge && description != null ? descriptionId : void 0].filter(Boolean).join(" ") || void 0,
|
|
122
122
|
"data-current": selected || void 0,
|
|
123
123
|
"data-tokens": mergeDataTokens(layout.startsWith("rail") ? "md-comp-nav-rail" : "md-comp-nav-bar", action),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NavItem.js","names":[],"sources":["../../../src/components/navigation/NavItem.tsx"],"sourcesContent":["'use client';\n\nimport { animate, useReducedMotion } from 'motion/react';\nimport {\n isValidElement,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type KeyboardEvent,\n type PointerEvent,\n type ReactElement,\n type ReactNode,\n type RefObject,\n} from 'react';\nimport { useM3Spring } from '../../motion/use-m3-spring';\nimport { setRippleOrigin, useRippleHold } from '../../primitives/use-m3-interaction';\nimport {\n ButtonBase,\n type ButtonBaseActionProps,\n type ButtonBaseLinkProps,\n} from '../../primitives/ButtonBase';\nimport { cn } from '../../utils/cn';\nimport { BadgedBox, type BadgeProps } from '../badge/Badge';\nimport { navItemStyles, type NavItemLayout } from './nav-item-styles';\nimport { mergeDataTokens } from '../../utils/merge-data-tokens';\n\nexport interface NavItemClassNames {\n /** The item's button or link, merged with `className`. */\n root?: string;\n /** The pill around the icon that holds the active indicator and the state layer. */\n pill?: string;\n /** The active indicator: the filled pill shown on the current destination. */\n indicator?: string;\n /** The state layer over the pill (hover, focus and press). */\n stateLayer?: string;\n /** The icon. */\n icon?: string;\n /** The label text. */\n label?: string;\n /** The badge's wrapper: on the icon, or after the label in the expanded rail. */\n badge?: string;\n}\n\nexport interface NavItemOwnProps {\n /** The icon. Hidden from assistive tech; the label names the item. */\n icon: ReactElement;\n /** Icon shown while selected, e.g. a filled variant. */\n selectedIcon?: ReactElement;\n /** The label. */\n children: ReactNode;\n /** Marks the current destination (`aria-current=\"page\"`). */\n selected?: boolean;\n /**\n * A `Badge` for dynamic information, such as a count. It overlaps the icon's top end, or\n * sits after the label in the expanded rail. It describes the item to assistive tech\n * with its text, or with `badgeLabel`.\n */\n badge?: ReactElement<BadgeProps>;\n /**\n * What the badge means, read after the item's name, e.g. \"3 new messages\". Defaults to\n * the badge's `label` or text; a small (dot) badge reads its `labels.newNotification`\n * (\"New notification\"), as M3 asks of badges without a count.\n */\n badgeLabel?: string;\n /**\n * Called when the current destination is pressed again. M3: re-selecting the active\n * destination resets the page's scroll position to the top, so scroll to the top here.\n */\n onReselect?: () => void;\n /** Classes for each part, by slot. */\n classNames?: NavItemClassNames;\n}\n\ntype OmitItem<T> = Omit<T, keyof NavItemOwnProps | 'toggle'>;\n\nexport type NavItemProps = NavItemOwnProps &\n (OmitItem<ButtonBaseActionProps> | OmitItem<ButtonBaseLinkProps>);\n\n/**\n * Slides the current item's pill from the previous current item's, on the fast spatial\n * spring, changing width on the way when their labels differ. `lastIndicatorRef` holds the\n * last current pill for every item in one list; without it the pill doesn't slide, and under\n * reduced motion it fades in place.\n */\nfunction useSlidingIndicator(\n selected: boolean,\n lastIndicatorRef: RefObject<HTMLElement | null> | undefined,\n) {\n const indicatorRef = useRef<HTMLSpanElement>(null);\n const wasSelected = useRef(selected);\n const spring = useM3Spring('spatial', 'fast');\n const reducedMotion = useReducedMotion();\n useLayoutEffect(() => {\n const indicator = indicatorRef.current;\n const changed = wasSelected.current !== selected;\n wasSelected.current = selected;\n if (!lastIndicatorRef || !selected || !indicator) return;\n const previous = lastIndicatorRef.current;\n lastIndicatorRef.current = indicator;\n if (!changed || reducedMotion || !previous?.isConnected || previous === indicator) return;\n // Measure both pills at rest; an earlier slide may have left a transform or width behind.\n indicator.style.transform = '';\n indicator.style.width = '';\n const from = previous.getBoundingClientRect();\n const to = indicator.getBoundingClientRect();\n const offset = from.top - to.top;\n if (offset === 0) return;\n // Start from the old place and width now: Motion's first frame comes a frame later.\n indicator.style.transform = `translateY(${offset}px)`;\n if (from.width === to.width) {\n animate(indicator, { y: [offset, 0] }, spring);\n return;\n }\n indicator.style.width = `${from.width}px`;\n // Width animates in px; once settled the pill sizes itself to its content again.\n void animate(indicator, { y: [offset, 0], width: [from.width, to.width] }, spring).then(() => {\n indicator.style.width = '';\n });\n }, [selected, lastIndicatorRef, reducedMotion, spring]);\n return indicatorRef;\n}\n\n/**\n * What a badge tells assistive tech about its item: its `label`, its text, or, for a small\n * badge (no children), \"New notification\" (`labels.newNotification`).\n */\nfunction badgeDescription(badge: ReactElement<BadgeProps> | undefined): string | null {\n // A badge can arrive as a lazy server reference without props yet (React's dev server\n // rendering), so read it only from a real element.\n if (!isValidElement<BadgeProps>(badge)) return null;\n const { label, labels, children } = badge.props;\n if (label != null) return label;\n if (typeof children === 'string' || typeof children === 'number') return String(children);\n if (children == null || children === false) {\n return labels?.newNotification ?? 'New notification';\n }\n return null;\n}\n\n/** Shared navigation item: a link (`href`) or button with the M3 pill indicator. */\nexport function NavItem({\n layout,\n enterLabel,\n lastIndicatorRef,\n icon,\n selectedIcon,\n children,\n selected,\n badge,\n badgeLabel,\n onReselect,\n classNames,\n className,\n ...base\n}: NavItemProps & {\n layout: NavItemLayout;\n enterLabel?: boolean;\n /** The list's last current pill, so this item's pill slides from it when it becomes current. */\n lastIndicatorRef?: RefObject<HTMLElement | null>;\n}) {\n const styles = navItemStyles({ layout, enterLabel, slide: Boolean(lastIndicatorRef) });\n const indicatorRef = useSlidingIndicator(Boolean(selected), lastIndicatorRef);\n const descriptionId = useId();\n const inline = layout === 'rail-start' || layout === 'bar-start';\n // M3: badges sit on the icon, except in the expanded rail, where they follow the label.\n const badgeAfterLabel = layout === 'rail-start';\n const description = badgeLabel ?? badgeDescription(badge);\n const label = (\n <span\n data-nav-label=\"\"\n data-slot=\"label\"\n className={styles.label({ class: classNames?.label })}\n >\n {children}\n </span>\n );\n const iconFaces = selectedIcon ? (\n <>\n <span data-shown={!selected || undefined} className={styles.iconFace()}>\n {icon}\n </span>\n <span data-shown={selected || undefined} className={styles.iconFace()}>\n {selectedIcon}\n </span>\n </>\n ) : (\n icon\n );\n const action = base as ButtonBaseActionProps;\n // M3: a press ripples through the pill. The item is the pressable element, so the pill's\n // state layer gets its own press state and a ripple origin in its own box.\n const stateLayerRef = useRef<HTMLSpanElement>(null);\n const [pressed, setPressed] = useState(false);\n const rippling = useRippleHold(pressed && !action.disabled);\n return (\n <ButtonBase\n {...action}\n onPress={(event) => {\n action.onPress?.(event);\n if (selected) onReselect?.();\n }}\n onPressChange={(isPressed) => {\n action.onPressChange?.(isPressed);\n setPressed(isPressed);\n }}\n onPointerDown={(event: PointerEvent<HTMLButtonElement>) => {\n action.onPointerDown?.(event);\n if (stateLayerRef.current && !action.disabled && event.button === 0) {\n setRippleOrigin(stateLayerRef.current, event.clientX, event.clientY);\n }\n }}\n onKeyDown={(event: KeyboardEvent<HTMLButtonElement>) => {\n action.onKeyDown?.(event);\n if (\n stateLayerRef.current &&\n !action.disabled &&\n (event.key === 'Enter' || event.key === ' ')\n ) {\n setRippleOrigin(stateLayerRef.current);\n }\n }}\n data-nav-item=\"\"\n aria-current={selected ? 'page' : undefined}\n aria-describedby={\n [action['aria-describedby'], badge && description != null ? descriptionId : undefined]\n .filter(Boolean)\n .join(' ') || undefined\n }\n data-current={selected || undefined}\n // The bar's or rail's component tokens, resolved on the item for its state and layout.\n data-tokens={mergeDataTokens(\n layout.startsWith('rail') ? 'md-comp-nav-rail' : 'md-comp-nav-bar',\n action,\n )}\n data-layout={layout}\n className={styles.root({ class: cn(classNames?.root, className) })}\n >\n <span data-slot=\"pill\" className={styles.pill({ class: classNames?.pill })}>\n <span\n ref={indicatorRef}\n aria-hidden=\"true\"\n data-slot=\"indicator\"\n className={styles.indicator({ class: classNames?.indicator })}\n />\n <span\n ref={stateLayerRef}\n aria-hidden=\"true\"\n data-rippling={rippling || undefined}\n data-disabled={action.disabled || undefined}\n className={styles.stateLayer({ class: classNames?.stateLayer })}\n />\n <span className={styles.content()}>\n <span\n aria-hidden=\"true\"\n data-slot=\"icon\"\n className={styles.icon({ class: classNames?.icon })}\n >\n {badge && !badgeAfterLabel ? (\n <BadgedBox badge={badge} className={styles.badgedBox({ class: classNames?.badge })}>\n <span className={styles.iconStack()}>{iconFaces}</span>\n </BadgedBox>\n ) : (\n iconFaces\n )}\n </span>\n {inline && label}\n {badge && badgeAfterLabel && (\n <span aria-hidden=\"true\" className={styles.badge({ class: classNames?.badge })}>\n {badge}\n </span>\n )}\n </span>\n </span>\n {!inline && label}\n {badge && description != null && (\n <span id={descriptionId} hidden>\n {description}\n </span>\n )}\n </ButtonBase>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"NavItem.js","names":[],"sources":["../../../src/components/navigation/NavItem.tsx"],"sourcesContent":["'use client';\n\nimport { animate, useReducedMotion } from 'motion/react';\nimport {\n isValidElement,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type KeyboardEvent,\n type PointerEvent,\n type ReactElement,\n type ReactNode,\n type RefObject,\n} from 'react';\nimport { useM3Spring } from '../../motion/use-m3-spring';\nimport { setRippleOrigin, useRippleHold } from '../../primitives/use-m3-interaction';\nimport {\n ButtonBase,\n type ButtonBaseActionProps,\n type ButtonBaseLinkProps,\n} from '../../primitives/ButtonBase';\nimport { cn } from '../../utils/cn';\nimport { BadgedBox, type BadgeProps } from '../badge/Badge';\nimport { navItemStyles, type NavItemLayout } from './nav-item-styles';\nimport { mergeDataTokens } from '../../utils/merge-data-tokens';\n\nexport interface NavItemClassNames {\n /** The item's button or link, merged with `className`. */\n root?: string;\n /** The pill around the icon that holds the active indicator and the state layer. */\n pill?: string;\n /** The active indicator: the filled pill shown on the current destination. */\n indicator?: string;\n /** The state layer over the pill (hover, focus and press). */\n stateLayer?: string;\n /** The icon. */\n icon?: string;\n /** The label text. */\n label?: string;\n /** The badge's wrapper: on the icon, or after the label in the expanded rail. */\n badge?: string;\n}\n\nexport interface NavItemOwnProps {\n /** The icon. Hidden from assistive tech; the label names the item. */\n icon: ReactElement;\n /** Icon shown while selected, e.g. a filled variant. */\n selectedIcon?: ReactElement;\n /** The label. */\n children: ReactNode;\n /** Marks the current destination (`aria-current=\"page\"`). */\n selected?: boolean;\n /**\n * A `Badge` for dynamic information, such as a count. It overlaps the icon's top end, or\n * sits after the label in the expanded rail. It describes the item to assistive tech\n * with its text, or with `badgeLabel`.\n */\n badge?: ReactElement<BadgeProps>;\n /**\n * What the badge means, read after the item's name, e.g. \"3 new messages\". Defaults to\n * the badge's `label` or text; a small (dot) badge reads its `labels.newNotification`\n * (\"New notification\"), as M3 asks of badges without a count.\n */\n badgeLabel?: string;\n /**\n * Called when the current destination is pressed again. M3: re-selecting the active\n * destination resets the page's scroll position to the top, so scroll to the top here.\n */\n onReselect?: () => void;\n /** Classes for each part, by slot. */\n classNames?: NavItemClassNames;\n}\n\ntype OmitItem<T> = Omit<T, keyof NavItemOwnProps | 'toggle'>;\n\nexport type NavItemProps = NavItemOwnProps &\n (OmitItem<ButtonBaseActionProps> | OmitItem<ButtonBaseLinkProps>);\n\n/**\n * Slides the current item's pill from the previous current item's, on the fast spatial\n * spring, changing width on the way when their labels differ. `lastIndicatorRef` holds the\n * last current pill for every item in one list; without it the pill doesn't slide, and under\n * reduced motion it fades in place.\n */\nfunction useSlidingIndicator(\n selected: boolean,\n lastIndicatorRef: RefObject<HTMLElement | null> | undefined,\n) {\n const indicatorRef = useRef<HTMLSpanElement>(null);\n const wasSelected = useRef(selected);\n const spring = useM3Spring('spatial', 'fast');\n const reducedMotion = useReducedMotion();\n useLayoutEffect(() => {\n const indicator = indicatorRef.current;\n const changed = wasSelected.current !== selected;\n wasSelected.current = selected;\n if (!lastIndicatorRef || !selected || !indicator) return;\n const previous = lastIndicatorRef.current;\n lastIndicatorRef.current = indicator;\n if (!changed || reducedMotion || !previous?.isConnected || previous === indicator) return;\n // Measure both pills at rest; an earlier slide may have left a transform or width behind.\n indicator.style.transform = '';\n indicator.style.width = '';\n const from = previous.getBoundingClientRect();\n const to = indicator.getBoundingClientRect();\n const offset = from.top - to.top;\n if (offset === 0) return;\n // Start from the old place and width now: Motion's first frame comes a frame later.\n indicator.style.transform = `translateY(${offset}px)`;\n if (from.width === to.width) {\n animate(indicator, { y: [offset, 0] }, spring);\n return;\n }\n indicator.style.width = `${from.width}px`;\n // Width animates in px; once settled the pill sizes itself to its content again.\n void animate(indicator, { y: [offset, 0], width: [from.width, to.width] }, spring).then(() => {\n indicator.style.width = '';\n });\n }, [selected, lastIndicatorRef, reducedMotion, spring]);\n return indicatorRef;\n}\n\n/**\n * What a badge tells assistive tech about its item: its `label`, its text, or, for a small\n * badge (no children), \"New notification\" (`labels.newNotification`).\n */\nfunction badgeDescription(badge: ReactElement<BadgeProps> | undefined): string | null {\n // A badge can arrive as a lazy server reference without props yet (React's dev server\n // rendering), so read it only from a real element.\n if (!isValidElement<BadgeProps>(badge)) return null;\n const { label, labels, children } = badge.props;\n if (label != null) return label;\n if (typeof children === 'string' || typeof children === 'number') return String(children);\n if (children == null || children === false) {\n return labels?.newNotification ?? 'New notification';\n }\n return null;\n}\n\n/** Shared navigation item: a link (`href`) or button with the M3 pill indicator. */\nexport function NavItem({\n layout,\n enterLabel,\n lastIndicatorRef,\n icon,\n selectedIcon,\n children,\n selected,\n badge,\n badgeLabel,\n onReselect,\n classNames,\n className,\n ...base\n}: NavItemProps & {\n layout: NavItemLayout;\n enterLabel?: boolean;\n /** The list's last current pill, so this item's pill slides from it when it becomes current. */\n lastIndicatorRef?: RefObject<HTMLElement | null>;\n}) {\n const styles = navItemStyles({ layout, enterLabel, slide: Boolean(lastIndicatorRef) });\n const indicatorRef = useSlidingIndicator(Boolean(selected), lastIndicatorRef);\n const descriptionId = useId();\n const inline = layout === 'rail-start' || layout === 'bar-start';\n // M3: badges sit on the icon, except in the expanded rail, where they follow the label.\n const badgeAfterLabel = layout === 'rail-start';\n const description = badgeLabel ?? badgeDescription(badge);\n const label = (\n <span\n data-nav-label=\"\"\n data-slot=\"label\"\n className={styles.label({ class: classNames?.label })}\n >\n {children}\n </span>\n );\n const iconFaces = selectedIcon ? (\n <>\n <span data-shown={!selected || undefined} className={styles.iconFace()}>\n {icon}\n </span>\n <span data-shown={selected || undefined} className={styles.iconFace()}>\n {selectedIcon}\n </span>\n </>\n ) : (\n icon\n );\n const action = base as ButtonBaseActionProps;\n // M3: a press ripples through the pill. The item is the pressable element, so the pill's\n // state layer gets its own press state and a ripple origin in its own box.\n const stateLayerRef = useRef<HTMLSpanElement>(null);\n const [pressed, setPressed] = useState(false);\n const rippling = useRippleHold(pressed && !action.disabled);\n return (\n <ButtonBase\n {...action}\n onPress={(event) => {\n action.onPress?.(event);\n if (selected) onReselect?.();\n }}\n onPressChange={(isPressed) => {\n action.onPressChange?.(isPressed);\n setPressed(isPressed);\n }}\n onPointerDown={(event: PointerEvent<HTMLButtonElement>) => {\n action.onPointerDown?.(event);\n if (stateLayerRef.current && !action.disabled && event.button === 0) {\n setRippleOrigin(stateLayerRef.current, event.clientX, event.clientY);\n }\n }}\n onKeyDown={(event: KeyboardEvent<HTMLButtonElement>) => {\n action.onKeyDown?.(event);\n if (\n stateLayerRef.current &&\n !action.disabled &&\n (event.key === 'Enter' || event.key === ' ')\n ) {\n setRippleOrigin(stateLayerRef.current);\n }\n }}\n data-nav-item=\"\"\n // An explicit value wins, for a destination that shows as current without being the page.\n aria-current={action['aria-current'] ?? (selected ? 'page' : undefined)}\n aria-describedby={\n [action['aria-describedby'], badge && description != null ? descriptionId : undefined]\n .filter(Boolean)\n .join(' ') || undefined\n }\n data-current={selected || undefined}\n // The bar's or rail's component tokens, resolved on the item for its state and layout.\n data-tokens={mergeDataTokens(\n layout.startsWith('rail') ? 'md-comp-nav-rail' : 'md-comp-nav-bar',\n action,\n )}\n data-layout={layout}\n className={styles.root({ class: cn(classNames?.root, className) })}\n >\n <span data-slot=\"pill\" className={styles.pill({ class: classNames?.pill })}>\n <span\n ref={indicatorRef}\n aria-hidden=\"true\"\n data-slot=\"indicator\"\n className={styles.indicator({ class: classNames?.indicator })}\n />\n <span\n ref={stateLayerRef}\n aria-hidden=\"true\"\n data-rippling={rippling || undefined}\n data-disabled={action.disabled || undefined}\n className={styles.stateLayer({ class: classNames?.stateLayer })}\n />\n <span className={styles.content()}>\n <span\n aria-hidden=\"true\"\n data-slot=\"icon\"\n className={styles.icon({ class: classNames?.icon })}\n >\n {badge && !badgeAfterLabel ? (\n <BadgedBox badge={badge} className={styles.badgedBox({ class: classNames?.badge })}>\n <span className={styles.iconStack()}>{iconFaces}</span>\n </BadgedBox>\n ) : (\n iconFaces\n )}\n </span>\n {inline && label}\n {badge && badgeAfterLabel && (\n <span aria-hidden=\"true\" className={styles.badge({ class: classNames?.badge })}>\n {badge}\n </span>\n )}\n </span>\n </span>\n {!inline && label}\n {badge && description != null && (\n <span id={descriptionId} hidden>\n {description}\n </span>\n )}\n </ButtonBase>\n );\n}\n"],"mappings":"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"}
|
|
@@ -35,8 +35,13 @@ interface NavigationRailOwnProps {
|
|
|
35
35
|
* @default false
|
|
36
36
|
*/
|
|
37
37
|
modal?: boolean;
|
|
38
|
-
/**
|
|
39
|
-
|
|
38
|
+
/**
|
|
39
|
+
* With `modal`, show nothing while collapsed (the expanded rail slides in). `compact` does so
|
|
40
|
+
* only below 600px wide, in CSS so the server's HTML is right: one rail then serves as the
|
|
41
|
+
* rail on larger windows and as the drawer on a phone.
|
|
42
|
+
* @default false
|
|
43
|
+
*/
|
|
44
|
+
hideOnCollapse?: boolean | 'compact';
|
|
40
45
|
/** Content above the items, e.g. a menu button and a FAB; a function receives the state. */
|
|
41
46
|
header?: ReactNode | ((state: {
|
|
42
47
|
expanded: boolean;
|
|
@@ -85,6 +90,20 @@ export type NavigationRailProps = NavigationRailOwnProps & Naming & Omit<Compone
|
|
|
85
90
|
*/
|
|
86
91
|
export declare function NavigationRail({ expanded, defaultExpanded, onExpandedChange, modal, hideOnCollapse, header, itemAlignment, divider, indicatorMotion, children, classNames, className, style, ...rest }: NavigationRailProps): import("react").JSX.Element;
|
|
87
92
|
export type NavigationRailItemProps = NavItemProps;
|
|
93
|
+
/** What {@link useNavigationRail} reports about the rail around a component. */
|
|
94
|
+
export interface NavigationRailState {
|
|
95
|
+
/** Whether the rail shows its labels beside the icons (expanded, or the modal sheet). */
|
|
96
|
+
expanded: boolean;
|
|
97
|
+
/** Whether this is the rail's modal sheet. */
|
|
98
|
+
modal: boolean;
|
|
99
|
+
/** Collapses the modal sheet; does nothing in the rail itself. */
|
|
100
|
+
close: () => void;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The rail around a component, for a custom destination that sits in a {@link NavigationRail}
|
|
104
|
+
* beside its items and changes with it. Throws outside a rail.
|
|
105
|
+
*/
|
|
106
|
+
export declare function useNavigationRail(): NavigationRailState;
|
|
88
107
|
/** A destination in a {@link NavigationRail}: a link (`href`) or a button. */
|
|
89
108
|
export declare function NavigationRailItem({ onPress, ...props }: NavigationRailItemProps): import("react").JSX.Element;
|
|
90
109
|
//#endregion
|
|
@@ -124,13 +124,14 @@ function NavigationRail({ expanded, defaultExpanded = false, onExpandedChange, m
|
|
|
124
124
|
expanded: inlineExpanded,
|
|
125
125
|
itemAlignment,
|
|
126
126
|
divider,
|
|
127
|
-
slidingIndicator: slide && inlineExpanded
|
|
127
|
+
slidingIndicator: slide && inlineExpanded,
|
|
128
|
+
hiddenOnCompact: modal && hideOnCollapse === "compact"
|
|
128
129
|
});
|
|
129
130
|
const headerContent = typeof header === "function" ? header({
|
|
130
131
|
expanded: isExpanded,
|
|
131
132
|
toggle
|
|
132
133
|
}) : header;
|
|
133
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [!(modal && hideOnCollapse) && /* @__PURE__ */ jsx("nav", {
|
|
134
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [!(modal && hideOnCollapse === true) && /* @__PURE__ */ jsx("nav", {
|
|
134
135
|
...rest,
|
|
135
136
|
ref: rootRef,
|
|
136
137
|
style: {
|
|
@@ -166,7 +167,7 @@ function NavigationRail({ expanded, defaultExpanded = false, onExpandedChange, m
|
|
|
166
167
|
onClose: () => setExpanded(false),
|
|
167
168
|
header: headerContent,
|
|
168
169
|
naming: rest,
|
|
169
|
-
slide: hideOnCollapse,
|
|
170
|
+
slide: hideOnCollapse !== false,
|
|
170
171
|
itemAlignment,
|
|
171
172
|
slidingIndicator: slide,
|
|
172
173
|
railRef: rootRef,
|
|
@@ -264,6 +265,19 @@ function ModalSheet({ state, isExiting, exitProps, header, naming, slide, itemAl
|
|
|
264
265
|
})]
|
|
265
266
|
})] });
|
|
266
267
|
}
|
|
268
|
+
/**
|
|
269
|
+
* The rail around a component, for a custom destination that sits in a {@link NavigationRail}
|
|
270
|
+
* beside its items and changes with it. Throws outside a rail.
|
|
271
|
+
*/
|
|
272
|
+
function useNavigationRail() {
|
|
273
|
+
const rail = useContext(RailContext);
|
|
274
|
+
if (!rail) throw new Error("useNavigationRail must be used inside a NavigationRail");
|
|
275
|
+
return {
|
|
276
|
+
expanded: rail.layout === "rail-start",
|
|
277
|
+
modal: rail.onNavigate !== void 0,
|
|
278
|
+
close: () => rail.onNavigate?.()
|
|
279
|
+
};
|
|
280
|
+
}
|
|
267
281
|
/** A destination in a {@link NavigationRail}: a link (`href`) or a button. */
|
|
268
282
|
function NavigationRailItem({ onPress, ...props }) {
|
|
269
283
|
const rail = useContext(RailContext);
|
|
@@ -280,6 +294,6 @@ function NavigationRailItem({ onPress, ...props }) {
|
|
|
280
294
|
});
|
|
281
295
|
}
|
|
282
296
|
//#endregion
|
|
283
|
-
export { NavigationRail, NavigationRailItem };
|
|
297
|
+
export { NavigationRail, NavigationRailItem, useNavigationRail };
|
|
284
298
|
|
|
285
299
|
//# sourceMappingURL=NavigationRail.js.map
|