@fanvue/ui 2.14.3 → 2.15.1
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/cjs/components/Chip/Chip.cjs +5 -2
- package/dist/cjs/components/Chip/Chip.cjs.map +1 -1
- package/dist/cjs/components/Dialog/Dialog.cjs +3 -1
- package/dist/cjs/components/Dialog/Dialog.cjs.map +1 -1
- package/dist/cjs/components/Drawer/Drawer.cjs +15 -4
- package/dist/cjs/components/Drawer/Drawer.cjs.map +1 -1
- package/dist/cjs/components/DropdownMenu/DropdownMenu.cjs +72 -2
- package/dist/cjs/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
- package/dist/cjs/components/InfoBox/InfoBox.cjs +3 -1
- package/dist/cjs/components/InfoBox/InfoBox.cjs.map +1 -1
- package/dist/cjs/components/InlineEdit/InlineEdit.cjs +176 -0
- package/dist/cjs/components/InlineEdit/InlineEdit.cjs.map +1 -0
- package/dist/cjs/components/Logo/Logo.cjs +13 -5
- package/dist/cjs/components/Logo/Logo.cjs.map +1 -1
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/utils/useSuppressClickAfterDrag.cjs +71 -0
- package/dist/cjs/utils/useSuppressClickAfterDrag.cjs.map +1 -0
- package/dist/components/Chip/Chip.mjs +5 -2
- package/dist/components/Chip/Chip.mjs.map +1 -1
- package/dist/components/Dialog/Dialog.mjs +3 -1
- package/dist/components/Dialog/Dialog.mjs.map +1 -1
- package/dist/components/Drawer/Drawer.mjs +15 -4
- package/dist/components/Drawer/Drawer.mjs.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.mjs +72 -2
- package/dist/components/DropdownMenu/DropdownMenu.mjs.map +1 -1
- package/dist/components/InfoBox/InfoBox.mjs +3 -1
- package/dist/components/InfoBox/InfoBox.mjs.map +1 -1
- package/dist/components/InlineEdit/InlineEdit.mjs +159 -0
- package/dist/components/InlineEdit/InlineEdit.mjs.map +1 -0
- package/dist/components/Logo/Logo.mjs +13 -5
- package/dist/components/Logo/Logo.mjs.map +1 -1
- package/dist/index.d.ts +94 -9
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -1
- package/dist/utils/useSuppressClickAfterDrag.mjs +54 -0
- package/dist/utils/useSuppressClickAfterDrag.mjs.map +1 -0
- package/package.json +7 -8
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
5
|
+
const React = require("react");
|
|
6
|
+
const cn = require("../../utils/cn.cjs");
|
|
7
|
+
const Chip = require("../Chip/Chip.cjs");
|
|
8
|
+
function _interopNamespaceDefault(e) {
|
|
9
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
10
|
+
if (e) {
|
|
11
|
+
for (const k in e) {
|
|
12
|
+
if (k !== "default") {
|
|
13
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
14
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: () => e[k]
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
n.default = e;
|
|
22
|
+
return Object.freeze(n);
|
|
23
|
+
}
|
|
24
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
25
|
+
const InlineEdit = React__namespace.forwardRef(
|
|
26
|
+
({
|
|
27
|
+
value,
|
|
28
|
+
onSubmit,
|
|
29
|
+
onCancel,
|
|
30
|
+
size = "40",
|
|
31
|
+
disabled = false,
|
|
32
|
+
editLabel = "Edit",
|
|
33
|
+
leftIcon,
|
|
34
|
+
validate,
|
|
35
|
+
className,
|
|
36
|
+
placeholder,
|
|
37
|
+
onChange,
|
|
38
|
+
onBlur,
|
|
39
|
+
onKeyDown,
|
|
40
|
+
...inputProps
|
|
41
|
+
}, ref) => {
|
|
42
|
+
const [isEditing, setIsEditing] = React__namespace.useState(false);
|
|
43
|
+
const [draft, setDraft] = React__namespace.useState(value);
|
|
44
|
+
const inputRef = React__namespace.useRef(null);
|
|
45
|
+
const setInputRef = React__namespace.useCallback(
|
|
46
|
+
(node) => {
|
|
47
|
+
inputRef.current = node;
|
|
48
|
+
if (typeof ref === "function") {
|
|
49
|
+
ref(node);
|
|
50
|
+
} else if (ref) {
|
|
51
|
+
ref.current = node;
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
[ref]
|
|
55
|
+
);
|
|
56
|
+
React__namespace.useEffect(() => {
|
|
57
|
+
if (!isEditing) {
|
|
58
|
+
setDraft(value);
|
|
59
|
+
}
|
|
60
|
+
}, [value, isEditing]);
|
|
61
|
+
React__namespace.useEffect(() => {
|
|
62
|
+
if (!isEditing) return;
|
|
63
|
+
const input = inputRef.current;
|
|
64
|
+
if (!input) return;
|
|
65
|
+
input.focus();
|
|
66
|
+
input.select();
|
|
67
|
+
}, [isEditing]);
|
|
68
|
+
const enterEditMode = () => {
|
|
69
|
+
if (disabled) return;
|
|
70
|
+
setDraft(value);
|
|
71
|
+
setIsEditing(true);
|
|
72
|
+
};
|
|
73
|
+
const commit = () => {
|
|
74
|
+
const trimmed = draft.trim();
|
|
75
|
+
const isValid = validate ? validate(trimmed) : trimmed.length > 0;
|
|
76
|
+
if (!isValid) {
|
|
77
|
+
setDraft(value);
|
|
78
|
+
setIsEditing(false);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (trimmed !== value) {
|
|
82
|
+
onSubmit(trimmed);
|
|
83
|
+
}
|
|
84
|
+
setIsEditing(false);
|
|
85
|
+
};
|
|
86
|
+
const cancel = () => {
|
|
87
|
+
setDraft(value);
|
|
88
|
+
setIsEditing(false);
|
|
89
|
+
onCancel?.();
|
|
90
|
+
};
|
|
91
|
+
const handleChange = (event) => {
|
|
92
|
+
onChange?.(event);
|
|
93
|
+
setDraft(event.target.value);
|
|
94
|
+
};
|
|
95
|
+
const handleBlur = (event) => {
|
|
96
|
+
onBlur?.(event);
|
|
97
|
+
commit();
|
|
98
|
+
};
|
|
99
|
+
const handleKeyDown = (event) => {
|
|
100
|
+
onKeyDown?.(event);
|
|
101
|
+
if (event.defaultPrevented) return;
|
|
102
|
+
if (event.key === "Enter") {
|
|
103
|
+
event.preventDefault();
|
|
104
|
+
commit();
|
|
105
|
+
} else if (event.key === "Escape") {
|
|
106
|
+
event.preventDefault();
|
|
107
|
+
cancel();
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const showLeftIcon = Boolean(leftIcon) && !isEditing;
|
|
111
|
+
const sizerText = (isEditing ? draft : value) || placeholder || " ";
|
|
112
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
113
|
+
"span",
|
|
114
|
+
{
|
|
115
|
+
"data-testid": "inline-edit",
|
|
116
|
+
className: cn.cn("relative inline-block align-middle", className),
|
|
117
|
+
children: [
|
|
118
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
119
|
+
"span",
|
|
120
|
+
{
|
|
121
|
+
"aria-hidden": "true",
|
|
122
|
+
className: cn.cn(
|
|
123
|
+
"typography-semibold-body-sm invisible block whitespace-pre border border-transparent px-3",
|
|
124
|
+
size === "32" ? "h-8" : "h-10",
|
|
125
|
+
showLeftIcon && "pl-9"
|
|
126
|
+
),
|
|
127
|
+
children: sizerText
|
|
128
|
+
}
|
|
129
|
+
),
|
|
130
|
+
isEditing ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
131
|
+
Chip.Chip,
|
|
132
|
+
{
|
|
133
|
+
asChild: true,
|
|
134
|
+
dotted: true,
|
|
135
|
+
variant: "square",
|
|
136
|
+
size,
|
|
137
|
+
className: "absolute inset-0 block h-auto w-full focus-within:shadow-focus-ring",
|
|
138
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
139
|
+
"input",
|
|
140
|
+
{
|
|
141
|
+
ref: setInputRef,
|
|
142
|
+
type: "text",
|
|
143
|
+
value: draft,
|
|
144
|
+
placeholder,
|
|
145
|
+
onChange: handleChange,
|
|
146
|
+
onKeyDown: handleKeyDown,
|
|
147
|
+
onBlur: handleBlur,
|
|
148
|
+
"aria-label": editLabel,
|
|
149
|
+
"data-testid": "inline-edit-input",
|
|
150
|
+
className: "block h-full w-full bg-transparent px-3 outline-none",
|
|
151
|
+
...inputProps
|
|
152
|
+
}
|
|
153
|
+
) })
|
|
154
|
+
}
|
|
155
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
156
|
+
Chip.Chip,
|
|
157
|
+
{
|
|
158
|
+
dotted: true,
|
|
159
|
+
variant: "square",
|
|
160
|
+
size,
|
|
161
|
+
disabled,
|
|
162
|
+
leftIcon,
|
|
163
|
+
onClick: enterEditMode,
|
|
164
|
+
"data-testid": "inline-edit-trigger",
|
|
165
|
+
className: "absolute inset-0 h-auto w-full cursor-text",
|
|
166
|
+
children: value
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
InlineEdit.displayName = "InlineEdit";
|
|
175
|
+
exports.InlineEdit = InlineEdit;
|
|
176
|
+
//# sourceMappingURL=InlineEdit.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InlineEdit.cjs","sources":["../../../../src/components/InlineEdit/InlineEdit.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Chip } from \"../Chip/Chip\";\n\n/** Height of the inline edit field in pixels. */\nexport type InlineEditSize = \"32\" | \"40\";\n\nexport interface InlineEditProps\n extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"value\" | \"defaultValue\" | \"size\" | \"onSubmit\"\n > {\n /** Current value displayed in the chip and used as the starting draft when editing. */\n value: string;\n /** Called with the trimmed draft when the user commits an edit (Enter or blur). */\n onSubmit: (value: string) => void;\n /** Called when the user cancels an edit with Escape. */\n onCancel?: () => void;\n /** Height of the field in pixels. @default \"40\" */\n size?: InlineEditSize;\n /** Whether the field is disabled — prevents entering edit mode. @default false */\n disabled?: boolean;\n /** Accessible label for the edit input. @default \"Edit\" */\n editLabel?: string;\n /** Icon rendered before the value in display mode. Hidden when editing. */\n leftIcon?: React.ReactNode;\n /**\n * Validator for the trimmed draft on commit. Returning `false` reverts to\n * the previous value without calling `onSubmit`. @default rejects empty drafts\n */\n validate?: (draft: string) => boolean;\n /** Additional class name applied to the root element. */\n className?: string;\n}\n\n/**\n * A chip-styled inline edit field. Renders as a dashed-border button that\n * swaps to a text input on click, allowing the value to be edited in place.\n *\n * Enter and blur commit the draft via `onSubmit`. Escape reverts to `value`\n * and calls `onCancel`. Drafts that fail `validate` are reverted.\n *\n * The forwarded ref points at the underlying `<input>` and is only populated\n * while the field is in edit mode — it resolves to `null` in display mode.\n *\n * Consumers may pass `onChange`, `onBlur`, and `onKeyDown` to participate in\n * input events. The component's own handlers run after the consumer's, and\n * keyboard handling is skipped if the consumer calls `event.preventDefault()`.\n *\n * @example\n * ```tsx\n * const [name, setName] = useState(\"New folder\");\n * <InlineEdit value={name} onSubmit={setName} />\n * ```\n */\nexport const InlineEdit = React.forwardRef<HTMLInputElement, InlineEditProps>(\n (\n {\n value,\n onSubmit,\n onCancel,\n size = \"40\",\n disabled = false,\n editLabel = \"Edit\",\n leftIcon,\n validate,\n className,\n placeholder,\n onChange,\n onBlur,\n onKeyDown,\n ...inputProps\n },\n ref,\n ) => {\n const [isEditing, setIsEditing] = React.useState(false);\n const [draft, setDraft] = React.useState(value);\n const inputRef = React.useRef<HTMLInputElement | null>(null);\n\n const setInputRef = React.useCallback(\n (node: HTMLInputElement | null) => {\n inputRef.current = node;\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.RefObject<HTMLInputElement | null>).current = node;\n }\n },\n [ref],\n );\n\n React.useEffect(() => {\n if (!isEditing) {\n setDraft(value);\n }\n }, [value, isEditing]);\n\n React.useEffect(() => {\n if (!isEditing) return;\n const input = inputRef.current;\n if (!input) return;\n input.focus();\n input.select();\n }, [isEditing]);\n\n const enterEditMode = () => {\n if (disabled) return;\n setDraft(value);\n setIsEditing(true);\n };\n\n const commit = () => {\n const trimmed = draft.trim();\n const isValid = validate ? validate(trimmed) : trimmed.length > 0;\n if (!isValid) {\n setDraft(value);\n setIsEditing(false);\n return;\n }\n if (trimmed !== value) {\n onSubmit(trimmed);\n }\n setIsEditing(false);\n };\n\n const cancel = () => {\n setDraft(value);\n setIsEditing(false);\n onCancel?.();\n };\n\n const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n onChange?.(event);\n setDraft(event.target.value);\n };\n\n const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {\n onBlur?.(event);\n commit();\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event);\n if (event.defaultPrevented) return;\n if (event.key === \"Enter\") {\n event.preventDefault();\n commit();\n } else if (event.key === \"Escape\") {\n event.preventDefault();\n cancel();\n }\n };\n\n const showLeftIcon = Boolean(leftIcon) && !isEditing;\n const sizerText = (isEditing ? draft : value) || placeholder || \" \";\n\n return (\n <span\n data-testid=\"inline-edit\"\n className={cn(\"relative inline-block align-middle\", className)}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n \"typography-semibold-body-sm invisible block whitespace-pre border border-transparent px-3\",\n size === \"32\" ? \"h-8\" : \"h-10\",\n showLeftIcon && \"pl-9\",\n )}\n >\n {sizerText}\n </span>\n {isEditing ? (\n <Chip\n asChild\n dotted\n variant=\"square\"\n size={size}\n className=\"absolute inset-0 block h-auto w-full focus-within:shadow-focus-ring\"\n >\n <span>\n <input\n ref={setInputRef}\n type=\"text\"\n value={draft}\n placeholder={placeholder}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n onBlur={handleBlur}\n aria-label={editLabel}\n data-testid=\"inline-edit-input\"\n className=\"block h-full w-full bg-transparent px-3 outline-none\"\n {...inputProps}\n />\n </span>\n </Chip>\n ) : (\n <Chip\n dotted\n variant=\"square\"\n size={size}\n disabled={disabled}\n leftIcon={leftIcon}\n onClick={enterEditMode}\n data-testid=\"inline-edit-trigger\"\n className=\"absolute inset-0 h-auto w-full cursor-text\"\n >\n {value}\n </Chip>\n )}\n </span>\n );\n },\n);\n\nInlineEdit.displayName = \"InlineEdit\";\n"],"names":["React","jsxs","cn","jsx","Chip"],"mappings":"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"}
|
|
@@ -59,12 +59,19 @@ const getLogoColors = (color, variant) => {
|
|
|
59
59
|
// Default to adaptive color
|
|
60
60
|
};
|
|
61
61
|
};
|
|
62
|
+
const sizeClasses = {
|
|
63
|
+
"16": "h-4",
|
|
64
|
+
"20": "h-5",
|
|
65
|
+
"24": "h-6",
|
|
66
|
+
"32": "h-8",
|
|
67
|
+
"40": "h-10",
|
|
68
|
+
"48": "h-12",
|
|
69
|
+
"64": "h-16"
|
|
70
|
+
};
|
|
62
71
|
const WordmarkSVG = ({ className }) => {
|
|
63
72
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
64
73
|
"svg",
|
|
65
74
|
{
|
|
66
|
-
width: "128",
|
|
67
|
-
height: "30",
|
|
68
75
|
viewBox: "0 0 128 30",
|
|
69
76
|
fill: "none",
|
|
70
77
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -82,10 +89,11 @@ const WordmarkSVG = ({ className }) => {
|
|
|
82
89
|
);
|
|
83
90
|
};
|
|
84
91
|
const Logo = React__namespace.forwardRef(
|
|
85
|
-
({ className, variant = "full", color = "fullColour", ...props }, ref) => {
|
|
92
|
+
({ className, variant = "full", color = "fullColour", size, ...props }, ref) => {
|
|
86
93
|
const colors = getLogoColors(color, variant);
|
|
87
94
|
const showIcon = variant === "full" || variant === "icon" || variant === "portrait";
|
|
88
95
|
const showWordmark = variant === "full" || variant === "wordmark" || variant === "portrait";
|
|
96
|
+
const sizeClass = sizeClasses[size ?? (variant === "icon" ? "40" : "32")];
|
|
89
97
|
const ariaProps = props["aria-label"] ? { role: "img" } : {};
|
|
90
98
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
91
99
|
"div",
|
|
@@ -107,7 +115,7 @@ const Logo = React__namespace.forwardRef(
|
|
|
107
115
|
viewBox: "0 0 39 39",
|
|
108
116
|
fill: "none",
|
|
109
117
|
xmlns: "http://www.w3.org/2000/svg",
|
|
110
|
-
className: cn.cn("shrink-0",
|
|
118
|
+
className: cn.cn("w-auto shrink-0", sizeClass),
|
|
111
119
|
"aria-hidden": "true",
|
|
112
120
|
"data-testid": "logo-icon",
|
|
113
121
|
children: [
|
|
@@ -130,7 +138,7 @@ const Logo = React__namespace.forwardRef(
|
|
|
130
138
|
]
|
|
131
139
|
}
|
|
132
140
|
),
|
|
133
|
-
showWordmark && /* @__PURE__ */ jsxRuntime.jsx(WordmarkSVG, { className: cn.cn(colors.textClass) })
|
|
141
|
+
showWordmark && /* @__PURE__ */ jsxRuntime.jsx(WordmarkSVG, { className: cn.cn("w-auto", sizeClass, colors.textClass) })
|
|
134
142
|
]
|
|
135
143
|
}
|
|
136
144
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Logo.cjs","sources":["../../../../src/components/Logo/Logo.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nconst getLogoColors = (color: LogoColor, variant: LogoVariant) => {\n if (color === \"fullColour\") {\n return {\n icon: \"var(--color-brand-primary-default)\",\n iconInner: \"var(--primitives-color-gray-black)\",\n textClass: \"\", // Uses parent's text-content-primary\n };\n }\n\n if (color === \"decolour\") {\n return {\n iconClass: \"fill-[#151515] dark:fill-[#ffffff]\",\n iconInnerClass: \"fill-[#ffffff] dark:fill-[#151515]\",\n textClass: \"\", // Uses parent's text-content-primary\n };\n }\n\n if (color === \"whiteAlways\") {\n return {\n icon:\n variant === \"icon\"\n ? \"var(--primitives-color-gray-white)\"\n : \"var(--color-brand-primary-default)\",\n iconInner: \"var(--primitives-color-gray-black)\",\n textClass: \"text-content-on-brand-inverted\",\n };\n }\n\n if (color === \"blackAlways\") {\n return {\n icon:\n variant === \"icon\"\n ? \"var(--primitives-color-gray-black)\"\n : \"var(--color-brand-primary-default)\",\n iconInner:\n variant === \"icon\"\n ? \"var(--primitives-color-gray-white)\"\n : \"var(--primitives-color-gray-black)\",\n textClass: \"text-content-on-brand\",\n };\n }\n\n return {\n icon: \"var(--color-brand-primary-default)\",\n iconInner: \"var(--primitives-color-gray-black)\",\n textClass: \"\", // Default to adaptive color\n };\n};\n\n/** Layout variant of the logo. */\nexport type LogoVariant = \"full\" | \"icon\" | \"wordmark\" | \"portrait\";\n/** Colour scheme of the logo. */\nexport type LogoColor = \"fullColour\" | \"decolour\" | \"whiteAlways\" | \"blackAlways\";\n\nexport interface LogoProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Layout variant of the logo. @default \"full\" */\n variant?: LogoVariant;\n /** Colour scheme of the logo. @default \"fullColour\" */\n color?: LogoColor;\n /**\n * Accessible label for the logo. Required when `type` is `\"icon\"` and\n * the logo is used inside interactive contexts (links, buttons).\n *\n * @example \"Fanvue home\"\n */\n \"aria-label\"?: string;\n}\n\nconst WordmarkSVG = ({ className }: { className?: string }) => {\n return (\n <svg\n width=\"128\"\n height=\"30\"\n viewBox=\"0 0 128 30\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n className={className}\n data-testid=\"logo-wordmark\"\n >\n <path\n d=\"M89.0679 20.1823C89.0679 23.4373 90.1256 25.553 93.0961 25.553C95.9847 25.553 98.1815 23.0304 98.1815 17.7818V8.01701H102.902V29.0523H98.2629V25.3495C97.1238 27.75 95.2114 29.6218 91.9566 29.6218C86.464 29.6218 84.3888 26.0006 84.3888 21.1589V8.01701H89.0679V20.1823ZM116.586 7.44485C123.787 7.44485 126.717 12.9782 126.757 18.9592C126.757 19.1627 126.757 19.4883 126.716 19.8544H110.523C110.889 23.5569 113.249 25.8353 116.586 25.8353C118.986 25.8353 121.02 24.8995 121.752 22.8245H126.432C125.211 27.0966 121.59 29.6192 116.586 29.6192C110.279 29.6192 106.007 25.1028 106.007 18.4707C106.007 12.0829 110.483 7.44485 116.586 7.44485ZM29.0135 7.40527C35.971 7.40527 37.8834 11.5958 37.8834 16.112V24.2089C37.8834 25.7957 37.965 27.8301 38.2091 29.0508H33.408C33.3266 28.237 33.2858 27.4232 33.2858 26.5688V25.5922H33.2451C32.5534 27.301 30.7633 29.5795 26.5726 29.5796C21.8122 29.5796 19.1673 26.6501 19.1673 23.3137C19.1674 17.4955 26.2876 17.0073 29.3391 16.5191C32.0245 16.1122 33.2451 15.5831 33.2451 13.7929C33.2451 12.1248 31.6581 11.067 29.0949 11.067C26.8165 11.067 25.1484 12.3691 24.6601 14.4441H20.1846C20.7135 11.1078 23.5208 7.40535 29.0135 7.40527ZM66.6676 8.01701C68.4577 13.5504 70.2072 18.8399 71.9568 24.3326H71.9973C73.5435 19.2874 75.4559 13.5911 77.2055 8.01701H82.2099C79.606 15.0559 77.0835 22.0134 74.5202 29.0523H69.312L61.6223 8.01701H66.6676ZM18.3094 4.15021H4.92328V12.2878H17.2107V16.3973H4.92328V29.0508H0V0H18.3094V4.15021ZM52.6473 7.44485C58.099 7.44493 60.2147 11.066 60.2147 15.9077V29.0497H55.536V16.8839C55.536 13.629 54.437 11.5133 51.5078 11.5133C48.5783 11.5133 46.4216 14.036 46.4216 19.2845V29.0497H41.7024V8.01436H46.3406V11.7168C47.4392 9.31627 49.3921 7.44485 52.6473 7.44485ZM33.3265 17.0886C32.879 18.2685 31.7802 19.2856 28.1997 19.9773C25.3111 20.5062 23.8464 21.4015 23.8464 23.1509C23.8464 24.8191 25.2704 26.04 27.7523 26.04C30.5597 26.04 33.3265 24.2902 33.3265 19.2857V17.0886ZM116.586 11.1066C113.249 11.1066 111.011 13.263 110.564 16.5179H122.119C121.834 13.5071 120.085 11.1066 116.586 11.1066Z\"\n fill=\"currentColor\"\n />\n </svg>\n );\n};\n\n/**\n * The Fanvue brand logo. Supports full (icon + wordmark), icon-only, wordmark-only,\n * and portrait (stacked) layouts with multiple colour schemes.\n *\n * @example\n * ```tsx\n * <Logo type=\"full\" color=\"fullColour\" />\n * ```\n */\nexport const Logo = React.forwardRef<HTMLDivElement, LogoProps>(\n ({ className, variant = \"full\", color = \"fullColour\", ...props }, ref) => {\n const colors = getLogoColors(color, variant);\n const showIcon = variant === \"full\" || variant === \"icon\" || variant === \"portrait\";\n const showWordmark = variant === \"full\" || variant === \"wordmark\" || variant === \"portrait\";\n\n // When aria-label is provided, add role=\"img\" for proper accessibility\n const ariaProps = props[\"aria-label\"] ? { role: \"img\" as const } : {};\n\n return (\n <div\n ref={ref}\n data-testid=\"logo\"\n className={cn(\n \"inline-flex items-center text-content-primary\",\n variant === \"portrait\" ? \"flex-col gap-2\" : \"flex-row\",\n variant === \"full\" && \"gap-2\",\n className,\n )}\n {...ariaProps}\n {...props}\n >\n {showIcon && (\n <svg\n viewBox=\"0 0 39 39\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={cn(\"shrink-0\", variant === \"icon\" ? \"h-10 w-10\" : \"h-8 w-8\")}\n aria-hidden=\"true\"\n data-testid=\"logo-icon\"\n >\n <path\n d=\"M0 11.2339C0 5.02957 5.02957 0 11.2339 0H27.7661C33.9704 0 39 5.02957 39 11.2339V27.7661C39 33.9704 33.9704 39 27.7661 39H11.2339C5.02957 39 0 33.9704 0 27.7661V11.2339Z\"\n {...(color === \"decolour\" ? { className: colors.iconClass } : { fill: colors.icon })}\n />\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M12.277 30.5825C11.4418 30.5825 11.0355 29.8659 11.2059 29.1153C11.4275 28.0916 12.5838 25.0548 11.7145 23.6899C10.4361 21.6938 7.25562 21.9838 6.5397 20.9602C6.02371 20.2089 6.48355 19.478 7.19738 19.0493C8.79967 18.0257 11.902 18.3157 14.9191 16.3025C16.5895 15.2106 18.1237 12.9927 18.993 11.662C20.2203 9.78527 20.7487 9.39287 23.3226 9.39287H32.3376C33.7574 9.39287 34.202 11.8036 31.8852 12.0686C31.2886 12.1368 29.6977 12.3757 27.4306 12.6487C25.2658 12.9216 20.4589 13.5728 22.351 16.6608C23.7658 18.2816 26.7488 18.0769 27.4306 19.0493C27.9238 19.7225 27.4875 20.4384 26.9505 20.7824C25.3311 21.8061 21.8737 21.6938 18.8566 23.6899C16.8111 25.0548 15.1478 28.0916 14.4659 29.1153C13.9716 29.8659 13.1293 30.5825 12.294 30.5825H12.277Z\"\n {...(color === \"decolour\"\n ? { className: colors.iconInnerClass }\n : { fill: colors.iconInner })}\n />\n </svg>\n )}\n {showWordmark && <WordmarkSVG className={cn(colors.textClass)} />}\n </div>\n );\n },\n);\n\nLogo.displayName = \"Logo\";\n"],"names":["jsx","React","jsxs","cn"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Logo.cjs","sources":["../../../../src/components/Logo/Logo.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nconst getLogoColors = (color: LogoColor, variant: LogoVariant) => {\n if (color === \"fullColour\") {\n return {\n icon: \"var(--color-brand-primary-default)\",\n iconInner: \"var(--primitives-color-gray-black)\",\n textClass: \"\", // Uses parent's text-content-primary\n };\n }\n\n if (color === \"decolour\") {\n return {\n iconClass: \"fill-[#151515] dark:fill-[#ffffff]\",\n iconInnerClass: \"fill-[#ffffff] dark:fill-[#151515]\",\n textClass: \"\", // Uses parent's text-content-primary\n };\n }\n\n if (color === \"whiteAlways\") {\n return {\n icon:\n variant === \"icon\"\n ? \"var(--primitives-color-gray-white)\"\n : \"var(--color-brand-primary-default)\",\n iconInner: \"var(--primitives-color-gray-black)\",\n textClass: \"text-content-on-brand-inverted\",\n };\n }\n\n if (color === \"blackAlways\") {\n return {\n icon:\n variant === \"icon\"\n ? \"var(--primitives-color-gray-black)\"\n : \"var(--color-brand-primary-default)\",\n iconInner:\n variant === \"icon\"\n ? \"var(--primitives-color-gray-white)\"\n : \"var(--primitives-color-gray-black)\",\n textClass: \"text-content-on-brand\",\n };\n }\n\n return {\n icon: \"var(--color-brand-primary-default)\",\n iconInner: \"var(--primitives-color-gray-black)\",\n textClass: \"\", // Default to adaptive color\n };\n};\n\n/** Layout variant of the logo. */\nexport type LogoVariant = \"full\" | \"icon\" | \"wordmark\" | \"portrait\";\n/** Colour scheme of the logo. */\nexport type LogoColor = \"fullColour\" | \"decolour\" | \"whiteAlways\" | \"blackAlways\";\n/** Height of the logo in pixels. Both icon and wordmark scale proportionally. */\nexport type LogoSize = \"16\" | \"20\" | \"24\" | \"32\" | \"40\" | \"48\" | \"64\";\n\nconst sizeClasses: Record<LogoSize, string> = {\n \"16\": \"h-4\",\n \"20\": \"h-5\",\n \"24\": \"h-6\",\n \"32\": \"h-8\",\n \"40\": \"h-10\",\n \"48\": \"h-12\",\n \"64\": \"h-16\",\n};\n\nexport interface LogoProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Layout variant of the logo. @default \"full\" */\n variant?: LogoVariant;\n /** Colour scheme of the logo. @default \"fullColour\" */\n color?: LogoColor;\n /** Height of the logo in pixels. @default \"32\" (or \"40\" when `variant=\"icon\"`) */\n size?: LogoSize;\n /**\n * Accessible label for the logo. Required when `type` is `\"icon\"` and\n * the logo is used inside interactive contexts (links, buttons).\n *\n * @example \"Fanvue home\"\n */\n \"aria-label\"?: string;\n}\n\nconst WordmarkSVG = ({ className }: { className?: string }) => {\n return (\n <svg\n viewBox=\"0 0 128 30\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n className={className}\n data-testid=\"logo-wordmark\"\n >\n <path\n d=\"M89.0679 20.1823C89.0679 23.4373 90.1256 25.553 93.0961 25.553C95.9847 25.553 98.1815 23.0304 98.1815 17.7818V8.01701H102.902V29.0523H98.2629V25.3495C97.1238 27.75 95.2114 29.6218 91.9566 29.6218C86.464 29.6218 84.3888 26.0006 84.3888 21.1589V8.01701H89.0679V20.1823ZM116.586 7.44485C123.787 7.44485 126.717 12.9782 126.757 18.9592C126.757 19.1627 126.757 19.4883 126.716 19.8544H110.523C110.889 23.5569 113.249 25.8353 116.586 25.8353C118.986 25.8353 121.02 24.8995 121.752 22.8245H126.432C125.211 27.0966 121.59 29.6192 116.586 29.6192C110.279 29.6192 106.007 25.1028 106.007 18.4707C106.007 12.0829 110.483 7.44485 116.586 7.44485ZM29.0135 7.40527C35.971 7.40527 37.8834 11.5958 37.8834 16.112V24.2089C37.8834 25.7957 37.965 27.8301 38.2091 29.0508H33.408C33.3266 28.237 33.2858 27.4232 33.2858 26.5688V25.5922H33.2451C32.5534 27.301 30.7633 29.5795 26.5726 29.5796C21.8122 29.5796 19.1673 26.6501 19.1673 23.3137C19.1674 17.4955 26.2876 17.0073 29.3391 16.5191C32.0245 16.1122 33.2451 15.5831 33.2451 13.7929C33.2451 12.1248 31.6581 11.067 29.0949 11.067C26.8165 11.067 25.1484 12.3691 24.6601 14.4441H20.1846C20.7135 11.1078 23.5208 7.40535 29.0135 7.40527ZM66.6676 8.01701C68.4577 13.5504 70.2072 18.8399 71.9568 24.3326H71.9973C73.5435 19.2874 75.4559 13.5911 77.2055 8.01701H82.2099C79.606 15.0559 77.0835 22.0134 74.5202 29.0523H69.312L61.6223 8.01701H66.6676ZM18.3094 4.15021H4.92328V12.2878H17.2107V16.3973H4.92328V29.0508H0V0H18.3094V4.15021ZM52.6473 7.44485C58.099 7.44493 60.2147 11.066 60.2147 15.9077V29.0497H55.536V16.8839C55.536 13.629 54.437 11.5133 51.5078 11.5133C48.5783 11.5133 46.4216 14.036 46.4216 19.2845V29.0497H41.7024V8.01436H46.3406V11.7168C47.4392 9.31627 49.3921 7.44485 52.6473 7.44485ZM33.3265 17.0886C32.879 18.2685 31.7802 19.2856 28.1997 19.9773C25.3111 20.5062 23.8464 21.4015 23.8464 23.1509C23.8464 24.8191 25.2704 26.04 27.7523 26.04C30.5597 26.04 33.3265 24.2902 33.3265 19.2857V17.0886ZM116.586 11.1066C113.249 11.1066 111.011 13.263 110.564 16.5179H122.119C121.834 13.5071 120.085 11.1066 116.586 11.1066Z\"\n fill=\"currentColor\"\n />\n </svg>\n );\n};\n\n/**\n * The Fanvue brand logo. Supports full (icon + wordmark), icon-only, wordmark-only,\n * and portrait (stacked) layouts with multiple colour schemes.\n *\n * @example\n * ```tsx\n * <Logo type=\"full\" color=\"fullColour\" />\n * ```\n */\nexport const Logo = React.forwardRef<HTMLDivElement, LogoProps>(\n ({ className, variant = \"full\", color = \"fullColour\", size, ...props }, ref) => {\n const colors = getLogoColors(color, variant);\n const showIcon = variant === \"full\" || variant === \"icon\" || variant === \"portrait\";\n const showWordmark = variant === \"full\" || variant === \"wordmark\" || variant === \"portrait\";\n const sizeClass = sizeClasses[size ?? (variant === \"icon\" ? \"40\" : \"32\")];\n\n // When aria-label is provided, add role=\"img\" for proper accessibility\n const ariaProps = props[\"aria-label\"] ? { role: \"img\" as const } : {};\n\n return (\n <div\n ref={ref}\n data-testid=\"logo\"\n className={cn(\n \"inline-flex items-center text-content-primary\",\n variant === \"portrait\" ? \"flex-col gap-2\" : \"flex-row\",\n variant === \"full\" && \"gap-2\",\n className,\n )}\n {...ariaProps}\n {...props}\n >\n {showIcon && (\n <svg\n viewBox=\"0 0 39 39\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={cn(\"w-auto shrink-0\", sizeClass)}\n aria-hidden=\"true\"\n data-testid=\"logo-icon\"\n >\n <path\n d=\"M0 11.2339C0 5.02957 5.02957 0 11.2339 0H27.7661C33.9704 0 39 5.02957 39 11.2339V27.7661C39 33.9704 33.9704 39 27.7661 39H11.2339C5.02957 39 0 33.9704 0 27.7661V11.2339Z\"\n {...(color === \"decolour\" ? { className: colors.iconClass } : { fill: colors.icon })}\n />\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M12.277 30.5825C11.4418 30.5825 11.0355 29.8659 11.2059 29.1153C11.4275 28.0916 12.5838 25.0548 11.7145 23.6899C10.4361 21.6938 7.25562 21.9838 6.5397 20.9602C6.02371 20.2089 6.48355 19.478 7.19738 19.0493C8.79967 18.0257 11.902 18.3157 14.9191 16.3025C16.5895 15.2106 18.1237 12.9927 18.993 11.662C20.2203 9.78527 20.7487 9.39287 23.3226 9.39287H32.3376C33.7574 9.39287 34.202 11.8036 31.8852 12.0686C31.2886 12.1368 29.6977 12.3757 27.4306 12.6487C25.2658 12.9216 20.4589 13.5728 22.351 16.6608C23.7658 18.2816 26.7488 18.0769 27.4306 19.0493C27.9238 19.7225 27.4875 20.4384 26.9505 20.7824C25.3311 21.8061 21.8737 21.6938 18.8566 23.6899C16.8111 25.0548 15.1478 28.0916 14.4659 29.1153C13.9716 29.8659 13.1293 30.5825 12.294 30.5825H12.277Z\"\n {...(color === \"decolour\"\n ? { className: colors.iconInnerClass }\n : { fill: colors.iconInner })}\n />\n </svg>\n )}\n {showWordmark && <WordmarkSVG className={cn(\"w-auto\", sizeClass, colors.textClass)} />}\n </div>\n );\n },\n);\n\nLogo.displayName = \"Logo\";\n"],"names":["jsx","React","jsxs","cn"],"mappings":"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"}
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -185,6 +185,7 @@ const WifiOffIcon = require("./components/Icons/WifiOffIcon.cjs");
|
|
|
185
185
|
const WifiOnIcon = require("./components/Icons/WifiOnIcon.cjs");
|
|
186
186
|
const WrenchIcon = require("./components/Icons/WrenchIcon.cjs");
|
|
187
187
|
const InfoBox = require("./components/InfoBox/InfoBox.cjs");
|
|
188
|
+
const InlineEdit = require("./components/InlineEdit/InlineEdit.cjs");
|
|
188
189
|
const Loader = require("./components/Loader/Loader.cjs");
|
|
189
190
|
const Logo = require("./components/Logo/Logo.cjs");
|
|
190
191
|
const MobileStepper = require("./components/MobileStepper/MobileStepper.cjs");
|
|
@@ -445,6 +446,7 @@ exports.WrenchIcon = WrenchIcon.WrenchIcon;
|
|
|
445
446
|
exports.InfoBox = InfoBox.InfoBox;
|
|
446
447
|
exports.InfoBoxContent = InfoBox.InfoBoxContent;
|
|
447
448
|
exports.InfoBoxTrigger = InfoBox.InfoBoxTrigger;
|
|
449
|
+
exports.InlineEdit = InlineEdit.InlineEdit;
|
|
448
450
|
exports.Loader = Loader.Loader;
|
|
449
451
|
exports.Logo = Logo.Logo;
|
|
450
452
|
exports.MobileStepper = MobileStepper.MobileStepper;
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const React = require("react");
|
|
5
|
+
function _interopNamespaceDefault(e) {
|
|
6
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
7
|
+
if (e) {
|
|
8
|
+
for (const k in e) {
|
|
9
|
+
if (k !== "default") {
|
|
10
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
11
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: () => e[k]
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
n.default = e;
|
|
19
|
+
return Object.freeze(n);
|
|
20
|
+
}
|
|
21
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
22
|
+
const TAP_MOVEMENT_THRESHOLD_PX = 10;
|
|
23
|
+
function useSuppressClickAfterDrag(props) {
|
|
24
|
+
const tapRef = React__namespace.useRef(null);
|
|
25
|
+
return {
|
|
26
|
+
...props,
|
|
27
|
+
onPointerDown(event) {
|
|
28
|
+
props.onPointerDown?.(event);
|
|
29
|
+
if (event.pointerType === "mouse") {
|
|
30
|
+
tapRef.current = null;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
34
|
+
tapRef.current = {
|
|
35
|
+
pointerId: event.pointerId,
|
|
36
|
+
x: event.clientX,
|
|
37
|
+
y: event.clientY,
|
|
38
|
+
movedPastThreshold: false
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
onPointerMove(event) {
|
|
42
|
+
props.onPointerMove?.(event);
|
|
43
|
+
const tap = tapRef.current;
|
|
44
|
+
if (tap === null || event.pointerId !== tap.pointerId || tap.movedPastThreshold) return;
|
|
45
|
+
const dx = event.clientX - tap.x;
|
|
46
|
+
const dy = event.clientY - tap.y;
|
|
47
|
+
if (Math.hypot(dx, dy) > TAP_MOVEMENT_THRESHOLD_PX) {
|
|
48
|
+
tap.movedPastThreshold = true;
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
onPointerCancel(event) {
|
|
52
|
+
props.onPointerCancel?.(event);
|
|
53
|
+
const tap = tapRef.current;
|
|
54
|
+
if (tap !== null && event.pointerId === tap.pointerId) {
|
|
55
|
+
tapRef.current = null;
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
onClick(event) {
|
|
59
|
+
const tap = tapRef.current;
|
|
60
|
+
tapRef.current = null;
|
|
61
|
+
if (tap?.movedPastThreshold) {
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
event.stopPropagation();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
props.onClick?.(event);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
exports.useSuppressClickAfterDrag = useSuppressClickAfterDrag;
|
|
71
|
+
//# sourceMappingURL=useSuppressClickAfterDrag.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSuppressClickAfterDrag.cjs","sources":["../../../src/utils/useSuppressClickAfterDrag.ts"],"sourcesContent":["import * as React from \"react\";\n\nconst TAP_MOVEMENT_THRESHOLD_PX = 10;\n\ntype ActiveTap = {\n pointerId: number;\n x: number;\n y: number;\n movedPastThreshold: boolean;\n};\n\ntype GatedHandlers = {\n onPointerDown?: React.PointerEventHandler;\n onPointerMove?: React.PointerEventHandler;\n onPointerCancel?: React.PointerEventHandler;\n onClick?: React.MouseEventHandler;\n};\n\n/**\n * Composes the consumer's pointer/click handlers with a touch-drag tracker\n * that suppresses the synthetic click an Android Chrome scroll-drag-end can\n * dispatch on a click-based Radix trigger (Popover / Dialog / Drawer).\n *\n * On touch / pen input, if the press-and-release crosses a movement threshold,\n * the subsequent `click` is preventDefault'd — short-circuiting Radix's own\n * `onOpenToggle` via `composeEventHandlers` — and stopPropagation'd so it\n * doesn't bubble to ancestors. Mouse input passes through unchanged because\n * mouse never triggers the Android scroll-drag bug class.\n *\n * Related: radix-ui/primitives#1912 (DropdownMenu pointerdown variant) and\n * #2702 (DismissableLayer touch dismiss). For the DropdownMenu pointerdown\n * variant see `components/DropdownMenu/DropdownMenu.tsx`.\n */\nexport function useSuppressClickAfterDrag<P extends GatedHandlers>(props: P): P {\n const tapRef = React.useRef<ActiveTap | null>(null);\n\n return {\n ...props,\n onPointerDown(event) {\n props.onPointerDown?.(event);\n if (event.pointerType === \"mouse\") {\n tapRef.current = null;\n return;\n }\n // Keep pointermove on this element if the finger drifts off.\n // Optional because jsdom (used in tests) doesn't implement it.\n event.currentTarget.setPointerCapture?.(event.pointerId);\n tapRef.current = {\n pointerId: event.pointerId,\n x: event.clientX,\n y: event.clientY,\n movedPastThreshold: false,\n };\n },\n onPointerMove(event) {\n props.onPointerMove?.(event);\n const tap = tapRef.current;\n if (tap === null || event.pointerId !== tap.pointerId || tap.movedPastThreshold) return;\n const dx = event.clientX - tap.x;\n const dy = event.clientY - tap.y;\n if (Math.hypot(dx, dy) > TAP_MOVEMENT_THRESHOLD_PX) {\n tap.movedPastThreshold = true;\n }\n },\n onPointerCancel(event) {\n props.onPointerCancel?.(event);\n const tap = tapRef.current;\n if (tap !== null && event.pointerId === tap.pointerId) {\n tapRef.current = null;\n }\n },\n onClick(event) {\n const tap = tapRef.current;\n tapRef.current = null;\n if (tap?.movedPastThreshold) {\n // preventDefault stops Radix's onClick → onOpenToggle via\n // composeEventHandlers. stopPropagation prevents the synthetic click\n // from bubbling to ancestor click handlers.\n event.preventDefault();\n event.stopPropagation();\n return;\n }\n props.onClick?.(event);\n },\n };\n}\n"],"names":["React"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAEA,MAAM,4BAA4B;AA+B3B,SAAS,0BAAmD,OAAa;AAC9E,QAAM,SAASA,iBAAM,OAAyB,IAAI;AAElD,SAAO;AAAA,IACL,GAAG;AAAA,IACH,cAAc,OAAO;AACnB,YAAM,gBAAgB,KAAK;AAC3B,UAAI,MAAM,gBAAgB,SAAS;AACjC,eAAO,UAAU;AACjB;AAAA,MACF;AAGA,YAAM,cAAc,oBAAoB,MAAM,SAAS;AACvD,aAAO,UAAU;AAAA,QACf,WAAW,MAAM;AAAA,QACjB,GAAG,MAAM;AAAA,QACT,GAAG,MAAM;AAAA,QACT,oBAAoB;AAAA,MAAA;AAAA,IAExB;AAAA,IACA,cAAc,OAAO;AACnB,YAAM,gBAAgB,KAAK;AAC3B,YAAM,MAAM,OAAO;AACnB,UAAI,QAAQ,QAAQ,MAAM,cAAc,IAAI,aAAa,IAAI,mBAAoB;AACjF,YAAM,KAAK,MAAM,UAAU,IAAI;AAC/B,YAAM,KAAK,MAAM,UAAU,IAAI;AAC/B,UAAI,KAAK,MAAM,IAAI,EAAE,IAAI,2BAA2B;AAClD,YAAI,qBAAqB;AAAA,MAC3B;AAAA,IACF;AAAA,IACA,gBAAgB,OAAO;AACrB,YAAM,kBAAkB,KAAK;AAC7B,YAAM,MAAM,OAAO;AACnB,UAAI,QAAQ,QAAQ,MAAM,cAAc,IAAI,WAAW;AACrD,eAAO,UAAU;AAAA,MACnB;AAAA,IACF;AAAA,IACA,QAAQ,OAAO;AACb,YAAM,MAAM,OAAO;AACnB,aAAO,UAAU;AACjB,UAAI,KAAK,oBAAoB;AAI3B,cAAM,eAAA;AACN,cAAM,gBAAA;AACN;AAAA,MACF;AACA,YAAM,UAAU,KAAK;AAAA,IACvB;AAAA,EAAA;AAEJ;;"}
|
|
@@ -11,6 +11,7 @@ const Chip = React.forwardRef(
|
|
|
11
11
|
selected = false,
|
|
12
12
|
disabled = false,
|
|
13
13
|
leftDot = false,
|
|
14
|
+
dotted = false,
|
|
14
15
|
leftIcon,
|
|
15
16
|
rightIcon,
|
|
16
17
|
notificationLabel,
|
|
@@ -37,10 +38,12 @@ const Chip = React.forwardRef(
|
|
|
37
38
|
// Variant colors
|
|
38
39
|
isDark && "bg-neutral-alphas-150 text-content-on-brand-inverted",
|
|
39
40
|
!isDark && selected && "bg-brand-primary-muted text-neutral-alphas-900",
|
|
40
|
-
!isDark && !selected && "bg-neutral-alphas-50 text-neutral-alphas-900",
|
|
41
|
+
!isDark && !selected && !dotted && "bg-neutral-alphas-50 text-neutral-alphas-900",
|
|
42
|
+
!isDark && !selected && dotted && "border border-dashed border-border-primary bg-transparent text-neutral-alphas-900",
|
|
41
43
|
// Interactive
|
|
42
44
|
isInteractive && !disabled && "cursor-pointer",
|
|
43
|
-
isInteractive && !disabled && !isDark && !selected && "hover:bg-brand-primary-muted active:bg-brand-primary-muted",
|
|
45
|
+
isInteractive && !disabled && !isDark && !selected && !dotted && "hover:bg-brand-primary-muted active:bg-brand-primary-muted",
|
|
46
|
+
isInteractive && !disabled && !isDark && !selected && dotted && "hover:border-neutral-alphas-500 hover:bg-neutral-alphas-50 active:border-neutral-alphas-500 active:bg-neutral-alphas-50",
|
|
44
47
|
// Focus
|
|
45
48
|
"focus-visible:shadow-focus-ring focus-visible:outline-none",
|
|
46
49
|
// Disabled
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chip.mjs","sources":["../../../src/components/Chip/Chip.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Visual variant of the chip. */\nexport type ChipVariant = \"rounded\" | \"square\" | \"dark\";\n/** Height of the chip in pixels. */\nexport type ChipSize = \"32\" | \"40\";\n\nexport interface ChipProps extends React.HTMLAttributes<HTMLElement> {\n /** Visual variant of the chip. @default \"rounded\" */\n variant?: ChipVariant;\n /** Height of the chip in pixels. @default \"32\" */\n size?: ChipSize;\n /** Whether the chip is in a selected (pressed) state. @default false */\n selected?: boolean;\n /** Whether the chip is disabled. @default false */\n disabled?: boolean;\n /** Whether to show a coloured status dot at the leading edge. @default false */\n leftDot?: boolean;\n /** Icon element displayed before the label. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed after the label. */\n rightIcon?: React.ReactNode;\n /** Notification badge content (e.g. `\"99+\"`). Passed as a string for i18n support. */\n notificationLabel?: string;\n /** Click handler — when provided, the chip renders as a `<button>` for accessibility. */\n onClick?: React.MouseEventHandler<HTMLElement>;\n /** Merge props onto a child element instead of rendering a wrapper. @default false */\n asChild?: boolean;\n}\n\n/**\n * A compact element for filters, tags, or toggleable actions. When an `onClick`\n * handler is provided, the chip renders as an interactive `<button>` with\n * `aria-pressed` support.\n *\n * @example\n * ```tsx\n * <Chip selected onClick={toggle}>Music</Chip>\n * ```\n */\nexport const Chip = React.forwardRef<HTMLButtonElement, ChipProps>(\n (\n {\n className,\n variant = \"rounded\",\n size = \"32\",\n selected = false,\n disabled = false,\n leftDot = false,\n leftIcon,\n rightIcon,\n notificationLabel,\n onClick,\n asChild = false,\n children,\n ...props\n },\n ref,\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Variant-heavy UI component\n ) => {\n const isInteractive = !!onClick && !asChild;\n const Comp = asChild ? Slot : isInteractive ? \"button\" : \"span\";\n const isDark = variant === \"dark\";\n\n return (\n <Comp\n ref={ref}\n data-testid=\"chip\"\n className={cn(\n \"typography-semibold-body-sm relative inline-flex min-w-0 items-center justify-center whitespace-nowrap motion-safe:transition-colors motion-safe:duration-150\",\n // Shape\n variant === \"square\" ? \"rounded-xs\" : \"rounded-full\",\n // Size\n size === \"32\" && \"h-8 py-1\",\n size === \"40\" && \"h-10 py-2.5\",\n // Variant colors\n isDark && \"bg-neutral-alphas-150 text-content-on-brand-inverted\",\n !isDark && selected && \"bg-brand-primary-muted text-neutral-alphas-900\",\n !isDark && !selected && \"bg-neutral-alphas-50 text-neutral-alphas-900\",\n // Interactive\n isInteractive && !disabled && \"cursor-pointer\",\n isInteractive &&\n !disabled &&\n !isDark &&\n !selected &&\n \"hover:bg-brand-primary-muted active:bg-brand-primary-muted\",\n // Focus\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n // Disabled\n disabled && isDark && \"pointer-events-none opacity-50\",\n disabled && !isDark && \"pointer-events-none text-neutral-alphas-400\",\n className,\n )}\n {...(isInteractive && {\n type: \"button\" as const,\n disabled,\n \"aria-pressed\": selected,\n onClick,\n })}\n {...(!isInteractive && disabled && { \"aria-disabled\": true })}\n {...(selected && { \"data-selected\": \"\" })}\n {...props}\n >\n {asChild ? (\n <Slottable>{children}</Slottable>\n ) : (\n <>\n <span className=\"flex min-w-0 items-center gap-0.5 overflow-hidden px-3\">\n {leftDot && (\n <span className=\"size-2 shrink-0 rounded-full bg-current\" aria-hidden=\"true\" />\n )}\n {leftIcon && (\n <span className=\"flex shrink-0 items-center justify-center\" aria-hidden=\"true\">\n {leftIcon}\n </span>\n )}\n <span className=\"min-w-0 truncate\">{children}</span>\n {rightIcon && (\n <span\n className=\"flex size-5 shrink-0 items-center justify-center\"\n aria-hidden=\"true\"\n >\n {rightIcon}\n </span>\n )}\n </span>\n {notificationLabel && (\n <span className=\"typography-semibold-body-sm absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-content-primary px-1 text-content-primary-inverted\">\n {notificationLabel}\n </span>\n )}\n </>\n )}\n </Comp>\n );\n },\n);\n\nChip.displayName = \"Chip\";\n"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"Chip.mjs","sources":["../../../src/components/Chip/Chip.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Visual variant of the chip. */\nexport type ChipVariant = \"rounded\" | \"square\" | \"dark\";\n/** Height of the chip in pixels. */\nexport type ChipSize = \"32\" | \"40\";\n\nexport interface ChipProps extends React.HTMLAttributes<HTMLElement> {\n /** Visual variant of the chip. @default \"rounded\" */\n variant?: ChipVariant;\n /** Height of the chip in pixels. @default \"32\" */\n size?: ChipSize;\n /** Whether the chip is in a selected (pressed) state. @default false */\n selected?: boolean;\n /** Whether the chip is disabled. @default false */\n disabled?: boolean;\n /** Whether to show a coloured status dot at the leading edge. @default false */\n leftDot?: boolean;\n /**\n * Whether the chip uses a dashed border for add/create affordances.\n * Has no effect when `variant=\"dark\"` or `selected` is `true`.\n * @default false\n */\n dotted?: boolean;\n /** Icon element displayed before the label. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed after the label. */\n rightIcon?: React.ReactNode;\n /** Notification badge content (e.g. `\"99+\"`). Passed as a string for i18n support. */\n notificationLabel?: string;\n /** Click handler — when provided, the chip renders as a `<button>` for accessibility. */\n onClick?: React.MouseEventHandler<HTMLElement>;\n /** Merge props onto a child element instead of rendering a wrapper. @default false */\n asChild?: boolean;\n}\n\n/**\n * A compact element for filters, tags, or toggleable actions. When an `onClick`\n * handler is provided, the chip renders as an interactive `<button>` with\n * `aria-pressed` support.\n *\n * @example\n * ```tsx\n * <Chip selected onClick={toggle}>Music</Chip>\n * ```\n */\nexport const Chip = React.forwardRef<HTMLButtonElement, ChipProps>(\n (\n {\n className,\n variant = \"rounded\",\n size = \"32\",\n selected = false,\n disabled = false,\n leftDot = false,\n dotted = false,\n leftIcon,\n rightIcon,\n notificationLabel,\n onClick,\n asChild = false,\n children,\n ...props\n },\n ref,\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Variant-heavy UI component\n ) => {\n const isInteractive = !!onClick && !asChild;\n const Comp = asChild ? Slot : isInteractive ? \"button\" : \"span\";\n const isDark = variant === \"dark\";\n\n return (\n <Comp\n ref={ref}\n data-testid=\"chip\"\n className={cn(\n \"typography-semibold-body-sm relative inline-flex min-w-0 items-center justify-center whitespace-nowrap motion-safe:transition-colors motion-safe:duration-150\",\n // Shape\n variant === \"square\" ? \"rounded-xs\" : \"rounded-full\",\n // Size\n size === \"32\" && \"h-8 py-1\",\n size === \"40\" && \"h-10 py-2.5\",\n // Variant colors\n isDark && \"bg-neutral-alphas-150 text-content-on-brand-inverted\",\n !isDark && selected && \"bg-brand-primary-muted text-neutral-alphas-900\",\n !isDark && !selected && !dotted && \"bg-neutral-alphas-50 text-neutral-alphas-900\",\n !isDark &&\n !selected &&\n dotted &&\n \"border border-dashed border-border-primary bg-transparent text-neutral-alphas-900\",\n // Interactive\n isInteractive && !disabled && \"cursor-pointer\",\n isInteractive &&\n !disabled &&\n !isDark &&\n !selected &&\n !dotted &&\n \"hover:bg-brand-primary-muted active:bg-brand-primary-muted\",\n isInteractive &&\n !disabled &&\n !isDark &&\n !selected &&\n dotted &&\n \"hover:border-neutral-alphas-500 hover:bg-neutral-alphas-50 active:border-neutral-alphas-500 active:bg-neutral-alphas-50\",\n // Focus\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n // Disabled\n disabled && isDark && \"pointer-events-none opacity-50\",\n disabled && !isDark && \"pointer-events-none text-neutral-alphas-400\",\n className,\n )}\n {...(isInteractive && {\n type: \"button\" as const,\n disabled,\n \"aria-pressed\": selected,\n onClick,\n })}\n {...(!isInteractive && disabled && { \"aria-disabled\": true })}\n {...(selected && { \"data-selected\": \"\" })}\n {...props}\n >\n {asChild ? (\n <Slottable>{children}</Slottable>\n ) : (\n <>\n <span className=\"flex min-w-0 items-center gap-0.5 overflow-hidden px-3\">\n {leftDot && (\n <span className=\"size-2 shrink-0 rounded-full bg-current\" aria-hidden=\"true\" />\n )}\n {leftIcon && (\n <span className=\"flex shrink-0 items-center justify-center\" aria-hidden=\"true\">\n {leftIcon}\n </span>\n )}\n <span className=\"min-w-0 truncate\">{children}</span>\n {rightIcon && (\n <span\n className=\"flex size-5 shrink-0 items-center justify-center\"\n aria-hidden=\"true\"\n >\n {rightIcon}\n </span>\n )}\n </span>\n {notificationLabel && (\n <span className=\"typography-semibold-body-sm absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-content-primary px-1 text-content-primary-inverted\">\n {notificationLabel}\n </span>\n )}\n </>\n )}\n </Comp>\n );\n },\n);\n\nChip.displayName = \"Chip\";\n"],"names":[],"mappings":";;;;;AAgDO,MAAM,OAAO,MAAM;AAAA,EACxB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV,OAAO;AAAA,IACP,WAAW;AAAA,IACX,WAAW;AAAA,IACX,UAAU;AAAA,IACV,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QAEG;AACH,UAAM,gBAAgB,CAAC,CAAC,WAAW,CAAC;AACpC,UAAM,OAAO,UAAU,OAAO,gBAAgB,WAAW;AACzD,UAAM,SAAS,YAAY;AAE3B,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,eAAY;AAAA,QACZ,WAAW;AAAA,UACT;AAAA;AAAA,UAEA,YAAY,WAAW,eAAe;AAAA;AAAA,UAEtC,SAAS,QAAQ;AAAA,UACjB,SAAS,QAAQ;AAAA;AAAA,UAEjB,UAAU;AAAA,UACV,CAAC,UAAU,YAAY;AAAA,UACvB,CAAC,UAAU,CAAC,YAAY,CAAC,UAAU;AAAA,UACnC,CAAC,UACC,CAAC,YACD,UACA;AAAA;AAAA,UAEF,iBAAiB,CAAC,YAAY;AAAA,UAC9B,iBACE,CAAC,YACD,CAAC,UACD,CAAC,YACD,CAAC,UACD;AAAA,UACF,iBACE,CAAC,YACD,CAAC,UACD,CAAC,YACD,UACA;AAAA;AAAA,UAEF;AAAA;AAAA,UAEA,YAAY,UAAU;AAAA,UACtB,YAAY,CAAC,UAAU;AAAA,UACvB;AAAA,QAAA;AAAA,QAED,GAAI,iBAAiB;AAAA,UACpB,MAAM;AAAA,UACN;AAAA,UACA,gBAAgB;AAAA,UAChB;AAAA,QAAA;AAAA,QAED,GAAI,CAAC,iBAAiB,YAAY,EAAE,iBAAiB,KAAA;AAAA,QACrD,GAAI,YAAY,EAAE,iBAAiB,GAAA;AAAA,QACnC,GAAG;AAAA,QAEH,UAAA,UACC,oBAAC,WAAA,EAAW,SAAA,CAAS,IAErB,qBAAA,UAAA,EACE,UAAA;AAAA,UAAA,qBAAC,QAAA,EAAK,WAAU,0DACb,UAAA;AAAA,YAAA,WACC,oBAAC,QAAA,EAAK,WAAU,2CAA0C,eAAY,QAAO;AAAA,YAE9E,YACC,oBAAC,QAAA,EAAK,WAAU,6CAA4C,eAAY,QACrE,UAAA,UACH;AAAA,YAEF,oBAAC,QAAA,EAAK,WAAU,oBAAoB,SAAA,CAAS;AAAA,YAC5C,aACC;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,WAAU;AAAA,gBACV,eAAY;AAAA,gBAEX,UAAA;AAAA,cAAA;AAAA,YAAA;AAAA,UACH,GAEJ;AAAA,UACC,qBACC,oBAAC,QAAA,EAAK,WAAU,wKACb,UAAA,kBAAA,CACH;AAAA,QAAA,EAAA,CAEJ;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,KAAK,cAAc;"}
|
|
@@ -3,11 +3,13 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { cn } from "../../utils/cn.mjs";
|
|
6
|
+
import { useSuppressClickAfterDrag } from "../../utils/useSuppressClickAfterDrag.mjs";
|
|
6
7
|
import { IconButton } from "../IconButton/IconButton.mjs";
|
|
7
8
|
import { ArrowLeftIcon } from "../Icons/ArrowLeftIcon.mjs";
|
|
8
9
|
import { CloseIcon } from "../Icons/CloseIcon.mjs";
|
|
9
10
|
const Dialog = DialogPrimitive.Root;
|
|
10
|
-
const DialogTrigger = DialogPrimitive.Trigger;
|
|
11
|
+
const DialogTrigger = React.forwardRef((props, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Trigger, { ref, ...useSuppressClickAfterDrag(props) }));
|
|
12
|
+
DialogTrigger.displayName = "DialogTrigger";
|
|
11
13
|
const DialogClose = DialogPrimitive.Close;
|
|
12
14
|
const DialogOverlay = React.forwardRef(({ className, style, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
13
15
|
DialogPrimitive.Overlay,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dialog.mjs","sources":["../../../src/components/Dialog/Dialog.tsx"],"sourcesContent":["import * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { ArrowLeftIcon } from \"../Icons/ArrowLeftIcon\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\n\n/** Props for the {@link Dialog} root component. */\nexport interface DialogProps extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root> {\n /** Controlled open state. When provided, you must also supply `onOpenChange`. */\n open?: boolean;\n /** Called when the open state changes. Required when `open` is controlled. */\n onOpenChange?: (open: boolean) => void;\n /** The open state of the dialog when it is initially rendered (uncontrolled). */\n defaultOpen?: boolean;\n}\n\n/** Root component that manages open/close state for a dialog. */\nexport const Dialog = DialogPrimitive.Root;\n\n/** Props for the {@link DialogTrigger} component. */\nexport type DialogTriggerProps = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Trigger>;\n\n/** The element that opens the dialog when clicked. */\nexport const DialogTrigger = DialogPrimitive.Trigger;\n\n/** Convenience alias for Radix `Dialog.Close`. Closes the dialog when clicked. */\nexport const DialogClose = DialogPrimitive.Close;\n\n/** Props for the {@link DialogClose} component. */\nexport type DialogCloseProps = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Close>;\n\nexport interface DialogOverlayProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> {}\n\n/**\n * Semi-transparent backdrop rendered behind the dialog content.\n * Rendered inside a portal automatically by {@link DialogContent}.\n */\nexport const DialogOverlay = React.forwardRef<\n React.ComponentRef<typeof DialogPrimitive.Overlay>,\n DialogOverlayProps\n>(({ className, style, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n \"data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 fixed inset-0 bg-bg-overlay data-[state=closed]:animate-out data-[state=open]:animate-in\",\n className,\n )}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...style }}\n {...props}\n />\n));\nDialogOverlay.displayName = \"DialogOverlay\";\n\nexport interface DialogContentProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {\n /**\n * Width preset for the dialog.\n * - `\"sm\"` — 400px max-width (confirmations, simple forms)\n * - `\"md\"` — 440px max-width (default, standard dialogs)\n * - `\"lg\"` — 600px max-width (complex content, tables)\n *\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /** When true, renders overlay automatically. @default true */\n overlay?: boolean;\n}\n\nconst SIZE_CLASSES: Record<NonNullable<DialogContentProps[\"size\"]>, string> = {\n sm: \"sm:max-w-[400px]\",\n md: \"sm:max-w-[440px]\",\n lg: \"sm:max-w-[600px]\",\n};\n\n/**\n * The dialog panel rendered inside a portal. Includes the overlay by default.\n *\n * On mobile viewports (<640px), the dialog slides up from the bottom as a sheet\n * with top-only border radius. On larger viewports it renders centered with\n * full border radius.\n *\n * @example\n * ```tsx\n * <Dialog>\n * <DialogTrigger asChild>\n * <Button>Open</Button>\n * </DialogTrigger>\n * <DialogContent>\n * <DialogHeader>\n * <DialogTitle>Title</DialogTitle>\n * </DialogHeader>\n * <DialogBody>Content here</DialogBody>\n * <DialogFooter>\n * <DialogClose asChild>\n * <Button variant=\"secondary\">Cancel</Button>\n * </DialogClose>\n * <Button>Accept</Button>\n * </DialogFooter>\n * </DialogContent>\n * </Dialog>\n * ```\n */\nexport const DialogContent = React.forwardRef<\n React.ComponentRef<typeof DialogPrimitive.Content>,\n DialogContentProps\n>(({ className, children, size = \"md\", overlay = true, style, onOpenAutoFocus, ...props }, ref) => (\n <DialogPrimitive.Portal>\n {overlay && <DialogOverlay />}\n <DialogPrimitive.Content\n ref={ref}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...style }}\n onOpenAutoFocus={(e) => {\n if (onOpenAutoFocus) {\n onOpenAutoFocus(e);\n return;\n }\n e.preventDefault();\n (e.currentTarget as HTMLElement).focus();\n }}\n className={cn(\n // Base\n \"fixed flex flex-col overflow-hidden bg-bg-primary shadow-lg focus:outline-none dark:bg-surface-primary\",\n // Mobile: bottom sheet\n \"inset-x-0 bottom-0 max-h-[85vh] w-full rounded-t-lg\",\n // Animation (shared)\n \"data-[state=open]:fade-in-0 data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=closed]:animate-out\",\n // Mobile: slide up from bottom\n \"data-[state=open]:slide-in-from-bottom-full\",\n \"data-[state=closed]:slide-out-to-bottom-full\",\n // Desktop: centered dialog\n \"sm:inset-auto sm:top-1/2 sm:left-1/2 sm:max-h-[85vh] sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-lg\",\n // Desktop: scale in/out (cancel mobile slide)\n \"sm:data-[state=open]:slide-in-from-bottom-0 sm:data-[state=open]:zoom-in-95\",\n \"sm:data-[state=closed]:slide-out-to-bottom-0 sm:data-[state=closed]:zoom-out-95\",\n // Duration\n \"duration-200\",\n // Size\n SIZE_CLASSES[size],\n className,\n )}\n {...props}\n >\n {children}\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n));\nDialogContent.displayName = \"DialogContent\";\n\nexport interface DialogHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Show the close (X) button in the header. @default true */\n showClose?: boolean;\n /** Show a back arrow button on the left side. Defaults to `true` when `onBack` is provided. */\n showBack?: boolean;\n /** Called when the back button is clicked. */\n onBack?: () => void;\n /** Accessible label for the back button. @default \"Go back\" */\n backLabel?: string;\n /** Accessible label for the close button. @default \"Close\" */\n closeLabel?: string;\n}\n\n/**\n * Header bar for the dialog. Renders the title with an optional back arrow\n * and close button.\n *\n * @example\n * ```tsx\n * <DialogHeader>\n * <DialogTitle>Settings</DialogTitle>\n * </DialogHeader>\n *\n * <DialogHeader showBack onBack={() => setStep(0)}>\n * <DialogTitle>Step 2</DialogTitle>\n * </DialogHeader>\n * ```\n */\nexport const DialogHeader = React.forwardRef<HTMLDivElement, DialogHeaderProps>(\n (\n {\n className,\n children,\n showClose = true,\n showBack,\n onBack,\n backLabel = \"Go back\",\n closeLabel = \"Close\",\n ...props\n },\n ref,\n ) => {\n const shouldShowBack = showBack ?? !!onBack;\n\n return (\n <div\n ref={ref}\n className={cn(\"flex h-16 shrink-0 items-center gap-2 px-6 py-4\", className)}\n {...props}\n >\n {shouldShowBack && (\n <IconButton\n variant=\"tertiary\"\n size=\"32\"\n icon={<ArrowLeftIcon />}\n onClick={onBack}\n disabled={!onBack}\n aria-label={backLabel}\n />\n )}\n <div className=\"min-w-0 flex-1\">{children}</div>\n {showClose && (\n <DialogPrimitive.Close asChild>\n <IconButton variant=\"tertiary\" size=\"32\" icon={<CloseIcon />} aria-label={closeLabel} />\n </DialogPrimitive.Close>\n )}\n </div>\n );\n },\n);\nDialogHeader.displayName = \"DialogHeader\";\n\n/** Props for the {@link DialogTitle} component. */\nexport type DialogTitleProps = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>;\n\n/**\n * Accessible title for the dialog. Must be rendered inside {@link DialogHeader}\n * or directly within {@link DialogContent}.\n */\nexport const DialogTitle = React.forwardRef<\n React.ComponentRef<typeof DialogPrimitive.Title>,\n DialogTitleProps\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\"typography-bold-heading-xs truncate text-content-primary\", className)}\n {...props}\n />\n));\nDialogTitle.displayName = \"DialogTitle\";\n\n/** Props for the {@link DialogDescription} component. */\nexport type DialogDescriptionProps = React.ComponentPropsWithoutRef<\n typeof DialogPrimitive.Description\n>;\n\n/** Accessible description for the dialog. Rendered as secondary text. */\nexport const DialogDescription = React.forwardRef<\n React.ComponentRef<typeof DialogPrimitive.Description>,\n DialogDescriptionProps\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn(\"typography-regular-body-lg text-content-secondary\", className)}\n {...props}\n />\n));\nDialogDescription.displayName = \"DialogDescription\";\n\nexport interface DialogBodyProps extends React.HTMLAttributes<HTMLDivElement> {}\n\n/**\n * Scrollable content area (slot) between the header and footer.\n * Grows to fill available space and scrolls when content overflows.\n */\nexport const DialogBody = React.forwardRef<HTMLDivElement, DialogBodyProps>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={cn(\"flex-1 overflow-y-auto px-6 py-4\", className)} {...props} />\n ),\n);\nDialogBody.displayName = \"DialogBody\";\n\nexport interface DialogFooterProps extends React.HTMLAttributes<HTMLDivElement> {}\n\n/**\n * Footer bar for the dialog. Typically contains action buttons.\n * Children are laid out in a horizontal row with equal flex-basis.\n */\nexport const DialogFooter = React.forwardRef<HTMLDivElement, DialogFooterProps>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n \"flex shrink-0 items-center gap-3 px-6 pt-3 pb-6\",\n \"[&>*]:min-w-0 [&>*]:flex-1\",\n className,\n )}\n {...props}\n />\n ),\n);\nDialogFooter.displayName = \"DialogFooter\";\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Dialog.mjs","sources":["../../../src/components/Dialog/Dialog.tsx"],"sourcesContent":["import * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { useSuppressClickAfterDrag } from \"../../utils/useSuppressClickAfterDrag\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { ArrowLeftIcon } from \"../Icons/ArrowLeftIcon\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\n\n/** Props for the {@link Dialog} root component. */\nexport interface DialogProps extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root> {\n /** Controlled open state. When provided, you must also supply `onOpenChange`. */\n open?: boolean;\n /** Called when the open state changes. Required when `open` is controlled. */\n onOpenChange?: (open: boolean) => void;\n /** The open state of the dialog when it is initially rendered (uncontrolled). */\n defaultOpen?: boolean;\n}\n\n/** Root component that manages open/close state for a dialog. */\nexport const Dialog = DialogPrimitive.Root;\n\n/** Props for the {@link DialogTrigger} component. */\nexport type DialogTriggerProps = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Trigger>;\n\n/**\n * The element that opens the dialog when clicked.\n *\n * On touch / pen, a press-and-release that crosses a small movement threshold\n * is treated as a drag and the resulting synthetic click is suppressed —\n * defends against Android Chrome opening the dialog on a scroll-drag-end.\n */\nexport const DialogTrigger = React.forwardRef<\n React.ComponentRef<typeof DialogPrimitive.Trigger>,\n DialogTriggerProps\n>((props, ref) => <DialogPrimitive.Trigger ref={ref} {...useSuppressClickAfterDrag(props)} />);\nDialogTrigger.displayName = \"DialogTrigger\";\n\n/** Convenience alias for Radix `Dialog.Close`. Closes the dialog when clicked. */\nexport const DialogClose = DialogPrimitive.Close;\n\n/** Props for the {@link DialogClose} component. */\nexport type DialogCloseProps = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Close>;\n\nexport interface DialogOverlayProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> {}\n\n/**\n * Semi-transparent backdrop rendered behind the dialog content.\n * Rendered inside a portal automatically by {@link DialogContent}.\n */\nexport const DialogOverlay = React.forwardRef<\n React.ComponentRef<typeof DialogPrimitive.Overlay>,\n DialogOverlayProps\n>(({ className, style, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n \"data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 fixed inset-0 bg-bg-overlay data-[state=closed]:animate-out data-[state=open]:animate-in\",\n className,\n )}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...style }}\n {...props}\n />\n));\nDialogOverlay.displayName = \"DialogOverlay\";\n\nexport interface DialogContentProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {\n /**\n * Width preset for the dialog.\n * - `\"sm\"` — 400px max-width (confirmations, simple forms)\n * - `\"md\"` — 440px max-width (default, standard dialogs)\n * - `\"lg\"` — 600px max-width (complex content, tables)\n *\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /** When true, renders overlay automatically. @default true */\n overlay?: boolean;\n}\n\nconst SIZE_CLASSES: Record<NonNullable<DialogContentProps[\"size\"]>, string> = {\n sm: \"sm:max-w-[400px]\",\n md: \"sm:max-w-[440px]\",\n lg: \"sm:max-w-[600px]\",\n};\n\n/**\n * The dialog panel rendered inside a portal. Includes the overlay by default.\n *\n * On mobile viewports (<640px), the dialog slides up from the bottom as a sheet\n * with top-only border radius. On larger viewports it renders centered with\n * full border radius.\n *\n * @example\n * ```tsx\n * <Dialog>\n * <DialogTrigger asChild>\n * <Button>Open</Button>\n * </DialogTrigger>\n * <DialogContent>\n * <DialogHeader>\n * <DialogTitle>Title</DialogTitle>\n * </DialogHeader>\n * <DialogBody>Content here</DialogBody>\n * <DialogFooter>\n * <DialogClose asChild>\n * <Button variant=\"secondary\">Cancel</Button>\n * </DialogClose>\n * <Button>Accept</Button>\n * </DialogFooter>\n * </DialogContent>\n * </Dialog>\n * ```\n */\nexport const DialogContent = React.forwardRef<\n React.ComponentRef<typeof DialogPrimitive.Content>,\n DialogContentProps\n>(({ className, children, size = \"md\", overlay = true, style, onOpenAutoFocus, ...props }, ref) => (\n <DialogPrimitive.Portal>\n {overlay && <DialogOverlay />}\n <DialogPrimitive.Content\n ref={ref}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...style }}\n onOpenAutoFocus={(e) => {\n if (onOpenAutoFocus) {\n onOpenAutoFocus(e);\n return;\n }\n e.preventDefault();\n (e.currentTarget as HTMLElement).focus();\n }}\n className={cn(\n // Base\n \"fixed flex flex-col overflow-hidden bg-bg-primary shadow-lg focus:outline-none dark:bg-surface-primary\",\n // Mobile: bottom sheet\n \"inset-x-0 bottom-0 max-h-[85vh] w-full rounded-t-lg\",\n // Animation (shared)\n \"data-[state=open]:fade-in-0 data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=closed]:animate-out\",\n // Mobile: slide up from bottom\n \"data-[state=open]:slide-in-from-bottom-full\",\n \"data-[state=closed]:slide-out-to-bottom-full\",\n // Desktop: centered dialog\n \"sm:inset-auto sm:top-1/2 sm:left-1/2 sm:max-h-[85vh] sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-lg\",\n // Desktop: scale in/out (cancel mobile slide)\n \"sm:data-[state=open]:slide-in-from-bottom-0 sm:data-[state=open]:zoom-in-95\",\n \"sm:data-[state=closed]:slide-out-to-bottom-0 sm:data-[state=closed]:zoom-out-95\",\n // Duration\n \"duration-200\",\n // Size\n SIZE_CLASSES[size],\n className,\n )}\n {...props}\n >\n {children}\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n));\nDialogContent.displayName = \"DialogContent\";\n\nexport interface DialogHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Show the close (X) button in the header. @default true */\n showClose?: boolean;\n /** Show a back arrow button on the left side. Defaults to `true` when `onBack` is provided. */\n showBack?: boolean;\n /** Called when the back button is clicked. */\n onBack?: () => void;\n /** Accessible label for the back button. @default \"Go back\" */\n backLabel?: string;\n /** Accessible label for the close button. @default \"Close\" */\n closeLabel?: string;\n}\n\n/**\n * Header bar for the dialog. Renders the title with an optional back arrow\n * and close button.\n *\n * @example\n * ```tsx\n * <DialogHeader>\n * <DialogTitle>Settings</DialogTitle>\n * </DialogHeader>\n *\n * <DialogHeader showBack onBack={() => setStep(0)}>\n * <DialogTitle>Step 2</DialogTitle>\n * </DialogHeader>\n * ```\n */\nexport const DialogHeader = React.forwardRef<HTMLDivElement, DialogHeaderProps>(\n (\n {\n className,\n children,\n showClose = true,\n showBack,\n onBack,\n backLabel = \"Go back\",\n closeLabel = \"Close\",\n ...props\n },\n ref,\n ) => {\n const shouldShowBack = showBack ?? !!onBack;\n\n return (\n <div\n ref={ref}\n className={cn(\"flex h-16 shrink-0 items-center gap-2 px-6 py-4\", className)}\n {...props}\n >\n {shouldShowBack && (\n <IconButton\n variant=\"tertiary\"\n size=\"32\"\n icon={<ArrowLeftIcon />}\n onClick={onBack}\n disabled={!onBack}\n aria-label={backLabel}\n />\n )}\n <div className=\"min-w-0 flex-1\">{children}</div>\n {showClose && (\n <DialogPrimitive.Close asChild>\n <IconButton variant=\"tertiary\" size=\"32\" icon={<CloseIcon />} aria-label={closeLabel} />\n </DialogPrimitive.Close>\n )}\n </div>\n );\n },\n);\nDialogHeader.displayName = \"DialogHeader\";\n\n/** Props for the {@link DialogTitle} component. */\nexport type DialogTitleProps = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>;\n\n/**\n * Accessible title for the dialog. Must be rendered inside {@link DialogHeader}\n * or directly within {@link DialogContent}.\n */\nexport const DialogTitle = React.forwardRef<\n React.ComponentRef<typeof DialogPrimitive.Title>,\n DialogTitleProps\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\"typography-bold-heading-xs truncate text-content-primary\", className)}\n {...props}\n />\n));\nDialogTitle.displayName = \"DialogTitle\";\n\n/** Props for the {@link DialogDescription} component. */\nexport type DialogDescriptionProps = React.ComponentPropsWithoutRef<\n typeof DialogPrimitive.Description\n>;\n\n/** Accessible description for the dialog. Rendered as secondary text. */\nexport const DialogDescription = React.forwardRef<\n React.ComponentRef<typeof DialogPrimitive.Description>,\n DialogDescriptionProps\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn(\"typography-regular-body-lg text-content-secondary\", className)}\n {...props}\n />\n));\nDialogDescription.displayName = \"DialogDescription\";\n\nexport interface DialogBodyProps extends React.HTMLAttributes<HTMLDivElement> {}\n\n/**\n * Scrollable content area (slot) between the header and footer.\n * Grows to fill available space and scrolls when content overflows.\n */\nexport const DialogBody = React.forwardRef<HTMLDivElement, DialogBodyProps>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={cn(\"flex-1 overflow-y-auto px-6 py-4\", className)} {...props} />\n ),\n);\nDialogBody.displayName = \"DialogBody\";\n\nexport interface DialogFooterProps extends React.HTMLAttributes<HTMLDivElement> {}\n\n/**\n * Footer bar for the dialog. Typically contains action buttons.\n * Children are laid out in a horizontal row with equal flex-basis.\n */\nexport const DialogFooter = React.forwardRef<HTMLDivElement, DialogFooterProps>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n \"flex shrink-0 items-center gap-3 px-6 pt-3 pb-6\",\n \"[&>*]:min-w-0 [&>*]:flex-1\",\n className,\n )}\n {...props}\n />\n ),\n);\nDialogFooter.displayName = \"DialogFooter\";\n"],"names":[],"mappings":"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"}
|
|
@@ -3,15 +3,18 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { cn } from "../../utils/cn.mjs";
|
|
6
|
+
import { useSuppressClickAfterDrag } from "../../utils/useSuppressClickAfterDrag.mjs";
|
|
6
7
|
import { IconButton } from "../IconButton/IconButton.mjs";
|
|
7
8
|
import { CloseIcon } from "../Icons/CloseIcon.mjs";
|
|
8
|
-
const DrawerContext = React.createContext({
|
|
9
|
+
const DrawerContext = React.createContext({
|
|
10
|
+
overlay: true
|
|
11
|
+
});
|
|
9
12
|
function Drawer({ overlay = true, modal, children, ...props }) {
|
|
10
13
|
const resolvedModal = modal ?? (overlay ? void 0 : false);
|
|
11
14
|
return /* @__PURE__ */ jsx(DrawerContext.Provider, { value: { overlay }, children: /* @__PURE__ */ jsx(DialogPrimitive.Root, { modal: resolvedModal, ...props, children }) });
|
|
12
15
|
}
|
|
13
16
|
Drawer.displayName = "Drawer";
|
|
14
|
-
const DrawerTrigger = DialogPrimitive.Trigger;
|
|
17
|
+
const DrawerTrigger = React.forwardRef((props, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Trigger, { ref, ...useSuppressClickAfterDrag(props) }));
|
|
15
18
|
DrawerTrigger.displayName = "DrawerTrigger";
|
|
16
19
|
const DrawerClose = DialogPrimitive.Close;
|
|
17
20
|
DrawerClose.displayName = "DrawerClose";
|
|
@@ -51,7 +54,12 @@ const DrawerContent = React.forwardRef(
|
|
|
51
54
|
const ctx = React.useContext(DrawerContext);
|
|
52
55
|
const overlay = overlayProp ?? ctx.overlay;
|
|
53
56
|
const isHorizontal = position === "left" || position === "right";
|
|
54
|
-
const sizeClass = isHorizontal ? {
|
|
57
|
+
const sizeClass = isHorizontal ? {
|
|
58
|
+
sm: "max-w-sm",
|
|
59
|
+
md: "max-w-md",
|
|
60
|
+
lg: "max-w-lg",
|
|
61
|
+
full: "max-w-full"
|
|
62
|
+
}[size] : {
|
|
55
63
|
sm: "max-h-[24rem]",
|
|
56
64
|
md: "max-h-[28rem]",
|
|
57
65
|
lg: "max-h-[32rem]",
|
|
@@ -63,7 +71,10 @@ const DrawerContent = React.forwardRef(
|
|
|
63
71
|
DialogPrimitive.Content,
|
|
64
72
|
{
|
|
65
73
|
ref,
|
|
66
|
-
style: {
|
|
74
|
+
style: {
|
|
75
|
+
zIndex: "calc(var(--fanvue-ui-portal-z-index, 50) + 1)",
|
|
76
|
+
...style
|
|
77
|
+
},
|
|
67
78
|
className: cn(
|
|
68
79
|
"fixed flex flex-col bg-surface-secondary shadow-lg outline-none backdrop-blur-lg",
|
|
69
80
|
"data-[state=closed]:animate-out data-[state=open]:animate-in",
|