@roy-ui/ui 0.0.7 → 0.0.8
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/{Button-XBBWB5ZT.css → Button-OZLAH5NO.css} +60 -13
- package/dist/Table-qVdGZkB4.d.ts +42 -0
- package/dist/TimePicker-BhRta4MK.d.ts +39 -0
- package/dist/chunk-4SGMAZBG.js +161 -0
- package/dist/chunk-4SGMAZBG.js.map +1 -0
- package/dist/chunk-5CIBIH7R.js +98 -0
- package/dist/chunk-5CIBIH7R.js.map +1 -0
- package/dist/chunk-75IGGPXL.js +518 -0
- package/dist/chunk-75IGGPXL.js.map +1 -0
- package/dist/chunk-C5X3TE5U.js +87 -0
- package/dist/chunk-C5X3TE5U.js.map +1 -0
- package/dist/chunk-HUCK7AM7.js +840 -0
- package/dist/chunk-HUCK7AM7.js.map +1 -0
- package/dist/chunk-KSHKVSNK.js +82 -0
- package/dist/chunk-KSHKVSNK.js.map +1 -0
- package/dist/chunk-M6HB6BMA.js +101 -0
- package/dist/chunk-M6HB6BMA.js.map +1 -0
- package/dist/chunk-MDPMEW4K.js +58 -0
- package/dist/chunk-MDPMEW4K.js.map +1 -0
- package/dist/chunk-PGV55XSZ.js +107 -0
- package/dist/chunk-PGV55XSZ.js.map +1 -0
- package/dist/chunk-RLBVY3DG.js +64 -0
- package/dist/chunk-RLBVY3DG.js.map +1 -0
- package/dist/chunk-SFENGB5N.js +410 -0
- package/dist/chunk-SFENGB5N.js.map +1 -0
- package/dist/chunk-XERZVDIT.js +194 -0
- package/dist/chunk-XERZVDIT.js.map +1 -0
- package/dist/components/button/index.d.ts +37 -0
- package/dist/components/button/index.js +4 -0
- package/dist/components/button/index.js.map +1 -0
- package/dist/components/data-table/index.d.ts +145 -0
- package/dist/components/data-table/index.js +9 -0
- package/dist/components/data-table/index.js.map +1 -0
- package/dist/components/date-range-picker/index.d.ts +30 -0
- package/dist/components/date-range-picker/index.js +4 -0
- package/dist/components/date-range-picker/index.js.map +1 -0
- package/dist/components/gradient-button/index.d.ts +12 -0
- package/dist/components/gradient-button/index.js +4 -0
- package/dist/components/gradient-button/index.js.map +1 -0
- package/dist/components/made-by/index.d.ts +23 -0
- package/dist/components/made-by/index.js +4 -0
- package/dist/components/made-by/index.js.map +1 -0
- package/dist/components/pagination/index.d.ts +23 -0
- package/dist/components/pagination/index.js +4 -0
- package/dist/components/pagination/index.js.map +1 -0
- package/dist/components/popover/index.d.ts +18 -0
- package/dist/components/popover/index.js +4 -0
- package/dist/components/popover/index.js.map +1 -0
- package/dist/components/table/index.d.ts +28 -0
- package/dist/components/table/index.js +4 -0
- package/dist/components/table/index.js.map +1 -0
- package/dist/components/table-search/index.d.ts +19 -0
- package/dist/components/table-search/index.js +4 -0
- package/dist/components/table-search/index.js.map +1 -0
- package/dist/components/text-morph/index.d.ts +28 -0
- package/dist/components/text-morph/index.js +4 -0
- package/dist/components/text-morph/index.js.map +1 -0
- package/dist/components/time-picker/index.d.ts +14 -0
- package/dist/components/time-picker/index.js +4 -0
- package/dist/components/time-picker/index.js.map +1 -0
- package/dist/components/tree-nav/index.d.ts +30 -0
- package/dist/components/tree-nav/index.js +4 -0
- package/dist/components/tree-nav/index.js.map +1 -0
- package/dist/dateUtils-B_m_EICl.d.ts +14 -0
- package/dist/index.d.ts +17 -422
- package/dist/index.js +12 -2519
- package/dist/index.js.map +1 -1
- package/package.json +51 -2
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useRef, useState, useCallback, useEffect } from 'react';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
import './TimePicker-44EKHQEJ.css';
|
|
5
|
+
|
|
6
|
+
// src/components/time-picker/TimePicker.tsx
|
|
7
|
+
function angleFromCenter(cx, cy, px, py) {
|
|
8
|
+
const dx = px - cx;
|
|
9
|
+
const dy = py - cy;
|
|
10
|
+
const a = Math.atan2(dy, dx) * (180 / Math.PI) + 90;
|
|
11
|
+
return (a + 360) % 360;
|
|
12
|
+
}
|
|
13
|
+
function AnalogClock({
|
|
14
|
+
value,
|
|
15
|
+
onChange,
|
|
16
|
+
hourCycle = 24,
|
|
17
|
+
minuteStep = 1,
|
|
18
|
+
size = 220
|
|
19
|
+
}) {
|
|
20
|
+
const ref = useRef(null);
|
|
21
|
+
const [mode, setMode] = useState("hours");
|
|
22
|
+
const [dragging, setDragging] = useState(false);
|
|
23
|
+
const updateFromPointer = useCallback(
|
|
24
|
+
(clientX, clientY, currentMode) => {
|
|
25
|
+
const svg = ref.current;
|
|
26
|
+
if (!svg) return;
|
|
27
|
+
const rect = svg.getBoundingClientRect();
|
|
28
|
+
const cx2 = rect.left + rect.width / 2;
|
|
29
|
+
const cy2 = rect.top + rect.height / 2;
|
|
30
|
+
const a = angleFromCenter(cx2, cy2, clientX, clientY);
|
|
31
|
+
if (currentMode === "hours") {
|
|
32
|
+
const hours122 = Math.round(a / 30) % 12;
|
|
33
|
+
const isAm2 = value.hours < 12;
|
|
34
|
+
const h24 = isAm2 ? hours122 : hours122 + 12;
|
|
35
|
+
onChange({ ...value, hours: h24 });
|
|
36
|
+
} else {
|
|
37
|
+
let m = Math.round(a / 6) % 60;
|
|
38
|
+
if (minuteStep > 1) m = Math.round(m / minuteStep) * minuteStep;
|
|
39
|
+
if (m === 60) m = 0;
|
|
40
|
+
onChange({ ...value, minutes: m });
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
[minuteStep, onChange, value]
|
|
44
|
+
);
|
|
45
|
+
const togglePeriod = () => {
|
|
46
|
+
const next = value.hours < 12 ? value.hours + 12 : value.hours - 12;
|
|
47
|
+
onChange({ ...value, hours: next });
|
|
48
|
+
};
|
|
49
|
+
const isAm = value.hours < 12;
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!dragging) return;
|
|
52
|
+
const onMove = (e) => updateFromPointer(e.clientX, e.clientY, mode);
|
|
53
|
+
const onUp = () => setDragging(false);
|
|
54
|
+
window.addEventListener("pointermove", onMove);
|
|
55
|
+
window.addEventListener("pointerup", onUp);
|
|
56
|
+
return () => {
|
|
57
|
+
window.removeEventListener("pointermove", onMove);
|
|
58
|
+
window.removeEventListener("pointerup", onUp);
|
|
59
|
+
};
|
|
60
|
+
}, [dragging, mode, updateFromPointer]);
|
|
61
|
+
const handlePointerDown = (e) => {
|
|
62
|
+
e.preventDefault();
|
|
63
|
+
setDragging(true);
|
|
64
|
+
updateFromPointer(e.clientX, e.clientY, mode);
|
|
65
|
+
};
|
|
66
|
+
const hours12 = hourCycle === 12 ? value.hours === 0 ? 12 : value.hours > 12 ? value.hours - 12 : value.hours : value.hours % 12 === 0 ? 12 : value.hours % 12;
|
|
67
|
+
const hourAngle = (hours12 % 12 + value.minutes / 60) * 30 - 90;
|
|
68
|
+
const minuteAngle = value.minutes / 60 * 360 - 90;
|
|
69
|
+
const r = size / 2;
|
|
70
|
+
const cx = r;
|
|
71
|
+
const cy = r;
|
|
72
|
+
const hourLen = r * 0.45;
|
|
73
|
+
const minuteLen = r * 0.7;
|
|
74
|
+
const tickOuter = r * 0.92;
|
|
75
|
+
const tickInner = r * 0.86;
|
|
76
|
+
const majorInner = r * 0.82;
|
|
77
|
+
const handX = (len, deg) => cx + len * Math.cos(deg * Math.PI / 180);
|
|
78
|
+
const handY = (len, deg) => cy + len * Math.sin(deg * Math.PI / 180);
|
|
79
|
+
return /* @__PURE__ */ jsxs("div", { className: "royui-tp-analog", children: [
|
|
80
|
+
/* @__PURE__ */ jsxs(
|
|
81
|
+
"svg",
|
|
82
|
+
{
|
|
83
|
+
ref,
|
|
84
|
+
width: size,
|
|
85
|
+
height: size,
|
|
86
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
87
|
+
onPointerDown: handlePointerDown,
|
|
88
|
+
className: "royui-tp-analog__face",
|
|
89
|
+
role: "application",
|
|
90
|
+
"aria-label": "Analog clock",
|
|
91
|
+
children: [
|
|
92
|
+
/* @__PURE__ */ jsx(
|
|
93
|
+
"circle",
|
|
94
|
+
{
|
|
95
|
+
cx,
|
|
96
|
+
cy,
|
|
97
|
+
r: r - 1,
|
|
98
|
+
className: "royui-tp-analog__bezel"
|
|
99
|
+
}
|
|
100
|
+
),
|
|
101
|
+
Array.from({ length: 60 }).map((_, i) => {
|
|
102
|
+
const angle = (i * 6 - 90) * (Math.PI / 180);
|
|
103
|
+
const isMajor = i % 5 === 0;
|
|
104
|
+
const inner = isMajor ? majorInner : tickInner;
|
|
105
|
+
return /* @__PURE__ */ jsx(
|
|
106
|
+
"line",
|
|
107
|
+
{
|
|
108
|
+
x1: cx + inner * Math.cos(angle),
|
|
109
|
+
y1: cy + inner * Math.sin(angle),
|
|
110
|
+
x2: cx + tickOuter * Math.cos(angle),
|
|
111
|
+
y2: cy + tickOuter * Math.sin(angle),
|
|
112
|
+
className: isMajor ? "royui-tp-analog__tick--major" : "royui-tp-analog__tick"
|
|
113
|
+
},
|
|
114
|
+
i
|
|
115
|
+
);
|
|
116
|
+
}),
|
|
117
|
+
[12, 3, 6, 9].map((n) => {
|
|
118
|
+
const idx = n % 12;
|
|
119
|
+
const angle = (idx * 30 - 90) * (Math.PI / 180);
|
|
120
|
+
const rr = r * 0.72;
|
|
121
|
+
return /* @__PURE__ */ jsx(
|
|
122
|
+
"text",
|
|
123
|
+
{
|
|
124
|
+
x: cx + rr * Math.cos(angle),
|
|
125
|
+
y: cy + rr * Math.sin(angle),
|
|
126
|
+
dy: "0.34em",
|
|
127
|
+
textAnchor: "middle",
|
|
128
|
+
className: "royui-tp-analog__numeral",
|
|
129
|
+
children: n
|
|
130
|
+
},
|
|
131
|
+
n
|
|
132
|
+
);
|
|
133
|
+
}),
|
|
134
|
+
/* @__PURE__ */ jsx(
|
|
135
|
+
"line",
|
|
136
|
+
{
|
|
137
|
+
x1: cx,
|
|
138
|
+
y1: cy,
|
|
139
|
+
x2: handX(hourLen, hourAngle),
|
|
140
|
+
y2: handY(hourLen, hourAngle),
|
|
141
|
+
className: `royui-tp-analog__hand royui-tp-analog__hand--hour ${mode === "hours" ? "royui-tp-analog__hand--active" : ""}`,
|
|
142
|
+
onPointerDown: (e) => {
|
|
143
|
+
e.stopPropagation();
|
|
144
|
+
setMode("hours");
|
|
145
|
+
setDragging(true);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
),
|
|
149
|
+
/* @__PURE__ */ jsx(
|
|
150
|
+
"line",
|
|
151
|
+
{
|
|
152
|
+
x1: cx,
|
|
153
|
+
y1: cy,
|
|
154
|
+
x2: handX(minuteLen, minuteAngle),
|
|
155
|
+
y2: handY(minuteLen, minuteAngle),
|
|
156
|
+
className: `royui-tp-analog__hand royui-tp-analog__hand--minute ${mode === "minutes" ? "royui-tp-analog__hand--active" : ""}`,
|
|
157
|
+
onPointerDown: (e) => {
|
|
158
|
+
e.stopPropagation();
|
|
159
|
+
setMode("minutes");
|
|
160
|
+
setDragging(true);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
),
|
|
164
|
+
/* @__PURE__ */ jsx("circle", { cx, cy, r: 3.5, className: "royui-tp-analog__pin" })
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
),
|
|
168
|
+
/* @__PURE__ */ jsxs("div", { className: "royui-tp-analog__modes", children: [
|
|
169
|
+
/* @__PURE__ */ jsx(
|
|
170
|
+
"button",
|
|
171
|
+
{
|
|
172
|
+
type: "button",
|
|
173
|
+
className: [
|
|
174
|
+
"royui-tp-analog__mode",
|
|
175
|
+
mode === "hours" && "royui-tp-analog__mode--on"
|
|
176
|
+
].filter(Boolean).join(" "),
|
|
177
|
+
onClick: () => setMode("hours"),
|
|
178
|
+
children: "Hours"
|
|
179
|
+
}
|
|
180
|
+
),
|
|
181
|
+
/* @__PURE__ */ jsx(
|
|
182
|
+
"button",
|
|
183
|
+
{
|
|
184
|
+
type: "button",
|
|
185
|
+
className: [
|
|
186
|
+
"royui-tp-analog__mode",
|
|
187
|
+
mode === "minutes" && "royui-tp-analog__mode--on"
|
|
188
|
+
].filter(Boolean).join(" "),
|
|
189
|
+
onClick: () => setMode("minutes"),
|
|
190
|
+
children: "Minutes"
|
|
191
|
+
}
|
|
192
|
+
),
|
|
193
|
+
/* @__PURE__ */ jsxs("div", { className: "royui-tp-analog__period", role: "group", "aria-label": "Day half", children: [
|
|
194
|
+
/* @__PURE__ */ jsx(
|
|
195
|
+
"button",
|
|
196
|
+
{
|
|
197
|
+
type: "button",
|
|
198
|
+
className: [
|
|
199
|
+
"royui-tp-analog__period-btn",
|
|
200
|
+
isAm && "royui-tp-analog__period-btn--on"
|
|
201
|
+
].filter(Boolean).join(" "),
|
|
202
|
+
onClick: () => {
|
|
203
|
+
if (!isAm) togglePeriod();
|
|
204
|
+
},
|
|
205
|
+
"aria-pressed": isAm,
|
|
206
|
+
children: "AM"
|
|
207
|
+
}
|
|
208
|
+
),
|
|
209
|
+
/* @__PURE__ */ jsx(
|
|
210
|
+
"button",
|
|
211
|
+
{
|
|
212
|
+
type: "button",
|
|
213
|
+
className: [
|
|
214
|
+
"royui-tp-analog__period-btn",
|
|
215
|
+
!isAm && "royui-tp-analog__period-btn--on"
|
|
216
|
+
].filter(Boolean).join(" "),
|
|
217
|
+
onClick: () => {
|
|
218
|
+
if (isAm) togglePeriod();
|
|
219
|
+
},
|
|
220
|
+
"aria-pressed": !isAm,
|
|
221
|
+
children: "PM"
|
|
222
|
+
}
|
|
223
|
+
)
|
|
224
|
+
] })
|
|
225
|
+
] })
|
|
226
|
+
] });
|
|
227
|
+
}
|
|
228
|
+
function pad(n) {
|
|
229
|
+
return String(n).padStart(2, "0");
|
|
230
|
+
}
|
|
231
|
+
function wrap(n, max) {
|
|
232
|
+
return (n % max + max) % max;
|
|
233
|
+
}
|
|
234
|
+
function DigitalClock({
|
|
235
|
+
value,
|
|
236
|
+
onChange,
|
|
237
|
+
hourCycle = 24,
|
|
238
|
+
minuteStep = 1
|
|
239
|
+
}) {
|
|
240
|
+
const hourBoundary = hourCycle === 12 ? 12 : 24;
|
|
241
|
+
const displayHour = hourCycle === 12 ? value.hours % 12 === 0 ? 12 : value.hours % 12 : value.hours;
|
|
242
|
+
const isAm = value.hours < 12;
|
|
243
|
+
const setDisplayHour = (next) => {
|
|
244
|
+
if (hourCycle === 24) {
|
|
245
|
+
onChange({ ...value, hours: wrap(next, 24) });
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
let h = next;
|
|
249
|
+
if (h <= 0) h = 12;
|
|
250
|
+
if (h > 12) h = 1;
|
|
251
|
+
const h24 = isAm ? h === 12 ? 0 : h : h === 12 ? 12 : h + 12;
|
|
252
|
+
onChange({ ...value, hours: h24 });
|
|
253
|
+
};
|
|
254
|
+
const setMinutes = (next) => {
|
|
255
|
+
const stepped = Math.round(next / minuteStep) * minuteStep;
|
|
256
|
+
onChange({ ...value, minutes: wrap(stepped, 60) });
|
|
257
|
+
};
|
|
258
|
+
const setAm = () => {
|
|
259
|
+
if (isAm) return;
|
|
260
|
+
onChange({ ...value, hours: value.hours - 12 });
|
|
261
|
+
};
|
|
262
|
+
const setPm = () => {
|
|
263
|
+
if (!isAm) return;
|
|
264
|
+
onChange({ ...value, hours: value.hours + 12 });
|
|
265
|
+
};
|
|
266
|
+
return /* @__PURE__ */ jsxs("div", { className: "royui-tp-digital", children: [
|
|
267
|
+
/* @__PURE__ */ jsxs("div", { className: "royui-tp-digital__row", children: [
|
|
268
|
+
/* @__PURE__ */ jsx(
|
|
269
|
+
Segment,
|
|
270
|
+
{
|
|
271
|
+
label: "Hours",
|
|
272
|
+
value: pad(displayHour),
|
|
273
|
+
onWheelStep: (d) => setDisplayHour(displayHour + d),
|
|
274
|
+
onArrow: (d) => setDisplayHour(displayHour + d),
|
|
275
|
+
max: hourBoundary
|
|
276
|
+
}
|
|
277
|
+
),
|
|
278
|
+
/* @__PURE__ */ jsx("span", { className: "royui-tp-digital__sep", "aria-hidden": true, children: ":" }),
|
|
279
|
+
/* @__PURE__ */ jsx(
|
|
280
|
+
Segment,
|
|
281
|
+
{
|
|
282
|
+
label: "Minutes",
|
|
283
|
+
value: pad(value.minutes),
|
|
284
|
+
onWheelStep: (d) => setMinutes(value.minutes + d * minuteStep),
|
|
285
|
+
onArrow: (d) => setMinutes(value.minutes + d * minuteStep),
|
|
286
|
+
max: 60
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
] }),
|
|
290
|
+
/* @__PURE__ */ jsxs("div", { className: "royui-tp-digital__period", role: "group", "aria-label": "Day half", children: [
|
|
291
|
+
/* @__PURE__ */ jsx(
|
|
292
|
+
"button",
|
|
293
|
+
{
|
|
294
|
+
type: "button",
|
|
295
|
+
className: [
|
|
296
|
+
"royui-tp-digital__period-btn",
|
|
297
|
+
isAm && "royui-tp-digital__period-btn--on"
|
|
298
|
+
].filter(Boolean).join(" "),
|
|
299
|
+
onClick: setAm,
|
|
300
|
+
"aria-pressed": isAm,
|
|
301
|
+
children: "AM"
|
|
302
|
+
}
|
|
303
|
+
),
|
|
304
|
+
/* @__PURE__ */ jsx(
|
|
305
|
+
"button",
|
|
306
|
+
{
|
|
307
|
+
type: "button",
|
|
308
|
+
className: [
|
|
309
|
+
"royui-tp-digital__period-btn",
|
|
310
|
+
!isAm && "royui-tp-digital__period-btn--on"
|
|
311
|
+
].filter(Boolean).join(" "),
|
|
312
|
+
onClick: setPm,
|
|
313
|
+
"aria-pressed": !isAm,
|
|
314
|
+
children: "PM"
|
|
315
|
+
}
|
|
316
|
+
)
|
|
317
|
+
] }),
|
|
318
|
+
/* @__PURE__ */ jsx("div", { className: "royui-tp-digital__hint", children: "Scroll or use arrow keys" })
|
|
319
|
+
] });
|
|
320
|
+
}
|
|
321
|
+
function Segment({
|
|
322
|
+
label,
|
|
323
|
+
value,
|
|
324
|
+
onWheelStep,
|
|
325
|
+
onArrow,
|
|
326
|
+
max
|
|
327
|
+
}) {
|
|
328
|
+
const ref = useRef(null);
|
|
329
|
+
const handleWheel = (e) => {
|
|
330
|
+
e.preventDefault();
|
|
331
|
+
onWheelStep(e.deltaY > 0 ? 1 : -1);
|
|
332
|
+
};
|
|
333
|
+
const handleKey = (e) => {
|
|
334
|
+
if (e.key === "ArrowUp" || e.key === "ArrowRight") {
|
|
335
|
+
e.preventDefault();
|
|
336
|
+
onArrow(1);
|
|
337
|
+
} else if (e.key === "ArrowDown" || e.key === "ArrowLeft") {
|
|
338
|
+
e.preventDefault();
|
|
339
|
+
onArrow(-1);
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
return /* @__PURE__ */ jsx(
|
|
343
|
+
"div",
|
|
344
|
+
{
|
|
345
|
+
ref,
|
|
346
|
+
role: "spinbutton",
|
|
347
|
+
tabIndex: 0,
|
|
348
|
+
"aria-label": label,
|
|
349
|
+
"aria-valuetext": value,
|
|
350
|
+
"aria-valuemax": max,
|
|
351
|
+
className: "royui-tp-digital__seg",
|
|
352
|
+
onWheel: handleWheel,
|
|
353
|
+
onKeyDown: handleKey,
|
|
354
|
+
children: value
|
|
355
|
+
}
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
function pad2(n) {
|
|
359
|
+
return String(n).padStart(2, "0");
|
|
360
|
+
}
|
|
361
|
+
function formatTime(t, hourCycle = 24) {
|
|
362
|
+
if (!t) return "";
|
|
363
|
+
if (hourCycle === 24) return `${pad2(t.hours)}:${pad2(t.minutes)}`;
|
|
364
|
+
const h = t.hours % 12 === 0 ? 12 : t.hours % 12;
|
|
365
|
+
const ampm = t.hours < 12 ? "AM" : "PM";
|
|
366
|
+
return `${pad2(h)}:${pad2(t.minutes)} ${ampm}`;
|
|
367
|
+
}
|
|
368
|
+
function TimePicker({
|
|
369
|
+
value,
|
|
370
|
+
defaultValue,
|
|
371
|
+
onChange,
|
|
372
|
+
variant = "analog",
|
|
373
|
+
switchable = true,
|
|
374
|
+
hourCycle = 24,
|
|
375
|
+
minuteStep = 1,
|
|
376
|
+
placeholder = "Pick a time",
|
|
377
|
+
align = "left",
|
|
378
|
+
className = "",
|
|
379
|
+
style,
|
|
380
|
+
triggerLabel,
|
|
381
|
+
disabled
|
|
382
|
+
}) {
|
|
383
|
+
const controlled = value !== void 0;
|
|
384
|
+
const [internal, setInternal] = useState(defaultValue ?? null);
|
|
385
|
+
const current = controlled ? value : internal;
|
|
386
|
+
const [open, setOpen] = useState(false);
|
|
387
|
+
const [mode, setMode] = useState(variant);
|
|
388
|
+
const [draft, setDraft] = useState(current ?? { hours: 12, minutes: 0 });
|
|
389
|
+
const wrap2 = useRef(null);
|
|
390
|
+
useEffect(() => {
|
|
391
|
+
if (!open) return;
|
|
392
|
+
function onDown(e) {
|
|
393
|
+
if (wrap2.current && !wrap2.current.contains(e.target)) {
|
|
394
|
+
setOpen(false);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
function onKey(e) {
|
|
398
|
+
if (e.key === "Escape") setOpen(false);
|
|
399
|
+
}
|
|
400
|
+
document.addEventListener("mousedown", onDown);
|
|
401
|
+
document.addEventListener("keydown", onKey);
|
|
402
|
+
return () => {
|
|
403
|
+
document.removeEventListener("mousedown", onDown);
|
|
404
|
+
document.removeEventListener("keydown", onKey);
|
|
405
|
+
};
|
|
406
|
+
}, [open]);
|
|
407
|
+
useEffect(() => {
|
|
408
|
+
if (open) setDraft(current ?? { hours: (/* @__PURE__ */ new Date()).getHours(), minutes: 0 });
|
|
409
|
+
}, [open]);
|
|
410
|
+
useEffect(() => {
|
|
411
|
+
setMode(variant);
|
|
412
|
+
}, [variant]);
|
|
413
|
+
const commit = (next) => {
|
|
414
|
+
if (!controlled) setInternal(next);
|
|
415
|
+
onChange?.(next);
|
|
416
|
+
};
|
|
417
|
+
const setNow = () => {
|
|
418
|
+
const now = /* @__PURE__ */ new Date();
|
|
419
|
+
setDraft({ hours: now.getHours(), minutes: now.getMinutes() });
|
|
420
|
+
};
|
|
421
|
+
const apply = () => {
|
|
422
|
+
commit(draft);
|
|
423
|
+
setOpen(false);
|
|
424
|
+
};
|
|
425
|
+
return /* @__PURE__ */ jsxs(
|
|
426
|
+
"div",
|
|
427
|
+
{
|
|
428
|
+
ref: wrap2,
|
|
429
|
+
className: ["royui-tp", className].filter(Boolean).join(" "),
|
|
430
|
+
style,
|
|
431
|
+
children: [
|
|
432
|
+
/* @__PURE__ */ jsxs(
|
|
433
|
+
"button",
|
|
434
|
+
{
|
|
435
|
+
type: "button",
|
|
436
|
+
className: "royui-tp__trigger",
|
|
437
|
+
onClick: () => !disabled && setOpen((o) => !o),
|
|
438
|
+
"aria-haspopup": "dialog",
|
|
439
|
+
"aria-expanded": open,
|
|
440
|
+
disabled,
|
|
441
|
+
children: [
|
|
442
|
+
/* @__PURE__ */ jsx("span", { className: "royui-tp__trigger-dot", "aria-hidden": true }),
|
|
443
|
+
/* @__PURE__ */ jsx("span", { className: "royui-tp__trigger-label", children: triggerLabel ?? (formatTime(current ?? null, hourCycle) || placeholder) })
|
|
444
|
+
]
|
|
445
|
+
}
|
|
446
|
+
),
|
|
447
|
+
open && /* @__PURE__ */ jsxs(
|
|
448
|
+
"div",
|
|
449
|
+
{
|
|
450
|
+
className: `royui-tp__panel royui-tp__panel--${align}`,
|
|
451
|
+
role: "dialog",
|
|
452
|
+
"aria-label": "Choose time",
|
|
453
|
+
children: [
|
|
454
|
+
/* @__PURE__ */ jsxs("div", { className: "royui-tp__head", children: [
|
|
455
|
+
/* @__PURE__ */ jsx("div", { className: "royui-tp__readout", children: formatTime(draft, hourCycle) }),
|
|
456
|
+
switchable && /* @__PURE__ */ jsxs("div", { className: "royui-tp__variants", role: "tablist", children: [
|
|
457
|
+
/* @__PURE__ */ jsx(
|
|
458
|
+
"button",
|
|
459
|
+
{
|
|
460
|
+
type: "button",
|
|
461
|
+
role: "tab",
|
|
462
|
+
"aria-selected": mode === "analog",
|
|
463
|
+
className: [
|
|
464
|
+
"royui-tp__variant",
|
|
465
|
+
mode === "analog" && "royui-tp__variant--on"
|
|
466
|
+
].filter(Boolean).join(" "),
|
|
467
|
+
onClick: () => setMode("analog"),
|
|
468
|
+
children: "Analog"
|
|
469
|
+
}
|
|
470
|
+
),
|
|
471
|
+
/* @__PURE__ */ jsx(
|
|
472
|
+
"button",
|
|
473
|
+
{
|
|
474
|
+
type: "button",
|
|
475
|
+
role: "tab",
|
|
476
|
+
"aria-selected": mode === "digital",
|
|
477
|
+
className: [
|
|
478
|
+
"royui-tp__variant",
|
|
479
|
+
mode === "digital" && "royui-tp__variant--on"
|
|
480
|
+
].filter(Boolean).join(" "),
|
|
481
|
+
onClick: () => setMode("digital"),
|
|
482
|
+
children: "Digital"
|
|
483
|
+
}
|
|
484
|
+
)
|
|
485
|
+
] })
|
|
486
|
+
] }),
|
|
487
|
+
/* @__PURE__ */ jsx("div", { className: "royui-tp__body", children: mode === "analog" ? /* @__PURE__ */ jsx(
|
|
488
|
+
AnalogClock,
|
|
489
|
+
{
|
|
490
|
+
value: draft,
|
|
491
|
+
onChange: setDraft,
|
|
492
|
+
hourCycle,
|
|
493
|
+
minuteStep
|
|
494
|
+
}
|
|
495
|
+
) : /* @__PURE__ */ jsx(
|
|
496
|
+
DigitalClock,
|
|
497
|
+
{
|
|
498
|
+
value: draft,
|
|
499
|
+
onChange: setDraft,
|
|
500
|
+
hourCycle,
|
|
501
|
+
minuteStep
|
|
502
|
+
}
|
|
503
|
+
) }),
|
|
504
|
+
/* @__PURE__ */ jsxs("div", { className: "royui-tp__foot", children: [
|
|
505
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "royui-tp__ghost", onClick: setNow, children: "Now" }),
|
|
506
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "royui-tp__primary", onClick: apply, children: "Apply" })
|
|
507
|
+
] })
|
|
508
|
+
]
|
|
509
|
+
}
|
|
510
|
+
)
|
|
511
|
+
]
|
|
512
|
+
}
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
export { AnalogClock, DigitalClock, TimePicker, formatTime };
|
|
517
|
+
//# sourceMappingURL=chunk-75IGGPXL.js.map
|
|
518
|
+
//# sourceMappingURL=chunk-75IGGPXL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/time-picker/AnalogClock.tsx","../src/components/time-picker/DigitalClock.tsx","../src/components/time-picker/TimePicker.tsx"],"names":["cx","cy","hours12","isAm","jsxs","jsx","useRef","pad","useState","wrap","useEffect"],"mappings":"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","file":"chunk-75IGGPXL.js","sourcesContent":["'use client';\n\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n type PointerEvent as ReactPointerEvent,\n} from 'react';\n\nexport type TimeValue = { hours: number; minutes: number };\n\nexport interface AnalogClockProps {\n value: TimeValue;\n onChange: (next: TimeValue) => void;\n hourCycle?: 12 | 24;\n minuteStep?: number;\n /** SVG size in px. Default 220. */\n size?: number;\n}\n\ntype Mode = 'hours' | 'minutes';\n\nfunction angleFromCenter(cx: number, cy: number, px: number, py: number): number {\n const dx = px - cx;\n const dy = py - cy;\n const a = Math.atan2(dy, dx) * (180 / Math.PI) + 90;\n return (a + 360) % 360;\n}\n\nexport function AnalogClock({\n value,\n onChange,\n hourCycle = 24,\n minuteStep = 1,\n size = 220,\n}: AnalogClockProps) {\n const ref = useRef<SVGSVGElement>(null);\n const [mode, setMode] = useState<Mode>('hours');\n const [dragging, setDragging] = useState(false);\n\n const updateFromPointer = useCallback(\n (clientX: number, clientY: number, currentMode: Mode) => {\n const svg = ref.current;\n if (!svg) return;\n const rect = svg.getBoundingClientRect();\n const cx = rect.left + rect.width / 2;\n const cy = rect.top + rect.height / 2;\n const a = angleFromCenter(cx, cy, clientX, clientY);\n\n if (currentMode === 'hours') {\n // hours12 is the position on the face (0..11, where 0 = the \"12\" position).\n // Combine with the current AM/PM half so the underlying 24h value preserves\n // whichever half the user is editing.\n const hours12 = Math.round(a / 30) % 12;\n const isAm = value.hours < 12;\n const h24 = isAm ? hours12 : hours12 + 12;\n onChange({ ...value, hours: h24 });\n } else {\n let m = Math.round(a / 6) % 60;\n if (minuteStep > 1) m = Math.round(m / minuteStep) * minuteStep;\n if (m === 60) m = 0;\n onChange({ ...value, minutes: m });\n }\n },\n [minuteStep, onChange, value],\n );\n\n const togglePeriod = () => {\n const next = value.hours < 12 ? value.hours + 12 : value.hours - 12;\n onChange({ ...value, hours: next });\n };\n\n const isAm = value.hours < 12;\n\n useEffect(() => {\n if (!dragging) return;\n const onMove = (e: PointerEvent) => updateFromPointer(e.clientX, e.clientY, mode);\n const onUp = () => setDragging(false);\n window.addEventListener('pointermove', onMove);\n window.addEventListener('pointerup', onUp);\n return () => {\n window.removeEventListener('pointermove', onMove);\n window.removeEventListener('pointerup', onUp);\n };\n }, [dragging, mode, updateFromPointer]);\n\n const handlePointerDown = (e: ReactPointerEvent<SVGSVGElement>) => {\n e.preventDefault();\n setDragging(true);\n updateFromPointer(e.clientX, e.clientY, mode);\n };\n\n // visible hour value\n const hours12 =\n hourCycle === 12\n ? value.hours === 0\n ? 12\n : value.hours > 12\n ? value.hours - 12\n : value.hours\n : value.hours % 12 === 0\n ? 12\n : value.hours % 12;\n\n const hourAngle = ((hours12 % 12) + value.minutes / 60) * 30 - 90;\n const minuteAngle = (value.minutes / 60) * 360 - 90;\n\n const r = size / 2;\n const cx = r;\n const cy = r;\n\n // hand lengths\n const hourLen = r * 0.45;\n const minuteLen = r * 0.7;\n const tickOuter = r * 0.92;\n const tickInner = r * 0.86;\n const majorInner = r * 0.82;\n\n const handX = (len: number, deg: number) => cx + len * Math.cos((deg * Math.PI) / 180);\n const handY = (len: number, deg: number) => cy + len * Math.sin((deg * Math.PI) / 180);\n\n return (\n <div className=\"royui-tp-analog\">\n <svg\n ref={ref}\n width={size}\n height={size}\n viewBox={`0 0 ${size} ${size}`}\n onPointerDown={handlePointerDown}\n className=\"royui-tp-analog__face\"\n role=\"application\"\n aria-label=\"Analog clock\"\n >\n <circle\n cx={cx}\n cy={cy}\n r={r - 1}\n className=\"royui-tp-analog__bezel\"\n />\n\n {/* minute ticks */}\n {Array.from({ length: 60 }).map((_, i) => {\n const angle = (i * 6 - 90) * (Math.PI / 180);\n const isMajor = i % 5 === 0;\n const inner = isMajor ? majorInner : tickInner;\n return (\n <line\n key={i}\n x1={cx + inner * Math.cos(angle)}\n y1={cy + inner * Math.sin(angle)}\n x2={cx + tickOuter * Math.cos(angle)}\n y2={cy + tickOuter * Math.sin(angle)}\n className={isMajor ? 'royui-tp-analog__tick--major' : 'royui-tp-analog__tick'}\n />\n );\n })}\n\n {/* hour numbers at 12, 3, 6, 9 */}\n {[12, 3, 6, 9].map((n) => {\n const idx = n % 12;\n const angle = (idx * 30 - 90) * (Math.PI / 180);\n const rr = r * 0.72;\n return (\n <text\n key={n}\n x={cx + rr * Math.cos(angle)}\n y={cy + rr * Math.sin(angle)}\n dy=\"0.34em\"\n textAnchor=\"middle\"\n className=\"royui-tp-analog__numeral\"\n >\n {n}\n </text>\n );\n })}\n\n {/* hour hand */}\n <line\n x1={cx}\n y1={cy}\n x2={handX(hourLen, hourAngle)}\n y2={handY(hourLen, hourAngle)}\n className={`royui-tp-analog__hand royui-tp-analog__hand--hour ${\n mode === 'hours' ? 'royui-tp-analog__hand--active' : ''\n }`}\n onPointerDown={(e) => {\n e.stopPropagation();\n setMode('hours');\n setDragging(true);\n }}\n />\n\n {/* minute hand */}\n <line\n x1={cx}\n y1={cy}\n x2={handX(minuteLen, minuteAngle)}\n y2={handY(minuteLen, minuteAngle)}\n className={`royui-tp-analog__hand royui-tp-analog__hand--minute ${\n mode === 'minutes' ? 'royui-tp-analog__hand--active' : ''\n }`}\n onPointerDown={(e) => {\n e.stopPropagation();\n setMode('minutes');\n setDragging(true);\n }}\n />\n\n <circle cx={cx} cy={cy} r={3.5} className=\"royui-tp-analog__pin\" />\n </svg>\n\n <div className=\"royui-tp-analog__modes\">\n <button\n type=\"button\"\n className={[\n 'royui-tp-analog__mode',\n mode === 'hours' && 'royui-tp-analog__mode--on',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={() => setMode('hours')}\n >\n Hours\n </button>\n <button\n type=\"button\"\n className={[\n 'royui-tp-analog__mode',\n mode === 'minutes' && 'royui-tp-analog__mode--on',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={() => setMode('minutes')}\n >\n Minutes\n </button>\n <div className=\"royui-tp-analog__period\" role=\"group\" aria-label=\"Day half\">\n <button\n type=\"button\"\n className={[\n 'royui-tp-analog__period-btn',\n isAm && 'royui-tp-analog__period-btn--on',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={() => {\n if (!isAm) togglePeriod();\n }}\n aria-pressed={isAm}\n >\n AM\n </button>\n <button\n type=\"button\"\n className={[\n 'royui-tp-analog__period-btn',\n !isAm && 'royui-tp-analog__period-btn--on',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={() => {\n if (isAm) togglePeriod();\n }}\n aria-pressed={!isAm}\n >\n PM\n </button>\n </div>\n </div>\n </div>\n );\n}\n","'use client';\n\nimport { useRef, type KeyboardEvent, type WheelEvent } from 'react';\nimport type { TimeValue } from './AnalogClock';\n\nexport interface DigitalClockProps {\n value: TimeValue;\n onChange: (next: TimeValue) => void;\n hourCycle?: 12 | 24;\n minuteStep?: number;\n}\n\nfunction pad(n: number): string {\n return String(n).padStart(2, '0');\n}\n\nfunction wrap(n: number, max: number): number {\n return ((n % max) + max) % max;\n}\n\nexport function DigitalClock({\n value,\n onChange,\n hourCycle = 24,\n minuteStep = 1,\n}: DigitalClockProps) {\n const hourBoundary = hourCycle === 12 ? 12 : 24;\n\n const displayHour =\n hourCycle === 12\n ? value.hours % 12 === 0\n ? 12\n : value.hours % 12\n : value.hours;\n\n const isAm = value.hours < 12;\n\n const setDisplayHour = (next: number) => {\n if (hourCycle === 24) {\n onChange({ ...value, hours: wrap(next, 24) });\n return;\n }\n let h = next;\n if (h <= 0) h = 12;\n if (h > 12) h = 1;\n const h24 = isAm ? (h === 12 ? 0 : h) : h === 12 ? 12 : h + 12;\n onChange({ ...value, hours: h24 });\n };\n\n const setMinutes = (next: number) => {\n const stepped = Math.round(next / minuteStep) * minuteStep;\n onChange({ ...value, minutes: wrap(stepped, 60) });\n };\n\n const setAm = () => {\n if (isAm) return;\n onChange({ ...value, hours: value.hours - 12 });\n };\n const setPm = () => {\n if (!isAm) return;\n onChange({ ...value, hours: value.hours + 12 });\n };\n\n return (\n <div className=\"royui-tp-digital\">\n <div className=\"royui-tp-digital__row\">\n <Segment\n label=\"Hours\"\n value={pad(displayHour)}\n onWheelStep={(d) => setDisplayHour(displayHour + d)}\n onArrow={(d) => setDisplayHour(displayHour + d)}\n max={hourBoundary}\n />\n <span className=\"royui-tp-digital__sep\" aria-hidden>\n :\n </span>\n <Segment\n label=\"Minutes\"\n value={pad(value.minutes)}\n onWheelStep={(d) => setMinutes(value.minutes + d * minuteStep)}\n onArrow={(d) => setMinutes(value.minutes + d * minuteStep)}\n max={60}\n />\n </div>\n <div className=\"royui-tp-digital__period\" role=\"group\" aria-label=\"Day half\">\n <button\n type=\"button\"\n className={[\n 'royui-tp-digital__period-btn',\n isAm && 'royui-tp-digital__period-btn--on',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={setAm}\n aria-pressed={isAm}\n >\n AM\n </button>\n <button\n type=\"button\"\n className={[\n 'royui-tp-digital__period-btn',\n !isAm && 'royui-tp-digital__period-btn--on',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={setPm}\n aria-pressed={!isAm}\n >\n PM\n </button>\n </div>\n <div className=\"royui-tp-digital__hint\">\n Scroll or use arrow keys\n </div>\n </div>\n );\n}\n\nfunction Segment({\n label,\n value,\n onWheelStep,\n onArrow,\n max,\n}: {\n label: string;\n value: string;\n onWheelStep: (delta: 1 | -1) => void;\n onArrow: (delta: 1 | -1) => void;\n max: number;\n}) {\n const ref = useRef<HTMLDivElement>(null);\n\n const handleWheel = (e: WheelEvent<HTMLDivElement>) => {\n e.preventDefault();\n onWheelStep(e.deltaY > 0 ? 1 : -1);\n };\n\n const handleKey = (e: KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'ArrowUp' || e.key === 'ArrowRight') {\n e.preventDefault();\n onArrow(1);\n } else if (e.key === 'ArrowDown' || e.key === 'ArrowLeft') {\n e.preventDefault();\n onArrow(-1);\n }\n };\n\n return (\n <div\n ref={ref}\n role=\"spinbutton\"\n tabIndex={0}\n aria-label={label}\n aria-valuetext={value}\n aria-valuemax={max}\n className=\"royui-tp-digital__seg\"\n onWheel={handleWheel}\n onKeyDown={handleKey}\n >\n {value}\n </div>\n );\n}\n","'use client';\n\nimport {\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from 'react';\nimport { AnalogClock, type TimeValue } from './AnalogClock';\nimport { DigitalClock } from './DigitalClock';\nimport './TimePicker.css';\n\nexport type TimePickerVariant = 'analog' | 'digital';\n\nexport interface TimePickerProps {\n value?: TimeValue | null;\n defaultValue?: TimeValue | null;\n onChange?: (next: TimeValue) => void;\n /** Picker style. Default 'analog'. */\n variant?: TimePickerVariant;\n /** Allow the user to switch between variants. Default true. */\n switchable?: boolean;\n hourCycle?: 12 | 24;\n minuteStep?: number;\n placeholder?: string;\n align?: 'left' | 'right';\n className?: string;\n style?: CSSProperties;\n triggerLabel?: ReactNode;\n disabled?: boolean;\n}\n\nfunction pad(n: number): string {\n return String(n).padStart(2, '0');\n}\n\nexport function formatTime(t: TimeValue | null | undefined, hourCycle: 12 | 24 = 24): string {\n if (!t) return '';\n if (hourCycle === 24) return `${pad(t.hours)}:${pad(t.minutes)}`;\n const h = t.hours % 12 === 0 ? 12 : t.hours % 12;\n const ampm = t.hours < 12 ? 'AM' : 'PM';\n return `${pad(h)}:${pad(t.minutes)} ${ampm}`;\n}\n\nexport function TimePicker({\n value,\n defaultValue,\n onChange,\n variant = 'analog',\n switchable = true,\n hourCycle = 24,\n minuteStep = 1,\n placeholder = 'Pick a time',\n align = 'left',\n className = '',\n style,\n triggerLabel,\n disabled,\n}: TimePickerProps) {\n const controlled = value !== undefined;\n const [internal, setInternal] = useState<TimeValue | null>(defaultValue ?? null);\n const current = controlled ? value : internal;\n\n const [open, setOpen] = useState(false);\n const [mode, setMode] = useState<TimePickerVariant>(variant);\n const [draft, setDraft] = useState<TimeValue>(current ?? { hours: 12, minutes: 0 });\n\n const wrap = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return;\n function onDown(e: MouseEvent) {\n if (wrap.current && !wrap.current.contains(e.target as Node)) {\n setOpen(false);\n }\n }\n function onKey(e: KeyboardEvent) {\n if (e.key === 'Escape') setOpen(false);\n }\n document.addEventListener('mousedown', onDown);\n document.addEventListener('keydown', onKey);\n return () => {\n document.removeEventListener('mousedown', onDown);\n document.removeEventListener('keydown', onKey);\n };\n }, [open]);\n\n useEffect(() => {\n if (open) setDraft(current ?? { hours: new Date().getHours(), minutes: 0 });\n }, [open]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n setMode(variant);\n }, [variant]);\n\n const commit = (next: TimeValue) => {\n if (!controlled) setInternal(next);\n onChange?.(next);\n };\n\n const setNow = () => {\n const now = new Date();\n setDraft({ hours: now.getHours(), minutes: now.getMinutes() });\n };\n\n const apply = () => {\n commit(draft);\n setOpen(false);\n };\n\n return (\n <div\n ref={wrap}\n className={['royui-tp', className].filter(Boolean).join(' ')}\n style={style}\n >\n <button\n type=\"button\"\n className=\"royui-tp__trigger\"\n onClick={() => !disabled && setOpen((o) => !o)}\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n disabled={disabled}\n >\n <span className=\"royui-tp__trigger-dot\" aria-hidden />\n <span className=\"royui-tp__trigger-label\">\n {triggerLabel ?? (formatTime(current ?? null, hourCycle) || placeholder)}\n </span>\n </button>\n\n {open && (\n <div\n className={`royui-tp__panel royui-tp__panel--${align}`}\n role=\"dialog\"\n aria-label=\"Choose time\"\n >\n <div className=\"royui-tp__head\">\n <div className=\"royui-tp__readout\">\n {formatTime(draft, hourCycle)}\n </div>\n {switchable && (\n <div className=\"royui-tp__variants\" role=\"tablist\">\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={mode === 'analog'}\n className={[\n 'royui-tp__variant',\n mode === 'analog' && 'royui-tp__variant--on',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={() => setMode('analog')}\n >\n Analog\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={mode === 'digital'}\n className={[\n 'royui-tp__variant',\n mode === 'digital' && 'royui-tp__variant--on',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={() => setMode('digital')}\n >\n Digital\n </button>\n </div>\n )}\n </div>\n\n <div className=\"royui-tp__body\">\n {mode === 'analog' ? (\n <AnalogClock\n value={draft}\n onChange={setDraft}\n hourCycle={hourCycle}\n minuteStep={minuteStep}\n />\n ) : (\n <DigitalClock\n value={draft}\n onChange={setDraft}\n hourCycle={hourCycle}\n minuteStep={minuteStep}\n />\n )}\n </div>\n\n <div className=\"royui-tp__foot\">\n <button type=\"button\" className=\"royui-tp__ghost\" onClick={setNow}>\n Now\n </button>\n <button type=\"button\" className=\"royui-tp__primary\" onClick={apply}>\n Apply\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport type { TimeValue };\n"]}
|