@spacesync/client 0.1.0 → 0.1.2
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/README.md +9 -14
- package/dist/analog-clock-picker.d.ts +2 -2
- package/dist/analog-clock-picker.js +12 -406
- package/dist/analog-clock-picker.js.map +1 -1
- package/dist/api.js +7 -87
- package/dist/api.js.map +1 -1
- package/dist/app-modal.d.ts +7 -8
- package/dist/app-modal.js +3 -74
- package/dist/app-modal.js.map +1 -1
- package/dist/app-shell-mobile-nav.d.ts +1 -2
- package/dist/app-shell-mobile-nav.js +2 -66
- package/dist/app-shell-mobile-nav.js.map +1 -1
- package/dist/app-web-shell.d.ts +2 -2
- package/dist/app-web-shell.js +1 -4
- package/dist/app-web-shell.js.map +1 -1
- package/dist/booking-schedule-calendars.d.ts +12 -12
- package/dist/booking-schedule-calendars.js +13 -402
- package/dist/booking-schedule-calendars.js.map +1 -1
- package/dist/chat-stick-to-bottom.d.ts +1 -2
- package/dist/chat-stick-to-bottom.js +12 -48
- package/dist/chat-stick-to-bottom.js.map +1 -1
- package/dist/chunk-4A6RCBB5.js +4925 -0
- package/dist/chunk-4A6RCBB5.js.map +1 -0
- package/dist/chunk-4NNK5IXY.js +79 -0
- package/dist/chunk-4NNK5IXY.js.map +1 -0
- package/dist/chunk-4Y5DBM6Z.js +6 -0
- package/dist/chunk-4Y5DBM6Z.js.map +1 -0
- package/dist/chunk-7YCAAP43.js +107 -0
- package/dist/chunk-7YCAAP43.js.map +1 -0
- package/dist/chunk-DMPSQ22Q.js +11 -0
- package/dist/chunk-DMPSQ22Q.js.map +1 -0
- package/dist/chunk-FCY5FYJB.js +104 -0
- package/dist/chunk-FCY5FYJB.js.map +1 -0
- package/dist/chunk-H7EX6B6N.js +580 -0
- package/dist/chunk-H7EX6B6N.js.map +1 -0
- package/dist/chunk-HCHASUB6.js +13 -0
- package/dist/chunk-HCHASUB6.js.map +1 -0
- package/dist/chunk-L65F4EMK.js +34 -0
- package/dist/chunk-L65F4EMK.js.map +1 -0
- package/dist/chunk-LUB7QWGU.js +301 -0
- package/dist/chunk-LUB7QWGU.js.map +1 -0
- package/dist/chunk-OMSJWTSZ.js +120 -0
- package/dist/chunk-OMSJWTSZ.js.map +1 -0
- package/dist/chunk-OWNUMPUC.js +9 -0
- package/dist/chunk-OWNUMPUC.js.map +1 -0
- package/dist/chunk-P65TMY3D.js +87 -0
- package/dist/chunk-P65TMY3D.js.map +1 -0
- package/dist/chunk-PBNBLCC2.js +84 -0
- package/dist/chunk-PBNBLCC2.js.map +1 -0
- package/dist/chunk-RV5PMZYZ.js +63 -0
- package/dist/chunk-RV5PMZYZ.js.map +1 -0
- package/dist/chunk-VHINHHZQ.js +48 -0
- package/dist/chunk-VHINHHZQ.js.map +1 -0
- package/dist/chunk-VVXGQKN7.js +107 -0
- package/dist/chunk-VVXGQKN7.js.map +1 -0
- package/dist/chunk-XU3EFSQX.js +22 -0
- package/dist/chunk-XU3EFSQX.js.map +1 -0
- package/dist/chunk-YAVEMXXV.js +381 -0
- package/dist/chunk-YAVEMXXV.js.map +1 -0
- package/dist/chunk-ZIJQ7BXU.js +27 -0
- package/dist/chunk-ZIJQ7BXU.js.map +1 -0
- package/dist/detail-page-header.d.ts +3 -3
- package/dist/detail-page-header.js +5 -149
- package/dist/detail-page-header.js.map +1 -1
- package/dist/{dialog-CIdvylPK.d.ts → dialog-CPkbi5N3.d.ts} +10 -11
- package/dist/file-picker-field.d.ts +2 -2
- package/dist/file-picker-field.js +3 -163
- package/dist/file-picker-field.js.map +1 -1
- package/dist/file-viewer-dialog.d.ts +2 -2
- package/dist/file-viewer-dialog.js +12 -223
- package/dist/file-viewer-dialog.js.map +1 -1
- package/dist/floor-switcher.d.ts +2 -2
- package/dist/floor-switcher.js +13 -323
- package/dist/floor-switcher.js.map +1 -1
- package/dist/forms.js +1 -23
- package/dist/forms.js.map +1 -1
- package/dist/image-picker-field.d.ts +7 -4
- package/dist/image-picker-field.js +17 -60
- package/dist/image-picker-field.js.map +1 -1
- package/dist/layout-canvas-view.d.ts +2 -2
- package/dist/layout-canvas-view.js +17 -220
- package/dist/layout-canvas-view.js.map +1 -1
- package/dist/lib/dateDisplay.js +1 -82
- package/dist/lib/dateDisplay.js.map +1 -1
- package/dist/live-resource-meta.js +1 -25
- package/dist/live-resource-meta.js.map +1 -1
- package/dist/live-space-pulse-dot.d.ts +2 -2
- package/dist/live-space-pulse-dot.js +2 -16
- package/dist/live-space-pulse-dot.js.map +1 -1
- package/dist/notification-sidebar-preview.d.ts +3 -3
- package/dist/notification-sidebar-preview.js +3 -61
- package/dist/notification-sidebar-preview.js.map +1 -1
- package/dist/page-list-chrome.d.ts +25 -25
- package/dist/page-list-chrome.js +6 -968
- package/dist/page-list-chrome.js.map +1 -1
- package/dist/platform-support-message-composer.d.ts +4 -4
- package/dist/platform-support-message-composer.js +15 -218
- package/dist/platform-support-message-composer.js.map +1 -1
- package/dist/shell-sidebar-nav.js +8 -13
- package/dist/shell-sidebar-nav.js.map +1 -1
- package/dist/shell-theme.js +1 -7
- package/dist/shell-theme.js.map +1 -1
- package/dist/shell-top-bar-chrome.js +3 -103
- package/dist/shell-top-bar-chrome.js.map +1 -1
- package/dist/shell-top-bar.d.ts +3 -3
- package/dist/shell-top-bar.js +1 -69
- package/dist/shell-top-bar.js.map +1 -1
- package/dist/support-presence-status.d.ts +2 -2
- package/dist/support-presence-status.js +3 -41
- package/dist/support-presence-status.js.map +1 -1
- package/dist/theme-appearance-control.d.ts +2 -2
- package/dist/theme-appearance-control.js +4 -63
- package/dist/theme-appearance-control.js.map +1 -1
- package/dist/theme-boot-script.js +1 -2
- package/dist/theme-boot-script.js.map +1 -1
- package/dist/theme-provider.d.ts +2 -2
- package/dist/theme-provider.js +2 -23
- package/dist/theme-provider.js.map +1 -1
- package/dist/ui.d.ts +261 -262
- package/dist/ui.js +11 -5635
- package/dist/ui.js.map +1 -1
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -2,38 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
Reusable React UI components, application shell primitives, themes, and API-client helpers for SpaceSync products.
|
|
4
4
|
|
|
5
|
-
## Install
|
|
5
|
+
## Install (consumers)
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install @spacesync/client
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
`react` and `react-dom` are peer dependencies
|
|
12
|
-
|
|
13
|
-
## Use
|
|
14
|
-
|
|
15
|
-
Import from a documented subpath:
|
|
11
|
+
`react` and `react-dom` are peer dependencies.
|
|
16
12
|
|
|
17
13
|
```tsx
|
|
18
14
|
import { Button } from "@spacesync/client/ui";
|
|
19
15
|
import { AppWebShell } from "@spacesync/client/app-web-shell";
|
|
20
16
|
```
|
|
21
17
|
|
|
22
|
-
For the SpaceSync theme, import the stylesheet from the application’s global CSS entry point:
|
|
23
|
-
|
|
24
18
|
```css
|
|
25
19
|
@import "@spacesync/client/styles/theme.css";
|
|
26
20
|
```
|
|
27
21
|
|
|
28
|
-
|
|
22
|
+
## Develop / publish (this package)
|
|
29
23
|
|
|
30
|
-
|
|
24
|
+
This folder is the package source. It is **not** part of the monorepo pnpm workspace (apps install the published npm package). Work on it independently:
|
|
31
25
|
|
|
32
26
|
```bash
|
|
33
|
-
|
|
27
|
+
cd packages/spacesync-client
|
|
28
|
+
pnpm install --ignore-workspace
|
|
34
29
|
pnpm typecheck
|
|
35
|
-
|
|
36
|
-
npm publish
|
|
30
|
+
pnpm build
|
|
31
|
+
npm publish --access public
|
|
37
32
|
```
|
|
38
33
|
|
|
39
|
-
|
|
34
|
+
To put this on GitHub, copy or move this directory into its own repository and push. Bumping `version` + publishing is what updates npm — git pushes do not.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
|
|
3
3
|
declare function AnalogClockPicker({ value, onChange, timeFormat, className, }: {
|
|
4
4
|
value: string;
|
|
5
5
|
onChange: (v: string) => void;
|
|
6
6
|
timeFormat?: "12h" | "24h";
|
|
7
7
|
className?: string;
|
|
8
|
-
}):
|
|
8
|
+
}): React.JSX.Element;
|
|
9
9
|
|
|
10
10
|
export { AnalogClockPicker };
|
|
@@ -1,408 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
import '
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (!m) return void 0;
|
|
14
|
-
const h = Number(m[1]);
|
|
15
|
-
const min = Number(m[2]);
|
|
16
|
-
if (h > 23 || min > 59) return void 0;
|
|
17
|
-
return { h, m: min };
|
|
18
|
-
}
|
|
19
|
-
function toTimeStr(h, m) {
|
|
20
|
-
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
|
|
21
|
-
}
|
|
22
|
-
function formatTimeForDisplay(value, timeFormat) {
|
|
23
|
-
const p = parseHm(value);
|
|
24
|
-
if (!p) return value.trim() || "\u2014";
|
|
25
|
-
if (timeFormat === "24h") return toTimeStr(p.h, p.m);
|
|
26
|
-
const ap = p.h >= 12 ? "pm" : "am";
|
|
27
|
-
const h12 = p.h % 12 || 12;
|
|
28
|
-
return `${String(h12).padStart(2, "0")}:${String(p.m).padStart(2, "0")} ${ap}`;
|
|
29
|
-
}
|
|
30
|
-
Array.from({ length: 24 }, (_, i) => String(i).padStart(2, "0"));
|
|
31
|
-
Array.from({ length: 60 }, (_, i) => String(i).padStart(2, "0"));
|
|
32
|
-
Array.from({ length: 12 }, (_, i) => String(i + 1));
|
|
33
|
-
var SIZE = 248;
|
|
34
|
-
var CX = SIZE / 2;
|
|
35
|
-
var CY = SIZE / 2;
|
|
36
|
-
var FACE_R = 98;
|
|
37
|
-
var HOUR_LABEL_R = 72;
|
|
38
|
-
var MINUTE_TICK_R = 84;
|
|
39
|
-
function pointToAngleDeg(x, y) {
|
|
40
|
-
const dx = x - CX;
|
|
41
|
-
const dy = y - CY;
|
|
42
|
-
let deg = Math.atan2(dy, dx) * 180 / Math.PI + 90;
|
|
43
|
-
if (deg < 0) deg += 360;
|
|
44
|
-
return deg;
|
|
45
|
-
}
|
|
46
|
-
function angleToHour12(deg) {
|
|
47
|
-
const h = Math.round(deg / 30) % 12;
|
|
48
|
-
return h === 0 ? 12 : h;
|
|
49
|
-
}
|
|
50
|
-
function angleToHour24(deg) {
|
|
51
|
-
return Math.round(deg / 15) % 24;
|
|
52
|
-
}
|
|
53
|
-
function angleToMinute(deg) {
|
|
54
|
-
return Math.round(deg / 6) % 60;
|
|
55
|
-
}
|
|
56
|
-
function hour12To24(h12, ap) {
|
|
57
|
-
if (ap === "am") return h12 === 12 ? 0 : h12;
|
|
58
|
-
return h12 === 12 ? 12 : h12 + 12;
|
|
59
|
-
}
|
|
60
|
-
function hour24To12(h24) {
|
|
61
|
-
return { h12: h24 % 12 || 12, ap: h24 >= 12 ? "pm" : "am" };
|
|
62
|
-
}
|
|
63
|
-
function handEnd(deg, length) {
|
|
64
|
-
const rad = (deg - 90) * Math.PI / 180;
|
|
65
|
-
return { x: CX + Math.cos(rad) * length, y: CY + Math.sin(rad) * length };
|
|
66
|
-
}
|
|
67
|
-
function hourCenterDeg(h, timeFormat) {
|
|
68
|
-
if (timeFormat === "24h") return h * 15;
|
|
69
|
-
return h % 12 * 30;
|
|
70
|
-
}
|
|
71
|
-
function sectorPath(centerDeg, spread, radius) {
|
|
72
|
-
const start = handEnd(centerDeg - spread, radius);
|
|
73
|
-
const end = handEnd(centerDeg + spread, radius);
|
|
74
|
-
return `M ${CX} ${CY} L ${start.x} ${start.y} A ${radius} ${radius} 0 0 1 ${end.x} ${end.y} Z`;
|
|
75
|
-
}
|
|
76
|
-
function pointerTarget(clientX, clientY, svg, phase, timeFormat) {
|
|
77
|
-
const rect = svg.getBoundingClientRect();
|
|
78
|
-
const scale = SIZE / rect.width;
|
|
79
|
-
const x = (clientX - rect.left) * scale;
|
|
80
|
-
const y = (clientY - rect.top) * scale;
|
|
81
|
-
const dist = Math.hypot(x - CX, y - CY);
|
|
82
|
-
if (dist < 24 || dist > FACE_R + 8) return null;
|
|
83
|
-
const deg = pointToAngleDeg(x, y);
|
|
84
|
-
if (phase === "hour") {
|
|
85
|
-
return timeFormat === "24h" ? angleToHour24(deg) : angleToHour12(deg);
|
|
86
|
-
}
|
|
87
|
-
return angleToMinute(deg);
|
|
88
|
-
}
|
|
89
|
-
function AnalogClockPicker({
|
|
90
|
-
value,
|
|
91
|
-
onChange,
|
|
92
|
-
timeFormat = "24h",
|
|
93
|
-
className
|
|
94
|
-
}) {
|
|
95
|
-
const svgRef = useRef(null);
|
|
96
|
-
const parsed = parseHm(value);
|
|
97
|
-
const editor = parsed ?? { h: 9, m: 0 };
|
|
98
|
-
const { h12, ap } = hour24To12(editor.h);
|
|
99
|
-
const [phase, setPhase] = useState("hour");
|
|
100
|
-
const [hoverTarget, setHoverTarget] = useState(null);
|
|
101
|
-
const setTime = useCallback(
|
|
102
|
-
(h, m) => {
|
|
103
|
-
onChange(toTimeStr(h, m));
|
|
104
|
-
},
|
|
105
|
-
[onChange]
|
|
106
|
-
);
|
|
107
|
-
const handlePointer = useCallback(
|
|
108
|
-
(clientX, clientY) => {
|
|
109
|
-
const svg = svgRef.current;
|
|
110
|
-
if (!svg) return;
|
|
111
|
-
const rect = svg.getBoundingClientRect();
|
|
112
|
-
const scale = SIZE / rect.width;
|
|
113
|
-
const x = (clientX - rect.left) * scale;
|
|
114
|
-
const y = (clientY - rect.top) * scale;
|
|
115
|
-
const deg = pointToAngleDeg(x, y);
|
|
116
|
-
if (phase === "hour") {
|
|
117
|
-
if (timeFormat === "24h") {
|
|
118
|
-
const h24 = angleToHour24(deg);
|
|
119
|
-
setTime(h24, editor.m);
|
|
120
|
-
} else {
|
|
121
|
-
const h = angleToHour12(deg);
|
|
122
|
-
setTime(hour12To24(h, ap), editor.m);
|
|
123
|
-
}
|
|
124
|
-
setPhase("minute");
|
|
125
|
-
} else {
|
|
126
|
-
const m = angleToMinute(deg);
|
|
127
|
-
setTime(editor.h, m);
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
[phase, timeFormat, ap, editor.h, editor.m, setTime]
|
|
131
|
-
);
|
|
132
|
-
const updateHover = useCallback(
|
|
133
|
-
(clientX, clientY) => {
|
|
134
|
-
const svg = svgRef.current;
|
|
135
|
-
if (!svg) return;
|
|
136
|
-
setHoverTarget(pointerTarget(clientX, clientY, svg, phase, timeFormat));
|
|
137
|
-
},
|
|
138
|
-
[phase, timeFormat]
|
|
139
|
-
);
|
|
140
|
-
const previewHour = phase === "hour" && hoverTarget != null ? timeFormat === "24h" ? hoverTarget : hour12To24(hoverTarget, ap) : editor.h;
|
|
141
|
-
const previewMinute = phase === "minute" && hoverTarget != null ? hoverTarget : editor.m;
|
|
142
|
-
const hourHandDeg = previewHour % 12 * 30 + previewMinute * 0.5;
|
|
143
|
-
const minuteHandDeg = previewMinute * 6;
|
|
144
|
-
const hourEnd = handEnd(hourHandDeg, 46);
|
|
145
|
-
const minuteEnd = handEnd(minuteHandDeg, 64);
|
|
146
|
-
const selectedHourKey = timeFormat === "24h" ? editor.h : h12;
|
|
147
|
-
const hoveredHourKey = phase === "hour" ? hoverTarget : null;
|
|
148
|
-
const hourMarkers = useMemo(() => {
|
|
149
|
-
if (timeFormat === "24h") {
|
|
150
|
-
return Array.from({ length: 24 }, (_, h) => {
|
|
151
|
-
const deg = h * 15;
|
|
152
|
-
const rad = (deg - 90) * Math.PI / 180;
|
|
153
|
-
const showLabel = h % 3 === 0;
|
|
154
|
-
const r = showLabel ? HOUR_LABEL_R - 6 : HOUR_LABEL_R + 4;
|
|
155
|
-
return {
|
|
156
|
-
key: h,
|
|
157
|
-
label: showLabel ? String(h).padStart(2, "0") : null,
|
|
158
|
-
x: CX + Math.cos(rad) * r,
|
|
159
|
-
y: CY + Math.sin(rad) * r,
|
|
160
|
-
deg,
|
|
161
|
-
h
|
|
162
|
-
};
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
return Array.from({ length: 12 }, (_, i) => {
|
|
166
|
-
const h = i === 0 ? 12 : i;
|
|
167
|
-
const deg = h * 30;
|
|
168
|
-
const rad = (deg - 90) * Math.PI / 180;
|
|
169
|
-
return {
|
|
170
|
-
key: h,
|
|
171
|
-
label: String(h),
|
|
172
|
-
x: CX + Math.cos(rad) * HOUR_LABEL_R,
|
|
173
|
-
y: CY + Math.sin(rad) * HOUR_LABEL_R,
|
|
174
|
-
deg,
|
|
175
|
-
h
|
|
176
|
-
};
|
|
177
|
-
});
|
|
178
|
-
}, [timeFormat]);
|
|
179
|
-
const minuteMarkers = useMemo(
|
|
180
|
-
() => Array.from({ length: 12 }, (_, i) => {
|
|
181
|
-
const m = i * 5;
|
|
182
|
-
const deg = m * 6;
|
|
183
|
-
const rad = (deg - 90) * Math.PI / 180;
|
|
184
|
-
const selected = editor.m === m;
|
|
185
|
-
const hovered = hoverTarget != null && Math.round(hoverTarget / 5) * 5 === m;
|
|
186
|
-
return {
|
|
187
|
-
m,
|
|
188
|
-
x: CX + Math.cos(rad) * MINUTE_TICK_R,
|
|
189
|
-
y: CY + Math.sin(rad) * MINUTE_TICK_R,
|
|
190
|
-
selected,
|
|
191
|
-
hovered
|
|
192
|
-
};
|
|
193
|
-
}),
|
|
194
|
-
[editor.m, hoverTarget]
|
|
195
|
-
);
|
|
196
|
-
const displayTime = formatTimeForDisplay(value || toTimeStr(editor.h, editor.m), timeFormat);
|
|
197
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-center gap-4", className), children: [
|
|
198
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 rounded-2xl border border-border bg-muted/60 px-4 py-2.5", children: [
|
|
199
|
-
/* @__PURE__ */ jsx(
|
|
200
|
-
"button",
|
|
201
|
-
{
|
|
202
|
-
type: "button",
|
|
203
|
-
onClick: () => setPhase("hour"),
|
|
204
|
-
className: cn(
|
|
205
|
-
"rounded-lg px-3 py-1 text-2xl font-semibold tabular-nums transition-colors",
|
|
206
|
-
phase === "hour" ? "bg-blue-500 text-white shadow-sm" : "text-foreground hover:bg-accent"
|
|
207
|
-
),
|
|
208
|
-
children: timeFormat === "24h" ? String(editor.h).padStart(2, "0") : String(h12).padStart(2, "0")
|
|
209
|
-
}
|
|
210
|
-
),
|
|
211
|
-
/* @__PURE__ */ jsx("span", { className: "text-2xl font-semibold text-muted-foreground", children: ":" }),
|
|
212
|
-
/* @__PURE__ */ jsx(
|
|
213
|
-
"button",
|
|
214
|
-
{
|
|
215
|
-
type: "button",
|
|
216
|
-
onClick: () => setPhase("minute"),
|
|
217
|
-
className: cn(
|
|
218
|
-
"rounded-lg px-3 py-1 text-2xl font-semibold tabular-nums transition-colors",
|
|
219
|
-
phase === "minute" ? "bg-blue-500 text-white shadow-sm" : "text-foreground hover:bg-accent"
|
|
220
|
-
),
|
|
221
|
-
children: String(editor.m).padStart(2, "0")
|
|
222
|
-
}
|
|
223
|
-
),
|
|
224
|
-
timeFormat === "12h" ? /* @__PURE__ */ jsx("div", { className: "ml-2 flex flex-col gap-0.5", children: ["am", "pm"].map((p) => /* @__PURE__ */ jsx(
|
|
225
|
-
"button",
|
|
226
|
-
{
|
|
227
|
-
type: "button",
|
|
228
|
-
onClick: () => setTime(hour12To24(h12, p), editor.m),
|
|
229
|
-
className: cn(
|
|
230
|
-
"rounded-md px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide transition-colors",
|
|
231
|
-
ap === p ? "bg-blue-100 text-blue-700 dark:bg-blue-950/50 dark:text-blue-300" : "text-muted-foreground hover:bg-accent"
|
|
232
|
-
),
|
|
233
|
-
children: p
|
|
234
|
-
},
|
|
235
|
-
p
|
|
236
|
-
)) }) : null
|
|
237
|
-
] }),
|
|
238
|
-
/* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsxs(
|
|
239
|
-
"svg",
|
|
240
|
-
{
|
|
241
|
-
ref: svgRef,
|
|
242
|
-
width: SIZE,
|
|
243
|
-
height: SIZE,
|
|
244
|
-
viewBox: `0 0 ${SIZE} ${SIZE}`,
|
|
245
|
-
className: "cursor-pointer touch-none select-none",
|
|
246
|
-
onClick: (e) => handlePointer(e.clientX, e.clientY),
|
|
247
|
-
onPointerMove: (e) => updateHover(e.clientX, e.clientY),
|
|
248
|
-
onPointerLeave: () => setHoverTarget(null),
|
|
249
|
-
onPointerDown: (e) => {
|
|
250
|
-
e.preventDefault();
|
|
251
|
-
handlePointer(e.clientX, e.clientY);
|
|
252
|
-
},
|
|
253
|
-
children: [
|
|
254
|
-
/* @__PURE__ */ jsx("circle", { cx: CX, cy: CY, r: FACE_R, className: "fill-blue-50/80 dark:fill-blue-950/30" }),
|
|
255
|
-
/* @__PURE__ */ jsx(
|
|
256
|
-
"circle",
|
|
257
|
-
{
|
|
258
|
-
cx: CX,
|
|
259
|
-
cy: CY,
|
|
260
|
-
r: FACE_R,
|
|
261
|
-
className: "fill-none stroke-border",
|
|
262
|
-
strokeWidth: 1.5
|
|
263
|
-
}
|
|
264
|
-
),
|
|
265
|
-
phase === "hour" && hoverTarget != null ? /* @__PURE__ */ jsx(
|
|
266
|
-
"path",
|
|
267
|
-
{
|
|
268
|
-
d: sectorPath(
|
|
269
|
-
hourCenterDeg(hoverTarget, timeFormat),
|
|
270
|
-
timeFormat === "24h" ? 7.5 : 15,
|
|
271
|
-
FACE_R - 2
|
|
272
|
-
),
|
|
273
|
-
className: "fill-blue-200/70 transition-opacity dark:fill-blue-500/25"
|
|
274
|
-
}
|
|
275
|
-
) : null,
|
|
276
|
-
phase === "minute" && hoverTarget != null ? /* @__PURE__ */ jsx(
|
|
277
|
-
"path",
|
|
278
|
-
{
|
|
279
|
-
d: sectorPath(hoverTarget * 6, 3, FACE_R - 2),
|
|
280
|
-
className: "fill-blue-200/70 transition-opacity dark:fill-blue-500/25"
|
|
281
|
-
}
|
|
282
|
-
) : null,
|
|
283
|
-
phase === "hour" ? hourMarkers.map((m) => {
|
|
284
|
-
const isSelected = m.h === selectedHourKey || timeFormat === "12h" && m.h === h12;
|
|
285
|
-
const isHovered = m.h === hoveredHourKey;
|
|
286
|
-
const active = isHovered || isSelected && hoveredHourKey == null;
|
|
287
|
-
return /* @__PURE__ */ jsxs("g", { children: [
|
|
288
|
-
active ? /* @__PURE__ */ jsx(
|
|
289
|
-
"circle",
|
|
290
|
-
{
|
|
291
|
-
cx: m.x,
|
|
292
|
-
cy: m.y,
|
|
293
|
-
r: isHovered ? 18 : 16,
|
|
294
|
-
className: cn(
|
|
295
|
-
"transition-all duration-100",
|
|
296
|
-
isHovered ? "fill-blue-500 shadow-sm" : "fill-blue-400/90"
|
|
297
|
-
)
|
|
298
|
-
}
|
|
299
|
-
) : null,
|
|
300
|
-
m.label ? /* @__PURE__ */ jsx(
|
|
301
|
-
"text",
|
|
302
|
-
{
|
|
303
|
-
x: m.x,
|
|
304
|
-
y: m.y,
|
|
305
|
-
textAnchor: "middle",
|
|
306
|
-
dominantBaseline: "central",
|
|
307
|
-
className: cn(
|
|
308
|
-
"pointer-events-none text-[11px] font-semibold transition-colors duration-100",
|
|
309
|
-
active ? "fill-white" : "fill-muted-foreground",
|
|
310
|
-
isHovered && "text-[12px] font-bold"
|
|
311
|
-
),
|
|
312
|
-
children: m.label
|
|
313
|
-
}
|
|
314
|
-
) : /* @__PURE__ */ jsx(
|
|
315
|
-
"circle",
|
|
316
|
-
{
|
|
317
|
-
cx: m.x,
|
|
318
|
-
cy: m.y,
|
|
319
|
-
r: isHovered ? 5 : 2,
|
|
320
|
-
className: cn(
|
|
321
|
-
"pointer-events-none transition-all duration-100",
|
|
322
|
-
isHovered ? "fill-white" : "fill-muted-foreground/40"
|
|
323
|
-
)
|
|
324
|
-
}
|
|
325
|
-
)
|
|
326
|
-
] }, m.key);
|
|
327
|
-
}) : minuteMarkers.map((m) => {
|
|
328
|
-
const active = m.selected || m.hovered;
|
|
329
|
-
return /* @__PURE__ */ jsxs("g", { children: [
|
|
330
|
-
/* @__PURE__ */ jsx(
|
|
331
|
-
"circle",
|
|
332
|
-
{
|
|
333
|
-
cx: m.x,
|
|
334
|
-
cy: m.y,
|
|
335
|
-
r: m.hovered ? 16 : m.selected ? 14 : 4,
|
|
336
|
-
className: cn(
|
|
337
|
-
"transition-all duration-100",
|
|
338
|
-
m.hovered ? "fill-blue-400" : m.selected ? "fill-blue-500" : "fill-blue-200/80 dark:fill-blue-800/60"
|
|
339
|
-
)
|
|
340
|
-
}
|
|
341
|
-
),
|
|
342
|
-
active || m.m % 15 === 0 ? /* @__PURE__ */ jsx(
|
|
343
|
-
"text",
|
|
344
|
-
{
|
|
345
|
-
x: m.x,
|
|
346
|
-
y: m.y,
|
|
347
|
-
textAnchor: "middle",
|
|
348
|
-
dominantBaseline: "central",
|
|
349
|
-
className: cn(
|
|
350
|
-
"pointer-events-none text-[10px] font-semibold transition-colors duration-100",
|
|
351
|
-
active ? "fill-white" : "fill-blue-700 dark:fill-blue-300"
|
|
352
|
-
),
|
|
353
|
-
children: String(m.m).padStart(2, "0")
|
|
354
|
-
}
|
|
355
|
-
) : null
|
|
356
|
-
] }, m.m);
|
|
357
|
-
}),
|
|
358
|
-
/* @__PURE__ */ jsx(
|
|
359
|
-
"line",
|
|
360
|
-
{
|
|
361
|
-
x1: CX,
|
|
362
|
-
y1: CY,
|
|
363
|
-
x2: hourEnd.x,
|
|
364
|
-
y2: hourEnd.y,
|
|
365
|
-
className: cn(
|
|
366
|
-
"stroke-blue-600 transition-all duration-100 dark:stroke-blue-400",
|
|
367
|
-
phase === "hour" && hoverTarget != null && "stroke-blue-500 opacity-90"
|
|
368
|
-
),
|
|
369
|
-
strokeWidth: 3.5,
|
|
370
|
-
strokeLinecap: "round"
|
|
371
|
-
}
|
|
372
|
-
),
|
|
373
|
-
/* @__PURE__ */ jsx(
|
|
374
|
-
"line",
|
|
375
|
-
{
|
|
376
|
-
x1: CX,
|
|
377
|
-
y1: CY,
|
|
378
|
-
x2: minuteEnd.x,
|
|
379
|
-
y2: minuteEnd.y,
|
|
380
|
-
className: cn(
|
|
381
|
-
"stroke-blue-500 transition-all duration-100 dark:stroke-blue-300",
|
|
382
|
-
phase === "minute" && hoverTarget != null && "stroke-blue-400 opacity-90"
|
|
383
|
-
),
|
|
384
|
-
strokeWidth: 2.5,
|
|
385
|
-
strokeLinecap: "round"
|
|
386
|
-
}
|
|
387
|
-
),
|
|
388
|
-
/* @__PURE__ */ jsx("circle", { cx: CX, cy: CY, r: 5, className: "fill-blue-600 dark:fill-blue-400" }),
|
|
389
|
-
/* @__PURE__ */ jsx("circle", { cx: CX, cy: CY, r: 2.5, className: "fill-white" })
|
|
390
|
-
]
|
|
391
|
-
}
|
|
392
|
-
) }),
|
|
393
|
-
/* @__PURE__ */ jsxs("p", { className: "text-center text-[11px] text-muted-foreground", children: [
|
|
394
|
-
phase === "hour" ? "Hover or tap an hour" : "Hover or tap minutes \xB7 ",
|
|
395
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: hoverTarget != null && phase === "hour" ? formatTimeForDisplay(
|
|
396
|
-
toTimeStr(
|
|
397
|
-
timeFormat === "24h" ? hoverTarget : hour12To24(hoverTarget, ap),
|
|
398
|
-
editor.m
|
|
399
|
-
),
|
|
400
|
-
timeFormat
|
|
401
|
-
) : hoverTarget != null && phase === "minute" ? formatTimeForDisplay(toTimeStr(editor.h, hoverTarget), timeFormat) : displayTime })
|
|
402
|
-
] })
|
|
403
|
-
] });
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
export { AnalogClockPicker };
|
|
1
|
+
export { AnalogClockPicker } from './chunk-YAVEMXXV.js';
|
|
2
|
+
import './chunk-4A6RCBB5.js';
|
|
3
|
+
import './chunk-RV5PMZYZ.js';
|
|
4
|
+
import './chunk-L65F4EMK.js';
|
|
5
|
+
import './chunk-PBNBLCC2.js';
|
|
6
|
+
import './chunk-VVXGQKN7.js';
|
|
7
|
+
import './chunk-7YCAAP43.js';
|
|
8
|
+
import './chunk-LUB7QWGU.js';
|
|
9
|
+
import './chunk-VHINHHZQ.js';
|
|
10
|
+
import './chunk-FCY5FYJB.js';
|
|
11
|
+
import './chunk-OWNUMPUC.js';
|
|
12
|
+
import './chunk-DMPSQ22Q.js';
|
|
407
13
|
//# sourceMappingURL=analog-clock-picker.js.map
|
|
408
14
|
//# sourceMappingURL=analog-clock-picker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ui/utils.ts","../src/ui/time-field.tsx","../src/booking/analog-clock-picker.tsx"],"names":["useState","jsxs","jsx"],"mappings":"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","file":"analog-clock-picker.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Clock } from \"lucide-react\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"./popover\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"./select\";\nimport { cn } from \"./utils\";\n\nconst SELECT_CONTENT_Z = \"z-[200]\";\n\nconst TIME_SELECT_TRIGGER_CLASS =\n \"border-white/20 bg-card/10 text-popover-foreground hover:bg-card/15 hover:border-white/30 focus-visible:border-blue-400/70 focus-visible:ring-blue-400/30 [&_svg:not([class*='text-'])]:text-white/60\";\n\nexport function parseHm(value: string): { h: number; m: number } | undefined {\n const m = /^([01]\\d|2[0-3]):([0-5]\\d)$/.exec(value.trim());\n if (!m) return undefined;\n const h = Number(m[1]);\n const min = Number(m[2]);\n if (h > 23 || min > 59) return undefined;\n return { h, m: min };\n}\n\nexport function toTimeStr(h: number, m: number): string {\n return `${String(h).padStart(2, \"0\")}:${String(m).padStart(2, \"0\")}`;\n}\n\nexport function formatTimeForDisplay(value: string, timeFormat: \"12h\" | \"24h\"): string {\n const p = parseHm(value);\n if (!p) return value.trim() || \"—\";\n if (timeFormat === \"24h\") return toTimeStr(p.h, p.m);\n const ap = p.h >= 12 ? \"pm\" : \"am\";\n const h12 = p.h % 12 || 12;\n return `${String(h12).padStart(2, \"0\")}:${String(p.m).padStart(2, \"0\")} ${ap}`;\n}\n\nfunction split12(h24: number): { h12: number; ap: \"am\" | \"pm\" } {\n return { h12: h24 % 12 || 12, ap: h24 >= 12 ? \"pm\" : \"am\" };\n}\n\nfunction from12(h12: number, m: number, ap: \"am\" | \"pm\"): { h: number; m: number } {\n if (ap === \"am\") {\n return { h: h12 === 12 ? 0 : h12, m };\n }\n return { h: h12 === 12 ? 12 : h12 + 12, m };\n}\n\nconst HOURS_24 = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, \"0\"));\nconst MINUTES = Array.from({ length: 60 }, (_, i) => String(i).padStart(2, \"0\"));\nconst HOURS_12 = Array.from({ length: 12 }, (_, i) => String(i + 1));\n\nexport function TimeField({\n value,\n onChange,\n disabled,\n id,\n placeholder = \"Select time\",\n triggerClassName,\n align = \"start\",\n timeFormat = \"24h\",\n}: {\n value: string;\n onChange: (v: string) => void;\n disabled?: boolean;\n id?: string;\n placeholder?: string;\n triggerClassName?: string;\n align?: \"start\" | \"center\" | \"end\";\n timeFormat?: \"12h\" | \"24h\";\n}) {\n const [open, setOpen] = React.useState(false);\n\n const parsed = parseHm(value);\n const editor = parsed ?? { h: 9, m: 0 };\n const label = parsed ? formatTimeForDisplay(value, timeFormat) : placeholder;\n const { h12, ap } = split12(editor.h);\n\n const setFrom24 = (h: number, m: number) => {\n onChange(toTimeStr(h, m));\n };\n\n return (\n <Popover modal={false} open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n id={id}\n disabled={disabled}\n className={cn(\n \"flex w-full min-w-0 items-center gap-2 rounded-md border border-border bg-card px-3 h-9 text-left text-sm text-muted-foreground outline-none transition-colors hover:bg-muted/80 focus-visible:border-blue-400 focus-visible:ring-[3px] focus-visible:ring-blue-500/25 disabled:cursor-not-allowed disabled:opacity-50\",\n triggerClassName,\n )}\n >\n <Clock size={13} className=\"shrink-0 text-muted-foreground\" aria-hidden />\n <span className={cn(\"min-w-0 flex-1 truncate\", !parsed && \"text-muted-foreground\")}>{label}</span>\n </button>\n </PopoverTrigger>\n <PopoverContent className=\"z-[100] w-auto p-3\" align={align}>\n {timeFormat === \"24h\" ? (\n <div className=\"flex items-center gap-2\">\n <Select\n value={String(editor.h).padStart(2, \"0\")}\n onValueChange={(v) => setFrom24(Number(v), editor.m)}\n >\n <SelectTrigger size=\"sm\" className={cn(\"h-8 w-[4.75rem] text-xs\", TIME_SELECT_TRIGGER_CLASS)}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent position=\"popper\" className={SELECT_CONTENT_Z}>\n {HOURS_24.map((h) => (\n <SelectItem key={h} value={h} className=\"text-xs\">\n {h}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <span className=\"text-xs font-medium text-white/60\">:</span>\n <Select\n value={String(editor.m).padStart(2, \"0\")}\n onValueChange={(v) => setFrom24(editor.h, Number(v))}\n >\n <SelectTrigger size=\"sm\" className={cn(\"h-8 w-[4.75rem] text-xs\", TIME_SELECT_TRIGGER_CLASS)}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent position=\"popper\" className={SELECT_CONTENT_Z}>\n {MINUTES.map((mi) => (\n <SelectItem key={mi} value={mi} className=\"text-xs\">\n {mi}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : (\n <div className=\"flex items-center gap-2\">\n <Select\n value={String(h12)}\n onValueChange={(v) => {\n const next = from12(Number(v), editor.m, ap);\n setFrom24(next.h, next.m);\n }}\n >\n <SelectTrigger size=\"sm\" className={cn(\"h-8 w-[3.75rem] text-xs\", TIME_SELECT_TRIGGER_CLASS)}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent position=\"popper\" className={SELECT_CONTENT_Z}>\n {HOURS_12.map((h) => (\n <SelectItem key={h} value={h} className=\"text-xs\">\n {h}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <span className=\"text-xs font-medium text-white/60\">:</span>\n <Select\n value={String(editor.m).padStart(2, \"0\")}\n onValueChange={(v) => {\n const next = from12(h12, Number(v), ap);\n setFrom24(next.h, next.m);\n }}\n >\n <SelectTrigger size=\"sm\" className={cn(\"h-8 w-[4.75rem] text-xs\", TIME_SELECT_TRIGGER_CLASS)}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent position=\"popper\" className={SELECT_CONTENT_Z}>\n {MINUTES.map((mi) => (\n <SelectItem key={mi} value={mi} className=\"text-xs\">\n {mi}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select\n value={ap}\n onValueChange={(v) => {\n const next = from12(h12, editor.m, v as \"am\" | \"pm\");\n setFrom24(next.h, next.m);\n }}\n >\n <SelectTrigger size=\"sm\" className={cn(\"h-8 w-[4.25rem] text-xs\", TIME_SELECT_TRIGGER_CLASS)}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent position=\"popper\" className={SELECT_CONTENT_Z}>\n <SelectItem value=\"am\" className=\"text-xs\">\n am\n </SelectItem>\n <SelectItem value=\"pm\" className=\"text-xs\">\n pm\n </SelectItem>\n </SelectContent>\n </Select>\n </div>\n )}\n </PopoverContent>\n </Popover>\n );\n}\n","import { useCallback, useMemo, useRef, useState } from \"react\";\nimport {\n formatTimeForDisplay,\n parseHm,\n toTimeStr,\n} from \"../ui/time-field\";\nimport { cn } from \"../ui\";\n\nconst SIZE = 248;\nconst CX = SIZE / 2;\nconst CY = SIZE / 2;\nconst FACE_R = 98;\nconst HOUR_LABEL_R = 72;\nconst MINUTE_TICK_R = 84;\n\ntype ClockPhase = \"hour\" | \"minute\";\n\nfunction pointToAngleDeg(x: number, y: number): number {\n const dx = x - CX;\n const dy = y - CY;\n let deg = (Math.atan2(dy, dx) * 180) / Math.PI + 90;\n if (deg < 0) deg += 360;\n return deg;\n}\n\nfunction angleToHour12(deg: number): number {\n const h = Math.round(deg / 30) % 12;\n return h === 0 ? 12 : h;\n}\n\nfunction angleToHour24(deg: number): number {\n return Math.round(deg / 15) % 24;\n}\n\nfunction angleToMinute(deg: number): number {\n return Math.round(deg / 6) % 60;\n}\n\nfunction hour12To24(h12: number, ap: \"am\" | \"pm\"): number {\n if (ap === \"am\") return h12 === 12 ? 0 : h12;\n return h12 === 12 ? 12 : h12 + 12;\n}\n\nfunction hour24To12(h24: number): { h12: number; ap: \"am\" | \"pm\" } {\n return { h12: h24 % 12 || 12, ap: h24 >= 12 ? \"pm\" : \"am\" };\n}\n\nfunction handEnd(deg: number, length: number): { x: number; y: number } {\n const rad = ((deg - 90) * Math.PI) / 180;\n return { x: CX + Math.cos(rad) * length, y: CY + Math.sin(rad) * length };\n}\n\nfunction hourCenterDeg(h: number, timeFormat: \"12h\" | \"24h\"): number {\n if (timeFormat === \"24h\") return h * 15;\n return (h % 12) * 30;\n}\n\nfunction sectorPath(centerDeg: number, spread: number, radius: number): string {\n const start = handEnd(centerDeg - spread, radius);\n const end = handEnd(centerDeg + spread, radius);\n return `M ${CX} ${CY} L ${start.x} ${start.y} A ${radius} ${radius} 0 0 1 ${end.x} ${end.y} Z`;\n}\n\nfunction pointerTarget(\n clientX: number,\n clientY: number,\n svg: SVGSVGElement,\n phase: ClockPhase,\n timeFormat: \"12h\" | \"24h\",\n): number | null {\n const rect = svg.getBoundingClientRect();\n const scale = SIZE / rect.width;\n const x = (clientX - rect.left) * scale;\n const y = (clientY - rect.top) * scale;\n const dist = Math.hypot(x - CX, y - CY);\n if (dist < 24 || dist > FACE_R + 8) return null;\n\n const deg = pointToAngleDeg(x, y);\n if (phase === \"hour\") {\n return timeFormat === \"24h\" ? angleToHour24(deg) : angleToHour12(deg);\n }\n return angleToMinute(deg);\n}\n\nexport function AnalogClockPicker({\n value,\n onChange,\n timeFormat = \"24h\",\n className,\n}: {\n value: string;\n onChange: (v: string) => void;\n timeFormat?: \"12h\" | \"24h\";\n className?: string;\n}) {\n const svgRef = useRef<SVGSVGElement>(null);\n const parsed = parseHm(value);\n const editor = parsed ?? { h: 9, m: 0 };\n const { h12, ap } = hour24To12(editor.h);\n\n const [phase, setPhase] = useState<ClockPhase>(\"hour\");\n const [hoverTarget, setHoverTarget] = useState<number | null>(null);\n\n const setTime = useCallback(\n (h: number, m: number) => {\n onChange(toTimeStr(h, m));\n },\n [onChange],\n );\n\n const handlePointer = useCallback(\n (clientX: number, clientY: number) => {\n const svg = svgRef.current;\n if (!svg) return;\n const rect = svg.getBoundingClientRect();\n const scale = SIZE / rect.width;\n const x = (clientX - rect.left) * scale;\n const y = (clientY - rect.top) * scale;\n const deg = pointToAngleDeg(x, y);\n\n if (phase === \"hour\") {\n if (timeFormat === \"24h\") {\n const h24 = angleToHour24(deg);\n setTime(h24, editor.m);\n } else {\n const h = angleToHour12(deg);\n setTime(hour12To24(h, ap), editor.m);\n }\n setPhase(\"minute\");\n } else {\n const m = angleToMinute(deg);\n setTime(editor.h, m);\n }\n },\n [phase, timeFormat, ap, editor.h, editor.m, setTime],\n );\n\n const updateHover = useCallback(\n (clientX: number, clientY: number) => {\n const svg = svgRef.current;\n if (!svg) return;\n setHoverTarget(pointerTarget(clientX, clientY, svg, phase, timeFormat));\n },\n [phase, timeFormat],\n );\n\n const previewHour =\n phase === \"hour\" && hoverTarget != null\n ? timeFormat === \"24h\"\n ? hoverTarget\n : hour12To24(hoverTarget, ap)\n : editor.h;\n\n const previewMinute = phase === \"minute\" && hoverTarget != null ? hoverTarget : editor.m;\n\n const hourHandDeg = (previewHour % 12) * 30 + previewMinute * 0.5;\n const minuteHandDeg = previewMinute * 6;\n const hourEnd = handEnd(hourHandDeg, 46);\n const minuteEnd = handEnd(minuteHandDeg, 64);\n\n const selectedHourKey =\n timeFormat === \"24h\" ? editor.h : h12;\n const hoveredHourKey = phase === \"hour\" ? hoverTarget : null;\n\n const hourMarkers = useMemo(() => {\n if (timeFormat === \"24h\") {\n return Array.from({ length: 24 }, (_, h) => {\n const deg = h * 15;\n const rad = ((deg - 90) * Math.PI) / 180;\n const showLabel = h % 3 === 0;\n const r = showLabel ? HOUR_LABEL_R - 6 : HOUR_LABEL_R + 4;\n return {\n key: h,\n label: showLabel ? String(h).padStart(2, \"0\") : null,\n x: CX + Math.cos(rad) * r,\n y: CY + Math.sin(rad) * r,\n deg,\n h,\n };\n });\n }\n return Array.from({ length: 12 }, (_, i) => {\n const h = i === 0 ? 12 : i;\n const deg = h * 30;\n const rad = ((deg - 90) * Math.PI) / 180;\n return {\n key: h,\n label: String(h),\n x: CX + Math.cos(rad) * HOUR_LABEL_R,\n y: CY + Math.sin(rad) * HOUR_LABEL_R,\n deg,\n h,\n };\n });\n }, [timeFormat]);\n\n const minuteMarkers = useMemo(\n () =>\n Array.from({ length: 12 }, (_, i) => {\n const m = i * 5;\n const deg = m * 6;\n const rad = ((deg - 90) * Math.PI) / 180;\n const selected = editor.m === m;\n const hovered =\n hoverTarget != null && Math.round(hoverTarget / 5) * 5 === m;\n return {\n m,\n x: CX + Math.cos(rad) * MINUTE_TICK_R,\n y: CY + Math.sin(rad) * MINUTE_TICK_R,\n selected,\n hovered,\n };\n }),\n [editor.m, hoverTarget],\n );\n\n const displayTime = formatTimeForDisplay(value || toTimeStr(editor.h, editor.m), timeFormat);\n\n return (\n <div className={cn(\"flex flex-col items-center gap-4\", className)}>\n <div className=\"flex items-center gap-1 rounded-2xl border border-border bg-muted/60 px-4 py-2.5\">\n <button\n type=\"button\"\n onClick={() => setPhase(\"hour\")}\n className={cn(\n \"rounded-lg px-3 py-1 text-2xl font-semibold tabular-nums transition-colors\",\n phase === \"hour\"\n ? \"bg-blue-500 text-white shadow-sm\"\n : \"text-foreground hover:bg-accent\",\n )}\n >\n {timeFormat === \"24h\"\n ? String(editor.h).padStart(2, \"0\")\n : String(h12).padStart(2, \"0\")}\n </button>\n <span className=\"text-2xl font-semibold text-muted-foreground\">:</span>\n <button\n type=\"button\"\n onClick={() => setPhase(\"minute\")}\n className={cn(\n \"rounded-lg px-3 py-1 text-2xl font-semibold tabular-nums transition-colors\",\n phase === \"minute\"\n ? \"bg-blue-500 text-white shadow-sm\"\n : \"text-foreground hover:bg-accent\",\n )}\n >\n {String(editor.m).padStart(2, \"0\")}\n </button>\n {timeFormat === \"12h\" ? (\n <div className=\"ml-2 flex flex-col gap-0.5\">\n {([\"am\", \"pm\"] as const).map((p) => (\n <button\n key={p}\n type=\"button\"\n onClick={() => setTime(hour12To24(h12, p), editor.m)}\n className={cn(\n \"rounded-md px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide transition-colors\",\n ap === p\n ? \"bg-blue-100 text-blue-700 dark:bg-blue-950/50 dark:text-blue-300\"\n : \"text-muted-foreground hover:bg-accent\",\n )}\n >\n {p}\n </button>\n ))}\n </div>\n ) : null}\n </div>\n\n <div className=\"relative\">\n <svg\n ref={svgRef}\n width={SIZE}\n height={SIZE}\n viewBox={`0 0 ${SIZE} ${SIZE}`}\n className=\"cursor-pointer touch-none select-none\"\n onClick={(e) => handlePointer(e.clientX, e.clientY)}\n onPointerMove={(e) => updateHover(e.clientX, e.clientY)}\n onPointerLeave={() => setHoverTarget(null)}\n onPointerDown={(e) => {\n e.preventDefault();\n handlePointer(e.clientX, e.clientY);\n }}\n >\n <circle cx={CX} cy={CY} r={FACE_R} className=\"fill-blue-50/80 dark:fill-blue-950/30\" />\n <circle\n cx={CX}\n cy={CY}\n r={FACE_R}\n className=\"fill-none stroke-border\"\n strokeWidth={1.5}\n />\n\n {phase === \"hour\" && hoverTarget != null ? (\n <path\n d={sectorPath(\n hourCenterDeg(hoverTarget, timeFormat),\n timeFormat === \"24h\" ? 7.5 : 15,\n FACE_R - 2,\n )}\n className=\"fill-blue-200/70 transition-opacity dark:fill-blue-500/25\"\n />\n ) : null}\n\n {phase === \"minute\" && hoverTarget != null ? (\n <path\n d={sectorPath(hoverTarget * 6, 3, FACE_R - 2)}\n className=\"fill-blue-200/70 transition-opacity dark:fill-blue-500/25\"\n />\n ) : null}\n\n {phase === \"hour\"\n ? hourMarkers.map((m) => {\n const isSelected = m.h === selectedHourKey || (timeFormat === \"12h\" && m.h === h12);\n const isHovered = m.h === hoveredHourKey;\n const active = isHovered || (isSelected && hoveredHourKey == null);\n return (\n <g key={m.key}>\n {active ? (\n <circle\n cx={m.x}\n cy={m.y}\n r={isHovered ? 18 : 16}\n className={cn(\n \"transition-all duration-100\",\n isHovered\n ? \"fill-blue-500 shadow-sm\"\n : \"fill-blue-400/90\",\n )}\n />\n ) : null}\n {m.label ? (\n <text\n x={m.x}\n y={m.y}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className={cn(\n \"pointer-events-none text-[11px] font-semibold transition-colors duration-100\",\n active ? \"fill-white\" : \"fill-muted-foreground\",\n isHovered && \"text-[12px] font-bold\",\n )}\n >\n {m.label}\n </text>\n ) : (\n <circle\n cx={m.x}\n cy={m.y}\n r={isHovered ? 5 : 2}\n className={cn(\n \"pointer-events-none transition-all duration-100\",\n isHovered ? \"fill-white\" : \"fill-muted-foreground/40\",\n )}\n />\n )}\n </g>\n );\n })\n : minuteMarkers.map((m) => {\n const active = m.selected || m.hovered;\n return (\n <g key={m.m}>\n <circle\n cx={m.x}\n cy={m.y}\n r={m.hovered ? 16 : m.selected ? 14 : 4}\n className={cn(\n \"transition-all duration-100\",\n m.hovered\n ? \"fill-blue-400\"\n : m.selected\n ? \"fill-blue-500\"\n : \"fill-blue-200/80 dark:fill-blue-800/60\",\n )}\n />\n {active || m.m % 15 === 0 ? (\n <text\n x={m.x}\n y={m.y}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className={cn(\n \"pointer-events-none text-[10px] font-semibold transition-colors duration-100\",\n active ? \"fill-white\" : \"fill-blue-700 dark:fill-blue-300\",\n )}\n >\n {String(m.m).padStart(2, \"0\")}\n </text>\n ) : null}\n </g>\n );\n })}\n\n <line\n x1={CX}\n y1={CY}\n x2={hourEnd.x}\n y2={hourEnd.y}\n className={cn(\n \"stroke-blue-600 transition-all duration-100 dark:stroke-blue-400\",\n phase === \"hour\" && hoverTarget != null && \"stroke-blue-500 opacity-90\",\n )}\n strokeWidth={3.5}\n strokeLinecap=\"round\"\n />\n <line\n x1={CX}\n y1={CY}\n x2={minuteEnd.x}\n y2={minuteEnd.y}\n className={cn(\n \"stroke-blue-500 transition-all duration-100 dark:stroke-blue-300\",\n phase === \"minute\" && hoverTarget != null && \"stroke-blue-400 opacity-90\",\n )}\n strokeWidth={2.5}\n strokeLinecap=\"round\"\n />\n <circle cx={CX} cy={CY} r={5} className=\"fill-blue-600 dark:fill-blue-400\" />\n <circle cx={CX} cy={CY} r={2.5} className=\"fill-white\" />\n </svg>\n </div>\n\n <p className=\"text-center text-[11px] text-muted-foreground\">\n {phase === \"hour\" ? \"Hover or tap an hour\" : \"Hover or tap minutes · \"}\n <span className=\"font-medium text-foreground\">\n {hoverTarget != null && phase === \"hour\"\n ? formatTimeForDisplay(\n toTimeStr(\n timeFormat === \"24h\" ? hoverTarget : hour12To24(hoverTarget, ap),\n editor.m,\n ),\n timeFormat,\n )\n : hoverTarget != null && phase === \"minute\"\n ? formatTimeForDisplay(toTimeStr(editor.h, hoverTarget), timeFormat)\n : displayTime}\n </span>\n </p>\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"analog-clock-picker.js"}
|