@rovula/ui 0.1.21 → 0.1.22
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/bundle.css +175 -26
- package/dist/cjs/bundle.js +675 -675
- package/dist/cjs/bundle.js.map +1 -1
- package/dist/cjs/types/components/Badge/Badge.d.ts +40 -0
- package/dist/cjs/types/components/Badge/Badge.stories.d.ts +295 -0
- package/dist/cjs/types/components/Badge/Badge.styles.d.ts +7 -0
- package/dist/cjs/types/components/Badge/index.d.ts +2 -0
- package/dist/cjs/types/components/Dropdown/Dropdown.d.ts +4 -8
- package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +1 -6
- package/dist/cjs/types/components/DropdownMenu/DropdownMenu.d.ts +5 -1
- package/dist/cjs/types/components/DropdownMenu/DropdownMenu.stories.d.ts +16 -0
- package/dist/cjs/types/index.d.ts +3 -1
- package/dist/cjs/types/patterns/menu/Menu.d.ts +70 -0
- package/dist/cjs/types/{components/Menu → patterns/menu}/Menu.stories.d.ts +17 -10
- package/dist/cjs/types/utils/mergeRefs.d.ts +20 -0
- package/dist/components/Avatar/Avatar.styles.js +2 -2
- package/dist/components/Badge/Badge.js +36 -0
- package/dist/components/Badge/Badge.stories.js +51 -0
- package/dist/components/Badge/Badge.styles.js +62 -0
- package/dist/components/Badge/index.js +2 -0
- package/dist/components/Dropdown/Dropdown.js +54 -163
- package/dist/components/Dropdown/Dropdown.stories.js +29 -0
- package/dist/components/DropdownMenu/DropdownMenu.js +22 -9
- package/dist/components/DropdownMenu/DropdownMenu.stories.js +54 -10
- package/dist/components/TextInput/TextInput.js +6 -3
- package/dist/esm/bundle.css +175 -26
- package/dist/esm/bundle.js +1545 -1545
- package/dist/esm/bundle.js.map +1 -1
- package/dist/esm/types/components/Badge/Badge.d.ts +40 -0
- package/dist/esm/types/components/Badge/Badge.stories.d.ts +295 -0
- package/dist/esm/types/components/Badge/Badge.styles.d.ts +7 -0
- package/dist/esm/types/components/Badge/index.d.ts +2 -0
- package/dist/esm/types/components/Dropdown/Dropdown.d.ts +4 -8
- package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +1 -6
- package/dist/esm/types/components/DropdownMenu/DropdownMenu.d.ts +5 -1
- package/dist/esm/types/components/DropdownMenu/DropdownMenu.stories.d.ts +16 -0
- package/dist/esm/types/index.d.ts +3 -1
- package/dist/esm/types/patterns/menu/Menu.d.ts +70 -0
- package/dist/esm/types/{components/Menu → patterns/menu}/Menu.stories.d.ts +17 -10
- package/dist/esm/types/utils/mergeRefs.d.ts +20 -0
- package/dist/index.d.ts +116 -73
- package/dist/index.js +2 -1
- package/dist/patterns/menu/Menu.js +95 -0
- package/dist/patterns/menu/Menu.stories.js +611 -0
- package/dist/src/theme/global.css +289 -37
- package/dist/utils/mergeRefs.js +42 -0
- package/package.json +1 -1
- package/src/components/Avatar/Avatar.styles.ts +2 -2
- package/src/components/Badge/Badge.stories.tsx +128 -0
- package/src/components/Badge/Badge.styles.ts +70 -0
- package/src/components/Badge/Badge.tsx +103 -0
- package/src/components/Badge/index.ts +3 -0
- package/src/components/Dropdown/Dropdown.stories.tsx +170 -1
- package/src/components/Dropdown/Dropdown.tsx +186 -276
- package/src/components/DropdownMenu/DropdownMenu.stories.tsx +1050 -113
- package/src/components/DropdownMenu/DropdownMenu.tsx +116 -52
- package/src/components/TextInput/TextInput.tsx +6 -3
- package/src/index.ts +3 -1
- package/src/patterns/menu/Menu.stories.tsx +1100 -0
- package/src/patterns/menu/Menu.tsx +282 -0
- package/src/theme/themes/xspector/baseline.css +0 -1
- package/src/theme/tokens/baseline.css +2 -1
- package/src/theme/tokens/components/badge.css +54 -0
- package/src/theme/tokens/components/dropdown-menu.css +15 -4
- package/src/utils/mergeRefs.ts +46 -0
- package/dist/cjs/types/components/Menu/Menu.d.ts +0 -65
- package/dist/cjs/types/components/Menu/helpers.d.ts +0 -19
- package/dist/cjs/types/components/Menu/index.d.ts +0 -4
- package/dist/components/Menu/Menu.js +0 -64
- package/dist/components/Menu/Menu.stories.js +0 -406
- package/dist/components/Menu/helpers.js +0 -28
- package/dist/components/Menu/index.js +0 -3
- package/dist/esm/types/components/Menu/Menu.d.ts +0 -65
- package/dist/esm/types/components/Menu/helpers.d.ts +0 -19
- package/dist/esm/types/components/Menu/index.d.ts +0 -4
- package/src/components/Menu/Menu.stories.tsx +0 -586
- package/src/components/Menu/Menu.tsx +0 -235
- package/src/components/Menu/helpers.ts +0 -45
- package/src/components/Menu/index.ts +0 -7
- package/src/theme/themes/xspector/components/dropdown-menu.css +0 -28
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Merges multiple refs into a single callback ref.
|
|
4
|
+
* NOTE: This creates a new function on every call — do NOT use inline in render.
|
|
5
|
+
* Use `useStableMergedRef` instead when you need a stable ref identity.
|
|
6
|
+
*/
|
|
7
|
+
export declare function mergeRefs<T>(...refs: (React.Ref<T> | undefined | null)[]): React.RefCallback<T>;
|
|
8
|
+
/**
|
|
9
|
+
* Returns a **stable** callback ref (never changes identity) that forwards the
|
|
10
|
+
* node to all given refs. Safe to use inline in JSX — will not cause
|
|
11
|
+
* detach/re-attach loops in libraries like Headless UI that watch refs.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const MyInput = forwardRef((props, ref) => {
|
|
15
|
+
* const internalRef = useRef(null);
|
|
16
|
+
* const stableRef = useStableMergedRef(ref, internalRef);
|
|
17
|
+
* return <input ref={stableRef} />;
|
|
18
|
+
* });
|
|
19
|
+
*/
|
|
20
|
+
export declare function useStableMergedRef<T>(...refs: (React.Ref<T> | undefined | null)[]): React.RefCallback<T>;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { cva } from "class-variance-authority";
|
|
2
2
|
export const avatarVariants = cva([
|
|
3
|
-
"flex items-center justify-center bg-grey2-
|
|
3
|
+
"flex items-center justify-center bg-grey2-900 text-text-white typography-subtitle6 truncate",
|
|
4
4
|
], {
|
|
5
5
|
variants: {
|
|
6
6
|
size: {
|
|
7
|
-
xxs: "w-[24px] h-[24px] typography-
|
|
7
|
+
xxs: "w-[24px] h-[24px] typography-small3",
|
|
8
8
|
xs: "w-[32px] h-[32px]",
|
|
9
9
|
sm: "w-[40px] h-[40px]",
|
|
10
10
|
md: "w-[48px] h-[48px]",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
+
import * as React from "react";
|
|
15
|
+
import { ChevronDownIcon } from "@heroicons/react/16/solid";
|
|
16
|
+
import { cn } from "@/utils/cn";
|
|
17
|
+
import { badgeVariants, severityBadgeVariants } from "./Badge.styles";
|
|
18
|
+
const Badge = React.forwardRef((_a, ref) => {
|
|
19
|
+
var { label, color = "default", clickable = false, percent, className } = _a, props = __rest(_a, ["label", "color", "clickable", "percent", "className"]);
|
|
20
|
+
const hasPercent = percent !== undefined;
|
|
21
|
+
return (_jsxs("span", Object.assign({ ref: ref, className: cn(badgeVariants({ color, clickable }), hasPercent && "flex-col gap-0.5", clickable && "cursor-pointer gap-1", className) }, props, { children: [_jsxs("span", { className: "flex items-center gap-1", children: [label, clickable && (_jsx(ChevronDownIcon, { className: "size-4 shrink-0", "aria-hidden": true }))] }), hasPercent && (_jsxs("span", { className: "tabular-nums", children: [percent, "%"] }))] })));
|
|
22
|
+
});
|
|
23
|
+
Badge.displayName = "Badge";
|
|
24
|
+
const SEVERITY_LABELS = {
|
|
25
|
+
highest: "Highest",
|
|
26
|
+
high: "High",
|
|
27
|
+
medium: "Medium",
|
|
28
|
+
low: "Low",
|
|
29
|
+
lowest: "Lowest",
|
|
30
|
+
};
|
|
31
|
+
const SeverityBadge = React.forwardRef((_a, ref) => {
|
|
32
|
+
var { severity, className } = _a, props = __rest(_a, ["severity", "className"]);
|
|
33
|
+
return (_jsx("span", Object.assign({ ref: ref, className: cn(severityBadgeVariants({ severity }), className) }, props, { children: SEVERITY_LABELS[severity] })));
|
|
34
|
+
});
|
|
35
|
+
SeverityBadge.displayName = "SeverityBadge";
|
|
36
|
+
export { Badge, SeverityBadge };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Badge, SeverityBadge } from "./Badge";
|
|
3
|
+
const meta = {
|
|
4
|
+
title: "Components/Badge",
|
|
5
|
+
component: Badge,
|
|
6
|
+
tags: ["autodocs"],
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: "fullscreen",
|
|
9
|
+
},
|
|
10
|
+
decorators: [
|
|
11
|
+
(Story) => (_jsx("div", { className: "p-8 bg-bg-bg1", children: _jsx(Story, {}) })),
|
|
12
|
+
],
|
|
13
|
+
};
|
|
14
|
+
export default meta;
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Default — single badge with controls
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
export const Default = {
|
|
19
|
+
args: {
|
|
20
|
+
label: "To do",
|
|
21
|
+
color: "default",
|
|
22
|
+
clickable: false,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Status Badges — all colors × clickable / static
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
const COLORS = ["default", "warning", "info", "error", "success"];
|
|
29
|
+
const COLOR_LABELS = {
|
|
30
|
+
default: "To do",
|
|
31
|
+
warning: "In Progress",
|
|
32
|
+
info: "Ready to review",
|
|
33
|
+
error: "In review",
|
|
34
|
+
success: "Completed",
|
|
35
|
+
};
|
|
36
|
+
export const StatusBadges = {
|
|
37
|
+
render: () => (_jsxs("div", { className: "flex flex-col gap-6", children: [_jsxs("div", { children: [_jsx("p", { className: "typography-small1 text-text-g-contrast-low mb-3", children: "Clickable" }), _jsx("div", { className: "flex flex-wrap gap-3", children: COLORS.map((color) => (_jsx(Badge, { color: color, label: COLOR_LABELS[color], clickable: true }, color))) })] }), _jsxs("div", { children: [_jsx("p", { className: "typography-small1 text-text-g-contrast-low mb-3", children: "Static" }), _jsx("div", { className: "flex flex-wrap gap-3", children: COLORS.map((color) => (_jsx(Badge, { color: color, label: COLOR_LABELS[color] }, color))) })] }), _jsxs("div", { children: [_jsx("p", { className: "typography-small1 text-text-g-contrast-low mb-3", children: "With percentage" }), _jsx("div", { className: "flex flex-wrap gap-3", children: COLORS.map((color, i) => (_jsx(Badge, { color: color, label: COLOR_LABELS[color], percent: i === 0 ? 0 : i === 4 ? 100 : 50 }, color))) })] })] })),
|
|
38
|
+
};
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Severity Badges
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
const SEVERITIES = [
|
|
43
|
+
"highest",
|
|
44
|
+
"high",
|
|
45
|
+
"medium",
|
|
46
|
+
"low",
|
|
47
|
+
"lowest",
|
|
48
|
+
];
|
|
49
|
+
export const SeverityBadges = {
|
|
50
|
+
render: () => (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("p", { className: "typography-small1 text-text-g-contrast-low mb-1", children: "Severity levels" }), _jsx("div", { className: "flex flex-wrap gap-3", children: SEVERITIES.map((severity) => (_jsx(SeverityBadge, { severity: severity }, severity))) })] })),
|
|
51
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { cva } from "class-variance-authority";
|
|
2
|
+
export const badgeVariants = cva([
|
|
3
|
+
"inline-flex items-center justify-center rounded-lg px-3 py-1",
|
|
4
|
+
"typography-body3",
|
|
5
|
+
], {
|
|
6
|
+
variants: {
|
|
7
|
+
color: {
|
|
8
|
+
default: [
|
|
9
|
+
"bg-[var(--badge-default-bg)]",
|
|
10
|
+
"text-[var(--badge-default-text)]",
|
|
11
|
+
],
|
|
12
|
+
success: [
|
|
13
|
+
"bg-[var(--badge-success-bg)]",
|
|
14
|
+
"text-[var(--badge-success-text)]",
|
|
15
|
+
],
|
|
16
|
+
warning: [
|
|
17
|
+
"bg-[var(--badge-warning-bg)]",
|
|
18
|
+
"text-[var(--badge-warning-text)]",
|
|
19
|
+
],
|
|
20
|
+
info: [
|
|
21
|
+
"bg-[var(--badge-info-bg)]",
|
|
22
|
+
"text-[var(--badge-info-text)]",
|
|
23
|
+
],
|
|
24
|
+
error: [
|
|
25
|
+
"bg-[var(--badge-error-bg)]",
|
|
26
|
+
"text-[var(--badge-error-text)]",
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
clickable: {
|
|
30
|
+
true: "border border-solid",
|
|
31
|
+
false: "",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
compoundVariants: [
|
|
35
|
+
{ color: "default", clickable: true, className: "border-[var(--badge-default-border)]" },
|
|
36
|
+
{ color: "success", clickable: true, className: "border-[var(--badge-success-border)]" },
|
|
37
|
+
{ color: "warning", clickable: true, className: "border-[var(--badge-warning-border)]" },
|
|
38
|
+
{ color: "info", clickable: true, className: "border-[var(--badge-info-border)]" },
|
|
39
|
+
{ color: "error", clickable: true, className: "border-[var(--badge-error-border)]" },
|
|
40
|
+
],
|
|
41
|
+
defaultVariants: {
|
|
42
|
+
color: "default",
|
|
43
|
+
clickable: false,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
export const severityBadgeVariants = cva([
|
|
47
|
+
"inline-flex items-center justify-center rounded px-1 py-0.5",
|
|
48
|
+
"typography-small6 text-[var(--badge-severity-text)]",
|
|
49
|
+
], {
|
|
50
|
+
variants: {
|
|
51
|
+
severity: {
|
|
52
|
+
highest: "bg-[var(--badge-severity-highest-bg)]",
|
|
53
|
+
high: "bg-[var(--badge-severity-high-bg)]",
|
|
54
|
+
medium: "bg-[var(--badge-severity-medium-bg)]",
|
|
55
|
+
low: "bg-[var(--badge-severity-low-bg)]",
|
|
56
|
+
lowest: "bg-[var(--badge-severity-lowest-bg)]",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
defaultVariants: {
|
|
60
|
+
severity: "medium",
|
|
61
|
+
},
|
|
62
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
3
|
var t = {};
|
|
3
4
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -9,190 +10,80 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
10
|
}
|
|
10
11
|
return t;
|
|
11
12
|
};
|
|
12
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
14
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, Fragment, } from "react";
|
|
14
|
-
import
|
|
15
|
+
import { Combobox, ComboboxInput, ComboboxOptions, ComboboxOption, } from "@headlessui/react";
|
|
15
16
|
import TextInput from "../TextInput/TextInput";
|
|
16
17
|
import { customInputVariant, dropdownIconVariant } from "./Dropdown.styles";
|
|
17
|
-
import { Menu } from "../Menu/Menu";
|
|
18
|
-
import { ChevronDownIcon } from "@heroicons/react/16/solid";
|
|
19
18
|
import { cn } from "@/utils/cn";
|
|
19
|
+
import Icon from "../Icon/Icon";
|
|
20
|
+
import { ChevronDownIcon } from "@heroicons/react/16/solid";
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Shared menu item styles (used by Dropdown items + renderOptions consumers)
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
export const menuItemBaseStyles = cn("relative flex gap-1 cursor-pointer select-none box-border items-center py-4 pl-4 pr-8 typography-subtitle4 outline-none transition-colors", "bg-[var(--dropdown-menu-default-bg)] text-[var(--dropdown-menu-default-text)]", "active:opacity-75", "hover:bg-[var(--dropdown-menu-hover-bg)] hover:text-[var(--dropdown-menu-hover-text)]");
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Dropdown
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
20
28
|
const Dropdown = forwardRef((_a, ref) => {
|
|
21
|
-
var { id, options = [], value, label, size = "md", rounded = "normal", variant = "outline",
|
|
29
|
+
var { id, options = [], value, label, size = "md", rounded = "normal", variant = "outline", helperText, errorMessage, fullwidth = true, disabled = false, error = false, filterMode = false, required = true, modal: _modal, onChangeText, onSelect, renderOptions: customRenderOptions, optionContainerClassName, optionItemClassName, optionNotFoundItemClassName, segmentedInput = true } = _a, props = __rest(_a, ["id", "options", "value", "label", "size", "rounded", "variant", "helperText", "errorMessage", "fullwidth", "disabled", "error", "filterMode", "required", "modal", "onChangeText", "onSelect", "renderOptions", "optionContainerClassName", "optionItemClassName", "optionNotFoundItemClassName", "segmentedInput"]);
|
|
22
30
|
const _id = id || `${label}-select`;
|
|
23
|
-
const [isFocused, setIsFocused] = useState(false);
|
|
24
31
|
const [selectedOption, setSelectedOption] = useState(null);
|
|
25
|
-
const [
|
|
26
|
-
const keyCode = useRef("");
|
|
27
|
-
const dropdownRef = useRef(null);
|
|
32
|
+
const [query, setQuery] = useState("");
|
|
28
33
|
const inputRef = useRef(null);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
useImperativeHandle(ref, () => inputRef === null || inputRef === void 0 ? void 0 : inputRef.current);
|
|
34
|
+
// Expose the inner <input> element via forwardRef
|
|
35
|
+
useImperativeHandle(ref, () => inputRef.current, []);
|
|
36
|
+
// Sync external value prop
|
|
33
37
|
useEffect(() => {
|
|
34
|
-
|
|
35
|
-
setSelectedOption(value);
|
|
36
|
-
setTextValue((_a = value === null || value === void 0 ? void 0 : value.label) !== null && _a !== void 0 ? _a : "");
|
|
38
|
+
setSelectedOption(value !== null && value !== void 0 ? value : null);
|
|
37
39
|
}, [value]);
|
|
38
|
-
/** ✅ Auto-detect if inside a Dialog */
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
let node = inputRef.current;
|
|
41
|
-
while (node) {
|
|
42
|
-
if (node.getAttribute("role") === "dialog") {
|
|
43
|
-
setIsInsideDialog(true);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
node = node.parentElement;
|
|
47
|
-
}
|
|
48
|
-
setIsInsideDialog(false);
|
|
49
|
-
}, []);
|
|
50
|
-
const handleOnChangeText = useCallback((event) => {
|
|
51
|
-
onChangeText === null || onChangeText === void 0 ? void 0 : onChangeText(event);
|
|
52
|
-
setTextValue(event.target.value);
|
|
53
|
-
if (!event.target.value) {
|
|
54
|
-
clearMismatchValue(event);
|
|
55
|
-
}
|
|
56
|
-
}, [onChangeText]);
|
|
57
|
-
const handleOptionClick = useCallback((option) => {
|
|
58
|
-
setSelectedOption(option);
|
|
59
|
-
setTextValue(option.label);
|
|
60
|
-
onSelect === null || onSelect === void 0 ? void 0 : onSelect(option);
|
|
61
|
-
setIsFocused(false);
|
|
62
|
-
}, [onSelect]);
|
|
63
40
|
const optionsFiltered = useMemo(() => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const spaceAbove = rect.top;
|
|
77
|
-
const shouldOpenAbove = spaceBelow < dropdownHeight && spaceAbove > spaceBelow;
|
|
78
|
-
setIsAbove(shouldOpenAbove);
|
|
79
|
-
if (usePortal) {
|
|
80
|
-
setDropdownStyles({
|
|
81
|
-
position: "absolute",
|
|
82
|
-
top: shouldOpenAbove
|
|
83
|
-
? `${rect.top - dropdownHeight}px`
|
|
84
|
-
: `${rect.bottom}px`,
|
|
85
|
-
left: `${rect.left}px`,
|
|
86
|
-
width: `${rect.width}px`,
|
|
87
|
-
zIndex: 9999,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
setDropdownStyles({
|
|
92
|
-
position: "absolute",
|
|
93
|
-
top: shouldOpenAbove ? `-${dropdownHeight}px` : "100%",
|
|
94
|
-
left: "0",
|
|
95
|
-
width: "100%",
|
|
96
|
-
zIndex: 9999,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}, [modal, isInsideDialog, usePortal]);
|
|
101
|
-
useEffect(() => {
|
|
102
|
-
if (isFocused) {
|
|
103
|
-
updateDropdownPosition();
|
|
104
|
-
window.addEventListener("resize", updateDropdownPosition);
|
|
41
|
+
if (!filterMode || !query)
|
|
42
|
+
return options;
|
|
43
|
+
return options.filter((opt) => opt.label.toLowerCase().includes(query.toLowerCase()));
|
|
44
|
+
}, [options, filterMode, query]);
|
|
45
|
+
const handleSelect = useCallback((option) => {
|
|
46
|
+
setSelectedOption(option);
|
|
47
|
+
setQuery("");
|
|
48
|
+
if (option) {
|
|
49
|
+
onSelect === null || onSelect === void 0 ? void 0 : onSelect(option);
|
|
50
|
+
// After selection Headless UI keeps focus on the input (via rAF refocus).
|
|
51
|
+
// Blur after that rAF so the next click triggers onFocus → immediate open.
|
|
52
|
+
setTimeout(() => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blur(); }, 0);
|
|
105
53
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
54
|
+
}, [onSelect]);
|
|
55
|
+
const handleInputChange = useCallback((e) => {
|
|
56
|
+
if (filterMode)
|
|
57
|
+
setQuery(e.target.value);
|
|
58
|
+
onChangeText === null || onChangeText === void 0 ? void 0 : onChangeText(e);
|
|
59
|
+
}, [filterMode, onChangeText]);
|
|
60
|
+
const renderOptionList = () => {
|
|
111
61
|
if (customRenderOptions) {
|
|
112
62
|
return customRenderOptions({
|
|
113
63
|
optionsFiltered,
|
|
114
64
|
selectedOption,
|
|
115
|
-
onClick:
|
|
116
|
-
style: dropdownStyles,
|
|
117
|
-
dropdownRef,
|
|
65
|
+
onClick: handleSelect,
|
|
118
66
|
});
|
|
119
67
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
68
|
+
if (optionsFiltered.length === 0) {
|
|
69
|
+
return (_jsx("div", { className: cn("px-4 py-14 text-center text-input-text", optionNotFoundItemClassName), children: "Not found" }));
|
|
70
|
+
}
|
|
71
|
+
return optionsFiltered.map((option) => {
|
|
123
72
|
if (option.renderLabel) {
|
|
124
|
-
return {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
"bg-[var(--dropdown-menu-selected-bg)] text-[var(--dropdown-menu-selected-text)] typography-subtitle5": (selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.value) === option.value,
|
|
132
|
-
}, optionItemClassName),
|
|
133
|
-
}) }, option.value)),
|
|
134
|
-
};
|
|
73
|
+
return (_jsx(Fragment, { children: option.renderLabel({
|
|
74
|
+
value: option.value,
|
|
75
|
+
label: option.label,
|
|
76
|
+
handleOnClick: () => handleSelect(option),
|
|
77
|
+
className: cn(menuItemBaseStyles, (selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.value) === option.value &&
|
|
78
|
+
"bg-[var(--dropdown-menu-selected-bg)] text-[var(--dropdown-menu-selected-text)]", optionItemClassName),
|
|
79
|
+
}) }, option.value));
|
|
135
80
|
}
|
|
136
|
-
return {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
type: defaultMenuItemType,
|
|
140
|
-
value: option.value,
|
|
141
|
-
label: option.label,
|
|
142
|
-
},
|
|
143
|
-
};
|
|
81
|
+
return (_jsx(ComboboxOption, { value: option, className: ({ focus, selected }) => cn(menuItemBaseStyles, (selected || (selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.value) === option.value) &&
|
|
82
|
+
"bg-[var(--dropdown-menu-selected-bg)] text-[var(--dropdown-menu-selected-text)]", focus &&
|
|
83
|
+
"bg-[var(--dropdown-menu-hover-bg)] text-[var(--dropdown-menu-hover-text)]", optionItemClassName), children: ({ selected }) => (_jsxs(_Fragment, { children: [_jsx("span", { className: "shrink-0 size-4 flex items-center justify-center", children: (selected || (selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.value) === option.value) && (_jsx(Icon, { type: "heroicons", name: "check", className: "size-4 text-[var(--dropdown-menu-selected-text)]" })) }), option.label] })) }, option.value));
|
|
144
84
|
});
|
|
145
|
-
// Add "not found" message if no results
|
|
146
|
-
if (finalMenuItems.length === 0) {
|
|
147
|
-
finalMenuItems.push({
|
|
148
|
-
type: "custom",
|
|
149
|
-
render: () => (_jsx("div", { className: cn("px-4 py-14 text-center text-input-text", optionNotFoundItemClassName), children: "Not found" }, "not-found")),
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
return (_jsx(Menu, { ref: dropdownRef, items: finalMenuItems, selectedValues: (selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.value) ? [selectedOption.value] : [], onSelect: (value) => {
|
|
153
|
-
const option = optionsFiltered.find((opt) => opt.value === value);
|
|
154
|
-
if (option) {
|
|
155
|
-
handleOptionClick(option);
|
|
156
|
-
}
|
|
157
|
-
}, className: cn("absolute mt-1 w-full max-h-60 overflow-y-auto", !usePortal && (isAbove ? "bottom-full mb-1" : "top-full mt-1"), optionContainerClassName), style: dropdownStyles }));
|
|
158
85
|
};
|
|
159
|
-
|
|
160
|
-
var _a;
|
|
161
|
-
setIsFocused(true);
|
|
162
|
-
(_a = props === null || props === void 0 ? void 0 : props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props, e);
|
|
163
|
-
}, [props === null || props === void 0 ? void 0 : props.onFocus]);
|
|
164
|
-
const clearMismatchValue = useCallback((e) => {
|
|
165
|
-
const matchSelectedValue = optionsFiltered.find((opt) => { var _a, _b; return opt.value === ((_a = e.target) === null || _a === void 0 ? void 0 : _a.value) || opt.label === ((_b = e.target) === null || _b === void 0 ? void 0 : _b.value); });
|
|
166
|
-
const isMatchSelectedValue = !!matchSelectedValue;
|
|
167
|
-
let option = matchSelectedValue || {
|
|
168
|
-
value: "",
|
|
169
|
-
label: "",
|
|
170
|
-
};
|
|
171
|
-
if (!isMatchSelectedValue && textValue) {
|
|
172
|
-
option = {
|
|
173
|
-
value: "",
|
|
174
|
-
label: "",
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
if (keyCode.current === "Enter") {
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
setSelectedOption(option);
|
|
181
|
-
setTextValue(option.label);
|
|
182
|
-
onSelect === null || onSelect === void 0 ? void 0 : onSelect(option);
|
|
183
|
-
}, [optionsFiltered, textValue]);
|
|
184
|
-
const handleOnBlur = useCallback((e) => {
|
|
185
|
-
var _a;
|
|
186
|
-
setTimeout(() => setIsFocused(false), 200);
|
|
187
|
-
clearMismatchValue(e);
|
|
188
|
-
(_a = props === null || props === void 0 ? void 0 : props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
|
|
189
|
-
}, [props === null || props === void 0 ? void 0 : props.onBlur, clearMismatchValue]);
|
|
190
|
-
const handleOnKeyDown = useCallback((e) => {
|
|
191
|
-
var _a;
|
|
192
|
-
keyCode.current = e.code;
|
|
193
|
-
(_a = props === null || props === void 0 ? void 0 : props.onKeyDown) === null || _a === void 0 ? void 0 : _a.call(props, e);
|
|
194
|
-
}, [props === null || props === void 0 ? void 0 : props.onKeyDown]);
|
|
195
|
-
return (_jsxs("div", { className: `relative ${fullwidth ? "w-full" : ""}`, children: [_jsx(TextInput, Object.assign({ hasClearIcon: false, endIcon: _jsx(ChevronDownIcon, { className: dropdownIconVariant({ isFocus: isFocused }) }) }, props, { ref: inputRef, readOnly: !filterMode, value: textValue, onChange: handleOnChangeText, label: label, placeholder: " ", type: "text", autoComplete: "off", rounded: rounded, variant: variant, helperText: helperText, errorMessage: errorMessage, fullwidth: fullwidth, error: error, required: required, id: _id, disabled: disabled, size: size, className: segmentedInput ? customInputVariant({ size }) : undefined, onFocus: handleOnFocus, onBlur: handleOnBlur, onKeyDown: handleOnKeyDown })), isFocused &&
|
|
196
|
-
(usePortal ? (_jsx(Portal.Root, { container: document.body, children: renderOptions() })) : (renderOptions()))] }));
|
|
86
|
+
return (_jsx(Combobox, { value: selectedOption, onChange: handleSelect, immediate: true, by: "value", disabled: disabled, children: ({ open }) => (_jsxs("div", { className: cn("relative", fullwidth && "w-full"), children: [_jsx(ComboboxInput, Object.assign({ as: TextInput, ref: inputRef, hasClearIcon: false, endIcon: _jsx(ChevronDownIcon, { className: dropdownIconVariant({ isFocus: open }) }), label: label, placeholder: " ", autoComplete: "off", rounded: rounded, variant: variant, helperText: helperText, errorMessage: errorMessage, fullwidth: fullwidth, error: error, required: required, id: _id, disabled: disabled, size: size, className: segmentedInput ? customInputVariant({ size }) : undefined, displayValue: (opt) => { var _a; return (_a = opt === null || opt === void 0 ? void 0 : opt.label) !== null && _a !== void 0 ? _a : ""; }, readOnly: !filterMode, onChange: handleInputChange }, props)), _jsx(ComboboxOptions, { className: cn("absolute top-full left-0 w-full mt-1 z-[51]", "min-w-[154px] max-h-60 overflow-y-auto", "rounded-lg bg-modal-surface text-text-g-contrast-high", optionContainerClassName), style: { boxShadow: "var(--dropdown-menu-shadow)" }, children: renderOptionList() })] })) }));
|
|
197
87
|
});
|
|
88
|
+
Dropdown.displayName = "Dropdown";
|
|
198
89
|
export default Dropdown;
|
|
@@ -3,6 +3,7 @@ import { useRef, useState } from "react";
|
|
|
3
3
|
import Dropdown from "./Dropdown";
|
|
4
4
|
import Button from "../Button/Button";
|
|
5
5
|
import { cn } from "@/utils/cn";
|
|
6
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody, DialogFooter, DialogTrigger, DialogClose, } from "../Dialog/Dialog";
|
|
6
7
|
// More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction
|
|
7
8
|
const meta = {
|
|
8
9
|
title: "Components/Dropdown",
|
|
@@ -113,3 +114,31 @@ export const WithIcons = {
|
|
|
113
114
|
return (_jsx("div", { className: "flex flex-row gap-4 w-full", children: _jsx(Dropdown, { id: "with-icons", size: "lg", label: "Select Food", fullwidth: true, options: optionsWithIcons, value: selectedValue, onSelect: (option) => setSelectedValue(option) }) }));
|
|
114
115
|
},
|
|
115
116
|
};
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Dropdown inside Dialog — showcases the fix for overflow:hidden bug
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
const dialogOptions = [
|
|
121
|
+
{ value: "design", label: "Design" },
|
|
122
|
+
{ value: "engineering", label: "Engineering" },
|
|
123
|
+
{ value: "product", label: "Product" },
|
|
124
|
+
{ value: "marketing", label: "Marketing" },
|
|
125
|
+
{ value: "data", label: "Data & Analytics" },
|
|
126
|
+
{ value: "ops", label: "Operations" },
|
|
127
|
+
];
|
|
128
|
+
const filterableOptions = new Array(20).fill("").map((_, i) => ({
|
|
129
|
+
value: `member-${i + 1}`,
|
|
130
|
+
label: `Team Member ${i + 1}`,
|
|
131
|
+
}));
|
|
132
|
+
export const InsideDialog = {
|
|
133
|
+
name: "Inside Dialog",
|
|
134
|
+
render: () => {
|
|
135
|
+
const [department, setDepartment] = useState();
|
|
136
|
+
const [member, setMember] = useState();
|
|
137
|
+
const [role, setRole] = useState();
|
|
138
|
+
return (_jsxs("div", { className: "flex gap-4 flex-wrap", children: [_jsxs("div", { children: [_jsx("p", { className: "typography-small4 text-text-g-contrast-medium mb-2", children: "Single Dropdown" }), _jsxs(Dialog, { children: [_jsx(DialogTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", children: "Open Dialog" }) }), _jsxs(DialogContent, { showCloseButton: true, children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Assign to Department" }), _jsx(DialogDescription, { children: "Dropdown popup appears above the dialog overlay \u2014 not clipped by overflow." })] }), _jsx(DialogBody, { className: "gap-4 py-2", children: _jsx(Dropdown, { id: "dept", label: "Department", size: "md", fullwidth: true, options: dialogOptions, value: department, onSelect: setDepartment }) }), _jsxs(DialogFooter, { children: [_jsx(DialogClose, { asChild: true, children: _jsx(Button, { variant: "outline", children: "Cancel" }) }), _jsx(Button, { children: "Confirm" })] })] })] })] }), _jsxs("div", { children: [_jsx("p", { className: "typography-small4 text-text-g-contrast-medium mb-2", children: "Multiple Dropdowns" }), _jsxs(Dialog, { children: [_jsx(DialogTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", children: "Open Dialog" }) }), _jsxs(DialogContent, { showCloseButton: true, children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Invite Team Member" }), _jsx(DialogDescription, { children: "Multiple dropdowns \u2014 each opens its own popup independently." })] }), _jsxs(DialogBody, { className: "gap-4 py-2", children: [_jsx(Dropdown, { id: "member", label: "Member", size: "md", fullwidth: true, options: filterableOptions, value: member, onSelect: setMember, filterMode: true }), _jsx(Dropdown, { id: "role", label: "Role", size: "md", fullwidth: true, options: [
|
|
139
|
+
{ value: "viewer", label: "Viewer" },
|
|
140
|
+
{ value: "editor", label: "Editor" },
|
|
141
|
+
{ value: "admin", label: "Admin" },
|
|
142
|
+
], value: role, onSelect: setRole })] }), _jsxs(DialogFooter, { children: [_jsx(DialogClose, { asChild: true, children: _jsx(Button, { variant: "outline", children: "Cancel" }) }), _jsx(Button, { disabled: !member || !role, children: "Send Invite" })] })] })] })] }), _jsxs("div", { children: [_jsx("p", { className: "typography-small4 text-text-g-contrast-medium mb-2", children: "Filter Mode" }), _jsxs(Dialog, { children: [_jsx(DialogTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", children: "Open Dialog" }) }), _jsxs(DialogContent, { showCloseButton: true, children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Search & Select" }), _jsx(DialogDescription, { children: "filterMode=true \u2014 type to filter options, popup stays properly positioned." })] }), _jsx(DialogBody, { className: "gap-4 py-2", children: _jsx(Dropdown, { id: "member-filter", label: "Search member", size: "md", fullwidth: true, filterMode: true, options: filterableOptions, value: member, onSelect: setMember }) }), _jsxs(DialogFooter, { children: [_jsx(DialogClose, { asChild: true, children: _jsx(Button, { variant: "outline", children: "Cancel" }) }), _jsx(Button, { disabled: !member, children: "Select" })] })] })] })] })] }));
|
|
143
|
+
},
|
|
144
|
+
};
|
|
@@ -23,7 +23,7 @@ const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
|
23
23
|
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
24
24
|
const DropdownMenuSubTrigger = React.forwardRef((_a, ref) => {
|
|
25
25
|
var { className, inset, children } = _a, props = __rest(_a, ["className", "inset", "children"]);
|
|
26
|
-
return (_jsxs(DropdownMenuPrimitive.SubTrigger, Object.assign({ ref: ref, className: cn("relative flex gap-4 cursor-pointer select-none box-border items-center py-4 pl-
|
|
26
|
+
return (_jsxs(DropdownMenuPrimitive.SubTrigger, Object.assign({ ref: ref, className: cn("relative flex gap-4 cursor-pointer select-none box-border items-center py-4 pl-4 pr-4 typography-subtitle4 outline-none transition-colors data-[disabled]:pointer-events-none", "bg-[var(--dropdown-menu-default-bg)] text-[var(--dropdown-menu-default-text)]", "active:opacity-75", "focus:!bg-[var(--dropdown-menu-hover-bg)] focus:!text-[var(--dropdown-menu-hover-text)]", "data-[disabled]:!bg-[var(--dropdown-menu-disabled-bg)] data-[disabled]:!text-[var(--dropdown-menu-disabled-text)]", inset && "pl-8", className) }, props, { children: [children, _jsx(Icon, { type: "heroicons", name: "chevron-right", className: "ml-auto h-4 w-4" })] })));
|
|
27
27
|
});
|
|
28
28
|
DropdownMenuSubTrigger.displayName =
|
|
29
29
|
DropdownMenuPrimitive.SubTrigger.displayName;
|
|
@@ -39,29 +39,42 @@ const DropdownMenuContent = React.forwardRef((_a, ref) => {
|
|
|
39
39
|
});
|
|
40
40
|
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
41
41
|
const DropdownMenuItem = React.forwardRef((_a, ref) => {
|
|
42
|
-
var { className, inset } = _a, props = __rest(_a, ["className", "inset"]);
|
|
43
|
-
|
|
42
|
+
var { className, inset, selected, icon, children } = _a, props = __rest(_a, ["className", "inset", "selected", "icon", "children"]);
|
|
43
|
+
const hasIcon = !!icon;
|
|
44
|
+
return (_jsxs(DropdownMenuPrimitive.Item, Object.assign({ ref: ref, className: cn("relative flex cursor-pointer select-none box-border items-center py-4 pl-4 pr-8 typography-subtitle4 outline-none transition-colors data-[disabled]:pointer-events-none", "bg-[var(--dropdown-menu-default-bg)] text-[var(--dropdown-menu-default-text)]", "active:opacity-75", "focus:!bg-[var(--dropdown-menu-hover-bg)] focus:!text-[var(--dropdown-menu-hover-text)]", selected &&
|
|
45
|
+
"bg-[var(--dropdown-menu-selected-bg)] text-[var(--dropdown-menu-selected-text)] typography-subtitle5", "data-[disabled]:!bg-[var(--dropdown-menu-disabled-bg)] data-[disabled]:!text-[var(--dropdown-menu-disabled-text)]", inset && "pl-8", className, hasIcon ? "gap-4" : "gap-1") }, props, { children: [_jsxs("div", { className: "flex shrink-0 flex-row gap-1", children: [_jsx("span", { className: "size-4 flex items-center justify-center", children: selected && (_jsx(Icon, { type: "heroicons", name: "check", className: "size-4 text-[var(--dropdown-menu-selected-text)]" })) }), icon] }), children] })));
|
|
44
46
|
});
|
|
45
47
|
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
46
48
|
const DropdownMenuCheckboxItem = React.forwardRef((_a, ref) => {
|
|
47
|
-
var { className, children, checked } = _a, props = __rest(_a, ["className", "children", "checked"]);
|
|
48
|
-
return (_jsxs(DropdownMenuPrimitive.CheckboxItem, Object.assign({ ref: ref, className: cn("relative flex gap-
|
|
49
|
+
var { className, children, checked, disabled } = _a, props = __rest(_a, ["className", "children", "checked", "disabled"]);
|
|
50
|
+
return (_jsxs(DropdownMenuPrimitive.CheckboxItem, Object.assign({ ref: ref, className: cn("relative flex gap-3 cursor-pointer select-none box-border items-center py-4 pl-4 pr-8 typography-subtitle4 outline-none transition-colors data-[disabled]:pointer-events-none", "bg-[var(--dropdown-menu-default-bg)] text-[var(--dropdown-menu-default-text)]", "active:opacity-75", "focus:!bg-[var(--dropdown-menu-hover-bg)] focus:!text-[var(--dropdown-menu-hover-text)]", "data-[state='checked']:bg-[var(--dropdown-menu-selected-bg)] data-[state='checked']:text-[var(--dropdown-menu-selected-text)]", "data-[disabled]:!bg-[var(--dropdown-menu-disabled-bg)] data-[disabled]:!text-[var(--dropdown-menu-disabled-text)]", className), checked: checked, disabled: disabled }, props, { children: [_jsx("span", { className: cn("shrink-0 size-4 rounded-[2px] border flex items-center justify-center transition-all overflow-hidden", checked &&
|
|
51
|
+
!disabled &&
|
|
52
|
+
"bg-[var(--dropdown-menu-checkbox-checked-bg)] border-[var(--dropdown-menu-checkbox-checked-bg)]", checked &&
|
|
53
|
+
disabled &&
|
|
54
|
+
"bg-[var(--dropdown-menu-checkbox-disabled-checked-bg)] border-transparent", !checked &&
|
|
55
|
+
disabled &&
|
|
56
|
+
"border-[var(--dropdown-menu-checkbox-disabled-border)]", !checked &&
|
|
57
|
+
!disabled &&
|
|
58
|
+
"border-[var(--dropdown-menu-checkbox-border)]"), children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx(Icon, { type: "heroicons", name: "check", className: "size-3 text-[var(--dropdown-menu-checkbox-checked-icon)]" }) }) }), children] })));
|
|
49
59
|
});
|
|
50
60
|
DropdownMenuCheckboxItem.displayName =
|
|
51
61
|
DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
52
62
|
const DropdownMenuRadioItem = React.forwardRef((_a, ref) => {
|
|
53
|
-
var { className, children } = _a, props = __rest(_a, ["className", "children"]);
|
|
54
|
-
|
|
63
|
+
var { className, children, disabled, icon } = _a, props = __rest(_a, ["className", "children", "disabled", "icon"]);
|
|
64
|
+
const hasIconSlot = !!icon;
|
|
65
|
+
return (_jsxs(DropdownMenuPrimitive.RadioItem, Object.assign({ ref: ref, className: cn("relative flex cursor-pointer select-none box-border items-center py-4 pl-4 pr-8 typography-subtitle4 outline-none transition-colors data-[disabled]:pointer-events-none", "bg-[var(--dropdown-menu-default-bg)] text-[var(--dropdown-menu-default-text)]", "active:opacity-75", "focus:!bg-[var(--dropdown-menu-hover-bg)] focus:!text-[var(--dropdown-menu-hover-text)]", "data-[state='checked']:bg-[var(--dropdown-menu-selected-bg)] data-[state='checked']:text-[var(--dropdown-menu-selected-text)]", "data-[disabled]:!bg-[var(--dropdown-menu-disabled-bg)] data-[disabled]:!text-[var(--dropdown-menu-radio-disabled-text)]", "data-[state='checked']:data-[disabled]:!text-[var(--dropdown-menu-radio-selected-disabled-text)]", className, hasIconSlot ? "gap-4" : "gap-1") }, props, { disabled: disabled, children: [_jsxs("div", { className: "flex shrink-0 flex-row gap-1", children: [_jsx("span", { className: "size-4", children: _jsx(DropdownMenuPrimitive.ItemIndicator, { className: "shrink-0", children: _jsx(Icon, { type: "heroicons", name: "check", className: cn("size-4", disabled
|
|
66
|
+
? "text-[var(--dropdown-menu-radio-selected-disabled-text)]"
|
|
67
|
+
: "text-[var(--dropdown-menu-selected-text)]") }) }) }), icon] }), children] })));
|
|
55
68
|
});
|
|
56
69
|
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
57
70
|
const DropdownMenuLabel = React.forwardRef((_a, ref) => {
|
|
58
71
|
var { className, inset } = _a, props = __rest(_a, ["className", "inset"]);
|
|
59
|
-
return (_jsx(DropdownMenuPrimitive.Label, Object.assign({ ref: ref, className: cn("px-3
|
|
72
|
+
return (_jsx(DropdownMenuPrimitive.Label, Object.assign({ ref: ref, className: cn("px-3 py-2 typography-small4 text-text-g-contrast-high", inset && "pl-8", className) }, props)));
|
|
60
73
|
});
|
|
61
74
|
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
62
75
|
const DropdownMenuSeparator = React.forwardRef((_a, ref) => {
|
|
63
76
|
var { className } = _a, props = __rest(_a, ["className"]);
|
|
64
|
-
return (_jsx(DropdownMenuPrimitive.Separator, Object.assign({ ref: ref, className: cn("
|
|
77
|
+
return (_jsx(DropdownMenuPrimitive.Separator, Object.assign({ ref: ref, className: cn("h-px bg-[var(--dropdown-menu-seperator-bg)]", className) }, props)));
|
|
65
78
|
});
|
|
66
79
|
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
67
80
|
const DropdownMenuShortcut = (_a) => {
|