@tuidom/elements 0.1.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.
Files changed (89) hide show
  1. package/dist/body/bodyElement.d.ts +33 -0
  2. package/dist/body/bodyElement.js +87 -0
  3. package/dist/button/buttonElement.d.ts +18 -0
  4. package/dist/button/buttonElement.js +68 -0
  5. package/dist/completionlist/completionDetailsElement.d.ts +49 -0
  6. package/dist/completionlist/completionDetailsElement.js +165 -0
  7. package/dist/completionlist/completionItemKindIcon.d.ts +2 -0
  8. package/dist/completionlist/completionItemKindIcon.js +43 -0
  9. package/dist/completionlist/completionListElement.d.ts +102 -0
  10. package/dist/completionlist/completionListElement.js +309 -0
  11. package/dist/completionlist/completionWidgetElement.d.ts +41 -0
  12. package/dist/completionlist/completionWidgetElement.js +102 -0
  13. package/dist/contextview/contextMenuController.d.ts +23 -0
  14. package/dist/contextview/contextMenuController.js +75 -0
  15. package/dist/contextview/overlayHostElement.d.ts +23 -0
  16. package/dist/contextview/overlayHostElement.js +47 -0
  17. package/dist/editorgroup/editorTabItemElement.d.ts +41 -0
  18. package/dist/editorgroup/editorTabItemElement.js +186 -0
  19. package/dist/editorgroup/editorTabStripElement.d.ts +33 -0
  20. package/dist/editorgroup/editorTabStripElement.js +118 -0
  21. package/dist/editorpart/editorPartElement.d.ts +95 -0
  22. package/dist/editorpart/editorPartElement.js +285 -0
  23. package/dist/inputbox/inputElement.d.ts +34 -0
  24. package/dist/inputbox/inputElement.js +207 -0
  25. package/dist/inputbox/inputState.d.ts +69 -0
  26. package/dist/inputbox/inputState.js +326 -0
  27. package/dist/layout/boxContainerElement.d.ts +27 -0
  28. package/dist/layout/boxContainerElement.js +121 -0
  29. package/dist/layout/boxElement.d.ts +4 -0
  30. package/dist/layout/boxElement.js +30 -0
  31. package/dist/layout/fillerElement.d.ts +12 -0
  32. package/dist/layout/fillerElement.js +16 -0
  33. package/dist/layout/fitContentElement.d.ts +18 -0
  34. package/dist/layout/fitContentElement.js +46 -0
  35. package/dist/layout/hFlexElement.d.ts +26 -0
  36. package/dist/layout/hFlexElement.js +134 -0
  37. package/dist/layout/paddingContainerElement.d.ts +30 -0
  38. package/dist/layout/paddingContainerElement.js +88 -0
  39. package/dist/layout/sizedBoxElement.d.ts +30 -0
  40. package/dist/layout/sizedBoxElement.js +74 -0
  41. package/dist/layout/vFlexElement.d.ts +32 -0
  42. package/dist/layout/vFlexElement.js +136 -0
  43. package/dist/layout/vStackElement.d.ts +18 -0
  44. package/dist/layout/vStackElement.js +74 -0
  45. package/dist/list/listViewElement.d.ts +232 -0
  46. package/dist/list/listViewElement.js +944 -0
  47. package/dist/menu/menuBarElement.d.ts +47 -0
  48. package/dist/menu/menuBarElement.js +285 -0
  49. package/dist/menu/menuBarItemElement.d.ts +20 -0
  50. package/dist/menu/menuBarItemElement.js +68 -0
  51. package/dist/menu/popupMenuElement.d.ts +81 -0
  52. package/dist/menu/popupMenuElement.js +318 -0
  53. package/dist/menu/popupMenuItemElement.d.ts +24 -0
  54. package/dist/menu/popupMenuItemElement.js +89 -0
  55. package/dist/panel/panelContainerElement.d.ts +64 -0
  56. package/dist/panel/panelContainerElement.js +219 -0
  57. package/dist/quickpick/quickPickElement.d.ts +161 -0
  58. package/dist/quickpick/quickPickElement.js +561 -0
  59. package/dist/sash/sashElement.d.ts +30 -0
  60. package/dist/sash/sashElement.js +78 -0
  61. package/dist/scrollbar/iScrollable.d.ts +8 -0
  62. package/dist/scrollbar/iScrollable.js +1 -0
  63. package/dist/scrollbar/scrollBarRenderer.d.ts +36 -0
  64. package/dist/scrollbar/scrollBarRenderer.js +76 -0
  65. package/dist/scrollbar/scrollContainerElement.d.ts +39 -0
  66. package/dist/scrollbar/scrollContainerElement.js +91 -0
  67. package/dist/scrollbar/scrollViewport.d.ts +28 -0
  68. package/dist/scrollbar/scrollViewport.js +65 -0
  69. package/dist/scrollbar/scrollableElement.d.ts +22 -0
  70. package/dist/scrollbar/scrollableElement.js +42 -0
  71. package/dist/selectbox/selectBoxElement.d.ts +48 -0
  72. package/dist/selectbox/selectBoxElement.js +161 -0
  73. package/dist/terminal/encodeKeyForPty.d.ts +3 -0
  74. package/dist/terminal/encodeKeyForPty.js +56 -0
  75. package/dist/terminal/terminalViewElement.d.ts +17 -0
  76. package/dist/terminal/terminalViewElement.js +172 -0
  77. package/dist/text/textBlockElement.d.ts +13 -0
  78. package/dist/text/textBlockElement.js +35 -0
  79. package/dist/text/textLabelElement.d.ts +28 -0
  80. package/dist/text/textLabelElement.js +77 -0
  81. package/dist/titledpanel/titledPanelElement.d.ts +14 -0
  82. package/dist/titledpanel/titledPanelElement.js +40 -0
  83. package/dist/tree/iTreeDataProvider.d.ts +19 -0
  84. package/dist/tree/iTreeDataProvider.js +1 -0
  85. package/dist/tree/treeViewElement.d.ts +116 -0
  86. package/dist/tree/treeViewElement.js +748 -0
  87. package/dist/workbenchlayout/workbenchLayoutElement.d.ts +60 -0
  88. package/dist/workbenchlayout/workbenchLayoutElement.js +203 -0
  89. package/package.json +33 -0
@@ -0,0 +1,134 @@
1
+ import { BoxConstraints, Offset, Point, Rect, Size } from "@tuidom/core/common/geometryPromitives";
2
+ import { RenderContext, TUIElement } from "@tuidom/core/dom/tuiElement";
3
+ export function hflexFixed(value) {
4
+ return { type: "fixed", value };
5
+ }
6
+ export function hflexFit() {
7
+ return { type: "fit" };
8
+ }
9
+ export function hflexFill() {
10
+ return { type: "fill" };
11
+ }
12
+ export class HFlexElement extends TUIElement {
13
+ addChild(child, style) {
14
+ if (style.width.type === "fill") {
15
+ const hasFill = this.getChildren().some((c) => c.layoutStyle.width.type === "fill");
16
+ if (hasFill) {
17
+ throw new Error("HFlexElement supports at most one fill child");
18
+ }
19
+ }
20
+ child.layoutStyle = style;
21
+ this.appendChild(child);
22
+ }
23
+ replaceChildren(newChildren) {
24
+ let fillCount = 0;
25
+ for (const child of newChildren) {
26
+ const style = child.layoutStyle;
27
+ if (style?.width.type === "fill")
28
+ fillCount++;
29
+ }
30
+ if (fillCount > 1) {
31
+ throw new Error("HFlexElement supports at most one fill child");
32
+ }
33
+ this.setChildren(newChildren);
34
+ }
35
+ // ─── Intrinsic Size ───
36
+ getMinIntrinsicWidth(height) {
37
+ let sum = 0;
38
+ for (const child of this.getChildren()) {
39
+ const style = child.layoutStyle;
40
+ if (style.width.type === "fixed") {
41
+ sum += style.width.value;
42
+ }
43
+ else {
44
+ sum += child.getMinIntrinsicWidth(height);
45
+ }
46
+ }
47
+ return sum;
48
+ }
49
+ getMaxIntrinsicWidth(height) {
50
+ let sum = 0;
51
+ for (const child of this.getChildren()) {
52
+ const style = child.layoutStyle;
53
+ if (style.width.type === "fixed") {
54
+ sum += style.width.value;
55
+ }
56
+ else {
57
+ sum += child.getMaxIntrinsicWidth(height);
58
+ }
59
+ }
60
+ return sum;
61
+ }
62
+ getMinIntrinsicHeight(width) {
63
+ let max = 0;
64
+ for (const child of this.getChildren()) {
65
+ const style = child.layoutStyle;
66
+ if (style.height === "fill") {
67
+ max = Math.max(max, child.getMinIntrinsicHeight(width));
68
+ }
69
+ else {
70
+ max = Math.max(max, style.height);
71
+ }
72
+ }
73
+ return max;
74
+ }
75
+ getMaxIntrinsicHeight(width) {
76
+ let max = 0;
77
+ for (const child of this.getChildren()) {
78
+ const style = child.layoutStyle;
79
+ if (style.height === "fill") {
80
+ max = Math.max(max, child.getMaxIntrinsicHeight(width));
81
+ }
82
+ else {
83
+ max = Math.max(max, style.height);
84
+ }
85
+ }
86
+ return max;
87
+ }
88
+ // ─── Layout ───
89
+ performLayout(constraints) {
90
+ const containerSize = super.performLayout(constraints);
91
+ const containerWidth = containerSize.width;
92
+ const containerHeight = containerSize.height;
93
+ let fixedSum = 0;
94
+ let fitSum = 0;
95
+ let fillChild = null;
96
+ // Pass 1: measure fixed and fit children
97
+ for (const child of this.getChildren()) {
98
+ const style = child.layoutStyle;
99
+ const childHeight = style.height === "fill" ? containerHeight : style.height;
100
+ if (style.width.type === "fixed") {
101
+ fixedSum += style.width.value;
102
+ }
103
+ else if (style.width.type === "fit") {
104
+ fitSum += child.getMaxIntrinsicWidth(childHeight);
105
+ }
106
+ else {
107
+ fillChild = child;
108
+ }
109
+ }
110
+ // Pass 2: compute fill width and lay out all children
111
+ const remaining = Math.max(0, containerWidth - fixedSum - fitSum);
112
+ let currentX = 0;
113
+ for (const child of this.getChildren()) {
114
+ const style = child.layoutStyle;
115
+ // Инвариант вложенности (Н2): дети не вылезают за контейнер — при
116
+ // нехватке места жадный кламп по остатку в порядке детей.
117
+ const childHeight = Math.min(style.height === "fill" ? containerHeight : style.height, containerHeight);
118
+ let childWidth;
119
+ if (style.width.type === "fixed") {
120
+ childWidth = style.width.value;
121
+ }
122
+ else if (style.width.type === "fit") {
123
+ childWidth = child.getMaxIntrinsicWidth(childHeight);
124
+ }
125
+ else {
126
+ childWidth = remaining;
127
+ }
128
+ childWidth = Math.max(0, Math.min(childWidth, containerWidth - currentX));
129
+ this.layoutChild(child, currentX, 0, BoxConstraints.tight(new Size(childWidth, childHeight)));
130
+ currentX += childWidth;
131
+ }
132
+ return containerSize;
133
+ }
134
+ }
@@ -0,0 +1,30 @@
1
+ import { BoxConstraints, Size } from "@tuidom/core/common/geometryPromitives";
2
+ import { TUIElement } from "@tuidom/core/dom/tuiElement";
3
+ export interface Padding {
4
+ top?: number;
5
+ right?: number;
6
+ bottom?: number;
7
+ left?: number;
8
+ }
9
+ export declare class PaddingContainerElement extends TUIElement {
10
+ private child;
11
+ private top;
12
+ private right;
13
+ private bottom;
14
+ private left;
15
+ constructor(child: TUIElement | null, padding?: Padding);
16
+ setChild(child: TUIElement | null): void;
17
+ getMinIntrinsicWidth(height: number): number;
18
+ getMaxIntrinsicWidth(height: number): number;
19
+ getMinIntrinsicHeight(width: number): number;
20
+ getMaxIntrinsicHeight(width: number): number;
21
+ getPaddingTop(): number;
22
+ setPaddingTop(value: number): void;
23
+ getPaddingRight(): number;
24
+ setPaddingRight(value: number): void;
25
+ getPaddingBottom(): number;
26
+ setPaddingBottom(value: number): void;
27
+ getPaddingLeft(): number;
28
+ setPaddingLeft(value: number): void;
29
+ protected performLayout(constraints: BoxConstraints): Size;
30
+ }
@@ -0,0 +1,88 @@
1
+ import { BoxConstraints, Offset, Point, Rect, Size } from "@tuidom/core/common/geometryPromitives";
2
+ import { TUIElement } from "@tuidom/core/dom/tuiElement";
3
+ export class PaddingContainerElement extends TUIElement {
4
+ child;
5
+ top;
6
+ right;
7
+ bottom;
8
+ left;
9
+ constructor(child, padding) {
10
+ super();
11
+ this.child = child;
12
+ if (this.child)
13
+ this.appendChild(this.child);
14
+ this.top = padding?.top ?? 0;
15
+ this.right = padding?.right ?? 0;
16
+ this.bottom = padding?.bottom ?? 0;
17
+ this.left = padding?.left ?? 0;
18
+ }
19
+ setChild(child) {
20
+ if (this.child)
21
+ this.removeChild(this.child);
22
+ this.child = child;
23
+ if (this.child)
24
+ this.appendChild(this.child);
25
+ this.markDirty();
26
+ }
27
+ getMinIntrinsicWidth(height) {
28
+ const paddingX = this.left + this.right;
29
+ if (!this.child)
30
+ return paddingX;
31
+ return this.child.getMinIntrinsicWidth(Math.max(0, height - this.top - this.bottom)) + paddingX;
32
+ }
33
+ getMaxIntrinsicWidth(height) {
34
+ const paddingX = this.left + this.right;
35
+ if (!this.child)
36
+ return paddingX;
37
+ return this.child.getMaxIntrinsicWidth(Math.max(0, height - this.top - this.bottom)) + paddingX;
38
+ }
39
+ getMinIntrinsicHeight(width) {
40
+ const paddingY = this.top + this.bottom;
41
+ if (!this.child)
42
+ return paddingY;
43
+ return this.child.getMinIntrinsicHeight(Math.max(0, width - this.left - this.right)) + paddingY;
44
+ }
45
+ getMaxIntrinsicHeight(width) {
46
+ const paddingY = this.top + this.bottom;
47
+ if (!this.child)
48
+ return paddingY;
49
+ return this.child.getMaxIntrinsicHeight(Math.max(0, width - this.left - this.right)) + paddingY;
50
+ }
51
+ getPaddingTop() {
52
+ return this.top;
53
+ }
54
+ setPaddingTop(value) {
55
+ this.top = value;
56
+ this.markDirty();
57
+ }
58
+ getPaddingRight() {
59
+ return this.right;
60
+ }
61
+ setPaddingRight(value) {
62
+ this.right = value;
63
+ this.markDirty();
64
+ }
65
+ getPaddingBottom() {
66
+ return this.bottom;
67
+ }
68
+ setPaddingBottom(value) {
69
+ this.bottom = value;
70
+ this.markDirty();
71
+ }
72
+ getPaddingLeft() {
73
+ return this.left;
74
+ }
75
+ setPaddingLeft(value) {
76
+ this.left = value;
77
+ this.markDirty();
78
+ }
79
+ performLayout(constraints) {
80
+ const containerSize = super.performLayout(constraints);
81
+ if (this.child) {
82
+ const childWidth = Math.max(0, containerSize.width - this.left - this.right);
83
+ const childHeight = Math.max(0, containerSize.height - this.top - this.bottom);
84
+ this.layoutChild(this.child, this.left, this.top, BoxConstraints.tight(new Size(childWidth, childHeight)));
85
+ }
86
+ return containerSize;
87
+ }
88
+ }
@@ -0,0 +1,30 @@
1
+ import { BoxConstraints, Size } from "@tuidom/core/common/geometryPromitives";
2
+ import { RenderContext, TUIElement } from "@tuidom/core/dom/tuiElement";
3
+ /**
4
+ * Контейнер фиксированного «предпочтительного» размера: под входящими
5
+ * constraints резолвит размер как `preferredWidth`/`preferredHeight`
6
+ * (заклампленный в `[min, max]`) и кладёт единственного ребёнка tight на этот
7
+ * размер. Неуказанная ось делегируется max-intrinsic ребёнка (по этой оси
8
+ * ведёт себя как {@link FitContentElement}).
9
+ *
10
+ * Нужен для overlay-виджетов фиксированной ширины: слой даёт loose-constraints
11
+ * на всю доступную область, а виджету надо занять свой предпочтительный размер
12
+ * (FitContent тянется по контенту, HFlex/VStack — по детям, выразить нечем).
13
+ * Полностью theme-agnostic: цветов не знает.
14
+ */
15
+ export declare class SizedBoxElement extends TUIElement {
16
+ private child;
17
+ private preferredWidth;
18
+ private preferredHeight;
19
+ constructor(preferredWidth?: number, preferredHeight?: number);
20
+ setPreferredWidth(value: number | undefined): void;
21
+ setPreferredHeight(value: number | undefined): void;
22
+ setChild(child: TUIElement | null): void;
23
+ getChild(): TUIElement | null;
24
+ getMinIntrinsicWidth(height: number): number;
25
+ getMaxIntrinsicWidth(height: number): number;
26
+ getMinIntrinsicHeight(width: number): number;
27
+ getMaxIntrinsicHeight(width: number): number;
28
+ protected performLayout(constraints: BoxConstraints): Size;
29
+ render(context: RenderContext): void;
30
+ }
@@ -0,0 +1,74 @@
1
+ import { BoxConstraints, Offset, Point, Rect, Size } from "@tuidom/core/common/geometryPromitives";
2
+ import { RenderContext, TUIElement } from "@tuidom/core/dom/tuiElement";
3
+ /**
4
+ * Контейнер фиксированного «предпочтительного» размера: под входящими
5
+ * constraints резолвит размер как `preferredWidth`/`preferredHeight`
6
+ * (заклампленный в `[min, max]`) и кладёт единственного ребёнка tight на этот
7
+ * размер. Неуказанная ось делегируется max-intrinsic ребёнка (по этой оси
8
+ * ведёт себя как {@link FitContentElement}).
9
+ *
10
+ * Нужен для overlay-виджетов фиксированной ширины: слой даёт loose-constraints
11
+ * на всю доступную область, а виджету надо занять свой предпочтительный размер
12
+ * (FitContent тянется по контенту, HFlex/VStack — по детям, выразить нечем).
13
+ * Полностью theme-agnostic: цветов не знает.
14
+ */
15
+ export class SizedBoxElement extends TUIElement {
16
+ child = null;
17
+ preferredWidth;
18
+ preferredHeight;
19
+ constructor(preferredWidth, preferredHeight) {
20
+ super();
21
+ this.preferredWidth = preferredWidth;
22
+ this.preferredHeight = preferredHeight;
23
+ }
24
+ setPreferredWidth(value) {
25
+ this.preferredWidth = value;
26
+ this.markDirty();
27
+ }
28
+ setPreferredHeight(value) {
29
+ this.preferredHeight = value;
30
+ this.markDirty();
31
+ }
32
+ setChild(child) {
33
+ if (this.child)
34
+ this.removeChild(this.child);
35
+ this.child = child;
36
+ if (this.child)
37
+ this.appendChild(this.child);
38
+ this.markDirty();
39
+ }
40
+ getChild() {
41
+ return this.child;
42
+ }
43
+ getMinIntrinsicWidth(height) {
44
+ return this.preferredWidth ?? this.child?.getMinIntrinsicWidth(height) ?? 0;
45
+ }
46
+ getMaxIntrinsicWidth(height) {
47
+ return this.preferredWidth ?? this.child?.getMaxIntrinsicWidth(height) ?? 0;
48
+ }
49
+ getMinIntrinsicHeight(width) {
50
+ return this.preferredHeight ?? this.child?.getMinIntrinsicHeight(width) ?? 0;
51
+ }
52
+ getMaxIntrinsicHeight(width) {
53
+ return this.preferredHeight ?? this.child?.getMaxIntrinsicHeight(width) ?? 0;
54
+ }
55
+ performLayout(constraints) {
56
+ const desiredWidth = this.getMaxIntrinsicWidth(this.preferredHeight ?? 0);
57
+ const desiredHeight = this.getMaxIntrinsicHeight(desiredWidth);
58
+ const size = constraints.constrain(new Size(desiredWidth, desiredHeight));
59
+ super.performLayout(BoxConstraints.tight(size));
60
+ if (this.child) {
61
+ this.layoutChild(this.child, 0, 0, BoxConstraints.tight(size));
62
+ }
63
+ return size;
64
+ }
65
+ render(context) {
66
+ if (this.child) {
67
+ // Ребёнок tight к собственному rect'у: клип не может опустеть —
68
+ // пустой контекст отсёк бы уже прунинг renderChildren родителя.
69
+ const childOffset = new Offset(this.child.localPosition.dx, this.child.localPosition.dy);
70
+ const childClip = new Rect(this.child.globalPosition, this.child.layoutSize);
71
+ this.child.render(context.withOffset(childOffset).withClip(childClip));
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,32 @@
1
+ import { BoxConstraints, Size } from "@tuidom/core/common/geometryPromitives";
2
+ import { TUIElement } from "@tuidom/core/dom/tuiElement";
3
+ export type VFlexChildSize = {
4
+ type: "fixed";
5
+ value: number;
6
+ } | {
7
+ type: "fit";
8
+ } | {
9
+ type: "fill";
10
+ };
11
+ export interface VFlexLayoutStyle {
12
+ height: VFlexChildSize;
13
+ width: number | "fill";
14
+ }
15
+ export declare function vflexFixed(value: number): VFlexChildSize;
16
+ export declare function vflexFit(): VFlexChildSize;
17
+ export declare function vflexFill(): VFlexChildSize;
18
+ /**
19
+ * Вертикальный flex — зеркало {@link HFlexElement} с переставленными осями:
20
+ * главная ось — высота (fixed/fit/fill, максимум один fill), поперечная —
21
+ * ширина (число или "fill" на всю ширину контейнера). JSX-адаптер сознательно
22
+ * не заведён — добавляется симметрично HFlex при первом JSX-потребителе.
23
+ */
24
+ export declare class VFlexElement extends TUIElement {
25
+ addChild(child: TUIElement, style: VFlexLayoutStyle): void;
26
+ replaceChildren(newChildren: TUIElement[]): void;
27
+ getMinIntrinsicHeight(width: number): number;
28
+ getMaxIntrinsicHeight(width: number): number;
29
+ getMinIntrinsicWidth(height: number): number;
30
+ getMaxIntrinsicWidth(height: number): number;
31
+ protected performLayout(constraints: BoxConstraints): Size;
32
+ }
@@ -0,0 +1,136 @@
1
+ import { BoxConstraints, Size } from "@tuidom/core/common/geometryPromitives";
2
+ import { TUIElement } from "@tuidom/core/dom/tuiElement";
3
+ export function vflexFixed(value) {
4
+ return { type: "fixed", value };
5
+ }
6
+ export function vflexFit() {
7
+ return { type: "fit" };
8
+ }
9
+ export function vflexFill() {
10
+ return { type: "fill" };
11
+ }
12
+ /**
13
+ * Вертикальный flex — зеркало {@link HFlexElement} с переставленными осями:
14
+ * главная ось — высота (fixed/fit/fill, максимум один fill), поперечная —
15
+ * ширина (число или "fill" на всю ширину контейнера). JSX-адаптер сознательно
16
+ * не заведён — добавляется симметрично HFlex при первом JSX-потребителе.
17
+ */
18
+ export class VFlexElement extends TUIElement {
19
+ addChild(child, style) {
20
+ if (style.height.type === "fill") {
21
+ const hasFill = this.getChildren().some((c) => c.layoutStyle.height.type === "fill");
22
+ if (hasFill) {
23
+ throw new Error("VFlexElement supports at most one fill child");
24
+ }
25
+ }
26
+ child.layoutStyle = style;
27
+ this.appendChild(child);
28
+ }
29
+ replaceChildren(newChildren) {
30
+ let fillCount = 0;
31
+ for (const child of newChildren) {
32
+ const style = child.layoutStyle;
33
+ if (style?.height.type === "fill")
34
+ fillCount++;
35
+ }
36
+ if (fillCount > 1) {
37
+ throw new Error("VFlexElement supports at most one fill child");
38
+ }
39
+ this.setChildren(newChildren);
40
+ }
41
+ // ─── Intrinsic Size ───
42
+ getMinIntrinsicHeight(width) {
43
+ let sum = 0;
44
+ for (const child of this.getChildren()) {
45
+ const style = child.layoutStyle;
46
+ if (style.height.type === "fixed") {
47
+ sum += style.height.value;
48
+ }
49
+ else {
50
+ sum += child.getMinIntrinsicHeight(width);
51
+ }
52
+ }
53
+ return sum;
54
+ }
55
+ getMaxIntrinsicHeight(width) {
56
+ let sum = 0;
57
+ for (const child of this.getChildren()) {
58
+ const style = child.layoutStyle;
59
+ if (style.height.type === "fixed") {
60
+ sum += style.height.value;
61
+ }
62
+ else {
63
+ sum += child.getMaxIntrinsicHeight(width);
64
+ }
65
+ }
66
+ return sum;
67
+ }
68
+ getMinIntrinsicWidth(height) {
69
+ let max = 0;
70
+ for (const child of this.getChildren()) {
71
+ const style = child.layoutStyle;
72
+ if (style.width === "fill") {
73
+ max = Math.max(max, child.getMinIntrinsicWidth(height));
74
+ }
75
+ else {
76
+ max = Math.max(max, style.width);
77
+ }
78
+ }
79
+ return max;
80
+ }
81
+ getMaxIntrinsicWidth(height) {
82
+ let max = 0;
83
+ for (const child of this.getChildren()) {
84
+ const style = child.layoutStyle;
85
+ if (style.width === "fill") {
86
+ max = Math.max(max, child.getMaxIntrinsicWidth(height));
87
+ }
88
+ else {
89
+ max = Math.max(max, style.width);
90
+ }
91
+ }
92
+ return max;
93
+ }
94
+ // ─── Layout ───
95
+ performLayout(constraints) {
96
+ const containerSize = super.performLayout(constraints);
97
+ const containerWidth = containerSize.width;
98
+ const containerHeight = containerSize.height;
99
+ let fixedSum = 0;
100
+ let fitSum = 0;
101
+ // Pass 1: measure fixed and fit children
102
+ for (const child of this.getChildren()) {
103
+ const style = child.layoutStyle;
104
+ const childWidth = style.width === "fill" ? containerWidth : style.width;
105
+ if (style.height.type === "fixed") {
106
+ fixedSum += style.height.value;
107
+ }
108
+ else if (style.height.type === "fit") {
109
+ fitSum += child.getMaxIntrinsicHeight(childWidth);
110
+ }
111
+ }
112
+ // Pass 2: compute fill height and lay out all children
113
+ const remaining = Math.max(0, containerHeight - fixedSum - fitSum);
114
+ let currentY = 0;
115
+ for (const child of this.getChildren()) {
116
+ const style = child.layoutStyle;
117
+ // Инвариант вложенности (Н2): дети не вылезают за контейнер — при
118
+ // нехватке места жадный кламп по остатку в порядке детей.
119
+ const childWidth = Math.min(style.width === "fill" ? containerWidth : style.width, containerWidth);
120
+ let childHeight;
121
+ if (style.height.type === "fixed") {
122
+ childHeight = style.height.value;
123
+ }
124
+ else if (style.height.type === "fit") {
125
+ childHeight = child.getMaxIntrinsicHeight(childWidth);
126
+ }
127
+ else {
128
+ childHeight = remaining;
129
+ }
130
+ childHeight = Math.max(0, Math.min(childHeight, containerHeight - currentY));
131
+ this.layoutChild(child, 0, currentY, BoxConstraints.tight(new Size(childWidth, childHeight)));
132
+ currentY += childHeight;
133
+ }
134
+ return containerSize;
135
+ }
136
+ }
@@ -0,0 +1,18 @@
1
+ import { BoxConstraints, Rect, Size } from "@tuidom/core/common/geometryPromitives";
2
+ import { TUIElement } from "@tuidom/core/dom/tuiElement";
3
+ export interface VStackLayoutStyle {
4
+ width: number | "fill" | "stretch";
5
+ height: number;
6
+ }
7
+ export interface VStackLayoutState {
8
+ rect: Rect;
9
+ }
10
+ export declare class VStackElement extends TUIElement {
11
+ addChild(child: TUIElement, style: VStackLayoutStyle): void;
12
+ replaceChildren(newChildren: TUIElement[]): void;
13
+ getMinIntrinsicWidth(height: number): number;
14
+ getMaxIntrinsicWidth(height: number): number;
15
+ getMinIntrinsicHeight(_width: number): number;
16
+ getMaxIntrinsicHeight(_width: number): number;
17
+ protected performLayout(constraints: BoxConstraints): Size;
18
+ }
@@ -0,0 +1,74 @@
1
+ import { BoxConstraints, Point, Rect, Size } from "@tuidom/core/common/geometryPromitives";
2
+ import { RenderContext, TUIElement } from "@tuidom/core/dom/tuiElement";
3
+ export class VStackElement extends TUIElement {
4
+ addChild(child, style) {
5
+ child.layoutStyle = style;
6
+ this.appendChild(child);
7
+ }
8
+ replaceChildren(newChildren) {
9
+ this.setChildren(newChildren);
10
+ }
11
+ getMinIntrinsicWidth(height) {
12
+ let max = 0;
13
+ for (const child of this.getChildren()) {
14
+ const style = child.layoutStyle;
15
+ if (style.width === "fill" || style.width === "stretch") {
16
+ max = Math.max(max, child.getMinIntrinsicWidth(height));
17
+ }
18
+ else {
19
+ max = Math.max(max, style.width);
20
+ }
21
+ }
22
+ return max;
23
+ }
24
+ getMaxIntrinsicWidth(height) {
25
+ let max = 0;
26
+ for (const child of this.getChildren()) {
27
+ const style = child.layoutStyle;
28
+ if (style.width === "fill" || style.width === "stretch") {
29
+ max = Math.max(max, child.getMaxIntrinsicWidth(height));
30
+ }
31
+ else {
32
+ max = Math.max(max, style.width);
33
+ }
34
+ }
35
+ return max;
36
+ }
37
+ getMinIntrinsicHeight(_width) {
38
+ let sum = 0;
39
+ for (const child of this.getChildren()) {
40
+ const style = child.layoutStyle;
41
+ sum += style.height;
42
+ }
43
+ return sum;
44
+ }
45
+ getMaxIntrinsicHeight(_width) {
46
+ let sum = 0;
47
+ for (const child of this.getChildren()) {
48
+ const style = child.layoutStyle;
49
+ sum += style.height;
50
+ }
51
+ return sum;
52
+ }
53
+ performLayout(constraints) {
54
+ // First, call parent implementation to set allocatedSize and mark as clean
55
+ const containerSize = super.performLayout(constraints);
56
+ const containerWidth = containerSize.width;
57
+ let currentY = 0;
58
+ for (const child of this.getChildren()) {
59
+ const style = child.layoutStyle;
60
+ // Инвариант вложенности (Н2): дети не вылезают за контейнер — при
61
+ // нехватке места жадный кламп по остатку в порядке детей.
62
+ const childWidth = Math.min(style.width === "fill" || style.width === "stretch" ? containerWidth : style.width, containerWidth);
63
+ const childHeight = Math.max(0, Math.min(style.height, containerSize.height - currentY));
64
+ const childSize = new Size(childWidth, childHeight);
65
+ // Store in layoutState for compatibility
66
+ child.layoutState = {
67
+ rect: new Rect(new Point(0, currentY), childSize),
68
+ };
69
+ this.layoutChild(child, 0, currentY, BoxConstraints.tight(childSize));
70
+ currentY += childHeight;
71
+ }
72
+ return containerSize;
73
+ }
74
+ }