@konce-pt/angular 0.6.1 → 0.7.5

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 (121) hide show
  1. package/CHANGELOG.md +384 -0
  2. package/README.md +38 -19
  3. package/fesm2022/konce-pt-angular-charts.mjs +1409 -0
  4. package/fesm2022/konce-pt-angular-charts.mjs.map +1 -0
  5. package/fesm2022/konce-pt-angular-grid.mjs +232 -0
  6. package/{dist/fesm2022 → fesm2022}/konce-pt-angular-grid.mjs.map +1 -1
  7. package/fesm2022/konce-pt-angular-map.mjs +4568 -0
  8. package/fesm2022/konce-pt-angular-map.mjs.map +1 -0
  9. package/fesm2022/konce-pt-angular-roadmap.mjs +1919 -0
  10. package/fesm2022/konce-pt-angular-roadmap.mjs.map +1 -0
  11. package/fesm2022/konce-pt-angular.mjs +12307 -0
  12. package/fesm2022/konce-pt-angular.mjs.map +1 -0
  13. package/map/src/lib/llms.txt +741 -0
  14. package/package.json +44 -54
  15. package/roadmap/src/lib/llms.txt +112 -0
  16. package/src/lib/app-shell/llms.txt +89 -0
  17. package/src/lib/breakpoint/llms.txt +45 -0
  18. package/{dist/src → src}/lib/datepicker/llms.txt +3 -0
  19. package/src/lib/dialog/llms.txt +68 -0
  20. package/{dist/src → src}/lib/form-field/llms.txt +24 -1
  21. package/{dist/src → src}/lib/icon/llms.txt +2 -1
  22. package/{dist/src → src}/lib/input/llms.txt +14 -4
  23. package/src/lib/input-number/llms.txt +61 -0
  24. package/{dist/src → src}/lib/select/llms.txt +9 -0
  25. package/src/lib/sidenav/llms.txt +29 -0
  26. package/src/lib/toolbar/llms.txt +31 -0
  27. package/src/lib/tooltip/llms.txt +33 -0
  28. package/{dist/types → types}/konce-pt-angular-charts.d.ts +1 -0
  29. package/types/konce-pt-angular-charts.d.ts.map +1 -0
  30. package/{dist/types → types}/konce-pt-angular-grid.d.ts +1 -0
  31. package/types/konce-pt-angular-grid.d.ts.map +1 -0
  32. package/{dist/types → types}/konce-pt-angular-icons.d.ts +1 -0
  33. package/types/konce-pt-angular-icons.d.ts.map +1 -0
  34. package/types/konce-pt-angular-map.d.ts +1585 -0
  35. package/types/konce-pt-angular-map.d.ts.map +1 -0
  36. package/types/konce-pt-angular-roadmap.d.ts +649 -0
  37. package/types/konce-pt-angular-roadmap.d.ts.map +1 -0
  38. package/{dist/types → types}/konce-pt-angular.d.ts +373 -23
  39. package/types/konce-pt-angular.d.ts.map +1 -0
  40. package/dist/LICENSE +0 -21
  41. package/dist/README.md +0 -316
  42. package/dist/fesm2022/konce-pt-angular-charts.mjs +0 -957
  43. package/dist/fesm2022/konce-pt-angular-charts.mjs.map +0 -1
  44. package/dist/fesm2022/konce-pt-angular-grid.mjs +0 -218
  45. package/dist/fesm2022/konce-pt-angular.mjs +0 -8462
  46. package/dist/fesm2022/konce-pt-angular.mjs.map +0 -1
  47. package/dist/package.json +0 -79
  48. package/dist/src/lib/app-shell/llms.txt +0 -29
  49. package/dist/src/lib/dialog/llms.txt +0 -27
  50. package/dist/src/lib/input-number/llms.txt +0 -17
  51. package/dist/src/lib/sidenav/llms.txt +0 -20
  52. package/dist/src/lib/toolbar/llms.txt +0 -23
  53. package/dist/src/lib/tooltip/llms.txt +0 -19
  54. /package/{dist/charts → charts}/src/lib/llms.txt +0 -0
  55. /package/{dist/fesm2022 → fesm2022}/konce-pt-angular-icons.mjs +0 -0
  56. /package/{dist/fesm2022 → fesm2022}/konce-pt-angular-icons.mjs.map +0 -0
  57. /package/{dist/grid → grid}/src/lib/llms.txt +0 -0
  58. /package/{dist/src → src}/lib/accordion/llms.txt +0 -0
  59. /package/{dist/src → src}/lib/alert/llms.txt +0 -0
  60. /package/{dist/src → src}/lib/autocomplete/llms.txt +0 -0
  61. /package/{dist/src → src}/lib/avatar/llms.txt +0 -0
  62. /package/{dist/src → src}/lib/avatar-group/llms.txt +0 -0
  63. /package/{dist/src → src}/lib/badge/llms.txt +0 -0
  64. /package/{dist/src → src}/lib/bottom-sheet/llms.txt +0 -0
  65. /package/{dist/src → src}/lib/breadcrumb/llms.txt +0 -0
  66. /package/{dist/src → src}/lib/button/llms.txt +0 -0
  67. /package/{dist/src → src}/lib/button-group/llms.txt +0 -0
  68. /package/{dist/src → src}/lib/card/llms.txt +0 -0
  69. /package/{dist/src → src}/lib/carousel/llms.txt +0 -0
  70. /package/{dist/src → src}/lib/checkbox/llms.txt +0 -0
  71. /package/{dist/src → src}/lib/chip/llms.txt +0 -0
  72. /package/{dist/src → src}/lib/chips-input/llms.txt +0 -0
  73. /package/{dist/src → src}/lib/color-picker/llms.txt +0 -0
  74. /package/{dist/src → src}/lib/confirm/llms.txt +0 -0
  75. /package/{dist/src → src}/lib/context-menu/llms.txt +0 -0
  76. /package/{dist/src → src}/lib/data-table/llms.txt +0 -0
  77. /package/{dist/src → src}/lib/data-view/llms.txt +0 -0
  78. /package/{dist/src → src}/lib/date-range/llms.txt +0 -0
  79. /package/{dist/src → src}/lib/divider/llms.txt +0 -0
  80. /package/{dist/src → src}/lib/drawer/llms.txt +0 -0
  81. /package/{dist/src → src}/lib/empty/llms.txt +0 -0
  82. /package/{dist/src → src}/lib/fab/llms.txt +0 -0
  83. /package/{dist/src → src}/lib/fieldset/llms.txt +0 -0
  84. /package/{dist/src → src}/lib/file-upload/llms.txt +0 -0
  85. /package/{dist/src → src}/lib/galleria/llms.txt +0 -0
  86. /package/{dist/src → src}/lib/i18n/llms.txt +0 -0
  87. /package/{dist/src → src}/lib/icon-button/llms.txt +0 -0
  88. /package/{dist/src → src}/lib/image/llms.txt +0 -0
  89. /package/{dist/src → src}/lib/input-mask/llms.txt +0 -0
  90. /package/{dist/src → src}/lib/input-otp/llms.txt +0 -0
  91. /package/{dist/src → src}/lib/knob/llms.txt +0 -0
  92. /package/{dist/src → src}/lib/listbox/llms.txt +0 -0
  93. /package/{dist/src → src}/lib/megamenu/llms.txt +0 -0
  94. /package/{dist/src → src}/lib/menu/llms.txt +0 -0
  95. /package/{dist/src → src}/lib/menubar/llms.txt +0 -0
  96. /package/{dist/src → src}/lib/meter-group/llms.txt +0 -0
  97. /package/{dist/src → src}/lib/order-list/llms.txt +0 -0
  98. /package/{dist/src → src}/lib/paginator/llms.txt +0 -0
  99. /package/{dist/src → src}/lib/panel/llms.txt +0 -0
  100. /package/{dist/src → src}/lib/password/llms.txt +0 -0
  101. /package/{dist/src → src}/lib/pick-list/llms.txt +0 -0
  102. /package/{dist/src → src}/lib/popover/llms.txt +0 -0
  103. /package/{dist/src → src}/lib/progress/llms.txt +0 -0
  104. /package/{dist/src → src}/lib/radio-group/llms.txt +0 -0
  105. /package/{dist/src → src}/lib/rating/llms.txt +0 -0
  106. /package/{dist/src → src}/lib/rich-text/llms.txt +0 -0
  107. /package/{dist/src → src}/lib/scroll-top/llms.txt +0 -0
  108. /package/{dist/src → src}/lib/skeleton/llms.txt +0 -0
  109. /package/{dist/src → src}/lib/slider/llms.txt +0 -0
  110. /package/{dist/src → src}/lib/speed-dial/llms.txt +0 -0
  111. /package/{dist/src → src}/lib/spinner/llms.txt +0 -0
  112. /package/{dist/src → src}/lib/split-button/llms.txt +0 -0
  113. /package/{dist/src → src}/lib/splitter/llms.txt +0 -0
  114. /package/{dist/src → src}/lib/stepper/llms.txt +0 -0
  115. /package/{dist/src → src}/lib/switch/llms.txt +0 -0
  116. /package/{dist/src → src}/lib/switch-group/llms.txt +0 -0
  117. /package/{dist/src → src}/lib/tabs/llms.txt +0 -0
  118. /package/{dist/src → src}/lib/textarea/llms.txt +0 -0
  119. /package/{dist/src → src}/lib/timeline/llms.txt +0 -0
  120. /package/{dist/src → src}/lib/toast/llms.txt +0 -0
  121. /package/{dist/src → src}/lib/tree/llms.txt +0 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,384 @@
1
+ # @konce-pt/angular
2
+
3
+ ## 0.7.5
4
+
5
+ ### Minor Changes
6
+
7
+ - Responsywny `kpt-app-shell`: nakładka, szyna ikon i panel zadokowany
8
+
9
+ `kpt-app-shell` sam dobiera układ do szerokości okna. Nowy tryb `auto` (od `sidenavBreakpoint`,
10
+ domyślnie `lg`, panel jest zadokowany; poniżej — nakładka ze scrimem, domyślnie zamknięta),
11
+ opcjonalna szyna ikon w paśmie do `sidenavRailBreakpoint`, `Escape` zamykające nakładkę,
12
+ `inert` na treści pod nią, poprawny kierunek wysuwu w RTL i wysokość `100dvh`.
13
+
14
+ O układzie decyduje wyłącznie CSS, więc nic nie migocze przed hydracją i całość działa na SSR.
15
+
16
+ **Zmiana zachowania:** domyślną wartością `sidenavMode` jest teraz `'auto'` zamiast `'side'`.
17
+ Jeśli aplikacja sama zarządza responsywnością szkieletu (własne `@media` i własny hamburger),
18
+ ustaw jawnie `sidenavMode="side"` — inaczej dostaniesz dwie warstwy tej samej logiki.
19
+ Tryby `'side'` i `'over'` działają dokładnie jak wcześniej.
20
+
21
+ Nowe API:
22
+
23
+ - `KptBreakpointObserver` — media query jako sygnały (`up`, `down`, `matches`), spięte
24
+ z tokenami `--kpt-breakpoint-*`; poza przeglądarką zwraca `false`.
25
+ - `KptShellNavToggle` (`[kptShellNavToggle]`) — hamburger, który sam trafia w stan właściwy
26
+ dla bieżącego układu i dokłada `aria-expanded` / `aria-controls` / etykietę z i18n.
27
+ - `kpt-app-shell`: `sidenavCompactOpen`, `sidenavBreakpoint`, `sidenavRailBreakpoint`,
28
+ `sidenavLabel`, `sidenavId`, `isOverlay()`, `isSidenavOpen()`,
29
+ `toggleSidenav()` / `openSidenav()` / `closeSidenav()`.
30
+ - `kpt-toolbar`: input `wrap` (zawijanie zawartości) oraz kompaktowa wysokość poniżej `sm`.
31
+ - `@konce-pt/grid`: klasy `kpt-display-*` z wariantami per breakpoint i skrótem `kpt-hide`.
32
+ - Tokeny: `--kpt-sidenav-rail-width` (4rem), `--kpt-toolbar-height-compact` (3rem).
33
+ - Słownik: namespace `appShell` (`toggleNavigation`, `navigation`) w EN i PL.
34
+
35
+ Poprawka: `kpt-drawer` używał fallbacku `--kpt-z-overlay: 1000` (wartość `z.dropdown`)
36
+ zamiast `1100`.
37
+
38
+ - Nowy komponent `KptMap` (kpt-map) w subpathu `@konce-pt/angular/map` — interaktywna mapa
39
+ z konfigurowalnymi znacznikami, na silniku Leaflet + OpenStreetMap.
40
+
41
+ - Kategorie obiektów z kolorem (rola semantyczna tokenów, nie literał) i ikoną z `KptIconRegistry`;
42
+ kształty `pin` / `dot` / `square`.
43
+ - Grupowanie znaczników z licznikiem — własny rdzeń pure TS (siatka w przestrzeni pikseli, O(n),
44
+ deterministyczna), wspólny dla obu silników i przenośny do portu React. Bez `leaflet.markercluster`.
45
+ - Popup szczegółów o szablonie zależnym od kategorii (`ng-template kptMapPopup`), renderowany
46
+ przez Angulara i pozycjonowany przez `engine.project()` — pełne komponenty `kpt-*` w treści.
47
+ Kontekst szablonu niesie też `distance` — odległość punktu w metrach od `[distanceFrom]`,
48
+ a bez niego od pozycji użytkownika.
49
+ - **Karta miejsca `KptMapPoiCard` (kpt-map-poi-card)**: dystans, zdjęcie 16:9, przycisk główny
50
+ z udostępnianiem i ulubionymi, wiersz kategorii, rozwijane godziny otwarcia ze statusem
51
+ otwarte/zamknięte i wiersze kontaktowe (telefon, www, e-mail, współrzędne) z kopiowaniem
52
+ do schowka. Sterowana danymi: bierze `KptMapPoi`, nie model domenowy aplikacji.
53
+ - `[popupLayout]="'card'"` oddaje popup w całości treści — bez paddingu, szerszy, przewijalny
54
+ i bez wbudowanego krzyżyka (daje go karta). Domyślne `plain` zostaje bez zmian.
55
+ - Nagłówek karty (tytuł, krzyżyk, dystans i adres) stoi nieruchomo; przewija się wyłącznie
56
+ reszta treści, w osobnym `.kpt-map-poi__scroll`. Przewijanie schodzi w tym celu z popupu
57
+ do karty — `popupLayout="card"` daje `.kpt-map__popup-body` `display: flex` i `overflow:
58
+ hidden`, a karcie pełną wysokość. Sam popup zostaje bez `overflow: hidden`, bo ucięłby
59
+ strzałkę `::after` wiszącą na `top: 100%`. Karta poza popupem, bez limitu wysokości,
60
+ nie przewija się wcale.
61
+ - Pasek „Anuluj / Zapisz" w formularzu dodawania jest przypięty do dołu (`position: sticky`),
62
+ więc nie ucieka na koniec długiej listy pól.
63
+ - Legenda jako filtr: `KptMapLegend` (kpt-map-legend) z `aria-pressed`, liczniki per kategoria,
64
+ ukrycie kategorii usuwa ją także z grup.
65
+ - Dodawanie lokalizacji prawym przyciskiem myszy: formularz budowany ze schemy `category.fields`
66
+ na Signal Forms albo własny `ng-template kptMapCreateForm`. Poza `text`/`textarea`/`number`/
67
+ `date`/`select`/`multiselect`/`checkbox` schema przyjmuje `tel`, `email` i `url` (z walidacją
68
+ formatu), `image` — wybór pliku czytany jako `data:` **albo** wklejony adres, oraz `hours` —
69
+ edytor tygodnia dający `KptMapOpeningHours`.
70
+ Plik zdjęcia zostaje w przeglądarce: biblioteka nigdzie go nie wysyła i nie ma wiedzy
71
+ o storage'u aplikacji. Limit 2 MB jest realny — data URL puchnie o ~⅓, a cała wartość
72
+ leci w `locationCreate`.
73
+ - Widok startowy z presetu kraju (`KPT_MAP_COUNTRIES`, domyślnie Polska) oraz opcjonalne
74
+ ograniczenie przesuwania do jego zasięgu.
75
+ - Znacznik własnego położenia z geolokalizacji wraz z kręgiem dokładności i kontrolką
76
+ „wyśrodkuj na mnie"; źródło pozycji przez port DI `KPT_MAP_GEOLOCATION`
77
+ (`provideKptMapGeolocationStub()` dla Storybooka i testów).
78
+ - Silnik za interfejsem `KptMapEngine` — `provideKptLeafletMap()` dziś, adapter Google Maps
79
+ bez zmian w API komponentu.
80
+ - Rdzeń pure TS w publicznym API: `clusterLocations`, `countryPreset`, projekcja Mercatora
81
+ (`project`/`unproject`/`boundsOf`/`haversine`/`metersPerPixel`/`zoomForBounds`),
82
+ generatory HTML znaczników, formatowanie (`formatDistance`, `formatLatLng`) oraz godziny
83
+ otwarcia (`isOpenAt`, `isAlwaysOpen`, `nextChange`, `groupWeek`, `weekdayName`, `emptyWeek`,
84
+ `parseTime`, `formatTime`). Testy: `packages/angular/map/src/core/*.test.ts`.
85
+ `isOpenAt()` liczy dwa dni — bieżący i poprzedni — więc przedział `22:00–01:00` obejmuje
86
+ wczesne godziny dnia następnego; `24:00` to koniec doby, a nie przejście przez północ.
87
+ `groupWeek()` skleja sąsiadujące dni o identycznym rozkładzie w `pon.–pt.`, ale nie zawija
88
+ tygodnia przez granicę niedziela–poniedziałek. Nazwy dni idą z `Intl`, nie ze słownika —
89
+ karta i edytor godzin działają dla dowolnego locale bez dopisywania tłumaczeń.
90
+ - Nowe tokeny komponentowe `--kpt-map-*` (w tym `popup-width`, `popup-width-card`,
91
+ `popup-max-height`, `poi-media-radius`, `poi-row-hover`) oraz klucze i18n `map.*`,
92
+ `map.poi.*` i `map.field.*` (EN + PL).
93
+ - Cztery ikony dopisane do **wbudowanego** zestawu `KPT_ICONS`: `mail`, `share`,
94
+ `external-link`, `crosshair`. Karta miejsca ich potrzebuje, a komponent biblioteczny nie może
95
+ wymagać `provideKptTablerIcons()`.
96
+ - `KptInput` przyjmuje `type="time"`.
97
+ - `KptDatepicker` dostaje wejście `ariaLabel` (nazwa dostępna tam, gdzie pola nie opisuje
98
+ `<label for>` — np. dwie godziny w jednym wierszu) i w trybie `time` rysuje w polu ikonę
99
+ zegara zamiast kalendarza. Godziny otwarcia wybiera się właśnie nim
100
+ (`selectionMode="time"`, `minuteStep=5`), a nie natywnym `<input type="time">`: tamten
101
+ otwiera systemowy dropdown, którego nie da się ostylować.
102
+ - **Naprawiony kolor ikony kategorii `muted` poza mapą.** Legenda malowała glif zaszytą
103
+ bielą na tle koloru roli, a `--kpt-color-muted` to neutral-100 — token tła, nie treści.
104
+ Kategoria `muted` miała więc w legendzie ikonę praktycznie niewidoczną, choć jej znacznik
105
+ na mapie był poprawny (ten bierze `contrastVarOf()`). Legenda używa teraz `contrastVarOf()`,
106
+ a nowe `foregroundVarOf()` w publicznym API daje kolor glifu stojącego wprost na powierzchni
107
+ (bez własnego tła) — z niego korzysta wiersz kategorii w karcie miejsca.
108
+ - `renderKptIconSvg()` wydzielone z `kpt-icon` do publicznego API — mapa buduje z niego
109
+ znaczniki poza Angularem, więc oba miejsca mają jedno źródło prawdy.
110
+
111
+ **Nowa zależność opcjonalna:** `leaflet` jako `peerDependencies` + `peerDependenciesMeta.optional`.
112
+ To pierwsza zależność runtime biblioteki. Ładowana dynamicznym `import()`, więc aplikacje bez mapy
113
+ jej nie pobierają. Użycie mapy wymaga `pnpm add leaflet` i dołączenia `leaflet/dist/leaflet.css`
114
+ (Angular CLI: `angular.json → styles[]`, nie `import` w `main.ts`).
115
+
116
+ **Uwaga o kaskadzie:** nadpisania `.leaflet-*` w `map.component.scss` stoją poza `@layer` — celowo
117
+ i jako jedyne w bibliotece. `leaflet.css` nie jest warstwowany, a CSS spoza warstw wygrywa z każdą
118
+ regułą w `@layer` niezależnie od specyficzności.
119
+
120
+ - Podpowiadanie adresu w formularzu „dodaj lokalizację": pole z `autofill` (`city`, `address`,
121
+ `street`, `houseNumber`, `postcode`, `state`, `county`, `country`, `countryCode`, `label`)
122
+ wypełnia się z geokodowania wstecznego współrzędnych kliknięcia. Źródło przez port DI
123
+ `KPT_MAP_GEOCODER`; gotowy adapter `provideKptNominatimGeocoder()` jest **opt-in** —
124
+ biblioteka nie odpytuje cudzego serwera bez zgody aplikacji. Podpowiedź wypełnia wyłącznie
125
+ pola puste, więc nigdy nie kasuje tego, co użytkownik wpisał.
126
+
127
+ **Uwaga o kaflach:** domyślne `tile.openstreetmap.org` to usługa best-effort bez SLA, z wymogiem
128
+ widocznej atrybucji i zakazem pre-fetchowania. Do produkcji podstaw własny serwer przez `[tileUrl]`.
129
+ To samo dotyczy Nominatim (jedno zapytanie na sekundę, zakaz masowego odpytywania) — adapter sam
130
+ trzyma odstęp, ale produkcja powinna mieć własną instancję albo dostawcę komercyjnego.
131
+
132
+ ***
133
+
134
+ Przy okazji naprawiony **utajony błąd `KptDialog`**: kontrolki na CDK Overlay (`kpt-select`,
135
+ `kpt-datepicker`, `kpt-autocomplete`, `kpt-menu`) otwierały się w środku okna modalnego
136
+ niewidoczne. `showModal()` wynosi `<dialog>` do warstwy szczytowej przeglądarki, ponad całe
137
+ drzewo dokumentu, a kontener CDK Overlay wisi na `<body>` — żaden `z-index` tego nie przebija.
138
+ `kpt-dialog` przenosi teraz kontener overlayów do wnętrza okna na czas jego otwarcia i oddaje
139
+ go na miejsce przy zamknięciu.
140
+
141
+ Błąd był w bibliotece od początku, ale nieuruchomiony: żaden przykład nie umieszczał kontrolki
142
+ overlayowej w oknie modalnym. Ujawnił go formularz „dodaj lokalizację" z pól `date` i `select`.
143
+
144
+ **Nieruchomy nagłówek okna.** `.kpt-dialog` jest teraz kolumną flex: nagłówek z krzyżykiem
145
+ i stopka `[kptDialogFooter]` stoją, a pasek przewijania biegnie wyłącznie wzdłuż
146
+ `.kpt-dialog__body`. Wcześniej okno przewijało się w całości, razem z nagłówkiem. Treść
147
+ dostaje `flex: 0 1 auto` + `min-height: 0`, celowo nie `flex: 1` — krótkie okno (`kpt-confirm`)
148
+ dalej kurczy się do treści, zamiast rozciągać na `max-height`. `display: flex` jest scopowane
149
+ `&[open]`: regułę `dialog:not([open]) { display: none }` daje arkusz przeglądarki, a każda
150
+ deklaracja autorska ją bije — także z `@layer`, więc bez scopingu zamknięte okna zostałyby
151
+ widoczne. `overflow: hidden` na oknie celowo nie ma: przycięłoby panele CDK Overlay
152
+ przeniesione do jego wnętrza.
153
+
154
+ `.kpt-map .leaflet-container` dostaje `z-index: 0`, żeby zamknąć skalę z-index Leafleta
155
+ (panes 200–700, kontrolki 800) we własnym kontekście układania — bez tego popup, kontrolki
156
+ zoomu i menu prawego przycisku są malowane pod kaflami.
157
+
158
+ ***
159
+
160
+ Naprawiony też **utajony błąd `KptSwitch`, `KptCheckbox` i `KptRadioGroup`**: ukryty `<input>`
161
+ tych kontrolek jest `position: absolute`, ale ich element główny nie był pozycjonowany. Blokiem
162
+ zawierającym stawał się więc najbliższy pozycjonowany przodek — w praktyce kontener aplikacji —
163
+ przez co input uciekał poza przewijany obszar i rozpychał dokument, dając **drugi pasek
164
+ przewijania** obok paska treści. Kontrolki zatrzymują teraz swój input u siebie
165
+ (`position: relative` na elemencie głównym).
166
+
167
+ Widoczne dopiero, gdy któraś z tych kontrolek trafi dostatecznie nisko w długiej, przewijanej
168
+ stronie — stąd ujawniło się na stronie mapy, która ma pięć przełączników.
169
+
170
+ - Geokoder per mapa — nowe wejście `[geocoder]` w `kpt-map`, symetryczne do `[engine]`.
171
+
172
+ - `[geocoder]` przyjmuje `KptMapGeocoderPort` i wygrywa nad `KPT_MAP_GEOCODER` z DI. Trafia do
173
+ obu konsumentów portu naraz: wyszukiwarki adresów i podpowiadania adresu w formularzu
174
+ „dodaj lokalizację". To samo wejście mają `kpt-map-search` i `kpt-map-create-dialog`, gdy stoją
175
+ poza mapą. Bez niego nic się nie zmienia — port przychodzi z DI, jak dotąd.
176
+ - **Po co.** Service Specific Terms §6.2 i §14.2 wiążą geokoder Google z **mapą** Google, a nie
177
+ z aplikacją. Odkąd `[engine]` pozwala postawić dwa silniki na jednej stronie, globalny provider
178
+ nie umiał tej reguły dotrzymać: musiałby złamać ją po jednej ze stron. Teraz mapa Leafleta
179
+ z geokoderem OSM i mapa Google z geokoderem Google mieszczą się obok siebie.
180
+ - W odróżnieniu od `[engine]` port czytany jest **reaktywnie**: podmiana zeruje listę podpowiedzi
181
+ i od następnej frazy pyta nowego dostawcę.
182
+ - `loadGoogleMapsApi(options)` wyeksportowany z `@konce-pt/angular/map` (plus typy
183
+ `KptGoogleLoadOptions` i `KptGoogleMapsApi`) — ten sam loader, którego używa silnik. Adapter
184
+ geokodera Google **musi** iść przez SDK, a nie przez REST: web service
185
+ `maps.googleapis.com/maps/api/geocode/json` odrzuca klucze ograniczone po `Referer`
186
+ („API keys with referer restrictions cannot be used with this API"), a to jedyna restrykcja
187
+ mająca sens dla klucza leżącego jawnie w bundlu przeglądarki. CORS na tym endpoincie działa,
188
+ więc odmowa przychodzi jako `200` z `REQUEST_DENIED` w treści — łatwo ją przeoczyć.
189
+ Samego adaptera biblioteka nadal nie dostarcza; przykład od początku do końca stoi
190
+ w playgroundzie.
191
+
192
+ - Drugi silnik mapy — Google Maps — i wybór silnika per instancja przez wejście `[engine]`.
193
+
194
+ - `provideKptGoogleMap({ apiKey, mapId, language?, region?, version?, minZoom?, maxZoom?,
195
+ scrollWheelZoom? })` obok `provideKptLeafletMap()`. **Zero nowych zależności npm**: Google Maps
196
+ JavaScript API dogrywa się skryptem przy pierwszej mapie, tak jak Leaflet ładuje się dynamicznym
197
+ `import()`. Paczka `@googlemaps/js-api-loader` dokładałaby wersję do pilnowania w zamian za te
198
+ same kilkanaście linijek.
199
+ - **`mapId` jest wymagany.** Bez niego nie działa `AdvancedMarkerElement`, a znaczniki tej
200
+ biblioteki to gotowy HTML z `marker-html.ts` — starsze `Marker` go nie przyjmie.
201
+ - Nowe wejście **`[engine]`** (`KptMapEngineFactory | null`) wygrywa nad `KPT_MAP_ENGINE` z DI.
202
+ Dwie mapy o różnych silnikach mieszczą się dzięki temu na jednej stronie bez komponentów-opakowań.
203
+ `KPT_MAP_ENGINE` staje się wstrzykiwany opcjonalnie, a brak silnika w obu źródłach daje czytelny
204
+ komunikat z instrukcją zamiast `NullInjectorError` z nazwą tokenu.
205
+ - Kontrakt `KptMapEngine` **nie zmienił się o ani jedną sygnaturę** — to był cel tego interfejsu
206
+ i teraz się zweryfikował. Grupowanie, popup, legenda, menu prawego przycisku, wyszukiwarka
207
+ i formularz dodawania działają w obu silnikach z tego samego kodu.
208
+ - Adapter Google liczy `project()`/`unproject()` **rdzeniem** (`core/geo.ts`), a nie
209
+ `map.getProjection()`: ten zwraca `null` do pierwszego `idle` i tak czy owak wymaga przeliczenia
210
+ na piksele kontenera. Wspólna matematyka znaczy, że popup ląduje piksel w piksel tak samo w obu
211
+ silnikach, zgadza się z grupowaniem i obsługuje zoom ułamkowy, którego Google używa przy gestach.
212
+ - Kliknięcie w znacznik łapie listener DOM ze `stopPropagation()`, a nie `gmp-click`
213
+ z `gmpClickable`. Bez zatrzymania propagacji to samo kliknięcie doszłoby do mapy i `onMapClick()`
214
+ zamknąłby popup w tej samej klatce, w której go otwieramy. Adapter Leafleta robi dokładnie to
215
+ samo przez `L.DomEvent.stopPropagation`; przy okazji odpada zależność od zachowania `gmp-click`
216
+ w danej wersji API.
217
+ - Krąg dokładności przy silniku Google jest elementem z tłem, a nie `google.maps.Circle`: Circle
218
+ przyjmuje kolory w opcjach, a kontrakt repo wymaga stylowania tokenami `var(--kpt-*)`. Średnicę
219
+ liczy `metersPerPixel()` z rdzenia i odświeża na `zoom_changed`. Silnik trafia na host jako
220
+ `[data-engine]`, więc różnicę widać w jednej regule SCSS.
221
+ - `[tileUrl]` i `[attribution]` są przy silniku Google **ignorowane** — kafle i atrybucję dostarcza
222
+ Google, a jej zasłonięcie albo usunięcie łamie warunki usługi. `setTileSource()` i
223
+ `invalidateSize()` to tam świadome no-opy.
224
+
225
+ **Dlaczego to w ogóle powstało.** Google Maps Platform Service Specific Terms §6.2 (Geocoding API)
226
+ i §14.2 (Places API) zabraniają używać ich wyników „in conjunction with a non-Google map".
227
+ Dopóki `kpt-map` umiał tylko Leaflet z kaflami OSM, każdy adapter Google pod `KPT_MAP_GEOCODER`
228
+ był niezgodny z umową. Silnik Google zdejmuje to u źródła. Sama biblioteka adaptera geokodera
229
+ Google **nie dostarcza** — jego miejsce jest w aplikacji, bo wolno go użyć tylko z jednym
230
+ z dwóch silników.
231
+
232
+ - Wyszukiwarka adresów w `kpt-map` — `[search]` (domyślnie wyłączona) pokazuje w rogu mapy pole
233
+ z podpowiedziami; wybór podpowiedzi przenosi widok na znalezione miejsce.
234
+
235
+ - Port `KPT_MAP_GEOCODER` dostaje **opcjonalną** metodę `search(query, options)` obok istniejącego
236
+ `reverse()`. Opcjonalną celowo: porty napisane w aplikacjach przed jej powstaniem kompilują się
237
+ bez zmian, a `kpt-map` bez niej po prostu nie renderuje pola. Nowe typy `KptMapSuggestion`
238
+ (`{ id, label, at?, bounds?, address? }`), `KptMapSearchResult` (to samo z wymaganym `at`)
239
+ i `KptMapSearchOptions` w publicznym API; `address` to ten sam `KptMapAddress`, co przy
240
+ geokodowaniu wstecznym — bez drugiego modelu adresu.
241
+ - `KptNominatimGeocoder` implementuje `search()` na `/search` i wchodzi w **tę samą kolejkę**
242
+ co `reverse()`: polityka Nominatim liczy jedno zapytanie na sekundę na całego klienta,
243
+ a nie na metodę.
244
+ - **Kolejka adaptera Nominatim liczy odstęp od zegara**, a nie stałym `delay(1000)` przed każdym
245
+ zapytaniem. Wcześniej pierwsze zapytanie po dowolnie długiej bezczynności też płaciło sekundę
246
+ — przy podpowiadaniu w trakcie pisania to różnica między „działa" a „zacina się". Zmiana
247
+ dotyczy także istniejącego `reverse()`; górna granica jednego zapytania na sekundę
248
+ zostaje nienaruszona.
249
+ - Nowe w rdzeniu (pure TS, publiczne API): `isFittableBounds(bounds, minSpan?)` i `KPT_MIN_FIT_SPAN`
250
+ (0,001°, ~100 m) — czy prostokąt warto wpasowywać w kadr, czy potraktować jak punkt.
251
+ Geokodery obrysowują pojedynczy adres prostokątem rzędu 0,0001°, więc `fitBounds` wjeżdżałby
252
+ na nim na górny limit zoomu, a dwa sąsiednie adresy lądowałyby w różnych przybliżeniach.
253
+ Próg wystarczy przekroczyć w jednej osi — długa, wąska ulica dalej zasługuje na `fitBounds`.
254
+ Testy: `packages/angular/map/src/core/geo.test.ts`.
255
+ - Nowe wejścia `kpt-map`: `search`, `searchPlaceholder`, `searchOptions`
256
+ (`minLength` = 3, `debounce` = 300 ms, `limit` = 5, `countryCodes`), `searchZoom` = 16,
257
+ `searchMarker` = true. Nowe wyjście `searchSelect`.
258
+ - Wynik z własnym zasięgiem (miasto, region) wchodzi w kadr przez `fitBounds`, a pojedynczy
259
+ punkt dostaje `[searchZoom]` — dzięki temu każdy adres ląduje w tym samym, przewidywalnym
260
+ przybliżeniu, niezależnie od tego, jak dostawca obrysował budynek.
261
+ - Zapytania są nachylone ku presetowi `[country]` (albo `[bounds]`) przez `viewbox`. To sam
262
+ bias — przestawia kolejność wyników i niczego nie odcina; adres z zagranicy dalej da się
263
+ znaleźć. Twarde `countrycodes` dokłada się dopiero przy `restrictToCountry` (mapa i tak nie
264
+ pozwoli przesunąć widoku poza kraj) albo jawnie przez `searchOptions.countryCodes`.
265
+ - Tymczasowa pinezka wyniku idzie tą samą drogą co reszta znaczników — przez
266
+ `engine.setMarkers()` — więc przesuwa się z mapą klatka po klatce i **nie wymaga niczego
267
+ od silnika**. Nie należy do wyniku grupowania, więc kliknięcie w nią nic nie otwiera.
268
+ Znika po wyczyszczeniu pola i po kliknięciu w mapę.
269
+ - Nowy komponent `KptMapSearch` (kpt-map-search) w publicznym API — jak legenda, do postawienia
270
+ także poza mapą. Świadomie **nie** stoi na `kpt-autocomplete`: tamten filtruje opcje po stronie
271
+ klienta (`includes(q)`), więc wynik geokodera tolerancyjny na literówki wypadałby z listy;
272
+ jego wartością jest sam `string`, więc nie ma czym przewieźć współrzędnych; i wymaga CDK
273
+ Overlay, którego mapa poza tym nie potrzebuje. Panel podpowiedzi rysuje więc warstwa Angulara
274
+ nad mapą, tak samo jak popup i menu prawego przycisku (`z-index: 5`, nad tamtymi).
275
+ - Wzorzec combobox z `aria-activedescendant` (fokus zostaje w `<input>`), klawiatura ↑/↓/Enter,
276
+ pierwszy Esc zamyka listę, drugi czyści pole; liczba wyników i „brak wyników" w `aria-live`.
277
+ - **Port przyjmuje dostawców, którzy nie podają współrzędnych na liście.** `search()` zwraca teraz
278
+ `KptMapSuggestion` (`at` opcjonalne) zamiast `KptMapSearchResult`, a port może dopisać
279
+ `resolve(suggestion)`, które dokłada `lat`/`lng` **po wybraniu pozycji**. Tak działa Google Places
280
+ Autocomplete: oddaje `placeId` i tekst, a współrzędne dociąga osobny Place Details — na tym stoi
281
+ jego rozliczanie sesyjne, więc bez tego kroku podpowiadanie z Google kosztowałoby pięć wywołań
282
+ Place Details na każdy zestaw podpowiedzi zamiast jednego na wybór.
283
+ Zmiana jest **wstecznie zgodna**: `KptMapSearchResult` to teraz `KptMapSuggestion` z wymaganym
284
+ `at`, więc adaptery zwracające pełne wyniki (Nominatim, Photon, każdy geokoder pytany wprost)
285
+ spełniają interfejs bez zmiany linijki, a `selected`/`searchSelect` dalej mają ten sam typ.
286
+ - Obsługa `resolve()` siedzi w `KptMapSearch`, nie w `kpt-map` — dzięki temu **`selected`
287
+ i `searchSelect` zawsze niosą współrzędne**, także gdy pole stoi poza mapą, i aplikacja nigdy
288
+ nie musi sprawdzać, czy `at` jest. Podpowiedź ze współrzędnymi emituje się natychmiast (zero
289
+ opóźnienia dla Nominatima); bez nich panel pokazuje „Szukam…" i `aria-busy`, a nieudane
290
+ dociągnięcie zostawia etykietę w polu i wraca do listy zamiast wywracać pole. Gdy port nie ma
291
+ `resolve()`, podpowiedzi bez `at` w ogóle nie trafiają na listę — pozycja, której kliknięcie
292
+ nie miałoby dokąd polecieć, jest gorsza niż jej brak.
293
+ - **`KptNominatimOptions.minIntervalMs`** — sekundowy odstęp przestał być stałą w kodzie.
294
+ Domyślna zależy od `baseUrl`: publiczna instancja OSM dostaje 1000 ms jak dotąd, własna 0,
295
+ bo regulamin OSMF nie obowiązuje na cudzym serwerze. `0` zdejmuje też kolejkowanie.
296
+ Wcześniej własna instancja Nominatima płaciła sekundę za każdą podpowiedź bez żadnego powodu
297
+ i nie dało się tego wyłączyć opcją.
298
+ - `provideKptMapGeocoderStub(address, results?, options?)` przyjmuje teraz listę podpowiedzi —
299
+ Storybook i testy dostają wyszukiwarkę bez ruchu sieciowego. Trzeci argument
300
+ (`{ deferCoordinates: true }`) każe atrapie udawać dostawcę bez współrzędnych na liście, żeby
301
+ ścieżkę `resolve()` dało się pokazać bez prawdziwego klucza API. Sygnatura wstecznie zgodna.
302
+ - Nowe tokeny `--kpt-map-search-width` i `--kpt-map-search-panel-max-height` oraz klucze
303
+ i18n `map.search.*` (EN + PL).
304
+ - Wyszukiwarkę pozycjonuje **opakowanie** `<div class="kpt-map__search">`, a nie klasa
305
+ postawiona wprost na `<kpt-map-search>`. Jeden element z obiema klasami znaczył dwie reguły
306
+ o identycznej specyficzności (0,1,0) w tej samej warstwie `kpt.components`, więc rozstrzygała
307
+ kolejność wstrzyknięcia stylów — a Angular wstrzykuje style dziecka po stylach rodzica.
308
+ `.kpt-map-search { position: relative }` (blok zawierający dla panelu podpowiedzi) wygrywało
309
+ z `absolute` rodzica, pole wypadało do normalnego przepływu za `.kpt-map__canvas`
310
+ (`height: 100%`) i znikało przycięte przez `overflow: hidden` widoku. Element był w DOM
311
+ z poprawnymi atrybutami — usterkę widać wyłącznie na zrzucie ekranu.
312
+
313
+ **Uwaga o polityce Nominatim.** Podpowiadanie w trakcie pisania z natury generuje serie zapytań,
314
+ więc pole broni się progiem `minLength` i `debounce`, a adapter wspólną kolejką i przerywaniem
315
+ porzuconych zapytań przez `AbortController`. Do produkcji dalej obowiązuje własna instancja
316
+ (`baseUrl`) albo dostawca komercyjny pod `KPT_MAP_GEOCODER` — publiczna instancja OSM zabrania
317
+ masowego odpytywania i może zablokować ruch bez uprzedzenia.
318
+
319
+ ## 0.7.0
320
+
321
+ ### Minor Changes
322
+
323
+ - Nowy komponent `KptRoadmap` (`kpt-roadmap`) w subpathu `@konce-pt/angular/roadmap` — zadania na
324
+ poziomej osi czasu w widoku typu Gantt. Przyklejona kolumna etykiet i nagłówek osi, przewijanie
325
+ w obu osiach, opcjonalna stała wysokość. Zoom: rok / kwartał / miesiąc / tydzień / dzień / godzina,
326
+ z oknem godzin roboczych. Numeracja tygodni wg ISO 8601 wraz z realnym zakresem dat; zmiana roku
327
+ oznaczona mocniejszą linią i czarnym znacznikiem. Kolumny zachowują równą szerokość mimo zmiany
328
+ czasu i różnej długości miesięcy. Grupy zwijane z paskiem zbiorczym, kamienie milowe, zależności
329
+ FS/SS/FF/SF z wykrywaniem naruszeń, wirtualizacja wierszy. Rdzeń obliczeń jako pure TS — subpath
330
+ eksportuje `buildTimeline`, `buildRows`, `resolveLinks`, `autoRange` i pomocniki kalendarza ISO.
331
+ Nowe klucze i18n `roadmap.*` (EN + PL).
332
+ - `KptTooltip`: nowe wejście `kptTooltipOnlyWhenTruncated` — dymek pojawia się wyłącznie wtedy, gdy
333
+ treść nie mieści się w elemencie i jest ucięta wielokropkiem. Przepełnienie sprawdzane jest przy
334
+ najechaniu (nie przy renderze), więc wynik pozostaje aktualny po zmianie szerokości czy powiększenia
335
+ bez użycia `ResizeObserver`. Pomiar obejmuje hosta i jego bezpośrednie dzieci, bo `text-overflow`
336
+ zwykle siedzi na wewnętrznym elemencie.
337
+ - `KptTooltip`: dymek podąża za hostem przy przewijaniu zagnieżdżonych kontenerów — wcześniej
338
+ zostawał w miejscu, gdy host przesuwał się we własnym obszarze przewijania.
339
+
340
+ ## 0.6.1
341
+
342
+ ### Patch Changes
343
+
344
+ - Wydanie porządkowe — wyrównanie wersji paczek. Bez zmian w API.
345
+
346
+ ## 0.6.0
347
+
348
+ ### Minor Changes
349
+
350
+ - Nowy entry point `@konce-pt/angular/grid` z dyrektywami `[kptGrid]`, `[kptCol]` i `[kptFlex]`
351
+ (sygnałowe inputy, zoneless) oraz skrótem `KPT_GRID_IMPORTS`. Ustawiają custom properties
352
+ inline, więc zmiana rozpiętości nie dodaje ani nie usuwa klas w DOM. Wymaga arkusza
353
+ `@konce-pt/grid`.
354
+ - `llms.txt` podpaczek `charts` i `grid` trafiają teraz do `dist` (wcześniej pomijane).
355
+
356
+ ### Patch Changes
357
+
358
+ - Updated dependencies
359
+ - @konce-pt/tokens@0.6.0
360
+
361
+ ## 0.1.2
362
+
363
+ ### Patch Changes
364
+
365
+ - Naprawa dokumentacji ładowania stylów: metodą główną dla Angular CLI jest `angular.json → styles[]`
366
+ (import '...css' w main.ts zostaje jako wariant dla Vite/webpack). Tokeny SCSS dostają mixiny
367
+ `kpt-root-tokens`/`kpt-dark-tokens` emitujące custom properties w runtime. Wejścia CSS dostają
368
+ warunek `types` + stub `.d.ts` (usuwa TS2882 w projektach z tsc). Bez zmian w API komponentów.
369
+
370
+ ## 0.1.1
371
+
372
+ ### Patch Changes
373
+
374
+ - Dodano profesjonalne README (EN/PL) i plik LICENSE do każdej paczki oraz metadane npm
375
+ (repository/homepage/bugs/keywords). Bez zmian w API komponentów.
376
+
377
+ ## 0.1.0
378
+
379
+ ### Minor Changes
380
+
381
+ - Pierwsze wydanie Koncept UI: design tokeny (OKLCH, motyw jasny/ciemny), bazowy CSS,
382
+ oraz komponenty Angular 22 — layout (app-shell/toolbar/sidenav/card/divider), formularze
383
+ na Signal Forms (input/select/checkbox/radio/switch/datepicker/form-field), flagowa tabela
384
+ danych (kpt-data-table) z paginatorem, oraz feedback (alert/dialog/toast/tooltip).
package/README.md CHANGED
@@ -93,40 +93,59 @@ import '@angular/cdk/overlay-prebuilt.css';
93
93
 
94
94
  ## Quick start
95
95
 
96
- A form powered by **Signal Forms** — no `ControlValueAccessor`, no `FormsModule`:
96
+ A login form powered by **Signal Forms** — no `ControlValueAccessor`, no `FormsModule`:
97
97
 
98
98
  ```ts
99
- import { Component, signal } from '@angular/core';
100
- import { form, required, email, FormField } from '@angular/forms/signals';
101
- import { KptFormField, KptInput, KptSelect, KptButton } from '@konce-pt/angular';
99
+ import { Component, computed, signal } from '@angular/core';
100
+ import { FormField, FormRoot, email, form, required, submit } from '@angular/forms/signals';
101
+ import { KptButton, KptFormField, KptInput } from '@konce-pt/angular';
102
102
 
103
103
  @Component({
104
104
  selector: 'app-login',
105
- imports: [FormField, KptFormField, KptInput, KptSelect, KptButton],
105
+ imports: [FormRoot, FormField, KptFormField, KptInput, KptButton],
106
106
  template: `
107
- <kpt-form-field label="E-mail" required>
108
- <kpt-input [formField]="loginForm.email" type="email" placeholder="you@company.com" />
109
- </kpt-form-field>
110
-
111
- <kpt-form-field label="Role">
112
- <kpt-select [formField]="loginForm.role" [options]="roles" placeholder="Pick a role…" />
113
- </kpt-form-field>
114
-
115
- <kpt-button (click)="submit()">Sign in</kpt-button>
107
+ <!-- formRoot sets novalidate, intercepts submit and calls submit() for you -->
108
+ <form [formRoot]="loginForm" (submit)="onSubmit()">
109
+ <kpt-form-field label="E-mail" [error]="emailError()" required controlId="f-email">
110
+ <kpt-input [formField]="loginForm.email" type="email" id="f-email" />
111
+ </kpt-form-field>
112
+
113
+ <kpt-form-field label="Password" [error]="passwordError()" required controlId="f-pass">
114
+ <kpt-input [formField]="loginForm.password" type="password" id="f-pass" />
115
+ </kpt-form-field>
116
+
117
+ <kpt-button type="submit" [disabled]="formInvalid()">Sign in</kpt-button>
118
+ </form>
116
119
  `,
117
120
  })
118
121
  export class Login {
119
- readonly model = signal({ email: '', role: null as string | null });
122
+ readonly model = signal({ email: '', password: '' });
120
123
  readonly loginForm = form(this.model, (p) => {
121
- required(p.email); email(p.email);
122
- required(p.role);
124
+ required(p.email, { message: 'E-mail is required' });
125
+ email(p.email, { message: 'Enter a valid e-mail address' });
126
+ required(p.password, { message: 'Password is required' });
123
127
  });
124
- readonly roles = [{ label: 'Admin', value: 'admin' }, { label: 'Editor', value: 'editor' }];
125
128
 
126
- submit() { if (this.loginForm().valid()) console.log(this.model()); }
129
+ // Validators stay pure — only the *message* waits for the field to be touched.
130
+ private errorFor(name: 'email' | 'password'): string | null {
131
+ const s = this.loginForm[name]();
132
+ return s.touched() && s.invalid() ? s.errors()[0]?.message ?? null : null;
133
+ }
134
+ readonly emailError = computed(() => this.errorFor('email'));
135
+ readonly passwordError = computed(() => this.errorFor('password'));
136
+ readonly formInvalid = computed(() => this.loginForm().invalid());
137
+
138
+ async onSubmit(): Promise<void> {
139
+ // submit() marks the whole tree as touched before running the action
140
+ await submit(this.loginForm, { action: async () => { console.log(this.model()); } });
141
+ }
127
142
  }
128
143
  ```
129
144
 
145
+ > Don't gate validators on UI state (`when: ({ state }) => state.touched()`) — it makes
146
+ > `loginForm().valid()` return `true` on an untouched form. Koncept UI controls already show
147
+ > the invalid state only once a field is touched.
148
+
130
149
  Data table with sorting, filtering, pagination and a custom cell template:
131
150
 
132
151
  ```html