@keboola/design 3.1.1 → 3.2.3
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 +427 -0
- package/COMPONENT-API.md +377 -0
- package/README.md +12 -3
- package/dist/index.cjs +102 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -19
- package/dist/index.d.ts +79 -19
- package/dist/index.js +102 -21
- package/dist/index.js.map +1 -1
- package/dist/markdown-editor.cjs.map +1 -1
- package/dist/markdown-editor.js.map +1 -1
- package/dist/mermaid.cjs.map +1 -1
- package/dist/mermaid.js.map +1 -1
- package/dist/styles.css +3 -1
- package/dist/testing.cjs +79 -16
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +80 -17
- package/dist/testing.js.map +1 -1
- package/package.json +10 -6
- package/src/styles.css +22 -1
package/COMPONENT-API.md
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
# @keboola/design — component API reference
|
|
2
|
+
|
|
3
|
+
> **Generated file — do not edit by hand.** Regenerate with `yarn workspace @keboola/design generate:api` (extracts prop signatures from the TypeScript types via `react-docgen-typescript`).
|
|
4
|
+
|
|
5
|
+
This is the prop-signature contract for the most-used components. For _which_ component to reach for, see [`AGENTS.md`](./AGENTS.md) (the decision tree); for live worked examples, open the colocated `*.stories.tsx` in Storybook (`yarn dev --filter=@keboola/design`).
|
|
6
|
+
|
|
7
|
+
Props listed are the ones each component **declares itself**. Components built on a native element (`Button` on `<button>`, `Link` on `<a>`, `Card` on `<div>`, …) also accept that element's standard attributes — called out in each entry's note.
|
|
8
|
+
|
|
9
|
+
## Actions
|
|
10
|
+
|
|
11
|
+
### `Button`
|
|
12
|
+
|
|
13
|
+
Source: `src/components/Button/Button.tsx`
|
|
14
|
+
|
|
15
|
+
> Icons are composed as children, not an `icon` prop. Plus all native `<button>` attributes.
|
|
16
|
+
|
|
17
|
+
| Prop | Type | Required | Default | Description |
|
|
18
|
+
| ----------- | --------------------------------------------------------------------------------------------------------------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
19
|
+
| `argo` | `boolean` | no | `false` | @internal Transitional opt-in; removed once consumers wrap argo sections in `<ThemeProvider>`. |
|
|
20
|
+
| `asChild` | `boolean` | no | `false` | Render as a Radix `Slot` so the parent JSX (e.g. a `react-router` `<Link>`) becomes the button element. |
|
|
21
|
+
| `block` | `boolean \| null` | no | `false` | |
|
|
22
|
+
| `isLoading` | `boolean` | no | `false` | Replaces the leading content with a spinner and disables the button visually. The caller still controls `disabled`. |
|
|
23
|
+
| `size` | `"small" \| "medium" \| "large" \| "icon-lg" \| "icon" \| "icon-sm" \| "icon-xs" \| "icon-2xs" \| "icon-3xs" \| null` | no | `medium` | |
|
|
24
|
+
| `variant` | `"primary" \| "secondary" \| "danger" \| "invisible" \| "outline" \| "inline" \| null` | no | `primary` | |
|
|
25
|
+
|
|
26
|
+
### `ButtonInline`
|
|
27
|
+
|
|
28
|
+
Source: `src/components/Button/ButtonInline.tsx`
|
|
29
|
+
|
|
30
|
+
> Inline text-link-style action. Plus all native `<button>` attributes.
|
|
31
|
+
|
|
32
|
+
| Prop | Type | Required | Default | Description |
|
|
33
|
+
| --------- | ---------------------------------------------- | -------- | ------- | ----------- |
|
|
34
|
+
| `asChild` | `boolean` | no | `false` | |
|
|
35
|
+
| `variant` | `"link" \| "custom" \| "link-reverse" \| null` | no | — | |
|
|
36
|
+
|
|
37
|
+
### `Link`
|
|
38
|
+
|
|
39
|
+
Source: `src/components/Link.tsx`
|
|
40
|
+
|
|
41
|
+
> Defaults `target="_blank"` + `rel="noopener noreferrer"`. Accepts all native `<a>` attributes (`href`, `target`, …) — none are required props.
|
|
42
|
+
|
|
43
|
+
_No own props — see note above for passthrough attributes._
|
|
44
|
+
|
|
45
|
+
## Status surfaces
|
|
46
|
+
|
|
47
|
+
### `Alert`
|
|
48
|
+
|
|
49
|
+
Source: `src/components/Alert/Alert.tsx`
|
|
50
|
+
|
|
51
|
+
> Discriminated by `mode`: `"default"` renders `title`/`children`/`actions`; `"collapsible"` requires `title`+`children` and forbids `actions`. `variant` is a categorical colour contract (`info`/`success`/`warning`/`error`).
|
|
52
|
+
|
|
53
|
+
| Prop | Type | Required | Default | Description |
|
|
54
|
+
| ---------- | ---------------------------- | -------- | --------- | ---------------------------------------------------------------------------------------------- |
|
|
55
|
+
| `actions` | `ReactNode` | no | — | |
|
|
56
|
+
| `argo` | `boolean` | no | — | @internal Transitional opt-in; removed once consumers wrap argo sections in `<ThemeProvider>`. |
|
|
57
|
+
| `hideIcon` | `boolean` | no | `false` | |
|
|
58
|
+
| `icon` | `ReactNode` | no | — | |
|
|
59
|
+
| `mode` | `"collapsible" \| "default"` | no | `default` | |
|
|
60
|
+
| `variant` | `Variant` | no | `info` | |
|
|
61
|
+
|
|
62
|
+
### `Toaster`
|
|
63
|
+
|
|
64
|
+
Source: `src/components/Toast/Toaster.tsx`
|
|
65
|
+
|
|
66
|
+
> Mount ONCE at app root. Wraps `sonner`'s `<Toaster>` — accepts all of sonner's props (`position`, `theme`, `richColors`, …). Fire toasts imperatively with `toast()` / `useNotification`, not by rendering children.
|
|
67
|
+
|
|
68
|
+
_No own props — see note above for passthrough attributes._
|
|
69
|
+
|
|
70
|
+
## Inputs — bare
|
|
71
|
+
|
|
72
|
+
### `TextInput`
|
|
73
|
+
|
|
74
|
+
Source: `src/components/Input/TextInput/TextInput.tsx`
|
|
75
|
+
|
|
76
|
+
> Bare input. Plus all native `<input>` attributes.
|
|
77
|
+
|
|
78
|
+
| Prop | Type | Required | Default | Description |
|
|
79
|
+
| -------------------- | --------------------------------------------------------------- | -------- | ------- | ----------------------------------------------------------------------------------------- |
|
|
80
|
+
| `allowClear` | `boolean` | no | — | |
|
|
81
|
+
| `defaultValue` | `string \| null` | no | — | |
|
|
82
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
83
|
+
| `leftSuffix` | `ReactNode` | no | — | |
|
|
84
|
+
| `onClear` | `(() => void)` | no | — | |
|
|
85
|
+
| `onValueBlur` | `((value: string) => void)` | no | — | Called with the unwrapped string value on blur. For the raw event use `onBlur`. |
|
|
86
|
+
| `onValueChange` | `((value: string) => void)` | no | — | Called with the unwrapped string value on every change. For the raw event use `onChange`. |
|
|
87
|
+
| `prefix` | `ReactNode` | no | — | |
|
|
88
|
+
| `rightSuffix` | `ReactNode` | no | — | |
|
|
89
|
+
| `rightSuffixVariant` | `"default" \| "addon"` | no | — | |
|
|
90
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
91
|
+
| `state` | `"warning" \| "error" \| "default" \| null` | no | — | |
|
|
92
|
+
| `testId` | `string` | no | — | |
|
|
93
|
+
| `type` | `"text" \| "email" \| "date" \| "datetime-local" \| "password"` | no | `text` | |
|
|
94
|
+
| `value` | `string \| null` | no | — | |
|
|
95
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
96
|
+
|
|
97
|
+
### `PasswordInput`
|
|
98
|
+
|
|
99
|
+
Source: `src/blocks/PasswordInput/PasswordInput.tsx`
|
|
100
|
+
|
|
101
|
+
| Prop | Type | Required | Default | Description |
|
|
102
|
+
| --------------------------- | ----------------------------------------------- | -------- | -------------- | ----------- |
|
|
103
|
+
| `allowClear` | `boolean` | no | `false` | |
|
|
104
|
+
| `allowPasswordToggle` | `boolean` | no | `false` | |
|
|
105
|
+
| `autoComplete` | `"off" \| "current-password" \| "new-password"` | no | `new-password` | |
|
|
106
|
+
| `defaultPasswordVisibility` | `boolean` | no | `false` | |
|
|
107
|
+
| `defaultValue` | `string \| null` | no | — | |
|
|
108
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
109
|
+
| `onValueBlur` | `((value: string) => void)` | no | — | |
|
|
110
|
+
| `onValueChange` | `((value: string) => void)` | no | — | |
|
|
111
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
112
|
+
| `state` | `"warning" \| "error" \| "default" \| null` | no | — | |
|
|
113
|
+
| `value` | `string \| null` | no | — | |
|
|
114
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
115
|
+
|
|
116
|
+
### `Textarea`
|
|
117
|
+
|
|
118
|
+
Source: `src/blocks/Textarea/Textarea.tsx`
|
|
119
|
+
|
|
120
|
+
| Prop | Type | Required | Default | Description |
|
|
121
|
+
| -------------------- | --------------------------------------------------------- | -------- | --------- | ----------- |
|
|
122
|
+
| `containerClassName` | `string` | no | — | |
|
|
123
|
+
| `defaultValue` | `string \| null` | no | — | |
|
|
124
|
+
| `disableResize` | `boolean` | no | — | |
|
|
125
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
126
|
+
| `maxRows` | `number \| "unlimited"` | no | — | |
|
|
127
|
+
| `onValueBlur` | `(((value: string) => void) & ((value: string) => void))` | no | — | |
|
|
128
|
+
| `onValueChange` | `(((value: string) => void) & ((value: string) => void))` | no | — | |
|
|
129
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
130
|
+
| `state` | `"warning" \| "error" \| "default" \| null` | no | `default` | |
|
|
131
|
+
| `value` | `string \| null` | no | — | |
|
|
132
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
133
|
+
|
|
134
|
+
## Inputs — FormGroup (label + input + help, shared validation state)
|
|
135
|
+
|
|
136
|
+
### `FormGroup`
|
|
137
|
+
|
|
138
|
+
Source: `src/components/FormGroup/FormGroup.tsx`
|
|
139
|
+
|
|
140
|
+
> `state="error"` propagates to nested `FormGroup.TextInput` / `FormGroup.Help` via context. Subcomponents: `FormGroup.TextInput`, `FormGroup.NumberInput`, `FormGroup.ColorInput`, `FormGroup.Textarea`, `FormGroup.Help`.
|
|
141
|
+
|
|
142
|
+
| Prop | Type | Required | Default | Description |
|
|
143
|
+
| ----------- | --------------- | -------- | ------- | ----------- |
|
|
144
|
+
| `className` | `string` | no | — | |
|
|
145
|
+
| `state` | `ValidityState` | no | — | |
|
|
146
|
+
| `testId` | `string` | no | — | |
|
|
147
|
+
|
|
148
|
+
### `FormGroup.TextInput`
|
|
149
|
+
|
|
150
|
+
Source: `src/components/FormGroup/FormGroupTextInput.tsx`
|
|
151
|
+
|
|
152
|
+
> NO `error` prop — error styling comes from the parent `FormGroup state="error"` via context. `state` is omitted from the surface for the same reason. Plus native `<input>` attributes.
|
|
153
|
+
|
|
154
|
+
| Prop | Type | Required | Default | Description |
|
|
155
|
+
| -------------------- | --------------------------------------------------------------- | -------- | ------- | ----------------------------------------------------------------------------------------- |
|
|
156
|
+
| `allowClear` | `boolean` | no | — | |
|
|
157
|
+
| `defaultValue` | `string \| null` | no | — | |
|
|
158
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
159
|
+
| `leftSuffix` | `ReactNode` | no | — | |
|
|
160
|
+
| `onClear` | `(() => void)` | no | — | |
|
|
161
|
+
| `onValueBlur` | `((value: string) => void)` | no | — | Called with the unwrapped string value on blur. For the raw event use `onBlur`. |
|
|
162
|
+
| `onValueChange` | `((value: string) => void)` | no | — | Called with the unwrapped string value on every change. For the raw event use `onChange`. |
|
|
163
|
+
| `prefix` | `ReactNode` | no | — | |
|
|
164
|
+
| `rightSuffix` | `ReactNode` | no | — | |
|
|
165
|
+
| `rightSuffixVariant` | `"default" \| "addon"` | no | — | |
|
|
166
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
167
|
+
| `testId` | `string` | no | — | |
|
|
168
|
+
| `type` | `"text" \| "email" \| "date" \| "datetime-local" \| "password"` | no | — | |
|
|
169
|
+
| `value` | `string \| null` | no | — | |
|
|
170
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
171
|
+
|
|
172
|
+
### `FormGroup.Help`
|
|
173
|
+
|
|
174
|
+
Source: `src/components/FormGroup/FormGroupHelp.tsx`
|
|
175
|
+
|
|
176
|
+
| Prop | Type | Required | Default | Description |
|
|
177
|
+
| ----------- | -------- | -------- | ------- | ----------- |
|
|
178
|
+
| `className` | `string` | no | — | |
|
|
179
|
+
|
|
180
|
+
## Forms — useAppForm field connectors (accessed as `field.X` inside `form.AppField`)
|
|
181
|
+
|
|
182
|
+
### `field.TextField`
|
|
183
|
+
|
|
184
|
+
Source: `src/blocks/Form/connectors/TextField.tsx`
|
|
185
|
+
|
|
186
|
+
> Value/onChange/onBlur/id/name are wired from the field — do not pass them. Forwards remaining `FormGroup.TextInput` props.
|
|
187
|
+
|
|
188
|
+
| Prop | Type | Required | Default | Description |
|
|
189
|
+
| -------------------- | --------------------------------------------------------------- | -------- | ------- | ----------------------------------------------------------------------------------------- |
|
|
190
|
+
| `allowClear` | `boolean` | no | — | |
|
|
191
|
+
| `className` | `string` | no | — | |
|
|
192
|
+
| `defaultValue` | `string \| null` | no | — | |
|
|
193
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
194
|
+
| `help` | `ReactNode` | no | — | |
|
|
195
|
+
| `label` | `ReactNode` | no | — | |
|
|
196
|
+
| `leftSuffix` | `ReactNode` | no | — | |
|
|
197
|
+
| `onClear` | `(() => void)` | no | — | |
|
|
198
|
+
| `onValueBlur` | `((value: string) => void)` | no | — | Called with the unwrapped string value on blur. For the raw event use `onBlur`. |
|
|
199
|
+
| `onValueChange` | `((value: string) => void)` | no | — | Called with the unwrapped string value on every change. For the raw event use `onChange`. |
|
|
200
|
+
| `prefix` | `ReactNode` | no | — | |
|
|
201
|
+
| `required` | `boolean` | no | — | |
|
|
202
|
+
| `rightSuffix` | `ReactNode` | no | — | |
|
|
203
|
+
| `rightSuffixVariant` | `"default" \| "addon"` | no | — | |
|
|
204
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
205
|
+
| `testId` | `string` | no | — | |
|
|
206
|
+
| `type` | `"text" \| "email" \| "date" \| "datetime-local" \| "password"` | no | — | |
|
|
207
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
208
|
+
|
|
209
|
+
### `field.PasswordField`
|
|
210
|
+
|
|
211
|
+
Source: `src/blocks/Form/connectors/PasswordField.tsx`
|
|
212
|
+
|
|
213
|
+
| Prop | Type | Required | Default | Description |
|
|
214
|
+
| --------------------------- | ----------------------------------------------- | -------- | ------- | ----------- |
|
|
215
|
+
| `allowClear` | `boolean` | no | — | |
|
|
216
|
+
| `allowPasswordToggle` | `boolean` | no | — | |
|
|
217
|
+
| `autoComplete` | `"off" \| "current-password" \| "new-password"` | no | — | |
|
|
218
|
+
| `className` | `string` | no | — | |
|
|
219
|
+
| `defaultPasswordVisibility` | `boolean` | no | — | |
|
|
220
|
+
| `defaultValue` | `string \| null` | no | — | |
|
|
221
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
222
|
+
| `help` | `ReactNode` | no | — | |
|
|
223
|
+
| `label` | `ReactNode` | no | — | |
|
|
224
|
+
| `onValueBlur` | `((value: string) => void)` | no | — | |
|
|
225
|
+
| `required` | `boolean` | no | — | |
|
|
226
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
227
|
+
| `state` | `"warning" \| "error" \| "default" \| null` | no | — | |
|
|
228
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
229
|
+
|
|
230
|
+
### `field.NumberField`
|
|
231
|
+
|
|
232
|
+
Source: `src/blocks/Form/connectors/NumberField.tsx`
|
|
233
|
+
|
|
234
|
+
| Prop | Type | Required | Default | Description |
|
|
235
|
+
| -------------- | -------------------------------------------- | -------- | ------- | ----------- |
|
|
236
|
+
| `className` | `string` | no | — | |
|
|
237
|
+
| `defaultValue` | `number \| null` | no | — | |
|
|
238
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
239
|
+
| `help` | `ReactNode` | no | — | |
|
|
240
|
+
| `label` | `ReactNode` | no | — | |
|
|
241
|
+
| `onValueBlur` | `((value: number \| null) => void)` | no | — | |
|
|
242
|
+
| `prefix` | `ReactNode` | no | — | |
|
|
243
|
+
| `required` | `boolean` | no | — | |
|
|
244
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
245
|
+
| `suffix` | `ReactNode` | no | — | |
|
|
246
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
247
|
+
|
|
248
|
+
### `field.SelectField`
|
|
249
|
+
|
|
250
|
+
Source: `src/blocks/Form/connectors/SelectField.tsx`
|
|
251
|
+
|
|
252
|
+
> `options` is required. Forwards `variant === "argo"` to `Select.Content`.
|
|
253
|
+
|
|
254
|
+
| Prop | Type | Required | Default | Description |
|
|
255
|
+
| ------------------ | -------------------------------------------- | -------- | ------- | ----------- |
|
|
256
|
+
| `options` | `readonly Option[]` | yes | — | |
|
|
257
|
+
| `autoComplete` | `string` | no | — | |
|
|
258
|
+
| `className` | `string` | no | — | |
|
|
259
|
+
| `defaultOpen` | `boolean` | no | — | |
|
|
260
|
+
| `defaultValue` | `string` | no | — | |
|
|
261
|
+
| `dir` | `"rtl" \| "ltr"` | no | — | |
|
|
262
|
+
| `disabled` | `boolean` | no | — | |
|
|
263
|
+
| `form` | `string` | no | — | |
|
|
264
|
+
| `help` | `ReactNode` | no | — | |
|
|
265
|
+
| `label` | `ReactNode` | no | — | |
|
|
266
|
+
| `name` | `string` | no | — | |
|
|
267
|
+
| `onOpenChange` | `((open: boolean) => void)` | no | — | |
|
|
268
|
+
| `open` | `boolean` | no | — | |
|
|
269
|
+
| `placeholder` | `string` | no | — | |
|
|
270
|
+
| `required` | `boolean` | no | — | |
|
|
271
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
272
|
+
| `state` | `"warning" \| "error" \| "default" \| null` | no | — | |
|
|
273
|
+
| `triggerClassName` | `string` | no | — | |
|
|
274
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
275
|
+
|
|
276
|
+
### `field.TextAreaField`
|
|
277
|
+
|
|
278
|
+
Source: `src/blocks/Form/connectors/TextAreaField.tsx`
|
|
279
|
+
|
|
280
|
+
| Prop | Type | Required | Default | Description |
|
|
281
|
+
| -------------------- | --------------------------------------------------------- | -------- | ------- | ----------- |
|
|
282
|
+
| `className` | `string` | no | — | |
|
|
283
|
+
| `containerClassName` | `string` | no | — | |
|
|
284
|
+
| `defaultValue` | `string \| null` | no | — | |
|
|
285
|
+
| `disableResize` | `boolean` | no | — | |
|
|
286
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
287
|
+
| `help` | `ReactNode` | no | — | |
|
|
288
|
+
| `label` | `ReactNode` | no | — | |
|
|
289
|
+
| `maxRows` | `number \| "unlimited"` | no | — | |
|
|
290
|
+
| `onValueBlur` | `(((value: string) => void) & ((value: string) => void))` | no | — | |
|
|
291
|
+
| `required` | `boolean` | no | — | |
|
|
292
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
293
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
294
|
+
|
|
295
|
+
### `field.CheckboxField`
|
|
296
|
+
|
|
297
|
+
Source: `src/blocks/Form/connectors/CheckboxField.tsx`
|
|
298
|
+
|
|
299
|
+
| Prop | Type | Required | Default | Description |
|
|
300
|
+
| ------------------ | ---------------------------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------- |
|
|
301
|
+
| `argo` | `boolean` | no | — | @internal Transitional opt-in; removed once consumers wrap argo sections in `<ThemeProvider>`. |
|
|
302
|
+
| `asChild` | `boolean` | no | — | |
|
|
303
|
+
| `className` | `string` | no | — | |
|
|
304
|
+
| `description` | `ReactNode` | no | — | |
|
|
305
|
+
| `help` | `ReactNode` | no | — | |
|
|
306
|
+
| `label` | `ReactNode` | no | — | |
|
|
307
|
+
| `position` | `"start" \| "end"` | no | — | |
|
|
308
|
+
| `tooltip` | `ReactNode` | no | — | |
|
|
309
|
+
| `tooltipPlacement` | `"top" \| "right" \| "bottom" \| "left"` | no | — | |
|
|
310
|
+
| `variant` | `"default" \| "boxed" \| null` | no | — | |
|
|
311
|
+
|
|
312
|
+
### `field.SwitchField`
|
|
313
|
+
|
|
314
|
+
Source: `src/blocks/Form/connectors/SwitchField.tsx`
|
|
315
|
+
|
|
316
|
+
| Prop | Type | Required | Default | Description |
|
|
317
|
+
| ------------- | ---------------------------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------- |
|
|
318
|
+
| `argo` | `boolean` | no | — | @internal Transitional opt-in; removed once consumers wrap argo sections in `<ThemeProvider>`. |
|
|
319
|
+
| `className` | `string` | no | — | |
|
|
320
|
+
| `description` | `ReactNode` | no | — | |
|
|
321
|
+
| `help` | `ReactNode` | no | — | |
|
|
322
|
+
| `label` | `ReactNode` | no | — | |
|
|
323
|
+
| `position` | `"start" \| "end"` | no | — | |
|
|
324
|
+
| `size` | `"small" \| "medium" \| "large" \| null` | no | — | |
|
|
325
|
+
| `variant` | `"default" \| "boxed" \| null` | no | — | |
|
|
326
|
+
|
|
327
|
+
## Data display — Card (compound)
|
|
328
|
+
|
|
329
|
+
### `Card`
|
|
330
|
+
|
|
331
|
+
Source: `src/components/Card/Card.tsx`
|
|
332
|
+
|
|
333
|
+
> Subcomponents that EXIST: `Card.Header`, `Card.Title`, `Card.Content`. There is NO `Card.Description` and NO `Card.Footer`. Plus native `<div>` attributes.
|
|
334
|
+
|
|
335
|
+
| Prop | Type | Required | Default | Description |
|
|
336
|
+
| ---------- | -------------------------------------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------- |
|
|
337
|
+
| `argo` | `boolean` | no | `false` | @internal Transitional opt-in; removed once consumers wrap argo sections in `<ThemeProvider>`. |
|
|
338
|
+
| `bordered` | `boolean \| null` | no | `false` | |
|
|
339
|
+
| `padding` | `"small" \| "medium" \| "large" \| "none" \| null` | no | `small` | |
|
|
340
|
+
|
|
341
|
+
### `Card.Header`
|
|
342
|
+
|
|
343
|
+
Source: `src/components/Card/CardHeader.tsx`
|
|
344
|
+
|
|
345
|
+
> Native `<div>` attributes only.
|
|
346
|
+
|
|
347
|
+
_No own props — see note above for passthrough attributes._
|
|
348
|
+
|
|
349
|
+
### `Card.Title`
|
|
350
|
+
|
|
351
|
+
Source: `src/components/Card/CardTitle.tsx`
|
|
352
|
+
|
|
353
|
+
> Native `<div>` attributes only.
|
|
354
|
+
|
|
355
|
+
_No own props — see note above for passthrough attributes._
|
|
356
|
+
|
|
357
|
+
### `Card.Content`
|
|
358
|
+
|
|
359
|
+
Source: `src/components/Card/CardContent.tsx`
|
|
360
|
+
|
|
361
|
+
> Native `<div>` attributes only.
|
|
362
|
+
|
|
363
|
+
_No own props — see note above for passthrough attributes._
|
|
364
|
+
|
|
365
|
+
## Theming
|
|
366
|
+
|
|
367
|
+
### `ThemeProvider`
|
|
368
|
+
|
|
369
|
+
Source: `src/components/ThemeProvider/ThemeProvider.tsx`
|
|
370
|
+
|
|
371
|
+
> Wrap the app root. `brand` defaults to the canonical `keboola` brand. Pair with `useTheme()` to read/flip the brand at runtime.
|
|
372
|
+
|
|
373
|
+
| Prop | Type | Required | Default | Description |
|
|
374
|
+
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
375
|
+
| `brand` | `{ id: string; name: string; colors: { foreground: string; background: string; muted: string; mutedForeground: string; border: string; primary: string; primaryForeground: string; destructive: string; destructiveForeground: string; accent?: string; accentForeground?: string; }; ... 6 more ...; ...` | no | — | Brand whose tokens drive the chrome (`@keboola/design`'s semantic CSS variables) inside this provider's subtree. Defaults to the canonical `keboola` brand so existing apps that mount `<ThemeProvider>` without a `brand` prop continue to render as today. |
|
|
376
|
+
| `colorScheme` | `ColorScheme` | no | `light` | Light/dark mode. `'system'` follows `prefers-color-scheme` and updates reactively. Defaults to `'light'` so existing mounts don't drift. Only the ROOT provider applies this (sets `data-theme`, owns the media listener); a nested provider inherits the root's resolved mode. |
|
|
377
|
+
| `setBrand` | `((brand: { id: string; name: string; colors: { foreground: string; background: string; muted: string; mutedForeground: string; border: string; primary: string; primaryForeground: string; destructive: string; destructiveForeground: string; accent?: string; accentForeground?: string; }; ... 6 m...` | no | — | Optional setter exposed through `useTheme()` so consumers can flip brands at runtime (Storybook toolbar, a customer-impersonation switch). |
|
package/README.md
CHANGED
|
@@ -7,9 +7,18 @@ The Keboola design system — shadcn-aligned, Radix-underneath, Lucide icons.
|
|
|
7
7
|
|
|
8
8
|
📦 **Storybook:** https://design-keboola.vercel.app/
|
|
9
9
|
|
|
10
|
-
📖 **Decision tree:** [`AGENTS.md`](./AGENTS.md) — which component to use when.
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
📖 **Decision tree:** [`AGENTS.md`](./AGENTS.md) — which component to use when (ships in the tarball).
|
|
11
|
+
🔧 **Prop signatures:** [`COMPONENT-API.md`](./COMPONENT-API.md) — generated, version-pinned (ships in the tarball).
|
|
12
|
+
|
|
13
|
+
## For agents and consumers: read the installed package's AGENTS.md first
|
|
14
|
+
|
|
15
|
+
Before writing UI with this design system, read the contract that ships with the version you installed:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
node_modules/@keboola/design/AGENTS.md
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
It is version-pinned to your lockfile, so it always matches the components you actually have — read it rather than relying on the published Storybook or these docs, which track `main`. More generally, before building against any installed `@keboola/*` package, read that package's `node_modules/@keboola/<name>/AGENTS.md` when present.
|
|
13
22
|
|
|
14
23
|
## Install
|
|
15
24
|
|
package/dist/index.cjs
CHANGED
|
@@ -5973,7 +5973,16 @@ var BadgeRoot = ({
|
|
|
5973
5973
|
className,
|
|
5974
5974
|
children,
|
|
5975
5975
|
...rest
|
|
5976
|
-
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
5976
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
5977
|
+
"div",
|
|
5978
|
+
{
|
|
5979
|
+
...rest,
|
|
5980
|
+
ref,
|
|
5981
|
+
"data-slot": "badge",
|
|
5982
|
+
className: cn(badgeRootVariants({ variant, color, className })),
|
|
5983
|
+
children
|
|
5984
|
+
}
|
|
5985
|
+
);
|
|
5977
5986
|
var BadgeInner = ({
|
|
5978
5987
|
ref,
|
|
5979
5988
|
// root props
|
|
@@ -10309,18 +10318,43 @@ var AIGradientsSvg = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "0", h
|
|
|
10309
10318
|
] }) });
|
|
10310
10319
|
|
|
10311
10320
|
// src/components/ThemeProvider/brandToCssVars.ts
|
|
10312
|
-
var brandToCssVars = (brand) => {
|
|
10313
|
-
const vars = {
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
"--color-
|
|
10317
|
-
"--color-
|
|
10318
|
-
"--color-
|
|
10319
|
-
"--color-
|
|
10320
|
-
"--color-
|
|
10321
|
-
"--color-
|
|
10322
|
-
"--color-
|
|
10323
|
-
|
|
10321
|
+
var brandToCssVars = (brand, resolvedMode) => {
|
|
10322
|
+
const vars = {};
|
|
10323
|
+
const colors8 = resolvedMode === "dark" ? brand.colorsDark : brand.colors;
|
|
10324
|
+
if (colors8) {
|
|
10325
|
+
vars["--color-background"] = colors8.background;
|
|
10326
|
+
vars["--color-foreground"] = colors8.foreground;
|
|
10327
|
+
vars["--color-muted"] = colors8.muted;
|
|
10328
|
+
vars["--color-muted-foreground"] = colors8.mutedForeground;
|
|
10329
|
+
vars["--color-border"] = colors8.border;
|
|
10330
|
+
vars["--color-primary"] = colors8.primary;
|
|
10331
|
+
vars["--color-primary-foreground"] = colors8.primaryForeground;
|
|
10332
|
+
vars["--color-destructive"] = colors8.destructive;
|
|
10333
|
+
vars["--color-destructive-foreground"] = colors8.destructiveForeground;
|
|
10334
|
+
if (colors8.accent !== void 0) vars["--color-accent"] = colors8.accent;
|
|
10335
|
+
if (colors8.accentForeground !== void 0)
|
|
10336
|
+
vars["--color-accent-foreground"] = colors8.accentForeground;
|
|
10337
|
+
}
|
|
10338
|
+
const sidebar = resolvedMode === "dark" ? brand.sidebarColorsDark : brand.sidebarColors;
|
|
10339
|
+
if (sidebar) {
|
|
10340
|
+
const sidebarVars = {
|
|
10341
|
+
"--color-sidebar": sidebar.sidebar,
|
|
10342
|
+
"--color-sidebar-foreground": sidebar.sidebarForeground,
|
|
10343
|
+
"--color-sidebar-primary": sidebar.sidebarPrimary,
|
|
10344
|
+
"--color-sidebar-primary-foreground": sidebar.sidebarPrimaryForeground,
|
|
10345
|
+
"--color-sidebar-accent": sidebar.sidebarAccent,
|
|
10346
|
+
"--color-sidebar-accent-foreground": sidebar.sidebarAccentForeground,
|
|
10347
|
+
"--color-sidebar-border": sidebar.sidebarBorder,
|
|
10348
|
+
"--color-sidebar-ring": sidebar.sidebarRing
|
|
10349
|
+
};
|
|
10350
|
+
for (const [name, value] of Object.entries(sidebarVars)) {
|
|
10351
|
+
if (value !== void 0) vars[name] = value;
|
|
10352
|
+
}
|
|
10353
|
+
}
|
|
10354
|
+
const fontFamily = brand.typography?.fontFamily?.trim();
|
|
10355
|
+
if (fontFamily) {
|
|
10356
|
+
vars["--font-sans"] = fontFamily;
|
|
10357
|
+
}
|
|
10324
10358
|
if (brand.radii) {
|
|
10325
10359
|
vars["--radius-sm"] = brand.radii.sm;
|
|
10326
10360
|
vars["--radius-md"] = brand.radii.md;
|
|
@@ -10328,12 +10362,50 @@ var brandToCssVars = (brand) => {
|
|
|
10328
10362
|
}
|
|
10329
10363
|
return vars;
|
|
10330
10364
|
};
|
|
10331
|
-
var
|
|
10365
|
+
var DARK_QUERY = "(prefers-color-scheme: dark)";
|
|
10366
|
+
var systemMode = () => {
|
|
10367
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return "light";
|
|
10368
|
+
return window.matchMedia(DARK_QUERY).matches ? "dark" : "light";
|
|
10369
|
+
};
|
|
10370
|
+
var ThemeProvider = ({
|
|
10371
|
+
brand = brandRegistry.keboola,
|
|
10372
|
+
setBrand,
|
|
10373
|
+
colorScheme = "light",
|
|
10374
|
+
children
|
|
10375
|
+
}) => {
|
|
10376
|
+
const parent = react.use(ThemeContext);
|
|
10377
|
+
const isRoot = parent === null;
|
|
10378
|
+
const [systemResolved, setSystemResolved] = react.useState(systemMode);
|
|
10379
|
+
react.useEffect(() => {
|
|
10380
|
+
if (!isRoot || colorScheme !== "system") return;
|
|
10381
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
10382
|
+
const mql = window.matchMedia(DARK_QUERY);
|
|
10383
|
+
const onChange = (event) => setSystemResolved(event.matches ? "dark" : "light");
|
|
10384
|
+
setSystemResolved(mql.matches ? "dark" : "light");
|
|
10385
|
+
mql.addEventListener("change", onChange);
|
|
10386
|
+
return () => mql.removeEventListener("change", onChange);
|
|
10387
|
+
}, [isRoot, colorScheme]);
|
|
10388
|
+
const ownMode = colorScheme === "system" ? systemResolved : colorScheme;
|
|
10389
|
+
const resolvedMode = isRoot ? ownMode : parent.resolvedMode;
|
|
10390
|
+
react.useEffect(() => {
|
|
10391
|
+
if (!isRoot || typeof document === "undefined") return;
|
|
10392
|
+
const root = document.documentElement;
|
|
10393
|
+
const previous = root.getAttribute("data-theme");
|
|
10394
|
+
root.setAttribute("data-theme", resolvedMode);
|
|
10395
|
+
return () => {
|
|
10396
|
+
if (root.getAttribute("data-theme") !== resolvedMode) return;
|
|
10397
|
+
if (previous === null) {
|
|
10398
|
+
root.removeAttribute("data-theme");
|
|
10399
|
+
} else {
|
|
10400
|
+
root.setAttribute("data-theme", previous);
|
|
10401
|
+
}
|
|
10402
|
+
};
|
|
10403
|
+
}, [isRoot, resolvedMode]);
|
|
10332
10404
|
react.useEffect(() => {
|
|
10333
10405
|
if (typeof document === "undefined") return;
|
|
10334
10406
|
const root = document.documentElement;
|
|
10335
10407
|
const previousBrand = root.getAttribute("data-brand");
|
|
10336
|
-
const vars = brandToCssVars(brand);
|
|
10408
|
+
const vars = brandToCssVars(brand, resolvedMode);
|
|
10337
10409
|
const previousValues = {};
|
|
10338
10410
|
for (const name of Object.keys(vars)) {
|
|
10339
10411
|
previousValues[name] = root.style.getPropertyValue(name);
|
|
@@ -10359,8 +10431,8 @@ var ThemeProvider = ({ brand = brandRegistry.keboola, setBrand, children }) => {
|
|
|
10359
10431
|
}
|
|
10360
10432
|
}
|
|
10361
10433
|
};
|
|
10362
|
-
}, [brand]);
|
|
10363
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(ThemeContext, { value: { brand, setBrand }, children: [
|
|
10434
|
+
}, [brand, resolvedMode]);
|
|
10435
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ThemeContext, { value: { brand, setBrand, colorScheme, resolvedMode }, children: [
|
|
10364
10436
|
/* @__PURE__ */ jsxRuntime.jsx(AIGradientsSvg, {}),
|
|
10365
10437
|
children
|
|
10366
10438
|
] });
|
|
@@ -10369,12 +10441,21 @@ var useTheme = () => {
|
|
|
10369
10441
|
const context = react.use(ThemeContext);
|
|
10370
10442
|
if (context === null) {
|
|
10371
10443
|
throw new Error(
|
|
10372
|
-
"useTheme must be used within a <ThemeProvider>. Wrap your app root with <ThemeProvider
|
|
10444
|
+
"useTheme must be used within a <ThemeProvider>. Wrap your app root with <ThemeProvider> from @keboola/design."
|
|
10373
10445
|
);
|
|
10374
10446
|
}
|
|
10375
10447
|
return context;
|
|
10376
10448
|
};
|
|
10377
|
-
var
|
|
10449
|
+
var useColorScheme = () => {
|
|
10450
|
+
const context = react.use(ThemeContext);
|
|
10451
|
+
if (context === null) {
|
|
10452
|
+
throw new Error(
|
|
10453
|
+
"useColorScheme must be used within a <ThemeProvider>. Wrap your app root with <ThemeProvider> from @keboola/design."
|
|
10454
|
+
);
|
|
10455
|
+
}
|
|
10456
|
+
return { colorScheme: context.colorScheme, resolvedMode: context.resolvedMode };
|
|
10457
|
+
};
|
|
10458
|
+
var ThemeDecorator = ({ brand, colorScheme = "light", children }) => /* @__PURE__ */ jsxRuntime.jsx(ThemeProvider, { brand, colorScheme, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tw-bg-background tw-text-foreground", children }) });
|
|
10378
10459
|
var themeToolbarBrands = [brandRegistry.keboola, brandRegistry.keboolaLegacy, brandRegistry.exampleCustomer];
|
|
10379
10460
|
var resolveStorybookBrand = (id) => {
|
|
10380
10461
|
if (typeof id !== "string") return brandRegistry.keboola;
|
|
@@ -10630,7 +10711,7 @@ var PageLayout = Object.assign(Root25, {
|
|
|
10630
10711
|
// src/blocks/PageHeader/styles.ts
|
|
10631
10712
|
var titleRowClass = "tw-flex tw-flex-wrap tw-items-center tw-gap-x-3 tw-gap-y-2";
|
|
10632
10713
|
var titleClass = "tw-m-0 tw-text-3xl tw-font-semibold tw-leading-9 tw-text-foreground tw-break-words";
|
|
10633
|
-
var badgesClass = "tw-flex tw-flex-wrap tw-items-center tw-gap-2";
|
|
10714
|
+
var badgesClass = "tw-flex tw-flex-wrap tw-items-center tw-gap-2 [&_[data-slot=badge]]:tw-rounded-full [&_[data-slot=badge]]:tw-normal-case [&_[data-slot=badge]]:tw-tracking-normal [&_[data-slot=badge]]:tw-py-0";
|
|
10634
10715
|
var descriptionClass = "tw-m-0 tw-max-w-[800px] tw-break-words tw-text-sm tw-leading-5 tw-text-muted-foreground";
|
|
10635
10716
|
var PageHeaderDetail = ({
|
|
10636
10717
|
ref,
|
|
@@ -16284,6 +16365,7 @@ exports.themeToolbarBrands = themeToolbarBrands;
|
|
|
16284
16365
|
exports.toast = toast;
|
|
16285
16366
|
exports.useAppForm = useAppForm;
|
|
16286
16367
|
exports.useAutosizeInput = useAutosizeInput;
|
|
16368
|
+
exports.useColorScheme = useColorScheme;
|
|
16287
16369
|
exports.useInputOtpContext = useInputOtpContext;
|
|
16288
16370
|
exports.useIsLegacyBrand = useIsLegacyBrand;
|
|
16289
16371
|
exports.useMergeDiff = useMergeDiff;
|