@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.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/accordion/accordion.d.ts +45 -0
- package/dist/accordion/accordion.js +69 -0
- package/dist/alert/alert.d.ts +25 -0
- package/dist/alert/alert.js +31 -0
- package/dist/app-shell/app-shell.d.ts +94 -0
- package/dist/app-shell/app-shell.js +105 -0
- package/dist/auth/auth-types.d.ts +44 -0
- package/dist/auth/auth-types.js +2 -0
- package/dist/auth/auth.d.ts +76 -0
- package/dist/auth/auth.js +184 -0
- package/dist/autocomplete/autocomplete.d.ts +18 -0
- package/dist/autocomplete/autocomplete.js +72 -0
- package/dist/avatar/avatar.d.ts +33 -0
- package/dist/avatar/avatar.js +30 -0
- package/dist/avatar-group/avatar-group.d.ts +21 -0
- package/dist/avatar-group/avatar-group.js +15 -0
- package/dist/badge/badge.d.ts +25 -0
- package/dist/badge/badge.js +14 -0
- package/dist/bottom-sheet/bottom-sheet.d.ts +21 -0
- package/dist/bottom-sheet/bottom-sheet.js +22 -0
- package/dist/breadcrumb/breadcrumb.d.ts +19 -0
- package/dist/breadcrumb/breadcrumb.js +20 -0
- package/dist/button/button.d.ts +27 -0
- package/dist/button/button.js +49 -0
- package/dist/button-group/button-group.d.ts +25 -0
- package/dist/button-group/button-group.js +30 -0
- package/dist/card/card.d.ts +66 -0
- package/dist/card/card.js +96 -0
- package/dist/carousel/carousel.d.ts +20 -0
- package/dist/carousel/carousel.js +24 -0
- package/dist/charts/chart-legend.d.ts +23 -0
- package/dist/charts/chart-legend.js +9 -0
- package/dist/charts/chart-tooltip.d.ts +21 -0
- package/dist/charts/chart-tooltip.js +13 -0
- package/dist/charts/chart.d.ts +18 -0
- package/dist/charts/chart.js +231 -0
- package/dist/charts-entry/public-api.d.ts +4 -0
- package/dist/charts-entry/public-api.js +7 -0
- package/dist/checkbox/checkbox.d.ts +33 -0
- package/dist/checkbox/checkbox.js +29 -0
- package/dist/chip/chip.d.ts +22 -0
- package/dist/chip/chip.js +16 -0
- package/dist/chips-input/chips-input.d.ts +25 -0
- package/dist/chips-input/chips-input.js +54 -0
- package/dist/clock/clock.d.ts +17 -0
- package/dist/clock/clock.js +57 -0
- package/dist/color-picker/color-picker.d.ts +23 -0
- package/dist/color-picker/color-picker.js +28 -0
- package/dist/confirm/confirm-container.d.ts +13 -0
- package/dist/confirm/confirm-container.js +26 -0
- package/dist/confirm/confirm-store.d.ts +29 -0
- package/dist/confirm/confirm-store.js +52 -0
- package/dist/context-menu/context-menu.d.ts +21 -0
- package/dist/context-menu/context-menu.js +36 -0
- package/dist/data-table/data-table-types.d.ts +67 -0
- package/dist/data-table/data-table-types.js +1 -0
- package/dist/data-table/data-table.d.ts +62 -0
- package/dist/data-table/data-table.js +453 -0
- package/dist/data-table/export-csv.d.ts +7 -0
- package/dist/data-table/export-csv.js +19 -0
- package/dist/data-view/data-view.d.ts +21 -0
- package/dist/data-view/data-view.js +25 -0
- package/dist/date-range/date-range.d.ts +30 -0
- package/dist/date-range/date-range.js +155 -0
- package/dist/datepicker/datepicker.d.ts +39 -0
- package/dist/datepicker/datepicker.js +202 -0
- package/dist/dialog/dialog.d.ts +35 -0
- package/dist/dialog/dialog.js +59 -0
- package/dist/divider/divider.d.ts +16 -0
- package/dist/divider/divider.js +13 -0
- package/dist/drawer/drawer.d.ts +25 -0
- package/dist/drawer/drawer.js +25 -0
- package/dist/empty/empty.d.ts +18 -0
- package/dist/empty/empty.js +14 -0
- package/dist/fab/fab.d.ts +22 -0
- package/dist/fab/fab.js +17 -0
- package/dist/fieldset/fieldset.d.ts +22 -0
- package/dist/fieldset/fieldset.js +24 -0
- package/dist/file-upload/file-upload.d.ts +22 -0
- package/dist/file-upload/file-upload.js +50 -0
- package/dist/form-field/form-field-context.d.ts +23 -0
- package/dist/form-field/form-field-context.js +17 -0
- package/dist/form-field/form-field.d.ts +39 -0
- package/dist/form-field/form-field.js +41 -0
- package/dist/galleria/galleria.d.ts +22 -0
- package/dist/galleria/galleria.js +21 -0
- package/dist/grid/col.d.ts +39 -0
- package/dist/grid/col.js +32 -0
- package/dist/grid/flex.d.ts +45 -0
- package/dist/grid/flex.js +35 -0
- package/dist/grid/grid.d.ts +47 -0
- package/dist/grid/grid.js +38 -0
- package/dist/grid/layout.d.ts +18 -0
- package/dist/grid/layout.js +32 -0
- package/dist/grid-entry/public-api.d.ts +6 -0
- package/dist/grid-entry/public-api.js +8 -0
- package/dist/i18n/i18n-context.d.ts +45 -0
- package/dist/i18n/i18n-context.js +65 -0
- package/dist/icon/icon-registry.d.ts +16 -0
- package/dist/icon/icon-registry.js +43 -0
- package/dist/icon/icon.d.ts +17 -0
- package/dist/icon/icon.js +23 -0
- package/dist/icon-button/icon-button.d.ts +23 -0
- package/dist/icon-button/icon-button.js +14 -0
- package/dist/icons-entry/public-api.d.ts +12 -0
- package/dist/icons-entry/public-api.js +20 -0
- package/dist/image/image.d.ts +17 -0
- package/dist/image/image.js +16 -0
- package/dist/input/input.d.ts +41 -0
- package/dist/input/input.js +36 -0
- package/dist/input-mask/input-mask.d.ts +19 -0
- package/dist/input-mask/input-mask.js +65 -0
- package/dist/input-number/input-number.d.ts +28 -0
- package/dist/input-number/input-number.js +52 -0
- package/dist/input-otp/input-otp.d.ts +21 -0
- package/dist/input-otp/input-otp.js +59 -0
- package/dist/internal/cn.d.ts +9 -0
- package/dist/internal/cn.js +19 -0
- package/dist/internal/compose-refs.d.ts +6 -0
- package/dist/internal/compose-refs.js +16 -0
- package/dist/internal/overlay-container.d.ts +3 -0
- package/dist/internal/overlay-container.js +19 -0
- package/dist/internal/overlay.d.ts +26 -0
- package/dist/internal/overlay.js +64 -0
- package/dist/internal/slot.d.ts +23 -0
- package/dist/internal/slot.js +49 -0
- package/dist/internal/types.d.ts +25 -0
- package/dist/internal/types.js +1 -0
- package/dist/internal/use-breakpoint.d.ts +18 -0
- package/dist/internal/use-breakpoint.js +52 -0
- package/dist/internal/use-controllable-state.d.ts +11 -0
- package/dist/internal/use-controllable-state.js +18 -0
- package/dist/internal/use-id.d.ts +9 -0
- package/dist/internal/use-id.js +13 -0
- package/dist/knob/knob.d.ts +27 -0
- package/dist/knob/knob.js +86 -0
- package/dist/listbox/listbox.d.ts +28 -0
- package/dist/listbox/listbox.js +43 -0
- package/dist/map/map-create-dialog.d.ts +21 -0
- package/dist/map/map-create-dialog.js +196 -0
- package/dist/map/map-hours-field.d.ts +22 -0
- package/dist/map/map-hours-field.js +90 -0
- package/dist/map/map-image-field.d.ts +24 -0
- package/dist/map/map-image-field.js +61 -0
- package/dist/map/map-legend.d.ts +29 -0
- package/dist/map/map-legend.js +41 -0
- package/dist/map/map-poi-card.d.ts +42 -0
- package/dist/map/map-poi-card.js +152 -0
- package/dist/map/map-search.d.ts +37 -0
- package/dist/map/map-search.js +201 -0
- package/dist/map/map.d.ts +113 -0
- package/dist/map/map.js +425 -0
- package/dist/map/ports.d.ts +76 -0
- package/dist/map/ports.js +174 -0
- package/dist/map-entry/public-api.d.ts +20 -0
- package/dist/map-entry/public-api.js +19 -0
- package/dist/megamenu/megamenu.d.ts +17 -0
- package/dist/megamenu/megamenu.js +28 -0
- package/dist/menu/menu-item.d.ts +33 -0
- package/dist/menu/menu-item.js +1 -0
- package/dist/menu/menu-rows.d.ts +9 -0
- package/dist/menu/menu-rows.js +15 -0
- package/dist/menu/menu.d.ts +22 -0
- package/dist/menu/menu.js +31 -0
- package/dist/menubar/menubar.d.ts +16 -0
- package/dist/menubar/menubar.js +39 -0
- package/dist/meter-group/meter-group.d.ts +20 -0
- package/dist/meter-group/meter-group.js +24 -0
- package/dist/order-list/order-list.d.ts +18 -0
- package/dist/order-list/order-list.js +37 -0
- package/dist/paginator/paginator.d.ts +29 -0
- package/dist/paginator/paginator.js +45 -0
- package/dist/panel/panel.d.ts +31 -0
- package/dist/panel/panel.js +36 -0
- package/dist/password/password.d.ts +28 -0
- package/dist/password/password.js +37 -0
- package/dist/pick-list/pick-list.d.ts +28 -0
- package/dist/pick-list/pick-list.js +65 -0
- package/dist/popover/popover.d.ts +23 -0
- package/dist/popover/popover.js +27 -0
- package/dist/progress/progress.d.ts +19 -0
- package/dist/progress/progress.js +21 -0
- package/dist/public-api.d.ts +181 -0
- package/dist/public-api.js +105 -0
- package/dist/radio-group/radio-group.d.ts +24 -0
- package/dist/radio-group/radio-group.js +23 -0
- package/dist/rating/rating.d.ts +22 -0
- package/dist/rating/rating.js +30 -0
- package/dist/rich-text/lists.d.ts +17 -0
- package/dist/rich-text/lists.js +144 -0
- package/dist/rich-text/rich-text.d.ts +28 -0
- package/dist/rich-text/rich-text.js +654 -0
- package/dist/rich-text/sanitize.d.ts +10 -0
- package/dist/rich-text/sanitize.js +85 -0
- package/dist/rich-text/tables.d.ts +44 -0
- package/dist/rich-text/tables.js +268 -0
- package/dist/roadmap/roadmap.d.ts +37 -0
- package/dist/roadmap/roadmap.js +271 -0
- package/dist/roadmap-entry/public-api.d.ts +4 -0
- package/dist/roadmap-entry/public-api.js +7 -0
- package/dist/scroll-top/scroll-top.d.ts +16 -0
- package/dist/scroll-top/scroll-top.js +38 -0
- package/dist/select/select.d.ts +37 -0
- package/dist/select/select.js +117 -0
- package/dist/shared/types.d.ts +8 -0
- package/dist/shared/types.js +1 -0
- package/dist/sidenav/sidenav.d.ts +26 -0
- package/dist/sidenav/sidenav.js +41 -0
- package/dist/skeleton/skeleton.d.ts +20 -0
- package/dist/skeleton/skeleton.js +15 -0
- package/dist/slider/slider.d.ts +28 -0
- package/dist/slider/slider.js +30 -0
- package/dist/speed-dial/speed-dial.d.ts +20 -0
- package/dist/speed-dial/speed-dial.js +23 -0
- package/dist/spinner/spinner.d.ts +15 -0
- package/dist/spinner/spinner.js +14 -0
- package/dist/split-button/split-button.d.ts +28 -0
- package/dist/split-button/split-button.js +34 -0
- package/dist/splitter/splitter.d.ts +39 -0
- package/dist/splitter/splitter.js +80 -0
- package/dist/stepper/stepper.d.ts +51 -0
- package/dist/stepper/stepper.js +71 -0
- package/dist/switch/switch.d.ts +39 -0
- package/dist/switch/switch.js +30 -0
- package/dist/switch-group/switch-group.d.ts +29 -0
- package/dist/switch-group/switch-group.js +47 -0
- package/dist/tabs/tabs.d.ts +38 -0
- package/dist/tabs/tabs.js +74 -0
- package/dist/textarea/textarea.d.ts +28 -0
- package/dist/textarea/textarea.js +44 -0
- package/dist/timeline/timeline.d.ts +19 -0
- package/dist/timeline/timeline.js +12 -0
- package/dist/toast/toast-container.d.ts +12 -0
- package/dist/toast/toast-container.js +23 -0
- package/dist/toast/toast-store.d.ts +26 -0
- package/dist/toast/toast-store.js +51 -0
- package/dist/toolbar/toolbar.d.ts +29 -0
- package/dist/toolbar/toolbar.js +24 -0
- package/dist/tooltip/tooltip.d.ts +26 -0
- package/dist/tooltip/tooltip.js +113 -0
- package/dist/tree/tree.d.ts +31 -0
- package/dist/tree/tree.js +62 -0
- package/package.json +91 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { isFittableBounds } from '@konce-pt/map';
|
|
2
|
+
/** Geolokalizacja przeglądarki. Brak zgody czy brak API nie wywraca komponentu. */
|
|
3
|
+
export function browserGeolocation(options) {
|
|
4
|
+
return {
|
|
5
|
+
watch(onPosition, onError) {
|
|
6
|
+
if (typeof navigator === 'undefined' || !navigator.geolocation) {
|
|
7
|
+
onError({ kind: 'unsupported', message: 'Geolocation is not available in this browser.' });
|
|
8
|
+
return () => { };
|
|
9
|
+
}
|
|
10
|
+
const id = navigator.geolocation.watchPosition((position) => onPosition({
|
|
11
|
+
at: { lat: position.coords.latitude, lng: position.coords.longitude },
|
|
12
|
+
accuracy: position.coords.accuracy,
|
|
13
|
+
timestamp: position.timestamp,
|
|
14
|
+
}), (error) => {
|
|
15
|
+
onPosition(null);
|
|
16
|
+
onError({ kind: kindOf(error), message: error.message });
|
|
17
|
+
}, { enableHighAccuracy: true, maximumAge: 10_000, timeout: 15_000, ...options });
|
|
18
|
+
return () => navigator.geolocation.clearWatch(id);
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function kindOf(error) {
|
|
23
|
+
if (error.code === error.PERMISSION_DENIED)
|
|
24
|
+
return 'denied';
|
|
25
|
+
if (error.code === error.POSITION_UNAVAILABLE)
|
|
26
|
+
return 'unavailable';
|
|
27
|
+
return 'timeout';
|
|
28
|
+
}
|
|
29
|
+
/** Stała pozycja — do dem, testów i SSR, gdzie prawdziwej geolokalizacji nie ma. */
|
|
30
|
+
export function geolocationStub(at, accuracy = 30) {
|
|
31
|
+
return {
|
|
32
|
+
watch(onPosition) {
|
|
33
|
+
onPosition({ at, accuracy, timestamp: Date.now() });
|
|
34
|
+
return () => { };
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Publiczna instancja OSM. Ma twardą politykę użycia: jedno zapytanie na sekundę. */
|
|
39
|
+
const NOMINATIM_URL = 'https://nominatim.openstreetmap.org';
|
|
40
|
+
const NOMINATIM_MIN_INTERVAL_MS = 1000;
|
|
41
|
+
/**
|
|
42
|
+
* Adapter Nominatim (OpenStreetMap): geokodowanie wsteczne i podpowiedzi adresowe.
|
|
43
|
+
*
|
|
44
|
+
* Kolejkuje zapytania z odstępem sekundy, bo tego wymaga regulamin publicznej instancji.
|
|
45
|
+
* Odstęp liczony od zegara, a nie stałym opóźnieniem przed każdym zapytaniem — inaczej
|
|
46
|
+
* pierwsza podpowiedź po minucie bezczynności też płaciłaby sekundę.
|
|
47
|
+
*
|
|
48
|
+
* **Nie rejestruj publicznej instancji w produkcji bez przeczytania jej polityki użycia.**
|
|
49
|
+
*/
|
|
50
|
+
export function createNominatimGeocoder(options = {}) {
|
|
51
|
+
const baseUrl = options.baseUrl ?? NOMINATIM_URL;
|
|
52
|
+
const minInterval = options.minIntervalMs ?? (options.baseUrl ? 0 : NOMINATIM_MIN_INTERVAL_MS);
|
|
53
|
+
/** Ogon kolejki — każde kolejne zapytanie czeka na swoją kolej. */
|
|
54
|
+
let next = Promise.resolve();
|
|
55
|
+
let lastAt = 0;
|
|
56
|
+
const slot = () => {
|
|
57
|
+
if (minInterval <= 0)
|
|
58
|
+
return Promise.resolve();
|
|
59
|
+
const mine = next.then(async () => {
|
|
60
|
+
const wait = lastAt + minInterval - Date.now();
|
|
61
|
+
if (wait > 0)
|
|
62
|
+
await new Promise((resolve) => setTimeout(resolve, wait));
|
|
63
|
+
lastAt = Date.now();
|
|
64
|
+
});
|
|
65
|
+
next = mine;
|
|
66
|
+
return mine;
|
|
67
|
+
};
|
|
68
|
+
const language = (override) => override ?? options.language ?? (typeof navigator === 'undefined' ? 'en' : navigator.language);
|
|
69
|
+
return {
|
|
70
|
+
async reverse(at, opts) {
|
|
71
|
+
if (typeof fetch === 'undefined')
|
|
72
|
+
return null;
|
|
73
|
+
await slot();
|
|
74
|
+
if (opts?.signal?.aborted)
|
|
75
|
+
return null;
|
|
76
|
+
const url = new URL('/reverse', baseUrl);
|
|
77
|
+
url.searchParams.set('format', 'jsonv2');
|
|
78
|
+
url.searchParams.set('lat', String(at.lat));
|
|
79
|
+
url.searchParams.set('lon', String(at.lng));
|
|
80
|
+
url.searchParams.set('zoom', String(options.zoom ?? 18));
|
|
81
|
+
url.searchParams.set('addressdetails', '1');
|
|
82
|
+
try {
|
|
83
|
+
const response = await fetch(url, {
|
|
84
|
+
signal: opts?.signal ?? null,
|
|
85
|
+
headers: { 'Accept-Language': language(opts?.language) },
|
|
86
|
+
});
|
|
87
|
+
if (!response.ok)
|
|
88
|
+
return null;
|
|
89
|
+
return toAddress((await response.json()));
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Brak adresu to nie awaria formularza — pole zostaje puste.
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
async search(query, opts) {
|
|
97
|
+
if (typeof fetch === 'undefined' || !query.trim())
|
|
98
|
+
return [];
|
|
99
|
+
await slot();
|
|
100
|
+
if (opts?.signal?.aborted)
|
|
101
|
+
return [];
|
|
102
|
+
const url = new URL('/search', baseUrl);
|
|
103
|
+
url.searchParams.set('format', 'jsonv2');
|
|
104
|
+
url.searchParams.set('q', query);
|
|
105
|
+
url.searchParams.set('addressdetails', '1');
|
|
106
|
+
url.searchParams.set('limit', String(opts?.limit ?? 5));
|
|
107
|
+
if (opts?.viewbox) {
|
|
108
|
+
const { west, south, east, north } = opts.viewbox;
|
|
109
|
+
url.searchParams.set('viewbox', `${west},${south},${east},${north}`);
|
|
110
|
+
}
|
|
111
|
+
if (opts?.countryCodes?.length) {
|
|
112
|
+
url.searchParams.set('countrycodes', opts.countryCodes.join(',').toLowerCase());
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const response = await fetch(url, {
|
|
116
|
+
signal: opts?.signal ?? null,
|
|
117
|
+
headers: { 'Accept-Language': language(opts?.language) },
|
|
118
|
+
});
|
|
119
|
+
if (!response.ok)
|
|
120
|
+
return [];
|
|
121
|
+
const places = (await response.json());
|
|
122
|
+
return places.map(toSuggestion);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/** Port bez sieci: stała lista podpowiedzi i stały adres. Do dem i testów. */
|
|
131
|
+
export function createGeocoderStub(options = {}) {
|
|
132
|
+
return {
|
|
133
|
+
reverse: async () => options.address ?? null,
|
|
134
|
+
search: async (query) => {
|
|
135
|
+
const needle = query.trim().toLowerCase();
|
|
136
|
+
const all = options.suggestions ?? [];
|
|
137
|
+
return needle ? all.filter((s) => s.label.toLowerCase().includes(needle)) : all;
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function toAddress(place) {
|
|
142
|
+
const address = place.address;
|
|
143
|
+
if (!address)
|
|
144
|
+
return null;
|
|
145
|
+
const street = address.road ?? '';
|
|
146
|
+
const houseNumber = address.house_number ?? '';
|
|
147
|
+
return {
|
|
148
|
+
street,
|
|
149
|
+
houseNumber,
|
|
150
|
+
// Jedno pole „Adres" — ulica z numerem, bo tak wypełnia się formularz.
|
|
151
|
+
address: [street, houseNumber].filter(Boolean).join(' '),
|
|
152
|
+
city: address.city ?? address.town ?? address.village ?? address.municipality ?? address.hamlet ?? '',
|
|
153
|
+
postcode: address.postcode ?? '',
|
|
154
|
+
state: address.state ?? '',
|
|
155
|
+
county: address.county ?? '',
|
|
156
|
+
country: address.country ?? '',
|
|
157
|
+
countryCode: (address.country_code ?? '').toUpperCase(),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function toSuggestion(place) {
|
|
161
|
+
const at = { lat: Number(place.lat ?? 0), lng: Number(place.lon ?? 0) };
|
|
162
|
+
const box = place.boundingbox;
|
|
163
|
+
const bounds = box
|
|
164
|
+
? { south: Number(box[0]), north: Number(box[1]), west: Number(box[2]), east: Number(box[3]) }
|
|
165
|
+
: undefined;
|
|
166
|
+
return {
|
|
167
|
+
id: String(place.place_id ?? place.display_name ?? at.lat + ':' + at.lng),
|
|
168
|
+
label: place.display_name ?? '',
|
|
169
|
+
at,
|
|
170
|
+
// Punktowy „prostokąt" pojedynczego budynku nie nadaje się do wpasowania w kadr.
|
|
171
|
+
bounds: bounds && isFittableBounds(bounds) ? bounds : undefined,
|
|
172
|
+
address: toAddress(place) ?? undefined,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { KptMap } from '../map/map.tsx';
|
|
2
|
+
export type { KptMapProps, KptMapPopupContext, KptMapCreateFormApi } from '../map/map.tsx';
|
|
3
|
+
export { KptMapLegend } from '../map/map-legend.tsx';
|
|
4
|
+
export type { KptMapLegendProps } from '../map/map-legend.tsx';
|
|
5
|
+
export { KptMapSearch } from '../map/map-search.tsx';
|
|
6
|
+
export type { KptMapSearchProps } from '../map/map-search.tsx';
|
|
7
|
+
export { KptMapPoiCard } from '../map/map-poi-card.tsx';
|
|
8
|
+
export type { KptMapPoiCardProps } from '../map/map-poi-card.tsx';
|
|
9
|
+
export { KptMapCreateDialog } from '../map/map-create-dialog.tsx';
|
|
10
|
+
export type { KptMapCreateDialogProps } from '../map/map-create-dialog.tsx';
|
|
11
|
+
export { KptMapHoursField } from '../map/map-hours-field.tsx';
|
|
12
|
+
export type { KptMapHoursFieldProps } from '../map/map-hours-field.tsx';
|
|
13
|
+
export { KptMapImageField } from '../map/map-image-field.tsx';
|
|
14
|
+
export type { KptMapImageFieldProps } from '../map/map-image-field.tsx';
|
|
15
|
+
export { browserGeolocation, geolocationStub, createNominatimGeocoder, createGeocoderStub } from '../map/ports.ts';
|
|
16
|
+
export type { KptMapGeocoderPort, KptMapGeolocationPort, KptMapSearchOptions, KptNominatimOptions, } from '../map/ports.ts';
|
|
17
|
+
export { createLeafletEngine, createGoogleEngine, loadGoogleMapsApi } from '@konce-pt/map';
|
|
18
|
+
export type { KptLeafletOptions, KptGoogleOptions, KptGoogleLoadOptions, KptGoogleMapsApi, } from '@konce-pt/map';
|
|
19
|
+
export type { KptLatLng, KptMapBounds, KptMapPoint, KptMapColor, KptMapColorRole, KptMapMarkerShape, KptMapCategory, KptMapLocation, KptMapCluster, KptMapClusterEntry, KptMapFieldDef, KptMapFieldOption, KptMapFieldType, KptMapUserPosition, KptMapGeolocationError, KptMapGeolocationErrorKind, KptMapCreateEvent, KptMapSuggestion, KptMapSearchResult, KptMapView, KptMapAddress, KptMapAddressPart, KptMapWeekday, KptMapHoursRange, KptMapDayHours, KptMapOpeningHours, KptMapPoi, KptMapPoiFact, KptMapEngine, KptMapEngineFactory, KptMapEngineInit, KptMapEngineEvents, KptMapEngineKind, KptMapPointerEvent, KptMapRenderedMarker, KptClusterOptions, KptMapCountryPreset, KptMapHoursGroup, } from '@konce-pt/map';
|
|
20
|
+
export { isCluster, clusterLocations, dominantCategory, countByCategory, isSplittable, KPT_CLUSTER_DEFAULTS, KPT_MAP_COUNTRIES, KPT_MAP_DEFAULT_COUNTRY, countryPreset, countryCodes, resolveCountryView, centerOfBounds, project, unproject, boundsOf, boundsCenter, isFittableBounds, KPT_MIN_FIT_SPAN, boundsContain, padBounds, haversine, metersPerPixel, zoomForBounds, clampLat, normalizeLng, worldSize, KPT_TILE_SIZE, KPT_MAX_LATITUDE, KPT_EARTH_RADIUS, renderMarkerHtml, renderClusterHtml, renderUserMarkerHtml, colorVarOf, contrastVarOf, foregroundVarOf, clusterTier, KPT_MAP_DEFAULT_TILE_URL, KPT_MAP_DEFAULT_ATTRIBUTION, parseTime, formatTime, isoWeekday, isOpenAt, isAlwaysOpen, nextChange, weekdayName, groupWeek, emptyWeek, formatDistance, formatLatLng, } from '@konce-pt/map';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @konce-pt/react/map — mapa z konfigurowalnymi znacznikami. Osobny subpath, bo silnik mapowy
|
|
3
|
+
* i style mapy są ciężkie względem rdzenia biblioteki i nie każda aplikacja ich potrzebuje.
|
|
4
|
+
*
|
|
5
|
+
* Silnik Leaflet wymaga zainstalowanej paczki `leaflet` (peer opcjonalny) i arkusza
|
|
6
|
+
* `leaflet/dist/leaflet.css` po stronie aplikacji.
|
|
7
|
+
*/
|
|
8
|
+
export { KptMap } from "../map/map.js";
|
|
9
|
+
export { KptMapLegend } from "../map/map-legend.js";
|
|
10
|
+
export { KptMapSearch } from "../map/map-search.js";
|
|
11
|
+
export { KptMapPoiCard } from "../map/map-poi-card.js";
|
|
12
|
+
export { KptMapCreateDialog } from "../map/map-create-dialog.js";
|
|
13
|
+
export { KptMapHoursField } from "../map/map-hours-field.js";
|
|
14
|
+
export { KptMapImageField } from "../map/map-image-field.js";
|
|
15
|
+
// Porty: geokoder i geolokalizacja. W Reakcie podaje się je propem, nie przez DI.
|
|
16
|
+
export { browserGeolocation, geolocationStub, createNominatimGeocoder, createGeocoderStub } from "../map/ports.js";
|
|
17
|
+
// Silniki i rdzeń (pure TS) — wspólne z portem Angulara.
|
|
18
|
+
export { createLeafletEngine, createGoogleEngine, loadGoogleMapsApi } from '@konce-pt/map';
|
|
19
|
+
export { isCluster, clusterLocations, dominantCategory, countByCategory, isSplittable, KPT_CLUSTER_DEFAULTS, KPT_MAP_COUNTRIES, KPT_MAP_DEFAULT_COUNTRY, countryPreset, countryCodes, resolveCountryView, centerOfBounds, project, unproject, boundsOf, boundsCenter, isFittableBounds, KPT_MIN_FIT_SPAN, boundsContain, padBounds, haversine, metersPerPixel, zoomForBounds, clampLat, normalizeLng, worldSize, KPT_TILE_SIZE, KPT_MAX_LATITUDE, KPT_EARTH_RADIUS, renderMarkerHtml, renderClusterHtml, renderUserMarkerHtml, colorVarOf, contrastVarOf, foregroundVarOf, clusterTier, KPT_MAP_DEFAULT_TILE_URL, KPT_MAP_DEFAULT_ATTRIBUTION, parseTime, formatTime, isoWeekday, isOpenAt, isAlwaysOpen, nextChange, weekdayName, groupWeek, emptyWeek, formatDistance, formatLatLng, } from '@konce-pt/map';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ComponentPropsWithRef } from 'react';
|
|
2
|
+
import type { KptMenuItem } from '../menu/menu-item.ts';
|
|
3
|
+
export interface KptMegamenuProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'onSelect'> {
|
|
4
|
+
items?: KptMenuItem<T>[];
|
|
5
|
+
onSelect?: (item: KptMenuItem<T>) => void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Mega-menu — pozioma belka z szerokim, wielokolumnowym panelem.
|
|
9
|
+
* Struktura: pozycja górna → `items` (kolumny) → `items` kolumny (odnośniki).
|
|
10
|
+
*
|
|
11
|
+
* Panel kotwiczy się do CAŁEJ belki, nie do pojedynczego przycisku: jest szeroki, więc ma
|
|
12
|
+
* zaczynać się przy krawędzi menu, a nie przy przypadkowej pozycji, w którą kliknięto.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* <KptMegamenu items={items} onSelect={onSelect} />
|
|
16
|
+
*/
|
|
17
|
+
export declare function KptMegamenu<T>({ items, onSelect, className, ...rest }: Readonly<KptMegamenuProps<T>>): import("react").JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useState } from 'react';
|
|
3
|
+
import { cn } from "../internal/cn.js";
|
|
4
|
+
import { KptOverlay } from "../internal/overlay.js";
|
|
5
|
+
import { KptIcon } from "../icon/icon.js";
|
|
6
|
+
/**
|
|
7
|
+
* Mega-menu — pozioma belka z szerokim, wielokolumnowym panelem.
|
|
8
|
+
* Struktura: pozycja górna → `items` (kolumny) → `items` kolumny (odnośniki).
|
|
9
|
+
*
|
|
10
|
+
* Panel kotwiczy się do CAŁEJ belki, nie do pojedynczego przycisku: jest szeroki, więc ma
|
|
11
|
+
* zaczynać się przy krawędzi menu, a nie przy przypadkowej pozycji, w którą kliknięto.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <KptMegamenu items={items} onSelect={onSelect} />
|
|
15
|
+
*/
|
|
16
|
+
export function KptMegamenu({ items = [], onSelect, className, ...rest }) {
|
|
17
|
+
const navRef = useRef(null);
|
|
18
|
+
const [openIndex, setOpenIndex] = useState(null);
|
|
19
|
+
const closeAll = () => setOpenIndex(null);
|
|
20
|
+
const columns = openIndex === null ? [] : (items[openIndex]?.items ?? []);
|
|
21
|
+
const pick = (link) => {
|
|
22
|
+
if (link.disabled)
|
|
23
|
+
return;
|
|
24
|
+
onSelect?.(link);
|
|
25
|
+
closeAll();
|
|
26
|
+
};
|
|
27
|
+
return (_jsxs("div", { className: cn('kpt-megamenu-host', className), ...rest, children: [_jsx("nav", { className: "kpt-megamenu", role: "menubar", ref: navRef, children: items.map((item, index) => (_jsxs("button", { type: "button", className: cn('kpt-megamenu__top', openIndex === index && 'is-open'), role: "menuitem", onClick: () => setOpenIndex((current) => (current === index ? null : index)), children: [item.icon ? _jsx(KptIcon, { name: item.icon }) : null, _jsx("span", { children: item.label }), _jsx(KptIcon, { name: "chevron-down", className: "kpt-megamenu__caret" })] }, index))) }), _jsx(KptOverlay, { open: openIndex !== null, anchor: navRef.current, onClose: closeAll, children: _jsx("div", { className: "kpt-megamenu__panel", role: "menu", children: columns.map((column, columnIndex) => (_jsxs("div", { className: "kpt-megamenu__column", children: [column.label ? _jsx("div", { className: "kpt-megamenu__col-title", children: column.label }) : null, (column.items ?? []).map((link, linkIndex) => (_jsxs("button", { type: "button", className: "kpt-megamenu__link", disabled: link.disabled, onClick: () => pick(link), children: [link.icon ? _jsx(KptIcon, { name: link.icon }) : null, _jsx("span", { children: link.label })] }, linkIndex)))] }, columnIndex))) }) })] }));
|
|
28
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { KptBadgeVariant } from '../badge/badge.tsx';
|
|
2
|
+
/** Awatar wiersza nagłówkowego — te same dane, które przyjmuje `KptAvatar`. */
|
|
3
|
+
export interface KptMenuItemAvatar {
|
|
4
|
+
src?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
icon?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Pozycja menu — wspólny kształt dla `KptMenu` i `KptContextMenu`. */
|
|
9
|
+
export interface KptMenuItem<T = unknown> {
|
|
10
|
+
label?: string;
|
|
11
|
+
value?: T;
|
|
12
|
+
icon?: string;
|
|
13
|
+
/** Druga linia tekstu: e-mail, rola, opis akcji. */
|
|
14
|
+
description?: string;
|
|
15
|
+
/** Odznaka po prawej stronie pozycji (licznik lub krótka etykieta). */
|
|
16
|
+
badge?: string | number;
|
|
17
|
+
/** Wariant odznaki — domyślnie `primary`. */
|
|
18
|
+
badgeVariant?: KptBadgeVariant;
|
|
19
|
+
/** Awatar po lewej — typowo w wierszu nagłówka menu użytkownika. */
|
|
20
|
+
avatar?: KptMenuItemAvatar;
|
|
21
|
+
/**
|
|
22
|
+
* Wiersz nagłówkowy: nieklikalny blok tekstu zamiast pozycji menu. Mieści dane zalogowanego
|
|
23
|
+
* użytkownika (`avatar` + `label` + `description`) albo opis funkcji menu.
|
|
24
|
+
*/
|
|
25
|
+
header?: boolean;
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
danger?: boolean;
|
|
28
|
+
separator?: boolean;
|
|
29
|
+
/** Pozycje podrzędne (menubar / megamenu). */
|
|
30
|
+
items?: KptMenuItem<T>[];
|
|
31
|
+
/** Adres odnośnika (menubar / megamenu). */
|
|
32
|
+
url?: string;
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { KptMenuItem } from './menu-item.ts';
|
|
3
|
+
export interface MenuRowsProps<T> {
|
|
4
|
+
items: KptMenuItem<T>[];
|
|
5
|
+
/** Prefiks bloku BEM: `kpt-menu` albo `kpt-context-menu`. */
|
|
6
|
+
block: string;
|
|
7
|
+
onPick: (item: KptMenuItem<T>) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function MenuRows<T>({ items, block, onPick }: Readonly<MenuRowsProps<T>>): ReactNode;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { KptAvatar } from "../avatar/avatar.js";
|
|
3
|
+
import { KptBadge } from "../badge/badge.js";
|
|
4
|
+
import { KptIcon } from "../icon/icon.js";
|
|
5
|
+
export function MenuRows({ items, block, onPick }) {
|
|
6
|
+
return items.map((item, index) => {
|
|
7
|
+
if (item.separator) {
|
|
8
|
+
return _jsx("div", { className: `${block}__separator`, role: "separator" }, index);
|
|
9
|
+
}
|
|
10
|
+
if (item.header) {
|
|
11
|
+
return (_jsxs("div", { className: `${block}__header`, role: "presentation", children: [item.avatar ? (_jsx(KptAvatar, { src: item.avatar.src ?? null, name: item.avatar.name ?? '', icon: item.avatar.icon ?? null, size: "sm" })) : item.icon ? (_jsx(KptIcon, { name: item.icon })) : null, _jsxs("span", { className: `${block}__text`, children: [item.label ? _jsx("span", { className: `${block}__label`, children: item.label }) : null, item.description ? _jsx("span", { className: `${block}__description`, children: item.description }) : null] })] }, index));
|
|
12
|
+
}
|
|
13
|
+
return (_jsxs("button", { type: "button", className: `${block}__item${item.danger ? ` ${block}__item--danger` : ''}`, role: "menuitem", disabled: item.disabled, onClick: () => onPick(item), children: [item.icon ? _jsx(KptIcon, { name: item.icon }) : null, _jsxs("span", { className: `${block}__text`, children: [_jsx("span", { className: `${block}__label`, children: item.label }), item.description ? _jsx("span", { className: `${block}__description`, children: item.description }) : null] }), item.badge !== undefined && item.badge !== null ? (_jsx(KptBadge, { className: `${block}__badge`, value: item.badge, variant: item.badgeVariant ?? 'primary' })) : null] }, index));
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ComponentPropsWithRef, type ReactNode } from 'react';
|
|
2
|
+
import type { KptMenuItem } from './menu-item.ts';
|
|
3
|
+
export interface KptMenuProps<T> extends Omit<ComponentPropsWithRef<'span'>, 'onSelect'> {
|
|
4
|
+
items?: KptMenuItem<T>[];
|
|
5
|
+
/** Element otwierający menu — renderowany w miejscu komponentu. */
|
|
6
|
+
trigger: ReactNode;
|
|
7
|
+
/** Wybrana pozycja; pozycje `header`, `separator` i wyłączone nigdy tu nie trafiają. */
|
|
8
|
+
onSelect?: (item: KptMenuItem<T>) => void;
|
|
9
|
+
open?: boolean;
|
|
10
|
+
defaultOpen?: boolean;
|
|
11
|
+
onOpenChange?: (open: boolean) => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Menu rozwijane (dropdown). Pozycje przez `items`, wyzwalacz przez prop `trigger`.
|
|
15
|
+
*
|
|
16
|
+
* Panel renderuje się w portalu (`KptOverlay`), więc nie potrzeba żadnych stylów overlay —
|
|
17
|
+
* w odróżnieniu od portu Angulara, który stoi na CDK.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* <KptMenu items={items} onSelect={onSelect} trigger={<KptButton variant="outline">Opcje</KptButton>} />
|
|
21
|
+
*/
|
|
22
|
+
export declare function KptMenu<T>({ items, trigger, onSelect, open, defaultOpen, onOpenChange, className, ...rest }: Readonly<KptMenuProps<T>>): import("react").JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useRef } 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 { MenuRows } from "./menu-rows.js";
|
|
7
|
+
/**
|
|
8
|
+
* Menu rozwijane (dropdown). Pozycje przez `items`, wyzwalacz przez prop `trigger`.
|
|
9
|
+
*
|
|
10
|
+
* Panel renderuje się w portalu (`KptOverlay`), więc nie potrzeba żadnych stylów overlay —
|
|
11
|
+
* w odróżnieniu od portu Angulara, który stoi na CDK.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <KptMenu items={items} onSelect={onSelect} trigger={<KptButton variant="outline">Opcje</KptButton>} />
|
|
15
|
+
*/
|
|
16
|
+
export function KptMenu({ items = [], trigger, onSelect, open, defaultOpen = false, onOpenChange, className, ...rest }) {
|
|
17
|
+
const triggerRef = useRef(null);
|
|
18
|
+
const [isOpen, setOpen] = useControllableState({
|
|
19
|
+
value: open,
|
|
20
|
+
defaultValue: defaultOpen,
|
|
21
|
+
onChange: onOpenChange,
|
|
22
|
+
});
|
|
23
|
+
const close = useCallback(() => setOpen(false), [setOpen]);
|
|
24
|
+
const pick = (item) => {
|
|
25
|
+
if (item.disabled)
|
|
26
|
+
return;
|
|
27
|
+
onSelect?.(item);
|
|
28
|
+
close();
|
|
29
|
+
};
|
|
30
|
+
return (_jsxs("span", { className: cn('kpt-menu-host', className), ...rest, children: [_jsx("span", { className: "kpt-menu__trigger", ref: triggerRef, onClick: () => setOpen(!isOpen), children: trigger }), _jsx(KptOverlay, { open: isOpen, anchor: triggerRef.current, onClose: close, children: _jsx("div", { className: "kpt-menu__panel", role: "menu", children: _jsx(MenuRows, { items: items, block: "kpt-menu", onPick: pick }) }) })] }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ComponentPropsWithRef } from 'react';
|
|
2
|
+
import type { KptMenuItem } from '../menu/menu-item.ts';
|
|
3
|
+
export interface KptMenubarProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'onSelect'> {
|
|
4
|
+
items?: KptMenuItem<T>[];
|
|
5
|
+
onSelect?: (item: KptMenuItem<T>) => void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Pozioma belka menu z rozwijanymi pod-menu. Pozycja bez `items` działa jak akcja lub link.
|
|
9
|
+
*
|
|
10
|
+
* Pod-menu renderuje się w portalu, więc rodzic z `overflow: hidden` go nie przycina — to ta sama
|
|
11
|
+
* korzyść, dla której port Angulara sięga po CDK Overlay, tylko bez CDK.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <KptMenubar items={items} onSelect={onSelect} />
|
|
15
|
+
*/
|
|
16
|
+
export declare function KptMenubar<T>({ items, onSelect, className, ...rest }: Readonly<KptMenubarProps<T>>): import("react").JSX.Element;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useState } from 'react';
|
|
3
|
+
import { cn } from "../internal/cn.js";
|
|
4
|
+
import { KptOverlay } from "../internal/overlay.js";
|
|
5
|
+
import { KptIcon } from "../icon/icon.js";
|
|
6
|
+
/**
|
|
7
|
+
* Pozioma belka menu z rozwijanymi pod-menu. Pozycja bez `items` działa jak akcja lub link.
|
|
8
|
+
*
|
|
9
|
+
* Pod-menu renderuje się w portalu, więc rodzic z `overflow: hidden` go nie przycina — to ta sama
|
|
10
|
+
* korzyść, dla której port Angulara sięga po CDK Overlay, tylko bez CDK.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* <KptMenubar items={items} onSelect={onSelect} />
|
|
14
|
+
*/
|
|
15
|
+
export function KptMenubar({ items = [], onSelect, className, ...rest }) {
|
|
16
|
+
const [openIndex, setOpenIndex] = useState(null);
|
|
17
|
+
const buttonsRef = useRef([]);
|
|
18
|
+
const closeAll = () => setOpenIndex(null);
|
|
19
|
+
const toggle = (index, item) => {
|
|
20
|
+
if (item.disabled)
|
|
21
|
+
return;
|
|
22
|
+
if (item.items?.length) {
|
|
23
|
+
setOpenIndex((current) => (current === index ? null : index));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
onSelect?.(item);
|
|
27
|
+
closeAll();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const pick = (item) => {
|
|
31
|
+
if (item.disabled)
|
|
32
|
+
return;
|
|
33
|
+
onSelect?.(item);
|
|
34
|
+
closeAll();
|
|
35
|
+
};
|
|
36
|
+
return (_jsx("div", { className: cn('kpt-menubar-host', className), ...rest, children: _jsx("nav", { className: "kpt-menubar", role: "menubar", children: items.map((item, index) => (_jsxs("div", { className: "kpt-menubar__group", children: [_jsxs("button", { ref: (el) => {
|
|
37
|
+
buttonsRef.current[index] = el;
|
|
38
|
+
}, type: "button", className: cn('kpt-menubar__top', openIndex === index && 'is-open'), role: "menuitem", disabled: item.disabled, onClick: () => toggle(index, item), children: [item.icon ? _jsx(KptIcon, { name: item.icon }) : null, _jsx("span", { children: item.label }), item.items?.length ? _jsx(KptIcon, { name: "chevron-down", className: "kpt-menubar__caret" }) : null] }), _jsx(KptOverlay, { open: openIndex === index && !!item.items?.length, anchor: buttonsRef.current[index] ?? null, onClose: closeAll, children: _jsx("div", { className: "kpt-menubar__dropdown", role: "menu", children: (item.items ?? []).map((sub, subIndex) => sub.separator ? (_jsx("div", { className: "kpt-menubar__separator", role: "separator" }, subIndex)) : (_jsxs("button", { type: "button", className: cn('kpt-menubar__item', sub.danger && 'kpt-menubar__item--danger'), role: "menuitem", disabled: sub.disabled, onClick: () => pick(sub), children: [sub.icon ? _jsx(KptIcon, { name: sub.icon }) : null, _jsx("span", { children: sub.label })] }, subIndex))) }) })] }, index))) }) }));
|
|
39
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ComponentPropsWithRef } from 'react';
|
|
2
|
+
export interface KptMeterItem {
|
|
3
|
+
label: string;
|
|
4
|
+
value: number;
|
|
5
|
+
/** Własny kolor segmentu; bez niego wchodzi kolejny z palety. */
|
|
6
|
+
color?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface KptMeterGroupProps extends ComponentPropsWithRef<'div'> {
|
|
9
|
+
values?: KptMeterItem[];
|
|
10
|
+
/** Suma odniesienia; domyślnie suma wartości, czyli segmenty wypełniają całość. */
|
|
11
|
+
max?: number;
|
|
12
|
+
showLegend?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Wielosegmentowy miernik z opcjonalną legendą.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* <KptMeterGroup values={[{ label: 'Użyte', value: 70 }, { label: 'Wolne', value: 30 }]} />
|
|
19
|
+
*/
|
|
20
|
+
export declare function KptMeterGroup({ values, max, showLegend, className, ...rest }: Readonly<KptMeterGroupProps>): import("react").JSX.Element;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../internal/cn.js";
|
|
3
|
+
const PALETTE = [
|
|
4
|
+
'var(--kpt-color-primary)',
|
|
5
|
+
'var(--kpt-color-info)',
|
|
6
|
+
'var(--kpt-color-success)',
|
|
7
|
+
'var(--kpt-color-warning)',
|
|
8
|
+
'var(--kpt-color-danger)',
|
|
9
|
+
];
|
|
10
|
+
/**
|
|
11
|
+
* Wielosegmentowy miernik z opcjonalną legendą.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <KptMeterGroup values={[{ label: 'Użyte', value: 70 }, { label: 'Wolne', value: 30 }]} />
|
|
15
|
+
*/
|
|
16
|
+
export function KptMeterGroup({ values = [], max, showLegend = true, className, ...rest }) {
|
|
17
|
+
const sum = values.reduce((acc, item) => acc + item.value, 0);
|
|
18
|
+
// Bez `max` odniesieniem jest suma wartości; `|| 1` chroni przed dzieleniem przez zero,
|
|
19
|
+
// gdy wszystkie segmenty są zerowe.
|
|
20
|
+
const total = max ?? (sum || 1);
|
|
21
|
+
const percent = (item) => (item.value / total) * 100;
|
|
22
|
+
const color = (item, index) => item.color || PALETTE[index % PALETTE.length];
|
|
23
|
+
return (_jsx("div", { className: cn('kpt-meter-group-host', className), ...rest, children: _jsxs("div", { className: "kpt-meter-group", children: [_jsx("div", { className: "kpt-meter-group__track", role: "meter", "aria-valuenow": sum, "aria-valuemax": max, children: values.map((item, index) => (_jsx("span", { className: "kpt-meter-group__segment", style: { width: `${percent(item)}%`, backgroundColor: color(item, index) }, title: item.label }, index))) }), showLegend ? (_jsx("ul", { className: "kpt-meter-group__legend", children: values.map((item, index) => (_jsxs("li", { className: "kpt-meter-group__legend-item", children: [_jsx("span", { className: "kpt-meter-group__dot", style: { backgroundColor: color(item, index) } }), _jsx("span", { children: item.label }), _jsxs("span", { className: "kpt-meter-group__val", children: [Math.round(percent(item)), "%"] })] }, index))) })) : null] }) }));
|
|
24
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ComponentPropsWithRef, type ReactNode } from 'react';
|
|
2
|
+
import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
|
|
3
|
+
export interface KptOrderListProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'onChange' | 'children'>, KptI18nOverrides {
|
|
4
|
+
items?: T[];
|
|
5
|
+
defaultItems?: T[];
|
|
6
|
+
onItemsChange?: (items: T[]) => void;
|
|
7
|
+
header?: string;
|
|
8
|
+
/** Szablon elementu — odpowiednik `<ng-template let-item>` z Angulara. */
|
|
9
|
+
renderItem: (item: T, index: number) => ReactNode;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* OrderList — lista z ręcznym porządkowaniem. Klik zaznacza element, przyciski przesuwają go
|
|
13
|
+
* w górę i w dół.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* <KptOrderList items={tasks} onItemsChange={setTasks} header="Kolejność" renderItem={(t) => t.name} />
|
|
17
|
+
*/
|
|
18
|
+
export declare function KptOrderList<T>({ items, defaultItems, onItemsChange, header, renderItem, locale, dictionary, className, ...rest }: Readonly<KptOrderListProps<T>>): import("react").JSX.Element;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { cn } from "../internal/cn.js";
|
|
4
|
+
import { useControllableState } from "../internal/use-controllable-state.js";
|
|
5
|
+
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
6
|
+
import { KptIcon } from "../icon/icon.js";
|
|
7
|
+
const EMPTY = [];
|
|
8
|
+
/**
|
|
9
|
+
* OrderList — lista z ręcznym porządkowaniem. Klik zaznacza element, przyciski przesuwają go
|
|
10
|
+
* w górę i w dół.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* <KptOrderList items={tasks} onItemsChange={setTasks} header="Kolejność" renderItem={(t) => t.name} />
|
|
14
|
+
*/
|
|
15
|
+
export function KptOrderList({ items, defaultItems = EMPTY, onItemsChange, header = '', renderItem, locale, dictionary, className, ...rest }) {
|
|
16
|
+
const { t } = useKptMessages({ locale, dictionary });
|
|
17
|
+
const [list, setList] = useControllableState({
|
|
18
|
+
value: items,
|
|
19
|
+
defaultValue: defaultItems,
|
|
20
|
+
onChange: onItemsChange,
|
|
21
|
+
});
|
|
22
|
+
const [selected, setSelected] = useState(null);
|
|
23
|
+
const move = (dir) => {
|
|
24
|
+
if (selected === null)
|
|
25
|
+
return;
|
|
26
|
+
const target = selected + dir;
|
|
27
|
+
if (target < 0 || target >= list.length)
|
|
28
|
+
return;
|
|
29
|
+
const next = [...list];
|
|
30
|
+
[next[selected], next[target]] = [next[target], next[selected]];
|
|
31
|
+
setList(next);
|
|
32
|
+
// Zaznaczenie wędruje za elementem, nie zostaje na pozycji — inaczej kolejne kliknięcie
|
|
33
|
+
// przesuwałoby sąsiada zamiast tego samego wpisu.
|
|
34
|
+
setSelected(target);
|
|
35
|
+
};
|
|
36
|
+
return (_jsx("div", { className: cn('kpt-order-list-host', className), ...rest, children: _jsxs("div", { className: "kpt-order-list", children: [_jsxs("div", { className: "kpt-order-list__controls", children: [_jsx("button", { type: "button", disabled: selected === null, "aria-label": t('orderList.up'), onClick: () => move(-1), children: _jsx(KptIcon, { name: "arrow-up" }) }), _jsx("button", { type: "button", disabled: selected === null, "aria-label": t('orderList.down'), onClick: () => move(1), children: _jsx(KptIcon, { name: "arrow-down" }) })] }), _jsxs("div", { className: "kpt-order-list__panel", children: [header ? _jsx("div", { className: "kpt-order-list__header", children: header }) : null, _jsx("ul", { className: "kpt-order-list__items", children: list.map((item, index) => (_jsx("li", { className: cn('kpt-order-list__item', selected === index && 'is-selected'), onClick: () => setSelected(index), children: renderItem(item, index) }, index))) })] })] }) }));
|
|
37
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ComponentPropsWithRef, type ReactNode } from 'react';
|
|
2
|
+
import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
|
|
3
|
+
export interface KptPaginatorProps extends Omit<ComponentPropsWithRef<'div'>, 'children'>, KptI18nOverrides {
|
|
4
|
+
/** Łączna liczba elementów. */
|
|
5
|
+
length: number;
|
|
6
|
+
/** Rozmiar strony. */
|
|
7
|
+
pageSize?: number;
|
|
8
|
+
defaultPageSize?: number;
|
|
9
|
+
onPageSizeChange?: (pageSize: number) => void;
|
|
10
|
+
/** Indeks strony od 0. */
|
|
11
|
+
pageIndex?: number;
|
|
12
|
+
defaultPageIndex?: number;
|
|
13
|
+
onPageIndexChange?: (pageIndex: number) => void;
|
|
14
|
+
/** Dostępne rozmiary strony. */
|
|
15
|
+
pageSizeOptions?: number[];
|
|
16
|
+
/** Treść przycisków nawigacji; domyślnie strzałki z wbudowanego zestawu ikon. */
|
|
17
|
+
prevIcon?: ReactNode;
|
|
18
|
+
nextIcon?: ReactNode;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Paginator Koncept UI: wybór rozmiaru strony, zakres pozycji i nawigacja.
|
|
22
|
+
*
|
|
23
|
+
* Działa kontrolowany (`pageIndex`/`pageSize` + handlery) i niekontrolowany
|
|
24
|
+
* (`defaultPageIndex`/`defaultPageSize`).
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* <KptPaginator length={total} pageIndex={page} onPageIndexChange={setPage} />
|
|
28
|
+
*/
|
|
29
|
+
export declare function KptPaginator({ length, pageSize, defaultPageSize, onPageSizeChange, pageIndex, defaultPageIndex, onPageIndexChange, pageSizeOptions, prevIcon, nextIcon, locale, dictionary, className, ...rest }: Readonly<KptPaginatorProps>): import("react").JSX.Element;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import { KptButton } from "../button/button.js";
|
|
4
|
+
import { KptIcon } from "../icon/icon.js";
|
|
5
|
+
import { cn } from "../internal/cn.js";
|
|
6
|
+
import { useControllableState } from "../internal/use-controllable-state.js";
|
|
7
|
+
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
8
|
+
/**
|
|
9
|
+
* Paginator Koncept UI: wybór rozmiaru strony, zakres pozycji i nawigacja.
|
|
10
|
+
*
|
|
11
|
+
* Działa kontrolowany (`pageIndex`/`pageSize` + handlery) i niekontrolowany
|
|
12
|
+
* (`defaultPageIndex`/`defaultPageSize`).
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* <KptPaginator length={total} pageIndex={page} onPageIndexChange={setPage} />
|
|
16
|
+
*/
|
|
17
|
+
export function KptPaginator({ length, pageSize, defaultPageSize = 10, onPageSizeChange, pageIndex, defaultPageIndex = 0, onPageIndexChange, pageSizeOptions = [5, 10, 20, 50], prevIcon = _jsx(KptIcon, { name: "chevron-left" }), nextIcon = _jsx(KptIcon, { name: "chevron-right" }), locale, dictionary, className, ...rest }) {
|
|
18
|
+
const { t } = useKptMessages({ locale, dictionary });
|
|
19
|
+
const [size, setSize] = useControllableState({
|
|
20
|
+
value: pageSize,
|
|
21
|
+
defaultValue: defaultPageSize,
|
|
22
|
+
onChange: onPageSizeChange,
|
|
23
|
+
});
|
|
24
|
+
const [index, setIndex] = useControllableState({
|
|
25
|
+
value: pageIndex,
|
|
26
|
+
defaultValue: defaultPageIndex,
|
|
27
|
+
onChange: onPageIndexChange,
|
|
28
|
+
});
|
|
29
|
+
const totalPages = Math.max(1, Math.ceil(length / size));
|
|
30
|
+
const rangeStart = length === 0 ? 0 : index * size + 1;
|
|
31
|
+
const rangeEnd = Math.min(length, (index + 1) * size);
|
|
32
|
+
const canPrev = index > 0;
|
|
33
|
+
const canNext = index < totalPages - 1;
|
|
34
|
+
// Utrzymuj poprawny indeks, gdy zmieni się długość lub rozmiar strony.
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const max = totalPages - 1;
|
|
37
|
+
if (index > max)
|
|
38
|
+
setIndex(max);
|
|
39
|
+
}, [index, totalPages, setIndex]);
|
|
40
|
+
const onSizeChange = (event) => {
|
|
41
|
+
setSize(Number(event.target.value));
|
|
42
|
+
setIndex(0);
|
|
43
|
+
};
|
|
44
|
+
return (_jsxs("div", { className: cn('kpt-paginator', className), ...rest, children: [_jsx("div", { className: "kpt-paginator__size", children: _jsxs("label", { children: [t('paginator.rowsPerPage'), _jsx("select", { className: "kpt-paginator__select", value: size, onChange: onSizeChange, children: pageSizeOptions.map((option) => (_jsx("option", { value: option, children: option }, option))) })] }) }), _jsx("span", { className: "kpt-paginator__range", children: t('paginator.range', { start: rangeStart, end: rangeEnd, total: length }) }), _jsxs("div", { className: "kpt-paginator__nav", children: [_jsx(KptButton, { variant: "outline", size: "sm", disabled: !canPrev, onClick: () => setIndex(index - 1), "aria-label": t('paginator.prevPage'), children: prevIcon }), _jsx(KptButton, { variant: "outline", size: "sm", disabled: !canNext, onClick: () => setIndex(index + 1), "aria-label": t('paginator.nextPage'), children: nextIcon })] })] }));
|
|
45
|
+
}
|