chat-layout 0.1.4 → 1.0.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 +200 -6
- package/index.d.mts +195 -110
- package/index.mjs +1304 -590
- package/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,27 +1,221 @@
|
|
|
1
1
|
# chat-layout
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Canvas-based chat and timeline layout primitives with a v2 flex-style layout model.
|
|
4
|
+
|
|
5
|
+
The current recommended APIs are:
|
|
6
|
+
|
|
7
|
+
- `Flex` for row/column layout
|
|
8
|
+
- `FlexItem` for explicit `grow`
|
|
9
|
+
- `Place` for single-child horizontal placement
|
|
10
|
+
- `MultilineText` `align` / `physicalAlign` for text content alignment
|
|
11
|
+
- `ChatRenderer` plus `ListState` for virtualized chat rendering
|
|
12
|
+
- `memoRenderItem` for object items, or `memoRenderItemBy` when your stable key is primitive / explicit
|
|
13
|
+
|
|
14
|
+
**Layout Model**
|
|
15
|
+
`Flex` and `Place` split layout concerns more clearly than the older API:
|
|
16
|
+
|
|
17
|
+
- Use `new Flex(children, { direction: "row" | "column" })` for main-axis layout.
|
|
18
|
+
- Use `new FlexItem(child, { grow: 1 })` when a child should consume remaining space.
|
|
19
|
+
- `Flex` shrink-wraps on the cross axis by default; `maxWidth` / `maxHeight` act as measurement caps rather than implicit fill signals.
|
|
20
|
+
- Use `alignItems` / `alignSelf: "stretch"` when a specific child should fill the container's computed cross axis.
|
|
21
|
+
- Use `new Place(child, { align: "start" | "center" | "end" })` when a single child should fill available width and then be placed left/center/right.
|
|
22
|
+
- Use `justifyContent`, `alignItems`, and `alignSelf` for container/item placement.
|
|
23
|
+
- Use `align: "start" | "center" | "end"` on `MultilineText` for logical alignment that matches `Place.align`.
|
|
24
|
+
- Use `physicalAlign: "left" | "center" | "right"` on `MultilineText` only when you explicitly want physical left/right semantics.
|
|
25
|
+
- `Text` / `MultilineText` preserve blank lines and edge whitespace by default; opt into cleanup with `whitespace: "trim-and-collapse"`.
|
|
26
|
+
|
|
27
|
+
**Example**
|
|
28
|
+
This is the recommended chat bubble shape used by [example/chat.ts](./example/chat.ts):
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
type ChatItem = {
|
|
32
|
+
sender: string;
|
|
33
|
+
content: string;
|
|
34
|
+
reply?: {
|
|
35
|
+
sender: string;
|
|
36
|
+
content: string;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const renderItem = memoRenderItem((item: ChatItem): Node<C> => {
|
|
41
|
+
const senderLine = new Flex<C>(
|
|
42
|
+
[avatarDot, senderLabel],
|
|
43
|
+
{
|
|
44
|
+
direction: "row",
|
|
45
|
+
gap: 4,
|
|
46
|
+
mainAxisSize: "fit-content",
|
|
47
|
+
reverse: item.sender === "A",
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const messageText = new FlexItem(
|
|
52
|
+
new MultilineText(item.content, {
|
|
53
|
+
lineHeight: 20,
|
|
54
|
+
font: "16px system-ui",
|
|
55
|
+
style: "black",
|
|
56
|
+
align: "start",
|
|
57
|
+
}),
|
|
58
|
+
{ alignSelf: "start" },
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const bubbleChildren: Node<C>[] = [];
|
|
62
|
+
if (item.reply != null) {
|
|
63
|
+
bubbleChildren.push(
|
|
64
|
+
new FlexItem(
|
|
65
|
+
new RoundedBox(
|
|
66
|
+
new Flex<C>(
|
|
67
|
+
[
|
|
68
|
+
new Text(item.reply.sender, {
|
|
69
|
+
lineHeight: 14,
|
|
70
|
+
font: "11px system-ui",
|
|
71
|
+
style: "#666",
|
|
72
|
+
}),
|
|
73
|
+
new MultilineText(item.reply.content, {
|
|
74
|
+
lineHeight: 16,
|
|
75
|
+
font: "13px system-ui",
|
|
76
|
+
style: "#444",
|
|
77
|
+
align: "start",
|
|
78
|
+
}),
|
|
79
|
+
],
|
|
80
|
+
{
|
|
81
|
+
direction: "column",
|
|
82
|
+
gap: 2,
|
|
83
|
+
alignItems: "start",
|
|
84
|
+
},
|
|
85
|
+
),
|
|
86
|
+
{
|
|
87
|
+
top: 5,
|
|
88
|
+
bottom: 5,
|
|
89
|
+
left: 8,
|
|
90
|
+
right: 8,
|
|
91
|
+
radii: 6,
|
|
92
|
+
fill: "#e2e2e2",
|
|
93
|
+
},
|
|
94
|
+
),
|
|
95
|
+
{ alignSelf: "stretch" },
|
|
96
|
+
),
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
bubbleChildren.push(messageText);
|
|
100
|
+
|
|
101
|
+
const bubbleColumn = new Flex<C>(bubbleChildren, {
|
|
102
|
+
direction: "column",
|
|
103
|
+
gap: 6,
|
|
104
|
+
// The bubble itself stays intrinsic on the cross axis.
|
|
105
|
+
// Only the reply preview stretches to the bubble width.
|
|
106
|
+
alignItems: "start",
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const content = new RoundedBox(
|
|
110
|
+
bubbleColumn,
|
|
111
|
+
{
|
|
112
|
+
top: 6,
|
|
113
|
+
bottom: 6,
|
|
114
|
+
left: 10,
|
|
115
|
+
right: 10,
|
|
116
|
+
radii: 8,
|
|
117
|
+
fill: "#ccc",
|
|
118
|
+
},
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const body = new Flex<C>([senderLine, content], {
|
|
122
|
+
direction: "column",
|
|
123
|
+
alignItems: item.sender === "A" ? "end" : "start",
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const alignedBody = new Place<C>(body, {
|
|
127
|
+
align: item.sender === "A" ? "end" : "start",
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return new Place(
|
|
131
|
+
new PaddingBox(
|
|
132
|
+
new Flex<C>(
|
|
133
|
+
[
|
|
134
|
+
avatar,
|
|
135
|
+
new FlexItem(alignedBody, { grow: 1 }),
|
|
136
|
+
new Fixed(32, 0),
|
|
137
|
+
],
|
|
138
|
+
{
|
|
139
|
+
direction: "row",
|
|
140
|
+
gap: 4,
|
|
141
|
+
reverse: item.sender === "A",
|
|
142
|
+
},
|
|
143
|
+
),
|
|
144
|
+
{
|
|
145
|
+
top: 4,
|
|
146
|
+
bottom: 4,
|
|
147
|
+
left: 4,
|
|
148
|
+
right: 4,
|
|
149
|
+
},
|
|
150
|
+
),
|
|
151
|
+
{
|
|
152
|
+
align: item.sender === "A" ? "end" : "start",
|
|
153
|
+
},
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
That combination gives you:
|
|
159
|
+
|
|
160
|
+
- explicit row/column structure
|
|
161
|
+
- explicit grow behavior through `FlexItem`
|
|
162
|
+
- left/right chat placement through `Place`
|
|
163
|
+
- wrapped message bubbles that respect available width without becoming full-width by default
|
|
164
|
+
- nested reply previews that use item-level cross-axis `stretch` to fill the bubble width
|
|
165
|
+
|
|
166
|
+
In other words: a finite `maxWidth` / `maxHeight` limits measurement, but does not force the `Flex` container to fill the cross axis. If you want a child to fill the computed bubble width, mark that child with `alignSelf: "stretch"` (or inherit `alignItems: "stretch"` from the parent).
|
|
167
|
+
|
|
168
|
+
## API notes
|
|
169
|
+
|
|
170
|
+
- `memoRenderItem()` now only accepts object items. If your list item is a primitive or you want to memoize by an explicit id, use `memoRenderItemBy(keyOf, renderItem)`.
|
|
171
|
+
- `FlexItemOptions` intentionally exposes only the implemented item-level controls: `grow` and `alignSelf`. The previously documented `shrink` / `basis` fields were removed because they were never implemented.
|
|
172
|
+
- `ListState.position` now uses `undefined` as the explicit “use renderer default anchor” state. Use `list.setAnchor(position, offset)` to opt into a concrete anchor.
|
|
173
|
+
- `ListState` can be seeded with `new ListState(items)` and reset with `list.reset(nextItems)`.
|
|
174
|
+
- `MultilineText` now uses only `align` / `physicalAlign`; the old `alignment` field has been removed.
|
|
175
|
+
|
|
176
|
+
### Migration notes
|
|
177
|
+
|
|
178
|
+
- Before:
|
|
179
|
+
- `memoRenderItem((item: number) => ...)`
|
|
180
|
+
- After:
|
|
181
|
+
- `memoRenderItemBy((item: number) => item, (item) => ...)`
|
|
182
|
+
- Before:
|
|
183
|
+
- `new FlexItem(node, { grow: 1, shrink: 1, basis: 100 })`
|
|
184
|
+
- After:
|
|
185
|
+
- `new FlexItem(node, { grow: 1 })`
|
|
186
|
+
- unsupported sizing semantics should be modeled explicitly in node measurement/layout instead of `shrink` / `basis`
|
|
187
|
+
- Before:
|
|
188
|
+
- `new MultilineText(text, { alignment: "left" })`
|
|
189
|
+
- After:
|
|
190
|
+
- `new MultilineText(text, { align: "start" })`
|
|
191
|
+
- or `new MultilineText(text, { physicalAlign: "left" })` when physical left/right semantics are required
|
|
192
|
+
- Before:
|
|
193
|
+
- `list.position = Number.NaN`
|
|
194
|
+
- After:
|
|
195
|
+
- `list.resetScroll()`
|
|
196
|
+
- or `list.setAnchor(index, offset)` for an explicit anchor
|
|
197
|
+
|
|
198
|
+
**Development**
|
|
199
|
+
Install dependencies:
|
|
4
200
|
|
|
5
201
|
```bash
|
|
6
202
|
bun install
|
|
7
203
|
```
|
|
8
204
|
|
|
9
|
-
|
|
205
|
+
Type-check:
|
|
10
206
|
|
|
11
207
|
```bash
|
|
12
208
|
bun run typecheck
|
|
13
209
|
```
|
|
14
210
|
|
|
15
|
-
|
|
211
|
+
Build distributable files:
|
|
16
212
|
|
|
17
213
|
```bash
|
|
18
214
|
bun run dist
|
|
19
215
|
```
|
|
20
216
|
|
|
21
|
-
|
|
217
|
+
Build the chat example:
|
|
22
218
|
|
|
23
219
|
```bash
|
|
24
220
|
bun run example
|
|
25
221
|
```
|
|
26
|
-
|
|
27
|
-
This project was created using `bun init` in bun v1.1.37. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|
package/index.d.mts
CHANGED
|
@@ -11,13 +11,50 @@ interface RenderFeedback {
|
|
|
11
11
|
/** Largest visible continuous item position, expressed in item coordinates rather than pixels. */
|
|
12
12
|
max: number;
|
|
13
13
|
}
|
|
14
|
-
type
|
|
14
|
+
type Axis = "row" | "column";
|
|
15
|
+
type MainAxisAlignment = "start" | "center" | "end" | "space-between" | "space-around" | "space-evenly";
|
|
16
|
+
type CrossAxisAlignment = "start" | "center" | "end" | "stretch";
|
|
17
|
+
type MainAxisSize = "fill" | "fit-content";
|
|
18
|
+
type TextAlign = "start" | "center" | "end";
|
|
19
|
+
type PhysicalTextAlign = "left" | "center" | "right";
|
|
20
|
+
type TextWhitespaceMode = "preserve" | "trim-and-collapse";
|
|
21
|
+
interface TextStyleOptions<C extends CanvasRenderingContext2D> {
|
|
22
|
+
lineHeight: number;
|
|
23
|
+
font: string;
|
|
24
|
+
style: DynValue<C, string>;
|
|
25
|
+
/** Default: preserve input whitespace, including blank lines and edge spaces. */
|
|
26
|
+
whitespace?: TextWhitespaceMode;
|
|
27
|
+
}
|
|
28
|
+
interface MultilineTextOptions<C extends CanvasRenderingContext2D> extends TextStyleOptions<C> {
|
|
29
|
+
/** Logical alignment that matches `Place.align`. */
|
|
30
|
+
align?: TextAlign;
|
|
31
|
+
/** Explicit physical alignment when left/right semantics are required. */
|
|
32
|
+
physicalAlign?: PhysicalTextAlign;
|
|
33
|
+
}
|
|
34
|
+
interface TextOptions<C extends CanvasRenderingContext2D> extends TextStyleOptions<C> {}
|
|
35
|
+
interface LayoutConstraints {
|
|
36
|
+
minWidth?: number;
|
|
37
|
+
maxWidth?: number;
|
|
38
|
+
minHeight?: number;
|
|
39
|
+
maxHeight?: number;
|
|
40
|
+
}
|
|
41
|
+
interface FlexItemOptions {
|
|
42
|
+
grow?: number;
|
|
43
|
+
alignSelf?: CrossAxisAlignment | "auto";
|
|
44
|
+
}
|
|
45
|
+
interface FlexContainerOptions {
|
|
46
|
+
direction?: Axis;
|
|
47
|
+
gap?: number;
|
|
48
|
+
justifyContent?: MainAxisAlignment;
|
|
49
|
+
alignItems?: CrossAxisAlignment;
|
|
50
|
+
reverse?: boolean;
|
|
51
|
+
mainAxisSize?: MainAxisSize;
|
|
52
|
+
}
|
|
15
53
|
interface Context<C extends CanvasRenderingContext2D> {
|
|
16
54
|
graphics: C;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
measureNode(node: Node<C>): Box;
|
|
55
|
+
/** v2: 显式布局约束 */
|
|
56
|
+
constraints?: LayoutConstraints;
|
|
57
|
+
measureNode(node: Node<C>, constraints?: LayoutConstraints): Box;
|
|
21
58
|
invalidateNode(node: Node<C>): void;
|
|
22
59
|
resolveDynValue<T>(value: DynValue<C, T>): T;
|
|
23
60
|
with<T>(this: Context<C>, cb: (g: C) => T): T;
|
|
@@ -35,55 +72,47 @@ interface Node<C extends CanvasRenderingContext2D> {
|
|
|
35
72
|
measure(ctx: Context<C>): Box;
|
|
36
73
|
draw(ctx: Context<C>, x: number, y: number): boolean;
|
|
37
74
|
hittest(ctx: Context<C>, test: HitTest): boolean;
|
|
38
|
-
|
|
75
|
+
}
|
|
76
|
+
interface LayoutRect {
|
|
77
|
+
x: number;
|
|
78
|
+
y: number;
|
|
79
|
+
width: number;
|
|
80
|
+
height: number;
|
|
81
|
+
}
|
|
82
|
+
interface ChildLayoutResult<C extends CanvasRenderingContext2D> {
|
|
83
|
+
node: Node<C>;
|
|
84
|
+
rect: LayoutRect;
|
|
85
|
+
contentBox: LayoutRect;
|
|
86
|
+
constraints?: LayoutConstraints;
|
|
87
|
+
}
|
|
88
|
+
interface FlexLayoutResult<C extends CanvasRenderingContext2D> {
|
|
89
|
+
containerBox: LayoutRect;
|
|
90
|
+
contentBox: LayoutRect;
|
|
91
|
+
children: ChildLayoutResult<C>[];
|
|
92
|
+
constraints?: LayoutConstraints;
|
|
39
93
|
}
|
|
40
94
|
//#endregion
|
|
41
|
-
//#region src/nodes.d.ts
|
|
95
|
+
//#region src/nodes/base.d.ts
|
|
42
96
|
declare abstract class Group<C extends CanvasRenderingContext2D> implements Node<C> {
|
|
43
|
-
|
|
97
|
+
#private;
|
|
44
98
|
constructor(children: Node<C>[]);
|
|
99
|
+
get children(): readonly Node<C>[];
|
|
100
|
+
replaceChildren(nextChildren: Node<C>[]): void;
|
|
45
101
|
abstract measure(ctx: Context<C>): Box;
|
|
46
102
|
abstract draw(ctx: Context<C>, x: number, y: number): boolean;
|
|
47
103
|
abstract hittest(ctx: Context<C>, test: HitTest): boolean;
|
|
48
|
-
get flex(): boolean;
|
|
49
|
-
}
|
|
50
|
-
declare class VStack<C extends CanvasRenderingContext2D> extends Group<C> {
|
|
51
|
-
readonly options: {
|
|
52
|
-
gap?: number;
|
|
53
|
-
alignment?: "left" | "center" | "right";
|
|
54
|
-
};
|
|
55
|
-
constructor(children: Node<C>[], options?: {
|
|
56
|
-
gap?: number;
|
|
57
|
-
alignment?: "left" | "center" | "right";
|
|
58
|
-
});
|
|
59
|
-
measure(ctx: Context<C>): Box;
|
|
60
|
-
draw(ctx: Context<C>, x: number, y: number): boolean;
|
|
61
|
-
hittest(ctx: Context<C>, test: HitTest): boolean;
|
|
62
|
-
}
|
|
63
|
-
declare class HStack<C extends CanvasRenderingContext2D> extends Group<C> {
|
|
64
|
-
readonly children: Node<C>[];
|
|
65
|
-
readonly options: {
|
|
66
|
-
reverse?: boolean;
|
|
67
|
-
gap?: number;
|
|
68
|
-
};
|
|
69
|
-
constructor(children: Node<C>[], options?: {
|
|
70
|
-
reverse?: boolean;
|
|
71
|
-
gap?: number;
|
|
72
|
-
});
|
|
73
|
-
measure(ctx: Context<C>): Box;
|
|
74
|
-
draw(ctx: Context<C>, x: number, y: number): boolean;
|
|
75
|
-
hittest(ctx: Context<C>, test: HitTest): boolean;
|
|
76
104
|
}
|
|
77
105
|
declare class Wrapper<C extends CanvasRenderingContext2D> implements Node<C> {
|
|
78
106
|
#private;
|
|
79
107
|
constructor(inner: Node<C>);
|
|
80
108
|
get inner(): Node<C>;
|
|
81
109
|
set inner(newNode: Node<C>);
|
|
82
|
-
get flex(): boolean;
|
|
83
110
|
measure(ctx: Context<C>): Box;
|
|
84
111
|
draw(ctx: Context<C>, x: number, y: number): boolean;
|
|
85
112
|
hittest(ctx: Context<C>, test: HitTest): boolean;
|
|
86
113
|
}
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/nodes/box.d.ts
|
|
87
116
|
declare class PaddingBox<C extends CanvasRenderingContext2D> extends Wrapper<C> {
|
|
88
117
|
#private;
|
|
89
118
|
readonly padding: {
|
|
@@ -102,103 +131,142 @@ declare class PaddingBox<C extends CanvasRenderingContext2D> extends Wrapper<C>
|
|
|
102
131
|
draw(ctx: Context<C>, x: number, y: number): boolean;
|
|
103
132
|
hittest(ctx: Context<C>, test: HitTest): boolean;
|
|
104
133
|
}
|
|
105
|
-
declare class
|
|
106
|
-
|
|
134
|
+
declare class Fixed<C extends CanvasRenderingContext2D> implements Node<C> {
|
|
135
|
+
readonly width: number;
|
|
136
|
+
readonly height: number;
|
|
137
|
+
constructor(width: number, height: number);
|
|
138
|
+
measure(_ctx: Context<C>): Box;
|
|
139
|
+
draw(_ctx: Context<C>, _x: number, _y: number): boolean;
|
|
140
|
+
hittest(_ctx: Context<C>, _test: HitTest): boolean;
|
|
141
|
+
}
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/nodes/flex.d.ts
|
|
144
|
+
declare class FlexItem<C extends CanvasRenderingContext2D> extends Wrapper<C> {
|
|
145
|
+
readonly item: FlexItemOptions;
|
|
146
|
+
constructor(inner: Node<C>, item?: FlexItemOptions);
|
|
147
|
+
}
|
|
148
|
+
declare class Flex<C extends CanvasRenderingContext2D> extends Group<C> {
|
|
149
|
+
readonly options: FlexContainerOptions;
|
|
150
|
+
constructor(children: Node<C>[], options?: FlexContainerOptions);
|
|
151
|
+
measure(ctx: Context<C>): Box;
|
|
152
|
+
draw(ctx: Context<C>, x: number, y: number): boolean;
|
|
153
|
+
hittest(ctx: Context<C>, test: HitTest): boolean;
|
|
154
|
+
}
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/nodes/place.d.ts
|
|
157
|
+
declare class Place<C extends CanvasRenderingContext2D> extends Wrapper<C> {
|
|
107
158
|
readonly options: {
|
|
108
|
-
|
|
159
|
+
align?: TextAlign;
|
|
160
|
+
expand?: boolean;
|
|
109
161
|
};
|
|
110
|
-
constructor(inner: Node<C>, options
|
|
111
|
-
|
|
162
|
+
constructor(inner: Node<C>, options?: {
|
|
163
|
+
align?: TextAlign;
|
|
164
|
+
expand?: boolean;
|
|
112
165
|
});
|
|
113
166
|
measure(ctx: Context<C>): Box;
|
|
114
167
|
draw(ctx: Context<C>, x: number, y: number): boolean;
|
|
115
168
|
hittest(ctx: Context<C>, test: HitTest): boolean;
|
|
116
169
|
}
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region src/nodes/text.d.ts
|
|
117
172
|
declare class MultilineText<C extends CanvasRenderingContext2D> implements Node<C> {
|
|
118
|
-
#private;
|
|
119
173
|
readonly text: string;
|
|
120
|
-
readonly options:
|
|
121
|
-
|
|
122
|
-
font: string;
|
|
123
|
-
alignment: "left" | "center" | "right";
|
|
124
|
-
style: DynValue<C, string>;
|
|
125
|
-
};
|
|
126
|
-
constructor(text: string, options: {
|
|
127
|
-
lineHeight: number;
|
|
128
|
-
font: string;
|
|
129
|
-
alignment: "left" | "center" | "right";
|
|
130
|
-
style: DynValue<C, string>;
|
|
131
|
-
});
|
|
132
|
-
get flex(): boolean;
|
|
174
|
+
readonly options: MultilineTextOptions<C>;
|
|
175
|
+
constructor(text: string, options: MultilineTextOptions<C>);
|
|
133
176
|
measure(ctx: Context<C>): Box;
|
|
134
177
|
draw(ctx: Context<C>, x: number, y: number): boolean;
|
|
135
|
-
hittest(_ctx: Context<C>, _test:
|
|
178
|
+
hittest(_ctx: Context<C>, _test: {
|
|
179
|
+
x: number;
|
|
180
|
+
y: number;
|
|
181
|
+
type: "click" | "auxclick" | "hover";
|
|
182
|
+
}): boolean;
|
|
136
183
|
}
|
|
137
184
|
declare class Text<C extends CanvasRenderingContext2D> implements Node<C> {
|
|
138
|
-
#private;
|
|
139
185
|
readonly text: string;
|
|
140
|
-
readonly options:
|
|
141
|
-
|
|
142
|
-
font: string;
|
|
143
|
-
style: DynValue<C, string>;
|
|
144
|
-
};
|
|
145
|
-
constructor(text: string, options: {
|
|
146
|
-
lineHeight: number;
|
|
147
|
-
font: string;
|
|
148
|
-
style: DynValue<C, string>;
|
|
149
|
-
});
|
|
150
|
-
get flex(): boolean;
|
|
186
|
+
readonly options: TextOptions<C>;
|
|
187
|
+
constructor(text: string, options: TextOptions<C>);
|
|
151
188
|
measure(ctx: Context<C>): Box;
|
|
152
189
|
draw(ctx: Context<C>, x: number, y: number): boolean;
|
|
153
|
-
hittest(_ctx: Context<C>, _test:
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
constructor(width: number, height: number);
|
|
159
|
-
get flex(): boolean;
|
|
160
|
-
measure(_ctx: Context<C>): Box;
|
|
161
|
-
draw(_ctx: Context<C>, _x: number, _y: number): boolean;
|
|
162
|
-
hittest(_ctx: Context<C>, _test: HitTest): boolean;
|
|
190
|
+
hittest(_ctx: Context<C>, _test: {
|
|
191
|
+
x: number;
|
|
192
|
+
y: number;
|
|
193
|
+
type: "click" | "auxclick" | "hover";
|
|
194
|
+
}): boolean;
|
|
163
195
|
}
|
|
164
196
|
//#endregion
|
|
165
|
-
//#region src/renderer.d.ts
|
|
197
|
+
//#region src/renderer/base.d.ts
|
|
166
198
|
declare class BaseRenderer<C extends CanvasRenderingContext2D, O extends {} = {}> {
|
|
167
199
|
#private;
|
|
168
200
|
readonly options: RendererOptions & O;
|
|
169
201
|
graphics: C;
|
|
170
202
|
protected get context(): Context<C>;
|
|
171
203
|
constructor(graphics: C, options: RendererOptions & O);
|
|
204
|
+
protected getRootConstraints(): LayoutConstraints;
|
|
205
|
+
protected getRootContext(): Context<C>;
|
|
206
|
+
protected measureRootNode(node: Node<C>): Box;
|
|
207
|
+
protected drawRootNode(node: Node<C>, x?: number, y?: number): boolean;
|
|
208
|
+
protected hittestRootNode(node: Node<C>, test: HitTest): boolean;
|
|
172
209
|
invalidateNode(node: Node<C>): void;
|
|
173
|
-
|
|
210
|
+
getLayoutResult(node: Node<C>, constraints?: LayoutConstraints): FlexLayoutResult<C> | undefined;
|
|
211
|
+
setLayoutResult(node: Node<C>, result: FlexLayoutResult<C>, constraints?: LayoutConstraints): void;
|
|
212
|
+
protected getTextLayout<T>(node: Node<C>, key: string): T | undefined;
|
|
213
|
+
protected setTextLayout<T>(node: Node<C>, key: string, layout: T): void;
|
|
214
|
+
measureNode(node: Node<C>, constraints?: LayoutConstraints): Box;
|
|
174
215
|
}
|
|
175
216
|
declare class DebugRenderer<C extends CanvasRenderingContext2D> extends BaseRenderer<C> {
|
|
176
217
|
draw(node: Node<C>): boolean;
|
|
177
218
|
hittest(node: Node<C>, test: HitTest): boolean;
|
|
178
219
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
};
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/renderer/list-state.d.ts
|
|
182
222
|
declare class ListState<T extends {}> {
|
|
183
223
|
offset: number;
|
|
184
|
-
position: number;
|
|
224
|
+
position: number | undefined;
|
|
185
225
|
items: T[];
|
|
226
|
+
constructor(items?: T[]);
|
|
186
227
|
unshift(...items: T[]): void;
|
|
187
228
|
unshiftAll(items: T[]): void;
|
|
188
229
|
push(...items: T[]): void;
|
|
189
230
|
pushAll(items: T[]): void;
|
|
190
|
-
|
|
231
|
+
setAnchor(position: number, offset?: number): void;
|
|
232
|
+
reset(items?: T[]): void;
|
|
191
233
|
resetScroll(): void;
|
|
192
234
|
applyScroll(delta: number): void;
|
|
193
235
|
}
|
|
194
|
-
|
|
236
|
+
//#endregion
|
|
237
|
+
//#region src/renderer/memo.d.ts
|
|
238
|
+
declare function memoRenderItem<C extends CanvasRenderingContext2D, T extends object>(renderItem: (item: T) => Node<C>): ((item: T) => Node<C>) & {
|
|
239
|
+
reset: (key: T) => boolean;
|
|
240
|
+
};
|
|
241
|
+
declare function memoRenderItemBy<C extends CanvasRenderingContext2D, T, K>(keyOf: (item: T) => K, renderItem: (item: T) => Node<C>): ((item: T) => Node<C>) & {
|
|
242
|
+
reset: (item: T) => boolean;
|
|
243
|
+
resetKey: (key: K) => boolean;
|
|
244
|
+
};
|
|
245
|
+
//#endregion
|
|
246
|
+
//#region src/renderer/virtualized/solver.d.ts
|
|
247
|
+
interface VisibleListState {
|
|
248
|
+
position?: number;
|
|
249
|
+
offset: number;
|
|
250
|
+
}
|
|
251
|
+
interface NormalizedListState {
|
|
252
|
+
position: number;
|
|
253
|
+
offset: number;
|
|
254
|
+
}
|
|
255
|
+
interface VisibleWindowEntry<T> {
|
|
195
256
|
idx: number;
|
|
196
|
-
|
|
257
|
+
value: T;
|
|
197
258
|
offset: number;
|
|
198
259
|
height: number;
|
|
199
|
-
}
|
|
260
|
+
}
|
|
261
|
+
interface VisibleWindow<T> {
|
|
262
|
+
drawList: VisibleWindowEntry<T>[];
|
|
263
|
+
shift: number;
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/renderer/virtualized/base.d.ts
|
|
200
267
|
interface JumpToOptions {
|
|
201
268
|
animated?: boolean;
|
|
269
|
+
block?: "start" | "center" | "end";
|
|
202
270
|
duration?: number;
|
|
203
271
|
onComplete?: () => void;
|
|
204
272
|
}
|
|
@@ -210,48 +278,65 @@ declare abstract class VirtualizedRenderer<C extends CanvasRenderingContext2D, T
|
|
|
210
278
|
static readonly MIN_JUMP_DURATION = 160;
|
|
211
279
|
static readonly MAX_JUMP_DURATION = 420;
|
|
212
280
|
static readonly JUMP_DURATION_PER_ITEM = 28;
|
|
213
|
-
get position(): number;
|
|
214
|
-
set position(value: number);
|
|
281
|
+
get position(): number | undefined;
|
|
282
|
+
set position(value: number | undefined);
|
|
215
283
|
get offset(): number;
|
|
216
284
|
set offset(value: number);
|
|
217
285
|
get items(): T[];
|
|
218
286
|
set items(value: T[]);
|
|
219
287
|
abstract render(feedback?: RenderFeedback): boolean;
|
|
220
|
-
abstract hittest(test:
|
|
288
|
+
abstract hittest(test: {
|
|
289
|
+
x: number;
|
|
290
|
+
y: number;
|
|
291
|
+
type: "click" | "auxclick" | "hover";
|
|
292
|
+
}): boolean;
|
|
293
|
+
protected _readListState(): VisibleListState;
|
|
294
|
+
protected _commitListState(state: NormalizedListState): void;
|
|
221
295
|
jumpTo(index: number, options?: JumpToOptions): void;
|
|
222
296
|
protected _resetRenderFeedback(feedback?: RenderFeedback): void;
|
|
223
297
|
protected _accumulateRenderFeedback(feedback: RenderFeedback, idx: number, top: number, height: number): void;
|
|
224
|
-
protected _renderDrawList(list:
|
|
298
|
+
protected _renderDrawList(list: VisibleWindow<Node<C>>["drawList"], shift: number, feedback?: RenderFeedback): boolean;
|
|
299
|
+
protected _renderVisibleWindow(window: VisibleWindow<Node<C>>, feedback?: RenderFeedback): boolean;
|
|
300
|
+
protected _hittestVisibleWindow(window: VisibleWindow<Node<C>>, test: {
|
|
301
|
+
x: number;
|
|
302
|
+
y: number;
|
|
303
|
+
type: "click" | "auxclick" | "hover";
|
|
304
|
+
}): boolean;
|
|
225
305
|
protected _prepareRender(): boolean;
|
|
226
306
|
protected _finishRender(requestRedraw: boolean): boolean;
|
|
227
307
|
protected _clampItemIndex(index: number): number;
|
|
228
308
|
protected _getItemHeight(index: number): number;
|
|
229
|
-
protected
|
|
230
|
-
protected abstract
|
|
309
|
+
protected _getAnchorAtOffset(index: number, offset: number): number;
|
|
310
|
+
protected abstract _normalizeListState(state: VisibleListState): NormalizedListState;
|
|
311
|
+
protected abstract _readAnchor(state: NormalizedListState): number;
|
|
231
312
|
protected abstract _applyAnchor(anchor: number): void;
|
|
232
|
-
protected abstract
|
|
313
|
+
protected abstract _getDefaultJumpBlock(): NonNullable<JumpToOptions["block"]>;
|
|
314
|
+
protected abstract _getTargetAnchor(index: number, block: NonNullable<JumpToOptions["block"]>): number;
|
|
233
315
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region src/renderer/virtualized/chat.d.ts
|
|
318
|
+
declare class ChatRenderer<C extends CanvasRenderingContext2D, T extends {}> extends VirtualizedRenderer<C, T> {
|
|
319
|
+
#private;
|
|
320
|
+
protected _getDefaultJumpBlock(): NonNullable<JumpToOptions["block"]>;
|
|
321
|
+
protected _normalizeListState(state: VisibleListState): NormalizedListState;
|
|
322
|
+
protected _readAnchor(state: NormalizedListState): number;
|
|
237
323
|
protected _applyAnchor(anchor: number): void;
|
|
238
|
-
protected _getTargetAnchor(index: number): number;
|
|
324
|
+
protected _getTargetAnchor(index: number, block: NonNullable<JumpToOptions["block"]>): number;
|
|
239
325
|
render(feedback?: RenderFeedback): boolean;
|
|
240
326
|
hittest(test: HitTest): boolean;
|
|
241
327
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
328
|
+
//#endregion
|
|
329
|
+
//#region src/renderer/virtualized/timeline.d.ts
|
|
330
|
+
declare class TimelineRenderer<C extends CanvasRenderingContext2D, T extends {}> extends VirtualizedRenderer<C, T> {
|
|
331
|
+
#private;
|
|
332
|
+
protected _getDefaultJumpBlock(): NonNullable<JumpToOptions["block"]>;
|
|
333
|
+
protected _normalizeListState(state: VisibleListState): NormalizedListState;
|
|
334
|
+
protected _readAnchor(state: NormalizedListState): number;
|
|
245
335
|
protected _applyAnchor(anchor: number): void;
|
|
246
|
-
protected _getTargetAnchor(index: number): number;
|
|
336
|
+
protected _getTargetAnchor(index: number, block: NonNullable<JumpToOptions["block"]>): number;
|
|
247
337
|
render(feedback?: RenderFeedback): boolean;
|
|
248
338
|
hittest(test: HitTest): boolean;
|
|
249
339
|
}
|
|
250
340
|
//#endregion
|
|
251
|
-
|
|
252
|
-
declare function registerNodeParent<C extends CanvasRenderingContext2D>(node: Node<C>, parent: Node<C>): void;
|
|
253
|
-
declare function unregisterNodeParent<C extends CanvasRenderingContext2D>(node: Node<C>): void;
|
|
254
|
-
declare function getNodeParent<C extends CanvasRenderingContext2D>(node: Node<C>): Node<C> | undefined;
|
|
255
|
-
//#endregion
|
|
256
|
-
export { AlignBox, Alignment, BaseRenderer, Box, ChatRenderer, Context, DebugRenderer, DynValue, Fixed, Group, HStack, HitTest, JumpToOptions, ListState, MultilineText, Node, PaddingBox, RenderFeedback, RendererOptions, Text, TimelineRenderer, VStack, VirtualizedRenderer, Wrapper, getNodeParent, memoRenderItem, registerNodeParent, unregisterNodeParent };
|
|
341
|
+
export { Axis, BaseRenderer, Box, ChatRenderer, ChildLayoutResult, Context, CrossAxisAlignment, DebugRenderer, DynValue, Fixed, Flex, FlexContainerOptions, FlexItem, FlexItemOptions, FlexLayoutResult, Group, HitTest, JumpToOptions, LayoutConstraints, LayoutRect, ListState, MainAxisAlignment, MainAxisSize, MultilineText, MultilineTextOptions, Node, PaddingBox, PhysicalTextAlign, Place, RenderFeedback, RendererOptions, Text, TextAlign, TextOptions, TextStyleOptions, TextWhitespaceMode, TimelineRenderer, VirtualizedRenderer, Wrapper, memoRenderItem, memoRenderItemBy };
|
|
257
342
|
//# sourceMappingURL=index.d.mts.map
|