@konce-pt/react 0.8.1 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +381 -27
  2. package/dist/accordion/llms.txt +56 -0
  3. package/dist/alert/llms.txt +45 -0
  4. package/dist/app-shell/app-shell.d.ts +20 -0
  5. package/dist/app-shell/app-shell.js +24 -0
  6. package/dist/app-shell/llms.txt +59 -0
  7. package/dist/auth/auth.d.ts +1 -1
  8. package/dist/auth/llms.txt +65 -0
  9. package/dist/autocomplete/autocomplete.d.ts +1 -1
  10. package/dist/autocomplete/llms.txt +44 -0
  11. package/dist/avatar/llms.txt +42 -0
  12. package/dist/avatar-group/avatar-group.d.ts +1 -1
  13. package/dist/avatar-group/llms.txt +38 -0
  14. package/dist/badge/llms.txt +38 -0
  15. package/dist/bottom-sheet/llms.txt +43 -0
  16. package/dist/breadcrumb/breadcrumb.d.ts +1 -1
  17. package/dist/breadcrumb/llms.txt +35 -0
  18. package/dist/button/llms.txt +56 -0
  19. package/dist/button-group/button-group.d.ts +1 -1
  20. package/dist/button-group/llms.txt +40 -0
  21. package/dist/card/llms.txt +60 -0
  22. package/dist/carousel/carousel.d.ts +1 -1
  23. package/dist/carousel/llms.txt +47 -0
  24. package/dist/charts/chart-legend.d.ts +1 -1
  25. package/dist/charts/llms.txt +57 -0
  26. package/dist/checkbox/llms.txt +39 -0
  27. package/dist/chip/llms.txt +41 -0
  28. package/dist/chips-input/llms.txt +40 -0
  29. package/dist/clock/llms.txt +50 -0
  30. package/dist/color-picker/color-picker.d.ts +1 -1
  31. package/dist/color-picker/llms.txt +40 -0
  32. package/dist/confirm/llms.txt +44 -0
  33. package/dist/context-menu/context-menu.d.ts +1 -1
  34. package/dist/context-menu/llms.txt +39 -0
  35. package/dist/data-table/data-table-types.d.ts +1 -1
  36. package/dist/data-table/data-table.d.ts +2 -10
  37. package/dist/data-table/data-table.js +17 -9
  38. package/dist/data-table/llms.txt +79 -0
  39. package/dist/data-view/data-view.d.ts +1 -1
  40. package/dist/data-view/llms.txt +49 -0
  41. package/dist/date-range/llms.txt +54 -0
  42. package/dist/datepicker/llms.txt +74 -0
  43. package/dist/dialog/llms.txt +51 -0
  44. package/dist/divider/llms.txt +28 -0
  45. package/dist/drawer/llms.txt +47 -0
  46. package/dist/empty/llms.txt +35 -0
  47. package/dist/fab/llms.txt +37 -0
  48. package/dist/fieldset/llms.txt +43 -0
  49. package/dist/file-upload/llms.txt +48 -0
  50. package/dist/form-field/llms.txt +55 -0
  51. package/dist/galleria/galleria.d.ts +1 -1
  52. package/dist/galleria/llms.txt +46 -0
  53. package/dist/grid/llms.txt +110 -0
  54. package/dist/icon/icon-registry.d.ts +5 -2
  55. package/dist/icon/icon-registry.js +5 -3
  56. package/dist/icon/llms.txt +66 -0
  57. package/dist/icon-button/llms.txt +38 -0
  58. package/dist/icons-entry/public-api.d.ts +5 -1
  59. package/dist/icons-entry/public-api.js +7 -1
  60. package/dist/image/llms.txt +46 -0
  61. package/dist/input/llms.txt +54 -0
  62. package/dist/input-mask/llms.txt +37 -0
  63. package/dist/input-number/llms.txt +44 -0
  64. package/dist/input-otp/llms.txt +43 -0
  65. package/dist/internal/use-breakpoint.js +18 -10
  66. package/dist/knob/llms.txt +46 -0
  67. package/dist/listbox/listbox.d.ts +1 -1
  68. package/dist/listbox/llms.txt +39 -0
  69. package/dist/map/llms.txt +108 -0
  70. package/dist/megamenu/llms.txt +48 -0
  71. package/dist/megamenu/megamenu.d.ts +1 -1
  72. package/dist/menu/llms.txt +56 -0
  73. package/dist/menu/menu-item.d.ts +1 -1
  74. package/dist/menu/menu-rows.d.ts +1 -1
  75. package/dist/menu/menu.d.ts +1 -1
  76. package/dist/menu/menu.js +12 -1
  77. package/dist/menubar/llms.txt +45 -0
  78. package/dist/menubar/menubar.d.ts +1 -1
  79. package/dist/meter-group/llms.txt +47 -0
  80. package/dist/meter-group/meter-group.d.ts +1 -1
  81. package/dist/order-list/llms.txt +41 -0
  82. package/dist/paginator/llms.txt +48 -0
  83. package/dist/paginator/paginator.d.ts +1 -1
  84. package/dist/paginator/paginator.js +7 -1
  85. package/dist/panel/llms.txt +55 -0
  86. package/dist/password/llms.txt +45 -0
  87. package/dist/pick-list/llms.txt +52 -0
  88. package/dist/popover/llms.txt +42 -0
  89. package/dist/progress/llms.txt +47 -0
  90. package/dist/public-api.d.ts +2 -1
  91. package/dist/public-api.js +1 -1
  92. package/dist/radio-group/llms.txt +40 -0
  93. package/dist/radio-group/radio-group.d.ts +1 -1
  94. package/dist/rating/llms.txt +43 -0
  95. package/dist/rich-text/llms.txt +61 -0
  96. package/dist/rich-text/rich-text.d.ts +1 -1
  97. package/dist/roadmap/llms.txt +82 -0
  98. package/dist/scroll-top/llms.txt +41 -0
  99. package/dist/select/llms.txt +56 -0
  100. package/dist/select/select.d.ts +1 -1
  101. package/dist/sidenav/llms.txt +53 -0
  102. package/dist/skeleton/llms.txt +33 -0
  103. package/dist/slider/llms.txt +50 -0
  104. package/dist/speed-dial/llms.txt +46 -0
  105. package/dist/speed-dial/speed-dial.d.ts +1 -1
  106. package/dist/spinner/llms.txt +33 -0
  107. package/dist/split-button/llms.txt +54 -0
  108. package/dist/split-button/split-button.d.ts +1 -1
  109. package/dist/splitter/llms.txt +44 -0
  110. package/dist/stepper/llms.txt +64 -0
  111. package/dist/switch/llms.txt +42 -0
  112. package/dist/switch-group/llms.txt +43 -0
  113. package/dist/switch-group/switch-group.d.ts +1 -1
  114. package/dist/tabs/llms.txt +48 -0
  115. package/dist/textarea/llms.txt +40 -0
  116. package/dist/timeline/llms.txt +53 -0
  117. package/dist/timeline/timeline.d.ts +1 -1
  118. package/dist/toast/llms.txt +52 -0
  119. package/dist/toolbar/llms.txt +45 -0
  120. package/dist/tooltip/llms.txt +42 -0
  121. package/dist/tree/llms.txt +45 -0
  122. package/dist/tree/tree.d.ts +2 -2
  123. package/package.json +11 -11
@@ -0,0 +1,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[rowKey] === b[rowKey] : a === b;
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[col.field]).toLowerCase().includes(query)));
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[col.field], value);
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[sortField];
111
- const bv = b[sortField];
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[col.field]);
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[field]))
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[col.field])) : row[col.field] }, col.field));
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[rowKey]) : index));
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.