@luxfi/ui 7.4.11 → 7.4.13
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/alert.cjs +68 -46
- package/dist/alert.js +69 -46
- package/dist/avatar.cjs +49 -47
- package/dist/avatar.js +52 -49
- package/dist/badge.cjs +82 -49
- package/dist/badge.js +83 -50
- package/dist/checkbox.cjs +42 -75
- package/dist/checkbox.js +43 -76
- package/dist/chrome.cjs +3 -7
- package/dist/chrome.js +3 -7
- package/dist/close-button.cjs +3 -7
- package/dist/close-button.js +3 -7
- package/dist/dialog.cjs +3 -7
- package/dist/dialog.js +3 -7
- package/dist/drawer.cjs +3 -7
- package/dist/drawer.js +3 -7
- package/dist/empty-state.cjs +13 -13
- package/dist/empty-state.js +13 -13
- package/dist/expiration-selector.cjs +58 -63
- package/dist/expiration-selector.js +58 -63
- package/dist/greeks-display.cjs +48 -39
- package/dist/greeks-display.js +48 -39
- package/dist/index.cjs +1053 -888
- package/dist/index.js +1054 -889
- package/dist/input-group.cjs +36 -19
- package/dist/input-group.js +37 -19
- package/dist/option-chain.cjs +89 -67
- package/dist/option-chain.js +89 -67
- package/dist/option-position.cjs +88 -85
- package/dist/option-position.js +88 -85
- package/dist/pin-input.cjs +30 -29
- package/dist/pin-input.js +30 -29
- package/dist/pnl-diagram.cjs +20 -20
- package/dist/pnl-diagram.js +20 -20
- package/dist/popover.cjs +3 -7
- package/dist/popover.js +3 -7
- package/dist/progress-circle.cjs +40 -22
- package/dist/progress-circle.d.cts +5 -5
- package/dist/progress-circle.d.ts +5 -5
- package/dist/progress-circle.js +41 -22
- package/dist/progress.cjs +15 -22
- package/dist/progress.d.cts +6 -6
- package/dist/progress.d.ts +6 -6
- package/dist/progress.js +15 -22
- package/dist/radio.cjs +35 -68
- package/dist/radio.d.cts +18 -18
- package/dist/radio.d.ts +18 -18
- package/dist/radio.js +36 -69
- package/dist/rating.cjs +15 -17
- package/dist/rating.js +15 -17
- package/dist/slider.cjs +33 -50
- package/dist/slider.js +34 -51
- package/dist/strategy-builder.cjs +194 -115
- package/dist/strategy-builder.js +194 -115
- package/dist/switch.cjs +48 -55
- package/dist/switch.js +49 -56
- package/dist/tag.cjs +115 -69
- package/dist/tag.js +116 -69
- package/dist/tooltip.cjs +28 -17
- package/dist/tooltip.js +30 -18
- package/package.json +1 -1
- package/src/alert.tsx +78 -45
- package/src/avatar.tsx +54 -38
- package/src/badge.tsx +65 -44
- package/src/checkbox.tsx +70 -89
- package/src/close-button.tsx +3 -8
- package/src/empty-state.tsx +21 -17
- package/src/expiration-selector.tsx +84 -66
- package/src/greeks-display.tsx +59 -51
- package/src/input-group.tsx +38 -24
- package/src/option-chain.tsx +154 -104
- package/src/option-position.tsx +143 -114
- package/src/pin-input.tsx +37 -29
- package/src/pnl-diagram.tsx +36 -28
- package/src/progress-circle.tsx +52 -28
- package/src/progress.tsx +17 -21
- package/src/radio.tsx +56 -76
- package/src/rating.tsx +22 -20
- package/src/slider.tsx +43 -45
- package/src/strategy-builder.tsx +260 -162
- package/src/switch.tsx +63 -64
- package/src/tag.tsx +89 -51
- package/src/tooltip.tsx +21 -13
- package/tokens.css +18 -0
package/src/avatar.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { Avatar as GuiAvatar } from '@hanzo/gui';
|
|
3
|
+
import { Avatar as GuiAvatar, XStack } from '@hanzo/gui';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { ink } from './ink';
|
|
7
7
|
|
|
8
8
|
type ImageProps = React.ImgHTMLAttributes<HTMLImageElement>;
|
|
9
9
|
|
|
@@ -12,19 +12,29 @@ type ImageProps = React.ImgHTMLAttributes<HTMLImageElement>;
|
|
|
12
12
|
type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
|
13
13
|
type AvatarVariant = 'solid' | 'subtle' | 'outline';
|
|
14
14
|
|
|
15
|
-
const
|
|
16
|
-
xs:
|
|
17
|
-
sm:
|
|
18
|
-
md:
|
|
19
|
-
lg:
|
|
20
|
-
xl:
|
|
21
|
-
'2xl':
|
|
15
|
+
const SIZE: Record<AvatarSize, { width: number; height: number; fontSize: number }> = {
|
|
16
|
+
xs: { width: 24, height: 24, fontSize: 10 },
|
|
17
|
+
sm: { width: 32, height: 32, fontSize: 12 },
|
|
18
|
+
md: { width: 40, height: 40, fontSize: 14 },
|
|
19
|
+
lg: { width: 48, height: 48, fontSize: 16 },
|
|
20
|
+
xl: { width: 64, height: 64, fontSize: 18 },
|
|
21
|
+
'2xl': { width: 80, height: 80, fontSize: 20 },
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
// The ring colour matches the page ground rather than staying a fixed white,
|
|
25
|
+
// so a stack of overlapping avatars reads as cut out of the page in both
|
|
26
|
+
// themes — the same job `ring-white dark:ring-gray-900` did.
|
|
27
|
+
const RING = 'var(--color-bg-body)' as never;
|
|
28
|
+
|
|
29
|
+
const VARIANT: Record<AvatarVariant, Record<string, unknown>> = {
|
|
30
|
+
solid: { backgroundColor: 'var(--color-gray-500)' as never, color: 'var(--color-white)' as never },
|
|
31
|
+
subtle: { backgroundColor: 'var(--color-badge-gray-bg)' as never, color: 'var(--color-badge-gray-fg)' as never },
|
|
32
|
+
outline: {
|
|
33
|
+
borderWidth: 2,
|
|
34
|
+
borderColor: 'var(--color-input-border)' as never,
|
|
35
|
+
backgroundColor: 'transparent',
|
|
36
|
+
color: 'var(--color-text-secondary)' as never,
|
|
37
|
+
},
|
|
28
38
|
};
|
|
29
39
|
|
|
30
40
|
const DEFAULT_SIZE: AvatarSize = 'md';
|
|
@@ -69,7 +79,6 @@ export const Avatar = React.forwardRef<HTMLSpanElement, AvatarProps>(
|
|
|
69
79
|
icon,
|
|
70
80
|
fallback,
|
|
71
81
|
children,
|
|
72
|
-
className,
|
|
73
82
|
size: sizeProp,
|
|
74
83
|
variant: variantProp,
|
|
75
84
|
borderless,
|
|
@@ -85,14 +94,16 @@ export const Avatar = React.forwardRef<HTMLSpanElement, AvatarProps>(
|
|
|
85
94
|
return (
|
|
86
95
|
<GuiAvatar
|
|
87
96
|
ref={ ref as never }
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
position="relative"
|
|
98
|
+
display="inline-flex"
|
|
99
|
+
flexShrink={ 0 }
|
|
100
|
+
alignItems="center"
|
|
101
|
+
justifyContent="center"
|
|
102
|
+
overflow="hidden"
|
|
103
|
+
borderRadius={ 9999 }
|
|
104
|
+
{ ...SIZE[size] }
|
|
105
|
+
{ ...VARIANT[variant] }
|
|
106
|
+
boxShadow={ isBorderless ? undefined : `0 0 0 2px ${ RING }` }
|
|
96
107
|
{ ...(rest as object) }
|
|
97
108
|
>
|
|
98
109
|
<AvatarFallback name={ name } icon={ icon }>
|
|
@@ -100,7 +111,10 @@ export const Avatar = React.forwardRef<HTMLSpanElement, AvatarProps>(
|
|
|
100
111
|
</AvatarFallback>
|
|
101
112
|
{ src != null && (
|
|
102
113
|
<GuiAvatar.Image
|
|
103
|
-
|
|
114
|
+
width="100%"
|
|
115
|
+
height="100%"
|
|
116
|
+
borderRadius={ 9999 }
|
|
117
|
+
objectFit="cover"
|
|
104
118
|
src={ src }
|
|
105
119
|
srcSet={ srcSet }
|
|
106
120
|
loading={ loading }
|
|
@@ -122,20 +136,18 @@ interface AvatarFallbackInternalProps extends React.ComponentPropsWithoutRef<'sp
|
|
|
122
136
|
|
|
123
137
|
const AvatarFallback = React.forwardRef<HTMLSpanElement, AvatarFallbackInternalProps>(
|
|
124
138
|
function AvatarFallback(props, ref) {
|
|
125
|
-
const { name, icon, children,
|
|
139
|
+
const { name, icon, children, ...rest } = props;
|
|
126
140
|
return (
|
|
127
141
|
<GuiAvatar.Fallback
|
|
128
142
|
ref={ ref as never }
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
143
|
+
display="flex"
|
|
144
|
+
width="100%"
|
|
145
|
+
height="100%"
|
|
146
|
+
alignItems="center"
|
|
147
|
+
justifyContent="center"
|
|
134
148
|
{ ...(rest as object) }
|
|
135
149
|
>
|
|
136
|
-
{ children }
|
|
137
|
-
{ name != null && children == null && <>{ getInitials(name) }</> }
|
|
138
|
-
{ name == null && children == null && icon }
|
|
150
|
+
{ ink(children ?? (name != null ? getInitials(name) : icon), undefined, { fontWeight: '500' }) }
|
|
139
151
|
</GuiAvatar.Fallback>
|
|
140
152
|
);
|
|
141
153
|
},
|
|
@@ -162,18 +174,22 @@ interface AvatarGroupProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
|
162
174
|
|
|
163
175
|
export const AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(
|
|
164
176
|
function AvatarGroup(props, ref) {
|
|
165
|
-
const { size, variant, borderless,
|
|
177
|
+
const { size, variant, borderless, children, ...rest } = props;
|
|
166
178
|
const contextValue = React.useMemo(
|
|
167
179
|
() => ({ size, variant, borderless }),
|
|
168
180
|
[ size, variant, borderless ],
|
|
169
181
|
);
|
|
170
182
|
return (
|
|
171
183
|
<AvatarGroupContext.Provider value={ contextValue }>
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
{
|
|
176
|
-
|
|
184
|
+
{ /* `-space-x-3`'s sibling-margin trick, done by cloning: each avatar
|
|
185
|
+
after the first overlaps the one before it by 12px. */ }
|
|
186
|
+
<XStack ref={ ref as never } { ...(rest as object) }>
|
|
187
|
+
{ React.Children.map(children, (child, index) => (
|
|
188
|
+
index === 0 || !React.isValidElement(child) ?
|
|
189
|
+
child :
|
|
190
|
+
React.cloneElement(child as React.ReactElement<{ marginLeft?: number }>, { marginLeft: -12 })
|
|
191
|
+
)) }
|
|
192
|
+
</XStack>
|
|
177
193
|
</AvatarGroupContext.Provider>
|
|
178
194
|
);
|
|
179
195
|
},
|
package/src/badge.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import { Text, XStack } from '@hanzo/gui';
|
|
1
2
|
import React from 'react';
|
|
2
3
|
|
|
3
|
-
import { cn } from './utils';
|
|
4
|
-
|
|
5
4
|
import { Skeleton } from './skeleton';
|
|
6
5
|
import { Tooltip } from './tooltip';
|
|
7
6
|
|
|
@@ -21,37 +20,45 @@ type ColorPalette =
|
|
|
21
20
|
'bright_yellow' | 'bright_teal' | 'bright_cyan' | 'bright_orange' |
|
|
22
21
|
'bright_purple' | 'bright_pink';
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
23
|
+
type BadgeSize = 'sm' | 'md' | 'lg';
|
|
24
|
+
|
|
25
|
+
/** The chip itself — layout only. `color`/`fontSize` are Text props, not View props. */
|
|
26
|
+
const SIZE: Record<BadgeSize, Record<string, number>> = {
|
|
27
|
+
sm: { padding: 4, height: 18, minHeight: 18 },
|
|
28
|
+
md: { paddingHorizontal: 4, paddingVertical: 2, minHeight: 24 },
|
|
29
|
+
lg: { paddingHorizontal: 8, paddingVertical: 4, minHeight: 28 },
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** The label inside it — same three sizes, the typography half. */
|
|
33
|
+
const SIZE_TEXT: Record<BadgeSize, { fontSize: number; fontWeight: '500' | '600' }> = {
|
|
34
|
+
sm: { fontSize: 12, fontWeight: '500' },
|
|
35
|
+
md: { fontSize: 14, fontWeight: '500' },
|
|
36
|
+
lg: { fontSize: 14, fontWeight: '600' },
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const COLOR_PALETTE: Record<ColorPalette, { bg: string; fg: string }> = {
|
|
40
|
+
gray: { bg: 'var(--color-badge-gray-bg)', fg: 'var(--color-badge-gray-fg)' },
|
|
41
|
+
green: { bg: 'var(--color-badge-green-bg)', fg: 'var(--color-badge-green-fg)' },
|
|
42
|
+
red: { bg: 'var(--color-badge-red-bg)', fg: 'var(--color-badge-red-fg)' },
|
|
43
|
+
purple: { bg: 'var(--color-badge-purple-bg)', fg: 'var(--color-badge-purple-fg)' },
|
|
44
|
+
orange: { bg: 'var(--color-badge-orange-bg)', fg: 'var(--color-badge-orange-fg)' },
|
|
45
|
+
blue: { bg: 'var(--color-badge-blue-bg)', fg: 'var(--color-badge-blue-fg)' },
|
|
46
|
+
yellow: { bg: 'var(--color-badge-yellow-bg)', fg: 'var(--color-badge-yellow-fg)' },
|
|
47
|
+
teal: { bg: 'var(--color-badge-teal-bg)', fg: 'var(--color-badge-teal-fg)' },
|
|
48
|
+
cyan: { bg: 'var(--color-badge-cyan-bg)', fg: 'var(--color-badge-cyan-fg)' },
|
|
49
|
+
pink: { bg: 'var(--color-badge-pink-bg)', fg: 'var(--color-badge-pink-fg)' },
|
|
50
|
+
purple_alt: { bg: 'var(--color-badge-purple-alt-bg)', fg: 'var(--color-badge-purple-alt-fg)' },
|
|
51
|
+
blue_alt: { bg: 'var(--color-badge-blue-alt-bg)', fg: 'var(--color-badge-blue-alt-fg)' },
|
|
52
|
+
bright_gray: { bg: 'var(--color-badge-bright-gray-bg)', fg: 'var(--color-badge-bright-gray-fg)' },
|
|
53
|
+
bright_green: { bg: 'var(--color-badge-bright-green-bg)', fg: 'var(--color-badge-bright-green-fg)' },
|
|
54
|
+
bright_red: { bg: 'var(--color-badge-bright-red-bg)', fg: 'var(--color-badge-bright-red-fg)' },
|
|
55
|
+
bright_blue: { bg: 'var(--color-badge-bright-blue-bg)', fg: 'var(--color-badge-bright-blue-fg)' },
|
|
56
|
+
bright_yellow: { bg: 'var(--color-badge-bright-yellow-bg)', fg: 'var(--color-badge-bright-yellow-fg)' },
|
|
57
|
+
bright_teal: { bg: 'var(--color-badge-bright-teal-bg)', fg: 'var(--color-badge-bright-teal-fg)' },
|
|
58
|
+
bright_cyan: { bg: 'var(--color-badge-bright-cyan-bg)', fg: 'var(--color-badge-bright-cyan-fg)' },
|
|
59
|
+
bright_orange: { bg: 'var(--color-badge-bright-orange-bg)', fg: 'var(--color-badge-bright-orange-fg)' },
|
|
60
|
+
bright_purple: { bg: 'var(--color-badge-bright-purple-bg)', fg: 'var(--color-badge-bright-purple-fg)' },
|
|
61
|
+
bright_pink: { bg: 'var(--color-badge-bright-pink-bg)', fg: 'var(--color-badge-bright-pink-fg)' },
|
|
55
62
|
};
|
|
56
63
|
|
|
57
64
|
export interface BadgeProps
|
|
@@ -106,8 +113,17 @@ export const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
|
|
|
106
113
|
if (_mr !== undefined) shimStyle.marginRight = typeof _mr === 'number' ? `${ _mr * S }px` : _mr;
|
|
107
114
|
const badgeStyle = Object.keys(shimStyle).length > 0 ? shimStyle : undefined;
|
|
108
115
|
|
|
116
|
+
const palette = COLOR_PALETTE[colorPalette];
|
|
117
|
+
|
|
109
118
|
const child = children ? (
|
|
110
|
-
|
|
119
|
+
// `ellipsis` is gui Text's own built-in variant for single-line
|
|
120
|
+
// truncation — it expands to the same overflow/text-overflow/
|
|
121
|
+
// white-space/max-width rule Tailwind's `text-ellipsis` did, but
|
|
122
|
+
// through the component's own variants (guaranteed to compile),
|
|
123
|
+
// rather than naming each longhand as a separate style prop.
|
|
124
|
+
<Text ellipsis { ...SIZE_TEXT[size] } color={ palette.fg as never }>
|
|
125
|
+
{ children }
|
|
126
|
+
</Text>
|
|
111
127
|
) : null;
|
|
112
128
|
|
|
113
129
|
const childrenElement = truncated ? (
|
|
@@ -117,21 +133,26 @@ export const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
|
|
|
117
133
|
) : child;
|
|
118
134
|
|
|
119
135
|
const badgeElement = (
|
|
120
|
-
<
|
|
121
|
-
ref={ ref as
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
136
|
+
<XStack
|
|
137
|
+
ref={ ref as never }
|
|
138
|
+
render={ <span/> }
|
|
139
|
+
display="inline-flex"
|
|
140
|
+
alignItems="center"
|
|
141
|
+
alignSelf="flex-start"
|
|
142
|
+
borderRadius={ 2 }
|
|
143
|
+
gap={ 4 }
|
|
144
|
+
maxWidth="100%"
|
|
145
|
+
userSelect="text"
|
|
146
|
+
{ ...SIZE[size] }
|
|
147
|
+
backgroundColor={ palette.bg as never }
|
|
148
|
+
className={ className }
|
|
128
149
|
style={ badgeStyle }
|
|
129
|
-
{ ...rest }
|
|
150
|
+
{ ...(rest as object) }
|
|
130
151
|
>
|
|
131
152
|
{ startElement }
|
|
132
153
|
{ childrenElement }
|
|
133
154
|
{ endElement }
|
|
134
|
-
</
|
|
155
|
+
</XStack>
|
|
135
156
|
);
|
|
136
157
|
|
|
137
158
|
if (loading) {
|
package/src/checkbox.tsx
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { Checkbox as GuiCheckbox } from '@hanzo/gui';
|
|
1
|
+
import { Checkbox as GuiCheckbox, Label, Text, View, VisuallyHidden } from '@hanzo/gui';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
|
-
import { cn } from './utils';
|
|
5
|
-
|
|
6
4
|
/** Tri-state, as the Chakra/Radix contract consumers already code against. */
|
|
7
5
|
type CheckedState = boolean | 'indeterminate';
|
|
8
6
|
|
|
@@ -40,7 +38,6 @@ const CheckboxGroupBase = React.forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
40
38
|
onValueChange,
|
|
41
39
|
orientation = 'vertical',
|
|
42
40
|
name: _name,
|
|
43
|
-
className,
|
|
44
41
|
...rest
|
|
45
42
|
} = props;
|
|
46
43
|
|
|
@@ -74,18 +71,16 @@ const CheckboxGroupBase = React.forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
74
71
|
|
|
75
72
|
return (
|
|
76
73
|
<CheckboxGroupContext.Provider value={ ctx }>
|
|
77
|
-
<
|
|
78
|
-
ref={ ref }
|
|
74
|
+
<View
|
|
75
|
+
ref={ ref as never }
|
|
79
76
|
role="group"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
) }
|
|
85
|
-
{ ...rest }
|
|
77
|
+
display="flex"
|
|
78
|
+
flexDirection={ orientation === 'vertical' ? 'column' : 'row' }
|
|
79
|
+
gap={ orientation === 'vertical' ? 12 : 32 }
|
|
80
|
+
{ ...(rest as object) }
|
|
86
81
|
>
|
|
87
82
|
{ children }
|
|
88
|
-
</
|
|
83
|
+
</View>
|
|
89
84
|
</CheckboxGroupContext.Provider>
|
|
90
85
|
);
|
|
91
86
|
},
|
|
@@ -110,43 +105,28 @@ export interface CheckboxProps extends Omit<React.ComponentPropsWithoutRef<'labe
|
|
|
110
105
|
required?: boolean;
|
|
111
106
|
}
|
|
112
107
|
|
|
113
|
-
const
|
|
114
|
-
sm: {
|
|
115
|
-
|
|
116
|
-
control: 'h-3.5 w-3.5 rounded-sm',
|
|
117
|
-
label: 'text-xs',
|
|
118
|
-
icon: 'h-3 w-3',
|
|
119
|
-
},
|
|
120
|
-
md: {
|
|
121
|
-
root: 'gap-2',
|
|
122
|
-
control: 'h-4 w-4 rounded',
|
|
123
|
-
label: 'text-sm',
|
|
124
|
-
icon: 'h-3.5 w-3.5',
|
|
125
|
-
},
|
|
108
|
+
const SIZE = {
|
|
109
|
+
sm: { rootGap: 6, control: 14, radius: 2, label: 12, icon: 12 },
|
|
110
|
+
md: { rootGap: 8, control: 16, radius: 4, label: 14, icon: 14 },
|
|
126
111
|
} as const;
|
|
127
112
|
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
>
|
|
113
|
+
const INK = 'var(--color-text-primary)' as never;
|
|
114
|
+
const ON_INK = 'var(--color-bg-body)' as never;
|
|
115
|
+
const RESTING_BORDER = 'var(--color-input-border)' as never;
|
|
116
|
+
const OUTLINE = 'var(--color-gray-500)' as never;
|
|
117
|
+
|
|
118
|
+
const CheckIcon = ({ size, color }: { readonly size: number; readonly color: string }) => (
|
|
119
|
+
<svg width={ size } height={ size } viewBox="0 0 12 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
135
120
|
<path
|
|
136
121
|
d="M10.59 0.59L4 7.17L1.41 4.59L0 6L4 10L12 2L10.59 0.59Z"
|
|
137
|
-
fill=
|
|
122
|
+
fill={ color }
|
|
138
123
|
/>
|
|
139
124
|
</svg>
|
|
140
125
|
);
|
|
141
126
|
|
|
142
|
-
const IndeterminateIcon = ({
|
|
143
|
-
<svg
|
|
144
|
-
|
|
145
|
-
viewBox="0 0 12 2"
|
|
146
|
-
fill="none"
|
|
147
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
148
|
-
>
|
|
149
|
-
<path d="M0 0H12V2H0V0Z" fill="currentColor"/>
|
|
127
|
+
const IndeterminateIcon = ({ size, color }: { readonly size: number; readonly color: string }) => (
|
|
128
|
+
<svg width={ size } height={ size } viewBox="0 0 12 2" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
129
|
+
<path d="M0 0H12V2H0V0Z" fill={ color }/>
|
|
150
130
|
</svg>
|
|
151
131
|
);
|
|
152
132
|
|
|
@@ -167,7 +147,6 @@ const CheckboxBase = React.forwardRef<HTMLInputElement, CheckboxProps>(
|
|
|
167
147
|
size = 'md',
|
|
168
148
|
name,
|
|
169
149
|
required,
|
|
170
|
-
className,
|
|
171
150
|
...rest
|
|
172
151
|
} = props;
|
|
173
152
|
|
|
@@ -237,25 +216,28 @@ const CheckboxBase = React.forwardRef<HTMLInputElement, CheckboxProps>(
|
|
|
237
216
|
[ readOnly, isControlled, isInGroup, group, value, onCheckedChange, onChange ],
|
|
238
217
|
);
|
|
239
218
|
|
|
240
|
-
const
|
|
219
|
+
const s = SIZE[size];
|
|
220
|
+
const isChecked = checkedState !== false;
|
|
241
221
|
|
|
242
222
|
return (
|
|
243
|
-
<
|
|
244
|
-
ref={ rootRef }
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
onChange={ onChange }
|
|
223
|
+
<Label
|
|
224
|
+
ref={ rootRef as never }
|
|
225
|
+
unstyled
|
|
226
|
+
flexDirection="row"
|
|
227
|
+
alignItems="center"
|
|
228
|
+
gap={ s.rootGap }
|
|
229
|
+
cursor={ disabled ? 'not-allowed' : readOnly ? 'default' : 'pointer' }
|
|
230
|
+
userSelect="none"
|
|
231
|
+
opacity={ disabled ? 0.5 : 1 }
|
|
232
|
+
onChange={ onChange as never }
|
|
253
233
|
data-disabled={ disabled || undefined }
|
|
254
234
|
data-readonly={ readOnly || undefined }
|
|
255
|
-
{ ...rest }
|
|
235
|
+
{ ...(rest as object) }
|
|
256
236
|
>
|
|
257
237
|
{ /* gui's Checkbox — role=checkbox + aria-checked from its headless core,
|
|
258
|
-
a real Pressable on native.
|
|
238
|
+
a real Pressable on native. Checked state is already tracked above,
|
|
239
|
+
so the fill is a consequence of that state rather than a
|
|
240
|
+
`data-[state=checked]` selector. */ }
|
|
259
241
|
<GuiCheckbox
|
|
260
242
|
unstyled
|
|
261
243
|
checked={ checkedState }
|
|
@@ -264,47 +246,46 @@ const CheckboxBase = React.forwardRef<HTMLInputElement, CheckboxProps>(
|
|
|
264
246
|
name={ name }
|
|
265
247
|
value={ value }
|
|
266
248
|
required={ required }
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
249
|
+
flexShrink={ 0 }
|
|
250
|
+
alignItems="center"
|
|
251
|
+
justifyContent="center"
|
|
252
|
+
width={ s.control }
|
|
253
|
+
height={ s.control }
|
|
254
|
+
borderRadius={ s.radius }
|
|
255
|
+
borderWidth={ 2 }
|
|
256
|
+
borderColor={ isChecked ? INK : RESTING_BORDER }
|
|
257
|
+
backgroundColor={ isChecked ? INK : 'transparent' }
|
|
258
|
+
transition="quicker"
|
|
259
|
+
focusStyle={{ outlineWidth: 2, outlineStyle: 'solid', outlineColor: OUTLINE, outlineOffset: 2 }}
|
|
278
260
|
>
|
|
279
261
|
{ icon ?? (
|
|
280
|
-
<GuiCheckbox.Indicator
|
|
281
|
-
{ checkedState === 'indeterminate' ?
|
|
282
|
-
<IndeterminateIcon
|
|
283
|
-
|
|
284
|
-
<CheckIcon className={ sizeClasses.icon }/>
|
|
285
|
-
) }
|
|
262
|
+
<GuiCheckbox.Indicator alignItems="center" justifyContent="center" width={ s.icon } height={ s.icon }>
|
|
263
|
+
{ checkedState === 'indeterminate' ?
|
|
264
|
+
<IndeterminateIcon size={ s.icon } color={ ON_INK }/> :
|
|
265
|
+
<CheckIcon size={ s.icon } color={ ON_INK }/> }
|
|
286
266
|
</GuiCheckbox.Indicator>
|
|
287
267
|
) }
|
|
288
268
|
</GuiCheckbox>
|
|
289
269
|
{ /* Hidden native input for form compatibility and ref forwarding */ }
|
|
290
|
-
<
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
270
|
+
<VisuallyHidden>
|
|
271
|
+
<input
|
|
272
|
+
ref={ setHiddenInputRef }
|
|
273
|
+
type="checkbox"
|
|
274
|
+
checked={ checkedState === true }
|
|
275
|
+
disabled={ disabled }
|
|
276
|
+
readOnly={ readOnly }
|
|
277
|
+
name={ name }
|
|
278
|
+
value={ value }
|
|
279
|
+
tabIndex={ -1 }
|
|
280
|
+
aria-hidden
|
|
281
|
+
onChange={ NOOP }
|
|
282
|
+
{ ...inputProps }
|
|
283
|
+
/>
|
|
284
|
+
</VisuallyHidden>
|
|
304
285
|
{ children != null && (
|
|
305
|
-
<
|
|
286
|
+
<Text fontSize={ s.label }>{ children }</Text>
|
|
306
287
|
) }
|
|
307
|
-
</
|
|
288
|
+
</Label>
|
|
308
289
|
);
|
|
309
290
|
},
|
|
310
291
|
);
|
package/src/close-button.tsx
CHANGED
|
@@ -36,12 +36,6 @@ const CloseButtonFrame = styled(View, {
|
|
|
36
36
|
} as const,
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
const CLOSE_BUTTON_CLASSES = [
|
|
40
|
-
'text-[var(--closeButton-fg,currentColor)]',
|
|
41
|
-
'hover:text-[var(--hover-color,currentColor)]',
|
|
42
|
-
'disabled:opacity-40',
|
|
43
|
-
].join(' ');
|
|
44
|
-
|
|
45
39
|
export interface CloseButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'size'> {
|
|
46
40
|
readonly variant?: 'plain';
|
|
47
41
|
readonly size?: 'md';
|
|
@@ -56,12 +50,13 @@ export const CloseButton = React.forwardRef<
|
|
|
56
50
|
return (
|
|
57
51
|
<CloseButtonFrame
|
|
58
52
|
ref={ref as any}
|
|
59
|
-
className={
|
|
53
|
+
className={className}
|
|
60
54
|
{...(rest as any)}
|
|
61
55
|
>
|
|
62
56
|
{children ?? (
|
|
63
57
|
<svg
|
|
64
|
-
|
|
58
|
+
width={20}
|
|
59
|
+
height={20}
|
|
65
60
|
viewBox="0 0 20 20"
|
|
66
61
|
fill="none"
|
|
67
62
|
aria-hidden="true"
|
package/src/empty-state.tsx
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
import { Text, XStack, YStack } from '@hanzo/gui';
|
|
1
2
|
import * as React from 'react';
|
|
2
3
|
|
|
3
|
-
import { cn } from './utils';
|
|
4
|
-
|
|
5
4
|
// Inline constants
|
|
6
|
-
const apos = '
|
|
5
|
+
const apos = '’'; // right single quotation mark (typographic apostrophe)
|
|
7
6
|
|
|
8
7
|
function upperFirst(str: string): string {
|
|
9
8
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
@@ -23,6 +22,8 @@ export interface EmptyStateProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
23
22
|
icon?: React.ReactNode;
|
|
24
23
|
}
|
|
25
24
|
|
|
25
|
+
const MUTED = 'var(--color-text-secondary)' as never;
|
|
26
|
+
|
|
26
27
|
export const EmptyState = React.forwardRef<HTMLDivElement, EmptyStateProps>(
|
|
27
28
|
function EmptyState(props, ref) {
|
|
28
29
|
const { title, description, term, type = 'query', icon, children, className, ...rest } = props;
|
|
@@ -70,28 +71,31 @@ export const EmptyState = React.forwardRef<HTMLDivElement, EmptyStateProps>(
|
|
|
70
71
|
})();
|
|
71
72
|
|
|
72
73
|
return (
|
|
73
|
-
<
|
|
74
|
-
ref={ ref }
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
<XStack
|
|
75
|
+
ref={ ref as never }
|
|
76
|
+
alignItems="center"
|
|
77
|
+
justifyContent="center"
|
|
78
|
+
paddingVertical={ 40 }
|
|
79
|
+
className={ className }
|
|
80
|
+
{ ...(rest as object) }
|
|
77
81
|
>
|
|
78
|
-
<
|
|
82
|
+
<YStack alignItems="center" gap={ 20 }>
|
|
79
83
|
{ iconContent && (
|
|
80
|
-
<
|
|
84
|
+
<XStack alignItems="center" justifyContent="center">{ iconContent }</XStack>
|
|
81
85
|
) }
|
|
82
86
|
{ descriptionContent ? (
|
|
83
|
-
<
|
|
84
|
-
<
|
|
85
|
-
<
|
|
87
|
+
<YStack alignItems="center" gap={ 8 }>
|
|
88
|
+
<Text fontSize={ 18 } fontWeight="600" textAlign="center" color={ MUTED }>{ titleContent }</Text>
|
|
89
|
+
<Text fontSize={ 14 } textAlign="center" color={ MUTED }>
|
|
86
90
|
{ descriptionContent }
|
|
87
|
-
</
|
|
88
|
-
</
|
|
91
|
+
</Text>
|
|
92
|
+
</YStack>
|
|
89
93
|
) : (
|
|
90
|
-
<
|
|
94
|
+
<Text fontSize={ 18 } fontWeight="600" textAlign="center" color={ MUTED }>{ titleContent }</Text>
|
|
91
95
|
) }
|
|
92
96
|
{ children }
|
|
93
|
-
</
|
|
94
|
-
</
|
|
97
|
+
</YStack>
|
|
98
|
+
</XStack>
|
|
95
99
|
);
|
|
96
100
|
},
|
|
97
101
|
);
|