@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,9 +1,10 @@
|
|
|
1
|
-
import type { ComponentProps, ElementRef, PointerEventHandler, Ref } from 'react';
|
|
1
|
+
import type { ComponentProps, ElementRef, PointerEventHandler, ReactNode, Ref } from 'react';
|
|
2
2
|
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
3
3
|
import { Root, Slottable } from '@radix-ui/react-slot';
|
|
4
4
|
|
|
5
|
+
import { Loader2 } from '../../icons';
|
|
5
6
|
import { cn } from '../../utils';
|
|
6
|
-
import
|
|
7
|
+
import { renderLegacyIcon } from '../../utils/legacyIcon';
|
|
7
8
|
|
|
8
9
|
import { ItemIcon } from './ItemIcon';
|
|
9
10
|
import { itemVariants } from './styles';
|
|
@@ -15,7 +16,12 @@ type Props = ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
|
|
15
16
|
* https://github.com/radix-ui/primitives/issues/2193#issuecomment-2758461323
|
|
16
17
|
*/
|
|
17
18
|
asModalTrigger?: boolean;
|
|
18
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Pass a Lucide icon node as a leading adornment. Legacy FontAwesome
|
|
21
|
+
* `"name"` / `[prefix, name]` string values are still accepted during the
|
|
22
|
+
* C2 deprecation window and rendered via the legacy `<Icon>` wrapper.
|
|
23
|
+
*/
|
|
24
|
+
icon?: ReactNode;
|
|
19
25
|
isLoading?: boolean;
|
|
20
26
|
iconClassName?: string;
|
|
21
27
|
};
|
|
@@ -29,13 +35,13 @@ export const DropdownMenuItem = ({
|
|
|
29
35
|
onPointerMove,
|
|
30
36
|
children,
|
|
31
37
|
asChild,
|
|
32
|
-
icon
|
|
38
|
+
icon,
|
|
33
39
|
iconClassName,
|
|
34
40
|
isLoading = false,
|
|
35
41
|
...props
|
|
36
42
|
}: Props & { ref?: Ref<ElementRef<'div'>> }) => {
|
|
37
43
|
const Comp = asChild ? Root : 'div';
|
|
38
|
-
const
|
|
44
|
+
const adornment = isLoading ? <Loader2 className="tw-animate-spin" /> : renderLegacyIcon(icon);
|
|
39
45
|
|
|
40
46
|
const handleOnSelect = (event: Event) => {
|
|
41
47
|
// prevents dropdown menu from closing to prevent modal unmounting from dom
|
|
@@ -66,7 +72,7 @@ export const DropdownMenuItem = ({
|
|
|
66
72
|
asChild
|
|
67
73
|
>
|
|
68
74
|
<Comp>
|
|
69
|
-
{
|
|
75
|
+
{adornment && <ItemIcon className={iconClassName}>{adornment}</ItemIcon>}
|
|
70
76
|
<Slottable>{children}</Slottable>
|
|
71
77
|
</Comp>
|
|
72
78
|
</DropdownMenuPrimitive.Item>
|
|
@@ -3,7 +3,7 @@ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
|
3
3
|
import type { VariantProps } from 'class-variance-authority';
|
|
4
4
|
import { cva } from 'class-variance-authority';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { Check, Circle, ToggleRight } from '../../icons';
|
|
7
7
|
|
|
8
8
|
const variants = cva(
|
|
9
9
|
[
|
|
@@ -33,6 +33,9 @@ const variants = cva(
|
|
|
33
33
|
|
|
34
34
|
'[&_svg]:tw-size-2',
|
|
35
35
|
'[&_svg]:data-[state=unchecked]:tw-hidden',
|
|
36
|
+
// Lucide Circle is stroke-only — force fill to match the stroke
|
|
37
|
+
// (currentColor) so the indicator reads as a solid dot.
|
|
38
|
+
'[&_svg]:tw-fill-current',
|
|
36
39
|
'group-data-[variant=argo]/dropdown-menu-content:[&_svg]:tw-text-primary',
|
|
37
40
|
'group-data-[variant=default]/dropdown-menu-content:[&_svg]:tw-text-secondary-500',
|
|
38
41
|
],
|
|
@@ -53,6 +56,9 @@ const variants = cva(
|
|
|
53
56
|
],
|
|
54
57
|
switch: [
|
|
55
58
|
'tw-size-5',
|
|
59
|
+
// Lucide SVGs default to 24×24 — constrain the rendered ToggleRight
|
|
60
|
+
// to the indicator slot so it doesn't overflow.
|
|
61
|
+
'[&_svg]:tw-size-5',
|
|
56
62
|
'tw-text-neutral-400',
|
|
57
63
|
'data-[state=unchecked]:tw-rotate-180',
|
|
58
64
|
'group-data-[variant=default]/dropdown-menu-content:data-[state=checked]:tw-text-secondary-500',
|
|
@@ -72,10 +78,10 @@ export const DropdownMenuItemIndicator = ({
|
|
|
72
78
|
forceMount: forceMountProp,
|
|
73
79
|
...rest
|
|
74
80
|
}: Props) => {
|
|
75
|
-
const
|
|
76
|
-
if (variant === 'switch') return
|
|
77
|
-
if (variant === 'radio') return
|
|
78
|
-
return
|
|
81
|
+
const IconComp = (() => {
|
|
82
|
+
if (variant === 'switch') return ToggleRight;
|
|
83
|
+
if (variant === 'radio') return Circle;
|
|
84
|
+
return Check;
|
|
79
85
|
})();
|
|
80
86
|
|
|
81
87
|
const forceMount = (() => {
|
|
@@ -92,7 +98,7 @@ export const DropdownMenuItemIndicator = ({
|
|
|
92
98
|
forceMount={forceMount}
|
|
93
99
|
className={variants({ variant, className })}
|
|
94
100
|
>
|
|
95
|
-
<
|
|
101
|
+
<IconComp />
|
|
96
102
|
</DropdownMenuPrimitive.ItemIndicator>
|
|
97
103
|
);
|
|
98
104
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { ComponentProps } from 'react';
|
|
1
|
+
import type { ComponentProps, ReactNode } from 'react';
|
|
2
2
|
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
3
3
|
|
|
4
|
+
import { Loader2 } from '../../../icons';
|
|
4
5
|
import { cn } from '../../../utils';
|
|
5
|
-
import
|
|
6
|
+
import { renderLegacyIcon } from '../../../utils/legacyIcon';
|
|
6
7
|
import { DropdownMenuItemIndicator } from '../DropdownMenuItemIndicator';
|
|
7
8
|
import { ItemIcon } from '../ItemIcon';
|
|
8
9
|
import { itemVariants } from '../styles';
|
|
@@ -11,7 +12,7 @@ type Props = ComponentProps<typeof DropdownMenuPrimitive.RadioItem> & {
|
|
|
11
12
|
closeOnSelect?: boolean;
|
|
12
13
|
indicatorAlign?: 'start' | 'end';
|
|
13
14
|
indicator?: 'radio' | 'check' | 'none';
|
|
14
|
-
icon?:
|
|
15
|
+
icon?: ReactNode;
|
|
15
16
|
iconClassName?: string;
|
|
16
17
|
isLoading?: boolean;
|
|
17
18
|
};
|
|
@@ -30,7 +31,9 @@ export const DropdownMenuRadioItem = ({
|
|
|
30
31
|
}: Props) => {
|
|
31
32
|
const isAtStart = indicatorAlign === 'start';
|
|
32
33
|
const indicatorEle = isLoading ? (
|
|
33
|
-
<ItemIcon
|
|
34
|
+
<ItemIcon className={iconClassName}>
|
|
35
|
+
<Loader2 className="tw-animate-spin" />
|
|
36
|
+
</ItemIcon>
|
|
34
37
|
) : (
|
|
35
38
|
<DropdownMenuItemIndicator variant={indicator} />
|
|
36
39
|
);
|
|
@@ -56,7 +59,7 @@ export const DropdownMenuRadioItem = ({
|
|
|
56
59
|
{...props}
|
|
57
60
|
>
|
|
58
61
|
{isAtStart && indicatorEle}
|
|
59
|
-
{icon && <ItemIcon
|
|
62
|
+
{icon && <ItemIcon className={iconClassName}>{renderLegacyIcon(icon)}</ItemIcon>}
|
|
60
63
|
<span className="tw-flex tw-min-w-0 tw-flex-1 tw-items-center tw-gap-2">{children}</span>
|
|
61
64
|
{!isAtStart && indicatorEle}
|
|
62
65
|
</DropdownMenuPrimitive.RadioItem>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ComponentProps } from 'react';
|
|
2
2
|
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
3
3
|
|
|
4
|
+
import { ChevronRight } from '../../../icons';
|
|
4
5
|
import { cn } from '../../../utils';
|
|
5
|
-
import { Icon } from '../../Icon';
|
|
6
6
|
import { itemVariants } from '../styles';
|
|
7
7
|
|
|
8
8
|
type Props = ComponentProps<typeof DropdownMenuPrimitive.SubTrigger>;
|
|
@@ -11,7 +11,7 @@ export const DropdownMenuSubTrigger = ({ className, children, ...props }: Props)
|
|
|
11
11
|
return (
|
|
12
12
|
<DropdownMenuPrimitive.SubTrigger className={cn(itemVariants, className)} {...props}>
|
|
13
13
|
{children}
|
|
14
|
-
<
|
|
14
|
+
<ChevronRight className="tw-ml-auto tw-size-5" />
|
|
15
15
|
</DropdownMenuPrimitive.SubTrigger>
|
|
16
16
|
);
|
|
17
17
|
};
|
|
@@ -1,22 +1,30 @@
|
|
|
1
|
-
import type { ComponentProps } from 'react';
|
|
1
|
+
import type { ComponentProps, ReactNode } from 'react';
|
|
2
2
|
|
|
3
3
|
import { cn } from '../../utils';
|
|
4
|
-
import { Icon } from '../Icon';
|
|
5
4
|
|
|
6
|
-
type Props = ComponentProps<
|
|
5
|
+
type Props = ComponentProps<'span'> & {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
};
|
|
7
8
|
|
|
8
|
-
export const ItemIcon = ({ className, ...props }: Props) => (
|
|
9
|
-
<
|
|
9
|
+
export const ItemIcon = ({ className, children, ...props }: Props) => (
|
|
10
|
+
<span
|
|
10
11
|
{...props}
|
|
11
12
|
className={cn(
|
|
13
|
+
'tw-inline-flex',
|
|
14
|
+
'tw-items-center',
|
|
15
|
+
'tw-justify-center',
|
|
12
16
|
'tw-aspect-square',
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
// 20px container fits the 16px Lucide glyph plus 2px breathing room
|
|
18
|
+
// each side — the previous tw-size-4 + tw-p-0.5 clipped the 16px child.
|
|
19
|
+
'tw-size-5',
|
|
20
|
+
'[&_svg]:tw-size-4',
|
|
15
21
|
'group-data-[variant=default]/dropdown-menu-content:tw-text-neutral-400',
|
|
16
22
|
'group-data-[variant=argo]/dropdown-menu-content:tw-text-primary',
|
|
17
23
|
'group-data-[variant=argo]/dropdown-menu-content:group-aria-disabled:tw-text-muted-foreground',
|
|
18
24
|
'group-data-[variant=default]/dropdown-menu-content:group-hover:tw-text-secondary-500',
|
|
19
25
|
className,
|
|
20
26
|
)}
|
|
21
|
-
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
</span>
|
|
22
30
|
);
|
|
@@ -10,12 +10,27 @@ import { FormGroupTextarea } from './FormGroupTextarea';
|
|
|
10
10
|
import { FormGroupTextInput } from './FormGroupTextInput';
|
|
11
11
|
import type { FormGroupContext } from './types';
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Props for `FormGroup`. `state` from `FormGroupContext` ("default" | "error") propagates to nested inputs
|
|
15
|
+
* and help text via context.
|
|
16
|
+
*/
|
|
13
17
|
type FormGroupProps = {
|
|
14
18
|
children: ReactNode;
|
|
15
19
|
className?: string;
|
|
16
20
|
testId?: string;
|
|
17
21
|
} & FormGroupContext;
|
|
18
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Label + input + help-text grouping with shared validation state. Provides the chrome for a single field.
|
|
25
|
+
*
|
|
26
|
+
* **When to use:** rendering a label + bare input + help/error text outside `useAppForm` (e.g. one-off
|
|
27
|
+
* filter fields, settings panes). The `state="error"` flips both `FormGroup.TextInput` and `FormGroup.Help`
|
|
28
|
+
* to error styling without per-component wiring.
|
|
29
|
+
* **When NOT to use:** for a full form with validation and submit state, use `useAppForm` from `blocks/Form` —
|
|
30
|
+
* it composes `FormGroup` internally via the typed field connectors.
|
|
31
|
+
* **Composition:** nest a `Label`, then one of `FormGroup.TextInput` / `FormGroup.NumberInput` /
|
|
32
|
+
* `FormGroup.ColorInput` / `FormGroup.Textarea`, optionally followed by `FormGroup.Help`.
|
|
33
|
+
*/
|
|
19
34
|
export const FormGroup = ({ className, children, state, testId }: FormGroupProps) => {
|
|
20
35
|
return (
|
|
21
36
|
<FormGroupProvider value={{ state }}>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import type { HTMLAttributes, PropsWithChildren, ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
+
import { ChevronLeft } from '../../icons';
|
|
4
5
|
import { cn } from '../../utils';
|
|
5
6
|
import { Button } from '../Button';
|
|
6
7
|
import { Truncated } from '../Truncated';
|
|
@@ -48,7 +49,9 @@ export const FullScreenModalHeader = ({
|
|
|
48
49
|
>
|
|
49
50
|
<div className={cn('tw-flex tw-flex-1 tw-items-center', titleWrapperClassName)}>
|
|
50
51
|
{previousTitle && onClose && (
|
|
51
|
-
<Button
|
|
52
|
+
<Button onClick={onClose} size="icon" className="tw-mr-6">
|
|
53
|
+
<ChevronLeft className="tw-size-4" />
|
|
54
|
+
</Button>
|
|
52
55
|
)}
|
|
53
56
|
|
|
54
57
|
<div className="tw-w-full">
|
|
@@ -2,6 +2,7 @@ import { useState } from 'react';
|
|
|
2
2
|
import type { ChangeEventHandler, ComponentProps, ElementRef, FocusEventHandler, Ref } from 'react';
|
|
3
3
|
import type { Matcher } from 'react-day-picker';
|
|
4
4
|
|
|
5
|
+
import { Calendar as CalendarIcon } from '../../../icons';
|
|
5
6
|
import { Button } from '../../Button';
|
|
6
7
|
import { DatePicker } from '../../DatePicker';
|
|
7
8
|
import { Input } from '../Input';
|
|
@@ -130,7 +131,9 @@ export const DateInput = ({
|
|
|
130
131
|
selected={currentValue ?? undefined}
|
|
131
132
|
startMonth={startMonth}
|
|
132
133
|
>
|
|
133
|
-
<Button aria-label="Select Date"
|
|
134
|
+
<Button aria-label="Select Date" variant="inline" size="icon-3xs">
|
|
135
|
+
<CalendarIcon className="tw-size-4" />
|
|
136
|
+
</Button>
|
|
134
137
|
</DatePicker>
|
|
135
138
|
) : undefined
|
|
136
139
|
}
|
|
@@ -2,6 +2,7 @@ import { useState } from 'react';
|
|
|
2
2
|
import type { ChangeEventHandler, ComponentProps, ElementRef, FocusEventHandler, Ref } from 'react';
|
|
3
3
|
import type { DateRange } from 'react-day-picker';
|
|
4
4
|
|
|
5
|
+
import { Calendar as CalendarIcon } from '../../../icons';
|
|
5
6
|
import { Button } from '../../Button';
|
|
6
7
|
import { DatePicker } from '../../DatePicker';
|
|
7
8
|
import { Input } from '../Input';
|
|
@@ -126,12 +127,9 @@ export const DateRangeInput = ({
|
|
|
126
127
|
startMonth={startMonth}
|
|
127
128
|
selected={currentValue ?? undefined}
|
|
128
129
|
>
|
|
129
|
-
<Button
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
variant="inline"
|
|
133
|
-
size="icon-3xs"
|
|
134
|
-
/>
|
|
130
|
+
<Button aria-label="Select Date Range" variant="inline" size="icon-3xs">
|
|
131
|
+
<CalendarIcon className="tw-size-4" />
|
|
132
|
+
</Button>
|
|
135
133
|
</DatePicker>
|
|
136
134
|
) : undefined
|
|
137
135
|
}
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
Ref,
|
|
10
10
|
} from 'react';
|
|
11
11
|
|
|
12
|
+
import { X } from '../../icons';
|
|
12
13
|
import { cn, createChangeInputEvent, mergeRefs } from '../../utils';
|
|
13
14
|
import { InputGroup } from '../InputGroup/InputGroup';
|
|
14
15
|
|
|
@@ -31,10 +32,23 @@ type OwnProps = {
|
|
|
31
32
|
defaultValue?: ComponentProps<'input'>['defaultValue'] | null;
|
|
32
33
|
};
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Props for `Input`. Combines `InputGroup` chrome (variant / sizing / state / focusVariant) with
|
|
37
|
+
* input-specific slots (`prefix`, `leftSuffix`, `rightSuffix`, `allowClear`).
|
|
38
|
+
*/
|
|
34
39
|
type Props = InputGroupProps &
|
|
35
40
|
OwnProps &
|
|
36
41
|
Omit<ComponentProps<'input'>, 'prefix' | 'value' | 'defaultValue'>;
|
|
37
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Low-level text input with `InputGroup` chrome — addons, clear button, sizing, focus variants.
|
|
45
|
+
*
|
|
46
|
+
* **When to use:** when you need an input with prefix / suffix slots or a clear button and don't need the
|
|
47
|
+
* `onValueChange` convenience callback. Rare on its own — most call sites should use `TextInput`.
|
|
48
|
+
* **When NOT to use:** for plain text fields use `TextInput`; for fields inside a `FormGroup` use
|
|
49
|
+
* `FormGroup.TextInput`; for search use `SearchInput`; for forms with validation use `useAppForm`.
|
|
50
|
+
* **Composition:** wrap in `FormGroup` for shared error state, or render directly as a controlled input.
|
|
51
|
+
*/
|
|
38
52
|
export const Input = ({
|
|
39
53
|
ref: forwardedRef,
|
|
40
54
|
state = 'default',
|
|
@@ -124,11 +138,13 @@ export const Input = ({
|
|
|
124
138
|
{leftSuffix}
|
|
125
139
|
{allowClear && isClearButtonVisible && (
|
|
126
140
|
<InputGroup.Button
|
|
127
|
-
|
|
141
|
+
aria-label="Clear"
|
|
128
142
|
onMouseDown={(e) => e.preventDefault()}
|
|
129
143
|
onClick={handleClear}
|
|
130
144
|
data-testid={testId.clearButton}
|
|
131
|
-
|
|
145
|
+
>
|
|
146
|
+
<X className="tw-size-4" />
|
|
147
|
+
</InputGroup.Button>
|
|
132
148
|
)}
|
|
133
149
|
{rightSuffix && (
|
|
134
150
|
<div
|
|
@@ -10,11 +10,17 @@ type NativeProps = Omit<
|
|
|
10
10
|
type OwnProps = {
|
|
11
11
|
value?: string | null;
|
|
12
12
|
defaultValue?: string | null;
|
|
13
|
+
/** Called with the unwrapped string value on every change. For the raw event use `onChange`. */
|
|
13
14
|
onValueChange?: (value: string) => void;
|
|
15
|
+
/** Called with the unwrapped string value on blur. For the raw event use `onBlur`. */
|
|
14
16
|
onValueBlur?: (value: string) => void;
|
|
15
17
|
type?: 'text' | 'email' | 'password' | 'datetime-local' | 'date';
|
|
16
18
|
};
|
|
17
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Props for `TextInput`. Extends `Input` with string-typed `value` / `defaultValue` and convenience
|
|
22
|
+
* `onValueChange` / `onValueBlur` callbacks. `null` / `undefined` are coerced to empty strings.
|
|
23
|
+
*/
|
|
18
24
|
export type Props = OwnProps & NativeProps;
|
|
19
25
|
|
|
20
26
|
const sanitizeTextInputValue = (value: string | null | undefined) => {
|
|
@@ -22,6 +28,16 @@ const sanitizeTextInputValue = (value: string | null | undefined) => {
|
|
|
22
28
|
return value;
|
|
23
29
|
};
|
|
24
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Standard text input with a string-only API. The default choice for a bare text input.
|
|
33
|
+
*
|
|
34
|
+
* **When to use:** any plain text / email / password / date-input field outside `useAppForm`. Pair with
|
|
35
|
+
* `onValueChange` instead of unwrapping `event.target.value` from `onChange`.
|
|
36
|
+
* **When NOT to use:** for fields inside a labelled group use `FormGroup.TextInput`; for forms with
|
|
37
|
+
* validation use `useAppForm` + `field.TextField`; for search use `SearchInput`.
|
|
38
|
+
* **Composition:** wrap in a `FormGroup` for shared error / help-text state, or render directly inside
|
|
39
|
+
* page layouts. In kbc-ui forms prefer `variant="secondary"` (gray background).
|
|
40
|
+
*/
|
|
25
41
|
export const TextInput = ({
|
|
26
42
|
ref,
|
|
27
43
|
value,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import type { ChangeEventHandler, ComponentProps, ElementRef, FocusEventHandler, Ref } from 'react';
|
|
3
3
|
|
|
4
|
+
import { Clock } from '../../../icons';
|
|
4
5
|
import { cn } from '../../../utils';
|
|
5
6
|
import { Button } from '../../Button';
|
|
6
7
|
import { TimePicker } from '../../TimePicker';
|
|
@@ -94,7 +95,9 @@ export const TimeInput = ({
|
|
|
94
95
|
onSelect={handleTimePickerSelect}
|
|
95
96
|
selected={currentValue}
|
|
96
97
|
>
|
|
97
|
-
<Button aria-label="Select Time"
|
|
98
|
+
<Button aria-label="Select Time" variant="inline" size="icon-3xs">
|
|
99
|
+
<Clock className="tw-size-4" />
|
|
100
|
+
</Button>
|
|
98
101
|
</TimePicker>
|
|
99
102
|
) : undefined
|
|
100
103
|
}
|
|
@@ -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 { Upload, X } from '@keboola/design/icons';
|
|
5
|
+
|
|
4
6
|
import { Button } from '../Button/Button';
|
|
5
7
|
import { Icon } from '../Icon';
|
|
6
8
|
|
|
@@ -69,7 +71,9 @@ export const WithButton: Story = {
|
|
|
69
71
|
<InputGroup {...args}>
|
|
70
72
|
<InputGroup.Input placeholder={placeholder} disabled={disabled} readOnly={readOnly} />
|
|
71
73
|
<InputGroup.Addon align="inline-end">
|
|
72
|
-
<InputGroup.Button
|
|
74
|
+
<InputGroup.Button aria-label="Clear">
|
|
75
|
+
<X className="tw-size-4" />
|
|
76
|
+
</InputGroup.Button>
|
|
73
77
|
</InputGroup.Addon>
|
|
74
78
|
</InputGroup>
|
|
75
79
|
),
|
|
@@ -139,7 +143,9 @@ export const BlockStartAddon: Story = {
|
|
|
139
143
|
<Icon icon="copy" />
|
|
140
144
|
script.js
|
|
141
145
|
</InputGroup.Text>
|
|
142
|
-
<InputGroup.Button
|
|
146
|
+
<InputGroup.Button aria-label="Send">
|
|
147
|
+
<Upload className="tw-size-4" />
|
|
148
|
+
</InputGroup.Button>
|
|
143
149
|
</InputGroup.Addon>
|
|
144
150
|
|
|
145
151
|
<InputGroup.TextArea placeholder={placeholder} disabled={disabled} readOnly={readOnly} />
|
|
@@ -187,7 +193,9 @@ export const BothBlockAddon: Story = {
|
|
|
187
193
|
<Icon icon="copy" />
|
|
188
194
|
script.js
|
|
189
195
|
</InputGroup.Text>
|
|
190
|
-
<InputGroup.Button
|
|
196
|
+
<InputGroup.Button aria-label="Send">
|
|
197
|
+
<Upload className="tw-size-4" />
|
|
198
|
+
</InputGroup.Button>
|
|
191
199
|
</InputGroup.Addon>
|
|
192
200
|
|
|
193
201
|
<InputGroup.TextArea
|
|
@@ -98,7 +98,6 @@ const ControlledDemo = () => {
|
|
|
98
98
|
onChange={setValue}
|
|
99
99
|
placeholder="Type something…"
|
|
100
100
|
onSave={() => alert('Saved!')}
|
|
101
|
-
onCancel={() => alert('Cancelled!')}
|
|
102
101
|
onImageUpload={mockImageUpload}
|
|
103
102
|
showToolbar
|
|
104
103
|
autoFocus
|
|
@@ -118,3 +117,66 @@ const ControlledDemo = () => {
|
|
|
118
117
|
export const Controlled: Story = {
|
|
119
118
|
render: ControlledDemo,
|
|
120
119
|
};
|
|
120
|
+
|
|
121
|
+
const InlineDemo = () => {
|
|
122
|
+
// Two-state pattern (per the inline-editable-text pattern spec):
|
|
123
|
+
// `committed` is what's persisted; `draft` tracks in-progress edits.
|
|
124
|
+
// onSave commits draft → committed. onCancel (Esc) reverts draft ← committed.
|
|
125
|
+
const [committed, setCommitted] = useState(
|
|
126
|
+
'A multi-line description that overflows two lines so you can see the bottom fade in display state. Click to edit — it expands to full height. Press Esc to revert.',
|
|
127
|
+
);
|
|
128
|
+
const [draft, setDraft] = useState(committed);
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<div className="tw-max-w-prose tw-rounded tw-border tw-border-border tw-bg-background tw-p-6">
|
|
132
|
+
<p className="tw-mb-2 tw-text-sm tw-text-muted-foreground">
|
|
133
|
+
Click to edit. Blur or Cmd+Enter commits; Esc reverts. Committed:{' '}
|
|
134
|
+
<code className="tw-text-xs">{committed}</code>
|
|
135
|
+
</p>
|
|
136
|
+
<MarkdownEditor
|
|
137
|
+
compact
|
|
138
|
+
saveOnBlur
|
|
139
|
+
clampDisplay
|
|
140
|
+
value={draft}
|
|
141
|
+
onChange={setDraft}
|
|
142
|
+
onSave={() => setCommitted(draft)}
|
|
143
|
+
onCancel={() => setDraft(committed)}
|
|
144
|
+
placeholder="Add a description…"
|
|
145
|
+
className="tw-text-sm tw-leading-5 tw-text-foreground"
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
148
|
+
);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export const Inline: Story = {
|
|
152
|
+
render: InlineDemo,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const InlineTitleDemo = () => {
|
|
156
|
+
const [committed, setCommitted] = useState('Untitled page');
|
|
157
|
+
const [draft, setDraft] = useState(committed);
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<div className="tw-max-w-prose tw-rounded tw-border tw-border-border tw-bg-background tw-p-6">
|
|
161
|
+
<p className="tw-mb-2 tw-text-sm tw-text-muted-foreground">
|
|
162
|
+
Click the heading. Enter, blur, or Cmd+Enter commits; Esc reverts. Committed:{' '}
|
|
163
|
+
<code className="tw-text-xs">{committed}</code>
|
|
164
|
+
</p>
|
|
165
|
+
<MarkdownEditor
|
|
166
|
+
compact
|
|
167
|
+
singleLine
|
|
168
|
+
saveOnBlur
|
|
169
|
+
value={draft}
|
|
170
|
+
onChange={setDraft}
|
|
171
|
+
onSave={() => setCommitted(draft)}
|
|
172
|
+
onCancel={() => setDraft(committed)}
|
|
173
|
+
placeholder="Untitled"
|
|
174
|
+
className="tw-break-words tw-text-3xl tw-font-semibold tw-leading-9 tw-text-foreground"
|
|
175
|
+
/>
|
|
176
|
+
</div>
|
|
177
|
+
);
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export const InlineTitle: Story = {
|
|
181
|
+
render: InlineTitleDemo,
|
|
182
|
+
};
|