@a-type/ui 3.0.32 → 3.0.34

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 (37) 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 +19 -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/css/main.css +1 -1
  17. package/dist/esm/components/datePicker/Calendar.d.ts +11 -0
  18. package/dist/esm/components/datePicker/Calendar.js +32 -0
  19. package/dist/esm/components/datePicker/Calendar.js.map +1 -0
  20. package/dist/esm/components/datePicker/DatePicker.d.ts +42 -13
  21. package/dist/esm/components/datePicker/DatePicker.js +32 -68
  22. package/dist/esm/components/datePicker/DatePicker.js.map +1 -1
  23. package/dist/esm/components/datePicker/DatePicker.stories.d.ts +36 -1
  24. package/dist/esm/components/datePicker/DatePicker.stories.js +20 -3
  25. package/dist/esm/components/datePicker/DatePicker.stories.js.map +1 -1
  26. package/dist/esm/components/datePicker/DateRangePicker.d.ts +55 -0
  27. package/dist/esm/components/datePicker/DateRangePicker.js +83 -0
  28. package/dist/esm/components/datePicker/DateRangePicker.js.map +1 -0
  29. package/dist/esm/components/datePicker/index.d.ts +2 -0
  30. package/dist/esm/components/datePicker/index.js +1 -0
  31. package/dist/esm/components/datePicker/index.js.map +1 -1
  32. package/package.json +1 -1
  33. package/src/components/datePicker/Calendar.tsx +83 -0
  34. package/src/components/datePicker/DatePicker.stories.tsx +38 -2
  35. package/src/components/datePicker/DatePicker.tsx +77 -222
  36. package/src/components/datePicker/DateRangePicker.tsx +161 -0
  37. package/src/components/datePicker/index.ts +2 -0
@@ -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
+ });
@@ -0,0 +1,161 @@
1
+ import { Calendar, CalendarDays, useCalendarContext } from 'calendar-blocks';
2
+ import clsx from 'clsx';
3
+ import { ReactNode, useCallback, useState } from 'react';
4
+ import { withClassName } from '../../hooks.js';
5
+ import { PaletteName } from '../../uno/index.js';
6
+ import { Icon } from '../icon/Icon.js';
7
+ import {
8
+ CalendarDay,
9
+ CalendarGrid,
10
+ DayLabels,
11
+ MonthButton,
12
+ MonthLabel,
13
+ } from './Calendar.js';
14
+
15
+ const RangeLayout = withClassName(
16
+ 'div',
17
+ 'grid [grid-template-areas:"prevMonth_leftMonth_nextMonth""leftGrid_leftGrid_leftGrid"] [grid-template-columns:auto_1fr_auto]',
18
+ '[grid-template-rows:auto_1fr] gap-2',
19
+ 'sm:grid-areas-[prevMonth_leftMonth_rightMonth_nextMonth]-[leftGrid_leftGrid_rightGrid_rightGrid] sm:[grid-template-columns:auto_1fr_1fr_auto]',
20
+ );
21
+
22
+ function DateRangePickerMonthControls() {
23
+ const { setDisplayInfo, month, year } = useCalendarContext();
24
+ const monthLabel = new Date(year, month).toLocaleDateString('en-US', {
25
+ month: 'long',
26
+ year: 'numeric',
27
+ });
28
+ const nextMonth = new Date(year, month + 1);
29
+ const nextMonthLabel = nextMonth.toLocaleDateString('en-US', {
30
+ month: 'long',
31
+ year: 'numeric',
32
+ });
33
+ return (
34
+ <>
35
+ <MonthButton
36
+ emphasis="ghost"
37
+ className="[grid-area:prevMonth]"
38
+ onClick={() =>
39
+ setDisplayInfo({
40
+ month: month - 1,
41
+ year: year,
42
+ })
43
+ }
44
+ >
45
+ <Icon name="arrowLeft" />
46
+ </MonthButton>
47
+ <MonthLabel className="[grid-area:leftMonth]">{monthLabel}</MonthLabel>
48
+ <MonthLabel className="[grid-area:rightMonth] !hidden !sm:block">
49
+ {nextMonthLabel}
50
+ </MonthLabel>
51
+ <MonthButton
52
+ emphasis="ghost"
53
+ className="[grid-area:nextMonth]"
54
+ onClick={() =>
55
+ setDisplayInfo({
56
+ month: month + 1,
57
+ year: year,
58
+ })
59
+ }
60
+ >
61
+ <Icon name="arrowRight" />
62
+ </MonthButton>
63
+ </>
64
+ );
65
+ }
66
+
67
+ function DateRangePickerRoot({
68
+ children,
69
+ color,
70
+ value,
71
+ onChange,
72
+ className,
73
+ ...rest
74
+ }: DateRangePickerProps & {
75
+ children?: ReactNode;
76
+ }) {
77
+ const [{ month, year }, setDisplay] = useState(() => ({
78
+ month: new Date().getMonth(),
79
+ year: new Date().getFullYear(),
80
+ }));
81
+
82
+ const onDisplayChange = useCallback(
83
+ ({ month: newMonth, year: newYear }: { month: number; year: number }) => {
84
+ /**
85
+ * Important UX consideration:
86
+ *
87
+ * since we are displaying 2 months at once, we don't
88
+ * always want to change our view if the user's cursor
89
+ * date moves from one month to another. Specifically,
90
+ * if they move from the first visible month to the
91
+ * second visible month, we don't need to change the view,
92
+ * since they are still within the visible range.
93
+ * So, we write logic to ignore that case!
94
+ */
95
+ if (newMonth === month + 1 && newYear === year) {
96
+ return; // ignore movement from the first to the second frame
97
+ }
98
+
99
+ setDisplay({
100
+ month: newMonth,
101
+ year: newYear,
102
+ });
103
+ },
104
+ [month, year],
105
+ );
106
+
107
+ return (
108
+ <Calendar
109
+ displayMonth={month}
110
+ displayYear={year}
111
+ rangeValue={value}
112
+ onRangeChange={(range) => onChange(range)}
113
+ onDisplayChange={onDisplayChange}
114
+ className={clsx(
115
+ 'flex justify-center',
116
+ color && `palette-${color}`,
117
+ className,
118
+ )}
119
+ {...rest}
120
+ >
121
+ <RangeLayout>{children}</RangeLayout>
122
+ </Calendar>
123
+ );
124
+ }
125
+
126
+ export interface DateRangePickerProps {
127
+ value: { start: Date | null; end: Date | null };
128
+ onChange: (value: { start: Date | null; end: Date | null }) => void;
129
+ className?: string;
130
+ color?: PaletteName;
131
+ }
132
+
133
+ function DateRangePickerBase(props: DateRangePickerProps) {
134
+ return (
135
+ <DateRangePickerRoot {...props}>
136
+ <DateRangePickerMonthControls />
137
+ <CalendarGrid className="[grid-area:leftGrid]">
138
+ {(value) => <CalendarDay value={value} key={value.key} />}
139
+ </CalendarGrid>
140
+ <CalendarGrid
141
+ className="[grid-area:rightGrid] !hidden !sm:grid"
142
+ monthOffset={1}
143
+ >
144
+ {(value) => <CalendarDay value={value} key={value.key} />}
145
+ </CalendarGrid>
146
+ </DateRangePickerRoot>
147
+ );
148
+ }
149
+
150
+ export const DateRangePicker = Object.assign(DateRangePickerBase, {
151
+ Root: DateRangePickerRoot,
152
+ RangeLayout,
153
+ DayLabels,
154
+ CalendarDay,
155
+ Calendar,
156
+ CalendarGrid,
157
+ CalendarDays,
158
+ MonthControls: DateRangePickerMonthControls,
159
+ MonthButton,
160
+ MonthLabel,
161
+ });
@@ -1 +1,3 @@
1
+ export type * from 'calendar-blocks';
1
2
  export * from './DatePicker.js';
3
+ export * from './DateRangePicker.js';