@weasel-js/text-dom 1.9.4 → 1.10.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/README.md +5 -0
- package/dist/index.d.ts +44 -74
- package/dist/index.js +424 -191
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -22,6 +22,11 @@ While an edit is open the hook reports the selection's shared style
|
|
|
22
22
|
(`MIXED` where the selected runs disagree) and applies a style or a flag such
|
|
23
23
|
as bold to the selected range.
|
|
24
24
|
|
|
25
|
+
A paste or a drop goes in as runs, never as the clipboard's markup: its blocks
|
|
26
|
+
become lines, its table cells are set apart by tabs, and it keeps its bold,
|
|
27
|
+
italic, underline, strikethrough, overline, and super- and subscript. Face,
|
|
28
|
+
size, and color come from the text it lands in.
|
|
29
|
+
|
|
25
30
|
`runsToDom` and `domToRuns` are the conversion between runs and the overlay's
|
|
26
31
|
DOM, and `charOffsetToDomPosition` / `domPositionToCharOffset` map a character
|
|
27
32
|
offset in the text to a DOM position and back.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
import { FlagKey, StyledRun, TextStyle, TextVerticalAlign, TextPaint, RangeStyle, RunStylePatch, ResolvedTextStyle } from '@weasel-js/text';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* In-place text editing via a contenteditable overlay positioned over the
|
|
5
|
-
* text node's screen-space pose. Enter / blur commit; Shift+Enter inserts a
|
|
6
|
-
* newline; Escape cancels. The hook owns the DOM lifecycle of the overlay
|
|
7
|
-
* element — caller just provides a container, the lookup hooks, and a
|
|
8
|
-
* `setText` callback (which is responsible for op-batching/undo).
|
|
9
|
-
*
|
|
10
|
-
* The text layer should hide the node it's currently editing
|
|
11
|
-
* (`isHidden: (n) => n.id === editingId`) so the overlay isn't drawn twice.
|
|
12
|
-
*/
|
|
3
|
+
/** The option, return, and pose types of `useTextEdit`. */
|
|
13
4
|
|
|
14
5
|
/**
|
|
15
6
|
* A styling a shortcut or a control toggles. The additive booleans, plus the
|
|
@@ -56,18 +47,19 @@ interface TextEditScreenPose {
|
|
|
56
47
|
* transform does the world→screen conversion.
|
|
57
48
|
*
|
|
58
49
|
* This is the only way run-level typography can be correct at a zoom other
|
|
59
|
-
* than 1.
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* scales node-level and run-level values by the same factor for free.
|
|
64
|
-
*
|
|
65
|
-
* Omit it (the default, `1`) and the pose is plain screen pixels — the hook
|
|
66
|
-
* then infers the world→screen factor from `fontSize / style.fontSize` to
|
|
67
|
-
* scale node-level `letterSpacing`, and run-level overrides are left
|
|
68
|
-
* unscaled. Correct only at zoom 1.
|
|
50
|
+
* than 1. Omit it (the default, `1`) and the pose is plain screen pixels —
|
|
51
|
+
* the hook then infers the world→screen factor from
|
|
52
|
+
* `fontSize / style.fontSize` to scale node-level `letterSpacing`, and
|
|
53
|
+
* run-level overrides are left unscaled.
|
|
69
54
|
*/
|
|
70
55
|
zoom?: number;
|
|
56
|
+
/**
|
|
57
|
+
* The frame the text is drawn in, when its box is turned or skewed: the
|
|
58
|
+
* linear part `[a, b, c, d]` (`x' = a·x + c·y`, `y' = b·x + d·y`) applied
|
|
59
|
+
* about the box's center. `x`, `y`, `width`, and `height` stay those of
|
|
60
|
+
* the box before it. Absent is the identity.
|
|
61
|
+
*/
|
|
62
|
+
frame?: readonly [a: number, b: number, c: number, d: number];
|
|
71
63
|
}
|
|
72
64
|
/** A box in CSS pixels relative to `useTextEdit`'s `container`. */
|
|
73
65
|
interface TextEditClipRect {
|
|
@@ -144,8 +136,7 @@ interface UseTextEditOptions {
|
|
|
144
136
|
* commit calls both `setText` (with `runsToPlainText(runs)`) and
|
|
145
137
|
* `setRuns(id, runs)` — but only when runs are actually in play: the node
|
|
146
138
|
* already had some, or the edit produced styling. A plain-text edit of a
|
|
147
|
-
* plain-text node still calls `setText` alone
|
|
148
|
-
* been styled doesn't grow a single-run `runs` array just for being edited.
|
|
139
|
+
* plain-text node still calls `setText` alone.
|
|
149
140
|
*/
|
|
150
141
|
setRuns?: (id: string, runs: StyledRun[]) => void;
|
|
151
142
|
/**
|
|
@@ -153,8 +144,7 @@ interface UseTextEditOptions {
|
|
|
153
144
|
* color popover, anything whose whole purpose is to style the text being
|
|
154
145
|
* edited? Focus moving into one does not end the edit.
|
|
155
146
|
*
|
|
156
|
-
* Without this,
|
|
157
|
-
* destroys it: clicking a size field blurs the overlay, blur commits, and
|
|
147
|
+
* Without this, clicking a size field blurs the overlay, blur commits, and
|
|
158
148
|
* the caret the control was about to act on is gone. Toggle buttons can
|
|
159
149
|
* dodge it by `preventDefault()`-ing their own mousedown, but a field the
|
|
160
150
|
* user has to type into cannot.
|
|
@@ -172,9 +162,7 @@ interface UseTextEditOptions {
|
|
|
172
162
|
*
|
|
173
163
|
* The overlay stands in for canvas text the host has hidden, so the wait
|
|
174
164
|
* shows nothing at all; past it, the fallback shows and reflows to the real
|
|
175
|
-
* face when that lands. A face already loaded
|
|
176
|
-
* `@weasel-js/font` builds it as soon as its bytes arrive — shows at once.
|
|
177
|
-
* `0` never waits.
|
|
165
|
+
* face when that lands. A face already loaded shows at once. `0` never waits.
|
|
178
166
|
*/
|
|
179
167
|
fontHold?: number;
|
|
180
168
|
}
|
|
@@ -186,10 +174,8 @@ interface UseTextEditOptions {
|
|
|
186
174
|
declare const TEXT_EDIT_FONT_HOLD = 100;
|
|
187
175
|
/** Options for `useTextEdit().startEdit`. */
|
|
188
176
|
interface StartEditOptions {
|
|
189
|
-
/**
|
|
190
|
-
*
|
|
191
|
-
* `'all'` selects the whole text (default — preserves the prior behavior).
|
|
192
|
-
*/
|
|
177
|
+
/** Where to place the caret on edit start. Number = caret offset (0..text.length);
|
|
178
|
+
* `'all'` selects the whole text (default). */
|
|
193
179
|
caret?: number | 'all';
|
|
194
180
|
}
|
|
195
181
|
/** Return shape of `useTextEdit`. */
|
|
@@ -215,7 +201,7 @@ interface UseTextEditReturn {
|
|
|
215
201
|
* n" stay distinguishable — a styling written at a collapsed caret arms
|
|
216
202
|
* {@link UseTextEditReturn.pendingStyle} instead of restyling a range.
|
|
217
203
|
*
|
|
218
|
-
* Follows the DOM selection, which browsers
|
|
204
|
+
* Follows the DOM selection, which browsers report from a task
|
|
219
205
|
* rather than synchronously; anything this hook writes itself updates it
|
|
220
206
|
* synchronously. A DOM selection that leaves the overlay does **not** clear
|
|
221
207
|
* it: that is what happens when the user clicks a styling control, and
|
|
@@ -240,10 +226,15 @@ interface UseTextEditReturn {
|
|
|
240
226
|
* Styling armed at a collapsed caret, applied to the next character typed
|
|
241
227
|
* and then dropped. `{}` when nothing is armed — which is always the case
|
|
242
228
|
* while `selection` covers a real range, since a range is styled directly.
|
|
243
|
-
*
|
|
244
|
-
* Moving the caret abandons it, as in any other editor.
|
|
229
|
+
* Moving the caret abandons it.
|
|
245
230
|
*/
|
|
246
231
|
pendingStyle: RunStylePatch;
|
|
232
|
+
/**
|
|
233
|
+
* What a styling control shows during an edit: the node's own style and
|
|
234
|
+
* paint, `rangeStyle` over it, and `pendingStyle` last — what the user
|
|
235
|
+
* will see themselves type. `null` when nothing is being edited.
|
|
236
|
+
*/
|
|
237
|
+
effectiveStyle: RangeStyle | null;
|
|
247
238
|
/**
|
|
248
239
|
* Write `patch` over `selection`. A no-op with no active edit or an empty
|
|
249
240
|
* patch. Over a real range this restyles the runs under it; at a collapsed
|
|
@@ -259,34 +250,20 @@ interface UseTextEditReturn {
|
|
|
259
250
|
* re-selecting, and `rangeStyle` reflects the write before this returns.
|
|
260
251
|
*/
|
|
261
252
|
applyStyleToSelection: (patch: RunStylePatch) => void;
|
|
262
|
-
/**
|
|
263
|
-
*
|
|
264
|
-
* uniformly carry it, clear it if it does. The shape a B / I / x² control
|
|
265
|
-
* or a keyboard shortcut wants, and what this hook's own shortcuts call.
|
|
266
|
-
*/
|
|
253
|
+
/** Toggle one styling over `selection` — set it if the range doesn't
|
|
254
|
+
* uniformly carry it, clear it if it does. */
|
|
267
255
|
toggleStyle: (toggle: StyleToggle) => void;
|
|
268
256
|
}
|
|
269
|
-
|
|
270
|
-
declare function useTextEdit(opts: UseTextEditOptions): UseTextEditReturn;
|
|
271
|
-
/**
|
|
272
|
-
* The overlay's `top`, in container pixels, for content `contentHeight` tall
|
|
273
|
-
* (pre-scale, like the rest of `pose`): the pose's top, shifted by the same
|
|
274
|
-
* `verticalAlign` slack the canvas applies, plus `drop` — how far below the
|
|
275
|
-
* CSS line's baseline the canvas puts its own, from {@link baselineDrop}.
|
|
276
|
-
* @internal
|
|
277
|
-
*/
|
|
278
|
-
declare function overlayTop(pose: TextEditScreenPose, contentHeight: number, drop?: number): number;
|
|
257
|
+
|
|
279
258
|
/**
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
* is read from both sides rather than predicted: the canvas's from its own layout, the
|
|
285
|
-
* overlay's from a zero-height inline box on a hidden line set exactly as the
|
|
286
|
-
* overlay is. Pre-scale; `0` when either side has nothing to measure.
|
|
287
|
-
* @internal
|
|
259
|
+
* In-place text editing via a contenteditable overlay positioned over the
|
|
260
|
+
* text node's screen-space pose. Enter / blur commit; Shift+Enter inserts a
|
|
261
|
+
* newline. The text layer should hide the node it's currently editing
|
|
262
|
+
* (`isHidden: (n) => n.id === editingId`) so the overlay isn't drawn twice.
|
|
288
263
|
*/
|
|
289
|
-
|
|
264
|
+
|
|
265
|
+
/** In-place text editing via a contenteditable overlay positioned over the text node's screen-space pose. */
|
|
266
|
+
declare function useTextEdit(opts: UseTextEditOptions): UseTextEditReturn;
|
|
290
267
|
|
|
291
268
|
/**
|
|
292
269
|
* The DOM position of plain-text character position `offset`: a text node
|
|
@@ -313,20 +290,15 @@ declare function charOffsetToDomPosition(parent: HTMLElement, offset: number): {
|
|
|
313
290
|
declare function domPositionToCharOffset(parent: HTMLElement, node: Node, offset: number): number;
|
|
314
291
|
|
|
315
292
|
/**
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
* styles. Newlines inside a run are literal `\n` characters; the overlay
|
|
320
|
-
* has `white-space: pre-wrap` so they render as line breaks. The browser
|
|
321
|
-
* breaks at no other UAX #14 hard break, so each of those is written as a
|
|
293
|
+
* DOM ↔ `StyledRun[]` serializers for the contenteditable overlay: a flat
|
|
294
|
+
* sequence of `<span data-run>` elements, one per run. The browser breaks at
|
|
295
|
+
* `\n` and at no other UAX #14 hard break, so each of those is written as a
|
|
322
296
|
* `<span data-break>` holding a `\n` in its place — one character for one,
|
|
323
297
|
* which keeps DOM offsets equal to source offsets.
|
|
324
298
|
*
|
|
325
|
-
* A small-caps run
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
* synthesis uses a fixed factor of its own. The pieces are presentation
|
|
329
|
-
* only — `domToRuns` reads straight through them.
|
|
299
|
+
* A small-caps run's lowercase letters sit in `<span data-small-caps>` pieces,
|
|
300
|
+
* uppercased and sized at the scale the canvas draws them with, since a
|
|
301
|
+
* browser's own synthesis uses a fixed factor of its own.
|
|
330
302
|
*/
|
|
331
303
|
|
|
332
304
|
/** Walk an overlay tree and emit a coalesced `StyledRun[]`. */
|
|
@@ -335,11 +307,9 @@ declare function domToRuns(parent: HTMLElement): StyledRun[];
|
|
|
335
307
|
* runs have it, and the face and transform that decide their pieces. */
|
|
336
308
|
type OverlayRunBase = Pick<ResolvedTextStyle, 'fontFamily' | 'fontWeight' | 'fontStyle' | 'textTransform' | 'fontVariantCaps'>;
|
|
337
309
|
/**
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
* inheriting small caps needs to be split by; without it the kit defaults
|
|
341
|
-
* stand in.
|
|
310
|
+
* Replace `parent`'s children with one `<span data-run>` per run. `base` is
|
|
311
|
+
* the node's style, which a run inheriting small caps is split by.
|
|
342
312
|
*/
|
|
343
313
|
declare function runsToDom(runs: readonly StyledRun[], parent: HTMLElement, base?: OverlayRunBase): void;
|
|
344
314
|
|
|
345
|
-
export { type StartEditOptions, type StyleToggle, TEXT_EDIT_FONT_HOLD, type TextEditClipRect, type TextEditCommit, type TextEditScreenPose, type TextEditSelection, type UseTextEditOptions, type UseTextEditReturn,
|
|
315
|
+
export { type OverlayRunBase, type StartEditOptions, type StyleToggle, TEXT_EDIT_FONT_HOLD, type TextEditClipRect, type TextEditCommit, type TextEditScreenPose, type TextEditSelection, type UseTextEditOptions, type UseTextEditReturn, charOffsetToDomPosition, domPositionToCharOffset, domToRuns, runsToDom, useTextEdit };
|