@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,271 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
3
+ import { buildRows, buildTimeline, linkGeometry, resolveLinks, resolveRange, sliceRows, } from '@konce-pt/roadmap';
4
+ import { cn } from "../internal/cn.js";
5
+ import { useControllableState } from "../internal/use-controllable-state.js";
6
+ import { useKptMessages } from "../i18n/i18n-context.js";
7
+ import { KptTooltip } from "../tooltip/tooltip.js";
8
+ /** Domyślna szerokość kolumny dla danego zoomu (px). */
9
+ const CELL_WIDTH = {
10
+ year: 96,
11
+ quarter: 88,
12
+ month: 80,
13
+ week: 76,
14
+ day: 44,
15
+ hour: 46,
16
+ };
17
+ /** Wysokość paska w trybie „jeden item na wiersz" (px). */
18
+ const BAR_HEIGHT = 22;
19
+ /** Odstęp między pasami w trybie „pack" (px). */
20
+ const LANE_GAP = 4;
21
+ /**
22
+ * Roadmapa — wiersze zadań na poziomej osi czasu (widok typu Gantt).
23
+ *
24
+ * Układ opiera się na DOM i `position: sticky` (kolumna etykiet oraz nagłówek),
25
+ * paski są pozycjonowane procentowo w torze, a zależności rysuje warstwa SVG
26
+ * rozpięta nad torami. Cała geometria pochodzi z `@konce-pt/roadmap` — tej samej
27
+ * paczki, z której korzysta port Angulara.
28
+ *
29
+ * @example
30
+ * <KptRoadmap config={{ zoom: 'week', items }} onItemClick={(item) => open(item.id)} />
31
+ */
32
+ export function KptRoadmap({ config, scrollHeight = null, labelWidth = '14rem', cellWidthPx = null, showToday = true, virtual = false, collapsedGroups, defaultCollapsedGroups = [], onCollapsedGroupsChange, onItemClick, onRangeChange, locale: localeOverride, dictionary, className, style, ...rest }) {
33
+ const { t, locale } = useKptMessages({ locale: localeOverride, dictionary });
34
+ const [collapsed, setCollapsed] = useControllableState({
35
+ value: collapsedGroups,
36
+ defaultValue: defaultCollapsedGroups,
37
+ onChange: onCollapsedGroupsChange,
38
+ });
39
+ /**
40
+ * Grupy zwinięte na starcie przez `group.collapsed`. Trzymamy je osobno od stanu aplikacji,
41
+ * żeby sterowanie z zewnątrz pozostało w jej rękach, a zwinięcie zadeklarowane w danych
42
+ * nadal działało. Wyliczamy w trakcie renderu i resetujemy, gdy zmieni się lista grup —
43
+ * efekt zrobiłby to samo o jeden render za późno, czyli po mignięciu rozwiniętej grupy.
44
+ */
45
+ const [fromConfig, setFromConfig] = useState(() => declaredCollapsed(config.groups));
46
+ const groupsRef = useRef(config.groups);
47
+ if (groupsRef.current !== config.groups) {
48
+ groupsRef.current = config.groups;
49
+ setFromConfig(declaredCollapsed(config.groups));
50
+ }
51
+ /** Suma obu źródeł — stan faktycznie użyty do budowy wierszy. */
52
+ const collapsedIds = useMemo(() => new Set([...fromConfig, ...collapsed]), [fromConfig, collapsed]);
53
+ const scrollRef = useRef(null);
54
+ const laneRef = useRef(null);
55
+ const [scrollTop, setScrollTop] = useState(0);
56
+ const [viewportHeight, setViewportHeight] = useState(0);
57
+ /** Szerokość toru w px — potrzebna warstwie SVG, która pracuje w pikselach. */
58
+ const [laneWidth, setLaneWidth] = useState(0);
59
+ const zoom = config.zoom ?? 'week';
60
+ const cellWidth = cellWidthPx ?? CELL_WIDTH[zoom];
61
+ const barHeight = config.rows?.laneHeight ?? BAR_HEIGHT;
62
+ const range = useMemo(() => resolveRange(config, config.items), [config]);
63
+ const timeline = useMemo(() => buildTimeline(range, zoom, {
64
+ ...config.timeline,
65
+ locale: config.locale ?? config.timeline?.locale ?? locale,
66
+ today: config.today ?? config.timeline?.today,
67
+ weekPrefix: config.timeline?.weekPrefix ?? t('roadmap.week'),
68
+ }), [range, zoom, config, locale, t]);
69
+ const layout = useMemo(() => buildRows(config.items, config.groups, collapsedIds, config.rows), [config.items, config.groups, config.rows, collapsedIds]);
70
+ /** Wiersze faktycznie renderowane — całość albo okno wirtualizacji. */
71
+ const visibleRows = useMemo(() => {
72
+ if (!virtual || viewportHeight <= 0)
73
+ return layout.rows;
74
+ return sliceRows(layout, scrollTop, viewportHeight).rows;
75
+ }, [virtual, viewportHeight, layout, scrollTop]);
76
+ // Zakres jest wielkością wyliczaną — informujemy o nim po każdym przeliczeniu.
77
+ useEffect(() => {
78
+ onRangeChange?.(range);
79
+ }, [range, onRangeChange]);
80
+ // Pomiar toru i okna przewijania; oba wpływają wyłącznie na warstwę widoku.
81
+ useLayoutEffect(() => {
82
+ const lane = laneRef.current;
83
+ const scroll = scrollRef.current;
84
+ if (!lane || typeof ResizeObserver === 'undefined')
85
+ return;
86
+ const measure = () => {
87
+ setLaneWidth(lane.clientWidth);
88
+ if (scroll)
89
+ setViewportHeight(scroll.clientHeight);
90
+ };
91
+ const observer = new ResizeObserver(measure);
92
+ observer.observe(lane);
93
+ if (scroll)
94
+ observer.observe(scroll);
95
+ measure();
96
+ return () => observer.disconnect();
97
+ }, []);
98
+ // Ostrzeżenie dla danych, których nie da się narysować.
99
+ useEffect(() => {
100
+ const invalid = layout.invalid;
101
+ if (invalid.length > 0 && isDevBuild()) {
102
+ console.warn('[kpt-roadmap] Pominięto elementy:', invalid.map((entry) => `${entry.id} (${entry.reason})`).join(', '));
103
+ }
104
+ }, [layout]);
105
+ /** Wysokość paska wraz z odstępem — wspólna dla widoku i geometrii zależności. */
106
+ const laneStep = barHeight + LANE_GAP;
107
+ /** Offset pionowy paska wewnątrz wiersza (px). */
108
+ const barTop = useCallback((row, lane) => {
109
+ const total = row.laneCount * barHeight + (row.laneCount - 1) * LANE_GAP;
110
+ return Math.max(0, (row.height - total) / 2) + lane * laneStep;
111
+ }, [barHeight, laneStep]);
112
+ /** Paski danego wiersza gotowe do renderu. */
113
+ const bars = useCallback((row) => {
114
+ const result = [];
115
+ for (const item of row.items) {
116
+ const geom = timeline.span(item.start, item.end);
117
+ if (!geom.visible)
118
+ continue;
119
+ const source = item.source;
120
+ result.push({
121
+ item,
122
+ left: geom.left * 100,
123
+ width: geom.width * 100,
124
+ top: barTop(row, item.lane),
125
+ height: barHeight,
126
+ label: source.label,
127
+ title: source.sublabel ? `${source.label} — ${source.sublabel}` : source.label,
128
+ variant: source.variant ?? 'primary',
129
+ color: source.color,
130
+ progress: source.progress ?? null,
131
+ isMilestone: item.isMilestone,
132
+ clippedStart: geom.clippedStart,
133
+ clippedEnd: geom.clippedEnd,
134
+ });
135
+ }
136
+ return result;
137
+ }, [timeline, barTop, barHeight]);
138
+ /** Pasek zbiorczy wiersza grupy. */
139
+ const summary = useCallback((row) => {
140
+ const span = row.groupState?.span;
141
+ if (!span)
142
+ return null;
143
+ const geom = timeline.span(span.start, span.end);
144
+ if (!geom.visible)
145
+ return null;
146
+ return {
147
+ left: geom.left * 100,
148
+ width: geom.width * 100,
149
+ variant: row.group?.variant ?? 'neutral',
150
+ color: row.group?.color,
151
+ };
152
+ }, [timeline]);
153
+ /**
154
+ * Geometria zależności w pikselach.
155
+ * Liczona dopiero po zmierzeniu toru; wiersze poza oknem wirtualizacji
156
+ * nie mają końców, więc ich krawędzie są pomijane.
157
+ */
158
+ const links = useMemo(() => {
159
+ if (config.dependencies?.enabled === false)
160
+ return [];
161
+ if (laneWidth <= 0)
162
+ return [];
163
+ const endpoints = new Map();
164
+ for (const row of visibleRows) {
165
+ for (const item of row.items) {
166
+ const geom = timeline.span(item.start, item.end);
167
+ if (!geom.visible)
168
+ continue;
169
+ // Romb kamienia milowego jest renderowany wyśrodkowany na dacie
170
+ // (`translateX(-50%)`), więc jego prostokąt zaczyna się o pół rozmiaru wcześniej.
171
+ const left = geom.left * laneWidth - (item.isMilestone ? barHeight / 2 : 0);
172
+ endpoints.set(item.id, {
173
+ id: item.id,
174
+ rect: {
175
+ x: left,
176
+ y: row.top + barTop(row, item.lane),
177
+ width: Math.max(geom.width * laneWidth, item.isMilestone ? barHeight : 1),
178
+ height: barHeight,
179
+ },
180
+ startTs: item.start,
181
+ endTs: item.end,
182
+ });
183
+ }
184
+ }
185
+ const resolved = resolveLinks([...layout.byId.values()], config.links);
186
+ const result = [];
187
+ for (const link of resolved.links) {
188
+ const geom = linkGeometry(link, endpoints.get(link.from), endpoints.get(link.to), config.dependencies);
189
+ if (geom)
190
+ result.push(geom);
191
+ }
192
+ return result;
193
+ }, [config.dependencies, config.links, laneWidth, visibleRows, timeline, layout, barTop, barHeight]);
194
+ const isCollapsed = (group) => group ? collapsedIds.has(group.id) : false;
195
+ const toggleGroup = (group) => {
196
+ if (collapsedIds.has(group.id)) {
197
+ // Rozwijanie musi zdjąć grupę z obu źródeł, inaczej `group.collapsed`
198
+ // natychmiast zwinęłoby ją z powrotem.
199
+ setFromConfig((ids) => ids.filter((id) => id !== group.id));
200
+ setCollapsed(collapsed.filter((id) => id !== group.id));
201
+ return;
202
+ }
203
+ setCollapsed([...collapsed, group.id]);
204
+ };
205
+ const handleItemClick = (item) => {
206
+ if (item.source.disabled)
207
+ return;
208
+ onItemClick?.(item.source);
209
+ };
210
+ const handleItemKeydown = (event, item) => {
211
+ if (event.key !== 'Enter' && event.key !== ' ')
212
+ return;
213
+ event.preventDefault();
214
+ handleItemClick(item);
215
+ };
216
+ /**
217
+ * Treść dymka dla kolumny etykiet. Pusta tam, gdzie dymek nie miałby czego
218
+ * uzupełnić — komponent pomija wtedy podpowiedź.
219
+ */
220
+ const labelTooltip = (row) => {
221
+ // Wiersz grupy ma własny dymek, osadzony głębiej (na stosie etykiet).
222
+ if (row.kind === 'group')
223
+ return '';
224
+ // Tryb 'pack' mieści wiele zadań w jednym wierszu — nie ma jednej etykiety.
225
+ if (row.items.length !== 1)
226
+ return '';
227
+ const source = row.items[0].source;
228
+ return source.sublabel ? `${source.label} — ${source.sublabel}` : source.label;
229
+ };
230
+ const onScroll = (event) => {
231
+ setScrollTop(event.currentTarget.scrollTop);
232
+ };
233
+ const hostStyle = {
234
+ ...style,
235
+ '--kpt-roadmap-cells': timeline.cells.length,
236
+ '--kpt-roadmap-cell-w': `${cellWidth}px`,
237
+ '--kpt-roadmap-label-w': labelWidth,
238
+ };
239
+ const isEmpty = layout.rows.length === 0;
240
+ return (_jsx("div", { className: cn('kpt-roadmap', scrollHeight && 'kpt-roadmap--fixed-height', className), style: hostStyle, ...rest, children: _jsxs("div", { ref: scrollRef, className: "kpt-roadmap__scroll", style: { maxBlockSize: scrollHeight ?? undefined }, onScroll: onScroll, children: [_jsxs("div", { className: "kpt-roadmap__head", children: [_jsx("div", { className: "kpt-roadmap__head-label" }), _jsxs("div", { className: "kpt-roadmap__head-track", children: [timeline.hasBands && (_jsx("div", { className: "kpt-roadmap__bands", children: timeline.bands.map((band) => (_jsx(KptTooltip, { text: band.sublabel ? `${band.label} ${band.sublabel}` : band.label, onlyWhenTruncated: true, children: _jsxs("div", { className: cn('kpt-roadmap__band', band.isYearStart && 'kpt-roadmap__band--year-start'), style: { inlineSize: `${(band.ratioEnd - band.ratioStart) * 100}%` }, children: [band.yearBadge && _jsx("span", { className: "kpt-roadmap__year-badge", children: band.yearBadge }), _jsx("span", { className: "kpt-roadmap__band-label", children: band.label }), band.sublabel && !band.yearBadge && (_jsx("span", { className: "kpt-roadmap__band-sublabel", children: band.sublabel }))] }) }, band.key))) })), _jsx("div", { ref: laneRef, className: "kpt-roadmap__cells", children: timeline.cells.map((cell) => (_jsx(KptTooltip, { text: cell.title, children: _jsxs("div", { className: cn('kpt-roadmap__cell', cell.isWeekend && 'kpt-roadmap__cell--weekend', cell.isToday && showToday && 'kpt-roadmap__cell--today', cell.isYearBoundary && 'kpt-roadmap__cell--year-boundary', cell.hasGapBefore && 'kpt-roadmap__cell--gap-before'), style: { inlineSize: `${(cell.ratioEnd - cell.ratioStart) * 100}%` }, children: [_jsx("span", { className: "kpt-roadmap__cell-label", children: cell.label }), cell.sublabel && _jsx("span", { className: "kpt-roadmap__cell-sublabel", children: cell.sublabel })] }) }, cell.key))) }), showToday && timeline.todayRatio !== null && (_jsx("span", { className: "kpt-roadmap__today-dot", style: { insetInlineStart: `${timeline.todayRatio * 100}%` }, title: t('roadmap.today') }))] })] }), isEmpty ? (_jsx("div", { className: "kpt-roadmap__empty", children: t('roadmap.empty') })) : (_jsxs("div", { className: "kpt-roadmap__body", style: { blockSize: `${layout.totalHeight}px` }, children: [_jsxs("div", { className: "kpt-roadmap__grid", "aria-hidden": "true", children: [_jsx("div", { className: "kpt-roadmap__grid-cells", children: timeline.cells.map((cell) => (_jsx("div", { className: cn('kpt-roadmap__grid-cell', cell.isWeekend && 'kpt-roadmap__grid-cell--weekend', cell.isNonWorking && 'kpt-roadmap__grid-cell--non-working', cell.isYearBoundary && 'kpt-roadmap__grid-cell--year-boundary', cell.hasGapBefore && 'kpt-roadmap__grid-cell--gap-before'), style: { inlineSize: `${(cell.ratioEnd - cell.ratioStart) * 100}%` } }, cell.key))) }), showToday && timeline.todayRatio !== null && (_jsx("div", { className: "kpt-roadmap__today", style: { insetInlineStart: `${timeline.todayRatio * 100}%` } }))] }), links.length > 0 && (_jsx("svg", { className: "kpt-roadmap__links", viewBox: `0 0 ${laneWidth} ${layout.totalHeight}`, width: laneWidth, height: layout.totalHeight, "aria-hidden": "true", children: links.map((link) => (_jsxs("g", { className: cn('kpt-roadmap__link', link.isViolated && 'kpt-roadmap__link--violated'), "data-variant": link.variant, children: [_jsx("path", { className: "kpt-roadmap__link-path", d: link.path, style: { stroke: link.color } }), _jsx("path", { className: "kpt-roadmap__link-arrow", d: link.arrow, style: { fill: link.color } })] }, link.key))) })), visibleRows.map((row) => {
241
+ const group = row.group;
242
+ const first = row.items[0];
243
+ const sum = row.kind === 'group' ? summary(row) : null;
244
+ return (_jsxs("div", { className: cn('kpt-roadmap__row', row.kind === 'group' && 'kpt-roadmap__row--group'), style: { insetBlockStart: `${row.top}px`, blockSize: `${row.height}px` }, children: [_jsx(KptTooltip, { text: labelTooltip(row), onlyWhenTruncated: true, children: _jsx("div", { className: cn('kpt-roadmap__label', row.kind === 'group' && 'kpt-roadmap__label--group'), style: { paddingInlineStart: `${0.75 + row.depth * 1}rem` }, children: group ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "kpt-roadmap__toggle", "aria-expanded": !isCollapsed(group), "aria-label": isCollapsed(group) ? t('roadmap.expandGroup') : t('roadmap.collapseGroup'), onClick: () => toggleGroup(group), children: _jsx("span", { className: cn('kpt-roadmap__toggle-icon', !isCollapsed(group) && 'kpt-roadmap__toggle-icon--open') }) }), _jsx(KptTooltip, { text: group.sublabel ? `${group.label} — ${group.sublabel}` : group.label, onlyWhenTruncated: true, children: _jsxs("span", { className: "kpt-roadmap__label-stack", children: [_jsx("span", { className: "kpt-roadmap__label-text kpt-roadmap__label-text--group", children: group.label }), group.sublabel && _jsx("span", { className: "kpt-roadmap__label-sub", children: group.sublabel })] }) }), row.groupState?.itemCount ? (_jsx("span", { className: "kpt-roadmap__label-count", children: row.groupState.itemCount })) : null] })) : (first && (_jsxs(_Fragment, { children: [_jsx("span", { className: "kpt-roadmap__label-text", children: first.source.label }), row.items.length === 1 && first.source.sublabel && (_jsx("span", { className: "kpt-roadmap__label-sub", children: first.source.sublabel }))] }))) }) }), _jsx("div", { className: "kpt-roadmap__lane", children: row.kind === 'group'
245
+ ? sum && (_jsx("div", { className: "kpt-roadmap__summary", "data-variant": sum.variant, style: {
246
+ insetInlineStart: `${sum.left}%`,
247
+ inlineSize: `${sum.width}%`,
248
+ backgroundColor: sum.color,
249
+ } }))
250
+ : bars(row).map((bar) => bar.isMilestone ? (_jsx(KptTooltip, { text: bar.title, children: _jsx("div", { className: "kpt-roadmap__milestone", "data-variant": bar.variant, "aria-label": `${t('roadmap.milestone')}: ${bar.label}`, style: {
251
+ insetInlineStart: `${bar.left}%`,
252
+ insetBlockStart: `${bar.top}px`,
253
+ inlineSize: `${bar.height}px`,
254
+ blockSize: `${bar.height}px`,
255
+ backgroundColor: bar.color,
256
+ }, tabIndex: 0, onClick: () => handleItemClick(bar.item), onKeyDown: (event) => handleItemKeydown(event, bar.item) }) }, bar.item.id)) : (_jsx(KptTooltip, { text: bar.title, onlyWhenTruncated: true, children: _jsxs("div", { className: cn('kpt-roadmap__bar', bar.clippedStart && 'kpt-roadmap__bar--clipped-start', bar.clippedEnd && 'kpt-roadmap__bar--clipped-end', bar.item.source.disabled && 'kpt-roadmap__bar--disabled'), "data-variant": bar.variant, style: {
257
+ insetInlineStart: `${bar.left}%`,
258
+ inlineSize: `${bar.width}%`,
259
+ insetBlockStart: `${bar.top}px`,
260
+ blockSize: `${bar.height}px`,
261
+ backgroundColor: bar.color,
262
+ }, tabIndex: 0, onClick: () => handleItemClick(bar.item), onKeyDown: (event) => handleItemKeydown(event, bar.item), children: [bar.progress !== null && (_jsx("span", { className: "kpt-roadmap__bar-progress", style: { inlineSize: `${bar.progress * 100}%` } })), _jsx("span", { className: "kpt-roadmap__bar-label", children: bar.label })] }) }, bar.item.id))) })] }, row.key));
263
+ })] }))] }) }));
264
+ }
265
+ function isDevBuild() {
266
+ return typeof process !== 'undefined' && process?.env?.['NODE_ENV'] !== 'production';
267
+ }
268
+ /** Identyfikatory grup zwiniętych deklaratywnie w danych. */
269
+ function declaredCollapsed(groups) {
270
+ return (groups ?? []).filter((group) => group.collapsed).map((group) => group.id);
271
+ }
@@ -0,0 +1,4 @@
1
+ export { KptRoadmap } from '../roadmap/roadmap.tsx';
2
+ export type { KptRoadmapProps } from '../roadmap/roadmap.tsx';
3
+ export type { KptRoadmapConfig, KptRoadmapItem, KptRoadmapGroup, KptRoadmapLink, KptRoadmapLinkType, KptRoadmapZoom, KptRoadmapVariant, KptRoadmapChange, KptRoadmapChangeKind, KptRoadmapConstraints, KptRoadmapNormalizedItem, KptRoadmapPositionMode, KptRoadmapRowMode, KptRoadmapRowOptions, KptRoadmapSummaryMode, KptRoadmapDependencyOptions, KptRoadmapInteractionOptions, KptTimelineOptions, KptTimeRange, KptWorkHours, KptDateInput, KptTimelineCell, KptTimelineBand, KptTimelineModel, KptSpanGeom, KptRoadmapRow, KptRoadmapRowKind, KptRoadmapLayout, KptRoadmapGroupState, KptRoadmapPlacedItem, KptLinkGeom, KptLinkEndpoint, } from '@konce-pt/roadmap';
4
+ export { buildTimeline, buildRows, normalizeItems, packLanes, sliceRows, spanOf, rollupProgress, resolveLinks, linkGeometry, roundedPolyline, arrowHead, autoRange, resolveRange, isoWeekNumber, isoWeekYear, isoWeekday, isoWeeksInYear, startOfIsoWeek, startOfDay, addDays, addHours, addMonths, addUnits, floorToUnit, ceilToUnit, countUnits, diffDays, daysInMonth, isSameDay, toTimestamp, } from '@konce-pt/roadmap';
@@ -0,0 +1,7 @@
1
+ /*
2
+ * @konce-pt/react/roadmap — oś czasu typu Gantt. Osobny subpath, bo geometria, style i dane
3
+ * roadmapy są ciężkie względem rdzenia biblioteki i nie każda aplikacja ich potrzebuje.
4
+ */
5
+ export { KptRoadmap } from "../roadmap/roadmap.js";
6
+ // Rdzeń obliczeniowy (pure TS) — przydatny do własnych rozszerzeń, SSR i testów.
7
+ export { buildTimeline, buildRows, normalizeItems, packLanes, sliceRows, spanOf, rollupProgress, resolveLinks, linkGeometry, roundedPolyline, arrowHead, autoRange, resolveRange, isoWeekNumber, isoWeekYear, isoWeekday, isoWeeksInYear, startOfIsoWeek, startOfDay, addDays, addHours, addMonths, addUnits, floorToUnit, ceilToUnit, countUnits, diffDays, daysInMonth, isSameDay, toTimestamp, } from '@konce-pt/roadmap';
@@ -0,0 +1,16 @@
1
+ import { type ComponentPropsWithRef } from 'react';
2
+ import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
3
+ export interface KptScrollTopProps extends ComponentPropsWithRef<'div'>, KptI18nOverrides {
4
+ /** Po ilu pikselach przewinięcia przycisk się pojawia. */
5
+ threshold?: number;
6
+ /** Selektor przewijanego elementu; bez niego nasłuchujemy okna. */
7
+ target?: string | null;
8
+ }
9
+ /**
10
+ * Przycisk „do góry" — pojawia się po przewinięciu ponad `threshold` px.
11
+ *
12
+ * @example
13
+ * <KptScrollTop threshold={300} />
14
+ * <KptScrollTop target="#panel" />
15
+ */
16
+ export declare function KptScrollTop({ threshold, target, locale, dictionary, className, ...rest }: Readonly<KptScrollTopProps>): import("react").JSX.Element;
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useState } 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
+ * Przycisk „do góry" — pojawia się po przewinięciu ponad `threshold` px.
8
+ *
9
+ * @example
10
+ * <KptScrollTop threshold={300} />
11
+ * <KptScrollTop target="#panel" />
12
+ */
13
+ export function KptScrollTop({ threshold = 300, target = null, locale, dictionary, className, ...rest }) {
14
+ const { t } = useKptMessages({ locale, dictionary });
15
+ const [visible, setVisible] = useState(false);
16
+ useEffect(() => {
17
+ // Element wyszukujemy w efekcie, a nie przy renderze: przewijany kontener bywa montowany
18
+ // po nas, a na serwerze nie ma go w ogóle.
19
+ const scroller = target
20
+ ? (document.querySelector(target) ?? window)
21
+ : window;
22
+ const top = () => (scroller === window ? window.scrollY : scroller.scrollTop);
23
+ const onScroll = () => setVisible(top() > threshold);
24
+ scroller.addEventListener('scroll', onScroll, { passive: true });
25
+ onScroll(); // strona może być już przewinięta, np. po wejściu z kotwicy
26
+ return () => scroller.removeEventListener('scroll', onScroll);
27
+ }, [target, threshold]);
28
+ const scrollUp = () => {
29
+ const scroller = target
30
+ ? (document.querySelector(target) ?? window)
31
+ : window;
32
+ scroller.scrollTo({ top: 0, behavior: 'smooth' });
33
+ };
34
+ return (_jsx("div", { className: cn('kpt-scroll-top-host', className), ...rest, children: _jsx("button", { type: "button", className: cn('kpt-scroll-top', visible && 'is-visible'),
35
+ // Ukryty przycisk zostaje w DOM (animacja pojawiania), więc z kolejności Tab wyjmuje go
36
+ // `tabindex`, a nie warunkowy render.
37
+ tabIndex: visible ? 0 : -1, "aria-label": t('scrollTop.label'), onClick: scrollUp, children: _jsx(KptIcon, { name: "arrow-up" }) }) }));
38
+ }
@@ -0,0 +1,37 @@
1
+ import { type ComponentPropsWithRef } from 'react';
2
+ import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
3
+ import type { KptOption } from '../shared/types.ts';
4
+ export interface KptSelectProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'children' | 'onChange' | 'defaultValue'>, KptI18nOverrides {
5
+ options?: KptOption<T>[];
6
+ placeholder?: string;
7
+ /** Wybór wielokrotny — wartością jest tablica, a wybrane opcje pokazują się jako chipy. */
8
+ multiple?: boolean;
9
+ /** Pole wyszukiwania nad listą. */
10
+ filter?: boolean;
11
+ disabled?: boolean;
12
+ invalid?: boolean;
13
+ touched?: boolean;
14
+ /** Wartość: pojedyncza (T) albo tablica (T[]) w trybie `multiple`. */
15
+ value?: T | T[] | null;
16
+ defaultValue?: T | T[] | null;
17
+ onValueChange?: (value: T | T[] | null) => void;
18
+ /** Stan panelu — pozwala otworzyć listę z zewnątrz i wiedzieć, kiedy user skończył wybierać. */
19
+ open?: boolean;
20
+ defaultOpen?: boolean;
21
+ onOpenChange?: (open: boolean) => void;
22
+ /** Wywoływane, gdy panel się zamyka albo trigger traci fokus. */
23
+ onTouch?: () => void;
24
+ }
25
+ /**
26
+ * Lista wyboru (select) na własnym panelu. Warianty: pojedynczy, z wyszukiwarką (`filter`)
27
+ * i wielokrotny (`multiple`, chipy).
28
+ *
29
+ * Wewnątrz `KptFormField` zgłasza wypełnienie hookiem `useKptFormFieldFilled`, więc etykieta
30
+ * Float Label unosi się nad wybraną wartością — natywnego pola, z którego wrapper mógłby to
31
+ * odczytać, tu nie ma.
32
+ *
33
+ * @example
34
+ * <KptSelect options={roles} value={role} onValueChange={setRole} placeholder="Wybierz…" />
35
+ * <KptSelect options={tags} multiple filter value={tags} onValueChange={setTags} />
36
+ */
37
+ export declare function KptSelect<T>({ options, placeholder, multiple, filter, disabled, invalid, touched, value, defaultValue, onValueChange, open, defaultOpen, onOpenChange, onTouch, locale, dictionary, className, onBlur, ...rest }: Readonly<KptSelectProps<T>>): import("react").JSX.Element;
@@ -0,0 +1,117 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
3
+ import { cn } from "../internal/cn.js";
4
+ import { KptOverlay } from "../internal/overlay.js";
5
+ import { useControllableState } from "../internal/use-controllable-state.js";
6
+ import { useKptMessages } from "../i18n/i18n-context.js";
7
+ import { useKptFormFieldFilled } from "../form-field/form-field-context.js";
8
+ import { KptIcon } from "../icon/icon.js";
9
+ /**
10
+ * Lista wyboru (select) na własnym panelu. Warianty: pojedynczy, z wyszukiwarką (`filter`)
11
+ * i wielokrotny (`multiple`, chipy).
12
+ *
13
+ * Wewnątrz `KptFormField` zgłasza wypełnienie hookiem `useKptFormFieldFilled`, więc etykieta
14
+ * Float Label unosi się nad wybraną wartością — natywnego pola, z którego wrapper mógłby to
15
+ * odczytać, tu nie ma.
16
+ *
17
+ * @example
18
+ * <KptSelect options={roles} value={role} onValueChange={setRole} placeholder="Wybierz…" />
19
+ * <KptSelect options={tags} multiple filter value={tags} onValueChange={setTags} />
20
+ */
21
+ export function KptSelect({ options = [], placeholder = '', multiple = false, filter = false, disabled = false, invalid = false, touched, value, defaultValue = null, onValueChange, open, defaultOpen = false, onOpenChange, onTouch, locale, dictionary, className, onBlur, ...rest }) {
22
+ const { t } = useKptMessages({ locale, dictionary });
23
+ const triggerRef = useRef(null);
24
+ const [current, setCurrent] = useControllableState({
25
+ value,
26
+ defaultValue,
27
+ onChange: onValueChange,
28
+ });
29
+ const [isOpen, setOpen] = useControllableState({
30
+ value: open,
31
+ defaultValue: defaultOpen,
32
+ onChange: onOpenChange,
33
+ });
34
+ const [filterText, setFilterText] = useState('');
35
+ const [activeIndex, setActiveIndex] = useState(-1);
36
+ // Panel może zostać otwarty także z zewnątrz (`open`), nie tylko klikiem w trigger —
37
+ // filtr i aktywną pozycję resetujemy więc przy każdym otwarciu, nie przy przełączaniu.
38
+ useEffect(() => {
39
+ if (!isOpen)
40
+ return;
41
+ setFilterText('');
42
+ setActiveIndex(-1);
43
+ }, [isOpen]);
44
+ const showInvalid = invalid && (touched ?? true);
45
+ const selectedValues = useMemo(() => {
46
+ if (multiple)
47
+ return Array.isArray(current) ? current : [];
48
+ return current == null ? [] : [current];
49
+ }, [current, multiple]);
50
+ const selectedOptions = useMemo(() => options.filter((option) => selectedValues.includes(option.value)), [options, selectedValues]);
51
+ const filteredOptions = useMemo(() => {
52
+ const q = filterText.trim().toLowerCase();
53
+ return q ? options.filter((option) => option.label.toLowerCase().includes(q)) : options;
54
+ }, [options, filterText]);
55
+ const filled = selectedOptions.length > 0;
56
+ useKptFormFieldFilled(filled);
57
+ const isSelected = (option) => selectedValues.includes(option.value);
58
+ const close = useCallback(() => {
59
+ if (!isOpen)
60
+ return;
61
+ setOpen(false);
62
+ onTouch?.();
63
+ }, [isOpen, setOpen, onTouch]);
64
+ const pick = (option) => {
65
+ if (option.disabled)
66
+ return;
67
+ if (multiple) {
68
+ setCurrent(isSelected(option) ? selectedValues.filter((v) => v !== option.value) : [...selectedValues, option.value]);
69
+ }
70
+ else {
71
+ setCurrent(option.value);
72
+ close();
73
+ }
74
+ };
75
+ const onTriggerKeyDown = (event) => {
76
+ if (disabled)
77
+ return;
78
+ if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {
79
+ event.preventDefault();
80
+ if (!isOpen)
81
+ setOpen(true);
82
+ }
83
+ else if (event.key === 'Escape') {
84
+ close();
85
+ }
86
+ };
87
+ const onPanelKeyDown = (event) => {
88
+ if (event.key === 'Escape') {
89
+ event.preventDefault();
90
+ close();
91
+ }
92
+ else if (event.key === 'ArrowDown') {
93
+ event.preventDefault();
94
+ setActiveIndex((index) => Math.min(index + 1, filteredOptions.length - 1));
95
+ }
96
+ else if (event.key === 'ArrowUp') {
97
+ event.preventDefault();
98
+ setActiveIndex((index) => Math.max(index - 1, 0));
99
+ }
100
+ else if (event.key === 'Enter') {
101
+ event.preventDefault();
102
+ const option = filteredOptions[activeIndex];
103
+ if (option)
104
+ pick(option);
105
+ }
106
+ };
107
+ return (_jsxs("span", { className: "kpt-select-host", children: [_jsxs("div", { ref: triggerRef, className: cn('kpt-select', isOpen && 'kpt-select--open', disabled && 'kpt-select--disabled', showInvalid && 'kpt-select--invalid', className), role: "combobox", tabIndex: 0, "aria-expanded": isOpen, "aria-disabled": disabled || undefined, onClick: () => !disabled && setOpen(!isOpen), onKeyDown: onTriggerKeyDown, onBlur: (event) => {
108
+ onBlur?.(event);
109
+ onTouch?.();
110
+ }, ...rest, children: [_jsx("div", { className: "kpt-select__value", children: multiple && selectedOptions.length ? (selectedOptions.map((option) => (_jsxs("span", { className: "kpt-select__chip", children: [option.label, _jsx("button", { type: "button", className: "kpt-select__chip-remove", "aria-label": t('select.removeChip'), onClick: (event) => {
111
+ event.stopPropagation();
112
+ setCurrent(selectedValues.filter((v) => v !== option.value));
113
+ }, children: _jsx(KptIcon, { name: "x" }) })] }, String(option.value))))) : !multiple && filled ? (_jsx("span", { className: "kpt-select__single", children: selectedOptions[0]?.label })) : (_jsx("span", { className: "kpt-select__placeholder", children: placeholder })) }), _jsx(KptIcon, { className: "kpt-select__chevron", name: "chevron-down" })] }), _jsx(KptOverlay, { open: isOpen, anchor: triggerRef.current, onClose: close, matchAnchorWidth: true, children: _jsxs("div", { className: "kpt-select__panel", onKeyDown: onPanelKeyDown, children: [filter ? (_jsxs("div", { className: "kpt-select__search", children: [_jsx(KptIcon, { name: "search" }), _jsx("input", { type: "text", className: "kpt-select__search-input", value: filterText, placeholder: t('select.search'), autoFocus: true, onChange: (event) => {
114
+ setFilterText(event.target.value);
115
+ setActiveIndex(-1);
116
+ } })] })) : null, _jsx("ul", { className: "kpt-select__list", role: "listbox", "aria-multiselectable": multiple, children: filteredOptions.length ? (filteredOptions.map((option, index) => (_jsxs("li", { className: cn('kpt-select__option', isSelected(option) && 'kpt-select__option--selected', index === activeIndex && 'kpt-select__option--active', option.disabled && 'kpt-select__option--disabled'), role: "option", "aria-selected": isSelected(option), onClick: () => pick(option), onMouseEnter: () => setActiveIndex(index), children: [multiple ? (_jsx("span", { className: cn('kpt-select__check', isSelected(option) && 'is-on'), children: _jsx(KptIcon, { name: "check" }) })) : null, _jsx("span", { className: "kpt-select__option-label", children: option.label }), !multiple && isSelected(option) ? (_jsx(KptIcon, { className: "kpt-select__option-tick", name: "check" })) : null] }, String(option.value))))) : (_jsx("li", { className: "kpt-select__empty", children: t('select.noResults') })) })] }) })] }));
117
+ }
@@ -0,0 +1,8 @@
1
+ /** Opcja wyboru dla kontrolek typu select / radio-group / button-group. */
2
+ export interface KptOption<T = unknown> {
3
+ label: string;
4
+ value: T;
5
+ disabled?: boolean;
6
+ }
7
+ /** Wariant komunikatu — wspólny dla alertu i toastów. */
8
+ export type KptAlertVariant = 'info' | 'success' | 'warning' | 'danger';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import { type ComponentPropsWithRef } from 'react';
2
+ export interface KptSidenavProps extends ComponentPropsWithRef<'div'> {
3
+ }
4
+ /**
5
+ * Panel boczny (sidenav) — stylowany kontener nawigacji, zwykle podawany do slotu
6
+ * `sidenav` komponentu `<KptAppShell>`.
7
+ *
8
+ * @example
9
+ * <KptSidenav>
10
+ * <a href="#">Pulpit</a>
11
+ * <a href="#">Ustawienia</a>
12
+ * <KptSidenav.Footer>wersja 0.7.12</KptSidenav.Footer>
13
+ * </KptSidenav>
14
+ */
15
+ export declare function KptSidenav({ className, children, ...rest }: Readonly<KptSidenavProps>): import("react").JSX.Element;
16
+ export declare namespace KptSidenav {
17
+ var Footer: typeof KptSidenavFooter;
18
+ }
19
+ /**
20
+ * Stopka panelu, przyklejona do dołu poza obszarem przewijania (blok użytkownika, wersja
21
+ * aplikacji, przełącznik motywu). Nosi atrybut `kptsidenavfooter`, w który celuje wspólny SCSS.
22
+ */
23
+ export declare function KptSidenavFooter({ className, ...rest }: Readonly<ComponentPropsWithRef<'div'>>): import("react").JSX.Element;
24
+ export declare namespace KptSidenavFooter {
25
+ var kptSidenavSlot: "footer";
26
+ }
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Children, isValidElement } from 'react';
3
+ import { cn } from "../internal/cn.js";
4
+ /*
5
+ * Panel odtwarza projekcję z Angulara: treść domyślna ląduje w obszarze przewijanym,
6
+ * a `<KptSidenav.Footer>` jest wynoszona POZA ten obszar. Dzięki temu stopka zostaje
7
+ * przyklejona do dołu panelu niezależnie od długości nawigacji.
8
+ */
9
+ /**
10
+ * Panel boczny (sidenav) — stylowany kontener nawigacji, zwykle podawany do slotu
11
+ * `sidenav` komponentu `<KptAppShell>`.
12
+ *
13
+ * @example
14
+ * <KptSidenav>
15
+ * <a href="#">Pulpit</a>
16
+ * <a href="#">Ustawienia</a>
17
+ * <KptSidenav.Footer>wersja 0.7.12</KptSidenav.Footer>
18
+ * </KptSidenav>
19
+ */
20
+ export function KptSidenav({ className, children, ...rest }) {
21
+ const scroll = [];
22
+ const footers = [];
23
+ Children.forEach(children, (child) => {
24
+ if (isValidElement(child) && child.type?.kptSidenavSlot === 'footer') {
25
+ footers.push(child);
26
+ }
27
+ else {
28
+ scroll.push(child);
29
+ }
30
+ });
31
+ return (_jsxs("div", { className: cn('kpt-sidenav', className), ...rest, children: [_jsx("div", { className: "kpt-sidenav__scroll", children: scroll }), footers] }));
32
+ }
33
+ /**
34
+ * Stopka panelu, przyklejona do dołu poza obszarem przewijania (blok użytkownika, wersja
35
+ * aplikacji, przełącznik motywu). Nosi atrybut `kptsidenavfooter`, w który celuje wspólny SCSS.
36
+ */
37
+ export function KptSidenavFooter({ className, ...rest }) {
38
+ return _jsx("div", { kptsidenavfooter: '', className: className, ...rest });
39
+ }
40
+ KptSidenavFooter.kptSidenavSlot = 'footer';
41
+ KptSidenav.Footer = KptSidenavFooter;
@@ -0,0 +1,20 @@
1
+ import type { ComponentPropsWithRef } from 'react';
2
+ export type KptSkeletonVariant = 'text' | 'circle' | 'rect';
3
+ export interface KptSkeletonProps extends Omit<ComponentPropsWithRef<'span'>, 'children'> {
4
+ variant?: KptSkeletonVariant;
5
+ width?: string;
6
+ height?: string;
7
+ /** Liczba linii w wariancie `text`. */
8
+ lines?: number;
9
+ }
10
+ /**
11
+ * Placeholder ładowania z animacją shimmer.
12
+ *
13
+ * Wariant `text` skraca ostatnią linię do 60% — pełny prostokąt nie wygląda jak akapit, a to
14
+ * właśnie akapit ma udawać.
15
+ *
16
+ * @example
17
+ * <KptSkeleton variant="text" lines={3} />
18
+ * <KptSkeleton variant="circle" width="3rem" height="3rem" />
19
+ */
20
+ export declare function KptSkeleton({ variant, width, height, lines, className, ...rest }: Readonly<KptSkeletonProps>): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../internal/cn.js";
3
+ /**
4
+ * Placeholder ładowania z animacją shimmer.
5
+ *
6
+ * Wariant `text` skraca ostatnią linię do 60% — pełny prostokąt nie wygląda jak akapit, a to
7
+ * właśnie akapit ma udawać.
8
+ *
9
+ * @example
10
+ * <KptSkeleton variant="text" lines={3} />
11
+ * <KptSkeleton variant="circle" width="3rem" height="3rem" />
12
+ */
13
+ export function KptSkeleton({ variant = 'rect', width = '100%', height = '1rem', lines = 3, className, ...rest }) {
14
+ return (_jsx("span", { className: cn('kpt-skeleton-host', className), "aria-hidden": "true", ...rest, children: variant === 'text' ? (Array.from({ length: lines }, (_, index) => (_jsx("span", { className: "kpt-skeleton kpt-skeleton--text", style: { width: index === lines - 1 ? '60%' : width } }, index)))) : (_jsx("span", { className: "kpt-skeleton", "data-variant": variant, style: { width, height } })) }));
15
+ }