@parallelworks/ui 0.50.0 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +204 -18
- package/dist/ai/agent/toolStrings.d.ts +2 -0
- package/dist/ai/agent/toolStrings.js +20 -18
- package/dist/ai/ai.css +12 -0
- package/dist/ai/components/AllocationSelector.js +5 -1
- package/dist/ai/components/AttachmentManager.js +51 -42
- package/dist/ai/components/AttachmentUpload.d.ts +4 -2
- package/dist/ai/components/AttachmentUpload.js +9 -9
- package/dist/ai/components/BlockedGroupBanner.js +8 -14
- package/dist/ai/components/BranchNavigator.js +11 -12
- package/dist/ai/components/ChatEmptyState.js +12 -19
- package/dist/ai/components/ChatInput.d.ts +4 -3
- package/dist/ai/components/ChatInput.js +27 -13
- package/dist/ai/components/ChatMessage.js +45 -121
- package/dist/ai/components/ChatMessageList.d.ts +5 -2
- package/dist/ai/components/ChatMessageList.js +89 -51
- package/dist/ai/components/ChatSidebar.js +11 -7
- package/dist/ai/components/ChatThread.js +1 -13
- package/dist/ai/components/DragOverlay.js +3 -3
- package/dist/ai/components/MessageAttachments.d.ts +10 -0
- package/dist/ai/components/MessageAttachments.js +78 -0
- package/dist/ai/components/ModelSelector.js +4 -4
- package/dist/ai/components/PastedText.js +2 -2
- package/dist/ai/components/ProviderIssueBanner.js +19 -23
- package/dist/ai/components/ShareDialog.js +2 -2
- package/dist/ai/components/agent/ActivityLine.d.ts +0 -2
- package/dist/ai/components/agent/ActivityLine.js +8 -51
- package/dist/ai/components/agent/AgentMessageParts.js +117 -56
- package/dist/ai/core/ChatProvider.d.ts +16 -5
- package/dist/ai/core/ChatProvider.js +41 -8
- package/dist/ai/core/config.js +4 -3
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/index.js +3 -3
- package/dist/ai/strings.d.ts +29 -3
- package/dist/ai/strings.js +81 -26
- package/dist/ai/types.d.ts +4 -0
- package/dist/ai/ui/Notice.d.ts +2 -2
- package/dist/ai/ui/Notice.js +29 -62
- package/dist/ai/ui/sidebar.d.ts +5 -3
- package/dist/ai/ui/sidebar.js +21 -9
- package/dist/components/ActionMenu.d.ts +1 -0
- package/dist/components/ActionMenu.js +6 -1
- package/dist/components/AppLink.d.ts +26 -0
- package/dist/components/AppLink.js +51 -0
- package/dist/components/Avatar.d.ts +2 -1
- package/dist/components/Avatar.js +4 -2
- package/dist/components/Button.d.ts +15 -5
- package/dist/components/Button.js +47 -40
- package/dist/components/Callout.d.ts +31 -7
- package/dist/components/Callout.js +51 -24
- package/dist/components/Card.d.ts +11 -6
- package/dist/components/Card.js +14 -8
- package/dist/components/Checkbox.js +2 -1
- package/dist/components/ColorInput.js +5 -3
- package/dist/components/Combobox.js +49 -19
- package/dist/components/ConfirmModal.d.ts +19 -8
- package/dist/components/ConfirmModal.js +31 -53
- package/dist/components/CopyButton.d.ts +38 -0
- package/dist/components/CopyButton.js +68 -0
- package/dist/components/CreateModal.js +118 -111
- package/dist/components/DatePicker.js +12 -8
- package/dist/components/DescriptionList.d.ts +51 -17
- package/dist/components/DescriptionList.js +117 -31
- package/dist/components/Disclosure.js +1 -1
- package/dist/components/Drawer.js +10 -16
- package/dist/components/Field.d.ts +15 -5
- package/dist/components/Field.js +41 -9
- package/dist/components/IconButton.d.ts +14 -5
- package/dist/components/IconButton.js +25 -14
- package/dist/components/InfoTip.d.ts +4 -1
- package/dist/components/InfoTip.js +2 -1
- package/dist/components/Input.d.ts +14 -8
- package/dist/components/Input.js +46 -31
- package/dist/components/Loader.js +1 -1
- package/dist/components/MenuEntries.d.ts +43 -12
- package/dist/components/MenuEntries.js +34 -19
- package/dist/components/Meter.js +5 -4
- package/dist/components/Modal.d.ts +28 -4
- package/dist/components/Modal.js +37 -14
- package/dist/components/Overview.d.ts +20 -12
- package/dist/components/Overview.js +14 -9
- package/dist/components/PageHeader.d.ts +47 -65
- package/dist/components/PageHeader.js +275 -153
- package/dist/components/PageHeaderAction.d.ts +67 -0
- package/dist/components/PageHeaderAction.js +107 -0
- package/dist/components/Pagination.js +6 -16
- package/dist/components/Popover.js +3 -2
- package/dist/components/Provider.d.ts +79 -19
- package/dist/components/Provider.js +140 -130
- package/dist/components/Radio.js +2 -1
- package/dist/components/RefreshButton.js +7 -8
- package/dist/components/RequiredMark.js +1 -1
- package/dist/components/RunStatusIcon.js +2 -2
- package/dist/components/Section.js +1 -1
- package/dist/components/Select.d.ts +4 -9
- package/dist/components/Select.js +24 -11
- package/dist/components/SettingModeOption.d.ts +1 -1
- package/dist/components/SettingModeOption.js +7 -1
- package/dist/components/SettingsCard.d.ts +5 -3
- package/dist/components/SettingsCard.js +27 -20
- package/dist/components/Slider.js +1 -1
- package/dist/components/StatusBadge.js +2 -2
- package/dist/components/StatusIcons.js +2 -2
- package/dist/components/Switch.js +5 -4
- package/dist/components/Table.d.ts +30 -20
- package/dist/components/Table.js +50 -73
- package/dist/components/Tabs.d.ts +3 -7
- package/dist/components/Tabs.js +6 -10
- package/dist/components/ThemeEditor.js +24 -14
- package/dist/components/Toast.d.ts +11 -3
- package/dist/components/Toast.js +47 -22
- package/dist/components/buttonClasses.js +2 -2
- package/dist/components/comboboxItems.d.ts +4 -0
- package/dist/components/comboboxItems.js +6 -1
- package/dist/components/dismiss.d.ts +12 -0
- package/dist/components/dismiss.js +19 -0
- package/dist/components/fieldBox.d.ts +4 -0
- package/dist/components/fieldBox.js +7 -3
- package/dist/components/floating.d.ts +3 -3
- package/dist/components/floating.js +1 -1
- package/dist/components/markdownCodeBreaks.js +3 -4
- package/dist/components/pageHeaderStore.d.ts +41 -0
- package/dist/components/pageHeaderStore.js +67 -0
- package/dist/components/pathBreaks.d.ts +8 -0
- package/dist/components/pathBreaks.js +28 -0
- package/dist/components/segmented.js +1 -1
- package/dist/components/shortcutKey.d.ts +2 -1
- package/dist/components/shortcutKey.js +19 -2
- package/dist/components/splitter.js +1 -1
- package/dist/components/tone.d.ts +5 -2
- package/dist/components/tone.js +15 -6
- package/dist/components/useCssIsDark.js +40 -18
- package/dist/components/useTheme.js +9 -10
- package/dist/dev.d.ts +9 -0
- package/dist/dev.js +14 -0
- package/dist/editor/Editor.d.ts +3 -2
- package/dist/editor/Editor.js +11 -2
- package/dist/editor/EditorField.d.ts +2 -1
- package/dist/editor/EditorField.js +11 -29
- package/dist/editor/FormCodePanel.d.ts +7 -3
- package/dist/editor/FormCodePanel.js +14 -7
- package/dist/editor/Monaco.d.ts +14 -5
- package/dist/editor/Monaco.js +163 -112
- package/dist/editor/index.d.ts +5 -5
- package/dist/editor/index.js +3 -3
- package/dist/editor/themes.d.ts +8 -5
- package/dist/editor/themes.js +58 -30
- package/dist/editor/yaml.d.ts +6 -2
- package/dist/editor/yaml.js +11 -2
- package/dist/file-explorer/FileExplorer.js +143 -124
- package/dist/file-explorer/FileExplorerContext.js +1 -1
- package/dist/file-explorer/FilePreview/StreamedBadge.js +3 -1
- package/dist/file-explorer/FilePreview/Viewer.js +2 -2
- package/dist/file-explorer/FilePreview/index.js +4 -4
- package/dist/file-explorer/FileRow.js +1 -1
- package/dist/file-explorer/FileRowsWindow.js +34 -3
- package/dist/file-explorer/FileTable.js +3 -1
- package/dist/file-explorer/TreeView.js +37 -14
- package/dist/file-explorer/helpers.d.ts +2 -1
- package/dist/file-explorer/helpers.js +21 -20
- package/dist/file-explorer/toasts.js +27 -19
- package/dist/file-explorer/treeRows.d.ts +6 -0
- package/dist/file-explorer/treeRows.js +19 -1
- package/dist/file-explorer/useNearEnd.js +3 -6
- package/dist/form/Fields/CheckboxGroupField.js +12 -5
- package/dist/form/Fields/DurationField.js +1 -1
- package/dist/form/Fields/LabelField.js +1 -1
- package/dist/form/Fields/ObjectField.d.ts +1 -1
- package/dist/form/Fields/ObjectField.js +3 -2
- package/dist/form/Fields/StringField.js +16 -29
- package/dist/form/Fields/TextAreaField.js +3 -4
- package/dist/form/Form.js +3 -8
- package/dist/form/Wizard/WizardNavigation.js +1 -4
- package/dist/form/Wizard/WizardStepIndicator.js +1 -1
- package/dist/form/Wizard/utils.js +1 -1
- package/dist/form/fieldFrame.d.ts +1 -1
- package/dist/form/fieldFrame.js +2 -2
- package/dist/form/types/fieldComponentTypes.d.ts +2 -1
- package/dist/graph/AnnotationBanner.d.ts +1 -2
- package/dist/graph/AnnotationBanner.js +30 -32
- package/dist/graph/Connectors.js +4 -1
- package/dist/graph/DependencyGraph.js +2 -2
- package/dist/graph/GraphEditor.js +12 -10
- package/dist/graph/GraphEditorDialogs.js +2 -2
- package/dist/graph/GraphNode.js +7 -4
- package/dist/graph/InputDialog.js +1 -1
- package/dist/graph/InputsEditor.js +14 -11
- package/dist/graph/MatrixGroup.js +9 -3
- package/dist/graph/ProblemsButton.js +2 -2
- package/dist/graph/Reveal.js +18 -10
- package/dist/graph/ShortcutsButton.d.ts +11 -2
- package/dist/graph/ShortcutsButton.js +15 -4
- package/dist/graph/TreeView.js +35 -18
- package/dist/graph/WorkflowSettingsDialog.js +1 -1
- package/dist/graph/editorChrome.js +1 -1
- package/dist/graph/editorFields.js +1 -1
- package/dist/graph/editorRows.js +16 -10
- package/dist/graph/editorStrings.d.ts +7 -0
- package/dist/graph/editorStrings.js +8 -1
- package/dist/graph/fieldProblems.js +1 -1
- package/dist/graph/inputRefs.js +1 -1
- package/dist/graph/settingsViews.js +11 -18
- package/dist/graph/stepWith.js +2 -2
- package/dist/icons.d.ts +2 -0
- package/dist/icons.js +4 -2
- package/dist/index.d.ts +12 -12
- package/dist/index.js +16 -16
- package/dist/list/ActionToolbar.d.ts +44 -8
- package/dist/list/ActionToolbar.js +301 -102
- package/dist/list/ListPage.d.ts +15 -23
- package/dist/list/ListPage.js +46 -45
- package/dist/list/ListTable.d.ts +32 -7
- package/dist/list/ListTable.js +48 -12
- package/dist/list/ListViewControls.js +2 -2
- package/dist/list/UserHoverCard.js +7 -15
- package/dist/list/index.d.ts +2 -2
- package/dist/list/index.js +2 -2
- package/dist/list/listSearch.js +12 -10
- package/dist/list/listView.d.ts +10 -1
- package/dist/list/listView.js +15 -1
- package/dist/list/rowMenu.d.ts +2 -2
- package/dist/logviewer/LogViewer.d.ts +4 -2
- package/dist/logviewer/LogViewer.js +88 -70
- package/dist/logviewer/logviewer.css +3 -2
- package/dist/reducedMotion.d.ts +5 -0
- package/dist/reducedMotion.js +20 -0
- package/dist/safeUrl.d.ts +1 -1
- package/dist/safeUrl.js +4 -2
- package/dist/styles/theme.css +24 -10
- package/dist/styles.css +1 -1
- package/dist/theme/derive.d.ts +1 -1
- package/dist/theme/derive.js +46 -21
- package/dist/theme/fit.js +10 -4
- package/package.json +1 -1
- package/dist/components/Breadcrumbs.d.ts +0 -37
- package/dist/components/Breadcrumbs.js +0 -90
- package/dist/components/CopyToClipboard.d.ts +0 -17
- package/dist/components/CopyToClipboard.js +0 -66
- package/dist/components/Sort.d.ts +0 -9
- package/dist/components/Sort.js +0 -42
package/dist/index.js
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { cx } from "./cx.js";
|
|
2
|
-
import { MenuCheckboxItem, MenuGroupLabel, MenuItem, MenuLinkItem, MenuPopup, MenuRadioItem, MenuSeparator, MenuSubmenuTrigger } from "./components/Menu.js";
|
|
3
|
-
import { EmptyState } from "./components/EmptyState.js";
|
|
4
2
|
import { buttonClasses } from "./components/buttonClasses.js";
|
|
3
|
+
import { MenuCheckboxItem, MenuGroupLabel, MenuItem, MenuLinkItem, MenuPopup, MenuRadioItem, MenuSeparator, MenuSubmenuTrigger } from "./components/Menu.js";
|
|
5
4
|
import { Loader } from "./components/Loader.js";
|
|
6
5
|
import { toast } from "./components/Toast.js";
|
|
7
6
|
import { Tooltip, TooltipProvider } from "./components/Tooltip.js";
|
|
8
7
|
import { UIProvider, useNavigation, useOptionalWorkflowEngine, usePersistedState, useProvisionCorsRules, useRunFile, useSlots, useStrings, useWorkflowActions, useWorkflowEditing, useWorkflowEngine, useWorkflowEngineLoader } from "./components/Provider.js";
|
|
8
|
+
import { AppLink, isPlainClick } from "./components/AppLink.js";
|
|
9
|
+
import { EmptyState } from "./components/EmptyState.js";
|
|
9
10
|
import { useCopy } from "./components/useCopy.js";
|
|
10
11
|
import { MenuEntries, MenuFilter, menuDivider } from "./components/MenuEntries.js";
|
|
11
12
|
import { ActionMenu } from "./components/ActionMenu.js";
|
|
12
13
|
import { Avatar, avatarInitials } from "./components/Avatar.js";
|
|
13
|
-
import { BreadcrumbProvider, useBreadcrumb, useClearBreadcrumbs, useHideBreadcrumbs, useSetBreadcrumbs, useSetDefaultBreadcrumbs } from "./components/Breadcrumbs.js";
|
|
14
14
|
import { Button, MethodButton } from "./components/Button.js";
|
|
15
|
-
import
|
|
15
|
+
import { isTextEntryContext, useShortcutKey } from "./components/shortcutKey.js";
|
|
16
|
+
import { IconButton } from "./components/IconButton.js";
|
|
17
|
+
import { Callout } from "./components/Callout.js";
|
|
16
18
|
import { Card, CardHeader } from "./components/Card.js";
|
|
19
|
+
import { fieldBox } from "./components/fieldBox.js";
|
|
17
20
|
import { Checkbox, CheckboxGroup } from "./components/Checkbox.js";
|
|
18
21
|
import { JND, composite, deltaE, parseColor, toHex, toP3 } from "./theme/color.js";
|
|
19
22
|
import { apcaContrast, contrastRatio, isDarkColor } from "./theme/contrast.js";
|
|
@@ -21,28 +24,25 @@ import { CODE_STYLES, DEFAULT_CONTRAST, DEFAULT_PRESET, DEFAULT_THEME, THEME_PRE
|
|
|
21
24
|
import { SIDEBAR_TOKENS, THEME_TOKENS, deriveTheme, generateTheme, themeStylesheet } from "./theme/derive.js";
|
|
22
25
|
import { fitSeed } from "./theme/fit.js";
|
|
23
26
|
import { applyTheme, prefersWideGamut } from "./theme/index.js";
|
|
27
|
+
import { ExternalLink } from "./components/ExternalLink.js";
|
|
28
|
+
import { Popover, PopoverClose, useClosePopover } from "./components/Popover.js";
|
|
29
|
+
import { InfoTip, formatInfoTipContent } from "./components/InfoTip.js";
|
|
24
30
|
import { RequiredMark } from "./components/RequiredMark.js";
|
|
25
31
|
import { ErrorText, Field, FieldOption, FieldValue, RevealToggle, useFieldControl } from "./components/Field.js";
|
|
26
|
-
import { fieldBox } from "./components/fieldBox.js";
|
|
27
32
|
import { Input, Textarea } from "./components/Input.js";
|
|
28
33
|
import { ColorInput } from "./components/ColorInput.js";
|
|
29
34
|
import { Autocomplete, Combobox } from "./components/Combobox.js";
|
|
30
35
|
import { Modal, ModalDescription, ModalTitle, modalPanelClassName } from "./components/Modal.js";
|
|
31
36
|
import { ConfirmModal } from "./components/ConfirmModal.js";
|
|
32
37
|
import { useContextMenu } from "./components/ContextMenu.js";
|
|
33
|
-
import
|
|
38
|
+
import { CopyButton, CopyCodeBlock } from "./components/CopyButton.js";
|
|
34
39
|
import { Disclosure, DisclosurePanel } from "./components/Disclosure.js";
|
|
35
40
|
import { Switch } from "./components/Switch.js";
|
|
36
41
|
import { CreateModal, CreateModalSelect, CreateModalSwitch } from "./components/CreateModal.js";
|
|
37
42
|
import { DatePicker } from "./components/DatePicker.js";
|
|
38
|
-
import {
|
|
39
|
-
import { Popover, PopoverClose, useClosePopover } from "./components/Popover.js";
|
|
40
|
-
import { InfoTip, formatInfoTipContent } from "./components/InfoTip.js";
|
|
41
|
-
import DescriptionList, { DescriptionListItem } from "./components/DescriptionList.js";
|
|
43
|
+
import DescriptionList, { DescriptionListGroup, DescriptionListItem } from "./components/DescriptionList.js";
|
|
42
44
|
import { Drawer } from "./components/Drawer.js";
|
|
43
45
|
import { HoverCard } from "./components/HoverCard.js";
|
|
44
|
-
import { isTextEntryContext, useShortcutKey } from "./components/shortcutKey.js";
|
|
45
|
-
import { IconButton } from "./components/IconButton.js";
|
|
46
46
|
import { LetterBadge } from "./components/LetterBadge.js";
|
|
47
47
|
import Markdown from "./components/Markdown.js";
|
|
48
48
|
import { Meter, ProgressBar, levelTone } from "./components/Meter.js";
|
|
@@ -50,7 +50,8 @@ import { NumberInput } from "./components/NumberInput.js";
|
|
|
50
50
|
import { OneTimeCodeInput } from "./components/OneTimeCodeInput.js";
|
|
51
51
|
import { LoadingRegion, Skeleton } from "./components/Skeleton.js";
|
|
52
52
|
import { Overview } from "./components/Overview.js";
|
|
53
|
-
import {
|
|
53
|
+
import { PageHeader, PageHeaderBar, PageHeaderProvider } from "./components/PageHeader.js";
|
|
54
|
+
import { PageHeaderAction, PageHeaderAddButton, PageHeaderMenu } from "./components/PageHeaderAction.js";
|
|
54
55
|
import { Select } from "./components/Select.js";
|
|
55
56
|
import { PAGE_SIZES, Pagination } from "./components/Pagination.js";
|
|
56
57
|
import { Radio, RadioGroup } from "./components/Radio.js";
|
|
@@ -64,12 +65,11 @@ import { SegmentedControl } from "./components/SegmentedControl.js";
|
|
|
64
65
|
import { SettingModeOption } from "./components/SettingModeOption.js";
|
|
65
66
|
import { SettingRow, SettingsCard, SettingsHeading } from "./components/SettingsCard.js";
|
|
66
67
|
import { Slider } from "./components/Slider.js";
|
|
67
|
-
import Sort from "./components/Sort.js";
|
|
68
68
|
import { StatusBadge, StatusDot, statusTextClass } from "./components/StatusBadge.js";
|
|
69
|
-
import {
|
|
69
|
+
import { Table } from "./components/Table.js";
|
|
70
70
|
import { TabPanel, Tabs } from "./components/Tabs.js";
|
|
71
71
|
import { ThemeEditor, ThemePreview } from "./components/ThemeEditor.js";
|
|
72
72
|
import { ToggleGroup } from "./components/ToggleGroup.js";
|
|
73
73
|
import { useCssIsDark } from "./components/useCssIsDark.js";
|
|
74
74
|
import { useApplyTheme, useColorMode } from "./components/useTheme.js";
|
|
75
|
-
export { ActionMenu, Autocomplete, Avatar,
|
|
75
|
+
export { ActionMenu, AppLink, Autocomplete, Avatar, Button, CODE_STYLES, Callout, Card, CardHeader, Checkbox, CheckboxGroup, ColorInput, Combobox, ConfirmModal, CopyButton, CopyCodeBlock, CreateModal, CreateModalSelect, CreateModalSwitch, DEFAULT_CONTRAST, DEFAULT_PRESET, DEFAULT_THEME, DatePicker, DescriptionList, DescriptionListGroup, DescriptionListItem, Disclosure, DisclosurePanel, Drawer, Elapsed, EmptyState, ErrorText, ExternalLink, Field, FieldOption, FieldValue, HoverCard, IconButton, InfoTip, Input, JND, LetterBadge, Loader, LoadingRegion, Markdown, MenuCheckboxItem, MenuEntries, MenuFilter, MenuGroupLabel, MenuItem, MenuLinkItem, MenuPopup, MenuRadioItem, MenuSeparator, MenuSubmenuTrigger, Meter, MethodButton, Modal, ModalDescription, ModalTitle, NumberInput, OneTimeCodeInput, Overview, PAGE_SIZES, PageHeader, PageHeaderAction, PageHeaderAddButton, PageHeaderBar, PageHeaderMenu, PageHeaderProvider, Pagination, Popover, PopoverClose, ProgressBar, Radio, RadioGroup, RefreshButton, RelativeTime, RequiredMark, RevealToggle, RunStatusIcon, SIDEBAR_TOKENS, SearchInput, Section, SegmentedControl, Select, SettingModeOption, SettingRow, SettingsCard, SettingsHeading, Skeleton, Slider, StatusBadge, StatusDot, Switch, THEME_PRESETS, THEME_TOKENS, TabPanel, Table, Tabs, Textarea, ThemeEditor, ThemePreview, ToggleGroup, Tooltip, TooltipProvider, UIProvider, apcaContrast, applyTheme, avatarInitials, buttonClasses, composite, contrastRatio, cx, deltaE, deriveTheme, fieldBox, fitSeed, formatBytes, formatCount, formatDateTime, formatDuration, formatInfoTipContent, formatTimeLeft, generateTheme, isDarkColor, isPlainClick, isTextEntryContext, levelTone, menuDivider, modalPanelClassName, modeOf, parseColor, parseTheme, prefersWideGamut, resolveMode, runStatusTone, schemeFor, serializeTheme, statusTextClass, themeStylesheet, toHex, toP3, toast, useApplyTheme, useClock, useClosePopover, useColorMode, useContextMenu, useCopy, useCssIsDark, useElapsed, useFieldControl, useNavigation, useOptionalWorkflowEngine, usePersistedState, useProvisionCorsRules, useRelativeTime, useRunFile, useShortcutKey, useSlots, useStrings, useWorkflowActions, useWorkflowEditing, useWorkflowEngine, useWorkflowEngineLoader };
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { RowAction } from './rowMenu.js';
|
|
2
2
|
/** A toolbar button, which moves into the `…` menu as a row when the toolbar runs out of room. */
|
|
3
|
-
export
|
|
3
|
+
export type ToolbarAction = RowAction & {
|
|
4
4
|
/** A hint on the enabled button. */
|
|
5
5
|
tooltip?: string | undefined;
|
|
6
|
+
/** The one action the view leads to, filled in the accent. Give it to one
|
|
7
|
+
* action at most: the rest are outlined, so it stands out. */
|
|
8
|
+
primary?: boolean | undefined;
|
|
6
9
|
hidden?: boolean;
|
|
7
|
-
}
|
|
10
|
+
};
|
|
8
11
|
/** A pre-rendered element (e.g. a button component that owns its modal). */
|
|
9
12
|
interface ToolbarElement {
|
|
10
13
|
key: string;
|
|
@@ -12,17 +15,50 @@ interface ToolbarElement {
|
|
|
12
15
|
hidden?: boolean;
|
|
13
16
|
}
|
|
14
17
|
export type ToolbarItem = ToolbarAction | ToolbarElement;
|
|
15
|
-
/** A toolbar action as a button: a link with `href`, a toggle with `checked`.
|
|
18
|
+
/** A toolbar action as a button: a link with `href`, a toggle with `checked`.
|
|
19
|
+
* In an ActionToolbar it is one of its items, reached with the arrow keys. */
|
|
16
20
|
export declare function ToolbarButton({ action }: {
|
|
17
21
|
action: ToolbarAction;
|
|
18
22
|
}): import("react").JSX.Element;
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
/** How the toolbar fits its row: the items whose labels fold to their icons,
|
|
24
|
+
* and those moved into the `…` menu, by key. */
|
|
25
|
+
export interface ToolbarFit {
|
|
26
|
+
folded: readonly string[];
|
|
27
|
+
overflow: readonly string[];
|
|
28
|
+
}
|
|
29
|
+
/** An item's widths with its label and folded to its icon. */
|
|
30
|
+
export interface ItemWidth {
|
|
31
|
+
key: string;
|
|
32
|
+
full: number;
|
|
33
|
+
folded: number;
|
|
34
|
+
primary: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Fits a toolbar to `available` pixels one item at a time, stopping as soon as
|
|
38
|
+
* it fits, so it keeps every label the width allows. Items go lowest priority
|
|
39
|
+
* first: the right-hand ones, then the last group's, and so on, each from the
|
|
40
|
+
* end. First their labels fold to their icons, one by one; then, all folded,
|
|
41
|
+
* they move into the `…` menu in the same order; the primary action never
|
|
42
|
+
* moves, and its label folds only once nothing else is left to try.
|
|
43
|
+
*/
|
|
44
|
+
export declare function fitToolbar(groups: ItemWidth[][], right: ItemWidth[], available: number): ToolbarFit;
|
|
45
|
+
/** Pins a toolbar to the top of the page's scroll area while the content scrolls under it. */
|
|
46
|
+
export declare const stickyToolbarClasses = "sticky top-0 z-(--layer-sticky) bg-(--theme-app-bg)";
|
|
47
|
+
/** A view's controls in one WAI-ARIA toolbar: a single tab stop, with the arrow
|
|
48
|
+
* keys moving between its items, the `…` menu and disabled ones included, so
|
|
49
|
+
* their reasons stay reachable. Action groups sit on the left and `right` holds
|
|
50
|
+
* secondary actions at the end. It stays one row: when it runs out of room
|
|
51
|
+
* labels fold to their icons one at a time, then actions move into a `…` menu,
|
|
52
|
+
* the right-hand ones first and the primary action never. Give each action its
|
|
53
|
+
* own icon, since a folded one is known by it, and mark the one action the
|
|
54
|
+
* view leads to `primary`. */
|
|
55
|
+
export declare function ActionToolbar({ groups, right, sticky, label, }: {
|
|
23
56
|
groups: ToolbarItem[][];
|
|
24
57
|
right?: ToolbarItem[];
|
|
25
|
-
/**
|
|
58
|
+
/** Pins it to the top of the page's scroll area while the page scrolls. */
|
|
26
59
|
sticky?: boolean;
|
|
60
|
+
/** Names the toolbar for assistive technology, such as "Log controls";
|
|
61
|
+
* defaults to "Actions". */
|
|
62
|
+
label?: string | undefined;
|
|
27
63
|
}): import("react").JSX.Element;
|
|
28
64
|
export {};
|
|
@@ -1,54 +1,93 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
|
-
import { MoreIcon } from "../icons.js";
|
|
3
|
-
import { MenuPopup } from "../components/Menu.js";
|
|
4
2
|
import { buttonClasses } from "../components/buttonClasses.js";
|
|
3
|
+
import { MoreIcon } from "../icons.js";
|
|
4
|
+
import { MenuPopup, MenuSeparator } from "../components/Menu.js";
|
|
5
5
|
import { Tooltip } from "../components/Tooltip.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import { useStrings } from "../components/Provider.js";
|
|
7
|
+
import { AppLink } from "../components/AppLink.js";
|
|
8
|
+
import { MenuEntries, useRunAction, warnIfInert } from "../components/MenuEntries.js";
|
|
9
9
|
import { Menu } from "@base-ui/react/menu";
|
|
10
10
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
-
import { Fragment as Fragment$1, createContext,
|
|
11
|
+
import { Fragment as Fragment$1, cloneElement, createContext, isValidElement, use, useLayoutEffect, useRef, useState } from "react";
|
|
12
|
+
import { Button } from "@base-ui/react/button";
|
|
12
13
|
import { Toggle } from "@base-ui/react/toggle";
|
|
14
|
+
import { Toolbar } from "@base-ui/react/toolbar";
|
|
13
15
|
//#region src/list/ActionToolbar.tsx
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
var PlacementContext = createContext("alone");
|
|
17
|
+
var FoldedContext = createContext(false);
|
|
18
|
+
/** A toolbar button's classes: outlined unless it's the view's one primary
|
|
19
|
+
* action, so the toolbar has a hierarchy; a toggle shows its state by
|
|
20
|
+
* `aria-pressed` instead of a fill. A destructive one reads in the error
|
|
21
|
+
* color, as it does in a menu. */
|
|
22
|
+
function actionClasses(action, iconOnly) {
|
|
23
|
+
const primary = action.primary && action.checked === void 0;
|
|
24
|
+
return cx(buttonClasses({
|
|
25
|
+
variant: primary ? "primary" : "outline",
|
|
26
|
+
size: "sm",
|
|
27
|
+
iconOnly
|
|
28
|
+
}), action.destructive && !primary && "text-(--theme-error-text)");
|
|
18
29
|
}
|
|
19
|
-
/** A toolbar action as a button: a link with `href`, a toggle with `checked`.
|
|
30
|
+
/** A toolbar action as a button: a link with `href`, a toggle with `checked`.
|
|
31
|
+
* In an ActionToolbar it is one of its items, reached with the arrow keys. */
|
|
20
32
|
function ToolbarButton({ action }) {
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
33
|
+
const placement = use(PlacementContext);
|
|
34
|
+
const runAction = useRunAction();
|
|
35
|
+
const folded = use(FoldedContext);
|
|
36
|
+
if (placement === "menu") return /* @__PURE__ */ jsx(MenuEntries, { entries: [action] });
|
|
37
|
+
warnIfInert(action, "ActionToolbar");
|
|
38
|
+
const iconOnly = Boolean(action.icon) && placement === "toolbar" && folded;
|
|
39
|
+
const className = actionClasses(action, iconOnly);
|
|
40
|
+
const reason = action.disabled ? action.disabledReason : void 0;
|
|
41
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
42
|
+
action.icon,
|
|
43
|
+
iconOnly ? /* @__PURE__ */ jsx("span", {
|
|
44
|
+
className: "sr-only",
|
|
45
|
+
children: action.label
|
|
46
|
+
}) : action.label,
|
|
47
|
+
reason && /* @__PURE__ */ jsxs("span", {
|
|
48
|
+
className: "sr-only",
|
|
49
|
+
children: [". ", reason]
|
|
50
|
+
})
|
|
51
|
+
] });
|
|
52
|
+
const run = () => runAction(action);
|
|
53
|
+
const inToolbar = placement === "toolbar";
|
|
29
54
|
let control;
|
|
30
|
-
if (action.href
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
else
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
if (action.href) {
|
|
56
|
+
const link = /* @__PURE__ */ jsx(AppLink, {
|
|
57
|
+
href: action.href,
|
|
58
|
+
reloadDocument: action.reloadDocument,
|
|
59
|
+
target: action.target,
|
|
60
|
+
disabled: action.disabled,
|
|
61
|
+
className
|
|
62
|
+
});
|
|
63
|
+
control = inToolbar ? /* @__PURE__ */ jsx(Toolbar.Link, {
|
|
64
|
+
render: link,
|
|
65
|
+
children: content
|
|
66
|
+
}) : cloneElement(link, void 0, content);
|
|
67
|
+
} else if (action.checked !== void 0) {
|
|
68
|
+
const toggle = /* @__PURE__ */ jsx(Toggle, {
|
|
69
|
+
pressed: action.checked,
|
|
70
|
+
onPressedChange: run,
|
|
71
|
+
disabled: action.disabled,
|
|
72
|
+
className
|
|
73
|
+
});
|
|
74
|
+
control = inToolbar ? /* @__PURE__ */ jsx(Toolbar.Button, {
|
|
75
|
+
render: toggle,
|
|
76
|
+
disabled: action.disabled,
|
|
77
|
+
children: content
|
|
78
|
+
}) : cloneElement(toggle, void 0, content);
|
|
79
|
+
} else {
|
|
80
|
+
const Control = inToolbar ? Toolbar.Button : Button;
|
|
81
|
+
control = /* @__PURE__ */ jsx(Control, {
|
|
82
|
+
onClick: run,
|
|
83
|
+
disabled: action.disabled,
|
|
84
|
+
focusableWhenDisabled: true,
|
|
85
|
+
className,
|
|
86
|
+
children: content
|
|
87
|
+
});
|
|
88
|
+
}
|
|
50
89
|
return /* @__PURE__ */ jsx(Tooltip, {
|
|
51
|
-
content: action.
|
|
90
|
+
content: reason ?? (iconOnly ? action.tooltip ?? action.label : action.tooltip),
|
|
52
91
|
side: "bottom",
|
|
53
92
|
children: control
|
|
54
93
|
});
|
|
@@ -57,11 +96,12 @@ function OverflowMenu({ items }) {
|
|
|
57
96
|
const { list: t } = useStrings();
|
|
58
97
|
return /* @__PURE__ */ jsxs(Menu.Root, { children: [/* @__PURE__ */ jsx(Tooltip, {
|
|
59
98
|
content: t.moreActions,
|
|
60
|
-
children: /* @__PURE__ */ jsx(
|
|
99
|
+
children: /* @__PURE__ */ jsx(Toolbar.Button, {
|
|
100
|
+
render: /* @__PURE__ */ jsx(Menu.Trigger, {}),
|
|
61
101
|
"aria-label": t.moreActions,
|
|
62
102
|
className: buttonClasses({
|
|
63
103
|
variant: "outline",
|
|
64
|
-
size: "
|
|
104
|
+
size: "sm",
|
|
65
105
|
iconOnly: true
|
|
66
106
|
}),
|
|
67
107
|
"data-testid": "action-toolbar-menu-button",
|
|
@@ -71,80 +111,239 @@ function OverflowMenu({ items }) {
|
|
|
71
111
|
keepMounted: true,
|
|
72
112
|
align: "end",
|
|
73
113
|
className: "min-w-56",
|
|
74
|
-
children: /* @__PURE__ */ jsx(
|
|
75
|
-
value:
|
|
76
|
-
children: items.map((item) => "element" in item ? /* @__PURE__ */ jsx(Fragment$1, { children: item.element }, item.key) : /* @__PURE__ */ jsx(MenuEntries, { entries: [item] }, item.key))
|
|
114
|
+
children: /* @__PURE__ */ jsx(PlacementContext, {
|
|
115
|
+
value: "menu",
|
|
116
|
+
children: items.map((group, i) => /* @__PURE__ */ jsxs(Fragment$1, { children: [i > 0 && /* @__PURE__ */ jsx(MenuSeparator, {}), group.map((item) => "element" in item ? /* @__PURE__ */ jsx(Fragment$1, { children: item.element }, item.key) : /* @__PURE__ */ jsx(MenuEntries, { entries: [item] }, item.key))] }, group[0]?.key ?? i))
|
|
77
117
|
})
|
|
78
118
|
})] });
|
|
79
119
|
}
|
|
80
|
-
var
|
|
120
|
+
var FITS_ALL = {
|
|
121
|
+
folded: [],
|
|
122
|
+
overflow: []
|
|
123
|
+
};
|
|
124
|
+
var GROUP_GAP = 10;
|
|
81
125
|
var ITEM_GAP = 4;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
126
|
+
var MENU_BUTTON_WIDTH = 28;
|
|
127
|
+
function rowWidth(groups, right, fit) {
|
|
128
|
+
const hidden = new Set(fit.overflow);
|
|
129
|
+
const folded = new Set(fit.folded);
|
|
130
|
+
const width = (item) => folded.has(item.key) ? item.folded : item.full;
|
|
131
|
+
const run = (items) => items.reduce((sum, item) => sum + width(item), 0) + ITEM_GAP * (items.length - 1);
|
|
132
|
+
const shownGroups = groups.map((group) => group.filter((item) => !hidden.has(item.key))).filter((group) => group.length > 0);
|
|
133
|
+
let total = shownGroups.reduce((sum, group) => sum + run(group), 0);
|
|
134
|
+
total += (shownGroups.length - 1) * 21;
|
|
135
|
+
const shownRight = right.filter((item) => !hidden.has(item.key));
|
|
136
|
+
const end = (shownRight.length > 0 ? run(shownRight) : 0) + (hidden.size > 0 ? MENU_BUTTON_WIDTH + (shownRight.length > 0 ? ITEM_GAP : 0) : 0);
|
|
137
|
+
if (end > 0) total += end + (shownGroups.length > 0 ? GROUP_GAP : 0);
|
|
138
|
+
return Math.max(total, 0);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Fits a toolbar to `available` pixels one item at a time, stopping as soon as
|
|
142
|
+
* it fits, so it keeps every label the width allows. Items go lowest priority
|
|
143
|
+
* first: the right-hand ones, then the last group's, and so on, each from the
|
|
144
|
+
* end. First their labels fold to their icons, one by one; then, all folded,
|
|
145
|
+
* they move into the `…` menu in the same order; the primary action never
|
|
146
|
+
* moves, and its label folds only once nothing else is left to try.
|
|
147
|
+
*/
|
|
148
|
+
function fitToolbar(groups, right, available) {
|
|
149
|
+
const fits = (fit) => rowWidth(groups, right, fit) <= available + .5;
|
|
150
|
+
if (fits(FITS_ALL)) return FITS_ALL;
|
|
151
|
+
const byPriority = [...right].reverse().concat(...[...groups].reverse().map((g) => [...g].reverse()));
|
|
152
|
+
const rest = byPriority.filter((item) => !item.primary);
|
|
153
|
+
const folded = [];
|
|
154
|
+
for (const item of rest) {
|
|
155
|
+
if (item.folded >= item.full) continue;
|
|
156
|
+
folded.push(item.key);
|
|
157
|
+
const fit = {
|
|
158
|
+
folded: [...folded],
|
|
159
|
+
overflow: []
|
|
160
|
+
};
|
|
161
|
+
if (fits(fit)) return fit;
|
|
91
162
|
}
|
|
92
|
-
|
|
163
|
+
const overflow = [];
|
|
164
|
+
for (const item of rest) {
|
|
165
|
+
overflow.push(item.key);
|
|
166
|
+
const fit = {
|
|
167
|
+
folded: [...folded],
|
|
168
|
+
overflow: [...overflow]
|
|
169
|
+
};
|
|
170
|
+
if (fits(fit)) return fit;
|
|
171
|
+
}
|
|
172
|
+
const primary = byPriority.filter((item) => item.primary).map((item) => item.key);
|
|
173
|
+
return {
|
|
174
|
+
folded: [...folded, ...primary],
|
|
175
|
+
overflow
|
|
176
|
+
};
|
|
93
177
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
178
|
+
var sameFit = (a, b) => a.folded.join("\n") === b.folded.join("\n") && a.overflow.join("\n") === b.overflow.join("\n");
|
|
179
|
+
var isPrimary = (item) => "label" in item && Boolean(item.primary);
|
|
180
|
+
/**
|
|
181
|
+
* Fits the toolbar to its own width. It measures a hidden copy of the actions,
|
|
182
|
+
* each with its label and folded, and caches the widths of element items as
|
|
183
|
+
* they render, so the answer doesn't depend on how the toolbar is folded at the
|
|
184
|
+
* moment and settles within one paint.
|
|
185
|
+
*/
|
|
186
|
+
function useToolbarFit(root, copy, groups, right) {
|
|
187
|
+
const [fit, setFit] = useState(FITS_ALL);
|
|
188
|
+
const elementWidths = useRef(/* @__PURE__ */ new Map());
|
|
189
|
+
const measure = useRef(() => {});
|
|
190
|
+
measure.current = () => {
|
|
191
|
+
const available = root?.clientWidth ?? 0;
|
|
192
|
+
if (!root || !copy || available === 0) {
|
|
193
|
+
setFit((prev) => sameFit(prev, FITS_ALL) ? prev : FITS_ALL);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const measured = /* @__PURE__ */ new Map();
|
|
197
|
+
for (const el of copy.querySelectorAll("[data-measure]")) {
|
|
198
|
+
const key = el.dataset["measure"] ?? "";
|
|
199
|
+
const widths = measured.get(key) ?? {};
|
|
200
|
+
widths[el.dataset["fold"] === "folded" ? "folded" : "full"] = el.getBoundingClientRect().width;
|
|
201
|
+
measured.set(key, widths);
|
|
202
|
+
}
|
|
203
|
+
for (const el of root.querySelectorAll("[data-toolbar-element]")) {
|
|
204
|
+
const key = el.dataset["toolbarElement"] ?? "";
|
|
205
|
+
const widths = elementWidths.current.get(key) ?? {};
|
|
206
|
+
widths[fit.folded.includes(key) ? "folded" : "full"] = el.getBoundingClientRect().width;
|
|
207
|
+
elementWidths.current.set(key, widths);
|
|
208
|
+
}
|
|
209
|
+
const widthsOf = (item) => {
|
|
210
|
+
const w = ("element" in item ? elementWidths.current : measured).get(item.key) ?? {};
|
|
211
|
+
const full = w.full ?? w.folded ?? 0;
|
|
212
|
+
return {
|
|
213
|
+
key: item.key,
|
|
214
|
+
full,
|
|
215
|
+
folded: w.folded ?? full,
|
|
216
|
+
primary: isPrimary(item)
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
const next = fitToolbar(groups.map((g) => g.map(widthsOf)), right.map(widthsOf), available);
|
|
220
|
+
setFit((prev) => sameFit(prev, next) ? prev : next);
|
|
221
|
+
};
|
|
222
|
+
useLayoutEffect(() => measure.current());
|
|
101
223
|
useLayoutEffect(() => {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const observer = new ResizeObserver(update);
|
|
107
|
-
observer.observe(el);
|
|
224
|
+
if (!root || !copy) return;
|
|
225
|
+
const observer = new ResizeObserver(() => measure.current());
|
|
226
|
+
observer.observe(root);
|
|
227
|
+
observer.observe(copy);
|
|
108
228
|
return () => observer.disconnect();
|
|
109
|
-
}, []);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
229
|
+
}, [root, copy]);
|
|
230
|
+
return fit;
|
|
231
|
+
}
|
|
232
|
+
/** The hidden copy `useToolbarFit` measures: each action with its label and
|
|
233
|
+
* folded to its icon, styled as its button is. */
|
|
234
|
+
function MeasuringCopy({ items, ref }) {
|
|
235
|
+
return /* @__PURE__ */ jsx("div", {
|
|
236
|
+
"aria-hidden": true,
|
|
237
|
+
inert: true,
|
|
238
|
+
className: "pointer-events-none invisible absolute top-0 left-0 size-0 overflow-hidden",
|
|
239
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
240
|
+
ref,
|
|
241
|
+
className: "flex w-max gap-1",
|
|
242
|
+
children: items.map((item) => "element" in item ? null : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
|
|
243
|
+
"data-measure": item.key,
|
|
244
|
+
"data-fold": "full",
|
|
245
|
+
className: actionClasses(item, false),
|
|
246
|
+
children: [item.icon, item.label]
|
|
247
|
+
}), item.icon && /* @__PURE__ */ jsx("span", {
|
|
248
|
+
"data-measure": item.key,
|
|
249
|
+
"data-fold": "folded",
|
|
250
|
+
className: actionClasses(item, true),
|
|
251
|
+
children: item.icon
|
|
252
|
+
})] }, item.key))
|
|
253
|
+
})
|
|
117
254
|
});
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return /* @__PURE__ */
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
ref: (el) => {
|
|
125
|
-
itemRefs.current[item.key] = el;
|
|
126
|
-
},
|
|
255
|
+
}
|
|
256
|
+
function ItemSlot({ item, folded }) {
|
|
257
|
+
return /* @__PURE__ */ jsx(FoldedContext, {
|
|
258
|
+
value: folded,
|
|
259
|
+
children: "element" in item ? /* @__PURE__ */ jsx("span", {
|
|
260
|
+
"data-toolbar-element": item.key,
|
|
127
261
|
className: "inline-flex shrink-0",
|
|
128
|
-
children:
|
|
129
|
-
}
|
|
262
|
+
children: item.element
|
|
263
|
+
}) : /* @__PURE__ */ jsx(ToolbarButton, { action: item })
|
|
130
264
|
});
|
|
131
265
|
}
|
|
132
|
-
/** Pins a
|
|
133
|
-
var stickyToolbarClasses = "sticky top-
|
|
134
|
-
/**
|
|
135
|
-
|
|
266
|
+
/** Pins a toolbar to the top of the page's scroll area while the content scrolls under it. */
|
|
267
|
+
var stickyToolbarClasses = "sticky top-0 z-(--layer-sticky) bg-(--theme-app-bg)";
|
|
268
|
+
/** A view's controls in one WAI-ARIA toolbar: a single tab stop, with the arrow
|
|
269
|
+
* keys moving between its items, the `…` menu and disabled ones included, so
|
|
270
|
+
* their reasons stay reachable. Action groups sit on the left and `right` holds
|
|
271
|
+
* secondary actions at the end. It stays one row: when it runs out of room
|
|
272
|
+
* labels fold to their icons one at a time, then actions move into a `…` menu,
|
|
273
|
+
* the right-hand ones first and the primary action never. Give each action its
|
|
274
|
+
* own icon, since a folded one is known by it, and mark the one action the
|
|
275
|
+
* view leads to `primary`. */
|
|
276
|
+
function ActionToolbar({ groups, right = [], sticky = false, label }) {
|
|
277
|
+
const { common } = useStrings();
|
|
278
|
+
const [root, setRoot] = useState(null);
|
|
279
|
+
const [copy, setCopy] = useState(null);
|
|
136
280
|
const visibleGroups = groups.map((group) => group.filter((a) => !a.hidden)).filter((group) => group.length > 0);
|
|
137
281
|
const visibleRight = right.filter((a) => !a.hidden);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
282
|
+
const all = [...visibleGroups.flat(), ...visibleRight];
|
|
283
|
+
warnIfManyPrimary(all);
|
|
284
|
+
warnIfIconsRepeat(all);
|
|
285
|
+
const fit = useToolbarFit(root, copy, visibleGroups, visibleRight);
|
|
286
|
+
const overflow = new Set(fit.overflow);
|
|
287
|
+
const folded = new Set(fit.folded);
|
|
288
|
+
const shown = (items) => items.filter((item) => !overflow.has(item.key));
|
|
289
|
+
const shownGroups = visibleGroups.map(shown).filter((group) => group.length > 0);
|
|
290
|
+
const shownRight = shown(visibleRight);
|
|
291
|
+
const menu = [...visibleGroups, visibleRight].map((group) => group.filter((item) => overflow.has(item.key))).filter((group) => group.length > 0);
|
|
292
|
+
return /* @__PURE__ */ jsxs(Toolbar.Root, {
|
|
293
|
+
ref: setRoot,
|
|
294
|
+
"aria-label": label ?? common.actions,
|
|
295
|
+
className: cx("relative flex min-w-0 items-center gap-2.5", sticky && cx("sticky top-0 z-(--layer-sticky) bg-(--theme-app-bg)", "py-1.5")),
|
|
296
|
+
children: [/* @__PURE__ */ jsx(MeasuringCopy, {
|
|
297
|
+
ref: setCopy,
|
|
298
|
+
items: all
|
|
299
|
+
}), /* @__PURE__ */ jsxs(PlacementContext, {
|
|
300
|
+
value: "toolbar",
|
|
301
|
+
children: [shownGroups.map((group, i) => /* @__PURE__ */ jsxs(Fragment$1, { children: [i > 0 && /* @__PURE__ */ jsx(Toolbar.Separator, {
|
|
302
|
+
orientation: "vertical",
|
|
303
|
+
className: "h-5 w-px shrink-0 bg-(--theme-border)"
|
|
304
|
+
}), /* @__PURE__ */ jsx(Toolbar.Group, {
|
|
305
|
+
className: "flex shrink-0 items-center gap-1",
|
|
306
|
+
children: group.map((item) => /* @__PURE__ */ jsx(ItemSlot, {
|
|
307
|
+
item,
|
|
308
|
+
folded: folded.has(item.key)
|
|
309
|
+
}, item.key))
|
|
310
|
+
})] }, group[0]?.key ?? i)), (shownRight.length > 0 || menu.length > 0) && /* @__PURE__ */ jsxs("div", {
|
|
311
|
+
className: "ml-auto flex shrink-0 items-center gap-1",
|
|
312
|
+
children: [shownRight.map((item) => /* @__PURE__ */ jsx(ItemSlot, {
|
|
313
|
+
item,
|
|
314
|
+
folded: folded.has(item.key)
|
|
315
|
+
}, item.key)), menu.length > 0 && /* @__PURE__ */ jsx(OverflowMenu, { items: menu })]
|
|
316
|
+
})]
|
|
317
|
+
})]
|
|
147
318
|
});
|
|
148
319
|
}
|
|
320
|
+
var warnedPrimary = /* @__PURE__ */ new Set();
|
|
321
|
+
/** Warns in development when more than one action is primary: the fill marks
|
|
322
|
+
* the one next step, so two of them mark none. */
|
|
323
|
+
function warnIfManyPrimary(items) {
|
|
324
|
+
if (process.env.NODE_ENV === "production") return;
|
|
325
|
+
const labels = items.flatMap((item) => "label" in item && item.primary ? [item.label] : []);
|
|
326
|
+
const names = labels.map((label) => `"${label}"`).join(", ");
|
|
327
|
+
if (labels.length < 2 || warnedPrimary.has(names)) return;
|
|
328
|
+
warnedPrimary.add(names);
|
|
329
|
+
console.warn(`ActionToolbar: ${names} are all primary; mark one action at most.`);
|
|
330
|
+
}
|
|
331
|
+
var warnedIcons = /* @__PURE__ */ new Set();
|
|
332
|
+
/** Warns in development when two actions share an icon: folded to their icons
|
|
333
|
+
* in a narrow toolbar, they would look the same. */
|
|
334
|
+
function warnIfIconsRepeat(items) {
|
|
335
|
+
if (process.env.NODE_ENV === "production") return;
|
|
336
|
+
const byIcon = /* @__PURE__ */ new Map();
|
|
337
|
+
for (const item of items) {
|
|
338
|
+
if ("element" in item || !isValidElement(item.icon) || typeof item.icon.type === "string") continue;
|
|
339
|
+
byIcon.set(item.icon.type, [...byIcon.get(item.icon.type) ?? [], item.label]);
|
|
340
|
+
}
|
|
341
|
+
for (const labels of byIcon.values()) {
|
|
342
|
+
const names = labels.map((label) => `"${label}"`).join(", ");
|
|
343
|
+
if (labels.length < 2 || warnedIcons.has(names)) continue;
|
|
344
|
+
warnedIcons.add(names);
|
|
345
|
+
console.warn(`ActionToolbar: ${names} share an icon, so they look the same once their labels fold; give each its own.`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
149
348
|
//#endregion
|
|
150
|
-
export { ActionToolbar, ToolbarButton, stickyToolbarClasses };
|
|
349
|
+
export { ActionToolbar, ToolbarButton, fitToolbar, stickyToolbarClasses };
|