@keboola/design 1.2.1 → 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.
Files changed (177) hide show
  1. package/README.md +25 -0
  2. package/dist/icon-names.cjs +18 -0
  3. package/dist/icon-names.cjs.map +1 -0
  4. package/dist/icon-names.js +16 -0
  5. package/dist/icon-names.js.map +1 -0
  6. package/dist/icons.cjs +731 -0
  7. package/dist/icons.cjs.map +1 -0
  8. package/dist/icons.js +29 -0
  9. package/dist/icons.js.map +1 -0
  10. package/dist/index.cjs +14728 -0
  11. package/dist/index.cjs.map +1 -0
  12. package/dist/index.js +14553 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/markdown-editor.cjs +4440 -0
  15. package/dist/markdown-editor.cjs.map +1 -0
  16. package/dist/markdown-editor.js +4413 -0
  17. package/dist/markdown-editor.js.map +1 -0
  18. package/dist/mermaid.cjs +1011 -0
  19. package/dist/mermaid.cjs.map +1 -0
  20. package/dist/mermaid.js +1005 -0
  21. package/dist/mermaid.js.map +1 -0
  22. package/dist/styles.css +1 -0
  23. package/package.json +10 -3
  24. package/src/blocks/AIKit/CollapsibleStepHeader.tsx +7 -8
  25. package/src/blocks/AIKit/InlineStepLabel.tsx +7 -7
  26. package/src/blocks/AIKit/MessageBubble.stories.tsx +11 -3
  27. package/src/blocks/AIKit/MessageBubble.tsx +1 -1
  28. package/src/blocks/AIKit/PlanApproval.tsx +9 -8
  29. package/src/blocks/AIKit/PlanRewritingIndicator.tsx +2 -2
  30. package/src/blocks/AIKit/PromptInput.stories.tsx +10 -2
  31. package/src/blocks/AIKit/PromptInputSubmit.tsx +12 -8
  32. package/src/blocks/AIKit/Question.tsx +8 -9
  33. package/src/blocks/AIKit/SuggestedPrompt.tsx +12 -5
  34. package/src/blocks/AIKit/ThinkingIndicator.tsx +2 -2
  35. package/src/blocks/AutoSaveTextarea/AutoSaveTextarea.tsx +4 -4
  36. package/src/blocks/ClipboardButton/ClipboardButton.tsx +7 -5
  37. package/src/blocks/ClipboardButton/ClipboardInline.tsx +2 -3
  38. package/src/blocks/ColorInput/ColorInput.tsx +4 -2
  39. package/src/blocks/ColorInput/ColorPicker.tsx +5 -2
  40. package/src/blocks/EmptyState/EmptyState.tsx +16 -0
  41. package/src/blocks/ErrorState/ErrorState.tsx +15 -0
  42. package/src/blocks/Form/formHook.ts +15 -0
  43. package/src/blocks/MultiActionButtonGroup/MultiActionButton.stories.tsx +12 -11
  44. package/src/blocks/MultiActionButtonGroup/MultiActionButton.tsx +4 -4
  45. package/src/blocks/MultiCombobox/MultiCombobox.tsx +28 -12
  46. package/src/blocks/PageHeader/AGENTS.md +63 -3
  47. package/src/blocks/PageHeader/PageHeader.stories.tsx +124 -28
  48. package/src/blocks/PageHeader/PageHeader.tsx +15 -0
  49. package/src/blocks/PageHeader/PageHeaderDetail.tsx +23 -1
  50. package/src/blocks/PageHeader/PageHeaderIndex.tsx +20 -1
  51. package/src/blocks/PageLayout/PageLayout.tsx +26 -0
  52. package/src/blocks/PageToolbar/PageToolbar.stories.tsx +348 -0
  53. package/src/blocks/PageToolbar/PageToolbar.tsx +119 -0
  54. package/src/blocks/PageToolbar/index.ts +1 -0
  55. package/src/blocks/PasswordInput/PasswordInput.tsx +10 -10
  56. package/src/blocks/SearchInput/SearchInput.tsx +30 -4
  57. package/src/blocks/SearchInputWithNavigation/SearchInputWithNavigation.tsx +9 -6
  58. package/src/blocks/SplitButton/SplitButton.stories.tsx +7 -4
  59. package/src/blocks/SplitButton/SplitButton.tsx +17 -4
  60. package/src/blocks/Tagger/TagItem.tsx +4 -1
  61. package/src/blocks/Tagger/TagSelectPopover/TagSelectCreateItem.tsx +3 -1
  62. package/src/blocks/Tagger/TagSelectPopover/TagSelectList.tsx +2 -2
  63. package/src/blocks/Tagger/Tagger.tsx +2 -2
  64. package/src/components/Alert/Alert.stories.tsx +11 -3
  65. package/src/components/Alert/Alert.tsx +19 -1
  66. package/src/components/Alert/AlertCollapsibleIcon.tsx +3 -4
  67. package/src/components/Alert/AlertIcon.tsx +18 -21
  68. package/src/components/Badge/Badge.stories.tsx +22 -4
  69. package/src/components/Badge/Badge.tsx +16 -0
  70. package/src/components/Badge/BadgeButton.tsx +7 -6
  71. package/src/components/Breadcrumb/BreadcrumbEllipsis.tsx +2 -2
  72. package/src/components/Breadcrumb/BreadcrumbSeparator.tsx +2 -2
  73. package/src/components/Button/Button.stories.tsx +14 -2
  74. package/src/components/Button/Button.tsx +42 -5
  75. package/src/components/Button/styles.ts +4 -6
  76. package/src/components/ButtonGroup/ButtonGroup.stories.tsx +32 -10
  77. package/src/components/Card/Card.tsx +17 -0
  78. package/src/components/Checkbox/Checkbox.tsx +3 -6
  79. package/src/components/CodeEditor/highlighting.ts +1 -1
  80. package/src/components/CollapsiblePanel.tsx +2 -5
  81. package/src/components/Combobox/Combobox.tsx +12 -14
  82. package/src/components/Combobox/ComboboxChips.tsx +6 -1
  83. package/src/components/Combobox/ComboboxItem.tsx +3 -4
  84. package/src/components/Combobox/ComboboxItemIndicator.tsx +6 -4
  85. package/src/components/Combobox/types.ts +2 -6
  86. package/src/components/Command/Command.stories.tsx +47 -56
  87. package/src/components/Command/CommandItem/CheckIndicator.tsx +26 -14
  88. package/src/components/Command/CommandItem/CommandItem.tsx +6 -3
  89. package/src/components/DropdownMenu/DropdownMenu.stories.tsx +8 -4
  90. package/src/components/DropdownMenu/DropdownMenuCheckboxItem.tsx +4 -1
  91. package/src/components/DropdownMenu/DropdownMenuItem.tsx +12 -6
  92. package/src/components/DropdownMenu/DropdownMenuItemIndicator.tsx +12 -6
  93. package/src/components/DropdownMenu/DropdownMenuRadioGroup/DropdownMenuRadioItem.tsx +8 -5
  94. package/src/components/DropdownMenu/DropdownMenuSub/DropdownMenuSubTrigger.tsx +2 -2
  95. package/src/components/DropdownMenu/ItemIcon.tsx +16 -8
  96. package/src/components/FormGroup/FormGroup.tsx +15 -0
  97. package/src/components/FullScreenModal/FullScreenModalHeader.tsx +4 -1
  98. package/src/components/Input/DateInput/DateInput.tsx +4 -1
  99. package/src/components/Input/DateRangeInput/DateRangeInput.tsx +4 -6
  100. package/src/components/Input/Input.tsx +18 -2
  101. package/src/components/Input/TextInput/TextInput.tsx +16 -0
  102. package/src/components/Input/TimeInput/TimeInput.tsx +4 -1
  103. package/src/components/InputGroup/InputGroup.stories.tsx +11 -3
  104. package/src/components/MarkdownEditor/MarkdownEditor.stories.tsx +63 -1
  105. package/src/components/MarkdownEditor/MarkdownEditor.tsx +105 -15
  106. package/src/components/MarkdownEditor/nodes/ImageNode.tsx +10 -10
  107. package/src/components/MarkdownEditor/nodes/MermaidNode.tsx +8 -8
  108. package/src/components/MarkdownEditor/plugins/KeyboardPlugin.tsx +47 -3
  109. package/src/components/MarkdownEditor/plugins/MarkdownSyncPlugin.tsx +12 -3
  110. package/src/components/MarkdownEditor/plugins/ToolbarPlugin.tsx +20 -10
  111. package/src/components/MarkdownEditor/plugins/toolbar/AlignmentDropdown.tsx +20 -14
  112. package/src/components/MarkdownEditor/plugins/toolbar/BlockTypeDropdown.tsx +25 -16
  113. package/src/components/MarkdownEditor/plugins/toolbar/LinkPanel.tsx +3 -3
  114. package/src/components/MarkdownEditor/transformers.ts +10 -0
  115. package/src/components/MergeEditor/AcceptControls/AcceptButton.tsx +9 -4
  116. package/src/components/MergeEditor/AcceptControls/AcceptControls.tsx +25 -14
  117. package/src/components/MergeEditor/ConflictStatus/ConflictStatusIndicator.tsx +2 -2
  118. package/src/components/MergeEditor/MergeEditor.stories.tsx +15 -0
  119. package/src/components/MergeEditor/MergeEditor.tsx +9 -0
  120. package/src/components/MergeEditor/MergePane.tsx +22 -4
  121. package/src/components/MergeEditor/decorations.ts +2 -2
  122. package/src/components/Modal/Modal.stories.tsx +6 -4
  123. package/src/components/Modal/Modal.tsx +26 -0
  124. package/src/components/Modal/ModalBody.tsx +5 -0
  125. package/src/components/Modal/ModalCloseIcon.tsx +2 -3
  126. package/src/components/Modal/ModalContent.tsx +8 -0
  127. package/src/components/Modal/ModalFooter.tsx +5 -0
  128. package/src/components/Modal/ModalHeader.tsx +11 -0
  129. package/src/components/Modal/ModalIcon.tsx +45 -19
  130. package/src/components/Navbar/Navbar.stories.tsx +26 -13
  131. package/src/components/Navbar/NavbarMenuButton.tsx +11 -10
  132. package/src/components/Pagination/PaginationEllipsis.tsx +2 -2
  133. package/src/components/Pagination/PaginationNext.tsx +2 -1
  134. package/src/components/Pagination/PaginationPrevious.tsx +4 -1
  135. package/src/components/PanelWithDetails.tsx +3 -4
  136. package/src/components/Popconfirm/Popconfirm.stories.tsx +13 -5
  137. package/src/components/RadioGroup/Radio.tsx +6 -12
  138. package/src/components/Search/ClearButton.tsx +2 -2
  139. package/src/components/Search/Search.tsx +3 -3
  140. package/src/components/Select/Select.tsx +16 -5
  141. package/src/components/Select/SelectContent.tsx +13 -4
  142. package/src/components/Select/SelectItem.tsx +6 -5
  143. package/src/components/Select/SelectTrigger.tsx +2 -2
  144. package/src/components/Sidebar/Sidebar.stories.tsx +55 -19
  145. package/src/components/Sidebar/SidebarGroup/SidebarGroupAction.tsx +8 -4
  146. package/src/components/Sidebar/SidebarMenu/SidebarMenuButton.tsx +4 -2
  147. package/src/components/Sidebar/SidebarMenu/SidebarMenuCard.tsx +3 -2
  148. package/src/components/Sidebar/SidebarMenu/SidebarMenuIcon.tsx +12 -12
  149. package/src/components/Sidebar/SidebarMenu/SidebarMenuSub.tsx +3 -7
  150. package/src/components/Sidebar/SidebarMenu/SidebarSearchInput.tsx +3 -5
  151. package/src/components/Skeleton/Skeleton.stories.tsx +52 -0
  152. package/src/components/Skeleton/Skeleton.tsx +24 -1
  153. package/src/components/Table/Table.stories.tsx +223 -1
  154. package/src/components/Table/Table.tsx +17 -0
  155. package/src/components/Tabs.tsx +25 -2
  156. package/src/components/Toast/ToastDismiss.tsx +3 -3
  157. package/src/components/Toast/ToastRoot.tsx +24 -19
  158. package/src/components/Toggle/Toggle.tsx +11 -6
  159. package/src/components/Tooltip/Tooltip.tsx +23 -0
  160. package/src/components/ZoomableSvg/ZoomableSvg.tsx +8 -6
  161. package/src/icons/Keboola.tsx +28 -0
  162. package/src/icons/icons.stories.tsx +174 -0
  163. package/src/icons/index.ts +180 -0
  164. package/src/index.ts +1 -0
  165. package/src/patterns/AlertWithAction.stories.tsx +30 -0
  166. package/src/patterns/CardWithEmptyState.stories.tsx +66 -0
  167. package/src/patterns/ConfirmDialog.stories.tsx +72 -0
  168. package/src/patterns/CreatableMultiCombobox.stories.tsx +33 -0
  169. package/src/patterns/DataCardWithSkeleton.stories.tsx +48 -0
  170. package/src/patterns/DisabledButtonTooltip.stories.tsx +30 -0
  171. package/src/patterns/FilterToolbar.stories.tsx +71 -0
  172. package/src/patterns/InfoTooltip.stories.tsx +56 -0
  173. package/src/patterns/ListDetail.stories.tsx +65 -0
  174. package/src/patterns/ModalForm.stories.tsx +59 -0
  175. package/src/patterns/PageHeaderTabs.stories.tsx +48 -0
  176. package/src/styles.css +16 -8
  177. package/src/utils/legacyIcon.tsx +28 -0
@@ -0,0 +1,71 @@
1
+ import { useMemo, useState } from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react-vite';
3
+
4
+ import { Combobox, PageLayout, SearchInput } from '@keboola/design';
5
+
6
+ type Row = { id: string; name: string; type: 'table' | 'file' };
7
+
8
+ const FilteredList = ({ rows }: { rows: Row[] }) => {
9
+ const [query, setQuery] = useState('');
10
+ const [type, setType] = useState<Row['type'] | null>(null);
11
+
12
+ const filtered = useMemo(
13
+ () =>
14
+ rows.filter((row) => {
15
+ const matchesQuery = row.name.toLowerCase().includes(query.toLowerCase());
16
+ const matchesType = type ? row.type === type : true;
17
+ return matchesQuery && matchesType;
18
+ }),
19
+ [rows, query, type],
20
+ );
21
+
22
+ return (
23
+ <PageLayout>
24
+ <PageLayout.Toolbar>
25
+ <SearchInput
26
+ allowClear
27
+ placeholder="Search by name"
28
+ value={query}
29
+ onValueChange={setQuery}
30
+ className="tw-w-64"
31
+ />
32
+ <Combobox
33
+ allowClear
34
+ items={['table', 'file'] as const}
35
+ value={type}
36
+ onValueChange={setType}
37
+ placeholder="Type"
38
+ className="tw-w-40"
39
+ />
40
+ <span className="tw-text-sm tw-text-muted-foreground">{filtered.length} results</span>
41
+ </PageLayout.Toolbar>
42
+ <PageLayout.Content>
43
+ <ul className="tw-flex tw-flex-col tw-gap-1">
44
+ {filtered.map((row) => (
45
+ <li key={row.id}>{row.name}</li>
46
+ ))}
47
+ </ul>
48
+ </PageLayout.Content>
49
+ </PageLayout>
50
+ );
51
+ };
52
+
53
+ const meta = {
54
+ title: 'Patterns/FilterToolbar',
55
+ component: FilteredList,
56
+ } satisfies Meta<typeof FilteredList>;
57
+
58
+ export default meta;
59
+
60
+ type Story = StoryObj<typeof meta>;
61
+
62
+ export const Default: Story = {
63
+ args: {
64
+ rows: [
65
+ { id: '1', name: 'marketing.users', type: 'table' },
66
+ { id: '2', name: 'sales.leads', type: 'table' },
67
+ { id: '3', name: 'export.csv', type: 'file' },
68
+ { id: '4', name: 'snapshot.json', type: 'file' },
69
+ ],
70
+ },
71
+ };
@@ -0,0 +1,56 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { Button, FormGroup, Label, TextInput, Tooltip } from '@keboola/design';
4
+ import { Info } from '@keboola/design/icons';
5
+
6
+ /** Info icon next to a label with a hover/focus tooltip — `Label` + icon-only `Button` + `Tooltip`. */
7
+ const FieldWithInfo = ({ id, label, hint }: { id: string; label: string; hint: string }) => (
8
+ <FormGroup>
9
+ <div className="tw-flex tw-items-center tw-gap-1">
10
+ <Label htmlFor={id}>{label}</Label>
11
+ <Tooltip tooltip={hint} placement="right" triggerAsChild>
12
+ <Button
13
+ variant="inline"
14
+ size="icon-3xs"
15
+ aria-label={`More info about ${label}`}
16
+ className="tw-text-muted-foreground"
17
+ >
18
+ <Info className="tw-size-3" />
19
+ </Button>
20
+ </Tooltip>
21
+ </div>
22
+ <TextInput id={id} />
23
+ </FormGroup>
24
+ );
25
+
26
+ const meta = {
27
+ title: 'Patterns/InfoTooltip',
28
+ component: FieldWithInfo,
29
+ parameters: {
30
+ docs: {
31
+ description: {
32
+ component: 'Info icon next to a label with a hover/focus tooltip.',
33
+ },
34
+ },
35
+ },
36
+ } satisfies Meta<typeof FieldWithInfo>;
37
+
38
+ export default meta;
39
+
40
+ type Story = StoryObj<typeof meta>;
41
+
42
+ export const Default: Story = {
43
+ args: {
44
+ id: 'account-name',
45
+ label: 'Account name',
46
+ hint: 'Used to identify your project in URLs.',
47
+ },
48
+ };
49
+
50
+ export const LongHint: Story = {
51
+ args: {
52
+ id: 'webhook-url',
53
+ label: 'Webhook URL',
54
+ hint: 'Endpoint that Keboola will POST to whenever this job finishes. Must respond with 2xx within 5 seconds or the call is retried.',
55
+ },
56
+ };
@@ -0,0 +1,65 @@
1
+ import { useState } from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react-vite';
3
+
4
+ import { Card, PageLayout, Sidebar, SidebarProvider } from '@keboola/design';
5
+
6
+ type Item = { id: string; name: string };
7
+
8
+ const ListDetailPage = ({ items }: { items: Item[] }) => {
9
+ const [selectedId, setSelectedId] = useState(items[0]?.id ?? '');
10
+ const selected = items.find((item) => item.id === selectedId);
11
+
12
+ return (
13
+ <SidebarProvider defaultLeftMode="expanded" leftVariant="inset" leftCollapsible="icon">
14
+ <div className="tw-flex tw-flex-1">
15
+ <Sidebar side="left">
16
+ <Sidebar.Content>
17
+ <Sidebar.Menu>
18
+ {items.map((item) => (
19
+ <Sidebar.MenuItem key={item.id}>
20
+ <Sidebar.MenuButton
21
+ tooltip={item.name}
22
+ aria-selected={item.id === selectedId}
23
+ onClick={() => setSelectedId(item.id)}
24
+ >
25
+ {item.name}
26
+ </Sidebar.MenuButton>
27
+ </Sidebar.MenuItem>
28
+ ))}
29
+ </Sidebar.Menu>
30
+ </Sidebar.Content>
31
+ </Sidebar>
32
+ <PageLayout>
33
+ <PageLayout.Content>
34
+ <Card bordered>
35
+ <Card.Header>
36
+ <Card.Title>{selected?.name ?? 'Select an item'}</Card.Title>
37
+ </Card.Header>
38
+ <Card.Content>Detail for {selected?.id}.</Card.Content>
39
+ </Card>
40
+ </PageLayout.Content>
41
+ </PageLayout>
42
+ </div>
43
+ </SidebarProvider>
44
+ );
45
+ };
46
+
47
+ const meta = {
48
+ title: 'Patterns/ListDetail',
49
+ component: ListDetailPage,
50
+ parameters: { layout: 'fullscreen' },
51
+ } satisfies Meta<typeof ListDetailPage>;
52
+
53
+ export default meta;
54
+
55
+ type Story = StoryObj<typeof meta>;
56
+
57
+ export const Default: Story = {
58
+ args: {
59
+ items: [
60
+ { id: 'marketing', name: 'Marketing' },
61
+ { id: 'sales', name: 'Sales' },
62
+ { id: 'analytics', name: 'Analytics' },
63
+ ],
64
+ },
65
+ };
@@ -0,0 +1,59 @@
1
+ import { useState } from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react-vite';
3
+
4
+ import { Button, Modal, useAppForm } from '@keboola/design';
5
+
6
+ const NewBucketModal = () => {
7
+ const [open, setOpen] = useState(false);
8
+
9
+ const form = useAppForm({
10
+ defaultValues: { name: '', description: '' },
11
+ onSubmit: async ({ value }) => {
12
+ await Promise.resolve(value);
13
+ setOpen(false);
14
+ form.reset();
15
+ },
16
+ });
17
+
18
+ return (
19
+ <Modal open={open} onOpenChange={setOpen}>
20
+ <Modal.Trigger asChild>
21
+ <Button>New bucket</Button>
22
+ </Modal.Trigger>
23
+ <Modal.Content size="medium">
24
+ <Modal.Header title="New bucket" description="Create an empty storage bucket." />
25
+ <Modal.Body>
26
+ <form.FormBody>
27
+ <form.AppField name="name">
28
+ {(field) => <field.TextField label="Name" required variant="secondary" />}
29
+ </form.AppField>
30
+ <form.AppField name="description">
31
+ {(field) => <field.TextAreaField label="Description" variant="secondary" />}
32
+ </form.AppField>
33
+ </form.FormBody>
34
+ </Modal.Body>
35
+ <Modal.Footer>
36
+ <Modal.Close asChild>
37
+ <Button type="button" variant="secondary">
38
+ Cancel
39
+ </Button>
40
+ </Modal.Close>
41
+ <form.AppForm>
42
+ <form.SubmitButton>Create</form.SubmitButton>
43
+ </form.AppForm>
44
+ </Modal.Footer>
45
+ </Modal.Content>
46
+ </Modal>
47
+ );
48
+ };
49
+
50
+ const meta = {
51
+ title: 'Patterns/ModalForm',
52
+ component: NewBucketModal,
53
+ } satisfies Meta<typeof NewBucketModal>;
54
+
55
+ export default meta;
56
+
57
+ type Story = StoryObj<typeof meta>;
58
+
59
+ export const Default: Story = {};
@@ -0,0 +1,48 @@
1
+ import { useState } from 'react';
2
+ import type { ReactNode } from 'react';
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { PageHeader, PageLayout, Tabs, TabsContent, type Trigger } from '@keboola/design';
6
+
7
+ const TABS: Trigger<string>[] = [
8
+ { value: '/storage', title: 'Tables & Buckets' },
9
+ { value: '/storage/files', title: 'Files' },
10
+ { value: '/storage/jobs', title: 'Storage Jobs' },
11
+ ];
12
+
13
+ // Story uses local state so the composition renders standalone in Storybook.
14
+ // See RECIPES.md for the router-driven version (TanStack Router).
15
+ const StoragePage = ({ children }: { children: ReactNode }) => {
16
+ const [activeTab, setActiveTab] = useState(TABS[0]!.value);
17
+
18
+ return (
19
+ <PageLayout>
20
+ <PageLayout.Header>
21
+ <PageHeader.Index
22
+ title="Storage"
23
+ metadata="3 buckets and 39 tables"
24
+ headerNav={
25
+ <Tabs triggers={TABS} defaultValue={activeTab} onValueChange={setActiveTab}>
26
+ <TabsContent value={activeTab}>{children}</TabsContent>
27
+ </Tabs>
28
+ }
29
+ />
30
+ </PageLayout.Header>
31
+ </PageLayout>
32
+ );
33
+ };
34
+
35
+ const meta = {
36
+ title: 'Patterns/PageHeaderTabs',
37
+ component: StoragePage,
38
+ } satisfies Meta<typeof StoragePage>;
39
+
40
+ export default meta;
41
+
42
+ type Story = StoryObj<typeof meta>;
43
+
44
+ export const Default: Story = {
45
+ args: {
46
+ children: <div className="tw-p-4 tw-text-muted-foreground">Tab content</div>,
47
+ },
48
+ };
package/src/styles.css CHANGED
@@ -13,6 +13,10 @@
13
13
  --color-background: 249 250 251; /* #f9fafb */
14
14
  --color-foreground: 30 41 59; /* #1e293b */
15
15
 
16
+ /* Primary — Keboola blue (matches secondary-500 / sidebar-primary). */
17
+ --color-primary: 31 143 255; /* #1f8fff */
18
+ --color-primary-foreground: 255 255 255; /* #ffffff */
19
+
16
20
  /* Popover */
17
21
  --color-popover: 255 255 255; /* #ffffff */
18
22
  --color-popover-foreground: 30 41 59; /* #1e293b */
@@ -54,6 +58,10 @@
54
58
  --color-background: 30 41 59; /* #1e293b */
55
59
  --color-foreground: 241 245 249; /* #f1f5f9 */
56
60
 
61
+ /* Primary — Keboola blue (kept consistent across themes; tune in UT-3936 follow-up). */
62
+ --color-primary: 31 143 255; /* #1f8fff */
63
+ --color-primary-foreground: 255 255 255; /* #ffffff */
64
+
57
65
  /* Popover */
58
66
  --color-popover: 30 41 59; /* #1e293b */
59
67
  --color-popover-foreground: 241 245 249; /* #f1f5f9 */
@@ -142,14 +150,14 @@
142
150
  h2 {
143
151
  font-size: 22px;
144
152
  line-height: 32px;
145
- @apply tw-text-neutral-900;
153
+ @apply tw-text-foreground;
146
154
  font-weight: 500;
147
155
  }
148
156
 
149
157
  h3 {
150
158
  font-size: 16px;
151
159
  line-height: 24px;
152
- @apply tw-text-neutral-900;
160
+ @apply tw-text-foreground;
153
161
  font-weight: 500;
154
162
  margin: 0;
155
163
  }
@@ -158,7 +166,7 @@
158
166
  font-size: 14px;
159
167
  line-height: 20px;
160
168
  font-weight: 500;
161
- @apply tw-text-neutral-900;
169
+ @apply tw-text-foreground;
162
170
  margin: 0;
163
171
  }
164
172
 
@@ -166,7 +174,7 @@
166
174
  font-size: 12px;
167
175
  line-height: 20px;
168
176
  font-weight: 500;
169
- @apply tw-text-neutral-900;
177
+ @apply tw-text-foreground;
170
178
  margin: 0;
171
179
  }
172
180
 
@@ -176,7 +184,7 @@
176
184
  h4 small,
177
185
  h5 small,
178
186
  h6 small {
179
- @apply tw-text-neutral-400;
187
+ @apply tw-text-muted-foreground;
180
188
  font-size: 75%;
181
189
  }
182
190
 
@@ -192,11 +200,11 @@
192
200
  }
193
201
 
194
202
  .tw-prose blockquote {
195
- border-left: 4px solid theme('colors.neutral.300');
196
- background-color: theme('colors.neutral.50');
203
+ border-left: 4px solid theme('colors.border');
204
+ background-color: theme('colors.muted.DEFAULT');
197
205
  padding: 12px 16px;
198
206
  border-radius: 2px;
199
- color: theme('colors.neutral.800');
207
+ color: theme('colors.foreground');
200
208
  font-size: 14px;
201
209
  font-style: italic;
202
210
  font-weight: normal;
@@ -0,0 +1,28 @@
1
+ import type { ComponentProps, ReactNode } from 'react';
2
+
3
+ import { Icon } from '../components/Icon';
4
+
5
+ /**
6
+ * Shim helpers for wrappers whose `icon` prop transitioned from a FontAwesome
7
+ * name / `[prefix, name]` tuple to a `ReactNode` (Lucide-composition) during the
8
+ * UT-4109 C2 sweep. New code should pass a Lucide icon node directly — these
9
+ * helpers exist only so existing `<Wrapper icon="cog">` callsites keep
10
+ * rendering an icon instead of the raw string while consumer-migration PRs
11
+ * (C2b–C2j) land. Removed by the final C2 cleanup PR.
12
+ */
13
+ type FAIconValue = ComponentProps<typeof Icon>['icon'];
14
+
15
+ export const isFAIconValue = (value: unknown): value is FAIconValue =>
16
+ typeof value === 'string' ||
17
+ (Array.isArray(value) &&
18
+ value.length === 2 &&
19
+ typeof value[0] === 'string' &&
20
+ typeof value[1] === 'string');
21
+
22
+ /**
23
+ * Render a node, transparently dispatching legacy FontAwesome icon values
24
+ * (string / `[prefix, name]` tuple) through the legacy `<Icon>` wrapper.
25
+ * For ReactNode inputs (the new composition path), returns the node as-is.
26
+ */
27
+ export const renderLegacyIcon = (value: ReactNode | FAIconValue): ReactNode =>
28
+ isFAIconValue(value) ? <Icon icon={value} /> : value;