@konce-pt/react 0.17.0 → 0.18.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/dist/charts/chart.js +26 -12
- package/dist/charts/llms.txt +11 -1
- package/dist/data-table/data-table.d.ts +8 -1
- package/dist/data-table/data-table.js +30 -11
- package/dist/data-table/llms.txt +6 -1
- package/dist/dialog/dialog.js +7 -0
- package/dist/input/input.d.ts +6 -1
- package/dist/input/input.js +2 -2
- package/dist/input/llms.txt +4 -0
- package/dist/popover/llms.txt +3 -0
- package/dist/public-api.d.ts +2 -2
- package/dist/select/llms.txt +5 -1
- package/dist/select/select.d.ts +24 -2
- package/dist/select/select.js +0 -19
- package/package.json +14 -14
package/dist/charts/chart.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useId, useRef, useState } from 'react';
|
|
4
|
-
import { KptChartMotion, areaPath, bandScale, barLayout, chartAccessibleName, chartDataTable, dataScale, deltaKind, domainScale,
|
|
4
|
+
import { KptChartMotion, areaPath, axisBase, bandPath, bandScale, barLayout, chartScale, chartAccessibleName, chartDataTable, dataScale, deltaKind, domainScale, formatNumber, linePath, linearScale, pieSlices, plotArea, polarSlices, polarToCartesian, polygonPath, radarGrid, radarPoints, radialArea, sameChartShape, scaleRows, seriesColor, significantDelta, stackSeries, } from '@konce-pt/chart';
|
|
5
5
|
import { cn } from "../internal/cn.js";
|
|
6
6
|
import { useKptMotion } from "../motion/motion-context.js";
|
|
7
7
|
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
@@ -106,8 +106,11 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
106
106
|
const to = config.series.map((s) => [...s.data]);
|
|
107
107
|
const withAxis = config.type === 'line' || config.type === 'bar';
|
|
108
108
|
const visible = (rows) => rows.filter((_, i) => !hidden.has(`ser:${config.series[i]?.name ?? ''}`));
|
|
109
|
+
const axis = { valueAxis: config.valueAxis, stacked: config.stacked };
|
|
110
|
+
// Skala trzymana na czas ducha obejmuje oba stany - z wierszy już złożonych w stos.
|
|
111
|
+
const held = [...scaleRows(visible(from), config.stacked), ...scaleRows(visible(to), config.stacked)];
|
|
109
112
|
const scales = withAxis
|
|
110
|
-
? { start: dataScale(visible(from)), hold:
|
|
113
|
+
? { start: dataScale(visible(from), axis), hold: chartScale(held, { valueAxis: config.valueAxis }), end: dataScale(visible(to), axis) }
|
|
111
114
|
: null;
|
|
112
115
|
motion.transition(from, to, { delta: withAxis && config.animation?.delta !== false, scales });
|
|
113
116
|
}, [config, hidden, motion, motionConfig]);
|
|
@@ -204,16 +207,19 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
204
207
|
if (view.type !== 'line')
|
|
205
208
|
return null;
|
|
206
209
|
const active = view.series.map((s, i) => ({ s, i })).filter(({ s }) => !hidden.has(`ser:${s.name}`));
|
|
207
|
-
const
|
|
208
|
-
const [mn, mx] = extent(values.length ? values : [0]);
|
|
210
|
+
const rows = active.map(({ s }) => s.data);
|
|
209
211
|
const scale = frame.domain
|
|
210
212
|
? domainScale(frame.domain.target, frame.domain.min, frame.domain.max)
|
|
211
|
-
:
|
|
213
|
+
: chartScale(rows, { valueAxis: view.valueAxis, stacked: view.stacked });
|
|
214
|
+
// Stos: punkty serii na narastającej sumie, pasmo od linii serii pod nią.
|
|
215
|
+
const stack = view.stacked ? stackSeries(rows) : null;
|
|
212
216
|
const plot = plotArea(width, height, VERTICAL_INSETS);
|
|
213
217
|
const yScale = linearScale(scale.min, scale.max, plot.y + plot.height, plot.y);
|
|
214
218
|
const count = view.labels.length;
|
|
215
219
|
const xAt = (i) => count > 1 ? plot.x + (i / (count - 1)) * plot.width : plot.x + plot.width / 2;
|
|
216
|
-
|
|
220
|
+
// Punkt nie wychodzi poza oś - przypięte `valueAxis.min`/`max` mogą uciąć wartość.
|
|
221
|
+
const yAt = (v) => yScale(Math.min(scale.max, Math.max(scale.min, v)));
|
|
222
|
+
const baseline = yScale(axisBase(scale));
|
|
217
223
|
const interpolation = view.interpolation ?? 'linear';
|
|
218
224
|
const showPoints = view.points ?? true;
|
|
219
225
|
const grid = scale.ticks.map((t) => ({
|
|
@@ -234,13 +240,21 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
234
240
|
text: label,
|
|
235
241
|
anchor: 'middle',
|
|
236
242
|
}));
|
|
237
|
-
const series = active.map(({ s, i }) => ({
|
|
243
|
+
const series = active.map(({ s, i }, k) => ({
|
|
238
244
|
key: `ser:${s.name}`,
|
|
239
245
|
name: s.name,
|
|
240
246
|
color: seriesColor(s.color, i),
|
|
241
247
|
i,
|
|
242
|
-
points: s.data.map((v, idx) => ({ x: xAt(idx), y:
|
|
248
|
+
points: s.data.map((v, idx) => ({ x: xAt(idx), y: yAt(stack?.top[k]?.[idx] ?? v), value: v, label: view.labels[idx] ?? '' })),
|
|
249
|
+
floor: stack ? s.data.map((_, idx) => ({ x: xAt(idx), y: yAt(stack.base[k]?.[idx] ?? 0) })) : null,
|
|
243
250
|
}));
|
|
251
|
+
// Duch w stosie stoi na sumach poprzedniego stanu, tak jak bieżące punkty.
|
|
252
|
+
const ghostRows = ghostState
|
|
253
|
+
? (() => {
|
|
254
|
+
const prev = active.map(({ i }) => ghostState.prev[i] ?? []);
|
|
255
|
+
return view.stacked ? stackSeries(prev).top : prev;
|
|
256
|
+
})()
|
|
257
|
+
: [];
|
|
244
258
|
const crosshairCat = config.crosshair && activeIndex !== null ? catLabels[activeIndex] : undefined;
|
|
245
259
|
// Prowadnica i pigułka jadą razem z dymkiem.
|
|
246
260
|
const crossX = crosshairCat ? (frame.tip?.x ?? crosshairCat.x) : 0;
|
|
@@ -268,10 +282,10 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
268
282
|
? series.map((s) => {
|
|
269
283
|
const prev = ghostState.prev[s.i] ?? [];
|
|
270
284
|
const next = ghostState.next[s.i] ?? [];
|
|
271
|
-
const gpts = prev.map((v, idx) => ({ x: xAt(idx), y:
|
|
285
|
+
const gpts = (ghostRows[series.indexOf(s)] ?? prev).map((v, idx) => ({ x: xAt(idx), y: yAt(v) }));
|
|
272
286
|
return (_jsxs("g", { className: "kpt-chart__ghost", "aria-hidden": "true", children: [_jsx("path", { className: "kpt-chart__ghost-line", d: linePath(gpts, interpolation) }), gpts.map((p, idx) => (_jsx("line", { className: "kpt-chart__delta", "data-kind": deltaKind(prev[idx] ?? 0, next[idx] ?? 0), x1: p.x, x2: p.x, y1: p.y, y2: s.points[idx]?.y ?? p.y }, idx)))] }, `${ghostState.id}:${s.key}`));
|
|
273
287
|
})
|
|
274
|
-
: null, series.map((s) => (_jsxs("g", { className: cn('kpt-chart__series', dim(s.key) && 'kpt-chart__series--dim'), children: [view.area ? (_jsx("path", { className: "kpt-chart__area", d: areaPath(s.points, baseline, interpolation), style: { fill: s.color } })) : null, _jsx("path", { className: "kpt-chart__line", d: linePath(s.points, interpolation), style: { stroke: s.color }, pathLength: 1 }), showPoints
|
|
288
|
+
: null, series.map((s) => (_jsxs("g", { className: cn('kpt-chart__series', dim(s.key) && 'kpt-chart__series--dim'), children: [view.area ? (_jsx("path", { className: "kpt-chart__area", d: s.floor ? bandPath(s.points, s.floor, interpolation) : areaPath(s.points, baseline, interpolation), style: { fill: s.color } })) : null, _jsx("path", { className: "kpt-chart__line", d: linePath(s.points, interpolation), style: { stroke: s.color }, pathLength: 1 }), showPoints
|
|
275
289
|
? s.points.map((p, idx) => (_jsx("circle", { className: cn('kpt-chart__point', activeIndex === idx && 'kpt-chart__point--active'), cx: p.x, cy: p.y, r: 4, style: { fill: s.color, ['--kpt-chart-i']: idx } }, idx)))
|
|
276
290
|
: null] }, s.key))), sheen
|
|
277
291
|
? series.map((s) => (_jsx("path", { className: "kpt-chart__sheen-line", d: linePath(s.points, interpolation), stroke: `url(#${sheenId})`, style: { opacity: sheen.opacity }, "aria-hidden": "true" }, s.key)))
|
|
@@ -301,7 +315,7 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
301
315
|
const active = view.series.map((s, i) => ({ s, i })).filter(({ s }) => !hidden.has(`ser:${s.name}`));
|
|
302
316
|
const plot = plotArea(width, height, vertical ? VERTICAL_INSETS : HORIZONTAL_INSETS);
|
|
303
317
|
const radius = view.borderRadius ?? 4;
|
|
304
|
-
const layout = barLayout(active.map(({ s }) => s.data), view.labels.length, plot, orientation, radius, frame.domain ? domainScale(frame.domain.target, frame.domain.min, frame.domain.max) : undefined);
|
|
318
|
+
const layout = barLayout(active.map(({ s }) => s.data), view.labels.length, plot, orientation, radius, frame.domain ? domainScale(frame.domain.target, frame.domain.min, frame.domain.max) : undefined, { stacked: view.stacked, valueAxis: view.valueAxis });
|
|
305
319
|
const colors = active.map(({ s, i }) => seriesColor(s.color, i));
|
|
306
320
|
const names = active.map(({ s }) => s.name);
|
|
307
321
|
const cat = vertical
|
|
@@ -335,7 +349,7 @@ export function KptChart({ config, width = 600, height = 360, locale, dictionary
|
|
|
335
349
|
? (() => {
|
|
336
350
|
const prevRows = active.map(({ i }) => ghostState.prev[i] ?? []);
|
|
337
351
|
const nextRows = active.map(({ i }) => ghostState.next[i] ?? []);
|
|
338
|
-
return barLayout(prevRows, view.labels.length, plot, orientation, radius, layout.scale).bars.map((g, k) => {
|
|
352
|
+
return barLayout(prevRows, view.labels.length, plot, orientation, radius, layout.scale, { stacked: view.stacked }).bars.map((g, k) => {
|
|
339
353
|
const cur = layout.bars[k] ?? g;
|
|
340
354
|
const a = barEnd(g, vertical);
|
|
341
355
|
const z = barEnd(cur, vertical);
|
package/dist/charts/llms.txt
CHANGED
|
@@ -19,9 +19,19 @@ proportionally (`width={300} height={180}`) instead of letting a 600-unit chart
|
|
|
19
19
|
|
|
20
20
|
## The config is the API
|
|
21
21
|
`{ type, labels, series, points?, area?, interpolation?, orientation?, borderRadius?, innerRadius?,
|
|
22
|
-
crosshair?, tooltip?, legend?, title?, subtitle?, animation?, ariaLabel?, description? }` - the same shape the Angular port
|
|
22
|
+
valueAxis?, stacked?, crosshair?, tooltip?, legend?, title?, subtitle?, animation?, ariaLabel?, description? }` - the same shape the Angular port
|
|
23
23
|
takes, because both read it from `@konce-pt/chart`.
|
|
24
24
|
|
|
25
|
+
## Value axis and stacking (line, bar)
|
|
26
|
+
- `valueAxis?: { zero?, min?, max? }` - the value axis starts at zero by default, so a bar and the area under a line
|
|
27
|
+
show the size directly. `zero: false` fits the axis to the data (a weight of 79-90 kg is no longer a flat line under
|
|
28
|
+
the ceiling); `min` / `max` pin an end of the axis exactly and win over `zero` - a value beyond a pinned end is
|
|
29
|
+
cut at the edge. Bars and areas grow from zero, or from the end of the axis nearer to it
|
|
30
|
+
- `stacked?: boolean` - stacked series: bars on top of each other in one band per category (positive values up,
|
|
31
|
+
negative down; only the outer segment is rounded), lines as running totals with a band under each one (with
|
|
32
|
+
`area`). The axis covers the totals; the tooltip shows the values of the series, not the totals
|
|
33
|
+
- The rules live in `@konce-pt/chart`: `axisScale`, `chartScale`, `stackSeries`, `bandPath` (tested)
|
|
34
|
+
|
|
25
35
|
## The geometry lives in a package
|
|
26
36
|
Scales, paths, bar layout and arcs come from `@konce-pt/chart` - pure functions with `node --test`
|
|
27
37
|
coverage, shared with the Angular port. The component only turns them into SVG elements; there is
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ComponentPropsWithRef, type ReactNode } from 'react';
|
|
2
|
+
import { type KptColumnGroup } from '@konce-pt/table';
|
|
2
3
|
import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
|
|
3
4
|
import { type KptSpeechColor } from '../speech/speech-button.tsx';
|
|
4
5
|
import type { KptCellEditEvent, KptCellEditRenderers, KptCellRenderers, KptColumn, KptTableLazyState } from './data-table-types.ts';
|
|
@@ -45,6 +46,12 @@ export interface KptDataTableProps<T> extends Omit<ComponentPropsWithRef<'div'>,
|
|
|
45
46
|
/** Szerokość kolumny bez liczalnego `width` w trybie `virtualScroll` (`px`/`rem`/`em`). */
|
|
46
47
|
columnWidth?: string;
|
|
47
48
|
rowHeight?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Nagłówki grupowane: wiersz nad etykietami, w którym sąsiednie kolumny grupy dzielą komórkę
|
|
51
|
+
* (`[{ header: 'Makroskładniki', fields: ['carbs', 'protein', 'fat'] }]`). Liczony z kolumn
|
|
52
|
+
* widocznych, w bieżącej kolejności - ukryta albo przestawiona kolumna wypada z grupy sama.
|
|
53
|
+
*/
|
|
54
|
+
columnGroups?: readonly KptColumnGroup[];
|
|
48
55
|
reorderable?: boolean;
|
|
49
56
|
/**
|
|
50
57
|
* Przykleja kolumny systemowe (rozwijanie, zaznaczanie) do lewej krawędzi także bez kolumn `frozen`.
|
|
@@ -83,4 +90,4 @@ export interface KptDataTableProps<T> extends Omit<ComponentPropsWithRef<'div'>,
|
|
|
83
90
|
/** Treść po lewej stronie paginatora (odpowiednik slotu `[kptPaginatorActions]`). */
|
|
84
91
|
paginatorActions?: ReactNode;
|
|
85
92
|
}
|
|
86
|
-
export declare function KptDataTable<T = Record<string, unknown>>({ columns, data, filterable, filterSpeech, filterSpeechLang, filterSpeechColor, filterRowPosition, pageSize, pageSizeOptions, selectable, loading, ariaLabel, caption, clickableRows, emptyMessage, rowKey, scrollHeight, virtualScroll, columnWidth, rowHeight, reorderable, frozenSystemColumns, exportable, exportFilename, lazy, totalRecords, expandable, multiExpand, summaryLabel, summaryValues, selection, defaultSelection, onSelectionChange, expandedRows, defaultExpandedRows, onExpandedRowsChange, hiddenFields, defaultHiddenFields, onHiddenFieldsChange, onRowClick, onLazyLoad, onCellEdit, cells, cellEditors, renderRowDetail, paginatorActions, locale, dictionary, className, style, ...rest }: Readonly<KptDataTableProps<T>>): import("react").JSX.Element;
|
|
93
|
+
export declare function KptDataTable<T = Record<string, unknown>>({ columns, data, filterable, filterSpeech, filterSpeechLang, filterSpeechColor, filterRowPosition, pageSize, pageSizeOptions, selectable, loading, ariaLabel, caption, clickableRows, emptyMessage, rowKey, scrollHeight, virtualScroll, columnWidth, rowHeight, reorderable, columnGroups, frozenSystemColumns, exportable, exportFilename, lazy, totalRecords, expandable, multiExpand, summaryLabel, summaryValues, selection, defaultSelection, onSelectionChange, expandedRows, defaultExpandedRows, onExpandedRowsChange, hiddenFields, defaultHiddenFields, onHiddenFieldsChange, onRowClick, onLazyLoad, onCellEdit, cells, cellEditors, renderRowDetail, paginatorActions, locale, dictionary, className, style, ...rest }: Readonly<KptDataTableProps<T>>): import("react").JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useEffectEvent, useId, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
4
|
-
import { aggregate, columnOffsets, createGlobalFilter, cssLengthToPx, isEmptyFilterValue, matchesColumnFilter, sortRows, toText, virtualColumnRange, virtualRange, } from '@konce-pt/table';
|
|
4
|
+
import { aggregate, columnOffsets, createGlobalFilter, cssLengthToPx, groupHeaderCells, isEmptyFilterValue, matchesColumnFilter, sortRows, toText, virtualColumnRange, virtualRange, } from '@konce-pt/table';
|
|
5
5
|
import { cn } from "../internal/cn.js";
|
|
6
6
|
import { useControllableState } from "../internal/use-controllable-state.js";
|
|
7
7
|
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
@@ -82,7 +82,7 @@ const EMPTY_FIELDS = [];
|
|
|
82
82
|
function getValue(row, field) {
|
|
83
83
|
return row[field];
|
|
84
84
|
}
|
|
85
|
-
export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, filterSpeech = false, filterSpeechLang, filterSpeechColor, filterRowPosition = 'bottom', pageSize = 0, pageSizeOptions = [5, 10, 20, 50], selectable = 'none', loading = false, ariaLabel = null, caption = null, clickableRows, emptyMessage, rowKey = null, scrollHeight = null, virtualScroll = false, columnWidth = '10rem', rowHeight = 41, reorderable = false, frozenSystemColumns = false, exportable = false, exportFilename = 'export.csv', lazy = false, totalRecords = null, expandable = false, multiExpand = true, summaryLabel, summaryValues = null, selection, defaultSelection = EMPTY_ROWS, onSelectionChange, expandedRows, defaultExpandedRows = EMPTY_ROWS, onExpandedRowsChange, hiddenFields, defaultHiddenFields = EMPTY_FIELDS, onHiddenFieldsChange, onRowClick, onLazyLoad, onCellEdit, cells, cellEditors, renderRowDetail, paginatorActions, locale, dictionary, className, style, ...rest }) {
|
|
85
|
+
export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, filterSpeech = false, filterSpeechLang, filterSpeechColor, filterRowPosition = 'bottom', pageSize = 0, pageSizeOptions = [5, 10, 20, 50], selectable = 'none', loading = false, ariaLabel = null, caption = null, clickableRows, emptyMessage, rowKey = null, scrollHeight = null, virtualScroll = false, columnWidth = '10rem', rowHeight = 41, reorderable = false, columnGroups, frozenSystemColumns = false, exportable = false, exportFilename = 'export.csv', lazy = false, totalRecords = null, expandable = false, multiExpand = true, summaryLabel, summaryValues = null, selection, defaultSelection = EMPTY_ROWS, onSelectionChange, expandedRows, defaultExpandedRows = EMPTY_ROWS, onExpandedRowsChange, hiddenFields, defaultHiddenFields = EMPTY_FIELDS, onHiddenFieldsChange, onRowClick, onLazyLoad, onCellEdit, cells, cellEditors, renderRowDetail, paginatorActions, locale, dictionary, className, style, ...rest }) {
|
|
86
86
|
const { t, locale: activeLocale } = useKptMessages({ locale, dictionary });
|
|
87
87
|
const uid = useId();
|
|
88
88
|
const rowsClickable = clickableRows ?? onRowClick !== undefined;
|
|
@@ -149,6 +149,8 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
149
149
|
const [viewport, setViewport] = useState({ height: 400, width: 1200 });
|
|
150
150
|
const scrollFrame = useRef(0);
|
|
151
151
|
const [theadOffset, setTheadOffset] = useState(0);
|
|
152
|
+
/** Offset sticky trzeciego wiersza nagłówka (grupy + etykiety + filtry). */
|
|
153
|
+
const [theadOffset2, setTheadOffset2] = useState(0);
|
|
152
154
|
const scrollRef = useRef(null);
|
|
153
155
|
const theadRef = useRef(null);
|
|
154
156
|
const editorHostRef = useRef(null);
|
|
@@ -234,14 +236,17 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
234
236
|
return;
|
|
235
237
|
requestLazyLoad({ pageIndex, pageSize: pageSizeState, sortField, sortDir, filter: filterText, columnFilters });
|
|
236
238
|
}, [lazy, pageIndex, pageSizeState, sortField, sortDir, filterText, columnFilters]);
|
|
237
|
-
//
|
|
239
|
+
// Offsety sticky drugiego i trzeciego wiersza nagłówka = wysokość jednego i dwóch pierwszych.
|
|
238
240
|
useLayoutEffect(() => {
|
|
239
|
-
const
|
|
241
|
+
const rows = theadRef.current?.rows;
|
|
240
242
|
// Dokładna wysokość, nie `offsetHeight`: ten zaokrągla (46,5 -> 47) i przy skali ekranu innej
|
|
241
243
|
// niż 100% między wierszami nagłówka zostawała szpara, przez którą prześwitywały wiersze.
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
244
|
+
const h1 = rows?.[0]?.getBoundingClientRect().height ?? 0;
|
|
245
|
+
const h2 = rows?.[1]?.getBoundingClientRect().height ?? 0;
|
|
246
|
+
if (rows?.[0])
|
|
247
|
+
setTheadOffset(h1);
|
|
248
|
+
setTheadOffset2(h1 + h2);
|
|
249
|
+
}, [filterRowPosition, hasColumnFilters, visibleColumns, columnGroups]);
|
|
245
250
|
// Rozmiar viewportu dla wirtualizacji - także po zmianie rozmiaru kontenera bez przewinięcia.
|
|
246
251
|
useLayoutEffect(() => {
|
|
247
252
|
const el = scrollRef.current;
|
|
@@ -400,8 +405,22 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
400
405
|
const tableWidth = virtualScroll
|
|
401
406
|
? scrollable.pinnedWidth + scrollable.offsets[scrollable.offsets.length - 1]
|
|
402
407
|
: undefined;
|
|
403
|
-
/**
|
|
404
|
-
|
|
408
|
+
/**
|
|
409
|
+
* Wiersz grup nad etykietami: komórki nad pozycjami `slots`. Komórka przykleja się do lewej, gdy
|
|
410
|
+
* wszystkie jej kolumny są zamrożone - inaczej przewija się z nimi. Pusta lista = bez wiersza.
|
|
411
|
+
*/
|
|
412
|
+
const groupCells = columnGroups?.length
|
|
413
|
+
? groupHeaderCells(slots.map((s) => s.meta?.col.field ?? null), columnGroups).map((cell) => {
|
|
414
|
+
const metas = slots.slice(cell.start, cell.start + cell.span).map((s) => s.meta);
|
|
415
|
+
const spacer = metas.some((m) => m === null);
|
|
416
|
+
const frozen = !spacer && metas.every((m) => m?.frozen);
|
|
417
|
+
return { ...cell, key: slots[cell.start]?.key ?? String(cell.start), spacer, frozen, left: frozen ? (metas[0]?.left ?? undefined) : undefined };
|
|
418
|
+
})
|
|
419
|
+
: [];
|
|
420
|
+
/** Wiersz grup jest pierwszym wierszem nagłówka - przesuwa `aria-rowindex` pozostałych. */
|
|
421
|
+
const groupRows = groupCells.length ? 1 : 0;
|
|
422
|
+
/** Liczba wierszy nagłówka (grupy, etykiety, ewentualnie filtry) - przesunięcie `aria-rowindex`. */
|
|
423
|
+
const headerRowCount = groupRows + (hasColumnFilters ? 2 : 1);
|
|
405
424
|
/** `aria-colindex` komórki kolumny (od 1, z kolumnami systemowymi) - tylko w trybie wirtualnym. */
|
|
406
425
|
const colIndex = (meta) => virtualScroll ? leadingCells + meta.index + 1 : undefined;
|
|
407
426
|
// -- Przestawianie kolumn --
|
|
@@ -671,7 +690,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
671
690
|
return null;
|
|
672
691
|
}
|
|
673
692
|
};
|
|
674
|
-
const filterRowIndex = filterRowPosition === 'top' ? 1 : 2;
|
|
693
|
+
const filterRowIndex = groupRows + (filterRowPosition === 'top' ? 1 : 2);
|
|
675
694
|
const filterRow = (_jsxs("tr", { className: "kpt-data-table__filter-row", "aria-rowindex": virtualScroll ? filterRowIndex : undefined, children: [hasRowDetail ? (_jsx("th", { className: cn('kpt-data-table__expander', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: expanderStickyLeft } })) : null, selectable !== 'none' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft } })) : null, slots.map((slot) => slot.meta ? (_jsx("th", { style: { width: virtualScroll ? undefined : slot.meta.col.width, left: slot.meta.left }, "data-align": slot.meta.align, "aria-colindex": colIndex(slot.meta), className: cn(slot.meta.frozen && 'kpt-data-table__cell--frozen'), children: filterCell(slot.meta.col) }, slot.key)) : (hspacer(slot)))] }));
|
|
676
695
|
const editor = (row, col) => {
|
|
677
696
|
const custom = cellEditors?.[col.field];
|
|
@@ -743,7 +762,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
743
762
|
startEdit(row, col, event);
|
|
744
763
|
}, children: editing ? (_jsx("div", { ref: editorHostRef, className: "kpt-data-table__editor", onClick: (event) => event.stopPropagation(), onBlur: onEditorFocusOut, children: editor(row, col) })) : cells?.[col.field] ? (cells[col.field](row, getValue(row, col.field))) : getValue(row, col.field) }, slot.key));
|
|
745
764
|
})] }), hasRowDetail && isExpanded(row) && !virtualScroll ? (_jsx("tr", { className: "kpt-data-table__detail-row", children: _jsx("td", { className: "kpt-data-table__detail", colSpan: colspan, children: renderRowDetail?.(row) }) })) : null] }, rowKey ? String(getValue(row, rowKey)) : index));
|
|
746
|
-
return (_jsxs("div", { className: cn('kpt-data-table', className), style: { ['--kpt-thead-offset']: `${theadOffset}px`, ...style }, ...rest, children: [filterable || exportable ? (_jsxs("div", { className: "kpt-data-table__toolbar", children: [filterable ? (_jsxs("div", { className: "kpt-data-table__search", "data-speech": filterSpeechState, children: [_jsx("input", { ref: filterRef, className: "kpt-data-table__filter", type: "search", placeholder: t('dataTable.search'), "aria-label": t('dataTable.search'), value: filterInput, onChange: (event) => onFilterInput(event.target.value) }), filterSpeech ? (_jsx(KptSpeechButton, { listeningColor: filterSpeechColor, speech: filterMic, size: "sm", unsupported: "hide", hostProps: { className: 'kpt-data-table__speech' } })) : null] })) : null, exportable ? (_jsx(KptButton, { variant: "outline", size: "sm", onClick: () => exportTableCsv(visibleColumns, (lazy ? data : sorted), exportFilename), children: t('dataTable.exportCsv') })) : null] })) : null, _jsx("div", { ref: scrollRef, className: "kpt-data-table__scroll", style: { maxHeight: scrollHeight ?? undefined }, onScroll: onScroll, children: _jsxs("table", { className: cn('kpt-data-table__table', hasFrozen && 'kpt-data-table__table--frozen', virtualScroll && 'kpt-data-table__table--virtual'), style: { width: tableWidth }, "aria-label": ariaLabel ?? undefined, "aria-busy": loading || undefined, "aria-rowcount": virtualScroll ? headerRowCount + sorted.length + (hasSummary ? 1 : 0) : undefined, "aria-colcount": virtualScroll ? leadingCells + visibleColumns.length : undefined, children: [caption ? _jsx("caption", { className: "kpt-data-table__caption", children: _jsx("span", { className: "kpt-data-table__caption-text", children: caption }) }) : null, virtualScroll ? (_jsxs("colgroup", { children: [hasRowDetail ? _jsx("col", { className: "kpt-data-table__col-expander" }) : null, selectable !== 'none' ? _jsx("col", { className: "kpt-data-table__col-checkbox" }) : null, slots.map((slot) => (_jsx("col", { style: { width: slot.width } }, slot.key)))] })) : null, _jsxs("thead", { ref: theadRef, children: [hasColumnFilters && filterRowPosition === 'top' ? filterRow : null, _jsxs("tr", { "aria-rowindex": virtualScroll ? (hasColumnFilters && filterRowPosition === 'top' ? 2 : 1) : undefined, children: [hasRowDetail ? (_jsx("th", { className: cn('kpt-data-table__expander', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: expanderStickyLeft } })) : null, selectable === 'multiple' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft }, children: _jsx("input", { type: "checkbox", checked: pageAllSelected, ref: (el) => {
|
|
765
|
+
return (_jsxs("div", { className: cn('kpt-data-table', className), style: { ['--kpt-thead-offset']: `${theadOffset}px`, ['--kpt-thead-offset-2']: `${theadOffset2}px`, ...style }, ...rest, children: [filterable || exportable ? (_jsxs("div", { className: "kpt-data-table__toolbar", children: [filterable ? (_jsxs("div", { className: "kpt-data-table__search", "data-speech": filterSpeechState, children: [_jsx("input", { ref: filterRef, className: "kpt-data-table__filter", type: "search", placeholder: t('dataTable.search'), "aria-label": t('dataTable.search'), value: filterInput, onChange: (event) => onFilterInput(event.target.value) }), filterSpeech ? (_jsx(KptSpeechButton, { listeningColor: filterSpeechColor, speech: filterMic, size: "sm", unsupported: "hide", hostProps: { className: 'kpt-data-table__speech' } })) : null] })) : null, exportable ? (_jsx(KptButton, { variant: "outline", size: "sm", onClick: () => exportTableCsv(visibleColumns, (lazy ? data : sorted), exportFilename), children: t('dataTable.exportCsv') })) : null] })) : null, _jsx("div", { ref: scrollRef, className: "kpt-data-table__scroll", style: { maxHeight: scrollHeight ?? undefined }, onScroll: onScroll, children: _jsxs("table", { className: cn('kpt-data-table__table', hasFrozen && 'kpt-data-table__table--frozen', virtualScroll && 'kpt-data-table__table--virtual'), style: { width: tableWidth }, "aria-label": ariaLabel ?? undefined, "aria-busy": loading || undefined, "aria-rowcount": virtualScroll ? headerRowCount + sorted.length + (hasSummary ? 1 : 0) : undefined, "aria-colcount": virtualScroll ? leadingCells + visibleColumns.length : undefined, children: [caption ? _jsx("caption", { className: "kpt-data-table__caption", children: _jsx("span", { className: "kpt-data-table__caption-text", children: caption }) }) : null, virtualScroll ? (_jsxs("colgroup", { children: [hasRowDetail ? _jsx("col", { className: "kpt-data-table__col-expander" }) : null, selectable !== 'none' ? _jsx("col", { className: "kpt-data-table__col-checkbox" }) : null, slots.map((slot) => (_jsx("col", { style: { width: slot.width } }, slot.key)))] })) : null, _jsxs("thead", { ref: theadRef, children: [groupCells.length ? (_jsxs("tr", { className: "kpt-data-table__group-row", "aria-rowindex": virtualScroll ? 1 : undefined, children: [hasRowDetail ? (_jsx("th", { className: cn('kpt-data-table__expander', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: expanderStickyLeft } })) : null, selectable !== 'none' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft } })) : null, groupCells.map((cell) => cell.spacer ? (_jsx("td", { className: "kpt-data-table__hspacer", colSpan: cell.span, "aria-hidden": "true" }, cell.key)) : (_jsx("th", { className: cn('kpt-data-table__group', !cell.header && 'kpt-data-table__group--empty', cell.frozen && 'kpt-data-table__cell--frozen'), style: { left: cell.left }, colSpan: cell.span > 1 ? cell.span : undefined, scope: cell.header ? 'colgroup' : undefined, children: cell.header }, cell.key)))] })) : null, hasColumnFilters && filterRowPosition === 'top' ? filterRow : null, _jsxs("tr", { "aria-rowindex": virtualScroll ? groupRows + (hasColumnFilters && filterRowPosition === 'top' ? 2 : 1) : undefined, children: [hasRowDetail ? (_jsx("th", { className: cn('kpt-data-table__expander', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: expanderStickyLeft } })) : null, selectable === 'multiple' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft }, children: _jsx("input", { type: "checkbox", checked: pageAllSelected, ref: (el) => {
|
|
747
766
|
if (el)
|
|
748
767
|
el.indeterminate = pageSomeSelected;
|
|
749
768
|
}, "aria-label": t('dataTable.selectAll'), onChange: toggleAllOnPage }) })) : selectable === 'single' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft } })) : null, slots.map((slot) => {
|
package/dist/data-table/llms.txt
CHANGED
|
@@ -18,6 +18,11 @@ rendered header.
|
|
|
18
18
|
|
|
19
19
|
## Props
|
|
20
20
|
- `columns` (required), `data`
|
|
21
|
+
- `columnGroups`: KptColumnGroup[] - grouped headers: a row above the column labels where adjacent columns of a
|
|
22
|
+
group share one cell (`[{ header: 'Macros', fields: ['carbs', 'protein', 'fat'] }]`, `scope="colgroup"`). It is
|
|
23
|
+
computed from the visible columns in their current order: a hidden column drops out, a group split by reordering
|
|
24
|
+
gets two cells with the same text, a column without a group gets an empty cell. A cell sticks to the left only
|
|
25
|
+
when all its columns are `frozen`. With column filters the header has three rows and all of them stay sticky
|
|
21
26
|
- `filterable`, `filterRowPosition` (`'bottom'` default), `pageSize` (0 = no pagination),
|
|
22
27
|
`pageSizeOptions`, `selectable` (`'none' | 'single' | 'multiple'`), `loading`, `emptyMessage`,
|
|
23
28
|
`rowKey`, `scrollHeight`, `virtualScroll` (rows **and** columns, see below), `columnWidth`
|
|
@@ -113,7 +118,7 @@ The toolbar, the states and the filter labels come from the `dataTable` namespac
|
|
|
113
118
|
the expanded row `--kpt-table-detail-bg`, the summary row `--kpt-table-summary-bg`.
|
|
114
119
|
|
|
115
120
|
## Not ported yet
|
|
116
|
-
|
|
121
|
+
A ready column-toggle UI is not part of either
|
|
117
122
|
port - `hiddenFields` is the hook to build one on.
|
|
118
123
|
|
|
119
124
|
## Filter by voice
|
package/dist/dialog/dialog.js
CHANGED
|
@@ -50,9 +50,16 @@ export function KptDialog({ open, onOpenChange, header = '', size = 'md', dismis
|
|
|
50
50
|
* `close` przychodzi z każdej drogi. Przy `dismissible=false` zamknąć wolno tylko z kodu:
|
|
51
51
|
* Chromium z CloseWatcherem pozwala anulować `cancel` jedynie po aktywacji użytkownika, więc drugi
|
|
52
52
|
* Escape z rzędu zamyka okno mimo `preventDefault` - wtedy otwieramy je z powrotem.
|
|
53
|
+
*
|
|
54
|
+
* Zdarzenie `close` jest asynchroniczne, więc może dojść, gdy okno jest już znowu otwarte: pod
|
|
55
|
+
* `StrictMode` efekt montuje się dwa razy (`showModal` -> sprzątanie `close` -> `showModal`)
|
|
56
|
+
* i dialog zamontowany od razu z `open` dostawał `onOpenChange(false)` od własnego sprzątania.
|
|
57
|
+
* Otwarty element znaczy, że to zamknięcie jest nieaktualne.
|
|
53
58
|
*/
|
|
54
59
|
const onClose = () => {
|
|
55
60
|
const el = dialogRef.current;
|
|
61
|
+
if (el?.open)
|
|
62
|
+
return;
|
|
56
63
|
if (!dismissible && open && el && !el.open) {
|
|
57
64
|
el.showModal();
|
|
58
65
|
return;
|
package/dist/input/input.d.ts
CHANGED
|
@@ -42,6 +42,11 @@ export interface KptInputProps extends Omit<ComponentPropsWithRef<'input'>, 'typ
|
|
|
42
42
|
* gdzie zastępuje systemowe "x" (ukryte zawsze, także przy `clearable={false}`).
|
|
43
43
|
*/
|
|
44
44
|
clearable?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Ikona z rejestru na początku pola (`search`, `mail`...). Dekoracyjna - nazwę pola daje etykieta
|
|
47
|
+
* albo `aria-label`, więc ikona nie trafia do drzewa dostępności.
|
|
48
|
+
*/
|
|
49
|
+
icon?: string;
|
|
45
50
|
}
|
|
46
51
|
/**
|
|
47
52
|
* Pole tekstowe Koncept UI. Renderuje natywny `<input>` w hoście `.kpt-input-host`.
|
|
@@ -57,4 +62,4 @@ export interface KptInputProps extends Omit<ComponentPropsWithRef<'input'>, 'typ
|
|
|
57
62
|
* <KptInput name="email" type="email" defaultValue="" />
|
|
58
63
|
* <KptInput value={email} onValueChange={setEmail} invalid={!ok} touched={blurred} />
|
|
59
64
|
*/
|
|
60
|
-
export declare function KptInput({ type, required, invalid, touched, onValueChange, onTouch, hostProps, className, id, onChange, onBlur, ref, speech, speechLang, speechColor, speechMode, clearable, onKeyDown, locale, dictionary, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy, ...rest }: Readonly<KptInputProps>): import("react").JSX.Element;
|
|
65
|
+
export declare function KptInput({ type, required, invalid, touched, onValueChange, onTouch, hostProps, className, id, onChange, onBlur, ref, speech, speechLang, speechColor, speechMode, clearable, icon, onKeyDown, locale, dictionary, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy, ...rest }: Readonly<KptInputProps>): import("react").JSX.Element;
|
package/dist/input/input.js
CHANGED
|
@@ -30,7 +30,7 @@ function setFieldValue(field, value) {
|
|
|
30
30
|
* <KptInput name="email" type="email" defaultValue="" />
|
|
31
31
|
* <KptInput value={email} onValueChange={setEmail} invalid={!ok} touched={blurred} />
|
|
32
32
|
*/
|
|
33
|
-
export function KptInput({ type = 'text', required = false, invalid = false, touched, onValueChange, onTouch, hostProps, className, id, onChange, onBlur, ref, speech = false, speechLang, speechColor, speechMode = 'insert', clearable, onKeyDown, locale, dictionary, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy, ...rest }) {
|
|
33
|
+
export function KptInput({ type = 'text', required = false, invalid = false, touched, onValueChange, onTouch, hostProps, className, id, onChange, onBlur, ref, speech = false, speechLang, speechColor, speechMode = 'insert', clearable, icon, onKeyDown, locale, dictionary, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy, ...rest }) {
|
|
34
34
|
const { className: hostClassName, ...hostRest } = hostProps ?? {};
|
|
35
35
|
const field = useKptFieldA11y(id, 'kpt-input');
|
|
36
36
|
const innerRef = useRef(null);
|
|
@@ -92,5 +92,5 @@ export function KptInput({ type = 'text', required = false, invalid = false, tou
|
|
|
92
92
|
onBlur?.(event);
|
|
93
93
|
onTouch?.();
|
|
94
94
|
}, [onBlur, onTouch]);
|
|
95
|
-
return (_jsxs("span", { className: cn('kpt-input-host', hostClassName), "data-speech": speechState, "data-clear": showClear ? '' : undefined, ...hostRest, children: [_jsx("input", { className: cn('kpt-input__field', className), id: field.id, "aria-describedby": joinIds(ariaDescribedBy, field.describedBy), type: type, "aria-required": required || undefined, "aria-invalid": ariaInvalid ?? (showInvalid || undefined), onChange: handleChange, onBlur: handleBlur, onKeyDown: handleKeyDown, ref: composeRefs(innerRef, ref), ...rest }), showClear ? (_jsx("button", { type: "button", className: "kpt-input__clear", tabIndex: -1, "aria-label": t('input.clear'), onMouseDown: (event) => event.preventDefault(), onClick: clear, children: _jsx(KptIcon, { name: "x" }) })) : null, speech ? (_jsx(KptSpeechButton, { listeningColor: speechColor, speech: mic, size: "sm", unsupported: "hide", disabled: !!rest.disabled || !!rest.readOnly, hostProps: { className: 'kpt-input__speech' } })) : null] }));
|
|
95
|
+
return (_jsxs("span", { className: cn('kpt-input-host', hostClassName), "data-speech": speechState, "data-clear": showClear ? '' : undefined, "data-icon": icon ? '' : undefined, ...hostRest, children: [icon ? _jsx(KptIcon, { className: "kpt-input__icon", name: icon }) : null, _jsx("input", { className: cn('kpt-input__field', className), id: field.id, "aria-describedby": joinIds(ariaDescribedBy, field.describedBy), type: type, "aria-required": required || undefined, "aria-invalid": ariaInvalid ?? (showInvalid || undefined), onChange: handleChange, onBlur: handleBlur, onKeyDown: handleKeyDown, ref: composeRefs(innerRef, ref), ...rest }), showClear ? (_jsx("button", { type: "button", className: "kpt-input__clear", tabIndex: -1, "aria-label": t('input.clear'), onMouseDown: (event) => event.preventDefault(), onClick: clear, children: _jsx(KptIcon, { name: "x" }) })) : null, speech ? (_jsx(KptSpeechButton, { listeningColor: speechColor, speech: mic, size: "sm", unsupported: "hide", disabled: !!rest.disabled || !!rest.readOnly, hostProps: { className: 'kpt-input__speech' } })) : null] }));
|
|
96
96
|
}
|
package/dist/input/llms.txt
CHANGED
|
@@ -16,6 +16,9 @@ Import: `import { KptInput } from '@konce-pt/react';`
|
|
|
16
16
|
- `required`: boolean - see the note below
|
|
17
17
|
- `invalid`: boolean, `touched`: boolean - the error state
|
|
18
18
|
- `clearable`: boolean (default: `true` only for `type="search"`) - a clear button, see "Clear button"
|
|
19
|
+
- `icon`: string - a registry icon at the start of the field (`search`, `mail`...). Decorative: the field's
|
|
20
|
+
name comes from the label or the aria label, so the icon stays out of the accessibility tree; a click on it
|
|
21
|
+
lands in the field
|
|
19
22
|
- `hostProps` - props for the `.kpt-input-host` wrapper
|
|
20
23
|
- `className`, `ref`, `name`, `placeholder`, `autoComplete` and every other `<input>` prop go to
|
|
21
24
|
the inner `<input>`
|
|
@@ -66,6 +69,7 @@ dictionary. The host gets `data-clear` while the button is visible.
|
|
|
66
69
|
|
|
67
70
|
<KptInput type="search" value={query} onValueChange={setQuery} />
|
|
68
71
|
<KptInput clearable value={coupon} onValueChange={setCoupon} />
|
|
72
|
+
<KptInput type="search" icon="search" value={query} onValueChange={setQuery} />
|
|
69
73
|
|
|
70
74
|
## Tokens
|
|
71
75
|
`--kpt-form-field-{bg,text,placeholder,border,border-hover,border-focus,border-error,radius}`.
|
package/dist/popover/llms.txt
CHANGED
|
@@ -17,6 +17,9 @@ Import: `import { KptPopover } from '@konce-pt/react';`
|
|
|
17
17
|
- `placement`: 'bottom' (default) | 'top' - the preferred side; it flips when there is no room
|
|
18
18
|
- `maxWidth`: string - the panel's maximum width, any CSS length (`'24rem'`; default `20rem`). The panel is
|
|
19
19
|
never wider than the window minus the 8 px overlay margin
|
|
20
|
+
- `ariaLabel`: string - the accessible name of the panel (`role="dialog"`); give one whenever the panel has
|
|
21
|
+
no visible heading. It names the panel, not the host - `aria-label` in the rest props would land on the
|
|
22
|
+
host `<span>`
|
|
20
23
|
- `className`, `ref` and every other `<span>` prop go to the host
|
|
21
24
|
|
|
22
25
|
## The panel is a portal
|
package/dist/public-api.d.ts
CHANGED
|
@@ -74,7 +74,7 @@ export { KptDataTable } from './data-table/data-table.tsx';
|
|
|
74
74
|
export type { KptDataTableProps } from './data-table/data-table.tsx';
|
|
75
75
|
export { exportTableCsv } from './data-table/export-csv.ts';
|
|
76
76
|
export type { KptCellEditContext, KptCellEditEvent, KptCellEditRenderers, KptCellRenderers, KptColumn, KptColumnEdit, KptColumnEditType, KptSortDir, KptTableLazyState, } from './data-table/data-table-types.ts';
|
|
77
|
-
export type { KptColumnFilter, KptColumnFilterType, KptColumnSummary, KptSummaryFn } from '@konce-pt/table';
|
|
77
|
+
export type { KptColumnFilter, KptColumnFilterType, KptColumnGroup, KptColumnSummary, KptSummaryFn } from '@konce-pt/table';
|
|
78
78
|
export { KptDataView } from './data-view/data-view.tsx';
|
|
79
79
|
export type { KptDataViewLayout, KptDataViewProps } from './data-view/data-view.tsx';
|
|
80
80
|
export { KptTree } from './tree/tree.tsx';
|
|
@@ -123,7 +123,7 @@ export type { KptOverlayProps } from './internal/overlay.tsx';
|
|
|
123
123
|
export { KptButtonGroup } from './button-group/button-group.tsx';
|
|
124
124
|
export type { KptButtonGroupProps } from './button-group/button-group.tsx';
|
|
125
125
|
export { KptSelect } from './select/select.tsx';
|
|
126
|
-
export type { KptSelectProps } from './select/select.tsx';
|
|
126
|
+
export type { KptSelectMultipleProps, KptSelectProps, KptSelectSingleProps } from './select/select.tsx';
|
|
127
127
|
export type { KptAlertVariant, KptNavigateEvent, KptOption } from './shared/types.ts';
|
|
128
128
|
export { KptFormField } from './form-field/form-field.tsx';
|
|
129
129
|
export type { KptFormFieldLabelType, KptFormFieldProps } from './form-field/form-field.tsx';
|
package/dist/select/llms.txt
CHANGED
|
@@ -15,7 +15,11 @@ A select on its own panel. Variants: single, with a search box (`filter`) and mu
|
|
|
15
15
|
|
|
16
16
|
## Props
|
|
17
17
|
- `options`: `KptOption<T>[]` - `{ label, value, disabled? }`
|
|
18
|
-
- `value` / `defaultValue` / `onValueChange` - a single `T`, or `T[]` in `multiple` mode
|
|
18
|
+
- `value` / `defaultValue` / `onValueChange` - a single `T`, or `T[]` in `multiple` mode. The handler type
|
|
19
|
+
follows `multiple` (overloads): without it `(value: T) => void` - a single select cannot clear its
|
|
20
|
+
choice, so it never reports an array or `null`; with `multiple` `(value: T[]) => void`. A flag computed at
|
|
21
|
+
run time (`multiple={flag}`) keeps the wide `T | T[] | null`. Types: `KptSelectSingleProps`,
|
|
22
|
+
`KptSelectMultipleProps`, `KptSelectProps` (the wide one)
|
|
19
23
|
- `open` / `defaultOpen` / `onOpenChange` - the panel state, so the list can be opened from outside
|
|
20
24
|
- `placeholder`, `multiple`, `filter`, `disabled`, `invalid`, `touched`
|
|
21
25
|
- `onTouch()` - fires when the panel closes or focus leaves the closed trigger
|
package/dist/select/select.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ComponentPropsWithRef } from 'react';
|
|
1
|
+
import { type ComponentPropsWithRef, type JSX } from 'react';
|
|
2
2
|
import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
|
|
3
3
|
import type { KptOption } from '../shared/types.ts';
|
|
4
4
|
export interface KptSelectProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'children' | 'onChange' | 'defaultValue'>, KptI18nOverrides {
|
|
@@ -28,6 +28,23 @@ export interface KptSelectProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'c
|
|
|
28
28
|
/** Wywoływane, gdy panel się zamyka albo trigger traci fokus. */
|
|
29
29
|
onTouch?: () => void;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Pojedynczy wybór: `onValueChange` dostaje zawsze wybraną opcję - nigdy tablicę ani `null`, bo
|
|
33
|
+
* lista bez `multiple` nie ma jak wyczyścić wyboru. Rodzic trzyma więc zwykłe `T`.
|
|
34
|
+
*/
|
|
35
|
+
export interface KptSelectSingleProps<T> extends Omit<KptSelectProps<T>, 'multiple' | 'value' | 'defaultValue' | 'onValueChange'> {
|
|
36
|
+
multiple?: false;
|
|
37
|
+
value?: T | null;
|
|
38
|
+
defaultValue?: T | null;
|
|
39
|
+
onValueChange?: (value: T) => void;
|
|
40
|
+
}
|
|
41
|
+
/** Wybór wielokrotny: wartością jest zawsze tablica (pusta, gdy nic nie wybrano). */
|
|
42
|
+
export interface KptSelectMultipleProps<T> extends Omit<KptSelectProps<T>, 'multiple' | 'value' | 'defaultValue' | 'onValueChange'> {
|
|
43
|
+
multiple: true;
|
|
44
|
+
value?: T[] | null;
|
|
45
|
+
defaultValue?: T[] | null;
|
|
46
|
+
onValueChange?: (value: T[]) => void;
|
|
47
|
+
}
|
|
31
48
|
/**
|
|
32
49
|
* Lista wyboru (select) na własnym panelu. Warianty: pojedynczy, z wyszukiwarką (`filter`)
|
|
33
50
|
* i wielokrotny (`multiple`, chipy).
|
|
@@ -46,5 +63,10 @@ export interface KptSelectProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'c
|
|
|
46
63
|
* @example
|
|
47
64
|
* <KptSelect options={roles} value={role} onValueChange={setRole} placeholder="Wybierz..." />
|
|
48
65
|
* <KptSelect options={tags} multiple filter value={tags} onValueChange={setTags} />
|
|
66
|
+
*
|
|
67
|
+
* Typ `onValueChange` idzie za `multiple`: bez niego `(value: T)`, z `multiple` - `(value: T[])`.
|
|
68
|
+
* `multiple` liczone w locie (`multiple={flag}`) zostawia szeroki typ `T | T[] | null`.
|
|
49
69
|
*/
|
|
50
|
-
export declare function KptSelect<T>(
|
|
70
|
+
export declare function KptSelect<T>(props: Readonly<KptSelectSingleProps<T>>): JSX.Element;
|
|
71
|
+
export declare function KptSelect<T>(props: Readonly<KptSelectMultipleProps<T>>): JSX.Element;
|
|
72
|
+
export declare function KptSelect<T>(props: Readonly<KptSelectProps<T>>): JSX.Element;
|
package/dist/select/select.js
CHANGED
|
@@ -18,25 +18,6 @@ function filterSelectOptions(options, text) {
|
|
|
18
18
|
const q = foldMentionText(text.trim());
|
|
19
19
|
return q ? options.filter((option) => foldMentionText(option.label).includes(q)) : options;
|
|
20
20
|
}
|
|
21
|
-
/**
|
|
22
|
-
* Lista wyboru (select) na własnym panelu. Warianty: pojedynczy, z wyszukiwarką (`filter`)
|
|
23
|
-
* i wielokrotny (`multiple`, chipy).
|
|
24
|
-
*
|
|
25
|
-
* Klawiatura według wzorca WAI-ARIA "select-only combobox": fokus zostaje na polu, a aktywna opcja
|
|
26
|
-
* idzie przez `aria-activedescendant`. `ArrowDown`/`ArrowUp`/`Enter`/`Spacja` otwierają listę na wybranej opcji,
|
|
27
|
-
* strzałki, `Home`/`End`, `PageUp`/`PageDown` chodzą po opcjach (z pominięciem wyłączonych),
|
|
28
|
-
* wpisywane litery skaczą do pasującej opcji (bez polskich znaków), `Enter` wybiera, `Escape`
|
|
29
|
-
* zamyka. Z `filter` fokus przechodzi do pola wyszukiwania, które obsługuje te same klawisze.
|
|
30
|
-
* W trybie `multiple` `Backspace` na zamkniętym polu usuwa ostatni wybór.
|
|
31
|
-
*
|
|
32
|
-
* Wewnątrz `KptFormField` zgłasza wypełnienie hookiem `useKptFormFieldFilled`, więc etykieta
|
|
33
|
-
* Float Label unosi się nad wybraną wartością - natywnego pola, z którego wrapper mógłby to
|
|
34
|
-
* odczytać, tu nie ma.
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* <KptSelect options={roles} value={role} onValueChange={setRole} placeholder="Wybierz..." />
|
|
38
|
-
* <KptSelect options={tags} multiple filter value={tags} onValueChange={setTags} />
|
|
39
|
-
*/
|
|
40
21
|
export function KptSelect({ options = [], placeholder = '', multiple = false, filter = false, disabled = false, readOnly = false, invalid = false, touched, value, defaultValue = null, onValueChange, open, defaultOpen = false, onOpenChange, onTouch, locale, dictionary, className, onBlur, id, required = false, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedBy, ...rest }) {
|
|
41
22
|
/** `div` z rolą nie dostaje nazwy z `label[for]` - stąd `aria-labelledby` na etykietę wrappera. */
|
|
42
23
|
const field = useKptFieldA11y(id, 'kpt-select-trigger');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@konce-pt/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Open-source React 19 component library on the Koncept UI design system - the same kpt-* CSS layer and --kpt-* tokens as the Angular port. Zero runtime dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "konce.pt",
|
|
@@ -65,19 +65,19 @@
|
|
|
65
65
|
"leaflet": "^1.9.0"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@konce-pt/
|
|
69
|
-
"@konce-pt/
|
|
70
|
-
"@konce-pt/
|
|
71
|
-
"@konce-pt/
|
|
72
|
-
"@konce-pt/
|
|
73
|
-
"@konce-pt/
|
|
74
|
-
"@konce-pt/icons": "0.
|
|
75
|
-
"@konce-pt/
|
|
76
|
-
"@konce-pt/
|
|
77
|
-
"@konce-pt/
|
|
78
|
-
"@konce-pt/
|
|
79
|
-
"@konce-pt/tokens": "0.
|
|
80
|
-
"@konce-pt/map": "0.
|
|
68
|
+
"@konce-pt/backdrop": "0.18.0",
|
|
69
|
+
"@konce-pt/mention": "0.18.0",
|
|
70
|
+
"@konce-pt/chat": "0.18.0",
|
|
71
|
+
"@konce-pt/chart": "0.18.0",
|
|
72
|
+
"@konce-pt/speech": "0.18.0",
|
|
73
|
+
"@konce-pt/i18n": "0.18.0",
|
|
74
|
+
"@konce-pt/icons": "0.18.0",
|
|
75
|
+
"@konce-pt/motion": "0.18.0",
|
|
76
|
+
"@konce-pt/roadmap": "0.18.0",
|
|
77
|
+
"@konce-pt/datetime": "0.18.0",
|
|
78
|
+
"@konce-pt/table": "0.18.0",
|
|
79
|
+
"@konce-pt/tokens": "0.18.0",
|
|
80
|
+
"@konce-pt/map": "0.18.0"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
83
|
"@types/react": "^19.2.0",
|