@tonyarbor/components 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Avatar.d.mts +42 -0
- package/dist/Avatar.d.ts +42 -0
- package/dist/Avatar.js +158 -0
- package/dist/Avatar.js.map +1 -0
- package/dist/Avatar.mjs +7 -0
- package/dist/Avatar.mjs.map +1 -0
- package/dist/Breadcrumbs.d.mts +51 -0
- package/dist/Breadcrumbs.d.ts +51 -0
- package/dist/Breadcrumbs.js +276 -0
- package/dist/Breadcrumbs.js.map +1 -0
- package/dist/Breadcrumbs.mjs +7 -0
- package/dist/Breadcrumbs.mjs.map +1 -0
- package/dist/SearchGlobal.d.mts +45 -0
- package/dist/SearchGlobal.d.ts +45 -0
- package/dist/SearchGlobal.js +209 -0
- package/dist/SearchGlobal.js.map +1 -0
- package/dist/SearchGlobal.mjs +7 -0
- package/dist/SearchGlobal.mjs.map +1 -0
- package/dist/SearchOnPage.d.mts +45 -0
- package/dist/SearchOnPage.d.ts +45 -0
- package/dist/SearchOnPage.js +171 -0
- package/dist/SearchOnPage.js.map +1 -0
- package/dist/SearchOnPage.mjs +7 -0
- package/dist/SearchOnPage.mjs.map +1 -0
- package/dist/chunk-B7RX3TPX.mjs +135 -0
- package/dist/chunk-B7RX3TPX.mjs.map +1 -0
- package/dist/chunk-JSG27ZZS.mjs +122 -0
- package/dist/chunk-JSG27ZZS.mjs.map +1 -0
- package/dist/chunk-RQP6ZGD7.mjs +240 -0
- package/dist/chunk-RQP6ZGD7.mjs.map +1 -0
- package/dist/chunk-UPBHDBAK.mjs +173 -0
- package/dist/chunk-UPBHDBAK.mjs.map +1 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +662 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +16 -0
- package/package.json +21 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// src/SearchOnPage/SearchOnPage.tsx
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Search, X } from "lucide-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
var SearchOnPage = React.forwardRef(
|
|
6
|
+
({
|
|
7
|
+
value = "",
|
|
8
|
+
onChange,
|
|
9
|
+
onSubmit,
|
|
10
|
+
onClear,
|
|
11
|
+
placeholder = "Search Reports",
|
|
12
|
+
className,
|
|
13
|
+
style,
|
|
14
|
+
"data-testid": dataTestId
|
|
15
|
+
}, ref) => {
|
|
16
|
+
const [isFocused, setIsFocused] = React.useState(false);
|
|
17
|
+
const [isHovered, setIsHovered] = React.useState(false);
|
|
18
|
+
const inputRef = React.useRef(null);
|
|
19
|
+
React.useImperativeHandle(ref, () => inputRef.current);
|
|
20
|
+
const handleFocus = () => {
|
|
21
|
+
setIsFocused(true);
|
|
22
|
+
};
|
|
23
|
+
const handleBlur = () => {
|
|
24
|
+
setIsFocused(false);
|
|
25
|
+
};
|
|
26
|
+
const handleChange = (e) => {
|
|
27
|
+
onChange?.(e.target.value);
|
|
28
|
+
};
|
|
29
|
+
const handleKeyDown = (e) => {
|
|
30
|
+
if (e.key === "Enter") {
|
|
31
|
+
onSubmit?.(value);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const handleClear = () => {
|
|
35
|
+
onChange?.("");
|
|
36
|
+
onClear?.();
|
|
37
|
+
inputRef.current?.focus();
|
|
38
|
+
};
|
|
39
|
+
const containerStyles = {
|
|
40
|
+
position: "relative",
|
|
41
|
+
width: "200px",
|
|
42
|
+
height: "32px",
|
|
43
|
+
backgroundColor: isFocused ? "#ffffff" : isHovered ? "#efefef" : "#ffffff",
|
|
44
|
+
borderRadius: "16px",
|
|
45
|
+
padding: "8px 16px",
|
|
46
|
+
display: "flex",
|
|
47
|
+
alignItems: "center",
|
|
48
|
+
gap: "8px",
|
|
49
|
+
cursor: "text",
|
|
50
|
+
transition: "all 0.2s ease-in-out",
|
|
51
|
+
boxSizing: "border-box",
|
|
52
|
+
border: isFocused ? "1px solid #efefef" : "none",
|
|
53
|
+
boxShadow: isFocused ? "0px 0px 0px 3px #3cad51" : "none",
|
|
54
|
+
...style
|
|
55
|
+
};
|
|
56
|
+
const iconContainerStyles = {
|
|
57
|
+
display: "flex",
|
|
58
|
+
alignItems: "center",
|
|
59
|
+
justifyContent: "center",
|
|
60
|
+
flexShrink: 0,
|
|
61
|
+
padding: "2px"
|
|
62
|
+
};
|
|
63
|
+
const inputStyles = {
|
|
64
|
+
border: "none",
|
|
65
|
+
outline: "none",
|
|
66
|
+
backgroundColor: "transparent",
|
|
67
|
+
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
|
68
|
+
fontSize: "13px",
|
|
69
|
+
fontWeight: 400,
|
|
70
|
+
color: "#2f2f2f",
|
|
71
|
+
lineHeight: "1.5",
|
|
72
|
+
flex: 1,
|
|
73
|
+
width: "100%",
|
|
74
|
+
minWidth: 0
|
|
75
|
+
};
|
|
76
|
+
const clearButtonStyles = {
|
|
77
|
+
display: "flex",
|
|
78
|
+
alignItems: "center",
|
|
79
|
+
justifyContent: "center",
|
|
80
|
+
flexShrink: 0,
|
|
81
|
+
width: "16px",
|
|
82
|
+
height: "16px",
|
|
83
|
+
cursor: "pointer",
|
|
84
|
+
border: "none",
|
|
85
|
+
background: "none",
|
|
86
|
+
padding: 0
|
|
87
|
+
};
|
|
88
|
+
const showClearButton = isFocused && value.length > 0;
|
|
89
|
+
const iconColor = isFocused || isHovered ? "#2f2f2f" : "#595959";
|
|
90
|
+
return /* @__PURE__ */ jsxs(
|
|
91
|
+
"div",
|
|
92
|
+
{
|
|
93
|
+
className,
|
|
94
|
+
style: containerStyles,
|
|
95
|
+
onMouseEnter: () => setIsHovered(true),
|
|
96
|
+
onMouseLeave: () => setIsHovered(false),
|
|
97
|
+
onClick: () => inputRef.current?.focus(),
|
|
98
|
+
"data-testid": dataTestId,
|
|
99
|
+
children: [
|
|
100
|
+
/* @__PURE__ */ jsx("div", { style: iconContainerStyles, children: /* @__PURE__ */ jsx(Search, { size: 12, color: iconColor, strokeWidth: 2 }) }),
|
|
101
|
+
/* @__PURE__ */ jsx(
|
|
102
|
+
"input",
|
|
103
|
+
{
|
|
104
|
+
ref: inputRef,
|
|
105
|
+
type: "text",
|
|
106
|
+
value,
|
|
107
|
+
onChange: handleChange,
|
|
108
|
+
onFocus: handleFocus,
|
|
109
|
+
onBlur: handleBlur,
|
|
110
|
+
onKeyDown: handleKeyDown,
|
|
111
|
+
placeholder: isFocused ? "" : placeholder,
|
|
112
|
+
style: inputStyles
|
|
113
|
+
}
|
|
114
|
+
),
|
|
115
|
+
showClearButton && /* @__PURE__ */ jsx(
|
|
116
|
+
"button",
|
|
117
|
+
{
|
|
118
|
+
type: "button",
|
|
119
|
+
onClick: handleClear,
|
|
120
|
+
style: clearButtonStyles,
|
|
121
|
+
"aria-label": "Clear search",
|
|
122
|
+
children: /* @__PURE__ */ jsx(X, { size: 13.333, color: "#2f2f2f", strokeWidth: 2 })
|
|
123
|
+
}
|
|
124
|
+
)
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
SearchOnPage.displayName = "SearchOnPage";
|
|
131
|
+
|
|
132
|
+
export {
|
|
133
|
+
SearchOnPage
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=chunk-B7RX3TPX.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/SearchOnPage/SearchOnPage.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Search, X } from 'lucide-react';\n\nexport interface SearchOnPageProps {\n /**\n * Value of the search input\n */\n value?: string;\n /**\n * Callback when value changes\n */\n onChange?: (value: string) => void;\n /**\n * Callback when search is submitted\n */\n onSubmit?: (value: string) => void;\n /**\n * Callback when clear button is clicked\n */\n onClear?: () => void;\n /**\n * Placeholder text\n */\n placeholder?: string;\n /**\n * Custom className\n */\n className?: string;\n /**\n * Custom style\n */\n style?: React.CSSProperties;\n /**\n * Test ID for testing\n */\n 'data-testid'?: string;\n}\n\n/**\n * SearchOnPage component - Arbor Design System\n *\n * A search input with fixed 200px width for filtering content on the current page.\n * Shows green focus ring when active and X button to clear.\n */\nexport const SearchOnPage = React.forwardRef<HTMLInputElement, SearchOnPageProps>(\n (\n {\n value = '',\n onChange,\n onSubmit,\n onClear,\n placeholder = 'Search Reports',\n className,\n style,\n 'data-testid': dataTestId,\n },\n ref\n ) => {\n const [isFocused, setIsFocused] = React.useState(false);\n const [isHovered, setIsHovered] = React.useState(false);\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n // Merge refs\n React.useImperativeHandle(ref, () => inputRef.current as HTMLInputElement);\n\n const handleFocus = () => {\n setIsFocused(true);\n };\n\n const handleBlur = () => {\n setIsFocused(false);\n };\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n onChange?.(e.target.value);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') {\n onSubmit?.(value);\n }\n };\n\n const handleClear = () => {\n onChange?.('');\n onClear?.();\n inputRef.current?.focus();\n };\n\n const containerStyles: React.CSSProperties = {\n position: 'relative',\n width: '200px',\n height: '32px',\n backgroundColor: isFocused ? '#ffffff' : isHovered ? '#efefef' : '#ffffff',\n borderRadius: '16px',\n padding: '8px 16px',\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n cursor: 'text',\n transition: 'all 0.2s ease-in-out',\n boxSizing: 'border-box',\n border: isFocused ? '1px solid #efefef' : 'none',\n boxShadow: isFocused ? '0px 0px 0px 3px #3cad51' : 'none',\n ...style,\n };\n\n const iconContainerStyles: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n padding: '2px',\n };\n\n const inputStyles: React.CSSProperties = {\n border: 'none',\n outline: 'none',\n backgroundColor: 'transparent',\n fontFamily: \"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n fontSize: '13px',\n fontWeight: 400,\n color: '#2f2f2f',\n lineHeight: '1.5',\n flex: 1,\n width: '100%',\n minWidth: 0,\n };\n\n const clearButtonStyles: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n width: '16px',\n height: '16px',\n cursor: 'pointer',\n border: 'none',\n background: 'none',\n padding: 0,\n };\n\n const showClearButton = isFocused && value.length > 0;\n const iconColor = isFocused || isHovered ? '#2f2f2f' : '#595959';\n\n return (\n <div\n className={className}\n style={containerStyles}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n onClick={() => inputRef.current?.focus()}\n data-testid={dataTestId}\n >\n <div style={iconContainerStyles}>\n <Search size={12} color={iconColor} strokeWidth={2} />\n </div>\n\n <input\n ref={inputRef}\n type=\"text\"\n value={value}\n onChange={handleChange}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n placeholder={isFocused ? '' : placeholder}\n style={inputStyles}\n />\n\n {showClearButton && (\n <button\n type=\"button\"\n onClick={handleClear}\n style={clearButtonStyles}\n aria-label=\"Clear search\"\n >\n <X size={13.333} color=\"#2f2f2f\" strokeWidth={2} />\n </button>\n )}\n </div>\n );\n }\n);\n\nSearchOnPage.displayName = 'SearchOnPage';\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// src/Avatar/Avatar.tsx
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { User } from "lucide-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
var sizeConfig = {
|
|
6
|
+
small: {
|
|
7
|
+
size: 20,
|
|
8
|
+
fontSize: 0,
|
|
9
|
+
// Too small for initials
|
|
10
|
+
fontWeight: 400,
|
|
11
|
+
iconSize: 12,
|
|
12
|
+
borderRadius: "99px"
|
|
13
|
+
},
|
|
14
|
+
medium: {
|
|
15
|
+
size: 32,
|
|
16
|
+
fontSize: 13,
|
|
17
|
+
fontWeight: 400,
|
|
18
|
+
iconSize: 16,
|
|
19
|
+
borderRadius: "99px"
|
|
20
|
+
},
|
|
21
|
+
large: {
|
|
22
|
+
size: 48,
|
|
23
|
+
fontSize: 13,
|
|
24
|
+
fontWeight: 400,
|
|
25
|
+
iconSize: 24,
|
|
26
|
+
borderRadius: "4px"
|
|
27
|
+
},
|
|
28
|
+
"extra-large": {
|
|
29
|
+
size: 96,
|
|
30
|
+
fontSize: 27,
|
|
31
|
+
fontWeight: 600,
|
|
32
|
+
iconSize: 48,
|
|
33
|
+
borderRadius: "8px"
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var Avatar = React.forwardRef(
|
|
37
|
+
({
|
|
38
|
+
size = "medium",
|
|
39
|
+
src,
|
|
40
|
+
initials,
|
|
41
|
+
alt = "Avatar",
|
|
42
|
+
className,
|
|
43
|
+
style,
|
|
44
|
+
"data-testid": dataTestId
|
|
45
|
+
}, ref) => {
|
|
46
|
+
const [isHovered, setIsHovered] = React.useState(false);
|
|
47
|
+
const [imageError, setImageError] = React.useState(false);
|
|
48
|
+
const config = sizeConfig[size];
|
|
49
|
+
const hasHoverState = size === "small";
|
|
50
|
+
const containerStyles = {
|
|
51
|
+
width: `${config.size}px`,
|
|
52
|
+
height: `${config.size}px`,
|
|
53
|
+
borderRadius: config.borderRadius,
|
|
54
|
+
border: "1px solid #efefef",
|
|
55
|
+
overflow: "hidden",
|
|
56
|
+
position: "relative",
|
|
57
|
+
backgroundColor: "#f8f8f8",
|
|
58
|
+
display: "flex",
|
|
59
|
+
alignItems: "center",
|
|
60
|
+
justifyContent: "center",
|
|
61
|
+
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
|
62
|
+
cursor: "default",
|
|
63
|
+
boxSizing: "border-box",
|
|
64
|
+
...style
|
|
65
|
+
};
|
|
66
|
+
const hoverOverlayStyles = {
|
|
67
|
+
position: "absolute",
|
|
68
|
+
inset: 0,
|
|
69
|
+
pointerEvents: "none",
|
|
70
|
+
boxShadow: isHovered && hasHoverState ? "inset 0px 4px 100px 0px rgba(32, 32, 32, 0.3)" : "none",
|
|
71
|
+
borderRadius: config.borderRadius,
|
|
72
|
+
transition: "box-shadow 0.2s ease-in-out"
|
|
73
|
+
};
|
|
74
|
+
const imageStyles = {
|
|
75
|
+
width: "100%",
|
|
76
|
+
height: "100%",
|
|
77
|
+
objectFit: "cover"
|
|
78
|
+
};
|
|
79
|
+
const initialsStyles = {
|
|
80
|
+
fontSize: `${config.fontSize}px`,
|
|
81
|
+
fontWeight: config.fontWeight,
|
|
82
|
+
color: "#2f2f2f",
|
|
83
|
+
lineHeight: 1.5,
|
|
84
|
+
textAlign: "center",
|
|
85
|
+
userSelect: "none"
|
|
86
|
+
};
|
|
87
|
+
const showImage = src && !imageError;
|
|
88
|
+
const showInitials = !showImage && initials && size !== "small";
|
|
89
|
+
const showPlaceholder = !showImage && !showInitials;
|
|
90
|
+
return /* @__PURE__ */ jsxs(
|
|
91
|
+
"div",
|
|
92
|
+
{
|
|
93
|
+
ref,
|
|
94
|
+
className,
|
|
95
|
+
style: containerStyles,
|
|
96
|
+
onMouseEnter: () => setIsHovered(true),
|
|
97
|
+
onMouseLeave: () => setIsHovered(false),
|
|
98
|
+
"data-testid": dataTestId,
|
|
99
|
+
children: [
|
|
100
|
+
showImage && /* @__PURE__ */ jsx(
|
|
101
|
+
"img",
|
|
102
|
+
{
|
|
103
|
+
src,
|
|
104
|
+
alt,
|
|
105
|
+
style: imageStyles,
|
|
106
|
+
onError: () => setImageError(true)
|
|
107
|
+
}
|
|
108
|
+
),
|
|
109
|
+
showInitials && /* @__PURE__ */ jsx("span", { style: initialsStyles, children: initials.slice(0, 2).toUpperCase() }),
|
|
110
|
+
showPlaceholder && /* @__PURE__ */ jsx(User, { size: config.iconSize, color: "#d1d1d1", strokeWidth: 2 }),
|
|
111
|
+
/* @__PURE__ */ jsx("div", { style: hoverOverlayStyles })
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
Avatar.displayName = "Avatar";
|
|
118
|
+
|
|
119
|
+
export {
|
|
120
|
+
Avatar
|
|
121
|
+
};
|
|
122
|
+
//# sourceMappingURL=chunk-JSG27ZZS.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/Avatar/Avatar.tsx"],"sourcesContent":["import * as React from 'react';\nimport { User } from 'lucide-react';\n\nexport type AvatarSize = 'small' | 'medium' | 'large' | 'extra-large';\n\nexport interface AvatarProps {\n /**\n * Size of the avatar\n */\n size?: AvatarSize;\n /**\n * Image source URL\n */\n src?: string;\n /**\n * Initials to display (2 characters) when no image is provided\n */\n initials?: string;\n /**\n * Alt text for the image\n */\n alt?: string;\n /**\n * Custom className\n */\n className?: string;\n /**\n * Custom style\n */\n style?: React.CSSProperties;\n /**\n * Test ID for testing\n */\n 'data-testid'?: string;\n}\n\nconst sizeConfig = {\n small: {\n size: 20,\n fontSize: 0, // Too small for initials\n fontWeight: 400,\n iconSize: 12,\n borderRadius: '99px',\n },\n medium: {\n size: 32,\n fontSize: 13,\n fontWeight: 400,\n iconSize: 16,\n borderRadius: '99px',\n },\n large: {\n size: 48,\n fontSize: 13,\n fontWeight: 400,\n iconSize: 24,\n borderRadius: '4px',\n },\n 'extra-large': {\n size: 96,\n fontSize: 27,\n fontWeight: 600,\n iconSize: 48,\n borderRadius: '8px',\n },\n};\n\n/**\n * Avatar component - Arbor Design System\n *\n * Displays a user avatar with support for images, initials, or a placeholder icon.\n * Features hover state with inner shadow effect.\n */\nexport const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(\n (\n {\n size = 'medium',\n src,\n initials,\n alt = 'Avatar',\n className,\n style,\n 'data-testid': dataTestId,\n },\n ref\n ) => {\n const [isHovered, setIsHovered] = React.useState(false);\n const [imageError, setImageError] = React.useState(false);\n const config = sizeConfig[size];\n\n // Only apply hover state for small size\n const hasHoverState = size === 'small';\n\n const containerStyles: React.CSSProperties = {\n width: `${config.size}px`,\n height: `${config.size}px`,\n borderRadius: config.borderRadius,\n border: '1px solid #efefef',\n overflow: 'hidden',\n position: 'relative',\n backgroundColor: '#f8f8f8',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontFamily: \"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n cursor: 'default',\n boxSizing: 'border-box',\n ...style,\n };\n\n const hoverOverlayStyles: React.CSSProperties = {\n position: 'absolute',\n inset: 0,\n pointerEvents: 'none',\n boxShadow: isHovered && hasHoverState ? 'inset 0px 4px 100px 0px rgba(32, 32, 32, 0.3)' : 'none',\n borderRadius: config.borderRadius,\n transition: 'box-shadow 0.2s ease-in-out',\n };\n\n const imageStyles: React.CSSProperties = {\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n };\n\n const initialsStyles: React.CSSProperties = {\n fontSize: `${config.fontSize}px`,\n fontWeight: config.fontWeight,\n color: '#2f2f2f',\n lineHeight: 1.5,\n textAlign: 'center',\n userSelect: 'none',\n };\n\n // Determine what to display\n const showImage = src && !imageError;\n const showInitials = !showImage && initials && size !== 'small';\n const showPlaceholder = !showImage && !showInitials;\n\n return (\n <div\n ref={ref}\n className={className}\n style={containerStyles}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n data-testid={dataTestId}\n >\n {showImage && (\n <img\n src={src}\n alt={alt}\n style={imageStyles}\n onError={() => setImageError(true)}\n />\n )}\n\n {showInitials && (\n <span style={initialsStyles}>\n {initials.slice(0, 2).toUpperCase()}\n </span>\n )}\n\n {showPlaceholder && (\n <User size={config.iconSize} color=\"#d1d1d1\" strokeWidth={2} />\n )}\n\n <div style={hoverOverlayStyles} />\n </div>\n );\n }\n);\n\nAvatar.displayName = 'Avatar';\n"],"mappings":";AAAA,YAAY,WAAW;AACvB,SAAS,YAAY;AA2If,SASI,KATJ;AAxGN,IAAM,aAAa;AAAA,EACjB,OAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA;AAAA,IACV,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,cAAc;AAAA,EAChB;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,cAAc;AAAA,EAChB;AAAA,EACA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,cAAc;AAAA,EAChB;AAAA,EACA,eAAe;AAAA,IACb,MAAM;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,cAAc;AAAA,EAChB;AACF;AAQO,IAAM,SAAe;AAAA,EAC1B,CACE;AAAA,IACE,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,eAAe;AAAA,EACjB,GACA,QACG;AACH,UAAM,CAAC,WAAW,YAAY,IAAU,eAAS,KAAK;AACtD,UAAM,CAAC,YAAY,aAAa,IAAU,eAAS,KAAK;AACxD,UAAM,SAAS,WAAW,IAAI;AAG9B,UAAM,gBAAgB,SAAS;AAE/B,UAAM,kBAAuC;AAAA,MAC3C,OAAO,GAAG,OAAO,IAAI;AAAA,MACrB,QAAQ,GAAG,OAAO,IAAI;AAAA,MACtB,cAAc,OAAO;AAAA,MACrB,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,UAAU;AAAA,MACV,iBAAiB;AAAA,MACjB,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,GAAG;AAAA,IACL;AAEA,UAAM,qBAA0C;AAAA,MAC9C,UAAU;AAAA,MACV,OAAO;AAAA,MACP,eAAe;AAAA,MACf,WAAW,aAAa,gBAAgB,kDAAkD;AAAA,MAC1F,cAAc,OAAO;AAAA,MACrB,YAAY;AAAA,IACd;AAEA,UAAM,cAAmC;AAAA,MACvC,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,WAAW;AAAA,IACb;AAEA,UAAM,iBAAsC;AAAA,MAC1C,UAAU,GAAG,OAAO,QAAQ;AAAA,MAC5B,YAAY,OAAO;AAAA,MACnB,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,YAAY;AAAA,IACd;AAGA,UAAM,YAAY,OAAO,CAAC;AAC1B,UAAM,eAAe,CAAC,aAAa,YAAY,SAAS;AACxD,UAAM,kBAAkB,CAAC,aAAa,CAAC;AAEvC,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,OAAO;AAAA,QACP,cAAc,MAAM,aAAa,IAAI;AAAA,QACrC,cAAc,MAAM,aAAa,KAAK;AAAA,QACtC,eAAa;AAAA,QAEZ;AAAA,uBACC;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA;AAAA,cACA,OAAO;AAAA,cACP,SAAS,MAAM,cAAc,IAAI;AAAA;AAAA,UACnC;AAAA,UAGD,gBACC,oBAAC,UAAK,OAAO,gBACV,mBAAS,MAAM,GAAG,CAAC,EAAE,YAAY,GACpC;AAAA,UAGD,mBACC,oBAAC,QAAK,MAAM,OAAO,UAAU,OAAM,WAAU,aAAa,GAAG;AAAA,UAG/D,oBAAC,SAAI,OAAO,oBAAoB;AAAA;AAAA;AAAA,IAClC;AAAA,EAEJ;AACF;AAEA,OAAO,cAAc;","names":[]}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// src/Breadcrumbs/Breadcrumbs.tsx
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Link, ChevronDown, MoreHorizontal } from "lucide-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
var Breadcrumbs = React.forwardRef(
|
|
6
|
+
({
|
|
7
|
+
items,
|
|
8
|
+
onCopy,
|
|
9
|
+
className,
|
|
10
|
+
style,
|
|
11
|
+
"data-testid": dataTestId
|
|
12
|
+
}, ref) => {
|
|
13
|
+
const [hoveredIndex, setHoveredIndex] = React.useState(null);
|
|
14
|
+
const [focusedIndex, setFocusedIndex] = React.useState(null);
|
|
15
|
+
const [showCopyTooltip, setShowCopyTooltip] = React.useState(false);
|
|
16
|
+
const [ellipsisFocused, setEllipsisFocused] = React.useState(false);
|
|
17
|
+
const [ellipsisHovered, setEllipsisHovered] = React.useState(false);
|
|
18
|
+
const displayItems = React.useMemo(() => {
|
|
19
|
+
if (items.length > 6) {
|
|
20
|
+
return [items[0], { label: "...", isEllipsis: true }, items[items.length - 1]];
|
|
21
|
+
}
|
|
22
|
+
return items;
|
|
23
|
+
}, [items]);
|
|
24
|
+
const handleCopy = () => {
|
|
25
|
+
onCopy?.();
|
|
26
|
+
const trail = items.map((item) => item.label).join(" / ");
|
|
27
|
+
navigator.clipboard.writeText(trail);
|
|
28
|
+
};
|
|
29
|
+
const containerStyles = {
|
|
30
|
+
display: "flex",
|
|
31
|
+
alignItems: "center",
|
|
32
|
+
gap: "8px",
|
|
33
|
+
...style
|
|
34
|
+
};
|
|
35
|
+
const breadcrumbItemStyles = (index, isActive, isEllipsis) => {
|
|
36
|
+
const isFocused = focusedIndex === index;
|
|
37
|
+
if (isEllipsis) {
|
|
38
|
+
return {
|
|
39
|
+
display: "flex",
|
|
40
|
+
alignItems: "center",
|
|
41
|
+
gap: "4px",
|
|
42
|
+
height: "24px",
|
|
43
|
+
overflow: "hidden"
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
display: "flex",
|
|
48
|
+
alignItems: "center",
|
|
49
|
+
gap: "4px",
|
|
50
|
+
height: "24px",
|
|
51
|
+
overflow: "hidden",
|
|
52
|
+
backgroundColor: isFocused ? "rgba(255, 255, 255, 0.01)" : "transparent",
|
|
53
|
+
borderRadius: "99px",
|
|
54
|
+
boxShadow: isFocused ? "0px 0px 0px 3px #3cad51" : "none",
|
|
55
|
+
padding: isFocused ? "0 6px" : "0 2px",
|
|
56
|
+
margin: isFocused ? "0" : "0 4px",
|
|
57
|
+
cursor: isActive ? "default" : "pointer",
|
|
58
|
+
textDecoration: "none"
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
const linkStyles = (isActive, isHovered) => ({
|
|
62
|
+
fontFamily: isActive ? "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" : "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
|
63
|
+
fontSize: "13px",
|
|
64
|
+
fontWeight: isActive ? 600 : 400,
|
|
65
|
+
color: isActive ? isHovered ? "#0e8a0e" : "#2f2f2f" : isHovered ? "#0e8a0e" : "#595959",
|
|
66
|
+
lineHeight: "1.5",
|
|
67
|
+
whiteSpace: "nowrap"
|
|
68
|
+
});
|
|
69
|
+
const dividerStyles = {
|
|
70
|
+
fontFamily: "'PT Sans', sans-serif",
|
|
71
|
+
fontSize: "14px",
|
|
72
|
+
color: "#595959",
|
|
73
|
+
lineHeight: "normal",
|
|
74
|
+
whiteSpace: "nowrap"
|
|
75
|
+
};
|
|
76
|
+
const ellipsisButtonStyles = {
|
|
77
|
+
display: "flex",
|
|
78
|
+
alignItems: "center",
|
|
79
|
+
justifyContent: "center",
|
|
80
|
+
width: "24px",
|
|
81
|
+
height: "24px",
|
|
82
|
+
borderRadius: "99px",
|
|
83
|
+
border: "none",
|
|
84
|
+
background: "transparent",
|
|
85
|
+
cursor: "pointer",
|
|
86
|
+
backgroundColor: ellipsisFocused ? "rgba(255, 255, 255, 0.01)" : ellipsisHovered ? "#efefef" : "transparent",
|
|
87
|
+
boxShadow: ellipsisFocused ? "0px 0px 0px 3px #3cad51" : "none"
|
|
88
|
+
};
|
|
89
|
+
const copyButtonStyles = {
|
|
90
|
+
display: "flex",
|
|
91
|
+
alignItems: "center",
|
|
92
|
+
justifyContent: "center",
|
|
93
|
+
width: "24px",
|
|
94
|
+
height: "24px",
|
|
95
|
+
borderRadius: "99px",
|
|
96
|
+
border: "none",
|
|
97
|
+
background: showCopyTooltip ? "#efefef" : "transparent",
|
|
98
|
+
cursor: "pointer",
|
|
99
|
+
position: "relative"
|
|
100
|
+
};
|
|
101
|
+
const tooltipStyles = {
|
|
102
|
+
position: "absolute",
|
|
103
|
+
top: "100%",
|
|
104
|
+
left: "50%",
|
|
105
|
+
transform: "translateX(-50%)",
|
|
106
|
+
marginTop: "8px",
|
|
107
|
+
backgroundColor: "#2f2f2f",
|
|
108
|
+
color: "white",
|
|
109
|
+
padding: "12px",
|
|
110
|
+
borderRadius: "8px",
|
|
111
|
+
fontSize: "13px",
|
|
112
|
+
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
|
113
|
+
fontWeight: 400,
|
|
114
|
+
lineHeight: "1.5",
|
|
115
|
+
whiteSpace: "nowrap",
|
|
116
|
+
boxShadow: "0px 4px 12px rgba(32, 32, 32, 0.08)",
|
|
117
|
+
zIndex: 1e3
|
|
118
|
+
};
|
|
119
|
+
return /* @__PURE__ */ jsx(
|
|
120
|
+
"div",
|
|
121
|
+
{
|
|
122
|
+
ref,
|
|
123
|
+
className,
|
|
124
|
+
style: containerStyles,
|
|
125
|
+
"data-testid": dataTestId,
|
|
126
|
+
children: displayItems.map((item, index) => {
|
|
127
|
+
const isActive = index === displayItems.length - 1;
|
|
128
|
+
const isEllipsis = "isEllipsis" in item && item.isEllipsis;
|
|
129
|
+
if (isEllipsis) {
|
|
130
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
131
|
+
/* @__PURE__ */ jsx(
|
|
132
|
+
"button",
|
|
133
|
+
{
|
|
134
|
+
style: ellipsisButtonStyles,
|
|
135
|
+
onFocus: () => setEllipsisFocused(true),
|
|
136
|
+
onBlur: () => setEllipsisFocused(false),
|
|
137
|
+
onMouseEnter: () => setEllipsisHovered(true),
|
|
138
|
+
onMouseLeave: () => setEllipsisHovered(false),
|
|
139
|
+
"aria-label": "More breadcrumbs",
|
|
140
|
+
children: /* @__PURE__ */ jsx(
|
|
141
|
+
MoreHorizontal,
|
|
142
|
+
{
|
|
143
|
+
size: 12,
|
|
144
|
+
color: "#2f2f2f",
|
|
145
|
+
strokeWidth: 2,
|
|
146
|
+
style: { display: "block", flexShrink: 0 }
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
),
|
|
151
|
+
/* @__PURE__ */ jsx("span", { style: dividerStyles, children: " /" })
|
|
152
|
+
] }, `ellipsis-${index}`);
|
|
153
|
+
}
|
|
154
|
+
const breadcrumbItem = item;
|
|
155
|
+
if (isActive) {
|
|
156
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
157
|
+
/* @__PURE__ */ jsx(
|
|
158
|
+
"div",
|
|
159
|
+
{
|
|
160
|
+
style: breadcrumbItemStyles(index, true),
|
|
161
|
+
onMouseEnter: () => setHoveredIndex(index),
|
|
162
|
+
onMouseLeave: () => setHoveredIndex(null),
|
|
163
|
+
onFocus: () => setFocusedIndex(index),
|
|
164
|
+
onBlur: () => setFocusedIndex(null),
|
|
165
|
+
tabIndex: 0,
|
|
166
|
+
children: /* @__PURE__ */ jsx("span", { style: linkStyles(true, hoveredIndex === index), children: breadcrumbItem.label })
|
|
167
|
+
}
|
|
168
|
+
),
|
|
169
|
+
onCopy && /* @__PURE__ */ jsxs(
|
|
170
|
+
"button",
|
|
171
|
+
{
|
|
172
|
+
style: copyButtonStyles,
|
|
173
|
+
onClick: handleCopy,
|
|
174
|
+
onMouseEnter: () => setShowCopyTooltip(true),
|
|
175
|
+
onMouseLeave: () => setShowCopyTooltip(false),
|
|
176
|
+
"aria-label": "Copy breadcrumb trail",
|
|
177
|
+
children: [
|
|
178
|
+
/* @__PURE__ */ jsx(
|
|
179
|
+
Link,
|
|
180
|
+
{
|
|
181
|
+
size: 12,
|
|
182
|
+
color: "#2f2f2f",
|
|
183
|
+
strokeWidth: 2,
|
|
184
|
+
style: { display: "block", flexShrink: 0 }
|
|
185
|
+
}
|
|
186
|
+
),
|
|
187
|
+
showCopyTooltip && /* @__PURE__ */ jsx("div", { style: tooltipStyles, children: "Copy breadcrumb trail" })
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
)
|
|
191
|
+
] }, index);
|
|
192
|
+
}
|
|
193
|
+
const Element = breadcrumbItem.href ? "a" : "button";
|
|
194
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
195
|
+
/* @__PURE__ */ jsxs(
|
|
196
|
+
Element,
|
|
197
|
+
{
|
|
198
|
+
...breadcrumbItem.href ? { href: breadcrumbItem.href } : {},
|
|
199
|
+
style: {
|
|
200
|
+
...breadcrumbItemStyles(index, false),
|
|
201
|
+
border: "none",
|
|
202
|
+
background: "transparent"
|
|
203
|
+
},
|
|
204
|
+
onClick: (e) => {
|
|
205
|
+
if (!breadcrumbItem.href && breadcrumbItem.onClick) {
|
|
206
|
+
e.preventDefault();
|
|
207
|
+
breadcrumbItem.onClick();
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
onMouseEnter: () => setHoveredIndex(index),
|
|
211
|
+
onMouseLeave: () => setHoveredIndex(null),
|
|
212
|
+
onFocus: () => setFocusedIndex(index),
|
|
213
|
+
onBlur: () => setFocusedIndex(null),
|
|
214
|
+
children: [
|
|
215
|
+
/* @__PURE__ */ jsx("span", { style: linkStyles(false, hoveredIndex === index), children: breadcrumbItem.label }),
|
|
216
|
+
breadcrumbItem.hasDropdown && /* @__PURE__ */ jsx(
|
|
217
|
+
ChevronDown,
|
|
218
|
+
{
|
|
219
|
+
size: 12,
|
|
220
|
+
color: hoveredIndex === index ? "#0e8a0e" : "#595959",
|
|
221
|
+
strokeWidth: 2,
|
|
222
|
+
style: { marginLeft: "2px", display: "inline-block", flexShrink: 0 }
|
|
223
|
+
}
|
|
224
|
+
)
|
|
225
|
+
]
|
|
226
|
+
}
|
|
227
|
+
),
|
|
228
|
+
/* @__PURE__ */ jsx("span", { style: dividerStyles, children: " /" })
|
|
229
|
+
] }, index);
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
Breadcrumbs.displayName = "Breadcrumbs";
|
|
236
|
+
|
|
237
|
+
export {
|
|
238
|
+
Breadcrumbs
|
|
239
|
+
};
|
|
240
|
+
//# sourceMappingURL=chunk-RQP6ZGD7.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/Breadcrumbs/Breadcrumbs.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Link, ChevronDown, MoreHorizontal } from 'lucide-react';\n\nexport interface BreadcrumbItem {\n /**\n * The label to display for this breadcrumb\n */\n label: string;\n /**\n * Optional href for the breadcrumb link\n */\n href?: string;\n /**\n * Optional click handler\n */\n onClick?: () => void;\n /**\n * Whether this breadcrumb has a dropdown menu\n */\n hasDropdown?: boolean;\n}\n\nexport interface BreadcrumbsProps {\n /**\n * Array of breadcrumb items\n */\n items: BreadcrumbItem[];\n /**\n * Callback when copy button is clicked\n */\n onCopy?: () => void;\n /**\n * Custom className\n */\n className?: string;\n /**\n * Custom style\n */\n style?: React.CSSProperties;\n /**\n * Test ID for testing\n */\n 'data-testid'?: string;\n}\n\n/**\n * Breadcrumbs component - Arbor Design System\n *\n * Navigation component showing the current page's location in the site hierarchy.\n * Automatically truncates to show first and last breadcrumb with ellipsis when more than 6 items.\n */\nexport const Breadcrumbs = React.forwardRef<HTMLDivElement, BreadcrumbsProps>(\n (\n {\n items,\n onCopy,\n className,\n style,\n 'data-testid': dataTestId,\n },\n ref\n ) => {\n const [hoveredIndex, setHoveredIndex] = React.useState<number | null>(null);\n const [focusedIndex, setFocusedIndex] = React.useState<number | null>(null);\n const [showCopyTooltip, setShowCopyTooltip] = React.useState(false);\n const [ellipsisFocused, setEllipsisFocused] = React.useState(false);\n const [ellipsisHovered, setEllipsisHovered] = React.useState(false);\n\n // Truncate breadcrumbs if more than 6 items\n const displayItems = React.useMemo(() => {\n if (items.length > 6) {\n // Show first item, ellipsis, and last item\n return [items[0], { label: '...', isEllipsis: true }, items[items.length - 1]];\n }\n return items;\n }, [items]);\n\n const handleCopy = () => {\n onCopy?.();\n // Copy the breadcrumb trail to clipboard\n const trail = items.map(item => item.label).join(' / ');\n navigator.clipboard.writeText(trail);\n };\n\n const containerStyles: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n ...style,\n };\n\n const breadcrumbItemStyles = (index: number, isActive: boolean, isEllipsis?: boolean): React.CSSProperties => {\n const isFocused = focusedIndex === index;\n\n if (isEllipsis) {\n return {\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n height: '24px',\n overflow: 'hidden',\n };\n }\n\n return {\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n height: '24px',\n overflow: 'hidden',\n backgroundColor: isFocused ? 'rgba(255, 255, 255, 0.01)' : 'transparent',\n borderRadius: '99px',\n boxShadow: isFocused ? '0px 0px 0px 3px #3cad51' : 'none',\n padding: isFocused ? '0 6px' : '0 2px',\n margin: isFocused ? '0' : '0 4px',\n cursor: isActive ? 'default' : 'pointer',\n textDecoration: 'none',\n };\n };\n\n const linkStyles = (isActive: boolean, isHovered: boolean): React.CSSProperties => ({\n fontFamily: isActive\n ? \"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\"\n : \"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n fontSize: '13px',\n fontWeight: isActive ? 600 : 400,\n color: isActive\n ? isHovered\n ? '#0e8a0e'\n : '#2f2f2f'\n : isHovered\n ? '#0e8a0e'\n : '#595959',\n lineHeight: '1.5',\n whiteSpace: 'nowrap',\n });\n\n const dividerStyles: React.CSSProperties = {\n fontFamily: \"'PT Sans', sans-serif\",\n fontSize: '14px',\n color: '#595959',\n lineHeight: 'normal',\n whiteSpace: 'nowrap',\n };\n\n const ellipsisButtonStyles: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '24px',\n height: '24px',\n borderRadius: '99px',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n backgroundColor: ellipsisFocused ? 'rgba(255, 255, 255, 0.01)' : ellipsisHovered ? '#efefef' : 'transparent',\n boxShadow: ellipsisFocused ? '0px 0px 0px 3px #3cad51' : 'none',\n };\n\n const copyButtonStyles: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '24px',\n height: '24px',\n borderRadius: '99px',\n border: 'none',\n background: showCopyTooltip ? '#efefef' : 'transparent',\n cursor: 'pointer',\n position: 'relative',\n };\n\n const tooltipStyles: React.CSSProperties = {\n position: 'absolute',\n top: '100%',\n left: '50%',\n transform: 'translateX(-50%)',\n marginTop: '8px',\n backgroundColor: '#2f2f2f',\n color: 'white',\n padding: '12px',\n borderRadius: '8px',\n fontSize: '13px',\n fontFamily: \"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n fontWeight: 400,\n lineHeight: '1.5',\n whiteSpace: 'nowrap',\n boxShadow: '0px 4px 12px rgba(32, 32, 32, 0.08)',\n zIndex: 1000,\n };\n\n return (\n <div\n ref={ref}\n className={className}\n style={containerStyles}\n data-testid={dataTestId}\n >\n {displayItems.map((item, index) => {\n const isActive = index === displayItems.length - 1;\n const isEllipsis = 'isEllipsis' in item && item.isEllipsis;\n\n if (isEllipsis) {\n return (\n <React.Fragment key={`ellipsis-${index}`}>\n <button\n style={ellipsisButtonStyles}\n onFocus={() => setEllipsisFocused(true)}\n onBlur={() => setEllipsisFocused(false)}\n onMouseEnter={() => setEllipsisHovered(true)}\n onMouseLeave={() => setEllipsisHovered(false)}\n aria-label=\"More breadcrumbs\"\n >\n <MoreHorizontal\n size={12}\n color=\"#2f2f2f\"\n strokeWidth={2}\n style={{ display: 'block', flexShrink: 0 }}\n />\n </button>\n <span style={dividerStyles}> /</span>\n </React.Fragment>\n );\n }\n\n const breadcrumbItem = item as BreadcrumbItem;\n\n if (isActive) {\n // Active breadcrumb (last item) - not clickable\n return (\n <React.Fragment key={index}>\n <div\n style={breadcrumbItemStyles(index, true)}\n onMouseEnter={() => setHoveredIndex(index)}\n onMouseLeave={() => setHoveredIndex(null)}\n onFocus={() => setFocusedIndex(index)}\n onBlur={() => setFocusedIndex(null)}\n tabIndex={0}\n >\n <span style={linkStyles(true, hoveredIndex === index)}>\n {breadcrumbItem.label}\n </span>\n </div>\n {/* Copy button - appears immediately after active breadcrumb */}\n {onCopy && (\n <button\n style={copyButtonStyles}\n onClick={handleCopy}\n onMouseEnter={() => setShowCopyTooltip(true)}\n onMouseLeave={() => setShowCopyTooltip(false)}\n aria-label=\"Copy breadcrumb trail\"\n >\n <Link\n size={12}\n color=\"#2f2f2f\"\n strokeWidth={2}\n style={{ display: 'block', flexShrink: 0 }}\n />\n {showCopyTooltip && (\n <div style={tooltipStyles}>Copy breadcrumb trail</div>\n )}\n </button>\n )}\n </React.Fragment>\n );\n }\n\n // Regular breadcrumb link\n const Element = breadcrumbItem.href ? 'a' : 'button';\n return (\n <React.Fragment key={index}>\n <Element\n {...(breadcrumbItem.href ? { href: breadcrumbItem.href } : {})}\n style={{\n ...breadcrumbItemStyles(index, false),\n border: 'none',\n background: 'transparent',\n }}\n onClick={(e) => {\n if (!breadcrumbItem.href && breadcrumbItem.onClick) {\n e.preventDefault();\n breadcrumbItem.onClick();\n }\n }}\n onMouseEnter={() => setHoveredIndex(index)}\n onMouseLeave={() => setHoveredIndex(null)}\n onFocus={() => setFocusedIndex(index)}\n onBlur={() => setFocusedIndex(null)}\n >\n <span style={linkStyles(false, hoveredIndex === index)}>\n {breadcrumbItem.label}\n </span>\n {breadcrumbItem.hasDropdown && (\n <ChevronDown\n size={12}\n color={hoveredIndex === index ? '#0e8a0e' : '#595959'}\n strokeWidth={2}\n style={{ marginLeft: '2px', display: 'inline-block', flexShrink: 0 }}\n />\n )}\n </Element>\n <span style={dividerStyles}> /</span>\n </React.Fragment>\n );\n })}\n </div>\n );\n }\n);\n\nBreadcrumbs.displayName = 'Breadcrumbs';\n"],"mappings":";AAAA,YAAY,WAAW;AACvB,SAAS,MAAM,aAAa,sBAAsB;AA2MpC,SASI,KATJ;AAzJP,IAAM,cAAoB;AAAA,EAC/B,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,EACjB,GACA,QACG;AACH,UAAM,CAAC,cAAc,eAAe,IAAU,eAAwB,IAAI;AAC1E,UAAM,CAAC,cAAc,eAAe,IAAU,eAAwB,IAAI;AAC1E,UAAM,CAAC,iBAAiB,kBAAkB,IAAU,eAAS,KAAK;AAClE,UAAM,CAAC,iBAAiB,kBAAkB,IAAU,eAAS,KAAK;AAClE,UAAM,CAAC,iBAAiB,kBAAkB,IAAU,eAAS,KAAK;AAGlE,UAAM,eAAqB,cAAQ,MAAM;AACvC,UAAI,MAAM,SAAS,GAAG;AAEpB,eAAO,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,OAAO,YAAY,KAAK,GAAG,MAAM,MAAM,SAAS,CAAC,CAAC;AAAA,MAC/E;AACA,aAAO;AAAA,IACT,GAAG,CAAC,KAAK,CAAC;AAEV,UAAM,aAAa,MAAM;AACvB,eAAS;AAET,YAAM,QAAQ,MAAM,IAAI,UAAQ,KAAK,KAAK,EAAE,KAAK,KAAK;AACtD,gBAAU,UAAU,UAAU,KAAK;AAAA,IACrC;AAEA,UAAM,kBAAuC;AAAA,MAC3C,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,KAAK;AAAA,MACL,GAAG;AAAA,IACL;AAEA,UAAM,uBAAuB,CAAC,OAAe,UAAmB,eAA8C;AAC5G,YAAM,YAAY,iBAAiB;AAEnC,UAAI,YAAY;AACd,eAAO;AAAA,UACL,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,KAAK;AAAA,UACL,QAAQ;AAAA,UACR,UAAU;AAAA,QACZ;AAAA,MACF;AAEA,aAAO;AAAA,QACL,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,iBAAiB,YAAY,8BAA8B;AAAA,QAC3D,cAAc;AAAA,QACd,WAAW,YAAY,4BAA4B;AAAA,QACnD,SAAS,YAAY,UAAU;AAAA,QAC/B,QAAQ,YAAY,MAAM;AAAA,QAC1B,QAAQ,WAAW,YAAY;AAAA,QAC/B,gBAAgB;AAAA,MAClB;AAAA,IACF;AAEA,UAAM,aAAa,CAAC,UAAmB,eAA6C;AAAA,MAClF,YAAY,WACR,uEACA;AAAA,MACJ,UAAU;AAAA,MACV,YAAY,WAAW,MAAM;AAAA,MAC7B,OAAO,WACH,YACE,YACA,YACF,YACA,YACA;AAAA,MACJ,YAAY;AAAA,MACZ,YAAY;AAAA,IACd;AAEA,UAAM,gBAAqC;AAAA,MACzC,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,YAAY;AAAA,IACd;AAEA,UAAM,uBAA4C;AAAA,MAChD,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,iBAAiB,kBAAkB,8BAA8B,kBAAkB,YAAY;AAAA,MAC/F,WAAW,kBAAkB,4BAA4B;AAAA,IAC3D;AAEA,UAAM,mBAAwC;AAAA,MAC5C,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,QAAQ;AAAA,MACR,YAAY,kBAAkB,YAAY;AAAA,MAC1C,QAAQ;AAAA,MACR,UAAU;AAAA,IACZ;AAEA,UAAM,gBAAqC;AAAA,MACzC,UAAU;AAAA,MACV,KAAK;AAAA,MACL,MAAM;AAAA,MACN,WAAW;AAAA,MACX,WAAW;AAAA,MACX,iBAAiB;AAAA,MACjB,OAAO;AAAA,MACP,SAAS;AAAA,MACT,cAAc;AAAA,MACd,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,QAAQ;AAAA,IACV;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,OAAO;AAAA,QACP,eAAa;AAAA,QAEZ,uBAAa,IAAI,CAAC,MAAM,UAAU;AACjC,gBAAM,WAAW,UAAU,aAAa,SAAS;AACjD,gBAAM,aAAa,gBAAgB,QAAQ,KAAK;AAEhD,cAAI,YAAY;AACd,mBACE,qBAAO,gBAAN,EACC;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,kBACP,SAAS,MAAM,mBAAmB,IAAI;AAAA,kBACtC,QAAQ,MAAM,mBAAmB,KAAK;AAAA,kBACtC,cAAc,MAAM,mBAAmB,IAAI;AAAA,kBAC3C,cAAc,MAAM,mBAAmB,KAAK;AAAA,kBAC5C,cAAW;AAAA,kBAEX;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAM;AAAA,sBACN,OAAM;AAAA,sBACN,aAAa;AAAA,sBACb,OAAO,EAAE,SAAS,SAAS,YAAY,EAAE;AAAA;AAAA,kBAC3C;AAAA;AAAA,cACF;AAAA,cACA,oBAAC,UAAK,OAAO,eAAe,gBAAE;AAAA,iBAhBX,YAAY,KAAK,EAiBtC;AAAA,UAEJ;AAEA,gBAAM,iBAAiB;AAEvB,cAAI,UAAU;AAEZ,mBACE,qBAAO,gBAAN,EACC;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO,qBAAqB,OAAO,IAAI;AAAA,kBACvC,cAAc,MAAM,gBAAgB,KAAK;AAAA,kBACzC,cAAc,MAAM,gBAAgB,IAAI;AAAA,kBACxC,SAAS,MAAM,gBAAgB,KAAK;AAAA,kBACpC,QAAQ,MAAM,gBAAgB,IAAI;AAAA,kBAClC,UAAU;AAAA,kBAEV,8BAAC,UAAK,OAAO,WAAW,MAAM,iBAAiB,KAAK,GACjD,yBAAe,OAClB;AAAA;AAAA,cACF;AAAA,cAEC,UACC;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,kBACP,SAAS;AAAA,kBACT,cAAc,MAAM,mBAAmB,IAAI;AAAA,kBAC3C,cAAc,MAAM,mBAAmB,KAAK;AAAA,kBAC5C,cAAW;AAAA,kBAEX;AAAA;AAAA,sBAAC;AAAA;AAAA,wBACC,MAAM;AAAA,wBACN,OAAM;AAAA,wBACN,aAAa;AAAA,wBACb,OAAO,EAAE,SAAS,SAAS,YAAY,EAAE;AAAA;AAAA,oBAC3C;AAAA,oBACC,mBACC,oBAAC,SAAI,OAAO,eAAe,mCAAqB;AAAA;AAAA;AAAA,cAEpD;AAAA,iBA/BiB,KAiCrB;AAAA,UAEJ;AAGA,gBAAM,UAAU,eAAe,OAAO,MAAM;AAC5C,iBACE,qBAAO,gBAAN,EACC;AAAA;AAAA,cAAC;AAAA;AAAA,gBACE,GAAI,eAAe,OAAO,EAAE,MAAM,eAAe,KAAK,IAAI,CAAC;AAAA,gBAC5D,OAAO;AAAA,kBACL,GAAG,qBAAqB,OAAO,KAAK;AAAA,kBACpC,QAAQ;AAAA,kBACR,YAAY;AAAA,gBACd;AAAA,gBACA,SAAS,CAAC,MAAM;AACd,sBAAI,CAAC,eAAe,QAAQ,eAAe,SAAS;AAClD,sBAAE,eAAe;AACjB,mCAAe,QAAQ;AAAA,kBACzB;AAAA,gBACF;AAAA,gBACA,cAAc,MAAM,gBAAgB,KAAK;AAAA,gBACzC,cAAc,MAAM,gBAAgB,IAAI;AAAA,gBACxC,SAAS,MAAM,gBAAgB,KAAK;AAAA,gBACpC,QAAQ,MAAM,gBAAgB,IAAI;AAAA,gBAElC;AAAA,sCAAC,UAAK,OAAO,WAAW,OAAO,iBAAiB,KAAK,GAClD,yBAAe,OAClB;AAAA,kBACC,eAAe,eACd;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAM;AAAA,sBACN,OAAO,iBAAiB,QAAQ,YAAY;AAAA,sBAC5C,aAAa;AAAA,sBACb,OAAO,EAAE,YAAY,OAAO,SAAS,gBAAgB,YAAY,EAAE;AAAA;AAAA,kBACrE;AAAA;AAAA;AAAA,YAEJ;AAAA,YACA,oBAAC,UAAK,OAAO,eAAe,gBAAE;AAAA,eA/BX,KAgCrB;AAAA,QAEJ,CAAC;AAAA;AAAA,IACH;AAAA,EAEJ;AACF;AAEA,YAAY,cAAc;","names":[]}
|