@bindtty/layout 0.1.0-alpha.1 → 0.1.0-alpha.3
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 +3 -4
- package/dist/basic-engine.js +3 -122
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/layout-props.d.ts +24 -0
- package/dist/layout-props.js +288 -0
- package/dist/yoga-engine.js +64 -170
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -16,14 +16,13 @@ const layoutTree = layoutRoot(runtime.root, {
|
|
|
16
16
|
|
|
17
17
|
## 已支持的 intrinsic 布局
|
|
18
18
|
|
|
19
|
-
`screen`、`box`、`vstack`、`hstack`、`text`、`spacer`,以及 `fragment` / `show` / `for`
|
|
19
|
+
`screen`、`box`、`vstack`、`hstack`、`text`、`spacer`,以及 `fragment` / `show` / `for` 结构节点。交互控件请使用 `@bindtty/widgets` 的组合实现。
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
高级 layout props(width、height、gap、flex、clip/scroll)见 [doc/specs/YOGA_AND_TEXT.md](../../doc/specs/YOGA_AND_TEXT.md) 与 [doc/packages/LAYOUT.md](../../doc/packages/LAYOUT.md)。默认 engine 为 `YogaLayoutEngine`。
|
|
21
|
+
高级 layout props 见 [doc/specs/LAYOUT_PROPS.md](../../doc/specs/LAYOUT_PROPS.md)(支持矩阵;运行 `npm run gen:layout-props` 同步文档)。默认 engine 为 `YogaLayoutEngine`。
|
|
24
22
|
|
|
25
23
|
## 文档
|
|
26
24
|
|
|
25
|
+
- [doc/specs/LAYOUT_PROPS.md](../../doc/specs/LAYOUT_PROPS.md) — layout prop 支持矩阵
|
|
27
26
|
- [doc/packages/LAYOUT.md](../../doc/packages/LAYOUT.md) — 包落地设计
|
|
28
27
|
- [doc/specs/YOGA_AND_TEXT.md](../../doc/specs/YOGA_AND_TEXT.md) — text + Yoga 摘要
|
|
29
28
|
- [doc/specs/SCROLL_VIEWPORT.md](../../doc/specs/SCROLL_VIEWPORT.md) — clip / scroll
|
package/dist/basic-engine.js
CHANGED
|
@@ -1,81 +1,6 @@
|
|
|
1
1
|
import { layoutText, readTextWrapMode } from "@bindtty/text";
|
|
2
|
+
import { basicSupportedPropsByTag, readOverflow, validateElementProps } from "./layout-props.js";
|
|
2
3
|
import { clampNonNegative, toNonNegativeNumber } from "./measure.js";
|
|
3
|
-
const supportedPropsByTag = {
|
|
4
|
-
screen: new Set(),
|
|
5
|
-
vstack: new Set(),
|
|
6
|
-
hstack: new Set(),
|
|
7
|
-
box: new Set([
|
|
8
|
-
"padding",
|
|
9
|
-
"border",
|
|
10
|
-
"height",
|
|
11
|
-
"width",
|
|
12
|
-
"overflow",
|
|
13
|
-
"scrollX",
|
|
14
|
-
"scrollY"
|
|
15
|
-
]),
|
|
16
|
-
text: new Set(["value", "wrap", "color", "bold"]),
|
|
17
|
-
spacer: new Set(["size"]),
|
|
18
|
-
button: new Set(["value", "disabled"]),
|
|
19
|
-
input: new Set(["value", "placeholder"])
|
|
20
|
-
};
|
|
21
|
-
const futureLayoutProps = new Set([
|
|
22
|
-
"width",
|
|
23
|
-
"height",
|
|
24
|
-
"minWidth",
|
|
25
|
-
"minHeight",
|
|
26
|
-
"maxWidth",
|
|
27
|
-
"maxHeight",
|
|
28
|
-
"paddingX",
|
|
29
|
-
"paddingY",
|
|
30
|
-
"paddingTop",
|
|
31
|
-
"paddingRight",
|
|
32
|
-
"paddingBottom",
|
|
33
|
-
"paddingLeft",
|
|
34
|
-
"margin",
|
|
35
|
-
"marginX",
|
|
36
|
-
"marginY",
|
|
37
|
-
"marginTop",
|
|
38
|
-
"marginRight",
|
|
39
|
-
"marginBottom",
|
|
40
|
-
"marginLeft",
|
|
41
|
-
"gap",
|
|
42
|
-
"flexDirection",
|
|
43
|
-
"flexWrap",
|
|
44
|
-
"justifyContent",
|
|
45
|
-
"alignItems",
|
|
46
|
-
"flexGrow",
|
|
47
|
-
"flexShrink"
|
|
48
|
-
]);
|
|
49
|
-
const layoutPropAliases = new Map([
|
|
50
|
-
["padding-top", "paddingTop"],
|
|
51
|
-
["padding-right", "paddingRight"],
|
|
52
|
-
["padding-bottom", "paddingBottom"],
|
|
53
|
-
["padding-left", "paddingLeft"],
|
|
54
|
-
["padding-x", "paddingX"],
|
|
55
|
-
["padding-y", "paddingY"],
|
|
56
|
-
["margin-top", "marginTop"],
|
|
57
|
-
["margin-right", "marginRight"],
|
|
58
|
-
["margin-bottom", "marginBottom"],
|
|
59
|
-
["margin-left", "marginLeft"],
|
|
60
|
-
["margin-x", "marginX"],
|
|
61
|
-
["margin-y", "marginY"],
|
|
62
|
-
["flex-direction", "flexDirection"],
|
|
63
|
-
["flex-wrap", "flexWrap"],
|
|
64
|
-
["justify-content", "justifyContent"],
|
|
65
|
-
["align-items", "alignItems"],
|
|
66
|
-
["flex-grow", "flexGrow"],
|
|
67
|
-
["flex-shrink", "flexShrink"],
|
|
68
|
-
["min-width", "minWidth"],
|
|
69
|
-
["min-height", "minHeight"],
|
|
70
|
-
["max-width", "maxWidth"],
|
|
71
|
-
["max-height", "maxHeight"]
|
|
72
|
-
]);
|
|
73
|
-
const nonLayoutProps = new Set([
|
|
74
|
-
"id",
|
|
75
|
-
"focusStyle",
|
|
76
|
-
"onKey",
|
|
77
|
-
"onFocusChange"
|
|
78
|
-
]);
|
|
79
4
|
export function createBasicLayoutEngine() {
|
|
80
5
|
return {
|
|
81
6
|
layout(root, options) {
|
|
@@ -104,7 +29,7 @@ function measureNode(node, constraint) {
|
|
|
104
29
|
}
|
|
105
30
|
}
|
|
106
31
|
function measureElement(node, constraint) {
|
|
107
|
-
validateElementProps(node);
|
|
32
|
+
validateElementProps(node, basicSupportedPropsByTag[node.tag]);
|
|
108
33
|
switch (node.tag) {
|
|
109
34
|
case "screen":
|
|
110
35
|
return {
|
|
@@ -121,9 +46,6 @@ function measureElement(node, constraint) {
|
|
|
121
46
|
return measureFlowChildren(node.children, "row", constraint);
|
|
122
47
|
case "box":
|
|
123
48
|
return measureBox(node, constraint);
|
|
124
|
-
case "button":
|
|
125
|
-
case "input":
|
|
126
|
-
throw new Error(`Unsupported layout element: ${node.tag}`);
|
|
127
49
|
}
|
|
128
50
|
}
|
|
129
51
|
function measureTextElement(node, constraint) {
|
|
@@ -209,7 +131,7 @@ function arrangeStructureNode(node, rect, constraint) {
|
|
|
209
131
|
};
|
|
210
132
|
}
|
|
211
133
|
function arrangeElement(node, rect, constraint) {
|
|
212
|
-
validateElementProps(node);
|
|
134
|
+
validateElementProps(node, basicSupportedPropsByTag[node.tag]);
|
|
213
135
|
switch (node.tag) {
|
|
214
136
|
case "screen":
|
|
215
137
|
return arrangeFlowElement(node, rect, rect, "column", constraint);
|
|
@@ -222,9 +144,6 @@ function arrangeElement(node, rect, constraint) {
|
|
|
222
144
|
case "text":
|
|
223
145
|
case "spacer":
|
|
224
146
|
return createLeafLayout(node, rect);
|
|
225
|
-
case "button":
|
|
226
|
-
case "input":
|
|
227
|
-
throw new Error(`Unsupported layout element: ${node.tag}`);
|
|
228
147
|
}
|
|
229
148
|
}
|
|
230
149
|
function arrangeBox(node, rect, constraint) {
|
|
@@ -363,50 +282,12 @@ function getStructureChildren(node) {
|
|
|
363
282
|
return node.children;
|
|
364
283
|
}
|
|
365
284
|
}
|
|
366
|
-
function validateElementProps(node) {
|
|
367
|
-
const supportedProps = supportedPropsByTag[node.tag];
|
|
368
|
-
const seenCanonicalProps = new Map();
|
|
369
|
-
const canonicalProps = [];
|
|
370
|
-
for (const propName of Object.keys(node.props)) {
|
|
371
|
-
if (nonLayoutProps.has(propName)) {
|
|
372
|
-
continue;
|
|
373
|
-
}
|
|
374
|
-
const canonicalName = layoutPropAliases.get(propName) ?? propName;
|
|
375
|
-
const previousName = seenCanonicalProps.get(canonicalName);
|
|
376
|
-
if (previousName && previousName !== propName) {
|
|
377
|
-
throw new Error(`Duplicate layout prop: ${canonicalName} / ${propName}`);
|
|
378
|
-
}
|
|
379
|
-
seenCanonicalProps.set(canonicalName, propName);
|
|
380
|
-
canonicalProps.push(canonicalName);
|
|
381
|
-
}
|
|
382
|
-
for (const canonicalName of canonicalProps) {
|
|
383
|
-
if (!supportedProps.has(canonicalName) &&
|
|
384
|
-
futureLayoutProps.has(canonicalName)) {
|
|
385
|
-
throw new Error(`Unsupported layout prop: ${canonicalName}`);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
if (node.tag === "box") {
|
|
389
|
-
readOverflow(node.props.overflow);
|
|
390
|
-
}
|
|
391
|
-
if (node.tag === "text") {
|
|
392
|
-
readTextWrapMode(node.props.wrap);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
285
|
function readOptionalSize(value) {
|
|
396
286
|
if (value === null || value === undefined) {
|
|
397
287
|
return undefined;
|
|
398
288
|
}
|
|
399
289
|
return toNonNegativeNumber(value);
|
|
400
290
|
}
|
|
401
|
-
function readOverflow(value) {
|
|
402
|
-
if (value === null || value === undefined) {
|
|
403
|
-
return "visible";
|
|
404
|
-
}
|
|
405
|
-
if (value === "visible" || value === "clip") {
|
|
406
|
-
return value;
|
|
407
|
-
}
|
|
408
|
-
throw new Error(`Unsupported overflow value: ${String(value)}`);
|
|
409
|
-
}
|
|
410
291
|
function readScrollOffset(value) {
|
|
411
292
|
return Math.floor(toNonNegativeNumber(value));
|
|
412
293
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { createBasicLayoutEngine } from "./basic-engine.js";
|
|
2
2
|
export { createYogaLayoutEngine } from "./yoga-engine.js";
|
|
3
3
|
export { layoutRoot } from "./layout.js";
|
|
4
|
+
export { basicSupportedPropsByTag, futureLayoutProps, getLayoutPropMatrixStatus, matrixLayoutProps, resolveMargin, resolvePadding, yogaSupportedPropsByTag } from "./layout-props.js";
|
|
5
|
+
export type { BoxMarginEdges, BoxPaddingEdges, LayoutElementTag, LayoutPropMatrixStatus, MatrixLayoutProp } from "./layout-props.js";
|
|
4
6
|
export { createZeroRect } from "./measure.js";
|
|
5
7
|
export type { LayoutFlow } from "./intrinsic.js";
|
|
6
8
|
export type { LayoutEngine, LayoutEngineOptions, LayoutNode, LayoutOptions, LayoutRect, LayoutScrollOffset, LayoutSize, LayoutViewport } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createBasicLayoutEngine } from "./basic-engine.js";
|
|
2
2
|
export { createYogaLayoutEngine } from "./yoga-engine.js";
|
|
3
3
|
export { layoutRoot } from "./layout.js";
|
|
4
|
+
export { basicSupportedPropsByTag, futureLayoutProps, getLayoutPropMatrixStatus, matrixLayoutProps, resolveMargin, resolvePadding, yogaSupportedPropsByTag } from "./layout-props.js";
|
|
4
5
|
export { createZeroRect } from "./measure.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { MountedElementNode } from "@bindtty/vnode";
|
|
2
|
+
export type LayoutOverflow = "visible" | "clip";
|
|
3
|
+
export type LayoutElementTag = MountedElementNode["tag"];
|
|
4
|
+
export declare const yogaSupportedPropsByTag: Record<LayoutElementTag, ReadonlySet<string>>;
|
|
5
|
+
export declare const basicSupportedPropsByTag: Record<LayoutElementTag, ReadonlySet<string>>;
|
|
6
|
+
export declare const futureLayoutProps: Set<string>;
|
|
7
|
+
export declare const layoutPropAliases: Map<string, string>;
|
|
8
|
+
export declare const nonLayoutProps: Set<string>;
|
|
9
|
+
export declare const matrixLayoutProps: readonly ["width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "padding", "paddingX", "paddingY", "paddingTop", "paddingRight", "paddingBottom", "paddingLeft", "margin", "marginX", "marginY", "marginTop", "marginRight", "marginBottom", "marginLeft", "border", "overflow", "scrollX", "scrollY", "gap", "flexWrap", "justifyContent", "alignItems", "flexGrow", "flexShrink", "flexDirection"];
|
|
10
|
+
export type MatrixLayoutProp = (typeof matrixLayoutProps)[number];
|
|
11
|
+
export type LayoutPropMatrixStatus = "supported" | "future" | "na";
|
|
12
|
+
export interface BoxPaddingEdges {
|
|
13
|
+
top: number;
|
|
14
|
+
right: number;
|
|
15
|
+
bottom: number;
|
|
16
|
+
left: number;
|
|
17
|
+
}
|
|
18
|
+
export type BoxMarginEdges = BoxPaddingEdges;
|
|
19
|
+
export declare function resolvePadding(props: Record<string, unknown>): BoxPaddingEdges;
|
|
20
|
+
export declare function resolveMargin(props: Record<string, unknown>): BoxMarginEdges;
|
|
21
|
+
export declare function getLayoutPropMatrixStatus(tag: LayoutElementTag, prop: MatrixLayoutProp, engine?: "yoga" | "basic"): LayoutPropMatrixStatus;
|
|
22
|
+
export declare function readLayoutProp(props: Record<string, unknown>, canonicalName: string): unknown;
|
|
23
|
+
export declare function readOverflow(value: unknown): LayoutOverflow;
|
|
24
|
+
export declare function validateElementProps(node: MountedElementNode, supportedProps: ReadonlySet<string>): void;
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { readTextWrapMode } from "@bindtty/text";
|
|
2
|
+
import { toNonNegativeNumber } from "./measure.js";
|
|
3
|
+
const yogaMinMaxSizeProps = [
|
|
4
|
+
"minWidth",
|
|
5
|
+
"minHeight",
|
|
6
|
+
"maxWidth",
|
|
7
|
+
"maxHeight"
|
|
8
|
+
];
|
|
9
|
+
const yogaBoxEdgePaddingProps = [
|
|
10
|
+
"paddingX",
|
|
11
|
+
"paddingY",
|
|
12
|
+
"paddingTop",
|
|
13
|
+
"paddingRight",
|
|
14
|
+
"paddingBottom",
|
|
15
|
+
"paddingLeft"
|
|
16
|
+
];
|
|
17
|
+
const yogaMarginProps = [
|
|
18
|
+
"margin",
|
|
19
|
+
"marginX",
|
|
20
|
+
"marginY",
|
|
21
|
+
"marginTop",
|
|
22
|
+
"marginRight",
|
|
23
|
+
"marginBottom",
|
|
24
|
+
"marginLeft"
|
|
25
|
+
];
|
|
26
|
+
const yogaLayoutItemTags = [
|
|
27
|
+
...yogaMinMaxSizeProps,
|
|
28
|
+
...yogaMarginProps
|
|
29
|
+
];
|
|
30
|
+
export const yogaSupportedPropsByTag = {
|
|
31
|
+
screen: new Set([
|
|
32
|
+
"gap",
|
|
33
|
+
"flexWrap",
|
|
34
|
+
"justifyContent",
|
|
35
|
+
"alignItems",
|
|
36
|
+
"flexGrow",
|
|
37
|
+
"flexShrink"
|
|
38
|
+
]),
|
|
39
|
+
vstack: new Set([
|
|
40
|
+
"gap",
|
|
41
|
+
"flexWrap",
|
|
42
|
+
"justifyContent",
|
|
43
|
+
"alignItems",
|
|
44
|
+
"flexGrow",
|
|
45
|
+
"flexShrink",
|
|
46
|
+
...yogaLayoutItemTags
|
|
47
|
+
]),
|
|
48
|
+
hstack: new Set([
|
|
49
|
+
"gap",
|
|
50
|
+
"flexWrap",
|
|
51
|
+
"justifyContent",
|
|
52
|
+
"alignItems",
|
|
53
|
+
"flexGrow",
|
|
54
|
+
"flexShrink",
|
|
55
|
+
...yogaLayoutItemTags
|
|
56
|
+
]),
|
|
57
|
+
box: new Set([
|
|
58
|
+
"padding",
|
|
59
|
+
"border",
|
|
60
|
+
"height",
|
|
61
|
+
"width",
|
|
62
|
+
"overflow",
|
|
63
|
+
"scrollX",
|
|
64
|
+
"scrollY",
|
|
65
|
+
"gap",
|
|
66
|
+
"flexWrap",
|
|
67
|
+
"justifyContent",
|
|
68
|
+
"alignItems",
|
|
69
|
+
"flexGrow",
|
|
70
|
+
"flexShrink",
|
|
71
|
+
...yogaLayoutItemTags,
|
|
72
|
+
...yogaBoxEdgePaddingProps
|
|
73
|
+
]),
|
|
74
|
+
text: new Set([
|
|
75
|
+
"value",
|
|
76
|
+
"wrap",
|
|
77
|
+
"color",
|
|
78
|
+
"bold",
|
|
79
|
+
"flexGrow",
|
|
80
|
+
"flexShrink",
|
|
81
|
+
...yogaLayoutItemTags
|
|
82
|
+
]),
|
|
83
|
+
spacer: new Set(["size", "flexGrow", "flexShrink", ...yogaLayoutItemTags])
|
|
84
|
+
};
|
|
85
|
+
export const basicSupportedPropsByTag = {
|
|
86
|
+
screen: new Set(),
|
|
87
|
+
vstack: new Set(),
|
|
88
|
+
hstack: new Set(),
|
|
89
|
+
box: new Set([
|
|
90
|
+
"padding",
|
|
91
|
+
"border",
|
|
92
|
+
"height",
|
|
93
|
+
"width",
|
|
94
|
+
"overflow",
|
|
95
|
+
"scrollX",
|
|
96
|
+
"scrollY"
|
|
97
|
+
]),
|
|
98
|
+
text: new Set(["value", "wrap", "color", "bold"]),
|
|
99
|
+
spacer: new Set(["size"])
|
|
100
|
+
};
|
|
101
|
+
export const futureLayoutProps = new Set([
|
|
102
|
+
"width",
|
|
103
|
+
"height",
|
|
104
|
+
"minWidth",
|
|
105
|
+
"minHeight",
|
|
106
|
+
"maxWidth",
|
|
107
|
+
"maxHeight",
|
|
108
|
+
"paddingX",
|
|
109
|
+
"paddingY",
|
|
110
|
+
"paddingTop",
|
|
111
|
+
"paddingRight",
|
|
112
|
+
"paddingBottom",
|
|
113
|
+
"paddingLeft",
|
|
114
|
+
"margin",
|
|
115
|
+
"marginX",
|
|
116
|
+
"marginY",
|
|
117
|
+
"marginTop",
|
|
118
|
+
"marginRight",
|
|
119
|
+
"marginBottom",
|
|
120
|
+
"marginLeft",
|
|
121
|
+
"gap",
|
|
122
|
+
"flexDirection",
|
|
123
|
+
"flexWrap",
|
|
124
|
+
"justifyContent",
|
|
125
|
+
"alignItems",
|
|
126
|
+
"flexGrow",
|
|
127
|
+
"flexShrink"
|
|
128
|
+
]);
|
|
129
|
+
export const layoutPropAliases = new Map([
|
|
130
|
+
["padding-top", "paddingTop"],
|
|
131
|
+
["padding-right", "paddingRight"],
|
|
132
|
+
["padding-bottom", "paddingBottom"],
|
|
133
|
+
["padding-left", "paddingLeft"],
|
|
134
|
+
["padding-x", "paddingX"],
|
|
135
|
+
["padding-y", "paddingY"],
|
|
136
|
+
["margin-top", "marginTop"],
|
|
137
|
+
["margin-right", "marginRight"],
|
|
138
|
+
["margin-bottom", "marginBottom"],
|
|
139
|
+
["margin-left", "marginLeft"],
|
|
140
|
+
["margin-x", "marginX"],
|
|
141
|
+
["margin-y", "marginY"],
|
|
142
|
+
["flex-direction", "flexDirection"],
|
|
143
|
+
["flex-wrap", "flexWrap"],
|
|
144
|
+
["justify-content", "justifyContent"],
|
|
145
|
+
["align-items", "alignItems"],
|
|
146
|
+
["flex-grow", "flexGrow"],
|
|
147
|
+
["flex-shrink", "flexShrink"],
|
|
148
|
+
["min-width", "minWidth"],
|
|
149
|
+
["min-height", "minHeight"],
|
|
150
|
+
["max-width", "maxWidth"],
|
|
151
|
+
["max-height", "maxHeight"]
|
|
152
|
+
]);
|
|
153
|
+
export const nonLayoutProps = new Set([
|
|
154
|
+
"id",
|
|
155
|
+
"focusStyle",
|
|
156
|
+
"focusable",
|
|
157
|
+
"onKeyCapture",
|
|
158
|
+
"onKey",
|
|
159
|
+
"onFocusChange"
|
|
160
|
+
]);
|
|
161
|
+
export const matrixLayoutProps = [
|
|
162
|
+
"width",
|
|
163
|
+
"height",
|
|
164
|
+
"minWidth",
|
|
165
|
+
"minHeight",
|
|
166
|
+
"maxWidth",
|
|
167
|
+
"maxHeight",
|
|
168
|
+
"padding",
|
|
169
|
+
"paddingX",
|
|
170
|
+
"paddingY",
|
|
171
|
+
"paddingTop",
|
|
172
|
+
"paddingRight",
|
|
173
|
+
"paddingBottom",
|
|
174
|
+
"paddingLeft",
|
|
175
|
+
"margin",
|
|
176
|
+
"marginX",
|
|
177
|
+
"marginY",
|
|
178
|
+
"marginTop",
|
|
179
|
+
"marginRight",
|
|
180
|
+
"marginBottom",
|
|
181
|
+
"marginLeft",
|
|
182
|
+
"border",
|
|
183
|
+
"overflow",
|
|
184
|
+
"scrollX",
|
|
185
|
+
"scrollY",
|
|
186
|
+
"gap",
|
|
187
|
+
"flexWrap",
|
|
188
|
+
"justifyContent",
|
|
189
|
+
"alignItems",
|
|
190
|
+
"flexGrow",
|
|
191
|
+
"flexShrink",
|
|
192
|
+
"flexDirection"
|
|
193
|
+
];
|
|
194
|
+
export function resolvePadding(props) {
|
|
195
|
+
const base = toNonNegativeNumber(readLayoutProp(props, "padding"));
|
|
196
|
+
const paddingX = readLayoutProp(props, "paddingX");
|
|
197
|
+
const paddingY = readLayoutProp(props, "paddingY");
|
|
198
|
+
const axisX = paddingX !== undefined ? toNonNegativeNumber(paddingX) : base;
|
|
199
|
+
const axisY = paddingY !== undefined ? toNonNegativeNumber(paddingY) : base;
|
|
200
|
+
const top = readLayoutProp(props, "paddingTop");
|
|
201
|
+
const right = readLayoutProp(props, "paddingRight");
|
|
202
|
+
const bottom = readLayoutProp(props, "paddingBottom");
|
|
203
|
+
const left = readLayoutProp(props, "paddingLeft");
|
|
204
|
+
return {
|
|
205
|
+
top: top !== undefined ? toNonNegativeNumber(top) : axisY,
|
|
206
|
+
right: right !== undefined ? toNonNegativeNumber(right) : axisX,
|
|
207
|
+
bottom: bottom !== undefined ? toNonNegativeNumber(bottom) : axisY,
|
|
208
|
+
left: left !== undefined ? toNonNegativeNumber(left) : axisX
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
export function resolveMargin(props) {
|
|
212
|
+
const base = toNonNegativeNumber(readLayoutProp(props, "margin"));
|
|
213
|
+
const marginX = readLayoutProp(props, "marginX");
|
|
214
|
+
const marginY = readLayoutProp(props, "marginY");
|
|
215
|
+
const axisX = marginX !== undefined ? toNonNegativeNumber(marginX) : base;
|
|
216
|
+
const axisY = marginY !== undefined ? toNonNegativeNumber(marginY) : base;
|
|
217
|
+
const top = readLayoutProp(props, "marginTop");
|
|
218
|
+
const right = readLayoutProp(props, "marginRight");
|
|
219
|
+
const bottom = readLayoutProp(props, "marginBottom");
|
|
220
|
+
const left = readLayoutProp(props, "marginLeft");
|
|
221
|
+
return {
|
|
222
|
+
top: top !== undefined ? toNonNegativeNumber(top) : axisY,
|
|
223
|
+
right: right !== undefined ? toNonNegativeNumber(right) : axisX,
|
|
224
|
+
bottom: bottom !== undefined ? toNonNegativeNumber(bottom) : axisY,
|
|
225
|
+
left: left !== undefined ? toNonNegativeNumber(left) : axisX
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
export function getLayoutPropMatrixStatus(tag, prop, engine = "yoga") {
|
|
229
|
+
const supportedProps = engine === "yoga" ? yogaSupportedPropsByTag[tag] : basicSupportedPropsByTag[tag];
|
|
230
|
+
if (supportedProps.has(prop)) {
|
|
231
|
+
return "supported";
|
|
232
|
+
}
|
|
233
|
+
if (futureLayoutProps.has(prop)) {
|
|
234
|
+
return "future";
|
|
235
|
+
}
|
|
236
|
+
return "na";
|
|
237
|
+
}
|
|
238
|
+
export function readLayoutProp(props, canonicalName) {
|
|
239
|
+
if (hasOwn(props, canonicalName)) {
|
|
240
|
+
return props[canonicalName];
|
|
241
|
+
}
|
|
242
|
+
for (const [alias, canonical] of layoutPropAliases) {
|
|
243
|
+
if (canonical === canonicalName && hasOwn(props, alias)) {
|
|
244
|
+
return props[alias];
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
export function readOverflow(value) {
|
|
250
|
+
if (value === null || value === undefined) {
|
|
251
|
+
return "visible";
|
|
252
|
+
}
|
|
253
|
+
if (value === "visible" || value === "clip") {
|
|
254
|
+
return value;
|
|
255
|
+
}
|
|
256
|
+
throw new Error(`Unsupported overflow value: ${String(value)}`);
|
|
257
|
+
}
|
|
258
|
+
export function validateElementProps(node, supportedProps) {
|
|
259
|
+
const seenCanonicalProps = new Map();
|
|
260
|
+
const canonicalProps = [];
|
|
261
|
+
for (const propName of Object.keys(node.props)) {
|
|
262
|
+
if (nonLayoutProps.has(propName)) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
const canonicalName = layoutPropAliases.get(propName) ?? propName;
|
|
266
|
+
const previousName = seenCanonicalProps.get(canonicalName);
|
|
267
|
+
if (previousName && previousName !== propName) {
|
|
268
|
+
throw new Error(`Duplicate layout prop: ${canonicalName} / ${propName}`);
|
|
269
|
+
}
|
|
270
|
+
seenCanonicalProps.set(canonicalName, propName);
|
|
271
|
+
canonicalProps.push(canonicalName);
|
|
272
|
+
}
|
|
273
|
+
for (const canonicalName of canonicalProps) {
|
|
274
|
+
if (!supportedProps.has(canonicalName) &&
|
|
275
|
+
futureLayoutProps.has(canonicalName)) {
|
|
276
|
+
throw new Error(`Unsupported layout prop: ${canonicalName}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (node.tag === "box") {
|
|
280
|
+
readOverflow(node.props.overflow);
|
|
281
|
+
}
|
|
282
|
+
if (node.tag === "text") {
|
|
283
|
+
readTextWrapMode(node.props.wrap);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function hasOwn(object, key) {
|
|
287
|
+
return Object.prototype.hasOwnProperty.call(object, key);
|
|
288
|
+
}
|
package/dist/yoga-engine.js
CHANGED
|
@@ -1,109 +1,7 @@
|
|
|
1
1
|
import Yoga from "yoga-layout";
|
|
2
2
|
import { layoutText, readTextWrapMode } from "@bindtty/text";
|
|
3
|
+
import { readLayoutProp, readOverflow, resolveMargin, resolvePadding, validateElementProps, yogaSupportedPropsByTag } from "./layout-props.js";
|
|
3
4
|
import { clampNonNegative, toNonNegativeNumber } from "./measure.js";
|
|
4
|
-
const supportedPropsByTag = {
|
|
5
|
-
screen: new Set([
|
|
6
|
-
"gap",
|
|
7
|
-
"flexWrap",
|
|
8
|
-
"justifyContent",
|
|
9
|
-
"alignItems",
|
|
10
|
-
"flexGrow",
|
|
11
|
-
"flexShrink"
|
|
12
|
-
]),
|
|
13
|
-
vstack: new Set([
|
|
14
|
-
"gap",
|
|
15
|
-
"flexWrap",
|
|
16
|
-
"justifyContent",
|
|
17
|
-
"alignItems",
|
|
18
|
-
"flexGrow",
|
|
19
|
-
"flexShrink"
|
|
20
|
-
]),
|
|
21
|
-
hstack: new Set([
|
|
22
|
-
"gap",
|
|
23
|
-
"flexWrap",
|
|
24
|
-
"justifyContent",
|
|
25
|
-
"alignItems",
|
|
26
|
-
"flexGrow",
|
|
27
|
-
"flexShrink"
|
|
28
|
-
]),
|
|
29
|
-
box: new Set([
|
|
30
|
-
"padding",
|
|
31
|
-
"border",
|
|
32
|
-
"height",
|
|
33
|
-
"width",
|
|
34
|
-
"overflow",
|
|
35
|
-
"scrollX",
|
|
36
|
-
"scrollY",
|
|
37
|
-
"gap",
|
|
38
|
-
"flexWrap",
|
|
39
|
-
"justifyContent",
|
|
40
|
-
"alignItems",
|
|
41
|
-
"flexGrow",
|
|
42
|
-
"flexShrink"
|
|
43
|
-
]),
|
|
44
|
-
text: new Set(["value", "wrap", "color", "bold", "flexGrow", "flexShrink"]),
|
|
45
|
-
spacer: new Set(["size", "flexGrow", "flexShrink"]),
|
|
46
|
-
button: new Set(["value", "disabled", "flexGrow", "flexShrink"]),
|
|
47
|
-
input: new Set(["value", "placeholder", "flexGrow", "flexShrink"])
|
|
48
|
-
};
|
|
49
|
-
const futureLayoutProps = new Set([
|
|
50
|
-
"width",
|
|
51
|
-
"height",
|
|
52
|
-
"minWidth",
|
|
53
|
-
"minHeight",
|
|
54
|
-
"maxWidth",
|
|
55
|
-
"maxHeight",
|
|
56
|
-
"paddingX",
|
|
57
|
-
"paddingY",
|
|
58
|
-
"paddingTop",
|
|
59
|
-
"paddingRight",
|
|
60
|
-
"paddingBottom",
|
|
61
|
-
"paddingLeft",
|
|
62
|
-
"margin",
|
|
63
|
-
"marginX",
|
|
64
|
-
"marginY",
|
|
65
|
-
"marginTop",
|
|
66
|
-
"marginRight",
|
|
67
|
-
"marginBottom",
|
|
68
|
-
"marginLeft",
|
|
69
|
-
"gap",
|
|
70
|
-
"flexDirection",
|
|
71
|
-
"flexWrap",
|
|
72
|
-
"justifyContent",
|
|
73
|
-
"alignItems",
|
|
74
|
-
"flexGrow",
|
|
75
|
-
"flexShrink"
|
|
76
|
-
]);
|
|
77
|
-
const layoutPropAliases = new Map([
|
|
78
|
-
["padding-top", "paddingTop"],
|
|
79
|
-
["padding-right", "paddingRight"],
|
|
80
|
-
["padding-bottom", "paddingBottom"],
|
|
81
|
-
["padding-left", "paddingLeft"],
|
|
82
|
-
["padding-x", "paddingX"],
|
|
83
|
-
["padding-y", "paddingY"],
|
|
84
|
-
["margin-top", "marginTop"],
|
|
85
|
-
["margin-right", "marginRight"],
|
|
86
|
-
["margin-bottom", "marginBottom"],
|
|
87
|
-
["margin-left", "marginLeft"],
|
|
88
|
-
["margin-x", "marginX"],
|
|
89
|
-
["margin-y", "marginY"],
|
|
90
|
-
["flex-direction", "flexDirection"],
|
|
91
|
-
["flex-wrap", "flexWrap"],
|
|
92
|
-
["justify-content", "justifyContent"],
|
|
93
|
-
["align-items", "alignItems"],
|
|
94
|
-
["flex-grow", "flexGrow"],
|
|
95
|
-
["flex-shrink", "flexShrink"],
|
|
96
|
-
["min-width", "minWidth"],
|
|
97
|
-
["min-height", "minHeight"],
|
|
98
|
-
["max-width", "maxWidth"],
|
|
99
|
-
["max-height", "maxHeight"]
|
|
100
|
-
]);
|
|
101
|
-
const nonLayoutProps = new Set([
|
|
102
|
-
"id",
|
|
103
|
-
"focusStyle",
|
|
104
|
-
"onKey",
|
|
105
|
-
"onFocusChange"
|
|
106
|
-
]);
|
|
107
5
|
const defaultYogaAdapter = {
|
|
108
6
|
createNode() {
|
|
109
7
|
return Yoga.Node.create();
|
|
@@ -170,8 +68,10 @@ function configureYogaNode(entry, inheritedFlow, options) {
|
|
|
170
68
|
}
|
|
171
69
|
}
|
|
172
70
|
function configureYogaElement(node, yogaNode, inheritedFlow, options) {
|
|
173
|
-
validateElementProps(node);
|
|
71
|
+
validateElementProps(node, yogaSupportedPropsByTag[node.tag]);
|
|
174
72
|
applyYogaItemProps(node, yogaNode);
|
|
73
|
+
applyYogaSizeProps(node, yogaNode);
|
|
74
|
+
applyYogaMarginProps(node, yogaNode);
|
|
175
75
|
switch (node.tag) {
|
|
176
76
|
case "screen":
|
|
177
77
|
yogaNode.setWidth(options.viewport.width);
|
|
@@ -196,14 +96,10 @@ function configureYogaElement(node, yogaNode, inheritedFlow, options) {
|
|
|
196
96
|
case "spacer":
|
|
197
97
|
configureYogaSpacer(node, yogaNode, inheritedFlow);
|
|
198
98
|
return;
|
|
199
|
-
case "button":
|
|
200
|
-
case "input":
|
|
201
|
-
throw new Error(`Unsupported layout element: ${node.tag}`);
|
|
202
99
|
}
|
|
203
100
|
}
|
|
204
101
|
function configureYogaBox(node, yogaNode) {
|
|
205
102
|
const border = node.props.border ? 1 : 0;
|
|
206
|
-
const padding = toNonNegativeNumber(node.props.padding);
|
|
207
103
|
const width = readOptionalSize(node.props.width);
|
|
208
104
|
const height = readOptionalSize(node.props.height);
|
|
209
105
|
const overflow = readOverflow(node.props.overflow);
|
|
@@ -218,13 +114,26 @@ function configureYogaBox(node, yogaNode) {
|
|
|
218
114
|
if (border > 0) {
|
|
219
115
|
yogaNode.setBorder(Yoga.EDGE_ALL, border);
|
|
220
116
|
}
|
|
221
|
-
|
|
222
|
-
yogaNode.setPadding(Yoga.EDGE_ALL, padding);
|
|
223
|
-
}
|
|
117
|
+
applyYogaBoxPadding(node, yogaNode);
|
|
224
118
|
if (overflow === "clip" || hasOwn(node.props, "scrollX") || hasOwn(node.props, "scrollY")) {
|
|
225
119
|
yogaNode.setOverflow(Yoga.OVERFLOW_HIDDEN);
|
|
226
120
|
}
|
|
227
121
|
}
|
|
122
|
+
function applyYogaBoxPadding(node, yogaNode) {
|
|
123
|
+
const padding = resolvePadding(node.props);
|
|
124
|
+
if (padding.top > 0) {
|
|
125
|
+
yogaNode.setPadding(Yoga.EDGE_TOP, padding.top);
|
|
126
|
+
}
|
|
127
|
+
if (padding.right > 0) {
|
|
128
|
+
yogaNode.setPadding(Yoga.EDGE_RIGHT, padding.right);
|
|
129
|
+
}
|
|
130
|
+
if (padding.bottom > 0) {
|
|
131
|
+
yogaNode.setPadding(Yoga.EDGE_BOTTOM, padding.bottom);
|
|
132
|
+
}
|
|
133
|
+
if (padding.left > 0) {
|
|
134
|
+
yogaNode.setPadding(Yoga.EDGE_LEFT, padding.left);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
228
137
|
function configureYogaText(node, yogaNode) {
|
|
229
138
|
const text = String(node.props.value ?? "");
|
|
230
139
|
const wrap = readTextWrapMode(node.props.wrap);
|
|
@@ -294,12 +203,13 @@ function readContentRect(entry, rect) {
|
|
|
294
203
|
if (entry.mounted.kind !== "element" || entry.mounted.tag !== "box") {
|
|
295
204
|
return rect;
|
|
296
205
|
}
|
|
297
|
-
const
|
|
206
|
+
const borderSize = entry.mounted.props.border ? 1 : 0;
|
|
207
|
+
const padding = resolvePadding(entry.mounted.props);
|
|
298
208
|
return {
|
|
299
|
-
x: rect.x +
|
|
300
|
-
y: rect.y +
|
|
301
|
-
width: clampNonNegative(rect.width -
|
|
302
|
-
height: clampNonNegative(rect.height -
|
|
209
|
+
x: rect.x + borderSize + padding.left,
|
|
210
|
+
y: rect.y + borderSize + padding.top,
|
|
211
|
+
width: clampNonNegative(rect.width - borderSize * 2 - padding.left - padding.right),
|
|
212
|
+
height: clampNonNegative(rect.height - borderSize * 2 - padding.top - padding.bottom)
|
|
303
213
|
};
|
|
304
214
|
}
|
|
305
215
|
function applyBoxMetadata(node, layout) {
|
|
@@ -327,8 +237,11 @@ function readContentSize(layout) {
|
|
|
327
237
|
let width = layout.contentRect.width;
|
|
328
238
|
let height = layout.contentRect.height;
|
|
329
239
|
for (const child of layout.children) {
|
|
330
|
-
|
|
331
|
-
|
|
240
|
+
const margin = child.mounted.kind === "element"
|
|
241
|
+
? resolveMargin(child.mounted.props)
|
|
242
|
+
: { top: 0, right: 0, bottom: 0, left: 0 };
|
|
243
|
+
width = Math.max(width, child.rect.x + child.rect.width + margin.right - layout.contentRect.x);
|
|
244
|
+
height = Math.max(height, child.rect.y + child.rect.height + margin.bottom - layout.contentRect.y);
|
|
332
245
|
}
|
|
333
246
|
return {
|
|
334
247
|
width: clampNonNegative(width),
|
|
@@ -359,35 +272,6 @@ function getStructureChildren(node) {
|
|
|
359
272
|
return node.children;
|
|
360
273
|
}
|
|
361
274
|
}
|
|
362
|
-
function validateElementProps(node) {
|
|
363
|
-
const supportedProps = supportedPropsByTag[node.tag];
|
|
364
|
-
const seenCanonicalProps = new Map();
|
|
365
|
-
const canonicalProps = [];
|
|
366
|
-
for (const propName of Object.keys(node.props)) {
|
|
367
|
-
if (nonLayoutProps.has(propName)) {
|
|
368
|
-
continue;
|
|
369
|
-
}
|
|
370
|
-
const canonicalName = layoutPropAliases.get(propName) ?? propName;
|
|
371
|
-
const previousName = seenCanonicalProps.get(canonicalName);
|
|
372
|
-
if (previousName && previousName !== propName) {
|
|
373
|
-
throw new Error(`Duplicate layout prop: ${canonicalName} / ${propName}`);
|
|
374
|
-
}
|
|
375
|
-
seenCanonicalProps.set(canonicalName, propName);
|
|
376
|
-
canonicalProps.push(canonicalName);
|
|
377
|
-
}
|
|
378
|
-
for (const canonicalName of canonicalProps) {
|
|
379
|
-
if (!supportedProps.has(canonicalName) &&
|
|
380
|
-
futureLayoutProps.has(canonicalName)) {
|
|
381
|
-
throw new Error(`Unsupported layout prop: ${canonicalName}`);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
if (node.tag === "box") {
|
|
385
|
-
readOverflow(node.props.overflow);
|
|
386
|
-
}
|
|
387
|
-
if (node.tag === "text") {
|
|
388
|
-
readTextWrapMode(node.props.wrap);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
275
|
function getMeasureWidth(width, widthMode, wrap) {
|
|
392
276
|
if (wrap === "legacy" || widthMode === Yoga.MEASURE_MODE_UNDEFINED) {
|
|
393
277
|
return undefined;
|
|
@@ -417,6 +301,39 @@ function applyYogaItemProps(node, yogaNode) {
|
|
|
417
301
|
yogaNode.setFlexShrink(flexShrink);
|
|
418
302
|
}
|
|
419
303
|
}
|
|
304
|
+
function applyYogaSizeProps(node, yogaNode) {
|
|
305
|
+
const minWidth = readOptionalSize(readLayoutProp(node.props, "minWidth"));
|
|
306
|
+
const minHeight = readOptionalSize(readLayoutProp(node.props, "minHeight"));
|
|
307
|
+
const maxWidth = readOptionalSize(readLayoutProp(node.props, "maxWidth"));
|
|
308
|
+
const maxHeight = readOptionalSize(readLayoutProp(node.props, "maxHeight"));
|
|
309
|
+
if (minWidth !== undefined) {
|
|
310
|
+
yogaNode.setMinWidth(minWidth);
|
|
311
|
+
}
|
|
312
|
+
if (minHeight !== undefined) {
|
|
313
|
+
yogaNode.setMinHeight(minHeight);
|
|
314
|
+
}
|
|
315
|
+
if (maxWidth !== undefined) {
|
|
316
|
+
yogaNode.setMaxWidth(maxWidth);
|
|
317
|
+
}
|
|
318
|
+
if (maxHeight !== undefined) {
|
|
319
|
+
yogaNode.setMaxHeight(maxHeight);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function applyYogaMarginProps(node, yogaNode) {
|
|
323
|
+
const margin = resolveMargin(node.props);
|
|
324
|
+
if (margin.top > 0) {
|
|
325
|
+
yogaNode.setMargin(Yoga.EDGE_TOP, margin.top);
|
|
326
|
+
}
|
|
327
|
+
if (margin.right > 0) {
|
|
328
|
+
yogaNode.setMargin(Yoga.EDGE_RIGHT, margin.right);
|
|
329
|
+
}
|
|
330
|
+
if (margin.bottom > 0) {
|
|
331
|
+
yogaNode.setMargin(Yoga.EDGE_BOTTOM, margin.bottom);
|
|
332
|
+
}
|
|
333
|
+
if (margin.left > 0) {
|
|
334
|
+
yogaNode.setMargin(Yoga.EDGE_LEFT, margin.left);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
420
337
|
function applyYogaContainerProps(node, yogaNode) {
|
|
421
338
|
const gap = readOptionalSize(readLayoutProp(node.props, "gap"));
|
|
422
339
|
const flexWrap = readLayoutProp(node.props, "flexWrap");
|
|
@@ -481,35 +398,12 @@ function readYogaJustifyContent(value) {
|
|
|
481
398
|
throw new Error(`Unsupported justifyContent value: ${String(value)}`);
|
|
482
399
|
}
|
|
483
400
|
}
|
|
484
|
-
function readLayoutProp(props, canonicalName) {
|
|
485
|
-
if (hasOwn(props, canonicalName)) {
|
|
486
|
-
return props[canonicalName];
|
|
487
|
-
}
|
|
488
|
-
for (const [alias, canonical] of layoutPropAliases) {
|
|
489
|
-
if (canonical === canonicalName && hasOwn(props, alias)) {
|
|
490
|
-
return props[alias];
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
return undefined;
|
|
494
|
-
}
|
|
495
|
-
function getBoxInset(node) {
|
|
496
|
-
return (node.props.border ? 1 : 0) + toNonNegativeNumber(node.props.padding);
|
|
497
|
-
}
|
|
498
401
|
function readOptionalSize(value) {
|
|
499
402
|
if (value === null || value === undefined) {
|
|
500
403
|
return undefined;
|
|
501
404
|
}
|
|
502
405
|
return toNonNegativeNumber(value);
|
|
503
406
|
}
|
|
504
|
-
function readOverflow(value) {
|
|
505
|
-
if (value === null || value === undefined) {
|
|
506
|
-
return "visible";
|
|
507
|
-
}
|
|
508
|
-
if (value === "visible" || value === "clip") {
|
|
509
|
-
return value;
|
|
510
|
-
}
|
|
511
|
-
throw new Error(`Unsupported overflow value: ${String(value)}`);
|
|
512
|
-
}
|
|
513
407
|
function readScrollOffset(value) {
|
|
514
408
|
return Math.floor(toNonNegativeNumber(value));
|
|
515
409
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bindtty/layout",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Layout tree generation for BindTTY mounted nodes.",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
"test": "npm run build --workspace @bindtty/text && npm run build --workspace @bindtty/vnode && npm run build --workspace @bindtty/jsx-runtime && npm run build --workspace @bindtty/runtime && npm run build && tsc -p test/tsconfig.json && node --test test/dist/*.test.js"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@bindtty/text": "0.1.0-alpha.
|
|
25
|
-
"@bindtty/vnode": "0.1.0-alpha.
|
|
24
|
+
"@bindtty/text": "0.1.0-alpha.3",
|
|
25
|
+
"@bindtty/vnode": "0.1.0-alpha.3",
|
|
26
26
|
"yoga-layout": "3.2.1"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@bindtty/jsx-runtime": "0.1.0-alpha.
|
|
30
|
-
"@bindtty/runtime": "0.1.0-alpha.
|
|
31
|
-
"@bindtty/signal": "0.1.0-alpha.
|
|
29
|
+
"@bindtty/jsx-runtime": "0.1.0-alpha.3",
|
|
30
|
+
"@bindtty/runtime": "0.1.0-alpha.3",
|
|
31
|
+
"@bindtty/signal": "0.1.0-alpha.3",
|
|
32
32
|
"@types/node": "^22.0.0",
|
|
33
33
|
"typescript": "^5.5.0"
|
|
34
34
|
},
|