Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.

Commit dfce794

Browse files
authored
style(ui): space selector menu — starred first, divider, flyout item style
The composer's space menu now leads with the starred spaces in their sidebar order (#me first), a separator, then the rest alphabetically. Items use the switchers' flyout vocabulary — leading check well, the space's own glyph (only #me carries one), left-aligned 13px name — and the content gets a proper min width. The blanket cube/lock icons are gone in favor of the channelGlyph rule. Generated-By: PostHog Code Task-Id: 0331ac58-0a1c-4b4e-b884-2ff7d8e71986
1 parent 490719c commit dfce794

1 file changed

Lines changed: 77 additions & 44 deletions

File tree

packages/ui/src/features/canvas/components/SpaceSelect.tsx

Lines changed: 77 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,29 @@
1-
import {
2-
CaretDown,
3-
CheckIcon,
4-
CubeFocusIcon,
5-
LockSimpleIcon,
6-
} from "@phosphor-icons/react";
1+
import { CaretDown, Check } from "@phosphor-icons/react";
72
import {
83
Button,
94
DropdownMenu,
105
DropdownMenuContent,
116
DropdownMenuItem,
7+
DropdownMenuSeparator,
128
DropdownMenuTrigger,
139
} from "@posthog/quill";
14-
import { useChannels } from "@posthog/ui/features/canvas/hooks/useChannels";
10+
import { channelGlyph } from "@posthog/ui/features/canvas/components/channelGlyph";
11+
import { useChannelStars } from "@posthog/ui/features/canvas/hooks/useChannelStars";
12+
import {
13+
type Channel,
14+
useChannels,
15+
} from "@posthog/ui/features/canvas/hooks/useChannels";
1516
import { PERSONAL_CHANNEL_NAME } from "@posthog/ui/features/canvas/hooks/useTaskChannels";
17+
import { useSpacesSidebarStore } from "@posthog/ui/features/canvas/stores/spacesSidebarStore";
1618
import { useMemo } from "react";
1719

18-
function spaceIcon(name: string | undefined) {
19-
return name === PERSONAL_CHANNEL_NAME ? (
20-
<LockSimpleIcon size={14} weight="regular" />
21-
) : (
22-
<CubeFocusIcon size={14} weight="regular" />
23-
);
24-
}
25-
2620
/**
2721
* Which space a new task files into — a chip for the composer's selector row,
28-
* drawn exactly like WorkspaceModeSelect ("Cloud"/"Local") beside it.
29-
* Personal space first, the rest alphabetical.
22+
* drawn exactly like WorkspaceModeSelect ("Cloud"/"Local") beside it. The menu
23+
* leads with the starred spaces in their sidebar order (#me first), a
24+
* separator, then everything else alphabetically — items in the flyout
25+
* vocabulary the switchers use: a leading check well, then the space's own
26+
* glyph (only #me carries one), then the name.
3027
*/
3128
export function SpaceSelect({
3229
value,
@@ -36,26 +33,68 @@ export function SpaceSelect({
3633
onChange: (channelId: string) => void;
3734
}) {
3835
const { channels } = useChannels();
36+
const { starredRefToShortcutId } = useChannelStars();
37+
const spaceOrder = useSpacesSidebarStore((s) => s.spaceOrder);
3938
const current = channels.find((c) => c.id === value);
4039

41-
const options = useMemo(
42-
() =>
43-
[...channels].sort((a, b) => {
44-
if (a.name === PERSONAL_CHANNEL_NAME) return -1;
45-
if (b.name === PERSONAL_CHANNEL_NAME) return 1;
46-
return a.name.localeCompare(b.name);
47-
}),
48-
[channels],
49-
);
40+
const { starred, rest } = useMemo(() => {
41+
const me = channels.filter((c) => c.name === PERSONAL_CHANNEL_NAME);
42+
const rank = new Map(spaceOrder.map((id, index) => [id, index]));
43+
const starredList = channels
44+
.filter(
45+
(c) =>
46+
c.name !== PERSONAL_CHANNEL_NAME &&
47+
starredRefToShortcutId.has(c.path),
48+
)
49+
.sort(
50+
(a, b) =>
51+
(rank.get(a.id) ?? Number.MAX_SAFE_INTEGER) -
52+
(rank.get(b.id) ?? Number.MAX_SAFE_INTEGER),
53+
);
54+
const starredIds = new Set(starredList.map((c) => c.id));
55+
return {
56+
starred: [...me, ...starredList],
57+
rest: channels
58+
.filter(
59+
(c) => c.name !== PERSONAL_CHANNEL_NAME && !starredIds.has(c.id),
60+
)
61+
.sort((a, b) => a.name.localeCompare(b.name)),
62+
};
63+
}, [channels, starredRefToShortcutId, spaceOrder]);
64+
65+
const triggerGlyph = channelGlyph(current?.name, { size: 14, space: true });
66+
67+
const renderItem = (space: Channel) => {
68+
const glyph = channelGlyph(space.name, { size: 14, space: true });
69+
return (
70+
<DropdownMenuItem
71+
key={space.id}
72+
className="justify-start"
73+
onClick={() => {
74+
if (space.id !== value) onChange(space.id);
75+
}}
76+
>
77+
<span className="flex w-4 shrink-0 items-center justify-center">
78+
{space.id === value && <Check size={14} className="text-accent-11" />}
79+
</span>
80+
{glyph && (
81+
<span className="flex shrink-0 items-center text-muted-foreground">
82+
{glyph}
83+
</span>
84+
)}
85+
<span className="min-w-0 truncate text-[13px]">{space.name}</span>
86+
</DropdownMenuItem>
87+
);
88+
};
5089

5190
return (
5291
<DropdownMenu>
5392
<DropdownMenuTrigger
5493
render={
5594
<Button type="button" variant="outline" size="sm" aria-label="Space">
56-
<span className="text-muted-foreground">
57-
{spaceIcon(current?.name)}
58-
</span>
95+
{triggerGlyph && (
96+
<span className="text-muted-foreground">{triggerGlyph}</span>
97+
)}
5998
{current?.name ?? "Space"}
6099
<CaretDown
61100
size={10}
@@ -65,21 +104,15 @@ export function SpaceSelect({
65104
</Button>
66105
}
67106
/>
68-
<DropdownMenuContent align="start" side="bottom" sideOffset={6}>
69-
{options.map((space) => (
70-
<DropdownMenuItem
71-
key={space.id}
72-
onClick={() => {
73-
if (space.id !== value) onChange(space.id);
74-
}}
75-
>
76-
<span className="text-muted-foreground">
77-
{spaceIcon(space.name)}
78-
</span>
79-
<span className="min-w-0 flex-1 truncate">{space.name}</span>
80-
{space.id === value && <CheckIcon size={14} />}
81-
</DropdownMenuItem>
82-
))}
107+
<DropdownMenuContent
108+
align="start"
109+
side="bottom"
110+
sideOffset={6}
111+
className="w-auto min-w-[200px]"
112+
>
113+
{starred.map(renderItem)}
114+
{starred.length > 0 && rest.length > 0 && <DropdownMenuSeparator />}
115+
{rest.map(renderItem)}
83116
</DropdownMenuContent>
84117
</DropdownMenu>
85118
);

0 commit comments

Comments
 (0)