@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,85 @@
1
+ /*
2
+ * Sanityzacja treści edytora. Kod nie zna Reacta — operuje wyłącznie na DOM, więc gdyby port
3
+ * Angulara kiedyś sięgnął po wspólny rdzeń, wystarczy przenieść ten plik do osobnej paczki
4
+ * (jak `@konce-pt/datetime`).
5
+ */
6
+ /** Znaczniki dopuszczone w treści; pozostałe rozwijamy do dzieci, nie usuwając tekstu. */
7
+ const ALLOWED_TAGS = new Set([
8
+ 'P', 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'B', 'STRONG', 'I', 'EM', 'U', 'S', 'STRIKE',
9
+ 'UL', 'OL', 'LI', 'A', 'SPAN', 'DIV', 'BLOCKQUOTE', 'FONT',
10
+ 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TD', 'TH', 'COLGROUP', 'COL', 'CAPTION',
11
+ ]);
12
+ const ALLOWED_STYLES = new Set([
13
+ 'color', 'background-color', 'text-align', 'font-weight', 'font-style', 'text-decoration',
14
+ 'width', 'height', 'border', 'border-color', 'border-width', 'border-style', 'vertical-align',
15
+ ]);
16
+ const ALLOWED_ATTRS = new Set(['colspan', 'rowspan', 'span', 'width', 'class', 'target', 'rel', 'title']);
17
+ // Schematy dopuszczone w `href` narzędzia „link"; blocklista byłaby obchodzalna.
18
+ const SAFE_URL_SCHEME = /^(https?:|mailto:|tel:)/i;
19
+ const HAS_URL_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
20
+ /** Deklaracje `style` z allowlisty; `null`, gdy nie zostaje nic. */
21
+ function sanitizeStyleAttr(value) {
22
+ const kept = value
23
+ .split(';')
24
+ .map((decl) => decl.trim())
25
+ .filter((decl) => ALLOWED_STYLES.has(decl.split(':')[0]?.trim().toLowerCase() ?? ''));
26
+ return kept.length ? kept.join('; ') : null;
27
+ }
28
+ function sanitizeAttribute(el, attr) {
29
+ const name = attr.name.toLowerCase();
30
+ if (name.startsWith('on')) {
31
+ el.removeAttribute(attr.name);
32
+ }
33
+ else if (name === 'href' || name === 'src') {
34
+ // Znaki kontrolne usuwamy przed testem schematu — przeglądarka i tak je pomija przy
35
+ // parsowaniu URL-a, więc `jav\tascript:` obszedłby samo porównanie napisów.
36
+ // eslint-disable-next-line no-control-regex
37
+ if (/^\s*javascript:/i.test(attr.value.replace(/[\x00-\x1f]/g, '')))
38
+ el.removeAttribute(attr.name);
39
+ }
40
+ else if (name === 'style') {
41
+ const kept = sanitizeStyleAttr(attr.value);
42
+ if (kept)
43
+ el.setAttribute('style', kept);
44
+ else
45
+ el.removeAttribute('style');
46
+ }
47
+ else if (!ALLOWED_ATTRS.has(name) && name !== 'color') {
48
+ el.removeAttribute(attr.name);
49
+ }
50
+ }
51
+ /** Tag spoza allowlisty rozwijamy do dzieci; atrybuty czyścimy po kolei. */
52
+ function sanitizeElement(el) {
53
+ if (!ALLOWED_TAGS.has(el.tagName)) {
54
+ el.replaceWith(...Array.from(el.childNodes));
55
+ return;
56
+ }
57
+ for (const attr of Array.from(el.attributes))
58
+ sanitizeAttribute(el, attr);
59
+ }
60
+ /**
61
+ * Czyści HTML wchodzący do edytora. Dotyczy **każdego** zapisu do DOM edytora, nie tylko
62
+ * wklejania: `value` z aplikacji też bywa niezaufane (baza, CMS, inny użytkownik).
63
+ */
64
+ export function sanitizeHtml(html) {
65
+ const template = document.createElement('template');
66
+ template.innerHTML = html;
67
+ for (const el of Array.from(template.content.querySelectorAll('*')))
68
+ sanitizeElement(el);
69
+ return template.innerHTML;
70
+ }
71
+ /**
72
+ * Waliduje URL z narzędzia „link". Brak schematu → dopisujemy `https://`; `#` i `/` przechodzą
73
+ * bez zmian; schemat spoza allowlisty → `null`, czyli link nie zostaje wstawiony.
74
+ */
75
+ export function sanitizeUrl(url) {
76
+ // eslint-disable-next-line no-control-regex
77
+ const trimmed = url.trim().replace(/[\x00-\x1f]/g, '');
78
+ if (!trimmed)
79
+ return null;
80
+ if (trimmed.startsWith('#') || trimmed.startsWith('/'))
81
+ return trimmed;
82
+ if (!HAS_URL_SCHEME.test(trimmed))
83
+ return `https://${trimmed}`;
84
+ return SAFE_URL_SCHEME.test(trimmed) ? trimmed : null;
85
+ }
@@ -0,0 +1,44 @@
1
+ /** Pozycja komórki w siatce (uwzględnia colspan/rowspan). */
2
+ export type GridPos = Map<HTMLTableCellElement, {
3
+ r: number;
4
+ c: number;
5
+ rowspan: number;
6
+ colspan: number;
7
+ }>;
8
+ /** Prostokąt siatki: wiersze `r0..r1`, kolumny `c0..c1` (włącznie). */
9
+ export interface Bounds {
10
+ r0: number;
11
+ c0: number;
12
+ r1: number;
13
+ c1: number;
14
+ }
15
+ /** Klasa znacząca komórki objęte zaznaczeniem prostokątnym. */
16
+ export declare const CELL_SELECTED = "kpt-rt-cell-sel";
17
+ /** Macierz komórek: pozycja w siatce, a nie w DOM — scalona komórka zajmuje kilka pól. */
18
+ export declare function buildGrid(table: HTMLTableElement): {
19
+ grid: HTMLTableCellElement[][];
20
+ pos: GridPos;
21
+ };
22
+ /** Komórki leżące w prostokącie siatki; scalona trafia raz, bo to `Set`. */
23
+ export declare function cellsInRegion(grid: HTMLTableCellElement[][], b: Bounds): Set<HTMLTableCellElement>;
24
+ /**
25
+ * Prostokątne zaznaczenie od komórki-kotwicy do komórki pod kursorem. Granice rosną, aż region
26
+ * w pełni obejmie scalone komórki na brzegach — inaczej scalanie działałoby na pół komórki.
27
+ */
28
+ export declare function regionBetween(anchor: HTMLTableCellElement, focus: HTMLTableCellElement): Set<HTMLTableCellElement>;
29
+ /** Równe szerokości kolumn po wstawieniu albo usunięciu kolumny. */
30
+ export declare function normalizeColWidths(table: HTMLTableElement): void;
31
+ /** HTML pustej tabeli o zadanym rozmiarze, z `colgroup` o równych szerokościach. */
32
+ export declare function tableHtml(rows: number, cols: number): string;
33
+ export declare function insertColumn(cell: HTMLTableCellElement, dir: 'left' | 'right'): void;
34
+ export declare function deleteColumn(cell: HTMLTableCellElement): void;
35
+ export declare function insertRow(cell: HTMLTableCellElement, dir: 'above' | 'below'): void;
36
+ export declare function deleteRow(cell: HTMLTableCellElement): void;
37
+ /** Podmienia znacznik komórki `td` ↔ `th`, zachowując atrybuty i treść. */
38
+ export declare function swapCellTag(cell: HTMLTableCellElement, tag: 'td' | 'th'): void;
39
+ export declare function toggleHeaderRow(cell: HTMLTableCellElement): void;
40
+ export declare function toggleHeaderColumn(cell: HTMLTableCellElement): void;
41
+ /** Scala zaznaczone komórki w jedną; treść pozostałych dokleja do górnej-lewej. */
42
+ export declare function mergeCells(cells: HTMLTableCellElement[]): void;
43
+ /** Rozbija scaloną komórkę, uzupełniając brakujące pola siatki pustymi komórkami. */
44
+ export declare function splitCell(cell: HTMLTableCellElement): void;
@@ -0,0 +1,268 @@
1
+ /*
2
+ * Operacje na tabelach edytora: siatka z uwzględnieniem colspan/rowspan, zaznaczanie prostokątne,
3
+ * scalanie i dzielenie komórek, wstawianie i usuwanie wierszy i kolumn. Czysty DOM, bez Reacta.
4
+ */
5
+ /** Klasa znacząca komórki objęte zaznaczeniem prostokątnym. */
6
+ export const CELL_SELECTED = 'kpt-rt-cell-sel';
7
+ /** Macierz komórek: pozycja w siatce, a nie w DOM — scalona komórka zajmuje kilka pól. */
8
+ export function buildGrid(table) {
9
+ const grid = [];
10
+ const pos = new Map();
11
+ Array.from(table.rows).forEach((tr, r) => {
12
+ grid[r] ??= [];
13
+ let c = 0;
14
+ for (const cell of Array.from(tr.cells)) {
15
+ while (grid[r][c])
16
+ c++; // pomiń pola zajęte przez rowspan z góry
17
+ const colspan = cell.colSpan || 1;
18
+ const rowspan = cell.rowSpan || 1;
19
+ pos.set(cell, { r, c, rowspan, colspan });
20
+ for (let dr = 0; dr < rowspan; dr++) {
21
+ grid[r + dr] ??= [];
22
+ for (let dc = 0; dc < colspan; dc++)
23
+ grid[r + dr][c + dc] = cell;
24
+ }
25
+ c += colspan;
26
+ }
27
+ });
28
+ return { grid, pos };
29
+ }
30
+ /** Komórki leżące w prostokącie siatki; scalona trafia raz, bo to `Set`. */
31
+ export function cellsInRegion(grid, b) {
32
+ const set = new Set();
33
+ for (let r = b.r0; r <= b.r1; r++) {
34
+ for (let c = b.c0; c <= b.c1; c++) {
35
+ const cell = grid[r]?.[c];
36
+ if (cell)
37
+ set.add(cell);
38
+ }
39
+ }
40
+ return set;
41
+ }
42
+ /** Jeden przebieg rozszerzania granic o komórki wystające poza region. */
43
+ function growBounds(grid, pos, b) {
44
+ let changed = false;
45
+ for (const cell of cellsInRegion(grid, b)) {
46
+ const p = pos.get(cell);
47
+ if (p.r < b.r0) {
48
+ b.r0 = p.r;
49
+ changed = true;
50
+ }
51
+ if (p.c < b.c0) {
52
+ b.c0 = p.c;
53
+ changed = true;
54
+ }
55
+ if (p.r + p.rowspan - 1 > b.r1) {
56
+ b.r1 = p.r + p.rowspan - 1;
57
+ changed = true;
58
+ }
59
+ if (p.c + p.colspan - 1 > b.c1) {
60
+ b.c1 = p.c + p.colspan - 1;
61
+ changed = true;
62
+ }
63
+ }
64
+ return changed;
65
+ }
66
+ /**
67
+ * Prostokątne zaznaczenie od komórki-kotwicy do komórki pod kursorem. Granice rosną, aż region
68
+ * w pełni obejmie scalone komórki na brzegach — inaczej scalanie działałoby na pół komórki.
69
+ */
70
+ export function regionBetween(anchor, focus) {
71
+ const table = anchor.closest('table');
72
+ if (!table)
73
+ return new Set();
74
+ const { grid, pos } = buildGrid(table);
75
+ const a = pos.get(anchor);
76
+ const f = pos.get(focus);
77
+ if (!a || !f)
78
+ return new Set();
79
+ const bounds = {
80
+ r0: Math.min(a.r, f.r),
81
+ c0: Math.min(a.c, f.c),
82
+ r1: Math.max(a.r + a.rowspan - 1, f.r + f.rowspan - 1),
83
+ c1: Math.max(a.c + a.colspan - 1, f.c + f.colspan - 1),
84
+ };
85
+ while (growBounds(grid, pos, bounds)) {
86
+ /* granice rosną do skutku */
87
+ }
88
+ return cellsInRegion(grid, bounds);
89
+ }
90
+ /** Równe szerokości kolumn po wstawieniu albo usunięciu kolumny. */
91
+ export function normalizeColWidths(table) {
92
+ const cols = Array.from(table.querySelectorAll('col'));
93
+ if (!cols.length)
94
+ return;
95
+ const width = (100 / cols.length).toFixed(4);
96
+ for (const col of cols)
97
+ col.style.width = `${width}%`;
98
+ }
99
+ /** HTML pustej tabeli o zadanym rozmiarze, z `colgroup` o równych szerokościach. */
100
+ export function tableHtml(rows, cols) {
101
+ const width = (100 / cols).toFixed(4);
102
+ const colgroup = `<colgroup>${`<col style="width:${width}%">`.repeat(cols)}</colgroup>`;
103
+ const body = `<tbody>${`<tr>${'<td><br></td>'.repeat(cols)}</tr>`.repeat(rows)}</tbody>`;
104
+ return `<table class="kpt-rt-table">${colgroup}${body}</table>`;
105
+ }
106
+ export function insertColumn(cell, dir) {
107
+ const table = cell.closest('table');
108
+ if (!table)
109
+ return;
110
+ const index = cell.cellIndex + (dir === 'right' ? 1 : 0);
111
+ for (const tr of Array.from(table.rows)) {
112
+ const td = document.createElement('td');
113
+ td.innerHTML = '<br>';
114
+ tr.insertBefore(td, tr.cells[index] ?? null);
115
+ }
116
+ const colgroup = table.querySelector('colgroup');
117
+ if (colgroup) {
118
+ colgroup.insertBefore(document.createElement('col'), colgroup.children[index] ?? null);
119
+ normalizeColWidths(table);
120
+ }
121
+ }
122
+ export function deleteColumn(cell) {
123
+ const table = cell.closest('table');
124
+ if (!table)
125
+ return;
126
+ const index = cell.cellIndex;
127
+ // Ostatnia kolumna = cała tabela; usuwamy ją zamiast zostawiać pusty szkielet.
128
+ if ((table.rows[0]?.cells.length ?? 0) <= 1) {
129
+ table.remove();
130
+ return;
131
+ }
132
+ for (const tr of Array.from(table.rows))
133
+ tr.cells[index]?.remove();
134
+ table.querySelector('colgroup')?.children[index]?.remove();
135
+ normalizeColWidths(table);
136
+ }
137
+ export function insertRow(cell, dir) {
138
+ const tr = cell.parentElement;
139
+ if (!tr)
140
+ return;
141
+ const row = document.createElement('tr');
142
+ row.innerHTML = Array.from({ length: tr.cells.length }, () => '<td><br></td>').join('');
143
+ tr.parentElement?.insertBefore(row, dir === 'below' ? tr.nextSibling : tr);
144
+ }
145
+ export function deleteRow(cell) {
146
+ const table = cell.closest('table');
147
+ const tr = cell.parentElement;
148
+ if (!table || !tr)
149
+ return;
150
+ if (table.rows.length <= 1) {
151
+ table.remove();
152
+ return;
153
+ }
154
+ tr.remove();
155
+ }
156
+ /** Podmienia znacznik komórki `td` ↔ `th`, zachowując atrybuty i treść. */
157
+ export function swapCellTag(cell, tag) {
158
+ if (cell.tagName.toLowerCase() === tag)
159
+ return;
160
+ const el = document.createElement(tag);
161
+ for (const attr of Array.from(cell.attributes))
162
+ el.setAttribute(attr.name, attr.value);
163
+ el.append(...Array.from(cell.childNodes));
164
+ cell.replaceWith(el);
165
+ }
166
+ export function toggleHeaderRow(cell) {
167
+ const tr = cell.parentElement;
168
+ if (!tr)
169
+ return;
170
+ const toHeader = tr.cells[0]?.tagName !== 'TH';
171
+ for (const c of Array.from(tr.cells))
172
+ swapCellTag(c, toHeader ? 'th' : 'td');
173
+ }
174
+ export function toggleHeaderColumn(cell) {
175
+ const table = cell.closest('table');
176
+ if (!table)
177
+ return;
178
+ const index = cell.cellIndex;
179
+ const toHeader = cell.tagName !== 'TH';
180
+ for (const tr of Array.from(table.rows)) {
181
+ const c = tr.cells[index];
182
+ if (c)
183
+ swapCellTag(c, toHeader ? 'th' : 'td');
184
+ }
185
+ }
186
+ /** Scala zaznaczone komórki w jedną; treść pozostałych dokleja do górnej-lewej. */
187
+ export function mergeCells(cells) {
188
+ if (cells.length < 2)
189
+ return;
190
+ const table = cells[0].closest('table');
191
+ if (!table)
192
+ return;
193
+ const { pos } = buildGrid(table);
194
+ let r0 = Infinity;
195
+ let c0 = Infinity;
196
+ let r1 = -1;
197
+ let c1 = -1;
198
+ for (const cell of cells) {
199
+ const p = pos.get(cell);
200
+ r0 = Math.min(r0, p.r);
201
+ c0 = Math.min(c0, p.c);
202
+ r1 = Math.max(r1, p.r + p.rowspan - 1);
203
+ c1 = Math.max(c1, p.c + p.colspan - 1);
204
+ }
205
+ const topLeft = cells.find((cell) => {
206
+ const p = pos.get(cell);
207
+ return p.r === r0 && p.c === c0;
208
+ }) ?? cells[0];
209
+ for (const cell of cells) {
210
+ if (cell === topLeft)
211
+ continue;
212
+ const html = cell.innerHTML.trim();
213
+ if (html && html !== '<br>')
214
+ topLeft.innerHTML += ` ${html}`;
215
+ cell.remove();
216
+ }
217
+ const colspan = c1 - c0 + 1;
218
+ const rowspan = r1 - r0 + 1;
219
+ if (colspan > 1)
220
+ topLeft.colSpan = colspan;
221
+ else
222
+ topLeft.removeAttribute('colspan');
223
+ if (rowspan > 1)
224
+ topLeft.rowSpan = rowspan;
225
+ else
226
+ topLeft.removeAttribute('rowspan');
227
+ }
228
+ /** Komórka w wierszu, przed którą wstawić (pierwsza o origin-col > `gridCol`), albo `null`. */
229
+ function cellBeforeGridCol(row, gridCol, table) {
230
+ const { pos } = buildGrid(table);
231
+ for (const cell of Array.from(row.cells)) {
232
+ const p = pos.get(cell);
233
+ if (p && p.c > gridCol)
234
+ return cell;
235
+ }
236
+ return null;
237
+ }
238
+ /** Rozbija scaloną komórkę, uzupełniając brakujące pola siatki pustymi komórkami. */
239
+ export function splitCell(cell) {
240
+ const table = cell.closest('table');
241
+ if (!table)
242
+ return;
243
+ const { pos } = buildGrid(table);
244
+ const p = pos.get(cell);
245
+ if (!p || (p.colspan <= 1 && p.rowspan <= 1))
246
+ return;
247
+ const tag = cell.tagName.toLowerCase();
248
+ cell.removeAttribute('colspan');
249
+ cell.removeAttribute('rowspan');
250
+ for (let dr = 0; dr < p.rowspan; dr++) {
251
+ // Origin zostaje w pierwszym wierszu, więc tam brakuje o jedną komórkę mniej.
252
+ const count = dr === 0 ? p.colspan - 1 : p.colspan;
253
+ if (count <= 0)
254
+ continue;
255
+ const row = table.rows[p.r + dr];
256
+ if (!row)
257
+ continue;
258
+ const before = cellBeforeGridCol(row, p.c, table);
259
+ for (let i = 0; i < count; i++) {
260
+ const el = document.createElement(tag);
261
+ el.innerHTML = '<br>';
262
+ if (before)
263
+ before.before(el);
264
+ else
265
+ row.append(el);
266
+ }
267
+ }
268
+ }
@@ -0,0 +1,37 @@
1
+ import { type ComponentPropsWithRef } from 'react';
2
+ import { type KptRoadmapConfig, type KptRoadmapItem, type KptTimeRange } from '@konce-pt/roadmap';
3
+ import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
4
+ export interface KptRoadmapProps<TItem = unknown, TGroup = unknown> extends Omit<ComponentPropsWithRef<'div'>, 'onClick' | 'children'>, KptI18nOverrides {
5
+ /** Pełna konfiguracja: dane, zoom, zakres i opcje. */
6
+ config: KptRoadmapConfig<TItem, TGroup>;
7
+ /** Stała wysokość obszaru przewijania (np. '420px'). Włącza przyklejony nagłówek. */
8
+ scrollHeight?: string | null;
9
+ /** Szerokość kolumny etykiet. */
10
+ labelWidth?: string;
11
+ /** Szerokość kolumny osi czasu; brak ⇒ wartość zależna od zoomu. */
12
+ cellWidthPx?: number | null;
13
+ /** Znacznik „dziś". */
14
+ showToday?: boolean;
15
+ /** Renderowanie tylko widocznych wierszy. Warto włączyć od ~100 wierszy. */
16
+ virtual?: boolean;
17
+ /** Zwinięte grupy — tryb kontrolowany. */
18
+ collapsedGroups?: string[];
19
+ /** Zwinięte grupy na starcie — tryb niekontrolowany. */
20
+ defaultCollapsedGroups?: string[];
21
+ onCollapsedGroupsChange?: (ids: string[]) => void;
22
+ onItemClick?: (item: KptRoadmapItem<TItem>) => void;
23
+ /** Wywoływane przy każdej zmianie wyliczonego zakresu osi. */
24
+ onRangeChange?: (range: KptTimeRange) => void;
25
+ }
26
+ /**
27
+ * Roadmapa — wiersze zadań na poziomej osi czasu (widok typu Gantt).
28
+ *
29
+ * Układ opiera się na DOM i `position: sticky` (kolumna etykiet oraz nagłówek),
30
+ * paski są pozycjonowane procentowo w torze, a zależności rysuje warstwa SVG
31
+ * rozpięta nad torami. Cała geometria pochodzi z `@konce-pt/roadmap` — tej samej
32
+ * paczki, z której korzysta port Angulara.
33
+ *
34
+ * @example
35
+ * <KptRoadmap config={{ zoom: 'week', items }} onItemClick={(item) => open(item.id)} />
36
+ */
37
+ export declare function KptRoadmap<TItem = unknown, TGroup = unknown>({ config, scrollHeight, labelWidth, cellWidthPx, showToday, virtual, collapsedGroups, defaultCollapsedGroups, onCollapsedGroupsChange, onItemClick, onRangeChange, locale: localeOverride, dictionary, className, style, ...rest }: Readonly<KptRoadmapProps<TItem, TGroup>>): import("react").JSX.Element;