@webiny/admin-ui 6.5.0-beta.9 → 6.5.1-beta.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/AutoComplete/primitives/AutoCompletePrimitive.d.ts +6 -0
- package/AutoComplete/primitives/AutoCompletePrimitive.js +1 -0
- package/AutoComplete/primitives/AutoCompletePrimitive.js.map +1 -1
- package/DatePicker/DatePicker.stories.d.ts +10 -0
- package/DatePicker/DatePicker.stories.js +44 -1
- package/DatePicker/DatePicker.stories.js.map +1 -1
- package/DatePicker/primitives/BirthdayPicker.d.ts +13 -0
- package/DatePicker/primitives/BirthdayPicker.js +355 -0
- package/DatePicker/primitives/BirthdayPicker.js.map +1 -0
- package/DatePicker/primitives/BirthdayPickerCell.d.ts +14 -0
- package/DatePicker/primitives/BirthdayPickerCell.js +19 -0
- package/DatePicker/primitives/BirthdayPickerCell.js.map +1 -0
- package/DatePicker/primitives/BirthdayPickerSegments.d.ts +9 -0
- package/DatePicker/primitives/BirthdayPickerSegments.js +43 -0
- package/DatePicker/primitives/BirthdayPickerSegments.js.map +1 -0
- package/DatePicker/primitives/DatePickerPrimitive.js +3 -0
- package/DatePicker/primitives/DatePickerPrimitive.js.map +1 -1
- package/DatePicker/utils/birthdayHelpers.d.ts +77 -0
- package/DatePicker/utils/birthdayHelpers.js +145 -0
- package/DatePicker/utils/birthdayHelpers.js.map +1 -0
- package/DatePicker/utils/birthdayHelpers.test.d.ts +1 -0
- package/DatePicker/utils/birthdayHelpers.test.js +394 -0
- package/DatePicker/utils/birthdayHelpers.test.js.map +1 -0
- package/DatePicker/utils/index.d.ts +1 -0
- package/DatePicker/utils/index.js +1 -0
- package/DatePicker/utils/types.d.ts +13 -2
- package/MultiAutoComplete/primitives/MultiAutoCompletePrimitive.d.ts +6 -0
- package/MultiAutoComplete/primitives/MultiAutoCompletePrimitive.js +1 -0
- package/MultiAutoComplete/primitives/MultiAutoCompletePrimitive.js.map +1 -1
- package/Switch/Switch.d.ts +4 -4
- package/Switch/Switch.js +21 -7
- package/Switch/Switch.js.map +1 -1
- package/Switch/domains/SwitchItemDto.d.ts +1 -1
- package/Switch/primitives/SwitchPrimitive.js +4 -3
- package/Switch/primitives/SwitchPrimitive.js.map +1 -1
- package/package.json +18 -18
|
@@ -74,6 +74,12 @@ interface AutoCompletePrimitiveProps {
|
|
|
74
74
|
* Refer to `InputPrimitiveProps["size"]` for possible values.
|
|
75
75
|
*/
|
|
76
76
|
size?: InputPrimitiveProps["size"];
|
|
77
|
+
/**
|
|
78
|
+
* Filter the options by the typed text on the client. Defaults to `true`.
|
|
79
|
+
* Set to `false` when `onValueSearch` already returns the matching options,
|
|
80
|
+
* so options matched on data other than their label are not dropped.
|
|
81
|
+
*/
|
|
82
|
+
shouldFilter?: boolean;
|
|
77
83
|
/**
|
|
78
84
|
* Icon to be displayed at the start of the input field.
|
|
79
85
|
*/
|
|
@@ -31,6 +31,7 @@ const DecoratableAutoCompletePrimitive = (props)=>{
|
|
|
31
31
|
onOpenChange: ()=>setListOpenState(true)
|
|
32
32
|
}, /*#__PURE__*/ react.createElement(Command, {
|
|
33
33
|
label: String(props.label),
|
|
34
|
+
shouldFilter: props.shouldFilter,
|
|
34
35
|
onKeyDown: handleKeyDown
|
|
35
36
|
}, /*#__PURE__*/ react.createElement(PopoverPrimitive.Trigger, {
|
|
36
37
|
asChild: true
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutoComplete/primitives/AutoCompletePrimitive.js","sources":["../../../src/AutoComplete/primitives/AutoCompletePrimitive.tsx"],"sourcesContent":["import type { KeyboardEvent } from \"react\";\nimport React from \"react\";\nimport { Command } from \"~/Command/index.js\";\nimport { PopoverPrimitive } from \"~/Popover/index.js\";\nimport type { InputPrimitiveProps } from \"~/Input/index.js\";\nimport { useAutoComplete } from \"./useAutoComplete.js\";\nimport { AutoCompleteInputIcons, AutoCompleteList } from \"./components/index.js\";\nimport type { AutoCompleteOption } from \"../domains/index.js\";\nimport { makeDecoratable } from \"~/utils.js\";\nimport type { Icon } from \"~/Icon/index.js\";\n\ninterface AutoCompletePrimitiveProps {\n /**\n * Indicates if the reset action should be displayed.\n */\n displayResetAction?: boolean;\n /**\n * Indicates if the field is disabled.\n */\n disabled?: boolean;\n /**\n * Message to display when there are no options.\n */\n emptyMessage?: React.ReactNode;\n /**\n * Message to display when there are no options loaded or selected.\n * Use it to invite the user to interact with the autocomplete by typing a value.\n */\n initialMessage?: React.ReactNode;\n /**\n * Reference to the input element.\n */\n inputRef?: React.Ref<HTMLInputElement>;\n /**\n * Indicates if the input field is invalid.\n * Refer to `InputPrimitiveProps[\"invalid\"]` for possible values.\n */\n invalid?: InputPrimitiveProps[\"invalid\"];\n /**\n * Accessible label for the command menu. Not shown visibly.\n */\n label?: string;\n /**\n * Indicates if the autocomplete is loading options.\n */\n loading?: boolean;\n /**\n * Message to display while loading options.\n */\n loadingMessage?: React.ReactNode;\n /**\n * Callback triggered when the open state changes.\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * Callback triggered to reset the value.\n */\n onValueReset?: () => void;\n /**\n * Callback triggered when a value has been searched by the user.\n */\n onValueSearch?: (value: string) => void;\n /**\n * Callback triggered when the value changes.\n */\n onValueChange: (value: string) => void;\n /**\n * List of options for the autocomplete.\n */\n options?: AutoCompleteOption[];\n /**\n * Custom renderer for the options.\n */\n optionRenderer?: (item: any, index: number) => React.ReactNode;\n /**\n * Placeholder text for the input field.\n */\n placeholder?: string;\n /**\n * Size of the input field.\n * Refer to `InputPrimitiveProps[\"size\"]` for possible values.\n */\n size?: InputPrimitiveProps[\"size\"];\n /**\n * Icon to be displayed at the start of the input field.\n */\n startIcon?: React.ReactElement<typeof Icon> | React.ReactElement;\n /**\n * Variant of the input field.\n * Refer to `InputPrimitiveProps[\"variant\"]` for possible values.\n */\n variant?: InputPrimitiveProps[\"variant\"];\n /**\n * Optional selected item.\n */\n value?: string;\n}\n\nconst DecoratableAutoCompletePrimitive = (props: AutoCompletePrimitiveProps) => {\n const {\n vm,\n setListOpenState,\n setSelectedOption,\n searchOption,\n resetSelectedOption,\n showSelectedOption\n } = useAutoComplete(props);\n\n const handleKeyDown = React.useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n if (props.disabled) {\n return;\n }\n\n if (event.key.toLowerCase() === \"escape\") {\n setListOpenState(false);\n } else {\n setListOpenState(true);\n }\n },\n [props.disabled, setListOpenState]\n );\n\n const handleSelectOption = React.useCallback(\n (value: string) => {\n setSelectedOption(value);\n setListOpenState(false);\n },\n [setSelectedOption, setListOpenState]\n );\n\n const handleOnBlur = React.useCallback(() => {\n setListOpenState(false);\n showSelectedOption();\n }, [setListOpenState]);\n\n return (\n <PopoverPrimitive open={vm.optionsListVm.open} onOpenChange={() => setListOpenState(true)}>\n <Command
|
|
1
|
+
{"version":3,"file":"AutoComplete/primitives/AutoCompletePrimitive.js","sources":["../../../src/AutoComplete/primitives/AutoCompletePrimitive.tsx"],"sourcesContent":["import type { KeyboardEvent } from \"react\";\nimport React from \"react\";\nimport { Command } from \"~/Command/index.js\";\nimport { PopoverPrimitive } from \"~/Popover/index.js\";\nimport type { InputPrimitiveProps } from \"~/Input/index.js\";\nimport { useAutoComplete } from \"./useAutoComplete.js\";\nimport { AutoCompleteInputIcons, AutoCompleteList } from \"./components/index.js\";\nimport type { AutoCompleteOption } from \"../domains/index.js\";\nimport { makeDecoratable } from \"~/utils.js\";\nimport type { Icon } from \"~/Icon/index.js\";\n\ninterface AutoCompletePrimitiveProps {\n /**\n * Indicates if the reset action should be displayed.\n */\n displayResetAction?: boolean;\n /**\n * Indicates if the field is disabled.\n */\n disabled?: boolean;\n /**\n * Message to display when there are no options.\n */\n emptyMessage?: React.ReactNode;\n /**\n * Message to display when there are no options loaded or selected.\n * Use it to invite the user to interact with the autocomplete by typing a value.\n */\n initialMessage?: React.ReactNode;\n /**\n * Reference to the input element.\n */\n inputRef?: React.Ref<HTMLInputElement>;\n /**\n * Indicates if the input field is invalid.\n * Refer to `InputPrimitiveProps[\"invalid\"]` for possible values.\n */\n invalid?: InputPrimitiveProps[\"invalid\"];\n /**\n * Accessible label for the command menu. Not shown visibly.\n */\n label?: string;\n /**\n * Indicates if the autocomplete is loading options.\n */\n loading?: boolean;\n /**\n * Message to display while loading options.\n */\n loadingMessage?: React.ReactNode;\n /**\n * Callback triggered when the open state changes.\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * Callback triggered to reset the value.\n */\n onValueReset?: () => void;\n /**\n * Callback triggered when a value has been searched by the user.\n */\n onValueSearch?: (value: string) => void;\n /**\n * Callback triggered when the value changes.\n */\n onValueChange: (value: string) => void;\n /**\n * List of options for the autocomplete.\n */\n options?: AutoCompleteOption[];\n /**\n * Custom renderer for the options.\n */\n optionRenderer?: (item: any, index: number) => React.ReactNode;\n /**\n * Placeholder text for the input field.\n */\n placeholder?: string;\n /**\n * Size of the input field.\n * Refer to `InputPrimitiveProps[\"size\"]` for possible values.\n */\n size?: InputPrimitiveProps[\"size\"];\n /**\n * Filter the options by the typed text on the client. Defaults to `true`.\n * Set to `false` when `onValueSearch` already returns the matching options,\n * so options matched on data other than their label are not dropped.\n */\n shouldFilter?: boolean;\n /**\n * Icon to be displayed at the start of the input field.\n */\n startIcon?: React.ReactElement<typeof Icon> | React.ReactElement;\n /**\n * Variant of the input field.\n * Refer to `InputPrimitiveProps[\"variant\"]` for possible values.\n */\n variant?: InputPrimitiveProps[\"variant\"];\n /**\n * Optional selected item.\n */\n value?: string;\n}\n\nconst DecoratableAutoCompletePrimitive = (props: AutoCompletePrimitiveProps) => {\n const {\n vm,\n setListOpenState,\n setSelectedOption,\n searchOption,\n resetSelectedOption,\n showSelectedOption\n } = useAutoComplete(props);\n\n const handleKeyDown = React.useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n if (props.disabled) {\n return;\n }\n\n if (event.key.toLowerCase() === \"escape\") {\n setListOpenState(false);\n } else {\n setListOpenState(true);\n }\n },\n [props.disabled, setListOpenState]\n );\n\n const handleSelectOption = React.useCallback(\n (value: string) => {\n setSelectedOption(value);\n setListOpenState(false);\n },\n [setSelectedOption, setListOpenState]\n );\n\n const handleOnBlur = React.useCallback(() => {\n setListOpenState(false);\n showSelectedOption();\n }, [setListOpenState]);\n\n return (\n <PopoverPrimitive open={vm.optionsListVm.open} onOpenChange={() => setListOpenState(true)}>\n <Command\n label={String(props.label)}\n shouldFilter={props.shouldFilter}\n onKeyDown={handleKeyDown}\n >\n <PopoverPrimitive.Trigger asChild>\n <span>\n <Command.Input\n inputRef={props.inputRef}\n value={vm.inputVm.value}\n onValueChange={searchOption}\n placeholder={vm.inputVm.placeholder}\n size={props.size}\n variant={props.variant}\n disabled={props.disabled}\n invalid={props.invalid}\n startIcon={props.startIcon}\n endIcon={\n <AutoCompleteInputIcons\n inputSize={props.size}\n inputVariant={props.variant}\n displayResetAction={vm.inputVm.displayResetAction}\n disabled={props.disabled}\n loading={props.loading}\n onResetValue={resetSelectedOption}\n onOpenChange={() => setListOpenState(!vm.optionsListVm.open)}\n />\n }\n onBlur={handleOnBlur}\n onFocus={() => setListOpenState(true)}\n />\n </span>\n </PopoverPrimitive.Trigger>\n <PopoverPrimitive.Content\n style={{ minWidth: \"var(--radix-popover-trigger-width)\" }}\n onOpenAutoFocus={e => e.preventDefault()}\n >\n <AutoCompleteList\n options={vm.optionsListVm.options}\n onOptionSelect={handleSelectOption}\n empty={vm.optionsListVm.empty}\n loading={props.loading}\n loadingMessage={vm.optionsListVm.loadingMessage}\n emptyMessage={vm.optionsListVm.emptyMessage}\n optionRenderer={props.optionRenderer}\n />\n </PopoverPrimitive.Content>\n </Command>\n </PopoverPrimitive>\n );\n};\n\nconst AutoCompletePrimitive = makeDecoratable(\n \"AutoCompletePrimitive\",\n DecoratableAutoCompletePrimitive\n);\n\nexport { AutoCompletePrimitive, type AutoCompletePrimitiveProps };\n"],"names":["DecoratableAutoCompletePrimitive","props","vm","setListOpenState","setSelectedOption","searchOption","resetSelectedOption","showSelectedOption","useAutoComplete","handleKeyDown","React","event","handleSelectOption","value","handleOnBlur","PopoverPrimitive","Command","String","AutoCompleteInputIcons","e","AutoCompleteList","AutoCompletePrimitive","makeDecoratable"],"mappings":";;;;;;AAwGA,MAAMA,mCAAmC,CAACC;IACtC,MAAM,EACFC,EAAE,EACFC,gBAAgB,EAChBC,iBAAiB,EACjBC,YAAY,EACZC,mBAAmB,EACnBC,kBAAkB,EACrB,GAAGC,gBAAgBP;IAEpB,MAAMQ,gBAAgBC,MAAAA,WAAiB,CACnC,CAACC;QACG,IAAIV,MAAM,QAAQ,EACd;QAG4B,aAA5BU,MAAM,GAAG,CAAC,WAAW,KACrBR,iBAAiB,SAEjBA,iBAAiB;IAEzB,GACA;QAACF,MAAM,QAAQ;QAAEE;KAAiB;IAGtC,MAAMS,qBAAqBF,MAAAA,WAAiB,CACxC,CAACG;QACGT,kBAAkBS;QAClBV,iBAAiB;IACrB,GACA;QAACC;QAAmBD;KAAiB;IAGzC,MAAMW,eAAeJ,MAAAA,WAAiB,CAAC;QACnCP,iBAAiB;QACjBI;IACJ,GAAG;QAACJ;KAAiB;IAErB,OAAO,WAAP,GACI,oBAACY,kBAAgBA;QAAC,MAAMb,GAAG,aAAa,CAAC,IAAI;QAAE,cAAc,IAAMC,iBAAiB;qBAChF,oBAACa,SAAOA;QACJ,OAAOC,OAAOhB,MAAM,KAAK;QACzB,cAAcA,MAAM,YAAY;QAChC,WAAWQ;qBAEX,oBAACM,iBAAiB,OAAO;QAAC;qBACtB,oBAAC,4BACG,oBAACC,QAAQ,KAAK;QACV,UAAUf,MAAM,QAAQ;QACxB,OAAOC,GAAG,OAAO,CAAC,KAAK;QACvB,eAAeG;QACf,aAAaH,GAAG,OAAO,CAAC,WAAW;QACnC,MAAMD,MAAM,IAAI;QAChB,SAASA,MAAM,OAAO;QACtB,UAAUA,MAAM,QAAQ;QACxB,SAASA,MAAM,OAAO;QACtB,WAAWA,MAAM,SAAS;QAC1B,uBACI,oBAACiB,wBAAsBA;YACnB,WAAWjB,MAAM,IAAI;YACrB,cAAcA,MAAM,OAAO;YAC3B,oBAAoBC,GAAG,OAAO,CAAC,kBAAkB;YACjD,UAAUD,MAAM,QAAQ;YACxB,SAASA,MAAM,OAAO;YACtB,cAAcK;YACd,cAAc,IAAMH,iBAAiB,CAACD,GAAG,aAAa,CAAC,IAAI;;QAGnE,QAAQY;QACR,SAAS,IAAMX,iBAAiB;wBAI5C,oBAACY,iBAAiB,OAAO;QACrB,OAAO;YAAE,UAAU;QAAqC;QACxD,iBAAiBI,CAAAA,IAAKA,EAAE,cAAc;qBAEtC,oBAACC,kBAAgBA;QACb,SAASlB,GAAG,aAAa,CAAC,OAAO;QACjC,gBAAgBU;QAChB,OAAOV,GAAG,aAAa,CAAC,KAAK;QAC7B,SAASD,MAAM,OAAO;QACtB,gBAAgBC,GAAG,aAAa,CAAC,cAAc;QAC/C,cAAcA,GAAG,aAAa,CAAC,YAAY;QAC3C,gBAAgBD,MAAM,cAAc;;AAM5D;AAEA,MAAMoB,wBAAwBC,gBAC1B,yBACAtB"}
|
|
@@ -12,6 +12,16 @@ export declare const WithErrors: Story;
|
|
|
12
12
|
export declare const Disabled: Story;
|
|
13
13
|
export declare const FullExample: Story;
|
|
14
14
|
export declare const WithPresets: Story;
|
|
15
|
+
/**
|
|
16
|
+
* Picks a date one step at a time: a day, then a month, then a year (newest first). Built for
|
|
17
|
+
* dates far in the past, like a date of birth. The value is a `YYYY-MM-DD` string.
|
|
18
|
+
*/
|
|
19
|
+
export declare const TypeBirthday: Story;
|
|
20
|
+
/**
|
|
21
|
+
* With `minDate` and `maxDate`, years run from the latest allowed one down to the earliest,
|
|
22
|
+
* and days and months that can't make an allowed date are dimmed. Here: 18 to 100 years old.
|
|
23
|
+
*/
|
|
24
|
+
export declare const TypeBirthdayWithLimits: Story;
|
|
15
25
|
export declare const TypeTime: Story;
|
|
16
26
|
export declare const TypeDateTimeLocal: Story;
|
|
17
27
|
export declare const TypeDateTimeTz: Story;
|
|
@@ -12,6 +12,7 @@ const meta = {
|
|
|
12
12
|
control: "select",
|
|
13
13
|
options: [
|
|
14
14
|
"date",
|
|
15
|
+
"birthday",
|
|
15
16
|
"time",
|
|
16
17
|
"dateTimeLocal",
|
|
17
18
|
"dateTimeTz",
|
|
@@ -144,6 +145,48 @@ const WithPresets = {
|
|
|
144
145
|
description: "Choose a date or use a preset."
|
|
145
146
|
}
|
|
146
147
|
};
|
|
148
|
+
const TypeBirthday = {
|
|
149
|
+
render: (args)=>{
|
|
150
|
+
const [value, setValue] = useState("1990-02-09");
|
|
151
|
+
return /*#__PURE__*/ react.createElement("div", {
|
|
152
|
+
className: "flex flex-col gap-md max-w-[480px]"
|
|
153
|
+
}, /*#__PURE__*/ react.createElement(DatePicker, {
|
|
154
|
+
...args,
|
|
155
|
+
type: "birthday",
|
|
156
|
+
value: value,
|
|
157
|
+
onChange: setValue
|
|
158
|
+
}), /*#__PURE__*/ react.createElement("span", {
|
|
159
|
+
className: "text-sm text-neutral-muted"
|
|
160
|
+
}, "Value: ", value ?? "(empty)"));
|
|
161
|
+
},
|
|
162
|
+
args: {
|
|
163
|
+
label: "Date of birth"
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
const TypeBirthdayWithLimits = {
|
|
167
|
+
render: (args)=>{
|
|
168
|
+
const [value, setValue] = useState();
|
|
169
|
+
const today = new Date();
|
|
170
|
+
const [minDate] = useState(()=>new Date(today.getFullYear() - 100, today.getMonth(), today.getDate()));
|
|
171
|
+
const [maxDate] = useState(()=>new Date(today.getFullYear() - 18, today.getMonth(), today.getDate()));
|
|
172
|
+
return /*#__PURE__*/ react.createElement("div", {
|
|
173
|
+
className: "flex flex-col gap-md max-w-[480px]"
|
|
174
|
+
}, /*#__PURE__*/ react.createElement(DatePicker, {
|
|
175
|
+
...args,
|
|
176
|
+
type: "birthday",
|
|
177
|
+
value: value,
|
|
178
|
+
onChange: setValue,
|
|
179
|
+
minDate: minDate,
|
|
180
|
+
maxDate: maxDate
|
|
181
|
+
}), /*#__PURE__*/ react.createElement("span", {
|
|
182
|
+
className: "text-sm text-neutral-muted"
|
|
183
|
+
}, "Value: ", value ?? "(empty)"));
|
|
184
|
+
},
|
|
185
|
+
args: {
|
|
186
|
+
label: "Date of birth",
|
|
187
|
+
description: "You must be 18 or older."
|
|
188
|
+
}
|
|
189
|
+
};
|
|
147
190
|
const TypeTime = {
|
|
148
191
|
render: (args)=>{
|
|
149
192
|
const [value, setValue] = useState();
|
|
@@ -399,6 +442,6 @@ const Documentation = {
|
|
|
399
442
|
}
|
|
400
443
|
};
|
|
401
444
|
export default DatePicker_stories;
|
|
402
|
-
export { Default, Disabled, Documentation, FullExample, TypeDateRange, TypeDateTimeLocal, TypeDateTimeTz, TypeMonth, TypeMultipleDates, TypeMultipleMonths, TypeMultipleYears, TypeTime, TypeWeek, TypeYear, WithDescription, WithErrors, WithLabel, WithLabelRequired, WithNotes, WithPresets };
|
|
445
|
+
export { Default, Disabled, Documentation, FullExample, TypeBirthday, TypeBirthdayWithLimits, TypeDateRange, TypeDateTimeLocal, TypeDateTimeTz, TypeMonth, TypeMultipleDates, TypeMultipleMonths, TypeMultipleYears, TypeTime, TypeWeek, TypeYear, WithDescription, WithErrors, WithLabel, WithLabelRequired, WithNotes, WithPresets };
|
|
403
446
|
|
|
404
447
|
//# sourceMappingURL=DatePicker.stories.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatePicker/DatePicker.stories.js","sources":["../../src/DatePicker/DatePicker.stories.tsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport type { Meta, StoryObj } from \"@storybook/react\";\nimport { addDays, nextSaturday } from \"date-fns\";\nimport { DatePicker } from \"./DatePicker.js\";\n\nconst meta: Meta<typeof DatePicker> = {\n title: \"Components/Form/DatePicker\",\n component: DatePicker,\n argTypes: {\n onChange: { action: \"onChange\" },\n type: {\n control: \"select\",\n options: [\n \"date\",\n \"time\",\n \"dateTimeLocal\",\n \"dateTimeTz\",\n \"month\",\n \"week\",\n \"year\",\n \"dateRange\",\n \"multipleDates\",\n \"multipleMonths\",\n \"multipleYears\"\n ],\n defaultValue: \"date\"\n },\n disabled: {\n control: \"boolean\",\n defaultValue: false\n }\n },\n parameters: {\n layout: \"padded\"\n },\n render: args => {\n const [value, setValue] = useState<any>();\n return <DatePicker {...args} value={value} onChange={setValue} />;\n }\n};\n\nexport default meta;\ntype Story = StoryObj<typeof DatePicker>;\n\nexport const Default: Story = {\n args: {\n type: \"date\"\n }\n};\n\nexport const WithLabel: Story = {\n args: {\n type: \"date\",\n label: \"Any field label\"\n }\n};\n\nexport const WithLabelRequired: Story = {\n args: {\n type: \"date\",\n label: \"Any field label\",\n required: true\n }\n};\n\nexport const WithDescription: Story = {\n args: {\n type: \"date\",\n description: \"Provide the required information for processing your request.\"\n }\n};\n\nexport const WithNotes: Story = {\n args: {\n type: \"date\",\n note: \"Note: Ensure your selection or input is accurate before proceeding.\"\n }\n};\n\nexport const WithErrors: Story = {\n args: {\n type: \"date\",\n validation: {\n isValid: false,\n message: \"This field is required.\"\n }\n }\n};\n\nexport const Disabled: Story = {\n args: {\n type: \"date\",\n label: \"Any field label\",\n disabled: true\n }\n};\n\nexport const FullExample: Story = {\n args: {\n type: \"date\",\n label: \"Any field label\",\n required: true,\n description: \"Provide the required information for processing your request.\",\n note: \"Note: Ensure your selection or input is accurate before proceeding.\",\n validation: {\n isValid: false,\n message: \"This field is required.\"\n }\n }\n};\n\nexport const WithPresets: Story = {\n render: args => {\n const [value, setValue] = useState<string | undefined>();\n const presets = [\n { label: \"Today\", value: () => new Date() },\n { label: \"Tomorrow\", value: () => addDays(new Date(), 1) },\n { label: \"In 3 days\", value: () => addDays(new Date(), 3) },\n { label: \"In a week\", value: () => addDays(new Date(), 7) },\n { label: \"In 2 weeks\", value: () => addDays(new Date(), 14) },\n { label: \"Next Saturday\", value: () => nextSaturday(new Date()) }\n ];\n return (\n <DatePicker {...args} type=\"date\" value={value} onChange={setValue} presets={presets} />\n );\n },\n args: {\n label: \"Pick a date\",\n description: \"Choose a date or use a preset.\"\n }\n};\n\nexport const TypeTime: Story = {\n render: args => {\n const [value, setValue] = useState<string | undefined>();\n return <DatePicker {...args} type=\"time\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Time\"\n }\n};\n\nexport const TypeDateTimeLocal: Story = {\n render: args => {\n const [value, setValue] = useState<string | undefined>();\n return <DatePicker {...args} type=\"dateTimeLocal\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Date & Time (local)\"\n }\n};\n\nexport const TypeDateTimeTz: Story = {\n render: args => {\n const [value, setValue] = useState<string | undefined>();\n return <DatePicker {...args} type=\"dateTimeTz\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Date & Time (with timezone)\"\n }\n};\n\nexport const TypeMonth: Story = {\n render: args => {\n const [value, setValue] = useState<string | undefined>();\n return <DatePicker {...args} type=\"month\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Month\"\n }\n};\n\nexport const TypeWeek: Story = {\n render: args => {\n const [value, setValue] = useState<string | undefined>();\n return <DatePicker {...args} type=\"week\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Week\"\n }\n};\n\nexport const TypeYear: Story = {\n render: args => {\n const [value, setValue] = useState<number | undefined>();\n return (\n <DatePicker\n {...args}\n type=\"year\"\n value={value}\n onChange={setValue}\n yearRange={[2015, 2035]}\n />\n );\n },\n args: {\n label: \"Year\"\n }\n};\n\nexport const TypeDateRange: Story = {\n render: args => {\n const [value, setValue] = useState<{ from?: string; to?: string } | undefined>();\n return <DatePicker {...args} type=\"dateRange\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Date Range\"\n }\n};\n\nexport const TypeMultipleDates: Story = {\n render: args => {\n const [value, setValue] = useState<string[]>([]);\n return <DatePicker {...args} type=\"multipleDates\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Multiple Dates\"\n }\n};\n\nexport const TypeMultipleMonths: Story = {\n render: args => {\n const [value, setValue] = useState<string[]>([]);\n return <DatePicker {...args} type=\"multipleMonths\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Multiple Months\"\n }\n};\n\nexport const TypeMultipleYears: Story = {\n render: args => {\n const [value, setValue] = useState<number[]>([]);\n return (\n <DatePicker\n {...args}\n type=\"multipleYears\"\n value={value}\n onChange={setValue}\n yearRange={[2015, 2035]}\n />\n );\n },\n args: {\n label: \"Multiple Years\"\n }\n};\n\nconst formatValue = (value: any): string => {\n if (value === undefined || value === null) {\n return \"undefined\";\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (Array.isArray(value)) {\n return JSON.stringify(\n value.map((v: any) => (v instanceof Date ? v.toISOString() : v)),\n null,\n 2\n );\n }\n if (typeof value === \"object\") {\n return JSON.stringify(value, (_k, v) => (v instanceof Date ? v.toISOString() : v), 2);\n }\n return String(value);\n};\n\nconst DocumentationInner = (args: any) => {\n const isMultiple = (args.type as string)?.startsWith(\"multiple\");\n const [value, setValue] = useState<any>(isMultiple ? [] : undefined);\n const [validation, setValidation] = useState({ isValid: true, message: \"\" });\n\n const handleChange = (newValue: any) => {\n setValue(newValue);\n const isEmpty =\n newValue === undefined ||\n newValue === null ||\n newValue === \"\" ||\n (Array.isArray(newValue) && newValue.length === 0);\n if (args.required && isEmpty) {\n setValidation({ isValid: false, message: \"This field is required\" });\n } else {\n setValidation({ isValid: true, message: \"\" });\n }\n };\n\n return (\n <div>\n <DatePicker {...args} value={value} onChange={handleChange} validation={validation} />\n <div className=\"mt-md rounded-md border-sm border-neutral-muted bg-neutral-light p-sm\">\n <div className=\"text-xs font-medium text-neutral-dimmed mb-xs\">Current value</div>\n <pre className=\"text-sm text-neutral-strong whitespace-pre-wrap m-0\">\n {formatValue(value)}\n </pre>\n </div>\n </div>\n );\n};\n\nexport const Documentation: Story = {\n render: args => {\n return <DocumentationInner key={args.type as string} {...args} />;\n },\n args: {\n type: \"date\",\n label: \"Event Date\",\n required: true,\n disabled: false,\n description: \"Select the date for your event\",\n note: \"Note: Choose a date that works for all participants\",\n placeholder: \"Pick a date\",\n validation: undefined\n },\n argTypes: {\n type: {\n description: \"The type of date picker variant to render\",\n control: \"select\",\n options: [\n \"date\",\n \"time\",\n \"dateTimeLocal\",\n \"dateTimeTz\",\n \"month\",\n \"week\",\n \"year\",\n \"dateRange\",\n \"multipleDates\",\n \"multipleMonths\",\n \"multipleYears\"\n ],\n defaultValue: \"date\"\n },\n label: {\n description: \"Label text displayed above the picker\",\n control: \"text\"\n },\n required: {\n description: \"Makes the field required\",\n control: \"boolean\",\n defaultValue: true\n },\n disabled: {\n description: \"Disables the date picker\",\n control: \"boolean\",\n defaultValue: false\n },\n description: {\n description: \"Helper text displayed below the label\",\n control: \"text\"\n },\n note: {\n description: \"Additional note displayed below the picker\",\n control: \"text\"\n },\n placeholder: {\n description: \"Placeholder text shown when no date is selected\",\n control: \"text\"\n },\n validation: {\n description:\n \"Object containing validation state and message. Please refer to the example code for details on usage.\"\n },\n onChange: {\n description: \"Callback function when the value changes\"\n }\n }\n};\n"],"names":["meta","DatePicker","args","value","setValue","useState","Default","WithLabel","WithLabelRequired","WithDescription","WithNotes","WithErrors","Disabled","FullExample","WithPresets","presets","Date","addDays","nextSaturday","TypeTime","TypeDateTimeLocal","TypeDateTimeTz","TypeMonth","TypeWeek","TypeYear","TypeDateRange","TypeMultipleDates","TypeMultipleMonths","TypeMultipleYears","formatValue","Array","JSON","v","_k","String","DocumentationInner","isMultiple","undefined","validation","setValidation","handleChange","newValue","isEmpty","Documentation"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DatePicker/DatePicker.stories.js","sources":["../../src/DatePicker/DatePicker.stories.tsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport type { Meta, StoryObj } from \"@storybook/react\";\nimport { addDays, nextSaturday } from \"date-fns\";\nimport { DatePicker } from \"./DatePicker.js\";\n\nconst meta: Meta<typeof DatePicker> = {\n title: \"Components/Form/DatePicker\",\n component: DatePicker,\n argTypes: {\n onChange: { action: \"onChange\" },\n type: {\n control: \"select\",\n options: [\n \"date\",\n \"birthday\",\n \"time\",\n \"dateTimeLocal\",\n \"dateTimeTz\",\n \"month\",\n \"week\",\n \"year\",\n \"dateRange\",\n \"multipleDates\",\n \"multipleMonths\",\n \"multipleYears\"\n ],\n defaultValue: \"date\"\n },\n disabled: {\n control: \"boolean\",\n defaultValue: false\n }\n },\n parameters: {\n layout: \"padded\"\n },\n render: args => {\n const [value, setValue] = useState<any>();\n return <DatePicker {...args} value={value} onChange={setValue} />;\n }\n};\n\nexport default meta;\ntype Story = StoryObj<typeof DatePicker>;\n\nexport const Default: Story = {\n args: {\n type: \"date\"\n }\n};\n\nexport const WithLabel: Story = {\n args: {\n type: \"date\",\n label: \"Any field label\"\n }\n};\n\nexport const WithLabelRequired: Story = {\n args: {\n type: \"date\",\n label: \"Any field label\",\n required: true\n }\n};\n\nexport const WithDescription: Story = {\n args: {\n type: \"date\",\n description: \"Provide the required information for processing your request.\"\n }\n};\n\nexport const WithNotes: Story = {\n args: {\n type: \"date\",\n note: \"Note: Ensure your selection or input is accurate before proceeding.\"\n }\n};\n\nexport const WithErrors: Story = {\n args: {\n type: \"date\",\n validation: {\n isValid: false,\n message: \"This field is required.\"\n }\n }\n};\n\nexport const Disabled: Story = {\n args: {\n type: \"date\",\n label: \"Any field label\",\n disabled: true\n }\n};\n\nexport const FullExample: Story = {\n args: {\n type: \"date\",\n label: \"Any field label\",\n required: true,\n description: \"Provide the required information for processing your request.\",\n note: \"Note: Ensure your selection or input is accurate before proceeding.\",\n validation: {\n isValid: false,\n message: \"This field is required.\"\n }\n }\n};\n\nexport const WithPresets: Story = {\n render: args => {\n const [value, setValue] = useState<string | undefined>();\n const presets = [\n { label: \"Today\", value: () => new Date() },\n { label: \"Tomorrow\", value: () => addDays(new Date(), 1) },\n { label: \"In 3 days\", value: () => addDays(new Date(), 3) },\n { label: \"In a week\", value: () => addDays(new Date(), 7) },\n { label: \"In 2 weeks\", value: () => addDays(new Date(), 14) },\n { label: \"Next Saturday\", value: () => nextSaturday(new Date()) }\n ];\n return (\n <DatePicker {...args} type=\"date\" value={value} onChange={setValue} presets={presets} />\n );\n },\n args: {\n label: \"Pick a date\",\n description: \"Choose a date or use a preset.\"\n }\n};\n\n/**\n * Picks a date one step at a time: a day, then a month, then a year (newest first). Built for\n * dates far in the past, like a date of birth. The value is a `YYYY-MM-DD` string.\n */\nexport const TypeBirthday: Story = {\n render: args => {\n const [value, setValue] = useState<string | undefined>(\"1990-02-09\");\n return (\n <div className=\"flex flex-col gap-md max-w-[480px]\">\n <DatePicker {...args} type=\"birthday\" value={value} onChange={setValue} />\n <span className=\"text-sm text-neutral-muted\">Value: {value ?? \"(empty)\"}</span>\n </div>\n );\n },\n args: {\n label: \"Date of birth\"\n }\n};\n\n/**\n * With `minDate` and `maxDate`, years run from the latest allowed one down to the earliest,\n * and days and months that can't make an allowed date are dimmed. Here: 18 to 100 years old.\n */\nexport const TypeBirthdayWithLimits: Story = {\n render: args => {\n const [value, setValue] = useState<string | undefined>();\n const today = new Date();\n const [minDate] = useState(\n () => new Date(today.getFullYear() - 100, today.getMonth(), today.getDate())\n );\n const [maxDate] = useState(\n () => new Date(today.getFullYear() - 18, today.getMonth(), today.getDate())\n );\n return (\n <div className=\"flex flex-col gap-md max-w-[480px]\">\n <DatePicker\n {...args}\n type=\"birthday\"\n value={value}\n onChange={setValue}\n minDate={minDate}\n maxDate={maxDate}\n />\n <span className=\"text-sm text-neutral-muted\">Value: {value ?? \"(empty)\"}</span>\n </div>\n );\n },\n args: {\n label: \"Date of birth\",\n description: \"You must be 18 or older.\"\n }\n};\n\nexport const TypeTime: Story = {\n render: args => {\n const [value, setValue] = useState<string | undefined>();\n return <DatePicker {...args} type=\"time\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Time\"\n }\n};\n\nexport const TypeDateTimeLocal: Story = {\n render: args => {\n const [value, setValue] = useState<string | undefined>();\n return <DatePicker {...args} type=\"dateTimeLocal\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Date & Time (local)\"\n }\n};\n\nexport const TypeDateTimeTz: Story = {\n render: args => {\n const [value, setValue] = useState<string | undefined>();\n return <DatePicker {...args} type=\"dateTimeTz\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Date & Time (with timezone)\"\n }\n};\n\nexport const TypeMonth: Story = {\n render: args => {\n const [value, setValue] = useState<string | undefined>();\n return <DatePicker {...args} type=\"month\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Month\"\n }\n};\n\nexport const TypeWeek: Story = {\n render: args => {\n const [value, setValue] = useState<string | undefined>();\n return <DatePicker {...args} type=\"week\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Week\"\n }\n};\n\nexport const TypeYear: Story = {\n render: args => {\n const [value, setValue] = useState<number | undefined>();\n return (\n <DatePicker\n {...args}\n type=\"year\"\n value={value}\n onChange={setValue}\n yearRange={[2015, 2035]}\n />\n );\n },\n args: {\n label: \"Year\"\n }\n};\n\nexport const TypeDateRange: Story = {\n render: args => {\n const [value, setValue] = useState<{ from?: string; to?: string } | undefined>();\n return <DatePicker {...args} type=\"dateRange\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Date Range\"\n }\n};\n\nexport const TypeMultipleDates: Story = {\n render: args => {\n const [value, setValue] = useState<string[]>([]);\n return <DatePicker {...args} type=\"multipleDates\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Multiple Dates\"\n }\n};\n\nexport const TypeMultipleMonths: Story = {\n render: args => {\n const [value, setValue] = useState<string[]>([]);\n return <DatePicker {...args} type=\"multipleMonths\" value={value} onChange={setValue} />;\n },\n args: {\n label: \"Multiple Months\"\n }\n};\n\nexport const TypeMultipleYears: Story = {\n render: args => {\n const [value, setValue] = useState<number[]>([]);\n return (\n <DatePicker\n {...args}\n type=\"multipleYears\"\n value={value}\n onChange={setValue}\n yearRange={[2015, 2035]}\n />\n );\n },\n args: {\n label: \"Multiple Years\"\n }\n};\n\nconst formatValue = (value: any): string => {\n if (value === undefined || value === null) {\n return \"undefined\";\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (Array.isArray(value)) {\n return JSON.stringify(\n value.map((v: any) => (v instanceof Date ? v.toISOString() : v)),\n null,\n 2\n );\n }\n if (typeof value === \"object\") {\n return JSON.stringify(value, (_k, v) => (v instanceof Date ? v.toISOString() : v), 2);\n }\n return String(value);\n};\n\nconst DocumentationInner = (args: any) => {\n const isMultiple = (args.type as string)?.startsWith(\"multiple\");\n const [value, setValue] = useState<any>(isMultiple ? [] : undefined);\n const [validation, setValidation] = useState({ isValid: true, message: \"\" });\n\n const handleChange = (newValue: any) => {\n setValue(newValue);\n const isEmpty =\n newValue === undefined ||\n newValue === null ||\n newValue === \"\" ||\n (Array.isArray(newValue) && newValue.length === 0);\n if (args.required && isEmpty) {\n setValidation({ isValid: false, message: \"This field is required\" });\n } else {\n setValidation({ isValid: true, message: \"\" });\n }\n };\n\n return (\n <div>\n <DatePicker {...args} value={value} onChange={handleChange} validation={validation} />\n <div className=\"mt-md rounded-md border-sm border-neutral-muted bg-neutral-light p-sm\">\n <div className=\"text-xs font-medium text-neutral-dimmed mb-xs\">Current value</div>\n <pre className=\"text-sm text-neutral-strong whitespace-pre-wrap m-0\">\n {formatValue(value)}\n </pre>\n </div>\n </div>\n );\n};\n\nexport const Documentation: Story = {\n render: args => {\n return <DocumentationInner key={args.type as string} {...args} />;\n },\n args: {\n type: \"date\",\n label: \"Event Date\",\n required: true,\n disabled: false,\n description: \"Select the date for your event\",\n note: \"Note: Choose a date that works for all participants\",\n placeholder: \"Pick a date\",\n validation: undefined\n },\n argTypes: {\n type: {\n description: \"The type of date picker variant to render\",\n control: \"select\",\n options: [\n \"date\",\n \"time\",\n \"dateTimeLocal\",\n \"dateTimeTz\",\n \"month\",\n \"week\",\n \"year\",\n \"dateRange\",\n \"multipleDates\",\n \"multipleMonths\",\n \"multipleYears\"\n ],\n defaultValue: \"date\"\n },\n label: {\n description: \"Label text displayed above the picker\",\n control: \"text\"\n },\n required: {\n description: \"Makes the field required\",\n control: \"boolean\",\n defaultValue: true\n },\n disabled: {\n description: \"Disables the date picker\",\n control: \"boolean\",\n defaultValue: false\n },\n description: {\n description: \"Helper text displayed below the label\",\n control: \"text\"\n },\n note: {\n description: \"Additional note displayed below the picker\",\n control: \"text\"\n },\n placeholder: {\n description: \"Placeholder text shown when no date is selected\",\n control: \"text\"\n },\n validation: {\n description:\n \"Object containing validation state and message. Please refer to the example code for details on usage.\"\n },\n onChange: {\n description: \"Callback function when the value changes\"\n }\n }\n};\n"],"names":["meta","DatePicker","args","value","setValue","useState","Default","WithLabel","WithLabelRequired","WithDescription","WithNotes","WithErrors","Disabled","FullExample","WithPresets","presets","Date","addDays","nextSaturday","TypeBirthday","TypeBirthdayWithLimits","today","minDate","maxDate","TypeTime","TypeDateTimeLocal","TypeDateTimeTz","TypeMonth","TypeWeek","TypeYear","TypeDateRange","TypeMultipleDates","TypeMultipleMonths","TypeMultipleYears","formatValue","Array","JSON","v","_k","String","DocumentationInner","isMultiple","undefined","validation","setValidation","handleChange","newValue","isEmpty","Documentation"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { BirthdayPickerProps } from "../utils/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Picks a date one part at a time: a day, then a month, then a year. Each pick moves to the
|
|
5
|
+
* next step. A pick that completes the date commits it, and the last step closes the picker.
|
|
6
|
+
* Meant for dates far in the past, like a date of birth, where paging a calendar month by
|
|
7
|
+
* month is slow.
|
|
8
|
+
*
|
|
9
|
+
* The value is a `YYYY-MM-DD` string, the same as the `date` picker's. Days, months and years
|
|
10
|
+
* that can't make a date between `minDate` and `maxDate` can't be picked.
|
|
11
|
+
*/
|
|
12
|
+
declare const BirthdayPicker: ({ value, onChange, disabled, size, variant, invalid, yearRange, minDate, maxDate, onOpenChange, className }: BirthdayPickerProps) => React.JSX.Element;
|
|
13
|
+
export { BirthdayPicker };
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import react, { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { Popover } from "radix-ui";
|
|
3
|
+
import { ReactComponent } from "@webiny/icons/calendar_month.svg";
|
|
4
|
+
import { ReactComponent as close_svg_ReactComponent } from "@webiny/icons/close.svg";
|
|
5
|
+
import { Icon } from "../../Icon/index.js";
|
|
6
|
+
import { inputVariants } from "../../Input/index.js";
|
|
7
|
+
import { ScrollArea } from "../../ScrollArea/index.js";
|
|
8
|
+
import { useEscapeScrollLock } from "../../hooks/index.js";
|
|
9
|
+
import { cn, cva } from "../../utils.js";
|
|
10
|
+
import { BirthdayPickerCell } from "./BirthdayPickerCell.js";
|
|
11
|
+
import { BirthdayPickerSegments } from "./BirthdayPickerSegments.js";
|
|
12
|
+
import { MONTH_NAMES_FULL } from "../utils/constants.js";
|
|
13
|
+
import { birthdayYears, formatBirthdayValue, isDayDisabled, isMonthDisabled, isYearDisabled, nextBirthdayStep, pad, parseBirthdayValue, pickDay, pickMonth, pickYear } from "../utils/birthdayHelpers.js";
|
|
14
|
+
const STEPS = [
|
|
15
|
+
"day",
|
|
16
|
+
"month",
|
|
17
|
+
"year"
|
|
18
|
+
];
|
|
19
|
+
const STEP_LABELS = {
|
|
20
|
+
day: "Select a day",
|
|
21
|
+
month: "Select a month",
|
|
22
|
+
year: "Select a year"
|
|
23
|
+
};
|
|
24
|
+
const DAYS = Array.from({
|
|
25
|
+
length: 31
|
|
26
|
+
}, (_, index)=>index + 1);
|
|
27
|
+
const MONTHS = Array.from({
|
|
28
|
+
length: 12
|
|
29
|
+
}, (_, index)=>index + 1);
|
|
30
|
+
const EXPAND_MS = 250;
|
|
31
|
+
const SLIDE_MS = 300;
|
|
32
|
+
const FADE_OUT_MS = 120;
|
|
33
|
+
const COLLAPSE_MS = 200;
|
|
34
|
+
const cardVariants = cva([
|
|
35
|
+
"bg-neutral-base border-sm border-neutral-black shadow-lg overflow-hidden",
|
|
36
|
+
"text-md text-neutral-strong fill-neutral-xstrong",
|
|
37
|
+
"w-(--radix-popover-trigger-width) min-w-[320px]"
|
|
38
|
+
], {
|
|
39
|
+
variants: {
|
|
40
|
+
size: {
|
|
41
|
+
md: "rounded-md",
|
|
42
|
+
lg: "rounded-md",
|
|
43
|
+
xl: "rounded-lg leading-6"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
defaultVariants: {
|
|
47
|
+
size: "lg"
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const cardHeaderVariants = cva("flex items-center justify-between gap-sm", {
|
|
51
|
+
variants: {
|
|
52
|
+
size: {
|
|
53
|
+
md: "px-[calc(var(--padding-sm-extra)-(var(--border-width-sm)))]",
|
|
54
|
+
lg: "px-[calc(var(--padding-sm-extra)-(var(--border-width-sm)))]",
|
|
55
|
+
xl: "px-[calc(var(--padding-md)-(var(--border-width-sm)))]"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
defaultVariants: {
|
|
59
|
+
size: "lg"
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
const gridHeight = "h-[calc(6*var(--spacing-xl)+5*var(--spacing-sm)+2*var(--spacing-xs))]";
|
|
63
|
+
const dayGridWidth = "max-w-[calc(6*var(--spacing-xxl)+5*var(--spacing-xs))]";
|
|
64
|
+
const wideGridWidth = "max-w-[calc(5*var(--spacing-3xl)+4*var(--spacing-sm))]";
|
|
65
|
+
const BirthdayPicker = ({ value, onChange, disabled, size, variant, invalid, yearRange, minDate, maxDate, onOpenChange, className })=>{
|
|
66
|
+
const [open, setOpen] = useState(false);
|
|
67
|
+
const [phase, setPhase] = useState("collapsed");
|
|
68
|
+
const [step, setStep] = useState("day");
|
|
69
|
+
const [draft, setDraft] = useState({});
|
|
70
|
+
const [barHeight, setBarHeight] = useState(0);
|
|
71
|
+
const [bodyHeight, setBodyHeight] = useState(0);
|
|
72
|
+
const barRef = useRef(null);
|
|
73
|
+
const cardRef = useRef(null);
|
|
74
|
+
const bodyRef = useRef(null);
|
|
75
|
+
const panelRefs = useRef({
|
|
76
|
+
day: null,
|
|
77
|
+
month: null,
|
|
78
|
+
year: null
|
|
79
|
+
});
|
|
80
|
+
const picked = useRef(new Set());
|
|
81
|
+
const closeTimer = useRef();
|
|
82
|
+
const escapeScrollLock = useEscapeScrollLock();
|
|
83
|
+
const saved = useMemo(()=>parseBirthdayValue(value), [
|
|
84
|
+
value
|
|
85
|
+
]);
|
|
86
|
+
const years = useMemo(()=>birthdayYears(yearRange, saved.year, {
|
|
87
|
+
minDate,
|
|
88
|
+
maxDate
|
|
89
|
+
}), [
|
|
90
|
+
yearRange,
|
|
91
|
+
saved.year,
|
|
92
|
+
minDate,
|
|
93
|
+
maxDate
|
|
94
|
+
]);
|
|
95
|
+
const range = useMemo(()=>({
|
|
96
|
+
years,
|
|
97
|
+
minDate,
|
|
98
|
+
maxDate
|
|
99
|
+
}), [
|
|
100
|
+
years,
|
|
101
|
+
minDate,
|
|
102
|
+
maxDate
|
|
103
|
+
]);
|
|
104
|
+
useEffect(()=>()=>clearTimeout(closeTimer.current), []);
|
|
105
|
+
const expand = ()=>{
|
|
106
|
+
if (disabled || open) return;
|
|
107
|
+
clearTimeout(closeTimer.current);
|
|
108
|
+
setBarHeight(barRef.current?.offsetHeight ?? 0);
|
|
109
|
+
setDraft(saved);
|
|
110
|
+
picked.current = new Set();
|
|
111
|
+
setStep("day");
|
|
112
|
+
setPhase("collapsed");
|
|
113
|
+
setOpen(true);
|
|
114
|
+
onOpenChange?.(true);
|
|
115
|
+
};
|
|
116
|
+
const collapse = ()=>{
|
|
117
|
+
if ("collapsing" === phase) return;
|
|
118
|
+
setPhase("collapsing");
|
|
119
|
+
closeTimer.current = setTimeout(()=>{
|
|
120
|
+
setOpen(false);
|
|
121
|
+
setPhase("collapsed");
|
|
122
|
+
onOpenChange?.(false);
|
|
123
|
+
}, FADE_OUT_MS + COLLAPSE_MS);
|
|
124
|
+
};
|
|
125
|
+
useLayoutEffect(()=>{
|
|
126
|
+
if (!open || "collapsed" !== phase) return;
|
|
127
|
+
let second = 0;
|
|
128
|
+
const first = requestAnimationFrame(()=>{
|
|
129
|
+
second = requestAnimationFrame(()=>{
|
|
130
|
+
setBodyHeight(bodyRef.current?.offsetHeight ?? 0);
|
|
131
|
+
setPhase("expanded");
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
return ()=>{
|
|
135
|
+
cancelAnimationFrame(first);
|
|
136
|
+
cancelAnimationFrame(second);
|
|
137
|
+
};
|
|
138
|
+
}, [
|
|
139
|
+
open,
|
|
140
|
+
phase
|
|
141
|
+
]);
|
|
142
|
+
useEffect(()=>{
|
|
143
|
+
if ("expanded" !== phase) return;
|
|
144
|
+
const timer = setTimeout(()=>{
|
|
145
|
+
const rect = cardRef.current?.getBoundingClientRect();
|
|
146
|
+
if (rect && rect.bottom > window.innerHeight) barRef.current?.scrollIntoView({
|
|
147
|
+
block: "center",
|
|
148
|
+
behavior: "smooth"
|
|
149
|
+
});
|
|
150
|
+
}, EXPAND_MS);
|
|
151
|
+
return ()=>clearTimeout(timer);
|
|
152
|
+
}, [
|
|
153
|
+
phase
|
|
154
|
+
]);
|
|
155
|
+
useEffect(()=>{
|
|
156
|
+
if (!open || "expanded" !== phase) return;
|
|
157
|
+
const panel = panelRefs.current[step];
|
|
158
|
+
if (!panel) return;
|
|
159
|
+
const selected = panel.querySelector("[data-selected=true]");
|
|
160
|
+
const viewport = panel.querySelector("[data-slot=scroll-area-viewport]");
|
|
161
|
+
if (viewport) if (selected) {
|
|
162
|
+
const offset = selected.getBoundingClientRect().top - viewport.getBoundingClientRect().top;
|
|
163
|
+
viewport.scrollTop += offset - viewport.clientHeight / 2 + selected.offsetHeight / 2;
|
|
164
|
+
} else viewport.scrollTop = 0;
|
|
165
|
+
const target = selected ?? panel.querySelector("button:enabled");
|
|
166
|
+
target?.focus({
|
|
167
|
+
preventScroll: true
|
|
168
|
+
});
|
|
169
|
+
}, [
|
|
170
|
+
open,
|
|
171
|
+
phase,
|
|
172
|
+
step
|
|
173
|
+
]);
|
|
174
|
+
const goToStep = (next)=>{
|
|
175
|
+
setStep(next);
|
|
176
|
+
};
|
|
177
|
+
const onPick = (pickedStep, next)=>{
|
|
178
|
+
setDraft(next);
|
|
179
|
+
picked.current.add(pickedStep);
|
|
180
|
+
const nextValue = formatBirthdayValue(next);
|
|
181
|
+
if (nextValue) onChange?.(nextValue);
|
|
182
|
+
const nextStep = nextBirthdayStep(next, pickedStep, picked.current);
|
|
183
|
+
if (nextStep) return void goToStep(nextStep);
|
|
184
|
+
collapse();
|
|
185
|
+
};
|
|
186
|
+
const onPickDay = (day)=>onPick("day", pickDay(draft, day, range));
|
|
187
|
+
const onPickMonth = (month)=>onPick("month", pickMonth(draft, month, range));
|
|
188
|
+
const onPickYear = (year)=>onPick("year", pickYear(draft, year));
|
|
189
|
+
const onBarKeyDown = (event)=>{
|
|
190
|
+
if (event.target !== event.currentTarget) return;
|
|
191
|
+
if ("Enter" === event.key || " " === event.key || "ArrowDown" === event.key) {
|
|
192
|
+
event.preventDefault();
|
|
193
|
+
expand();
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
const expanded = "expanded" === phase;
|
|
197
|
+
const cardHeight = expanded ? barHeight + bodyHeight : barHeight;
|
|
198
|
+
const stepIndex = STEPS.indexOf(step);
|
|
199
|
+
const icons = (interactive)=>/*#__PURE__*/ react.createElement("span", {
|
|
200
|
+
className: "flex items-center gap-xs shrink-0"
|
|
201
|
+
}, interactive && value && !disabled ? /*#__PURE__*/ react.createElement("button", {
|
|
202
|
+
type: "button",
|
|
203
|
+
"aria-label": "Clear date",
|
|
204
|
+
className: "flex cursor-pointer rounded-xs outline-none focus-visible:ring-sm focus-visible:ring-primary-dimmed",
|
|
205
|
+
onClick: (event)=>{
|
|
206
|
+
event.stopPropagation();
|
|
207
|
+
onChange?.(void 0);
|
|
208
|
+
},
|
|
209
|
+
onKeyDown: (event)=>event.stopPropagation()
|
|
210
|
+
}, /*#__PURE__*/ react.createElement(Icon, {
|
|
211
|
+
icon: /*#__PURE__*/ react.createElement(close_svg_ReactComponent, null),
|
|
212
|
+
label: "Clear",
|
|
213
|
+
size: "sm",
|
|
214
|
+
color: "inherit"
|
|
215
|
+
})) : null, /*#__PURE__*/ react.createElement(Icon, {
|
|
216
|
+
icon: /*#__PURE__*/ react.createElement(ReactComponent, null),
|
|
217
|
+
label: "Calendar",
|
|
218
|
+
size: "sm",
|
|
219
|
+
color: "inherit"
|
|
220
|
+
}));
|
|
221
|
+
const panelProps = (panelStep)=>({
|
|
222
|
+
ref: (element)=>{
|
|
223
|
+
panelRefs.current[panelStep] = element;
|
|
224
|
+
},
|
|
225
|
+
"aria-hidden": step !== panelStep,
|
|
226
|
+
"data-step": panelStep
|
|
227
|
+
});
|
|
228
|
+
return /*#__PURE__*/ react.createElement("div", {
|
|
229
|
+
className: className
|
|
230
|
+
}, /*#__PURE__*/ react.createElement(Popover.Root, {
|
|
231
|
+
open: open,
|
|
232
|
+
onOpenChange: (isOpen)=>isOpen ? expand() : collapse()
|
|
233
|
+
}, /*#__PURE__*/ react.createElement(Popover.Anchor, {
|
|
234
|
+
asChild: true
|
|
235
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
236
|
+
ref: barRef,
|
|
237
|
+
role: "button",
|
|
238
|
+
tabIndex: disabled ? -1 : 0,
|
|
239
|
+
"aria-haspopup": "dialog",
|
|
240
|
+
"aria-expanded": open,
|
|
241
|
+
"data-disabled": disabled,
|
|
242
|
+
"data-focused": open,
|
|
243
|
+
onClick: expand,
|
|
244
|
+
onKeyDown: onBarKeyDown,
|
|
245
|
+
className: cn(inputVariants({
|
|
246
|
+
size,
|
|
247
|
+
variant,
|
|
248
|
+
invalid
|
|
249
|
+
}), "cursor-pointer justify-between select-none min-w-0 overflow-hidden outline-none", disabled && "pointer-events-none")
|
|
250
|
+
}, /*#__PURE__*/ react.createElement(BirthdayPickerSegments, {
|
|
251
|
+
parts: saved
|
|
252
|
+
}), icons(true))), /*#__PURE__*/ react.createElement(Popover.Portal, null, /*#__PURE__*/ react.createElement(Popover.Content, {
|
|
253
|
+
ref: escapeScrollLock,
|
|
254
|
+
side: "bottom",
|
|
255
|
+
align: "start",
|
|
256
|
+
sideOffset: -barHeight,
|
|
257
|
+
avoidCollisions: false,
|
|
258
|
+
onOpenAutoFocus: (event)=>event.preventDefault(),
|
|
259
|
+
onCloseAutoFocus: (event)=>{
|
|
260
|
+
event.preventDefault();
|
|
261
|
+
barRef.current?.focus({
|
|
262
|
+
preventScroll: true
|
|
263
|
+
});
|
|
264
|
+
},
|
|
265
|
+
className: "z-popover outline-none"
|
|
266
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
267
|
+
ref: cardRef,
|
|
268
|
+
role: "dialog",
|
|
269
|
+
"aria-label": STEP_LABELS[step],
|
|
270
|
+
"data-phase": phase,
|
|
271
|
+
className: cn(cardVariants({
|
|
272
|
+
size
|
|
273
|
+
}), "collapsing" === phase && "pointer-events-none"),
|
|
274
|
+
style: {
|
|
275
|
+
height: cardHeight,
|
|
276
|
+
transition: "collapsing" === phase ? `height ${COLLAPSE_MS}ms ease-in ${FADE_OUT_MS}ms` : `height ${EXPAND_MS}ms ease-out`
|
|
277
|
+
}
|
|
278
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
279
|
+
className: cardHeaderVariants({
|
|
280
|
+
size
|
|
281
|
+
}),
|
|
282
|
+
style: {
|
|
283
|
+
height: `calc(${barHeight}px - 2 * var(--border-width-sm))`
|
|
284
|
+
}
|
|
285
|
+
}, /*#__PURE__*/ react.createElement(BirthdayPickerSegments, {
|
|
286
|
+
parts: draft,
|
|
287
|
+
activeStep: step,
|
|
288
|
+
onStepClick: goToStep
|
|
289
|
+
}), icons(false)), /*#__PURE__*/ react.createElement("div", {
|
|
290
|
+
ref: bodyRef
|
|
291
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
292
|
+
className: "h-px bg-neutral-dimmed origin-center",
|
|
293
|
+
style: {
|
|
294
|
+
transform: expanded ? "scaleX(1)" : "scaleX(0)",
|
|
295
|
+
transition: `transform ${EXPAND_MS}ms ease-out`
|
|
296
|
+
}
|
|
297
|
+
}), /*#__PURE__*/ react.createElement("div", {
|
|
298
|
+
className: "pb-md",
|
|
299
|
+
style: {
|
|
300
|
+
opacity: expanded ? 1 : 0,
|
|
301
|
+
transition: expanded ? `opacity 200ms ease-out ${EXPAND_MS - 100}ms` : `opacity ${FADE_OUT_MS}ms ease-in`
|
|
302
|
+
}
|
|
303
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
304
|
+
className: "pt-sm-extra pb-xs text-center text-sm text-neutral-muted",
|
|
305
|
+
"aria-live": "polite"
|
|
306
|
+
}, STEP_LABELS[step]), /*#__PURE__*/ react.createElement("div", {
|
|
307
|
+
className: cn("overflow-hidden", gridHeight)
|
|
308
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
309
|
+
className: "flex h-full w-[300%]",
|
|
310
|
+
style: {
|
|
311
|
+
transform: `translateX(-${100 * stepIndex / 3}%)`,
|
|
312
|
+
transition: `transform ${SLIDE_MS}ms ease-in-out`
|
|
313
|
+
}
|
|
314
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
315
|
+
...panelProps("day"),
|
|
316
|
+
className: "w-1/3 h-full px-md py-xs"
|
|
317
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
318
|
+
className: cn("grid grid-cols-6 gap-x-xs gap-y-sm mx-auto", dayGridWidth)
|
|
319
|
+
}, DAYS.map((day)=>/*#__PURE__*/ react.createElement(BirthdayPickerCell, {
|
|
320
|
+
key: day,
|
|
321
|
+
selected: draft.day === day,
|
|
322
|
+
disabled: isDayDisabled(day, range),
|
|
323
|
+
tabbable: "day" === step,
|
|
324
|
+
onClick: ()=>onPickDay(day)
|
|
325
|
+
}, pad(day))))), /*#__PURE__*/ react.createElement("div", {
|
|
326
|
+
...panelProps("month"),
|
|
327
|
+
className: "w-1/3 h-full px-md py-xs flex items-center"
|
|
328
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
329
|
+
className: cn("grid grid-cols-3 gap-x-sm gap-y-md w-full mx-auto", wideGridWidth)
|
|
330
|
+
}, MONTHS.map((month)=>/*#__PURE__*/ react.createElement(BirthdayPickerCell, {
|
|
331
|
+
key: month,
|
|
332
|
+
selected: draft.month === month,
|
|
333
|
+
disabled: isMonthDisabled(month, draft, range),
|
|
334
|
+
tabbable: "month" === step,
|
|
335
|
+
onClick: ()=>onPickMonth(month)
|
|
336
|
+
}, MONTH_NAMES_FULL[month - 1])))), /*#__PURE__*/ react.createElement("div", {
|
|
337
|
+
...panelProps("year"),
|
|
338
|
+
className: "w-1/3 h-full"
|
|
339
|
+
}, /*#__PURE__*/ react.createElement(ScrollArea, {
|
|
340
|
+
className: "h-full"
|
|
341
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
342
|
+
className: "px-md py-xs"
|
|
343
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
344
|
+
className: cn("grid grid-cols-5 gap-sm w-full mx-auto", wideGridWidth)
|
|
345
|
+
}, years.map((year)=>/*#__PURE__*/ react.createElement(BirthdayPickerCell, {
|
|
346
|
+
key: year,
|
|
347
|
+
selected: draft.year === year,
|
|
348
|
+
disabled: isYearDisabled(year, draft, range),
|
|
349
|
+
tabbable: "year" === step,
|
|
350
|
+
onClick: ()=>onPickYear(year)
|
|
351
|
+
}, year)))))))))))))));
|
|
352
|
+
};
|
|
353
|
+
export { BirthdayPicker };
|
|
354
|
+
|
|
355
|
+
//# sourceMappingURL=BirthdayPicker.js.map
|