@fungi.computer/caps 0.0.0-stage → 0.0.1
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/LICENSE +21 -0
- package/README.md +1125 -2
- package/dist/blocks/app-bar.d.ts +43 -0
- package/dist/blocks/app-bar.js +244 -0
- package/dist/blocks/app-status-bar.d.ts +93 -0
- package/dist/blocks/app-status-bar.js +42 -0
- package/dist/blocks/async-state.d.ts +21 -0
- package/dist/blocks/async-state.js +8 -0
- package/dist/blocks/card-grid.d.ts +11 -0
- package/dist/blocks/card-grid.js +21 -0
- package/dist/blocks/command-menu.d.ts +26 -0
- package/dist/blocks/command-menu.js +70 -0
- package/dist/blocks/composer.d.ts +23 -0
- package/dist/blocks/composer.js +13 -0
- package/dist/blocks/empty-state.d.ts +59 -0
- package/dist/blocks/empty-state.js +35 -0
- package/dist/blocks/faq-chat.d.ts +62 -0
- package/dist/blocks/faq-chat.js +77 -0
- package/dist/blocks/game-share-card.d.ts +17 -0
- package/dist/blocks/game-share-card.js +9 -0
- package/dist/blocks/launcher-grid.d.ts +50 -0
- package/dist/blocks/launcher-grid.js +11 -0
- package/dist/blocks/listing-detail.d.ts +23 -0
- package/dist/blocks/listing-detail.js +12 -0
- package/dist/blocks/loading-state.d.ts +5 -0
- package/dist/blocks/loading-state.js +8 -0
- package/dist/blocks/mosaic.d.ts +29 -0
- package/dist/blocks/mosaic.js +19 -0
- package/dist/blocks/permission-grid.d.ts +45 -0
- package/dist/blocks/permission-grid.js +14 -0
- package/dist/blocks/pricing-card.d.ts +53 -0
- package/dist/blocks/pricing-card.js +12 -0
- package/dist/blocks/release-tag.d.ts +20 -0
- package/dist/blocks/release-tag.js +10 -0
- package/dist/blocks/resource-select.d.ts +22 -0
- package/dist/blocks/resource-select.js +16 -0
- package/dist/blocks/result-state.d.ts +15 -0
- package/dist/blocks/result-state.js +8 -0
- package/dist/blocks/section-card.d.ts +31 -0
- package/dist/blocks/section-card.js +7 -0
- package/dist/blocks/section-heading.d.ts +13 -0
- package/dist/blocks/section-heading.js +11 -0
- package/dist/blocks/spec-sheet.d.ts +125 -0
- package/dist/blocks/spec-sheet.js +47 -0
- package/dist/blocks/status-tray.d.ts +83 -0
- package/dist/blocks/status-tray.js +79 -0
- package/dist/blocks/upload-queue.d.ts +48 -0
- package/dist/blocks/upload-queue.js +24 -0
- package/dist/components/action-row.d.ts +16 -0
- package/dist/components/action-row.js +11 -0
- package/dist/components/activity-line.d.ts +33 -0
- package/dist/components/activity-line.js +69 -0
- package/dist/components/alert.d.ts +17 -0
- package/dist/components/alert.js +17 -0
- package/dist/components/badge.d.ts +19 -0
- package/dist/components/badge.js +24 -0
- package/dist/components/button.d.ts +55 -0
- package/dist/components/button.js +230 -0
- package/dist/components/card.d.ts +61 -0
- package/dist/components/card.js +48 -0
- package/dist/components/chat-bubble.d.ts +34 -0
- package/dist/components/chat-bubble.js +10 -0
- package/dist/components/checkbox-bead.d.ts +6 -0
- package/dist/components/checkbox-bead.js +67 -0
- package/dist/components/checkbox.d.ts +21 -0
- package/dist/components/checkbox.js +46 -0
- package/dist/components/code-input.d.ts +35 -0
- package/dist/components/code-input.js +126 -0
- package/dist/components/combobox.d.ts +86 -0
- package/dist/components/combobox.js +218 -0
- package/dist/components/confirm-action.d.ts +23 -0
- package/dist/components/confirm-action.js +64 -0
- package/dist/components/copy-field.d.ts +53 -0
- package/dist/components/copy-field.js +49 -0
- package/dist/components/count-badge.d.ts +15 -0
- package/dist/components/count-badge.js +9 -0
- package/dist/components/cover-pattern.d.ts +19 -0
- package/dist/components/cover-pattern.js +74 -0
- package/dist/components/data-list.d.ts +25 -0
- package/dist/components/data-list.js +16 -0
- package/dist/components/dialog.d.ts +39 -0
- package/dist/components/dialog.js +73 -0
- package/dist/components/diff-view.d.ts +14 -0
- package/dist/components/diff-view.js +13 -0
- package/dist/components/disclosure.d.ts +29 -0
- package/dist/components/disclosure.js +19 -0
- package/dist/components/dropdown-menu.d.ts +49 -0
- package/dist/components/dropdown-menu.js +98 -0
- package/dist/components/field-guide.d.ts +41 -0
- package/dist/components/field-guide.js +18 -0
- package/dist/components/field.d.ts +164 -0
- package/dist/components/field.js +228 -0
- package/dist/components/game-feedback.d.ts +84 -0
- package/dist/components/game-feedback.js +48 -0
- package/dist/components/game-hud.d.ts +126 -0
- package/dist/components/game-hud.js +136 -0
- package/dist/components/generation-frame.d.ts +36 -0
- package/dist/components/generation-frame.js +97 -0
- package/dist/components/gooey.d.ts +32 -0
- package/dist/components/gooey.js +25 -0
- package/dist/components/hold-button.d.ts +26 -0
- package/dist/components/hold-button.js +115 -0
- package/dist/components/icon-button.d.ts +24 -0
- package/dist/components/icon-button.js +18 -0
- package/dist/components/icon-checkbox.d.ts +20 -0
- package/dist/components/icon-checkbox.js +9 -0
- package/dist/components/icon-choice-mark.d.ts +6 -0
- package/dist/components/icon-choice-mark.js +5 -0
- package/dist/components/icon-lamp.d.ts +42 -0
- package/dist/components/icon-lamp.js +15 -0
- package/dist/components/icon-radio.d.ts +16 -0
- package/dist/components/icon-radio.js +9 -0
- package/dist/components/inline-code.d.ts +3 -0
- package/dist/components/inline-code.js +7 -0
- package/dist/components/input-group.d.ts +9 -0
- package/dist/components/input-group.js +15 -0
- package/dist/components/input.d.ts +15 -0
- package/dist/components/input.js +15 -0
- package/dist/components/item-grid.d.ts +110 -0
- package/dist/components/item-grid.js +136 -0
- package/dist/components/keycap.d.ts +80 -0
- package/dist/components/keycap.js +18 -0
- package/dist/components/label.d.ts +13 -0
- package/dist/components/label.js +7 -0
- package/dist/components/listbox.d.ts +50 -0
- package/dist/components/listbox.js +134 -0
- package/dist/components/loader-blobs.d.ts +5 -0
- package/dist/components/loader-blobs.js +128 -0
- package/dist/components/loader.d.ts +21 -0
- package/dist/components/loader.js +25 -0
- package/dist/components/markdown.d.ts +23 -0
- package/dist/components/markdown.js +70 -0
- package/dist/components/minimap-labels.d.ts +39 -0
- package/dist/components/minimap-labels.js +113 -0
- package/dist/components/monogram.d.ts +32 -0
- package/dist/components/monogram.js +24 -0
- package/dist/components/motion-visibility.d.ts +2 -0
- package/dist/components/motion-visibility.js +25 -0
- package/dist/components/navigation-rail.d.ts +11 -0
- package/dist/components/navigation-rail.js +55 -0
- package/dist/components/overlay-root.d.ts +16 -0
- package/dist/components/overlay-root.js +24 -0
- package/dist/components/pixel-number.d.ts +50 -0
- package/dist/components/pixel-number.js +113 -0
- package/dist/components/popover.d.ts +31 -0
- package/dist/components/popover.js +24 -0
- package/dist/components/presence.d.ts +59 -0
- package/dist/components/presence.js +232 -0
- package/dist/components/price-tag.d.ts +11 -0
- package/dist/components/price-tag.js +6 -0
- package/dist/components/progress.d.ts +25 -0
- package/dist/components/progress.js +24 -0
- package/dist/components/prose.d.ts +14 -0
- package/dist/components/prose.js +10 -0
- package/dist/components/quota-meter.d.ts +24 -0
- package/dist/components/quota-meter.js +11 -0
- package/dist/components/radio-bead.d.ts +4 -0
- package/dist/components/radio-bead.js +53 -0
- package/dist/components/radio-group.d.ts +54 -0
- package/dist/components/radio-group.js +64 -0
- package/dist/components/resize-handle.d.ts +23 -0
- package/dist/components/resize-handle.js +31 -0
- package/dist/components/rough-drawing.d.ts +42 -0
- package/dist/components/rough-drawing.js +231 -0
- package/dist/components/rough.d.ts +68 -0
- package/dist/components/rough.js +33 -0
- package/dist/components/scroll-area.d.ts +12 -0
- package/dist/components/scroll-area.js +96 -0
- package/dist/components/scroll-motion.d.ts +12 -0
- package/dist/components/scroll-motion.js +31 -0
- package/dist/components/scroll-to-bottom-blob-state.d.ts +40 -0
- package/dist/components/scroll-to-bottom-blob-state.js +109 -0
- package/dist/components/scroll-to-bottom-blob.d.ts +16 -0
- package/dist/components/scroll-to-bottom-blob.js +174 -0
- package/dist/components/select.d.ts +9 -0
- package/dist/components/select.js +12 -0
- package/dist/components/selectable.d.ts +13 -0
- package/dist/components/selectable.js +45 -0
- package/dist/components/send-action.d.ts +33 -0
- package/dist/components/send-action.js +96 -0
- package/dist/components/separator.d.ts +11 -0
- package/dist/components/separator.js +7 -0
- package/dist/components/skeleton.d.ts +5 -0
- package/dist/components/skeleton.js +7 -0
- package/dist/components/slider.d.ts +18 -0
- package/dist/components/slider.js +31 -0
- package/dist/components/stat.d.ts +17 -0
- package/dist/components/stat.js +7 -0
- package/dist/components/status-bead.d.ts +9 -0
- package/dist/components/status-bead.js +107 -0
- package/dist/components/status-mark.d.ts +12 -0
- package/dist/components/status-mark.js +177 -0
- package/dist/components/status.d.ts +38 -0
- package/dist/components/status.js +28 -0
- package/dist/components/store-card.d.ts +21 -0
- package/dist/components/store-card.js +10 -0
- package/dist/components/surface.d.ts +89 -0
- package/dist/components/surface.js +31 -0
- package/dist/components/switch-bead.d.ts +4 -0
- package/dist/components/switch-bead.js +62 -0
- package/dist/components/switch.d.ts +11 -0
- package/dist/components/switch.js +36 -0
- package/dist/components/tabs.d.ts +55 -0
- package/dist/components/tabs.js +81 -0
- package/dist/components/technonomicon.d.ts +192 -0
- package/dist/components/technonomicon.js +121 -0
- package/dist/components/textarea.d.ts +9 -0
- package/dist/components/textarea.js +11 -0
- package/dist/components/toaster.d.ts +4 -0
- package/dist/components/toaster.js +13 -0
- package/dist/components/tool-call-chip.d.ts +22 -0
- package/dist/components/tool-call-chip.js +100 -0
- package/dist/components/tooltip.d.ts +41 -0
- package/dist/components/tooltip.js +169 -0
- package/dist/components/trading-card.d.ts +60 -0
- package/dist/components/trading-card.js +13 -0
- package/dist/components/undo-action.d.ts +52 -0
- package/dist/components/undo-action.js +100 -0
- package/dist/components/undo-toast.d.ts +21 -0
- package/dist/components/undo-toast.js +58 -0
- package/dist/components/visually-hidden.d.ts +9 -0
- package/dist/components/visually-hidden.js +11 -0
- package/dist/components/window.d.ts +65 -0
- package/dist/components/window.js +31 -0
- package/dist/components/wizard.d.ts +17 -0
- package/dist/components/wizard.js +19 -0
- package/dist/enhance/cigarette-card.d.ts +8 -0
- package/dist/enhance/cigarette-card.js +33 -0
- package/dist/enhance/sketch.d.ts +16 -0
- package/dist/enhance/sketch.js +130 -0
- package/dist/form-error.d.ts +7 -0
- package/dist/form-error.js +14 -0
- package/dist/form-fields.d.ts +15 -0
- package/dist/form-fields.js +250 -0
- package/dist/form-schema.d.ts +166 -0
- package/dist/form-schema.js +398 -0
- package/dist/form.d.ts +30 -0
- package/dist/form.js +20 -0
- package/dist/lib/cn.d.ts +8 -0
- package/dist/lib/cn.js +12 -0
- package/dist/lib/format-money.d.ts +2 -0
- package/dist/lib/format-money.js +22 -0
- package/dist/lib/glide.d.ts +12 -0
- package/dist/lib/glide.js +143 -0
- package/dist/lib/ime.d.ts +3 -0
- package/dist/lib/ime.js +8 -0
- package/dist/lib/motion-spring.d.ts +27 -0
- package/dist/lib/motion-spring.js +36 -0
- package/dist/lib/motion.d.ts +51 -0
- package/dist/lib/motion.js +161 -0
- package/dist/lib/reorder-motion.d.ts +13 -0
- package/dist/lib/reorder-motion.js +201 -0
- package/dist/lib/seed-hash.d.ts +6 -0
- package/dist/lib/seed-hash.js +13 -0
- package/dist/lib/tanstack-highlight.d.ts +4 -0
- package/dist/lib/tanstack-highlight.js +41 -0
- package/dist/schema-form.d.ts +120 -0
- package/dist/schema-form.js +129 -0
- package/dist/styles.css +18814 -0
- package/docs/api/README.md +134 -0
- package/docs/api/blocks/app-bar.md +227 -0
- package/docs/api/blocks/app-status-bar.md +257 -0
- package/docs/api/blocks/async-state.md +53 -0
- package/docs/api/blocks/card-grid.md +31 -0
- package/docs/api/blocks/command-menu.md +89 -0
- package/docs/api/blocks/composer.md +40 -0
- package/docs/api/blocks/empty-state.md +97 -0
- package/docs/api/blocks/faq-chat.md +147 -0
- package/docs/api/blocks/game-share-card.md +40 -0
- package/docs/api/blocks/launcher-grid.md +86 -0
- package/docs/api/blocks/listing-detail.md +51 -0
- package/docs/api/blocks/loading-state.md +21 -0
- package/docs/api/blocks/mosaic.md +94 -0
- package/docs/api/blocks/permission-grid.md +79 -0
- package/docs/api/blocks/pricing-card.md +90 -0
- package/docs/api/blocks/release-tag.md +62 -0
- package/docs/api/blocks/resource-select.md +126 -0
- package/docs/api/blocks/result-state.md +42 -0
- package/docs/api/blocks/section-card.md +42 -0
- package/docs/api/blocks/section-heading.md +61 -0
- package/docs/api/blocks/spec-sheet.md +400 -0
- package/docs/api/blocks/status-tray.md +141 -0
- package/docs/api/blocks/upload-queue.md +110 -0
- package/docs/api/components/action-row.md +51 -0
- package/docs/api/components/activity-line.md +103 -0
- package/docs/api/components/alert.md +60 -0
- package/docs/api/components/badge.md +77 -0
- package/docs/api/components/button.md +94 -0
- package/docs/api/components/card.md +165 -0
- package/docs/api/components/chat-bubble.md +66 -0
- package/docs/api/components/checkbox-bead.md +20 -0
- package/docs/api/components/checkbox.md +42 -0
- package/docs/api/components/code-input.md +61 -0
- package/docs/api/components/combobox.md +303 -0
- package/docs/api/components/confirm-action.md +58 -0
- package/docs/api/components/copy-field.md +106 -0
- package/docs/api/components/count-badge.md +39 -0
- package/docs/api/components/cover-pattern.md +43 -0
- package/docs/api/components/data-list.md +80 -0
- package/docs/api/components/dialog.md +235 -0
- package/docs/api/components/diff-view.md +59 -0
- package/docs/api/components/disclosure.md +71 -0
- package/docs/api/components/dropdown-menu.md +269 -0
- package/docs/api/components/field-guide.md +174 -0
- package/docs/api/components/field.md +252 -0
- package/docs/api/components/game-feedback.md +157 -0
- package/docs/api/components/game-hud.md +359 -0
- package/docs/api/components/generation-frame.md +59 -0
- package/docs/api/components/hold-button.md +44 -0
- package/docs/api/components/icon-button.md +56 -0
- package/docs/api/components/icon-checkbox.md +42 -0
- package/docs/api/components/icon-choice-mark.md +31 -0
- package/docs/api/components/icon-lamp.md +92 -0
- package/docs/api/components/icon-radio.md +42 -0
- package/docs/api/components/inline-code.md +21 -0
- package/docs/api/components/input-group.md +53 -0
- package/docs/api/components/input.md +54 -0
- package/docs/api/components/item-grid.md +262 -0
- package/docs/api/components/keycap.md +102 -0
- package/docs/api/components/label.md +40 -0
- package/docs/api/components/listbox.md +131 -0
- package/docs/api/components/loader-blobs.md +9 -0
- package/docs/api/components/loader.md +51 -0
- package/docs/api/components/markdown.md +91 -0
- package/docs/api/components/minimap-labels.md +84 -0
- package/docs/api/components/monogram.md +96 -0
- package/docs/api/components/motion-visibility.md +9 -0
- package/docs/api/components/navigation-rail.md +37 -0
- package/docs/api/components/overlay-root.md +92 -0
- package/docs/api/components/pixel-number.md +120 -0
- package/docs/api/components/popover.md +80 -0
- package/docs/api/components/presence.md +106 -0
- package/docs/api/components/price-tag.md +48 -0
- package/docs/api/components/progress.md +90 -0
- package/docs/api/components/prose.md +48 -0
- package/docs/api/components/quota-meter.md +47 -0
- package/docs/api/components/radio-bead.md +9 -0
- package/docs/api/components/radio-group.md +126 -0
- package/docs/api/components/resize-handle.md +46 -0
- package/docs/api/components/rough-drawing.md +122 -0
- package/docs/api/components/rough.md +66 -0
- package/docs/api/components/scroll-area.md +22 -0
- package/docs/api/components/scroll-motion.md +9 -0
- package/docs/api/components/scroll-to-bottom-blob-state.md +162 -0
- package/docs/api/components/scroll-to-bottom-blob.md +73 -0
- package/docs/api/components/select.md +39 -0
- package/docs/api/components/send-action.md +58 -0
- package/docs/api/components/separator.md +38 -0
- package/docs/api/components/skeleton.md +31 -0
- package/docs/api/components/slider.md +70 -0
- package/docs/api/components/stat.md +42 -0
- package/docs/api/components/status-bead.md +19 -0
- package/docs/api/components/status-mark.md +19 -0
- package/docs/api/components/status.md +105 -0
- package/docs/api/components/store-card.md +53 -0
- package/docs/api/components/surface.md +161 -0
- package/docs/api/components/switch-bead.md +9 -0
- package/docs/api/components/switch.md +40 -0
- package/docs/api/components/tabs.md +138 -0
- package/docs/api/components/technonomicon.md +773 -0
- package/docs/api/components/textarea.md +40 -0
- package/docs/api/components/toaster.md +29 -0
- package/docs/api/components/tool-call-chip.md +68 -0
- package/docs/api/components/tooltip.md +113 -0
- package/docs/api/components/trading-card.md +95 -0
- package/docs/api/components/undo-action.md +117 -0
- package/docs/api/components/undo-toast.md +49 -0
- package/docs/api/components/visually-hidden.md +37 -0
- package/docs/api/components/window.md +291 -0
- package/docs/api/components/wizard.md +65 -0
- package/docs/api/enhance/cigarette-card.md +52 -0
- package/docs/api/enhance/sketch.md +42 -0
- package/docs/api/form.md +1623 -0
- package/docs/api/lib/cn.md +46 -0
- package/docs/api/lib/format-money.md +30 -0
- package/docs/api/lib/glide.md +36 -0
- package/docs/api/lib/ime.md +29 -0
- package/docs/api/lib/motion-spring.md +91 -0
- package/docs/api/lib/motion.md +212 -0
- package/docs/api/lib/reorder-motion.md +46 -0
- package/docs/api/lib/seed-hash.md +31 -0
- package/docs/api/lib/tanstack-highlight.md +40 -0
- package/package.json +597 -3
- package/src/app-status-bar.css +158 -0
- package/src/fonts.css +1 -0
- package/src/status-tray.css +282 -0
- package/src/styles.css +10177 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/** A caller-owned running App window or available App launcher. */
|
|
3
|
+
export type AppBarItem = {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
icon?: ReactNode;
|
|
7
|
+
active?: boolean;
|
|
8
|
+
/** Request focusing a running window or opening an available App. */
|
|
9
|
+
onSelect: () => void;
|
|
10
|
+
/** Request closing; the caller owns disposal and removal. */
|
|
11
|
+
onClose?: () => void;
|
|
12
|
+
};
|
|
13
|
+
/** Caller-owned apps and actions; AppBar owns only presentation and disclosure. */
|
|
14
|
+
export type AppBarProps = {
|
|
15
|
+
/** Portal container for the App dial; defaults to the document body. */
|
|
16
|
+
container?: HTMLElement | null;
|
|
17
|
+
windows: readonly AppBarItem[];
|
|
18
|
+
/** Team Apps available to launch from the speed dial. */
|
|
19
|
+
launchers: readonly AppBarItem[];
|
|
20
|
+
/** Recent Sessions supplied by the shell's session owner. */
|
|
21
|
+
sessions?: readonly AppBarItem[];
|
|
22
|
+
/** Keep long launcher names readable instead of truncating them. */
|
|
23
|
+
launcherLabels?: "truncate" | "wrap";
|
|
24
|
+
alwaysExpanded?: boolean;
|
|
25
|
+
/** Control the compact App dial from the surrounding shell. */
|
|
26
|
+
isOpen?: boolean;
|
|
27
|
+
/** Report requests to open or dismiss the compact App dial. */
|
|
28
|
+
onOpenChange?: (open: boolean) => void;
|
|
29
|
+
emptyLabel?: ReactNode;
|
|
30
|
+
launcher: {
|
|
31
|
+
label: string;
|
|
32
|
+
icon?: ReactNode;
|
|
33
|
+
};
|
|
34
|
+
/** A primary action revealed above the launcher, such as starting a chat. */
|
|
35
|
+
primaryAction?: {
|
|
36
|
+
label: string;
|
|
37
|
+
icon: ReactNode;
|
|
38
|
+
/** Invoke the caller-owned primary action. */
|
|
39
|
+
onSelect: () => void;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
/** An anchored launcher between persistent running apps and a spring-out app tray. */
|
|
43
|
+
export declare function AppBar({ container, windows, launchers, sessions, launcherLabels, alwaysExpanded, isOpen, onOpenChange, emptyLabel, launcher, primaryAction, }: AppBarProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useOverlayRoot, useOverlayDismiss, } from "../components/overlay-root.js";
|
|
3
|
+
import { Popover as BasePopover } from "@base-ui/react/popover";
|
|
4
|
+
import { animate, stagger } from "animejs";
|
|
5
|
+
import { motionDefaults, readMotion, useReducedMotion } from "../lib/motion.js";
|
|
6
|
+
import { themedEase, themedSpring } from "../lib/motion-spring.js";
|
|
7
|
+
import { PanelsTopLeft, X } from "lucide-react";
|
|
8
|
+
import { useEffect, useId, useLayoutEffect, useRef, useState, } from "react";
|
|
9
|
+
function AppBarItemView({ item, available, expanded, onSelect, onClose, session = false, launcherLabels = "truncate", }) {
|
|
10
|
+
const icon = useRef(null);
|
|
11
|
+
const motion = useRef();
|
|
12
|
+
const reduced = useReducedMotion();
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (reduced && icon.current) {
|
|
15
|
+
motion.current?.cancel();
|
|
16
|
+
motion.current = animate(icon.current, { y: 0, scale: 1, duration: 0 });
|
|
17
|
+
}
|
|
18
|
+
}, [reduced]);
|
|
19
|
+
useEffect(() => () => {
|
|
20
|
+
motion.current?.cancel();
|
|
21
|
+
}, []);
|
|
22
|
+
const lift = (raised) => {
|
|
23
|
+
if (!icon.current)
|
|
24
|
+
return;
|
|
25
|
+
motion.current?.cancel();
|
|
26
|
+
const feel = readMotion(icon.current);
|
|
27
|
+
motion.current = animate(icon.current, {
|
|
28
|
+
y: raised && !reduced ? -feel.distance : 0,
|
|
29
|
+
scale: raised && !reduced ? 1.12 : 1,
|
|
30
|
+
...themedSpring(feel, { bounce: 0.4, duration: 320 }),
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
const tabIndex = !expanded ? -1 : 0;
|
|
34
|
+
return (_jsxs("div", { className: "caps-app-bar-item", "data-app-bar-item-id": item.id, "data-launcher-labels": available ? launcherLabels : undefined, "data-session": session ? "true" : undefined, children: [_jsxs("button", { type: "button", className: "caps-app-bar-button", "aria-label": item.label, title: item.label, "aria-pressed": available ? undefined : item.active === true, "data-active": item.active === true ? "true" : undefined, "data-running": available ? undefined : "true", tabIndex: tabIndex, onClick: onSelect, onPointerEnter: () => {
|
|
35
|
+
lift(true);
|
|
36
|
+
}, onPointerLeave: () => {
|
|
37
|
+
lift(false);
|
|
38
|
+
}, onFocus: () => {
|
|
39
|
+
lift(true);
|
|
40
|
+
}, onBlur: () => {
|
|
41
|
+
lift(false);
|
|
42
|
+
}, children: [_jsx("span", { ref: icon, className: "caps-app-bar-icon", "aria-hidden": "true", children: item.icon ?? _jsx(PanelsTopLeft, {}) }), available ? (_jsx("span", { className: "caps-app-bar-item-label", children: item.label })) : null] }), item.onClose ? (_jsx("button", { type: "button", className: "caps-app-bar-close", "aria-label": `Close ${item.label}`, tabIndex: tabIndex, onClick: onClose, children: _jsx(X, { "aria-hidden": "true" }) })) : null] }));
|
|
43
|
+
}
|
|
44
|
+
/** An anchored launcher between persistent running apps and a spring-out app tray. */
|
|
45
|
+
export function AppBar({ container, windows, launchers, sessions = [], launcherLabels = "truncate", alwaysExpanded = false, isOpen, onOpenChange, emptyLabel, launcher, primaryAction, }) {
|
|
46
|
+
const popoverRoot = useOverlayRoot("popover");
|
|
47
|
+
const portalContainer = container ?? popoverRoot;
|
|
48
|
+
const [internalOpen, setInternalOpen] = useState(false);
|
|
49
|
+
const [notchLeft, setNotchLeft] = useState(0);
|
|
50
|
+
const open = isOpen ?? internalOpen;
|
|
51
|
+
useOverlayDismiss(() => {
|
|
52
|
+
setOpen(false);
|
|
53
|
+
});
|
|
54
|
+
const setOpen = (next) => {
|
|
55
|
+
setInternalOpen(next);
|
|
56
|
+
onOpenChange?.(next);
|
|
57
|
+
};
|
|
58
|
+
const expanded = alwaysExpanded || open;
|
|
59
|
+
const root = useRef(null);
|
|
60
|
+
const runningRef = useRef(null);
|
|
61
|
+
const launcherRef = useRef(null);
|
|
62
|
+
const availableRef = useRef(null);
|
|
63
|
+
const actionRef = useRef(null);
|
|
64
|
+
const previous = useRef(expanded);
|
|
65
|
+
const previousAction = useRef(open);
|
|
66
|
+
const reduced = useReducedMotion();
|
|
67
|
+
const id = useId();
|
|
68
|
+
const availableId = `caps-apps-${id}`;
|
|
69
|
+
const actionId = `caps-app-action-${id}`;
|
|
70
|
+
const activeId = windows.find((item) => item.active)?.id;
|
|
71
|
+
const hasPrimaryAction = primaryAction !== undefined;
|
|
72
|
+
useLayoutEffect(() => {
|
|
73
|
+
const revealActive = () => {
|
|
74
|
+
const region = runningRef.current;
|
|
75
|
+
const selected = region?.querySelector('[data-active="true"]');
|
|
76
|
+
if (!region || !selected)
|
|
77
|
+
return;
|
|
78
|
+
const tray = region.getBoundingClientRect();
|
|
79
|
+
const item = selected.getBoundingClientRect();
|
|
80
|
+
region.scrollLeft +=
|
|
81
|
+
Math.max(0, item.right - tray.right) +
|
|
82
|
+
Math.min(0, item.left - tray.left);
|
|
83
|
+
region.scrollTop +=
|
|
84
|
+
Math.max(0, item.bottom - tray.bottom) +
|
|
85
|
+
Math.min(0, item.top - tray.top);
|
|
86
|
+
};
|
|
87
|
+
revealActive();
|
|
88
|
+
window.addEventListener("resize", revealActive);
|
|
89
|
+
return () => {
|
|
90
|
+
window.removeEventListener("resize", revealActive);
|
|
91
|
+
};
|
|
92
|
+
}, [activeId, windows.length]);
|
|
93
|
+
useLayoutEffect(() => {
|
|
94
|
+
const updateNotch = () => {
|
|
95
|
+
const nav = root.current;
|
|
96
|
+
const button = launcherRef.current;
|
|
97
|
+
if (nav && button) {
|
|
98
|
+
const navBounds = nav.getBoundingClientRect();
|
|
99
|
+
const buttonBounds = button.getBoundingClientRect();
|
|
100
|
+
setNotchLeft(buttonBounds.left - navBounds.left + buttonBounds.width / 2);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
updateNotch();
|
|
104
|
+
window.addEventListener("resize", updateNotch);
|
|
105
|
+
return () => {
|
|
106
|
+
window.removeEventListener("resize", updateNotch);
|
|
107
|
+
};
|
|
108
|
+
}, [windows.length]);
|
|
109
|
+
useLayoutEffect(() => {
|
|
110
|
+
const immediate = reduced || previous.current === expanded;
|
|
111
|
+
previous.current = expanded;
|
|
112
|
+
if (!availableRef.current)
|
|
113
|
+
return;
|
|
114
|
+
const feel = readMotion(availableRef.current);
|
|
115
|
+
const { duration, ease } = immediate
|
|
116
|
+
? { duration: reduced ? feel.fast : 0, ease: "linear" }
|
|
117
|
+
: expanded
|
|
118
|
+
? themedSpring(feel, { bounce: 0.4, duration: 420 })
|
|
119
|
+
: themedEase(feel, { ease: "inOutQuad", duration: 150 });
|
|
120
|
+
const motions = [];
|
|
121
|
+
if (immediate)
|
|
122
|
+
motions.push(animate(availableRef.current.querySelectorAll(".caps-app-bar-item"), {
|
|
123
|
+
y: 0,
|
|
124
|
+
scale: 1,
|
|
125
|
+
duration: 0,
|
|
126
|
+
}));
|
|
127
|
+
if (reduced)
|
|
128
|
+
motions.push(animate(availableRef.current, { x: 0, scale: 1, duration: 0 }));
|
|
129
|
+
motions.push(animate(availableRef.current, {
|
|
130
|
+
opacity: expanded ? 1 : 0,
|
|
131
|
+
x: reduced || expanded ? 0 : -feel.distance * 3,
|
|
132
|
+
scale: reduced || expanded ? 1 : 0.88,
|
|
133
|
+
duration,
|
|
134
|
+
ease,
|
|
135
|
+
}));
|
|
136
|
+
if (expanded && !immediate)
|
|
137
|
+
motions.push(animate(availableRef.current.querySelectorAll(".caps-app-bar-item"), {
|
|
138
|
+
y: [8, 0],
|
|
139
|
+
scale: [0.9, 1],
|
|
140
|
+
delay: stagger(feel.stagger * (28 / motionDefaults.stagger)),
|
|
141
|
+
...themedSpring(feel, { bounce: 0.35, duration: 360 }),
|
|
142
|
+
}));
|
|
143
|
+
return () => {
|
|
144
|
+
motions.forEach((motion) => {
|
|
145
|
+
motion.cancel();
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
}, [expanded, reduced]);
|
|
149
|
+
useLayoutEffect(() => {
|
|
150
|
+
const immediate = reduced || previousAction.current === open;
|
|
151
|
+
previousAction.current = open;
|
|
152
|
+
const target = actionRef.current ?? launcherRef.current;
|
|
153
|
+
if (!target)
|
|
154
|
+
return;
|
|
155
|
+
const feel = readMotion(target);
|
|
156
|
+
const settle = immediate
|
|
157
|
+
? { duration: reduced ? feel.fast : 0, ease: "linear" }
|
|
158
|
+
: themedSpring(feel, { bounce: 0.35, duration: 320 });
|
|
159
|
+
const motions = [];
|
|
160
|
+
if (actionRef.current && reduced)
|
|
161
|
+
motions.push(animate(actionRef.current, { y: 0, scale: 1, duration: 0 }));
|
|
162
|
+
if (actionRef.current)
|
|
163
|
+
motions.push(animate(actionRef.current, {
|
|
164
|
+
opacity: open ? 1 : 0,
|
|
165
|
+
y: reduced || open ? 0 : feel.distance * 3.5,
|
|
166
|
+
scale: reduced || open ? 1 : 0.88,
|
|
167
|
+
...settle,
|
|
168
|
+
}));
|
|
169
|
+
if (launcherRef.current && !immediate) {
|
|
170
|
+
const squash = themedEase(feel, { ease: "outQuad", duration: 65 });
|
|
171
|
+
const release = themedSpring(feel, { bounce: 0.45, duration: 360 });
|
|
172
|
+
motions.push(animate(launcherRef.current, {
|
|
173
|
+
scaleX: [
|
|
174
|
+
{ to: 1.07, duration: squash.duration },
|
|
175
|
+
{ to: 1, ...release },
|
|
176
|
+
],
|
|
177
|
+
scaleY: [
|
|
178
|
+
{ to: 0.91, duration: squash.duration },
|
|
179
|
+
{ to: 1, ...release },
|
|
180
|
+
],
|
|
181
|
+
y: [
|
|
182
|
+
{ to: 2, duration: squash.duration },
|
|
183
|
+
{ to: 0, ...release },
|
|
184
|
+
],
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
187
|
+
if (launcherRef.current && immediate)
|
|
188
|
+
motions.push(animate(launcherRef.current, {
|
|
189
|
+
scaleX: 1,
|
|
190
|
+
scaleY: 1,
|
|
191
|
+
y: 0,
|
|
192
|
+
duration: 0,
|
|
193
|
+
}));
|
|
194
|
+
return () => {
|
|
195
|
+
motions.forEach((motion) => {
|
|
196
|
+
motion.cancel();
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
}, [open, reduced, hasPrimaryAction]);
|
|
200
|
+
const close = () => {
|
|
201
|
+
setOpen(false);
|
|
202
|
+
launcherRef.current?.focus();
|
|
203
|
+
};
|
|
204
|
+
const select = (onSelect) => {
|
|
205
|
+
// Restore disclosure focus first, so the host may deliberately focus its app.
|
|
206
|
+
if (expanded)
|
|
207
|
+
close();
|
|
208
|
+
onSelect();
|
|
209
|
+
};
|
|
210
|
+
const renderItem = (item, available, session = false) => (_jsx(AppBarItemView, { item: item, available: available, expanded: available ? expanded : true, onSelect: () => {
|
|
211
|
+
select(item.onSelect);
|
|
212
|
+
}, onClose: () => {
|
|
213
|
+
launcherRef.current?.focus();
|
|
214
|
+
item.onClose?.();
|
|
215
|
+
}, launcherLabels: launcherLabels, session: session }, item.id));
|
|
216
|
+
const appBarStyle = {
|
|
217
|
+
"--caps-app-bar-notch-left": `${String(notchLeft)}px`,
|
|
218
|
+
};
|
|
219
|
+
const changeOpen = (next, details) => {
|
|
220
|
+
const target = details.event.target;
|
|
221
|
+
if (!next &&
|
|
222
|
+
details.reason === "outside-press" &&
|
|
223
|
+
target instanceof Node &&
|
|
224
|
+
(root.current?.contains(target) ||
|
|
225
|
+
availableRef.current?.contains(target) ||
|
|
226
|
+
actionRef.current?.contains(target))) {
|
|
227
|
+
details.cancel();
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (!next && details.reason === "escape-key")
|
|
231
|
+
close();
|
|
232
|
+
else
|
|
233
|
+
setOpen(next);
|
|
234
|
+
};
|
|
235
|
+
return (_jsx(BasePopover.Root, { open: expanded, onOpenChange: changeOpen, modal: false, children: _jsxs("nav", { ref: root, style: appBarStyle, className: "caps-app-bar", "aria-label": "Team Apps and Sessions launcher", "data-open": expanded ? "true" : "false", children: [_jsx("div", { ref: runningRef, className: "caps-app-bar-region caps-app-bar-items", role: "group", "aria-label": "Running apps", "aria-hidden": "false", children: windows.map((item) => renderItem(item, false)) }), _jsx("div", { className: "caps-app-bar-launcher", children: _jsx("button", { ref: launcherRef, type: "button", className: "caps-app-bar-launcher-button", "aria-label": launcher.label, title: launcher.label, "aria-expanded": expanded, "aria-controls": primaryAction ? actionId : availableId, onClick: () => {
|
|
236
|
+
setOpen(!open);
|
|
237
|
+
}, children: _jsx("span", { className: "caps-app-bar-mascot", "aria-hidden": "true", children: launcher.icon ?? _jsx(PanelsTopLeft, {}) }) }) }), portalContainer ? (_jsx(BasePopover.Portal, { container: portalContainer, keepMounted: true, children: _jsx(BasePopover.Positioner, { anchor: root, side: "bottom", align: "start", sideOffset: 4, collisionPadding: 8, collisionBoundary: portalContainer === document.body ? undefined : portalContainer, className: "caps-app-bar-positioner", children: _jsxs(BasePopover.Popup, { initialFocus: false, finalFocus: false, style: appBarStyle, ref: availableRef, id: availableId, className: "caps-app-bar-region caps-app-bar-launchers", role: "group", "aria-label": "Speed dial", "aria-hidden": !expanded, children: [launchers.length > 0 ? (_jsxs("div", { className: "caps-app-bar-section", role: "group", "aria-label": "Team Apps", children: [_jsx("span", { className: "caps-app-bar-section-label", children: "Team Apps" }), _jsx("div", { className: "caps-app-bar-app-list", children: launchers.map((item) => renderItem(item, true)) })] })) : null, sessions.length > 0 ? (_jsxs("div", { className: "caps-app-bar-section caps-app-bar-sessions", role: "group", "aria-label": "Recent Sessions", children: [_jsx("span", { className: "caps-app-bar-section-label", children: "Recent Sessions" }), _jsx("div", { className: "caps-app-bar-session-list", children: sessions.map((item) => renderItem(item, true, true)) })] })) : null, launchers.length === 0 &&
|
|
238
|
+
sessions.length === 0 &&
|
|
239
|
+
emptyLabel ? (_jsx("span", { className: "caps-app-bar-empty", role: "status", children: emptyLabel })) : null] }) }) })) : null, primaryAction && portalContainer ? (_jsx(BasePopover.Root, { open: open, onOpenChange: changeOpen, modal: false, children: _jsx(BasePopover.Portal, { container: portalContainer, keepMounted: true, children: _jsx(BasePopover.Positioner, { anchor: launcherRef, side: "bottom", align: "end", sideOffset: 8, collisionPadding: 8, collisionBoundary: portalContainer === document.body
|
|
240
|
+
? undefined
|
|
241
|
+
: portalContainer, className: "caps-app-bar-positioner", children: _jsx(BasePopover.Popup, { initialFocus: false, finalFocus: false, children: _jsxs("button", { ref: actionRef, id: actionId, type: "button", className: "caps-app-bar-primary", "aria-hidden": !open, tabIndex: open ? 0 : -1, onClick: () => {
|
|
242
|
+
select(primaryAction.onSelect);
|
|
243
|
+
}, children: [_jsx("span", { "aria-hidden": "true", children: primaryAction.icon }), primaryAction.label] }) }) }) }) })) : null] }) }));
|
|
244
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
/** Props accepted by AppStatusBar. */
|
|
3
|
+
export type AppStatusBarProps = HTMLAttributes<HTMLElement> & {
|
|
4
|
+
/**
|
|
5
|
+
* What this window is attached to: a StatusBarPicker, or a plain
|
|
6
|
+
* AppStatusItem when it cannot be changed. Every App keeps it first.
|
|
7
|
+
*/
|
|
8
|
+
attachment?: ReactNode;
|
|
9
|
+
/** Readouts held at the far end, such as save state. */
|
|
10
|
+
end?: ReactNode;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* One App window's status bar: its attachment first, then the App's own
|
|
14
|
+
* readouts, with an optional far-end readout. One bar per window.
|
|
15
|
+
*/
|
|
16
|
+
export declare function AppStatusBar({ attachment, end, children, className, ...props }: AppStatusBarProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
/** One quiet status readout. */
|
|
18
|
+
export declare function AppStatusItem({ className, ...props }: HTMLAttributes<HTMLSpanElement>): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
/** One choice a status bar picker offers. */
|
|
20
|
+
export type StatusBarPickerOption = Readonly<{
|
|
21
|
+
id: string;
|
|
22
|
+
label: string;
|
|
23
|
+
}>;
|
|
24
|
+
/** Props accepted by StatusBarPicker. */
|
|
25
|
+
export type StatusBarPickerProps = {
|
|
26
|
+
/** What is picked, e.g. "Computer"; the trigger is named "<noun>: <label>". */
|
|
27
|
+
noun: string;
|
|
28
|
+
/** The plural used for searching, e.g. "Computers". */
|
|
29
|
+
pluralNoun: string;
|
|
30
|
+
/** The picked thing's mark, shown before its label. */
|
|
31
|
+
icon: ReactNode;
|
|
32
|
+
options: readonly StatusBarPickerOption[];
|
|
33
|
+
/** The selected option id; `null` when nothing is attached. */
|
|
34
|
+
value: string | null;
|
|
35
|
+
/** Called with a different option's id. */
|
|
36
|
+
onSelect: (id: string) => void;
|
|
37
|
+
/** Shown when the list is empty, e.g. why nothing can be chosen. */
|
|
38
|
+
emptyMessage?: ReactNode;
|
|
39
|
+
/** The trigger's accessible name; defaults to "<noun>: <label>". */
|
|
40
|
+
accessibleLabel?: string;
|
|
41
|
+
disabled?: boolean;
|
|
42
|
+
/** Host-owned popup visibility; omitted for the ordinary trigger-owned picker. */
|
|
43
|
+
open?: boolean;
|
|
44
|
+
/** Reports popup dismissal or trigger activation to the host. */
|
|
45
|
+
onOpenChange?: (open: boolean) => void;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The status bar's attachment picker: a quiet label that opens a searchable
|
|
49
|
+
* list above the bar. Selection is a request; the caller owns the change.
|
|
50
|
+
*/
|
|
51
|
+
export declare function StatusBarPicker({ noun, pluralNoun, icon, options, value, onSelect, emptyMessage, accessibleLabel, disabled, open, onOpenChange, }: StatusBarPickerProps): import("react/jsx-runtime").JSX.Element;
|
|
52
|
+
/** Props accepted by StatusBarChip. */
|
|
53
|
+
export type StatusBarChipProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> & {
|
|
54
|
+
/** Visible name of the item, e.g. "Ports". */
|
|
55
|
+
label: ReactNode;
|
|
56
|
+
/** Decorative mark before the label; on a phone the chip shows it alone. */
|
|
57
|
+
icon?: ReactNode;
|
|
58
|
+
/** A readout after the label, such as a CountBadge. */
|
|
59
|
+
readout?: ReactNode;
|
|
60
|
+
/**
|
|
61
|
+
* Toggle state. `undefined` is an ordinary action; a boolean adds
|
|
62
|
+
* `aria-pressed` and fills the chip while on.
|
|
63
|
+
*/
|
|
64
|
+
pressed?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Disclosure state for a chip that opens a menu or panel. A boolean adds
|
|
67
|
+
* `aria-expanded` and fills the chip while open.
|
|
68
|
+
*/
|
|
69
|
+
expanded?: boolean;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* One App-declared status bar control (an action, a toggle or a menu
|
|
73
|
+
* trigger): a flat chip with the same quiet face as StatusBarPicker. It
|
|
74
|
+
* forwards its ref and props, so a menu can render it as its trigger.
|
|
75
|
+
*/
|
|
76
|
+
export declare const StatusBarChip: import("react").ForwardRefExoticComponent<Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> & {
|
|
77
|
+
/** Visible name of the item, e.g. "Ports". */
|
|
78
|
+
label: ReactNode;
|
|
79
|
+
/** Decorative mark before the label; on a phone the chip shows it alone. */
|
|
80
|
+
icon?: ReactNode;
|
|
81
|
+
/** A readout after the label, such as a CountBadge. */
|
|
82
|
+
readout?: ReactNode;
|
|
83
|
+
/**
|
|
84
|
+
* Toggle state. `undefined` is an ordinary action; a boolean adds
|
|
85
|
+
* `aria-pressed` and fills the chip while on.
|
|
86
|
+
*/
|
|
87
|
+
pressed?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Disclosure state for a chip that opens a menu or panel. A boolean adds
|
|
90
|
+
* `aria-expanded` and fills the chip while open.
|
|
91
|
+
*/
|
|
92
|
+
expanded?: boolean;
|
|
93
|
+
} & import("react").RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronsUpDown } from "lucide-react";
|
|
3
|
+
import { forwardRef, } from "react";
|
|
4
|
+
import { Button } from "../components/button.js";
|
|
5
|
+
import { Combobox } from "../components/combobox.js";
|
|
6
|
+
import { Separator } from "../components/separator.js";
|
|
7
|
+
import { cn } from "../lib/cn.js";
|
|
8
|
+
/**
|
|
9
|
+
* One App window's status bar: its attachment first, then the App's own
|
|
10
|
+
* readouts, with an optional far-end readout. One bar per window.
|
|
11
|
+
*/
|
|
12
|
+
export function AppStatusBar({ attachment, end, children, className, ...props }) {
|
|
13
|
+
return (_jsxs("footer", { ...props, className: cn("caps-app-status-bar", className), children: [attachment, attachment !== undefined && children !== undefined ? (
|
|
14
|
+
// The host's attachment ends here; the App's own items follow.
|
|
15
|
+
_jsx(Separator, { orientation: "vertical", className: "caps-app-status-bar-rule" })) : null, children, end === undefined ? null : (_jsx("span", { className: "caps-app-status-item caps-app-status-bar-end", children: end }))] }));
|
|
16
|
+
}
|
|
17
|
+
/** One quiet status readout. */
|
|
18
|
+
export function AppStatusItem({ className, ...props }) {
|
|
19
|
+
return _jsx("span", { ...props, className: cn("caps-app-status-item", className) });
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The status bar's attachment picker: a quiet label that opens a searchable
|
|
23
|
+
* list above the bar. Selection is a request; the caller owns the change.
|
|
24
|
+
*/
|
|
25
|
+
export function StatusBarPicker({ noun, pluralNoun, icon, options, value, onSelect, emptyMessage, accessibleLabel, disabled = false, open, onOpenChange, }) {
|
|
26
|
+
const label = options.find((option) => option.id === value)?.label ??
|
|
27
|
+
(value === null ? `No ${noun}` : noun);
|
|
28
|
+
return (_jsx(Combobox, { ...(open === undefined ? {} : { open }), ...(onOpenChange === undefined ? {} : { onOpenChange }), "aria-label": noun, contentClassName: "caps-status-bar-picker-list", emptyMessage: options.length === 0
|
|
29
|
+
? (emptyMessage ?? `No ${pluralNoun}`)
|
|
30
|
+
: `No matching ${pluralNoun}`, getKey: (option) => option.id, getLabel: (option) => option.label, inputProps: { "aria-label": `Search ${pluralNoun}` }, items: options, placeholder: `Search ${pluralNoun}…`, renderItem: (option) => (_jsxs("span", { className: "caps-status-bar-picker-option", children: [icon, option.label] })), trigger: _jsxs(Button, { variant: "ghost", size: "xs", className: "caps-status-bar-picker", "aria-label": accessibleLabel ?? `${noun}: ${label}`, title: `${noun}: ${label}`, disabled: disabled, children: [icon, _jsx("span", { className: "caps-status-bar-picker-label", children: label }), _jsx(ChevronsUpDown, { "aria-hidden": "true" })] }), value: value, onValueChange: (next) => {
|
|
31
|
+
if (next !== null && next !== value)
|
|
32
|
+
onSelect(next);
|
|
33
|
+
} }));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* One App-declared status bar control (an action, a toggle or a menu
|
|
37
|
+
* trigger): a flat chip with the same quiet face as StatusBarPicker. It
|
|
38
|
+
* forwards its ref and props, so a menu can render it as its trigger.
|
|
39
|
+
*/
|
|
40
|
+
export const StatusBarChip = forwardRef(function StatusBarChip({ label, icon, readout, pressed, expanded, className, ...props }, ref) {
|
|
41
|
+
return (_jsxs(Button, { ...props, ref: ref, variant: "ghost", size: "xs", className: cn("caps-status-bar-chip", className), "aria-pressed": pressed ?? props["aria-pressed"], "aria-expanded": expanded ?? props["aria-expanded"], children: [icon === undefined ? null : (_jsx("span", { className: "caps-status-bar-chip-icon", "aria-hidden": "true", children: icon })), _jsx("span", { className: "caps-status-bar-chip-label", children: label }), readout] }));
|
|
42
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
/** Supported AsyncStateValue values. */
|
|
3
|
+
export type AsyncStateValue = "loading" | "empty" | "error" | "content";
|
|
4
|
+
/** Props accepted by AsyncState. */
|
|
5
|
+
export type AsyncStateProps = Omit<HTMLAttributes<HTMLDivElement>, "content"> & {
|
|
6
|
+
/** The host's explicit state; this block does not infer it from its slots. */
|
|
7
|
+
state: AsyncStateValue;
|
|
8
|
+
loading: ReactNode;
|
|
9
|
+
empty: ReactNode;
|
|
10
|
+
error: ReactNode;
|
|
11
|
+
content: ReactNode;
|
|
12
|
+
};
|
|
13
|
+
/** Selects a caller-rendered state region without fetching or inventing copy. */
|
|
14
|
+
export declare const AsyncState: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "content"> & {
|
|
15
|
+
/** The host's explicit state; this block does not infer it from its slots. */
|
|
16
|
+
state: AsyncStateValue;
|
|
17
|
+
loading: ReactNode;
|
|
18
|
+
empty: ReactNode;
|
|
19
|
+
error: ReactNode;
|
|
20
|
+
content: ReactNode;
|
|
21
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
/** Selects a caller-rendered state region without fetching or inventing copy. */
|
|
5
|
+
export const AsyncState = forwardRef(function AsyncState({ className, content, empty, error, loading, state, ...props }, ref) {
|
|
6
|
+
const regions = { content, empty, error, loading };
|
|
7
|
+
return (_jsx("div", { ...props, className: cn("async-state", className), ref: ref, children: regions[state] }));
|
|
8
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
/** Minimum width for a card column before the grid adds another column. */
|
|
3
|
+
export type CardGridMinWidth = "narrow" | "regular" | "wide";
|
|
4
|
+
/** A responsive grid that fits complete cards to the available content width. */
|
|
5
|
+
export declare const CardGrid: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
6
|
+
minColumnWidth?: CardGridMinWidth;
|
|
7
|
+
shelf?: Readonly<{
|
|
8
|
+
title: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
}>;
|
|
11
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useId, } from "react";
|
|
3
|
+
import { SectionHeading } from "./section-heading.js";
|
|
4
|
+
import { cn } from "../lib/cn.js";
|
|
5
|
+
const minWidthTokens = {
|
|
6
|
+
narrow: "16rem",
|
|
7
|
+
regular: "20rem",
|
|
8
|
+
wide: "24rem",
|
|
9
|
+
};
|
|
10
|
+
/** A responsive grid that fits complete cards to the available content width. */
|
|
11
|
+
export const CardGrid = forwardRef(function CardGrid({ children, className, minColumnWidth = "regular", style, shelf, ...props }, ref) {
|
|
12
|
+
const titleId = useId();
|
|
13
|
+
const gridStyle = {
|
|
14
|
+
...style,
|
|
15
|
+
"--caps-card-grid-min-column": minWidthTokens[minColumnWidth],
|
|
16
|
+
};
|
|
17
|
+
const grid = (_jsx("div", { ...props, className: cn("caps-card-grid", className), ref: ref, style: gridStyle, children: children }));
|
|
18
|
+
if (!shelf)
|
|
19
|
+
return grid;
|
|
20
|
+
return (_jsxs("section", { className: "flex flex-col gap-md", "aria-labelledby": titleId, children: [_jsxs("div", { className: "flex flex-col gap-xs", children: [_jsx(SectionHeading, { id: titleId, children: shelf.title }), shelf.description ? (_jsx("p", { className: "text-sm text-base-content/70", children: shelf.description })) : null] }), grid] }));
|
|
21
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
/** Caller-provided command menu item data. */
|
|
3
|
+
export type CommandMenuItem = {
|
|
4
|
+
description?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
/** Key hint shown before the label, e.g. `z` in a which-key list. */
|
|
9
|
+
shortcut?: string;
|
|
10
|
+
};
|
|
11
|
+
/** Props accepted by CommandMenu. */
|
|
12
|
+
export type CommandMenuProps = Omit<HTMLAttributes<HTMLUListElement>, "children" | "onSelect"> & {
|
|
13
|
+
items: readonly CommandMenuItem[];
|
|
14
|
+
/** Called after a non-disabled item is selected. */
|
|
15
|
+
onSelect: (id: string) => void;
|
|
16
|
+
/** Caller-owned selected state; distinct from local keyboard focus. */
|
|
17
|
+
selected?: string;
|
|
18
|
+
};
|
|
19
|
+
/** A single-focus listbox with local navigation and caller-owned selection. */
|
|
20
|
+
export declare const CommandMenu: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLUListElement>, "children" | "onSelect"> & {
|
|
21
|
+
items: readonly CommandMenuItem[];
|
|
22
|
+
/** Called after a non-disabled item is selected. */
|
|
23
|
+
onSelect: (id: string) => void;
|
|
24
|
+
/** Caller-owned selected state; distinct from local keyboard focus. */
|
|
25
|
+
selected?: string;
|
|
26
|
+
} & import("react").RefAttributes<HTMLUListElement>>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useEffect, useId, useCallback, useImperativeHandle, useRef, } from "react";
|
|
3
|
+
import { useGlide } from "../lib/glide.js";
|
|
4
|
+
import { useSelectableCollection } from "../components/selectable.js";
|
|
5
|
+
/** A single-focus listbox with local navigation and caller-owned selection. */
|
|
6
|
+
export const CommandMenu = forwardRef(function CommandMenu({ className, items, onKeyDown, onSelect, selected, ...props }, ref) {
|
|
7
|
+
const instanceId = useId();
|
|
8
|
+
const list = useRef(null);
|
|
9
|
+
useImperativeHandle(ref, () => list.current);
|
|
10
|
+
const glide = useGlide('[role="option"][data-active]');
|
|
11
|
+
const attach = useCallback((node) => {
|
|
12
|
+
list.current = node;
|
|
13
|
+
glide(node);
|
|
14
|
+
}, [glide]);
|
|
15
|
+
const collection = useSelectableCollection({
|
|
16
|
+
getKey: (item) => item.id,
|
|
17
|
+
isDisabled: (item) => item.disabled === true,
|
|
18
|
+
items,
|
|
19
|
+
});
|
|
20
|
+
const { activeKey: activeId, enabledItems: enabled } = collection;
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (activeId && enabled.some((item) => item.id === activeId))
|
|
23
|
+
return;
|
|
24
|
+
collection.setActiveKey(selected && enabled.some((item) => item.id === selected)
|
|
25
|
+
? selected
|
|
26
|
+
: (enabled[0]?.id ?? null));
|
|
27
|
+
}, [activeId, collection.setActiveKey, enabled, selected]);
|
|
28
|
+
const optionId = (id) => `${instanceId}-option-${encodeURIComponent(id)}`;
|
|
29
|
+
return (_jsx("ul", { ...props, ref: attach, "aria-activedescendant": activeId ? optionId(activeId) : undefined, className: [
|
|
30
|
+
"menu max-h-56 overflow-y-auto rounded-none bg-base-100 shadow-lg",
|
|
31
|
+
className,
|
|
32
|
+
]
|
|
33
|
+
.filter(Boolean)
|
|
34
|
+
.join(" "), onKeyDown: (event) => {
|
|
35
|
+
onKeyDown?.(event);
|
|
36
|
+
if (event.defaultPrevented || !enabled.length)
|
|
37
|
+
return;
|
|
38
|
+
if (event.key === "ArrowDown") {
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
collection.move(1);
|
|
41
|
+
}
|
|
42
|
+
else if (event.key === "ArrowUp") {
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
collection.move(-1);
|
|
45
|
+
}
|
|
46
|
+
else if (event.key === "Home") {
|
|
47
|
+
event.preventDefault();
|
|
48
|
+
collection.move(-1, "start");
|
|
49
|
+
}
|
|
50
|
+
else if (event.key === "End") {
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
collection.move(1, "end");
|
|
53
|
+
}
|
|
54
|
+
else if ((event.key === "Enter" || event.key === " ") && activeId) {
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
onSelect(activeId);
|
|
57
|
+
}
|
|
58
|
+
}, role: "listbox", tabIndex: 0, children: items.map((item) => (_jsx("li", { role: "none", children: _jsxs("div", { "aria-disabled": item.disabled === true ? true : undefined, "aria-selected": selected === item.id, "data-active": activeId === item.id || undefined, className: [
|
|
59
|
+
"flex w-full items-center gap-sm px-md py-xs text-left text-xs",
|
|
60
|
+
activeId === item.id
|
|
61
|
+
? "bg-base-300 text-base-content"
|
|
62
|
+
: "text-base-content",
|
|
63
|
+
item.disabled
|
|
64
|
+
? "cursor-not-allowed bg-[var(--stipe-surface)] text-base-content outline outline-1 outline-dashed outline-base-300"
|
|
65
|
+
: "cursor-pointer",
|
|
66
|
+
].join(" "), "aria-keyshortcuts": item.shortcut, "data-search-option": item.id, id: optionId(item.id), onClick: () => {
|
|
67
|
+
if (!item.disabled)
|
|
68
|
+
onSelect(item.id);
|
|
69
|
+
}, role: "option", children: [item.shortcut ? (_jsx("kbd", { className: "kbd kbd-sm min-w-6 shrink-0", children: item.shortcut })) : null, _jsx("span", { className: "flex-1 truncate", children: item.label }), item.description ? (_jsx("span", { className: "text-xs text-base-content", children: item.description })) : null] }) }, item.id))) }));
|
|
70
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
/** Props accepted by Composer. */
|
|
3
|
+
export type ComposerProps = HTMLAttributes<HTMLDivElement> & {
|
|
4
|
+
/** The caller-owned primary input control. */
|
|
5
|
+
input: ReactNode;
|
|
6
|
+
/** The caller-owned action control. Its disabled state and accessible name stay on that control. */
|
|
7
|
+
action?: ReactNode;
|
|
8
|
+
/** Optional caller-owned leading slot, such as an avatar. */
|
|
9
|
+
leading?: ReactNode;
|
|
10
|
+
/** Classes for the joined row; useful when the host exposes a busy state. */
|
|
11
|
+
rowClassName?: string;
|
|
12
|
+
};
|
|
13
|
+
/** Layout and slots only. It never assigns action-control semantics. */
|
|
14
|
+
export declare const Composer: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
15
|
+
/** The caller-owned primary input control. */
|
|
16
|
+
input: ReactNode;
|
|
17
|
+
/** The caller-owned action control. Its disabled state and accessible name stay on that control. */
|
|
18
|
+
action?: ReactNode;
|
|
19
|
+
/** Optional caller-owned leading slot, such as an avatar. */
|
|
20
|
+
leading?: ReactNode;
|
|
21
|
+
/** Classes for the joined row; useful when the host exposes a busy state. */
|
|
22
|
+
rowClassName?: string;
|
|
23
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
/** Layout and slots only. It never assigns action-control semantics. */
|
|
4
|
+
export const Composer = forwardRef(function Composer({ action, className, input, leading, rowClassName, ...rootProps }, ref) {
|
|
5
|
+
return (_jsx("div", { ...rootProps, ref: ref, className: [
|
|
6
|
+
"caps-composer flex min-w-0 flex-1 items-end gap-sm",
|
|
7
|
+
className,
|
|
8
|
+
]
|
|
9
|
+
.filter(Boolean)
|
|
10
|
+
.join(" "), children: _jsxs("div", { className: ["join w-full p-2xs", rowClassName]
|
|
11
|
+
.filter(Boolean)
|
|
12
|
+
.join(" "), children: [leading, input, action] }) }));
|
|
13
|
+
});
|