@konce-pt/react 0.8.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +381 -27
- package/dist/accordion/llms.txt +56 -0
- package/dist/alert/llms.txt +45 -0
- package/dist/app-shell/app-shell.d.ts +20 -0
- package/dist/app-shell/app-shell.js +24 -0
- package/dist/app-shell/llms.txt +59 -0
- package/dist/auth/auth.d.ts +1 -1
- package/dist/auth/llms.txt +65 -0
- package/dist/autocomplete/autocomplete.d.ts +1 -1
- package/dist/autocomplete/llms.txt +44 -0
- package/dist/avatar/llms.txt +42 -0
- package/dist/avatar-group/avatar-group.d.ts +1 -1
- package/dist/avatar-group/llms.txt +38 -0
- package/dist/badge/llms.txt +38 -0
- package/dist/bottom-sheet/llms.txt +43 -0
- package/dist/breadcrumb/breadcrumb.d.ts +1 -1
- package/dist/breadcrumb/llms.txt +35 -0
- package/dist/button/llms.txt +56 -0
- package/dist/button-group/button-group.d.ts +1 -1
- package/dist/button-group/llms.txt +40 -0
- package/dist/card/llms.txt +60 -0
- package/dist/carousel/carousel.d.ts +1 -1
- package/dist/carousel/llms.txt +47 -0
- package/dist/charts/chart-legend.d.ts +1 -1
- package/dist/charts/llms.txt +57 -0
- package/dist/checkbox/llms.txt +39 -0
- package/dist/chip/llms.txt +41 -0
- package/dist/chips-input/llms.txt +40 -0
- package/dist/clock/llms.txt +50 -0
- package/dist/color-picker/color-picker.d.ts +1 -1
- package/dist/color-picker/llms.txt +40 -0
- package/dist/confirm/llms.txt +44 -0
- package/dist/context-menu/context-menu.d.ts +1 -1
- package/dist/context-menu/llms.txt +39 -0
- package/dist/data-table/data-table-types.d.ts +1 -1
- package/dist/data-table/data-table.d.ts +2 -10
- package/dist/data-table/data-table.js +17 -9
- package/dist/data-table/llms.txt +79 -0
- package/dist/data-view/data-view.d.ts +1 -1
- package/dist/data-view/llms.txt +49 -0
- package/dist/date-range/llms.txt +54 -0
- package/dist/datepicker/llms.txt +74 -0
- package/dist/dialog/llms.txt +51 -0
- package/dist/divider/llms.txt +28 -0
- package/dist/drawer/llms.txt +47 -0
- package/dist/empty/llms.txt +35 -0
- package/dist/fab/llms.txt +37 -0
- package/dist/fieldset/llms.txt +43 -0
- package/dist/file-upload/llms.txt +48 -0
- package/dist/form-field/llms.txt +55 -0
- package/dist/galleria/galleria.d.ts +1 -1
- package/dist/galleria/llms.txt +46 -0
- package/dist/grid/llms.txt +110 -0
- package/dist/icon/icon-registry.d.ts +5 -2
- package/dist/icon/icon-registry.js +5 -3
- package/dist/icon/llms.txt +66 -0
- package/dist/icon-button/llms.txt +38 -0
- package/dist/icons-entry/public-api.d.ts +5 -1
- package/dist/icons-entry/public-api.js +7 -1
- package/dist/image/llms.txt +46 -0
- package/dist/input/llms.txt +54 -0
- package/dist/input-mask/llms.txt +37 -0
- package/dist/input-number/llms.txt +44 -0
- package/dist/input-otp/llms.txt +43 -0
- package/dist/internal/use-breakpoint.js +18 -10
- package/dist/knob/llms.txt +46 -0
- package/dist/listbox/listbox.d.ts +1 -1
- package/dist/listbox/llms.txt +39 -0
- package/dist/map/llms.txt +108 -0
- package/dist/megamenu/llms.txt +48 -0
- package/dist/megamenu/megamenu.d.ts +1 -1
- package/dist/menu/llms.txt +56 -0
- package/dist/menu/menu-item.d.ts +1 -1
- package/dist/menu/menu-rows.d.ts +1 -1
- package/dist/menu/menu.d.ts +1 -1
- package/dist/menu/menu.js +12 -1
- package/dist/menubar/llms.txt +45 -0
- package/dist/menubar/menubar.d.ts +1 -1
- package/dist/meter-group/llms.txt +47 -0
- package/dist/meter-group/meter-group.d.ts +1 -1
- package/dist/order-list/llms.txt +41 -0
- package/dist/paginator/llms.txt +48 -0
- package/dist/paginator/paginator.d.ts +1 -1
- package/dist/paginator/paginator.js +7 -1
- package/dist/panel/llms.txt +55 -0
- package/dist/password/llms.txt +45 -0
- package/dist/pick-list/llms.txt +52 -0
- package/dist/popover/llms.txt +42 -0
- package/dist/progress/llms.txt +47 -0
- package/dist/public-api.d.ts +2 -1
- package/dist/public-api.js +1 -1
- package/dist/radio-group/llms.txt +40 -0
- package/dist/radio-group/radio-group.d.ts +1 -1
- package/dist/rating/llms.txt +43 -0
- package/dist/rich-text/llms.txt +61 -0
- package/dist/rich-text/rich-text.d.ts +1 -1
- package/dist/roadmap/llms.txt +82 -0
- package/dist/scroll-top/llms.txt +41 -0
- package/dist/select/llms.txt +56 -0
- package/dist/select/select.d.ts +1 -1
- package/dist/sidenav/llms.txt +53 -0
- package/dist/skeleton/llms.txt +33 -0
- package/dist/slider/llms.txt +50 -0
- package/dist/speed-dial/llms.txt +46 -0
- package/dist/speed-dial/speed-dial.d.ts +1 -1
- package/dist/spinner/llms.txt +33 -0
- package/dist/split-button/llms.txt +54 -0
- package/dist/split-button/split-button.d.ts +1 -1
- package/dist/splitter/llms.txt +44 -0
- package/dist/stepper/llms.txt +64 -0
- package/dist/switch/llms.txt +42 -0
- package/dist/switch-group/llms.txt +43 -0
- package/dist/switch-group/switch-group.d.ts +1 -1
- package/dist/tabs/llms.txt +48 -0
- package/dist/textarea/llms.txt +40 -0
- package/dist/timeline/llms.txt +53 -0
- package/dist/timeline/timeline.d.ts +1 -1
- package/dist/toast/llms.txt +52 -0
- package/dist/toolbar/llms.txt +45 -0
- package/dist/tooltip/llms.txt +42 -0
- package/dist/tree/llms.txt +45 -0
- package/dist/tree/tree.d.ts +2 -2
- package/package.json +11 -11
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# KptCheckbox (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A checkbox. The whole layout is a `<label>`, so clicking the text toggles the field with no
|
|
4
|
+
`htmlFor` — the native `<input>` inside carries the semantics and the keyboard.
|
|
5
|
+
Import: `import { KptCheckbox } from '@konce-pt/react';`
|
|
6
|
+
|
|
7
|
+
## DOM
|
|
8
|
+
<span class="kpt-checkbox-host">
|
|
9
|
+
<label class="kpt-checkbox">
|
|
10
|
+
<input type="checkbox" class="kpt-checkbox__input">
|
|
11
|
+
<span class="kpt-checkbox__box" aria-hidden="true"><svg class="kpt-checkbox__check">…</svg></span>
|
|
12
|
+
<span class="kpt-checkbox__label">I accept the terms</span>
|
|
13
|
+
</label>
|
|
14
|
+
</span>
|
|
15
|
+
|
|
16
|
+
## Props
|
|
17
|
+
- `label`: string — the text next to the field; richer content goes in `children`
|
|
18
|
+
- `checked` / `defaultChecked` — controlled or uncontrolled, like a native checkbox
|
|
19
|
+
- `onCheckedChange(checked)` — the boolean alone; `onChange` still fires with the full event
|
|
20
|
+
- `onTouch()` — fires on blur
|
|
21
|
+
- `required`: boolean — set as `aria-required`, not as the native attribute
|
|
22
|
+
- `invalid`, `touched` — the error state, gated the same way as in `KptInput`
|
|
23
|
+
- `hostProps` — props for the `.kpt-checkbox-host` wrapper
|
|
24
|
+
- `className` goes to the `.kpt-checkbox` block; `name`, `value`, `ref` and the handlers go to the
|
|
25
|
+
native `<input>`, which is where a form expects them
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
<KptCheckbox name="terms" label="I accept the terms" />
|
|
29
|
+
<KptCheckbox checked={on} onCheckedChange={setOn} />
|
|
30
|
+
<KptCheckbox label="Newsletter" invalid={!!err} touched={blurred} />
|
|
31
|
+
|
|
32
|
+
## Tokens
|
|
33
|
+
`--kpt-form-field-border`, `--kpt-color-primary` (the checked box), `--kpt-color-on-primary`
|
|
34
|
+
(the tick), `--kpt-form-field-border-error`.
|
|
35
|
+
|
|
36
|
+
## Accessibility
|
|
37
|
+
A native `<input type="checkbox">` inside a `<label>` — the label is the accessible name and the
|
|
38
|
+
whole row is clickable. The box and the tick are `aria-hidden`, being pure decoration over the real
|
|
39
|
+
control.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# KptChip (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A chip / tag — a label, optionally removable.
|
|
4
|
+
Import: `import { KptChip } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<span class="kpt-chip-host">
|
|
8
|
+
<span class="kpt-chip" data-variant="primary">
|
|
9
|
+
React
|
|
10
|
+
<button type="button" class="kpt-chip__remove" aria-label="Remove">…icon…</button>
|
|
11
|
+
</span>
|
|
12
|
+
</span>
|
|
13
|
+
|
|
14
|
+
## Props
|
|
15
|
+
- `variant`: 'neutral' | 'primary' | 'success' | 'danger' | 'warning' | 'info' (default 'neutral')
|
|
16
|
+
- `removable`: boolean — adds the remove button
|
|
17
|
+
- `disabled`: boolean
|
|
18
|
+
- `onRemove()` — fired by the remove button
|
|
19
|
+
- `locale`, `dictionary` — i18n overrides for this component only
|
|
20
|
+
- `hostProps` — props for the `.kpt-chip-host` wrapper
|
|
21
|
+
- `className`, `ref` and every other `<span>` prop go to the inner `.kpt-chip`
|
|
22
|
+
|
|
23
|
+
## Removing is your decision
|
|
24
|
+
`onRemove` reports the intent; the chip does not remove itself from anything. Drop it from your own
|
|
25
|
+
list — that keeps the chip usable both in a controlled collection and as a standalone label.
|
|
26
|
+
|
|
27
|
+
## i18n
|
|
28
|
+
The remove button's label comes from `common.remove` in the dictionary.
|
|
29
|
+
|
|
30
|
+
## Examples
|
|
31
|
+
<KptChip>React</KptChip>
|
|
32
|
+
<KptChip variant="success" removable onRemove={() => drop(tag)}>UI</KptChip>
|
|
33
|
+
<KptCard.Tags><KptChip variant="info">Daily</KptChip></KptCard.Tags>
|
|
34
|
+
|
|
35
|
+
## Tokens
|
|
36
|
+
`--kpt-color-muted`, `--kpt-color-on-muted` and the semantic colour pairs of each variant
|
|
37
|
+
(`--kpt-color-primary` / `-subtle`, `-success`, `-danger`, `-warning`, `-info`).
|
|
38
|
+
|
|
39
|
+
## Accessibility
|
|
40
|
+
The remove button is a native `<button>` with `aria-label`, so it is reachable by keyboard and goes
|
|
41
|
+
`disabled` together with the chip.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# KptChipsInput (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A tag field — add with Enter or a comma, remove with Backspace or ×.
|
|
4
|
+
Import: `import { KptChipsInput } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<div class="kpt-chips-input-host">
|
|
8
|
+
<div class="kpt-chips-input">
|
|
9
|
+
…KptChip per tag…
|
|
10
|
+
<input class="kpt-chips-input__field" type="text">
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
## Props
|
|
15
|
+
- `value` / `defaultValue` / `onValueChange` — `string[]`
|
|
16
|
+
- `placeholder`: string — omitted falls back to `chipsInput.placeholder`
|
|
17
|
+
- `allowDuplicates`: boolean (default false)
|
|
18
|
+
- `disabled`, `invalid`, `touched`, `onTouch()`
|
|
19
|
+
- `locale`, `dictionary` — i18n overrides for this component only
|
|
20
|
+
|
|
21
|
+
## The buffer is not state
|
|
22
|
+
The text being typed lives in the DOM, not in React state: the component's value is the list of
|
|
23
|
+
committed tags, and the typing buffer is the field's own business. Every keystroke therefore does
|
|
24
|
+
not re-render the chips.
|
|
25
|
+
|
|
26
|
+
## Keyboard
|
|
27
|
+
Enter and a comma commit the tag. Backspace in an empty field eats the last chip — the behaviour
|
|
28
|
+
every mail client teaches. Blur commits whatever is left in the field, so a half-typed tag is not
|
|
29
|
+
lost by clicking away.
|
|
30
|
+
|
|
31
|
+
## Examples
|
|
32
|
+
<KptChipsInput value={tags} onValueChange={setTags} placeholder="Add a tag…" />
|
|
33
|
+
<KptChipsInput value={tags} onValueChange={setTags} allowDuplicates />
|
|
34
|
+
|
|
35
|
+
## Tokens
|
|
36
|
+
`--kpt-form-field-{bg,border,border-focus,border-error,radius}` plus the chip tokens.
|
|
37
|
+
|
|
38
|
+
## Accessibility
|
|
39
|
+
Each chip is a `KptChip` with a labelled remove button, and the field itself is a native input, so
|
|
40
|
+
tags are reachable and removable from the keyboard alone.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# KptClock (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A clock dial — two stages: hours → minutes, by click or by drag. Used by `KptDatepicker` and
|
|
4
|
+
`KptDateRange`, and available on its own.
|
|
5
|
+
Import: `import { KptClock } from '@konce-pt/react';`
|
|
6
|
+
|
|
7
|
+
## DOM
|
|
8
|
+
<div class="kpt-clock-host">
|
|
9
|
+
<div class="kpt-clock">
|
|
10
|
+
<div class="kpt-clock__time">
|
|
11
|
+
<button class="kpt-clock__seg is-active">15</button>:<button class="kpt-clock__seg">30</button>
|
|
12
|
+
<div class="kpt-clock__ampm">…12h only…</div>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="kpt-clock__face">
|
|
15
|
+
<span class="kpt-clock__hand"></span>
|
|
16
|
+
<button class="kpt-clock__num kpt-clock__num--inner">15</button>…
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
## Props
|
|
22
|
+
- `value` / `onValueChange` — 'HH:mm', always 24h regardless of `hourFormat`
|
|
23
|
+
- `hourFormat`: 12 | 24 (default 24)
|
|
24
|
+
- `minuteStep`: number (default 1) — minute snapping while dragging
|
|
25
|
+
- `className`, `ref` and every other `<div>` prop go to the host
|
|
26
|
+
|
|
27
|
+
## The value is the only state
|
|
28
|
+
Hours and minutes are derived from `value` on every render, so replacing it from outside needs no
|
|
29
|
+
synchronisation. The component keeps one piece of state of its own: which stage the dial is on.
|
|
30
|
+
|
|
31
|
+
## Two rings in 24h
|
|
32
|
+
12 and 1–11 sit on the outer ring, 00 and 13–23 on the inner one, because 24 numbers on one circle
|
|
33
|
+
are unreadable. While dragging, the click radius decides the ring; in 12h the AM/PM toggle decides
|
|
34
|
+
the half-day instead.
|
|
35
|
+
|
|
36
|
+
## Pointer moves without a button do nothing
|
|
37
|
+
The dial updates on `pointerdown` and on `pointermove` with the primary button held. Without that
|
|
38
|
+
check, merely moving the mouse across the clock would change the time.
|
|
39
|
+
|
|
40
|
+
## Examples
|
|
41
|
+
<KptClock value={time} onValueChange={setTime} />
|
|
42
|
+
<KptClock value={time} onValueChange={setTime} hourFormat={12} minuteStep={5} />
|
|
43
|
+
|
|
44
|
+
## Tokens
|
|
45
|
+
The dial `--kpt-color-surface-raised`, the hand and the selection `--kpt-color-primary`,
|
|
46
|
+
the numbers `--kpt-color-on-surface`.
|
|
47
|
+
|
|
48
|
+
## Accessibility
|
|
49
|
+
Every mark is a real `<button>`, so the dial can be used without a pointer; the hour and minute
|
|
50
|
+
segments are buttons too and switch the stage.
|
|
@@ -5,7 +5,7 @@ export interface KptColorPickerProps extends Omit<ComponentPropsWithRef<'div'>,
|
|
|
5
5
|
defaultValue?: string;
|
|
6
6
|
onValueChange?: (value: string) => void;
|
|
7
7
|
/** Paleta szybkiego wyboru. */
|
|
8
|
-
presets?: string[];
|
|
8
|
+
presets?: readonly string[];
|
|
9
9
|
disabled?: boolean;
|
|
10
10
|
invalid?: boolean;
|
|
11
11
|
touched?: boolean;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# KptColorPicker (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
Colour selection: a swatch, a hex field and presets. The value is a hex string.
|
|
4
|
+
Import: `import { KptColorPicker } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<div class="kpt-color-picker-host">
|
|
8
|
+
<div class="kpt-color-picker">
|
|
9
|
+
<label class="kpt-color-picker__swatch" style="background-color:#3b82f6">
|
|
10
|
+
<input type="color">
|
|
11
|
+
</label>
|
|
12
|
+
<input type="text" class="kpt-color-picker__hex" maxlength="7" value="#3b82f6">
|
|
13
|
+
<div class="kpt-color-picker__presets">
|
|
14
|
+
<button class="kpt-color-picker__preset is-active" aria-label="#3b82f6"></button>…
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
## Props
|
|
20
|
+
- `value` / `defaultValue` / `onValueChange` — the hex colour (default `'#3b82f6'`)
|
|
21
|
+
- `presets`: string[] — the quick-pick palette (8 colours by default; `[]` hides the row)
|
|
22
|
+
- `disabled`, `invalid`, `touched`, `onTouch()`
|
|
23
|
+
- `className`, `ref` and every other `<div>` prop go to the host
|
|
24
|
+
|
|
25
|
+
## The swatch is a native colour input
|
|
26
|
+
`<input type="color">` sits transparent under the coloured square, so the system palette is the
|
|
27
|
+
browser's — it works on a phone and there is no picker of ours to maintain. The hex field edits the
|
|
28
|
+
same value as text, and the presets write it directly.
|
|
29
|
+
|
|
30
|
+
## Examples
|
|
31
|
+
<KptColorPicker value={color} onValueChange={setColor} />
|
|
32
|
+
<KptColorPicker value={color} onValueChange={setColor} presets={['#ef4444', '#3b82f6']} />
|
|
33
|
+
|
|
34
|
+
## Tokens
|
|
35
|
+
The border `--kpt-form-field-border`, the field `--kpt-form-field-{bg,text,radius}`, the active
|
|
36
|
+
preset `--kpt-color-focus-ring`.
|
|
37
|
+
|
|
38
|
+
## Accessibility
|
|
39
|
+
Every preset is a `<button>` labelled with its hex value; the hex field carries `aria-invalid` once
|
|
40
|
+
the field is touched.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# kptConfirm + KptConfirmContainer (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
Confirmation built on `KptDialog`. Same pattern as `kptToast`: a module singleton plus one
|
|
4
|
+
container rendered once.
|
|
5
|
+
Import: `import { kptConfirm, KptConfirmContainer } from '@konce-pt/react';`
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
1. Render `<KptConfirmContainer />` once in the app, next to `<KptToastContainer />`.
|
|
9
|
+
2. `await kptConfirm(options)` from anywhere — it resolves to `true` or `false`.
|
|
10
|
+
|
|
11
|
+
## KptConfirmOptions
|
|
12
|
+
- `message`: string (required); `header?`: string
|
|
13
|
+
- `acceptLabel?`, `rejectLabel?`: string
|
|
14
|
+
- `variant?`: `'filled'` | `'danger'` — the accept button and the icon colour
|
|
15
|
+
- `icon?`: an icon name from the registry
|
|
16
|
+
|
|
17
|
+
## A store, not a hook
|
|
18
|
+
The state is a module singleton, so `kptConfirm()` works outside the React tree too — a data layer,
|
|
19
|
+
a navigation guard — without wrapping the app in a provider. `useKptConfirmRequest()` exposes the
|
|
20
|
+
current request through `useSyncExternalStore` for anyone rendering their own container.
|
|
21
|
+
|
|
22
|
+
## The promise always settles
|
|
23
|
+
Escape and the × close the dialog through `onOpenChange`, which resolves `false` — an awaiting
|
|
24
|
+
caller is never left hanging. A second `kptConfirm()` while one is open rejects the previous
|
|
25
|
+
request for the same reason: one request at a time, and nothing is dropped silently.
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
<KptConfirmContainer />
|
|
29
|
+
|
|
30
|
+
if (await kptConfirm({ message: 'Delete this record?', variant: 'danger', acceptLabel: 'Delete', icon: 'alert-triangle' })) {
|
|
31
|
+
remove();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
## i18n
|
|
35
|
+
The default header and the Confirm/Cancel labels come from the `confirm` namespace; the option
|
|
36
|
+
fields take priority.
|
|
37
|
+
|
|
38
|
+
## Tokens
|
|
39
|
+
Inherited from `KptDialog`; the icon follows the variant (`--kpt-color-danger` /
|
|
40
|
+
`--kpt-color-primary`).
|
|
41
|
+
|
|
42
|
+
## Accessibility
|
|
43
|
+
Everything comes from `KptDialog`, that is from the native `<dialog>`: the focus trap, Escape and
|
|
44
|
+
the backdrop are the platform's, not ours.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ComponentPropsWithRef, type ReactNode } from 'react';
|
|
2
2
|
import type { KptMenuItem } from '../menu/menu-item.ts';
|
|
3
3
|
export interface KptContextMenuProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'onSelect'> {
|
|
4
|
-
items?: KptMenuItem<T>[];
|
|
4
|
+
items?: readonly KptMenuItem<T>[];
|
|
5
5
|
/** Obszar reagujący na prawy przycisk myszy. */
|
|
6
6
|
children?: ReactNode;
|
|
7
7
|
onSelect?: (item: KptMenuItem<T>) => void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# KptContextMenu (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A context menu (right-click) over the area of its children. The panel is positioned at the cursor.
|
|
4
|
+
Import: `import { KptContextMenu } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<div class="kpt-context-menu-host">
|
|
8
|
+
<div class="kpt-context-menu__area">…children…</div>
|
|
9
|
+
<div class="kpt-context-menu__backdrop"></div>
|
|
10
|
+
<div class="kpt-context-menu__panel" role="menu" style="left: 320px; top: 210px">…</div>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
## Props
|
|
14
|
+
- `items`: `KptMenuItem<T>[]` — the same shape as `KptMenu`
|
|
15
|
+
- `children`: ReactNode — the area that reacts to the right mouse button
|
|
16
|
+
- `onSelect(item)` — the picked item
|
|
17
|
+
- `className`, `ref` and every other `<div>` prop go to the host
|
|
18
|
+
|
|
19
|
+
## Not an overlay
|
|
20
|
+
Unlike `KptMenu` this panel does not go through `KptOverlay`: there is no trigger to pin it to, so
|
|
21
|
+
the position comes from the click coordinates, and the look and the layer come from the shared SCSS
|
|
22
|
+
(`.kpt-context-menu__panel`). The panel therefore lives inside the host, not in a portal.
|
|
23
|
+
|
|
24
|
+
## Closing
|
|
25
|
+
A left click on the backdrop closes it; a right click on the backdrop closes it too and does not
|
|
26
|
+
open the browser's own menu. Picking an item closes it and reports the choice.
|
|
27
|
+
|
|
28
|
+
## Examples
|
|
29
|
+
<KptContextMenu items={items} onSelect={onSelect}>
|
|
30
|
+
<div className="area">Right-click here</div>
|
|
31
|
+
</KptContextMenu>
|
|
32
|
+
|
|
33
|
+
## Tokens
|
|
34
|
+
`--kpt-color-surface-raised`, `--kpt-color-border`, `--kpt-elevation-3`, `--kpt-color-danger`.
|
|
35
|
+
|
|
36
|
+
## Accessibility
|
|
37
|
+
The panel is `role="menu"` with `role="menuitem"` buttons. There is no keyboard path to open it —
|
|
38
|
+
that is what the right mouse button and the platform's context key are for, so pair the same actions
|
|
39
|
+
with a `KptMenu` when they must be reachable from the keyboard.
|
|
@@ -6,7 +6,7 @@ export type KptColumnEditType = 'text' | 'number' | 'date' | 'select' | 'multise
|
|
|
6
6
|
export interface KptColumnEdit {
|
|
7
7
|
type: KptColumnEditType;
|
|
8
8
|
/** Opcje dla `type: 'select'` / `'multiselect'`. */
|
|
9
|
-
options?: KptOption<unknown>[];
|
|
9
|
+
options?: readonly KptOption<unknown>[];
|
|
10
10
|
placeholder?: string;
|
|
11
11
|
/** Dla `type: 'number'`. */
|
|
12
12
|
min?: number;
|
|
@@ -9,7 +9,7 @@ export interface KptDataTableProps<T> extends Omit<ComponentPropsWithRef<'div'>,
|
|
|
9
9
|
filterRowPosition?: 'top' | 'bottom';
|
|
10
10
|
/** Rozmiar strony; 0 wyłącza paginację. */
|
|
11
11
|
pageSize?: number;
|
|
12
|
-
pageSizeOptions?: number[];
|
|
12
|
+
pageSizeOptions?: readonly number[];
|
|
13
13
|
selectable?: 'none' | 'single' | 'multiple';
|
|
14
14
|
loading?: boolean;
|
|
15
15
|
emptyMessage?: string;
|
|
@@ -51,12 +51,4 @@ export interface KptDataTableProps<T> extends Omit<ComponentPropsWithRef<'div'>,
|
|
|
51
51
|
/** Treść po lewej stronie paginatora (odpowiednik slotu `[kptPaginatorActions]`). */
|
|
52
52
|
paginatorActions?: ReactNode;
|
|
53
53
|
}
|
|
54
|
-
|
|
55
|
-
* Flagowa tabela danych: kolumny konfiguracją, sortowanie, filtr globalny i filtry kolumn,
|
|
56
|
-
* paginacja, zaznaczanie, kolumny zamrożone i przestawiane, edycja inline, wiersz podsumowania,
|
|
57
|
-
* rozwijane wiersze, wirtualizacja i eksport CSV.
|
|
58
|
-
*
|
|
59
|
-
* @example
|
|
60
|
-
* <KptDataTable columns={cols} data={rows} filterable pageSize={10} selectable="multiple" rowKey="id" />
|
|
61
|
-
*/
|
|
62
|
-
export declare function KptDataTable<T extends Record<string, unknown>>({ columns, data, filterable, filterRowPosition, pageSize, pageSizeOptions, selectable, loading, emptyMessage, rowKey, scrollHeight, virtualScroll, rowHeight, reorderable, exportable, exportFilename, lazy, totalRecords, expandable, multiExpand, summaryLabel, summaryValues, selection, defaultSelection, onSelectionChange, expandedRows, defaultExpandedRows, onExpandedRowsChange, hiddenFields, defaultHiddenFields, onHiddenFieldsChange, onRowClick, onLazyLoad, onCellEdit, cells, cellEditors, renderRowDetail, paginatorActions, locale, dictionary, className, style, ...rest }: Readonly<KptDataTableProps<T>>): import("react").JSX.Element;
|
|
54
|
+
export declare function KptDataTable<T = Record<string, unknown>>({ columns, data, filterable, filterRowPosition, pageSize, pageSizeOptions, selectable, loading, emptyMessage, rowKey, scrollHeight, virtualScroll, rowHeight, reorderable, exportable, exportFilename, lazy, totalRecords, expandable, multiExpand, summaryLabel, summaryValues, selection, defaultSelection, onSelectionChange, expandedRows, defaultExpandedRows, onExpandedRowsChange, hiddenFields, defaultHiddenFields, onHiddenFieldsChange, onRowClick, onLazyLoad, onCellEdit, cells, cellEditors, renderRowDetail, paginatorActions, locale, dictionary, className, style, ...rest }: Readonly<KptDataTableProps<T>>): import("react").JSX.Element;
|
|
@@ -38,6 +38,14 @@ const EMPTY_FIELDS = [];
|
|
|
38
38
|
* @example
|
|
39
39
|
* <KptDataTable columns={cols} data={rows} filterable pageSize={10} selectable="multiple" rowKey="id" />
|
|
40
40
|
*/
|
|
41
|
+
/**
|
|
42
|
+
* Odczyt pola wiersza po nazwie. Generyk `T` celowo nie ma ograniczenia `Record<string, unknown>`
|
|
43
|
+
* (port Angulara też go nie ma): wymuszałoby ono deklarowanie modelu wiersza jako `type`, bo
|
|
44
|
+
* `interface` nie dostaje domyślnej sygnatury indeksu. Rzutowanie zamknięte w jednym miejscu.
|
|
45
|
+
*/
|
|
46
|
+
function getValue(row, field) {
|
|
47
|
+
return row[field];
|
|
48
|
+
}
|
|
41
49
|
export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, filterRowPosition = 'bottom', pageSize = 0, pageSizeOptions = [5, 10, 20, 50], selectable = 'none', loading = false, emptyMessage, rowKey = null, scrollHeight = null, virtualScroll = false, rowHeight = 41, reorderable = false, exportable = false, exportFilename = 'export.csv', lazy = false, totalRecords = null, expandable = false, multiExpand = true, summaryLabel, summaryValues = null, selection, defaultSelection = EMPTY_ROWS, onSelectionChange, expandedRows, defaultExpandedRows = EMPTY_ROWS, onExpandedRowsChange, hiddenFields, defaultHiddenFields = EMPTY_FIELDS, onHiddenFieldsChange, onRowClick, onLazyLoad, onCellEdit, cells, cellEditors, renderRowDetail, paginatorActions, locale, dictionary, className, style, ...rest }) {
|
|
42
50
|
const { t } = useKptMessages({ locale, dictionary });
|
|
43
51
|
const [picked, setPicked] = useControllableState({
|
|
@@ -82,13 +90,13 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
82
90
|
const visibleColumns = useMemo(() => order.filter((col) => !hidden.includes(col.field)), [order, hidden]);
|
|
83
91
|
const hasColumnFilters = columns.some((col) => col.filter);
|
|
84
92
|
const hasRowDetail = expandable && !!renderRowDetail;
|
|
85
|
-
const sameRow = (a, b) => rowKey ? a
|
|
93
|
+
const sameRow = (a, b) => rowKey ? getValue(a, rowKey) === getValue(b, rowKey) : a === b;
|
|
86
94
|
// ── Potok danych ──
|
|
87
95
|
const filtered = useMemo(() => {
|
|
88
96
|
const query = filterText.trim().toLowerCase();
|
|
89
97
|
let rows = data;
|
|
90
98
|
if (query) {
|
|
91
|
-
rows = rows.filter((row) => columns.some((col) => toText(row
|
|
99
|
+
rows = rows.filter((row) => columns.some((col) => toText(getValue(row, col.field)).toLowerCase().includes(query)));
|
|
92
100
|
}
|
|
93
101
|
if (hasColumnFilters) {
|
|
94
102
|
rows = rows.filter((row) => columns.every((col) => {
|
|
@@ -97,7 +105,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
97
105
|
const value = columnFilters[col.field];
|
|
98
106
|
if (isEmptyFilterValue(value))
|
|
99
107
|
return true;
|
|
100
|
-
return matchesColumnFilter(col.filter, row
|
|
108
|
+
return matchesColumnFilter(col.filter, getValue(row, col.field), value);
|
|
101
109
|
}));
|
|
102
110
|
}
|
|
103
111
|
return rows;
|
|
@@ -107,8 +115,8 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
107
115
|
return filtered;
|
|
108
116
|
const sign = sortDir === 'asc' ? 1 : -1;
|
|
109
117
|
return [...filtered].sort((a, b) => {
|
|
110
|
-
const av = a
|
|
111
|
-
const bv = b
|
|
118
|
+
const av = getValue(a, sortField);
|
|
119
|
+
const bv = getValue(b, sortField);
|
|
112
120
|
// Puste komórki lądują na końcu niezależnie od kierunku — sortowanie ma pokazywać dane.
|
|
113
121
|
if (av == null)
|
|
114
122
|
return 1;
|
|
@@ -294,7 +302,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
294
302
|
if (!isEditable(col) || isEditingCell(row, col.field))
|
|
295
303
|
return;
|
|
296
304
|
setEditingCell({ row, field: col.field });
|
|
297
|
-
setEditingValue(row
|
|
305
|
+
setEditingValue(getValue(row, col.field));
|
|
298
306
|
// Edytory z panelem otwieramy od razu: ich zamknięcie jest jedynym momentem, w którym
|
|
299
307
|
// wiadomo, że użytkownik skończył wybierać.
|
|
300
308
|
setEditorPanelOpen(!!col.edit && OVERLAY_EDITORS.has(col.edit.type));
|
|
@@ -305,7 +313,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
305
313
|
const { row, field } = editingCell;
|
|
306
314
|
setEditingCell(null);
|
|
307
315
|
setEditorPanelOpen(false);
|
|
308
|
-
if (sameEditValue(value, row
|
|
316
|
+
if (sameEditValue(value, getValue(row, field)))
|
|
309
317
|
return;
|
|
310
318
|
onCellEdit?.({ row, field, value, updatedRow: { ...row, [field]: value } });
|
|
311
319
|
};
|
|
@@ -429,8 +437,8 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
429
437
|
}, onKeyDown: (event) => {
|
|
430
438
|
if (event.key === 'Enter' && editable)
|
|
431
439
|
startEdit(row, col, event);
|
|
432
|
-
}, children: editing ? (_jsx("div", { ref: editorHostRef, className: "kpt-data-table__editor", onClick: (event) => event.stopPropagation(), onBlur: onEditorFocusOut, children: editor(row, col) })) : cells?.[col.field] ? (cells[col.field](row, row
|
|
433
|
-
})] }), hasRowDetail && isExpanded(row) && !virtualScroll ? (_jsx("tr", { className: "kpt-data-table__detail-row", children: _jsx("td", { className: "kpt-data-table__detail", colSpan: colspan, children: renderRowDetail?.(row) }) })) : null] }, rowKey ? String(row
|
|
440
|
+
}, children: editing ? (_jsx("div", { ref: editorHostRef, className: "kpt-data-table__editor", onClick: (event) => event.stopPropagation(), onBlur: onEditorFocusOut, children: editor(row, col) })) : cells?.[col.field] ? (cells[col.field](row, getValue(row, col.field))) : getValue(row, col.field) }, col.field));
|
|
441
|
+
})] }), hasRowDetail && isExpanded(row) && !virtualScroll ? (_jsx("tr", { className: "kpt-data-table__detail-row", children: _jsx("td", { className: "kpt-data-table__detail", colSpan: colspan, children: renderRowDetail?.(row) }) })) : null] }, rowKey ? String(getValue(row, rowKey)) : index));
|
|
434
442
|
return (_jsxs("div", { className: cn('kpt-data-table', className), style: { ['--kpt-thead-offset']: `${theadOffset}px`, ...style }, ...rest, children: [filterable || exportable ? (_jsxs("div", { className: "kpt-data-table__toolbar", children: [filterable ? (_jsx("input", { className: "kpt-data-table__filter", type: "search", placeholder: t('dataTable.search'), value: filterText, onChange: (event) => {
|
|
435
443
|
setFilterText(event.target.value);
|
|
436
444
|
setPageIndex(0);
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# KptDataTable (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
The flagship data table: columns from configuration, sorting, a global filter and per-column
|
|
4
|
+
filters, pagination, selection, frozen and reorderable columns, inline editing, a summary row,
|
|
5
|
+
expandable rows, virtual scrolling and CSV export.
|
|
6
|
+
Import: `import { KptDataTable, type KptColumn } from '@konce-pt/react';`
|
|
7
|
+
|
|
8
|
+
## The column model (KptColumn)
|
|
9
|
+
`{ field, header, sortable?, align?, width?, frozen?, filter?, edit?, summary? }`
|
|
10
|
+
`frozen` columns (leading, adjacent) stick during horizontal scrolling — they require `width`.
|
|
11
|
+
|
|
12
|
+
- `filter`: `KptColumnFilter` from `@konce-pt/table` — `text`, `number`, `select` (with `multiple`),
|
|
13
|
+
`numberRange`, `slider`, `dateRange`. Any column with a `filter` renders the filter row.
|
|
14
|
+
- `edit`: `{ type: 'text' | 'number' | 'date' | 'select' | 'multiselect', options?, placeholder?, min?, max?, step? }`
|
|
15
|
+
- `summary`: `{ fn: 'sum' | 'avg' | 'min' | 'max' | 'count' | ((rows) => unknown), format? }`
|
|
16
|
+
|
|
17
|
+
## Props
|
|
18
|
+
- `columns` (required), `data`
|
|
19
|
+
- `filterable`, `filterRowPosition` (`'bottom'` default), `pageSize` (0 = no pagination),
|
|
20
|
+
`pageSizeOptions`, `selectable` (`'none' | 'single' | 'multiple'`), `loading`, `emptyMessage`,
|
|
21
|
+
`rowKey`, `scrollHeight`, `virtualScroll`, `rowHeight` (41), `reorderable`, `exportable`,
|
|
22
|
+
`exportFilename`, `lazy`, `totalRecords`, `expandable`, `multiExpand`, `summaryLabel`,
|
|
23
|
+
`summaryValues`
|
|
24
|
+
- Controllable state: `selection`, `expandedRows`, `hiddenFields` (each with `default*` and
|
|
25
|
+
`on*Change`)
|
|
26
|
+
- Events: `onRowClick`, `onLazyLoad`, `onCellEdit`
|
|
27
|
+
|
|
28
|
+
## Templates are render props
|
|
29
|
+
- `cells`: `Record<field, (row, value) => ReactNode>` — the equivalent of `kptCell`
|
|
30
|
+
- `cellEditors`: `Record<field, (context) => ReactNode>` — the equivalent of `kptCellEdit`;
|
|
31
|
+
the context carries `value`, `row`, `commit(value)`, `cancel()`
|
|
32
|
+
- `renderRowDetail(row)` — the expanded row's content
|
|
33
|
+
- `paginatorActions` — the slot to the left of the paginator (a column-toggle button, say)
|
|
34
|
+
|
|
35
|
+
## The logic lives in a package
|
|
36
|
+
Filter matching, aggregations, CSV escaping and cell-to-text come from `@konce-pt/table` — pure
|
|
37
|
+
functions with `node --test` coverage, shared with the Angular port. What stays here is the state
|
|
38
|
+
and the markup.
|
|
39
|
+
|
|
40
|
+
## Export as a function, not a method
|
|
41
|
+
Angular exposes `exportCsv()` on the component instance; React has no equivalent, so the same job is
|
|
42
|
+
`exportTableCsv(columns, rows, filename)`, exported from the package. The `exportable` toolbar
|
|
43
|
+
button calls exactly that, over the currently visible columns and the filtered, sorted rows.
|
|
44
|
+
|
|
45
|
+
## Inline editing never commits on blur of the control
|
|
46
|
+
Controls with a panel (`date`, `select`, `multiselect`) open immediately and commit when the panel
|
|
47
|
+
closes; the rest commit when focus leaves **the whole editor**. `KptInputNumber` has focusable
|
|
48
|
+
`+`/`-` buttons beside the field, so reacting to the control's own blur would close the editor on
|
|
49
|
+
every arrow click. `cellEdit` fires only when the value actually changed (arrays are compared by
|
|
50
|
+
content).
|
|
51
|
+
|
|
52
|
+
## The summary covers the filtered set, not the page
|
|
53
|
+
Aggregations run over every row after filtering — page 1 and all the rest — so paging does not move
|
|
54
|
+
them while the search box does. With `lazy` the table only holds one page, so it computes nothing:
|
|
55
|
+
`summaryValues` is then the only source.
|
|
56
|
+
|
|
57
|
+
## Examples
|
|
58
|
+
<KptDataTable columns={cols} data={rows} filterable pageSize={10} selectable="multiple" rowKey="id" />
|
|
59
|
+
|
|
60
|
+
<KptDataTable
|
|
61
|
+
columns={cols}
|
|
62
|
+
data={rows}
|
|
63
|
+
rowKey="id"
|
|
64
|
+
cells={{ status: (row, value) => <span className="badge">{String(value)}</span> }}
|
|
65
|
+
onCellEdit={(event) => save(event.updatedRow)}
|
|
66
|
+
/>
|
|
67
|
+
|
|
68
|
+
## i18n
|
|
69
|
+
The toolbar, the states and the filter labels come from the `dataTable` namespace (`search`,
|
|
70
|
+
`exportCsv`, `loading`, `empty`, `total`, `selectAll`, `selectRow`, `expandRow`, `editCell`,
|
|
71
|
+
`filter` / `filterMin` / `filterMax` with `{column}`, `all`, `min`, `max`, `dateRange`).
|
|
72
|
+
|
|
73
|
+
## Tokens
|
|
74
|
+
`--kpt-table-{bg,fg,border,radius,header-bg,header-fg,row-hover,row-selected,cell-padding}`,
|
|
75
|
+
the expanded row `--kpt-table-detail-bg`, the summary row `--kpt-table-summary-bg`.
|
|
76
|
+
|
|
77
|
+
## Not ported yet
|
|
78
|
+
Grouped columns (absent in the Angular port too). A ready column-toggle UI is not part of either
|
|
79
|
+
port — `hiddenFields` is the hook to build one on.
|
|
@@ -2,7 +2,7 @@ import { type ComponentPropsWithRef, type ReactNode } from 'react';
|
|
|
2
2
|
import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
|
|
3
3
|
export type KptDataViewLayout = 'list' | 'grid';
|
|
4
4
|
export interface KptDataViewProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'children'>, KptI18nOverrides {
|
|
5
|
-
items?: T[];
|
|
5
|
+
items?: readonly T[];
|
|
6
6
|
layout?: KptDataViewLayout;
|
|
7
7
|
defaultLayout?: KptDataViewLayout;
|
|
8
8
|
onLayoutChange?: (layout: KptDataViewLayout) => void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# KptDataView (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
A list of items in a list or grid layout, with optional pagination.
|
|
4
|
+
Import: `import { KptDataView } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<div class="kpt-data-view-host">
|
|
8
|
+
<div class="kpt-data-view">
|
|
9
|
+
<div class="kpt-data-view__toolbar">
|
|
10
|
+
<div class="kpt-data-view__count">8 items</div>
|
|
11
|
+
<div class="kpt-data-view__switch">…list, grid…</div>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="kpt-data-view__content" data-layout="grid">
|
|
14
|
+
<div class="kpt-data-view__item">…</div>
|
|
15
|
+
</div>
|
|
16
|
+
<!-- KptPaginator -->
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
- `items`: `T[]`
|
|
22
|
+
- `layout` / `defaultLayout` / `onLayoutChange` — `'list' | 'grid'` (default `'grid'`)
|
|
23
|
+
- `pageSize`: number — 0 turns pagination off
|
|
24
|
+
- `paginated`: boolean (default true)
|
|
25
|
+
- `renderItem(item, index)` — required; the equivalent of `<ng-template let-item>`
|
|
26
|
+
- `className`, `ref` and every other `<div>` prop go to the host
|
|
27
|
+
|
|
28
|
+
## The layout is an attribute
|
|
29
|
+
`data-layout` on the content switches the grid rules; the toolbar only flips it. Everything else —
|
|
30
|
+
the item's markup — is the caller's, which is why there are no card props here.
|
|
31
|
+
|
|
32
|
+
## The pager appears when it is needed
|
|
33
|
+
Only when `paginated`, `pageSize > 0` and there are more items than fit on a page — a paginator
|
|
34
|
+
under a single page of results is noise.
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
<KptDataView items={products} pageSize={6} renderItem={(p) => <div>{p.name}</div>} />
|
|
38
|
+
<KptDataView items={products} layout="list" paginated={false} renderItem={renderCard} />
|
|
39
|
+
|
|
40
|
+
## i18n
|
|
41
|
+
The count and the two switch buttons come from the `dataView` namespace (`count` with `{count}`,
|
|
42
|
+
`list`, `grid`, `empty`).
|
|
43
|
+
|
|
44
|
+
## Tokens
|
|
45
|
+
The grid gap `--kpt-space-4`, the active switch `--kpt-color-primary`.
|
|
46
|
+
|
|
47
|
+
## Accessibility
|
|
48
|
+
Both layout buttons carry labels from the dictionary; an empty list renders a message instead of
|
|
49
|
+
a bare empty area.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# KptDateRange (@konce-pt/react)
|
|
2
|
+
|
|
3
|
+
Picking a date (and time) range with a calendar.
|
|
4
|
+
Import: `import { KptDateRange, type KptDateRangeValue } from '@konce-pt/react';`
|
|
5
|
+
|
|
6
|
+
## DOM
|
|
7
|
+
<div class="kpt-date-range-host">
|
|
8
|
+
<div class="kpt-date-range" role="button" tabindex="0">
|
|
9
|
+
<span class="kpt-date-range__value">08.09.2026 – 14.09.2026</span>
|
|
10
|
+
…clear, icon…
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
<!-- in a portal: <div class="kpt-calendar kpt-calendar--range kpt-calendar--multi">…</div> -->
|
|
14
|
+
|
|
15
|
+
## Props
|
|
16
|
+
- `value` / `defaultValue` / `onValueChange` — `{ start, end }`: dates 'YYYY-MM-DD',
|
|
17
|
+
datetime 'YYYY-MM-DDTHH:mm', times 'HH:mm'
|
|
18
|
+
- `mode`: `'date'` (default) | `'datetime'` | `'time'`
|
|
19
|
+
- `visibleMonths`: number (default 1, clamped to 1–3) — months side by side; ignored in 'time' mode
|
|
20
|
+
- `hourFormat`: 12 | 24 (default 24); `minuteStep`: number (default 1)
|
|
21
|
+
- `placeholder` — without one it falls back to `dateRange.placeholder`
|
|
22
|
+
- `displayFormat`: `'pl'` (default) | `'iso'`
|
|
23
|
+
- `disabled`, `invalid`, `touched`, `onTouch()`
|
|
24
|
+
- `className`, `ref` and every other `<div>` prop go to the host
|
|
25
|
+
|
|
26
|
+
## Two clicks, one band
|
|
27
|
+
The first click sets `start`, the second `end`, a third starts over; clicking before the start
|
|
28
|
+
normalizes the order rather than producing a reversed range (`pickRangeDay` in `@konce-pt/datetime`).
|
|
29
|
+
The panel stays open until the range closes — the second click needs somewhere to land.
|
|
30
|
+
|
|
31
|
+
With several panels the adjacent-month days become blank slots: otherwise the same date would be
|
|
32
|
+
clickable twice, and the band would paint in both places.
|
|
33
|
+
|
|
34
|
+
## The clock remounts when the end switches
|
|
35
|
+
`key={timeEnd}` on `KptClock`, so the dial returns to the hours stage after the From/To toggle —
|
|
36
|
+
in Angular that came for free from two separate template branches.
|
|
37
|
+
|
|
38
|
+
## Examples
|
|
39
|
+
<KptDateRange value={period} onValueChange={setPeriod} />
|
|
40
|
+
<KptDateRange visibleMonths={2} value={period} onValueChange={setPeriod} />
|
|
41
|
+
<KptDateRange mode="datetime" hourFormat={12} value={span} onValueChange={setSpan} />
|
|
42
|
+
<KptDateRange mode="time" value={hours} onValueChange={setHours} />
|
|
43
|
+
|
|
44
|
+
## i18n
|
|
45
|
+
Day and month names come from `Intl`; "From/To", the navigation, "Done" and the placeholder come
|
|
46
|
+
from the `dateRange`, `datepicker` and `common` namespaces.
|
|
47
|
+
|
|
48
|
+
## Tokens
|
|
49
|
+
The field `--kpt-form-field-*`; the calendar `--kpt-color-surface-raised`, the range
|
|
50
|
+
`--kpt-color-primary`.
|
|
51
|
+
|
|
52
|
+
## Accessibility
|
|
53
|
+
The trigger is `role="button"` with Enter support; the days are real buttons, and the time ends are
|
|
54
|
+
switched with a two-button toggle rather than a custom control.
|