From e08d2bd5e5758edba756a09e410064fffd3c1ce7 Mon Sep 17 00:00:00 2001 From: Amr Mohammed El-Sheraey <141947355+AmrMsCLL@users.noreply.github.com> Date: Thu, 1 Oct 2026 23:13:27 +0300 Subject: [PATCH] feat: add the components Strata's foundation needs Adds Tabs, TagChip with a fixed set of tag colours, Avatar with a presence dot, CodeInput for one-time codes, Sidebar, and CommandPalette with a Ctrl+K and Cmd+K shortcut hook. AppShell takes an optional sidebar that sits beside the page on wider screens. The search icon joins the icon set, and the preview shows every new component. --- preview/main.tsx | 77 +++++++++- src/components/AppShell.tsx | 17 ++- src/components/Avatar.tsx | 16 ++ src/components/CodeInput.tsx | 25 ++++ src/components/CommandPalette.tsx | 161 ++++++++++++++++++++ src/components/Icon.tsx | 1 + src/components/Sidebar.tsx | 50 +++++++ src/components/Tabs.tsx | 25 ++++ src/components/TagChip.tsx | 41 ++++++ src/index.ts | 11 ++ src/theme.css | 236 ++++++++++++++++++++++++++++++ test/workspace.spec.tsx | 174 ++++++++++++++++++++++ 12 files changed, 829 insertions(+), 5 deletions(-) create mode 100644 src/components/Avatar.tsx create mode 100644 src/components/CodeInput.tsx create mode 100644 src/components/CommandPalette.tsx create mode 100644 src/components/Sidebar.tsx create mode 100644 src/components/Tabs.tsx create mode 100644 src/components/TagChip.tsx create mode 100644 test/workspace.spec.tsx diff --git a/preview/main.tsx b/preview/main.tsx index a37daa5..d92b33c 100644 --- a/preview/main.tsx +++ b/preview/main.tsx @@ -1,7 +1,10 @@ -import { StrictMode, useState, type ReactNode } from 'react'; +import { StrictMode, useCallback, useState, type ReactNode } from 'react'; import { createRoot } from 'react-dom/client'; import { AppShell, + Avatar, + CodeInput, + CommandPalette, ConfirmDialog, Dialog, EmptyState, @@ -14,13 +17,19 @@ import { OutageGate, Pagination, Popover, + Sidebar, SmoothImage, SnackbarProvider, + Tabs, + TagChip, + tagColors, TextAreaField, TextField, Toggle, timeAgo, + useCommandShortcut, useSnackbar, + type CommandItem, type IconName, } from '../src/index.ts'; import './styles.css'; @@ -38,7 +47,13 @@ const colors = [ ['wip', 'bg-wip'], ['dormant', 'bg-dormant'], ]; -const icons: IconName[] = ['bell', 'compass', 'filter', 'gamepad', 'home', 'library', 'user']; +const icons: IconName[] = ['bell', 'compass', 'filter', 'gamepad', 'home', 'library', 'search', 'user']; +const commands: CommandItem[] = [ + { group: 'Go to', href: '#workspace', id: 'notes', label: 'Notes', hint: 'g n' }, + { group: 'Go to', href: '#workspace', id: 'agenda', label: 'Agenda', hint: 'g a' }, + { group: 'Actions', id: 'task', label: 'New task', onSelect: () => {} }, + { group: 'Actions', id: 'note', label: 'New note', onSelect: () => {} }, +]; const outageListeners = new Set<() => void>(); function Section({ children, title }: { children: ReactNode; title: string }) { @@ -65,6 +80,10 @@ function SnackbarDemo() { function Preview() { const [dialog, setDialog] = useState<'plain' | 'confirm' | null>(null); + const [palette, setPalette] = useState(false); + const [query, setQuery] = useState(''); + const openPalette = useCallback(() => setPalette(true), []); + useCommandShortcut(openPalette); const [checked, setChecked] = useState(true); const [page, setPage] = useState(3); @@ -240,6 +259,60 @@ function Preview() { )} +
+ +
+ {tagColors.map((color) => ( + + ))} + + {}} /> +
+
+ + + +
+
+ +
+
+ +
+ + {palette && ( + command.label.toLowerCase().includes(query.toLowerCase()))} + onClose={() => { + setPalette(false); + setQuery(''); + }} + onQueryChange={setQuery} + query={query} + /> + )} +
+
diff --git a/src/components/AppShell.tsx b/src/components/AppShell.tsx index d0680d8..0a0cd6e 100644 --- a/src/components/AppShell.tsx +++ b/src/components/AppShell.tsx @@ -21,6 +21,7 @@ export function AppShell({ children, footer, nav, + sidebar, tabs, }: { actions?: ReactNode; @@ -29,6 +30,7 @@ export function AppShell({ children: ReactNode; footer?: ReactNode; nav: ShellNavItem[]; + sidebar?: ReactNode; tabs: ShellTab[]; }) { const Link = useLink(); @@ -62,9 +64,18 @@ export function AppShell({ {alert} -
- {children} -
+ {sidebar ? ( +
+ +
+ {children} +
+
+ ) : ( +
+ {children} +
+ )} {footer}