chat-layout 1.1.2 → 1.2.0-1
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 +21 -6
- package/example/chat.ts +6 -1
- package/index.d.mts +24 -6
- package/index.mjs +1553 -410
- package/index.mjs.map +1 -1
- package/package.json +2 -4
package/README.md
CHANGED
|
@@ -7,13 +7,14 @@ The current v2-style APIs are:
|
|
|
7
7
|
- `Flex`: row/column layout
|
|
8
8
|
- `FlexItem`: explicit `grow` / `shrink` / `alignSelf`
|
|
9
9
|
- `Place`: place a single child at `start` / `center` / `end`
|
|
10
|
+
- `ShrinkWrap`: search the narrowest width that keeps the current height stable
|
|
10
11
|
- `MultilineText`: text layout with logical `align` or physical `physicalAlign`
|
|
11
12
|
- `ChatRenderer` + `ListState`: virtualized chat rendering
|
|
12
13
|
- `memoRenderItem` / `memoRenderItemBy`: item render memoization
|
|
13
14
|
|
|
14
15
|
## Quick example
|
|
15
16
|
|
|
16
|
-
Use `Flex` to build structure, `FlexItem` to control resize behavior, and `Place` to align the final bubble:
|
|
17
|
+
Use `Flex` to build structure, `FlexItem` to control resize behavior, `ShrinkWrap` to keep the bubble as narrow as possible without adding lines, and `Place` to align the final bubble:
|
|
17
18
|
|
|
18
19
|
```ts
|
|
19
20
|
const bubble = new RoundedBox(
|
|
@@ -26,17 +27,27 @@ const bubble = new RoundedBox(
|
|
|
26
27
|
{ top: 6, bottom: 6, left: 10, right: 10, radii: 8, fill: "#ccc" },
|
|
27
28
|
);
|
|
28
29
|
|
|
30
|
+
const body = new ShrinkWrap(
|
|
31
|
+
new Flex(
|
|
32
|
+
[senderLine, bubble],
|
|
33
|
+
{ direction: "column", gap: 4, alignItems: item.sender === "A" ? "end" : "start" },
|
|
34
|
+
),
|
|
35
|
+
);
|
|
36
|
+
|
|
29
37
|
const row = new Flex(
|
|
30
38
|
[
|
|
31
39
|
avatar,
|
|
32
|
-
new FlexItem(
|
|
40
|
+
new FlexItem(
|
|
41
|
+
new Place(body, {
|
|
42
|
+
align: item.sender === "A" ? "end" : "start",
|
|
43
|
+
}),
|
|
44
|
+
{ grow: 1, shrink: 1 },
|
|
45
|
+
),
|
|
33
46
|
],
|
|
34
47
|
{ direction: "row", gap: 4, reverse: item.sender === "A" },
|
|
35
48
|
);
|
|
36
49
|
|
|
37
|
-
return
|
|
38
|
-
align: item.sender === "A" ? "end" : "start",
|
|
39
|
-
});
|
|
50
|
+
return row;
|
|
40
51
|
```
|
|
41
52
|
|
|
42
53
|
See [example/chat.ts](./example/chat.ts) for a full chat example.
|
|
@@ -47,9 +58,10 @@ See [example/chat.ts](./example/chat.ts) for a full chat example.
|
|
|
47
58
|
- `maxWidth` / `maxHeight` limit measurement, but do not automatically make children fill the cross axis.
|
|
48
59
|
- Use `alignItems: "stretch"` or `alignSelf: "stretch"` when a child should fill the computed cross size.
|
|
49
60
|
- `Place` is the simplest way to align a single bubble left, center, or right.
|
|
61
|
+
- `ShrinkWrap` is useful when a bubble sits inside a growable slot but should still collapse to the narrowest width that preserves its current line count.
|
|
50
62
|
- `MultilineText.align` uses logical values: `start`, `center`, `end`.
|
|
51
63
|
- `MultilineText.physicalAlign` uses physical values: `left`, `center`, `right`.
|
|
52
|
-
- `Text` and `MultilineText` default to `whiteSpace: "normal"`,
|
|
64
|
+
- `Text` and `MultilineText` default to `whiteSpace: "normal"`, using the library's canvas-first collapsible whitespace behavior.
|
|
53
65
|
- Use `whiteSpace: "pre-wrap"` when blank lines, hard breaks, or edge spaces must stay visible.
|
|
54
66
|
- `Text` and `MultilineText` default to `overflowWrap: "break-word"`, which preserves compatibility-first min-content sizing for shrink layouts.
|
|
55
67
|
- Use `overflowWrap: "anywhere"` when long unspaced strings should contribute grapheme-level breakpoints to min-content sizing.
|
|
@@ -103,6 +115,7 @@ Notes:
|
|
|
103
115
|
- Shrink only applies when there is a finite main-axis constraint and total content size overflows it.
|
|
104
116
|
- Overflow is redistributed by `shrink * basis`; today `basis` is internal-only and always `"auto"`.
|
|
105
117
|
- Custom nodes can implement `measureMinContent()` for better shrink results.
|
|
118
|
+
- `ShrinkWrap` complements flex shrink: it keeps probing narrower `maxWidth` values until the child would become taller, then uses the last safe width as the final layout.
|
|
106
119
|
- Known limitation: column shrink with `MultilineText` does not clip drawing by itself.
|
|
107
120
|
|
|
108
121
|
## Migration notes
|
|
@@ -139,3 +152,5 @@ Build the chat example:
|
|
|
139
152
|
```bash
|
|
140
153
|
bun run example
|
|
141
154
|
```
|
|
155
|
+
|
|
156
|
+
文本性能观测基线见 `docs/text-performance.md`。
|
package/example/chat.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
MultilineText,
|
|
8
8
|
PaddingBox,
|
|
9
9
|
Place,
|
|
10
|
+
ShrinkWrap,
|
|
10
11
|
Text,
|
|
11
12
|
Wrapper,
|
|
12
13
|
memoRenderItem,
|
|
@@ -357,7 +358,11 @@ const renderItem = memoRenderItem((item: ChatItem): Node<C> => {
|
|
|
357
358
|
alignItems: item.sender === "A" ? "end" : "start",
|
|
358
359
|
});
|
|
359
360
|
|
|
360
|
-
const
|
|
361
|
+
const shrinkWrappedBody = new ShrinkWrap<C>(body, {
|
|
362
|
+
preferredMinWidth: 160,
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const alignedBody = new Place<C>(shrinkWrappedBody, {
|
|
361
366
|
align: item.sender === "A" ? "end" : "start",
|
|
362
367
|
});
|
|
363
368
|
|
package/index.d.mts
CHANGED
|
@@ -53,7 +53,7 @@ type TextWhiteSpaceMode = "normal" | "pre-wrap";
|
|
|
53
53
|
*/
|
|
54
54
|
type TextOverflowMode = "clip" | "ellipsis";
|
|
55
55
|
/**
|
|
56
|
-
* Word breaking mode used by
|
|
56
|
+
* Word breaking mode used by the internal canvas text layout engine.
|
|
57
57
|
*/
|
|
58
58
|
type TextWordBreakMode = "normal" | "keep-all";
|
|
59
59
|
/**
|
|
@@ -74,9 +74,9 @@ interface TextStyleOptions<C extends CanvasRenderingContext2D> {
|
|
|
74
74
|
font: string;
|
|
75
75
|
/** Color or resolver used when drawing the text. */
|
|
76
76
|
color: DynValue<C, string>;
|
|
77
|
-
/** Default: normal;
|
|
77
|
+
/** Default: normal; uses canvas-first CSS-style collapsible whitespace behavior. */
|
|
78
78
|
whiteSpace?: TextWhiteSpaceMode;
|
|
79
|
-
/** Default: normal; use keep-all
|
|
79
|
+
/** Default: normal; use keep-all for CJK-friendly line breaking. */
|
|
80
80
|
wordBreak?: TextWordBreakMode;
|
|
81
81
|
/** Default: break-word; use anywhere when min-content should honor grapheme break opportunities. */
|
|
82
82
|
overflowWrap?: TextOverflowWrapMode;
|
|
@@ -91,9 +91,9 @@ interface InlineSpan<C extends CanvasRenderingContext2D> {
|
|
|
91
91
|
font?: string;
|
|
92
92
|
/** Color override for this fragment. Falls back to the node-level color. */
|
|
93
93
|
color?: DynValue<C, string>;
|
|
94
|
-
/** Optional break hint
|
|
94
|
+
/** Optional break hint for atomic inline spans. */
|
|
95
95
|
break?: "normal" | "never";
|
|
96
|
-
/** Optional extra occupied width
|
|
96
|
+
/** Optional extra occupied width appended after the span's rendered text. */
|
|
97
97
|
extraWidth?: number;
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
@@ -364,6 +364,24 @@ declare class Place<C extends CanvasRenderingContext2D> extends Wrapper<C> {
|
|
|
364
364
|
hittest(ctx: Context<C>, test: HitTest): boolean;
|
|
365
365
|
}
|
|
366
366
|
//#endregion
|
|
367
|
+
//#region src/nodes/shrinkwrap.d.ts
|
|
368
|
+
interface ShrinkWrapOptions {
|
|
369
|
+
tolerance?: number;
|
|
370
|
+
preferredMinWidth?: number;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Shrinks a single child to the narrowest width that does not increase its reference height.
|
|
374
|
+
*/
|
|
375
|
+
declare class ShrinkWrap<C extends CanvasRenderingContext2D> extends Wrapper<C> {
|
|
376
|
+
#private;
|
|
377
|
+
readonly options: ShrinkWrapOptions;
|
|
378
|
+
constructor(inner: Node<C>, options?: ShrinkWrapOptions);
|
|
379
|
+
measure(ctx: Context<C>): Box;
|
|
380
|
+
measureMinContent(ctx: Context<C>): Box;
|
|
381
|
+
draw(ctx: Context<C>, x: number, y: number): boolean;
|
|
382
|
+
hittest(ctx: Context<C>, test: HitTest): boolean;
|
|
383
|
+
}
|
|
384
|
+
//#endregion
|
|
367
385
|
//#region src/nodes/text.d.ts
|
|
368
386
|
/**
|
|
369
387
|
* Draws wrapped text using the configured line height and alignment.
|
|
@@ -658,5 +676,5 @@ declare class TimelineRenderer<C extends CanvasRenderingContext2D, T extends {}>
|
|
|
658
676
|
hittest(test: HitTest): boolean;
|
|
659
677
|
}
|
|
660
678
|
//#endregion
|
|
661
|
-
export { Axis, BaseRenderer, Box, ChatRenderer, ChildLayoutResult, Context, CrossAxisAlignment, DebugRenderer, DynValue, Fixed, Flex, FlexContainerOptions, FlexItem, FlexItemOptions, FlexLayoutResult, Group, HitTest, InlineSpan, JumpToOptions, LayoutConstraints, LayoutRect, ListState, MainAxisAlignment, MainAxisSize, MultilineText, MultilineTextOptions, Node, PaddingBox, PhysicalTextAlign, Place, RenderFeedback, RendererOptions, ReplaceListItemAnimationOptions, Text, TextAlign, TextEllipsisPosition, TextOptions, TextOverflowMode, TextOverflowWrapMode, TextStyleOptions, TextWhiteSpaceMode, TextWordBreakMode, TimelineRenderer, VirtualizedRenderer, Wrapper, memoRenderItem, memoRenderItemBy };
|
|
679
|
+
export { Axis, BaseRenderer, Box, ChatRenderer, ChildLayoutResult, Context, CrossAxisAlignment, DebugRenderer, DynValue, Fixed, Flex, FlexContainerOptions, FlexItem, FlexItemOptions, FlexLayoutResult, Group, HitTest, InlineSpan, JumpToOptions, LayoutConstraints, LayoutRect, ListState, MainAxisAlignment, MainAxisSize, MultilineText, MultilineTextOptions, Node, PaddingBox, PhysicalTextAlign, Place, RenderFeedback, RendererOptions, ReplaceListItemAnimationOptions, ShrinkWrap, Text, TextAlign, TextEllipsisPosition, TextOptions, TextOverflowMode, TextOverflowWrapMode, TextStyleOptions, TextWhiteSpaceMode, TextWordBreakMode, TimelineRenderer, VirtualizedRenderer, Wrapper, memoRenderItem, memoRenderItemBy };
|
|
662
680
|
//# sourceMappingURL=index.d.mts.map
|