@ctrl-ui/react 0.8.6 → 0.8.8
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/dist/action-bar.d.ts +15 -7
- package/dist/action-bar.js +39 -19
- package/dist/activity.js +7 -15
- package/dist/audio-recorder.d.ts +14 -4
- package/dist/audio-recorder.js +82 -29
- package/dist/audio-visualizer.js +1 -1
- package/dist/blocks/coding-agent.js +54 -57
- package/dist/blocks/design-canvas-data.d.ts +32 -0
- package/dist/blocks/design-canvas-data.js +100 -0
- package/dist/blocks/design-canvas-inspector.d.ts +8 -0
- package/dist/blocks/design-canvas-inspector.js +238 -0
- package/dist/blocks/design-canvas.d.ts +21 -0
- package/dist/blocks/design-canvas.js +452 -0
- package/dist/blocks/discussion.d.ts +28 -0
- package/dist/blocks/discussion.js +67 -0
- package/dist/blocks/file-explorer-data.d.ts +1 -0
- package/dist/blocks/file-explorer-data.js +4 -1
- package/dist/blocks/file-explorer.d.ts +9 -1
- package/dist/blocks/file-explorer.js +461 -192
- package/dist/blocks/settings.d.ts +2 -1
- package/dist/blocks/settings.js +101 -79
- package/dist/blocks/team-chat.d.ts +12 -0
- package/dist/blocks/team-chat.js +26 -0
- package/dist/blocks/theme-toggle.js +23 -31
- package/dist/chat-composer-attachment.d.ts +12 -3
- package/dist/chat-composer-attachment.js +61 -19
- package/dist/chat-composer-editor/extensions/mention.d.ts +5 -1
- package/dist/chat-composer-editor/extensions/mention.js +58 -27
- package/dist/chat-composer-editor/ghost.js +2 -4
- package/dist/chat-composer-editor/types.d.ts +5 -0
- package/dist/chat-composer-editor.d.ts +1 -1
- package/dist/chat-composer-editor.js +71 -28
- package/dist/chat-composer.d.ts +13 -32
- package/dist/chat-composer.js +54 -13
- package/dist/chat-layout.d.ts +19 -0
- package/dist/chat-layout.js +96 -32
- package/dist/chat-message/social.d.ts +26 -0
- package/dist/chat-message/social.js +122 -0
- package/dist/chat-message.d.ts +28 -29
- package/dist/chat-message.js +106 -25
- package/dist/context.d.ts +2 -2
- package/dist/context.js +38 -23
- package/dist/dynamic-notification-canvas-loop.d.ts +24 -0
- package/dist/dynamic-notification-canvas-loop.js +113 -0
- package/dist/dynamic-notification-glass.js +23 -114
- package/dist/dynamic-notification-liquid.js +14 -118
- package/dist/dynamic-notification.d.ts +16 -8
- package/dist/dynamic-notification.js +85 -22
- package/dist/email/email-brand.d.ts +7 -4
- package/dist/email/email-brand.js +9 -6
- package/dist/email/email-code.d.ts +3 -2
- package/dist/email/email-code.js +11 -9
- package/dist/email/email-footer.d.ts +5 -3
- package/dist/email/email-footer.js +11 -5
- package/dist/email/email.d.ts +31 -9
- package/dist/email/email.js +50 -26
- package/dist/email/templates.d.ts +10 -9
- package/dist/email/templates.js +102 -38
- package/dist/email/theme.js +1 -1
- package/dist/environment-variables.d.ts +4 -2
- package/dist/environment-variables.js +52 -32
- package/dist/filter-bar/context.d.ts +32 -0
- package/dist/filter-bar/context.js +11 -0
- package/dist/filter-bar/editor.d.ts +14 -0
- package/dist/filter-bar/editor.js +216 -0
- package/dist/filter-bar/model.d.ts +10 -0
- package/dist/filter-bar/model.js +57 -0
- package/dist/filter-bar/motion.d.ts +10 -0
- package/dist/filter-bar/motion.js +84 -0
- package/dist/filter-bar/parts.d.ts +19 -0
- package/dist/filter-bar/parts.js +244 -0
- package/dist/filter-bar/types.d.ts +54 -0
- package/dist/filter-bar/types.js +0 -0
- package/dist/filter-bar.d.ts +27 -0
- package/dist/filter-bar.js +310 -0
- package/dist/hooks/use-audio-recorder.js +12 -6
- package/dist/hooks/use-chat-composer.d.ts +21 -7
- package/dist/hooks/use-chat-composer.js +32 -5
- package/dist/hooks/use-chat-message.d.ts +28 -2
- package/dist/hooks/use-chat-message.js +41 -10
- package/dist/hooks/use-chat-thread-scroll.js +43 -7
- package/dist/hooks/use-color-area.d.ts +3 -3
- package/dist/hooks/use-color-area.js +13 -12
- package/dist/hooks/use-copy-to-clipboard.d.ts +2 -1
- package/dist/hooks/use-copy-to-clipboard.js +20 -20
- package/dist/hooks/use-dropzone.d.ts +7 -4
- package/dist/hooks/use-dropzone.js +34 -3
- package/dist/hooks/use-dynamic-notification.d.ts +6 -2
- package/dist/hooks/use-dynamic-notification.js +26 -10
- package/dist/hooks/use-mobile.d.ts +3 -2
- package/dist/hooks/use-mobile.js +8 -4
- package/dist/hooks/use-textarea-trigger-menu.js +2 -1
- package/dist/hooks/use-trigger-menu.d.ts +54 -0
- package/dist/hooks/use-trigger-menu.js +102 -47
- package/dist/hooks/use-user-ask.d.ts +2 -0
- package/dist/hooks/use-user-ask.js +35 -41
- package/dist/inline-attachment.d.ts +6 -2
- package/dist/inline-attachment.js +44 -29
- package/dist/inline-citation.d.ts +12 -5
- package/dist/inline-citation.js +25 -16
- package/dist/knob-contracts/audio-recorder-knobs.d.ts +1 -1
- package/dist/knob-contracts/audio-recorder-knobs.js +2 -1
- package/dist/knob-contracts/audio-visualizer-knobs.d.ts +1 -1
- package/dist/knob-contracts/audio-visualizer-knobs.js +2 -1
- package/dist/knob-contracts/avatar-knobs.d.ts +1 -1
- package/dist/knob-contracts/avatar-knobs.js +6 -1
- package/dist/knob-contracts/badge-knobs.d.ts +1 -1
- package/dist/knob-contracts/badge-knobs.js +2 -1
- package/dist/knob-contracts/breadcrumb-knobs.d.ts +4 -0
- package/dist/knob-contracts/breadcrumb-knobs.js +8 -0
- package/dist/knob-contracts/button-knobs.d.ts +1 -1
- package/dist/knob-contracts/button-knobs.js +1 -0
- package/dist/knob-contracts/chat-layout-knobs.d.ts +1 -1
- package/dist/knob-contracts/chat-layout-knobs.js +2 -1
- package/dist/knob-contracts/chat-message-knobs.d.ts +1 -1
- package/dist/knob-contracts/chat-message-knobs.js +8 -1
- package/dist/knob-contracts/choice-knobs.d.ts +1 -1
- package/dist/knob-contracts/choice-knobs.js +1 -0
- package/dist/knob-contracts/code-diff-knobs.d.ts +1 -1
- package/dist/knob-contracts/code-diff-knobs.js +2 -0
- package/dist/knob-contracts/code-knobs.d.ts +1 -1
- package/dist/knob-contracts/code-knobs.js +2 -1
- package/dist/knob-contracts/collapsible-knobs.d.ts +1 -1
- package/dist/knob-contracts/collapsible-knobs.js +1 -1
- package/dist/knob-contracts/dynamic-notification-knobs.d.ts +1 -1
- package/dist/knob-contracts/dynamic-notification-knobs.js +2 -0
- package/dist/knob-contracts/emoji-picker-knobs.d.ts +4 -0
- package/dist/knob-contracts/emoji-picker-knobs.js +10 -0
- package/dist/knob-contracts/field-knobs.d.ts +1 -1
- package/dist/knob-contracts/field-knobs.js +5 -1
- package/dist/knob-contracts/filter-bar-knobs.d.ts +4 -0
- package/dist/knob-contracts/filter-bar-knobs.js +15 -0
- package/dist/knob-contracts/infinite-canvas-knobs.d.ts +1 -1
- package/dist/knob-contracts/infinite-canvas-knobs.js +3 -0
- package/dist/knob-contracts/markdown-knobs.d.ts +1 -1
- package/dist/knob-contracts/markdown-knobs.js +3 -1
- package/dist/knob-contracts/page-layout-knobs.d.ts +1 -1
- package/dist/knob-contracts/page-layout-knobs.js +4 -1
- package/dist/knob-contracts/popup-knobs.d.ts +1 -1
- package/dist/knob-contracts/popup-knobs.js +28 -0
- package/dist/knob-contracts/range-knobs.d.ts +1 -1
- package/dist/knob-contracts/range-knobs.js +1 -0
- package/dist/knob-contracts/rich-tooltip-knobs.d.ts +1 -1
- package/dist/knob-contracts/rich-tooltip-knobs.js +2 -1
- package/dist/knob-contracts/sidebar-knobs.d.ts +1 -1
- package/dist/knob-contracts/sidebar-knobs.js +27 -2
- package/dist/knob-contracts/spinner-knobs.d.ts +1 -1
- package/dist/knob-contracts/spinner-knobs.js +1 -1
- package/dist/knob-contracts/stepper-knobs.d.ts +1 -1
- package/dist/knob-contracts/stepper-knobs.js +4 -1
- package/dist/knob-contracts/switch-knobs.d.ts +1 -1
- package/dist/knob-contracts/switch-knobs.js +5 -0
- package/dist/knob-contracts/table-of-contents-knobs.d.ts +1 -1
- package/dist/knob-contracts/table-of-contents-knobs.js +4 -5
- package/dist/knob-contracts/tabs-knobs.d.ts +1 -1
- package/dist/knob-contracts/tabs-knobs.js +1 -0
- package/dist/knob-contracts/tree-knobs.d.ts +1 -1
- package/dist/knob-contracts/tree-knobs.js +2 -1
- package/dist/lib/cn.js +0 -4
- package/dist/lib/contrast.d.ts +1 -0
- package/dist/lib/contrast.js +17 -5
- package/dist/lib/dropzone-validation.js +41 -28
- package/dist/lib/env-file.d.ts +1 -1
- package/dist/lib/env-file.js +5 -5
- package/dist/lib/motion.d.ts +4 -0
- package/dist/lib/motion.js +15 -0
- package/dist/lib/trigger-detect.js +6 -8
- package/dist/markdown-block.js +13 -8
- package/dist/popup-parts.d.ts +8 -0
- package/dist/popup-parts.js +43 -0
- package/dist/scripts/contrast-eval.js +5 -4
- package/dist/source-badge.js +3 -1
- package/dist/styles/code.css +0 -2
- package/dist/styles/effects.css +6 -32
- package/dist/styles/index.css +14 -8
- package/dist/styles/recipes/accordion.css +9 -6
- package/dist/styles/recipes/action-bar.css +0 -15
- package/dist/styles/recipes/activity.css +5 -11
- package/dist/styles/recipes/alert.css +4 -4
- package/dist/styles/recipes/audio-recorder.css +15 -3
- package/dist/styles/recipes/audio-visualizer.css +13 -2
- package/dist/styles/recipes/avatar.css +78 -1
- package/dist/styles/recipes/badge.css +14 -4
- package/dist/styles/recipes/breadcrumb.css +77 -0
- package/dist/styles/recipes/button-group.css +6 -8
- package/dist/styles/recipes/button-states.css +66 -0
- package/dist/styles/recipes/button.css +26 -85
- package/dist/styles/recipes/calendar.css +17 -23
- package/dist/styles/recipes/card.css +2 -2
- package/dist/styles/recipes/chat-composer-attachment.css +9 -3
- package/dist/styles/recipes/chat-composer.css +21 -16
- package/dist/styles/recipes/chat-layout.css +64 -2
- package/dist/styles/recipes/chat-message-social.css +123 -0
- package/dist/styles/recipes/chat-message.css +157 -37
- package/dist/styles/recipes/choice.css +25 -21
- package/dist/styles/recipes/code-diff.css +51 -26
- package/dist/styles/recipes/code.css +44 -18
- package/dist/styles/recipes/collapsible.css +43 -1
- package/dist/styles/recipes/color-picker-details.css +3 -4
- package/dist/styles/recipes/color-picker-values.css +9 -8
- package/dist/styles/recipes/color-picker.css +23 -4
- package/dist/styles/recipes/context.css +3 -3
- package/dist/styles/recipes/dockable-panel.css +18 -7
- package/dist/styles/recipes/dropzone.css +7 -8
- package/dist/styles/recipes/dynamic-notification-input.css +8 -5
- package/dist/styles/recipes/dynamic-notification-motion.css +12 -21
- package/dist/styles/recipes/dynamic-notification.css +40 -26
- package/dist/styles/recipes/emoji-picker.css +132 -0
- package/dist/styles/recipes/empty.css +5 -4
- package/dist/styles/recipes/environment-variables.css +4 -15
- package/dist/styles/recipes/field-parts.css +89 -21
- package/dist/styles/recipes/field.css +83 -26
- package/dist/styles/recipes/filter-bar.css +161 -0
- package/dist/styles/recipes/gradient-editor.css +18 -7
- package/dist/styles/recipes/infinite-canvas.css +51 -4
- package/dist/styles/recipes/inline-attachment.css +9 -4
- package/dist/styles/recipes/inline-citation.css +10 -4
- package/dist/styles/recipes/item.css +23 -9
- package/dist/styles/recipes/markdown-block.css +5 -4
- package/dist/styles/recipes/markdown.css +68 -26
- package/dist/styles/recipes/morphing-panel.css +8 -8
- package/dist/styles/recipes/page-layout.css +36 -10
- package/dist/styles/recipes/pagination.css +5 -6
- package/dist/styles/recipes/phone-input.css +12 -8
- package/dist/styles/recipes/popup-controls.css +175 -26
- package/dist/styles/recipes/popup-drawer.css +35 -44
- package/dist/styles/recipes/popup-lists.css +139 -29
- package/dist/styles/recipes/popup-motion.css +4 -45
- package/dist/styles/recipes/popup-toast.css +55 -26
- package/dist/styles/recipes/popup.css +149 -153
- package/dist/styles/recipes/range.css +55 -19
- package/dist/styles/recipes/resizable.css +36 -7
- package/dist/styles/recipes/rich-tooltip.css +24 -34
- package/dist/styles/recipes/scroll-area.css +8 -1
- package/dist/styles/recipes/sidebar-menu.css +149 -34
- package/dist/styles/recipes/sidebar-parts.css +154 -0
- package/dist/styles/recipes/sidebar.css +187 -128
- package/dist/styles/recipes/source-badge.css +1 -1
- package/dist/styles/recipes/spinner.css +12 -1
- package/dist/styles/recipes/stepper.css +31 -13
- package/dist/styles/recipes/switch.css +68 -23
- package/dist/styles/recipes/table-of-contents.css +49 -41
- package/dist/styles/recipes/table.css +14 -10
- package/dist/styles/recipes/tabs.css +38 -12
- package/dist/styles/recipes/task-list.css +7 -14
- package/dist/styles/recipes/thread-rail.css +42 -19
- package/dist/styles/recipes/timeline.css +2 -0
- package/dist/styles/recipes/toolbar.css +27 -25
- package/dist/styles/recipes/track-highlight.css +12 -8
- package/dist/styles/recipes/transcript-divider.css +2 -2
- package/dist/styles/recipes/tree.css +31 -4
- package/dist/styles/recipes/user-ask-details.css +0 -1
- package/dist/styles/recipes/user-ask.css +4 -4
- package/dist/styles/theme.css +287 -107
- package/dist/task-list.d.ts +4 -1
- package/dist/task-list.js +24 -8
- package/dist/thread-rail.d.ts +1 -1
- package/dist/thread-rail.js +61 -19
- package/dist/transcript-divider.js +2 -1
- package/dist/ui/alert.d.ts +3 -1
- package/dist/ui/alert.js +14 -8
- package/dist/ui/autocomplete.js +7 -1
- package/dist/ui/avatar.d.ts +8 -0
- package/dist/ui/avatar.js +23 -3
- package/dist/ui/badge.js +1 -1
- package/dist/ui/breadcrumb.d.ts +27 -0
- package/dist/ui/breadcrumb.js +88 -0
- package/dist/ui/button.d.ts +2 -1
- package/dist/ui/button.js +1 -1
- package/dist/ui/calendar.js +2 -2
- package/dist/ui/card.d.ts +3 -1
- package/dist/ui/card.js +12 -7
- package/dist/ui/checkbox-group.js +1 -1
- package/dist/ui/checkbox.d.ts +2 -0
- package/dist/ui/code-diff.d.ts +1 -1
- package/dist/ui/code-diff.js +71 -27
- package/dist/ui/code.d.ts +5 -3
- package/dist/ui/code.js +27 -28
- package/dist/ui/color-picker.d.ts +5 -2
- package/dist/ui/color-picker.js +84 -36
- package/dist/ui/combobox-disabled-selection.d.ts +1 -2
- package/dist/ui/combobox-disabled-selection.js +7 -8
- package/dist/ui/combobox.d.ts +5 -23
- package/dist/ui/combobox.js +36 -16
- package/dist/ui/command.d.ts +8 -2
- package/dist/ui/command.js +39 -15
- package/dist/ui/context-menu.js +3 -0
- package/dist/ui/dialog.d.ts +2 -1
- package/dist/ui/dialog.js +3 -22
- package/dist/ui/dockable-panel-geometry.d.ts +1 -0
- package/dist/ui/dockable-panel-geometry.js +7 -1
- package/dist/ui/dockable-panel.d.ts +9 -6
- package/dist/ui/dockable-panel.js +81 -33
- package/dist/ui/drawer.d.ts +7 -4
- package/dist/ui/drawer.js +35 -22
- package/dist/ui/dropdown-menu.js +4 -3
- package/dist/ui/dropzone.d.ts +5 -3
- package/dist/ui/dropzone.js +78 -17
- package/dist/ui/emoji-picker.d.ts +18 -0
- package/dist/ui/emoji-picker.js +136 -0
- package/dist/ui/empty.d.ts +3 -1
- package/dist/ui/empty.js +12 -7
- package/dist/ui/field.js +1 -1
- package/dist/ui/gradient-editor.d.ts +1 -1
- package/dist/ui/gradient-editor.js +37 -21
- package/dist/ui/infinite-canvas.d.ts +13 -1
- package/dist/ui/infinite-canvas.js +139 -20
- package/dist/ui/input-group.d.ts +2 -4
- package/dist/ui/input-group.js +13 -14
- package/dist/ui/input-otp.d.ts +1 -1
- package/dist/ui/input-otp.js +32 -15
- package/dist/ui/input.js +11 -2
- package/dist/ui/item.d.ts +2 -1
- package/dist/ui/item.js +11 -7
- package/dist/ui/live-status.d.ts +7 -0
- package/dist/ui/live-status.js +17 -0
- package/dist/ui/markdown-elements.d.ts +6 -0
- package/dist/ui/markdown-elements.js +42 -9
- package/dist/ui/menubar.js +2 -2
- package/dist/ui/morphing-panel.js +2 -1
- package/dist/ui/native-select.d.ts +1 -1
- package/dist/ui/native-select.js +24 -11
- package/dist/ui/number-field.d.ts +1 -1
- package/dist/ui/number-field.js +13 -2
- package/dist/ui/page-layout.d.ts +2 -2
- package/dist/ui/page-layout.js +2 -4
- package/dist/ui/pagination.d.ts +7 -5
- package/dist/ui/pagination.js +19 -13
- package/dist/ui/phone-input.d.ts +1 -0
- package/dist/ui/phone-input.js +56 -58
- package/dist/ui/radio-group.d.ts +3 -0
- package/dist/ui/radio-group.js +11 -1
- package/dist/ui/resizable.d.ts +20 -2
- package/dist/ui/resizable.js +130 -8
- package/dist/ui/responsive-dialog.d.ts +1 -1
- package/dist/ui/responsive-dialog.js +9 -23
- package/dist/ui/rich-tooltip.d.ts +10 -4
- package/dist/ui/rich-tooltip.js +81 -33
- package/dist/ui/scroll-area.d.ts +4 -1
- package/dist/ui/scroll-area.js +44 -28
- package/dist/ui/select.d.ts +2 -1
- package/dist/ui/select.js +57 -32
- package/dist/ui/sidebar-menu.d.ts +5 -1
- package/dist/ui/sidebar-menu.js +31 -33
- package/dist/ui/sidebar-provider.d.ts +10 -1
- package/dist/ui/sidebar-provider.js +38 -13
- package/dist/ui/sidebar-resize-rail.js +7 -3
- package/dist/ui/sidebar.d.ts +11 -6
- package/dist/ui/sidebar.js +46 -23
- package/dist/ui/slider.js +31 -22
- package/dist/ui/spinner.d.ts +3 -2
- package/dist/ui/spinner.js +3 -2
- package/dist/ui/stepper.d.ts +8 -1
- package/dist/ui/stepper.js +74 -25
- package/dist/ui/switch.d.ts +1 -0
- package/dist/ui/table-of-contents.d.ts +5 -4
- package/dist/ui/table-of-contents.js +143 -52
- package/dist/ui/table.js +15 -9
- package/dist/ui/textarea.d.ts +1 -1
- package/dist/ui/textarea.js +20 -9
- package/dist/ui/timeline.d.ts +3 -1
- package/dist/ui/timeline.js +17 -9
- package/dist/ui/toast.d.ts +11 -5
- package/dist/ui/toast.js +100 -54
- package/dist/ui/toggle.d.ts +1 -1
- package/dist/ui/toggle.js +2 -1
- package/dist/ui/toolbar.d.ts +3 -2
- package/dist/ui/toolbar.js +4 -2
- package/dist/ui/tooltip.js +3 -20
- package/dist/ui/tree.js +120 -80
- package/dist/ui/trigger-menu.js +7 -2
- package/dist/user-ask.d.ts +10 -5
- package/dist/user-ask.js +31 -11
- package/package.json +5 -1
- package/dist/hooks/use-keyboard-navigation.d.ts +0 -3
- package/dist/hooks/use-keyboard-navigation.js +0 -22
package/dist/ui/tree.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { useSkin } from "../skin-provider.js";
|
|
4
4
|
import { TrackHighlight } from "../extensions/track-highlight.js";
|
|
5
|
-
import { Children, createContext, isValidElement, useContext, useRef, useState } from "react";
|
|
5
|
+
import { Children, createContext, isValidElement, useContext, useEffect, useId, useRef, useState } from "react";
|
|
6
6
|
import { useRender } from "@base-ui/react/use-render";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { ChevronRightIcon } from "lucide-react";
|
|
@@ -34,8 +34,18 @@ function compareTreeItems(a, b) {
|
|
|
34
34
|
if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;
|
|
35
35
|
return 0;
|
|
36
36
|
}
|
|
37
|
-
function
|
|
38
|
-
|
|
37
|
+
function isUnderExpandedAncestors(item, items, expanded) {
|
|
38
|
+
for (let parentValue = item.parentValue; parentValue !== void 0; parentValue = items.get(parentValue)?.parentValue) if (!expanded.has(parentValue)) return false;
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
function reachableItems(items, expanded) {
|
|
42
|
+
return [...items.values()].filter((item) => isUnderExpandedAncestors(item, items, expanded)).sort(compareTreeItems);
|
|
43
|
+
}
|
|
44
|
+
function resolveTabStop(items, expanded, selected, focusedValue) {
|
|
45
|
+
const reachable = reachableItems(items, expanded);
|
|
46
|
+
if (reachable.some((item) => item.value === focusedValue)) return focusedValue;
|
|
47
|
+
const enabled = reachable.filter((item) => !item.disabled);
|
|
48
|
+
return (enabled.find((item) => selected.has(item.value)) ?? enabled[0])?.value ?? null;
|
|
39
49
|
}
|
|
40
50
|
function focusItem(item, ctx) {
|
|
41
51
|
if (!item) return;
|
|
@@ -55,40 +65,56 @@ function handleTypeahead(event, items, index, ctx, typeahead) {
|
|
|
55
65
|
const now = Date.now();
|
|
56
66
|
typeahead.current.query = now - typeahead.current.at > TYPEAHEAD_TIMEOUT ? event.key : typeahead.current.query + event.key;
|
|
57
67
|
typeahead.current.at = now;
|
|
58
|
-
const
|
|
59
|
-
const
|
|
68
|
+
const typed = typeahead.current.query.toLowerCase();
|
|
69
|
+
const query = [...typed].every((character) => character === typed[0]) ? typed.charAt(0) : typed;
|
|
70
|
+
const searchStart = query.length === 1 ? index + 1 : index;
|
|
71
|
+
const match = [...items.slice(searchStart), ...items.slice(0, searchStart)].find((item) => labelOf(item, ctx).toLowerCase().startsWith(query));
|
|
60
72
|
if (match) focusItem(match, ctx);
|
|
61
73
|
}
|
|
62
|
-
function registeredItemFromTarget(target, root, itemsByNode) {
|
|
63
|
-
let node = target;
|
|
64
|
-
while (node && root.contains(node)) {
|
|
65
|
-
const item = itemsByNode.get(node);
|
|
66
|
-
if (item) return item;
|
|
67
|
-
if (node === root) return;
|
|
68
|
-
node = node.parentElement;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
74
|
function getTreeKeyboardState(event, ctx) {
|
|
72
75
|
if (event.defaultPrevented || !(event.target instanceof HTMLElement)) return null;
|
|
73
|
-
const
|
|
74
|
-
if (!
|
|
75
|
-
const items = ctx.
|
|
76
|
-
const index = items.indexOf(
|
|
76
|
+
const item = ctx.getItemFromTarget(event.target, event.currentTarget);
|
|
77
|
+
if (!item) return null;
|
|
78
|
+
const items = ctx.getNavigableItems();
|
|
79
|
+
const index = items.indexOf(item);
|
|
77
80
|
if (index === -1) return null;
|
|
78
81
|
return {
|
|
79
|
-
|
|
82
|
+
item,
|
|
80
83
|
items,
|
|
81
84
|
index,
|
|
82
|
-
|
|
83
|
-
isExpanded: ctx.expanded.has(current.value),
|
|
84
|
-
level: current.level
|
|
85
|
+
isExpanded: ctx.expanded.has(item.value)
|
|
85
86
|
};
|
|
86
87
|
}
|
|
88
|
+
const RTL_ARROW_KEYS = {
|
|
89
|
+
ArrowLeft: "ArrowRight",
|
|
90
|
+
ArrowRight: "ArrowLeft"
|
|
91
|
+
};
|
|
92
|
+
function handleInlineArrow(key, { item, items, index, isExpanded }, ctx) {
|
|
93
|
+
const isBranch = item.expandable && !item.disabled;
|
|
94
|
+
if (key === "ArrowRight") {
|
|
95
|
+
if (!isBranch) return;
|
|
96
|
+
if (isExpanded) focusItem(items[index + 1], ctx);
|
|
97
|
+
else ctx.toggleExpanded(item.value, true, "keyboard");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (isBranch && isExpanded) ctx.toggleExpanded(item.value, false, "keyboard");
|
|
101
|
+
else focusParentItem(items, index, item.level, ctx);
|
|
102
|
+
}
|
|
103
|
+
function activateItem(key, item, ctx) {
|
|
104
|
+
if (item.disabled) return;
|
|
105
|
+
if (key === " ") {
|
|
106
|
+
ctx.select(item.value, "keyboard", ctx.selectionMode === "multiple");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
ctx.select(item.value, "keyboard", false);
|
|
110
|
+
if (item.expandable) ctx.toggleExpanded(item.value, void 0, "keyboard");
|
|
111
|
+
}
|
|
87
112
|
function handleTreeKeyDown(event, ctx, typeahead) {
|
|
88
113
|
const state = getTreeKeyboardState(event, ctx);
|
|
89
114
|
if (!state) return;
|
|
90
|
-
const {
|
|
91
|
-
|
|
115
|
+
const { item, items, index } = state;
|
|
116
|
+
const key = getComputedStyle(event.currentTarget).direction === "rtl" && RTL_ARROW_KEYS[event.key] || event.key;
|
|
117
|
+
switch (key) {
|
|
92
118
|
case "ArrowDown":
|
|
93
119
|
event.preventDefault();
|
|
94
120
|
focusItem(items[index + 1], ctx);
|
|
@@ -98,14 +124,9 @@ function handleTreeKeyDown(event, ctx, typeahead) {
|
|
|
98
124
|
focusItem(items[index - 1], ctx);
|
|
99
125
|
break;
|
|
100
126
|
case "ArrowRight":
|
|
101
|
-
event.preventDefault();
|
|
102
|
-
if (isBranch && !isExpanded) ctx.toggleExpanded(value, true, "keyboard");
|
|
103
|
-
else if (isBranch && isExpanded) focusItem(items[index + 1], ctx);
|
|
104
|
-
break;
|
|
105
127
|
case "ArrowLeft":
|
|
106
128
|
event.preventDefault();
|
|
107
|
-
|
|
108
|
-
else focusParentItem(items, index, level, ctx);
|
|
129
|
+
handleInlineArrow(key, state, ctx);
|
|
109
130
|
break;
|
|
110
131
|
case "Home":
|
|
111
132
|
event.preventDefault();
|
|
@@ -116,13 +137,9 @@ function handleTreeKeyDown(event, ctx, typeahead) {
|
|
|
116
137
|
focusItem(items[items.length - 1], ctx);
|
|
117
138
|
break;
|
|
118
139
|
case "Enter":
|
|
119
|
-
event.preventDefault();
|
|
120
|
-
ctx.select(value, "keyboard", false);
|
|
121
|
-
if (isBranch) ctx.toggleExpanded(value, void 0, "keyboard");
|
|
122
|
-
break;
|
|
123
140
|
case " ":
|
|
124
141
|
event.preventDefault();
|
|
125
|
-
|
|
142
|
+
activateItem(key, item, ctx);
|
|
126
143
|
break;
|
|
127
144
|
default: handleTypeahead(event, items, index, ctx, typeahead);
|
|
128
145
|
}
|
|
@@ -132,31 +149,23 @@ function Tree({ selectionMode = "single", indicator, value, defaultValue, onValu
|
|
|
132
149
|
const [selectedState, setSelectedState] = useState(() => new Set(defaultValue));
|
|
133
150
|
const [expandedState, setExpandedState] = useState(() => new Set(defaultExpandedValue));
|
|
134
151
|
const [focusedValue, setFocusedValue] = useState(null);
|
|
152
|
+
const [items, setItems] = useState(() => /* @__PURE__ */ new Map());
|
|
153
|
+
const [registerItem] = useState(() => (item) => {
|
|
154
|
+
setItems((current) => new Map(current).set(item.value, item));
|
|
155
|
+
return () => setItems((current) => {
|
|
156
|
+
if (current.get(item.value) !== item) return current;
|
|
157
|
+
const next = new Map(current);
|
|
158
|
+
next.delete(item.value);
|
|
159
|
+
return next;
|
|
160
|
+
});
|
|
161
|
+
});
|
|
135
162
|
const selected = value ? new Set(value) : selectedState;
|
|
136
163
|
const expanded = expandedValue ? new Set(expandedValue) : expandedState;
|
|
137
164
|
const typeahead = useRef({
|
|
138
165
|
query: "",
|
|
139
166
|
at: 0
|
|
140
167
|
});
|
|
141
|
-
const itemsRef = useRef(/* @__PURE__ */ new Map());
|
|
142
|
-
const itemsByNodeRef = useRef(/* @__PURE__ */ new WeakMap());
|
|
143
168
|
const labelsRef = useRef(/* @__PURE__ */ new Map());
|
|
144
|
-
const seededFocusRef = useRef(false);
|
|
145
|
-
const registerItem = (item) => {
|
|
146
|
-
const previous = itemsRef.current.get(item.value);
|
|
147
|
-
if (previous) itemsByNodeRef.current.delete(previous.node);
|
|
148
|
-
itemsRef.current.set(item.value, item);
|
|
149
|
-
itemsByNodeRef.current.set(item.node, item);
|
|
150
|
-
if (!seededFocusRef.current && !item.disabled) {
|
|
151
|
-
seededFocusRef.current = true;
|
|
152
|
-
setFocusedValue(item.value);
|
|
153
|
-
}
|
|
154
|
-
return () => {
|
|
155
|
-
if (itemsRef.current.get(item.value) !== item) return;
|
|
156
|
-
itemsRef.current.delete(item.value);
|
|
157
|
-
itemsByNodeRef.current.delete(item.node);
|
|
158
|
-
};
|
|
159
|
-
};
|
|
160
169
|
const registerLabel = (itemValue, node) => {
|
|
161
170
|
if (!node) return;
|
|
162
171
|
labelsRef.current.set(itemValue, node);
|
|
@@ -164,10 +173,15 @@ function Tree({ selectionMode = "single", indicator, value, defaultValue, onValu
|
|
|
164
173
|
if (labelsRef.current.get(itemValue) === node) labelsRef.current.delete(itemValue);
|
|
165
174
|
};
|
|
166
175
|
};
|
|
167
|
-
const
|
|
168
|
-
const getItemFromTarget = (target, root) =>
|
|
176
|
+
const getNavigableItems = () => reachableItems(items, expanded).filter((item) => item.node.checkVisibility());
|
|
177
|
+
const getItemFromTarget = (target, root) => {
|
|
178
|
+
const node = target.closest("[data-control-family=\"tree\"][data-slot=\"item\"]");
|
|
179
|
+
if (!(node instanceof HTMLLIElement) || !root.contains(node)) return;
|
|
180
|
+
const item = items.get(node.dataset.value ?? "");
|
|
181
|
+
return item?.node === node ? item : void 0;
|
|
182
|
+
};
|
|
169
183
|
const getLabel = (itemValue) => labelsRef.current.get(itemValue)?.textContent ?? "";
|
|
170
|
-
const getItem = (itemValue) =>
|
|
184
|
+
const getItem = (itemValue) => items.get(itemValue);
|
|
171
185
|
const select = (itemValue, reason, toggle) => {
|
|
172
186
|
if (selectionMode === "none") return;
|
|
173
187
|
let next;
|
|
@@ -201,10 +215,10 @@ function Tree({ selectionMode = "single", indicator, value, defaultValue, onValu
|
|
|
201
215
|
indicator: resolvedIndicator,
|
|
202
216
|
selected,
|
|
203
217
|
expanded,
|
|
204
|
-
focusedValue,
|
|
218
|
+
tabStopValue: resolveTabStop(items, expanded, selected, focusedValue),
|
|
205
219
|
registerItem,
|
|
206
220
|
registerLabel,
|
|
207
|
-
|
|
221
|
+
getNavigableItems,
|
|
208
222
|
getItemFromTarget,
|
|
209
223
|
getLabel,
|
|
210
224
|
getItem,
|
|
@@ -252,33 +266,48 @@ function Tree({ selectionMode = "single", indicator, value, defaultValue, onValu
|
|
|
252
266
|
}
|
|
253
267
|
function TreeItem({ value, disabled = false, label, className, style, children, ref, ...props }) {
|
|
254
268
|
const tree = useTree();
|
|
255
|
-
const
|
|
269
|
+
const parent = useContext(TreeItemContext);
|
|
270
|
+
const parentValue = parent?.value;
|
|
271
|
+
const level = (parent?.level ?? 0) + 1;
|
|
256
272
|
const expandable = Children.toArray(children).some((child) => isValidElement(child) && child.type === TreeItemContent);
|
|
273
|
+
const labelId = useId();
|
|
274
|
+
const [labelled, setLabelled] = useState(false);
|
|
275
|
+
const nodeRef = useRef(null);
|
|
276
|
+
const { registerItem } = tree;
|
|
277
|
+
useEffect(() => {
|
|
278
|
+
const node = nodeRef.current;
|
|
279
|
+
if (!node) return;
|
|
280
|
+
return registerItem({
|
|
281
|
+
value,
|
|
282
|
+
parentValue,
|
|
283
|
+
node,
|
|
284
|
+
level,
|
|
285
|
+
disabled,
|
|
286
|
+
expandable,
|
|
287
|
+
label
|
|
288
|
+
});
|
|
289
|
+
}, [
|
|
290
|
+
registerItem,
|
|
291
|
+
value,
|
|
292
|
+
parentValue,
|
|
293
|
+
level,
|
|
294
|
+
disabled,
|
|
295
|
+
expandable,
|
|
296
|
+
label
|
|
297
|
+
]);
|
|
257
298
|
const expanded = expandable && tree.expanded.has(value);
|
|
258
299
|
const selected = tree.selected.has(value);
|
|
259
300
|
const itemContext = {
|
|
260
301
|
value,
|
|
261
302
|
level,
|
|
262
303
|
disabled,
|
|
263
|
-
expandable
|
|
304
|
+
expandable,
|
|
305
|
+
labelId,
|
|
306
|
+
setLabelled
|
|
264
307
|
};
|
|
265
308
|
const itemRef = (node) => {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (!itemNode) return;
|
|
269
|
-
const unregister = tree.registerItem({
|
|
270
|
-
value,
|
|
271
|
-
node: itemNode,
|
|
272
|
-
level,
|
|
273
|
-
disabled,
|
|
274
|
-
expandable,
|
|
275
|
-
label
|
|
276
|
-
});
|
|
277
|
-
return () => {
|
|
278
|
-
unregister();
|
|
279
|
-
if (typeof refCleanup === "function") refCleanup();
|
|
280
|
-
else setRef(ref, null);
|
|
281
|
-
};
|
|
309
|
+
nodeRef.current = node instanceof HTMLLIElement ? node : null;
|
|
310
|
+
return setRef(ref, nodeRef.current);
|
|
282
311
|
};
|
|
283
312
|
const baseClass = cn("list-none", className);
|
|
284
313
|
const itemStyle = {
|
|
@@ -286,6 +315,7 @@ function TreeItem({ value, disabled = false, label, className, style, children,
|
|
|
286
315
|
...style
|
|
287
316
|
};
|
|
288
317
|
const shared = {
|
|
318
|
+
"aria-labelledby": labelled ? labelId : void 0,
|
|
289
319
|
...props,
|
|
290
320
|
"data-control-ui": "tree",
|
|
291
321
|
"data-control-family": "tree",
|
|
@@ -298,7 +328,7 @@ function TreeItem({ value, disabled = false, label, className, style, children,
|
|
|
298
328
|
"aria-level": level,
|
|
299
329
|
"aria-selected": tree.selectionMode === "none" ? void 0 : selected,
|
|
300
330
|
"aria-disabled": disabled || void 0,
|
|
301
|
-
tabIndex: tree.
|
|
331
|
+
tabIndex: tree.tabStopValue === value ? 0 : -1,
|
|
302
332
|
style: itemStyle
|
|
303
333
|
};
|
|
304
334
|
if (!expandable) return /* @__PURE__ */ jsx(TreeItemContext.Provider, {
|
|
@@ -321,7 +351,7 @@ function TreeItem({ value, disabled = false, label, className, style, children,
|
|
|
321
351
|
...shared,
|
|
322
352
|
role: "treeitem",
|
|
323
353
|
"aria-expanded": expanded,
|
|
324
|
-
tabIndex:
|
|
354
|
+
tabIndex: shared.tabIndex,
|
|
325
355
|
"data-state": expanded ? "open" : "closed",
|
|
326
356
|
className: cn(renderProps.className, baseClass)
|
|
327
357
|
}),
|
|
@@ -353,7 +383,7 @@ function TreeItemTrigger({ className, children, onClick, render, ...props }) {
|
|
|
353
383
|
if (item.expandable) tree.toggleExpanded(item.value);
|
|
354
384
|
focusItem(tree.getItem(item.value), tree);
|
|
355
385
|
},
|
|
356
|
-
className: cn("flex cursor-pointer select-none items-center", "
|
|
386
|
+
className: cn("flex cursor-pointer select-none items-center", "aria-disabled:pointer-events-none", className),
|
|
357
387
|
children
|
|
358
388
|
}
|
|
359
389
|
});
|
|
@@ -378,12 +408,20 @@ function TreeItemIndicator({ className, children, ...props }) {
|
|
|
378
408
|
"aria-hidden": true,
|
|
379
409
|
className: cn("inline-flex shrink-0 items-center justify-center", className),
|
|
380
410
|
...props,
|
|
381
|
-
children: children ?? /* @__PURE__ */ jsx(ChevronRightIcon, {
|
|
411
|
+
children: children ?? /* @__PURE__ */ jsx(ChevronRightIcon, {
|
|
412
|
+
"data-icon-dir": "inline",
|
|
413
|
+
className: "size-4"
|
|
414
|
+
})
|
|
382
415
|
});
|
|
383
416
|
}
|
|
384
417
|
function TreeItemLabel({ className, children, render, ref, ...props }) {
|
|
385
418
|
const tree = useTree();
|
|
386
419
|
const item = useTreeItem();
|
|
420
|
+
const { setLabelled } = item;
|
|
421
|
+
useEffect(() => {
|
|
422
|
+
setLabelled(true);
|
|
423
|
+
return () => setLabelled(false);
|
|
424
|
+
}, [setLabelled]);
|
|
387
425
|
const labelRef = (node) => {
|
|
388
426
|
const unregister = tree.registerLabel(item.value, node);
|
|
389
427
|
const cleanup = setRef(ref, node);
|
|
@@ -398,8 +436,10 @@ function TreeItemLabel({ className, children, render, ref, ...props }) {
|
|
|
398
436
|
defaultTagName: "span",
|
|
399
437
|
render,
|
|
400
438
|
props: {
|
|
439
|
+
title: typeof children === "string" ? children : void 0,
|
|
401
440
|
...props,
|
|
402
441
|
ref: labelRef,
|
|
442
|
+
id: item.labelId,
|
|
403
443
|
"data-control-ui": "tree",
|
|
404
444
|
"data-control-family": "tree",
|
|
405
445
|
"data-slot": "item-label",
|
package/dist/ui/trigger-menu.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { controlEffectsAttribute } from "../skin.js";
|
|
4
4
|
import { useSkin } from "../skin-provider.js";
|
|
5
|
+
import { ScrollArea } from "./scroll-area.js";
|
|
5
6
|
import { popupItemStructureClasses } from "../surface-variants.js";
|
|
6
7
|
import { jsx } from "react/jsx-runtime";
|
|
7
8
|
import { Popover } from "@base-ui/react/popover";
|
|
@@ -33,8 +34,11 @@ function TriggerMenu({ open, onOpenChange, anchorRect, side = "top", align = "st
|
|
|
33
34
|
"data-popup-part": "list-surface",
|
|
34
35
|
initialFocus: false,
|
|
35
36
|
finalFocus: false,
|
|
36
|
-
className: cn("max-h-[min(18rem,var(--available-height))] w-64 max-w-[var(--available-width)]
|
|
37
|
-
children
|
|
37
|
+
className: cn("grid max-h-[min(18rem,var(--available-height))] w-64 max-w-[var(--available-width)] grid-rows-[minmax(0,1fr)]", className),
|
|
38
|
+
children: /* @__PURE__ */ jsx(ScrollArea, {
|
|
39
|
+
lockAxis: "x",
|
|
40
|
+
children
|
|
41
|
+
})
|
|
38
42
|
})
|
|
39
43
|
}) })
|
|
40
44
|
});
|
|
@@ -99,6 +103,7 @@ function TriggerMenuGroup({ className, ...props }) {
|
|
|
99
103
|
"data-control-family": "popup",
|
|
100
104
|
"data-popup-kind": "trigger-menu",
|
|
101
105
|
"data-slot": "group",
|
|
106
|
+
role: "group",
|
|
102
107
|
className: cn("flex flex-col", className),
|
|
103
108
|
...props
|
|
104
109
|
});
|
package/dist/user-ask.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { CSSProperties, ComponentProps } from "react";
|
|
|
6
6
|
type UserAskStyleProps<Props, Style> = Omit<Props, "style"> & {
|
|
7
7
|
style?: CSSProperties & Style;
|
|
8
8
|
};
|
|
9
|
-
export declare function UserAsk({ onComplete, onDismiss, autoFocus, className, children, ...props }: UserAskProps): import("react").JSX.Element;
|
|
9
|
+
export declare function UserAsk({ onComplete, onDismiss, autoFocus, questionStatusLabel, className, children, ...props }: UserAskProps): import("react").JSX.Element;
|
|
10
10
|
export type UserAskHeaderProps = ComponentProps<"header"> & {
|
|
11
11
|
style?: CSSProperties & UserAskKnobStyle;
|
|
12
12
|
};
|
|
@@ -44,8 +44,13 @@ export type UserAskFooterProps = ComponentProps<"footer"> & {
|
|
|
44
44
|
style?: CSSProperties & UserAskKnobStyle;
|
|
45
45
|
};
|
|
46
46
|
export declare function UserAskFooter({ className, ...props }: UserAskFooterProps): import("react").JSX.Element;
|
|
47
|
-
export type UserAskDismissProps = ComponentProps<typeof Button
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
export declare function
|
|
47
|
+
export type UserAskDismissProps = ComponentProps<typeof Button> & {
|
|
48
|
+
label?: string;
|
|
49
|
+
};
|
|
50
|
+
export declare function UserAskDismiss({ label, className, children, onClick, ...props }: UserAskDismissProps): import("react").JSX.Element;
|
|
51
|
+
export type UserAskSubmitProps = ComponentProps<typeof Button> & {
|
|
52
|
+
continueLabel?: string;
|
|
53
|
+
submitLabel?: string;
|
|
54
|
+
};
|
|
55
|
+
export declare function UserAskSubmit({ continueLabel, submitLabel, className, children, disabled, onClick, ...props }: UserAskSubmitProps): import("react").JSX.Element;
|
|
51
56
|
//#endregion
|
package/dist/user-ask.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "./lib/cn.js";
|
|
3
3
|
import { Button } from "./ui/button.js";
|
|
4
|
+
import { LiveStatus } from "./ui/live-status.js";
|
|
4
5
|
import { useUserAsk } from "./hooks/use-user-ask.js";
|
|
5
6
|
import { Kbd } from "./ui/kbd.js";
|
|
6
|
-
import { createContext, useContext, useEffect, useId, useRef } from "react";
|
|
7
|
+
import { createContext, useContext, useEffect, useId, useRef, useState } from "react";
|
|
7
8
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
8
9
|
import { Check, ChevronLeft, ChevronRight, PencilLine } from "lucide-react";
|
|
9
10
|
//#region src/user-ask.tsx
|
|
@@ -19,13 +20,18 @@ function useUserAskQuestionContext() {
|
|
|
19
20
|
if (!context) throw new Error("UserAsk options must be rendered inside <UserAskQuestion>.");
|
|
20
21
|
return context;
|
|
21
22
|
}
|
|
22
|
-
function
|
|
23
|
+
function defaultQuestionStatusLabel(current, total, title) {
|
|
24
|
+
return `Question ${current} of ${total}: ${title}`;
|
|
25
|
+
}
|
|
26
|
+
function UserAsk({ onComplete, onDismiss, autoFocus = false, questionStatusLabel = defaultQuestionStatusLabel, className, children, ...props }) {
|
|
23
27
|
const ask = useUserAsk({
|
|
24
28
|
onComplete,
|
|
25
29
|
onDismiss
|
|
26
30
|
});
|
|
27
31
|
const titleId = useId();
|
|
28
32
|
const panelRef = useRef(null);
|
|
33
|
+
const [hasNavigated, setHasNavigated] = useState(false);
|
|
34
|
+
const questionStatus = hasNavigated && ask.activeQuestion ? questionStatusLabel(ask.activeIndex + 1, ask.questions.length, ask.activeQuestion.title) : "";
|
|
29
35
|
useEffect(() => {
|
|
30
36
|
if (autoFocus) panelRef.current?.focus();
|
|
31
37
|
}, [autoFocus]);
|
|
@@ -33,6 +39,7 @@ function UserAsk({ onComplete, onDismiss, autoFocus = false, className, children
|
|
|
33
39
|
useEffect(() => {
|
|
34
40
|
if (lastActiveIndex.current === ask.activeIndex) return;
|
|
35
41
|
lastActiveIndex.current = ask.activeIndex;
|
|
42
|
+
setHasNavigated(true);
|
|
36
43
|
const panel = panelRef.current;
|
|
37
44
|
const active = document.activeElement;
|
|
38
45
|
const activeIsUsable = active instanceof HTMLElement && panel?.contains(active) && active.checkVisibility();
|
|
@@ -43,7 +50,7 @@ function UserAsk({ onComplete, onDismiss, autoFocus = false, className, children
|
|
|
43
50
|
...ask,
|
|
44
51
|
titleId
|
|
45
52
|
},
|
|
46
|
-
children: /* @__PURE__ */
|
|
53
|
+
children: /* @__PURE__ */ jsxs("section", {
|
|
47
54
|
ref: panelRef,
|
|
48
55
|
"data-control-ui": "user-ask",
|
|
49
56
|
"data-control-family": "user-ask",
|
|
@@ -54,7 +61,7 @@ function UserAsk({ onComplete, onDismiss, autoFocus = false, className, children
|
|
|
54
61
|
onKeyDown: ask.handleKeyDown,
|
|
55
62
|
className: cn("relative w-full", className),
|
|
56
63
|
...props,
|
|
57
|
-
children
|
|
64
|
+
children: [children, /* @__PURE__ */ jsx(LiveStatus, { message: questionStatus })]
|
|
58
65
|
})
|
|
59
66
|
});
|
|
60
67
|
}
|
|
@@ -212,6 +219,7 @@ function UserAskOption({ value, recommended = false, disabled = false, className
|
|
|
212
219
|
role: question.multiple ? "checkbox" : "radio",
|
|
213
220
|
"aria-checked": selected,
|
|
214
221
|
disabled,
|
|
222
|
+
tabIndex: ask.isTabStop(question.id, key) ? 0 : -1,
|
|
215
223
|
"data-control-ui": "user-ask",
|
|
216
224
|
"data-control-family": "user-ask",
|
|
217
225
|
"data-slot": "option",
|
|
@@ -297,6 +305,7 @@ function UserAskOptionInput({ label = "Other", placeholder = "Type your own answ
|
|
|
297
305
|
role: question.multiple ? "checkbox" : "radio",
|
|
298
306
|
"aria-checked": false,
|
|
299
307
|
disabled,
|
|
308
|
+
tabIndex: ask.isTabStop(question.id, key) ? 0 : -1,
|
|
300
309
|
"data-control-ui": "user-ask",
|
|
301
310
|
"data-control-family": "user-ask",
|
|
302
311
|
"data-slot": "option",
|
|
@@ -334,7 +343,7 @@ function UserAskOptionInput({ label = "Other", placeholder = "Type your own answ
|
|
|
334
343
|
type: "button",
|
|
335
344
|
role: "checkbox",
|
|
336
345
|
"aria-checked": true,
|
|
337
|
-
"aria-label":
|
|
346
|
+
"aria-label": label,
|
|
338
347
|
"data-control-ui": "user-ask",
|
|
339
348
|
"data-control-family": "user-ask",
|
|
340
349
|
"data-slot": "option-indicator",
|
|
@@ -343,12 +352,17 @@ function UserAskOptionInput({ label = "Other", placeholder = "Type your own answ
|
|
|
343
352
|
onClick: () => ask.select(question.id, key),
|
|
344
353
|
className: "inline-flex shrink-0 items-center justify-center",
|
|
345
354
|
children: /* @__PURE__ */ jsx(Check, { className: "size-3" })
|
|
346
|
-
}) : /* @__PURE__ */ jsx("
|
|
347
|
-
|
|
355
|
+
}) : /* @__PURE__ */ jsx("button", {
|
|
356
|
+
type: "button",
|
|
357
|
+
role: "radio",
|
|
358
|
+
"aria-checked": true,
|
|
359
|
+
"aria-label": label,
|
|
360
|
+
tabIndex: -1,
|
|
348
361
|
"data-control-ui": "user-ask",
|
|
349
362
|
"data-control-family": "user-ask",
|
|
350
363
|
"data-slot": "option-indicator",
|
|
351
364
|
"data-selected": "",
|
|
365
|
+
onClick: () => inputRef.current?.focus(),
|
|
352
366
|
className: "inline-flex shrink-0 items-center justify-center",
|
|
353
367
|
children: /* @__PURE__ */ jsx(PencilLine, { className: "size-3" })
|
|
354
368
|
}), /* @__PURE__ */ jsx("input", {
|
|
@@ -376,7 +390,7 @@ function UserAskFooter({ className, ...props }) {
|
|
|
376
390
|
...props
|
|
377
391
|
});
|
|
378
392
|
}
|
|
379
|
-
function UserAskDismiss({ className, children, onClick, ...props }) {
|
|
393
|
+
function UserAskDismiss({ label = "Dismiss", className, children, onClick, ...props }) {
|
|
380
394
|
const ask = useUserAskContext();
|
|
381
395
|
function handleClick(event) {
|
|
382
396
|
onClick?.(event);
|
|
@@ -389,13 +403,17 @@ function UserAskDismiss({ className, children, onClick, ...props }) {
|
|
|
389
403
|
type: "button",
|
|
390
404
|
variant: "quiet",
|
|
391
405
|
size: "xs",
|
|
406
|
+
"aria-keyshortcuts": "Escape",
|
|
392
407
|
onClick: handleClick,
|
|
393
408
|
className: cn(className),
|
|
394
409
|
...props,
|
|
395
|
-
children: children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
410
|
+
children: children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [label, /* @__PURE__ */ jsx(Kbd, {
|
|
411
|
+
"aria-hidden": "true",
|
|
412
|
+
children: "Esc"
|
|
413
|
+
})] })
|
|
396
414
|
});
|
|
397
415
|
}
|
|
398
|
-
function UserAskSubmit({ className, children, disabled, onClick, ...props }) {
|
|
416
|
+
function UserAskSubmit({ continueLabel = "Continue", submitLabel = "Submit answers", className, children, disabled, onClick, ...props }) {
|
|
399
417
|
const ask = useUserAskContext();
|
|
400
418
|
function handleClick(event) {
|
|
401
419
|
onClick?.(event);
|
|
@@ -410,10 +428,12 @@ function UserAskSubmit({ className, children, disabled, onClick, ...props }) {
|
|
|
410
428
|
tone: "primary",
|
|
411
429
|
size: "xs",
|
|
412
430
|
disabled: disabled ?? !ask.canContinue,
|
|
431
|
+
"aria-keyshortcuts": "Enter",
|
|
413
432
|
onClick: handleClick,
|
|
414
433
|
className,
|
|
415
434
|
...props,
|
|
416
|
-
children: children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
435
|
+
children: children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [ask.isLastQuestion ? submitLabel : continueLabel, /* @__PURE__ */ jsx(Kbd, {
|
|
436
|
+
"aria-hidden": "true",
|
|
417
437
|
"data-user-ask-submit-kbd": "true",
|
|
418
438
|
children: "⏎"
|
|
419
439
|
})] })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ctrl-ui/react",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.8",
|
|
4
4
|
"description": "Control UI components, blocks, and primitives with neutral defaults and consumer-owned themes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"clsx": "^2.1.1",
|
|
41
41
|
"cmdk": "^1.1.1",
|
|
42
42
|
"diff": "^9.0.0",
|
|
43
|
+
"frimousse": "^0.4.0",
|
|
43
44
|
"libphonenumber-js": "^1.13.13",
|
|
44
45
|
"lucide-react": "^1.47.0",
|
|
45
46
|
"prosemirror-commands": "^1.7.2",
|
|
@@ -69,6 +70,9 @@
|
|
|
69
70
|
"diff": {
|
|
70
71
|
"optional": true
|
|
71
72
|
},
|
|
73
|
+
"frimousse": {
|
|
74
|
+
"optional": true
|
|
75
|
+
},
|
|
72
76
|
"libphonenumber-js": {
|
|
73
77
|
"optional": true
|
|
74
78
|
},
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
2
|
-
//#region src/hooks/use-keyboard-navigation.ts
|
|
3
|
-
function useKeyboardNavigation() {
|
|
4
|
-
const [keyboardNavigation, setKeyboardNavigation] = useState(false);
|
|
5
|
-
useEffect(() => {
|
|
6
|
-
function markKeyboardNavigation(event) {
|
|
7
|
-
if (event.key === "Tab") setKeyboardNavigation(true);
|
|
8
|
-
}
|
|
9
|
-
function clearKeyboardNavigation() {
|
|
10
|
-
setKeyboardNavigation(false);
|
|
11
|
-
}
|
|
12
|
-
document.addEventListener("keydown", markKeyboardNavigation, true);
|
|
13
|
-
document.addEventListener("pointerdown", clearKeyboardNavigation, true);
|
|
14
|
-
return () => {
|
|
15
|
-
document.removeEventListener("keydown", markKeyboardNavigation, true);
|
|
16
|
-
document.removeEventListener("pointerdown", clearKeyboardNavigation, true);
|
|
17
|
-
};
|
|
18
|
-
}, []);
|
|
19
|
-
return keyboardNavigation;
|
|
20
|
-
}
|
|
21
|
-
//#endregion
|
|
22
|
-
export { useKeyboardNavigation };
|