@keboola/design 11.3.0 → 11.5.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/AGENTS.md +208 -1
- package/CHANGELOG.md +528 -0
- package/COMPONENT-API.md +68 -46
- package/dist/{AIKit-DaFT7p92.js → AIKit-CNX6aiKm.js} +338 -113
- package/dist/AIKit-CNX6aiKm.js.map +1 -0
- package/dist/{Alert-CseY3yAj.js → Alert-Ba5b2YPq.js} +3 -3
- package/dist/{Alert-CseY3yAj.js.map → Alert-Ba5b2YPq.js.map} +1 -1
- package/dist/{AlertModal-DQAGpsEy.js → AlertModal-BGJdaRRj.js} +3 -3
- package/dist/{AlertModal-DQAGpsEy.js.map → AlertModal-BGJdaRRj.js.map} +1 -1
- package/dist/{AppShell-BUoxM3fv.js → AppShell-DRgJxpXW.js} +2 -2
- package/dist/{AppShell-BUoxM3fv.js.map → AppShell-DRgJxpXW.js.map} +1 -1
- package/dist/{Badge-Ch26CXoP.js → Badge-D0pXdekt.js} +2 -2
- package/dist/Badge-D0pXdekt.js.map +1 -0
- package/dist/{BrandEditor-BnTLMRMp.js → BrandEditor-CSANsxUz.js} +8 -8
- package/dist/{BrandEditor-BnTLMRMp.js.map → BrandEditor-CSANsxUz.js.map} +1 -1
- package/dist/{BrandLogo-V2pRUAeO.js → BrandLogo-CoKBk4MI.js} +5 -5
- package/dist/BrandLogo-CoKBk4MI.js.map +1 -0
- package/dist/{Breadcrumb-CQTPn_KB.js → Breadcrumb-g_xn_-Nf.js} +3 -3
- package/dist/{Breadcrumb-CQTPn_KB.js.map → Breadcrumb-g_xn_-Nf.js.map} +1 -1
- package/dist/{Button-CPsEQuTN.js → Button-DsRrU5F1.js} +6 -5
- package/dist/Button-DsRrU5F1.js.map +1 -0
- package/dist/{Card-Cd5HSBmv.js → Card-Dyo7055Q.js} +2 -2
- package/dist/{Card-Cd5HSBmv.js.map → Card-Dyo7055Q.js.map} +1 -1
- package/dist/{Checkbox-5VNCcV7E.js → Checkbox-CUIn2jap.js} +3 -3
- package/dist/{Checkbox-5VNCcV7E.js.map → Checkbox-CUIn2jap.js.map} +1 -1
- package/dist/{Clipboard-B7rckqV_.js → Clipboard-BTW6C-b_.js} +2 -2
- package/dist/{Clipboard-B7rckqV_.js.map → Clipboard-BTW6C-b_.js.map} +1 -1
- package/dist/{ClipboardButton-CQ6Aewpg.js → ClipboardButton-CwZJdRaz.js} +3 -3
- package/dist/{ClipboardButton-CQ6Aewpg.js.map → ClipboardButton-CwZJdRaz.js.map} +1 -1
- package/dist/{CodeEditor-CUse8znx.js → CodeEditor-C9zy7PxK.js} +16 -27
- package/dist/CodeEditor-C9zy7PxK.js.map +1 -0
- package/dist/{ColorInput-Q1D9Vcc_.js → ColorInput-BZV-u_94.js} +3 -3
- package/dist/{ColorInput-Q1D9Vcc_.js.map → ColorInput-BZV-u_94.js.map} +1 -1
- package/dist/{ColorModeSwitch-B5PXCVqB.js → ColorModeSwitch-CsWLiTBX.js} +3 -3
- package/dist/{ColorModeSwitch-B5PXCVqB.js.map → ColorModeSwitch-CsWLiTBX.js.map} +1 -1
- package/dist/{Combobox-CyoRA7oY.js → Combobox-BfIfaVMA.js} +285 -109
- package/dist/Combobox-BfIfaVMA.js.map +1 -0
- package/dist/{Command-Ds68v2rX.js → Command-Bfa1kpbt.js} +6 -6
- package/dist/{Command-Ds68v2rX.js.map → Command-Bfa1kpbt.js.map} +1 -1
- package/dist/{CopyField-DI66uY_g.js → CopyField-DpT0Ngz1.js} +2 -2
- package/dist/{CopyField-DI66uY_g.js.map → CopyField-DpT0Ngz1.js.map} +1 -1
- package/dist/{DateInput-BPj2pzsn.js → DateInput-DiEd5uCL.js} +4 -4
- package/dist/{DateInput-BPj2pzsn.js.map → DateInput-DiEd5uCL.js.map} +1 -1
- package/dist/{DatePicker-DyEGuW9m.js → DatePicker-nA8YdE84.js} +2 -2
- package/dist/{DatePicker-DyEGuW9m.js.map → DatePicker-nA8YdE84.js.map} +1 -1
- package/dist/{DateRangeInput-u9DxNbHW.js → DateRangeInput-DPfVYchl.js} +4 -4
- package/dist/{DateRangeInput-u9DxNbHW.js.map → DateRangeInput-DPfVYchl.js.map} +1 -1
- package/dist/{DateRangePicker-CYYhX6S7.js → DateRangePicker-DjSUFjh4.js} +5 -5
- package/dist/{DateRangePicker-CYYhX6S7.js.map → DateRangePicker-DjSUFjh4.js.map} +1 -1
- package/dist/{DropdownMenu-DpMEcyCq.js → DropdownMenu-CASr-jgl.js} +2 -2
- package/dist/{DropdownMenu-DpMEcyCq.js.map → DropdownMenu-CASr-jgl.js.map} +1 -1
- package/dist/{FieldItem-qQKGU9lF.js → FieldItem-B7XMmZSz.js} +3 -3
- package/dist/{FieldItem-qQKGU9lF.js.map → FieldItem-B7XMmZSz.js.map} +1 -1
- package/dist/{FileUpload-C336MVnU.js → FileUpload-ABThVZyg.js} +2 -2
- package/dist/{FileUpload-C336MVnU.js.map → FileUpload-ABThVZyg.js.map} +1 -1
- package/dist/{Form-CTygENDc.js → Form-CsttTHn4.js} +8 -8
- package/dist/Form-CsttTHn4.js.map +1 -0
- package/dist/{FormGroup-BnwP0jNu.js → FormGroup-DHvTFdno.js} +3 -3
- package/dist/{FormGroup-BnwP0jNu.js.map → FormGroup-DHvTFdno.js.map} +1 -1
- package/dist/{FullScreenModal-j-M4aPFd.js → FullScreenModal-BvPuYibe.js} +3 -3
- package/dist/{FullScreenModal-j-M4aPFd.js.map → FullScreenModal-BvPuYibe.js.map} +1 -1
- package/dist/{HoverCard-yQq-O49c.js → HoverCard-B9ZMJxjn.js} +2 -2
- package/dist/{HoverCard-yQq-O49c.js.map → HoverCard-B9ZMJxjn.js.map} +1 -1
- package/dist/{InputGroup-D7zlmqPB.js → InputGroup-DcZRlVnN.js} +24 -16
- package/dist/InputGroup-DcZRlVnN.js.map +1 -0
- package/dist/{Kbd-D6LpR7G2.js → Kbd-Cvvt91Q8.js} +14 -4
- package/dist/Kbd-Cvvt91Q8.js.map +1 -0
- package/dist/MarkedText-DNGEUVWc.js +67 -0
- package/dist/MarkedText-DNGEUVWc.js.map +1 -0
- package/dist/{MermaidDiagram-JlvuGcY-.js → MermaidDiagram-CXG5tC6J.js} +19 -18
- package/dist/MermaidDiagram-CXG5tC6J.js.map +1 -0
- package/dist/{Modal-o4ATIexE.js → Modal-Coj-H02o.js} +16 -4
- package/dist/Modal-Coj-H02o.js.map +1 -0
- package/dist/{MultiActionButtonGroup-GTeFk-L8.js → MultiActionButtonGroup-DokHbPgM.js} +3 -3
- package/dist/{MultiActionButtonGroup-GTeFk-L8.js.map → MultiActionButtonGroup-DokHbPgM.js.map} +1 -1
- package/dist/{Navbar-CAQs4BZz.js → Navbar-Liy1TPI7.js} +7 -7
- package/dist/{Navbar-CAQs4BZz.js.map → Navbar-Liy1TPI7.js.map} +1 -1
- package/dist/PageHeader-Q3IpCnQd.js.map +1 -1
- package/dist/{PageToolbar-bG2YtSa3.js → PageToolbar-DExqEYGl.js} +4 -4
- package/dist/{PageToolbar-bG2YtSa3.js.map → PageToolbar-DExqEYGl.js.map} +1 -1
- package/dist/{Pagination-BB3X7ooY.js → Pagination-C_b7Xhey.js} +2 -2
- package/dist/{Pagination-BB3X7ooY.js.map → Pagination-C_b7Xhey.js.map} +1 -1
- package/dist/{Popconfirm-C2Ky5hJO.js → Popconfirm-5pBzT_HQ.js} +12 -13
- package/dist/{Popconfirm-C2Ky5hJO.js.map → Popconfirm-5pBzT_HQ.js.map} +1 -1
- package/dist/{Popover-DsbI2HKu.js → Popover-BEbQrC_P.js} +22 -19
- package/dist/Popover-BEbQrC_P.js.map +1 -0
- package/dist/{RadioGroup-BMxuZEbT.js → RadioGroup-DXPbSj8P.js} +3 -3
- package/dist/{RadioGroup-BMxuZEbT.js.map → RadioGroup-DXPbSj8P.js.map} +1 -1
- package/dist/{Search-DFeECt5D.js → Search-5uQAiNIP.js} +2 -2
- package/dist/{Search-DFeECt5D.js.map → Search-5uQAiNIP.js.map} +1 -1
- package/dist/{SearchInput-CCpdUego.js → SearchInput-D4UgAXJK.js} +3 -3
- package/dist/{SearchInput-CCpdUego.js.map → SearchInput-D4UgAXJK.js.map} +1 -1
- package/dist/{SectionNav-Bw_MVL0_.js → SectionNav-OkKCjSA-.js} +5 -5
- package/dist/{SectionNav-Bw_MVL0_.js.map → SectionNav-OkKCjSA-.js.map} +1 -1
- package/dist/{Select-D6IZVvwL.js → Select-DV3Hak_l.js} +31 -21
- package/dist/Select-DV3Hak_l.js.map +1 -0
- package/dist/{Sheet-BIzs2K2H.js → Sheet-Chf0kX1E.js} +3 -3
- package/dist/{Sheet-BIzs2K2H.js.map → Sheet-Chf0kX1E.js.map} +1 -1
- package/dist/{Sidebar-r1TW4g_b.js → Sidebar-D3cY1kha.js} +48 -22
- package/dist/Sidebar-D3cY1kha.js.map +1 -0
- package/dist/{Slider-CjxyfEty.js → Slider-CRf-bB4y.js} +3 -3
- package/dist/{Slider-CjxyfEty.js.map → Slider-CRf-bB4y.js.map} +1 -1
- package/dist/{Switch-BueTV03C.js → Switch-B0WdGSjz.js} +8 -8
- package/dist/{Switch-BueTV03C.js.map → Switch-B0WdGSjz.js.map} +1 -1
- package/dist/{Tabs-l72-WuhK.js → Tabs-eB974xJf.js} +2 -2
- package/dist/{Tabs-l72-WuhK.js.map → Tabs-eB974xJf.js.map} +1 -1
- package/dist/{TextInput-DhdRk8qm.js → TextInput-DH5SkbsE.js} +3 -3
- package/dist/{TextInput-DhdRk8qm.js.map → TextInput-DH5SkbsE.js.map} +1 -1
- package/dist/{ThemeProvider-DZfGdf1v.js → ThemeProvider-2RMdVGxO.js} +95 -15
- package/dist/ThemeProvider-2RMdVGxO.js.map +1 -0
- package/dist/{TimePicker-B6d2rAbX.js → TimePicker-BGeqc-Ed.js} +2 -2
- package/dist/{TimePicker-B6d2rAbX.js.map → TimePicker-BGeqc-Ed.js.map} +1 -1
- package/dist/{Timeline-6btyJl4D.js → Timeline-BG5Xogxh.js} +11 -11
- package/dist/{Timeline-6btyJl4D.js.map → Timeline-BG5Xogxh.js.map} +1 -1
- package/dist/{Toast-CxXUulYN.js → Toast-Dr6Uw7ao.js} +2 -2
- package/dist/{Toast-CxXUulYN.js.map → Toast-Dr6Uw7ao.js.map} +1 -1
- package/dist/{Workbench-BzE-Vvd9.js → Workbench-UR2FBC7I.js} +3 -1
- package/dist/Workbench-UR2FBC7I.js.map +1 -0
- package/dist/{ZoomableSvg-CNIzRHKH.js → ZoomableSvg-pooPb1Cu.js} +4 -4
- package/dist/{ZoomableSvg-CNIzRHKH.js.map → ZoomableSvg-pooPb1Cu.js.map} +1 -1
- package/dist/{context-BrNtMc1H.js → context-D7X470dy.js} +4 -3
- package/dist/context-D7X470dy.js.map +1 -0
- package/dist/{defaults-CTfvj2KB.js → defaults-i_xG2F_1.js} +2 -4
- package/dist/defaults-i_xG2F_1.js.map +1 -0
- package/dist/editors.js +5 -5
- package/dist/findLiteralMatches-BGBw8Vbl.js +62 -0
- package/dist/findLiteralMatches-BGBw8Vbl.js.map +1 -0
- package/dist/flint.js +134 -8
- package/dist/flint.js.map +1 -1
- package/dist/index.d.ts +1118 -566
- package/dist/index.js +64 -62
- package/dist/internal/blocks/AIKit.js +2 -2
- package/dist/internal/blocks/AppShell.js +1 -1
- package/dist/internal/blocks/AuthLayout.js +118 -0
- package/dist/internal/blocks/AuthLayout.js.map +1 -0
- package/dist/internal/blocks/BrandEditor.js +1 -1
- package/dist/internal/blocks/ChartPanel.js +1 -1
- package/dist/internal/blocks/ClipboardButton.js +1 -1
- package/dist/internal/blocks/ColorInput.js +1 -1
- package/dist/internal/blocks/ColorModeSwitch.js +1 -1
- package/dist/internal/blocks/Command.js +1 -1
- package/dist/internal/blocks/CopyField.js +1 -1
- package/dist/internal/blocks/DateInput.js +1 -1
- package/dist/internal/blocks/DateRangeInput.js +1 -1
- package/dist/internal/blocks/EmptyConfigurationActionCard.js +1 -1
- package/dist/internal/blocks/ErrorState.js +1 -1
- package/dist/internal/blocks/ExportMenu.js +5 -5
- package/dist/internal/blocks/ExportMenu.js.map +1 -1
- package/dist/internal/blocks/Form/connectors/MultiComboboxField.js +1 -1
- package/dist/internal/blocks/Form.js +1 -1
- package/dist/internal/blocks/FormGroup.js +1 -1
- package/dist/internal/blocks/MultiActionButtonGroup.js +1 -1
- package/dist/internal/blocks/MultiCombobox.js +116 -54
- package/dist/internal/blocks/MultiCombobox.js.map +1 -1
- package/dist/internal/blocks/NumberInput.js +1 -1
- package/dist/internal/blocks/PageToolbar.js +1 -1
- package/dist/internal/blocks/PasswordInput.js +2 -2
- package/dist/internal/blocks/SearchInput.js +1 -1
- package/dist/internal/blocks/SearchInputWithNavigation.js +3 -3
- package/dist/internal/blocks/SectionNav.js +1 -1
- package/dist/internal/blocks/SplitButton.js +2 -2
- package/dist/internal/blocks/StatCard.js +1 -1
- package/dist/internal/blocks/TextInput.js +1 -1
- package/dist/internal/blocks/Textarea.js +1 -1
- package/dist/internal/blocks/TimeInput.js +3 -3
- package/dist/internal/components/Alert.js +1 -1
- package/dist/internal/components/AlertModal.js +1 -1
- package/dist/internal/components/Avatar.js +1 -1
- package/dist/internal/components/Badge.js +1 -1
- package/dist/internal/components/BrandLogo.js +1 -1
- package/dist/internal/components/Breadcrumb.js +1 -1
- package/dist/internal/components/Button.js +1 -1
- package/dist/internal/components/Card.js +1 -1
- package/dist/internal/components/Checkbox.js +1 -1
- package/dist/internal/components/Clipboard.js +1 -1
- package/dist/internal/components/CollapsiblePanel.js +1 -1
- package/dist/internal/components/Combobox.js +2 -2
- package/dist/internal/components/DatePicker.js +1 -1
- package/dist/internal/components/DateRangePicker.js +1 -1
- package/dist/internal/components/DropdownMenu.js +1 -1
- package/dist/internal/components/FileUpload.js +1 -1
- package/dist/internal/components/FullScreenModal.js +1 -1
- package/dist/internal/components/HoverCard.js +1 -1
- package/dist/internal/components/InputGroup.js +1 -1
- package/dist/internal/components/Kbd.js +1 -1
- package/dist/internal/components/MarkedText.js +1 -1
- package/dist/internal/components/Modal.js +1 -1
- package/dist/internal/components/Navbar.js +1 -1
- package/dist/internal/components/Pagination.js +1 -1
- package/dist/internal/components/Popconfirm.js +1 -1
- package/dist/internal/components/Popover.js +1 -1
- package/dist/internal/components/RadioGroup.js +1 -1
- package/dist/internal/components/Search.js +1 -1
- package/dist/internal/components/Select.js +1 -1
- package/dist/internal/components/Sheet.js +1 -1
- package/dist/internal/components/Sidebar.js +1 -1
- package/dist/internal/components/Slider.js +1 -1
- package/dist/internal/components/Switch.js +1 -1
- package/dist/internal/components/Tabs.js +1 -1
- package/dist/internal/components/ThemeProvider.js +4 -4
- package/dist/internal/components/TimePicker.js +1 -1
- package/dist/internal/components/Timeline.js +1 -1
- package/dist/internal/components/Toast.js +2 -2
- package/dist/internal/components/Toggle.js +3 -3
- package/dist/internal/components/Toggle.js.map +1 -1
- package/dist/internal/components/Tooltip.js +1 -1
- package/dist/internal/components/Truncated.js +1 -1
- package/dist/internal/components/Workbench.js +1 -1
- package/dist/internal/components/ZoomableSvg.js +1 -1
- package/dist/internal/utils.js +3 -3
- package/dist/markdown-editor.js +32 -26
- package/dist/markdown-editor.js.map +1 -1
- package/dist/mermaid.js +1 -1
- package/dist/rjsf.js +24 -25
- package/dist/rjsf.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/testing.js +4 -4
- package/dist/testing.js.map +1 -1
- package/dist/{useColorSchemeState-BJEk6esG.js → themeScopeContext-D7fSyb06.js} +17 -4
- package/dist/themeScopeContext-D7fSyb06.js.map +1 -0
- package/dist/{themeTokens-7T1AFLhq.js → themeTokens-qP1-55O7.js} +38 -11
- package/dist/themeTokens-qP1-55O7.js.map +1 -0
- package/dist/{toast-BweSoKn3.js → toast-DIC0iNRf.js} +2 -2
- package/dist/{toast-BweSoKn3.js.map → toast-DIC0iNRf.js.map} +1 -1
- package/dist/{utils-B-g7d_Jb.js → utils-DSvrhU8E.js} +7 -3
- package/dist/{utils-B-g7d_Jb.js.map → utils-DSvrhU8E.js.map} +1 -1
- package/dist/vega.d.ts +1 -1
- package/dist/vega.js +3 -3
- package/dist/vega.js.map +1 -1
- package/dist/{vegaTheme-FBXgAdHK.js → vegaTheme-CSvFEMBl.js} +6 -6
- package/dist/vegaTheme-CSvFEMBl.js.map +1 -0
- package/package.json +12 -10
- package/src/styles.css +3 -0
- package/dist/AIKit-DaFT7p92.js.map +0 -1
- package/dist/Badge-Ch26CXoP.js.map +0 -1
- package/dist/BrandLogo-V2pRUAeO.js.map +0 -1
- package/dist/Button-CPsEQuTN.js.map +0 -1
- package/dist/CodeEditor-CUse8znx.js.map +0 -1
- package/dist/Combobox-CyoRA7oY.js.map +0 -1
- package/dist/Form-CTygENDc.js.map +0 -1
- package/dist/InputGroup-D7zlmqPB.js.map +0 -1
- package/dist/Kbd-D6LpR7G2.js.map +0 -1
- package/dist/MarkedText-Ckbft5mw.js +0 -44
- package/dist/MarkedText-Ckbft5mw.js.map +0 -1
- package/dist/MermaidDiagram-JlvuGcY-.js.map +0 -1
- package/dist/Modal-o4ATIexE.js.map +0 -1
- package/dist/Popover-DsbI2HKu.js.map +0 -1
- package/dist/Select-D6IZVvwL.js.map +0 -1
- package/dist/Sidebar-r1TW4g_b.js.map +0 -1
- package/dist/ThemeProvider-DZfGdf1v.js.map +0 -1
- package/dist/Workbench-BzE-Vvd9.js.map +0 -1
- package/dist/context-BrNtMc1H.js.map +0 -1
- package/dist/defaults-CTfvj2KB.js.map +0 -1
- package/dist/escapeStringRegexp-8MxP2knR.js +0 -6
- package/dist/escapeStringRegexp-8MxP2knR.js.map +0 -1
- package/dist/themeTokens-7T1AFLhq.js.map +0 -1
- package/dist/useColorSchemeState-BJEk6esG.js.map +0 -1
- package/dist/vegaTheme-FBXgAdHK.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -93,8 +93,12 @@ This file answers **which** component to use; [`COMPONENT-API.md`](./COMPONENT-A
|
|
|
93
93
|
|
|
94
94
|
**Never** build custom label + input combinations — compose `Label` + `FormGroup.TextInput` + `FormGroup.Help` inside `FormGroup`.
|
|
95
95
|
|
|
96
|
+
**`Select` and `Select.Trigger` will not compile without an accessible name** — one of `aria-label`, `aria-labelledby`, or an `id` is required (the JSDoc on the prop says why). Pass `id` and point a `<Label htmlFor>` at it where the field has a visible label, `aria-labelledby` where that label is some other node, and `aria-label` for a bare control — a toolbar filter, a repeated row. `field.SelectField` needs none of the three: `FieldItem` owns the label and hands the control its `id`.
|
|
97
|
+
|
|
96
98
|
`FileUpload` composes `FileUpload.DropZone` (the dashed box, owning the drag / error visuals) with `FileUpload.FileList` (selected files; `invalid` tints the rows). It deliberately ships **no** rejection copy — the wording is product copy, so read the current rejections with `useFileUploadRejections()` inside `FileUpload` and write the sentence yourself. Reach for `useFileDropTarget` only when there is no box to render: it returns `getRootProps()` / `isDragActive` for an affordance you style, and `noClick` / `noKeyboard` keep an already-interactive subtree (a composer, a form) from opening the file dialog on every click.
|
|
97
99
|
|
|
100
|
+
**`InputGroup.Addon` (`align="inline-start"` / `"inline-end"`) keeps its own content width** — it no longer gives up space to the input, so a badge or a rendered selection stays fully visible instead of being squeezed to an ellipsis. Bound long, caller-controlled content yourself (a `max-w-[…] overflow-hidden` on the addon) if it isn't a fixed-size icon or button. `InputGroup`'s padding/sizing rules reach any descendant `input`/`textarea`, not just a direct child, so a consumer that wraps the input in its own layout (chips, an overlay) picks up that padding automatically — cancel it with `!px-0`/`!py-0` where the wrapping layout already owns its own edge padding (see `MultiCombobox`).
|
|
101
|
+
|
|
98
102
|
### Status surfaces
|
|
99
103
|
|
|
100
104
|
| Use | When |
|
|
@@ -176,6 +180,7 @@ The config holds resolved `rgb(…)` strings, not `var()` references — vega do
|
|
|
176
180
|
| Tailwind classes | All ad-hoc layout: `tw:flex tw:flex-col tw:gap-4`, `tw:grid …`, `tw:p-6` |
|
|
177
181
|
| `AppShell` | App-root shell that follows the active brand (sidebar vs top-nav) |
|
|
178
182
|
| `PageLayout` | Top-level page shape: header, tabs, toolbar, content, footer |
|
|
183
|
+
| `AuthLayout` | Signed-out page frame: sign-in, sign-up, password reset, MFA, project picker |
|
|
179
184
|
| `PageToolbar` | Index-page toolbar: search + filters / sort / actions / view + chips row |
|
|
180
185
|
| `Card` | Semantic content surface with `Card.Header` / `Card.Title` / `Card.Content` slots |
|
|
181
186
|
| `Sidebar` | App-shell left navigation rail (host inside `AppShell.Sidebar`) |
|
|
@@ -248,7 +253,153 @@ The `label` prop implies `orientation="horizontal"` — labels are not rendered
|
|
|
248
253
|
| `CodeEditor` | Generic code editing (JSON, YAML, shell) |
|
|
249
254
|
| `SqlEditor` | SQL editing (syntax + autocomplete) |
|
|
250
255
|
|
|
251
|
-
`Select` is wrong above ~10 items — use `Combobox`. `CodeEditor`/`SqlEditor` load the full CodeMirror bundle and ship from the `@keboola/design/editors` subpath (not the root); reserve for real code editing.
|
|
256
|
+
`Select` is wrong above ~10 items — use `Combobox`. How richly the **selected** value has to render decides nothing here: both controls carry a rich selection (see below), so a short enum whose selection is a badge is still a `Select`. `CodeEditor`/`SqlEditor` load the full CodeMirror bundle and ship from the `@keboola/design/editors` subpath (not the root); reserve for real code editing.
|
|
257
|
+
|
|
258
|
+
#### Clear affordance
|
|
259
|
+
|
|
260
|
+
| Control | Clear affordance |
|
|
261
|
+
| ---------------------------- | --------------------------------------------------------- |
|
|
262
|
+
| `Combobox` / `MultiCombobox` | Default on; `allowClear={false}` to withhold it |
|
|
263
|
+
| `Select` | None — add an explicit "None" / "All" item to the `items` |
|
|
264
|
+
|
|
265
|
+
Withhold it where clearing must not be one click away: a required single-value field, or a control
|
|
266
|
+
whose empty state the surrounding form cannot represent. The button only mounts once there is a
|
|
267
|
+
value, so an empty control renders identically either way. A **required multi** field keeps
|
|
268
|
+
clear-all — the chips are individually removable, so the empty state is reachable regardless and
|
|
269
|
+
withholding the button removes convenience without preventing anything. That is why the schema-driven
|
|
270
|
+
`SelectWidget` derives `allowClear={!required}` while `MultiComboboxField` does not.
|
|
271
|
+
|
|
272
|
+
#### Chip content in a `MultiCombobox`
|
|
273
|
+
|
|
274
|
+
| Use | When |
|
|
275
|
+
| ------------------------------- | ------------------------------------------------------------------------ |
|
|
276
|
+
| `itemToChipLabel` | Anything one chip contains — plain text, a link, an action of its own |
|
|
277
|
+
| `itemToChipColor` | A `Badge` colour per value (deleted, linked, stale) |
|
|
278
|
+
| `pinnedChipLabels` | A chip the control shows but the user does not own — nothing in `value` |
|
|
279
|
+
| `renderChips` + `Combobox.Chip` | One node standing for the whole selection, or a chip count ≠ value count |
|
|
280
|
+
|
|
281
|
+
`itemToChipLabel` returns a `ReactNode`, and a chip yields Enter and Space to interactive content
|
|
282
|
+
inside it — so a link or a button in a chip is not a reason to reach for `renderChips`. Reach for
|
|
283
|
+
the slot only when no per-value accessor can express the area: it replaces the whole thing, so
|
|
284
|
+
`maxVisible` and its `+N` chip become the caller's job too.
|
|
285
|
+
|
|
286
|
+
#### One creatable entry, several values
|
|
287
|
+
|
|
288
|
+
A `MultiCombobox` with `allowCreate` splits a created entry on `createSeparator`, so a pasted list
|
|
289
|
+
becomes one chip per value. It is **on by default** (`','`) — the paste is the reason a multi
|
|
290
|
+
control is creatable at all, and a chip literally named `id1, id2, id3` is never what the user
|
|
291
|
+
meant.
|
|
292
|
+
|
|
293
|
+
| Use | When |
|
|
294
|
+
| ----------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
295
|
+
| the default (`','`) | Values that arrive as a list — tags, column names, primary keys, ids, emails |
|
|
296
|
+
| `createSeparator={';'}`, `createSeparator={/[\s,]+/}` | Another convention — an Outlook address list, whitespace-separated ids |
|
|
297
|
+
| `createSeparator={null}` | A value can itself contain the separator — free text, a sentence, a name |
|
|
298
|
+
|
|
299
|
+
Each value is trimmed and the empty ones dropped, and repeats within one entry collapse. **The row
|
|
300
|
+
is offered whenever the entry adds anything to the selection, and names exactly those values** — a
|
|
301
|
+
value naming an existing item counts, because committing selects that item rather than minting a
|
|
302
|
+
twin of it. It withholds itself only when the selection already holds every value in the entry, or
|
|
303
|
+
when a single value names an item — that item's own row is the affordance there.
|
|
304
|
+
|
|
305
|
+
`queryToCreateLabel` is handed those values, re-joined with the entry's own separator, so the row
|
|
306
|
+
can neither name a value it drops nor stay silent about one it selects. `queryToCreateValue` is
|
|
307
|
+
called once per value, which is where a per-value projection or normalisation belongs, and
|
|
308
|
+
`canCreate` gates each value rather than the whole entry — so a pasted list keeps the values whose
|
|
309
|
+
format passes and drops the rest, and a value naming an existing item never reaches the gate
|
|
310
|
+
because committing selects that item instead of minting one. `Combobox` has no counterpart: one
|
|
311
|
+
entry there can only be one value.
|
|
312
|
+
|
|
313
|
+
#### How richly the selected value renders
|
|
314
|
+
|
|
315
|
+
| Use | When |
|
|
316
|
+
| ---------------------------------- | ----------------------------------------------------------------------------------- |
|
|
317
|
+
| `Select` — `label` | The trigger shows the item's `label` node as-is, Radix mirrors it from the row |
|
|
318
|
+
| `Select` — `icon` / `hint` | The **row** carries more than the trigger should — both sit outside the mirror |
|
|
319
|
+
| `Combobox` — `itemToStringLabel` | The selection is plain text, which is also the input's value and the search subject |
|
|
320
|
+
| `Combobox` — `itemToSelectedLabel` | The closed control needs a badge, an icon or a secondary line |
|
|
321
|
+
|
|
322
|
+
The three `Combobox` label accessors answer three different questions and are free to disagree:
|
|
323
|
+
`itemToLabel` styles the open list's rows (and takes the query, for `MarkedText`),
|
|
324
|
+
`itemToStringLabel` is the input's text, and `itemToSelectedLabel` is what the closed control shows.
|
|
325
|
+
The last one is decoration in front of the input, not a replacement for it — the input keeps the
|
|
326
|
+
string label as its value, so the control still reports its selection to a screen reader, and a
|
|
327
|
+
query dissolves the node and hands the row back to the search. Drawing the selection yourself in
|
|
328
|
+
`prefix` with `value` pinned to `null` gets the same picture and costs exactly that accessible value.
|
|
329
|
+
|
|
330
|
+
#### Who owns the typed query
|
|
331
|
+
|
|
332
|
+
The control owns its input text, always — a selection snaps it back to that item's label, and
|
|
333
|
+
creating or clearing a value rewrites it. Neither combobox takes a controlled `inputValue`, because
|
|
334
|
+
a parent holding it would have to reproduce all three of those writers. Two hooks cover what a call
|
|
335
|
+
site actually needs from the query:
|
|
336
|
+
|
|
337
|
+
| Use | When |
|
|
338
|
+
| --------------------- | ----------------------------------------------------------------------------- |
|
|
339
|
+
| `inputValueSanitizer` | The typed text has a character set — a bucket, table or column identifier |
|
|
340
|
+
| `onQueryChange` | The owner needs the query itself — a server-side search, an option order |
|
|
341
|
+
| `canCreate` | The value has a **format** the user must get right; the gate is not a rewrite |
|
|
342
|
+
| `queryToCreateValue` | Only the created item needs shaping, not the text the user sees while typing |
|
|
343
|
+
|
|
344
|
+
The last two are about the **value**, not the text: reach for them when the query is fine as typed
|
|
345
|
+
and only what it becomes needs gating or shaping.
|
|
346
|
+
|
|
347
|
+
#### Search semantics — `filter`
|
|
348
|
+
|
|
349
|
+
`Combobox` and `MultiCombobox` match the typed query as **one substring** of the item's label, so
|
|
350
|
+
`frankfurt europe` finds nothing in a list holding `Europe (Frankfurt)`. Any multi-word label wants
|
|
351
|
+
one of these instead:
|
|
352
|
+
|
|
353
|
+
| Use | When |
|
|
354
|
+
| ----------------------------------------------- | -------------------------------------------------------------------- |
|
|
355
|
+
| `filter={filterByWords}` | The searchable text **is** the label — every multi-word label |
|
|
356
|
+
| `filter={(item, q) => matchByWords([a, b], q)}` | Something the label omits is searchable too (an email behind a name) |
|
|
357
|
+
|
|
358
|
+
#### Blank values — `''` and `' '`
|
|
359
|
+
|
|
360
|
+
Some predicates are about blankness itself: "this Storage column equals the empty string" versus
|
|
361
|
+
"equals a single space". Whether a control can carry those depends on the primitive under it.
|
|
362
|
+
|
|
363
|
+
| Control | `''` / `' '` as a value |
|
|
364
|
+
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
365
|
+
| `MultiCombobox` | **Genuine values** — the selection shows as chips, so a blank one is nameable |
|
|
366
|
+
| `Combobox` | Genuine values; add `itemToSelectedLabel={namedLabel}` to name a blank selection in the closed control |
|
|
367
|
+
| `Select` | **Impossible** — Radix reads an empty root value as "nothing selected", so an item carrying it renders a blank trigger. Use a named sentinel item (`'none'`) and map it at the call site |
|
|
368
|
+
|
|
369
|
+
`withBlankValues` offers both blanks ahead of a value list and collapses duplicates — a column's
|
|
370
|
+
distinct values can already hold a blank, and two `Object.is`-equal rows both report `aria-selected`
|
|
371
|
+
and toggle each other.
|
|
372
|
+
|
|
373
|
+
```tsx
|
|
374
|
+
import { MultiCombobox, withBlankValues } from '@keboola/design';
|
|
375
|
+
|
|
376
|
+
<MultiCombobox
|
|
377
|
+
allowCreate
|
|
378
|
+
items={withBlankValues(columnValues)}
|
|
379
|
+
value={values}
|
|
380
|
+
onValueChange={setValues}
|
|
381
|
+
placeholder="Add a value"
|
|
382
|
+
/>;
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**Rows and chips name a blank; the input does not.** A row or a chip rendering as nothing is
|
|
386
|
+
indistinguishable from an empty control, so the display accessors name blanks `[empty string]`,
|
|
387
|
+
`[space character]`, and `[whitespace]` for anything else whose text is all whitespace — only the
|
|
388
|
+
first two read distinctly. `itemToStringLabel` is left alone on purpose: it is the item's string
|
|
389
|
+
identity, the default search text **and** the input's accessible value (see `toLabel`). Two
|
|
390
|
+
consequences at the call site:
|
|
391
|
+
|
|
392
|
+
- Typing never reaches a blank row, whatever `allowCreate` says, because the search text is the raw
|
|
393
|
+
value. Clear the query to pick one. `allowCreate` cannot create a blank either — it trims the
|
|
394
|
+
query — so fixed rows are the only way in.
|
|
395
|
+
- Naming a blank selection in a closed `Combobox` is `itemToSelectedLabel`'s job, **not**
|
|
396
|
+
`itemToStringLabel`'s: the slot renders a node in front of the input and leaves identity, search
|
|
397
|
+
and the accessible value intact.
|
|
398
|
+
|
|
399
|
+
Overriding a display accessor — `itemToLabel`, `itemToChipLabel`, `itemToSelectedLabel`, or the
|
|
400
|
+
`renderChips` slot, which replaces the chip area wholesale — composes `namedLabel` rather than
|
|
401
|
+
retyping the labels. `allowSelectAll` needs nothing: it commits the whole item set, blanks along
|
|
402
|
+
with it.
|
|
252
403
|
|
|
253
404
|
## Composition recipes
|
|
254
405
|
|
|
@@ -497,6 +648,58 @@ Both router wirings (TanStack Router, and a custom named router) are in [`RECIPE
|
|
|
497
648
|
/>
|
|
498
649
|
```
|
|
499
650
|
|
|
651
|
+
**Action picker `Select`** — a `Select` whose options are actions ("convert all names to UPPERCASE") rather than a value the form holds. Pin `value` to `''`: the trigger keeps showing its `placeholder`, and every pick fires, including the same one twice running.
|
|
652
|
+
|
|
653
|
+
```tsx
|
|
654
|
+
<Select
|
|
655
|
+
aria-label="Convert all column names"
|
|
656
|
+
placeholder="Convert All Names To"
|
|
657
|
+
value=""
|
|
658
|
+
items={CONVERSIONS}
|
|
659
|
+
onValueChange={renameEveryColumn}
|
|
660
|
+
/>
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
Radix routes the root value through `useControllableState`, which calls `onValueChange` only when the picked value differs from the current one — on the uncontrolled path (no `value` at all) it compares against the previous internal state, so leaving `value` off does **not** help. Storing the last action and feeding it back makes re-picking it a silent no-op, which matters whenever the action is still worth repeating: the user edited one of the renamed columns by hand and wants the bulk rename applied again.
|
|
664
|
+
|
|
665
|
+
**Pinned chip in a `MultiCombobox`** — a chip the control shows but the user does not own (a set of packages the runtime already ships, a role every member has). `pinnedChipLabels` is a render-only slot, so nothing enters `value`: there is no sentinel to strip on save and the control's own counting never sees a non-item.
|
|
666
|
+
|
|
667
|
+
```tsx
|
|
668
|
+
<MultiCombobox
|
|
669
|
+
allowCreate
|
|
670
|
+
value={packages}
|
|
671
|
+
onValueChange={save}
|
|
672
|
+
pinnedChipLabels={['Default packages']}
|
|
673
|
+
/>
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
Don't fake one by prepending a sentinel to `value` — that costs the caller a strip on every change plus a label branch, and leaks the sentinel into the primitive's counting.
|
|
677
|
+
|
|
678
|
+
**Selection summarised in a `MultiCombobox`** — `renderChips` renders the chip area from the
|
|
679
|
+
selected values, for the shape no per-value accessor can express: one node standing for the whole
|
|
680
|
+
selection. `Combobox.Chip` builds a removable chip for the values that still get one, and Base UI
|
|
681
|
+
removes the chip at an index, so render one per entry of `value`, in order.
|
|
682
|
+
|
|
683
|
+
```tsx
|
|
684
|
+
<MultiCombobox
|
|
685
|
+
items={tables}
|
|
686
|
+
value={selected}
|
|
687
|
+
onValueChange={save}
|
|
688
|
+
renderChips={(values) =>
|
|
689
|
+
values.length === 1 ? (
|
|
690
|
+
<Combobox.Chip>{labelOf(values[0])}</Combobox.Chip>
|
|
691
|
+
) : (
|
|
692
|
+
<Badge color="blue">{values.length} tables selected</Badge>
|
|
693
|
+
)
|
|
694
|
+
}
|
|
695
|
+
/>
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
A chip that merely holds a link or an action of its own does not need this — `itemToChipLabel`
|
|
699
|
+
takes the node, and the chip lets it have the keyboard.
|
|
700
|
+
|
|
701
|
+
**Select All in a `MultiCombobox`** — `allowSelectAll` means the **whole item set**, never the filtered one, so the row hides itself as soon as a query narrows the list. Don't reach for it to mean "select the matches" — a filtered bulk select needs its own labelled affordance stating the count, not a row whose meaning changes with the input.
|
|
702
|
+
|
|
500
703
|
**Sheet** — use `Sheet.Header title="…" description="…"` for a styled title + close icon; do **not** hand-roll with the raw `Sheet.Title`, which renders an unstyled `<h2>`. `Sheet.Body` provides horizontal padding only (`tw:px-4`) — add your own vertical rhythm/gap:
|
|
501
704
|
|
|
502
705
|
```tsx
|
|
@@ -535,6 +738,10 @@ Layer-0 colour usage splits into three buckets.
|
|
|
535
738
|
|
|
536
739
|
A border **width** utility carries no colour of its own — a lone `tw:border` / `tw:border-t` / `tw:border-2` paints the element's _text_ colour, a near-black frame where a light-grey rule belongs. Pair every width with a colour: `tw:border tw:border-border` for chrome, `tw:border-input` on form controls. `pnpm test:stories` fails on any story that ships a width without one.
|
|
537
740
|
|
|
741
|
+
An **opacity modifier composites over `transparent`, not over the element's own rest fill.** On a control that is opaque at rest (`tw:bg-card`, `tw:bg-background`) a `tw:hover:bg-<token>/NN` therefore _replaces_ that fill, and the control shows whatever it overlaps — a flow-canvas edge, an `Alert` tint. Mix the state fill into the control's rest token instead: `tw:hover:bg-[color-mix(in_oklab,rgb(var(--muted-foreground))_15%,rgb(var(--card)))]`. Use the raw `--*` triples — `--color-*` is `@theme inline`, build-time only, and an unresolved one invalidates the declaration into `transparent`. A control that is **transparent at rest** (menu item, ghost button, table row) is the case the modifier is for; leave those alone.
|
|
742
|
+
|
|
743
|
+
A tint also cannot carry a **state change** on a light surface — a brand-primary tint stays within ~1.5:1 of the surface at any opacity that still reads as a tint. Step the border for that, and keep the roles apart: hover steps the chrome border (`border-input` → `border-muted-foreground`), `focus-visible` owns the brand border.
|
|
744
|
+
|
|
538
745
|
**Status — themeable under argo, with AA-legible defaults.** The four semantic states — `success`, `warning`, `info`, and `destructive` (used for "error") — are brand-overridable tokens, not fixed scales. Alert `variant`, `HelpBlock` / field-error state, `ErrorState`, and `Stepper` status read them, so a brand recolours every status surface at once. **Rule:** consume the token as **tint + text/icon** — `tw:bg-<role>/10` for the surface, `tw:text-<role>` / `tw:border-<role>/25` for the mark. The default token values ship dark enough to be AA (≥4.5:1) as text/icon on their own tint, in light **and** dark mode — a brand overriding them should preserve that. Avoid solid fills with a white foreground (`tw:bg-<role>` + white): the token flips light in dark mode, so white-on-token fails there. A brand _can_ recolour "error" away from red — that is now allowed; the accessible-on-tint contract is what must hold, not the hue. **Legacy exception:** the themeable treatment is gated to argo (`data-[argo=true]:` / `group-data-[argo=true]/…:`). Under legacy chrome (`useArgoDesign()` → `false`) Alert and `ErrorState` keep their original fixed status scales (`blue-*`/`green-*`/`warning-*`/`error-*`), and the legacy brand retains its original status token hues — so legacy status colour is unchanged.
|
|
539
746
|
|
|
540
747
|
**Decorative — stays fixed across brands.** The non-semantic palette that encodes category rather than state: `Badge` decorative colours (`purple`, `cyan`, `teal`, `blue`, …), chart colours, decorative tag palette. **Rule:** keep using primitive scales (`purple-500`, `cyan-500`, …). These are an identity/category contract with the consumer and do not re-skin.
|