@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.
- package/dist/body/bodyElement.d.ts +33 -0
- package/dist/body/bodyElement.js +87 -0
- package/dist/button/buttonElement.d.ts +18 -0
- package/dist/button/buttonElement.js +68 -0
- package/dist/completionlist/completionDetailsElement.d.ts +49 -0
- package/dist/completionlist/completionDetailsElement.js +165 -0
- package/dist/completionlist/completionItemKindIcon.d.ts +2 -0
- package/dist/completionlist/completionItemKindIcon.js +43 -0
- package/dist/completionlist/completionListElement.d.ts +102 -0
- package/dist/completionlist/completionListElement.js +309 -0
- package/dist/completionlist/completionWidgetElement.d.ts +41 -0
- package/dist/completionlist/completionWidgetElement.js +102 -0
- package/dist/contextview/contextMenuController.d.ts +23 -0
- package/dist/contextview/contextMenuController.js +75 -0
- package/dist/contextview/overlayHostElement.d.ts +23 -0
- package/dist/contextview/overlayHostElement.js +47 -0
- package/dist/editorgroup/editorTabItemElement.d.ts +41 -0
- package/dist/editorgroup/editorTabItemElement.js +186 -0
- package/dist/editorgroup/editorTabStripElement.d.ts +33 -0
- package/dist/editorgroup/editorTabStripElement.js +118 -0
- package/dist/editorpart/editorPartElement.d.ts +95 -0
- package/dist/editorpart/editorPartElement.js +285 -0
- package/dist/inputbox/inputElement.d.ts +34 -0
- package/dist/inputbox/inputElement.js +207 -0
- package/dist/inputbox/inputState.d.ts +69 -0
- package/dist/inputbox/inputState.js +326 -0
- package/dist/layout/boxContainerElement.d.ts +27 -0
- package/dist/layout/boxContainerElement.js +121 -0
- package/dist/layout/boxElement.d.ts +4 -0
- package/dist/layout/boxElement.js +30 -0
- package/dist/layout/fillerElement.d.ts +12 -0
- package/dist/layout/fillerElement.js +16 -0
- package/dist/layout/fitContentElement.d.ts +18 -0
- package/dist/layout/fitContentElement.js +46 -0
- package/dist/layout/hFlexElement.d.ts +26 -0
- package/dist/layout/hFlexElement.js +134 -0
- package/dist/layout/paddingContainerElement.d.ts +30 -0
- package/dist/layout/paddingContainerElement.js +88 -0
- package/dist/layout/sizedBoxElement.d.ts +30 -0
- package/dist/layout/sizedBoxElement.js +74 -0
- package/dist/layout/vFlexElement.d.ts +32 -0
- package/dist/layout/vFlexElement.js +136 -0
- package/dist/layout/vStackElement.d.ts +18 -0
- package/dist/layout/vStackElement.js +74 -0
- package/dist/list/listViewElement.d.ts +232 -0
- package/dist/list/listViewElement.js +944 -0
- package/dist/menu/menuBarElement.d.ts +47 -0
- package/dist/menu/menuBarElement.js +285 -0
- package/dist/menu/menuBarItemElement.d.ts +20 -0
- package/dist/menu/menuBarItemElement.js +68 -0
- package/dist/menu/popupMenuElement.d.ts +81 -0
- package/dist/menu/popupMenuElement.js +318 -0
- package/dist/menu/popupMenuItemElement.d.ts +24 -0
- package/dist/menu/popupMenuItemElement.js +89 -0
- package/dist/panel/panelContainerElement.d.ts +64 -0
- package/dist/panel/panelContainerElement.js +219 -0
- package/dist/quickpick/quickPickElement.d.ts +161 -0
- package/dist/quickpick/quickPickElement.js +561 -0
- package/dist/sash/sashElement.d.ts +30 -0
- package/dist/sash/sashElement.js +78 -0
- package/dist/scrollbar/iScrollable.d.ts +8 -0
- package/dist/scrollbar/iScrollable.js +1 -0
- package/dist/scrollbar/scrollBarRenderer.d.ts +36 -0
- package/dist/scrollbar/scrollBarRenderer.js +76 -0
- package/dist/scrollbar/scrollContainerElement.d.ts +39 -0
- package/dist/scrollbar/scrollContainerElement.js +91 -0
- package/dist/scrollbar/scrollViewport.d.ts +28 -0
- package/dist/scrollbar/scrollViewport.js +65 -0
- package/dist/scrollbar/scrollableElement.d.ts +22 -0
- package/dist/scrollbar/scrollableElement.js +42 -0
- package/dist/selectbox/selectBoxElement.d.ts +48 -0
- package/dist/selectbox/selectBoxElement.js +161 -0
- package/dist/terminal/encodeKeyForPty.d.ts +3 -0
- package/dist/terminal/encodeKeyForPty.js +56 -0
- package/dist/terminal/terminalViewElement.d.ts +17 -0
- package/dist/terminal/terminalViewElement.js +172 -0
- package/dist/text/textBlockElement.d.ts +13 -0
- package/dist/text/textBlockElement.js +35 -0
- package/dist/text/textLabelElement.d.ts +28 -0
- package/dist/text/textLabelElement.js +77 -0
- package/dist/titledpanel/titledPanelElement.d.ts +14 -0
- package/dist/titledpanel/titledPanelElement.js +40 -0
- package/dist/tree/iTreeDataProvider.d.ts +19 -0
- package/dist/tree/iTreeDataProvider.js +1 -0
- package/dist/tree/treeViewElement.d.ts +116 -0
- package/dist/tree/treeViewElement.js +748 -0
- package/dist/workbenchlayout/workbenchLayoutElement.d.ts +60 -0
- package/dist/workbenchlayout/workbenchLayoutElement.js +203 -0
- package/package.json +33 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { DisplayLine } from "@tuidom/core/common/displayLine";
|
|
2
|
+
/**
|
|
3
|
+
* Pure state model for a single-line text input.
|
|
4
|
+
* Tracks the text and cursor position (as a grapheme-aware offset).
|
|
5
|
+
* No UI dependencies.
|
|
6
|
+
*/
|
|
7
|
+
export class InputState {
|
|
8
|
+
textValue = "";
|
|
9
|
+
cursorOffsetValue = 0;
|
|
10
|
+
anchorOffset = null;
|
|
11
|
+
undoStack = [];
|
|
12
|
+
redoStack = [];
|
|
13
|
+
lastEditKind = null;
|
|
14
|
+
get text() {
|
|
15
|
+
return this.textValue;
|
|
16
|
+
}
|
|
17
|
+
get cursorOffset() {
|
|
18
|
+
return this.cursorOffsetValue;
|
|
19
|
+
}
|
|
20
|
+
get value() {
|
|
21
|
+
return this.textValue;
|
|
22
|
+
}
|
|
23
|
+
set value(v) {
|
|
24
|
+
this.textValue = v;
|
|
25
|
+
this.cursorOffsetValue = v.length;
|
|
26
|
+
this.anchorOffset = null;
|
|
27
|
+
// Programmatic content replacement is a fresh baseline — drop edit history.
|
|
28
|
+
this.undoStack = [];
|
|
29
|
+
this.redoStack = [];
|
|
30
|
+
this.lastEditKind = null;
|
|
31
|
+
}
|
|
32
|
+
// ─── Undo / Redo ─────────────────────────────────────────
|
|
33
|
+
get canUndo() {
|
|
34
|
+
return this.undoStack.length > 0;
|
|
35
|
+
}
|
|
36
|
+
get canRedo() {
|
|
37
|
+
return this.redoStack.length > 0;
|
|
38
|
+
}
|
|
39
|
+
undo() {
|
|
40
|
+
const prev = this.undoStack.pop();
|
|
41
|
+
if (prev === undefined)
|
|
42
|
+
return;
|
|
43
|
+
this.redoStack.push(this.snapshot());
|
|
44
|
+
this.restore(prev);
|
|
45
|
+
this.lastEditKind = null;
|
|
46
|
+
}
|
|
47
|
+
redo() {
|
|
48
|
+
const next = this.redoStack.pop();
|
|
49
|
+
if (next === undefined)
|
|
50
|
+
return;
|
|
51
|
+
this.undoStack.push(this.snapshot());
|
|
52
|
+
this.restore(next);
|
|
53
|
+
this.lastEditKind = null;
|
|
54
|
+
}
|
|
55
|
+
snapshot() {
|
|
56
|
+
return { text: this.textValue, cursorOffset: this.cursorOffsetValue, anchorOffset: this.anchorOffset };
|
|
57
|
+
}
|
|
58
|
+
restore(s) {
|
|
59
|
+
this.textValue = s.text;
|
|
60
|
+
this.cursorOffsetValue = s.cursorOffset;
|
|
61
|
+
this.anchorOffset = s.anchorOffset;
|
|
62
|
+
}
|
|
63
|
+
/** Push a snapshot of the current state and invalidate the redo stack. */
|
|
64
|
+
pushUndo() {
|
|
65
|
+
this.undoStack.push(this.snapshot());
|
|
66
|
+
this.redoStack = [];
|
|
67
|
+
}
|
|
68
|
+
/** Start a brand-new undo group: snapshot now, and don't coalesce the next edit into it. */
|
|
69
|
+
breakUndo() {
|
|
70
|
+
this.pushUndo();
|
|
71
|
+
this.lastEditKind = null;
|
|
72
|
+
}
|
|
73
|
+
/** Record an edit that may coalesce with the previous one when it is of the same kind. */
|
|
74
|
+
recordEdit(kind) {
|
|
75
|
+
if (this.lastEditKind !== kind) {
|
|
76
|
+
this.pushUndo();
|
|
77
|
+
}
|
|
78
|
+
this.lastEditKind = kind;
|
|
79
|
+
}
|
|
80
|
+
// ─── Selection ───────────────────────────────────────────
|
|
81
|
+
get hasSelection() {
|
|
82
|
+
return this.anchorOffset !== null && this.anchorOffset !== this.cursorOffsetValue;
|
|
83
|
+
}
|
|
84
|
+
get selectionStart() {
|
|
85
|
+
if (this.anchorOffset === null)
|
|
86
|
+
return this.cursorOffsetValue;
|
|
87
|
+
return Math.min(this.anchorOffset, this.cursorOffsetValue);
|
|
88
|
+
}
|
|
89
|
+
get selectionEnd() {
|
|
90
|
+
if (this.anchorOffset === null)
|
|
91
|
+
return this.cursorOffsetValue;
|
|
92
|
+
return Math.max(this.anchorOffset, this.cursorOffsetValue);
|
|
93
|
+
}
|
|
94
|
+
get selectedText() {
|
|
95
|
+
if (!this.hasSelection)
|
|
96
|
+
return "";
|
|
97
|
+
return this.textValue.slice(this.selectionStart, this.selectionEnd);
|
|
98
|
+
}
|
|
99
|
+
clearSelection() {
|
|
100
|
+
this.anchorOffset = null;
|
|
101
|
+
this.lastEditKind = null;
|
|
102
|
+
}
|
|
103
|
+
selectAll() {
|
|
104
|
+
this.anchorOffset = 0;
|
|
105
|
+
this.cursorOffsetValue = this.textValue.length;
|
|
106
|
+
this.lastEditKind = null;
|
|
107
|
+
}
|
|
108
|
+
selectLeft() {
|
|
109
|
+
this.anchorOffset ??= this.cursorOffsetValue;
|
|
110
|
+
this.moveCursorLeftRaw();
|
|
111
|
+
this.lastEditKind = null;
|
|
112
|
+
}
|
|
113
|
+
selectRight() {
|
|
114
|
+
this.anchorOffset ??= this.cursorOffsetValue;
|
|
115
|
+
this.moveCursorRightRaw();
|
|
116
|
+
this.lastEditKind = null;
|
|
117
|
+
}
|
|
118
|
+
selectToStart() {
|
|
119
|
+
this.anchorOffset ??= this.cursorOffsetValue;
|
|
120
|
+
this.cursorOffsetValue = 0;
|
|
121
|
+
this.lastEditKind = null;
|
|
122
|
+
}
|
|
123
|
+
selectToEnd() {
|
|
124
|
+
this.anchorOffset ??= this.cursorOffsetValue;
|
|
125
|
+
this.cursorOffsetValue = this.textValue.length;
|
|
126
|
+
this.lastEditKind = null;
|
|
127
|
+
}
|
|
128
|
+
selectWordLeft() {
|
|
129
|
+
this.anchorOffset ??= this.cursorOffsetValue;
|
|
130
|
+
this.cursorOffsetValue = this.wordBoundaryLeft(this.cursorOffsetValue);
|
|
131
|
+
this.lastEditKind = null;
|
|
132
|
+
}
|
|
133
|
+
selectWordRight() {
|
|
134
|
+
this.anchorOffset ??= this.cursorOffsetValue;
|
|
135
|
+
this.cursorOffsetValue = this.wordBoundaryRight(this.cursorOffsetValue);
|
|
136
|
+
this.lastEditKind = null;
|
|
137
|
+
}
|
|
138
|
+
deleteSelection() {
|
|
139
|
+
const start = this.selectionStart;
|
|
140
|
+
const end = this.selectionEnd;
|
|
141
|
+
this.textValue = this.textValue.slice(0, start) + this.textValue.slice(end);
|
|
142
|
+
this.cursorOffsetValue = start;
|
|
143
|
+
this.anchorOffset = null;
|
|
144
|
+
}
|
|
145
|
+
// ─── Editing ─────────────────────────────────────────────
|
|
146
|
+
/** Insert text at the current cursor position (replaces selection if any). */
|
|
147
|
+
insert(chars) {
|
|
148
|
+
// Single-char typing coalesces into one undo step; multi-char (paste) and
|
|
149
|
+
// typing-over-a-selection each form their own undo group.
|
|
150
|
+
if (chars.length === 1 && !this.hasSelection) {
|
|
151
|
+
this.recordEdit("insert");
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
this.breakUndo();
|
|
155
|
+
}
|
|
156
|
+
if (this.hasSelection)
|
|
157
|
+
this.deleteSelection();
|
|
158
|
+
this.textValue =
|
|
159
|
+
this.textValue.slice(0, this.cursorOffsetValue) + chars + this.textValue.slice(this.cursorOffsetValue);
|
|
160
|
+
this.cursorOffsetValue += chars.length;
|
|
161
|
+
}
|
|
162
|
+
/** Delete the grapheme cluster immediately to the left of the cursor (Backspace). */
|
|
163
|
+
deleteLeft() {
|
|
164
|
+
if (this.hasSelection) {
|
|
165
|
+
this.breakUndo();
|
|
166
|
+
this.deleteSelection();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (this.cursorOffsetValue === 0)
|
|
170
|
+
return;
|
|
171
|
+
this.recordEdit("delete");
|
|
172
|
+
const dl = new DisplayLine(this.textValue);
|
|
173
|
+
for (const slot of dl.slots) {
|
|
174
|
+
if (slot.offset + slot.length === this.cursorOffsetValue) {
|
|
175
|
+
this.textValue = this.textValue.slice(0, slot.offset) + this.textValue.slice(this.cursorOffsetValue);
|
|
176
|
+
this.cursorOffsetValue = slot.offset;
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Fallback: delete one code unit
|
|
181
|
+
this.textValue =
|
|
182
|
+
this.textValue.slice(0, this.cursorOffsetValue - 1) + this.textValue.slice(this.cursorOffsetValue);
|
|
183
|
+
this.cursorOffsetValue--;
|
|
184
|
+
}
|
|
185
|
+
/** Delete the grapheme cluster immediately to the right of the cursor (Delete key). */
|
|
186
|
+
deleteRight() {
|
|
187
|
+
if (this.hasSelection) {
|
|
188
|
+
this.breakUndo();
|
|
189
|
+
this.deleteSelection();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (this.cursorOffsetValue >= this.textValue.length)
|
|
193
|
+
return;
|
|
194
|
+
this.recordEdit("delete");
|
|
195
|
+
const dl = new DisplayLine(this.textValue);
|
|
196
|
+
for (const slot of dl.slots) {
|
|
197
|
+
if (slot.offset === this.cursorOffsetValue) {
|
|
198
|
+
this.textValue =
|
|
199
|
+
this.textValue.slice(0, this.cursorOffsetValue) + this.textValue.slice(slot.offset + slot.length);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Fallback: delete one code unit
|
|
204
|
+
this.textValue =
|
|
205
|
+
this.textValue.slice(0, this.cursorOffsetValue) + this.textValue.slice(this.cursorOffsetValue + 1);
|
|
206
|
+
}
|
|
207
|
+
/** Move cursor one grapheme to the left (ArrowLeft). */
|
|
208
|
+
moveCursorLeft() {
|
|
209
|
+
this.lastEditKind = null;
|
|
210
|
+
if (this.hasSelection) {
|
|
211
|
+
this.cursorOffsetValue = this.selectionStart;
|
|
212
|
+
this.anchorOffset = null;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
this.anchorOffset = null;
|
|
216
|
+
this.moveCursorLeftRaw();
|
|
217
|
+
}
|
|
218
|
+
/** Move cursor one grapheme to the right (ArrowRight). */
|
|
219
|
+
moveCursorRight() {
|
|
220
|
+
this.lastEditKind = null;
|
|
221
|
+
if (this.hasSelection) {
|
|
222
|
+
this.cursorOffsetValue = this.selectionEnd;
|
|
223
|
+
this.anchorOffset = null;
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
this.anchorOffset = null;
|
|
227
|
+
this.moveCursorRightRaw();
|
|
228
|
+
}
|
|
229
|
+
/** Move cursor to the beginning of the line (Home). */
|
|
230
|
+
moveCursorToStart() {
|
|
231
|
+
this.anchorOffset = null;
|
|
232
|
+
this.cursorOffsetValue = 0;
|
|
233
|
+
this.lastEditKind = null;
|
|
234
|
+
}
|
|
235
|
+
/** Move cursor to the end of the line (End). */
|
|
236
|
+
moveCursorToEnd() {
|
|
237
|
+
this.anchorOffset = null;
|
|
238
|
+
this.cursorOffsetValue = this.textValue.length;
|
|
239
|
+
this.lastEditKind = null;
|
|
240
|
+
}
|
|
241
|
+
/** Move cursor to the start of the previous word (Ctrl+ArrowLeft). */
|
|
242
|
+
moveCursorWordLeft() {
|
|
243
|
+
this.anchorOffset = null;
|
|
244
|
+
this.cursorOffsetValue = this.wordBoundaryLeft(this.cursorOffsetValue);
|
|
245
|
+
this.lastEditKind = null;
|
|
246
|
+
}
|
|
247
|
+
/** Move cursor to the end of the next word (Ctrl+ArrowRight). */
|
|
248
|
+
moveCursorWordRight() {
|
|
249
|
+
this.anchorOffset = null;
|
|
250
|
+
this.cursorOffsetValue = this.wordBoundaryRight(this.cursorOffsetValue);
|
|
251
|
+
this.lastEditKind = null;
|
|
252
|
+
}
|
|
253
|
+
/** Delete from cursor to the previous word boundary (Ctrl+Backspace). */
|
|
254
|
+
deleteWordLeft() {
|
|
255
|
+
if (this.hasSelection) {
|
|
256
|
+
this.breakUndo();
|
|
257
|
+
this.deleteSelection();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const target = this.wordBoundaryLeft(this.cursorOffsetValue);
|
|
261
|
+
if (target === this.cursorOffsetValue)
|
|
262
|
+
return;
|
|
263
|
+
this.breakUndo();
|
|
264
|
+
this.textValue = this.textValue.slice(0, target) + this.textValue.slice(this.cursorOffsetValue);
|
|
265
|
+
this.cursorOffsetValue = target;
|
|
266
|
+
}
|
|
267
|
+
/** Delete from cursor to the next word boundary (Ctrl+Delete). */
|
|
268
|
+
deleteWordRight() {
|
|
269
|
+
if (this.hasSelection) {
|
|
270
|
+
this.breakUndo();
|
|
271
|
+
this.deleteSelection();
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const target = this.wordBoundaryRight(this.cursorOffsetValue);
|
|
275
|
+
if (target === this.cursorOffsetValue)
|
|
276
|
+
return;
|
|
277
|
+
this.breakUndo();
|
|
278
|
+
this.textValue = this.textValue.slice(0, this.cursorOffsetValue) + this.textValue.slice(target);
|
|
279
|
+
}
|
|
280
|
+
moveCursorLeftRaw() {
|
|
281
|
+
if (this.cursorOffsetValue === 0)
|
|
282
|
+
return;
|
|
283
|
+
const dl = new DisplayLine(this.textValue);
|
|
284
|
+
for (const slot of dl.slots) {
|
|
285
|
+
if (slot.offset + slot.length === this.cursorOffsetValue) {
|
|
286
|
+
this.cursorOffsetValue = slot.offset;
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
this.cursorOffsetValue = Math.max(0, this.cursorOffsetValue - 1);
|
|
291
|
+
}
|
|
292
|
+
moveCursorRightRaw() {
|
|
293
|
+
if (this.cursorOffsetValue >= this.textValue.length)
|
|
294
|
+
return;
|
|
295
|
+
const dl = new DisplayLine(this.textValue);
|
|
296
|
+
for (const slot of dl.slots) {
|
|
297
|
+
if (slot.offset === this.cursorOffsetValue) {
|
|
298
|
+
this.cursorOffsetValue = slot.offset + slot.length;
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
this.cursorOffsetValue = Math.min(this.textValue.length, this.cursorOffsetValue + 1);
|
|
303
|
+
}
|
|
304
|
+
wordBoundaryLeft(pos) {
|
|
305
|
+
// Skip non-word chars going left, then skip word chars going left
|
|
306
|
+
let p = pos;
|
|
307
|
+
while (p > 0 && !InputState.isWordChar(this.textValue[p - 1]))
|
|
308
|
+
p--;
|
|
309
|
+
while (p > 0 && InputState.isWordChar(this.textValue[p - 1]))
|
|
310
|
+
p--;
|
|
311
|
+
return p;
|
|
312
|
+
}
|
|
313
|
+
wordBoundaryRight(pos) {
|
|
314
|
+
// Skip non-word chars going right, then skip word chars going right
|
|
315
|
+
let p = pos;
|
|
316
|
+
const len = this.textValue.length;
|
|
317
|
+
while (p < len && !InputState.isWordChar(this.textValue[p]))
|
|
318
|
+
p++;
|
|
319
|
+
while (p < len && InputState.isWordChar(this.textValue[p]))
|
|
320
|
+
p++;
|
|
321
|
+
return p;
|
|
322
|
+
}
|
|
323
|
+
static isWordChar(ch) {
|
|
324
|
+
return /\w/.test(ch);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { BoxConstraints, Size } from "@tuidom/core/common/geometryPromitives";
|
|
2
|
+
import type { StyleColor } from "@tuidom/core/dom/styles/tuiStyle";
|
|
3
|
+
import { RenderContext, TUIElement } from "@tuidom/core/dom/tuiElement";
|
|
4
|
+
export declare class BoxContainerElement extends TUIElement {
|
|
5
|
+
private bg;
|
|
6
|
+
private borderFg;
|
|
7
|
+
private title;
|
|
8
|
+
private titleFg;
|
|
9
|
+
private hasSeparator;
|
|
10
|
+
private child;
|
|
11
|
+
constructor();
|
|
12
|
+
setBg(value: StyleColor): void;
|
|
13
|
+
setBorderFg(value: StyleColor): void;
|
|
14
|
+
setTitle(value: string | undefined): void;
|
|
15
|
+
setTitleFg(value: StyleColor): void;
|
|
16
|
+
setHasSeparator(value: boolean): void;
|
|
17
|
+
setChild(child: TUIElement | null): void;
|
|
18
|
+
private get headerRows();
|
|
19
|
+
/** Вклад рамки (+ строк заголовка) в размер: контент живёт внутри этих insets. */
|
|
20
|
+
private get contentInsets();
|
|
21
|
+
getMinIntrinsicWidth(height: number): number;
|
|
22
|
+
getMaxIntrinsicWidth(height: number): number;
|
|
23
|
+
getMinIntrinsicHeight(width: number): number;
|
|
24
|
+
getMaxIntrinsicHeight(width: number): number;
|
|
25
|
+
protected performLayout(constraints: BoxConstraints): Size;
|
|
26
|
+
render(context: RenderContext): void;
|
|
27
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { DEFAULT_COLOR } from "@tuidom/core/common/colorUtils";
|
|
2
|
+
import { BoxConstraints, Offset, Point, Rect, Size } from "@tuidom/core/common/geometryPromitives";
|
|
3
|
+
import { BORDER_THICKNESS } from "@tuidom/core/dom/borderStyle";
|
|
4
|
+
import { RenderContext, TUIElement } from "@tuidom/core/dom/tuiElement";
|
|
5
|
+
export class BoxContainerElement extends TUIElement {
|
|
6
|
+
bg;
|
|
7
|
+
borderFg;
|
|
8
|
+
title;
|
|
9
|
+
titleFg;
|
|
10
|
+
hasSeparator;
|
|
11
|
+
child = null;
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
this.bg = DEFAULT_COLOR;
|
|
15
|
+
this.borderFg = DEFAULT_COLOR;
|
|
16
|
+
this.titleFg = DEFAULT_COLOR;
|
|
17
|
+
this.hasSeparator = false;
|
|
18
|
+
}
|
|
19
|
+
setBg(value) {
|
|
20
|
+
this.bg = value;
|
|
21
|
+
this.markDirty();
|
|
22
|
+
}
|
|
23
|
+
setBorderFg(value) {
|
|
24
|
+
this.borderFg = value;
|
|
25
|
+
this.markDirty();
|
|
26
|
+
}
|
|
27
|
+
setTitle(value) {
|
|
28
|
+
this.title = value;
|
|
29
|
+
this.markDirty();
|
|
30
|
+
}
|
|
31
|
+
setTitleFg(value) {
|
|
32
|
+
this.titleFg = value;
|
|
33
|
+
this.markDirty();
|
|
34
|
+
}
|
|
35
|
+
setHasSeparator(value) {
|
|
36
|
+
this.hasSeparator = value;
|
|
37
|
+
this.markDirty();
|
|
38
|
+
}
|
|
39
|
+
setChild(child) {
|
|
40
|
+
if (this.child) {
|
|
41
|
+
this.removeChild(this.child);
|
|
42
|
+
}
|
|
43
|
+
this.child = child;
|
|
44
|
+
if (this.child) {
|
|
45
|
+
this.appendChild(this.child);
|
|
46
|
+
}
|
|
47
|
+
this.markDirty();
|
|
48
|
+
}
|
|
49
|
+
get headerRows() {
|
|
50
|
+
if (!this.title)
|
|
51
|
+
return 0;
|
|
52
|
+
return this.hasSeparator ? 2 : 1;
|
|
53
|
+
}
|
|
54
|
+
/** Вклад рамки (+ строк заголовка) в размер: контент живёт внутри этих insets. */
|
|
55
|
+
get contentInsets() {
|
|
56
|
+
return {
|
|
57
|
+
left: BORDER_THICKNESS,
|
|
58
|
+
top: BORDER_THICKNESS + this.headerRows,
|
|
59
|
+
right: BORDER_THICKNESS,
|
|
60
|
+
bottom: BORDER_THICKNESS,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
getMinIntrinsicWidth(height) {
|
|
64
|
+
const insetsX = this.contentInsets.left + this.contentInsets.right;
|
|
65
|
+
if (!this.child)
|
|
66
|
+
return insetsX;
|
|
67
|
+
return this.child.getMinIntrinsicWidth(height) + insetsX;
|
|
68
|
+
}
|
|
69
|
+
getMaxIntrinsicWidth(height) {
|
|
70
|
+
const insetsX = this.contentInsets.left + this.contentInsets.right;
|
|
71
|
+
if (!this.child)
|
|
72
|
+
return insetsX;
|
|
73
|
+
return this.child.getMaxIntrinsicWidth(height) + insetsX;
|
|
74
|
+
}
|
|
75
|
+
getMinIntrinsicHeight(width) {
|
|
76
|
+
const insets = this.contentInsets;
|
|
77
|
+
const insetsY = insets.top + insets.bottom;
|
|
78
|
+
if (!this.child)
|
|
79
|
+
return insetsY;
|
|
80
|
+
return this.child.getMinIntrinsicHeight(Math.max(0, width - insets.left - insets.right)) + insetsY;
|
|
81
|
+
}
|
|
82
|
+
getMaxIntrinsicHeight(width) {
|
|
83
|
+
const insets = this.contentInsets;
|
|
84
|
+
const insetsY = insets.top + insets.bottom;
|
|
85
|
+
if (!this.child)
|
|
86
|
+
return insetsY;
|
|
87
|
+
return this.child.getMaxIntrinsicHeight(Math.max(0, width - insets.left - insets.right)) + insetsY;
|
|
88
|
+
}
|
|
89
|
+
performLayout(constraints) {
|
|
90
|
+
const containerSize = super.performLayout(constraints);
|
|
91
|
+
if (this.child) {
|
|
92
|
+
const insets = this.contentInsets;
|
|
93
|
+
const childWidth = Math.max(0, containerSize.width - insets.left - insets.right);
|
|
94
|
+
const childHeight = Math.max(0, containerSize.height - insets.top - insets.bottom);
|
|
95
|
+
this.layoutChild(this.child, insets.left, insets.top, BoxConstraints.tight(new Size(childWidth, childHeight)));
|
|
96
|
+
}
|
|
97
|
+
return containerSize;
|
|
98
|
+
}
|
|
99
|
+
render(context) {
|
|
100
|
+
const w = this.layoutSize.width;
|
|
101
|
+
const h = this.layoutSize.height;
|
|
102
|
+
// Fill background + frame (separator row when title has one).
|
|
103
|
+
const separators = this.title && this.hasSeparator ? [2] : undefined;
|
|
104
|
+
context.drawBox(0, 0, w, h, {
|
|
105
|
+
fg: this.resolveColor(this.borderFg),
|
|
106
|
+
bg: this.resolveColor(this.bg),
|
|
107
|
+
fill: true,
|
|
108
|
+
separators,
|
|
109
|
+
});
|
|
110
|
+
// Title row (y=1)
|
|
111
|
+
if (this.title) {
|
|
112
|
+
const titleX = Math.floor((w - this.title.length) / 2);
|
|
113
|
+
context.drawText(titleX, 1, this.title, {
|
|
114
|
+
fg: this.resolveColor(this.titleFg),
|
|
115
|
+
bg: this.resolveColor(this.bg),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
// Render child
|
|
119
|
+
this.renderChildren(context);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { RenderContext, TUIElement } from "@tuidom/core/dom/tuiElement";
|
|
2
|
+
export class BoxElement extends TUIElement {
|
|
3
|
+
render(context) {
|
|
4
|
+
const w = this.layoutSize.width;
|
|
5
|
+
const h = this.layoutSize.height;
|
|
6
|
+
const resolved = this.resolvedStyle;
|
|
7
|
+
for (let y = 0; y < h; y++) {
|
|
8
|
+
for (let x = 0; x < w; x++) {
|
|
9
|
+
const isTop = y === 0;
|
|
10
|
+
const isBottom = y === h - 1;
|
|
11
|
+
const isLeft = x === 0;
|
|
12
|
+
const isRight = x === w - 1;
|
|
13
|
+
let char;
|
|
14
|
+
if ((isTop || isBottom) && (isLeft || isRight)) {
|
|
15
|
+
char = "+";
|
|
16
|
+
}
|
|
17
|
+
else if (isTop || isBottom) {
|
|
18
|
+
char = "-";
|
|
19
|
+
}
|
|
20
|
+
else if (isLeft || isRight) {
|
|
21
|
+
char = "|";
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
char = " ";
|
|
25
|
+
}
|
|
26
|
+
context.setCell(x, y, { char, fg: resolved.fg, bg: resolved.bg });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TUIElement } from "@tuidom/core/dom/tuiElement";
|
|
2
|
+
/**
|
|
3
|
+
* «Красящий фон» кусок для flex-раскладок: разделители и паддинги статус-бара,
|
|
4
|
+
* хвост строки табов, пустая область группы редакторов. Собственного render
|
|
5
|
+
* нет: конструктор объявляет фон сентинелами INHERITED_* — «владею фоном,
|
|
6
|
+
* крашу его унаследованными цветами» — и заливку делает база
|
|
7
|
+
* (paintOwnBackground). Интринсики не переопределены: базовые нули — ровно то,
|
|
8
|
+
* что нужно филлеру (сам по себе места не просит, размер назначает контейнер).
|
|
9
|
+
*/
|
|
10
|
+
export declare class FillerElement extends TUIElement {
|
|
11
|
+
constructor();
|
|
12
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { INHERITED_BG, INHERITED_FG } from "@tuidom/core/dom/styles/tuiStyle";
|
|
2
|
+
import { TUIElement } from "@tuidom/core/dom/tuiElement";
|
|
3
|
+
/**
|
|
4
|
+
* «Красящий фон» кусок для flex-раскладок: разделители и паддинги статус-бара,
|
|
5
|
+
* хвост строки табов, пустая область группы редакторов. Собственного render
|
|
6
|
+
* нет: конструктор объявляет фон сентинелами INHERITED_* — «владею фоном,
|
|
7
|
+
* крашу его унаследованными цветами» — и заливку делает база
|
|
8
|
+
* (paintOwnBackground). Интринсики не переопределены: базовые нули — ровно то,
|
|
9
|
+
* что нужно филлеру (сам по себе места не просит, размер назначает контейнер).
|
|
10
|
+
*/
|
|
11
|
+
export class FillerElement extends TUIElement {
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
this.style = { fg: INHERITED_FG, bg: INHERITED_BG };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BoxConstraints, Size } from "@tuidom/core/common/geometryPromitives";
|
|
2
|
+
import { TUIElement } from "@tuidom/core/dom/tuiElement";
|
|
3
|
+
/**
|
|
4
|
+
* Контейнер «по содержимому»: занимает max-intrinsic размер единственного
|
|
5
|
+
* ребёнка (в пределах входящих constraints) вместо того, чтобы растягиваться.
|
|
6
|
+
* Типовой корень для диалогов/поповеров в overlay: слой даёт loose-constraints
|
|
7
|
+
* на весь экран, а окно должно остаться размером с контент.
|
|
8
|
+
*/
|
|
9
|
+
export declare class FitContentElement extends TUIElement {
|
|
10
|
+
private child;
|
|
11
|
+
setChild(child: TUIElement | null): void;
|
|
12
|
+
getChild(): TUIElement | null;
|
|
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,46 @@
|
|
|
1
|
+
import { BoxConstraints, Size } from "@tuidom/core/common/geometryPromitives";
|
|
2
|
+
import { TUIElement } from "@tuidom/core/dom/tuiElement";
|
|
3
|
+
/**
|
|
4
|
+
* Контейнер «по содержимому»: занимает max-intrinsic размер единственного
|
|
5
|
+
* ребёнка (в пределах входящих constraints) вместо того, чтобы растягиваться.
|
|
6
|
+
* Типовой корень для диалогов/поповеров в overlay: слой даёт loose-constraints
|
|
7
|
+
* на весь экран, а окно должно остаться размером с контент.
|
|
8
|
+
*/
|
|
9
|
+
export class FitContentElement extends TUIElement {
|
|
10
|
+
child = null;
|
|
11
|
+
setChild(child) {
|
|
12
|
+
if (this.child) {
|
|
13
|
+
this.removeChild(this.child);
|
|
14
|
+
}
|
|
15
|
+
this.child = child;
|
|
16
|
+
if (this.child) {
|
|
17
|
+
this.appendChild(this.child);
|
|
18
|
+
}
|
|
19
|
+
this.markDirty();
|
|
20
|
+
}
|
|
21
|
+
getChild() {
|
|
22
|
+
return this.child;
|
|
23
|
+
}
|
|
24
|
+
getMinIntrinsicWidth(height) {
|
|
25
|
+
return this.child?.getMinIntrinsicWidth(height) ?? 0;
|
|
26
|
+
}
|
|
27
|
+
getMaxIntrinsicWidth(height) {
|
|
28
|
+
return this.child?.getMaxIntrinsicWidth(height) ?? 0;
|
|
29
|
+
}
|
|
30
|
+
getMinIntrinsicHeight(width) {
|
|
31
|
+
return this.child?.getMinIntrinsicHeight(width) ?? 0;
|
|
32
|
+
}
|
|
33
|
+
getMaxIntrinsicHeight(width) {
|
|
34
|
+
return this.child?.getMaxIntrinsicHeight(width) ?? 0;
|
|
35
|
+
}
|
|
36
|
+
performLayout(constraints) {
|
|
37
|
+
const w = this.getMaxIntrinsicWidth(0);
|
|
38
|
+
const h = this.getMaxIntrinsicHeight(w);
|
|
39
|
+
const resultSize = constraints.constrain(new Size(w, h));
|
|
40
|
+
super.performLayout(BoxConstraints.tight(resultSize));
|
|
41
|
+
if (this.child) {
|
|
42
|
+
this.layoutChild(this.child, 0, 0, BoxConstraints.tight(resultSize));
|
|
43
|
+
}
|
|
44
|
+
return resultSize;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { BoxConstraints, Size } from "@tuidom/core/common/geometryPromitives";
|
|
2
|
+
import { TUIElement } from "@tuidom/core/dom/tuiElement";
|
|
3
|
+
export type HFlexChildSize = {
|
|
4
|
+
type: "fixed";
|
|
5
|
+
value: number;
|
|
6
|
+
} | {
|
|
7
|
+
type: "fit";
|
|
8
|
+
} | {
|
|
9
|
+
type: "fill";
|
|
10
|
+
};
|
|
11
|
+
export interface HFlexLayoutStyle {
|
|
12
|
+
width: HFlexChildSize;
|
|
13
|
+
height: number | "fill";
|
|
14
|
+
}
|
|
15
|
+
export declare function hflexFixed(value: number): HFlexChildSize;
|
|
16
|
+
export declare function hflexFit(): HFlexChildSize;
|
|
17
|
+
export declare function hflexFill(): HFlexChildSize;
|
|
18
|
+
export declare class HFlexElement extends TUIElement {
|
|
19
|
+
addChild(child: TUIElement, style: HFlexLayoutStyle): void;
|
|
20
|
+
replaceChildren(newChildren: TUIElement[]): void;
|
|
21
|
+
getMinIntrinsicWidth(height: number): number;
|
|
22
|
+
getMaxIntrinsicWidth(height: number): number;
|
|
23
|
+
getMinIntrinsicHeight(width: number): number;
|
|
24
|
+
getMaxIntrinsicHeight(width: number): number;
|
|
25
|
+
protected performLayout(constraints: BoxConstraints): Size;
|
|
26
|
+
}
|