@luxfi/ui 7.4.11 → 7.4.12
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 -46
- package/dist/avatar.js +52 -48
- 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 +756 -703
- package/dist/index.js +757 -704
- 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/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.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 -67
- package/dist/radio.d.cts +18 -18
- package/dist/radio.d.ts +18 -18
- package/dist/radio.js +36 -68
- package/dist/slider.cjs +33 -49
- package/dist/slider.js +34 -50
- package/dist/switch.cjs +48 -55
- package/dist/switch.js +49 -56
- package/dist/tag.cjs +112 -69
- package/dist/tag.js +113 -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 -37
- 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 +136 -114
- package/src/pin-input.tsx +37 -29
- package/src/pnl-diagram.tsx +36 -28
- package/src/progress.tsx +17 -21
- package/src/radio.tsx +56 -75
- package/src/slider.tsx +43 -44
- package/src/strategy-builder.tsx +245 -156
- 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/strategy-builder.tsx
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { Text, View, 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
|
// ---------------------------------------------------------------------------
|
|
@@ -163,6 +162,21 @@ const STRATEGY_PRESETS: StrategyPreset[] = [
|
|
|
163
162
|
},
|
|
164
163
|
];
|
|
165
164
|
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
// Shared control shape — same tokens input.tsx's native text control uses.
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
|
|
169
|
+
const MONO = { fontFamily: 'monospace' } as never;
|
|
170
|
+
|
|
171
|
+
const CONTROL = {
|
|
172
|
+
backgroundColor: 'var(--color-input-bg)',
|
|
173
|
+
borderWidth: 1,
|
|
174
|
+
borderColor: 'var(--color-input-border)',
|
|
175
|
+
color: 'var(--color-input-fg)',
|
|
176
|
+
outlineWidth: 0,
|
|
177
|
+
focusStyle: { borderColor: 'var(--color-input-border-focus)' },
|
|
178
|
+
} as const;
|
|
179
|
+
|
|
166
180
|
// ---------------------------------------------------------------------------
|
|
167
181
|
// LegRow
|
|
168
182
|
// ---------------------------------------------------------------------------
|
|
@@ -183,100 +197,148 @@ function LegRow({ leg, index, strikes, expirations, removable, onChange, onRemov
|
|
|
183
197
|
};
|
|
184
198
|
|
|
185
199
|
return (
|
|
186
|
-
<
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
200
|
+
<XStack
|
|
201
|
+
alignItems="center"
|
|
202
|
+
gap={ 8 }
|
|
203
|
+
borderRadius={ 6 }
|
|
204
|
+
backgroundColor={ 'var(--color-tag-subtle-bg)' as never }
|
|
205
|
+
paddingHorizontal={ 8 }
|
|
206
|
+
paddingVertical={ 6 }
|
|
207
|
+
>
|
|
208
|
+
<Text fontSize={ 10 } style={ MONO } flexShrink={ 0 } width={ 16 } color={ 'var(--color-text-secondary)' as never }>
|
|
209
|
+
{ index + 1 }
|
|
210
|
+
</Text>
|
|
190
211
|
|
|
191
212
|
{/* Action */}
|
|
192
|
-
<
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
213
|
+
<XStack
|
|
214
|
+
unstyled
|
|
215
|
+
render={ <button type="button"/> }
|
|
216
|
+
onClick={ () => update({ action: leg.action === 'buy' ? 'sell' : 'buy' }) }
|
|
217
|
+
paddingHorizontal={ 8 }
|
|
218
|
+
paddingVertical={ 2 }
|
|
219
|
+
borderRadius={ 4 }
|
|
220
|
+
borderWidth={ 0 }
|
|
221
|
+
cursor="pointer"
|
|
222
|
+
transition="quick"
|
|
223
|
+
backgroundColor={ (leg.action === 'buy'
|
|
224
|
+
? 'color-mix(in srgb, var(--color-status-good) 20%, transparent)'
|
|
225
|
+
: 'color-mix(in srgb, var(--color-status-bad) 20%, transparent)') as never }
|
|
201
226
|
>
|
|
202
|
-
{leg.action === 'buy' ? '
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
227
|
+
<Text fontSize={ 12 } fontWeight="600" color={ (leg.action === 'buy' ? 'var(--color-status-good)' : 'var(--color-status-bad)') as never }>
|
|
228
|
+
{ leg.action === 'buy' ? 'BUY' : 'SELL' }
|
|
229
|
+
</Text>
|
|
230
|
+
</XStack>
|
|
231
|
+
|
|
232
|
+
{/* Type — call vs put is a rank difference, not a hue difference. */}
|
|
233
|
+
<XStack
|
|
234
|
+
unstyled
|
|
235
|
+
render={ <button type="button"/> }
|
|
236
|
+
onClick={ () => update({ type: leg.type === 'call' ? 'put' : 'call' }) }
|
|
237
|
+
paddingHorizontal={ 8 }
|
|
238
|
+
paddingVertical={ 2 }
|
|
239
|
+
borderRadius={ 4 }
|
|
240
|
+
borderWidth={ 0 }
|
|
241
|
+
cursor="pointer"
|
|
242
|
+
transition="quick"
|
|
243
|
+
backgroundColor={ (leg.type === 'call' ? 'var(--secondary)' : 'var(--muted)') as never }
|
|
216
244
|
>
|
|
217
|
-
{leg.type === 'call' ? '
|
|
218
|
-
|
|
245
|
+
<Text fontSize={ 12 } fontWeight="500" color={ (leg.type === 'call' ? 'var(--foreground)' : 'var(--muted-foreground)') as never }>
|
|
246
|
+
{ leg.type === 'call' ? 'CALL' : 'PUT' }
|
|
247
|
+
</Text>
|
|
248
|
+
</XStack>
|
|
219
249
|
|
|
220
250
|
{/* Strike */}
|
|
221
|
-
<
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
251
|
+
<View
|
|
252
|
+
unstyled
|
|
253
|
+
render={ <select/> }
|
|
254
|
+
value={ leg.strike }
|
|
255
|
+
onChange={ (e: React.ChangeEvent<HTMLSelectElement>) => update({ strike: Number(e.target.value) }) }
|
|
256
|
+
{ ...CONTROL }
|
|
257
|
+
style={ MONO }
|
|
258
|
+
fontSize={ 12 }
|
|
259
|
+
borderRadius={ 4 }
|
|
260
|
+
paddingHorizontal={ 6 }
|
|
261
|
+
paddingVertical={ 2 }
|
|
262
|
+
minWidth={ 72 }
|
|
225
263
|
>
|
|
226
|
-
{strikes.map((s) => (
|
|
227
|
-
<option key={s} value={s}>
|
|
228
|
-
{s.toFixed(2)}
|
|
264
|
+
{ strikes.map((s) => (
|
|
265
|
+
<option key={ s } value={ s }>
|
|
266
|
+
{ s.toFixed(2) }
|
|
229
267
|
</option>
|
|
230
|
-
))}
|
|
231
|
-
</
|
|
268
|
+
)) }
|
|
269
|
+
</View>
|
|
232
270
|
|
|
233
271
|
{/* Expiration */}
|
|
234
|
-
<
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
272
|
+
<View
|
|
273
|
+
unstyled
|
|
274
|
+
render={ <select/> }
|
|
275
|
+
value={ leg.expiration }
|
|
276
|
+
onChange={ (e: React.ChangeEvent<HTMLSelectElement>) => update({ expiration: e.target.value }) }
|
|
277
|
+
{ ...CONTROL }
|
|
278
|
+
fontSize={ 12 }
|
|
279
|
+
borderRadius={ 4 }
|
|
280
|
+
paddingHorizontal={ 6 }
|
|
281
|
+
paddingVertical={ 2 }
|
|
282
|
+
minWidth={ 80 }
|
|
238
283
|
>
|
|
239
|
-
{expirations.map((exp) => (
|
|
240
|
-
<option key={exp} value={exp}>
|
|
241
|
-
{exp}
|
|
284
|
+
{ expirations.map((exp) => (
|
|
285
|
+
<option key={ exp } value={ exp }>
|
|
286
|
+
{ exp }
|
|
242
287
|
</option>
|
|
243
|
-
))}
|
|
244
|
-
</
|
|
288
|
+
)) }
|
|
289
|
+
</View>
|
|
245
290
|
|
|
246
291
|
{/* Quantity */}
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
292
|
+
<View
|
|
293
|
+
unstyled
|
|
294
|
+
render={ <input type="number"/> }
|
|
295
|
+
min={ 1 }
|
|
296
|
+
max={ 999 }
|
|
297
|
+
value={ leg.quantity }
|
|
298
|
+
onChange={ (e: React.ChangeEvent<HTMLInputElement>) => update({ quantity: Math.max(1, parseInt(e.target.value, 10) || 1) }) }
|
|
299
|
+
{ ...CONTROL }
|
|
300
|
+
style={ MONO }
|
|
301
|
+
fontSize={ 12 }
|
|
302
|
+
borderRadius={ 4 }
|
|
303
|
+
paddingHorizontal={ 6 }
|
|
304
|
+
paddingVertical={ 2 }
|
|
305
|
+
width={ 48 }
|
|
306
|
+
textAlign="center"
|
|
254
307
|
/>
|
|
255
308
|
|
|
256
309
|
{/* Premium (read-only if provided) */}
|
|
257
|
-
{leg.premium !== undefined && (
|
|
258
|
-
<
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
310
|
+
{ leg.premium !== undefined && (
|
|
311
|
+
<Text
|
|
312
|
+
fontSize={ 12 }
|
|
313
|
+
style={ MONO }
|
|
314
|
+
fontVariant={ ['tabular-nums'] as never }
|
|
315
|
+
marginLeft="auto"
|
|
316
|
+
color={ (leg.premium >= 0 ? 'var(--color-status-good)' : 'var(--color-status-bad)') as never }
|
|
317
|
+
>
|
|
318
|
+
{ leg.premium >= 0 ? '+' : '' }{ leg.premium.toFixed(2) }
|
|
319
|
+
</Text>
|
|
320
|
+
) }
|
|
265
321
|
|
|
266
322
|
{/* Remove */}
|
|
267
|
-
{removable && (
|
|
268
|
-
<
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
323
|
+
{ removable && (
|
|
324
|
+
<XStack
|
|
325
|
+
unstyled
|
|
326
|
+
render={ <button type="button" aria-label="Remove leg"/> }
|
|
327
|
+
onClick={ () => onRemove(index) }
|
|
328
|
+
marginLeft="auto"
|
|
329
|
+
padding={ 2 }
|
|
330
|
+
borderWidth={ 0 }
|
|
331
|
+
cursor="pointer"
|
|
332
|
+
transition="quick"
|
|
333
|
+
color={ 'var(--color-text-secondary)' as never }
|
|
334
|
+
hoverStyle={ { color: 'var(--color-status-bad)' } as never }
|
|
273
335
|
>
|
|
274
|
-
<svg
|
|
275
|
-
<path d="M18 6L6 18M6 6l12 12" stroke="currentColor" strokeWidth="2" strokeLinecap="round"
|
|
336
|
+
<svg width={ 14 } height={ 14 } viewBox="0 0 24 24" fill="none">
|
|
337
|
+
<path d="M18 6L6 18M6 6l12 12" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
|
|
276
338
|
</svg>
|
|
277
|
-
</
|
|
278
|
-
)}
|
|
279
|
-
</
|
|
339
|
+
</XStack>
|
|
340
|
+
) }
|
|
341
|
+
</XStack>
|
|
280
342
|
);
|
|
281
343
|
}
|
|
282
344
|
|
|
@@ -360,108 +422,135 @@ export const StrategyBuilder = React.forwardRef<HTMLDivElement, StrategyBuilderP
|
|
|
360
422
|
}, [onSubmit, strategyType, legs, netPremium]);
|
|
361
423
|
|
|
362
424
|
const currentPreset = STRATEGY_PRESETS.find((p) => p.value === strategyType);
|
|
425
|
+
const netColor = netPremium > 0 ? 'var(--color-status-good)' : netPremium < 0 ? 'var(--color-status-bad)' : 'var(--color-text-secondary)';
|
|
363
426
|
|
|
364
427
|
return (
|
|
365
|
-
<
|
|
366
|
-
ref={ref}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
)}
|
|
371
|
-
{
|
|
428
|
+
<YStack
|
|
429
|
+
ref={ ref as never }
|
|
430
|
+
gap={ 12 }
|
|
431
|
+
borderRadius={ 8 }
|
|
432
|
+
borderWidth={ 1 }
|
|
433
|
+
borderColor={ 'var(--color-border-divider)' as never }
|
|
434
|
+
backgroundColor={ 'var(--card)' as never }
|
|
435
|
+
padding={ 12 }
|
|
436
|
+
className={ className }
|
|
437
|
+
{ ...(rest as object) }
|
|
372
438
|
>
|
|
373
439
|
{/* Header */}
|
|
374
|
-
<
|
|
375
|
-
<
|
|
376
|
-
<
|
|
440
|
+
<XStack alignItems="center" justifyContent="space-between" gap={ 12 }>
|
|
441
|
+
<XStack alignItems="center" gap={ 8 }>
|
|
442
|
+
<Text fontSize={ 12 } fontWeight="600" textTransform="uppercase" letterSpacing={ 0.6 } color={ 'var(--color-text-secondary)' as never }>
|
|
377
443
|
Strategy
|
|
378
|
-
</
|
|
379
|
-
<
|
|
380
|
-
{underlying}
|
|
381
|
-
</
|
|
382
|
-
</
|
|
383
|
-
<
|
|
384
|
-
Spot: <
|
|
385
|
-
</
|
|
386
|
-
</
|
|
444
|
+
</Text>
|
|
445
|
+
<Text fontSize={ 14 } fontWeight="500" color={ 'var(--color-text-primary)' as never }>
|
|
446
|
+
{ underlying }
|
|
447
|
+
</Text>
|
|
448
|
+
</XStack>
|
|
449
|
+
<Text fontSize={ 10 } color={ 'var(--color-text-secondary)' as never }>
|
|
450
|
+
Spot: <Text style={ MONO } fontVariant={ ['tabular-nums'] as never } color={ 'var(--color-text-primary)' as never }>{ spotPrice.toFixed(2) }</Text>
|
|
451
|
+
</Text>
|
|
452
|
+
</XStack>
|
|
387
453
|
|
|
388
454
|
{/* Strategy selector */}
|
|
389
|
-
<
|
|
390
|
-
<
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
455
|
+
<XStack alignItems="center" gap={ 8 }>
|
|
456
|
+
<View
|
|
457
|
+
unstyled
|
|
458
|
+
render={ <select/> }
|
|
459
|
+
value={ strategyType }
|
|
460
|
+
onChange={ handleStrategyChange }
|
|
461
|
+
flex={ 1 }
|
|
462
|
+
{ ...CONTROL }
|
|
463
|
+
fontSize={ 14 }
|
|
464
|
+
borderRadius={ 6 }
|
|
465
|
+
paddingHorizontal={ 10 }
|
|
466
|
+
paddingVertical={ 6 }
|
|
394
467
|
>
|
|
395
|
-
{STRATEGY_PRESETS.map((preset) => (
|
|
396
|
-
<option key={preset.value} value={preset.value}>
|
|
397
|
-
{preset.label}
|
|
468
|
+
{ STRATEGY_PRESETS.map((preset) => (
|
|
469
|
+
<option key={ preset.value } value={ preset.value }>
|
|
470
|
+
{ preset.label }
|
|
398
471
|
</option>
|
|
399
|
-
))}
|
|
400
|
-
</
|
|
401
|
-
</
|
|
472
|
+
)) }
|
|
473
|
+
</View>
|
|
474
|
+
</XStack>
|
|
402
475
|
|
|
403
|
-
{currentPreset && (
|
|
404
|
-
<
|
|
405
|
-
{currentPreset.description}
|
|
406
|
-
</
|
|
407
|
-
)}
|
|
476
|
+
{ currentPreset && (
|
|
477
|
+
<Text fontSize={ 11 } marginTop={ -4 } color={ 'var(--color-text-secondary)' as never }>
|
|
478
|
+
{ currentPreset.description }
|
|
479
|
+
</Text>
|
|
480
|
+
) }
|
|
408
481
|
|
|
409
482
|
{/* Legs */}
|
|
410
|
-
<
|
|
411
|
-
{legs.map((leg, i) => (
|
|
483
|
+
<YStack gap={ 6 }>
|
|
484
|
+
{ legs.map((leg, i) => (
|
|
412
485
|
<LegRow
|
|
413
|
-
key={i}
|
|
414
|
-
leg={leg}
|
|
415
|
-
index={i}
|
|
416
|
-
strikes={strikes}
|
|
417
|
-
expirations={expirations}
|
|
418
|
-
removable={strategyType === 'custom' && legs.length > 1}
|
|
419
|
-
onChange={handleLegChange}
|
|
420
|
-
onRemove={handleLegRemove}
|
|
486
|
+
key={ i }
|
|
487
|
+
leg={ leg }
|
|
488
|
+
index={ i }
|
|
489
|
+
strikes={ strikes }
|
|
490
|
+
expirations={ expirations }
|
|
491
|
+
removable={ strategyType === 'custom' && legs.length > 1 }
|
|
492
|
+
onChange={ handleLegChange }
|
|
493
|
+
onRemove={ handleLegRemove }
|
|
421
494
|
/>
|
|
422
|
-
))}
|
|
423
|
-
</
|
|
495
|
+
)) }
|
|
496
|
+
</YStack>
|
|
424
497
|
|
|
425
498
|
{/* Add leg button (custom only) */}
|
|
426
|
-
{strategyType === 'custom' && legs.length < 4 && (
|
|
427
|
-
<
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
499
|
+
{ strategyType === 'custom' && legs.length < 4 && (
|
|
500
|
+
<XStack
|
|
501
|
+
unstyled
|
|
502
|
+
render={ <button type="button"/> }
|
|
503
|
+
onClick={ handleAddLeg }
|
|
504
|
+
alignItems="center"
|
|
505
|
+
justifyContent="center"
|
|
506
|
+
gap={ 4 }
|
|
507
|
+
borderRadius={ 6 }
|
|
508
|
+
borderWidth={ 1 }
|
|
509
|
+
paddingVertical={ 4 }
|
|
510
|
+
cursor="pointer"
|
|
511
|
+
transition="quick"
|
|
512
|
+
style={ { borderStyle: 'dashed' } as never }
|
|
513
|
+
borderColor={ 'var(--color-border-divider)' as never }
|
|
514
|
+
color={ 'var(--color-text-secondary)' as never }
|
|
515
|
+
hoverStyle={ { borderColor: 'var(--color-hover)', color: 'var(--color-hover)' } as never }
|
|
431
516
|
>
|
|
432
|
-
<svg
|
|
433
|
-
<path d="M12 5v14M5 12h14" stroke="currentColor" strokeWidth="2" strokeLinecap="round"
|
|
517
|
+
<svg width={ 12 } height={ 12 } viewBox="0 0 24 24" fill="none">
|
|
518
|
+
<path d="M12 5v14M5 12h14" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
|
|
434
519
|
</svg>
|
|
435
|
-
Add Leg
|
|
436
|
-
</
|
|
437
|
-
)}
|
|
520
|
+
<Text unstyled fontSize={ 12 }>Add Leg</Text>
|
|
521
|
+
</XStack>
|
|
522
|
+
) }
|
|
438
523
|
|
|
439
524
|
{/* Net premium + submit */}
|
|
440
|
-
<
|
|
441
|
-
<
|
|
442
|
-
<
|
|
443
|
-
<
|
|
444
|
-
'
|
|
445
|
-
netPremium
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
)}
|
|
525
|
+
<XStack alignItems="center" justifyContent="space-between" paddingTop={ 4 } borderTopWidth={ 1 } borderColor={ 'var(--color-border-divider)' as never }>
|
|
526
|
+
<XStack alignItems="center" gap={ 8 }>
|
|
527
|
+
<Text fontSize={ 12 } color={ 'var(--color-text-secondary)' as never }>Net:</Text>
|
|
528
|
+
<Text fontSize={ 14 } fontWeight="600" style={ MONO } fontVariant={ ['tabular-nums'] as never } color={ netColor as never }>
|
|
529
|
+
{ netPremium > 0 ? 'Credit ' : netPremium < 0 ? 'Debit ' : '' }
|
|
530
|
+
{ netPremium !== 0 ? `$${ Math.abs(netPremium).toFixed(2) }` : '-' }
|
|
531
|
+
</Text>
|
|
532
|
+
</XStack>
|
|
533
|
+
<XStack
|
|
534
|
+
unstyled
|
|
535
|
+
render={ <button type="button"/> }
|
|
536
|
+
onClick={ handleSubmit }
|
|
537
|
+
disabled={ legs.length === 0 }
|
|
538
|
+
paddingHorizontal={ 16 }
|
|
539
|
+
paddingVertical={ 6 }
|
|
540
|
+
borderRadius={ 6 }
|
|
541
|
+
borderWidth={ 0 }
|
|
542
|
+
cursor="pointer"
|
|
543
|
+
transition="quick"
|
|
544
|
+
backgroundColor={ 'var(--color-button-solid-bg)' as never }
|
|
545
|
+
hoverStyle={ { backgroundColor: 'var(--color-hover)' } as never }
|
|
546
|
+
disabledStyle={ { opacity: 0.4, cursor: 'not-allowed' } as never }
|
|
460
547
|
>
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
548
|
+
<Text fontSize={ 12 } fontWeight="600" color={ 'var(--color-button-solid-text)' as never }>
|
|
549
|
+
Review Order
|
|
550
|
+
</Text>
|
|
551
|
+
</XStack>
|
|
552
|
+
</XStack>
|
|
553
|
+
</YStack>
|
|
465
554
|
);
|
|
466
555
|
},
|
|
467
556
|
);
|
package/src/switch.tsx
CHANGED
|
@@ -1,32 +1,23 @@
|
|
|
1
|
-
import { Switch as GuiSwitch } from '@hanzo/gui';
|
|
1
|
+
import { Label, Switch as GuiSwitch, Text, VisuallyHidden } from '@hanzo/gui';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
|
-
import { cn } from './utils';
|
|
5
|
-
|
|
6
4
|
const NOOP = () => { /* noop */ };
|
|
7
5
|
|
|
8
6
|
// ─── Size mappings ────────────────────────────────────────────────────
|
|
9
7
|
// Paint and box size only. Thumb travel is NOT a hardcoded translate any more:
|
|
10
8
|
// gui measures the track and the knob and moves the knob by the difference, so
|
|
11
9
|
// the switch stays correct at any size and on native.
|
|
12
|
-
const
|
|
13
|
-
sm: {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
label: 'text-xs',
|
|
17
|
-
},
|
|
18
|
-
md: {
|
|
19
|
-
root: 'h-5 w-9',
|
|
20
|
-
thumb: 'h-4 w-4',
|
|
21
|
-
label: 'text-sm',
|
|
22
|
-
},
|
|
23
|
-
lg: {
|
|
24
|
-
root: 'h-6 w-11',
|
|
25
|
-
thumb: 'h-5 w-5',
|
|
26
|
-
label: 'text-base',
|
|
27
|
-
},
|
|
10
|
+
const SIZE = {
|
|
11
|
+
sm: { width: 28, height: 16, thumb: 12, label: 12 },
|
|
12
|
+
md: { width: 36, height: 20, thumb: 16, label: 14 },
|
|
13
|
+
lg: { width: 44, height: 24, thumb: 20, label: 16 },
|
|
28
14
|
} as const;
|
|
29
15
|
|
|
16
|
+
const TRACK_ON = 'var(--color-text-primary)' as never;
|
|
17
|
+
const TRACK_OFF = 'var(--color-switch-bg)' as never;
|
|
18
|
+
const OUTLINE = 'var(--color-gray-500)' as never;
|
|
19
|
+
const WHITE = 'var(--color-white)' as never;
|
|
20
|
+
|
|
30
21
|
// ─── Props ────────────────────────────────────────────────────────────
|
|
31
22
|
export interface SwitchProps extends Omit<React.ComponentPropsWithoutRef<'label'>, 'onChange' | 'dir'> {
|
|
32
23
|
inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
|
|
@@ -59,7 +50,6 @@ const SwitchBase = React.forwardRef<HTMLInputElement, SwitchProps>(
|
|
|
59
50
|
disabled = false,
|
|
60
51
|
size = 'md',
|
|
61
52
|
direction,
|
|
62
|
-
className,
|
|
63
53
|
...rest
|
|
64
54
|
} = props;
|
|
65
55
|
|
|
@@ -80,78 +70,87 @@ const SwitchBase = React.forwardRef<HTMLInputElement, SwitchProps>(
|
|
|
80
70
|
[ isControlled, onCheckedChange ],
|
|
81
71
|
);
|
|
82
72
|
|
|
83
|
-
const
|
|
73
|
+
const s = SIZE[size];
|
|
84
74
|
const isRtl = direction === 'rtl';
|
|
85
75
|
|
|
86
76
|
return (
|
|
87
|
-
<
|
|
88
|
-
ref={ rootRef }
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
77
|
+
<Label
|
|
78
|
+
ref={ rootRef as never }
|
|
79
|
+
unstyled
|
|
80
|
+
flexDirection={ isRtl ? 'row-reverse' : 'row' }
|
|
81
|
+
alignItems="center"
|
|
82
|
+
gap={ 8 }
|
|
83
|
+
cursor={ disabled ? 'not-allowed' : 'pointer' }
|
|
84
|
+
userSelect="none"
|
|
85
|
+
opacity={ disabled ? 0.5 : 1 }
|
|
86
|
+
onChange={ onChange as never }
|
|
96
87
|
data-disabled={ disabled || undefined }
|
|
97
|
-
{ ...rest }
|
|
88
|
+
{ ...(rest as object) }
|
|
98
89
|
>
|
|
99
90
|
<GuiSwitch
|
|
100
91
|
unstyled
|
|
101
92
|
checked={ checkedState }
|
|
102
93
|
onCheckedChange={ handleCheckedChange }
|
|
103
94
|
disabled={ disabled }
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
95
|
+
position="relative"
|
|
96
|
+
flexShrink={ 0 }
|
|
97
|
+
alignItems="center"
|
|
98
|
+
width={ s.width }
|
|
99
|
+
height={ s.height }
|
|
100
|
+
borderRadius={ 9999 }
|
|
101
|
+
backgroundColor={ checkedState ? TRACK_ON : TRACK_OFF }
|
|
102
|
+
transition="quick"
|
|
103
|
+
focusStyle={{ outlineWidth: 2, outlineStyle: 'solid', outlineColor: OUTLINE, outlineOffset: 2 }}
|
|
112
104
|
>
|
|
113
105
|
{ trackLabel && (
|
|
114
|
-
<
|
|
106
|
+
<Text
|
|
107
|
+
position="absolute"
|
|
108
|
+
width="100%"
|
|
109
|
+
textAlign="center"
|
|
110
|
+
fontSize={ 8 }
|
|
111
|
+
fontWeight="700"
|
|
112
|
+
color={ WHITE }
|
|
113
|
+
pointerEvents="none"
|
|
114
|
+
>
|
|
115
115
|
{ checkedState ? trackLabel.on : trackLabel.off }
|
|
116
|
-
</
|
|
116
|
+
</Text>
|
|
117
117
|
) }
|
|
118
118
|
<GuiSwitch.Thumb
|
|
119
119
|
unstyled
|
|
120
120
|
data-thumb
|
|
121
121
|
transition="quicker"
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
width={ s.thumb }
|
|
123
|
+
height={ s.thumb }
|
|
124
|
+
borderRadius={ 9999 }
|
|
125
|
+
backgroundColor={ WHITE }
|
|
126
|
+
boxShadow="0 1px 2px 0 rgba(0,0,0,0.05)"
|
|
126
127
|
>
|
|
127
128
|
{ thumbLabel && (
|
|
128
|
-
<
|
|
129
|
+
<Text width="100%" height="100%" textAlign="center" fontSize={ 8 }>
|
|
129
130
|
{ checkedState ? thumbLabel.on : thumbLabel.off }
|
|
130
|
-
</
|
|
131
|
+
</Text>
|
|
131
132
|
) }
|
|
132
133
|
</GuiSwitch.Thumb>
|
|
133
134
|
</GuiSwitch>
|
|
134
135
|
{ /* Hidden native input for form compat and ref forwarding */ }
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
136
|
+
<VisuallyHidden>
|
|
137
|
+
<input
|
|
138
|
+
ref={ ref }
|
|
139
|
+
type="checkbox"
|
|
140
|
+
checked={ checkedState }
|
|
141
|
+
disabled={ disabled }
|
|
142
|
+
tabIndex={ -1 }
|
|
143
|
+
aria-hidden
|
|
144
|
+
onChange={ NOOP }
|
|
145
|
+
{ ...inputProps }
|
|
146
|
+
/>
|
|
147
|
+
</VisuallyHidden>
|
|
146
148
|
{ children != null && (
|
|
147
|
-
<
|
|
148
|
-
{ ...labelProps }
|
|
149
|
-
className={ cn(sizeClasses.label, labelProps?.className) }
|
|
150
|
-
>
|
|
149
|
+
<Text { ...(labelProps as object) } fontSize={ s.label }>
|
|
151
150
|
{ children }
|
|
152
|
-
</
|
|
151
|
+
</Text>
|
|
153
152
|
) }
|
|
154
|
-
</
|
|
153
|
+
</Label>
|
|
155
154
|
);
|
|
156
155
|
},
|
|
157
156
|
);
|