@konce-pt/react 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -0
  3. package/dist/accordion/accordion.d.ts +45 -0
  4. package/dist/accordion/accordion.js +69 -0
  5. package/dist/alert/alert.d.ts +25 -0
  6. package/dist/alert/alert.js +31 -0
  7. package/dist/app-shell/app-shell.d.ts +94 -0
  8. package/dist/app-shell/app-shell.js +105 -0
  9. package/dist/auth/auth-types.d.ts +44 -0
  10. package/dist/auth/auth-types.js +2 -0
  11. package/dist/auth/auth.d.ts +76 -0
  12. package/dist/auth/auth.js +184 -0
  13. package/dist/autocomplete/autocomplete.d.ts +18 -0
  14. package/dist/autocomplete/autocomplete.js +72 -0
  15. package/dist/avatar/avatar.d.ts +33 -0
  16. package/dist/avatar/avatar.js +30 -0
  17. package/dist/avatar-group/avatar-group.d.ts +21 -0
  18. package/dist/avatar-group/avatar-group.js +15 -0
  19. package/dist/badge/badge.d.ts +25 -0
  20. package/dist/badge/badge.js +14 -0
  21. package/dist/bottom-sheet/bottom-sheet.d.ts +21 -0
  22. package/dist/bottom-sheet/bottom-sheet.js +22 -0
  23. package/dist/breadcrumb/breadcrumb.d.ts +19 -0
  24. package/dist/breadcrumb/breadcrumb.js +20 -0
  25. package/dist/button/button.d.ts +27 -0
  26. package/dist/button/button.js +49 -0
  27. package/dist/button-group/button-group.d.ts +25 -0
  28. package/dist/button-group/button-group.js +30 -0
  29. package/dist/card/card.d.ts +66 -0
  30. package/dist/card/card.js +96 -0
  31. package/dist/carousel/carousel.d.ts +20 -0
  32. package/dist/carousel/carousel.js +24 -0
  33. package/dist/charts/chart-legend.d.ts +23 -0
  34. package/dist/charts/chart-legend.js +9 -0
  35. package/dist/charts/chart-tooltip.d.ts +21 -0
  36. package/dist/charts/chart-tooltip.js +13 -0
  37. package/dist/charts/chart.d.ts +18 -0
  38. package/dist/charts/chart.js +231 -0
  39. package/dist/charts-entry/public-api.d.ts +4 -0
  40. package/dist/charts-entry/public-api.js +7 -0
  41. package/dist/checkbox/checkbox.d.ts +33 -0
  42. package/dist/checkbox/checkbox.js +29 -0
  43. package/dist/chip/chip.d.ts +22 -0
  44. package/dist/chip/chip.js +16 -0
  45. package/dist/chips-input/chips-input.d.ts +25 -0
  46. package/dist/chips-input/chips-input.js +54 -0
  47. package/dist/clock/clock.d.ts +17 -0
  48. package/dist/clock/clock.js +57 -0
  49. package/dist/color-picker/color-picker.d.ts +23 -0
  50. package/dist/color-picker/color-picker.js +28 -0
  51. package/dist/confirm/confirm-container.d.ts +13 -0
  52. package/dist/confirm/confirm-container.js +26 -0
  53. package/dist/confirm/confirm-store.d.ts +29 -0
  54. package/dist/confirm/confirm-store.js +52 -0
  55. package/dist/context-menu/context-menu.d.ts +21 -0
  56. package/dist/context-menu/context-menu.js +36 -0
  57. package/dist/data-table/data-table-types.d.ts +67 -0
  58. package/dist/data-table/data-table-types.js +1 -0
  59. package/dist/data-table/data-table.d.ts +62 -0
  60. package/dist/data-table/data-table.js +453 -0
  61. package/dist/data-table/export-csv.d.ts +7 -0
  62. package/dist/data-table/export-csv.js +19 -0
  63. package/dist/data-view/data-view.d.ts +21 -0
  64. package/dist/data-view/data-view.js +25 -0
  65. package/dist/date-range/date-range.d.ts +30 -0
  66. package/dist/date-range/date-range.js +155 -0
  67. package/dist/datepicker/datepicker.d.ts +39 -0
  68. package/dist/datepicker/datepicker.js +202 -0
  69. package/dist/dialog/dialog.d.ts +35 -0
  70. package/dist/dialog/dialog.js +59 -0
  71. package/dist/divider/divider.d.ts +16 -0
  72. package/dist/divider/divider.js +13 -0
  73. package/dist/drawer/drawer.d.ts +25 -0
  74. package/dist/drawer/drawer.js +25 -0
  75. package/dist/empty/empty.d.ts +18 -0
  76. package/dist/empty/empty.js +14 -0
  77. package/dist/fab/fab.d.ts +22 -0
  78. package/dist/fab/fab.js +17 -0
  79. package/dist/fieldset/fieldset.d.ts +22 -0
  80. package/dist/fieldset/fieldset.js +24 -0
  81. package/dist/file-upload/file-upload.d.ts +22 -0
  82. package/dist/file-upload/file-upload.js +50 -0
  83. package/dist/form-field/form-field-context.d.ts +23 -0
  84. package/dist/form-field/form-field-context.js +17 -0
  85. package/dist/form-field/form-field.d.ts +39 -0
  86. package/dist/form-field/form-field.js +41 -0
  87. package/dist/galleria/galleria.d.ts +22 -0
  88. package/dist/galleria/galleria.js +21 -0
  89. package/dist/grid/col.d.ts +39 -0
  90. package/dist/grid/col.js +32 -0
  91. package/dist/grid/flex.d.ts +45 -0
  92. package/dist/grid/flex.js +35 -0
  93. package/dist/grid/grid.d.ts +47 -0
  94. package/dist/grid/grid.js +38 -0
  95. package/dist/grid/layout.d.ts +18 -0
  96. package/dist/grid/layout.js +32 -0
  97. package/dist/grid-entry/public-api.d.ts +6 -0
  98. package/dist/grid-entry/public-api.js +8 -0
  99. package/dist/i18n/i18n-context.d.ts +45 -0
  100. package/dist/i18n/i18n-context.js +65 -0
  101. package/dist/icon/icon-registry.d.ts +16 -0
  102. package/dist/icon/icon-registry.js +43 -0
  103. package/dist/icon/icon.d.ts +17 -0
  104. package/dist/icon/icon.js +23 -0
  105. package/dist/icon-button/icon-button.d.ts +23 -0
  106. package/dist/icon-button/icon-button.js +14 -0
  107. package/dist/icons-entry/public-api.d.ts +12 -0
  108. package/dist/icons-entry/public-api.js +20 -0
  109. package/dist/image/image.d.ts +17 -0
  110. package/dist/image/image.js +16 -0
  111. package/dist/input/input.d.ts +41 -0
  112. package/dist/input/input.js +36 -0
  113. package/dist/input-mask/input-mask.d.ts +19 -0
  114. package/dist/input-mask/input-mask.js +65 -0
  115. package/dist/input-number/input-number.d.ts +28 -0
  116. package/dist/input-number/input-number.js +52 -0
  117. package/dist/input-otp/input-otp.d.ts +21 -0
  118. package/dist/input-otp/input-otp.js +59 -0
  119. package/dist/internal/cn.d.ts +9 -0
  120. package/dist/internal/cn.js +19 -0
  121. package/dist/internal/compose-refs.d.ts +6 -0
  122. package/dist/internal/compose-refs.js +16 -0
  123. package/dist/internal/overlay-container.d.ts +3 -0
  124. package/dist/internal/overlay-container.js +19 -0
  125. package/dist/internal/overlay.d.ts +26 -0
  126. package/dist/internal/overlay.js +64 -0
  127. package/dist/internal/slot.d.ts +23 -0
  128. package/dist/internal/slot.js +49 -0
  129. package/dist/internal/types.d.ts +25 -0
  130. package/dist/internal/types.js +1 -0
  131. package/dist/internal/use-breakpoint.d.ts +18 -0
  132. package/dist/internal/use-breakpoint.js +52 -0
  133. package/dist/internal/use-controllable-state.d.ts +11 -0
  134. package/dist/internal/use-controllable-state.js +18 -0
  135. package/dist/internal/use-id.d.ts +9 -0
  136. package/dist/internal/use-id.js +13 -0
  137. package/dist/knob/knob.d.ts +27 -0
  138. package/dist/knob/knob.js +86 -0
  139. package/dist/listbox/listbox.d.ts +28 -0
  140. package/dist/listbox/listbox.js +43 -0
  141. package/dist/map/map-create-dialog.d.ts +21 -0
  142. package/dist/map/map-create-dialog.js +196 -0
  143. package/dist/map/map-hours-field.d.ts +22 -0
  144. package/dist/map/map-hours-field.js +90 -0
  145. package/dist/map/map-image-field.d.ts +24 -0
  146. package/dist/map/map-image-field.js +61 -0
  147. package/dist/map/map-legend.d.ts +29 -0
  148. package/dist/map/map-legend.js +41 -0
  149. package/dist/map/map-poi-card.d.ts +42 -0
  150. package/dist/map/map-poi-card.js +152 -0
  151. package/dist/map/map-search.d.ts +37 -0
  152. package/dist/map/map-search.js +201 -0
  153. package/dist/map/map.d.ts +113 -0
  154. package/dist/map/map.js +425 -0
  155. package/dist/map/ports.d.ts +76 -0
  156. package/dist/map/ports.js +174 -0
  157. package/dist/map-entry/public-api.d.ts +20 -0
  158. package/dist/map-entry/public-api.js +19 -0
  159. package/dist/megamenu/megamenu.d.ts +17 -0
  160. package/dist/megamenu/megamenu.js +28 -0
  161. package/dist/menu/menu-item.d.ts +33 -0
  162. package/dist/menu/menu-item.js +1 -0
  163. package/dist/menu/menu-rows.d.ts +9 -0
  164. package/dist/menu/menu-rows.js +15 -0
  165. package/dist/menu/menu.d.ts +22 -0
  166. package/dist/menu/menu.js +31 -0
  167. package/dist/menubar/menubar.d.ts +16 -0
  168. package/dist/menubar/menubar.js +39 -0
  169. package/dist/meter-group/meter-group.d.ts +20 -0
  170. package/dist/meter-group/meter-group.js +24 -0
  171. package/dist/order-list/order-list.d.ts +18 -0
  172. package/dist/order-list/order-list.js +37 -0
  173. package/dist/paginator/paginator.d.ts +29 -0
  174. package/dist/paginator/paginator.js +45 -0
  175. package/dist/panel/panel.d.ts +31 -0
  176. package/dist/panel/panel.js +36 -0
  177. package/dist/password/password.d.ts +28 -0
  178. package/dist/password/password.js +37 -0
  179. package/dist/pick-list/pick-list.d.ts +28 -0
  180. package/dist/pick-list/pick-list.js +65 -0
  181. package/dist/popover/popover.d.ts +23 -0
  182. package/dist/popover/popover.js +27 -0
  183. package/dist/progress/progress.d.ts +19 -0
  184. package/dist/progress/progress.js +21 -0
  185. package/dist/public-api.d.ts +181 -0
  186. package/dist/public-api.js +105 -0
  187. package/dist/radio-group/radio-group.d.ts +24 -0
  188. package/dist/radio-group/radio-group.js +23 -0
  189. package/dist/rating/rating.d.ts +22 -0
  190. package/dist/rating/rating.js +30 -0
  191. package/dist/rich-text/lists.d.ts +17 -0
  192. package/dist/rich-text/lists.js +144 -0
  193. package/dist/rich-text/rich-text.d.ts +28 -0
  194. package/dist/rich-text/rich-text.js +654 -0
  195. package/dist/rich-text/sanitize.d.ts +10 -0
  196. package/dist/rich-text/sanitize.js +85 -0
  197. package/dist/rich-text/tables.d.ts +44 -0
  198. package/dist/rich-text/tables.js +268 -0
  199. package/dist/roadmap/roadmap.d.ts +37 -0
  200. package/dist/roadmap/roadmap.js +271 -0
  201. package/dist/roadmap-entry/public-api.d.ts +4 -0
  202. package/dist/roadmap-entry/public-api.js +7 -0
  203. package/dist/scroll-top/scroll-top.d.ts +16 -0
  204. package/dist/scroll-top/scroll-top.js +38 -0
  205. package/dist/select/select.d.ts +37 -0
  206. package/dist/select/select.js +117 -0
  207. package/dist/shared/types.d.ts +8 -0
  208. package/dist/shared/types.js +1 -0
  209. package/dist/sidenav/sidenav.d.ts +26 -0
  210. package/dist/sidenav/sidenav.js +41 -0
  211. package/dist/skeleton/skeleton.d.ts +20 -0
  212. package/dist/skeleton/skeleton.js +15 -0
  213. package/dist/slider/slider.d.ts +28 -0
  214. package/dist/slider/slider.js +30 -0
  215. package/dist/speed-dial/speed-dial.d.ts +20 -0
  216. package/dist/speed-dial/speed-dial.js +23 -0
  217. package/dist/spinner/spinner.d.ts +15 -0
  218. package/dist/spinner/spinner.js +14 -0
  219. package/dist/split-button/split-button.d.ts +28 -0
  220. package/dist/split-button/split-button.js +34 -0
  221. package/dist/splitter/splitter.d.ts +39 -0
  222. package/dist/splitter/splitter.js +80 -0
  223. package/dist/stepper/stepper.d.ts +51 -0
  224. package/dist/stepper/stepper.js +71 -0
  225. package/dist/switch/switch.d.ts +39 -0
  226. package/dist/switch/switch.js +30 -0
  227. package/dist/switch-group/switch-group.d.ts +29 -0
  228. package/dist/switch-group/switch-group.js +47 -0
  229. package/dist/tabs/tabs.d.ts +38 -0
  230. package/dist/tabs/tabs.js +74 -0
  231. package/dist/textarea/textarea.d.ts +28 -0
  232. package/dist/textarea/textarea.js +44 -0
  233. package/dist/timeline/timeline.d.ts +19 -0
  234. package/dist/timeline/timeline.js +12 -0
  235. package/dist/toast/toast-container.d.ts +12 -0
  236. package/dist/toast/toast-container.js +23 -0
  237. package/dist/toast/toast-store.d.ts +26 -0
  238. package/dist/toast/toast-store.js +51 -0
  239. package/dist/toolbar/toolbar.d.ts +29 -0
  240. package/dist/toolbar/toolbar.js +24 -0
  241. package/dist/tooltip/tooltip.d.ts +26 -0
  242. package/dist/tooltip/tooltip.js +113 -0
  243. package/dist/tree/tree.d.ts +31 -0
  244. package/dist/tree/tree.js +62 -0
  245. package/package.json +91 -0
@@ -0,0 +1,184 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Children, Fragment, isValidElement, useId, useRef, useState, } from 'react';
3
+ import { cn } from "../internal/cn.js";
4
+ import { useControllableState } from "../internal/use-controllable-state.js";
5
+ import { useKptMessages } from "../i18n/i18n-context.js";
6
+ import { KptAlert } from "../alert/alert.js";
7
+ import { KptButton } from "../button/button.js";
8
+ import { KptCheckbox } from "../checkbox/checkbox.js";
9
+ import { KptFormField } from "../form-field/form-field.js";
10
+ import { KptIcon } from "../icon/icon.js";
11
+ import { KptInput } from "../input/input.js";
12
+ import { KptPassword } from "../password/password.js";
13
+ import { KPT_AUTH_MIN_PASSWORD, } from "./auth-types.js";
14
+ function makeSlot(name) {
15
+ const Slot = (_props) => null;
16
+ Slot.kptAuthSlot = name;
17
+ Slot.displayName = `KptAuth.${name}`;
18
+ return Slot;
19
+ }
20
+ const Brand = makeSlot('brand');
21
+ const Promo = makeSlot('promo');
22
+ const Sso = makeSlot('sso');
23
+ const Fields = makeSlot('fields');
24
+ const Footer = makeSlot('footer');
25
+ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
26
+ /**
27
+ * Ekran uwierzytelniania: logowanie, rejestracja i odzyskiwanie hasła w jednym komponencie,
28
+ * z panelem promocyjnym i listą dostawców zewnętrznych.
29
+ *
30
+ * Komponent jest prezentacyjny — nie woła API ani nie nawiguje. Wywołania backendu podpina
31
+ * aplikacja pod `onSignIn` / `onSignUp` / `onResetPassword` / `onProviderSelect`, a stan
32
+ * operacji podaje z powrotem przez `busy`, `error` i `notice`.
33
+ *
34
+ * @example
35
+ * <KptAuth providers={providers} busy={loading} error={serverError} onSignIn={login}>
36
+ * <KptAuth.Brand><img src="/logo.svg" alt="Konce" /></KptAuth.Brand>
37
+ * </KptAuth>
38
+ */
39
+ export function KptAuth({ mode, defaultMode = 'signIn', onModeChange, layout = 'slider', providers = [], providerLayout = 'list', showName = true, requireTerms = true, showRemember = true, busy = false, error = null, notice = null, promoTitle = null, promoText = null, animated = true, animationDuration = 320, onSignIn, onSignUp, onResetPassword, onProviderSelect, renderProvider, locale, dictionary, className, style, children, ...rest }) {
40
+ const { t } = useKptMessages({ locale, dictionary });
41
+ const [activeMode, setActiveMode] = useControllableState({
42
+ value: mode,
43
+ defaultValue: defaultMode,
44
+ onChange: onModeChange,
45
+ });
46
+ const uid = useId();
47
+ const ids = {
48
+ signIn: `${uid}-signin`,
49
+ signUp: `${uid}-signup`,
50
+ forgot: `${uid}-forgot`,
51
+ email: `${uid}-email`,
52
+ password: `${uid}-password`,
53
+ name: `${uid}-name`,
54
+ regEmail: `${uid}-reg-email`,
55
+ regPassword: `${uid}-reg-password`,
56
+ regConfirm: `${uid}-reg-confirm`,
57
+ resetEmail: `${uid}-reset-email`,
58
+ };
59
+ const headingRef = useRef(null);
60
+ // ── Sloty ──
61
+ const slots = {};
62
+ Children.forEach(children, (child) => {
63
+ if (!isValidElement(child))
64
+ return;
65
+ const name = child.type?.kptAuthSlot;
66
+ if (name)
67
+ slots[name] = child.props.children;
68
+ });
69
+ // ── Modele formularzy ──
70
+ const [signInModel, setSignInModel] = useState({
71
+ email: '',
72
+ password: '',
73
+ remember: false,
74
+ });
75
+ const [signUpModel, setSignUpModel] = useState({
76
+ name: '',
77
+ email: '',
78
+ password: '',
79
+ confirm: '',
80
+ terms: false,
81
+ });
82
+ const [resetModel, setResetModel] = useState({ email: '' });
83
+ const [touched, setTouched] = useState(new Set());
84
+ const touch = (field) => setTouched((prev) => new Set(prev).add(field));
85
+ /*
86
+ * Walidacja jest bezwarunkowa i zwraca sam `kind` — komunikat składamy dopiero przy
87
+ * renderowaniu, dzięki czemu zmiana języka w runtime przerysowuje błędy. Bramka
88
+ * „nie strasz czerwienią świeżego pola" (`touched`) żyje w prezentacji, nie w walidatorze,
89
+ * więc `invalid` mówi prawdę także wtedy, gdy pola nikt jeszcze nie dotknął.
90
+ */
91
+ const signInErrors = {
92
+ email: !signInModel.email ? 'required' : !EMAIL_RE.test(signInModel.email) ? 'email' : null,
93
+ password: !signInModel.password ? 'required' : null,
94
+ };
95
+ const signUpErrors = {
96
+ name: showName && !signUpModel.name ? 'required' : null,
97
+ email: !signUpModel.email ? 'required' : !EMAIL_RE.test(signUpModel.email) ? 'email' : null,
98
+ password: !signUpModel.password
99
+ ? 'required'
100
+ : signUpModel.password.length < KPT_AUTH_MIN_PASSWORD
101
+ ? 'minLength'
102
+ : null,
103
+ // Puste „powtórz hasło" nie jest jeszcze niezgodnością — dopiero wpisana, inna wartość.
104
+ confirm: signUpModel.confirm && signUpModel.confirm !== signUpModel.password ? 'mismatch' : null,
105
+ terms: requireTerms && !signUpModel.terms ? 'required' : null,
106
+ };
107
+ const resetErrors = {
108
+ email: !resetModel.email ? 'required' : !EMAIL_RE.test(resetModel.email) ? 'email' : null,
109
+ };
110
+ const message = (kind) => {
111
+ switch (kind) {
112
+ case 'required':
113
+ return t('auth.errorRequired');
114
+ case 'email':
115
+ return t('auth.errorEmail');
116
+ case 'minLength':
117
+ return t('auth.errorMinLength', { min: KPT_AUTH_MIN_PASSWORD });
118
+ case 'mismatch':
119
+ return t('auth.errorMismatch');
120
+ default:
121
+ return null;
122
+ }
123
+ };
124
+ const errorFor = (form, field, kinds) => touched.has(`${form}.${field}`) ? message(kinds[field] ?? null) : null;
125
+ const anyError = (kinds) => Object.values(kinds).some((kind) => kind !== null);
126
+ const submitAll = (form, fields) => {
127
+ // Submit dotyka całego formularza — inaczej pola, których użytkownik nie ruszył, nie
128
+ // pokazałyby błędu i przycisk wyglądałby na zepsuty.
129
+ setTouched((prev) => {
130
+ const next = new Set(prev);
131
+ for (const field of fields)
132
+ next.add(`${form}.${field}`);
133
+ return next;
134
+ });
135
+ };
136
+ const changeMode = (next) => {
137
+ if (next === activeMode)
138
+ return;
139
+ setActiveMode(next);
140
+ // Fokus na nagłówku nowego trybu: bez tego czytnik nie ogłosi zmiany, a fokus zostaje
141
+ // na przycisku, który właśnie zjechał z ekranu.
142
+ requestAnimationFrame(() => headingRef.current?.focus());
143
+ };
144
+ const selectProvider = (provider) => {
145
+ if (busy)
146
+ return;
147
+ onProviderSelect?.(provider);
148
+ };
149
+ const signUpActive = activeMode === 'signUp';
150
+ const promoMode = signUpActive ? 'signIn' : 'signUp';
151
+ const promoHeading = promoTitle === null ? t(`auth.promo.${promoMode}Title`) : promoTitle;
152
+ const promoBody = promoText === null ? t(`auth.promo.${promoMode}Text`) : promoText;
153
+ return (_jsx("div", { className: cn('kpt-auth-host', className), "data-layout": layout, "data-mode": activeMode, style: { ['--kpt-anim-duration']: `${animated ? animationDuration : 0}ms`, ...style }, ...rest, children: _jsx("div", { className: "kpt-auth", children: _jsxs("div", { className: "kpt-auth__card", children: [_jsx("div", { className: "kpt-auth__form-side", children: _jsxs("div", { className: "kpt-auth__inner", "data-mode": activeMode, children: [_jsx("div", { className: "kpt-auth__brand", children: slots.brand }), _jsx("h2", { ref: headingRef, className: "kpt-auth__title", id: ids[activeMode], "data-mode": activeMode, tabIndex: -1, children: t(`auth.${activeMode}Title`) }), _jsx("p", { className: "kpt-auth__subtitle", children: t(`auth.${activeMode}Subtitle`) }), error ? _jsx(KptAlert, { variant: "danger", children: error }) : null, notice ? _jsx(KptAlert, { variant: "success", children: notice }) : null, activeMode === 'signIn' ? (_jsxs("form", { className: "kpt-auth__form", "aria-labelledby": ids.signIn, onSubmit: (event) => {
154
+ event.preventDefault();
155
+ submitAll('signIn', ['email', 'password']);
156
+ if (!anyError(signInErrors))
157
+ onSignIn?.({ ...signInModel });
158
+ }, children: [_jsx(KptFormField, { label: t('auth.email'), error: errorFor('signIn', 'email', signInErrors), required: true, controlId: ids.email, children: _jsx(KptInput, { type: "email", autoComplete: "email", id: ids.email, placeholder: t('auth.emailPlaceholder'), value: signInModel.email, onValueChange: (email) => setSignInModel({ ...signInModel, email }), onTouch: () => touch('signIn.email') }) }), _jsx(KptFormField, { label: t('auth.password'), error: errorFor('signIn', 'password', signInErrors), required: true, controlId: ids.password, children: _jsx(KptPassword, { autoComplete: "current-password", id: ids.password, value: signInModel.password, onValueChange: (password) => setSignInModel({ ...signInModel, password }), onTouch: () => touch('signIn.password') }) }), slots.fields, _jsxs("div", { className: "kpt-auth__row", children: [showRemember ? (_jsx(KptCheckbox, { label: t('auth.remember'), checked: signInModel.remember, onCheckedChange: (remember) => setSignInModel({ ...signInModel, remember }) })) : (_jsx("span", {})), _jsx("button", { type: "button", className: "kpt-auth__link", onClick: () => changeMode('forgot'), children: t('auth.forgotLink') })] }), _jsx(KptButton, { type: "submit", fullWidth: true, loading: busy, disabled: anyError(signInErrors), children: t('auth.signInSubmit') })] })) : null, activeMode === 'signUp' ? (_jsxs("form", { className: "kpt-auth__form", "aria-labelledby": ids.signUp, onSubmit: (event) => {
159
+ event.preventDefault();
160
+ submitAll('signUp', ['name', 'email', 'password', 'confirm', 'terms']);
161
+ if (!anyError(signUpErrors)) {
162
+ const { name, email, password, terms } = signUpModel;
163
+ onSignUp?.({ name, email, password, terms });
164
+ }
165
+ }, children: [showName ? (_jsx(KptFormField, { label: t('auth.name'), error: errorFor('signUp', 'name', signUpErrors), required: true, controlId: ids.name, children: _jsx(KptInput, { autoComplete: "name", id: ids.name, value: signUpModel.name, onValueChange: (name) => setSignUpModel({ ...signUpModel, name }), onTouch: () => touch('signUp.name') }) })) : null, _jsx(KptFormField, { label: t('auth.email'), error: errorFor('signUp', 'email', signUpErrors), required: true, controlId: ids.regEmail, children: _jsx(KptInput, { type: "email", autoComplete: "email", id: ids.regEmail, placeholder: t('auth.emailPlaceholder'), value: signUpModel.email, onValueChange: (email) => setSignUpModel({ ...signUpModel, email }), onTouch: () => touch('signUp.email') }) }), _jsx(KptFormField, { label: t('auth.password'), error: errorFor('signUp', 'password', signUpErrors), hint: t('auth.passwordHint', { min: KPT_AUTH_MIN_PASSWORD }), required: true, controlId: ids.regPassword, children: _jsx(KptPassword, { autoComplete: "new-password", feedback: true, id: ids.regPassword, value: signUpModel.password, onValueChange: (password) => setSignUpModel({ ...signUpModel, password }), onTouch: () => touch('signUp.password') }) }), _jsx(KptFormField, { label: t('auth.confirmPassword'), error: errorFor('signUp', 'confirm', signUpErrors), required: true, controlId: ids.regConfirm, children: _jsx(KptPassword, { autoComplete: "new-password", id: ids.regConfirm, value: signUpModel.confirm, onValueChange: (confirm) => setSignUpModel({ ...signUpModel, confirm }), onTouch: () => touch('signUp.confirm') }) }), slots.fields, requireTerms ? (_jsx(KptFormField, { error: errorFor('signUp', 'terms', signUpErrors), children: _jsx(KptCheckbox, { label: t('auth.terms'), checked: signUpModel.terms, onCheckedChange: (terms) => setSignUpModel({ ...signUpModel, terms }), onTouch: () => touch('signUp.terms') }) })) : null, _jsx(KptButton, { type: "submit", fullWidth: true, loading: busy, disabled: anyError(signUpErrors), children: t('auth.signUpSubmit') })] })) : null, activeMode === 'forgot' ? (_jsxs("form", { className: "kpt-auth__form", "aria-labelledby": ids.forgot, onSubmit: (event) => {
166
+ event.preventDefault();
167
+ submitAll('forgot', ['email']);
168
+ if (!anyError(resetErrors))
169
+ onResetPassword?.({ ...resetModel });
170
+ }, children: [_jsx(KptFormField, { label: t('auth.email'), error: errorFor('forgot', 'email', resetErrors), required: true, controlId: ids.resetEmail, children: _jsx(KptInput, { type: "email", autoComplete: "email", id: ids.resetEmail, placeholder: t('auth.emailPlaceholder'), value: resetModel.email, onValueChange: (email) => setResetModel({ email }), onTouch: () => touch('forgot.email') }) }), _jsx(KptButton, { type: "submit", fullWidth: true, loading: busy, disabled: anyError(resetErrors), children: t('auth.resetSubmit') }), _jsx("button", { type: "button", className: "kpt-auth__link kpt-auth__link--center", onClick: () => changeMode('signIn'), children: t('auth.backToSignIn') })] })) : null, activeMode !== 'forgot' ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "kpt-auth__sso", children: slots.sso }), providers.length > 0 ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "kpt-auth__separator", role: "separator", "aria-label": t('auth.orContinueWith'), children: _jsx("span", { className: "kpt-auth__separator-text", children: t('auth.orContinueWith') }) }), _jsx("div", { className: "kpt-auth__providers", "data-layout": providerLayout, children: providers.map((provider) => {
171
+ const custom = renderProvider?.(provider, () => selectProvider(provider));
172
+ // Fragment, nie `<span>`: lista dostawców to grid, więc dodatkowy
173
+ // element stałby się jej dzieckiem i własny przycisk przestałby
174
+ // rozciągać się na szerokość kolumny (Angular wstawia `ng-container`).
175
+ if (custom)
176
+ return _jsx(Fragment, { children: custom }, provider.id);
177
+ return (_jsxs("button", { type: "button", className: "kpt-auth__provider", disabled: busy, "aria-label": t('auth.continueWith', { provider: provider.label }), onClick: () => selectProvider(provider), children: [provider.iconUrl ? (_jsx("img", { className: "kpt-auth__provider-logo", src: provider.iconUrl, alt: "" })) : provider.icon ? (_jsx(KptIcon, { className: "kpt-auth__provider-logo", name: provider.icon })) : null, _jsx("span", { className: "kpt-auth__provider-label", children: provider.label }), provider.description && providerLayout === 'list' ? (_jsx("span", { className: "kpt-auth__provider-desc", children: provider.description })) : null] }, provider.id));
178
+ }) })] })) : null, _jsxs("p", { className: "kpt-auth__switch", children: [signUpActive ? t('auth.haveAccount') : t('auth.noAccount'), ' ', _jsx("button", { type: "button", className: "kpt-auth__link", onClick: () => changeMode(signUpActive ? 'signIn' : 'signUp'), children: signUpActive ? t('auth.signInSwitch') : t('auth.signUpSwitch') })] })] })) : null, _jsx("div", { className: "kpt-auth__footer", children: slots.footer })] }) }), _jsx("aside", { className: "kpt-auth__promo", children: _jsxs("div", { className: "kpt-auth__promo-inner", children: [promoHeading ? _jsx("p", { className: "kpt-auth__promo-title", children: promoHeading }) : null, promoBody ? _jsx("p", { className: "kpt-auth__promo-text", children: promoBody }) : null, slots.promo, activeMode !== 'forgot' ? (_jsx(KptButton, { hostProps: { className: 'kpt-auth__promo-cta' }, variant: "outline", onClick: () => changeMode(promoMode), children: promoMode === 'signUp' ? t('auth.signUpSwitch') : t('auth.signInSwitch') })) : null] }) })] }) }) }));
179
+ }
180
+ KptAuth.Brand = Brand;
181
+ KptAuth.Promo = Promo;
182
+ KptAuth.Sso = Sso;
183
+ KptAuth.Fields = Fields;
184
+ KptAuth.Footer = Footer;
@@ -0,0 +1,18 @@
1
+ import { type ComponentPropsWithRef } from 'react';
2
+ export interface KptAutocompleteProps extends Omit<ComponentPropsWithRef<'input'>, 'value' | 'defaultValue' | 'onChange'> {
3
+ options?: string[];
4
+ value?: string;
5
+ defaultValue?: string;
6
+ onValueChange?: (value: string) => void;
7
+ invalid?: boolean;
8
+ touched?: boolean;
9
+ onTouch?: () => void;
10
+ }
11
+ /**
12
+ * Pole z podpowiedziami. Wartością jest wpisany tekst, nie wybrana opcja — podpowiedzi
13
+ * skracają pisanie, ale nie ograniczają wpisu, więc pole nadaje się też do wartości spoza listy.
14
+ *
15
+ * @example
16
+ * <KptAutocomplete options={cities} value={city} onValueChange={setCity} placeholder="Miasto…" />
17
+ */
18
+ export declare function KptAutocomplete({ options, value, defaultValue, onValueChange, invalid, touched, onTouch, disabled, className, onBlur, onFocus, onKeyDown, ref, ...rest }: Readonly<KptAutocompleteProps>): import("react").JSX.Element;
@@ -0,0 +1,72 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useMemo, useRef, useState, } from 'react';
3
+ import { cn } from "../internal/cn.js";
4
+ import { composeRefs } from "../internal/compose-refs.js";
5
+ import { KptOverlay } from "../internal/overlay.js";
6
+ import { useControllableState } from "../internal/use-controllable-state.js";
7
+ /**
8
+ * Pole z podpowiedziami. Wartością jest wpisany tekst, nie wybrana opcja — podpowiedzi
9
+ * skracają pisanie, ale nie ograniczają wpisu, więc pole nadaje się też do wartości spoza listy.
10
+ *
11
+ * @example
12
+ * <KptAutocomplete options={cities} value={city} onValueChange={setCity} placeholder="Miasto…" />
13
+ */
14
+ export function KptAutocomplete({ options = [], value, defaultValue = '', onValueChange, invalid = false, touched, onTouch, disabled = false, className, onBlur, onFocus, onKeyDown, ref, ...rest }) {
15
+ const inputRef = useRef(null);
16
+ const [current, setCurrent] = useControllableState({
17
+ value,
18
+ defaultValue,
19
+ onChange: onValueChange,
20
+ });
21
+ const [open, setOpen] = useState(false);
22
+ const [activeIndex, setActiveIndex] = useState(-1);
23
+ const showInvalid = invalid && (touched ?? true);
24
+ const filtered = useMemo(() => {
25
+ const q = current.trim().toLowerCase();
26
+ return q ? options.filter((option) => option.toLowerCase().includes(q)) : options;
27
+ }, [options, current]);
28
+ const pick = useCallback((option) => {
29
+ setCurrent(option);
30
+ setOpen(false);
31
+ }, [setCurrent]);
32
+ const handleKeyDown = (event) => {
33
+ onKeyDown?.(event);
34
+ if (event.key === 'ArrowDown') {
35
+ event.preventDefault();
36
+ setOpen(true);
37
+ setActiveIndex((index) => Math.min(index + 1, filtered.length - 1));
38
+ }
39
+ else if (event.key === 'ArrowUp') {
40
+ event.preventDefault();
41
+ setActiveIndex((index) => Math.max(index - 1, 0));
42
+ }
43
+ else if (event.key === 'Enter') {
44
+ const option = filtered[activeIndex];
45
+ if (option) {
46
+ event.preventDefault();
47
+ pick(option);
48
+ }
49
+ }
50
+ else if (event.key === 'Escape') {
51
+ setOpen(false);
52
+ }
53
+ };
54
+ return (_jsxs("span", { className: "kpt-autocomplete-host", children: [_jsx("input", { ref: composeRefs(inputRef, ref), className: cn('kpt-autocomplete__input', className), type: "text", role: "combobox", "aria-expanded": open, "aria-invalid": showInvalid || undefined, disabled: disabled, value: current, onChange: (event) => {
55
+ setCurrent(event.target.value);
56
+ setOpen(true);
57
+ setActiveIndex(-1);
58
+ }, onFocus: (event) => {
59
+ onFocus?.(event);
60
+ setOpen(true);
61
+ }, onBlur: (event) => {
62
+ onBlur?.(event);
63
+ setOpen(false);
64
+ onTouch?.();
65
+ }, onKeyDown: handleKeyDown, ...rest }), _jsx(KptOverlay, { open: open && filtered.length > 0, anchor: inputRef.current, onClose: () => setOpen(false), matchAnchorWidth: true, withBackdrop: false, children: _jsx("ul", { className: "kpt-autocomplete__panel", role: "listbox", children: filtered.map((option, index) => (_jsx("li", { className: cn('kpt-autocomplete__option', index === activeIndex && 'is-active'), role: "option", "aria-selected": index === activeIndex,
66
+ // `mousedown` z zablokowanym domyślnym zachowaniem: klik w opcję nie może
67
+ // zabrać fokusu polu, bo `blur` zamknąłby panel przed wyborem.
68
+ onMouseDown: (event) => {
69
+ event.preventDefault();
70
+ pick(option);
71
+ }, onMouseEnter: () => setActiveIndex(index), children: option }, option))) }) })] }));
72
+ }
@@ -0,0 +1,33 @@
1
+ import type { ComponentPropsWithRef } from 'react';
2
+ import type { KptHostProps } from '../internal/types.ts';
3
+ export type KptAvatarSize = 'sm' | 'md' | 'lg' | 'xl';
4
+ export type KptAvatarShape = 'circle' | 'square';
5
+ export interface KptAvatarProps extends Omit<ComponentPropsWithRef<'span'>, 'children'> {
6
+ /** Adres obrazu; ma pierwszeństwo przed inicjałami i ikoną. */
7
+ src?: string | null;
8
+ /** Imię i nazwisko — źródło inicjałów, `title` oraz etykiety dostępności. */
9
+ name?: string;
10
+ /** Nazwa ikony z rejestru, gdy nie ma obrazu ani nazwy. */
11
+ icon?: string | null;
12
+ size?: KptAvatarSize;
13
+ shape?: KptAvatarShape;
14
+ /**
15
+ * Propsy wrappera hosta (`.kpt-avatar-host`). To on jest elementem układu, więc klasy
16
+ * pozycjonujące awatar w rodzicu — jak `kpt-card__avatar` — idą właśnie tutaj.
17
+ */
18
+ hostProps?: KptHostProps<'span'>;
19
+ }
20
+ /**
21
+ * Awatar — obraz, inicjały (z `name`) lub ikona.
22
+ *
23
+ * `name` trafia do natywnego `title` (podpowiedź spod kursora — inicjały same w sobie nie mówią,
24
+ * kto to jest). Bez `src` awatar dostaje dodatkowo `role="img"` i `aria-label`, a same inicjały
25
+ * są `aria-hidden`, żeby czytnik nie przeczytał „AK" litera po literze. Przy `src` etykietę
26
+ * niesie już `alt` obrazu — nie dublujemy jej.
27
+ *
28
+ * @example
29
+ * <KptAvatar src="/u/anna.jpg" />
30
+ * <KptAvatar name="Anna Kowalska" size="lg" />
31
+ * <KptAvatar icon="user" shape="square" />
32
+ */
33
+ export declare function KptAvatar({ src, name, icon, size, shape, hostProps, className, ...rest }: Readonly<KptAvatarProps>): import("react").JSX.Element;
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../internal/cn.js";
3
+ import { KptIcon } from "../icon/icon.js";
4
+ /**
5
+ * Awatar — obraz, inicjały (z `name`) lub ikona.
6
+ *
7
+ * `name` trafia do natywnego `title` (podpowiedź spod kursora — inicjały same w sobie nie mówią,
8
+ * kto to jest). Bez `src` awatar dostaje dodatkowo `role="img"` i `aria-label`, a same inicjały
9
+ * są `aria-hidden`, żeby czytnik nie przeczytał „AK" litera po literze. Przy `src` etykietę
10
+ * niesie już `alt` obrazu — nie dublujemy jej.
11
+ *
12
+ * @example
13
+ * <KptAvatar src="/u/anna.jpg" />
14
+ * <KptAvatar name="Anna Kowalska" size="lg" />
15
+ * <KptAvatar icon="user" shape="square" />
16
+ */
17
+ export function KptAvatar({ src = null, name = '', icon = null, size = 'md', shape = 'circle', hostProps, className, ...rest }) {
18
+ const { className: hostClassName, ...hostRest } = hostProps ?? {};
19
+ const labelled = !src && !!name;
20
+ return (_jsx("span", { className: cn('kpt-avatar-host', hostClassName), ...hostRest, children: _jsx("span", { className: cn('kpt-avatar', className), ...rest, "data-size": size, "data-shape": shape, title: name || undefined, role: labelled ? 'img' : undefined, "aria-label": labelled ? name : undefined, children: src ? (_jsx("img", { className: "kpt-avatar__img", src: src, alt: name })) : name ? (_jsx("span", { className: "kpt-avatar__initials", "aria-hidden": "true", children: initialsOf(name) })) : icon ? (_jsx(KptIcon, { name: icon })) : null }) }));
21
+ }
22
+ /** Do dwóch pierwszych liter z kolejnych członów nazwy. */
23
+ function initialsOf(name) {
24
+ return name
25
+ .trim()
26
+ .split(/\s+/)
27
+ .slice(0, 2)
28
+ .map((word) => word[0]?.toUpperCase() ?? '')
29
+ .join('');
30
+ }
@@ -0,0 +1,21 @@
1
+ import type { ComponentPropsWithRef } from 'react';
2
+ import { type KptAvatarSize } from '../avatar/avatar.tsx';
3
+ export interface KptAvatarItem {
4
+ name?: string;
5
+ src?: string;
6
+ icon?: string;
7
+ }
8
+ export interface KptAvatarGroupProps extends ComponentPropsWithRef<'div'> {
9
+ avatars?: KptAvatarItem[];
10
+ /** Ile awatarów pokazać, zanim pojawi się „+N". */
11
+ max?: number;
12
+ size?: KptAvatarSize;
13
+ }
14
+ /**
15
+ * Grupa nakładających się awatarów z licznikiem „+N". Buduje na `KptAvatar`, więc każdy element
16
+ * dziedziczy jego `title` z imieniem — inicjały same w sobie nie mówią, kto to jest.
17
+ *
18
+ * @example
19
+ * <KptAvatarGroup avatars={team} max={4} />
20
+ */
21
+ export declare function KptAvatarGroup({ avatars, max, size, className, ...rest }: Readonly<KptAvatarGroupProps>): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../internal/cn.js";
3
+ import { KptAvatar } from "../avatar/avatar.js";
4
+ /**
5
+ * Grupa nakładających się awatarów z licznikiem „+N". Buduje na `KptAvatar`, więc każdy element
6
+ * dziedziczy jego `title` z imieniem — inicjały same w sobie nie mówią, kto to jest.
7
+ *
8
+ * @example
9
+ * <KptAvatarGroup avatars={team} max={4} />
10
+ */
11
+ export function KptAvatarGroup({ avatars = [], max = 4, size = 'md', className, ...rest }) {
12
+ const shown = avatars.slice(0, max);
13
+ const overflow = Math.max(0, avatars.length - max);
14
+ return (_jsx("div", { className: cn('kpt-avatar-group-host', className), ...rest, children: _jsxs("div", { className: "kpt-avatar-group", "data-size": size, children: [shown.map((avatar, index) => (_jsx("span", { className: "kpt-avatar-group__item", children: _jsx(KptAvatar, { name: avatar.name || '', src: avatar.src || null, icon: avatar.icon || null, size: size }) }, index))), overflow > 0 ? (_jsxs("span", { className: "kpt-avatar-group__item kpt-avatar-group__more", "data-size": size, children: ["+", overflow] })) : null] }) }));
15
+ }
@@ -0,0 +1,25 @@
1
+ import type { ComponentPropsWithRef, ReactNode } from 'react';
2
+ export type KptBadgeVariant = 'neutral' | 'primary' | 'success' | 'danger' | 'warning' | 'info';
3
+ export type KptBadgeSize = 'sm' | 'md' | 'lg';
4
+ export interface KptBadgeProps extends ComponentPropsWithRef<'span'> {
5
+ value?: string | number | null;
6
+ variant?: KptBadgeVariant;
7
+ /** Rozmiar — `sm` mieści się w wierszu tekstu, `md` to domyślny licznik. */
8
+ size?: KptBadgeSize;
9
+ /** Sama kropka statusu, bez liczby. */
10
+ dot?: boolean;
11
+ /** Nałożenie na treść podaną jako `children`. */
12
+ overlay?: boolean;
13
+ /** Powyżej tej wartości licznik pokazuje `99+`. */
14
+ max?: number;
15
+ children?: ReactNode;
16
+ }
17
+ /**
18
+ * Znacznik (badge) — licznik lub kropka statusu. Samodzielny albo nałożony na treść (`overlay`).
19
+ *
20
+ * @example
21
+ * <KptBadge value={3} variant="danger" />
22
+ * <KptBadge value={128} overlay><KptIcon name="menu" /></KptBadge>
23
+ * <KptBadge dot variant="success" overlay><KptAvatar name="AK" /></KptBadge>
24
+ */
25
+ export declare function KptBadge({ value, variant, size, dot, overlay, max, className, children, ...rest }: Readonly<KptBadgeProps>): import("react").JSX.Element;
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../internal/cn.js";
3
+ /**
4
+ * Znacznik (badge) — licznik lub kropka statusu. Samodzielny albo nałożony na treść (`overlay`).
5
+ *
6
+ * @example
7
+ * <KptBadge value={3} variant="danger" />
8
+ * <KptBadge value={128} overlay><KptIcon name="menu" /></KptBadge>
9
+ * <KptBadge dot variant="success" overlay><KptAvatar name="AK" /></KptBadge>
10
+ */
11
+ export function KptBadge({ value = null, variant = 'primary', size = 'md', dot = false, overlay = false, max = 99, className, children, ...rest }) {
12
+ const display = typeof value === 'number' && value > max ? `${max}+` : (value ?? '');
13
+ return (_jsxs("span", { className: cn('kpt-badge-host', className), ...rest, children: [children, _jsx("span", { className: cn('kpt-badge', overlay && 'kpt-badge--overlay', dot && 'kpt-badge--dot'), "data-variant": variant, "data-size": size, children: dot ? '' : display })] }));
14
+ }
@@ -0,0 +1,21 @@
1
+ import type { ComponentPropsWithRef, ReactNode } from 'react';
2
+ import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
3
+ export interface KptBottomSheetProps extends ComponentPropsWithRef<'div'>, KptI18nOverrides {
4
+ open: boolean;
5
+ onOpenChange: (open: boolean) => void;
6
+ header?: string;
7
+ closeOnBackdrop?: boolean;
8
+ children?: ReactNode;
9
+ }
10
+ /**
11
+ * Arkusz wysuwany z dołu ekranu.
12
+ *
13
+ * Arkusz zostaje w DOM także zamknięty — chowa go `transform` i `visibility`, nigdy
14
+ * `display: none`, bo inaczej animacje wjazdu i zjazdu nie miałyby od czego wystartować.
15
+ * `visibility` wyjmuje go przy tym z kolejności Tab i z drzewa dostępności bez `inert`,
16
+ * i robi to już na pierwszej klatce.
17
+ *
18
+ * @example
19
+ * <KptBottomSheet open={open} onOpenChange={setOpen} header="Udostępnij">…</KptBottomSheet>
20
+ */
21
+ export declare function KptBottomSheet({ open, onOpenChange, header, closeOnBackdrop, locale, dictionary, className, children, ...rest }: Readonly<KptBottomSheetProps>): import("react").JSX.Element;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../internal/cn.js";
3
+ import { useKptMessages } from "../i18n/i18n-context.js";
4
+ /**
5
+ * Arkusz wysuwany z dołu ekranu.
6
+ *
7
+ * Arkusz zostaje w DOM także zamknięty — chowa go `transform` i `visibility`, nigdy
8
+ * `display: none`, bo inaczej animacje wjazdu i zjazdu nie miałyby od czego wystartować.
9
+ * `visibility` wyjmuje go przy tym z kolejności Tab i z drzewa dostępności bez `inert`,
10
+ * i robi to już na pierwszej klatce.
11
+ *
12
+ * @example
13
+ * <KptBottomSheet open={open} onOpenChange={setOpen} header="Udostępnij">…</KptBottomSheet>
14
+ */
15
+ export function KptBottomSheet({ open, onOpenChange, header = '', closeOnBackdrop = true, locale, dictionary, className, children, ...rest }) {
16
+ const { t } = useKptMessages({ locale, dictionary });
17
+ const close = () => onOpenChange(false);
18
+ return (_jsxs("div", { className: cn('kpt-bottom-sheet-host', className), ...rest, children: [_jsx("div", { className: cn('kpt-bottom-sheet__backdrop', open && 'is-open'), onClick: () => closeOnBackdrop && close() }), _jsxs("div", { className: cn('kpt-bottom-sheet__panel', open && 'is-open'), role: "dialog", "aria-modal": "true", tabIndex: -1, onKeyDown: (event) => {
19
+ if (event.key === 'Escape')
20
+ close();
21
+ }, children: [_jsx("button", { type: "button", className: "kpt-bottom-sheet__handle", onClick: close, "aria-label": t('common.close') }), header ? _jsx("div", { className: "kpt-bottom-sheet__header", children: header }) : null, _jsx("div", { className: "kpt-bottom-sheet__body", children: children })] })] }));
22
+ }
@@ -0,0 +1,19 @@
1
+ import type { ComponentPropsWithRef } from 'react';
2
+ import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
3
+ export interface KptBreadcrumbItem {
4
+ label: string;
5
+ url?: string;
6
+ }
7
+ export interface KptBreadcrumbProps extends Omit<ComponentPropsWithRef<'div'>, 'children'>, KptI18nOverrides {
8
+ items?: KptBreadcrumbItem[];
9
+ }
10
+ /**
11
+ * Ścieżka nawigacji (breadcrumbs).
12
+ *
13
+ * Ostatnia pozycja nigdy nie jest odnośnikiem, nawet gdy ma `url` — to bieżąca strona, a link
14
+ * prowadzący w to samo miejsce, w którym już jesteś, tylko myli.
15
+ *
16
+ * @example
17
+ * <KptBreadcrumb items={[{ label: 'Start', url: '/' }, { label: 'Ustawienia' }]} />
18
+ */
19
+ export declare function KptBreadcrumb({ items, locale, dictionary, className, ...rest }: Readonly<KptBreadcrumbProps>): import("react").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../internal/cn.js";
3
+ import { useKptMessages } from "../i18n/i18n-context.js";
4
+ import { KptIcon } from "../icon/icon.js";
5
+ /**
6
+ * Ścieżka nawigacji (breadcrumbs).
7
+ *
8
+ * Ostatnia pozycja nigdy nie jest odnośnikiem, nawet gdy ma `url` — to bieżąca strona, a link
9
+ * prowadzący w to samo miejsce, w którym już jesteś, tylko myli.
10
+ *
11
+ * @example
12
+ * <KptBreadcrumb items={[{ label: 'Start', url: '/' }, { label: 'Ustawienia' }]} />
13
+ */
14
+ export function KptBreadcrumb({ items = [], locale, dictionary, className, ...rest }) {
15
+ const { t } = useKptMessages({ locale, dictionary });
16
+ return (_jsx("div", { className: cn('kpt-breadcrumb-host', className), ...rest, children: _jsx("nav", { className: "kpt-breadcrumb", "aria-label": t('breadcrumb.label'), children: items.map((item, index) => {
17
+ const last = index === items.length - 1;
18
+ return (_jsxs("span", { style: { display: 'contents' }, children: [item.url && !last ? (_jsx("a", { className: "kpt-breadcrumb__link", href: item.url, children: item.label })) : (_jsx("span", { className: "kpt-breadcrumb__current", "aria-current": last ? 'page' : undefined, children: item.label })), last ? null : _jsx(KptIcon, { name: "chevron-right", className: "kpt-breadcrumb__sep" })] }, index));
19
+ }) }) }));
20
+ }
@@ -0,0 +1,27 @@
1
+ import { type ComponentPropsWithRef } from 'react';
2
+ import type { AsChildProps, KptHostProps } from '../internal/types.ts';
3
+ export type KptButtonVariant = 'filled' | 'tonal' | 'outline' | 'text' | 'danger';
4
+ export type KptButtonSize = 'sm' | 'md' | 'lg';
5
+ export interface KptButtonProps extends ComponentPropsWithRef<'button'>, AsChildProps {
6
+ /** Wariant wizualny. */
7
+ variant?: KptButtonVariant;
8
+ /** Rozmiar. */
9
+ size?: KptButtonSize;
10
+ /** Spinner + zablokowana interakcja; ustawia `aria-busy`. */
11
+ loading?: boolean;
12
+ /** Pełna szerokość kontenera. */
13
+ fullWidth?: boolean;
14
+ /** Propsy wrappera hosta (`.kpt-button-host`). `className` i `ref` idą na `<button>`. */
15
+ hostProps?: KptHostProps<'span'>;
16
+ }
17
+ /**
18
+ * Przycisk Koncept UI. Renderuje natywny `<button>` (semantyka + submit formularza)
19
+ * w hoście `.kpt-button-host` — dwa poziomy DOM, dokładnie jak w porcie Angulara,
20
+ * bo host niesie własne style (`display: inline-flex`, wariant pełnej szerokości).
21
+ *
22
+ * @example
23
+ * <KptButton onClick={save}>Zapisz</KptButton>
24
+ * <KptButton variant="outline" size="sm">Anuluj</KptButton>
25
+ * <KptButton asChild><a href="/docs">Dokumentacja</a></KptButton>
26
+ */
27
+ export declare function KptButton({ variant, size, type, loading, fullWidth, disabled, asChild, hostProps, className, children, ...rest }: Readonly<KptButtonProps>): import("react").JSX.Element;
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { isValidElement } from 'react';
3
+ import { cn } from "../internal/cn.js";
4
+ import { renderAsChild } from "../internal/slot.js";
5
+ /**
6
+ * Przycisk Koncept UI. Renderuje natywny `<button>` (semantyka + submit formularza)
7
+ * w hoście `.kpt-button-host` — dwa poziomy DOM, dokładnie jak w porcie Angulara,
8
+ * bo host niesie własne style (`display: inline-flex`, wariant pełnej szerokości).
9
+ *
10
+ * @example
11
+ * <KptButton onClick={save}>Zapisz</KptButton>
12
+ * <KptButton variant="outline" size="sm">Anuluj</KptButton>
13
+ * <KptButton asChild><a href="/docs">Dokumentacja</a></KptButton>
14
+ */
15
+ export function KptButton({ variant = 'filled', size = 'md', type = 'button', loading = false, fullWidth = false, disabled = false, asChild = false, hostProps, className, children, ...rest }) {
16
+ const { className: hostClassName, ...hostRest } = hostProps ?? {};
17
+ const isDisabled = disabled || loading;
18
+ // Zawartość jest identyczna w obu trybach — w `asChild` podmieniamy tylko element
19
+ // zewnętrzny, nie strukturę środka, żeby SCSS (`__spinner`, `__content`) dalej działał.
20
+ const content = (_jsxs(_Fragment, { children: [loading ? _jsx("span", { className: "kpt-button__spinner", "aria-hidden": "true" }) : null, _jsx("span", { className: "kpt-button__content", children: asChildContent(asChild, children) })] }));
21
+ const buttonProps = {
22
+ className: cn('kpt-button', loading && 'kpt-button--loading', className),
23
+ 'data-variant': variant,
24
+ 'data-size': size,
25
+ // React serializuje atrybuty aria-* z booleana na "true"/"false" — wynikowy DOM
26
+ // ma `aria-busy="true"`, tak jak port Angulara.
27
+ 'aria-busy': loading || undefined,
28
+ ...rest,
29
+ };
30
+ return (_jsx("span", { className: cn('kpt-button-host', fullWidth && 'kpt-button-host--full', hostClassName), ...hostRest, children: asChild && isValidElement(children) ? (renderAsChild(children, { ...buttonProps, ...disabledProps(children, isDisabled) }, content)) : (_jsx("button", { type: type, disabled: isDisabled, ...buttonProps, children: content })) }));
31
+ }
32
+ /**
33
+ * Stan wyłączenia dla podstawionego elementu. Natywny `<button>` dostaje `disabled`
34
+ * (SCSS stylizuje `:disabled`); każdy inny element — `aria-disabled`, bo `disabled`
35
+ * nie ma tam znaczenia. Element bez `:disabled` nie dostanie wyglądu wyłączonego,
36
+ * więc `asChild` + `disabled` to świadomie ograniczone połączenie.
37
+ */
38
+ function disabledProps(child, isDisabled) {
39
+ if (!isDisabled)
40
+ return {};
41
+ const isNativeButton = isValidElement(child) && child.type === 'button';
42
+ return isNativeButton ? { disabled: true } : { 'aria-disabled': true };
43
+ }
44
+ /** W trybie `asChild` treścią są dzieci podstawionego elementu, nie on sam. */
45
+ function asChildContent(asChild, children) {
46
+ if (!asChild || !isValidElement(children))
47
+ return children;
48
+ return children.props.children;
49
+ }
@@ -0,0 +1,25 @@
1
+ import type { ComponentPropsWithRef } from 'react';
2
+ import type { KptHostProps } from '../internal/types.ts';
3
+ import type { KptOption } from '../shared/types.ts';
4
+ export interface KptButtonGroupProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'children' | 'onChange' | 'defaultValue'> {
5
+ options?: KptOption<T>[];
6
+ value?: T;
7
+ defaultValue?: T;
8
+ onValueChange?: (value: T) => void;
9
+ size?: 'sm' | 'md' | 'lg';
10
+ disabled?: boolean;
11
+ /** Wywoływane po wyborze — moment, w którym kontrolka staje się dotknięta. */
12
+ onTouch?: () => void;
13
+ /** Propsy wrappera hosta (`.kpt-button-group-host`). */
14
+ hostProps?: KptHostProps<'span'>;
15
+ }
16
+ /**
17
+ * Segmentowana grupa przycisków (toggle) — pojedynczy wybór z opcji.
18
+ *
19
+ * Działa kontrolowana (`value` + `onValueChange`) i niekontrolowana (`defaultValue`).
20
+ *
21
+ * @example
22
+ * <KptButtonGroup value={view} onValueChange={setView}
23
+ * options={[{ label: 'Lista', value: 'list' }, { label: 'Siatka', value: 'grid' }]} />
24
+ */
25
+ export declare function KptButtonGroup<T>({ options, value, defaultValue, onValueChange, size, disabled, onTouch, hostProps, className, ...rest }: Readonly<KptButtonGroupProps<T>>): import("react").JSX.Element;