@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
|
@@ -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.
|
|
@@ -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.
|