@rovula/ui 0.1.21 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/cjs/bundle.css +175 -26
  2. package/dist/cjs/bundle.js +675 -675
  3. package/dist/cjs/bundle.js.map +1 -1
  4. package/dist/cjs/types/components/Badge/Badge.d.ts +40 -0
  5. package/dist/cjs/types/components/Badge/Badge.stories.d.ts +295 -0
  6. package/dist/cjs/types/components/Badge/Badge.styles.d.ts +7 -0
  7. package/dist/cjs/types/components/Badge/index.d.ts +2 -0
  8. package/dist/cjs/types/components/Dropdown/Dropdown.d.ts +4 -8
  9. package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +1 -6
  10. package/dist/cjs/types/components/DropdownMenu/DropdownMenu.d.ts +5 -1
  11. package/dist/cjs/types/components/DropdownMenu/DropdownMenu.stories.d.ts +16 -0
  12. package/dist/cjs/types/index.d.ts +3 -1
  13. package/dist/cjs/types/patterns/menu/Menu.d.ts +70 -0
  14. package/dist/cjs/types/{components/Menu → patterns/menu}/Menu.stories.d.ts +17 -10
  15. package/dist/cjs/types/utils/mergeRefs.d.ts +20 -0
  16. package/dist/components/Avatar/Avatar.styles.js +2 -2
  17. package/dist/components/Badge/Badge.js +36 -0
  18. package/dist/components/Badge/Badge.stories.js +51 -0
  19. package/dist/components/Badge/Badge.styles.js +62 -0
  20. package/dist/components/Badge/index.js +2 -0
  21. package/dist/components/Dropdown/Dropdown.js +54 -163
  22. package/dist/components/Dropdown/Dropdown.stories.js +29 -0
  23. package/dist/components/DropdownMenu/DropdownMenu.js +22 -9
  24. package/dist/components/DropdownMenu/DropdownMenu.stories.js +54 -10
  25. package/dist/components/TextInput/TextInput.js +6 -3
  26. package/dist/esm/bundle.css +175 -26
  27. package/dist/esm/bundle.js +1545 -1545
  28. package/dist/esm/bundle.js.map +1 -1
  29. package/dist/esm/types/components/Badge/Badge.d.ts +40 -0
  30. package/dist/esm/types/components/Badge/Badge.stories.d.ts +295 -0
  31. package/dist/esm/types/components/Badge/Badge.styles.d.ts +7 -0
  32. package/dist/esm/types/components/Badge/index.d.ts +2 -0
  33. package/dist/esm/types/components/Dropdown/Dropdown.d.ts +4 -8
  34. package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +1 -6
  35. package/dist/esm/types/components/DropdownMenu/DropdownMenu.d.ts +5 -1
  36. package/dist/esm/types/components/DropdownMenu/DropdownMenu.stories.d.ts +16 -0
  37. package/dist/esm/types/index.d.ts +3 -1
  38. package/dist/esm/types/patterns/menu/Menu.d.ts +70 -0
  39. package/dist/esm/types/{components/Menu → patterns/menu}/Menu.stories.d.ts +17 -10
  40. package/dist/esm/types/utils/mergeRefs.d.ts +20 -0
  41. package/dist/index.d.ts +116 -73
  42. package/dist/index.js +2 -1
  43. package/dist/patterns/menu/Menu.js +95 -0
  44. package/dist/patterns/menu/Menu.stories.js +611 -0
  45. package/dist/src/theme/global.css +289 -37
  46. package/dist/utils/mergeRefs.js +42 -0
  47. package/package.json +1 -1
  48. package/src/components/Avatar/Avatar.styles.ts +2 -2
  49. package/src/components/Badge/Badge.stories.tsx +128 -0
  50. package/src/components/Badge/Badge.styles.ts +70 -0
  51. package/src/components/Badge/Badge.tsx +103 -0
  52. package/src/components/Badge/index.ts +3 -0
  53. package/src/components/Dropdown/Dropdown.stories.tsx +170 -1
  54. package/src/components/Dropdown/Dropdown.tsx +186 -276
  55. package/src/components/DropdownMenu/DropdownMenu.stories.tsx +1050 -113
  56. package/src/components/DropdownMenu/DropdownMenu.tsx +116 -52
  57. package/src/components/TextInput/TextInput.tsx +6 -3
  58. package/src/index.ts +3 -1
  59. package/src/patterns/menu/Menu.stories.tsx +1100 -0
  60. package/src/patterns/menu/Menu.tsx +282 -0
  61. package/src/theme/themes/xspector/baseline.css +0 -1
  62. package/src/theme/tokens/baseline.css +2 -1
  63. package/src/theme/tokens/components/badge.css +54 -0
  64. package/src/theme/tokens/components/dropdown-menu.css +15 -4
  65. package/src/utils/mergeRefs.ts +46 -0
  66. package/dist/cjs/types/components/Menu/Menu.d.ts +0 -65
  67. package/dist/cjs/types/components/Menu/helpers.d.ts +0 -19
  68. package/dist/cjs/types/components/Menu/index.d.ts +0 -4
  69. package/dist/components/Menu/Menu.js +0 -64
  70. package/dist/components/Menu/Menu.stories.js +0 -406
  71. package/dist/components/Menu/helpers.js +0 -28
  72. package/dist/components/Menu/index.js +0 -3
  73. package/dist/esm/types/components/Menu/Menu.d.ts +0 -65
  74. package/dist/esm/types/components/Menu/helpers.d.ts +0 -19
  75. package/dist/esm/types/components/Menu/index.d.ts +0 -4
  76. package/src/components/Menu/Menu.stories.tsx +0 -586
  77. package/src/components/Menu/Menu.tsx +0 -235
  78. package/src/components/Menu/helpers.ts +0 -45
  79. package/src/components/Menu/index.ts +0 -7
  80. package/src/theme/themes/xspector/components/dropdown-menu.css +0 -28
@@ -27,13 +27,13 @@ const DropdownMenuSubTrigger = React.forwardRef<
27
27
  <DropdownMenuPrimitive.SubTrigger
28
28
  ref={ref}
29
29
  className={cn(
30
- "relative flex gap-4 cursor-pointer select-none box-border items-center py-4 pl-9 pr-4 typography-subtitle4 outline-none transition-colors data-[disabled]:pointer-events-none",
30
+ "relative flex gap-4 cursor-pointer select-none box-border items-center py-4 pl-4 pr-4 typography-subtitle4 outline-none transition-colors data-[disabled]:pointer-events-none",
31
31
  "bg-[var(--dropdown-menu-default-bg)] text-[var(--dropdown-menu-default-text)]",
32
32
  "active:opacity-75",
33
33
  "focus:!bg-[var(--dropdown-menu-hover-bg)] focus:!text-[var(--dropdown-menu-hover-text)]",
34
34
  "data-[disabled]:!bg-[var(--dropdown-menu-disabled-bg)] data-[disabled]:!text-[var(--dropdown-menu-disabled-text)]",
35
35
  inset && "pl-8",
36
- className
36
+ className,
37
37
  )}
38
38
  {...props}
39
39
  >
@@ -52,7 +52,7 @@ const DropdownMenuSubContent = React.forwardRef<
52
52
  ref={ref}
53
53
  className={cn(
54
54
  "z-50 min-w-[154px] overflow-hidden rounded-lg bg-modal-surface text-text-contrast-low data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
55
- className
55
+ className,
56
56
  )}
57
57
  {...props}
58
58
  style={{
@@ -74,7 +74,7 @@ const DropdownMenuContent = React.forwardRef<
74
74
  sideOffset={sideOffset}
75
75
  className={cn(
76
76
  "z-50 min-w-[154px] overflow-hidden rounded-lg bg-modal-surface text-text-contrast-low data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
77
- className
77
+ className,
78
78
  )}
79
79
  {...props}
80
80
  style={{
@@ -90,45 +90,88 @@ const DropdownMenuItem = React.forwardRef<
90
90
  React.ElementRef<typeof DropdownMenuPrimitive.Item>,
91
91
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
92
92
  inset?: boolean;
93
+ selected?: boolean;
94
+ icon?: React.ReactNode;
93
95
  }
94
- >(({ className, inset, ...props }, ref) => (
95
- <DropdownMenuPrimitive.Item
96
- ref={ref}
97
- className={cn(
98
- "relative flex gap-4 cursor-pointer select-none box-border items-center py-4 pl-9 pr-xxl typography-subtitle4 outline-none transition-colors data-[disabled]:pointer-events-none",
99
- "bg-[var(--dropdown-menu-default-bg)] text-[var(--dropdown-menu-default-text)]",
100
- "active:opacity-75",
101
- "focus:!bg-[var(--dropdown-menu-hover-bg)] focus:!text-[var(--dropdown-menu-hover-text)]",
102
- "data-[disabled]:!bg-[var(--dropdown-menu-disabled-bg)] data-[disabled]:!text-[var(--dropdown-menu-disabled-text)]",
103
- inset && "pl-8",
104
- className
105
- )}
106
- {...props}
107
- />
108
- ));
96
+ >(({ className, inset, selected, icon, children, ...props }, ref) => {
97
+ const hasIcon = !!icon;
98
+ return (
99
+ <DropdownMenuPrimitive.Item
100
+ ref={ref}
101
+ className={cn(
102
+ "relative flex cursor-pointer select-none box-border items-center py-4 pl-4 pr-8 typography-subtitle4 outline-none transition-colors data-[disabled]:pointer-events-none",
103
+ "bg-[var(--dropdown-menu-default-bg)] text-[var(--dropdown-menu-default-text)]",
104
+ "active:opacity-75",
105
+ "focus:!bg-[var(--dropdown-menu-hover-bg)] focus:!text-[var(--dropdown-menu-hover-text)]",
106
+ selected &&
107
+ "bg-[var(--dropdown-menu-selected-bg)] text-[var(--dropdown-menu-selected-text)] typography-subtitle5",
108
+ "data-[disabled]:!bg-[var(--dropdown-menu-disabled-bg)] data-[disabled]:!text-[var(--dropdown-menu-disabled-text)]",
109
+ inset && "pl-8",
110
+ className,
111
+ hasIcon ? "gap-4" : "gap-1",
112
+ )}
113
+ {...props}
114
+ >
115
+ <div className="flex shrink-0 flex-row gap-1">
116
+ <span className="size-4 flex items-center justify-center">
117
+ {selected && (
118
+ <Icon
119
+ type="heroicons"
120
+ name="check"
121
+ className="size-4 text-[var(--dropdown-menu-selected-text)]"
122
+ />
123
+ )}
124
+ </span>
125
+ {icon}
126
+ </div>
127
+ {children}
128
+ </DropdownMenuPrimitive.Item>
129
+ );
130
+ });
109
131
  DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
110
132
 
111
133
  const DropdownMenuCheckboxItem = React.forwardRef<
112
134
  React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
113
135
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
114
- >(({ className, children, checked, ...props }, ref) => (
136
+ >(({ className, children, checked, disabled, ...props }, ref) => (
115
137
  <DropdownMenuPrimitive.CheckboxItem
116
138
  ref={ref}
117
139
  className={cn(
118
- "relative flex gap-4 cursor-pointer select-none box-border items-center py-4 pl-9 pr-xxl typography-subtitle4 outline-none transition-colors data-[disabled]:pointer-events-none",
140
+ "relative flex gap-3 cursor-pointer select-none box-border items-center py-4 pl-4 pr-8 typography-subtitle4 outline-none transition-colors data-[disabled]:pointer-events-none",
119
141
  "bg-[var(--dropdown-menu-default-bg)] text-[var(--dropdown-menu-default-text)]",
120
142
  "active:opacity-75",
121
143
  "focus:!bg-[var(--dropdown-menu-hover-bg)] focus:!text-[var(--dropdown-menu-hover-text)]",
122
- "data-[state='checked']:bg-[var(--dropdown-menu-selected-bg)] data-[state='checked']:text-[var(--dropdown-menu-selected-text)] data-[state='checked']:typography-subtitle5",
144
+ "data-[state='checked']:bg-[var(--dropdown-menu-selected-bg)] data-[state='checked']:text-[var(--dropdown-menu-selected-text)]",
123
145
  "data-[disabled]:!bg-[var(--dropdown-menu-disabled-bg)] data-[disabled]:!text-[var(--dropdown-menu-disabled-text)]",
124
- className
146
+ className,
125
147
  )}
126
148
  checked={checked}
149
+ disabled={disabled}
127
150
  {...props}
128
151
  >
129
- <span className="absolute left-4 flex items-center justify-center">
152
+ <span
153
+ className={cn(
154
+ "shrink-0 size-4 rounded-[2px] border flex items-center justify-center transition-all overflow-hidden",
155
+ checked &&
156
+ !disabled &&
157
+ "bg-[var(--dropdown-menu-checkbox-checked-bg)] border-[var(--dropdown-menu-checkbox-checked-bg)]",
158
+ checked &&
159
+ disabled &&
160
+ "bg-[var(--dropdown-menu-checkbox-disabled-checked-bg)] border-transparent",
161
+ !checked &&
162
+ disabled &&
163
+ "border-[var(--dropdown-menu-checkbox-disabled-border)]",
164
+ !checked &&
165
+ !disabled &&
166
+ "border-[var(--dropdown-menu-checkbox-border)]",
167
+ )}
168
+ >
130
169
  <DropdownMenuPrimitive.ItemIndicator>
131
- <Icon type="heroicons" name="check" className="size-4" />
170
+ <Icon
171
+ type="heroicons"
172
+ name="check"
173
+ className="size-3 text-[var(--dropdown-menu-checkbox-checked-icon)]"
174
+ />
132
175
  </DropdownMenuPrimitive.ItemIndicator>
133
176
  </span>
134
177
  {children}
@@ -139,29 +182,50 @@ DropdownMenuCheckboxItem.displayName =
139
182
 
140
183
  const DropdownMenuRadioItem = React.forwardRef<
141
184
  React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
142
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
143
- >(({ className, children, ...props }, ref) => (
144
- <DropdownMenuPrimitive.RadioItem
145
- ref={ref}
146
- className={cn(
147
- "relative flex gap-4 cursor-pointer select-none box-border items-center py-4 pl-9 pr-xxl typography-subtitle4 outline-none transition-colors data-[disabled]:pointer-events-none",
148
- "bg-[var(--dropdown-menu-default-bg)] text-[var(--dropdown-menu-default-text)]",
149
- "active:opacity-75",
150
- "focus:!bg-[var(--dropdown-menu-hover-bg)] focus:!text-[var(--dropdown-menu-hover-text)]",
151
- "data-[state='checked']:bg-[var(--dropdown-menu-selected-bg)] data-[state='checked']:text-[var(--dropdown-menu-selected-text)] data-[state='checked']:typography-subtitle5",
152
- "data-[disabled]:!bg-[var(--dropdown-menu-disabled-bg)] data-[disabled]:!text-[var(--dropdown-menu-disabled-text)]",
153
- className
154
- )}
155
- {...props}
156
- >
157
- <span className="absolute left-4 flex items-center justify-center">
158
- <DropdownMenuPrimitive.ItemIndicator>
159
- <Icon type="heroicons" name="circle" className="h-2 w-2 fill-current" />
160
- </DropdownMenuPrimitive.ItemIndicator>
161
- </span>
162
- {children}
163
- </DropdownMenuPrimitive.RadioItem>
164
- ));
185
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> & {
186
+ icon?: React.ReactNode;
187
+ }
188
+ >(({ className, children, disabled, icon, ...props }, ref) => {
189
+ const hasIconSlot = !!icon;
190
+
191
+ return (
192
+ <DropdownMenuPrimitive.RadioItem
193
+ ref={ref}
194
+ className={cn(
195
+ "relative flex cursor-pointer select-none box-border items-center py-4 pl-4 pr-8 typography-subtitle4 outline-none transition-colors data-[disabled]:pointer-events-none",
196
+ "bg-[var(--dropdown-menu-default-bg)] text-[var(--dropdown-menu-default-text)]",
197
+ "active:opacity-75",
198
+ "focus:!bg-[var(--dropdown-menu-hover-bg)] focus:!text-[var(--dropdown-menu-hover-text)]",
199
+ "data-[state='checked']:bg-[var(--dropdown-menu-selected-bg)] data-[state='checked']:text-[var(--dropdown-menu-selected-text)]",
200
+ "data-[disabled]:!bg-[var(--dropdown-menu-disabled-bg)] data-[disabled]:!text-[var(--dropdown-menu-radio-disabled-text)]",
201
+ "data-[state='checked']:data-[disabled]:!text-[var(--dropdown-menu-radio-selected-disabled-text)]",
202
+ className,
203
+ hasIconSlot ? "gap-4" : "gap-1",
204
+ )}
205
+ {...props}
206
+ disabled={disabled}
207
+ >
208
+ <div className="flex shrink-0 flex-row gap-1">
209
+ <span className="size-4">
210
+ <DropdownMenuPrimitive.ItemIndicator className="shrink-0">
211
+ <Icon
212
+ type="heroicons"
213
+ name="check"
214
+ className={cn(
215
+ "size-4",
216
+ disabled
217
+ ? "text-[var(--dropdown-menu-radio-selected-disabled-text)]"
218
+ : "text-[var(--dropdown-menu-selected-text)]",
219
+ )}
220
+ />
221
+ </DropdownMenuPrimitive.ItemIndicator>
222
+ </span>
223
+ {icon}
224
+ </div>
225
+ {children}
226
+ </DropdownMenuPrimitive.RadioItem>
227
+ );
228
+ });
165
229
  DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
166
230
 
167
231
  const DropdownMenuLabel = React.forwardRef<
@@ -173,9 +237,9 @@ const DropdownMenuLabel = React.forwardRef<
173
237
  <DropdownMenuPrimitive.Label
174
238
  ref={ref}
175
239
  className={cn(
176
- "px-3 pt-4 pb-2 typography-small4 text-text-g-contrast-high",
240
+ "px-3 py-2 typography-small4 text-text-g-contrast-high",
177
241
  inset && "pl-8",
178
- className
242
+ className,
179
243
  )}
180
244
  {...props}
181
245
  />
@@ -189,8 +253,8 @@ const DropdownMenuSeparator = React.forwardRef<
189
253
  <DropdownMenuPrimitive.Separator
190
254
  ref={ref}
191
255
  className={cn(
192
- "-mx-2 my-2 h-px bg-[var(--dropdown-menu-seperator-bg)]",
193
- className
256
+ "h-px bg-[var(--dropdown-menu-seperator-bg)]",
257
+ className,
194
258
  )}
195
259
  {...props}
196
260
  />
@@ -2,13 +2,13 @@ import React, {
2
2
  ReactNode,
3
3
  forwardRef,
4
4
  useCallback,
5
- useImperativeHandle,
6
5
  useMemo,
7
6
  useRef,
8
7
  FocusEvent,
9
8
  KeyboardEvent,
10
9
  ChangeEvent,
11
10
  } from "react";
11
+ import { useStableMergedRef } from "@/utils/mergeRefs";
12
12
  import {
13
13
  helperTextVariant,
14
14
  iconActionVariant,
@@ -111,6 +111,10 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
111
111
  ) => {
112
112
  const inputRef = useRef<HTMLInputElement>(null);
113
113
  const _id = id || `${type}-${label}-input`;
114
+
115
+ // Stable merged ref — identity never changes so Headless UI (or any library
116
+ // that watches refs) won't trigger detach/re-attach loops.
117
+ const stableRef = useStableMergedRef(ref, inputRef);
114
118
  const hasLeftSectionIcon = !!startIcon || !!renderStartIcon;
115
119
  const hasRightSectionIcon = !!endIcon || !!renderEndIcon;
116
120
  const feedbackStatus =
@@ -183,7 +187,6 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
183
187
  position: "end",
184
188
  });
185
189
 
186
- useImperativeHandle(ref, () => inputRef?.current as HTMLInputElement);
187
190
 
188
191
  const handleChange = useCallback(
189
192
  (e: ChangeEvent<HTMLInputElement>) => {
@@ -382,7 +385,7 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
382
385
  <input
383
386
  {...props}
384
387
  placeholder=" "
385
- ref={inputRef}
388
+ ref={stableRef}
386
389
  type={type}
387
390
  id={_id}
388
391
  disabled={disabled}
package/src/index.ts CHANGED
@@ -19,6 +19,7 @@ export { Navbar } from "./components/Navbar";
19
19
  export { Footer } from "./components/Footer";
20
20
  export { default as ActionButton } from "./components/ActionButton/ActionButton";
21
21
  export { Avatar, AvatarGroup } from "./components/Avatar";
22
+ export { Badge, SeverityBadge } from "./components/Badge";
22
23
  export { Collapsible } from "./components/Collapsible";
23
24
  export { Calendar } from "./components/Calendar";
24
25
  export { default as DatePicker } from "./components/DatePicker/DatePicker";
@@ -41,7 +42,6 @@ export * from "./components/InputFilter/InputFilter";
41
42
  export * from "./components/Slider/Slider";
42
43
  export * from "./components/Switch/Switch";
43
44
  export * from "./components/DropdownMenu/DropdownMenu";
44
- export * from "./components/Menu/Menu";
45
45
  export * from "./components/Tooltip/Tooltip";
46
46
  export * from "./components/Tooltip/TooltipSimple";
47
47
  export * from "./components/Toast/Toast";
@@ -56,6 +56,7 @@ export * from "./components/Form";
56
56
  // Patterns
57
57
  export * from "./patterns/confirm-dialog/ConfirmDialog";
58
58
  export * from "./patterns/form-dialog/FormDialog";
59
+ export * from "./patterns/menu/Menu";
59
60
 
60
61
  // Export component types
61
62
  export type { ButtonProps } from "./components/Button/Button";
@@ -74,6 +75,7 @@ export type { NavbarProps, NavbarVariant } from "./components/Navbar/Navbar";
74
75
  export type { FooterProps, FooterVariant } from "./components/Footer/Footer";
75
76
  export type { AvatarProps } from "./components/Avatar/Avatar";
76
77
  export type { AvatarGroupProps } from "./components/Avatar/AvatarGroup";
78
+ export type { BadgeProps, BadgeColor, SeverityBadgeProps, SeverityLevel } from "./components/Badge/Badge";
77
79
 
78
80
  // UTILS
79
81
  export {