@dust-tt/sparkle 0.2.274 → 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.
Files changed (157) hide show
  1. package/dist/cjs/index.js +8245 -8259
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/esm/components/AssistantPreview.d.ts.map +1 -1
  4. package/dist/esm/components/AssistantPreview.js +1 -1
  5. package/dist/esm/components/AssistantPreview.js.map +1 -1
  6. package/dist/esm/components/Banner.js +1 -1
  7. package/dist/esm/components/Banner.js.map +1 -1
  8. package/dist/esm/components/BarHeader.js +5 -5
  9. package/dist/esm/components/BarHeader.js.map +1 -1
  10. package/dist/esm/components/Button.d.ts +20 -24
  11. package/dist/esm/components/Button.d.ts.map +1 -1
  12. package/dist/esm/components/Button.js +76 -117
  13. package/dist/esm/components/Button.js.map +1 -1
  14. package/dist/esm/components/Citation.js +1 -1
  15. package/dist/esm/components/Citation.js.map +1 -1
  16. package/dist/esm/components/ConversationMessageActions.d.ts.map +1 -1
  17. package/dist/esm/components/ConversationMessageActions.js +2 -2
  18. package/dist/esm/components/ConversationMessageActions.js.map +1 -1
  19. package/dist/esm/components/DataTable.js +2 -2
  20. package/dist/esm/components/DataTable.js.map +1 -1
  21. package/dist/esm/components/Dialog.d.ts +2 -2
  22. package/dist/esm/components/Dialog.d.ts.map +1 -1
  23. package/dist/esm/components/Dialog.js +2 -2
  24. package/dist/esm/components/Dialog.js.map +1 -1
  25. package/dist/esm/components/FilterChips.d.ts.map +1 -1
  26. package/dist/esm/components/FilterChips.js +1 -1
  27. package/dist/esm/components/FilterChips.js.map +1 -1
  28. package/dist/esm/components/IconButton.d.ts +4 -3
  29. package/dist/esm/components/IconButton.d.ts.map +1 -1
  30. package/dist/esm/components/IconButton.js +41 -65
  31. package/dist/esm/components/IconButton.js.map +1 -1
  32. package/dist/esm/components/Input.d.ts.map +1 -1
  33. package/dist/esm/components/Input.js +20 -7
  34. package/dist/esm/components/Input.js.map +1 -1
  35. package/dist/esm/components/Label.js +1 -1
  36. package/dist/esm/components/Label.js.map +1 -1
  37. package/dist/esm/components/Markdown.d.ts.map +1 -1
  38. package/dist/esm/components/Markdown.js +2 -2
  39. package/dist/esm/components/Markdown.js.map +1 -1
  40. package/dist/esm/components/NavigationList.d.ts +18 -0
  41. package/dist/esm/components/NavigationList.d.ts.map +1 -0
  42. package/dist/esm/components/NavigationList.js +70 -0
  43. package/dist/esm/components/NavigationList.js.map +1 -0
  44. package/dist/esm/components/NewDropdown.js +2 -2
  45. package/dist/esm/components/NewDropdown.js.map +1 -1
  46. package/dist/esm/components/Notification.js +1 -1
  47. package/dist/esm/components/Notification.js.map +1 -1
  48. package/dist/esm/components/Pagination.d.ts.map +1 -1
  49. package/dist/esm/components/Pagination.js +2 -2
  50. package/dist/esm/components/Pagination.js.map +1 -1
  51. package/dist/esm/components/Popover.js +1 -1
  52. package/dist/esm/components/Popup.js +1 -1
  53. package/dist/esm/components/Popup.js.map +1 -1
  54. package/dist/esm/components/RadioGroup.d.ts.map +1 -1
  55. package/dist/esm/components/RadioGroup.js +4 -4
  56. package/dist/esm/components/RadioGroup.js.map +1 -1
  57. package/dist/esm/components/Searchbar.js +1 -1
  58. package/dist/esm/components/Searchbar.js.map +1 -1
  59. package/dist/esm/components/Spinner.d.ts +6 -2
  60. package/dist/esm/components/Spinner.d.ts.map +1 -1
  61. package/dist/esm/components/Spinner.js +34 -47
  62. package/dist/esm/components/Spinner.js.map +1 -1
  63. package/dist/esm/components/Tree.d.ts +2 -1
  64. package/dist/esm/components/Tree.d.ts.map +1 -1
  65. package/dist/esm/components/Tree.js +9 -9
  66. package/dist/esm/components/Tree.js.map +1 -1
  67. package/dist/esm/components/ZoomableImageCitationWrapper.js +1 -1
  68. package/dist/esm/components/index.d.ts +2 -1
  69. package/dist/esm/components/index.d.ts.map +1 -1
  70. package/dist/esm/components/index.js +2 -1
  71. package/dist/esm/components/index.js.map +1 -1
  72. package/dist/esm/stories/Banner.stories.js +1 -1
  73. package/dist/esm/stories/Banner.stories.js.map +1 -1
  74. package/dist/esm/stories/Button.stories.d.ts +3 -16
  75. package/dist/esm/stories/Button.stories.d.ts.map +1 -1
  76. package/dist/esm/stories/Button.stories.js +54 -166
  77. package/dist/esm/stories/Button.stories.js.map +1 -1
  78. package/dist/esm/stories/CardButton.stories.js +1 -1
  79. package/dist/esm/stories/CardButton.stories.js.map +1 -1
  80. package/dist/esm/stories/ContextItem.stories.d.ts.map +1 -1
  81. package/dist/esm/stories/ContextItem.stories.js +5 -5
  82. package/dist/esm/stories/ContextItem.stories.js.map +1 -1
  83. package/dist/esm/stories/Dialog.stories.js +1 -1
  84. package/dist/esm/stories/Dialog.stories.js.map +1 -1
  85. package/dist/esm/stories/DropdownMenu.stories.d.ts.map +1 -1
  86. package/dist/esm/stories/DropdownMenu.stories.js +7 -7
  87. package/dist/esm/stories/DropdownMenu.stories.js.map +1 -1
  88. package/dist/esm/stories/IconButton.stories.js +4 -4
  89. package/dist/esm/stories/IconButton.stories.js.map +1 -1
  90. package/dist/esm/stories/Input.stories.js +1 -1
  91. package/dist/esm/stories/Input.stories.js.map +1 -1
  92. package/dist/esm/stories/Markdown.stories.d.ts +1 -2
  93. package/dist/esm/stories/Markdown.stories.d.ts.map +1 -1
  94. package/dist/esm/stories/Modal.stories.d.ts.map +1 -1
  95. package/dist/esm/stories/Modal.stories.js +1 -2
  96. package/dist/esm/stories/Modal.stories.js.map +1 -1
  97. package/dist/esm/stories/NavigationList.stories.d.ts +7 -0
  98. package/dist/esm/stories/NavigationList.stories.d.ts.map +1 -0
  99. package/dist/esm/stories/NavigationList.stories.js +140 -0
  100. package/dist/esm/stories/NavigationList.stories.js.map +1 -0
  101. package/dist/esm/stories/NewDropdown.stories.js +2 -2
  102. package/dist/esm/stories/NewDropdown.stories.js.map +1 -1
  103. package/dist/esm/stories/Page.stories.d.ts.map +1 -1
  104. package/dist/esm/stories/Page.stories.js +6 -6
  105. package/dist/esm/stories/Page.stories.js.map +1 -1
  106. package/dist/esm/stories/Popover.stories.js +1 -1
  107. package/dist/esm/stories/Popover.stories.js.map +1 -1
  108. package/dist/esm/stories/Searchbar.stories.js +3 -3
  109. package/dist/esm/stories/Searchbar.stories.js.map +1 -1
  110. package/dist/esm/stories/SliderToggle.stories.js +2 -2
  111. package/dist/esm/stories/SliderToggle.stories.js.map +1 -1
  112. package/dist/esm/stories/Tree.stories.d.ts.map +1 -1
  113. package/dist/esm/stories/Tree.stories.js +6 -6
  114. package/dist/esm/stories/Tree.stories.js.map +1 -1
  115. package/dist/sparkle.css +186 -281
  116. package/package.json +1 -1
  117. package/src/components/AssistantPreview.tsx +1 -5
  118. package/src/components/Banner.tsx +1 -1
  119. package/src/components/BarHeader.tsx +8 -11
  120. package/src/components/Button.tsx +169 -237
  121. package/src/components/Citation.tsx +1 -1
  122. package/src/components/ConversationMessageActions.tsx +3 -7
  123. package/src/components/DataTable.tsx +2 -2
  124. package/src/components/Dialog.tsx +5 -5
  125. package/src/components/FilterChips.tsx +1 -2
  126. package/src/components/IconButton.tsx +56 -87
  127. package/src/components/Input.tsx +44 -31
  128. package/src/components/Label.tsx +1 -1
  129. package/src/components/Markdown.tsx +2 -3
  130. package/src/components/NavigationList.tsx +137 -0
  131. package/src/components/NewDropdown.tsx +2 -2
  132. package/src/components/Notification.tsx +1 -1
  133. package/src/components/Pagination.tsx +2 -8
  134. package/src/components/Popover.tsx +1 -1
  135. package/src/components/Popup.tsx +1 -1
  136. package/src/components/RadioGroup.tsx +20 -22
  137. package/src/components/Searchbar.tsx +1 -1
  138. package/src/components/Spinner.tsx +45 -68
  139. package/src/components/Tree.tsx +11 -9
  140. package/src/components/ZoomableImageCitationWrapper.tsx +1 -1
  141. package/src/components/index.ts +6 -1
  142. package/src/stories/Banner.stories.tsx +1 -1
  143. package/src/stories/Button.stories.tsx +62 -607
  144. package/src/stories/CardButton.stories.tsx +1 -1
  145. package/src/stories/ContextItem.stories.tsx +6 -11
  146. package/src/stories/Dialog.stories.tsx +1 -1
  147. package/src/stories/DropdownMenu.stories.tsx +8 -27
  148. package/src/stories/IconButton.stories.tsx +4 -4
  149. package/src/stories/Input.stories.tsx +14 -14
  150. package/src/stories/Modal.stories.tsx +1 -2
  151. package/src/stories/NavigationList.stories.tsx +173 -0
  152. package/src/stories/NewDropdown.stories.tsx +2 -16
  153. package/src/stories/Page.stories.tsx +6 -9
  154. package/src/stories/Popover.stories.tsx +1 -1
  155. package/src/stories/Searchbar.stories.tsx +3 -3
  156. package/src/stories/SliderToggle.stories.tsx +2 -2
  157. package/src/stories/Tree.stories.tsx +6 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dust-tt/sparkle",
3
- "version": "0.2.274",
3
+ "version": "0.2.276",
4
4
  "scripts": {
5
5
  "build": "rm -rf dist && npm run tailwind && npm run build:esm && npm run build:cjs",
6
6
  "tailwind": "tailwindcss -i ./src/styles/tailwind.css -o dist/sparkle.css",
@@ -146,13 +146,9 @@ const MinimalVariantContent = ({
146
146
  }: MinimalVariantAssistantPreviewProps) => {
147
147
  const actionButton = actionElement ?? (
148
148
  <Button
149
- label=""
150
149
  icon={MoreIcon}
151
- variant="tertiary"
150
+ variant="ghost"
152
151
  size="sm"
153
- labelVisible={false}
154
- hasMagnifying={false}
155
- disabledTooltip={true}
156
152
  onClick={(e) => {
157
153
  e.stopPropagation();
158
154
  onActionClick?.();
@@ -50,7 +50,7 @@ export function Banner({
50
50
  <IconButton
51
51
  icon={XMarkIcon}
52
52
  size="sm"
53
- variant="secondary"
53
+ variant="outline"
54
54
  onClick={() => {
55
55
  setIsDismissed(true);
56
56
  if (onDismiss) {
@@ -83,9 +83,8 @@ BarHeader.ButtonBar = function (props: BarHeaderButtonBarProps) {
83
83
  <Button
84
84
  size="sm"
85
85
  icon={ChevronLeftIcon}
86
- variant="tertiary"
87
- label="Back"
88
- labelVisible={false}
86
+ variant="ghost"
87
+ tooltip="Back"
89
88
  onClick={props.onBack}
90
89
  />
91
90
  );
@@ -94,9 +93,8 @@ BarHeader.ButtonBar = function (props: BarHeaderButtonBarProps) {
94
93
  <Button
95
94
  size="sm"
96
95
  icon={XMarkIcon}
97
- variant="tertiary"
98
- label="Close"
99
- labelVisible={false}
96
+ variant="ghost"
97
+ tooltip="Close"
100
98
  onClick={props.onClose}
101
99
  />
102
100
  );
@@ -106,7 +104,7 @@ BarHeader.ButtonBar = function (props: BarHeaderButtonBarProps) {
106
104
  <Button
107
105
  size="sm"
108
106
  label="Cancel"
109
- variant="tertiary"
107
+ variant="ghost"
110
108
  onClick={props.onCancel}
111
109
  disabled={!props.onCancel || props.isSaving}
112
110
  />
@@ -128,17 +126,16 @@ BarHeader.ButtonBar = function (props: BarHeaderButtonBarProps) {
128
126
  <>
129
127
  <Button
130
128
  size="sm"
131
- label="Delete"
132
129
  icon={TrashIcon}
133
- variant="tertiary"
134
- labelVisible={false}
130
+ tooltip="Delete"
131
+ variant="ghost"
135
132
  onClick={props.onDelete}
136
133
  />
137
134
  <Button
138
135
  size="sm"
139
136
  label="Share"
140
137
  icon={ArrowUpOnSquareIcon}
141
- variant="tertiary"
138
+ variant="ghost"
142
139
  onClick={props.onShare}
143
140
  />
144
141
  </>
@@ -1,261 +1,193 @@
1
- import React, {
2
- Children,
3
- cloneElement,
4
- ComponentType,
5
- MouseEvent,
6
- ReactNode,
7
- } from "react";
8
-
9
- import { Tooltip } from "@sparkle/components/Tooltip";
10
- import { ChevronDownIcon, ChevronUpDownIcon } from "@sparkle/icons/solid";
11
- import { classNames } from "@sparkle/lib/utils";
12
-
13
- import { Icon, IconProps } from "./Icon";
1
+ import { Slot } from "@radix-ui/react-slot";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import * as React from "react";
4
+
5
+ import {
6
+ Icon,
7
+ Spinner,
8
+ TooltipContent,
9
+ TooltipProvider,
10
+ TooltipRoot,
11
+ TooltipTrigger,
12
+ } from "@sparkle/components";
13
+ import { SpinnerProps } from "@sparkle/components/Spinner";
14
+ import { ChevronDownIcon } from "@sparkle/icons";
15
+ import { cn } from "@sparkle/lib/utils";
14
16
 
15
17
  const BUTTON_VARIANTS = [
16
18
  "primary",
17
- "primaryWarning",
18
- "secondary",
19
- "secondaryWarning",
20
- "tertiary",
19
+ "highlight",
20
+ "warning",
21
+ "outline",
22
+ "ghost",
23
+ "white",
21
24
  ] as const;
22
25
 
23
26
  export type ButtonVariantType = (typeof BUTTON_VARIANTS)[number];
24
27
 
25
- export type ButtonProps = {
26
- variant?: ButtonVariantType;
27
- type?: "button" | "menu" | "select";
28
- size?: "xs" | "sm" | "md" | "lg";
29
- onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
30
- disabled?: boolean;
31
- hasMagnifying?: boolean;
32
- label: string;
33
- labelVisible?: boolean;
34
- icon?: ComponentType;
35
- className?: string;
36
- tooltipPosition?: React.ComponentProps<typeof Tooltip>["side"];
37
- disabledTooltip?: boolean;
28
+ const BUTTON_SIZES = ["xs", "sm", "md"] as const;
29
+
30
+ type ButtonSizeType = (typeof BUTTON_SIZES)[number];
31
+
32
+ const styleVariants: Record<ButtonVariantType, string> = {
33
+ primary:
34
+ "s-bg-primary s-text-white hover:s-bg-primary-light active:s-bg-primary-dark disabled:s-bg-primary-muted",
35
+ highlight:
36
+ "s-bg-highlight s-text-white hover:s-bg-highlight-light active:s-bg-highlight-dark disabled:s-bg-highlight-muted",
37
+ warning:
38
+ "s-bg-warning s-text-white hover:s-bg-warning-light active:s-bg-warning-dark disabled:s-bg-warning-muted",
39
+ outline:
40
+ "s-border s-text-primary-dark s-border-border-dark hover:s-text-primary hover:s-bg-primary-100 hover:s-border-primary-200 active:s-bg-primary-300 disabled:s-text-primary-muted disabled:s-border-structure-100",
41
+ ghost:
42
+ "s-border s-border-primary-200/0 s-text-primary-950 hover:s-bg-primary-100 hover:s-text-primary-900 active:s-bg-primary-200 hover:s-border-primary-200 disabled:s-text-primary-400",
43
+ white:
44
+ "s-bg-white s-text-primary-dark s-border s-border-border-dark hover:s-bg-primary-100 hover:s-border-primary-200 active:s-bg-primary-300 disabled:s-text-primary-muted",
38
45
  };
39
46
 
40
- const textClasses = {
41
- xs: "s-text-xs s-font-semibold s-rounded-lg",
42
- sm: "s-text-sm s-font-semibold s-rounded-xl",
43
- md: "s-text-base s-font-bold s-rounded-2xl",
44
- lg: "s-text-lg s-font-bold s-rounded-3xl",
47
+ const sizeVariants: Record<ButtonSizeType, string> = {
48
+ xs: "s-h-7 s-px-2.5 s-rounded-lg s-text-xs s-gap-1.5",
49
+ sm: "s-h-9 s-px-3 s-rounded-xl s-text-sm s-gap-2",
50
+ md: "s-h-12 s-px-4 s-py-2 s-rounded-2xl s-text-base s-gap-2.5",
45
51
  };
46
52
 
47
- const sizeClasses = {
48
- xs: "s-gap-x-1 s-px-2 s-h-7",
49
- sm: "s-gap-x-1 s-px-3 s-h-9",
50
- md: "s-gap-x-1.5 s-px-4 s-h-12",
51
- lg: "s-gap-x-3 s-px-5 s-h-16",
52
- };
53
+ const buttonVariants = cva(
54
+ "s-inline-flex s-items-center s-justify-center s-whitespace-nowrap s-font-medium s-ring-offset-background s-transition-colors " +
55
+ "focus-visible:s-outline-none focus-visible:s-ring-2 focus-visible:s-ring-ring focus-visible:s-ring-offset-2 " +
56
+ "disabled:s-pointer-events-none",
57
+ {
58
+ variants: {
59
+ variant: styleVariants,
60
+ size: sizeVariants,
61
+ },
62
+ }
63
+ );
53
64
 
54
- const containerClasses = {
55
- xs: "s-px-0.5",
56
- sm: "s-px-1",
57
- md: "s-px-1",
58
- lg: "s-px-1",
59
- };
65
+ type SpinnerVariant = NonNullable<SpinnerProps["variant"]>;
60
66
 
61
- const variantClasses = {
62
- primary: {
63
- base: "s-text-white s-bg-action-500 s-border-action-600",
64
- hover: "hover:s-bg-action-400 hover:s-border-action-500",
65
- active: "active:s-bg-action-600 active:s-border-action-700",
66
- disabled: "s-text-white s-bg-action-200 s-border-action-200",
67
- dark: {
68
- base: "",
69
- hover: "",
70
- active: "",
71
- disabled:
72
- "dark:s-text-action-700-dark dark:s-bg-action-100-dark dark:s-border-action-200-dark dark:s-saturate-50",
73
- },
74
- },
75
- primaryWarning: {
76
- base: "s-text-white s-bg-warning-500 s-border-warning-600",
77
- hover: "hover:s-bg-warning-400 hover:s-border-warning-500",
78
- active: "active:s-bg-warning-600 active:s-border-warning-700",
79
- disabled: "s-text-white s-bg-warning-200 s-border-warning-200",
80
- dark: {
81
- base: "",
82
- hover: "",
83
- active: "",
84
- disabled:
85
- "dark:s-text-warning-700-dark dark:s-bg-warning-100-dark dark:s-border-warning-200-dark dark:s-saturate-50",
86
- },
87
- },
88
- secondary: {
89
- base: "s-text-action-500 s-border-structure-200 s-bg-structure-0",
90
- hover: "hover:s-bg-action-50 hover:s-border-action-200",
91
- active: "active:s-bg-action-100 active:s-border-action-500",
92
- disabled: "s-text-action-300 s-border-structure-200 s-bg-structure-0",
93
- dark: {
94
- base: "dark:s-text-action-600-dark dark:s-border-structure-200-dark dark:s-bg-structure-100-dark",
95
- hover:
96
- "dark:hover:s-text-action-700-dark dark:hover:s-bg-action-50-dark dark:hover:s-border-action-300-dark",
97
- active:
98
- "dark:active:s-bg-action-100-dark dark:active:s-border-action-500-dark",
99
- disabled:
100
- "dark:s-text-element-600-dark dark:s-border-structure-100-dark dark:s-bg-structure-200-dark",
101
- },
102
- },
103
- secondaryWarning: {
104
- base: "s-text-warning-500 s-border-structure-200 s-bg-structure-0",
105
- hover: "hover:s-bg-warning-50 hover:s-border-warning-200",
106
- active: "active:s-bg-warning-100 active:s-border-warning-500",
107
- disabled: "s-text-warning-300 s-border-structure-200 s-bg-structure-0",
108
- dark: {
109
- base: "dark:s-text-warning-600-dark dark:s-border-structure-300-dark dark:s-bg-structure-100-dark",
110
- hover:
111
- "dark:hover:s-bg-warning-50-dark dark:hover:s-border-warning-300-dark",
112
- active:
113
- "dark:active:s-bg-warning-100-dark dark:active:s-border-warning-500-dark",
114
- disabled:
115
- "dark:s-text-element-600-dark dark:s-border-structure-100-dark dark:s-bg-structure-200-dark",
116
- },
117
- },
118
- tertiary: {
119
- base: "s-text-element-800 s-border-structure-200 s-bg-structure-0",
120
- hover:
121
- "hover:s-bg-action-50 hover:s-text-action-500 hover:s-border-action-200",
122
- active:
123
- "active:s-bg-action-100 active:s-text-action-600 active:s-border-action-500",
124
- disabled: "s-text-element-500 s-border-structure-200 s-bg-structure-0",
125
- dark: {
126
- base: "dark:s-text-element-800-dark dark:s-border-structure-300-dark dark:s-bg-structure-100-dark",
127
- hover:
128
- "dark:hover:s-text-action-300 dark:hover:s-bg-action-50-dark dark:hover:s-border-action-300-dark",
129
- active:
130
- "dark:active:s-bg-action-100-dark dark:active:s-border-action-500-dark",
131
- disabled:
132
- "dark:s-text-element-600-dark dark:s-border-structure-100-dark dark:s-bg-structure-200-dark",
133
- },
134
- },
67
+ const spinnerVariantsMap: Record<ButtonVariantType, SpinnerVariant> = {
68
+ primary: "light",
69
+ highlight: "light",
70
+ warning: "light",
71
+ outline: "dark",
72
+ ghost: "dark",
73
+ white: "light",
135
74
  };
136
75
 
137
- const transitionClasses =
138
- "s-transition-all s-ease-out s-duration-200 s-cursor-pointer";
76
+ const spinnerVariantsMapIsLoading: Record<ButtonVariantType, SpinnerVariant> = {
77
+ primary: "light",
78
+ highlight: "light",
79
+ warning: "light",
80
+ white: "light",
81
+ outline: "slate400",
82
+ ghost: "slate400",
83
+ };
139
84
 
140
- const magnifyingClasses =
141
- "hover:s-scale-105 hover:s-drop-shadow-md active:s-scale-100 active:s-drop-shadow-none";
85
+ interface MetaButtonProps
86
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
87
+ VariantProps<typeof buttonVariants> {
88
+ asChild?: boolean;
89
+ hasVisual?: boolean;
90
+ variant?: ButtonVariantType;
91
+ }
142
92
 
143
- export function Button({
144
- variant = "primary",
145
- type = "button",
146
- size = "sm",
147
- onClick,
148
- disabled = false,
149
- labelVisible = true,
150
- label,
151
- icon,
152
- className = "",
153
- tooltipPosition,
154
- hasMagnifying = false,
155
- disabledTooltip = false,
156
- }: ButtonProps) {
157
- let buttonClasses = classNames(
158
- "s-inline-flex s-items-center s-border s-scale-100 s-box-border s-whitespace-nowrap",
159
- sizeClasses[size],
160
- textClasses[size],
161
- className
162
- );
163
- if (disabled) {
164
- buttonClasses = classNames(
165
- buttonClasses,
166
- variantClasses[variant]?.disabled,
167
- variantClasses[variant]?.dark.disabled,
168
- disabled ? "s-cursor-default" : ""
169
- );
170
- } else {
171
- buttonClasses = classNames(
172
- buttonClasses,
173
- transitionClasses,
174
- hasMagnifying ? magnifyingClasses : "",
175
- variantClasses[variant]?.base,
176
- variantClasses[variant]?.hover,
177
- variantClasses[variant]?.active,
178
- variantClasses[variant]?.dark?.base,
179
- variantClasses[variant]?.dark?.hover,
180
- variantClasses[variant]?.dark?.active
93
+ const MetaButton = React.forwardRef<HTMLButtonElement, MetaButtonProps>(
94
+ (
95
+ { className, variant, size = "sm", asChild = false, children, ...props },
96
+ ref
97
+ ) => {
98
+ const Comp = asChild ? Slot : "button";
99
+
100
+ return (
101
+ <Comp
102
+ className={cn(buttonVariants({ variant, size }), className)}
103
+ ref={ref}
104
+ {...props}
105
+ >
106
+ {children}
107
+ </Comp>
181
108
  );
182
109
  }
183
-
184
- const finalContainerClasses = classNames(containerClasses[size]);
185
-
186
- const buttonBase = (
187
- <button
188
- type="button"
189
- className={buttonClasses}
190
- onClick={onClick}
191
- disabled={disabled}
192
- aria-label={label}
193
- >
194
- {icon && <Icon visual={icon} size={size as IconProps["size"]} />}
195
- {labelVisible ? (
196
- <div className={classNames("s-truncate", finalContainerClasses)}>
197
- {label}
198
- </div>
199
- ) : null}
200
- {type === "menu" && (
201
- <Icon
202
- className="s-opacity-50"
203
- visual={ChevronDownIcon}
204
- size={size as IconProps["size"]}
205
- />
206
- )}
207
- {type === "select" && (
208
- <Icon
209
- className="s-opacity-60"
210
- visual={ChevronUpDownIcon}
211
- size={size as IconProps["size"]}
212
- />
213
- )}
214
- </button>
215
- );
216
-
217
- return labelVisible || disabledTooltip ? (
218
- buttonBase
219
- ) : (
220
- <Tooltip
221
- trigger={buttonBase}
222
- label={label}
223
- side={tooltipPosition}
224
- tooltipTriggerAsChild
225
- />
226
- );
110
+ );
111
+ MetaButton.displayName = "MetaButton";
112
+
113
+ export interface ButtonProps extends MetaButtonProps {
114
+ label?: string;
115
+ icon?: React.ComponentType;
116
+ isSelect?: boolean;
117
+ isLoading?: boolean;
118
+ isPulsing?: boolean;
119
+ tooltip?: string;
227
120
  }
228
121
 
229
- interface ButtonListProps {
230
- children: ReactNode;
231
- isWrapping?: boolean;
232
- className?: string;
233
- }
234
-
235
- Button.List = function ({
236
- children,
237
- isWrapping = false,
238
- className,
239
- }: ButtonListProps) {
240
- const modifiedChildren = Children.map(children, (child) => {
241
- // Check if this child is a Button
242
- if (React.isValidElement<ButtonProps>(child) && child.type === Button) {
243
- // Clone the element with hasMagnifying set to false
244
- return cloneElement(child, { hasMagnifying: false });
245
- }
246
- return child;
247
- });
122
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
123
+ (
124
+ {
125
+ label,
126
+ icon,
127
+ isLoading = false,
128
+ variant = "primary",
129
+ tooltip,
130
+ isSelect = false,
131
+ isPulsing = false,
132
+ size,
133
+ ...props
134
+ },
135
+ ref
136
+ ) => {
137
+ const buttonSize = size || "sm";
138
+ const spinnerVariant = isLoading
139
+ ? spinnerVariantsMapIsLoading[variant] || "slate400"
140
+ : spinnerVariantsMap[variant] || "slate400";
141
+
142
+ const renderIcon = (visual: React.ComponentType, extraClass = "") => (
143
+ <Icon visual={visual} size={buttonSize} className={extraClass} />
144
+ );
248
145
 
249
- return (
250
- <div className={classNames(className ? className : "", "s-flex")}>
251
- <div
252
- className={classNames(
253
- "s-flex s-flex-row s-gap-2",
254
- isWrapping ? "s-flex-wrap" : "s-flex-nowrap"
146
+ const content = (
147
+ <>
148
+ {isLoading ? (
149
+ <div className="-s-mx-0.5">
150
+ <Spinner size={buttonSize} variant={spinnerVariant} />
151
+ </div>
152
+ ) : (
153
+ icon && renderIcon(icon, "-s-mx-0.5")
255
154
  )}
155
+ {label}
156
+ {isSelect && renderIcon(ChevronDownIcon, isLoading ? "" : "-s-mr-1")}
157
+ </>
158
+ );
159
+
160
+ const buttonElement = (
161
+ <MetaButton
162
+ ref={ref}
163
+ size={buttonSize}
164
+ variant={variant}
165
+ disabled={isLoading || props.disabled}
166
+ hasVisual={!!icon || isLoading}
167
+ className={isPulsing ? "s-animate-pulse" : ""}
168
+ style={
169
+ {
170
+ "--pulse-color": "#93C5FD",
171
+ "--duration": "1.5s",
172
+ } as React.CSSProperties
173
+ }
174
+ {...props}
256
175
  >
257
- {modifiedChildren}
258
- </div>
259
- </div>
260
- );
261
- };
176
+ {content}
177
+ </MetaButton>
178
+ );
179
+
180
+ return tooltip ? (
181
+ <TooltipProvider>
182
+ <TooltipRoot>
183
+ <TooltipTrigger>{buttonElement}</TooltipTrigger>
184
+ <TooltipContent>{tooltip}</TooltipContent>
185
+ </TooltipRoot>
186
+ </TooltipProvider>
187
+ ) : (
188
+ buttonElement
189
+ );
190
+ }
191
+ );
192
+
193
+ export { Button, buttonVariants, MetaButton };
@@ -128,7 +128,7 @@ export function Citation({
128
128
  >
129
129
  <IconButton
130
130
  icon={XCircleIcon}
131
- variant={type === "image" ? "white" : "tertiary"}
131
+ variant={type === "image" ? "primary" : "ghost"}
132
132
  onClick={onClose}
133
133
  />
134
134
  </div>
@@ -28,10 +28,9 @@ export function ConversationMessageActions({
28
28
  const buttonNodes = buttons?.map((button, i) => (
29
29
  <Button
30
30
  key={`message-button-${i}`}
31
- variant="tertiary"
31
+ variant="outline"
32
32
  size="xs"
33
33
  label={button.label}
34
- labelVisible={false}
35
34
  icon={button.icon}
36
35
  onClick={button.onClick}
37
36
  disabled={button.disabled || false}
@@ -165,13 +164,10 @@ function EmojiSelector({
165
164
  trigger={
166
165
  <div ref={buttonRef}>
167
166
  <Button
168
- variant="tertiary"
167
+ variant="outline"
169
168
  size="xs"
170
169
  icon={ReactionIcon}
171
- labelVisible={false}
172
- label="Reaction picker"
173
- disabledTooltip
174
- type="menu"
170
+ isSelect
175
171
  disabled={isSubmittingEmoji}
176
172
  />
177
173
  </div>
@@ -358,7 +358,7 @@ DataTable.Row = function Row({
358
358
  <IconButton
359
359
  icon={MoreIcon}
360
360
  size="sm"
361
- variant="tertiary"
361
+ variant="outline"
362
362
  className="s-m-1"
363
363
  />
364
364
  </DropdownMenu.Button>
@@ -518,7 +518,7 @@ DataTable.CellContentWithCopy = function CellContentWithCopy({
518
518
  <span className="s-truncate">{children}</span>
519
519
  <IconButton
520
520
  icon={isCopied ? ClipboardCheckIcon : ClipboardIcon}
521
- variant="tertiary"
521
+ variant="outline"
522
522
  onClick={async (e) => {
523
523
  e.stopPropagation();
524
524
  await handleCopy();
@@ -5,7 +5,7 @@ import ConfettiBackground from "@sparkle/components/ConfettiBackground";
5
5
  import Spinner from "@sparkle/components/Spinner";
6
6
  import { classNames } from "@sparkle/lib/utils";
7
7
 
8
- import { Button, ButtonVariantType } from "./Button";
8
+ import { Button } from "./Button";
9
9
 
10
10
  export type BaseDialogProps = {
11
11
  backgroundType?: "confetti" | "snow" | "none";
@@ -16,7 +16,7 @@ export type BaseDialogProps = {
16
16
  onValidate: () => void;
17
17
  title: string;
18
18
  validateLabel?: string;
19
- validateVariant?: ButtonVariantType;
19
+ validateVariant?: React.ComponentProps<typeof Button>["variant"];
20
20
  cancelLabel?: string;
21
21
  };
22
22
 
@@ -89,13 +89,13 @@ export function Dialog({
89
89
  </HeadlessDialog.Title>
90
90
  <div className="s-text-base s-text-element-700">{children}</div>
91
91
  <div className="s-flex s-w-full s-justify-end">
92
- <Button.List>
92
+ <div className="s-flex s-gap-2">
93
93
  {!isSaving && (
94
94
  <>
95
95
  {props.onCancel && (
96
96
  <Button
97
97
  label={props.cancelLabel ?? "Cancel"}
98
- variant="tertiary"
98
+ variant="outline"
99
99
  onClick={props.onCancel}
100
100
  />
101
101
  )}
@@ -108,7 +108,7 @@ export function Dialog({
108
108
  </>
109
109
  )}
110
110
  {isSaving && <Spinner variant="color" />}
111
- </Button.List>
111
+ </div>
112
112
  </div>
113
113
  {backgroundType !== "none" && (
114
114
  <div className="s-absolute s-bottom-0 s-left-0 s-right-0 s-top-0 s-z-0">
@@ -34,10 +34,9 @@ export function FilterChips<T extends string>({
34
34
  {filters.map((filterName) => (
35
35
  <Button
36
36
  label={filterName}
37
- variant={selectedFilter === filterName ? "primary" : "tertiary"}
37
+ variant={selectedFilter === filterName ? "primary" : "ghost"}
38
38
  key={filterName}
39
39
  size="xs"
40
- hasMagnifying={false}
41
40
  onClick={() => handleFilterClick(filterName)}
42
41
  />
43
42
  ))}