@ctrl-ui/react 0.8.7 → 0.8.9
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 +39 -39
- package/dist/blocks/design-canvas.d.ts +5 -1
- package/dist/blocks/design-canvas.js +198 -92
- 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 +4 -1
- package/dist/chat-composer-attachment.js +35 -10
- package/dist/chat-composer-editor/extensions/mention.d.ts +5 -1
- package/dist/chat-composer-editor/extensions/mention.js +59 -28
- 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 +57 -30
- package/dist/chat-composer.d.ts +13 -32
- package/dist/chat-composer.js +52 -12
- package/dist/chat-layout.d.ts +19 -0
- package/dist/chat-layout.js +91 -40
- 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/editor.js +20 -8
- package/dist/filter-bar/model.d.ts +1 -0
- package/dist/filter-bar/model.js +10 -1
- package/dist/filter-bar/motion.js +4 -4
- package/dist/filter-bar/parts.js +19 -8
- package/dist/filter-bar/types.d.ts +1 -0
- package/dist/filter-bar.js +19 -13
- package/dist/hooks/use-audio-recorder.js +12 -6
- package/dist/hooks/use-chat-composer.d.ts +19 -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-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 +36 -15
- 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-message-knobs.d.ts +1 -1
- package/dist/knob-contracts/chat-message-knobs.js +8 -1
- 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/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 +1 -1
- package/dist/knob-contracts/filter-bar-knobs.js +1 -2
- 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 +2 -1
- package/dist/knob-contracts/popup-knobs.d.ts +1 -1
- package/dist/knob-contracts/popup-knobs.js +27 -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 +2 -1
- 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 +2 -1
- package/dist/popup-parts.d.ts +8 -0
- package/dist/popup-parts.js +43 -0
- 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 +10 -5
- 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 +5 -7
- package/dist/styles/recipes/button-states.css +66 -0
- package/dist/styles/recipes/button.css +24 -83
- 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 +7 -10
- package/dist/styles/recipes/chat-layout.css +35 -0
- 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 +18 -21
- package/dist/styles/recipes/code-diff.css +46 -33
- package/dist/styles/recipes/code.css +36 -16
- package/dist/styles/recipes/collapsible.css +7 -2
- 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 +11 -2
- package/dist/styles/recipes/context.css +3 -3
- package/dist/styles/recipes/dockable-panel.css +7 -4
- 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 +30 -24
- package/dist/styles/recipes/field.css +61 -26
- package/dist/styles/recipes/filter-bar.css +18 -31
- package/dist/styles/recipes/gradient-editor.css +18 -7
- package/dist/styles/recipes/infinite-canvas.css +0 -1
- package/dist/styles/recipes/inline-attachment.css +3 -3
- 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 +20 -12
- package/dist/styles/recipes/pagination.css +4 -5
- package/dist/styles/recipes/phone-input.css +12 -8
- package/dist/styles/recipes/popup-controls.css +166 -33
- package/dist/styles/recipes/popup-drawer.css +3 -14
- package/dist/styles/recipes/popup-lists.css +128 -27
- package/dist/styles/recipes/popup-motion.css +4 -45
- package/dist/styles/recipes/popup-toast.css +55 -26
- package/dist/styles/recipes/popup.css +139 -141
- package/dist/styles/recipes/range.css +54 -18
- package/dist/styles/recipes/resizable.css +2 -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 +166 -34
- package/dist/styles/recipes/sidebar-parts.css +192 -0
- package/dist/styles/recipes/sidebar.css +188 -128
- package/dist/styles/recipes/source-badge.css +1 -1
- package/dist/styles/recipes/spinner.css +8 -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 +9 -1
- package/dist/styles/recipes/table.css +14 -10
- package/dist/styles/recipes/tabs.css +17 -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 +10 -7
- 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 +37 -16
- 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 +3 -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.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 +26 -27
- 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 -9
- package/dist/ui/combobox.js +13 -5
- package/dist/ui/command.d.ts +8 -2
- package/dist/ui/command.js +30 -14
- 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 +63 -23
- package/dist/ui/dropdown-menu.js +4 -3
- package/dist/ui/dropzone.d.ts +2 -2
- package/dist/ui/dropzone.js +75 -16
- 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/gradient-editor.d.ts +1 -1
- package/dist/ui/gradient-editor.js +37 -21
- package/dist/ui/infinite-canvas.d.ts +3 -2
- package/dist/ui/infinite-canvas.js +63 -19
- package/dist/ui/input-group.d.ts +0 -3
- package/dist/ui/input-group.js +3 -13
- package/dist/ui/input-otp.d.ts +1 -1
- package/dist/ui/input-otp.js +32 -15
- package/dist/ui/input.js +4 -4
- 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 +31 -1
- 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/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 +2 -0
- package/dist/ui/resizable.d.ts +3 -2
- package/dist/ui/resizable.js +5 -3
- package/dist/ui/responsive-dialog.d.ts +1 -1
- package/dist/ui/responsive-dialog.js +4 -21
- package/dist/ui/rich-tooltip.d.ts +10 -4
- package/dist/ui/rich-tooltip.js +81 -33
- package/dist/ui/scroll-area.d.ts +1 -1
- package/dist/ui/scroll-area.js +43 -28
- package/dist/ui/sidebar-menu.d.ts +5 -1
- package/dist/ui/sidebar-menu.js +34 -34
- 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 +48 -24
- 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 +2 -2
- package/dist/ui/table-of-contents.js +20 -8
- package/dist/ui/table.js +4 -2
- 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/toolbar.d.ts +2 -1
- package/dist/ui/toolbar.js +3 -1
- package/dist/ui/tooltip.js +3 -20
- package/dist/ui/tree.js +120 -80
- package/dist/ui/trigger-menu.js +1 -0
- 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
|
@@ -3,9 +3,9 @@ import { cn } from "./lib/cn.js";
|
|
|
3
3
|
import { Button } from "./ui/button.js";
|
|
4
4
|
import { DUPLICATE_ENVIRONMENT_VARIABLE_MESSAGE, collectEnvironmentVariables, rowsToEnvFileText } from "./lib/env-file.js";
|
|
5
5
|
import { useEnvironmentVariables } from "./hooks/use-environment-variables.js";
|
|
6
|
-
import { InputGroup, InputGroupAddon
|
|
6
|
+
import { InputGroup, InputGroupAddon } from "./ui/input-group.js";
|
|
7
7
|
import { Input } from "./ui/input.js";
|
|
8
|
-
import { createContext, use, useId } from "react";
|
|
8
|
+
import { createContext, use, useId, useRef, useState } from "react";
|
|
9
9
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
import { AlertTriangleIcon, EyeIcon, EyeOffIcon, FileUpIcon, PlusIcon, RotateCcwIcon, SaveIcon, Trash2Icon } from "lucide-react";
|
|
11
11
|
//#region src/environment-variables.tsx
|
|
@@ -15,18 +15,28 @@ function useEnvironmentVariablesContext(componentName) {
|
|
|
15
15
|
if (!context) throw new Error(`${componentName} must be used inside <EnvironmentVariables.Root>.`);
|
|
16
16
|
return context;
|
|
17
17
|
}
|
|
18
|
-
function EnvironmentVariablesRoot({ editor, disabled = false, readOnly = false, rowErrors, keyLabel = "Key", valueLabel = "Value", keyPlaceholder = "OPENAI_API_KEY", valuePlaceholder = "sk
|
|
19
|
-
|
|
18
|
+
function EnvironmentVariablesRoot({ editor, disabled = false, readOnly = false, rowErrors, keyLabel = "Key", valueLabel = "Value", keyPlaceholder = "OPENAI_API_KEY", valuePlaceholder = "sk-…", duplicateKeyMessage = DUPLICATE_ENVIRONMENT_VARIABLE_MESSAGE, revealValueLabel = (key) => `Show value for ${key}`, onSubmit, className, children, ...props }) {
|
|
19
|
+
const pendingRef = useRef(false);
|
|
20
|
+
const [pending, setPending] = useState(false);
|
|
21
|
+
async function handleSubmit(event) {
|
|
20
22
|
if (event.defaultPrevented || !onSubmit) return;
|
|
21
23
|
event.preventDefault();
|
|
22
|
-
if (editor.hasDuplicateKeys) return;
|
|
24
|
+
if (pendingRef.current || editor.hasDuplicateKeys) return;
|
|
23
25
|
const rows = editor.getRowsForSubmit();
|
|
24
|
-
|
|
26
|
+
const payload = {
|
|
25
27
|
rows,
|
|
26
28
|
variables: collectEnvironmentVariables(rows),
|
|
27
29
|
envFileText: rowsToEnvFileText(rows),
|
|
28
30
|
reset: () => editor.resetRows(rows)
|
|
29
|
-
}
|
|
31
|
+
};
|
|
32
|
+
pendingRef.current = true;
|
|
33
|
+
setPending(true);
|
|
34
|
+
try {
|
|
35
|
+
await onSubmit(payload);
|
|
36
|
+
} finally {
|
|
37
|
+
pendingRef.current = false;
|
|
38
|
+
setPending(false);
|
|
39
|
+
}
|
|
30
40
|
}
|
|
31
41
|
return /* @__PURE__ */ jsx(EnvironmentVariablesContext.Provider, {
|
|
32
42
|
value: {
|
|
@@ -38,7 +48,9 @@ function EnvironmentVariablesRoot({ editor, disabled = false, readOnly = false,
|
|
|
38
48
|
valueLabel,
|
|
39
49
|
keyPlaceholder,
|
|
40
50
|
valuePlaceholder,
|
|
41
|
-
duplicateKeyMessage
|
|
51
|
+
duplicateKeyMessage,
|
|
52
|
+
revealValueLabel,
|
|
53
|
+
pending
|
|
42
54
|
},
|
|
43
55
|
children: /* @__PURE__ */ jsx("form", {
|
|
44
56
|
"data-control-ui": "environment-variables",
|
|
@@ -47,6 +59,7 @@ function EnvironmentVariablesRoot({ editor, disabled = false, readOnly = false,
|
|
|
47
59
|
"data-surface": "panel",
|
|
48
60
|
"data-disabled": disabled ? "true" : void 0,
|
|
49
61
|
"data-readonly": readOnly ? "true" : void 0,
|
|
62
|
+
"aria-busy": pending || void 0,
|
|
50
63
|
className: cn("flex min-w-0 flex-col", className),
|
|
51
64
|
onSubmit: handleSubmit,
|
|
52
65
|
...props,
|
|
@@ -157,6 +170,7 @@ function EnvironmentVariablesUploadButton({ inputLabel = "Import .env file", chi
|
|
|
157
170
|
type: "file",
|
|
158
171
|
accept: ".env,text/plain",
|
|
159
172
|
className: "sr-only",
|
|
173
|
+
tabIndex: -1,
|
|
160
174
|
disabled: isDisabled,
|
|
161
175
|
"aria-label": inputLabel,
|
|
162
176
|
onChange: handleChange
|
|
@@ -203,7 +217,7 @@ function EnvironmentVariablesRows({ rowErrors, className, children, ...props })
|
|
|
203
217
|
});
|
|
204
218
|
}
|
|
205
219
|
function EnvironmentVariablesRow({ row, index, rowErrors, className, ...props }) {
|
|
206
|
-
const { editor, disabled, readOnly, rowErrors: contextRowErrors, keyLabel, valueLabel, keyPlaceholder, valuePlaceholder, duplicateKeyMessage } = useEnvironmentVariablesContext("EnvironmentVariables.Row");
|
|
220
|
+
const { editor, disabled, readOnly, rowErrors: contextRowErrors, keyLabel, valueLabel, keyPlaceholder, valuePlaceholder, duplicateKeyMessage, revealValueLabel } = useEnvironmentVariablesContext("EnvironmentVariables.Row");
|
|
207
221
|
const resolvedRowErrors = rowErrors ?? contextRowErrors;
|
|
208
222
|
const keyError = resolvedRowErrors?.[index]?.key ?? (editor.rowHasDuplicateKey(index) ? duplicateKeyMessage : null);
|
|
209
223
|
const valueError = resolvedRowErrors?.[index]?.value;
|
|
@@ -211,8 +225,11 @@ function EnvironmentVariablesRow({ row, index, rowErrors, className, ...props })
|
|
|
211
225
|
const rowId = editor.getRowId(index);
|
|
212
226
|
const keyInputId = `${rowId}-key`;
|
|
213
227
|
const valueInputId = `${rowId}-value`;
|
|
214
|
-
const
|
|
215
|
-
const
|
|
228
|
+
const keyErrorId = `${keyInputId}-error`;
|
|
229
|
+
const valueErrorId = `${valueInputId}-error`;
|
|
230
|
+
const rowName = row.key.trim() || `row ${index + 1}`;
|
|
231
|
+
const valueRevealed = editor.isValueRevealed(index);
|
|
232
|
+
const removeRowLabel = `Remove environment variable ${rowName}`;
|
|
216
233
|
function handlePaste(event) {
|
|
217
234
|
if (editor.handlePaste(index, event.clipboardData.getData("text"))) event.preventDefault();
|
|
218
235
|
}
|
|
@@ -235,19 +252,18 @@ function EnvironmentVariablesRow({ row, index, rowErrors, className, ...props })
|
|
|
235
252
|
children: keyLabel
|
|
236
253
|
}),
|
|
237
254
|
/* @__PURE__ */ jsx(Input, {
|
|
238
|
-
"data-control-ui": "environment-variables",
|
|
239
|
-
"data-control-family": "environment-variables",
|
|
240
|
-
"data-slot": "key-input",
|
|
241
255
|
id: keyInputId,
|
|
242
256
|
size: "sm",
|
|
243
257
|
value: row.key,
|
|
244
258
|
placeholder: keyPlaceholder,
|
|
245
259
|
disabled: isDisabled,
|
|
246
260
|
"aria-invalid": Boolean(keyError),
|
|
261
|
+
"aria-describedby": keyError ? keyErrorId : void 0,
|
|
247
262
|
onChange: (event) => editor.updateRow(index, { key: event.target.value }),
|
|
248
263
|
onPaste: handlePaste
|
|
249
264
|
}),
|
|
250
265
|
keyError ? /* @__PURE__ */ jsx("p", {
|
|
266
|
+
id: keyErrorId,
|
|
251
267
|
"data-control-ui": "environment-variables",
|
|
252
268
|
"data-control-family": "environment-variables",
|
|
253
269
|
"data-slot": "field-error",
|
|
@@ -267,35 +283,30 @@ function EnvironmentVariablesRow({ row, index, rowErrors, className, ...props })
|
|
|
267
283
|
children: valueLabel
|
|
268
284
|
}),
|
|
269
285
|
/* @__PURE__ */ jsxs(InputGroup, {
|
|
270
|
-
"data-control-ui": "environment-variables",
|
|
271
|
-
"data-control-family": "environment-variables",
|
|
272
|
-
"data-slot": "value-group",
|
|
273
|
-
"data-invalid": valueError ? "true" : void 0,
|
|
274
286
|
size: "sm",
|
|
275
|
-
children: [/* @__PURE__ */ jsx(
|
|
276
|
-
"data-control-ui": "environment-variables",
|
|
277
|
-
"data-control-family": "environment-variables",
|
|
278
|
-
"data-slot": "value-input",
|
|
287
|
+
children: [/* @__PURE__ */ jsx(Input, {
|
|
279
288
|
id: valueInputId,
|
|
280
289
|
value: row.value,
|
|
281
290
|
placeholder: valuePlaceholder,
|
|
282
291
|
disabled: isDisabled,
|
|
283
292
|
"aria-invalid": Boolean(valueError),
|
|
284
|
-
|
|
293
|
+
"aria-describedby": valueError ? valueErrorId : void 0,
|
|
294
|
+
type: valueRevealed ? "text" : "password",
|
|
285
295
|
onChange: (event) => editor.updateRow(index, { value: event.target.value }),
|
|
286
296
|
onPaste: handlePaste
|
|
287
297
|
}), /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
288
|
-
className: "
|
|
298
|
+
className: "pe-1",
|
|
289
299
|
children: /* @__PURE__ */ jsx(Button, {
|
|
290
300
|
type: "button",
|
|
291
301
|
variant: "quiet",
|
|
292
302
|
size: "sm",
|
|
293
303
|
iconOnly: true,
|
|
294
304
|
disabled: isDisabled,
|
|
295
|
-
"aria-label": revealValueLabel,
|
|
296
|
-
|
|
305
|
+
"aria-label": revealValueLabel(rowName),
|
|
306
|
+
"aria-pressed": valueRevealed,
|
|
307
|
+
title: revealValueLabel(rowName),
|
|
297
308
|
onClick: () => editor.toggleValueVisibility(index),
|
|
298
|
-
children:
|
|
309
|
+
children: valueRevealed ? /* @__PURE__ */ jsx(EyeOffIcon, {
|
|
299
310
|
"aria-hidden": "true",
|
|
300
311
|
className: "size-3.5"
|
|
301
312
|
}) : /* @__PURE__ */ jsx(EyeIcon, {
|
|
@@ -306,6 +317,7 @@ function EnvironmentVariablesRow({ row, index, rowErrors, className, ...props })
|
|
|
306
317
|
})]
|
|
307
318
|
}),
|
|
308
319
|
valueError ? /* @__PURE__ */ jsx("p", {
|
|
320
|
+
id: valueErrorId,
|
|
309
321
|
"data-control-ui": "environment-variables",
|
|
310
322
|
"data-control-family": "environment-variables",
|
|
311
323
|
"data-slot": "field-error",
|
|
@@ -322,7 +334,7 @@ function EnvironmentVariablesRow({ row, index, rowErrors, className, ...props })
|
|
|
322
334
|
disabled,
|
|
323
335
|
"aria-label": removeRowLabel,
|
|
324
336
|
title: "Remove variable",
|
|
325
|
-
className: "absolute top-2
|
|
337
|
+
className: "absolute top-2 end-1 sm:static sm:justify-self-end sm:self-start",
|
|
326
338
|
onClick: () => editor.removeRow(index),
|
|
327
339
|
children: /* @__PURE__ */ jsx(Trash2Icon, {
|
|
328
340
|
"aria-hidden": "true",
|
|
@@ -371,14 +383,14 @@ function EnvironmentVariablesResetButton({ children = "Reset", variant = "quiet"
|
|
|
371
383
|
});
|
|
372
384
|
}
|
|
373
385
|
function EnvironmentVariablesSubmitButton({ children = "Save", variant = "solid", tone = "primary", size = "sm", disabled, ...props }) {
|
|
374
|
-
const { editor, disabled: contextDisabled, readOnly } = useEnvironmentVariablesContext("EnvironmentVariables.SubmitButton");
|
|
386
|
+
const { editor, disabled: contextDisabled, readOnly, pending } = useEnvironmentVariablesContext("EnvironmentVariables.SubmitButton");
|
|
375
387
|
if (readOnly) return null;
|
|
376
388
|
return /* @__PURE__ */ jsxs(Button, {
|
|
377
389
|
type: "submit",
|
|
378
390
|
variant,
|
|
379
391
|
tone,
|
|
380
392
|
size,
|
|
381
|
-
disabled: disabled ?? (contextDisabled || editor.hasDuplicateKeys),
|
|
393
|
+
disabled: disabled ?? (contextDisabled || pending || editor.hasDuplicateKeys),
|
|
382
394
|
...props,
|
|
383
395
|
children: [/* @__PURE__ */ jsx(SaveIcon, {
|
|
384
396
|
"aria-hidden": "true",
|
|
@@ -449,7 +461,7 @@ function EnvironmentVariablesReadOnlyList({ rows, emptyMessage = "No environment
|
|
|
449
461
|
}))
|
|
450
462
|
});
|
|
451
463
|
}
|
|
452
|
-
function EnvironmentVariablesReadOnlyItem({ name, value =
|
|
464
|
+
function EnvironmentVariablesReadOnlyItem({ name, value, revealed = false, hiddenValueLabel = "Hidden value", className, ...props }) {
|
|
453
465
|
return /* @__PURE__ */ jsxs("div", {
|
|
454
466
|
"data-control-ui": "environment-variables",
|
|
455
467
|
"data-control-family": "environment-variables",
|
|
@@ -461,13 +473,21 @@ function EnvironmentVariablesReadOnlyItem({ name, value = "********", revealed =
|
|
|
461
473
|
"data-control-family": "environment-variables",
|
|
462
474
|
"data-slot": "readonly-key",
|
|
463
475
|
className: "min-w-0 truncate",
|
|
476
|
+
title: typeof name === "string" ? name : void 0,
|
|
464
477
|
children: name
|
|
465
478
|
}), /* @__PURE__ */ jsx("span", {
|
|
466
479
|
"data-control-ui": "environment-variables",
|
|
467
480
|
"data-control-family": "environment-variables",
|
|
468
481
|
"data-slot": "readonly-value",
|
|
469
482
|
className: "min-w-0 truncate",
|
|
470
|
-
|
|
483
|
+
title: revealed && typeof value === "string" ? value : void 0,
|
|
484
|
+
children: revealed ? value : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
485
|
+
"aria-hidden": "true",
|
|
486
|
+
children: "••••••••"
|
|
487
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
488
|
+
className: "sr-only",
|
|
489
|
+
children: hiddenValueLabel
|
|
490
|
+
})] })
|
|
471
491
|
})]
|
|
472
492
|
});
|
|
473
493
|
}
|
|
@@ -36,7 +36,8 @@ function editorChoices(context, draft) {
|
|
|
36
36
|
if (!field) return [];
|
|
37
37
|
if (draft.stage === "operator") return fieldOperators(field, context.operators).map((operator) => ({
|
|
38
38
|
id: operator.id,
|
|
39
|
-
label: operator.label
|
|
39
|
+
label: operator.label,
|
|
40
|
+
ariaLabel: operator.ariaLabel
|
|
40
41
|
}));
|
|
41
42
|
return valueChoices(field, draft.query);
|
|
42
43
|
}
|
|
@@ -45,6 +46,9 @@ function searchLabel(stage, field) {
|
|
|
45
46
|
if (stage === "operator") return "Filter operator";
|
|
46
47
|
return `${field?.label ?? "Filter"} value`;
|
|
47
48
|
}
|
|
49
|
+
function statusId(owner) {
|
|
50
|
+
return `${owner}-filter-status`;
|
|
51
|
+
}
|
|
48
52
|
function handleSelection(context, draft, choice) {
|
|
49
53
|
if (!choice || !draft) return;
|
|
50
54
|
const field = context.fields.find((candidate) => candidate.id === draft.fieldId);
|
|
@@ -70,19 +74,21 @@ function handleEditorKeyDown(context, draft, event) {
|
|
|
70
74
|
context.commit(draft.values);
|
|
71
75
|
return true;
|
|
72
76
|
}
|
|
73
|
-
function EditorOptions({ field, invalidNumber }) {
|
|
77
|
+
function EditorOptions({ field, invalidNumber, owner }) {
|
|
74
78
|
if (field?.loading || field?.error) return /* @__PURE__ */ jsx("div", {
|
|
79
|
+
id: statusId(owner),
|
|
75
80
|
className: "p-3 text-caption",
|
|
76
|
-
role: field.error ? "alert" : "status",
|
|
77
81
|
children: field.error ?? "Loading values…"
|
|
78
82
|
});
|
|
79
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ComboboxEmpty, { children: invalidNumber ? "Enter a
|
|
83
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ComboboxEmpty, { children: invalidNumber ? "Enter a number, like 42 or 3.5." : "No matching options." }), /* @__PURE__ */ jsx(ComboboxList, { children: (choice) => /* @__PURE__ */ jsx(ComboboxItem, {
|
|
80
84
|
value: choice,
|
|
81
85
|
disabled: choice.disabled,
|
|
86
|
+
"aria-label": choice.ariaLabel,
|
|
82
87
|
children: /* @__PURE__ */ jsxs("span", {
|
|
83
88
|
className: "flex min-w-0 items-center gap-2",
|
|
84
89
|
children: [choice.icon, /* @__PURE__ */ jsx("span", {
|
|
85
90
|
className: "truncate",
|
|
91
|
+
title: choice.label,
|
|
86
92
|
children: choice.label
|
|
87
93
|
})]
|
|
88
94
|
})
|
|
@@ -113,14 +119,15 @@ function editorView(context, draft) {
|
|
|
113
119
|
invalidNumber: stage === "value" && Boolean(customNumber) && query.trim() !== "" && field !== void 0 && parseCustomValue(field, query) === void 0,
|
|
114
120
|
remote: stage === "value" && field?.optionsMode === "remote",
|
|
115
121
|
unavailable: stage === "value" && Boolean(field?.loading || field?.error),
|
|
116
|
-
label: searchLabel(stage, field)
|
|
122
|
+
label: searchLabel(stage, field),
|
|
123
|
+
described: stage === "value" && Boolean(field?.error)
|
|
117
124
|
};
|
|
118
125
|
}
|
|
119
126
|
function FilterBarEditor({ owner, mode, anchorRef, children }) {
|
|
120
127
|
const context = useFilterBarContext();
|
|
121
128
|
const searchRef = useRef(null);
|
|
122
129
|
const draft = context.draft?.owner === owner ? context.draft : null;
|
|
123
|
-
const { field, stage, many, query, choices, selected, invalidNumber, remote, unavailable, label } = editorView(context, draft);
|
|
130
|
+
const { field, stage, many, query, choices, selected, invalidNumber, remote, unavailable, label, described } = editorView(context, draft);
|
|
124
131
|
const canCommitMany = selected.length > 0 && !unavailable && !context.disabled && !context.readOnly;
|
|
125
132
|
return /* @__PURE__ */ jsxs(Combobox, {
|
|
126
133
|
items: choices,
|
|
@@ -158,6 +165,7 @@ function FilterBarEditor({ owner, mode, anchorRef, children }) {
|
|
|
158
165
|
ref: searchRef,
|
|
159
166
|
size: "sm",
|
|
160
167
|
"aria-label": label,
|
|
168
|
+
"aria-describedby": described ? statusId(owner) : void 0,
|
|
161
169
|
placeholder: label,
|
|
162
170
|
inputMode: field?.type === "number" && stage === "value" ? "decimal" : void 0,
|
|
163
171
|
onKeyDown: (event) => handleEditorKeyDown(context, draft, event)
|
|
@@ -165,14 +173,15 @@ function FilterBarEditor({ owner, mode, anchorRef, children }) {
|
|
|
165
173
|
}),
|
|
166
174
|
/* @__PURE__ */ jsx(EditorOptions, {
|
|
167
175
|
field: stage === "value" ? field : void 0,
|
|
168
|
-
invalidNumber
|
|
176
|
+
invalidNumber,
|
|
177
|
+
owner
|
|
169
178
|
}),
|
|
170
179
|
many && /* @__PURE__ */ jsx("div", {
|
|
171
180
|
className: "flex justify-end p-2",
|
|
172
181
|
children: /* @__PURE__ */ jsx(Button, {
|
|
173
182
|
disabled: !canCommitMany,
|
|
174
183
|
onClick: () => context.commit(draft?.values ?? []),
|
|
175
|
-
children: "
|
|
184
|
+
children: "Apply"
|
|
176
185
|
})
|
|
177
186
|
})
|
|
178
187
|
]
|
|
@@ -182,6 +191,7 @@ function FilterBarEditor({ owner, mode, anchorRef, children }) {
|
|
|
182
191
|
function FilterBarInlineInput({ owner, inputRef, className, ...props }) {
|
|
183
192
|
const context = useFilterBarContext();
|
|
184
193
|
const draft = context.draft?.owner === owner ? context.draft : null;
|
|
194
|
+
const { label, described } = editorView(context, draft);
|
|
185
195
|
function handleKeyDown(event) {
|
|
186
196
|
props.onKeyDown?.(event);
|
|
187
197
|
if (event.defaultPrevented || event.nativeEvent.isComposing) return;
|
|
@@ -197,6 +207,8 @@ function FilterBarInlineInput({ owner, inputRef, className, ...props }) {
|
|
|
197
207
|
size: "sm",
|
|
198
208
|
className,
|
|
199
209
|
...props,
|
|
210
|
+
"aria-label": draft ? label : props["aria-label"],
|
|
211
|
+
"aria-describedby": described ? statusId(owner) : props["aria-describedby"],
|
|
200
212
|
onKeyDown: handleKeyDown
|
|
201
213
|
});
|
|
202
214
|
}
|
|
@@ -5,5 +5,6 @@ export declare function fieldOptions(field: FilterBarField): readonly FilterBarO
|
|
|
5
5
|
export declare function parseCustomValue(field: FilterBarField, query: string): FilterBarScalar | undefined;
|
|
6
6
|
export declare function allowsCustomValue(field: FilterBarField): boolean;
|
|
7
7
|
export declare function formatFilterValue(item: FilterBarItem, field: FilterBarField | undefined): string;
|
|
8
|
+
export declare function describeFilter(item: FilterBarItem, fields: readonly FilterBarField[], operators: readonly FilterBarOperator[]): string;
|
|
8
9
|
export declare function filterNavigationKeys(items: readonly FilterBarItem[]): string[];
|
|
9
10
|
//#endregion
|
package/dist/filter-bar/model.js
CHANGED
|
@@ -36,6 +36,15 @@ function formatFilterValue(item, field) {
|
|
|
36
36
|
function fieldOptionsForLabel(field, value) {
|
|
37
37
|
return (field ? fieldOptions(field).find((option) => option.value === value)?.label : void 0) ?? String(value);
|
|
38
38
|
}
|
|
39
|
+
function describeFilter(item, fields, operators) {
|
|
40
|
+
const field = fields.find((candidate) => candidate.id === item.fieldId);
|
|
41
|
+
const operator = operators.find((candidate) => candidate.id === item.operatorId);
|
|
42
|
+
return [
|
|
43
|
+
field?.label ?? item.fieldId,
|
|
44
|
+
operator?.ariaLabel ?? operator?.label,
|
|
45
|
+
formatFilterValue(item, field)
|
|
46
|
+
].filter(Boolean).join(" ");
|
|
47
|
+
}
|
|
39
48
|
function filterNavigationKeys(items) {
|
|
40
49
|
return items.flatMap((item) => [
|
|
41
50
|
"field",
|
|
@@ -45,4 +54,4 @@ function filterNavigationKeys(items) {
|
|
|
45
54
|
].map((segment) => `${item.id}:${segment}`));
|
|
46
55
|
}
|
|
47
56
|
//#endregion
|
|
48
|
-
export { allowsCustomValue, fieldOperators, fieldOptions, filterNavigationKeys, formatFilterValue, parseCustomValue };
|
|
57
|
+
export { allowsCustomValue, describeFilter, fieldOperators, fieldOptions, filterNavigationKeys, formatFilterValue, parseCustomValue };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { prefersReducedMotion, readDurationMs } from "../lib/motion.js";
|
|
2
3
|
import { useLayoutEffect, useRef, useState } from "react";
|
|
3
4
|
//#region src/filter-bar/motion.ts
|
|
4
5
|
function filterMotionEnabled(element) {
|
|
5
|
-
return !
|
|
6
|
+
return !prefersReducedMotion(element) && readDurationMs(element, "--duration-fast") > 0;
|
|
6
7
|
}
|
|
7
8
|
function animateMovement(element, before, after, duration, easing) {
|
|
8
9
|
const x = before.left - after.left;
|
|
@@ -56,9 +57,8 @@ function useFilterBarMotion() {
|
|
|
56
57
|
captured = null;
|
|
57
58
|
cancel();
|
|
58
59
|
const next = measure();
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
const easing = computed.getPropertyValue("--ease-standard").trim();
|
|
60
|
+
const duration = readDurationMs(root, "--duration-fast");
|
|
61
|
+
const easing = getComputedStyle(root).getPropertyValue("--ease-standard").trim();
|
|
62
62
|
animateLayout(elements, origin, next, animations, duration, easing);
|
|
63
63
|
}
|
|
64
64
|
};
|
package/dist/filter-bar/parts.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { Button } from "../ui/button.js";
|
|
4
4
|
import { useFilterBarContext } from "./context.js";
|
|
5
|
-
import { formatFilterValue } from "./model.js";
|
|
5
|
+
import { describeFilter, formatFilterValue } from "./model.js";
|
|
6
6
|
import { FilterBarEditor, FilterBarInlineInput } from "./editor.js";
|
|
7
7
|
import { useEffect, useId, useRef } from "react";
|
|
8
8
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -17,7 +17,8 @@ function chipSegments(item, field, operator, draft) {
|
|
|
17
17
|
}];
|
|
18
18
|
if (operator || draft) segments.push({
|
|
19
19
|
stage: "operator",
|
|
20
|
-
label: operator?.label ?? "Operator…"
|
|
20
|
+
label: operator?.label ?? "Operator…",
|
|
21
|
+
spokenLabel: operator?.ariaLabel
|
|
21
22
|
});
|
|
22
23
|
if (item.value !== null || draft && operator) segments.push({
|
|
23
24
|
stage: "value",
|
|
@@ -54,7 +55,7 @@ function FilterBarChipContent({ item, readOnly = false, className, ref, ...props
|
|
|
54
55
|
"data-control-ui": "filter-bar",
|
|
55
56
|
"data-control-family": "filter-bar",
|
|
56
57
|
"data-slot": "remove",
|
|
57
|
-
"aria-label": `Remove ${
|
|
58
|
+
"aria-label": `Remove filter: ${describeFilter(item, context.fields, context.operators)}`,
|
|
58
59
|
disabled: context.disabled,
|
|
59
60
|
onClick: () => context.remove(item),
|
|
60
61
|
onKeyDown: (event) => context.navigate(event, item.id, "remove"),
|
|
@@ -75,10 +76,19 @@ function FilterBarSegment({ item, segment, locked, draft }) {
|
|
|
75
76
|
className: "inline-flex min-w-0 items-center",
|
|
76
77
|
children: /* @__PURE__ */ jsxs("span", {
|
|
77
78
|
className: "flex min-w-0 items-center gap-1.5",
|
|
78
|
-
children: [
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
children: [
|
|
80
|
+
segment.icon,
|
|
81
|
+
/* @__PURE__ */ jsx("span", {
|
|
82
|
+
className: "truncate",
|
|
83
|
+
title: segment.label,
|
|
84
|
+
"aria-hidden": segment.spokenLabel ? true : void 0,
|
|
85
|
+
children: segment.label
|
|
86
|
+
}),
|
|
87
|
+
segment.spokenLabel ? /* @__PURE__ */ jsx("span", {
|
|
88
|
+
className: "sr-only",
|
|
89
|
+
children: segment.spokenLabel
|
|
90
|
+
}) : null
|
|
91
|
+
]
|
|
82
92
|
})
|
|
83
93
|
});
|
|
84
94
|
return /* @__PURE__ */ jsxs(Button, {
|
|
@@ -89,7 +99,7 @@ function FilterBarSegment({ item, segment, locked, draft }) {
|
|
|
89
99
|
"data-control-family": "filter-bar",
|
|
90
100
|
"data-slot": "segment",
|
|
91
101
|
className: "shrink",
|
|
92
|
-
"aria-label": `Edit ${segment.stage}: ${segment.label}`,
|
|
102
|
+
"aria-label": `Edit ${segment.stage}: ${segment.spokenLabel ?? segment.label}`,
|
|
93
103
|
"aria-haspopup": "listbox",
|
|
94
104
|
"aria-expanded": context.draft?.owner === item.id && context.draft.stage === segment.stage,
|
|
95
105
|
disabled: context.disabled || draft,
|
|
@@ -97,6 +107,7 @@ function FilterBarSegment({ item, segment, locked, draft }) {
|
|
|
97
107
|
onKeyDown: (event) => context.navigate(event, item.id, segment.stage),
|
|
98
108
|
children: [segment.icon, /* @__PURE__ */ jsx("span", {
|
|
99
109
|
className: "truncate",
|
|
110
|
+
title: segment.label,
|
|
100
111
|
children: segment.label
|
|
101
112
|
})]
|
|
102
113
|
});
|
package/dist/filter-bar.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "./lib/cn.js";
|
|
3
|
+
import { LiveStatus } from "./ui/live-status.js";
|
|
3
4
|
import { FilterBarContext } from "./filter-bar/context.js";
|
|
4
|
-
import { fieldOperators, filterNavigationKeys } from "./filter-bar/model.js";
|
|
5
|
+
import { describeFilter, fieldOperators, filterNavigationKeys } from "./filter-bar/model.js";
|
|
5
6
|
import { filterMotionEnabled, useFilterBarMotion } from "./filter-bar/motion.js";
|
|
6
7
|
import { FilterBarAddButton, FilterBarChip, FilterBarChips, FilterBarClear, FilterBarInput } from "./filter-bar/parts.js";
|
|
7
8
|
import { useState } from "react";
|
|
8
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
10
|
import { useRender } from "@base-ui/react/use-render";
|
|
10
11
|
//#region src/filter-bar.tsx
|
|
12
|
+
function valueStatusMessage(field) {
|
|
13
|
+
if (field?.error) return typeof field.error === "string" ? field.error : "Couldn’t load values.";
|
|
14
|
+
if (field?.loading) return "Loading values…";
|
|
15
|
+
}
|
|
11
16
|
function FilterBarRoot({ fields, operators, value, onValueChange, createItemId = () => crypto.randomUUID(), disabled = false, readOnly = false, className, children, ref, "aria-label": ariaLabel = "Filters", ...props }) {
|
|
12
17
|
const [draft, setDraft] = useState(null);
|
|
13
18
|
const [exits, setExits] = useState([]);
|
|
@@ -92,7 +97,7 @@ function FilterBarRoot({ fields, operators, value, onValueChange, createItemId =
|
|
|
92
97
|
};
|
|
93
98
|
motion.capture();
|
|
94
99
|
onValueChange(next.editing ? value.map((current) => current.id === item.id ? item : current) : [...value, item]);
|
|
95
|
-
setAnnouncement(next.editing ? "
|
|
100
|
+
setAnnouncement(`${next.editing ? "Updated" : "Added"} filter: ${describeFilter(item, fields, operators)}`);
|
|
96
101
|
if (next.anchor.isConnected) next.anchor.focus();
|
|
97
102
|
setDraft(null);
|
|
98
103
|
}
|
|
@@ -160,7 +165,7 @@ function FilterBarRoot({ fields, operators, value, onValueChange, createItemId =
|
|
|
160
165
|
else focusEntry();
|
|
161
166
|
if (draft?.id === item.id) setDraft(null);
|
|
162
167
|
onValueChange(value.filter((candidate) => candidate.id !== item.id));
|
|
163
|
-
setAnnouncement(
|
|
168
|
+
setAnnouncement(`Removed filter: ${describeFilter(item, fields, operators)}`);
|
|
164
169
|
}
|
|
165
170
|
function clear() {
|
|
166
171
|
if (disabled || readOnly) return;
|
|
@@ -169,7 +174,7 @@ function FilterBarRoot({ fields, operators, value, onValueChange, createItemId =
|
|
|
169
174
|
setDraft(null);
|
|
170
175
|
onValueChange([]);
|
|
171
176
|
focusEntry();
|
|
172
|
-
setAnnouncement("
|
|
177
|
+
setAnnouncement("Cleared all filters");
|
|
173
178
|
}
|
|
174
179
|
function deleteFocusedFilter(event, itemId) {
|
|
175
180
|
const item = value.find((candidate) => candidate.id === itemId);
|
|
@@ -220,6 +225,7 @@ function FilterBarRoot({ fields, operators, value, onValueChange, createItemId =
|
|
|
220
225
|
releaseExit: (id) => setExits((current) => current.filter((exit) => exit.item.id !== id)),
|
|
221
226
|
navigate
|
|
222
227
|
};
|
|
228
|
+
const liveMessage = valueStatusMessage(draft?.stage === "value" ? fields.find((field) => field.id === draft.fieldId) : void 0) ?? announcement;
|
|
223
229
|
const rootElement = useRender({
|
|
224
230
|
ref: [rootRef, ref ?? null],
|
|
225
231
|
render: /* @__PURE__ */ jsxs("fieldset", {
|
|
@@ -231,11 +237,7 @@ function FilterBarRoot({ fields, operators, value, onValueChange, createItemId =
|
|
|
231
237
|
"data-readonly": readOnly || void 0,
|
|
232
238
|
className: cn("relative flex min-w-0 flex-wrap items-center", className),
|
|
233
239
|
...props,
|
|
234
|
-
children: [children, /* @__PURE__ */ jsx(
|
|
235
|
-
className: "sr-only",
|
|
236
|
-
"aria-live": "polite",
|
|
237
|
-
children: announcement
|
|
238
|
-
})]
|
|
240
|
+
children: [children, /* @__PURE__ */ jsx(LiveStatus, { message: liveMessage })]
|
|
239
241
|
})
|
|
240
242
|
});
|
|
241
243
|
return /* @__PURE__ */ jsx(FilterBarContext.Provider, {
|
|
@@ -270,19 +272,23 @@ const DEFAULT_FILTER_OPERATORS = [
|
|
|
270
272
|
},
|
|
271
273
|
{
|
|
272
274
|
id: "gt",
|
|
273
|
-
label: ">"
|
|
275
|
+
label: ">",
|
|
276
|
+
ariaLabel: "greater than"
|
|
274
277
|
},
|
|
275
278
|
{
|
|
276
279
|
id: "gte",
|
|
277
|
-
label: "≥"
|
|
280
|
+
label: "≥",
|
|
281
|
+
ariaLabel: "at least"
|
|
278
282
|
},
|
|
279
283
|
{
|
|
280
284
|
id: "lt",
|
|
281
|
-
label: "<"
|
|
285
|
+
label: "<",
|
|
286
|
+
ariaLabel: "less than"
|
|
282
287
|
},
|
|
283
288
|
{
|
|
284
289
|
id: "lte",
|
|
285
|
-
label: "≤"
|
|
290
|
+
label: "≤",
|
|
291
|
+
ariaLabel: "at most"
|
|
286
292
|
},
|
|
287
293
|
{
|
|
288
294
|
id: "in",
|
|
@@ -20,7 +20,7 @@ function formatAudioInputDeviceLabel(device, fallback = "Microphone") {
|
|
|
20
20
|
return `${label.replace(deviceIdRegex, "").trim() || fallback} (${id})`;
|
|
21
21
|
}
|
|
22
22
|
async function enumerateAudioInputDevices() {
|
|
23
|
-
if (typeof navigator === "undefined" || !navigator.mediaDevices?.enumerateDevices) throw
|
|
23
|
+
if (typeof navigator === "undefined" || !navigator.mediaDevices?.enumerateDevices) throw unsupportedRecordingError();
|
|
24
24
|
return (await navigator.mediaDevices.enumerateDevices()).filter((device) => device.kind === "audioinput");
|
|
25
25
|
}
|
|
26
26
|
function useAudioInputDevices() {
|
|
@@ -43,7 +43,7 @@ function useAudioInputDevices() {
|
|
|
43
43
|
}
|
|
44
44
|
async function requestPermission() {
|
|
45
45
|
if (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia) {
|
|
46
|
-
setError(
|
|
46
|
+
setError(unsupportedRecordingError());
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
49
|
setLoading(true);
|
|
@@ -96,10 +96,16 @@ function browserEnvironment() {
|
|
|
96
96
|
function asError(error) {
|
|
97
97
|
return error instanceof Error ? error : new Error(String(error));
|
|
98
98
|
}
|
|
99
|
+
function namedError(name, message) {
|
|
100
|
+
return Object.assign(new Error(message), { name });
|
|
101
|
+
}
|
|
102
|
+
function unsupportedRecordingError() {
|
|
103
|
+
return namedError("NotSupportedError", "This browser can't record audio. Try a current version of Chrome, Safari or Firefox.");
|
|
104
|
+
}
|
|
99
105
|
function microphoneError(error) {
|
|
100
106
|
const nextError = asError(error);
|
|
101
|
-
if (nextError.name === "NotAllowedError" || nextError.name === "SecurityError") return
|
|
102
|
-
if (nextError.name === "NotFoundError" || nextError.name === "OverconstrainedError") return
|
|
107
|
+
if (nextError.name === "NotAllowedError" || nextError.name === "SecurityError") return namedError("NotAllowedError", "Microphone access is blocked. Allow it in your browser's site settings, then try again.");
|
|
108
|
+
if (nextError.name === "NotFoundError" || nextError.name === "OverconstrainedError") return namedError("NotFoundError", "Selected microphone isn't connected. Plug it in or choose another microphone.");
|
|
103
109
|
return nextError;
|
|
104
110
|
}
|
|
105
111
|
function clampLevel(value) {
|
|
@@ -351,7 +357,7 @@ function createControllerForEnvironment(initialOptions, environment) {
|
|
|
351
357
|
}
|
|
352
358
|
function beginRecording(nextStream) {
|
|
353
359
|
const MediaRecorderImpl = environment.MediaRecorder;
|
|
354
|
-
if (!MediaRecorderImpl) throw
|
|
360
|
+
if (!MediaRecorderImpl) throw unsupportedRecordingError();
|
|
355
361
|
selectedMimeType = preferredAudioMimeType(MediaRecorderImpl) ?? "";
|
|
356
362
|
const nextRecorder = new MediaRecorderImpl(nextStream, selectedMimeType ? { mimeType: selectedMimeType } : void 0);
|
|
357
363
|
chunks = [];
|
|
@@ -388,7 +394,7 @@ function createControllerForEnvironment(initialOptions, environment) {
|
|
|
388
394
|
if (!environment.mediaDevices?.getUserMedia || !environment.MediaRecorder) {
|
|
389
395
|
setSnapshot({
|
|
390
396
|
state: "error",
|
|
391
|
-
error:
|
|
397
|
+
error: unsupportedRecordingError()
|
|
392
398
|
});
|
|
393
399
|
return false;
|
|
394
400
|
}
|