@a-type/ui 3.0.31 → 3.0.33

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.
Files changed (49) hide show
  1. package/dist/cjs/components/datePicker/Calendar.d.ts +11 -0
  2. package/dist/cjs/components/datePicker/Calendar.js +37 -0
  3. package/dist/cjs/components/datePicker/Calendar.js.map +1 -0
  4. package/dist/cjs/components/datePicker/DatePicker.d.ts +42 -13
  5. package/dist/cjs/components/datePicker/DatePicker.js +31 -71
  6. package/dist/cjs/components/datePicker/DatePicker.js.map +1 -1
  7. package/dist/cjs/components/datePicker/DatePicker.stories.d.ts +36 -1
  8. package/dist/cjs/components/datePicker/DatePicker.stories.js +18 -2
  9. package/dist/cjs/components/datePicker/DatePicker.stories.js.map +1 -1
  10. package/dist/cjs/components/datePicker/DateRangePicker.d.ts +55 -0
  11. package/dist/cjs/components/datePicker/DateRangePicker.js +89 -0
  12. package/dist/cjs/components/datePicker/DateRangePicker.js.map +1 -0
  13. package/dist/cjs/components/datePicker/index.d.ts +2 -0
  14. package/dist/cjs/components/datePicker/index.js +1 -0
  15. package/dist/cjs/components/datePicker/index.js.map +1 -1
  16. package/dist/cjs/components/forms/EmojiField.js +5 -1
  17. package/dist/cjs/components/forms/EmojiField.js.map +1 -1
  18. package/dist/cjs/uno/preflights/fonts.d.ts +2 -2
  19. package/dist/cjs/uno/preflights/fonts.js +1 -3
  20. package/dist/cjs/uno/preflights/fonts.js.map +1 -1
  21. package/dist/css/main.css +1 -1
  22. package/dist/esm/components/datePicker/Calendar.d.ts +11 -0
  23. package/dist/esm/components/datePicker/Calendar.js +32 -0
  24. package/dist/esm/components/datePicker/Calendar.js.map +1 -0
  25. package/dist/esm/components/datePicker/DatePicker.d.ts +42 -13
  26. package/dist/esm/components/datePicker/DatePicker.js +32 -68
  27. package/dist/esm/components/datePicker/DatePicker.js.map +1 -1
  28. package/dist/esm/components/datePicker/DatePicker.stories.d.ts +36 -1
  29. package/dist/esm/components/datePicker/DatePicker.stories.js +19 -3
  30. package/dist/esm/components/datePicker/DatePicker.stories.js.map +1 -1
  31. package/dist/esm/components/datePicker/DateRangePicker.d.ts +55 -0
  32. package/dist/esm/components/datePicker/DateRangePicker.js +83 -0
  33. package/dist/esm/components/datePicker/DateRangePicker.js.map +1 -0
  34. package/dist/esm/components/datePicker/index.d.ts +2 -0
  35. package/dist/esm/components/datePicker/index.js +1 -0
  36. package/dist/esm/components/datePicker/index.js.map +1 -1
  37. package/dist/esm/components/forms/EmojiField.js +5 -1
  38. package/dist/esm/components/forms/EmojiField.js.map +1 -1
  39. package/dist/esm/uno/preflights/fonts.d.ts +2 -2
  40. package/dist/esm/uno/preflights/fonts.js +1 -3
  41. package/dist/esm/uno/preflights/fonts.js.map +1 -1
  42. package/package.json +1 -1
  43. package/src/components/datePicker/Calendar.tsx +83 -0
  44. package/src/components/datePicker/DatePicker.stories.tsx +37 -2
  45. package/src/components/datePicker/DatePicker.tsx +77 -222
  46. package/src/components/datePicker/DateRangePicker.tsx +161 -0
  47. package/src/components/datePicker/index.ts +2 -0
  48. package/src/components/forms/EmojiField.tsx +5 -1
  49. package/src/uno/preflights/fonts.ts +4 -7
@@ -13,6 +13,7 @@ var __rest = (this && this.__rest) || function (s, e) {
13
13
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
14
  import clsx from 'clsx';
15
15
  import { useField } from 'formik';
16
+ import { useState } from 'react';
16
17
  import { useIdOrGenerated } from '../../hooks/useIdOrGenerated.js';
17
18
  import { Box } from '../box/Box.js';
18
19
  import { Button } from '../button/Button.js';
@@ -24,12 +25,15 @@ export function EmojiField(_a) {
24
25
  var { name, label, className, required, id: providedId } = _a, rest = __rest(_a, ["name", "label", "className", "required", "id"]);
25
26
  const [props, _, tools] = useField({ name });
26
27
  const id = useIdOrGenerated(providedId);
27
- return (_jsxs(Box, { gap: "sm", className: className, children: [_jsxs(Popover, { children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx(Button, Object.assign({ id: id, "aria-label": "Select emoji", size: "small", className: clsx('p-0 transition-color', className) }, rest, { children: _jsx(Button.Icon, { className: "text-[19px] w-touch h-touch flex items-center justify-center", children: props.value || _jsx(Icon, { name: "smile" }) }) })) }), _jsx(Popover.Content, { children: _jsx(EmojiPicker, { onValueChange: (v) => {
28
+ const [open, setOpen] = useState(false);
29
+ return (_jsxs(Box, { gap: "sm", className: className, children: [_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx(Button, Object.assign({ id: id, "aria-label": "Select emoji", size: "small", className: clsx('p-0 transition-color', className) }, rest, { children: _jsx(Button.Icon, { className: "text-[19px] w-touch h-touch flex items-center justify-center", children: props.value || _jsx(Icon, { name: "smile" }) }) })) }), _jsx(Popover.Content, { children: _jsx(EmojiPicker, { onValueChange: (v) => {
28
30
  tools.setValue(v);
31
+ setOpen(false);
29
32
  }, onClear: required
30
33
  ? undefined
31
34
  : () => {
32
35
  tools.setValue('');
36
+ setOpen(false);
33
37
  }, id: id }) })] }), label && (_jsx(HorizontalFieldLabel, { htmlFor: id, children: label }))] }));
34
38
  }
35
39
  //# sourceMappingURL=EmojiField.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"EmojiField.js","sourceRoot":"","sources":["../../../../src/components/forms/EmojiField.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAQvD,MAAM,UAAU,UAAU,CAAC,EAOT;QAPS,EAC1B,IAAI,EACJ,KAAK,EACL,SAAS,EACT,QAAQ,EACR,EAAE,EAAE,UAAU,OAEG,EADd,IAAI,cANmB,gDAO1B,CADO;IAEP,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,GAAG,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,MAAM,EAAE,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IACxC,OAAO,CACN,MAAC,GAAG,IAAC,GAAG,EAAC,IAAI,EAAC,SAAS,EAAE,SAAS,aACjC,MAAC,OAAO,eACP,KAAC,OAAO,CAAC,OAAO,IAAC,OAAO,kBACvB,KAAC,MAAM,kBACN,EAAE,EAAE,EAAE,gBACK,cAAc,EACzB,IAAI,EAAC,OAAO,EACZ,SAAS,EAAE,IAAI,CAAC,sBAAsB,EAAE,SAAS,CAAC,IAC9C,IAAI,cAER,KAAC,MAAM,CAAC,IAAI,IAAC,SAAS,EAAC,8DAA8D,YACnF,KAAK,CAAC,KAAK,IAAI,KAAC,IAAI,IAAC,IAAI,EAAC,OAAO,GAAG,GACxB,IACN,GACQ,EAClB,KAAC,OAAO,CAAC,OAAO,cACf,KAAC,WAAW,IACX,aAAa,EAAE,CAAC,CAAC,EAAE,EAAE;gCACpB,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;4BACnB,CAAC,EACD,OAAO,EACN,QAAQ;gCACP,CAAC,CAAC,SAAS;gCACX,CAAC,CAAC,GAAG,EAAE;oCACL,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;gCACnB,CAAC,EAEL,EAAE,EAAE,EAAE,GACL,GACe,IACT,EACT,KAAK,IAAI,CACT,KAAC,oBAAoB,IAAC,OAAO,EAAE,EAAE,YAAG,KAAK,GAAwB,CACjE,IACI,CACN,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"EmojiField.js","sourceRoot":"","sources":["../../../../src/components/forms/EmojiField.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAQvD,MAAM,UAAU,UAAU,CAAC,EAOT;QAPS,EAC1B,IAAI,EACJ,KAAK,EACL,SAAS,EACT,QAAQ,EACR,EAAE,EAAE,UAAU,OAEG,EADd,IAAI,cANmB,gDAO1B,CADO;IAEP,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,GAAG,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,MAAM,EAAE,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IACxC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxC,OAAO,CACN,MAAC,GAAG,IAAC,GAAG,EAAC,IAAI,EAAC,SAAS,EAAE,SAAS,aACjC,MAAC,OAAO,IAAC,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,aACzC,KAAC,OAAO,CAAC,OAAO,IAAC,OAAO,kBACvB,KAAC,MAAM,kBACN,EAAE,EAAE,EAAE,gBACK,cAAc,EACzB,IAAI,EAAC,OAAO,EACZ,SAAS,EAAE,IAAI,CAAC,sBAAsB,EAAE,SAAS,CAAC,IAC9C,IAAI,cAER,KAAC,MAAM,CAAC,IAAI,IAAC,SAAS,EAAC,8DAA8D,YACnF,KAAK,CAAC,KAAK,IAAI,KAAC,IAAI,IAAC,IAAI,EAAC,OAAO,GAAG,GACxB,IACN,GACQ,EAClB,KAAC,OAAO,CAAC,OAAO,cACf,KAAC,WAAW,IACX,aAAa,EAAE,CAAC,CAAC,EAAE,EAAE;gCACpB,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gCAClB,OAAO,CAAC,KAAK,CAAC,CAAC;4BAChB,CAAC,EACD,OAAO,EACN,QAAQ;gCACP,CAAC,CAAC,SAAS;gCACX,CAAC,CAAC,GAAG,EAAE;oCACL,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;oCACnB,OAAO,CAAC,KAAK,CAAC,CAAC;gCACf,CAAC,EAEL,EAAE,EAAE,EAAE,GACL,GACe,IACT,EACT,KAAK,IAAI,CACT,KAAC,oBAAoB,IAAC,OAAO,EAAE,EAAE,YAAG,KAAK,GAAwB,CACjE,IACI,CACN,CAAC;AACH,CAAC"}
@@ -1,4 +1,4 @@
1
1
  export interface FontsPreflightOptions {
2
- interFontLocation: string;
2
+ interFontLocation?: string;
3
3
  }
4
- export declare const fontsPreflight: ({ interFontLocation }?: FontsPreflightOptions) => import("unocss").Preflight<object>;
4
+ export declare const fontsPreflight: ({ interFontLocation, }?: FontsPreflightOptions) => import("unocss").Preflight<object>;
@@ -1,8 +1,6 @@
1
1
  // @unocss-include
2
2
  import { preflight } from './_util.js';
3
- export const fontsPreflight = ({ interFontLocation } = {
4
- interFontLocation: 'https://resources.biscuits.club/fonts/Inter-VariableFont_slnt,wght.ttf',
5
- }) => preflight({
3
+ export const fontsPreflight = ({ interFontLocation = 'https://resources.biscuits.club/fonts/Inter-VariableFont_slnt,wght.ttf', } = {}) => preflight({
6
4
  getCSS: () => `
7
5
  @font-face {
8
6
  font-family: "Inter";
@@ -1 +1 @@
1
- {"version":3,"file":"fonts.js","sourceRoot":"","sources":["../../../../src/uno/preflights/fonts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAMvC,MAAM,CAAC,MAAM,cAAc,GAAG,CAC7B,EAAE,iBAAiB,KAA4B;IAC9C,iBAAiB,EAChB,wEAAwE;CACzE,EACA,EAAE,CACH,SAAS,CAAC;IACT,MAAM,EAAE,GAAG,EAAE,CAAC;;;eAGD,iBAAiB;;;;;EAK9B;CACA,CAAC,CAAC"}
1
+ {"version":3,"file":"fonts.js","sourceRoot":"","sources":["../../../../src/uno/preflights/fonts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAMvC,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAC9B,iBAAiB,GAAG,wEAAwE,MAClE,EAAE,EAAE,EAAE,CAChC,SAAS,CAAC;IACT,MAAM,EAAE,GAAG,EAAE,CAAC;;;eAGD,iBAAiB;;;;;EAK9B;CACA,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a-type/ui",
3
- "version": "3.0.31",
3
+ "version": "3.0.33",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "url": "https://github.com/a-type/ui"
@@ -0,0 +1,83 @@
1
+ import {
2
+ CalendarDay as BaseCalendarDay,
3
+ CalendarDays,
4
+ CalendarDaysProps,
5
+ } from 'calendar-blocks';
6
+ import { withClassName } from '../../hooks.js';
7
+ import { Button } from '../button/index.js';
8
+
9
+ export const MonthRow = withClassName(
10
+ 'div',
11
+ 'flex flex-row justify-between items-center w-full',
12
+ );
13
+
14
+ export const MonthLabel = withClassName(
15
+ 'span',
16
+ 'text-sm font-bold min-w-0 overflow-hidden text-center text-ellipsis',
17
+ 'self-center',
18
+ );
19
+
20
+ export const MonthButton = withClassName(Button, 'self-center');
21
+
22
+ export const CalendarGridRoot = withClassName(
23
+ 'div',
24
+ 'grid grid-cols-[repeat(7,var(--day-size,32px))] [grid-auto-rows:var(--day-size,32px)]',
25
+ 'height-[calc(var(--day-size,32px)*7)] rounded overflow-hidden p-2',
26
+ );
27
+
28
+ export const DayLabel = withClassName(
29
+ 'div',
30
+ 'flex items-center justify-center text-sm color-gray-dark',
31
+ );
32
+
33
+ export const DayLabels = () => (
34
+ <>
35
+ <DayLabel>S</DayLabel>
36
+ <DayLabel>M</DayLabel>
37
+ <DayLabel>T</DayLabel>
38
+ <DayLabel>W</DayLabel>
39
+ <DayLabel>T</DayLabel>
40
+ <DayLabel>F</DayLabel>
41
+ <DayLabel>S</DayLabel>
42
+ </>
43
+ );
44
+
45
+ export function CalendarGrid({
46
+ className,
47
+ ...props
48
+ }: CalendarDaysProps & { className?: string }) {
49
+ return (
50
+ <CalendarGridRoot className={className}>
51
+ <DayLabels />
52
+ <CalendarDays {...props} />
53
+ </CalendarGridRoot>
54
+ );
55
+ }
56
+
57
+ export const CalendarDay = withClassName(
58
+ BaseCalendarDay,
59
+ 'border border-solid border-transparent bg-white mr--1px mb--1px relative color-black',
60
+ 'flex items-center justify-center transition cursor-pointer',
61
+ '[&[data-highlighted]]:(z-1 ring-2 ring-accent)',
62
+ 'hover:(z-1 ring-2 ring-accent)',
63
+ 'active:(bg-main-light rounded)',
64
+ '[&[data-selected]]:(bg-main z-2 rounded)',
65
+ '[&[data-in-range]]:(bg-main-light rounded-none z-1)',
66
+ '[&[data-range-start]]:(bg-main rounded-l rounded-r-none z-1)',
67
+ '[&[data-range-end]]:(bg-main rounded-r rounded-l-none z-1)',
68
+ 'disabled:(opacity-50 cursor-default)',
69
+ // today dot
70
+ "[&[data-today]]:before:(content-[''] absolute left-[1px] top-[1px] w-[6px] h-[6px] rounded-lg bg-attention border-1 border-solid border-black)",
71
+ // calendar edges
72
+ '[&[data-top-edge]]:(border-t-gray)',
73
+ '[&[data-bottom-edge]]:(border-b-gray)',
74
+ '[&[data-first-column]]:(border-l-gray)',
75
+ '[&[data-last-column]]:(border-r-gray)',
76
+ '[&[data-day-first]]:(border-l-gray rounded-tl)',
77
+ '[&[data-day-last]]:(border-r-gray rounded-br)',
78
+ '[&[data-first-column][data-bottom-edge]]:rounded-bl',
79
+ '[&[data-last-column][data-bottom-edge]]:rounded-br',
80
+ '[&[data-first-column][data-top-edge]]:rounded-tl',
81
+ '[&[data-last-column][data-top-edge]]:rounded-tr',
82
+ '[&[data-different-month]]:[visibility:hidden]',
83
+ );
@@ -1,6 +1,9 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
- import { useState } from 'react';
3
- import { DatePicker, DateRangePicker } from './DatePicker.js';
2
+ import { CalendarDayValue } from 'calendar-blocks';
3
+ import { useEffect, useState } from 'react';
4
+ import { Spinner } from '../spinner/Spinner.js';
5
+ import { DatePicker } from './DatePicker.js';
6
+ import { DateRangePicker } from './DateRangePicker.js';
4
7
 
5
8
  const meta = {
6
9
  title: 'Components/DatePicker',
@@ -31,3 +34,35 @@ export const Range: Story = {
31
34
  return <DateRangePicker value={value} onChange={setValue} />;
32
35
  },
33
36
  };
37
+
38
+ export const CustomComposition: Story = {
39
+ render() {
40
+ const [value, setValue] = useState<Date | null>(null);
41
+
42
+ return (
43
+ <DatePicker.Root value={value} onChange={setValue}>
44
+ <DatePicker.MonthControls />
45
+ <DatePicker.CalendarGrid>
46
+ {(value) => <FakeLoadingDay value={value} key={value.key} />}
47
+ </DatePicker.CalendarGrid>
48
+ </DatePicker.Root>
49
+ );
50
+ },
51
+ };
52
+
53
+ function FakeLoadingDay({ value }: { value: CalendarDayValue }) {
54
+ const [loading, setLoading] = useState(true);
55
+ useEffect(() => {
56
+ const timeout = setTimeout(
57
+ () => setLoading(false),
58
+ Math.random() * 2000 + 500,
59
+ );
60
+ return () => clearTimeout(timeout);
61
+ }, []);
62
+
63
+ return (
64
+ <DatePicker.CalendarDay value={value}>
65
+ {loading ? <Spinner size={10} /> : value.date.getDate()}
66
+ </DatePicker.CalendarDay>
67
+ );
68
+ }
@@ -1,72 +1,76 @@
1
- import {
2
- CalendarDay as BaseCalendarDay,
3
- Calendar,
4
- CalendarDays,
5
- } from 'calendar-blocks';
6
- import classNames from 'clsx';
7
- import { useCallback, useState } from 'react';
8
- import { withClassName } from '../../hooks.js';
1
+ import { Calendar, CalendarDays, useCalendarContext } from 'calendar-blocks';
2
+ import { clsx } from 'clsx';
3
+ import { ReactNode, useState } from 'react';
9
4
  import { PaletteName } from '../../uno/index.js';
10
- import { Button } from '../button/index.js';
11
5
  import { Icon } from '../icon/index.js';
6
+ import {
7
+ CalendarDay,
8
+ CalendarGrid,
9
+ DayLabels,
10
+ MonthButton,
11
+ MonthLabel,
12
+ MonthRow,
13
+ } from './Calendar.js';
12
14
 
13
- export interface DatePickerProps {
14
- value: Date | null;
15
- onChange: (date: Date | null) => void;
16
- className?: string;
17
- color?: PaletteName;
15
+ function DatePickerMonthControls({}: {}) {
16
+ const { setDisplayInfo, month, year } = useCalendarContext();
17
+ const monthLabel = new Date(year, month).toLocaleDateString('en-US', {
18
+ month: 'long',
19
+ year: 'numeric',
20
+ });
21
+
22
+ return (
23
+ <MonthRow>
24
+ <MonthButton
25
+ emphasis="ghost"
26
+ onClick={() =>
27
+ setDisplayInfo({
28
+ month: month - 1,
29
+ year: year,
30
+ })
31
+ }
32
+ >
33
+ <Icon name="arrowLeft" />
34
+ </MonthButton>
35
+ <MonthLabel>{monthLabel}</MonthLabel>
36
+ <MonthButton
37
+ emphasis="ghost"
38
+ onClick={() =>
39
+ setDisplayInfo({
40
+ month: month + 1,
41
+ year: year,
42
+ })
43
+ }
44
+ >
45
+ <Icon name="arrowRight" />
46
+ </MonthButton>
47
+ </MonthRow>
48
+ );
18
49
  }
19
50
 
20
- export function DatePicker({
21
- value,
22
- onChange,
51
+ function DatePickerRoot({
23
52
  className,
24
53
  color,
54
+ value,
55
+ onChange,
56
+ children,
25
57
  ...rest
26
- }: DatePickerProps) {
58
+ }: DatePickerProps & {
59
+ children?: ReactNode;
60
+ }) {
27
61
  const [{ month, year }, setDisplay] = useState(() => ({
28
62
  month: new Date().getMonth(),
29
63
  year: new Date().getFullYear(),
30
64
  }));
31
- const monthLabel = new Date(year, month).toLocaleDateString('en-US', {
32
- month: 'long',
33
- year: 'numeric',
34
- });
35
65
 
36
66
  return (
37
67
  <div
38
- className={classNames(
68
+ className={clsx(
39
69
  color && `palette-${color}`,
40
70
  'layer-components:(flex flex-col items-center justify-center w-[calc(var(--day-size,32px)*7)])',
41
- className,
42
71
  )}
43
72
  {...rest}
44
73
  >
45
- <MonthRow>
46
- <MonthButton
47
- emphasis="ghost"
48
- onClick={() =>
49
- setDisplay((cur) => ({
50
- month: cur.month - 1,
51
- year: cur.year,
52
- }))
53
- }
54
- >
55
- <Icon name="arrowLeft" />
56
- </MonthButton>
57
- <MonthLabel>{monthLabel}</MonthLabel>
58
- <MonthButton
59
- emphasis="ghost"
60
- onClick={() =>
61
- setDisplay((cur) => ({
62
- month: cur.month + 1,
63
- year: cur.year,
64
- }))
65
- }
66
- >
67
- <Icon name="arrowRight" />
68
- </MonthButton>
69
- </MonthRow>
70
74
  <Calendar
71
75
  displayMonth={month}
72
76
  displayYear={year}
@@ -74,188 +78,39 @@ export function DatePicker({
74
78
  onChange={onChange}
75
79
  onDisplayChange={setDisplay}
76
80
  >
77
- <CalendarGrid>
78
- <DayLabels />
79
- <CalendarDays>
80
- {(value) => <CalendarDay value={value} key={value.key} />}
81
- </CalendarDays>
82
- </CalendarGrid>
81
+ {children}
83
82
  </Calendar>
84
83
  </div>
85
84
  );
86
85
  }
87
86
 
88
- export interface DateRangePickerProps {
89
- value: { start: Date | null; end: Date | null };
90
- onChange: (value: { start: Date | null; end: Date | null }) => void;
87
+ export interface DatePickerProps {
88
+ value: Date | null;
89
+ onChange: (date: Date | null) => void;
91
90
  className?: string;
91
+ color?: PaletteName;
92
92
  }
93
93
 
94
- export function DateRangePicker({
95
- value,
96
- onChange,
97
- className,
98
- }: DateRangePickerProps) {
99
- const [{ month, year }, setDisplay] = useState(() => ({
100
- month: new Date().getMonth(),
101
- year: new Date().getFullYear(),
102
- }));
103
- const monthLabel = new Date(year, month).toLocaleDateString('en-US', {
104
- month: 'long',
105
- year: 'numeric',
106
- });
107
- const nextMonth = new Date(year, month + 1);
108
- const nextMonthLabel = nextMonth.toLocaleDateString('en-US', {
109
- month: 'long',
110
- year: 'numeric',
111
- });
112
- const onDisplayChange = useCallback(
113
- ({ month: newMonth, year: newYear }: { month: number; year: number }) => {
114
- /**
115
- * Important UX consideration:
116
- *
117
- * since we are displaying 2 months at once, we don't
118
- * always want to change our view if the user's cursor
119
- * date moves from one month to another. Specifically,
120
- * if they move from the first visible month to the
121
- * second visible month, we don't need to change the view,
122
- * since they are still within the visible range.
123
- * So, we write logic to ignore that case!
124
- */
125
- if (newMonth === month + 1 && newYear === year) {
126
- return; // ignore movement from the first to the second frame
127
- }
128
-
129
- setDisplay({
130
- month: newMonth,
131
- year: newYear,
132
- });
133
- },
134
- [month, year],
135
- );
136
-
94
+ function DatePickerDefault(props: DatePickerProps) {
137
95
  return (
138
- <Calendar
139
- displayMonth={month}
140
- displayYear={year}
141
- rangeValue={value}
142
- onRangeChange={(range) => onChange(range)}
143
- onDisplayChange={onDisplayChange}
144
- className={classNames('flex justify-center', className)}
145
- >
146
- <RangeLayout>
147
- <MonthButton
148
- emphasis="ghost"
149
- className="[grid-area:prevMonth]"
150
- onClick={() =>
151
- setDisplay((cur) => ({
152
- month: cur.month - 1,
153
- year: cur.year,
154
- }))
155
- }
156
- >
157
- <Icon name="arrowLeft" />
158
- </MonthButton>
159
- <MonthLabel className="[grid-area:leftMonth]">{monthLabel}</MonthLabel>
160
- <MonthLabel className="[grid-area:rightMonth] !hidden !sm:block">
161
- {nextMonthLabel}
162
- </MonthLabel>
163
- <MonthButton
164
- emphasis="ghost"
165
- className="[grid-area:nextMonth]"
166
- onClick={() =>
167
- setDisplay((cur) => ({
168
- month: cur.month + 1,
169
- year: cur.year,
170
- }))
171
- }
172
- >
173
- <Icon name="arrowRight" />
174
- </MonthButton>
175
- <CalendarGrid className="[grid-area:leftGrid]">
176
- <DayLabels />
177
- <CalendarDays>
178
- {(value) => <CalendarDay value={value} key={value.key} />}
179
- </CalendarDays>
180
- </CalendarGrid>
181
- <CalendarGrid className="[grid-area:rightGrid] !hidden !sm:grid">
182
- <DayLabels />
183
- <CalendarDays monthOffset={1}>
184
- {(value) => <CalendarDay value={value} key={value.key} />}
185
- </CalendarDays>
186
- </CalendarGrid>
187
- </RangeLayout>
188
- </Calendar>
96
+ <DatePickerRoot {...props}>
97
+ <DatePickerMonthControls />
98
+ <CalendarGrid>
99
+ {(value) => <CalendarDay value={value} key={value.key} />}
100
+ </CalendarGrid>
101
+ </DatePickerRoot>
189
102
  );
190
103
  }
191
104
 
192
- const MonthRow = withClassName(
193
- 'div',
194
- 'flex flex-row justify-between items-center w-full',
195
- );
196
-
197
- const MonthLabel = withClassName(
198
- 'span',
199
- 'text-sm font-bold min-w-0 overflow-hidden text-center text-ellipsis',
200
- 'self-center',
201
- );
202
-
203
- const MonthButton = withClassName(Button, 'self-center');
204
-
205
- const CalendarGrid = withClassName(
206
- 'div',
207
- 'grid grid-cols-[repeat(7,var(--day-size,32px))] [grid-auto-rows:var(--day-size,32px)]',
208
- 'height-[calc(var(--day-size,32px)*7)] rounded overflow-hidden p-2',
209
- );
210
-
211
- const CalendarDay = withClassName(
212
- BaseCalendarDay,
213
- 'border border-solid border-transparent bg-white mr--1px mb--1px relative color-black',
214
- 'flex items-center justify-center transition cursor-pointer',
215
- '[&[data-highlighted]]:(z-1 ring-2 ring-accent)',
216
- 'hover:(z-1 ring-2 ring-accent)',
217
- 'active:(bg-main-light rounded)',
218
- '[&[data-selected]]:(bg-main z-2 rounded)',
219
- '[&[data-in-range]]:(bg-main-light rounded-none z-1)',
220
- '[&[data-range-start]]:(bg-main rounded-l rounded-r-none z-1)',
221
- '[&[data-range-end]]:(bg-main rounded-r rounded-l-none z-1)',
222
- 'disabled:(opacity-50 cursor-default)',
223
- // today dot
224
- "[&[data-today]]:before:(content-[''] absolute left-[1px] top-[1px] w-[6px] h-[6px] rounded-lg bg-attention border-1 border-solid border-black)",
225
- // calendar edges
226
- '[&[data-top-edge]]:(border-t-gray)',
227
- '[&[data-bottom-edge]]:(border-b-gray)',
228
- '[&[data-first-column]]:(border-l-gray)',
229
- '[&[data-last-column]]:(border-r-gray)',
230
- '[&[data-day-first]]:(border-l-gray rounded-tl)',
231
- '[&[data-day-last]]:(border-r-gray rounded-br)',
232
- '[&[data-first-column][data-bottom-edge]]:rounded-bl',
233
- '[&[data-last-column][data-bottom-edge]]:rounded-br',
234
- '[&[data-first-column][data-top-edge]]:rounded-tl',
235
- '[&[data-last-column][data-top-edge]]:rounded-tr',
236
- '[&[data-different-month]]:[visibility:hidden]',
237
- );
238
-
239
- const DayLabel = withClassName(
240
- 'div',
241
- 'flex items-center justify-center text-sm color-gray-dark',
242
- );
243
-
244
- const DayLabels = () => (
245
- <>
246
- <DayLabel>S</DayLabel>
247
- <DayLabel>M</DayLabel>
248
- <DayLabel>T</DayLabel>
249
- <DayLabel>W</DayLabel>
250
- <DayLabel>T</DayLabel>
251
- <DayLabel>F</DayLabel>
252
- <DayLabel>S</DayLabel>
253
- </>
254
- );
255
-
256
- const RangeLayout = withClassName(
257
- 'div',
258
- 'grid [grid-template-areas:"prevMonth_leftMonth_nextMonth""leftGrid_leftGrid_leftGrid"] [grid-template-columns:auto_1fr_auto]',
259
- '[grid-template-rows:auto_1fr] gap-2',
260
- 'sm:grid-areas-[prevMonth_leftMonth_rightMonth_nextMonth]-[leftGrid_leftGrid_rightGrid_rightGrid] sm:[grid-template-columns:auto_1fr_1fr_auto]',
261
- );
105
+ export const DatePicker = Object.assign(DatePickerDefault, {
106
+ Root: DatePickerRoot,
107
+ Calendar,
108
+ CalendarDay,
109
+ CalendarDays,
110
+ CalendarGrid,
111
+ DayLabels,
112
+ MonthControls: DatePickerMonthControls,
113
+ MonthButton,
114
+ MonthLabel,
115
+ MonthRow,
116
+ });