@cueloop/client 0.1.0-alpha.9 → 0.1.0-alpha.91
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/native/build-pty.sh +51 -0
- package/native/build.sh +82 -0
- package/native/darwin-arm64/libcuelooppty.dylib +0 -0
- package/native/darwin-arm64/libcueloopvt.dylib +0 -0
- package/native/src/pty.zig +193 -0
- package/native/src/shim.zig +114 -0
- package/package.json +42 -34
- package/src/App.appearance.test.tsx +53 -0
- package/src/App.autoclose.test.tsx +77 -51
- package/src/App.diff.test.tsx +259 -45
- package/src/App.export.test.tsx +58 -41
- package/src/App.focus.test.ts +89 -0
- package/src/App.identity.test.tsx +78 -0
- package/src/App.plan-render.test.tsx +209 -0
- package/src/App.plan-review.test.tsx +330 -161
- package/src/App.prototype-markdown.test.tsx +128 -0
- package/src/App.prototype.test.tsx +176 -0
- package/src/App.readonly.test.tsx +129 -67
- package/src/App.regression.test.tsx +94 -0
- package/src/App.rendering.test.tsx +61 -35
- package/src/App.share-dialog.test.tsx +172 -0
- package/src/App.submit-confirm.test.tsx +264 -0
- package/src/App.test.tsx +402 -126
- package/src/App.toast.test.tsx +98 -0
- package/src/App.tsx +1406 -1060
- package/src/App.visual.test.tsx +57 -0
- package/src/App.walk.test.tsx +299 -0
- package/src/App.workbench.test.tsx +787 -0
- package/src/__snapshots__/view-plan.regression.test.ts.snap +100 -0
- package/src/annotation-palette.ts +62 -0
- package/src/ansi-screen.test.ts +28 -0
- package/src/ansi-screen.ts +72 -0
- package/src/app-key-state.ts +61 -0
- package/src/app-screens.tsx +577 -0
- package/src/app-view-model.ts +168 -0
- package/src/attribution.test.ts +31 -0
- package/src/attribution.ts +42 -0
- package/src/client-extension-registry.test.tsx +61 -0
- package/src/client-extension-registry.ts +159 -0
- package/src/clipboard.test.ts +23 -0
- package/src/clipboard.ts +33 -0
- package/src/components/AnnotatableFileView.stories.tsx +42 -0
- package/src/components/AnnotatableFileView.test.tsx +102 -0
- package/src/components/AnnotatableFileView.tsx +134 -0
- package/src/components/AnnotationCards.stories.tsx +86 -0
- package/src/components/AnnotationCards.tsx +341 -0
- package/src/components/AnsiScreen.stories.tsx +12 -0
- package/src/components/AnsiScreen.tsx +28 -0
- package/src/components/AppHeader.stories.tsx +50 -0
- package/src/components/AppHeader.tsx +83 -0
- package/src/components/AppShell.brand.test.tsx +58 -0
- package/src/components/AppShell.extensions.test.tsx +191 -0
- package/src/components/AppShell.stories.tsx +89 -0
- package/src/components/AppShell.tsx +362 -0
- package/src/components/AppShell.zoom.test.tsx +135 -0
- package/src/components/BareWorkbenchFileView.stories.tsx +27 -0
- package/src/components/BareWorkbenchFileView.tsx +64 -0
- package/src/components/Breadcrumb.stories.tsx +19 -0
- package/src/components/Breadcrumb.tsx +45 -0
- package/src/components/ChangesColumn.stories.tsx +44 -0
- package/src/components/ChangesColumn.tsx +199 -0
- package/src/components/ChangesFileTree.test.tsx +61 -0
- package/src/components/CodeBlock.stories.tsx +42 -0
- package/src/components/CodeBlock.tsx +84 -0
- package/src/components/CompletionOverlay.stories.tsx +37 -0
- package/src/components/CompletionOverlay.tsx +77 -0
- package/src/components/ConfirmCard.stories.tsx +37 -0
- package/src/components/ConfirmCard.tsx +150 -0
- package/src/components/ConfirmDialog.stories.tsx +21 -0
- package/src/components/ConfirmDialog.tsx +72 -0
- package/src/components/DiffContentView.scroll.test.tsx +149 -0
- package/src/components/DiffContentView.split.test.tsx +140 -0
- package/src/components/DiffContentView.stories.tsx +80 -0
- package/src/components/DiffContentView.syntax.test.tsx +82 -0
- package/src/components/DiffContentView.tsx +906 -0
- package/src/components/DiscussionMarkerRail.stories.tsx +54 -0
- package/src/components/DiscussionMarkerRail.tsx +186 -0
- package/src/components/EditorGrid.stories.tsx +59 -0
- package/src/components/EditorGrid.tabs.test.tsx +118 -0
- package/src/components/EditorGrid.tsx +426 -0
- package/src/components/EditorGrid.zoom.test.tsx +68 -0
- package/src/components/ExtensionZone.stories.tsx +28 -0
- package/src/components/ExtensionZone.tsx +145 -0
- package/src/components/FileContentsView.stories.tsx +16 -0
- package/src/components/FileContentsView.test.tsx +29 -0
- package/src/components/FileContentsView.tsx +98 -0
- package/src/components/GridTabContent.stories.tsx +59 -0
- package/src/components/GridTabContent.tsx +182 -0
- package/src/components/HandleChipsInput.stories.tsx +24 -0
- package/src/components/HandleChipsInput.test.tsx +35 -0
- package/src/components/HandleChipsInput.tsx +145 -0
- package/src/components/KeybindsDialog.stories.tsx +31 -0
- package/src/components/KeybindsDialog.tsx +103 -0
- package/src/components/MarkdownGridBlock.stories.tsx +33 -0
- package/src/components/MarkdownGridBlock.tsx +72 -0
- package/src/components/MarkdownThreadEditor.stories.tsx +15 -0
- package/src/components/MarkdownThreadEditor.test.tsx +98 -0
- package/src/components/MarkdownThreadEditor.tsx +162 -0
- package/src/components/MenuBar.stories.tsx +28 -0
- package/src/components/MenuBar.tsx +44 -0
- package/src/components/NavModeHint.stories.tsx +40 -0
- package/src/components/NavModeHint.tsx +31 -0
- package/src/components/OverlayScrollbar.stories.tsx +33 -0
- package/src/components/OverlayScrollbar.test.tsx +139 -0
- package/src/components/OverlayScrollbar.tsx +129 -0
- package/src/components/PaneGrid.stories.tsx +84 -0
- package/src/components/PaneGrid.tsx +128 -0
- package/src/components/PanelColumn.stories.tsx +25 -0
- package/src/components/PanelColumn.tsx +98 -0
- package/src/components/ProjectTreeView.stories.tsx +25 -0
- package/src/components/ProjectTreeView.test.tsx +75 -0
- package/src/components/ProjectTreeView.tsx +125 -0
- package/src/components/PromptDialog.stories.tsx +36 -0
- package/src/components/PromptDialog.test.tsx +61 -0
- package/src/components/PromptDialog.tsx +99 -0
- package/src/components/PrototypeContentView.stories.tsx +19 -0
- package/src/components/PrototypeContentView.tsx +446 -0
- package/src/components/RootOverlay.stories.tsx +45 -0
- package/src/components/RootOverlay.tsx +48 -0
- package/src/components/ScrollArea.stories.tsx +20 -0
- package/src/components/ScrollArea.tsx +44 -0
- package/src/components/SettingsDialog.stories.tsx +117 -0
- package/src/components/SettingsDialog.tsx +186 -0
- package/src/components/SettingsRows.stories.tsx +18 -0
- package/src/components/SettingsRows.tsx +112 -0
- package/src/components/ShareDialog.stories.tsx +121 -0
- package/src/components/ShareDialog.tsx +552 -0
- package/src/components/ShellHeader.stories.tsx +50 -0
- package/src/components/ShellHeader.tsx +137 -0
- package/src/components/SlashComposer.stories.tsx +37 -0
- package/src/components/SlashComposer.tsx +87 -0
- package/src/components/ThreadFooter.stories.tsx +30 -0
- package/src/components/ThreadFooter.test.tsx +47 -0
- package/src/components/ThreadFooter.tsx +76 -0
- package/src/components/ThreadTree.stories.tsx +46 -0
- package/src/components/ThreadTree.tsx +255 -0
- package/src/components/ThreadView.selection.test.tsx +951 -0
- package/src/components/ThreadView.slash.test.tsx +146 -0
- package/src/components/ThreadView.stories.tsx +26 -0
- package/src/components/ThreadView.tsx +525 -0
- package/src/components/Toast.stories.tsx +19 -0
- package/src/components/Toast.tsx +52 -0
- package/src/components/Tooltip.stories.tsx +26 -0
- package/src/components/Tooltip.tsx +72 -0
- package/src/components/TreePane.stories.tsx +106 -0
- package/src/components/TreePane.tsx +157 -0
- package/src/components/WalkWizard.stories.tsx +100 -0
- package/src/components/WalkWizard.tsx +150 -0
- package/src/components/WelcomePlayground.stories.tsx +12 -0
- package/src/components/WelcomePlayground.test.tsx +49 -0
- package/src/components/WelcomePlayground.tsx +139 -0
- package/src/components/WorkspacePanels.stories.tsx +36 -0
- package/src/components/WorkspacePanels.tsx +116 -0
- package/src/components/__snapshots__/stories.test.tsx.snap +2264 -0
- package/src/components/diff-content-view-layout.test.ts +198 -0
- package/src/components/diff-content-view-layout.ts +84 -0
- package/src/components/editor-grid.test.ts +86 -0
- package/src/components/editor-grid.ts +138 -0
- package/src/components/file-tree.test.ts +60 -0
- package/src/components/file-tree.ts +112 -0
- package/src/components/menu-control.stories.tsx +35 -0
- package/src/components/menu-control.tsx +43 -0
- package/src/components/pages/Workspace.stories.tsx +181 -0
- package/src/components/primitives/Button.stories.tsx +32 -0
- package/src/components/primitives/Button.tsx +59 -0
- package/src/components/primitives/Card.stories.tsx +25 -0
- package/src/components/primitives/Card.tsx +63 -0
- package/src/components/primitives/Dialog.stories.tsx +24 -0
- package/src/components/primitives/Dialog.test.tsx +37 -0
- package/src/components/primitives/Dialog.tsx +71 -0
- package/src/components/primitives/DialogActions.stories.tsx +20 -0
- package/src/components/primitives/DialogActions.tsx +39 -0
- package/src/components/primitives/IconButton.stories.tsx +36 -0
- package/src/components/primitives/IconButton.tsx +62 -0
- package/src/components/primitives/StatusBar.stories.tsx +11 -0
- package/src/components/primitives/StatusBar.tsx +23 -0
- package/src/components/primitives/Switch.stories.tsx +17 -0
- package/src/components/primitives/Switch.tsx +32 -0
- package/src/components/primitives/Tabs.stories.tsx +29 -0
- package/src/components/primitives/Tabs.tsx +102 -0
- package/src/components/primitives/Toolbar.stories.tsx +17 -0
- package/src/components/primitives/Toolbar.tsx +14 -0
- package/src/components/primitives/Tree.stories.tsx +124 -0
- package/src/components/primitives/Tree.tsx +100 -0
- package/src/components/primitives/frame.test.ts +12 -0
- package/src/components/primitives/frame.ts +17 -0
- package/src/components/primitives/icons.ts +61 -0
- package/src/components/primitives/tree-model.test.ts +99 -0
- package/src/components/primitives/tree-model.ts +137 -0
- package/src/components/quick-actions-editor.stories.tsx +58 -0
- package/src/components/quick-actions-editor.test.tsx +117 -0
- package/src/components/quick-actions-editor.tsx +226 -0
- package/src/components/share-dialog-store.ts +58 -0
- package/src/components/stories-app.tsx +193 -0
- package/src/components/stories.test.tsx +74 -0
- package/src/components/story-fixtures.ts +102 -0
- package/src/components/story-tree.test.ts +108 -0
- package/src/components/story.ts +159 -0
- package/src/components/syntax-highlight.ts +133 -0
- package/src/components/terminal-pane.test.tsx +40 -0
- package/src/components/terminal-pane.ts +199 -0
- package/src/components/theme-context.stories.tsx +46 -0
- package/src/components/theme-context.tsx +37 -0
- package/src/components/thread-tree.test.ts +134 -0
- package/src/components/thread-tree.ts +109 -0
- package/src/components/truncate-text.ts +7 -0
- package/src/components/truncate-title.test.ts +31 -0
- package/src/components/truncate-title.ts +15 -0
- package/src/config-identity.test.ts +78 -0
- package/src/config.test.ts +579 -34
- package/src/config.ts +533 -57
- package/src/diff-fold.test.ts +78 -0
- package/src/diff-fold.ts +126 -0
- package/src/diff-hunk-curate.test.ts +315 -0
- package/src/diff-hunk-curate.ts +153 -0
- package/src/diff-intraline.test.ts +238 -0
- package/src/diff-intraline.ts +236 -0
- package/src/diff-syntax.test.ts +112 -0
- package/src/diff-syntax.ts +179 -0
- package/src/discussions.ts +79 -0
- package/src/editor.test.ts +149 -0
- package/src/editor.ts +139 -9
- package/src/ghostty-terminal.test.ts +85 -0
- package/src/ghostty-terminal.ts +199 -0
- package/src/github-identity.test.ts +58 -0
- package/src/github-identity.ts +41 -0
- package/src/herdr.test.ts +9 -34
- package/src/herdr.ts +25 -21
- package/src/index.ts +29 -0
- package/src/integrations.test.ts +41 -0
- package/src/integrations.ts +32 -0
- package/src/intent-dispatch.test.ts +724 -0
- package/src/intent-dispatch.ts +690 -0
- package/src/key-bindings.test.ts +156 -0
- package/src/key-bindings.ts +459 -0
- package/src/keymap.test.ts +456 -115
- package/src/keymap.ts +315 -81
- package/src/kitty-image.test.ts +66 -0
- package/src/kitty-image.ts +138 -0
- package/src/launch-layout.test.ts +78 -0
- package/src/launch-layout.ts +62 -0
- package/src/mark-runs.ts +121 -0
- package/src/markdown-editor-syntax-style.ts +50 -0
- package/src/markdown-grid.test.ts +101 -0
- package/src/markdown-grid.ts +203 -0
- package/src/markdown-highlight.test.ts +61 -0
- package/src/markdown-highlight.ts +122 -0
- package/src/pasted-image.test.ts +67 -0
- package/src/pasted-image.ts +60 -0
- package/src/perf/otlp-trace.test.ts +116 -0
- package/src/perf/otlp-trace.ts +130 -0
- package/src/perf/perf-report.ts +17 -0
- package/src/perf/perf-timings.test.ts +18 -0
- package/src/perf/perf-timings.ts +38 -0
- package/src/prototype-browser.test.ts +81 -0
- package/src/prototype-browser.ts +329 -0
- package/src/prototype-pixels.tsx +35 -0
- package/src/pty.test.ts +81 -0
- package/src/pty.ts +245 -0
- package/src/ready-signal.test.ts +85 -0
- package/src/ready-signal.ts +52 -0
- package/src/run.ts +85 -12
- package/src/serve.test.ts +24 -12
- package/src/serve.ts +52 -10
- package/src/share.ts +237 -0
- package/src/skills.test.ts +74 -0
- package/src/skills.ts +75 -0
- package/src/slash-palette.test.ts +103 -0
- package/src/slash-palette.ts +129 -0
- package/src/split-diff.test.ts +110 -0
- package/src/split-diff.ts +149 -0
- package/src/test-support.test.ts +264 -0
- package/src/test-support.ts +369 -0
- package/src/theme-presets.test.ts +51 -0
- package/src/theme-presets.ts +219 -0
- package/src/theme.ts +88 -19
- package/src/thread-chords.test.ts +79 -0
- package/src/thread-chords.ts +163 -0
- package/src/thread-code-syntax.ts +45 -0
- package/src/thread-controller.curate.test.ts +476 -0
- package/src/thread-controller.derivation.test.ts +62 -0
- package/src/thread-controller.live-diff.test.ts +322 -0
- package/src/thread-controller.optimistic.test.ts +69 -0
- package/src/thread-controller.share.test.ts +561 -0
- package/src/thread-controller.switch-cache.test.ts +176 -0
- package/src/thread-controller.tree.test.ts +261 -0
- package/src/thread-controller.ts +2050 -0
- package/src/thread-selection.test.ts +210 -0
- package/src/thread-selection.ts +243 -0
- package/src/tree-view.test.ts +128 -0
- package/src/tree-view.ts +153 -0
- package/src/use-annotation-surface.tsx +975 -0
- package/src/use-changes-workbench.ts +190 -0
- package/src/use-frame-measure.ts +41 -0
- package/src/use-remember-layout.ts +31 -0
- package/src/use-repo-changes.ts +87 -0
- package/src/use-settings-dialog.tsx +364 -0
- package/src/use-terminal-virtualizer.ts +259 -0
- package/src/use-thread-body-editing.tsx +58 -0
- package/src/version.ts +5 -0
- package/src/view-diff.test.ts +279 -9
- package/src/view-diff.ts +460 -24
- package/src/view-plan.regression.test.ts +60 -0
- package/src/view-plan.test.ts +545 -0
- package/src/view-plan.ts +1019 -0
- package/src/visual/contrast.test.ts +35 -0
- package/src/visual/contrast.ts +33 -0
- package/src/walk.test.ts +121 -0
- package/src/walk.ts +74 -0
- package/src/workbench-snapshot.test.ts +99 -0
- package/src/workbench-snapshot.ts +59 -0
- package/src/session-controller.ts +0 -398
- package/src/syntax.test.ts +0 -41
- package/src/syntax.ts +0 -88
- package/src/view.test.ts +0 -157
- package/src/view.ts +0 -364
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DARK } from "../theme";
|
|
3
|
+
import type { Story, StoryMeta } from "./story";
|
|
4
|
+
import { MenuControlProvider, useMenuControl } from "./menu-control";
|
|
5
|
+
|
|
6
|
+
export const meta: StoryMeta = { title: "Layout/MenuControl" };
|
|
7
|
+
|
|
8
|
+
// One open menu at a time: with "alpha" open, only its row reads as open.
|
|
9
|
+
function MenuControlDemo(): React.ReactNode {
|
|
10
|
+
const { openMenuId } = useMenuControl();
|
|
11
|
+
const row = (id: string): React.ReactNode => (
|
|
12
|
+
<text fg={openMenuId === id ? DARK.accent : DARK.textDim}>
|
|
13
|
+
{`${id}: ${openMenuId === id ? "open" : "closed"}`}
|
|
14
|
+
</text>
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<box style={{ flexDirection: "column" }}>
|
|
19
|
+
{row("alpha")}
|
|
20
|
+
{row("beta")}
|
|
21
|
+
</box>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const OneOpenAtATime: Story = {
|
|
26
|
+
render: () => (
|
|
27
|
+
<MenuControlProvider value={{ openMenuId: "alpha", toggleMenu: () => {}, closeMenu: () => {} }}>
|
|
28
|
+
<box style={{ padding: 1, backgroundColor: DARK.panel }}>
|
|
29
|
+
<MenuControlDemo />
|
|
30
|
+
</box>
|
|
31
|
+
</MenuControlProvider>
|
|
32
|
+
),
|
|
33
|
+
expectedColors: [DARK.accent],
|
|
34
|
+
size: { width: 30, height: 5 },
|
|
35
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// One open menu at a time across a shell: the thread row menu and the editor split menu share this
|
|
2
|
+
// single open id, so opening either closes the other. Owned by App and by the no-thread shell, one each.
|
|
3
|
+
|
|
4
|
+
import React, { createContext, useContext, useMemo, useState } from "react";
|
|
5
|
+
|
|
6
|
+
export interface MenuControlApi {
|
|
7
|
+
openMenuId: string | null;
|
|
8
|
+
toggleMenu: (id: string) => void;
|
|
9
|
+
closeMenu: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const MenuControlContext = createContext<MenuControlApi>({
|
|
13
|
+
openMenuId: null,
|
|
14
|
+
toggleMenu: () => {},
|
|
15
|
+
closeMenu: () => {},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export function useMenuControl(): MenuControlApi {
|
|
19
|
+
return useContext(MenuControlContext);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function useMenuControlState(): MenuControlApi {
|
|
23
|
+
const [openMenuId, setOpenMenuId] = useState<string | null>(null);
|
|
24
|
+
|
|
25
|
+
return useMemo(
|
|
26
|
+
() => ({
|
|
27
|
+
openMenuId,
|
|
28
|
+
toggleMenu: (id: string) => setOpenMenuId((current) => (current === id ? null : id)),
|
|
29
|
+
closeMenu: () => setOpenMenuId(null),
|
|
30
|
+
}),
|
|
31
|
+
[openMenuId],
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function MenuControlProvider({
|
|
36
|
+
value,
|
|
37
|
+
children,
|
|
38
|
+
}: {
|
|
39
|
+
value: MenuControlApi;
|
|
40
|
+
children: React.ReactNode;
|
|
41
|
+
}): React.ReactNode {
|
|
42
|
+
return <MenuControlContext.Provider value={value}>{children}</MenuControlContext.Provider>;
|
|
43
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DARK } from "../../theme";
|
|
3
|
+
import type { Story, StoryMeta } from "../story";
|
|
4
|
+
import { PaneGrid } from "../PaneGrid";
|
|
5
|
+
import { Tree } from "../primitives/Tree";
|
|
6
|
+
import type { TreeNode } from "../primitives/tree-model";
|
|
7
|
+
|
|
8
|
+
export const meta: StoryMeta = { title: "Pages/Workspace" };
|
|
9
|
+
|
|
10
|
+
const NAVIGATION: TreeNode[] = [
|
|
11
|
+
{
|
|
12
|
+
id: "projects",
|
|
13
|
+
label: "Projects",
|
|
14
|
+
children: [
|
|
15
|
+
{
|
|
16
|
+
id: "cueloop",
|
|
17
|
+
label: "cueloop",
|
|
18
|
+
children: [
|
|
19
|
+
{ id: "cueloop/review", label: "Review the app shell" },
|
|
20
|
+
{ id: "cueloop/read", label: "Read Cueloop Repository" },
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "threads",
|
|
27
|
+
label: "Threads",
|
|
28
|
+
children: [{ id: "threads/welcome", label: "Welcome to cueloop" }],
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const CHANGED_FILES: TreeNode[] = [
|
|
33
|
+
{
|
|
34
|
+
id: "packages",
|
|
35
|
+
label: "packages",
|
|
36
|
+
children: [
|
|
37
|
+
{
|
|
38
|
+
id: "client",
|
|
39
|
+
label: "client",
|
|
40
|
+
children: [
|
|
41
|
+
{ id: "client/AppShell.tsx", label: "AppShell.tsx", status: "modified" },
|
|
42
|
+
{ id: "client/Workspace.stories.tsx", label: "Workspace.stories.tsx", status: "added" },
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
{ id: "README.md", label: "README.md", status: "modified" },
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
interface CodeSampleLine {
|
|
51
|
+
number: number;
|
|
52
|
+
code: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const CODE_SAMPLE: readonly CodeSampleLine[] = [
|
|
56
|
+
{ number: 17, code: "export function AppShell({" },
|
|
57
|
+
{ number: 18, code: " header," },
|
|
58
|
+
{ number: 19, code: " sidebar," },
|
|
59
|
+
{ number: 20, code: " main," },
|
|
60
|
+
{ number: 21, code: " inspector," },
|
|
61
|
+
{ number: 22, code: "}: AppShellProps) {" },
|
|
62
|
+
{ number: 23, code: " const tokens = theme ?? DARK;" },
|
|
63
|
+
{ number: 24, code: " return <box>{main}</box>;" },
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
function Sidebar(): React.ReactNode {
|
|
67
|
+
return (
|
|
68
|
+
<box style={{ flexDirection: "column", paddingTop: 1 }}>
|
|
69
|
+
<Tree
|
|
70
|
+
nodes={NAVIGATION}
|
|
71
|
+
expandedIds={new Set(["projects", "cueloop", "threads"])}
|
|
72
|
+
selectedId="cueloop/review"
|
|
73
|
+
/>
|
|
74
|
+
</box>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function TranscriptLine({ children }: { children: React.ReactNode }): React.ReactNode {
|
|
79
|
+
return <text fg={DARK.textMuted}>{children}</text>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function CommentRow(): React.ReactNode {
|
|
83
|
+
return (
|
|
84
|
+
<box
|
|
85
|
+
style={{
|
|
86
|
+
border: ["left"],
|
|
87
|
+
borderStyle: "single",
|
|
88
|
+
borderColor: DARK.accent,
|
|
89
|
+
flexDirection: "column",
|
|
90
|
+
paddingLeft: 1,
|
|
91
|
+
marginTop: 1,
|
|
92
|
+
marginBottom: 1,
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
<text fg={DARK.textDim}>markus commented on AppShell.tsx</text>
|
|
96
|
+
<text fg={DARK.text}>The single rules read cleaner than an enclosing frame here.</text>
|
|
97
|
+
</box>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function Main(): React.ReactNode {
|
|
102
|
+
return (
|
|
103
|
+
<box style={{ flexDirection: "column", paddingLeft: 1, paddingTop: 1 }}>
|
|
104
|
+
<text fg={DARK.text}>Review the app shell</text>
|
|
105
|
+
<text> </text>
|
|
106
|
+
<TranscriptLine>
|
|
107
|
+
The three-pane layout divides regions with straight rules only.
|
|
108
|
+
</TranscriptLine>
|
|
109
|
+
<TranscriptLine>
|
|
110
|
+
Sidebar navigates, the transcript reads, the inspector shows code.
|
|
111
|
+
</TranscriptLine>
|
|
112
|
+
<CommentRow />
|
|
113
|
+
<TranscriptLine>Header and footer are thin label rows the shell rules itself.</TranscriptLine>
|
|
114
|
+
</box>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function CodePane(): React.ReactNode {
|
|
119
|
+
return (
|
|
120
|
+
<box style={{ flexDirection: "column" }}>
|
|
121
|
+
<text fg={DARK.textMuted}>AppShell.tsx</text>
|
|
122
|
+
<text> </text>
|
|
123
|
+
{CODE_SAMPLE.map((line) => (
|
|
124
|
+
<box key={line.number} style={{ flexDirection: "row" }}>
|
|
125
|
+
<text fg={DARK.textDim}>{String(line.number).padStart(3, " ")} </text>
|
|
126
|
+
<text fg={DARK.text}>{line.code}</text>
|
|
127
|
+
</box>
|
|
128
|
+
))}
|
|
129
|
+
</box>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function Inspector(): React.ReactNode {
|
|
134
|
+
return (
|
|
135
|
+
<box style={{ flexDirection: "column", paddingLeft: 1, paddingTop: 1 }}>
|
|
136
|
+
<CodePane />
|
|
137
|
+
<text> </text>
|
|
138
|
+
<text fg={DARK.textDim}>Changed files</text>
|
|
139
|
+
<Tree
|
|
140
|
+
nodes={CHANGED_FILES}
|
|
141
|
+
expandedIds={new Set(["packages", "client"])}
|
|
142
|
+
selectedId="client/AppShell.tsx"
|
|
143
|
+
showStatus
|
|
144
|
+
/>
|
|
145
|
+
</box>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function Header(): React.ReactNode {
|
|
150
|
+
return (
|
|
151
|
+
<box style={{ flexDirection: "row", paddingLeft: 1 }}>
|
|
152
|
+
<text fg={DARK.textMuted}>Review the app shell</text>
|
|
153
|
+
<box style={{ flexGrow: 1 }} />
|
|
154
|
+
<text fg={DARK.textDim}>cueloop / main</text>
|
|
155
|
+
</box>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function Footer(): React.ReactNode {
|
|
160
|
+
return (
|
|
161
|
+
<box style={{ flexDirection: "row", paddingLeft: 1 }}>
|
|
162
|
+
<text fg={DARK.textDim}>2 files changed</text>
|
|
163
|
+
<box style={{ flexGrow: 1 }} />
|
|
164
|
+
<text fg={DARK.textDim}>Fable 5</text>
|
|
165
|
+
</box>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export const ThreePane: Story = {
|
|
170
|
+
render: () => (
|
|
171
|
+
<PaneGrid
|
|
172
|
+
sidebar={<Sidebar />}
|
|
173
|
+
main={<Main />}
|
|
174
|
+
mainHeader={<Header />}
|
|
175
|
+
inspector={<Inspector />}
|
|
176
|
+
footer={<Footer />}
|
|
177
|
+
/>
|
|
178
|
+
),
|
|
179
|
+
expectedColors: [DARK.border, DARK.accent],
|
|
180
|
+
size: { width: 160, height: 42 },
|
|
181
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DARK } from "../../theme";
|
|
3
|
+
import type { Story, StoryMeta } from "../story";
|
|
4
|
+
import { Button } from "./Button";
|
|
5
|
+
import { Toolbar } from "./Toolbar";
|
|
6
|
+
|
|
7
|
+
export const meta: StoryMeta = { title: "Primitives/Button" };
|
|
8
|
+
|
|
9
|
+
export const Variants: Story = {
|
|
10
|
+
render: () => (
|
|
11
|
+
<Toolbar>
|
|
12
|
+
<Button variant="solid" marginRight={2} onPress={() => {}}>
|
|
13
|
+
{" Save "}
|
|
14
|
+
</Button>
|
|
15
|
+
<Button marginRight={2} onPress={() => {}}>
|
|
16
|
+
{" Cancel esc "}
|
|
17
|
+
</Button>
|
|
18
|
+
<Button variant="accent-text" onPress={() => {}}>
|
|
19
|
+
{"Submit review"}
|
|
20
|
+
</Button>
|
|
21
|
+
</Toolbar>
|
|
22
|
+
),
|
|
23
|
+
expectedColors: [DARK.accent, DARK.accentInk, DARK.textDim],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const Disabled: Story = {
|
|
27
|
+
render: () => (
|
|
28
|
+
<Button isDisabled onPress={() => {}}>
|
|
29
|
+
{" Edit "}
|
|
30
|
+
</Button>
|
|
31
|
+
),
|
|
32
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Word-button: a pressable label. `onPress` unifies mouse and (future)
|
|
3
|
+
* keyboard activation; `isDisabled` swallows presses without changing the
|
|
4
|
+
* rendered label, so read-only affordances keep their place in the layout.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from "react";
|
|
8
|
+
import type { MouseEvent } from "@opentui/core";
|
|
9
|
+
import type { Theme } from "../../theme";
|
|
10
|
+
import { useComponentTheme } from "../theme-context";
|
|
11
|
+
|
|
12
|
+
export interface ButtonProps {
|
|
13
|
+
/** Unified activation: mouse release today, keyboard activation tomorrow. */
|
|
14
|
+
onPress: () => void;
|
|
15
|
+
isDisabled?: boolean;
|
|
16
|
+
/** solid = accent-filled call to action; plain = quiet word-button. */
|
|
17
|
+
variant?: "solid" | "plain" | "accent-text";
|
|
18
|
+
foreground?: string;
|
|
19
|
+
marginRight?: number;
|
|
20
|
+
theme?: Theme;
|
|
21
|
+
children: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function Button({
|
|
25
|
+
onPress,
|
|
26
|
+
isDisabled = false,
|
|
27
|
+
variant = "plain",
|
|
28
|
+
foreground: foregroundOverride,
|
|
29
|
+
marginRight,
|
|
30
|
+
theme,
|
|
31
|
+
children,
|
|
32
|
+
}: ButtonProps): React.ReactNode {
|
|
33
|
+
const tokens = useComponentTheme(theme);
|
|
34
|
+
// a disabled solid button reads as muted, so it never looks like a live call to action
|
|
35
|
+
const backgroundColor =
|
|
36
|
+
variant === "solid" ? (isDisabled ? tokens.border : tokens.accent) : undefined;
|
|
37
|
+
const foreground =
|
|
38
|
+
foregroundOverride ??
|
|
39
|
+
(variant === "solid"
|
|
40
|
+
? isDisabled
|
|
41
|
+
? tokens.textMuted
|
|
42
|
+
: tokens.accentInk
|
|
43
|
+
: variant === "accent-text"
|
|
44
|
+
? tokens.accent
|
|
45
|
+
: tokens.textDim);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<box
|
|
49
|
+
style={{ backgroundColor, marginRight }}
|
|
50
|
+
onMouseUp={(event: MouseEvent) => {
|
|
51
|
+
// Consume the press so it never bubbles to an ancestor's onMouseUp (e.g. a card).
|
|
52
|
+
event.stopPropagation();
|
|
53
|
+
if (!isDisabled) onPress();
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
<text fg={foreground}>{children}</text>
|
|
57
|
+
</box>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DARK } from "../../theme";
|
|
3
|
+
import type { Story, StoryMeta } from "../story";
|
|
4
|
+
import { Card } from "./Card";
|
|
5
|
+
|
|
6
|
+
export const meta: StoryMeta = { title: "Primitives/Card" };
|
|
7
|
+
|
|
8
|
+
export const Titled: Story = {
|
|
9
|
+
render: () => (
|
|
10
|
+
<Card title=" a titled card " contentRows={2}>
|
|
11
|
+
<text>first content row</text>
|
|
12
|
+
<text>second content row</text>
|
|
13
|
+
</Card>
|
|
14
|
+
),
|
|
15
|
+
expectedColors: [DARK.border, DARK.elevated],
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const AccentBorder: Story = {
|
|
19
|
+
render: () => (
|
|
20
|
+
<Card title=" accented " contentRows={1} borderColor={DARK.accent}>
|
|
21
|
+
<text>content</text>
|
|
22
|
+
</Card>
|
|
23
|
+
),
|
|
24
|
+
expectedColors: [DARK.accent],
|
|
25
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bordered box with an optional title. The card owns the
|
|
3
|
+
* height-derivation rule: its height is always the declared content row
|
|
4
|
+
* count plus the two border rows, so layout math and the mounted box can
|
|
5
|
+
* never drift apart - border-collapse bugs die here.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from "react";
|
|
9
|
+
import type { Theme } from "../../theme";
|
|
10
|
+
import { useComponentTheme } from "../theme-context";
|
|
11
|
+
import { FRAME_BORDER_STYLE, frameTitle } from "./frame";
|
|
12
|
+
|
|
13
|
+
export interface CardProps {
|
|
14
|
+
title?: string;
|
|
15
|
+
/** Number of content rows inside the border; the box adds the two border rows. */
|
|
16
|
+
contentRows: number;
|
|
17
|
+
width?: number;
|
|
18
|
+
borderColor?: string;
|
|
19
|
+
backgroundColor?: string;
|
|
20
|
+
marginLeft?: number;
|
|
21
|
+
marginRight?: number;
|
|
22
|
+
theme?: Theme;
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** The one height rule: content rows plus the top and bottom border rows. */
|
|
27
|
+
export function cardHeight(contentRows: number): number {
|
|
28
|
+
return contentRows + 2;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function Card({
|
|
32
|
+
title,
|
|
33
|
+
contentRows,
|
|
34
|
+
width,
|
|
35
|
+
borderColor,
|
|
36
|
+
backgroundColor,
|
|
37
|
+
marginLeft,
|
|
38
|
+
marginRight,
|
|
39
|
+
theme,
|
|
40
|
+
children,
|
|
41
|
+
}: CardProps): React.ReactNode {
|
|
42
|
+
const tokens = useComponentTheme(theme);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<box
|
|
46
|
+
style={{
|
|
47
|
+
height: cardHeight(contentRows),
|
|
48
|
+
width,
|
|
49
|
+
marginLeft,
|
|
50
|
+
marginRight,
|
|
51
|
+
border: true,
|
|
52
|
+
borderStyle: FRAME_BORDER_STYLE,
|
|
53
|
+
borderColor: borderColor ?? tokens.border,
|
|
54
|
+
backgroundColor: backgroundColor ?? tokens.elevated,
|
|
55
|
+
flexDirection: "column",
|
|
56
|
+
paddingLeft: 1,
|
|
57
|
+
}}
|
|
58
|
+
title={frameTitle(title)}
|
|
59
|
+
>
|
|
60
|
+
{children}
|
|
61
|
+
</box>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DARK } from "../../theme";
|
|
3
|
+
import type { Story, StoryMeta } from "../story";
|
|
4
|
+
import { Dialog } from "./Dialog";
|
|
5
|
+
|
|
6
|
+
export const meta: StoryMeta = { title: "Primitives/Dialog" };
|
|
7
|
+
|
|
8
|
+
export const Open: Story = {
|
|
9
|
+
render: () => (
|
|
10
|
+
<Dialog isOpen title=" a modal " width={40} height={8}>
|
|
11
|
+
<text>dialog body content</text>
|
|
12
|
+
</Dialog>
|
|
13
|
+
),
|
|
14
|
+
expectedColors: [DARK.accent],
|
|
15
|
+
size: { width: 60, height: 16 },
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const Closed: Story = {
|
|
19
|
+
render: () => (
|
|
20
|
+
<Dialog isOpen={false} title=" hidden " width={40} height={8}>
|
|
21
|
+
<text>never rendered</text>
|
|
22
|
+
</Dialog>
|
|
23
|
+
),
|
|
24
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** The dialog dismisses on a backdrop click (outside the panel) but never on a click inside it. */
|
|
2
|
+
|
|
3
|
+
import { test, expect } from "bun:test";
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { testRender } from "@opentui/react/test-utils";
|
|
6
|
+
import { Dialog } from "./Dialog";
|
|
7
|
+
import { settle } from "../../test-support";
|
|
8
|
+
import { DARK } from "../../theme";
|
|
9
|
+
|
|
10
|
+
test("clicking the backdrop dismisses; clicking inside the panel does not", async () => {
|
|
11
|
+
let dismissed = 0;
|
|
12
|
+
const setup = await testRender(
|
|
13
|
+
<Dialog
|
|
14
|
+
isOpen
|
|
15
|
+
title=" test "
|
|
16
|
+
width={20}
|
|
17
|
+
height={5}
|
|
18
|
+
background={DARK.elevated}
|
|
19
|
+
onDismiss={() => (dismissed += 1)}
|
|
20
|
+
theme={DARK}
|
|
21
|
+
>
|
|
22
|
+
<text>body</text>
|
|
23
|
+
</Dialog>,
|
|
24
|
+
{ width: 60, height: 20 },
|
|
25
|
+
);
|
|
26
|
+
await settle(setup);
|
|
27
|
+
|
|
28
|
+
// the top-left corner is backdrop, outside the centered panel
|
|
29
|
+
await setup.mockMouse.click(0, 0);
|
|
30
|
+
expect(dismissed).toBe(1);
|
|
31
|
+
|
|
32
|
+
// the screen centre lands inside the panel, so it must not dismiss
|
|
33
|
+
await setup.mockMouse.click(30, 10);
|
|
34
|
+
expect(dismissed).toBe(1);
|
|
35
|
+
|
|
36
|
+
setup.renderer.destroy();
|
|
37
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centered modal: a full-screen layer (backdrop token, transparent by
|
|
3
|
+
* default) centers a bordered, unpainted panel over the session - the frame
|
|
4
|
+
* delineates the dialog, the content behind stays visible.
|
|
5
|
+
* `isOpen` keeps the mount decision with the caller-visible prop vocabulary.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from "react";
|
|
9
|
+
import type { Theme } from "../../theme";
|
|
10
|
+
import { useComponentTheme } from "../theme-context";
|
|
11
|
+
import { FRAME_BORDER_STYLE, frameTitle } from "./frame";
|
|
12
|
+
|
|
13
|
+
export interface DialogProps {
|
|
14
|
+
isOpen: boolean;
|
|
15
|
+
title?: string;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
/** Solid panel fill for content-heavy dialogs; default transparent. */
|
|
19
|
+
background?: string;
|
|
20
|
+
/** Dismiss on a click outside the panel. */
|
|
21
|
+
onDismiss?: () => void;
|
|
22
|
+
theme?: Theme;
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function Dialog({
|
|
27
|
+
isOpen,
|
|
28
|
+
title,
|
|
29
|
+
width,
|
|
30
|
+
height,
|
|
31
|
+
background,
|
|
32
|
+
onDismiss,
|
|
33
|
+
theme,
|
|
34
|
+
children,
|
|
35
|
+
}: DialogProps): React.ReactNode {
|
|
36
|
+
const tokens = useComponentTheme(theme);
|
|
37
|
+
|
|
38
|
+
if (!isOpen) return null;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<box
|
|
42
|
+
onMouseUp={onDismiss}
|
|
43
|
+
style={{
|
|
44
|
+
position: "absolute",
|
|
45
|
+
left: 0,
|
|
46
|
+
top: 0,
|
|
47
|
+
width: "100%",
|
|
48
|
+
height: "100%",
|
|
49
|
+
backgroundColor: tokens.backdrop,
|
|
50
|
+
alignItems: "center",
|
|
51
|
+
justifyContent: "center",
|
|
52
|
+
}}
|
|
53
|
+
>
|
|
54
|
+
<box
|
|
55
|
+
onMouseUp={(event) => event.stopPropagation()}
|
|
56
|
+
style={{
|
|
57
|
+
width,
|
|
58
|
+
height,
|
|
59
|
+
border: true,
|
|
60
|
+
borderStyle: FRAME_BORDER_STYLE,
|
|
61
|
+
borderColor: tokens.accent,
|
|
62
|
+
backgroundColor: background,
|
|
63
|
+
flexDirection: "column",
|
|
64
|
+
}}
|
|
65
|
+
title={frameTitle(title)}
|
|
66
|
+
>
|
|
67
|
+
{children}
|
|
68
|
+
</box>
|
|
69
|
+
</box>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DARK } from "../../theme";
|
|
3
|
+
import type { Story, StoryMeta } from "../story";
|
|
4
|
+
import { DialogActions } from "./DialogActions";
|
|
5
|
+
|
|
6
|
+
export const meta: StoryMeta = { title: "Primitives/DialogActions" };
|
|
7
|
+
|
|
8
|
+
const noop = () => {};
|
|
9
|
+
|
|
10
|
+
export const SaveCancel: Story = {
|
|
11
|
+
render: () => <DialogActions confirmLabel="save" onConfirm={noop} onCancel={noop} />,
|
|
12
|
+
expectedColors: [DARK.accent],
|
|
13
|
+
size: { width: 40, height: 3 },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const DeleteCancel: Story = {
|
|
17
|
+
render: () => <DialogActions confirmLabel="delete" onConfirm={noop} onCancel={noop} />,
|
|
18
|
+
expectedColors: [DARK.accent],
|
|
19
|
+
size: { width: 40, height: 3 },
|
|
20
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** The one action row every overlay uses: a solid confirm word-button and a plain cancel, lowercased. */
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import type { Theme } from "../../theme";
|
|
5
|
+
import { Toolbar } from "./Toolbar";
|
|
6
|
+
import { Button } from "./Button";
|
|
7
|
+
|
|
8
|
+
export interface DialogActionsProps {
|
|
9
|
+
confirmLabel: string;
|
|
10
|
+
onConfirm: () => void;
|
|
11
|
+
onCancel: () => void;
|
|
12
|
+
cancelLabel?: string;
|
|
13
|
+
confirmDisabled?: boolean;
|
|
14
|
+
theme?: Theme;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function DialogActions({
|
|
18
|
+
confirmLabel,
|
|
19
|
+
onConfirm,
|
|
20
|
+
onCancel,
|
|
21
|
+
cancelLabel = "cancel",
|
|
22
|
+
confirmDisabled = false,
|
|
23
|
+
theme,
|
|
24
|
+
}: DialogActionsProps): React.ReactNode {
|
|
25
|
+
return (
|
|
26
|
+
<Toolbar>
|
|
27
|
+
<Button
|
|
28
|
+
variant="solid"
|
|
29
|
+
marginRight={2}
|
|
30
|
+
onPress={onConfirm}
|
|
31
|
+
isDisabled={confirmDisabled}
|
|
32
|
+
theme={theme}
|
|
33
|
+
>
|
|
34
|
+
{` ${confirmLabel} `}
|
|
35
|
+
</Button>
|
|
36
|
+
<Button onPress={onCancel} theme={theme}>{` ${cancelLabel} `}</Button>
|
|
37
|
+
</Toolbar>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DARK } from "../../theme";
|
|
3
|
+
import type { Story, StoryMeta } from "../story";
|
|
4
|
+
import { IconButton } from "./IconButton";
|
|
5
|
+
import { NERD } from "./icons";
|
|
6
|
+
|
|
7
|
+
export const meta: StoryMeta = { title: "Primitives/IconButton" };
|
|
8
|
+
|
|
9
|
+
function Row({ children }: { children: React.ReactNode }): React.ReactNode {
|
|
10
|
+
return (
|
|
11
|
+
<box style={{ flexDirection: "row", padding: 1, backgroundColor: DARK.panel }}>{children}</box>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const States: Story = {
|
|
16
|
+
render: () => (
|
|
17
|
+
<Row>
|
|
18
|
+
<IconButton glyph={NERD.settings} onPress={() => {}} marginRight={3} />
|
|
19
|
+
<IconButton glyph={NERD.listTree} active onPress={() => {}} marginRight={3} />
|
|
20
|
+
<IconButton glyph={NERD.diff} disabled marginRight={3} />
|
|
21
|
+
<IconButton glyph={NERD.submit} onPress={() => {}} />
|
|
22
|
+
</Row>
|
|
23
|
+
),
|
|
24
|
+
expectedColors: [DARK.accent, DARK.textMuted, DARK.textDim],
|
|
25
|
+
size: { width: 40, height: 3 },
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const Submit: Story = {
|
|
29
|
+
render: () => (
|
|
30
|
+
<Row>
|
|
31
|
+
<IconButton glyph={NERD.submit} active onPress={() => {}} />
|
|
32
|
+
</Row>
|
|
33
|
+
),
|
|
34
|
+
expectedColors: [DARK.accent],
|
|
35
|
+
size: { width: 20, height: 3 },
|
|
36
|
+
};
|