@reopt-ai/opt-ui 1.5.0 → 1.7.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/COMPONENT_CATALOG.md +178 -50
- package/README.md +28 -7
- package/dist/app.css +170 -0
- package/dist/core/index.cjs +164 -324
- package/dist/core/index.d.cts +3 -6
- package/dist/core/index.d.ts +3 -6
- package/dist/core/index.js +3 -323
- package/dist/docs/01-getting-started.md +25 -5
- package/dist/docs/02-components/01-core.md +36 -28
- package/dist/docs/02-components/02-visuals.md +1 -1
- package/dist/docs/02-components/03-shells.md +144 -23
- package/dist/docs/02-components/04-surfaces.md +1 -1
- package/dist/docs/02-components/index.md +4 -4
- package/dist/docs/03-recipes/03-layouts.md +16 -12
- package/dist/docs/05-migration/01-breaking-changes.md +46 -1
- package/dist/field-sidebar-BTZCBviA.d.cts +1031 -0
- package/dist/field-sidebar-BTZCBviA.d.cts.map +1 -0
- package/dist/field-sidebar-CB8vjvUM.d.ts +1031 -0
- package/dist/field-sidebar-CB8vjvUM.d.ts.map +1 -0
- package/dist/field-sidebar-Cs-J-5VE.js +4233 -0
- package/dist/field-sidebar-Cs-J-5VE.js.map +1 -0
- package/dist/field-sidebar-CzIQX70-.cjs +4561 -0
- package/dist/id-registry.cjs +1939 -1872
- package/dist/id-registry.d.cts +14 -11
- package/dist/id-registry.d.cts.map +1 -0
- package/dist/id-registry.d.ts +14 -11
- package/dist/id-registry.d.ts.map +1 -0
- package/dist/id-registry.js +1937 -1871
- package/dist/id-registry.js.map +1 -0
- package/dist/id-registry.json +113 -28
- package/dist/index.cjs +8380 -9790
- package/dist/index.d.cts +1555 -1442
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +1555 -1442
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7996 -9778
- package/dist/index.js.map +1 -0
- package/dist/meta.cjs +7753 -6875
- package/dist/meta.d.cts.map +1 -0
- package/dist/meta.d.ts.map +1 -0
- package/dist/meta.js +7752 -6846
- package/dist/meta.js.map +1 -0
- package/dist/pagination-DWbvACuN.js +3850 -0
- package/dist/pagination-DWbvACuN.js.map +1 -0
- package/dist/pagination-DjfxBcjX.cjs +4739 -0
- package/dist/query.cjs +386 -0
- package/dist/query.d.cts +141 -0
- package/dist/query.d.cts.map +1 -0
- package/dist/query.d.ts +141 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +371 -0
- package/dist/query.js.map +1 -0
- package/dist/shader-surface-BUPZ0P-n.js +3654 -0
- package/dist/shader-surface-BUPZ0P-n.js.map +1 -0
- package/dist/shader-surface-BfTP9c1Q.d.ts +1788 -0
- package/dist/shader-surface-BfTP9c1Q.d.ts.map +1 -0
- package/dist/shader-surface-DRrcLrWf.d.cts +1788 -0
- package/dist/shader-surface-DRrcLrWf.d.cts.map +1 -0
- package/dist/shader-surface-Fav5K9UC.cjs +4041 -0
- package/dist/shells/index.cjs +58 -65
- package/dist/shells/index.d.cts +4 -5
- package/dist/shells/index.d.ts +4 -5
- package/dist/shells/index.js +4 -64
- package/dist/tailwind.css +49 -0
- package/dist/theme/presets/default.css +2 -2
- package/dist/theme/presets/minimal.css +2 -2
- package/dist/theme/presets/mono-dark.css +2 -2
- package/dist/theme/presets/natural.css +1 -1
- package/dist/theme/presets/pro.css +2 -2
- package/dist/theme/server.cjs +257 -228
- package/dist/theme/server.d.cts +48 -60
- package/dist/theme/server.d.cts.map +1 -0
- package/dist/theme/server.d.ts +48 -60
- package/dist/theme/server.d.ts.map +1 -0
- package/dist/theme/server.js +234 -189
- package/dist/theme/server.js.map +1 -0
- package/dist/types-D0FlcYnM.d.cts +301 -0
- package/dist/types-D0FlcYnM.d.cts.map +1 -0
- package/dist/types-D0FlcYnM.d.ts +301 -0
- package/dist/types-D0FlcYnM.d.ts.map +1 -0
- package/dist/visuals/index.cjs +10 -4
- package/dist/visuals/index.d.cts +2 -1
- package/dist/visuals/index.d.ts +2 -1
- package/dist/visuals/index.js +1 -2
- package/package.json +27 -11
- package/dist/chunk-3GWWZKX7.js +0 -38
- package/dist/chunk-3QFYBBL6.cjs +0 -4759
- package/dist/chunk-3XD4HIFL.js +0 -3241
- package/dist/chunk-AFF2HPE5.cjs +0 -5008
- package/dist/chunk-NXZJIHEZ.cjs +0 -3241
- package/dist/chunk-ONE3C5RV.cjs +0 -38
- package/dist/chunk-RBM2RNC2.js +0 -5008
- package/dist/chunk-VBGPEW45.js +0 -4759
- package/dist/index-BZ_lBlO1.d.ts +0 -474
- package/dist/index-BuvxoWHf.d.cts +0 -474
- package/dist/index-DWyqDIkH.d.cts +0 -1689
- package/dist/index-Uyijm14M.d.ts +0 -1689
- package/dist/types-D4-0lwaE.d.cts +0 -298
- package/dist/types-D4-0lwaE.d.ts +0 -298
|
@@ -0,0 +1,3654 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { $t as koBreadcrumbLabels, Bn as _objectSpread2, Cn as OPT_FOCUS, En as OPT_INPUT, In as OPT_TEXT_PRIMARY, Ln as OPT_TEXT_SECONDARY, Pn as OPT_TEXTAREA_SIZE, Qt as Button, Rn as OPT_TEXT_TERTIARY, Un as cn, Vn as _objectWithoutProperties, Yt as Spinner, an as RequiredMark, at as MenuPopover$1, cn as OPT_ANIMATE_DRAWER, ct as MenuTrigger$1, hn as OPT_BORDER, in as FieldMessages, kn as OPT_SURFACE, ln as OPT_ANIMATE_ENTER_EXIT, nt as MenuItem$1, on as getDescribedBy, ot as MenuRoot$1, qt as useInputId, rn as useControlledState, st as MenuSeparator$1, wn as OPT_FOCUS_VISIBLE } from "./pagination-DWbvACuN.js";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { createContext, createElement, forwardRef, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
6
|
+
import { AccordionContent, AccordionItem, AccordionRoot, AccordionTrigger, DialogDismiss, DialogPanel, DialogRoot, Meter, RadioGroupRoot, Separator, Toggle, ToggleGroup, useDialogClose, useRadioGroupContext } from "@reopt-ai/opt-ui-primitives";
|
|
7
|
+
import { ChevronDown, GripVertical } from "lucide-react";
|
|
8
|
+
import { Group, Panel, Separator as Separator$1 } from "react-resizable-panels";
|
|
9
|
+
import { Mesh, Program, Renderer, Triangle } from "ogl";
|
|
10
|
+
import { hexToSrgb } from "@reopt-ai/opt-palette";
|
|
11
|
+
//#region src/core/text.tsx
|
|
12
|
+
const _excluded$17 = [
|
|
13
|
+
"as",
|
|
14
|
+
"variant",
|
|
15
|
+
"tone",
|
|
16
|
+
"truncate",
|
|
17
|
+
"className",
|
|
18
|
+
"children"
|
|
19
|
+
];
|
|
20
|
+
const toneStyles = {
|
|
21
|
+
primary: "text-text-primary",
|
|
22
|
+
secondary: "text-text-secondary",
|
|
23
|
+
tertiary: "text-text-tertiary",
|
|
24
|
+
inherit: "text-inherit"
|
|
25
|
+
};
|
|
26
|
+
/** Semantic body text whose visual role is controlled by foundation tokens. */
|
|
27
|
+
function Text(_ref) {
|
|
28
|
+
let { as, variant = "body", tone = "primary", truncate = false, className, children } = _ref, props = _objectWithoutProperties(_ref, _excluded$17);
|
|
29
|
+
const Component = as !== null && as !== void 0 ? as : "span";
|
|
30
|
+
const elementProps = _objectSpread2(_objectSpread2({}, props), {}, {
|
|
31
|
+
className: cn(`opt-type-${variant}`, toneStyles[tone], truncate && "truncate", className),
|
|
32
|
+
"data-opt-component": "text",
|
|
33
|
+
"data-opt-part": "root",
|
|
34
|
+
"data-opt-id": "NFKP2",
|
|
35
|
+
"data-opt-slug": "text.Text",
|
|
36
|
+
"data-role": variant,
|
|
37
|
+
"data-tone": tone
|
|
38
|
+
});
|
|
39
|
+
return createElement(Component, elementProps, children);
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/core/heading.tsx
|
|
43
|
+
const _excluded$16 = [
|
|
44
|
+
"level",
|
|
45
|
+
"variant",
|
|
46
|
+
"className",
|
|
47
|
+
"children"
|
|
48
|
+
];
|
|
49
|
+
/** Semantic heading with independently selectable visual hierarchy. */
|
|
50
|
+
function Heading(_ref) {
|
|
51
|
+
let { level = 2, variant = `heading-${level}`, className, children } = _ref, props = _objectWithoutProperties(_ref, _excluded$16);
|
|
52
|
+
const Component = {
|
|
53
|
+
1: "h1",
|
|
54
|
+
2: "h2",
|
|
55
|
+
3: "h3",
|
|
56
|
+
4: "h4",
|
|
57
|
+
5: "h5",
|
|
58
|
+
6: "h6"
|
|
59
|
+
}[level];
|
|
60
|
+
return createElement(Component, _objectSpread2(_objectSpread2({ className: cn(`opt-type-${variant}`, "text-text-primary", className) }, props), {}, {
|
|
61
|
+
"data-opt-component": "heading",
|
|
62
|
+
"data-opt-part": "root",
|
|
63
|
+
"data-opt-id": "NF0BT",
|
|
64
|
+
"data-opt-slug": "heading.Heading",
|
|
65
|
+
"data-level": level,
|
|
66
|
+
"data-role": variant
|
|
67
|
+
}), children);
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region src/core/separator.tsx
|
|
71
|
+
/**
|
|
72
|
+
* Visual divider between content. Wraps the headless primitive with design
|
|
73
|
+
* tokens. With `children`, renders a centered label flanked by rules.
|
|
74
|
+
*/
|
|
75
|
+
function Separator$2({ orientation = "horizontal", decorative = false, children, className }) {
|
|
76
|
+
if (orientation === "horizontal" && children != null) return /* @__PURE__ */ jsxs("div", {
|
|
77
|
+
className: cn("gap-element flex items-center", className),
|
|
78
|
+
"data-opt-id": "7PWJS",
|
|
79
|
+
"data-opt-slug": "separator.Separator",
|
|
80
|
+
children: [
|
|
81
|
+
/* @__PURE__ */ jsx(Separator, {
|
|
82
|
+
decorative: true,
|
|
83
|
+
orientation: "horizontal",
|
|
84
|
+
className: "bg-border h-px flex-1"
|
|
85
|
+
}),
|
|
86
|
+
/* @__PURE__ */ jsx("span", {
|
|
87
|
+
className: "text-text-tertiary shrink-0 text-xs",
|
|
88
|
+
children
|
|
89
|
+
}),
|
|
90
|
+
/* @__PURE__ */ jsx(Separator, {
|
|
91
|
+
decorative: true,
|
|
92
|
+
orientation: "horizontal",
|
|
93
|
+
className: "bg-border h-px flex-1"
|
|
94
|
+
})
|
|
95
|
+
]
|
|
96
|
+
});
|
|
97
|
+
return /* @__PURE__ */ jsx(Separator, {
|
|
98
|
+
orientation,
|
|
99
|
+
decorative,
|
|
100
|
+
className: cn("bg-border shrink-0", orientation === "horizontal" ? "h-px w-full" : "h-full w-px self-stretch", className),
|
|
101
|
+
"data-opt-id": "7PWJS",
|
|
102
|
+
"data-opt-slug": "separator.Separator"
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
Separator$2.displayName = "Separator";
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/core/otp-field.tsx
|
|
108
|
+
const PATTERNS = {
|
|
109
|
+
numeric: /[0-9]/,
|
|
110
|
+
alphanumeric: /[a-zA-Z0-9]/
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* One-time-code / PIN entry split into per-character cells, with paste
|
|
114
|
+
* distribution, auto-advancing focus, and arrow/backspace navigation.
|
|
115
|
+
*/
|
|
116
|
+
function OtpField({ length = 6, value: controlledValue, defaultValue = "", onChange, onComplete, type = "numeric", mask = false, disabled = false, autoFocus = false, name, label, required, error, hint, className, id, "aria-label": ariaLabel }) {
|
|
117
|
+
const reactId = React.useId();
|
|
118
|
+
const fieldId = id !== null && id !== void 0 ? id : `otp-${reactId}`;
|
|
119
|
+
const hasError = Boolean(error);
|
|
120
|
+
const pattern = PATTERNS[type];
|
|
121
|
+
const inputsRef = React.useRef([]);
|
|
122
|
+
const labelId = label ? `${fieldId}-label` : void 0;
|
|
123
|
+
const groupLabelledBy = ariaLabel ? void 0 : labelId;
|
|
124
|
+
const groupAriaLabel = ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : groupLabelledBy ? void 0 : "One-time code";
|
|
125
|
+
const [value, setRawValue] = useControlledState(defaultValue, controlledValue, onChange);
|
|
126
|
+
const completedRef = React.useRef(false);
|
|
127
|
+
const setValue = React.useCallback((next) => {
|
|
128
|
+
const clipped = next.slice(0, length);
|
|
129
|
+
setRawValue(clipped);
|
|
130
|
+
if (clipped.length === length) {
|
|
131
|
+
if (!completedRef.current) {
|
|
132
|
+
completedRef.current = true;
|
|
133
|
+
onComplete === null || onComplete === void 0 || onComplete(clipped);
|
|
134
|
+
}
|
|
135
|
+
} else completedRef.current = false;
|
|
136
|
+
}, [
|
|
137
|
+
length,
|
|
138
|
+
setRawValue,
|
|
139
|
+
onComplete
|
|
140
|
+
]);
|
|
141
|
+
const focusCell = React.useCallback((index) => {
|
|
142
|
+
var _inputsRef$current$ta, _inputsRef$current$ta2;
|
|
143
|
+
const target = Math.max(0, Math.min(index, inputsRef.current.length - 1));
|
|
144
|
+
(_inputsRef$current$ta = inputsRef.current[target]) === null || _inputsRef$current$ta === void 0 || _inputsRef$current$ta.focus();
|
|
145
|
+
(_inputsRef$current$ta2 = inputsRef.current[target]) === null || _inputsRef$current$ta2 === void 0 || _inputsRef$current$ta2.select();
|
|
146
|
+
}, []);
|
|
147
|
+
const cells = React.useMemo(() => Array.from({ length }, (_, i) => {
|
|
148
|
+
var _value$i;
|
|
149
|
+
return (_value$i = value[i]) !== null && _value$i !== void 0 ? _value$i : "";
|
|
150
|
+
}), [length, value]);
|
|
151
|
+
const setCell = React.useCallback((index, char) => {
|
|
152
|
+
const arr = Array.from({ length }, (_, i) => {
|
|
153
|
+
var _value$i2;
|
|
154
|
+
return (_value$i2 = value[i]) !== null && _value$i2 !== void 0 ? _value$i2 : "";
|
|
155
|
+
});
|
|
156
|
+
arr[index] = char;
|
|
157
|
+
setValue(arr.join(""));
|
|
158
|
+
}, [
|
|
159
|
+
length,
|
|
160
|
+
value,
|
|
161
|
+
setValue
|
|
162
|
+
]);
|
|
163
|
+
const handleChange = React.useCallback((index) => (e) => {
|
|
164
|
+
var _chars;
|
|
165
|
+
const raw = e.target.value;
|
|
166
|
+
const chars = raw.split("").filter((c) => pattern.test(c));
|
|
167
|
+
const char = (_chars = chars[chars.length - 1]) !== null && _chars !== void 0 ? _chars : "";
|
|
168
|
+
if (char === "" && raw !== "") return;
|
|
169
|
+
setCell(index, char);
|
|
170
|
+
if (char) focusCell(index + 1);
|
|
171
|
+
}, [
|
|
172
|
+
pattern,
|
|
173
|
+
setCell,
|
|
174
|
+
focusCell
|
|
175
|
+
]);
|
|
176
|
+
const handleKeyDown = React.useCallback((index) => (e) => {
|
|
177
|
+
switch (e.key) {
|
|
178
|
+
case "Backspace":
|
|
179
|
+
e.preventDefault();
|
|
180
|
+
if (cells[index]) setCell(index, "");
|
|
181
|
+
else if (index > 0) {
|
|
182
|
+
setCell(index - 1, "");
|
|
183
|
+
focusCell(index - 1);
|
|
184
|
+
}
|
|
185
|
+
break;
|
|
186
|
+
case "ArrowLeft":
|
|
187
|
+
e.preventDefault();
|
|
188
|
+
focusCell(index - 1);
|
|
189
|
+
break;
|
|
190
|
+
case "ArrowRight":
|
|
191
|
+
e.preventDefault();
|
|
192
|
+
focusCell(index + 1);
|
|
193
|
+
}
|
|
194
|
+
}, [
|
|
195
|
+
cells,
|
|
196
|
+
setCell,
|
|
197
|
+
focusCell
|
|
198
|
+
]);
|
|
199
|
+
const handlePaste = React.useCallback((index) => (e) => {
|
|
200
|
+
e.preventDefault();
|
|
201
|
+
const pasted = e.clipboardData.getData("text").split("").filter((c) => pattern.test(c)).join("");
|
|
202
|
+
if (!pasted) return;
|
|
203
|
+
const arr = Array.from({ length }, (_, i) => {
|
|
204
|
+
var _value$i3;
|
|
205
|
+
return (_value$i3 = value[i]) !== null && _value$i3 !== void 0 ? _value$i3 : "";
|
|
206
|
+
});
|
|
207
|
+
for (let i = 0; i < pasted.length && index + i < length; i++) arr[index + i] = pasted[i];
|
|
208
|
+
setValue(arr.join(""));
|
|
209
|
+
focusCell(index + pasted.length);
|
|
210
|
+
}, [
|
|
211
|
+
pattern,
|
|
212
|
+
length,
|
|
213
|
+
value,
|
|
214
|
+
setValue,
|
|
215
|
+
focusCell
|
|
216
|
+
]);
|
|
217
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
218
|
+
className: cn("flex flex-col gap-1.5", className),
|
|
219
|
+
"data-opt-id": "0083Q",
|
|
220
|
+
"data-opt-slug": "otp-field.OtpField",
|
|
221
|
+
children: [
|
|
222
|
+
label && /* @__PURE__ */ jsxs("span", {
|
|
223
|
+
className: "text-sm font-medium text-text-primary",
|
|
224
|
+
id: labelId,
|
|
225
|
+
children: [label, required && /* @__PURE__ */ jsx(RequiredMark, {})]
|
|
226
|
+
}),
|
|
227
|
+
/* @__PURE__ */ jsx("div", {
|
|
228
|
+
role: "group",
|
|
229
|
+
"aria-label": groupAriaLabel,
|
|
230
|
+
"aria-labelledby": groupLabelledBy,
|
|
231
|
+
"aria-describedby": getDescribedBy(fieldId, error, hint),
|
|
232
|
+
className: "gap-element flex items-center",
|
|
233
|
+
children: cells.map((cell, i) => /* @__PURE__ */ jsx("input", {
|
|
234
|
+
ref: (node) => {
|
|
235
|
+
inputsRef.current[i] = node;
|
|
236
|
+
},
|
|
237
|
+
type: mask ? "password" : "text",
|
|
238
|
+
inputMode: type === "numeric" ? "numeric" : "text",
|
|
239
|
+
autoComplete: i === 0 ? "one-time-code" : "off",
|
|
240
|
+
autoFocus: autoFocus && i === 0,
|
|
241
|
+
maxLength: 1,
|
|
242
|
+
disabled,
|
|
243
|
+
"aria-label": `${i + 1}번째 자리`,
|
|
244
|
+
"aria-invalid": hasError || void 0,
|
|
245
|
+
value: cell,
|
|
246
|
+
onChange: handleChange(i),
|
|
247
|
+
onKeyDown: handleKeyDown(i),
|
|
248
|
+
onPaste: handlePaste(i),
|
|
249
|
+
onFocus: (e) => e.target.select(),
|
|
250
|
+
className: cn(OPT_INPUT, "h-12 w-10 text-center text-lg font-medium", hasError && "border-danger focus-visible:outline-danger")
|
|
251
|
+
}, i))
|
|
252
|
+
}),
|
|
253
|
+
name && /* @__PURE__ */ jsx("input", {
|
|
254
|
+
type: "hidden",
|
|
255
|
+
name,
|
|
256
|
+
value,
|
|
257
|
+
disabled
|
|
258
|
+
}),
|
|
259
|
+
/* @__PURE__ */ jsx(FieldMessages, {
|
|
260
|
+
id: fieldId,
|
|
261
|
+
error,
|
|
262
|
+
hint
|
|
263
|
+
})
|
|
264
|
+
]
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
OtpField.displayName = "OtpField";
|
|
268
|
+
//#endregion
|
|
269
|
+
//#region src/core/toggle.tsx
|
|
270
|
+
const _excluded$15 = [
|
|
271
|
+
"size",
|
|
272
|
+
"className",
|
|
273
|
+
"children"
|
|
274
|
+
];
|
|
275
|
+
const _excluded2$8 = [
|
|
276
|
+
"className",
|
|
277
|
+
"children",
|
|
278
|
+
"orientation"
|
|
279
|
+
];
|
|
280
|
+
const sizeStyles$5 = {
|
|
281
|
+
sm: "h-8 min-w-8 px-2 text-xs gap-1.5",
|
|
282
|
+
md: "h-10 min-w-10 px-3 text-sm gap-2"
|
|
283
|
+
};
|
|
284
|
+
const toggleBase = cn("inline-flex cursor-pointer items-center justify-center rounded-md border border-border bg-surface font-medium text-text-secondary", "hover:bg-bg-subtle hover:text-text-primary", "data-[pressed]:border-accent data-[pressed]:bg-accent-subtle data-[pressed]:text-accent", "disabled:pointer-events-none disabled:opacity-40", OPT_FOCUS_VISIBLE);
|
|
285
|
+
/** A two-state (pressed) button. Use standalone or inside `ToggleGroup`. */
|
|
286
|
+
function Toggle$1(_ref) {
|
|
287
|
+
let { size = "md", className, children } = _ref, props = _objectWithoutProperties(_ref, _excluded$15);
|
|
288
|
+
return /* @__PURE__ */ jsx(Toggle, _objectSpread2(_objectSpread2({ className: cn(toggleBase, sizeStyles$5[size], className) }, props), {}, {
|
|
289
|
+
"data-opt-id": "GFCMY",
|
|
290
|
+
"data-opt-slug": "toggle.Toggle",
|
|
291
|
+
children
|
|
292
|
+
}));
|
|
293
|
+
}
|
|
294
|
+
Toggle$1.displayName = "Toggle";
|
|
295
|
+
/** A set of related toggles with shared selection and keyboard navigation. */
|
|
296
|
+
function ToggleGroup$1(_ref2) {
|
|
297
|
+
let { className, children, orientation = "horizontal" } = _ref2, props = _objectWithoutProperties(_ref2, _excluded2$8);
|
|
298
|
+
return /* @__PURE__ */ jsx(ToggleGroup, _objectSpread2(_objectSpread2({
|
|
299
|
+
orientation,
|
|
300
|
+
"aria-orientation": orientation,
|
|
301
|
+
className: cn("inline-flex gap-1", orientation === "vertical" ? "flex-col" : "flex-row", className)
|
|
302
|
+
}, props), {}, {
|
|
303
|
+
"data-opt-id": "47J37",
|
|
304
|
+
"data-opt-slug": "toggle.ToggleGroup",
|
|
305
|
+
children
|
|
306
|
+
}));
|
|
307
|
+
}
|
|
308
|
+
ToggleGroup$1.displayName = "ToggleGroup";
|
|
309
|
+
//#endregion
|
|
310
|
+
//#region src/core/accordion.tsx
|
|
311
|
+
const _excluded$14 = ["className"];
|
|
312
|
+
const _excluded2$7 = ["className"];
|
|
313
|
+
const _excluded3$4 = ["className", "children"];
|
|
314
|
+
const _excluded4$3 = ["className", "children"];
|
|
315
|
+
const triggerClass = `group/acc-trigger flex w-full cursor-pointer items-center justify-between gap-2 py-4 text-left text-sm font-medium text-text-primary transition-colors hover:text-text-secondary ${OPT_FOCUS} disabled:cursor-not-allowed disabled:opacity-50`;
|
|
316
|
+
const contentClass = `overflow-hidden text-sm text-text-secondary ${OPT_ANIMATE_ENTER_EXIT}`;
|
|
317
|
+
/** A vertically stacked set of expandable panels (single or multiple open). */
|
|
318
|
+
function AccordionRoot$1(_ref) {
|
|
319
|
+
let { className } = _ref, props = _objectWithoutProperties(_ref, _excluded$14);
|
|
320
|
+
return /* @__PURE__ */ jsx("div", {
|
|
321
|
+
className: cn("border-border border-y", className),
|
|
322
|
+
"data-opt-id": "CSBA9",
|
|
323
|
+
"data-opt-slug": "accordion.AccordionRoot",
|
|
324
|
+
children: /* @__PURE__ */ jsx(AccordionRoot, _objectSpread2({}, props))
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
AccordionRoot$1.displayName = "AccordionRoot";
|
|
328
|
+
/** A single accordion entry, identified by its `value`. */
|
|
329
|
+
function AccordionItem$1(_ref2) {
|
|
330
|
+
let { className } = _ref2, props = _objectWithoutProperties(_ref2, _excluded2$7);
|
|
331
|
+
return /* @__PURE__ */ jsx(AccordionItem, _objectSpread2(_objectSpread2({ className: cn("border-border border-b last:border-b-0", className) }, props), {}, {
|
|
332
|
+
"data-opt-id": "JTY9M",
|
|
333
|
+
"data-opt-slug": "accordion.AccordionItem"
|
|
334
|
+
}));
|
|
335
|
+
}
|
|
336
|
+
AccordionItem$1.displayName = "AccordionItem";
|
|
337
|
+
/** The clickable header that expands/collapses its panel. */
|
|
338
|
+
function AccordionTrigger$1(_ref3) {
|
|
339
|
+
let { className, children } = _ref3, props = _objectWithoutProperties(_ref3, _excluded3$4);
|
|
340
|
+
return /* @__PURE__ */ jsxs(AccordionTrigger, _objectSpread2(_objectSpread2({ className: cn(triggerClass, className) }, props), {}, {
|
|
341
|
+
"data-opt-id": "CWJZZ",
|
|
342
|
+
"data-opt-slug": "accordion.AccordionTrigger",
|
|
343
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
344
|
+
className: "flex-1",
|
|
345
|
+
children
|
|
346
|
+
}), /* @__PURE__ */ jsx(ChevronDown, {
|
|
347
|
+
"aria-hidden": "true",
|
|
348
|
+
className: "text-text-tertiary h-4 w-4 shrink-0 transition-transform duration-200 group-data-[state=open]/acc-trigger:rotate-180"
|
|
349
|
+
})]
|
|
350
|
+
}));
|
|
351
|
+
}
|
|
352
|
+
AccordionTrigger$1.displayName = "AccordionTrigger";
|
|
353
|
+
/** The collapsible panel body associated with a trigger. */
|
|
354
|
+
function AccordionContent$1(_ref4) {
|
|
355
|
+
let { className, children } = _ref4, props = _objectWithoutProperties(_ref4, _excluded4$3);
|
|
356
|
+
return /* @__PURE__ */ jsx(AccordionContent, _objectSpread2(_objectSpread2({ className: cn(contentClass, className) }, props), {}, {
|
|
357
|
+
"data-opt-id": "J98AG",
|
|
358
|
+
"data-opt-slug": "accordion.AccordionContent",
|
|
359
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
360
|
+
className: "pb-4",
|
|
361
|
+
children
|
|
362
|
+
})
|
|
363
|
+
}));
|
|
364
|
+
}
|
|
365
|
+
AccordionContent$1.displayName = "AccordionContent";
|
|
366
|
+
//#endregion
|
|
367
|
+
//#region src/core/breadcrumb.tsx
|
|
368
|
+
function interpolate(template, values) {
|
|
369
|
+
return template.replace(/\{(\w+)\}/g, (_, key) => key in values ? String(values[key]) : `{${key}}`);
|
|
370
|
+
}
|
|
371
|
+
/** Renders the breadcrumb component. */
|
|
372
|
+
function Breadcrumb({ items, separator = "/", className, onNavigate, "aria-label": ariaLabel = "Breadcrumb" }) {
|
|
373
|
+
return /* @__PURE__ */ jsx("nav", {
|
|
374
|
+
"aria-label": ariaLabel,
|
|
375
|
+
className: cn("flex items-center gap-1 px-1.5 text-sm", className),
|
|
376
|
+
"data-opt-id": "3FR0Y",
|
|
377
|
+
"data-opt-slug": "breadcrumb.Breadcrumb",
|
|
378
|
+
children: /* @__PURE__ */ jsx("ol", {
|
|
379
|
+
className: "flex items-center gap-1",
|
|
380
|
+
children: items.map((item, index) => {
|
|
381
|
+
const isLast = index === items.length - 1;
|
|
382
|
+
const linkClassName = cn(`flex items-center gap-1.5 rounded-sm ${OPT_FOCUS} transition-colors`, item.disabled ? `${OPT_TEXT_TERTIARY} cursor-not-allowed` : `${OPT_TEXT_SECONDARY} hover:text-text-primary cursor-pointer`);
|
|
383
|
+
return /* @__PURE__ */ jsx("li", {
|
|
384
|
+
className: "flex items-center gap-1",
|
|
385
|
+
children: isLast ? /* @__PURE__ */ jsxs("span", {
|
|
386
|
+
"aria-current": "page",
|
|
387
|
+
className: `flex items-center gap-1.5 font-medium ${OPT_TEXT_PRIMARY}`,
|
|
388
|
+
children: [item.icon, item.label]
|
|
389
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [item.href ? /* @__PURE__ */ jsxs("a", {
|
|
390
|
+
href: item.disabled ? void 0 : item.href,
|
|
391
|
+
"aria-disabled": item.disabled || void 0,
|
|
392
|
+
className: linkClassName,
|
|
393
|
+
onClick: () => !item.disabled && (onNavigate === null || onNavigate === void 0 ? void 0 : onNavigate(item)),
|
|
394
|
+
children: [item.icon, item.label]
|
|
395
|
+
}) : /* @__PURE__ */ jsxs("button", {
|
|
396
|
+
type: "button",
|
|
397
|
+
disabled: item.disabled,
|
|
398
|
+
"aria-label": interpolate(koBreadcrumbLabels.navAriaLabel, { label: item.label }),
|
|
399
|
+
className: linkClassName,
|
|
400
|
+
onClick: () => !item.disabled && (onNavigate === null || onNavigate === void 0 ? void 0 : onNavigate(item)),
|
|
401
|
+
children: [item.icon, item.label]
|
|
402
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
403
|
+
"aria-hidden": "true",
|
|
404
|
+
className: OPT_TEXT_TERTIARY,
|
|
405
|
+
children: separator
|
|
406
|
+
})] })
|
|
407
|
+
}, item.id);
|
|
408
|
+
})
|
|
409
|
+
})
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
//#endregion
|
|
413
|
+
//#region src/core/card.tsx
|
|
414
|
+
const _excluded$13 = [
|
|
415
|
+
"variant",
|
|
416
|
+
"padding",
|
|
417
|
+
"shadow",
|
|
418
|
+
"selectable",
|
|
419
|
+
"selected",
|
|
420
|
+
"onSelectedChange",
|
|
421
|
+
"className",
|
|
422
|
+
"children",
|
|
423
|
+
"onClick",
|
|
424
|
+
"onKeyDown",
|
|
425
|
+
"role",
|
|
426
|
+
"tabIndex"
|
|
427
|
+
];
|
|
428
|
+
const _excluded2$6 = [
|
|
429
|
+
"gap",
|
|
430
|
+
"className",
|
|
431
|
+
"children"
|
|
432
|
+
];
|
|
433
|
+
const _excluded3$3 = [
|
|
434
|
+
"as",
|
|
435
|
+
"className",
|
|
436
|
+
"children"
|
|
437
|
+
];
|
|
438
|
+
const _excluded4$2 = ["className", "children"];
|
|
439
|
+
const _excluded5 = [
|
|
440
|
+
"spacing",
|
|
441
|
+
"className",
|
|
442
|
+
"children"
|
|
443
|
+
];
|
|
444
|
+
const _excluded6 = [
|
|
445
|
+
"divider",
|
|
446
|
+
"className",
|
|
447
|
+
"children"
|
|
448
|
+
];
|
|
449
|
+
/** Renders the card component. */
|
|
450
|
+
function Card(_ref) {
|
|
451
|
+
let { variant = "default", padding = "md", shadow = true, selectable = false, selected = false, onSelectedChange, className, children, onClick, onKeyDown, role, tabIndex } = _ref, props = _objectWithoutProperties(_ref, _excluded$13);
|
|
452
|
+
const isInteractive = selectable || !!onClick;
|
|
453
|
+
const handleClick = selectable ? (e) => {
|
|
454
|
+
onSelectedChange === null || onSelectedChange === void 0 || onSelectedChange(!selected);
|
|
455
|
+
onClick === null || onClick === void 0 || onClick(e);
|
|
456
|
+
} : onClick;
|
|
457
|
+
const handleKeyDown = selectable ? (e) => {
|
|
458
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
459
|
+
e.preventDefault();
|
|
460
|
+
onSelectedChange === null || onSelectedChange === void 0 || onSelectedChange(!selected);
|
|
461
|
+
}
|
|
462
|
+
onKeyDown === null || onKeyDown === void 0 || onKeyDown(e);
|
|
463
|
+
} : isInteractive ? (e) => {
|
|
464
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
465
|
+
e.preventDefault();
|
|
466
|
+
e.currentTarget.click();
|
|
467
|
+
}
|
|
468
|
+
onKeyDown === null || onKeyDown === void 0 || onKeyDown(e);
|
|
469
|
+
} : onKeyDown;
|
|
470
|
+
return /* @__PURE__ */ jsx("div", _objectSpread2(_objectSpread2(_objectSpread2({}, props), {}, { className: cn("rounded-xl", variant === "default" && cn(`border border-border bg-surface`, shadow && "shadow-sm"), variant === "outline" && "border-border border-2 bg-transparent", variant === "ghost" && "bg-bg-subtle", padding === "none" && "p-0", padding === "sm" && "p-3", padding === "md" && "p-5", padding === "lg" && "p-8", selectable && "cursor-pointer transition-colors", selectable && selected && "ring-accent border-accent ring-2 ring-offset-1", selectable && !selected && "hover:border-accent/50", className) }, isInteractive ? {
|
|
471
|
+
role: selectable ? "option" : "button",
|
|
472
|
+
tabIndex: 0,
|
|
473
|
+
"aria-selected": selectable ? selected : void 0,
|
|
474
|
+
onClick: handleClick,
|
|
475
|
+
onKeyDown: handleKeyDown
|
|
476
|
+
} : {
|
|
477
|
+
role,
|
|
478
|
+
tabIndex,
|
|
479
|
+
"aria-selected": selectable ? selected : void 0,
|
|
480
|
+
onKeyDown
|
|
481
|
+
}), {}, {
|
|
482
|
+
"data-opt-id": "0A18R",
|
|
483
|
+
"data-opt-slug": "card.Card",
|
|
484
|
+
children
|
|
485
|
+
}));
|
|
486
|
+
}
|
|
487
|
+
const headerGapClasses = {
|
|
488
|
+
sm: "gap-1",
|
|
489
|
+
md: "gap-1.5",
|
|
490
|
+
lg: "gap-2"
|
|
491
|
+
};
|
|
492
|
+
/** Renders the card header component. */
|
|
493
|
+
function CardHeader(_ref2) {
|
|
494
|
+
let { gap = "sm", className, children } = _ref2, props = _objectWithoutProperties(_ref2, _excluded2$6);
|
|
495
|
+
return /* @__PURE__ */ jsx("div", _objectSpread2(_objectSpread2({ className: cn("flex flex-col", headerGapClasses[gap], className) }, props), {}, {
|
|
496
|
+
"data-opt-id": "JST3J",
|
|
497
|
+
"data-opt-slug": "card.CardHeader",
|
|
498
|
+
children
|
|
499
|
+
}));
|
|
500
|
+
}
|
|
501
|
+
/** Renders the card title component. */
|
|
502
|
+
function CardTitle(_ref3) {
|
|
503
|
+
let { as: Tag = "h3", className, children } = _ref3, props = _objectWithoutProperties(_ref3, _excluded3$3);
|
|
504
|
+
return /* @__PURE__ */ jsx(Tag, _objectSpread2(_objectSpread2({ className: cn(`text-lg font-semibold ${OPT_TEXT_PRIMARY}`, className) }, props), {}, {
|
|
505
|
+
"data-opt-id": "V15TX",
|
|
506
|
+
"data-opt-slug": "card.CardTitle",
|
|
507
|
+
children
|
|
508
|
+
}));
|
|
509
|
+
}
|
|
510
|
+
/** Renders the card description component. */
|
|
511
|
+
function CardDescription(_ref4) {
|
|
512
|
+
let { className, children } = _ref4, props = _objectWithoutProperties(_ref4, _excluded4$2);
|
|
513
|
+
return /* @__PURE__ */ jsx("p", _objectSpread2(_objectSpread2({ className: cn(`text-sm ${OPT_TEXT_SECONDARY}`, className) }, props), {}, {
|
|
514
|
+
"data-opt-id": "YB1RN",
|
|
515
|
+
"data-opt-slug": "card.CardDescription",
|
|
516
|
+
children
|
|
517
|
+
}));
|
|
518
|
+
}
|
|
519
|
+
const contentSpacingClasses = {
|
|
520
|
+
none: "",
|
|
521
|
+
sm: "mt-2",
|
|
522
|
+
md: "mt-4",
|
|
523
|
+
lg: "mt-6"
|
|
524
|
+
};
|
|
525
|
+
/** Renders the card content component. */
|
|
526
|
+
function CardContent(_ref5) {
|
|
527
|
+
let { spacing = "md", className, children } = _ref5, props = _objectWithoutProperties(_ref5, _excluded5);
|
|
528
|
+
return /* @__PURE__ */ jsx("div", _objectSpread2(_objectSpread2({ className: cn(contentSpacingClasses[spacing], className) }, props), {}, {
|
|
529
|
+
"data-opt-id": "9Y68R",
|
|
530
|
+
"data-opt-slug": "card.CardContent",
|
|
531
|
+
children
|
|
532
|
+
}));
|
|
533
|
+
}
|
|
534
|
+
/** Renders the card footer component. */
|
|
535
|
+
function CardFooter(_ref6) {
|
|
536
|
+
let { divider = true, className, children } = _ref6, props = _objectWithoutProperties(_ref6, _excluded6);
|
|
537
|
+
return /* @__PURE__ */ jsx("div", _objectSpread2(_objectSpread2({ className: cn("mt-4 flex items-center justify-end gap-2 pt-4", divider && `border-t border border-border`, className) }, props), {}, {
|
|
538
|
+
"data-opt-id": "GRNTR",
|
|
539
|
+
"data-opt-slug": "card.CardFooter",
|
|
540
|
+
children
|
|
541
|
+
}));
|
|
542
|
+
}
|
|
543
|
+
//#endregion
|
|
544
|
+
//#region src/core/switch.tsx
|
|
545
|
+
const _excluded$12 = [
|
|
546
|
+
"label",
|
|
547
|
+
"required",
|
|
548
|
+
"error",
|
|
549
|
+
"hint",
|
|
550
|
+
"description",
|
|
551
|
+
"checked",
|
|
552
|
+
"defaultChecked",
|
|
553
|
+
"onChange",
|
|
554
|
+
"size",
|
|
555
|
+
"disabled",
|
|
556
|
+
"className",
|
|
557
|
+
"id",
|
|
558
|
+
"onClick",
|
|
559
|
+
"aria-label",
|
|
560
|
+
"aria-labelledby"
|
|
561
|
+
];
|
|
562
|
+
const sizeStyles$4 = {
|
|
563
|
+
sm: {
|
|
564
|
+
track: "h-5 w-9",
|
|
565
|
+
thumb: "h-4 w-4",
|
|
566
|
+
translate: "translate-x-4"
|
|
567
|
+
},
|
|
568
|
+
md: {
|
|
569
|
+
track: "h-6 w-11",
|
|
570
|
+
thumb: "h-5 w-5",
|
|
571
|
+
translate: "translate-x-5"
|
|
572
|
+
},
|
|
573
|
+
lg: {
|
|
574
|
+
track: "h-7 w-14",
|
|
575
|
+
thumb: "h-6 w-6",
|
|
576
|
+
translate: "translate-x-7"
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
const Switch = forwardRef((_ref, ref) => {
|
|
580
|
+
var _useInputId;
|
|
581
|
+
let { label, required, error, hint, description, checked: controlledChecked, defaultChecked = false, onChange, size = "md", disabled, className, id, onClick, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy } = _ref, props = _objectWithoutProperties(_ref, _excluded$12);
|
|
582
|
+
const fallbackId = useId();
|
|
583
|
+
const switchId = (_useInputId = useInputId(id, label, "switch")) !== null && _useInputId !== void 0 ? _useInputId : `switch-${fallbackId}`;
|
|
584
|
+
const hintText = hint !== null && hint !== void 0 ? hint : description;
|
|
585
|
+
const hasError = Boolean(error);
|
|
586
|
+
const styles = sizeStyles$4[size];
|
|
587
|
+
const labelId = label ? `${switchId}-label` : void 0;
|
|
588
|
+
const switchLabelledBy = ariaLabelledBy !== null && ariaLabelledBy !== void 0 ? ariaLabelledBy : ariaLabel ? void 0 : labelId;
|
|
589
|
+
const switchAriaLabel = ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : switchLabelledBy ? void 0 : "Switch";
|
|
590
|
+
const isControlled = controlledChecked !== void 0;
|
|
591
|
+
const [internalChecked, setInternalChecked] = useState(defaultChecked);
|
|
592
|
+
const checked = isControlled ? controlledChecked : internalChecked;
|
|
593
|
+
const handleClick = useCallback((e) => {
|
|
594
|
+
const next = !checked;
|
|
595
|
+
if (!isControlled) setInternalChecked(next);
|
|
596
|
+
onChange === null || onChange === void 0 || onChange(next);
|
|
597
|
+
onClick === null || onClick === void 0 || onClick(e);
|
|
598
|
+
}, [
|
|
599
|
+
checked,
|
|
600
|
+
isControlled,
|
|
601
|
+
onChange,
|
|
602
|
+
onClick
|
|
603
|
+
]);
|
|
604
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
605
|
+
className: cn("flex flex-col gap-1.5", className),
|
|
606
|
+
"data-opt-id": "51Z9R",
|
|
607
|
+
"data-opt-slug": "switch.Switch",
|
|
608
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
609
|
+
className: "flex items-start gap-3",
|
|
610
|
+
children: [/* @__PURE__ */ jsx("button", _objectSpread2(_objectSpread2({
|
|
611
|
+
ref,
|
|
612
|
+
type: "button",
|
|
613
|
+
role: "switch",
|
|
614
|
+
id: switchId,
|
|
615
|
+
"aria-checked": checked,
|
|
616
|
+
"aria-label": switchAriaLabel,
|
|
617
|
+
"aria-labelledby": switchLabelledBy,
|
|
618
|
+
disabled,
|
|
619
|
+
className: cn("group relative inline-flex shrink-0 cursor-pointer rounded-full p-0.5 transition-colors", styles.track, "bg-bg-muted", "aria-[checked=true]:bg-accent", "disabled:cursor-not-allowed disabled:opacity-50", OPT_FOCUS_VISIBLE),
|
|
620
|
+
"aria-invalid": hasError,
|
|
621
|
+
"aria-required": required,
|
|
622
|
+
"aria-describedby": getDescribedBy(switchId, error, hintText),
|
|
623
|
+
onClick: handleClick
|
|
624
|
+
}, props), {}, { children: /* @__PURE__ */ jsx("span", { className: cn("pointer-events-none inline-block transform rounded-full bg-white shadow-sm ring-0 transition-transform", styles.thumb, "translate-x-0", size === "sm" && "group-aria-[checked=true]:translate-x-4", size === "md" && "group-aria-[checked=true]:translate-x-5", size === "lg" && "group-aria-[checked=true]:translate-x-7") }) })), (label || hintText) && /* @__PURE__ */ jsxs("div", {
|
|
625
|
+
className: "flex flex-col",
|
|
626
|
+
children: [label && /* @__PURE__ */ jsxs("span", {
|
|
627
|
+
id: labelId,
|
|
628
|
+
className: cn("text-text-primary text-sm font-medium", disabled && "opacity-50"),
|
|
629
|
+
children: [label, required && /* @__PURE__ */ jsx(RequiredMark, {})]
|
|
630
|
+
}), hintText && /* @__PURE__ */ jsx("p", {
|
|
631
|
+
id: `${switchId}-hint`,
|
|
632
|
+
className: cn("text-text-tertiary text-sm", disabled && "opacity-50"),
|
|
633
|
+
children: hintText
|
|
634
|
+
})]
|
|
635
|
+
})]
|
|
636
|
+
}), /* @__PURE__ */ jsx(FieldMessages, {
|
|
637
|
+
id: switchId,
|
|
638
|
+
error
|
|
639
|
+
})]
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
Switch.displayName = "Switch";
|
|
643
|
+
//#endregion
|
|
644
|
+
//#region src/core/skeleton.tsx
|
|
645
|
+
const _excluded$11 = [
|
|
646
|
+
"variant",
|
|
647
|
+
"width",
|
|
648
|
+
"height",
|
|
649
|
+
"lines",
|
|
650
|
+
"animated",
|
|
651
|
+
"className",
|
|
652
|
+
"style"
|
|
653
|
+
];
|
|
654
|
+
const _excluded2$5 = ["lines", "className"];
|
|
655
|
+
const _excluded3$2 = ["size", "className"];
|
|
656
|
+
const _excluded4$1 = ["className"];
|
|
657
|
+
const variantStyles$2 = {
|
|
658
|
+
text: "rounded h-4",
|
|
659
|
+
circular: "rounded-full",
|
|
660
|
+
rectangular: "rounded-lg"
|
|
661
|
+
};
|
|
662
|
+
/** Renders the skeleton component. */
|
|
663
|
+
function Skeleton(_ref) {
|
|
664
|
+
let { variant = "text", width, height, lines = 1, animated = true, className, style } = _ref, props = _objectWithoutProperties(_ref, _excluded$11);
|
|
665
|
+
const baseClass = cn("bg-bg-subtle", animated && "animate-pulse", variantStyles$2[variant], className);
|
|
666
|
+
const computedStyle = _objectSpread2({
|
|
667
|
+
width: typeof width === "number" ? `${width}px` : width,
|
|
668
|
+
height: typeof height === "number" ? `${height}px` : height
|
|
669
|
+
}, style);
|
|
670
|
+
if (variant === "text" && lines > 1) return /* @__PURE__ */ jsxs("div", {
|
|
671
|
+
className: "gap-element flex flex-col",
|
|
672
|
+
role: "status",
|
|
673
|
+
"aria-label": "Loading",
|
|
674
|
+
"data-opt-id": "EJT87",
|
|
675
|
+
"data-opt-slug": "skeleton.Skeleton",
|
|
676
|
+
children: [Array.from({ length: lines }).map((_, i) => /* @__PURE__ */ jsx("div", _objectSpread2({
|
|
677
|
+
className: cn(baseClass, i === lines - 1 && "w-4/5"),
|
|
678
|
+
style: i === lines - 1 ? void 0 : computedStyle
|
|
679
|
+
}, props), i)), /* @__PURE__ */ jsx("span", {
|
|
680
|
+
className: "sr-only",
|
|
681
|
+
children: "Loading..."
|
|
682
|
+
})]
|
|
683
|
+
});
|
|
684
|
+
return /* @__PURE__ */ jsx("div", _objectSpread2(_objectSpread2({
|
|
685
|
+
className: baseClass,
|
|
686
|
+
style: computedStyle,
|
|
687
|
+
role: "status",
|
|
688
|
+
"aria-label": "Loading"
|
|
689
|
+
}, props), {}, {
|
|
690
|
+
"data-opt-id": "EJT87",
|
|
691
|
+
"data-opt-slug": "skeleton.Skeleton",
|
|
692
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
693
|
+
className: "sr-only",
|
|
694
|
+
children: "Loading..."
|
|
695
|
+
})
|
|
696
|
+
}));
|
|
697
|
+
}
|
|
698
|
+
/** Renders the skeleton text component. */
|
|
699
|
+
function SkeletonText(_ref2) {
|
|
700
|
+
let { lines = 3, className } = _ref2, props = _objectWithoutProperties(_ref2, _excluded2$5);
|
|
701
|
+
return /* @__PURE__ */ jsx(Skeleton, _objectSpread2(_objectSpread2({
|
|
702
|
+
variant: "text",
|
|
703
|
+
lines,
|
|
704
|
+
className
|
|
705
|
+
}, props), {}, {
|
|
706
|
+
"data-opt-id": "MX3D5",
|
|
707
|
+
"data-opt-slug": "skeleton.SkeletonText"
|
|
708
|
+
}));
|
|
709
|
+
}
|
|
710
|
+
/** Renders the skeleton avatar component. */
|
|
711
|
+
function SkeletonAvatar(_ref3) {
|
|
712
|
+
let { size = 40, className } = _ref3, props = _objectWithoutProperties(_ref3, _excluded3$2);
|
|
713
|
+
return /* @__PURE__ */ jsx(Skeleton, _objectSpread2(_objectSpread2({
|
|
714
|
+
variant: "circular",
|
|
715
|
+
width: size,
|
|
716
|
+
height: size,
|
|
717
|
+
className
|
|
718
|
+
}, props), {}, {
|
|
719
|
+
"data-opt-id": "MP0HZ",
|
|
720
|
+
"data-opt-slug": "skeleton.SkeletonAvatar"
|
|
721
|
+
}));
|
|
722
|
+
}
|
|
723
|
+
/** Renders the skeleton card component. */
|
|
724
|
+
function SkeletonCard(_ref4) {
|
|
725
|
+
let { className } = _ref4, props = _objectWithoutProperties(_ref4, _excluded4$1);
|
|
726
|
+
return /* @__PURE__ */ jsxs("div", _objectSpread2(_objectSpread2({ className: cn("gap-group flex flex-col", className) }, props), {}, {
|
|
727
|
+
"data-opt-id": "34V5D",
|
|
728
|
+
"data-opt-slug": "skeleton.SkeletonCard",
|
|
729
|
+
children: [
|
|
730
|
+
/* @__PURE__ */ jsx(Skeleton, {
|
|
731
|
+
variant: "rectangular",
|
|
732
|
+
height: 120
|
|
733
|
+
}),
|
|
734
|
+
/* @__PURE__ */ jsx(Skeleton, {
|
|
735
|
+
variant: "text",
|
|
736
|
+
width: "60%"
|
|
737
|
+
}),
|
|
738
|
+
/* @__PURE__ */ jsx(Skeleton, {
|
|
739
|
+
variant: "text",
|
|
740
|
+
lines: 2
|
|
741
|
+
})
|
|
742
|
+
]
|
|
743
|
+
}));
|
|
744
|
+
}
|
|
745
|
+
/** Renders the skeleton table component. */
|
|
746
|
+
function SkeletonTable({ rows = 5, columns = 4, className }) {
|
|
747
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
748
|
+
className: cn("gap-element flex flex-col", className),
|
|
749
|
+
role: "status",
|
|
750
|
+
"aria-label": "Loading table",
|
|
751
|
+
"data-opt-id": "3KEDN",
|
|
752
|
+
"data-opt-slug": "skeleton.SkeletonTable",
|
|
753
|
+
children: [
|
|
754
|
+
/* @__PURE__ */ jsx("div", {
|
|
755
|
+
className: "border-border-subtle gap-group flex border-b pb-2",
|
|
756
|
+
children: Array.from({ length: columns }).map((_, i) => /* @__PURE__ */ jsx(Skeleton, {
|
|
757
|
+
variant: "text",
|
|
758
|
+
className: "flex-1",
|
|
759
|
+
height: 16
|
|
760
|
+
}, i))
|
|
761
|
+
}),
|
|
762
|
+
Array.from({ length: rows }).map((_, rowIndex) => /* @__PURE__ */ jsx("div", {
|
|
763
|
+
className: "gap-group flex py-2",
|
|
764
|
+
children: Array.from({ length: columns }).map((_, colIndex) => /* @__PURE__ */ jsx(Skeleton, {
|
|
765
|
+
variant: "text",
|
|
766
|
+
className: "flex-1",
|
|
767
|
+
height: 14
|
|
768
|
+
}, colIndex))
|
|
769
|
+
}, rowIndex)),
|
|
770
|
+
/* @__PURE__ */ jsx("span", {
|
|
771
|
+
className: "sr-only",
|
|
772
|
+
children: "Loading..."
|
|
773
|
+
})
|
|
774
|
+
]
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
//#endregion
|
|
778
|
+
//#region src/core/textarea.tsx
|
|
779
|
+
const _excluded$10 = [
|
|
780
|
+
"label",
|
|
781
|
+
"error",
|
|
782
|
+
"hint",
|
|
783
|
+
"fullWidth",
|
|
784
|
+
"required",
|
|
785
|
+
"resize",
|
|
786
|
+
"size",
|
|
787
|
+
"className",
|
|
788
|
+
"id",
|
|
789
|
+
"rows"
|
|
790
|
+
];
|
|
791
|
+
const textareaSizeStyles = OPT_TEXTAREA_SIZE;
|
|
792
|
+
const resizeStyles = {
|
|
793
|
+
none: "resize-none",
|
|
794
|
+
vertical: "resize-y",
|
|
795
|
+
horizontal: "resize-x",
|
|
796
|
+
both: "resize"
|
|
797
|
+
};
|
|
798
|
+
const Textarea = forwardRef((_ref, ref) => {
|
|
799
|
+
var _useInputId;
|
|
800
|
+
let { label, error, hint, fullWidth = false, required, resize = "vertical", size = "md", className, id, rows = 4 } = _ref, props = _objectWithoutProperties(_ref, _excluded$10);
|
|
801
|
+
const fallbackId = useId().replace(/:/g, "");
|
|
802
|
+
const textareaId = (_useInputId = useInputId(id, label, "textarea")) !== null && _useInputId !== void 0 ? _useInputId : `textarea-${fallbackId}`;
|
|
803
|
+
const hasError = Boolean(error);
|
|
804
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
805
|
+
className: cn("flex flex-col gap-1.5", fullWidth && "w-full"),
|
|
806
|
+
"data-opt-id": "6T58H",
|
|
807
|
+
"data-opt-slug": "textarea.Textarea",
|
|
808
|
+
children: [
|
|
809
|
+
label && /* @__PURE__ */ jsxs("label", {
|
|
810
|
+
htmlFor: textareaId,
|
|
811
|
+
className: "text-text-primary text-sm font-medium",
|
|
812
|
+
children: [label, required && /* @__PURE__ */ jsx(RequiredMark, {})]
|
|
813
|
+
}),
|
|
814
|
+
/* @__PURE__ */ jsx("textarea", _objectSpread2({
|
|
815
|
+
ref,
|
|
816
|
+
id: textareaId,
|
|
817
|
+
rows,
|
|
818
|
+
className: cn("border-border bg-surface text-text-primary placeholder:text-text-tertiary w-full rounded-lg border px-4 py-3 text-sm outline-transparent", OPT_FOCUS_VISIBLE, textareaSizeStyles[size], resizeStyles[resize], hasError && "border-danger focus-visible:outline-danger", !fullWidth && "w-auto", className),
|
|
819
|
+
"aria-invalid": hasError,
|
|
820
|
+
"aria-describedby": getDescribedBy(textareaId, error, hint)
|
|
821
|
+
}, props)),
|
|
822
|
+
/* @__PURE__ */ jsx(FieldMessages, {
|
|
823
|
+
id: textareaId,
|
|
824
|
+
error,
|
|
825
|
+
hint
|
|
826
|
+
})
|
|
827
|
+
]
|
|
828
|
+
});
|
|
829
|
+
});
|
|
830
|
+
Textarea.displayName = "Textarea";
|
|
831
|
+
//#endregion
|
|
832
|
+
//#region src/core/avatar.tsx
|
|
833
|
+
const _excluded$9 = [
|
|
834
|
+
"src",
|
|
835
|
+
"srcSet",
|
|
836
|
+
"sizes",
|
|
837
|
+
"crossOrigin",
|
|
838
|
+
"referrerPolicy",
|
|
839
|
+
"alt",
|
|
840
|
+
"name",
|
|
841
|
+
"size",
|
|
842
|
+
"fallback",
|
|
843
|
+
"status",
|
|
844
|
+
"className",
|
|
845
|
+
"onLoadingStatusChange"
|
|
846
|
+
];
|
|
847
|
+
const _excluded2$4 = [
|
|
848
|
+
"max",
|
|
849
|
+
"size",
|
|
850
|
+
"children",
|
|
851
|
+
"className"
|
|
852
|
+
];
|
|
853
|
+
const useIsoLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
|
854
|
+
/**
|
|
855
|
+
* Tracks an image's loading status by preloading it off-DOM, so the avatar can
|
|
856
|
+
* show its fallback until the image is actually decoded (no broken-image flash)
|
|
857
|
+
* and recover from late `error`/`load` events. Supports responsive `srcSet`/`sizes`.
|
|
858
|
+
*/
|
|
859
|
+
function useImageLoadingStatus(src, { srcSet, sizes, crossOrigin, referrerPolicy }) {
|
|
860
|
+
const [status, setStatus] = useState("idle");
|
|
861
|
+
useIsoLayoutEffect(() => {
|
|
862
|
+
if (!src && !srcSet) {
|
|
863
|
+
setStatus("error");
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
let isMounted = true;
|
|
867
|
+
const image = new window.Image();
|
|
868
|
+
const update = (next) => () => {
|
|
869
|
+
if (isMounted) setStatus(next);
|
|
870
|
+
};
|
|
871
|
+
setStatus("loading");
|
|
872
|
+
image.onload = update("loaded");
|
|
873
|
+
image.onerror = update("error");
|
|
874
|
+
if (referrerPolicy) image.referrerPolicy = referrerPolicy;
|
|
875
|
+
image.crossOrigin = crossOrigin !== null && crossOrigin !== void 0 ? crossOrigin : null;
|
|
876
|
+
if (sizes) image.sizes = sizes;
|
|
877
|
+
if (srcSet) image.srcset = srcSet;
|
|
878
|
+
if (src) image.src = src;
|
|
879
|
+
if (image.complete) setStatus(image.naturalWidth > 0 ? "loaded" : "error");
|
|
880
|
+
return () => {
|
|
881
|
+
isMounted = false;
|
|
882
|
+
};
|
|
883
|
+
}, [
|
|
884
|
+
src,
|
|
885
|
+
srcSet,
|
|
886
|
+
sizes,
|
|
887
|
+
crossOrigin,
|
|
888
|
+
referrerPolicy
|
|
889
|
+
]);
|
|
890
|
+
return status;
|
|
891
|
+
}
|
|
892
|
+
const sizeStyles$3 = {
|
|
893
|
+
xs: "h-6 w-6 text-[10px]",
|
|
894
|
+
sm: "h-8 w-8 text-xs",
|
|
895
|
+
md: "h-10 w-10 text-sm",
|
|
896
|
+
lg: "h-12 w-12 text-base",
|
|
897
|
+
xl: "h-16 w-16 text-lg"
|
|
898
|
+
};
|
|
899
|
+
const statusStyles = {
|
|
900
|
+
online: "bg-success",
|
|
901
|
+
offline: "bg-bg-muted",
|
|
902
|
+
busy: "bg-danger",
|
|
903
|
+
away: "bg-warning"
|
|
904
|
+
};
|
|
905
|
+
const statusSizeStyles = {
|
|
906
|
+
xs: "h-1.5 w-1.5 border",
|
|
907
|
+
sm: "h-2 w-2 border",
|
|
908
|
+
md: "h-2.5 w-2.5 border-2",
|
|
909
|
+
lg: "h-3 w-3 border-2",
|
|
910
|
+
xl: "h-4 w-4 border-2"
|
|
911
|
+
};
|
|
912
|
+
function getInitials(name) {
|
|
913
|
+
const parts = name.trim().split(/\s+/);
|
|
914
|
+
if (parts.length === 1) return parts[0].charAt(0).toUpperCase();
|
|
915
|
+
return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
|
|
916
|
+
}
|
|
917
|
+
const bgColors = [
|
|
918
|
+
"bg-blue-500",
|
|
919
|
+
"bg-emerald-500",
|
|
920
|
+
"bg-amber-500",
|
|
921
|
+
"bg-rose-500",
|
|
922
|
+
"bg-purple-500",
|
|
923
|
+
"bg-cyan-500",
|
|
924
|
+
"bg-orange-500",
|
|
925
|
+
"bg-indigo-500"
|
|
926
|
+
];
|
|
927
|
+
function getColorFromName(name) {
|
|
928
|
+
let hash = 0;
|
|
929
|
+
for (let i = 0; i < name.length; i++) hash = name.charCodeAt(i) + ((hash << 5) - hash);
|
|
930
|
+
return bgColors[Math.abs(hash) % bgColors.length];
|
|
931
|
+
}
|
|
932
|
+
/** Renders the avatar component. */
|
|
933
|
+
function Avatar(_ref) {
|
|
934
|
+
var _ref2, _ref3;
|
|
935
|
+
let { src, srcSet, sizes, crossOrigin, referrerPolicy, alt, name, size = "md", fallback, status, className, onLoadingStatusChange } = _ref, props = _objectWithoutProperties(_ref, _excluded$9);
|
|
936
|
+
const loadingStatus = useImageLoadingStatus(src, {
|
|
937
|
+
srcSet,
|
|
938
|
+
sizes,
|
|
939
|
+
crossOrigin,
|
|
940
|
+
referrerPolicy
|
|
941
|
+
});
|
|
942
|
+
useEffect(() => {
|
|
943
|
+
onLoadingStatusChange === null || onLoadingStatusChange === void 0 || onLoadingStatusChange(loadingStatus);
|
|
944
|
+
}, [loadingStatus, onLoadingStatusChange]);
|
|
945
|
+
const showImage = (Boolean(src) || Boolean(srcSet)) && loadingStatus === "loaded";
|
|
946
|
+
const initials = name ? getInitials(name) : null;
|
|
947
|
+
const bgColor = name ? getColorFromName(name) : "bg-bg-subtle";
|
|
948
|
+
const avatarLabel = status ? `${(_ref2 = name !== null && name !== void 0 ? name : alt) !== null && _ref2 !== void 0 ? _ref2 : "Avatar"} (${status})` : (_ref3 = name !== null && name !== void 0 ? name : alt) !== null && _ref3 !== void 0 ? _ref3 : "Avatar";
|
|
949
|
+
let avatarContent;
|
|
950
|
+
if (showImage) {
|
|
951
|
+
var _ref4;
|
|
952
|
+
avatarContent = /* @__PURE__ */ jsx("img", {
|
|
953
|
+
src,
|
|
954
|
+
srcSet,
|
|
955
|
+
sizes,
|
|
956
|
+
crossOrigin,
|
|
957
|
+
referrerPolicy,
|
|
958
|
+
alt: (_ref4 = alt !== null && alt !== void 0 ? alt : name) !== null && _ref4 !== void 0 ? _ref4 : "Avatar",
|
|
959
|
+
className: "h-full w-full object-cover"
|
|
960
|
+
});
|
|
961
|
+
} else if (fallback) avatarContent = fallback;
|
|
962
|
+
else if (initials) avatarContent = /* @__PURE__ */ jsx("span", {
|
|
963
|
+
className: "font-medium text-white",
|
|
964
|
+
children: initials
|
|
965
|
+
});
|
|
966
|
+
else avatarContent = /* @__PURE__ */ jsx("svg", {
|
|
967
|
+
className: "text-text-tertiary h-3/5 w-3/5",
|
|
968
|
+
fill: "currentColor",
|
|
969
|
+
viewBox: "0 0 24 24",
|
|
970
|
+
"aria-hidden": "true",
|
|
971
|
+
children: /* @__PURE__ */ jsx("path", { d: "M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" })
|
|
972
|
+
});
|
|
973
|
+
return /* @__PURE__ */ jsxs("span", _objectSpread2(_objectSpread2({
|
|
974
|
+
role: "img",
|
|
975
|
+
"aria-label": avatarLabel,
|
|
976
|
+
className: cn("relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full", sizeStyles$3[size], !showImage && bgColor, className)
|
|
977
|
+
}, props), {}, {
|
|
978
|
+
"data-opt-id": "SCMM0",
|
|
979
|
+
"data-opt-slug": "avatar.Avatar",
|
|
980
|
+
children: [avatarContent, status && /* @__PURE__ */ jsx("span", {
|
|
981
|
+
className: cn("border-surface absolute right-0 bottom-0 rounded-full", statusStyles[status], statusSizeStyles[size]),
|
|
982
|
+
"aria-hidden": "true"
|
|
983
|
+
})]
|
|
984
|
+
}));
|
|
985
|
+
}
|
|
986
|
+
/** Renders the avatar group component. */
|
|
987
|
+
function AvatarGroup(_ref5) {
|
|
988
|
+
let { max = 4, size = "md", children, className } = _ref5, props = _objectWithoutProperties(_ref5, _excluded2$4);
|
|
989
|
+
const avatars = Array.isArray(children) ? children : [children];
|
|
990
|
+
const visibleAvatars = avatars.slice(0, max);
|
|
991
|
+
const remainingCount = avatars.length - max;
|
|
992
|
+
return /* @__PURE__ */ jsxs("div", _objectSpread2(_objectSpread2({ className: cn("flex", className) }, props), {}, {
|
|
993
|
+
"data-opt-id": "MVB53",
|
|
994
|
+
"data-opt-slug": "avatar.AvatarGroup",
|
|
995
|
+
children: [visibleAvatars.map((child, index) => /* @__PURE__ */ jsx("span", {
|
|
996
|
+
className: "ring-surface relative -ml-2 inline-block rounded-full ring-2 first:ml-0",
|
|
997
|
+
children: child
|
|
998
|
+
}, index)), remainingCount > 0 && /* @__PURE__ */ jsxs("span", {
|
|
999
|
+
className: cn("bg-bg-subtle text-text-secondary ring-surface relative inline-flex items-center justify-center rounded-full font-medium ring-2", sizeStyles$3[size]),
|
|
1000
|
+
children: ["+", remainingCount]
|
|
1001
|
+
})]
|
|
1002
|
+
}));
|
|
1003
|
+
}
|
|
1004
|
+
//#endregion
|
|
1005
|
+
//#region src/core/progress.tsx
|
|
1006
|
+
const _excluded$8 = [
|
|
1007
|
+
"value",
|
|
1008
|
+
"max",
|
|
1009
|
+
"size",
|
|
1010
|
+
"variant",
|
|
1011
|
+
"showLabel",
|
|
1012
|
+
"label",
|
|
1013
|
+
"animated",
|
|
1014
|
+
"indeterminate",
|
|
1015
|
+
"className"
|
|
1016
|
+
];
|
|
1017
|
+
const _excluded2$3 = [
|
|
1018
|
+
"value",
|
|
1019
|
+
"max",
|
|
1020
|
+
"size",
|
|
1021
|
+
"strokeWidth",
|
|
1022
|
+
"variant",
|
|
1023
|
+
"showLabel",
|
|
1024
|
+
"label",
|
|
1025
|
+
"indeterminate",
|
|
1026
|
+
"className"
|
|
1027
|
+
];
|
|
1028
|
+
const sizeStyles$2 = {
|
|
1029
|
+
sm: "h-1",
|
|
1030
|
+
md: "h-2",
|
|
1031
|
+
lg: "h-3"
|
|
1032
|
+
};
|
|
1033
|
+
const variantStyles$1 = {
|
|
1034
|
+
default: "bg-accent",
|
|
1035
|
+
success: "bg-success",
|
|
1036
|
+
warning: "bg-warning",
|
|
1037
|
+
error: "bg-danger"
|
|
1038
|
+
};
|
|
1039
|
+
function getProgressValue(value, max) {
|
|
1040
|
+
const safeMax = Number.isFinite(max) ? Math.max(0, max) : 100;
|
|
1041
|
+
const clampedValue = safeMax > 0 ? Math.min(Math.max(Number.isFinite(value) ? value : 0, 0), safeMax) : 0;
|
|
1042
|
+
const percentage = safeMax > 0 ? clampedValue / safeMax * 100 : 0;
|
|
1043
|
+
return {
|
|
1044
|
+
clampedValue,
|
|
1045
|
+
max: safeMax,
|
|
1046
|
+
percentage,
|
|
1047
|
+
valueText: `${Math.round(percentage)}%`
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
/** Renders the progress component. */
|
|
1051
|
+
function Progress(_ref) {
|
|
1052
|
+
let { value, max = 100, size = "md", variant = "default", showLabel = false, label, animated = true, indeterminate = false, className } = _ref, props = _objectWithoutProperties(_ref, _excluded$8);
|
|
1053
|
+
const progress = indeterminate || value === void 0 ? null : getProgressValue(value, max);
|
|
1054
|
+
const shouldRenderHeader = Boolean(label) || showLabel && progress;
|
|
1055
|
+
return /* @__PURE__ */ jsxs("div", _objectSpread2(_objectSpread2({ className: cn("w-full", className) }, props), {}, {
|
|
1056
|
+
"data-opt-id": "Y5N8R",
|
|
1057
|
+
"data-opt-slug": "progress.Progress",
|
|
1058
|
+
children: [shouldRenderHeader && /* @__PURE__ */ jsxs("div", {
|
|
1059
|
+
className: "mb-1.5 flex items-center justify-between text-sm",
|
|
1060
|
+
children: [label && /* @__PURE__ */ jsx("span", {
|
|
1061
|
+
className: "text-text-primary font-medium",
|
|
1062
|
+
children: label
|
|
1063
|
+
}), showLabel && progress && /* @__PURE__ */ jsx("span", {
|
|
1064
|
+
className: "text-text-tertiary",
|
|
1065
|
+
children: progress.valueText
|
|
1066
|
+
})]
|
|
1067
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
1068
|
+
className: cn("bg-bg-subtle w-full overflow-hidden rounded-full", sizeStyles$2[size]),
|
|
1069
|
+
role: "progressbar",
|
|
1070
|
+
"aria-valuenow": progress === null || progress === void 0 ? void 0 : progress.clampedValue,
|
|
1071
|
+
"aria-valuemin": progress ? 0 : void 0,
|
|
1072
|
+
"aria-valuemax": progress === null || progress === void 0 ? void 0 : progress.max,
|
|
1073
|
+
"aria-valuetext": progress === null || progress === void 0 ? void 0 : progress.valueText,
|
|
1074
|
+
"aria-label": label !== null && label !== void 0 ? label : progress ? `${progress.valueText} complete` : "Loading",
|
|
1075
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1076
|
+
className: cn("h-full rounded-full", variantStyles$1[variant], !progress && "w-1/3 animate-pulse", animated && (progress ? "transition-all duration-300 ease-out" : "transition-opacity duration-700 ease-in-out")),
|
|
1077
|
+
style: progress ? { width: `${progress.percentage}%` } : void 0
|
|
1078
|
+
})
|
|
1079
|
+
})]
|
|
1080
|
+
}));
|
|
1081
|
+
}
|
|
1082
|
+
const circularVariantStyles = {
|
|
1083
|
+
default: "text-accent",
|
|
1084
|
+
success: "text-success",
|
|
1085
|
+
warning: "text-warning",
|
|
1086
|
+
error: "text-danger"
|
|
1087
|
+
};
|
|
1088
|
+
/** Renders the circular progress component. */
|
|
1089
|
+
function CircularProgress(_ref2) {
|
|
1090
|
+
var _progress$percentage;
|
|
1091
|
+
let { value, max = 100, size = 48, strokeWidth = 4, variant = "default", showLabel = false, label, indeterminate = false, className } = _ref2, props = _objectWithoutProperties(_ref2, _excluded2$3);
|
|
1092
|
+
const labelId = useId();
|
|
1093
|
+
const isIndeterminate = indeterminate || value === void 0;
|
|
1094
|
+
const progress = isIndeterminate ? null : getProgressValue(value, max);
|
|
1095
|
+
const accessibleName = label !== null && label !== void 0 ? label : progress ? `${progress.valueText} complete` : "Loading";
|
|
1096
|
+
const radius = (size - strokeWidth) / 2;
|
|
1097
|
+
const circumference = radius * 2 * Math.PI;
|
|
1098
|
+
const strokeDashoffset = circumference - ((_progress$percentage = progress === null || progress === void 0 ? void 0 : progress.percentage) !== null && _progress$percentage !== void 0 ? _progress$percentage : 0) / 100 * circumference;
|
|
1099
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1100
|
+
className: "relative inline-flex items-center justify-center",
|
|
1101
|
+
"data-opt-id": "Q69KP",
|
|
1102
|
+
"data-opt-slug": "progress.CircularProgress",
|
|
1103
|
+
children: [
|
|
1104
|
+
label && /* @__PURE__ */ jsx("span", {
|
|
1105
|
+
id: labelId,
|
|
1106
|
+
className: "sr-only",
|
|
1107
|
+
children: label
|
|
1108
|
+
}),
|
|
1109
|
+
/* @__PURE__ */ jsxs("svg", _objectSpread2(_objectSpread2({
|
|
1110
|
+
width: size,
|
|
1111
|
+
height: size,
|
|
1112
|
+
className: cn(isIndeterminate ? "animate-spin" : "-rotate-90", className),
|
|
1113
|
+
role: isIndeterminate ? "progressbar" : "meter",
|
|
1114
|
+
"aria-labelledby": label ? labelId : void 0,
|
|
1115
|
+
"aria-valuenow": progress === null || progress === void 0 ? void 0 : progress.clampedValue,
|
|
1116
|
+
"aria-valuemin": progress ? 0 : void 0,
|
|
1117
|
+
"aria-valuemax": progress === null || progress === void 0 ? void 0 : progress.max,
|
|
1118
|
+
"aria-valuetext": progress === null || progress === void 0 ? void 0 : progress.valueText,
|
|
1119
|
+
"aria-label": label ? void 0 : accessibleName
|
|
1120
|
+
}, props), {}, { children: [/* @__PURE__ */ jsx("circle", {
|
|
1121
|
+
cx: size / 2,
|
|
1122
|
+
cy: size / 2,
|
|
1123
|
+
r: radius,
|
|
1124
|
+
fill: "none",
|
|
1125
|
+
strokeWidth,
|
|
1126
|
+
className: "stroke-border-subtle"
|
|
1127
|
+
}), /* @__PURE__ */ jsx("circle", {
|
|
1128
|
+
cx: size / 2,
|
|
1129
|
+
cy: size / 2,
|
|
1130
|
+
r: radius,
|
|
1131
|
+
fill: "none",
|
|
1132
|
+
strokeWidth,
|
|
1133
|
+
strokeLinecap: "round",
|
|
1134
|
+
className: cn("transition-all duration-300 ease-out", circularVariantStyles[variant]),
|
|
1135
|
+
style: {
|
|
1136
|
+
strokeDasharray: isIndeterminate ? `${circumference * .28} ${circumference}` : circumference,
|
|
1137
|
+
strokeDashoffset: isIndeterminate ? 0 : strokeDashoffset,
|
|
1138
|
+
stroke: "currentColor"
|
|
1139
|
+
}
|
|
1140
|
+
})] })),
|
|
1141
|
+
showLabel && progress && /* @__PURE__ */ jsx("span", {
|
|
1142
|
+
className: "text-text-primary absolute text-xs font-medium",
|
|
1143
|
+
children: progress.valueText
|
|
1144
|
+
})
|
|
1145
|
+
]
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
//#endregion
|
|
1149
|
+
//#region src/core/meter.tsx
|
|
1150
|
+
const _excluded$7 = [
|
|
1151
|
+
"value",
|
|
1152
|
+
"min",
|
|
1153
|
+
"max",
|
|
1154
|
+
"size",
|
|
1155
|
+
"variant",
|
|
1156
|
+
"showValue",
|
|
1157
|
+
"label",
|
|
1158
|
+
"getValueLabel",
|
|
1159
|
+
"className"
|
|
1160
|
+
];
|
|
1161
|
+
const sizeStyles$1 = {
|
|
1162
|
+
sm: "h-1",
|
|
1163
|
+
md: "h-2",
|
|
1164
|
+
lg: "h-3"
|
|
1165
|
+
};
|
|
1166
|
+
const variantStyles = {
|
|
1167
|
+
default: "bg-accent",
|
|
1168
|
+
success: "bg-success",
|
|
1169
|
+
warning: "bg-warning",
|
|
1170
|
+
error: "bg-danger"
|
|
1171
|
+
};
|
|
1172
|
+
/**
|
|
1173
|
+
* A scalar measurement within a known range (disk usage, score, capacity) —
|
|
1174
|
+
* `role="meter"`, distinct from Progress (task completion). Built on the
|
|
1175
|
+
* opt-ui-primitives Meter primitive, which owns the role/aria/valuetext.
|
|
1176
|
+
*/
|
|
1177
|
+
function Meter$1(_ref) {
|
|
1178
|
+
let { value, min = 0, max = 100, size = "md", variant = "default", showValue = false, label, getValueLabel, className } = _ref, props = _objectWithoutProperties(_ref, _excluded$7);
|
|
1179
|
+
const clamped = Math.min(Math.max(value, min), max);
|
|
1180
|
+
const percentage = max === min ? 0 : (clamped - min) / (max - min) * 100;
|
|
1181
|
+
return /* @__PURE__ */ jsxs("div", _objectSpread2(_objectSpread2({ className: cn("w-full", className) }, props), {}, {
|
|
1182
|
+
"data-opt-id": "G01TE",
|
|
1183
|
+
"data-opt-slug": "meter.Meter",
|
|
1184
|
+
children: [(showValue || label) && /* @__PURE__ */ jsxs("div", {
|
|
1185
|
+
className: "mb-1.5 flex items-center justify-between text-sm",
|
|
1186
|
+
children: [label && /* @__PURE__ */ jsx("span", {
|
|
1187
|
+
className: "text-text-primary font-medium",
|
|
1188
|
+
children: label
|
|
1189
|
+
}), showValue && /* @__PURE__ */ jsxs("span", {
|
|
1190
|
+
className: "text-text-tertiary",
|
|
1191
|
+
children: [Math.round(percentage), "%"]
|
|
1192
|
+
})]
|
|
1193
|
+
}), /* @__PURE__ */ jsx(Meter, {
|
|
1194
|
+
value: clamped,
|
|
1195
|
+
min,
|
|
1196
|
+
max,
|
|
1197
|
+
getValueLabel,
|
|
1198
|
+
"aria-label": label !== null && label !== void 0 ? label : `${Math.round(percentage)}%`,
|
|
1199
|
+
className: cn("bg-bg-subtle w-full overflow-hidden rounded-full", sizeStyles$1[size]),
|
|
1200
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1201
|
+
className: cn("h-full rounded-full transition-all duration-300 ease-out", variantStyles[variant]),
|
|
1202
|
+
style: { width: `${percentage}%` }
|
|
1203
|
+
})
|
|
1204
|
+
})]
|
|
1205
|
+
}));
|
|
1206
|
+
}
|
|
1207
|
+
//#endregion
|
|
1208
|
+
//#region src/core/step-builder.tsx
|
|
1209
|
+
/** Renders the step builder component. */
|
|
1210
|
+
function StepBuilder({ steps, onChange, label, addLabel = "Add step", maxSteps, className, "aria-label": ariaLabel }) {
|
|
1211
|
+
var _ref;
|
|
1212
|
+
const [editingId, setEditingId] = useState(null);
|
|
1213
|
+
const nextStepIdRef = useRef(1);
|
|
1214
|
+
const createStepId = useCallback(() => {
|
|
1215
|
+
const existingIds = new Set(steps.map((step) => step.id));
|
|
1216
|
+
let id = "";
|
|
1217
|
+
do
|
|
1218
|
+
id = `step-${nextStepIdRef.current++}`;
|
|
1219
|
+
while (existingIds.has(id));
|
|
1220
|
+
return id;
|
|
1221
|
+
}, [steps]);
|
|
1222
|
+
const addStep = useCallback(() => {
|
|
1223
|
+
if (maxSteps && steps.length >= maxSteps) return;
|
|
1224
|
+
const newStep = {
|
|
1225
|
+
id: createStepId(),
|
|
1226
|
+
label: `Step ${steps.length + 1}`
|
|
1227
|
+
};
|
|
1228
|
+
onChange([...steps, newStep]);
|
|
1229
|
+
setEditingId(newStep.id);
|
|
1230
|
+
}, [
|
|
1231
|
+
steps,
|
|
1232
|
+
onChange,
|
|
1233
|
+
maxSteps,
|
|
1234
|
+
createStepId
|
|
1235
|
+
]);
|
|
1236
|
+
const removeStep = useCallback((id) => {
|
|
1237
|
+
onChange(steps.filter((s) => s.id !== id));
|
|
1238
|
+
if (editingId === id) setEditingId(null);
|
|
1239
|
+
}, [
|
|
1240
|
+
steps,
|
|
1241
|
+
onChange,
|
|
1242
|
+
editingId
|
|
1243
|
+
]);
|
|
1244
|
+
const updateStep = useCallback((id, updates) => {
|
|
1245
|
+
onChange(steps.map((s) => s.id === id ? _objectSpread2(_objectSpread2({}, s), updates) : s));
|
|
1246
|
+
}, [steps, onChange]);
|
|
1247
|
+
const moveStep = useCallback((index, direction) => {
|
|
1248
|
+
const target = index + direction;
|
|
1249
|
+
if (target < 0 || target >= steps.length) return;
|
|
1250
|
+
const newSteps = [...steps];
|
|
1251
|
+
[newSteps[index], newSteps[target]] = [newSteps[target], newSteps[index]];
|
|
1252
|
+
onChange(newSteps);
|
|
1253
|
+
}, [steps, onChange]);
|
|
1254
|
+
const atLimit = Boolean(maxSteps && steps.length >= maxSteps);
|
|
1255
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1256
|
+
className: cn("flex flex-col gap-3", className),
|
|
1257
|
+
role: "list",
|
|
1258
|
+
"aria-label": (_ref = ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : label) !== null && _ref !== void 0 ? _ref : "Step builder",
|
|
1259
|
+
"data-opt-id": "7BQDT",
|
|
1260
|
+
"data-opt-slug": "step-builder.StepBuilder",
|
|
1261
|
+
children: [
|
|
1262
|
+
label && /* @__PURE__ */ jsx("span", {
|
|
1263
|
+
className: "text-sm font-medium text-text-primary",
|
|
1264
|
+
children: label
|
|
1265
|
+
}),
|
|
1266
|
+
steps.map((step, index) => {
|
|
1267
|
+
var _step$description;
|
|
1268
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1269
|
+
className: cn(OPT_SURFACE, OPT_BORDER, "flex items-start gap-3 p-3"),
|
|
1270
|
+
role: "listitem",
|
|
1271
|
+
children: [
|
|
1272
|
+
/* @__PURE__ */ jsx("span", {
|
|
1273
|
+
className: "bg-accent text-accent-fg flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full text-xs font-bold",
|
|
1274
|
+
"aria-hidden": "true",
|
|
1275
|
+
children: index + 1
|
|
1276
|
+
}),
|
|
1277
|
+
/* @__PURE__ */ jsx("div", {
|
|
1278
|
+
className: "min-w-0 flex-1",
|
|
1279
|
+
children: editingId === step.id ? /* @__PURE__ */ jsxs("div", {
|
|
1280
|
+
className: "flex flex-col gap-2",
|
|
1281
|
+
children: [
|
|
1282
|
+
/* @__PURE__ */ jsx("input", {
|
|
1283
|
+
type: "text",
|
|
1284
|
+
value: step.label,
|
|
1285
|
+
onChange: (e) => updateStep(step.id, { label: e.target.value }),
|
|
1286
|
+
onKeyDown: (e) => {
|
|
1287
|
+
if (e.key === "Enter") setEditingId(null);
|
|
1288
|
+
},
|
|
1289
|
+
className: cn("border-border bg-surface focus-visible:ring-ring focus-visible:ring-offset-ring-offset w-full rounded-md border px-2 py-1 text-sm outline-none focus-visible:ring-2 focus-visible:ring-offset-1", OPT_TEXT_PRIMARY, OPT_FOCUS_VISIBLE),
|
|
1290
|
+
"aria-label": "Step label",
|
|
1291
|
+
autoFocus: true
|
|
1292
|
+
}),
|
|
1293
|
+
/* @__PURE__ */ jsx("input", {
|
|
1294
|
+
type: "text",
|
|
1295
|
+
value: (_step$description = step.description) !== null && _step$description !== void 0 ? _step$description : "",
|
|
1296
|
+
onChange: (e) => updateStep(step.id, { description: e.target.value }),
|
|
1297
|
+
placeholder: "Description (optional)",
|
|
1298
|
+
className: cn("border-border bg-surface focus-visible:ring-ring focus-visible:ring-offset-ring-offset w-full rounded-md border px-2 py-1 text-sm outline-none focus-visible:ring-2 focus-visible:ring-offset-1", OPT_TEXT_PRIMARY, "placeholder:text-text-tertiary", OPT_FOCUS_VISIBLE),
|
|
1299
|
+
"aria-label": "Step description"
|
|
1300
|
+
}),
|
|
1301
|
+
/* @__PURE__ */ jsx(Button, {
|
|
1302
|
+
variant: "ghost",
|
|
1303
|
+
size: "sm",
|
|
1304
|
+
onClick: () => setEditingId(null),
|
|
1305
|
+
children: "Done"
|
|
1306
|
+
})
|
|
1307
|
+
]
|
|
1308
|
+
}) : /* @__PURE__ */ jsxs("button", {
|
|
1309
|
+
type: "button",
|
|
1310
|
+
className: "w-full text-left",
|
|
1311
|
+
onClick: () => setEditingId(step.id),
|
|
1312
|
+
"aria-label": `Edit ${step.label}`,
|
|
1313
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1314
|
+
className: cn(OPT_TEXT_PRIMARY, "text-sm font-medium"),
|
|
1315
|
+
children: step.label
|
|
1316
|
+
}), step.description && /* @__PURE__ */ jsx("span", {
|
|
1317
|
+
className: cn("text-text-secondary", "mt-0.5 block text-xs"),
|
|
1318
|
+
children: step.description
|
|
1319
|
+
})]
|
|
1320
|
+
})
|
|
1321
|
+
}),
|
|
1322
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1323
|
+
className: "flex flex-shrink-0 items-center gap-1",
|
|
1324
|
+
children: [
|
|
1325
|
+
/* @__PURE__ */ jsx("button", {
|
|
1326
|
+
type: "button",
|
|
1327
|
+
onClick: () => moveStep(index, -1),
|
|
1328
|
+
disabled: index === 0,
|
|
1329
|
+
className: cn("rounded p-1 text-sm", OPT_TEXT_TERTIARY, "hover:bg-bg-subtle", "disabled:opacity-30", OPT_FOCUS_VISIBLE),
|
|
1330
|
+
"aria-label": "Move up",
|
|
1331
|
+
children: "▲"
|
|
1332
|
+
}),
|
|
1333
|
+
/* @__PURE__ */ jsx("button", {
|
|
1334
|
+
type: "button",
|
|
1335
|
+
onClick: () => moveStep(index, 1),
|
|
1336
|
+
disabled: index === steps.length - 1,
|
|
1337
|
+
className: cn("rounded p-1 text-sm", OPT_TEXT_TERTIARY, "hover:bg-bg-subtle", "disabled:opacity-30", OPT_FOCUS_VISIBLE),
|
|
1338
|
+
"aria-label": "Move down",
|
|
1339
|
+
children: "▼"
|
|
1340
|
+
}),
|
|
1341
|
+
/* @__PURE__ */ jsx("button", {
|
|
1342
|
+
type: "button",
|
|
1343
|
+
onClick: () => removeStep(step.id),
|
|
1344
|
+
className: cn("text-danger-fg hover:bg-danger-subtle rounded p-1 text-sm", OPT_FOCUS_VISIBLE),
|
|
1345
|
+
"aria-label": `Remove ${step.label}`,
|
|
1346
|
+
children: "✕"
|
|
1347
|
+
})
|
|
1348
|
+
]
|
|
1349
|
+
})
|
|
1350
|
+
]
|
|
1351
|
+
}, step.id);
|
|
1352
|
+
}),
|
|
1353
|
+
/* @__PURE__ */ jsxs(Button, {
|
|
1354
|
+
variant: "secondary",
|
|
1355
|
+
size: "sm",
|
|
1356
|
+
onClick: addStep,
|
|
1357
|
+
disabled: atLimit,
|
|
1358
|
+
className: "self-start",
|
|
1359
|
+
children: ["+ ", addLabel]
|
|
1360
|
+
})
|
|
1361
|
+
]
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
StepBuilder.displayName = "StepBuilder";
|
|
1365
|
+
//#endregion
|
|
1366
|
+
//#region src/lib/formatters.ts
|
|
1367
|
+
const OPT_UI_DEFAULT_LOCALE = "ko-KR";
|
|
1368
|
+
const KST_OFFSET_MS = 324e5;
|
|
1369
|
+
function getKstDateParts(input) {
|
|
1370
|
+
const date = input instanceof Date ? input : new Date(input);
|
|
1371
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
1372
|
+
const kstDate = new Date(date.getTime() + KST_OFFSET_MS);
|
|
1373
|
+
return {
|
|
1374
|
+
year: kstDate.getUTCFullYear(),
|
|
1375
|
+
month: kstDate.getUTCMonth() + 1,
|
|
1376
|
+
day: kstDate.getUTCDate(),
|
|
1377
|
+
hour: kstDate.getUTCHours(),
|
|
1378
|
+
minute: kstDate.getUTCMinutes(),
|
|
1379
|
+
second: kstDate.getUTCSeconds()
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
function pad2(value) {
|
|
1383
|
+
return String(value).padStart(2, "0");
|
|
1384
|
+
}
|
|
1385
|
+
function meridiem(hour) {
|
|
1386
|
+
return hour < 12 ? "오전" : "오후";
|
|
1387
|
+
}
|
|
1388
|
+
function hour12(hour) {
|
|
1389
|
+
const normalized = hour % 12;
|
|
1390
|
+
return normalized === 0 ? 12 : normalized;
|
|
1391
|
+
}
|
|
1392
|
+
/** Formats a date as Korean short date text in the opt-ui default timezone. */
|
|
1393
|
+
function formatOptShortDate(input) {
|
|
1394
|
+
const parts = getKstDateParts(input);
|
|
1395
|
+
if (!parts) return "-";
|
|
1396
|
+
return `${parts.month}월 ${parts.day}일`;
|
|
1397
|
+
}
|
|
1398
|
+
/** Formats a date as Korean clock time in the opt-ui default timezone. */
|
|
1399
|
+
function formatOptTime(input) {
|
|
1400
|
+
const parts = getKstDateParts(input);
|
|
1401
|
+
if (!parts) return "-";
|
|
1402
|
+
return `${meridiem(parts.hour)} ${pad2(hour12(parts.hour))}:${pad2(parts.minute)}`;
|
|
1403
|
+
}
|
|
1404
|
+
/** Formats a date as Korean date-time text in the opt-ui default timezone. */
|
|
1405
|
+
function formatOptDateTime(input) {
|
|
1406
|
+
const parts = getKstDateParts(input);
|
|
1407
|
+
if (!parts) return "-";
|
|
1408
|
+
return `${parts.year}. ${parts.month}. ${parts.day}. ${meridiem(parts.hour)} ${hour12(parts.hour)}:${pad2(parts.minute)}:${pad2(parts.second)}`;
|
|
1409
|
+
}
|
|
1410
|
+
/** Formats numbers without depending on the runtime default locale. */
|
|
1411
|
+
function formatOptNumber(value) {
|
|
1412
|
+
return new Intl.NumberFormat(OPT_UI_DEFAULT_LOCALE).format(value);
|
|
1413
|
+
}
|
|
1414
|
+
//#endregion
|
|
1415
|
+
//#region src/core/condition-builder.tsx
|
|
1416
|
+
const OPERATOR_LABELS = {
|
|
1417
|
+
equals: "equals",
|
|
1418
|
+
not_equals: "does not equal",
|
|
1419
|
+
contains: "contains",
|
|
1420
|
+
not_contains: "does not contain",
|
|
1421
|
+
gt: ">",
|
|
1422
|
+
lt: "<",
|
|
1423
|
+
gte: ">=",
|
|
1424
|
+
lte: "<=",
|
|
1425
|
+
is_set: "is set",
|
|
1426
|
+
is_not_set: "is not set"
|
|
1427
|
+
};
|
|
1428
|
+
const OPERATORS = /* @__PURE__ */ new Set([
|
|
1429
|
+
"equals",
|
|
1430
|
+
"not_equals",
|
|
1431
|
+
"contains",
|
|
1432
|
+
"not_contains",
|
|
1433
|
+
"gt",
|
|
1434
|
+
"lt",
|
|
1435
|
+
"gte",
|
|
1436
|
+
"lte",
|
|
1437
|
+
"is_set",
|
|
1438
|
+
"is_not_set"
|
|
1439
|
+
]);
|
|
1440
|
+
function isConditionOperator(v) {
|
|
1441
|
+
return OPERATORS.has(v);
|
|
1442
|
+
}
|
|
1443
|
+
const NO_VALUE_OPS = ["is_set", "is_not_set"];
|
|
1444
|
+
const DEFAULT_PROPERTIES = [
|
|
1445
|
+
"event_name",
|
|
1446
|
+
"user_id",
|
|
1447
|
+
"email",
|
|
1448
|
+
"country",
|
|
1449
|
+
"browser",
|
|
1450
|
+
"os",
|
|
1451
|
+
"device",
|
|
1452
|
+
"page_url",
|
|
1453
|
+
"referrer",
|
|
1454
|
+
"amount"
|
|
1455
|
+
];
|
|
1456
|
+
const ConditionBuilderIdScopeContext = React.createContext(null);
|
|
1457
|
+
function collectConditionBuilderIds(groups) {
|
|
1458
|
+
const groupIds = /* @__PURE__ */ new Set();
|
|
1459
|
+
const conditionIds = /* @__PURE__ */ new Set();
|
|
1460
|
+
const visit = (items) => {
|
|
1461
|
+
for (const group of items) {
|
|
1462
|
+
groupIds.add(group.id);
|
|
1463
|
+
for (const condition of group.conditions) conditionIds.add(condition.id);
|
|
1464
|
+
if (group.children) visit(group.children);
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
visit(groups);
|
|
1468
|
+
return {
|
|
1469
|
+
groupIds,
|
|
1470
|
+
conditionIds
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
/** Renders the condition builder component. */
|
|
1474
|
+
function ConditionBuilder({ groups, onChange, properties = DEFAULT_PROPERTIES, label, addConditionLabel = "Add condition", addGroupLabel = "Add group", addNestedGroupLabel = "Add nested group", nested = false, maxDepth = 3, conditionTypes, onPreview, previewCount, className, "aria-label": ariaLabel }) {
|
|
1475
|
+
var _ref;
|
|
1476
|
+
const inheritedIdScope = React.useContext(ConditionBuilderIdScopeContext);
|
|
1477
|
+
const nextGroupIdRef = React.useRef(1);
|
|
1478
|
+
const nextConditionIdRef = React.useRef(1);
|
|
1479
|
+
const getLocalReservedIds = useCallback(() => collectConditionBuilderIds(groups), [groups]);
|
|
1480
|
+
const localIdScope = React.useMemo(() => ({
|
|
1481
|
+
nextGroupIdRef,
|
|
1482
|
+
nextConditionIdRef,
|
|
1483
|
+
getReservedIds: getLocalReservedIds
|
|
1484
|
+
}), [getLocalReservedIds]);
|
|
1485
|
+
const idScope = inheritedIdScope !== null && inheritedIdScope !== void 0 ? inheritedIdScope : localIdScope;
|
|
1486
|
+
const createGroupId = useCallback((suffix = "") => {
|
|
1487
|
+
const { groupIds } = idScope.getReservedIds();
|
|
1488
|
+
let id = "";
|
|
1489
|
+
do
|
|
1490
|
+
id = `group-${idScope.nextGroupIdRef.current++}${suffix}`;
|
|
1491
|
+
while (groupIds.has(id));
|
|
1492
|
+
return id;
|
|
1493
|
+
}, [idScope]);
|
|
1494
|
+
const createConditionId = useCallback((suffix = "") => {
|
|
1495
|
+
const { conditionIds } = idScope.getReservedIds();
|
|
1496
|
+
let id = "";
|
|
1497
|
+
do
|
|
1498
|
+
id = `cond-${idScope.nextConditionIdRef.current++}${suffix}`;
|
|
1499
|
+
while (conditionIds.has(id));
|
|
1500
|
+
return id;
|
|
1501
|
+
}, [idScope]);
|
|
1502
|
+
useEffect(() => {
|
|
1503
|
+
onPreview === null || onPreview === void 0 || onPreview(groups);
|
|
1504
|
+
}, [groups, onPreview]);
|
|
1505
|
+
const addGroup = useCallback(() => {
|
|
1506
|
+
var _properties$;
|
|
1507
|
+
const newGroup = {
|
|
1508
|
+
id: createGroupId(),
|
|
1509
|
+
logic: "and",
|
|
1510
|
+
conditions: [{
|
|
1511
|
+
id: createConditionId(),
|
|
1512
|
+
property: (_properties$ = properties[0]) !== null && _properties$ !== void 0 ? _properties$ : "",
|
|
1513
|
+
operator: "equals",
|
|
1514
|
+
value: ""
|
|
1515
|
+
}]
|
|
1516
|
+
};
|
|
1517
|
+
onChange([...groups, newGroup]);
|
|
1518
|
+
}, [
|
|
1519
|
+
groups,
|
|
1520
|
+
onChange,
|
|
1521
|
+
properties,
|
|
1522
|
+
createGroupId,
|
|
1523
|
+
createConditionId
|
|
1524
|
+
]);
|
|
1525
|
+
const removeGroup = useCallback((groupId) => {
|
|
1526
|
+
onChange(groups.filter((g) => g.id !== groupId));
|
|
1527
|
+
}, [groups, onChange]);
|
|
1528
|
+
const updateGroupLogic = useCallback((groupId, logic) => {
|
|
1529
|
+
onChange(groups.map((g) => g.id === groupId ? _objectSpread2(_objectSpread2({}, g), {}, { logic }) : g));
|
|
1530
|
+
}, [groups, onChange]);
|
|
1531
|
+
const addCondition = useCallback((groupId) => {
|
|
1532
|
+
onChange(groups.map((g) => {
|
|
1533
|
+
var _properties$2;
|
|
1534
|
+
return g.id === groupId ? _objectSpread2(_objectSpread2({}, g), {}, { conditions: [...g.conditions, {
|
|
1535
|
+
id: createConditionId(),
|
|
1536
|
+
property: (_properties$2 = properties[0]) !== null && _properties$2 !== void 0 ? _properties$2 : "",
|
|
1537
|
+
operator: "equals",
|
|
1538
|
+
value: ""
|
|
1539
|
+
}] }) : g;
|
|
1540
|
+
}));
|
|
1541
|
+
}, [
|
|
1542
|
+
groups,
|
|
1543
|
+
onChange,
|
|
1544
|
+
properties,
|
|
1545
|
+
createConditionId
|
|
1546
|
+
]);
|
|
1547
|
+
const removeCondition = useCallback((groupId, condId) => {
|
|
1548
|
+
onChange(groups.map((g) => g.id === groupId ? _objectSpread2(_objectSpread2({}, g), {}, { conditions: g.conditions.filter((c) => c.id !== condId) }) : g));
|
|
1549
|
+
}, [groups, onChange]);
|
|
1550
|
+
const updateCondition = useCallback((groupId, condId, updates) => {
|
|
1551
|
+
onChange(groups.map((g) => g.id === groupId ? _objectSpread2(_objectSpread2({}, g), {}, { conditions: g.conditions.map((c) => c.id === condId ? _objectSpread2(_objectSpread2({}, c), updates) : c) }) : g));
|
|
1552
|
+
}, [groups, onChange]);
|
|
1553
|
+
return /* @__PURE__ */ jsx(ConditionBuilderIdScopeContext.Provider, {
|
|
1554
|
+
value: idScope,
|
|
1555
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1556
|
+
className: cn("flex flex-col gap-3", className),
|
|
1557
|
+
"aria-label": (_ref = ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : label) !== null && _ref !== void 0 ? _ref : "Condition builder",
|
|
1558
|
+
"data-opt-id": "SXXKJ",
|
|
1559
|
+
"data-opt-slug": "condition-builder.ConditionBuilder",
|
|
1560
|
+
children: [
|
|
1561
|
+
label && /* @__PURE__ */ jsx("span", {
|
|
1562
|
+
className: "text-sm font-medium text-text-primary",
|
|
1563
|
+
children: label
|
|
1564
|
+
}),
|
|
1565
|
+
groups.map((group, groupIndex) => /* @__PURE__ */ jsxs("div", { children: [groupIndex > 0 && /* @__PURE__ */ jsxs("div", {
|
|
1566
|
+
className: "my-2 flex items-center gap-2",
|
|
1567
|
+
children: [
|
|
1568
|
+
/* @__PURE__ */ jsx("div", { className: "bg-border-subtle h-px flex-1" }),
|
|
1569
|
+
/* @__PURE__ */ jsx("span", {
|
|
1570
|
+
className: "text-warning-fg text-xs font-semibold uppercase",
|
|
1571
|
+
children: "OR"
|
|
1572
|
+
}),
|
|
1573
|
+
/* @__PURE__ */ jsx("div", { className: "bg-border-subtle h-px flex-1" })
|
|
1574
|
+
]
|
|
1575
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1576
|
+
className: cn(OPT_SURFACE, OPT_BORDER, "p-3"),
|
|
1577
|
+
role: "group",
|
|
1578
|
+
"aria-label": `Condition group ${groupIndex + 1}`,
|
|
1579
|
+
children: [
|
|
1580
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1581
|
+
className: "mb-2 flex items-center justify-between",
|
|
1582
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1583
|
+
className: "flex items-center gap-2",
|
|
1584
|
+
children: [
|
|
1585
|
+
/* @__PURE__ */ jsx("span", {
|
|
1586
|
+
className: cn(OPT_TEXT_SECONDARY, "text-xs font-medium"),
|
|
1587
|
+
children: "Match"
|
|
1588
|
+
}),
|
|
1589
|
+
/* @__PURE__ */ jsxs("select", {
|
|
1590
|
+
value: group.logic,
|
|
1591
|
+
onChange: (e) => {
|
|
1592
|
+
const v = e.target.value;
|
|
1593
|
+
if (v === "and" || v === "or") updateGroupLogic(group.id, v);
|
|
1594
|
+
},
|
|
1595
|
+
className: cn("border-border bg-surface focus-visible:ring-ring focus-visible:ring-offset-ring-offset rounded border px-2 py-0.5 text-xs font-semibold outline-none focus-visible:ring-2 focus-visible:ring-offset-1", OPT_TEXT_PRIMARY, OPT_FOCUS_VISIBLE),
|
|
1596
|
+
"aria-label": "Group logic",
|
|
1597
|
+
children: [/* @__PURE__ */ jsx("option", {
|
|
1598
|
+
value: "and",
|
|
1599
|
+
children: "ALL"
|
|
1600
|
+
}), /* @__PURE__ */ jsx("option", {
|
|
1601
|
+
value: "or",
|
|
1602
|
+
children: "ANY"
|
|
1603
|
+
})]
|
|
1604
|
+
}),
|
|
1605
|
+
/* @__PURE__ */ jsx("span", {
|
|
1606
|
+
className: cn(OPT_TEXT_SECONDARY, "text-xs"),
|
|
1607
|
+
children: "of the following"
|
|
1608
|
+
})
|
|
1609
|
+
]
|
|
1610
|
+
}), groups.length > 1 && /* @__PURE__ */ jsx("button", {
|
|
1611
|
+
type: "button",
|
|
1612
|
+
onClick: () => removeGroup(group.id),
|
|
1613
|
+
className: cn("text-danger-fg hover:bg-danger-subtle rounded p-1 text-xs", "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-ring-offset"),
|
|
1614
|
+
"aria-label": "Remove group",
|
|
1615
|
+
children: "✕"
|
|
1616
|
+
})]
|
|
1617
|
+
}),
|
|
1618
|
+
/* @__PURE__ */ jsx("div", {
|
|
1619
|
+
className: "flex flex-col gap-2",
|
|
1620
|
+
children: group.conditions.map((cond, condIndex) => {
|
|
1621
|
+
const customRenderer = cond.type != null ? conditionTypes === null || conditionTypes === void 0 ? void 0 : conditionTypes[cond.type] : void 0;
|
|
1622
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1623
|
+
className: "flex items-center gap-2",
|
|
1624
|
+
children: [
|
|
1625
|
+
condIndex > 0 && /* @__PURE__ */ jsx("span", {
|
|
1626
|
+
className: cn("w-8 text-center text-[10px] font-semibold uppercase", group.logic === "and" ? "text-info-fg" : "text-warning-fg"),
|
|
1627
|
+
children: group.logic
|
|
1628
|
+
}),
|
|
1629
|
+
condIndex === 0 && /* @__PURE__ */ jsx("span", { className: "w-8" }),
|
|
1630
|
+
customRenderer ? /* @__PURE__ */ jsx("div", {
|
|
1631
|
+
className: "min-w-[100px] flex-1",
|
|
1632
|
+
role: "group",
|
|
1633
|
+
"aria-label": customRenderer.label,
|
|
1634
|
+
children: customRenderer.render(cond, (updates) => updateCondition(group.id, cond.id, updates))
|
|
1635
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1636
|
+
/* @__PURE__ */ jsx("select", {
|
|
1637
|
+
value: cond.property,
|
|
1638
|
+
onChange: (e) => updateCondition(group.id, cond.id, { property: e.target.value }),
|
|
1639
|
+
className: cn("border-border bg-surface focus-visible:ring-ring focus-visible:ring-offset-ring-offset min-w-[120px] rounded border px-2 py-1.5 text-sm outline-none focus-visible:ring-2 focus-visible:ring-offset-1", OPT_TEXT_PRIMARY, OPT_FOCUS_VISIBLE),
|
|
1640
|
+
"aria-label": "Property",
|
|
1641
|
+
children: properties.map((p) => /* @__PURE__ */ jsx("option", {
|
|
1642
|
+
value: p,
|
|
1643
|
+
children: p
|
|
1644
|
+
}, p))
|
|
1645
|
+
}),
|
|
1646
|
+
/* @__PURE__ */ jsx("select", {
|
|
1647
|
+
value: cond.operator,
|
|
1648
|
+
onChange: (e) => {
|
|
1649
|
+
const v = e.target.value;
|
|
1650
|
+
if (isConditionOperator(v)) updateCondition(group.id, cond.id, { operator: v });
|
|
1651
|
+
},
|
|
1652
|
+
className: cn("border-border bg-surface focus-visible:ring-ring focus-visible:ring-offset-ring-offset min-w-[130px] rounded border px-2 py-1.5 text-sm outline-none focus-visible:ring-2 focus-visible:ring-offset-1", OPT_TEXT_PRIMARY, OPT_FOCUS_VISIBLE),
|
|
1653
|
+
"aria-label": "Operator",
|
|
1654
|
+
children: Object.entries(OPERATOR_LABELS).map(([key, lbl]) => /* @__PURE__ */ jsx("option", {
|
|
1655
|
+
value: key,
|
|
1656
|
+
children: lbl
|
|
1657
|
+
}, key))
|
|
1658
|
+
}),
|
|
1659
|
+
!NO_VALUE_OPS.includes(cond.operator) && /* @__PURE__ */ jsx("input", {
|
|
1660
|
+
type: "text",
|
|
1661
|
+
value: cond.value,
|
|
1662
|
+
onChange: (e) => updateCondition(group.id, cond.id, { value: e.target.value }),
|
|
1663
|
+
placeholder: "Value",
|
|
1664
|
+
className: cn("border-border bg-surface focus-visible:ring-ring focus-visible:ring-offset-ring-offset min-w-[100px] flex-1 rounded border px-2 py-1.5 text-sm outline-none focus-visible:ring-2 focus-visible:ring-offset-1", "text-text-primary", "placeholder:text-text-tertiary", "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-ring-offset"),
|
|
1665
|
+
"aria-label": "Value"
|
|
1666
|
+
})
|
|
1667
|
+
] }),
|
|
1668
|
+
group.conditions.length > 1 && /* @__PURE__ */ jsx("button", {
|
|
1669
|
+
type: "button",
|
|
1670
|
+
onClick: () => removeCondition(group.id, cond.id),
|
|
1671
|
+
className: cn("text-danger-fg hover:bg-danger-subtle rounded p-1 text-sm", "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-ring-offset"),
|
|
1672
|
+
"aria-label": "Remove condition",
|
|
1673
|
+
children: "✕"
|
|
1674
|
+
})
|
|
1675
|
+
]
|
|
1676
|
+
}, cond.id);
|
|
1677
|
+
})
|
|
1678
|
+
}),
|
|
1679
|
+
nested && group.children && group.children.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
1680
|
+
className: "border-accent/30 mt-2 ml-4 border-l-2 pl-3",
|
|
1681
|
+
children: /* @__PURE__ */ jsx(ConditionBuilder, {
|
|
1682
|
+
groups: group.children,
|
|
1683
|
+
onChange: (newChildren) => {
|
|
1684
|
+
onChange(groups.map((g) => g.id === group.id ? _objectSpread2(_objectSpread2({}, g), {}, { children: newChildren }) : g));
|
|
1685
|
+
},
|
|
1686
|
+
properties,
|
|
1687
|
+
addConditionLabel,
|
|
1688
|
+
addGroupLabel,
|
|
1689
|
+
addNestedGroupLabel,
|
|
1690
|
+
nested,
|
|
1691
|
+
maxDepth: maxDepth - 1,
|
|
1692
|
+
conditionTypes
|
|
1693
|
+
})
|
|
1694
|
+
}),
|
|
1695
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1696
|
+
className: "mt-2 flex items-center gap-2",
|
|
1697
|
+
children: [/* @__PURE__ */ jsxs(Button, {
|
|
1698
|
+
variant: "ghost",
|
|
1699
|
+
size: "sm",
|
|
1700
|
+
onClick: () => addCondition(group.id),
|
|
1701
|
+
children: ["+ ", addConditionLabel]
|
|
1702
|
+
}), nested && maxDepth > 1 && /* @__PURE__ */ jsxs(Button, {
|
|
1703
|
+
variant: "ghost",
|
|
1704
|
+
size: "sm",
|
|
1705
|
+
onClick: () => {
|
|
1706
|
+
var _properties$3;
|
|
1707
|
+
const nestedGroup = {
|
|
1708
|
+
id: createGroupId("-nested"),
|
|
1709
|
+
logic: group.logic === "and" ? "or" : "and",
|
|
1710
|
+
conditions: [{
|
|
1711
|
+
id: createConditionId("-nested"),
|
|
1712
|
+
property: (_properties$3 = properties[0]) !== null && _properties$3 !== void 0 ? _properties$3 : "",
|
|
1713
|
+
operator: "equals",
|
|
1714
|
+
value: ""
|
|
1715
|
+
}]
|
|
1716
|
+
};
|
|
1717
|
+
onChange(groups.map((g) => {
|
|
1718
|
+
var _g$children;
|
|
1719
|
+
return g.id === group.id ? _objectSpread2(_objectSpread2({}, g), {}, { children: [...(_g$children = g.children) !== null && _g$children !== void 0 ? _g$children : [], nestedGroup] }) : g;
|
|
1720
|
+
}));
|
|
1721
|
+
},
|
|
1722
|
+
children: ["+ ", addNestedGroupLabel]
|
|
1723
|
+
})]
|
|
1724
|
+
})
|
|
1725
|
+
]
|
|
1726
|
+
})] }, group.id)),
|
|
1727
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1728
|
+
className: "flex items-center gap-3",
|
|
1729
|
+
children: [/* @__PURE__ */ jsxs(Button, {
|
|
1730
|
+
variant: "secondary",
|
|
1731
|
+
size: "sm",
|
|
1732
|
+
onClick: addGroup,
|
|
1733
|
+
className: "self-start",
|
|
1734
|
+
children: ["+ ", addGroupLabel]
|
|
1735
|
+
}), previewCount != null && /* @__PURE__ */ jsxs("span", {
|
|
1736
|
+
className: cn("text-text-secondary", "text-sm"),
|
|
1737
|
+
children: [formatOptNumber(previewCount), " matches"]
|
|
1738
|
+
})]
|
|
1739
|
+
})
|
|
1740
|
+
]
|
|
1741
|
+
})
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
ConditionBuilder.displayName = "ConditionBuilder";
|
|
1745
|
+
//#endregion
|
|
1746
|
+
//#region src/core/dropdown-menu.tsx
|
|
1747
|
+
const _excluded$6 = ["className"];
|
|
1748
|
+
const _excluded2$2 = ["className"];
|
|
1749
|
+
const _excluded3$1 = ["className", "variant"];
|
|
1750
|
+
const _excluded4 = ["className"];
|
|
1751
|
+
const DropdownMenu = MenuRoot$1;
|
|
1752
|
+
/** Renders the dropdown trigger component. */
|
|
1753
|
+
function DropdownTrigger(_ref) {
|
|
1754
|
+
let { className } = _ref, props = _objectWithoutProperties(_ref, _excluded$6);
|
|
1755
|
+
return /* @__PURE__ */ jsx(MenuTrigger$1, _objectSpread2(_objectSpread2({ className: cn("cursor-pointer gap-2", className) }, props), {}, {
|
|
1756
|
+
"data-opt-id": "C4BSS",
|
|
1757
|
+
"data-opt-slug": "dropdown-menu.DropdownTrigger"
|
|
1758
|
+
}));
|
|
1759
|
+
}
|
|
1760
|
+
/** Renders the dropdown content component. */
|
|
1761
|
+
function DropdownContent(_ref2) {
|
|
1762
|
+
let { className } = _ref2, props = _objectWithoutProperties(_ref2, _excluded2$2);
|
|
1763
|
+
return /* @__PURE__ */ jsx(MenuPopover$1, _objectSpread2(_objectSpread2({ className: cn(className) }, props), {}, {
|
|
1764
|
+
"data-opt-id": "J6NF7",
|
|
1765
|
+
"data-opt-slug": "dropdown-menu.DropdownContent"
|
|
1766
|
+
}));
|
|
1767
|
+
}
|
|
1768
|
+
/** Renders the dropdown item component. */
|
|
1769
|
+
function DropdownItem(_ref3) {
|
|
1770
|
+
let { className, variant = "default" } = _ref3, props = _objectWithoutProperties(_ref3, _excluded3$1);
|
|
1771
|
+
return /* @__PURE__ */ jsx(MenuItem$1, _objectSpread2(_objectSpread2({ className: cn("cursor-pointer data-[disabled]:cursor-not-allowed", variant === "destructive" && "text-danger-fg data-[active-item]:bg-danger-subtle data-[active-item]:text-danger-fg", className) }, props), {}, {
|
|
1772
|
+
"data-opt-id": "5M6GA",
|
|
1773
|
+
"data-opt-slug": "dropdown-menu.DropdownItem"
|
|
1774
|
+
}));
|
|
1775
|
+
}
|
|
1776
|
+
/** Renders the dropdown separator component. */
|
|
1777
|
+
function DropdownSeparator(_ref4) {
|
|
1778
|
+
let { className } = _ref4, props = _objectWithoutProperties(_ref4, _excluded4);
|
|
1779
|
+
return /* @__PURE__ */ jsx(MenuSeparator$1, _objectSpread2(_objectSpread2({ className: cn(className) }, props), {}, {
|
|
1780
|
+
"data-opt-id": "K9Y54",
|
|
1781
|
+
"data-opt-slug": "dropdown-menu.DropdownSeparator"
|
|
1782
|
+
}));
|
|
1783
|
+
}
|
|
1784
|
+
//#endregion
|
|
1785
|
+
//#region src/core/radio-group.tsx
|
|
1786
|
+
const _excluded$5 = [
|
|
1787
|
+
"label",
|
|
1788
|
+
"hint",
|
|
1789
|
+
"description",
|
|
1790
|
+
"value",
|
|
1791
|
+
"name",
|
|
1792
|
+
"size",
|
|
1793
|
+
"disabled",
|
|
1794
|
+
"className"
|
|
1795
|
+
];
|
|
1796
|
+
const sizeStyles = {
|
|
1797
|
+
sm: {
|
|
1798
|
+
radio: "h-4 w-4",
|
|
1799
|
+
dot: "h-2 w-2",
|
|
1800
|
+
text: "text-sm"
|
|
1801
|
+
},
|
|
1802
|
+
md: {
|
|
1803
|
+
radio: "h-5 w-5",
|
|
1804
|
+
dot: "h-2.5 w-2.5",
|
|
1805
|
+
text: "text-sm"
|
|
1806
|
+
},
|
|
1807
|
+
lg: {
|
|
1808
|
+
radio: "h-6 w-6",
|
|
1809
|
+
dot: "h-3 w-3",
|
|
1810
|
+
text: "text-base"
|
|
1811
|
+
}
|
|
1812
|
+
};
|
|
1813
|
+
const RadioGroupContext = createContext({ size: "md" });
|
|
1814
|
+
/** Renders the radio group component. */
|
|
1815
|
+
function RadioGroup({ children, value, defaultValue = "", onChange, disabled, size = "md", error, required, className, orientation = "vertical", label, name }) {
|
|
1816
|
+
const ctxValue = useMemo(() => ({
|
|
1817
|
+
size,
|
|
1818
|
+
disabled
|
|
1819
|
+
}), [size, disabled]);
|
|
1820
|
+
const hasError = Boolean(error);
|
|
1821
|
+
const fallbackGroupId = useId().replace(/:/g, "");
|
|
1822
|
+
const groupId = label ? `radio-group-${label.replace(/\s+/g, "-").toLowerCase()}` : `radio-group-${fallbackGroupId}`;
|
|
1823
|
+
const labelId = label ? `${groupId}-label` : void 0;
|
|
1824
|
+
return /* @__PURE__ */ jsx(RadioGroupContext.Provider, {
|
|
1825
|
+
value: ctxValue,
|
|
1826
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1827
|
+
className: cn("flex flex-col gap-1.5", className),
|
|
1828
|
+
"data-opt-id": "2F20H",
|
|
1829
|
+
"data-opt-slug": "radio-group.RadioGroup",
|
|
1830
|
+
children: [/* @__PURE__ */ jsxs(RadioGroupRoot, {
|
|
1831
|
+
value,
|
|
1832
|
+
defaultValue,
|
|
1833
|
+
onChange,
|
|
1834
|
+
disabled,
|
|
1835
|
+
orientation,
|
|
1836
|
+
name,
|
|
1837
|
+
id: groupId,
|
|
1838
|
+
"aria-label": label ? void 0 : "Radio group",
|
|
1839
|
+
"aria-labelledby": labelId,
|
|
1840
|
+
"aria-required": required,
|
|
1841
|
+
"aria-invalid": hasError,
|
|
1842
|
+
"aria-describedby": getDescribedBy(groupId, error, void 0),
|
|
1843
|
+
className: cn("flex", orientation === "vertical" ? "flex-col gap-3" : "flex-row gap-6"),
|
|
1844
|
+
children: [label && /* @__PURE__ */ jsxs("div", {
|
|
1845
|
+
id: labelId,
|
|
1846
|
+
className: "text-text-secondary mb-1 text-sm font-medium",
|
|
1847
|
+
children: [label, required && /* @__PURE__ */ jsx(RequiredMark, {})]
|
|
1848
|
+
}), children]
|
|
1849
|
+
}), /* @__PURE__ */ jsx(FieldMessages, {
|
|
1850
|
+
id: groupId,
|
|
1851
|
+
error
|
|
1852
|
+
})]
|
|
1853
|
+
})
|
|
1854
|
+
});
|
|
1855
|
+
}
|
|
1856
|
+
const Radio = forwardRef((_ref, ref) => {
|
|
1857
|
+
var _ref2;
|
|
1858
|
+
let { label, hint, description, value, name: _name, size: sizeProp, disabled: disabledProp, className } = _ref, props = _objectWithoutProperties(_ref, _excluded$5);
|
|
1859
|
+
const groupContext = useContext(RadioGroupContext);
|
|
1860
|
+
const primContext = useRadioGroupContext();
|
|
1861
|
+
const size = sizeProp !== null && sizeProp !== void 0 ? sizeProp : groupContext.size;
|
|
1862
|
+
const disabled = (_ref2 = disabledProp !== null && disabledProp !== void 0 ? disabledProp : groupContext.disabled) !== null && _ref2 !== void 0 ? _ref2 : primContext === null || primContext === void 0 ? void 0 : primContext.disabled;
|
|
1863
|
+
const hintText = hint !== null && hint !== void 0 ? hint : description;
|
|
1864
|
+
const styles = sizeStyles[size];
|
|
1865
|
+
const isChecked = (primContext === null || primContext === void 0 ? void 0 : primContext.value) === value;
|
|
1866
|
+
const radioId = `radio-${useId().replace(/:/g, "")}`;
|
|
1867
|
+
const labelId = label ? `${radioId}-label` : void 0;
|
|
1868
|
+
const hintId = hintText ? `${radioId}-hint` : void 0;
|
|
1869
|
+
const handleChange = useCallback(() => {
|
|
1870
|
+
if (!disabled && value != null) primContext === null || primContext === void 0 || primContext.setValue(value);
|
|
1871
|
+
}, [
|
|
1872
|
+
disabled,
|
|
1873
|
+
value,
|
|
1874
|
+
primContext
|
|
1875
|
+
]);
|
|
1876
|
+
return /* @__PURE__ */ jsxs("label", _objectSpread2(_objectSpread2({
|
|
1877
|
+
ref,
|
|
1878
|
+
"aria-checked": isChecked,
|
|
1879
|
+
className: cn("group flex items-start gap-3", disabled && "cursor-not-allowed opacity-50", !disabled && "cursor-pointer", className)
|
|
1880
|
+
}, props), {}, {
|
|
1881
|
+
"data-opt-id": "HY7YG",
|
|
1882
|
+
"data-opt-slug": "radio-group.Radio",
|
|
1883
|
+
children: [
|
|
1884
|
+
/* @__PURE__ */ jsx("input", {
|
|
1885
|
+
id: radioId,
|
|
1886
|
+
type: "radio",
|
|
1887
|
+
name: primContext === null || primContext === void 0 ? void 0 : primContext.name,
|
|
1888
|
+
value,
|
|
1889
|
+
checked: isChecked,
|
|
1890
|
+
disabled,
|
|
1891
|
+
onChange: handleChange,
|
|
1892
|
+
"aria-label": label ? void 0 : value !== null && value !== void 0 ? value : "Radio option",
|
|
1893
|
+
"aria-labelledby": labelId,
|
|
1894
|
+
"aria-describedby": hintId,
|
|
1895
|
+
className: "sr-only"
|
|
1896
|
+
}),
|
|
1897
|
+
/* @__PURE__ */ jsx("span", {
|
|
1898
|
+
className: cn("relative mt-0.5 inline-flex shrink-0 items-center justify-center rounded-full", styles.radio, "border-border border-2", "bg-surface", "group-aria-checked:border-accent", "transition-colors", "group-has-[:focus-visible]:outline-ring outline-transparent group-has-[:focus-visible]:outline-2 group-has-[:focus-visible]:-outline-offset-1"),
|
|
1899
|
+
"aria-hidden": "true",
|
|
1900
|
+
children: /* @__PURE__ */ jsx("span", { className: cn(styles.dot, "bg-accent rounded-full", "scale-0 opacity-0 transition-all", "group-aria-checked:scale-100 group-aria-checked:opacity-100") })
|
|
1901
|
+
}),
|
|
1902
|
+
(label || hintText) && /* @__PURE__ */ jsxs("div", {
|
|
1903
|
+
className: "flex flex-col",
|
|
1904
|
+
children: [label && /* @__PURE__ */ jsx("span", {
|
|
1905
|
+
id: labelId,
|
|
1906
|
+
className: cn("text-text-primary font-medium", styles.text),
|
|
1907
|
+
children: label
|
|
1908
|
+
}), hintText && /* @__PURE__ */ jsx("span", {
|
|
1909
|
+
id: hintId,
|
|
1910
|
+
className: "text-text-tertiary text-sm",
|
|
1911
|
+
children: hintText
|
|
1912
|
+
})]
|
|
1913
|
+
})
|
|
1914
|
+
]
|
|
1915
|
+
}));
|
|
1916
|
+
});
|
|
1917
|
+
Radio.displayName = "Radio";
|
|
1918
|
+
//#endregion
|
|
1919
|
+
//#region src/core/loading-overlay.tsx
|
|
1920
|
+
const _excluded$4 = [
|
|
1921
|
+
"loading",
|
|
1922
|
+
"children",
|
|
1923
|
+
"label",
|
|
1924
|
+
"className"
|
|
1925
|
+
];
|
|
1926
|
+
/** Renders the loading overlay component. */
|
|
1927
|
+
function LoadingOverlay(_ref) {
|
|
1928
|
+
let { loading, children, label = "Loading…", className } = _ref, props = _objectWithoutProperties(_ref, _excluded$4);
|
|
1929
|
+
return /* @__PURE__ */ jsxs("div", _objectSpread2(_objectSpread2({
|
|
1930
|
+
"data-opt-id": "380ZR",
|
|
1931
|
+
"data-opt-slug": "loading-overlay.LoadingOverlay"
|
|
1932
|
+
}, props), {}, {
|
|
1933
|
+
className: cn("relative", className),
|
|
1934
|
+
children: [children, loading && /* @__PURE__ */ jsx("div", {
|
|
1935
|
+
className: "bg-surface/60 absolute inset-0 z-10 flex items-center justify-center rounded-lg backdrop-blur-[1px]",
|
|
1936
|
+
role: "status",
|
|
1937
|
+
"aria-live": "polite",
|
|
1938
|
+
"aria-label": label,
|
|
1939
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1940
|
+
className: "flex flex-col items-center gap-2",
|
|
1941
|
+
children: [/* @__PURE__ */ jsx(Spinner, {
|
|
1942
|
+
size: "md",
|
|
1943
|
+
"aria-hidden": "true"
|
|
1944
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1945
|
+
className: "text-text-tertiary text-sm",
|
|
1946
|
+
children: label
|
|
1947
|
+
})]
|
|
1948
|
+
})
|
|
1949
|
+
})]
|
|
1950
|
+
}));
|
|
1951
|
+
}
|
|
1952
|
+
LoadingOverlay.displayName = "LoadingOverlay";
|
|
1953
|
+
//#endregion
|
|
1954
|
+
//#region src/core/block-layout.tsx
|
|
1955
|
+
const _excluded$3 = [
|
|
1956
|
+
"children",
|
|
1957
|
+
"className",
|
|
1958
|
+
"spacing",
|
|
1959
|
+
"inset",
|
|
1960
|
+
"loading",
|
|
1961
|
+
"loadingLabel"
|
|
1962
|
+
];
|
|
1963
|
+
const spacingClass = {
|
|
1964
|
+
section: "gap-section",
|
|
1965
|
+
group: "gap-group",
|
|
1966
|
+
element: "gap-element",
|
|
1967
|
+
none: "gap-0"
|
|
1968
|
+
};
|
|
1969
|
+
const insetClass = {
|
|
1970
|
+
section: "p-section",
|
|
1971
|
+
group: "p-group",
|
|
1972
|
+
element: "p-element",
|
|
1973
|
+
none: "p-0"
|
|
1974
|
+
};
|
|
1975
|
+
/** Renders the block layout component. */
|
|
1976
|
+
function BlockLayout(_ref) {
|
|
1977
|
+
let { children, className, spacing = "section", inset, loading, loadingLabel } = _ref, props = _objectWithoutProperties(_ref, _excluded$3);
|
|
1978
|
+
const layoutClassName = cn("flex flex-col", spacingClass[spacing], inset ? insetClass[inset] : void 0, className);
|
|
1979
|
+
if (loading) return /* @__PURE__ */ jsx(LoadingOverlay, _objectSpread2(_objectSpread2({
|
|
1980
|
+
"data-opt-id": "9D8QR",
|
|
1981
|
+
"data-opt-slug": "block-layout.BlockLayout"
|
|
1982
|
+
}, props), {}, {
|
|
1983
|
+
className: layoutClassName,
|
|
1984
|
+
loading,
|
|
1985
|
+
label: loadingLabel,
|
|
1986
|
+
children
|
|
1987
|
+
}));
|
|
1988
|
+
return /* @__PURE__ */ jsx("div", _objectSpread2(_objectSpread2({
|
|
1989
|
+
"data-opt-id": "9D8QR",
|
|
1990
|
+
"data-opt-slug": "block-layout.BlockLayout"
|
|
1991
|
+
}, props), {}, {
|
|
1992
|
+
className: layoutClassName,
|
|
1993
|
+
children
|
|
1994
|
+
}));
|
|
1995
|
+
}
|
|
1996
|
+
BlockLayout.displayName = "BlockLayout";
|
|
1997
|
+
//#endregion
|
|
1998
|
+
//#region src/core/create-block.tsx
|
|
1999
|
+
const _excluded$2 = ["loading", "className"];
|
|
2000
|
+
/**
|
|
2001
|
+
* Block 팩토리. BlockLayout 래핑을 구조적으로 강제합니다.
|
|
2002
|
+
*
|
|
2003
|
+
* @param name - displayName으로 사용할 컴포넌트 이름
|
|
2004
|
+
* @param render - 나머지 props를 받아 콘텐츠를 반환하는 렌더 함수
|
|
2005
|
+
* @param options - layoutClassName: BlockLayout에 전달할 추가 className
|
|
2006
|
+
*/
|
|
2007
|
+
function createBlock(name, render, options) {
|
|
2008
|
+
const Component = (props) => {
|
|
2009
|
+
const { loading, className } = props, rest = _objectWithoutProperties(props, _excluded$2);
|
|
2010
|
+
const layoutCn = (options === null || options === void 0 ? void 0 : options.layoutClassName) ? `${options.layoutClassName}${className ? ` ${className}` : ""}` : className;
|
|
2011
|
+
return /* @__PURE__ */ jsx(BlockLayout, {
|
|
2012
|
+
loading,
|
|
2013
|
+
className: layoutCn,
|
|
2014
|
+
children: render(rest)
|
|
2015
|
+
});
|
|
2016
|
+
};
|
|
2017
|
+
Component.displayName = name;
|
|
2018
|
+
return Component;
|
|
2019
|
+
}
|
|
2020
|
+
//#endregion
|
|
2021
|
+
//#region src/core/page-header.tsx
|
|
2022
|
+
/** Renders the page header component. */
|
|
2023
|
+
function PageHeader({ title, description, actions, className }) {
|
|
2024
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2025
|
+
className: cn("gap-group flex flex-col items-stretch sm:flex-row sm:items-center sm:justify-between", className),
|
|
2026
|
+
"data-opt-id": "7AK8Z",
|
|
2027
|
+
"data-opt-slug": "page-header.PageHeader",
|
|
2028
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
2029
|
+
className: "gap-element flex min-w-0 flex-col",
|
|
2030
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
2031
|
+
className: "text-text-primary text-lg font-semibold",
|
|
2032
|
+
children: title
|
|
2033
|
+
}), description && /* @__PURE__ */ jsx("p", {
|
|
2034
|
+
className: "text-text-tertiary text-sm",
|
|
2035
|
+
children: description
|
|
2036
|
+
})]
|
|
2037
|
+
}), actions && /* @__PURE__ */ jsx("div", {
|
|
2038
|
+
className: "gap-element flex w-full shrink-0 items-center sm:w-auto",
|
|
2039
|
+
children: actions
|
|
2040
|
+
})]
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
2043
|
+
PageHeader.displayName = "PageHeader";
|
|
2044
|
+
//#endregion
|
|
2045
|
+
//#region src/core/scroll-area.tsx
|
|
2046
|
+
const overflowMap = {
|
|
2047
|
+
vertical: "overflow-y-auto overflow-x-hidden",
|
|
2048
|
+
horizontal: "overflow-x-auto overflow-y-hidden",
|
|
2049
|
+
both: "overflow-auto"
|
|
2050
|
+
};
|
|
2051
|
+
/** Renders the scroll area component. */
|
|
2052
|
+
function ScrollArea({ children, className, orientation = "vertical" }) {
|
|
2053
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2054
|
+
className: cn(overflowMap[orientation], "scrollbar-thin scrollbar-track-transparent scrollbar-thumb-border hover:scrollbar-thumb-border-hover", className),
|
|
2055
|
+
"data-opt-id": "A9X4K",
|
|
2056
|
+
"data-opt-slug": "scroll-area.ScrollArea",
|
|
2057
|
+
children
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
2060
|
+
ScrollArea.displayName = "ScrollArea";
|
|
2061
|
+
//#endregion
|
|
2062
|
+
//#region src/core/container.tsx
|
|
2063
|
+
const maxWidthMap = {
|
|
2064
|
+
sm: "max-w-screen-sm",
|
|
2065
|
+
md: "max-w-screen-md",
|
|
2066
|
+
lg: "max-w-screen-lg",
|
|
2067
|
+
xl: "max-w-screen-xl",
|
|
2068
|
+
"2xl": "max-w-screen-2xl",
|
|
2069
|
+
full: "max-w-full"
|
|
2070
|
+
};
|
|
2071
|
+
/** Renders the container component. */
|
|
2072
|
+
function Container({ children, maxWidth = "xl", center = true, className }) {
|
|
2073
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2074
|
+
className: cn(maxWidthMap[maxWidth], center && "mx-auto", "px-4", className),
|
|
2075
|
+
"data-opt-id": "H3G96",
|
|
2076
|
+
"data-opt-slug": "container.Container",
|
|
2077
|
+
children
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
Container.displayName = "Container";
|
|
2081
|
+
//#endregion
|
|
2082
|
+
//#region src/core/section.tsx
|
|
2083
|
+
/** Renders the section component. */
|
|
2084
|
+
function Section({ children, title, description, className }) {
|
|
2085
|
+
const titleId = `${useId()}-title`;
|
|
2086
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
2087
|
+
className: cn("gap-group flex flex-col", className),
|
|
2088
|
+
"aria-labelledby": title ? titleId : void 0,
|
|
2089
|
+
"data-opt-id": "M3QXW",
|
|
2090
|
+
"data-opt-slug": "section.Section",
|
|
2091
|
+
children: [(title || description) && /* @__PURE__ */ jsxs("div", {
|
|
2092
|
+
className: "gap-element flex flex-col",
|
|
2093
|
+
children: [title && /* @__PURE__ */ jsx("h3", {
|
|
2094
|
+
id: titleId,
|
|
2095
|
+
className: "text-text-primary text-base font-semibold",
|
|
2096
|
+
children: title
|
|
2097
|
+
}), description && /* @__PURE__ */ jsx("p", {
|
|
2098
|
+
className: "text-text-tertiary text-sm",
|
|
2099
|
+
children: description
|
|
2100
|
+
})]
|
|
2101
|
+
}), children]
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
Section.displayName = "Section";
|
|
2105
|
+
//#endregion
|
|
2106
|
+
//#region src/core/resizable.tsx
|
|
2107
|
+
const _excluded$1 = [
|
|
2108
|
+
"direction",
|
|
2109
|
+
"children",
|
|
2110
|
+
"className",
|
|
2111
|
+
"id",
|
|
2112
|
+
"autoSaveId"
|
|
2113
|
+
];
|
|
2114
|
+
const _excluded2$1 = [
|
|
2115
|
+
"collapsedSize",
|
|
2116
|
+
"defaultSize",
|
|
2117
|
+
"minSize",
|
|
2118
|
+
"maxSize",
|
|
2119
|
+
"children",
|
|
2120
|
+
"className"
|
|
2121
|
+
];
|
|
2122
|
+
const _excluded3 = ["className", "withHandle"];
|
|
2123
|
+
function toPanelSize(size) {
|
|
2124
|
+
return typeof size === "number" ? `${size}%` : size;
|
|
2125
|
+
}
|
|
2126
|
+
/** Renders the resizable panel group component. */
|
|
2127
|
+
function ResizablePanelGroup(_ref) {
|
|
2128
|
+
let { direction, children, className, id, autoSaveId } = _ref, props = _objectWithoutProperties(_ref, _excluded$1);
|
|
2129
|
+
return /* @__PURE__ */ jsx(Group, _objectSpread2(_objectSpread2({
|
|
2130
|
+
orientation: direction,
|
|
2131
|
+
id: id !== null && id !== void 0 ? id : autoSaveId,
|
|
2132
|
+
className: cn("flex h-full w-full", className)
|
|
2133
|
+
}, props), {}, {
|
|
2134
|
+
"data-opt-id": "BM5RR",
|
|
2135
|
+
"data-opt-slug": "resizable.ResizablePanelGroup",
|
|
2136
|
+
children
|
|
2137
|
+
}));
|
|
2138
|
+
}
|
|
2139
|
+
ResizablePanelGroup.displayName = "ResizablePanelGroup";
|
|
2140
|
+
/** Renders the resizable panel component. */
|
|
2141
|
+
function ResizablePanel(_ref2) {
|
|
2142
|
+
let { collapsedSize, defaultSize, minSize, maxSize, children, className } = _ref2, props = _objectWithoutProperties(_ref2, _excluded2$1);
|
|
2143
|
+
return /* @__PURE__ */ jsx(Panel, _objectSpread2(_objectSpread2({
|
|
2144
|
+
collapsedSize: toPanelSize(collapsedSize),
|
|
2145
|
+
defaultSize: toPanelSize(defaultSize),
|
|
2146
|
+
minSize: toPanelSize(minSize),
|
|
2147
|
+
maxSize: toPanelSize(maxSize),
|
|
2148
|
+
className: cn("overflow-auto", className)
|
|
2149
|
+
}, props), {}, {
|
|
2150
|
+
"data-opt-id": "88X4V",
|
|
2151
|
+
"data-opt-slug": "resizable.ResizablePanel",
|
|
2152
|
+
children
|
|
2153
|
+
}));
|
|
2154
|
+
}
|
|
2155
|
+
ResizablePanel.displayName = "ResizablePanel";
|
|
2156
|
+
/** Renders the resizable handle component. */
|
|
2157
|
+
function ResizableHandle(_ref3) {
|
|
2158
|
+
let { className, withHandle = false } = _ref3, props = _objectWithoutProperties(_ref3, _excluded3);
|
|
2159
|
+
return /* @__PURE__ */ jsx(Separator$1, _objectSpread2(_objectSpread2({ className: cn("bg-border relative flex items-center justify-center", "after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2", "data-[resize-handle-active]:bg-accent", "[&[data-orientation=vertical]]:h-px [&[data-orientation=vertical]]:w-full", "[&[data-orientation=horizontal]]:h-full [&[data-orientation=horizontal]]:w-px", className) }, props), {}, {
|
|
2160
|
+
"data-opt-id": "6Z22W",
|
|
2161
|
+
"data-opt-slug": "resizable.ResizableHandle",
|
|
2162
|
+
children: withHandle && /* @__PURE__ */ jsx("div", {
|
|
2163
|
+
className: "border-border bg-surface z-10 flex h-4 w-3 items-center justify-center rounded-sm border",
|
|
2164
|
+
children: /* @__PURE__ */ jsx(GripVertical, {
|
|
2165
|
+
"aria-hidden": "true",
|
|
2166
|
+
className: "text-text-tertiary h-3 w-3"
|
|
2167
|
+
})
|
|
2168
|
+
})
|
|
2169
|
+
}));
|
|
2170
|
+
}
|
|
2171
|
+
ResizableHandle.displayName = "ResizableHandle";
|
|
2172
|
+
//#endregion
|
|
2173
|
+
//#region src/core/logo.tsx
|
|
2174
|
+
const markSizeStyles = {
|
|
2175
|
+
sm: "h-6 w-6",
|
|
2176
|
+
md: "h-8 w-8",
|
|
2177
|
+
lg: "h-10 w-10"
|
|
2178
|
+
};
|
|
2179
|
+
const textSizeStyles = {
|
|
2180
|
+
sm: "text-sm",
|
|
2181
|
+
md: "text-base",
|
|
2182
|
+
lg: "text-lg"
|
|
2183
|
+
};
|
|
2184
|
+
/** Renders the logo component. */
|
|
2185
|
+
function Logo({ src, icon, text, alt = "Logo", size = "md", href, onClick, className }) {
|
|
2186
|
+
const [imageError, setImageError] = useState(false);
|
|
2187
|
+
const showImage = src && !imageError;
|
|
2188
|
+
const fallbackLetter = text ? text.charAt(0).toUpperCase() : null;
|
|
2189
|
+
let mark = null;
|
|
2190
|
+
if (showImage) mark = /* @__PURE__ */ jsx("img", {
|
|
2191
|
+
src,
|
|
2192
|
+
alt,
|
|
2193
|
+
className: cn("object-contain", markSizeStyles[size]),
|
|
2194
|
+
onError: () => setImageError(true)
|
|
2195
|
+
});
|
|
2196
|
+
else if (icon) mark = /* @__PURE__ */ jsx("span", {
|
|
2197
|
+
className: cn("flex shrink-0 items-center justify-center", markSizeStyles[size]),
|
|
2198
|
+
"aria-hidden": "true",
|
|
2199
|
+
children: icon
|
|
2200
|
+
});
|
|
2201
|
+
else if (fallbackLetter) mark = /* @__PURE__ */ jsx("span", {
|
|
2202
|
+
className: cn("bg-accent text-on-accent flex shrink-0 items-center justify-center rounded-lg font-bold", markSizeStyles[size], textSizeStyles[size]),
|
|
2203
|
+
"aria-hidden": "true",
|
|
2204
|
+
children: fallbackLetter
|
|
2205
|
+
});
|
|
2206
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [mark, text && /* @__PURE__ */ jsx("span", {
|
|
2207
|
+
className: cn("text-text-primary font-semibold", textSizeStyles[size]),
|
|
2208
|
+
children: text
|
|
2209
|
+
})] });
|
|
2210
|
+
const sharedClassName = cn("inline-flex items-center gap-element", className);
|
|
2211
|
+
if (href) return /* @__PURE__ */ jsx("a", {
|
|
2212
|
+
href,
|
|
2213
|
+
onClick,
|
|
2214
|
+
className: cn(sharedClassName, "no-underline"),
|
|
2215
|
+
"aria-label": text !== null && text !== void 0 ? text : alt,
|
|
2216
|
+
"data-opt-id": "R906A",
|
|
2217
|
+
"data-opt-slug": "logo.Logo",
|
|
2218
|
+
children: content
|
|
2219
|
+
});
|
|
2220
|
+
if (onClick) return /* @__PURE__ */ jsx("button", {
|
|
2221
|
+
type: "button",
|
|
2222
|
+
onClick,
|
|
2223
|
+
className: sharedClassName,
|
|
2224
|
+
"aria-label": text !== null && text !== void 0 ? text : alt,
|
|
2225
|
+
"data-opt-id": "R906A",
|
|
2226
|
+
"data-opt-slug": "logo.Logo",
|
|
2227
|
+
children: content
|
|
2228
|
+
});
|
|
2229
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2230
|
+
className: sharedClassName,
|
|
2231
|
+
"data-opt-id": "R906A",
|
|
2232
|
+
"data-opt-slug": "logo.Logo",
|
|
2233
|
+
children: content
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
//#endregion
|
|
2237
|
+
//#region src/core/drawer.tsx
|
|
2238
|
+
const _excluded = [
|
|
2239
|
+
"className",
|
|
2240
|
+
"side",
|
|
2241
|
+
"size",
|
|
2242
|
+
"swipeToDismiss",
|
|
2243
|
+
"swipeDismissThreshold",
|
|
2244
|
+
"style",
|
|
2245
|
+
"onPointerDown"
|
|
2246
|
+
];
|
|
2247
|
+
const _excluded2 = ["className"];
|
|
2248
|
+
const sizeMap = {
|
|
2249
|
+
sm: "max-w-xs",
|
|
2250
|
+
md: "max-w-md",
|
|
2251
|
+
lg: "max-w-xl",
|
|
2252
|
+
full: "max-w-full"
|
|
2253
|
+
};
|
|
2254
|
+
const heightMap = {
|
|
2255
|
+
sm: "max-h-[320px]",
|
|
2256
|
+
md: "max-h-[480px]",
|
|
2257
|
+
lg: "max-h-[640px]",
|
|
2258
|
+
full: "max-h-full"
|
|
2259
|
+
};
|
|
2260
|
+
const positionClass = {
|
|
2261
|
+
right: "fixed inset-y-0 right-0",
|
|
2262
|
+
left: "fixed inset-y-0 left-0",
|
|
2263
|
+
bottom: "fixed inset-x-0 bottom-0"
|
|
2264
|
+
};
|
|
2265
|
+
/** Renders the drawer root component. */
|
|
2266
|
+
function DrawerRoot(props) {
|
|
2267
|
+
return /* @__PURE__ */ jsx(DialogRoot, _objectSpread2(_objectSpread2({ animated: true }, props), {}, {
|
|
2268
|
+
"data-opt-id": "YGJ0M",
|
|
2269
|
+
"data-opt-slug": "drawer.DrawerRoot"
|
|
2270
|
+
}));
|
|
2271
|
+
}
|
|
2272
|
+
/** Maps a drag delta to the dismiss offset for a given side (0 = no progress). */
|
|
2273
|
+
function dismissOffset(side, dx, dy) {
|
|
2274
|
+
switch (side) {
|
|
2275
|
+
case "right": return Math.max(0, dx);
|
|
2276
|
+
case "left": return Math.min(0, dx);
|
|
2277
|
+
case "bottom": return Math.max(0, dy);
|
|
2278
|
+
default: return 0;
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
/** Renders the drawer panel component. */
|
|
2282
|
+
function DrawerPanel(_ref) {
|
|
2283
|
+
let { className, side = "right", size = "md", swipeToDismiss = true, swipeDismissThreshold = 80, style, onPointerDown } = _ref, props = _objectWithoutProperties(_ref, _excluded);
|
|
2284
|
+
const isHorizontal = side === "left" || side === "right";
|
|
2285
|
+
const close = useDialogClose();
|
|
2286
|
+
const dragStart = useRef(null);
|
|
2287
|
+
const [offset, setOffset] = useState(0);
|
|
2288
|
+
const [dragging, setDragging] = useState(false);
|
|
2289
|
+
const axis = side === "bottom" ? "Y" : "X";
|
|
2290
|
+
const DRAG_ACTIVATE = 6;
|
|
2291
|
+
const handlePointerDown = useCallback((e) => {
|
|
2292
|
+
onPointerDown === null || onPointerDown === void 0 || onPointerDown(e);
|
|
2293
|
+
if (!swipeToDismiss || e.button !== 0) return;
|
|
2294
|
+
dragStart.current = {
|
|
2295
|
+
x: e.clientX,
|
|
2296
|
+
y: e.clientY
|
|
2297
|
+
};
|
|
2298
|
+
}, [swipeToDismiss, onPointerDown]);
|
|
2299
|
+
const handlePointerMove = useCallback((e) => {
|
|
2300
|
+
const start = dragStart.current;
|
|
2301
|
+
if (!start) return;
|
|
2302
|
+
const next = dismissOffset(side, e.clientX - start.x, e.clientY - start.y);
|
|
2303
|
+
if (!dragging) {
|
|
2304
|
+
if (Math.abs(next) < DRAG_ACTIVATE) return;
|
|
2305
|
+
setDragging(true);
|
|
2306
|
+
try {
|
|
2307
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
2308
|
+
} catch (_unused) {}
|
|
2309
|
+
}
|
|
2310
|
+
setOffset(next);
|
|
2311
|
+
}, [side, dragging]);
|
|
2312
|
+
const endDrag = useCallback((e) => {
|
|
2313
|
+
const wasDragging = dragging;
|
|
2314
|
+
dragStart.current = null;
|
|
2315
|
+
setDragging(false);
|
|
2316
|
+
try {
|
|
2317
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
2318
|
+
} catch (_unused2) {}
|
|
2319
|
+
if (wasDragging && Math.abs(offset) >= swipeDismissThreshold) close();
|
|
2320
|
+
setOffset(0);
|
|
2321
|
+
}, [
|
|
2322
|
+
dragging,
|
|
2323
|
+
offset,
|
|
2324
|
+
swipeDismissThreshold,
|
|
2325
|
+
close
|
|
2326
|
+
]);
|
|
2327
|
+
const [keyboardInset, setKeyboardInset] = useState(0);
|
|
2328
|
+
useEffect(() => {
|
|
2329
|
+
if (side !== "bottom" || typeof window === "undefined") return;
|
|
2330
|
+
const vv = window.visualViewport;
|
|
2331
|
+
if (!vv) return;
|
|
2332
|
+
const onResize = () => {
|
|
2333
|
+
const inset = Math.max(0, window.innerHeight - vv.height - vv.offsetTop);
|
|
2334
|
+
setKeyboardInset(inset);
|
|
2335
|
+
};
|
|
2336
|
+
onResize();
|
|
2337
|
+
vv.addEventListener("resize", onResize);
|
|
2338
|
+
vv.addEventListener("scroll", onResize);
|
|
2339
|
+
return () => {
|
|
2340
|
+
vv.removeEventListener("resize", onResize);
|
|
2341
|
+
vv.removeEventListener("scroll", onResize);
|
|
2342
|
+
};
|
|
2343
|
+
}, [side]);
|
|
2344
|
+
const dragStyle = offset !== 0 ? { transform: `translate${axis}(${offset}px)` } : void 0;
|
|
2345
|
+
return /* @__PURE__ */ jsx(DialogPanel, _objectSpread2(_objectSpread2({
|
|
2346
|
+
className: cn("border-border bg-surface z-50 m-0 flex flex-col border p-0 shadow-2xl", "data-[enter]:fade-in data-[leave]:fade-out", dragging ? "touch-none" : OPT_ANIMATE_DRAWER[side], positionClass[side], isHorizontal ? ["h-full w-full", sizeMap[size]] : ["w-full", heightMap[size]], className),
|
|
2347
|
+
style: _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({}, style), dragStyle), keyboardInset > 0 ? { marginBottom: keyboardInset } : null), dragging ? { transition: "none" } : null),
|
|
2348
|
+
onPointerDown: handlePointerDown,
|
|
2349
|
+
onPointerMove: swipeToDismiss ? handlePointerMove : void 0,
|
|
2350
|
+
onPointerUp: swipeToDismiss ? endDrag : void 0,
|
|
2351
|
+
onPointerCancel: swipeToDismiss ? endDrag : void 0
|
|
2352
|
+
}, props), {}, {
|
|
2353
|
+
"data-opt-id": "RQERV",
|
|
2354
|
+
"data-opt-slug": "drawer.DrawerPanel"
|
|
2355
|
+
}));
|
|
2356
|
+
}
|
|
2357
|
+
/** Renders the drawer close component. */
|
|
2358
|
+
function DrawerClose(_ref2) {
|
|
2359
|
+
let { className } = _ref2, props = _objectWithoutProperties(_ref2, _excluded2);
|
|
2360
|
+
return /* @__PURE__ */ jsx(DialogDismiss, _objectSpread2(_objectSpread2({ className: cn("hover:bg-bg-subtle rounded-md p-1.5 transition-colors", "cursor-pointer data-[disabled]:cursor-not-allowed", OPT_FOCUS, className) }, props), {}, {
|
|
2361
|
+
"data-opt-id": "M6D6D",
|
|
2362
|
+
"data-opt-slug": "drawer.DrawerClose"
|
|
2363
|
+
}));
|
|
2364
|
+
}
|
|
2365
|
+
//#endregion
|
|
2366
|
+
//#region src/core/virtual-list.tsx
|
|
2367
|
+
/** Renders the virtual list component. */
|
|
2368
|
+
function VirtualList({ items, renderItem, itemHeight, overscan = 3, className, style }) {
|
|
2369
|
+
const containerRef = useRef(null);
|
|
2370
|
+
const scrollTopRef = useRef(0);
|
|
2371
|
+
const containerHeightRef = useRef(0);
|
|
2372
|
+
const rafIdRef = useRef(0);
|
|
2373
|
+
const [visibleRange, setVisibleRange] = useState({
|
|
2374
|
+
start: 0,
|
|
2375
|
+
end: 0
|
|
2376
|
+
});
|
|
2377
|
+
const totalHeight = items.length * itemHeight;
|
|
2378
|
+
const computeRange = useCallback((scrollTop, height) => {
|
|
2379
|
+
const start = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
|
|
2380
|
+
const visibleCount = Math.ceil(height / itemHeight);
|
|
2381
|
+
return {
|
|
2382
|
+
start,
|
|
2383
|
+
end: Math.min(items.length - 1, start + visibleCount + overscan * 2)
|
|
2384
|
+
};
|
|
2385
|
+
}, [
|
|
2386
|
+
itemHeight,
|
|
2387
|
+
overscan,
|
|
2388
|
+
items.length
|
|
2389
|
+
]);
|
|
2390
|
+
const scheduleUpdate = useCallback(() => {
|
|
2391
|
+
if (rafIdRef.current) return;
|
|
2392
|
+
rafIdRef.current = requestAnimationFrame(() => {
|
|
2393
|
+
rafIdRef.current = 0;
|
|
2394
|
+
const newRange = computeRange(scrollTopRef.current, containerHeightRef.current);
|
|
2395
|
+
setVisibleRange((prev) => prev.start === newRange.start && prev.end === newRange.end ? prev : newRange);
|
|
2396
|
+
});
|
|
2397
|
+
}, [computeRange]);
|
|
2398
|
+
const handleScroll = useCallback(() => {
|
|
2399
|
+
const el = containerRef.current;
|
|
2400
|
+
if (el) {
|
|
2401
|
+
scrollTopRef.current = el.scrollTop;
|
|
2402
|
+
if (el.clientHeight !== containerHeightRef.current) containerHeightRef.current = el.clientHeight;
|
|
2403
|
+
scheduleUpdate();
|
|
2404
|
+
}
|
|
2405
|
+
}, [scheduleUpdate]);
|
|
2406
|
+
const handleRef = useCallback((el) => {
|
|
2407
|
+
containerRef.current = el;
|
|
2408
|
+
if (el) {
|
|
2409
|
+
containerHeightRef.current = el.clientHeight;
|
|
2410
|
+
const range = computeRange(0, el.clientHeight);
|
|
2411
|
+
setVisibleRange(range);
|
|
2412
|
+
}
|
|
2413
|
+
}, [computeRange]);
|
|
2414
|
+
useEffect(() => {
|
|
2415
|
+
const el = containerRef.current;
|
|
2416
|
+
if (!el || typeof ResizeObserver === "undefined") return;
|
|
2417
|
+
const observer = new ResizeObserver(() => {
|
|
2418
|
+
const nextHeight = el.clientHeight;
|
|
2419
|
+
if (nextHeight !== containerHeightRef.current) {
|
|
2420
|
+
containerHeightRef.current = nextHeight;
|
|
2421
|
+
scheduleUpdate();
|
|
2422
|
+
}
|
|
2423
|
+
});
|
|
2424
|
+
observer.observe(el);
|
|
2425
|
+
return () => {
|
|
2426
|
+
observer.disconnect();
|
|
2427
|
+
};
|
|
2428
|
+
}, [scheduleUpdate]);
|
|
2429
|
+
useEffect(() => {
|
|
2430
|
+
scheduleUpdate();
|
|
2431
|
+
return () => {
|
|
2432
|
+
if (rafIdRef.current) {
|
|
2433
|
+
cancelAnimationFrame(rafIdRef.current);
|
|
2434
|
+
rafIdRef.current = 0;
|
|
2435
|
+
}
|
|
2436
|
+
};
|
|
2437
|
+
}, [
|
|
2438
|
+
scheduleUpdate,
|
|
2439
|
+
items.length,
|
|
2440
|
+
itemHeight,
|
|
2441
|
+
overscan
|
|
2442
|
+
]);
|
|
2443
|
+
const visibleItems = useMemo(() => {
|
|
2444
|
+
const result = [];
|
|
2445
|
+
for (let i = visibleRange.start; i <= visibleRange.end && i < items.length; i++) result.push(/* @__PURE__ */ jsx("div", {
|
|
2446
|
+
role: "listitem",
|
|
2447
|
+
style: {
|
|
2448
|
+
position: "absolute",
|
|
2449
|
+
top: i * itemHeight,
|
|
2450
|
+
left: 0,
|
|
2451
|
+
right: 0,
|
|
2452
|
+
height: itemHeight
|
|
2453
|
+
},
|
|
2454
|
+
children: renderItem(items[i], i)
|
|
2455
|
+
}, i));
|
|
2456
|
+
return result;
|
|
2457
|
+
}, [
|
|
2458
|
+
visibleRange.start,
|
|
2459
|
+
visibleRange.end,
|
|
2460
|
+
items,
|
|
2461
|
+
itemHeight,
|
|
2462
|
+
renderItem
|
|
2463
|
+
]);
|
|
2464
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2465
|
+
ref: handleRef,
|
|
2466
|
+
onScroll: handleScroll,
|
|
2467
|
+
className: cn("overflow-auto", className),
|
|
2468
|
+
style: _objectSpread2({ position: "relative" }, style),
|
|
2469
|
+
role: "list",
|
|
2470
|
+
"data-opt-id": "VT2NP",
|
|
2471
|
+
"data-opt-slug": "virtual-list.VirtualList",
|
|
2472
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2473
|
+
style: {
|
|
2474
|
+
height: totalHeight,
|
|
2475
|
+
position: "relative"
|
|
2476
|
+
},
|
|
2477
|
+
children: visibleItems
|
|
2478
|
+
})
|
|
2479
|
+
});
|
|
2480
|
+
}
|
|
2481
|
+
//#endregion
|
|
2482
|
+
//#region src/core/shader-presets.ts
|
|
2483
|
+
/** 모든 프리셋이 공유하는 풀스크린 정점 셰이더. */
|
|
2484
|
+
const SHADER_VERTEX = `
|
|
2485
|
+
attribute vec2 position;
|
|
2486
|
+
attribute vec2 uv;
|
|
2487
|
+
varying vec2 vUv;
|
|
2488
|
+
void main() {
|
|
2489
|
+
vUv = uv;
|
|
2490
|
+
gl_Position = vec4(position, 0.0, 1.0);
|
|
2491
|
+
}
|
|
2492
|
+
`;
|
|
2493
|
+
/**
|
|
2494
|
+
* 모든 프래그먼트 본문 앞에 붙는 공유 프렐류드.
|
|
2495
|
+
* uniform 선언 + 공통 helper(rotate/value noise/fbm/grain/lux/coords).
|
|
2496
|
+
*/
|
|
2497
|
+
const SHADER_PRELUDE = `
|
|
2498
|
+
precision highp float;
|
|
2499
|
+
varying vec2 vUv;
|
|
2500
|
+
uniform float u_time;
|
|
2501
|
+
uniform vec2 u_resolution;
|
|
2502
|
+
uniform float u_scale;
|
|
2503
|
+
uniform float u_speed;
|
|
2504
|
+
uniform float u_distortion;
|
|
2505
|
+
uniform float u_grain;
|
|
2506
|
+
uniform float u_contrast;
|
|
2507
|
+
uniform float u_angle;
|
|
2508
|
+
uniform float u_dim;
|
|
2509
|
+
uniform float u_vignette;
|
|
2510
|
+
uniform float u_scrim;
|
|
2511
|
+
uniform vec2 u_mouse;
|
|
2512
|
+
uniform float u_interactive;
|
|
2513
|
+
uniform float u_bars;
|
|
2514
|
+
uniform float u_blur;
|
|
2515
|
+
uniform float u_peak;
|
|
2516
|
+
uniform float u_valley;
|
|
2517
|
+
uniform vec3 u_color1;
|
|
2518
|
+
uniform vec3 u_color2;
|
|
2519
|
+
uniform vec3 u_color3;
|
|
2520
|
+
|
|
2521
|
+
const float PI = 3.14159265359;
|
|
2522
|
+
|
|
2523
|
+
mat2 rot(float a){ float c = cos(a), s = sin(a); return mat2(c, -s, s, c); }
|
|
2524
|
+
|
|
2525
|
+
float hash21(vec2 p){
|
|
2526
|
+
p = fract(p * vec2(123.34, 345.45));
|
|
2527
|
+
p += dot(p, p + 34.345);
|
|
2528
|
+
return fract(p.x * p.y);
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
float vnoise(vec2 p){
|
|
2532
|
+
vec2 i = floor(p);
|
|
2533
|
+
vec2 f = fract(p);
|
|
2534
|
+
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
2535
|
+
float a = hash21(i);
|
|
2536
|
+
float b = hash21(i + vec2(1.0, 0.0));
|
|
2537
|
+
float c = hash21(i + vec2(0.0, 1.0));
|
|
2538
|
+
float d = hash21(i + vec2(1.0, 1.0));
|
|
2539
|
+
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
float fbm(vec2 p){
|
|
2543
|
+
float v = 0.0;
|
|
2544
|
+
float amp = 0.5;
|
|
2545
|
+
for (int i = 0; i < 5; i++){
|
|
2546
|
+
v += amp * vnoise(p);
|
|
2547
|
+
p = rot(0.5) * p * 2.0;
|
|
2548
|
+
amp *= 0.5;
|
|
2549
|
+
}
|
|
2550
|
+
return v;
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
// fine film grain — subtle, premium texture
|
|
2554
|
+
vec3 grain(vec3 col, vec2 uv){
|
|
2555
|
+
float g = hash21(uv * u_resolution.xy + fract(u_time));
|
|
2556
|
+
return col + (g - 0.5) * u_grain;
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
// premium finish: gentle desaturation for an understated, refined look.
|
|
2560
|
+
vec3 lux(vec3 col){
|
|
2561
|
+
float l = dot(col, vec3(0.299, 0.587, 0.114));
|
|
2562
|
+
return clamp(mix(vec3(l), col, 0.82), 0.0, 1.0);
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
// shared output finish: refine + legibility overlays (dim / vignette / bottom
|
|
2566
|
+
// scrim) + film grain. Every preset ends with finish(col, vUv).
|
|
2567
|
+
vec3 finish(vec3 col, vec2 uv){
|
|
2568
|
+
col = lux(col);
|
|
2569
|
+
col *= 1.0 - u_dim * 0.85;
|
|
2570
|
+
float edge = length(uv - 0.5);
|
|
2571
|
+
col *= 1.0 - u_vignette * smoothstep(0.35, 0.85, edge);
|
|
2572
|
+
col *= 1.0 - u_scrim * smoothstep(0.2, 1.0, uv.y);
|
|
2573
|
+
col = grain(col, uv);
|
|
2574
|
+
return col;
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
// aspect-correct, angle-rotated, centered coords + pointer parallax
|
|
2578
|
+
vec2 coords(){
|
|
2579
|
+
vec2 uv = vUv - 0.5;
|
|
2580
|
+
uv.x *= u_resolution.x / max(u_resolution.y, 1.0);
|
|
2581
|
+
uv += (u_mouse - 0.5) * u_interactive * 0.3;
|
|
2582
|
+
uv = rot(radians(u_angle)) * uv;
|
|
2583
|
+
return uv;
|
|
2584
|
+
}
|
|
2585
|
+
`;
|
|
2586
|
+
const PARAM_RANGE = {
|
|
2587
|
+
scale: {
|
|
2588
|
+
min: .4,
|
|
2589
|
+
max: 3,
|
|
2590
|
+
step: .1,
|
|
2591
|
+
default: 1
|
|
2592
|
+
},
|
|
2593
|
+
speed: {
|
|
2594
|
+
min: 0,
|
|
2595
|
+
max: 3,
|
|
2596
|
+
step: .1,
|
|
2597
|
+
default: .5
|
|
2598
|
+
},
|
|
2599
|
+
distortion: {
|
|
2600
|
+
min: 0,
|
|
2601
|
+
max: 1,
|
|
2602
|
+
step: .01,
|
|
2603
|
+
default: .4
|
|
2604
|
+
},
|
|
2605
|
+
grain: {
|
|
2606
|
+
min: 0,
|
|
2607
|
+
max: .5,
|
|
2608
|
+
step: .01,
|
|
2609
|
+
default: .04
|
|
2610
|
+
},
|
|
2611
|
+
contrast: {
|
|
2612
|
+
min: 0,
|
|
2613
|
+
max: 1,
|
|
2614
|
+
step: .01,
|
|
2615
|
+
default: .4
|
|
2616
|
+
},
|
|
2617
|
+
angle: {
|
|
2618
|
+
min: 0,
|
|
2619
|
+
max: 360,
|
|
2620
|
+
step: 1,
|
|
2621
|
+
default: 0
|
|
2622
|
+
},
|
|
2623
|
+
dim: {
|
|
2624
|
+
min: 0,
|
|
2625
|
+
max: 1,
|
|
2626
|
+
step: .01,
|
|
2627
|
+
default: 0
|
|
2628
|
+
},
|
|
2629
|
+
vignette: {
|
|
2630
|
+
min: 0,
|
|
2631
|
+
max: 1,
|
|
2632
|
+
step: .01,
|
|
2633
|
+
default: 0
|
|
2634
|
+
},
|
|
2635
|
+
scrim: {
|
|
2636
|
+
min: 0,
|
|
2637
|
+
max: 1,
|
|
2638
|
+
step: .01,
|
|
2639
|
+
default: 0
|
|
2640
|
+
},
|
|
2641
|
+
interactive: {
|
|
2642
|
+
min: 0,
|
|
2643
|
+
max: 1,
|
|
2644
|
+
step: .01,
|
|
2645
|
+
default: 0
|
|
2646
|
+
},
|
|
2647
|
+
bars: {
|
|
2648
|
+
min: 2,
|
|
2649
|
+
max: 40,
|
|
2650
|
+
step: 1,
|
|
2651
|
+
default: 19
|
|
2652
|
+
},
|
|
2653
|
+
blur: {
|
|
2654
|
+
min: 0,
|
|
2655
|
+
max: 1,
|
|
2656
|
+
step: .01,
|
|
2657
|
+
default: .29
|
|
2658
|
+
},
|
|
2659
|
+
peak: {
|
|
2660
|
+
min: 0,
|
|
2661
|
+
max: 1,
|
|
2662
|
+
step: .01,
|
|
2663
|
+
default: .98
|
|
2664
|
+
},
|
|
2665
|
+
valley: {
|
|
2666
|
+
min: 0,
|
|
2667
|
+
max: 1,
|
|
2668
|
+
step: .01,
|
|
2669
|
+
default: .55
|
|
2670
|
+
}
|
|
2671
|
+
};
|
|
2672
|
+
/** 프리셋이 선언하지 않은 파라미터의 전역 기본값 (uniform 폴백). */
|
|
2673
|
+
const SHADER_PARAM_FALLBACK = {
|
|
2674
|
+
scale: PARAM_RANGE.scale.default,
|
|
2675
|
+
speed: PARAM_RANGE.speed.default,
|
|
2676
|
+
distortion: PARAM_RANGE.distortion.default,
|
|
2677
|
+
grain: PARAM_RANGE.grain.default,
|
|
2678
|
+
contrast: PARAM_RANGE.contrast.default,
|
|
2679
|
+
angle: PARAM_RANGE.angle.default,
|
|
2680
|
+
dim: PARAM_RANGE.dim.default,
|
|
2681
|
+
vignette: PARAM_RANGE.vignette.default,
|
|
2682
|
+
scrim: PARAM_RANGE.scrim.default,
|
|
2683
|
+
interactive: PARAM_RANGE.interactive.default,
|
|
2684
|
+
bars: PARAM_RANGE.bars.default,
|
|
2685
|
+
blur: PARAM_RANGE.blur.default,
|
|
2686
|
+
peak: PARAM_RANGE.peak.default,
|
|
2687
|
+
valley: PARAM_RANGE.valley.default
|
|
2688
|
+
};
|
|
2689
|
+
function param(key, defaultValue) {
|
|
2690
|
+
const range = PARAM_RANGE[key];
|
|
2691
|
+
return {
|
|
2692
|
+
key,
|
|
2693
|
+
min: range.min,
|
|
2694
|
+
max: range.max,
|
|
2695
|
+
step: range.step,
|
|
2696
|
+
default: defaultValue !== null && defaultValue !== void 0 ? defaultValue : range.default
|
|
2697
|
+
};
|
|
2698
|
+
}
|
|
2699
|
+
const SHADER_PRESETS = [
|
|
2700
|
+
{
|
|
2701
|
+
id: "felix-metallic",
|
|
2702
|
+
category: "luxury",
|
|
2703
|
+
colorSlots: 3,
|
|
2704
|
+
defaultColors: [
|
|
2705
|
+
"#0a1424",
|
|
2706
|
+
"#3a5a8c",
|
|
2707
|
+
"#c4d4e8"
|
|
2708
|
+
],
|
|
2709
|
+
params: [
|
|
2710
|
+
param("scale"),
|
|
2711
|
+
param("speed", .4),
|
|
2712
|
+
param("distortion", .45),
|
|
2713
|
+
param("contrast", .4),
|
|
2714
|
+
param("grain", .04)
|
|
2715
|
+
],
|
|
2716
|
+
body: `
|
|
2717
|
+
void main(){
|
|
2718
|
+
vec2 uv = coords() * u_scale;
|
|
2719
|
+
float t = u_time * u_speed * 0.3;
|
|
2720
|
+
vec2 q = vec2(fbm(uv + t), fbm(uv - t + 5.2));
|
|
2721
|
+
vec2 r = vec2(
|
|
2722
|
+
fbm(uv + q * 2.0 + vec2(1.7, 9.2) + t),
|
|
2723
|
+
fbm(uv + q * 2.0 + vec2(8.3, 2.8) - t)
|
|
2724
|
+
);
|
|
2725
|
+
float f = fbm(uv + r * (1.0 + u_distortion * 2.0));
|
|
2726
|
+
float band = sin((f + r.x * 0.5) * 6.2831 * 2.0);
|
|
2727
|
+
float spec = pow(abs(band), mix(2.0, 5.0, u_contrast));
|
|
2728
|
+
vec3 col = mix(u_color1, u_color2, smoothstep(0.0, 1.0, f + r.y * 0.4));
|
|
2729
|
+
col = mix(col, u_color3, spec * 0.5);
|
|
2730
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
2731
|
+
}
|
|
2732
|
+
`
|
|
2733
|
+
},
|
|
2734
|
+
{
|
|
2735
|
+
id: "liquid-silk",
|
|
2736
|
+
category: "luxury",
|
|
2737
|
+
colorSlots: 3,
|
|
2738
|
+
defaultColors: [
|
|
2739
|
+
"#1c1428",
|
|
2740
|
+
"#6a5a8c",
|
|
2741
|
+
"#d8cfe4"
|
|
2742
|
+
],
|
|
2743
|
+
params: [
|
|
2744
|
+
param("scale"),
|
|
2745
|
+
param("speed", .4),
|
|
2746
|
+
param("distortion", .4),
|
|
2747
|
+
param("grain", .04),
|
|
2748
|
+
param("angle", 30)
|
|
2749
|
+
],
|
|
2750
|
+
body: `
|
|
2751
|
+
void main(){
|
|
2752
|
+
vec2 uv = coords() * u_scale;
|
|
2753
|
+
float t = u_time * u_speed * 0.25;
|
|
2754
|
+
float warp = fbm(uv * 1.5 + t);
|
|
2755
|
+
float folds = sin((uv.x * 3.0 + uv.y * 1.5 + warp * (2.0 + u_distortion * 4.0)) * PI);
|
|
2756
|
+
float sheen = 0.5 + 0.5 * folds;
|
|
2757
|
+
vec3 col = mix(u_color1, u_color2, sheen);
|
|
2758
|
+
col = mix(col, u_color3, pow(sheen, 5.0) * 0.6);
|
|
2759
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
2760
|
+
}
|
|
2761
|
+
`
|
|
2762
|
+
},
|
|
2763
|
+
{
|
|
2764
|
+
id: "iridescent-pearl",
|
|
2765
|
+
category: "luxury",
|
|
2766
|
+
colorSlots: 3,
|
|
2767
|
+
defaultColors: [
|
|
2768
|
+
"#c9b8d6",
|
|
2769
|
+
"#a8c4d4",
|
|
2770
|
+
"#e4d4dc"
|
|
2771
|
+
],
|
|
2772
|
+
params: [
|
|
2773
|
+
param("scale"),
|
|
2774
|
+
param("speed", .35),
|
|
2775
|
+
param("distortion", .4),
|
|
2776
|
+
param("grain", .04)
|
|
2777
|
+
],
|
|
2778
|
+
body: `
|
|
2779
|
+
vec3 palette3(float t){
|
|
2780
|
+
t = fract(t);
|
|
2781
|
+
float s = t * 3.0;
|
|
2782
|
+
if (s < 1.0) return mix(u_color1, u_color2, smoothstep(0.0, 1.0, s));
|
|
2783
|
+
if (s < 2.0) return mix(u_color2, u_color3, smoothstep(0.0, 1.0, s - 1.0));
|
|
2784
|
+
return mix(u_color3, u_color1, smoothstep(0.0, 1.0, s - 2.0));
|
|
2785
|
+
}
|
|
2786
|
+
void main(){
|
|
2787
|
+
vec2 uv = coords() * u_scale;
|
|
2788
|
+
float t = u_time * u_speed * 0.2;
|
|
2789
|
+
float n = fbm(uv * 1.2 + vec2(t, -t));
|
|
2790
|
+
float thin = fbm(uv * 2.5 - n * (1.0 + u_distortion * 3.0) + t * 0.5);
|
|
2791
|
+
vec3 col = palette3(thin * 1.5 + n * 0.5 + t * 0.1);
|
|
2792
|
+
col += pow(thin, 3.0) * 0.12;
|
|
2793
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
2794
|
+
}
|
|
2795
|
+
`
|
|
2796
|
+
},
|
|
2797
|
+
{
|
|
2798
|
+
id: "molten-gold",
|
|
2799
|
+
category: "luxury",
|
|
2800
|
+
colorSlots: 3,
|
|
2801
|
+
defaultColors: [
|
|
2802
|
+
"#1c1208",
|
|
2803
|
+
"#b08848",
|
|
2804
|
+
"#e8d4a8"
|
|
2805
|
+
],
|
|
2806
|
+
params: [
|
|
2807
|
+
param("scale"),
|
|
2808
|
+
param("speed", .4),
|
|
2809
|
+
param("contrast", .4),
|
|
2810
|
+
param("grain", .05)
|
|
2811
|
+
],
|
|
2812
|
+
body: `
|
|
2813
|
+
void main(){
|
|
2814
|
+
vec2 uv = coords() * u_scale;
|
|
2815
|
+
float t = u_time * u_speed * 0.3;
|
|
2816
|
+
vec2 q = vec2(fbm(uv + t), fbm(uv * 1.3 - t));
|
|
2817
|
+
float f = fbm(uv * 1.5 + q * 2.5);
|
|
2818
|
+
float heat = smoothstep(0.25, 0.95, f);
|
|
2819
|
+
vec3 col = mix(u_color1, u_color2, heat);
|
|
2820
|
+
float hot = pow(smoothstep(0.6, 1.0, f + q.x * 0.3), mix(3.0, 6.0, u_contrast));
|
|
2821
|
+
col = mix(col, u_color3, hot * 0.7);
|
|
2822
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
2823
|
+
}
|
|
2824
|
+
`
|
|
2825
|
+
},
|
|
2826
|
+
{
|
|
2827
|
+
id: "liquid-glass",
|
|
2828
|
+
category: "luxury",
|
|
2829
|
+
colorSlots: 3,
|
|
2830
|
+
defaultColors: [
|
|
2831
|
+
"#0c1c2c",
|
|
2832
|
+
"#3a6a88",
|
|
2833
|
+
"#bcdce4"
|
|
2834
|
+
],
|
|
2835
|
+
params: [
|
|
2836
|
+
param("scale"),
|
|
2837
|
+
param("speed", .4),
|
|
2838
|
+
param("distortion", .4),
|
|
2839
|
+
param("grain", .03)
|
|
2840
|
+
],
|
|
2841
|
+
body: `
|
|
2842
|
+
void main(){
|
|
2843
|
+
vec2 uv = coords() * u_scale;
|
|
2844
|
+
float t = u_time * u_speed * 0.2;
|
|
2845
|
+
vec2 p = uv;
|
|
2846
|
+
for (int i = 1; i < 5; i++){
|
|
2847
|
+
float fi = float(i);
|
|
2848
|
+
p += (1.0 + u_distortion) * vec2(
|
|
2849
|
+
0.3 / fi * sin(fi * p.y + t + 0.3 * fi),
|
|
2850
|
+
0.3 / fi * cos(fi * p.x + t + 0.3 * fi)
|
|
2851
|
+
);
|
|
2852
|
+
}
|
|
2853
|
+
float caustic = 0.5 + 0.5 * sin(p.x * 3.0) * cos(p.y * 3.0);
|
|
2854
|
+
vec3 col = mix(u_color1, u_color2, caustic);
|
|
2855
|
+
col = mix(col, u_color3, pow(caustic, 6.0) * 0.6);
|
|
2856
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
2857
|
+
}
|
|
2858
|
+
`
|
|
2859
|
+
},
|
|
2860
|
+
{
|
|
2861
|
+
id: "aurora-veil",
|
|
2862
|
+
category: "luxury",
|
|
2863
|
+
colorSlots: 3,
|
|
2864
|
+
defaultColors: [
|
|
2865
|
+
"#050d18",
|
|
2866
|
+
"#2a8a8c",
|
|
2867
|
+
"#6a5a9c"
|
|
2868
|
+
],
|
|
2869
|
+
params: [
|
|
2870
|
+
param("scale"),
|
|
2871
|
+
param("speed", .4),
|
|
2872
|
+
param("distortion", .45),
|
|
2873
|
+
param("grain", .03),
|
|
2874
|
+
param("angle", 0)
|
|
2875
|
+
],
|
|
2876
|
+
body: `
|
|
2877
|
+
void main(){
|
|
2878
|
+
vec2 uv = coords() * u_scale;
|
|
2879
|
+
float t = u_time * u_speed * 0.3;
|
|
2880
|
+
vec3 col = u_color1;
|
|
2881
|
+
for (int i = 0; i < 3; i++){
|
|
2882
|
+
float fi = float(i);
|
|
2883
|
+
float band = uv.y * 1.5
|
|
2884
|
+
+ sin(uv.x * 2.0 + t + fi * 1.7) * (0.3 + u_distortion * 0.5)
|
|
2885
|
+
+ fbm(uv * 1.5 + t * 0.5 + fi) * 0.6;
|
|
2886
|
+
float glow = exp(-abs(band) * 3.0);
|
|
2887
|
+
vec3 rib = mix(u_color2, u_color3, fi * 0.5);
|
|
2888
|
+
col += rib * glow * 0.5;
|
|
2889
|
+
}
|
|
2890
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
2891
|
+
}
|
|
2892
|
+
`
|
|
2893
|
+
},
|
|
2894
|
+
{
|
|
2895
|
+
id: "holographic",
|
|
2896
|
+
category: "luxury",
|
|
2897
|
+
colorSlots: 3,
|
|
2898
|
+
defaultColors: [
|
|
2899
|
+
"#14101e",
|
|
2900
|
+
"#8a6a9c",
|
|
2901
|
+
"#7a9caa"
|
|
2902
|
+
],
|
|
2903
|
+
params: [
|
|
2904
|
+
param("scale"),
|
|
2905
|
+
param("speed", .35),
|
|
2906
|
+
param("distortion", .4),
|
|
2907
|
+
param("grain", .03)
|
|
2908
|
+
],
|
|
2909
|
+
body: `
|
|
2910
|
+
vec3 hsv2rgb(vec3 c){
|
|
2911
|
+
vec3 p = abs(fract(c.xxx + vec3(0.0, 2.0 / 3.0, 1.0 / 3.0)) * 6.0 - 3.0);
|
|
2912
|
+
return c.z * mix(vec3(1.0), clamp(p - 1.0, 0.0, 1.0), c.y);
|
|
2913
|
+
}
|
|
2914
|
+
void main(){
|
|
2915
|
+
vec2 uv = coords() * u_scale;
|
|
2916
|
+
float t = u_time * u_speed * 0.2;
|
|
2917
|
+
float n = fbm(uv * 1.5 + t);
|
|
2918
|
+
float ripple = fbm(uv * 3.0 - n * (1.0 + u_distortion * 2.0) + t);
|
|
2919
|
+
float hue = fract(ripple * 1.2 + n * 0.5 + length(uv) * 0.3 + t * 0.1);
|
|
2920
|
+
vec3 holo = hsv2rgb(vec3(hue, 0.3, 0.92));
|
|
2921
|
+
vec3 base = mix(u_color1, u_color2, n);
|
|
2922
|
+
vec3 col = mix(base, holo, 0.35);
|
|
2923
|
+
col = mix(col, u_color3, pow(ripple, 4.0) * 0.5);
|
|
2924
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
2925
|
+
}
|
|
2926
|
+
`
|
|
2927
|
+
},
|
|
2928
|
+
{
|
|
2929
|
+
id: "chrome-ripple",
|
|
2930
|
+
category: "luxury",
|
|
2931
|
+
colorSlots: 3,
|
|
2932
|
+
defaultColors: [
|
|
2933
|
+
"#0e1420",
|
|
2934
|
+
"#6a7a92",
|
|
2935
|
+
"#d4dce4"
|
|
2936
|
+
],
|
|
2937
|
+
params: [
|
|
2938
|
+
param("scale"),
|
|
2939
|
+
param("speed", .4),
|
|
2940
|
+
param("contrast", .4),
|
|
2941
|
+
param("grain", .04)
|
|
2942
|
+
],
|
|
2943
|
+
body: `
|
|
2944
|
+
void main(){
|
|
2945
|
+
vec2 uv = coords() * u_scale;
|
|
2946
|
+
float t = u_time * u_speed * 0.3;
|
|
2947
|
+
vec2 p = uv;
|
|
2948
|
+
for (int i = 1; i < 6; i++){
|
|
2949
|
+
float fi = float(i);
|
|
2950
|
+
p.x += (0.4 / fi) * sin(fi * 1.5 * p.y + t + fi);
|
|
2951
|
+
p.y += (0.4 / fi) * cos(fi * 1.5 * p.x + t + fi);
|
|
2952
|
+
}
|
|
2953
|
+
float sheen = 0.5 + 0.5 * sin(p.x * 3.0 + p.y * 2.0);
|
|
2954
|
+
float spec = pow(sheen, mix(3.0, 6.0, u_contrast));
|
|
2955
|
+
vec3 col = mix(u_color1, u_color2, sheen);
|
|
2956
|
+
col = mix(col, u_color3, spec * 0.5);
|
|
2957
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
2958
|
+
}
|
|
2959
|
+
`
|
|
2960
|
+
},
|
|
2961
|
+
{
|
|
2962
|
+
id: "mesh-flow",
|
|
2963
|
+
category: "flow",
|
|
2964
|
+
colorSlots: 3,
|
|
2965
|
+
defaultColors: [
|
|
2966
|
+
"#3a4a6a",
|
|
2967
|
+
"#6a5a7c",
|
|
2968
|
+
"#4a7a82"
|
|
2969
|
+
],
|
|
2970
|
+
params: [
|
|
2971
|
+
param("scale"),
|
|
2972
|
+
param("speed", .5),
|
|
2973
|
+
param("grain", .04)
|
|
2974
|
+
],
|
|
2975
|
+
body: `
|
|
2976
|
+
void main(){
|
|
2977
|
+
vec2 uv = coords() * u_scale;
|
|
2978
|
+
float t = u_time * u_speed * 0.4;
|
|
2979
|
+
vec2 p1 = vec2(sin(t * 0.7), cos(t * 0.9)) * 0.5;
|
|
2980
|
+
vec2 p2 = vec2(cos(t * 0.8 + 1.0), sin(t * 0.6 + 2.0)) * 0.5;
|
|
2981
|
+
vec2 p3 = vec2(sin(t * 0.5 + 3.0), cos(t * 0.7 + 4.0)) * 0.5;
|
|
2982
|
+
float d1 = 1.0 / (0.15 + dot(uv - p1, uv - p1));
|
|
2983
|
+
float d2 = 1.0 / (0.15 + dot(uv - p2, uv - p2));
|
|
2984
|
+
float d3 = 1.0 / (0.15 + dot(uv - p3, uv - p3));
|
|
2985
|
+
float sum = d1 + d2 + d3;
|
|
2986
|
+
vec3 col = (u_color1 * d1 + u_color2 * d2 + u_color3 * d3) / sum;
|
|
2987
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
2988
|
+
}
|
|
2989
|
+
`
|
|
2990
|
+
},
|
|
2991
|
+
{
|
|
2992
|
+
id: "plasma-wave",
|
|
2993
|
+
category: "flow",
|
|
2994
|
+
colorSlots: 3,
|
|
2995
|
+
defaultColors: [
|
|
2996
|
+
"#2a2440",
|
|
2997
|
+
"#4a5a7c",
|
|
2998
|
+
"#8a6a7c"
|
|
2999
|
+
],
|
|
3000
|
+
params: [
|
|
3001
|
+
param("scale"),
|
|
3002
|
+
param("speed", .4),
|
|
3003
|
+
param("distortion", .4),
|
|
3004
|
+
param("grain", .03)
|
|
3005
|
+
],
|
|
3006
|
+
body: `
|
|
3007
|
+
void main(){
|
|
3008
|
+
vec2 uv = coords() * u_scale * 3.0;
|
|
3009
|
+
float t = u_time * u_speed * 0.5;
|
|
3010
|
+
float v = sin(uv.x + t)
|
|
3011
|
+
+ sin(uv.y + t * 1.3)
|
|
3012
|
+
+ sin((uv.x + uv.y) * 0.7 + t)
|
|
3013
|
+
+ sin(length(uv) * (1.0 + u_distortion * 2.0) - t * 1.5);
|
|
3014
|
+
v *= 0.25;
|
|
3015
|
+
float m = 0.5 + 0.5 * sin(v * PI);
|
|
3016
|
+
vec3 col = mix(u_color1, u_color2, m);
|
|
3017
|
+
col = mix(col, u_color3, (0.5 + 0.5 * sin(v * 6.2831)) * 0.5);
|
|
3018
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
3019
|
+
}
|
|
3020
|
+
`
|
|
3021
|
+
},
|
|
3022
|
+
{
|
|
3023
|
+
id: "swirl-vortex",
|
|
3024
|
+
category: "flow",
|
|
3025
|
+
colorSlots: 3,
|
|
3026
|
+
defaultColors: [
|
|
3027
|
+
"#0a0e16",
|
|
3028
|
+
"#7c4a5c",
|
|
3029
|
+
"#3a7a72"
|
|
3030
|
+
],
|
|
3031
|
+
params: [
|
|
3032
|
+
param("scale"),
|
|
3033
|
+
param("speed", .4),
|
|
3034
|
+
param("distortion", .45),
|
|
3035
|
+
param("grain", .03)
|
|
3036
|
+
],
|
|
3037
|
+
body: `
|
|
3038
|
+
void main(){
|
|
3039
|
+
vec2 uv = coords() * u_scale;
|
|
3040
|
+
float t = u_time * u_speed * 0.3;
|
|
3041
|
+
float r = length(uv);
|
|
3042
|
+
float a = atan(uv.y, uv.x);
|
|
3043
|
+
a += (1.0 + u_distortion * 3.0) * (0.5 / (r + 0.2)) + t;
|
|
3044
|
+
vec2 p = vec2(cos(a), sin(a)) * r;
|
|
3045
|
+
float n = fbm(p * 2.0 + t);
|
|
3046
|
+
float bands = sin((a * 3.0 + r * 6.0 - t * 2.0) + n * 4.0);
|
|
3047
|
+
float m = 0.5 + 0.5 * bands;
|
|
3048
|
+
vec3 col = mix(u_color1, u_color2, m);
|
|
3049
|
+
col = mix(col, u_color3, pow(m, 3.0) * 0.6);
|
|
3050
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
3051
|
+
}
|
|
3052
|
+
`
|
|
3053
|
+
},
|
|
3054
|
+
{
|
|
3055
|
+
id: "caustics",
|
|
3056
|
+
category: "flow",
|
|
3057
|
+
colorSlots: 3,
|
|
3058
|
+
defaultColors: [
|
|
3059
|
+
"#04161f",
|
|
3060
|
+
"#2a6a7c",
|
|
3061
|
+
"#a8d4d0"
|
|
3062
|
+
],
|
|
3063
|
+
params: [
|
|
3064
|
+
param("scale"),
|
|
3065
|
+
param("speed", .4),
|
|
3066
|
+
param("distortion", .4),
|
|
3067
|
+
param("grain", .03)
|
|
3068
|
+
],
|
|
3069
|
+
body: `
|
|
3070
|
+
void main(){
|
|
3071
|
+
vec2 uv = coords() * u_scale * 3.0;
|
|
3072
|
+
float t = u_time * u_speed * 0.4;
|
|
3073
|
+
vec2 p = uv;
|
|
3074
|
+
float c = 0.0;
|
|
3075
|
+
for (int i = 0; i < 4; i++){
|
|
3076
|
+
float fi = float(i);
|
|
3077
|
+
p += vec2(sin(p.y * 1.5 + t + fi), cos(p.x * 1.5 + t * 1.1 + fi))
|
|
3078
|
+
* (0.4 + u_distortion * 0.5);
|
|
3079
|
+
c += 1.0 / (length(fract(p) - 0.5) + 0.1);
|
|
3080
|
+
}
|
|
3081
|
+
c = pow(c * 0.06, 2.0);
|
|
3082
|
+
vec3 col = mix(u_color1, u_color2, clamp(c, 0.0, 1.0));
|
|
3083
|
+
col += u_color3 * pow(clamp(c - 0.6, 0.0, 1.0), 2.0) * 0.6;
|
|
3084
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
3085
|
+
}
|
|
3086
|
+
`
|
|
3087
|
+
},
|
|
3088
|
+
{
|
|
3089
|
+
id: "nebula",
|
|
3090
|
+
category: "flow",
|
|
3091
|
+
colorSlots: 3,
|
|
3092
|
+
defaultColors: [
|
|
3093
|
+
"#06040e",
|
|
3094
|
+
"#4a3a6c",
|
|
3095
|
+
"#8a5a7c"
|
|
3096
|
+
],
|
|
3097
|
+
params: [
|
|
3098
|
+
param("scale"),
|
|
3099
|
+
param("speed", .35),
|
|
3100
|
+
param("distortion", .5),
|
|
3101
|
+
param("grain", .03)
|
|
3102
|
+
],
|
|
3103
|
+
body: `
|
|
3104
|
+
void main(){
|
|
3105
|
+
vec2 uv = coords() * u_scale;
|
|
3106
|
+
float t = u_time * u_speed * 0.15;
|
|
3107
|
+
vec2 q = vec2(fbm(uv + t), fbm(uv * 1.2 - t + 3.0));
|
|
3108
|
+
float density = fbm(uv * 1.5 + q * (2.0 + u_distortion * 2.0));
|
|
3109
|
+
density = pow(density, 2.0);
|
|
3110
|
+
vec3 col = mix(u_color1, u_color2, density);
|
|
3111
|
+
col = mix(col, u_color3, pow(density, 3.0) * 0.8);
|
|
3112
|
+
float stars = step(0.99, hash21(floor(vUv * u_resolution.xy / 2.5)));
|
|
3113
|
+
col += vec3(stars) * 0.35;
|
|
3114
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
3115
|
+
}
|
|
3116
|
+
`
|
|
3117
|
+
},
|
|
3118
|
+
{
|
|
3119
|
+
id: "voronoi-crystal",
|
|
3120
|
+
category: "geometric",
|
|
3121
|
+
colorSlots: 3,
|
|
3122
|
+
defaultColors: [
|
|
3123
|
+
"#121a30",
|
|
3124
|
+
"#3a5a82",
|
|
3125
|
+
"#c4d0e0"
|
|
3126
|
+
],
|
|
3127
|
+
params: [
|
|
3128
|
+
param("scale"),
|
|
3129
|
+
param("speed", .35),
|
|
3130
|
+
param("contrast", .4),
|
|
3131
|
+
param("grain", .03)
|
|
3132
|
+
],
|
|
3133
|
+
body: `
|
|
3134
|
+
vec2 hash22(vec2 p){
|
|
3135
|
+
p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
|
|
3136
|
+
return fract(sin(p) * 43758.5453);
|
|
3137
|
+
}
|
|
3138
|
+
void main(){
|
|
3139
|
+
vec2 uv = coords() * u_scale * 3.0;
|
|
3140
|
+
float t = u_time * u_speed * 0.3;
|
|
3141
|
+
vec2 ip = floor(uv);
|
|
3142
|
+
vec2 fp = fract(uv);
|
|
3143
|
+
float f1 = 8.0;
|
|
3144
|
+
float f2 = 8.0;
|
|
3145
|
+
vec2 cellId = vec2(0.0);
|
|
3146
|
+
for (int j = -1; j <= 1; j++){
|
|
3147
|
+
for (int i = -1; i <= 1; i++){
|
|
3148
|
+
vec2 n = vec2(float(i), float(j));
|
|
3149
|
+
vec2 pnt = n + 0.5 + 0.4 * sin(t + 6.2831 * hash22(ip + n));
|
|
3150
|
+
float d = length(pnt - fp);
|
|
3151
|
+
if (d < f1){ f2 = f1; f1 = d; cellId = ip + n; }
|
|
3152
|
+
else if (d < f2){ f2 = d; }
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
float facet = f2 - f1;
|
|
3156
|
+
float cell = hash22(cellId).x;
|
|
3157
|
+
vec3 col = mix(u_color1, u_color2, cell);
|
|
3158
|
+
col = mix(col, u_color3, smoothstep(0.05, 0.0, facet) * mix(0.4, 0.7, u_contrast));
|
|
3159
|
+
col += pow(max(0.0, 1.0 - f1), 4.0) * 0.1;
|
|
3160
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
3161
|
+
}
|
|
3162
|
+
`
|
|
3163
|
+
},
|
|
3164
|
+
{
|
|
3165
|
+
id: "kaleidoscope",
|
|
3166
|
+
category: "geometric",
|
|
3167
|
+
colorSlots: 3,
|
|
3168
|
+
defaultColors: [
|
|
3169
|
+
"#1a1428",
|
|
3170
|
+
"#7c5a6c",
|
|
3171
|
+
"#b09a6a"
|
|
3172
|
+
],
|
|
3173
|
+
params: [
|
|
3174
|
+
param("scale"),
|
|
3175
|
+
param("speed", .35),
|
|
3176
|
+
param("distortion", .4),
|
|
3177
|
+
param("grain", .03)
|
|
3178
|
+
],
|
|
3179
|
+
body: `
|
|
3180
|
+
void main(){
|
|
3181
|
+
vec2 uv = coords() * u_scale;
|
|
3182
|
+
float t = u_time * u_speed * 0.3;
|
|
3183
|
+
float r = length(uv);
|
|
3184
|
+
float a = atan(uv.y, uv.x);
|
|
3185
|
+
float seg = 6.0 + floor(u_distortion * 6.0);
|
|
3186
|
+
a = mod(a, 6.2831 / seg);
|
|
3187
|
+
a = abs(a - 3.14159 / seg);
|
|
3188
|
+
vec2 p = vec2(cos(a), sin(a)) * r;
|
|
3189
|
+
float pattern = fbm(p * 3.0 + t);
|
|
3190
|
+
float m = 0.5 + 0.5 * sin(pattern * 6.2831 + r * 4.0 - t);
|
|
3191
|
+
vec3 col = mix(u_color1, u_color2, m);
|
|
3192
|
+
col = mix(col, u_color3, pow(m, 3.0) * 0.6);
|
|
3193
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
3194
|
+
}
|
|
3195
|
+
`
|
|
3196
|
+
},
|
|
3197
|
+
{
|
|
3198
|
+
id: "dither-gradient",
|
|
3199
|
+
category: "retro",
|
|
3200
|
+
colorSlots: 3,
|
|
3201
|
+
defaultColors: [
|
|
3202
|
+
"#14202e",
|
|
3203
|
+
"#7c4a54",
|
|
3204
|
+
"#d4d0c8"
|
|
3205
|
+
],
|
|
3206
|
+
params: [
|
|
3207
|
+
param("scale"),
|
|
3208
|
+
param("speed", .35),
|
|
3209
|
+
param("distortion", .35)
|
|
3210
|
+
],
|
|
3211
|
+
body: `
|
|
3212
|
+
float bayer2(vec2 a){ a = floor(a); return fract(dot(a, vec2(0.5, a.y * 0.75))); }
|
|
3213
|
+
float bayer4(vec2 a){ return bayer2(0.5 * a) * 0.25 + bayer2(a); }
|
|
3214
|
+
void main(){
|
|
3215
|
+
vec2 uv = coords() * u_scale;
|
|
3216
|
+
float t = u_time * u_speed * 0.2;
|
|
3217
|
+
float g = fbm(uv * 1.5 + vec2(t, -t) * (0.5 + u_distortion));
|
|
3218
|
+
float levels = 5.0;
|
|
3219
|
+
float d = bayer4(gl_FragCoord.xy);
|
|
3220
|
+
float q = floor(g * levels + d) / levels;
|
|
3221
|
+
vec3 col = mix(u_color1, u_color2, clamp(q, 0.0, 1.0));
|
|
3222
|
+
col = mix(col, u_color3, smoothstep(0.7, 1.0, q));
|
|
3223
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
3224
|
+
}
|
|
3225
|
+
`
|
|
3226
|
+
},
|
|
3227
|
+
{
|
|
3228
|
+
id: "halftone",
|
|
3229
|
+
category: "retro",
|
|
3230
|
+
colorSlots: 3,
|
|
3231
|
+
defaultColors: [
|
|
3232
|
+
"#161616",
|
|
3233
|
+
"#8a5a3a",
|
|
3234
|
+
"#c4a06a"
|
|
3235
|
+
],
|
|
3236
|
+
params: [
|
|
3237
|
+
param("scale"),
|
|
3238
|
+
param("speed", .35),
|
|
3239
|
+
param("distortion", .45)
|
|
3240
|
+
],
|
|
3241
|
+
body: `
|
|
3242
|
+
void main(){
|
|
3243
|
+
vec2 uv = coords() * u_scale;
|
|
3244
|
+
float t = u_time * u_speed * 0.2;
|
|
3245
|
+
float g = fbm(uv * 1.5 + t);
|
|
3246
|
+
vec2 gp = gl_FragCoord.xy / mix(22.0, 9.0, clamp(u_distortion, 0.0, 1.0));
|
|
3247
|
+
vec2 cell = fract(gp) - 0.5;
|
|
3248
|
+
float radius = g * 0.7;
|
|
3249
|
+
float dot = smoothstep(radius, radius - 0.12, length(cell));
|
|
3250
|
+
vec3 col = mix(u_color1, u_color2, g);
|
|
3251
|
+
col = mix(col, u_color3, dot * 0.85);
|
|
3252
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
3253
|
+
}
|
|
3254
|
+
`
|
|
3255
|
+
},
|
|
3256
|
+
{
|
|
3257
|
+
id: "dia-gradient",
|
|
3258
|
+
category: "gradient",
|
|
3259
|
+
colorSlots: 3,
|
|
3260
|
+
defaultColors: [
|
|
3261
|
+
"#FF9ECF",
|
|
3262
|
+
"#A9C9FF",
|
|
3263
|
+
"#FFF3A8"
|
|
3264
|
+
],
|
|
3265
|
+
params: [
|
|
3266
|
+
param("bars"),
|
|
3267
|
+
param("blur"),
|
|
3268
|
+
param("peak"),
|
|
3269
|
+
param("valley"),
|
|
3270
|
+
param("speed", .3)
|
|
3271
|
+
],
|
|
3272
|
+
body: `
|
|
3273
|
+
void main(){
|
|
3274
|
+
vec2 uv = vUv;
|
|
3275
|
+
float t = u_time * u_speed * 0.15;
|
|
3276
|
+
float cx = 0.5 + sin(t) * 0.04;
|
|
3277
|
+
float peakShape = clamp(1.0 - abs(uv.x - cx) * 2.0, 0.0, 1.0);
|
|
3278
|
+
peakShape = pow(peakShape, 0.85);
|
|
3279
|
+
float profile = mix(u_valley, u_peak, peakShape);
|
|
3280
|
+
float heightFromBottom = 1.0 - uv.y;
|
|
3281
|
+
float h = heightFromBottom / max(profile, 0.02);
|
|
3282
|
+
float bars = max(1.0, floor(u_bars));
|
|
3283
|
+
float hb = h * bars;
|
|
3284
|
+
float band = floor(hb);
|
|
3285
|
+
float frac = hb - band;
|
|
3286
|
+
float soft = mix(0.04, 0.5, clamp(u_blur, 0.0, 1.0));
|
|
3287
|
+
float sm = smoothstep(0.5 - soft, 0.5 + soft, frac);
|
|
3288
|
+
float hv = clamp((band + sm) / bars, 0.0, 1.0);
|
|
3289
|
+
vec3 col;
|
|
3290
|
+
if (hv < 0.33) col = mix(u_color1, u_color2, smoothstep(0.0, 0.33, hv));
|
|
3291
|
+
else if (hv < 0.66) col = mix(u_color2, u_color3, smoothstep(0.33, 0.66, hv));
|
|
3292
|
+
else col = mix(u_color3, vec3(1.0), smoothstep(0.66, 1.0, hv));
|
|
3293
|
+
float above = smoothstep(1.0, 1.0 + soft * 0.5, h);
|
|
3294
|
+
col = mix(col, vec3(1.0), above);
|
|
3295
|
+
gl_FragColor = vec4(finish(col, vUv), 1.0);
|
|
3296
|
+
}
|
|
3297
|
+
`
|
|
3298
|
+
}
|
|
3299
|
+
];
|
|
3300
|
+
const SHADER_PRESET_IDS = SHADER_PRESETS.map((preset) => preset.id);
|
|
3301
|
+
const SHADER_PARAM_KEYS = [
|
|
3302
|
+
"scale",
|
|
3303
|
+
"speed",
|
|
3304
|
+
"distortion",
|
|
3305
|
+
"grain",
|
|
3306
|
+
"contrast",
|
|
3307
|
+
"angle",
|
|
3308
|
+
"dim",
|
|
3309
|
+
"vignette",
|
|
3310
|
+
"scrim",
|
|
3311
|
+
"interactive",
|
|
3312
|
+
"bars",
|
|
3313
|
+
"blur",
|
|
3314
|
+
"peak",
|
|
3315
|
+
"valley"
|
|
3316
|
+
];
|
|
3317
|
+
/** 패턴과 무관하게 모든 프리셋에 공통 적용되는 마감(가독성) 파라미터 키. */
|
|
3318
|
+
const SHADER_OVERLAY_PARAM_KEYS = [
|
|
3319
|
+
"dim",
|
|
3320
|
+
"vignette",
|
|
3321
|
+
"scrim"
|
|
3322
|
+
];
|
|
3323
|
+
const PRESET_BY_ID = new Map(SHADER_PRESETS.map((preset) => [preset.id, preset]));
|
|
3324
|
+
/** id로 프리셋 조회 (없으면 첫 프리셋 폴백). */
|
|
3325
|
+
function getShaderPreset(id) {
|
|
3326
|
+
var _PRESET_BY_ID$get;
|
|
3327
|
+
return (_PRESET_BY_ID$get = PRESET_BY_ID.get(id)) !== null && _PRESET_BY_ID$get !== void 0 ? _PRESET_BY_ID$get : SHADER_PRESETS[0];
|
|
3328
|
+
}
|
|
3329
|
+
/** 공유 프렐류드 + 프리셋 본문을 합친 완성 프래그먼트 셰이더 소스. */
|
|
3330
|
+
function getShaderFragment(preset) {
|
|
3331
|
+
return `${SHADER_PRELUDE}\n${preset.body}`;
|
|
3332
|
+
}
|
|
3333
|
+
//#endregion
|
|
3334
|
+
//#region src/core/shader-surface.tsx
|
|
3335
|
+
/** 정지(`paused`) 프레임에 사용하는 고정 시간 — t=0의 밋밋함 회피. */
|
|
3336
|
+
const STATIC_TIME = 8;
|
|
3337
|
+
/** hex → 0..1 RGB. 잘못된 hex는 검정으로 폴백. */
|
|
3338
|
+
function toRgbFloat(hex) {
|
|
3339
|
+
try {
|
|
3340
|
+
const [r, g, b] = hexToSrgb(hex);
|
|
3341
|
+
return [
|
|
3342
|
+
r / 255,
|
|
3343
|
+
g / 255,
|
|
3344
|
+
b / 255
|
|
3345
|
+
];
|
|
3346
|
+
} catch (_unused) {
|
|
3347
|
+
return [
|
|
3348
|
+
0,
|
|
3349
|
+
0,
|
|
3350
|
+
0
|
|
3351
|
+
];
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
function resolveColors(presetId, colors) {
|
|
3355
|
+
var _source$, _ref, _source$2, _ref2, _ref3, _source$3;
|
|
3356
|
+
const preset = getShaderPreset(presetId);
|
|
3357
|
+
const source = colors && colors.length > 0 ? colors : preset.defaultColors;
|
|
3358
|
+
return [
|
|
3359
|
+
(_source$ = source[0]) !== null && _source$ !== void 0 ? _source$ : preset.defaultColors[0],
|
|
3360
|
+
(_ref = (_source$2 = source[1]) !== null && _source$2 !== void 0 ? _source$2 : source[0]) !== null && _ref !== void 0 ? _ref : preset.defaultColors[1],
|
|
3361
|
+
(_ref2 = (_ref3 = (_source$3 = source[2]) !== null && _source$3 !== void 0 ? _source$3 : source[1]) !== null && _ref3 !== void 0 ? _ref3 : source[0]) !== null && _ref2 !== void 0 ? _ref2 : preset.defaultColors[2]
|
|
3362
|
+
];
|
|
3363
|
+
}
|
|
3364
|
+
function resolveParam(presetId, key, override) {
|
|
3365
|
+
var _spec$default;
|
|
3366
|
+
if (typeof override === "number" && Number.isFinite(override)) return override;
|
|
3367
|
+
const spec = getShaderPreset(presetId).params.find((p) => p.key === key);
|
|
3368
|
+
return (_spec$default = spec === null || spec === void 0 ? void 0 : spec.default) !== null && _spec$default !== void 0 ? _spec$default : SHADER_PARAM_FALLBACK[key];
|
|
3369
|
+
}
|
|
3370
|
+
function prefersReducedMotion() {
|
|
3371
|
+
return typeof window !== "undefined" && window.matchMedia ? window.matchMedia("(prefers-reduced-motion: reduce)").matches : false;
|
|
3372
|
+
}
|
|
3373
|
+
const ShaderSurfaceComponent = forwardRef(function ShaderSurface({ preset, colors, scale, speed, distortion, grain, contrast, angle, dim, vignette, scrim, interactive, params, time, paused = false, dpr, className, style, "aria-hidden": ariaHidden = true }, ref) {
|
|
3374
|
+
const containerRef = useRef(null);
|
|
3375
|
+
const canvasRef = useRef(null);
|
|
3376
|
+
const stateRef = useRef({
|
|
3377
|
+
preset,
|
|
3378
|
+
colors,
|
|
3379
|
+
scale,
|
|
3380
|
+
speed,
|
|
3381
|
+
distortion,
|
|
3382
|
+
grain,
|
|
3383
|
+
contrast,
|
|
3384
|
+
angle,
|
|
3385
|
+
dim,
|
|
3386
|
+
vignette,
|
|
3387
|
+
scrim,
|
|
3388
|
+
interactive,
|
|
3389
|
+
params,
|
|
3390
|
+
time,
|
|
3391
|
+
paused
|
|
3392
|
+
});
|
|
3393
|
+
stateRef.current = {
|
|
3394
|
+
preset,
|
|
3395
|
+
colors,
|
|
3396
|
+
scale,
|
|
3397
|
+
speed,
|
|
3398
|
+
distortion,
|
|
3399
|
+
grain,
|
|
3400
|
+
contrast,
|
|
3401
|
+
angle,
|
|
3402
|
+
dim,
|
|
3403
|
+
vignette,
|
|
3404
|
+
scrim,
|
|
3405
|
+
interactive,
|
|
3406
|
+
params,
|
|
3407
|
+
time,
|
|
3408
|
+
paused
|
|
3409
|
+
};
|
|
3410
|
+
const rendererRef = useRef(null);
|
|
3411
|
+
const programRef = useRef(null);
|
|
3412
|
+
const renderAtRef = useRef(null);
|
|
3413
|
+
const loopRef = useRef(null);
|
|
3414
|
+
const mouseRef = useRef([.5, .5]);
|
|
3415
|
+
const fallbackBackground = useMemo(() => {
|
|
3416
|
+
const [c1, c2, c3] = resolveColors(preset, colors);
|
|
3417
|
+
return `linear-gradient(${resolveParam(preset, "angle", angle) + 120}deg, ${c1}, ${c2}, ${c3})`;
|
|
3418
|
+
}, [
|
|
3419
|
+
preset,
|
|
3420
|
+
colors,
|
|
3421
|
+
angle
|
|
3422
|
+
]);
|
|
3423
|
+
useImperativeHandle(ref, () => ({ capture: () => {
|
|
3424
|
+
var _stateRef$current$tim;
|
|
3425
|
+
const canvas = canvasRef.current;
|
|
3426
|
+
if (!canvas || !renderAtRef.current || !programRef.current) return null;
|
|
3427
|
+
const now = stateRef.current.paused ? (_stateRef$current$tim = stateRef.current.time) !== null && _stateRef$current$tim !== void 0 ? _stateRef$current$tim : STATIC_TIME : performance.now() / 1e3;
|
|
3428
|
+
renderAtRef.current(now);
|
|
3429
|
+
try {
|
|
3430
|
+
return canvas.toDataURL("image/png");
|
|
3431
|
+
} catch (_unused2) {
|
|
3432
|
+
return null;
|
|
3433
|
+
}
|
|
3434
|
+
} }), []);
|
|
3435
|
+
useEffect(() => {
|
|
3436
|
+
const container = containerRef.current;
|
|
3437
|
+
const canvas = canvasRef.current;
|
|
3438
|
+
if (!container || !canvas) return;
|
|
3439
|
+
let renderer;
|
|
3440
|
+
try {
|
|
3441
|
+
renderer = new Renderer({
|
|
3442
|
+
canvas,
|
|
3443
|
+
dpr: dpr !== null && dpr !== void 0 ? dpr : typeof window !== "undefined" ? Math.min(window.devicePixelRatio || 1, 2) : 1,
|
|
3444
|
+
alpha: true,
|
|
3445
|
+
premultipliedAlpha: false
|
|
3446
|
+
});
|
|
3447
|
+
} catch (_unused3) {
|
|
3448
|
+
return;
|
|
3449
|
+
}
|
|
3450
|
+
const gl = renderer.gl;
|
|
3451
|
+
rendererRef.current = {
|
|
3452
|
+
renderer,
|
|
3453
|
+
gl
|
|
3454
|
+
};
|
|
3455
|
+
const resize = () => {
|
|
3456
|
+
const rect = container.getBoundingClientRect();
|
|
3457
|
+
renderer.setSize(Math.max(1, rect.width), Math.max(1, rect.height));
|
|
3458
|
+
};
|
|
3459
|
+
resize();
|
|
3460
|
+
const renderAt = (timeSeconds) => {
|
|
3461
|
+
const current = programRef.current;
|
|
3462
|
+
if (!current) return;
|
|
3463
|
+
const s = stateRef.current;
|
|
3464
|
+
const [c1, c2, c3] = resolveColors(s.preset, s.colors);
|
|
3465
|
+
const u = current.program.uniforms;
|
|
3466
|
+
const val = (key, override) => {
|
|
3467
|
+
var _s$params;
|
|
3468
|
+
return resolveParam(s.preset, key, override !== null && override !== void 0 ? override : (_s$params = s.params) === null || _s$params === void 0 ? void 0 : _s$params[key]);
|
|
3469
|
+
};
|
|
3470
|
+
u.u_time.value = timeSeconds;
|
|
3471
|
+
u.u_resolution.value = [gl.canvas.width, gl.canvas.height];
|
|
3472
|
+
u.u_scale.value = val("scale", s.scale);
|
|
3473
|
+
u.u_speed.value = val("speed", s.speed);
|
|
3474
|
+
u.u_distortion.value = val("distortion", s.distortion);
|
|
3475
|
+
u.u_grain.value = val("grain", s.grain);
|
|
3476
|
+
u.u_contrast.value = val("contrast", s.contrast);
|
|
3477
|
+
u.u_angle.value = val("angle", s.angle);
|
|
3478
|
+
u.u_dim.value = val("dim", s.dim);
|
|
3479
|
+
u.u_vignette.value = val("vignette", s.vignette);
|
|
3480
|
+
u.u_scrim.value = val("scrim", s.scrim);
|
|
3481
|
+
u.u_interactive.value = val("interactive", s.interactive);
|
|
3482
|
+
u.u_bars.value = val("bars", void 0);
|
|
3483
|
+
u.u_blur.value = val("blur", void 0);
|
|
3484
|
+
u.u_peak.value = val("peak", void 0);
|
|
3485
|
+
u.u_valley.value = val("valley", void 0);
|
|
3486
|
+
u.u_mouse.value = mouseRef.current;
|
|
3487
|
+
u.u_color1.value = toRgbFloat(c1);
|
|
3488
|
+
u.u_color2.value = toRgbFloat(c2);
|
|
3489
|
+
u.u_color3.value = toRgbFloat(c3);
|
|
3490
|
+
renderer.render({ scene: current.mesh });
|
|
3491
|
+
};
|
|
3492
|
+
renderAtRef.current = renderAt;
|
|
3493
|
+
const staticTime = () => {
|
|
3494
|
+
var _stateRef$current$tim2;
|
|
3495
|
+
return (_stateRef$current$tim2 = stateRef.current.time) !== null && _stateRef$current$tim2 !== void 0 ? _stateRef$current$tim2 : STATIC_TIME;
|
|
3496
|
+
};
|
|
3497
|
+
const reduced = prefersReducedMotion();
|
|
3498
|
+
let inView = true;
|
|
3499
|
+
const isAnimating = () => !stateRef.current.paused && !reduced && inView;
|
|
3500
|
+
let raf = 0;
|
|
3501
|
+
let startTime = performance.now();
|
|
3502
|
+
const loop = (nowMs) => {
|
|
3503
|
+
renderAt((nowMs - startTime) / 1e3);
|
|
3504
|
+
raf = requestAnimationFrame(loop);
|
|
3505
|
+
};
|
|
3506
|
+
const start = () => {
|
|
3507
|
+
if (raf) return;
|
|
3508
|
+
startTime = performance.now();
|
|
3509
|
+
raf = requestAnimationFrame(loop);
|
|
3510
|
+
};
|
|
3511
|
+
const stop = () => {
|
|
3512
|
+
if (raf) cancelAnimationFrame(raf);
|
|
3513
|
+
raf = 0;
|
|
3514
|
+
};
|
|
3515
|
+
loopRef.current = {
|
|
3516
|
+
start,
|
|
3517
|
+
stop,
|
|
3518
|
+
isAnimating
|
|
3519
|
+
};
|
|
3520
|
+
const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
|
|
3521
|
+
resize();
|
|
3522
|
+
if (!isAnimating()) renderAt(staticTime());
|
|
3523
|
+
}) : null;
|
|
3524
|
+
resizeObserver === null || resizeObserver === void 0 || resizeObserver.observe(container);
|
|
3525
|
+
const intersectionObserver = typeof IntersectionObserver !== "undefined" ? new IntersectionObserver((entries) => {
|
|
3526
|
+
var _entries$0$isIntersec, _entries$;
|
|
3527
|
+
inView = (_entries$0$isIntersec = (_entries$ = entries[0]) === null || _entries$ === void 0 ? void 0 : _entries$.isIntersecting) !== null && _entries$0$isIntersec !== void 0 ? _entries$0$isIntersec : true;
|
|
3528
|
+
if (inView && isAnimating()) start();
|
|
3529
|
+
else stop();
|
|
3530
|
+
}, { threshold: 0 }) : null;
|
|
3531
|
+
intersectionObserver === null || intersectionObserver === void 0 || intersectionObserver.observe(container);
|
|
3532
|
+
const handlePointer = (event) => {
|
|
3533
|
+
const rect = container.getBoundingClientRect();
|
|
3534
|
+
mouseRef.current = [(event.clientX - rect.left) / Math.max(1, rect.width), (event.clientY - rect.top) / Math.max(1, rect.height)];
|
|
3535
|
+
const s = stateRef.current;
|
|
3536
|
+
if (!isAnimating() && resolveParam(s.preset, "interactive", s.interactive) > 0) renderAt(staticTime());
|
|
3537
|
+
};
|
|
3538
|
+
container.addEventListener("pointermove", handlePointer);
|
|
3539
|
+
if (isAnimating()) start();
|
|
3540
|
+
return () => {
|
|
3541
|
+
stop();
|
|
3542
|
+
resizeObserver === null || resizeObserver === void 0 || resizeObserver.disconnect();
|
|
3543
|
+
intersectionObserver === null || intersectionObserver === void 0 || intersectionObserver.disconnect();
|
|
3544
|
+
container.removeEventListener("pointermove", handlePointer);
|
|
3545
|
+
loopRef.current = null;
|
|
3546
|
+
renderAtRef.current = null;
|
|
3547
|
+
rendererRef.current = null;
|
|
3548
|
+
};
|
|
3549
|
+
}, [dpr]);
|
|
3550
|
+
useEffect(() => {
|
|
3551
|
+
var _renderAtRef$current, _stateRef$current$tim3;
|
|
3552
|
+
const engine = rendererRef.current;
|
|
3553
|
+
if (!engine) return;
|
|
3554
|
+
const { gl } = engine;
|
|
3555
|
+
const presetDef = getShaderPreset(preset);
|
|
3556
|
+
const geometry = new Triangle(gl);
|
|
3557
|
+
const program = new Program(gl, {
|
|
3558
|
+
vertex: SHADER_VERTEX,
|
|
3559
|
+
fragment: getShaderFragment(presetDef),
|
|
3560
|
+
uniforms: {
|
|
3561
|
+
u_time: { value: 0 },
|
|
3562
|
+
u_resolution: { value: [1, 1] },
|
|
3563
|
+
u_scale: { value: 1 },
|
|
3564
|
+
u_speed: { value: 1 },
|
|
3565
|
+
u_distortion: { value: .5 },
|
|
3566
|
+
u_grain: { value: 0 },
|
|
3567
|
+
u_contrast: { value: .5 },
|
|
3568
|
+
u_angle: { value: 0 },
|
|
3569
|
+
u_dim: { value: 0 },
|
|
3570
|
+
u_vignette: { value: 0 },
|
|
3571
|
+
u_scrim: { value: 0 },
|
|
3572
|
+
u_mouse: { value: [.5, .5] },
|
|
3573
|
+
u_interactive: { value: 0 },
|
|
3574
|
+
u_bars: { value: 19 },
|
|
3575
|
+
u_blur: { value: .29 },
|
|
3576
|
+
u_peak: { value: .98 },
|
|
3577
|
+
u_valley: { value: .55 },
|
|
3578
|
+
u_color1: { value: [
|
|
3579
|
+
0,
|
|
3580
|
+
0,
|
|
3581
|
+
0
|
|
3582
|
+
] },
|
|
3583
|
+
u_color2: { value: [
|
|
3584
|
+
0,
|
|
3585
|
+
0,
|
|
3586
|
+
0
|
|
3587
|
+
] },
|
|
3588
|
+
u_color3: { value: [
|
|
3589
|
+
0,
|
|
3590
|
+
0,
|
|
3591
|
+
0
|
|
3592
|
+
] }
|
|
3593
|
+
}
|
|
3594
|
+
});
|
|
3595
|
+
const mesh = new Mesh(gl, {
|
|
3596
|
+
geometry,
|
|
3597
|
+
program
|
|
3598
|
+
});
|
|
3599
|
+
programRef.current = {
|
|
3600
|
+
program,
|
|
3601
|
+
mesh,
|
|
3602
|
+
geometry
|
|
3603
|
+
};
|
|
3604
|
+
const loop = loopRef.current;
|
|
3605
|
+
if (loop === null || loop === void 0 ? void 0 : loop.isAnimating()) loop.start();
|
|
3606
|
+
else (_renderAtRef$current = renderAtRef.current) === null || _renderAtRef$current === void 0 || _renderAtRef$current.call(renderAtRef, (_stateRef$current$tim3 = stateRef.current.time) !== null && _stateRef$current$tim3 !== void 0 ? _stateRef$current$tim3 : STATIC_TIME);
|
|
3607
|
+
return () => {
|
|
3608
|
+
programRef.current = null;
|
|
3609
|
+
program.remove();
|
|
3610
|
+
geometry.remove();
|
|
3611
|
+
};
|
|
3612
|
+
}, [preset]);
|
|
3613
|
+
useEffect(() => {
|
|
3614
|
+
const loop = loopRef.current;
|
|
3615
|
+
if (!loop) return;
|
|
3616
|
+
if (loop.isAnimating()) loop.start();
|
|
3617
|
+
else {
|
|
3618
|
+
var _renderAtRef$current2, _stateRef$current$tim4;
|
|
3619
|
+
loop.stop();
|
|
3620
|
+
(_renderAtRef$current2 = renderAtRef.current) === null || _renderAtRef$current2 === void 0 || _renderAtRef$current2.call(renderAtRef, (_stateRef$current$tim4 = stateRef.current.time) !== null && _stateRef$current$tim4 !== void 0 ? _stateRef$current$tim4 : STATIC_TIME);
|
|
3621
|
+
}
|
|
3622
|
+
}, [
|
|
3623
|
+
paused,
|
|
3624
|
+
colors,
|
|
3625
|
+
scale,
|
|
3626
|
+
speed,
|
|
3627
|
+
distortion,
|
|
3628
|
+
grain,
|
|
3629
|
+
contrast,
|
|
3630
|
+
angle,
|
|
3631
|
+
dim,
|
|
3632
|
+
vignette,
|
|
3633
|
+
scrim,
|
|
3634
|
+
interactive,
|
|
3635
|
+
params,
|
|
3636
|
+
time
|
|
3637
|
+
]);
|
|
3638
|
+
return /* @__PURE__ */ jsx("div", {
|
|
3639
|
+
ref: containerRef,
|
|
3640
|
+
"aria-hidden": ariaHidden,
|
|
3641
|
+
className: cn("relative overflow-hidden", className),
|
|
3642
|
+
style: _objectSpread2({ background: fallbackBackground }, style),
|
|
3643
|
+
children: /* @__PURE__ */ jsx("canvas", {
|
|
3644
|
+
ref: canvasRef,
|
|
3645
|
+
className: "absolute inset-0 block h-full w-full"
|
|
3646
|
+
})
|
|
3647
|
+
});
|
|
3648
|
+
});
|
|
3649
|
+
ShaderSurfaceComponent.displayName = "ShaderSurface";
|
|
3650
|
+
const ShaderSurface = ShaderSurfaceComponent;
|
|
3651
|
+
//#endregion
|
|
3652
|
+
export { CardHeader as $, DropdownTrigger as A, Avatar as B, LoadingOverlay as C, DropdownItem as D, DropdownContent as E, formatOptTime as F, SkeletonCard as G, Textarea as H, StepBuilder as I, Switch as J, SkeletonTable as K, Meter$1 as L, formatOptDateTime as M, formatOptNumber as N, DropdownMenu as O, formatOptShortDate as P, CardFooter as Q, CircularProgress as R, BlockLayout as S, RadioGroup as T, Skeleton as U, AvatarGroup as V, SkeletonAvatar as W, CardContent as X, Card as Y, CardDescription as Z, Section as _, SHADER_PRESETS as a, AccordionTrigger$1 as at, PageHeader as b, getShaderPreset as c, OtpField as ct, DrawerPanel as d, Text as dt, CardTitle as et, DrawerRoot as f, ResizablePanelGroup as g, ResizablePanel as h, SHADER_PARAM_KEYS as i, AccordionRoot$1 as it, ConditionBuilder as j, DropdownSeparator as k, VirtualList as l, Separator$2 as lt, ResizableHandle as m, SHADER_OVERLAY_PARAM_KEYS as n, AccordionContent$1 as nt, SHADER_PRESET_IDS as o, Toggle$1 as ot, Logo as p, SkeletonText as q, SHADER_PARAM_FALLBACK as r, AccordionItem$1 as rt, getShaderFragment as s, ToggleGroup$1 as st, ShaderSurface as t, Breadcrumb as tt, DrawerClose as u, Heading as ut, Container as v, Radio as w, createBlock as x, ScrollArea as y, Progress as z };
|
|
3653
|
+
|
|
3654
|
+
//# sourceMappingURL=shader-surface-BUPZ0P-n.js.map
|