@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,155 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useRef, useState } from 'react';
3
+ import { buildRangeGrid, clampVisibleMonths, datePart, formatDate, formatTime, pad, parseTime, pickRangeDay, timePart, toIso, } from '@konce-pt/datetime';
4
+ import { monthNames, weekdayNames } from '@konce-pt/i18n';
5
+ import { cn } from "../internal/cn.js";
6
+ import { useControllableState } from "../internal/use-controllable-state.js";
7
+ import { KptOverlay } from "../internal/overlay.js";
8
+ import { useKptMessages } from "../i18n/i18n-context.js";
9
+ import { useKptFormFieldContext } from "../form-field/form-field-context.js";
10
+ import { KptButton } from "../button/button.js";
11
+ import { KptIcon } from "../icon/icon.js";
12
+ import { KptClock } from "../clock/clock.js";
13
+ const EMPTY_RANGE = { start: '', end: '' };
14
+ /**
15
+ * Wybór zakresu dat (i godzin) z kalendarzem. Pierwszy klik ustawia początek, drugi koniec;
16
+ * `visibleMonths` pokazuje kilka miesięcy obok siebie, a zakres podświetla się ciągłym pasmem
17
+ * przez wszystkie panele.
18
+ *
19
+ * @example
20
+ * <KptDateRange value={period} onValueChange={setPeriod} visibleMonths={2} />
21
+ * <KptDateRange mode="datetime" hourFormat={12} value={span} onValueChange={setSpan} />
22
+ */
23
+ export function KptDateRange({ value, defaultValue = EMPTY_RANGE, onValueChange, mode = 'date', visibleMonths = 1, hourFormat = 24, minuteStep = 1, placeholder, displayFormat = 'pl', disabled = false, invalid = false, touched, onTouch, locale, dictionary, className, ...rest }) {
24
+ const { t, locale: activeLocale } = useKptMessages({ locale, dictionary });
25
+ const field = useKptFormFieldContext();
26
+ const [current, setCurrent] = useControllableState({ value, defaultValue, onChange: onValueChange });
27
+ const [isOpen, setOpen] = useState(false);
28
+ const [anchor, setAnchor] = useState(null);
29
+ const today = useRef(new Date()).current;
30
+ const todayIso = toIso(today);
31
+ const showInvalid = invalid && (touched ?? true);
32
+ const [page, setPage] = useState(() => ({ y: today.getFullYear(), m: today.getMonth() }));
33
+ const [startTime, setStartTime] = useState('');
34
+ const [endTime, setEndTime] = useState('');
35
+ const [timeEnd, setTimeEnd] = useState('start');
36
+ // Część kalendarzowa zakresu w trybie datetime — wartość powstaje dopiero przy „Gotowe".
37
+ const [startDate, setStartDate] = useState('');
38
+ const [endDate, setEndDate] = useState('');
39
+ const months = monthNames(activeLocale);
40
+ const weekdays = weekdayNames(activeLocale);
41
+ const panelCount = mode === 'time' ? 1 : clampVisibleMonths(visibleMonths);
42
+ const multi = panelCount > 1;
43
+ const fmtTime = (hhmm) => {
44
+ const parsed = parseTime(hhmm);
45
+ return parsed ? formatTime(parsed.h, parsed.m, hourFormat) : '';
46
+ };
47
+ const display = (() => {
48
+ const { start, end } = current;
49
+ if (!start)
50
+ return '';
51
+ if (mode === 'time') {
52
+ return end ? `${fmtTime(start)} – ${fmtTime(end)}` : `${fmtTime(start)} – …`;
53
+ }
54
+ if (mode === 'datetime') {
55
+ const one = (v) => {
56
+ const date = formatDate(datePart(v), displayFormat);
57
+ const time = fmtTime(timePart(v));
58
+ return time ? `${date} ${time}` : date;
59
+ };
60
+ return end ? `${one(start)} – ${one(end)}` : `${one(start)} – …`;
61
+ }
62
+ return end
63
+ ? `${formatDate(start, displayFormat)} – ${formatDate(end, displayFormat)}`
64
+ : `${formatDate(start, displayFormat)} – …`;
65
+ })();
66
+ const toggleOpen = () => {
67
+ if (disabled)
68
+ return;
69
+ if (!isOpen) {
70
+ const { start, end } = current;
71
+ const base = datePart(start) || todayIso;
72
+ const [y, m] = [+base.slice(0, 4), +base.slice(5, 7) - 1];
73
+ setPage({ y, m });
74
+ if (mode === 'datetime') {
75
+ setStartDate(datePart(start));
76
+ setEndDate(datePart(end));
77
+ setStartTime(timePart(start) || '00:00');
78
+ setEndTime(timePart(end) || '00:00');
79
+ }
80
+ else if (mode === 'time') {
81
+ setStartTime(/^\d{2}:\d{2}$/.test(start) ? start : '00:00');
82
+ setEndTime(/^\d{2}:\d{2}$/.test(end) ? end : '00:00');
83
+ }
84
+ setTimeEnd('start');
85
+ }
86
+ setOpen(!isOpen);
87
+ };
88
+ const close = () => {
89
+ if (!isOpen)
90
+ return;
91
+ setOpen(false);
92
+ onTouch?.();
93
+ };
94
+ const panels = Array.from({ length: panelCount }, (_, i) => {
95
+ const first = new Date(page.y, page.m + i, 1);
96
+ const py = first.getFullYear();
97
+ const pm = first.getMonth();
98
+ return {
99
+ key: `${py}-${pad(pm + 1)}`,
100
+ label: `${months[pm]} ${py}`,
101
+ weeks: buildRangeGrid({
102
+ page: { y: py, m: pm },
103
+ start: mode === 'datetime' ? startDate : current.start,
104
+ end: mode === 'datetime' ? endDate : current.end,
105
+ todayIso,
106
+ hideOutside: multi,
107
+ }),
108
+ };
109
+ });
110
+ const selectDay = (iso) => {
111
+ if (mode === 'datetime') {
112
+ const next = pickRangeDay({ start: startDate, end: endDate }, iso);
113
+ setStartDate(next.start);
114
+ setEndDate(next.end);
115
+ return; // czas do wyboru → zamknięcie przez „Gotowe"
116
+ }
117
+ const next = pickRangeDay(current, iso);
118
+ setCurrent(next);
119
+ // Panel zostaje otwarty do czasu domknięcia zakresu — inaczej drugi klik nie miałby gdzie paść.
120
+ if (next.end)
121
+ close();
122
+ };
123
+ const commit = () => {
124
+ if (mode === 'time') {
125
+ const s = startTime || '00:00';
126
+ const e = endTime || '00:00';
127
+ setCurrent(s <= e ? { start: s, end: e } : { start: e, end: s });
128
+ }
129
+ else if (mode === 'datetime') {
130
+ if (!startDate) {
131
+ close();
132
+ return;
133
+ }
134
+ const start = `${startDate}T${startTime || '00:00'}`;
135
+ const end = `${endDate || startDate}T${endTime || '00:00'}`;
136
+ setCurrent(start <= end ? { start, end } : { start: end, end: start });
137
+ }
138
+ close();
139
+ };
140
+ const clear = (event) => {
141
+ event.stopPropagation();
142
+ setCurrent(EMPTY_RANGE);
143
+ setStartDate('');
144
+ setEndDate('');
145
+ };
146
+ const clockValue = timeEnd === 'start' ? startTime : endTime;
147
+ const setClockValue = timeEnd === 'start' ? setStartTime : setEndTime;
148
+ return (_jsxs("div", { className: cn('kpt-date-range-host', className), ...rest, children: [_jsxs("div", { ref: setAnchor, className: cn('kpt-date-range', isOpen && 'kpt-date-range--open', disabled && 'kpt-date-range--disabled', showInvalid && 'kpt-date-range--invalid'), role: "button", tabIndex: 0, id: field?.controlId, "aria-disabled": disabled || undefined, onClick: toggleOpen, onKeyDown: (event) => {
149
+ if (event.key === 'Enter')
150
+ toggleOpen();
151
+ }, onBlur: () => onTouch?.(), children: [display ? (_jsx("span", { className: "kpt-date-range__value", children: display })) : (_jsx("span", { className: "kpt-date-range__placeholder", children: placeholder ?? t('dateRange.placeholder') })), _jsx("span", { className: "kpt-date-range__spacer" }), display ? (_jsx("button", { type: "button", className: "kpt-date-range__clear", onClick: clear, "aria-label": t('common.clear'), children: _jsx(KptIcon, { name: "x" }) })) : null, _jsx(KptIcon, { className: "kpt-date-range__icon", name: "calendar" })] }), _jsx(KptOverlay, { open: isOpen, anchor: anchor, onClose: close, offset: 4, children: _jsx("div", { className: cn('kpt-calendar', mode === 'time' && 'kpt-calendar--clock', mode !== 'time' && 'kpt-calendar--range', multi && mode !== 'time' && 'kpt-calendar--multi'), children: mode === 'time' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "kpt-range-tabs", children: [_jsx("button", { type: "button", className: cn(timeEnd === 'start' && 'is-active'), onClick: () => setTimeEnd('start'), children: t('dateRange.from') }), _jsx("button", { type: "button", className: cn(timeEnd === 'end' && 'is-active'), onClick: () => setTimeEnd('end'), children: t('dateRange.to') })] }), _jsx(KptClock
152
+ // Klucz na edytowanym końcu wymusza remount przy przełączeniu Od/Koniec — tarcza ma
153
+ // wtedy wrócić do etapu godzin, tak jak dwie osobne instancje w porcie Angulara.
154
+ , { value: clockValue, onValueChange: setClockValue, hourFormat: hourFormat, minuteStep: minuteStep }, timeEnd), _jsx("div", { className: "kpt-calendar__footer", children: _jsx(KptButton, { size: "sm", onClick: commit, children: t('common.done') }) })] })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "kpt-calendar__panels", children: panels.map((panel, index) => (_jsxs("div", { className: "kpt-calendar__panel", children: [_jsxs("div", { className: "kpt-calendar__header", children: [index === 0 ? (_jsx("button", { type: "button", className: "kpt-calendar__nav", onClick: () => setPage(({ y, m }) => (m === 0 ? { y: y - 1, m: 11 } : { y, m: m - 1 })), "aria-label": t('datepicker.prevMonth'), children: _jsx(KptIcon, { name: "chevron-left" }) })) : (_jsx("span", { className: "kpt-calendar__nav-slot" })), _jsx("span", { className: "kpt-calendar__title", children: panel.label }), index === panels.length - 1 ? (_jsx("button", { type: "button", className: "kpt-calendar__nav", onClick: () => setPage(({ y, m }) => (m === 11 ? { y: y + 1, m: 0 } : { y, m: m + 1 })), "aria-label": t('datepicker.nextMonth'), children: _jsx(KptIcon, { name: "chevron-right" }) })) : (_jsx("span", { className: "kpt-calendar__nav-slot" }))] }), _jsx("div", { className: "kpt-calendar__weekdays", children: weekdays.map((weekday) => (_jsx("span", { className: "kpt-calendar__weekday", children: weekday }, weekday))) }), _jsx("div", { className: "kpt-calendar__grid", children: panel.weeks.flat().map((cell) => cell.blank ? (_jsx("span", { className: "kpt-calendar__day kpt-calendar__day--blank" }, cell.iso)) : (_jsx("button", { type: "button", className: cn('kpt-calendar__day', !cell.inMonth && 'kpt-calendar__day--muted', cell.today && 'kpt-calendar__day--today', cell.inRange && 'kpt-calendar__day--range', cell.rangeStart && 'kpt-calendar__day--range-start', cell.rangeEnd && 'kpt-calendar__day--range-end', (cell.isStart || cell.isEnd) && 'kpt-calendar__day--selected'), onClick: () => selectDay(cell.iso), children: cell.day }, cell.iso))) })] }, panel.key))) }), mode === 'datetime' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "kpt-range-tabs", children: [_jsx("button", { type: "button", className: cn(timeEnd === 'start' && 'is-active'), onClick: () => setTimeEnd('start'), children: t('dateRange.fromTime') }), _jsx("button", { type: "button", className: cn(timeEnd === 'end' && 'is-active'), onClick: () => setTimeEnd('end'), children: t('dateRange.toTime') })] }), _jsx(KptClock, { value: clockValue, onValueChange: setClockValue, hourFormat: hourFormat, minuteStep: minuteStep }, timeEnd), _jsx("div", { className: "kpt-calendar__footer", children: _jsx(KptButton, { size: "sm", onClick: commit, children: t('common.done') }) })] })) : null] })) }) })] }));
155
+ }
@@ -0,0 +1,39 @@
1
+ import { type ComponentPropsWithRef } from 'react';
2
+ import { type KptCalendarView, type KptDateSelectionMode } from '@konce-pt/datetime';
3
+ import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
4
+ export type { KptDateSelectionMode as KptDatepickerMode, KptCalendarView as KptDatepickerView };
5
+ export interface KptDatepickerProps extends Omit<ComponentPropsWithRef<'div'>, 'onChange' | 'defaultValue'>, KptI18nOverrides {
6
+ value?: string;
7
+ defaultValue?: string;
8
+ onValueChange?: (value: string) => void;
9
+ /** Granulacja wyboru; od niej zależy format wartości. */
10
+ selectionMode?: KptDateSelectionMode;
11
+ /** Poziom, od którego otwiera się panel; `null` = poziom zatwierdzania danego trybu. */
12
+ startView?: KptCalendarView | null;
13
+ minDate?: string | null;
14
+ maxDate?: string | null;
15
+ placeholder?: string;
16
+ /** Prezentacja miesiąca w trybie 'month' i w etykietach: słownie czy liczbą. */
17
+ monthDisplay?: 'long' | 'number';
18
+ displayFormat?: 'pl' | 'iso';
19
+ hourFormat?: 12 | 24;
20
+ minuteStep?: number;
21
+ /** Stan panelu — pozwala otworzyć kalendarz z zewnątrz i wiedzieć, kiedy wybór się skończył. */
22
+ open?: boolean;
23
+ onOpenChange?: (open: boolean) => void;
24
+ /** Nazwa dostępna, gdy pola nie opisuje `<label for>`. */
25
+ ariaLabel?: string | null;
26
+ disabled?: boolean;
27
+ invalid?: boolean;
28
+ touched?: boolean;
29
+ onTouch?: () => void;
30
+ }
31
+ /**
32
+ * Wybór daty i godziny z własnym kalendarzem. Panel jest stosem widoków
33
+ * dekady → lata → miesiące → dni: klik w komórkę schodzi poziom niżej, klik w tytuł wchodzi wyżej.
34
+ *
35
+ * @example
36
+ * <KptDatepicker value={date} onValueChange={setDate} startView="decade" minDate="1920-01-01" />
37
+ * <KptDatepicker selectionMode="datetime" hourFormat={12} value={at} onValueChange={setAt} />
38
+ */
39
+ export declare function KptDatepicker({ value, defaultValue, onValueChange, selectionMode, startView, minDate, maxDate, placeholder, monthDisplay, displayFormat, hourFormat, minuteStep, open, onOpenChange, ariaLabel, disabled, invalid, touched, onTouch, locale, dictionary, className, ...rest }: Readonly<KptDatepickerProps>): import("react").JSX.Element;
@@ -0,0 +1,202 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from 'react';
3
+ import { KPT_CALENDAR_LEVELS, buildDayGrid, buildDecadeCells, buildMonthCells, buildYearCells, canPageBack, canPageForward, centuryStart, commitLevelFor, decadeStart, formatDate, formatTime, initialPage, pad, pageBy, parseDateTime, parseTime, resolveStartView, selectedYearMonth, toIso, } from '@konce-pt/datetime';
4
+ import { monthNames, weekdayNames } from '@konce-pt/i18n';
5
+ import { cn } from "../internal/cn.js";
6
+ import { useControllableState } from "../internal/use-controllable-state.js";
7
+ import { KptOverlay } from "../internal/overlay.js";
8
+ import { useKptMessages } from "../i18n/i18n-context.js";
9
+ import { useKptFormFieldContext } from "../form-field/form-field-context.js";
10
+ import { KptButton } from "../button/button.js";
11
+ import { KptIcon } from "../icon/icon.js";
12
+ import { KptClock } from "../clock/clock.js";
13
+ /**
14
+ * Wybór daty i godziny z własnym kalendarzem. Panel jest stosem widoków
15
+ * dekady → lata → miesiące → dni: klik w komórkę schodzi poziom niżej, klik w tytuł wchodzi wyżej.
16
+ *
17
+ * @example
18
+ * <KptDatepicker value={date} onValueChange={setDate} startView="decade" minDate="1920-01-01" />
19
+ * <KptDatepicker selectionMode="datetime" hourFormat={12} value={at} onValueChange={setAt} />
20
+ */
21
+ export function KptDatepicker({ value, defaultValue = '', onValueChange, selectionMode = 'date', startView = null, minDate = null, maxDate = null, placeholder = '', monthDisplay = 'long', displayFormat = 'pl', hourFormat = 24, minuteStep = 1, open, onOpenChange, ariaLabel = null, disabled = false, invalid = false, touched, onTouch, locale, dictionary, className, ...rest }) {
22
+ const { t, locale: activeLocale } = useKptMessages({ locale, dictionary });
23
+ const field = useKptFormFieldContext();
24
+ const [current, setCurrent] = useControllableState({ value, defaultValue, onChange: onValueChange });
25
+ const [isOpen, setOpen] = useControllableState({ value: open, defaultValue: false, onChange: onOpenChange });
26
+ const [anchor, setAnchor] = useState(null);
27
+ const triggerRef = useRef(null);
28
+ const today = useRef(new Date()).current;
29
+ const todayIso = toIso(today);
30
+ const showInvalid = invalid && (touched ?? true);
31
+ const [page, setPage] = useState(() => initialPage(current, selectionMode, today));
32
+ const [panelView, setPanelView] = useState(() => resolveStartView(selectionMode, startView));
33
+ const [timeStr, setTimeStr] = useState('');
34
+ const [pendingDate, setPendingDate] = useState('');
35
+ const [dtView, setDtView] = useState('calendar');
36
+ const months = monthNames(activeLocale);
37
+ const weekdays = weekdayNames(activeLocale);
38
+ const commitLevel = commitLevelFor(selectionMode);
39
+ // Panel można otworzyć także z zewnątrz (`open`), nie tylko klikiem w trigger — widok
40
+ // i bufor czasu ustawiamy więc przy każdym otwarciu, a nie w obsłudze kliknięcia.
41
+ useEffect(() => {
42
+ if (!isOpen)
43
+ return;
44
+ setPage(initialPage(current, selectionMode, today));
45
+ setPanelView(resolveStartView(selectionMode, startView));
46
+ setDtView(selectionMode === 'time' ? 'clock' : 'calendar');
47
+ if (selectionMode === 'time') {
48
+ const parsed = parseTime(current);
49
+ setTimeStr(parsed ? `${pad(parsed.h)}:${pad(parsed.m)}` : '00:00');
50
+ }
51
+ else if (selectionMode === 'datetime') {
52
+ const parsed = parseDateTime(current);
53
+ setTimeStr(parsed ? `${pad(parsed.h)}:${pad(parsed.m)}` : '00:00');
54
+ setPendingDate(parsed?.date ?? '');
55
+ }
56
+ // Zależność wyłącznie od `isOpen`: to otwarcie ma ustawić stan początkowy, a późniejsze
57
+ // zmiany wartości w otwartym panelu nie mogą go zresetować pod palcami użytkownika.
58
+ // eslint-disable-next-line react-hooks/exhaustive-deps
59
+ }, [isOpen]);
60
+ const showClock = selectionMode === 'time' || (selectionMode === 'datetime' && dtView === 'clock');
61
+ const hasFooter = selectionMode === 'time' || selectionMode === 'datetime';
62
+ const hasNav = selectionMode !== 'month';
63
+ const close = () => {
64
+ if (!isOpen)
65
+ return;
66
+ setOpen(false);
67
+ onTouch?.();
68
+ };
69
+ // ── Prezentacja w triggerze ──
70
+ const effectivePlaceholder = (() => {
71
+ if (placeholder)
72
+ return placeholder;
73
+ const iso = displayFormat === 'iso';
74
+ const time = hourFormat === 12 ? '--:-- --' : '--:--';
75
+ switch (selectionMode) {
76
+ case 'monthYear':
77
+ return iso ? 'RRRR-MM' : 'MM.RRRR';
78
+ case 'year':
79
+ return 'RRRR';
80
+ case 'month':
81
+ return monthDisplay === 'number' ? 'MM' : t('datepicker.month');
82
+ case 'time':
83
+ return time;
84
+ case 'datetime':
85
+ return `${iso ? 'RRRR-MM-DD' : 'DD.MM.RRRR'} ${time}`;
86
+ default:
87
+ return iso ? 'RRRR-MM-DD' : 'DD.MM.RRRR';
88
+ }
89
+ })();
90
+ /** Wartość niepasująca do formatu trybu daje pusty tekst — trigger nie pokazuje śmieci. */
91
+ const display = (() => {
92
+ if (!current)
93
+ return '';
94
+ const iso = displayFormat === 'iso';
95
+ switch (selectionMode) {
96
+ case 'monthYear': {
97
+ const match = /^(\d{4})-(\d{2})$/.exec(current);
98
+ if (!match)
99
+ return '';
100
+ return iso ? current : `${match[2]}.${match[1]}`;
101
+ }
102
+ case 'year':
103
+ return /^\d{4}$/.test(current) ? current : '';
104
+ case 'month': {
105
+ const match = /^(\d{2})$/.exec(current);
106
+ if (!match)
107
+ return '';
108
+ return monthDisplay === 'number' ? match[1] : (months[+match[1] - 1] ?? '');
109
+ }
110
+ case 'time': {
111
+ const parsed = parseTime(current);
112
+ return parsed ? formatTime(parsed.h, parsed.m, hourFormat) : '';
113
+ }
114
+ case 'datetime': {
115
+ const parsed = parseDateTime(current);
116
+ if (!parsed)
117
+ return '';
118
+ const sep = iso ? 'T' : ' ';
119
+ return `${formatDate(parsed.date, displayFormat)}${sep}${formatTime(parsed.h, parsed.m, hourFormat)}`;
120
+ }
121
+ default:
122
+ return formatDate(current, displayFormat);
123
+ }
124
+ })();
125
+ // ── Siatki ──
126
+ const selectedDateIso = selectionMode === 'datetime' ? pendingDate : current;
127
+ const weeks = buildDayGrid({ page, selectedIso: selectedDateIso, todayIso, minDate, maxDate });
128
+ const selectedYM = selectedYearMonth(selectionMode === 'datetime' ? pendingDate : current, selectionMode);
129
+ const levelCells = panelView === 'year'
130
+ ? buildYearCells({ year: page.y, selected: selectedYM, today, minDate, maxDate })
131
+ : panelView === 'decade'
132
+ ? buildDecadeCells({ year: page.y, selected: selectedYM, today, minDate, maxDate })
133
+ : buildMonthCells({
134
+ year: page.y,
135
+ monthNames: months,
136
+ selected: selectedYM,
137
+ today,
138
+ monthOnly: selectionMode === 'month',
139
+ numeric: monthDisplay === 'number',
140
+ minDate,
141
+ maxDate,
142
+ });
143
+ const navLabel = (dir) => {
144
+ switch (panelView) {
145
+ case 'month':
146
+ return t(dir === 'prev' ? 'datepicker.prevYear' : 'datepicker.nextYear');
147
+ case 'year':
148
+ return t(dir === 'prev' ? 'datepicker.prevYears' : 'datepicker.nextYears');
149
+ case 'decade':
150
+ return t(dir === 'prev' ? 'datepicker.prevDecades' : 'datepicker.nextDecades');
151
+ default:
152
+ return t(dir === 'prev' ? 'datepicker.prevMonth' : 'datepicker.nextMonth');
153
+ }
154
+ };
155
+ // ── Wybór ──
156
+ const selectDay = (iso, cellDisabled) => {
157
+ if (cellDisabled)
158
+ return;
159
+ if (selectionMode === 'datetime') {
160
+ setPendingDate(iso); // czas jeszcze do wyboru — zamyka dopiero „Gotowe"
161
+ return;
162
+ }
163
+ setCurrent(iso);
164
+ close();
165
+ };
166
+ const selectCell = (cell) => {
167
+ if (cell.disabled)
168
+ return;
169
+ if (panelView === commitLevel) {
170
+ const mm = pad(cell.m + 1);
171
+ setCurrent(panelView === 'year' ? String(cell.y) : selectionMode === 'month' ? mm : `${cell.y}-${mm}`);
172
+ close();
173
+ return;
174
+ }
175
+ setPage(({ m }) => ({ y: cell.y, m: panelView === 'month' ? cell.m : m }));
176
+ setPanelView(KPT_CALENDAR_LEVELS[KPT_CALENDAR_LEVELS.indexOf(panelView) - 1]);
177
+ };
178
+ const commit = () => {
179
+ const time = timeStr || '00:00';
180
+ if (selectionMode === 'time') {
181
+ setCurrent(time);
182
+ }
183
+ else {
184
+ const parsed = parseDateTime(current);
185
+ setCurrent(`${pendingDate || parsed?.date || todayIso}T${time}`);
186
+ }
187
+ close();
188
+ };
189
+ const clear = (event) => {
190
+ event.stopPropagation();
191
+ setCurrent('');
192
+ setPendingDate('');
193
+ };
194
+ const timeParts = parseTime(timeStr);
195
+ return (_jsxs("div", { className: cn('kpt-datepicker-host', className), ...rest, children: [_jsxs("div", { ref: (el) => {
196
+ triggerRef.current = el;
197
+ setAnchor(el);
198
+ }, className: cn('kpt-datepicker', isOpen && 'kpt-datepicker--open', disabled && 'kpt-datepicker--disabled', showInvalid && 'kpt-datepicker--invalid'), role: "button", tabIndex: 0, id: field?.controlId, "aria-disabled": disabled || undefined, "aria-label": ariaLabel ?? undefined, onClick: () => !disabled && setOpen(!isOpen), onKeyDown: (event) => {
199
+ if (event.key === 'Enter' && !disabled)
200
+ setOpen(!isOpen);
201
+ }, onBlur: () => onTouch?.(), children: [display ? (_jsx("span", { className: "kpt-datepicker__value", children: display })) : (_jsx("span", { className: "kpt-datepicker__placeholder", children: effectivePlaceholder })), _jsx("span", { className: "kpt-datepicker__spacer" }), display ? (_jsx("button", { type: "button", className: "kpt-datepicker__clear", onClick: clear, "aria-label": t('common.clear'), children: _jsx(KptIcon, { name: "x" }) })) : null, _jsx(KptIcon, { className: "kpt-datepicker__icon", name: selectionMode === 'time' ? 'clock' : 'calendar' })] }), _jsx(KptOverlay, { open: isOpen, anchor: anchor, onClose: close, offset: 4, children: _jsxs("div", { className: cn('kpt-calendar', showClock && 'kpt-calendar--clock'), children: [showClock ? (_jsxs(_Fragment, { children: [selectionMode === 'datetime' ? (_jsxs("div", { className: "kpt-calendar__header", children: [_jsx("button", { type: "button", className: "kpt-calendar__nav", onClick: () => setDtView('calendar'), "aria-label": t('datepicker.calendar'), children: _jsx(KptIcon, { name: "chevron-left" }) }), _jsx("span", { className: "kpt-calendar__title", children: t('datepicker.time') })] })) : null, _jsx(KptClock, { value: timeStr, onValueChange: setTimeStr, hourFormat: hourFormat, minuteStep: minuteStep })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "kpt-calendar__header", children: [hasNav ? (_jsx("button", { type: "button", className: "kpt-calendar__nav", disabled: !canPageBack(page, panelView, minDate), onClick: () => setPage(pageBy(page, panelView, -1)), "aria-label": navLabel('prev'), children: _jsx(KptIcon, { name: "chevron-left" }) })) : null, _jsxs("span", { className: "kpt-calendar__titles", children: [panelView === 'day' ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "kpt-calendar__title-btn", onClick: () => setPanelView('month'), "aria-label": t('datepicker.chooseMonth'), children: months[page.m] }), _jsx("button", { type: "button", className: "kpt-calendar__title-btn", onClick: () => setPanelView('year'), "aria-label": t('datepicker.chooseYear'), children: page.y })] })) : null, panelView === 'month' ? (selectionMode === 'month' ? (_jsx("span", { className: "kpt-calendar__title", children: t('datepicker.month') })) : (_jsx("button", { type: "button", className: "kpt-calendar__title-btn", onClick: () => setPanelView('year'), "aria-label": t('datepicker.chooseYear'), children: page.y }))) : null, panelView === 'year' ? (_jsx("button", { type: "button", className: "kpt-calendar__title-btn", onClick: () => setPanelView('decade'), "aria-label": t('datepicker.chooseDecade'), children: `${decadeStart(page.y)} – ${decadeStart(page.y) + 9}` })) : null, panelView === 'decade' ? (_jsx("span", { className: "kpt-calendar__title", children: `${centuryStart(page.y)} – ${centuryStart(page.y) + 119}` })) : null] }), hasNav ? (_jsx("button", { type: "button", className: "kpt-calendar__nav", disabled: !canPageForward(page, panelView, maxDate), onClick: () => setPage(pageBy(page, panelView, 1)), "aria-label": navLabel('next'), children: _jsx(KptIcon, { name: "chevron-right" }) })) : null] }), panelView === 'day' ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "kpt-calendar__weekdays", children: weekdays.map((weekday) => (_jsx("span", { className: "kpt-calendar__weekday", children: weekday }, weekday))) }), _jsx("div", { className: "kpt-calendar__grid", children: weeks.flat().map((cell) => (_jsx("button", { type: "button", className: cn('kpt-calendar__day', !cell.inMonth && 'kpt-calendar__day--muted', cell.today && 'kpt-calendar__day--today', cell.selected && 'kpt-calendar__day--selected'), disabled: cell.disabled, "aria-pressed": cell.selected, onClick: () => selectDay(cell.iso, cell.disabled), children: cell.day }, cell.iso))) })] })) : (_jsx("div", { className: cn('kpt-calendar__grid kpt-calendar__grid--cells', panelView === 'decade' && 'kpt-calendar__grid--decades'), children: levelCells.map((cell) => (_jsx("button", { type: "button", className: cn('kpt-calendar__cell', cell.selected && 'kpt-calendar__cell--selected', cell.current && 'kpt-calendar__cell--current'), disabled: cell.disabled, "aria-pressed": cell.selected, onClick: () => selectCell(cell), children: cell.label }, cell.label))) })), selectionMode === 'datetime' ? (_jsxs("button", { type: "button", className: "kpt-calendar__timebar", onClick: () => setDtView('clock'), children: [_jsx(KptIcon, { name: "clock" }), _jsx("span", { children: formatTime(timeParts?.h ?? 0, timeParts?.m ?? 0, hourFormat) })] })) : null] })), hasFooter ? (_jsx("div", { className: "kpt-calendar__footer", children: _jsx(KptButton, { size: "sm", onClick: commit, children: t('common.done') }) })) : null] }) })] }));
202
+ }
@@ -0,0 +1,35 @@
1
+ import { type ComponentPropsWithRef, type ReactNode } from 'react';
2
+ import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
3
+ export interface KptDialogProps extends Omit<ComponentPropsWithRef<'dialog'>, 'open' | 'onClose'>, KptI18nOverrides {
4
+ open: boolean;
5
+ onOpenChange: (open: boolean) => void;
6
+ header?: string;
7
+ /** Pozwala zamknąć klawiszem Esc i przyciskiem ×. */
8
+ dismissible?: boolean;
9
+ children?: ReactNode;
10
+ }
11
+ /**
12
+ * Okno modalne na natywnym `<dialog>` — pułapka fokusu, Escape i backdrop pochodzą z platformy,
13
+ * nie z naszego kodu. Stopkę wstaw przez `<KptDialog.Footer>`.
14
+ *
15
+ * @example
16
+ * <KptDialog open={open} onOpenChange={setOpen} header="Usunąć?">
17
+ * Tej operacji nie można cofnąć.
18
+ * <KptDialog.Footer>
19
+ * <KptButton variant="text" onClick={() => setOpen(false)}>Anuluj</KptButton>
20
+ * <KptButton variant="danger" onClick={remove}>Usuń</KptButton>
21
+ * </KptDialog.Footer>
22
+ * </KptDialog>
23
+ */
24
+ export declare function KptDialog({ open, onOpenChange, header, dismissible, locale, dictionary, className, children, ref, ...rest }: Readonly<KptDialogProps>): import("react").JSX.Element;
25
+ export declare namespace KptDialog {
26
+ var Footer: typeof KptDialogFooter;
27
+ }
28
+ /**
29
+ * Stopka okna. Nosi atrybut `kptdialogfooter`, w który celuje wspólny SCSS (nazwy atrybutów
30
+ * w selektorach CSS są w HTML case-insensitive, więc jedna reguła obsługuje oba porty).
31
+ */
32
+ export declare function KptDialogFooter({ className, ...rest }: Readonly<ComponentPropsWithRef<'div'>>): import("react").JSX.Element;
33
+ export declare namespace KptDialogFooter {
34
+ var kptDialogSlot: "footer";
35
+ }
@@ -0,0 +1,59 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Children, isValidElement, useEffect, useRef, useState, } from 'react';
3
+ import { cn } from "../internal/cn.js";
4
+ import { composeRefs } from "../internal/compose-refs.js";
5
+ import { KptOverlayContainerContext } from "../internal/overlay-container.js";
6
+ import { useKptMessages } from "../i18n/i18n-context.js";
7
+ import { KptIcon } from "../icon/icon.js";
8
+ /**
9
+ * Okno modalne na natywnym `<dialog>` — pułapka fokusu, Escape i backdrop pochodzą z platformy,
10
+ * nie z naszego kodu. Stopkę wstaw przez `<KptDialog.Footer>`.
11
+ *
12
+ * @example
13
+ * <KptDialog open={open} onOpenChange={setOpen} header="Usunąć?">
14
+ * Tej operacji nie można cofnąć.
15
+ * <KptDialog.Footer>
16
+ * <KptButton variant="text" onClick={() => setOpen(false)}>Anuluj</KptButton>
17
+ * <KptButton variant="danger" onClick={remove}>Usuń</KptButton>
18
+ * </KptDialog.Footer>
19
+ * </KptDialog>
20
+ */
21
+ export function KptDialog({ open, onOpenChange, header = '', dismissible = true, locale, dictionary, className, children, ref, ...rest }) {
22
+ const { t } = useKptMessages({ locale, dictionary });
23
+ const dialogRef = useRef(null);
24
+ // Element trafia do kontekstu, więc musi wywołać render — sam `ref` by nie wystarczył.
25
+ const [dialogEl, setDialogEl] = useState(null);
26
+ useEffect(() => {
27
+ const el = dialogRef.current;
28
+ if (!el)
29
+ return;
30
+ if (open && !el.open)
31
+ el.showModal();
32
+ else if (!open && el.open)
33
+ el.close();
34
+ }, [open]);
35
+ const footers = [];
36
+ const body = [];
37
+ Children.forEach(children, (child) => {
38
+ const isFooter = isValidElement(child) && child.type?.kptDialogSlot === 'footer';
39
+ (isFooter ? footers : body).push(child);
40
+ });
41
+ const onCancel = (event) => {
42
+ // Esc idzie przez `cancel`; blokujemy go, gdy okna nie wolno zamknąć.
43
+ if (!dismissible) {
44
+ event.preventDefault();
45
+ return;
46
+ }
47
+ onOpenChange(false);
48
+ };
49
+ return (_jsx(KptOverlayContainerContext.Provider, { value: open ? dialogEl : null, children: _jsxs("dialog", { ref: composeRefs(dialogRef, setDialogEl, ref), className: cn('kpt-dialog', className), onClose: () => onOpenChange(false), onCancel: onCancel, ...rest, children: [header ? (_jsxs("div", { className: "kpt-dialog__header", children: [_jsx("h2", { className: "kpt-dialog__title", children: header }), dismissible ? (_jsx("button", { className: "kpt-dialog__close", type: "button", onClick: () => onOpenChange(false), "aria-label": t('common.close'), children: _jsx(KptIcon, { name: "x" }) })) : null] })) : null, _jsx("div", { className: "kpt-dialog__body", children: body }), footers] }) }));
50
+ }
51
+ /**
52
+ * Stopka okna. Nosi atrybut `kptdialogfooter`, w który celuje wspólny SCSS (nazwy atrybutów
53
+ * w selektorach CSS są w HTML case-insensitive, więc jedna reguła obsługuje oba porty).
54
+ */
55
+ export function KptDialogFooter({ className, ...rest }) {
56
+ return _jsx("div", { kptdialogfooter: '', className: className, ...rest });
57
+ }
58
+ KptDialogFooter.kptDialogSlot = 'footer';
59
+ KptDialog.Footer = KptDialogFooter;
@@ -0,0 +1,16 @@
1
+ import type { ComponentPropsWithRef } from 'react';
2
+ export type KptDividerOrientation = 'horizontal' | 'vertical';
3
+ export interface KptDividerProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
4
+ orientation?: KptDividerOrientation;
5
+ /** Wcięcie (odstęp od krawędzi kontenera). */
6
+ inset?: boolean;
7
+ }
8
+ /**
9
+ * Separator (linia pozioma lub pionowa). Kolor z `--kpt-color-border`.
10
+ *
11
+ * @example
12
+ * <KptDivider />
13
+ * <KptDivider orientation="vertical" />
14
+ * <KptDivider inset />
15
+ */
16
+ export declare function KptDivider({ orientation, inset, className, ...rest }: Readonly<KptDividerProps>): import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../internal/cn.js";
3
+ /**
4
+ * Separator (linia pozioma lub pionowa). Kolor z `--kpt-color-border`.
5
+ *
6
+ * @example
7
+ * <KptDivider />
8
+ * <KptDivider orientation="vertical" />
9
+ * <KptDivider inset />
10
+ */
11
+ export function KptDivider({ orientation = 'horizontal', inset = false, className, ...rest }) {
12
+ return (_jsx("div", { className: cn('kpt-divider', inset && 'kpt-divider--inset', className), role: "separator", "aria-orientation": orientation, "data-orientation": orientation, ...rest }));
13
+ }
@@ -0,0 +1,25 @@
1
+ import { type ComponentPropsWithRef, type ReactNode } from 'react';
2
+ import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
3
+ export type KptDrawerPosition = 'left' | 'right' | 'top' | 'bottom';
4
+ export interface KptDrawerProps extends Omit<ComponentPropsWithRef<'div'>, 'onChange'>, KptI18nOverrides {
5
+ open: boolean;
6
+ onOpenChange: (open: boolean) => void;
7
+ position?: KptDrawerPosition;
8
+ header?: string;
9
+ /** Pokazuje przycisk × i pozwala zamknąć klawiszem Escape. */
10
+ dismissible?: boolean;
11
+ closeOnBackdrop?: boolean;
12
+ children?: ReactNode;
13
+ }
14
+ /**
15
+ * Panel wysuwany (drawer / off-canvas), niezależny od app-shell.
16
+ *
17
+ * Panel i backdrop są zawsze w DOM, a o widoczności decyduje klasa `is-open` — dzięki temu
18
+ * wysunięcie i schowanie da się animować, tak samo jak w porcie Angulara.
19
+ *
20
+ * @example
21
+ * <KptDrawer open={open} onOpenChange={setOpen} position="right" header="Filtry">
22
+ * Treść panelu…
23
+ * </KptDrawer>
24
+ */
25
+ export declare function KptDrawer({ open, onOpenChange, position, header, dismissible, closeOnBackdrop, locale, dictionary, className, children, ...rest }: Readonly<KptDrawerProps>): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback } from 'react';
3
+ import { cn } from "../internal/cn.js";
4
+ import { useKptMessages } from "../i18n/i18n-context.js";
5
+ import { KptIcon } from "../icon/icon.js";
6
+ /**
7
+ * Panel wysuwany (drawer / off-canvas), niezależny od app-shell.
8
+ *
9
+ * Panel i backdrop są zawsze w DOM, a o widoczności decyduje klasa `is-open` — dzięki temu
10
+ * wysunięcie i schowanie da się animować, tak samo jak w porcie Angulara.
11
+ *
12
+ * @example
13
+ * <KptDrawer open={open} onOpenChange={setOpen} position="right" header="Filtry">
14
+ * Treść panelu…
15
+ * </KptDrawer>
16
+ */
17
+ export function KptDrawer({ open, onOpenChange, position = 'left', header = '', dismissible = true, closeOnBackdrop = true, locale, dictionary, className, children, ...rest }) {
18
+ const { t } = useKptMessages({ locale, dictionary });
19
+ const close = useCallback(() => onOpenChange(false), [onOpenChange]);
20
+ const onKeyDown = (event) => {
21
+ if (event.key === 'Escape' && dismissible)
22
+ close();
23
+ };
24
+ return (_jsxs("div", { className: cn('kpt-drawer-host', className), ...rest, children: [_jsx("div", { className: cn('kpt-drawer__backdrop', open && 'is-open'), onClick: () => closeOnBackdrop && close() }), _jsxs("aside", { className: cn('kpt-drawer__panel', open && 'is-open'), "data-position": position, role: "dialog", "aria-modal": "true", tabIndex: -1, onKeyDown: onKeyDown, children: [header || dismissible ? (_jsxs("div", { className: "kpt-drawer__header", children: [_jsx("span", { className: "kpt-drawer__title", children: header }), dismissible ? (_jsx("button", { type: "button", className: "kpt-drawer__close", onClick: close, "aria-label": t('common.close'), children: _jsx(KptIcon, { name: "x" }) })) : null] })) : null, _jsx("div", { className: "kpt-drawer__body", children: children })] })] }));
25
+ }
@@ -0,0 +1,18 @@
1
+ import type { ComponentPropsWithRef, ReactNode } from 'react';
2
+ export interface KptEmptyProps extends Omit<ComponentPropsWithRef<'div'>, 'title'> {
3
+ /** Nazwa ikony z rejestru; `null` chowa ikonę. */
4
+ icon?: string | null;
5
+ title?: string;
6
+ description?: string;
7
+ /** Akcje pod opisem. */
8
+ children?: ReactNode;
9
+ }
10
+ /**
11
+ * Stan pusty (empty state) — ikona, tytuł, opis i opcjonalne akcje.
12
+ *
13
+ * @example
14
+ * <KptEmpty icon="search" title="Brak wyników" description="Zmień kryteria wyszukiwania.">
15
+ * <KptButton>Wyczyść filtry</KptButton>
16
+ * </KptEmpty>
17
+ */
18
+ export declare function KptEmpty({ icon, title, description, className, children, ...rest }: Readonly<KptEmptyProps>): 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
+ import { KptIcon } from "../icon/icon.js";
4
+ /**
5
+ * Stan pusty (empty state) — ikona, tytuł, opis i opcjonalne akcje.
6
+ *
7
+ * @example
8
+ * <KptEmpty icon="search" title="Brak wyników" description="Zmień kryteria wyszukiwania.">
9
+ * <KptButton>Wyczyść filtry</KptButton>
10
+ * </KptEmpty>
11
+ */
12
+ export function KptEmpty({ icon = 'info-circle', title = '', description = '', className, children, ...rest }) {
13
+ return (_jsx("div", { className: cn('kpt-empty-host', className), ...rest, children: _jsxs("div", { className: "kpt-empty", children: [icon ? (_jsx("div", { className: "kpt-empty__icon", children: _jsx(KptIcon, { name: icon }) })) : null, title ? _jsx("p", { className: "kpt-empty__title", children: title }) : null, description ? _jsx("p", { className: "kpt-empty__desc", children: description }) : null, _jsx("div", { className: "kpt-empty__actions", children: children })] }) }));
14
+ }