@konce-pt/react 0.8.3 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/backdrop/backdrop.d.ts +79 -0
- package/dist/backdrop/backdrop.js +195 -0
- package/dist/backdrop/llms.txt +132 -0
- package/dist/chat/chat.d.ts +53 -0
- package/dist/chat/chat.js +160 -0
- package/dist/chat/llms.txt +78 -0
- package/dist/chat-composer/chat-composer.d.ts +41 -0
- package/dist/chat-composer/chat-composer.js +81 -0
- package/dist/chat-composer/llms.txt +56 -0
- package/dist/chat-dock/chat-dock.d.ts +40 -0
- package/dist/chat-dock/chat-dock.js +67 -0
- package/dist/chat-dock/llms.txt +71 -0
- package/dist/chat-entry/public-api.d.ts +12 -0
- package/dist/chat-entry/public-api.js +10 -0
- package/dist/chat-message/chat-message.d.ts +53 -0
- package/dist/chat-message/chat-message.js +112 -0
- package/dist/chat-message/llms.txt +73 -0
- package/dist/chat-typing/chat-typing.d.ts +23 -0
- package/dist/chat-typing/chat-typing.js +30 -0
- package/dist/chat-typing/llms.txt +41 -0
- package/dist/chip/chip.d.ts +10 -1
- package/dist/chip/chip.js +3 -2
- package/dist/chip/llms.txt +9 -0
- package/dist/data-view/data-view.d.ts +15 -3
- package/dist/data-view/data-view.js +22 -5
- package/dist/data-view/llms.txt +20 -7
- package/dist/internal/dev.d.ts +1 -0
- package/dist/internal/dev.js +3 -0
- package/dist/internal/overlay.d.ts +6 -1
- package/dist/internal/overlay.js +5 -3
- package/dist/mention/llms.txt +60 -0
- package/dist/mention/mention.d.ts +38 -0
- package/dist/mention/mention.js +29 -0
- package/dist/mention/mentions.d.ts +31 -0
- package/dist/mention/mentions.js +24 -0
- package/dist/order-list/llms.txt +16 -5
- package/dist/order-list/order-list.d.ts +5 -3
- package/dist/order-list/order-list.js +69 -4
- package/dist/peek/llms.txt +42 -0
- package/dist/peek/peek.d.ts +35 -0
- package/dist/peek/peek.js +127 -0
- package/dist/pick-list/llms.txt +19 -5
- package/dist/pick-list/pick-list.d.ts +7 -2
- package/dist/pick-list/pick-list.js +123 -17
- package/dist/public-api.d.ts +13 -1
- package/dist/public-api.js +6 -0
- package/dist/rich-text/llms.txt +10 -0
- package/dist/rich-text/rich-text.d.ts +11 -1
- package/dist/rich-text/rich-text.js +157 -5
- package/dist/rich-text/sanitize.js +10 -1
- package/dist/roadmap/roadmap.js +1 -3
- package/package.json +16 -9
package/dist/public-api.d.ts
CHANGED
|
@@ -36,8 +36,12 @@ export { KptAvatar } from './avatar/avatar.tsx';
|
|
|
36
36
|
export type { KptAvatarProps, KptAvatarShape, KptAvatarSize } from './avatar/avatar.tsx';
|
|
37
37
|
export { KptBadge } from './badge/badge.tsx';
|
|
38
38
|
export type { KptBadgeProps, KptBadgeSize, KptBadgeVariant } from './badge/badge.tsx';
|
|
39
|
+
export { KptBackdrop } from './backdrop/backdrop.tsx';
|
|
40
|
+
export type { KptBackdropAnchor, KptBackdropEffect, KptBackdropProps } from './backdrop/backdrop.tsx';
|
|
41
|
+
export type { KptBackdropRenderOptions, KptBackdropRenderer, KptOklab, KptRgb, KptSignalGridCell, KptSignalGridFrame, KptSignalGridOptions, KptWaveOptions, KptWaveSettings, } from '@konce-pt/backdrop';
|
|
42
|
+
export { KPT_SIGNAL_GRID_CELL, KPT_SIGNAL_GRID_DENSITY, KPT_SIGNAL_GRID_FALLOFF, KPT_SIGNAL_GRID_GAP, KPT_WAVE_AMPLITUDE, KPT_WAVE_BAND, KPT_WAVE_RESOLUTION, KPT_WAVE_ROTATION, buildSignalGrid, createBackdropRenderer, mixOklch, readCssColor, resolveWave, shiftHue, signalGridAlpha, signalGridDensity, waveIntensity, waveRamp, } from '@konce-pt/backdrop';
|
|
39
43
|
export { KptChip } from './chip/chip.tsx';
|
|
40
|
-
export type { KptChipProps, KptChipVariant } from './chip/chip.tsx';
|
|
44
|
+
export type { KptChipProps, KptChipTextTransform, KptChipVariant } from './chip/chip.tsx';
|
|
41
45
|
export { KptDialog, KptDialogFooter } from './dialog/dialog.tsx';
|
|
42
46
|
export type { KptDialogProps } from './dialog/dialog.tsx';
|
|
43
47
|
export { KptDrawer } from './drawer/drawer.tsx';
|
|
@@ -102,6 +106,14 @@ export { KptTooltip } from './tooltip/tooltip.tsx';
|
|
|
102
106
|
export type { KptTooltipPosition, KptTooltipProps } from './tooltip/tooltip.tsx';
|
|
103
107
|
export { KptPopover } from './popover/popover.tsx';
|
|
104
108
|
export type { KptPopoverProps } from './popover/popover.tsx';
|
|
109
|
+
export { KptPeek } from './peek/peek.tsx';
|
|
110
|
+
export type { KptPeekPlacement, KptPeekProps } from './peek/peek.tsx';
|
|
111
|
+
export { KptMention } from './mention/mention.tsx';
|
|
112
|
+
export type { KptMentionProps } from './mention/mention.tsx';
|
|
113
|
+
export { KptMentions } from './mention/mentions.tsx';
|
|
114
|
+
export type { KptMentionsProps } from './mention/mentions.tsx';
|
|
115
|
+
export type { KptMentionItem, KptMentionQuery, KptMentionSegment } from '@konce-pt/mention';
|
|
116
|
+
export { KPT_MENTION_LIMIT, applyMention, collectMentions, filterMentions, foldMentionText, mentionQuery, mentionToken, parseMentionText, } from '@konce-pt/mention';
|
|
105
117
|
export { KptOverlay } from './internal/overlay.tsx';
|
|
106
118
|
export type { KptOverlayProps } from './internal/overlay.tsx';
|
|
107
119
|
export { KptButtonGroup } from './button-group/button-group.tsx';
|
package/dist/public-api.js
CHANGED
|
@@ -27,6 +27,8 @@ export { KptIcon } from "./icon/icon.js";
|
|
|
27
27
|
export { getKptIcon, hasKptIcon, registerKptIcons } from "./icon/icon-registry.js";
|
|
28
28
|
export { KptAvatar } from "./avatar/avatar.js";
|
|
29
29
|
export { KptBadge } from "./badge/badge.js";
|
|
30
|
+
export { KptBackdrop } from "./backdrop/backdrop.js";
|
|
31
|
+
export { KPT_SIGNAL_GRID_CELL, KPT_SIGNAL_GRID_DENSITY, KPT_SIGNAL_GRID_FALLOFF, KPT_SIGNAL_GRID_GAP, KPT_WAVE_AMPLITUDE, KPT_WAVE_BAND, KPT_WAVE_RESOLUTION, KPT_WAVE_ROTATION, buildSignalGrid, createBackdropRenderer, mixOklch, readCssColor, resolveWave, shiftHue, signalGridAlpha, signalGridDensity, waveIntensity, waveRamp, } from '@konce-pt/backdrop';
|
|
30
32
|
export { KptChip } from "./chip/chip.js";
|
|
31
33
|
// Komponenty — nakładki
|
|
32
34
|
export { KptDialog, KptDialogFooter } from "./dialog/dialog.js";
|
|
@@ -60,6 +62,10 @@ export { KptBreadcrumb } from "./breadcrumb/breadcrumb.js";
|
|
|
60
62
|
export { KptContextMenu } from "./context-menu/context-menu.js";
|
|
61
63
|
export { KptTooltip } from "./tooltip/tooltip.js";
|
|
62
64
|
export { KptPopover } from "./popover/popover.js";
|
|
65
|
+
export { KptPeek } from "./peek/peek.js";
|
|
66
|
+
export { KptMention } from "./mention/mention.js";
|
|
67
|
+
export { KptMentions } from "./mention/mentions.js";
|
|
68
|
+
export { KPT_MENTION_LIMIT, applyMention, collectMentions, filterMentions, foldMentionText, mentionQuery, mentionToken, parseMentionText, } from '@konce-pt/mention';
|
|
63
69
|
export { KptOverlay } from "./internal/overlay.js";
|
|
64
70
|
// Komponenty — formularze
|
|
65
71
|
export { KptButtonGroup } from "./button-group/button-group.js";
|
package/dist/rich-text/llms.txt
CHANGED
|
@@ -9,8 +9,18 @@ Import: `import { KptRichText, type KptRichTool } from '@konce-pt/react';`
|
|
|
9
9
|
- `toolbar`: `KptRichTool[] | KptRichTool[][]` — a flat list, or groups = sections separated by a
|
|
10
10
|
divider. Leaving a tool or a group out disables it; the full toolbar in 5 sections by default.
|
|
11
11
|
- `placeholder`, `maxListLevel` (6), `disabled`, `invalid`, `touched`, `onTouch()`
|
|
12
|
+
- `mentions`: `readonly KptMentionItem[]`, `mentionLimit` (8), `onMention(item)` — suggestions
|
|
13
|
+
for `@`; an empty list turns the mechanism off, so `@` stays an ordinary character
|
|
12
14
|
- `className`, `ref` and every other `<div>` prop go to the host
|
|
13
15
|
|
|
16
|
+
## Mentions (`@`)
|
|
17
|
+
With `mentions` set, typing `@` opens a suggestion list anchored at the caret: arrows move through
|
|
18
|
+
it, Enter or Tab inserts, Escape closes. The query is read from the **text node** at the caret and
|
|
19
|
+
recognised by `mentionQuery()` from `@konce-pt/mention` — the same rules as in the Angular port.
|
|
20
|
+
An inserted mention is `<span class="kpt-mention" data-mention="id" contenteditable="false">@Name`
|
|
21
|
+
plus a non-breaking space: the id survives saving, the flag keeps the mention in one piece and the
|
|
22
|
+
space gives the caret a place to stand after it.
|
|
23
|
+
|
|
14
24
|
## Tools (KptRichTool)
|
|
15
25
|
Clipboard: `copy`, `paste` (sanitized), `pasteText`. Style: `heading` (a panel: Normal + H1–H6).
|
|
16
26
|
Format: `bold`, `italic`, `underline`, `strike`, `ul`, `ol`, `indent`, `outdent`.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ComponentPropsWithRef } from 'react';
|
|
2
|
+
import { type KptMentionItem } from '@konce-pt/mention';
|
|
2
3
|
import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
|
|
3
4
|
export type KptRichTool = 'copy' | 'paste' | 'pasteText' | 'heading' | 'bold' | 'italic' | 'underline' | 'strike' | 'ul' | 'ol' | 'indent' | 'outdent' | 'foreColor' | 'backColor' | 'alignLeft' | 'alignCenter' | 'alignRight' | 'alignJustify' | 'link' | 'table' | 'clear' | 'html';
|
|
4
5
|
/** Pasek: płaska lista narzędzi albo grupy — sekcje rozdzielone separatorem. */
|
|
@@ -16,6 +17,15 @@ export interface KptRichTextProps extends Omit<ComponentPropsWithRef<'div'>, 'on
|
|
|
16
17
|
invalid?: boolean;
|
|
17
18
|
touched?: boolean;
|
|
18
19
|
onTouch?: () => void;
|
|
20
|
+
/**
|
|
21
|
+
* Pozycje do podpowiedzi wzmianek. Pusta lista wyłącza mechanizm: `@` zostaje wtedy zwykłym
|
|
22
|
+
* znakiem, bo nie ma czego podpowiadać.
|
|
23
|
+
*/
|
|
24
|
+
mentions?: readonly KptMentionItem[];
|
|
25
|
+
/** Ile podpowiedzi naraz. */
|
|
26
|
+
mentionLimit?: number;
|
|
27
|
+
/** Wstawiona wzmianka — moment na powiadomienie osoby albo dopięcie powiązania. */
|
|
28
|
+
onMention?: (item: KptMentionItem) => void;
|
|
19
29
|
}
|
|
20
30
|
/**
|
|
21
31
|
* Lekki edytor tekstu sformatowanego na `contenteditable`, z konfigurowalnym paskiem sekcji
|
|
@@ -25,4 +35,4 @@ export interface KptRichTextProps extends Omit<ComponentPropsWithRef<'div'>, 'on
|
|
|
25
35
|
* <KptRichText value={html} onValueChange={setHtml} />
|
|
26
36
|
* <KptRichText value={html} onValueChange={setHtml} toolbar={[['bold', 'italic'], ['link']]} />
|
|
27
37
|
*/
|
|
28
|
-
export declare function KptRichText({ value, defaultValue, onValueChange, toolbar, placeholder, maxListLevel, disabled, invalid, touched, onTouch, locale, dictionary, className, ...rest }: Readonly<KptRichTextProps>): import("react").JSX.Element;
|
|
38
|
+
export declare function KptRichText({ value, defaultValue, onValueChange, toolbar, placeholder, maxListLevel, disabled, invalid, touched, onTouch, mentions, mentionLimit, onMention, locale, dictionary, className, ...rest }: Readonly<KptRichTextProps>): import("react").JSX.Element;
|
|
@@ -3,6 +3,7 @@ import { useCallback, useEffect, useRef, useState, } from 'react';
|
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
import { cn } from "../internal/cn.js";
|
|
5
5
|
import { useControllableState } from "../internal/use-controllable-state.js";
|
|
6
|
+
import { KPT_MENTION_LIMIT, filterMentions, mentionQuery } from '@konce-pt/mention';
|
|
6
7
|
import { useKptOverlayContainer } from "../internal/overlay-container.js";
|
|
7
8
|
import { KptOverlay } from "../internal/overlay.js";
|
|
8
9
|
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
@@ -85,7 +86,7 @@ function legacyState(cmd) {
|
|
|
85
86
|
* <KptRichText value={html} onValueChange={setHtml} />
|
|
86
87
|
* <KptRichText value={html} onValueChange={setHtml} toolbar={[['bold', 'italic'], ['link']]} />
|
|
87
88
|
*/
|
|
88
|
-
export function KptRichText({ value, defaultValue = '', onValueChange, toolbar = DEFAULT_TOOLBAR, placeholder = '', maxListLevel = 6, disabled = false, invalid = false, touched, onTouch, locale, dictionary, className, ...rest }) {
|
|
89
|
+
export function KptRichText({ value, defaultValue = '', onValueChange, toolbar = DEFAULT_TOOLBAR, placeholder = '', maxListLevel = 6, disabled = false, invalid = false, touched, onTouch, mentions = [], mentionLimit = KPT_MENTION_LIMIT, onMention, locale, dictionary, className, ...rest }) {
|
|
89
90
|
const { t } = useKptMessages({ locale, dictionary });
|
|
90
91
|
const container = useKptOverlayContainer();
|
|
91
92
|
const editorRef = useRef(null);
|
|
@@ -105,6 +106,14 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
105
106
|
const [sourceView, setSourceView] = useState(false);
|
|
106
107
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
107
108
|
const [menuPoint, setMenuPoint] = useState({ x: 0, y: 0 });
|
|
109
|
+
// ── Wzmianki (`@`) ──
|
|
110
|
+
const [mentionOpen, setMentionOpen] = useState(false);
|
|
111
|
+
const [mentionMatches, setMentionMatches] = useState([]);
|
|
112
|
+
const [mentionActive, setMentionActive] = useState(0);
|
|
113
|
+
/** Zaczepienie listy w miejscu kursora — punkt, nie element, bo `@` nie ma własnego elementu. */
|
|
114
|
+
const [mentionPoint, setMentionPoint] = useState({ x: 0, y: 0 });
|
|
115
|
+
/** Węzeł tekstowy i zakres zapytania `@…`, który podmieni wybrana pozycja. */
|
|
116
|
+
const mentionRange = useRef(null);
|
|
108
117
|
const [borderOpen, setBorderOpen] = useState(false);
|
|
109
118
|
const [borderTarget, setBorderTarget] = useState('table');
|
|
110
119
|
const [activeCmds, setActiveCmds] = useState(new Set());
|
|
@@ -175,13 +184,106 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
175
184
|
selCells.current = new Set();
|
|
176
185
|
setSelCount(0);
|
|
177
186
|
}, []);
|
|
187
|
+
/** Zamknięcie podpowiedzi — czyści też zapamiętany zakres zapytania. */
|
|
188
|
+
const closeMention = useCallback(() => {
|
|
189
|
+
mentionRange.current = null;
|
|
190
|
+
setMentionMatches([]);
|
|
191
|
+
setMentionOpen(false);
|
|
192
|
+
}, []);
|
|
193
|
+
/** Punkt pod kursorem — lista podpowiedzi zaczepia się o niego, bo `@` nie ma swojego elementu. */
|
|
194
|
+
const caretPoint = useCallback((node, offset) => {
|
|
195
|
+
const range = document.createRange();
|
|
196
|
+
range.setStart(node, Math.min(offset, node.length));
|
|
197
|
+
range.collapse(true);
|
|
198
|
+
const rect = range.getBoundingClientRect();
|
|
199
|
+
// Zwinięty zakres na początku wiersza potrafi dać zerowy prostokąt — wtedy bierzemy
|
|
200
|
+
// krawędź samego edytora, żeby lista nie wylądowała w rogu ekranu.
|
|
201
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
202
|
+
const fallback = editorRef.current?.getBoundingClientRect();
|
|
203
|
+
return { x: fallback?.left ?? 0, y: fallback?.top ?? 0 };
|
|
204
|
+
}
|
|
205
|
+
return { x: rect.left, y: rect.bottom };
|
|
206
|
+
}, []);
|
|
207
|
+
/**
|
|
208
|
+
* Sprawdza, czy kursor stoi w zapytaniu `@…`, i ustawia listę podpowiedzi.
|
|
209
|
+
*
|
|
210
|
+
* Zapytanie liczymy z **węzła tekstowego**, nie z całego HTML-a: w treści siedzą tagi, a `@`
|
|
211
|
+
* ma widzieć tylko to, co użytkownik naprawdę napisał w tym miejscu. Rozpoznanie zostawiamy
|
|
212
|
+
* `mentionQuery()` z `@konce-pt/mention` — te same reguły działają w obu portach.
|
|
213
|
+
*/
|
|
214
|
+
const syncMention = useCallback(() => {
|
|
215
|
+
if (disabled || !mentions.length) {
|
|
216
|
+
closeMention();
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const selection = window.getSelection();
|
|
220
|
+
const node = selection?.anchorNode;
|
|
221
|
+
if (!selection?.isCollapsed || !(node instanceof Text) || !editorRef.current?.contains(node)) {
|
|
222
|
+
closeMention();
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const query = mentionQuery(node.data, selection.anchorOffset);
|
|
226
|
+
if (!query) {
|
|
227
|
+
closeMention();
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const matches = filterMentions(mentions, query.query, mentionLimit);
|
|
231
|
+
if (!matches.length) {
|
|
232
|
+
closeMention();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
mentionRange.current = { node, start: query.start, end: query.end };
|
|
236
|
+
setMentionMatches(matches);
|
|
237
|
+
setMentionActive(0);
|
|
238
|
+
setMentionPoint(caretPoint(node, query.end));
|
|
239
|
+
setMentionOpen(true);
|
|
240
|
+
}, [caretPoint, closeMention, disabled, mentionLimit, mentions]);
|
|
241
|
+
/**
|
|
242
|
+
* Wstawia wzmiankę w miejsce zapytania `@…`.
|
|
243
|
+
*
|
|
244
|
+
* Wzmianka jest elementem, nie samym tekstem: `data-mention` niesie identyfikator, który
|
|
245
|
+
* przeżyje zmianę nazwy, a `contenteditable="false"` trzyma ją w jednym kawałku — bez tego
|
|
246
|
+
* kursor wchodziłby w środek nazwiska i psuł zapis. Spacja po wzmiance daje gdzie postawić
|
|
247
|
+
* kursor: bez niej nie da się wyjść poza element, który nie jest edytowalny.
|
|
248
|
+
*/
|
|
249
|
+
const insertMention = useCallback((item) => {
|
|
250
|
+
const target = mentionRange.current;
|
|
251
|
+
if (!target)
|
|
252
|
+
return;
|
|
253
|
+
const range = document.createRange();
|
|
254
|
+
range.setStart(target.node, target.start);
|
|
255
|
+
range.setEnd(target.node, Math.min(target.end, target.node.length));
|
|
256
|
+
range.deleteContents();
|
|
257
|
+
const element = document.createElement('span');
|
|
258
|
+
element.className = 'kpt-mention';
|
|
259
|
+
element.dataset['mention'] = item.id;
|
|
260
|
+
element.setAttribute('contenteditable', 'false');
|
|
261
|
+
element.textContent = '@' + item.label;
|
|
262
|
+
const space = document.createTextNode(' ');
|
|
263
|
+
range.insertNode(space);
|
|
264
|
+
range.insertNode(element);
|
|
265
|
+
const after = document.createRange();
|
|
266
|
+
after.setStartAfter(space);
|
|
267
|
+
after.collapse(true);
|
|
268
|
+
const selection = window.getSelection();
|
|
269
|
+
selection?.removeAllRanges();
|
|
270
|
+
selection?.addRange(after);
|
|
271
|
+
closeMention();
|
|
272
|
+
pushValue();
|
|
273
|
+
onMention?.(item);
|
|
274
|
+
}, [closeMention, onMention, pushValue]);
|
|
178
275
|
// Stan narzędzi zależy od zaznaczenia, a to zmienia się także poza naszymi zdarzeniami
|
|
179
276
|
// (myszą, klawiaturą, przez inne okno), więc nasłuchujemy `selectionchange` na dokumencie.
|
|
180
277
|
useEffect(() => {
|
|
181
|
-
const onSelectionChange = () =>
|
|
278
|
+
const onSelectionChange = () => {
|
|
279
|
+
refreshState();
|
|
280
|
+
// Kursor może odjechać od `@` także bez pisania (klik, strzałki) — wtedy podpowiedzi
|
|
281
|
+
// muszą zniknąć, inaczej Enter wstawiłby wzmiankę w zupełnie innym miejscu.
|
|
282
|
+
syncMention();
|
|
283
|
+
};
|
|
182
284
|
document.addEventListener('selectionchange', onSelectionChange);
|
|
183
285
|
return () => document.removeEventListener('selectionchange', onSelectionChange);
|
|
184
|
-
}, [refreshState]);
|
|
286
|
+
}, [refreshState, syncMention]);
|
|
185
287
|
// Przeciąganie krawędzi kolumny i zaznaczanie komórek biegną na całym dokumencie: kursor
|
|
186
288
|
// wyjeżdża poza tabelę praktycznie zawsze.
|
|
187
289
|
useEffect(() => {
|
|
@@ -344,8 +446,43 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
344
446
|
* Tab: wcięcie w liście, przeskok komórki w tabeli. Poza nimi i na krawędziach zostawiamy Tab
|
|
345
447
|
* natywny — inaczej edytor byłby pułapką klawiaturową (WCAG 2.1.2).
|
|
346
448
|
*/
|
|
449
|
+
/** Klawiatura listy podpowiedzi; `true` = zdarzenie obsłużone i edytor go już nie zobaczy. */
|
|
450
|
+
const onMentionKeyDown = (event) => {
|
|
451
|
+
const count = mentionMatches.length;
|
|
452
|
+
if (!count)
|
|
453
|
+
return false;
|
|
454
|
+
switch (event.key) {
|
|
455
|
+
case 'ArrowDown':
|
|
456
|
+
setMentionActive((index) => (index + 1) % count);
|
|
457
|
+
break;
|
|
458
|
+
case 'ArrowUp':
|
|
459
|
+
setMentionActive((index) => (index - 1 + count) % count);
|
|
460
|
+
break;
|
|
461
|
+
case 'Enter':
|
|
462
|
+
case 'Tab': {
|
|
463
|
+
const item = mentionMatches[mentionActive];
|
|
464
|
+
if (!item)
|
|
465
|
+
return false;
|
|
466
|
+
insertMention(item);
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
case 'Escape':
|
|
470
|
+
closeMention();
|
|
471
|
+
break;
|
|
472
|
+
default:
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
event.preventDefault();
|
|
476
|
+
return true;
|
|
477
|
+
};
|
|
347
478
|
const onKeyDown = (event) => {
|
|
348
|
-
if (disabled
|
|
479
|
+
if (disabled)
|
|
480
|
+
return;
|
|
481
|
+
// Podpowiedzi wzmianek przechwytują klawiaturę jako pierwsze: Tab i Enter znaczą w nich
|
|
482
|
+
// co innego niż w samym edytorze.
|
|
483
|
+
if (mentionOpen && onMentionKeyDown(event))
|
|
484
|
+
return;
|
|
485
|
+
if (event.key !== 'Tab' || event.ctrlKey || event.altKey || event.metaKey)
|
|
349
486
|
return;
|
|
350
487
|
if (shiftItems(editorRef.current, event.shiftKey ? 'outdent' : 'indent', maxListLevel)) {
|
|
351
488
|
event.preventDefault();
|
|
@@ -570,7 +707,22 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
570
707
|
return (_jsx("button", { ref: setTableAnchor, type: "button", className: cn('kpt-rich-text__tool', tableOpen && 'is-active'), "aria-label": t('richText.table'), title: t('richText.table'), disabled: disabled, onMouseDown: (event) => event.preventDefault(), onClick: () => exec(item), children: _jsx(KptIcon, { name: ICON[item] }) }, item));
|
|
571
708
|
}
|
|
572
709
|
return toolButton(item);
|
|
573
|
-
})] }, index))) }), sourceView ? (_jsx("textarea", { className: "kpt-rich-text__source", value: current, disabled: disabled, spellCheck: false, "aria-label": t('richText.htmlSource'), onChange: (event) => setCurrent(event.target.value), onBlur: () => onTouch?.() })) : (_jsx("div", { ref: editorRef, className: "kpt-rich-text__editor", contentEditable: !disabled, suppressContentEditableWarning: true, "data-placeholder": placeholder, "aria-invalid": showInvalid || undefined, role: "textbox", "aria-multiline": "true", onInput:
|
|
710
|
+
})] }, index))) }), sourceView ? (_jsx("textarea", { className: "kpt-rich-text__source", value: current, disabled: disabled, spellCheck: false, "aria-label": t('richText.htmlSource'), onChange: (event) => setCurrent(event.target.value), onBlur: () => onTouch?.() })) : (_jsx("div", { ref: editorRef, className: "kpt-rich-text__editor", contentEditable: !disabled, suppressContentEditableWarning: true, "data-placeholder": placeholder, "aria-invalid": showInvalid || undefined, role: "textbox", "aria-multiline": "true", onInput: () => {
|
|
711
|
+
pushValue();
|
|
712
|
+
syncMention();
|
|
713
|
+
}, onKeyDown: onKeyDown, onPaste: onPaste, onContextMenu: onContextMenu, onMouseDown: onEditorMouseDown, onMouseMove: onEditorMouseMove, onBlur: () => onTouch?.() }))] }), mentionOpen && container
|
|
714
|
+
? createPortal(_jsx("ul", { className: "kpt-rt-mentions", role: "listbox", "aria-label": t('richText.mentions'), style: {
|
|
715
|
+
position: 'fixed',
|
|
716
|
+
left: mentionPoint.x,
|
|
717
|
+
top: mentionPoint.y,
|
|
718
|
+
zIndex: 'var(--kpt-z-overlay, 1100)',
|
|
719
|
+
}, children: mentionMatches.map((item, index) => (_jsxs("li", { className: "kpt-rt-mentions__item", role: "option", "aria-selected": index === mentionActive, "data-active": index === mentionActive ? '' : undefined, onMouseDown: (event) => {
|
|
720
|
+
// `mousedown` zamiast `click`: kliknięcie nie może zabrać fokusu edytorowi,
|
|
721
|
+
// bo razem z nim zniknąłby zakres, w który wstawiamy wzmiankę.
|
|
722
|
+
event.preventDefault();
|
|
723
|
+
insertMention(item);
|
|
724
|
+
}, onMouseEnter: () => setMentionActive(index), children: [_jsx("span", { className: "kpt-rt-mentions__label", children: item.label }), item.kind ? _jsx("span", { className: "kpt-rt-mentions__kind", children: item.kind }) : null] }, item.id))) }), container)
|
|
725
|
+
: null, _jsx(KptOverlay, { open: styleOpen, anchor: styleAnchor, onClose: () => setStyleOpen(false), offset: 4, children: _jsx("ul", { className: "kpt-rich-text__style-panel", role: "listbox", children: styleOptions.map((option) => (_jsx("li", { className: cn(`kpt-rich-text__style-opt kpt-rich-text__style-opt--${option.tag}`, currentBlock === option.tag && 'is-selected'), role: "option", "aria-selected": currentBlock === option.tag, onClick: () => {
|
|
574
726
|
setStyleOpen(false);
|
|
575
727
|
focusEditor();
|
|
576
728
|
restoreSelection();
|
|
@@ -13,7 +13,16 @@ const ALLOWED_STYLES = new Set([
|
|
|
13
13
|
'color', 'background-color', 'text-align', 'font-weight', 'font-style', 'text-decoration',
|
|
14
14
|
'width', 'height', 'border', 'border-color', 'border-width', 'border-style', 'vertical-align',
|
|
15
15
|
]);
|
|
16
|
-
|
|
16
|
+
/*
|
|
17
|
+
* `data-mention` i `contenteditable` są tu dla wzmianek: identyfikator musi przeżyć zapis treści,
|
|
18
|
+
* a `contenteditable="false"` trzyma wzmiankę jako jeden kawałek, w którego środku nie da się
|
|
19
|
+
* pisać. Oba atrybuty są bierne — nie wykonują niczego i nie wskazują zasobów, więc wpuszczenie
|
|
20
|
+
* ich nie otwiera nowej furtki.
|
|
21
|
+
*/
|
|
22
|
+
const ALLOWED_ATTRS = new Set([
|
|
23
|
+
'colspan', 'rowspan', 'span', 'width', 'class', 'target', 'rel', 'title',
|
|
24
|
+
'data-mention', 'contenteditable',
|
|
25
|
+
]);
|
|
17
26
|
// Schematy dopuszczone w `href` narzędzia „link"; blocklista byłaby obchodzalna.
|
|
18
27
|
const SAFE_URL_SCHEME = /^(https?:|mailto:|tel:)/i;
|
|
19
28
|
const HAS_URL_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
|
package/dist/roadmap/roadmap.js
CHANGED
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
3
3
|
import { buildRows, buildTimeline, linkGeometry, resolveLinks, resolveRange, sliceRows, } from '@konce-pt/roadmap';
|
|
4
4
|
import { cn } from "../internal/cn.js";
|
|
5
|
+
import { isDevBuild } from "../internal/dev.js";
|
|
5
6
|
import { useControllableState } from "../internal/use-controllable-state.js";
|
|
6
7
|
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
7
8
|
import { KptTooltip } from "../tooltip/tooltip.js";
|
|
@@ -262,9 +263,6 @@ export function KptRoadmap({ config, scrollHeight = null, labelWidth = '14rem',
|
|
|
262
263
|
}, tabIndex: 0, onClick: () => handleItemClick(bar.item), onKeyDown: (event) => handleItemKeydown(event, bar.item), children: [bar.progress !== null && (_jsx("span", { className: "kpt-roadmap__bar-progress", style: { inlineSize: `${bar.progress * 100}%` } })), _jsx("span", { className: "kpt-roadmap__bar-label", children: bar.label })] }) }, bar.item.id))) })] }, row.key));
|
|
263
264
|
})] }))] }) }));
|
|
264
265
|
}
|
|
265
|
-
function isDevBuild() {
|
|
266
|
-
return typeof process !== 'undefined' && process?.env?.['NODE_ENV'] !== 'production';
|
|
267
|
-
}
|
|
268
266
|
/** Identyfikatory grup zwiniętych deklaratywnie w danych. */
|
|
269
267
|
function declaredCollapsed(groups) {
|
|
270
268
|
return (groups ?? []).filter((group) => group.collapsed).map((group) => group.id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@konce-pt/react",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
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",
|
|
@@ -42,6 +42,10 @@
|
|
|
42
42
|
"types": "./dist/charts-entry/public-api.d.ts",
|
|
43
43
|
"default": "./dist/charts-entry/public-api.js"
|
|
44
44
|
},
|
|
45
|
+
"./chat": {
|
|
46
|
+
"types": "./dist/chat-entry/public-api.d.ts",
|
|
47
|
+
"default": "./dist/chat-entry/public-api.js"
|
|
48
|
+
},
|
|
45
49
|
"./roadmap": {
|
|
46
50
|
"types": "./dist/roadmap-entry/public-api.d.ts",
|
|
47
51
|
"default": "./dist/roadmap-entry/public-api.js"
|
|
@@ -61,14 +65,17 @@
|
|
|
61
65
|
"leaflet": "^1.9.0"
|
|
62
66
|
},
|
|
63
67
|
"dependencies": {
|
|
64
|
-
"@konce-pt/
|
|
65
|
-
"@konce-pt/
|
|
66
|
-
"@konce-pt/datetime": "0.8.
|
|
67
|
-
"@konce-pt/
|
|
68
|
-
"@konce-pt/
|
|
69
|
-
"@konce-pt/
|
|
70
|
-
"@konce-pt/
|
|
71
|
-
"@konce-pt/
|
|
68
|
+
"@konce-pt/chart": "0.8.5",
|
|
69
|
+
"@konce-pt/backdrop": "0.8.5",
|
|
70
|
+
"@konce-pt/datetime": "0.8.5",
|
|
71
|
+
"@konce-pt/mention": "0.8.5",
|
|
72
|
+
"@konce-pt/chat": "0.8.5",
|
|
73
|
+
"@konce-pt/icons": "0.8.5",
|
|
74
|
+
"@konce-pt/roadmap": "0.8.5",
|
|
75
|
+
"@konce-pt/table": "0.8.5",
|
|
76
|
+
"@konce-pt/map": "0.8.5",
|
|
77
|
+
"@konce-pt/tokens": "0.8.5",
|
|
78
|
+
"@konce-pt/i18n": "0.8.5"
|
|
72
79
|
},
|
|
73
80
|
"devDependencies": {
|
|
74
81
|
"@types/react": "^19.2.0",
|