cyberui-2045 2.3.0 → 2.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/AGENT.md +25 -23
- package/README.md +31 -28
- package/bin/init.js +11 -5
- package/bin/markers.js +18 -0
- package/bin/usage-content.js +136 -210
- package/dist/component-manifest.json +1454 -0
- package/dist/components/Badge.js +49 -0
- package/dist/components/Badge.js.map +1 -0
- package/dist/components/Button.js +62 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/Card.js +31 -0
- package/dist/components/Card.js.map +1 -0
- package/dist/components/Carousel.js +585 -0
- package/dist/components/Carousel.js.map +1 -0
- package/dist/components/Checkbox.js +92 -0
- package/dist/components/Checkbox.js.map +1 -0
- package/dist/components/CircularProgress.js +87 -0
- package/dist/components/CircularProgress.js.map +1 -0
- package/dist/components/Divider.js +24 -0
- package/dist/components/Divider.js.map +1 -0
- package/dist/components/GradientText.js +27 -0
- package/dist/components/GradientText.js.map +1 -0
- package/dist/components/Image.js +369 -0
- package/dist/components/Image.js.map +1 -0
- package/dist/components/Input.js +70 -0
- package/dist/components/Input.js.map +1 -0
- package/dist/components/LinearProgress.js +39 -0
- package/dist/components/LinearProgress.js.map +1 -0
- package/dist/components/Modal.d.ts +19 -0
- package/dist/components/Modal.js +211 -0
- package/dist/components/Modal.js.map +1 -0
- package/dist/components/Notification.js +88 -0
- package/dist/components/Notification.js.map +1 -0
- package/dist/components/SectionTitle.js +33 -0
- package/dist/components/SectionTitle.js.map +1 -0
- package/dist/components/SegmentedProgress.js +82 -0
- package/dist/components/SegmentedProgress.js.map +1 -0
- package/dist/components/Select.js +93 -0
- package/dist/components/Select.js.map +1 -0
- package/dist/components/Skeleton.js +106 -0
- package/dist/components/Skeleton.js.map +1 -0
- package/dist/components/Steps.js +88 -0
- package/dist/components/Steps.js.map +1 -0
- package/dist/components/TabNavigation.js +153 -0
- package/dist/components/TabNavigation.js.map +1 -0
- package/dist/components/Timeline.js +99 -0
- package/dist/components/Timeline.js.map +1 -0
- package/dist/components/Toggle.js +73 -0
- package/dist/components/Toggle.js.map +1 -0
- package/dist/contexts/NotificationContext.js +100 -0
- package/dist/contexts/NotificationContext.js.map +1 -0
- package/dist/contexts/NotificationContextBase.js +6 -0
- package/dist/contexts/NotificationContextBase.js.map +1 -0
- package/dist/hooks/useAnimatedProgress.js +17 -0
- package/dist/hooks/useAnimatedProgress.js.map +1 -0
- package/dist/hooks/useCyberNotifications.js +14 -0
- package/dist/hooks/useCyberNotifications.js.map +1 -0
- package/dist/hooks/useCyberScrollbar.js +254 -0
- package/dist/hooks/useCyberScrollbar.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +63 -5954
- package/dist/index.js +13 -13
- package/dist/utils/cn.js +9 -0
- package/dist/utils/cn.js.map +1 -0
- package/dist/utils/devWarn.d.ts +5 -0
- package/dist/utils/devWarn.js +8 -0
- package/dist/utils/devWarn.js.map +1 -0
- package/dist/utils/responsive.js +120 -0
- package/dist/utils/responsive.js.map +1 -0
- package/package.json +158 -125
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Select.js","sources":["../../src/components/Select.tsx"],"sourcesContent":["import React, { useId } from \"react\";\r\nimport type { ResponsiveValue } from \"../utils/responsive\";\r\nimport {\r\n getResponsiveClasses,\r\n RESPONSIVE_SIZE_MAPS,\r\n} from \"../utils/responsive\";\r\nimport { cn } from \"../utils/cn\";\r\n\r\n/**\r\n * Represents a single choice in the Select component.\r\n */\r\nexport interface SelectOption {\r\n /** Internal value of the option */\r\n value: string;\r\n /** Human-readable label displayed in the dropdown */\r\n label: string;\r\n /** Whether this specific option is disabled */\r\n disabled?: boolean;\r\n}\r\n\r\n/**\r\n * A styled dropdown selection component.\r\n *\r\n * @example\r\n * // Basic select\r\n * <Select\r\n * label=\"Choose Sector\"\r\n * options={[\r\n * { value: 'sector-7', label: 'Sector 7' },\r\n * { value: 'sector-9', label: 'Sector 9' }\r\n * ]}\r\n * />\r\n *\r\n * @example\r\n * // Select with error\r\n * <Select\r\n * label=\"Clearance Level\"\r\n * error=\"Insufficient permissions\"\r\n * options={levels}\r\n * />\r\n */\r\nexport interface SelectProps\r\n extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, \"size\"> {\r\n /** Label for the select field */\r\n label?: string;\r\n /**\r\n * Size of the select input.\r\n * @default 'md'\r\n */\r\n size?: ResponsiveValue<\"sm\" | \"md\" | \"lg\">;\r\n /**\r\n * Visual style variant.\r\n * @default 'primary'\r\n */\r\n variant?: \"primary\" | \"secondary\" | \"danger\" | \"ghost\";\r\n /** List of options to choose from */\r\n options: SelectOption[];\r\n /** Placeholder text shown when no option is selected */\r\n placeholder?: string;\r\n /** Optional helper text shown below the select */\r\n helperText?: string;\r\n /** Error message that highlights the select and overrides helper text */\r\n error?: string;\r\n /** Optional custom class name */\r\n className?: string;\r\n /**\r\n * Convenience callback that receives the selected string value directly.\r\n * Use instead of onChange when you only need the value (not the raw event).\r\n */\r\n onValueChange?: (value: string) => void;\r\n}\r\n\r\n/**\r\n * A futuristic Cyberpunk-themed dropdown select component with support for labels and error states.\r\n * \r\n * @example\r\n * <Select \r\n * label=\"Neural Port\" \r\n * options={[{ value: 'com1', label: 'COM 1' }, { value: 'com2', label: 'COM 2' }]} \r\n * variant=\"primary\" \r\n * />\r\n */\r\nconst Select: React.FC<SelectProps> = ({\r\n label,\r\n size = \"md\",\r\n variant = \"primary\",\r\n options,\r\n placeholder,\r\n helperText,\r\n error,\r\n className = \"\",\r\n disabled = false,\r\n id,\r\n name,\r\n onValueChange,\r\n onChange,\r\n ...props\r\n}) => {\r\n const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {\r\n onChange?.(e);\r\n onValueChange?.(e.target.value);\r\n };\r\n const generatedId = useId();\r\n const selectId = id || generatedId;\r\n const selectName = name || selectId;\r\n const getSizeClasses = (\r\n size: ResponsiveValue<\"sm\" | \"md\" | \"lg\">\r\n ): string => {\r\n return getResponsiveClasses(size, RESPONSIVE_SIZE_MAPS.input);\r\n };\r\n\r\n const getVariantClasses = (\r\n variant: string,\r\n disabled: boolean,\r\n hasError: boolean\r\n ): string => {\r\n if (disabled) {\r\n return \"bg-base border-2 border-border-default/30 text-muted/50 cursor-not-allowed opacity-50\";\r\n }\r\n\r\n if (hasError) {\r\n return \"bg-surface border-2 border-error text-default focus:ring-2 focus:ring-error focus:border-error shadow-error\";\r\n }\r\n\r\n const variants = {\r\n primary:\r\n \"bg-surface border-2 border-border-default text-default focus:ring-2 focus:ring-primary focus:border-primary shadow-primary\",\r\n secondary:\r\n \"bg-surface border-2 border-secondary text-default focus:ring-2 focus:ring-secondary focus:border-secondary shadow-secondary/30\",\r\n danger:\r\n \"bg-surface border-2 border-error text-default focus:ring-2 focus:ring-error focus:border-error shadow-error/30\",\r\n ghost:\r\n \"bg-base border-2 border-accent/20 text-default focus:ring-2 focus:ring-accent focus:border-accent shadow-secondary\",\r\n };\r\n\r\n return variants[variant as keyof typeof variants] || variants.primary;\r\n };\r\n\r\n const selectClasses = cn(\r\n \"appearance-none w-full rounded-lg font-mono transition-all duration-300 focus:outline-none px-4 pr-10\",\r\n getSizeClasses(size),\r\n getVariantClasses(variant, disabled, !!error),\r\n className\r\n );\r\n\r\n const containerClasses = \"relative w-full\";\r\n\r\n const labelClasses = cn(\r\n \"block text-sm font-medium mb-2 transition-colors duration-200\",\r\n disabled ? \"text-muted opacity-50\" : error ? \"text-error\" : \"text-default\"\r\n );\r\n\r\n const helperTextClasses = cn(\"mt-2 text-xs transition-colors duration-200\", error ? \"text-error\" : \"text-muted\");\r\n\r\n return (\r\n <div className=\"w-full\">\r\n {label && (\r\n <label htmlFor={selectId} className={labelClasses}>\r\n {label}\r\n </label>\r\n )}\r\n\r\n <div className={containerClasses}>\r\n <select\r\n className={selectClasses}\r\n disabled={disabled}\r\n id={selectId}\r\n name={selectName}\r\n onChange={handleChange}\r\n {...props}\r\n >\r\n {placeholder && (\r\n <option value=\"\" disabled>\r\n {placeholder}\r\n </option>\r\n )}\r\n {options.map((option) => (\r\n <option\r\n key={option.value}\r\n value={option.value}\r\n disabled={option.disabled}\r\n className=\"bg-surface text-default\"\r\n >\r\n {option.label}\r\n </option>\r\n ))}\r\n </select>\r\n\r\n {/* Dropdown arrow icon */}\r\n <div\r\n className={`pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 ${\r\n disabled ? \"text-muted/50\" : error ? \"text-error\" : \"text-accent\"\r\n }`}\r\n >\r\n <svg className=\"w-4 h-4\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\r\n <path\r\n fillRule=\"evenodd\"\r\n d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\r\n clipRule=\"evenodd\"\r\n />\r\n </svg>\r\n </div>\r\n </div>\r\n\r\n {(helperText || error) && (\r\n <div className={helperTextClasses}>{error || helperText}</div>\r\n )}\r\n </div>\r\n );\r\n};\r\n\r\nSelect.displayName = \"CyberUI.Select\";\r\n\r\nexport default Select;\r\n"],"names":["Select","label","size","variant","options","placeholder","helperText","error","className","disabled","id","name","onValueChange","onChange","props","handleChange","generatedId","useId","selectId","selectName","getSizeClasses","getResponsiveClasses","RESPONSIVE_SIZE_MAPS","getVariantClasses","hasError","variants","selectClasses","cn","containerClasses","labelClasses","helperTextClasses","jsxs","option","jsx"],"mappings":";;;;AAkFA,MAAMA,IAAgC,CAAC;AAAA,EACrC,OAAAC;AAAA,EACA,MAAAC,IAAO;AAAA,EACP,SAAAC,IAAU;AAAA,EACV,SAAAC;AAAA,EACA,aAAAC;AAAA,EACA,YAAAC;AAAA,EACA,OAAAC;AAAA,EACA,WAAAC,IAAY;AAAA,EACZ,UAAAC,IAAW;AAAA,EACX,IAAAC;AAAA,EACA,MAAAC;AAAA,EACA,eAAAC;AAAA,EACA,UAAAC;AAAA,EACA,GAAGC;AACL,MAAM;AACJ,QAAMC,IAAe,CAAC,MAA4C;AAChE,IAAAF,IAAW,CAAC,GACZD,IAAgB,EAAE,OAAO,KAAK;AAAA,EAChC,GACMI,IAAcC,EAAA,GACdC,IAAWR,KAAMM,GACjBG,IAAaR,KAAQO,GACrBE,IAAiB,CACrBlB,MAEOmB,EAAqBnB,GAAMoB,EAAqB,KAAK,GAGxDC,IAAoB,CACxBpB,GACAM,GACAe,MACW;AACX,QAAIf;AACF,aAAO;AAGT,QAAIe;AACF,aAAO;AAGT,UAAMC,IAAW;AAAA,MACf,SACE;AAAA,MACF,WACE;AAAA,MACF,QACE;AAAA,MACF,OACE;AAAA,IAAA;AAGJ,WAAOA,EAAStB,CAAgC,KAAKsB,EAAS;AAAA,EAChE,GAEMC,IAAgBC;AAAA,IACpB;AAAA,IACAP,EAAelB,CAAI;AAAA,IACnBqB,EAAkBpB,GAASM,GAAU,CAAC,CAACF,CAAK;AAAA,IAC5CC;AAAA,EAAA,GAGIoB,IAAmB,mBAEnBC,IAAeF;AAAA,IACnB;AAAA,IACAlB,IAAW,0BAA0BF,IAAQ,eAAe;AAAA,EAAA,GAGxDuB,IAAoBH,EAAG,+CAA+CpB,IAAQ,eAAe,YAAY;AAE/G,SACEwB,gBAAAA,EAAAA,KAAC,OAAA,EAAI,WAAU,UACZ,UAAA;AAAA,IAAA9B,2BACE,SAAA,EAAM,SAASiB,GAAU,WAAWW,GAClC,UAAA5B,GACH;AAAA,IAGF8B,gBAAAA,EAAAA,KAAC,OAAA,EAAI,WAAWH,GACd,UAAA;AAAA,MAAAG,gBAAAA,EAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAWL;AAAA,UACX,UAAAjB;AAAA,UACA,IAAIS;AAAA,UACJ,MAAMC;AAAA,UACN,UAAUJ;AAAA,UACT,GAAGD;AAAA,UAEH,UAAA;AAAA,YAAAT,2BACE,UAAA,EAAO,OAAM,IAAG,UAAQ,IACtB,UAAAA,GACH;AAAA,YAEDD,EAAQ,IAAI,CAAC4B,MACZC,gBAAAA,EAAAA;AAAAA,cAAC;AAAA,cAAA;AAAA,gBAEC,OAAOD,EAAO;AAAA,gBACd,UAAUA,EAAO;AAAA,gBACjB,WAAU;AAAA,gBAET,UAAAA,EAAO;AAAA,cAAA;AAAA,cALHA,EAAO;AAAA,YAAA,CAOf;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,MAIHC,gBAAAA,EAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAW,yEACTxB,IAAW,kBAAkBF,IAAQ,eAAe,aACtD;AAAA,UAEA,gCAAC,OAAA,EAAI,WAAU,WAAU,MAAK,gBAAe,SAAQ,aACnD,UAAA0B,gBAAAA,EAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,UAAS;AAAA,cACT,GAAE;AAAA,cACF,UAAS;AAAA,YAAA;AAAA,UAAA,EACX,CACF;AAAA,QAAA;AAAA,MAAA;AAAA,IACF,GACF;AAAA,KAEE3B,KAAcC,MACd0B,gBAAAA,EAAAA,IAAC,SAAI,WAAWH,GAAoB,eAASxB,EAAA,CAAW;AAAA,EAAA,GAE5D;AAEJ;AAEAN,EAAO,cAAc;"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { j as s } from "../_virtual/jsx-runtime.js";
|
|
2
|
+
import "react";
|
|
3
|
+
import { getResponsiveClasses as x, RESPONSIVE_SIZE_MAPS as h } from "../utils/responsive.js";
|
|
4
|
+
import { cn as p } from "../utils/cn.js";
|
|
5
|
+
const f = ({
|
|
6
|
+
variant: a = "text",
|
|
7
|
+
size: d = "md",
|
|
8
|
+
width: t,
|
|
9
|
+
height: c,
|
|
10
|
+
className: r = "",
|
|
11
|
+
lines: u = 3,
|
|
12
|
+
animate: i = !0
|
|
13
|
+
}) => {
|
|
14
|
+
const e = p("bg-gray-600", i && "animate-pulse", ((l) => x(l, h.skeleton))(d), r), n = () => {
|
|
15
|
+
const l = {};
|
|
16
|
+
return t && (l.width = typeof t == "number" ? `${t}px` : t), c && (l.height = typeof c == "number" ? `${c}px` : c), l;
|
|
17
|
+
};
|
|
18
|
+
if (a === "text")
|
|
19
|
+
return /* @__PURE__ */ s.jsx("div", { className: `space-y-3 ${r}`, children: Array.from({ length: u }).map((l, o) => {
|
|
20
|
+
const m = o === u - 1 ? "w-2/3" : o % 2 === 0 ? "w-full" : "w-5/6";
|
|
21
|
+
return /* @__PURE__ */ s.jsx(
|
|
22
|
+
"div",
|
|
23
|
+
{
|
|
24
|
+
className: `h-3 rounded ${e} ${m}`,
|
|
25
|
+
style: n()
|
|
26
|
+
},
|
|
27
|
+
o
|
|
28
|
+
);
|
|
29
|
+
}) });
|
|
30
|
+
if (a === "circular") {
|
|
31
|
+
const l = d === "sm" ? "h-12 w-12" : d === "lg" ? "h-20 w-20" : "h-16 w-16";
|
|
32
|
+
return /* @__PURE__ */ s.jsx(
|
|
33
|
+
"div",
|
|
34
|
+
{
|
|
35
|
+
className: `rounded-full ${e} ${l}`,
|
|
36
|
+
style: n()
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
if (a === "rectangular") {
|
|
41
|
+
const l = d === "sm" ? "h-20" : d === "lg" ? "h-40" : "h-32";
|
|
42
|
+
return /* @__PURE__ */ s.jsx(
|
|
43
|
+
"div",
|
|
44
|
+
{
|
|
45
|
+
className: `rounded-lg ${e} w-full ${l}`,
|
|
46
|
+
style: n()
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (a === "avatar-text") {
|
|
51
|
+
const l = d === "sm" ? "h-12 w-12" : d === "lg" ? "h-20 w-20" : "h-16 w-16";
|
|
52
|
+
return /* @__PURE__ */ s.jsxs("div", { className: `flex items-center space-x-4 ${r}`, children: [
|
|
53
|
+
/* @__PURE__ */ s.jsx("div", { className: `rounded-full ${e} ${l}` }),
|
|
54
|
+
/* @__PURE__ */ s.jsxs("div", { className: "flex-1 space-y-3", children: [
|
|
55
|
+
/* @__PURE__ */ s.jsx("div", { className: `h-4 rounded w-3/4 ${e}` }),
|
|
56
|
+
/* @__PURE__ */ s.jsx("div", { className: `h-3 rounded w-1/2 ${e}` })
|
|
57
|
+
] })
|
|
58
|
+
] });
|
|
59
|
+
}
|
|
60
|
+
if (a === "button-group") {
|
|
61
|
+
const l = d === "sm" ? "h-8" : d === "lg" ? "h-12" : "h-10";
|
|
62
|
+
return /* @__PURE__ */ s.jsxs("div", { className: `flex space-x-4 ${r}`, children: [
|
|
63
|
+
/* @__PURE__ */ s.jsx("div", { className: `${l} rounded w-20 ${e}` }),
|
|
64
|
+
/* @__PURE__ */ s.jsx("div", { className: `${l} rounded w-24 ${e}` })
|
|
65
|
+
] });
|
|
66
|
+
}
|
|
67
|
+
if (a === "card") {
|
|
68
|
+
const l = d === "sm" ? "p-4" : d === "lg" ? "p-8" : "p-6";
|
|
69
|
+
return /* @__PURE__ */ s.jsx(
|
|
70
|
+
"div",
|
|
71
|
+
{
|
|
72
|
+
className: `border border-border-default rounded-lg bg-surface ${l} space-y-4 ${r}`,
|
|
73
|
+
children: i && /* @__PURE__ */ s.jsxs("div", { className: "animate-pulse space-y-4", children: [
|
|
74
|
+
/* @__PURE__ */ s.jsxs("div", { className: "flex items-center space-x-4", children: [
|
|
75
|
+
/* @__PURE__ */ s.jsx("div", { className: `rounded-full ${e} h-16 w-16` }),
|
|
76
|
+
/* @__PURE__ */ s.jsxs("div", { className: "flex-1 space-y-3", children: [
|
|
77
|
+
/* @__PURE__ */ s.jsx("div", { className: `h-4 rounded w-3/4 ${e}` }),
|
|
78
|
+
/* @__PURE__ */ s.jsx("div", { className: `h-3 rounded w-1/2 ${e}` })
|
|
79
|
+
] })
|
|
80
|
+
] }),
|
|
81
|
+
/* @__PURE__ */ s.jsxs("div", { className: "space-y-3", children: [
|
|
82
|
+
/* @__PURE__ */ s.jsx("div", { className: `h-3 rounded ${e}` }),
|
|
83
|
+
/* @__PURE__ */ s.jsx("div", { className: `h-3 rounded w-5/6 ${e}` }),
|
|
84
|
+
/* @__PURE__ */ s.jsx("div", { className: `h-3 rounded w-4/6 ${e}` })
|
|
85
|
+
] }),
|
|
86
|
+
/* @__PURE__ */ s.jsxs("div", { className: "flex space-x-4 pt-4", children: [
|
|
87
|
+
/* @__PURE__ */ s.jsx("div", { className: `h-8 rounded w-20 ${e}` }),
|
|
88
|
+
/* @__PURE__ */ s.jsx("div", { className: `h-8 rounded w-24 ${e}` })
|
|
89
|
+
] })
|
|
90
|
+
] })
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return /* @__PURE__ */ s.jsx(
|
|
95
|
+
"div",
|
|
96
|
+
{
|
|
97
|
+
className: `h-4 rounded ${e} w-full`,
|
|
98
|
+
style: n()
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
f.displayName = "CyberUI.Skeleton";
|
|
103
|
+
export {
|
|
104
|
+
f as default
|
|
105
|
+
};
|
|
106
|
+
//# sourceMappingURL=Skeleton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Skeleton.js","sources":["../../src/components/Skeleton.tsx"],"sourcesContent":["import React from \"react\";\r\nimport type { ResponsiveValue } from \"../utils/responsive\";\r\nimport {\r\n getResponsiveClasses,\r\n RESPONSIVE_SIZE_MAPS,\r\n} from \"../utils/responsive\";\r\nimport { cn } from \"../utils/cn\";\r\n\r\n/**\r\n * A placeholder component for loading states.\r\n *\r\n * @example\r\n * // Text skeleton\r\n * <Skeleton variant=\"text\" lines={3} />\r\n *\r\n * @example\r\n * // Avatar and text combination\r\n * <Skeleton variant=\"avatar-text\" size=\"lg\" />\r\n *\r\n * @example\r\n * // Custom rectangular skeleton\r\n * <Skeleton\r\n * variant=\"rectangular\"\r\n * width={200}\r\n * height={150}\r\n * animate={false}\r\n * />\r\n */\r\nexport interface SkeletonProps {\r\n /**\r\n * Shape variant of the skeleton.\r\n * @default 'text'\r\n */\r\n variant?:\r\n | \"text\"\r\n | \"circular\"\r\n | \"rectangular\"\r\n | \"card\"\r\n | \"avatar-text\"\r\n | \"button-group\";\r\n /**\r\n * Size of the skeleton element.\r\n * @default 'md'\r\n */\r\n size?: ResponsiveValue<\"sm\" | \"md\" | \"lg\">;\r\n /** Custom width for the skeleton */\r\n width?: string | number;\r\n /** Custom height for the skeleton */\r\n height?: string | number;\r\n /** Optional custom class name */\r\n className?: string;\r\n /**\r\n * Number of lines for text variant.\r\n * @default 3\r\n */\r\n lines?: number; // For text variant\r\n /**\r\n * Whether to show the pulse animation.\r\n * @default true\r\n */\r\n animate?: boolean;\r\n}\r\n\r\n/**\r\n * A versatile skeleton loading indicator with multiple layout variants and pulse animation.\r\n * \r\n * @example\r\n * <Skeleton variant=\"card\" size=\"lg\" />\r\n * \r\n * @example\r\n * <Skeleton variant=\"text\" lines={5} width=\"80%\" />\r\n */\r\nconst Skeleton: React.FC<SkeletonProps> = ({\r\n variant = \"text\",\r\n size = \"md\",\r\n width,\r\n height,\r\n className = \"\",\r\n lines = 3,\r\n animate = true,\r\n}) => {\r\n const getSizeClasses = (\r\n size: ResponsiveValue<\"sm\" | \"md\" | \"lg\">\r\n ): string => {\r\n return getResponsiveClasses(size, RESPONSIVE_SIZE_MAPS.skeleton);\r\n };\r\n\r\n const baseClasses = cn('bg-gray-600', animate && 'animate-pulse', getSizeClasses(size), className);\r\n\r\n const getCustomStyles = () => {\r\n const styles: React.CSSProperties = {};\r\n if (width) styles.width = typeof width === \"number\" ? `${width}px` : width;\r\n if (height)\r\n styles.height = typeof height === \"number\" ? `${height}px` : height;\r\n return styles;\r\n };\r\n\r\n // Text skeleton with multiple lines\r\n if (variant === \"text\") {\r\n return (\r\n <div className={`space-y-3 ${className}`}>\r\n {Array.from({ length: lines }).map((_, index) => {\r\n const isLastLine = index === lines - 1;\r\n const widthClass = isLastLine\r\n ? \"w-2/3\"\r\n : index % 2 === 0\r\n ? \"w-full\"\r\n : \"w-5/6\";\r\n\r\n return (\r\n <div\r\n key={index}\r\n className={`h-3 rounded ${baseClasses} ${widthClass}`}\r\n style={getCustomStyles()}\r\n />\r\n );\r\n })}\r\n </div>\r\n );\r\n }\r\n\r\n // Circular skeleton (avatars, profile pics)\r\n if (variant === \"circular\") {\r\n const sizeClass =\r\n size === \"sm\" ? \"h-12 w-12\" : size === \"lg\" ? \"h-20 w-20\" : \"h-16 w-16\";\r\n\r\n return (\r\n <div\r\n className={`rounded-full ${baseClasses} ${sizeClass}`}\r\n style={getCustomStyles()}\r\n />\r\n );\r\n }\r\n\r\n // Rectangular skeleton (images, cards)\r\n if (variant === \"rectangular\") {\r\n const defaultHeight =\r\n size === \"sm\" ? \"h-20\" : size === \"lg\" ? \"h-40\" : \"h-32\";\r\n\r\n return (\r\n <div\r\n className={`rounded-lg ${baseClasses} w-full ${defaultHeight}`}\r\n style={getCustomStyles()}\r\n />\r\n );\r\n }\r\n\r\n // Avatar + text combination (profile cards)\r\n if (variant === \"avatar-text\") {\r\n const avatarSize =\r\n size === \"sm\" ? \"h-12 w-12\" : size === \"lg\" ? \"h-20 w-20\" : \"h-16 w-16\";\r\n\r\n return (\r\n <div className={`flex items-center space-x-4 ${className}`}>\r\n <div className={`rounded-full ${baseClasses} ${avatarSize}`} />\r\n <div className=\"flex-1 space-y-3\">\r\n <div className={`h-4 rounded w-3/4 ${baseClasses}`} />\r\n <div className={`h-3 rounded w-1/2 ${baseClasses}`} />\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n // Button group skeleton\r\n if (variant === \"button-group\") {\r\n const buttonHeight =\r\n size === \"sm\" ? \"h-8\" : size === \"lg\" ? \"h-12\" : \"h-10\";\r\n\r\n return (\r\n <div className={`flex space-x-4 ${className}`}>\r\n <div className={`${buttonHeight} rounded w-20 ${baseClasses}`} />\r\n <div className={`${buttonHeight} rounded w-24 ${baseClasses}`} />\r\n </div>\r\n );\r\n }\r\n\r\n // Card skeleton (complex layout)\r\n if (variant === \"card\") {\r\n const paddingClass = size === \"sm\" ? \"p-4\" : size === \"lg\" ? \"p-8\" : \"p-6\";\r\n\r\n return (\r\n <div\r\n className={`border border-border-default rounded-lg bg-surface ${paddingClass} space-y-4 ${className}`}\r\n >\r\n {animate && (\r\n <div className=\"animate-pulse space-y-4\">\r\n {/* Header with avatar */}\r\n <div className=\"flex items-center space-x-4\">\r\n <div className={`rounded-full ${baseClasses} h-16 w-16`} />\r\n <div className=\"flex-1 space-y-3\">\r\n <div className={`h-4 rounded w-3/4 ${baseClasses}`} />\r\n <div className={`h-3 rounded w-1/2 ${baseClasses}`} />\r\n </div>\r\n </div>\r\n\r\n {/* Content lines */}\r\n <div className=\"space-y-3\">\r\n <div className={`h-3 rounded ${baseClasses}`} />\r\n <div className={`h-3 rounded w-5/6 ${baseClasses}`} />\r\n <div className={`h-3 rounded w-4/6 ${baseClasses}`} />\r\n </div>\r\n\r\n {/* Action buttons */}\r\n <div className=\"flex space-x-4 pt-4\">\r\n <div className={`h-8 rounded w-20 ${baseClasses}`} />\r\n <div className={`h-8 rounded w-24 ${baseClasses}`} />\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n );\r\n }\r\n\r\n // Default rectangular skeleton\r\n return (\r\n <div\r\n className={`h-4 rounded ${baseClasses} w-full`}\r\n style={getCustomStyles()}\r\n />\r\n );\r\n};\r\n\r\nSkeleton.displayName = \"CyberUI.Skeleton\";\r\n\r\nexport default Skeleton;\r\n"],"names":["Skeleton","variant","size","width","height","className","lines","animate","baseClasses","cn","getResponsiveClasses","RESPONSIVE_SIZE_MAPS","getCustomStyles","styles","_","index","widthClass","jsx","sizeClass","defaultHeight","avatarSize","jsxs","buttonHeight","paddingClass"],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { j as o } from "../_virtual/jsx-runtime.js";
|
|
2
|
+
import j from "react";
|
|
3
|
+
import { useResponsiveValue as w } from "../utils/responsive.js";
|
|
4
|
+
import { cn as n } from "../utils/cn.js";
|
|
5
|
+
import { warnOnce as N } from "../utils/devWarn.js";
|
|
6
|
+
const C = ({
|
|
7
|
+
items: r,
|
|
8
|
+
current: l = 0,
|
|
9
|
+
orientation: h = { base: "vertical", md: "horizontal" },
|
|
10
|
+
className: d = ""
|
|
11
|
+
}) => {
|
|
12
|
+
const f = w(h, "vertical") === "vertical";
|
|
13
|
+
if (r.length > 0 && (l < 0 || l >= r.length)) {
|
|
14
|
+
const t = l === r.length ? " Did you mean 1-based indexing? Steps is 0-based — subtract 1." : "";
|
|
15
|
+
N(
|
|
16
|
+
`steps-oob-${l}-${r.length}`,
|
|
17
|
+
`Steps: current={${l}} is out of range for ${r.length} step(s) (valid: 0-${r.length - 1}).${t} No step will render as "current".`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
const b = (t, e) => {
|
|
21
|
+
const s = t.status === "completed" || t.status === void 0 && e < l, a = t.status === "current" || t.status === void 0 && e === l && l < r.length, i = t.status === "error";
|
|
22
|
+
return { isCompleted: s, isCurrent: a, isError: i };
|
|
23
|
+
}, g = (t, e, s) => {
|
|
24
|
+
const a = n(
|
|
25
|
+
"relative px-2 py-1 pb-2 font-bold text-sm transition-colors duration-200 whitespace-nowrap overflow-hidden",
|
|
26
|
+
"after:content-[''] after:absolute after:left-0 after:right-0 after:bottom-0 after:h-[2px] after:w-full after:scale-x-0 after:origin-center after:transition-transform after:duration-300 after:ease-out",
|
|
27
|
+
"before:content-[''] before:absolute before:left-1/2 before:-translate-x-1/2 before:-bottom-[3px] before:w-1.5 before:h-1.5 before:bg-accent before:rotate-45 before:rounded-[2px] before:opacity-0 before:transition-opacity before:duration-200"
|
|
28
|
+
);
|
|
29
|
+
return t ? n(a, "text-secondary after:bg-linear-(--gradient-accent) after:shadow-lg-accent after:scale-x-100") : e ? n(a, "text-secondary before:opacity-100") : s ? n(a, "text-error") : n(a, "text-muted");
|
|
30
|
+
}, x = (t, e, s, a) => ({
|
|
31
|
+
color: "var(--color-primary)",
|
|
32
|
+
opacity: s ? 0.8 : 0.3,
|
|
33
|
+
filter: s ? "drop-shadow(0 0 4px color-mix(in srgb, var(--color-primary), transparent 40%))" : "none",
|
|
34
|
+
animation: e && t ? `chevronFlow 1.5s ease-in-out infinite ${a * 0.2}s` : "none"
|
|
35
|
+
}), m = f ? n("flex flex-col items-start gap-0", d) : n("flex items-center justify-center gap-4", d), v = (t, e, s) => t ? "text-secondary drop-shadow-[0_0_4px_var(--color-secondary)]" : e ? "text-accent drop-shadow-[0_0_6px_var(--color-accent)]" : s ? "text-error drop-shadow-[0_0_4px_var(--color-error)]" : "text-muted/50", y = (t, e, s) => {
|
|
36
|
+
const a = n(
|
|
37
|
+
"relative font-bold text-sm transition-colors duration-200 pb-1 w-fit",
|
|
38
|
+
"after:content-[''] after:absolute after:left-0 after:right-0 after:bottom-0 after:h-[2px] after:w-full after:scale-x-0 after:origin-left after:transition-transform after:duration-300 after:ease-out"
|
|
39
|
+
);
|
|
40
|
+
return t ? n(a, "text-secondary after:bg-linear-(--gradient-accent) after:shadow-lg-accent after:scale-x-100") : e ? n(a, "text-secondary") : s ? n(a, "text-error") : n(a, "text-muted");
|
|
41
|
+
};
|
|
42
|
+
return /* @__PURE__ */ o.jsx("div", { className: m, children: r.map((t, e) => {
|
|
43
|
+
const { isCompleted: s, isCurrent: a, isError: i } = b(t, e), p = e < r.length - 1 && (r[e + 1]?.status === "current" || r[e + 1]?.status === void 0 && e + 1 === l && l < r.length), u = e < r.length - 1 && (r[e + 1]?.status === "completed" || r[e + 1]?.status === void 0 && e + 1 < l);
|
|
44
|
+
return /* @__PURE__ */ o.jsxs(j.Fragment, { children: [
|
|
45
|
+
f ? (
|
|
46
|
+
// Vertical layout: step row + chevrons below
|
|
47
|
+
/* @__PURE__ */ o.jsxs("div", { className: "flex flex-col w-full", children: [
|
|
48
|
+
/* @__PURE__ */ o.jsxs("div", { className: "flex items-start gap-1.5", children: [
|
|
49
|
+
/* @__PURE__ */ o.jsx("span", { className: n("text-xs leading-5 flex-shrink-0", v(s, a, i)), children: "▸" }),
|
|
50
|
+
/* @__PURE__ */ o.jsxs("div", { className: "flex flex-col", children: [
|
|
51
|
+
/* @__PURE__ */ o.jsx("div", { className: y(s, a, i), children: t.title }),
|
|
52
|
+
t.description && /* @__PURE__ */ o.jsx("div", { className: "text-xs text-muted mt-0.5 font-normal", children: t.description })
|
|
53
|
+
] })
|
|
54
|
+
] }),
|
|
55
|
+
e < r.length - 1 && /* @__PURE__ */ o.jsx("div", { className: "flex flex-col items-center py-0.5", children: [0, 1, 2].map((c) => /* @__PURE__ */ o.jsx(
|
|
56
|
+
"div",
|
|
57
|
+
{
|
|
58
|
+
className: "text-sm font-bold transition-all duration-300 rotate-90 leading-[0.6]",
|
|
59
|
+
style: x(s, p, u, c),
|
|
60
|
+
children: "›"
|
|
61
|
+
},
|
|
62
|
+
c
|
|
63
|
+
)) })
|
|
64
|
+
] })
|
|
65
|
+
) : (
|
|
66
|
+
// Horizontal layout: title and description stacked with underline indicator
|
|
67
|
+
/* @__PURE__ */ o.jsxs("div", { className: "flex flex-col items-center", children: [
|
|
68
|
+
/* @__PURE__ */ o.jsx("div", { className: g(s, a, i), children: t.title }),
|
|
69
|
+
t.description && /* @__PURE__ */ o.jsx("div", { className: "text-xs text-muted mt-1 font-normal", children: t.description })
|
|
70
|
+
] })
|
|
71
|
+
),
|
|
72
|
+
!f && e < r.length - 1 && /* @__PURE__ */ o.jsx("div", { className: "flex", children: [0, 1, 2].map((c) => /* @__PURE__ */ o.jsx(
|
|
73
|
+
"div",
|
|
74
|
+
{
|
|
75
|
+
className: "text-2xl font-bold transition-all duration-300",
|
|
76
|
+
style: x(s, p, u, c),
|
|
77
|
+
children: "›"
|
|
78
|
+
},
|
|
79
|
+
c
|
|
80
|
+
)) })
|
|
81
|
+
] }, e);
|
|
82
|
+
}) });
|
|
83
|
+
};
|
|
84
|
+
C.displayName = "CyberUI.Steps";
|
|
85
|
+
export {
|
|
86
|
+
C as default
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=Steps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Steps.js","sources":["../../src/components/Steps.tsx"],"sourcesContent":["import React from 'react';\r\nimport type { ResponsiveValue } from \"../utils/responsive\";\r\nimport { useResponsiveValue } from \"../utils/responsive\";\r\nimport { cn } from '../utils/cn';\r\nimport { warnOnce } from '../utils/devWarn';\r\n\r\n/**\r\n * Individual step item data.\r\n */\r\nexport interface StepItem {\r\n /**\r\n * Step title.\r\n */\r\n title: string;\r\n /**\r\n * Optional step description.\r\n */\r\n description?: string;\r\n /**\r\n * Step status for explicit control. If not provided, status is derived from `current` prop.\r\n */\r\n status?: 'pending' | 'current' | 'completed' | 'error';\r\n}\r\n\r\n/**\r\n * A horizontal stepper component for multi-step processes.\r\n *\r\n * Styled with half-diamond indicators for current step, gradient underlines for completed steps,\r\n * and animated chevron separators.\r\n *\r\n * @example\r\n * // Basic steps with current index\r\n * <Steps\r\n * current={1}\r\n * items={[\r\n * { title: \"Login\" },\r\n * { title: \"Verify\" },\r\n * { title: \"Complete\" }\r\n * ]}\r\n * />\r\n *\r\n * @example\r\n * // With descriptions and explicit status\r\n * <Steps\r\n * items={[\r\n * { title: \"Login\", description: \"Enter credentials\", status: \"completed\" },\r\n * { title: \"Verify\", description: \"2FA authentication\", status: \"current\" },\r\n * { title: \"Complete\", description: \"Access granted\", status: \"pending\" }\r\n * ]}\r\n * />\r\n *\r\n * @example\r\n * // Responsive orientation (vertical on mobile, horizontal on desktop)\r\n * <Steps\r\n * orientation={{ base: 'vertical', md: 'horizontal' }}\r\n * items={[...]}\r\n * />\r\n */\r\nexport interface StepsProps {\r\n /**\r\n * Array of step items to display.\r\n */\r\n items: StepItem[];\r\n /**\r\n * Current active step index. **0-based** — first step is `current={0}`, second is `current={1}`.\r\n * A common mistake is passing a 1-based value which skips the first step.\r\n * @default 0\r\n */\r\n current?: number;\r\n /**\r\n * Layout orientation. Supports responsive values.\r\n * @default { base: 'vertical', md: 'horizontal' }\r\n */\r\n orientation?: ResponsiveValue<'horizontal' | 'vertical'>;\r\n /**\r\n * Additional CSS classes.\r\n */\r\n className?: string;\r\n}\r\n\r\nconst Steps: React.FC<StepsProps> = ({\r\n items,\r\n current = 0,\r\n orientation = { base: 'vertical', md: 'horizontal' },\r\n className = '',\r\n}) => {\r\n const currentOrientation = useResponsiveValue(orientation, 'vertical');\r\n const isVertical = currentOrientation === 'vertical';\r\n\r\n if (items.length > 0 && (current < 0 || current >= items.length)) {\r\n const hint = current === items.length\r\n ? ' Did you mean 1-based indexing? Steps is 0-based — subtract 1.'\r\n : '';\r\n warnOnce(\r\n `steps-oob-${current}-${items.length}`,\r\n `Steps: current={${current}} is out of range for ${items.length} step(s) (valid: 0-${items.length - 1}).${hint} No step will render as \"current\".`\r\n );\r\n }\r\n\r\n const getStepStatus = (item: StepItem, index: number): { isCompleted: boolean; isCurrent: boolean; isError: boolean } => {\r\n const isCompleted = item.status === 'completed' || (item.status === undefined && index < current);\r\n const isCurrent = item.status === 'current' || (item.status === undefined && index === current && current < items.length);\r\n const isError = item.status === 'error';\r\n return { isCompleted, isCurrent, isError };\r\n };\r\n\r\n const getTitleClasses = (isCompleted: boolean, isCurrent: boolean, isError: boolean): string => {\r\n const baseClasses = cn(\r\n 'relative px-2 py-1 pb-2 font-bold text-sm transition-colors duration-200 whitespace-nowrap overflow-hidden',\r\n \"after:content-[''] after:absolute after:left-0 after:right-0 after:bottom-0 after:h-[2px] after:w-full after:scale-x-0 after:origin-center after:transition-transform after:duration-300 after:ease-out\",\r\n \"before:content-[''] before:absolute before:left-1/2 before:-translate-x-1/2 before:-bottom-[3px] before:w-1.5 before:h-1.5 before:bg-accent before:rotate-45 before:rounded-[2px] before:opacity-0 before:transition-opacity before:duration-200\",\r\n );\r\n\r\n if (isCompleted) {\r\n return cn(baseClasses, 'text-secondary after:bg-linear-(--gradient-accent) after:shadow-lg-accent after:scale-x-100');\r\n }\r\n if (isCurrent) {\r\n return cn(baseClasses, 'text-secondary before:opacity-100');\r\n }\r\n if (isError) {\r\n return cn(baseClasses, 'text-error');\r\n }\r\n return cn(baseClasses, 'text-muted');\r\n };\r\n\r\n const getChevronStyle = (\r\n isCompleted: boolean,\r\n nextStepCurrent: boolean,\r\n nextStepCompleted: boolean,\r\n chevronIndex: number\r\n ): React.CSSProperties => {\r\n return {\r\n color: 'var(--color-primary)',\r\n opacity: nextStepCompleted ? 0.8 : 0.3,\r\n filter: nextStepCompleted\r\n ? 'drop-shadow(0 0 4px color-mix(in srgb, var(--color-primary), transparent 40%))'\r\n : 'none',\r\n animation: nextStepCurrent && isCompleted\r\n ? `chevronFlow 1.5s ease-in-out infinite ${chevronIndex * 0.2}s`\r\n : 'none',\r\n };\r\n };\r\n\r\n const containerClasses = isVertical\r\n ? cn('flex flex-col items-start gap-0', className)\r\n : cn('flex items-center justify-center gap-4', className);\r\n\r\n // Triangle indicator classes for vertical mode (using unicode character)\r\n const getTriangleColorClass = (isCompleted: boolean, isCurrent: boolean, isError: boolean): string => {\r\n if (isCompleted) return 'text-secondary drop-shadow-[0_0_4px_var(--color-secondary)]';\r\n if (isCurrent) return 'text-accent drop-shadow-[0_0_6px_var(--color-accent)]';\r\n if (isError) return 'text-error drop-shadow-[0_0_4px_var(--color-error)]';\r\n return 'text-muted/50';\r\n };\r\n\r\n // Vertical title classes with animated underline (left-to-right animation)\r\n const getVerticalTitleClasses = (isCompleted: boolean, isCurrent: boolean, isError: boolean): string => {\r\n const baseClasses = cn(\r\n 'relative font-bold text-sm transition-colors duration-200 pb-1 w-fit',\r\n \"after:content-[''] after:absolute after:left-0 after:right-0 after:bottom-0 after:h-[2px] after:w-full after:scale-x-0 after:origin-left after:transition-transform after:duration-300 after:ease-out\",\r\n );\r\n\r\n if (isCompleted) {\r\n return cn(baseClasses, 'text-secondary after:bg-linear-(--gradient-accent) after:shadow-lg-accent after:scale-x-100');\r\n }\r\n if (isCurrent) return cn(baseClasses, 'text-secondary');\r\n if (isError) return cn(baseClasses, 'text-error');\r\n return cn(baseClasses, 'text-muted');\r\n };\r\n\r\n return (\r\n <div className={containerClasses}>\r\n {items.map((item, index) => {\r\n const { isCompleted, isCurrent, isError } = getStepStatus(item, index);\r\n const nextStepCurrent = index < items.length - 1 &&\r\n (items[index + 1]?.status === 'current' || (items[index + 1]?.status === undefined && index + 1 === current && current < items.length));\r\n const nextStepCompleted = index < items.length - 1 &&\r\n (items[index + 1]?.status === 'completed' || (items[index + 1]?.status === undefined && index + 1 < current));\r\n\r\n return (\r\n <React.Fragment key={index}>\r\n {isVertical ? (\r\n // Vertical layout: step row + chevrons below\r\n <div className=\"flex flex-col w-full\">\r\n {/* Step row: triangle + title/description */}\r\n <div className=\"flex items-start gap-1.5\">\r\n <span className={cn('text-xs leading-5 flex-shrink-0', getTriangleColorClass(isCompleted, isCurrent, isError))}>\r\n ▸\r\n </span>\r\n <div className=\"flex flex-col\">\r\n <div className={getVerticalTitleClasses(isCompleted, isCurrent, isError)}>\r\n {item.title}\r\n </div>\r\n {item.description && (\r\n <div className=\"text-xs text-muted mt-0.5 font-normal\">{item.description}</div>\r\n )}\r\n </div>\r\n </div>\r\n {/* Chevrons aligned at fixed position */}\r\n {index < items.length - 1 && (\r\n <div className=\"flex flex-col items-center py-0.5\">\r\n {[0, 1, 2].map((chevronIndex) => (\r\n <div\r\n key={chevronIndex}\r\n className=\"text-sm font-bold transition-all duration-300 rotate-90 leading-[0.6]\"\r\n style={getChevronStyle(isCompleted, nextStepCurrent, nextStepCompleted, chevronIndex)}\r\n >\r\n ›\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n ) : (\r\n // Horizontal layout: title and description stacked with underline indicator\r\n <div className=\"flex flex-col items-center\">\r\n <div className={getTitleClasses(isCompleted, isCurrent, isError)}>\r\n {item.title}\r\n </div>\r\n {item.description && (\r\n <div className=\"text-xs text-muted mt-1 font-normal\">{item.description}</div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Horizontal chevrons (only for horizontal mode) */}\r\n {!isVertical && index < items.length - 1 && (\r\n <div className=\"flex\">\r\n {[0, 1, 2].map((chevronIndex) => (\r\n <div\r\n key={chevronIndex}\r\n className=\"text-2xl font-bold transition-all duration-300\"\r\n style={getChevronStyle(isCompleted, nextStepCurrent, nextStepCompleted, chevronIndex)}\r\n >\r\n ›\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </React.Fragment>\r\n );\r\n })}\r\n </div>\r\n );\r\n};\r\n\r\nSteps.displayName = \"CyberUI.Steps\";\r\n\r\nexport default Steps;\r\n\r\n"],"names":["Steps","items","current","orientation","className","isVertical","useResponsiveValue","hint","warnOnce","getStepStatus","item","index","isCompleted","isCurrent","isError","getTitleClasses","baseClasses","cn","getChevronStyle","nextStepCurrent","nextStepCompleted","chevronIndex","containerClasses","getTriangleColorClass","getVerticalTitleClasses","jsx","jsxs","React"],"mappings":"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"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { j as e } from "../_virtual/jsx-runtime.js";
|
|
2
|
+
import { useState as b, useRef as E, useEffect as S } from "react";
|
|
3
|
+
import { useResponsiveValue as I, getResponsiveClasses as O, RESPONSIVE_SIZE_MAPS as z } from "../utils/responsive.js";
|
|
4
|
+
const T = ({
|
|
5
|
+
tabs: d,
|
|
6
|
+
activeTab: n,
|
|
7
|
+
onTabChange: u,
|
|
8
|
+
sizeClasses: h,
|
|
9
|
+
containerClassName: g = "",
|
|
10
|
+
anchorClassName: i = "",
|
|
11
|
+
menuClassName: m = "",
|
|
12
|
+
menuItemClassName: w = "",
|
|
13
|
+
dropdownLabel: c,
|
|
14
|
+
anchorIcon: v,
|
|
15
|
+
showAnchorLabel: p = !0,
|
|
16
|
+
anchorAriaLabel: y,
|
|
17
|
+
closeOnSelect: j = !0
|
|
18
|
+
}) => {
|
|
19
|
+
const [r, a] = b(!1), [C, f] = b(!1), [l, s] = b(!1), [x, t] = b(!1), $ = E(null), R = E(null);
|
|
20
|
+
S(() => {
|
|
21
|
+
if (!r) return;
|
|
22
|
+
const o = (N) => {
|
|
23
|
+
$.current && !$.current.contains(N.target) && (s(!0), setTimeout(() => {
|
|
24
|
+
a(!1), s(!1);
|
|
25
|
+
}, 180));
|
|
26
|
+
};
|
|
27
|
+
return document.addEventListener("mousedown", o), () => document.removeEventListener("mousedown", o);
|
|
28
|
+
}, [r]), S(() => {
|
|
29
|
+
if (!r) return;
|
|
30
|
+
const o = R.current;
|
|
31
|
+
if (!o) return;
|
|
32
|
+
const N = o.getBoundingClientRect();
|
|
33
|
+
t(N.right > window.innerWidth);
|
|
34
|
+
}, [r]);
|
|
35
|
+
const k = () => {
|
|
36
|
+
r ? (s(!0), setTimeout(() => {
|
|
37
|
+
a(!1), s(!1);
|
|
38
|
+
}, 180)) : (f(!0), a(!0), setTimeout(() => f(!1), 30));
|
|
39
|
+
};
|
|
40
|
+
return /* @__PURE__ */ e.jsx("div", { className: `${g}`, children: /* @__PURE__ */ e.jsxs("div", { ref: $, className: "relative inline-block", children: [
|
|
41
|
+
/* @__PURE__ */ e.jsxs(
|
|
42
|
+
"button",
|
|
43
|
+
{
|
|
44
|
+
onClick: k,
|
|
45
|
+
className: `inline-flex items-center gap-2 ${h} bg-surface text-default border-2 border-border-default rounded-lg transition-all duration-300 hover:text-secondary hover:border-secondary hover:cursor-pointer shadow-secondary/30 ${i}`,
|
|
46
|
+
"aria-haspopup": "menu",
|
|
47
|
+
"aria-expanded": r,
|
|
48
|
+
"aria-label": p ? void 0 : y ?? (typeof c == "string" ? c : "Open tabs"),
|
|
49
|
+
children: [
|
|
50
|
+
v ?? /* @__PURE__ */ e.jsxs("svg", { className: "w-5 h-5", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true", children: [
|
|
51
|
+
/* @__PURE__ */ e.jsx("rect", { x: "3", y: "5", width: "14", height: "2", rx: "1" }),
|
|
52
|
+
/* @__PURE__ */ e.jsx("rect", { x: "3", y: "9", width: "14", height: "2", rx: "1" }),
|
|
53
|
+
/* @__PURE__ */ e.jsx("rect", { x: "3", y: "13", width: "14", height: "2", rx: "1" })
|
|
54
|
+
] }),
|
|
55
|
+
p && /* @__PURE__ */ e.jsx("span", { children: c ?? n })
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
),
|
|
59
|
+
(r || l) && /* @__PURE__ */ e.jsx(
|
|
60
|
+
"div",
|
|
61
|
+
{
|
|
62
|
+
ref: R,
|
|
63
|
+
"aria-hidden": !r,
|
|
64
|
+
className: `absolute ${x ? "right-0" : "left-0"} mt-2 min-w-40 z-50 bg-surface border-2 border-border-default rounded-lg shadow-secondary overflow-hidden ${m}
|
|
65
|
+
transition-transform transition-opacity duration-200 ease-[cubic-bezier(.2,0,0,1)] transform-gpu origin-top will-change-transform will-change-opacity ${C || l ? "opacity-0 scale-y-0 pointer-events-none" : "opacity-100 scale-y-100 pointer-events-auto"}`,
|
|
66
|
+
children: /* @__PURE__ */ e.jsx("div", { className: "py-1", children: d.map((o) => /* @__PURE__ */ e.jsx(
|
|
67
|
+
"button",
|
|
68
|
+
{
|
|
69
|
+
onClick: () => {
|
|
70
|
+
u(o), j && (s(!0), setTimeout(() => {
|
|
71
|
+
a(!1), s(!1);
|
|
72
|
+
}, 180));
|
|
73
|
+
},
|
|
74
|
+
className: `w-full flex items-center justify-between text-left px-4 py-2 font-bold transition-colors duration-200 outline-none hover:cursor-pointer ${n === o ? "text-secondary relative pl-4 before:content-[''] before:h-full before:absolute before:left-0 before:top-1/2 before:-translate-y-1/2 before:h-4 before:w-[3px] before:rounded before:bg-accent before:shadow-lg-accent" : "text-default hover:text-secondary hover:bg-base/70"} ${w}`,
|
|
75
|
+
children: /* @__PURE__ */ e.jsx("span", { children: o })
|
|
76
|
+
},
|
|
77
|
+
o
|
|
78
|
+
)) })
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
] }) });
|
|
82
|
+
}, B = ({
|
|
83
|
+
tabs: d,
|
|
84
|
+
activeTab: n,
|
|
85
|
+
onTabChange: u,
|
|
86
|
+
size: h = "md",
|
|
87
|
+
mode: g = "scroll",
|
|
88
|
+
containerClassName: i = "",
|
|
89
|
+
tabsClassName: m = "",
|
|
90
|
+
dropdownLabel: w,
|
|
91
|
+
anchorIcon: c,
|
|
92
|
+
showAnchorLabel: v = !0,
|
|
93
|
+
anchorAriaLabel: p,
|
|
94
|
+
closeOnSelect: y = !0,
|
|
95
|
+
anchorClassName: j = "",
|
|
96
|
+
menuClassName: r = "",
|
|
97
|
+
menuItemClassName: a = ""
|
|
98
|
+
}) => {
|
|
99
|
+
const f = ((t) => O(t, z.tabNavigation))(h), l = I(g, "scroll"), s = () => {
|
|
100
|
+
switch (l) {
|
|
101
|
+
case "scroll":
|
|
102
|
+
return "w-full overflow-x-auto overflow-y-hidden scrollbar-thin scrollbar-thumb-border-default scrollbar-track-transparent hover:scrollbar-thumb-secondary/50 py-1";
|
|
103
|
+
case "wrap":
|
|
104
|
+
return "flex flex-wrap gap-2";
|
|
105
|
+
case "dropdown":
|
|
106
|
+
case "collapsible":
|
|
107
|
+
return "flex space-x-2";
|
|
108
|
+
// Will be handled by specific implementations
|
|
109
|
+
default:
|
|
110
|
+
return "flex space-x-2";
|
|
111
|
+
}
|
|
112
|
+
}, x = /* @__PURE__ */ e.jsx(e.Fragment, { children: d.map((t) => /* @__PURE__ */ e.jsx(
|
|
113
|
+
"button",
|
|
114
|
+
{
|
|
115
|
+
onClick: () => u(t),
|
|
116
|
+
className: `
|
|
117
|
+
${f} font-bold transition-colors duration-200 rounded-t-lg cursor-pointer whitespace-nowrap flex-shrink-0 relative
|
|
118
|
+
after:content-[''] after:absolute after:left-0 after:right-0 after:-bottom-[2px] after:h-[2px] after:w-full after:scale-x-0 after:origin-center after:transition-transform after:duration-300 after:ease-out
|
|
119
|
+
before:content-[''] before:absolute before:left-1/2 before:-translate-x-1/2 before:-bottom-[6px] before:w-1.5 before:h-1.5 before:bg-accent before:rotate-45 before:rounded-[2px] before:opacity-0 before:transition-opacity before:duration-200
|
|
120
|
+
${n === t ? "text-secondary after:bg-linear-(--gradient-accent) after:shadow-lg-accent after:scale-x-100" : "text-muted hover:text-secondary hover:before:opacity-70"}
|
|
121
|
+
${m}
|
|
122
|
+
`,
|
|
123
|
+
role: "tab",
|
|
124
|
+
"aria-selected": n === t,
|
|
125
|
+
tabIndex: n === t ? 0 : -1,
|
|
126
|
+
children: t
|
|
127
|
+
},
|
|
128
|
+
t
|
|
129
|
+
)) });
|
|
130
|
+
return l === "dropdown" ? /* @__PURE__ */ e.jsx(
|
|
131
|
+
T,
|
|
132
|
+
{
|
|
133
|
+
tabs: d,
|
|
134
|
+
activeTab: n,
|
|
135
|
+
onTabChange: u,
|
|
136
|
+
sizeClasses: f,
|
|
137
|
+
containerClassName: i,
|
|
138
|
+
anchorClassName: j,
|
|
139
|
+
menuClassName: r,
|
|
140
|
+
menuItemClassName: a,
|
|
141
|
+
dropdownLabel: w,
|
|
142
|
+
anchorIcon: c,
|
|
143
|
+
showAnchorLabel: v,
|
|
144
|
+
anchorAriaLabel: p,
|
|
145
|
+
closeOnSelect: y
|
|
146
|
+
}
|
|
147
|
+
) : l === "scroll" ? /* @__PURE__ */ e.jsx("div", { className: `${s()} ${i}`, role: "tablist", "aria-label": "Tabs", children: /* @__PURE__ */ e.jsx("div", { className: "w-max mx-auto flex space-x-2", children: x }) }) : /* @__PURE__ */ e.jsx("div", { className: `${s()} ${i}`, role: "tablist", "aria-label": "Tabs", children: x });
|
|
148
|
+
};
|
|
149
|
+
B.displayName = "CyberUI.TabNavigation";
|
|
150
|
+
export {
|
|
151
|
+
B as default
|
|
152
|
+
};
|
|
153
|
+
//# sourceMappingURL=TabNavigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TabNavigation.js","sources":["../../src/components/TabNavigation.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect } from \"react\";\r\nimport type { ResponsiveValue } from \"../utils/responsive\";\r\nimport {\r\n getResponsiveClasses,\r\n RESPONSIVE_SIZE_MAPS,\r\n useResponsiveValue,\r\n} from \"../utils/responsive\";\r\n\r\nexport type TabNavigationMode = \"scroll\" | \"wrap\" | \"dropdown\" | \"collapsible\";\r\n\r\ninterface TabDropdownProps {\r\n tabs: readonly string[];\r\n activeTab: string;\r\n onTabChange: (tab: string) => void;\r\n sizeClasses: string;\r\n containerClassName?: string;\r\n anchorClassName?: string;\r\n menuClassName?: string;\r\n menuItemClassName?: string;\r\n dropdownLabel?: React.ReactNode;\r\n anchorIcon?: React.ReactNode;\r\n showAnchorLabel?: boolean;\r\n anchorAriaLabel?: string;\r\n closeOnSelect?: boolean;\r\n}\r\n\r\nconst TabDropdown: React.FC<TabDropdownProps> = ({\r\n tabs,\r\n activeTab,\r\n onTabChange,\r\n sizeClasses,\r\n containerClassName = \"\",\r\n anchorClassName = \"\",\r\n menuClassName = \"\",\r\n menuItemClassName = \"\",\r\n dropdownLabel,\r\n anchorIcon,\r\n showAnchorLabel = true,\r\n anchorAriaLabel,\r\n closeOnSelect = true,\r\n}) => {\r\n const [open, setOpen] = useState(false);\r\n const [isOpening, setIsOpening] = useState(false);\r\n const [isClosing, setIsClosing] = useState(false);\r\n const [alignRight, setAlignRight] = useState(false);\r\n const wrapperRef = useRef<HTMLDivElement | null>(null);\r\n const menuRef = useRef<HTMLDivElement | null>(null);\r\n\r\n useEffect(() => {\r\n if (!open) return;\r\n const handleMouseDown = (e: MouseEvent) => {\r\n if (\r\n wrapperRef.current &&\r\n !wrapperRef.current.contains(e.target as Node)\r\n ) {\r\n // JS-staged close to align with Image component behavior\r\n setIsClosing(true);\r\n setTimeout(() => {\r\n setOpen(false);\r\n setIsClosing(false);\r\n }, 180);\r\n }\r\n };\r\n document.addEventListener(\"mousedown\", handleMouseDown);\r\n return () => document.removeEventListener(\"mousedown\", handleMouseDown);\r\n }, [open]);\r\n\r\n useEffect(() => {\r\n if (!open) return;\r\n const el = menuRef.current;\r\n if (!el) return;\r\n const rect = el.getBoundingClientRect();\r\n setAlignRight(rect.right > window.innerWidth);\r\n }, [open]);\r\n\r\n const toggleOpen = () => {\r\n if (open) {\r\n setIsClosing(true);\r\n setTimeout(() => {\r\n setOpen(false);\r\n setIsClosing(false);\r\n }, 180);\r\n } else {\r\n setIsOpening(true);\r\n setOpen(true);\r\n setTimeout(() => setIsOpening(false), 30);\r\n }\r\n };\r\n\r\n return (\r\n <div className={`${containerClassName}`}>\r\n <div ref={wrapperRef} className=\"relative inline-block\">\r\n <button\r\n onClick={toggleOpen}\r\n className={`inline-flex items-center gap-2 ${sizeClasses} bg-surface text-default border-2 border-border-default rounded-lg transition-all duration-300 hover:text-secondary hover:border-secondary hover:cursor-pointer shadow-secondary/30 ${anchorClassName}`}\r\n aria-haspopup=\"menu\"\r\n aria-expanded={open}\r\n aria-label={!showAnchorLabel ? (anchorAriaLabel ?? (typeof dropdownLabel === 'string' ? dropdownLabel : 'Open tabs')) : undefined}\r\n >\r\n {anchorIcon ?? (\r\n <svg className=\"w-5 h-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\r\n <rect x=\"3\" y=\"5\" width=\"14\" height=\"2\" rx=\"1\" />\r\n <rect x=\"3\" y=\"9\" width=\"14\" height=\"2\" rx=\"1\" />\r\n <rect x=\"3\" y=\"13\" width=\"14\" height=\"2\" rx=\"1\" />\r\n </svg>\r\n )}\r\n {showAnchorLabel && (\r\n <span>{dropdownLabel ?? activeTab}</span>\r\n )}\r\n </button>\r\n\r\n {(open || isClosing) && (\r\n <div\r\n ref={menuRef}\r\n aria-hidden={!open}\r\n className={`absolute ${\r\n alignRight ? \"right-0\" : \"left-0\"\r\n } mt-2 min-w-40 z-50 bg-surface border-2 border-border-default rounded-lg shadow-secondary overflow-hidden ${menuClassName}\r\n transition-transform transition-opacity duration-200 ease-[cubic-bezier(.2,0,0,1)] transform-gpu origin-top will-change-transform will-change-opacity ${\r\n isOpening || isClosing\r\n ? \"opacity-0 scale-y-0 pointer-events-none\"\r\n : \"opacity-100 scale-y-100 pointer-events-auto\"\r\n }`}\r\n >\r\n <div className=\"py-1\">\r\n {tabs.map((tab) => (\r\n <button\r\n key={tab}\r\n onClick={() => {\r\n onTabChange(tab);\r\n if (closeOnSelect) {\r\n setIsClosing(true);\r\n setTimeout(() => {\r\n setOpen(false);\r\n setIsClosing(false);\r\n }, 180);\r\n }\r\n }}\r\n className={`w-full flex items-center justify-between text-left px-4 py-2 font-bold transition-colors duration-200 outline-none hover:cursor-pointer ${\r\n activeTab === tab\r\n ? \"text-secondary relative pl-4 before:content-[''] before:h-full before:absolute before:left-0 before:top-1/2 before:-translate-y-1/2 before:h-4 before:w-[3px] before:rounded before:bg-accent before:shadow-lg-accent\"\r\n : \"text-default hover:text-secondary hover:bg-base/70\"\r\n } ${menuItemClassName}`}\r\n >\r\n <span>{tab}</span>\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n};\r\n\r\n/**\r\n * A responsive tab navigation component.\r\n *\r\n * @example\r\n * // Basic tabs\r\n * <TabNavigation\r\n * tabs={['Home', 'Profile', 'Settings']}\r\n * activeTab={activeTab}\r\n * onTabChange={setActiveTab}\r\n * />\r\n *\r\n * @example\r\n * // Tabs with dropdown mode for mobile\r\n * <TabNavigation\r\n * tabs={categories}\r\n * activeTab={category}\r\n * onTabChange={setCategory}\r\n * mode=\"dropdown\"\r\n * dropdownLabel=\"Select Category\"\r\n * />\r\n */\r\nexport interface TabNavigationProps {\r\n /**\r\n * List of tab labels.\r\n */\r\n tabs: readonly string[];\r\n /**\r\n * Currently active tab label.\r\n */\r\n activeTab: string;\r\n /**\r\n * Callback when a tab is selected.\r\n */\r\n onTabChange: (tab: string) => void;\r\n /**\r\n * Size of the tabs.\r\n * @default 'md'\r\n */\r\n size?: ResponsiveValue<\"sm\" | \"md\" | \"lg\">;\r\n /**\r\n * Responsive display mode.\r\n * - `scroll`: Horizontal scrolling container.\r\n * - `wrap`: Wraps to new lines.\r\n * - `dropdown`: Collapses into a dropdown menu.\r\n * @default 'scroll'\r\n */\r\n mode?: ResponsiveValue<TabNavigationMode>;\r\n /** Optional custom class for the outer container */\r\n containerClassName?: string;\r\n /** Optional custom class for the tab buttons */\r\n tabsClassName?: string;\r\n /** Label for the dropdown anchor (mode=\"dropdown\" only) */\r\n dropdownLabel?: React.ReactNode;\r\n /** Custom icon for the dropdown anchor (mode=\"dropdown\" only) */\r\n anchorIcon?: React.ReactNode;\r\n /** Whether to show the label text next to the anchor icon */\r\n showAnchorLabel?: boolean;\r\n /** ARIA label for the dropdown anchor */\r\n anchorAriaLabel?: string;\r\n /** Whether to close the dropdown after choosing a tab */\r\n closeOnSelect?: boolean;\r\n /** Optional class for the dropdown anchor button */\r\n anchorClassName?: string;\r\n /** Optional class for the dropdown menu container */\r\n menuClassName?: string;\r\n /** Optional class for items in the dropdown menu */\r\n menuItemClassName?: string;\r\n}\r\n\r\n/**\r\n * A highly adaptive tabbed navigation system with cyberpunk animations and multiple layout modes.\r\n * \r\n * @example\r\n * <TabNavigation \r\n * tabs={['DASHBOARD', 'NETWORK', 'SECURITY']} \r\n * activeTab=\"DASHBOARD\" \r\n * onTabChange={setTab} \r\n * mode={{ base: 'dropdown', md: 'scroll' }} \r\n * />\r\n */\r\nconst TabNavigation: React.FC<TabNavigationProps> = ({\r\n tabs,\r\n activeTab,\r\n onTabChange,\r\n size = \"md\",\r\n mode = \"scroll\",\r\n containerClassName = \"\",\r\n tabsClassName = \"\",\r\n dropdownLabel,\r\n anchorIcon,\r\n showAnchorLabel = true,\r\n anchorAriaLabel,\r\n closeOnSelect = true,\r\n anchorClassName = \"\",\r\n menuClassName = \"\",\r\n menuItemClassName = \"\",\r\n}) => {\r\n const getSizeClasses = (\r\n size: ResponsiveValue<\"sm\" | \"md\" | \"lg\">\r\n ): string => {\r\n return getResponsiveClasses(size, RESPONSIVE_SIZE_MAPS.tabNavigation);\r\n };\r\n\r\n const sizeClasses = getSizeClasses(size);\r\n const currentMode = useResponsiveValue<TabNavigationMode>(mode, \"scroll\");\r\n\r\n const getContainerClasses = (): string => {\r\n switch (currentMode) {\r\n case \"scroll\":\r\n return \"w-full overflow-x-auto overflow-y-hidden scrollbar-thin scrollbar-thumb-border-default scrollbar-track-transparent hover:scrollbar-thumb-secondary/50 py-1\";\r\n case \"wrap\":\r\n return \"flex flex-wrap gap-2\";\r\n case \"dropdown\":\r\n case \"collapsible\":\r\n return \"flex space-x-2\"; // Will be handled by specific implementations\r\n default:\r\n return \"flex space-x-2\";\r\n }\r\n };\r\n\r\n const tabButtons = (\r\n <>\r\n {tabs.map((tab) => (\r\n <button\r\n key={tab}\r\n onClick={() => onTabChange(tab)}\r\n className={`\r\n ${sizeClasses} font-bold transition-colors duration-200 rounded-t-lg cursor-pointer whitespace-nowrap flex-shrink-0 relative\r\n after:content-[''] after:absolute after:left-0 after:right-0 after:-bottom-[2px] after:h-[2px] after:w-full after:scale-x-0 after:origin-center after:transition-transform after:duration-300 after:ease-out\r\n before:content-[''] before:absolute before:left-1/2 before:-translate-x-1/2 before:-bottom-[6px] before:w-1.5 before:h-1.5 before:bg-accent before:rotate-45 before:rounded-[2px] before:opacity-0 before:transition-opacity before:duration-200\r\n ${\r\n activeTab === tab\r\n ? \"text-secondary after:bg-linear-(--gradient-accent) after:shadow-lg-accent after:scale-x-100\"\r\n : `text-muted hover:text-secondary hover:before:opacity-70`\r\n }\r\n ${tabsClassName}\r\n `}\r\n role=\"tab\"\r\n aria-selected={activeTab === tab}\r\n tabIndex={activeTab === tab ? 0 : -1}\r\n >\r\n {tab}\r\n </button>\r\n ))}\r\n </>\r\n );\r\n\r\n if (currentMode === \"dropdown\") {\r\n return (\r\n <TabDropdown\r\n tabs={tabs}\r\n activeTab={activeTab}\r\n onTabChange={onTabChange}\r\n sizeClasses={sizeClasses}\r\n containerClassName={containerClassName}\r\n anchorClassName={anchorClassName}\r\n menuClassName={menuClassName}\r\n menuItemClassName={menuItemClassName}\r\n dropdownLabel={dropdownLabel}\r\n anchorIcon={anchorIcon}\r\n showAnchorLabel={showAnchorLabel}\r\n anchorAriaLabel={anchorAriaLabel}\r\n closeOnSelect={closeOnSelect}\r\n />\r\n );\r\n }\r\n\r\n // Scroll mode uses an inner track to center when not overflowing,\r\n // while preserving correct scroll extents when overflowing.\r\n if (currentMode === \"scroll\") {\r\n return (\r\n <div className={`${getContainerClasses()} ${containerClassName}`} role=\"tablist\" aria-label=\"Tabs\">\r\n <div className=\"w-max mx-auto flex space-x-2\">{tabButtons}</div>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className={`${getContainerClasses()} ${containerClassName}`} role=\"tablist\" aria-label=\"Tabs\">\r\n {tabButtons}\r\n </div>\r\n );\r\n};\r\n\r\nTabNavigation.displayName = \"CyberUI.TabNavigation\";\r\n\r\nexport default TabNavigation;\r\n"],"names":["TabDropdown","tabs","activeTab","onTabChange","sizeClasses","containerClassName","anchorClassName","menuClassName","menuItemClassName","dropdownLabel","anchorIcon","showAnchorLabel","anchorAriaLabel","closeOnSelect","open","setOpen","useState","isOpening","setIsOpening","isClosing","setIsClosing","alignRight","setAlignRight","wrapperRef","useRef","menuRef","useEffect","handleMouseDown","e","el","rect","toggleOpen","jsx","jsxs","tab","TabNavigation","size","mode","tabsClassName","getResponsiveClasses","RESPONSIVE_SIZE_MAPS","currentMode","useResponsiveValue","getContainerClasses","tabButtons","Fragment"],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { j as s } from "../_virtual/jsx-runtime.js";
|
|
2
|
+
import "react";
|
|
3
|
+
import { getResponsiveClasses as a } from "../utils/responsive.js";
|
|
4
|
+
import { cn as e } from "../utils/cn.js";
|
|
5
|
+
const C = ({
|
|
6
|
+
events: n,
|
|
7
|
+
size: r = "md",
|
|
8
|
+
className: l = ""
|
|
9
|
+
}) => {
|
|
10
|
+
const d = {
|
|
11
|
+
sm: "w-5 h-5",
|
|
12
|
+
md: "w-6 h-6",
|
|
13
|
+
lg: "w-8 h-8"
|
|
14
|
+
}, m = {
|
|
15
|
+
sm: "inset-1.5",
|
|
16
|
+
md: "inset-2",
|
|
17
|
+
lg: "inset-2.5"
|
|
18
|
+
}, p = {
|
|
19
|
+
sm: "text-sm",
|
|
20
|
+
md: "text-base",
|
|
21
|
+
lg: "text-lg"
|
|
22
|
+
}, g = {
|
|
23
|
+
sm: "text-xs",
|
|
24
|
+
md: "text-sm",
|
|
25
|
+
lg: "text-base"
|
|
26
|
+
}, x = {
|
|
27
|
+
sm: "text-[10px]",
|
|
28
|
+
md: "text-xs",
|
|
29
|
+
lg: "text-sm"
|
|
30
|
+
}, b = {
|
|
31
|
+
sm: "gap-3",
|
|
32
|
+
md: "gap-4",
|
|
33
|
+
lg: "gap-5"
|
|
34
|
+
}, u = {
|
|
35
|
+
sm: "space-y-4",
|
|
36
|
+
md: "space-y-6",
|
|
37
|
+
lg: "space-y-8"
|
|
38
|
+
}, h = a(r, d), f = a(r, m), _ = a(r, p), w = a(r, g), j = a(r, x), v = a(r, b), N = a(r, u), y = (o) => {
|
|
39
|
+
const t = {
|
|
40
|
+
success: {
|
|
41
|
+
border: "border-success bg-success/30 shadow-[0_0_10px_var(--color-success)]",
|
|
42
|
+
background: "border-success bg-success/30 shadow-[0_0_10px_var(--color-success)]",
|
|
43
|
+
inner: "bg-success/60"
|
|
44
|
+
},
|
|
45
|
+
error: {
|
|
46
|
+
border: "border-error bg-error/30 shadow-[0_0_10px_var(--color-error)]",
|
|
47
|
+
background: "border-error bg-error/30 shadow-[0_0_10px_var(--color-error)]",
|
|
48
|
+
inner: "bg-error/60"
|
|
49
|
+
},
|
|
50
|
+
warning: {
|
|
51
|
+
border: "border-warning bg-warning/30 shadow-[0_0_10px_var(--color-warning)]",
|
|
52
|
+
background: "border-warning bg-warning/30 shadow-[0_0_10px_var(--color-warning)]",
|
|
53
|
+
inner: "bg-warning/60"
|
|
54
|
+
},
|
|
55
|
+
info: {
|
|
56
|
+
border: "border-info bg-info/30 shadow-[0_0_10px_var(--color-info)]",
|
|
57
|
+
background: "border-info bg-info/30 shadow-[0_0_10px_var(--color-info)]",
|
|
58
|
+
inner: "bg-info/60"
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
return t[o] || t.info;
|
|
62
|
+
}, c = "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)";
|
|
63
|
+
return /* @__PURE__ */ s.jsx("div", { className: e(N, l), children: n.map((o, t) => {
|
|
64
|
+
const i = y(o.status);
|
|
65
|
+
return /* @__PURE__ */ s.jsxs("div", { className: e("flex", v, "group"), children: [
|
|
66
|
+
/* @__PURE__ */ s.jsxs("div", { className: "flex flex-col items-center", children: [
|
|
67
|
+
/* @__PURE__ */ s.jsxs("div", { className: e("relative flex-shrink-0 mt-1.5", h), children: [
|
|
68
|
+
/* @__PURE__ */ s.jsx(
|
|
69
|
+
"div",
|
|
70
|
+
{
|
|
71
|
+
className: e("absolute inset-0 border-2 transition-all duration-300", i.border),
|
|
72
|
+
style: { clipPath: c }
|
|
73
|
+
}
|
|
74
|
+
),
|
|
75
|
+
/* @__PURE__ */ s.jsx(
|
|
76
|
+
"div",
|
|
77
|
+
{
|
|
78
|
+
className: e("absolute", f, i.inner),
|
|
79
|
+
style: { clipPath: c }
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
] }),
|
|
83
|
+
t < n.length - 1 && /* @__PURE__ */ s.jsx("div", { className: "w-[2px] flex-1 bg-gradient-to-b from-secondary/50 to-transparent mt-2" })
|
|
84
|
+
] }),
|
|
85
|
+
/* @__PURE__ */ s.jsxs("div", { className: "flex-1 pb-6", children: [
|
|
86
|
+
/* @__PURE__ */ s.jsxs("div", { className: "flex items-center justify-between mb-1", children: [
|
|
87
|
+
/* @__PURE__ */ s.jsx("h4", { className: e("font-semibold text-default group-hover:text-secondary transition-colors", _), children: o.title }),
|
|
88
|
+
/* @__PURE__ */ s.jsx("span", { className: e("text-muted whitespace-nowrap ml-4", j), children: o.time })
|
|
89
|
+
] }),
|
|
90
|
+
o.description && /* @__PURE__ */ s.jsx("p", { className: e("text-muted mt-1", w), children: o.description })
|
|
91
|
+
] })
|
|
92
|
+
] }, t);
|
|
93
|
+
}) });
|
|
94
|
+
};
|
|
95
|
+
C.displayName = "CyberUI.Timeline";
|
|
96
|
+
export {
|
|
97
|
+
C as default
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=Timeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Timeline.js","sources":["../../src/components/Timeline.tsx"],"sourcesContent":["import React from 'react';\r\nimport type { ResponsiveValue } from \"../utils/responsive\";\r\nimport { getResponsiveClasses } from \"../utils/responsive\";\r\nimport { cn } from '../utils/cn';\r\n\r\n/**\r\n * Individual timeline event data.\r\n */\r\nexport interface TimelineEvent {\r\n /**\r\n * Event title.\r\n */\r\n title: string;\r\n /**\r\n * Optional event description.\r\n */\r\n description?: string;\r\n /**\r\n * Event timestamp display string.\r\n */\r\n time: string;\r\n /**\r\n * Event status/type for visual styling.\r\n * @default 'info'\r\n */\r\n status?: 'success' | 'error' | 'warning' | 'info';\r\n}\r\n\r\n/**\r\n * A vertical timeline component for displaying activity feeds or event logs.\r\n *\r\n * Features diamond-shaped status indicators with neon glow effects.\r\n *\r\n * @example\r\n * // Basic timeline\r\n * <Timeline events={[\r\n * {\r\n * title: \"System Initialized\",\r\n * description: \"All modules loaded successfully\",\r\n * time: \"2 min ago\",\r\n * status: \"success\"\r\n * },\r\n * {\r\n * title: \"Warning Detected\",\r\n * time: \"5 min ago\",\r\n * status: \"warning\"\r\n * }\r\n * ]} />\r\n *\r\n * @example\r\n * // Mixed status events\r\n * <Timeline events={[\r\n * { title: \"Connected\", time: \"Now\", status: \"success\" },\r\n * { title: \"High Memory\", time: \"1m ago\", status: \"warning\" },\r\n * { title: \"Connection Lost\", time: \"5m ago\", status: \"error\" }\r\n * ]} />\r\n */\r\nexport interface TimelineProps {\r\n /**\r\n * Array of timeline events to display.\r\n */\r\n events: TimelineEvent[];\r\n /**\r\n * Size of the timeline. Supports responsive values.\r\n * @default 'md'\r\n */\r\n size?: ResponsiveValue<'sm' | 'md' | 'lg'>;\r\n /**\r\n * Additional CSS classes.\r\n */\r\n className?: string;\r\n}\r\n\r\nconst Timeline: React.FC<TimelineProps> = ({\r\n events,\r\n size = 'md',\r\n className = '',\r\n}) => {\r\n const diamondSizeMap = {\r\n sm: 'w-5 h-5',\r\n md: 'w-6 h-6',\r\n lg: 'w-8 h-8',\r\n };\r\n\r\n const innerSizeMap = {\r\n sm: 'inset-1.5',\r\n md: 'inset-2',\r\n lg: 'inset-2.5',\r\n };\r\n\r\n const titleSizeMap = {\r\n sm: 'text-sm',\r\n md: 'text-base',\r\n lg: 'text-lg',\r\n };\r\n\r\n const descSizeMap = {\r\n sm: 'text-xs',\r\n md: 'text-sm',\r\n lg: 'text-base',\r\n };\r\n\r\n const timeSizeMap = {\r\n sm: 'text-[10px]',\r\n md: 'text-xs',\r\n lg: 'text-sm',\r\n };\r\n\r\n const gapMap = {\r\n sm: 'gap-3',\r\n md: 'gap-4',\r\n lg: 'gap-5',\r\n };\r\n\r\n const spacingMap = {\r\n sm: 'space-y-4',\r\n md: 'space-y-6',\r\n lg: 'space-y-8',\r\n };\r\n\r\n const diamondClasses = getResponsiveClasses(size, diamondSizeMap);\r\n const innerClasses = getResponsiveClasses(size, innerSizeMap);\r\n const titleClasses = getResponsiveClasses(size, titleSizeMap);\r\n const descClasses = getResponsiveClasses(size, descSizeMap);\r\n const timeClasses = getResponsiveClasses(size, timeSizeMap);\r\n const gapClasses = getResponsiveClasses(size, gapMap);\r\n const spacingClasses = getResponsiveClasses(size, spacingMap);\r\n\r\n const getStatusClasses = (status?: string): { border: string; background: string; inner: string } => {\r\n const statusStyles = {\r\n success: {\r\n border: 'border-success bg-success/30 shadow-[0_0_10px_var(--color-success)]',\r\n background: 'border-success bg-success/30 shadow-[0_0_10px_var(--color-success)]',\r\n inner: 'bg-success/60',\r\n },\r\n error: {\r\n border: 'border-error bg-error/30 shadow-[0_0_10px_var(--color-error)]',\r\n background: 'border-error bg-error/30 shadow-[0_0_10px_var(--color-error)]',\r\n inner: 'bg-error/60',\r\n },\r\n warning: {\r\n border: 'border-warning bg-warning/30 shadow-[0_0_10px_var(--color-warning)]',\r\n background: 'border-warning bg-warning/30 shadow-[0_0_10px_var(--color-warning)]',\r\n inner: 'bg-warning/60',\r\n },\r\n info: {\r\n border: 'border-info bg-info/30 shadow-[0_0_10px_var(--color-info)]',\r\n background: 'border-info bg-info/30 shadow-[0_0_10px_var(--color-info)]',\r\n inner: 'bg-info/60',\r\n },\r\n };\r\n return statusStyles[status as keyof typeof statusStyles] || statusStyles.info;\r\n };\r\n\r\n const diamondClipPath = 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)';\r\n\r\n return (\r\n <div className={cn(spacingClasses, className)}>\r\n {events.map((event, index) => {\r\n const statusClasses = getStatusClasses(event.status);\r\n\r\n return (\r\n <div key={index} className={cn('flex', gapClasses, 'group')}>\r\n <div className=\"flex flex-col items-center\">\r\n <div className={cn('relative flex-shrink-0 mt-1.5', diamondClasses)}>\r\n <div\r\n className={cn('absolute inset-0 border-2 transition-all duration-300', statusClasses.border)}\r\n style={{ clipPath: diamondClipPath }}\r\n />\r\n <div\r\n className={cn('absolute', innerClasses, statusClasses.inner)}\r\n style={{ clipPath: diamondClipPath }}\r\n />\r\n </div>\r\n {index < events.length - 1 && (\r\n <div className=\"w-[2px] flex-1 bg-gradient-to-b from-secondary/50 to-transparent mt-2\" />\r\n )}\r\n </div>\r\n\r\n <div className=\"flex-1 pb-6\">\r\n <div className=\"flex items-center justify-between mb-1\">\r\n <h4 className={cn('font-semibold text-default group-hover:text-secondary transition-colors', titleClasses)}>\r\n {event.title}\r\n </h4>\r\n <span className={cn('text-muted whitespace-nowrap ml-4', timeClasses)}>\r\n {event.time}\r\n </span>\r\n </div>\r\n {event.description && (\r\n <p className={cn('text-muted mt-1', descClasses)}>\r\n {event.description}\r\n </p>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n })}\r\n </div>\r\n );\r\n};\r\n\r\nTimeline.displayName = \"CyberUI.Timeline\";\r\n\r\nexport default Timeline;\r\n\r\n"],"names":["Timeline","events","size","className","diamondSizeMap","innerSizeMap","titleSizeMap","descSizeMap","timeSizeMap","gapMap","spacingMap","diamondClasses","getResponsiveClasses","innerClasses","titleClasses","descClasses","timeClasses","gapClasses","spacingClasses","getStatusClasses","status","statusStyles","diamondClipPath","jsx","cn","event","index","statusClasses","jsxs"],"mappings":"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"}
|