@drakkar.software/octospaces-ui 0.4.0 → 0.4.2
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/CHANGELOG.md +34 -0
- package/dist/index.d.ts +128 -1
- package/dist/index.js +314 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/sidebar/SpaceSwitcher.tsx +541 -0
- package/src/sidebar/index.ts +2 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog — @drakkar.software/octospaces-ui
|
|
2
|
+
|
|
3
|
+
## 0.4.2
|
|
4
|
+
|
|
5
|
+
### `SpaceSwitcher` — `onTriggerPress` override
|
|
6
|
+
|
|
7
|
+
Added an optional `onTriggerPress?: () => void` prop to `SpaceSwitcher`.
|
|
8
|
+
|
|
9
|
+
When provided:
|
|
10
|
+
- The trigger button's `onPress` calls `onTriggerPress` instead of opening the
|
|
11
|
+
built-in dropdown.
|
|
12
|
+
- The chevron icon is hidden (the button no longer implies a picker).
|
|
13
|
+
- The `renderContainer` callback is never invoked.
|
|
14
|
+
|
|
15
|
+
Use this on surfaces where a dropdown is redundant (e.g. a desktop sidebar that
|
|
16
|
+
already has a rail for space switching) and a single tap to navigate to the
|
|
17
|
+
space-details page is the right UX.
|
|
18
|
+
|
|
19
|
+
Fully backward-compatible — existing consumers that do not pass `onTriggerPress`
|
|
20
|
+
retain identical behaviour.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 0.4.1
|
|
25
|
+
|
|
26
|
+
- `Lightbox` component added (headless fullscreen media viewer + zoom).
|
|
27
|
+
|
|
28
|
+
## 0.4.0
|
|
29
|
+
|
|
30
|
+
- `Sidebar`, `SidebarHeader`, `SidebarActionButton`, `SidebarItem` — shared
|
|
31
|
+
desktop sidebar panel shell and primitives.
|
|
32
|
+
- `SpaceSwitcher` — headless space picker trigger with `renderContainer` delegation.
|
|
33
|
+
- `sidebarPanel` added to `Palette` type (panel background distinct from rail
|
|
34
|
+
background `sidebar`).
|
package/dist/index.d.ts
CHANGED
|
@@ -645,6 +645,133 @@ interface SidebarItemProps {
|
|
|
645
645
|
}
|
|
646
646
|
declare function SidebarItem({ label, icon, active, badge, onPress, onLongPress, trailing, indent, }: SidebarItemProps): React.JSX.Element;
|
|
647
647
|
|
|
648
|
+
/**
|
|
649
|
+
* Headless themed space-switcher component.
|
|
650
|
+
*
|
|
651
|
+
* Renders a trigger button (active-space avatar + name + chevron) that opens a
|
|
652
|
+
* dropdown listing all spaces with per-row selection, a "join or create" action,
|
|
653
|
+
* optional space settings, and an app-provided footer slot (account section, etc.).
|
|
654
|
+
*
|
|
655
|
+
* The popup container (Popover on desktop, Sheet on mobile) is fully delegated to
|
|
656
|
+
* the host via `renderContainer` so this package stays free of modal dependencies.
|
|
657
|
+
* Avatars and icons are delegated via render-props for the same reason.
|
|
658
|
+
*
|
|
659
|
+
* @example
|
|
660
|
+
* ```tsx
|
|
661
|
+
* // OctoVault — sidebar variant with Popover container
|
|
662
|
+
* <SpaceSwitcher
|
|
663
|
+
* spaces={spaces}
|
|
664
|
+
* activeId={activeId}
|
|
665
|
+
* onSelect={switchSpace}
|
|
666
|
+
* onAdd={() => router.push('/join')}
|
|
667
|
+
* onSettings={() => router.push(`/space/${activeId}`)}
|
|
668
|
+
* variant="sidebar"
|
|
669
|
+
* renderTriggerAvatar={(space, size) => (
|
|
670
|
+
* <Avatar label={space?.short ?? ''} image={space?.image} size={size} />
|
|
671
|
+
* )}
|
|
672
|
+
* renderSpaceAvatar={(space, size) => (
|
|
673
|
+
* <Avatar label={space.short ?? ''} image={space.image} size={size} />
|
|
674
|
+
* )}
|
|
675
|
+
* renderIcon={(name, size, color) => <Icon name={SWITCHER_ICON[name]} size={size} color={color} />}
|
|
676
|
+
* renderContainer={({ isOpen, onClose, anchorRef, children }) => (
|
|
677
|
+
* <>
|
|
678
|
+
* <Popover visible={isOpen} onClose={onClose} anchorRef={anchorRef} placement="bottom-start" width={240}>
|
|
679
|
+
* {children}
|
|
680
|
+
* </Popover>
|
|
681
|
+
* </>
|
|
682
|
+
* )}
|
|
683
|
+
* footerSlot={<AccountSwitcher onRequestClose={...} onViewProfile={...} />}
|
|
684
|
+
* />
|
|
685
|
+
* ```
|
|
686
|
+
*/
|
|
687
|
+
|
|
688
|
+
/** Structural space item — no SDK dependency. */
|
|
689
|
+
interface SwitcherSpace {
|
|
690
|
+
id: string;
|
|
691
|
+
name: string;
|
|
692
|
+
/** 2-letter monogram used as avatar fallback. */
|
|
693
|
+
short?: string;
|
|
694
|
+
/** Uploaded space image URI; absent → host renders monogram. */
|
|
695
|
+
image?: string;
|
|
696
|
+
}
|
|
697
|
+
/** Icon name union for the switcher's built-in glyphs. */
|
|
698
|
+
type SwitcherIconName = 'chevron-down' | 'check' | 'plus' | 'gear';
|
|
699
|
+
interface SpaceSwitcherProps {
|
|
700
|
+
spaces: SwitcherSpace[];
|
|
701
|
+
activeId?: string | null;
|
|
702
|
+
/** Called when the user taps a space row. */
|
|
703
|
+
onSelect: (id: string) => void;
|
|
704
|
+
/** "Join or create a space" action. Omit to hide the row. */
|
|
705
|
+
onAdd?: () => void;
|
|
706
|
+
/** Override the add-row label. @default "Join or create a space" */
|
|
707
|
+
addLabel?: string;
|
|
708
|
+
/**
|
|
709
|
+
* "Space settings" action. Only shown when both `onSettings` and `activeId`
|
|
710
|
+
* are provided. Omit to hide.
|
|
711
|
+
*/
|
|
712
|
+
onSettings?: () => void;
|
|
713
|
+
/** Override the settings-row label. @default "Space settings" */
|
|
714
|
+
settingsLabel?: string;
|
|
715
|
+
/**
|
|
716
|
+
* Visual variant:
|
|
717
|
+
* - `'sidebar'` — compact left-aligned trigger for the desktop sidebar header.
|
|
718
|
+
* - `'appbar'` — centered trigger for a phone app-bar title area.
|
|
719
|
+
*/
|
|
720
|
+
variant: 'sidebar' | 'appbar';
|
|
721
|
+
/**
|
|
722
|
+
* Wraps the dropdown content in the host app's container (Popover / Sheet).
|
|
723
|
+
* Called with `{ isOpen, onClose, anchorRef, children }` — must render
|
|
724
|
+
* children inside an appropriate modal surface.
|
|
725
|
+
*/
|
|
726
|
+
renderContainer: (props: {
|
|
727
|
+
isOpen: boolean;
|
|
728
|
+
onClose: () => void;
|
|
729
|
+
anchorRef: React.RefObject<View>;
|
|
730
|
+
children: React.ReactNode;
|
|
731
|
+
}) => React.ReactNode;
|
|
732
|
+
/**
|
|
733
|
+
* Render the active-space avatar inside the trigger button.
|
|
734
|
+
* Receives the active `SwitcherSpace` (or `null` when none) and a pixel size.
|
|
735
|
+
* Omit to render nothing in the avatar slot.
|
|
736
|
+
*/
|
|
737
|
+
renderTriggerAvatar?: (space: SwitcherSpace | null, size: number) => React.ReactNode;
|
|
738
|
+
/**
|
|
739
|
+
* Render a space row's leading avatar.
|
|
740
|
+
* Receives the `SwitcherSpace` and a pixel size.
|
|
741
|
+
* Omit to render nothing in the leading slot.
|
|
742
|
+
*/
|
|
743
|
+
renderSpaceAvatar?: (space: SwitcherSpace, size: number) => React.ReactNode;
|
|
744
|
+
/**
|
|
745
|
+
* Render an icon glyph. Name is one of `'chevron-down' | 'check' | 'plus' | 'gear'`.
|
|
746
|
+
* Omit to hide chevron, check, and action icons (spaces remain selectable).
|
|
747
|
+
*/
|
|
748
|
+
renderIcon?: (name: SwitcherIconName, size: number, color: string) => React.ReactNode;
|
|
749
|
+
/**
|
|
750
|
+
* Footer rendered below the space list + action rows — use for account-switcher
|
|
751
|
+
* sections (with separator if needed). Fully app-owned.
|
|
752
|
+
*/
|
|
753
|
+
footerSlot?: React.ReactNode;
|
|
754
|
+
/**
|
|
755
|
+
* When provided, replaces the default "open dropdown" behaviour on trigger press.
|
|
756
|
+
* The chevron is also hidden. Use this to navigate directly to a space-details
|
|
757
|
+
* page instead of opening a picker — e.g. on desktop where a rail handles
|
|
758
|
+
* switching and a dropdown is redundant.
|
|
759
|
+
*
|
|
760
|
+
* @example
|
|
761
|
+
* ```tsx
|
|
762
|
+
* // Navigate to space details instead of opening the picker (desktop sidebar)
|
|
763
|
+
* <SpaceSwitcher
|
|
764
|
+
* onTriggerPress={() => router.push(`/space/${activeId}`)}
|
|
765
|
+
* // renderContainer still required but never called when onTriggerPress is set
|
|
766
|
+
* renderContainer={() => null}
|
|
767
|
+
* ...
|
|
768
|
+
* />
|
|
769
|
+
* ```
|
|
770
|
+
*/
|
|
771
|
+
onTriggerPress?: () => void;
|
|
772
|
+
}
|
|
773
|
+
declare function SpaceSwitcher({ spaces, activeId, onSelect, onAdd, addLabel, onSettings, settingsLabel, variant, renderContainer, renderTriggerAvatar, renderSpaceAvatar, renderIcon, footerSlot, onTriggerPress, }: SpaceSwitcherProps): React.JSX.Element;
|
|
774
|
+
|
|
648
775
|
/**
|
|
649
776
|
* Full-screen scrim overlay that centers its content. Tapping the backdrop, the
|
|
650
777
|
* close button, the Escape key (web) or the hardware back (Android) dismisses it.
|
|
@@ -700,4 +827,4 @@ interface LightboxProps {
|
|
|
700
827
|
*/
|
|
701
828
|
declare function Lightbox({ visible, onClose, children, closeLabel, renderCloseButton, renderActions, }: LightboxProps): React.JSX.Element;
|
|
702
829
|
|
|
703
|
-
export { type ColorScheme, type DiscoverEntry, DiscoverList, type DiscoverListProps, DiscoverRow, type DiscoverRowProps, DiscoverScreen, type DiscoverScreenProps, type Easing, type Fonts, type LabelTracking, type Layers, type Layout, Lightbox, type LightboxProps, type Motion, type MotionToken, OctoSpacesThemeProvider, type OctoSpacesThemeProviderProps, type Opacity, type Palette, type Radii, type RailIconName, type RailSpace, type ShadowToken, type Shadows, Sidebar, SidebarActionButton, type SidebarActionButtonProps, SidebarHeader, type SidebarHeaderProps, SidebarItem, type SidebarItemProps, type SidebarProps, SpacesRail, type SpacesRailProps, type Spacing, type Swatches, type Theme, type TypeScale, type Typography, avatarTint, filterDiscoverEntries, focusRingStyle, glowShadow, paperBorder, presenceColor, sortDiscoverEntries, statusColor, swatch, useOctoSpacesTheme, verificationColor };
|
|
830
|
+
export { type ColorScheme, type DiscoverEntry, DiscoverList, type DiscoverListProps, DiscoverRow, type DiscoverRowProps, DiscoverScreen, type DiscoverScreenProps, type Easing, type Fonts, type LabelTracking, type Layers, type Layout, Lightbox, type LightboxProps, type Motion, type MotionToken, OctoSpacesThemeProvider, type OctoSpacesThemeProviderProps, type Opacity, type Palette, type Radii, type RailIconName, type RailSpace, type ShadowToken, type Shadows, Sidebar, SidebarActionButton, type SidebarActionButtonProps, SidebarHeader, type SidebarHeaderProps, SidebarItem, type SidebarItemProps, type SidebarProps, SpaceSwitcher, type SpaceSwitcherProps, SpacesRail, type SpacesRailProps, type Spacing, type Swatches, type SwitcherIconName, type SwitcherSpace, type Theme, type TypeScale, type Typography, avatarTint, filterDiscoverEntries, focusRingStyle, glowShadow, paperBorder, presenceColor, sortDiscoverEntries, statusColor, swatch, useOctoSpacesTheme, verificationColor };
|
package/dist/index.js
CHANGED
|
@@ -1049,9 +1049,313 @@ var styles2 = StyleSheet2.create({
|
|
|
1049
1049
|
iconSlot: { width: 18, alignItems: "center", justifyContent: "center" }
|
|
1050
1050
|
});
|
|
1051
1051
|
|
|
1052
|
+
// src/sidebar/SpaceSwitcher.tsx
|
|
1053
|
+
import React10, { useRef as useRef2, useState as useState5 } from "react";
|
|
1054
|
+
import { Pressable as RNPressable4, StyleSheet as StyleSheet3, Text as Text6, View as View8 } from "react-native";
|
|
1055
|
+
var Pressable6 = RNPressable4;
|
|
1056
|
+
function SpaceSwitcher({
|
|
1057
|
+
spaces,
|
|
1058
|
+
activeId,
|
|
1059
|
+
onSelect,
|
|
1060
|
+
onAdd,
|
|
1061
|
+
addLabel = "Join or create a space",
|
|
1062
|
+
onSettings,
|
|
1063
|
+
settingsLabel = "Space settings",
|
|
1064
|
+
variant,
|
|
1065
|
+
renderContainer,
|
|
1066
|
+
renderTriggerAvatar,
|
|
1067
|
+
renderSpaceAvatar,
|
|
1068
|
+
renderIcon,
|
|
1069
|
+
footerSlot,
|
|
1070
|
+
onTriggerPress
|
|
1071
|
+
}) {
|
|
1072
|
+
const theme = useOctoSpacesTheme();
|
|
1073
|
+
const { colors, type: typeScale, fonts, spacing: sp, radii } = theme;
|
|
1074
|
+
const [open, setOpen] = useState5(false);
|
|
1075
|
+
const [triggerHovered, setTriggerHovered] = useState5(false);
|
|
1076
|
+
const anchorRef = useRef2(null);
|
|
1077
|
+
const active = spaces.find((s) => s.id === activeId) ?? spaces[0] ?? null;
|
|
1078
|
+
const close = () => setOpen(false);
|
|
1079
|
+
const handleSelect = (id) => {
|
|
1080
|
+
close();
|
|
1081
|
+
onSelect(id);
|
|
1082
|
+
};
|
|
1083
|
+
const handleAdd = () => {
|
|
1084
|
+
close();
|
|
1085
|
+
onAdd?.();
|
|
1086
|
+
};
|
|
1087
|
+
const handleSettings = () => {
|
|
1088
|
+
close();
|
|
1089
|
+
onSettings?.();
|
|
1090
|
+
};
|
|
1091
|
+
const sp1 = sp["1"] ?? 4;
|
|
1092
|
+
const sp2 = sp["2"] ?? 8;
|
|
1093
|
+
const sp3 = sp["3"] ?? 12;
|
|
1094
|
+
const sp4 = sp["4"] ?? 16;
|
|
1095
|
+
const radMd = radii["md"] ?? 6;
|
|
1096
|
+
const bodyFont = fonts["body"] ?? void 0;
|
|
1097
|
+
const bodySize = typeScale["callout"]?.size ?? 13;
|
|
1098
|
+
const bodyLine = typeScale["callout"]?.lineHeight ?? 18;
|
|
1099
|
+
const labelSize = typeScale["caption"]?.size ?? 11;
|
|
1100
|
+
const labelLine = typeScale["caption"]?.lineHeight ?? 16;
|
|
1101
|
+
const triggerStyle = variant === "sidebar" ? {
|
|
1102
|
+
flex: 1,
|
|
1103
|
+
flexDirection: "row",
|
|
1104
|
+
alignItems: "center",
|
|
1105
|
+
gap: sp2,
|
|
1106
|
+
paddingHorizontal: sp2,
|
|
1107
|
+
paddingVertical: sp1 + 2,
|
|
1108
|
+
borderRadius: radMd,
|
|
1109
|
+
minWidth: 0,
|
|
1110
|
+
backgroundColor: triggerHovered ? colors.primarySubtle ?? "rgba(0,0,0,0.05)" : "transparent"
|
|
1111
|
+
} : {
|
|
1112
|
+
flexDirection: "row",
|
|
1113
|
+
alignItems: "center",
|
|
1114
|
+
justifyContent: "center",
|
|
1115
|
+
gap: sp2,
|
|
1116
|
+
paddingHorizontal: sp2,
|
|
1117
|
+
paddingVertical: sp1,
|
|
1118
|
+
borderRadius: radMd,
|
|
1119
|
+
backgroundColor: triggerHovered ? colors.primarySubtle ?? "rgba(0,0,0,0.05)" : "transparent"
|
|
1120
|
+
};
|
|
1121
|
+
const dropdownContent = /* @__PURE__ */ React10.createElement(View8, { style: { paddingVertical: sp1 } }, spaces.length > 0 ? /* @__PURE__ */ React10.createElement(
|
|
1122
|
+
SectionLabel,
|
|
1123
|
+
{
|
|
1124
|
+
label: "Spaces",
|
|
1125
|
+
color: colors.textTertiary,
|
|
1126
|
+
size: labelSize,
|
|
1127
|
+
lineHeight: labelLine,
|
|
1128
|
+
font: bodyFont,
|
|
1129
|
+
paddingH: sp4,
|
|
1130
|
+
paddingV: sp1
|
|
1131
|
+
}
|
|
1132
|
+
) : null, spaces.map((s) => /* @__PURE__ */ React10.createElement(
|
|
1133
|
+
SpaceRow,
|
|
1134
|
+
{
|
|
1135
|
+
key: s.id,
|
|
1136
|
+
space: s,
|
|
1137
|
+
active: s.id === (active?.id ?? null),
|
|
1138
|
+
onPress: () => handleSelect(s.id),
|
|
1139
|
+
renderAvatar: renderSpaceAvatar,
|
|
1140
|
+
renderIcon,
|
|
1141
|
+
colors,
|
|
1142
|
+
bodyFont,
|
|
1143
|
+
bodySize,
|
|
1144
|
+
bodyLine,
|
|
1145
|
+
sp2,
|
|
1146
|
+
sp3,
|
|
1147
|
+
sp4,
|
|
1148
|
+
radMd
|
|
1149
|
+
}
|
|
1150
|
+
)), onAdd ? /* @__PURE__ */ React10.createElement(
|
|
1151
|
+
ActionRow,
|
|
1152
|
+
{
|
|
1153
|
+
label: spaces.length > 0 ? addLabel : "Create your first space",
|
|
1154
|
+
iconName: "plus",
|
|
1155
|
+
onPress: handleAdd,
|
|
1156
|
+
renderIcon,
|
|
1157
|
+
colors,
|
|
1158
|
+
bodyFont,
|
|
1159
|
+
bodySize,
|
|
1160
|
+
bodyLine,
|
|
1161
|
+
sp2,
|
|
1162
|
+
sp3,
|
|
1163
|
+
sp4,
|
|
1164
|
+
radMd
|
|
1165
|
+
}
|
|
1166
|
+
) : null, onSettings && active ? /* @__PURE__ */ React10.createElement(
|
|
1167
|
+
ActionRow,
|
|
1168
|
+
{
|
|
1169
|
+
label: settingsLabel,
|
|
1170
|
+
iconName: "gear",
|
|
1171
|
+
onPress: handleSettings,
|
|
1172
|
+
renderIcon,
|
|
1173
|
+
colors,
|
|
1174
|
+
bodyFont,
|
|
1175
|
+
bodySize,
|
|
1176
|
+
bodyLine,
|
|
1177
|
+
sp2,
|
|
1178
|
+
sp3,
|
|
1179
|
+
sp4,
|
|
1180
|
+
radMd
|
|
1181
|
+
}
|
|
1182
|
+
) : null, footerSlot != null ? /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(
|
|
1183
|
+
View8,
|
|
1184
|
+
{
|
|
1185
|
+
style: {
|
|
1186
|
+
height: StyleSheet3.hairlineWidth,
|
|
1187
|
+
backgroundColor: colors.borderSubtle,
|
|
1188
|
+
marginVertical: sp1,
|
|
1189
|
+
marginHorizontal: sp2
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
), footerSlot) : null);
|
|
1193
|
+
return /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(
|
|
1194
|
+
Pressable6,
|
|
1195
|
+
{
|
|
1196
|
+
ref: anchorRef,
|
|
1197
|
+
accessibilityRole: "button",
|
|
1198
|
+
accessibilityLabel: active ? `${active.name} \u2014 switch space` : "Switch space",
|
|
1199
|
+
accessibilityState: { expanded: open },
|
|
1200
|
+
hitSlop: 6,
|
|
1201
|
+
onPress: onTriggerPress ?? (() => setOpen(true)),
|
|
1202
|
+
onMouseEnter: () => setTriggerHovered(true),
|
|
1203
|
+
onMouseLeave: () => setTriggerHovered(false),
|
|
1204
|
+
style: triggerStyle
|
|
1205
|
+
},
|
|
1206
|
+
renderTriggerAvatar ? renderTriggerAvatar(active, 22) : null,
|
|
1207
|
+
/* @__PURE__ */ React10.createElement(
|
|
1208
|
+
Text6,
|
|
1209
|
+
{
|
|
1210
|
+
numberOfLines: 1,
|
|
1211
|
+
style: {
|
|
1212
|
+
flex: variant === "sidebar" ? 1 : void 0,
|
|
1213
|
+
minWidth: 0,
|
|
1214
|
+
flexShrink: 1,
|
|
1215
|
+
fontSize: typeScale["heading"]?.size ?? 15,
|
|
1216
|
+
lineHeight: typeScale["heading"]?.lineHeight ?? 20,
|
|
1217
|
+
fontWeight: "600",
|
|
1218
|
+
color: colors.text,
|
|
1219
|
+
fontFamily: bodyFont
|
|
1220
|
+
}
|
|
1221
|
+
},
|
|
1222
|
+
active?.name ?? "Spaces"
|
|
1223
|
+
),
|
|
1224
|
+
!onTriggerPress && renderIcon ? renderIcon("chevron-down", 14, colors.textTertiary) : null
|
|
1225
|
+
), onTriggerPress ? null : renderContainer({ isOpen: open, onClose: close, anchorRef, children: dropdownContent }));
|
|
1226
|
+
}
|
|
1227
|
+
function SectionLabel({ label, color, size, lineHeight, font, paddingH, paddingV }) {
|
|
1228
|
+
return /* @__PURE__ */ React10.createElement(
|
|
1229
|
+
Text6,
|
|
1230
|
+
{
|
|
1231
|
+
style: {
|
|
1232
|
+
fontSize: size,
|
|
1233
|
+
lineHeight,
|
|
1234
|
+
fontWeight: "600",
|
|
1235
|
+
color,
|
|
1236
|
+
fontFamily: font,
|
|
1237
|
+
textTransform: "uppercase",
|
|
1238
|
+
letterSpacing: 0.5,
|
|
1239
|
+
paddingHorizontal: paddingH,
|
|
1240
|
+
paddingVertical: paddingV
|
|
1241
|
+
}
|
|
1242
|
+
},
|
|
1243
|
+
label
|
|
1244
|
+
);
|
|
1245
|
+
}
|
|
1246
|
+
function SpaceRow({
|
|
1247
|
+
space,
|
|
1248
|
+
active,
|
|
1249
|
+
onPress,
|
|
1250
|
+
renderAvatar,
|
|
1251
|
+
renderIcon,
|
|
1252
|
+
colors,
|
|
1253
|
+
bodyFont,
|
|
1254
|
+
bodySize,
|
|
1255
|
+
bodyLine,
|
|
1256
|
+
sp2,
|
|
1257
|
+
sp3,
|
|
1258
|
+
sp4,
|
|
1259
|
+
radMd
|
|
1260
|
+
}) {
|
|
1261
|
+
const [hovered, setHovered] = useState5(false);
|
|
1262
|
+
const bg = hovered ? colors.primarySubtle ?? "rgba(0,0,0,0.04)" : "transparent";
|
|
1263
|
+
return /* @__PURE__ */ React10.createElement(
|
|
1264
|
+
Pressable6,
|
|
1265
|
+
{
|
|
1266
|
+
accessibilityRole: "menuitem",
|
|
1267
|
+
accessibilityLabel: active ? `${space.name} (current)` : `Switch to ${space.name}`,
|
|
1268
|
+
accessibilityState: { selected: active },
|
|
1269
|
+
onPress,
|
|
1270
|
+
onMouseEnter: () => setHovered(true),
|
|
1271
|
+
onMouseLeave: () => setHovered(false),
|
|
1272
|
+
style: {
|
|
1273
|
+
flexDirection: "row",
|
|
1274
|
+
alignItems: "center",
|
|
1275
|
+
gap: sp3,
|
|
1276
|
+
paddingHorizontal: sp4,
|
|
1277
|
+
paddingVertical: sp2,
|
|
1278
|
+
borderRadius: radMd,
|
|
1279
|
+
backgroundColor: bg
|
|
1280
|
+
}
|
|
1281
|
+
},
|
|
1282
|
+
renderAvatar ? renderAvatar(space, 24) : null,
|
|
1283
|
+
/* @__PURE__ */ React10.createElement(
|
|
1284
|
+
Text6,
|
|
1285
|
+
{
|
|
1286
|
+
numberOfLines: 1,
|
|
1287
|
+
style: {
|
|
1288
|
+
flex: 1,
|
|
1289
|
+
minWidth: 0,
|
|
1290
|
+
fontSize: bodySize,
|
|
1291
|
+
lineHeight: bodyLine,
|
|
1292
|
+
fontWeight: active ? "600" : "400",
|
|
1293
|
+
color: active ? colors.primary : colors.text,
|
|
1294
|
+
fontFamily: bodyFont
|
|
1295
|
+
}
|
|
1296
|
+
},
|
|
1297
|
+
space.name
|
|
1298
|
+
),
|
|
1299
|
+
active && renderIcon ? renderIcon("check", 15, colors.primary) : null
|
|
1300
|
+
);
|
|
1301
|
+
}
|
|
1302
|
+
function ActionRow({
|
|
1303
|
+
label,
|
|
1304
|
+
iconName,
|
|
1305
|
+
onPress,
|
|
1306
|
+
renderIcon,
|
|
1307
|
+
colors,
|
|
1308
|
+
bodyFont,
|
|
1309
|
+
bodySize,
|
|
1310
|
+
bodyLine,
|
|
1311
|
+
sp2,
|
|
1312
|
+
sp3,
|
|
1313
|
+
sp4,
|
|
1314
|
+
radMd
|
|
1315
|
+
}) {
|
|
1316
|
+
const [hovered, setHovered] = useState5(false);
|
|
1317
|
+
const bg = hovered ? colors.primarySubtle ?? "rgba(0,0,0,0.04)" : "transparent";
|
|
1318
|
+
return /* @__PURE__ */ React10.createElement(
|
|
1319
|
+
Pressable6,
|
|
1320
|
+
{
|
|
1321
|
+
accessibilityRole: "menuitem",
|
|
1322
|
+
accessibilityLabel: label,
|
|
1323
|
+
onPress,
|
|
1324
|
+
onMouseEnter: () => setHovered(true),
|
|
1325
|
+
onMouseLeave: () => setHovered(false),
|
|
1326
|
+
style: {
|
|
1327
|
+
flexDirection: "row",
|
|
1328
|
+
alignItems: "center",
|
|
1329
|
+
gap: sp3,
|
|
1330
|
+
paddingHorizontal: sp4,
|
|
1331
|
+
paddingVertical: sp2,
|
|
1332
|
+
borderRadius: radMd,
|
|
1333
|
+
backgroundColor: bg
|
|
1334
|
+
}
|
|
1335
|
+
},
|
|
1336
|
+
renderIcon ? /* @__PURE__ */ React10.createElement(View8, { style: { width: 24, alignItems: "center", justifyContent: "center" } }, renderIcon(iconName, 15, colors.textSecondary)) : null,
|
|
1337
|
+
/* @__PURE__ */ React10.createElement(
|
|
1338
|
+
Text6,
|
|
1339
|
+
{
|
|
1340
|
+
numberOfLines: 1,
|
|
1341
|
+
style: {
|
|
1342
|
+
flex: 1,
|
|
1343
|
+
minWidth: 0,
|
|
1344
|
+
fontSize: bodySize,
|
|
1345
|
+
lineHeight: bodyLine,
|
|
1346
|
+
fontWeight: "400",
|
|
1347
|
+
color: colors.text,
|
|
1348
|
+
fontFamily: bodyFont
|
|
1349
|
+
}
|
|
1350
|
+
},
|
|
1351
|
+
label
|
|
1352
|
+
)
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1052
1356
|
// src/lightbox/Lightbox.tsx
|
|
1053
|
-
import
|
|
1054
|
-
import { Modal, Platform, Pressable as
|
|
1357
|
+
import React11, { useEffect as useEffect2 } from "react";
|
|
1358
|
+
import { Modal, Platform, Pressable as Pressable7, View as View9 } from "react-native";
|
|
1055
1359
|
function Lightbox({
|
|
1056
1360
|
visible,
|
|
1057
1361
|
onClose,
|
|
@@ -1072,7 +1376,7 @@ function Lightbox({
|
|
|
1072
1376
|
const pad = theme.spacing["6"] ?? 24;
|
|
1073
1377
|
const insetV = theme.spacing["8"] ?? 32;
|
|
1074
1378
|
const insetH = theme.spacing["4"] ?? 16;
|
|
1075
|
-
return /* @__PURE__ */
|
|
1379
|
+
return /* @__PURE__ */ React11.createElement(
|
|
1076
1380
|
Modal,
|
|
1077
1381
|
{
|
|
1078
1382
|
visible,
|
|
@@ -1081,8 +1385,8 @@ function Lightbox({
|
|
|
1081
1385
|
onRequestClose: onClose,
|
|
1082
1386
|
statusBarTranslucent: true
|
|
1083
1387
|
},
|
|
1084
|
-
/* @__PURE__ */
|
|
1085
|
-
|
|
1388
|
+
/* @__PURE__ */ React11.createElement(
|
|
1389
|
+
Pressable7,
|
|
1086
1390
|
{
|
|
1087
1391
|
style: {
|
|
1088
1392
|
flex: 1,
|
|
@@ -1094,16 +1398,16 @@ function Lightbox({
|
|
|
1094
1398
|
onPress: onClose,
|
|
1095
1399
|
accessibilityLabel: closeLabel
|
|
1096
1400
|
},
|
|
1097
|
-
/* @__PURE__ */
|
|
1098
|
-
|
|
1401
|
+
/* @__PURE__ */ React11.createElement(
|
|
1402
|
+
View9,
|
|
1099
1403
|
{
|
|
1100
1404
|
style: { alignItems: "center", justifyContent: "center" },
|
|
1101
1405
|
pointerEvents: "box-none"
|
|
1102
1406
|
},
|
|
1103
1407
|
children
|
|
1104
1408
|
),
|
|
1105
|
-
renderCloseButton ? /* @__PURE__ */
|
|
1106
|
-
renderActions ? /* @__PURE__ */
|
|
1409
|
+
renderCloseButton ? /* @__PURE__ */ React11.createElement(View9, { style: { position: "absolute", top: insetV, right: insetH } }, renderCloseButton(onClose)) : null,
|
|
1410
|
+
renderActions ? /* @__PURE__ */ React11.createElement(View9, { style: { position: "absolute", bottom: insetV, right: insetH } }, renderActions()) : null
|
|
1107
1411
|
)
|
|
1108
1412
|
);
|
|
1109
1413
|
}
|
|
@@ -1117,6 +1421,7 @@ export {
|
|
|
1117
1421
|
SidebarActionButton,
|
|
1118
1422
|
SidebarHeader,
|
|
1119
1423
|
SidebarItem,
|
|
1424
|
+
SpaceSwitcher,
|
|
1120
1425
|
SpacesRail,
|
|
1121
1426
|
avatarTint,
|
|
1122
1427
|
filterDiscoverEntries,
|