@fulate/yoga 1.0.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 +68 -0
- package/dist/div.d.ts +33 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +642 -0
- package/dist/runtime.d.ts +39 -0
- package/dist/span.d.ts +45 -0
- package/dist/style.d.ts +59 -0
- package/dist/text-row/contract.d.ts +15 -0
- package/dist/text-row/index.d.ts +24 -0
- package/package.json +23 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# `@fulate/yoga`
|
|
2
|
+
|
|
3
|
+
> 文档角色:`GUIDE`。权威来源:Yoga adapter 源码与测试;Core/UI 的场景和元素契约由对应指南拥有。
|
|
4
|
+
|
|
5
|
+
`@fulate/yoga` 用 `yoga-layout` 为 Canvas 元素提供 Flex 布局。`Div` 是带 Yoga 样式的矩形容器,
|
|
6
|
+
`Span` 是带文本测量的 Yoga 文本元素;两者都仍是普通 Fulate Node/Element,不创建独立场景树。
|
|
7
|
+
|
|
8
|
+
`Div`、`Span` 会在模块加载时注册为 `f-div`、`f-span`;重复导入只更新同一 renderer registry,
|
|
9
|
+
不建立第二套元素类型系统。
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { Div, Span, FlexDirection, Justify } from "@fulate/yoga";
|
|
13
|
+
|
|
14
|
+
const panel = new Div({
|
|
15
|
+
width: 320,
|
|
16
|
+
height: 120,
|
|
17
|
+
flexDirection: FlexDirection.Row,
|
|
18
|
+
justifyContent: Justify.SpaceBetween,
|
|
19
|
+
children: [new Span({ text: "Hello" })]
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 样式与布局
|
|
24
|
+
|
|
25
|
+
Yoga 样式(尺寸、margin/padding、flex、align、position、overflow 等)直接作为 `YogaOption` 传入。
|
|
26
|
+
它们与普通 Element 属性共同存放在元素唯一 Properties 中;`setOptions()`、trusted batch、History、
|
|
27
|
+
属性 snapshot 和 JSON 都读写这同一份 authored state。属性 hook 把最终 changed fields 直接交给
|
|
28
|
+
`yogaNode.setWidth()`、`setFlexGrow()` 等 native setter,不建立 `yogaStyles` 镜像,也不借用 Core
|
|
29
|
+
runtime options。
|
|
30
|
+
|
|
31
|
+
连续的 Yoga 父子节点参与同一棵树;直接 parent 不是 Yoga 的节点自动成为 Yoga root,因此普通
|
|
32
|
+
Element、ScrollView 等非 Yoga 节点自然形成布局边界。Yoga native tree 自己向上传播 dirty;只有
|
|
33
|
+
实际 Yoga root 安装 `setDirtiedFunc`,由该 callback 把稳定 owner identity 合并进所属
|
|
34
|
+
`RootFrameRuntime`。普通 style、measure 和 children 更新不手写 root 上溯或另建 scheduler。
|
|
35
|
+
|
|
36
|
+
一帧先完成 authored mutation,再对每个 dirty Yoga root 计算一次布局。结果侧由 `hasNewLayout`
|
|
37
|
+
裁剪读取分支,再由 Transform 的 resolved-frame commit 只安装数值真实变化的
|
|
38
|
+
`left/top/width/height`。全部 layout owner 完成后,FrameRuntime 把实际 affected elements 合并为一次
|
|
39
|
+
SceneRegistry 通知,随后进入既有 geometry、spatial 与 paint。计算结果不写回 authored
|
|
40
|
+
Properties,也不进入 History。
|
|
41
|
+
|
|
42
|
+
`layout()` 是同步 drain 入口:它只结清 FrameRuntime 已登记的工作,与 RAF 使用完全相同的 phase;
|
|
43
|
+
clean 状态调用不会重新计算 Yoga。普通异步使用无需手动调用,等待 `root.nextTick()` 即可读取稳定结果。
|
|
44
|
+
|
|
45
|
+
## Vue 注册
|
|
46
|
+
|
|
47
|
+
导入 `@fulate/yoga` 会注册 `f-div` 与 `f-span` 元素,供 `@fulate/vue` renderer 使用:
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
<f-div flexDirection={FlexDirection.Column}>
|
|
51
|
+
<f-span text="Ready" />
|
|
52
|
+
</f-div>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## TextRow
|
|
56
|
+
|
|
57
|
+
`TextRow` 保留 Yoga 对直接子项的横向尺寸和位置计算,并让直接 `Span` 与嵌套
|
|
58
|
+
`TextRow` 使用同一条实际 alphabetic baseline。普通 `Div` 仍可作为固定或
|
|
59
|
+
`flexGrow` spacer,不参与文字基线计算。
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<f-text-row width={520} gap={8}>
|
|
63
|
+
<f-span text="P:" fontSize={18} />
|
|
64
|
+
<f-div flexGrow={1} />
|
|
65
|
+
<f-span text="30036" fontSize={20} />
|
|
66
|
+
<f-span text="kW" fontSize={12} />
|
|
67
|
+
</f-text-row>
|
|
68
|
+
```
|
package/dist/div.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Layer, PropertyChanges, Root } from "@fulate/core";
|
|
2
|
+
import type { Rect } from "@fulate/share";
|
|
3
|
+
import { Rectangle, RectangleProperties } from "@fulate/ui";
|
|
4
|
+
import { type YogaOption, type YogaOptionPatch } from "./style";
|
|
5
|
+
import { YogaNodeRuntime, yogaRuntimeKey, type YogaElementHost } from "./runtime";
|
|
6
|
+
export type { YogaOption, YogaOptionPatch } from "./style";
|
|
7
|
+
export { Align, BoxSizing, Display, Edge, FlexDirection, Gutter, Justify, Overflow, PositionType, Wrap } from "yoga-layout";
|
|
8
|
+
declare class DivProperties extends RectangleProperties {
|
|
9
|
+
constructor(options?: YogaOption);
|
|
10
|
+
getImpact(changes: PropertyChanges): import("@fulate/core").ChangeImpact;
|
|
11
|
+
}
|
|
12
|
+
/** Rectangle paint surface whose authored frame and flex style are owned by Yoga. */
|
|
13
|
+
export declare class Div extends Rectangle<DivProperties, any> implements YogaElementHost {
|
|
14
|
+
readonly [yogaRuntimeKey]: YogaNodeRuntime;
|
|
15
|
+
constructor(options?: YogaOption);
|
|
16
|
+
get yogaNode(): import("yoga-layout").Node;
|
|
17
|
+
get left(): number;
|
|
18
|
+
get top(): number;
|
|
19
|
+
get width(): number;
|
|
20
|
+
get height(): number;
|
|
21
|
+
get overflow(): "visible" | "hidden";
|
|
22
|
+
setOptions(options?: YogaOptionPatch): this;
|
|
23
|
+
layout(): this;
|
|
24
|
+
/** @internal RootFrameRuntime 采用一次 Yoga 最终 frame。 */
|
|
25
|
+
commitYogaFrame(frame: Readonly<Rect>): boolean;
|
|
26
|
+
protected onParentChanged(): void;
|
|
27
|
+
protected onMount(): void;
|
|
28
|
+
protected onActivate(root: Root | null, layer: Layer | null): void;
|
|
29
|
+
protected onDeactivate(root: Root | null, layer: Layer | null): void;
|
|
30
|
+
protected onChildrenChanged(): void;
|
|
31
|
+
protected onPropertiesChanged(current: DivProperties, changes: PropertyChanges): void;
|
|
32
|
+
protected onUnmount(): void;
|
|
33
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { Div } from "./div";
|
|
2
|
+
export type { YogaOption, YogaOptionPatch } from "./div";
|
|
3
|
+
export { Span } from "./span";
|
|
4
|
+
export type { SpanOption, SpanOptionPatch } from "./span";
|
|
5
|
+
export { TextRow } from "./text-row";
|
|
6
|
+
export type { TextRowOption, TextRowOptionPatch } from "./text-row";
|
|
7
|
+
export { Align, BoxSizing, Display, Edge, FlexDirection, Gutter, Justify, Overflow, PositionType, Wrap } from "./div";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
import { Rectangle, RectangleProperties, RectangleTransform, Text, TextProperties, TextTransform } from "@fulate/ui";
|
|
2
|
+
import Yoga, { Align, Align as Align$1, BoxSizing, BoxSizing as BoxSizing$1, Display, Display as Display$1, Edge, Edge as Edge$1, FlexDirection, FlexDirection as FlexDirection$1, Gutter, Gutter as Gutter$1, Justify, Justify as Justify$1, MeasureMode, Overflow, Overflow as Overflow$1, PositionType, PositionType as PositionType$1, Wrap, Wrap as Wrap$1 } from "yoga-layout";
|
|
3
|
+
import { registerElement } from "@fulate/vue";
|
|
4
|
+
//#region packages/yoga/src/style.ts
|
|
5
|
+
var YOGA_STYLE_KEYS = [
|
|
6
|
+
"display",
|
|
7
|
+
"left",
|
|
8
|
+
"top",
|
|
9
|
+
"bottom",
|
|
10
|
+
"right",
|
|
11
|
+
"width",
|
|
12
|
+
"height",
|
|
13
|
+
"minWidth",
|
|
14
|
+
"minHeight",
|
|
15
|
+
"maxWidth",
|
|
16
|
+
"maxHeight",
|
|
17
|
+
"alignContent",
|
|
18
|
+
"alignItems",
|
|
19
|
+
"alignSelf",
|
|
20
|
+
"aspectRatio",
|
|
21
|
+
"flex",
|
|
22
|
+
"flexBasis",
|
|
23
|
+
"flexDirection",
|
|
24
|
+
"flexGrow",
|
|
25
|
+
"flexShrink",
|
|
26
|
+
"flexWrap",
|
|
27
|
+
"justifyContent",
|
|
28
|
+
"overflow",
|
|
29
|
+
"paddingTop",
|
|
30
|
+
"paddingLeft",
|
|
31
|
+
"paddingRight",
|
|
32
|
+
"paddingBottom",
|
|
33
|
+
"padding",
|
|
34
|
+
"marginTop",
|
|
35
|
+
"marginLeft",
|
|
36
|
+
"marginRight",
|
|
37
|
+
"marginBottom",
|
|
38
|
+
"margin",
|
|
39
|
+
"position",
|
|
40
|
+
"gap",
|
|
41
|
+
"inset",
|
|
42
|
+
"boxSizing"
|
|
43
|
+
];
|
|
44
|
+
var YOGA_STYLE_KEY_SET = new Set(YOGA_STYLE_KEYS);
|
|
45
|
+
var RESOLVED_FRAME_KEYS = new Set([
|
|
46
|
+
"left",
|
|
47
|
+
"top",
|
|
48
|
+
"width",
|
|
49
|
+
"height"
|
|
50
|
+
]);
|
|
51
|
+
var NO_PROPERTY_CHANGES = /* @__PURE__ */ new Map();
|
|
52
|
+
/** 把 Yoga authored fields 安装到元素唯一 Properties,不建立旁路 style state。 */
|
|
53
|
+
function initializeYogaStyleProperties(properties, options) {
|
|
54
|
+
properties.left = options.left;
|
|
55
|
+
properties.top = options.top;
|
|
56
|
+
for (const key of YOGA_STYLE_KEYS) if (!(key in properties)) properties[key] = options[key];
|
|
57
|
+
}
|
|
58
|
+
/** resolved frame fields 只由 Yoga 计算结果驱动 Core transform impact。 */
|
|
59
|
+
function withoutYogaResolvedFrameChanges(changes) {
|
|
60
|
+
let frameChanges = 0;
|
|
61
|
+
for (const key of RESOLVED_FRAME_KEYS) if (changes.has(key)) frameChanges++;
|
|
62
|
+
if (frameChanges === 0) return changes;
|
|
63
|
+
if (frameChanges === changes.size) return NO_PROPERTY_CHANGES;
|
|
64
|
+
return new Map([...changes].filter(([key]) => !RESOLVED_FRAME_KEYS.has(key)));
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region packages/yoga/src/runtime.ts
|
|
68
|
+
var yogaRuntimeKey = Symbol();
|
|
69
|
+
function getYogaRuntime(node) {
|
|
70
|
+
return node?.[yogaRuntimeKey];
|
|
71
|
+
}
|
|
72
|
+
/** 一个 Yoga Element 唯一的 native node、tree relation 与 derived layout owner。 */
|
|
73
|
+
var YogaNodeRuntime = class {
|
|
74
|
+
node = Yoga.Node.create();
|
|
75
|
+
yogaChildren = [];
|
|
76
|
+
ownsDirtiedCallback = false;
|
|
77
|
+
handleDirtied = () => {
|
|
78
|
+
if (!this.host.isActive || !this.isLayoutRoot()) return;
|
|
79
|
+
this.host.activeRoot?.frameRuntime.scheduleLayout(this);
|
|
80
|
+
};
|
|
81
|
+
constructor(host) {
|
|
82
|
+
this.host = host;
|
|
83
|
+
this.applyStyles();
|
|
84
|
+
this.syncChildren();
|
|
85
|
+
}
|
|
86
|
+
get key() {
|
|
87
|
+
return this.host.key;
|
|
88
|
+
}
|
|
89
|
+
get styles() {
|
|
90
|
+
return this.host.properties;
|
|
91
|
+
}
|
|
92
|
+
getParentRuntime() {
|
|
93
|
+
return getYogaRuntime(this.host.parent);
|
|
94
|
+
}
|
|
95
|
+
isLayoutRoot() {
|
|
96
|
+
return !this.getParentRuntime();
|
|
97
|
+
}
|
|
98
|
+
mount() {
|
|
99
|
+
this.syncChildren();
|
|
100
|
+
}
|
|
101
|
+
activate() {
|
|
102
|
+
this.syncChildren();
|
|
103
|
+
this.syncLayoutRootRole();
|
|
104
|
+
}
|
|
105
|
+
deactivate(root) {
|
|
106
|
+
this.unsetDirtiedCallback();
|
|
107
|
+
root?.frameRuntime.forgetLayout(this);
|
|
108
|
+
}
|
|
109
|
+
unmount() {
|
|
110
|
+
this.host.activeRoot?.frameRuntime.forgetLayout(this);
|
|
111
|
+
this.unsetDirtiedCallback();
|
|
112
|
+
this.yogaChildren = [];
|
|
113
|
+
this.node?.free();
|
|
114
|
+
this.node = null;
|
|
115
|
+
}
|
|
116
|
+
parentChanged() {
|
|
117
|
+
this.syncLayoutRootRole();
|
|
118
|
+
}
|
|
119
|
+
childrenChanged() {
|
|
120
|
+
this.syncChildren();
|
|
121
|
+
}
|
|
122
|
+
propertiesChanged(changes) {
|
|
123
|
+
for (const key of changes.keys()) {
|
|
124
|
+
if (!YOGA_STYLE_KEY_SET.has(key)) continue;
|
|
125
|
+
this.applyStyle(key);
|
|
126
|
+
}
|
|
127
|
+
if (changes.has("display") && !changes.has("flexDirection")) this.applyStyle("flexDirection");
|
|
128
|
+
if (changes.has("borderWidth")) this.node?.setBorder(Edge$1.All, this.host.borderWidth);
|
|
129
|
+
}
|
|
130
|
+
markMeasureDirty() {
|
|
131
|
+
this.node?.markDirty();
|
|
132
|
+
}
|
|
133
|
+
layout() {
|
|
134
|
+
this.host.activeRoot?.frameRuntime.drain();
|
|
135
|
+
}
|
|
136
|
+
flushLayout() {
|
|
137
|
+
const node = this.node;
|
|
138
|
+
if (!node || !this.host.isActive || this.host.isUnmounted || !this.isLayoutRoot()) return;
|
|
139
|
+
node.calculateLayout("auto", "auto");
|
|
140
|
+
const affected = [];
|
|
141
|
+
this.commitComputedTree(affected);
|
|
142
|
+
return affected;
|
|
143
|
+
}
|
|
144
|
+
syncLayoutRootRole() {
|
|
145
|
+
const node = this.node;
|
|
146
|
+
const frameRuntime = this.host.activeRoot?.frameRuntime;
|
|
147
|
+
if (node && this.host.isActive && this.isLayoutRoot()) {
|
|
148
|
+
if (this.ownsDirtiedCallback) return;
|
|
149
|
+
node.setDirtiedFunc(this.handleDirtied);
|
|
150
|
+
this.ownsDirtiedCallback = true;
|
|
151
|
+
frameRuntime?.scheduleLayout(this);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this.unsetDirtiedCallback();
|
|
155
|
+
frameRuntime?.forgetLayout(this);
|
|
156
|
+
}
|
|
157
|
+
unsetDirtiedCallback() {
|
|
158
|
+
if (!this.ownsDirtiedCallback) return;
|
|
159
|
+
this.node?.unsetDirtiedFunc();
|
|
160
|
+
this.ownsDirtiedCallback = false;
|
|
161
|
+
}
|
|
162
|
+
commitComputedTree(affected) {
|
|
163
|
+
const node = this.node;
|
|
164
|
+
if (!node || !this.host.isActive || !node.hasNewLayout()) return;
|
|
165
|
+
const frame = node.getComputedLayout();
|
|
166
|
+
node.markLayoutSeen();
|
|
167
|
+
if (this.host.commitYogaFrame(frame)) affected.push(this.host);
|
|
168
|
+
for (const child of this.host.children) getYogaRuntime(child)?.commitComputedTree(affected);
|
|
169
|
+
}
|
|
170
|
+
syncChildren() {
|
|
171
|
+
const node = this.node;
|
|
172
|
+
if (!node) return;
|
|
173
|
+
const children = this.host.children.flatMap((child) => {
|
|
174
|
+
const runtime = getYogaRuntime(child);
|
|
175
|
+
return runtime?.node ? [runtime.node] : [];
|
|
176
|
+
});
|
|
177
|
+
if (this.yogaChildren.length === children.length && children.every((child, index) => this.yogaChildren[index] === child)) return;
|
|
178
|
+
const current = [...this.yogaChildren];
|
|
179
|
+
const next = new Set(children);
|
|
180
|
+
for (let index = current.length - 1; index >= 0; index--) {
|
|
181
|
+
const child = current[index];
|
|
182
|
+
if (next.has(child)) continue;
|
|
183
|
+
node.removeChild(child);
|
|
184
|
+
current.splice(index, 1);
|
|
185
|
+
}
|
|
186
|
+
for (let index = 0; index < children.length; index++) {
|
|
187
|
+
const child = children[index];
|
|
188
|
+
if (current[index] === child) continue;
|
|
189
|
+
const previousIndex = current.indexOf(child, index + 1);
|
|
190
|
+
if (previousIndex !== -1) {
|
|
191
|
+
node.removeChild(child);
|
|
192
|
+
current.splice(previousIndex, 1);
|
|
193
|
+
}
|
|
194
|
+
node.insertChild(child, index);
|
|
195
|
+
current.splice(index, 0, child);
|
|
196
|
+
}
|
|
197
|
+
this.yogaChildren = children;
|
|
198
|
+
}
|
|
199
|
+
applyStyles() {
|
|
200
|
+
for (const key of YOGA_STYLE_KEYS) this.applyStyle(key);
|
|
201
|
+
this.node?.setBorder(Edge$1.All, this.host.borderWidth);
|
|
202
|
+
}
|
|
203
|
+
applyStyle(key) {
|
|
204
|
+
const node = this.node;
|
|
205
|
+
if (!node) return;
|
|
206
|
+
const styles = this.styles;
|
|
207
|
+
switch (key) {
|
|
208
|
+
case "display":
|
|
209
|
+
node.setDisplay(styles.display ?? Display$1.Flex);
|
|
210
|
+
return;
|
|
211
|
+
case "width":
|
|
212
|
+
node.setWidth(styles.width);
|
|
213
|
+
return;
|
|
214
|
+
case "height":
|
|
215
|
+
node.setHeight(styles.height);
|
|
216
|
+
return;
|
|
217
|
+
case "minWidth":
|
|
218
|
+
node.setMinWidth(styles.minWidth);
|
|
219
|
+
return;
|
|
220
|
+
case "minHeight":
|
|
221
|
+
node.setMinHeight(styles.minHeight);
|
|
222
|
+
return;
|
|
223
|
+
case "maxWidth":
|
|
224
|
+
node.setMaxWidth(styles.maxWidth);
|
|
225
|
+
return;
|
|
226
|
+
case "maxHeight":
|
|
227
|
+
node.setMaxHeight(styles.maxHeight);
|
|
228
|
+
return;
|
|
229
|
+
case "justifyContent":
|
|
230
|
+
node.setJustifyContent(styles.justifyContent ?? Justify$1.FlexStart);
|
|
231
|
+
return;
|
|
232
|
+
case "alignContent":
|
|
233
|
+
node.setAlignContent(styles.alignContent ?? Align$1.Stretch);
|
|
234
|
+
return;
|
|
235
|
+
case "alignItems":
|
|
236
|
+
node.setAlignItems(styles.alignItems ?? Align$1.Stretch);
|
|
237
|
+
return;
|
|
238
|
+
case "alignSelf":
|
|
239
|
+
node.setAlignSelf(styles.alignSelf ?? Align$1.Auto);
|
|
240
|
+
return;
|
|
241
|
+
case "aspectRatio":
|
|
242
|
+
node.setAspectRatio(styles.aspectRatio);
|
|
243
|
+
return;
|
|
244
|
+
case "flex":
|
|
245
|
+
node.setFlex(styles.flex);
|
|
246
|
+
return;
|
|
247
|
+
case "flexBasis":
|
|
248
|
+
node.setFlexBasis(styles.flexBasis);
|
|
249
|
+
return;
|
|
250
|
+
case "flexDirection":
|
|
251
|
+
node.setFlexDirection(styles.flexDirection ?? (styles.display === Display$1.Flex ? FlexDirection$1.Row : FlexDirection$1.Column));
|
|
252
|
+
return;
|
|
253
|
+
case "flexGrow":
|
|
254
|
+
node.setFlexGrow(styles.flexGrow);
|
|
255
|
+
return;
|
|
256
|
+
case "flexShrink":
|
|
257
|
+
node.setFlexShrink(styles.flexShrink ?? 1);
|
|
258
|
+
return;
|
|
259
|
+
case "flexWrap":
|
|
260
|
+
node.setFlexWrap(styles.flexWrap ?? Wrap$1.NoWrap);
|
|
261
|
+
return;
|
|
262
|
+
case "overflow":
|
|
263
|
+
node.setOverflow(styles.overflow === "hidden" ? Overflow$1.Hidden : styles.overflow === "visible" ? Overflow$1.Visible : styles.overflow ?? Overflow$1.Visible);
|
|
264
|
+
return;
|
|
265
|
+
case "gap":
|
|
266
|
+
node.setGap(Gutter$1.All, styles.gap);
|
|
267
|
+
return;
|
|
268
|
+
case "padding":
|
|
269
|
+
node.setPadding(Edge$1.All, styles.padding);
|
|
270
|
+
return;
|
|
271
|
+
case "paddingLeft":
|
|
272
|
+
node.setPadding(Edge$1.Left, styles.paddingLeft);
|
|
273
|
+
return;
|
|
274
|
+
case "paddingTop":
|
|
275
|
+
node.setPadding(Edge$1.Top, styles.paddingTop);
|
|
276
|
+
return;
|
|
277
|
+
case "paddingRight":
|
|
278
|
+
node.setPadding(Edge$1.Right, styles.paddingRight);
|
|
279
|
+
return;
|
|
280
|
+
case "paddingBottom":
|
|
281
|
+
node.setPadding(Edge$1.Bottom, styles.paddingBottom);
|
|
282
|
+
return;
|
|
283
|
+
case "margin":
|
|
284
|
+
node.setMargin(Edge$1.All, styles.margin);
|
|
285
|
+
return;
|
|
286
|
+
case "marginLeft":
|
|
287
|
+
node.setMargin(Edge$1.Left, styles.marginLeft);
|
|
288
|
+
return;
|
|
289
|
+
case "marginTop":
|
|
290
|
+
node.setMargin(Edge$1.Top, styles.marginTop);
|
|
291
|
+
return;
|
|
292
|
+
case "marginRight":
|
|
293
|
+
node.setMargin(Edge$1.Right, styles.marginRight);
|
|
294
|
+
return;
|
|
295
|
+
case "marginBottom":
|
|
296
|
+
node.setMargin(Edge$1.Bottom, styles.marginBottom);
|
|
297
|
+
return;
|
|
298
|
+
case "position":
|
|
299
|
+
node.setPositionType(styles.position ?? PositionType$1.Relative);
|
|
300
|
+
return;
|
|
301
|
+
case "inset":
|
|
302
|
+
node.setPosition(Edge$1.All, styles.inset);
|
|
303
|
+
return;
|
|
304
|
+
case "left":
|
|
305
|
+
node.setPosition(Edge$1.Left, styles.left);
|
|
306
|
+
return;
|
|
307
|
+
case "top":
|
|
308
|
+
node.setPosition(Edge$1.Top, styles.top);
|
|
309
|
+
return;
|
|
310
|
+
case "bottom":
|
|
311
|
+
node.setPosition(Edge$1.Bottom, styles.bottom);
|
|
312
|
+
return;
|
|
313
|
+
case "right":
|
|
314
|
+
node.setPosition(Edge$1.Right, styles.right);
|
|
315
|
+
return;
|
|
316
|
+
case "boxSizing": node.setBoxSizing(styles.boxSizing ?? BoxSizing$1.BorderBox);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
//#endregion
|
|
321
|
+
//#region packages/yoga/src/div.ts
|
|
322
|
+
var DivProperties = class extends RectangleProperties {
|
|
323
|
+
constructor(options = {}) {
|
|
324
|
+
super(options);
|
|
325
|
+
initializeYogaStyleProperties(this, options);
|
|
326
|
+
}
|
|
327
|
+
getImpact(changes) {
|
|
328
|
+
return super.getImpact(withoutYogaResolvedFrameChanges(changes));
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
var DivTransform = class extends RectangleTransform {
|
|
332
|
+
resolveFitSize() {}
|
|
333
|
+
};
|
|
334
|
+
/** Rectangle paint surface whose authored frame and flex style are owned by Yoga. */
|
|
335
|
+
var Div = class extends Rectangle {
|
|
336
|
+
[yogaRuntimeKey];
|
|
337
|
+
constructor(options = {}) {
|
|
338
|
+
super(options, new DivProperties(options), DivTransform);
|
|
339
|
+
this[yogaRuntimeKey] = new YogaNodeRuntime(this);
|
|
340
|
+
}
|
|
341
|
+
get yogaNode() {
|
|
342
|
+
return this[yogaRuntimeKey].node;
|
|
343
|
+
}
|
|
344
|
+
get left() {
|
|
345
|
+
return this.transform.resolvedLeft ?? 0;
|
|
346
|
+
}
|
|
347
|
+
get top() {
|
|
348
|
+
return this.transform.resolvedTop ?? 0;
|
|
349
|
+
}
|
|
350
|
+
get width() {
|
|
351
|
+
return this.transform.resolvedWidth;
|
|
352
|
+
}
|
|
353
|
+
get height() {
|
|
354
|
+
return this.transform.resolvedHeight;
|
|
355
|
+
}
|
|
356
|
+
get overflow() {
|
|
357
|
+
const value = this.properties.overflow;
|
|
358
|
+
return value === void 0 || value === "visible" || value === Overflow$1.Visible ? "visible" : "hidden";
|
|
359
|
+
}
|
|
360
|
+
setOptions(options) {
|
|
361
|
+
return super.setOptions(options);
|
|
362
|
+
}
|
|
363
|
+
layout() {
|
|
364
|
+
this[yogaRuntimeKey].layout();
|
|
365
|
+
return this;
|
|
366
|
+
}
|
|
367
|
+
/** @internal RootFrameRuntime 采用一次 Yoga 最终 frame。 */
|
|
368
|
+
commitYogaFrame(frame) {
|
|
369
|
+
return this.transform.commitResolvedFrame(frame);
|
|
370
|
+
}
|
|
371
|
+
onParentChanged() {
|
|
372
|
+
super.onParentChanged();
|
|
373
|
+
this[yogaRuntimeKey].parentChanged();
|
|
374
|
+
}
|
|
375
|
+
onMount() {
|
|
376
|
+
super.onMount();
|
|
377
|
+
this[yogaRuntimeKey].mount();
|
|
378
|
+
}
|
|
379
|
+
onActivate(root, layer) {
|
|
380
|
+
super.onActivate(root, layer);
|
|
381
|
+
this[yogaRuntimeKey].activate();
|
|
382
|
+
}
|
|
383
|
+
onDeactivate(root, layer) {
|
|
384
|
+
this[yogaRuntimeKey].deactivate(root);
|
|
385
|
+
super.onDeactivate(root, layer);
|
|
386
|
+
}
|
|
387
|
+
onChildrenChanged() {
|
|
388
|
+
super.onChildrenChanged();
|
|
389
|
+
this[yogaRuntimeKey].childrenChanged();
|
|
390
|
+
}
|
|
391
|
+
onPropertiesChanged(current, changes) {
|
|
392
|
+
super.onPropertiesChanged(current, changes);
|
|
393
|
+
this[yogaRuntimeKey].propertiesChanged(changes);
|
|
394
|
+
}
|
|
395
|
+
onUnmount() {
|
|
396
|
+
this[yogaRuntimeKey].unmount();
|
|
397
|
+
super.onUnmount();
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
//#endregion
|
|
401
|
+
//#region packages/yoga/src/text-row/contract.ts
|
|
402
|
+
var textRowParticipantKey = Symbol();
|
|
403
|
+
var textRowProviderKey = Symbol();
|
|
404
|
+
function getTextRowParticipant(node) {
|
|
405
|
+
return node[textRowParticipantKey];
|
|
406
|
+
}
|
|
407
|
+
function getTextRowProvider(node) {
|
|
408
|
+
return node?.[textRowProviderKey];
|
|
409
|
+
}
|
|
410
|
+
//#endregion
|
|
411
|
+
//#region packages/yoga/src/span.ts
|
|
412
|
+
var TEXT_MEASURE_KEYS = new Set([
|
|
413
|
+
"text",
|
|
414
|
+
"fontSize",
|
|
415
|
+
"fontFamily",
|
|
416
|
+
"fontWeight",
|
|
417
|
+
"fontStyle",
|
|
418
|
+
"lineHeight",
|
|
419
|
+
"wordWrap",
|
|
420
|
+
"ellipsis",
|
|
421
|
+
"letterSpacing",
|
|
422
|
+
"maxLines"
|
|
423
|
+
]);
|
|
424
|
+
var SpanProperties = class extends TextProperties {
|
|
425
|
+
constructor(options = {}) {
|
|
426
|
+
super(options);
|
|
427
|
+
initializeYogaStyleProperties(this, options);
|
|
428
|
+
}
|
|
429
|
+
getImpact(changes) {
|
|
430
|
+
return super.getImpact(withoutYogaResolvedFrameChanges(changes));
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
var SpanTransform = class extends TextTransform {
|
|
434
|
+
resolveFitSize() {}
|
|
435
|
+
};
|
|
436
|
+
/** Text paint surface measured and framed by its owning Yoga tree. */
|
|
437
|
+
var Span = class extends Text {
|
|
438
|
+
[yogaRuntimeKey];
|
|
439
|
+
[textRowParticipantKey] = this;
|
|
440
|
+
measureYogaText = (width, widthMode, height, heightMode) => this.measureText(width, widthMode, height, heightMode);
|
|
441
|
+
constructor(options = {}) {
|
|
442
|
+
const prepared = {
|
|
443
|
+
textAlign: "left",
|
|
444
|
+
verticalAlign: "top",
|
|
445
|
+
...options
|
|
446
|
+
};
|
|
447
|
+
super(prepared, new SpanProperties(prepared), SpanTransform);
|
|
448
|
+
this[yogaRuntimeKey] = new YogaNodeRuntime(this);
|
|
449
|
+
this.yogaNode?.setMeasureFunc(this.measureYogaText);
|
|
450
|
+
}
|
|
451
|
+
get yogaNode() {
|
|
452
|
+
return this[yogaRuntimeKey].node;
|
|
453
|
+
}
|
|
454
|
+
get left() {
|
|
455
|
+
return this.transform.resolvedLeft ?? 0;
|
|
456
|
+
}
|
|
457
|
+
get top() {
|
|
458
|
+
return this.transform.resolvedTop ?? 0;
|
|
459
|
+
}
|
|
460
|
+
get width() {
|
|
461
|
+
return this.transform.resolvedWidth;
|
|
462
|
+
}
|
|
463
|
+
get height() {
|
|
464
|
+
return this.transform.resolvedHeight;
|
|
465
|
+
}
|
|
466
|
+
get overflow() {
|
|
467
|
+
const value = this.properties.overflow;
|
|
468
|
+
return value === void 0 || value === "visible" || value === Overflow$1.Visible ? "visible" : "hidden";
|
|
469
|
+
}
|
|
470
|
+
setOptions(options) {
|
|
471
|
+
return super.setOptions(options);
|
|
472
|
+
}
|
|
473
|
+
layout() {
|
|
474
|
+
this[yogaRuntimeKey].layout();
|
|
475
|
+
return this;
|
|
476
|
+
}
|
|
477
|
+
getTextRowLineMetrics() {
|
|
478
|
+
return this.getTextLineBoxMetrics();
|
|
479
|
+
}
|
|
480
|
+
textRowMetricsChanged() {
|
|
481
|
+
this[yogaRuntimeKey].markMeasureDirty();
|
|
482
|
+
this.transform.markGeometryNeedsLayout(false);
|
|
483
|
+
}
|
|
484
|
+
getVisibleTextBounds() {
|
|
485
|
+
const bounds = super.getVisibleTextBounds();
|
|
486
|
+
if (!bounds) return null;
|
|
487
|
+
const offsetY = this.getTextRowPaintOffset();
|
|
488
|
+
return offsetY === 0 ? bounds : {
|
|
489
|
+
...bounds,
|
|
490
|
+
top: bounds.top + offsetY
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
/** @internal RootFrameRuntime 采用一次 Yoga 最终 frame 后同步唯一 TextBlock。 */
|
|
494
|
+
commitYogaFrame(frame) {
|
|
495
|
+
const changed = this.transform.commitResolvedFrame(frame);
|
|
496
|
+
this.syncTextLayout();
|
|
497
|
+
return changed;
|
|
498
|
+
}
|
|
499
|
+
onParentChanged() {
|
|
500
|
+
super.onParentChanged();
|
|
501
|
+
this[yogaRuntimeKey].parentChanged();
|
|
502
|
+
}
|
|
503
|
+
commitTextLayoutSize(_width, _height) {}
|
|
504
|
+
getLayoutWidth() {
|
|
505
|
+
return Math.max(this.width ?? 0, 0);
|
|
506
|
+
}
|
|
507
|
+
getResolvedHeight() {
|
|
508
|
+
return this.height ?? 0;
|
|
509
|
+
}
|
|
510
|
+
onMount() {
|
|
511
|
+
super.onMount();
|
|
512
|
+
this[yogaRuntimeKey].mount();
|
|
513
|
+
}
|
|
514
|
+
onActivate(root, layer) {
|
|
515
|
+
super.onActivate(root, layer);
|
|
516
|
+
this[yogaRuntimeKey].activate();
|
|
517
|
+
getTextRowProvider(this.parent)?.syncTextRowMetrics();
|
|
518
|
+
}
|
|
519
|
+
onDeactivate(root, layer) {
|
|
520
|
+
this[yogaRuntimeKey].deactivate(root);
|
|
521
|
+
super.onDeactivate(root, layer);
|
|
522
|
+
}
|
|
523
|
+
onChildrenChanged() {
|
|
524
|
+
super.onChildrenChanged();
|
|
525
|
+
this[yogaRuntimeKey].childrenChanged();
|
|
526
|
+
}
|
|
527
|
+
onPropertiesChanged(current, changes) {
|
|
528
|
+
super.onPropertiesChanged(current, changes);
|
|
529
|
+
this[yogaRuntimeKey].propertiesChanged(changes);
|
|
530
|
+
let textRowMetricsChanged = changes.has("textBaseline");
|
|
531
|
+
for (const key of changes.keys()) {
|
|
532
|
+
if (!TEXT_MEASURE_KEYS.has(key)) continue;
|
|
533
|
+
this[yogaRuntimeKey].markMeasureDirty();
|
|
534
|
+
textRowMetricsChanged = true;
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
if (textRowMetricsChanged) getTextRowProvider(this.parent)?.syncTextRowMetrics();
|
|
538
|
+
}
|
|
539
|
+
onTextLayoutInvalidated() {
|
|
540
|
+
if (this.isActive && this.yogaNode) this[yogaRuntimeKey].markMeasureDirty();
|
|
541
|
+
getTextRowProvider(this.parent)?.syncTextRowMetrics();
|
|
542
|
+
}
|
|
543
|
+
paintContent(ctx) {
|
|
544
|
+
this.paintTextContent(ctx, this.getTextRowPaintOffset());
|
|
545
|
+
}
|
|
546
|
+
onUnmount() {
|
|
547
|
+
this[yogaRuntimeKey].unmount();
|
|
548
|
+
super.onUnmount();
|
|
549
|
+
}
|
|
550
|
+
measureText(constraintWidth, widthMode, constraintHeight, heightMode) {
|
|
551
|
+
const style = this.getResolvedTextStyle();
|
|
552
|
+
const lineHeightPx = style.fontSize * style.lineHeight;
|
|
553
|
+
const textRowHeight = getTextRowProvider(this.parent)?.textRowContentHeight ?? 0;
|
|
554
|
+
if (!this.text) return {
|
|
555
|
+
width: 0,
|
|
556
|
+
height: Math.max(lineHeightPx, textRowHeight)
|
|
557
|
+
};
|
|
558
|
+
const wrapWidth = widthMode === MeasureMode.Undefined ? Infinity : constraintWidth;
|
|
559
|
+
const metrics = this.measureTextLayout(wrapWidth, heightMode === MeasureMode.Undefined ? void 0 : constraintHeight);
|
|
560
|
+
const measuredHeight = Math.max(Math.max(metrics.lineCount, 1) * lineHeightPx, textRowHeight);
|
|
561
|
+
if (widthMode === MeasureMode.Exactly) return {
|
|
562
|
+
width: constraintWidth,
|
|
563
|
+
height: measuredHeight
|
|
564
|
+
};
|
|
565
|
+
return {
|
|
566
|
+
width: widthMode === MeasureMode.AtMost ? Math.min(metrics.maxLineWidth, constraintWidth) : metrics.maxLineWidth,
|
|
567
|
+
height: measuredHeight
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
getTextRowPaintOffset() {
|
|
571
|
+
const provider = getTextRowProvider(this.parent);
|
|
572
|
+
if (!provider) return 0;
|
|
573
|
+
const baseline = this.getTextBlockFirstAlphabeticBaseline();
|
|
574
|
+
return provider.getTextRowAlphabeticBaseline() - this.top - baseline;
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
//#endregion
|
|
578
|
+
//#region packages/yoga/src/text-row/index.ts
|
|
579
|
+
/** A Yoga row whose direct text participants share one painted baseline. */
|
|
580
|
+
var TextRow = class extends Div {
|
|
581
|
+
[textRowParticipantKey] = this;
|
|
582
|
+
[textRowProviderKey] = this;
|
|
583
|
+
alphabeticBaseline = 0;
|
|
584
|
+
intrinsicContentHeight = 0;
|
|
585
|
+
constructor(options = {}) {
|
|
586
|
+
super({
|
|
587
|
+
...options,
|
|
588
|
+
alignItems: Align$1.FlexStart,
|
|
589
|
+
flexDirection: FlexDirection$1.Row
|
|
590
|
+
});
|
|
591
|
+
this.syncTextRowMetrics();
|
|
592
|
+
}
|
|
593
|
+
setOptions(options) {
|
|
594
|
+
return super.setOptions(options);
|
|
595
|
+
}
|
|
596
|
+
getTextRowLineMetrics() {
|
|
597
|
+
return {
|
|
598
|
+
alphabeticBaseline: this.alphabeticBaseline,
|
|
599
|
+
height: this.intrinsicContentHeight
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
get textRowContentHeight() {
|
|
603
|
+
return Math.max(this.intrinsicContentHeight, getTextRowProvider(this.parent)?.textRowContentHeight ?? 0);
|
|
604
|
+
}
|
|
605
|
+
getTextRowAlphabeticBaseline() {
|
|
606
|
+
const parent = getTextRowProvider(this.parent);
|
|
607
|
+
if (parent) return parent.getTextRowAlphabeticBaseline() - this.top;
|
|
608
|
+
return (this.yogaNode?.getComputedBorder(Edge$1.Top) ?? 0) + (this.yogaNode?.getComputedPadding(Edge$1.Top) ?? 0) + this.alphabeticBaseline;
|
|
609
|
+
}
|
|
610
|
+
syncTextRowMetrics() {
|
|
611
|
+
let alphabeticBaseline = 0;
|
|
612
|
+
let belowBaseline = 0;
|
|
613
|
+
for (const child of this.children) {
|
|
614
|
+
const participant = getTextRowParticipant(child);
|
|
615
|
+
if (!participant) continue;
|
|
616
|
+
const metrics = participant.getTextRowLineMetrics();
|
|
617
|
+
alphabeticBaseline = Math.max(alphabeticBaseline, metrics.alphabeticBaseline);
|
|
618
|
+
belowBaseline = Math.max(belowBaseline, metrics.height - metrics.alphabeticBaseline);
|
|
619
|
+
}
|
|
620
|
+
const contentHeight = alphabeticBaseline + belowBaseline;
|
|
621
|
+
if (alphabeticBaseline === this.alphabeticBaseline && contentHeight === this.intrinsicContentHeight) return;
|
|
622
|
+
this.alphabeticBaseline = alphabeticBaseline;
|
|
623
|
+
this.intrinsicContentHeight = contentHeight;
|
|
624
|
+
for (const child of this.children) getTextRowParticipant(child)?.textRowMetricsChanged();
|
|
625
|
+
getTextRowProvider(this.parent)?.syncTextRowMetrics();
|
|
626
|
+
}
|
|
627
|
+
textRowMetricsChanged() {
|
|
628
|
+
for (const child of this.children) getTextRowParticipant(child)?.textRowMetricsChanged();
|
|
629
|
+
this.transform.markGeometryNeedsLayout(false);
|
|
630
|
+
}
|
|
631
|
+
onChildrenChanged() {
|
|
632
|
+
super.onChildrenChanged();
|
|
633
|
+
this.syncTextRowMetrics();
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
//#endregion
|
|
637
|
+
//#region packages/yoga/src/index.ts
|
|
638
|
+
registerElement("f-div", Div);
|
|
639
|
+
registerElement("f-span", Span);
|
|
640
|
+
registerElement("f-text-row", TextRow);
|
|
641
|
+
//#endregion
|
|
642
|
+
export { Align, BoxSizing, Display, Div, Edge, FlexDirection, Gutter, Justify, Overflow, PositionType, Span, TextRow, Wrap };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Element, PropertyChanges, Root, RootFrameLayoutOwner } from "@fulate/core";
|
|
2
|
+
import type { Rect } from "@fulate/share";
|
|
3
|
+
import Yoga from "yoga-layout";
|
|
4
|
+
export type YogaNode = ReturnType<typeof Yoga.Node.create>;
|
|
5
|
+
export declare const yogaRuntimeKey: unique symbol;
|
|
6
|
+
export interface YogaElementHost extends Element {
|
|
7
|
+
readonly [yogaRuntimeKey]: YogaNodeRuntime;
|
|
8
|
+
readonly borderWidth: number;
|
|
9
|
+
commitYogaFrame(frame: Readonly<Rect>): boolean;
|
|
10
|
+
}
|
|
11
|
+
/** 一个 Yoga Element 唯一的 native node、tree relation 与 derived layout owner。 */
|
|
12
|
+
export declare class YogaNodeRuntime implements RootFrameLayoutOwner {
|
|
13
|
+
private readonly host;
|
|
14
|
+
node: YogaNode | null;
|
|
15
|
+
private yogaChildren;
|
|
16
|
+
private ownsDirtiedCallback;
|
|
17
|
+
private readonly handleDirtied;
|
|
18
|
+
constructor(host: YogaElementHost);
|
|
19
|
+
get key(): string;
|
|
20
|
+
private get styles();
|
|
21
|
+
private getParentRuntime;
|
|
22
|
+
private isLayoutRoot;
|
|
23
|
+
mount(): void;
|
|
24
|
+
activate(): void;
|
|
25
|
+
deactivate(root: Root | null): void;
|
|
26
|
+
unmount(): void;
|
|
27
|
+
parentChanged(): void;
|
|
28
|
+
childrenChanged(): void;
|
|
29
|
+
propertiesChanged(changes: PropertyChanges): void;
|
|
30
|
+
markMeasureDirty(): void;
|
|
31
|
+
layout(): void;
|
|
32
|
+
flushLayout(): Element<import("@fulate/core").ElementProperties, import("@fulate/core").BaseElementOption<any>, Partial<Omit<import("@fulate/core").BaseElementOption<any>, "children" | "key">>>[];
|
|
33
|
+
private syncLayoutRootRole;
|
|
34
|
+
private unsetDirtiedCallback;
|
|
35
|
+
private commitComputedTree;
|
|
36
|
+
private syncChildren;
|
|
37
|
+
private applyStyles;
|
|
38
|
+
private applyStyle;
|
|
39
|
+
}
|
package/dist/span.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Layer, PropertyChanges, Root } from "@fulate/core";
|
|
2
|
+
import type { Rect } from "@fulate/share";
|
|
3
|
+
import { Text, TextProperties, type TextLineBoxMetrics } from "@fulate/ui";
|
|
4
|
+
import { type SpanOption, type SpanOptionPatch } from "./style";
|
|
5
|
+
import { YogaNodeRuntime, yogaRuntimeKey, type YogaElementHost } from "./runtime";
|
|
6
|
+
import { textRowParticipantKey, type TextRowParticipant } from "./text-row/contract";
|
|
7
|
+
declare class SpanProperties extends TextProperties {
|
|
8
|
+
constructor(options?: SpanOption);
|
|
9
|
+
getImpact(changes: PropertyChanges): import("@fulate/core").ChangeImpact;
|
|
10
|
+
}
|
|
11
|
+
/** Text paint surface measured and framed by its owning Yoga tree. */
|
|
12
|
+
export declare class Span extends Text<SpanProperties, any> implements TextRowParticipant, YogaElementHost {
|
|
13
|
+
readonly [yogaRuntimeKey]: YogaNodeRuntime;
|
|
14
|
+
readonly [textRowParticipantKey]: this;
|
|
15
|
+
private readonly measureYogaText;
|
|
16
|
+
constructor(options?: SpanOption);
|
|
17
|
+
get yogaNode(): import("yoga-layout").Node;
|
|
18
|
+
get left(): number;
|
|
19
|
+
get top(): number;
|
|
20
|
+
get width(): number;
|
|
21
|
+
get height(): number;
|
|
22
|
+
get overflow(): "visible" | "hidden";
|
|
23
|
+
setOptions(options?: SpanOptionPatch): this;
|
|
24
|
+
layout(): this;
|
|
25
|
+
getTextRowLineMetrics(): TextLineBoxMetrics;
|
|
26
|
+
textRowMetricsChanged(): void;
|
|
27
|
+
getVisibleTextBounds(): Rect;
|
|
28
|
+
/** @internal RootFrameRuntime 采用一次 Yoga 最终 frame 后同步唯一 TextBlock。 */
|
|
29
|
+
commitYogaFrame(frame: Readonly<Rect>): boolean;
|
|
30
|
+
protected onParentChanged(): void;
|
|
31
|
+
protected commitTextLayoutSize(_width: number | undefined, _height: number | undefined): void;
|
|
32
|
+
protected getLayoutWidth(): number;
|
|
33
|
+
protected getResolvedHeight(): number;
|
|
34
|
+
protected onMount(): void;
|
|
35
|
+
protected onActivate(root: Root | null, layer: Layer | null): void;
|
|
36
|
+
protected onDeactivate(root: Root | null, layer: Layer | null): void;
|
|
37
|
+
protected onChildrenChanged(): void;
|
|
38
|
+
protected onPropertiesChanged(current: SpanProperties, changes: PropertyChanges): void;
|
|
39
|
+
protected onTextLayoutInvalidated(): void;
|
|
40
|
+
protected paintContent(ctx: CanvasRenderingContext2D): void;
|
|
41
|
+
protected onUnmount(): void;
|
|
42
|
+
private measureText;
|
|
43
|
+
private getTextRowPaintOffset;
|
|
44
|
+
}
|
|
45
|
+
export type { SpanOption, SpanOptionPatch } from "./style";
|
package/dist/style.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { PropertyChanges } from "@fulate/core";
|
|
2
|
+
import type { RectangleOption, TextOption } from "@fulate/ui";
|
|
3
|
+
import type { Align, BoxSizing, Display, FlexDirection, Justify, Overflow, PositionType, Wrap } from "yoga-layout";
|
|
4
|
+
type YogaStyleSize = number | `${number}%`;
|
|
5
|
+
type YogaStyleSizeAndAuto = YogaStyleSize | "auto";
|
|
6
|
+
export interface YogaStyleOptions {
|
|
7
|
+
display?: Display;
|
|
8
|
+
width?: YogaStyleSizeAndAuto;
|
|
9
|
+
height?: YogaStyleSizeAndAuto;
|
|
10
|
+
minWidth?: YogaStyleSize;
|
|
11
|
+
minHeight?: YogaStyleSize;
|
|
12
|
+
maxWidth?: YogaStyleSize;
|
|
13
|
+
maxHeight?: YogaStyleSize;
|
|
14
|
+
alignContent?: Align;
|
|
15
|
+
alignItems?: Align;
|
|
16
|
+
alignSelf?: Align;
|
|
17
|
+
aspectRatio?: number;
|
|
18
|
+
flex?: number;
|
|
19
|
+
flexBasis?: YogaStyleSizeAndAuto;
|
|
20
|
+
flexDirection?: FlexDirection;
|
|
21
|
+
flexGrow?: number;
|
|
22
|
+
flexShrink?: number;
|
|
23
|
+
flexWrap?: Wrap;
|
|
24
|
+
justifyContent?: Justify;
|
|
25
|
+
overflow?: Overflow | "visible" | "hidden";
|
|
26
|
+
paddingTop?: YogaStyleSize;
|
|
27
|
+
paddingLeft?: YogaStyleSize;
|
|
28
|
+
paddingRight?: YogaStyleSize;
|
|
29
|
+
paddingBottom?: YogaStyleSize;
|
|
30
|
+
padding?: YogaStyleSize;
|
|
31
|
+
marginTop?: YogaStyleSizeAndAuto;
|
|
32
|
+
marginLeft?: YogaStyleSizeAndAuto;
|
|
33
|
+
marginRight?: YogaStyleSizeAndAuto;
|
|
34
|
+
marginBottom?: YogaStyleSizeAndAuto;
|
|
35
|
+
margin?: YogaStyleSizeAndAuto;
|
|
36
|
+
position?: PositionType;
|
|
37
|
+
left?: YogaStyleSize;
|
|
38
|
+
top?: YogaStyleSize;
|
|
39
|
+
bottom?: YogaStyleSize;
|
|
40
|
+
right?: YogaStyleSize;
|
|
41
|
+
gap?: YogaStyleSize;
|
|
42
|
+
inset?: YogaStyleSize;
|
|
43
|
+
boxSizing?: BoxSizing;
|
|
44
|
+
}
|
|
45
|
+
type YogaOwnedBaseKeys = keyof YogaStyleOptions | "fitWidth" | "fitHeight";
|
|
46
|
+
/** Div 的唯一 authored creation state。 */
|
|
47
|
+
export type YogaOption = Omit<RectangleOption<any>, YogaOwnedBaseKeys> & YogaStyleOptions;
|
|
48
|
+
/** Span 的唯一 authored creation state。 */
|
|
49
|
+
export type SpanOption = Omit<TextOption<any>, YogaOwnedBaseKeys> & YogaStyleOptions;
|
|
50
|
+
export type YogaOptionPatch = Partial<Omit<YogaOption, "key" | "children">>;
|
|
51
|
+
export type SpanOptionPatch = Partial<Omit<SpanOption, "key" | "children">>;
|
|
52
|
+
export declare const YOGA_STYLE_KEYS: readonly ["display", "left", "top", "bottom", "right", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "alignContent", "alignItems", "alignSelf", "aspectRatio", "flex", "flexBasis", "flexDirection", "flexGrow", "flexShrink", "flexWrap", "justifyContent", "overflow", "paddingTop", "paddingLeft", "paddingRight", "paddingBottom", "padding", "marginTop", "marginLeft", "marginRight", "marginBottom", "margin", "position", "gap", "inset", "boxSizing"];
|
|
53
|
+
export type YogaStyleKey = typeof YOGA_STYLE_KEYS[number];
|
|
54
|
+
export declare const YOGA_STYLE_KEY_SET: Set<"display" | "width" | "height" | "minWidth" | "minHeight" | "maxWidth" | "maxHeight" | "alignContent" | "alignItems" | "alignSelf" | "aspectRatio" | "flex" | "flexBasis" | "flexDirection" | "flexGrow" | "flexShrink" | "flexWrap" | "justifyContent" | "overflow" | "paddingTop" | "paddingLeft" | "paddingRight" | "paddingBottom" | "padding" | "marginTop" | "marginLeft" | "marginRight" | "marginBottom" | "margin" | "position" | "left" | "top" | "bottom" | "right" | "gap" | "inset" | "boxSizing">;
|
|
55
|
+
/** 把 Yoga authored fields 安装到元素唯一 Properties,不建立旁路 style state。 */
|
|
56
|
+
export declare function initializeYogaStyleProperties(properties: Record<string, any>, options: YogaStyleOptions): void;
|
|
57
|
+
/** resolved frame fields 只由 Yoga 计算结果驱动 Core transform impact。 */
|
|
58
|
+
export declare function withoutYogaResolvedFrameChanges(changes: PropertyChanges): PropertyChanges;
|
|
59
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Node } from "@fulate/core";
|
|
2
|
+
import type { TextLineBoxMetrics } from "@fulate/ui";
|
|
3
|
+
export declare const textRowParticipantKey: unique symbol;
|
|
4
|
+
export declare const textRowProviderKey: unique symbol;
|
|
5
|
+
export interface TextRowParticipant {
|
|
6
|
+
getTextRowLineMetrics(): TextLineBoxMetrics;
|
|
7
|
+
textRowMetricsChanged(): void;
|
|
8
|
+
}
|
|
9
|
+
export interface TextRowProvider {
|
|
10
|
+
readonly textRowContentHeight: number;
|
|
11
|
+
getTextRowAlphabeticBaseline(): number;
|
|
12
|
+
syncTextRowMetrics(): void;
|
|
13
|
+
}
|
|
14
|
+
export declare function getTextRowParticipant(node: Node): TextRowParticipant;
|
|
15
|
+
export declare function getTextRowProvider(node: Node | undefined): TextRowProvider;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Div, type YogaOption } from "../div";
|
|
2
|
+
import { textRowParticipantKey, textRowProviderKey, type TextRowParticipant, type TextRowProvider } from "./contract";
|
|
3
|
+
type TextRowOwnedStyle = "alignItems" | "flexDirection";
|
|
4
|
+
export type TextRowOption = Omit<YogaOption, TextRowOwnedStyle>;
|
|
5
|
+
export type TextRowOptionPatch = Partial<Omit<TextRowOption, "key" | "children">>;
|
|
6
|
+
/** A Yoga row whose direct text participants share one painted baseline. */
|
|
7
|
+
export declare class TextRow extends Div implements TextRowParticipant, TextRowProvider {
|
|
8
|
+
readonly [textRowParticipantKey]: this;
|
|
9
|
+
readonly [textRowProviderKey]: this;
|
|
10
|
+
private alphabeticBaseline;
|
|
11
|
+
private intrinsicContentHeight;
|
|
12
|
+
constructor(options?: TextRowOption);
|
|
13
|
+
setOptions(options?: TextRowOptionPatch): this;
|
|
14
|
+
getTextRowLineMetrics(): {
|
|
15
|
+
alphabeticBaseline: number;
|
|
16
|
+
height: number;
|
|
17
|
+
};
|
|
18
|
+
get textRowContentHeight(): number;
|
|
19
|
+
getTextRowAlphabeticBaseline(): number;
|
|
20
|
+
syncTextRowMetrics(): void;
|
|
21
|
+
textRowMetricsChanged(): void;
|
|
22
|
+
protected onChildrenChanged(): void;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fulate/yoga",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@fulate/core": "*",
|
|
19
|
+
"@fulate/ui": "*",
|
|
20
|
+
"@fulate/vue": "*",
|
|
21
|
+
"yoga-layout": "^3.2.1"
|
|
22
|
+
}
|
|
23
|
+
}
|