@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.
Files changed (53) hide show
  1. package/README.md +4 -4
  2. package/dist/backdrop/backdrop.d.ts +79 -0
  3. package/dist/backdrop/backdrop.js +195 -0
  4. package/dist/backdrop/llms.txt +132 -0
  5. package/dist/chat/chat.d.ts +53 -0
  6. package/dist/chat/chat.js +160 -0
  7. package/dist/chat/llms.txt +78 -0
  8. package/dist/chat-composer/chat-composer.d.ts +41 -0
  9. package/dist/chat-composer/chat-composer.js +81 -0
  10. package/dist/chat-composer/llms.txt +56 -0
  11. package/dist/chat-dock/chat-dock.d.ts +40 -0
  12. package/dist/chat-dock/chat-dock.js +67 -0
  13. package/dist/chat-dock/llms.txt +71 -0
  14. package/dist/chat-entry/public-api.d.ts +12 -0
  15. package/dist/chat-entry/public-api.js +10 -0
  16. package/dist/chat-message/chat-message.d.ts +53 -0
  17. package/dist/chat-message/chat-message.js +112 -0
  18. package/dist/chat-message/llms.txt +73 -0
  19. package/dist/chat-typing/chat-typing.d.ts +23 -0
  20. package/dist/chat-typing/chat-typing.js +30 -0
  21. package/dist/chat-typing/llms.txt +41 -0
  22. package/dist/chip/chip.d.ts +10 -1
  23. package/dist/chip/chip.js +3 -2
  24. package/dist/chip/llms.txt +9 -0
  25. package/dist/data-view/data-view.d.ts +15 -3
  26. package/dist/data-view/data-view.js +22 -5
  27. package/dist/data-view/llms.txt +20 -7
  28. package/dist/internal/dev.d.ts +1 -0
  29. package/dist/internal/dev.js +3 -0
  30. package/dist/internal/overlay.d.ts +6 -1
  31. package/dist/internal/overlay.js +5 -3
  32. package/dist/mention/llms.txt +60 -0
  33. package/dist/mention/mention.d.ts +38 -0
  34. package/dist/mention/mention.js +29 -0
  35. package/dist/mention/mentions.d.ts +31 -0
  36. package/dist/mention/mentions.js +24 -0
  37. package/dist/order-list/llms.txt +16 -5
  38. package/dist/order-list/order-list.d.ts +5 -3
  39. package/dist/order-list/order-list.js +69 -4
  40. package/dist/peek/llms.txt +42 -0
  41. package/dist/peek/peek.d.ts +35 -0
  42. package/dist/peek/peek.js +127 -0
  43. package/dist/pick-list/llms.txt +19 -5
  44. package/dist/pick-list/pick-list.d.ts +7 -2
  45. package/dist/pick-list/pick-list.js +123 -17
  46. package/dist/public-api.d.ts +13 -1
  47. package/dist/public-api.js +6 -0
  48. package/dist/rich-text/llms.txt +10 -0
  49. package/dist/rich-text/rich-text.d.ts +11 -1
  50. package/dist/rich-text/rich-text.js +157 -5
  51. package/dist/rich-text/sanitize.js +10 -1
  52. package/dist/roadmap/roadmap.js +1 -3
  53. package/package.json +16 -9
@@ -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';
@@ -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";
@@ -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 = () => refreshState();
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 || event.key !== 'Tab' || event.ctrlKey || event.altKey || event.metaKey)
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: pushValue, onKeyDown: onKeyDown, onPaste: onPaste, onContextMenu: onContextMenu, onMouseDown: onEditorMouseDown, onMouseMove: onEditorMouseMove, onBlur: () => onTouch?.() }))] }), _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: () => {
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
- const ALLOWED_ATTRS = new Set(['colspan', 'rowspan', 'span', 'width', 'class', 'target', 'rel', 'title']);
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;
@@ -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",
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/i18n": "0.8.3",
65
- "@konce-pt/chart": "0.8.3",
66
- "@konce-pt/datetime": "0.8.3",
67
- "@konce-pt/roadmap": "0.8.3",
68
- "@konce-pt/tokens": "0.8.3",
69
- "@konce-pt/table": "0.8.3",
70
- "@konce-pt/map": "0.8.3",
71
- "@konce-pt/icons": "0.8.3"
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",