@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,98 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Menu as BaseMenu } from "@base-ui/react/menu";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { createContext, forwardRef, useContext, useCallback, useImperativeHandle, useRef, } from "react";
|
|
5
|
+
import { cn, resolveClassName } from "../lib/cn.js";
|
|
6
|
+
import { useGlide } from "../lib/glide.js";
|
|
7
|
+
import { useOverlayRoot, useOverlayDismiss } from "./overlay-root.js";
|
|
8
|
+
const MenuContext = createContext(null);
|
|
9
|
+
/** A menu controller with explicit controlled or uncontrolled open state. */
|
|
10
|
+
export function DropdownMenu({ actionsRef: suppliedActionsRef, children, interactionOwner = "local", onOpenChange, ...props }) {
|
|
11
|
+
const internalActionsRef = useRef(null);
|
|
12
|
+
const actionsRef = suppliedActionsRef ?? internalActionsRef;
|
|
13
|
+
useOverlayDismiss(() => actionsRef.current?.close());
|
|
14
|
+
return (_jsx(MenuContext.Provider, { value: { actionsRef, interactionOwner }, children: _jsx(BaseMenu.Root, { ...props, actionsRef: actionsRef, onOpenChange: (next, details) => {
|
|
15
|
+
if (interactionOwner === "host" &&
|
|
16
|
+
!next &&
|
|
17
|
+
details.reason === "escape-key") {
|
|
18
|
+
details.cancel();
|
|
19
|
+
details.allowPropagation();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
onOpenChange?.(next, details);
|
|
23
|
+
}, children: children }) }));
|
|
24
|
+
}
|
|
25
|
+
/** Access the nearest DropdownMenu close operation. */
|
|
26
|
+
export function useDropdownMenu() {
|
|
27
|
+
const menu = useMenu();
|
|
28
|
+
return {
|
|
29
|
+
/** Close the nearest menu, if it is mounted. */
|
|
30
|
+
close: () => menu.actionsRef.current?.close(),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function useMenu() {
|
|
34
|
+
const value = useContext(MenuContext);
|
|
35
|
+
if (!value)
|
|
36
|
+
throw new Error("DropdownMenu parts must be used inside <DropdownMenu>");
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
/** The trigger control for a DropdownMenu. */
|
|
40
|
+
export const DropdownMenuTrigger = forwardRef(function DropdownMenuTrigger({ className, render, type = "button", ...props }, ref) {
|
|
41
|
+
return (_jsx(BaseMenu.Trigger, { ...props, className: (state) => cn("btn caps-dropdown-trigger", resolveClassName(className, state)), ref: ref, render: render, type: type }));
|
|
42
|
+
});
|
|
43
|
+
/** Shell popover destination, with Base UI container and mount lifetime overrides. */
|
|
44
|
+
export const DropdownMenuPortal = forwardRef(function DropdownMenuPortal({ container, ...props }, ref) {
|
|
45
|
+
const root = useOverlayRoot("popover");
|
|
46
|
+
if (container == null && root === null)
|
|
47
|
+
return null;
|
|
48
|
+
return _jsx(BaseMenu.Portal, { ...props, container: container ?? root, ref: ref });
|
|
49
|
+
});
|
|
50
|
+
/** Base UI owns placement; a shell's popover root supplies its collision boundary. */
|
|
51
|
+
export const DropdownMenuPositioner = forwardRef(function DropdownMenuPositioner({ collisionBoundary, ...props }, ref) {
|
|
52
|
+
const root = useOverlayRoot("popover");
|
|
53
|
+
return (_jsx(BaseMenu.Positioner, { ...props, collisionBoundary: collisionBoundary ?? (root && root !== document.body ? root : undefined), ref: ref }));
|
|
54
|
+
});
|
|
55
|
+
/** The styled popup for caller-composed menu placement and portal ownership. */
|
|
56
|
+
export const DropdownMenuPopup = forwardRef(function DropdownMenuPopup({ className, finalFocus, ...props }, ref) {
|
|
57
|
+
const { interactionOwner } = useMenu();
|
|
58
|
+
const popup = useRef(null);
|
|
59
|
+
useImperativeHandle(ref, () => popup.current);
|
|
60
|
+
const glide = useGlide(".caps-dropdown-item[data-highlighted]");
|
|
61
|
+
const attach = useCallback((node) => {
|
|
62
|
+
popup.current = node;
|
|
63
|
+
glide(node);
|
|
64
|
+
}, [glide]);
|
|
65
|
+
return (_jsx(BaseMenu.Popup, { ...props, className: (state) => cn("caps-dropdown-popup min-w-40 rounded-box border border-base-300 bg-base-100 p-xs shadow-lg outline-none", resolveClassName(className, state)), finalFocus: interactionOwner === "host" ? false : (finalFocus ?? true), ref: attach }));
|
|
66
|
+
});
|
|
67
|
+
/** The positioned content region for a DropdownMenu. */
|
|
68
|
+
export const DropdownMenuContent = forwardRef(function DropdownMenuContent({ align = "end", alignOffset, children, container, keepMounted, side = "bottom", sideOffset = 4, ...props }, ref) {
|
|
69
|
+
return (_jsx(DropdownMenuPortal, { container: container, keepMounted: keepMounted, children: _jsx(DropdownMenuPositioner, { align: align, alignOffset: alignOffset, side: side, sideOffset: sideOffset, children: _jsx(DropdownMenuPopup, { ...props, ref: ref, children: children }) }) }));
|
|
70
|
+
});
|
|
71
|
+
const itemClasses = "caps-dropdown-item flex w-full items-center gap-sm rounded-field px-md py-sm text-left text-sm outline-none transition-colors hover:bg-base-200 focus-visible:bg-base-200 aria-[current=page]:bg-base-200 aria-[current=page]:font-semibold disabled:cursor-not-allowed disabled:text-base-content/40 data-disabled:cursor-not-allowed data-disabled:text-base-content/40";
|
|
72
|
+
/** An actionable item in a DropdownMenu. */
|
|
73
|
+
export const DropdownMenuItem = forwardRef(function DropdownMenuItem({ className, onClick, onSelect, variant = "default", ...props }, ref) {
|
|
74
|
+
return (_jsx(BaseMenu.Item, { ...props, className: (state) => cn(itemClasses, variant === "destructive" &&
|
|
75
|
+
"text-error hover:bg-error hover:text-error-content focus-visible:bg-error focus-visible:text-error-content", resolveClassName(className, state)), onClick: (event) => {
|
|
76
|
+
onClick?.(event);
|
|
77
|
+
if (!event.defaultPrevented)
|
|
78
|
+
onSelect?.(event);
|
|
79
|
+
}, ref: ref }));
|
|
80
|
+
});
|
|
81
|
+
/** A checkable item in a DropdownMenu. */
|
|
82
|
+
export const DropdownMenuCheckboxItem = forwardRef(function DropdownMenuCheckboxItem({ className, onClick, onSelect, ...props }, ref) {
|
|
83
|
+
return (_jsx(BaseMenu.CheckboxItem, { ...props, className: (state) => cn(itemClasses, resolveClassName(className, state)), onClick: (event) => {
|
|
84
|
+
onClick?.(event);
|
|
85
|
+
if (!event.defaultPrevented)
|
|
86
|
+
onSelect?.(event);
|
|
87
|
+
}, ref: ref }));
|
|
88
|
+
});
|
|
89
|
+
/** A visual separator in a DropdownMenu. */
|
|
90
|
+
export const DropdownMenuSeparator = forwardRef(function DropdownMenuSeparator({ className, ...props }, ref) {
|
|
91
|
+
return (_jsx(BaseMenu.Separator, { ...props, className: (state) => cn("my-xs h-px bg-base-300", resolveClassName(className, state)), ref: ref }));
|
|
92
|
+
});
|
|
93
|
+
/** A named set of related items; pair it with a DropdownMenuLabel. */
|
|
94
|
+
export const DropdownMenuGroup = BaseMenu.Group;
|
|
95
|
+
/** The visible name of a DropdownMenuGroup, announced with its items. */
|
|
96
|
+
export const DropdownMenuLabel = forwardRef(function DropdownMenuLabel({ className, ...props }, ref) {
|
|
97
|
+
return (_jsx(BaseMenu.GroupLabel, { ...props, className: (state) => cn("px-md pb-xs pt-sm text-xs font-semibold text-base-content/60", resolveClassName(className, state)), ref: ref }));
|
|
98
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
/** A visible keyboard key from a shortcut or instruction. */
|
|
3
|
+
export type KeycapProps = {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
} & HTMLAttributes<HTMLElement>;
|
|
6
|
+
/** The shared field-guide treatment for a keyboard key. */
|
|
7
|
+
export declare function Keycap({ children, className, ...props }: KeycapProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
/** Semantic accent pigments for the static status light. */
|
|
9
|
+
export type StatusLedTone = "spore" | "mycelium" | "fruiting";
|
|
10
|
+
/** Props accepted by StatusLed. */
|
|
11
|
+
export type StatusLedProps = {
|
|
12
|
+
active: boolean;
|
|
13
|
+
label: string;
|
|
14
|
+
tone?: StatusLedTone;
|
|
15
|
+
} & Omit<HTMLAttributes<HTMLSpanElement>, "children">;
|
|
16
|
+
/** A static, labelled indicator using the current field-guide pigment tokens. */
|
|
17
|
+
export declare function StatusLed({ active, className, label, tone, ...props }: StatusLedProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
/** Props accepted by CartridgeLabel. */
|
|
19
|
+
export type CartridgeLabelProps = {
|
|
20
|
+
title: string;
|
|
21
|
+
subtitle?: string;
|
|
22
|
+
mark?: ReactNode;
|
|
23
|
+
} & HTMLAttributes<HTMLElement>;
|
|
24
|
+
/** A ruled, token-backed printed label for a physical game cartridge. */
|
|
25
|
+
export declare function CartridgeLabel({ className, mark, subtitle, title, ...props }: CartridgeLabelProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
/** Props accepted by SpecimenLabel. */
|
|
27
|
+
export type SpecimenLabelProps = {
|
|
28
|
+
plate: string;
|
|
29
|
+
detail: string;
|
|
30
|
+
kind: string;
|
|
31
|
+
number: string;
|
|
32
|
+
title: ReactNode;
|
|
33
|
+
summary?: ReactNode;
|
|
34
|
+
/**
|
|
35
|
+
* Mark the plate number for a pencil circle and the detail for a pencil
|
|
36
|
+
* underline. The ruled CSS marks remain until `enhance/sketch` draws them.
|
|
37
|
+
*/
|
|
38
|
+
sketch?: boolean;
|
|
39
|
+
} & Omit<HTMLAttributes<HTMLElement>, "title">;
|
|
40
|
+
/** A caption, catalogue line, title, and note for a specimen illustration. */
|
|
41
|
+
export declare function SpecimenLabel({ className, detail, kind, number, plate, sketch, summary, title, ...props }: SpecimenLabelProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
/** The shared field-guide treatment for a keyboard key. */
|
|
4
|
+
export function Keycap({ children, className, ...props }) {
|
|
5
|
+
return (_jsx("kbd", { ...props, className: cn("caps-keycap", className), children: children }));
|
|
6
|
+
}
|
|
7
|
+
/** A static, labelled indicator using the current field-guide pigment tokens. */
|
|
8
|
+
export function StatusLed({ active, className, label, tone = "mycelium", ...props }) {
|
|
9
|
+
return (_jsxs("span", { ...props, "aria-label": `${label}: ${active ? "on" : "off"}`, className: cn("caps-led", className), "data-active": active, "data-tone": tone, role: "img", children: [_jsx("span", { "aria-hidden": "true", className: "caps-led-bead" }), _jsx("span", { "aria-hidden": "true", children: label })] }));
|
|
10
|
+
}
|
|
11
|
+
/** A ruled, token-backed printed label for a physical game cartridge. */
|
|
12
|
+
export function CartridgeLabel({ className, mark, subtitle, title, ...props }) {
|
|
13
|
+
return (_jsxs("figure", { ...props, "aria-label": `${title} cartridge label`, className: cn("caps-cartridge-label", className), children: [_jsx("span", { "aria-hidden": "true", className: "caps-cartridge-label-rule" }), mark ? (_jsx("span", { "aria-hidden": "true", className: "caps-cartridge-label-mark", children: mark })) : null, _jsx("span", { className: "caps-cartridge-label-title", children: title }), subtitle ? (_jsx("span", { className: "caps-cartridge-label-subtitle", children: subtitle })) : null, _jsx("span", { "aria-hidden": "true", className: "caps-cartridge-label-rule" })] }));
|
|
14
|
+
}
|
|
15
|
+
/** A caption, catalogue line, title, and note for a specimen illustration. */
|
|
16
|
+
export function SpecimenLabel({ className, detail, kind, number, plate, sketch = false, summary, title, ...props }) {
|
|
17
|
+
return (_jsxs("aside", { ...props, className: cn("caps-specimen-label", className), children: [_jsxs("p", { className: "caps-specimen-label-caption", children: [_jsx("span", { className: "caps-specimen-label-plate", "data-sketch": sketch ? "circle" : undefined, children: plate }), _jsx("span", { className: "caps-specimen-label-detail", "data-sketch": sketch ? "underline" : undefined, children: detail })] }), _jsxs("span", { className: "caps-specimen-label-meta", children: [kind, " \u00B7 ", number] }), _jsx("div", { className: "caps-specimen-label-title", children: title }), summary ? (_jsx("p", { className: "caps-specimen-label-summary", children: summary })) : null] }));
|
|
18
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { type FieldsetHTMLAttributes, type FormHTMLAttributes, type HTMLAttributes, type InputHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
/** An announced error that takes no space when absent and collapses smoothly. */
|
|
3
|
+
export declare function FieldError({ children, ...props }: HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
/** Props accepted by FormFooter. */
|
|
5
|
+
export type FormFooterProps = {
|
|
6
|
+
/** The submit-time error, announced above the actions. */
|
|
7
|
+
error?: ReactNode;
|
|
8
|
+
/** `end` right-aligns actions; `stretch` stacks full-width actions for single-column cards. */
|
|
9
|
+
layout?: "end" | "stretch";
|
|
10
|
+
/** Quiet state opposite the actions (role="status"), such as a Status LED reading "Saved". */
|
|
11
|
+
status?: ReactNode;
|
|
12
|
+
/** Draws the rule above the footer; short inline forms leave it off. */
|
|
13
|
+
divider?: boolean;
|
|
14
|
+
} & HTMLAttributes<HTMLElement>;
|
|
15
|
+
/**
|
|
16
|
+
* A form's stationary action area. Put exactly one primary Button last;
|
|
17
|
+
* every other action is `ghost` or `outline`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function FormFooter({ children, divider, error, layout, status, ...props }: FormFooterProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
/** Props accepted by Form. */
|
|
21
|
+
export type FormProps = {
|
|
22
|
+
/** Disables every control while a submission is in flight. */
|
|
23
|
+
busy?: boolean;
|
|
24
|
+
} & FormHTMLAttributes<HTMLFormElement>;
|
|
25
|
+
/**
|
|
26
|
+
* A form with Caps' field rhythm. While `busy`, its fieldset disables every
|
|
27
|
+
* control and the form reports `aria-busy`; validation stays with the caller.
|
|
28
|
+
*/
|
|
29
|
+
export declare const Form: import("react").ForwardRefExoticComponent<{
|
|
30
|
+
/** Disables every control while a submission is in flight. */
|
|
31
|
+
busy?: boolean;
|
|
32
|
+
} & FormHTMLAttributes<HTMLFormElement> & import("react").RefAttributes<HTMLFormElement>>;
|
|
33
|
+
/** Props accepted by FormSection. */
|
|
34
|
+
export type FormSectionProps = {
|
|
35
|
+
title: ReactNode;
|
|
36
|
+
description?: ReactNode;
|
|
37
|
+
/** Heading level inside the page outline. */
|
|
38
|
+
level?: 2 | 3 | 4;
|
|
39
|
+
children: ReactNode;
|
|
40
|
+
} & Omit<HTMLAttributes<HTMLElement>, "title">;
|
|
41
|
+
/** A titled group of fields; consecutive sections share one quiet rule. */
|
|
42
|
+
export declare const FormSection: import("react").ForwardRefExoticComponent<{
|
|
43
|
+
title: ReactNode;
|
|
44
|
+
description?: ReactNode;
|
|
45
|
+
/** Heading level inside the page outline. */
|
|
46
|
+
level?: 2 | 3 | 4;
|
|
47
|
+
children: ReactNode;
|
|
48
|
+
} & Omit<HTMLAttributes<HTMLElement>, "title"> & import("react").RefAttributes<HTMLElement>>;
|
|
49
|
+
/** Props accepted by FieldSet. */
|
|
50
|
+
export type FieldSetProps = {
|
|
51
|
+
legend: ReactNode;
|
|
52
|
+
description?: ReactNode;
|
|
53
|
+
/** The group's announced error, such as "Choose at least one right". */
|
|
54
|
+
error?: ReactNode;
|
|
55
|
+
children: ReactNode;
|
|
56
|
+
} & FieldsetHTMLAttributes<HTMLFieldSetElement>;
|
|
57
|
+
/**
|
|
58
|
+
* A legend-named group of related choices, such as a list of inline
|
|
59
|
+
* checkbox Fields. `disabled` disables every control inside.
|
|
60
|
+
*/
|
|
61
|
+
export declare const FieldSet: import("react").ForwardRefExoticComponent<{
|
|
62
|
+
legend: ReactNode;
|
|
63
|
+
description?: ReactNode;
|
|
64
|
+
/** The group's announced error, such as "Choose at least one right". */
|
|
65
|
+
error?: ReactNode;
|
|
66
|
+
children: ReactNode;
|
|
67
|
+
} & FieldsetHTMLAttributes<HTMLFieldSetElement> & import("react").RefAttributes<HTMLFieldSetElement>>;
|
|
68
|
+
/** Props accepted by Field. */
|
|
69
|
+
export type FieldProps = {
|
|
70
|
+
children: ReactNode;
|
|
71
|
+
description?: ReactNode;
|
|
72
|
+
error?: ReactNode;
|
|
73
|
+
/**
|
|
74
|
+
* A decorative marker, such as a Sigil or App icon, before the label. It is
|
|
75
|
+
* hidden from assistive tech; the label stays the control's name.
|
|
76
|
+
*/
|
|
77
|
+
icon?: ReactNode;
|
|
78
|
+
label?: ReactNode;
|
|
79
|
+
/**
|
|
80
|
+
* `vertical` stacks label, control and error; `horizontal` puts the control
|
|
81
|
+
* opposite its copy (settings rows); `inline` puts a checkbox or switch
|
|
82
|
+
* before its copy.
|
|
83
|
+
*/
|
|
84
|
+
orientation?: "horizontal" | "inline" | "vertical";
|
|
85
|
+
/** Marks an optional field in a mostly required form. */
|
|
86
|
+
optional?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* The child is a read-only readout, such as a Status, not a control: the
|
|
89
|
+
* label names the row and nothing is wired to the child.
|
|
90
|
+
*/
|
|
91
|
+
readout?: boolean;
|
|
92
|
+
required?: boolean;
|
|
93
|
+
} & HTMLAttributes<HTMLDivElement>;
|
|
94
|
+
/** A labelled form-field layout that renders caller-provided control and supporting content. */
|
|
95
|
+
export declare const Field: import("react").ForwardRefExoticComponent<{
|
|
96
|
+
children: ReactNode;
|
|
97
|
+
description?: ReactNode;
|
|
98
|
+
error?: ReactNode;
|
|
99
|
+
/**
|
|
100
|
+
* A decorative marker, such as a Sigil or App icon, before the label. It is
|
|
101
|
+
* hidden from assistive tech; the label stays the control's name.
|
|
102
|
+
*/
|
|
103
|
+
icon?: ReactNode;
|
|
104
|
+
label?: ReactNode;
|
|
105
|
+
/**
|
|
106
|
+
* `vertical` stacks label, control and error; `horizontal` puts the control
|
|
107
|
+
* opposite its copy (settings rows); `inline` puts a checkbox or switch
|
|
108
|
+
* before its copy.
|
|
109
|
+
*/
|
|
110
|
+
orientation?: "horizontal" | "inline" | "vertical";
|
|
111
|
+
/** Marks an optional field in a mostly required form. */
|
|
112
|
+
optional?: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* The child is a read-only readout, such as a Status, not a control: the
|
|
115
|
+
* label names the row and nothing is wired to the child.
|
|
116
|
+
*/
|
|
117
|
+
readout?: boolean;
|
|
118
|
+
required?: boolean;
|
|
119
|
+
} & HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
|
|
120
|
+
/** Props accepted by NumberField. */
|
|
121
|
+
export type NumberFieldProps = {
|
|
122
|
+
/** Controlled value; `null` is empty. */
|
|
123
|
+
value?: number | null;
|
|
124
|
+
defaultValue?: number | null;
|
|
125
|
+
/** Called with each committed value, already clamped to min/max. */
|
|
126
|
+
onValueChange?: (value: number | null) => void;
|
|
127
|
+
min?: number;
|
|
128
|
+
max?: number;
|
|
129
|
+
/** Arrow keys and the − / + keys move by `step`; Page Up/Down by ten steps. */
|
|
130
|
+
step?: number;
|
|
131
|
+
/** A short unit readout after the value, such as "min" or "GB". */
|
|
132
|
+
unit?: ReactNode;
|
|
133
|
+
/** Cycle from max back to min (and back) instead of stopping at the ends. */
|
|
134
|
+
wrap?: boolean;
|
|
135
|
+
invalid?: boolean;
|
|
136
|
+
/** Accessible names for the − / + keys. */
|
|
137
|
+
decrementLabel?: string;
|
|
138
|
+
incrementLabel?: string;
|
|
139
|
+
} & Omit<InputHTMLAttributes<HTMLInputElement>, "defaultValue" | "max" | "min" | "onChange" | "step" | "type" | "value">;
|
|
140
|
+
/**
|
|
141
|
+
* A number spinbutton: a native `input type="number"` between − and + keys,
|
|
142
|
+
* with the value as a mono readout. Field wires its label, description and
|
|
143
|
+
* error to the input. The `caps-number-field-*` parts are stable so a context
|
|
144
|
+
* such as a Screen can redraw them; the semantics stay here.
|
|
145
|
+
*/
|
|
146
|
+
export declare const NumberField: import("react").ForwardRefExoticComponent<{
|
|
147
|
+
/** Controlled value; `null` is empty. */
|
|
148
|
+
value?: number | null;
|
|
149
|
+
defaultValue?: number | null;
|
|
150
|
+
/** Called with each committed value, already clamped to min/max. */
|
|
151
|
+
onValueChange?: (value: number | null) => void;
|
|
152
|
+
min?: number;
|
|
153
|
+
max?: number;
|
|
154
|
+
/** Arrow keys and the − / + keys move by `step`; Page Up/Down by ten steps. */
|
|
155
|
+
step?: number;
|
|
156
|
+
/** A short unit readout after the value, such as "min" or "GB". */
|
|
157
|
+
unit?: ReactNode;
|
|
158
|
+
/** Cycle from max back to min (and back) instead of stopping at the ends. */
|
|
159
|
+
wrap?: boolean;
|
|
160
|
+
invalid?: boolean;
|
|
161
|
+
/** Accessible names for the − / + keys. */
|
|
162
|
+
decrementLabel?: string;
|
|
163
|
+
incrementLabel?: string;
|
|
164
|
+
} & Omit<InputHTMLAttributes<HTMLInputElement>, "defaultValue" | "onChange" | "type" | "value" | "max" | "min" | "step"> & import("react").RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Children, cloneElement, forwardRef, isValidElement, useId, useRef, useState, } from "react";
|
|
3
|
+
import { Minus, Plus } from "lucide-react";
|
|
4
|
+
import { cn } from "../lib/cn.js";
|
|
5
|
+
import { Button } from "./button.js";
|
|
6
|
+
import { Label } from "./label.js";
|
|
7
|
+
import { InputGroup, InputGroupAddon } from "./input-group.js";
|
|
8
|
+
import { Collapse } from "./presence.js";
|
|
9
|
+
/** An announced error that takes no space when absent and collapses smoothly. */
|
|
10
|
+
export function FieldError({ children, ...props }) {
|
|
11
|
+
const [lastError, setLastError] = useState(children);
|
|
12
|
+
// Retain the closing content so its intrinsic height can animate to zero.
|
|
13
|
+
if (children && children !== lastError)
|
|
14
|
+
setLastError(children);
|
|
15
|
+
return (_jsx(Collapse, { ...props, className: cn("caps-field-error-reveal", props.className), open: Boolean(children), children: _jsx("div", { className: "caps-field-error", role: "alert", "aria-hidden": !children, children: lastError }) }));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A form's stationary action area. Put exactly one primary Button last;
|
|
19
|
+
* every other action is `ghost` or `outline`.
|
|
20
|
+
*/
|
|
21
|
+
export function FormFooter({ children, divider = true, error, layout = "end", status, ...props }) {
|
|
22
|
+
return (_jsxs("footer", { ...props, className: cn("caps-form-footer", props.className), "data-divider": divider, "data-layout": layout, children: [_jsx(FieldError, { children: error }), _jsxs("div", { className: "caps-form-footer-row", children: [status ? (_jsx("div", { className: "caps-form-footer-status", role: "status", children: status })) : null, _jsx("div", { className: "caps-form-footer-actions", children: children })] })] }));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A form with Caps' field rhythm. While `busy`, its fieldset disables every
|
|
26
|
+
* control and the form reports `aria-busy`; validation stays with the caller.
|
|
27
|
+
*/
|
|
28
|
+
export const Form = forwardRef(function Form({ busy = false, children, className, ...props }, ref) {
|
|
29
|
+
return (_jsx("form", { ...props, "aria-busy": busy || undefined, className: cn("caps-form", className), ref: ref, children: _jsx("fieldset", { className: "caps-form-body", disabled: busy, children: children }) }));
|
|
30
|
+
});
|
|
31
|
+
const sectionHeadings = { 2: "h2", 3: "h3", 4: "h4" };
|
|
32
|
+
/** A titled group of fields; consecutive sections share one quiet rule. */
|
|
33
|
+
export const FormSection = forwardRef(function FormSection({ children, className, description, level = 3, title, ...props }, ref) {
|
|
34
|
+
const id = useId();
|
|
35
|
+
const Heading = sectionHeadings[level];
|
|
36
|
+
return (_jsxs("section", { ...props, "aria-labelledby": `${id}-title`, className: cn("caps-form-section", className), ref: ref, children: [_jsxs("header", { className: "caps-form-section-copy", children: [_jsx(Heading, { className: "caps-form-section-title", id: `${id}-title`, children: title }), description ? (_jsx("p", { className: "caps-form-section-description", children: description })) : null] }), _jsx("div", { className: "caps-form-section-fields", children: children })] }));
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* A legend-named group of related choices, such as a list of inline
|
|
40
|
+
* checkbox Fields. `disabled` disables every control inside.
|
|
41
|
+
*/
|
|
42
|
+
export const FieldSet = forwardRef(function FieldSet({ children, className, description, error, legend, ...props }, ref) {
|
|
43
|
+
const id = useId();
|
|
44
|
+
const describedBy = [
|
|
45
|
+
description ? `${id}-description` : props["aria-describedby"],
|
|
46
|
+
error ? `${id}-error` : null,
|
|
47
|
+
]
|
|
48
|
+
.filter(Boolean)
|
|
49
|
+
.join(" ") || undefined;
|
|
50
|
+
return (_jsxs("fieldset", { ...props, "aria-describedby": describedBy, "aria-invalid": error ? true : props["aria-invalid"], className: cn("caps-fieldset", className), ref: ref, children: [_jsx("legend", { className: "caps-fieldset-legend", children: legend }), description ? (_jsx("p", { className: "caps-fieldset-description", id: `${id}-description`, children: description })) : null, _jsx("div", { className: "caps-fieldset-fields", children: children }), error === undefined ? null : (_jsx(FieldError, { id: `${id}-error`, children: error }))] }));
|
|
51
|
+
});
|
|
52
|
+
/** The labelled control inside an InputGroup: its first non-addon element. */
|
|
53
|
+
function groupControl(group) {
|
|
54
|
+
const controls = [];
|
|
55
|
+
Children.forEach(group.props.children, (part) => {
|
|
56
|
+
if (isValidElement(part) &&
|
|
57
|
+
part.type !== InputGroupAddon)
|
|
58
|
+
controls.push(part);
|
|
59
|
+
});
|
|
60
|
+
return controls[0] ?? null;
|
|
61
|
+
}
|
|
62
|
+
/** A labelled form-field layout that renders caller-provided control and supporting content. */
|
|
63
|
+
export const Field = forwardRef(function Field({ children, className, description, error, icon, label, optional, orientation = "vertical", readout = false, required, ...props }, ref) {
|
|
64
|
+
const generatedId = useId();
|
|
65
|
+
const child = isValidElement(children) ? children : null;
|
|
66
|
+
// An InputGroup is layout: the labelled control is the input inside it.
|
|
67
|
+
const group = child?.type === InputGroup;
|
|
68
|
+
const controlElement = group ? groupControl(child) : child;
|
|
69
|
+
const id = controlElement?.props.id;
|
|
70
|
+
const controlId = id ?? generatedId;
|
|
71
|
+
const descriptionId = `${controlId}-description`;
|
|
72
|
+
const errorId = `${controlId}-error`;
|
|
73
|
+
// Keep any description the caller already wired to the control.
|
|
74
|
+
const describedBy = [
|
|
75
|
+
controlElement?.props["aria-describedby"],
|
|
76
|
+
description ? descriptionId : null,
|
|
77
|
+
error ? errorId : null,
|
|
78
|
+
]
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
.join(" ") || undefined;
|
|
81
|
+
const requiredValue = required === true ? true : controlElement?.props.required;
|
|
82
|
+
const invalidValue = error ? true : controlElement?.props["aria-invalid"];
|
|
83
|
+
const ariaRequiredValue = required === true ? true : controlElement?.props["aria-required"];
|
|
84
|
+
const clonedProps = { id: controlId };
|
|
85
|
+
if (requiredValue !== undefined)
|
|
86
|
+
clonedProps.required = requiredValue;
|
|
87
|
+
if (describedBy !== undefined)
|
|
88
|
+
clonedProps["aria-describedby"] = describedBy;
|
|
89
|
+
if (invalidValue !== undefined)
|
|
90
|
+
clonedProps["aria-invalid"] = invalidValue;
|
|
91
|
+
if (ariaRequiredValue !== undefined)
|
|
92
|
+
clonedProps["aria-required"] = ariaRequiredValue;
|
|
93
|
+
const control = child && controlElement && !readout
|
|
94
|
+
? group
|
|
95
|
+
? cloneElement(child, undefined, Children.map(child.props.children, (part) => part === controlElement
|
|
96
|
+
? cloneElement(controlElement, clonedProps)
|
|
97
|
+
: part))
|
|
98
|
+
: cloneElement(child, clonedProps)
|
|
99
|
+
: children;
|
|
100
|
+
const labelContent = label ? (_jsx(Label, { htmlFor: readout ? undefined : controlId,
|
|
101
|
+
// A choice's own label reads as body text; the group's legend carries weight.
|
|
102
|
+
className: orientation === "inline" ? "font-normal" : undefined, ...(required === undefined ? {} : { required }), ...(optional === undefined ? {} : { optional }), children: label })) : null;
|
|
103
|
+
const copy = label || description ? (_jsxs("div", { className: "caps-field-copy", children: [labelContent, description ? (_jsx("p", { className: "caps-field-description", id: descriptionId, children: description })) : null] })) : null;
|
|
104
|
+
const heading = icon ? (_jsxs("div", { className: "caps-field-lead", children: [_jsx("span", { className: "caps-field-icon", "aria-hidden": "true", children: icon }), copy] })) : (copy);
|
|
105
|
+
return (_jsxs("div", { ...props, className: cn("caps-field", className), "data-orientation": orientation, ref: ref, children: [orientation === "horizontal" ? (_jsxs("div", { className: "caps-field-row", children: [heading, control] })) : orientation === "inline" ? (_jsxs("div", { className: "caps-field-row", children: [control, heading] })) : (_jsxs(_Fragment, { children: [heading, control] })), _jsx(FieldError, { id: errorId, children: error })] }));
|
|
106
|
+
});
|
|
107
|
+
function stepPrecision(step) {
|
|
108
|
+
const text = String(step);
|
|
109
|
+
const dot = text.indexOf(".");
|
|
110
|
+
return dot === -1 ? 0 : text.length - dot - 1;
|
|
111
|
+
}
|
|
112
|
+
/** A 270° encoder arc from 7:30 to 4:30, measured as 100 path units. */
|
|
113
|
+
const DIAL_ARC = "M 9.86 38.14 A 20 20 0 1 1 38.14 38.14";
|
|
114
|
+
/** Pixels of vertical drag per step on the dial. */
|
|
115
|
+
const DIAL_TRAVEL = 6;
|
|
116
|
+
const keySteps = new Map([
|
|
117
|
+
["ArrowUp", 1],
|
|
118
|
+
["ArrowDown", -1],
|
|
119
|
+
["PageUp", 10],
|
|
120
|
+
["PageDown", -10],
|
|
121
|
+
]);
|
|
122
|
+
/**
|
|
123
|
+
* A number spinbutton: a native `input type="number"` between − and + keys,
|
|
124
|
+
* with the value as a mono readout. Field wires its label, description and
|
|
125
|
+
* error to the input. The `caps-number-field-*` parts are stable so a context
|
|
126
|
+
* such as a Screen can redraw them; the semantics stay here.
|
|
127
|
+
*/
|
|
128
|
+
export const NumberField = forwardRef(function NumberField({ className, decrementLabel = "Decrease", defaultValue = null, disabled, incrementLabel = "Increase", invalid, max, min, onBlur, onKeyDown, onValueChange, step = 1, unit, value, wrap = false, ...props }, ref) {
|
|
129
|
+
const [inner, setInner] = useState(defaultValue);
|
|
130
|
+
const [draft, setDraft] = useState(null);
|
|
131
|
+
const current = value === undefined ? inner : value;
|
|
132
|
+
const precision = stepPrecision(step);
|
|
133
|
+
const clamp = (next) => {
|
|
134
|
+
const rounded = Number(next.toFixed(precision));
|
|
135
|
+
if (min !== undefined && rounded < min)
|
|
136
|
+
return min;
|
|
137
|
+
if (max !== undefined && rounded > max)
|
|
138
|
+
return max;
|
|
139
|
+
return rounded;
|
|
140
|
+
};
|
|
141
|
+
const commit = (next) => {
|
|
142
|
+
const settled = next === null ? null : clamp(next);
|
|
143
|
+
setInner(settled);
|
|
144
|
+
setDraft(null);
|
|
145
|
+
if (settled !== current)
|
|
146
|
+
onValueChange?.(settled);
|
|
147
|
+
};
|
|
148
|
+
const commitDraft = () => {
|
|
149
|
+
if (draft === null)
|
|
150
|
+
return;
|
|
151
|
+
const parsed = Number(draft);
|
|
152
|
+
commit(draft.trim() === "" || !Number.isFinite(parsed) ? null : parsed);
|
|
153
|
+
};
|
|
154
|
+
const stepBy = (steps) => {
|
|
155
|
+
const next = (current ?? min ?? 0) + steps * step;
|
|
156
|
+
if (wrap && min !== undefined && max !== undefined) {
|
|
157
|
+
if (next > max) {
|
|
158
|
+
commit(min);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (next < min) {
|
|
162
|
+
commit(max);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
commit(next);
|
|
167
|
+
};
|
|
168
|
+
const atMin = min !== undefined && current !== null && current <= min;
|
|
169
|
+
const atMax = max !== undefined && current !== null && current >= max;
|
|
170
|
+
const isDisabled = disabled === true;
|
|
171
|
+
const drag = useRef(null);
|
|
172
|
+
// The dial is drawn only where a context (a Screen) asks for it, and only
|
|
173
|
+
// for a bounded value; the input stays the spinbutton either way.
|
|
174
|
+
const fraction = min !== undefined && max !== undefined && max > min
|
|
175
|
+
? ((current ?? min) - min) / (max - min)
|
|
176
|
+
: null;
|
|
177
|
+
const onDialDown = (event) => {
|
|
178
|
+
if (isDisabled)
|
|
179
|
+
return;
|
|
180
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
181
|
+
drag.current = { y: event.clientY, value: current ?? min ?? 0 };
|
|
182
|
+
};
|
|
183
|
+
const onDialMove = (event) => {
|
|
184
|
+
if (!drag.current)
|
|
185
|
+
return;
|
|
186
|
+
const steps = Math.trunc((drag.current.y - event.clientY) / DIAL_TRAVEL);
|
|
187
|
+
const next = clamp(drag.current.value + steps * step);
|
|
188
|
+
if (next !== current)
|
|
189
|
+
commit(next);
|
|
190
|
+
};
|
|
191
|
+
const endDial = () => {
|
|
192
|
+
drag.current = null;
|
|
193
|
+
};
|
|
194
|
+
return (_jsxs("span", { className: cn("caps-number-field", className), "data-at-max": atMax || undefined, "data-at-min": atMin || undefined, "data-disabled": isDisabled || undefined, "data-wrap": wrap || undefined, children: [fraction === null ? null : (_jsxs("svg", { className: "caps-number-field-dial", viewBox: "0 0 48 48", "aria-hidden": "true", focusable: "false", onPointerDown: onDialDown, onPointerMove: onDialMove, onPointerUp: endDial, onPointerCancel: endDial, children: [_jsx("path", { className: "caps-number-field-dial-track", d: DIAL_ARC, pathLength: 100 }), _jsx("path", { className: "caps-number-field-dial-level", d: DIAL_ARC, pathLength: 100, strokeDasharray: `${String(Math.max(0.5, fraction * 100))} 100` })] })), _jsx(Button, { type: "button", variant: "outline", size: "icon", className: "caps-number-field-decrement", "aria-label": decrementLabel, "aria-controls": props.id, tabIndex: -1, disabled: isDisabled || (atMin && !wrap), onClick: () => {
|
|
195
|
+
stepBy(-1);
|
|
196
|
+
}, children: _jsx(Minus, { "aria-hidden": "true" }) }), _jsxs("span", { className: "caps-number-field-readout", children: [_jsx("input", { ...props, "aria-invalid": invalid
|
|
197
|
+
? true
|
|
198
|
+
: props["aria-invalid"] === false
|
|
199
|
+
? undefined
|
|
200
|
+
: props["aria-invalid"], className: "input caps-input caps-number-field-input", disabled: disabled, inputMode: props.inputMode ?? (precision > 0 ? "decimal" : "numeric"), max: max, min: min, onBlur: (event) => {
|
|
201
|
+
commitDraft();
|
|
202
|
+
onBlur?.(event);
|
|
203
|
+
}, onChange: (event) => {
|
|
204
|
+
setDraft(event.target.value);
|
|
205
|
+
}, onKeyDown: (event) => {
|
|
206
|
+
onKeyDown?.(event);
|
|
207
|
+
if (event.defaultPrevented)
|
|
208
|
+
return;
|
|
209
|
+
const steps = keySteps.get(event.key);
|
|
210
|
+
if (steps !== undefined) {
|
|
211
|
+
event.preventDefault();
|
|
212
|
+
stepBy(steps);
|
|
213
|
+
}
|
|
214
|
+
else if (event.key === "Home" && min !== undefined) {
|
|
215
|
+
event.preventDefault();
|
|
216
|
+
commit(min);
|
|
217
|
+
}
|
|
218
|
+
else if (event.key === "End" && max !== undefined) {
|
|
219
|
+
event.preventDefault();
|
|
220
|
+
commit(max);
|
|
221
|
+
}
|
|
222
|
+
else if (event.key === "Enter") {
|
|
223
|
+
commitDraft();
|
|
224
|
+
}
|
|
225
|
+
}, ref: ref, step: step, type: "number", value: draft ?? (current === null ? "" : String(current)) }), unit ? (_jsx("span", { className: "caps-number-field-unit", "aria-hidden": "true", children: unit })) : null] }), _jsx(Button, { type: "button", variant: "outline", size: "icon", className: "caps-number-field-increment", "aria-label": incrementLabel, "aria-controls": props.id, tabIndex: -1, disabled: isDisabled || (atMax && !wrap), onClick: () => {
|
|
226
|
+
stepBy(1);
|
|
227
|
+
}, children: _jsx(Plus, { "aria-hidden": "true" }) })] }));
|
|
228
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
/** What happened: an order taken, an order refused, a hit, or something gained. */
|
|
3
|
+
export type FeedbackKind = "confirm" | "deny" | "hit" | "gain";
|
|
4
|
+
/** Props accepted by FeedbackLayer. */
|
|
5
|
+
export type FeedbackLayerProps = HTMLAttributes<HTMLDivElement> & {
|
|
6
|
+
/** Freeze every mark where it is, e.g. while the game is paused. */
|
|
7
|
+
paused?: boolean;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A click-through overlay for game feedback, placed over the world by the
|
|
11
|
+
* host. Marks inside it are positioned in the layer's own pixels.
|
|
12
|
+
*/
|
|
13
|
+
export declare const FeedbackLayer: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
14
|
+
/** Freeze every mark where it is, e.g. while the game is paused. */
|
|
15
|
+
paused?: boolean;
|
|
16
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
17
|
+
/** Props accepted by FeedbackMark. */
|
|
18
|
+
export type FeedbackMarkProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
19
|
+
kind: FeedbackKind;
|
|
20
|
+
/** Anchor in layer pixels. */
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
/** A short note, such as the host's refusal reason. */
|
|
24
|
+
label?: ReactNode;
|
|
25
|
+
/** A floating number, drawn signed in outlined pixel numerals. */
|
|
26
|
+
amount?: number;
|
|
27
|
+
/** Total life in milliseconds; the host removes the mark when it ends. */
|
|
28
|
+
life?: number;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* One piece of game feedback at a point: a stepped flash ring, an optional
|
|
32
|
+
* floating number and an optional note. Reduced motion keeps the ring and
|
|
33
|
+
* note still and only fades them.
|
|
34
|
+
*/
|
|
35
|
+
export declare const FeedbackMark: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
36
|
+
kind: FeedbackKind;
|
|
37
|
+
/** Anchor in layer pixels. */
|
|
38
|
+
x: number;
|
|
39
|
+
y: number;
|
|
40
|
+
/** A short note, such as the host's refusal reason. */
|
|
41
|
+
label?: ReactNode;
|
|
42
|
+
/** A floating number, drawn signed in outlined pixel numerals. */
|
|
43
|
+
amount?: number;
|
|
44
|
+
/** Total life in milliseconds; the host removes the mark when it ends. */
|
|
45
|
+
life?: number;
|
|
46
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
47
|
+
/** A generic feedback tone: game-neutral, so any cartridge can author with
|
|
48
|
+
* it. `bite` is for a creature's bite, not any one game's monster. */
|
|
49
|
+
export type FeedbackWordTone = "impact" | "gun" | "mech" | "wet" | "quiet" | "bite";
|
|
50
|
+
/** Named display sizes for game feedback words. */
|
|
51
|
+
export type FeedbackWordSize = "s" | "m" | "l" | "xl";
|
|
52
|
+
/** A two-phase accent that plays alongside the word: `jaw` claps two wedges
|
|
53
|
+
* shut once, then eases back open. */
|
|
54
|
+
export type FeedbackWordAccent = "jaw";
|
|
55
|
+
/** Props accepted by FeedbackWordMark. */
|
|
56
|
+
export type FeedbackWordMarkProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
57
|
+
/** Comic lettering, at most 12 characters (e.g. "CRUNCH", "BLAM"). */
|
|
58
|
+
text: string;
|
|
59
|
+
/** Anchor in layer pixels; the word sits bottom-centre above this point. */
|
|
60
|
+
x: number;
|
|
61
|
+
y: number;
|
|
62
|
+
tone?: FeedbackWordTone;
|
|
63
|
+
size?: FeedbackWordSize;
|
|
64
|
+
accent?: FeedbackWordAccent;
|
|
65
|
+
/** Total life in milliseconds; the host removes the mark when it ends. */
|
|
66
|
+
life?: number;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* One comic sound word at a point: punchy lettering that pops in with a
|
|
70
|
+
* little overshoot, drifts up, and fades. Reduced motion keeps it still at
|
|
71
|
+
* full strength until the host removes it.
|
|
72
|
+
*/
|
|
73
|
+
export declare const FeedbackWordMark: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
74
|
+
/** Comic lettering, at most 12 characters (e.g. "CRUNCH", "BLAM"). */
|
|
75
|
+
text: string;
|
|
76
|
+
/** Anchor in layer pixels; the word sits bottom-centre above this point. */
|
|
77
|
+
x: number;
|
|
78
|
+
y: number;
|
|
79
|
+
tone?: FeedbackWordTone;
|
|
80
|
+
size?: FeedbackWordSize;
|
|
81
|
+
accent?: FeedbackWordAccent;
|
|
82
|
+
/** Total life in milliseconds; the host removes the mark when it ends. */
|
|
83
|
+
life?: number;
|
|
84
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|