astryx-dracula 0.3.0 → 0.4.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.
@@ -1,12 +1,12 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
  // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // L > LC > Ctr[h=80vh] > B.primary"Open settings"[opens=#settings] ;; Dlg#settings > L > (LP[w=280 divider p=3] > V[g=4] > Tx"Account settings"[t=label] + (UL > LI"Personal information"*8)) + (LC[p=6] > V[g=6] > DH"Account" + SE"Settings section" + (V[g=0] > TabList + (V[g=0] > Hd"Login"[level=3] + D + (H[j=between a=start] > (V[g=0] > Tx"Password"[weight=semibold] + Tx"Not created"[t=supporting]) + Lk"Create") + (H[g=3 a=start] > Ic + (V[g=0] > (H[g=2 a=center wrap] > Tx"OS X 10.15.7 Chrome"[weight=semibold] + SD) + Tx"March 30, 2026"[t=supporting])))))
3
+ // L > LC > Ctr[h=80vh] > B.primary"Open settings"[opens=#settings] ;; Dlg#settings > L > (LP[w=280 divider p=3] > V[g=4] > Tx"Account settings"[t=label] + (UL > LI"Personal information"*8)) + (LC[p=6] > V[g=6] > DH"Account" + SE"Settings section" + (V[g=0] > TabList + (V[g=0] > Hd"Login"[level=3] + (H[j=between a=start] > (V[g=0] > Tx"Password"[weight=semibold] + Tx"Not created"[t=supporting]) + B"Create") + (H[g=3 a=start] > Ic + (V[g=0] > (H[g=2 a=center wrap] > Tx"OS X 10.15.7 Chrome"[weight=semibold] + SD) + Tx"March 30, 2026"[t=supporting])))))
4
4
 
5
5
  /**
6
6
  * Settings Dialog — account sections inside one modal. Frame: the trigger page, then a Dialog that sizes to (Frame/responsive/container: see XLE header above.)
7
7
  */
8
8
 
9
- import React, {useState, type CSSProperties} from 'react';
9
+ import {useState, type CSSProperties} from 'react';
10
10
  import {
11
11
  VStack,
12
12
  HStack,
@@ -19,7 +19,6 @@ import {Text, Heading} from '@astryxdesign/core/Text';
19
19
  import {Button} from '@astryxdesign/core/Button';
20
20
  import {Dialog, DialogHeader} from '@astryxdesign/core/Dialog';
21
21
  import {List, ListItem} from '@astryxdesign/core/List';
22
- import {Divider} from '@astryxdesign/core/Divider';
23
22
  import {Selector} from '@astryxdesign/core/Selector';
24
23
  import {TextInput} from '@astryxdesign/core/TextInput';
25
24
  import {Card} from '@astryxdesign/core/Card';
@@ -31,7 +30,7 @@ import {Icon} from '@astryxdesign/core/Icon';
31
30
  import {Center} from '@astryxdesign/core/Center';
32
31
  import {useMediaQuery} from '@astryxdesign/core/hooks';
33
32
  import {Lock, ShieldCheck, Monitor} from 'lucide-react';
34
- import {NAV_ITEMS, LOGIN_ROWS, SOCIAL_ROWS, DEVICE_ROWS, INFO_TILES, iconBox, actionNoWrap, sideNavHeading} from 'astryx-dracula/shared/settings-data';
33
+ import {NAV_ITEMS, LOGIN_ROWS, SOCIAL_ROWS, DEVICE_ROWS, INFO_TILES, iconBox, sideNavHeading} from 'astryx-dracula/shared/settings-data';
35
34
  import {InfoRowItem, ExpandableRow} from 'astryx-dracula/shared/settings-rows';
36
35
  import type {DeviceRow, InfoTileData} from 'astryx-dracula/shared/settings-data';
37
36
 
@@ -89,7 +88,7 @@ function InfoTile({icon, title, body}: InfoTileData) {
89
88
  <Center width={48} height={48} style={iconBox}>
90
89
  <Icon icon={icon} />
91
90
  </Center>
92
- <VStack gap={0}>
91
+ <VStack gap={1}>
93
92
  <Text type="body" weight="semibold" display="block">
94
93
  {title}
95
94
  </Text>
@@ -104,43 +103,37 @@ function InfoTile({icon, title, body}: InfoTileData) {
104
103
 
105
104
  function DeviceRowItem({label, isCurrent, location, action}: DeviceRow) {
106
105
  return (
107
- <>
108
- <HStack gap={3} vAlign="start">
109
- <Icon icon={Monitor} />
110
- <StackItem size="fill">
111
- <VStack gap={0}>
112
- {/* wrap: the label plus the session status exceed the compact
113
- content width (~280px). */}
114
- <HStack gap={2} vAlign="center" wrap="wrap">
115
- <Text type="body" weight="semibold">
116
- {label}
117
- </Text>
118
- {isCurrent && (
119
- <HStack gap={1} vAlign="center">
120
- <StatusDot variant="success" label="Current session" />
121
- <Text type="supporting" color="secondary">
122
- Current session
123
- </Text>
124
- </HStack>
125
- )}
126
- </HStack>
127
- <Text
128
- type="supporting"
129
- color="secondary"
130
- display="block"
131
- hasTabularNumbers>
132
- {location}
106
+ <HStack gap={3} vAlign="start">
107
+ <Icon icon={Monitor} />
108
+ <StackItem size="fill">
109
+ <VStack gap={1}>
110
+ {/* wrap: the label plus the session status exceed the compact
111
+ content width (~280px). */}
112
+ <HStack gap={2} vAlign="center" wrap="wrap">
113
+ <Text type="body" weight="semibold">
114
+ {label}
133
115
  </Text>
134
- </VStack>
135
- </StackItem>
136
- {action && (
137
- <Link href={SELF_HASH} style={actionNoWrap}>
138
- {action}
139
- </Link>
140
- )}
141
- </HStack>
142
- <Divider />
143
- </>
116
+ {isCurrent && (
117
+ <HStack gap={1} vAlign="center">
118
+ <StatusDot variant="success" label="Current session" />
119
+ <Text type="supporting" color="secondary">
120
+ Current session
121
+ </Text>
122
+ </HStack>
123
+ )}
124
+ </HStack>
125
+ <Text
126
+ type="supporting"
127
+ color="secondary"
128
+ display="block"
129
+ hasTabularNumbers>
130
+ {location}
131
+ </Text>
132
+ </VStack>
133
+ </StackItem>
134
+ {/* Ending a session changes state in place — a Button, not a Link. */}
135
+ {action && <Button label={action} variant="secondary" size="sm" />}
136
+ </HStack>
144
137
  );
145
138
  }
146
139
 
@@ -274,7 +267,7 @@ export default function SettingsDialog() {
274
267
  onEdit={() => handleEdit('legalName')}
275
268
  onCancel={handleCancel}
276
269
  onSave={handleSave}
277
- href={SELF_HASH}>
270
+ hasDivider={false}>
278
271
  <TextInput
279
272
  label="Legal name"
280
273
  isLabelHidden
@@ -289,7 +282,7 @@ export default function SettingsDialog() {
289
282
  onEdit={() => handleEdit('preferredName')}
290
283
  onCancel={handleCancel}
291
284
  onSave={handleSave}
292
- href={SELF_HASH}>
285
+ hasDivider={false}>
293
286
  <TextInput
294
287
  label="Preferred first name"
295
288
  isLabelHidden
@@ -304,7 +297,7 @@ export default function SettingsDialog() {
304
297
  onEdit={() => handleEdit('email')}
305
298
  onCancel={handleCancel}
306
299
  onSave={handleSave}
307
- href={SELF_HASH}>
300
+ hasDivider={false}>
308
301
  <TextInput
309
302
  label="Email address"
310
303
  isLabelHidden
@@ -319,7 +312,7 @@ export default function SettingsDialog() {
319
312
  onEdit={() => handleEdit('phone')}
320
313
  onCancel={handleCancel}
321
314
  onSave={handleSave}
322
- href={SELF_HASH}>
315
+ hasDivider={false}>
323
316
  <TextInput
324
317
  label="Phone number"
325
318
  isLabelHidden
@@ -331,7 +324,7 @@ export default function SettingsDialog() {
331
324
  label="Identity verification"
332
325
  value="Verified"
333
326
  action=""
334
- href={SELF_HASH}
327
+ hasDivider={false}
335
328
  />
336
329
  <ExpandableRow
337
330
  label="Residential address"
@@ -340,7 +333,7 @@ export default function SettingsDialog() {
340
333
  onEdit={() => handleEdit('address')}
341
334
  onCancel={handleCancel}
342
335
  onSave={handleSave}
343
- href={SELF_HASH}>
336
+ hasDivider={false}>
344
337
  <TextInput
345
338
  label="Residential address"
346
339
  isLabelHidden
@@ -355,7 +348,7 @@ export default function SettingsDialog() {
355
348
  onEdit={() => handleEdit('mailingAddress')}
356
349
  onCancel={handleCancel}
357
350
  onSave={handleSave}
358
- href={SELF_HASH}>
351
+ hasDivider={false}>
359
352
  <TextInput
360
353
  label="Mailing address"
361
354
  isLabelHidden
@@ -370,7 +363,7 @@ export default function SettingsDialog() {
370
363
  onEdit={() => handleEdit('emergencyContact')}
371
364
  onCancel={handleCancel}
372
365
  onSave={handleSave}
373
- href={SELF_HASH}>
366
+ hasDivider={false}>
374
367
  <TextInput
375
368
  label="Emergency contact"
376
369
  isLabelHidden
@@ -380,13 +373,12 @@ export default function SettingsDialog() {
380
373
  </ExpandableRow>
381
374
  </VStack>
382
375
 
376
+ {/* One divider at most per panel: the tiles are one
377
+ group, so `gap` carries the rhythm. */}
383
378
  <Card padding={4}>
384
379
  <VStack gap={4}>
385
- {INFO_TILES.map((tile, i) => (
386
- <React.Fragment key={tile.title}>
387
- {i > 0 && <Divider />}
388
- <InfoTile {...tile} />
389
- </React.Fragment>
380
+ {INFO_TILES.map(tile => (
381
+ <InfoTile key={tile.title} {...tile} />
390
382
  ))}
391
383
  </VStack>
392
384
  </Card>
@@ -403,27 +395,27 @@ export default function SettingsDialog() {
403
395
  <Tab value="shared" label="Shared access" />
404
396
  </TabList>
405
397
 
398
+ {/* Four genuinely different groups, each with its own
399
+ heading: `gap` is the boundary, so no rule between
400
+ them and none after the last row. */}
406
401
  {activeTab === 'login' && (
407
402
  <VStack gap={8}>
408
- <VStack gap={0}>
403
+ <VStack gap={2}>
409
404
  <Heading level={3}>Login</Heading>
410
- <Divider />
411
405
  {LOGIN_ROWS.map(row => (
412
- <InfoRowItem key={row.label} {...row} href={SELF_HASH} />
406
+ <InfoRowItem key={row.label} {...row} hasDivider={false} />
413
407
  ))}
414
408
  </VStack>
415
409
 
416
- <VStack gap={0}>
410
+ <VStack gap={2}>
417
411
  <Heading level={3}>Social accounts</Heading>
418
- <Divider />
419
412
  {SOCIAL_ROWS.map(row => (
420
- <InfoRowItem key={row.label} {...row} href={SELF_HASH} />
413
+ <InfoRowItem key={row.label} {...row} hasDivider={false} />
421
414
  ))}
422
415
  </VStack>
423
416
 
424
- <VStack gap={0}>
417
+ <VStack gap={2}>
425
418
  <Heading level={3}>Device history</Heading>
426
- <Divider />
427
419
  {DEVICE_ROWS.map(device => (
428
420
  <DeviceRowItem
429
421
  key={device.location}
@@ -432,11 +424,10 @@ export default function SettingsDialog() {
432
424
  ))}
433
425
  </VStack>
434
426
 
435
- <VStack gap={0}>
427
+ <VStack gap={2}>
436
428
  <Heading level={3}>Account</Heading>
437
- <Divider />
438
429
  <HStack hAlign="between" vAlign="start">
439
- <VStack gap={0}>
430
+ <VStack gap={1}>
440
431
  <Text
441
432
  type="body"
442
433
  weight="semibold"
@@ -450,9 +441,13 @@ export default function SettingsDialog() {
450
441
  This action cannot be undone
451
442
  </Text>
452
443
  </VStack>
453
- <Link href={SELF_HASH}>Deactivate</Link>
444
+ {/* Deactivating changes state in place. */}
445
+ <Button
446
+ label="Deactivate"
447
+ variant="destructive"
448
+ size="sm"
449
+ />
454
450
  </HStack>
455
- <Divider />
456
451
  </VStack>
457
452
  </VStack>
458
453
  )}
@@ -461,7 +456,6 @@ export default function SettingsDialog() {
461
456
  <VStack gap={8}>
462
457
  <VStack gap={2}>
463
458
  <Heading level={3}>Shared access</Heading>
464
- <Divider />
465
459
  <Text type="body" color="secondary">
466
460
  Review each request carefully before approving
467
461
  access. We&apos;ll email your kin or coven-mate
@@ -506,7 +500,7 @@ export default function SettingsDialog() {
506
500
  onEdit={() => handleEdit('language')}
507
501
  onCancel={handleCancel}
508
502
  onSave={handleSave}
509
- href={SELF_HASH}>
503
+ hasDivider={false}>
510
504
  <Selector
511
505
  label="Language"
512
506
  isLabelHidden
@@ -526,7 +520,7 @@ export default function SettingsDialog() {
526
520
  onEdit={() => handleEdit('currency')}
527
521
  onCancel={handleCancel}
528
522
  onSave={handleSave}
529
- href={SELF_HASH}>
523
+ hasDivider={false}>
530
524
  <Selector
531
525
  label="Currency"
532
526
  isLabelHidden
@@ -546,7 +540,7 @@ export default function SettingsDialog() {
546
540
  onEdit={() => handleEdit('timezone')}
547
541
  onCancel={handleCancel}
548
542
  onSave={handleSave}
549
- href={SELF_HASH}>
543
+ hasDivider={false}>
550
544
  <Selector
551
545
  label="Time zone"
552
546
  isLabelHidden
@@ -572,7 +566,6 @@ export default function SettingsDialog() {
572
566
  labelPosition="start"
573
567
  labelSpacing="spread"
574
568
  />
575
- <Divider />
576
569
  <Switch
577
570
  label="Push notifications"
578
571
  description="Time-sensitive alerts, delivered by raven."
@@ -581,7 +574,6 @@ export default function SettingsDialog() {
581
574
  labelPosition="start"
582
575
  labelSpacing="spread"
583
576
  />
584
- <Divider />
585
577
  </VStack>
586
578
  </VStack>
587
579
  )}
@@ -590,18 +582,17 @@ export default function SettingsDialog() {
590
582
  <VStack gap={6}>
591
583
  <VStack gap={4}>
592
584
  <Heading level={3}>Payments</Heading>
593
- <Divider />
594
585
  <InfoRowItem
595
586
  label="Payout crypt"
596
587
  value="Visa ending in 4821"
597
588
  action=""
598
- href={SELF_HASH}
589
+ hasDivider={false}
599
590
  />
600
591
  <InfoRowItem
601
592
  label="Past payouts"
602
593
  value="No tributes yet"
603
594
  action=""
604
- href={SELF_HASH}
595
+ hasDivider={false}
605
596
  />
606
597
  </VStack>
607
598
  </VStack>
@@ -611,18 +602,17 @@ export default function SettingsDialog() {
611
602
  <VStack gap={6}>
612
603
  <VStack gap={4}>
613
604
  <Heading level={3}>Taxes</Heading>
614
- <Divider />
615
605
  <InfoRowItem
616
606
  label="Tax scrolls"
617
607
  value="Not submitted"
618
608
  action=""
619
- href={SELF_HASH}
609
+ hasDivider={false}
620
610
  />
621
611
  <InfoRowItem
622
612
  label="Past scrolls"
623
613
  value="Available after your first tribute"
624
614
  action=""
625
- href={SELF_HASH}
615
+ hasDivider={false}
626
616
  />
627
617
  </VStack>
628
618
  </VStack>
@@ -640,7 +630,6 @@ export default function SettingsDialog() {
640
630
  labelPosition="start"
641
631
  labelSpacing="spread"
642
632
  />
643
- <Divider />
644
633
  </VStack>
645
634
  </VStack>
646
635
  )}
@@ -663,7 +652,6 @@ export default function SettingsDialog() {
663
652
  </Text>
664
653
  <Link href={SELF_HASH}>View</Link>
665
654
  </HStack>
666
- <Divider />
667
655
  </VStack>
668
656
 
669
657
  <VStack gap={4}>
@@ -676,14 +664,15 @@ export default function SettingsDialog() {
676
664
  labelPosition="start"
677
665
  labelSpacing="spread"
678
666
  />
679
- <Divider />
680
667
  </VStack>
681
668
 
682
669
  <VStack gap={4}>
683
670
  <Heading level={3}>Reviews</Heading>
684
671
  <Text type="body" color="secondary">
685
672
  Choose what&apos;s shared when you write a review.{' '}
686
- <Link href={SELF_HASH}>Learn more</Link>
673
+ <Link href={SELF_HASH} hasUnderline>
674
+ Learn more
675
+ </Link>
687
676
  </Text>
688
677
  <VStack gap={4}>
689
678
  <Switch
@@ -719,7 +708,6 @@ export default function SettingsDialog() {
719
708
  labelSpacing="spread"
720
709
  />
721
710
  </VStack>
722
- <Divider />
723
711
  </VStack>
724
712
 
725
713
  <VStack gap={4}>
@@ -727,7 +715,12 @@ export default function SettingsDialog() {
727
715
  <Card>
728
716
  <HStack hAlign="between" vAlign="center">
729
717
  <Text type="body">Request my personal data</Text>
730
- <Link href={SELF_HASH}>Request</Link>
718
+ {/* A request is submitted in place, so it acts. */}
719
+ <Button
720
+ label="Request"
721
+ variant="secondary"
722
+ size="sm"
723
+ />
731
724
  </HStack>
732
725
  </Card>
733
726
  <Switch
@@ -741,7 +734,11 @@ export default function SettingsDialog() {
741
734
  <Card>
742
735
  <HStack hAlign="between" vAlign="center">
743
736
  <Text type="body">Delete my account</Text>
744
- <Link href={SELF_HASH}>Delete</Link>
737
+ <Button
738
+ label="Delete"
739
+ variant="destructive"
740
+ size="sm"
741
+ />
745
742
  </HStack>
746
743
  </Card>
747
744
  <Card variant="muted">
@@ -756,7 +753,7 @@ export default function SettingsDialog() {
756
753
  <Text type="body" color="secondary">
757
754
  We&apos;re committed to keeping your data
758
755
  protected. See details in our{' '}
759
- <Link href={SELF_HASH}>
756
+ <Link href={SELF_HASH} hasUnderline>
760
757
  Privacy Policy
761
758
  </Link>
762
759
  .