@konce-pt/react 0.8.0 → 0.8.2

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 +374 -26
  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
package/README.md CHANGED
@@ -1,27 +1,230 @@
1
- <p align="center">
2
- <img src="https://ui.konce.pt/images/koncept_ui_lib.png" alt="Koncept UI — component library" width="100%" />
3
- </p>
1
+ <div align="center">
4
2
 
5
- # @konce-pt/react
3
+ <img src="https://ui.konce.pt/images/koncept_ui_lib.png" alt="Koncept UI — React component library" width="100%" />
6
4
 
7
- Komponenty React 19 systemu projektowego [Koncept UI](https://gitlab.com/konce-pt/koncept-ui).
8
- Ta sama warstwa CSS co port Angulara — te same klasy `kpt-*`, te same tokeny `--kpt-*` —
9
- więc oba frameworki wyglądają identycznie. **Zero zależności runtime.**
5
+ # Koncept UI — React
6
+
7
+ **Open-source React 19 component library on the Koncept UI design system.**
8
+ 80+ components sharing the exact CSS layer with the Angular port — zero runtime dependencies.
9
+
10
+ [![npm version](https://img.shields.io/npm/v/@konce-pt/react?color=%233b82f6&label=npm)](https://www.npmjs.com/package/@konce-pt/react)
11
+ [![npm downloads](https://img.shields.io/npm/dm/@konce-pt/react?color=%233b82f6)](https://www.npmjs.com/package/@konce-pt/react)
12
+ [![minzipped size](https://img.shields.io/bundlephobia/minzip/@konce-pt/react)](https://bundlephobia.com/package/@konce-pt/react)
13
+ [![types](https://img.shields.io/npm/types/@konce-pt/react)](https://www.npmjs.com/package/@konce-pt/react)
14
+ [![license](https://img.shields.io/npm/l/@konce-pt/react?color=%2310b981)](https://gitlab.com/konce-pt/koncept-ui/-/blob/main/LICENSE)
15
+
16
+ ![React](https://img.shields.io/badge/React-19-61dafb?logo=react&logoColor=white)
17
+ ![dependencies](https://img.shields.io/badge/runtime%20deps-0-10b981)
18
+ ![CSS](https://img.shields.io/badge/CSS-shared%20with%20Angular-3b82f6)
19
+ ![Components](https://img.shields.io/badge/components-80%2B-8b5cf6)
20
+
21
+ </div>
22
+
23
+ ---
24
+
25
+ ## Why Koncept UI React
26
+
27
+ **Koncept UI** is a free, MIT-licensed design system. This package is its **React 19** port — not a
28
+ lookalike, but the same rendering contract:
29
+
30
+ - 🎯 **One CSS layer for both ports** — the same `kpt-*` classes, the same `--kpt-*` tokens, the same
31
+ `@layer kpt.*` ordering. The DOM tree matches the Angular port node for node, so an app can mix
32
+ the two and nothing shifts by a pixel.
33
+ - 📦 **Zero runtime dependencies** — no `clsx`, no `radix`, no CSS-in-JS. `cn`, `Slot` and
34
+ `composeRefs` ship with the package.
35
+ - ⚛️ **React 19 native** — `ref` is a plain prop (no `forwardRef`), and every form control renders a
36
+ native `name`/`form`, so `<form action={…}>` Actions and React Hook Form's `register()` work
37
+ **without an adapter**. Controls are usable controlled *and* uncontrolled.
38
+ - 📊 **Flagship data table** — sorting, global and per-column filters, pagination, virtual scroll
39
+ (5000+ rows), selection, column reorder & freeze, inline editing, summary row, CSV export.
40
+ - 🎨 **100% tokenized** — every style is a `var(--kpt-*)` custom property. Light/dark theme, neutral
41
+ OKLCH palette. Rebrand by swapping one token layer.
42
+ - ♿ **Native elements first** — `<button>`, `<input>`, `<label>` wherever one exists, so keyboard and
43
+ screen-reader behaviour comes from the platform. Invalid state travels on `aria-invalid`, never on
44
+ a class.
45
+ - 🌍 **i18n built in** — English (default) & Polish out of the box, runtime locale switch, add any
46
+ language with one JSON.
47
+
48
+ ## Installation
10
49
 
11
50
  ```bash
12
- npm i @konce-pt/react @konce-pt/styles @konce-pt/tokens
51
+ npm i @konce-pt/react @konce-pt/tokens @konce-pt/styles
52
+ # or: pnpm add / yarn add
13
53
  ```
14
54
 
15
- CSS ładujesz raz, w wejściu aplikacji (pakiet wysyła wyłącznie JavaScript):
55
+ `react` and `react-dom` (`>=19`) are peers you already have. Optional extras:
56
+
57
+ - [`@konce-pt/grid`](https://www.npmjs.com/package/@konce-pt/grid) — the mobile-first layout system;
58
+ its React components live in `@konce-pt/react/grid`.
59
+ - [`@konce-pt/validators`](https://www.npmjs.com/package/@konce-pt/validators) — Polish-market
60
+ validators (NIP, REGON, PESEL, IBAN, postal code).
61
+ - `leaflet` (`^1.9`) — an **optional** peer, needed only by `@konce-pt/react/map`, loaded through a
62
+ dynamic `import()`.
63
+
64
+ ## Setup
65
+
66
+ The package ships **JavaScript only**. Load the shared CSS layer once, in your app entry:
16
67
 
17
68
  ```ts
18
69
  import '@konce-pt/tokens/css';
19
70
  import '@konce-pt/tokens/css/dark';
20
- import '@konce-pt/styles'; // reset, warstwy, motyw
21
- import '@konce-pt/styles/components'; // warstwa komponentów — wymagane
22
- import '@konce-pt/grid'; // opcjonalnie — klasy układu
71
+ import '@konce-pt/styles'; // reset, @layer ordering, theme
72
+ import '@konce-pt/styles/components'; // the component layer — REQUIRED, this is what styles kpt-*
73
+ import '@konce-pt/grid'; // optional — only if you use the layout classes
74
+ ```
75
+
76
+ `@konce-pt/styles/components` is generated from the same SCSS the Angular port compiles into its own
77
+ bundle, which is why both ports render identically. The Angular package injects those styles at
78
+ runtime; React has no other source for them, so this import is not optional.
79
+
80
+ Dark theme: set `<html data-theme="dark">` (or rely on `prefers-color-scheme`).
81
+
82
+ > **No CDK here.** Components with a floating panel (`KptSelect`, `KptPopover`, `KptMenu`,
83
+ > `KptAutocomplete`, `KptDatepicker`…) portal into `document.body` through the internal
84
+ > `KptOverlay`, so `@angular/cdk/overlay-prebuilt.css` is **not** needed — positioning lives in the
85
+ > component, the look in `@konce-pt/styles/components`. Inside an open `KptDialog` those panels
86
+ > portal into the `<dialog>` instead, because `showModal()` moves it to the browser top layer.
87
+
88
+ ## Quick start
89
+
90
+ A login form — controlled values, the error gated on touch, no form library required:
91
+
92
+ ```tsx
93
+ import { useState } from 'react';
94
+ import { KptButton, KptFormField, KptInput } from '@konce-pt/react';
95
+
96
+ export function Login() {
97
+ const [email, setEmail] = useState('');
98
+ const [password, setPassword] = useState('');
99
+ const [touched, setTouched] = useState<Record<string, boolean>>({});
100
+
101
+ const emailError = !email.includes('@') ? 'Enter a valid e-mail address' : null;
102
+ const passwordError = password.length < 8 ? 'At least 8 characters' : null;
103
+ const invalid = Boolean(emailError || passwordError);
104
+
105
+ return (
106
+ <form
107
+ onSubmit={(e) => {
108
+ e.preventDefault();
109
+ console.log({ email, password });
110
+ }}
111
+ >
112
+ <KptFormField label="E-mail" error={touched.email ? emailError : null} required>
113
+ <KptInput
114
+ type="email"
115
+ name="email"
116
+ value={email}
117
+ onValueChange={setEmail}
118
+ onTouch={() => setTouched((t) => ({ ...t, email: true }))}
119
+ invalid={Boolean(emailError)}
120
+ />
121
+ </KptFormField>
122
+
123
+ <KptFormField label="Password" error={touched.password ? passwordError : null} required>
124
+ <KptInput
125
+ type="password"
126
+ name="password"
127
+ value={password}
128
+ onValueChange={setPassword}
129
+ onTouch={() => setTouched((t) => ({ ...t, password: true }))}
130
+ invalid={Boolean(passwordError)}
131
+ />
132
+ </KptFormField>
133
+
134
+ <KptButton type="submit" disabled={invalid}>Sign in</KptButton>
135
+ </form>
136
+ );
137
+ }
23
138
  ```
24
139
 
140
+ `KptFormField` generates the control id and publishes it through context, so `label[for]` and
141
+ `input[id]` match with no configuration. Drop `value`/`onValueChange` and the field becomes
142
+ uncontrolled — the value lives in the DOM, and a native `<form action={…}>` or React Hook Form's
143
+ `register()` picks it up as-is.
144
+
145
+ Data table with sorting, filtering, pagination and a custom cell:
146
+
147
+ ```tsx
148
+ <KptDataTable
149
+ columns={columns}
150
+ data={users}
151
+ filterable
152
+ exportable
153
+ pageSize={10}
154
+ selectable="multiple"
155
+ rowKey="id"
156
+ selection={selected}
157
+ onSelectionChange={setSelected}
158
+ cells={{ status: (row, value) => <KptBadge value={String(value)} /> }}
159
+ />
160
+ ```
161
+
162
+ ## Components (80+)
163
+
164
+ | Category | Components |
165
+ | --- | --- |
166
+ | **Forms** | `KptInput`, `KptTextarea`, `KptSelect`, `KptAutocomplete`, `KptCheckbox`, `KptSwitch`, `KptSwitchGroup`, `KptRadioGroup`, `KptSlider`, `KptRating`, `KptDatepicker`, `KptDateRange`, `KptClock`, `KptColorPicker`, `KptFileUpload`, `KptInputNumber`, `KptPassword`, `KptInputOtp`, `KptChipsInput`, `KptInputMask`, `KptListbox`, `KptKnob`, `KptRichText`, `KptFormField` |
167
+ | **Buttons & actions** | `KptButton`, `KptIconButton`, `KptButtonGroup`, `KptFab`, `KptSplitButton`, `KptSpeedDial` |
168
+ | **Layout** | `KptAppShell`, `KptToolbar`, `KptSidenav`, `KptCard` (+ `Header`, `Footer`, `Media`, `Title`…), `KptDivider`, `KptPanel`, `KptFieldset`, `KptSplitter`, `KptScrollTop` |
169
+ | **Navigation** | `KptTabs`, `KptAccordion`, `KptBreadcrumb`, `KptStepper`, `KptMenu`, `KptMenubar`, `KptMegamenu`, `KptContextMenu` |
170
+ | **Data** | `KptDataTable`, `KptPaginator`, `KptTree`, `KptTimeline`, `KptCarousel`, `KptDataView`, `KptPickList`, `KptOrderList`, `KptGalleria`, `KptMeterGroup` |
171
+ | **Feedback & overlay** | `KptAlert`, `KptDialog`, `kptToast` + `KptToastContainer`, `KptTooltip`, `KptPopover`, `KptDrawer`, `KptBottomSheet`, `kptConfirm`, `KptBadge`, `KptChip`, `KptAvatar`, `KptAvatarGroup`, `KptSpinner`, `KptProgress`, `KptSkeleton`, `KptEmpty`, `KptImage`, `KptAuth` |
172
+ | **Subpaths** | `@konce-pt/react/charts` (`KptChart`), `/roadmap` (`KptRoadmap`), `/grid` (`KptGrid`, `KptCol`, `KptFlex`), `/map` (`KptMap` + legend, POI card, search), `/icons` (full Tabler set) |
173
+
174
+ The heavy pieces (charts, roadmap, maps, the full icon set) live in subpaths on purpose, so the core
175
+ entry point stays small. Every component ships an `llms.txt` API sheet next to its source.
176
+
177
+ ## Theming
178
+
179
+ All visuals are driven by `var(--kpt-*)` tokens (three tiers: primitives → semantic → component).
180
+ Override them in a stylesheet imported **after** `@konce-pt/styles`:
181
+
182
+ ```css
183
+ :root {
184
+ --kpt-color-primary: oklch(0.55 0.2 265); /* rebrand in one line */
185
+ --kpt-color-primary-hover: oklch(0.5 0.2 265);
186
+ }
187
+ :root[data-theme='dark'] {
188
+ --kpt-color-primary: oklch(0.7 0.16 265);
189
+ }
190
+ ```
191
+
192
+ **Common token names:** surfaces `--kpt-color-surface`, `--kpt-color-surface-sunken|raised|variant|hover|selected`; text `--kpt-color-on-surface`, `--kpt-color-on-surface-muted` (aliases `--kpt-color-text`, `--kpt-color-text-muted|subtle|inverse`); borders `--kpt-color-border`, `--kpt-color-border-strong`; accent roles in full — `{primary,danger,success,warning,info}` each with `-hover`, `-contrast`, `-subtle`, `-border`; radii `--kpt-radius-sm|md|lg|xl|full|none`; elevation `--kpt-elevation-1..4` (aliases `--kpt-shadow-sm|md|lg`). There is no `--kpt-color-bg`.
193
+
194
+ Variants and sizes travel on `data-*` attributes (`data-variant`, `data-size`), never on modifier
195
+ classes — that is what the SCSS reads.
196
+
197
+ ## Icons
198
+
199
+ **84 icons are built in and need no configuration** — what the components themselves draw plus the
200
+ staples of an application shell (`sun`, `moon`, `bell`, `settings`, `users`, `logout`,
201
+ `layout-dashboard`, `home`, `activity`, `chart-bar`, `brand-github` …). A typical admin panel needs
202
+ nothing else.
203
+
204
+ For the full **Tabler Icons** set (MIT, 5130 icons) there are two paths, and the difference is the
205
+ bundle. Every icon is its own export, so importing by name lets the bundler keep just those:
206
+
207
+ ```tsx
208
+ // Production — two icons reach the bundle.
209
+ import { LayoutDashboard, Rocket } from '@konce-pt/react/icons';
210
+ import { registerKptIcons } from '@konce-pt/react';
211
+ registerKptIcons([LayoutDashboard, Rocket]);
212
+
213
+ // Prototype — every icon, about 1.2 MB.
214
+ import { registerKptTablerIcons } from '@konce-pt/react/icons';
215
+ registerKptTablerIcons();
216
+ ```
217
+
218
+ `registerKptIcons` is the one that shrinks: it takes the data as an argument and imports nothing from
219
+ the Tabler subpath. For the same reason `registerKptIcons(tabler)` with a namespace import
220
+ (`import * as tabler`) registers everything — a namespace object handed to a function blocks static
221
+ analysis. The registry is global, so one call at the app entry is enough; no provider needed.
222
+
223
+ ## Internationalization (i18n)
224
+
225
+ Component labels ship in **English (default)** and **Polish**. Without a provider everything renders
226
+ in English — a missing provider is never an error. `KptI18nProvider` sets the language for a subtree:
227
+
25
228
  ```tsx
26
229
  import { KptI18nProvider, useKptI18n } from '@konce-pt/react';
27
230
 
@@ -34,25 +237,170 @@ export function App() {
34
237
  }
35
238
 
36
239
  function Toolbar() {
37
- const { t } = useKptI18n();
38
- return <span>{t('paginator.range', { start: 1, end: 10, total: 42 })}</span>;
240
+ const { t, setLocale } = useKptI18n();
241
+ return (
242
+ <button onClick={() => setLocale('en')}>
243
+ {t('paginator.range', { start: 1, end: 10, total: 42 })}
244
+ </button>
245
+ );
39
246
  }
40
247
  ```
41
248
 
42
- ## Zasady portu
249
+ `useKptI18n()` returns `{ locale, setLocale, messages, messagesFor, t }`. `t(key, params?)` takes a
250
+ dotted key with `{name}` interpolation and falls back to English, then to the key itself.
251
+
252
+ **Add any language with one JSON** (shape = the `KptMessages` contract; missing keys fall back to
253
+ English):
254
+
255
+ ```tsx
256
+ import de from './i18n/de.json';
257
+
258
+ <KptI18nProvider locale="de" messages={{ de }}>…</KptI18nProvider>
259
+ ```
260
+
261
+ Any component with labels also accepts `locale` and `dictionary` props, which take priority over the
262
+ provider. Calendar weekday/month names come from the browser's `Intl` API. The contract and the
263
+ dictionaries live in [`@konce-pt/i18n`](https://www.npmjs.com/package/@konce-pt/i18n), shared with
264
+ the Angular port.
265
+
266
+ ## Helpers
267
+
268
+ - `cn(...values)` — class-name joining (the `clsx` role, no dependency).
269
+ - `Slot` + the `asChild` prop — render as another element while keeping classes and attributes.
270
+ - `composeRefs(...refs)` — merge several refs into one callback.
271
+
272
+ ## Documentation
273
+
274
+ - **Playground** — live demos with copyable code: [ui.konce.pt/react](https://ui.konce.pt/react).
275
+ - **`llms.txt`** — LLM-friendly API sheets per component, shipped inside the package; the
276
+ package-wide sheet lives at [ui.konce.pt/llms/react/llms.txt](https://ui.konce.pt/llms/react/llms.txt)
277
+ (PL mirror: [llms-pl.txt](https://ui.konce.pt/llms/react/llms-pl.txt)).
278
+ - Repository: [gitlab.com/konce-pt/koncept-ui](https://gitlab.com/konce-pt/koncept-ui)
279
+
280
+ ## License
281
+
282
+ MIT © konce.pt
283
+
284
+ ---
285
+
286
+ <div align="center">
287
+
288
+ ## 🇵🇱 Wersja polska
289
+
290
+ </div>
291
+
292
+ **Koncept UI** to darmowy system projektowy na licencji **MIT**. Ta paczka to jego port na
293
+ **Reacta 19** — nie podobnie wyglądający zamiennik, tylko ten sam kontrakt renderowania.
294
+
295
+ - 🎯 **Jedna warstwa CSS dla obu portów** — te same klasy `kpt-*`, te same tokeny `--kpt-*`, ta sama
296
+ kolejność `@layer kpt.*`. Drzewo DOM odpowiada wersji Angulara węzeł w węzeł.
297
+ - 📦 **Zero zależności runtime** — `cn`, `Slot` i `composeRefs` są w paczce.
298
+ - ⚛️ **React 19** — `ref` jako zwykły prop (bez `forwardRef`), kontrolki działają kontrolowane
299
+ i niekontrolowane, z natywnym `name`/`form` (React 19 Actions i React Hook Form bez adaptera).
300
+ - 📊 **Flagowa tabela danych** — sortowanie, filtry globalne i kolumnowe, paginacja, virtual scroll,
301
+ zaznaczanie, przestawianie i zamrażanie kolumn, edycja w miejscu, podsumowanie, eksport CSV.
302
+ - 🎨 **100% na tokenach** — każdy styl to `var(--kpt-*)`. Motyw jasny/ciemny, neutralna paleta OKLCH.
303
+ - ♿ **Natywne elementy** — `<button>`, `<input>`, `<label>` wszędzie tam, gdzie istnieją; stan błędu
304
+ na `aria-invalid`, nie na klasie.
305
+ - 🌍 **Wbudowane i18n** — angielski (domyślny) i polski, przełączanie w runtime, dowolny język
306
+ jednym plikiem JSON.
307
+
308
+ ### Instalacja
309
+
310
+ ```bash
311
+ npm i @konce-pt/react @konce-pt/tokens @konce-pt/styles
312
+ ```
313
+
314
+ `react` i `react-dom` (`>=19`) to peery, które masz już w aplikacji. Opcjonalnie:
315
+ `@konce-pt/grid` (układ; komponenty w `@konce-pt/react/grid`), `@konce-pt/validators`
316
+ (walidatory PL: NIP, REGON, PESEL, IBAN, kod pocztowy) oraz `leaflet` (`^1.9`) — **opcjonalny**
317
+ peer, potrzebny wyłącznie dla `@konce-pt/react/map`.
318
+
319
+ ### Konfiguracja
320
+
321
+ Paczka wysyła **sam JavaScript**. CSS ładujesz raz, w wejściu aplikacji:
322
+
323
+ ```ts
324
+ import '@konce-pt/tokens/css';
325
+ import '@konce-pt/tokens/css/dark';
326
+ import '@konce-pt/styles'; // reset, kolejność warstw, motyw
327
+ import '@konce-pt/styles/components'; // warstwa komponentów — WYMAGANE, to ona styluje kpt-*
328
+ import '@konce-pt/grid'; // opcjonalnie — klasy układu
329
+ ```
330
+
331
+ `@konce-pt/styles/components` powstaje z tego samego SCSS-a, który port Angulara kompiluje do
332
+ własnego bundla — stąd identyczny wygląd obu portów. Angular wstrzykuje te style w runtime, React
333
+ nie ma dla nich innego źródła, więc ten import nie jest opcjonalny.
334
+
335
+ Motyw ciemny: `<html data-theme="dark">` (albo `prefers-color-scheme`). Nadpisania `--kpt-color-*`
336
+ w arkuszu importowanym **po** `@konce-pt/styles`.
337
+
338
+ > **CDK nie jest tu potrzebny.** Komponenty z panelem (`KptSelect`, `KptPopover`, `KptMenu`,
339
+ > `KptAutocomplete`, `KptDatepicker`…) renderują go przez wewnętrzny `KptOverlay` do portalu na
340
+ > `document.body`, więc `@angular/cdk/overlay-prebuilt.css` nie jest wymagany. W otwartym
341
+ > `KptDialog` panel trafia do `<dialog>`, bo `showModal()` przenosi go do górnej warstwy przeglądarki.
342
+
343
+ ### Ikony
344
+
345
+ **84 ikony są wbudowane i nie wymagają konfiguracji** — to, co rysują same komponenty, plus staple
346
+ powłoki aplikacji (`sun`, `moon`, `bell`, `settings`, `users`, `logout`, `layout-dashboard`,
347
+ `home`, `activity`, `chart-bar`, `brand-github`…). Typowy panel administracyjny nie potrzebuje nic
348
+ więcej.
349
+
350
+ Pełny zestaw **Tabler Icons** (MIT, 5130 ikon) ma dwie ścieżki, a różnica jest w bundlu. Każda ikona
351
+ jest osobnym eksportem, więc import po nazwie zostawia w bundlu wyłącznie użyte:
352
+
353
+ ```tsx
354
+ // Produkcja — do bundla wchodzą dwie ikony.
355
+ import { LayoutDashboard, Rocket } from '@konce-pt/react/icons';
356
+ import { registerKptIcons } from '@konce-pt/react';
357
+ registerKptIcons([LayoutDashboard, Rocket]);
358
+
359
+ // Prototyp — wszystkie ikony, około 1,2 MB.
360
+ import { registerKptTablerIcons } from '@konce-pt/react/icons';
361
+ registerKptTablerIcons();
362
+ ```
363
+
364
+ To `registerKptIcons` odchudza, bo przyjmuje dane argumentem i sam nie importuje pełnego zestawu.
365
+ Z tego samego powodu `registerKptIcons(tabler)` z importem przestrzeni nazw (`import * as tabler`)
366
+ rejestruje komplet — namespace przekazany do funkcji blokuje analizę statyczną. Rejestr jest
367
+ globalny, więc wystarczy jedno wywołanie w wejściu aplikacji.
368
+
369
+ ### Internacjonalizacja (i18n)
370
+
371
+ Etykiety są dostępne po **angielsku (domyślnie)** i **polsku**. Bez providera wszystko renderuje się
372
+ po angielsku — brak providera nigdy nie jest błędem. `<KptI18nProvider locale="pl">` ustawia język
373
+ dla poddrzewa, `useKptI18n()` zwraca `{ locale, setLocale, messages, messagesFor, t }`, a
374
+ `t('paginator.range', { start: 1, end: 10, total: 42 })` interpoluje `{nazwa}` i spada na EN, potem
375
+ na sam klucz.
376
+
377
+ **Własny język jednym plikiem JSON** (kształt = kontrakt `KptMessages`; brakujące klucze spadają
378
+ na EN):
379
+
380
+ ```tsx
381
+ import de from './i18n/de.json';
382
+
383
+ <KptI18nProvider locale="de" messages={{ de }}>…</KptI18nProvider>
384
+ ```
385
+
386
+ Propsy `locale` i `dictionary` na komponencie mają priorytet nad providerem. Nazwy dni i miesięcy
387
+ w kalendarzach pochodzą z `Intl`. Kontrakt i słowniki żyją w `@konce-pt/i18n`, wspólnym z portem
388
+ Angulara.
389
+
390
+ ### Komponenty (80+)
391
+
392
+ Formularze, przyciski i akcje, layout, nawigacja, dane (z flagową `KptDataTable`), feedback
393
+ i overlay — pełna lista w tabeli powyżej. Ciężkie rzeczy (wykresy, roadmapa, mapy, pełny Tabler)
394
+ siedzą w subpathach `@konce-pt/react/{charts,roadmap,grid,map,icons}`, żeby nie obciążać rdzenia.
395
+ Każdy komponent ma obok źródła plik `llms.txt` z opisem API.
43
396
 
44
- - Drzewo DOM odpowiada wersji Angulara węzeł w węzeł — host jest odtwarzany jako wrapper
45
- (`<span class="kpt-button-host">` wokół `<button class="kpt-button">`), dzięki czemu SCSS
46
- jest reużyty bez zmian.
47
- - `ref` i `className` trafiają na element wewnętrzny; wrapper konfiguruje `hostProps`.
48
- - Warianty i rozmiary przez atrybuty `data-*`, nie przez klasy modyfikatorów.
49
- - React 19: `ref` jest zwykłym propem — bez `forwardRef`.
50
- - Kontrolki formularza działają kontrolowane i niekontrolowane, z natywnym `name`/`form`
51
- (React 19 Actions i React Hook Form bez adaptera).
52
- - Tłumaczenia pochodzą z `@konce-pt/i18n`, wspólnego z portem Angulara.
397
+ ### Dokumentacja
53
398
 
54
- Pełna dokumentacja dla modeli: [`llms.txt`](./llms.txt) (EN) i [`llms-pl.txt`](./llms-pl.txt) (PL).
399
+ Playground z demami: [ui.konce.pt/react](https://ui.konce.pt/react). Opisy API dla modeli:
400
+ [`llms.txt`](https://ui.konce.pt/llms/react/llms.txt) (EN) i
401
+ [`llms-pl.txt`](https://ui.konce.pt/llms/react/llms-pl.txt) (PL). Repozytorium:
402
+ [gitlab.com/konce-pt/koncept-ui](https://gitlab.com/konce-pt/koncept-ui).
55
403
 
56
- ## Licencja
404
+ ### Licencja
57
405
 
58
406
  MIT © konce.pt
@@ -0,0 +1,56 @@
1
+ # KptAccordion (@konce-pt/react)
2
+
3
+ An accordion — a group of expandable panels. `multi` allows several open at once.
4
+ Import: `import { KptAccordion } from '@konce-pt/react';`
5
+
6
+ ## DOM
7
+ <div class="kpt-accordion-host">
8
+ <div class="kpt-accordion-panel-host" style="--kpt-anim-duration: 500ms">
9
+ <div class="kpt-accordion-panel is-open">
10
+ <button class="kpt-accordion-panel__header" aria-expanded="true">
11
+ <span class="kpt-accordion-panel__title">Section 1</span>
12
+ <span class="kpt-icon-host kpt-accordion-panel__chevron is-open">…</span>
13
+ </button>
14
+ <div class="kpt-accordion-panel__collapse is-open">
15
+ <div class="kpt-accordion-panel__collapse-inner">
16
+ <div class="kpt-accordion-panel__body">…children…</div>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </div>
22
+
23
+ ## KptAccordion props
24
+ - `multi`: boolean — several panels open at once
25
+ - `openIndexes` / `defaultOpenIndexes` / `onOpenIndexesChange` — the open state, by index
26
+ - `animated`: boolean (default true), `animationDuration`: number (ms, default 500)
27
+
28
+ ## KptAccordion.Panel props
29
+ - `title` (required), `disabled`
30
+ - `expanded` / `defaultExpanded` / `onExpandedChange` — used when the panel stands alone
31
+ - `children`
32
+
33
+ ## Who owns the state
34
+ The accordion owns it, and panels receive their index by cloning — without that, single mode would
35
+ have no way to close its siblings. A panel used outside an accordion falls back to its own state, so
36
+ it works standalone with no extra wrapper.
37
+
38
+ ## Collapsed content stays mounted
39
+ It gets `inert` and `aria-hidden` instead of being removed, so the height can be animated while
40
+ nothing inside takes focus or reaches a screen reader.
41
+
42
+ ## Examples
43
+ <KptAccordion defaultOpenIndexes={[0]}>
44
+ <KptAccordion.Panel title="Section 1">…</KptAccordion.Panel>
45
+ <KptAccordion.Panel title="Section 2">…</KptAccordion.Panel>
46
+ </KptAccordion>
47
+
48
+ <KptAccordion multi openIndexes={open} onOpenIndexesChange={setOpen}>…</KptAccordion>
49
+
50
+ ## Tokens
51
+ `--kpt-color-border`, `--kpt-color-surface`, `--kpt-motion-easing-standard`, plus
52
+ `--kpt-anim-duration` set by the accordion on each panel.
53
+
54
+ ## Accessibility
55
+ The header is a native button with `aria-expanded`; the collapsed region is `inert` and
56
+ `aria-hidden`, so it is skipped by both the keyboard and the screen reader.
@@ -0,0 +1,45 @@
1
+ # KptAlert (@konce-pt/react)
2
+
3
+ An inline message. Variants info/success/warning/danger, an optional title and dismissal.
4
+ Import: `import { KptAlert } from '@konce-pt/react';`
5
+
6
+ ## DOM
7
+ <div class="kpt-alert-host">
8
+ <div class="kpt-alert" data-variant="success" role="alert">
9
+ <span class="kpt-icon-host kpt-alert__icon">…</span>
10
+ <div class="kpt-alert__content">
11
+ <div class="kpt-alert__title">Saved</div>
12
+ <div class="kpt-alert__message">…children…</div>
13
+ </div>
14
+ <button class="kpt-alert__close" type="button" aria-label="Close">…</button>
15
+ </div>
16
+ </div>
17
+
18
+ ## Props
19
+ - `variant`: 'info' | 'success' | 'warning' | 'danger' (default 'info')
20
+ - `title`: string
21
+ - `dismissible`: boolean — adds the close button
22
+ - `onClosed()` — fires after the user dismisses it
23
+ - `locale`, `dictionary` — i18n overrides for this component only
24
+ - `className`, `ref` and every other `<div>` prop go to the host
25
+
26
+ ## Dismissal hides, it does not unmount
27
+ Closing sets `hidden` on the host, the same as the Angular port, so the same alert can be shown
28
+ again without rebuilding the tree. When you would rather drive it from outside, simply do not render
29
+ the component — the internal state is a convenience, not the only way.
30
+
31
+ ## The icon follows the variant
32
+ Each variant picks its own icon from the built-in set (`info`, `circle-check`, `alert-triangle`,
33
+ `circle-x`), so there is nothing to configure and no way for the icon to contradict the colour.
34
+
35
+ ## Examples
36
+ <KptAlert variant="info" title="Info">A new version is available.</KptAlert>
37
+ <KptAlert variant="danger" title="Error" dismissible onClosed={onClosed}>Could not save.</KptAlert>
38
+
39
+ ## Tokens
40
+ The semantic colour pairs of each variant (`--kpt-color-info` / `-success` / `-warning` /
41
+ `-danger` with their `-subtle` counterparts), `--kpt-radius-md`.
42
+
43
+ ## Accessibility
44
+ The message is `role="alert"`, so a screen reader announces it as it appears. Use it for something
45
+ that just happened — for a permanent note, style your own container without that role.
@@ -92,3 +92,23 @@ export declare function useKptShellNavToggle(options?: {
92
92
  readonly 'aria-label': string;
93
93
  readonly onClick: () => void;
94
94
  };
95
+ /**
96
+ * Etykieta pozycji nawigacji. W szynie ikon (układ md–xl) shell chowa ją techniką
97
+ * „visually hidden", więc pozycja zwęża się do samego znaku, a czytnik ekranu wciąż
98
+ * odczytuje pełny tekst.
99
+ *
100
+ * Nosi atrybut `kptnavlabel` — to po nim celuje wspólny SCSS. Nazwy atrybutów w selektorach
101
+ * CSS są w HTML case-insensitive, więc jedna reguła obsługuje oba porty; zapis małymi literami
102
+ * omija przy okazji ostrzeżenie React o nieznanym propie.
103
+ *
104
+ * @example
105
+ * <a href="/pulpit"><KptIcon name="layout-dashboard" /> <KptNavLabel>Pulpit</KptNavLabel></a>
106
+ */
107
+ export declare function KptNavLabel({ className, ...rest }: Readonly<ComponentPropsWithRef<'span'>>): import("react").JSX.Element;
108
+ /**
109
+ * Nagłówek grupy pozycji w nawigacji. W szynie ikon chowany tak samo jak [[KptNavLabel]].
110
+ *
111
+ * @example
112
+ * <KptNavSection>Raporty</KptNavSection>
113
+ */
114
+ export declare function KptNavSection({ className, ...rest }: Readonly<ComponentPropsWithRef<'div'>>): import("react").JSX.Element;
@@ -103,3 +103,27 @@ export function useKptShellNavToggle(options) {
103
103
  onClick: toggleSidenav,
104
104
  };
105
105
  }
106
+ /**
107
+ * Etykieta pozycji nawigacji. W szynie ikon (układ md–xl) shell chowa ją techniką
108
+ * „visually hidden", więc pozycja zwęża się do samego znaku, a czytnik ekranu wciąż
109
+ * odczytuje pełny tekst.
110
+ *
111
+ * Nosi atrybut `kptnavlabel` — to po nim celuje wspólny SCSS. Nazwy atrybutów w selektorach
112
+ * CSS są w HTML case-insensitive, więc jedna reguła obsługuje oba porty; zapis małymi literami
113
+ * omija przy okazji ostrzeżenie React o nieznanym propie.
114
+ *
115
+ * @example
116
+ * <a href="/pulpit"><KptIcon name="layout-dashboard" /> <KptNavLabel>Pulpit</KptNavLabel></a>
117
+ */
118
+ export function KptNavLabel({ className, ...rest }) {
119
+ return _jsx("span", { kptnavlabel: '', className: className, ...rest });
120
+ }
121
+ /**
122
+ * Nagłówek grupy pozycji w nawigacji. W szynie ikon chowany tak samo jak [[KptNavLabel]].
123
+ *
124
+ * @example
125
+ * <KptNavSection>Raporty</KptNavSection>
126
+ */
127
+ export function KptNavSection({ className, ...rest }) {
128
+ return _jsx("div", { kptnavsection: '', className: className, ...rest });
129
+ }
@@ -0,0 +1,59 @@
1
+ # KptAppShell (@konce-pt/react)
2
+
3
+ The application skeleton: a bar on top, a side panel and the content.
4
+ Import: `import { KptAppShell, useKptShellNavToggle } from '@konce-pt/react';`
5
+
6
+ ## DOM
7
+ <div class="kpt-app-shell kpt-app-shell--nav-open"
8
+ data-sidenav-mode="auto" data-sidenav-breakpoint="lg">
9
+ …toolbar…
10
+ <div class="kpt-app-shell__body">
11
+ <aside class="kpt-app-shell__sidenav" role="navigation" tabindex="-1" id="…">…sidenav…</aside>
12
+ <div class="kpt-app-shell__scrim" aria-hidden="true"></div>
13
+ <main class="kpt-app-shell__content">…children…</main>
14
+ </div>
15
+ </div>
16
+
17
+ ## Props
18
+ - `toolbar`: ReactNode — the slot above the content area (usually `<KptToolbar>`)
19
+ - `sidenav`: ReactNode — the side panel slot (usually `<KptSidenav>`)
20
+ - `children`: ReactNode — the application content
21
+ - `sidenavOpen` / `defaultSidenavOpen` / `onSidenavOpenChange` — the docked layout's open state
22
+ (default open)
23
+ - `sidenavCompactOpen` / `defaultSidenavCompactOpen` / `onSidenavCompactOpenChange` — the overlay's
24
+ open state (default closed)
25
+ - `sidenavMode`: 'auto' | 'side' | 'over' (default 'auto')
26
+ - `sidenavBreakpoint`: 'sm' | 'md' | 'lg' | 'xl' | '2xl' (default 'lg')
27
+ - `sidenavRailBreakpoint`: the same union or null (default null)
28
+ - `sidenavLabel`: string — empty falls back to `appShell.navigation` from the dictionary
29
+ - `locale`, `dictionary` — i18n overrides for this component only
30
+
31
+ ## Two open states, on purpose
32
+ The docked layout and the overlay keep separate state. If they shared one, narrowing the window
33
+ would have to overwrite the consumer's state, and widening it back would have nothing to restore.
34
+ `useKptShellNavToggle` hits whichever state governs the current layout, so one button serves both.
35
+
36
+ ## The nav toggle
37
+ const toggle = useKptShellNavToggle(); // optional { label }
38
+ <KptButton variant="text" {...toggle}>Menu</KptButton>
39
+
40
+ It returns `aria-expanded`, `aria-controls`, `aria-label` and `onClick`. Spread them on a native
41
+ `<button>` or on `<KptButton>` (which forwards them to its inner `<button>`) — ARIA attributes must
42
+ land on the element that carries the role, never on a wrapper.
43
+
44
+ `useKptAppShell()` exposes the same state directly: `{ isSidenavOpen, isOverlay, sidenavId,
45
+ toggleSidenav, openSidenav, closeSidenav }`. It throws outside `<KptAppShell>`.
46
+
47
+ ## Layout is CSS, behaviour is JS
48
+ Which layout applies is decided entirely by the stylesheet from the `data-*` attributes, so nothing
49
+ flickers before hydration and the page is correct with JavaScript disabled. The breakpoint hook only
50
+ decides WHICH state the button flips. The scrim is always in the DOM; CSS controls its visibility.
51
+
52
+ ## Tokens
53
+ `--kpt-sidenav-width` (16rem), `--kpt-sidenav-rail-width` (4rem), `--kpt-z-overlay`,
54
+ `--kpt-color-overlay-scrim`, `--kpt-motion-duration-base`, `--kpt-motion-easing-standard`.
55
+
56
+ ## Accessibility
57
+ - The panel is an `<aside role="navigation">` with a label and an `id` for `aria-controls`.
58
+ - A closed docked panel leaves the tab order through `visibility`, correct on the first frame.
59
+ - While the overlay is open the content gets `inert`, and Escape closes the panel.
@@ -10,7 +10,7 @@ export interface KptAuthProps extends Omit<ComponentPropsWithRef<'div'>, 'onSubm
10
10
  defaultMode?: KptAuthMode;
11
11
  onModeChange?: (mode: KptAuthMode) => void;
12
12
  layout?: KptAuthLayout;
13
- providers?: KptAuthProvider[];
13
+ providers?: readonly KptAuthProvider[];
14
14
  providerLayout?: KptAuthProviderLayout;
15
15
  showName?: boolean;
16
16
  requireTerms?: boolean;