@luxfi/ui 5.5.3 → 5.6.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 (244) hide show
  1. package/dist/accordion.cjs +213 -0
  2. package/dist/accordion.js +186 -0
  3. package/dist/alert.cjs +553 -0
  4. package/dist/alert.js +531 -0
  5. package/dist/avatar.cjs +149 -0
  6. package/dist/avatar.js +125 -0
  7. package/dist/badge.cjs +611 -0
  8. package/dist/badge.js +589 -0
  9. package/dist/button.cjs +689 -0
  10. package/dist/button.js +664 -0
  11. package/dist/checkbox.cjs +265 -0
  12. package/dist/checkbox.js +241 -0
  13. package/dist/close-button.cjs +73 -0
  14. package/dist/close-button.js +51 -0
  15. package/dist/collapsible.cjs +702 -0
  16. package/dist/collapsible.js +679 -0
  17. package/dist/color-mode.cjs +96 -0
  18. package/dist/color-mode.js +72 -0
  19. package/dist/dialog.cjs +279 -0
  20. package/dist/dialog.js +246 -0
  21. package/dist/drawer.cjs +207 -0
  22. package/dist/drawer.js +175 -0
  23. package/dist/empty-state.cjs +93 -0
  24. package/dist/empty-state.js +71 -0
  25. package/dist/field.cjs +183 -0
  26. package/dist/field.js +160 -0
  27. package/dist/heading.cjs +46 -0
  28. package/dist/heading.js +40 -0
  29. package/dist/icon-button.cjs +491 -0
  30. package/dist/icon-button.js +470 -0
  31. package/dist/image.cjs +572 -0
  32. package/dist/image.js +551 -0
  33. package/dist/index.cjs +5779 -0
  34. package/dist/index.js +5619 -0
  35. package/dist/input-group.cjs +155 -0
  36. package/dist/input-group.js +133 -0
  37. package/dist/input.cjs +65 -0
  38. package/dist/input.js +59 -0
  39. package/dist/link.cjs +630 -0
  40. package/dist/link.js +606 -0
  41. package/dist/menu.cjs +305 -0
  42. package/dist/menu.js +269 -0
  43. package/dist/pin-input.cjs +182 -0
  44. package/dist/pin-input.js +160 -0
  45. package/dist/popover.cjs +327 -0
  46. package/dist/popover.js +294 -0
  47. package/dist/progress-circle.cjs +152 -0
  48. package/dist/progress-circle.js +128 -0
  49. package/dist/progress.cjs +117 -0
  50. package/dist/progress.js +94 -0
  51. package/dist/provider.cjs +62 -0
  52. package/dist/provider.js +40 -0
  53. package/dist/radio.cjs +177 -0
  54. package/dist/radio.js +153 -0
  55. package/dist/rating.cjs +80 -0
  56. package/dist/rating.js +58 -0
  57. package/dist/select.cjs +791 -0
  58. package/dist/select.js +757 -0
  59. package/dist/separator.cjs +57 -0
  60. package/dist/separator.js +51 -0
  61. package/dist/skeleton.cjs +370 -0
  62. package/dist/skeleton.js +346 -0
  63. package/dist/slider.cjs +138 -0
  64. package/dist/slider.js +115 -0
  65. package/dist/switch.cjs +163 -0
  66. package/dist/switch.js +140 -0
  67. package/dist/table.cjs +1044 -0
  68. package/dist/table.js +1013 -0
  69. package/dist/tabs.cjs +240 -0
  70. package/dist/tabs.js +213 -0
  71. package/dist/tag.cjs +651 -0
  72. package/dist/tag.js +628 -0
  73. package/dist/textarea.cjs +65 -0
  74. package/dist/textarea.js +59 -0
  75. package/dist/toaster.cjs +99 -0
  76. package/dist/toaster.js +96 -0
  77. package/dist/tooltip.cjs +171 -0
  78. package/dist/tooltip.js +148 -0
  79. package/dist/utils.cjs +11 -0
  80. package/dist/utils.js +9 -0
  81. package/package.json +270 -65
  82. package/src/accordion.tsx +285 -0
  83. package/src/alert.tsx +221 -0
  84. package/src/avatar.tsx +174 -0
  85. package/src/badge.tsx +158 -0
  86. package/src/button.tsx +411 -0
  87. package/src/checkbox.tsx +307 -0
  88. package/src/close-button.tsx +51 -0
  89. package/src/collapsible.tsx +126 -0
  90. package/src/color-mode.tsx +125 -0
  91. package/src/dialog.tsx +356 -0
  92. package/src/drawer.tsx +186 -0
  93. package/src/empty-state.tsx +97 -0
  94. package/src/field.tsx +202 -0
  95. package/src/heading.tsx +55 -0
  96. package/src/icon-button.tsx +192 -0
  97. package/src/image.tsx +280 -0
  98. package/src/index.ts +192 -0
  99. package/src/input-group.tsx +159 -0
  100. package/src/input.tsx +60 -0
  101. package/src/link.tsx +326 -0
  102. package/src/menu.tsx +471 -0
  103. package/src/pin-input.tsx +187 -0
  104. package/src/popover.tsx +400 -0
  105. package/src/progress-circle.tsx +180 -0
  106. package/src/progress.tsx +109 -0
  107. package/src/provider.tsx +12 -0
  108. package/src/radio.tsx +175 -0
  109. package/src/rating.tsx +79 -0
  110. package/src/select.tsx +696 -0
  111. package/src/separator.tsx +59 -0
  112. package/src/skeleton.tsx +302 -0
  113. package/src/slider.tsx +152 -0
  114. package/src/switch.tsx +158 -0
  115. package/src/table.tsx +621 -0
  116. package/src/tabs.tsx +354 -0
  117. package/src/tag.tsx +159 -0
  118. package/src/textarea.tsx +60 -0
  119. package/src/toaster.tsx +117 -0
  120. package/src/tokens.css +438 -0
  121. package/src/tooltip.tsx +184 -0
  122. package/src/utils/cn.ts +7 -0
  123. package/src/utils.ts +6 -0
  124. package/tokens.css +438 -0
  125. package/commerce/ui/conf.ts +0 -13
  126. package/commerce/ui/context.tsx +0 -123
  127. package/commerce/ui/store.ts +0 -295
  128. package/components/access-code-input.tsx +0 -71
  129. package/components/analytics.tsx +0 -23
  130. package/components/auth/auth-listener.tsx +0 -29
  131. package/components/auth/auth-token/clear-auth-token.tsx +0 -12
  132. package/components/auth/auth-token/set-auth-token.tsx +0 -16
  133. package/components/auth/common-auth-domains.ts +0 -17
  134. package/components/auth/login-panel.tsx +0 -111
  135. package/components/auth/mobile-login-button.tsx +0 -107
  136. package/components/auth/signup-panel.tsx +0 -113
  137. package/components/back-button.tsx +0 -49
  138. package/components/chat-widget.tsx +0 -85
  139. package/components/commerce/bag-button.tsx +0 -98
  140. package/components/commerce/buy-button.tsx +0 -34
  141. package/components/commerce/checkout-button.tsx +0 -129
  142. package/components/commerce/checkout-panel/cart-accordian.tsx +0 -66
  143. package/components/commerce/checkout-panel/checkout-panel-props.ts +0 -10
  144. package/components/commerce/checkout-panel/desktop-bag-carousel.tsx +0 -36
  145. package/components/commerce/checkout-panel/desktop-cp.tsx +0 -83
  146. package/components/commerce/checkout-panel/index.tsx +0 -126
  147. package/components/commerce/checkout-panel/mobile-cp.tsx +0 -67
  148. package/components/commerce/checkout-panel/policy-links.tsx +0 -29
  149. package/components/commerce/checkout-panel/steps-indicator.tsx +0 -39
  150. package/components/commerce/checkout-panel/thank-you.tsx +0 -18
  151. package/components/commerce/desktop-bag-popup.tsx +0 -78
  152. package/components/commerce/drawer/index.tsx +0 -88
  153. package/components/commerce/drawer/micro.tsx +0 -145
  154. package/components/commerce/drawer/shell.tsx +0 -85
  155. package/components/contact-dialog/contact-form.tsx +0 -116
  156. package/components/contact-dialog/disclaimer.tsx +0 -13
  157. package/components/contact-dialog/index.tsx +0 -64
  158. package/components/copyright.tsx +0 -21
  159. package/components/drawer-margin.tsx +0 -28
  160. package/components/footer.tsx +0 -78
  161. package/components/header/desktop-nav-menu.tsx +0 -204
  162. package/components/header/desktop.tsx +0 -65
  163. package/components/header/index.tsx +0 -50
  164. package/components/header/mobile-bag-drawer.tsx +0 -51
  165. package/components/header/mobile-menu-toggle-button.tsx +0 -35
  166. package/components/header/mobile-nav-menu-ai.tsx +0 -51
  167. package/components/header/mobile-nav-menu-item.tsx +0 -47
  168. package/components/header/mobile-nav-menu.tsx +0 -102
  169. package/components/header/mobile.tsx +0 -170
  170. package/components/header/theme-toggle.tsx +0 -26
  171. package/components/icons/avatar.tsx +0 -11
  172. package/components/icons/bag-icon.tsx +0 -10
  173. package/components/icons/index.ts +0 -6
  174. package/components/icons/left-arrow.tsx +0 -11
  175. package/components/icons/lux-logo.tsx +0 -10
  176. package/components/icons/right-arrow.tsx +0 -10
  177. package/components/icons/social-icon.tsx +0 -35
  178. package/components/icons/social-svg.css +0 -3
  179. package/components/index.ts +0 -26
  180. package/components/logo.tsx +0 -92
  181. package/components/main.tsx +0 -27
  182. package/components/mini-chart/index.tsx +0 -8
  183. package/components/mini-chart/mini-chart-props.ts +0 -44
  184. package/components/mini-chart/mini-chart.tsx +0 -85
  185. package/components/mini-chart/wrapper.tsx +0 -23
  186. package/components/not-found/index.tsx +0 -28
  187. package/components/not-found/not-found-content.mdx +0 -5
  188. package/components/tooltip.tsx +0 -31
  189. package/environment.d.ts +0 -6
  190. package/next/analytics/fpixel.ts +0 -16
  191. package/next/analytics/google-analytics.ts +0 -14
  192. package/next/analytics/index.ts +0 -3
  193. package/next/analytics/pixel-analytics.tsx +0 -55
  194. package/next/font/get-app-router-font-classes.ts +0 -17
  195. package/next/font/load-and-return-lux-next-fonts-on-import.ts +0 -68
  196. package/next/font/local/Druk-Wide-Bold.ttf +0 -0
  197. package/next/font/local/Druk-Wide-Medium.ttf +0 -0
  198. package/next/font/local/InterVariable-Italic.ttf +0 -0
  199. package/next/font/local/InterVariable-Italic.woff2 +0 -0
  200. package/next/font/local/InterVariable.ttf +0 -0
  201. package/next/font/local/InterVariable.woff2 +0 -0
  202. package/next/font/next-font-desc.ts +0 -28
  203. package/next/font/pages-router-font-vars.tsx +0 -18
  204. package/next/head-metadata/from-next/metadata-types.ts +0 -158
  205. package/next/head-metadata/from-next/opengraph-types.ts +0 -267
  206. package/next/head-metadata/from-next/twitter-types.ts +0 -92
  207. package/next/head-metadata/index.tsx +0 -208
  208. package/next/index.ts +0 -2
  209. package/next/middleware/determine-device-mw.ts +0 -29
  210. package/root-layout/WHY_THIS_IS_SEPARATE.txt +0 -2
  211. package/root-layout/index.tsx +0 -112
  212. package/site-def/footer/community.tsx +0 -61
  213. package/site-def/footer/company.ts +0 -37
  214. package/site-def/footer/ecosystem.ts +0 -37
  215. package/site-def/footer/index.tsx +0 -26
  216. package/site-def/footer/legal.ts +0 -28
  217. package/site-def/footer/network.ts +0 -45
  218. package/site-def/footer/svg/warpcast-logo.svg +0 -12
  219. package/site-def/index.ts +0 -4
  220. package/site-def/main-nav.tsx +0 -460
  221. package/style/cart-animation.css +0 -29
  222. package/style/checkout-animation.css +0 -23
  223. package/style/drawer-handle-overrides.css +0 -160
  224. package/style/fonts/COPY_TO_PUBLIC_FOR_NON_NEXT.txt +0 -0
  225. package/style/fonts/Druk-Wide-Bold.ttf +0 -0
  226. package/style/fonts/Druk-Wide-Medium.ttf +0 -0
  227. package/style/fonts/InterVariable-Italic.ttf +0 -0
  228. package/style/fonts/InterVariable-Italic.woff2 +0 -0
  229. package/style/fonts/InterVariable.ttf +0 -0
  230. package/style/fonts/InterVariable.woff2 +0 -0
  231. package/style/lux-colors.css +0 -85
  232. package/style/lux-fonts.css +0 -30
  233. package/style/lux-global-non-next.css +0 -52
  234. package/style/lux-global.css +0 -51
  235. package/tailwind/fontFamily.tailwind.lux.ts +0 -18
  236. package/tailwind/index.ts +0 -2
  237. package/tailwind/lux-tw-fonts.ts +0 -40
  238. package/tailwind/tailwind.config.lux-preset.ts +0 -10
  239. package/tsconfig.json +0 -15
  240. package/types/chatbot-config.ts +0 -7
  241. package/types/chatbot-suggested-question.ts +0 -7
  242. package/types/contact-info.ts +0 -11
  243. package/types/index.ts +0 -4
  244. package/types/site-def.ts +0 -46
@@ -0,0 +1,59 @@
1
+ import React from 'react';
2
+
3
+ import { cn } from './utils';
4
+
5
+ type Orientation = 'horizontal' | 'vertical';
6
+ type Variant = 'solid' | 'dashed' | 'dotted';
7
+ type Size = 'xs' | 'sm' | 'md' | 'lg';
8
+
9
+ export interface SeparatorProps extends React.HTMLAttributes<HTMLHRElement> {
10
+ readonly orientation?: Orientation;
11
+ readonly variant?: Variant;
12
+ readonly size?: Size;
13
+ }
14
+
15
+ const VARIANT_CLASSES: Record<Variant, string> = {
16
+ solid: 'border-solid',
17
+ dashed: 'border-dashed',
18
+ dotted: 'border-dotted',
19
+ };
20
+
21
+ const HORIZONTAL_SIZE_CLASSES: Record<Size, string> = {
22
+ xs: 'border-t-[0.5px]',
23
+ sm: 'border-t',
24
+ md: 'border-t-2',
25
+ lg: 'border-t-[3px]',
26
+ };
27
+
28
+ const VERTICAL_SIZE_CLASSES: Record<Size, string> = {
29
+ xs: 'border-l-[0.5px]',
30
+ sm: 'border-l',
31
+ md: 'border-l-2',
32
+ lg: 'border-l-[3px]',
33
+ };
34
+
35
+ export const Separator = React.forwardRef<HTMLHRElement, SeparatorProps>(
36
+ function Separator(
37
+ { orientation = 'horizontal', variant = 'solid', size = 'sm', className, ...rest },
38
+ ref,
39
+ ) {
40
+ const isVertical = orientation === 'vertical';
41
+
42
+ return (
43
+ <hr
44
+ ref={ ref }
45
+ role="separator"
46
+ aria-orientation={ orientation }
47
+ className={ cn(
48
+ 'border-[var(--color-border-divider)]',
49
+ 'border-0',
50
+ VARIANT_CLASSES[variant],
51
+ isVertical ? VERTICAL_SIZE_CLASSES[size] : HORIZONTAL_SIZE_CLASSES[size],
52
+ isVertical ? 'self-stretch h-auto w-0' : 'w-full',
53
+ className,
54
+ ) }
55
+ { ...rest }
56
+ />
57
+ );
58
+ },
59
+ );
@@ -0,0 +1,302 @@
1
+ import * as React from 'react';
2
+
3
+ import { cn } from './utils';
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Skeleton
7
+ // ---------------------------------------------------------------------------
8
+
9
+ export interface SkeletonProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color'> {
10
+ readonly loading: boolean | undefined;
11
+
12
+ /** When true, the Skeleton wraps its single child element instead of adding a wrapper div. */
13
+ readonly asChild?: boolean;
14
+
15
+ // Legacy Chakra style-prop shims — converted to className / inline style.
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ readonly w?: any;
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
+ readonly h?: any;
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ readonly minW?: any;
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
+ readonly maxW?: any;
24
+ readonly display?: string;
25
+ readonly flexGrow?: number;
26
+ readonly flexShrink?: number;
27
+ readonly flexBasis?: string;
28
+ readonly fontWeight?: number | string;
29
+ readonly textStyle?: string;
30
+ readonly borderRadius?: string;
31
+ readonly alignSelf?: string;
32
+ readonly alignItems?: string;
33
+ readonly justifyContent?: string;
34
+ readonly color?: string;
35
+ readonly mt?: number | string;
36
+ readonly mb?: number | string;
37
+ readonly ml?: number | string;
38
+ readonly mr?: number | string;
39
+ readonly height?: string;
40
+ readonly overflow?: string;
41
+ readonly whiteSpace?: string;
42
+ readonly textOverflow?: string;
43
+ readonly textTransform?: string;
44
+ readonly gap?: number | string;
45
+ readonly gridTemplateColumns?: string;
46
+ readonly minWidth?: string;
47
+ readonly boxSize?: number | string;
48
+ readonly py?: number | string;
49
+ readonly px?: number | string;
50
+ readonly p?: number | string;
51
+ readonly hideBelow?: string;
52
+ readonly as?: React.ElementType;
53
+ readonly fontSize?: string;
54
+ readonly flexWrap?: React.CSSProperties['flexWrap'];
55
+ readonly wordBreak?: React.CSSProperties['wordBreak'];
56
+ readonly lineHeight?: string;
57
+ readonly marginRight?: string;
58
+ readonly position?: React.CSSProperties['position'];
59
+ readonly background?: string;
60
+ }
61
+
62
+ const SPACING_SCALE = 4;
63
+
64
+ function toStylePx(value: number | string | undefined): string | undefined {
65
+ if (value === undefined) return undefined;
66
+ if (typeof value === 'string') return value;
67
+ return `${ value * SPACING_SCALE }px`;
68
+ }
69
+
70
+ function extractSkeletonStyleProps(props: Record<string, unknown>): {
71
+ style: React.CSSProperties;
72
+ rest: Record<string, unknown>;
73
+ } {
74
+ const style: React.CSSProperties = {};
75
+ const rest: Record<string, unknown> = {};
76
+
77
+ function resolveDimension(value: unknown): string | undefined {
78
+ if (value === undefined || value === null) return undefined;
79
+ if (typeof value === 'number') return `${ value * SPACING_SCALE }px`;
80
+ if (typeof value === 'string') return value;
81
+ if (typeof value === 'object') {
82
+ const obj = value as Record<string, string>;
83
+ return obj.base ?? obj.lg ?? obj.xl ?? Object.values(obj)[0];
84
+ }
85
+ return undefined;
86
+ }
87
+
88
+ for (const [ key, value ] of Object.entries(props)) {
89
+ if (value === undefined) continue;
90
+ switch (key) {
91
+ case 'w': {
92
+ const v = resolveDimension(value); if (v) style.width = v; break;
93
+ }
94
+ case 'h': {
95
+ const v = resolveDimension(value); if (v) style.height = v; break;
96
+ }
97
+ case 'minW': {
98
+ const v = resolveDimension(value); if (v) style.minWidth = v; break;
99
+ }
100
+ case 'maxW': {
101
+ const v = resolveDimension(value); if (v) style.maxWidth = v; break;
102
+ }
103
+ case 'height': style.height = value as string; break;
104
+ case 'display': style.display = value as string; break;
105
+ case 'flexGrow': style.flexGrow = value as number; break;
106
+ case 'flexShrink': style.flexShrink = value as number; break;
107
+ case 'flexBasis': style.flexBasis = value as string; break;
108
+ case 'fontWeight': style.fontWeight = value as number | string; break;
109
+ case 'borderRadius': style.borderRadius = value as string; break;
110
+ case 'alignSelf': style.alignSelf = value as string; break;
111
+ case 'alignItems': style.alignItems = value as string; break;
112
+ case 'justifyContent': style.justifyContent = value as string; break;
113
+ case 'color': style.color = value as string; break;
114
+ case 'mt': style.marginTop = toStylePx(value as number | string); break;
115
+ case 'mb': style.marginBottom = toStylePx(value as number | string); break;
116
+ case 'ml': style.marginLeft = toStylePx(value as number | string); break;
117
+ case 'mr': style.marginRight = toStylePx(value as number | string); break;
118
+ case 'overflow': style.overflow = value as string; break;
119
+ case 'whiteSpace': style.whiteSpace = value as string; break;
120
+ case 'textOverflow': style.textOverflow = value as string; break;
121
+ case 'textTransform': style.textTransform = value as string; break;
122
+ case 'gap': style.gap = typeof value === 'number' ? `${ value * SPACING_SCALE }px` : value as string; break;
123
+ case 'gridTemplateColumns': style.gridTemplateColumns = value as string; break;
124
+ case 'minWidth': style.minWidth = value as string; break;
125
+ case 'boxSize': {
126
+ const s = typeof value === 'number' ? `${ value * SPACING_SCALE }px` : value as string; style.width = s; style.height = s; break;
127
+ }
128
+ case 'py': {
129
+ const v = toStylePx(value as number | string); style.paddingTop = v; style.paddingBottom = v; break;
130
+ }
131
+ case 'px': {
132
+ const v = toStylePx(value as number | string); style.paddingLeft = v; style.paddingRight = v; break;
133
+ }
134
+ case 'p': {
135
+ const v = toStylePx(value as number | string); style.padding = v; break;
136
+ }
137
+ case 'hideBelow': break; // handled via className
138
+ case 'textStyle': break; // drop textStyle, not directly applicable
139
+ case 'fontSize': style.fontSize = value as string; break;
140
+ case 'flexWrap': style.flexWrap = value as React.CSSProperties['flexWrap']; break;
141
+ case 'wordBreak': style.wordBreak = value as React.CSSProperties['wordBreak']; break;
142
+ case 'lineHeight': style.lineHeight = value as string; break;
143
+ case 'marginRight': style.marginRight = value as string; break;
144
+ case 'position': style.position = value as React.CSSProperties['position']; break;
145
+ case 'background': style.background = value as string; break;
146
+ default: rest[key] = value; break;
147
+ }
148
+ }
149
+
150
+ return { style, rest };
151
+ }
152
+
153
+ export const Skeleton = React.forwardRef<HTMLDivElement, SkeletonProps>(
154
+ function Skeleton(props, ref) {
155
+ const {
156
+ loading = false,
157
+ asChild,
158
+ className,
159
+ children,
160
+ style: styleProp,
161
+ // Destructure style-prop shims so they don't leak into DOM
162
+ w: _w, h: _h, minW: _minW, maxW: _maxW, display: _display,
163
+ flexGrow: _flexGrow, flexShrink: _flexShrink, flexBasis: _flexBasis,
164
+ fontWeight: _fontWeight, textStyle: _textStyle, borderRadius: _borderRadius,
165
+ alignSelf: _alignSelf, alignItems: _alignItems, justifyContent: _justifyContent,
166
+ color: _color, mt: _mt, mb: _mb, ml: _ml, mr: _mr, height: _height,
167
+ overflow: _overflow, whiteSpace: _whiteSpace, textOverflow: _textOverflow,
168
+ textTransform: _textTransform, gap: _gap, gridTemplateColumns: _gridTemplateColumns,
169
+ minWidth: _minWidth, boxSize: _boxSize, py: _py, px: _px, p: _p,
170
+ hideBelow: _hideBelow, fontSize: _fontSize, flexWrap: _flexWrap, wordBreak: _wordBreak, lineHeight: _lineHeight,
171
+ marginRight: _marginRight, position: _position, background: _background,
172
+ as: Component = 'div',
173
+ ...htmlRest
174
+ } = props;
175
+
176
+ // Collect legacy style props into a CSSProperties object
177
+ const { style: shimStyle } = extractSkeletonStyleProps({
178
+ w: _w, h: _h, minW: _minW, maxW: _maxW, display: _display,
179
+ flexGrow: _flexGrow, flexShrink: _flexShrink, flexBasis: _flexBasis,
180
+ fontWeight: _fontWeight, textStyle: _textStyle, borderRadius: _borderRadius,
181
+ alignSelf: _alignSelf, alignItems: _alignItems, justifyContent: _justifyContent,
182
+ color: _color, mt: _mt, mb: _mb, ml: _ml, mr: _mr, height: _height,
183
+ overflow: _overflow, whiteSpace: _whiteSpace, textOverflow: _textOverflow,
184
+ textTransform: _textTransform, gap: _gap, gridTemplateColumns: _gridTemplateColumns,
185
+ minWidth: _minWidth, boxSize: _boxSize, py: _py, px: _px, p: _p,
186
+ hideBelow: _hideBelow, fontSize: _fontSize, flexWrap: _flexWrap, wordBreak: _wordBreak, lineHeight: _lineHeight,
187
+ marginRight: _marginRight, position: _position, background: _background,
188
+ });
189
+ const mergedStyle = Object.keys(shimStyle).length > 0 || styleProp ?
190
+ { ...shimStyle, ...styleProp } :
191
+ undefined;
192
+
193
+ const HIDE_BELOW_MAP: Record<string, string> = { lg: 'lg:hidden', md: 'md:hidden', sm: 'sm:hidden' };
194
+ const hideBelowClass = _hideBelow ? HIDE_BELOW_MAP[_hideBelow] : undefined;
195
+ const finalClassName = hideBelowClass ? cn(className, hideBelowClass) : className;
196
+
197
+ if (!loading) {
198
+ // When asChild is true, render children directly without a wrapper div
199
+ if (asChild && React.isValidElement(children)) {
200
+ return children;
201
+ }
202
+ return (
203
+ <Component ref={ ref } className={ finalClassName } style={ mergedStyle } { ...htmlRest }>
204
+ { children }
205
+ </Component>
206
+ );
207
+ }
208
+
209
+ // When asChild is true and loading, wrap the child in skeleton styles
210
+ if (asChild && React.isValidElement(children)) {
211
+ return (
212
+ <Component
213
+ ref={ ref }
214
+ data-loading
215
+ className={ cn(
216
+ 'animate-skeleton-shimmer rounded-sm',
217
+ 'bg-[linear-gradient(90deg,var(--color-skeleton-start)_0%,var(--color-skeleton-end)_50%,var(--color-skeleton-start)_100%)]',
218
+ 'bg-[length:200%_100%]',
219
+ 'text-transparent [&_*]:invisible',
220
+ finalClassName,
221
+ ) }
222
+ style={ mergedStyle }
223
+ { ...htmlRest }
224
+ >
225
+ { children }
226
+ </Component>
227
+ );
228
+ }
229
+
230
+ return (
231
+ <Component
232
+ ref={ ref }
233
+ data-loading
234
+ className={ cn(
235
+ 'animate-skeleton-shimmer rounded-sm',
236
+ 'bg-[linear-gradient(90deg,var(--color-skeleton-start)_0%,var(--color-skeleton-end)_50%,var(--color-skeleton-start)_100%)]',
237
+ 'bg-[length:200%_100%]',
238
+ children ? 'text-transparent [&_*]:invisible' : 'min-h-5',
239
+ finalClassName,
240
+ ) }
241
+ style={ mergedStyle }
242
+ { ...htmlRest }
243
+ >
244
+ { children }
245
+ </Component>
246
+ );
247
+ },
248
+ );
249
+
250
+ // ---------------------------------------------------------------------------
251
+ // SkeletonCircle
252
+ // ---------------------------------------------------------------------------
253
+
254
+ export interface SkeletonCircleProps extends React.HTMLAttributes<HTMLDivElement> {
255
+ readonly size?: string | number;
256
+ readonly loading?: boolean;
257
+ }
258
+
259
+ export const SkeletonCircle = React.forwardRef<HTMLDivElement, SkeletonCircleProps>(
260
+ function SkeletonCircle(props, ref) {
261
+ const { size = 40, loading = true, className, ...rest } = props;
262
+
263
+ const dimension = typeof size === 'number' ? `${ size }px` : size;
264
+
265
+ return (
266
+ <Skeleton
267
+ ref={ ref }
268
+ loading={ loading }
269
+ className={ cn('rounded-full shrink-0', className) }
270
+ style={{ width: dimension, height: dimension, ...rest.style }}
271
+ { ...rest }
272
+ />
273
+ );
274
+ },
275
+ );
276
+
277
+ // ---------------------------------------------------------------------------
278
+ // SkeletonText
279
+ // ---------------------------------------------------------------------------
280
+
281
+ export interface SkeletonTextProps extends React.HTMLAttributes<HTMLDivElement> {
282
+ readonly noOfLines?: number;
283
+ readonly loading?: boolean;
284
+ }
285
+
286
+ export const SkeletonText = React.forwardRef<HTMLDivElement, SkeletonTextProps>(
287
+ function SkeletonText(props, ref) {
288
+ const { noOfLines = 3, loading = true, className, ...rest } = props;
289
+
290
+ return (
291
+ <div ref={ ref } className={ cn('flex w-full flex-col gap-2', className) } { ...rest }>
292
+ { Array.from({ length: noOfLines }).map((_, index) => (
293
+ <Skeleton
294
+ key={ index }
295
+ loading={ loading }
296
+ className={ cn('h-4', index === noOfLines - 1 && 'max-w-[80%]') }
297
+ />
298
+ )) }
299
+ </div>
300
+ );
301
+ },
302
+ );
package/src/slider.tsx ADDED
@@ -0,0 +1,152 @@
1
+ import * as RadixSlider from '@radix-ui/react-slider';
2
+ import * as React from 'react';
3
+
4
+ import { cn } from './utils';
5
+
6
+ export interface SliderProps {
7
+ readonly marks?: Array<number | { readonly value: number; readonly label: React.ReactNode }>;
8
+ readonly label?: React.ReactNode;
9
+ readonly showValue?: boolean;
10
+ readonly value?: Array<number>;
11
+ readonly defaultValue?: Array<number>;
12
+ readonly min?: number;
13
+ readonly max?: number;
14
+ readonly step?: number;
15
+ readonly disabled?: boolean;
16
+ readonly orientation?: 'horizontal' | 'vertical';
17
+ readonly name?: string;
18
+ readonly onValueChange?: (value: Array<number>) => void;
19
+ readonly onValueCommit?: (value: Array<number>) => void;
20
+ readonly className?: string;
21
+ }
22
+
23
+ const THUMB_SIZE = 20;
24
+ const TRACK_HEIGHT = 6;
25
+
26
+ export const Slider = React.forwardRef<HTMLSpanElement, SliderProps>(
27
+ function Slider(props, ref) {
28
+ const {
29
+ marks: marksProp,
30
+ label,
31
+ showValue,
32
+ value,
33
+ defaultValue,
34
+ min = 0,
35
+ max = 100,
36
+ step,
37
+ disabled,
38
+ orientation,
39
+ name,
40
+ onValueChange,
41
+ onValueCommit,
42
+ className,
43
+ } = props;
44
+
45
+ const resolvedValue = defaultValue ?? value;
46
+
47
+ const marks = marksProp?.map((mark) => {
48
+ if (typeof mark === 'number') return { value: mark, label: undefined };
49
+ return mark;
50
+ });
51
+
52
+ const hasMarkLabel = Boolean(marks?.some((mark) => mark.label));
53
+
54
+ return (
55
+ <div className={ cn('flex flex-col gap-1', className) }>
56
+ { label && !showValue && (
57
+ <label className="text-sm font-medium text-text-secondary">
58
+ { label }
59
+ </label>
60
+ ) }
61
+ { label && showValue && (
62
+ <div className="flex items-center justify-between">
63
+ <label className="text-sm font-medium text-text-secondary">
64
+ { label }
65
+ </label>
66
+ <span className="text-sm text-text-secondary">
67
+ { (value ?? defaultValue ?? [])?.join(', ') }
68
+ </span>
69
+ </div>
70
+ ) }
71
+
72
+ <RadixSlider.Root
73
+ ref={ ref }
74
+ className={ cn(
75
+ 'relative flex w-full touch-none select-none items-center',
76
+ hasMarkLabel && 'mb-6',
77
+ ) }
78
+ value={ value }
79
+ defaultValue={ defaultValue }
80
+ min={ min }
81
+ max={ max }
82
+ step={ step }
83
+ disabled={ disabled }
84
+ orientation={ orientation }
85
+ name={ name }
86
+ onValueChange={ onValueChange }
87
+ onValueCommit={ onValueCommit }
88
+ >
89
+ <RadixSlider.Track
90
+ className={ cn(
91
+ 'relative grow rounded-full bg-border-divider',
92
+ `h-[${ TRACK_HEIGHT }px]`,
93
+ ) }
94
+ >
95
+ <RadixSlider.Range className="absolute h-full rounded-full bg-link-primary"/>
96
+ </RadixSlider.Track>
97
+
98
+ { resolvedValue?.map((_, index) => (
99
+ <RadixSlider.Thumb
100
+ key={ index }
101
+ className={ cn(
102
+ `block h-[${ THUMB_SIZE }px] w-[${ THUMB_SIZE }px]`,
103
+ 'rounded-full border-2 border-link-primary bg-white',
104
+ 'shadow-sm transition-colors',
105
+ 'hover:border-link-primary-hover',
106
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-link-primary focus-visible:ring-offset-2',
107
+ 'disabled:pointer-events-none disabled:opacity-50',
108
+ ) }
109
+ />
110
+ )) }
111
+ </RadixSlider.Root>
112
+
113
+ <SliderMarks marks={ marks } min={ min } max={ max }/>
114
+ </div>
115
+ );
116
+ },
117
+ );
118
+
119
+ interface SliderMarksProps {
120
+ readonly marks?: Array<{ readonly value: number; readonly label?: React.ReactNode }>;
121
+ readonly min: number;
122
+ readonly max: number;
123
+ }
124
+
125
+ function SliderMarks(props: SliderMarksProps): React.ReactNode {
126
+ const { marks, min, max } = props;
127
+ if (!marks?.length) return null;
128
+
129
+ const range = max - min;
130
+
131
+ return (
132
+ <div className="relative w-full h-4">
133
+ { marks.map((mark, index) => {
134
+ const percent = ((mark.value - min) / range) * 100;
135
+ return (
136
+ <div
137
+ key={ index }
138
+ className="absolute flex flex-col items-center -translate-x-1/2"
139
+ style={{ left: `${ percent }%` }}
140
+ >
141
+ <div className="h-1.5 w-0.5 bg-border-divider"/>
142
+ { mark.label != null && (
143
+ <span className="mt-0.5 text-xs text-text-secondary whitespace-nowrap">
144
+ { mark.label }
145
+ </span>
146
+ ) }
147
+ </div>
148
+ );
149
+ }) }
150
+ </div>
151
+ );
152
+ }
package/src/switch.tsx ADDED
@@ -0,0 +1,158 @@
1
+ import * as RadixSwitch from '@radix-ui/react-switch';
2
+ // chakra() HOC removed — pure Radix + Tailwind
3
+ import * as React from 'react';
4
+
5
+ import { cn } from './utils';
6
+
7
+ const NOOP = () => { /* noop */ };
8
+
9
+ // ─── Size mappings ────────────────────────────────────────────────────
10
+ const SIZE_CLASSES = {
11
+ sm: {
12
+ root: 'h-4 w-7',
13
+ thumb: 'h-3 w-3 data-[state=checked]:translate-x-3',
14
+ label: 'text-xs',
15
+ },
16
+ md: {
17
+ root: 'h-5 w-9',
18
+ thumb: 'h-4 w-4 data-[state=checked]:translate-x-4',
19
+ label: 'text-sm',
20
+ },
21
+ lg: {
22
+ root: 'h-6 w-11',
23
+ thumb: 'h-5 w-5 data-[state=checked]:translate-x-5',
24
+ label: 'text-base',
25
+ },
26
+ } as const;
27
+
28
+ // ─── Props ────────────────────────────────────────────────────────────
29
+ export interface SwitchProps extends Omit<React.ComponentPropsWithoutRef<'label'>, 'onChange' | 'dir'> {
30
+ inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
31
+ labelProps?: React.ComponentPropsWithoutRef<'span'>;
32
+ rootRef?: React.Ref<HTMLLabelElement>;
33
+ trackLabel?: { on: React.ReactNode; off: React.ReactNode };
34
+ thumbLabel?: { on: React.ReactNode; off: React.ReactNode };
35
+ checked?: boolean;
36
+ defaultChecked?: boolean;
37
+ onCheckedChange?: (details: { checked: boolean }) => void;
38
+ onChange?: React.FormEventHandler<HTMLLabelElement>;
39
+ disabled?: boolean;
40
+ size?: 'sm' | 'md' | 'lg';
41
+ direction?: 'ltr' | 'rtl';
42
+ }
43
+
44
+ const SwitchBase = React.forwardRef<HTMLInputElement, SwitchProps>(
45
+ function Switch(props, ref) {
46
+ const {
47
+ inputProps,
48
+ children,
49
+ rootRef,
50
+ labelProps,
51
+ trackLabel,
52
+ thumbLabel,
53
+ checked: checkedProp,
54
+ defaultChecked,
55
+ onCheckedChange,
56
+ onChange,
57
+ disabled = false,
58
+ size = 'md',
59
+ direction,
60
+ className,
61
+ ...rest
62
+ } = props;
63
+
64
+ const [ internalChecked, setInternalChecked ] = React.useState(defaultChecked ?? false);
65
+
66
+ const isControlled = checkedProp !== undefined;
67
+ const checkedState = isControlled ? checkedProp : internalChecked;
68
+
69
+ const handleCheckedChange = React.useCallback(
70
+ (nextChecked: boolean) => {
71
+ if (!isControlled) {
72
+ setInternalChecked(nextChecked);
73
+ }
74
+
75
+ if (onCheckedChange) {
76
+ // Support both Chakra-style ({ checked }) and Radix-style (checked) signatures.
77
+ // We call with a single object arg; if the consumer ignores it, that's fine.
78
+ (onCheckedChange as (details: { checked: boolean }) => void)({ checked: nextChecked });
79
+ }
80
+ },
81
+ [ isControlled, onCheckedChange ],
82
+ );
83
+
84
+ const sizeClasses = SIZE_CLASSES[size];
85
+ const isRtl = direction === 'rtl';
86
+
87
+ return (
88
+ <label
89
+ ref={ rootRef }
90
+ className={ cn(
91
+ 'inline-flex items-center cursor-pointer select-none gap-2',
92
+ isRtl && 'flex-row-reverse',
93
+ disabled && 'opacity-50 cursor-not-allowed',
94
+ className,
95
+ ) }
96
+ onChange={ onChange }
97
+ data-disabled={ disabled || undefined }
98
+ { ...rest }
99
+ >
100
+ <RadixSwitch.Root
101
+ checked={ checkedState }
102
+ onCheckedChange={ handleCheckedChange }
103
+ disabled={ disabled }
104
+ className={ cn(
105
+ 'relative inline-flex shrink-0 items-center rounded-full',
106
+ 'bg-gray-300 dark:bg-gray-600',
107
+ 'data-[state=checked]:bg-gray-800 dark:bg-white',
108
+ 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-500',
109
+ 'transition-colors duration-200',
110
+ sizeClasses.root,
111
+ ) }
112
+ >
113
+ { trackLabel && (
114
+ <span className="absolute inset-0 flex items-center justify-center text-[8px] font-bold text-white pointer-events-none">
115
+ { checkedState ? trackLabel.on : trackLabel.off }
116
+ </span>
117
+ ) }
118
+ <RadixSwitch.Thumb
119
+ className={ cn(
120
+ 'block rounded-full bg-white shadow-sm',
121
+ 'transition-transform duration-200',
122
+ 'translate-x-0.5',
123
+ sizeClasses.thumb,
124
+ ) }
125
+ >
126
+ { thumbLabel && (
127
+ <span className="flex items-center justify-center h-full w-full text-[8px]">
128
+ { checkedState ? thumbLabel.on : thumbLabel.off }
129
+ </span>
130
+ ) }
131
+ </RadixSwitch.Thumb>
132
+ </RadixSwitch.Root>
133
+ { /* Hidden native input for form compat and ref forwarding */ }
134
+ <input
135
+ ref={ ref }
136
+ type="checkbox"
137
+ className="sr-only"
138
+ checked={ checkedState }
139
+ disabled={ disabled }
140
+ tabIndex={ -1 }
141
+ aria-hidden
142
+ onChange={ NOOP }
143
+ { ...inputProps }
144
+ />
145
+ { children != null && (
146
+ <span
147
+ { ...labelProps }
148
+ className={ cn(sizeClasses.label, labelProps?.className) }
149
+ >
150
+ { children }
151
+ </span>
152
+ ) }
153
+ </label>
154
+ );
155
+ },
156
+ );
157
+
158
+ export const Switch = SwitchBase;