@wire-dsl/engine 0.5.0 → 0.6.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/dist/index.cjs +90 -26
- package/dist/index.d.cts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.js +90 -26
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -2220,8 +2220,8 @@ var IRStyleSchema = import_zod.z.object({
|
|
|
2220
2220
|
var IRNodeStyleSchema = import_zod.z.object({
|
|
2221
2221
|
padding: import_zod.z.string().optional(),
|
|
2222
2222
|
gap: import_zod.z.string().optional(),
|
|
2223
|
-
|
|
2224
|
-
|
|
2223
|
+
justify: import_zod.z.enum(["start", "center", "end", "stretch", "spaceBetween", "spaceAround"]).optional(),
|
|
2224
|
+
align: import_zod.z.enum(["start", "center", "end"]).optional(),
|
|
2225
2225
|
background: import_zod.z.string().optional()
|
|
2226
2226
|
});
|
|
2227
2227
|
var IRMetaSchema = import_zod.z.object({
|
|
@@ -2507,6 +2507,9 @@ ${messages}`);
|
|
|
2507
2507
|
if (layoutParams.gap !== void 0) {
|
|
2508
2508
|
style.gap = String(layoutParams.gap);
|
|
2509
2509
|
}
|
|
2510
|
+
if (layoutParams.justify !== void 0) {
|
|
2511
|
+
style.justify = layoutParams.justify;
|
|
2512
|
+
}
|
|
2510
2513
|
if (layoutParams.align !== void 0) {
|
|
2511
2514
|
style.align = layoutParams.align;
|
|
2512
2515
|
}
|
|
@@ -3153,8 +3156,9 @@ var LayoutEngine = class {
|
|
|
3153
3156
|
}
|
|
3154
3157
|
});
|
|
3155
3158
|
} else {
|
|
3156
|
-
const
|
|
3157
|
-
|
|
3159
|
+
const justify = node.style.justify || "stretch";
|
|
3160
|
+
const crossAlign = node.style.align || "start";
|
|
3161
|
+
if (justify === "stretch") {
|
|
3158
3162
|
let currentX = x;
|
|
3159
3163
|
const childWidth = this.calculateChildWidth(children.length, width, gap);
|
|
3160
3164
|
let stackHeight = 0;
|
|
@@ -3176,43 +3180,44 @@ var LayoutEngine = class {
|
|
|
3176
3180
|
});
|
|
3177
3181
|
} else {
|
|
3178
3182
|
const childWidths = [];
|
|
3183
|
+
const childHeights = [];
|
|
3179
3184
|
const explicitHeightFlags = [];
|
|
3180
|
-
const
|
|
3185
|
+
const flexIndices = /* @__PURE__ */ new Set();
|
|
3181
3186
|
let stackHeight = 0;
|
|
3182
3187
|
children.forEach((childRef, index) => {
|
|
3183
3188
|
const childNode = this.nodes[childRef.ref];
|
|
3184
|
-
let childWidth = this.getIntrinsicComponentWidth(childNode);
|
|
3185
|
-
let childHeight = this.getComponentHeight();
|
|
3186
3189
|
const hasExplicitHeight = childNode?.kind === "component" && !!childNode.props.height;
|
|
3187
3190
|
const hasExplicitWidth = childNode?.kind === "component" && !!childNode.props.width;
|
|
3188
3191
|
const isBlockButton = childNode?.kind === "component" && childNode.componentType === "Button" && !hasExplicitWidth && this.parseBooleanProp(childNode.props.block, false);
|
|
3189
|
-
|
|
3192
|
+
const isFlexContainer = !hasExplicitWidth && childNode?.kind === "container" && !this.containerHasIntrinsicWidth(childNode);
|
|
3193
|
+
let childWidth;
|
|
3194
|
+
if (isBlockButton || isFlexContainer) {
|
|
3190
3195
|
childWidth = 0;
|
|
3191
|
-
|
|
3196
|
+
flexIndices.add(index);
|
|
3192
3197
|
} else if (hasExplicitWidth) {
|
|
3193
3198
|
childWidth = Number(childNode.props.width);
|
|
3194
|
-
}
|
|
3195
|
-
|
|
3196
|
-
childHeight = Number(childNode.props.height);
|
|
3199
|
+
} else {
|
|
3200
|
+
childWidth = this.getIntrinsicWidth(childNode, width);
|
|
3197
3201
|
}
|
|
3198
3202
|
childWidths.push(childWidth);
|
|
3203
|
+
childHeights.push(this.getComponentHeight());
|
|
3199
3204
|
explicitHeightFlags.push(hasExplicitHeight);
|
|
3200
3205
|
});
|
|
3201
3206
|
const totalGapWidth = gap * Math.max(0, children.length - 1);
|
|
3202
|
-
if (
|
|
3207
|
+
if (flexIndices.size > 0) {
|
|
3203
3208
|
const fixedWidth = childWidths.reduce((sum, w, idx) => {
|
|
3204
|
-
return
|
|
3209
|
+
return flexIndices.has(idx) ? sum : sum + w;
|
|
3205
3210
|
}, 0);
|
|
3206
3211
|
const remainingWidth = width - totalGapWidth - fixedWidth;
|
|
3207
|
-
const
|
|
3208
|
-
|
|
3209
|
-
childWidths[
|
|
3212
|
+
const widthPerFlex = Math.max(1, remainingWidth / flexIndices.size);
|
|
3213
|
+
flexIndices.forEach((idx) => {
|
|
3214
|
+
childWidths[idx] = widthPerFlex;
|
|
3210
3215
|
});
|
|
3211
3216
|
}
|
|
3212
3217
|
children.forEach((childRef, index) => {
|
|
3213
3218
|
const childNode = this.nodes[childRef.ref];
|
|
3214
|
-
let childHeight = this.getComponentHeight();
|
|
3215
3219
|
const childWidth = childWidths[index];
|
|
3220
|
+
let childHeight = this.getComponentHeight();
|
|
3216
3221
|
if (explicitHeightFlags[index] && childNode?.kind === "component") {
|
|
3217
3222
|
childHeight = Number(childNode.props.height);
|
|
3218
3223
|
} else if (childNode?.kind === "container") {
|
|
@@ -3220,21 +3225,37 @@ var LayoutEngine = class {
|
|
|
3220
3225
|
} else if (childNode?.kind === "component") {
|
|
3221
3226
|
childHeight = this.getIntrinsicComponentHeight(childNode, childWidth);
|
|
3222
3227
|
}
|
|
3228
|
+
childHeights[index] = childHeight;
|
|
3223
3229
|
stackHeight = Math.max(stackHeight, childHeight);
|
|
3224
3230
|
});
|
|
3225
3231
|
const totalChildWidth = childWidths.reduce((sum, w) => sum + w, 0);
|
|
3226
3232
|
const totalContentWidth = totalChildWidth + totalGapWidth;
|
|
3227
3233
|
let startX = x;
|
|
3228
|
-
|
|
3234
|
+
let dynamicGap = gap;
|
|
3235
|
+
if (justify === "center") {
|
|
3229
3236
|
startX = x + (width - totalContentWidth) / 2;
|
|
3230
|
-
} else if (
|
|
3237
|
+
} else if (justify === "end") {
|
|
3231
3238
|
startX = x + width - totalContentWidth;
|
|
3239
|
+
} else if (justify === "spaceBetween") {
|
|
3240
|
+
startX = x;
|
|
3241
|
+
dynamicGap = children.length > 1 ? (width - totalChildWidth) / (children.length - 1) : 0;
|
|
3242
|
+
} else if (justify === "spaceAround") {
|
|
3243
|
+
const spacing = children.length > 0 ? (width - totalChildWidth) / children.length : 0;
|
|
3244
|
+
startX = x + spacing / 2;
|
|
3245
|
+
dynamicGap = spacing;
|
|
3232
3246
|
}
|
|
3233
3247
|
let currentX = startX;
|
|
3234
3248
|
children.forEach((childRef, index) => {
|
|
3235
3249
|
const childWidth = childWidths[index];
|
|
3236
|
-
|
|
3237
|
-
|
|
3250
|
+
const childHeight = childHeights[index];
|
|
3251
|
+
let childY = y;
|
|
3252
|
+
if (crossAlign === "center") {
|
|
3253
|
+
childY = y + Math.round((stackHeight - childHeight) / 2);
|
|
3254
|
+
} else if (crossAlign === "end") {
|
|
3255
|
+
childY = y + stackHeight - childHeight;
|
|
3256
|
+
}
|
|
3257
|
+
this.calculateNode(childRef.ref, currentX, childY, childWidth, childHeight, "stack");
|
|
3258
|
+
currentX += childWidth + dynamicGap;
|
|
3238
3259
|
});
|
|
3239
3260
|
}
|
|
3240
3261
|
}
|
|
@@ -3757,6 +3778,47 @@ var LayoutEngine = class {
|
|
|
3757
3778
|
getControlLabelOffset(label) {
|
|
3758
3779
|
return label.trim().length > 0 ? 18 : 0;
|
|
3759
3780
|
}
|
|
3781
|
+
/**
|
|
3782
|
+
* Returns true when a container's width can be calculated from its children
|
|
3783
|
+
* (i.e. it is a horizontal non-stretch stack). False means the container
|
|
3784
|
+
* behaves like `flex-grow:1` and should absorb remaining space.
|
|
3785
|
+
*/
|
|
3786
|
+
containerHasIntrinsicWidth(node) {
|
|
3787
|
+
if (node.kind !== "container") return false;
|
|
3788
|
+
return node.containerType === "stack" && String(node.params.direction || "vertical") === "horizontal" && (node.style.justify || "stretch") !== "stretch";
|
|
3789
|
+
}
|
|
3790
|
+
/**
|
|
3791
|
+
* Returns the natural (intrinsic) width of any node — component or container.
|
|
3792
|
+
* For horizontal non-stretch containers the width is the sum of their children's
|
|
3793
|
+
* intrinsic widths plus gaps, capped at `availableWidth`. All other containers
|
|
3794
|
+
* are assumed to take the full available width (they stretch or grow).
|
|
3795
|
+
*/
|
|
3796
|
+
getIntrinsicWidth(node, availableWidth) {
|
|
3797
|
+
if (!node) return 120;
|
|
3798
|
+
if (node.kind === "component") {
|
|
3799
|
+
return this.getIntrinsicComponentWidth(node);
|
|
3800
|
+
}
|
|
3801
|
+
if (node.kind === "container") {
|
|
3802
|
+
if (this.containerHasIntrinsicWidth(node)) {
|
|
3803
|
+
const gap = this.resolveSpacing(node.style.gap);
|
|
3804
|
+
const padding = this.resolveSpacing(node.style.padding);
|
|
3805
|
+
const innerAvailable = Math.max(0, availableWidth - padding * 2);
|
|
3806
|
+
const children = node.children ?? [];
|
|
3807
|
+
let total = padding * 2;
|
|
3808
|
+
children.forEach((childRef, idx) => {
|
|
3809
|
+
const child = this.nodes[childRef.ref];
|
|
3810
|
+
total += this.getIntrinsicWidth(child, innerAvailable);
|
|
3811
|
+
if (idx < children.length - 1) total += gap;
|
|
3812
|
+
});
|
|
3813
|
+
return Math.min(total, availableWidth);
|
|
3814
|
+
}
|
|
3815
|
+
return availableWidth;
|
|
3816
|
+
}
|
|
3817
|
+
if (node.kind === "instance") {
|
|
3818
|
+
return availableWidth;
|
|
3819
|
+
}
|
|
3820
|
+
return 120;
|
|
3821
|
+
}
|
|
3760
3822
|
getIntrinsicComponentWidth(node) {
|
|
3761
3823
|
if (!node || node.kind !== "component") {
|
|
3762
3824
|
return 120;
|
|
@@ -5617,7 +5679,8 @@ var SVGRenderer = class {
|
|
|
5617
5679
|
const fontSize = this.tokens.text.fontSize;
|
|
5618
5680
|
const lineHeightPx = Math.ceil(fontSize * this.tokens.text.lineHeight);
|
|
5619
5681
|
const lines = this.wrapTextToLines(text, pos.width, fontSize);
|
|
5620
|
-
const
|
|
5682
|
+
const totalTextHeight = lines.length * lineHeightPx;
|
|
5683
|
+
const firstLineY = pos.y + Math.round(Math.max(0, (pos.height - totalTextHeight) / 2)) + fontSize;
|
|
5621
5684
|
const tspans = lines.map(
|
|
5622
5685
|
(line, index) => `<tspan x="${pos.x}" dy="${index === 0 ? 0 : lineHeightPx}">${this.escapeXml(line)}</tspan>`
|
|
5623
5686
|
).join("");
|
|
@@ -5630,8 +5693,9 @@ var SVGRenderer = class {
|
|
|
5630
5693
|
}
|
|
5631
5694
|
renderLabel(node, pos) {
|
|
5632
5695
|
const text = String(node.props.text || "Label");
|
|
5696
|
+
const textY = pos.y + Math.round(pos.height / 2) + 4;
|
|
5633
5697
|
return `<g${this.getDataNodeId(node)}>
|
|
5634
|
-
<text x="${pos.x}" y="${
|
|
5698
|
+
<text x="${pos.x}" y="${textY}"
|
|
5635
5699
|
font-family="Arial, Helvetica, sans-serif"
|
|
5636
5700
|
font-size="12"
|
|
5637
5701
|
fill="${this.renderTheme.textMuted}">${this.escapeXml(text)}</text>
|
|
@@ -6768,8 +6832,8 @@ var SVGRenderer = class {
|
|
|
6768
6832
|
return false;
|
|
6769
6833
|
}
|
|
6770
6834
|
const direction = String(parent.params.direction || "vertical");
|
|
6771
|
-
const
|
|
6772
|
-
return direction === "horizontal" &&
|
|
6835
|
+
const justify = parent.style.justify || "stretch";
|
|
6836
|
+
return direction === "horizontal" && justify === "stretch";
|
|
6773
6837
|
}
|
|
6774
6838
|
buildParentContainerIndex() {
|
|
6775
6839
|
this.parentContainerByChildId.clear();
|
package/dist/index.d.cts
CHANGED
|
@@ -290,8 +290,8 @@ interface IRComponentNode {
|
|
|
290
290
|
interface IRNodeStyle {
|
|
291
291
|
padding?: string;
|
|
292
292
|
gap?: string;
|
|
293
|
-
|
|
294
|
-
|
|
293
|
+
justify?: 'start' | 'center' | 'end' | 'stretch' | 'spaceBetween' | 'spaceAround';
|
|
294
|
+
align?: 'start' | 'center' | 'end';
|
|
295
295
|
background?: string;
|
|
296
296
|
}
|
|
297
297
|
interface IRMeta {
|
|
@@ -480,6 +480,19 @@ declare class LayoutEngine {
|
|
|
480
480
|
private wrapTextToLines;
|
|
481
481
|
private getIntrinsicComponentHeight;
|
|
482
482
|
private getControlLabelOffset;
|
|
483
|
+
/**
|
|
484
|
+
* Returns true when a container's width can be calculated from its children
|
|
485
|
+
* (i.e. it is a horizontal non-stretch stack). False means the container
|
|
486
|
+
* behaves like `flex-grow:1` and should absorb remaining space.
|
|
487
|
+
*/
|
|
488
|
+
private containerHasIntrinsicWidth;
|
|
489
|
+
/**
|
|
490
|
+
* Returns the natural (intrinsic) width of any node — component or container.
|
|
491
|
+
* For horizontal non-stretch containers the width is the sum of their children's
|
|
492
|
+
* intrinsic widths plus gaps, capped at `availableWidth`. All other containers
|
|
493
|
+
* are assumed to take the full available width (they stretch or grow).
|
|
494
|
+
*/
|
|
495
|
+
private getIntrinsicWidth;
|
|
483
496
|
private getIntrinsicComponentWidth;
|
|
484
497
|
private calculateChildHeight;
|
|
485
498
|
private calculateChildWidth;
|
package/dist/index.d.ts
CHANGED
|
@@ -290,8 +290,8 @@ interface IRComponentNode {
|
|
|
290
290
|
interface IRNodeStyle {
|
|
291
291
|
padding?: string;
|
|
292
292
|
gap?: string;
|
|
293
|
-
|
|
294
|
-
|
|
293
|
+
justify?: 'start' | 'center' | 'end' | 'stretch' | 'spaceBetween' | 'spaceAround';
|
|
294
|
+
align?: 'start' | 'center' | 'end';
|
|
295
295
|
background?: string;
|
|
296
296
|
}
|
|
297
297
|
interface IRMeta {
|
|
@@ -480,6 +480,19 @@ declare class LayoutEngine {
|
|
|
480
480
|
private wrapTextToLines;
|
|
481
481
|
private getIntrinsicComponentHeight;
|
|
482
482
|
private getControlLabelOffset;
|
|
483
|
+
/**
|
|
484
|
+
* Returns true when a container's width can be calculated from its children
|
|
485
|
+
* (i.e. it is a horizontal non-stretch stack). False means the container
|
|
486
|
+
* behaves like `flex-grow:1` and should absorb remaining space.
|
|
487
|
+
*/
|
|
488
|
+
private containerHasIntrinsicWidth;
|
|
489
|
+
/**
|
|
490
|
+
* Returns the natural (intrinsic) width of any node — component or container.
|
|
491
|
+
* For horizontal non-stretch containers the width is the sum of their children's
|
|
492
|
+
* intrinsic widths plus gaps, capped at `availableWidth`. All other containers
|
|
493
|
+
* are assumed to take the full available width (they stretch or grow).
|
|
494
|
+
*/
|
|
495
|
+
private getIntrinsicWidth;
|
|
483
496
|
private getIntrinsicComponentWidth;
|
|
484
497
|
private calculateChildHeight;
|
|
485
498
|
private calculateChildWidth;
|
package/dist/index.js
CHANGED
|
@@ -2174,8 +2174,8 @@ var IRStyleSchema = z.object({
|
|
|
2174
2174
|
var IRNodeStyleSchema = z.object({
|
|
2175
2175
|
padding: z.string().optional(),
|
|
2176
2176
|
gap: z.string().optional(),
|
|
2177
|
-
|
|
2178
|
-
|
|
2177
|
+
justify: z.enum(["start", "center", "end", "stretch", "spaceBetween", "spaceAround"]).optional(),
|
|
2178
|
+
align: z.enum(["start", "center", "end"]).optional(),
|
|
2179
2179
|
background: z.string().optional()
|
|
2180
2180
|
});
|
|
2181
2181
|
var IRMetaSchema = z.object({
|
|
@@ -2461,6 +2461,9 @@ ${messages}`);
|
|
|
2461
2461
|
if (layoutParams.gap !== void 0) {
|
|
2462
2462
|
style.gap = String(layoutParams.gap);
|
|
2463
2463
|
}
|
|
2464
|
+
if (layoutParams.justify !== void 0) {
|
|
2465
|
+
style.justify = layoutParams.justify;
|
|
2466
|
+
}
|
|
2464
2467
|
if (layoutParams.align !== void 0) {
|
|
2465
2468
|
style.align = layoutParams.align;
|
|
2466
2469
|
}
|
|
@@ -3107,8 +3110,9 @@ var LayoutEngine = class {
|
|
|
3107
3110
|
}
|
|
3108
3111
|
});
|
|
3109
3112
|
} else {
|
|
3110
|
-
const
|
|
3111
|
-
|
|
3113
|
+
const justify = node.style.justify || "stretch";
|
|
3114
|
+
const crossAlign = node.style.align || "start";
|
|
3115
|
+
if (justify === "stretch") {
|
|
3112
3116
|
let currentX = x;
|
|
3113
3117
|
const childWidth = this.calculateChildWidth(children.length, width, gap);
|
|
3114
3118
|
let stackHeight = 0;
|
|
@@ -3130,43 +3134,44 @@ var LayoutEngine = class {
|
|
|
3130
3134
|
});
|
|
3131
3135
|
} else {
|
|
3132
3136
|
const childWidths = [];
|
|
3137
|
+
const childHeights = [];
|
|
3133
3138
|
const explicitHeightFlags = [];
|
|
3134
|
-
const
|
|
3139
|
+
const flexIndices = /* @__PURE__ */ new Set();
|
|
3135
3140
|
let stackHeight = 0;
|
|
3136
3141
|
children.forEach((childRef, index) => {
|
|
3137
3142
|
const childNode = this.nodes[childRef.ref];
|
|
3138
|
-
let childWidth = this.getIntrinsicComponentWidth(childNode);
|
|
3139
|
-
let childHeight = this.getComponentHeight();
|
|
3140
3143
|
const hasExplicitHeight = childNode?.kind === "component" && !!childNode.props.height;
|
|
3141
3144
|
const hasExplicitWidth = childNode?.kind === "component" && !!childNode.props.width;
|
|
3142
3145
|
const isBlockButton = childNode?.kind === "component" && childNode.componentType === "Button" && !hasExplicitWidth && this.parseBooleanProp(childNode.props.block, false);
|
|
3143
|
-
|
|
3146
|
+
const isFlexContainer = !hasExplicitWidth && childNode?.kind === "container" && !this.containerHasIntrinsicWidth(childNode);
|
|
3147
|
+
let childWidth;
|
|
3148
|
+
if (isBlockButton || isFlexContainer) {
|
|
3144
3149
|
childWidth = 0;
|
|
3145
|
-
|
|
3150
|
+
flexIndices.add(index);
|
|
3146
3151
|
} else if (hasExplicitWidth) {
|
|
3147
3152
|
childWidth = Number(childNode.props.width);
|
|
3148
|
-
}
|
|
3149
|
-
|
|
3150
|
-
childHeight = Number(childNode.props.height);
|
|
3153
|
+
} else {
|
|
3154
|
+
childWidth = this.getIntrinsicWidth(childNode, width);
|
|
3151
3155
|
}
|
|
3152
3156
|
childWidths.push(childWidth);
|
|
3157
|
+
childHeights.push(this.getComponentHeight());
|
|
3153
3158
|
explicitHeightFlags.push(hasExplicitHeight);
|
|
3154
3159
|
});
|
|
3155
3160
|
const totalGapWidth = gap * Math.max(0, children.length - 1);
|
|
3156
|
-
if (
|
|
3161
|
+
if (flexIndices.size > 0) {
|
|
3157
3162
|
const fixedWidth = childWidths.reduce((sum, w, idx) => {
|
|
3158
|
-
return
|
|
3163
|
+
return flexIndices.has(idx) ? sum : sum + w;
|
|
3159
3164
|
}, 0);
|
|
3160
3165
|
const remainingWidth = width - totalGapWidth - fixedWidth;
|
|
3161
|
-
const
|
|
3162
|
-
|
|
3163
|
-
childWidths[
|
|
3166
|
+
const widthPerFlex = Math.max(1, remainingWidth / flexIndices.size);
|
|
3167
|
+
flexIndices.forEach((idx) => {
|
|
3168
|
+
childWidths[idx] = widthPerFlex;
|
|
3164
3169
|
});
|
|
3165
3170
|
}
|
|
3166
3171
|
children.forEach((childRef, index) => {
|
|
3167
3172
|
const childNode = this.nodes[childRef.ref];
|
|
3168
|
-
let childHeight = this.getComponentHeight();
|
|
3169
3173
|
const childWidth = childWidths[index];
|
|
3174
|
+
let childHeight = this.getComponentHeight();
|
|
3170
3175
|
if (explicitHeightFlags[index] && childNode?.kind === "component") {
|
|
3171
3176
|
childHeight = Number(childNode.props.height);
|
|
3172
3177
|
} else if (childNode?.kind === "container") {
|
|
@@ -3174,21 +3179,37 @@ var LayoutEngine = class {
|
|
|
3174
3179
|
} else if (childNode?.kind === "component") {
|
|
3175
3180
|
childHeight = this.getIntrinsicComponentHeight(childNode, childWidth);
|
|
3176
3181
|
}
|
|
3182
|
+
childHeights[index] = childHeight;
|
|
3177
3183
|
stackHeight = Math.max(stackHeight, childHeight);
|
|
3178
3184
|
});
|
|
3179
3185
|
const totalChildWidth = childWidths.reduce((sum, w) => sum + w, 0);
|
|
3180
3186
|
const totalContentWidth = totalChildWidth + totalGapWidth;
|
|
3181
3187
|
let startX = x;
|
|
3182
|
-
|
|
3188
|
+
let dynamicGap = gap;
|
|
3189
|
+
if (justify === "center") {
|
|
3183
3190
|
startX = x + (width - totalContentWidth) / 2;
|
|
3184
|
-
} else if (
|
|
3191
|
+
} else if (justify === "end") {
|
|
3185
3192
|
startX = x + width - totalContentWidth;
|
|
3193
|
+
} else if (justify === "spaceBetween") {
|
|
3194
|
+
startX = x;
|
|
3195
|
+
dynamicGap = children.length > 1 ? (width - totalChildWidth) / (children.length - 1) : 0;
|
|
3196
|
+
} else if (justify === "spaceAround") {
|
|
3197
|
+
const spacing = children.length > 0 ? (width - totalChildWidth) / children.length : 0;
|
|
3198
|
+
startX = x + spacing / 2;
|
|
3199
|
+
dynamicGap = spacing;
|
|
3186
3200
|
}
|
|
3187
3201
|
let currentX = startX;
|
|
3188
3202
|
children.forEach((childRef, index) => {
|
|
3189
3203
|
const childWidth = childWidths[index];
|
|
3190
|
-
|
|
3191
|
-
|
|
3204
|
+
const childHeight = childHeights[index];
|
|
3205
|
+
let childY = y;
|
|
3206
|
+
if (crossAlign === "center") {
|
|
3207
|
+
childY = y + Math.round((stackHeight - childHeight) / 2);
|
|
3208
|
+
} else if (crossAlign === "end") {
|
|
3209
|
+
childY = y + stackHeight - childHeight;
|
|
3210
|
+
}
|
|
3211
|
+
this.calculateNode(childRef.ref, currentX, childY, childWidth, childHeight, "stack");
|
|
3212
|
+
currentX += childWidth + dynamicGap;
|
|
3192
3213
|
});
|
|
3193
3214
|
}
|
|
3194
3215
|
}
|
|
@@ -3711,6 +3732,47 @@ var LayoutEngine = class {
|
|
|
3711
3732
|
getControlLabelOffset(label) {
|
|
3712
3733
|
return label.trim().length > 0 ? 18 : 0;
|
|
3713
3734
|
}
|
|
3735
|
+
/**
|
|
3736
|
+
* Returns true when a container's width can be calculated from its children
|
|
3737
|
+
* (i.e. it is a horizontal non-stretch stack). False means the container
|
|
3738
|
+
* behaves like `flex-grow:1` and should absorb remaining space.
|
|
3739
|
+
*/
|
|
3740
|
+
containerHasIntrinsicWidth(node) {
|
|
3741
|
+
if (node.kind !== "container") return false;
|
|
3742
|
+
return node.containerType === "stack" && String(node.params.direction || "vertical") === "horizontal" && (node.style.justify || "stretch") !== "stretch";
|
|
3743
|
+
}
|
|
3744
|
+
/**
|
|
3745
|
+
* Returns the natural (intrinsic) width of any node — component or container.
|
|
3746
|
+
* For horizontal non-stretch containers the width is the sum of their children's
|
|
3747
|
+
* intrinsic widths plus gaps, capped at `availableWidth`. All other containers
|
|
3748
|
+
* are assumed to take the full available width (they stretch or grow).
|
|
3749
|
+
*/
|
|
3750
|
+
getIntrinsicWidth(node, availableWidth) {
|
|
3751
|
+
if (!node) return 120;
|
|
3752
|
+
if (node.kind === "component") {
|
|
3753
|
+
return this.getIntrinsicComponentWidth(node);
|
|
3754
|
+
}
|
|
3755
|
+
if (node.kind === "container") {
|
|
3756
|
+
if (this.containerHasIntrinsicWidth(node)) {
|
|
3757
|
+
const gap = this.resolveSpacing(node.style.gap);
|
|
3758
|
+
const padding = this.resolveSpacing(node.style.padding);
|
|
3759
|
+
const innerAvailable = Math.max(0, availableWidth - padding * 2);
|
|
3760
|
+
const children = node.children ?? [];
|
|
3761
|
+
let total = padding * 2;
|
|
3762
|
+
children.forEach((childRef, idx) => {
|
|
3763
|
+
const child = this.nodes[childRef.ref];
|
|
3764
|
+
total += this.getIntrinsicWidth(child, innerAvailable);
|
|
3765
|
+
if (idx < children.length - 1) total += gap;
|
|
3766
|
+
});
|
|
3767
|
+
return Math.min(total, availableWidth);
|
|
3768
|
+
}
|
|
3769
|
+
return availableWidth;
|
|
3770
|
+
}
|
|
3771
|
+
if (node.kind === "instance") {
|
|
3772
|
+
return availableWidth;
|
|
3773
|
+
}
|
|
3774
|
+
return 120;
|
|
3775
|
+
}
|
|
3714
3776
|
getIntrinsicComponentWidth(node) {
|
|
3715
3777
|
if (!node || node.kind !== "component") {
|
|
3716
3778
|
return 120;
|
|
@@ -5571,7 +5633,8 @@ var SVGRenderer = class {
|
|
|
5571
5633
|
const fontSize = this.tokens.text.fontSize;
|
|
5572
5634
|
const lineHeightPx = Math.ceil(fontSize * this.tokens.text.lineHeight);
|
|
5573
5635
|
const lines = this.wrapTextToLines(text, pos.width, fontSize);
|
|
5574
|
-
const
|
|
5636
|
+
const totalTextHeight = lines.length * lineHeightPx;
|
|
5637
|
+
const firstLineY = pos.y + Math.round(Math.max(0, (pos.height - totalTextHeight) / 2)) + fontSize;
|
|
5575
5638
|
const tspans = lines.map(
|
|
5576
5639
|
(line, index) => `<tspan x="${pos.x}" dy="${index === 0 ? 0 : lineHeightPx}">${this.escapeXml(line)}</tspan>`
|
|
5577
5640
|
).join("");
|
|
@@ -5584,8 +5647,9 @@ var SVGRenderer = class {
|
|
|
5584
5647
|
}
|
|
5585
5648
|
renderLabel(node, pos) {
|
|
5586
5649
|
const text = String(node.props.text || "Label");
|
|
5650
|
+
const textY = pos.y + Math.round(pos.height / 2) + 4;
|
|
5587
5651
|
return `<g${this.getDataNodeId(node)}>
|
|
5588
|
-
<text x="${pos.x}" y="${
|
|
5652
|
+
<text x="${pos.x}" y="${textY}"
|
|
5589
5653
|
font-family="Arial, Helvetica, sans-serif"
|
|
5590
5654
|
font-size="12"
|
|
5591
5655
|
fill="${this.renderTheme.textMuted}">${this.escapeXml(text)}</text>
|
|
@@ -6722,8 +6786,8 @@ var SVGRenderer = class {
|
|
|
6722
6786
|
return false;
|
|
6723
6787
|
}
|
|
6724
6788
|
const direction = String(parent.params.direction || "vertical");
|
|
6725
|
-
const
|
|
6726
|
-
return direction === "horizontal" &&
|
|
6789
|
+
const justify = parent.style.justify || "stretch";
|
|
6790
|
+
return direction === "horizontal" && justify === "stretch";
|
|
6727
6791
|
}
|
|
6728
6792
|
buildParentContainerIndex() {
|
|
6729
6793
|
this.parentContainerByChildId.clear();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wire-dsl/engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "WireDSL engine - Parser, IR generator, layout engine, and SVG renderer (browser-safe, pure JS/TS)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"chevrotain": "11.1.1",
|
|
34
34
|
"zod": "4.3.6",
|
|
35
|
-
"@wire-dsl/language-support": "0.
|
|
35
|
+
"@wire-dsl/language-support": "0.6.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "25.2.0",
|