@luxfi/ui 6.1.3 → 7.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.
- package/dist/accordion.cjs +213 -0
- package/dist/accordion.d.cts +74 -0
- package/dist/accordion.d.ts +74 -0
- package/dist/accordion.js +186 -0
- package/dist/alert.cjs +430 -0
- package/dist/alert.d.cts +30 -0
- package/dist/alert.d.ts +30 -0
- package/dist/alert.js +408 -0
- package/dist/avatar.cjs +149 -0
- package/dist/avatar.d.cts +25 -0
- package/dist/avatar.d.ts +25 -0
- package/dist/avatar.js +125 -0
- package/dist/badge.cjs +452 -0
- package/dist/badge.d.cts +19 -0
- package/dist/badge.d.ts +19 -0
- package/dist/badge.js +431 -0
- package/dist/bank.cjs +272 -0
- package/dist/bank.d.cts +76 -0
- package/dist/bank.d.ts +76 -0
- package/dist/bank.js +258 -0
- package/dist/button.cjs +578 -0
- package/dist/button.d.cts +55 -0
- package/dist/button.d.ts +55 -0
- package/dist/button.js +553 -0
- package/dist/checkbox.cjs +265 -0
- package/dist/checkbox.d.cts +31 -0
- package/dist/checkbox.d.ts +31 -0
- package/dist/checkbox.js +241 -0
- package/dist/close-button.cjs +92 -0
- package/dist/close-button.d.cts +9 -0
- package/dist/close-button.d.ts +9 -0
- package/dist/close-button.js +70 -0
- package/dist/collapsible.cjs +555 -0
- package/dist/collapsible.d.cts +23 -0
- package/dist/collapsible.d.ts +23 -0
- package/dist/collapsible.js +532 -0
- package/dist/color-mode.cjs +96 -0
- package/dist/color-mode.d.cts +25 -0
- package/dist/color-mode.d.ts +25 -0
- package/dist/color-mode.js +72 -0
- package/dist/dialog.cjs +295 -0
- package/dist/dialog.d.cts +65 -0
- package/dist/dialog.d.ts +65 -0
- package/dist/dialog.js +263 -0
- package/dist/drawer.cjs +231 -0
- package/dist/drawer.d.cts +36 -0
- package/dist/drawer.d.ts +36 -0
- package/dist/drawer.js +199 -0
- package/dist/empty-state.cjs +93 -0
- package/dist/empty-state.d.cts +13 -0
- package/dist/empty-state.d.ts +13 -0
- package/dist/empty-state.js +71 -0
- package/dist/expiration-selector.cjs +138 -0
- package/dist/expiration-selector.d.cts +16 -0
- package/dist/expiration-selector.d.ts +16 -0
- package/dist/expiration-selector.js +116 -0
- package/dist/field.cjs +183 -0
- package/dist/field.d.cts +24 -0
- package/dist/field.d.ts +24 -0
- package/dist/field.js +160 -0
- package/dist/greeks-display.cjs +114 -0
- package/dist/greeks-display.d.cts +14 -0
- package/dist/greeks-display.d.ts +14 -0
- package/dist/greeks-display.js +92 -0
- package/dist/heading.cjs +41 -0
- package/dist/heading.d.cts +10 -0
- package/dist/heading.d.ts +10 -0
- package/dist/heading.js +35 -0
- package/dist/icon-button.cjs +367 -0
- package/dist/icon-button.d.cts +33 -0
- package/dist/icon-button.d.ts +33 -0
- package/dist/icon-button.js +345 -0
- package/dist/image.cjs +425 -0
- package/dist/image.d.cts +57 -0
- package/dist/image.d.ts +57 -0
- package/dist/image.js +404 -0
- package/dist/index.cjs +7220 -0
- package/dist/index.d.cts +55 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +7043 -0
- package/dist/input-group.cjs +155 -0
- package/dist/input-group.d.cts +17 -0
- package/dist/input-group.d.ts +17 -0
- package/dist/input-group.js +133 -0
- package/dist/input.cjs +65 -0
- package/dist/input.d.cts +11 -0
- package/dist/input.d.ts +11 -0
- package/dist/input.js +59 -0
- package/dist/link.cjs +483 -0
- package/dist/link.d.cts +37 -0
- package/dist/link.d.ts +37 -0
- package/dist/link.js +459 -0
- package/dist/menu.cjs +305 -0
- package/dist/menu.d.cts +84 -0
- package/dist/menu.d.ts +84 -0
- package/dist/menu.js +269 -0
- package/dist/option-chain.cjs +221 -0
- package/dist/option-chain.d.cts +37 -0
- package/dist/option-chain.d.ts +37 -0
- package/dist/option-chain.js +199 -0
- package/dist/option-position.cjs +164 -0
- package/dist/option-position.d.cts +29 -0
- package/dist/option-position.d.ts +29 -0
- package/dist/option-position.js +142 -0
- package/dist/pin-input.cjs +182 -0
- package/dist/pin-input.d.cts +25 -0
- package/dist/pin-input.d.ts +25 -0
- package/dist/pin-input.js +160 -0
- package/dist/pnl-diagram.cjs +453 -0
- package/dist/pnl-diagram.d.cts +11 -0
- package/dist/pnl-diagram.d.ts +11 -0
- package/dist/pnl-diagram.js +431 -0
- package/dist/popover.cjs +342 -0
- package/dist/popover.d.cts +73 -0
- package/dist/popover.d.ts +73 -0
- package/dist/popover.js +310 -0
- package/dist/progress-circle.cjs +152 -0
- package/dist/progress-circle.d.cts +47 -0
- package/dist/progress-circle.d.ts +47 -0
- package/dist/progress-circle.js +128 -0
- package/dist/progress.cjs +117 -0
- package/dist/progress.d.cts +28 -0
- package/dist/progress.d.ts +28 -0
- package/dist/progress.js +94 -0
- package/dist/provider.cjs +62 -0
- package/dist/provider.d.cts +7 -0
- package/dist/provider.d.ts +7 -0
- package/dist/provider.js +40 -0
- package/dist/radio.cjs +177 -0
- package/dist/radio.d.cts +53 -0
- package/dist/radio.d.ts +53 -0
- package/dist/radio.js +153 -0
- package/dist/rating.cjs +80 -0
- package/dist/rating.d.cts +14 -0
- package/dist/rating.d.ts +14 -0
- package/dist/rating.js +58 -0
- package/dist/select.cjs +644 -0
- package/dist/select.d.cts +130 -0
- package/dist/select.d.ts +130 -0
- package/dist/select.js +610 -0
- package/dist/separator.cjs +57 -0
- package/dist/separator.d.cts +13 -0
- package/dist/separator.d.ts +13 -0
- package/dist/separator.js +51 -0
- package/dist/skeleton.cjs +223 -0
- package/dist/skeleton.d.cts +60 -0
- package/dist/skeleton.d.ts +60 -0
- package/dist/skeleton.js +199 -0
- package/dist/slider.cjs +138 -0
- package/dist/slider.d.cts +24 -0
- package/dist/slider.d.ts +24 -0
- package/dist/slider.js +115 -0
- package/dist/strategy-builder.cjs +373 -0
- package/dist/strategy-builder.d.cts +26 -0
- package/dist/strategy-builder.d.ts +26 -0
- package/dist/strategy-builder.js +351 -0
- package/dist/switch.cjs +163 -0
- package/dist/switch.d.cts +27 -0
- package/dist/switch.d.ts +27 -0
- package/dist/switch.js +140 -0
- package/dist/table.cjs +897 -0
- package/dist/table.d.cts +107 -0
- package/dist/table.d.ts +107 -0
- package/dist/table.js +866 -0
- package/dist/tabs.cjs +240 -0
- package/dist/tabs.d.cts +78 -0
- package/dist/tabs.d.ts +78 -0
- package/dist/tabs.js +213 -0
- package/dist/tag.cjs +529 -0
- package/dist/tag.d.cts +21 -0
- package/dist/tag.d.ts +21 -0
- package/dist/tag.js +507 -0
- package/dist/textarea.cjs +65 -0
- package/dist/textarea.d.cts +11 -0
- package/dist/textarea.d.ts +11 -0
- package/dist/textarea.js +59 -0
- package/dist/toaster.cjs +99 -0
- package/dist/toaster.d.cts +29 -0
- package/dist/toaster.d.ts +29 -0
- package/dist/toaster.js +96 -0
- package/dist/tooltip.cjs +172 -0
- package/dist/tooltip.d.cts +38 -0
- package/dist/tooltip.d.ts +38 -0
- package/dist/tooltip.js +150 -0
- package/dist/utils.cjs +11 -0
- package/dist/utils.d.cts +5 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +9 -0
- package/package.json +284 -89
- package/src/accordion.tsx +285 -0
- package/src/alert.tsx +221 -0
- package/src/avatar.tsx +174 -0
- package/src/badge.tsx +147 -0
- package/src/bank.tsx +325 -0
- package/src/button.tsx +477 -0
- package/src/checkbox.tsx +307 -0
- package/src/close-button.tsx +77 -0
- package/src/collapsible.tsx +126 -0
- package/src/color-mode.tsx +125 -0
- package/src/dialog.tsx +346 -0
- package/src/drawer.tsx +186 -0
- package/src/empty-state.tsx +97 -0
- package/src/expiration-selector.tsx +163 -0
- package/src/field.tsx +202 -0
- package/src/greeks-display.tsx +138 -0
- package/src/heading.tsx +44 -0
- package/src/icon-button.tsx +217 -0
- package/src/image.tsx +280 -0
- package/src/index.ts +267 -138
- package/src/input-group.tsx +159 -0
- package/src/input.tsx +60 -0
- package/src/link.tsx +326 -0
- package/src/menu.tsx +471 -0
- package/src/option-chain.tsx +261 -0
- package/src/option-position.tsx +200 -0
- package/src/pin-input.tsx +187 -0
- package/src/pnl-diagram.tsx +458 -0
- package/src/popover.tsx +386 -0
- package/src/progress-circle.tsx +180 -0
- package/src/progress.tsx +109 -0
- package/src/provider.tsx +12 -0
- package/src/radio.tsx +175 -0
- package/src/rating.tsx +79 -0
- package/src/select.tsx +696 -0
- package/src/separator.tsx +59 -0
- package/src/skeleton.tsx +254 -0
- package/src/slider.tsx +152 -0
- package/src/strategy-builder.tsx +466 -0
- package/src/switch.tsx +158 -0
- package/src/table.tsx +621 -0
- package/src/tabs.tsx +354 -0
- package/src/tag.tsx +159 -0
- package/src/textarea.tsx +60 -0
- package/src/toaster.tsx +117 -0
- package/src/tokens.css +438 -0
- package/src/tooltip.tsx +191 -0
- package/src/utils/cn.ts +7 -0
- package/src/utils.ts +6 -0
- package/tokens.css +438 -0
- package/LICENSE +0 -122
- package/README.md +0 -109
|
@@ -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
|
+
);
|
package/src/skeleton.tsx
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
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 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
|
+
// Style-prop shim names that must NOT leak into the DOM.
|
|
63
|
+
const SHIM_PROPS = [
|
|
64
|
+
'w', 'h', 'minW', 'maxW', 'display', 'flexGrow', 'flexShrink', 'flexBasis',
|
|
65
|
+
'fontWeight', 'textStyle', 'borderRadius', 'alignSelf', 'alignItems',
|
|
66
|
+
'justifyContent', 'color', 'mt', 'mb', 'ml', 'mr', 'height', 'overflow',
|
|
67
|
+
'whiteSpace', 'textOverflow', 'textTransform', 'gap', 'gridTemplateColumns',
|
|
68
|
+
'minWidth', 'boxSize', 'py', 'px', 'p', 'hideBelow', 'fontSize', 'flexWrap',
|
|
69
|
+
'wordBreak', 'lineHeight', 'marginRight', 'position', 'background',
|
|
70
|
+
] as const;
|
|
71
|
+
|
|
72
|
+
const S = 4; // Chakra spacing scale
|
|
73
|
+
|
|
74
|
+
/** Resolve a dimension value (number → px via spacing scale, responsive object → first value). */
|
|
75
|
+
function dim(v: unknown): string | undefined {
|
|
76
|
+
if (v == null) return undefined;
|
|
77
|
+
if (typeof v === 'number') return `${ v * S }px`;
|
|
78
|
+
if (typeof v === 'string') return v;
|
|
79
|
+
if (typeof v === 'object') {
|
|
80
|
+
const o = v as Record<string, string>;
|
|
81
|
+
return o.base ?? o.lg ?? o.xl ?? Object.values(o)[0];
|
|
82
|
+
}
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Convert a spacing number|string via the 4px scale. */
|
|
87
|
+
function sp(v: number | string | undefined): string | undefined {
|
|
88
|
+
if (v === undefined) return undefined;
|
|
89
|
+
return typeof v === 'number' ? `${ v * S }px` : v;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Build a CSSProperties object from legacy Chakra style-prop shims. */
|
|
93
|
+
function shimToStyle(props: SkeletonProps): React.CSSProperties {
|
|
94
|
+
const s: React.CSSProperties = {};
|
|
95
|
+
if (props.w !== undefined) s.width = dim(props.w);
|
|
96
|
+
if (props.h !== undefined) s.height = dim(props.h);
|
|
97
|
+
if (props.minW !== undefined) s.minWidth = dim(props.minW);
|
|
98
|
+
if (props.maxW !== undefined) s.maxWidth = dim(props.maxW);
|
|
99
|
+
if (props.display !== undefined) s.display = props.display;
|
|
100
|
+
if (props.flexGrow !== undefined) s.flexGrow = props.flexGrow;
|
|
101
|
+
if (props.flexShrink !== undefined) s.flexShrink = props.flexShrink;
|
|
102
|
+
if (props.flexBasis !== undefined) s.flexBasis = props.flexBasis;
|
|
103
|
+
if (props.fontWeight !== undefined) s.fontWeight = props.fontWeight;
|
|
104
|
+
if (props.borderRadius !== undefined) s.borderRadius = props.borderRadius;
|
|
105
|
+
if (props.alignSelf !== undefined) s.alignSelf = props.alignSelf;
|
|
106
|
+
if (props.alignItems !== undefined) s.alignItems = props.alignItems;
|
|
107
|
+
if (props.justifyContent !== undefined) s.justifyContent = props.justifyContent;
|
|
108
|
+
if (props.color !== undefined) s.color = props.color;
|
|
109
|
+
if (props.mt !== undefined) s.marginTop = sp(props.mt);
|
|
110
|
+
if (props.mb !== undefined) s.marginBottom = sp(props.mb);
|
|
111
|
+
if (props.ml !== undefined) s.marginLeft = sp(props.ml);
|
|
112
|
+
if (props.mr !== undefined) s.marginRight = sp(props.mr);
|
|
113
|
+
if (props.height !== undefined) s.height = props.height;
|
|
114
|
+
if (props.overflow !== undefined) s.overflow = props.overflow;
|
|
115
|
+
if (props.whiteSpace !== undefined) s.whiteSpace = props.whiteSpace;
|
|
116
|
+
if (props.textOverflow !== undefined) s.textOverflow = props.textOverflow;
|
|
117
|
+
if (props.textTransform !== undefined) s.textTransform = props.textTransform;
|
|
118
|
+
if (props.gap !== undefined) s.gap = sp(props.gap);
|
|
119
|
+
if (props.gridTemplateColumns !== undefined) s.gridTemplateColumns = props.gridTemplateColumns;
|
|
120
|
+
if (props.minWidth !== undefined) s.minWidth = props.minWidth;
|
|
121
|
+
if (props.boxSize !== undefined) {
|
|
122
|
+
const v = typeof props.boxSize === 'number' ? `${ props.boxSize * S }px` : props.boxSize;
|
|
123
|
+
s.width = v; s.height = v;
|
|
124
|
+
}
|
|
125
|
+
if (props.py !== undefined) { const v = sp(props.py); s.paddingTop = v; s.paddingBottom = v; }
|
|
126
|
+
if (props.px !== undefined) { const v = sp(props.px); s.paddingLeft = v; s.paddingRight = v; }
|
|
127
|
+
if (props.p !== undefined) s.padding = sp(props.p);
|
|
128
|
+
if (props.fontSize !== undefined) s.fontSize = props.fontSize;
|
|
129
|
+
if (props.flexWrap !== undefined) s.flexWrap = props.flexWrap;
|
|
130
|
+
if (props.wordBreak !== undefined) s.wordBreak = props.wordBreak;
|
|
131
|
+
if (props.lineHeight !== undefined) s.lineHeight = props.lineHeight;
|
|
132
|
+
if (props.marginRight !== undefined) s.marginRight = props.marginRight;
|
|
133
|
+
if (props.position !== undefined) s.position = props.position;
|
|
134
|
+
if (props.background !== undefined) s.background = props.background;
|
|
135
|
+
return s;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Strip shim keys from props so they don't leak into the DOM. */
|
|
139
|
+
function stripShims<T extends Record<string, unknown>>(props: T): Omit<T, (typeof SHIM_PROPS)[number]> {
|
|
140
|
+
const out = { ...props };
|
|
141
|
+
for (const k of SHIM_PROPS) delete out[k];
|
|
142
|
+
return out as Omit<T, (typeof SHIM_PROPS)[number]>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const SKELETON_CLASSES = [
|
|
146
|
+
'animate-skeleton-shimmer rounded-sm',
|
|
147
|
+
'bg-[linear-gradient(90deg,var(--color-skeleton-start)_0%,var(--color-skeleton-end)_50%,var(--color-skeleton-start)_100%)]',
|
|
148
|
+
'bg-[length:200%_100%]',
|
|
149
|
+
].join(' ');
|
|
150
|
+
|
|
151
|
+
const HIDE_BELOW_MAP: Record<string, string> = { lg: 'lg:hidden', md: 'md:hidden', sm: 'sm:hidden' };
|
|
152
|
+
|
|
153
|
+
export const Skeleton = React.forwardRef<HTMLDivElement, SkeletonProps>(
|
|
154
|
+
function Skeleton(props, ref) {
|
|
155
|
+
const { loading = false, asChild, className, children, style: styleProp, as: Component = 'div', ...allRest } = props;
|
|
156
|
+
|
|
157
|
+
const shimStyle = shimToStyle(props);
|
|
158
|
+
const mergedStyle = Object.keys(shimStyle).length > 0 || styleProp ? { ...shimStyle, ...styleProp } : undefined;
|
|
159
|
+
const hideBelowClass = props.hideBelow ? HIDE_BELOW_MAP[props.hideBelow] : undefined;
|
|
160
|
+
const cls = hideBelowClass ? cn(className, hideBelowClass) : className;
|
|
161
|
+
|
|
162
|
+
// Strip shim props from what reaches the DOM.
|
|
163
|
+
const htmlRest = stripShims(allRest);
|
|
164
|
+
|
|
165
|
+
if (!loading) {
|
|
166
|
+
if (asChild && React.isValidElement(children)) return children;
|
|
167
|
+
return (
|
|
168
|
+
<Component ref={ ref } className={ cls } style={ mergedStyle } { ...htmlRest }>
|
|
169
|
+
{ children }
|
|
170
|
+
</Component>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (asChild && React.isValidElement(children)) {
|
|
175
|
+
return (
|
|
176
|
+
<Component
|
|
177
|
+
ref={ ref }
|
|
178
|
+
data-loading
|
|
179
|
+
className={ cn(SKELETON_CLASSES, 'text-transparent [&_*]:invisible', cls) }
|
|
180
|
+
style={ mergedStyle }
|
|
181
|
+
{ ...htmlRest }
|
|
182
|
+
>
|
|
183
|
+
{ children }
|
|
184
|
+
</Component>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<Component
|
|
190
|
+
ref={ ref }
|
|
191
|
+
data-loading
|
|
192
|
+
className={ cn(SKELETON_CLASSES, children ? 'text-transparent [&_*]:invisible' : 'min-h-5', cls) }
|
|
193
|
+
style={ mergedStyle }
|
|
194
|
+
{ ...htmlRest }
|
|
195
|
+
>
|
|
196
|
+
{ children }
|
|
197
|
+
</Component>
|
|
198
|
+
);
|
|
199
|
+
},
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
// SkeletonCircle
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
export interface SkeletonCircleProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
207
|
+
readonly size?: string | number;
|
|
208
|
+
readonly loading?: boolean;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export const SkeletonCircle = React.forwardRef<HTMLDivElement, SkeletonCircleProps>(
|
|
212
|
+
function SkeletonCircle(props, ref) {
|
|
213
|
+
const { size = 40, loading = true, className, ...rest } = props;
|
|
214
|
+
|
|
215
|
+
const dimension = typeof size === 'number' ? `${ size }px` : size;
|
|
216
|
+
|
|
217
|
+
return (
|
|
218
|
+
<Skeleton
|
|
219
|
+
ref={ ref }
|
|
220
|
+
loading={ loading }
|
|
221
|
+
className={ cn('rounded-full shrink-0', className) }
|
|
222
|
+
style={{ width: dimension, height: dimension, ...rest.style }}
|
|
223
|
+
{ ...rest }
|
|
224
|
+
/>
|
|
225
|
+
);
|
|
226
|
+
},
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
// SkeletonText
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
|
|
233
|
+
export interface SkeletonTextProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
234
|
+
readonly noOfLines?: number;
|
|
235
|
+
readonly loading?: boolean;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export const SkeletonText = React.forwardRef<HTMLDivElement, SkeletonTextProps>(
|
|
239
|
+
function SkeletonText(props, ref) {
|
|
240
|
+
const { noOfLines = 3, loading = true, className, ...rest } = props;
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<div ref={ ref } className={ cn('flex w-full flex-col gap-2', className) } { ...rest }>
|
|
244
|
+
{ Array.from({ length: noOfLines }).map((_, index) => (
|
|
245
|
+
<Skeleton
|
|
246
|
+
key={ index }
|
|
247
|
+
loading={ loading }
|
|
248
|
+
className={ cn('h-4', index === noOfLines - 1 && 'max-w-[80%]') }
|
|
249
|
+
/>
|
|
250
|
+
)) }
|
|
251
|
+
</div>
|
|
252
|
+
);
|
|
253
|
+
},
|
|
254
|
+
);
|
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
|
+
}
|