maquinaweb-ui 2.64.0 → 2.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/rolldown_runtime.js +30 -0
- package/dist/container-animation/container-animation.d.ts +29 -0
- package/dist/container-animation/container-animation.d.ts.map +1 -0
- package/dist/container-animation/container-animation.js +57 -0
- package/dist/container-animation/container-animation.js.map +1 -0
- package/dist/container-animation.d.ts +2 -29
- package/dist/container-animation.js +2 -128
- package/dist/date-field/DateField.d.ts +35 -0
- package/dist/date-field/DateField.d.ts.map +1 -0
- package/dist/date-field/DateField.js +99 -0
- package/dist/date-field/DateField.js.map +1 -0
- package/dist/date-field.d.ts +2 -35
- package/dist/date-field.js +2 -96
- package/dist/date-hour-field/DateHourField.d.ts +35 -0
- package/dist/date-hour-field/DateHourField.d.ts.map +1 -0
- package/dist/date-hour-field/DateHourField.js +208 -0
- package/dist/date-hour-field/DateHourField.js.map +1 -0
- package/dist/date-hour-field/date-hour-field.utils.js +78 -0
- package/dist/date-hour-field/date-hour-field.utils.js.map +1 -0
- package/dist/date-hour-field/time-wheel-column.js +59 -0
- package/dist/date-hour-field/time-wheel-column.js.map +1 -0
- package/dist/date-hour-field.d.ts +2 -35
- package/dist/date-hour-field.js +2 -329
- package/dist/input-suggest/input-suggest.d.ts +77 -0
- package/dist/input-suggest/input-suggest.d.ts.map +1 -0
- package/dist/input-suggest/input-suggest.js +188 -0
- package/dist/input-suggest/input-suggest.js.map +1 -0
- package/dist/input-suggest.d.ts +2 -77
- package/dist/input-suggest.js +2 -183
- package/dist/kanban-dnd/constants.js +28 -0
- package/dist/kanban-dnd/constants.js.map +1 -0
- package/dist/kanban-dnd/context.d.ts +23 -0
- package/dist/kanban-dnd/context.d.ts.map +1 -0
- package/dist/kanban-dnd/context.js +62 -0
- package/dist/kanban-dnd/context.js.map +1 -0
- package/dist/kanban-dnd/drag-preview.js +58 -0
- package/dist/kanban-dnd/drag-preview.js.map +1 -0
- package/dist/kanban-dnd/drop-animation.js +129 -0
- package/dist/kanban-dnd/drop-animation.js.map +1 -0
- package/dist/kanban-dnd/kanban-card-item.d.ts +14 -0
- package/dist/kanban-dnd/kanban-card-item.d.ts.map +1 -0
- package/dist/kanban-dnd/kanban-card-item.js +72 -0
- package/dist/kanban-dnd/kanban-card-item.js.map +1 -0
- package/dist/kanban-dnd/kanban-dnd-monitor.d.ts +29 -0
- package/dist/kanban-dnd/kanban-dnd-monitor.d.ts.map +1 -0
- package/dist/kanban-dnd/kanban-dnd-monitor.js +292 -0
- package/dist/kanban-dnd/kanban-dnd-monitor.js.map +1 -0
- package/dist/kanban-dnd/kanban-dnd.d.ts +8 -0
- package/dist/kanban-dnd/kanban-dropzone.d.ts +13 -0
- package/dist/kanban-dnd/kanban-dropzone.d.ts.map +1 -0
- package/dist/kanban-dnd/kanban-dropzone.js +38 -0
- package/dist/kanban-dnd/kanban-dropzone.js.map +1 -0
- package/dist/kanban-dnd/kanban-selector.d.ts +12 -0
- package/dist/kanban-dnd/kanban-selector.d.ts.map +1 -0
- package/dist/kanban-dnd/kanban-selector.js +113 -0
- package/dist/kanban-dnd/kanban-selector.js.map +1 -0
- package/dist/kanban-dnd/move-cards.d.ts +11 -0
- package/dist/kanban-dnd/move-cards.d.ts.map +1 -0
- package/dist/kanban-dnd/move-cards.js +67 -0
- package/dist/kanban-dnd/move-cards.js.map +1 -0
- package/dist/kanban-dnd/types.d.ts +73 -0
- package/dist/kanban-dnd/types.d.ts.map +1 -0
- package/dist/kanban-dnd/use-kanban-column-dnd.d.ts +11 -0
- package/dist/kanban-dnd/use-kanban-column-dnd.d.ts.map +1 -0
- package/dist/kanban-dnd/use-kanban-column-dnd.js +37 -0
- package/dist/kanban-dnd/use-kanban-column-dnd.js.map +1 -0
- package/dist/kanban-dnd/use-kanban-selector-auto-scroll.js +127 -0
- package/dist/kanban-dnd/use-kanban-selector-auto-scroll.js.map +1 -0
- package/dist/kanban-dnd/utils.js +12 -0
- package/dist/kanban-dnd/utils.js.map +1 -0
- package/dist/kanban-dnd.d.ts +10 -155
- package/dist/kanban-dnd.js +9 -959
- package/dist/landing-content.d.ts +2 -0
- package/dist/landing-content.js +3 -0
- package/dist/landing-text/client-landing-text.js +119 -0
- package/dist/landing-text/client-landing-text.js.map +1 -0
- package/dist/landing-text/landing-content.d.ts +55 -0
- package/dist/landing-text/landing-content.d.ts.map +1 -0
- package/dist/landing-text/landing-content.js +112 -0
- package/dist/landing-text/landing-content.js.map +1 -0
- package/dist/landing-text/landing-text.d.ts +26 -0
- package/dist/landing-text/landing-text.d.ts.map +1 -0
- package/dist/landing-text/landing-text.js +34 -0
- package/dist/landing-text/landing-text.js.map +1 -0
- package/dist/landing-text/server-landing-text.d.ts +24 -0
- package/dist/landing-text/server-landing-text.d.ts.map +1 -0
- package/dist/landing-text/server-landing-text.js +18 -0
- package/dist/landing-text/server-landing-text.js.map +1 -0
- package/dist/landing-text.d.ts +3 -96
- package/dist/landing-text.js +3 -950
- package/dist/node_modules/cookie/dist/index.js +300 -0
- package/dist/node_modules/cookie/dist/index.js.map +1 -0
- package/dist/node_modules/cookies-next/lib/client/context.js +111 -0
- package/dist/node_modules/cookies-next/lib/client/context.js.map +1 -0
- package/dist/node_modules/cookies-next/lib/client/cookie-functions.js +85 -0
- package/dist/node_modules/cookies-next/lib/client/cookie-functions.js.map +1 -0
- package/dist/node_modules/cookies-next/lib/client/hooks.js +129 -0
- package/dist/node_modules/cookies-next/lib/client/hooks.js.map +1 -0
- package/dist/node_modules/cookies-next/lib/client/index.js +44 -0
- package/dist/node_modules/cookies-next/lib/client/index.js.map +1 -0
- package/dist/node_modules/cookies-next/lib/common/types.js +12 -0
- package/dist/node_modules/cookies-next/lib/common/types.js.map +1 -0
- package/dist/node_modules/cookies-next/lib/common/utils.js +35 -0
- package/dist/node_modules/cookies-next/lib/common/utils.js.map +1 -0
- package/dist/node_modules/framer-motion/dist/es/render/dom/viewport/index.js +40 -0
- package/dist/node_modules/framer-motion/dist/es/render/dom/viewport/index.js.map +1 -0
- package/dist/node_modules/framer-motion/dist/es/utils/use-in-view.js +34 -0
- package/dist/node_modules/framer-motion/dist/es/utils/use-in-view.js.map +1 -0
- package/dist/node_modules/motion-dom/dist/es/utils/resolve-elements.js +16 -0
- package/dist/node_modules/motion-dom/dist/es/utils/resolve-elements.js.map +1 -0
- package/dist/page-header/page-header.d.ts +16 -0
- package/dist/page-header/page-header.d.ts.map +1 -0
- package/dist/page-header/page-header.js +23 -0
- package/dist/page-header/page-header.js.map +1 -0
- package/dist/page-header.d.ts +2 -16
- package/dist/page-header.js +2 -24
- package/dist/remote-selector/remote-selector.d.ts +64 -0
- package/dist/remote-selector/remote-selector.d.ts.map +1 -0
- package/dist/remote-selector/remote-selector.js +114 -0
- package/dist/remote-selector/remote-selector.js.map +1 -0
- package/dist/remote-selector.d.ts +2 -107
- package/dist/remote-selector.js +2 -575
- package/dist/split-text-poor/split-text-poor.d.ts +59 -0
- package/dist/split-text-poor/split-text-poor.d.ts.map +1 -0
- package/dist/split-text-poor/split-text-poor.js +79 -0
- package/dist/split-text-poor/split-text-poor.js.map +1 -0
- package/dist/split-text-poor.d.ts +2 -59
- package/dist/split-text-poor.js +2 -75
- package/dist/src/hooks/get-mask-options.js +91 -0
- package/dist/src/hooks/get-mask-options.js.map +1 -0
- package/dist/src/hooks/is-server.js +7 -0
- package/dist/src/hooks/is-server.js.map +1 -0
- package/dist/src/hooks/module-interop.js +15 -0
- package/dist/src/hooks/module-interop.js.map +1 -0
- package/dist/src/hooks/use-money-input.js +39 -0
- package/dist/src/hooks/use-money-input.js.map +1 -0
- package/dist/{with-mask-BLZS7b9k.d.ts → src/hooks/with-mask.d.ts} +2 -2
- package/dist/src/hooks/with-mask.d.ts.map +1 -0
- package/dist/src/hooks/with-mask.js +16 -0
- package/dist/src/hooks/with-mask.js.map +1 -0
- package/dist/{utils-C8_amEgK.js → src/lib/utils.js} +2 -2
- package/dist/src/lib/utils.js.map +1 -0
- package/dist/text-field/TextField.d.ts +103 -0
- package/dist/text-field/TextField.d.ts.map +1 -0
- package/dist/text-field/TextField.js +198 -0
- package/dist/text-field/TextField.js.map +1 -0
- package/dist/text-field.d.ts +2 -103
- package/dist/text-field.js +2 -223
- package/dist/toggle-field/ToggleField.d.ts +19 -0
- package/dist/toggle-field/ToggleField.d.ts.map +1 -0
- package/dist/toggle-field/ToggleField.js +47 -0
- package/dist/toggle-field/ToggleField.js.map +1 -0
- package/dist/toggle-field/ToggleGroup.d.ts +34 -0
- package/dist/toggle-field/ToggleGroup.d.ts.map +1 -0
- package/dist/toggle-field/ToggleGroup.js +128 -0
- package/dist/toggle-field/ToggleGroup.js.map +1 -0
- package/dist/toggle-field.d.ts +2 -49
- package/dist/toggle-field.js +2 -162
- package/dist/ui/badge.js +26 -0
- package/dist/ui/badge.js.map +1 -0
- package/dist/{button-B3nLhVyZ.js → ui/button.js} +3 -3
- package/dist/ui/button.js.map +1 -0
- package/dist/{input-date-field-DToF0FmE.js → ui/calendar.js} +8 -51
- package/dist/ui/calendar.js.map +1 -0
- package/dist/ui/checkbox.js +71 -0
- package/dist/ui/checkbox.js.map +1 -0
- package/dist/ui/command.js +60 -0
- package/dist/ui/command.js.map +1 -0
- package/dist/ui/content-help.js +27 -0
- package/dist/ui/content-help.js.map +1 -0
- package/dist/ui/form.js +82 -0
- package/dist/ui/form.js.map +1 -0
- package/dist/ui/input-date-field.js +52 -0
- package/dist/ui/input-date-field.js.map +1 -0
- package/dist/ui/input-help.js +27 -0
- package/dist/ui/input-help.js.map +1 -0
- package/dist/ui/input.js +16 -0
- package/dist/ui/input.js.map +1 -0
- package/dist/{label-Bkg7B2j8.js → ui/label.js} +6 -3
- package/dist/ui/label.js.map +1 -0
- package/dist/{popover-D9IIn0lW.js → ui/popover.js} +6 -3
- package/dist/ui/popover.js.map +1 -0
- package/dist/{scroll-area-C1kW_eA9.js → ui/scroll-area.js} +6 -3
- package/dist/ui/scroll-area.js.map +1 -0
- package/dist/ui/selector.d.ts +48 -0
- package/dist/ui/selector.d.ts.map +1 -0
- package/dist/ui/selector.js +243 -0
- package/dist/ui/selector.js.map +1 -0
- package/dist/ui/tooltip.js +41 -0
- package/dist/ui/tooltip.js.map +1 -0
- package/dist/ui/tree-item-renderer.js +114 -0
- package/dist/ui/tree-item-renderer.js.map +1 -0
- package/package.json +5 -1
- package/dist/button-B3nLhVyZ.js.map +0 -1
- package/dist/container-animation.d.ts.map +0 -1
- package/dist/container-animation.js.map +0 -1
- package/dist/date-field.d.ts.map +0 -1
- package/dist/date-field.js.map +0 -1
- package/dist/date-hour-field.d.ts.map +0 -1
- package/dist/date-hour-field.js.map +0 -1
- package/dist/input-Bs61WBGW.js +0 -133
- package/dist/input-Bs61WBGW.js.map +0 -1
- package/dist/input-date-field-DToF0FmE.js.map +0 -1
- package/dist/input-help-D1JqF0YH.js +0 -149
- package/dist/input-help-D1JqF0YH.js.map +0 -1
- package/dist/input-suggest.d.ts.map +0 -1
- package/dist/input-suggest.js.map +0 -1
- package/dist/kanban-dnd.d.ts.map +0 -1
- package/dist/kanban-dnd.js.map +0 -1
- package/dist/label-Bkg7B2j8.js.map +0 -1
- package/dist/landing-text.d.ts.map +0 -1
- package/dist/landing-text.js.map +0 -1
- package/dist/page-header.d.ts.map +0 -1
- package/dist/page-header.js.map +0 -1
- package/dist/popover-D9IIn0lW.js.map +0 -1
- package/dist/remote-selector.d.ts.map +0 -1
- package/dist/remote-selector.js.map +0 -1
- package/dist/scroll-area-C1kW_eA9.js.map +0 -1
- package/dist/split-text-poor.d.ts.map +0 -1
- package/dist/split-text-poor.js.map +0 -1
- package/dist/text-field.d.ts.map +0 -1
- package/dist/text-field.js.map +0 -1
- package/dist/toggle-field.d.ts.map +0 -1
- package/dist/toggle-field.js.map +0 -1
- package/dist/utils-C8_amEgK.js.map +0 -1
- package/dist/with-mask-BLZS7b9k.d.ts.map +0 -1
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { cn } from "../src/lib/utils.js";
|
|
5
|
+
import withMask from "../src/hooks/with-mask.js";
|
|
6
|
+
import { FormDescription, FormFieldContext, FormItem, FormLabel, FormMessage } from "../ui/form.js";
|
|
7
|
+
import { Input } from "../ui/input.js";
|
|
8
|
+
import { InputHelp } from "../ui/input-help.js";
|
|
9
|
+
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover.js";
|
|
10
|
+
import { Calendar } from "../ui/calendar.js";
|
|
11
|
+
import { InputDateField } from "../ui/input-date-field.js";
|
|
12
|
+
import { HOUR_OPTIONS, MINUTE_OPTIONS, formatDateTimeAsLocalString, formatDateTimeAsTime, formatTimeValue, getFallbackTime, mergeDateAndTime, normalizeDateValue, parseTime, sanitizeTimeInput } from "./date-hour-field.utils.js";
|
|
13
|
+
import { TimeWheelColumn } from "./time-wheel-column.js";
|
|
14
|
+
import { useController, useFormContext } from "react-hook-form";
|
|
15
|
+
import { useEffect, useState } from "react";
|
|
16
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
17
|
+
import { ptBR } from "date-fns/locale";
|
|
18
|
+
|
|
19
|
+
//#region src/components/date-hour-field/DateHourField.tsx
|
|
20
|
+
function DateHourField({ name, label, description, inputDisabled, endMonth, className, disabled, initialValue, required, help, hourPlaceholder = "Hora", valueType = "local-string" }) {
|
|
21
|
+
const form = useFormContext();
|
|
22
|
+
const { field, fieldState } = useController({
|
|
23
|
+
name,
|
|
24
|
+
defaultValue: initialValue,
|
|
25
|
+
rules: { required },
|
|
26
|
+
control: form.control
|
|
27
|
+
});
|
|
28
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
29
|
+
const [timeValue, setTimeValue] = useState("");
|
|
30
|
+
const valueAsDate = normalizeDateValue(field.value);
|
|
31
|
+
const selectedTime = parseTime(timeValue);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!valueAsDate) {
|
|
34
|
+
setTimeValue("");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const nextValue = formatDateTimeAsTime(valueAsDate);
|
|
38
|
+
setTimeValue((currentValue) => currentValue === nextValue ? currentValue : nextValue);
|
|
39
|
+
}, [valueAsDate]);
|
|
40
|
+
const updateFieldTime = (timePatch) => {
|
|
41
|
+
const fallbackTime = getFallbackTime(valueAsDate);
|
|
42
|
+
const baseTime = selectedTime ?? fallbackTime;
|
|
43
|
+
const nextTime = {
|
|
44
|
+
hours: baseTime.hours,
|
|
45
|
+
minutes: baseTime.minutes,
|
|
46
|
+
...timePatch
|
|
47
|
+
};
|
|
48
|
+
const nextDate = new Date(valueAsDate ?? /* @__PURE__ */ new Date());
|
|
49
|
+
nextDate.setHours(nextTime.hours, nextTime.minutes, 0, 0);
|
|
50
|
+
setTimeValue(formatTimeValue(nextTime.hours, nextTime.minutes));
|
|
51
|
+
if (valueType === "local-string") {
|
|
52
|
+
field.onChange(formatDateTimeAsLocalString(nextDate));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
field.onChange(nextDate);
|
|
56
|
+
};
|
|
57
|
+
const handleDateSelect = (date) => {
|
|
58
|
+
if (!date) {
|
|
59
|
+
setTimeValue("");
|
|
60
|
+
field.onChange(void 0);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const mergedDate = mergeDateAndTime(date, timeValue, valueAsDate);
|
|
64
|
+
setTimeValue(formatDateTimeAsTime(mergedDate));
|
|
65
|
+
if (valueType === "local-string") {
|
|
66
|
+
field.onChange(formatDateTimeAsLocalString(mergedDate));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
field.onChange(mergedDate);
|
|
70
|
+
};
|
|
71
|
+
const handleTimeInputChange = (nextValue) => {
|
|
72
|
+
const sanitizedValue = sanitizeTimeInput(nextValue);
|
|
73
|
+
setTimeValue(sanitizedValue);
|
|
74
|
+
if (sanitizedValue.length !== 5) return;
|
|
75
|
+
const parsedTime = parseTime(sanitizedValue);
|
|
76
|
+
if (!parsedTime) return;
|
|
77
|
+
updateFieldTime(parsedTime);
|
|
78
|
+
};
|
|
79
|
+
const handleHourSelect = (hourLabel) => {
|
|
80
|
+
const selectedHourValue = Number(hourLabel);
|
|
81
|
+
if (Number.isNaN(selectedHourValue)) return;
|
|
82
|
+
updateFieldTime({ hours: selectedHourValue });
|
|
83
|
+
};
|
|
84
|
+
const handleMinuteSelect = (minuteLabel) => {
|
|
85
|
+
const selectedMinuteValue = Number(minuteLabel);
|
|
86
|
+
if (Number.isNaN(selectedMinuteValue)) return;
|
|
87
|
+
updateFieldTime({ minutes: selectedMinuteValue });
|
|
88
|
+
};
|
|
89
|
+
return /* @__PURE__ */ jsx(FormFieldContext.Provider, {
|
|
90
|
+
value: { name },
|
|
91
|
+
children: /* @__PURE__ */ jsxs(FormItem, {
|
|
92
|
+
className,
|
|
93
|
+
id: `date-hour-${name.replace(".", "-")}`,
|
|
94
|
+
children: [
|
|
95
|
+
/* @__PURE__ */ jsxs("div", {
|
|
96
|
+
className: "flex items-end gap-1.5",
|
|
97
|
+
children: [/* @__PURE__ */ jsxs(FormLabel, {
|
|
98
|
+
htmlFor: name,
|
|
99
|
+
children: [
|
|
100
|
+
label,
|
|
101
|
+
":",
|
|
102
|
+
required && /* @__PURE__ */ jsx("span", {
|
|
103
|
+
className: "text-red-500 text-lg leading-px",
|
|
104
|
+
children: "*"
|
|
105
|
+
})
|
|
106
|
+
]
|
|
107
|
+
}), /* @__PURE__ */ jsx(InputHelp, {
|
|
108
|
+
help,
|
|
109
|
+
name
|
|
110
|
+
})]
|
|
111
|
+
}),
|
|
112
|
+
/* @__PURE__ */ jsxs(Popover, {
|
|
113
|
+
modal: true,
|
|
114
|
+
onOpenChange: setIsOpen,
|
|
115
|
+
open: !inputDisabled && isOpen,
|
|
116
|
+
children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
117
|
+
asChild: true,
|
|
118
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
119
|
+
className: "flex w-full items-center",
|
|
120
|
+
onClick: (event) => {
|
|
121
|
+
event.preventDefault();
|
|
122
|
+
event.stopPropagation();
|
|
123
|
+
},
|
|
124
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
125
|
+
className: "w-full",
|
|
126
|
+
children: /* @__PURE__ */ jsx(InputDateField, {
|
|
127
|
+
className: cn("border-r-0! rounded-r-none", fieldState.error && "border-destructive"),
|
|
128
|
+
disabled: inputDisabled,
|
|
129
|
+
field: {
|
|
130
|
+
...field,
|
|
131
|
+
onChange(nextValue) {
|
|
132
|
+
const normalizedDate = normalizeDateValue(nextValue);
|
|
133
|
+
if (!normalizedDate) {
|
|
134
|
+
setTimeValue("");
|
|
135
|
+
field.onChange(void 0);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const mergedDate = mergeDateAndTime(normalizedDate, timeValue, valueAsDate);
|
|
139
|
+
setTimeValue(formatDateTimeAsTime(mergedDate));
|
|
140
|
+
if (valueType === "local-string") {
|
|
141
|
+
field.onChange(formatDateTimeAsLocalString(mergedDate));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
field.onChange(mergedDate);
|
|
145
|
+
},
|
|
146
|
+
value: valueAsDate
|
|
147
|
+
},
|
|
148
|
+
id: name,
|
|
149
|
+
onFocus: () => setIsOpen(true)
|
|
150
|
+
})
|
|
151
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
152
|
+
className: cn("w-28 min-w-28 border-l rounded-l-none", fieldState.error && "border-destructive"),
|
|
153
|
+
disabled: inputDisabled,
|
|
154
|
+
id: `${name}-hour`,
|
|
155
|
+
onChange: (event) => handleTimeInputChange(event.target.value),
|
|
156
|
+
onFocus: () => setIsOpen(true),
|
|
157
|
+
placeholder: hourPlaceholder,
|
|
158
|
+
ref: withMask("99:99", {
|
|
159
|
+
clearMaskOnLostFocus: true,
|
|
160
|
+
showMaskOnFocus: false,
|
|
161
|
+
showMaskOnHover: false,
|
|
162
|
+
undoOnEscape: false
|
|
163
|
+
}),
|
|
164
|
+
value: timeValue
|
|
165
|
+
})]
|
|
166
|
+
})
|
|
167
|
+
}), /* @__PURE__ */ jsxs(PopoverContent, {
|
|
168
|
+
className: "flex w-fit gap-2 p-0 items-center",
|
|
169
|
+
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
170
|
+
children: [/* @__PURE__ */ jsx(Calendar, {
|
|
171
|
+
defaultMonth: valueAsDate || /* @__PURE__ */ new Date(),
|
|
172
|
+
disabled,
|
|
173
|
+
endMonth,
|
|
174
|
+
locale: ptBR,
|
|
175
|
+
mode: "single",
|
|
176
|
+
onSelect: handleDateSelect,
|
|
177
|
+
selected: valueAsDate
|
|
178
|
+
}, `${name}-${valueAsDate?.toString()}-${timeValue}`), /* @__PURE__ */ jsx("div", {
|
|
179
|
+
className: "relative shrink-0 my-auto border-l p-3 pr-3 ",
|
|
180
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
181
|
+
className: "flex gap-2",
|
|
182
|
+
children: [/* @__PURE__ */ jsx(TimeWheelColumn, {
|
|
183
|
+
isOpen,
|
|
184
|
+
label: "Hora",
|
|
185
|
+
onSelect: handleHourSelect,
|
|
186
|
+
options: HOUR_OPTIONS,
|
|
187
|
+
selectedValue: selectedTime?.hours
|
|
188
|
+
}), /* @__PURE__ */ jsx(TimeWheelColumn, {
|
|
189
|
+
isOpen,
|
|
190
|
+
label: "Min",
|
|
191
|
+
onSelect: handleMinuteSelect,
|
|
192
|
+
options: MINUTE_OPTIONS,
|
|
193
|
+
selectedValue: selectedTime?.minutes
|
|
194
|
+
})]
|
|
195
|
+
})
|
|
196
|
+
})]
|
|
197
|
+
})]
|
|
198
|
+
}),
|
|
199
|
+
/* @__PURE__ */ jsx(FormDescription, { children: description }),
|
|
200
|
+
fieldState.error && /* @__PURE__ */ jsx(FormMessage, {})
|
|
201
|
+
]
|
|
202
|
+
})
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
//#endregion
|
|
207
|
+
export { DateHourField };
|
|
208
|
+
//# sourceMappingURL=DateHourField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateHourField.js","names":[],"sources":["../../src/components/date-hour-field/DateHourField.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\n\nimport {\n type FieldPath,\n type FieldValues,\n type UseControllerProps,\n useController,\n useFormContext,\n} from 'react-hook-form';\nimport withMask from '@/hooks/with-mask';\n\nimport { Calendar } from '@/ui/calendar';\nimport {\n FormDescription,\n FormFieldContext,\n FormItem,\n FormLabel,\n FormMessage,\n} from '@/ui/form';\nimport { Input } from '@/ui/input';\nimport { InputDateField } from '@/ui/input-date-field';\nimport { InputHelp } from '@/ui/input-help';\nimport { Popover, PopoverContent, PopoverTrigger } from '@/ui/popover';\n\nimport { cn } from '@/lib/utils';\nimport { ptBR } from 'date-fns/locale';\nimport type { Matcher } from 'react-day-picker';\nimport {\n type ParsedTime,\n HOUR_OPTIONS,\n MINUTE_OPTIONS,\n formatDateTimeAsTime,\n formatDateTimeAsLocalString,\n formatTimeValue,\n getFallbackTime,\n mergeDateAndTime,\n normalizeDateValue,\n parseTime,\n sanitizeTimeInput,\n} from './date-hour-field.utils';\nimport { TimeWheelColumn } from './time-wheel-column';\n\nexport interface DateHourFieldProps<\n TFieldValues extends FieldValues = FieldValues,\n TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> extends Omit<UseControllerProps<TFieldValues, TFieldName>, 'disabled'> {\n label: string;\n description?: string;\n className?: string;\n disabled?: Matcher | Matcher[] | undefined;\n inputDisabled?: boolean;\n initialValue?: Date;\n endMonth?: Date;\n required?: boolean;\n help?: string;\n hourPlaceholder?: string;\n valueType?: 'date' | 'local-string';\n}\n\nfunction DateHourField<\n TFieldValues extends FieldValues = FieldValues,\n TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n name,\n label,\n description,\n inputDisabled,\n endMonth,\n className,\n disabled,\n initialValue,\n required,\n help,\n hourPlaceholder = 'Hora',\n valueType = 'local-string',\n}: DateHourFieldProps<TFieldValues, TFieldName>) {\n const form = useFormContext();\n const { field, fieldState } = useController({\n name,\n defaultValue: initialValue as any,\n rules: { required },\n control: form.control,\n });\n\n const [isOpen, setIsOpen] = useState(false);\n const [timeValue, setTimeValue] = useState('');\n\n const valueAsDate = normalizeDateValue(field.value);\n const selectedTime = parseTime(timeValue);\n\n useEffect(() => {\n if (!valueAsDate) {\n setTimeValue('');\n return;\n }\n\n const nextValue = formatDateTimeAsTime(valueAsDate);\n setTimeValue((currentValue) =>\n currentValue === nextValue ? currentValue : nextValue\n );\n }, [valueAsDate]);\n\n const updateFieldTime = (timePatch: Partial<ParsedTime>) => {\n const fallbackTime = getFallbackTime(valueAsDate);\n const baseTime = selectedTime ?? fallbackTime;\n\n const nextTime = {\n hours: baseTime.hours,\n minutes: baseTime.minutes,\n ...timePatch,\n };\n\n const nextDate = new Date(valueAsDate ?? new Date());\n nextDate.setHours(nextTime.hours, nextTime.minutes, 0, 0);\n\n setTimeValue(formatTimeValue(nextTime.hours, nextTime.minutes));\n if (valueType === 'local-string') {\n field.onChange(formatDateTimeAsLocalString(nextDate));\n return;\n }\n\n field.onChange(nextDate);\n };\n\n const handleDateSelect = (date: Date | undefined) => {\n if (!date) {\n setTimeValue('');\n field.onChange(undefined);\n return;\n }\n\n const mergedDate = mergeDateAndTime(date, timeValue, valueAsDate);\n setTimeValue(formatDateTimeAsTime(mergedDate));\n if (valueType === 'local-string') {\n field.onChange(formatDateTimeAsLocalString(mergedDate));\n return;\n }\n\n field.onChange(mergedDate);\n };\n\n const handleTimeInputChange = (nextValue: string) => {\n const sanitizedValue = sanitizeTimeInput(nextValue);\n setTimeValue(sanitizedValue);\n\n if (sanitizedValue.length !== 5) return;\n\n const parsedTime = parseTime(sanitizedValue);\n if (!parsedTime) return;\n\n updateFieldTime(parsedTime);\n };\n\n const handleHourSelect = (hourLabel: string) => {\n const selectedHourValue = Number(hourLabel);\n if (Number.isNaN(selectedHourValue)) return;\n\n updateFieldTime({ hours: selectedHourValue });\n };\n\n const handleMinuteSelect = (minuteLabel: string) => {\n const selectedMinuteValue = Number(minuteLabel);\n if (Number.isNaN(selectedMinuteValue)) return;\n\n updateFieldTime({ minutes: selectedMinuteValue });\n };\n\n return (\n <FormFieldContext.Provider value={{ name }}>\n <FormItem\n className={className}\n id={`date-hour-${name.replace('.', '-')}`}\n >\n <div className=\"flex items-end gap-1.5\">\n <FormLabel htmlFor={name}>\n {label}:\n {required && (\n <span className=\"text-red-500 text-lg leading-px\">*</span>\n )}\n </FormLabel>\n\n <InputHelp help={help} name={name} />\n </div>\n\n <Popover modal onOpenChange={setIsOpen} open={!inputDisabled && isOpen}>\n <PopoverTrigger asChild>\n <div\n className=\"flex w-full items-center\"\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n }}\n >\n <div className=\"w-full\">\n <InputDateField\n className={cn(\n 'border-r-0! rounded-r-none',\n fieldState.error && 'border-destructive'\n )}\n disabled={inputDisabled}\n field={{\n ...field,\n onChange(nextValue) {\n const normalizedDate = normalizeDateValue(nextValue);\n if (!normalizedDate) {\n setTimeValue('');\n field.onChange(undefined);\n return;\n }\n\n const mergedDate = mergeDateAndTime(\n normalizedDate,\n timeValue,\n valueAsDate\n );\n\n setTimeValue(formatDateTimeAsTime(mergedDate));\n if (valueType === 'local-string') {\n field.onChange(formatDateTimeAsLocalString(mergedDate));\n return;\n }\n\n field.onChange(mergedDate);\n },\n value: valueAsDate,\n }}\n id={name}\n onFocus={() => setIsOpen(true)}\n />\n </div>\n\n <Input\n className={cn(\n 'w-28 min-w-28 border-l rounded-l-none',\n fieldState.error && 'border-destructive'\n )}\n disabled={inputDisabled}\n id={`${name}-hour`}\n onChange={(event) => handleTimeInputChange(event.target.value)}\n onFocus={() => setIsOpen(true)}\n placeholder={hourPlaceholder}\n ref={\n withMask('99:99', {\n clearMaskOnLostFocus: true,\n showMaskOnFocus: false,\n showMaskOnHover: false,\n undoOnEscape: false,\n }) as any\n }\n value={timeValue}\n />\n </div>\n </PopoverTrigger>\n\n <PopoverContent\n className=\"flex w-fit gap-2 p-0 items-center\"\n onOpenAutoFocus={(event) => event.preventDefault()}\n >\n <Calendar\n defaultMonth={valueAsDate || new Date()}\n disabled={disabled}\n endMonth={endMonth}\n key={`${name}-${valueAsDate?.toString()}-${timeValue}`}\n locale={ptBR}\n mode=\"single\"\n onSelect={handleDateSelect}\n selected={valueAsDate}\n />\n\n <div className=\"relative shrink-0 my-auto border-l p-3 pr-3 \">\n <div className=\"flex gap-2\">\n <TimeWheelColumn\n isOpen={isOpen}\n label=\"Hora\"\n onSelect={handleHourSelect}\n options={HOUR_OPTIONS}\n selectedValue={selectedTime?.hours}\n />\n\n <TimeWheelColumn\n isOpen={isOpen}\n label=\"Min\"\n onSelect={handleMinuteSelect}\n options={MINUTE_OPTIONS}\n selectedValue={selectedTime?.minutes}\n />\n </div>\n </div>\n </PopoverContent>\n </Popover>\n\n <FormDescription>{description}</FormDescription>\n {fieldState.error && <FormMessage />}\n </FormItem>\n </FormFieldContext.Provider>\n );\n}\n\nexport { DateHourField };\n"],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { format, isValid, parse, parseISO } from "date-fns";
|
|
2
|
+
|
|
3
|
+
//#region src/components/date-hour-field/date-hour-field.utils.ts
|
|
4
|
+
const HOUR_OPTIONS = Array.from({ length: 24 }, (_, index) => String(index).padStart(2, "0"));
|
|
5
|
+
const MINUTE_OPTIONS = Array.from({ length: 60 }, (_, index) => String(index).padStart(2, "0"));
|
|
6
|
+
const parseTime = (value) => {
|
|
7
|
+
const match = value.match(/^(\d{2}):(\d{2})$/);
|
|
8
|
+
if (!match) return void 0;
|
|
9
|
+
const hours = Number(match[1]);
|
|
10
|
+
const minutes = Number(match[2]);
|
|
11
|
+
if (Number.isNaN(hours) || Number.isNaN(minutes) || hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return;
|
|
12
|
+
return {
|
|
13
|
+
hours,
|
|
14
|
+
minutes
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
const sanitizeTimeInput = (value) => {
|
|
18
|
+
const [hoursRaw = "", minutesRaw = ""] = value.split(":");
|
|
19
|
+
const hasColon = value.includes(":");
|
|
20
|
+
let hours = hoursRaw;
|
|
21
|
+
let minutes = minutesRaw;
|
|
22
|
+
if (hours.length === 2) {
|
|
23
|
+
const parsedHours = Number(hours);
|
|
24
|
+
if (!Number.isNaN(parsedHours) && parsedHours > 23) hours = "23";
|
|
25
|
+
}
|
|
26
|
+
if (minutes.length === 2) {
|
|
27
|
+
const parsedMinutes = Number(minutes);
|
|
28
|
+
if (!Number.isNaN(parsedMinutes) && parsedMinutes > 59) minutes = "59";
|
|
29
|
+
}
|
|
30
|
+
if (!hasColon) return hours;
|
|
31
|
+
return `${hours}:${minutes}`;
|
|
32
|
+
};
|
|
33
|
+
const normalizeDateValue = (value) => {
|
|
34
|
+
if (!value) return void 0;
|
|
35
|
+
if (value instanceof Date) return isValid(value) ? value : void 0;
|
|
36
|
+
if (typeof value === "string") {
|
|
37
|
+
const parsedLocalDateTimeWithSeconds = parse(value, "yyyy-MM-dd HH:mm:ss", /* @__PURE__ */ new Date());
|
|
38
|
+
if (isValid(parsedLocalDateTimeWithSeconds)) return parsedLocalDateTimeWithSeconds;
|
|
39
|
+
const parsedLocalDateTime = parse(value, "yyyy-MM-dd HH:mm", /* @__PURE__ */ new Date());
|
|
40
|
+
if (isValid(parsedLocalDateTime)) return parsedLocalDateTime;
|
|
41
|
+
const parsedIso = parseISO(value);
|
|
42
|
+
if (isValid(parsedIso)) return parsedIso;
|
|
43
|
+
const parsedDateOnly = parse(value, "yyyy-MM-dd", /* @__PURE__ */ new Date());
|
|
44
|
+
if (isValid(parsedDateOnly)) return parsedDateOnly;
|
|
45
|
+
const parsedAsDate = new Date(value);
|
|
46
|
+
if (isValid(parsedAsDate)) return parsedAsDate;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const formatTimeValue = (hours, minutes) => {
|
|
50
|
+
return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
|
|
51
|
+
};
|
|
52
|
+
const formatDateTimeAsTime = (date) => {
|
|
53
|
+
return formatTimeValue(date.getHours(), date.getMinutes());
|
|
54
|
+
};
|
|
55
|
+
const formatDateTimeAsLocalString = (date) => {
|
|
56
|
+
return format(date, "yyyy-MM-dd HH:mm:ss");
|
|
57
|
+
};
|
|
58
|
+
const getFallbackTime = (fallbackDate) => {
|
|
59
|
+
if (fallbackDate) return {
|
|
60
|
+
hours: fallbackDate.getHours(),
|
|
61
|
+
minutes: fallbackDate.getMinutes()
|
|
62
|
+
};
|
|
63
|
+
const now = /* @__PURE__ */ new Date();
|
|
64
|
+
return {
|
|
65
|
+
hours: now.getHours(),
|
|
66
|
+
minutes: now.getMinutes()
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
const mergeDateAndTime = (selectedDate, timeValue, fallbackDate) => {
|
|
70
|
+
const mergedDate = new Date(selectedDate);
|
|
71
|
+
const parsedTime = parseTime(timeValue) ?? getFallbackTime(fallbackDate);
|
|
72
|
+
mergedDate.setHours(parsedTime.hours, parsedTime.minutes, 0, 0);
|
|
73
|
+
return mergedDate;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
//#endregion
|
|
77
|
+
export { HOUR_OPTIONS, MINUTE_OPTIONS, formatDateTimeAsLocalString, formatDateTimeAsTime, formatTimeValue, getFallbackTime, mergeDateAndTime, normalizeDateValue, parseTime, sanitizeTimeInput };
|
|
78
|
+
//# sourceMappingURL=date-hour-field.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-hour-field.utils.js","names":[],"sources":["../../src/components/date-hour-field/date-hour-field.utils.ts"],"sourcesContent":["import { format, isValid, parse, parseISO } from 'date-fns';\n\nexport type ParsedTime = {\n hours: number;\n minutes: number;\n};\n\nexport const HOUR_OPTIONS = Array.from({ length: 24 }, (_, index) =>\n String(index).padStart(2, '0')\n);\n\nexport const MINUTE_OPTIONS = Array.from({ length: 60 }, (_, index) =>\n String(index).padStart(2, '0')\n);\n\nexport const parseTime = (value: string): ParsedTime | undefined => {\n const match = value.match(/^(\\d{2}):(\\d{2})$/);\n if (!match) return undefined;\n\n const hours = Number(match[1]);\n const minutes = Number(match[2]);\n\n if (\n Number.isNaN(hours) ||\n Number.isNaN(minutes) ||\n hours < 0 ||\n hours > 23 ||\n minutes < 0 ||\n minutes > 59\n ) {\n return undefined;\n }\n\n return { hours, minutes };\n};\n\nexport const sanitizeTimeInput = (value: string) => {\n const [hoursRaw = '', minutesRaw = ''] = value.split(':');\n const hasColon = value.includes(':');\n\n let hours = hoursRaw;\n let minutes = minutesRaw;\n\n if (hours.length === 2) {\n const parsedHours = Number(hours);\n if (!Number.isNaN(parsedHours) && parsedHours > 23) {\n hours = '23';\n }\n }\n\n if (minutes.length === 2) {\n const parsedMinutes = Number(minutes);\n if (!Number.isNaN(parsedMinutes) && parsedMinutes > 59) {\n minutes = '59';\n }\n }\n\n if (!hasColon) return hours;\n return `${hours}:${minutes}`;\n};\n\nexport const normalizeDateValue = (value: unknown): Date | undefined => {\n if (!value) return undefined;\n\n if (value instanceof Date) {\n return isValid(value) ? value : undefined;\n }\n\n if (typeof value === 'string') {\n const parsedLocalDateTimeWithSeconds = parse(\n value,\n 'yyyy-MM-dd HH:mm:ss',\n new Date()\n );\n if (isValid(parsedLocalDateTimeWithSeconds)) {\n return parsedLocalDateTimeWithSeconds;\n }\n\n const parsedLocalDateTime = parse(value, 'yyyy-MM-dd HH:mm', new Date());\n if (isValid(parsedLocalDateTime)) {\n return parsedLocalDateTime;\n }\n\n const parsedIso = parseISO(value);\n if (isValid(parsedIso)) return parsedIso;\n\n const parsedDateOnly = parse(value, 'yyyy-MM-dd', new Date());\n if (isValid(parsedDateOnly)) return parsedDateOnly;\n\n const parsedAsDate = new Date(value);\n if (isValid(parsedAsDate)) return parsedAsDate;\n }\n\n return undefined;\n};\n\nexport const formatTimeValue = (hours: number, minutes: number) => {\n return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;\n};\n\nexport const formatDateTimeAsTime = (date: Date) => {\n return formatTimeValue(date.getHours(), date.getMinutes());\n};\n\nexport const formatDateTimeAsLocalString = (date: Date) => {\n return format(date, 'yyyy-MM-dd HH:mm:ss');\n};\n\nexport const getFallbackTime = (fallbackDate?: Date): ParsedTime => {\n if (fallbackDate) {\n return {\n hours: fallbackDate.getHours(),\n minutes: fallbackDate.getMinutes(),\n };\n }\n\n const now = new Date();\n return {\n hours: now.getHours(),\n minutes: now.getMinutes(),\n };\n};\n\nexport const mergeDateAndTime = (\n selectedDate: Date,\n timeValue: string,\n fallbackDate?: Date\n) => {\n const mergedDate = new Date(selectedDate);\n const parsedTime = parseTime(timeValue) ?? getFallbackTime(fallbackDate);\n\n mergedDate.setHours(parsedTime.hours, parsedTime.minutes, 0, 0);\n return mergedDate;\n};\n"],"mappings":";;;AAOA,MAAa,eAAe,MAAM,KAAK,EAAE,QAAQ,IAAI,GAAG,GAAG,UACzD,OAAO,MAAM,CAAC,SAAS,GAAG,IAAI,CAC/B;AAED,MAAa,iBAAiB,MAAM,KAAK,EAAE,QAAQ,IAAI,GAAG,GAAG,UAC3D,OAAO,MAAM,CAAC,SAAS,GAAG,IAAI,CAC/B;AAED,MAAa,aAAa,UAA0C;CAClE,MAAM,QAAQ,MAAM,MAAM,oBAAoB;AAC9C,KAAI,CAAC,MAAO,QAAO;CAEnB,MAAM,QAAQ,OAAO,MAAM,GAAG;CAC9B,MAAM,UAAU,OAAO,MAAM,GAAG;AAEhC,KACE,OAAO,MAAM,MAAM,IACnB,OAAO,MAAM,QAAQ,IACrB,QAAQ,KACR,QAAQ,MACR,UAAU,KACV,UAAU,GAEV;AAGF,QAAO;EAAE;EAAO;EAAS;;AAG3B,MAAa,qBAAqB,UAAkB;CAClD,MAAM,CAAC,WAAW,IAAI,aAAa,MAAM,MAAM,MAAM,IAAI;CACzD,MAAM,WAAW,MAAM,SAAS,IAAI;CAEpC,IAAI,QAAQ;CACZ,IAAI,UAAU;AAEd,KAAI,MAAM,WAAW,GAAG;EACtB,MAAM,cAAc,OAAO,MAAM;AACjC,MAAI,CAAC,OAAO,MAAM,YAAY,IAAI,cAAc,GAC9C,SAAQ;;AAIZ,KAAI,QAAQ,WAAW,GAAG;EACxB,MAAM,gBAAgB,OAAO,QAAQ;AACrC,MAAI,CAAC,OAAO,MAAM,cAAc,IAAI,gBAAgB,GAClD,WAAU;;AAId,KAAI,CAAC,SAAU,QAAO;AACtB,QAAO,GAAG,MAAM,GAAG;;AAGrB,MAAa,sBAAsB,UAAqC;AACtE,KAAI,CAAC,MAAO,QAAO;AAEnB,KAAI,iBAAiB,KACnB,QAAO,QAAQ,MAAM,GAAG,QAAQ;AAGlC,KAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,iCAAiC,MACrC,OACA,uCACA,IAAI,MAAM,CACX;AACD,MAAI,QAAQ,+BAA+B,CACzC,QAAO;EAGT,MAAM,sBAAsB,MAAM,OAAO,oCAAoB,IAAI,MAAM,CAAC;AACxE,MAAI,QAAQ,oBAAoB,CAC9B,QAAO;EAGT,MAAM,YAAY,SAAS,MAAM;AACjC,MAAI,QAAQ,UAAU,CAAE,QAAO;EAE/B,MAAM,iBAAiB,MAAM,OAAO,8BAAc,IAAI,MAAM,CAAC;AAC7D,MAAI,QAAQ,eAAe,CAAE,QAAO;EAEpC,MAAM,eAAe,IAAI,KAAK,MAAM;AACpC,MAAI,QAAQ,aAAa,CAAE,QAAO;;;AAMtC,MAAa,mBAAmB,OAAe,YAAoB;AACjE,QAAO,GAAG,OAAO,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,OAAO,QAAQ,CAAC,SAAS,GAAG,IAAI;;AAG9E,MAAa,wBAAwB,SAAe;AAClD,QAAO,gBAAgB,KAAK,UAAU,EAAE,KAAK,YAAY,CAAC;;AAG5D,MAAa,+BAA+B,SAAe;AACzD,QAAO,OAAO,MAAM,sBAAsB;;AAG5C,MAAa,mBAAmB,iBAAoC;AAClE,KAAI,aACF,QAAO;EACL,OAAO,aAAa,UAAU;EAC9B,SAAS,aAAa,YAAY;EACnC;CAGH,MAAM,sBAAM,IAAI,MAAM;AACtB,QAAO;EACL,OAAO,IAAI,UAAU;EACrB,SAAS,IAAI,YAAY;EAC1B;;AAGH,MAAa,oBACX,cACA,WACA,iBACG;CACH,MAAM,aAAa,IAAI,KAAK,aAAa;CACzC,MAAM,aAAa,UAAU,UAAU,IAAI,gBAAgB,aAAa;AAExE,YAAW,SAAS,WAAW,OAAO,WAAW,SAAS,GAAG,EAAE;AAC/D,QAAO"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { cn } from "../src/lib/utils.js";
|
|
2
|
+
import { useEffect, useMemo } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { WheelGesturesPlugin } from "embla-carousel-wheel-gestures";
|
|
5
|
+
import useEmblaCarousel from "embla-carousel-react";
|
|
6
|
+
|
|
7
|
+
//#region src/components/date-hour-field/time-wheel-column.tsx
|
|
8
|
+
function TimeWheelColumn({ isOpen, label, options, selectedValue, onSelect }) {
|
|
9
|
+
const [emblaRef, emblaApi] = useEmblaCarousel({
|
|
10
|
+
axis: "y",
|
|
11
|
+
align: "center",
|
|
12
|
+
containScroll: "keepSnaps",
|
|
13
|
+
dragFree: true
|
|
14
|
+
}, useMemo(() => [WheelGesturesPlugin()], []));
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!emblaApi || !isOpen) return;
|
|
17
|
+
emblaApi.reInit();
|
|
18
|
+
if (selectedValue === void 0) {
|
|
19
|
+
emblaApi.scrollTo(0, true);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
emblaApi.scrollTo(selectedValue, true);
|
|
23
|
+
}, [
|
|
24
|
+
emblaApi,
|
|
25
|
+
isOpen,
|
|
26
|
+
selectedValue
|
|
27
|
+
]);
|
|
28
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
29
|
+
className: "relative w-16",
|
|
30
|
+
children: [
|
|
31
|
+
/* @__PURE__ */ jsx("p", {
|
|
32
|
+
className: "text-muted-foreground mb-2 text-[11px] font-semibold tracking-[0.12em] uppercase",
|
|
33
|
+
children: label
|
|
34
|
+
}),
|
|
35
|
+
/* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-x-0 top-6 z-20 h-12 bg-linear-to-b from-popover to-transparent" }),
|
|
36
|
+
/* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 z-20 h-12 bg-linear-to-t from-popover to-transparent" }),
|
|
37
|
+
/* @__PURE__ */ jsx("div", {
|
|
38
|
+
className: "h-48 overflow-hidden",
|
|
39
|
+
ref: emblaRef,
|
|
40
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
41
|
+
className: "flex h-full flex-col gap-1 py-4",
|
|
42
|
+
children: options.map((optionLabel) => /* @__PURE__ */ jsx("div", {
|
|
43
|
+
className: "shrink-0 basis-8 ",
|
|
44
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
45
|
+
className: cn("h-full w-full rounded-md text-xs font-medium transition-colors", selectedValue === Number(optionLabel) ? "border-primary bg-primary text-primary-foreground" : "border-border bg-background hover:bg-accent hover:text-accent-foreground"),
|
|
46
|
+
onClick: () => onSelect(optionLabel),
|
|
47
|
+
type: "button",
|
|
48
|
+
children: optionLabel
|
|
49
|
+
})
|
|
50
|
+
}, optionLabel))
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
]
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//#endregion
|
|
58
|
+
export { TimeWheelColumn };
|
|
59
|
+
//# sourceMappingURL=time-wheel-column.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time-wheel-column.js","names":[],"sources":["../../src/components/date-hour-field/time-wheel-column.tsx"],"sourcesContent":["import { WheelGesturesPlugin } from 'embla-carousel-wheel-gestures';\nimport useEmblaCarousel from 'embla-carousel-react';\nimport { useEffect, useMemo } from 'react';\n\nimport { cn } from '@/lib/utils';\n\ntype TimeWheelColumnProps = {\n isOpen: boolean;\n label: string;\n options: string[];\n selectedValue?: number;\n onSelect: (value: string) => void;\n};\n\nfunction TimeWheelColumn({\n isOpen,\n label,\n options,\n selectedValue,\n onSelect,\n}: TimeWheelColumnProps) {\n const plugins = useMemo(() => [WheelGesturesPlugin()], []);\n const [emblaRef, emblaApi] = useEmblaCarousel(\n {\n axis: 'y',\n align: 'center',\n containScroll: 'keepSnaps',\n dragFree: true,\n },\n plugins\n );\n\n useEffect(() => {\n if (!emblaApi || !isOpen) return;\n\n emblaApi.reInit();\n\n if (selectedValue === undefined) {\n emblaApi.scrollTo(0, true);\n return;\n }\n\n emblaApi.scrollTo(selectedValue, true);\n }, [emblaApi, isOpen, selectedValue]);\n\n return (\n <div className=\"relative w-16\">\n <p className=\"text-muted-foreground mb-2 text-[11px] font-semibold tracking-[0.12em] uppercase\">\n {label}\n </p>\n\n <div className=\"pointer-events-none absolute inset-x-0 top-6 z-20 h-12 bg-linear-to-b from-popover to-transparent\" />\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 z-20 h-12 bg-linear-to-t from-popover to-transparent\" />\n\n <div className=\"h-48 overflow-hidden\" ref={emblaRef}>\n <div className=\"flex h-full flex-col gap-1 py-4\">\n {options.map((optionLabel) => (\n <div className=\"shrink-0 basis-8 \" key={optionLabel}>\n <button\n className={cn(\n 'h-full w-full rounded-md text-xs font-medium transition-colors',\n selectedValue === Number(optionLabel)\n ? 'border-primary bg-primary text-primary-foreground'\n : 'border-border bg-background hover:bg-accent hover:text-accent-foreground'\n )}\n onClick={() => onSelect(optionLabel)}\n type=\"button\"\n >\n {optionLabel}\n </button>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nexport { TimeWheelColumn };\n"],"mappings":";;;;;;;AAcA,SAAS,gBAAgB,EACvB,QACA,OACA,SACA,eACA,YACuB;CAEvB,MAAM,CAAC,UAAU,YAAY,iBAC3B;EACE,MAAM;EACN,OAAO;EACP,eAAe;EACf,UAAU;EACX,EAPa,cAAc,CAAC,qBAAqB,CAAC,EAAE,EAAE,CAAC,CASzD;AAED,iBAAgB;AACd,MAAI,CAAC,YAAY,CAAC,OAAQ;AAE1B,WAAS,QAAQ;AAEjB,MAAI,kBAAkB,QAAW;AAC/B,YAAS,SAAS,GAAG,KAAK;AAC1B;;AAGF,WAAS,SAAS,eAAe,KAAK;IACrC;EAAC;EAAU;EAAQ;EAAc,CAAC;AAErC,QACE,qBAAC;EAAI,WAAU;;GACb,oBAAC;IAAE,WAAU;cACV;KACC;GAEJ,oBAAC,SAAI,WAAU,sGAAsG;GACrH,oBAAC,SAAI,WAAU,yGAAyG;GAExH,oBAAC;IAAI,WAAU;IAAuB,KAAK;cACzC,oBAAC;KAAI,WAAU;eACZ,QAAQ,KAAK,gBACZ,oBAAC;MAAI,WAAU;gBACb,oBAAC;OACC,WAAW,GACT,mEACA,kBAAkB,OAAO,YAAY,GACjC,sDACA,2EACL;OACD,eAAe,SAAS,YAAY;OACpC,MAAK;iBAEJ;QACM;QAZ6B,YAalC,CACN;MACE;KACF;;GACF"}
|
|
@@ -1,35 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { Matcher } from "react-day-picker";
|
|
4
|
-
|
|
5
|
-
//#region src/components/date-hour-field/DateHourField.d.ts
|
|
6
|
-
interface DateHourFieldProps<TFieldValues extends FieldValues = FieldValues, TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> extends Omit<UseControllerProps<TFieldValues, TFieldName>, 'disabled'> {
|
|
7
|
-
label: string;
|
|
8
|
-
description?: string;
|
|
9
|
-
className?: string;
|
|
10
|
-
disabled?: Matcher | Matcher[] | undefined;
|
|
11
|
-
inputDisabled?: boolean;
|
|
12
|
-
initialValue?: Date;
|
|
13
|
-
endMonth?: Date;
|
|
14
|
-
required?: boolean;
|
|
15
|
-
help?: string;
|
|
16
|
-
hourPlaceholder?: string;
|
|
17
|
-
valueType?: 'date' | 'local-string';
|
|
18
|
-
}
|
|
19
|
-
declare function DateHourField<TFieldValues extends FieldValues = FieldValues, TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({
|
|
20
|
-
name,
|
|
21
|
-
label,
|
|
22
|
-
description,
|
|
23
|
-
inputDisabled,
|
|
24
|
-
endMonth,
|
|
25
|
-
className,
|
|
26
|
-
disabled,
|
|
27
|
-
initialValue,
|
|
28
|
-
required,
|
|
29
|
-
help,
|
|
30
|
-
hourPlaceholder,
|
|
31
|
-
valueType
|
|
32
|
-
}: DateHourFieldProps<TFieldValues, TFieldName>): react_jsx_runtime1.JSX.Element;
|
|
33
|
-
//#endregion
|
|
34
|
-
export { DateHourField, type DateHourFieldProps };
|
|
35
|
-
//# sourceMappingURL=date-hour-field.d.ts.map
|
|
1
|
+
import { DateHourField, DateHourFieldProps } from "./date-hour-field/DateHourField.js";
|
|
2
|
+
export { DateHourField, type DateHourFieldProps };
|