@dust-tt/sparkle 0.2.275 → 0.2.276
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.js +103 -25
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/components/Dialog.js +1 -1
- package/dist/esm/components/Dialog.js.map +1 -1
- package/dist/esm/components/Input.d.ts.map +1 -1
- package/dist/esm/components/Input.js +20 -7
- package/dist/esm/components/Input.js.map +1 -1
- package/dist/esm/components/Label.js +1 -1
- package/dist/esm/components/Label.js.map +1 -1
- package/dist/esm/components/NavigationList.d.ts +18 -0
- package/dist/esm/components/NavigationList.d.ts.map +1 -0
- package/dist/esm/components/NavigationList.js +70 -0
- package/dist/esm/components/NavigationList.js.map +1 -0
- package/dist/esm/components/NewDropdown.js +2 -2
- package/dist/esm/components/NewDropdown.js.map +1 -1
- package/dist/esm/components/Popover.js +1 -1
- package/dist/esm/components/RadioGroup.d.ts.map +1 -1
- package/dist/esm/components/RadioGroup.js +4 -4
- package/dist/esm/components/RadioGroup.js.map +1 -1
- package/dist/esm/components/Spinner.js +1 -1
- package/dist/esm/components/Tree.d.ts +2 -1
- package/dist/esm/components/Tree.d.ts.map +1 -1
- package/dist/esm/components/Tree.js +8 -8
- package/dist/esm/components/Tree.js.map +1 -1
- package/dist/esm/components/index.d.ts +2 -1
- package/dist/esm/components/index.d.ts.map +1 -1
- package/dist/esm/components/index.js +2 -1
- package/dist/esm/components/index.js.map +1 -1
- package/dist/esm/stories/Input.stories.js +1 -1
- package/dist/esm/stories/Input.stories.js.map +1 -1
- package/dist/esm/stories/NavigationList.stories.d.ts +7 -0
- package/dist/esm/stories/NavigationList.stories.d.ts.map +1 -0
- package/dist/esm/stories/NavigationList.stories.js +140 -0
- package/dist/esm/stories/NavigationList.stories.js.map +1 -0
- package/dist/sparkle.css +95 -98
- package/package.json +1 -1
- package/src/components/Dialog.tsx +1 -1
- package/src/components/Input.tsx +44 -31
- package/src/components/Label.tsx +1 -1
- package/src/components/NavigationList.tsx +137 -0
- package/src/components/NewDropdown.tsx +2 -2
- package/src/components/Popover.tsx +1 -1
- package/src/components/RadioGroup.tsx +20 -22
- package/src/components/Spinner.tsx +1 -1
- package/src/components/Tree.tsx +10 -8
- package/src/components/index.ts +6 -1
- package/src/stories/Input.stories.tsx +14 -14
- package/src/stories/NavigationList.stories.tsx +173 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { Button, Icon } from "@sparkle/components/";
|
|
5
|
+
import { MoreIcon } from "@sparkle/icons";
|
|
6
|
+
import { cn } from "@sparkle/lib/utils";
|
|
7
|
+
|
|
8
|
+
const listStyles = cva("s-flex", {
|
|
9
|
+
variants: {
|
|
10
|
+
layout: {
|
|
11
|
+
container: "s-gap-1 s-flex-col s-overflow-hidden",
|
|
12
|
+
item: cn(
|
|
13
|
+
"s-box-border s-items-center s-w-full s-flex s-gap-1.5 s-cursor-pointer s-select-none s-items-center s-outline-none s-rounded-xl s-text-sm s-px-3 s-py-2 s-transition-colors s-duration-300",
|
|
14
|
+
"data-[disabled]:s-pointer-events-none data-[disabled]:s-text-muted-foreground",
|
|
15
|
+
"hover:s-text-foreground hover:s-bg-structure-150"
|
|
16
|
+
),
|
|
17
|
+
},
|
|
18
|
+
state: {
|
|
19
|
+
active: "active:s-bg-structure-200",
|
|
20
|
+
selected: "s-text-foreground s-font-medium s-bg-structure-150",
|
|
21
|
+
unselected: "s-text-muted-foreground",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
layout: "container",
|
|
26
|
+
state: "unselected",
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const labelStyles = cva(
|
|
31
|
+
"s-font-semibold s-pt-6 s-pb-2 s-text-xs s-whitespace-nowrap s-overflow-hidden s-text-ellipsis"
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const NavigationList = React.forwardRef<
|
|
35
|
+
HTMLDivElement,
|
|
36
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
37
|
+
>(({ className, ...props }, ref) => (
|
|
38
|
+
<div
|
|
39
|
+
ref={ref}
|
|
40
|
+
className={cn(listStyles({ layout: "container" }), className)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
));
|
|
44
|
+
NavigationList.displayName = "NavigationList";
|
|
45
|
+
|
|
46
|
+
interface NavigationListItemProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
47
|
+
selected?: boolean;
|
|
48
|
+
label?: string;
|
|
49
|
+
icon?: React.ComponentType;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const NavigationListItem = React.forwardRef<
|
|
53
|
+
HTMLDivElement,
|
|
54
|
+
NavigationListItemProps
|
|
55
|
+
>(({ className, selected, label, icon, ...props }, ref) => {
|
|
56
|
+
const [isHovered, setIsHovered] = React.useState(false);
|
|
57
|
+
const [isPressed, setIsPressed] = React.useState(false);
|
|
58
|
+
|
|
59
|
+
const handleMouseDown = (event: React.MouseEvent) => {
|
|
60
|
+
if (!(event.target as HTMLElement).closest(".new-button-class")) {
|
|
61
|
+
setIsPressed(true);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div className={className} ref={ref} {...props}>
|
|
67
|
+
<div
|
|
68
|
+
className={listStyles({
|
|
69
|
+
layout: "item",
|
|
70
|
+
state: selected ? "selected" : isPressed ? "active" : "unselected",
|
|
71
|
+
})}
|
|
72
|
+
onMouseEnter={() => setIsHovered(true)}
|
|
73
|
+
onMouseLeave={() => {
|
|
74
|
+
setIsHovered(false);
|
|
75
|
+
setIsPressed(false);
|
|
76
|
+
}}
|
|
77
|
+
onMouseDown={handleMouseDown}
|
|
78
|
+
onMouseUp={() => setIsPressed(false)}
|
|
79
|
+
>
|
|
80
|
+
{icon && <Icon visual={icon} size="sm" />}
|
|
81
|
+
{label && (
|
|
82
|
+
<span className="s-grow s-overflow-hidden s-text-ellipsis s-whitespace-nowrap">
|
|
83
|
+
{label}
|
|
84
|
+
</span>
|
|
85
|
+
)}
|
|
86
|
+
{isHovered && (
|
|
87
|
+
<div className="-s-mr-2 s-flex s-h-4 s-items-center">
|
|
88
|
+
<Button
|
|
89
|
+
variant="ghost"
|
|
90
|
+
icon={MoreIcon}
|
|
91
|
+
size="xs"
|
|
92
|
+
onClick={(e) => e.stopPropagation()}
|
|
93
|
+
onMouseDown={(e) => e.stopPropagation()}
|
|
94
|
+
onMouseUp={(e) => e.stopPropagation()}
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
97
|
+
)}
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
NavigationListItem.displayName = "NavigationListItem";
|
|
103
|
+
|
|
104
|
+
const variantStyles = cva("", {
|
|
105
|
+
variants: {
|
|
106
|
+
variant: {
|
|
107
|
+
primary: "s-text-foreground",
|
|
108
|
+
secondary: "s-text-muted-foreground",
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
defaultVariants: {
|
|
112
|
+
variant: "primary",
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
interface NavigationListLabelProps
|
|
117
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
118
|
+
VariantProps<typeof variantStyles> {
|
|
119
|
+
label: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const NavigationListLabel = React.forwardRef<
|
|
123
|
+
HTMLDivElement,
|
|
124
|
+
NavigationListLabelProps
|
|
125
|
+
>(({ className, variant, label, ...props }, ref) => (
|
|
126
|
+
<div
|
|
127
|
+
ref={ref}
|
|
128
|
+
className={cn(labelStyles(), variantStyles({ variant }), className)}
|
|
129
|
+
{...props}
|
|
130
|
+
>
|
|
131
|
+
{label}
|
|
132
|
+
</div>
|
|
133
|
+
));
|
|
134
|
+
|
|
135
|
+
NavigationListLabel.displayName = "NavigationListLabel";
|
|
136
|
+
|
|
137
|
+
export { NavigationList, NavigationListItem, NavigationListLabel };
|
|
@@ -13,7 +13,7 @@ type ItemVariantType = (typeof ITEM_VARIANTS)[number];
|
|
|
13
13
|
export const menuStyleClasses = {
|
|
14
14
|
inset: "s-pl-8",
|
|
15
15
|
container: cn(
|
|
16
|
-
"s-rounded-
|
|
16
|
+
"s-rounded-xl s-border s-border-hovering s-bg-white s-p-1 s-text-primary-950",
|
|
17
17
|
"s-z-50 s-min-w-[8rem] s-overflow-hidden",
|
|
18
18
|
"data-[state=open]:s-animate-in data-[state=closed]:s-animate-out data-[state=closed]:s-fade-out-0 data-[state=open]:s-fade-in-0 data-[state=closed]:s-zoom-out-95 data-[state=open]:s-zoom-in-95 data-[side=bottom]:s-slide-in-from-top-2 data-[side=left]:s-slide-in-from-right-2 data-[side=right]:s-slide-in-from-left-2 data-[side=top]:s-slide-in-from-bottom-2"
|
|
19
19
|
),
|
|
@@ -83,7 +83,7 @@ const ItemWithLabelIconAndDescription = <
|
|
|
83
83
|
return (
|
|
84
84
|
<>
|
|
85
85
|
{label && (
|
|
86
|
-
<div className="s-grid s-grid-cols-[auto,1fr] s-gap-x-1">
|
|
86
|
+
<div className="s-grid s-grid-cols-[auto,1fr] s-gap-x-1.5">
|
|
87
87
|
{icon && (
|
|
88
88
|
<div
|
|
89
89
|
className={cn(
|
|
@@ -38,7 +38,7 @@ const PopoverContent = React.forwardRef<
|
|
|
38
38
|
"data-[side=left]:s-slide-in-from-right-2",
|
|
39
39
|
"data-[side=right]:s-slide-in-from-left-2",
|
|
40
40
|
"data-[side=top]:s-slide-in-from-bottom-2",
|
|
41
|
-
"s-z-50 s-rounded-
|
|
41
|
+
"s-z-50 s-rounded-xl s-border s-bg-background s-text-primary-950 s-shadow-md s-outline-none",
|
|
42
42
|
fullWidth ? "s-grow" : "s-w-72 s-p-4",
|
|
43
43
|
className
|
|
44
44
|
)}
|
|
@@ -46,7 +46,7 @@ const RadioGroup = React.forwardRef<
|
|
|
46
46
|
>(({ className, ...props }, ref) => {
|
|
47
47
|
return (
|
|
48
48
|
<RadioGroupPrimitive.Root
|
|
49
|
-
className={cn("s-grid", className)}
|
|
49
|
+
className={cn("s-grid s-gap-2", className)}
|
|
50
50
|
{...props}
|
|
51
51
|
ref={ref}
|
|
52
52
|
/>
|
|
@@ -69,34 +69,32 @@ const RadioGroupItem = React.forwardRef<
|
|
|
69
69
|
{ tooltipMessage, className, size, tooltipAsChild = false, ...props },
|
|
70
70
|
ref
|
|
71
71
|
) => {
|
|
72
|
+
const item = (
|
|
73
|
+
<RadioGroupPrimitive.Item
|
|
74
|
+
ref={ref}
|
|
75
|
+
className={cn(radioStyles({ size }), className)}
|
|
76
|
+
{...props}
|
|
77
|
+
>
|
|
78
|
+
<RadioGroupPrimitive.Indicator
|
|
79
|
+
className={radioIndicatorStyles({ size })}
|
|
80
|
+
/>
|
|
81
|
+
</RadioGroupPrimitive.Item>
|
|
82
|
+
);
|
|
72
83
|
return (
|
|
73
|
-
<div
|
|
84
|
+
<div
|
|
85
|
+
className={cn(
|
|
86
|
+
"s-group",
|
|
87
|
+
size === "sm" ? "s-h-5 s-w-5" : "-s-mt-1.5 s-h-4 s-w-4"
|
|
88
|
+
)}
|
|
89
|
+
>
|
|
74
90
|
{tooltipMessage ? (
|
|
75
91
|
<Tooltip
|
|
76
92
|
triggerAsChild={tooltipAsChild}
|
|
77
|
-
trigger={
|
|
78
|
-
<RadioGroupPrimitive.Item
|
|
79
|
-
ref={ref}
|
|
80
|
-
className={cn(radioStyles({ size }), className)}
|
|
81
|
-
{...props}
|
|
82
|
-
>
|
|
83
|
-
<RadioGroupPrimitive.Indicator
|
|
84
|
-
className={radioIndicatorStyles({ size })}
|
|
85
|
-
/>
|
|
86
|
-
</RadioGroupPrimitive.Item>
|
|
87
|
-
}
|
|
93
|
+
trigger={item}
|
|
88
94
|
label={<span>{tooltipMessage}</span>}
|
|
89
95
|
/>
|
|
90
96
|
) : (
|
|
91
|
-
|
|
92
|
-
ref={ref}
|
|
93
|
-
className={cn(radioStyles({ size }), className)}
|
|
94
|
-
{...props}
|
|
95
|
-
>
|
|
96
|
-
<RadioGroupPrimitive.Indicator
|
|
97
|
-
className={radioIndicatorStyles({ size })}
|
|
98
|
-
/>
|
|
99
|
-
</RadioGroupPrimitive.Item>
|
|
97
|
+
item
|
|
100
98
|
)}
|
|
101
99
|
</div>
|
|
102
100
|
);
|
|
@@ -47,7 +47,7 @@ const colors: Record<Exclude<SpinnerVariantType, "color">, LottieColorType> = {
|
|
|
47
47
|
dark: [0.0588, 0.0902, 0.1647, 1],
|
|
48
48
|
purple900: [0.298, 0.1137, 0.5843, 1], // #4C1D95
|
|
49
49
|
pink900: [0.5137, 0.0941, 0.2627, 1], // #831843
|
|
50
|
-
slate400: [0.
|
|
50
|
+
slate400: [0.5804, 0.6392, 0.7216, 1], // #94A3B8
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
const isColorArray = (arr: unknown): arr is LottieColorType => {
|
package/src/components/Tree.tsx
CHANGED
|
@@ -14,6 +14,7 @@ export interface TreeProps {
|
|
|
14
14
|
isLoading?: boolean;
|
|
15
15
|
tailwindIconTextColor?: string;
|
|
16
16
|
variant?: "navigator" | "finder";
|
|
17
|
+
className?: string;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export function Tree({
|
|
@@ -22,6 +23,7 @@ export function Tree({
|
|
|
22
23
|
isBoxed = false,
|
|
23
24
|
tailwindIconTextColor,
|
|
24
25
|
variant = "finder",
|
|
26
|
+
className,
|
|
25
27
|
}: TreeProps) {
|
|
26
28
|
const modifiedChildren = React.Children.map(children, (child) => {
|
|
27
29
|
// /!\ Limitation: This stops on the first invalid element.
|
|
@@ -44,7 +46,7 @@ export function Tree({
|
|
|
44
46
|
});
|
|
45
47
|
|
|
46
48
|
return isLoading ? (
|
|
47
|
-
<div className="s-py-2 s-pl-4">
|
|
49
|
+
<div className={cn("s-py-2 s-pl-4", className)}>
|
|
48
50
|
<Spinner size="xs" variant="dark" />
|
|
49
51
|
</div>
|
|
50
52
|
) : (
|
|
@@ -52,7 +54,8 @@ export function Tree({
|
|
|
52
54
|
className={cn(
|
|
53
55
|
"s-flex s-flex-col s-gap-0.5 s-overflow-hidden",
|
|
54
56
|
isBoxed &&
|
|
55
|
-
"s-rounded-xl s-border s-border-structure-200 s-bg-structure-50 s-p-4"
|
|
57
|
+
"s-rounded-xl s-border s-border-structure-200 s-bg-structure-50 s-p-4",
|
|
58
|
+
className
|
|
56
59
|
)}
|
|
57
60
|
>
|
|
58
61
|
{modifiedChildren}
|
|
@@ -61,12 +64,11 @@ export function Tree({
|
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
const treeItemStyleClasses = {
|
|
64
|
-
base: "s-group/tree s-flex s-cursor-default s-flex-row s-items-center s-gap-2 s-py-
|
|
67
|
+
base: "s-group/tree s-flex s-cursor-default s-flex-row s-items-center s-gap-2 s-py-2",
|
|
65
68
|
isNavigatableBase:
|
|
66
|
-
"s-rounded-
|
|
67
|
-
isNavigatableUnselected:
|
|
68
|
-
|
|
69
|
-
isNavigatableSelected: "s-border-structure-200 s-bg-white",
|
|
69
|
+
"s-rounded-xl s-pl-1.5 s-pr-3 s-transition-colors s-duration-300 s-ease-out s-cursor-pointer",
|
|
70
|
+
isNavigatableUnselected: "s-bg-structure-150/0 hover:s-bg-structure-150",
|
|
71
|
+
isNavigatableSelected: "s-font-medium s-bg-structure-150",
|
|
70
72
|
};
|
|
71
73
|
|
|
72
74
|
interface TreeItemProps {
|
|
@@ -172,7 +174,7 @@ Tree.Item = function ({
|
|
|
172
174
|
)}
|
|
173
175
|
{type === "leaf" && <div className="s-w-4 s-flex-shrink-0"></div>}
|
|
174
176
|
{checkbox && <Checkbox {...checkbox} size="xs" />}
|
|
175
|
-
<Icon visual={visual} size="
|
|
177
|
+
<Icon visual={visual} size="sm" className={tailwindIconTextColor} />
|
|
176
178
|
<div
|
|
177
179
|
className={`s-font-regular s-truncate s-text-sm s-text-element-900 ${labelClassName}`}
|
|
178
180
|
>
|
package/src/components/index.ts
CHANGED
|
@@ -44,6 +44,11 @@ export { Item } from "./Item";
|
|
|
44
44
|
export { Label } from "./Label";
|
|
45
45
|
export { Markdown } from "./Markdown";
|
|
46
46
|
export { Modal } from "./Modal";
|
|
47
|
+
export {
|
|
48
|
+
NavigationList,
|
|
49
|
+
NavigationListItem,
|
|
50
|
+
NavigationListLabel,
|
|
51
|
+
} from "./NavigationList";
|
|
47
52
|
export {
|
|
48
53
|
NewDropdownMenu,
|
|
49
54
|
NewDropdownMenuCheckboxItem,
|
|
@@ -74,7 +79,7 @@ export {
|
|
|
74
79
|
export { Popup } from "./Popup";
|
|
75
80
|
export { PriceTable } from "./PriceTable";
|
|
76
81
|
export { RadioGroup, RadioGroupChoice, RadioGroupItem } from "./RadioGroup";
|
|
77
|
-
export { ScrollArea } from "./ScrollArea";
|
|
82
|
+
export { ScrollArea, ScrollBar } from "./ScrollArea";
|
|
78
83
|
export { Searchbar } from "./Searchbar";
|
|
79
84
|
export { Separator } from "./Separator";
|
|
80
85
|
export { SliderToggle } from "./SliderToggle";
|
|
@@ -17,27 +17,27 @@ export const InputExample = () => (
|
|
|
17
17
|
<Input
|
|
18
18
|
placeholder="placeholder"
|
|
19
19
|
name="input"
|
|
20
|
-
value=
|
|
21
|
-
error=
|
|
20
|
+
value="value"
|
|
21
|
+
error="errored because it's a very long message"
|
|
22
22
|
showErrorLabel
|
|
23
23
|
/>
|
|
24
24
|
<Input
|
|
25
25
|
placeholder="placeholder"
|
|
26
26
|
name="input"
|
|
27
|
-
value=
|
|
28
|
-
error=
|
|
27
|
+
value="value"
|
|
28
|
+
error="errored"
|
|
29
29
|
/>
|
|
30
30
|
<Input
|
|
31
31
|
placeholder="placeholder"
|
|
32
32
|
name="input"
|
|
33
|
-
value=
|
|
34
|
-
error=
|
|
33
|
+
value="value"
|
|
34
|
+
error="errored because it's a very long message"
|
|
35
35
|
showErrorLabel
|
|
36
36
|
/>
|
|
37
37
|
<Input
|
|
38
38
|
placeholder="placeholder"
|
|
39
39
|
name="input"
|
|
40
|
-
value=
|
|
40
|
+
value="disabled"
|
|
41
41
|
disabled={true}
|
|
42
42
|
showErrorLabel
|
|
43
43
|
/>
|
|
@@ -47,27 +47,27 @@ export const InputExample = () => (
|
|
|
47
47
|
<Input
|
|
48
48
|
placeholder="placeholder"
|
|
49
49
|
name="input"
|
|
50
|
-
value=
|
|
51
|
-
error=
|
|
50
|
+
value="value"
|
|
51
|
+
error="errored because it's a very long message"
|
|
52
52
|
showErrorLabel
|
|
53
53
|
/>
|
|
54
54
|
<Input
|
|
55
55
|
placeholder="placeholder"
|
|
56
56
|
name="input"
|
|
57
|
-
value=
|
|
58
|
-
error=
|
|
57
|
+
value="value"
|
|
58
|
+
error="errored"
|
|
59
59
|
/>
|
|
60
60
|
<Input
|
|
61
61
|
placeholder="placeholder"
|
|
62
62
|
name="input"
|
|
63
|
-
value=
|
|
64
|
-
error=
|
|
63
|
+
value="value"
|
|
64
|
+
error="errored because it's a very long message"
|
|
65
65
|
showErrorLabel
|
|
66
66
|
/>
|
|
67
67
|
<Input
|
|
68
68
|
placeholder="placeholder"
|
|
69
69
|
name="input"
|
|
70
|
-
value=
|
|
70
|
+
value="test"
|
|
71
71
|
showErrorLabel
|
|
72
72
|
/>
|
|
73
73
|
</div>
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type { Meta } from "@storybook/react";
|
|
2
|
+
import React, { useEffect, useState } from "react";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
NavigationList,
|
|
6
|
+
NavigationListItem,
|
|
7
|
+
NavigationListLabel,
|
|
8
|
+
ScrollArea,
|
|
9
|
+
ScrollBar,
|
|
10
|
+
} from "../index_with_tw_base";
|
|
11
|
+
|
|
12
|
+
const meta = {
|
|
13
|
+
title: "Primitives/NavigationList",
|
|
14
|
+
} satisfies Meta;
|
|
15
|
+
|
|
16
|
+
export default meta;
|
|
17
|
+
|
|
18
|
+
const getRandomTitles = (count: number) => {
|
|
19
|
+
const shuffled = fakeTitles.sort(() => 0.5 - Math.random());
|
|
20
|
+
return shuffled.slice(0, count);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const NewNavigationListDemo = () => {
|
|
24
|
+
const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
|
|
25
|
+
const [conversationTitles, setConversationTitles] = useState<
|
|
26
|
+
{ label: string; items: string[] }[]
|
|
27
|
+
>([]);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
// Generate random titles for each date section only once
|
|
31
|
+
setConversationTitles([
|
|
32
|
+
{ label: "Today", items: getRandomTitles(5) },
|
|
33
|
+
{ label: "Yesterday", items: getRandomTitles(10) },
|
|
34
|
+
]);
|
|
35
|
+
console.log(conversationTitles); // Add this line
|
|
36
|
+
}, []);
|
|
37
|
+
|
|
38
|
+
// Flatten the items array to easily manage indices
|
|
39
|
+
const allItems = conversationTitles.flatMap((section) => section.items);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className="s-h-[400px] s-w-[200px] s-py-12">
|
|
43
|
+
<ScrollArea>
|
|
44
|
+
<NavigationList className="s-w-full">
|
|
45
|
+
{conversationTitles.map((section, sectionIndex) => (
|
|
46
|
+
<React.Fragment key={sectionIndex}>
|
|
47
|
+
<NavigationListLabel label={section.label} />
|
|
48
|
+
{section.items.map((title, index) => {
|
|
49
|
+
const itemIndex = allItems.indexOf(title); // Calculate the global index
|
|
50
|
+
return (
|
|
51
|
+
<NavigationListItem
|
|
52
|
+
key={index}
|
|
53
|
+
selected={itemIndex === selectedIndex}
|
|
54
|
+
onClick={() => setSelectedIndex(itemIndex)}
|
|
55
|
+
label={title}
|
|
56
|
+
className="s-w-full"
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
})}
|
|
60
|
+
</React.Fragment>
|
|
61
|
+
))}
|
|
62
|
+
</NavigationList>
|
|
63
|
+
<ScrollBar />
|
|
64
|
+
</ScrollArea>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const fakeTitles = [
|
|
70
|
+
"Project Kickoff Meeting",
|
|
71
|
+
"Budget Review Discussion",
|
|
72
|
+
"Weekly Sync with Team",
|
|
73
|
+
"AI Bot Training Session",
|
|
74
|
+
"Quarterly Planning Meeting",
|
|
75
|
+
"Feedback on Latest Design",
|
|
76
|
+
"Client Requirements Gathering",
|
|
77
|
+
"Sprint Retrospective",
|
|
78
|
+
"Daily Standup",
|
|
79
|
+
"Marketing Strategy Planning",
|
|
80
|
+
"Code Review Session",
|
|
81
|
+
"Product Launch Preparation",
|
|
82
|
+
"Onboarding New Team Members",
|
|
83
|
+
"Customer Feedback Analysis",
|
|
84
|
+
"Feature Prioritization Discussion",
|
|
85
|
+
"Technical Debt Assessment",
|
|
86
|
+
"Supply Chain Optimization",
|
|
87
|
+
"Sales Performance Review",
|
|
88
|
+
"Cross-Department Collaboration",
|
|
89
|
+
"Innovation Brainstorming",
|
|
90
|
+
"Risk Management Workshop",
|
|
91
|
+
"Holiday Schedule Planning",
|
|
92
|
+
"Compliance and Security Update",
|
|
93
|
+
"UI/UX Design Critique",
|
|
94
|
+
"End-of-Year Wrap Up",
|
|
95
|
+
"Resource Allocation Meeting",
|
|
96
|
+
"Vendor Negotiation Strategy",
|
|
97
|
+
"Crisis Management Scenario",
|
|
98
|
+
"SEO Best Practices Review",
|
|
99
|
+
"New Hire Orientation",
|
|
100
|
+
"Remote Work Policy Update",
|
|
101
|
+
"Company Values Workshop",
|
|
102
|
+
"Leadership Development Session",
|
|
103
|
+
"Diversity and Inclusion Training",
|
|
104
|
+
"Performance Improvement Plan",
|
|
105
|
+
"Customer Success Story Sharing",
|
|
106
|
+
"Community Engagement Strategy",
|
|
107
|
+
"Internal Product Demo",
|
|
108
|
+
"Cost Reduction Initiative",
|
|
109
|
+
"Change Management Planning",
|
|
110
|
+
"Employee Recognition Program",
|
|
111
|
+
"IT Infrastructure Upgrade",
|
|
112
|
+
"Content Marketing Planning",
|
|
113
|
+
"Team Building Activities",
|
|
114
|
+
"Data Privacy Compliance",
|
|
115
|
+
"Board Meeting Preparation",
|
|
116
|
+
"Investor Relations Update",
|
|
117
|
+
"KPI Tracking and Reporting",
|
|
118
|
+
"Industry Trends Analysis",
|
|
119
|
+
"Partnership Opportunities Exploration",
|
|
120
|
+
"Employee Wellness Program",
|
|
121
|
+
"Talent Acquisition Strategy",
|
|
122
|
+
"Brand Positioning Workshop",
|
|
123
|
+
"Social Media Campaign Planning",
|
|
124
|
+
"Competitive Analysis Review",
|
|
125
|
+
"Legal Compliance Training",
|
|
126
|
+
"Cybersecurity Awareness Session",
|
|
127
|
+
"Cultural Exchange Program",
|
|
128
|
+
"Product Roadmap Presentation",
|
|
129
|
+
"Customer Journey Mapping",
|
|
130
|
+
"Financial Forecasting Session",
|
|
131
|
+
"Brand Storytelling Workshop",
|
|
132
|
+
"AI Ethics and Governance Discussion",
|
|
133
|
+
"Operational Efficiency Assessment",
|
|
134
|
+
"Annual Report Drafting",
|
|
135
|
+
"Project Milestone Celebration",
|
|
136
|
+
"Quality Assurance Review",
|
|
137
|
+
"Public Relations Strategy",
|
|
138
|
+
"Team Performance Metrics",
|
|
139
|
+
"Innovation Lab Tour",
|
|
140
|
+
"Digital Transformation Roadmap",
|
|
141
|
+
"Sustainability Initiatives Planning",
|
|
142
|
+
"Internal Communications Strategy",
|
|
143
|
+
"Customer Advisory Board Meeting",
|
|
144
|
+
"Agile Methodology Training",
|
|
145
|
+
"E-commerce Platform Update",
|
|
146
|
+
"Risk Assessment and Mitigation",
|
|
147
|
+
"Employee Satisfaction Survey Results",
|
|
148
|
+
"Sales Funnel Optimization",
|
|
149
|
+
"Cross-Cultural Communication Training",
|
|
150
|
+
"Global Expansion Strategy",
|
|
151
|
+
"Cloud Migration Plan",
|
|
152
|
+
"Crisis Communication Strategy",
|
|
153
|
+
"Webinar Content Creation",
|
|
154
|
+
"Supply Chain Risk Management",
|
|
155
|
+
"Data Analytics and Insights",
|
|
156
|
+
"Customer Onboarding Process",
|
|
157
|
+
"Brand Awareness Campaign",
|
|
158
|
+
"Product Feature Request Review",
|
|
159
|
+
"Annual Budget Allocation",
|
|
160
|
+
"Employee Exit Interview",
|
|
161
|
+
"User Feedback Session",
|
|
162
|
+
"Strategic Partnership Negotiation",
|
|
163
|
+
"Market Entry Strategy",
|
|
164
|
+
"Employee Handbook Update",
|
|
165
|
+
"Stakeholder Engagement Plan",
|
|
166
|
+
"AI Chatbot Development",
|
|
167
|
+
"Customer Retention Strategy",
|
|
168
|
+
"Company Anniversary Celebration",
|
|
169
|
+
"Leadership Team Offsite",
|
|
170
|
+
"Innovation Challenge Kickoff",
|
|
171
|
+
"Employee Benefits Review",
|
|
172
|
+
"Business Continuity Planning",
|
|
173
|
+
];
|