@blenx-dev/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/LICENSE +21 -0
  3. package/package.json +49 -0
  4. package/src/DataTable/data-table-column-toggle.tsx +73 -0
  5. package/src/DataTable/data-table-empty.tsx +27 -0
  6. package/src/DataTable/data-table-error.tsx +25 -0
  7. package/src/DataTable/data-table-infinite-loader.tsx +73 -0
  8. package/src/DataTable/data-table-loading.tsx +67 -0
  9. package/src/DataTable/data-table-pagination.tsx +80 -0
  10. package/src/DataTable/data-table-toolbar.tsx +62 -0
  11. package/src/DataTable/data-table.css.ts +420 -0
  12. package/src/DataTable/data-table.tsx +507 -0
  13. package/src/DataTable/index.ts +24 -0
  14. package/src/DataTable/types.ts +169 -0
  15. package/src/DataTable/use-infinite-scroll.ts +67 -0
  16. package/src/components/Accordion/accordion.css.ts +84 -0
  17. package/src/components/Accordion/accordion.tsx +87 -0
  18. package/src/components/Accordion/index.ts +8 -0
  19. package/src/components/Alert/alert.css.ts +29 -0
  20. package/src/components/Alert/alert.tsx +40 -0
  21. package/src/components/Alert/index.ts +1 -0
  22. package/src/components/AlertDialog/alert-dialog.css.ts +62 -0
  23. package/src/components/AlertDialog/alert-dialog.tsx +199 -0
  24. package/src/components/AlertDialog/index.ts +1 -0
  25. package/src/components/AspectRatio/aspect-ratio.css.ts +7 -0
  26. package/src/components/AspectRatio/aspect-ratio.tsx +20 -0
  27. package/src/components/AspectRatio/index.ts +1 -0
  28. package/src/components/Autocomplete/autocomplete.css.ts +167 -0
  29. package/src/components/Autocomplete/autocomplete.tsx +226 -0
  30. package/src/components/Autocomplete/index.ts +1 -0
  31. package/src/components/Avatar/avatar.css.ts +65 -0
  32. package/src/components/Avatar/avatar.tsx +44 -0
  33. package/src/components/Avatar/index.ts +1 -0
  34. package/src/components/Badge/badge.css.ts +180 -0
  35. package/src/components/Badge/badge.tsx +47 -0
  36. package/src/components/Badge/index.ts +1 -0
  37. package/src/components/Box/box.css.ts +5 -0
  38. package/src/components/Box/box.tsx +21 -0
  39. package/src/components/Box/index.ts +1 -0
  40. package/src/components/Breadcrumbs/breadcrumbs.css.ts +72 -0
  41. package/src/components/Breadcrumbs/breadcrumbs.tsx +79 -0
  42. package/src/components/Breadcrumbs/index.ts +9 -0
  43. package/src/components/Button/button.css.ts +200 -0
  44. package/src/components/Button/button.tsx +55 -0
  45. package/src/components/Button/index.ts +1 -0
  46. package/src/components/Calendar/calendar.css.ts +187 -0
  47. package/src/components/Calendar/calendar.tsx +143 -0
  48. package/src/components/Calendar/index.ts +1 -0
  49. package/src/components/Card/card.tsx +32 -0
  50. package/src/components/Card/index.ts +1 -0
  51. package/src/components/Checkbox/checkbox.css.ts +76 -0
  52. package/src/components/Checkbox/checkbox.tsx +94 -0
  53. package/src/components/Checkbox/index.ts +1 -0
  54. package/src/components/CloseButton/close-button.css.ts +11 -0
  55. package/src/components/CloseButton/close-button.tsx +15 -0
  56. package/src/components/CloseButton/index.ts +2 -0
  57. package/src/components/ColorPicker/color-picker.tsx +123 -0
  58. package/src/components/ColorPicker/index.ts +1 -0
  59. package/src/components/ColorSwatch/color-swatch.tsx +21 -0
  60. package/src/components/ColorSwatch/index.ts +1 -0
  61. package/src/components/Combobox/combobox.css.ts +333 -0
  62. package/src/components/Combobox/combobox.tsx +350 -0
  63. package/src/components/Combobox/index.ts +1 -0
  64. package/src/components/Command/command.css.ts +130 -0
  65. package/src/components/Command/command.tsx +413 -0
  66. package/src/components/Command/index.ts +7 -0
  67. package/src/components/Container/container.css.ts +41 -0
  68. package/src/components/Container/container.tsx +25 -0
  69. package/src/components/Container/index.ts +1 -0
  70. package/src/components/CopyButton/copy-button.css.ts +11 -0
  71. package/src/components/CopyButton/copy-button.tsx +45 -0
  72. package/src/components/CopyButton/index.ts +2 -0
  73. package/src/components/DatePicker/date-picker.tsx +75 -0
  74. package/src/components/DatePicker/index.ts +1 -0
  75. package/src/components/Dialog/dialog.css.ts +57 -0
  76. package/src/components/Dialog/dialog.tsx +181 -0
  77. package/src/components/Dialog/index.ts +1 -0
  78. package/src/components/Drawer/drawer.css.ts +404 -0
  79. package/src/components/Drawer/drawer.tsx +573 -0
  80. package/src/components/Drawer/index.ts +1 -0
  81. package/src/components/Field/field.css.ts +35 -0
  82. package/src/components/Field/field.tsx +101 -0
  83. package/src/components/Field/index.ts +1 -0
  84. package/src/components/Grid/grid.css.ts +12 -0
  85. package/src/components/Grid/grid.tsx +32 -0
  86. package/src/components/Grid/index.ts +1 -0
  87. package/src/components/Icon/icon.css.ts +10 -0
  88. package/src/components/Icon/icon.tsx +15 -0
  89. package/src/components/Icon/index.ts +1 -0
  90. package/src/components/IconButton/icon-button.css.ts +6 -0
  91. package/src/components/IconButton/icon-button.tsx +11 -0
  92. package/src/components/IconButton/index.ts +2 -0
  93. package/src/components/Input/index.ts +1 -0
  94. package/src/components/Input/input.css.ts +72 -0
  95. package/src/components/Input/input.tsx +50 -0
  96. package/src/components/InputGroup/index.ts +1 -0
  97. package/src/components/InputGroup/input-group.css.ts +156 -0
  98. package/src/components/InputGroup/input-group.tsx +133 -0
  99. package/src/components/Menu/index.ts +1 -0
  100. package/src/components/Menu/menu.css.ts +121 -0
  101. package/src/components/Menu/menu.tsx +115 -0
  102. package/src/components/OTPField/index.ts +1 -0
  103. package/src/components/OTPField/otp-field.css.ts +54 -0
  104. package/src/components/OTPField/otp-field.tsx +46 -0
  105. package/src/components/Popover/index.ts +1 -0
  106. package/src/components/Popover/popover.css.ts +81 -0
  107. package/src/components/Popover/popover.tsx +113 -0
  108. package/src/components/Progress/index.ts +7 -0
  109. package/src/components/Progress/progress.css.ts +37 -0
  110. package/src/components/Progress/progress.tsx +62 -0
  111. package/src/components/Radio/index.ts +1 -0
  112. package/src/components/Radio/radio.css.ts +72 -0
  113. package/src/components/Radio/radio.tsx +49 -0
  114. package/src/components/ScrollArea/index.ts +1 -0
  115. package/src/components/ScrollArea/scroll-area.css.ts +79 -0
  116. package/src/components/ScrollArea/scroll-area.tsx +96 -0
  117. package/src/components/SegmentedControl/index.ts +1 -0
  118. package/src/components/SegmentedControl/segmented-control.tsx +42 -0
  119. package/src/components/Select/index.ts +1 -0
  120. package/src/components/Select/select.css.ts +182 -0
  121. package/src/components/Select/select.tsx +165 -0
  122. package/src/components/Separator/index.ts +1 -0
  123. package/src/components/Separator/separator.css.ts +59 -0
  124. package/src/components/Separator/separator.tsx +34 -0
  125. package/src/components/Sheet/index.ts +1 -0
  126. package/src/components/Sheet/sheet.css.ts +184 -0
  127. package/src/components/Sheet/sheet.tsx +215 -0
  128. package/src/components/Slider/index.ts +1 -0
  129. package/src/components/Slider/slider.css.ts +81 -0
  130. package/src/components/Slider/slider.tsx +100 -0
  131. package/src/components/Spinner/index.ts +1 -0
  132. package/src/components/Spinner/spinner.css.ts +17 -0
  133. package/src/components/Spinner/spinner.tsx +15 -0
  134. package/src/components/Splitter/index.ts +1 -0
  135. package/src/components/Splitter/splitter.css.ts +69 -0
  136. package/src/components/Splitter/splitter.tsx +521 -0
  137. package/src/components/Stack/index.ts +1 -0
  138. package/src/components/Stack/stack.css.ts +42 -0
  139. package/src/components/Stack/stack.tsx +32 -0
  140. package/src/components/Surface/index.ts +1 -0
  141. package/src/components/Surface/surface.css.ts +40 -0
  142. package/src/components/Surface/surface.tsx +19 -0
  143. package/src/components/Switch/index.ts +1 -0
  144. package/src/components/Switch/switch.css.ts +46 -0
  145. package/src/components/Switch/switch.tsx +25 -0
  146. package/src/components/Table/index.ts +2 -0
  147. package/src/components/Table/table.css.ts +71 -0
  148. package/src/components/Table/table.tsx +117 -0
  149. package/src/components/Tabs/index.ts +1 -0
  150. package/src/components/Tabs/tabs.css.ts +250 -0
  151. package/src/components/Tabs/tabs.tsx +119 -0
  152. package/src/components/Text/index.ts +2 -0
  153. package/src/components/Text/text.css.ts +118 -0
  154. package/src/components/Text/text.tsx +66 -0
  155. package/src/components/Textarea/index.ts +1 -0
  156. package/src/components/Textarea/textarea.css.ts +66 -0
  157. package/src/components/Textarea/textarea.tsx +48 -0
  158. package/src/components/Toggle/index.ts +1 -0
  159. package/src/components/Toggle/toggle.css.ts +91 -0
  160. package/src/components/Toggle/toggle.tsx +44 -0
  161. package/src/components/ToggleGroup/index.ts +1 -0
  162. package/src/components/ToggleGroup/toggle-group.css.ts +77 -0
  163. package/src/components/ToggleGroup/toggle-group.tsx +131 -0
  164. package/src/components/index.ts +54 -0
  165. package/src/index.ts +3 -0
  166. package/src/utils/drawer-styles.css.ts +85 -0
  167. package/src/utils/heights.ts +16 -0
  168. package/src/utils/sprinkles.css.ts +197 -0
  169. package/src/utils/sprinkles.tokens.ts +74 -0
  170. package/src/utils/types.ts +10 -0
  171. package/src/utils/ve-style.utils.ts +51 -0
  172. package/tsconfig.json +10 -0
@@ -0,0 +1,79 @@
1
+ import { style } from "@vanilla-extract/css";
2
+ import { semanticVars, tokenVars } from "@blenx-dev/theme/contract";
3
+
4
+ export const root = style({
5
+ height: "100%",
6
+ minHeight: 0,
7
+ width: "100%",
8
+ boxSizing: "border-box",
9
+ });
10
+
11
+ export const viewport = style({
12
+ height: "100%",
13
+ minWidth: 0,
14
+ borderRadius: tokenVars.borderRadius.default,
15
+ outline: "none",
16
+ boxSizing: "border-box",
17
+ overscrollBehaviorY: "contain",
18
+ overscrollBehaviorX: "contain",
19
+ selectors: {
20
+ "&:focus-visible": {
21
+ boxShadow: `0 0 0 2px ${semanticVars.border.strong}`,
22
+ },
23
+ },
24
+ });
25
+
26
+ export const viewportFade = style({
27
+ WebkitMaskImage:
28
+ "linear-gradient(to bottom, transparent, black 1.5rem, black calc(100% - 1.5rem), transparent)",
29
+ maskImage:
30
+ "linear-gradient(to bottom, transparent, black 1.5rem, black calc(100% - 1.5rem), transparent)",
31
+ });
32
+
33
+ export const viewportGutter = style({
34
+ paddingRight: tokenVars.spacing.sm,
35
+ paddingBottom: tokenVars.spacing.sm,
36
+ });
37
+
38
+ export const contentFill = style({
39
+ height: "100%",
40
+ width: "100%",
41
+ });
42
+
43
+ export const scrollbar = style({
44
+ margin: tokenVars.spacing.xs,
45
+ display: "flex",
46
+ opacity: 0,
47
+ boxSizing: "border-box",
48
+ transitionProperty: "opacity",
49
+ transitionDuration: "300ms",
50
+ transitionTimingFunction: "ease",
51
+ selectors: {
52
+ "&[data-hovering]": {
53
+ opacity: 1,
54
+ transitionDelay: "0ms",
55
+ transitionDuration: "100ms",
56
+ },
57
+ "&[data-scrolling]": {
58
+ opacity: 1,
59
+ transitionDelay: "0ms",
60
+ transitionDuration: "100ms",
61
+ },
62
+ },
63
+ });
64
+
65
+ export const scrollbarHorizontal = style({
66
+ height: "6px",
67
+ flexDirection: "column",
68
+ });
69
+
70
+ export const scrollbarVertical = style({
71
+ width: "6px",
72
+ });
73
+
74
+ export const thumb = style({
75
+ position: "relative",
76
+ flexGrow: 1,
77
+ borderRadius: tokenVars.borderRadius.default,
78
+ backgroundColor: semanticVars.border.strong,
79
+ });
@@ -0,0 +1,96 @@
1
+ "use client";
2
+
3
+ import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area";
4
+ import clsx from "clsx";
5
+ import type React from "react";
6
+ import type { CSSProperties } from "react";
7
+ import {
8
+ root,
9
+ viewport,
10
+ viewportFade,
11
+ viewportGutter,
12
+ contentFill,
13
+ scrollbar,
14
+ scrollbarHorizontal,
15
+ scrollbarVertical,
16
+ thumb,
17
+ } from "./scroll-area.css";
18
+ import type { NamedHeight } from "../../utils/heights";
19
+
20
+ type ScrollAreaProps = ScrollAreaPrimitive.Root.Props & {
21
+ scrollFade?: boolean;
22
+ scrollbarGutter?: boolean;
23
+ fill?: boolean;
24
+ height?: NamedHeight | CSSProperties["height"];
25
+ className?: string;
26
+ style?: React.CSSProperties;
27
+ };
28
+
29
+ function ScrollArea({
30
+ children,
31
+ scrollFade = false,
32
+ scrollbarGutter = true,
33
+ fill = true,
34
+ height = "60svh",
35
+ className,
36
+ style,
37
+ ...props
38
+ }: ScrollAreaProps): React.ReactElement {
39
+ const dynamicHeight: React.CSSProperties =
40
+ typeof height === "string" ? { height, maxHeight: height } : {};
41
+ return (
42
+ <ScrollAreaPrimitive.Root
43
+ className={clsx(root, className)}
44
+ style={{ ...dynamicHeight, ...style }}
45
+ data-slot="scroll-area-root"
46
+ {...props}
47
+ >
48
+ <ScrollAreaPrimitive.Viewport
49
+ className={clsx(viewport, scrollFade && viewportFade, scrollbarGutter && viewportGutter)}
50
+ data-slot="scroll-area-viewport"
51
+ >
52
+ <ScrollAreaPrimitive.Content
53
+ className={clsx(fill && contentFill)}
54
+ data-slot="scroll-area-content"
55
+ >
56
+ {children}
57
+ </ScrollAreaPrimitive.Content>
58
+ </ScrollAreaPrimitive.Viewport>
59
+ <ScrollBar orientation="vertical" />
60
+ <ScrollBar orientation="horizontal" />
61
+ <ScrollAreaPrimitive.Corner data-slot="scroll-area-corner" />
62
+ </ScrollAreaPrimitive.Root>
63
+ );
64
+ }
65
+
66
+ type ScrollBarProps = ScrollAreaPrimitive.Scrollbar.Props & {
67
+ className?: string;
68
+ style?: React.CSSProperties;
69
+ };
70
+
71
+ function ScrollBar({
72
+ orientation = "vertical",
73
+ className,
74
+ style,
75
+ ...props
76
+ }: ScrollBarProps): React.ReactElement {
77
+ return (
78
+ <ScrollAreaPrimitive.Scrollbar
79
+ className={clsx(
80
+ scrollbar,
81
+ orientation === "horizontal" && scrollbarHorizontal,
82
+ orientation === "vertical" && scrollbarVertical,
83
+ className,
84
+ )}
85
+ style={style}
86
+ data-slot="scroll-area-scrollbar"
87
+ orientation={orientation}
88
+ {...props}
89
+ >
90
+ <ScrollAreaPrimitive.Thumb className={thumb} data-slot="scroll-area-thumb" />
91
+ </ScrollAreaPrimitive.Scrollbar>
92
+ );
93
+ }
94
+
95
+ export { ScrollArea, ScrollBar };
96
+ export type { ScrollAreaProps, ScrollBarProps };
@@ -0,0 +1 @@
1
+ export * from "./segmented-control";
@@ -0,0 +1,42 @@
1
+ import type { ToggleProps } from "../Toggle/toggle";
2
+ import { ToggleGroup, ToggleGroupItem, type ToggleGroupProps } from "../ToggleGroup/toggle-group";
3
+
4
+ type Option<T extends string> = {
5
+ value: T;
6
+ label: string;
7
+ };
8
+
9
+ type SegmentedControlProps<T extends string> = Omit<ToggleGroupProps, "value" | "onValueChange"> & {
10
+ value: T;
11
+ onValueChange: (value: T) => void;
12
+ options: Option<T>[];
13
+ radius?: ToggleProps["radius"];
14
+ };
15
+ export function SegmentedControl<T extends string>({
16
+ value,
17
+ onValueChange,
18
+ options,
19
+ radius = "md",
20
+ variant = "outline",
21
+ ...props
22
+ }: SegmentedControlProps<T>) {
23
+ return (
24
+ <ToggleGroup
25
+ value={[value]}
26
+ onValueChange={(values) => {
27
+ if (values.length === 0) {
28
+ return;
29
+ }
30
+ onValueChange(values[0] as T);
31
+ }}
32
+ variant={variant}
33
+ {...props}
34
+ >
35
+ {options.map((option) => (
36
+ <ToggleGroupItem key={option.value} value={option.value} radius={radius}>
37
+ {option.label}
38
+ </ToggleGroupItem>
39
+ ))}
40
+ </ToggleGroup>
41
+ );
42
+ }
@@ -0,0 +1 @@
1
+ export * from "./select";
@@ -0,0 +1,182 @@
1
+ import { style } from "@vanilla-extract/css";
2
+ import { semanticVars, tokenVars } from "@blenx-dev/theme/contract";
3
+
4
+ export const label = style({
5
+ fontSize: tokenVars.fontSize.xs,
6
+ fontWeight: tokenVars.fontWeight.semibold,
7
+ color: semanticVars.text.secondary,
8
+ paddingBottom: tokenVars.spacing.xs,
9
+ paddingLeft: tokenVars.spacing.xs,
10
+ paddingRight: tokenVars.spacing.xs,
11
+ });
12
+
13
+ export const trigger = style({
14
+ display: "flex",
15
+ alignItems: "center",
16
+ justifyContent: "space-between",
17
+ gap: tokenVars.spacing.sm,
18
+ boxSizing: "border-box",
19
+ width: "100%",
20
+ paddingTop: tokenVars.spacing.sm,
21
+ paddingBottom: tokenVars.spacing.sm,
22
+ paddingLeft: tokenVars.spacing.md,
23
+ paddingRight: tokenVars.spacing.sm,
24
+ fontSize: tokenVars.fontSize.sm,
25
+ lineHeight: "1.5",
26
+ fontFamily: "inherit",
27
+ color: semanticVars.text.primary,
28
+ backgroundColor: semanticVars.surface.default,
29
+ borderWidth: 1,
30
+ borderStyle: "solid",
31
+ borderColor: semanticVars.border.default,
32
+ borderRadius: tokenVars.borderRadius.md,
33
+ outline: "none",
34
+ cursor: "pointer",
35
+ transition: "border-color 0.15s ease, box-shadow 0.15s ease",
36
+ selectors: {
37
+ "&:hover": {
38
+ borderColor: semanticVars.border.strong,
39
+ },
40
+ "&:focus-visible": {
41
+ borderColor: semanticVars.interactive.primary.default,
42
+ boxShadow: `0 0 0 2px ${semanticVars.focus.ring}`,
43
+ },
44
+ "&:disabled": {
45
+ opacity: 0.5,
46
+ cursor: "not-allowed",
47
+ },
48
+ "&[data-placeholder]": {
49
+ color: semanticVars.text.disabled,
50
+ },
51
+ },
52
+ });
53
+
54
+ export const triggerSm = style({
55
+ paddingTop: tokenVars.spacing.xs,
56
+ paddingBottom: tokenVars.spacing.xs,
57
+ paddingLeft: tokenVars.spacing.sm,
58
+ paddingRight: tokenVars.spacing.xs,
59
+ fontSize: 14,
60
+ });
61
+
62
+ export const triggerLg = style({
63
+ paddingTop: tokenVars.spacing.md,
64
+ paddingBottom: tokenVars.spacing.md,
65
+ paddingLeft: tokenVars.spacing.lg,
66
+ paddingRight: tokenVars.spacing.md,
67
+ fontSize: 16,
68
+ });
69
+
70
+ export const icon = style({
71
+ display: "flex",
72
+ alignItems: "center",
73
+ color: semanticVars.text.secondary,
74
+ flexShrink: 0,
75
+ });
76
+
77
+ export const popup = style({
78
+ boxSizing: "border-box",
79
+ maxHeight: 300,
80
+ paddingTop: tokenVars.spacing.xs,
81
+ paddingBottom: tokenVars.spacing.xs,
82
+ backgroundColor: semanticVars.surface.default,
83
+ borderWidth: 1,
84
+ borderStyle: "solid",
85
+ borderColor: semanticVars.border.default,
86
+ borderRadius: tokenVars.borderRadius.lg,
87
+ boxShadow: tokenVars.shadow.lg,
88
+ zIndex: 1400,
89
+ minWidth: "var(--anchor-width)",
90
+ maxWidth: "var(--available-width)",
91
+ outline: "none",
92
+ overflowY: "auto",
93
+ selectors: {
94
+ "&:focus-visible": {
95
+ outline: "none",
96
+ },
97
+ },
98
+ });
99
+
100
+ export const item = style({
101
+ display: "flex",
102
+ alignItems: "center",
103
+ gap: tokenVars.spacing.sm,
104
+ boxSizing: "border-box",
105
+ minHeight: 32,
106
+ paddingTop: tokenVars.spacing.xs,
107
+ paddingBottom: tokenVars.spacing.xs,
108
+ paddingLeft: tokenVars.spacing.sm,
109
+ paddingRight: tokenVars.spacing.sm,
110
+ marginLeft: tokenVars.spacing.xs,
111
+ marginRight: tokenVars.spacing.xs,
112
+ borderRadius: tokenVars.borderRadius.sm,
113
+ fontSize: tokenVars.fontSize.sm,
114
+ lineHeight: "1.4",
115
+ color: semanticVars.text.primary,
116
+ cursor: "pointer",
117
+ userSelect: "none",
118
+ selectors: {
119
+ "&:hover": {
120
+ backgroundColor: semanticVars.background.subtle,
121
+ },
122
+ "&[data-highlighted]": {
123
+ backgroundColor: semanticVars.background.subtle,
124
+ },
125
+ "&[data-selected]": {
126
+ backgroundColor: semanticVars.interactive.primary.default,
127
+ color: semanticVars.interactive.primaryFg,
128
+ },
129
+ "&[data-selected]:hover": {
130
+ backgroundColor: semanticVars.interactive.primary.default,
131
+ },
132
+ "&[data-selected][data-highlighted]": {
133
+ backgroundColor: semanticVars.interactive.primary.default,
134
+ },
135
+ "&[data-disabled]": {
136
+ color: semanticVars.text.disabled,
137
+ cursor: "default",
138
+ },
139
+ "&[data-disabled]:hover": {
140
+ backgroundColor: "transparent",
141
+ },
142
+ },
143
+ });
144
+
145
+ export const itemIndicator = style({
146
+ display: "inline-flex",
147
+ alignItems: "center",
148
+ flexShrink: 0,
149
+ width: 14,
150
+ });
151
+
152
+ export const separator = style({
153
+ height: 1,
154
+ marginTop: tokenVars.spacing.xs,
155
+ marginBottom: tokenVars.spacing.xs,
156
+ backgroundColor: semanticVars.border.subtle,
157
+ });
158
+
159
+ export const groupLabel = style({
160
+ paddingTop: tokenVars.spacing.sm,
161
+ paddingBottom: tokenVars.spacing.xs,
162
+ paddingLeft: tokenVars.spacing.sm,
163
+ paddingRight: tokenVars.spacing.sm,
164
+ fontSize: tokenVars.fontSize.xs,
165
+ fontWeight: tokenVars.fontWeight.semibold,
166
+ color: semanticVars.text.secondary,
167
+ });
168
+
169
+ export const scrollArrow = style({
170
+ display: "flex",
171
+ alignItems: "center",
172
+ justifyContent: "center",
173
+ width: "100%",
174
+ height: 24,
175
+ color: semanticVars.text.secondary,
176
+ cursor: "pointer",
177
+ selectors: {
178
+ "&:hover": {
179
+ backgroundColor: semanticVars.background.subtle,
180
+ },
181
+ },
182
+ });
@@ -0,0 +1,165 @@
1
+ import { Select as SelectPrimitive } from "@base-ui/react/select";
2
+ import { CaretDownIcon, CaretUpIcon, CheckIcon } from "@phosphor-icons/react";
3
+ import clsx from "clsx";
4
+ import type * as React from "react";
5
+ import { Field, FieldError, FieldLabel } from "../Field/field";
6
+ import {
7
+ groupLabel,
8
+ icon as iconStyle,
9
+ item as itemStyle,
10
+ itemIndicator,
11
+ label as labelStyle,
12
+ popup,
13
+ scrollArrow,
14
+ separator as separatorStyle,
15
+ trigger as triggerStyle,
16
+ triggerLg,
17
+ triggerSm,
18
+ } from "./select.css";
19
+
20
+ type SelectSize = "sm" | "default" | "lg";
21
+
22
+ type SelectItemProp = string | number | { label: React.ReactNode; value: unknown };
23
+
24
+ type SelectRootProps = SelectPrimitive.Root.Props<SelectItemProp>;
25
+
26
+ export function SelectRoot({ children, ...props }: SelectRootProps) {
27
+ return <SelectPrimitive.Root {...props}>{children}</SelectPrimitive.Root>;
28
+ }
29
+
30
+ export function SelectLabel(props: SelectPrimitive.Label.Props) {
31
+ return <SelectPrimitive.Label className={labelStyle} {...props} />;
32
+ }
33
+
34
+ export function SelectTrigger({
35
+ size = "default",
36
+ className,
37
+ style,
38
+ ...props
39
+ }: SelectPrimitive.Trigger.Props & {
40
+ size?: SelectSize;
41
+ className?: string;
42
+ style?: React.CSSProperties;
43
+ }) {
44
+ return (
45
+ <SelectPrimitive.Trigger
46
+ className={clsx(
47
+ triggerStyle,
48
+ size === "sm" && triggerSm,
49
+ size === "lg" && triggerLg,
50
+ className,
51
+ )}
52
+ style={style}
53
+ {...props}
54
+ />
55
+ );
56
+ }
57
+
58
+ export function SelectValue({ placeholder, ...props }: SelectPrimitive.Value.Props) {
59
+ return <SelectPrimitive.Value placeholder={placeholder} {...props} />;
60
+ }
61
+
62
+ export function SelectIcon(props: SelectPrimitive.Icon.Props) {
63
+ return (
64
+ <SelectPrimitive.Icon className={iconStyle} {...props}>
65
+ <CaretDownIcon size={16} />
66
+ </SelectPrimitive.Icon>
67
+ );
68
+ }
69
+
70
+ export function SelectPortal(props: SelectPrimitive.Portal.Props) {
71
+ return <SelectPrimitive.Portal {...props} />;
72
+ }
73
+
74
+ export function SelectPositioner({ sideOffset = 4, ...props }: SelectPrimitive.Positioner.Props) {
75
+ return <SelectPrimitive.Positioner sideOffset={sideOffset} {...props} />;
76
+ }
77
+
78
+ export function SelectPopup(props: SelectPrimitive.Popup.Props) {
79
+ return <SelectPrimitive.Popup className={popup} {...props} />;
80
+ }
81
+
82
+ export function SelectList(props: SelectPrimitive.List.Props) {
83
+ return <SelectPrimitive.List {...props} />;
84
+ }
85
+
86
+ export function SelectItem({
87
+ children,
88
+ ...props
89
+ }: SelectPrimitive.Item.Props & { children: React.ReactNode }) {
90
+ return (
91
+ <SelectPrimitive.Item className={itemStyle} {...props}>
92
+ <SelectPrimitive.ItemIndicator className={itemIndicator}>
93
+ <CheckIcon size={14} weight="bold" />
94
+ </SelectPrimitive.ItemIndicator>
95
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
96
+ </SelectPrimitive.Item>
97
+ );
98
+ }
99
+
100
+ export function SelectSeparator(props: SelectPrimitive.Separator.Props) {
101
+ return <SelectPrimitive.Separator className={separatorStyle} {...props} />;
102
+ }
103
+
104
+ export function SelectGroup(props: SelectPrimitive.Group.Props) {
105
+ return <SelectPrimitive.Group {...props} />;
106
+ }
107
+
108
+ export function SelectGroupLabel(props: SelectPrimitive.GroupLabel.Props) {
109
+ return <SelectPrimitive.GroupLabel className={groupLabel} {...props} />;
110
+ }
111
+
112
+ export function SelectScrollUpArrow(props: SelectPrimitive.ScrollUpArrow.Props) {
113
+ return (
114
+ <SelectPrimitive.ScrollUpArrow className={scrollArrow} {...props}>
115
+ <CaretUpIcon size={12} />
116
+ </SelectPrimitive.ScrollUpArrow>
117
+ );
118
+ }
119
+
120
+ export function SelectScrollDownArrow(props: SelectPrimitive.ScrollDownArrow.Props) {
121
+ return (
122
+ <SelectPrimitive.ScrollDownArrow className={scrollArrow} {...props}>
123
+ <CaretDownIcon size={12} />
124
+ </SelectPrimitive.ScrollDownArrow>
125
+ );
126
+ }
127
+
128
+ // ─── Convenience wrapper ──────────────────────────────────────────────────────
129
+
130
+ interface SelectWrapperProps {
131
+ label?: string;
132
+ error?: string;
133
+ children: React.ReactNode;
134
+ }
135
+
136
+ export function SelectWrapper({ label, error, children }: SelectWrapperProps) {
137
+ return (
138
+ <Field invalid={Boolean(error)}>
139
+ {label && <FieldLabel>{label}</FieldLabel>}
140
+ {children}
141
+ {error && <FieldError>{error}</FieldError>}
142
+ </Field>
143
+ );
144
+ }
145
+
146
+ // ─── Compound component namespace ─────────────────────────────────────────────
147
+
148
+ export const Select = {
149
+ Root: SelectRoot,
150
+ Label: SelectLabel,
151
+ Trigger: SelectTrigger,
152
+ Value: SelectValue,
153
+ Icon: SelectIcon,
154
+ Portal: SelectPortal,
155
+ Positioner: SelectPositioner,
156
+ Popup: SelectPopup,
157
+ List: SelectList,
158
+ Item: SelectItem,
159
+ Separator: SelectSeparator,
160
+ Group: SelectGroup,
161
+ GroupLabel: SelectGroupLabel,
162
+ ScrollUpArrow: SelectScrollUpArrow,
163
+ ScrollDownArrow: SelectScrollDownArrow,
164
+ Wrapper: SelectWrapper,
165
+ };
@@ -0,0 +1 @@
1
+ export * from "./separator";
@@ -0,0 +1,59 @@
1
+ import { recipe } from "@vanilla-extract/recipes";
2
+ import { style } from "@vanilla-extract/css";
3
+ import { semanticVars, tokenVars } from "@blenx-dev/theme/contract";
4
+
5
+ export const separator = recipe({
6
+ base: {
7
+ boxSizing: "border-box",
8
+ flexShrink: 0,
9
+ backgroundColor: "currentColor",
10
+ },
11
+ variants: {
12
+ orientation: {
13
+ horizontal: { flexGrow: 1, height: 1 },
14
+ vertical: { width: 1, height: "100%" },
15
+ },
16
+ tone: {
17
+ subtle: { color: semanticVars.border.subtle },
18
+ strong: { color: semanticVars.border.strong },
19
+ },
20
+ },
21
+ defaultVariants: {
22
+ orientation: "horizontal",
23
+ tone: "strong",
24
+ },
25
+ });
26
+
27
+ export const withLabel = style({
28
+ backgroundColor: "transparent",
29
+ display: "flex",
30
+ alignItems: "center",
31
+ gap: tokenVars.spacing.sm,
32
+ width: "100%",
33
+ selectors: {
34
+ "&::before": {
35
+ content: '""',
36
+ flex: 1,
37
+ height: 1,
38
+ backgroundColor: "currentColor",
39
+ opacity: 1,
40
+ },
41
+ "&::after": {
42
+ content: '""',
43
+ flex: 1,
44
+ height: 1,
45
+ backgroundColor: "currentColor",
46
+ opacity: 1,
47
+ },
48
+ },
49
+ });
50
+
51
+ export const label = style({
52
+ paddingLeft: tokenVars.spacing.xs,
53
+ paddingRight: tokenVars.spacing.xs,
54
+ color: semanticVars.text.secondary,
55
+ fontSize: tokenVars.fontSize.xs,
56
+ fontWeight: tokenVars.fontWeight.medium,
57
+ lineHeight: 1.2,
58
+ whiteSpace: "nowrap",
59
+ });
@@ -0,0 +1,34 @@
1
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator";
2
+ import clsx from "clsx";
3
+ import type * as React from "react";
4
+ import { separator, withLabel, label as labelStyle } from "./separator.css";
5
+
6
+ type Orientation = "horizontal" | "vertical";
7
+ type SeparatorTone = "subtle" | "strong";
8
+
9
+ export type SeparatorProps = Omit<SeparatorPrimitive.Props, "className" | "style"> & {
10
+ orientation?: Orientation;
11
+ label?: React.ReactNode;
12
+ tone?: SeparatorTone;
13
+ className?: string;
14
+ style?: React.CSSProperties;
15
+ };
16
+
17
+ export function Separator({
18
+ orientation = "horizontal",
19
+ label,
20
+ tone = "subtle",
21
+ className,
22
+ style,
23
+ ...props
24
+ }: SeparatorProps) {
25
+ const baseCls = separator({ orientation, tone });
26
+ if (orientation === "horizontal" && label) {
27
+ return (
28
+ <SeparatorPrimitive className={clsx(baseCls, withLabel, className)} style={style} {...props}>
29
+ <span className={labelStyle}>{label}</span>
30
+ </SeparatorPrimitive>
31
+ );
32
+ }
33
+ return <SeparatorPrimitive className={clsx(baseCls, className)} style={style} {...props} />;
34
+ }
@@ -0,0 +1 @@
1
+ export * from "./sheet";