@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,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimap label placement: every named mark tries its label below, above,
|
|
3
|
+
* right and left of its dot, then at its four corners, and keeps the first spot that stays inside
|
|
4
|
+
* the chart and clear of every fixed dot and every label already placed. Goals
|
|
5
|
+
* are placed first, then places in the order the game listed them, so a
|
|
6
|
+
* game lists its landmarks first. A label with no clear spot is hidden; its
|
|
7
|
+
* full name still shows on hover and focus, and reads to assistive tech.
|
|
8
|
+
* Pure and deterministic: the same marks and size give the same layout.
|
|
9
|
+
*/
|
|
10
|
+
const overlaps = (a, b) => a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom;
|
|
11
|
+
const SIDES = [
|
|
12
|
+
"below",
|
|
13
|
+
"above",
|
|
14
|
+
"right",
|
|
15
|
+
"left",
|
|
16
|
+
"below-right",
|
|
17
|
+
"below-left",
|
|
18
|
+
"above-right",
|
|
19
|
+
"above-left",
|
|
20
|
+
];
|
|
21
|
+
/** The side each mark's label takes, index for index with `marks`. */
|
|
22
|
+
export function layoutMinimapLabels(marks, metrics) {
|
|
23
|
+
const { width, height, charWidth, lineHeight, padding, dot, gap } = metrics;
|
|
24
|
+
const centre = (mark) => ({
|
|
25
|
+
x: mark.x * width,
|
|
26
|
+
y: mark.y * height,
|
|
27
|
+
});
|
|
28
|
+
const taken = marks
|
|
29
|
+
.filter((mark) => mark.solid)
|
|
30
|
+
.map((mark) => {
|
|
31
|
+
const { x, y } = centre(mark);
|
|
32
|
+
return {
|
|
33
|
+
left: x - dot / 2,
|
|
34
|
+
top: y - dot / 2,
|
|
35
|
+
right: x + dot / 2,
|
|
36
|
+
bottom: y + dot / 2,
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
const sides = marks.map(() => "hidden");
|
|
40
|
+
const order = marks
|
|
41
|
+
.map((mark, index) => ({ mark, index }))
|
|
42
|
+
.filter(({ mark }) => mark.named)
|
|
43
|
+
.sort((a, b) => Number(b.mark.goal) - Number(a.mark.goal) || a.index - b.index);
|
|
44
|
+
for (const { mark, index } of order) {
|
|
45
|
+
const { x, y } = centre(mark);
|
|
46
|
+
const w = mark.label.length * charWidth + padding * 2;
|
|
47
|
+
const h = lineHeight;
|
|
48
|
+
const reach = dot / 2 + gap;
|
|
49
|
+
const boxes = {
|
|
50
|
+
below: {
|
|
51
|
+
left: x - w / 2,
|
|
52
|
+
top: y + reach,
|
|
53
|
+
right: x + w / 2,
|
|
54
|
+
bottom: y + reach + h,
|
|
55
|
+
},
|
|
56
|
+
above: {
|
|
57
|
+
left: x - w / 2,
|
|
58
|
+
top: y - reach - h,
|
|
59
|
+
right: x + w / 2,
|
|
60
|
+
bottom: y - reach,
|
|
61
|
+
},
|
|
62
|
+
right: {
|
|
63
|
+
left: x + reach,
|
|
64
|
+
top: y - h / 2,
|
|
65
|
+
right: x + reach + w,
|
|
66
|
+
bottom: y + h / 2,
|
|
67
|
+
},
|
|
68
|
+
left: {
|
|
69
|
+
left: x - reach - w,
|
|
70
|
+
top: y - h / 2,
|
|
71
|
+
right: x - reach,
|
|
72
|
+
bottom: y + h / 2,
|
|
73
|
+
},
|
|
74
|
+
"below-right": {
|
|
75
|
+
left: x,
|
|
76
|
+
top: y + reach,
|
|
77
|
+
right: x + w,
|
|
78
|
+
bottom: y + reach + h,
|
|
79
|
+
},
|
|
80
|
+
"below-left": {
|
|
81
|
+
left: x - w,
|
|
82
|
+
top: y + reach,
|
|
83
|
+
right: x,
|
|
84
|
+
bottom: y + reach + h,
|
|
85
|
+
},
|
|
86
|
+
"above-right": {
|
|
87
|
+
left: x,
|
|
88
|
+
top: y - reach - h,
|
|
89
|
+
right: x + w,
|
|
90
|
+
bottom: y - reach,
|
|
91
|
+
},
|
|
92
|
+
"above-left": {
|
|
93
|
+
left: x - w,
|
|
94
|
+
top: y - reach - h,
|
|
95
|
+
right: x,
|
|
96
|
+
bottom: y - reach,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
for (const side of SIDES) {
|
|
100
|
+
const box = boxes[side];
|
|
101
|
+
const inside = box.left >= 0 &&
|
|
102
|
+
box.top >= 0 &&
|
|
103
|
+
box.right <= width &&
|
|
104
|
+
box.bottom <= height;
|
|
105
|
+
if (!inside || taken.some((other) => overlaps(box, other)))
|
|
106
|
+
continue;
|
|
107
|
+
taken.push(box);
|
|
108
|
+
sides[index] = side;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return sides;
|
|
113
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
/** Theme tones a Monogram may take; the current Stipe theme supplies each color. */
|
|
3
|
+
export type MonogramTone = "primary" | "secondary" | "accent" | "info" | "success" | "warning";
|
|
4
|
+
/** A stable tone for an identity: the same seed always gets the same tone. */
|
|
5
|
+
export declare function monogramTone(seed: string): MonogramTone;
|
|
6
|
+
/** Monogram scales: `default` sits inline with text; larger sizes stand alone as an identity mark. */
|
|
7
|
+
export type MonogramSize = "default" | "lg" | "xl";
|
|
8
|
+
/** Who the Monogram marks: people and Agents get a disc, Apps a rounded tile. */
|
|
9
|
+
export type MonogramSubject = "actor" | "app";
|
|
10
|
+
/** Props accepted by Monogram. */
|
|
11
|
+
export type MonogramProps = {
|
|
12
|
+
/** The name whose first letter is shown. */
|
|
13
|
+
name: string;
|
|
14
|
+
/** Stable identity that picks the tone; defaults to the name. */
|
|
15
|
+
seed?: string;
|
|
16
|
+
size?: MonogramSize;
|
|
17
|
+
subject?: MonogramSubject;
|
|
18
|
+
variant?: "initial" | "specimen";
|
|
19
|
+
} & Omit<HTMLAttributes<HTMLSpanElement>, "children">;
|
|
20
|
+
/**
|
|
21
|
+
* A small decorative identity mark: the name's first letter on a quiet tinted
|
|
22
|
+
* disc. The enclosing control owns the accessible name.
|
|
23
|
+
*/
|
|
24
|
+
export declare const Monogram: import("react").ForwardRefExoticComponent<{
|
|
25
|
+
/** The name whose first letter is shown. */
|
|
26
|
+
name: string;
|
|
27
|
+
/** Stable identity that picks the tone; defaults to the name. */
|
|
28
|
+
seed?: string;
|
|
29
|
+
size?: MonogramSize;
|
|
30
|
+
subject?: MonogramSubject;
|
|
31
|
+
variant?: "initial" | "specimen";
|
|
32
|
+
} & Omit<HTMLAttributes<HTMLSpanElement>, "children"> & import("react").RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { seedHash } from "../lib/seed-hash.js";
|
|
5
|
+
const tones = [
|
|
6
|
+
"primary",
|
|
7
|
+
"secondary",
|
|
8
|
+
"accent",
|
|
9
|
+
"info",
|
|
10
|
+
"success",
|
|
11
|
+
"warning",
|
|
12
|
+
];
|
|
13
|
+
/** A stable tone for an identity: the same seed always gets the same tone. */
|
|
14
|
+
export function monogramTone(seed) {
|
|
15
|
+
return tones[seedHash(seed) % tones.length] ?? "primary";
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A small decorative identity mark: the name's first letter on a quiet tinted
|
|
19
|
+
* disc. The enclosing control owns the accessible name.
|
|
20
|
+
*/
|
|
21
|
+
export const Monogram = forwardRef(function Monogram({ className, name, seed, size = "default", subject = "actor", variant = "initial", ...props }, ref) {
|
|
22
|
+
const letter = Array.from(name.trim())[0]?.toLocaleUpperCase() ?? "?";
|
|
23
|
+
return (_jsx("span", { "aria-hidden": "true", ...props, className: cn("caps-monogram", className), "data-subject": subject === "actor" ? undefined : subject, "data-size": size === "default" ? undefined : size, "data-tone": monogramTone(seed ?? name), ref: ref, children: variant === "specimen" ? (_jsx("svg", { viewBox: "0 0 48 48", width: "80%", height: "80%", fill: "none", "aria-hidden": "true", children: _jsxs("g", { transform: `rotate(${String((seed ?? name).split("").reduce((sum, unit) => sum + (unit.codePointAt(0) ?? 0), 0) % 360)} 24 24)`, stroke: "currentColor", strokeWidth: "1.5", children: [Array.from({ length: 20 }, (_, index) => (_jsx("path", { d: "M24 19 Q19 12 24 5", transform: `rotate(${String(index * 18)} 24 24)` }, index))), _jsx("circle", { cx: "24", cy: "24", r: "5" })] }) })) : (letter) }));
|
|
24
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** @internal Pause decorative motion outside the visible document/viewport. */
|
|
2
|
+
export function observeMotionVisibility(element, onChange) {
|
|
3
|
+
const document = element.ownerDocument;
|
|
4
|
+
let intersecting = true;
|
|
5
|
+
const update = () => {
|
|
6
|
+
onChange(intersecting && !document.hidden);
|
|
7
|
+
};
|
|
8
|
+
const browser = globalThis;
|
|
9
|
+
const observer = "IntersectionObserver" in globalThis && browser.IntersectionObserver
|
|
10
|
+
? new browser.IntersectionObserver((entries) => {
|
|
11
|
+
const entry = entries.find((candidate) => candidate.target === element);
|
|
12
|
+
if (!entry)
|
|
13
|
+
return;
|
|
14
|
+
intersecting = entry.isIntersecting;
|
|
15
|
+
update();
|
|
16
|
+
})
|
|
17
|
+
: undefined;
|
|
18
|
+
observer?.observe(element);
|
|
19
|
+
document.addEventListener("visibilitychange", update);
|
|
20
|
+
update();
|
|
21
|
+
return () => {
|
|
22
|
+
observer?.disconnect();
|
|
23
|
+
document.removeEventListener("visibilitychange", update);
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
/** Props accepted by NavigationRail. */
|
|
3
|
+
export type NavigationRailProps = HTMLAttributes<HTMLElement> & {
|
|
4
|
+
/** Let a long rail fit across wide layouts while retaining mobile scrolling. */
|
|
5
|
+
fitAtWide?: boolean;
|
|
6
|
+
};
|
|
7
|
+
/** A horizontal navigation list that signals when links continue off-screen. */
|
|
8
|
+
export declare const NavigationRail: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLElement> & {
|
|
9
|
+
/** Let a long rail fit across wide layouts while retaining mobile scrolling. */
|
|
10
|
+
fitAtWide?: boolean;
|
|
11
|
+
} & import("react").RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useEffect, useRef, useState, } from "react";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
/** A horizontal navigation list that signals when links continue off-screen. */
|
|
5
|
+
export const NavigationRail = forwardRef(function NavigationRail({ children, className, fitAtWide, ...props }, forwardedRef) {
|
|
6
|
+
const viewport = useRef(null);
|
|
7
|
+
const content = useRef(null);
|
|
8
|
+
const [edges, setEdges] = useState({
|
|
9
|
+
start: false,
|
|
10
|
+
end: false,
|
|
11
|
+
});
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const scroller = viewport.current;
|
|
14
|
+
const links = content.current;
|
|
15
|
+
if (!scroller || !links)
|
|
16
|
+
return;
|
|
17
|
+
const update = () => {
|
|
18
|
+
const maximum = scroller.scrollWidth - scroller.clientWidth;
|
|
19
|
+
const next = {
|
|
20
|
+
start: scroller.scrollLeft > 1,
|
|
21
|
+
end: maximum - scroller.scrollLeft > 1,
|
|
22
|
+
};
|
|
23
|
+
setEdges((current) => current.start === next.start && current.end === next.end
|
|
24
|
+
? current
|
|
25
|
+
: next);
|
|
26
|
+
};
|
|
27
|
+
update();
|
|
28
|
+
scroller.addEventListener("scroll", update, { passive: true });
|
|
29
|
+
window.addEventListener("resize", update);
|
|
30
|
+
const active = links.querySelector('[aria-current="page"]');
|
|
31
|
+
if (active) {
|
|
32
|
+
// Centre the active link, then land on a link boundary so no link is
|
|
33
|
+
// cut mid-word at the leading edge.
|
|
34
|
+
const target = active.offsetLeft - (scroller.clientWidth - active.clientWidth) / 2;
|
|
35
|
+
const starts = Array.from(links.children, (link) => link instanceof HTMLElement ? link.offsetLeft : 0);
|
|
36
|
+
const maximum = scroller.scrollWidth - scroller.clientWidth;
|
|
37
|
+
const boundary = starts.find((start) => start >= target);
|
|
38
|
+
if (boundary !== undefined && boundary <= maximum) {
|
|
39
|
+
scroller.scrollLeft = boundary;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Past the last boundary the rail cannot scroll on; show the link
|
|
43
|
+
// that would be cut whole instead.
|
|
44
|
+
const cut = starts.filter((start) => start < maximum).at(-1);
|
|
45
|
+
scroller.scrollLeft = cut ?? maximum;
|
|
46
|
+
}
|
|
47
|
+
update();
|
|
48
|
+
}
|
|
49
|
+
return () => {
|
|
50
|
+
scroller.removeEventListener("scroll", update);
|
|
51
|
+
window.removeEventListener("resize", update);
|
|
52
|
+
};
|
|
53
|
+
}, [children]);
|
|
54
|
+
return (_jsx("nav", { ...props, className: cn("caps-navigation-rail", fitAtWide && "caps-navigation-rail-fit-wide", className), "data-overflow-end": edges.end ? "true" : undefined, "data-overflow-start": edges.start ? "true" : undefined, ref: forwardedRef, children: _jsx("div", { className: "caps-navigation-rail-viewport", ref: viewport, children: _jsx("div", { className: "caps-navigation-rail-content", ref: content, children: children }) }) }));
|
|
55
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/** Semantic destinations; the host owns their paint order and positioning. */
|
|
3
|
+
export type OverlayLayer = "popover" | "dialog" | "toast" | "tooltip";
|
|
4
|
+
/** Host portal roots; null keeps an overlay waiting until its layer mounts. */
|
|
5
|
+
export type OverlayContainers = Readonly<Record<OverlayLayer, HTMLElement | null>>;
|
|
6
|
+
/** Supply shell portal roots without teaching Caps about the shell's layout. */
|
|
7
|
+
export declare function OverlayRoot({ containers, children, subscribeDismiss, }: {
|
|
8
|
+
containers: OverlayContainers;
|
|
9
|
+
children?: ReactNode;
|
|
10
|
+
/** Subscribe to the host losing focus to a child surface or another window. */
|
|
11
|
+
subscribeDismiss?: (listener: () => void) => () => void;
|
|
12
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
/** Wait for a provided root; standalone overlays use the current document body. */
|
|
14
|
+
export declare function useOverlayRoot(layer: OverlayLayer): HTMLElement | null;
|
|
15
|
+
/** Retire a transient overlay when its owning host requests light dismissal. */
|
|
16
|
+
export declare function useOverlayDismiss(dismiss: () => void): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useEffect, useRef, } from "react";
|
|
3
|
+
const DismissContext = createContext(undefined);
|
|
4
|
+
const Context = createContext(null);
|
|
5
|
+
/** Supply shell portal roots without teaching Caps about the shell's layout. */
|
|
6
|
+
export function OverlayRoot({ containers, children, subscribeDismiss, }) {
|
|
7
|
+
return (_jsx(Context.Provider, { value: containers, children: _jsx(DismissContext.Provider, { value: subscribeDismiss, children: children }) }));
|
|
8
|
+
}
|
|
9
|
+
/** Wait for a provided root; standalone overlays use the current document body. */
|
|
10
|
+
export function useOverlayRoot(layer) {
|
|
11
|
+
const containers = useContext(Context);
|
|
12
|
+
if (containers !== null)
|
|
13
|
+
return containers[layer];
|
|
14
|
+
return Object.hasOwn(globalThis, "document") ? document.body : null;
|
|
15
|
+
}
|
|
16
|
+
/** Retire a transient overlay when its owning host requests light dismissal. */
|
|
17
|
+
export function useOverlayDismiss(dismiss) {
|
|
18
|
+
const subscribe = useContext(DismissContext);
|
|
19
|
+
const callback = useRef(dismiss);
|
|
20
|
+
callback.current = dismiss;
|
|
21
|
+
useEffect(() => subscribe?.(() => {
|
|
22
|
+
callback.current();
|
|
23
|
+
}), [subscribe]);
|
|
24
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
/** Characters the pixel face can draw. */
|
|
3
|
+
export declare const PIXEL_NUMBER_CHARACTERS: readonly string[];
|
|
4
|
+
/** Supported PixelNumber sizes: one font pixel is 2, 3, 4 or 6 CSS pixels. */
|
|
5
|
+
export type PixelNumberSize = "sm" | "md" | "lg" | "xl";
|
|
6
|
+
/** Semantic tones supplied by the current theme. */
|
|
7
|
+
export type PixelNumberTone = "current" | "primary" | "accent" | "success" | "warning" | "error";
|
|
8
|
+
/** Props accepted by PixelNumber. Pass a `value`, or readout `text`. */
|
|
9
|
+
export type PixelNumberProps = Omit<HTMLAttributes<HTMLSpanElement>, "children"> & {
|
|
10
|
+
/** A number to draw. */
|
|
11
|
+
value?: number;
|
|
12
|
+
/** Readout text instead of a number: digits and `: . + - / × %` and spaces. */
|
|
13
|
+
text?: string;
|
|
14
|
+
/** Left-pad the whole part of `value` with zeros to this many digits. */
|
|
15
|
+
pad?: number;
|
|
16
|
+
/** Prefix a positive `value` with `+`, for gains and floating numbers. */
|
|
17
|
+
signed?: boolean;
|
|
18
|
+
size?: PixelNumberSize;
|
|
19
|
+
tone?: PixelNumberTone;
|
|
20
|
+
/**
|
|
21
|
+
* Ring every lit pixel with the ink line, like a sprite outline, so the
|
|
22
|
+
* number reads over any world: damage, pickups and floating gains.
|
|
23
|
+
*/
|
|
24
|
+
outline?: boolean;
|
|
25
|
+
};
|
|
26
|
+
/** Format a number the way PixelNumber draws it. */
|
|
27
|
+
export declare function formatPixelNumber(value: number, pad?: number, signed?: boolean): string;
|
|
28
|
+
/** Reject characters the pixel face cannot draw, naming the ones it can. */
|
|
29
|
+
export declare function checkPixelText(text: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Crisp pixel numerals drawn as SVG, so they stay sharp at every size and
|
|
32
|
+
* never depend on a font file. The readable text is kept for assistive tech.
|
|
33
|
+
*/
|
|
34
|
+
export declare const PixelNumber: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLSpanElement>, "children"> & {
|
|
35
|
+
/** A number to draw. */
|
|
36
|
+
value?: number;
|
|
37
|
+
/** Readout text instead of a number: digits and `: . + - / × %` and spaces. */
|
|
38
|
+
text?: string;
|
|
39
|
+
/** Left-pad the whole part of `value` with zeros to this many digits. */
|
|
40
|
+
pad?: number;
|
|
41
|
+
/** Prefix a positive `value` with `+`, for gains and floating numbers. */
|
|
42
|
+
signed?: boolean;
|
|
43
|
+
size?: PixelNumberSize;
|
|
44
|
+
tone?: PixelNumberTone;
|
|
45
|
+
/**
|
|
46
|
+
* Ring every lit pixel with the ink line, like a sprite outline, so the
|
|
47
|
+
* number reads over any world: damage, pickups and floating gains.
|
|
48
|
+
*/
|
|
49
|
+
outline?: boolean;
|
|
50
|
+
} & import("react").RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
/**
|
|
5
|
+
* A 3×5 pixel face for game readouts: clocks, counters, damage and pickups.
|
|
6
|
+
* Each glyph is rows of lit columns, top to bottom.
|
|
7
|
+
*/
|
|
8
|
+
const GLYPHS = new Map([
|
|
9
|
+
["0", ["###", "#.#", "#.#", "#.#", "###"]],
|
|
10
|
+
["1", [".#.", "##.", ".#.", ".#.", "###"]],
|
|
11
|
+
["2", ["###", "..#", "###", "#..", "###"]],
|
|
12
|
+
["3", ["###", "..#", ".##", "..#", "###"]],
|
|
13
|
+
["4", ["#.#", "#.#", "###", "..#", "..#"]],
|
|
14
|
+
["5", ["###", "#..", "###", "..#", "###"]],
|
|
15
|
+
["6", ["###", "#..", "###", "#.#", "###"]],
|
|
16
|
+
["7", ["###", "..#", ".#.", ".#.", ".#."]],
|
|
17
|
+
["8", ["###", "#.#", "###", "#.#", "###"]],
|
|
18
|
+
["9", ["###", "#.#", "###", "..#", "###"]],
|
|
19
|
+
[":", [".", "#", ".", "#", "."]],
|
|
20
|
+
[".", [".", ".", ".", ".", "#"]],
|
|
21
|
+
["+", ["...", ".#.", "###", ".#.", "..."]],
|
|
22
|
+
["-", ["...", "...", "###", "...", "..."]],
|
|
23
|
+
["/", ["..#", "..#", ".#.", "#..", "#.."]],
|
|
24
|
+
["×", ["...", "#.#", ".#.", "#.#", "..."]],
|
|
25
|
+
["%", ["#.#", "..#", ".#.", "#..", "#.#"]],
|
|
26
|
+
[" ", [".", ".", ".", ".", "."]],
|
|
27
|
+
]);
|
|
28
|
+
const BLANK = [".", ".", ".", ".", "."];
|
|
29
|
+
/** Characters the pixel face can draw. */
|
|
30
|
+
export const PIXEL_NUMBER_CHARACTERS = Object.freeze([...GLYPHS.keys()]);
|
|
31
|
+
/** Format a number the way PixelNumber draws it. */
|
|
32
|
+
export function formatPixelNumber(value, pad = 0, signed = false) {
|
|
33
|
+
if (!Number.isFinite(value))
|
|
34
|
+
throw new RangeError("PixelNumber needs a finite number");
|
|
35
|
+
const whole = String(Math.trunc(Math.abs(value))).padStart(pad, "0");
|
|
36
|
+
const fraction = String(Math.abs(value)).split(".")[1];
|
|
37
|
+
const sign = value < 0 ? "-" : signed && value > 0 ? "+" : "";
|
|
38
|
+
return checkPixelText(`${sign}${whole}${fraction ? `.${fraction}` : ""}`);
|
|
39
|
+
}
|
|
40
|
+
/** Reject characters the pixel face cannot draw, naming the ones it can. */
|
|
41
|
+
export function checkPixelText(text) {
|
|
42
|
+
for (const character of text)
|
|
43
|
+
if (!GLYPHS.has(character))
|
|
44
|
+
throw new RangeError(`PixelNumber cannot draw "${character}"; use digits and ${PIXEL_NUMBER_CHARACTERS.filter((glyph) => !/\d/.test(glyph)).join(" ")}`);
|
|
45
|
+
return text;
|
|
46
|
+
}
|
|
47
|
+
/** Lit pixels of a string in font pixels, shifted right and down by `margin`. */
|
|
48
|
+
function litPixels(text, margin) {
|
|
49
|
+
const lit = new Set();
|
|
50
|
+
let cursor = margin;
|
|
51
|
+
for (const character of text) {
|
|
52
|
+
const glyph = GLYPHS.get(character) ?? BLANK;
|
|
53
|
+
glyph.forEach((row, y) => {
|
|
54
|
+
for (let x = 0; x < row.length; x++)
|
|
55
|
+
if (row.charAt(x) === "#")
|
|
56
|
+
lit.add(`${String(cursor + x)},${String(y + margin)}`);
|
|
57
|
+
});
|
|
58
|
+
cursor += (glyph.at(0)?.length ?? 1) + 1;
|
|
59
|
+
}
|
|
60
|
+
return { lit, width: Math.max(1, cursor - 1 - margin) + margin * 2 };
|
|
61
|
+
}
|
|
62
|
+
/** Ink pixels touching a lit pixel on any side or corner, but not lit. */
|
|
63
|
+
function outlinePixels(lit) {
|
|
64
|
+
const ring = new Set();
|
|
65
|
+
for (const key of lit) {
|
|
66
|
+
const [x = 0, y = 0] = key.split(",").map(Number);
|
|
67
|
+
for (let dy = -1; dy <= 1; dy++)
|
|
68
|
+
for (let dx = -1; dx <= 1; dx++) {
|
|
69
|
+
const next = `${String(x + dx)},${String(y + dy)}`;
|
|
70
|
+
if (!lit.has(next))
|
|
71
|
+
ring.add(next);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return ring;
|
|
75
|
+
}
|
|
76
|
+
/** Horizontal runs `[x, y, width]` covering a pixel set, for fewer rects. */
|
|
77
|
+
function pixelRuns(pixels, width, height) {
|
|
78
|
+
const runs = [];
|
|
79
|
+
for (let y = 0; y < height; y++) {
|
|
80
|
+
let start = -1;
|
|
81
|
+
for (let x = 0; x <= width; x++) {
|
|
82
|
+
const on = pixels.has(`${String(x)},${String(y)}`);
|
|
83
|
+
if (on && start < 0)
|
|
84
|
+
start = x;
|
|
85
|
+
if (!on && start >= 0) {
|
|
86
|
+
runs.push([start, y, x - start]);
|
|
87
|
+
start = -1;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return runs;
|
|
92
|
+
}
|
|
93
|
+
function Runs({ runs, className, }) {
|
|
94
|
+
return (_jsx("g", { className: className, children: runs.map(([x, y, run]) => (_jsx("rect", { x: x, y: y, width: run, height: 1 }, `${String(x)}-${String(y)}`))) }));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Crisp pixel numerals drawn as SVG, so they stay sharp at every size and
|
|
98
|
+
* never depend on a font file. The readable text is kept for assistive tech.
|
|
99
|
+
*/
|
|
100
|
+
export const PixelNumber = forwardRef(function PixelNumber({ value, text, pad, signed, size = "md", tone = "current", outline = false, className, ...props }, ref) {
|
|
101
|
+
if (text === undefined && value === undefined)
|
|
102
|
+
throw new TypeError("PixelNumber needs a value or text");
|
|
103
|
+
const shown = text === undefined
|
|
104
|
+
? formatPixelNumber(value ?? Number.NaN, pad, signed)
|
|
105
|
+
: checkPixelText(text);
|
|
106
|
+
const margin = outline ? 1 : 0;
|
|
107
|
+
const { lit, width } = litPixels(shown, margin);
|
|
108
|
+
const height = 5 + margin * 2;
|
|
109
|
+
const scale = PIXEL_SCALE[size];
|
|
110
|
+
return (_jsxs("span", { ...props, ref: ref, className: cn("caps-pixel-number", className), "data-size": size, "data-tone": tone, "data-outline": outline, children: [_jsxs("svg", { "aria-hidden": "true", focusable: "false", viewBox: `0 0 ${String(width)} ${String(height)}`, width: width * scale, height: height * scale, children: [outline ? (_jsx(Runs, { className: "caps-pixel-number-outline", runs: pixelRuns(outlinePixels(lit), width, height) })) : null, _jsx(Runs, { className: "caps-pixel-number-face", runs: pixelRuns(lit, width, height) })] }), _jsx("span", { className: "sr-only", children: shown })] }));
|
|
111
|
+
});
|
|
112
|
+
/** CSS pixels per font pixel. */
|
|
113
|
+
const PIXEL_SCALE = { sm: 2, md: 3, lg: 4, xl: 6 };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
/** The positioned box a Popover attaches to. */
|
|
3
|
+
export declare const PopoverAnchor: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
|
|
4
|
+
/** Props accepted by Popover. */
|
|
5
|
+
export type PopoverProps = HTMLAttributes<HTMLDivElement> & {
|
|
6
|
+
/** Whether the popover is shown. */
|
|
7
|
+
open: boolean;
|
|
8
|
+
/** Which edge of the anchor it opens from. */
|
|
9
|
+
side?: "top" | "bottom";
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* A non-modal raised panel attached to its PopoverAnchor. It never takes
|
|
13
|
+
* focus, so the anchored control (for example a composer) keeps typing while
|
|
14
|
+
* the popover lists suggestions.
|
|
15
|
+
*/
|
|
16
|
+
export declare const Popover: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
17
|
+
/** Whether the popover is shown. */
|
|
18
|
+
open: boolean;
|
|
19
|
+
/** Which edge of the anchor it opens from. */
|
|
20
|
+
side?: "top" | "bottom";
|
|
21
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
22
|
+
/** Props accepted by PopoverOption. */
|
|
23
|
+
export type PopoverOptionProps = HTMLAttributes<HTMLDivElement> & {
|
|
24
|
+
/** Whether keyboard navigation currently highlights this option. */
|
|
25
|
+
active: boolean;
|
|
26
|
+
};
|
|
27
|
+
/** One option row inside a Popover list; pointer presses keep focus where it is. */
|
|
28
|
+
export declare const PopoverOption: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
29
|
+
/** Whether keyboard navigation currently highlights this option. */
|
|
30
|
+
active: boolean;
|
|
31
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
/** The positioned box a Popover attaches to. */
|
|
5
|
+
export const PopoverAnchor = forwardRef(function PopoverAnchor({ className, ...props }, ref) {
|
|
6
|
+
return (_jsx("div", { ...props, ref: ref, className: cn("caps-popover-anchor", className) }));
|
|
7
|
+
});
|
|
8
|
+
/**
|
|
9
|
+
* A non-modal raised panel attached to its PopoverAnchor. It never takes
|
|
10
|
+
* focus, so the anchored control (for example a composer) keeps typing while
|
|
11
|
+
* the popover lists suggestions.
|
|
12
|
+
*/
|
|
13
|
+
export const Popover = forwardRef(function Popover({ className, open, side = "top", ...props }, ref) {
|
|
14
|
+
if (!open)
|
|
15
|
+
return null;
|
|
16
|
+
return (_jsx("div", { ...props, ref: ref, className: cn("caps-popover", className), "data-side": side }));
|
|
17
|
+
});
|
|
18
|
+
/** One option row inside a Popover list; pointer presses keep focus where it is. */
|
|
19
|
+
export const PopoverOption = forwardRef(function PopoverOption({ active, className, onMouseDown, ...props }, ref) {
|
|
20
|
+
return (_jsx("div", { role: "option", "aria-selected": active, ...props, ref: ref, className: cn("caps-popover-option", className), "data-active": active, onMouseDown: (event) => {
|
|
21
|
+
event.preventDefault();
|
|
22
|
+
onMouseDown?.(event);
|
|
23
|
+
} }));
|
|
24
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
/** Props accepted by Presence. */
|
|
3
|
+
export type PresenceProps = {
|
|
4
|
+
/** Whether the content is present. Leaving content stays mounted until it has left. */
|
|
5
|
+
show: boolean;
|
|
6
|
+
/** Animate the first mount too (for content that arrives after the page). */
|
|
7
|
+
appear?: boolean;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
} & HTMLAttributes<HTMLDivElement>;
|
|
10
|
+
/**
|
|
11
|
+
* Content that arrives and leaves instead of popping: it makes room, fades in
|
|
12
|
+
* while travelling the theme's `--stipe-motion-distance`, and on leaving fades
|
|
13
|
+
* out and gives its room back before it unmounts. Reduced motion keeps a short
|
|
14
|
+
* fade only.
|
|
15
|
+
*/
|
|
16
|
+
export declare const Presence: import("react").ForwardRefExoticComponent<{
|
|
17
|
+
/** Whether the content is present. Leaving content stays mounted until it has left. */
|
|
18
|
+
show: boolean;
|
|
19
|
+
/** Animate the first mount too (for content that arrives after the page). */
|
|
20
|
+
appear?: boolean;
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
} & HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
|
|
23
|
+
/** Props accepted by Collapse. */
|
|
24
|
+
export type CollapseProps = {
|
|
25
|
+
/** Open: the content takes its height. Closed: no space, and out of the accessibility tree. */
|
|
26
|
+
open: boolean;
|
|
27
|
+
children: ReactNode;
|
|
28
|
+
} & HTMLAttributes<HTMLDivElement>;
|
|
29
|
+
/**
|
|
30
|
+
* Content that opens and closes its own space: an inserted alert, a field
|
|
31
|
+
* error, a disclosure body. Height and opacity follow the theme's tempo; under
|
|
32
|
+
* reduced motion the space changes at once and only the fade remains.
|
|
33
|
+
*/
|
|
34
|
+
export declare const Collapse: import("react").ForwardRefExoticComponent<{
|
|
35
|
+
/** Open: the content takes its height. Closed: no space, and out of the accessibility tree. */
|
|
36
|
+
open: boolean;
|
|
37
|
+
children: ReactNode;
|
|
38
|
+
} & HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
|
|
39
|
+
/** Props accepted by Swap. */
|
|
40
|
+
export type SwapProps = {
|
|
41
|
+
/** Names the current content. A new key crossfades from the old content to the new. */
|
|
42
|
+
swapKey: string;
|
|
43
|
+
/** Which edge leaving content holds to while the box resizes; `end` for right-aligned actions. */
|
|
44
|
+
anchor?: "start" | "end";
|
|
45
|
+
children: ReactNode;
|
|
46
|
+
} & HTMLAttributes<HTMLDivElement>;
|
|
47
|
+
/**
|
|
48
|
+
* One piece of content replaced by another without a hard cut: loading to
|
|
49
|
+
* loaded, "Saving…" to "Saved". The old content fades out (blurred by the
|
|
50
|
+
* theme's `--stipe-motion-blur`) as the new one arrives, and the box eases to
|
|
51
|
+
* the new size. Reduced motion keeps the crossfade and drops the travel.
|
|
52
|
+
*/
|
|
53
|
+
export declare const Swap: import("react").ForwardRefExoticComponent<{
|
|
54
|
+
/** Names the current content. A new key crossfades from the old content to the new. */
|
|
55
|
+
swapKey: string;
|
|
56
|
+
/** Which edge leaving content holds to while the box resizes; `end` for right-aligned actions. */
|
|
57
|
+
anchor?: "start" | "end";
|
|
58
|
+
children: ReactNode;
|
|
59
|
+
} & HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
|