@drakkar.software/octospaces-ui 0.2.1 → 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.
package/dist/index.js CHANGED
@@ -398,11 +398,726 @@ function DiscoverScreen({
398
398
  }
399
399
  ));
400
400
  }
401
+
402
+ // src/sidebar/SpacesRail.tsx
403
+ import React5, { useState as useState2 } from "react";
404
+ import {
405
+ Pressable as RNPressable,
406
+ ScrollView,
407
+ Text as Text4,
408
+ View as View4
409
+ } from "react-native";
410
+
411
+ // src/sidebar/tile-state.ts
412
+ function railTileState(state, tokens, radiusActive, radiusDefault) {
413
+ const { active, hovered, over } = state;
414
+ let bg;
415
+ let borderColor;
416
+ let borderWidth;
417
+ let labelColor;
418
+ if (active) {
419
+ bg = tokens.primary;
420
+ borderColor = "transparent";
421
+ borderWidth = 0;
422
+ labelColor = tokens.textOnPrimary;
423
+ } else if (hovered) {
424
+ bg = tokens.primaryMuted;
425
+ borderColor = tokens.railTileHoverBorder;
426
+ borderWidth = 1;
427
+ labelColor = tokens.railTileHoverInk;
428
+ } else {
429
+ bg = tokens.railTile;
430
+ borderColor = tokens.borderSubtle;
431
+ borderWidth = 1;
432
+ labelColor = tokens.textSecondary;
433
+ }
434
+ if (over && !active) {
435
+ borderColor = tokens.primary;
436
+ borderWidth = 1;
437
+ }
438
+ const radius = active || hovered || over ? radiusActive : radiusDefault;
439
+ const shadow = active ? glowShadow(tokens.railGlow, 8, 0.3) : null;
440
+ return { bg, borderColor, borderWidth, radius, labelColor, shadow };
441
+ }
442
+
443
+ // src/sidebar/SpacesRail.tsx
444
+ var Pressable3 = RNPressable;
445
+ function resolveRailTokens(theme) {
446
+ const { colors, swatches } = theme;
447
+ return {
448
+ primary: colors.primary,
449
+ primaryMuted: colors.primaryMuted,
450
+ primarySubtle: colors.primarySubtle,
451
+ surfaceInput: colors.surfaceInput,
452
+ borderSubtle: colors.borderSubtle,
453
+ textOnPrimary: colors.textOnPrimary,
454
+ textSecondary: colors.textSecondary,
455
+ textTertiary: colors.textTertiary,
456
+ railTile: swatches["railTile"] ?? colors.surfaceInput,
457
+ railTileHoverBorder: swatches["railTileHoverBorder"] ?? colors.primarySubtle,
458
+ railGlow: swatches["railGlow"] ?? colors.primary,
459
+ railTileHoverInk: swatches["railTileHoverInk"] ?? colors.primary
460
+ };
461
+ }
462
+ var TILE_SIZE = 40;
463
+ var CORNER_SIZE = 16;
464
+ var BADGE_OFFSET = -5;
465
+ var CORNER_OFFSET = -3;
466
+ function TileContent({
467
+ space,
468
+ labelColor,
469
+ fontFamily,
470
+ fontSize,
471
+ lineHeight,
472
+ cornerBg,
473
+ cornerBorder,
474
+ cornerIconColor,
475
+ renderIcon,
476
+ renderTileImage,
477
+ renderBadge,
478
+ showLockCorner
479
+ }) {
480
+ return /* @__PURE__ */ React5.createElement(React5.Fragment, null, space.image && renderTileImage ? renderTileImage(space) : /* @__PURE__ */ React5.createElement(
481
+ Text4,
482
+ {
483
+ style: {
484
+ fontSize,
485
+ lineHeight,
486
+ fontWeight: "700",
487
+ fontFamily: fontFamily ?? void 0,
488
+ color: labelColor
489
+ },
490
+ numberOfLines: 1
491
+ },
492
+ space.short
493
+ ), showLockCorner && renderIcon ? /* @__PURE__ */ React5.createElement(
494
+ View4,
495
+ {
496
+ style: {
497
+ position: "absolute",
498
+ bottom: CORNER_OFFSET,
499
+ right: CORNER_OFFSET,
500
+ width: CORNER_SIZE,
501
+ height: CORNER_SIZE,
502
+ borderRadius: CORNER_SIZE / 2,
503
+ borderWidth: 1,
504
+ alignItems: "center",
505
+ justifyContent: "center",
506
+ backgroundColor: cornerBg,
507
+ borderColor: cornerBorder
508
+ }
509
+ },
510
+ renderIcon("lock", 9, cornerIconColor)
511
+ ) : null, space.muted && renderIcon ? /* @__PURE__ */ React5.createElement(
512
+ View4,
513
+ {
514
+ style: {
515
+ position: "absolute",
516
+ bottom: CORNER_OFFSET,
517
+ left: CORNER_OFFSET,
518
+ width: CORNER_SIZE,
519
+ height: CORNER_SIZE,
520
+ borderRadius: CORNER_SIZE / 2,
521
+ borderWidth: 1,
522
+ alignItems: "center",
523
+ justifyContent: "center",
524
+ backgroundColor: cornerBg,
525
+ borderColor: cornerBorder
526
+ }
527
+ },
528
+ renderIcon("mute", 9, cornerIconColor)
529
+ ) : null, space.unread ? /* @__PURE__ */ React5.createElement(
530
+ View4,
531
+ {
532
+ style: {
533
+ position: "absolute",
534
+ top: BADGE_OFFSET,
535
+ right: BADGE_OFFSET
536
+ }
537
+ },
538
+ renderBadge ? renderBadge(space.unread) : null
539
+ ) : null);
540
+ }
541
+ function PlainTile({
542
+ space,
543
+ active,
544
+ onPress,
545
+ tokens,
546
+ radiusActive,
547
+ radiusDefault,
548
+ renderIcon,
549
+ renderTileImage,
550
+ renderBadge,
551
+ showLockCorner,
552
+ cornerBg,
553
+ cornerBorder,
554
+ fontFamily,
555
+ fontSize,
556
+ lineHeight
557
+ }) {
558
+ const [hovered, setHovered] = useState2(false);
559
+ const s = railTileState({ active, hovered, over: false }, tokens, radiusActive, radiusDefault);
560
+ return /* @__PURE__ */ React5.createElement(
561
+ Pressable3,
562
+ {
563
+ onPress,
564
+ onMouseEnter: () => setHovered(true),
565
+ onMouseLeave: () => setHovered(false),
566
+ accessibilityRole: "button",
567
+ accessibilityLabel: space.short,
568
+ style: {
569
+ position: "relative",
570
+ width: TILE_SIZE,
571
+ height: TILE_SIZE,
572
+ alignItems: "center",
573
+ justifyContent: "center",
574
+ overflow: "hidden",
575
+ borderRadius: s.radius,
576
+ backgroundColor: s.bg,
577
+ borderWidth: s.borderWidth,
578
+ borderColor: s.borderColor,
579
+ ...s.shadow ?? {}
580
+ }
581
+ },
582
+ /* @__PURE__ */ React5.createElement(
583
+ TileContent,
584
+ {
585
+ space,
586
+ labelColor: s.labelColor,
587
+ fontFamily,
588
+ fontSize,
589
+ lineHeight,
590
+ cornerBg,
591
+ cornerBorder,
592
+ cornerIconColor: tokens.textTertiary,
593
+ renderIcon,
594
+ renderTileImage,
595
+ renderBadge,
596
+ showLockCorner
597
+ }
598
+ )
599
+ );
600
+ }
601
+ function DndTile({
602
+ space,
603
+ active,
604
+ onPress,
605
+ tokens,
606
+ radiusActive,
607
+ radiusDefault,
608
+ renderIcon,
609
+ renderTileImage,
610
+ renderBadge,
611
+ showLockCorner,
612
+ cornerBg,
613
+ cornerBorder,
614
+ fontFamily,
615
+ fontSize,
616
+ lineHeight,
617
+ dnd
618
+ }) {
619
+ const [hovered, setHovered] = useState2(false);
620
+ const { ref, over = false } = dnd(space.id);
621
+ const s = railTileState({ active, hovered, over }, tokens, radiusActive, radiusDefault);
622
+ return /* @__PURE__ */ React5.createElement(
623
+ Pressable3,
624
+ {
625
+ ref,
626
+ onPress,
627
+ onMouseEnter: () => setHovered(true),
628
+ onMouseLeave: () => setHovered(false),
629
+ accessibilityRole: "button",
630
+ accessibilityLabel: space.short,
631
+ style: {
632
+ position: "relative",
633
+ width: TILE_SIZE,
634
+ height: TILE_SIZE,
635
+ alignItems: "center",
636
+ justifyContent: "center",
637
+ overflow: "hidden",
638
+ borderRadius: s.radius,
639
+ backgroundColor: s.bg,
640
+ borderWidth: s.borderWidth,
641
+ borderColor: s.borderColor,
642
+ ...s.shadow ?? {}
643
+ }
644
+ },
645
+ /* @__PURE__ */ React5.createElement(
646
+ TileContent,
647
+ {
648
+ space,
649
+ labelColor: s.labelColor,
650
+ fontFamily,
651
+ fontSize,
652
+ lineHeight,
653
+ cornerBg,
654
+ cornerBorder,
655
+ cornerIconColor: tokens.textTertiary,
656
+ renderIcon,
657
+ renderTileImage,
658
+ renderBadge,
659
+ showLockCorner
660
+ }
661
+ )
662
+ );
663
+ }
664
+ function SpacesRail({
665
+ spaces,
666
+ activeId,
667
+ onSelect,
668
+ onAdd,
669
+ onSelectDms,
670
+ dmsActive = false,
671
+ dmUnread,
672
+ dmLabel = "Direct messages",
673
+ addLabel = "Create or join a space",
674
+ renderIcon,
675
+ renderTileImage,
676
+ renderBadge,
677
+ showLockCorner = false,
678
+ renderFoot,
679
+ useTileDnd
680
+ }) {
681
+ const theme = useOctoSpacesTheme();
682
+ const { colors, spacing, radii, type: typeScale, fonts, layout } = theme;
683
+ const tokens = resolveRailTokens(theme);
684
+ const railWidth = layout["railWidth"] ?? 64;
685
+ const spaceV = spacing["2"] ?? 8;
686
+ const spaceXs = spacing["1"] ?? 4;
687
+ const spaceS = spacing["2"] ?? 8;
688
+ const spaceMd = spacing["3"] ?? 12;
689
+ const radiusActive = radii["lg"] ?? 12;
690
+ const radiusDefault = radii["xl"] ?? 16;
691
+ const footnoteSize = typeScale["footnote"]?.size ?? 12;
692
+ const footnoteLineH = typeScale["footnote"]?.lineHeight ?? 18;
693
+ const monoFont = fonts["mono"] ?? void 0;
694
+ const cornerBg = colors.sidebar;
695
+ const cornerBorder = colors.border;
696
+ const tileShared = {
697
+ tokens,
698
+ radiusActive,
699
+ radiusDefault,
700
+ renderIcon,
701
+ renderTileImage,
702
+ renderBadge,
703
+ showLockCorner,
704
+ cornerBg,
705
+ cornerBorder,
706
+ fontFamily: monoFont,
707
+ fontSize: footnoteSize,
708
+ lineHeight: footnoteLineH
709
+ };
710
+ const [dmHovered, setDmHovered] = useState2(false);
711
+ const dmTileStyle = railTileState(
712
+ { active: dmsActive, hovered: dmHovered, over: false },
713
+ tokens,
714
+ radiusActive,
715
+ radiusDefault
716
+ );
717
+ const dmIconColor = dmsActive ? colors.textOnPrimary : dmHovered ? tokens.railTileHoverInk : colors.textSecondary;
718
+ const [addHovered, setAddHovered] = useState2(false);
719
+ const hasDnd = !!useTileDnd;
720
+ return /* @__PURE__ */ React5.createElement(
721
+ View4,
722
+ {
723
+ style: {
724
+ width: railWidth,
725
+ paddingVertical: spaceMd,
726
+ borderRightWidth: 1,
727
+ borderRightColor: colors.border,
728
+ backgroundColor: colors.sidebar,
729
+ alignItems: "center",
730
+ gap: spaceS
731
+ }
732
+ },
733
+ /* @__PURE__ */ React5.createElement(
734
+ ScrollView,
735
+ {
736
+ style: { alignSelf: "stretch", flex: 1 },
737
+ contentContainerStyle: {
738
+ alignItems: "center",
739
+ gap: spaceV,
740
+ paddingVertical: spaceXs
741
+ },
742
+ showsVerticalScrollIndicator: false
743
+ },
744
+ onSelectDms ? /* @__PURE__ */ React5.createElement(View4, { style: { position: "relative" } }, /* @__PURE__ */ React5.createElement(
745
+ Pressable3,
746
+ {
747
+ onPress: onSelectDms,
748
+ onMouseEnter: () => setDmHovered(true),
749
+ onMouseLeave: () => setDmHovered(false),
750
+ accessibilityRole: "button",
751
+ accessibilityLabel: dmLabel,
752
+ style: {
753
+ width: TILE_SIZE,
754
+ height: TILE_SIZE,
755
+ alignItems: "center",
756
+ justifyContent: "center",
757
+ borderRadius: dmTileStyle.radius,
758
+ backgroundColor: dmTileStyle.bg,
759
+ borderWidth: dmTileStyle.borderWidth,
760
+ borderColor: dmTileStyle.borderColor,
761
+ ...dmTileStyle.shadow ?? {}
762
+ }
763
+ },
764
+ renderIcon ? renderIcon("dm", 20, dmIconColor) : null
765
+ ), showLockCorner && renderIcon ? /* @__PURE__ */ React5.createElement(
766
+ View4,
767
+ {
768
+ style: {
769
+ position: "absolute",
770
+ bottom: CORNER_OFFSET,
771
+ right: CORNER_OFFSET,
772
+ width: CORNER_SIZE,
773
+ height: CORNER_SIZE,
774
+ borderRadius: CORNER_SIZE / 2,
775
+ borderWidth: 1,
776
+ alignItems: "center",
777
+ justifyContent: "center",
778
+ backgroundColor: cornerBg,
779
+ borderColor: cornerBorder
780
+ }
781
+ },
782
+ renderIcon("lock", 9, tokens.textTertiary)
783
+ ) : null, dmUnread ? /* @__PURE__ */ React5.createElement(View4, { style: { position: "absolute", top: BADGE_OFFSET, right: BADGE_OFFSET } }, renderBadge ? renderBadge(dmUnread) : null) : null) : null,
784
+ spaces.map(
785
+ (s) => hasDnd ? /* @__PURE__ */ React5.createElement(
786
+ DndTile,
787
+ {
788
+ key: s.id,
789
+ space: s,
790
+ active: s.id === activeId,
791
+ onPress: () => onSelect?.(s.id),
792
+ dnd: useTileDnd,
793
+ ...tileShared
794
+ }
795
+ ) : /* @__PURE__ */ React5.createElement(
796
+ PlainTile,
797
+ {
798
+ key: s.id,
799
+ space: s,
800
+ active: s.id === activeId,
801
+ onPress: () => onSelect?.(s.id),
802
+ ...tileShared
803
+ }
804
+ )
805
+ ),
806
+ /* @__PURE__ */ React5.createElement(
807
+ Pressable3,
808
+ {
809
+ onPress: onAdd,
810
+ onMouseEnter: () => setAddHovered(true),
811
+ onMouseLeave: () => setAddHovered(false),
812
+ accessibilityRole: "button",
813
+ accessibilityLabel: addLabel,
814
+ style: {
815
+ width: TILE_SIZE,
816
+ height: TILE_SIZE,
817
+ alignItems: "center",
818
+ justifyContent: "center",
819
+ borderRadius: radiusDefault,
820
+ borderWidth: 1,
821
+ borderStyle: "dashed",
822
+ borderColor: addHovered ? colors.border : colors.borderSubtle
823
+ }
824
+ },
825
+ renderIcon ? renderIcon("add", 16, addHovered ? tokens.railTileHoverInk : colors.textTertiary) : null
826
+ )
827
+ ),
828
+ renderFoot ? renderFoot() : null
829
+ );
830
+ }
831
+
832
+ // src/sidebar/Sidebar.tsx
833
+ import React6 from "react";
834
+ import { ScrollView as ScrollView2, View as View5 } from "react-native";
835
+ function Sidebar({
836
+ header,
837
+ footer,
838
+ children,
839
+ width,
840
+ scrollable = true,
841
+ contentContainerStyle,
842
+ background
843
+ }) {
844
+ const theme = useOctoSpacesTheme();
845
+ const { colors, layout } = theme;
846
+ const panelWidth = width ?? layout["sidebarWidth"] ?? 248;
847
+ const bg = background ?? colors.sidebarPanel;
848
+ return /* @__PURE__ */ React6.createElement(
849
+ View5,
850
+ {
851
+ style: {
852
+ width: panelWidth,
853
+ backgroundColor: bg,
854
+ borderRightWidth: 1,
855
+ borderRightColor: colors.border,
856
+ flexDirection: "column"
857
+ }
858
+ },
859
+ header ?? null,
860
+ scrollable ? /* @__PURE__ */ React6.createElement(
861
+ ScrollView2,
862
+ {
863
+ style: { flex: 1 },
864
+ contentContainerStyle: contentContainerStyle ?? void 0,
865
+ showsVerticalScrollIndicator: false
866
+ },
867
+ children
868
+ ) : /* @__PURE__ */ React6.createElement(View5, { style: [{ flex: 1 }, contentContainerStyle] }, children),
869
+ footer ?? null
870
+ );
871
+ }
872
+
873
+ // src/sidebar/SidebarHeader.tsx
874
+ import React7 from "react";
875
+ import { StyleSheet, View as View6 } from "react-native";
876
+ function SidebarHeader({
877
+ leading,
878
+ actions,
879
+ extra,
880
+ divider = false,
881
+ style
882
+ }) {
883
+ const theme = useOctoSpacesTheme();
884
+ const { colors } = theme;
885
+ return /* @__PURE__ */ React7.createElement(
886
+ View6,
887
+ {
888
+ style: [
889
+ styles.root,
890
+ style,
891
+ divider ? {
892
+ borderBottomWidth: StyleSheet.hairlineWidth,
893
+ borderBottomColor: colors.borderSubtle
894
+ } : void 0
895
+ ]
896
+ },
897
+ /* @__PURE__ */ React7.createElement(View6, { style: styles.row }, /* @__PURE__ */ React7.createElement(View6, { style: styles.leading }, leading), actions != null ? /* @__PURE__ */ React7.createElement(View6, { style: styles.actions }, actions) : null),
898
+ extra != null ? /* @__PURE__ */ React7.createElement(View6, null, extra) : null
899
+ );
900
+ }
901
+ var styles = StyleSheet.create({
902
+ root: {
903
+ flexDirection: "column"
904
+ },
905
+ row: {
906
+ flexDirection: "row",
907
+ alignItems: "center"
908
+ },
909
+ leading: {
910
+ flex: 1,
911
+ minWidth: 0
912
+ },
913
+ actions: {
914
+ flexDirection: "row",
915
+ alignItems: "center",
916
+ flexShrink: 0
917
+ }
918
+ });
919
+
920
+ // src/sidebar/SidebarActionButton.tsx
921
+ import React8, { useState as useState3 } from "react";
922
+ import { Pressable as RNPressable2 } from "react-native";
923
+ var Pressable4 = RNPressable2;
924
+ function SidebarActionButton({
925
+ icon,
926
+ onPress,
927
+ accessibilityLabel,
928
+ size = 32
929
+ }) {
930
+ const theme = useOctoSpacesTheme();
931
+ const { colors, radii } = theme;
932
+ const [hovered, setHovered] = useState3(false);
933
+ const [pressed, setPressed] = useState3(false);
934
+ const bg = pressed ? colors.primaryMuted ?? "rgba(0,0,0,0.10)" : hovered ? colors.primarySubtle ?? "rgba(0,0,0,0.05)" : "transparent";
935
+ const radius = radii["sm"] ?? 4;
936
+ return /* @__PURE__ */ React8.createElement(
937
+ Pressable4,
938
+ {
939
+ onPress,
940
+ onPressIn: () => setPressed(true),
941
+ onPressOut: () => setPressed(false),
942
+ onMouseEnter: () => setHovered(true),
943
+ onMouseLeave: () => setHovered(false),
944
+ accessibilityRole: "button",
945
+ accessibilityLabel,
946
+ style: {
947
+ width: size,
948
+ height: size,
949
+ alignItems: "center",
950
+ justifyContent: "center",
951
+ borderRadius: radius,
952
+ backgroundColor: bg
953
+ }
954
+ },
955
+ icon
956
+ );
957
+ }
958
+
959
+ // src/sidebar/SidebarItem.tsx
960
+ import React9, { useState as useState4 } from "react";
961
+ import { Pressable as RNPressable3, StyleSheet as StyleSheet2, Text as Text5, View as View7 } from "react-native";
962
+ var Pressable5 = RNPressable3;
963
+ function SidebarItem({
964
+ label,
965
+ icon,
966
+ active = false,
967
+ badge,
968
+ onPress,
969
+ onLongPress,
970
+ trailing,
971
+ indent = 0
972
+ }) {
973
+ const theme = useOctoSpacesTheme();
974
+ const { colors, type: typeScale, fonts, spacing, radii } = theme;
975
+ const [hovered, setHovered] = useState4(false);
976
+ const sp1 = spacing["1"] ?? 4;
977
+ const sp2 = spacing["2"] ?? 8;
978
+ const sp3 = spacing["3"] ?? 12;
979
+ const radSm = radii["sm"] ?? 4;
980
+ const indentPx = indent * 16;
981
+ const bg = active ? colors.sidebarActive : hovered ? colors.primarySubtle ?? "rgba(0,0,0,0.04)" : "transparent";
982
+ const textColor = active ? colors.primary : colors.text;
983
+ return /* @__PURE__ */ React9.createElement(
984
+ Pressable5,
985
+ {
986
+ onPress,
987
+ onLongPress,
988
+ onMouseEnter: () => setHovered(true),
989
+ onMouseLeave: () => setHovered(false),
990
+ accessibilityRole: "button",
991
+ accessibilityLabel: label,
992
+ style: {
993
+ flexDirection: "row",
994
+ alignItems: "center",
995
+ gap: sp2,
996
+ paddingVertical: sp1 + 2,
997
+ paddingLeft: sp3 + indentPx,
998
+ paddingRight: sp3,
999
+ borderRadius: radSm,
1000
+ backgroundColor: bg
1001
+ }
1002
+ },
1003
+ icon != null ? /* @__PURE__ */ React9.createElement(View7, { style: styles2.iconSlot }, icon) : null,
1004
+ /* @__PURE__ */ React9.createElement(
1005
+ Text5,
1006
+ {
1007
+ style: {
1008
+ flex: 1,
1009
+ fontSize: typeScale["callout"]?.size ?? 13,
1010
+ lineHeight: typeScale["callout"]?.lineHeight ?? 18,
1011
+ fontWeight: active ? "600" : "400",
1012
+ color: textColor,
1013
+ fontFamily: fonts["body"] ?? void 0
1014
+ },
1015
+ numberOfLines: 1
1016
+ },
1017
+ label
1018
+ ),
1019
+ badge != null ? /* @__PURE__ */ React9.createElement(
1020
+ View7,
1021
+ {
1022
+ style: {
1023
+ minWidth: 16,
1024
+ height: 16,
1025
+ borderRadius: 8,
1026
+ backgroundColor: active ? colors.primary : colors.textTertiary,
1027
+ alignItems: "center",
1028
+ justifyContent: "center",
1029
+ paddingHorizontal: sp1
1030
+ }
1031
+ },
1032
+ /* @__PURE__ */ React9.createElement(
1033
+ Text5,
1034
+ {
1035
+ style: {
1036
+ fontSize: typeScale["micro"]?.size ?? 10,
1037
+ lineHeight: typeScale["micro"]?.lineHeight ?? 14,
1038
+ fontWeight: "700",
1039
+ color: active ? colors.textOnPrimary : colors.textInverse
1040
+ }
1041
+ },
1042
+ String(badge)
1043
+ )
1044
+ ) : null,
1045
+ trailing != null ? trailing : null
1046
+ );
1047
+ }
1048
+ var styles2 = StyleSheet2.create({
1049
+ iconSlot: { width: 18, alignItems: "center", justifyContent: "center" }
1050
+ });
1051
+
1052
+ // src/lightbox/Lightbox.tsx
1053
+ import React10, { useEffect as useEffect2 } from "react";
1054
+ import { Modal, Platform, Pressable as Pressable6, View as View8 } from "react-native";
1055
+ function Lightbox({
1056
+ visible,
1057
+ onClose,
1058
+ children,
1059
+ closeLabel = "Close preview",
1060
+ renderCloseButton,
1061
+ renderActions
1062
+ }) {
1063
+ const theme = useOctoSpacesTheme();
1064
+ useEffect2(() => {
1065
+ if (!visible || Platform.OS !== "web") return;
1066
+ const onKey = (e) => {
1067
+ if (e.key === "Escape") onClose();
1068
+ };
1069
+ window.addEventListener("keydown", onKey);
1070
+ return () => window.removeEventListener("keydown", onKey);
1071
+ }, [visible, onClose]);
1072
+ const pad = theme.spacing["6"] ?? 24;
1073
+ const insetV = theme.spacing["8"] ?? 32;
1074
+ const insetH = theme.spacing["4"] ?? 16;
1075
+ return /* @__PURE__ */ React10.createElement(
1076
+ Modal,
1077
+ {
1078
+ visible,
1079
+ transparent: true,
1080
+ animationType: "fade",
1081
+ onRequestClose: onClose,
1082
+ statusBarTranslucent: true
1083
+ },
1084
+ /* @__PURE__ */ React10.createElement(
1085
+ Pressable6,
1086
+ {
1087
+ style: {
1088
+ flex: 1,
1089
+ alignItems: "center",
1090
+ justifyContent: "center",
1091
+ padding: pad,
1092
+ backgroundColor: theme.colors.overlay ?? "rgba(0,0,0,0.85)"
1093
+ },
1094
+ onPress: onClose,
1095
+ accessibilityLabel: closeLabel
1096
+ },
1097
+ /* @__PURE__ */ React10.createElement(
1098
+ View8,
1099
+ {
1100
+ style: { alignItems: "center", justifyContent: "center" },
1101
+ pointerEvents: "box-none"
1102
+ },
1103
+ children
1104
+ ),
1105
+ renderCloseButton ? /* @__PURE__ */ React10.createElement(View8, { style: { position: "absolute", top: insetV, right: insetH } }, renderCloseButton(onClose)) : null,
1106
+ renderActions ? /* @__PURE__ */ React10.createElement(View8, { style: { position: "absolute", bottom: insetV, right: insetH } }, renderActions()) : null
1107
+ )
1108
+ );
1109
+ }
401
1110
  export {
402
1111
  DiscoverList,
403
1112
  DiscoverRow,
404
1113
  DiscoverScreen,
1114
+ Lightbox,
405
1115
  OctoSpacesThemeProvider,
1116
+ Sidebar,
1117
+ SidebarActionButton,
1118
+ SidebarHeader,
1119
+ SidebarItem,
1120
+ SpacesRail,
406
1121
  avatarTint,
407
1122
  filterDiscoverEntries,
408
1123
  focusRingStyle,