@konce-pt/react 0.8.1 → 0.8.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/README.md +381 -27
- package/dist/accordion/llms.txt +56 -0
- package/dist/alert/llms.txt +45 -0
- package/dist/app-shell/app-shell.d.ts +20 -0
- package/dist/app-shell/app-shell.js +24 -0
- package/dist/app-shell/llms.txt +59 -0
- package/dist/auth/auth.d.ts +1 -1
- package/dist/auth/llms.txt +65 -0
- package/dist/autocomplete/autocomplete.d.ts +1 -1
- package/dist/autocomplete/llms.txt +44 -0
- package/dist/avatar/llms.txt +42 -0
- package/dist/avatar-group/avatar-group.d.ts +1 -1
- package/dist/avatar-group/llms.txt +38 -0
- package/dist/badge/llms.txt +38 -0
- package/dist/bottom-sheet/llms.txt +43 -0
- package/dist/breadcrumb/breadcrumb.d.ts +1 -1
- package/dist/breadcrumb/llms.txt +35 -0
- package/dist/button/llms.txt +56 -0
- package/dist/button-group/button-group.d.ts +1 -1
- package/dist/button-group/llms.txt +40 -0
- package/dist/card/llms.txt +60 -0
- package/dist/carousel/carousel.d.ts +1 -1
- package/dist/carousel/llms.txt +47 -0
- package/dist/charts/chart-legend.d.ts +1 -1
- package/dist/charts/llms.txt +57 -0
- package/dist/checkbox/llms.txt +39 -0
- package/dist/chip/llms.txt +41 -0
- package/dist/chips-input/llms.txt +40 -0
- package/dist/clock/llms.txt +50 -0
- package/dist/color-picker/color-picker.d.ts +1 -1
- package/dist/color-picker/llms.txt +40 -0
- package/dist/confirm/llms.txt +44 -0
- package/dist/context-menu/context-menu.d.ts +1 -1
- package/dist/context-menu/llms.txt +39 -0
- package/dist/data-table/data-table-types.d.ts +1 -1
- package/dist/data-table/data-table.d.ts +2 -10
- package/dist/data-table/data-table.js +17 -9
- package/dist/data-table/llms.txt +79 -0
- package/dist/data-view/data-view.d.ts +1 -1
- package/dist/data-view/llms.txt +49 -0
- package/dist/date-range/llms.txt +54 -0
- package/dist/datepicker/llms.txt +74 -0
- package/dist/dialog/llms.txt +51 -0
- package/dist/divider/llms.txt +28 -0
- package/dist/drawer/llms.txt +47 -0
- package/dist/empty/llms.txt +35 -0
- package/dist/fab/llms.txt +37 -0
- package/dist/fieldset/llms.txt +43 -0
- package/dist/file-upload/llms.txt +48 -0
- package/dist/form-field/llms.txt +55 -0
- package/dist/galleria/galleria.d.ts +1 -1
- package/dist/galleria/llms.txt +46 -0
- package/dist/grid/llms.txt +110 -0
- package/dist/icon/icon-registry.d.ts +5 -2
- package/dist/icon/icon-registry.js +5 -3
- package/dist/icon/llms.txt +66 -0
- package/dist/icon-button/llms.txt +38 -0
- package/dist/icons-entry/public-api.d.ts +5 -1
- package/dist/icons-entry/public-api.js +7 -1
- package/dist/image/llms.txt +46 -0
- package/dist/input/llms.txt +54 -0
- package/dist/input-mask/llms.txt +37 -0
- package/dist/input-number/llms.txt +44 -0
- package/dist/input-otp/llms.txt +43 -0
- package/dist/internal/use-breakpoint.js +18 -10
- package/dist/knob/llms.txt +46 -0
- package/dist/listbox/listbox.d.ts +1 -1
- package/dist/listbox/llms.txt +39 -0
- package/dist/map/llms.txt +108 -0
- package/dist/megamenu/llms.txt +48 -0
- package/dist/megamenu/megamenu.d.ts +1 -1
- package/dist/menu/llms.txt +56 -0
- package/dist/menu/menu-item.d.ts +1 -1
- package/dist/menu/menu-rows.d.ts +1 -1
- package/dist/menu/menu.d.ts +1 -1
- package/dist/menu/menu.js +12 -1
- package/dist/menubar/llms.txt +45 -0
- package/dist/menubar/menubar.d.ts +1 -1
- package/dist/meter-group/llms.txt +47 -0
- package/dist/meter-group/meter-group.d.ts +1 -1
- package/dist/order-list/llms.txt +41 -0
- package/dist/paginator/llms.txt +48 -0
- package/dist/paginator/paginator.d.ts +1 -1
- package/dist/paginator/paginator.js +7 -1
- package/dist/panel/llms.txt +55 -0
- package/dist/password/llms.txt +45 -0
- package/dist/pick-list/llms.txt +52 -0
- package/dist/popover/llms.txt +42 -0
- package/dist/progress/llms.txt +47 -0
- package/dist/public-api.d.ts +2 -1
- package/dist/public-api.js +1 -1
- package/dist/radio-group/llms.txt +40 -0
- package/dist/radio-group/radio-group.d.ts +1 -1
- package/dist/rating/llms.txt +43 -0
- package/dist/rich-text/llms.txt +61 -0
- package/dist/rich-text/rich-text.d.ts +1 -1
- package/dist/roadmap/llms.txt +82 -0
- package/dist/scroll-top/llms.txt +41 -0
- package/dist/select/llms.txt +56 -0
- package/dist/select/select.d.ts +1 -1
- package/dist/sidenav/llms.txt +53 -0
- package/dist/skeleton/llms.txt +33 -0
- package/dist/slider/llms.txt +50 -0
- package/dist/speed-dial/llms.txt +46 -0
- package/dist/speed-dial/speed-dial.d.ts +1 -1
- package/dist/spinner/llms.txt +33 -0
- package/dist/split-button/llms.txt +54 -0
- package/dist/split-button/split-button.d.ts +1 -1
- package/dist/splitter/llms.txt +44 -0
- package/dist/stepper/llms.txt +64 -0
- package/dist/switch/llms.txt +42 -0
- package/dist/switch-group/llms.txt +43 -0
- package/dist/switch-group/switch-group.d.ts +1 -1
- package/dist/tabs/llms.txt +48 -0
- package/dist/textarea/llms.txt +40 -0
- package/dist/timeline/llms.txt +53 -0
- package/dist/timeline/timeline.d.ts +1 -1
- package/dist/toast/llms.txt +52 -0
- package/dist/toolbar/llms.txt +45 -0
- package/dist/tooltip/llms.txt +42 -0
- package/dist/tree/llms.txt +45 -0
- package/dist/tree/tree.d.ts +2 -2
- package/package.json +11 -11
package/dist/public-api.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { KptAppShell, useKptAppShell, useKptShellNavToggle } from './app-shell/app-shell.tsx';
|
|
1
|
+
export { KptAppShell, KptNavLabel, KptNavSection, useKptAppShell, useKptShellNavToggle } from './app-shell/app-shell.tsx';
|
|
2
2
|
export type { KptAppShellContextValue, KptAppShellProps, KptSidenavMode } from './app-shell/app-shell.tsx';
|
|
3
3
|
export { KptToolbar, KptToolbarSpacer } from './toolbar/toolbar.tsx';
|
|
4
4
|
export type { KptToolbarProps } from './toolbar/toolbar.tsx';
|
|
@@ -31,6 +31,7 @@ export type { KptSkeletonProps, KptSkeletonVariant } from './skeleton/skeleton.t
|
|
|
31
31
|
export { KptIcon } from './icon/icon.tsx';
|
|
32
32
|
export type { KptIconProps } from './icon/icon.tsx';
|
|
33
33
|
export { getKptIcon, hasKptIcon, registerKptIcons } from './icon/icon-registry.ts';
|
|
34
|
+
export type { KptIconDef, KptIconInput } from '@konce-pt/icons';
|
|
34
35
|
export { KptAvatar } from './avatar/avatar.tsx';
|
|
35
36
|
export type { KptAvatarProps, KptAvatarShape, KptAvatarSize } from './avatar/avatar.tsx';
|
|
36
37
|
export { KptBadge } from './badge/badge.tsx';
|
package/dist/public-api.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* jest wspólna i nie ma tu własnych stylów.
|
|
7
7
|
*/
|
|
8
8
|
// Komponenty — układ
|
|
9
|
-
export { KptAppShell, useKptAppShell, useKptShellNavToggle } from "./app-shell/app-shell.js";
|
|
9
|
+
export { KptAppShell, KptNavLabel, KptNavSection, useKptAppShell, useKptShellNavToggle } from "./app-shell/app-shell.js";
|
|
10
10
|
export { KptToolbar, KptToolbarSpacer } from "./toolbar/toolbar.js";
|
|
11
11
|
export { KptSidenav, KptSidenavFooter } from "./sidenav/sidenav.js";
|
|
12
12
|
// Komponenty — panele
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# KptRadioGroup (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A group of radio buttons, driven by `options`.
|
|
4
|
+
Import: `import { KptRadioGroup } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<div class="kpt-radio-group-host">
|
|
8
|
+
<div class="kpt-radio-group" role="radiogroup">
|
|
9
|
+
<label class="kpt-radio">
|
|
10
|
+
<input type="radio" class="kpt-radio__input" name="kpt-radio-r1" checked>
|
|
11
|
+
<span class="kpt-radio__dot" aria-hidden="true"></span>
|
|
12
|
+
<span class="kpt-radio__label">Pro</span>
|
|
13
|
+
</label>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
## Props
|
|
18
|
+
- `options`: `KptOption<T>[]` — `{ label, value, disabled? }`
|
|
19
|
+
- `value` / `defaultValue` / `onValueChange` — controlled or uncontrolled
|
|
20
|
+
- `name`: string — the form group name; omitted means one is generated
|
|
21
|
+
- `disabled`, `invalid`, `touched`, `onTouch()`
|
|
22
|
+
- `className`, `ref` and every other `<div>` prop go to the host
|
|
23
|
+
|
|
24
|
+
## The shared name does the work
|
|
25
|
+
All inputs share one `name`, and that — not any code in the component — is what makes the browser
|
|
26
|
+
treat them as one group: arrow keys move between them, only one can be checked, and a native form
|
|
27
|
+
submits a single value. The generated name is unique per instance, so two groups on one page never
|
|
28
|
+
interfere.
|
|
29
|
+
|
|
30
|
+
## Examples
|
|
31
|
+
<KptRadioGroup options={plans} value={plan} onValueChange={setPlan} />
|
|
32
|
+
<KptRadioGroup name="plan" options={plans} defaultValue="pro" />
|
|
33
|
+
|
|
34
|
+
## Tokens
|
|
35
|
+
`--kpt-color-primary` (the checked dot), `--kpt-form-field-border`,
|
|
36
|
+
`--kpt-form-field-border-error`.
|
|
37
|
+
|
|
38
|
+
## Accessibility
|
|
39
|
+
The container is `role="radiogroup"` with `aria-invalid` when the error shows; every option is a
|
|
40
|
+
native radio inside a `<label>`, so the label text is clickable and is the accessible name.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ComponentPropsWithRef } from 'react';
|
|
2
2
|
import type { KptOption } from '../shared/types.ts';
|
|
3
3
|
export interface KptRadioGroupProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'onChange' | 'defaultValue'> {
|
|
4
|
-
options?: KptOption<T>[];
|
|
4
|
+
options?: readonly KptOption<T>[];
|
|
5
5
|
value?: T;
|
|
6
6
|
defaultValue?: T;
|
|
7
7
|
onValueChange?: (value: T) => void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# KptRating (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A star rating.
|
|
4
|
+
Import: `import { KptRating } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<div class="kpt-rating-host">
|
|
8
|
+
<div class="kpt-rating" role="slider" aria-valuenow="3" aria-valuemax="5">
|
|
9
|
+
<button class="kpt-rating__star is-filled" aria-label="1 of 5">…</button>
|
|
10
|
+
…count buttons…
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
## Props
|
|
15
|
+
- `value` / `defaultValue` / `onValueChange` — the score as a number, `0` means unrated
|
|
16
|
+
- `count`: number (default 5)
|
|
17
|
+
- `readOnly`: boolean — display only, no clicking and no hover preview
|
|
18
|
+
- `disabled`: boolean
|
|
19
|
+
- `onTouch()` — fired together with a pick
|
|
20
|
+
- `className`, `ref` and every other `<div>` prop go to the host
|
|
21
|
+
|
|
22
|
+
## Clicking a lit star clears the score
|
|
23
|
+
Picking the star that already holds the value writes `0`, so a rating can be taken back without a
|
|
24
|
+
separate "clear" button — the same contract as the Angular port.
|
|
25
|
+
|
|
26
|
+
## Hover beats the value, but only while the pointer is there
|
|
27
|
+
The lit count comes from `hover || value`, so the row previews the score under the cursor and
|
|
28
|
+
falls back to the real value on `mouseleave`. `readOnly` and `disabled` freeze both.
|
|
29
|
+
|
|
30
|
+
## i18n
|
|
31
|
+
Each star is labelled from `rating.star` (`{value}`, `{max}`) — EN `1 of 5`, PL `1 z 5`. Override
|
|
32
|
+
per instance with the `locale` / `dictionary` props.
|
|
33
|
+
|
|
34
|
+
## Examples
|
|
35
|
+
<KptRating value={score} onValueChange={setScore} count={5} />
|
|
36
|
+
<KptRating value={4} readOnly />
|
|
37
|
+
|
|
38
|
+
## Tokens
|
|
39
|
+
Lit stars `--kpt-color-warning`, empty ones `--kpt-color-border-strong`.
|
|
40
|
+
|
|
41
|
+
## Accessibility
|
|
42
|
+
`role="slider"` with `aria-valuenow` / `aria-valuemax`; every star is a real `<button>`, so the
|
|
43
|
+
row is reachable with Tab and each value can be picked from the keyboard.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# KptRichText (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A lightweight rich text editor (contenteditable) with a configurable, sectioned toolbar and table
|
|
4
|
+
editing. The value is HTML.
|
|
5
|
+
Import: `import { KptRichText, type KptRichTool } from '@konce-pt/react';`
|
|
6
|
+
|
|
7
|
+
## Props
|
|
8
|
+
- `value` / `defaultValue` / `onValueChange` — the HTML
|
|
9
|
+
- `toolbar`: `KptRichTool[] | KptRichTool[][]` — a flat list, or groups = sections separated by a
|
|
10
|
+
divider. Leaving a tool or a group out disables it; the full toolbar in 5 sections by default.
|
|
11
|
+
- `placeholder`, `maxListLevel` (6), `disabled`, `invalid`, `touched`, `onTouch()`
|
|
12
|
+
- `className`, `ref` and every other `<div>` prop go to the host
|
|
13
|
+
|
|
14
|
+
## Tools (KptRichTool)
|
|
15
|
+
Clipboard: `copy`, `paste` (sanitized), `pasteText`. Style: `heading` (a panel: Normal + H1–H6).
|
|
16
|
+
Format: `bold`, `italic`, `underline`, `strike`, `ul`, `ol`, `indent`, `outdent`.
|
|
17
|
+
Colours: `foreColor`, `backColor` (a palette + a native colour input). Alignment: `alignLeft`,
|
|
18
|
+
`alignCenter`, `alignRight`, `alignJustify`. Plus `link`, `table`, `clear`, `html`.
|
|
19
|
+
|
|
20
|
+
## The editor is not rendered by React
|
|
21
|
+
`contentEditable` cannot be a React-rendered subtree: re-rendering would wipe the selection and the
|
|
22
|
+
caret on every keystroke. The value is written into the DOM by hand, only when it differs from what
|
|
23
|
+
is already there — so typing never triggers a rewrite, and sanitization runs solely on writes
|
|
24
|
+
coming from the application or the HTML source view.
|
|
25
|
+
|
|
26
|
+
## Everything that enters the DOM is sanitized
|
|
27
|
+
Not just paste: a `value` from a database, a CMS or another user is untrusted too. Tags outside the
|
|
28
|
+
allowlist are unwrapped (the text survives), `on*` handlers and `javascript:` URLs are dropped,
|
|
29
|
+
`style` is filtered declaration by declaration. `sanitize.ts` knows nothing about React.
|
|
30
|
+
|
|
31
|
+
## Lists are indented by our own DOM code
|
|
32
|
+
`execCommand('indent')` produces invalid `ul > ul` in Chrome, so nesting goes through `lists.ts`
|
|
33
|
+
(`li > ul > li`, a sublist inherits the parent's type). Tab and Shift+Tab indent and outdent, but
|
|
34
|
+
only when the operation actually changed something — otherwise Tab stays native and the editor
|
|
35
|
+
never becomes a keyboard trap (WCAG 2.1.2). In a table cell Tab moves to the next cell; a list
|
|
36
|
+
inside the cell takes precedence.
|
|
37
|
+
|
|
38
|
+
## Tables
|
|
39
|
+
Insert from an 8×8 grid popover. Dragging across cells makes a rectangular selection that grows
|
|
40
|
+
until it fully covers merged cells on its edges. Right-click opens the menu: insert and delete rows
|
|
41
|
+
and columns, header row and column (td↔th), cell background, alignment, borders, merge and split,
|
|
42
|
+
delete the table. Dragging a column's right edge changes its width. `tables.ts` holds the grid
|
|
43
|
+
math — also framework-free.
|
|
44
|
+
|
|
45
|
+
## Point-anchored panels
|
|
46
|
+
The table menu and its popovers have no trigger element, so they do not go through `KptOverlay`:
|
|
47
|
+
they are portalled at the cursor coordinates and clamped to the viewport, because CDK's automatic
|
|
48
|
+
repositioning is not there to do it.
|
|
49
|
+
|
|
50
|
+
## Examples
|
|
51
|
+
<KptRichText value={body} onValueChange={setBody} />
|
|
52
|
+
<KptRichText value={body} onValueChange={setBody} toolbar={[['bold', 'italic'], ['ul', 'ol'], ['link']]} />
|
|
53
|
+
|
|
54
|
+
## i18n
|
|
55
|
+
Every label comes from the `richText` namespace, including `richText.menu.*` and
|
|
56
|
+
`richText.border.*`.
|
|
57
|
+
|
|
58
|
+
## Note
|
|
59
|
+
Formatting goes through `document.execCommand`. The API is formally deprecated, but it is the only
|
|
60
|
+
route into the browser's native undo stack (Ctrl+Z); replacing it would mean writing our own
|
|
61
|
+
history, that is, a different editor.
|
|
@@ -2,7 +2,7 @@ import { type ComponentPropsWithRef } from 'react';
|
|
|
2
2
|
import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
|
|
3
3
|
export type KptRichTool = 'copy' | 'paste' | 'pasteText' | 'heading' | 'bold' | 'italic' | 'underline' | 'strike' | 'ul' | 'ol' | 'indent' | 'outdent' | 'foreColor' | 'backColor' | 'alignLeft' | 'alignCenter' | 'alignRight' | 'alignJustify' | 'link' | 'table' | 'clear' | 'html';
|
|
4
4
|
/** Pasek: płaska lista narzędzi albo grupy — sekcje rozdzielone separatorem. */
|
|
5
|
-
export type KptRichToolbar = KptRichTool[] | KptRichTool[][];
|
|
5
|
+
export type KptRichToolbar = readonly KptRichTool[] | readonly (readonly KptRichTool[])[];
|
|
6
6
|
export interface KptRichTextProps extends Omit<ComponentPropsWithRef<'div'>, 'onChange' | 'children'>, KptI18nOverrides {
|
|
7
7
|
/** Wartość jako HTML. */
|
|
8
8
|
value?: string;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# KptRoadmap (@konce-pt/react/roadmap)
|
|
2
|
+
|
|
3
|
+
Rows of tasks on a horizontal timeline (a Gantt-style view). The label column and the axis header
|
|
4
|
+
stay stuck while scrolling, the bars are positioned as percentages, and dependencies are drawn by
|
|
5
|
+
an SVG layer.
|
|
6
|
+
Import: `import { KptRoadmap, type KptRoadmapConfig } from '@konce-pt/react/roadmap';`
|
|
7
|
+
|
|
8
|
+
A separate subpath, because the geometry, the styles and the data model are heavy next to the
|
|
9
|
+
library core and not every app needs them.
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
- `config`: `KptRoadmapConfig` (required) — the data and every option
|
|
13
|
+
- `scrollHeight`: string | null — a fixed height, e.g. '420px'; enables the sticky header
|
|
14
|
+
- `labelWidth`: string — the width of the label column (default '14rem')
|
|
15
|
+
- `cellWidthPx`: number | null — the width of an axis column; without it the value depends on the zoom
|
|
16
|
+
- `showToday`: boolean — the "today" marker (default true)
|
|
17
|
+
- `virtual`: boolean — renders only the visible rows; worth it from roughly 100 rows up
|
|
18
|
+
- `collapsedGroups` / `defaultCollapsedGroups` / `onCollapsedGroupsChange` — the ids of collapsed
|
|
19
|
+
groups, controlled or uncontrolled
|
|
20
|
+
- `onItemClick(item)`, `onRangeChange(range)` — the click on a bar and the computed axis range
|
|
21
|
+
- `className`, `ref` and every other `<div>` prop go to the host
|
|
22
|
+
- `locale`, `dictionary` — the per-component i18n overrides
|
|
23
|
+
|
|
24
|
+
## The config is the API
|
|
25
|
+
`{ items, groups?, links?, zoom?, range?, locale?, today?, timeline?, rows?, dependencies?,
|
|
26
|
+
interaction? }` — the same shape the Angular port takes, because both read it from
|
|
27
|
+
`@konce-pt/roadmap`. An item is `{ id, label, start, end?, sublabel?, variant?, color?, progress?,
|
|
28
|
+
groupId?, dependsOn?, milestone?, meta? }`; no `end` means a milestone diamond, and a bare string in
|
|
29
|
+
`dependsOn` is an 'FS' dependency.
|
|
30
|
+
|
|
31
|
+
## The geometry lives in a package
|
|
32
|
+
The calendar arithmetic, the timeline bands and cells, the row layout, lane packing and the
|
|
33
|
+
dependency polylines come from `@konce-pt/roadmap` — pure functions with `node --test` coverage,
|
|
34
|
+
shared with the Angular port. The component only turns them into DOM and SVG.
|
|
35
|
+
|
|
36
|
+
## Collapsing has two sources
|
|
37
|
+
`group.collapsed` in the data and the `collapsedGroups` prop are kept apart, so an app that controls
|
|
38
|
+
the state keeps control while a group collapsed declaratively still works. Expanding removes the
|
|
39
|
+
group from both — otherwise `group.collapsed` would immediately collapse it again. The declarative
|
|
40
|
+
set is recomputed during render whenever the `groups` array identity changes; an effect would do it
|
|
41
|
+
one render too late, that is after the group flashed open.
|
|
42
|
+
|
|
43
|
+
## Measuring, not guessing
|
|
44
|
+
The SVG dependency layer works in pixels, so the lane width and the scroll viewport are measured
|
|
45
|
+
with a `ResizeObserver` in a layout effect. Before the first measurement no link is drawn — half a
|
|
46
|
+
frame without arrows beats arrows in the wrong place.
|
|
47
|
+
|
|
48
|
+
## Examples
|
|
49
|
+
<KptRoadmap config={{ zoom: 'week', items }} scrollHeight="420px" />
|
|
50
|
+
|
|
51
|
+
const [collapsed, setCollapsed] = useState<string[]>(['analytics']);
|
|
52
|
+
|
|
53
|
+
<KptRoadmap
|
|
54
|
+
config={roadmap}
|
|
55
|
+
collapsedGroups={collapsed}
|
|
56
|
+
onCollapsedGroupsChange={setCollapsed}
|
|
57
|
+
onItemClick={(item) => open(item.id)}
|
|
58
|
+
/>
|
|
59
|
+
|
|
60
|
+
## Behavior
|
|
61
|
+
- The axis is computed in local time; week numbering follows ISO 8601.
|
|
62
|
+
- The 'uniform' position mode gives columns of equal width despite DST changes and months of
|
|
63
|
+
different lengths; inside a column the position is interpolated linearly.
|
|
64
|
+
- An item extending past the axis range is cut off on the side where it is clipped.
|
|
65
|
+
- Tooltips use `KptTooltip`, mostly with `onlyWhenTruncated`: a tooltip shows what cannot be seen.
|
|
66
|
+
Milestones and header cells carry one unconditionally — a diamond shows no text at all.
|
|
67
|
+
- Dependencies: 'FS' (the default), 'SS', 'FF', 'SF'; violated ones are drawn as a dashed line in
|
|
68
|
+
the danger color.
|
|
69
|
+
- Entries with an invalid date or a duplicate `id` are skipped, with a warning in a development build.
|
|
70
|
+
|
|
71
|
+
## i18n
|
|
72
|
+
The `roadmap.*` keys: `empty`, `week`, `today`, `milestone`, `expandGroup`, `collapseGroup`.
|
|
73
|
+
Month and weekday names come from `Intl` per `config.locale` or the locale from `KptI18nProvider`.
|
|
74
|
+
|
|
75
|
+
## Tokens
|
|
76
|
+
Surfaces `--kpt-color-surface{,-variant,-sunken,-hover}`, outlines `--kpt-color-border{,-strong}`,
|
|
77
|
+
bars per variant `--kpt-color-{primary,success,danger,warning,info,muted}`.
|
|
78
|
+
Sizes: `--kpt-roadmap-label-w`, `--kpt-roadmap-cell-w` (both can be overridden from outside).
|
|
79
|
+
|
|
80
|
+
## Accessibility
|
|
81
|
+
Group toggles are real buttons with `aria-expanded`; bars and milestones are focusable and answer
|
|
82
|
+
Enter and Space like a click. The background grid and the dependency layer are `aria-hidden`.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# KptScrollTop (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A "back to top" button — appears once the scroll passes `threshold` px.
|
|
4
|
+
Import: `import { KptScrollTop } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<div class="kpt-scroll-top-host">
|
|
8
|
+
<button class="kpt-scroll-top is-visible" tabindex="0" aria-label="Scroll to top">…</button>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
- `threshold`: number (px, default 300)
|
|
13
|
+
- `target`: string | null — a selector for the scrolled element; the window by default
|
|
14
|
+
- `className`, `ref` and every other `<div>` prop go to the host
|
|
15
|
+
|
|
16
|
+
## `target` is not optional in a shell
|
|
17
|
+
Whenever the app scrolls a container rather than the page — an `KptAppShell` layout, for instance —
|
|
18
|
+
the window never scrolls and the button would never appear. Point it at the container:
|
|
19
|
+
`target=".kpt-app-shell__content"`.
|
|
20
|
+
|
|
21
|
+
## The element is found in an effect
|
|
22
|
+
The scrolled container is queried after mount, not during render: it is often mounted after this
|
|
23
|
+
component, and on the server it does not exist at all. The listener is removed on cleanup, and the
|
|
24
|
+
position is read once on mount, because the page may already be scrolled (arriving at an anchor).
|
|
25
|
+
|
|
26
|
+
## The hidden button stays in the DOM
|
|
27
|
+
It fades in and out, so hiding it is `tabindex="-1"` plus the CSS class, not a conditional render —
|
|
28
|
+
otherwise the animation would have nothing to run on.
|
|
29
|
+
|
|
30
|
+
## Examples
|
|
31
|
+
<KptScrollTop threshold={300} />
|
|
32
|
+
<KptScrollTop target="#panel" />
|
|
33
|
+
|
|
34
|
+
## i18n
|
|
35
|
+
The label comes from `scrollTop.label`.
|
|
36
|
+
|
|
37
|
+
## Tokens
|
|
38
|
+
`--kpt-color-primary` / `--kpt-color-on-primary`, `--kpt-elevation-3`.
|
|
39
|
+
|
|
40
|
+
## Accessibility
|
|
41
|
+
A real `<button>` with a label from the dictionary; while hidden it is out of the tab order.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# KptSelect (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A select on its own panel. Variants: single, with a search box (`filter`) and multiple
|
|
4
|
+
(`multiple`, chips). Import: `import { KptSelect } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<span class="kpt-select-host">
|
|
8
|
+
<div class="kpt-select" role="combobox" tabindex="0" aria-expanded="false">
|
|
9
|
+
<div class="kpt-select__value"><span class="kpt-select__placeholder">Choose…</span></div>
|
|
10
|
+
<span class="kpt-icon-host kpt-select__chevron">…</span>
|
|
11
|
+
</div>
|
|
12
|
+
</span>
|
|
13
|
+
<!-- while open, in a portal on document.body: -->
|
|
14
|
+
<div class="kpt-select__panel"><ul class="kpt-select__list" role="listbox">…</ul></div>
|
|
15
|
+
|
|
16
|
+
## Props
|
|
17
|
+
- `options`: `KptOption<T>[]` — `{ label, value, disabled? }`
|
|
18
|
+
- `value` / `defaultValue` / `onValueChange` — a single `T`, or `T[]` in `multiple` mode
|
|
19
|
+
- `open` / `defaultOpen` / `onOpenChange` — the panel state, so the list can be opened from outside
|
|
20
|
+
- `placeholder`, `multiple`, `filter`, `disabled`, `invalid`, `touched`
|
|
21
|
+
- `onTouch()` — fires when the panel closes or the trigger loses focus
|
|
22
|
+
- `locale`, `dictionary` — i18n overrides for this component only
|
|
23
|
+
- `className`, `ref` and every other `<div>` prop go to the trigger
|
|
24
|
+
|
|
25
|
+
## The panel is a portal
|
|
26
|
+
The Angular port stands on CDK Overlay, which React has no equivalent of. The panel renders into a
|
|
27
|
+
portal on `document.body` with the same behaviour: the trigger's width, a position below it that
|
|
28
|
+
flips above when there is no room, a transparent backdrop that closes on click, and Escape. It
|
|
29
|
+
follows scrolling and resizing, so the panel never drifts away from its trigger.
|
|
30
|
+
|
|
31
|
+
## Keyboard
|
|
32
|
+
On the trigger: ArrowDown, Enter or Space open the list; Escape closes it. In the panel: ArrowUp and
|
|
33
|
+
ArrowDown move the active option, Enter picks it, Escape closes. Hovering an option makes it active,
|
|
34
|
+
so the mouse and the keyboard share one highlight.
|
|
35
|
+
|
|
36
|
+
## Inside KptFormField
|
|
37
|
+
There is no native field for the wrapper to read, so the select reports its filled state with
|
|
38
|
+
`useKptFormFieldFilled` — the float label then floats over the chosen value.
|
|
39
|
+
|
|
40
|
+
## i18n
|
|
41
|
+
`select.search` (the search placeholder), `select.noResults` (the empty list),
|
|
42
|
+
`select.removeChip` (the chip's remove button).
|
|
43
|
+
|
|
44
|
+
## Examples
|
|
45
|
+
<KptSelect options={roles} value={role} onValueChange={setRole} placeholder="Choose…" />
|
|
46
|
+
<KptSelect options={roles} value={role} onValueChange={setRole} filter />
|
|
47
|
+
<KptSelect options={tags} value={tags} onValueChange={setTags} multiple filter />
|
|
48
|
+
|
|
49
|
+
## Tokens
|
|
50
|
+
`--kpt-form-field-{bg,text,placeholder,border,border-hover,border-focus,border-error,radius}`,
|
|
51
|
+
`--kpt-color-surface-raised` and `--kpt-elevation-3` for the panel, `--kpt-z-overlay` for the layer.
|
|
52
|
+
|
|
53
|
+
## Accessibility
|
|
54
|
+
The trigger is `role="combobox"` with `aria-expanded` and `tabindex="0"`; the list is
|
|
55
|
+
`role="listbox"` with `aria-multiselectable`, and each option is `role="option"` with
|
|
56
|
+
`aria-selected`.
|
package/dist/select/select.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type ComponentPropsWithRef } from 'react';
|
|
|
2
2
|
import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
|
|
3
3
|
import type { KptOption } from '../shared/types.ts';
|
|
4
4
|
export interface KptSelectProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'children' | 'onChange' | 'defaultValue'>, KptI18nOverrides {
|
|
5
|
-
options?: KptOption<T>[];
|
|
5
|
+
options?: readonly KptOption<T>[];
|
|
6
6
|
placeholder?: string;
|
|
7
7
|
/** Wybór wielokrotny — wartością jest tablica, a wybrane opcje pokazują się jako chipy. */
|
|
8
8
|
multiple?: boolean;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# KptSidenav (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
The side panel — a styled navigation container, usually handed to the `sidenav` slot of
|
|
4
|
+
`<KptAppShell>`. Import: `import { KptSidenav } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<div class="kpt-sidenav">
|
|
8
|
+
<div class="kpt-sidenav__scroll">…children…</div>
|
|
9
|
+
<div kptsidenavfooter="">…footer…</div>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
## Props
|
|
13
|
+
- `className`, `ref` and every other `<div>` prop go to the panel root
|
|
14
|
+
|
|
15
|
+
## Sub-components
|
|
16
|
+
- `KptSidenav.Footer` — lifted OUT of the scrolling area, so it stays pinned to the bottom of the
|
|
17
|
+
panel however long the navigation gets (a user block, an app version, a theme switch). It carries
|
|
18
|
+
the `kptsidenavfooter` attribute the shared SCSS targets.
|
|
19
|
+
|
|
20
|
+
Default children land in the scrolling area. Plain `<a>` elements inside it are styled by the
|
|
21
|
+
stylesheet already — padding, radius, a hover background — so a simple link list needs no classes.
|
|
22
|
+
|
|
23
|
+
## Examples
|
|
24
|
+
<KptSidenav>
|
|
25
|
+
<a href="/">Dashboard</a>
|
|
26
|
+
<a href="/settings">Settings</a>
|
|
27
|
+
<KptSidenav.Footer>v0.7.12</KptSidenav.Footer>
|
|
28
|
+
</KptSidenav>
|
|
29
|
+
|
|
30
|
+
<KptAppShell sidenav={<KptSidenav>…</KptSidenav>} toolbar={…}>…</KptAppShell>
|
|
31
|
+
|
|
32
|
+
## Rail mode
|
|
33
|
+
When `<KptAppShell sidenavRailBreakpoint>` is on, a closed panel narrows instead of hiding. Mark the
|
|
34
|
+
labels inside your menu items with `<KptNavLabel>` (and group headings with `<KptNavSection>`) and
|
|
35
|
+
the shell will clip them the accessible way (visually hidden, still in the accessibility tree).
|
|
36
|
+
|
|
37
|
+
import { KptNavLabel, KptNavSection } from '@konce-pt/react';
|
|
38
|
+
|
|
39
|
+
<KptNavSection>Reports</KptNavSection>
|
|
40
|
+
<a href="/dashboard"><KptIcon name="layout-dashboard" /> <KptNavLabel>Dashboard</KptNavLabel></a>
|
|
41
|
+
|
|
42
|
+
Both render the lowercase attribute the shared SCSS targets (`kptnavlabel` / `kptnavsection`);
|
|
43
|
+
attribute names in CSS selectors are case-insensitive in HTML, so one rule serves both ports. Use
|
|
44
|
+
the components rather than writing the attribute by hand — JSX rejects an unknown camelCase
|
|
45
|
+
attribute, and a hand-written one is easy to misspell.
|
|
46
|
+
|
|
47
|
+
## Tokens
|
|
48
|
+
`--kpt-sidenav-bg`, `--kpt-sidenav-fg`, `--kpt-sidenav-border`, plus `--kpt-sidenav-width` and
|
|
49
|
+
`--kpt-sidenav-rail-width`, which are read by `KptAppShell`.
|
|
50
|
+
|
|
51
|
+
## Accessibility
|
|
52
|
+
The panel itself is a plain container — the `role="navigation"` and the label live on the shell's
|
|
53
|
+
`<aside>` around it, so a sidenav used outside `KptAppShell` needs its own landmark.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# KptSkeleton (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A loading placeholder with a shimmer animation.
|
|
4
|
+
Import: `import { KptSkeleton } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<span class="kpt-skeleton-host" aria-hidden="true">
|
|
8
|
+
<span class="kpt-skeleton kpt-skeleton--text" style="width: 100%"></span>
|
|
9
|
+
<span class="kpt-skeleton kpt-skeleton--text" style="width: 60%"></span>
|
|
10
|
+
</span>
|
|
11
|
+
|
|
12
|
+
## Props
|
|
13
|
+
- `variant`: 'text' | 'circle' | 'rect' (default 'rect')
|
|
14
|
+
- `width`: string (default '100%'), `height`: string (default '1rem')
|
|
15
|
+
- `lines`: number (default 3) — the number of lines in the `text` variant
|
|
16
|
+
- `className`, `ref` and every other `<span>` prop go to the host
|
|
17
|
+
|
|
18
|
+
## The last line is shorter
|
|
19
|
+
In the `text` variant the last line is 60% wide. A stack of full-width rectangles does not read as
|
|
20
|
+
a paragraph, and a paragraph is exactly what it stands in for.
|
|
21
|
+
|
|
22
|
+
## Examples
|
|
23
|
+
<KptSkeleton variant="text" lines={3} />
|
|
24
|
+
<KptSkeleton variant="circle" width="3rem" height="3rem" />
|
|
25
|
+
<KptSkeleton variant="rect" width="100%" height="8rem" />
|
|
26
|
+
|
|
27
|
+
## Tokens
|
|
28
|
+
`--kpt-color-muted` and `--kpt-color-surface` for the shimmer gradient, `--kpt-radius-md`.
|
|
29
|
+
|
|
30
|
+
## Accessibility
|
|
31
|
+
The host is `aria-hidden="true"` — a placeholder has nothing to announce, and reading out empty
|
|
32
|
+
boxes only adds noise. Announce the wait with `KptSpinner` or `KptProgress` next to it when it
|
|
33
|
+
matters.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# KptSlider (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A value slider. In `range` mode it exposes two handles and the value becomes a `[min, max]` pair.
|
|
4
|
+
Import: `import { KptSlider } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<!-- single -->
|
|
8
|
+
<div class="kpt-slider-host">
|
|
9
|
+
<input class="kpt-slider__input" type="range" style="--_pct: 40%">
|
|
10
|
+
<span class="kpt-slider__value">40</span>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<!-- range -->
|
|
14
|
+
<div class="kpt-slider-host">
|
|
15
|
+
<div class="kpt-slider__range">
|
|
16
|
+
<div class="kpt-slider__rail"></div>
|
|
17
|
+
<div class="kpt-slider__fill" style="left: 20%; right: 20%"></div>
|
|
18
|
+
<input class="kpt-slider__thumb kpt-slider__thumb--low" type="range" aria-label="Minimum">
|
|
19
|
+
<input class="kpt-slider__thumb kpt-slider__thumb--high" type="range" aria-label="Maksimum">
|
|
20
|
+
</div>
|
|
21
|
+
<span class="kpt-slider__value">20–80</span>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
## Props
|
|
25
|
+
- `value` / `defaultValue` / `onValueChange` — `number`, or `[number, number]` in `range` mode
|
|
26
|
+
- `range`: boolean — two handles
|
|
27
|
+
- `min` (default 0), `max` (default 100), `step` (default 1)
|
|
28
|
+
- `showValue`: boolean — prints the current value next to the slider
|
|
29
|
+
- `disabled`, `onTouch()`
|
|
30
|
+
- `className`, `ref` and every other `<div>` prop go to the host
|
|
31
|
+
|
|
32
|
+
## Native inputs all the way down
|
|
33
|
+
Both handles are native `<input type="range">` stacked on each other, so the keyboard, touch
|
|
34
|
+
gestures and screen readers work with no handling of our own — there is no custom drag logic to get
|
|
35
|
+
wrong. The rail and the fill are decoration underneath them.
|
|
36
|
+
|
|
37
|
+
## The handles cannot cross
|
|
38
|
+
Moving the low handle above the high one clamps it to the high value, and the other way round. The
|
|
39
|
+
pair therefore always stays ordered, so the fill never inverts.
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
<KptSlider value={volume} onValueChange={(v) => setVolume(v as number)} showValue />
|
|
43
|
+
<KptSlider range value={span} onValueChange={(v) => setSpan(v as [number, number])} showValue />
|
|
44
|
+
|
|
45
|
+
## Tokens
|
|
46
|
+
`--kpt-color-primary` (the fill and the handles), `--kpt-color-muted` (the rail).
|
|
47
|
+
|
|
48
|
+
## Accessibility
|
|
49
|
+
Native range inputs carry their own role and value semantics. In `range` mode each handle gets an
|
|
50
|
+
`aria-label` telling the two apart, because "slider" twice would say nothing.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# KptSpeedDial (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A speed dial — a FAB that fans out a set of actions. Built on `KptFab`.
|
|
4
|
+
Import: `import { KptSpeedDial } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<div class="kpt-speed-dial-host">
|
|
8
|
+
<div class="kpt-speed-dial is-open" data-direction="up">
|
|
9
|
+
<div class="kpt-speed-dial__actions">
|
|
10
|
+
<button class="kpt-speed-dial__action" aria-label="Upload file">…</button>…
|
|
11
|
+
</div>
|
|
12
|
+
<span class="kpt-fab-host"><button class="kpt-fab">…</button></span>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="kpt-speed-dial__backdrop"></div>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
## Props
|
|
18
|
+
- `items`: `KptMenuItem<T>[]` — the actions (`label`, `icon`, `disabled`)
|
|
19
|
+
- `direction`: `'up'` (default) | `'down'` | `'left'` | `'right'`
|
|
20
|
+
- `icon`: string (default `'plus'`) — the SCSS rotates it 45° when open
|
|
21
|
+
- `ariaLabel`: string — the closed-state label; without it `speedDial.actions`
|
|
22
|
+
- `onSelect(item)` — fired on pick; a disabled item fires nothing
|
|
23
|
+
- `className`, `ref` and every other `<div>` prop go to the host
|
|
24
|
+
|
|
25
|
+
## The label follows the state
|
|
26
|
+
Closed, the FAB is named "actions"; open, it is `common.close`, because that is what pressing it
|
|
27
|
+
does then. One button, two meanings, and a screen reader hears the current one.
|
|
28
|
+
|
|
29
|
+
## The backdrop exists only while open
|
|
30
|
+
It is rendered conditionally, not hidden with CSS: a transparent, full-screen layer that is always
|
|
31
|
+
present would swallow clicks meant for the page underneath.
|
|
32
|
+
|
|
33
|
+
## Examples
|
|
34
|
+
<KptSpeedDial items={actions} onSelect={onAction} />
|
|
35
|
+
<KptSpeedDial items={actions} direction="left" icon="dots" ariaLabel="More" />
|
|
36
|
+
|
|
37
|
+
## i18n
|
|
38
|
+
`speedDial.actions` and `common.close`; `locale` and `dictionary` override them per instance.
|
|
39
|
+
|
|
40
|
+
## Tokens
|
|
41
|
+
The main button comes from `KptFab`; the actions use `--kpt-color-surface-raised` and
|
|
42
|
+
`--kpt-elevation-2`.
|
|
43
|
+
|
|
44
|
+
## Accessibility
|
|
45
|
+
Every action is a `<button>` labelled with its own `label`, so the fan works from the keyboard even
|
|
46
|
+
when the actions show icons only.
|
|
@@ -3,7 +3,7 @@ import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
|
|
|
3
3
|
import type { KptMenuItem } from '../menu/menu-item.ts';
|
|
4
4
|
export type KptSpeedDialDirection = 'up' | 'down' | 'left' | 'right';
|
|
5
5
|
export interface KptSpeedDialProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'onSelect'>, KptI18nOverrides {
|
|
6
|
-
items?: KptMenuItem<T>[];
|
|
6
|
+
items?: readonly KptMenuItem<T>[];
|
|
7
7
|
direction?: KptSpeedDialDirection;
|
|
8
8
|
/** Ikona głównego przycisku; SCSS obraca ją o 45° po otwarciu. */
|
|
9
9
|
icon?: string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# KptSpinner (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A loading indicator. Import: `import { KptSpinner } from '@konce-pt/react';`
|
|
4
|
+
|
|
5
|
+
## DOM
|
|
6
|
+
<span class="kpt-spinner-host">
|
|
7
|
+
<span class="kpt-spinner" role="status" aria-label="Loading" style="width: 1.75rem; height: 1.75rem"></span>
|
|
8
|
+
</span>
|
|
9
|
+
|
|
10
|
+
## Props
|
|
11
|
+
- `size`: string (default '1.5rem') — a CSS length, applied to width and height
|
|
12
|
+
- `label`: string — the accessibility label; omitted falls back to `spinner.label`
|
|
13
|
+
- `locale`, `dictionary` — i18n overrides for this component only
|
|
14
|
+
- `className`, `ref` and every other `<span>` prop go to the host
|
|
15
|
+
|
|
16
|
+
## The colour is inherited
|
|
17
|
+
The spinner draws itself in `currentColor`, so it takes the colour of the text around it. Put it
|
|
18
|
+
inside a button and it matches the button; put it on a card and it matches the card. There is no
|
|
19
|
+
colour prop, on purpose.
|
|
20
|
+
|
|
21
|
+
## Examples
|
|
22
|
+
<KptSpinner />
|
|
23
|
+
<KptSpinner size="2rem" label="Loading data" />
|
|
24
|
+
<KptButton disabled><KptSpinner size="1em" /> Saving…</KptButton>
|
|
25
|
+
|
|
26
|
+
## Tokens
|
|
27
|
+
None of its own — the size comes from the prop, the colour from `currentColor`, the speed from
|
|
28
|
+
`--kpt-motion-duration-*`.
|
|
29
|
+
|
|
30
|
+
## Accessibility
|
|
31
|
+
`role="status"` with a label, so a screen reader announces the wait politely. For a longer
|
|
32
|
+
operation prefer `KptProgress` — a spinner says "something is happening", a progress bar says how
|
|
33
|
+
much is left.
|