@comitor/ui 0.1.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/LICENSE +21 -0
- package/README.md +458 -0
- package/dist/chart/chart.d.ts +88 -0
- package/dist/chart/chart.js +199 -0
- package/dist/chart/chart.js.map +1 -0
- package/dist/chart/comitor-chart.d.ts +109 -0
- package/dist/chart/comitor-chart.js +189 -0
- package/dist/chart/comitor-chart.js.map +1 -0
- package/dist/chart/index.d.ts +4 -0
- package/dist/chart/index.js +3 -0
- package/dist/chart/index.js.map +1 -0
- package/dist/components/composite/avatar-group.d.ts +33 -0
- package/dist/components/composite/avatar-group.js +86 -0
- package/dist/components/composite/avatar-group.js.map +1 -0
- package/dist/components/composite/combobox.d.ts +70 -0
- package/dist/components/composite/combobox.js +291 -0
- package/dist/components/composite/combobox.js.map +1 -0
- package/dist/components/composite/command-palette.d.ts +56 -0
- package/dist/components/composite/command-palette.js +131 -0
- package/dist/components/composite/command-palette.js.map +1 -0
- package/dist/components/composite/confirm-dialog.d.ts +40 -0
- package/dist/components/composite/confirm-dialog.js +96 -0
- package/dist/components/composite/confirm-dialog.js.map +1 -0
- package/dist/components/composite/copy-button.d.ts +27 -0
- package/dist/components/composite/copy-button.js +80 -0
- package/dist/components/composite/copy-button.js.map +1 -0
- package/dist/components/composite/data-table.d.ts +136 -0
- package/dist/components/composite/data-table.js +300 -0
- package/dist/components/composite/data-table.js.map +1 -0
- package/dist/components/composite/date-picker.d.ts +77 -0
- package/dist/components/composite/date-picker.js +268 -0
- package/dist/components/composite/date-picker.js.map +1 -0
- package/dist/components/composite/empty-state.d.ts +38 -0
- package/dist/components/composite/empty-state.js +35 -0
- package/dist/components/composite/empty-state.js.map +1 -0
- package/dist/components/composite/filter-chips.d.ts +39 -0
- package/dist/components/composite/filter-chips.js +79 -0
- package/dist/components/composite/filter-chips.js.map +1 -0
- package/dist/components/composite/form-field.d.ts +58 -0
- package/dist/components/composite/form-field.js +81 -0
- package/dist/components/composite/form-field.js.map +1 -0
- package/dist/components/composite/form-section.d.ts +33 -0
- package/dist/components/composite/form-section.js +85 -0
- package/dist/components/composite/form-section.js.map +1 -0
- package/dist/components/composite/inline-edit.d.ts +38 -0
- package/dist/components/composite/inline-edit.js +165 -0
- package/dist/components/composite/inline-edit.js.map +1 -0
- package/dist/components/composite/keyboard-hint.d.ts +31 -0
- package/dist/components/composite/keyboard-hint.js +72 -0
- package/dist/components/composite/keyboard-hint.js.map +1 -0
- package/dist/components/composite/letter-avatar.d.ts +47 -0
- package/dist/components/composite/letter-avatar.js +66 -0
- package/dist/components/composite/letter-avatar.js.map +1 -0
- package/dist/components/composite/page-container.d.ts +25 -0
- package/dist/components/composite/page-container.js +23 -0
- package/dist/components/composite/page-container.js.map +1 -0
- package/dist/components/composite/page-header.d.ts +49 -0
- package/dist/components/composite/page-header.js +75 -0
- package/dist/components/composite/page-header.js.map +1 -0
- package/dist/components/composite/relative-time.d.ts +28 -0
- package/dist/components/composite/relative-time.js +44 -0
- package/dist/components/composite/relative-time.js.map +1 -0
- package/dist/components/composite/search-filter-bar.d.ts +38 -0
- package/dist/components/composite/search-filter-bar.js +135 -0
- package/dist/components/composite/search-filter-bar.js.map +1 -0
- package/dist/components/composite/stat-card.d.ts +48 -0
- package/dist/components/composite/stat-card.js +76 -0
- package/dist/components/composite/stat-card.js.map +1 -0
- package/dist/components/composite/status-config.d.ts +85 -0
- package/dist/components/composite/status-config.js +41 -0
- package/dist/components/composite/status-config.js.map +1 -0
- package/dist/components/composite/status-pill.d.ts +26 -0
- package/dist/components/composite/status-pill.js +59 -0
- package/dist/components/composite/status-pill.js.map +1 -0
- package/dist/components/composite/table-pagination.d.ts +31 -0
- package/dist/components/composite/table-pagination.js +103 -0
- package/dist/components/composite/table-pagination.js.map +1 -0
- package/dist/components/composite/text-utils.d.ts +38 -0
- package/dist/components/composite/text-utils.js +40 -0
- package/dist/components/composite/text-utils.js.map +1 -0
- package/dist/components/composite/toast.d.ts +56 -0
- package/dist/components/composite/toast.js +63 -0
- package/dist/components/composite/toast.js.map +1 -0
- package/dist/components/ui/accordion.d.ts +17 -0
- package/dist/components/ui/accordion.js +53 -0
- package/dist/components/ui/accordion.js.map +1 -0
- package/dist/components/ui/alert-dialog.d.ts +34 -0
- package/dist/components/ui/alert-dialog.js +103 -0
- package/dist/components/ui/alert-dialog.js.map +1 -0
- package/dist/components/ui/alert.d.ts +16 -0
- package/dist/components/ui/alert.js +50 -0
- package/dist/components/ui/alert.js.map +1 -0
- package/dist/components/ui/aspect-ratio.d.ts +7 -0
- package/dist/components/ui/aspect-ratio.js +10 -0
- package/dist/components/ui/aspect-ratio.js.map +1 -0
- package/dist/components/ui/avatar.d.ts +11 -0
- package/dist/components/ui/avatar.js +33 -0
- package/dist/components/ui/avatar.js.map +1 -0
- package/dist/components/ui/badge.d.ts +15 -0
- package/dist/components/ui/badge.js +37 -0
- package/dist/components/ui/badge.js.map +1 -0
- package/dist/components/ui/breadcrumb.d.ts +30 -0
- package/dist/components/ui/breadcrumb.js +89 -0
- package/dist/components/ui/breadcrumb.js.map +1 -0
- package/dist/components/ui/button-group.d.ts +21 -0
- package/dist/components/ui/button-group.js +62 -0
- package/dist/components/ui/button-group.js.map +1 -0
- package/dist/components/ui/button.d.ts +21 -0
- package/dist/components/ui/button.js +70 -0
- package/dist/components/ui/button.js.map +1 -0
- package/dist/components/ui/calendar.d.ts +26 -0
- package/dist/components/ui/calendar.js +148 -0
- package/dist/components/ui/calendar.js.map +1 -0
- package/dist/components/ui/card.d.ts +33 -0
- package/dist/components/ui/card.js +70 -0
- package/dist/components/ui/card.js.map +1 -0
- package/dist/components/ui/carousel.d.ts +46 -0
- package/dist/components/ui/carousel.js +173 -0
- package/dist/components/ui/carousel.js.map +1 -0
- package/dist/components/ui/checkbox.d.ts +17 -0
- package/dist/components/ui/checkbox.js +67 -0
- package/dist/components/ui/checkbox.js.map +1 -0
- package/dist/components/ui/collapsible.d.ts +17 -0
- package/dist/components/ui/collapsible.js +18 -0
- package/dist/components/ui/collapsible.js.map +1 -0
- package/dist/components/ui/command.d.ts +41 -0
- package/dist/components/ui/command.js +122 -0
- package/dist/components/ui/command.js.map +1 -0
- package/dist/components/ui/context-menu.d.ts +51 -0
- package/dist/components/ui/context-menu.js +166 -0
- package/dist/components/ui/context-menu.js.map +1 -0
- package/dist/components/ui/dialog.d.ts +48 -0
- package/dist/components/ui/dialog.js +131 -0
- package/dist/components/ui/dialog.js.map +1 -0
- package/dist/components/ui/drawer.d.ts +32 -0
- package/dist/components/ui/drawer.js +102 -0
- package/dist/components/ui/drawer.js.map +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +52 -0
- package/dist/components/ui/dropdown-menu.js +167 -0
- package/dist/components/ui/dropdown-menu.js.map +1 -0
- package/dist/components/ui/empty.d.ts +22 -0
- package/dist/components/ui/empty.js +87 -0
- package/dist/components/ui/empty.js.map +1 -0
- package/dist/components/ui/field.d.ts +46 -0
- package/dist/components/ui/field.js +197 -0
- package/dist/components/ui/field.js.map +1 -0
- package/dist/components/ui/form.d.ts +50 -0
- package/dist/components/ui/form.js +100 -0
- package/dist/components/ui/form.js.map +1 -0
- package/dist/components/ui/hover-card.d.ts +17 -0
- package/dist/components/ui/hover-card.js +31 -0
- package/dist/components/ui/hover-card.js.map +1 -0
- package/dist/components/ui/input-group.d.ts +26 -0
- package/dist/components/ui/input-group.js +148 -0
- package/dist/components/ui/input-group.js.map +1 -0
- package/dist/components/ui/input-otp.d.ts +23 -0
- package/dist/components/ui/input-otp.js +54 -0
- package/dist/components/ui/input-otp.js.map +1 -0
- package/dist/components/ui/input.d.ts +21 -0
- package/dist/components/ui/input.js +62 -0
- package/dist/components/ui/input.js.map +1 -0
- package/dist/components/ui/item.d.ts +39 -0
- package/dist/components/ui/item.js +142 -0
- package/dist/components/ui/item.js.map +1 -0
- package/dist/components/ui/kbd.d.ts +8 -0
- package/dist/components/ui/kbd.js +25 -0
- package/dist/components/ui/kbd.js.map +1 -0
- package/dist/components/ui/label.d.ts +7 -0
- package/dist/components/ui/label.js +21 -0
- package/dist/components/ui/label.js.map +1 -0
- package/dist/components/ui/menubar.d.ts +51 -0
- package/dist/components/ui/menubar.js +196 -0
- package/dist/components/ui/menubar.js.map +1 -0
- package/dist/components/ui/navigation-menu.d.ts +39 -0
- package/dist/components/ui/navigation-menu.js +123 -0
- package/dist/components/ui/navigation-menu.js.map +1 -0
- package/dist/components/ui/pagination.d.ts +40 -0
- package/dist/components/ui/pagination.js +95 -0
- package/dist/components/ui/pagination.js.map +1 -0
- package/dist/components/ui/popover.d.ts +19 -0
- package/dist/components/ui/popover.js +35 -0
- package/dist/components/ui/popover.js.map +1 -0
- package/dist/components/ui/progress.d.ts +12 -0
- package/dist/components/ui/progress.js +27 -0
- package/dist/components/ui/progress.js.map +1 -0
- package/dist/components/ui/radio-group.d.ts +9 -0
- package/dist/components/ui/radio-group.js +34 -0
- package/dist/components/ui/radio-group.js.map +1 -0
- package/dist/components/ui/resizable.d.ts +19 -0
- package/dist/components/ui/resizable.js +38 -0
- package/dist/components/ui/resizable.js.map +1 -0
- package/dist/components/ui/scroll-area.d.ts +13 -0
- package/dist/components/ui/scroll-area.js +46 -0
- package/dist/components/ui/scroll-area.js.map +1 -0
- package/dist/components/ui/select.d.ts +33 -0
- package/dist/components/ui/select.js +133 -0
- package/dist/components/ui/select.js.map +1 -0
- package/dist/components/ui/separator.d.ts +7 -0
- package/dist/components/ui/separator.js +23 -0
- package/dist/components/ui/separator.js.map +1 -0
- package/dist/components/ui/sheet.d.ts +38 -0
- package/dist/components/ui/sheet.js +96 -0
- package/dist/components/ui/sheet.js.map +1 -0
- package/dist/components/ui/skeleton.d.ts +6 -0
- package/dist/components/ui/skeleton.js +9 -0
- package/dist/components/ui/skeleton.js.map +1 -0
- package/dist/components/ui/slider.d.ts +12 -0
- package/dist/components/ui/slider.js +54 -0
- package/dist/components/ui/slider.js.map +1 -0
- package/dist/components/ui/sonner.d.ts +9 -0
- package/dist/components/ui/sonner.js +25 -0
- package/dist/components/ui/sonner.js.map +1 -0
- package/dist/components/ui/spinner.d.ts +6 -0
- package/dist/components/ui/spinner.js +10 -0
- package/dist/components/ui/spinner.js.map +1 -0
- package/dist/components/ui/switch.d.ts +14 -0
- package/dist/components/ui/switch.js +65 -0
- package/dist/components/ui/switch.js.map +1 -0
- package/dist/components/ui/table.d.ts +30 -0
- package/dist/components/ui/table.js +87 -0
- package/dist/components/ui/table.js.map +1 -0
- package/dist/components/ui/tabs.d.ts +35 -0
- package/dist/components/ui/tabs.js +54 -0
- package/dist/components/ui/tabs.js.map +1 -0
- package/dist/components/ui/textarea.d.ts +6 -0
- package/dist/components/ui/textarea.js +19 -0
- package/dist/components/ui/textarea.js.map +1 -0
- package/dist/components/ui/toggle-group.d.ts +18 -0
- package/dist/components/ui/toggle-group.js +52 -0
- package/dist/components/ui/toggle-group.js.map +1 -0
- package/dist/components/ui/toggle.d.ts +15 -0
- package/dist/components/ui/toggle.js +33 -0
- package/dist/components/ui/toggle.js.map +1 -0
- package/dist/components/ui/tooltip.d.ts +21 -0
- package/dist/components/ui/tooltip.js +38 -0
- package/dist/components/ui/tooltip.js.map +1 -0
- package/dist/form.d.ts +5 -0
- package/dist/form.js +2 -0
- package/dist/form.js.map +1 -0
- package/dist/index.d.ts +111 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cn.d.ts +16 -0
- package/dist/lib/cn.js +9 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/shell/app-header.d.ts +41 -0
- package/dist/shell/app-header.js +136 -0
- package/dist/shell/app-header.js.map +1 -0
- package/dist/shell/app-launcher.d.ts +33 -0
- package/dist/shell/app-launcher.js +189 -0
- package/dist/shell/app-launcher.js.map +1 -0
- package/dist/shell/app-shell.d.ts +44 -0
- package/dist/shell/app-shell.js +99 -0
- package/dist/shell/app-shell.js.map +1 -0
- package/dist/shell/comitor-lockup.d.ts +33 -0
- package/dist/shell/comitor-lockup.js +112 -0
- package/dist/shell/comitor-lockup.js.map +1 -0
- package/dist/shell/comitor-logo.d.ts +28 -0
- package/dist/shell/comitor-logo.js +92 -0
- package/dist/shell/comitor-logo.js.map +1 -0
- package/dist/shell/index.d.ts +24 -0
- package/dist/shell/index.js +23 -0
- package/dist/shell/index.js.map +1 -0
- package/dist/shell/mobile-menu.d.ts +26 -0
- package/dist/shell/mobile-menu.js +56 -0
- package/dist/shell/mobile-menu.js.map +1 -0
- package/dist/shell/notifications-button.d.ts +22 -0
- package/dist/shell/notifications-button.js +39 -0
- package/dist/shell/notifications-button.js.map +1 -0
- package/dist/shell/shell-context.d.ts +91 -0
- package/dist/shell/shell-context.js +233 -0
- package/dist/shell/shell-context.js.map +1 -0
- package/dist/shell/shell-link.d.ts +19 -0
- package/dist/shell/shell-link.js +18 -0
- package/dist/shell/shell-link.js.map +1 -0
- package/dist/shell/sidebar-group.d.ts +21 -0
- package/dist/shell/sidebar-group.js +43 -0
- package/dist/shell/sidebar-group.js.map +1 -0
- package/dist/shell/sidebar-menu-item.d.ts +27 -0
- package/dist/shell/sidebar-menu-item.js +152 -0
- package/dist/shell/sidebar-menu-item.js.map +1 -0
- package/dist/shell/sidebar.d.ts +26 -0
- package/dist/shell/sidebar.js +92 -0
- package/dist/shell/sidebar.js.map +1 -0
- package/dist/shell/theme-provider.d.ts +18 -0
- package/dist/shell/theme-provider.js +29 -0
- package/dist/shell/theme-provider.js.map +1 -0
- package/dist/shell/theme-toggle.d.ts +28 -0
- package/dist/shell/theme-toggle.js +67 -0
- package/dist/shell/theme-toggle.js.map +1 -0
- package/dist/shell/types.d.ts +168 -0
- package/dist/shell/types.js +38 -0
- package/dist/shell/types.js.map +1 -0
- package/dist/shell/user-menu.d.ts +42 -0
- package/dist/shell/user-menu.js +132 -0
- package/dist/shell/user-menu.js.map +1 -0
- package/dist/shell/utils.d.ts +62 -0
- package/dist/shell/utils.js +73 -0
- package/dist/shell/utils.js.map +1 -0
- package/dist/shell/workspace-switcher.d.ts +24 -0
- package/dist/shell/workspace-switcher.js +219 -0
- package/dist/shell/workspace-switcher.js.map +1 -0
- package/dist/tokens.d.ts +173 -0
- package/dist/tokens.js +161 -0
- package/dist/tokens.js.map +1 -0
- package/package.json +151 -0
- package/styles.css +357 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import {
|
|
6
|
+
Controller,
|
|
7
|
+
FormProvider,
|
|
8
|
+
useFormContext,
|
|
9
|
+
useFormState
|
|
10
|
+
} from "react-hook-form";
|
|
11
|
+
import { cn } from "../../lib/cn.js";
|
|
12
|
+
import { Label } from "./label.js";
|
|
13
|
+
const Form = FormProvider;
|
|
14
|
+
const FormFieldContext = React.createContext({});
|
|
15
|
+
const FormField = ({
|
|
16
|
+
...props
|
|
17
|
+
}) => {
|
|
18
|
+
return /* @__PURE__ */ jsx(FormFieldContext.Provider, { value: { name: props.name }, children: /* @__PURE__ */ jsx(Controller, { ...props }) });
|
|
19
|
+
};
|
|
20
|
+
const FormItemContext = React.createContext({});
|
|
21
|
+
const useFormField = () => {
|
|
22
|
+
const fieldContext = React.useContext(FormFieldContext);
|
|
23
|
+
const itemContext = React.useContext(FormItemContext);
|
|
24
|
+
if (!fieldContext?.name) {
|
|
25
|
+
throw new Error("useFormField ph\u1EA3i \u0111\u01B0\u1EE3c d\xF9ng b\xEAn trong <FormField>");
|
|
26
|
+
}
|
|
27
|
+
const { getFieldState } = useFormContext();
|
|
28
|
+
const formState = useFormState({ name: fieldContext.name });
|
|
29
|
+
const fieldState = getFieldState(fieldContext.name, formState);
|
|
30
|
+
const { id } = itemContext;
|
|
31
|
+
return {
|
|
32
|
+
id,
|
|
33
|
+
name: fieldContext.name,
|
|
34
|
+
formItemId: `${id}-form-item`,
|
|
35
|
+
formDescriptionId: `${id}-form-item-description`,
|
|
36
|
+
formMessageId: `${id}-form-item-message`,
|
|
37
|
+
...fieldState
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
function FormItem({ className, ...props }) {
|
|
41
|
+
const id = React.useId();
|
|
42
|
+
return /* @__PURE__ */ jsx(FormItemContext.Provider, { value: { id }, children: /* @__PURE__ */ jsx("div", { "data-slot": "form-item", className: cn("grid gap-2", className), ...props }) });
|
|
43
|
+
}
|
|
44
|
+
function FormLabel({ className, ...props }) {
|
|
45
|
+
const { error, formItemId } = useFormField();
|
|
46
|
+
return /* @__PURE__ */ jsx(
|
|
47
|
+
Label,
|
|
48
|
+
{
|
|
49
|
+
"data-slot": "form-label",
|
|
50
|
+
"data-error": !!error,
|
|
51
|
+
className: cn("data-[error=true]:text-destructive", className),
|
|
52
|
+
htmlFor: formItemId,
|
|
53
|
+
...props
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
function FormControl({ ...props }) {
|
|
58
|
+
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
|
|
59
|
+
return /* @__PURE__ */ jsx(
|
|
60
|
+
Slot,
|
|
61
|
+
{
|
|
62
|
+
"data-slot": "form-control",
|
|
63
|
+
id: formItemId,
|
|
64
|
+
"aria-describedby": !error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`,
|
|
65
|
+
"aria-invalid": !!error,
|
|
66
|
+
...props
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
function FormDescription({ className, ...props }) {
|
|
71
|
+
const { formDescriptionId } = useFormField();
|
|
72
|
+
return /* @__PURE__ */ jsx(
|
|
73
|
+
"p",
|
|
74
|
+
{
|
|
75
|
+
"data-slot": "form-description",
|
|
76
|
+
id: formDescriptionId,
|
|
77
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
78
|
+
...props
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
function FormMessage({ className, ...props }) {
|
|
83
|
+
const { error, formMessageId } = useFormField();
|
|
84
|
+
const body = error ? String(error?.message ?? "") : props.children;
|
|
85
|
+
if (!body) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
return /* @__PURE__ */ jsx("p", { "data-slot": "form-message", id: formMessageId, className: cn("text-destructive text-sm", className), ...props, children: body });
|
|
89
|
+
}
|
|
90
|
+
export {
|
|
91
|
+
Form,
|
|
92
|
+
FormControl,
|
|
93
|
+
FormDescription,
|
|
94
|
+
FormField,
|
|
95
|
+
FormItem,
|
|
96
|
+
FormLabel,
|
|
97
|
+
FormMessage,
|
|
98
|
+
useFormField
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=form.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/form.tsx"],"sourcesContent":["\"use client\";\n\nimport type * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport {\n Controller,\n type ControllerProps,\n type FieldPath,\n type FieldValues,\n FormProvider,\n useFormContext,\n useFormState\n} from \"react-hook-form\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Label } from \"./label.js\";\n\n/**\n * Binding shadcn ⇄ react-hook-form.\n *\n * ⚠ `react-hook-form` là peer OPTIONAL và có React context singleton: app và\n * package BẮT BUỘC dùng chung một bản, nếu không `useFormContext()` sẽ đọc nhầm\n * provider. Vì vậy file này phải nằm ở entry riêng `./form`, KHÔNG gom vào barrel `.`\n * (xem §3.2 + §8.2 của docs/spec-package.md).\n *\n * Lưu ý tên: `FormField` ở đây là wrapper của `Controller` (API shadcn), khác với\n * composite `FormField` (grid + label + error) ở Tier 2.\n */\n\nconst Form = FormProvider;\n\ntype FormFieldContextValue<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n> = {\n name: TName;\n};\n\nconst FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue);\n\nexport type FormProps = React.ComponentProps<typeof FormProvider>;\nexport type FormFieldProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n> = ControllerProps<TFieldValues, TName>;\nexport type FormItemProps = React.ComponentProps<\"div\">;\nexport type FormLabelProps = React.ComponentProps<typeof LabelPrimitive.Root>;\nexport type FormControlProps = React.ComponentProps<typeof Slot>;\nexport type FormDescriptionProps = React.ComponentProps<\"p\">;\nexport type FormMessageProps = React.ComponentProps<\"p\">;\n\nconst FormField = <\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n>({\n ...props\n}: FormFieldProps<TFieldValues, TName>) => {\n return (\n <FormFieldContext.Provider value={{ name: props.name }}>\n <Controller {...props} />\n </FormFieldContext.Provider>\n );\n};\n\ntype FormItemContextValue = {\n id: string;\n};\n\nconst FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue);\n\n/**\n * Gom id (a11y) + trạng thái lỗi của field đang bao quanh.\n * Khác bản comitor-ds: kiểm tra context TRƯỚC khi dùng (bản gốc throw sau khi đã\n * đọc `fieldContext.name`, nên lỗi thật bị che bởi crash khác).\n */\nconst useFormField = () => {\n const fieldContext = React.useContext(FormFieldContext);\n const itemContext = React.useContext(FormItemContext);\n\n if (!fieldContext?.name) {\n throw new Error(\"useFormField phải được dùng bên trong <FormField>\");\n }\n\n const { getFieldState } = useFormContext();\n const formState = useFormState({ name: fieldContext.name });\n const fieldState = getFieldState(fieldContext.name, formState);\n\n const { id } = itemContext;\n\n return {\n id,\n name: fieldContext.name,\n formItemId: `${id}-form-item`,\n formDescriptionId: `${id}-form-item-description`,\n formMessageId: `${id}-form-item-message`,\n ...fieldState\n };\n};\n\nfunction FormItem({ className, ...props }: FormItemProps) {\n const id = React.useId();\n\n return (\n <FormItemContext.Provider value={{ id }}>\n <div data-slot=\"form-item\" className={cn(\"grid gap-2\", className)} {...props} />\n </FormItemContext.Provider>\n );\n}\n\nfunction FormLabel({ className, ...props }: FormLabelProps) {\n const { error, formItemId } = useFormField();\n\n return (\n <Label\n data-slot=\"form-label\"\n data-error={!!error}\n className={cn(\"data-[error=true]:text-destructive\", className)}\n htmlFor={formItemId}\n {...props}\n />\n );\n}\n\nfunction FormControl({ ...props }: FormControlProps) {\n const { error, formItemId, formDescriptionId, formMessageId } = useFormField();\n\n return (\n <Slot\n data-slot=\"form-control\"\n id={formItemId}\n aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}\n aria-invalid={!!error}\n {...props}\n />\n );\n}\n\nfunction FormDescription({ className, ...props }: FormDescriptionProps) {\n const { formDescriptionId } = useFormField();\n\n return (\n <p\n data-slot=\"form-description\"\n id={formDescriptionId}\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nfunction FormMessage({ className, ...props }: FormMessageProps) {\n const { error, formMessageId } = useFormField();\n const body = error ? String(error?.message ?? \"\") : props.children;\n\n if (!body) {\n return null;\n }\n\n return (\n <p data-slot=\"form-message\" id={formMessageId} className={cn(\"text-destructive text-sm\", className)} {...props}>\n {body}\n </p>\n );\n}\n\nexport { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, useFormField };\n"],"mappings":";AA2DM;AAxDN,SAAS,YAAY;AACrB,YAAY,WAAW;AACvB;AAAA,EACE;AAAA,EAIA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,UAAU;AACnB,SAAS,aAAa;AActB,MAAM,OAAO;AASb,MAAM,mBAAmB,MAAM,cAAqC,CAAC,CAA0B;AAa/F,MAAM,YAAY,CAGhB;AAAA,EACA,GAAG;AACL,MAA2C;AACzC,SACE,oBAAC,iBAAiB,UAAjB,EAA0B,OAAO,EAAE,MAAM,MAAM,KAAK,GACnD,8BAAC,cAAY,GAAG,OAAO,GACzB;AAEJ;AAMA,MAAM,kBAAkB,MAAM,cAAoC,CAAC,CAAyB;AAO5F,MAAM,eAAe,MAAM;AACzB,QAAM,eAAe,MAAM,WAAW,gBAAgB;AACtD,QAAM,cAAc,MAAM,WAAW,eAAe;AAEpD,MAAI,CAAC,cAAc,MAAM;AACvB,UAAM,IAAI,MAAM,6EAAmD;AAAA,EACrE;AAEA,QAAM,EAAE,cAAc,IAAI,eAAe;AACzC,QAAM,YAAY,aAAa,EAAE,MAAM,aAAa,KAAK,CAAC;AAC1D,QAAM,aAAa,cAAc,aAAa,MAAM,SAAS;AAE7D,QAAM,EAAE,GAAG,IAAI;AAEf,SAAO;AAAA,IACL;AAAA,IACA,MAAM,aAAa;AAAA,IACnB,YAAY,GAAG,EAAE;AAAA,IACjB,mBAAmB,GAAG,EAAE;AAAA,IACxB,eAAe,GAAG,EAAE;AAAA,IACpB,GAAG;AAAA,EACL;AACF;AAEA,SAAS,SAAS,EAAE,WAAW,GAAG,MAAM,GAAkB;AACxD,QAAM,KAAK,MAAM,MAAM;AAEvB,SACE,oBAAC,gBAAgB,UAAhB,EAAyB,OAAO,EAAE,GAAG,GACpC,8BAAC,SAAI,aAAU,aAAY,WAAW,GAAG,cAAc,SAAS,GAAI,GAAG,OAAO,GAChF;AAEJ;AAEA,SAAS,UAAU,EAAE,WAAW,GAAG,MAAM,GAAmB;AAC1D,QAAM,EAAE,OAAO,WAAW,IAAI,aAAa;AAE3C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAY,CAAC,CAAC;AAAA,MACd,WAAW,GAAG,sCAAsC,SAAS;AAAA,MAC7D,SAAS;AAAA,MACR,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,YAAY,EAAE,GAAG,MAAM,GAAqB;AACnD,QAAM,EAAE,OAAO,YAAY,mBAAmB,cAAc,IAAI,aAAa;AAE7E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,IAAI;AAAA,MACJ,oBAAkB,CAAC,QAAQ,GAAG,iBAAiB,KAAK,GAAG,iBAAiB,IAAI,aAAa;AAAA,MACzF,gBAAc,CAAC,CAAC;AAAA,MACf,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,gBAAgB,EAAE,WAAW,GAAG,MAAM,GAAyB;AACtE,QAAM,EAAE,kBAAkB,IAAI,aAAa;AAE3C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,IAAI;AAAA,MACJ,WAAW,GAAG,iCAAiC,SAAS;AAAA,MACvD,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAqB;AAC9D,QAAM,EAAE,OAAO,cAAc,IAAI,aAAa;AAC9C,QAAM,OAAO,QAAQ,OAAO,OAAO,WAAW,EAAE,IAAI,MAAM;AAE1D,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,SACE,oBAAC,OAAE,aAAU,gBAAe,IAAI,eAAe,WAAW,GAAG,4BAA4B,SAAS,GAAI,GAAG,OACtG,gBACH;AAEJ;","names":[]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as HoverCardPrimitive from '@radix-ui/react-hover-card';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HoverCard — thẻ xem nhanh khi rê chuột (Radix HoverCard).
|
|
6
|
+
*
|
|
7
|
+
* Dùng cho hồ sơ người dùng, xem trước task/thẻ CRM. Chỉ kích hoạt bằng chuột nên
|
|
8
|
+
* nội dung bên trong phải là thông tin bổ trợ, không được là đường duy nhất để thao tác.
|
|
9
|
+
*/
|
|
10
|
+
type HoverCardProps = React.ComponentProps<typeof HoverCardPrimitive.Root>;
|
|
11
|
+
declare function HoverCard({ ...props }: HoverCardProps): React.JSX.Element;
|
|
12
|
+
type HoverCardTriggerProps = React.ComponentProps<typeof HoverCardPrimitive.Trigger>;
|
|
13
|
+
declare function HoverCardTrigger({ ...props }: HoverCardTriggerProps): React.JSX.Element;
|
|
14
|
+
type HoverCardContentProps = React.ComponentProps<typeof HoverCardPrimitive.Content>;
|
|
15
|
+
declare function HoverCardContent({ className, align, sideOffset, ...props }: HoverCardContentProps): React.JSX.Element;
|
|
16
|
+
|
|
17
|
+
export { HoverCard, HoverCardContent, type HoverCardContentProps, type HoverCardProps, HoverCardTrigger, type HoverCardTriggerProps };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
|
|
4
|
+
import { cn } from "../../lib/cn.js";
|
|
5
|
+
function HoverCard({ ...props }) {
|
|
6
|
+
return /* @__PURE__ */ jsx(HoverCardPrimitive.Root, { "data-slot": "hover-card", ...props });
|
|
7
|
+
}
|
|
8
|
+
function HoverCardTrigger({ ...props }) {
|
|
9
|
+
return /* @__PURE__ */ jsx(HoverCardPrimitive.Trigger, { "data-slot": "hover-card-trigger", ...props });
|
|
10
|
+
}
|
|
11
|
+
function HoverCardContent({ className, align = "center", sideOffset = 4, ...props }) {
|
|
12
|
+
return /* @__PURE__ */ jsx(HoverCardPrimitive.Portal, { "data-slot": "hover-card-portal", children: /* @__PURE__ */ jsx(
|
|
13
|
+
HoverCardPrimitive.Content,
|
|
14
|
+
{
|
|
15
|
+
"data-slot": "hover-card-content",
|
|
16
|
+
align,
|
|
17
|
+
sideOffset,
|
|
18
|
+
className: cn(
|
|
19
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-overlay outline-hidden",
|
|
20
|
+
className
|
|
21
|
+
),
|
|
22
|
+
...props
|
|
23
|
+
}
|
|
24
|
+
) });
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
HoverCard,
|
|
28
|
+
HoverCardContent,
|
|
29
|
+
HoverCardTrigger
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=hover-card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/hover-card.tsx"],"sourcesContent":["\"use client\";\n\nimport * as HoverCardPrimitive from \"@radix-ui/react-hover-card\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * HoverCard — thẻ xem nhanh khi rê chuột (Radix HoverCard).\n *\n * Dùng cho hồ sơ người dùng, xem trước task/thẻ CRM. Chỉ kích hoạt bằng chuột nên\n * nội dung bên trong phải là thông tin bổ trợ, không được là đường duy nhất để thao tác.\n */\n\nexport type HoverCardProps = React.ComponentProps<typeof HoverCardPrimitive.Root>;\n\nfunction HoverCard({ ...props }: HoverCardProps) {\n return <HoverCardPrimitive.Root data-slot=\"hover-card\" {...props} />;\n}\n\nexport type HoverCardTriggerProps = React.ComponentProps<typeof HoverCardPrimitive.Trigger>;\n\nfunction HoverCardTrigger({ ...props }: HoverCardTriggerProps) {\n return <HoverCardPrimitive.Trigger data-slot=\"hover-card-trigger\" {...props} />;\n}\n\nexport type HoverCardContentProps = React.ComponentProps<typeof HoverCardPrimitive.Content>;\n\nfunction HoverCardContent({ className, align = \"center\", sideOffset = 4, ...props }: HoverCardContentProps) {\n return (\n <HoverCardPrimitive.Portal data-slot=\"hover-card-portal\">\n <HoverCardPrimitive.Content\n data-slot=\"hover-card-content\"\n align={align}\n sideOffset={sideOffset}\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-overlay outline-hidden\",\n className\n )}\n {...props}\n />\n </HoverCardPrimitive.Portal>\n );\n}\n\nexport { HoverCard, HoverCardContent, HoverCardTrigger };\n"],"mappings":";AAgBS;AAdT,YAAY,wBAAwB;AAEpC,SAAS,UAAU;AAWnB,SAAS,UAAU,EAAE,GAAG,MAAM,GAAmB;AAC/C,SAAO,oBAAC,mBAAmB,MAAnB,EAAwB,aAAU,cAAc,GAAG,OAAO;AACpE;AAIA,SAAS,iBAAiB,EAAE,GAAG,MAAM,GAA0B;AAC7D,SAAO,oBAAC,mBAAmB,SAAnB,EAA2B,aAAU,sBAAsB,GAAG,OAAO;AAC/E;AAIA,SAAS,iBAAiB,EAAE,WAAW,QAAQ,UAAU,aAAa,GAAG,GAAG,MAAM,GAA0B;AAC1G,SACE,oBAAC,mBAAmB,QAAnB,EAA0B,aAAU,qBACnC;AAAA,IAAC,mBAAmB;AAAA,IAAnB;AAAA,MACC,aAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN,GACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
2
|
+
import { VariantProps } from 'class-variance-authority';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { ButtonProps } from './button.js';
|
|
5
|
+
|
|
6
|
+
type InputGroupProps = React.ComponentProps<"div">;
|
|
7
|
+
declare function InputGroup({ className, ...props }: InputGroupProps): React.JSX.Element;
|
|
8
|
+
declare const inputGroupAddonVariants: (props?: ({
|
|
9
|
+
align?: "inline-end" | "inline-start" | "block-end" | "block-start" | null | undefined;
|
|
10
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
11
|
+
interface InputGroupAddonProps extends React.ComponentProps<"div">, VariantProps<typeof inputGroupAddonVariants> {
|
|
12
|
+
}
|
|
13
|
+
declare function InputGroupAddon({ className, align, ...props }: InputGroupAddonProps): React.JSX.Element;
|
|
14
|
+
declare const inputGroupButtonVariants: (props?: ({
|
|
15
|
+
size?: "xs" | "sm" | "icon-sm" | "icon-xs" | null | undefined;
|
|
16
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
17
|
+
type InputGroupButtonProps = Omit<ButtonProps, "size"> & VariantProps<typeof inputGroupButtonVariants>;
|
|
18
|
+
declare function InputGroupButton({ className, type, variant, size, ...props }: InputGroupButtonProps): React.JSX.Element;
|
|
19
|
+
type InputGroupTextProps = React.ComponentProps<"span">;
|
|
20
|
+
declare function InputGroupText({ className, ...props }: InputGroupTextProps): React.JSX.Element;
|
|
21
|
+
type InputGroupInputProps = React.ComponentProps<"input">;
|
|
22
|
+
declare function InputGroupInput({ className, ...props }: InputGroupInputProps): React.JSX.Element;
|
|
23
|
+
type InputGroupTextareaProps = React.ComponentProps<"textarea">;
|
|
24
|
+
declare function InputGroupTextarea({ className, ...props }: InputGroupTextareaProps): React.JSX.Element;
|
|
25
|
+
|
|
26
|
+
export { InputGroup, InputGroupAddon, type InputGroupAddonProps, InputGroupButton, type InputGroupButtonProps, InputGroupInput, type InputGroupInputProps, type InputGroupProps, InputGroupText, type InputGroupTextProps, InputGroupTextarea, type InputGroupTextareaProps, inputGroupAddonVariants, inputGroupButtonVariants };
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { cn } from "../../lib/cn.js";
|
|
5
|
+
import { Button } from "./button.js";
|
|
6
|
+
import { Input } from "./input.js";
|
|
7
|
+
import { Textarea } from "./textarea.js";
|
|
8
|
+
function InputGroup({ className, ...props }) {
|
|
9
|
+
return /* @__PURE__ */ jsx(
|
|
10
|
+
"div",
|
|
11
|
+
{
|
|
12
|
+
"data-slot": "input-group",
|
|
13
|
+
role: "group",
|
|
14
|
+
className: cn(
|
|
15
|
+
"group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-colors duration-150 ease-out outline-none",
|
|
16
|
+
"h-9 has-[>textarea]:h-auto",
|
|
17
|
+
// Biến thể theo vị trí addon.
|
|
18
|
+
"has-[>[data-align=inline-start]]:[&>input]:pl-2",
|
|
19
|
+
"has-[>[data-align=inline-end]]:[&>input]:pr-2",
|
|
20
|
+
"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
|
|
21
|
+
"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
|
|
22
|
+
// Trạng thái focus.
|
|
23
|
+
"has-[[data-slot=input-group-control]:focus-visible]:border-gold-300 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-2 has-[[data-slot=input-group-control]:focus-visible]:ring-offset-2 has-[[data-slot=input-group-control]:focus-visible]:ring-offset-background",
|
|
24
|
+
// Trạng thái lỗi.
|
|
25
|
+
"has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
|
|
26
|
+
className
|
|
27
|
+
),
|
|
28
|
+
...props
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
const inputGroupAddonVariants = cva(
|
|
33
|
+
"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50",
|
|
34
|
+
{
|
|
35
|
+
variants: {
|
|
36
|
+
align: {
|
|
37
|
+
"inline-start": "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
|
|
38
|
+
"inline-end": "order-last pr-3 has-[>button]:mr-[-0.4rem] has-[>kbd]:mr-[-0.35rem]",
|
|
39
|
+
"block-start": "order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5",
|
|
40
|
+
"block-end": "order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
defaultVariants: {
|
|
44
|
+
align: "inline-start"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
function InputGroupAddon({ className, align = "inline-start", ...props }) {
|
|
49
|
+
return (
|
|
50
|
+
// biome-ignore lint/a11y/useKeyWithClickEvents: chỉ là lối tắt bằng chuột, bàn phím đã Tab thẳng tới control
|
|
51
|
+
/* @__PURE__ */ jsx(
|
|
52
|
+
"div",
|
|
53
|
+
{
|
|
54
|
+
role: "group",
|
|
55
|
+
"data-slot": "input-group-addon",
|
|
56
|
+
"data-align": align,
|
|
57
|
+
className: cn(inputGroupAddonVariants({ align }), className),
|
|
58
|
+
onClick: (event) => {
|
|
59
|
+
if (event.target.closest("button")) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
event.currentTarget.parentElement?.querySelector("input")?.focus();
|
|
63
|
+
},
|
|
64
|
+
...props
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
const inputGroupButtonVariants = cva("text-sm shadow-none flex gap-2 items-center", {
|
|
70
|
+
variants: {
|
|
71
|
+
size: {
|
|
72
|
+
xs: "h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2",
|
|
73
|
+
sm: "h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5",
|
|
74
|
+
"icon-xs": "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
|
|
75
|
+
"icon-sm": "size-8 p-0 has-[>svg]:p-0"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
defaultVariants: {
|
|
79
|
+
size: "xs"
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
function InputGroupButton({
|
|
83
|
+
className,
|
|
84
|
+
type = "button",
|
|
85
|
+
variant = "ghost",
|
|
86
|
+
size = "xs",
|
|
87
|
+
...props
|
|
88
|
+
}) {
|
|
89
|
+
return /* @__PURE__ */ jsx(
|
|
90
|
+
Button,
|
|
91
|
+
{
|
|
92
|
+
type,
|
|
93
|
+
"data-size": size,
|
|
94
|
+
variant,
|
|
95
|
+
className: cn(inputGroupButtonVariants({ size }), className),
|
|
96
|
+
...props
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
function InputGroupText({ className, ...props }) {
|
|
101
|
+
return /* @__PURE__ */ jsx(
|
|
102
|
+
"span",
|
|
103
|
+
{
|
|
104
|
+
className: cn(
|
|
105
|
+
"text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
106
|
+
className
|
|
107
|
+
),
|
|
108
|
+
...props
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
function InputGroupInput({ className, ...props }) {
|
|
113
|
+
return /* @__PURE__ */ jsx(
|
|
114
|
+
Input,
|
|
115
|
+
{
|
|
116
|
+
"data-slot": "input-group-control",
|
|
117
|
+
className: cn(
|
|
118
|
+
"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",
|
|
119
|
+
className
|
|
120
|
+
),
|
|
121
|
+
...props
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
function InputGroupTextarea({ className, ...props }) {
|
|
126
|
+
return /* @__PURE__ */ jsx(
|
|
127
|
+
Textarea,
|
|
128
|
+
{
|
|
129
|
+
"data-slot": "input-group-control",
|
|
130
|
+
className: cn(
|
|
131
|
+
"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent",
|
|
132
|
+
className
|
|
133
|
+
),
|
|
134
|
+
...props
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
export {
|
|
139
|
+
InputGroup,
|
|
140
|
+
InputGroupAddon,
|
|
141
|
+
InputGroupButton,
|
|
142
|
+
InputGroupInput,
|
|
143
|
+
InputGroupText,
|
|
144
|
+
InputGroupTextarea,
|
|
145
|
+
inputGroupAddonVariants,
|
|
146
|
+
inputGroupButtonVariants
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=input-group.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/input-group.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Button, type ButtonProps } from \"./button.js\";\nimport { Input } from \"./input.js\";\nimport { Textarea } from \"./textarea.js\";\n\nexport type InputGroupProps = React.ComponentProps<\"div\">;\n\nfunction InputGroup({ className, ...props }: InputGroupProps) {\n return (\n <div\n data-slot=\"input-group\"\n role=\"group\"\n className={cn(\n \"group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-colors duration-150 ease-out outline-none\",\n \"h-9 has-[>textarea]:h-auto\",\n\n // Biến thể theo vị trí addon.\n \"has-[>[data-align=inline-start]]:[&>input]:pl-2\",\n \"has-[>[data-align=inline-end]]:[&>input]:pr-2\",\n \"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3\",\n \"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3\",\n\n // Trạng thái focus.\n \"has-[[data-slot=input-group-control]:focus-visible]:border-gold-300 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-2 has-[[data-slot=input-group-control]:focus-visible]:ring-offset-2 has-[[data-slot=input-group-control]:focus-visible]:ring-offset-background\",\n\n // Trạng thái lỗi.\n \"has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40\",\n\n className\n )}\n {...props}\n />\n );\n}\n\nconst inputGroupAddonVariants = cva(\n \"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50\",\n {\n variants: {\n align: {\n \"inline-start\": \"order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]\",\n \"inline-end\": \"order-last pr-3 has-[>button]:mr-[-0.4rem] has-[>kbd]:mr-[-0.35rem]\",\n \"block-start\":\n \"order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5\",\n \"block-end\": \"order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5\"\n }\n },\n defaultVariants: {\n align: \"inline-start\"\n }\n }\n);\n\nexport interface InputGroupAddonProps\n extends React.ComponentProps<\"div\">,\n VariantProps<typeof inputGroupAddonVariants> {}\n\n/*\n * Bấm vào vùng addon thì dồn focus sang ô nhập bên trong (trừ khi bấm trúng một nút).\n * Đây là tiện ích chuột thuần tuý — bàn phím vẫn Tab thẳng vào input/button nên không\n * cần onKeyDown; addon không phải phần tử tương tác.\n */\nfunction InputGroupAddon({ className, align = \"inline-start\", ...props }: InputGroupAddonProps) {\n return (\n // biome-ignore lint/a11y/useKeyWithClickEvents: chỉ là lối tắt bằng chuột, bàn phím đã Tab thẳng tới control\n <div\n role=\"group\"\n data-slot=\"input-group-addon\"\n data-align={align}\n className={cn(inputGroupAddonVariants({ align }), className)}\n onClick={(event) => {\n if ((event.target as HTMLElement).closest(\"button\")) {\n return;\n }\n event.currentTarget.parentElement?.querySelector(\"input\")?.focus();\n }}\n {...props}\n />\n );\n}\n\nconst inputGroupButtonVariants = cva(\"text-sm shadow-none flex gap-2 items-center\", {\n variants: {\n size: {\n xs: \"h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2\",\n sm: \"h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5\",\n \"icon-xs\": \"size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0\",\n \"icon-sm\": \"size-8 p-0 has-[>svg]:p-0\"\n }\n },\n defaultVariants: {\n size: \"xs\"\n }\n});\n\nexport type InputGroupButtonProps = Omit<ButtonProps, \"size\"> & VariantProps<typeof inputGroupButtonVariants>;\n\nfunction InputGroupButton({\n className,\n type = \"button\",\n variant = \"ghost\",\n size = \"xs\",\n ...props\n}: InputGroupButtonProps) {\n return (\n <Button\n type={type}\n data-size={size}\n variant={variant}\n className={cn(inputGroupButtonVariants({ size }), className)}\n {...props}\n />\n );\n}\n\nexport type InputGroupTextProps = React.ComponentProps<\"span\">;\n\nfunction InputGroupText({ className, ...props }: InputGroupTextProps) {\n return (\n <span\n className={cn(\n \"text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type InputGroupInputProps = React.ComponentProps<\"input\">;\n\nfunction InputGroupInput({ className, ...props }: InputGroupInputProps) {\n return (\n <Input\n data-slot=\"input-group-control\"\n className={cn(\n \"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type InputGroupTextareaProps = React.ComponentProps<\"textarea\">;\n\nfunction InputGroupTextarea({ className, ...props }: InputGroupTextareaProps) {\n return (\n <Textarea\n data-slot=\"input-group-control\"\n className={cn(\n \"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport {\n InputGroup,\n InputGroupAddon,\n InputGroupButton,\n InputGroupInput,\n InputGroupText,\n InputGroupTextarea,\n inputGroupAddonVariants,\n inputGroupButtonVariants\n};\n"],"mappings":";AAaI;AAXJ,SAAS,WAA8B;AAEvC,SAAS,UAAU;AACnB,SAAS,cAAgC;AACzC,SAAS,aAAa;AACtB,SAAS,gBAAgB;AAIzB,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAAoB;AAC5D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,MAAK;AAAA,MACL,WAAW;AAAA,QACT;AAAA,QACA;AAAA;AAAA,QAGA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,QAGA;AAAA;AAAA,QAGA;AAAA,QAEA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,MAAM,0BAA0B;AAAA,EAC9B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,OAAO;AAAA,QACL,gBAAgB;AAAA,QAChB,cAAc;AAAA,QACd,eACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,OAAO;AAAA,IACT;AAAA,EACF;AACF;AAWA,SAAS,gBAAgB,EAAE,WAAW,QAAQ,gBAAgB,GAAG,MAAM,GAAyB;AAC9F;AAAA;AAAA,IAEE;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,aAAU;AAAA,QACV,cAAY;AAAA,QACZ,WAAW,GAAG,wBAAwB,EAAE,MAAM,CAAC,GAAG,SAAS;AAAA,QAC3D,SAAS,CAAC,UAAU;AAClB,cAAK,MAAM,OAAuB,QAAQ,QAAQ,GAAG;AACnD;AAAA,UACF;AACA,gBAAM,cAAc,eAAe,cAAc,OAAO,GAAG,MAAM;AAAA,QACnE;AAAA,QACC,GAAG;AAAA;AAAA,IACN;AAAA;AAEJ;AAEA,MAAM,2BAA2B,IAAI,+CAA+C;AAAA,EAClF,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf,MAAM;AAAA,EACR;AACF,CAAC;AAID,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA,OAAO;AAAA,EACP,UAAU;AAAA,EACV,OAAO;AAAA,EACP,GAAG;AACL,GAA0B;AACxB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAW;AAAA,MACX;AAAA,MACA,WAAW,GAAG,yBAAyB,EAAE,KAAK,CAAC,GAAG,SAAS;AAAA,MAC1D,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAwB;AACpE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,gBAAgB,EAAE,WAAW,GAAG,MAAM,GAAyB;AACtE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,mBAAmB,EAAE,WAAW,GAAG,MAAM,GAA4B;AAC5E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { OTPInput } from 'input-otp';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* InputOTP — ô nhập mã OTP (input-otp). Dùng cho luồng 2FA / xác thực email.
|
|
6
|
+
*
|
|
7
|
+
* Khác bản comitor-ds: đọc slot bằng optional chaining thay vì `?? {}` — tsconfig
|
|
8
|
+
* bật `noUncheckedIndexedAccess` nên `slots[index]` là `Slot | undefined`.
|
|
9
|
+
*/
|
|
10
|
+
type InputOTPProps = React.ComponentProps<typeof OTPInput> & {
|
|
11
|
+
containerClassName?: string;
|
|
12
|
+
};
|
|
13
|
+
type InputOTPGroupProps = React.ComponentProps<"div">;
|
|
14
|
+
type InputOTPSlotProps = React.ComponentProps<"div"> & {
|
|
15
|
+
index: number;
|
|
16
|
+
};
|
|
17
|
+
type InputOTPSeparatorProps = React.ComponentProps<"div">;
|
|
18
|
+
declare function InputOTP({ className, containerClassName, ...props }: InputOTPProps): React.JSX.Element;
|
|
19
|
+
declare function InputOTPGroup({ className, ...props }: InputOTPGroupProps): React.JSX.Element;
|
|
20
|
+
declare function InputOTPSlot({ index, className, ...props }: InputOTPSlotProps): React.JSX.Element;
|
|
21
|
+
declare function InputOTPSeparator({ ...props }: InputOTPSeparatorProps): React.JSX.Element;
|
|
22
|
+
|
|
23
|
+
export { InputOTP, InputOTPGroup, type InputOTPGroupProps, type InputOTPProps, InputOTPSeparator, type InputOTPSeparatorProps, InputOTPSlot, type InputOTPSlotProps };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { OTPInput, OTPInputContext } from "input-otp";
|
|
4
|
+
import { MinusIcon } from "lucide-react";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { cn } from "../../lib/cn.js";
|
|
7
|
+
function InputOTP({ className, containerClassName, ...props }) {
|
|
8
|
+
return /* @__PURE__ */ jsx(
|
|
9
|
+
OTPInput,
|
|
10
|
+
{
|
|
11
|
+
"data-slot": "input-otp",
|
|
12
|
+
containerClassName: cn("flex items-center gap-2 has-disabled:opacity-50", containerClassName),
|
|
13
|
+
className: cn("disabled:cursor-not-allowed", className),
|
|
14
|
+
...props
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
function InputOTPGroup({ className, ...props }) {
|
|
19
|
+
return /* @__PURE__ */ jsx("div", { "data-slot": "input-otp-group", className: cn("flex items-center", className), ...props });
|
|
20
|
+
}
|
|
21
|
+
function InputOTPSlot({ index, className, ...props }) {
|
|
22
|
+
const inputOTPContext = React.useContext(OTPInputContext);
|
|
23
|
+
const slot = inputOTPContext?.slots[index];
|
|
24
|
+
return /* @__PURE__ */ jsxs(
|
|
25
|
+
"div",
|
|
26
|
+
{
|
|
27
|
+
"data-slot": "input-otp-slot",
|
|
28
|
+
"data-active": slot?.isActive,
|
|
29
|
+
className: cn(
|
|
30
|
+
"data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-colors duration-150 ease-out outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
|
|
31
|
+
className
|
|
32
|
+
),
|
|
33
|
+
...props,
|
|
34
|
+
children: [
|
|
35
|
+
slot?.char,
|
|
36
|
+
slot?.hasFakeCaret && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "animate-caret-blink bg-foreground h-4 w-px duration-1000" }) })
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
function InputOTPSeparator({ ...props }) {
|
|
42
|
+
return (
|
|
43
|
+
// biome-ignore lint/a11y/useFocusableInteractive: dấu phân cách tĩnh giữa các nhóm ô, không phải splitter kéo được nên không cần focus
|
|
44
|
+
// biome-ignore lint/a11y/useAriaPropsForRole: separator tĩnh không có giá trị → không cần aria-valuenow
|
|
45
|
+
/* @__PURE__ */ jsx("div", { "data-slot": "input-otp-separator", role: "separator", ...props, children: /* @__PURE__ */ jsx(MinusIcon, {}) })
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
InputOTP,
|
|
50
|
+
InputOTPGroup,
|
|
51
|
+
InputOTPSeparator,
|
|
52
|
+
InputOTPSlot
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=input-otp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/input-otp.tsx"],"sourcesContent":["\"use client\";\n\nimport { OTPInput, OTPInputContext } from \"input-otp\";\nimport { MinusIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * InputOTP — ô nhập mã OTP (input-otp). Dùng cho luồng 2FA / xác thực email.\n *\n * Khác bản comitor-ds: đọc slot bằng optional chaining thay vì `?? {}` — tsconfig\n * bật `noUncheckedIndexedAccess` nên `slots[index]` là `Slot | undefined`.\n */\n\nexport type InputOTPProps = React.ComponentProps<typeof OTPInput> & {\n containerClassName?: string;\n};\nexport type InputOTPGroupProps = React.ComponentProps<\"div\">;\nexport type InputOTPSlotProps = React.ComponentProps<\"div\"> & { index: number };\nexport type InputOTPSeparatorProps = React.ComponentProps<\"div\">;\n\nfunction InputOTP({ className, containerClassName, ...props }: InputOTPProps) {\n return (\n <OTPInput\n data-slot=\"input-otp\"\n containerClassName={cn(\"flex items-center gap-2 has-disabled:opacity-50\", containerClassName)}\n className={cn(\"disabled:cursor-not-allowed\", className)}\n {...props}\n />\n );\n}\n\nfunction InputOTPGroup({ className, ...props }: InputOTPGroupProps) {\n return <div data-slot=\"input-otp-group\" className={cn(\"flex items-center\", className)} {...props} />;\n}\n\nfunction InputOTPSlot({ index, className, ...props }: InputOTPSlotProps) {\n const inputOTPContext = React.useContext(OTPInputContext);\n const slot = inputOTPContext?.slots[index];\n\n return (\n <div\n data-slot=\"input-otp-slot\"\n data-active={slot?.isActive}\n className={cn(\n \"data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-colors duration-150 ease-out outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]\",\n className\n )}\n {...props}\n >\n {slot?.char}\n {slot?.hasFakeCaret && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <div className=\"animate-caret-blink bg-foreground h-4 w-px duration-1000\" />\n </div>\n )}\n </div>\n );\n}\n\nfunction InputOTPSeparator({ ...props }: InputOTPSeparatorProps) {\n return (\n // biome-ignore lint/a11y/useFocusableInteractive: dấu phân cách tĩnh giữa các nhóm ô, không phải splitter kéo được nên không cần focus\n // biome-ignore lint/a11y/useAriaPropsForRole: separator tĩnh không có giá trị → không cần aria-valuenow\n <div data-slot=\"input-otp-separator\" role=\"separator\" {...props}>\n <MinusIcon />\n </div>\n );\n}\n\nexport { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot };\n"],"mappings":";AAuBI,cAkBA,YAlBA;AArBJ,SAAS,UAAU,uBAAuB;AAC1C,SAAS,iBAAiB;AAC1B,YAAY,WAAW;AACvB,SAAS,UAAU;AAgBnB,SAAS,SAAS,EAAE,WAAW,oBAAoB,GAAG,MAAM,GAAkB;AAC5E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,oBAAoB,GAAG,mDAAmD,kBAAkB;AAAA,MAC5F,WAAW,GAAG,+BAA+B,SAAS;AAAA,MACrD,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,cAAc,EAAE,WAAW,GAAG,MAAM,GAAuB;AAClE,SAAO,oBAAC,SAAI,aAAU,mBAAkB,WAAW,GAAG,qBAAqB,SAAS,GAAI,GAAG,OAAO;AACpG;AAEA,SAAS,aAAa,EAAE,OAAO,WAAW,GAAG,MAAM,GAAsB;AACvE,QAAM,kBAAkB,MAAM,WAAW,eAAe;AACxD,QAAM,OAAO,iBAAiB,MAAM,KAAK;AAEzC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,eAAa,MAAM;AAAA,MACnB,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA,cAAM;AAAA,QACN,MAAM,gBACL,oBAAC,SAAI,WAAU,yEACb,8BAAC,SAAI,WAAU,4DAA2D,GAC5E;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,SAAS,kBAAkB,EAAE,GAAG,MAAM,GAA2B;AAC/D;AAAA;AAAA;AAAA,IAGE,oBAAC,SAAI,aAAU,uBAAsB,MAAK,aAAa,GAAG,OACxD,8BAAC,aAAU,GACb;AAAA;AAEJ;","names":[]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface InputProps extends React.ComponentProps<"input"> {
|
|
4
|
+
/** Nhãn hiển thị phía trên ô nhập. */
|
|
5
|
+
label?: string;
|
|
6
|
+
/** Thông báo lỗi — có giá trị là ô nhập chuyển sang trạng thái lỗi. */
|
|
7
|
+
error?: string;
|
|
8
|
+
/** Gợi ý phụ dưới ô nhập; bị `error` che khi có lỗi. */
|
|
9
|
+
hint?: string;
|
|
10
|
+
/** Nội dung chèn bên trái trong lòng ô nhập (icon, đơn vị…). */
|
|
11
|
+
leftAddon?: React.ReactNode;
|
|
12
|
+
/** Nội dung chèn bên phải trong lòng ô nhập. */
|
|
13
|
+
rightAddon?: React.ReactNode;
|
|
14
|
+
/** Alias của `leftAddon`. */
|
|
15
|
+
leftIcon?: React.ReactNode;
|
|
16
|
+
/** Alias của `rightAddon`. */
|
|
17
|
+
rightIcon?: React.ReactNode;
|
|
18
|
+
}
|
|
19
|
+
declare function Input({ className, type, label, error, hint, leftAddon, rightAddon, leftIcon, rightIcon, id, "aria-invalid": ariaInvalid, ...props }: InputProps): React.JSX.Element;
|
|
20
|
+
|
|
21
|
+
export { Input, type InputProps };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../../lib/cn.js";
|
|
5
|
+
function Input({
|
|
6
|
+
className,
|
|
7
|
+
type,
|
|
8
|
+
label,
|
|
9
|
+
error,
|
|
10
|
+
hint,
|
|
11
|
+
leftAddon,
|
|
12
|
+
rightAddon,
|
|
13
|
+
leftIcon,
|
|
14
|
+
rightIcon,
|
|
15
|
+
id,
|
|
16
|
+
"aria-invalid": ariaInvalid,
|
|
17
|
+
...props
|
|
18
|
+
}) {
|
|
19
|
+
const reactId = React.useId();
|
|
20
|
+
const inputId = id ?? reactId;
|
|
21
|
+
const hasError = Boolean(error) || ariaInvalid === true || ariaInvalid === "true";
|
|
22
|
+
const left = leftAddon ?? leftIcon;
|
|
23
|
+
const right = rightAddon ?? rightIcon;
|
|
24
|
+
const inputEl = /* @__PURE__ */ jsx(
|
|
25
|
+
"input",
|
|
26
|
+
{
|
|
27
|
+
id: inputId,
|
|
28
|
+
type,
|
|
29
|
+
"data-slot": "input",
|
|
30
|
+
"aria-invalid": hasError || void 0,
|
|
31
|
+
"aria-describedby": error ? `${inputId}-error` : hint ? `${inputId}-hint` : void 0,
|
|
32
|
+
className: cn(
|
|
33
|
+
"w-full h-9 px-3 rounded-md border bg-background text-sm text-foreground placeholder:text-muted-foreground",
|
|
34
|
+
"transition-colors duration-150 ease-out",
|
|
35
|
+
"focus:outline-none focus:ring-2 focus:ring-ring/50 focus:border-gold-300",
|
|
36
|
+
"disabled:opacity-50 disabled:cursor-not-allowed disabled:bg-muted",
|
|
37
|
+
hasError ? "border-red focus:ring-red/30 focus:border-red" : "border-input hover:border-ash-300 dark:hover:border-ash-500",
|
|
38
|
+
left && "pl-9",
|
|
39
|
+
right && "pr-9",
|
|
40
|
+
className
|
|
41
|
+
),
|
|
42
|
+
...props
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
if (!label && !error && !hint && !left && !right) {
|
|
46
|
+
return inputEl;
|
|
47
|
+
}
|
|
48
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
|
|
49
|
+
label && /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "mb-1.5 block text-sm font-medium text-foreground", children: label }),
|
|
50
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex items-center", children: [
|
|
51
|
+
left && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute left-3 flex items-center text-muted-foreground", children: left }),
|
|
52
|
+
inputEl,
|
|
53
|
+
right && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute right-3 flex items-center text-muted-foreground", children: right })
|
|
54
|
+
] }),
|
|
55
|
+
error && /* @__PURE__ */ jsx("p", { id: `${inputId}-error`, className: "mt-1.5 text-xs text-red", children: error }),
|
|
56
|
+
hint && !error && /* @__PURE__ */ jsx("p", { id: `${inputId}-hint`, className: "mt-1.5 text-xs text-muted-foreground", children: hint })
|
|
57
|
+
] });
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
Input
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/input.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\nexport interface InputProps extends React.ComponentProps<\"input\"> {\n /** Nhãn hiển thị phía trên ô nhập. */\n label?: string;\n /** Thông báo lỗi — có giá trị là ô nhập chuyển sang trạng thái lỗi. */\n error?: string;\n /** Gợi ý phụ dưới ô nhập; bị `error` che khi có lỗi. */\n hint?: string;\n /** Nội dung chèn bên trái trong lòng ô nhập (icon, đơn vị…). */\n leftAddon?: React.ReactNode;\n /** Nội dung chèn bên phải trong lòng ô nhập. */\n rightAddon?: React.ReactNode;\n /** Alias của `leftAddon`. */\n leftIcon?: React.ReactNode;\n /** Alias của `rightAddon`. */\n rightIcon?: React.ReactNode;\n}\n\n/*\n * Style port nguyên trạng từ comitor-ds. Các prop label / error / hint / addon là phần\n * BỔ SUNG: không truyền cái nào thì component render đúng một thẻ <input> trần đã style.\n * (Đổi gray-* → ash-* theo namespace thang xám của package — xem styles.css.)\n */\nfunction Input({\n className,\n type,\n label,\n error,\n hint,\n leftAddon,\n rightAddon,\n leftIcon,\n rightIcon,\n id,\n \"aria-invalid\": ariaInvalid,\n ...props\n}: InputProps) {\n const reactId = React.useId();\n const inputId = id ?? reactId;\n const hasError = Boolean(error) || ariaInvalid === true || ariaInvalid === \"true\";\n const left = leftAddon ?? leftIcon;\n const right = rightAddon ?? rightIcon;\n\n const inputEl = (\n <input\n id={inputId}\n type={type}\n data-slot=\"input\"\n aria-invalid={hasError || undefined}\n aria-describedby={error ? `${inputId}-error` : hint ? `${inputId}-hint` : undefined}\n className={cn(\n \"w-full h-9 px-3 rounded-md border bg-background text-sm text-foreground placeholder:text-muted-foreground\",\n \"transition-colors duration-150 ease-out\",\n \"focus:outline-none focus:ring-2 focus:ring-ring/50 focus:border-gold-300\",\n \"disabled:opacity-50 disabled:cursor-not-allowed disabled:bg-muted\",\n hasError\n ? \"border-red focus:ring-red/30 focus:border-red\"\n : \"border-input hover:border-ash-300 dark:hover:border-ash-500\",\n left && \"pl-9\",\n right && \"pr-9\",\n className\n )}\n {...props}\n />\n );\n\n if (!label && !error && !hint && !left && !right) {\n return inputEl;\n }\n\n return (\n <div className=\"w-full\">\n {label && (\n <label htmlFor={inputId} className=\"mb-1.5 block text-sm font-medium text-foreground\">\n {label}\n </label>\n )}\n <div className=\"relative flex items-center\">\n {left && (\n <div className=\"pointer-events-none absolute left-3 flex items-center text-muted-foreground\">{left}</div>\n )}\n {inputEl}\n {right && (\n <div className=\"pointer-events-none absolute right-3 flex items-center text-muted-foreground\">{right}</div>\n )}\n </div>\n {error && (\n <p id={`${inputId}-error`} className=\"mt-1.5 text-xs text-red\">\n {error}\n </p>\n )}\n {hint && !error && (\n <p id={`${inputId}-hint`} className=\"mt-1.5 text-xs text-muted-foreground\">\n {hint}\n </p>\n )}\n </div>\n );\n}\n\nexport { Input };\n"],"mappings":";AAgDI,cAiCE,YAjCF;AA9CJ,YAAY,WAAW;AACvB,SAAS,UAAU;AAwBnB,SAAS,MAAM;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB,GAAG;AACL,GAAe;AACb,QAAM,UAAU,MAAM,MAAM;AAC5B,QAAM,UAAU,MAAM;AACtB,QAAM,WAAW,QAAQ,KAAK,KAAK,gBAAgB,QAAQ,gBAAgB;AAC3E,QAAM,OAAO,aAAa;AAC1B,QAAM,QAAQ,cAAc;AAE5B,QAAM,UACJ;AAAA,IAAC;AAAA;AAAA,MACC,IAAI;AAAA,MACJ;AAAA,MACA,aAAU;AAAA,MACV,gBAAc,YAAY;AAAA,MAC1B,oBAAkB,QAAQ,GAAG,OAAO,WAAW,OAAO,GAAG,OAAO,UAAU;AAAA,MAC1E,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WACI,kDACA;AAAA,QACJ,QAAQ;AAAA,QACR,SAAS;AAAA,QACT;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAGF,MAAI,CAAC,SAAS,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO;AAChD,WAAO;AAAA,EACT;AAEA,SACE,qBAAC,SAAI,WAAU,UACZ;AAAA,aACC,oBAAC,WAAM,SAAS,SAAS,WAAU,oDAChC,iBACH;AAAA,IAEF,qBAAC,SAAI,WAAU,8BACZ;AAAA,cACC,oBAAC,SAAI,WAAU,+EAA+E,gBAAK;AAAA,MAEpG;AAAA,MACA,SACC,oBAAC,SAAI,WAAU,gFAAgF,iBAAM;AAAA,OAEzG;AAAA,IACC,SACC,oBAAC,OAAE,IAAI,GAAG,OAAO,UAAU,WAAU,2BAClC,iBACH;AAAA,IAED,QAAQ,CAAC,SACR,oBAAC,OAAE,IAAI,GAAG,OAAO,SAAS,WAAU,wCACjC,gBACH;AAAA,KAEJ;AAEJ;","names":[]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
2
|
+
import { VariantProps } from 'class-variance-authority';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { SeparatorProps } from './separator.js';
|
|
5
|
+
import '@radix-ui/react-separator';
|
|
6
|
+
|
|
7
|
+
type ItemGroupProps = React.ComponentProps<"div">;
|
|
8
|
+
type ItemSeparatorProps = SeparatorProps;
|
|
9
|
+
declare function ItemGroup({ className, ...props }: ItemGroupProps): React.JSX.Element;
|
|
10
|
+
declare function ItemSeparator({ className, ...props }: ItemSeparatorProps): React.JSX.Element;
|
|
11
|
+
declare const itemVariants: (props?: ({
|
|
12
|
+
variant?: "default" | "outline" | "muted" | null | undefined;
|
|
13
|
+
size?: "sm" | "default" | null | undefined;
|
|
14
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
15
|
+
interface ItemProps extends React.ComponentProps<"div">, VariantProps<typeof itemVariants> {
|
|
16
|
+
/** Render ra chính phần tử con (Radix Slot) — dùng khi cần <a> hoặc <button>. */
|
|
17
|
+
asChild?: boolean;
|
|
18
|
+
}
|
|
19
|
+
declare function Item({ className, variant, size, asChild, ...props }: ItemProps): React.JSX.Element;
|
|
20
|
+
declare const itemMediaVariants: (props?: ({
|
|
21
|
+
variant?: "image" | "default" | "icon" | null | undefined;
|
|
22
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
23
|
+
interface ItemMediaProps extends React.ComponentProps<"div">, VariantProps<typeof itemMediaVariants> {
|
|
24
|
+
}
|
|
25
|
+
type ItemContentProps = React.ComponentProps<"div">;
|
|
26
|
+
type ItemTitleProps = React.ComponentProps<"div">;
|
|
27
|
+
type ItemDescriptionProps = React.ComponentProps<"p">;
|
|
28
|
+
type ItemActionsProps = React.ComponentProps<"div">;
|
|
29
|
+
type ItemHeaderProps = React.ComponentProps<"div">;
|
|
30
|
+
type ItemFooterProps = React.ComponentProps<"div">;
|
|
31
|
+
declare function ItemMedia({ className, variant, ...props }: ItemMediaProps): React.JSX.Element;
|
|
32
|
+
declare function ItemContent({ className, ...props }: ItemContentProps): React.JSX.Element;
|
|
33
|
+
declare function ItemTitle({ className, ...props }: ItemTitleProps): React.JSX.Element;
|
|
34
|
+
declare function ItemDescription({ className, ...props }: ItemDescriptionProps): React.JSX.Element;
|
|
35
|
+
declare function ItemActions({ className, ...props }: ItemActionsProps): React.JSX.Element;
|
|
36
|
+
declare function ItemHeader({ className, ...props }: ItemHeaderProps): React.JSX.Element;
|
|
37
|
+
declare function ItemFooter({ className, ...props }: ItemFooterProps): React.JSX.Element;
|
|
38
|
+
|
|
39
|
+
export { Item, ItemActions, type ItemActionsProps, ItemContent, type ItemContentProps, ItemDescription, type ItemDescriptionProps, ItemFooter, type ItemFooterProps, ItemGroup, type ItemGroupProps, ItemHeader, type ItemHeaderProps, ItemMedia, type ItemMediaProps, type ItemProps, ItemSeparator, type ItemSeparatorProps, ItemTitle, type ItemTitleProps, itemMediaVariants, itemVariants };
|