@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.
Files changed (123) hide show
  1. package/README.md +381 -27
  2. package/dist/accordion/llms.txt +56 -0
  3. package/dist/alert/llms.txt +45 -0
  4. package/dist/app-shell/app-shell.d.ts +20 -0
  5. package/dist/app-shell/app-shell.js +24 -0
  6. package/dist/app-shell/llms.txt +59 -0
  7. package/dist/auth/auth.d.ts +1 -1
  8. package/dist/auth/llms.txt +65 -0
  9. package/dist/autocomplete/autocomplete.d.ts +1 -1
  10. package/dist/autocomplete/llms.txt +44 -0
  11. package/dist/avatar/llms.txt +42 -0
  12. package/dist/avatar-group/avatar-group.d.ts +1 -1
  13. package/dist/avatar-group/llms.txt +38 -0
  14. package/dist/badge/llms.txt +38 -0
  15. package/dist/bottom-sheet/llms.txt +43 -0
  16. package/dist/breadcrumb/breadcrumb.d.ts +1 -1
  17. package/dist/breadcrumb/llms.txt +35 -0
  18. package/dist/button/llms.txt +56 -0
  19. package/dist/button-group/button-group.d.ts +1 -1
  20. package/dist/button-group/llms.txt +40 -0
  21. package/dist/card/llms.txt +60 -0
  22. package/dist/carousel/carousel.d.ts +1 -1
  23. package/dist/carousel/llms.txt +47 -0
  24. package/dist/charts/chart-legend.d.ts +1 -1
  25. package/dist/charts/llms.txt +57 -0
  26. package/dist/checkbox/llms.txt +39 -0
  27. package/dist/chip/llms.txt +41 -0
  28. package/dist/chips-input/llms.txt +40 -0
  29. package/dist/clock/llms.txt +50 -0
  30. package/dist/color-picker/color-picker.d.ts +1 -1
  31. package/dist/color-picker/llms.txt +40 -0
  32. package/dist/confirm/llms.txt +44 -0
  33. package/dist/context-menu/context-menu.d.ts +1 -1
  34. package/dist/context-menu/llms.txt +39 -0
  35. package/dist/data-table/data-table-types.d.ts +1 -1
  36. package/dist/data-table/data-table.d.ts +2 -10
  37. package/dist/data-table/data-table.js +17 -9
  38. package/dist/data-table/llms.txt +79 -0
  39. package/dist/data-view/data-view.d.ts +1 -1
  40. package/dist/data-view/llms.txt +49 -0
  41. package/dist/date-range/llms.txt +54 -0
  42. package/dist/datepicker/llms.txt +74 -0
  43. package/dist/dialog/llms.txt +51 -0
  44. package/dist/divider/llms.txt +28 -0
  45. package/dist/drawer/llms.txt +47 -0
  46. package/dist/empty/llms.txt +35 -0
  47. package/dist/fab/llms.txt +37 -0
  48. package/dist/fieldset/llms.txt +43 -0
  49. package/dist/file-upload/llms.txt +48 -0
  50. package/dist/form-field/llms.txt +55 -0
  51. package/dist/galleria/galleria.d.ts +1 -1
  52. package/dist/galleria/llms.txt +46 -0
  53. package/dist/grid/llms.txt +110 -0
  54. package/dist/icon/icon-registry.d.ts +5 -2
  55. package/dist/icon/icon-registry.js +5 -3
  56. package/dist/icon/llms.txt +66 -0
  57. package/dist/icon-button/llms.txt +38 -0
  58. package/dist/icons-entry/public-api.d.ts +5 -1
  59. package/dist/icons-entry/public-api.js +7 -1
  60. package/dist/image/llms.txt +46 -0
  61. package/dist/input/llms.txt +54 -0
  62. package/dist/input-mask/llms.txt +37 -0
  63. package/dist/input-number/llms.txt +44 -0
  64. package/dist/input-otp/llms.txt +43 -0
  65. package/dist/internal/use-breakpoint.js +18 -10
  66. package/dist/knob/llms.txt +46 -0
  67. package/dist/listbox/listbox.d.ts +1 -1
  68. package/dist/listbox/llms.txt +39 -0
  69. package/dist/map/llms.txt +108 -0
  70. package/dist/megamenu/llms.txt +48 -0
  71. package/dist/megamenu/megamenu.d.ts +1 -1
  72. package/dist/menu/llms.txt +56 -0
  73. package/dist/menu/menu-item.d.ts +1 -1
  74. package/dist/menu/menu-rows.d.ts +1 -1
  75. package/dist/menu/menu.d.ts +1 -1
  76. package/dist/menu/menu.js +12 -1
  77. package/dist/menubar/llms.txt +45 -0
  78. package/dist/menubar/menubar.d.ts +1 -1
  79. package/dist/meter-group/llms.txt +47 -0
  80. package/dist/meter-group/meter-group.d.ts +1 -1
  81. package/dist/order-list/llms.txt +41 -0
  82. package/dist/paginator/llms.txt +48 -0
  83. package/dist/paginator/paginator.d.ts +1 -1
  84. package/dist/paginator/paginator.js +7 -1
  85. package/dist/panel/llms.txt +55 -0
  86. package/dist/password/llms.txt +45 -0
  87. package/dist/pick-list/llms.txt +52 -0
  88. package/dist/popover/llms.txt +42 -0
  89. package/dist/progress/llms.txt +47 -0
  90. package/dist/public-api.d.ts +2 -1
  91. package/dist/public-api.js +1 -1
  92. package/dist/radio-group/llms.txt +40 -0
  93. package/dist/radio-group/radio-group.d.ts +1 -1
  94. package/dist/rating/llms.txt +43 -0
  95. package/dist/rich-text/llms.txt +61 -0
  96. package/dist/rich-text/rich-text.d.ts +1 -1
  97. package/dist/roadmap/llms.txt +82 -0
  98. package/dist/scroll-top/llms.txt +41 -0
  99. package/dist/select/llms.txt +56 -0
  100. package/dist/select/select.d.ts +1 -1
  101. package/dist/sidenav/llms.txt +53 -0
  102. package/dist/skeleton/llms.txt +33 -0
  103. package/dist/slider/llms.txt +50 -0
  104. package/dist/speed-dial/llms.txt +46 -0
  105. package/dist/speed-dial/speed-dial.d.ts +1 -1
  106. package/dist/spinner/llms.txt +33 -0
  107. package/dist/split-button/llms.txt +54 -0
  108. package/dist/split-button/split-button.d.ts +1 -1
  109. package/dist/splitter/llms.txt +44 -0
  110. package/dist/stepper/llms.txt +64 -0
  111. package/dist/switch/llms.txt +42 -0
  112. package/dist/switch-group/llms.txt +43 -0
  113. package/dist/switch-group/switch-group.d.ts +1 -1
  114. package/dist/tabs/llms.txt +48 -0
  115. package/dist/textarea/llms.txt +40 -0
  116. package/dist/timeline/llms.txt +53 -0
  117. package/dist/timeline/timeline.d.ts +1 -1
  118. package/dist/toast/llms.txt +52 -0
  119. package/dist/toolbar/llms.txt +45 -0
  120. package/dist/tooltip/llms.txt +42 -0
  121. package/dist/tree/llms.txt +45 -0
  122. package/dist/tree/tree.d.ts +2 -2
  123. package/package.json +11 -11
@@ -0,0 +1,65 @@
1
+ # KptAuth (@konce-pt/react)
2
+
3
+ An authentication screen: sign in, sign up and password recovery in one component, with a promo
4
+ panel and a list of external providers.
5
+ Import: `import { KptAuth, type KptAuthProvider } from '@konce-pt/react';`
6
+
7
+ The component is **presentational** — it calls no API and does not navigate; the app does that in
8
+ the handlers.
9
+
10
+ ## Props
11
+ - `mode` / `defaultMode` / `onModeChange` — `'signIn' | 'signUp' | 'forgot'`
12
+ - `layout`: `'slider'` (default) | `'split'` — the promo panel slides to the other side, or stays put
13
+ - `providers`: `KptAuthProvider[]` — `{ id, label, icon?, iconUrl?, description? }`
14
+ - `providerLayout`: `'list'` (default) | `'grid'`
15
+ - `showName`, `requireTerms`, `showRemember`: boolean (default true)
16
+ - `busy`: boolean — the buttons show a loading state, providers are blocked
17
+ - `error` / `notice`: string | null — a server message (a `danger` / `success` alert)
18
+ - `promoTitle` / `promoText`: `null` = mode-dependent text from i18n, `''` = hidden
19
+ - `animated` (true), `animationDuration` (320 ms)
20
+ - `onSignIn`, `onSignUp`, `onResetPassword`, `onProviderSelect`
21
+ - `renderProvider(provider, select)` — custom markup for one provider; return `null` for the rest
22
+
23
+ ## Slots
24
+ `KptAuth.Brand`, `KptAuth.Promo`, `KptAuth.Sso`, `KptAuth.Fields`, `KptAuth.Footer` — the
25
+ equivalents of the `[kptAuthBrand]`, `[kptAuthPromo]`, `[kptAuthSso]`, `[kptAuthFields]` and
26
+ `[kptAuthFooter]` projections. **Fields from the `Fields` slot are not part of the component's
27
+ model** — bind them to your own state and read them in the handler.
28
+
29
+ ## Validation without a form library
30
+ Three independent forms, one per mode, validated on every render: e-mail required and valid,
31
+ password required (sign up: at least 8 characters), the confirmation matching, consent required
32
+ when `requireTerms`. The validator returns a `kind`, and the message is composed at render time,
33
+ so **changing the language repaints the errors**. The "do not shout at an untouched field" gate
34
+ lives in the presentation, so the submit button's disabled state tells the truth from the start;
35
+ submitting marks the whole form as touched.
36
+
37
+ ## The heading takes focus after a mode change
38
+ Without it a screen reader never announces the change, and focus stays on a button that has just
39
+ slid off screen. Only the active mode's form is in the DOM, so nothing invisible keeps a tab stop.
40
+
41
+ ## Provider icons
42
+ The core set carries no brand icons: use `registerKptTablerIcons()` from `@konce-pt/react/icons`
43
+ (`icon: 'brand-google'`), your own registry entry, `iconUrl`, or `renderProvider`. The icon is
44
+ decorative, so the button's accessible name always comes from `label`.
45
+
46
+ ## Examples
47
+ <KptAuth
48
+ providers={[{ id: 'google', label: 'Google', icon: 'brand-google' }]}
49
+ busy={loading}
50
+ error={serverError}
51
+ onSignIn={login}
52
+ onProviderSelect={(provider) => oauth(provider.id)}
53
+ >
54
+ <KptAuth.Brand><img src="/logo.svg" alt="Konce" /></KptAuth.Brand>
55
+ <KptAuth.Footer><small>© 2026 Konce</small></KptAuth.Footer>
56
+ </KptAuth>
57
+
58
+ ## Styling
59
+ `--kpt-auth-width` (64rem), `--kpt-auth-card-height` (34rem), `--kpt-auth-min-height` (100dvh),
60
+ `--kpt-auth-promo-bg`, `--kpt-auth-padding`, `--kpt-auth-card-radius` / `-border` / `-shadow`.
61
+ Below the `md` breakpoint the layout drops to a single column.
62
+
63
+ ## i18n
64
+ Every string comes from the `auth` namespace, including `auth.promo.*`, `auth.continueWith`
65
+ (`{provider}`) and `auth.passwordHint` / `auth.errorMinLength` (`{min}`).
@@ -1,6 +1,6 @@
1
1
  import { type ComponentPropsWithRef } from 'react';
2
2
  export interface KptAutocompleteProps extends Omit<ComponentPropsWithRef<'input'>, 'value' | 'defaultValue' | 'onChange'> {
3
- options?: string[];
3
+ options?: readonly string[];
4
4
  value?: string;
5
5
  defaultValue?: string;
6
6
  onValueChange?: (value: string) => void;
@@ -0,0 +1,44 @@
1
+ # KptAutocomplete (@konce-pt/react)
2
+
3
+ A text field with suggestions. Import: `import { KptAutocomplete } from '@konce-pt/react';`
4
+
5
+ ## DOM
6
+ <span class="kpt-autocomplete-host">
7
+ <input class="kpt-autocomplete__input" type="text" role="combobox" aria-expanded="true">
8
+ </span>
9
+ <!-- while open, in a portal: -->
10
+ <ul class="kpt-autocomplete__panel" role="listbox">
11
+ <li class="kpt-autocomplete__option is-active" role="option">Kraków</li>
12
+ </ul>
13
+
14
+ ## Props
15
+ - `options`: string[] — the suggestions
16
+ - `value` / `defaultValue` / `onValueChange` — the typed text
17
+ - `invalid`, `touched`, `onTouch()`
18
+ - `className`, `ref` and every other `<input>` prop go to the input
19
+
20
+ ## The value is the text, not the option
21
+ Whatever is typed becomes the value; the suggestions only shorten the typing. That makes the field
22
+ usable for values outside the list — a city nobody put in `options` still submits. When the value
23
+ must come from the list, reach for `KptSelect` instead.
24
+
25
+ ## No backdrop
26
+ Unlike the other overlays this panel has none: a backdrop would cover the input it belongs to and
27
+ swallow every click on it. The panel closes on blur, on Escape and on picking an option. Options
28
+ are picked on `mousedown` with the default prevented, so the click cannot steal focus from the
29
+ input and close the panel before the pick lands.
30
+
31
+ ## Keyboard
32
+ ArrowDown opens the list and moves the active option, ArrowUp moves back, Enter picks the active
33
+ one, Escape closes.
34
+
35
+ ## Examples
36
+ <KptAutocomplete options={cities} value={city} onValueChange={setCity} placeholder="City…" />
37
+
38
+ ## Tokens
39
+ `--kpt-form-field-{bg,text,placeholder,border,border-focus,radius}`,
40
+ `--kpt-color-surface-raised` and `--kpt-elevation-3` for the panel.
41
+
42
+ ## Accessibility
43
+ The input is `role="combobox"` with `aria-expanded`; the panel is `role="listbox"` with
44
+ `role="option"` rows carrying `aria-selected`.
@@ -0,0 +1,42 @@
1
+ # KptAvatar (@konce-pt/react)
2
+
3
+ An avatar — a photo, initials (from `name`) or an icon.
4
+ Import: `import { KptAvatar } from '@konce-pt/react';`
5
+
6
+ ## DOM
7
+ <span class="kpt-avatar-host">
8
+ <span class="kpt-avatar" data-size="md" data-shape="circle" title="Anna Kowalska">
9
+ <img class="kpt-avatar__img" src="…" alt="Anna Kowalska">
10
+ </span>
11
+ </span>
12
+
13
+ ## Props
14
+ - `src`: string | null — a photo; it wins over initials and the icon
15
+ - `name`: string — the source of the initials, the `title` and the accessible label
16
+ - `icon`: string | null — a registry icon name, used when there is neither photo nor name
17
+ - `size`: 'sm' | 'md' | 'lg' | 'xl' (default 'md')
18
+ - `shape`: 'circle' | 'square' (default 'circle')
19
+ - `hostProps` — props for the `.kpt-avatar-host` wrapper
20
+ - `className`, `ref` and every other `<span>` prop go to the inner `.kpt-avatar`
21
+
22
+ ## Fallback order
23
+ `src` → `name` (up to two initials, uppercased) → `icon`. With none of them the avatar renders as
24
+ an empty shape, which is a valid placeholder.
25
+
26
+ ## Positioning inside a parent
27
+ The host is the layout element, so classes that place the avatar in its parent go to `hostProps` —
28
+ the overlapping avatar on a card is `hostProps={{ className: 'kpt-card__avatar' }}`.
29
+
30
+ ## Examples
31
+ <KptAvatar src="/u/anna.jpg" size="lg" />
32
+ <KptAvatar name="Anna Kowalska" size="lg" />
33
+ <KptAvatar icon="user" shape="square" />
34
+ <KptAvatar src="/u/anna.jpg" size="lg" hostProps={{ className: 'kpt-card__avatar' }} />
35
+
36
+ ## Tokens
37
+ `--kpt-color-muted`, `--kpt-color-on-muted`, `--kpt-radius-full`, `--kpt-radius-md` (square).
38
+
39
+ ## Accessibility
40
+ `name` becomes the native `title` — initials alone do not say who this is. Without `src` the avatar
41
+ also gets `role="img"` and `aria-label`, and the initials are `aria-hidden` so a screen reader does
42
+ not spell out "A-K". With `src` the label already lives on the image's `alt`, so it is not doubled.
@@ -6,7 +6,7 @@ export interface KptAvatarItem {
6
6
  icon?: string;
7
7
  }
8
8
  export interface KptAvatarGroupProps extends ComponentPropsWithRef<'div'> {
9
- avatars?: KptAvatarItem[];
9
+ avatars?: readonly KptAvatarItem[];
10
10
  /** Ile awatarów pokazać, zanim pojawi się „+N". */
11
11
  max?: number;
12
12
  size?: KptAvatarSize;
@@ -0,0 +1,38 @@
1
+ # KptAvatarGroup (@konce-pt/react)
2
+
3
+ A group of overlapping avatars with a "+N" counter. Built on `KptAvatar`.
4
+ Import: `import { KptAvatarGroup } from '@konce-pt/react';`
5
+
6
+ ## DOM
7
+ <div class="kpt-avatar-group-host">
8
+ <div class="kpt-avatar-group" data-size="lg">
9
+ <span class="kpt-avatar-group__item">…KptAvatar…</span>…
10
+ <span class="kpt-avatar-group__item kpt-avatar-group__more" data-size="lg">+2</span>
11
+ </div>
12
+ </div>
13
+
14
+ ## Props
15
+ - `avatars`: `KptAvatarItem[]` — `{ name?, src?, icon? }`
16
+ - `max`: number (default 4) — how many to show before "+N"
17
+ - `size`: `'sm' | 'md' | 'lg' | 'xl'`
18
+ - `className`, `ref` and every other `<div>` prop go to the host
19
+
20
+ ## One list, three fallbacks
21
+ Each entry falls back in order: `src` (a photo) → `name` (initials) → `icon`. A team where only
22
+ some people have a photo therefore needs no branching at the call site — which is exactly the
23
+ common case.
24
+
25
+ ## The name survives the overlap
26
+ Every avatar keeps the `title` from `KptAvatar`, so hovering tells you who it is even at
27
+ `size="sm"`, where initials alone are a guess.
28
+
29
+ ## Examples
30
+ <KptAvatarGroup avatars={team} max={4} />
31
+ <KptAvatarGroup avatars={team} max={3} size="sm" />
32
+
33
+ ## Tokens
34
+ The overlap ring uses `--kpt-color-surface`, the "+N" chip `--kpt-color-muted`.
35
+
36
+ ## Accessibility
37
+ The avatars come from `KptAvatar` with its `role="img"` and `aria-label`; "+N" is plain text, so
38
+ the count is read as part of the group.
@@ -0,0 +1,38 @@
1
+ # KptBadge (@konce-pt/react)
2
+
3
+ A badge — a counter or a status dot. Standalone, or laid over content (`overlay`).
4
+ Import: `import { KptBadge } from '@konce-pt/react';`
5
+
6
+ ## DOM
7
+ <span class="kpt-badge-host">
8
+ …children…
9
+ <span class="kpt-badge kpt-badge--overlay" data-variant="danger" data-size="md">8</span>
10
+ </span>
11
+
12
+ ## Props
13
+ - `value`: string | number | null — the counter content
14
+ - `variant`: 'neutral' | 'primary' | 'success' | 'danger' | 'warning' | 'info' (default 'primary')
15
+ - `size`: 'sm' | 'md' | 'lg' (default 'md') — `sm` fits inside a line of text
16
+ - `dot`: boolean — a bare status dot, no number
17
+ - `overlay`: boolean — laid over the `children`
18
+ - `max`: number (default 99) — above it the counter reads `99+`
19
+ - `className`, `ref` and every other `<span>` prop go to the host
20
+
21
+ ## Overlay
22
+ `overlay` positions the badge in the corner of `children`, so the host has to be the thing being
23
+ marked: an icon, an avatar, a button. The badge scales with `font-size`, which is why the demo sets
24
+ it on the host rather than on the badge.
25
+
26
+ ## Examples
27
+ <KptBadge value={3} variant="danger" />
28
+ <KptBadge dot variant="success" />
29
+ <KptBadge value={128} overlay style={{ fontSize: '1.5rem' }}><KptIcon name="menu" /></KptBadge>
30
+ <span>Notifications <KptBadge value={4} size="sm" variant="success" /></span>
31
+
32
+ ## Tokens
33
+ The semantic colour pairs of each variant (`--kpt-color-primary` / `--kpt-color-on-primary`,
34
+ `-success`, `-danger`, `-warning`, `-info`, `--kpt-color-muted` / `--kpt-color-on-muted`).
35
+
36
+ ## Accessibility
37
+ The badge is text inside the host, so a screen reader reads it together with the content it marks.
38
+ For a counter that has to be announced on its own, add your own `aria-label` on the host.
@@ -0,0 +1,43 @@
1
+ # KptBottomSheet (@konce-pt/react)
2
+
3
+ A sheet that slides up from the bottom of the screen, with a handle and a backdrop.
4
+ Import: `import { KptBottomSheet } from '@konce-pt/react';`
5
+
6
+ ## DOM
7
+ <div class="kpt-bottom-sheet-host">
8
+ <div class="kpt-bottom-sheet__backdrop is-open"></div>
9
+ <div class="kpt-bottom-sheet__panel is-open" role="dialog" aria-modal="true" tabindex="-1">
10
+ <button class="kpt-bottom-sheet__handle"></button>
11
+ <div class="kpt-bottom-sheet__header">Share</div>
12
+ <div class="kpt-bottom-sheet__body">…</div>
13
+ </div>
14
+ </div>
15
+
16
+ ## Props
17
+ - `open`, `onOpenChange` — controlled, like `KptDialog`
18
+ - `header`: string
19
+ - `closeOnBackdrop`: boolean (default true)
20
+ - `children` — the sheet's content
21
+ - `className`, `ref` and every other `<div>` prop go to the host
22
+
23
+ ## The closed sheet stays in the DOM
24
+ It is hidden with `transform` and `visibility`, never `display: none` — the slide-in and slide-out
25
+ animations need something to run on. `visibility` also takes it out of the tab order and out of the
26
+ accessibility tree without `inert`, and does so on the very first frame, with no JavaScript.
27
+ `prefers-reduced-motion: reduce` drops the transitions.
28
+
29
+ ## No portal
30
+ The panel and the backdrop are `position: fixed` and the host is `display: contents`, so the sheet
31
+ needs no portal — the same DOM as the Angular port. A transformed ancestor would break the fixed
32
+ positioning there too.
33
+
34
+ ## Examples
35
+ <KptButton onClick={() => setOpen(true)}>Share</KptButton>
36
+ <KptBottomSheet open={open} onOpenChange={setOpen} header="Share">…</KptBottomSheet>
37
+
38
+ ## Tokens
39
+ The panel `--kpt-color-surface-raised`, the top radius `--kpt-radius-lg`, `--kpt-elevation-4`.
40
+
41
+ ## Accessibility
42
+ `role="dialog"` with `aria-modal`; Escape and the handle close the sheet, and the handle is a real
43
+ button labelled from `common.close`.
@@ -5,7 +5,7 @@ export interface KptBreadcrumbItem {
5
5
  url?: string;
6
6
  }
7
7
  export interface KptBreadcrumbProps extends Omit<ComponentPropsWithRef<'div'>, 'children'>, KptI18nOverrides {
8
- items?: KptBreadcrumbItem[];
8
+ items?: readonly KptBreadcrumbItem[];
9
9
  }
10
10
  /**
11
11
  * Ścieżka nawigacji (breadcrumbs).
@@ -0,0 +1,35 @@
1
+ # KptBreadcrumb (@konce-pt/react)
2
+
3
+ A breadcrumb trail. Import: `import { KptBreadcrumb } from '@konce-pt/react';`
4
+
5
+ ## DOM
6
+ <div class="kpt-breadcrumb-host">
7
+ <nav class="kpt-breadcrumb" aria-label="breadcrumb">
8
+ <a class="kpt-breadcrumb__link" href="/">Home</a>
9
+ <span class="kpt-icon-host kpt-breadcrumb__sep">…</span>
10
+ <span class="kpt-breadcrumb__current" aria-current="page">Showcase</span>
11
+ </nav>
12
+ </div>
13
+
14
+ ## Props
15
+ - `items`: `{ label, url? }[]`
16
+ - `locale`, `dictionary` — i18n overrides for this component only
17
+ - `className`, `ref` and every other `<div>` prop go to the host
18
+
19
+ ## The last item is never a link
20
+ Even with a `url`, the last item renders as plain text: it is the current page, and a link pointing
21
+ where you already are only misleads. It gets `aria-current="page"` instead.
22
+
23
+ ## i18n
24
+ The nav label comes from `breadcrumb.label` in the dictionary.
25
+
26
+ ## Examples
27
+ <KptBreadcrumb items={[{ label: 'Home', url: '/' }, { label: 'Settings' }]} />
28
+
29
+ ## Tokens
30
+ `--kpt-color-on-surface-muted` (the separators and the current page), `--kpt-color-primary`
31
+ (the links).
32
+
33
+ ## Accessibility
34
+ A `<nav>` with a label from the dictionary; the current page carries `aria-current="page"`, so a
35
+ screen reader can tell the trail from the position in it.
@@ -0,0 +1,56 @@
1
+ # KptButton (@konce-pt/react)
2
+
3
+ The Koncept UI button. It renders a native `<button>` (semantics + form submit) inside a
4
+ `.kpt-button-host` wrapper — two DOM levels, exactly like the Angular port, because the host
5
+ carries its own styles. Import: `import { KptButton } from '@konce-pt/react';`
6
+
7
+ ## DOM
8
+ <span class="kpt-button-host">
9
+ <button class="kpt-button" type="button" data-variant="filled" data-size="md">
10
+ <span class="kpt-button__content">…</span>
11
+ </button>
12
+ </span>
13
+
14
+ ## Props
15
+ - `variant`: 'filled' | 'tonal' | 'outline' | 'text' | 'danger' (default 'filled')
16
+ - `size`: 'sm' | 'md' | 'lg' (default 'md')
17
+ - `type`: 'button' | 'submit' | 'reset' (default 'button')
18
+ - `disabled`: boolean
19
+ - `loading`: boolean — a spinner + blocked interaction, sets `aria-busy`
20
+ - `fullWidth`: boolean — the full width of the container
21
+ - `asChild`: boolean — render the single child instead of `<button>`, keeping every class
22
+ - `hostProps`: props for the `.kpt-button-host` wrapper
23
+ - `className`, `ref` and every other `<button>` prop go to the INNER `<button>`
24
+
25
+ ## Events
26
+ No events of its own — bind `onClick` and it lands on the native `<button>`.
27
+
28
+ ## Examples
29
+ <KptButton onClick={save}>Save</KptButton>
30
+ <KptButton variant="outline" size="sm">Cancel</KptButton>
31
+ <KptButton variant="danger" loading={deleting}>Delete</KptButton>
32
+ <form action={submitAction}><KptButton type="submit">Send</KptButton></form>
33
+ <KptButton asChild><a href="/docs">Documentation</a></KptButton>
34
+
35
+ ## asChild
36
+ The wrapper is still rendered; only the inner element is swapped. The content stays wrapped in
37
+ `.kpt-button__content`, so the spinner and the layout keep working. A disabled substituted
38
+ element gets `disabled` when it is a native `<button>`, and `aria-disabled` otherwise — an
39
+ element with no `:disabled` state therefore does not get the dimmed look.
40
+
41
+ ## Tokens (styling)
42
+ `--kpt-button-radius`, `--kpt-button-font-weight`, `--kpt-button-gap`,
43
+ `--kpt-button-filled-{bg,fg,bg-hover,bg-active}`, `--kpt-button-tonal-{bg,fg,bg-hover}`,
44
+ `--kpt-button-outline-{border,fg,bg-hover}`, `--kpt-button-text-{fg,bg-hover}`.
45
+ The 'danger' variant uses `--kpt-color-danger` / `--kpt-color-on-danger`.
46
+
47
+ ## The state layer (hover / active)
48
+ Hover and active feedback is carried by an `::after` pseudo-element (`opacity` 0 → 1), not by
49
+ animating the button's own `background-color`. The CSS is shared with the Angular port, where
50
+ the pattern was introduced to stop the variant flicker on first paint; here it simply comes
51
+ along with the stylesheet.
52
+
53
+ ## Accessibility
54
+ - A native `<button>` — full keyboard and screen reader support.
55
+ - `loading` sets `aria-busy="true"` and disables the button.
56
+ - The focus ring comes from `@konce-pt/styles` (`--kpt-color-focus-ring`).
@@ -2,7 +2,7 @@ import type { ComponentPropsWithRef } from 'react';
2
2
  import type { KptHostProps } from '../internal/types.ts';
3
3
  import type { KptOption } from '../shared/types.ts';
4
4
  export interface KptButtonGroupProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'children' | 'onChange' | 'defaultValue'> {
5
- options?: KptOption<T>[];
5
+ options?: readonly KptOption<T>[];
6
6
  value?: T;
7
7
  defaultValue?: T;
8
8
  onValueChange?: (value: T) => void;
@@ -0,0 +1,40 @@
1
+ # KptButtonGroup (@konce-pt/react)
2
+
3
+ A segmented button group (toggle) — a single choice out of the options.
4
+ Import: `import { KptButtonGroup } from '@konce-pt/react';`
5
+
6
+ ## DOM
7
+ <span class="kpt-button-group-host">
8
+ <div class="kpt-button-group" role="group" data-size="md">
9
+ <button type="button" class="kpt-button-group__item is-active" aria-pressed="true">List</button>
10
+ <button type="button" class="kpt-button-group__item" aria-pressed="false">Grid</button>
11
+ </div>
12
+ </span>
13
+
14
+ ## Props
15
+ - `options`: `KptOption<T>[]` — `{ label, value, disabled? }`
16
+ - `value` / `defaultValue` / `onValueChange` — controlled or uncontrolled
17
+ - `size`: 'sm' | 'md' | 'lg' (default 'md')
18
+ - `disabled`: boolean — disables the whole group; a single option disables itself
19
+ - `onTouch()` — fires on a pick, the moment the control becomes touched
20
+ - `hostProps` — props for the `.kpt-button-group-host` wrapper
21
+ - `className`, `ref` and every other `<div>` prop go to `.kpt-button-group`
22
+
23
+ ## Values of any type
24
+ The value is generic, so options can carry objects, not just strings. Keys come from
25
+ `String(option.value)`, so distinct options need distinct string forms — for object values give
26
+ each one a stable id and map back to the object yourself.
27
+
28
+ ## Examples
29
+ <KptButtonGroup value={view} onValueChange={setView}
30
+ options={[{ label: 'List', value: 'list' }, { label: 'Grid', value: 'grid' }]} />
31
+
32
+ <KptButtonGroup size="sm" defaultValue="M" options={sizeOptions} />
33
+
34
+ ## Tokens
35
+ `--kpt-color-surface`, `--kpt-color-muted`, `--kpt-color-primary` (the active segment),
36
+ `--kpt-color-border-strong`, `--kpt-radius-md`.
37
+
38
+ ## Accessibility
39
+ A `role="group"` of native buttons, each with `aria-pressed` — a screen reader announces which
40
+ segment is on, and the keyboard walks them like any other buttons.
@@ -0,0 +1,60 @@
1
+ # KptCard (@konce-pt/react)
2
+
3
+ A card — a surface with elevation (`elevated`) or a border (`outlined`). The card renders its own
4
+ body; the header and the footer are lifted out of the children and placed outside that body,
5
+ exactly where the Angular template puts them, so the DOM tree matches node for node.
6
+ Import: `import { KptCard } from '@konce-pt/react';`
7
+
8
+ ## DOM
9
+ <div class="kpt-card" data-variant="elevated">
10
+ <div class="kpt-card__header">…</div>
11
+ <div class="kpt-card__body">…</div>
12
+ <div kptcardfooter="">…</div>
13
+ </div>
14
+
15
+ ## Props
16
+ - `variant`: 'elevated' | 'outlined' (default 'elevated')
17
+ - `header`: string — optional header text; use `<KptCard.Header>` for richer content
18
+ - `flush`: boolean — a body with no padding (for rich layouts that manage their own sections)
19
+ - `className`, `ref` and every other `<div>` prop go to the card root
20
+
21
+ ## Sub-components
22
+ - `KptCard.Header` — lifted above the body, `.kpt-card__header`
23
+ - `KptCard.Footer` — lifted below the body; carries the `kptcardfooter` attribute the shared
24
+ SCSS targets (attribute names in CSS selectors are case-insensitive in HTML, so one rule
25
+ serves both ports)
26
+ - `KptCard.Media` — an `<img>` on the full width, clipped to the radius; set `aspect-ratio`/`height`
27
+ - `KptCard.Lead` — the header row (avatar + titles), with padding
28
+ - `KptCard.Title`, `KptCard.Subtitle` — title/subtitle typography
29
+ - `KptCard.Section` — a padded section (a column with gaps)
30
+ - `KptCard.Tags` — a row of chips; `KptCard.Meta` — an icon+text row (rating/location)
31
+ - `KptCard.Actions` — a row of buttons (equal widths)
32
+
33
+ There is no `KptCard.Body`: the body belongs to the card itself, and `flush` removes its padding.
34
+ An overlapping avatar is a class you apply to your own element: `className="kpt-card__avatar"`.
35
+
36
+ ## Examples
37
+ <KptCard header="Settings" variant="outlined">
38
+ <p>Content…</p>
39
+ <KptCard.Footer><KptButton>Save</KptButton></KptCard.Footer>
40
+ </KptCard>
41
+
42
+ <KptCard variant="outlined" flush>
43
+ <KptCard.Lead>
44
+ <img className="kpt-card__avatar" src="/u.jpg" alt="" width={48} height={48} />
45
+ <div>
46
+ <KptCard.Title>Shiba Inu</KptCard.Title>
47
+ <KptCard.Subtitle>Dog Breed</KptCard.Subtitle>
48
+ </div>
49
+ </KptCard.Lead>
50
+ <KptCard.Media src="/photo.jpg" style={{ aspectRatio: '4 / 3' }} />
51
+ <KptCard.Section>Description…</KptCard.Section>
52
+ <KptCard.Footer><KptButton variant="text" size="sm">LIKE</KptButton></KptCard.Footer>
53
+ </KptCard>
54
+
55
+ ## Tokens
56
+ `--kpt-card-{bg,fg,border,radius,padding,shadow}`.
57
+
58
+ ## Accessibility
59
+ The card is a plain container with no role of its own — give it heading semantics through your
60
+ own content when it needs them.
@@ -1,7 +1,7 @@
1
1
  import type { ComponentPropsWithRef, ReactNode } from 'react';
2
2
  import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
3
3
  export interface KptCarouselProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'onChange' | 'children'>, KptI18nOverrides {
4
- items?: T[];
4
+ items?: readonly T[];
5
5
  activeIndex?: number;
6
6
  defaultActiveIndex?: number;
7
7
  onActiveIndexChange?: (index: number) => void;
@@ -0,0 +1,47 @@
1
+ # KptCarousel (@konce-pt/react)
2
+
3
+ A slide carousel. The data comes from `items`, the look of a slide from `renderSlide`.
4
+ Import: `import { KptCarousel } from '@konce-pt/react';`
5
+
6
+ ## DOM
7
+ <div class="kpt-carousel-host">
8
+ <div class="kpt-carousel">
9
+ <div class="kpt-carousel__viewport">
10
+ <div class="kpt-carousel__track" style="transform:translateX(-100%)">
11
+ <div class="kpt-carousel__slide">…</div>…
12
+ </div>
13
+ </div>
14
+ <button class="kpt-carousel__nav kpt-carousel__nav--prev">…</button>
15
+ <button class="kpt-carousel__nav kpt-carousel__nav--next">…</button>
16
+ <div class="kpt-carousel__dots"><button class="kpt-carousel__dot is-active"></button>…</div>
17
+ </div>
18
+ </div>
19
+
20
+ ## Props
21
+ - `items`: `T[]`
22
+ - `activeIndex` / `defaultActiveIndex` / `onActiveIndexChange`
23
+ - `renderSlide(item, index)` — required
24
+ - `className`, `ref` and every other `<div>` prop go to the host
25
+
26
+ ## The render prop replaces the template
27
+ `renderSlide` is the direct equivalent of Angular's `<ng-template let-item let-i="index">`: the
28
+ carousel owns the layout and the navigation, the caller owns what a slide looks like. Nothing is
29
+ guessed about the item's shape, so the component stays generic in `T`.
30
+
31
+ ## One transform, every slide mounted
32
+ The track slides with a single `translateX` of a multiple of 100%, so every slide stays mounted —
33
+ paging does not lose state inside a slide (a form, a playing video, a scroll position).
34
+
35
+ ## Examples
36
+ <KptCarousel items={photos} renderSlide={(photo) => <img src={photo} style={{ width: '100%' }} />} />
37
+ <KptCarousel items={slides} activeIndex={index} onActiveIndexChange={setIndex} renderSlide={renderSlide} />
38
+
39
+ ## i18n
40
+ The arrows use `common.previous` / `common.next`, the dots `carousel.slide` (`{index}`).
41
+
42
+ ## Tokens
43
+ The navigation and dots `--kpt-color-primary`, the arrow background `--kpt-color-surface-raised`.
44
+
45
+ ## Accessibility
46
+ The arrows are disabled at the ends instead of wrapping around, and every dot is a labelled button,
47
+ so a slide can be reached directly from the keyboard.
@@ -6,7 +6,7 @@ export interface KptChartLegendItem {
6
6
  hidden: boolean;
7
7
  }
8
8
  export interface KptChartLegendProps {
9
- items?: KptChartLegendItem[];
9
+ items?: readonly KptChartLegendItem[];
10
10
  position?: KptLegendPosition;
11
11
  align?: KptAlign;
12
12
  pointStyle?: KptPointStyle;
@@ -0,0 +1,57 @@
1
+ # KptChart (@konce-pt/react/charts)
2
+
3
+ An SVG chart driven by a single `config` object: line, bar, pie, doughnut, polarArea, radar.
4
+ Import: `import { KptChart, type KptChartConfig } from '@konce-pt/react/charts';`
5
+
6
+ A separate subpath, because the geometry and the chart styles are heavy next to the library core
7
+ and not every app needs them.
8
+
9
+ ## Props
10
+ - `config`: `KptChartConfig` (required)
11
+ - `width`, `height` — the viewBox in SVG units (600 × 360); the chart scales to its container
12
+
13
+ The whole drawing lives in that coordinate system, TYPOGRAPHY INCLUDED: the axis labels are set in
14
+ `font-size: 11px`, but inside an SVG that means 11 user units, not 11 device pixels. Render a
15
+ 600-unit chart in a 300px column and the labels come out at roughly half size. `width`/`height` are
16
+ therefore not just an aspect ratio — they are the text scale. In a narrow container shrink them
17
+ proportionally (`width={300} height={180}`) instead of letting a 600-unit chart squeeze itself.
18
+ - `className`, `ref` and every other `<div>` prop go to the host
19
+
20
+ ## The config is the API
21
+ `{ type, labels, series, points?, area?, interpolation?, orientation?, borderRadius?, innerRadius?,
22
+ crosshair?, tooltip?, legend?, title?, subtitle?, animation? }` — the same shape the Angular port
23
+ takes, because both read it from `@konce-pt/chart`.
24
+
25
+ ## The geometry lives in a package
26
+ Scales, paths, bar layout and arcs come from `@konce-pt/chart` — pure functions with `node --test`
27
+ coverage, shared with the Angular port. The component only turns them into SVG elements; there is
28
+ no DOM manipulation and no canvas.
29
+
30
+ ## Points have a bigger hit target than they look
31
+ A 4px point is not something a pointer can reliably hit, so each one is paired with an invisible
32
+ 12px circle that carries the events. The visible point only reacts to the state.
33
+
34
+ ## Interaction is local state
35
+ Hidden series, the highlighted one, the tooltip and the active category are `useState` — the config
36
+ stays a plain object the caller owns, and nothing is written back into it.
37
+
38
+ ## Examples
39
+ <KptChart config={{ type: 'line', labels: ['Jan', 'Feb'], series: [{ name: 'Sales', data: [12, 19] }] }} />
40
+
41
+ <KptChart
42
+ config={{
43
+ type: 'doughnut',
44
+ innerRadius: 0.62,
45
+ labels: ['Done', 'In progress'],
46
+ series: [{ name: 'Tasks', data: [42, 15] }],
47
+ legend: { position: 'right' },
48
+ }}
49
+ />
50
+
51
+ ## Tokens
52
+ `--kpt-chart-{duration,stagger,delay}` for the animation; series colours come from
53
+ `KPT_CHART_PALETTE` or `series.color`.
54
+
55
+ ## Accessibility
56
+ The `<svg>` carries `role="img"`; the legend is a list of real buttons, so series can be toggled
57
+ from the keyboard.