@refraktor/dates 0.0.3 → 0.0.5

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 (63) hide show
  1. package/build/components/date-range-picker/date-range-picker.d.ts +4 -0
  2. package/build/components/date-range-picker/date-range-picker.d.ts.map +1 -0
  3. package/build/components/date-range-picker/date-range-picker.js +379 -0
  4. package/build/components/date-range-picker/date-range-picker.types.d.ts +100 -0
  5. package/build/components/date-range-picker/date-range-picker.types.d.ts.map +1 -0
  6. package/build/components/date-range-picker/date-range-picker.types.js +1 -0
  7. package/build/components/date-range-picker/index.d.ts +3 -0
  8. package/build/components/date-range-picker/index.d.ts.map +1 -0
  9. package/build/components/date-range-picker/index.js +1 -0
  10. package/build/components/time-input/index.d.ts +1 -1
  11. package/build/components/time-input/index.d.ts.map +1 -1
  12. package/build/components/time-input/time-input.d.ts.map +1 -1
  13. package/build/components/time-input/time-input.js +7 -196
  14. package/build/components/time-input/time-input.types.d.ts +5 -83
  15. package/build/components/time-input/time-input.types.d.ts.map +1 -1
  16. package/build/components/time-picker/index.d.ts +1 -1
  17. package/build/components/time-picker/index.d.ts.map +1 -1
  18. package/build/components/time-picker/time-picker.d.ts.map +1 -1
  19. package/build/components/time-picker/time-picker.js +498 -350
  20. package/build/components/time-picker/time-picker.types.d.ts +96 -61
  21. package/build/components/time-picker/time-picker.types.d.ts.map +1 -1
  22. package/build/style.css +2 -2
  23. package/package.json +33 -4
  24. package/.turbo/turbo-build.log +0 -4
  25. package/refraktor-dates-0.0.1-alpha.0.tgz +0 -0
  26. package/src/components/date-input/date-input.tsx +0 -379
  27. package/src/components/date-input/date-input.types.ts +0 -161
  28. package/src/components/date-input/index.ts +0 -13
  29. package/src/components/date-picker/date-picker.tsx +0 -649
  30. package/src/components/date-picker/date-picker.types.ts +0 -145
  31. package/src/components/date-picker/index.ts +0 -15
  32. package/src/components/dates-provider/context.ts +0 -18
  33. package/src/components/dates-provider/dates-provider.tsx +0 -136
  34. package/src/components/dates-provider/index.ts +0 -10
  35. package/src/components/dates-provider/types.ts +0 -33
  36. package/src/components/dates-provider/use-dates.ts +0 -5
  37. package/src/components/index.ts +0 -9
  38. package/src/components/month-input/index.ts +0 -13
  39. package/src/components/month-input/month-input.tsx +0 -366
  40. package/src/components/month-input/month-input.types.ts +0 -139
  41. package/src/components/month-picker/index.ts +0 -14
  42. package/src/components/month-picker/month-picker.tsx +0 -458
  43. package/src/components/month-picker/month-picker.types.ts +0 -117
  44. package/src/components/picker-shared/index.ts +0 -7
  45. package/src/components/picker-shared/picker-header.tsx +0 -178
  46. package/src/components/picker-shared/picker-header.types.ts +0 -49
  47. package/src/components/picker-shared/picker.styles.ts +0 -69
  48. package/src/components/picker-shared/picker.types.ts +0 -4
  49. package/src/components/time-input/index.ts +0 -23
  50. package/src/components/time-input/time-input.tsx +0 -453
  51. package/src/components/time-input/time-input.types.ts +0 -163
  52. package/src/components/time-picker/index.ts +0 -19
  53. package/src/components/time-picker/time-picker.tsx +0 -737
  54. package/src/components/time-picker/time-picker.types.ts +0 -135
  55. package/src/components/year-input/index.ts +0 -13
  56. package/src/components/year-input/year-input.tsx +0 -350
  57. package/src/components/year-input/year-input.types.ts +0 -118
  58. package/src/components/year-picker/index.ts +0 -15
  59. package/src/components/year-picker/year-picker.tsx +0 -504
  60. package/src/components/year-picker/year-picker.types.ts +0 -108
  61. package/src/index.ts +0 -3
  62. package/src/style.css +0 -1
  63. package/tsconfig.json +0 -13
@@ -1,458 +0,0 @@
1
- import { useId, useUncontrolled } from "@refraktor/utils";
2
- import { KeyboardEvent, useEffect, useMemo, useState } from "react";
3
- import {
4
- createClassNamesConfig,
5
- createComponentConfig,
6
- factory,
7
- useTheme,
8
- useClassNames,
9
- useProps
10
- } from "@refraktor/core";
11
- import {
12
- getGridColumns,
13
- getPickerSizeStyles,
14
- PickerHeader
15
- } from "../picker-shared";
16
- import { useDates } from "../dates-provider";
17
- import { YearPicker } from "../year-picker";
18
- import {
19
- MonthPickerClassNames,
20
- MonthPickerFactoryPayload,
21
- MonthPickerNavigationDirection,
22
- MonthPickerProps
23
- } from "./month-picker.types";
24
-
25
- const DEFAULT_COLUMNS = 3;
26
- const DEFAULT_YEAR_PICKER_YEARS_PER_PAGE = 9;
27
- const DEFAULT_YEAR_PICKER_COLUMNS = 3;
28
- const MONTHS_IN_YEAR = 12;
29
-
30
- const defaultProps = {
31
- columns: DEFAULT_COLUMNS,
32
- yearPickerYearsPerPage: DEFAULT_YEAR_PICKER_YEARS_PER_PAGE,
33
- yearPickerColumns: DEFAULT_YEAR_PICKER_COLUMNS,
34
- disabled: false,
35
- size: "md",
36
- radius: "default"
37
- } satisfies Partial<MonthPickerProps>;
38
-
39
- type YearBounds = {
40
- min: number;
41
- max: number;
42
- hasMin: boolean;
43
- hasMax: boolean;
44
- };
45
-
46
- const isValidDate = (value: unknown): value is Date =>
47
- value instanceof Date && !Number.isNaN(value.getTime());
48
-
49
- const toSafeInteger = (value: number | undefined, fallback: number) => {
50
- if (!Number.isFinite(value)) {
51
- return fallback;
52
- }
53
-
54
- return Math.trunc(value as number);
55
- };
56
-
57
- const clamp = (value: number, min: number, max: number) =>
58
- Math.min(max, Math.max(min, value));
59
-
60
- const getBounds = (minYear?: number, maxYear?: number): YearBounds => {
61
- const hasMin = Number.isFinite(minYear);
62
- const hasMax = Number.isFinite(maxYear);
63
-
64
- const min = hasMin
65
- ? Math.trunc(minYear as number)
66
- : Number.MIN_SAFE_INTEGER;
67
- const max = hasMax
68
- ? Math.trunc(maxYear as number)
69
- : Number.MAX_SAFE_INTEGER;
70
-
71
- if (min <= max) {
72
- return { min, max, hasMin, hasMax };
73
- }
74
-
75
- return {
76
- min: max,
77
- max: min,
78
- hasMin,
79
- hasMax
80
- };
81
- };
82
-
83
- const createMonthValue = (baseDate: Date, year: number, month: number) => {
84
- const nextDate = new Date(baseDate);
85
- nextDate.setFullYear(year, month, 1);
86
- nextDate.setHours(0, 0, 0, 0);
87
- return nextDate;
88
- };
89
-
90
- const isSameMonth = (first: Date, second: Date) =>
91
- first.getFullYear() === second.getFullYear() &&
92
- first.getMonth() === second.getMonth();
93
-
94
- type MonthPickerView = "month" | "year";
95
-
96
- const MonthPicker = factory<MonthPickerFactoryPayload>((_props, ref) => {
97
- const { cx, getRadius } = useTheme();
98
- const { createDate } = useDates();
99
- const {
100
- id,
101
- value,
102
- defaultValue,
103
- onChange,
104
- minYear,
105
- maxYear,
106
- columns,
107
- yearPickerYearsPerPage,
108
- yearPickerColumns,
109
- disabled,
110
- size,
111
- radius,
112
- getMonthLabel,
113
- getMonthAriaLabel,
114
- getHeaderLabel,
115
- getNavigationAriaLabel,
116
- className,
117
- classNames,
118
- ...props
119
- } = useProps("MonthPicker", defaultProps, _props);
120
- const classes = useClassNames("MonthPicker", classNames);
121
-
122
- const _id = useId(id);
123
-
124
- const today = new Date();
125
- const currentYear = today.getFullYear();
126
- const bounds = useMemo(
127
- () => getBounds(minYear, maxYear),
128
- [minYear, maxYear]
129
- );
130
-
131
- const safeColumns = clamp(
132
- toSafeInteger(columns, DEFAULT_COLUMNS),
133
- 1,
134
- Math.min(6, MONTHS_IN_YEAR)
135
- );
136
- const safeYearPickerYearsPerPage = Math.max(
137
- 1,
138
- toSafeInteger(
139
- yearPickerYearsPerPage,
140
- DEFAULT_YEAR_PICKER_YEARS_PER_PAGE
141
- )
142
- );
143
- const safeYearPickerColumns = clamp(
144
- toSafeInteger(yearPickerColumns, DEFAULT_YEAR_PICKER_COLUMNS),
145
- 1,
146
- Math.min(6, safeYearPickerYearsPerPage)
147
- );
148
-
149
- const [selectedDateState, setSelectedDate] = useUncontrolled<
150
- Date | undefined
151
- >({
152
- value,
153
- defaultValue,
154
- finalValue: undefined,
155
- onChange: (nextDate) => {
156
- if (nextDate !== undefined) {
157
- onChange?.(nextDate);
158
- }
159
- }
160
- });
161
-
162
- const selectedDate = isValidDate(selectedDateState)
163
- ? selectedDateState
164
- : undefined;
165
- const selectedYear = selectedDate?.getFullYear();
166
-
167
- const [displayedYear, setDisplayedYear] = useState(() =>
168
- clamp(selectedYear ?? currentYear, bounds.min, bounds.max)
169
- );
170
- const [view, setView] = useState<MonthPickerView>("month");
171
-
172
- useEffect(() => {
173
- setDisplayedYear((previousYear) =>
174
- clamp(previousYear, bounds.min, bounds.max)
175
- );
176
- }, [bounds.max, bounds.min]);
177
-
178
- useEffect(() => {
179
- if (selectedYear === undefined) {
180
- return;
181
- }
182
-
183
- const normalizedYear = clamp(selectedYear, bounds.min, bounds.max);
184
-
185
- setDisplayedYear((previousYear) => {
186
- if (previousYear === normalizedYear) {
187
- return previousYear;
188
- }
189
-
190
- return normalizedYear;
191
- });
192
- }, [bounds.max, bounds.min, selectedYear]);
193
-
194
- const canGoPrevious =
195
- !disabled && (!bounds.hasMin || displayedYear > bounds.min);
196
- const canGoNext = !disabled && (!bounds.hasMax || displayedYear < bounds.max);
197
-
198
- const sizeStyles = getPickerSizeStyles(size);
199
-
200
- const resolveNavigationLabel = (
201
- direction: MonthPickerNavigationDirection
202
- ) => {
203
- if (getNavigationAriaLabel) {
204
- return getNavigationAriaLabel(direction, displayedYear);
205
- }
206
-
207
- return direction === "previous"
208
- ? `Show previous year (${displayedYear - 1})`
209
- : `Show next year (${displayedYear + 1})`;
210
- };
211
-
212
- const monthItems = useMemo(
213
- () =>
214
- Array.from({ length: MONTHS_IN_YEAR }, (_, month) => {
215
- const dayjsMonth = createDate(new Date(displayedYear, month, 1));
216
-
217
- return {
218
- month,
219
- label: getMonthLabel
220
- ? getMonthLabel(month, displayedYear, dayjsMonth.toDate())
221
- : dayjsMonth.format("MMM"),
222
- fullLabel: dayjsMonth.format("MMMM")
223
- };
224
- }),
225
- [createDate, displayedYear, getMonthLabel]
226
- );
227
-
228
- const selectedMonth =
229
- selectedDate !== undefined && selectedDate.getFullYear() === displayedYear
230
- ? selectedDate.getMonth()
231
- : undefined;
232
-
233
- const resolveMonthAriaLabel = (month: number, isSelected: boolean) => {
234
- if (getMonthAriaLabel) {
235
- return getMonthAriaLabel(month, displayedYear, isSelected);
236
- }
237
-
238
- const fullMonthLabel = monthItems[month]?.fullLabel ?? `Month ${month + 1}`;
239
-
240
- return isSelected
241
- ? `${fullMonthLabel} ${displayedYear}, selected`
242
- : `Choose ${fullMonthLabel} ${displayedYear}`;
243
- };
244
-
245
- const handleMonthSelect = (month: number, year = displayedYear) => {
246
- if (disabled) {
247
- return;
248
- }
249
-
250
- const normalizedMonth = clamp(month, 0, MONTHS_IN_YEAR - 1);
251
- const normalizedYear = clamp(year, bounds.min, bounds.max);
252
-
253
- setDisplayedYear(normalizedYear);
254
-
255
- const baseDate = selectedDate ?? today;
256
- const nextDate = createMonthValue(baseDate, normalizedYear, normalizedMonth);
257
-
258
- if (selectedDate && isSameMonth(selectedDate, nextDate)) {
259
- return;
260
- }
261
-
262
- setSelectedDate(nextDate);
263
- };
264
-
265
- const shiftYear = (direction: -1 | 1) => {
266
- setDisplayedYear((previousYear) =>
267
- clamp(previousYear + direction, bounds.min, bounds.max)
268
- );
269
- };
270
-
271
- const handlePrevious = () => {
272
- if (!canGoPrevious) {
273
- return;
274
- }
275
-
276
- shiftYear(-1);
277
- };
278
-
279
- const handleNext = () => {
280
- if (!canGoNext) {
281
- return;
282
- }
283
-
284
- shiftYear(1);
285
- };
286
-
287
- const handleGridKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
288
- if (disabled) {
289
- return;
290
- }
291
-
292
- const keyboardBaseMonth = selectedMonth ?? 0;
293
-
294
- const keyboardSteps: Record<string, number> = {
295
- ArrowLeft: -1,
296
- ArrowRight: 1,
297
- ArrowUp: -safeColumns,
298
- ArrowDown: safeColumns
299
- };
300
-
301
- const step = keyboardSteps[event.key];
302
-
303
- if (step !== undefined) {
304
- event.preventDefault();
305
- handleMonthSelect(keyboardBaseMonth + step);
306
- return;
307
- }
308
-
309
- if (event.key === "Home") {
310
- event.preventDefault();
311
- handleMonthSelect(0);
312
- return;
313
- }
314
-
315
- if (event.key === "End") {
316
- event.preventDefault();
317
- handleMonthSelect(MONTHS_IN_YEAR - 1);
318
- return;
319
- }
320
-
321
- if (event.key === "PageUp") {
322
- event.preventDefault();
323
- handleMonthSelect(keyboardBaseMonth, displayedYear - 1);
324
- return;
325
- }
326
-
327
- if (event.key === "PageDown") {
328
- event.preventDefault();
329
- handleMonthSelect(keyboardBaseMonth, displayedYear + 1);
330
- }
331
- };
332
-
333
- const handleHeaderLabelClick = () => {
334
- if (disabled) {
335
- return;
336
- }
337
-
338
- setView("year");
339
- };
340
-
341
- const handleYearSelect = (year: number) => {
342
- setDisplayedYear(clamp(year, bounds.min, bounds.max));
343
- setView("month");
344
- };
345
-
346
- const hasVisibleSelection = selectedMonth !== undefined;
347
-
348
- return (
349
- <div
350
- ref={ref}
351
- id={_id}
352
- className={cx(
353
- "inline-flex w-full flex-col gap-2 bg-[var(--refraktor-bg)] p-2",
354
- getRadius(radius),
355
- classes.root,
356
- className
357
- )}
358
- {...props}
359
- >
360
- {view === "year" ? (
361
- <YearPicker
362
- value={displayedYear}
363
- onChange={handleYearSelect}
364
- minYear={minYear}
365
- maxYear={maxYear}
366
- yearsPerPage={safeYearPickerYearsPerPage}
367
- columns={safeYearPickerColumns}
368
- disabled={disabled}
369
- size={size}
370
- radius={radius}
371
- className={cx("bg-transparent p-0", classes.yearPicker)}
372
- />
373
- ) : (
374
- <>
375
- <PickerHeader
376
- label={
377
- getHeaderLabel ? getHeaderLabel(displayedYear) : displayedYear
378
- }
379
- onPrevious={handlePrevious}
380
- onNext={handleNext}
381
- onLabelClick={
382
- disabled ? undefined : handleHeaderLabelClick
383
- }
384
- previousDisabled={!canGoPrevious}
385
- nextDisabled={!canGoNext}
386
- previousLabel={resolveNavigationLabel("previous")}
387
- nextLabel={resolveNavigationLabel("next")}
388
- size={size}
389
- radius={radius}
390
- classNames={{
391
- root: classes.header,
392
- controls: classes.headerControls,
393
- control: classes.headerControl,
394
- previousControl: classes.headerPreviousControl,
395
- nextControl: classes.headerNextControl,
396
- label: classes.headerLabel
397
- }}
398
- />
399
-
400
- <div
401
- role="grid"
402
- aria-label={`Month picker, ${displayedYear}`}
403
- className={cx(
404
- "grid",
405
- getGridColumns(safeColumns),
406
- sizeStyles.gridGap,
407
- classes.grid
408
- )}
409
- onKeyDown={handleGridKeyDown}
410
- >
411
- {monthItems.map(({ month, label }) => {
412
- const isSelected = month === selectedMonth;
413
- const tabIndex =
414
- isSelected || (!hasVisibleSelection && month === 0)
415
- ? 0
416
- : -1;
417
-
418
- return (
419
- <button
420
- key={month}
421
- type="button"
422
- role="gridcell"
423
- aria-selected={isSelected}
424
- aria-label={resolveMonthAriaLabel(month, isSelected)}
425
- tabIndex={tabIndex}
426
- data-active={isSelected}
427
- data-disabled={disabled}
428
- disabled={disabled}
429
- className={cx(
430
- "inline-flex items-center justify-center font-medium text-[var(--refraktor-text)] transition-colors",
431
- isSelected
432
- ? "bg-[var(--refraktor-primary)] text-[var(--refraktor-primary-text)]"
433
- : "hover:bg-[var(--refraktor-bg-hover)]",
434
- disabled &&
435
- "pointer-events-none cursor-not-allowed opacity-50 data-[disabled=true]:pointer-events-none",
436
- sizeStyles.cell,
437
- getRadius(radius),
438
- classes.month,
439
- isSelected && classes.monthActive
440
- )}
441
- onClick={() => handleMonthSelect(month)}
442
- >
443
- {label}
444
- </button>
445
- );
446
- })}
447
- </div>
448
- </>
449
- )}
450
- </div>
451
- );
452
- });
453
-
454
- MonthPicker.displayName = "@refraktor/dates/MonthPicker";
455
- MonthPicker.configure = createComponentConfig<MonthPickerProps>();
456
- MonthPicker.classNames = createClassNamesConfig<MonthPickerClassNames>();
457
-
458
- export default MonthPicker;
@@ -1,117 +0,0 @@
1
- import { ComponentPropsWithoutRef, ReactNode } from "react";
2
- import {
3
- RefraktorRadius,
4
- RefraktorSize,
5
- createClassNamesConfig,
6
- createComponentConfig,
7
- FactoryPayload
8
- } from "@refraktor/core";
9
-
10
- export type MonthPickerValue = Date;
11
- export type MonthPickerSize = RefraktorSize;
12
- export type MonthPickerRadius = RefraktorRadius;
13
-
14
- export type MonthPickerNavigationDirection = "previous" | "next";
15
-
16
- export type MonthPickerOnChange = (value: MonthPickerValue) => void;
17
-
18
- export type MonthPickerGetMonthLabel = (
19
- month: number,
20
- year: number,
21
- value: Date
22
- ) => ReactNode;
23
-
24
- export type MonthPickerGetMonthAriaLabel = (
25
- month: number,
26
- year: number,
27
- selected: boolean
28
- ) => string;
29
-
30
- export type MonthPickerGetHeaderLabel = (year: number) => ReactNode;
31
-
32
- export type MonthPickerGetNavigationAriaLabel = (
33
- direction: MonthPickerNavigationDirection,
34
- year: number
35
- ) => string;
36
-
37
- export type MonthPickerClassNames = {
38
- root?: string;
39
- header?: string;
40
- headerControls?: string;
41
- headerControl?: string;
42
- headerPreviousControl?: string;
43
- headerNextControl?: string;
44
- headerLabel?: string;
45
- grid?: string;
46
- month?: string;
47
- monthActive?: string;
48
- yearPicker?: string;
49
- };
50
-
51
- export interface MonthPickerProps
52
- extends Omit<
53
- ComponentPropsWithoutRef<"div">,
54
- "onChange" | "value" | "defaultValue"
55
- > {
56
- /** Active month (controlled). */
57
- value?: MonthPickerValue;
58
-
59
- /** Initial active month (uncontrolled). */
60
- defaultValue?: MonthPickerValue;
61
-
62
- /** Callback called when active month changes. */
63
- onChange?: MonthPickerOnChange;
64
-
65
- /** Minimum available year in month and year views. */
66
- minYear?: number;
67
-
68
- /** Maximum available year in month and year views. */
69
- maxYear?: number;
70
-
71
- /** Grid columns used by the month list @default `3` */
72
- columns?: number;
73
-
74
- /** Year picker years rendered in one page @default `9` */
75
- yearPickerYearsPerPage?: number;
76
-
77
- /** Year picker columns @default `3` */
78
- yearPickerColumns?: number;
79
-
80
- /** Whether all controls are disabled @default `false` */
81
- disabled?: boolean;
82
-
83
- /** Component size @default `md` */
84
- size?: MonthPickerSize;
85
-
86
- /** Border radius @default `default` */
87
- radius?: MonthPickerRadius;
88
-
89
- /** Custom month label renderer. */
90
- getMonthLabel?: MonthPickerGetMonthLabel;
91
-
92
- /** Custom aria-label generator for month buttons. */
93
- getMonthAriaLabel?: MonthPickerGetMonthAriaLabel;
94
-
95
- /** Custom header label renderer for visible year. */
96
- getHeaderLabel?: MonthPickerGetHeaderLabel;
97
-
98
- /** Custom aria-label generator for previous/next controls. */
99
- getNavigationAriaLabel?: MonthPickerGetNavigationAriaLabel;
100
-
101
- /** Used for editing root class name. */
102
- className?: string;
103
-
104
- /** Used for styling different parts of the component. */
105
- classNames?: MonthPickerClassNames;
106
- }
107
-
108
- export interface MonthPickerFactoryPayload extends FactoryPayload {
109
- props: MonthPickerProps;
110
- ref: HTMLDivElement;
111
- compound: {
112
- configure: ReturnType<typeof createComponentConfig<MonthPickerProps>>;
113
- classNames: ReturnType<
114
- typeof createClassNamesConfig<MonthPickerClassNames>
115
- >;
116
- };
117
- }
@@ -1,7 +0,0 @@
1
- export { default as PickerHeader } from "./picker-header";
2
- export type {
3
- PickerHeaderClassNames,
4
- PickerHeaderProps
5
- } from "./picker-header.types";
6
- export { getGridColumns, getPickerSizeStyles } from "./picker.styles";
7
- export type { PickerRadius, PickerSize } from "./picker.types";