@seekalfred/primitives 0.6.0 → 0.7.3
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/README.md +214 -15
- package/dist/activity-heatmap/activity-heatmap.d.ts +92 -0
- package/dist/activity-heatmap/activity-heatmap.js +699 -0
- package/dist/activity-heatmap/activity-heatmap.js.map +1 -0
- package/dist/activity-heatmap/index.d.ts +2 -0
- package/dist/activity-heatmap/index.js +2 -0
- package/dist/alert/alert.d.ts +53 -4
- package/dist/alert/alert.js +100 -11
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert/index.d.ts +2 -2
- package/dist/alert/index.js +2 -2
- package/dist/alert-dialog/alert-dialog.d.ts +58 -18
- package/dist/alert-dialog/alert-dialog.js +152 -9
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/alert-dialog/index.d.ts +2 -2
- package/dist/alert-dialog/index.js +2 -2
- package/dist/animated-counter/animated-counter.d.ts +38 -0
- package/dist/animated-counter/animated-counter.js +230 -0
- package/dist/animated-counter/animated-counter.js.map +1 -0
- package/dist/animated-counter/index.d.ts +2 -0
- package/dist/animated-counter/index.js +2 -0
- package/dist/announcement-bar/announcement-bar.d.ts +67 -0
- package/dist/announcement-bar/announcement-bar.js +364 -0
- package/dist/announcement-bar/announcement-bar.js.map +1 -0
- package/dist/announcement-bar/index.d.ts +2 -0
- package/dist/announcement-bar/index.js +2 -0
- package/dist/badge/badge.d.ts +1 -1
- package/dist/bar-chart/bar-chart.d.ts +51 -0
- package/dist/bar-chart/bar-chart.js +565 -0
- package/dist/bar-chart/bar-chart.js.map +1 -0
- package/dist/bar-chart/index.d.ts +2 -0
- package/dist/bar-chart/index.js +2 -0
- package/dist/breadcrumb/breadcrumb.d.ts +2 -2
- package/dist/breadcrumb/breadcrumb.js +12 -3
- package/dist/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/brush-chart/brush-chart.d.ts +64 -0
- package/dist/brush-chart/brush-chart.js +1056 -0
- package/dist/brush-chart/brush-chart.js.map +1 -0
- package/dist/brush-chart/index.d.ts +2 -0
- package/dist/brush-chart/index.js +2 -0
- package/dist/button/button.d.ts +1 -1
- package/dist/button/button.js +31 -21
- package/dist/button/button.js.map +1 -1
- package/dist/button-group/button-group.js +3 -4
- package/dist/button-group/button-group.js.map +1 -1
- package/dist/calendar/calendar.d.ts +1 -1
- package/dist/card/card.d.ts +22 -1
- package/dist/card/card.js +60 -7
- package/dist/card/card.js.map +1 -1
- package/dist/card-stack/card-stack.d.ts +54 -0
- package/dist/card-stack/card-stack.js +492 -0
- package/dist/card-stack/card-stack.js.map +1 -0
- package/dist/card-stack/index.d.ts +2 -0
- package/dist/card-stack/index.js +2 -0
- package/dist/carousel/carousel.d.ts +77 -0
- package/dist/carousel/carousel.js +415 -0
- package/dist/carousel/carousel.js.map +1 -0
- package/dist/carousel/index.d.ts +2 -0
- package/dist/carousel/index.js +2 -0
- package/dist/checkbox/checkbox.js +4 -4
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/chip-group/chip-group.d.ts +37 -0
- package/dist/chip-group/chip-group.js +148 -0
- package/dist/chip-group/chip-group.js.map +1 -0
- package/dist/chip-group/index.d.ts +2 -0
- package/dist/chip-group/index.js +2 -0
- package/dist/copy-button/copy-button.d.ts +18 -2
- package/dist/copy-button/copy-button.js +29 -8
- package/dist/copy-button/copy-button.js.map +1 -1
- package/dist/countdown/countdown.d.ts +45 -0
- package/dist/countdown/countdown.js +298 -0
- package/dist/countdown/countdown.js.map +1 -0
- package/dist/countdown/index.d.ts +2 -0
- package/dist/countdown/index.js +2 -0
- package/dist/date-picker/picker-parts.js +1 -1
- package/dist/date-picker/picker-parts.js.map +1 -1
- package/dist/dialog/dialog.js +1 -1
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/donut-chart/donut-chart.d.ts +86 -0
- package/dist/donut-chart/donut-chart.js +593 -0
- package/dist/donut-chart/donut-chart.js.map +1 -0
- package/dist/donut-chart/index.d.ts +2 -0
- package/dist/donut-chart/index.js +2 -0
- package/dist/empty-state/empty-state.d.ts +42 -10
- package/dist/empty-state/empty-state.js +44 -26
- package/dist/empty-state/empty-state.js.map +1 -1
- package/dist/empty-state/index.d.ts +2 -2
- package/dist/empty-state/index.js +2 -2
- package/dist/file-drop-zone/file-drop-zone.js +1 -1
- package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
- package/dist/filter-bar/filter-bar.d.ts +50 -26
- package/dist/filter-bar/filter-bar.js +317 -157
- package/dist/filter-bar/filter-bar.js.map +1 -1
- package/dist/gauge/gauge.d.ts +45 -0
- package/dist/gauge/gauge.js +219 -0
- package/dist/gauge/gauge.js.map +1 -0
- package/dist/gauge/index.d.ts +2 -0
- package/dist/gauge/index.js +2 -0
- package/dist/in-view-title/in-view-title.d.ts +33 -0
- package/dist/in-view-title/in-view-title.js +119 -0
- package/dist/in-view-title/in-view-title.js.map +1 -0
- package/dist/in-view-title/index.d.ts +2 -0
- package/dist/in-view-title/index.js +2 -0
- package/dist/index.d.ts +53 -10
- package/dist/index.js +52 -12
- package/dist/input/input.js +4 -4
- package/dist/input/input.js.map +1 -1
- package/dist/lib/cn.js +11 -1
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/field-message.js +28 -10
- package/dist/lib/field-message.js.map +1 -1
- package/dist/lib/field-shell.js +10 -3
- package/dist/lib/field-shell.js.map +1 -1
- package/dist/lib/field-surface.js +31 -9
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/flag-emoji.js +50 -0
- package/dist/lib/flag-emoji.js.map +1 -0
- package/dist/lib/flip.js +11 -3
- package/dist/lib/flip.js.map +1 -1
- package/dist/lib/menu-highlight.js +64 -0
- package/dist/lib/menu-highlight.js.map +1 -0
- package/dist/lib/motion.js +9 -2
- package/dist/lib/motion.js.map +1 -1
- package/dist/lib/rolling-value.js +1 -1
- package/dist/lib/rolling-value.js.map +1 -1
- package/dist/lib/spring-tween.js +500 -0
- package/dist/lib/spring-tween.js.map +1 -0
- package/dist/lib/swap-text.js +3 -1
- package/dist/lib/swap-text.js.map +1 -1
- package/dist/lib/use-copy-feedback.js +17 -3
- package/dist/lib/use-copy-feedback.js.map +1 -1
- package/dist/line-chart/index.d.ts +2 -0
- package/dist/line-chart/index.js +2 -0
- package/dist/line-chart/line-chart.d.ts +116 -0
- package/dist/line-chart/line-chart.js +740 -0
- package/dist/line-chart/line-chart.js.map +1 -0
- package/dist/mention-input/index.d.ts +2 -0
- package/dist/mention-input/index.js +2 -0
- package/dist/mention-input/mention-input.d.ts +98 -0
- package/dist/mention-input/mention-input.js +576 -0
- package/dist/mention-input/mention-input.js.map +1 -0
- package/dist/menu/menu.d.ts +2 -0
- package/dist/menu/menu.js +23 -12
- package/dist/menu/menu.js.map +1 -1
- package/dist/money-input/index.d.ts +2 -0
- package/dist/money-input/index.js +2 -0
- package/dist/money-input/money-input.d.ts +86 -0
- package/dist/money-input/money-input.js +613 -0
- package/dist/money-input/money-input.js.map +1 -0
- package/dist/morph-select/index.d.ts +2 -0
- package/dist/morph-select/index.js +2 -0
- package/dist/morph-select/morph-select.d.ts +89 -0
- package/dist/morph-select/morph-select.js +712 -0
- package/dist/morph-select/morph-select.js.map +1 -0
- package/dist/multi-select/index.d.ts +2 -0
- package/dist/multi-select/index.js +2 -0
- package/dist/multi-select/multi-select.d.ts +77 -0
- package/dist/multi-select/multi-select.js +209 -0
- package/dist/multi-select/multi-select.js.map +1 -0
- package/dist/number-field/index.d.ts +2 -0
- package/dist/number-field/index.js +2 -0
- package/dist/number-field/number-field.d.ts +42 -0
- package/dist/number-field/number-field.js +199 -0
- package/dist/number-field/number-field.js.map +1 -0
- package/dist/onboarding-checklist/index.d.ts +2 -0
- package/dist/onboarding-checklist/index.js +2 -0
- package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
- package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
- package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
- package/dist/password-field/password-field.d.ts +16 -0
- package/dist/password-field/password-field.js +39 -10
- package/dist/password-field/password-field.js.map +1 -1
- package/dist/phone-input/index.d.ts +3 -0
- package/dist/phone-input/index.js +3 -0
- package/dist/phone-input/phone-countries.d.ts +29 -0
- package/dist/phone-input/phone-countries.js +170 -0
- package/dist/phone-input/phone-countries.js.map +1 -0
- package/dist/phone-input/phone-input.d.ts +66 -0
- package/dist/phone-input/phone-input.js +440 -0
- package/dist/phone-input/phone-input.js.map +1 -0
- package/dist/popover/index.d.ts +2 -2
- package/dist/popover/index.js +2 -2
- package/dist/popover/popover.d.ts +51 -2
- package/dist/popover/popover.js +59 -8
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.d.ts +16 -2
- package/dist/progress/progress.js +16 -9
- package/dist/progress/progress.js.map +1 -1
- package/dist/radio-group/index.d.ts +2 -2
- package/dist/radio-group/index.js +2 -2
- package/dist/radio-group/radio-group.d.ts +33 -3
- package/dist/radio-group/radio-group.js +110 -10
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/reactions/index.d.ts +2 -0
- package/dist/reactions/index.js +2 -0
- package/dist/reactions/reactions.d.ts +50 -0
- package/dist/reactions/reactions.js +315 -0
- package/dist/reactions/reactions.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +3 -0
- package/dist/rich-text-editor/index.js +2 -0
- package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
- package/dist/rich-text-editor/rich-text-editor.js +909 -0
- package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
- package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
- package/dist/rich-text-editor/rich-text-engine.js +366 -0
- package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
- package/dist/rich-text-editor/rich-text-html.js +174 -0
- package/dist/rich-text-editor/rich-text-html.js.map +1 -0
- package/dist/rich-text-editor/rich-text-icons.js +43 -0
- package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
- package/dist/ridgeline/index.d.ts +2 -0
- package/dist/ridgeline/index.js +2 -0
- package/dist/ridgeline/ridgeline.d.ts +55 -0
- package/dist/ridgeline/ridgeline.js +701 -0
- package/dist/ridgeline/ridgeline.js.map +1 -0
- package/dist/scroll-area/index.d.ts +2 -2
- package/dist/scroll-area/index.js +2 -2
- package/dist/scroll-area/scroll-area.d.ts +68 -31
- package/dist/scroll-area/scroll-area.js +212 -62
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/scrub-input/evaluate.d.ts +14 -0
- package/dist/scrub-input/evaluate.js +72 -0
- package/dist/scrub-input/evaluate.js.map +1 -0
- package/dist/scrub-input/index.d.ts +3 -0
- package/dist/scrub-input/index.js +3 -0
- package/dist/scrub-input/scrub-input.d.ts +74 -0
- package/dist/scrub-input/scrub-input.js +486 -0
- package/dist/scrub-input/scrub-input.js.map +1 -0
- package/dist/search-field/search-field.d.ts +24 -2
- package/dist/search-field/search-field.js +24 -9
- package/dist/search-field/search-field.js.map +1 -1
- package/dist/select/select.d.ts +3 -1
- package/dist/select/select.js +28 -14
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +22 -10
- package/dist/separator/separator.js +33 -11
- package/dist/separator/separator.js.map +1 -1
- package/dist/sheet/sheet.js +2 -1
- package/dist/sheet/sheet.js.map +1 -1
- package/dist/shortcut-recorder/index.d.ts +3 -0
- package/dist/shortcut-recorder/index.js +3 -0
- package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
- package/dist/shortcut-recorder/shortcut-model.js +206 -0
- package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
- package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
- package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
- package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
- package/dist/slider/index.d.ts +2 -2
- package/dist/slider/slider.d.ts +46 -14
- package/dist/slider/slider.js +151 -67
- package/dist/slider/slider.js.map +1 -1
- package/dist/slope-chart/index.d.ts +2 -0
- package/dist/slope-chart/index.js +2 -0
- package/dist/slope-chart/slope-chart.d.ts +54 -0
- package/dist/slope-chart/slope-chart.js +568 -0
- package/dist/slope-chart/slope-chart.js.map +1 -0
- package/dist/sparkline/index.d.ts +2 -0
- package/dist/sparkline/index.js +2 -0
- package/dist/sparkline/sparkline.d.ts +46 -0
- package/dist/sparkline/sparkline.js +390 -0
- package/dist/sparkline/sparkline.js.map +1 -0
- package/dist/split-bar/index.d.ts +2 -0
- package/dist/split-bar/index.js +2 -0
- package/dist/split-bar/split-bar.d.ts +62 -0
- package/dist/split-bar/split-bar.js +513 -0
- package/dist/split-bar/split-bar.js.map +1 -0
- package/dist/split-button/split-button.d.ts +1 -1
- package/dist/split-button/split-button.js +12 -11
- package/dist/split-button/split-button.js.map +1 -1
- package/dist/stat-card/index.d.ts +2 -2
- package/dist/stat-card/index.js +2 -2
- package/dist/stat-card/stat-card.d.ts +58 -5
- package/dist/stat-card/stat-card.js +144 -17
- package/dist/stat-card/stat-card.js.map +1 -1
- package/dist/stepper/index.d.ts +2 -0
- package/dist/stepper/index.js +2 -0
- package/dist/stepper/stepper.d.ts +51 -0
- package/dist/stepper/stepper.js +215 -0
- package/dist/stepper/stepper.js.map +1 -0
- package/dist/streamgraph/index.d.ts +2 -0
- package/dist/streamgraph/index.js +2 -0
- package/dist/streamgraph/streamgraph.d.ts +70 -0
- package/dist/streamgraph/streamgraph.js +613 -0
- package/dist/streamgraph/streamgraph.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/swipe-actions/index.d.ts +2 -0
- package/dist/swipe-actions/index.js +2 -0
- package/dist/swipe-actions/swipe-actions.d.ts +67 -0
- package/dist/swipe-actions/swipe-actions.js +552 -0
- package/dist/swipe-actions/swipe-actions.js.map +1 -0
- package/dist/switch/switch.js +2 -2
- package/dist/switch/switch.js.map +1 -1
- package/dist/table/index.d.ts +2 -2
- package/dist/table/index.js +2 -2
- package/dist/table/table.d.ts +55 -3
- package/dist/table/table.js +340 -165
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.js +2 -2
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/tag-input/index.d.ts +2 -0
- package/dist/tag-input/index.js +2 -0
- package/dist/tag-input/tag-input.d.ts +40 -0
- package/dist/tag-input/tag-input.js +269 -0
- package/dist/tag-input/tag-input.js.map +1 -0
- package/dist/text-morph/index.d.ts +2 -0
- package/dist/text-morph/index.js +2 -0
- package/dist/text-morph/text-morph.d.ts +23 -0
- package/dist/text-morph/text-morph.js +21 -0
- package/dist/text-morph/text-morph.js.map +1 -0
- package/dist/text-reveal/index.d.ts +2 -0
- package/dist/text-reveal/index.js +2 -0
- package/dist/text-reveal/text-reveal.d.ts +27 -0
- package/dist/text-reveal/text-reveal.js +51 -0
- package/dist/text-reveal/text-reveal.js.map +1 -0
- package/dist/text-shimmer/index.d.ts +2 -0
- package/dist/text-shimmer/index.js +2 -0
- package/dist/text-shimmer/text-shimmer.d.ts +29 -0
- package/dist/text-shimmer/text-shimmer.js +29 -0
- package/dist/text-shimmer/text-shimmer.js.map +1 -0
- package/dist/time-picker/time-picker.d.ts +36 -1
- package/dist/time-picker/time-picker.js +35 -7
- package/dist/time-picker/time-picker.js.map +1 -1
- package/dist/toast/index.d.ts +2 -2
- package/dist/toast/index.js +2 -2
- package/dist/toast/toast.d.ts +7 -1
- package/dist/toast/toast.js +82 -40
- package/dist/toast/toast.js.map +1 -1
- package/dist/tooltip/tooltip.d.ts +9 -2
- package/dist/tooltip/tooltip.js +7 -7
- package/dist/tooltip/tooltip.js.map +1 -1
- package/dist/treemap/index.d.ts +2 -0
- package/dist/treemap/index.js +2 -0
- package/dist/treemap/treemap.d.ts +53 -0
- package/dist/treemap/treemap.js +699 -0
- package/dist/treemap/treemap.js.map +1 -0
- package/dist/usage-meter/index.d.ts +2 -0
- package/dist/usage-meter/index.js +2 -0
- package/dist/usage-meter/usage-meter.d.ts +80 -0
- package/dist/usage-meter/usage-meter.js +458 -0
- package/dist/usage-meter/usage-meter.js.map +1 -0
- package/dist/waffle-chart/index.d.ts +2 -0
- package/dist/waffle-chart/index.js +2 -0
- package/dist/waffle-chart/waffle-chart.d.ts +57 -0
- package/dist/waffle-chart/waffle-chart.js +426 -0
- package/dist/waffle-chart/waffle-chart.js.map +1 -0
- package/package.json +165 -3
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn, cnAll } from "../lib/cn.js";
|
|
3
|
+
import { fieldSurfaceVariants } from "../lib/field-surface.js";
|
|
4
|
+
import { FieldShell, useFieldWiring } from "../lib/field-shell.js";
|
|
5
|
+
import { formatShortcut, keyId, modifiersFromEvent, normalizeShortcut, shortcutFromEvent, shortcutTokens, splitShortcut } from "./shortcut-model.js";
|
|
6
|
+
import { forwardRef, useCallback, useEffect, useId, useRef, useState, useSyncExternalStore } from "react";
|
|
7
|
+
import { Button } from "react-aria-components";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
//#region src/shortcut-recorder/shortcut-recorder.tsx
|
|
10
|
+
const subscribe = () => () => {};
|
|
11
|
+
const detectPlatform = () => {
|
|
12
|
+
if (typeof navigator === "undefined") return "mac";
|
|
13
|
+
const hint = navigator.userAgentData?.platform ?? navigator.platform ?? navigator.userAgent;
|
|
14
|
+
return /mac|iphone|ipad|ipod/i.test(hint) ? "mac" : "other";
|
|
15
|
+
};
|
|
16
|
+
/** The viewer's platform. A server render assumes a Mac and settles on the first client render. */
|
|
17
|
+
function usePlatform(override) {
|
|
18
|
+
const detected = useSyncExternalStore(subscribe, detectPlatform, () => "mac");
|
|
19
|
+
return override ?? detected;
|
|
20
|
+
}
|
|
21
|
+
const EMPTY = /* @__PURE__ */ new Set();
|
|
22
|
+
/**
|
|
23
|
+
* The keys held down right now, by physical id (feed it to `ShortcutKeys` to light the
|
|
24
|
+
* caps as they are pressed). Clears when the window loses focus, and after Command lifts
|
|
25
|
+
* (macOS drops the other keyups).
|
|
26
|
+
*/
|
|
27
|
+
function usePressedKeys(enabled = true) {
|
|
28
|
+
const [held, setHeld] = useState(() => /* @__PURE__ */ new Set());
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!enabled) return;
|
|
31
|
+
const update = (change) => setHeld((current) => {
|
|
32
|
+
const next = new Set(current);
|
|
33
|
+
change(next);
|
|
34
|
+
return next.size === current.size && [...next].every((key) => current.has(key)) ? current : next;
|
|
35
|
+
});
|
|
36
|
+
const down = (event) => {
|
|
37
|
+
const id = keyId(event);
|
|
38
|
+
if (id) update((next) => next.add(id));
|
|
39
|
+
};
|
|
40
|
+
const up = (event) => {
|
|
41
|
+
const id = keyId(event);
|
|
42
|
+
update((next) => {
|
|
43
|
+
if (id) next.delete(id);
|
|
44
|
+
if (id === "meta") {
|
|
45
|
+
for (const key of [...next]) if (![
|
|
46
|
+
"ctrl",
|
|
47
|
+
"alt",
|
|
48
|
+
"shift"
|
|
49
|
+
].includes(key)) next.delete(key);
|
|
50
|
+
}
|
|
51
|
+
if (!event.metaKey) next.delete("meta");
|
|
52
|
+
if (!event.ctrlKey) next.delete("ctrl");
|
|
53
|
+
if (!event.altKey) next.delete("alt");
|
|
54
|
+
if (!event.shiftKey) next.delete("shift");
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
const clear = () => setHeld((current) => current.size ? /* @__PURE__ */ new Set() : current);
|
|
58
|
+
window.addEventListener("keydown", down, true);
|
|
59
|
+
window.addEventListener("keyup", up, true);
|
|
60
|
+
window.addEventListener("blur", clear);
|
|
61
|
+
document.addEventListener("visibilitychange", clear);
|
|
62
|
+
return () => {
|
|
63
|
+
window.removeEventListener("keydown", down, true);
|
|
64
|
+
window.removeEventListener("keyup", up, true);
|
|
65
|
+
window.removeEventListener("blur", clear);
|
|
66
|
+
document.removeEventListener("visibilitychange", clear);
|
|
67
|
+
};
|
|
68
|
+
}, [enabled]);
|
|
69
|
+
return enabled ? held : EMPTY;
|
|
70
|
+
}
|
|
71
|
+
/** One key cap. Use it inline in copy, or through `ShortcutKeys` for a whole combination. */
|
|
72
|
+
const ShortcutKbd = forwardRef(function ShortcutKbd({ children, pressed = false, tone, size = "md", className, ...props }, ref) {
|
|
73
|
+
return /* @__PURE__ */ jsx("kbd", {
|
|
74
|
+
ref,
|
|
75
|
+
...props,
|
|
76
|
+
"data-slot": "shortcut-kbd",
|
|
77
|
+
"data-size": size,
|
|
78
|
+
"data-pressed": pressed || void 0,
|
|
79
|
+
"data-tone": tone,
|
|
80
|
+
className: cn(cnAll("inline-grid h-6.5 min-w-6.5 place-items-center rounded-sm border-(length:--border-width) border-b-2 border-border-strong bg-surface px-1.75 pb-px font-sans text-xs leading-none font-medium whitespace-nowrap text-foreground tabular-nums", "data-[size=sm]:h-5.5 data-[size=sm]:min-w-5.5 data-[size=sm]:px-1.25", "motion-safe:transition-[translate,border-color,background-color,color] motion-safe:duration-(--duration-instant) motion-safe:ease-settle", "data-[pressed]:translate-y-px data-[pressed]:border-b-(length:--border-width) data-[pressed]:border-primary data-[pressed]:bg-primary-subtle data-[pressed]:text-primary-strong", "data-[tone=warning]:border-warning-strong data-[tone=warning]:bg-warning-subtle data-[tone=warning]:text-foreground"), className),
|
|
81
|
+
children
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
/** A shortcut as key caps, with one spoken label ("Command Shift K") for assistive technology. */
|
|
85
|
+
function ShortcutKeys({ shortcut, platform: platformProp, pressed, size = "md", className }) {
|
|
86
|
+
const platform = usePlatform(platformProp);
|
|
87
|
+
const tokens = shortcutTokens(shortcut, platform);
|
|
88
|
+
return /* @__PURE__ */ jsx("span", {
|
|
89
|
+
"data-slot": "shortcut-keys",
|
|
90
|
+
role: "img",
|
|
91
|
+
"aria-label": tokens.map((token) => token.spoken).join(" "),
|
|
92
|
+
className: cn("inline-flex flex-none items-center gap-1", className),
|
|
93
|
+
children: tokens.map((token, index) => /* @__PURE__ */ jsx(ShortcutKbd, {
|
|
94
|
+
size,
|
|
95
|
+
pressed: pressed?.has(token.id),
|
|
96
|
+
"aria-hidden": "true",
|
|
97
|
+
children: token.label
|
|
98
|
+
}, `${index}-${token.id}`))
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const RESERVED = [
|
|
102
|
+
{
|
|
103
|
+
shortcut: "mod+w",
|
|
104
|
+
label: "Close tab"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
shortcut: "mod+t",
|
|
108
|
+
label: "New tab"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
shortcut: "mod+n",
|
|
112
|
+
label: "New window"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
shortcut: "mod+q",
|
|
116
|
+
label: "Quit"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
shortcut: "mod+l",
|
|
120
|
+
label: "Address bar"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
shortcut: "mod+r",
|
|
124
|
+
label: "Reload"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
shortcut: "mod+shift+t",
|
|
128
|
+
label: "Reopen tab"
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
shortcut: "mod+c",
|
|
132
|
+
label: "Copy"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
shortcut: "mod+v",
|
|
136
|
+
label: "Paste"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
shortcut: "mod+x",
|
|
140
|
+
label: "Cut"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
shortcut: "mod+z",
|
|
144
|
+
label: "Undo"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
shortcut: "mod+a",
|
|
148
|
+
label: "Select all"
|
|
149
|
+
}
|
|
150
|
+
];
|
|
151
|
+
/** A no-break space, appended to a repeated announcement so a screen reader says it again. */
|
|
152
|
+
const NBSP = String.fromCharCode(160);
|
|
153
|
+
/** Stagger between the caps of a finished shortcut as they settle in (ms). */
|
|
154
|
+
const CAP_STAGGER_MS = 35;
|
|
155
|
+
/** How long the caps stay lit after a shortcut is set (ms). */
|
|
156
|
+
const SETTLE_MS = 700;
|
|
157
|
+
const controlClass = cnAll(fieldSurfaceVariants(), "relative flex h-(--density-control-h-md) items-center", "data-[state=idle]:hover:border-border-strong", "data-[state=recording]:border-primary data-[state=recording]:bg-primary-subtle", "data-[state=conflict]:border-warning-strong", "has-[[data-slot=shortcut-recorder-button]:focus-visible]:border-border-strong has-[[data-slot=shortcut-recorder-button]:focus-visible]:outline-solid has-[[data-slot=shortcut-recorder-button]:focus-visible]:outline-(length:--focus-ring-width) has-[[data-slot=shortcut-recorder-button]:focus-visible]:outline-offset-(--focus-ring-offset) has-[[data-slot=shortcut-recorder-button]:focus-visible]:outline-(--color-ring)");
|
|
158
|
+
const actionClass = "grid size-8 flex-none cursor-pointer place-items-center rounded-full border-0 bg-transparent p-0 text-text-secondary outline-none motion-safe:transition-[background-color,color,scale] motion-safe:duration-(--duration-quick) motion-safe:ease-settle data-[hovered]:bg-muted data-[hovered]:text-foreground data-[pressed]:scale-95 data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-(--color-ring) [&_svg]:pointer-events-none [&_svg]:size-4";
|
|
159
|
+
const linkClass = "inline-flex min-h-6 cursor-pointer items-center rounded-xs border-0 bg-transparent px-1 font-sans text-xs font-medium text-foreground underline decoration-border-strong underline-offset-3 outline-none motion-safe:transition-[text-decoration-color,color] motion-safe:duration-(--duration-quick) data-[hovered]:decoration-current data-[focus-visible]:decoration-current data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-(--color-ring)";
|
|
160
|
+
/** A small icon button inside the field. One that appears after mount scales in; one there at first render does not. */
|
|
161
|
+
function FieldAction({ label, arrives, onPress, children }) {
|
|
162
|
+
const [animated] = useState(arrives);
|
|
163
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
164
|
+
"aria-label": label,
|
|
165
|
+
onPress,
|
|
166
|
+
"data-slot": "shortcut-recorder-action",
|
|
167
|
+
className: cn(actionClass, animated && "motion-safe:animate-field-clear-in motion-reduce:animate-fade-in"),
|
|
168
|
+
children
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
/** One cap of the row. Decided once, at its own mount: caps there at first render stay still. */
|
|
172
|
+
function Cap({ arrives, delay, children }) {
|
|
173
|
+
const [animated] = useState(arrives);
|
|
174
|
+
return /* @__PURE__ */ jsx("span", {
|
|
175
|
+
className: cn("inline-flex", animated && "motion-safe:animate-chip-reveal motion-reduce:animate-fade-in"),
|
|
176
|
+
style: animated && delay ? { animationDelay: `${delay}ms` } : void 0,
|
|
177
|
+
children
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* A field that records a keyboard shortcut. Press it (or focus it and press Enter or
|
|
182
|
+
* Space), then press the combination: held modifiers show as caps while you build the
|
|
183
|
+
* chord, the finished shortcut settles into place and is announced, and a combination
|
|
184
|
+
* that is already taken asks before it is used.
|
|
185
|
+
*
|
|
186
|
+
* Keyboard: Escape cancels, Backspace or Delete clears, and Tab leaves the field
|
|
187
|
+
* (it is never swallowed, so recording cannot trap focus). Escape and Tab can still be
|
|
188
|
+
* recorded with a modifier held. Outside recording, Backspace or Delete clears the
|
|
189
|
+
* shortcut, and the reset button restores the default. Caps follow the platform:
|
|
190
|
+
* Command symbols on a Mac, Ctrl and Alt elsewhere.
|
|
191
|
+
*
|
|
192
|
+
* While recording, the chord belongs to the recorder: its keys are kept from the page's
|
|
193
|
+
* own shortcuts. Nothing animates on first render.
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* <ShortcutRecorder label="Open command menu" defaultValue="mod+k" onValueChange={save} />
|
|
197
|
+
*/
|
|
198
|
+
function ShortcutRecorder({ label, hideLabel = false, value, defaultValue = null, onValueChange, resetValue, bindings = [], warnReserved = true, requireModifier = true, platform: platformProp, placeholder = "Record shortcut", description, disabled = false, id, className }) {
|
|
199
|
+
const platform = usePlatform(platformProp);
|
|
200
|
+
const conflictId = useId();
|
|
201
|
+
const [inner, setInner] = useState(defaultValue);
|
|
202
|
+
const current = value !== void 0 ? value : inner;
|
|
203
|
+
const restoreTo = resetValue !== void 0 ? resetValue : defaultValue;
|
|
204
|
+
const [recording, setRecording] = useState(false);
|
|
205
|
+
const [live, setLive] = useState([]);
|
|
206
|
+
const [pending, setPending] = useState(null);
|
|
207
|
+
const [nudge, setNudge] = useState(null);
|
|
208
|
+
const [settled, setSettled] = useState(0);
|
|
209
|
+
const [announcement, setAnnouncement] = useState("");
|
|
210
|
+
const buttonRef = useRef(null);
|
|
211
|
+
const swallowClick = useRef(false);
|
|
212
|
+
const [ready, setReady] = useState(false);
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
const frame = requestAnimationFrame(() => setReady(true));
|
|
215
|
+
return () => cancelAnimationFrame(frame);
|
|
216
|
+
}, []);
|
|
217
|
+
const say = (message) => setAnnouncement((previous) => previous === message ? message + NBSP : message);
|
|
218
|
+
const spoken = (shortcut) => shortcut ? formatShortcut(shortcut, platform).spoken : "none";
|
|
219
|
+
const commit = useCallback((next, replaced) => {
|
|
220
|
+
setPending(null);
|
|
221
|
+
setNudge(null);
|
|
222
|
+
if (value === void 0) setInner(next);
|
|
223
|
+
onValueChange?.(next, { replaced });
|
|
224
|
+
setSettled((count) => count + 1);
|
|
225
|
+
say(next ? `Shortcut set to ${formatShortcut(next, platform).spoken}` : "Shortcut cleared");
|
|
226
|
+
}, [
|
|
227
|
+
onValueChange,
|
|
228
|
+
platform,
|
|
229
|
+
value
|
|
230
|
+
]);
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
if (!settled) return;
|
|
233
|
+
const timer = window.setTimeout(() => setSettled(0), SETTLE_MS);
|
|
234
|
+
return () => window.clearTimeout(timer);
|
|
235
|
+
}, [settled]);
|
|
236
|
+
const start = () => {
|
|
237
|
+
if (disabled) return;
|
|
238
|
+
setPending(null);
|
|
239
|
+
setNudge(null);
|
|
240
|
+
setLive([]);
|
|
241
|
+
setRecording(true);
|
|
242
|
+
say("Recording. Press the new shortcut, or Escape to cancel.");
|
|
243
|
+
};
|
|
244
|
+
const stop = () => {
|
|
245
|
+
setRecording(false);
|
|
246
|
+
setLive([]);
|
|
247
|
+
};
|
|
248
|
+
const conflictFor = (shortcut) => {
|
|
249
|
+
const normal = normalizeShortcut(shortcut, platform);
|
|
250
|
+
if (current && normalizeShortcut(current, platform) === normal) return null;
|
|
251
|
+
const taken = bindings.find((binding) => normalizeShortcut(binding.shortcut, platform) === normal);
|
|
252
|
+
if (taken) return {
|
|
253
|
+
shortcut,
|
|
254
|
+
conflict: taken,
|
|
255
|
+
reserved: false
|
|
256
|
+
};
|
|
257
|
+
const reserved = warnReserved ? RESERVED.find((binding) => normalizeShortcut(binding.shortcut, platform) === normal) : void 0;
|
|
258
|
+
return reserved ? {
|
|
259
|
+
shortcut,
|
|
260
|
+
conflict: reserved,
|
|
261
|
+
reserved: true
|
|
262
|
+
} : null;
|
|
263
|
+
};
|
|
264
|
+
function onKeyDown(event) {
|
|
265
|
+
const bare = !event.metaKey && !event.ctrlKey && !event.altKey;
|
|
266
|
+
if (!recording) {
|
|
267
|
+
if (bare && !event.shiftKey && (event.key === "Backspace" || event.key === "Delete") && current) {
|
|
268
|
+
event.preventDefault();
|
|
269
|
+
commit(null);
|
|
270
|
+
} else if (bare && !event.shiftKey && event.key === "Escape" && pending) {
|
|
271
|
+
event.preventDefault();
|
|
272
|
+
setPending(null);
|
|
273
|
+
say("Kept the previous shortcut");
|
|
274
|
+
}
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (event.key === "Tab" && bare) {
|
|
278
|
+
stop();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
event.preventDefault();
|
|
282
|
+
event.stopPropagation();
|
|
283
|
+
if (event.key === " ") swallowClick.current = true;
|
|
284
|
+
const noMods = bare && !event.shiftKey;
|
|
285
|
+
if (noMods && event.key === "Escape") {
|
|
286
|
+
stop();
|
|
287
|
+
say("Recording canceled");
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (noMods && (event.key === "Backspace" || event.key === "Delete")) {
|
|
291
|
+
stop();
|
|
292
|
+
commit(null);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const shortcut = shortcutFromEvent(event.nativeEvent, platform);
|
|
296
|
+
if (!shortcut) {
|
|
297
|
+
setLive(modifiersFromEvent(event, platform));
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const { mods, key } = splitShortcut(shortcut);
|
|
301
|
+
const strong = mods.has("mod") || mods.has("ctrl") || mods.has("alt") || mods.has("meta");
|
|
302
|
+
if (requireModifier && !strong && !/^f\d+$/.test(key)) {
|
|
303
|
+
const hint = platform === "mac" ? "Include ⌘, ⌃, or ⌥" : "Include Ctrl or Alt";
|
|
304
|
+
setNudge(hint);
|
|
305
|
+
say(hint);
|
|
306
|
+
setLive(modifiersFromEvent(event, platform));
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
stop();
|
|
310
|
+
const conflict = conflictFor(shortcut);
|
|
311
|
+
if (conflict) {
|
|
312
|
+
setPending(conflict);
|
|
313
|
+
say(`${formatShortcut(shortcut, platform).spoken} is ${conflict.reserved ? "reserved for" : "used by"} ${conflict.conflict.label}. Use anyway, or record another.`);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
commit(shortcut);
|
|
317
|
+
}
|
|
318
|
+
function onKeyUp(event) {
|
|
319
|
+
if (recording) setLive(modifiersFromEvent(event, platform));
|
|
320
|
+
if (swallowClick.current) setTimeout(() => swallowClick.current = false, 0);
|
|
321
|
+
}
|
|
322
|
+
const shown = pending?.shortcut ?? current;
|
|
323
|
+
const tokens = recording ? shortcutTokens(live.join("+"), platform) : shown ? shortcutTokens(shown, platform) : [];
|
|
324
|
+
const state = recording ? "recording" : pending ? "conflict" : "idle";
|
|
325
|
+
const canReset = !recording && (pending !== null || (current ?? null) !== (restoreTo ?? null));
|
|
326
|
+
const canClear = !recording && !!current && !pending;
|
|
327
|
+
const hint = pending ? void 0 : nudge ?? (recording ? "Esc to cancel, Backspace to clear" : description);
|
|
328
|
+
const field = useFieldWiring({
|
|
329
|
+
enabled: true,
|
|
330
|
+
id,
|
|
331
|
+
describedBy: void 0,
|
|
332
|
+
invalid: void 0,
|
|
333
|
+
description: hint,
|
|
334
|
+
error: void 0
|
|
335
|
+
});
|
|
336
|
+
const describedBy = [pending ? conflictId : void 0, field.hintId].filter(Boolean).join(" ");
|
|
337
|
+
return /* @__PURE__ */ jsx("div", {
|
|
338
|
+
"data-slot": "shortcut-recorder",
|
|
339
|
+
className: cn("w-full min-w-0 font-sans", className),
|
|
340
|
+
children: /* @__PURE__ */ jsxs(FieldShell, {
|
|
341
|
+
label: hideLabel ? null : label,
|
|
342
|
+
controlId: field.controlId,
|
|
343
|
+
description: hint,
|
|
344
|
+
hintId: field.hintId,
|
|
345
|
+
error: void 0,
|
|
346
|
+
errorId: void 0,
|
|
347
|
+
numbers: false,
|
|
348
|
+
children: [
|
|
349
|
+
/* @__PURE__ */ jsxs("div", {
|
|
350
|
+
"data-slot": "shortcut-recorder-control",
|
|
351
|
+
"data-state": state,
|
|
352
|
+
"data-disabled": disabled || void 0,
|
|
353
|
+
className: controlClass,
|
|
354
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
355
|
+
ref: buttonRef,
|
|
356
|
+
id: field.controlId,
|
|
357
|
+
"aria-describedby": describedBy || void 0,
|
|
358
|
+
type: "button",
|
|
359
|
+
"data-slot": "shortcut-recorder-button",
|
|
360
|
+
disabled,
|
|
361
|
+
"aria-label": `${label}, ${recording ? "recording" : spoken(shown)}`,
|
|
362
|
+
"aria-pressed": recording,
|
|
363
|
+
onClick: (event) => {
|
|
364
|
+
if (swallowClick.current) return;
|
|
365
|
+
if (recording) {
|
|
366
|
+
if (event.detail !== 0) {
|
|
367
|
+
stop();
|
|
368
|
+
say("Recording canceled");
|
|
369
|
+
}
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
start();
|
|
373
|
+
},
|
|
374
|
+
onKeyDown,
|
|
375
|
+
onKeyUp,
|
|
376
|
+
onBlur: () => {
|
|
377
|
+
if (recording) {
|
|
378
|
+
stop();
|
|
379
|
+
setNudge(null);
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
className: "flex h-full min-w-0 flex-1 cursor-pointer touch-manipulation items-center rounded-control border-0 bg-transparent ps-2.5 pe-1.5 text-start font-sans text-foreground outline-none disabled:cursor-not-allowed",
|
|
383
|
+
children: /* @__PURE__ */ jsxs("span", {
|
|
384
|
+
"aria-hidden": "true",
|
|
385
|
+
className: "relative flex min-w-0 items-center gap-1",
|
|
386
|
+
children: [
|
|
387
|
+
recording ? /* @__PURE__ */ jsx("span", { className: "me-1.5 ms-0.75 size-1.75 flex-none rounded-full bg-primary motion-safe:animate-shortcut-recording" }) : null,
|
|
388
|
+
tokens.map((token, index) => /* @__PURE__ */ jsx(Cap, {
|
|
389
|
+
arrives: ready,
|
|
390
|
+
delay: recording ? 0 : index * CAP_STAGGER_MS,
|
|
391
|
+
children: /* @__PURE__ */ jsx(ShortcutKbd, {
|
|
392
|
+
pressed: recording || settled > 0 && !pending,
|
|
393
|
+
tone: pending ? "warning" : void 0,
|
|
394
|
+
children: token.label
|
|
395
|
+
})
|
|
396
|
+
}, token.id)),
|
|
397
|
+
tokens.length === 0 ? /* @__PURE__ */ jsx("span", {
|
|
398
|
+
className: cnAll("ps-0.75 text-sm whitespace-nowrap", recording ? "text-text-secondary" : "text-muted-foreground", ready && "motion-safe:animate-field-word-in motion-reduce:animate-fade-in"),
|
|
399
|
+
children: recording ? "Press keys" : placeholder
|
|
400
|
+
}, recording ? "prompt" : "placeholder") : null
|
|
401
|
+
]
|
|
402
|
+
})
|
|
403
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
404
|
+
className: "flex flex-none items-center gap-0.5 pe-1",
|
|
405
|
+
children: [canReset ? /* @__PURE__ */ jsx(FieldAction, {
|
|
406
|
+
arrives: ready,
|
|
407
|
+
label: `Reset to ${spoken(restoreTo ?? null)}`,
|
|
408
|
+
onPress: () => {
|
|
409
|
+
commit(restoreTo ?? null);
|
|
410
|
+
buttonRef.current?.focus();
|
|
411
|
+
},
|
|
412
|
+
children: /* @__PURE__ */ jsxs("svg", {
|
|
413
|
+
"aria-hidden": "true",
|
|
414
|
+
viewBox: "0 0 24 24",
|
|
415
|
+
fill: "none",
|
|
416
|
+
stroke: "currentColor",
|
|
417
|
+
strokeWidth: "1.75",
|
|
418
|
+
strokeLinecap: "round",
|
|
419
|
+
strokeLinejoin: "round",
|
|
420
|
+
children: [/* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 1 0 3-6.7L3 8" }), /* @__PURE__ */ jsx("path", { d: "M3 3v5h5" })]
|
|
421
|
+
})
|
|
422
|
+
}, "reset") : null, canClear ? /* @__PURE__ */ jsx(FieldAction, {
|
|
423
|
+
arrives: ready,
|
|
424
|
+
label: "Clear shortcut",
|
|
425
|
+
onPress: () => {
|
|
426
|
+
commit(null);
|
|
427
|
+
buttonRef.current?.focus();
|
|
428
|
+
},
|
|
429
|
+
children: /* @__PURE__ */ jsx("svg", {
|
|
430
|
+
"aria-hidden": "true",
|
|
431
|
+
viewBox: "0 0 16 16",
|
|
432
|
+
fill: "none",
|
|
433
|
+
stroke: "currentColor",
|
|
434
|
+
strokeWidth: "1.75",
|
|
435
|
+
strokeLinecap: "round",
|
|
436
|
+
children: /* @__PURE__ */ jsx("path", { d: "m4 4 8 8M12 4l-8 8" })
|
|
437
|
+
})
|
|
438
|
+
}, "clear") : null]
|
|
439
|
+
})]
|
|
440
|
+
}),
|
|
441
|
+
pending ? /* @__PURE__ */ jsxs("div", {
|
|
442
|
+
id: conflictId,
|
|
443
|
+
"data-slot": "shortcut-recorder-conflict",
|
|
444
|
+
className: "flex flex-wrap items-baseline justify-between gap-x-3 pt-1.5 font-sans text-xs leading-(--leading-normal) text-text-secondary motion-safe:animate-field-word-in motion-reduce:animate-fade-in",
|
|
445
|
+
children: [/* @__PURE__ */ jsxs("span", { children: [
|
|
446
|
+
/* @__PURE__ */ jsx("span", {
|
|
447
|
+
"aria-hidden": "true",
|
|
448
|
+
className: "me-1.5 ms-px mb-px inline-block size-1.5 rounded-full bg-warning-strong align-middle"
|
|
449
|
+
}),
|
|
450
|
+
pending.reserved ? "Reserved for" : "Already used by",
|
|
451
|
+
" ",
|
|
452
|
+
/* @__PURE__ */ jsx("strong", {
|
|
453
|
+
className: "font-medium text-foreground",
|
|
454
|
+
children: pending.conflict.label
|
|
455
|
+
})
|
|
456
|
+
] }), /* @__PURE__ */ jsxs("span", {
|
|
457
|
+
className: "inline-flex gap-2",
|
|
458
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
459
|
+
className: linkClass,
|
|
460
|
+
onPress: () => {
|
|
461
|
+
commit(pending.shortcut, pending.reserved ? void 0 : pending.conflict);
|
|
462
|
+
buttonRef.current?.focus();
|
|
463
|
+
},
|
|
464
|
+
children: "Use anyway"
|
|
465
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
466
|
+
className: cn(linkClass, "font-normal text-text-secondary no-underline"),
|
|
467
|
+
onPress: () => {
|
|
468
|
+
setPending(null);
|
|
469
|
+
say("Kept the previous shortcut");
|
|
470
|
+
buttonRef.current?.focus();
|
|
471
|
+
},
|
|
472
|
+
children: "Cancel"
|
|
473
|
+
})]
|
|
474
|
+
})]
|
|
475
|
+
}) : null,
|
|
476
|
+
/* @__PURE__ */ jsx("span", {
|
|
477
|
+
className: "sr-only",
|
|
478
|
+
role: "status",
|
|
479
|
+
"aria-live": "polite",
|
|
480
|
+
children: announcement
|
|
481
|
+
})
|
|
482
|
+
]
|
|
483
|
+
})
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
//#endregion
|
|
487
|
+
export { ShortcutKbd, ShortcutKeys, ShortcutRecorder, usePlatform, usePressedKeys };
|
|
488
|
+
|
|
489
|
+
//# sourceMappingURL=shortcut-recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shortcut-recorder.js","names":["AriaButton"],"sources":["../../src/shortcut-recorder/shortcut-recorder.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n useSyncExternalStore,\n type HTMLAttributes,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { Button as AriaButton } from \"react-aria-components\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { FieldShell, useFieldWiring } from \"../lib/field-shell.js\";\nimport { fieldSurfaceVariants } from \"../lib/field-surface.js\";\nimport {\n formatShortcut,\n keyId,\n modifiersFromEvent,\n normalizeShortcut,\n shortcutFromEvent,\n shortcutTokens,\n splitShortcut,\n type Platform,\n} from \"./shortcut-model.js\";\n\n/*\n * Shortcut keys and a recorder for them. The model (parsing, normalising,\n * platform labels, matching) lives in shortcut-model.ts; this file is the\n * three parts that draw it: a key cap, a row of caps, and the recording field.\n */\n\nconst subscribe = () => () => {};\n\nconst detectPlatform = (): Platform => {\n if (typeof navigator === \"undefined\") return \"mac\";\n const hint =\n (navigator as Navigator & { userAgentData?: { platform?: string } }).userAgentData?.platform ??\n navigator.platform ??\n navigator.userAgent;\n return /mac|iphone|ipad|ipod/i.test(hint) ? \"mac\" : \"other\";\n};\n\n/** The viewer's platform. A server render assumes a Mac and settles on the first client render. */\nexport function usePlatform(override?: Platform): Platform {\n const detected = useSyncExternalStore<Platform>(subscribe, detectPlatform, () => \"mac\");\n return override ?? detected;\n}\n\nconst EMPTY: ReadonlySet<string> = new Set();\n\n/**\n * The keys held down right now, by physical id (feed it to `ShortcutKeys` to light the\n * caps as they are pressed). Clears when the window loses focus, and after Command lifts\n * (macOS drops the other keyups).\n */\nexport function usePressedKeys(enabled = true): ReadonlySet<string> {\n const [held, setHeld] = useState<ReadonlySet<string>>(() => new Set());\n useEffect(() => {\n if (!enabled) return;\n const update = (change: (next: Set<string>) => void) =>\n setHeld((current) => {\n const next = new Set(current);\n change(next);\n return next.size === current.size && [...next].every((key) => current.has(key))\n ? current\n : next;\n });\n const down = (event: KeyboardEvent) => {\n const id = keyId(event);\n if (id) update((next) => next.add(id));\n };\n const up = (event: KeyboardEvent) => {\n const id = keyId(event);\n update((next) => {\n if (id) next.delete(id);\n if (id === \"meta\")\n for (const key of [...next])\n if (![\"ctrl\", \"alt\", \"shift\"].includes(key)) next.delete(key);\n if (!event.metaKey) next.delete(\"meta\");\n if (!event.ctrlKey) next.delete(\"ctrl\");\n if (!event.altKey) next.delete(\"alt\");\n if (!event.shiftKey) next.delete(\"shift\");\n });\n };\n const clear = () => setHeld((current) => (current.size ? new Set() : current));\n // Capture phase, so keys still register while a focused recorder keeps the chord from bubbling.\n window.addEventListener(\"keydown\", down, true);\n window.addEventListener(\"keyup\", up, true);\n window.addEventListener(\"blur\", clear);\n document.addEventListener(\"visibilitychange\", clear);\n return () => {\n window.removeEventListener(\"keydown\", down, true);\n window.removeEventListener(\"keyup\", up, true);\n window.removeEventListener(\"blur\", clear);\n document.removeEventListener(\"visibilitychange\", clear);\n };\n }, [enabled]);\n return enabled ? held : EMPTY;\n}\n\n/* ---------------------------------------------- ShortcutKbd and ShortcutKeys ---------------------------------------------- */\n\nexport interface ShortcutKbdProps extends Omit<HTMLAttributes<HTMLElement>, \"className\"> {\n children: ReactNode;\n /** Draws the key pushed down and lit, such as while it is held. */\n pressed?: boolean;\n /** `warning` tints the cap, for a combination that is taken. */\n tone?: \"warning\";\n size?: \"sm\" | \"md\";\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\n/** One key cap. Use it inline in copy, or through `ShortcutKeys` for a whole combination. */\nexport const ShortcutKbd = forwardRef<HTMLElement, ShortcutKbdProps>(function ShortcutKbd(\n { children, pressed = false, tone, size = \"md\", className, ...props },\n ref,\n) {\n return (\n <kbd\n ref={ref}\n {...props}\n data-slot=\"shortcut-kbd\"\n data-size={size}\n data-pressed={pressed || undefined}\n data-tone={tone}\n className={cn(\n cnAll(\n // A one-pixel edge with a heavier bottom reads as a key without a shadow. Held, it drops a pixel and takes the accent.\n \"inline-grid h-6.5 min-w-6.5 place-items-center rounded-sm border-(length:--border-width) border-b-2 border-border-strong bg-surface px-1.75 pb-px font-sans text-xs leading-none font-medium whitespace-nowrap text-foreground tabular-nums\",\n \"data-[size=sm]:h-5.5 data-[size=sm]:min-w-5.5 data-[size=sm]:px-1.25\",\n \"motion-safe:transition-[translate,border-color,background-color,color] motion-safe:duration-(--duration-instant) motion-safe:ease-settle\",\n \"data-[pressed]:translate-y-px data-[pressed]:border-b-(length:--border-width) data-[pressed]:border-primary data-[pressed]:bg-primary-subtle data-[pressed]:text-primary-strong\",\n \"data-[tone=warning]:border-warning-strong data-[tone=warning]:bg-warning-subtle data-[tone=warning]:text-foreground\",\n ),\n className,\n )}\n >\n {children}\n </kbd>\n );\n});\n\nexport interface ShortcutKeysProps {\n /** A shortcut such as `\"mod+shift+k\"`. `mod` is Command on a Mac and Control elsewhere. */\n shortcut: string;\n /** Defaults to the viewer's platform. */\n platform?: Platform;\n /** Held key ids from `usePressedKeys`: the matching caps light up. */\n pressed?: ReadonlySet<string>;\n size?: \"sm\" | \"md\";\n /** Extra classes. Merged last. */\n className?: string;\n}\n\n/** A shortcut as key caps, with one spoken label (\"Command Shift K\") for assistive technology. */\nexport function ShortcutKeys({\n shortcut,\n platform: platformProp,\n pressed,\n size = \"md\",\n className,\n}: ShortcutKeysProps) {\n const platform = usePlatform(platformProp);\n const tokens = shortcutTokens(shortcut, platform);\n return (\n <span\n data-slot=\"shortcut-keys\"\n role=\"img\"\n aria-label={tokens.map((token) => token.spoken).join(\" \")}\n className={cn(\"inline-flex flex-none items-center gap-1\", className)}\n >\n {tokens.map((token, index) => (\n <ShortcutKbd\n key={`${index}-${token.id}`}\n size={size}\n pressed={pressed?.has(token.id)}\n aria-hidden=\"true\"\n >\n {token.label}\n </ShortcutKbd>\n ))}\n </span>\n );\n}\n\n/* ---------------------------------------------- ShortcutRecorder ---------------------------------------------- */\n\nexport interface ShortcutBinding {\n shortcut: string;\n /** What the shortcut does, shown when a recording collides with it. */\n label: string;\n}\n\nexport interface ShortcutRecorderProps {\n /** What the shortcut is for, and the field's accessible name. */\n label: string;\n /** Hides the label visually; it stays the accessible name. */\n hideLabel?: boolean;\n /** The shortcut, such as `\"mod+shift+k\"`, or `null` for none. Controlled when given. */\n value?: string | null;\n defaultValue?: string | null;\n /** `replaced` is the binding a shortcut was taken from after \"Use anyway\", so the caller can unbind it. */\n onValueChange?: (value: string | null, details: { replaced?: ShortcutBinding }) => void;\n /** What the reset button restores. Defaults to `defaultValue`. */\n resetValue?: string | null;\n /** Shortcuts already in use. Recording one of them asks before taking it. */\n bindings?: ShortcutBinding[];\n /** Also warn about combinations a browser or system keeps, such as Command W and Command C. @default true */\n warnReserved?: boolean;\n /** Require Command, Control or Option (function keys are always allowed), so a bare letter cannot be recorded. @default true */\n requireModifier?: boolean;\n /** Defaults to the viewer's platform. */\n platform?: Platform;\n /** Shown while there is no shortcut. @default \"Record shortcut\" */\n placeholder?: string;\n /** Helper text under the field. */\n description?: string;\n disabled?: boolean;\n id?: string;\n /** Extra classes for the wrapper. Merged last. */\n className?: string;\n}\n\nconst RESERVED: ShortcutBinding[] = [\n { shortcut: \"mod+w\", label: \"Close tab\" },\n { shortcut: \"mod+t\", label: \"New tab\" },\n { shortcut: \"mod+n\", label: \"New window\" },\n { shortcut: \"mod+q\", label: \"Quit\" },\n { shortcut: \"mod+l\", label: \"Address bar\" },\n { shortcut: \"mod+r\", label: \"Reload\" },\n { shortcut: \"mod+shift+t\", label: \"Reopen tab\" },\n { shortcut: \"mod+c\", label: \"Copy\" },\n { shortcut: \"mod+v\", label: \"Paste\" },\n { shortcut: \"mod+x\", label: \"Cut\" },\n { shortcut: \"mod+z\", label: \"Undo\" },\n { shortcut: \"mod+a\", label: \"Select all\" },\n];\n\ninterface Pending {\n shortcut: string;\n conflict: ShortcutBinding;\n reserved: boolean;\n}\n\n/** A no-break space, appended to a repeated announcement so a screen reader says it again. */\nconst NBSP = String.fromCharCode(160);\n\n/** Stagger between the caps of a finished shortcut as they settle in (ms). */\nconst CAP_STAGGER_MS = 35;\n\n/** How long the caps stay lit after a shortcut is set (ms). */\nconst SETTLE_MS = 700;\n\nconst controlClass = cnAll(\n fieldSurfaceVariants(),\n \"relative flex h-(--density-control-h-md) items-center\",\n \"data-[state=idle]:hover:border-border-strong\",\n \"data-[state=recording]:border-primary data-[state=recording]:bg-primary-subtle\",\n \"data-[state=conflict]:border-warning-strong\",\n // The recorder is a button, so the control shows the ring whenever it has keyboard focus.\n \"has-[[data-slot=shortcut-recorder-button]:focus-visible]:border-border-strong has-[[data-slot=shortcut-recorder-button]:focus-visible]:outline-solid has-[[data-slot=shortcut-recorder-button]:focus-visible]:outline-(length:--focus-ring-width) has-[[data-slot=shortcut-recorder-button]:focus-visible]:outline-offset-(--focus-ring-offset) has-[[data-slot=shortcut-recorder-button]:focus-visible]:outline-(--color-ring)\",\n);\n\nconst actionClass =\n \"grid size-8 flex-none cursor-pointer place-items-center rounded-full border-0 bg-transparent p-0 text-text-secondary outline-none motion-safe:transition-[background-color,color,scale] motion-safe:duration-(--duration-quick) motion-safe:ease-settle data-[hovered]:bg-muted data-[hovered]:text-foreground data-[pressed]:scale-95 data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-(--color-ring) [&_svg]:pointer-events-none [&_svg]:size-4\";\n\nconst linkClass =\n \"inline-flex min-h-6 cursor-pointer items-center rounded-xs border-0 bg-transparent px-1 font-sans text-xs font-medium text-foreground underline decoration-border-strong underline-offset-3 outline-none motion-safe:transition-[text-decoration-color,color] motion-safe:duration-(--duration-quick) data-[hovered]:decoration-current data-[focus-visible]:decoration-current data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-(--color-ring)\";\n\n/** A small icon button inside the field. One that appears after mount scales in; one there at first render does not. */\nfunction FieldAction({\n label,\n arrives,\n onPress,\n children,\n}: {\n label: string;\n arrives: boolean;\n onPress: () => void;\n children: ReactNode;\n}) {\n const [animated] = useState(arrives);\n return (\n <AriaButton\n aria-label={label}\n onPress={onPress}\n data-slot=\"shortcut-recorder-action\"\n className={cn(\n actionClass,\n animated && \"motion-safe:animate-field-clear-in motion-reduce:animate-fade-in\",\n )}\n >\n {children}\n </AriaButton>\n );\n}\n\n/** One cap of the row. Decided once, at its own mount: caps there at first render stay still. */\nfunction Cap({\n arrives,\n delay,\n children,\n}: {\n arrives: boolean;\n delay: number;\n children: ReactNode;\n}) {\n const [animated] = useState(arrives);\n return (\n <span\n className={cn(\n \"inline-flex\",\n animated && \"motion-safe:animate-chip-reveal motion-reduce:animate-fade-in\",\n )}\n style={animated && delay ? { animationDelay: `${delay}ms` } : undefined}\n >\n {children}\n </span>\n );\n}\n\n/**\n * A field that records a keyboard shortcut. Press it (or focus it and press Enter or\n * Space), then press the combination: held modifiers show as caps while you build the\n * chord, the finished shortcut settles into place and is announced, and a combination\n * that is already taken asks before it is used.\n *\n * Keyboard: Escape cancels, Backspace or Delete clears, and Tab leaves the field\n * (it is never swallowed, so recording cannot trap focus). Escape and Tab can still be\n * recorded with a modifier held. Outside recording, Backspace or Delete clears the\n * shortcut, and the reset button restores the default. Caps follow the platform:\n * Command symbols on a Mac, Ctrl and Alt elsewhere.\n *\n * While recording, the chord belongs to the recorder: its keys are kept from the page's\n * own shortcuts. Nothing animates on first render.\n *\n * @example\n * <ShortcutRecorder label=\"Open command menu\" defaultValue=\"mod+k\" onValueChange={save} />\n */\nexport function ShortcutRecorder({\n label,\n hideLabel = false,\n value,\n defaultValue = null,\n onValueChange,\n resetValue,\n bindings = [],\n warnReserved = true,\n requireModifier = true,\n platform: platformProp,\n placeholder = \"Record shortcut\",\n description,\n disabled = false,\n id,\n className,\n}: ShortcutRecorderProps) {\n const platform = usePlatform(platformProp);\n const conflictId = useId();\n\n const [inner, setInner] = useState<string | null>(defaultValue);\n const current = value !== undefined ? value : inner;\n const restoreTo = resetValue !== undefined ? resetValue : defaultValue;\n\n const [recording, setRecording] = useState(false);\n const [live, setLive] = useState<string[]>([]);\n const [pending, setPending] = useState<Pending | null>(null);\n const [nudge, setNudge] = useState<string | null>(null);\n const [settled, setSettled] = useState(0);\n const [announcement, setAnnouncement] = useState(\"\");\n const buttonRef = useRef<HTMLButtonElement>(null);\n const swallowClick = useRef(false);\n\n // Caps and buttons that mount from the next frame on arrive animated.\n const [ready, setReady] = useState(false);\n useEffect(() => {\n const frame = requestAnimationFrame(() => setReady(true));\n return () => cancelAnimationFrame(frame);\n }, []);\n\n const say = (message: string) =>\n setAnnouncement((previous) => (previous === message ? message + NBSP : message));\n const spoken = (shortcut: string | null) =>\n shortcut ? formatShortcut(shortcut, platform).spoken : \"none\";\n\n const commit = useCallback(\n (next: string | null, replaced?: ShortcutBinding) => {\n setPending(null);\n setNudge(null);\n if (value === undefined) setInner(next);\n onValueChange?.(next, { replaced });\n setSettled((count) => count + 1);\n say(next ? `Shortcut set to ${formatShortcut(next, platform).spoken}` : \"Shortcut cleared\");\n },\n [onValueChange, platform, value],\n );\n\n useEffect(() => {\n if (!settled) return;\n const timer = window.setTimeout(() => setSettled(0), SETTLE_MS);\n return () => window.clearTimeout(timer);\n }, [settled]);\n\n const start = () => {\n if (disabled) return;\n setPending(null);\n setNudge(null);\n setLive([]);\n setRecording(true);\n say(\"Recording. Press the new shortcut, or Escape to cancel.\");\n };\n const stop = () => {\n setRecording(false);\n setLive([]);\n };\n\n const conflictFor = (shortcut: string): Pending | null => {\n const normal = normalizeShortcut(shortcut, platform);\n if (current && normalizeShortcut(current, platform) === normal) return null;\n const taken = bindings.find(\n (binding) => normalizeShortcut(binding.shortcut, platform) === normal,\n );\n if (taken) return { shortcut, conflict: taken, reserved: false };\n const reserved = warnReserved\n ? RESERVED.find((binding) => normalizeShortcut(binding.shortcut, platform) === normal)\n : undefined;\n return reserved ? { shortcut, conflict: reserved, reserved: true } : null;\n };\n\n function onKeyDown(event: ReactKeyboardEvent<HTMLButtonElement>) {\n const bare = !event.metaKey && !event.ctrlKey && !event.altKey;\n if (!recording) {\n if (\n bare &&\n !event.shiftKey &&\n (event.key === \"Backspace\" || event.key === \"Delete\") &&\n current\n ) {\n event.preventDefault();\n commit(null);\n } else if (bare && !event.shiftKey && event.key === \"Escape\" && pending) {\n event.preventDefault();\n setPending(null);\n say(\"Kept the previous shortcut\");\n }\n return;\n }\n // Tab (with or without Shift) is how a keyboard user leaves: it stops recording and moves focus on.\n if (event.key === \"Tab\" && bare) {\n stop();\n return;\n }\n // The chord belongs to the recorder: keep it from the page's own shortcuts, such as a command menu on Command K.\n event.preventDefault();\n event.stopPropagation();\n if (event.key === \" \") swallowClick.current = true;\n const noMods = bare && !event.shiftKey;\n if (noMods && event.key === \"Escape\") {\n stop();\n say(\"Recording canceled\");\n return;\n }\n if (noMods && (event.key === \"Backspace\" || event.key === \"Delete\")) {\n stop();\n commit(null);\n return;\n }\n const shortcut = shortcutFromEvent(event.nativeEvent, platform);\n if (!shortcut) {\n setLive(modifiersFromEvent(event, platform));\n return;\n }\n const { mods, key } = splitShortcut(shortcut);\n const strong = mods.has(\"mod\") || mods.has(\"ctrl\") || mods.has(\"alt\") || mods.has(\"meta\");\n if (requireModifier && !strong && !/^f\\d+$/.test(key)) {\n const hint = platform === \"mac\" ? \"Include ⌘, ⌃, or ⌥\" : \"Include Ctrl or Alt\";\n setNudge(hint);\n say(hint);\n setLive(modifiersFromEvent(event, platform));\n return;\n }\n stop();\n const conflict = conflictFor(shortcut);\n if (conflict) {\n setPending(conflict);\n say(\n `${formatShortcut(shortcut, platform).spoken} is ${conflict.reserved ? \"reserved for\" : \"used by\"} ${conflict.conflict.label}. Use anyway, or record another.`,\n );\n return;\n }\n commit(shortcut);\n }\n\n function onKeyUp(event: ReactKeyboardEvent<HTMLButtonElement>) {\n if (recording) setLive(modifiersFromEvent(event, platform));\n // A Space the recorder took must not click the button when it comes back up.\n if (swallowClick.current) setTimeout(() => (swallowClick.current = false), 0);\n }\n\n const shown = pending?.shortcut ?? current;\n const tokens = recording\n ? shortcutTokens(live.join(\"+\"), platform)\n : shown\n ? shortcutTokens(shown, platform)\n : [];\n const state = recording ? \"recording\" : pending ? \"conflict\" : \"idle\";\n const canReset = !recording && (pending !== null || (current ?? null) !== (restoreTo ?? null));\n const canClear = !recording && !!current && !pending;\n const hint = pending\n ? undefined\n : (nudge ?? (recording ? \"Esc to cancel, Backspace to clear\" : description));\n\n const field = useFieldWiring({\n enabled: true,\n id,\n describedBy: undefined,\n invalid: undefined,\n description: hint,\n error: undefined,\n });\n const describedBy = [pending ? conflictId : undefined, field.hintId].filter(Boolean).join(\" \");\n\n return (\n <div data-slot=\"shortcut-recorder\" className={cn(\"w-full min-w-0 font-sans\", className)}>\n <FieldShell\n label={hideLabel ? null : label}\n controlId={field.controlId}\n description={hint}\n hintId={field.hintId}\n error={undefined}\n errorId={undefined}\n numbers={false}\n >\n <div\n data-slot=\"shortcut-recorder-control\"\n data-state={state}\n data-disabled={disabled || undefined}\n className={controlClass}\n >\n <button\n ref={buttonRef}\n id={field.controlId}\n aria-describedby={describedBy || undefined}\n type=\"button\"\n data-slot=\"shortcut-recorder-button\"\n disabled={disabled}\n aria-label={`${label}, ${recording ? \"recording\" : spoken(shown)}`}\n aria-pressed={recording}\n onClick={(event) => {\n if (swallowClick.current) return;\n if (recording) {\n // A pointer click cancels; a keyboard click cannot reach here, as the keydown was taken.\n if (event.detail !== 0) {\n stop();\n say(\"Recording canceled\");\n }\n return;\n }\n start();\n }}\n onKeyDown={onKeyDown}\n onKeyUp={onKeyUp}\n onBlur={() => {\n if (recording) {\n stop();\n setNudge(null);\n }\n }}\n className=\"flex h-full min-w-0 flex-1 cursor-pointer touch-manipulation items-center rounded-control border-0 bg-transparent ps-2.5 pe-1.5 text-start font-sans text-foreground outline-none disabled:cursor-not-allowed\"\n >\n <span aria-hidden=\"true\" className=\"relative flex min-w-0 items-center gap-1\">\n {recording ? (\n <span className=\"me-1.5 ms-0.75 size-1.75 flex-none rounded-full bg-primary motion-safe:animate-shortcut-recording\" />\n ) : null}\n {tokens.map((token, index) => (\n <Cap key={token.id} arrives={ready} delay={recording ? 0 : index * CAP_STAGGER_MS}>\n <ShortcutKbd\n pressed={recording || (settled > 0 && !pending)}\n tone={pending ? \"warning\" : undefined}\n >\n {token.label}\n </ShortcutKbd>\n </Cap>\n ))}\n {tokens.length === 0 ? (\n <span\n key={recording ? \"prompt\" : \"placeholder\"}\n className={cnAll(\n \"ps-0.75 text-sm whitespace-nowrap\",\n recording ? \"text-text-secondary\" : \"text-muted-foreground\",\n ready && \"motion-safe:animate-field-word-in motion-reduce:animate-fade-in\",\n )}\n >\n {recording ? \"Press keys\" : placeholder}\n </span>\n ) : null}\n </span>\n </button>\n <span className=\"flex flex-none items-center gap-0.5 pe-1\">\n {canReset ? (\n <FieldAction\n key=\"reset\"\n arrives={ready}\n label={`Reset to ${spoken(restoreTo ?? null)}`}\n onPress={() => {\n commit(restoreTo ?? null);\n buttonRef.current?.focus();\n }}\n >\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M3 12a9 9 0 1 0 3-6.7L3 8\" />\n <path d=\"M3 3v5h5\" />\n </svg>\n </FieldAction>\n ) : null}\n {canClear ? (\n <FieldAction\n key=\"clear\"\n arrives={ready}\n label=\"Clear shortcut\"\n onPress={() => {\n commit(null);\n buttonRef.current?.focus();\n }}\n >\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n >\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </svg>\n </FieldAction>\n ) : null}\n </span>\n </div>\n {/* The conflict: its copy settles in; it goes away at once, as its buttons must not stay reachable while it fades. */}\n {pending ? (\n <div\n id={conflictId}\n data-slot=\"shortcut-recorder-conflict\"\n className=\"flex flex-wrap items-baseline justify-between gap-x-3 pt-1.5 font-sans text-xs leading-(--leading-normal) text-text-secondary motion-safe:animate-field-word-in motion-reduce:animate-fade-in\"\n >\n <span>\n <span\n aria-hidden=\"true\"\n className=\"me-1.5 ms-px mb-px inline-block size-1.5 rounded-full bg-warning-strong align-middle\"\n />\n {pending.reserved ? \"Reserved for\" : \"Already used by\"}{\" \"}\n <strong className=\"font-medium text-foreground\">{pending.conflict.label}</strong>\n </span>\n <span className=\"inline-flex gap-2\">\n <AriaButton\n className={linkClass}\n onPress={() => {\n commit(pending.shortcut, pending.reserved ? undefined : pending.conflict);\n buttonRef.current?.focus();\n }}\n >\n Use anyway\n </AriaButton>\n <AriaButton\n className={cn(linkClass, \"font-normal text-text-secondary no-underline\")}\n onPress={() => {\n setPending(null);\n say(\"Kept the previous shortcut\");\n buttonRef.current?.focus();\n }}\n >\n Cancel\n </AriaButton>\n </span>\n </div>\n ) : null}\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {announcement}\n </span>\n </FieldShell>\n </div>\n );\n}\n"],"mappings":"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"}
|
package/dist/slider/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Slider, SliderProps, sliderBarVariants, sliderFillVariants, sliderThumbVariants, sliderTrackVariants, sliderVariants } from "./slider.js";
|
|
2
|
-
export { Slider, type SliderProps, sliderBarVariants, sliderFillVariants, sliderThumbVariants, sliderTrackVariants, sliderVariants };
|
|
1
|
+
import { Slider, SliderMark, SliderProps, SliderValue, sliderBarVariants, sliderFillVariants, sliderThumbVariants, sliderTrackVariants, sliderVariants } from "./slider.js";
|
|
2
|
+
export { Slider, type SliderMark, type SliderProps, type SliderValue, sliderBarVariants, sliderFillVariants, sliderThumbVariants, sliderTrackVariants, sliderVariants };
|