@konce-pt/react 0.8.5 → 0.9.0
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/dist/button/button.d.ts +23 -7
- package/dist/button/button.js +33 -9
- package/dist/button/llms.txt +48 -12
- package/dist/button-group/button-group.d.ts +7 -1
- package/dist/button-group/button-group.js +9 -4
- package/dist/button-group/llms.txt +30 -5
- package/dist/dialog/dialog.d.ts +9 -1
- package/dist/dialog/dialog.js +2 -2
- package/dist/progress/llms.txt +70 -22
- package/dist/progress/progress.d.ts +34 -7
- package/dist/progress/progress.js +46 -9
- package/dist/public-api.d.ts +2 -2
- package/dist/shared/types.d.ts +2 -0
- package/package.json +12 -12
package/dist/button/button.d.ts
CHANGED
|
@@ -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
|
|
20
|
-
* bo host niesie własne style
|
|
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
|
|
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;
|
package/dist/button/button.js
CHANGED
|
@@ -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
|
|
8
|
-
* bo host niesie własne style
|
|
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
|
|
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
|
|
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:
|
|
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.
|
|
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)
|
package/dist/button/llms.txt
CHANGED
|
@@ -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)
|
|
4
|
-
`.kpt-button-host` wrapper
|
|
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
|
|
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
|
|
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
|
-
<
|
|
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-
|
|
44
|
-
`--kpt-button-
|
|
45
|
-
|
|
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 (
|
|
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">
|
|
10
|
-
|
|
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".
|
package/dist/dialog/dialog.d.ts
CHANGED
|
@@ -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
|
}
|
package/dist/dialog/dialog.js
CHANGED
|
@@ -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
|
package/dist/progress/llms.txt
CHANGED
|
@@ -1,47 +1,95 @@
|
|
|
1
1
|
# KptProgress (@konce-pt/react)
|
|
2
2
|
|
|
3
|
-
A progress indicator — linear or circular, determinate (
|
|
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
|
-
<
|
|
10
|
-
|
|
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
|
-
<
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
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) —
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
|
34
|
-
|
|
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={
|
|
38
|
-
<KptProgress value={
|
|
39
|
-
<KptProgress
|
|
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-
|
|
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
|
|
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
|
|
4
|
-
|
|
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?:
|
|
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 (
|
|
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
|
|
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 (
|
|
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
|
|
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
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
}
|
package/dist/public-api.d.ts
CHANGED
|
@@ -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';
|
package/dist/shared/types.d.ts
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "0.9.0",
|
|
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/
|
|
69
|
-
"@konce-pt/
|
|
70
|
-
"@konce-pt/
|
|
71
|
-
"@konce-pt/
|
|
72
|
-
"@konce-pt/
|
|
73
|
-
"@konce-pt/
|
|
74
|
-
"@konce-pt/
|
|
75
|
-
"@konce-pt/
|
|
76
|
-
"@konce-pt/
|
|
77
|
-
"@konce-pt/
|
|
78
|
-
"@konce-pt/i18n": "0.
|
|
68
|
+
"@konce-pt/datetime": "0.9.0",
|
|
69
|
+
"@konce-pt/chat": "0.9.0",
|
|
70
|
+
"@konce-pt/backdrop": "0.9.0",
|
|
71
|
+
"@konce-pt/chart": "0.9.0",
|
|
72
|
+
"@konce-pt/icons": "0.9.0",
|
|
73
|
+
"@konce-pt/roadmap": "0.9.0",
|
|
74
|
+
"@konce-pt/table": "0.9.0",
|
|
75
|
+
"@konce-pt/mention": "0.9.0",
|
|
76
|
+
"@konce-pt/tokens": "0.9.0",
|
|
77
|
+
"@konce-pt/map": "0.9.0",
|
|
78
|
+
"@konce-pt/i18n": "0.9.0"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@types/react": "^19.2.0",
|