@fanvue/ui 1.2.1 → 1.4.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/cjs/components/AudioUpload/AudioUpload.cjs +286 -0
- package/dist/cjs/components/AudioUpload/AudioUpload.cjs.map +1 -0
- package/dist/cjs/components/AudioUpload/AudioWaveform.cjs +121 -0
- package/dist/cjs/components/AudioUpload/AudioWaveform.cjs.map +1 -0
- package/dist/cjs/components/AudioUpload/audioUtils.cjs +44 -0
- package/dist/cjs/components/AudioUpload/audioUtils.cjs.map +1 -0
- package/dist/cjs/components/AudioUpload/constants.cjs +21 -0
- package/dist/cjs/components/AudioUpload/constants.cjs.map +1 -0
- package/dist/cjs/components/AudioUpload/useAudioRecorder.cjs +191 -0
- package/dist/cjs/components/AudioUpload/useAudioRecorder.cjs.map +1 -0
- package/dist/cjs/components/DatePicker/DatePicker.cjs +1 -1
- package/dist/cjs/components/DatePicker/DatePicker.cjs.map +1 -1
- package/dist/cjs/components/Icons/CheckOutlineIcon.cjs +55 -0
- package/dist/cjs/components/Icons/CheckOutlineIcon.cjs.map +1 -0
- package/dist/cjs/components/Icons/EyeClosedIcon.cjs +49 -0
- package/dist/cjs/components/Icons/EyeClosedIcon.cjs.map +1 -0
- package/dist/cjs/components/Icons/UploadCloudIcon.cjs +61 -0
- package/dist/cjs/components/Icons/UploadCloudIcon.cjs.map +1 -0
- package/dist/cjs/components/PasswordField/PasswordField.cjs +59 -0
- package/dist/cjs/components/PasswordField/PasswordField.cjs.map +1 -0
- package/dist/cjs/components/TextArea/TextArea.cjs +209 -0
- package/dist/cjs/components/TextArea/TextArea.cjs.map +1 -0
- package/dist/cjs/components/TextField/TextField.cjs +22 -34
- package/dist/cjs/components/TextField/TextField.cjs.map +1 -1
- package/dist/cjs/index.cjs +14 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/components/AudioUpload/AudioUpload.mjs +269 -0
- package/dist/components/AudioUpload/AudioUpload.mjs.map +1 -0
- package/dist/components/AudioUpload/AudioWaveform.mjs +104 -0
- package/dist/components/AudioUpload/AudioWaveform.mjs.map +1 -0
- package/dist/components/AudioUpload/audioUtils.mjs +44 -0
- package/dist/components/AudioUpload/audioUtils.mjs.map +1 -0
- package/dist/components/AudioUpload/constants.mjs +21 -0
- package/dist/components/AudioUpload/constants.mjs.map +1 -0
- package/dist/components/AudioUpload/useAudioRecorder.mjs +174 -0
- package/dist/components/AudioUpload/useAudioRecorder.mjs.map +1 -0
- package/dist/components/DatePicker/DatePicker.mjs +1 -1
- package/dist/components/DatePicker/DatePicker.mjs.map +1 -1
- package/dist/components/Icons/CheckOutlineIcon.mjs +38 -0
- package/dist/components/Icons/CheckOutlineIcon.mjs.map +1 -0
- package/dist/components/Icons/EyeClosedIcon.mjs +32 -0
- package/dist/components/Icons/EyeClosedIcon.mjs.map +1 -0
- package/dist/components/Icons/UploadCloudIcon.mjs +44 -0
- package/dist/components/Icons/UploadCloudIcon.mjs.map +1 -0
- package/dist/components/PasswordField/PasswordField.mjs +42 -0
- package/dist/components/PasswordField/PasswordField.mjs.map +1 -0
- package/dist/components/TextArea/TextArea.mjs +192 -0
- package/dist/components/TextArea/TextArea.mjs.map +1 -0
- package/dist/components/TextField/TextField.mjs +22 -34
- package/dist/components/TextField/TextField.mjs.map +1 -1
- package/dist/index.d.ts +178 -0
- package/dist/index.mjs +15 -1
- package/dist/index.mjs.map +1 -1
- package/dist/styles/theme.css +3 -0
- package/package.json +2 -1
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const React = require("react");
|
|
5
|
+
const audioUtils = require("./audioUtils.cjs");
|
|
6
|
+
const constants = require("./constants.cjs");
|
|
7
|
+
function _interopNamespaceDefault(e) {
|
|
8
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
9
|
+
if (e) {
|
|
10
|
+
for (const k in e) {
|
|
11
|
+
if (k !== "default") {
|
|
12
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: () => e[k]
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
24
|
+
function stopMediaTracks(stream) {
|
|
25
|
+
if (!stream) return;
|
|
26
|
+
for (const track of stream.getTracks()) {
|
|
27
|
+
track.stop();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function closeAudioContext(ctx) {
|
|
31
|
+
if (ctx?.state !== "closed") {
|
|
32
|
+
ctx?.close();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function classifyMediaError(error) {
|
|
36
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
37
|
+
const name = error?.name ?? err.name;
|
|
38
|
+
if (name === "NotAllowedError" || name === "PermissionDeniedError") {
|
|
39
|
+
return { handler: "permission", error: err };
|
|
40
|
+
}
|
|
41
|
+
if (name === "NotFoundError") {
|
|
42
|
+
return { handler: "permission", error: new Error("No microphone found on this device") };
|
|
43
|
+
}
|
|
44
|
+
return { handler: "error", error: err };
|
|
45
|
+
}
|
|
46
|
+
function useAudioRecorder(options = {}) {
|
|
47
|
+
const {
|
|
48
|
+
maxDuration = constants.DEFAULT_MAX_RECORDING_DURATION,
|
|
49
|
+
minDuration = constants.DEFAULT_MIN_RECORDING_DURATION,
|
|
50
|
+
onComplete,
|
|
51
|
+
onTooShort,
|
|
52
|
+
onPermissionError,
|
|
53
|
+
onError
|
|
54
|
+
} = options;
|
|
55
|
+
const [isRecording, setIsRecording] = React__namespace.useState(false);
|
|
56
|
+
const [elapsedMs, setElapsedMs] = React__namespace.useState(0);
|
|
57
|
+
const [analyserNode, setAnalyserNode] = React__namespace.useState(null);
|
|
58
|
+
const mediaRecorderRef = React__namespace.useRef(null);
|
|
59
|
+
const audioChunksRef = React__namespace.useRef([]);
|
|
60
|
+
const streamRef = React__namespace.useRef(null);
|
|
61
|
+
const audioContextRef = React__namespace.useRef(null);
|
|
62
|
+
const sourceNodeRef = React__namespace.useRef(null);
|
|
63
|
+
const startTimeRef = React__namespace.useRef(0);
|
|
64
|
+
const intervalRef = React__namespace.useRef(null);
|
|
65
|
+
const timeoutRef = React__namespace.useRef(null);
|
|
66
|
+
const onCompleteRef = React__namespace.useRef(onComplete);
|
|
67
|
+
const onTooShortRef = React__namespace.useRef(onTooShort);
|
|
68
|
+
const onPermissionErrorRef = React__namespace.useRef(onPermissionError);
|
|
69
|
+
const onErrorRef = React__namespace.useRef(onError);
|
|
70
|
+
onCompleteRef.current = onComplete;
|
|
71
|
+
onTooShortRef.current = onTooShort;
|
|
72
|
+
onPermissionErrorRef.current = onPermissionError;
|
|
73
|
+
onErrorRef.current = onError;
|
|
74
|
+
const maxDurationMs = maxDuration * 1e3;
|
|
75
|
+
const minDurationMs = minDuration * 1e3;
|
|
76
|
+
const isSupported = React__namespace.useMemo(
|
|
77
|
+
() => typeof window !== "undefined" && typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function" && typeof MediaRecorder !== "undefined",
|
|
78
|
+
[]
|
|
79
|
+
);
|
|
80
|
+
const resetState = React__namespace.useCallback(() => {
|
|
81
|
+
sourceNodeRef.current?.disconnect();
|
|
82
|
+
sourceNodeRef.current = null;
|
|
83
|
+
stopMediaTracks(streamRef.current);
|
|
84
|
+
streamRef.current = null;
|
|
85
|
+
closeAudioContext(audioContextRef.current);
|
|
86
|
+
audioContextRef.current = null;
|
|
87
|
+
setAnalyserNode(null);
|
|
88
|
+
if (intervalRef.current) clearInterval(intervalRef.current);
|
|
89
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
90
|
+
intervalRef.current = null;
|
|
91
|
+
timeoutRef.current = null;
|
|
92
|
+
setIsRecording(false);
|
|
93
|
+
setElapsedMs(0);
|
|
94
|
+
}, []);
|
|
95
|
+
const cleanup = React__namespace.useCallback(() => {
|
|
96
|
+
if (mediaRecorderRef.current?.state === "recording") {
|
|
97
|
+
mediaRecorderRef.current.stop();
|
|
98
|
+
}
|
|
99
|
+
mediaRecorderRef.current = null;
|
|
100
|
+
audioChunksRef.current = [];
|
|
101
|
+
resetState();
|
|
102
|
+
}, [resetState]);
|
|
103
|
+
const handleRecordingStop = React__namespace.useCallback(
|
|
104
|
+
(mimeType) => {
|
|
105
|
+
const elapsed = Date.now() - startTimeRef.current;
|
|
106
|
+
const chunks = audioChunksRef.current;
|
|
107
|
+
resetState();
|
|
108
|
+
mediaRecorderRef.current = null;
|
|
109
|
+
audioChunksRef.current = [];
|
|
110
|
+
if (chunks.length === 0) return;
|
|
111
|
+
const blob = new Blob(chunks, { type: mimeType });
|
|
112
|
+
if (elapsed < minDurationMs) {
|
|
113
|
+
onTooShortRef.current?.(elapsed, minDurationMs);
|
|
114
|
+
} else {
|
|
115
|
+
onCompleteRef.current?.(blob, elapsed);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
[minDurationMs, resetState]
|
|
119
|
+
);
|
|
120
|
+
const startRecording = React__namespace.useCallback(async () => {
|
|
121
|
+
if (!isSupported) {
|
|
122
|
+
onErrorRef.current?.(new Error("Audio recording is not supported in this browser"));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
127
|
+
streamRef.current = stream;
|
|
128
|
+
const audioContext = new AudioContext();
|
|
129
|
+
audioContextRef.current = audioContext;
|
|
130
|
+
const source = audioContext.createMediaStreamSource(stream);
|
|
131
|
+
sourceNodeRef.current = source;
|
|
132
|
+
const analyser = audioContext.createAnalyser();
|
|
133
|
+
analyser.fftSize = 256;
|
|
134
|
+
source.connect(analyser);
|
|
135
|
+
setAnalyserNode(analyser);
|
|
136
|
+
const mimeType = audioUtils.getRecordingMimeType();
|
|
137
|
+
const recorderOptions = mimeType ? { mimeType } : void 0;
|
|
138
|
+
const mediaRecorder = new MediaRecorder(stream, recorderOptions);
|
|
139
|
+
mediaRecorderRef.current = mediaRecorder;
|
|
140
|
+
audioChunksRef.current = [];
|
|
141
|
+
mediaRecorder.ondataavailable = (event) => {
|
|
142
|
+
if (event.data.size > 0) {
|
|
143
|
+
audioChunksRef.current.push(event.data);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
mediaRecorder.onstop = () => handleRecordingStop(mediaRecorder.mimeType);
|
|
147
|
+
mediaRecorder.onerror = () => {
|
|
148
|
+
onErrorRef.current?.(new Error("Recording failed"));
|
|
149
|
+
cleanup();
|
|
150
|
+
};
|
|
151
|
+
mediaRecorder.start();
|
|
152
|
+
startTimeRef.current = Date.now();
|
|
153
|
+
setIsRecording(true);
|
|
154
|
+
setElapsedMs(0);
|
|
155
|
+
intervalRef.current = setInterval(() => {
|
|
156
|
+
setElapsedMs(Date.now() - startTimeRef.current);
|
|
157
|
+
}, 200);
|
|
158
|
+
timeoutRef.current = setTimeout(() => {
|
|
159
|
+
if (mediaRecorderRef.current?.state === "recording") {
|
|
160
|
+
mediaRecorderRef.current.stop();
|
|
161
|
+
}
|
|
162
|
+
}, maxDurationMs);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
cleanup();
|
|
165
|
+
const classified = classifyMediaError(error);
|
|
166
|
+
if (classified.handler === "permission") {
|
|
167
|
+
onPermissionErrorRef.current?.(classified.error);
|
|
168
|
+
} else {
|
|
169
|
+
onErrorRef.current?.(classified.error);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}, [isSupported, maxDurationMs, handleRecordingStop, cleanup]);
|
|
173
|
+
const stopRecording = React__namespace.useCallback(() => {
|
|
174
|
+
if (mediaRecorderRef.current?.state === "recording") {
|
|
175
|
+
mediaRecorderRef.current.stop();
|
|
176
|
+
}
|
|
177
|
+
}, []);
|
|
178
|
+
React__namespace.useEffect(() => {
|
|
179
|
+
return cleanup;
|
|
180
|
+
}, [cleanup]);
|
|
181
|
+
return {
|
|
182
|
+
isRecording,
|
|
183
|
+
elapsedMs,
|
|
184
|
+
startRecording,
|
|
185
|
+
stopRecording,
|
|
186
|
+
analyserNode,
|
|
187
|
+
isSupported
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
exports.useAudioRecorder = useAudioRecorder;
|
|
191
|
+
//# sourceMappingURL=useAudioRecorder.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAudioRecorder.cjs","sources":["../../../../src/components/AudioUpload/useAudioRecorder.ts"],"sourcesContent":["import * as React from \"react\";\nimport { getRecordingMimeType } from \"./audioUtils\";\nimport { DEFAULT_MAX_RECORDING_DURATION, DEFAULT_MIN_RECORDING_DURATION } from \"./constants\";\n\nexport interface UseAudioRecorderOptions {\n /** Maximum recording duration in seconds. @default 30 */\n maxDuration?: number;\n /** Minimum recording duration in seconds. @default 5 */\n minDuration?: number;\n /** Called when recording completes successfully (meets minimum duration) */\n onComplete?: (blob: Blob, durationMs: number) => void;\n /** Called when recording is stopped but does not meet minimum duration */\n onTooShort?: (durationMs: number, minDurationMs: number) => void;\n /** Called when microphone permission is denied or unavailable */\n onPermissionError?: (error: Error) => void;\n /** Called on any recording error */\n onError?: (error: Error) => void;\n}\n\nexport interface UseAudioRecorderReturn {\n /** Whether audio is currently being recorded */\n isRecording: boolean;\n /** Elapsed recording time in milliseconds */\n elapsedMs: number;\n /** Start recording. Requests mic permission if needed. */\n startRecording: () => Promise<void>;\n /** Stop recording. Triggers onComplete or onTooShort callback. */\n stopRecording: () => void;\n /** AnalyserNode for waveform visualization (null when not recording) */\n analyserNode: AnalyserNode | null;\n /** Whether the browser supports audio recording */\n isSupported: boolean;\n}\n\nfunction stopMediaTracks(stream: MediaStream | null) {\n if (!stream) return;\n for (const track of stream.getTracks()) {\n track.stop();\n }\n}\n\nfunction closeAudioContext(ctx: AudioContext | null) {\n if (ctx?.state !== \"closed\") {\n ctx?.close();\n }\n}\n\nfunction classifyMediaError(error: unknown): { handler: \"permission\" | \"error\"; error: Error } {\n const err = error instanceof Error ? error : new Error(String(error));\n // Read name from the original error — DOMException may not extend Error in all environments\n const name = (error as { name?: string })?.name ?? err.name;\n if (name === \"NotAllowedError\" || name === \"PermissionDeniedError\") {\n return { handler: \"permission\", error: err };\n }\n if (name === \"NotFoundError\") {\n return { handler: \"permission\", error: new Error(\"No microphone found on this device\") };\n }\n return { handler: \"error\", error: err };\n}\n\n/**\n * Hook for audio recording with MediaRecorder API.\n * Provides recording controls, timer, and an AnalyserNode for waveform visualization.\n */\nexport function useAudioRecorder(options: UseAudioRecorderOptions = {}): UseAudioRecorderReturn {\n const {\n maxDuration = DEFAULT_MAX_RECORDING_DURATION,\n minDuration = DEFAULT_MIN_RECORDING_DURATION,\n onComplete,\n onTooShort,\n onPermissionError,\n onError,\n } = options;\n\n const [isRecording, setIsRecording] = React.useState(false);\n const [elapsedMs, setElapsedMs] = React.useState(0);\n const [analyserNode, setAnalyserNode] = React.useState<AnalyserNode | null>(null);\n\n const mediaRecorderRef = React.useRef<MediaRecorder | null>(null);\n const audioChunksRef = React.useRef<Blob[]>([]);\n const streamRef = React.useRef<MediaStream | null>(null);\n const audioContextRef = React.useRef<AudioContext | null>(null);\n const sourceNodeRef = React.useRef<MediaStreamAudioSourceNode | null>(null);\n const startTimeRef = React.useRef<number>(0);\n const intervalRef = React.useRef<ReturnType<typeof setInterval> | null>(null);\n const timeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Store latest callbacks in refs to avoid re-creating startRecording/stopRecording\n const onCompleteRef = React.useRef(onComplete);\n const onTooShortRef = React.useRef(onTooShort);\n const onPermissionErrorRef = React.useRef(onPermissionError);\n const onErrorRef = React.useRef(onError);\n onCompleteRef.current = onComplete;\n onTooShortRef.current = onTooShort;\n onPermissionErrorRef.current = onPermissionError;\n onErrorRef.current = onError;\n\n const maxDurationMs = maxDuration * 1000;\n const minDurationMs = minDuration * 1000;\n\n const isSupported = React.useMemo(\n () =>\n typeof window !== \"undefined\" &&\n typeof navigator !== \"undefined\" &&\n typeof navigator.mediaDevices?.getUserMedia === \"function\" &&\n typeof MediaRecorder !== \"undefined\",\n [],\n );\n\n const resetState = React.useCallback(() => {\n sourceNodeRef.current?.disconnect();\n sourceNodeRef.current = null;\n stopMediaTracks(streamRef.current);\n streamRef.current = null;\n closeAudioContext(audioContextRef.current);\n audioContextRef.current = null;\n setAnalyserNode(null);\n if (intervalRef.current) clearInterval(intervalRef.current);\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n intervalRef.current = null;\n timeoutRef.current = null;\n setIsRecording(false);\n setElapsedMs(0);\n }, []);\n\n const cleanup = React.useCallback(() => {\n if (mediaRecorderRef.current?.state === \"recording\") {\n mediaRecorderRef.current.stop();\n }\n mediaRecorderRef.current = null;\n audioChunksRef.current = [];\n resetState();\n }, [resetState]);\n\n const handleRecordingStop = React.useCallback(\n (mimeType: string) => {\n const elapsed = Date.now() - startTimeRef.current;\n const chunks = audioChunksRef.current;\n\n resetState();\n mediaRecorderRef.current = null;\n audioChunksRef.current = [];\n\n if (chunks.length === 0) return;\n\n const blob = new Blob(chunks, { type: mimeType });\n\n if (elapsed < minDurationMs) {\n onTooShortRef.current?.(elapsed, minDurationMs);\n } else {\n onCompleteRef.current?.(blob, elapsed);\n }\n },\n [minDurationMs, resetState],\n );\n\n const startRecording = React.useCallback(async () => {\n if (!isSupported) {\n onErrorRef.current?.(new Error(\"Audio recording is not supported in this browser\"));\n return;\n }\n\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n streamRef.current = stream;\n\n const audioContext = new AudioContext();\n audioContextRef.current = audioContext;\n const source = audioContext.createMediaStreamSource(stream);\n sourceNodeRef.current = source;\n const analyser = audioContext.createAnalyser();\n analyser.fftSize = 256;\n source.connect(analyser);\n setAnalyserNode(analyser);\n\n const mimeType = getRecordingMimeType();\n const recorderOptions = mimeType ? { mimeType } : undefined;\n const mediaRecorder = new MediaRecorder(stream, recorderOptions);\n mediaRecorderRef.current = mediaRecorder;\n audioChunksRef.current = [];\n\n mediaRecorder.ondataavailable = (event) => {\n if (event.data.size > 0) {\n audioChunksRef.current.push(event.data);\n }\n };\n mediaRecorder.onstop = () => handleRecordingStop(mediaRecorder.mimeType);\n mediaRecorder.onerror = () => {\n onErrorRef.current?.(new Error(\"Recording failed\"));\n cleanup();\n };\n\n mediaRecorder.start();\n startTimeRef.current = Date.now();\n setIsRecording(true);\n setElapsedMs(0);\n intervalRef.current = setInterval(() => {\n setElapsedMs(Date.now() - startTimeRef.current);\n }, 200);\n timeoutRef.current = setTimeout(() => {\n if (mediaRecorderRef.current?.state === \"recording\") {\n mediaRecorderRef.current.stop();\n }\n }, maxDurationMs);\n } catch (error) {\n cleanup();\n const classified = classifyMediaError(error);\n if (classified.handler === \"permission\") {\n onPermissionErrorRef.current?.(classified.error);\n } else {\n onErrorRef.current?.(classified.error);\n }\n }\n }, [isSupported, maxDurationMs, handleRecordingStop, cleanup]);\n\n const stopRecording = React.useCallback(() => {\n if (mediaRecorderRef.current?.state === \"recording\") {\n mediaRecorderRef.current.stop();\n }\n }, []);\n\n React.useEffect(() => {\n return cleanup;\n }, [cleanup]);\n\n return {\n isRecording,\n elapsedMs,\n startRecording,\n stopRecording,\n analyserNode,\n isSupported,\n };\n}\n"],"names":["DEFAULT_MAX_RECORDING_DURATION","DEFAULT_MIN_RECORDING_DURATION","React","getRecordingMimeType"],"mappings":"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"}
|
|
@@ -38,7 +38,7 @@ function DayButton({ day, modifiers, className, ...buttonProps }) {
|
|
|
38
38
|
"relative z-10 inline-flex size-10 cursor-pointer items-center justify-center rounded-lg",
|
|
39
39
|
"typography-body-2-regular",
|
|
40
40
|
"transition-colors hover:bg-brand-green-50 not-disabled:active:bg-brand-green-50",
|
|
41
|
-
"focus-visible:outline-2 focus-visible:outline-
|
|
41
|
+
"focus-visible:outline-2 focus-visible:outline-brand-purple-500 focus-visible:outline-offset-[-2px]",
|
|
42
42
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
43
43
|
modifiers.today && !modifiers.selected && "border border-brand-green-500",
|
|
44
44
|
modifiers.selected && !modifiers.range_middle ? "bg-brand-green-500 text-body-black-solid-constant hover:bg-brand-green-500" : "text-body-100",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatePicker.cjs","sources":["../../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["import { forwardRef, useEffect, useRef } from \"react\";\nimport {\n type ChevronProps,\n type DateRange,\n type DayButtonProps,\n DayPicker,\n type DayPickerProps,\n type DayProps,\n type Modifiers,\n type MonthGridProps,\n type WeekdayProps,\n type WeekdaysProps,\n type WeekProps,\n type WeeksProps,\n} from \"react-day-picker\";\nimport { cn } from \"../../utils/cn\";\nimport type { OmitDistributed } from \"../../utils/types\";\nimport { Button } from \"../Button/Button\";\nimport { ChevronLeftIcon } from \"../Icons/ChevronLeftIcon\";\nimport { ChevronRightIcon } from \"../Icons/ChevronRightIcon\";\n\nexport type { DateRange }; // Needed by consumers when passing props\n\n/** Layout variant — single or side-by-side month display. */\nexport type DatePickerVariant = \"single\" | \"double\";\n\n/** Props specific to the DatePicker wrapper (not inherited from react-day-picker). */\nexport interface DatePickerOwnProps {\n /** Display one month or two side-by-side. @default \"single\" */\n variant?: DatePickerVariant;\n /** Callback fired when the Apply button is clicked. */\n onApply?: () => void;\n /** Callback fired when the Cancel button is clicked. */\n onCancel?: () => void;\n /** Label for the cancel button. @default \"Cancel\" */\n cancelLabel?: string;\n /** Label for the apply button. @default \"Apply\" */\n applyLabel?: string;\n /** Whether to render the cancel / apply footer buttons. @default true */\n showFooter?: boolean;\n /** Additional CSS class name for the outer container. */\n className?: string;\n}\n\nfunction Day({ day, modifiers, className, ...divProps }: DayProps) {\n const { range_start, range_end } = modifiers;\n const isSingleDayRange = range_start && range_end;\n\n return (\n <div\n className={cn(\n className,\n (range_start || range_end) && !isSingleDayRange && \"from-50% from-transparent to-50%\",\n range_start && !isSingleDayRange && \"bg-linear-to-r to-brand-green-50\",\n range_end && !isSingleDayRange && \"bg-linear-to-l to-brand-green-50\",\n )}\n {...divProps}\n />\n );\n}\n\nfunction DayButton({ day, modifiers, className, ...buttonProps }: DayButtonProps) {\n const ref = useRef<HTMLButtonElement>(null);\n\n useEffect(() => {\n if (modifiers.focused) ref.current?.focus();\n }, [modifiers.focused]);\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(\n \"relative z-10 inline-flex size-10 cursor-pointer items-center justify-center rounded-lg\",\n \"typography-body-2-regular\",\n \"transition-colors hover:bg-brand-green-50 not-disabled:active:bg-brand-green-50\",\n \"focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-brand-purple-500\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n modifiers.today && !modifiers.selected && \"border border-brand-green-500\",\n modifiers.selected && !modifiers.range_middle\n ? \"bg-brand-green-500 text-body-black-solid-constant hover:bg-brand-green-500\"\n : \"text-body-100\",\n modifiers.range_middle && \"rounded-none bg-transparent\",\n modifiers.outside && \"pointer-events-none opacity-50\",\n )}\n {...buttonProps}\n />\n );\n}\n\n/** Combined props — own DatePicker options plus all react-day-picker props (except `numberOfMonths`). */\nexport type DatePickerProps = DatePickerOwnProps &\n OmitDistributed<DayPickerProps, \"numberOfMonths\">;\n\n/**\n * A calendar date picker supporting single-date and date-range selection with\n * optional side-by-side month display and footer action buttons.\n *\n * Built on top of [react-day-picker](https://react-day-picker.js.org/) — all\n * `DayPickerProps` (except `numberOfMonths`) are forwarded.\n *\n * @example\n * ```tsx\n * <DatePicker\n * mode=\"range\"\n * type=\"double\"\n * selected={range}\n * onSelect={setRange}\n * onApply={save}\n * onCancel={close}\n * />\n * ```\n */\nexport const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(\n (\n {\n variant = \"single\",\n onApply,\n onCancel,\n cancelLabel = \"Cancel\",\n applyLabel = \"Apply\",\n showFooter = true,\n className,\n formatters,\n ...dayPickerProps\n },\n ref,\n ) => {\n const numberOfMonths = variant === \"double\" ? 2 : 1;\n const isMulti = numberOfMonths > 1;\n\n // Wrap onSelect for range mode: when clicking inside a complete range,\n // move the nearest boundary instead of always resetting the end date.\n const resolvedDayPickerProps = (() => {\n if (dayPickerProps.mode !== \"range\") return dayPickerProps;\n\n const { selected, onSelect } = dayPickerProps as {\n selected?: DateRange;\n onSelect?: (\n range: DateRange | undefined,\n triggerDate: Date,\n modifiers: Modifiers,\n e: React.MouseEvent | React.KeyboardEvent,\n ) => void;\n };\n\n if (!onSelect || !selected?.from || !selected?.to) return dayPickerProps;\n\n const { from, to } = selected;\n\n return {\n ...dayPickerProps,\n onSelect: (\n range: DateRange | undefined,\n triggerDate: Date,\n modifiers: Modifiers,\n e: React.MouseEvent | React.KeyboardEvent,\n ) => {\n const clickedTime = triggerDate.getTime();\n const fromTime = from.getTime();\n const toTime = to.getTime();\n\n if (clickedTime > fromTime && clickedTime < toTime) {\n if (clickedTime - fromTime <= toTime - clickedTime) {\n onSelect({ from: triggerDate, to }, triggerDate, modifiers, e);\n } else {\n onSelect({ from, to: triggerDate }, triggerDate, modifiers, e);\n }\n return;\n }\n\n onSelect(range, triggerDate, modifiers, e);\n },\n } as typeof dayPickerProps;\n })();\n\n return (\n <div\n ref={ref}\n className={cn(\n \"inline-flex flex-col rounded-2xl border border-neutral-200 bg-background-inverse-solid shadow-[0px_6px_12px_0px_rgba(0,0,0,0.1)] backdrop-blur-sm\",\n className,\n )}\n >\n <DayPicker\n showOutsideDays\n numberOfMonths={numberOfMonths}\n formatters={{\n formatCaption: (date: Date) =>\n date.toLocaleDateString(\"en-US\", { month: \"short\", year: \"numeric\" }),\n ...formatters,\n }}\n classNames={{\n root: \"w-full\",\n months: \"relative flex\",\n month: \"flex flex-1 flex-col\",\n month_caption: cn(\"flex items-center py-4\", isMulti ? \"justify-center px-2\" : \"px-5\"),\n caption_label: \"typography-body-1-semibold text-body-100\",\n nav: cn(\n \"absolute top-4 z-20 flex\",\n isMulti ? \"pointer-events-none inset-x-3 justify-between\" : \"right-3 gap-1\",\n ),\n button_previous:\n \"pointer-events-auto inline-flex size-8 cursor-pointer items-center justify-center rounded-full text-body-100 transition-colors hover:bg-brand-green-50 not-disabled:active:bg-brand-green-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-purple-500 disabled:cursor-not-allowed disabled:opacity-50\", // !TODO https://linear.app/fanvue/issue/ENG-7301/swap-out-typography-tailwind-utility-classes\n button_next:\n \"pointer-events-auto inline-flex size-8 cursor-pointer items-center justify-center rounded-full text-body-100 transition-colors hover:bg-brand-green-50 not-disabled:active:bg-brand-green-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-purple-500 disabled:cursor-not-allowed disabled:opacity-50\", // !TODO https://linear.app/fanvue/issue/ENG-7301/swap-out-typography-tailwind-utility-classes\n month_grid: cn(\"mb-4\", isMulti ? \"mx-2\" : \"mx-4\"),\n weekdays: \"flex\",\n weekday:\n \"flex h-[30px] w-10 flex-1 items-center justify-center typography-body-2-regular text-body-200\",\n week: \"flex overflow-hidden rounded-lg\",\n day: \"relative flex w-10 flex-1 items-center justify-center\",\n range_middle: \"bg-brand-green-50\",\n hidden: \"hidden\",\n }}\n components={{\n /**\n * !NOTE: We're unable to use semantic elements for the grid due to rdp, as such we've disabled the a11y lint rules for these elements in biome.json.\n */\n Chevron: ({ orientation }: ChevronProps) =>\n orientation === \"left\" ? <ChevronLeftIcon /> : <ChevronRightIcon />,\n MonthGrid: (props: MonthGridProps) => <div role=\"grid\" {...props} />,\n Weekdays: (props: WeekdaysProps) => <div role=\"row\" {...props} />,\n Weekday: (props: WeekdayProps) => <div role=\"columnheader\" {...props} />,\n Weeks: (props: WeeksProps) => <div role=\"rowgroup\" {...props} />,\n Week: ({ week, ...props }: WeekProps) => <div role=\"row\" {...props} />,\n Day,\n DayButton,\n }}\n {...resolvedDayPickerProps}\n />\n\n {showFooter && (\n <div className=\"flex gap-4 px-5 pb-4\">\n <Button variant=\"secondary\" size=\"40\" className=\"flex-1\" onClick={onCancel}>\n {cancelLabel}\n </Button>\n <Button variant=\"primary\" size=\"40\" className=\"flex-1\" onClick={onApply}>\n {applyLabel}\n </Button>\n </div>\n )}\n </div>\n );\n },\n);\n\nDatePicker.displayName = \"DatePicker\";\n"],"names":["jsx","cn","useRef","useEffect","forwardRef","jsxs","DayPicker","ChevronLeftIcon","ChevronRightIcon","Button"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DatePicker.cjs","sources":["../../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["import { forwardRef, useEffect, useRef } from \"react\";\nimport {\n type ChevronProps,\n type DateRange,\n type DayButtonProps,\n DayPicker,\n type DayPickerProps,\n type DayProps,\n type Modifiers,\n type MonthGridProps,\n type WeekdayProps,\n type WeekdaysProps,\n type WeekProps,\n type WeeksProps,\n} from \"react-day-picker\";\nimport { cn } from \"../../utils/cn\";\nimport type { OmitDistributed } from \"../../utils/types\";\nimport { Button } from \"../Button/Button\";\nimport { ChevronLeftIcon } from \"../Icons/ChevronLeftIcon\";\nimport { ChevronRightIcon } from \"../Icons/ChevronRightIcon\";\n\nexport type { DateRange }; // Needed by consumers when passing props\n\n/** Layout variant — single or side-by-side month display. */\nexport type DatePickerVariant = \"single\" | \"double\";\n\n/** Props specific to the DatePicker wrapper (not inherited from react-day-picker). */\nexport interface DatePickerOwnProps {\n /** Display one month or two side-by-side. @default \"single\" */\n variant?: DatePickerVariant;\n /** Callback fired when the Apply button is clicked. */\n onApply?: () => void;\n /** Callback fired when the Cancel button is clicked. */\n onCancel?: () => void;\n /** Label for the cancel button. @default \"Cancel\" */\n cancelLabel?: string;\n /** Label for the apply button. @default \"Apply\" */\n applyLabel?: string;\n /** Whether to render the cancel / apply footer buttons. @default true */\n showFooter?: boolean;\n /** Additional CSS class name for the outer container. */\n className?: string;\n}\n\nfunction Day({ day, modifiers, className, ...divProps }: DayProps) {\n const { range_start, range_end } = modifiers;\n const isSingleDayRange = range_start && range_end;\n\n return (\n <div\n className={cn(\n className,\n (range_start || range_end) && !isSingleDayRange && \"from-50% from-transparent to-50%\",\n range_start && !isSingleDayRange && \"bg-linear-to-r to-brand-green-50\",\n range_end && !isSingleDayRange && \"bg-linear-to-l to-brand-green-50\",\n )}\n {...divProps}\n />\n );\n}\n\nfunction DayButton({ day, modifiers, className, ...buttonProps }: DayButtonProps) {\n const ref = useRef<HTMLButtonElement>(null);\n\n useEffect(() => {\n if (modifiers.focused) ref.current?.focus();\n }, [modifiers.focused]);\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(\n \"relative z-10 inline-flex size-10 cursor-pointer items-center justify-center rounded-lg\",\n \"typography-body-2-regular\",\n \"transition-colors hover:bg-brand-green-50 not-disabled:active:bg-brand-green-50\",\n \"focus-visible:outline-2 focus-visible:outline-brand-purple-500 focus-visible:outline-offset-[-2px]\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n modifiers.today && !modifiers.selected && \"border border-brand-green-500\",\n modifiers.selected && !modifiers.range_middle\n ? \"bg-brand-green-500 text-body-black-solid-constant hover:bg-brand-green-500\"\n : \"text-body-100\",\n modifiers.range_middle && \"rounded-none bg-transparent\",\n modifiers.outside && \"pointer-events-none opacity-50\",\n )}\n {...buttonProps}\n />\n );\n}\n\n/** Combined props — own DatePicker options plus all react-day-picker props (except `numberOfMonths`). */\nexport type DatePickerProps = DatePickerOwnProps &\n OmitDistributed<DayPickerProps, \"numberOfMonths\">;\n\n/**\n * A calendar date picker supporting single-date and date-range selection with\n * optional side-by-side month display and footer action buttons.\n *\n * Built on top of [react-day-picker](https://react-day-picker.js.org/) — all\n * `DayPickerProps` (except `numberOfMonths`) are forwarded.\n *\n * @example\n * ```tsx\n * <DatePicker\n * mode=\"range\"\n * type=\"double\"\n * selected={range}\n * onSelect={setRange}\n * onApply={save}\n * onCancel={close}\n * />\n * ```\n */\nexport const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(\n (\n {\n variant = \"single\",\n onApply,\n onCancel,\n cancelLabel = \"Cancel\",\n applyLabel = \"Apply\",\n showFooter = true,\n className,\n formatters,\n ...dayPickerProps\n },\n ref,\n ) => {\n const numberOfMonths = variant === \"double\" ? 2 : 1;\n const isMulti = numberOfMonths > 1;\n\n // Wrap onSelect for range mode: when clicking inside a complete range,\n // move the nearest boundary instead of always resetting the end date.\n const resolvedDayPickerProps = (() => {\n if (dayPickerProps.mode !== \"range\") return dayPickerProps;\n\n const { selected, onSelect } = dayPickerProps as {\n selected?: DateRange;\n onSelect?: (\n range: DateRange | undefined,\n triggerDate: Date,\n modifiers: Modifiers,\n e: React.MouseEvent | React.KeyboardEvent,\n ) => void;\n };\n\n if (!onSelect || !selected?.from || !selected?.to) return dayPickerProps;\n\n const { from, to } = selected;\n\n return {\n ...dayPickerProps,\n onSelect: (\n range: DateRange | undefined,\n triggerDate: Date,\n modifiers: Modifiers,\n e: React.MouseEvent | React.KeyboardEvent,\n ) => {\n const clickedTime = triggerDate.getTime();\n const fromTime = from.getTime();\n const toTime = to.getTime();\n\n if (clickedTime > fromTime && clickedTime < toTime) {\n if (clickedTime - fromTime <= toTime - clickedTime) {\n onSelect({ from: triggerDate, to }, triggerDate, modifiers, e);\n } else {\n onSelect({ from, to: triggerDate }, triggerDate, modifiers, e);\n }\n return;\n }\n\n onSelect(range, triggerDate, modifiers, e);\n },\n } as typeof dayPickerProps;\n })();\n\n return (\n <div\n ref={ref}\n className={cn(\n \"inline-flex flex-col rounded-2xl border border-neutral-200 bg-background-inverse-solid shadow-[0px_6px_12px_0px_rgba(0,0,0,0.1)] backdrop-blur-sm\",\n className,\n )}\n >\n <DayPicker\n showOutsideDays\n numberOfMonths={numberOfMonths}\n formatters={{\n formatCaption: (date: Date) =>\n date.toLocaleDateString(\"en-US\", { month: \"short\", year: \"numeric\" }),\n ...formatters,\n }}\n classNames={{\n root: \"w-full\",\n months: \"relative flex\",\n month: \"flex flex-1 flex-col\",\n month_caption: cn(\"flex items-center py-4\", isMulti ? \"justify-center px-2\" : \"px-5\"),\n caption_label: \"typography-body-1-semibold text-body-100\",\n nav: cn(\n \"absolute top-4 z-20 flex\",\n isMulti ? \"pointer-events-none inset-x-3 justify-between\" : \"right-3 gap-1\",\n ),\n button_previous:\n \"pointer-events-auto inline-flex size-8 cursor-pointer items-center justify-center rounded-full text-body-100 transition-colors hover:bg-brand-green-50 not-disabled:active:bg-brand-green-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-purple-500 disabled:cursor-not-allowed disabled:opacity-50\", // !TODO https://linear.app/fanvue/issue/ENG-7301/swap-out-typography-tailwind-utility-classes\n button_next:\n \"pointer-events-auto inline-flex size-8 cursor-pointer items-center justify-center rounded-full text-body-100 transition-colors hover:bg-brand-green-50 not-disabled:active:bg-brand-green-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-purple-500 disabled:cursor-not-allowed disabled:opacity-50\", // !TODO https://linear.app/fanvue/issue/ENG-7301/swap-out-typography-tailwind-utility-classes\n month_grid: cn(\"mb-4\", isMulti ? \"mx-2\" : \"mx-4\"),\n weekdays: \"flex\",\n weekday:\n \"flex h-[30px] w-10 flex-1 items-center justify-center typography-body-2-regular text-body-200\",\n week: \"flex overflow-hidden rounded-lg\",\n day: \"relative flex w-10 flex-1 items-center justify-center\",\n range_middle: \"bg-brand-green-50\",\n hidden: \"hidden\",\n }}\n components={{\n /**\n * !NOTE: We're unable to use semantic elements for the grid due to rdp, as such we've disabled the a11y lint rules for these elements in biome.json.\n */\n Chevron: ({ orientation }: ChevronProps) =>\n orientation === \"left\" ? <ChevronLeftIcon /> : <ChevronRightIcon />,\n MonthGrid: (props: MonthGridProps) => <div role=\"grid\" {...props} />,\n Weekdays: (props: WeekdaysProps) => <div role=\"row\" {...props} />,\n Weekday: (props: WeekdayProps) => <div role=\"columnheader\" {...props} />,\n Weeks: (props: WeeksProps) => <div role=\"rowgroup\" {...props} />,\n Week: ({ week, ...props }: WeekProps) => <div role=\"row\" {...props} />,\n Day,\n DayButton,\n }}\n {...resolvedDayPickerProps}\n />\n\n {showFooter && (\n <div className=\"flex gap-4 px-5 pb-4\">\n <Button variant=\"secondary\" size=\"40\" className=\"flex-1\" onClick={onCancel}>\n {cancelLabel}\n </Button>\n <Button variant=\"primary\" size=\"40\" className=\"flex-1\" onClick={onApply}>\n {applyLabel}\n </Button>\n </div>\n )}\n </div>\n );\n },\n);\n\nDatePicker.displayName = \"DatePicker\";\n"],"names":["jsx","cn","useRef","useEffect","forwardRef","jsxs","DayPicker","ChevronLeftIcon","ChevronRightIcon","Button"],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
5
|
+
const React = require("react");
|
|
6
|
+
const cn = require("../../utils/cn.cjs");
|
|
7
|
+
function _interopNamespaceDefault(e) {
|
|
8
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
9
|
+
if (e) {
|
|
10
|
+
for (const k in e) {
|
|
11
|
+
if (k !== "default") {
|
|
12
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: () => e[k]
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
24
|
+
const CheckOutlineIcon = React__namespace.forwardRef(
|
|
25
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
26
|
+
"svg",
|
|
27
|
+
{
|
|
28
|
+
ref,
|
|
29
|
+
viewBox: "0 0 20 20",
|
|
30
|
+
fill: "none",
|
|
31
|
+
"aria-hidden": "true",
|
|
32
|
+
className: cn.cn("size-5", className),
|
|
33
|
+
...props,
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36
|
+
"path",
|
|
37
|
+
{
|
|
38
|
+
d: "M10.315 2C8.67045 2 7.06283 2.48767 5.69544 3.40133C4.32804 4.31499 3.26229 5.61362 2.63295 7.13299C2.0036 8.65235 1.83894 10.3242 2.15977 11.9372C2.48061 13.5501 3.27254 15.0317 4.43541 16.1946C5.59828 17.3575 7.07987 18.1494 8.69283 18.4702C10.3058 18.7911 11.9776 18.6264 13.497 17.9971C15.0164 17.3677 16.315 16.302 17.2287 14.9346C18.1423 13.5672 18.63 11.9595 18.63 10.315C18.63 9.22306 18.4149 8.14181 17.9971 7.13299C17.5792 6.12416 16.9667 5.20753 16.1946 4.43541C15.4225 3.66329 14.5058 3.05081 13.497 2.63294C12.4882 2.21507 11.4069 2 10.315 2ZM10.315 16.967C8.99936 16.967 7.71326 16.5769 6.61935 15.8459C5.52543 15.115 4.67283 14.0761 4.16936 12.8606C3.66588 11.6451 3.53415 10.3076 3.79082 9.01726C4.04749 7.7269 4.68103 6.54162 5.61133 5.61133C6.54163 4.68103 7.7269 4.04748 9.01726 3.79082C10.3076 3.53415 11.6451 3.66588 12.8606 4.16935C14.0761 4.67283 15.115 5.52543 15.8459 6.61935C16.5769 7.71326 16.967 8.99936 16.967 10.315C16.967 12.0792 16.2662 13.7712 15.0187 15.0187C13.7712 16.2662 12.0792 16.967 10.315 16.967Z",
|
|
39
|
+
fill: "currentColor"
|
|
40
|
+
}
|
|
41
|
+
),
|
|
42
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
43
|
+
"path",
|
|
44
|
+
{
|
|
45
|
+
d: "M12.56 7.31325L9.4169 11.4708L8.06156 9.71629C7.92593 9.54207 7.72666 9.42886 7.50757 9.40158C7.28848 9.37429 7.06752 9.43515 6.8933 9.57077C6.71908 9.7064 6.60588 9.90568 6.57859 10.1248C6.5513 10.3439 6.61216 10.5648 6.74779 10.739L8.76833 13.325C8.84664 13.4241 8.94649 13.504 9.06031 13.5588C9.17412 13.6135 9.29892 13.6416 9.42522 13.641C9.5522 13.6407 9.67743 13.6113 9.7913 13.5551C9.90516 13.4988 10.0046 13.4173 10.0821 13.3167L13.8821 8.32768C14.0166 8.15126 14.0755 7.92863 14.0459 7.70875C14.0163 7.48888 13.9005 7.28979 13.7241 7.15527C13.5477 7.02074 13.325 6.96182 13.1051 6.99144C12.8853 7.02107 12.6862 7.13683 12.5517 7.31325H12.56Z",
|
|
46
|
+
fill: "currentColor"
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
);
|
|
53
|
+
CheckOutlineIcon.displayName = "CheckOutlineIcon";
|
|
54
|
+
exports.CheckOutlineIcon = CheckOutlineIcon;
|
|
55
|
+
//# sourceMappingURL=CheckOutlineIcon.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CheckOutlineIcon.cjs","sources":["../../../../src/components/Icons/CheckOutlineIcon.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport type { IconProps } from \"./types\";\n\n/** Small checkmark icon for use in checkbox/toggle components (12x12 viewBox) */\nexport const CheckOutlineIcon = React.forwardRef<SVGSVGElement, IconProps>(\n ({ className, ...props }, ref) => (\n <svg\n ref={ref}\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n aria-hidden=\"true\"\n className={cn(\"size-5\", className)}\n {...props}\n >\n <path\n d=\"M10.315 2C8.67045 2 7.06283 2.48767 5.69544 3.40133C4.32804 4.31499 3.26229 5.61362 2.63295 7.13299C2.0036 8.65235 1.83894 10.3242 2.15977 11.9372C2.48061 13.5501 3.27254 15.0317 4.43541 16.1946C5.59828 17.3575 7.07987 18.1494 8.69283 18.4702C10.3058 18.7911 11.9776 18.6264 13.497 17.9971C15.0164 17.3677 16.315 16.302 17.2287 14.9346C18.1423 13.5672 18.63 11.9595 18.63 10.315C18.63 9.22306 18.4149 8.14181 17.9971 7.13299C17.5792 6.12416 16.9667 5.20753 16.1946 4.43541C15.4225 3.66329 14.5058 3.05081 13.497 2.63294C12.4882 2.21507 11.4069 2 10.315 2ZM10.315 16.967C8.99936 16.967 7.71326 16.5769 6.61935 15.8459C5.52543 15.115 4.67283 14.0761 4.16936 12.8606C3.66588 11.6451 3.53415 10.3076 3.79082 9.01726C4.04749 7.7269 4.68103 6.54162 5.61133 5.61133C6.54163 4.68103 7.7269 4.04748 9.01726 3.79082C10.3076 3.53415 11.6451 3.66588 12.8606 4.16935C14.0761 4.67283 15.115 5.52543 15.8459 6.61935C16.5769 7.71326 16.967 8.99936 16.967 10.315C16.967 12.0792 16.2662 13.7712 15.0187 15.0187C13.7712 16.2662 12.0792 16.967 10.315 16.967Z\"\n fill=\"currentColor\"\n />\n <path\n d=\"M12.56 7.31325L9.4169 11.4708L8.06156 9.71629C7.92593 9.54207 7.72666 9.42886 7.50757 9.40158C7.28848 9.37429 7.06752 9.43515 6.8933 9.57077C6.71908 9.7064 6.60588 9.90568 6.57859 10.1248C6.5513 10.3439 6.61216 10.5648 6.74779 10.739L8.76833 13.325C8.84664 13.4241 8.94649 13.504 9.06031 13.5588C9.17412 13.6135 9.29892 13.6416 9.42522 13.641C9.5522 13.6407 9.67743 13.6113 9.7913 13.5551C9.90516 13.4988 10.0046 13.4173 10.0821 13.3167L13.8821 8.32768C14.0166 8.15126 14.0755 7.92863 14.0459 7.70875C14.0163 7.48888 13.9005 7.28979 13.7241 7.15527C13.5477 7.02074 13.325 6.96182 13.1051 6.99144C12.8853 7.02107 12.6862 7.13683 12.5517 7.31325H12.56Z\"\n fill=\"currentColor\"\n />\n </svg>\n ),\n);\n\nCheckOutlineIcon.displayName = \"CheckOutlineIcon\";\n"],"names":["React","jsxs","cn","jsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAKO,MAAM,mBAAmBA,iBAAM;AAAA,EACpC,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QACxBC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,eAAY;AAAA,MACZ,WAAWC,GAAAA,GAAG,UAAU,SAAS;AAAA,MAChC,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAAC,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,GAAE;AAAA,YACF,MAAK;AAAA,UAAA;AAAA,QAAA;AAAA,QAEPA,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,GAAE;AAAA,YACF,MAAK;AAAA,UAAA;AAAA,QAAA;AAAA,MACP;AAAA,IAAA;AAAA,EAAA;AAGN;AAEA,iBAAiB,cAAc;;"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
5
|
+
const React = require("react");
|
|
6
|
+
const cn = require("../../utils/cn.cjs");
|
|
7
|
+
function _interopNamespaceDefault(e) {
|
|
8
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
9
|
+
if (e) {
|
|
10
|
+
for (const k in e) {
|
|
11
|
+
if (k !== "default") {
|
|
12
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: () => e[k]
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
24
|
+
const EyeClosedIcon = React__namespace.forwardRef(
|
|
25
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
26
|
+
"svg",
|
|
27
|
+
{
|
|
28
|
+
ref,
|
|
29
|
+
viewBox: "0 0 20 20",
|
|
30
|
+
fill: "none",
|
|
31
|
+
"aria-hidden": "true",
|
|
32
|
+
className: cn.cn("size-5", className),
|
|
33
|
+
...props,
|
|
34
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
35
|
+
"path",
|
|
36
|
+
{
|
|
37
|
+
d: "M8.233 8.233A2.5 2.5 0 1 0 11.767 11.767M8.233 8.233 2.5 2.5M8.233 8.233 11.767 11.767M11.767 11.767 17.5 17.5M14.95 14.95c1.55-1.217 2.55-2.95 2.55-4.95 0-1.667-3.333-5.833-7.5-5.833-1.333 0-2.617.5-3.717 1.217M5.05 5.05C3.5 6.267 2.5 8 2.5 10c0 1.667 3.333 5.833 7.5 5.833 1.333 0 2.617-.5 3.717-1.217",
|
|
38
|
+
stroke: "currentColor",
|
|
39
|
+
strokeWidth: "1.5",
|
|
40
|
+
strokeLinecap: "round",
|
|
41
|
+
strokeLinejoin: "round"
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
);
|
|
47
|
+
EyeClosedIcon.displayName = "EyeClosedIcon";
|
|
48
|
+
exports.EyeClosedIcon = EyeClosedIcon;
|
|
49
|
+
//# sourceMappingURL=EyeClosedIcon.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EyeClosedIcon.cjs","sources":["../../../../src/components/Icons/EyeClosedIcon.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport type { IconProps } from \"./types\";\n\n/** A closed eye icon for visibility/show actions (20 × 20). */\nexport const EyeClosedIcon = React.forwardRef<SVGSVGElement, IconProps>(\n ({ className, ...props }, ref) => (\n <svg\n ref={ref}\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n aria-hidden=\"true\"\n className={cn(\"size-5\", className)}\n {...props}\n >\n <path\n d=\"M8.233 8.233A2.5 2.5 0 1 0 11.767 11.767M8.233 8.233 2.5 2.5M8.233 8.233 11.767 11.767M11.767 11.767 17.5 17.5M14.95 14.95c1.55-1.217 2.55-2.95 2.55-4.95 0-1.667-3.333-5.833-7.5-5.833-1.333 0-2.617.5-3.717 1.217M5.05 5.05C3.5 6.267 2.5 8 2.5 10c0 1.667 3.333 5.833 7.5 5.833 1.333 0 2.617-.5 3.717-1.217\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n ),\n);\n\nEyeClosedIcon.displayName = \"EyeClosedIcon\";\n"],"names":["React","jsx","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAKO,MAAM,gBAAgBA,iBAAM;AAAA,EACjC,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QACxBC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,eAAY;AAAA,MACZ,WAAWC,GAAAA,GAAG,UAAU,SAAS;AAAA,MAChC,GAAG;AAAA,MAEJ,UAAAD,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,GAAE;AAAA,UACF,QAAO;AAAA,UACP,aAAY;AAAA,UACZ,eAAc;AAAA,UACd,gBAAe;AAAA,QAAA;AAAA,MAAA;AAAA,IACjB;AAAA,EAAA;AAGN;AAEA,cAAc,cAAc;;"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
5
|
+
const React = require("react");
|
|
6
|
+
const cn = require("../../utils/cn.cjs");
|
|
7
|
+
function _interopNamespaceDefault(e) {
|
|
8
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
9
|
+
if (e) {
|
|
10
|
+
for (const k in e) {
|
|
11
|
+
if (k !== "default") {
|
|
12
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: () => e[k]
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
24
|
+
const UploadCloudIcon = React__namespace.forwardRef(
|
|
25
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
26
|
+
"svg",
|
|
27
|
+
{
|
|
28
|
+
ref,
|
|
29
|
+
viewBox: "0 0 20 20",
|
|
30
|
+
fill: "none",
|
|
31
|
+
"aria-hidden": "true",
|
|
32
|
+
className: cn.cn("size-5", className),
|
|
33
|
+
...props,
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36
|
+
"path",
|
|
37
|
+
{
|
|
38
|
+
d: "M6.667 13.333 10 10m0 0 3.333 3.333M10 10v7.5",
|
|
39
|
+
stroke: "currentColor",
|
|
40
|
+
strokeWidth: 1.5,
|
|
41
|
+
strokeLinecap: "round",
|
|
42
|
+
strokeLinejoin: "round"
|
|
43
|
+
}
|
|
44
|
+
),
|
|
45
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
46
|
+
"path",
|
|
47
|
+
{
|
|
48
|
+
d: "M16.992 14.825a4.167 4.167 0 0 0-2.159-7.158 5.835 5.835 0 0 0-10.75 2.916A3.333 3.333 0 0 0 5 17.5h11.167a4.167 4.167 0 0 0 .825-2.675Z",
|
|
49
|
+
stroke: "currentColor",
|
|
50
|
+
strokeWidth: 1.5,
|
|
51
|
+
strokeLinecap: "round",
|
|
52
|
+
strokeLinejoin: "round"
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
UploadCloudIcon.displayName = "UploadCloudIcon";
|
|
60
|
+
exports.UploadCloudIcon = UploadCloudIcon;
|
|
61
|
+
//# sourceMappingURL=UploadCloudIcon.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UploadCloudIcon.cjs","sources":["../../../../src/components/Icons/UploadCloudIcon.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport type { IconProps } from \"./types\";\n\n/** An upload-to-cloud icon with an upward arrow (20 × 20). */\nexport const UploadCloudIcon = React.forwardRef<SVGSVGElement, IconProps>(\n ({ className, ...props }, ref) => (\n <svg\n ref={ref}\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n aria-hidden=\"true\"\n className={cn(\"size-5\", className)}\n {...props}\n >\n <path\n d=\"M6.667 13.333 10 10m0 0 3.333 3.333M10 10v7.5\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M16.992 14.825a4.167 4.167 0 0 0-2.159-7.158 5.835 5.835 0 0 0-10.75 2.916A3.333 3.333 0 0 0 5 17.5h11.167a4.167 4.167 0 0 0 .825-2.675Z\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n ),\n);\n\nUploadCloudIcon.displayName = \"UploadCloudIcon\";\n"],"names":["React","jsxs","cn","jsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAKO,MAAM,kBAAkBA,iBAAM;AAAA,EACnC,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QACxBC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,eAAY;AAAA,MACZ,WAAWC,GAAAA,GAAG,UAAU,SAAS;AAAA,MAChC,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAAC,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,GAAE;AAAA,YACF,QAAO;AAAA,YACP,aAAa;AAAA,YACb,eAAc;AAAA,YACd,gBAAe;AAAA,UAAA;AAAA,QAAA;AAAA,QAEjBA,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,GAAE;AAAA,YACF,QAAO;AAAA,YACP,aAAa;AAAA,YACb,eAAc;AAAA,YACd,gBAAe;AAAA,UAAA;AAAA,QAAA;AAAA,MACjB;AAAA,IAAA;AAAA,EAAA;AAGN;AAEA,gBAAgB,cAAc;;"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
5
|
+
const React = require("react");
|
|
6
|
+
const EyeClosedIcon = require("../Icons/EyeClosedIcon.cjs");
|
|
7
|
+
const EyeIcon = require("../Icons/EyeIcon.cjs");
|
|
8
|
+
const TextField = require("../TextField/TextField.cjs");
|
|
9
|
+
function _interopNamespaceDefault(e) {
|
|
10
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
11
|
+
if (e) {
|
|
12
|
+
for (const k in e) {
|
|
13
|
+
if (k !== "default") {
|
|
14
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
15
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: () => e[k]
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
n.default = e;
|
|
23
|
+
return Object.freeze(n);
|
|
24
|
+
}
|
|
25
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
26
|
+
const PasswordField = React__namespace.forwardRef(
|
|
27
|
+
({ disabled, ...props }, ref) => {
|
|
28
|
+
const [showPassword, setShowPassword] = React__namespace.useState(false);
|
|
29
|
+
const togglePasswordVisibility = () => {
|
|
30
|
+
setShowPassword((prev) => !prev);
|
|
31
|
+
};
|
|
32
|
+
const rightIcon = /* @__PURE__ */ jsxRuntime.jsx(
|
|
33
|
+
"button",
|
|
34
|
+
{
|
|
35
|
+
type: "button",
|
|
36
|
+
onClick: togglePasswordVisibility,
|
|
37
|
+
disabled,
|
|
38
|
+
"aria-label": showPassword ? "Hide password" : "Show password",
|
|
39
|
+
tabIndex: -1,
|
|
40
|
+
className: "flex size-5 shrink-0 items-center justify-center text-body-200 transition-colors hover:text-body-100 focus:outline-none disabled:cursor-not-allowed",
|
|
41
|
+
children: showPassword ? /* @__PURE__ */ jsxRuntime.jsx(EyeClosedIcon.EyeClosedIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(EyeIcon.EyeIcon, {})
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
45
|
+
TextField.TextField,
|
|
46
|
+
{
|
|
47
|
+
ref,
|
|
48
|
+
type: showPassword ? "text" : "password",
|
|
49
|
+
disabled,
|
|
50
|
+
rightIcon,
|
|
51
|
+
"aria-label": !props.label ? "Password field" : void 0,
|
|
52
|
+
...props
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
PasswordField.displayName = "PasswordField";
|
|
58
|
+
exports.PasswordField = PasswordField;
|
|
59
|
+
//# sourceMappingURL=PasswordField.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PasswordField.cjs","sources":["../../../../src/components/PasswordField/PasswordField.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { EyeClosedIcon } from \"../Icons/EyeClosedIcon\";\nimport { EyeIcon } from \"../Icons/EyeIcon\";\nimport { TextField, type TextFieldProps } from \"../TextField/TextField\";\n\nexport type PasswordFieldSize = \"48\" | \"40\" | \"32\";\n\nexport interface PasswordFieldProps extends Omit<TextFieldProps, \"type\" | \"rightIcon\"> {\n /** Size variant of the password field */\n size?: PasswordFieldSize;\n}\n\nexport const PasswordField = React.forwardRef<HTMLInputElement, PasswordFieldProps>(\n ({ disabled, ...props }, ref) => {\n const [showPassword, setShowPassword] = React.useState(false);\n\n const togglePasswordVisibility = () => {\n setShowPassword((prev) => !prev);\n };\n\n const rightIcon = (\n <button\n type=\"button\"\n onClick={togglePasswordVisibility}\n disabled={disabled}\n aria-label={showPassword ? \"Hide password\" : \"Show password\"}\n tabIndex={-1}\n className=\"flex size-5 shrink-0 items-center justify-center text-body-200 transition-colors hover:text-body-100 focus:outline-none disabled:cursor-not-allowed\"\n >\n {showPassword ? <EyeClosedIcon /> : <EyeIcon />}\n </button>\n );\n\n return (\n <TextField\n ref={ref}\n type={showPassword ? \"text\" : \"password\"}\n disabled={disabled}\n rightIcon={rightIcon}\n aria-label={!props.label ? \"Password field\" : undefined}\n {...props}\n />\n );\n },\n);\n\nPasswordField.displayName = \"PasswordField\";\n"],"names":["React","jsx","EyeClosedIcon","EyeIcon","TextField"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAYO,MAAM,gBAAgBA,iBAAM;AAAA,EACjC,CAAC,EAAE,UAAU,GAAG,MAAA,GAAS,QAAQ;AAC/B,UAAM,CAAC,cAAc,eAAe,IAAIA,iBAAM,SAAS,KAAK;AAE5D,UAAM,2BAA2B,MAAM;AACrC,sBAAgB,CAAC,SAAS,CAAC,IAAI;AAAA,IACjC;AAEA,UAAM,YACJC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT;AAAA,QACA,cAAY,eAAe,kBAAkB;AAAA,QAC7C,UAAU;AAAA,QACV,WAAU;AAAA,QAET,UAAA,eAAeA,+BAACC,cAAAA,eAAA,CAAA,CAAc,mCAAMC,QAAAA,SAAA,CAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAIjD,WACEF,2BAAAA;AAAAA,MAACG,UAAAA;AAAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAM,eAAe,SAAS;AAAA,QAC9B;AAAA,QACA;AAAA,QACA,cAAY,CAAC,MAAM,QAAQ,mBAAmB;AAAA,QAC7C,GAAG;AAAA,MAAA;AAAA,IAAA;AAAA,EAGV;AACF;AAEA,cAAc,cAAc;;"}
|