@classytic/fluid 0.4.2 → 0.5.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/README.md +21 -1
- package/dist/client/calendar.d.mts +1 -2
- package/dist/client/calendar.mjs +4 -4
- package/dist/client/color-picker.d.mts +41 -25
- package/dist/client/color-picker.mjs +121 -73
- package/dist/client/core.d.mts +243 -557
- package/dist/client/core.mjs +351 -1462
- package/dist/client/error.d.mts +41 -41
- package/dist/client/error.mjs +35 -35
- package/dist/client/gallery.d.mts +33 -33
- package/dist/client/gallery.mjs +128 -127
- package/dist/client/hooks.d.mts +57 -39
- package/dist/client/hooks.mjs +29 -7
- package/dist/client/spreadsheet.d.mts +28 -28
- package/dist/client/spreadsheet.mjs +77 -77
- package/dist/client/table.d.mts +66 -33
- package/dist/client/table.mjs +87 -54
- package/dist/client/theme.mjs +1 -1
- package/dist/command.d.mts +6 -4
- package/dist/command.mjs +3 -3
- package/dist/compact.d.mts +97 -95
- package/dist/compact.mjs +336 -322
- package/dist/dashboard.d.mts +614 -422
- package/dist/dashboard.mjs +1051 -762
- package/dist/{dropdown-wrapper-B86u9Fri.mjs → dropdown-wrapper-B9nRDUlz.mjs} +25 -35
- package/dist/forms.d.mts +1037 -972
- package/dist/forms.mjs +2849 -2721
- package/dist/index.d.mts +218 -152
- package/dist/index.mjs +357 -264
- package/dist/layouts.d.mts +94 -94
- package/dist/layouts.mjs +115 -110
- package/dist/phone-input-B9_XPNvv.mjs +429 -0
- package/dist/phone-input-CLH_UjQZ.d.mts +31 -0
- package/dist/{search-context-DR7DBs7S.mjs → search-context-1g3ZmOvx.mjs} +1 -1
- package/dist/search.d.mts +168 -164
- package/dist/search.mjs +305 -301
- package/dist/{sheet-wrapper-C13Y-Q6w.mjs → sheet-wrapper-B2uxookb.mjs} +1 -1
- package/dist/timeline-Bgu1mIe9.d.mts +373 -0
- package/dist/timeline-HJtWf4Op.mjs +804 -0
- package/dist/{use-base-search-BGgWnWaF.d.mts → use-base-search-DFC4QKYU.d.mts} +1 -1
- package/dist/{use-media-query-BnVNIKT4.mjs → use-media-query-ChLfFChU.mjs} +6 -7
- package/package.json +2 -2
- /package/dist/{api-pagination-CJ0vR_w6.d.mts → api-pagination-C30ser2L.d.mts} +0 -0
- /package/dist/{filter-utils-DqMmy_v-.mjs → filter-utils-BGIvtq1R.mjs} +0 -0
- /package/dist/{filter-utils-IZ0GtuPo.d.mts → filter-utils-DOFTBWm1.d.mts} +0 -0
- /package/dist/{use-debounce-xmZucz5e.mjs → use-debounce-BNoNiEon.mjs} +0 -0
- /package/dist/{use-keyboard-shortcut-Bl6YM5Q7.mjs → use-keyboard-shortcut-C_Vk-36P.mjs} +0 -0
- /package/dist/{use-keyboard-shortcut-_mRCh3QO.d.mts → use-keyboard-shortcut-Q4CSPzSI.d.mts} +0 -0
- /package/dist/{use-mobile-BX3SQVo2.mjs → use-mobile-CnEmFiQx.mjs} +0 -0
- /package/dist/{use-scroll-detection-CsgsQYvy.mjs → use-scroll-detection-BKfqkmEC.mjs} +0 -0
- /package/dist/{utils-CDue7cEt.d.mts → utils-rqvYP1by.d.mts} +0 -0
package/dist/compact.mjs
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { t as cn } from "./utils-DQ5SCVoW.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import { Check, Minus, Plus, Wand2, X } from "lucide-react";
|
|
7
|
-
import {
|
|
4
|
+
import React, { createContext, memo, useContext, useId, useMemo, useState } from "react";
|
|
5
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { Check, ChevronsUpDown, Minus, Plus, Wand2, X } from "lucide-react";
|
|
7
|
+
import { Badge } from "@/components/ui/badge";
|
|
8
8
|
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupTextarea } from "@/components/ui/input-group";
|
|
9
9
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
|
10
|
-
import { Badge } from "@/components/ui/badge";
|
|
11
10
|
import { Input } from "@/components/ui/input";
|
|
12
11
|
import { Controller } from "react-hook-form";
|
|
13
|
-
import { Textarea } from "@/components/ui/textarea";
|
|
14
12
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
13
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
15
14
|
|
|
16
15
|
//#region src/components/compact/field.tsx
|
|
17
16
|
const FieldContext = createContext(null);
|
|
@@ -222,193 +221,6 @@ function CompactInput({ control, name, description, required, label, placeholder
|
|
|
222
221
|
});
|
|
223
222
|
}
|
|
224
223
|
|
|
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
224
|
//#endregion
|
|
413
225
|
//#region src/components/compact/compact-number-input.tsx
|
|
414
226
|
/**
|
|
@@ -633,16 +445,206 @@ function CompactNumberInputInternal({ label, placeholder, min = 0, max, step = 1
|
|
|
633
445
|
}
|
|
634
446
|
|
|
635
447
|
//#endregion
|
|
636
|
-
//#region src/components/compact/compact-
|
|
448
|
+
//#region src/components/compact/compact-select.tsx
|
|
449
|
+
const CLEAR_VALUE = "__CLEAR__";
|
|
450
|
+
/**
|
|
451
|
+
* CompactSelect - Floating-label select dropdown
|
|
452
|
+
*
|
|
453
|
+
* @example
|
|
454
|
+
* <CompactSelect
|
|
455
|
+
* label="Status"
|
|
456
|
+
* items={[
|
|
457
|
+
* { value: "active", label: "Active" },
|
|
458
|
+
* { value: "inactive", label: "Inactive" }
|
|
459
|
+
* ]}
|
|
460
|
+
* clearable
|
|
461
|
+
* />
|
|
462
|
+
*/
|
|
463
|
+
function CompactSelect({ control, name, description, required, label, placeholder = "Select option", disabled, items = [], clearable, className, onValueChange, value, error, ref, ...props }) {
|
|
464
|
+
const displayItems = useMemo(() => {
|
|
465
|
+
if (clearable) return [{
|
|
466
|
+
value: CLEAR_VALUE,
|
|
467
|
+
label: placeholder
|
|
468
|
+
}, ...items];
|
|
469
|
+
return items;
|
|
470
|
+
}, [
|
|
471
|
+
items,
|
|
472
|
+
clearable,
|
|
473
|
+
placeholder
|
|
474
|
+
]);
|
|
475
|
+
const handleValueChange = (newValue, fieldOnChange) => {
|
|
476
|
+
const actualValue = newValue === CLEAR_VALUE ? "" : newValue;
|
|
477
|
+
if (fieldOnChange) fieldOnChange(actualValue);
|
|
478
|
+
onValueChange?.(actualValue);
|
|
479
|
+
};
|
|
480
|
+
const toSelectValue = (val) => {
|
|
481
|
+
if (val === "") return CLEAR_VALUE;
|
|
482
|
+
return val;
|
|
483
|
+
};
|
|
484
|
+
const renderSelect = (selectValue, onChange, isDisabled, isInvalid, fieldId) => /* @__PURE__ */ jsxs(Select, {
|
|
485
|
+
value: toSelectValue(selectValue),
|
|
486
|
+
onValueChange: (val) => handleValueChange(val, onChange),
|
|
487
|
+
disabled: isDisabled,
|
|
488
|
+
...props,
|
|
489
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
490
|
+
ref,
|
|
491
|
+
id: fieldId,
|
|
492
|
+
className: cn("w-full data-[size=default]:h-11 text-sm dark:bg-transparent", COMPACT_SELECT_FOCUS, isInvalid && COMPACT_SELECT_FOCUS_ERROR, className),
|
|
493
|
+
"aria-invalid": isInvalid || void 0,
|
|
494
|
+
"aria-describedby": [description ? `${fieldId ?? name}-description` : void 0, isInvalid ? `${fieldId ?? name}-error` : void 0].filter(Boolean).join(" ") || void 0,
|
|
495
|
+
children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "" })
|
|
496
|
+
}), /* @__PURE__ */ jsx(SelectContent, { children: displayItems.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
497
|
+
className: "py-6 text-center text-sm text-muted-foreground",
|
|
498
|
+
children: "No options available"
|
|
499
|
+
}) : displayItems.map((item) => /* @__PURE__ */ jsx(SelectItem, {
|
|
500
|
+
value: item.value,
|
|
501
|
+
disabled: item.disabled,
|
|
502
|
+
children: item.label
|
|
503
|
+
}, item.value)) })]
|
|
504
|
+
});
|
|
505
|
+
if (control && name) return /* @__PURE__ */ jsx(Controller, {
|
|
506
|
+
name,
|
|
507
|
+
control,
|
|
508
|
+
render: ({ field, fieldState }) => /* @__PURE__ */ jsxs(Field.Root, {
|
|
509
|
+
disabled,
|
|
510
|
+
invalid: !!fieldState?.error,
|
|
511
|
+
children: [
|
|
512
|
+
/* @__PURE__ */ jsxs("div", {
|
|
513
|
+
className: "relative group",
|
|
514
|
+
"data-floated": !!field.value || void 0,
|
|
515
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, { children: label }), renderSelect(field.value, field.onChange, disabled, !!fieldState?.error, name)]
|
|
516
|
+
}),
|
|
517
|
+
fieldState?.error && /* @__PURE__ */ jsx(Field.Error, { children: fieldState.error.message }),
|
|
518
|
+
description && /* @__PURE__ */ jsx(Field.Description, { children: description })
|
|
519
|
+
]
|
|
520
|
+
})
|
|
521
|
+
});
|
|
522
|
+
return /* @__PURE__ */ jsxs(Field.Root, {
|
|
523
|
+
disabled,
|
|
524
|
+
invalid: !!error,
|
|
525
|
+
children: [
|
|
526
|
+
/* @__PURE__ */ jsxs("div", {
|
|
527
|
+
className: "relative group",
|
|
528
|
+
"data-floated": !!value || void 0,
|
|
529
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, { children: label }), renderSelect(value, () => {}, disabled, !!error)]
|
|
530
|
+
}),
|
|
531
|
+
error && /* @__PURE__ */ jsx(Field.Error, { children: error }),
|
|
532
|
+
description && /* @__PURE__ */ jsx(Field.Description, { children: description })
|
|
533
|
+
]
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
//#endregion
|
|
538
|
+
//#region src/components/compact/compact-slug-field.tsx
|
|
539
|
+
/**
|
|
540
|
+
* Generates a URL-friendly slug from a string
|
|
541
|
+
*/
|
|
542
|
+
function generateSlug(text) {
|
|
543
|
+
if (!text) return "";
|
|
544
|
+
return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
545
|
+
}
|
|
637
546
|
/**
|
|
638
|
-
*
|
|
547
|
+
* CompactSlugField - Compact slug input with auto-generation
|
|
639
548
|
*
|
|
640
549
|
* Features:
|
|
641
|
-
* -
|
|
642
|
-
* -
|
|
643
|
-
* -
|
|
550
|
+
* - Compact design with floating label
|
|
551
|
+
* - Auto-generate slug from source field
|
|
552
|
+
* - Manual editing support
|
|
553
|
+
* - InputGroup with generate button
|
|
644
554
|
* - Form integration via control prop
|
|
645
|
-
|
|
555
|
+
*/
|
|
556
|
+
function CompactSlugField({ control, name, description, required, label, placeholder = "my-page-slug", disabled, icon, sourceValue, onGenerate, className, inputClassName, onValueChange, value, onChange, error, ref, ...props }) {
|
|
557
|
+
const handleGenerate = (currentValue, fieldOnChange) => {
|
|
558
|
+
const newSlug = onGenerate ? onGenerate(sourceValue || "") : generateSlug(sourceValue || "");
|
|
559
|
+
fieldOnChange(newSlug);
|
|
560
|
+
onValueChange?.(newSlug);
|
|
561
|
+
};
|
|
562
|
+
const renderInput = (fieldValue, fieldOnChange, isDisabled, fieldState) => {
|
|
563
|
+
const inputProps = {
|
|
564
|
+
ref,
|
|
565
|
+
id: name,
|
|
566
|
+
type: "text",
|
|
567
|
+
disabled: isDisabled,
|
|
568
|
+
placeholder: label ? void 0 : placeholder,
|
|
569
|
+
value: fieldValue || "",
|
|
570
|
+
onChange: (e) => {
|
|
571
|
+
const newValue = e.target.value;
|
|
572
|
+
fieldOnChange(newValue);
|
|
573
|
+
onValueChange?.(newValue);
|
|
574
|
+
},
|
|
575
|
+
className: cn("h-11 text-sm dark:bg-transparent", COMPACT_FOCUS, fieldState?.error && COMPACT_FOCUS_ERROR, inputClassName),
|
|
576
|
+
...props
|
|
577
|
+
};
|
|
578
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [icon && /* @__PURE__ */ jsx(Field.Icon, { children: icon }), /* @__PURE__ */ jsxs(InputGroup, {
|
|
579
|
+
className: cn("h-11", INPUT_GROUP_FOCUS),
|
|
580
|
+
children: [/* @__PURE__ */ jsx(InputGroupInput, {
|
|
581
|
+
...inputProps,
|
|
582
|
+
className: cn(inputProps.className, icon && "pl-9")
|
|
583
|
+
}), /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
584
|
+
align: "inline-end",
|
|
585
|
+
children: /* @__PURE__ */ jsxs(InputGroupButton, {
|
|
586
|
+
type: "button",
|
|
587
|
+
size: "sm",
|
|
588
|
+
onClick: () => handleGenerate(fieldValue, fieldOnChange),
|
|
589
|
+
disabled: isDisabled || !sourceValue,
|
|
590
|
+
title: "Generate slug from name",
|
|
591
|
+
children: [/* @__PURE__ */ jsx(Wand2, { className: "h-4 w-4" }), "Generate"]
|
|
592
|
+
})
|
|
593
|
+
})]
|
|
594
|
+
})] });
|
|
595
|
+
};
|
|
596
|
+
if (control && name) return /* @__PURE__ */ jsx(Controller, {
|
|
597
|
+
name,
|
|
598
|
+
control,
|
|
599
|
+
render: ({ field, fieldState }) => /* @__PURE__ */ jsxs(Field.Root, {
|
|
600
|
+
className,
|
|
601
|
+
disabled,
|
|
602
|
+
invalid: !!fieldState?.error,
|
|
603
|
+
children: [
|
|
604
|
+
/* @__PURE__ */ jsxs("div", {
|
|
605
|
+
className: "relative group",
|
|
606
|
+
"data-floated": !!field.value || void 0,
|
|
607
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, {
|
|
608
|
+
className: icon ? "left-9 group-focus-within:left-3 group-data-[floated]:left-3" : void 0,
|
|
609
|
+
children: label
|
|
610
|
+
}), renderInput(field.value, field.onChange, disabled, fieldState)]
|
|
611
|
+
}),
|
|
612
|
+
fieldState?.error && /* @__PURE__ */ jsx(Field.Error, { children: fieldState.error.message }),
|
|
613
|
+
/* @__PURE__ */ jsx(Field.Description, { children: description })
|
|
614
|
+
]
|
|
615
|
+
})
|
|
616
|
+
});
|
|
617
|
+
const handleDirectChange = (newValue) => {
|
|
618
|
+
onChange?.({ target: { value: newValue } });
|
|
619
|
+
onValueChange?.(newValue);
|
|
620
|
+
};
|
|
621
|
+
return /* @__PURE__ */ jsxs(Field.Root, {
|
|
622
|
+
className,
|
|
623
|
+
disabled,
|
|
624
|
+
invalid: !!error,
|
|
625
|
+
children: [
|
|
626
|
+
/* @__PURE__ */ jsxs("div", {
|
|
627
|
+
className: "relative group",
|
|
628
|
+
"data-floated": !!value || void 0,
|
|
629
|
+
children: [label && /* @__PURE__ */ jsx(Field.Label, {
|
|
630
|
+
className: icon ? "left-9 group-focus-within:left-3 group-data-[floated]:left-3" : void 0,
|
|
631
|
+
children: label
|
|
632
|
+
}), renderInput(value, handleDirectChange, disabled, { error: error ? { message: error } : void 0 })]
|
|
633
|
+
}),
|
|
634
|
+
error && /* @__PURE__ */ jsx(Field.Error, { children: error }),
|
|
635
|
+
/* @__PURE__ */ jsx(Field.Description, { children: description })
|
|
636
|
+
]
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
//#endregion
|
|
641
|
+
//#region src/components/compact/compact-tag-choice.tsx
|
|
642
|
+
/**
|
|
643
|
+
* CompactTagChoice - A compact tag selection input with floating label
|
|
644
|
+
*
|
|
645
|
+
* Uses a trigger button that shows selected count or placeholder,
|
|
646
|
+
* with a popover dropdown listing available choices.
|
|
647
|
+
* Selected items display as inline badges below the trigger.
|
|
646
648
|
*/
|
|
647
649
|
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
650
|
if (control && name) return /* @__PURE__ */ jsx(Controller, {
|
|
@@ -687,17 +689,20 @@ function CompactTagChoice({ control, name, label, description, placeholder = "Se
|
|
|
687
689
|
}
|
|
688
690
|
function CompactTagChoiceInternal({ label, placeholder, disabled, value = [], onChange, choices = [], maxSelections, error, className, containerClassName, labelClassName, inputClassName, ref }) {
|
|
689
691
|
const [open, setOpen] = useState(false);
|
|
690
|
-
const
|
|
691
|
-
const
|
|
692
|
+
const hasValue = value.length > 0;
|
|
693
|
+
const canAdd = !maxSelections || value.length < maxSelections;
|
|
694
|
+
choices.filter((c) => !value.includes(c.value));
|
|
692
695
|
const addChoice = (choiceValue) => {
|
|
693
696
|
if (value.includes(choiceValue)) return;
|
|
694
697
|
if (maxSelections && value.length >= maxSelections) return;
|
|
695
|
-
|
|
696
|
-
onChange?.(updated);
|
|
698
|
+
onChange?.([...value, choiceValue]);
|
|
697
699
|
};
|
|
698
700
|
const removeChoice = (choiceValue) => {
|
|
699
|
-
|
|
700
|
-
|
|
701
|
+
onChange?.(value.filter((v) => v !== choiceValue));
|
|
702
|
+
};
|
|
703
|
+
const toggleChoice = (choiceValue) => {
|
|
704
|
+
if (value.includes(choiceValue)) removeChoice(choiceValue);
|
|
705
|
+
else addChoice(choiceValue);
|
|
701
706
|
};
|
|
702
707
|
return /* @__PURE__ */ jsxs(Field.Root, {
|
|
703
708
|
className: containerClassName,
|
|
@@ -705,143 +710,152 @@ function CompactTagChoiceInternal({ label, placeholder, disabled, value = [], on
|
|
|
705
710
|
invalid: !!error,
|
|
706
711
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
707
712
|
className: "relative group",
|
|
708
|
-
"data-floated":
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
713
|
+
"data-floated": hasValue || open || void 0,
|
|
714
|
+
ref,
|
|
715
|
+
children: [
|
|
716
|
+
label && /* @__PURE__ */ jsx(Field.Label, {
|
|
717
|
+
className: labelClassName,
|
|
718
|
+
children: label
|
|
719
|
+
}),
|
|
720
|
+
/* @__PURE__ */ jsxs(Popover, {
|
|
721
|
+
open,
|
|
722
|
+
onOpenChange: disabled ? void 0 : setOpen,
|
|
723
|
+
children: [/* @__PURE__ */ jsxs(PopoverTrigger, {
|
|
724
|
+
render: /* @__PURE__ */ jsx("button", {
|
|
725
|
+
type: "button",
|
|
726
|
+
disabled,
|
|
727
|
+
className: cn("flex h-11 w-full items-center justify-between rounded-md border border-input bg-card px-3 text-sm", "transition-all duration-200 hover:border-ring/50", COMPACT_FOCUS, error && "border-destructive focus-visible:border-destructive focus-visible:outline-destructive", disabled && "opacity-50 cursor-not-allowed", inputClassName)
|
|
728
|
+
}),
|
|
729
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
730
|
+
className: cn("truncate", !hasValue && "text-transparent"),
|
|
731
|
+
children: hasValue ? `${value.length} selected` : label || placeholder
|
|
732
|
+
}), /* @__PURE__ */ jsx(ChevronsUpDown, { className: "size-3.5 shrink-0 opacity-50" })]
|
|
733
|
+
}), /* @__PURE__ */ jsx(PopoverContent, {
|
|
734
|
+
className: "w-[var(--popover-trigger-width)] p-1",
|
|
735
|
+
align: "start",
|
|
736
|
+
children: choices.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
737
|
+
className: "px-2 py-3 text-sm text-muted-foreground text-center",
|
|
738
|
+
children: "No options available"
|
|
739
|
+
}) : /* @__PURE__ */ jsxs("div", {
|
|
740
|
+
className: "space-y-0.5",
|
|
741
|
+
children: [choices.map((choice) => {
|
|
742
|
+
const isSelected = value.includes(choice.value);
|
|
743
|
+
const isDisabledChoice = !isSelected && !canAdd;
|
|
744
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
724
745
|
type: "button",
|
|
725
|
-
|
|
726
|
-
onClick: (
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
746
|
+
disabled: isDisabledChoice,
|
|
747
|
+
onClick: () => toggleChoice(choice.value),
|
|
748
|
+
className: cn("flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-left", "hover:bg-accent hover:text-accent-foreground cursor-pointer", "focus-visible:outline-none focus-visible:bg-accent", isSelected && "bg-accent/50", isDisabledChoice && "opacity-40 cursor-not-allowed"),
|
|
749
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
750
|
+
className: cn("flex size-4 shrink-0 items-center justify-center rounded-sm border", isSelected ? "border-primary bg-primary text-primary-foreground" : "border-muted-foreground/30"),
|
|
751
|
+
children: isSelected && /* @__PURE__ */ jsx(Check, { className: "size-3" })
|
|
752
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
753
|
+
className: "flex-1",
|
|
754
|
+
children: choice.label
|
|
755
|
+
})]
|
|
756
|
+
}, choice.value);
|
|
757
|
+
}), maxSelections && /* @__PURE__ */ jsxs("div", {
|
|
758
|
+
className: "px-2 pt-1 pb-0.5 text-[11px] text-muted-foreground border-t mt-1",
|
|
759
|
+
children: [
|
|
760
|
+
value.length,
|
|
761
|
+
"/",
|
|
762
|
+
maxSelections,
|
|
763
|
+
" selected"
|
|
764
|
+
]
|
|
765
|
+
})]
|
|
734
766
|
})
|
|
735
|
-
})
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
variant: "
|
|
743
|
-
className:
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
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
|
-
]
|
|
767
|
+
})]
|
|
768
|
+
}),
|
|
769
|
+
hasValue && /* @__PURE__ */ jsx("div", {
|
|
770
|
+
className: "flex flex-wrap gap-1 mt-1.5 px-0.5",
|
|
771
|
+
children: value.map((val) => {
|
|
772
|
+
const choice = choices.find((c) => c.value === val);
|
|
773
|
+
return /* @__PURE__ */ jsxs(Badge, {
|
|
774
|
+
variant: "secondary",
|
|
775
|
+
className: "gap-1 px-2 py-0.5 text-xs",
|
|
776
|
+
children: [choice?.label ?? val, !disabled && /* @__PURE__ */ jsx("button", {
|
|
777
|
+
type: "button",
|
|
778
|
+
className: "text-muted-foreground hover:text-foreground -mr-0.5",
|
|
779
|
+
onClick: () => removeChoice(val),
|
|
780
|
+
"aria-label": `Remove ${choice?.label ?? val}`,
|
|
781
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-3" })
|
|
782
|
+
})]
|
|
783
|
+
}, val);
|
|
781
784
|
})
|
|
782
|
-
|
|
783
|
-
|
|
785
|
+
})
|
|
786
|
+
]
|
|
784
787
|
}), error && /* @__PURE__ */ jsx(Field.Error, { children: error })]
|
|
785
788
|
});
|
|
786
789
|
}
|
|
787
790
|
|
|
788
791
|
//#endregion
|
|
789
|
-
//#region src/components/compact/compact-
|
|
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
|
-
}
|
|
792
|
+
//#region src/components/compact/compact-textarea.tsx
|
|
797
793
|
/**
|
|
798
|
-
*
|
|
794
|
+
* CompactTextarea - Enhanced textarea with InputGroup support
|
|
799
795
|
*
|
|
800
796
|
* Features:
|
|
801
|
-
* -
|
|
802
|
-
* -
|
|
803
|
-
* -
|
|
804
|
-
* -
|
|
805
|
-
* -
|
|
797
|
+
* - Floating label design
|
|
798
|
+
* - InputGroup support with addons
|
|
799
|
+
* - Character counter with maxLength
|
|
800
|
+
* - Controller integration for react-hook-form
|
|
801
|
+
* - Direct usage without form
|
|
806
802
|
*/
|
|
807
|
-
function
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
};
|
|
813
|
-
const renderInput = (fieldValue, fieldOnChange, isDisabled, fieldState) => {
|
|
814
|
-
const inputProps = {
|
|
803
|
+
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 }) {
|
|
804
|
+
const hasInputGroup = addonLeft || addonRight;
|
|
805
|
+
const renderTextarea = (fieldValue, fieldOnChange, isDisabled, fieldError) => {
|
|
806
|
+
const currentCharCount = fieldValue?.length || 0;
|
|
807
|
+
const textareaProps = {
|
|
815
808
|
ref,
|
|
816
809
|
id: name,
|
|
817
|
-
|
|
810
|
+
name,
|
|
818
811
|
disabled: isDisabled,
|
|
819
812
|
placeholder: label ? void 0 : placeholder,
|
|
813
|
+
rows,
|
|
820
814
|
value: fieldValue || "",
|
|
821
815
|
onChange: (e) => {
|
|
822
816
|
const newValue = e.target.value;
|
|
823
|
-
fieldOnChange(newValue);
|
|
817
|
+
fieldOnChange?.(newValue);
|
|
824
818
|
onValueChange?.(newValue);
|
|
825
819
|
},
|
|
826
|
-
className: cn("
|
|
820
|
+
className: cn("resize-none pt-3 text-sm dark:bg-transparent", COMPACT_FOCUS, fieldError && COMPACT_FOCUS_ERROR, inputClassName),
|
|
821
|
+
cols,
|
|
822
|
+
wrap,
|
|
823
|
+
autoComplete,
|
|
824
|
+
autoFocus,
|
|
825
|
+
maxLength,
|
|
826
|
+
minLength,
|
|
827
|
+
readOnly,
|
|
828
|
+
spellCheck,
|
|
827
829
|
...props
|
|
828
830
|
};
|
|
829
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
830
|
-
className:
|
|
831
|
-
children: [
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
disabled: isDisabled || !sourceValue,
|
|
841
|
-
title: "Generate slug from name",
|
|
842
|
-
children: [/* @__PURE__ */ jsx(Wand2, { className: "h-4 w-4" }), "Generate"]
|
|
831
|
+
if (hasInputGroup) return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(InputGroup, {
|
|
832
|
+
className: INPUT_GROUP_FOCUS,
|
|
833
|
+
children: [
|
|
834
|
+
addonLeft && /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
835
|
+
align: "block-start",
|
|
836
|
+
children: addonLeft
|
|
837
|
+
}),
|
|
838
|
+
/* @__PURE__ */ jsx(InputGroupTextarea, { ...textareaProps }),
|
|
839
|
+
addonRight && /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
840
|
+
align: "block-end",
|
|
841
|
+
children: addonRight
|
|
843
842
|
})
|
|
844
|
-
|
|
843
|
+
]
|
|
844
|
+
}), maxLength && currentCharCount > 0 && /* @__PURE__ */ jsxs("div", {
|
|
845
|
+
className: "text-xs text-muted-foreground mt-1.5 text-right",
|
|
846
|
+
children: [
|
|
847
|
+
currentCharCount,
|
|
848
|
+
"/",
|
|
849
|
+
maxLength
|
|
850
|
+
]
|
|
851
|
+
})] });
|
|
852
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Textarea, { ...textareaProps }), maxLength && currentCharCount > 0 && /* @__PURE__ */ jsxs("div", {
|
|
853
|
+
className: "text-xs text-muted-foreground mt-1.5 text-right",
|
|
854
|
+
children: [
|
|
855
|
+
currentCharCount,
|
|
856
|
+
"/",
|
|
857
|
+
maxLength
|
|
858
|
+
]
|
|
845
859
|
})] });
|
|
846
860
|
};
|
|
847
861
|
if (control && name) return /* @__PURE__ */ jsx(Controller, {
|
|
@@ -856,9 +870,9 @@ function CompactSlugField({ control, name, description, required, label, placeho
|
|
|
856
870
|
className: "relative group",
|
|
857
871
|
"data-floated": !!field.value || void 0,
|
|
858
872
|
children: [label && /* @__PURE__ */ jsx(Field.Label, {
|
|
859
|
-
className:
|
|
873
|
+
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",
|
|
860
874
|
children: label
|
|
861
|
-
}),
|
|
875
|
+
}), renderTextarea(field.value, field.onChange, disabled, fieldState?.error?.message)]
|
|
862
876
|
}),
|
|
863
877
|
fieldState?.error && /* @__PURE__ */ jsx(Field.Error, { children: fieldState.error.message }),
|
|
864
878
|
/* @__PURE__ */ jsx(Field.Description, { children: description })
|
|
@@ -878,9 +892,9 @@ function CompactSlugField({ control, name, description, required, label, placeho
|
|
|
878
892
|
className: "relative group",
|
|
879
893
|
"data-floated": !!value || void 0,
|
|
880
894
|
children: [label && /* @__PURE__ */ jsx(Field.Label, {
|
|
881
|
-
className:
|
|
895
|
+
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",
|
|
882
896
|
children: label
|
|
883
|
-
}),
|
|
897
|
+
}), renderTextarea(value, handleDirectChange, disabled, error)]
|
|
884
898
|
}),
|
|
885
899
|
error && /* @__PURE__ */ jsx(Field.Error, { children: error }),
|
|
886
900
|
/* @__PURE__ */ jsx(Field.Description, { children: description })
|