@classytic/fluid 0.2.4 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +149 -62
- package/dist/api-pagination-CJ0vR_w6.d.mts +34 -0
- package/dist/api-pagination-DBTE0yk4.mjs +190 -0
- package/dist/chunk-DQk6qfdC.mjs +18 -0
- package/dist/client/calendar.d.mts +105 -0
- package/dist/client/calendar.mjs +202 -0
- package/dist/client/core.d.mts +1614 -0
- package/dist/client/core.mjs +2779 -0
- package/dist/client/error.d.mts +125 -0
- package/dist/client/error.mjs +166 -0
- package/dist/client/hooks.d.mts +162 -0
- package/dist/client/hooks.mjs +447 -0
- package/dist/client/table.d.mts +84 -0
- package/dist/client/table.mjs +373 -0
- package/dist/client/theme.d.mts +6 -0
- package/dist/client/theme.mjs +65 -0
- package/dist/command.d.mts +134 -0
- package/dist/command.mjs +132 -0
- package/dist/compact.d.mts +359 -0
- package/dist/compact.mjs +892 -0
- package/dist/dashboard.d.mts +778 -0
- package/dist/dashboard.mjs +1617 -0
- package/dist/filter-utils-DqMmy_v-.mjs +72 -0
- package/dist/filter-utils-IZ0GtuPo.d.mts +40 -0
- package/dist/forms.d.mts +1549 -0
- package/dist/forms.mjs +3740 -0
- package/dist/index.d.mts +296 -0
- package/dist/index.mjs +432 -0
- package/dist/layouts.d.mts +215 -0
- package/dist/layouts.mjs +460 -0
- package/dist/search-context-DR7DBs7S.mjs +19 -0
- package/dist/search.d.mts +254 -0
- package/dist/search.mjs +523 -0
- package/dist/sheet-wrapper-CWNCvYMD.mjs +211 -0
- package/dist/use-base-search-BGgWnWaF.d.mts +35 -0
- package/dist/use-debounce-xmZucz5e.mjs +53 -0
- package/dist/use-keyboard-shortcut-Bl6YM5Q7.mjs +82 -0
- package/dist/use-keyboard-shortcut-_mRCh3QO.d.mts +24 -0
- package/dist/use-media-query-BnVNIKT4.mjs +17 -0
- package/dist/use-mobile-BX3SQVo2.mjs +20 -0
- package/dist/use-scroll-detection-CsgsQYvy.mjs +43 -0
- package/dist/utils-CDue7cEt.d.mts +6 -0
- package/dist/utils-DQ5SCVoW.mjs +10 -0
- package/package.json +85 -45
- package/styles.css +2 -2
- package/dist/chunk-GUHK2DTW.js +0 -15
- package/dist/chunk-GUHK2DTW.js.map +0 -1
- package/dist/chunk-H3NFL3GJ.js +0 -57
- package/dist/chunk-H3NFL3GJ.js.map +0 -1
- package/dist/chunk-J2YRTQE4.js +0 -293
- package/dist/chunk-J2YRTQE4.js.map +0 -1
- package/dist/compact.d.ts +0 -217
- package/dist/compact.js +0 -986
- package/dist/compact.js.map +0 -1
- package/dist/dashboard.d.ts +0 -387
- package/dist/dashboard.js +0 -1032
- package/dist/dashboard.js.map +0 -1
- package/dist/index.d.ts +0 -2140
- package/dist/index.js +0 -6422
- package/dist/index.js.map +0 -1
- package/dist/layout.d.ts +0 -25
- package/dist/layout.js +0 -4
- package/dist/layout.js.map +0 -1
- package/dist/search.d.ts +0 -172
- package/dist/search.js +0 -341
- package/dist/search.js.map +0 -1
- package/dist/use-base-search-AS5Z3SAy.d.ts +0 -64
- package/dist/utils-Cbsgs0XP.d.ts +0 -5
package/dist/compact.mjs
ADDED
|
@@ -0,0 +1,892 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { t as cn } from "./utils-DQ5SCVoW.mjs";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import React, { createContext, memo, useContext, useId, useMemo, useRef, useState } from "react";
|
|
6
|
+
import { Check, Minus, Plus, Wand2, X } from "lucide-react";
|
|
7
|
+
import { Button } from "@/components/ui/button";
|
|
8
|
+
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupTextarea } from "@/components/ui/input-group";
|
|
9
|
+
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
|
10
|
+
import { Badge } from "@/components/ui/badge";
|
|
11
|
+
import { Input } from "@/components/ui/input";
|
|
12
|
+
import { Controller } from "react-hook-form";
|
|
13
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
14
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
15
|
+
|
|
16
|
+
//#region src/components/compact/field.tsx
|
|
17
|
+
const FieldContext = createContext(null);
|
|
18
|
+
/**
|
|
19
|
+
* Field wrapper — provides context (id, disabled, invalid) and
|
|
20
|
+
* top padding so the floating label has room above the border.
|
|
21
|
+
*
|
|
22
|
+
* Emits `data-slot="field"`, `data-disabled`, `data-invalid` to match
|
|
23
|
+
* the shadcn/ui Field primitive so consuming apps can target via CSS.
|
|
24
|
+
*/
|
|
25
|
+
const Root = memo(function FieldRoot({ children, className, disabled = false, invalid = false }) {
|
|
26
|
+
const id = useId();
|
|
27
|
+
const value = useMemo(() => ({
|
|
28
|
+
id,
|
|
29
|
+
disabled,
|
|
30
|
+
invalid
|
|
31
|
+
}), [
|
|
32
|
+
id,
|
|
33
|
+
disabled,
|
|
34
|
+
invalid
|
|
35
|
+
]);
|
|
36
|
+
return /* @__PURE__ */ jsx(FieldContext.Provider, {
|
|
37
|
+
value,
|
|
38
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
39
|
+
"data-slot": "field",
|
|
40
|
+
"data-disabled": disabled || void 0,
|
|
41
|
+
"data-invalid": invalid || void 0,
|
|
42
|
+
className: cn("w-full pt-3", className),
|
|
43
|
+
children
|
|
44
|
+
})
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
/**
|
|
48
|
+
* Animated floating label — rests inside the input when empty,
|
|
49
|
+
* floats up to the border on focus or when the field has a value.
|
|
50
|
+
*
|
|
51
|
+
* The parent `div.group` must set `data-floated` when the field
|
|
52
|
+
* has a value so the label stays floated after blur.
|
|
53
|
+
*
|
|
54
|
+
* Emits `data-slot="field-label"` to match shadcn/ui FieldLabel.
|
|
55
|
+
*/
|
|
56
|
+
const Label = memo(function FieldLabel({ children, className }) {
|
|
57
|
+
const ctx = useContext(FieldContext);
|
|
58
|
+
return /* @__PURE__ */ jsx("label", {
|
|
59
|
+
htmlFor: ctx?.id,
|
|
60
|
+
"data-slot": "field-label",
|
|
61
|
+
className: cn("absolute left-3 z-10", "pointer-events-none select-none whitespace-nowrap", "origin-[0] transition-all duration-200 ease-out", "top-1/2 -translate-y-1/2", "text-sm text-muted-foreground", "group-focus-within:top-0 group-focus-within:-translate-y-1/2", "group-focus-within:text-xs group-focus-within:font-medium", "group-focus-within:bg-[var(--compact-field-bg,var(--color-card))] group-focus-within:px-1.5", "group-focus-within:text-primary", "group-data-[floated]:top-0 group-data-[floated]:-translate-y-1/2", "group-data-[floated]:text-xs group-data-[floated]:font-medium", "group-data-[floated]:bg-[var(--compact-field-bg,var(--color-card))] group-data-[floated]:px-1.5", ctx?.disabled && "opacity-60", ctx?.invalid && "text-destructive group-focus-within:text-destructive", className),
|
|
62
|
+
children
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
const Error = memo(function FieldError({ children, className }) {
|
|
66
|
+
if (!children) return null;
|
|
67
|
+
return /* @__PURE__ */ jsx("p", {
|
|
68
|
+
role: "alert",
|
|
69
|
+
"data-slot": "field-error",
|
|
70
|
+
className: cn("text-[11px] text-destructive mt-1.5 pl-3", className),
|
|
71
|
+
children
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
/**
|
|
75
|
+
* Helper / description text below the input control.
|
|
76
|
+
*
|
|
77
|
+
* Emits `data-slot="field-description"` to match shadcn/ui FieldDescription.
|
|
78
|
+
*/
|
|
79
|
+
const Description = memo(function FieldDescription({ children, className }) {
|
|
80
|
+
if (!children) return null;
|
|
81
|
+
return /* @__PURE__ */ jsx("p", {
|
|
82
|
+
"data-slot": "field-description",
|
|
83
|
+
className: cn("text-[11px] text-muted-foreground mt-1.5 pl-3", className),
|
|
84
|
+
children
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
const Icon = memo(function FieldIcon({ children, className }) {
|
|
88
|
+
return /* @__PURE__ */ jsx("div", {
|
|
89
|
+
className: cn("absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none z-10", "text-muted-foreground", className),
|
|
90
|
+
children
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
const Field = {
|
|
94
|
+
Root,
|
|
95
|
+
Label,
|
|
96
|
+
Error,
|
|
97
|
+
Description,
|
|
98
|
+
Icon
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Material-style focus: clean border + outline using the `ring` token
|
|
102
|
+
* to stay consistent with the base Input primitive.
|
|
103
|
+
*
|
|
104
|
+
* Use on standalone `<Input>` / `<Textarea>` inside compact fields.
|
|
105
|
+
*/
|
|
106
|
+
const COMPACT_FOCUS = "focus-visible:ring-0 focus-visible:border-ring focus-visible:outline focus-visible:outline-1 focus-visible:-outline-offset-1 focus-visible:outline-ring";
|
|
107
|
+
/**
|
|
108
|
+
* Same focus style but scoped to InputGroup container.
|
|
109
|
+
* Targets the inner `[data-slot=input-group-control]` to override
|
|
110
|
+
* InputGroup's default `ring-3` glow with a clean outline.
|
|
111
|
+
*/
|
|
112
|
+
const INPUT_GROUP_FOCUS = "has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-0 has-[[data-slot=input-group-control]:focus-visible]:outline has-[[data-slot=input-group-control]:focus-visible]:outline-1 has-[[data-slot=input-group-control]:focus-visible]:-outline-offset-1 has-[[data-slot=input-group-control]:focus-visible]:outline-ring";
|
|
113
|
+
/**
|
|
114
|
+
* Error variant of COMPACT_FOCUS — used when field has validation error.
|
|
115
|
+
*/
|
|
116
|
+
const COMPACT_FOCUS_ERROR = "border-destructive focus-visible:border-destructive focus-visible:outline-destructive";
|
|
117
|
+
/**
|
|
118
|
+
* Same as COMPACT_FOCUS but using `focus:` instead of `focus-visible:`.
|
|
119
|
+
* SelectTrigger is a button — buttons use `focus:` in shadcn/ui.
|
|
120
|
+
*/
|
|
121
|
+
const COMPACT_SELECT_FOCUS = "focus:ring-0 focus:border-ring focus:outline focus:outline-1 focus:-outline-offset-1 focus:outline-ring";
|
|
122
|
+
const COMPACT_SELECT_FOCUS_ERROR = "border-destructive focus:border-destructive focus:outline-destructive";
|
|
123
|
+
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/components/compact/compact-input.tsx
|
|
126
|
+
/**
|
|
127
|
+
* CompactInput - Enhanced form input with InputGroup support
|
|
128
|
+
*
|
|
129
|
+
* Features:
|
|
130
|
+
* - Floating label design
|
|
131
|
+
* - InputGroup support with icons, buttons, and text addons
|
|
132
|
+
* - Controller integration for react-hook-form
|
|
133
|
+
* - Direct usage without form
|
|
134
|
+
*/
|
|
135
|
+
function CompactInput({ control, name, description, required, label, placeholder, disabled, type = "text", addonLeft, addonRight, className, inputClassName, onValueChange, value, onChange, error, autoComplete, autoFocus, maxLength, minLength, max, min, pattern, readOnly, step, inputMode, enterKeyHint, ref, ...props }) {
|
|
136
|
+
const hasInputGroup = addonLeft || addonRight;
|
|
137
|
+
const renderInput = (fieldValue, fieldOnChange, isDisabled, fieldError) => {
|
|
138
|
+
const inputProps = {
|
|
139
|
+
ref,
|
|
140
|
+
id: name,
|
|
141
|
+
name,
|
|
142
|
+
type,
|
|
143
|
+
disabled: isDisabled,
|
|
144
|
+
placeholder: label ? void 0 : placeholder,
|
|
145
|
+
value: fieldValue || "",
|
|
146
|
+
onChange: (e) => {
|
|
147
|
+
const newValue = e.target.value;
|
|
148
|
+
fieldOnChange?.(newValue);
|
|
149
|
+
onValueChange?.(newValue);
|
|
150
|
+
},
|
|
151
|
+
className: cn("h-11 text-sm dark:bg-transparent", COMPACT_FOCUS, fieldError && COMPACT_FOCUS_ERROR, inputClassName),
|
|
152
|
+
autoComplete,
|
|
153
|
+
autoFocus,
|
|
154
|
+
maxLength,
|
|
155
|
+
minLength,
|
|
156
|
+
max,
|
|
157
|
+
min,
|
|
158
|
+
pattern,
|
|
159
|
+
readOnly,
|
|
160
|
+
step,
|
|
161
|
+
inputMode,
|
|
162
|
+
enterKeyHint,
|
|
163
|
+
...props
|
|
164
|
+
};
|
|
165
|
+
if (hasInputGroup) return /* @__PURE__ */ jsxs(InputGroup, {
|
|
166
|
+
className: cn("h-11", INPUT_GROUP_FOCUS),
|
|
167
|
+
children: [
|
|
168
|
+
addonLeft && /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
169
|
+
align: "inline-start",
|
|
170
|
+
children: addonLeft
|
|
171
|
+
}),
|
|
172
|
+
/* @__PURE__ */ jsx(InputGroupInput, { ...inputProps }),
|
|
173
|
+
addonRight && /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
174
|
+
align: "inline-end",
|
|
175
|
+
children: addonRight
|
|
176
|
+
})
|
|
177
|
+
]
|
|
178
|
+
});
|
|
179
|
+
return /* @__PURE__ */ jsx(Input, { ...inputProps });
|
|
180
|
+
};
|
|
181
|
+
if (control && name) return /* @__PURE__ */ jsx(Controller, {
|
|
182
|
+
name,
|
|
183
|
+
control,
|
|
184
|
+
render: ({ field, fieldState }) => /* @__PURE__ */ jsxs(Field.Root, {
|
|
185
|
+
className,
|
|
186
|
+
disabled,
|
|
187
|
+
invalid: !!fieldState?.error,
|
|
188
|
+
children: [
|
|
189
|
+
/* @__PURE__ */ jsxs("div", {
|
|
190
|
+
className: "relative group",
|
|
191
|
+
"data-floated": !!field.value || void 0,
|
|
192
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, {
|
|
193
|
+
className: addonLeft ? "left-9 group-focus-within:left-3 group-data-[floated]:left-3" : void 0,
|
|
194
|
+
children: label
|
|
195
|
+
}), renderInput(field.value, field.onChange, disabled, fieldState?.error?.message)]
|
|
196
|
+
}),
|
|
197
|
+
fieldState?.error && /* @__PURE__ */ jsx(Field.Error, { children: fieldState.error.message }),
|
|
198
|
+
/* @__PURE__ */ jsx(Field.Description, { children: description })
|
|
199
|
+
]
|
|
200
|
+
})
|
|
201
|
+
});
|
|
202
|
+
const handleDirectChange = (newValue) => {
|
|
203
|
+
onChange?.({ target: { value: newValue } });
|
|
204
|
+
onValueChange?.(newValue);
|
|
205
|
+
};
|
|
206
|
+
return /* @__PURE__ */ jsxs(Field.Root, {
|
|
207
|
+
className,
|
|
208
|
+
disabled,
|
|
209
|
+
invalid: !!error,
|
|
210
|
+
children: [
|
|
211
|
+
/* @__PURE__ */ jsxs("div", {
|
|
212
|
+
className: "relative group",
|
|
213
|
+
"data-floated": !!value || void 0,
|
|
214
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, {
|
|
215
|
+
className: addonLeft ? "left-9 group-focus-within:left-3 group-data-[floated]:left-3" : void 0,
|
|
216
|
+
children: label
|
|
217
|
+
}), renderInput(value, handleDirectChange, disabled, error)]
|
|
218
|
+
}),
|
|
219
|
+
error && /* @__PURE__ */ jsx(Field.Error, { children: error }),
|
|
220
|
+
/* @__PURE__ */ jsx(Field.Description, { children: description })
|
|
221
|
+
]
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
//#endregion
|
|
226
|
+
//#region src/components/compact/compact-textarea.tsx
|
|
227
|
+
/**
|
|
228
|
+
* CompactTextarea - Enhanced textarea with InputGroup support
|
|
229
|
+
*
|
|
230
|
+
* Features:
|
|
231
|
+
* - Floating label design
|
|
232
|
+
* - InputGroup support with addons
|
|
233
|
+
* - Character counter with maxLength
|
|
234
|
+
* - Controller integration for react-hook-form
|
|
235
|
+
* - Direct usage without form
|
|
236
|
+
*/
|
|
237
|
+
function CompactTextarea({ control, name, description, required, label, placeholder, disabled, rows = 3, addonLeft, addonRight, className, inputClassName, onValueChange, value, onChange, error, cols, wrap, autoComplete, autoFocus, maxLength, minLength, readOnly, spellCheck, ref, ...props }) {
|
|
238
|
+
const hasInputGroup = addonLeft || addonRight;
|
|
239
|
+
const renderTextarea = (fieldValue, fieldOnChange, isDisabled, fieldError) => {
|
|
240
|
+
const currentCharCount = fieldValue?.length || 0;
|
|
241
|
+
const textareaProps = {
|
|
242
|
+
ref,
|
|
243
|
+
id: name,
|
|
244
|
+
name,
|
|
245
|
+
disabled: isDisabled,
|
|
246
|
+
placeholder: label ? void 0 : placeholder,
|
|
247
|
+
rows,
|
|
248
|
+
value: fieldValue || "",
|
|
249
|
+
onChange: (e) => {
|
|
250
|
+
const newValue = e.target.value;
|
|
251
|
+
fieldOnChange?.(newValue);
|
|
252
|
+
onValueChange?.(newValue);
|
|
253
|
+
},
|
|
254
|
+
className: cn("resize-none pt-3 text-sm dark:bg-transparent", COMPACT_FOCUS, fieldError && COMPACT_FOCUS_ERROR, inputClassName),
|
|
255
|
+
cols,
|
|
256
|
+
wrap,
|
|
257
|
+
autoComplete,
|
|
258
|
+
autoFocus,
|
|
259
|
+
maxLength,
|
|
260
|
+
minLength,
|
|
261
|
+
readOnly,
|
|
262
|
+
spellCheck,
|
|
263
|
+
...props
|
|
264
|
+
};
|
|
265
|
+
if (hasInputGroup) return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(InputGroup, {
|
|
266
|
+
className: INPUT_GROUP_FOCUS,
|
|
267
|
+
children: [
|
|
268
|
+
addonLeft && /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
269
|
+
align: "block-start",
|
|
270
|
+
children: addonLeft
|
|
271
|
+
}),
|
|
272
|
+
/* @__PURE__ */ jsx(InputGroupTextarea, { ...textareaProps }),
|
|
273
|
+
addonRight && /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
274
|
+
align: "block-end",
|
|
275
|
+
children: addonRight
|
|
276
|
+
})
|
|
277
|
+
]
|
|
278
|
+
}), maxLength && currentCharCount > 0 && /* @__PURE__ */ jsxs("div", {
|
|
279
|
+
className: "text-xs text-muted-foreground mt-1.5 text-right",
|
|
280
|
+
children: [
|
|
281
|
+
currentCharCount,
|
|
282
|
+
"/",
|
|
283
|
+
maxLength
|
|
284
|
+
]
|
|
285
|
+
})] });
|
|
286
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Textarea, { ...textareaProps }), maxLength && currentCharCount > 0 && /* @__PURE__ */ jsxs("div", {
|
|
287
|
+
className: "text-xs text-muted-foreground mt-1.5 text-right",
|
|
288
|
+
children: [
|
|
289
|
+
currentCharCount,
|
|
290
|
+
"/",
|
|
291
|
+
maxLength
|
|
292
|
+
]
|
|
293
|
+
})] });
|
|
294
|
+
};
|
|
295
|
+
if (control && name) return /* @__PURE__ */ jsx(Controller, {
|
|
296
|
+
name,
|
|
297
|
+
control,
|
|
298
|
+
render: ({ field, fieldState }) => /* @__PURE__ */ jsxs(Field.Root, {
|
|
299
|
+
className,
|
|
300
|
+
disabled,
|
|
301
|
+
invalid: !!fieldState?.error,
|
|
302
|
+
children: [
|
|
303
|
+
/* @__PURE__ */ jsxs("div", {
|
|
304
|
+
className: "relative group",
|
|
305
|
+
"data-floated": !!field.value || void 0,
|
|
306
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, {
|
|
307
|
+
className: "top-3 translate-y-0 group-focus-within:top-0 group-focus-within:-translate-y-1/2 group-data-[floated]:top-0 group-data-[floated]:-translate-y-1/2",
|
|
308
|
+
children: label
|
|
309
|
+
}), renderTextarea(field.value, field.onChange, disabled, fieldState?.error?.message)]
|
|
310
|
+
}),
|
|
311
|
+
fieldState?.error && /* @__PURE__ */ jsx(Field.Error, { children: fieldState.error.message }),
|
|
312
|
+
/* @__PURE__ */ jsx(Field.Description, { children: description })
|
|
313
|
+
]
|
|
314
|
+
})
|
|
315
|
+
});
|
|
316
|
+
const handleDirectChange = (newValue) => {
|
|
317
|
+
onChange?.({ target: { value: newValue } });
|
|
318
|
+
onValueChange?.(newValue);
|
|
319
|
+
};
|
|
320
|
+
return /* @__PURE__ */ jsxs(Field.Root, {
|
|
321
|
+
className,
|
|
322
|
+
disabled,
|
|
323
|
+
invalid: !!error,
|
|
324
|
+
children: [
|
|
325
|
+
/* @__PURE__ */ jsxs("div", {
|
|
326
|
+
className: "relative group",
|
|
327
|
+
"data-floated": !!value || void 0,
|
|
328
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, {
|
|
329
|
+
className: "top-3 translate-y-0 group-focus-within:top-0 group-focus-within:-translate-y-1/2 group-data-[floated]:top-0 group-data-[floated]:-translate-y-1/2",
|
|
330
|
+
children: label
|
|
331
|
+
}), renderTextarea(value, handleDirectChange, disabled, error)]
|
|
332
|
+
}),
|
|
333
|
+
error && /* @__PURE__ */ jsx(Field.Error, { children: error }),
|
|
334
|
+
/* @__PURE__ */ jsx(Field.Description, { children: description })
|
|
335
|
+
]
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
//#endregion
|
|
340
|
+
//#region src/components/compact/compact-select.tsx
|
|
341
|
+
/**
|
|
342
|
+
* CompactSelect - Simple, clean select dropdown
|
|
343
|
+
*
|
|
344
|
+
* @example
|
|
345
|
+
* <CompactSelect
|
|
346
|
+
* label="Status"
|
|
347
|
+
* items={[
|
|
348
|
+
* { value: "active", label: "Active" },
|
|
349
|
+
* { value: "inactive", label: "Inactive" }
|
|
350
|
+
* ]}
|
|
351
|
+
* />
|
|
352
|
+
*/
|
|
353
|
+
function CompactSelect({ control, name, description, required, label, placeholder = "Select option", disabled, items = [], className, onValueChange, value, error, ref, ...props }) {
|
|
354
|
+
if (control && name) return /* @__PURE__ */ jsx(Controller, {
|
|
355
|
+
name,
|
|
356
|
+
control,
|
|
357
|
+
render: ({ field, fieldState }) => /* @__PURE__ */ jsxs(Field.Root, {
|
|
358
|
+
disabled,
|
|
359
|
+
invalid: !!fieldState?.error,
|
|
360
|
+
children: [
|
|
361
|
+
/* @__PURE__ */ jsxs("div", {
|
|
362
|
+
className: "relative group",
|
|
363
|
+
"data-floated": !!field.value || void 0,
|
|
364
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, { children: label }), /* @__PURE__ */ jsxs(Select, {
|
|
365
|
+
value: field.value,
|
|
366
|
+
onValueChange: (val) => {
|
|
367
|
+
field.onChange(val);
|
|
368
|
+
onValueChange?.(val);
|
|
369
|
+
},
|
|
370
|
+
disabled,
|
|
371
|
+
...props,
|
|
372
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
373
|
+
ref,
|
|
374
|
+
id: name,
|
|
375
|
+
className: cn("w-full data-[size=default]:h-11 text-sm dark:bg-transparent", COMPACT_SELECT_FOCUS, fieldState?.error && COMPACT_SELECT_FOCUS_ERROR, className),
|
|
376
|
+
children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "" })
|
|
377
|
+
}), /* @__PURE__ */ jsx(SelectContent, { children: items.map((item) => /* @__PURE__ */ jsx(SelectItem, {
|
|
378
|
+
value: item.value,
|
|
379
|
+
children: item.label
|
|
380
|
+
}, item.value)) })]
|
|
381
|
+
})]
|
|
382
|
+
}),
|
|
383
|
+
fieldState?.error && /* @__PURE__ */ jsx(Field.Error, { children: fieldState.error.message }),
|
|
384
|
+
/* @__PURE__ */ jsx(Field.Description, { children: description })
|
|
385
|
+
]
|
|
386
|
+
})
|
|
387
|
+
});
|
|
388
|
+
return /* @__PURE__ */ jsxs(Field.Root, {
|
|
389
|
+
disabled,
|
|
390
|
+
invalid: !!error,
|
|
391
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
392
|
+
className: "relative group",
|
|
393
|
+
"data-floated": !!value || void 0,
|
|
394
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, { children: label }), /* @__PURE__ */ jsxs(Select, {
|
|
395
|
+
value,
|
|
396
|
+
onValueChange,
|
|
397
|
+
disabled,
|
|
398
|
+
...props,
|
|
399
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
400
|
+
ref,
|
|
401
|
+
className: cn("w-full data-[size=default]:h-11 text-sm dark:bg-transparent", COMPACT_SELECT_FOCUS, error && COMPACT_SELECT_FOCUS_ERROR, className),
|
|
402
|
+
children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "" })
|
|
403
|
+
}), /* @__PURE__ */ jsx(SelectContent, { children: items.map((item) => /* @__PURE__ */ jsx(SelectItem, {
|
|
404
|
+
value: item.value,
|
|
405
|
+
children: item.label
|
|
406
|
+
}, item.value)) })]
|
|
407
|
+
})]
|
|
408
|
+
}), error && /* @__PURE__ */ jsx(Field.Error, { children: error })]
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
//#endregion
|
|
413
|
+
//#region src/components/compact/compact-number-input.tsx
|
|
414
|
+
/**
|
|
415
|
+
* CompactNumberInput - A space-efficient number input with optional increment/decrement buttons
|
|
416
|
+
*
|
|
417
|
+
* Features:
|
|
418
|
+
* - Floating label design
|
|
419
|
+
* - Optional increment/decrement buttons
|
|
420
|
+
* - Support for min/max/step values
|
|
421
|
+
* - Prefix/suffix support
|
|
422
|
+
* - Form integration via control prop
|
|
423
|
+
* - Direct usage without form
|
|
424
|
+
*/
|
|
425
|
+
function CompactNumberInput({ control, name, description, required, label, placeholder, disabled, min = 0, max, step = 1, prefix, suffix, showButtons = false, buttonVariant = "ghost", className, inputClassName, labelClassName, containerClassName, onValueChange, value, defaultValue, onChange, error, ref, ...props }) {
|
|
426
|
+
if (control && name) return /* @__PURE__ */ jsx(Controller, {
|
|
427
|
+
name,
|
|
428
|
+
control,
|
|
429
|
+
render: ({ field, fieldState }) => /* @__PURE__ */ jsx(CompactNumberInputInternal, {
|
|
430
|
+
ref,
|
|
431
|
+
label,
|
|
432
|
+
placeholder,
|
|
433
|
+
disabled,
|
|
434
|
+
min,
|
|
435
|
+
max,
|
|
436
|
+
step,
|
|
437
|
+
prefix,
|
|
438
|
+
suffix,
|
|
439
|
+
showButtons,
|
|
440
|
+
buttonVariant,
|
|
441
|
+
className,
|
|
442
|
+
inputClassName,
|
|
443
|
+
labelClassName,
|
|
444
|
+
containerClassName,
|
|
445
|
+
error: fieldState?.error?.message,
|
|
446
|
+
value: field.value ?? "",
|
|
447
|
+
onChange: (val) => {
|
|
448
|
+
field.onChange(val);
|
|
449
|
+
onValueChange?.(val);
|
|
450
|
+
},
|
|
451
|
+
...props
|
|
452
|
+
})
|
|
453
|
+
});
|
|
454
|
+
return /* @__PURE__ */ jsx(CompactNumberInputInternal, {
|
|
455
|
+
ref,
|
|
456
|
+
label,
|
|
457
|
+
placeholder,
|
|
458
|
+
disabled,
|
|
459
|
+
min,
|
|
460
|
+
max,
|
|
461
|
+
step,
|
|
462
|
+
prefix,
|
|
463
|
+
suffix,
|
|
464
|
+
showButtons,
|
|
465
|
+
buttonVariant,
|
|
466
|
+
className,
|
|
467
|
+
inputClassName,
|
|
468
|
+
labelClassName,
|
|
469
|
+
containerClassName,
|
|
470
|
+
error,
|
|
471
|
+
value: value ?? "",
|
|
472
|
+
defaultValue,
|
|
473
|
+
onChange,
|
|
474
|
+
onValueChange,
|
|
475
|
+
...props
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
function CompactNumberInputInternal({ label, placeholder, min = 0, max, step = 1, prefix, suffix, showButtons, buttonVariant, error, className, inputClassName, labelClassName, containerClassName, disabled, value, defaultValue, onChange, onValueChange, ref, ...props }) {
|
|
479
|
+
const handleIncrement = () => {
|
|
480
|
+
const newValue = (Number(value) || 0) + step;
|
|
481
|
+
if (max === void 0 || newValue <= max) {
|
|
482
|
+
const finalValue = Number(newValue.toFixed(10));
|
|
483
|
+
onChange?.(finalValue);
|
|
484
|
+
onValueChange?.(finalValue);
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
const handleDecrement = () => {
|
|
488
|
+
const newValue = (Number(value) || 0) - step;
|
|
489
|
+
if (newValue >= min) {
|
|
490
|
+
const finalValue = Number(newValue.toFixed(10));
|
|
491
|
+
onChange?.(finalValue);
|
|
492
|
+
onValueChange?.(finalValue);
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
const handleInputChange = (e) => {
|
|
496
|
+
const val = e.target.value;
|
|
497
|
+
if (val === "") {
|
|
498
|
+
onChange?.("");
|
|
499
|
+
onValueChange?.("");
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const numVal = Number(val);
|
|
503
|
+
if (!isNaN(numVal)) {
|
|
504
|
+
if (min !== void 0 && numVal < min || max !== void 0 && numVal > max) return;
|
|
505
|
+
onChange?.(numVal);
|
|
506
|
+
onValueChange?.(numVal);
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
const hasValue = value !== "" && value != null;
|
|
510
|
+
if (showButtons) return /* @__PURE__ */ jsxs(Field.Root, {
|
|
511
|
+
className: containerClassName,
|
|
512
|
+
disabled,
|
|
513
|
+
invalid: !!error,
|
|
514
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
515
|
+
className: "relative group",
|
|
516
|
+
"data-floated": hasValue || void 0,
|
|
517
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, {
|
|
518
|
+
className: cn("left-12 group-focus-within:left-3 group-data-[floated]:left-3", labelClassName),
|
|
519
|
+
children: label
|
|
520
|
+
}), /* @__PURE__ */ jsxs(InputGroup, {
|
|
521
|
+
className: cn("h-11", INPUT_GROUP_FOCUS),
|
|
522
|
+
children: [
|
|
523
|
+
/* @__PURE__ */ jsx(InputGroupAddon, {
|
|
524
|
+
align: "inline-start",
|
|
525
|
+
children: /* @__PURE__ */ jsx(InputGroupButton, {
|
|
526
|
+
type: "button",
|
|
527
|
+
variant: buttonVariant,
|
|
528
|
+
size: "icon-sm",
|
|
529
|
+
onClick: handleDecrement,
|
|
530
|
+
disabled: disabled || Number(value) <= min,
|
|
531
|
+
children: /* @__PURE__ */ jsx(Minus, { className: "h-4 w-4" })
|
|
532
|
+
})
|
|
533
|
+
}),
|
|
534
|
+
/* @__PURE__ */ jsx(InputGroupInput, {
|
|
535
|
+
ref,
|
|
536
|
+
type: "number",
|
|
537
|
+
value,
|
|
538
|
+
defaultValue,
|
|
539
|
+
onChange: handleInputChange,
|
|
540
|
+
min,
|
|
541
|
+
max,
|
|
542
|
+
step,
|
|
543
|
+
disabled,
|
|
544
|
+
placeholder: label ? void 0 : placeholder,
|
|
545
|
+
className: cn("text-center text-sm", inputClassName, className),
|
|
546
|
+
...props
|
|
547
|
+
}),
|
|
548
|
+
/* @__PURE__ */ jsx(InputGroupAddon, {
|
|
549
|
+
align: "inline-end",
|
|
550
|
+
children: /* @__PURE__ */ jsx(InputGroupButton, {
|
|
551
|
+
type: "button",
|
|
552
|
+
variant: buttonVariant,
|
|
553
|
+
size: "icon-sm",
|
|
554
|
+
onClick: handleIncrement,
|
|
555
|
+
disabled: disabled || max !== void 0 && Number(value) >= max,
|
|
556
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" })
|
|
557
|
+
})
|
|
558
|
+
})
|
|
559
|
+
]
|
|
560
|
+
})]
|
|
561
|
+
}), error && /* @__PURE__ */ jsx(Field.Error, { children: error })]
|
|
562
|
+
});
|
|
563
|
+
if (prefix || suffix) return /* @__PURE__ */ jsxs(Field.Root, {
|
|
564
|
+
className: containerClassName,
|
|
565
|
+
disabled,
|
|
566
|
+
invalid: !!error,
|
|
567
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
568
|
+
className: "relative group",
|
|
569
|
+
"data-floated": hasValue || void 0,
|
|
570
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, {
|
|
571
|
+
className: cn(prefix && "left-9 group-focus-within:left-3 group-data-[floated]:left-3", labelClassName),
|
|
572
|
+
children: label
|
|
573
|
+
}), /* @__PURE__ */ jsxs(InputGroup, {
|
|
574
|
+
className: cn("h-11", INPUT_GROUP_FOCUS),
|
|
575
|
+
children: [
|
|
576
|
+
prefix && /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
577
|
+
align: "inline-start",
|
|
578
|
+
children: typeof prefix === "string" ? /* @__PURE__ */ jsx("span", {
|
|
579
|
+
className: "text-xs",
|
|
580
|
+
children: prefix
|
|
581
|
+
}) : prefix
|
|
582
|
+
}),
|
|
583
|
+
/* @__PURE__ */ jsx(InputGroupInput, {
|
|
584
|
+
ref,
|
|
585
|
+
type: "number",
|
|
586
|
+
value,
|
|
587
|
+
defaultValue,
|
|
588
|
+
onChange: handleInputChange,
|
|
589
|
+
min,
|
|
590
|
+
max,
|
|
591
|
+
step,
|
|
592
|
+
disabled,
|
|
593
|
+
placeholder: label ? void 0 : placeholder,
|
|
594
|
+
className: cn("text-sm", inputClassName, className),
|
|
595
|
+
...props
|
|
596
|
+
}),
|
|
597
|
+
suffix && /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
598
|
+
align: "inline-end",
|
|
599
|
+
children: typeof suffix === "string" ? /* @__PURE__ */ jsx("span", {
|
|
600
|
+
className: "text-xs",
|
|
601
|
+
children: suffix
|
|
602
|
+
}) : suffix
|
|
603
|
+
})
|
|
604
|
+
]
|
|
605
|
+
})]
|
|
606
|
+
}), error && /* @__PURE__ */ jsx(Field.Error, { children: error })]
|
|
607
|
+
});
|
|
608
|
+
return /* @__PURE__ */ jsxs(Field.Root, {
|
|
609
|
+
className: containerClassName,
|
|
610
|
+
disabled,
|
|
611
|
+
invalid: !!error,
|
|
612
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
613
|
+
className: "relative group",
|
|
614
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, {
|
|
615
|
+
className: labelClassName,
|
|
616
|
+
children: label
|
|
617
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
618
|
+
ref,
|
|
619
|
+
type: "number",
|
|
620
|
+
value,
|
|
621
|
+
defaultValue,
|
|
622
|
+
onChange: handleInputChange,
|
|
623
|
+
min,
|
|
624
|
+
max,
|
|
625
|
+
step,
|
|
626
|
+
disabled,
|
|
627
|
+
placeholder,
|
|
628
|
+
className: cn("h-11 text-sm dark:bg-transparent", COMPACT_FOCUS, error && COMPACT_FOCUS_ERROR, inputClassName, className),
|
|
629
|
+
...props
|
|
630
|
+
})]
|
|
631
|
+
}), error && /* @__PURE__ */ jsx(Field.Error, { children: error })]
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
//#endregion
|
|
636
|
+
//#region src/components/compact/compact-tag-choice.tsx
|
|
637
|
+
/**
|
|
638
|
+
* CompactTagChoice - A compact tag selection input
|
|
639
|
+
*
|
|
640
|
+
* Features:
|
|
641
|
+
* - Multi-select tag interface
|
|
642
|
+
* - Maximum selection limit
|
|
643
|
+
* - Popover for selecting options
|
|
644
|
+
* - Form integration via control prop
|
|
645
|
+
* - Direct usage without form
|
|
646
|
+
*/
|
|
647
|
+
function CompactTagChoice({ control, name, label, description, placeholder = "Select options...", required, disabled, choices = [], maxSelections, className, containerClassName, labelClassName, inputClassName, value: propValue = [], onChange: propOnChange, onValueChange, error, ref }) {
|
|
648
|
+
if (control && name) return /* @__PURE__ */ jsx(Controller, {
|
|
649
|
+
name,
|
|
650
|
+
control,
|
|
651
|
+
render: ({ field, fieldState }) => /* @__PURE__ */ jsx(CompactTagChoiceInternal, {
|
|
652
|
+
ref,
|
|
653
|
+
label,
|
|
654
|
+
placeholder,
|
|
655
|
+
disabled,
|
|
656
|
+
error: fieldState?.error?.message,
|
|
657
|
+
value: field.value || [],
|
|
658
|
+
onChange: (vals) => {
|
|
659
|
+
field.onChange(vals);
|
|
660
|
+
onValueChange?.(vals);
|
|
661
|
+
},
|
|
662
|
+
choices,
|
|
663
|
+
maxSelections,
|
|
664
|
+
containerClassName: cn(className, containerClassName),
|
|
665
|
+
labelClassName,
|
|
666
|
+
inputClassName
|
|
667
|
+
})
|
|
668
|
+
});
|
|
669
|
+
return /* @__PURE__ */ jsx(CompactTagChoiceInternal, {
|
|
670
|
+
ref,
|
|
671
|
+
label,
|
|
672
|
+
placeholder,
|
|
673
|
+
disabled,
|
|
674
|
+
value: propValue,
|
|
675
|
+
onChange: (vals) => {
|
|
676
|
+
propOnChange?.(vals);
|
|
677
|
+
onValueChange?.(vals);
|
|
678
|
+
},
|
|
679
|
+
choices,
|
|
680
|
+
maxSelections,
|
|
681
|
+
containerClassName,
|
|
682
|
+
labelClassName,
|
|
683
|
+
inputClassName,
|
|
684
|
+
className,
|
|
685
|
+
error
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
function CompactTagChoiceInternal({ label, placeholder, disabled, value = [], onChange, choices = [], maxSelections, error, className, containerClassName, labelClassName, inputClassName, ref }) {
|
|
689
|
+
const [open, setOpen] = useState(false);
|
|
690
|
+
const triggerRef = useRef(null);
|
|
691
|
+
const availableChoices = choices.filter((c) => !value.includes(c.value));
|
|
692
|
+
const addChoice = (choiceValue) => {
|
|
693
|
+
if (value.includes(choiceValue)) return;
|
|
694
|
+
if (maxSelections && value.length >= maxSelections) return;
|
|
695
|
+
const updated = [...value, choiceValue];
|
|
696
|
+
onChange?.(updated);
|
|
697
|
+
};
|
|
698
|
+
const removeChoice = (choiceValue) => {
|
|
699
|
+
const updated = value.filter((v) => v !== choiceValue);
|
|
700
|
+
onChange?.(updated);
|
|
701
|
+
};
|
|
702
|
+
return /* @__PURE__ */ jsxs(Field.Root, {
|
|
703
|
+
className: containerClassName,
|
|
704
|
+
disabled,
|
|
705
|
+
invalid: !!error,
|
|
706
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
707
|
+
className: "relative group",
|
|
708
|
+
"data-floated": value.length > 0 || void 0,
|
|
709
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, {
|
|
710
|
+
className: labelClassName,
|
|
711
|
+
children: label
|
|
712
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
713
|
+
tabIndex: disabled ? void 0 : 0,
|
|
714
|
+
className: cn("min-h-10 w-full rounded-md border border-input bg-card px-3 py-2 text-sm", "focus-within:border-ring focus-within:outline focus-within:outline-1 focus-within:-outline-offset-1 focus-within:outline-ring", "focus:outline-none", "hover:border-ring/50 transition-all duration-200", error && "border-destructive focus-within:border-destructive focus-within:outline-destructive", disabled && "opacity-50 cursor-not-allowed", inputClassName),
|
|
715
|
+
ref,
|
|
716
|
+
children: [
|
|
717
|
+
/* @__PURE__ */ jsx("div", {
|
|
718
|
+
className: cn("flex flex-wrap gap-1", value.length > 0 && "mb-2"),
|
|
719
|
+
children: value.map((val) => {
|
|
720
|
+
return /* @__PURE__ */ jsxs(Badge, {
|
|
721
|
+
variant: "secondary",
|
|
722
|
+
className: "flex items-center gap-1 px-2 py-0.5 text-xs",
|
|
723
|
+
children: [/* @__PURE__ */ jsx("span", { children: choices.find((c) => c.value === val)?.label ?? val }), !disabled && /* @__PURE__ */ jsx("button", {
|
|
724
|
+
type: "button",
|
|
725
|
+
className: "text-muted-foreground hover:text-foreground ml-0.5",
|
|
726
|
+
onClick: (e) => {
|
|
727
|
+
e.preventDefault();
|
|
728
|
+
e.stopPropagation();
|
|
729
|
+
removeChoice(val);
|
|
730
|
+
},
|
|
731
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" })
|
|
732
|
+
})]
|
|
733
|
+
}, val);
|
|
734
|
+
})
|
|
735
|
+
}),
|
|
736
|
+
!disabled && availableChoices.length > 0 && (!maxSelections || value.length < maxSelections) && /* @__PURE__ */ jsxs(Popover, {
|
|
737
|
+
open,
|
|
738
|
+
onOpenChange: setOpen,
|
|
739
|
+
children: [/* @__PURE__ */ jsx(PopoverTrigger, { render: /* @__PURE__ */ jsxs(Button, {
|
|
740
|
+
ref: triggerRef,
|
|
741
|
+
type: "button",
|
|
742
|
+
variant: "ghost",
|
|
743
|
+
className: cn("inline-flex items-center justify-center h-auto p-1 rounded-md text-xs font-medium transition-colors", "text-muted-foreground hover:text-foreground hover:bg-accent", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"),
|
|
744
|
+
onClick: (e) => {
|
|
745
|
+
e.preventDefault();
|
|
746
|
+
e.stopPropagation();
|
|
747
|
+
setOpen(true);
|
|
748
|
+
},
|
|
749
|
+
children: [/* @__PURE__ */ jsx(Plus, { className: "h-3 w-3 mr-1" }), value.length === 0 && !label ? placeholder : "Add..."]
|
|
750
|
+
}) }), /* @__PURE__ */ jsx(PopoverContent, {
|
|
751
|
+
className: "w-[220px] p-0",
|
|
752
|
+
align: "start",
|
|
753
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
754
|
+
className: "p-1",
|
|
755
|
+
children: availableChoices.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
756
|
+
className: "px-2 py-3 text-sm text-muted-foreground text-center",
|
|
757
|
+
children: "No options available"
|
|
758
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
759
|
+
className: "space-y-0.5",
|
|
760
|
+
children: availableChoices.map((choice) => /* @__PURE__ */ jsxs("div", {
|
|
761
|
+
onClick: (e) => {
|
|
762
|
+
e.preventDefault();
|
|
763
|
+
e.stopPropagation();
|
|
764
|
+
addChoice(choice.value);
|
|
765
|
+
},
|
|
766
|
+
className: "flex items-center justify-between px-2 py-1.5 text-sm rounded-sm hover:bg-accent hover:text-accent-foreground cursor-pointer",
|
|
767
|
+
children: [/* @__PURE__ */ jsx("span", { children: choice.label }), value.includes(choice.value) && /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" })]
|
|
768
|
+
}, choice.value))
|
|
769
|
+
})
|
|
770
|
+
})
|
|
771
|
+
})]
|
|
772
|
+
}),
|
|
773
|
+
maxSelections && /* @__PURE__ */ jsxs("div", {
|
|
774
|
+
className: "text-[11px] text-muted-foreground mt-1",
|
|
775
|
+
children: [
|
|
776
|
+
value.length,
|
|
777
|
+
"/",
|
|
778
|
+
maxSelections,
|
|
779
|
+
" selected"
|
|
780
|
+
]
|
|
781
|
+
})
|
|
782
|
+
]
|
|
783
|
+
})]
|
|
784
|
+
}), error && /* @__PURE__ */ jsx(Field.Error, { children: error })]
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
//#endregion
|
|
789
|
+
//#region src/components/compact/compact-slug-field.tsx
|
|
790
|
+
/**
|
|
791
|
+
* Generates a URL-friendly slug from a string
|
|
792
|
+
*/
|
|
793
|
+
function generateSlug(text) {
|
|
794
|
+
if (!text) return "";
|
|
795
|
+
return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* CompactSlugField - Compact slug input with auto-generation
|
|
799
|
+
*
|
|
800
|
+
* Features:
|
|
801
|
+
* - Compact design with floating label
|
|
802
|
+
* - Auto-generate slug from source field
|
|
803
|
+
* - Manual editing support
|
|
804
|
+
* - InputGroup with generate button
|
|
805
|
+
* - Form integration via control prop
|
|
806
|
+
*/
|
|
807
|
+
function CompactSlugField({ control, name, description, required, label, placeholder = "my-page-slug", disabled, icon, sourceValue, onGenerate, className, inputClassName, onValueChange, value, onChange, error, ref, ...props }) {
|
|
808
|
+
const handleGenerate = (currentValue, fieldOnChange) => {
|
|
809
|
+
const newSlug = onGenerate ? onGenerate(sourceValue || "") : generateSlug(sourceValue || "");
|
|
810
|
+
fieldOnChange(newSlug);
|
|
811
|
+
onValueChange?.(newSlug);
|
|
812
|
+
};
|
|
813
|
+
const renderInput = (fieldValue, fieldOnChange, isDisabled, fieldState) => {
|
|
814
|
+
const inputProps = {
|
|
815
|
+
ref,
|
|
816
|
+
id: name,
|
|
817
|
+
type: "text",
|
|
818
|
+
disabled: isDisabled,
|
|
819
|
+
placeholder: label ? void 0 : placeholder,
|
|
820
|
+
value: fieldValue || "",
|
|
821
|
+
onChange: (e) => {
|
|
822
|
+
const newValue = e.target.value;
|
|
823
|
+
fieldOnChange(newValue);
|
|
824
|
+
onValueChange?.(newValue);
|
|
825
|
+
},
|
|
826
|
+
className: cn("h-11 text-sm dark:bg-transparent", COMPACT_FOCUS, fieldState?.error && COMPACT_FOCUS_ERROR, inputClassName),
|
|
827
|
+
...props
|
|
828
|
+
};
|
|
829
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [icon && /* @__PURE__ */ jsx(Field.Icon, { children: icon }), /* @__PURE__ */ jsxs(InputGroup, {
|
|
830
|
+
className: cn("h-11", INPUT_GROUP_FOCUS),
|
|
831
|
+
children: [/* @__PURE__ */ jsx(InputGroupInput, {
|
|
832
|
+
...inputProps,
|
|
833
|
+
className: cn(inputProps.className, icon && "pl-9")
|
|
834
|
+
}), /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
835
|
+
align: "inline-end",
|
|
836
|
+
children: /* @__PURE__ */ jsxs(InputGroupButton, {
|
|
837
|
+
type: "button",
|
|
838
|
+
size: "sm",
|
|
839
|
+
onClick: () => handleGenerate(fieldValue, fieldOnChange),
|
|
840
|
+
disabled: isDisabled || !sourceValue,
|
|
841
|
+
title: "Generate slug from name",
|
|
842
|
+
children: [/* @__PURE__ */ jsx(Wand2, { className: "h-4 w-4" }), "Generate"]
|
|
843
|
+
})
|
|
844
|
+
})]
|
|
845
|
+
})] });
|
|
846
|
+
};
|
|
847
|
+
if (control && name) return /* @__PURE__ */ jsx(Controller, {
|
|
848
|
+
name,
|
|
849
|
+
control,
|
|
850
|
+
render: ({ field, fieldState }) => /* @__PURE__ */ jsxs(Field.Root, {
|
|
851
|
+
className,
|
|
852
|
+
disabled,
|
|
853
|
+
invalid: !!fieldState?.error,
|
|
854
|
+
children: [
|
|
855
|
+
/* @__PURE__ */ jsxs("div", {
|
|
856
|
+
className: "relative group",
|
|
857
|
+
"data-floated": !!field.value || void 0,
|
|
858
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, {
|
|
859
|
+
className: icon ? "left-9 group-focus-within:left-3 group-data-[floated]:left-3" : void 0,
|
|
860
|
+
children: label
|
|
861
|
+
}), renderInput(field.value, field.onChange, disabled, fieldState)]
|
|
862
|
+
}),
|
|
863
|
+
fieldState?.error && /* @__PURE__ */ jsx(Field.Error, { children: fieldState.error.message }),
|
|
864
|
+
/* @__PURE__ */ jsx(Field.Description, { children: description })
|
|
865
|
+
]
|
|
866
|
+
})
|
|
867
|
+
});
|
|
868
|
+
const handleDirectChange = (newValue) => {
|
|
869
|
+
onChange?.({ target: { value: newValue } });
|
|
870
|
+
onValueChange?.(newValue);
|
|
871
|
+
};
|
|
872
|
+
return /* @__PURE__ */ jsxs(Field.Root, {
|
|
873
|
+
className,
|
|
874
|
+
disabled,
|
|
875
|
+
invalid: !!error,
|
|
876
|
+
children: [
|
|
877
|
+
/* @__PURE__ */ jsxs("div", {
|
|
878
|
+
className: "relative group",
|
|
879
|
+
"data-floated": !!value || void 0,
|
|
880
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, {
|
|
881
|
+
className: icon ? "left-9 group-focus-within:left-3 group-data-[floated]:left-3" : void 0,
|
|
882
|
+
children: label
|
|
883
|
+
}), renderInput(value, handleDirectChange, disabled, { error: error ? { message: error } : void 0 })]
|
|
884
|
+
}),
|
|
885
|
+
error && /* @__PURE__ */ jsx(Field.Error, { children: error }),
|
|
886
|
+
/* @__PURE__ */ jsx(Field.Description, { children: description })
|
|
887
|
+
]
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
//#endregion
|
|
892
|
+
export { CompactInput, CompactNumberInput, CompactSelect, CompactSlugField, CompactTagChoice, CompactTextarea, generateSlug };
|