@health-samurai/react-components 0.0.0-alpha.5 → 0.0.0-alpha.7

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 (146) hide show
  1. package/dist/bundle.css +318 -31
  2. package/dist/src/components/button-dropdown.d.ts +10 -0
  3. package/dist/src/components/button-dropdown.d.ts.map +1 -0
  4. package/dist/src/components/button-dropdown.js +70 -0
  5. package/dist/src/components/button-dropdown.js.map +1 -0
  6. package/dist/src/components/button-dropdown.stories.d.ts +11 -0
  7. package/dist/src/components/button-dropdown.stories.d.ts.map +1 -0
  8. package/dist/src/components/button-dropdown.stories.js +48 -0
  9. package/dist/src/components/button-dropdown.stories.js.map +1 -0
  10. package/dist/src/components/code-editor/index.d.ts +3 -2
  11. package/dist/src/components/code-editor/index.d.ts.map +1 -1
  12. package/dist/src/components/code-editor/index.js +152 -4
  13. package/dist/src/components/code-editor/index.js.map +1 -1
  14. package/dist/src/components/code-editor.stories.d.ts +1 -0
  15. package/dist/src/components/code-editor.stories.d.ts.map +1 -1
  16. package/dist/src/components/code-editor.stories.js +252 -1
  17. package/dist/src/components/code-editor.stories.js.map +1 -1
  18. package/dist/src/components/copy-icon.d.ts +5 -1
  19. package/dist/src/components/copy-icon.d.ts.map +1 -1
  20. package/dist/src/components/copy-icon.js +41 -3
  21. package/dist/src/components/copy-icon.js.map +1 -1
  22. package/dist/src/components/data-table.d.ts +9 -0
  23. package/dist/src/components/data-table.d.ts.map +1 -0
  24. package/dist/src/components/data-table.js +66 -0
  25. package/dist/src/components/data-table.js.map +1 -0
  26. package/dist/src/components/data-table.stories.d.ts +7 -0
  27. package/dist/src/components/data-table.stories.d.ts.map +1 -0
  28. package/dist/src/components/data-table.stories.js +240 -0
  29. package/dist/src/components/data-table.stories.js.map +1 -0
  30. package/dist/src/components/fhir-structure-view.d.ts +34 -0
  31. package/dist/src/components/fhir-structure-view.d.ts.map +1 -0
  32. package/dist/src/components/fhir-structure-view.js +226 -0
  33. package/dist/src/components/fhir-structure-view.js.map +1 -0
  34. package/dist/src/components/fhir-structure-view.stories.d.ts +7 -0
  35. package/dist/src/components/fhir-structure-view.stories.d.ts.map +1 -0
  36. package/dist/src/components/fhir-structure-view.stories.js +447 -0
  37. package/dist/src/components/fhir-structure-view.stories.js.map +1 -0
  38. package/dist/src/components/request-line-editor.d.ts +3 -1
  39. package/dist/src/components/request-line-editor.d.ts.map +1 -1
  40. package/dist/src/components/request-line-editor.js +26 -5
  41. package/dist/src/components/request-line-editor.js.map +1 -1
  42. package/dist/src/components/segment-control.d.ts +16 -0
  43. package/dist/src/components/segment-control.d.ts.map +1 -0
  44. package/dist/src/components/segment-control.js +48 -0
  45. package/dist/src/components/segment-control.js.map +1 -0
  46. package/dist/src/components/segment-control.stories.d.ts +15 -0
  47. package/dist/src/components/segment-control.stories.d.ts.map +1 -0
  48. package/dist/src/components/segment-control.stories.js +33 -0
  49. package/dist/src/components/segment-control.stories.js.map +1 -0
  50. package/dist/src/components/split-button.d.ts +5 -0
  51. package/dist/src/components/split-button.d.ts.map +1 -0
  52. package/dist/src/components/split-button.js +12 -0
  53. package/dist/src/components/split-button.js.map +1 -0
  54. package/dist/src/components/split-button.stories.d.ts +7 -0
  55. package/dist/src/components/split-button.stories.d.ts.map +1 -0
  56. package/dist/src/components/split-button.stories.js +57 -0
  57. package/dist/src/components/split-button.stories.js.map +1 -0
  58. package/dist/src/components/tree-view.d.ts +16 -7
  59. package/dist/src/components/tree-view.d.ts.map +1 -1
  60. package/dist/src/components/tree-view.js +75 -19
  61. package/dist/src/components/tree-view.js.map +1 -1
  62. package/dist/src/components/tree-view.stories.d.ts +3 -3
  63. package/dist/src/components/tree-view.stories.d.ts.map +1 -1
  64. package/dist/src/components/tree-view.stories.js +14 -5
  65. package/dist/src/components/tree-view.stories.js.map +1 -1
  66. package/dist/src/icons.d.ts +6 -0
  67. package/dist/src/icons.d.ts.map +1 -1
  68. package/dist/src/icons.js +235 -3
  69. package/dist/src/icons.js.map +1 -1
  70. package/dist/src/index.d.ts +10 -0
  71. package/dist/src/index.d.ts.map +1 -1
  72. package/dist/src/index.js +10 -0
  73. package/dist/src/index.js.map +1 -1
  74. package/dist/src/shadcn/components/ui/alert-dialog.d.ts +3 -1
  75. package/dist/src/shadcn/components/ui/alert-dialog.d.ts.map +1 -1
  76. package/dist/src/shadcn/components/ui/alert-dialog.js +5 -2
  77. package/dist/src/shadcn/components/ui/alert-dialog.js.map +1 -1
  78. package/dist/src/shadcn/components/ui/badge.d.ts +1 -1
  79. package/dist/src/shadcn/components/ui/card.d.ts +5 -1
  80. package/dist/src/shadcn/components/ui/card.d.ts.map +1 -1
  81. package/dist/src/shadcn/components/ui/card.js +21 -8
  82. package/dist/src/shadcn/components/ui/card.js.map +1 -1
  83. package/dist/src/shadcn/components/ui/card.stories.d.ts +302 -1
  84. package/dist/src/shadcn/components/ui/card.stories.d.ts.map +1 -1
  85. package/dist/src/shadcn/components/ui/card.stories.js +23 -2
  86. package/dist/src/shadcn/components/ui/card.stories.js.map +1 -1
  87. package/dist/src/shadcn/components/ui/combobox.d.ts +13 -0
  88. package/dist/src/shadcn/components/ui/combobox.d.ts.map +1 -1
  89. package/dist/src/shadcn/components/ui/combobox.js +102 -0
  90. package/dist/src/shadcn/components/ui/combobox.js.map +1 -1
  91. package/dist/src/shadcn/components/ui/dropdown-menu.d.ts.map +1 -1
  92. package/dist/src/shadcn/components/ui/dropdown-menu.js +1 -1
  93. package/dist/src/shadcn/components/ui/dropdown-menu.js.map +1 -1
  94. package/dist/src/shadcn/components/ui/input.d.ts +3 -1
  95. package/dist/src/shadcn/components/ui/input.d.ts.map +1 -1
  96. package/dist/src/shadcn/components/ui/input.js +39 -1
  97. package/dist/src/shadcn/components/ui/input.js.map +1 -1
  98. package/dist/src/shadcn/components/ui/pagination.d.ts +8 -1
  99. package/dist/src/shadcn/components/ui/pagination.d.ts.map +1 -1
  100. package/dist/src/shadcn/components/ui/pagination.js +36 -19
  101. package/dist/src/shadcn/components/ui/pagination.js.map +1 -1
  102. package/dist/src/shadcn/components/ui/pagination.stories.d.ts.map +1 -1
  103. package/dist/src/shadcn/components/ui/pagination.stories.js +44 -37
  104. package/dist/src/shadcn/components/ui/pagination.stories.js.map +1 -1
  105. package/dist/src/shadcn/components/ui/table.d.ts.map +1 -1
  106. package/dist/src/shadcn/components/ui/table.js +1 -1
  107. package/dist/src/shadcn/components/ui/table.js.map +1 -1
  108. package/dist/src/shadcn/components/ui/tabs.d.ts.map +1 -1
  109. package/dist/src/shadcn/components/ui/tabs.js +1 -0
  110. package/dist/src/shadcn/components/ui/tabs.js.map +1 -1
  111. package/dist/src/shadcn/components/ui/tree.d.ts +10 -2
  112. package/dist/src/shadcn/components/ui/tree.d.ts.map +1 -1
  113. package/dist/src/shadcn/components/ui/tree.js +31 -8
  114. package/dist/src/shadcn/components/ui/tree.js.map +1 -1
  115. package/dist/src/typography.css +36 -8
  116. package/package.json +5 -1
  117. package/src/components/button-dropdown.stories.tsx +41 -0
  118. package/src/components/button-dropdown.tsx +95 -0
  119. package/src/components/code-editor/index.tsx +129 -4
  120. package/src/components/code-editor.stories.tsx +294 -0
  121. package/src/components/copy-icon.tsx +57 -3
  122. package/src/components/data-table.stories.tsx +89 -0
  123. package/src/components/data-table.tsx +120 -0
  124. package/src/components/fhir-structure-view.stories.tsx +439 -0
  125. package/src/components/fhir-structure-view.tsx +229 -0
  126. package/src/components/request-line-editor.tsx +30 -4
  127. package/src/components/segment-control.stories.tsx +29 -0
  128. package/src/components/segment-control.tsx +81 -0
  129. package/src/components/split-button.stories.tsx +49 -0
  130. package/src/components/split-button.tsx +17 -0
  131. package/src/components/tree-view.stories.tsx +20 -15
  132. package/src/components/tree-view.tsx +118 -15
  133. package/src/icons.tsx +245 -3
  134. package/src/index.tsx +10 -2
  135. package/src/shadcn/components/ui/alert-dialog.tsx +6 -2
  136. package/src/shadcn/components/ui/card.stories.tsx +17 -3
  137. package/src/shadcn/components/ui/card.tsx +52 -8
  138. package/src/shadcn/components/ui/combobox.tsx +127 -0
  139. package/src/shadcn/components/ui/dropdown-menu.tsx +1 -2
  140. package/src/shadcn/components/ui/input.tsx +119 -0
  141. package/src/shadcn/components/ui/pagination.stories.tsx +8 -2
  142. package/src/shadcn/components/ui/pagination.tsx +54 -3
  143. package/src/shadcn/components/ui/table.tsx +6 -1
  144. package/src/shadcn/components/ui/tabs.tsx +1 -0
  145. package/src/shadcn/components/ui/tree.tsx +63 -10
  146. package/src/typography.css +36 -8
@@ -118,6 +118,119 @@ export function Combobox({
118
118
  );
119
119
  }
120
120
 
121
+ interface MultiComboboxProps {
122
+ options: ComboboxOption[];
123
+ value?: string[];
124
+ onValueChange?: (value: string[]) => void;
125
+ placeholder?: string;
126
+ searchPlaceholder?: string;
127
+ emptyText?: string;
128
+ disabled?: boolean;
129
+ className?: string;
130
+ maxDisplay?: number;
131
+ }
132
+
133
+ export function MultiCombobox({
134
+ options,
135
+ value = [],
136
+ onValueChange,
137
+ placeholder = "Select options...",
138
+ searchPlaceholder = "Search...",
139
+ emptyText = "No options found.",
140
+ disabled = false,
141
+ className,
142
+ maxDisplay = 2,
143
+ }: MultiComboboxProps) {
144
+ const [open, setOpen] = React.useState(false);
145
+ const [searchValue, setSearchValue] = React.useState("");
146
+ const inputRef = React.useRef<HTMLInputElement>(null);
147
+
148
+ const filteredOptions = React.useMemo(() => {
149
+ if (!searchValue) return options;
150
+ return options.filter((option) =>
151
+ option.label.toLowerCase().includes(searchValue.toLowerCase()),
152
+ );
153
+ }, [options, searchValue]);
154
+
155
+ const selectedOptions = React.useMemo(
156
+ () => options.filter((option) => value.includes(option.value)),
157
+ [options, value],
158
+ );
159
+
160
+ const displayText = React.useMemo(() => {
161
+ if (selectedOptions.length === 0) return placeholder;
162
+ if (selectedOptions.length <= maxDisplay) {
163
+ return selectedOptions.map((opt) => opt.label).join(", ");
164
+ }
165
+ return `${selectedOptions
166
+ .slice(0, maxDisplay)
167
+ .map((opt) => opt.label)
168
+ .join(", ")} +${selectedOptions.length - maxDisplay}`;
169
+ }, [selectedOptions, placeholder, maxDisplay]);
170
+
171
+ const handleSelect = (selectedValue: string) => {
172
+ const newValue = value.includes(selectedValue)
173
+ ? value.filter((v) => v !== selectedValue)
174
+ : [...value, selectedValue];
175
+ onValueChange?.(newValue);
176
+ };
177
+
178
+ // Reset search when closing and auto-focus when opening
179
+ React.useEffect(() => {
180
+ if (!open) {
181
+ setSearchValue("");
182
+ } else {
183
+ // Auto-focus on search input when opening
184
+ setTimeout(() => {
185
+ inputRef.current?.focus();
186
+ }, 0);
187
+ }
188
+ }, [open]);
189
+
190
+ return (
191
+ <Select open={open} onOpenChange={setOpen}>
192
+ <SelectTrigger
193
+ className={`${className} ${displayText ? "!text-text-primary" : undefined}`}
194
+ disabled={disabled}
195
+ >
196
+ <SelectValue placeholder={displayText || placeholder} />
197
+ </SelectTrigger>
198
+ <SelectContent className="p-0 [&_[data-radix-select-viewport]]:p-0">
199
+ <Command className="w-full">
200
+ <CommandInput
201
+ ref={inputRef}
202
+ placeholder={searchPlaceholder}
203
+ value={searchValue}
204
+ onValueChange={setSearchValue}
205
+ />
206
+ <CommandList>
207
+ <CommandEmpty>{emptyText}</CommandEmpty>
208
+
209
+ {filteredOptions.map((option) => (
210
+ <CommandItem
211
+ key={option.value}
212
+ value={option.value}
213
+ data-state={
214
+ value.includes(option.value) ? "checked" : undefined
215
+ }
216
+ onSelect={handleSelect}
217
+ >
218
+ {option.label}
219
+ <CheckIcon
220
+ className={cn(
221
+ "ml-auto size-4",
222
+ value.includes(option.value) ? "opacity-100" : "opacity-0",
223
+ )}
224
+ />
225
+ </CommandItem>
226
+ ))}
227
+ </CommandList>
228
+ </Command>
229
+ </SelectContent>
230
+ </Select>
231
+ );
232
+ }
233
+
121
234
  // Demo component for Storybook
122
235
  const demoOptions = [
123
236
  { value: "next.js", label: "Next.js" },
@@ -140,3 +253,17 @@ export function ComboboxDemo() {
140
253
  />
141
254
  );
142
255
  }
256
+
257
+ export function MultiComboboxDemo() {
258
+ const [value, setValue] = React.useState<string[]>([]);
259
+
260
+ return (
261
+ <MultiCombobox
262
+ options={demoOptions}
263
+ value={value}
264
+ onValueChange={setValue}
265
+ placeholder="Select frameworks..."
266
+ searchPlaceholder="Search framework..."
267
+ />
268
+ );
269
+ }
@@ -21,8 +21,7 @@ const dropdownMenuContentStyles = cn(
21
21
  "border",
22
22
  "border-border-separator",
23
23
  // Spacing
24
- "px-2",
25
- "py-3",
24
+ "p-2",
26
25
  "space-y-0.5",
27
26
  // Shadow
28
27
  "shadow-lg",
@@ -105,6 +105,37 @@ const iconRightItemClasses = cn(
105
105
  "duration-300",
106
106
  );
107
107
 
108
+ // Prefix styles
109
+ const prefixClasses = cn(
110
+ // Borders
111
+ "border-border-primary",
112
+ "border",
113
+ "border-r-0",
114
+
115
+ // Background & Colors
116
+ "bg-bg-tertiary",
117
+ "text-text-tertiary",
118
+
119
+ // Layout & Flexbox
120
+ "flex",
121
+ "items-center",
122
+
123
+ // Spacing & Sizing
124
+ "rounded-l-md",
125
+ "px-3",
126
+ "py-1",
127
+
128
+ // Typography
129
+ "typo-body",
130
+
131
+ // Cursor & Interactions
132
+ "cursor-default",
133
+
134
+ // Transitions
135
+ "transition-colors",
136
+ "duration-300",
137
+ );
138
+
108
139
  // Suffix styles
109
140
  const suffixClasses = cn(
110
141
  // Borders
@@ -135,6 +166,36 @@ const suffixClasses = cn(
135
166
  "duration-300",
136
167
  );
137
168
 
169
+ // Disabled prefix styles
170
+ const prefixDisabledClasses = cn(
171
+ // Borders
172
+ "border-border-primary",
173
+ "border",
174
+
175
+ // Background & Colors
176
+ "bg-bg-tertiary",
177
+ "text-text-disabled",
178
+
179
+ // Layout & Flexbox
180
+ "flex",
181
+ "items-center",
182
+
183
+ // Spacing & Sizing
184
+ "rounded-l-md",
185
+ "px-3",
186
+ "py-1",
187
+
188
+ // Typography
189
+ "typo-body",
190
+
191
+ // Cursor & Interactions
192
+ "hover:cursor-not-allowed",
193
+
194
+ // Transitions
195
+ "transition-colors",
196
+ "duration-300",
197
+ );
198
+
138
199
  // Disabled suffix styles
139
200
  const suffixDisabledClasses = cn(
140
201
  // Borders
@@ -165,6 +226,36 @@ const suffixDisabledClasses = cn(
165
226
  "duration-300",
166
227
  );
167
228
 
229
+ // Invalid prefix styles
230
+ const prefixInvalidClasses = cn(
231
+ // Borders
232
+ "border-border-error",
233
+ "border",
234
+
235
+ // Background & Colors
236
+ "bg-bg-secondary",
237
+ "text-text-tertiary",
238
+
239
+ // Layout & Flexbox
240
+ "flex",
241
+ "items-center",
242
+
243
+ // Spacing & Sizing
244
+ "rounded-l-md",
245
+ "px-3",
246
+ "py-1",
247
+
248
+ // Typography
249
+ "typo-body",
250
+
251
+ // Cursor & Interactions
252
+ "cursor-default",
253
+
254
+ // Transitions
255
+ "transition-colors",
256
+ "duration-300",
257
+ );
258
+
168
259
  // Invalid suffix styles
169
260
  const suffixInvalidClasses = cn(
170
261
  // Borders
@@ -279,6 +370,17 @@ const inputVariants = cva(
279
370
  "pr-9",
280
371
  ),
281
372
  },
373
+ hasPrefix: {
374
+ true: cn(
375
+ // Border adjustments for prefix
376
+ "rounded-r-md",
377
+ "rounded-l-none",
378
+
379
+ // Focus adjustments
380
+ "focus-visible:ring-offset-0",
381
+ "focus-visible:border-l-1",
382
+ ),
383
+ },
282
384
  hasSuffix: {
283
385
  true: cn(
284
386
  // Border adjustments for suffix
@@ -309,6 +411,7 @@ const inputVariants = cva(
309
411
  },
310
412
  },
311
413
  defaultVariants: {
414
+ hasPrefix: false,
312
415
  hasSuffix: false,
313
416
  invalid: false,
314
417
  },
@@ -319,6 +422,7 @@ interface InputProps
319
422
  extends React.ComponentProps<"input">,
320
423
  VariantProps<typeof inputVariants> {
321
424
  type?: "text" | "password";
425
+ prefixValue?: React.ReactNode;
322
426
  suffix?: string;
323
427
  invalid?: boolean;
324
428
  leftSlot?: React.ReactNode;
@@ -329,6 +433,7 @@ function Input({
329
433
  className,
330
434
  type,
331
435
  invalid,
436
+ prefixValue,
332
437
  suffix,
333
438
  leftSlot,
334
439
  rightSlot,
@@ -390,6 +495,7 @@ function Input({
390
495
  invalid,
391
496
  hasLeftSlot: !!leftSlot,
392
497
  hasRightSlot: !!rightSlot,
498
+ hasPrefix: !!prefixValue,
393
499
  hasSuffix: !!suffix,
394
500
  className,
395
501
  }),
@@ -400,6 +506,19 @@ function Input({
400
506
 
401
507
  return (
402
508
  <div className="flex w-full min-w-0">
509
+ {prefixValue && (
510
+ <div
511
+ className={
512
+ props.disabled
513
+ ? prefixDisabledClasses
514
+ : invalid
515
+ ? prefixInvalidClasses
516
+ : prefixClasses
517
+ }
518
+ >
519
+ {prefixValue}
520
+ </div>
521
+ )}
403
522
  <div className="flex-1 relative">
404
523
  {renderLeftSlot()}
405
524
  {inputElement}
@@ -7,6 +7,7 @@ import {
7
7
  PaginationItem,
8
8
  PaginationLink,
9
9
  PaginationNext,
10
+ PaginationPageSizeSelector,
10
11
  PaginationPrevious,
11
12
  } from "#shadcn/components/ui/pagination";
12
13
 
@@ -20,6 +21,11 @@ type Story = StoryObj<typeof meta>;
20
21
  export const Demo = {
21
22
  render: () => (
22
23
  <Pagination>
24
+ <PaginationPageSizeSelector
25
+ pageSize={10}
26
+ onPageSizeChange={(_: number) => {}}
27
+ className="p-2"
28
+ />
23
29
  <PaginationContent>
24
30
  <PaginationItem>
25
31
  <PaginationPrevious href="#" />
@@ -33,10 +39,10 @@ export const Demo = {
33
39
  </PaginationLink>
34
40
  </PaginationItem>
35
41
  <PaginationItem>
36
- <PaginationLink href="#">3</PaginationLink>
42
+ <PaginationEllipsis />
37
43
  </PaginationItem>
38
44
  <PaginationItem>
39
- <PaginationEllipsis />
45
+ <PaginationLink href="#">3</PaginationLink>
40
46
  </PaginationItem>
41
47
  <PaginationItem>
42
48
  <PaginationNext href="#" />
@@ -1,10 +1,17 @@
1
1
  import {
2
+ ChevronDownIcon,
2
3
  ChevronLeftIcon,
3
4
  ChevronRightIcon,
4
5
  MoreHorizontalIcon,
5
6
  } from "lucide-react";
6
7
  import type * as React from "react";
7
8
  import { type Button, buttonVariants } from "#shadcn/components/ui/button";
9
+ import {
10
+ DropdownMenu,
11
+ DropdownMenuContent,
12
+ DropdownMenuItem,
13
+ DropdownMenuTrigger,
14
+ } from "#shadcn/components/ui/dropdown-menu";
8
15
  import { cn } from "#shadcn/lib/utils";
9
16
 
10
17
  function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
@@ -15,7 +22,7 @@ function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
15
22
  role="navigation"
16
23
  aria-label="pagination"
17
24
  data-slot="pagination"
18
- className={cn("mx-auto", "flex", "w-full", "justify-center", className)}
25
+ className={cn("flex", "justify-center", className)}
19
26
  {...props}
20
27
  />
21
28
  );
@@ -84,7 +91,6 @@ function PaginationPrevious({
84
91
  {...props}
85
92
  >
86
93
  <ChevronLeftIcon />
87
- <span className="hidden sm:block">Previous</span>
88
94
  </PaginationLink>
89
95
  );
90
96
  }
@@ -100,7 +106,6 @@ function PaginationNext({
100
106
  className={cn("gap-1", "px-2.5", "sm:pr-2.5", className)}
101
107
  {...props}
102
108
  >
103
- <span className="hidden sm:block">Next</span>
104
109
  <ChevronRightIcon />
105
110
  </PaginationLink>
106
111
  );
@@ -129,6 +134,51 @@ function PaginationEllipsis({
129
134
  );
130
135
  }
131
136
 
137
+ type PaginationPageSizeSelectorProps = {
138
+ pageSize: number;
139
+ onPageSizeChange: (pageSize: number) => void;
140
+ pageSizeOptions?: number[];
141
+ className?: string;
142
+ };
143
+
144
+ function PaginationPageSizeSelector({
145
+ pageSize,
146
+ onPageSizeChange,
147
+ pageSizeOptions = [10, 20, 50, 100],
148
+ className,
149
+ }: PaginationPageSizeSelectorProps) {
150
+ return (
151
+ <DropdownMenu>
152
+ <DropdownMenuTrigger
153
+ className={cn(
154
+ "flex",
155
+ "items-center",
156
+ "gap-1",
157
+ "text-sm",
158
+ "text-text-secondary",
159
+ "hover:text-text-primary",
160
+ "outline-none",
161
+ className,
162
+ )}
163
+ data-slot="pagination-page-size-selector"
164
+ >
165
+ {pageSize} / Page
166
+ <ChevronDownIcon className="size-4" />
167
+ </DropdownMenuTrigger>
168
+ <DropdownMenuContent>
169
+ {pageSizeOptions.map((option) => (
170
+ <DropdownMenuItem
171
+ key={option}
172
+ onClick={() => onPageSizeChange(option)}
173
+ >
174
+ {option}
175
+ </DropdownMenuItem>
176
+ ))}
177
+ </DropdownMenuContent>
178
+ </DropdownMenu>
179
+ );
180
+ }
181
+
132
182
  export {
133
183
  Pagination,
134
184
  PaginationContent,
@@ -137,4 +187,5 @@ export {
137
187
  PaginationPrevious,
138
188
  PaginationNext,
139
189
  PaginationEllipsis,
190
+ PaginationPageSizeSelector,
140
191
  };
@@ -3,7 +3,12 @@ import type * as React from "react";
3
3
  import { cn } from "#shadcn/lib/utils";
4
4
 
5
5
  // Table container styles
6
- const tableContainerStyles = cn("relative", "w-full", "overflow-x-auto");
6
+ const tableContainerStyles = cn(
7
+ "relative",
8
+ "h-full",
9
+ "w-full",
10
+ "overflow-auto",
11
+ );
7
12
 
8
13
  // Table styles
9
14
  const tableStyles = cn("w-full", "caption-bottom", "text-sm");
@@ -101,6 +101,7 @@ const tabsVariants = cva("", {
101
101
  `**:data-[slot=tabs-trigger]:max-w-80
102
102
  **:data-[slot=tabs-trigger]:w-60
103
103
  **:data-[slot=tabs-trigger]:min-w-40
104
+ **:data-[slot=tabs-trigger]:data-[state=inactive]:text-text-secondary
104
105
  **:data-[slot=tabs-trigger]:data-[state=inactive]:border-b-1
105
106
  **:data-[slot=tabs-trigger]:data-[state=inactive]:border-b-border-secondary
106
107
  **:data-[slot=tabs-trigger]:data-[state=inactive]:pt-[9px]
@@ -5,6 +5,12 @@ import * as React from "react";
5
5
 
6
6
  import { cn } from "#shadcn/lib/utils";
7
7
 
8
+ type WithMeta = {
9
+ meta?: {
10
+ lastNode?: boolean;
11
+ };
12
+ };
13
+
8
14
  // biome-ignore lint/suspicious/noExplicitAny: FIXME Origin UI as-is
9
15
  interface TreeContextValue<T = any> {
10
16
  indent: number;
@@ -42,7 +48,7 @@ function Tree({ indent = 20, tree, className, ...props }: TreeProps) {
42
48
  const mergedStyle = {
43
49
  ...propStyle,
44
50
  "--tree-indent": `${indent}px`,
45
- "--border": `var(--color-fg-disabled)`,
51
+ "--border": `var(--color-border-separator)`,
46
52
  } as React.CSSProperties;
47
53
 
48
54
  return (
@@ -87,7 +93,7 @@ function TreeItem<T = any>({
87
93
  "--tree-padding": `${item.getItemMeta().level * indent}px`,
88
94
  } as React.CSSProperties;
89
95
 
90
- const Comp = asChild ? Slot.Root : "button";
96
+ const Comp = asChild ? Slot.Root : "span";
91
97
 
92
98
  return (
93
99
  <TreeContext.Provider value={{ indent, currentItem: item }}>
@@ -124,7 +130,16 @@ function TreeItem<T = any>({
124
130
  : undefined
125
131
  }
126
132
  aria-expanded={item.isExpanded()}
127
- {...otherProps}
133
+ {...Object.fromEntries(
134
+ Object.entries(otherProps).filter(
135
+ ([key]) => key !== "onClick" && key !== "onDragStart",
136
+ ),
137
+ )}
138
+ onDragStart={(e) => {
139
+ if ((e.target as HTMLElement).dataset.slot === "drag-handle") {
140
+ item.getProps().onDragStart(e);
141
+ }
142
+ }}
128
143
  >
129
144
  {children}
130
145
  </Comp>
@@ -135,17 +150,22 @@ function TreeItem<T = any>({
135
150
  // biome-ignore lint/suspicious/noExplicitAny: FIXME Origin UI as-is
136
151
  interface TreeItemLabelProps<T = any>
137
152
  extends React.HTMLAttributes<HTMLSpanElement> {
153
+ hideChevron?: boolean;
154
+ disableHover?: boolean;
138
155
  item?: ItemInstance<T>;
156
+ horizontalLines?: boolean;
139
157
  }
140
158
 
141
- // biome-ignore lint/suspicious/noExplicitAny: FIXME Origin UI as-is
142
- function TreeItemLabel<T = any>({
159
+ function TreeItemLabel<T>({
143
160
  item: propItem,
144
161
  children,
145
162
  className,
163
+ disableHover,
164
+ horizontalLines,
165
+ hideChevron,
146
166
  ...props
147
- }: TreeItemLabelProps<T>) {
148
- const { currentItem } = useTreeContext<T>();
167
+ }: TreeItemLabelProps<T & WithMeta>) {
168
+ const { currentItem } = useTreeContext<T & WithMeta>();
149
169
  const item = propItem || currentItem;
150
170
 
151
171
  if (!item) {
@@ -153,17 +173,50 @@ function TreeItemLabel<T = any>({
153
173
  return null;
154
174
  }
155
175
 
176
+ const data = item.getItemData?.();
177
+ const isLastNode = data?.meta?.lastNode;
178
+ const itemMeta = item.getItemMeta();
179
+
156
180
  return (
157
181
  <span
158
182
  data-slot="tree-item-label"
159
183
  className={cn(
160
- "group/tree-item-label cursor-pointer in-data-[selected=true]:border-l-border-brand border-l-2 border-l-transparent in-focus-visible:ring-ring/50 bg-background hover:bg-bg-secondary in-data-[selected=true]:bg-bg-quaternary! text-text-secondary hover:text-text-primary in-data-[selected=true]:text-text-primary in-data-[drag-target=true]:bg-accent flex items-center gap-2 pr-2 pl-1.5 py-1.5 text-sm transition-colors not-in-data-[folder=true]:ps-2.5 in-focus-visible:ring-[3px] in-data-[search-match=true]:bg-blue-400/20! [&_svg]:pointer-events-none [&_svg]:shrink-0",
184
+ "group/tree-item-label relative select-text in-focus-visible:ring-ring/50 bg-background text-text-secondary in-data-[drag-target=true]:bg-accent flex items-center gap-2 pr-2 pl-2.5 py-1.5 text-sm transition-colors not-in-data-[folder=true]:ps-2.5 in-focus-visible:ring-[3px] in-data-[search-match=true]:bg-blue-400/20! [&_svg]:pointer-events-none [&_svg]:shrink-0",
185
+ !disableHover &&
186
+ "in-data-[selected=true]:bg-bg-secondary in-data-[selected=true]:text-text-primary",
187
+ !disableHover &&
188
+ item.isFolder() &&
189
+ "hover:bg-bg-secondary hover:text-text-primary cursor-pointer",
190
+ disableHover && "text-text-primary",
161
191
  className,
162
192
  )}
163
193
  {...props}
164
194
  >
165
195
  {item.isFolder() && (
166
- <ChevronDownIcon className="text-muted-foreground size-4 in-aria-[expanded=false]:-rotate-90" />
196
+ <button
197
+ type="button"
198
+ className="self-start mt-0.5 cursor-pointer"
199
+ onClick={() => {
200
+ item.isExpanded() ? item.collapse() : item.expand();
201
+ }}
202
+ >
203
+ <ChevronDownIcon className="text-muted-foreground size-4 in-aria-[expanded=false]:-rotate-90" />
204
+ </button>
205
+ )}
206
+ {!item.isFolder() && horizontalLines && (
207
+ <div
208
+ className={`w-5 min-w-5 h-px border-t mt-2 -ml-1 self-start`}
209
+ ></div>
210
+ )}
211
+ {item.isFolder() && item.isExpanded() && horizontalLines && (
212
+ <div
213
+ className={`absolute left-4.25 top-8 w-px min-h-full h-full border-l mt-2.25 self-start `}
214
+ ></div>
215
+ )}
216
+ {horizontalLines && (
217
+ <div
218
+ className={`absolute left-0 top-4.75 -m-[5px] border-t w-4 ${isLastNode ? "h-full bg-inherit " : ""} ${itemMeta.level === 0 ? "hidden" : ""}`}
219
+ ></div>
167
220
  )}
168
221
  {children ||
169
222
  (typeof item.getItemName === "function" ? item.getItemName() : null)}
@@ -197,4 +250,4 @@ function TreeDragLine({
197
250
  );
198
251
  }
199
252
 
200
- export { Tree, TreeItem, TreeItemLabel, TreeDragLine };
253
+ export { Tree, TreeItem, TreeItemLabel, TreeDragLine, type ItemInstance };
@@ -42,6 +42,18 @@ body {
42
42
  font-weight: var(--font-weight-normal);
43
43
  line-height: var(--font-leading-4);
44
44
  }
45
+ .typo-body-xs {
46
+ font-size: var(--font-size-xs);
47
+ font-family: var(--font-family-sans);
48
+ font-weight: var(--font-weight-normal);
49
+ line-height: var(--font-leading-3);
50
+ }
51
+ .typo-label-xs {
52
+ font-size: var(--font-size-xs);
53
+ font-family: var(--font-family-sans);
54
+ font-weight: var(--font-weight-medium);
55
+ line-height: var(--font-leading-3);
56
+ }
45
57
 
46
58
  .h1 {
47
59
  font-size: var(--font-size-4xl);
@@ -57,14 +69,6 @@ body {
57
69
  }
58
70
  }
59
71
 
60
- .h2 {
61
- font-size: var(--font-size-3xl);
62
- font-family: var(--font-family-sans);
63
- font-weight: var(--font-weight-semibold);
64
- line-height: var(--font-leading-none);
65
- letter-spacing: var(--font-tracking-tight);
66
- }
67
-
68
72
  .h3 {
69
73
  font-size: var(--font-size-2xl);
70
74
  font-family: var(--font-family-sans);
@@ -96,3 +100,27 @@ body {
96
100
  line-height: var(--font-leading-none);
97
101
  letter-spacing: var(--font-tracking-tight);
98
102
  }
103
+
104
+ .h2 {
105
+ font-size: var(--font-size-4xl);
106
+ font-family: var(--font-family-sans);
107
+ font-weight: var(--font-weight-bold);
108
+ line-height: var(--font-leading-10);
109
+ letter-spacing: var(--font-tracking-normal);
110
+ }
111
+
112
+ .body16 {
113
+ font-size: var(--font-size-base);
114
+ font-family: var(--font-family-sans);
115
+ font-weight: var(--font-weight-normal);
116
+ line-height: var(--font-leading-6);
117
+ letter-spacing: var(--font-tracking-normal);
118
+ }
119
+
120
+ .body12 {
121
+ font-size: var(--font-size-xs);
122
+ font-family: var(--font-family-sans);
123
+ font-weight: var(--font-weight-normal);
124
+ line-height: var(--font-leading-4);
125
+ letter-spacing: var(--font-tracking-normal);
126
+ }