@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.
Files changed (84) hide show
  1. package/dist/alert.cjs +68 -46
  2. package/dist/alert.js +69 -46
  3. package/dist/avatar.cjs +49 -47
  4. package/dist/avatar.js +52 -49
  5. package/dist/badge.cjs +82 -49
  6. package/dist/badge.js +83 -50
  7. package/dist/checkbox.cjs +42 -75
  8. package/dist/checkbox.js +43 -76
  9. package/dist/chrome.cjs +3 -7
  10. package/dist/chrome.js +3 -7
  11. package/dist/close-button.cjs +3 -7
  12. package/dist/close-button.js +3 -7
  13. package/dist/dialog.cjs +3 -7
  14. package/dist/dialog.js +3 -7
  15. package/dist/drawer.cjs +3 -7
  16. package/dist/drawer.js +3 -7
  17. package/dist/empty-state.cjs +13 -13
  18. package/dist/empty-state.js +13 -13
  19. package/dist/expiration-selector.cjs +58 -63
  20. package/dist/expiration-selector.js +58 -63
  21. package/dist/greeks-display.cjs +48 -39
  22. package/dist/greeks-display.js +48 -39
  23. package/dist/index.cjs +1053 -888
  24. package/dist/index.js +1054 -889
  25. package/dist/input-group.cjs +36 -19
  26. package/dist/input-group.js +37 -19
  27. package/dist/option-chain.cjs +89 -67
  28. package/dist/option-chain.js +89 -67
  29. package/dist/option-position.cjs +88 -85
  30. package/dist/option-position.js +88 -85
  31. package/dist/pin-input.cjs +30 -29
  32. package/dist/pin-input.js +30 -29
  33. package/dist/pnl-diagram.cjs +20 -20
  34. package/dist/pnl-diagram.js +20 -20
  35. package/dist/popover.cjs +3 -7
  36. package/dist/popover.js +3 -7
  37. package/dist/progress-circle.cjs +40 -22
  38. package/dist/progress-circle.d.cts +5 -5
  39. package/dist/progress-circle.d.ts +5 -5
  40. package/dist/progress-circle.js +41 -22
  41. package/dist/progress.cjs +15 -22
  42. package/dist/progress.d.cts +6 -6
  43. package/dist/progress.d.ts +6 -6
  44. package/dist/progress.js +15 -22
  45. package/dist/radio.cjs +35 -68
  46. package/dist/radio.d.cts +18 -18
  47. package/dist/radio.d.ts +18 -18
  48. package/dist/radio.js +36 -69
  49. package/dist/rating.cjs +15 -17
  50. package/dist/rating.js +15 -17
  51. package/dist/slider.cjs +33 -50
  52. package/dist/slider.js +34 -51
  53. package/dist/strategy-builder.cjs +194 -115
  54. package/dist/strategy-builder.js +194 -115
  55. package/dist/switch.cjs +48 -55
  56. package/dist/switch.js +49 -56
  57. package/dist/tag.cjs +115 -69
  58. package/dist/tag.js +116 -69
  59. package/dist/tooltip.cjs +28 -17
  60. package/dist/tooltip.js +30 -18
  61. package/package.json +1 -1
  62. package/src/alert.tsx +78 -45
  63. package/src/avatar.tsx +54 -38
  64. package/src/badge.tsx +65 -44
  65. package/src/checkbox.tsx +70 -89
  66. package/src/close-button.tsx +3 -8
  67. package/src/empty-state.tsx +21 -17
  68. package/src/expiration-selector.tsx +84 -66
  69. package/src/greeks-display.tsx +59 -51
  70. package/src/input-group.tsx +38 -24
  71. package/src/option-chain.tsx +154 -104
  72. package/src/option-position.tsx +143 -114
  73. package/src/pin-input.tsx +37 -29
  74. package/src/pnl-diagram.tsx +36 -28
  75. package/src/progress-circle.tsx +52 -28
  76. package/src/progress.tsx +17 -21
  77. package/src/radio.tsx +56 -76
  78. package/src/rating.tsx +22 -20
  79. package/src/slider.tsx +43 -45
  80. package/src/strategy-builder.tsx +260 -162
  81. package/src/switch.tsx +63 -64
  82. package/src/tag.tsx +89 -51
  83. package/src/tooltip.tsx +21 -13
  84. package/tokens.css +18 -0
@@ -1,9 +1,8 @@
1
1
  'use client';
2
2
 
3
+ import { Text, XStack, YStack } from '@hanzo/gui';
3
4
  import * as React from 'react';
4
5
 
5
- import { cn } from './utils';
6
-
7
6
  // ---------------------------------------------------------------------------
8
7
  // Types
9
8
  // ---------------------------------------------------------------------------
@@ -48,31 +47,62 @@ interface ExpirationPillProps {
48
47
  readonly onClick: () => void;
49
48
  }
50
49
 
50
+ // Resting and selected are the same two states every selectable pill in this
51
+ // package uses (tabs, menu rows): a solid fill when selected, a bordered card
52
+ // otherwise. Stacks carry no `color` of their own (only Text does — same
53
+ // reason they carry no `fontSize`), so ink is computed per-state below and
54
+ // set directly on each Text rather than left to cascade from the button.
55
+ const SHAPE = {
56
+ selected: {
57
+ borderColor: 'transparent',
58
+ backgroundColor: 'var(--color-selected-control-bg)',
59
+ },
60
+ resting: {
61
+ borderColor: 'var(--color-border-divider)',
62
+ backgroundColor: 'var(--card)',
63
+ hoverStyle: { borderColor: 'var(--color-hover)' },
64
+ },
65
+ } as const;
66
+
51
67
  function ExpirationPill({ exp, isSelected, onClick }: ExpirationPillProps) {
68
+ const shape = isSelected ? SHAPE.selected : SHAPE.resting;
69
+ const dateColor = isSelected ? 'var(--color-selected-control-text)' : 'var(--color-text-secondary)';
70
+
71
+ // Near-expiry is a notice, and a notice in Lux is the loudest neutral — it
72
+ // only shows while resting; a selected pill's own fill already carries the
73
+ // emphasis.
74
+ const dteColor = exp.dte <= 7 && !isSelected
75
+ ? 'var(--color-status-warn)'
76
+ : isSelected ? 'var(--color-selected-control-text)' : 'var(--muted-foreground)';
77
+
52
78
  return (
53
- <button
54
- type="button"
55
- onClick={onClick}
56
- className={cn(
57
- 'flex flex-col items-center gap-0.5 rounded-md px-3 py-1.5 transition-colors shrink-0',
58
- 'border text-xs',
59
- isSelected
60
- ? 'border-blue-500/60 bg-blue-900/30 text-blue-300'
61
- : 'border-zinc-800 bg-zinc-900/50 text-zinc-400 hover:border-zinc-600 hover:text-zinc-200',
62
- )}
79
+ <YStack
80
+ unstyled
81
+ render={ <button type="button"/> }
82
+ onClick={ onClick }
83
+ alignItems="center"
84
+ gap={ 2 }
85
+ borderRadius={ 6 }
86
+ paddingHorizontal={ 12 }
87
+ paddingVertical={ 6 }
88
+ flexShrink={ 0 }
89
+ borderWidth={ 1 }
90
+ borderStyle="solid"
91
+ cursor="pointer"
92
+ { ...(shape as object) }
63
93
  >
64
- <span className="font-medium whitespace-nowrap">
65
- {formatExpDate(exp.date)}
66
- </span>
67
- <span className={cn(
68
- 'font-mono tabular-nums text-[10px]',
69
- isSelected ? 'text-[var(--foreground)]' : 'text-[var(--muted-foreground)]',
70
- // Near expiry is a notice, and a notice in Lux is the loudest neutral.
71
- exp.dte <= 7 && !isSelected && 'text-[var(--color-status-warn)]',
72
- )}>
73
- {exp.dte}d
74
- </span>
75
- </button>
94
+ <Text fontSize={ 12 } fontWeight="500" style={ { whiteSpace: 'nowrap' } as never } color={ dateColor as never }>
95
+ { formatExpDate(exp.date) }
96
+ </Text>
97
+ <Text
98
+ fontVariant={ ['tabular-nums'] as never }
99
+ style={ { fontFamily: 'monospace' } as never }
100
+ fontSize={ 10 }
101
+ color={ dteColor as never }
102
+ >
103
+ { exp.dte }d
104
+ </Text>
105
+ </YStack>
76
106
  );
77
107
  }
78
108
 
@@ -111,54 +141,42 @@ export const ExpirationSelector = React.forwardRef<HTMLDivElement, ExpirationSel
111
141
  })).filter((g) => g.items.length > 0);
112
142
 
113
143
  return (
114
- <div
115
- ref={ref}
116
- className={cn('flex flex-col gap-2', className)}
117
- {...rest}
118
- >
119
- {grouped.map((group) => (
120
- <div key={group.type} className="flex flex-col gap-1">
121
- <span className="text-[10px] font-semibold uppercase tracking-wider text-zinc-600 px-1">
122
- {group.label}
123
- </span>
124
- <div className="flex gap-1.5 overflow-x-auto no-scrollbar">
125
- {group.items.map((exp) => (
126
- <div key={exp.date} data-selected={exp.date === selected || undefined}>
127
- <ExpirationPill
128
- exp={exp}
129
- isSelected={exp.date === selected}
130
- onClick={() => onSelect(exp.date)}
131
- />
144
+ <YStack ref={ ref as never } gap={ 8 } className={ className } { ...(rest as object) }>
145
+ { grouped.map((group) => (
146
+ <YStack key={ group.type } gap={ 4 }>
147
+ <Text
148
+ fontSize={ 10 }
149
+ fontWeight="600"
150
+ textTransform="uppercase"
151
+ letterSpacing={ 0.5 }
152
+ paddingHorizontal={ 4 }
153
+ color={ 'var(--color-text-secondary)' as never }
154
+ >
155
+ { group.label }
156
+ </Text>
157
+ <XStack gap={ 6 } overflowX="auto" overflowY="hidden" className="lux-no-scrollbar">
158
+ { group.items.map((exp) => (
159
+ <div key={ exp.date } data-selected={ exp.date === selected || undefined }>
160
+ <ExpirationPill exp={ exp } isSelected={ exp.date === selected } onClick={ () => onSelect(exp.date) }/>
132
161
  </div>
133
- ))}
134
- </div>
135
- </div>
136
- ))}
137
- </div>
162
+ )) }
163
+ </XStack>
164
+ </YStack>
165
+ )) }
166
+ </YStack>
138
167
  );
139
168
  }
140
169
 
141
170
  return (
142
- <div
143
- ref={ref}
144
- className={cn('flex items-center gap-1', className)}
145
- {...rest}
146
- >
147
- <div
148
- ref={scrollRef}
149
- className="flex gap-1.5 overflow-x-auto no-scrollbar"
150
- >
151
- {expirations.map((exp) => (
152
- <div key={exp.date} data-selected={exp.date === selected || undefined}>
153
- <ExpirationPill
154
- exp={exp}
155
- isSelected={exp.date === selected}
156
- onClick={() => onSelect(exp.date)}
157
- />
171
+ <XStack ref={ ref as never } alignItems="center" gap={ 4 } className={ className } { ...(rest as object) }>
172
+ <XStack ref={ scrollRef as never } gap={ 6 } overflowX="auto" overflowY="hidden" className="lux-no-scrollbar">
173
+ { expirations.map((exp) => (
174
+ <div key={ exp.date } data-selected={ exp.date === selected || undefined }>
175
+ <ExpirationPill exp={ exp } isSelected={ exp.date === selected } onClick={ () => onSelect(exp.date) }/>
158
176
  </div>
159
- ))}
160
- </div>
161
- </div>
177
+ )) }
178
+ </XStack>
179
+ </XStack>
162
180
  );
163
181
  },
164
182
  );
@@ -1,9 +1,8 @@
1
1
  'use client';
2
2
 
3
+ import { Text, View, XStack } from '@hanzo/gui';
3
4
  import * as React from 'react';
4
5
 
5
- import { cn } from './utils';
6
-
7
6
  // ---------------------------------------------------------------------------
8
7
  // Types
9
8
  // ---------------------------------------------------------------------------
@@ -36,12 +35,12 @@ interface GreekDef {
36
35
  // on one achromatic ramp (tokens.css --chart-*, plus --foreground for the sixth),
37
36
  // so the panel obeys the brand atom and reskins with the host.
38
37
  const GREEKS: GreekDef[] = [
39
- { key: 'delta', label: 'Delta', symbol: '\u0394', color: 'text-[var(--foreground)]', barColor: 'bg-[var(--foreground)]', decimals: 4, maxMagnitude: 1 },
40
- { key: 'gamma', label: 'Gamma', symbol: '\u0393', color: 'text-[var(--chart-1)]', barColor: 'bg-[var(--chart-1)]', decimals: 4, maxMagnitude: 0.1 },
41
- { key: 'theta', label: 'Theta', symbol: '\u0398', color: 'text-[var(--chart-4)]', barColor: 'bg-[var(--chart-4)]', decimals: 4, maxMagnitude: 1 },
42
- { key: 'vega', label: 'Vega', symbol: '\u03BD', color: 'text-[var(--chart-2)]', barColor: 'bg-[var(--chart-2)]', decimals: 4, maxMagnitude: 1 },
43
- { key: 'rho', label: 'Rho', symbol: '\u03C1', color: 'text-[var(--chart-3)]', barColor: 'bg-[var(--chart-3)]', decimals: 4, maxMagnitude: 1 },
44
- { key: 'iv', label: 'IV', symbol: '\u03C3', color: 'text-[var(--chart-5)]', barColor: 'bg-[var(--chart-5)]', decimals: 1, maxMagnitude: 100 },
38
+ { key: 'delta', label: 'Delta', symbol: 'Δ', color: 'var(--foreground)', barColor: 'var(--foreground)', decimals: 4, maxMagnitude: 1 },
39
+ { key: 'gamma', label: 'Gamma', symbol: 'Γ', color: 'var(--chart-1)', barColor: 'var(--chart-1)', decimals: 4, maxMagnitude: 0.1 },
40
+ { key: 'theta', label: 'Theta', symbol: 'Θ', color: 'var(--chart-4)', barColor: 'var(--chart-4)', decimals: 4, maxMagnitude: 1 },
41
+ { key: 'vega', label: 'Vega', symbol: 'ν', color: 'var(--chart-2)', barColor: 'var(--chart-2)', decimals: 4, maxMagnitude: 1 },
42
+ { key: 'rho', label: 'Rho', symbol: 'ρ', color: 'var(--chart-3)', barColor: 'var(--chart-3)', decimals: 4, maxMagnitude: 1 },
43
+ { key: 'iv', label: 'IV', symbol: 'σ', color: 'var(--chart-5)', barColor: 'var(--chart-5)', decimals: 1, maxMagnitude: 100 },
45
44
  ];
46
45
 
47
46
  // ---------------------------------------------------------------------------
@@ -61,35 +60,45 @@ function GreekItem({ def, value, compact }: GreekItemProps) {
61
60
  : value.toFixed(def.decimals);
62
61
 
63
62
  return (
64
- <div
65
- className={cn(
66
- 'flex items-center gap-1.5',
67
- compact ? 'min-w-0' : 'min-w-[100px]',
68
- )}
69
- >
70
- <span className={cn('text-[10px] font-semibold shrink-0', def.color)}>
71
- {def.symbol}
72
- </span>
73
- {!compact && (
74
- <span className="text-[10px] text-zinc-500 shrink-0 w-[34px]">
75
- {def.label}
76
- </span>
77
- )}
78
- <span className={cn(
79
- 'font-mono tabular-nums text-xs text-zinc-200 shrink-0',
80
- value < 0 && 'text-red-400',
81
- )}>
82
- {displayValue}
83
- </span>
84
- {!compact && (
85
- <div className="flex-1 h-1 rounded-full bg-zinc-800 min-w-[24px] max-w-[48px] overflow-hidden">
86
- <div
87
- className={cn('h-full rounded-full transition-all', def.barColor)}
88
- style={{ width: `${magnitude * 100}%`, opacity: 0.7 }}
63
+ <XStack alignItems="center" gap={ 6 } minWidth={ compact ? 0 : 100 }>
64
+ <Text fontSize={ 10 } fontWeight="600" flexShrink={ 0 } color={ def.color as never }>
65
+ { def.symbol }
66
+ </Text>
67
+ { !compact && (
68
+ <Text fontSize={ 10 } flexShrink={ 0 } width={ 34 } color={ 'var(--color-text-secondary)' as never }>
69
+ { def.label }
70
+ </Text>
71
+ ) }
72
+ <Text
73
+ fontSize={ 12 }
74
+ flexShrink={ 0 }
75
+ style={ { fontFamily: 'monospace' } as never }
76
+ fontVariant={ ['tabular-nums'] as never }
77
+ color={ (value < 0 ? 'var(--color-status-bad)' : 'var(--color-text-primary)') as never }
78
+ >
79
+ { displayValue }
80
+ </Text>
81
+ { !compact && (
82
+ <View
83
+ flex={ 1 }
84
+ height={ 4 }
85
+ borderRadius={ 9999 }
86
+ minWidth={ 24 }
87
+ maxWidth={ 48 }
88
+ overflow="hidden"
89
+ backgroundColor={ 'var(--color-progress-track)' as never }
90
+ >
91
+ <View
92
+ height="100%"
93
+ borderRadius={ 9999 }
94
+ transition="quick"
95
+ width={ `${ magnitude * 100 }%` as never }
96
+ opacity={ 0.7 }
97
+ backgroundColor={ def.barColor as never }
89
98
  />
90
- </div>
91
- )}
92
- </div>
99
+ </View>
100
+ ) }
101
+ </XStack>
93
102
  );
94
103
  }
95
104
 
@@ -118,24 +127,23 @@ export const GreeksDisplay = React.forwardRef<HTMLDivElement, GreeksDisplayProps
118
127
  const activeGreeks = GREEKS.filter((g) => values[g.key] !== undefined);
119
128
 
120
129
  return (
121
- <div
122
- ref={ref}
123
- className={cn(
124
- 'flex flex-wrap items-center',
125
- compact ? 'gap-3' : 'gap-4',
126
- className,
127
- )}
128
- {...rest}
130
+ <XStack
131
+ ref={ ref as never }
132
+ flexWrap="wrap"
133
+ alignItems="center"
134
+ gap={ compact ? 12 : 16 }
135
+ className={ className }
136
+ { ...(rest as object) }
129
137
  >
130
- {activeGreeks.map((g) => (
138
+ { activeGreeks.map((g) => (
131
139
  <GreekItem
132
- key={g.key}
133
- def={g}
134
- value={values[g.key] as number}
135
- compact={compact}
140
+ key={ g.key }
141
+ def={ g }
142
+ value={ values[g.key] as number }
143
+ compact={ compact }
136
144
  />
137
- ))}
138
- </div>
145
+ )) }
146
+ </XStack>
139
147
  );
140
148
  },
141
149
  );
@@ -1,7 +1,8 @@
1
+ import { XStack } from '@hanzo/gui';
1
2
  import { debounce } from 'es-toolkit';
2
3
  import * as React from 'react';
3
4
 
4
- import { cn } from './utils';
5
+ import { ink } from './ink';
5
6
 
6
7
  import type { InputProps } from './input';
7
8
 
@@ -110,22 +111,30 @@ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
110
111
  const { className: endClassName, ...endRest } = endElementProps ?? {};
111
112
 
112
113
  return (
113
- <div
114
- ref={ combinedRef }
115
- className={ cn('relative flex w-full items-center', className) }
116
- { ...rest }
114
+ <XStack
115
+ ref={ combinedRef as never }
116
+ position="relative"
117
+ width="100%"
118
+ alignItems="center"
119
+ className={ className }
120
+ { ...(rest as object) }
117
121
  >
118
122
  { startElement && (
119
- <div
120
- ref={ startElementRef }
121
- className={ cn(
122
- 'pointer-events-none absolute inset-y-0 left-0 z-[1] flex items-center text-[var(--chakra-colors-input-element)]',
123
- startClassName,
124
- ) }
125
- { ...startRest }
123
+ <XStack
124
+ ref={ startElementRef as never }
125
+ position="absolute"
126
+ top={ 0 }
127
+ bottom={ 0 }
128
+ left={ 0 }
129
+ zIndex={ 1 }
130
+ alignItems="center"
131
+ pointerEvents="none"
132
+ style={{ color: 'var(--color-icon-secondary)' }}
133
+ className={ startClassName }
134
+ { ...(startRest as object) }
126
135
  >
127
- { startElement }
128
- </div>
136
+ { ink(startElement) }
137
+ </XStack>
129
138
  ) }
130
139
  { React.Children.map(children, (child: React.ReactElement<InputProps>) => {
131
140
  if (!React.isValidElement(child)) {
@@ -149,18 +158,23 @@ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
149
158
  });
150
159
  }) }
151
160
  { endElement && (
152
- <div
153
- ref={ endElementRef }
154
- className={ cn(
155
- 'pointer-events-none absolute inset-y-0 right-0 z-[1] flex items-center text-[var(--chakra-colors-input-element)]',
156
- endClassName,
157
- ) }
158
- { ...endRest }
161
+ <XStack
162
+ ref={ endElementRef as never }
163
+ position="absolute"
164
+ top={ 0 }
165
+ bottom={ 0 }
166
+ right={ 0 }
167
+ zIndex={ 1 }
168
+ alignItems="center"
169
+ pointerEvents="none"
170
+ style={{ color: 'var(--color-icon-secondary)' }}
171
+ className={ endClassName }
172
+ { ...(endRest as object) }
159
173
  >
160
- { endElement }
161
- </div>
174
+ { ink(endElement) }
175
+ </XStack>
162
176
  ) }
163
- </div>
177
+ </XStack>
164
178
  );
165
179
  },
166
180
  );