@keboola/design 1.2.0 → 1.3.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 +25 -0
- package/dist/icons.cjs +731 -0
- package/dist/icons.cjs.map +1 -0
- package/dist/icons.js +29 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.cjs +429 -333
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +426 -331
- package/dist/index.js.map +1 -1
- package/dist/markdown-editor.cjs +270 -135
- package/dist/markdown-editor.cjs.map +1 -1
- package/dist/markdown-editor.js +270 -135
- package/dist/markdown-editor.js.map +1 -1
- package/dist/mermaid.cjs +24 -15
- package/dist/mermaid.cjs.map +1 -1
- package/dist/mermaid.js +24 -15
- package/dist/mermaid.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +12 -5
- package/src/blocks/AIKit/CollapsibleStepHeader.tsx +7 -8
- package/src/blocks/AIKit/InlineStepLabel.tsx +7 -7
- package/src/blocks/AIKit/MessageBubble.stories.tsx +11 -3
- package/src/blocks/AIKit/MessageBubble.tsx +1 -1
- package/src/blocks/AIKit/PlanApproval.tsx +9 -8
- package/src/blocks/AIKit/PlanRewritingIndicator.tsx +2 -2
- package/src/blocks/AIKit/PromptInput.stories.tsx +10 -2
- package/src/blocks/AIKit/PromptInputSubmit.tsx +12 -8
- package/src/blocks/AIKit/Question.tsx +8 -9
- package/src/blocks/AIKit/SuggestedPrompt.tsx +12 -5
- package/src/blocks/AIKit/ThinkingIndicator.tsx +2 -2
- package/src/blocks/AutoSaveTextarea/AutoSaveTextarea.tsx +4 -4
- package/src/blocks/ClipboardButton/ClipboardButton.tsx +7 -5
- package/src/blocks/ClipboardButton/ClipboardInline.tsx +2 -3
- package/src/blocks/ColorInput/ColorInput.tsx +4 -2
- package/src/blocks/ColorInput/ColorPicker.tsx +5 -2
- package/src/blocks/EmptyState/EmptyState.tsx +16 -0
- package/src/blocks/ErrorState/ErrorState.tsx +15 -0
- package/src/blocks/Form/formHook.ts +15 -0
- package/src/blocks/MultiActionButtonGroup/MultiActionButton.stories.tsx +12 -11
- package/src/blocks/MultiActionButtonGroup/MultiActionButton.tsx +4 -4
- package/src/blocks/MultiCombobox/MultiCombobox.tsx +28 -12
- package/src/blocks/PageHeader/AGENTS.md +63 -3
- package/src/blocks/PageHeader/PageHeader.stories.tsx +124 -28
- package/src/blocks/PageHeader/PageHeader.tsx +15 -0
- package/src/blocks/PageHeader/PageHeaderDetail.tsx +23 -1
- package/src/blocks/PageHeader/PageHeaderIndex.tsx +20 -1
- package/src/blocks/PageLayout/PageLayout.tsx +26 -0
- package/src/blocks/PageToolbar/PageToolbar.stories.tsx +348 -0
- package/src/blocks/PageToolbar/PageToolbar.tsx +119 -0
- package/src/blocks/PageToolbar/index.ts +1 -0
- package/src/blocks/PasswordInput/PasswordInput.tsx +10 -10
- package/src/blocks/SearchInput/SearchInput.tsx +30 -4
- package/src/blocks/SearchInputWithNavigation/SearchInputWithNavigation.tsx +9 -6
- package/src/blocks/SplitButton/SplitButton.stories.tsx +7 -4
- package/src/blocks/SplitButton/SplitButton.tsx +17 -4
- package/src/blocks/Tagger/TagItem.tsx +4 -1
- package/src/blocks/Tagger/TagSelectPopover/TagSelectCreateItem.tsx +3 -1
- package/src/blocks/Tagger/TagSelectPopover/TagSelectList.tsx +2 -2
- package/src/blocks/Tagger/Tagger.tsx +2 -2
- package/src/components/Alert/Alert.stories.tsx +11 -3
- package/src/components/Alert/Alert.tsx +19 -1
- package/src/components/Alert/AlertCollapsibleIcon.tsx +3 -4
- package/src/components/Alert/AlertIcon.tsx +18 -21
- package/src/components/Badge/Badge.stories.tsx +22 -4
- package/src/components/Badge/Badge.tsx +16 -0
- package/src/components/Badge/BadgeButton.tsx +7 -6
- package/src/components/Breadcrumb/BreadcrumbEllipsis.tsx +2 -2
- package/src/components/Breadcrumb/BreadcrumbSeparator.tsx +2 -2
- package/src/components/Button/Button.stories.tsx +14 -2
- package/src/components/Button/Button.tsx +42 -5
- package/src/components/Button/styles.ts +4 -6
- package/src/components/ButtonGroup/ButtonGroup.stories.tsx +32 -10
- package/src/components/Card/Card.tsx +17 -0
- package/src/components/Checkbox/Checkbox.tsx +3 -6
- package/src/components/CodeEditor/highlighting.ts +1 -1
- package/src/components/CollapsiblePanel.tsx +2 -5
- package/src/components/Combobox/Combobox.tsx +12 -14
- package/src/components/Combobox/ComboboxChips.tsx +6 -1
- package/src/components/Combobox/ComboboxItem.tsx +3 -4
- package/src/components/Combobox/ComboboxItemIndicator.tsx +6 -4
- package/src/components/Combobox/types.ts +2 -6
- package/src/components/Command/Command.stories.tsx +47 -56
- package/src/components/Command/CommandItem/CheckIndicator.tsx +26 -14
- package/src/components/Command/CommandItem/CommandItem.tsx +6 -3
- package/src/components/DropdownMenu/DropdownMenu.stories.tsx +8 -4
- package/src/components/DropdownMenu/DropdownMenuCheckboxItem.tsx +4 -1
- package/src/components/DropdownMenu/DropdownMenuItem.tsx +12 -6
- package/src/components/DropdownMenu/DropdownMenuItemIndicator.tsx +12 -6
- package/src/components/DropdownMenu/DropdownMenuRadioGroup/DropdownMenuRadioItem.tsx +8 -5
- package/src/components/DropdownMenu/DropdownMenuSub/DropdownMenuSubTrigger.tsx +2 -2
- package/src/components/DropdownMenu/ItemIcon.tsx +16 -8
- package/src/components/FormGroup/FormGroup.tsx +15 -0
- package/src/components/FullScreenModal/FullScreenModalHeader.tsx +4 -1
- package/src/components/Input/DateInput/DateInput.tsx +4 -1
- package/src/components/Input/DateRangeInput/DateRangeInput.tsx +4 -6
- package/src/components/Input/Input.tsx +18 -2
- package/src/components/Input/TextInput/TextInput.tsx +16 -0
- package/src/components/Input/TimeInput/TimeInput.tsx +4 -1
- package/src/components/InputGroup/InputGroup.stories.tsx +11 -3
- package/src/components/MarkdownEditor/MarkdownEditor.stories.tsx +63 -1
- package/src/components/MarkdownEditor/MarkdownEditor.tsx +105 -15
- package/src/components/MarkdownEditor/nodes/ImageNode.tsx +10 -10
- package/src/components/MarkdownEditor/nodes/MermaidNode.tsx +8 -8
- package/src/components/MarkdownEditor/plugins/KeyboardPlugin.tsx +47 -3
- package/src/components/MarkdownEditor/plugins/MarkdownSyncPlugin.tsx +12 -3
- package/src/components/MarkdownEditor/plugins/ToolbarPlugin.tsx +20 -10
- package/src/components/MarkdownEditor/plugins/toolbar/AlignmentDropdown.tsx +20 -14
- package/src/components/MarkdownEditor/plugins/toolbar/BlockTypeDropdown.tsx +25 -16
- package/src/components/MarkdownEditor/plugins/toolbar/LinkPanel.tsx +3 -3
- package/src/components/MarkdownEditor/transformers.ts +10 -0
- package/src/components/MergeEditor/AcceptControls/AcceptButton.tsx +9 -4
- package/src/components/MergeEditor/AcceptControls/AcceptControls.tsx +25 -14
- package/src/components/MergeEditor/ConflictStatus/ConflictStatusIndicator.tsx +2 -2
- package/src/components/MergeEditor/MergeEditor.stories.tsx +15 -0
- package/src/components/MergeEditor/MergeEditor.tsx +9 -0
- package/src/components/MergeEditor/MergePane.tsx +22 -4
- package/src/components/MergeEditor/decorations.ts +2 -2
- package/src/components/Modal/Modal.stories.tsx +6 -4
- package/src/components/Modal/Modal.tsx +26 -0
- package/src/components/Modal/ModalBody.tsx +5 -0
- package/src/components/Modal/ModalCloseIcon.tsx +2 -3
- package/src/components/Modal/ModalContent.tsx +8 -0
- package/src/components/Modal/ModalFooter.tsx +5 -0
- package/src/components/Modal/ModalHeader.tsx +11 -0
- package/src/components/Modal/ModalIcon.tsx +45 -19
- package/src/components/Navbar/Navbar.stories.tsx +26 -13
- package/src/components/Navbar/NavbarMenuButton.tsx +11 -10
- package/src/components/Pagination/PaginationEllipsis.tsx +2 -2
- package/src/components/Pagination/PaginationNext.tsx +2 -1
- package/src/components/Pagination/PaginationPrevious.tsx +4 -1
- package/src/components/PanelWithDetails.tsx +3 -4
- package/src/components/Popconfirm/Popconfirm.stories.tsx +13 -5
- package/src/components/RadioGroup/Radio.tsx +6 -12
- package/src/components/Search/ClearButton.tsx +2 -2
- package/src/components/Search/Search.tsx +3 -3
- package/src/components/Select/Select.tsx +16 -5
- package/src/components/Select/SelectContent.tsx +13 -4
- package/src/components/Select/SelectItem.tsx +6 -5
- package/src/components/Select/SelectTrigger.tsx +2 -2
- package/src/components/Sidebar/Sidebar.stories.tsx +55 -19
- package/src/components/Sidebar/SidebarGroup/SidebarGroupAction.tsx +8 -4
- package/src/components/Sidebar/SidebarMenu/SidebarMenuButton.tsx +4 -2
- package/src/components/Sidebar/SidebarMenu/SidebarMenuCard.tsx +3 -2
- package/src/components/Sidebar/SidebarMenu/SidebarMenuIcon.tsx +12 -12
- package/src/components/Sidebar/SidebarMenu/SidebarMenuSub.tsx +3 -7
- package/src/components/Sidebar/SidebarMenu/SidebarSearchInput.tsx +3 -5
- package/src/components/Skeleton/Skeleton.stories.tsx +52 -0
- package/src/components/Skeleton/Skeleton.tsx +24 -1
- package/src/components/Table/Table.stories.tsx +223 -1
- package/src/components/Table/Table.tsx +17 -0
- package/src/components/Tabs.tsx +25 -2
- package/src/components/Toast/ToastDismiss.tsx +3 -3
- package/src/components/Toast/ToastRoot.tsx +24 -19
- package/src/components/Toggle/Toggle.tsx +11 -6
- package/src/components/Tooltip/Tooltip.tsx +23 -0
- package/src/components/ZoomableSvg/ZoomableSvg.tsx +8 -6
- package/src/icons/Keboola.tsx +28 -0
- package/src/icons/icons.stories.tsx +174 -0
- package/src/icons/index.ts +180 -0
- package/src/index.ts +1 -0
- package/src/patterns/AlertWithAction.stories.tsx +30 -0
- package/src/patterns/CardWithEmptyState.stories.tsx +66 -0
- package/src/patterns/ConfirmDialog.stories.tsx +72 -0
- package/src/patterns/CreatableMultiCombobox.stories.tsx +33 -0
- package/src/patterns/DataCardWithSkeleton.stories.tsx +48 -0
- package/src/patterns/DisabledButtonTooltip.stories.tsx +30 -0
- package/src/patterns/FilterToolbar.stories.tsx +71 -0
- package/src/patterns/InfoTooltip.stories.tsx +56 -0
- package/src/patterns/ListDetail.stories.tsx +65 -0
- package/src/patterns/ModalForm.stories.tsx +59 -0
- package/src/patterns/PageHeaderTabs.stories.tsx +48 -0
- package/src/styles.css +16 -8
- package/src/utils/legacyIcon.tsx +28 -0
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { ComponentProps, ElementRef, ReactNode, Ref } from 'react';
|
|
2
2
|
|
|
3
|
+
import { ChevronDown } from '@keboola/design/icons';
|
|
4
|
+
|
|
3
5
|
import { Button } from '../../components/Button';
|
|
4
6
|
import { ButtonGroup } from '../../components/ButtonGroup';
|
|
5
7
|
import { DropdownMenu } from '../../components/DropdownMenu';
|
|
8
|
+
import { renderLegacyIcon } from '../../utils/legacyIcon';
|
|
6
9
|
|
|
7
10
|
type Variant = ComponentProps<typeof Button>['variant'];
|
|
8
11
|
type Size = NonNullable<ComponentProps<typeof Button>['size']>;
|
|
@@ -15,10 +18,12 @@ type Props = {
|
|
|
15
18
|
actions?: ReactNode;
|
|
16
19
|
isActionsOpen?: boolean;
|
|
17
20
|
onActionsOpenChange?: (open: boolean) => void;
|
|
21
|
+
/** Optional leading-icon slot — pass a Lucide icon node. */
|
|
22
|
+
icon?: ReactNode;
|
|
18
23
|
// inline and invisible aren't supported in SplitButton
|
|
19
24
|
variant?: Exclude<Variant, 'inline' | 'invisible'>;
|
|
20
25
|
size?: SpitButtonSize;
|
|
21
|
-
} & Omit<ComponentProps<typeof Button>, 'variant' | 'size'>;
|
|
26
|
+
} & Omit<ComponentProps<typeof Button>, 'variant' | 'size' | 'icon'>;
|
|
22
27
|
|
|
23
28
|
const iconSizeMap: Record<SpitButtonSize, Size> = {
|
|
24
29
|
medium: 'icon',
|
|
@@ -35,10 +40,17 @@ const SplitButtonBase = ({
|
|
|
35
40
|
isActionsOpen,
|
|
36
41
|
onActionsOpenChange,
|
|
37
42
|
argo = false,
|
|
43
|
+
icon,
|
|
44
|
+
children,
|
|
38
45
|
...rest
|
|
39
46
|
}: Props & { ref?: Ref<ElementRef<'button'>> }) => (
|
|
40
47
|
<ButtonGroup space="compact">
|
|
41
|
-
<Button {...rest} ref={ref} disabled={disabled} variant={variant} size={size} argo={argo}
|
|
48
|
+
<Button {...rest} ref={ref} disabled={disabled} variant={variant} size={size} argo={argo}>
|
|
49
|
+
{/* Suppress the leading icon while Button renders its spinner so the two
|
|
50
|
+
glyphs don't double-stack. */}
|
|
51
|
+
{!rest.isLoading && renderLegacyIcon(icon)}
|
|
52
|
+
{children}
|
|
53
|
+
</Button>
|
|
42
54
|
|
|
43
55
|
{actions && (
|
|
44
56
|
<DropdownMenu
|
|
@@ -52,9 +64,10 @@ const SplitButtonBase = ({
|
|
|
52
64
|
argo={argo}
|
|
53
65
|
size={iconSizeMap[size]}
|
|
54
66
|
variant={variant}
|
|
55
|
-
icon="caret-down"
|
|
56
67
|
aria-label="Additional actions"
|
|
57
|
-
|
|
68
|
+
>
|
|
69
|
+
<ChevronDown className="tw-size-5" />
|
|
70
|
+
</Button>
|
|
58
71
|
</DropdownMenu.Trigger>
|
|
59
72
|
<DropdownMenu.Content align="end">{actions}</DropdownMenu.Content>
|
|
60
73
|
</DropdownMenu>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Badge } from '../../components/Badge';
|
|
2
|
+
import { X } from '../../icons';
|
|
2
3
|
|
|
3
4
|
import type { TagOption } from './types';
|
|
4
5
|
|
|
@@ -21,7 +22,9 @@ export const TagItem = ({ tag, onRemove, disabled = false }: Props) => {
|
|
|
21
22
|
|
|
22
23
|
{onRemove && !isDisabled && (
|
|
23
24
|
<Badge.Addon variant="floating" align="inline-end">
|
|
24
|
-
<Badge.Button onClick={handleRemove}
|
|
25
|
+
<Badge.Button onClick={handleRemove} aria-label="Remove Tag">
|
|
26
|
+
<X className="tw-size-3" />
|
|
27
|
+
</Badge.Button>
|
|
25
28
|
</Badge.Addon>
|
|
26
29
|
)}
|
|
27
30
|
</Badge.Root>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ComponentProps } from 'react';
|
|
2
2
|
import { useCommandState } from 'cmdk';
|
|
3
3
|
|
|
4
|
+
import { Plus } from '@keboola/design/icons';
|
|
5
|
+
|
|
4
6
|
import { Command } from '../../../components/Command';
|
|
5
7
|
import { cn } from '../../../utils';
|
|
6
8
|
|
|
@@ -26,7 +28,7 @@ export const TagSelectCreateItem = ({ onCreate, value }: Props) => {
|
|
|
26
28
|
className={cn('[&_svg]:tw-text-primary-500', {
|
|
27
29
|
'tw-hidden': !isCreateItemVisible,
|
|
28
30
|
})}
|
|
29
|
-
icon="
|
|
31
|
+
icon={<Plus className="tw-size-4" />}
|
|
30
32
|
>
|
|
31
33
|
Create{' '}
|
|
32
34
|
{isSearched ? (
|
|
@@ -2,7 +2,7 @@ import { type ComponentProps, useState } from 'react';
|
|
|
2
2
|
import { defaultFilter } from 'cmdk';
|
|
3
3
|
|
|
4
4
|
import { Command } from '../../../components/Command';
|
|
5
|
-
import {
|
|
5
|
+
import { Search } from '../../../icons';
|
|
6
6
|
import type { TagOption } from '../types';
|
|
7
7
|
|
|
8
8
|
import { TagSelectCreateItem } from './TagSelectCreateItem';
|
|
@@ -41,7 +41,7 @@ export const TagSelectList = ({ options = defaultOptions, onAdd, onCreate }: Pro
|
|
|
41
41
|
<div className="tw-border-b tw-border-neutral-100 tw-p-3">
|
|
42
42
|
<Command.Input
|
|
43
43
|
variant="secondary"
|
|
44
|
-
prefix={<
|
|
44
|
+
prefix={<Search className="tw-size-4 tw-text-neutral-400" />}
|
|
45
45
|
placeholder="Search tags"
|
|
46
46
|
allowClear
|
|
47
47
|
/>
|
|
@@ -2,8 +2,8 @@ import { useMemo } from 'react';
|
|
|
2
2
|
import type { ComponentProps } from 'react';
|
|
3
3
|
|
|
4
4
|
import { Badge } from '../../components/Badge';
|
|
5
|
-
import { Icon } from '../../components/Icon';
|
|
6
5
|
import { TooltipProvider } from '../../components/Tooltip';
|
|
6
|
+
import { Plus } from '../../icons';
|
|
7
7
|
import { cn } from '../../utils';
|
|
8
8
|
|
|
9
9
|
import { TagItem } from './TagItem';
|
|
@@ -82,7 +82,7 @@ export const Tagger = ({
|
|
|
82
82
|
<TagSelectPopover options={tagsToAdd} onAdd={handleAdd} onCreate={handleCreate}>
|
|
83
83
|
<Badge color="gray-inverse" asChild>
|
|
84
84
|
<button type="button" className="tw-flex tw-items-center tw-gap-1">
|
|
85
|
-
<
|
|
85
|
+
<Plus className="tw-size-4" />
|
|
86
86
|
<span
|
|
87
87
|
className={cn({
|
|
88
88
|
'tw-sr-only': isAnySelected,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ComponentProps } from 'react';
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
3
|
|
|
4
|
+
import { Eye, Info, X } from '@keboola/design/icons';
|
|
5
|
+
|
|
4
6
|
import { withTooltipProvider } from '../../utils';
|
|
5
7
|
import { Button } from '../Button';
|
|
6
8
|
|
|
@@ -47,9 +49,15 @@ export const WithActions: Story = {
|
|
|
47
49
|
args: {
|
|
48
50
|
actions: (
|
|
49
51
|
<>
|
|
50
|
-
<Button variant="inline"
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
<Button variant="inline" size="icon-3xs" aria-label="Info">
|
|
53
|
+
<Info className="tw-size-4" />
|
|
54
|
+
</Button>
|
|
55
|
+
<Button variant="inline" size="icon-3xs" aria-label="Seen">
|
|
56
|
+
<Eye className="tw-size-4" />
|
|
57
|
+
</Button>
|
|
58
|
+
<Button variant="inline" size="icon-3xs" aria-label="Close">
|
|
59
|
+
<X className="tw-size-4" />
|
|
60
|
+
</Button>
|
|
53
61
|
</>
|
|
54
62
|
),
|
|
55
63
|
},
|
|
@@ -13,7 +13,7 @@ import { AlertTitle } from './AlertTitle';
|
|
|
13
13
|
|
|
14
14
|
type CommonProps = {
|
|
15
15
|
hideIcon?: boolean;
|
|
16
|
-
icon?:
|
|
16
|
+
icon?: ReactNode;
|
|
17
17
|
} & Pick<ComponentProps<typeof Collapsible>, 'open' | 'onOpenChange' | 'defaultOpen'> &
|
|
18
18
|
Omit<ComponentProps<typeof AlertRoot>, 'children'>;
|
|
19
19
|
|
|
@@ -31,8 +31,26 @@ type DefaultProps = {
|
|
|
31
31
|
actions?: ReactNode;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Props for `Alert`. Discriminated by `mode` — `"default"` renders `title` / `children` / `actions`,
|
|
36
|
+
* `"collapsible"` requires `title` + `children` (no `actions`) and renders a collapsible disclosure.
|
|
37
|
+
* `variant` (`info` | `success` | `warning` | `error`) is a categorical colour contract.
|
|
38
|
+
*/
|
|
34
39
|
type Props = CommonProps & (CollapsibleProps | DefaultProps);
|
|
35
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Persistent inline banner for contextual messages tied to the page layout.
|
|
43
|
+
*
|
|
44
|
+
* **When to use:** a message that lives in the page (info banner, warning about a misconfiguration,
|
|
45
|
+
* post-save validation summary). Use `variant` to encode meaning; pass `actions` for inline buttons.
|
|
46
|
+
* `mode="collapsible"` collapses long error details behind a toggle.
|
|
47
|
+
* **When NOT to use:** for transient feedback (save succeeded, job started) use `toast()`; for
|
|
48
|
+
* field-level validation errors use `FormGroup state="error"`; for a confirmation popover use
|
|
49
|
+
* `Popconfirm`.
|
|
50
|
+
* **Composition:** typically rendered at the top of `PageLayout.Content` or above a form section.
|
|
51
|
+
*
|
|
52
|
+
* @see RECIPES.md#alert-with-action — for action-bearing alert composition
|
|
53
|
+
*/
|
|
36
54
|
export const Alert = ({
|
|
37
55
|
title,
|
|
38
56
|
variant = 'info',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { ChevronDown } from '../../icons';
|
|
1
2
|
import { cn } from '../../utils';
|
|
2
|
-
import { Icon } from '../Icon';
|
|
3
3
|
import { Tooltip } from '../Tooltip';
|
|
4
4
|
|
|
5
5
|
type Props = {
|
|
@@ -8,11 +8,10 @@ type Props = {
|
|
|
8
8
|
|
|
9
9
|
export const AlertCollapsibleIcon = ({ open }: Props) => (
|
|
10
10
|
<Tooltip placement="top" tooltip={open ? 'Collapse alert' : 'Expand alert'} triggerAsChild>
|
|
11
|
-
<
|
|
12
|
-
icon="chevron-down"
|
|
11
|
+
<ChevronDown
|
|
13
12
|
className={cn(
|
|
14
13
|
'tw-mt-px',
|
|
15
|
-
'tw-size-
|
|
14
|
+
'tw-size-5',
|
|
16
15
|
'tw-text-neutral-400',
|
|
17
16
|
'group-data-[state=open]/alert:tw-rotate-180',
|
|
18
17
|
|
|
@@ -1,43 +1,40 @@
|
|
|
1
|
-
import type { ComponentProps,
|
|
1
|
+
import type { ComponentProps, ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
+
import { AlertTriangle, CircleCheck, Info } from '../../icons';
|
|
3
4
|
import { cn } from '../../utils';
|
|
4
|
-
import { Icon } from '../Icon';
|
|
5
5
|
|
|
6
6
|
import type { Variant } from './AlertRoot';
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
info:
|
|
10
|
-
warning:
|
|
11
|
-
error:
|
|
12
|
-
success:
|
|
8
|
+
const VARIANT_ICONS: Record<Variant, ReactNode> = {
|
|
9
|
+
info: <Info />,
|
|
10
|
+
warning: <AlertTriangle />,
|
|
11
|
+
error: <AlertTriangle />,
|
|
12
|
+
success: <CircleCheck />,
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
type Props = {
|
|
16
16
|
variant: Variant;
|
|
17
|
-
icon?:
|
|
18
|
-
} & Omit<ComponentProps<
|
|
17
|
+
icon?: ReactNode;
|
|
18
|
+
} & Omit<ComponentProps<'span'>, 'children'>;
|
|
19
19
|
|
|
20
|
-
export const AlertIcon = ({
|
|
21
|
-
|
|
22
|
-
variant,
|
|
23
|
-
icon,
|
|
24
|
-
className,
|
|
25
|
-
...rest
|
|
26
|
-
}: Props & { ref?: Ref<ElementRef<'svg'>> }) => (
|
|
27
|
-
<Icon
|
|
20
|
+
export const AlertIcon = ({ variant, icon, className, ...rest }: Props) => (
|
|
21
|
+
<span
|
|
28
22
|
{...rest}
|
|
29
|
-
ref={ref}
|
|
30
23
|
data-slot="alert-icon"
|
|
31
|
-
icon={icon ?? ICONS[variant]}
|
|
32
24
|
className={cn(
|
|
25
|
+
'tw-inline-flex',
|
|
26
|
+
'tw-items-center',
|
|
27
|
+
'tw-justify-center',
|
|
33
28
|
'tw-self-start',
|
|
34
29
|
'tw-p-0.5',
|
|
35
|
-
'tw-
|
|
30
|
+
'[&_svg]:tw-size-4',
|
|
36
31
|
'group-data-[variant=info]/alert:tw-text-secondary-500',
|
|
37
32
|
'group-data-[variant=warning]/alert:tw-text-warning-500',
|
|
38
33
|
'group-data-[variant=error]/alert:tw-text-error-500',
|
|
39
34
|
'group-data-[variant=success]/alert:tw-text-primary-500',
|
|
40
35
|
className,
|
|
41
36
|
)}
|
|
42
|
-
|
|
37
|
+
>
|
|
38
|
+
{icon ?? VARIANT_ICONS[variant]}
|
|
39
|
+
</span>
|
|
43
40
|
);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
2
|
|
|
3
|
+
import { Plus, X } from '@keboola/design/icons';
|
|
4
|
+
|
|
3
5
|
import { Icon } from '../Icon';
|
|
4
6
|
|
|
5
7
|
import { Badge } from './Badge';
|
|
@@ -80,21 +82,37 @@ export const AsButton: Story = {
|
|
|
80
82
|
|
|
81
83
|
export const StartAddon: Story = {
|
|
82
84
|
args: {
|
|
83
|
-
prefix:
|
|
85
|
+
prefix: (
|
|
86
|
+
<Badge.Button onClick={() => alert('Removed')} aria-label="Add">
|
|
87
|
+
<Plus className="tw-size-4" />
|
|
88
|
+
</Badge.Button>
|
|
89
|
+
),
|
|
84
90
|
},
|
|
85
91
|
};
|
|
86
92
|
|
|
87
93
|
export const EndAddon: Story = {
|
|
88
94
|
args: {
|
|
89
|
-
suffix:
|
|
95
|
+
suffix: (
|
|
96
|
+
<Badge.Button onClick={() => alert('Removed')} aria-label="Remove">
|
|
97
|
+
<X className="tw-size-4" />
|
|
98
|
+
</Badge.Button>
|
|
99
|
+
),
|
|
90
100
|
prefixVariant: 'floating',
|
|
91
101
|
},
|
|
92
102
|
};
|
|
93
103
|
|
|
94
104
|
export const BothAddons: Story = {
|
|
95
105
|
args: {
|
|
96
|
-
prefix:
|
|
97
|
-
|
|
106
|
+
prefix: (
|
|
107
|
+
<Badge.Button onClick={() => alert('Removed')} aria-label="Add">
|
|
108
|
+
<Plus className="tw-size-4" />
|
|
109
|
+
</Badge.Button>
|
|
110
|
+
),
|
|
111
|
+
suffix: (
|
|
112
|
+
<Badge.Button onClick={() => alert('Removed')} aria-label="Remove">
|
|
113
|
+
<X className="tw-size-4" />
|
|
114
|
+
</Badge.Button>
|
|
115
|
+
),
|
|
98
116
|
},
|
|
99
117
|
};
|
|
100
118
|
|
|
@@ -9,6 +9,11 @@ type AddonVariant = ComponentProps<typeof BadgeAddon>['variant'];
|
|
|
9
9
|
type BadgeRootProps = ComponentProps<typeof BadgeRoot>;
|
|
10
10
|
type BadgeLabelProps = ComponentProps<typeof BadgeLabel>;
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Props for `Badge`. `color` is a categorical contract (`green` = success, `red` = error, `yellow` = warning,
|
|
14
|
+
* `purple/cyan/teal/pink/blue/gray` for decorative tags) — colours stay fixed across brand themes.
|
|
15
|
+
* Pass `prefix` / `suffix` for icon-style addons.
|
|
16
|
+
*/
|
|
12
17
|
type Props = Pick<BadgeRootProps, 'variant' | 'color'> &
|
|
13
18
|
Omit<BadgeLabelProps, 'prefix'> & {
|
|
14
19
|
prefix?: ReactNode;
|
|
@@ -18,6 +23,17 @@ type Props = Pick<BadgeRootProps, 'variant' | 'color'> &
|
|
|
18
23
|
labelClassName?: string;
|
|
19
24
|
};
|
|
20
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Compact status / category label rendered as an inline pill.
|
|
28
|
+
*
|
|
29
|
+
* **When to use:** encoding status (active, deprecated, beta), counts, or decorative tags next to a title
|
|
30
|
+
* or in a table cell. The `color` prop is a semantic contract — `green` means success, `red` means error
|
|
31
|
+
* etc., and colours are intentionally stable across customer brand themes.
|
|
32
|
+
* **When NOT to use:** for an inline action use `Button` / `ButtonInline`; for a persistent contextual
|
|
33
|
+
* banner use `Alert`; for a counter-only chip prefer `Badge` with just a numeric label.
|
|
34
|
+
* **Composition:** drop into `PageHeader.Detail` `badges` slot, `Table.Cell`, or alongside titles in cards.
|
|
35
|
+
* Use `Badge.Button` for an interactive (clickable) badge variant.
|
|
36
|
+
*/
|
|
21
37
|
const BadgeInner = ({
|
|
22
38
|
ref,
|
|
23
39
|
// root props
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import type { ComponentProps, ElementRef, Ref } from 'react';
|
|
2
2
|
|
|
3
3
|
import { cn } from '../../utils';
|
|
4
|
-
import { Icon } from '../Icon';
|
|
5
4
|
|
|
6
|
-
type Props = ComponentProps<'button'
|
|
7
|
-
icon: ComponentProps<typeof Icon>['icon'];
|
|
8
|
-
};
|
|
5
|
+
type Props = ComponentProps<'button'>;
|
|
9
6
|
|
|
10
7
|
export const BadgeButton = ({
|
|
11
8
|
ref,
|
|
12
9
|
className,
|
|
13
|
-
icon,
|
|
14
10
|
type = 'button',
|
|
11
|
+
children,
|
|
15
12
|
...rest
|
|
16
13
|
}: Props & { ref?: Ref<ElementRef<'button'>> }) => (
|
|
17
14
|
<button
|
|
@@ -31,9 +28,13 @@ export const BadgeButton = ({
|
|
|
31
28
|
|
|
32
29
|
'hover:tw-brightness-95',
|
|
33
30
|
'tw-px-1',
|
|
31
|
+
// Default sizing for Lucide-icon children (which ship at 24×24); keeps
|
|
32
|
+
// the visual baseline aligned with the previous FA-icon API and other
|
|
33
|
+
// icon-wrapper components (ItemIcon, SidebarMenuIcon).
|
|
34
|
+
'[&_svg]:tw-size-3',
|
|
34
35
|
className,
|
|
35
36
|
)}
|
|
36
37
|
>
|
|
37
|
-
|
|
38
|
+
{children}
|
|
38
39
|
</button>
|
|
39
40
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ComponentProps, ElementRef, Ref } from 'react';
|
|
2
2
|
|
|
3
|
+
import { Ellipsis } from '../../icons';
|
|
3
4
|
import { cn } from '../../utils';
|
|
4
|
-
import { Icon } from '../Icon';
|
|
5
5
|
|
|
6
6
|
type Props = ComponentProps<'button'>;
|
|
7
7
|
|
|
@@ -37,7 +37,7 @@ export const BreadcrumbEllipsis = ({
|
|
|
37
37
|
)}
|
|
38
38
|
{...props}
|
|
39
39
|
>
|
|
40
|
-
<
|
|
40
|
+
<Ellipsis className="tw-size-4" />
|
|
41
41
|
<span className="tw-sr-only">More</span>
|
|
42
42
|
</button>
|
|
43
43
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ComponentProps } from 'react';
|
|
2
2
|
|
|
3
|
+
import { ChevronRight } from '../../icons';
|
|
3
4
|
import { cn } from '../../utils';
|
|
4
|
-
import { Icon } from '../Icon';
|
|
5
5
|
|
|
6
6
|
type Props = ComponentProps<'li'>;
|
|
7
7
|
|
|
@@ -23,6 +23,6 @@ export const BreadcrumbSeparator = ({ children, className, ...props }: Props) =>
|
|
|
23
23
|
)}
|
|
24
24
|
{...props}
|
|
25
25
|
>
|
|
26
|
-
{children ?? <
|
|
26
|
+
{children ?? <ChevronRight className="!tw-size-4" />}
|
|
27
27
|
</li>
|
|
28
28
|
);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ComponentProps } from 'react';
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
3
|
|
|
4
|
+
import { CircleCheck, X } from '../../icons';
|
|
5
|
+
|
|
4
6
|
import { Button } from './Button';
|
|
5
7
|
|
|
6
8
|
type Props = ComponentProps<typeof Button>;
|
|
@@ -43,11 +45,21 @@ export const Default: Story = {
|
|
|
43
45
|
};
|
|
44
46
|
|
|
45
47
|
export const WithIcon: Story = {
|
|
46
|
-
args: {
|
|
48
|
+
args: {
|
|
49
|
+
children: (
|
|
50
|
+
<>
|
|
51
|
+
<CircleCheck className="tw-size-4" /> Button
|
|
52
|
+
</>
|
|
53
|
+
),
|
|
54
|
+
},
|
|
47
55
|
};
|
|
48
56
|
|
|
49
57
|
export const IconOnly: Story = {
|
|
50
|
-
args: {
|
|
58
|
+
args: {
|
|
59
|
+
size: 'icon',
|
|
60
|
+
children: <X className="tw-size-4" />,
|
|
61
|
+
'aria-label': 'Delete',
|
|
62
|
+
},
|
|
51
63
|
argTypes: {
|
|
52
64
|
size: {
|
|
53
65
|
control: 'radio',
|
|
@@ -2,26 +2,57 @@ import type { ComponentProps, ElementRef, Ref } from 'react';
|
|
|
2
2
|
import { Root, Slottable } from '@radix-ui/react-slot';
|
|
3
3
|
import type { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
|
+
import { Loader2 } from '../../icons';
|
|
5
6
|
import { cn } from '../../utils';
|
|
6
7
|
import { Icon } from '../Icon';
|
|
7
8
|
|
|
8
9
|
import { argoVariants, variants } from './styles';
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Props for the `Button` component.
|
|
13
|
+
*
|
|
14
|
+
* Pass `variant` for primary/secondary/danger/outline/invisible looks, `size`
|
|
15
|
+
* for height (including `icon-*` sizes when rendering icon-only). Icons are
|
|
16
|
+
* composed as children — `<Button><Trash2 className="tw-size-4" /> Delete</Button>`.
|
|
17
|
+
* `argo` opts into the Argo redesign styling (PageHeader / sidebar contexts) —
|
|
18
|
+
* required when nested inside `PageHeader` or `PageLayout.Footer` slots.
|
|
19
|
+
*/
|
|
10
20
|
type Props = {
|
|
11
|
-
|
|
21
|
+
/** Replaces the leading content with a spinner and disables the button visually. The caller still controls `disabled`. */
|
|
12
22
|
isLoading?: boolean;
|
|
23
|
+
/** Render as a Radix `Slot` so the parent JSX (e.g. a `react-router` `<Link>`) becomes the button element. */
|
|
13
24
|
asChild?: boolean;
|
|
25
|
+
/** Opt into the Argo redesign variant. Required inside `PageHeader` slots and other Argo surfaces. */
|
|
14
26
|
argo?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* @deprecated FA-name icon prop kept as a shim during the kbc-ui consumer
|
|
29
|
+
* migration sweep (UT-4109 C2). New code should compose icons as children
|
|
30
|
+
* via direct Lucide imports — `<Button><Trash2 className="tw-size-4" /> Delete</Button>`.
|
|
31
|
+
* Removed in the final C2 cleanup once every consumer is migrated.
|
|
32
|
+
*/
|
|
33
|
+
icon?: ComponentProps<typeof Icon>['icon'];
|
|
15
34
|
} & ComponentProps<'button'> &
|
|
16
35
|
VariantProps<typeof variants>;
|
|
17
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Primary action surface for the design system — covers all clickable controls except inline text links.
|
|
39
|
+
*
|
|
40
|
+
* **When to use:** any user-initiated action (submit, save, open modal, run job). Use `size="icon-*"` with no
|
|
41
|
+
* children for an icon-only button; there is no separate `IconButton` component.
|
|
42
|
+
* **When NOT to use:** for inline text-link actions inside body copy use `ButtonInline`; for navigation that
|
|
43
|
+
* opens a URL use `Link`; for "primary action + dropdown of related actions" use `SplitButton`.
|
|
44
|
+
* **Composition:** wrap in `Tooltip` when icon-only or when disabled state needs an explanation; pair with
|
|
45
|
+
* `Popconfirm` for destructive confirmations; pass through `Modal.Trigger` / `DropdownMenu.Trigger` via `asChild`.
|
|
46
|
+
*
|
|
47
|
+
* @see RECIPES.md#disabled-button-tooltip — for the disabled-button + tooltip pattern
|
|
48
|
+
* @see RECIPES.md#confirm-dialog — for destructive-action confirmation
|
|
49
|
+
*/
|
|
18
50
|
export const Button = ({
|
|
19
51
|
ref,
|
|
20
52
|
children,
|
|
21
53
|
disabled = false,
|
|
22
54
|
className,
|
|
23
55
|
type = 'button',
|
|
24
|
-
icon: iconProp,
|
|
25
56
|
isLoading = false,
|
|
26
57
|
asChild = false,
|
|
27
58
|
onClick,
|
|
@@ -29,10 +60,10 @@ export const Button = ({
|
|
|
29
60
|
variant = 'primary',
|
|
30
61
|
size = 'medium',
|
|
31
62
|
block = false,
|
|
63
|
+
icon,
|
|
32
64
|
...buttonProps
|
|
33
65
|
}: Props & { ref?: Ref<ElementRef<'button'>> }) => {
|
|
34
66
|
const Comp = asChild ? Root : 'button';
|
|
35
|
-
const icon = isLoading ? 'spinner' : iconProp;
|
|
36
67
|
const classNames = argo
|
|
37
68
|
? argoVariants({ variant: variant, size, block, className })
|
|
38
69
|
: variants({
|
|
@@ -61,12 +92,18 @@ export const Button = ({
|
|
|
61
92
|
disabled={disabled}
|
|
62
93
|
aria-disabled={disabled}
|
|
63
94
|
data-variant={variant}
|
|
64
|
-
|
|
95
|
+
// `size="icon"` / `size="icon-*"` flags the button as icon-only — needed
|
|
96
|
+
// by `ButtonGroup space="block"` which stretches non-icon children to
|
|
97
|
+
// equal width. Detection by `size` works for both the legacy shim path
|
|
98
|
+
// (no children, FA icon prop) and shadcn composition (icon as the only
|
|
99
|
+
// child).
|
|
100
|
+
data-icon-only={String(size).startsWith('icon')}
|
|
65
101
|
type={type}
|
|
66
102
|
onClick={handleClick}
|
|
67
103
|
className={cn(classNames)}
|
|
68
104
|
>
|
|
69
|
-
{
|
|
105
|
+
{isLoading && <Loader2 className="tw-size-4 tw-shrink-0 tw-animate-spin" />}
|
|
106
|
+
{!isLoading && icon && <Icon icon={icon} />}
|
|
70
107
|
<Slottable>{children}</Slottable>
|
|
71
108
|
</Comp>
|
|
72
109
|
);
|
|
@@ -222,13 +222,11 @@ export const argoVariants = cva(
|
|
|
222
222
|
'focus-visible:tw-ring-primary/50',
|
|
223
223
|
],
|
|
224
224
|
secondary: [
|
|
225
|
-
'tw-bg-
|
|
226
|
-
'tw-text-
|
|
227
|
-
'hover:tw-bg-
|
|
228
|
-
'hover:tw-text-secondary-foreground',
|
|
229
|
-
'focus:tw-text-secondary-foreground',
|
|
225
|
+
'tw-bg-primary/10',
|
|
226
|
+
'tw-text-primary',
|
|
227
|
+
'hover:not-disabled:tw-bg-primary/20',
|
|
230
228
|
|
|
231
|
-
'focus-visible:tw-border-
|
|
229
|
+
'focus-visible:tw-border-primary',
|
|
232
230
|
'focus-visible:tw-ring-primary/50',
|
|
233
231
|
],
|
|
234
232
|
|