@godxjp/ui 19.5.0 → 20.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/data-display/card.d.ts +2 -2
- package/dist/components/data-display/collapsible.d.ts +23 -4
- package/dist/components/data-display/collapsible.js +119 -4
- package/dist/components/data-display/index.d.ts +3 -1
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-display/legend.d.ts +15 -0
- package/dist/components/data-display/legend.js +11 -0
- package/dist/components/data-display/list-row.js +1 -1
- package/dist/components/data-display/popover.d.ts +58 -5
- package/dist/components/data-display/popover.js +206 -27
- package/dist/components/data-display/progress.d.ts +50 -2
- package/dist/components/data-display/progress.js +57 -9
- package/dist/components/data-entry/calendar.d.ts +1 -1
- package/dist/components/data-entry/calendar.js +67 -32
- package/dist/components/data-entry/checkbox.d.ts +19 -6
- package/dist/components/data-entry/checkbox.js +55 -16
- package/dist/components/data-entry/choice-option.d.ts +1 -1
- package/dist/components/data-entry/date-range-picker.d.ts +1 -1
- package/dist/components/data-entry/field.js +0 -1
- package/dist/components/data-entry/form.js +1 -1
- package/dist/components/data-entry/label.d.ts +3 -2
- package/dist/components/data-entry/label.js +23 -10
- package/dist/components/data-entry/month-picker.d.ts +1 -1
- package/dist/components/data-entry/month-range-picker.d.ts +1 -1
- package/dist/components/data-entry/transfer.js +16 -6
- package/dist/components/data-entry/tree-select-strategy.d.ts +1 -1
- package/dist/components/data-entry/tree-select.js +29 -12
- package/dist/components/data-entry/tree-utils.d.ts +1 -1
- package/dist/components/data-entry/upload.js +7 -2
- package/dist/components/feedback/dialog.d.ts +105 -38
- package/dist/components/feedback/dialog.js +272 -194
- package/dist/components/feedback/overlay-close-focus.d.ts +31 -0
- package/dist/components/feedback/overlay-close-focus.js +31 -0
- package/dist/components/feedback/overlay-header-tone.d.ts +1 -1
- package/dist/components/feedback/sheet.d.ts +53 -11
- package/dist/components/feedback/sheet.js +150 -81
- package/dist/components/feedback/tooltip.d.ts +51 -7
- package/dist/components/feedback/tooltip.js +107 -25
- package/dist/components/general/button.js +2 -2
- package/dist/components/general/logo.d.ts +17 -0
- package/dist/components/general/logo.js +22 -16
- package/dist/components/general/typography.d.ts +7 -1
- package/dist/components/general/typography.js +20 -5
- package/dist/components/layout/app-shell.d.ts +1 -1
- package/dist/components/layout/app-shell.js +52 -8
- package/dist/components/layout/aspect-ratio.js +0 -1
- package/dist/components/layout/auth-divider.js +0 -1
- package/dist/components/layout/flex.d.ts +1 -1
- package/dist/components/layout/flex.js +19 -2
- package/dist/components/layout/nav-surface.d.ts +26 -0
- package/dist/components/layout/nav-surface.js +17 -0
- package/dist/components/layout/org-switcher.d.ts +5 -1
- package/dist/components/layout/org-switcher.js +5 -3
- package/dist/components/layout/separator.js +0 -1
- package/dist/components/layout/sidebar.js +4 -1
- package/dist/components/layout/split-pane.d.ts +14 -1
- package/dist/components/layout/split-pane.js +26 -13
- package/dist/components/layout/topbar-item.js +1 -2
- package/dist/components/navigation/app-setting-picker.js +21 -2
- package/dist/components/navigation/app-setting-toggle.d.ts +16 -0
- package/dist/components/navigation/app-setting-toggle.js +96 -0
- package/dist/components/navigation/dropdown-menu.d.ts +199 -18
- package/dist/components/navigation/dropdown-menu.js +344 -117
- package/dist/components/navigation/index.d.ts +2 -0
- package/dist/components/navigation/index.js +2 -0
- package/dist/components/navigation/pagination-utils.d.ts +2 -1
- package/dist/components/navigation/tabs.d.ts +47 -6
- package/dist/components/navigation/tabs.js +152 -90
- package/dist/components/ui/accordion.d.ts +50 -5
- package/dist/components/ui/accordion.js +239 -33
- package/dist/components/ui/aspect-ratio.d.ts +23 -2
- package/dist/components/ui/aspect-ratio.js +15 -13
- package/dist/components/ui/avatar.d.ts +29 -4
- package/dist/components/ui/avatar.js +111 -25
- package/dist/components/ui/hover-card.d.ts +42 -4
- package/dist/components/ui/hover-card.js +183 -27
- package/dist/components/ui/label.js +0 -1
- package/dist/components/ui/segmented.d.ts +3 -3
- package/dist/components/ui/separator.d.ts +8 -2
- package/dist/components/ui/separator.js +17 -9
- package/dist/components/ui/toggle-group.d.ts +50 -5
- package/dist/components/ui/toggle-group.js +79 -20
- package/dist/components/ui/toggle.d.ts +31 -5
- package/dist/components/ui/toggle.js +42 -3
- package/dist/i18n/messages/en.json +8 -0
- package/dist/i18n/messages/ja.json +8 -0
- package/dist/i18n/messages/vi.json +8 -0
- package/dist/lib/control-styles.d.ts +1 -1
- package/dist/lib/control-styles.js +1 -1
- package/dist/lib/slot.d.ts +32 -0
- package/dist/lib/slot.js +22 -0
- package/dist/lib/variants.d.ts +22 -3
- package/dist/lib/variants.js +56 -1
- package/dist/props/components/app.prop.d.ts +25 -1
- package/dist/props/components/data-display.prop.d.ts +17 -0
- package/dist/props/components/data-entry.prop.d.ts +32 -4
- package/dist/props/components/general.prop.d.ts +31 -1
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +118 -5
- package/dist/props/components/navigation.prop.d.ts +1 -1
- package/dist/props/registry.d.ts +22 -2
- package/dist/props/registry.js +33 -2
- package/dist/props/vocabulary/content.prop.d.ts +1 -1
- package/dist/props/vocabulary/index.d.ts +2 -2
- package/dist/props/vocabulary/interaction.prop.d.ts +17 -1
- package/dist/props/vocabulary/layout.prop.d.ts +57 -1
- package/dist/styles/control.css +142 -1
- package/dist/styles/core.css +5 -2
- package/dist/styles/data-display-layout.css +72 -0
- package/dist/styles/focus-ring.css +13 -1
- package/dist/styles/index.css +5 -2
- package/dist/styles/layout.css +106 -0
- package/dist/styles/shell-layout.css +140 -8
- package/dist/styles/text-layout.css +16 -4
- package/dist/tokens/base.css +2 -1
- package/dist/tokens/components/data-display.css +11 -0
- package/dist/tokens/components/shell.css +3 -1
- package/dist/tokens/components/text.css +7 -0
- package/dist/tokens/foundation.css +2 -0
- package/docs/COMPONENTS.md +9 -3
- package/docs/DESIGN-AUTHORITY.md +96 -71
- package/docs/FRAME-COVERAGE-REPORT.md +5 -3
- package/docs/README.md +14 -14
- package/docs/WHAT-BELONGS-HERE.md +179 -0
- package/docs/data-display/legend.tsx +145 -0
- package/docs/data-display/progress.tsx +32 -0
- package/docs/data-entry/segmented.tsx +1 -1
- package/docs/data-entry/select.tsx +2 -2
- package/docs/feedback/sheet.tsx +1 -1
- package/docs/general/typography.tsx +84 -1
- package/docs/layout/app-shell-arrangements.tsx +225 -0
- package/docs/layout/aspect-ratio.tsx +1 -1
- package/docs/navigation/app-setting-picker.tsx +26 -1
- package/docs/navigation/app-setting-toggle.tsx +111 -0
- package/package.json +11 -8
- package/scripts/_agent-setup.mjs +165 -3
- package/scripts/consumer-rule.md +98 -0
- package/scripts/guinea-pig-skill.md +322 -0
- package/scripts/init-guinea-pig.mjs +62 -0
- package/scripts/postinstall.mjs +13 -2
- package/scripts/ui-audit.mjs +115 -14
- /package/dist/tokens/{antd.generated.css → derived.css} +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
3
2
|
import { type VariantProps } from "class-variance-authority";
|
|
4
3
|
import type { SheetResponsiveProp } from "../../props/components/feedback.prop.js";
|
|
5
4
|
import type { ToneProp, WidthProp } from "../../props/vocabulary/index.js";
|
|
@@ -12,18 +11,58 @@ export type SheetPresentation = "side" | "bottom";
|
|
|
12
11
|
* viewport is at or below `--sheet-responsive-breakpoint-width`.
|
|
13
12
|
*/
|
|
14
13
|
export declare function useSheetResponsiveMode(responsive?: SheetResponsiveProp): SheetPresentation;
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
export interface SheetProps {
|
|
15
|
+
/** Trạng thái mở, có kiểm soát. */
|
|
16
|
+
open?: boolean;
|
|
17
|
+
/** Trạng thái mở ban đầu khi không kiểm soát. */
|
|
18
|
+
defaultOpen?: boolean;
|
|
19
|
+
/** Gọi khi trạng thái mở đổi. */
|
|
20
|
+
onOpenChange?: (open: boolean) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Giữ tên prop của Radix. RAC không có kiểu overlay không-modal: `Modal` LUÔN khoá cuộn và ẩn
|
|
23
|
+
* nền khỏi trình đọc màn hình, nên `modal={false}` không còn tắt được điều đó.
|
|
24
|
+
*/
|
|
25
|
+
modal?: boolean;
|
|
26
|
+
children?: React.ReactNode;
|
|
27
|
+
}
|
|
28
|
+
export declare function Sheet({ open, defaultOpen, onOpenChange, modal: _modal, children }: SheetProps): React.JSX.Element;
|
|
29
|
+
export interface SheetTriggerProps extends React.ComponentPropsWithRef<"button"> {
|
|
30
|
+
/** Mượn thẻ của con thay vì dựng `<button>` riêng. */
|
|
31
|
+
asChild?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export declare function SheetTrigger({ asChild, onClick, ...props }: SheetTriggerProps): React.JSX.Element;
|
|
34
|
+
export interface SheetCloseProps extends React.ComponentPropsWithRef<"button"> {
|
|
35
|
+
/** Mượn thẻ của con thay vì dựng `<button>` riêng. */
|
|
36
|
+
asChild?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare function SheetClose({ asChild, onClick, ...props }: SheetCloseProps): React.JSX.Element;
|
|
39
|
+
export interface SheetPortalProps {
|
|
40
|
+
/** Giữ tên prop của Radix; RAC tự portal ra `document.body` từ `ModalOverlay`. */
|
|
41
|
+
forceMount?: true;
|
|
42
|
+
children?: React.ReactNode;
|
|
43
|
+
}
|
|
44
|
+
export declare function SheetPortal({ children }: SheetPortalProps): React.JSX.Element;
|
|
45
|
+
export interface SheetOverlayProps extends React.ComponentPropsWithRef<"div"> {
|
|
46
|
+
/** Giữ tên prop của Radix. */
|
|
47
|
+
forceMount?: true;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Không dựng gì — xem đầu tệp. Màn nền là `ModalOverlay` bên trong `SheetContent`, và nó vẫn mang
|
|
51
|
+
* `data-slot="sheet-overlay"` + `.ui-sheet-overlay` như trước.
|
|
52
|
+
*/
|
|
53
|
+
export declare function SheetOverlay(_props: SheetOverlayProps): null;
|
|
54
|
+
export declare namespace SheetOverlay {
|
|
55
|
+
var displayName: string;
|
|
56
|
+
}
|
|
20
57
|
declare const sheetVariants: (props?: ({
|
|
21
58
|
side?: "bottom" | "left" | "right" | "top" | null | undefined;
|
|
22
59
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
23
|
-
export interface SheetContentProps extends React.
|
|
60
|
+
export interface SheetContentProps extends React.ComponentPropsWithRef<"section">, VariantProps<typeof sheetVariants> {
|
|
24
61
|
showCloseButton?: boolean;
|
|
25
62
|
/** Optional semantic class for the owned backdrop (for a shell-specific overlay token). */
|
|
26
63
|
overlayClassName?: string;
|
|
64
|
+
/** Giữ tên prop của Radix; RAC tự portal ra `document.body` từ `ModalOverlay`. */
|
|
65
|
+
forceMount?: true;
|
|
27
66
|
/**
|
|
28
67
|
* Desired panel size for side left/right (Ant Drawer `width`). Omit to keep the canonical drawer
|
|
29
68
|
* default from `--sheet-width-default`.
|
|
@@ -35,9 +74,12 @@ export interface SheetContentProps extends React.ComponentPropsWithoutRef<typeof
|
|
|
35
74
|
*/
|
|
36
75
|
responsive?: SheetResponsiveProp;
|
|
37
76
|
}
|
|
38
|
-
export declare
|
|
77
|
+
export declare function SheetContent({ side, className, children, showCloseButton, overlayClassName, forceMount: _forceMount, width, responsive, style, ref, ...props }: SheetContentProps): React.JSX.Element;
|
|
78
|
+
export declare namespace SheetContent {
|
|
79
|
+
var displayName: string;
|
|
80
|
+
}
|
|
39
81
|
export interface SheetHeaderProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
|
40
|
-
/** Header title (Ant Drawer-style). Rendered as the
|
|
82
|
+
/** Header title (Ant Drawer-style). Rendered as the bound SheetTitle (accessible name). */
|
|
41
83
|
title?: React.ReactNode;
|
|
42
84
|
/** Secondary line under the title (rendered as SheetDescription). */
|
|
43
85
|
subtitle?: React.ReactNode;
|
|
@@ -49,5 +91,5 @@ export interface SheetHeaderProps extends Omit<React.HTMLAttributes<HTMLDivEleme
|
|
|
49
91
|
export declare const SheetHeader: ({ className, title, subtitle, extra, tone, children, ...props }: SheetHeaderProps) => React.JSX.Element;
|
|
50
92
|
export declare const SheetBody: ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => React.JSX.Element;
|
|
51
93
|
export declare const SheetFooter: ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => React.JSX.Element;
|
|
52
|
-
export declare const SheetTitle: React.ForwardRefExoticComponent<Omit<
|
|
53
|
-
export declare const SheetDescription: React.ForwardRefExoticComponent<Omit<
|
|
94
|
+
export declare const SheetTitle: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>, "ref"> & React.RefAttributes<HTMLHeadingElement>>;
|
|
95
|
+
export declare const SheetDescription: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>, "ref"> & React.RefAttributes<HTMLParagraphElement>>;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import
|
|
4
|
+
import { chain, mergeRefs } from "@react-aria/utils";
|
|
5
|
+
import { Dialog as RacDialog, Modal, ModalOverlay } from "react-aria-components";
|
|
5
6
|
import { cva } from "class-variance-authority";
|
|
6
7
|
import { X } from "lucide-react";
|
|
7
8
|
import { useMediaQuery } from "../../lib/hooks.js";
|
|
8
9
|
import { cn } from "../../lib/utils.js";
|
|
10
|
+
import { Slot } from "../../lib/slot.js";
|
|
9
11
|
import { overlayHeaderToneClass } from "./overlay-header-tone.js";
|
|
12
|
+
import { useOverlayCloseFocus } from "./overlay-close-focus.js";
|
|
10
13
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
11
14
|
const toCssLength = (v) => typeof v === "number" ? `${v}px` : v;
|
|
12
15
|
const SHEET_BREAKPOINT_TOKEN = "--sheet-responsive-breakpoint-width";
|
|
@@ -36,33 +39,74 @@ function useSheetResponsiveMode(responsive = "side") {
|
|
|
36
39
|
if (responsive === "side" || responsive === "bottom") return responsive;
|
|
37
40
|
return compact ? "bottom" : "side";
|
|
38
41
|
}
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
const SheetOpenContext = React.createContext(null);
|
|
43
|
+
function useSheetOpenState(component) {
|
|
44
|
+
const state = React.useContext(SheetOpenContext);
|
|
45
|
+
if (!state) {
|
|
46
|
+
throw new Error(`\`${component}\` ph\u1EA3i n\u1EB1m trong \`Sheet\`.`);
|
|
47
|
+
}
|
|
48
|
+
return state;
|
|
49
|
+
}
|
|
50
|
+
const SheetLabelContext = React.createContext(null);
|
|
51
|
+
function Sheet({ open, defaultOpen, onOpenChange, modal: _modal, children }) {
|
|
52
|
+
const [uncontrolled, setUncontrolled] = React.useState(defaultOpen ?? false);
|
|
53
|
+
const isOpen = open ?? uncontrolled;
|
|
54
|
+
const setOpen = React.useCallback(
|
|
55
|
+
(next) => {
|
|
56
|
+
if (next === isOpen) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (open === void 0) {
|
|
60
|
+
setUncontrolled(next);
|
|
61
|
+
}
|
|
62
|
+
onOpenChange?.(next);
|
|
63
|
+
},
|
|
64
|
+
[isOpen, open, onOpenChange]
|
|
65
|
+
);
|
|
66
|
+
const state = React.useMemo(() => ({ isOpen, setOpen }), [isOpen, setOpen]);
|
|
67
|
+
return /* @__PURE__ */ jsx(SheetOpenContext.Provider, { value: state, children });
|
|
68
|
+
}
|
|
69
|
+
function SheetTrigger({ asChild, onClick, ...props }) {
|
|
70
|
+
const state = useSheetOpenState("SheetTrigger");
|
|
71
|
+
const Comp = asChild ? Slot : "button";
|
|
72
|
+
return /* @__PURE__ */ jsx(
|
|
73
|
+
Comp,
|
|
74
|
+
{
|
|
75
|
+
type: "button",
|
|
76
|
+
"data-slot": "sheet-trigger",
|
|
77
|
+
"aria-haspopup": "dialog",
|
|
78
|
+
"aria-expanded": state.isOpen,
|
|
79
|
+
"data-state": state.isOpen ? "open" : "closed",
|
|
80
|
+
...props,
|
|
81
|
+
onClick: chain(onClick, () => {
|
|
82
|
+
state.setOpen(!state.isOpen);
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
);
|
|
41
86
|
}
|
|
42
|
-
function
|
|
43
|
-
|
|
87
|
+
function SheetClose({ asChild, onClick, ...props }) {
|
|
88
|
+
const state = useSheetOpenState("SheetClose");
|
|
89
|
+
const Comp = asChild ? Slot : "button";
|
|
90
|
+
return /* @__PURE__ */ jsx(
|
|
91
|
+
Comp,
|
|
92
|
+
{
|
|
93
|
+
type: "button",
|
|
94
|
+
"data-slot": "sheet-close",
|
|
95
|
+
...props,
|
|
96
|
+
onClick: chain(onClick, () => {
|
|
97
|
+
state.setOpen(false);
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
);
|
|
44
101
|
}
|
|
45
|
-
function
|
|
46
|
-
return /* @__PURE__ */ jsx(
|
|
102
|
+
function SheetPortal({ children }) {
|
|
103
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
47
104
|
}
|
|
48
|
-
function
|
|
49
|
-
return
|
|
105
|
+
function SheetOverlay(_props) {
|
|
106
|
+
return null;
|
|
50
107
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
{
|
|
54
|
-
ref,
|
|
55
|
-
"data-slot": "sheet-overlay",
|
|
56
|
-
className: cn(
|
|
57
|
-
// Scrim colour comes from the shared --overlay-background token (see dialog-layout.css),
|
|
58
|
-
// so a service tints every overlay's backdrop from one knob instead of a baked bg-black/50.
|
|
59
|
-
"ui-sheet-overlay data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 fixed",
|
|
60
|
-
className
|
|
61
|
-
),
|
|
62
|
-
...props
|
|
63
|
-
}
|
|
64
|
-
));
|
|
65
|
-
SheetOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
108
|
+
SheetOverlay.displayName = "SheetOverlay";
|
|
109
|
+
const SHEET_OVERLAY_CLASS = "ui-sheet-overlay data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 fixed";
|
|
66
110
|
const sheetVariants = cva(
|
|
67
111
|
"ui-sheet-panel fixed flex flex-col gap-[var(--space-chrome-gap)] bg-background px-[var(--sheet-pad-x)] py-[var(--sheet-pad-y)] transition ease-in-out data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:animate-in data-[state=open]:duration-500",
|
|
68
112
|
{
|
|
@@ -82,35 +126,50 @@ const sheetVariants = cva(
|
|
|
82
126
|
defaultVariants: { side: "right" }
|
|
83
127
|
}
|
|
84
128
|
);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
129
|
+
function SheetContent({
|
|
130
|
+
side = "right",
|
|
131
|
+
className,
|
|
132
|
+
children,
|
|
133
|
+
showCloseButton = true,
|
|
134
|
+
overlayClassName,
|
|
135
|
+
forceMount: _forceMount,
|
|
136
|
+
width,
|
|
137
|
+
responsive = "side",
|
|
138
|
+
style,
|
|
139
|
+
ref,
|
|
140
|
+
...props
|
|
141
|
+
}) {
|
|
142
|
+
const { t } = useTranslation();
|
|
143
|
+
const state = useSheetOpenState("SheetContent");
|
|
144
|
+
const presentation = useSheetResponsiveMode(responsive);
|
|
145
|
+
const titleId = React.useId();
|
|
146
|
+
const descriptionId = React.useId();
|
|
147
|
+
const labels = React.useMemo(() => ({ titleId, descriptionId }), [titleId, descriptionId]);
|
|
148
|
+
const resolvedSide = presentation === "bottom" ? "bottom" : side;
|
|
149
|
+
const bottomSheet = responsive !== "side" && presentation === "bottom";
|
|
150
|
+
const horizontal = resolvedSide === "left" || resolvedSide === "right";
|
|
151
|
+
const widthSet = width != null && horizontal;
|
|
152
|
+
const mergedStyle = widthSet ? { ...style, ["--sheet-width"]: toCssLength(width) } : style;
|
|
153
|
+
const dataState = state.isOpen ? "open" : "closed";
|
|
154
|
+
useOverlayCloseFocus(state.isOpen);
|
|
155
|
+
return /* @__PURE__ */ jsx(
|
|
156
|
+
ModalOverlay,
|
|
157
|
+
{
|
|
158
|
+
isOpen: state.isOpen,
|
|
159
|
+
onOpenChange: state.setOpen,
|
|
160
|
+
isDismissable: true,
|
|
161
|
+
"data-slot": "sheet-overlay",
|
|
162
|
+
"data-state": dataState,
|
|
163
|
+
className: cn(SHEET_OVERLAY_CLASS, overlayClassName),
|
|
164
|
+
children: /* @__PURE__ */ jsx(Modal, { className: "contents", children: /* @__PURE__ */ jsx(
|
|
165
|
+
RacDialog,
|
|
108
166
|
{
|
|
109
|
-
|
|
167
|
+
"aria-labelledby": props["aria-labelledby"] ?? titleId,
|
|
168
|
+
"aria-describedby": props["aria-describedby"] ?? descriptionId,
|
|
110
169
|
"data-slot": "sheet-content",
|
|
170
|
+
"data-state": dataState,
|
|
111
171
|
"data-responsive": responsive,
|
|
112
172
|
"data-side": resolvedSide,
|
|
113
|
-
style: mergedStyle,
|
|
114
173
|
className: cn(
|
|
115
174
|
sheetVariants({ side: resolvedSide }),
|
|
116
175
|
// `width` caps at the viewport: full-width panel on a small screen, capped on a large one.
|
|
@@ -120,13 +179,15 @@ const SheetContent = React.forwardRef(
|
|
|
120
179
|
bottomSheet && "max-h-[var(--sheet-bottom-max-height)]",
|
|
121
180
|
className
|
|
122
181
|
),
|
|
123
|
-
|
|
124
|
-
|
|
182
|
+
render: (racProps) => {
|
|
183
|
+
const { "data-rac": _rac, ref: racRef, ...rest } = racProps;
|
|
184
|
+
return /* @__PURE__ */ jsx("section", { ...rest, ...props, style: mergedStyle, ref: mergeRefs(ref, racRef) });
|
|
185
|
+
},
|
|
186
|
+
children: /* @__PURE__ */ jsxs(SheetLabelContext.Provider, { value: labels, children: [
|
|
125
187
|
children,
|
|
126
188
|
showCloseButton ? /* @__PURE__ */ jsxs(
|
|
127
|
-
|
|
189
|
+
SheetClose,
|
|
128
190
|
{
|
|
129
|
-
"data-slot": "sheet-close",
|
|
130
191
|
className: "ui-sheet-close ui-focus-ring disabled:pointer-events-none",
|
|
131
192
|
children: [
|
|
132
193
|
/* @__PURE__ */ jsx(X, { className: "ui-sheet-close-icon", "aria-hidden": "true" }),
|
|
@@ -134,13 +195,13 @@ const SheetContent = React.forwardRef(
|
|
|
134
195
|
]
|
|
135
196
|
}
|
|
136
197
|
) : null
|
|
137
|
-
]
|
|
198
|
+
] })
|
|
138
199
|
}
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
SheetContent.displayName =
|
|
200
|
+
) })
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
SheetContent.displayName = "SheetContent";
|
|
144
205
|
const SheetHeader = ({
|
|
145
206
|
className,
|
|
146
207
|
title,
|
|
@@ -191,7 +252,7 @@ const SheetBody = ({ className, ...props }) => (
|
|
|
191
252
|
)
|
|
192
253
|
);
|
|
193
254
|
const SheetFooter = ({ className, ...props }) => (
|
|
194
|
-
// Pinned action bar (
|
|
255
|
+
// Pinned action bar (the Drawer footer convention): sticks to the bottom, full-bleed top border, actions
|
|
195
256
|
// RIGHT-aligned (primary rightmost). A destructive / clear / reset action goes far-LEFT — give it
|
|
196
257
|
// `className="me-auto"`. See cardinal rule "Drawer & dialog footer layout".
|
|
197
258
|
// Owns its full vertical padding (symmetric 16/16) via `py-4`; `-mb-6` cancels SheetContent's
|
|
@@ -208,26 +269,34 @@ const SheetFooter = ({ className, ...props }) => (
|
|
|
208
269
|
}
|
|
209
270
|
)
|
|
210
271
|
);
|
|
211
|
-
const SheetTitle = React.forwardRef(({ className, ...props }, ref) =>
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
272
|
+
const SheetTitle = React.forwardRef(({ className, ...props }, ref) => {
|
|
273
|
+
const labels = React.useContext(SheetLabelContext);
|
|
274
|
+
return /* @__PURE__ */ jsx(
|
|
275
|
+
"h2",
|
|
276
|
+
{
|
|
277
|
+
ref,
|
|
278
|
+
id: labels?.titleId,
|
|
279
|
+
"data-slot": "sheet-title",
|
|
280
|
+
className: cn("ui-sheet-title", className),
|
|
281
|
+
...props
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
});
|
|
285
|
+
SheetTitle.displayName = "SheetTitle";
|
|
286
|
+
const SheetDescription = React.forwardRef(({ className, ...props }, ref) => {
|
|
287
|
+
const labels = React.useContext(SheetLabelContext);
|
|
288
|
+
return /* @__PURE__ */ jsx(
|
|
289
|
+
"p",
|
|
290
|
+
{
|
|
291
|
+
ref,
|
|
292
|
+
id: labels?.descriptionId,
|
|
293
|
+
"data-slot": "sheet-description",
|
|
294
|
+
className: cn("ui-sheet-description", className),
|
|
295
|
+
...props
|
|
296
|
+
}
|
|
297
|
+
);
|
|
298
|
+
});
|
|
299
|
+
SheetDescription.displayName = "SheetDescription";
|
|
231
300
|
export {
|
|
232
301
|
Sheet,
|
|
233
302
|
SheetBody,
|
|
@@ -1,10 +1,54 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
|
|
2
|
+
type Side = "top" | "right" | "bottom" | "left";
|
|
3
|
+
type Align = "start" | "center" | "end";
|
|
4
|
+
interface TooltipProviderProps {
|
|
5
|
+
/** Delay mở dùng chung cho mọi `Tooltip` bên trong, tính bằng ms. */
|
|
6
|
+
delayDuration?: number;
|
|
7
|
+
/** Không còn tác dụng trên nền RAC — cửa sổ warm-up là biến toàn cục, cứng ở 500ms. */
|
|
8
|
+
skipDelayDuration?: number;
|
|
9
|
+
/** Không còn tác dụng trên nền RAC — tooltip của React Aria luôn giữ mở khi rê lên nội dung. */
|
|
10
|
+
disableHoverableContent?: boolean;
|
|
11
|
+
}
|
|
3
12
|
/** Opt-in provider for tuning delay across a subtree. Each <Tooltip> already self-provides. */
|
|
4
|
-
export declare function TooltipProvider({ delayDuration,
|
|
5
|
-
|
|
6
|
-
|
|
13
|
+
export declare function TooltipProvider({ delayDuration, skipDelayDuration: _skipDelayDuration, disableHoverableContent: _disableHoverableContent, children, }: React.PropsWithChildren<TooltipProviderProps>): React.JSX.Element;
|
|
14
|
+
interface TooltipProps {
|
|
15
|
+
/** Trạng thái mở, có kiểm soát. */
|
|
16
|
+
open?: boolean;
|
|
17
|
+
/** Trạng thái mở ban đầu khi không kiểm soát. */
|
|
18
|
+
defaultOpen?: boolean;
|
|
19
|
+
/** Gọi khi trạng thái mở/đóng đổi. */
|
|
20
|
+
onOpenChange?: (open: boolean) => void;
|
|
21
|
+
/** Delay mở riêng cho tooltip này; thắng giá trị của `TooltipProvider`. */
|
|
7
22
|
delayDuration?: number;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
23
|
+
/** Không còn tác dụng trên nền RAC — tooltip của React Aria luôn giữ mở khi rê lên nội dung. */
|
|
24
|
+
disableHoverableContent?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** Self-contained tooltip — no app-level provider needed. Controllable via `open`/`onOpenChange`. */
|
|
27
|
+
export declare function Tooltip({ open, defaultOpen, onOpenChange, delayDuration, disableHoverableContent: _disableHoverableContent, children, }: React.PropsWithChildren<TooltipProps>): React.JSX.Element;
|
|
28
|
+
interface TooltipTriggerProps extends React.ComponentPropsWithRef<"button"> {
|
|
29
|
+
/** Mượn thẻ của con thay vì dựng `<button>` riêng. */
|
|
30
|
+
asChild?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare function TooltipTrigger({ asChild, children, ...props }: TooltipTriggerProps): React.JSX.Element;
|
|
33
|
+
interface TooltipContentProps extends React.ComponentPropsWithRef<"div"> {
|
|
34
|
+
/** Cạnh của trigger mà tooltip bám vào. */
|
|
35
|
+
side?: Side;
|
|
36
|
+
/** Canh tooltip theo cạnh của trigger. */
|
|
37
|
+
align?: Align;
|
|
38
|
+
/** Khoảng cách theo trục chính, tính bằng px. */
|
|
39
|
+
sideOffset?: number;
|
|
40
|
+
/** Khoảng cách theo trục phụ, tính bằng px. */
|
|
41
|
+
alignOffset?: number;
|
|
42
|
+
/** Lật tooltip sang cạnh đối diện khi hết chỗ. */
|
|
43
|
+
avoidCollisions?: boolean;
|
|
44
|
+
/** Khoảng chừa với mép khung nhìn. RAC chỉ nhận một số; object lấy cạnh lớn nhất. */
|
|
45
|
+
collisionPadding?: number | Partial<Record<Side, number>>;
|
|
46
|
+
/** Không còn tác dụng trên nền RAC — giữ trong kiểu vì là API công khai. */
|
|
47
|
+
sticky?: "partial" | "always";
|
|
48
|
+
/** Không còn tác dụng trên nền RAC — giữ trong kiểu vì là API công khai. */
|
|
49
|
+
hideWhenDetached?: boolean;
|
|
50
|
+
/** Không còn tác dụng trên nền RAC — giữ trong kiểu vì là API công khai. */
|
|
51
|
+
forceMount?: true;
|
|
52
|
+
}
|
|
53
|
+
export declare function TooltipContent({ className, style, children, ref, side, align, sideOffset, alignOffset, avoidCollisions, collisionPadding, sticky: _sticky, hideWhenDetached: _hideWhenDetached, forceMount: _forceMount, ...props }: TooltipContentProps): React.JSX.Element;
|
|
54
|
+
export {};
|
|
@@ -1,39 +1,121 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import
|
|
4
|
+
import { mergeRefs } from "@react-aria/utils";
|
|
5
|
+
import {
|
|
6
|
+
Focusable,
|
|
7
|
+
Tooltip as AriaTooltip,
|
|
8
|
+
TooltipTrigger as AriaTooltipTrigger,
|
|
9
|
+
TooltipTriggerStateContext
|
|
10
|
+
} from "react-aria-components";
|
|
5
11
|
import { cn } from "../../lib/utils.js";
|
|
12
|
+
import { Slot } from "../../lib/slot.js";
|
|
13
|
+
const DEFAULT_DELAY_DURATION = 200;
|
|
14
|
+
function toPlacement(side, align) {
|
|
15
|
+
if (align === "center") return side;
|
|
16
|
+
if (side === "top" || side === "bottom") return `${side} ${align}`;
|
|
17
|
+
return `${side} ${align === "start" ? "top" : "bottom"}`;
|
|
18
|
+
}
|
|
19
|
+
function toContainerPadding(padding) {
|
|
20
|
+
if (padding == null) return void 0;
|
|
21
|
+
if (typeof padding === "number") return padding;
|
|
22
|
+
const sides = Object.values(padding).filter(
|
|
23
|
+
(value) => typeof value === "number"
|
|
24
|
+
);
|
|
25
|
+
return sides.length ? Math.max(...sides) : void 0;
|
|
26
|
+
}
|
|
27
|
+
const TooltipDelayContext = React.createContext(void 0);
|
|
6
28
|
function TooltipProvider({
|
|
7
|
-
delayDuration =
|
|
8
|
-
|
|
29
|
+
delayDuration = DEFAULT_DELAY_DURATION,
|
|
30
|
+
skipDelayDuration: _skipDelayDuration,
|
|
31
|
+
disableHoverableContent: _disableHoverableContent,
|
|
32
|
+
children
|
|
9
33
|
}) {
|
|
10
|
-
return /* @__PURE__ */ jsx(
|
|
34
|
+
return /* @__PURE__ */ jsx(TooltipDelayContext.Provider, { value: delayDuration, children });
|
|
11
35
|
}
|
|
12
36
|
function Tooltip({
|
|
13
|
-
|
|
37
|
+
open,
|
|
38
|
+
defaultOpen,
|
|
39
|
+
onOpenChange,
|
|
40
|
+
delayDuration,
|
|
41
|
+
disableHoverableContent: _disableHoverableContent,
|
|
42
|
+
children
|
|
43
|
+
}) {
|
|
44
|
+
const providedDelay = React.useContext(TooltipDelayContext);
|
|
45
|
+
return /* @__PURE__ */ jsx(
|
|
46
|
+
AriaTooltipTrigger,
|
|
47
|
+
{
|
|
48
|
+
delay: delayDuration ?? providedDelay ?? DEFAULT_DELAY_DURATION,
|
|
49
|
+
isOpen: open,
|
|
50
|
+
defaultOpen,
|
|
51
|
+
onOpenChange,
|
|
52
|
+
children
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
function TooltipTrigger({ asChild, children, ...props }) {
|
|
57
|
+
const state = React.useContext(TooltipTriggerStateContext);
|
|
58
|
+
const Comp = asChild ? Slot : "button";
|
|
59
|
+
return /* @__PURE__ */ jsx(Focusable, { children: /* @__PURE__ */ jsx(Comp, { type: "button", "data-state": state?.isOpen ? "delayed-open" : "closed", ...props, children }) });
|
|
60
|
+
}
|
|
61
|
+
function TooltipContent({
|
|
62
|
+
className,
|
|
63
|
+
style,
|
|
64
|
+
children,
|
|
65
|
+
ref,
|
|
66
|
+
side = "top",
|
|
67
|
+
align = "center",
|
|
68
|
+
sideOffset = 6,
|
|
69
|
+
alignOffset,
|
|
70
|
+
avoidCollisions = true,
|
|
71
|
+
collisionPadding,
|
|
72
|
+
sticky: _sticky,
|
|
73
|
+
hideWhenDetached: _hideWhenDetached,
|
|
74
|
+
forceMount: _forceMount,
|
|
14
75
|
...props
|
|
15
76
|
}) {
|
|
16
|
-
return /* @__PURE__ */ jsx(
|
|
77
|
+
return /* @__PURE__ */ jsx(
|
|
78
|
+
AriaTooltip,
|
|
79
|
+
{
|
|
80
|
+
placement: toPlacement(side, align),
|
|
81
|
+
offset: sideOffset,
|
|
82
|
+
crossOffset: alignOffset,
|
|
83
|
+
shouldFlip: avoidCollisions,
|
|
84
|
+
containerPadding: toContainerPadding(collisionPadding),
|
|
85
|
+
render: (racProps, { isExiting }) => {
|
|
86
|
+
const {
|
|
87
|
+
className: _racClassName,
|
|
88
|
+
"data-rac": _rac,
|
|
89
|
+
ref: racRef,
|
|
90
|
+
style: racStyle,
|
|
91
|
+
children: _racChildren,
|
|
92
|
+
...rest
|
|
93
|
+
} = racProps;
|
|
94
|
+
return /* @__PURE__ */ jsx(
|
|
95
|
+
"div",
|
|
96
|
+
{
|
|
97
|
+
...rest,
|
|
98
|
+
"data-slot": "tooltip-content",
|
|
99
|
+
"data-side": side,
|
|
100
|
+
"data-align": align,
|
|
101
|
+
"data-state": isExiting ? "closed" : "delayed-open",
|
|
102
|
+
...props,
|
|
103
|
+
ref: mergeRefs(ref, racRef),
|
|
104
|
+
className: cn(
|
|
105
|
+
"ui-tooltip-content",
|
|
106
|
+
"data-[state=delayed-open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=delayed-open]:fade-in-0",
|
|
107
|
+
"data-[side=bottom]:slide-in-from-top-1 data-[side=top]:slide-in-from-bottom-1",
|
|
108
|
+
"data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1",
|
|
109
|
+
className
|
|
110
|
+
),
|
|
111
|
+
style: { ...racStyle, ...style },
|
|
112
|
+
children
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
);
|
|
17
118
|
}
|
|
18
|
-
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
19
|
-
const TooltipContent = React.forwardRef(({ className, sideOffset = 6, children, ...props }, ref) => /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(
|
|
20
|
-
TooltipPrimitive.Content,
|
|
21
|
-
{
|
|
22
|
-
ref,
|
|
23
|
-
"data-slot": "tooltip-content",
|
|
24
|
-
sideOffset,
|
|
25
|
-
className: cn(
|
|
26
|
-
"ui-tooltip-content",
|
|
27
|
-
"data-[state=delayed-open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=delayed-open]:fade-in-0",
|
|
28
|
-
"data-[side=bottom]:slide-in-from-top-1 data-[side=top]:slide-in-from-bottom-1",
|
|
29
|
-
"data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1",
|
|
30
|
-
className
|
|
31
|
-
),
|
|
32
|
-
...props,
|
|
33
|
-
children
|
|
34
|
-
}
|
|
35
|
-
) }));
|
|
36
|
-
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
37
119
|
export {
|
|
38
120
|
Tooltip,
|
|
39
121
|
TooltipContent,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { Slot } from "
|
|
4
|
+
import { Slot } from "../../lib/slot.js";
|
|
5
5
|
import { cva } from "class-variance-authority";
|
|
6
6
|
import { Loader2 } from "lucide-react";
|
|
7
7
|
import { cn } from "../../lib/utils.js";
|
|
@@ -12,7 +12,7 @@ const buttonVariants = cva("ui-button", {
|
|
|
12
12
|
// Colour is the TOKEN LAYER's (`.ui-button--default` in styles/control.css), not a utility's.
|
|
13
13
|
// The three utilities that used to sit here — `bg-primary text-primary-foreground
|
|
14
14
|
// hover:bg-primary/90` — restated the fill the components layer already declares and, being
|
|
15
|
-
// utilities, out-ranked it: the hover step this library
|
|
15
|
+
// utilities, out-ranked it: the hover step this library takes from the derived tier
|
|
16
16
|
// (`--primary-hover`) could never take effect while `hover:bg-primary/90` was emitted here.
|
|
17
17
|
default: "ui-button--default",
|
|
18
18
|
destructive: "ui-button--destructive bg-destructive text-destructive-foreground",
|
|
@@ -45,6 +45,23 @@ export interface LogoProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "
|
|
|
45
45
|
* readable wordmark sits beside it.
|
|
46
46
|
*/
|
|
47
47
|
label?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Mượn thẻ của con làm GỐC của logo thay vì dựng `<span>` — cách để cả cái logo trở thành một
|
|
50
|
+
* LIÊN KẾT (`<a>`, hay `<Link>` của router).
|
|
51
|
+
*
|
|
52
|
+
* Vì sao cần một prop chứ không phải bọc ngoài: `.ui-logo-lockup` là `display: inline-flex`, nên
|
|
53
|
+
* bọc nó trong một `<a>` thường (`display: inline`) đặt lockup lên một line box và phần descender
|
|
54
|
+
* của strut đội thêm vài px vào ô mark — logo bị nhích lên trong thanh trên. Consumer chỉ còn
|
|
55
|
+
* cách viết `className="flex"` lên thẻ `<a>`, thứ `ui-audit` chặn đúng theo luật
|
|
56
|
+
* `no-utility-layout` (đo ở godx-chat, 08/09/2026). Với `asChild`, chính thẻ của consumer MANG
|
|
57
|
+
* `.ui-logo-lockup`, nên không còn phần tử trung gian nào để canh.
|
|
58
|
+
*
|
|
59
|
+
* Đúng MỘT con, và ruột của nó bị bỏ qua: nội dung của logo vẫn do `glyph`/`mark`/`wordmark`
|
|
60
|
+
* quyết định.
|
|
61
|
+
*/
|
|
62
|
+
asChild?: boolean;
|
|
63
|
+
/** Chỉ dùng cùng `asChild`: đúng một phần tử để đi mượn. */
|
|
64
|
+
children?: React.ReactNode;
|
|
48
65
|
}
|
|
49
66
|
/**
|
|
50
67
|
* The band the glyph's ink actually occupies, as the (top edge, bottom edge) of the UNION of every
|