@konce-pt/angular 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konce-pt/angular",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "Open-source Angular 22 component library on Signals & Signal Forms — 70+ components, rich data table, zoneless, fully tokenized. Selektory kpt-*, klasy Kpt*.",
5
5
  "license": "MIT",
6
6
  "author": "konce.pt",
@@ -46,13 +46,16 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "tslib": "^2.3.0",
49
- "@konce-pt/datetime": "0.8.3",
50
- "@konce-pt/icons": "0.8.3",
51
- "@konce-pt/i18n": "0.8.3",
52
- "@konce-pt/chart": "0.8.3",
53
- "@konce-pt/roadmap": "0.8.3",
54
- "@konce-pt/map": "0.8.3",
55
- "@konce-pt/table": "0.8.3"
49
+ "@konce-pt/chart": "0.8.5",
50
+ "@konce-pt/backdrop": "0.8.5",
51
+ "@konce-pt/datetime": "0.8.5",
52
+ "@konce-pt/chat": "0.8.5",
53
+ "@konce-pt/map": "0.8.5",
54
+ "@konce-pt/i18n": "0.8.5",
55
+ "@konce-pt/mention": "0.8.5",
56
+ "@konce-pt/table": "0.8.5",
57
+ "@konce-pt/roadmap": "0.8.5",
58
+ "@konce-pt/icons": "0.8.5"
56
59
  },
57
60
  "module": "fesm2022/konce-pt-angular.mjs",
58
61
  "typings": "types/konce-pt-angular.d.ts",
@@ -68,6 +71,10 @@
68
71
  "types": "./types/konce-pt-angular-charts.d.ts",
69
72
  "default": "./fesm2022/konce-pt-angular-charts.mjs"
70
73
  },
74
+ "./chat": {
75
+ "types": "./types/konce-pt-angular-chat.d.ts",
76
+ "default": "./fesm2022/konce-pt-angular-chat.mjs"
77
+ },
71
78
  "./grid": {
72
79
  "types": "./types/konce-pt-angular-grid.d.ts",
73
80
  "default": "./fesm2022/konce-pt-angular-grid.mjs"
@@ -0,0 +1,163 @@
1
+ # KptBackdrop (kpt-backdrop)
2
+
3
+ A decorative background layer for a section: the component wraps your content and paints an
4
+ animated canvas beneath it. You reach for it where a flat surface looks unfinished, and where
5
+ a video or a large image would cost bandwidth and refuse to follow the theme.
6
+ Projected content sits a layer above the canvas and behaves normally.
7
+ Import: `import { KptBackdrop } from '@konce-pt/angular';`
8
+
9
+ ## Inputs (shared)
10
+ - `effect`: 'signal-grid' | 'wave' (default 'signal-grid') — the effect to draw
11
+ - `background`: string — the section background; empty takes `--kpt-color-surface`
12
+ - `speed`: number (default 1) — 1 is the calm pace of either effect, 0 freezes the picture
13
+ - `seed`: number (default 0) — the same value gives the same layout and the same sway
14
+ - `paused`: boolean — stops the animation, leaving the last frame
15
+ - `adaptive`: boolean — carries hardcoded colours onto the current theme's surface
16
+
17
+ ## Inputs (signal-grid)
18
+ - `color`: string — the square colour, any CSS notation, tokens included; empty takes
19
+ `--kpt-color-primary`
20
+ - `anchor`: 'left' | 'right' (default 'right') — the edge the grid is densest at
21
+ - `cell`: number (default 5) — the square's side in px
22
+ - `gap`: number (default 11) — the space between squares in px
23
+ - `density`: number (default 0.8) — the share of lit cells at the anchor, 0–1
24
+ - `falloff`: number (default 1.4) — how fast the grid fades towards the far edge
25
+
26
+ ## Inputs (wave)
27
+ - `colors`: string[] — the colour ramp the current slides along, closed into a loop; every
28
+ stop takes any CSS notation, `color`'s rules exactly
29
+ - `rotation`: number (default 12) — the angle the current crosses the frame at, in degrees
30
+ - `band`: number (default 0.34) — the band's width as a fraction of the frame's height
31
+ - `amplitude`: number (default 0.16) — how far the centre line sways
32
+
33
+ An input that does not belong to the current effect is simply not read.
34
+
35
+ ## Methods
36
+ - `reseed()` — a fresh run of the effect at the same size and the same inputs
37
+
38
+ ## Example
39
+ <kpt-backdrop effect="wave" [rotation]="18" [colors]="['#5B8AF2', '#A86DF2', '#F25B9E']">
40
+ <h1>Build your app from the first prompt</h1>
41
+ <kpt-button>Get started</kpt-button>
42
+ </kpt-backdrop>
43
+
44
+ ## signal-grid
45
+ A fine grid of squares that quietly twinkle, dense at one edge and fading to empty at the other.
46
+ The gradient comes from **how many** squares are lit, not from opacity laid over an even grid —
47
+ that is why it reads as a signal rather than as a texture with a mask on top.
48
+
49
+ Each cell derives its own draws — whether it is lit, its twinkle phase and its rate — from its own
50
+ column and row, so the layout survives a resize instead of reshuffling. The rates are spread over
51
+ 0.45–1.55, because at one shared rate the whole grid pulses in unison and the effect turns into
52
+ breathing. The grid deliberately runs past the area and the overflow is clipped: a grid that stops
53
+ before the edge looks cut off.
54
+
55
+ ## wave
56
+ One soft current of light flowing across the frame, with hues sliding along it and no hard edge
57
+ anywhere. The centre line is a sum of harmonics at incommensurable frequencies, so the current
58
+ sways without a repeating pattern; brightness falls away from it on a Gaussian curve.
59
+
60
+ `rotation` turns the whole current: 0 runs it horizontally, 90 stands it on end. `band` sets how
61
+ wide it is, `amplitude` how much it sways — set `amplitude` to 0 and the shape stands still while
62
+ the colours keep flowing.
63
+
64
+ The ramp is closed into a loop, so the last colour returns to the first; a ramp with a seam would
65
+ show it in the middle of the frame. Colours are mixed on the polar form of OKLab, going round the
66
+ circle rather than across it: a straight mix of opposite hues crosses the achromatic axis and the
67
+ current goes grey exactly where it is brightest.
68
+
69
+ Without `colors` the ramp is `--kpt-color-primary` plus two hues derived from it by rotation.
70
+ The library's palette is neutral, so that default current is single-hued and muted — the hue
71
+ sliding only shows once you give it a real ramp.
72
+
73
+ Every stop is put to the browser, so `oklch()`, `color-mix()`, `rgb()` and named colours are read
74
+ the same as hex; alpha is dropped. A stop the browser refuses falls out of the ramp, and if every
75
+ one of them falls out the default ramp takes over rather than the section going blank — a dev
76
+ build says in the console which stops it dropped.
77
+
78
+ ## What it costs
79
+ `wave` is computed in a buffer whose longer side is 160 px and scaled up: softness comes from the
80
+ browser's interpolation, so a few thousand pixels a frame cover a frame of any size. It is not
81
+ drawn at full canvas resolution.
82
+
83
+ ## Where the maths lives
84
+ The grid layout, the density ramp, the twinkle, the current and the colour ramp — plus the
85
+ renderers for both effects — live in `@konce-pt/backdrop`, plain TypeScript with its own
86
+ `node --test` suite, shared by both ports. The component is the lifecycle around them.
87
+
88
+ ## Rest
89
+ Drawing stops when there is nothing to watch: the section is off-screen (`IntersectionObserver`),
90
+ the tab is in the background, `paused` is set, `speed` is 0, or the system asks for reduced motion
91
+ (`prefers-reduced-motion`). What is left is a single static frame, not emptiness — the effect is
92
+ part of the composition, not decoration.
93
+
94
+ `speed` feeds the clock rather than the phase, so moving a speed slider accelerates the picture
95
+ instead of jumping it.
96
+
97
+ ## Accessibility
98
+ The canvas carries `aria-hidden="true"` and `pointer-events: none` — it is decoration with no
99
+ content and nothing to hit. Content keeps its own roles and tab order. Under reduced motion the
100
+ animation does not start at all.
101
+
102
+ ## Tokens
103
+ `--kpt-color-primary` (squares and the default ramp), `--kpt-color-surface` (background),
104
+ `--kpt-color-on-surface`. Overrides for a whole section without touching the inputs:
105
+ `--kpt-backdrop-color`, `--kpt-backdrop-surface`.
106
+
107
+ ## Adaptive colours
108
+ A literal stays put by design, which is right for a brand colour and wrong for a section meant to
109
+ read in both themes. `adaptive` closes that gap: it carries `color`, `colors` and `background`
110
+ onto the surface of the current theme, keeping each one's distance from that surface. Black picked
111
+ for a white section comes out white on a near-black ground, and that white background comes out at
112
+ the surface's own lightness. Only lightness moves — hue and chroma stay, so a brand colour keeps
113
+ being that colour and only changes how light it is.
114
+
115
+ It touches literals only. An empty input takes a token, and a value reaching through `var()`
116
+ reaches a token too; both already follow the theme, and adapting them would turn them back again.
117
+ Values written for the light theme are the assumption — the library keeps light in `:root` and
118
+ dark as the override.
119
+
120
+ ## Content on a custom background
121
+ The host paints `--kpt-color-surface` and lets content inherit `--kpt-color-on-surface`, which is
122
+ right until the section gets a `background` of its own — then that token describes the page around
123
+ it, not the ground the content actually sits on. A dark section in a light theme would come out
124
+ black on black.
125
+
126
+ So a section with its own background works out its own ink: the host sets `color` to match what it
127
+ paints, and announces the result as `data-tone` (`light` for a light background, `dark` for a dark
128
+ one). Text follows by inheritance; anything needing more than a text colour — buttons in the
129
+ section, say — can hang its own tokens off the attribute:
130
+
131
+ :where(kpt-backdrop[data-tone='light']) .hero { --kpt-button-filled-bg: #15181a; }
132
+
133
+ The tone is computed from the background the section really paints, so with `adaptive` on it
134
+ matches the adapted value, not the one written in the template. A section without its own
135
+ background announces nothing and content stays on `--kpt-color-on-surface`.
136
+
137
+ ## Theme
138
+ Canvas does not expand `var()`, so the colour is read once and baked into the renderer — the
139
+ component therefore watches for anything that can restyle the tokens under it and reads them
140
+ again. `[data-theme="dark"]` is not tied to `:root`, so the watch covers `data-theme`, `class`
141
+ and `style` on every ancestor from the host up to `<html>`, plus the `prefers-color-scheme` query
142
+ for an application that keeps its dark tokens in a media query. A runtime override of
143
+ `--kpt-color-primary` is picked up the same way as a theme switch. The renderer is rebuilt only
144
+ when the colour actually came out different, so an unrelated class landing on `<html>` costs one
145
+ cheap read and nothing else.
146
+
147
+ Whether an explicit `color` or `colors` follows the theme is up to how it is written. A literal
148
+ stays put — `#5b8af2` is `#5b8af2` in either theme, which is what someone spelling out a brand
149
+ colour wants. An expression reaching for a token is resolved against the tree on every rebuild, so
150
+ it moves with the theme on its own:
151
+
152
+ <kpt-backdrop color="color-mix(in oklab, #5b8af2, var(--kpt-color-surface) 30%)">
153
+
154
+ `var(--kpt-color-primary)` works the same way. A name that resolves to nothing falls back to the
155
+ token for `color`, and drops out of the ramp for `colors`.
156
+
157
+ ## Contrast
158
+ The palette is neutral, so `--kpt-color-primary` is near-black on a light surface and near-white
159
+ on a dark one — at the default density the picture is strong, and text laid over the dense half
160
+ loses its reading. Keep the copy on the side the grid fades towards (that is what `anchor` is
161
+ for), or drop `density`. `wave` is brightest along its centre line, so the same rule applies
162
+ through `rotation` and `band`. On a custom `background` give the colours a muted shade:
163
+ a full-strength colour behind text costs the reading, not the effect.
@@ -4,11 +4,19 @@ A chip / tag — a label, optionally removable. Import: `import { KptChip } from
4
4
 
5
5
  ## Inputs / outputs
6
6
  - `variant`: 'neutral'|'primary'|'success'|'danger'|'warning'|'info'
7
+ - `textTransform`: 'none'|'capitalize'|'uppercase'|'lowercase' (default 'none') — the label's letter case
7
8
  - `removable`: boolean (a × button); `disabled`: boolean; `removed`: output<void>
8
9
 
9
10
  ## Examples
10
11
  <kpt-chip>Angular</kpt-chip>
11
12
  <kpt-chip variant="success" removable (removed)="onRemove()">UI</kpt-chip>
13
+ <kpt-chip textTransform="capitalize">new · starter kit</kpt-chip>
14
+
15
+ ## Letter case
16
+ `textTransform` sets `text-transform` on the chip, so the label can stay in the application's own
17
+ wording while the chip decides how it is displayed. `capitalize` raises the first letter of every
18
+ word — **including after a hyphen** ("Agent-Ready"), because CSS does not know where a compound
19
+ word ends. For labels with a hyphen, spelling it out in the content is the exact route.
12
20
 
13
21
  ## Tokens
14
22
  A color-mix background over the variant, `--kpt-color-muted` (neutral).
@@ -7,12 +7,28 @@ Import: `import { KptDataView } from '@konce-pt/angular';`
7
7
  ## Inputs / model
8
8
  - `items`: T[]
9
9
  - `layout`: model<'list' | 'grid'> (the toggle in the bar)
10
- - `pageSize`: number (0 = no pagination); `paginated`: boolean
10
+ - `pageSize`: model<number> (0 = no pagination) — two-way, because the paginator's select writes to it
11
+ - `pageSizeOptions`: number[] (empty = the select offers the current size only)
12
+ - `gridColumns`: model<number> (0 = as many as fit) — two-way, the density bar writes to it
13
+ - `gridColumnsOptions`: number[] (fewer than two = no density bar)
14
+ - `gridMinWidth`: string (default `10rem`) — the narrowest card worth rendering
15
+ - `paginated`: boolean
11
16
 
12
17
  ## Example
13
- <kpt-data-view [items]="products" [pageSize]="6">
18
+ <kpt-data-view [items]="products" [(pageSize)]="size" [pageSizeOptions]="[3, 6, 9]">
14
19
  <ng-template let-p><div>{{ p.name }}</div></ng-template>
15
20
  </kpt-data-view>
16
21
 
22
+ ## The column count is a ceiling
23
+ `gridColumns` caps the grid, it does not pin it: the track minimum grows to the width of one of
24
+ N columns, so a narrow container drops to two or one column instead of squeezing cards below
25
+ `gridMinWidth`. Set `gridColumns` to one of `gridColumnsOptions` — with a value outside the list
26
+ no density button reads as active.
27
+
28
+ ## The pager stays while there is a choice
29
+ It shows up when the items outgrow one page, and also when `pageSizeOptions` holds more than one
30
+ size. Otherwise picking the largest size would hide the very select that was just used, with no
31
+ way back to a smaller one.
32
+
17
33
  ## Tokens
18
34
  Item cards `--kpt-color-surface-raised`; the toggle `--kpt-color-primary`.
@@ -7,6 +7,7 @@ Import: `import { KptIconButton } from '@konce-pt/angular';`
7
7
  - `variant`: 'filled'|'tonal'|'outline'|'text'|'danger'; `size`: 'sm'|'md'|'lg'; `shape`: 'round'|'square'
8
8
  - `type`, `disabled`, `loading`, `ariaLabel` (required)
9
9
  - `ariaPressed`: `boolean | null` = `null` — toggle state; lands on the inner `<button>`, so a screen reader announces it (`[attr.aria-pressed]` on the tag would sit on the host, which has no button role)
10
+ - `ariaExpanded`: `boolean | null` = `null` — disclosure state for a button that opens a panel; lands on the inner `<button>` for the same reason
10
11
 
11
12
  ## Examples
12
13
  <kpt-icon-button ariaLabel="Settings"><kpt-icon name="menu" /></kpt-icon-button>
@@ -0,0 +1,60 @@
1
+ # KptMention (kpt-mention) / KptMentions (kpt-mentions)
2
+
3
+ Mentions in text: `@someone` as an element you can hover to see a peek card with the rest of the
4
+ connections. `kpt-mention` is a single mention; `kpt-mentions` turns a whole paragraph into text
5
+ plus mentions.
6
+ Requires `import '@angular/cdk/overlay-prebuilt.css';` (the card runs on `kpt-peek`)
7
+ Import: `import { KptMention, KptMentions } from '@konce-pt/angular';`
8
+
9
+ ## KptMention — inputs
10
+ - `item`: `KptMentionItem` — the whole entry at once; the single inputs below win over it
11
+ - `id`, `label`, `kind`: string — `kind` lands in `data-kind`, so it can be styled per type
12
+ - `prefix`: string (default '@') — an empty string leaves the bare name
13
+ - `href`: string | null — with it the mention is a link, without it a button opening the card
14
+ - `placement`, `openDelay` — passed to `kpt-peek`
15
+ - `disabled`: boolean — no card; the mention stays a plain highlighted word
16
+ - projected content = the card
17
+
18
+ ## KptMentions — inputs
19
+ - `text`: string — the content with `@id` or `@[Name with spaces](id)`
20
+ - `items`: `readonly KptMentionItem[]` — entries the ids resolve against
21
+ - `prefix`, `placement` — passed to every mention
22
+ - an `<ng-template>` inside = the card; its context is the item as `$implicit` plus `id` and `label`
23
+
24
+ ## Example
25
+ <kpt-mentions [text]="note" [items]="people">
26
+ <ng-template let-item let-label="label">
27
+ <kpt-card>{{ item?.data?.role ?? label }}</kpt-card>
28
+ </ng-template>
29
+ </kpt-mentions>
30
+
31
+ ## The text stays text
32
+ `kpt-mentions` does not render HTML: the paragraph goes in as a plain string, `parseMentionText()`
33
+ from `@konce-pt/mention` splits it into segments and the mention is an element the library builds
34
+ itself. An application does not have to push user-written content through a sanitizer to get
35
+ mentions out of it.
36
+
37
+ An id that is not on the list still renders — the label falls back to the id, because a note has to
38
+ stay readable after someone leaves the team. Without a card template the mention is a highlighted
39
+ word, not a button: there is nothing to open, so it does not pretend otherwise.
40
+
41
+ ## The id is what is stored
42
+ `id` survives a rename, the label does not — that is why the text carries the id and the name comes
43
+ from `items`. `@[Anna Kowalska](anna.k)` is the only way to write a name with spaces, and even then
44
+ the list wins over the label written into the text: data is usually fresher than a note from last
45
+ month.
46
+
47
+ ## Mentions in the editor
48
+ `kpt-rich-text` has `mentions`, `mentionLimit` and `(mentioned)`: typing `@` opens the suggestion
49
+ list, arrows and Enter (or Tab) insert the mention as
50
+ `<span class="kpt-mention" data-mention="id" contenteditable="false">`. The id survives saving,
51
+ and `contenteditable="false"` keeps the mention in one piece — without it the caret would walk into
52
+ the middle of a surname.
53
+
54
+ ## Tokens
55
+ The mention is underlined with a **dotted** line: `--kpt-color-primary-border` at rest, darkening
56
+ to `--kpt-color-primary` on hover and while the card is open. Dotted, not solid — a solid underline
57
+ would read as a link, and a mention usually leads nowhere, it opens a card. Text
58
+ `--kpt-color-primary`; a mention without a card `--kpt-color-text` + `--kpt-font-weight-medium` and
59
+ no line at all, since there is nothing to promise. Focus ring `--kpt-color-focus-ring`. The card
60
+ takes its own tokens from `kpt-peek`.
@@ -1,17 +1,28 @@
1
1
  # KptOrderList (kpt-order-list)
2
2
 
3
- A list with manual reordering (up/down). Select an item and move it with the buttons.
3
+ A list with manual reordering (up/down). Select an item and move it with the buttons, or turn on
4
+ `dragdrop` and drag it into place.
4
5
  The item template comes through projection: `<ng-template let-item>`.
5
6
  Import: `import { KptOrderList } from '@konce-pt/angular';`
6
7
 
7
8
  ## Inputs / model
8
9
  - `items`: model<T[]> (two-way — returns the new order)
9
10
  - `header`: string
11
+ - `dragdrop`: boolean, default false — enables dragging (opt-in, nothing changes without it)
10
12
 
11
13
  ## Example
12
- <kpt-order-list [(items)]="tasks" header="Order">
14
+ <kpt-order-list [(items)]="tasks" header="Order" dragdrop>
13
15
  <ng-template let-item>{{ item.name }}</ng-template>
14
16
  </kpt-order-list>
15
17
 
18
+ ## Where a dragged item lands
19
+ Dropping onto a row puts the dragged item at that row's position and everything else shifts, while
20
+ the buttons keep swapping with the neighbour. Dropping on the empty part of the list appends at the
21
+ end. Either way the selection follows the item, so the next button press moves the same entry.
22
+
16
23
  ## Tokens
17
24
  Selection `--kpt-color-primary`; buttons `--kpt-color-surface-raised`.
25
+
26
+ ## Accessibility
27
+ Native HTML5 drag and drop has no keyboard equivalent, so the up/down buttons stay the full keyboard
28
+ path — `dragdrop` adds to them, it never replaces them.
@@ -0,0 +1,45 @@
1
+ # KptPeek (kpt-peek)
2
+
3
+ A peek card — content tied to what you are looking at, shown without leaving the text. Hover opens
4
+ it, the card itself is interactive (links, buttons), and a click pins it.
5
+ Requires `import '@angular/cdk/overlay-prebuilt.css';`
6
+ Import: `import { KptPeek, KptPeekTrigger } from '@konce-pt/angular';`
7
+
8
+ ## Slots
9
+ - the element carrying the `kptPeekTrigger` attribute = the trigger; everything else = the card
10
+
11
+ ## Inputs / outputs
12
+ - `placement`: 'top' | 'bottom' (default 'bottom') — CDK flips it when there is no room
13
+ - `openDelay`: number (default 300) — the wait before hover opens the card
14
+ - `closeDelay`: number (default 200) — the wait before leaving the trigger closes it
15
+ - `disabled`: boolean — the card never opens; the trigger stays a plain link or button
16
+ - `label`: string — the card's accessible name; without it the trigger's text is used
17
+ - `(opened)`, `(closed)` → `void` — the moment to fetch the card's data
18
+
19
+ ## Example
20
+ <kpt-peek>
21
+ <a kptPeekTrigger href="/people/anna">@Anna Kowalska</a>
22
+ <kpt-card>Profile, projects, contact…</kpt-card>
23
+ </kpt-peek>
24
+
25
+ ## Which of the three
26
+ `kptTooltip` carries plain text and cannot be clicked (`pointer-events: none`), so a link inside it
27
+ is unreachable. `kpt-popover` opens on click and puts a backdrop over the page, so it is a small
28
+ modal. `kpt-peek` sits between them: it opens on hover, the card is interactive, and nothing is
29
+ covered.
30
+
31
+ ## Hover is not the only way in
32
+ Hover opens after `openDelay` and leaving closes after `closeDelay` — the pointer needs time to
33
+ travel from the trigger to the card, so entering the card cancels the close. Focus opens it with no
34
+ delay (a keyboard does not sweep across elements by accident). A click, Enter or a tap **pins** the
35
+ card: it stays after the pointer leaves and focus moves inside it. That is the only route to the
36
+ card's content where there is no hover at all, so interactive content stays reachable on touch and
37
+ from the keyboard. Escape closes it and hands focus back to the trigger; so does a click outside.
38
+
39
+ ## Accessibility
40
+ The trigger keeps its own role (a link stays a link) and carries `aria-expanded` plus
41
+ `aria-controls` while the card is open. The card is `role="dialog"` with a name from `label` or
42
+ the trigger's text. It is not a focus trap — the card is not modal and the page stays usable.
43
+
44
+ ## Tokens
45
+ `--kpt-color-surface-raised`, `--kpt-color-border`, `--kpt-elevation-3`, `--kpt-color-focus-ring`.
@@ -1,17 +1,33 @@
1
1
  # KptPickList (kpt-pick-list)
2
2
 
3
- Transfers items between two lists (source and target). Select and move them (one at a time or all).
3
+ Transfers items between two lists (source and target). Select and move them (one at a time or all),
4
+ double-click a row to send it across, or turn on `dragdrop` and drag it.
4
5
  The item template comes through projection: `<ng-template let-item>`.
5
6
  Import: `import { KptPickList } from '@konce-pt/angular';`
6
7
 
7
8
  ## Inputs / model
8
9
  - `source`: model<T[]>; `target`: model<T[]> (both two-way)
9
10
  - `sourceHeader`/`targetHeader`: string
11
+ - `dragdrop`: boolean, default false — enables dragging (opt-in, nothing changes without it)
10
12
 
11
13
  ## Example
12
- <kpt-pick-list [(source)]="available" [(target)]="selected" sourceHeader="Available" targetHeader="Selected">
14
+ <kpt-pick-list [(source)]="available" [(target)]="selected" sourceHeader="Available" targetHeader="Selected" dragdrop>
13
15
  <ng-template let-item>{{ item.name }}</ng-template>
14
16
  </kpt-pick-list>
15
17
 
18
+ ## A double-click moves one item
19
+ It sends exactly the row you clicked to the other panel and appends it at the end, whatever else is
20
+ selected. It does not need `dragdrop` — the two clicks underneath cancel each other out, so the rest
21
+ of the selection is left alone.
22
+
23
+ ## Where a dragged item lands
24
+ Dropping onto a row puts the dragged item at that row's position: inside one panel that reorders,
25
+ across panels it transfers. Dropping on the empty part of a panel appends at the end. An item that
26
+ leaves a panel also leaves that panel's selection.
27
+
16
28
  ## Tokens
17
29
  Selection `--kpt-color-primary`; headers `--kpt-color-surface-variant`.
30
+
31
+ ## Accessibility
32
+ Native HTML5 drag and drop has no keyboard equivalent, so the four move buttons stay the full
33
+ keyboard path — `dragdrop` adds to them, it never replaces them.
@@ -12,6 +12,21 @@ Import: `import { KptRichText, KptRichTool, KptRichToolbar } from '@konce-pt/ang
12
12
  - `placeholder`: string; `disabled`/`invalid`/`touched`: boolean; `touch`: output
13
13
  - `maxListLevel`: number = 6 — the maximum list nesting depth. The limit applies to interaction
14
14
  (Tab and the `indent`/`outdent` tools); pasted content is not flattened, so that no data is lost.
15
+ - `mentions`: `readonly KptMentionItem[]` — suggestions for `@`; an empty list turns the mechanism
16
+ off, so `@` stays an ordinary character. `mentionLimit`: number = 8; `(mentioned)` fires with the
17
+ inserted entry — the moment to notify a person or attach a link.
18
+
19
+ ## Mentions (`@`)
20
+ With `mentions` set, typing `@` opens a suggestion list anchored at the caret: arrows move through
21
+ it, Enter or Tab inserts, Escape closes. The query is read from the **text node** at the caret, not
22
+ from the HTML, and recognised by `mentionQuery()` from `@konce-pt/mention` — the same rules as in a
23
+ plain text field.
24
+
25
+ An inserted mention is `<span class="kpt-mention" data-mention="id" contenteditable="false">@Name`
26
+ plus a non-breaking space. The id survives a rename and the round trip through the value;
27
+ `contenteditable="false"` keeps the mention in one piece, and the space is what gives the caret a
28
+ place to stand after a block it cannot enter. Both attributes are on the sanitizer's allowlist —
29
+ they are inert, so they open no new door.
15
30
 
16
31
  ## Tools (KptRichTool)
17
32
  - Clipboard: `copy`, `paste` (with formatting, sanitized), `pasteText` (without formatting)