@codehz/draw-call 0.5.0 → 0.5.2

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/node/index.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
1
2
  let _napi_rs_canvas = require("@napi-rs/canvas");
2
3
 
3
4
  //#region src/compat/index.ts
@@ -78,6 +79,19 @@ function normalizeBorderRadius(value) {
78
79
 
79
80
  //#endregion
80
81
  //#region src/layout/components/box.ts
82
+ /**
83
+ * 安全获取元素的 margin
84
+ * Transform 元素没有 margin,返回默认 spacing
85
+ */
86
+ function getElementMargin$2(element) {
87
+ if (element.type === "transform") return {
88
+ top: 0,
89
+ right: 0,
90
+ bottom: 0,
91
+ left: 0
92
+ };
93
+ return normalizeSpacing(element.margin);
94
+ }
81
95
  function calcEffectiveSize(element, padding, availableWidth) {
82
96
  return {
83
97
  width: typeof element.width === "number" ? element.width - padding.left - padding.right : availableWidth > 0 ? availableWidth : 0,
@@ -87,7 +101,7 @@ function calcEffectiveSize(element, padding, availableWidth) {
87
101
  function collectChildSizes(children, ctx, availableWidth, padding, measureChild) {
88
102
  const childSizes = [];
89
103
  for (const child of children) {
90
- const childMargin = normalizeSpacing(child.margin);
104
+ const childMargin = getElementMargin$2(child);
91
105
  const childSize = measureChild(child, ctx, availableWidth - padding.left - padding.right - childMargin.left - childMargin.right);
92
106
  childSizes.push({
93
107
  width: childSize.width,
@@ -156,7 +170,7 @@ function measureBoxSize(element, ctx, availableWidth, measureChild) {
156
170
  contentHeight = wrapped.height;
157
171
  } else for (let i = 0; i < children.length; i++) {
158
172
  const child = children[i];
159
- const childMargin = normalizeSpacing(child.margin);
173
+ const childMargin = getElementMargin$2(child);
160
174
  const childSize = measureChild(child, ctx, availableWidth - padding.left - padding.right - childMargin.left - childMargin.right);
161
175
  if (isRow) {
162
176
  contentWidth += childSize.width + childMargin.left + childMargin.right;
@@ -390,6 +404,19 @@ function wrapRichText(ctx, spans, maxWidth, lineHeightScale = 1.2, elementStyle
390
404
  //#endregion
391
405
  //#region src/layout/components/stack.ts
392
406
  /**
407
+ * 安全获取元素的 margin
408
+ * Transform 元素没有 margin,返回默认 spacing
409
+ */
410
+ function getElementMargin$1(element) {
411
+ if (element.type === "transform") return {
412
+ top: 0,
413
+ right: 0,
414
+ bottom: 0,
415
+ left: 0
416
+ };
417
+ return normalizeSpacing(element.margin);
418
+ }
419
+ /**
393
420
  * 测量 Stack 元素的固有尺寸
394
421
  */
395
422
  function measureStackSize(element, ctx, availableWidth, measureChild) {
@@ -398,7 +425,7 @@ function measureStackSize(element, ctx, availableWidth, measureChild) {
398
425
  let contentHeight = 0;
399
426
  const children = element.children ?? [];
400
427
  for (const child of children) {
401
- const childMargin = normalizeSpacing(child.margin);
428
+ const childMargin = getElementMargin$1(child);
402
429
  const childSize = measureChild(child, ctx, availableWidth - padding.left - padding.right - childMargin.left - childMargin.right);
403
430
  contentWidth = Math.max(contentWidth, childSize.width + childMargin.left + childMargin.right);
404
431
  contentHeight = Math.max(contentHeight, childSize.height + childMargin.top + childMargin.bottom);
@@ -637,18 +664,79 @@ function sizeNeedsParent(size) {
637
664
 
638
665
  //#endregion
639
666
  //#region src/layout/engine.ts
667
+ /**
668
+ * 类型守卫:检查 Element 是否为 LayoutElement(非 Transform)
669
+ * 由于 Transform 元素在 computeLayoutImpl 开始时被处理,
670
+ * 此时只应处理 LayoutElement
671
+ */
672
+ function assertLayoutElement(element) {
673
+ if (element.type === "transform") throw new Error("Transform elements should be handled at entry point");
674
+ }
675
+ /**
676
+ * 安全获取元素的 margin
677
+ * Transform 元素没有 margin,返回默认 spacing
678
+ */
679
+ function getElementMargin(element) {
680
+ if (element.type === "transform") return {
681
+ top: 0,
682
+ right: 0,
683
+ bottom: 0,
684
+ left: 0
685
+ };
686
+ return normalizeSpacing(element.margin);
687
+ }
688
+ /**
689
+ * 安全获取元素的布局属性(width, height, flex等)
690
+ * Transform 元素这些属性为 undefined
691
+ */
692
+ function getElementLayoutProps(element) {
693
+ if (element.type === "transform") return {
694
+ width: void 0,
695
+ height: void 0,
696
+ flex: void 0,
697
+ minWidth: void 0,
698
+ maxWidth: void 0,
699
+ minHeight: void 0,
700
+ maxHeight: void 0,
701
+ alignSelf: void 0
702
+ };
703
+ const le = element;
704
+ return {
705
+ width: le.width,
706
+ height: le.height,
707
+ flex: le.flex,
708
+ minWidth: le.minWidth,
709
+ maxWidth: le.maxWidth,
710
+ minHeight: le.minHeight,
711
+ maxHeight: le.maxHeight,
712
+ alignSelf: le.alignSelf
713
+ };
714
+ }
715
+ /**
716
+ * 布局计算主函数
717
+ * 内部使用 Element 类型以支持 Transform,外部通过 LayoutElement 约束类型
718
+ */
640
719
  function computeLayout(element, ctx, constraints, x = 0, y = 0) {
641
- const margin = normalizeSpacing(element.margin);
642
- const padding = normalizeSpacing("padding" in element ? element.padding : void 0);
720
+ return computeLayoutImpl(element, ctx, constraints, x, y);
721
+ }
722
+ /**
723
+ * 内部实现函数,处理所有元素类型包括 Transform
724
+ */
725
+ function computeLayoutImpl(element, ctx, constraints, x = 0, y = 0) {
726
+ if (element.type === "transform") return computeLayoutImpl(element.children, ctx, constraints, x, y);
727
+ assertLayoutElement(element);
728
+ const layoutElement = element;
729
+ const margin = normalizeSpacing(layoutElement.margin);
730
+ const padding = normalizeSpacing("padding" in layoutElement ? layoutElement.padding : void 0);
643
731
  const availableWidth = constraints.maxWidth - margin.left - margin.right;
644
732
  const availableHeight = constraints.maxHeight - margin.top - margin.bottom;
645
- const intrinsic = measureIntrinsicSize(element, ctx, availableWidth);
646
- let width = constraints.minWidth === constraints.maxWidth && constraints.minWidth > 0 ? constraints.maxWidth - margin.left - margin.right : resolveSize(element.width, availableWidth, intrinsic.width);
647
- let height = constraints.minHeight === constraints.maxHeight && constraints.minHeight > 0 ? constraints.maxHeight - margin.top - margin.bottom : resolveSize(element.height, availableHeight, intrinsic.height);
648
- if (element.minWidth !== void 0) width = Math.max(width, element.minWidth);
649
- if (element.maxWidth !== void 0) width = Math.min(width, element.maxWidth);
650
- if (element.minHeight !== void 0) height = Math.max(height, element.minHeight);
651
- if (element.maxHeight !== void 0) height = Math.min(height, element.maxHeight);
733
+ const intrinsic = measureIntrinsicSize(layoutElement, ctx, availableWidth);
734
+ let width = constraints.minWidth === constraints.maxWidth && constraints.minWidth > 0 ? constraints.maxWidth - margin.left - margin.right : resolveSize(layoutElement.width, availableWidth, intrinsic.width);
735
+ let height = constraints.minHeight === constraints.maxHeight && constraints.minHeight > 0 ? constraints.maxHeight - margin.top - margin.bottom : resolveSize(layoutElement.height, availableHeight, intrinsic.height);
736
+ if (layoutElement.minWidth !== void 0) width = Math.max(width, layoutElement.minWidth);
737
+ if (layoutElement.maxWidth !== void 0) width = Math.min(width, layoutElement.maxWidth);
738
+ if (layoutElement.minHeight !== void 0) height = Math.max(height, layoutElement.minHeight);
739
+ if (layoutElement.maxHeight !== void 0) height = Math.min(height, layoutElement.maxHeight);
652
740
  const actualX = x + margin.left;
653
741
  const actualY = y + margin.top;
654
742
  const contentX = actualX + padding.left;
@@ -656,7 +744,7 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
656
744
  const contentWidth = width - padding.left - padding.right;
657
745
  const contentHeight = height - padding.top - padding.bottom;
658
746
  const node = {
659
- element,
747
+ element: layoutElement,
660
748
  layout: {
661
749
  x: actualX,
662
750
  y: actualY,
@@ -669,14 +757,14 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
669
757
  },
670
758
  children: []
671
759
  };
672
- if (element.type === "text") {
673
- const font = element.font ?? {};
674
- if (element.wrap && contentWidth > 0) {
675
- let { lines, offsets } = wrapText(ctx, element.content, contentWidth, font);
676
- if (element.maxLines && lines.length > element.maxLines) {
677
- lines = lines.slice(0, element.maxLines);
678
- offsets = offsets.slice(0, element.maxLines);
679
- if (element.ellipsis && lines.length > 0) {
760
+ if (layoutElement.type === "text") {
761
+ const font = layoutElement.font ?? {};
762
+ if (layoutElement.wrap && contentWidth > 0) {
763
+ let { lines, offsets } = wrapText(ctx, layoutElement.content, contentWidth, font);
764
+ if (layoutElement.maxLines && lines.length > layoutElement.maxLines) {
765
+ lines = lines.slice(0, layoutElement.maxLines);
766
+ offsets = offsets.slice(0, layoutElement.maxLines);
767
+ if (layoutElement.ellipsis && lines.length > 0) {
680
768
  const lastIdx = lines.length - 1;
681
769
  const truncated = truncateText(ctx, lines[lastIdx], contentWidth, font);
682
770
  lines[lastIdx] = truncated.text;
@@ -686,17 +774,17 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
686
774
  node.lines = lines;
687
775
  node.lineOffsets = offsets;
688
776
  } else {
689
- const { text, offset } = truncateText(ctx, element.content, contentWidth > 0 && element.ellipsis ? contentWidth : Infinity, font);
777
+ const { text, offset } = truncateText(ctx, layoutElement.content, contentWidth > 0 && layoutElement.ellipsis ? contentWidth : Infinity, font);
690
778
  node.lines = [text];
691
779
  node.lineOffsets = [offset];
692
780
  }
693
781
  }
694
- if (element.type === "richtext") {
695
- const lineHeight = element.lineHeight ?? 1.2;
696
- let lines = wrapRichText(ctx, element.spans, contentWidth, lineHeight);
697
- if (element.maxLines && lines.length > element.maxLines) {
698
- lines = lines.slice(0, element.maxLines);
699
- if (element.ellipsis && lines.length > 0) {
782
+ if (layoutElement.type === "richtext") {
783
+ const lineHeight = layoutElement.lineHeight ?? 1.2;
784
+ let lines = wrapRichText(ctx, layoutElement.spans, contentWidth, lineHeight);
785
+ if (layoutElement.maxLines && lines.length > layoutElement.maxLines) {
786
+ lines = lines.slice(0, layoutElement.maxLines);
787
+ if (layoutElement.ellipsis && lines.length > 0) {
700
788
  const lastLine = lines[lines.length - 1];
701
789
  if (lastLine.segments.length > 0) {
702
790
  const lastSeg = lastLine.segments[lastLine.segments.length - 1];
@@ -708,19 +796,19 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
708
796
  }
709
797
  node.richLines = lines;
710
798
  }
711
- if (element.type === "box" || element.type === "stack") {
712
- const children = element.children ?? [];
713
- if (element.type === "stack") {
714
- const stackAlign = element.align ?? "start";
715
- const stackJustify = element.justify ?? "start";
799
+ if (layoutElement.type === "box" || layoutElement.type === "stack") {
800
+ const children = layoutElement.children ?? [];
801
+ if (layoutElement.type === "stack") {
802
+ const stackAlign = layoutElement.align ?? "start";
803
+ const stackJustify = layoutElement.justify ?? "start";
716
804
  for (const child of children) {
717
- const childNode = computeLayout(child, ctx, {
805
+ const childNode = computeLayoutImpl(child, ctx, {
718
806
  minWidth: 0,
719
807
  maxWidth: contentWidth,
720
808
  minHeight: 0,
721
809
  maxHeight: contentHeight
722
810
  }, contentX, contentY);
723
- const childMargin = normalizeSpacing(child.margin);
811
+ const childMargin = getElementMargin(child);
724
812
  const childOuterWidth = childNode.layout.width + childMargin.left + childMargin.right;
725
813
  const childOuterHeight = childNode.layout.height + childMargin.top + childMargin.bottom;
726
814
  let offsetX = 0;
@@ -733,19 +821,21 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
733
821
  node.children.push(childNode);
734
822
  }
735
823
  } else {
736
- const direction = element.direction ?? "row";
737
- const justify = element.justify ?? "start";
738
- const align = element.align ?? "stretch";
739
- const gap = element.gap ?? 0;
740
- const wrap = element.wrap ?? false;
824
+ const boxElement = layoutElement;
825
+ const direction = boxElement.direction ?? "row";
826
+ const justify = boxElement.justify ?? "start";
827
+ const align = boxElement.align ?? "stretch";
828
+ const gap = boxElement.gap ?? 0;
829
+ const wrap = boxElement.wrap ?? false;
741
830
  const isRow = direction === "row" || direction === "row-reverse";
742
831
  const isReverse = direction === "row-reverse" || direction === "column-reverse";
743
832
  const getContentMainSize = () => isRow ? contentWidth : contentHeight;
744
833
  const getContentCrossSize = () => isRow ? contentHeight : contentWidth;
745
834
  const childInfos = [];
746
835
  for (const child of children) {
747
- const childMargin = normalizeSpacing(child.margin);
748
- const childFlex = child.flex ?? 0;
836
+ const childMargin = getElementMargin(child);
837
+ const childProps = getElementLayoutProps(child);
838
+ const childFlex = childProps.flex ?? 0;
749
839
  if (childFlex > 0) childInfos.push({
750
840
  element: child,
751
841
  width: 0,
@@ -755,10 +845,10 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
755
845
  });
756
846
  else {
757
847
  const size = measureIntrinsicSize(child, ctx, contentWidth - childMargin.left - childMargin.right);
758
- const shouldStretchWidth = !isRow && child.width === void 0 && align === "stretch";
759
- const shouldStretchHeight = isRow && child.height === void 0 && align === "stretch";
760
- let w = sizeNeedsParent(child.width) ? resolveSize(child.width, contentWidth - childMargin.left - childMargin.right, size.width) : resolveSize(child.width, 0, size.width);
761
- let h = sizeNeedsParent(child.height) ? resolveSize(child.height, contentHeight - childMargin.top - childMargin.bottom, size.height) : resolveSize(child.height, 0, size.height);
848
+ const shouldStretchWidth = !isRow && childProps.width === void 0 && align === "stretch";
849
+ const shouldStretchHeight = isRow && childProps.height === void 0 && align === "stretch";
850
+ let w = sizeNeedsParent(childProps.width) ? resolveSize(childProps.width, contentWidth - childMargin.left - childMargin.right, size.width) : resolveSize(childProps.width, 0, size.width);
851
+ let h = sizeNeedsParent(childProps.height) ? resolveSize(childProps.height, contentHeight - childMargin.top - childMargin.bottom, size.height) : resolveSize(childProps.height, 0, size.height);
762
852
  if (shouldStretchWidth && !wrap) w = contentWidth - childMargin.left - childMargin.right;
763
853
  if (shouldStretchHeight && !wrap) h = contentHeight - childMargin.top - childMargin.bottom;
764
854
  childInfos.push({
@@ -799,14 +889,15 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
799
889
  const availableForFlex = Math.max(0, mainAxisSize - totalFixed - totalGap);
800
890
  for (const info of lineInfos) if (info.flex > 0) {
801
891
  const flexSize = totalFlex > 0 ? availableForFlex * info.flex / totalFlex : 0;
892
+ const childProps = getElementLayoutProps(info.element);
802
893
  if (isRow) {
803
894
  info.width = flexSize;
804
895
  const size = measureIntrinsicSize(info.element, ctx, flexSize);
805
- info.height = sizeNeedsParent(info.element.height) ? resolveSize(info.element.height, contentHeight - info.margin.top - info.margin.bottom, size.height) : resolveSize(info.element.height, 0, size.height);
896
+ info.height = sizeNeedsParent(childProps.height) ? resolveSize(childProps.height, contentHeight - info.margin.top - info.margin.bottom, size.height) : resolveSize(childProps.height, 0, size.height);
806
897
  } else {
807
898
  info.height = flexSize;
808
899
  const size = measureIntrinsicSize(info.element, ctx, contentWidth - info.margin.left - info.margin.right);
809
- info.width = sizeNeedsParent(info.element.width) ? resolveSize(info.element.width, contentWidth - info.margin.left - info.margin.right, size.width) : resolveSize(info.element.width, 0, size.width);
900
+ info.width = sizeNeedsParent(childProps.width) ? resolveSize(childProps.width, contentWidth - info.margin.left - info.margin.right, size.width) : resolveSize(childProps.width, 0, size.width);
810
901
  }
811
902
  }
812
903
  }
@@ -857,17 +948,18 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
857
948
  const orderedInfos = isReverse ? [...lineInfos].reverse() : lineInfos;
858
949
  for (let i = 0; i < orderedInfos.length; i++) {
859
950
  const info = orderedInfos[i];
951
+ const childProps = getElementLayoutProps(info.element);
860
952
  const crossAxisSize = wrap ? lineCrossSize : getContentCrossSize();
861
953
  const childCrossSize = isRow ? info.height + info.margin.top + info.margin.bottom : info.width + info.margin.left + info.margin.right;
862
954
  let itemCrossOffset = 0;
863
- const effectiveAlign = info.element.alignSelf ?? align;
955
+ const effectiveAlign = childProps.alignSelf ?? align;
864
956
  if (effectiveAlign === "start") itemCrossOffset = 0;
865
957
  else if (effectiveAlign === "end") itemCrossOffset = crossAxisSize - childCrossSize;
866
958
  else if (effectiveAlign === "center") itemCrossOffset = (crossAxisSize - childCrossSize) / 2;
867
959
  else if (effectiveAlign === "stretch") {
868
960
  itemCrossOffset = 0;
869
- if (isRow && info.element.height === void 0) info.height = crossAxisSize - info.margin.top - info.margin.bottom;
870
- else if (!isRow && info.element.width === void 0) info.width = crossAxisSize - info.margin.left - info.margin.right;
961
+ if (isRow && childProps.height === void 0) info.height = crossAxisSize - info.margin.top - info.margin.bottom;
962
+ else if (!isRow && childProps.width === void 0) info.width = crossAxisSize - info.margin.left - info.margin.right;
871
963
  }
872
964
  const childX = isRow ? contentX + mainOffset + info.margin.left : contentX + crossOffset + itemCrossOffset + info.margin.left;
873
965
  const childY = isRow ? contentY + crossOffset + itemCrossOffset + info.margin.top : contentY + mainOffset + info.margin.top;
@@ -876,26 +968,29 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
876
968
  let minHeight = 0;
877
969
  let maxHeight = info.height;
878
970
  let shouldStretchCross = false;
879
- if (info.flex > 0) if (isRow) {
880
- minWidth = maxWidth = info.width;
881
- if (info.element.height === void 0 && align === "stretch") {
882
- minHeight = info.height;
883
- maxHeight = element.height !== void 0 ? info.height : Infinity;
884
- shouldStretchCross = true;
971
+ if (info.flex > 0) {
972
+ const childProps = getElementLayoutProps(info.element);
973
+ if (isRow) {
974
+ minWidth = maxWidth = info.width;
975
+ if (childProps.height === void 0 && align === "stretch") {
976
+ minHeight = info.height;
977
+ maxHeight = boxElement.height !== void 0 ? info.height : Infinity;
978
+ shouldStretchCross = true;
979
+ }
980
+ } else {
981
+ minHeight = maxHeight = info.height;
982
+ if (childProps.width === void 0 && align === "stretch") {
983
+ minWidth = info.width;
984
+ maxWidth = boxElement.width !== void 0 ? info.width : Infinity;
985
+ shouldStretchCross = true;
986
+ }
885
987
  }
886
988
  } else {
887
- minHeight = maxHeight = info.height;
888
- if (info.element.width === void 0 && align === "stretch") {
889
- minWidth = info.width;
890
- maxWidth = element.width !== void 0 ? info.width : Infinity;
891
- shouldStretchCross = true;
892
- }
989
+ const childProps = getElementLayoutProps(info.element);
990
+ if (!isRow && childProps.width === void 0 && align === "stretch") minWidth = maxWidth = crossAxisSize - info.margin.left - info.margin.right;
991
+ if (isRow && childProps.height === void 0 && align === "stretch") minHeight = maxHeight = crossAxisSize - info.margin.top - info.margin.bottom;
893
992
  }
894
- else {
895
- if (!isRow && info.element.width === void 0 && align === "stretch") minWidth = maxWidth = crossAxisSize - info.margin.left - info.margin.right;
896
- if (isRow && info.element.height === void 0 && align === "stretch") minHeight = maxHeight = crossAxisSize - info.margin.top - info.margin.bottom;
897
- }
898
- const childNode = computeLayout(info.element, ctx, {
993
+ const childNode = computeLayoutImpl(info.element, ctx, {
899
994
  minWidth,
900
995
  maxWidth,
901
996
  minHeight,
@@ -918,12 +1013,12 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
918
1013
  crossOffset += lineCrossSize;
919
1014
  if (lineIndex < lines.length - 1) crossOffset += gap;
920
1015
  }
921
- if (wrap && element.height === void 0 && isRow) {
1016
+ if (wrap && boxElement.height === void 0 && isRow) {
922
1017
  const actualContentHeight = crossOffset;
923
1018
  const actualHeight = actualContentHeight + padding.top + padding.bottom;
924
1019
  node.layout.height = actualHeight;
925
1020
  node.layout.contentHeight = actualContentHeight;
926
- } else if (wrap && element.width === void 0 && !isRow) {
1021
+ } else if (wrap && boxElement.width === void 0 && !isRow) {
927
1022
  const actualContentWidth = crossOffset;
928
1023
  const actualWidth = actualContentWidth + padding.left + padding.right;
929
1024
  node.layout.width = actualWidth;
@@ -932,7 +1027,7 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
932
1027
  if (!wrap) {
933
1028
  let maxChildCrossSize = 0;
934
1029
  for (const childNode of node.children) {
935
- const childMargin = normalizeSpacing(childNode.element.margin);
1030
+ const childMargin = getElementMargin(childNode.element);
936
1031
  if (isRow) {
937
1032
  const childOuterHeight = childNode.layout.height + childMargin.top + childMargin.bottom;
938
1033
  maxChildCrossSize = Math.max(maxChildCrossSize, childOuterHeight);
@@ -941,13 +1036,13 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
941
1036
  maxChildCrossSize = Math.max(maxChildCrossSize, childOuterWidth);
942
1037
  }
943
1038
  }
944
- if (isRow && element.height === void 0) {
1039
+ if (isRow && boxElement.height === void 0) {
945
1040
  const actualHeight = maxChildCrossSize + padding.top + padding.bottom;
946
1041
  if (actualHeight > node.layout.height) {
947
1042
  node.layout.height = actualHeight;
948
1043
  node.layout.contentHeight = maxChildCrossSize;
949
1044
  }
950
- } else if (!isRow && element.width === void 0) {
1045
+ } else if (!isRow && boxElement.width === void 0) {
951
1046
  const actualWidth = maxChildCrossSize + padding.left + padding.right;
952
1047
  if (actualWidth > node.layout.width) {
953
1048
  node.layout.width = actualWidth;
@@ -957,48 +1052,24 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
957
1052
  }
958
1053
  if (isReverse) node.children.reverse();
959
1054
  }
960
- } else if (element.type === "transform") {
961
- const child = element.children;
962
- if (child) {
963
- const childMargin = normalizeSpacing(child.margin);
964
- const childNode = computeLayout(child, ctx, {
965
- minWidth: 0,
966
- maxWidth: contentWidth,
967
- minHeight: 0,
968
- maxHeight: contentHeight
969
- }, contentX, contentY);
970
- node.children.push(childNode);
971
- if (element.width === void 0) {
972
- const childOuterWidth = childNode.layout.width + childMargin.left + childMargin.right;
973
- const actualWidth = childOuterWidth + padding.left + padding.right;
974
- node.layout.width = actualWidth;
975
- node.layout.contentWidth = childOuterWidth;
976
- }
977
- if (element.height === void 0) {
978
- const childOuterHeight = childNode.layout.height + childMargin.top + childMargin.bottom;
979
- const actualHeight = childOuterHeight + padding.top + padding.bottom;
980
- node.layout.height = actualHeight;
981
- node.layout.contentHeight = childOuterHeight;
982
- }
983
- }
984
- } else if (element.type === "customdraw") {
985
- const child = element.children;
1055
+ } else if (layoutElement.type === "customdraw") {
1056
+ const child = layoutElement.children;
986
1057
  if (child) {
987
1058
  const childMargin = normalizeSpacing(child.margin);
988
- const childNode = computeLayout(child, ctx, {
1059
+ const childNode = computeLayoutImpl(child, ctx, {
989
1060
  minWidth: 0,
990
1061
  maxWidth: contentWidth,
991
1062
  minHeight: 0,
992
1063
  maxHeight: contentHeight
993
1064
  }, contentX, contentY);
994
1065
  node.children.push(childNode);
995
- if (element.width === void 0) {
1066
+ if (layoutElement.width === void 0) {
996
1067
  const childOuterWidth = childNode.layout.width + childMargin.left + childMargin.right;
997
1068
  const actualWidth = childOuterWidth + padding.left + padding.right;
998
1069
  node.layout.width = actualWidth;
999
1070
  node.layout.contentWidth = childOuterWidth;
1000
1071
  }
1001
- if (element.height === void 0) {
1072
+ if (layoutElement.height === void 0) {
1002
1073
  const childOuterHeight = childNode.layout.height + childMargin.top + childMargin.bottom;
1003
1074
  const actualHeight = childOuterHeight + padding.top + padding.bottom;
1004
1075
  node.layout.height = actualHeight;
@@ -2008,10 +2079,12 @@ function getElementType(element) {
2008
2079
  switch (element.type) {
2009
2080
  case "box": return "Box";
2010
2081
  case "text": return `Text "${element.content.slice(0, 20)}${element.content.length > 20 ? "..." : ""}"`;
2082
+ case "richtext": return "RichText";
2011
2083
  case "stack": return "Stack";
2012
2084
  case "image": return "Image";
2013
2085
  case "svg": return "Svg";
2014
- default: return element.type;
2086
+ case "transform": return "Transform";
2087
+ case "customdraw": return "CustomDraw";
2015
2088
  }
2016
2089
  }
2017
2090
  /**
package/node/index.d.cts CHANGED
@@ -363,7 +363,8 @@ interface CustomDrawProps extends LayoutProps {
363
363
  interface CustomDrawElement extends ElementBase, CustomDrawProps {
364
364
  type: "customdraw";
365
365
  }
366
- type Element = BoxElement | TextElement | RichTextElement | ImageElement | SvgElement | StackElement | TransformElement | CustomDrawElement;
366
+ type LayoutElement = BoxElement | TextElement | RichTextElement | ImageElement | SvgElement | StackElement | CustomDrawElement;
367
+ type Element = LayoutElement | TransformElement;
367
368
  //#endregion
368
369
  //#region src/canvas.d.ts
369
370
  interface CanvasOptions<T extends HTMLCanvasElement | OffscreenCanvas | Canvas = HTMLCanvasElement | OffscreenCanvas | Canvas> {
@@ -382,7 +383,7 @@ interface DrawCallCanvas<T extends HTMLCanvasElement | OffscreenCanvas | Canvas
382
383
  readonly height: number;
383
384
  readonly pixelRatio: number;
384
385
  readonly canvas: T;
385
- render(element: Element): LayoutNode;
386
+ render(element: LayoutElement): LayoutNode;
386
387
  clear(): void;
387
388
  getContext(): CanvasRenderingContext2D;
388
389
  toDataURL(type?: string, quality?: number): string;
@@ -431,7 +432,11 @@ declare function Text(props: TextProps): TextElement;
431
432
  declare function Transform(props: TransformProps): TransformElement;
432
433
  //#endregion
433
434
  //#region src/layout/engine.d.ts
434
- declare function computeLayout(element: Element, ctx: MeasureContext, constraints: LayoutConstraints, x?: number, y?: number): LayoutNode;
435
+ /**
436
+ * 布局计算主函数
437
+ * 内部使用 Element 类型以支持 Transform,外部通过 LayoutElement 约束类型
438
+ */
439
+ declare function computeLayout(element: LayoutElement, ctx: MeasureContext, constraints: LayoutConstraints, x?: number, y?: number): LayoutNode;
435
440
  //#endregion
436
441
  //#region src/layout/utils/print.d.ts
437
442
  /**
@@ -446,4 +451,4 @@ declare function printLayout(node: LayoutNode): void;
446
451
  */
447
452
  declare function layoutToString(node: LayoutNode, _indent?: string): string;
448
453
  //#endregion
449
- export { type AlignItems, type AlignSelf, type Border, type Bounds, Box, type BoxElement, type BoxProps, type CanvasOptions, type Color, type ColorStop, type ContainerLayoutProps, CustomDraw, type CustomDrawElement, type CustomDrawProps, type DrawCallCanvas, type Element, type FlexDirection, type FontProps, type GradientDescriptor, Image, type JustifyContent, type LayoutNode, type LayoutProps, type LinearGradientDescriptor, type MeasureContext, type ProxiedCanvasContextOptions, type RadialGradientDescriptor, RichText, type RichTextElement, type RichTextProps, type RichTextSpan, type Shadow, type Size, type Spacing, Stack, type StackAlign, type StackElement, type StackProps, type StrokeProps, Svg, type SvgAlign, type SvgChild, type SvgCircleChild, type SvgElement, type SvgEllipseChild, type SvgGroupChild, type SvgLineChild, type SvgPathChild, type SvgPolygonChild, type SvgPolylineChild, type SvgProps, type SvgRectChild, type SvgStyleProps, type SvgTextChild, type SvgTransformProps, Text, type TextElement, type TextProps, Transform, type TransformElement, type TransformProps, computeLayout, createCanvas, createCanvasMeasureContext, layoutToString, linearGradient, printLayout, radialGradient, svg };
454
+ export { type AlignItems, type AlignSelf, type Border, type Bounds, Box, type BoxElement, type BoxProps, type CanvasOptions, type Color, type ColorStop, type ContainerLayoutProps, CustomDraw, type CustomDrawElement, type CustomDrawProps, type DrawCallCanvas, type Element, type FlexDirection, type FontProps, type GradientDescriptor, Image, type JustifyContent, type LayoutElement, type LayoutNode, type LayoutProps, type LinearGradientDescriptor, type MeasureContext, type ProxiedCanvasContextOptions, type RadialGradientDescriptor, RichText, type RichTextElement, type RichTextProps, type RichTextSpan, type Shadow, type Size, type Spacing, Stack, type StackAlign, type StackElement, type StackProps, type StrokeProps, Svg, type SvgAlign, type SvgChild, type SvgCircleChild, type SvgElement, type SvgEllipseChild, type SvgGroupChild, type SvgLineChild, type SvgPathChild, type SvgPolygonChild, type SvgPolylineChild, type SvgProps, type SvgRectChild, type SvgStyleProps, type SvgTextChild, type SvgTransformProps, Text, type TextElement, type TextProps, Transform, type TransformElement, type TransformProps, computeLayout, createCanvas, createCanvasMeasureContext, layoutToString, linearGradient, printLayout, radialGradient, svg };
package/node/index.d.mts CHANGED
@@ -363,7 +363,8 @@ interface CustomDrawProps extends LayoutProps {
363
363
  interface CustomDrawElement extends ElementBase, CustomDrawProps {
364
364
  type: "customdraw";
365
365
  }
366
- type Element = BoxElement | TextElement | RichTextElement | ImageElement | SvgElement | StackElement | TransformElement | CustomDrawElement;
366
+ type LayoutElement = BoxElement | TextElement | RichTextElement | ImageElement | SvgElement | StackElement | CustomDrawElement;
367
+ type Element = LayoutElement | TransformElement;
367
368
  //#endregion
368
369
  //#region src/canvas.d.ts
369
370
  interface CanvasOptions<T extends HTMLCanvasElement | OffscreenCanvas | Canvas = HTMLCanvasElement | OffscreenCanvas | Canvas> {
@@ -382,7 +383,7 @@ interface DrawCallCanvas<T extends HTMLCanvasElement | OffscreenCanvas | Canvas
382
383
  readonly height: number;
383
384
  readonly pixelRatio: number;
384
385
  readonly canvas: T;
385
- render(element: Element): LayoutNode;
386
+ render(element: LayoutElement): LayoutNode;
386
387
  clear(): void;
387
388
  getContext(): CanvasRenderingContext2D;
388
389
  toDataURL(type?: string, quality?: number): string;
@@ -431,7 +432,11 @@ declare function Text(props: TextProps): TextElement;
431
432
  declare function Transform(props: TransformProps): TransformElement;
432
433
  //#endregion
433
434
  //#region src/layout/engine.d.ts
434
- declare function computeLayout(element: Element, ctx: MeasureContext, constraints: LayoutConstraints, x?: number, y?: number): LayoutNode;
435
+ /**
436
+ * 布局计算主函数
437
+ * 内部使用 Element 类型以支持 Transform,外部通过 LayoutElement 约束类型
438
+ */
439
+ declare function computeLayout(element: LayoutElement, ctx: MeasureContext, constraints: LayoutConstraints, x?: number, y?: number): LayoutNode;
435
440
  //#endregion
436
441
  //#region src/layout/utils/print.d.ts
437
442
  /**
@@ -446,4 +451,4 @@ declare function printLayout(node: LayoutNode): void;
446
451
  */
447
452
  declare function layoutToString(node: LayoutNode, _indent?: string): string;
448
453
  //#endregion
449
- export { type AlignItems, type AlignSelf, type Border, type Bounds, Box, type BoxElement, type BoxProps, type CanvasOptions, type Color, type ColorStop, type ContainerLayoutProps, CustomDraw, type CustomDrawElement, type CustomDrawProps, type DrawCallCanvas, type Element, type FlexDirection, type FontProps, type GradientDescriptor, Image, type JustifyContent, type LayoutNode, type LayoutProps, type LinearGradientDescriptor, type MeasureContext, type ProxiedCanvasContextOptions, type RadialGradientDescriptor, RichText, type RichTextElement, type RichTextProps, type RichTextSpan, type Shadow, type Size, type Spacing, Stack, type StackAlign, type StackElement, type StackProps, type StrokeProps, Svg, type SvgAlign, type SvgChild, type SvgCircleChild, type SvgElement, type SvgEllipseChild, type SvgGroupChild, type SvgLineChild, type SvgPathChild, type SvgPolygonChild, type SvgPolylineChild, type SvgProps, type SvgRectChild, type SvgStyleProps, type SvgTextChild, type SvgTransformProps, Text, type TextElement, type TextProps, Transform, type TransformElement, type TransformProps, computeLayout, createCanvas, createCanvasMeasureContext, layoutToString, linearGradient, printLayout, radialGradient, svg };
454
+ export { type AlignItems, type AlignSelf, type Border, type Bounds, Box, type BoxElement, type BoxProps, type CanvasOptions, type Color, type ColorStop, type ContainerLayoutProps, CustomDraw, type CustomDrawElement, type CustomDrawProps, type DrawCallCanvas, type Element, type FlexDirection, type FontProps, type GradientDescriptor, Image, type JustifyContent, type LayoutElement, type LayoutNode, type LayoutProps, type LinearGradientDescriptor, type MeasureContext, type ProxiedCanvasContextOptions, type RadialGradientDescriptor, RichText, type RichTextElement, type RichTextProps, type RichTextSpan, type Shadow, type Size, type Spacing, Stack, type StackAlign, type StackElement, type StackProps, type StrokeProps, Svg, type SvgAlign, type SvgChild, type SvgCircleChild, type SvgElement, type SvgEllipseChild, type SvgGroupChild, type SvgLineChild, type SvgPathChild, type SvgPolygonChild, type SvgPolylineChild, type SvgProps, type SvgRectChild, type SvgStyleProps, type SvgTextChild, type SvgTransformProps, Text, type TextElement, type TextProps, Transform, type TransformElement, type TransformProps, computeLayout, createCanvas, createCanvasMeasureContext, layoutToString, linearGradient, printLayout, radialGradient, svg };