@konce-pt/react 0.8.5 → 0.9.1

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.
@@ -1,8 +1,8 @@
1
1
  import { type ComponentPropsWithRef } from 'react';
2
2
  import type { AsChildProps, KptHostProps } from '../internal/types.ts';
3
- export type KptButtonVariant = 'filled' | 'tonal' | 'outline' | 'text' | 'danger';
4
- export type KptButtonSize = 'sm' | 'md' | 'lg';
5
- export interface KptButtonProps extends ComponentPropsWithRef<'button'>, AsChildProps {
3
+ export type KptButtonVariant = 'filled' | 'elevated' | 'tonal' | 'outline' | 'text' | 'link' | 'danger' | 'success' | 'warning' | 'info';
4
+ export type KptButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
5
+ export interface KptButtonProps extends Omit<ComponentPropsWithRef<'button'>, 'href'>, AsChildProps {
6
6
  /** Wariant wizualny. */
7
7
  variant?: KptButtonVariant;
8
8
  /** Rozmiar. */
@@ -11,17 +11,33 @@ export interface KptButtonProps extends ComponentPropsWithRef<'button'>, AsChild
11
11
  loading?: boolean;
12
12
  /** Pełna szerokość kontenera. */
13
13
  fullWidth?: boolean;
14
+ /** Nazwa ikony wiodącej z rejestru (`KptIconRegistry`). */
15
+ icon?: string;
16
+ /** Nazwa ikony zamykającej — zwykle znacznik kierunku, nie akcja. */
17
+ iconEnd?: string;
18
+ /** Klasa dokładana do ikony wiodącej; obok stałego haka `.kpt-button__icon`. */
19
+ iconClassName?: string;
20
+ iconEndClassName?: string;
21
+ /** Podany adres zamienia `<button>` na `<a>`. Ignorowany przy `asChild`. */
22
+ href?: string;
23
+ target?: string;
24
+ rel?: string;
14
25
  /** Propsy wrappera hosta (`.kpt-button-host`). `className` i `ref` idą na `<button>`. */
15
26
  hostProps?: KptHostProps<'span'>;
16
27
  }
17
28
  /**
18
29
  * Przycisk Koncept UI. Renderuje natywny `<button>` (semantyka + submit formularza)
19
- * w hoście `.kpt-button-host` — dwa poziomy DOM, dokładnie jak w porcie Angulara,
20
- * bo host niesie własne style (`display: inline-flex`, wariant pełnej szerokości).
30
+ * albo `<a>`, gdy podasz `href` — w hoście `.kpt-button-host`, czyli dwa poziomy DOM,
31
+ * dokładnie jak w porcie Angulara, bo host niesie własne style.
32
+ *
33
+ * Ikonę można podać nazwą (`icon`/`iconEnd`) albo wstawić w `children` — oba sposoby
34
+ * dostają ten sam odstęp i tę samą skalę. Przy `loading` spinner zajmuje miejsce ikony wiodącej.
21
35
  *
22
36
  * @example
23
37
  * <KptButton onClick={save}>Zapisz</KptButton>
24
- * <KptButton variant="outline" size="sm">Anuluj</KptButton>
38
+ * <KptButton icon="device-floppy" onClick={save}>Zapisz</KptButton>
39
+ * <KptButton iconEnd="chevron-right" variant="outline">Dalej</KptButton>
40
+ * <KptButton variant="link" href="/docs">Dokumentacja</KptButton>
25
41
  * <KptButton asChild><a href="/docs">Dokumentacja</a></KptButton>
26
42
  */
27
- export declare function KptButton({ variant, size, type, loading, fullWidth, disabled, asChild, hostProps, className, children, ...rest }: Readonly<KptButtonProps>): import("react").JSX.Element;
43
+ export declare function KptButton({ variant, size, type, loading, fullWidth, disabled, asChild, icon, iconEnd, iconClassName, iconEndClassName, href, target, rel, hostProps, className, children, ...rest }: Readonly<KptButtonProps>): import("react").JSX.Element;
@@ -2,22 +2,28 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import { isValidElement } from 'react';
3
3
  import { cn } from "../internal/cn.js";
4
4
  import { renderAsChild } from "../internal/slot.js";
5
+ import { KptIcon } from "../icon/icon.js";
5
6
  /**
6
7
  * Przycisk Koncept UI. Renderuje natywny `<button>` (semantyka + submit formularza)
7
- * w hoście `.kpt-button-host` — dwa poziomy DOM, dokładnie jak w porcie Angulara,
8
- * bo host niesie własne style (`display: inline-flex`, wariant pełnej szerokości).
8
+ * albo `<a>`, gdy podasz `href` — w hoście `.kpt-button-host`, czyli dwa poziomy DOM,
9
+ * dokładnie jak w porcie Angulara, bo host niesie własne style.
10
+ *
11
+ * Ikonę można podać nazwą (`icon`/`iconEnd`) albo wstawić w `children` — oba sposoby
12
+ * dostają ten sam odstęp i tę samą skalę. Przy `loading` spinner zajmuje miejsce ikony wiodącej.
9
13
  *
10
14
  * @example
11
15
  * <KptButton onClick={save}>Zapisz</KptButton>
12
- * <KptButton variant="outline" size="sm">Anuluj</KptButton>
16
+ * <KptButton icon="device-floppy" onClick={save}>Zapisz</KptButton>
17
+ * <KptButton iconEnd="chevron-right" variant="outline">Dalej</KptButton>
18
+ * <KptButton variant="link" href="/docs">Dokumentacja</KptButton>
13
19
  * <KptButton asChild><a href="/docs">Dokumentacja</a></KptButton>
14
20
  */
15
- export function KptButton({ variant = 'filled', size = 'md', type = 'button', loading = false, fullWidth = false, disabled = false, asChild = false, hostProps, className, children, ...rest }) {
21
+ export function KptButton({ variant = 'filled', size = 'md', type = 'button', loading = false, fullWidth = false, disabled = false, asChild = false, icon, iconEnd, iconClassName, iconEndClassName, href, target, rel, hostProps, className, children, ...rest }) {
16
22
  const { className: hostClassName, ...hostRest } = hostProps ?? {};
17
23
  const isDisabled = disabled || loading;
18
- // Zawartość jest identyczna w obu trybach — w `asChild` podmieniamy tylko element
24
+ // Zawartość jest identyczna we wszystkich trybach — w `asChild` podmieniamy tylko element
19
25
  // zewnętrzny, nie strukturę środka, żeby SCSS (`__spinner`, `__content`) dalej działał.
20
- const content = (_jsxs(_Fragment, { children: [loading ? _jsx("span", { className: "kpt-button__spinner", "aria-hidden": "true" }) : null, _jsx("span", { className: "kpt-button__content", children: asChildContent(asChild, children) })] }));
26
+ const content = (_jsxs(_Fragment, { children: [loading ? (_jsx("span", { className: "kpt-button__spinner", "aria-hidden": "true" })) : icon ? (_jsx(KptIcon, { className: cn('kpt-button__icon', iconClassName), name: icon })) : null, _jsx("span", { className: "kpt-button__content", children: asChildContent(asChild, children) }), iconEnd ? (_jsx(KptIcon, { className: cn('kpt-button__icon', 'kpt-button__icon--end', iconEndClassName), name: iconEnd })) : null] }));
21
27
  const buttonProps = {
22
28
  className: cn('kpt-button', loading && 'kpt-button--loading', className),
23
29
  'data-variant': variant,
@@ -27,13 +33,31 @@ export function KptButton({ variant = 'filled', size = 'md', type = 'button', lo
27
33
  'aria-busy': loading || undefined,
28
34
  ...rest,
29
35
  };
30
- return (_jsx("span", { className: cn('kpt-button-host', fullWidth && 'kpt-button-host--full', hostClassName), ...hostRest, children: asChild && isValidElement(children) ? (renderAsChild(children, { ...buttonProps, ...disabledProps(children, isDisabled) }, content)) : (_jsx("button", { type: type, disabled: isDisabled, ...buttonProps, children: content })) }));
36
+ return (_jsx("span", { className: cn('kpt-button-host', fullWidth && 'kpt-button-host--full', hostClassName), ...hostRest, children: renderInner({ asChild, children, buttonProps, content, href, target, rel, type, isDisabled }) }));
37
+ }
38
+ /**
39
+ * Element wewnętrzny. `asChild` ma pierwszeństwo przed `href` — kto podstawia własny
40
+ * element, ten decyduje o znaczniku.
41
+ */
42
+ function renderInner(args) {
43
+ const { asChild, children, buttonProps, content, href, target, rel, type, isDisabled } = args;
44
+ if (asChild && isValidElement(children)) {
45
+ return renderAsChild(children, { ...buttonProps, ...disabledProps(children, isDisabled) }, content);
46
+ }
47
+ if (href !== undefined) {
48
+ return (_jsx("a", {
49
+ // Wyłączony odnośnik traci `href`, a nie tylko dostaje atrybut — inaczej dałoby się go
50
+ // kliknąć i wejść na niego z klawiatury.
51
+ href: isDisabled ? undefined : href, target: target,
52
+ // `target="_blank"` bez `rel` to wektor na `window.opener` — dokładamy domyślne.
53
+ rel: rel ?? (target === '_blank' ? 'noopener noreferrer' : undefined), "aria-disabled": isDisabled || undefined, tabIndex: isDisabled ? -1 : undefined, ...buttonProps, children: content }));
54
+ }
55
+ return (_jsx("button", { type: type, disabled: isDisabled, ...buttonProps, children: content }));
31
56
  }
32
57
  /**
33
58
  * Stan wyłączenia dla podstawionego elementu. Natywny `<button>` dostaje `disabled`
34
59
  * (SCSS stylizuje `:disabled`); każdy inny element — `aria-disabled`, bo `disabled`
35
- * nie ma tam znaczenia. Element bez `:disabled` nie dostanie wyglądu wyłączonego,
36
- * więc `asChild` + `disabled` to świadomie ograniczone połączenie.
60
+ * nie ma tam znaczenia.
37
61
  */
38
62
  function disabledProps(child, isDisabled) {
39
63
  if (!isDisabled)
@@ -1,35 +1,68 @@
1
1
  # KptButton (@konce-pt/react)
2
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';`
3
+ The Koncept UI button. It renders a native `<button>` (semantics + form submit) — or an `<a>` when
4
+ you pass `href` — inside a `.kpt-button-host` wrapper: two DOM levels, exactly like the Angular port,
5
+ because the host carries its own styles. Import: `import { KptButton } from '@konce-pt/react';`
6
6
 
7
7
  ## DOM
8
8
  <span class="kpt-button-host">
9
9
  <button class="kpt-button" type="button" data-variant="filled" data-size="md">
10
+ <span class="kpt-button__icon kpt-icon-host">…</span>
10
11
  <span class="kpt-button__content">…</span>
12
+ <span class="kpt-button__icon kpt-button__icon--end kpt-icon-host">…</span>
11
13
  </button>
12
14
  </span>
13
15
 
14
16
  ## Props
15
- - `variant`: 'filled' | 'tonal' | 'outline' | 'text' | 'danger' (default 'filled')
16
- - `size`: 'sm' | 'md' | 'lg' (default 'md')
17
+ - `variant`: 'filled' | 'elevated' | 'tonal' | 'outline' | 'text' | 'link' | 'danger' | 'success' | 'warning' | 'info' (default 'filled')
18
+ - `size`: 'xs' | 'sm' | 'md' | 'lg' | 'xl' (default 'md')
17
19
  - `type`: 'button' | 'submit' | 'reset' (default 'button')
18
20
  - `disabled`: boolean
19
21
  - `loading`: boolean — a spinner + blocked interaction, sets `aria-busy`
20
22
  - `fullWidth`: boolean — the full width of the container
23
+ - `icon` / `iconEnd`: string — a name from the icon registry
24
+ - `iconClassName` / `iconEndClassName`: string — an extra class on that icon
25
+ - `href`, `target`, `rel`: string — render an `<a>` instead of a `<button>`
21
26
  - `asChild`: boolean — render the single child instead of `<button>`, keeping every class
22
27
  - `hostProps`: props for the `.kpt-button-host` wrapper
23
- - `className`, `ref` and every other `<button>` prop go to the INNER `<button>`
28
+ - `className`, `ref` and every other `<button>` prop go to the INNER element
24
29
 
25
30
  ## Events
26
31
  No events of its own — bind `onClick` and it lands on the native `<button>`.
27
32
 
33
+ ## Icons
34
+ Two ways in, one result. `icon` and `iconEnd` take a name from the icon registry and the component
35
+ renders it; anything put in `children` is laid out the same way. Both get the gap from
36
+ `--kpt-button-gap` and a size that follows `size` (0.875rem at `xs` up to 1.5rem at `xl`) — the
37
+ content box is a flex container, so an icon sits on the centre line rather than on the text baseline.
38
+
39
+ `loading` replaces the leading icon with the spinner instead of standing next to it, and the
40
+ spinner takes that icon's size, so the label does not jump when loading starts. The trailing icon
41
+ stays put — it usually marks direction rather than the action.
42
+
43
+ `iconClassName` / `iconEndClassName` add a class next to the permanent `.kpt-button__icon` hook, for
44
+ the cases where one instance needs a different colour or a rotation.
45
+
46
+ ## Link and the `link` variant
47
+ Two different things. `variant="link"` only changes the looks — underline, transparent background,
48
+ no side padding, so it sits inside a sentence. `href` changes the element: the component renders
49
+ `<a class="kpt-button">` instead of `<button>`, and the two combine freely (a filled button can be
50
+ a link, a `link` variant can be a plain button). `asChild` wins over `href`: whoever substitutes
51
+ their own element decides the tag.
52
+
53
+ A disabled link loses its `href` rather than just gaining an attribute — otherwise it would stay
54
+ clickable and reachable with the keyboard; it also gets `aria-disabled="true"` and `tabindex="-1"`.
55
+ `target="_blank"` without an explicit `rel` gets `rel="noopener noreferrer"`.
56
+
28
57
  ## Examples
29
58
  <KptButton onClick={save}>Save</KptButton>
30
- <KptButton variant="outline" size="sm">Cancel</KptButton>
59
+ <KptButton icon="device-floppy" onClick={save}>Save</KptButton>
60
+ <KptButton iconEnd="chevron-right" variant="outline">Next</KptButton>
61
+ <KptButton icon="printer" variant="text" size="sm">Print</KptButton>
62
+ <KptButton variant="success">Approve</KptButton>
63
+ <KptButton variant="link" href="/docs">Documentation</KptButton>
31
64
  <KptButton variant="danger" loading={deleting}>Delete</KptButton>
32
- <form action={submitAction}><KptButton type="submit">Send</KptButton></form>
65
+ <KptButton variant="outline"><KptIcon name="printer" />Print</KptButton>
33
66
  <KptButton asChild><a href="/docs">Documentation</a></KptButton>
34
67
 
35
68
  ## asChild
@@ -39,10 +72,12 @@ element gets `disabled` when it is a native `<button>`, and `aria-disabled` othe
39
72
  element with no `:disabled` state therefore does not get the dimmed look.
40
73
 
41
74
  ## 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`.
75
+ `--kpt-button-radius`, `--kpt-button-font-weight`, `--kpt-button-gap`, and one group per variant:
76
+ `--kpt-button-filled-{bg,fg,bg-hover,bg-active}`, `--kpt-button-elevated-{bg,fg,bg-hover}`,
77
+ `--kpt-button-tonal-{bg,fg,bg-hover}`, `--kpt-button-outline-{border,fg,bg-hover}`,
78
+ `--kpt-button-text-{fg,bg-hover}`, `--kpt-button-link-{fg,fg-hover}`,
79
+ `--kpt-button-danger-{bg,fg,bg-hover}` and the same triplet for success, warning and info.
80
+ The elevated variant also reaches for `--kpt-shadow-sm` and `--kpt-shadow-md`.
46
81
 
47
82
  ## The state layer (hover / active)
48
83
  Hover and active feedback is carried by an `::after` pseudo-element (`opacity` 0 → 1), not by
@@ -53,4 +88,5 @@ along with the stylesheet.
53
88
  ## Accessibility
54
89
  - A native `<button>` — full keyboard and screen reader support.
55
90
  - `loading` sets `aria-busy="true"` and disables the button.
91
+ - An `<a>` matches no `:disabled`, so the disabled look keys off `[aria-disabled='true']` as well.
56
92
  - The focus ring comes from `@konce-pt/styles` (`--kpt-color-focus-ring`).
@@ -10,6 +10,8 @@ export interface KptButtonGroupProps<T> extends Omit<ComponentPropsWithRef<'div'
10
10
  disabled?: boolean;
11
11
  /** Wywoływane po wyborze — moment, w którym kontrolka staje się dotknięta. */
12
12
  onTouch?: () => void;
13
+ /** Chowa etykiety — zostaje sama ikona, a `label` zasila `aria-label` i `title`. */
14
+ iconOnly?: boolean;
13
15
  /** Propsy wrappera hosta (`.kpt-button-group-host`). */
14
16
  hostProps?: KptHostProps<'span'>;
15
17
  }
@@ -17,9 +19,13 @@ export interface KptButtonGroupProps<T> extends Omit<ComponentPropsWithRef<'div'
17
19
  * Segmentowana grupa przycisków (toggle) — pojedynczy wybór z opcji.
18
20
  *
19
21
  * Działa kontrolowana (`value` + `onValueChange`) i niekontrolowana (`defaultValue`).
22
+ * Pozycja może mieć ikonę (`KptOption.icon`); `iconOnly` chowa etykietę, ale zostawia ją
23
+ * w nazwie dostępnej, więc czytnik ekranu dalej wie, co wybiera.
20
24
  *
21
25
  * @example
22
26
  * <KptButtonGroup value={view} onValueChange={setView}
23
27
  * options={[{ label: 'Lista', value: 'list' }, { label: 'Siatka', value: 'grid' }]} />
28
+ * <KptButtonGroup value={view} onValueChange={setView} iconOnly
29
+ * options={[{ icon: 'list', label: 'Lista', value: 'list' }]} />
24
30
  */
25
- export declare function KptButtonGroup<T>({ options, value, defaultValue, onValueChange, size, disabled, onTouch, hostProps, className, ...rest }: Readonly<KptButtonGroupProps<T>>): import("react").JSX.Element;
31
+ export declare function KptButtonGroup<T>({ options, value, defaultValue, onValueChange, size, disabled, onTouch, iconOnly, hostProps, className, ...rest }: Readonly<KptButtonGroupProps<T>>): import("react").JSX.Element;
@@ -1,16 +1,21 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cn } from "../internal/cn.js";
3
3
  import { useControllableState } from "../internal/use-controllable-state.js";
4
+ import { KptIcon } from "../icon/icon.js";
4
5
  /**
5
6
  * Segmentowana grupa przycisków (toggle) — pojedynczy wybór z opcji.
6
7
  *
7
8
  * Działa kontrolowana (`value` + `onValueChange`) i niekontrolowana (`defaultValue`).
9
+ * Pozycja może mieć ikonę (`KptOption.icon`); `iconOnly` chowa etykietę, ale zostawia ją
10
+ * w nazwie dostępnej, więc czytnik ekranu dalej wie, co wybiera.
8
11
  *
9
12
  * @example
10
13
  * <KptButtonGroup value={view} onValueChange={setView}
11
14
  * options={[{ label: 'Lista', value: 'list' }, { label: 'Siatka', value: 'grid' }]} />
15
+ * <KptButtonGroup value={view} onValueChange={setView} iconOnly
16
+ * options={[{ icon: 'list', label: 'Lista', value: 'list' }]} />
12
17
  */
13
- export function KptButtonGroup({ options = [], value, defaultValue, onValueChange, size = 'md', disabled = false, onTouch, hostProps, className, ...rest }) {
18
+ export function KptButtonGroup({ options = [], value, defaultValue, onValueChange, size = 'md', disabled = false, onTouch, iconOnly = false, hostProps, className, ...rest }) {
14
19
  const { className: hostClassName, ...hostRest } = hostProps ?? {};
15
20
  const [current, setCurrent] = useControllableState({
16
21
  value,
@@ -23,8 +28,8 @@ export function KptButtonGroup({ options = [], value, defaultValue, onValueChang
23
28
  setCurrent(option.value);
24
29
  onTouch?.();
25
30
  };
26
- return (_jsx("span", { className: cn('kpt-button-group-host', hostClassName), ...hostRest, children: _jsx("div", { className: cn('kpt-button-group', className), role: "group", "data-size": size, ...rest, children: options.map((option) => {
31
+ return (_jsx("span", { className: cn('kpt-button-group-host', hostClassName), ...hostRest, children: _jsx("div", { className: cn('kpt-button-group', className), role: "group", "data-size": size, "data-icon-only": iconOnly ? '' : undefined, ...rest, children: options.map((option) => {
27
32
  const active = option.value === current;
28
- return (_jsx("button", { type: "button", className: cn('kpt-button-group__item', active && 'is-active'), disabled: disabled || option.disabled, "aria-pressed": active, onClick: () => select(option), children: option.label }, String(option.value)));
33
+ return (_jsxs("button", { type: "button", className: cn('kpt-button-group__item', active && 'is-active'), disabled: disabled || option.disabled, "aria-pressed": active, "aria-label": iconOnly ? option.label : undefined, title: iconOnly ? option.label : undefined, onClick: () => select(option), children: [option.icon ? _jsx(KptIcon, { className: "kpt-button-group__icon", name: option.icon }) : null, iconOnly ? null : _jsx("span", { className: "kpt-button-group__label", children: option.label })] }, String(option.value)));
29
34
  }) }) }));
30
35
  }
@@ -6,20 +6,39 @@ Import: `import { KptButtonGroup } from '@konce-pt/react';`
6
6
  ## DOM
7
7
  <span class="kpt-button-group-host">
8
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>
9
+ <button type="button" class="kpt-button-group__item is-active" aria-pressed="true">
10
+ <span class="kpt-button-group__icon kpt-icon-host">…</span>
11
+ <span class="kpt-button-group__label">List</span>
12
+ </button>
13
+ <button type="button" class="kpt-button-group__item" aria-pressed="false">
14
+ <span class="kpt-button-group__label">Grid</span>
15
+ </button>
11
16
  </div>
12
17
  </span>
13
18
 
14
19
  ## Props
15
- - `options`: `KptOption<T>[]` — `{ label, value, disabled? }`
20
+ - `options`: `KptOption<T>[]` — `{ label, value, disabled?, icon? }`
16
21
  - `value` / `defaultValue` / `onValueChange` — controlled or uncontrolled
17
22
  - `size`: 'sm' | 'md' | 'lg' (default 'md')
18
23
  - `disabled`: boolean — disables the whole group; a single option disables itself
24
+ - `iconOnly`: boolean — hides the labels and leaves the icons alone
19
25
  - `onTouch()` — fires on a pick, the moment the control becomes touched
20
26
  - `hostProps` — props for the `.kpt-button-group-host` wrapper
21
27
  - `className`, `ref` and every other `<div>` prop go to `.kpt-button-group`
22
28
 
29
+ ## Icons
30
+ An option carries its icon by name: `KptOption.icon` is a key from the icon registry, and the group
31
+ renders it before the label. The item is a flex box, so the icon gets the `--kpt-button-gap` spacing
32
+ and a size that follows `size` (1rem at `sm` up to 1.25rem at `lg`) — the same scale as `KptButton`,
33
+ so a group standing next to a button matches it.
34
+
35
+ `iconOnly` drops the label from the page but keeps it as the accessible name: the item gets both
36
+ `aria-label` and `title`, so a screen reader still says "Grid" and a mouse user gets a tooltip.
37
+ Each item also becomes a square, because a lone icon inside a 1rem side padding looks adrift.
38
+
39
+ The icon takes its colour from `currentColor`, so the active item — filled with
40
+ `--kpt-color-primary` — flips the icon to `--kpt-color-on-primary` with no extra rule.
41
+
23
42
  ## Values of any type
24
43
  The value is generic, so options can carry objects, not just strings. Keys come from
25
44
  `String(option.value)`, so distinct options need distinct string forms — for object values give
@@ -29,12 +48,18 @@ each one a stable id and map back to the object yourself.
29
48
  <KptButtonGroup value={view} onValueChange={setView}
30
49
  options={[{ label: 'List', value: 'list' }, { label: 'Grid', value: 'grid' }]} />
31
50
 
51
+ <KptButtonGroup value={view} onValueChange={setView}
52
+ options={[{ icon: 'list', label: 'List', value: 'list' }]} />
53
+
54
+ <KptButtonGroup value={align} onValueChange={setAlign} iconOnly options={alignOptions} />
55
+
32
56
  <KptButtonGroup size="sm" defaultValue="M" options={sizeOptions} />
33
57
 
34
58
  ## Tokens
35
59
  `--kpt-color-surface`, `--kpt-color-muted`, `--kpt-color-primary` (the active segment),
36
- `--kpt-color-border-strong`, `--kpt-radius-md`.
60
+ `--kpt-color-border-strong`, `--kpt-radius-md`, `--kpt-button-gap`.
37
61
 
38
62
  ## Accessibility
39
63
  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.
64
+ segment is on, and the keyboard walks them like any other buttons. With `iconOnly` the name comes
65
+ from `aria-label`, so the announcement does not degrade to a bare "button".
@@ -1,9 +1,17 @@
1
1
  import { type ComponentPropsWithRef, type ReactNode } from 'react';
2
2
  import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
3
+ /** Szerokość okna. `md` to wartość domyślna i zarazem jedyna, jaką okno miało wcześniej. */
4
+ export type KptDialogSize = 'sm' | 'md' | 'lg';
3
5
  export interface KptDialogProps extends Omit<ComponentPropsWithRef<'dialog'>, 'open' | 'onClose'>, KptI18nOverrides {
4
6
  open: boolean;
5
7
  onOpenChange: (open: boolean) => void;
6
8
  header?: string;
9
+ /**
10
+ * Szerokość okna: `sm` 24rem, `md` 32rem (domyślna), `lg` 48rem — zawsze przycięta do szerokości
11
+ * okna przeglądarki. `lg` jest dla treści, której nie da się sensownie zwęzić: kodu, tabeli,
12
+ * porównania dwóch kolumn.
13
+ */
14
+ size?: KptDialogSize;
7
15
  /** Pozwala zamknąć klawiszem Esc i przyciskiem ×. */
8
16
  dismissible?: boolean;
9
17
  children?: ReactNode;
@@ -21,7 +29,7 @@ export interface KptDialogProps extends Omit<ComponentPropsWithRef<'dialog'>, 'o
21
29
  * </KptDialog.Footer>
22
30
  * </KptDialog>
23
31
  */
24
- export declare function KptDialog({ open, onOpenChange, header, dismissible, locale, dictionary, className, children, ref, ...rest }: Readonly<KptDialogProps>): import("react").JSX.Element;
32
+ export declare function KptDialog({ open, onOpenChange, header, size, dismissible, locale, dictionary, className, children, ref, ...rest }: Readonly<KptDialogProps>): import("react").JSX.Element;
25
33
  export declare namespace KptDialog {
26
34
  var Footer: typeof KptDialogFooter;
27
35
  }
@@ -18,7 +18,7 @@ import { KptIcon } from "../icon/icon.js";
18
18
  * </KptDialog.Footer>
19
19
  * </KptDialog>
20
20
  */
21
- export function KptDialog({ open, onOpenChange, header = '', dismissible = true, locale, dictionary, className, children, ref, ...rest }) {
21
+ export function KptDialog({ open, onOpenChange, header = '', size = 'md', dismissible = true, locale, dictionary, className, children, ref, ...rest }) {
22
22
  const { t } = useKptMessages({ locale, dictionary });
23
23
  const dialogRef = useRef(null);
24
24
  // Element trafia do kontekstu, więc musi wywołać render — sam `ref` by nie wystarczył.
@@ -46,7 +46,7 @@ export function KptDialog({ open, onOpenChange, header = '', dismissible = true,
46
46
  }
47
47
  onOpenChange(false);
48
48
  };
49
- return (_jsx(KptOverlayContainerContext.Provider, { value: open ? dialogEl : null, children: _jsxs("dialog", { ref: composeRefs(dialogRef, setDialogEl, ref), className: cn('kpt-dialog', className), onClose: () => onOpenChange(false), onCancel: onCancel, ...rest, children: [header ? (_jsxs("div", { className: "kpt-dialog__header", children: [_jsx("h2", { className: "kpt-dialog__title", children: header }), dismissible ? (_jsx("button", { className: "kpt-dialog__close", type: "button", onClick: () => onOpenChange(false), "aria-label": t('common.close'), children: _jsx(KptIcon, { name: "x" }) })) : null] })) : null, _jsx("div", { className: "kpt-dialog__body", children: body }), footers] }) }));
49
+ return (_jsx(KptOverlayContainerContext.Provider, { value: open ? dialogEl : null, children: _jsxs("dialog", { ref: composeRefs(dialogRef, setDialogEl, ref), className: cn('kpt-dialog', className), "data-size": size, onClose: () => onOpenChange(false), onCancel: onCancel, ...rest, children: [header ? (_jsxs("div", { className: "kpt-dialog__header", children: [_jsx("h2", { className: "kpt-dialog__title", children: header }), dismissible ? (_jsx("button", { className: "kpt-dialog__close", type: "button", onClick: () => onOpenChange(false), "aria-label": t('common.close'), children: _jsx(KptIcon, { name: "x" }) })) : null] })) : null, _jsx("div", { className: "kpt-dialog__body", children: body }), footers] }) }));
50
50
  }
51
51
  /**
52
52
  * Stopka okna. Nosi atrybut `kptdialogfooter`, w który celuje wspólny SCSS (nazwy atrybutów
@@ -1,47 +1,95 @@
1
1
  # KptProgress (@konce-pt/react)
2
2
 
3
- A progress indicator — linear or circular, determinate (0–100) or indeterminate (`value` = null).
3
+ A progress indicator — linear or circular, determinate (`min`–`max`) or indeterminate (`value` = null).
4
4
  Import: `import { KptProgress } from '@konce-pt/react';`
5
5
 
6
6
  ## DOM
7
- <!-- linear -->
8
- <div class="kpt-progress-host">
9
- <div class="kpt-progress__linear" data-variant="primary" role="progressbar" aria-valuenow="65">
10
- <div class="kpt-progress__bar" style="width: 65%"></div>
7
+ <!-- linear, with a label, a buffer and a value -->
8
+ <div class="kpt-progress-host" data-type="linear" data-size="md" data-variant="primary" data-label-position="top">
9
+ <span class="kpt-progress__label">Loading…</span>
10
+ <div class="kpt-progress__linear" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="80">
11
+ <div class="kpt-progress__buffer" style="width: 92%"></div>
12
+ <div class="kpt-progress__bar" style="width: 80%"></div>
11
13
  </div>
14
+ <span class="kpt-progress__value" aria-hidden="true">80%</span>
12
15
  </div>
13
16
 
14
17
  <!-- circular -->
15
- <svg class="kpt-progress__circular" data-variant="success" viewBox="0 0 40 40" role="progressbar" aria-valuenow="75">
16
- <circle class="kpt-progress__track" cx="20" cy="20" r="16"></circle>
17
- <circle class="kpt-progress__bar" cx="20" cy="20" r="16" style="stroke-dasharray: 100.531; stroke-dashoffset: 25.1327"></circle>
18
- </svg>
18
+ <div class="kpt-progress-host" data-type="circular" data-size="md" data-variant="success">
19
+ <svg class="kpt-progress__circular" viewBox="0 0 40 40" role="progressbar" aria-valuenow="75">
20
+ <circle class="kpt-progress__track" cx="20" cy="20" r="16"></circle>
21
+ <circle class="kpt-progress__bar" cx="20" cy="20" r="16" style="stroke-dasharray: 100.531; stroke-dashoffset: 25.1327"></circle>
22
+ </svg>
23
+ <span class="kpt-progress__value" aria-hidden="true">75%</span>
24
+ </div>
25
+
26
+ The label, the track and the value are siblings straight under the host — there is no wrapper element.
27
+ `data-variant`, `data-type`, `data-size` and `data-label-position` all sit on the host, which is also
28
+ where the private `--_color` and `--_thickness` are resolved. This markup matches the Angular port
29
+ element for element, so both share one stylesheet.
19
30
 
20
31
  ## Props
21
- - `value`: number | null (default null) — 0–100, or null for indeterminate
32
+ - `value`: number | null (default null) — a value on the `min`–`max` scale, or null for indeterminate
33
+ - `min`: number (default 0); `max`: number (default 100)
22
34
  - `variant`: 'primary' | 'success' | 'danger' | 'warning' | 'info' (default 'primary')
23
35
  - `type`: 'linear' | 'circular' (default 'linear')
36
+ - `size`: 'sm' | 'md' | 'lg' (default 'md') — track thickness, and arc width on the circular variant
24
37
  - `diameter`: number (default 40) — the circular variant's size in pixels
38
+ - `bufferValue`: number | null (default null) — a second, dimmed bar behind the main one; linear only
39
+ - `showValue`: boolean (default false) — turns the value area on; `children` needs it too
40
+ - `valueFormat`: 'percent' | 'fraction' | 'none' (default 'percent')
41
+ - `label`: string — descriptive text next to the bar; it also becomes the `aria-label`
42
+ - `labelPosition`: 'top' | 'bottom' | 'end' (default 'top') — ignored by the circular variant
43
+ - `locale`, `dictionary` — per-instance dictionary overrides
25
44
  - `className`, `ref` and every other `<div>` prop go to the host
26
45
 
46
+ ## Value label
47
+ `showValue` opens the value area; `valueFormat` decides what goes in it — `percent` renders `80%`,
48
+ `fraction` renders `152 / 160` from `value` and `max`, `none` leaves it empty. Both strings come from
49
+ the `KptMessages` dictionary (`progress.percent`, `progress.fraction`), so a new locale can reshape them.
50
+
51
+ `children` replaces the formatted value instead of adding to it, which is how a circle shows a headline
52
+ number over a caption. It still requires `showValue`, because that prop is what renders the element the
53
+ content lands in.
54
+
55
+ The circular variant always centres the value over the arc; `label` is not drawn there, it only feeds
56
+ the accessible name.
57
+
27
58
  ## How the arc is drawn
28
- The circle lives in a fixed `viewBox="0 0 40 40"` with `r=16`, so the circumference is computed
29
- from that radius, not from `diameter` — the SVG scales, the maths does not change. The arc is the
30
- dash offset: full circumference means empty, zero means complete. An indeterminate circle is pinned
31
- at a quarter of the circumference and spun by CSS.
59
+ The circle lives in a fixed `viewBox="0 0 40 40"` with `r=16`, so the circumference is computed from
60
+ that radius, not from `diameter` — the SVG scales, the maths does not change. The arc is the dash
61
+ offset: full circumference means empty, zero means complete. `size` only changes `stroke-width`, and
62
+ the widest step still fits inside the box, so no arc is ever clipped.
32
63
 
33
- Values outside 0–100 are clamped rather than rejected, so a computed percentage that briefly
34
- overshoots cannot break the drawing.
64
+ Values outside the range are clamped rather than rejected, `max` ≤ `min` collapses to zero, and the
65
+ buffer is never allowed behind the main bar — a computed percentage that briefly overshoots cannot
66
+ break the drawing.
35
67
 
36
68
  ## Examples
37
- <KptProgress value={65} />
38
- <KptProgress value={null} variant="info" />
39
- <KptProgress type="circular" value={75} variant="success" diameter={64} />
69
+ <KptProgress value={60} />
70
+ <KptProgress value={80} label="Loading…" showValue />
71
+ <KptProgress value={152} max={160} size="lg" showValue valueFormat="fraction" labelPosition="bottom" />
72
+ <KptProgress value={65} showValue labelPosition="end" />
73
+ <KptProgress value={38} bufferValue={62} />
74
+ <KptProgress type="circular" value={80} showValue />
75
+ <KptProgress type="circular" value={72} diameter={140} size="lg" showValue>
76
+ <strong>29</strong>
77
+ <small>Excellent</small>
78
+ </KptProgress>
79
+ <KptProgress value={null} />
40
80
 
41
81
  ## Tokens
42
82
  The semantic colour of each variant (`--kpt-color-primary` / `-success` / `-danger` / `-warning` /
43
- `-info`), `--kpt-color-muted` for the track.
83
+ `-info`), plus the component layer: `--kpt-progress-track-bg`, `--kpt-progress-thickness-sm`,
84
+ `--kpt-progress-thickness-md`, `--kpt-progress-thickness-lg`, `--kpt-progress-buffer-opacity`,
85
+ `--kpt-progress-label-fg`, `--kpt-progress-value-fg`, `--kpt-progress-gap`.
44
86
 
45
87
  ## Accessibility
46
- `role="progressbar"` with `aria-valuenow`; an indeterminate bar leaves the value out, which is how
47
- a screen reader learns the progress is unknown rather than zero.
88
+ `role="progressbar"` with `aria-valuemin`, `aria-valuemax` and `aria-valuenow`; an indeterminate bar
89
+ leaves the value out, which is how a screen reader learns the progress is unknown rather than zero.
90
+ `valueFormat="fraction"` also sets `aria-valuetext`, so the reader says "152 / 160" instead of
91
+ announcing the raw number as a percentage. The visible value is `aria-hidden`, so it is never read
92
+ twice. The accessible name comes from `label`, or from the dictionary when there is none.
93
+
94
+ Under `prefers-reduced-motion: reduce` the indeterminate animation slows down instead of stopping —
95
+ a frozen progress indicator reads as a hung application.
@@ -1,19 +1,46 @@
1
- import type { ComponentPropsWithRef } from 'react';
1
+ import type { ComponentPropsWithRef, ReactNode } from 'react';
2
+ import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
2
3
  export type KptProgressVariant = 'primary' | 'success' | 'danger' | 'warning' | 'info';
3
- export interface KptProgressProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
4
- /** 0–100 albo `null` = postęp nieokreślony. */
4
+ export type KptProgressType = 'linear' | 'circular';
5
+ export type KptProgressSize = 'sm' | 'md' | 'lg';
6
+ /** Postać etykiety wartości: procent, „ile z ilu" albo brak. */
7
+ export type KptProgressValueFormat = 'percent' | 'fraction' | 'none';
8
+ /** Gdzie siada wiersz etykiety względem paska (tylko `type="linear"`). */
9
+ export type KptProgressLabelPosition = 'top' | 'bottom' | 'end';
10
+ export interface KptProgressProps extends ComponentPropsWithRef<'div'>, KptI18nOverrides {
11
+ /** Wartość w skali `min`–`max` albo `null` = postęp nieokreślony. */
5
12
  value?: number | null;
13
+ min?: number;
14
+ max?: number;
6
15
  variant?: KptProgressVariant;
7
- type?: 'linear' | 'circular';
16
+ type?: KptProgressType;
17
+ /** Grubość toru: `sm` 0.25rem, `md` 0.5rem, `lg` 0.75rem. */
18
+ size?: KptProgressSize;
8
19
  /** Średnica wariantu kołowego w pikselach. */
9
20
  diameter?: number;
21
+ /** Postęp buforowania — drugi, przygaszony pasek pod głównym (tylko liniowy). */
22
+ bufferValue?: number | null;
23
+ /** Włącza obszar etykiety wartości — razem z nim renderuje się `children`. */
24
+ showValue?: boolean;
25
+ valueFormat?: KptProgressValueFormat;
26
+ /** Tekst opisowy obok paska; służy też za `aria-label`. */
27
+ label?: string;
28
+ labelPosition?: KptProgressLabelPosition;
29
+ /** Zastępuje sformatowaną wartość — wymaga `showValue`. */
30
+ children?: ReactNode;
10
31
  }
11
32
  /**
12
- * Wskaźnik postępu — liniowy lub kołowy, określony (0–100) albo nieokreślony (`value` = null).
33
+ * Wskaźnik postępu — liniowy lub kołowy, określony (`min`–`max`) albo nieokreślony (`value` = null).
34
+ *
35
+ * Etykietę wartości włącza `showValue`; `children` zastępuje sformatowaną wartość,
36
+ * więc w środku koła można pokazać cokolwiek.
13
37
  *
14
38
  * @example
15
39
  * <KptProgress value={60} />
16
- * <KptProgress type="circular" value={75} variant="success" />
40
+ * <KptProgress value={80} label="Ładowanie…" showValue />
41
+ * <KptProgress value={152} max={160} showValue valueFormat="fraction" labelPosition="bottom" />
42
+ * <KptProgress value={38} bufferValue={62} />
43
+ * <KptProgress type="circular" value={75} variant="success" showValue />
17
44
  * <KptProgress value={null} />
18
45
  */
19
- export declare function KptProgress({ value, variant, type, diameter, className, ...rest }: Readonly<KptProgressProps>): import("react").JSX.Element;
46
+ export declare function KptProgress({ value, min, max, variant, type, size, diameter, bufferValue, showValue, valueFormat, label, labelPosition, locale, dictionary, className, children, ...rest }: Readonly<KptProgressProps>): import("react").JSX.Element;
@@ -1,21 +1,58 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { cn } from "../internal/cn.js";
3
+ import { useKptMessages } from "../i18n/i18n-context.js";
3
4
  /** Promień okręgu w viewBox 0 0 40 40 — obwód liczymy z niego, nie ze średnicy w pikselach. */
4
5
  const RADIUS = 16;
5
6
  const CIRCUMFERENCE = 2 * Math.PI * RADIUS;
6
7
  /**
7
- * Wskaźnik postępu — liniowy lub kołowy, określony (0–100) albo nieokreślony (`value` = null).
8
+ * Wskaźnik postępu — liniowy lub kołowy, określony (`min`–`max`) albo nieokreślony (`value` = null).
9
+ *
10
+ * Etykietę wartości włącza `showValue`; `children` zastępuje sformatowaną wartość,
11
+ * więc w środku koła można pokazać cokolwiek.
8
12
  *
9
13
  * @example
10
14
  * <KptProgress value={60} />
11
- * <KptProgress type="circular" value={75} variant="success" />
15
+ * <KptProgress value={80} label="Ładowanie…" showValue />
16
+ * <KptProgress value={152} max={160} showValue valueFormat="fraction" labelPosition="bottom" />
17
+ * <KptProgress value={38} bufferValue={62} />
18
+ * <KptProgress type="circular" value={75} variant="success" showValue />
12
19
  * <KptProgress value={null} />
13
20
  */
14
- export function KptProgress({ value = null, variant = 'primary', type = 'linear', diameter = 40, className, ...rest }) {
21
+ export function KptProgress({ value = null, min = 0, max = 100, variant = 'primary', type = 'linear', size = 'md', diameter = 40, bufferValue = null, showValue = false, valueFormat = 'percent', label, labelPosition = 'top', locale, dictionary, className, children, ...rest }) {
22
+ const { t } = useKptMessages({ locale, dictionary });
15
23
  const indeterminate = value == null;
16
- // Nieokreślony postęp pokazuje wycinek 25% obwodu, który obraca CSS — stąd stały offset.
17
- const offset = indeterminate
18
- ? CIRCUMFERENCE * 0.75
19
- : CIRCUMFERENCE * (1 - Math.min(100, Math.max(0, value)) / 100);
20
- return (_jsx("div", { className: cn('kpt-progress-host', className), ...rest, children: type === 'circular' ? (_jsxs("svg", { className: cn('kpt-progress__circular', indeterminate && 'kpt-progress--indeterminate'), "data-variant": variant, width: diameter, height: diameter, viewBox: "0 0 40 40", role: "progressbar", "aria-valuenow": value ?? undefined, children: [_jsx("circle", { className: "kpt-progress__track", cx: "20", cy: "20", r: RADIUS }), _jsx("circle", { className: "kpt-progress__bar", cx: "20", cy: "20", r: RADIUS, style: { strokeDasharray: CIRCUMFERENCE, strokeDashoffset: offset } })] })) : (_jsx("div", { className: "kpt-progress__linear", "data-variant": variant, role: "progressbar", "aria-valuenow": value ?? undefined, children: _jsx("div", { className: cn('kpt-progress__bar', indeterminate && 'kpt-progress--indeterminate'), style: { width: `${value ?? 0}%` } }) })) }));
24
+ /** Ułamek 0–1. Odporny na `max` ≤ `min` i na wartości spoza zakresu. */
25
+ const toRatio = (v) => {
26
+ if (v == null)
27
+ return 0;
28
+ const span = max - min;
29
+ if (span <= 0)
30
+ return 0;
31
+ return Math.min(1, Math.max(0, (v - min) / span));
32
+ };
33
+ const ratio = toRatio(value);
34
+ const percent = Math.round(ratio * 100);
35
+ // Bufor nigdy nie cofa się za pasek główny — inaczej wyglądałby jak błąd renderowania.
36
+ const bufferRatio = Math.max(ratio, toRatio(bufferValue));
37
+ const offset = CIRCUMFERENCE * (1 - ratio);
38
+ const fractionText = () => t('progress.fraction', { value: value ?? 0, max });
39
+ let valueText = '';
40
+ if (!indeterminate && valueFormat !== 'none') {
41
+ valueText = valueFormat === 'fraction' ? fractionText() : t('progress.percent', { value: percent });
42
+ }
43
+ /**
44
+ * Czytnik ekranu domyślnie odczytuje `aria-valuenow` jako procent, więc przy formacie „ile z ilu"
45
+ * podajemy mu ten sam napis, który widzi użytkownik.
46
+ */
47
+ const ariaValueText = indeterminate || valueFormat !== 'fraction' ? undefined : fractionText();
48
+ const ariaLabel = label ?? t(indeterminate ? 'progress.loading' : 'progress.label');
49
+ const barAria = {
50
+ role: 'progressbar',
51
+ 'aria-label': ariaLabel,
52
+ 'aria-valuemin': min,
53
+ 'aria-valuemax': max,
54
+ 'aria-valuenow': indeterminate ? undefined : (value ?? undefined),
55
+ 'aria-valuetext': ariaValueText,
56
+ };
57
+ return (_jsxs("div", { className: cn('kpt-progress-host', className), "data-type": type, "data-size": size, "data-variant": variant, "data-label-position": labelPosition, ...rest, children: [type === 'circular' ? (_jsxs("svg", { className: cn('kpt-progress__circular', indeterminate && 'kpt-progress--indeterminate'), width: diameter, height: diameter, viewBox: "0 0 40 40", ...barAria, children: [_jsx("circle", { className: "kpt-progress__track", cx: "20", cy: "20", r: RADIUS }), _jsx("circle", { className: "kpt-progress__bar", cx: "20", cy: "20", r: RADIUS, style: indeterminate ? undefined : { strokeDasharray: CIRCUMFERENCE, strokeDashoffset: offset } })] })) : (_jsxs(_Fragment, { children: [label ? _jsx("span", { className: "kpt-progress__label", children: label }) : null, _jsxs("div", { className: "kpt-progress__linear", ...barAria, children: [bufferValue != null && !indeterminate ? (_jsx("div", { className: "kpt-progress__buffer", style: { width: `${bufferRatio * 100}%` } })) : null, _jsx("div", { className: cn('kpt-progress__bar', indeterminate && 'kpt-progress--indeterminate'), style: indeterminate ? undefined : { width: `${ratio * 100}%` } })] })] })), showValue ? (_jsx("span", { className: "kpt-progress__value", "aria-hidden": "true", children: children ?? valueText })) : null] }));
21
58
  }
@@ -23,7 +23,7 @@ export type { KptDividerOrientation, KptDividerProps } from './divider/divider.t
23
23
  export { KptEmpty } from './empty/empty.tsx';
24
24
  export type { KptEmptyProps } from './empty/empty.tsx';
25
25
  export { KptProgress } from './progress/progress.tsx';
26
- export type { KptProgressProps, KptProgressVariant } from './progress/progress.tsx';
26
+ export type { KptProgressProps, KptProgressVariant, KptProgressType, KptProgressSize, KptProgressValueFormat, KptProgressLabelPosition, } from './progress/progress.tsx';
27
27
  export { KptSpinner } from './spinner/spinner.tsx';
28
28
  export type { KptSpinnerProps } from './spinner/spinner.tsx';
29
29
  export { KptSkeleton } from './skeleton/skeleton.tsx';
@@ -43,7 +43,7 @@ export { KPT_SIGNAL_GRID_CELL, KPT_SIGNAL_GRID_DENSITY, KPT_SIGNAL_GRID_FALLOFF,
43
43
  export { KptChip } from './chip/chip.tsx';
44
44
  export type { KptChipProps, KptChipTextTransform, KptChipVariant } from './chip/chip.tsx';
45
45
  export { KptDialog, KptDialogFooter } from './dialog/dialog.tsx';
46
- export type { KptDialogProps } from './dialog/dialog.tsx';
46
+ export type { KptDialogProps, KptDialogSize } from './dialog/dialog.tsx';
47
47
  export { KptDrawer } from './drawer/drawer.tsx';
48
48
  export type { KptDrawerPosition, KptDrawerProps } from './drawer/drawer.tsx';
49
49
  export { KptBottomSheet } from './bottom-sheet/bottom-sheet.tsx';
@@ -3,6 +3,8 @@ export interface KptOption<T = unknown> {
3
3
  label: string;
4
4
  value: T;
5
5
  disabled?: boolean;
6
+ /** Nazwa ikony z rejestru. Renderuje ją dziś wyłącznie `kpt-button-group`. */
7
+ icon?: string;
6
8
  }
7
9
  /** Wariant komunikatu — wspólny dla alertu i toastów. */
8
10
  export type KptAlertVariant = 'info' | 'success' | 'warning' | 'danger';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konce-pt/react",
3
- "version": "0.8.5",
3
+ "version": "0.9.1",
4
4
  "description": "Open-source React 19 component library on the Koncept UI design system — the same kpt-* CSS layer and --kpt-* tokens as the Angular port. Zero runtime dependencies.",
5
5
  "license": "MIT",
6
6
  "author": "konce.pt",
@@ -65,17 +65,17 @@
65
65
  "leaflet": "^1.9.0"
66
66
  },
67
67
  "dependencies": {
68
- "@konce-pt/chart": "0.8.5",
69
- "@konce-pt/backdrop": "0.8.5",
70
- "@konce-pt/datetime": "0.8.5",
71
- "@konce-pt/mention": "0.8.5",
72
- "@konce-pt/chat": "0.8.5",
73
- "@konce-pt/icons": "0.8.5",
74
- "@konce-pt/roadmap": "0.8.5",
75
- "@konce-pt/table": "0.8.5",
76
- "@konce-pt/map": "0.8.5",
77
- "@konce-pt/tokens": "0.8.5",
78
- "@konce-pt/i18n": "0.8.5"
68
+ "@konce-pt/chart": "0.9.1",
69
+ "@konce-pt/backdrop": "0.9.1",
70
+ "@konce-pt/datetime": "0.9.1",
71
+ "@konce-pt/chat": "0.9.1",
72
+ "@konce-pt/i18n": "0.9.1",
73
+ "@konce-pt/icons": "0.9.1",
74
+ "@konce-pt/tokens": "0.9.1",
75
+ "@konce-pt/table": "0.9.1",
76
+ "@konce-pt/map": "0.9.1",
77
+ "@konce-pt/roadmap": "0.9.1",
78
+ "@konce-pt/mention": "0.9.1"
79
79
  },
80
80
  "devDependencies": {
81
81
  "@types/react": "^19.2.0",