lexgui 8.3.2 → 8.4.1
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/build/components/BaseComponent.d.ts +4 -2
- package/build/components/Empty.d.ts +8 -0
- package/build/core/Namespace.js +1 -1
- package/build/core/Namespace.js.map +1 -1
- package/build/core/Panel.d.ts +14 -7
- package/build/extensions/AssetView.d.ts +3 -2
- package/build/extensions/AssetView.js +37 -19
- package/build/extensions/AssetView.js.map +1 -1
- package/build/extensions/CodeEditor.d.ts +11 -2
- package/build/extensions/CodeEditor.js +214 -40
- package/build/extensions/CodeEditor.js.map +1 -1
- package/build/lexgui.all.js +420 -77
- package/build/lexgui.all.js.map +1 -1
- package/build/lexgui.all.min.js +1 -1
- package/build/lexgui.all.module.js +420 -77
- package/build/lexgui.all.module.js.map +1 -1
- package/build/lexgui.all.module.min.js +1 -1
- package/build/lexgui.css +38 -2
- package/build/lexgui.js +383 -58
- package/build/lexgui.js.map +1 -1
- package/build/lexgui.min.css +1 -1
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +383 -58
- package/build/lexgui.module.js.map +1 -1
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +28 -1
- package/examples/all-components.html +1 -0
- package/examples/asset-view.html +7 -33
- package/examples/code-editor.html +8 -0
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@ const g$2 = globalThis;
|
|
|
12
12
|
let LX = g$2.LX;
|
|
13
13
|
if (!LX) {
|
|
14
14
|
LX = {
|
|
15
|
-
version: '8.
|
|
15
|
+
version: '8.4.1',
|
|
16
16
|
ready: false,
|
|
17
17
|
extensions: [], // Store extensions used
|
|
18
18
|
extraCommandbarEntries: [], // User specific entries for command bar
|
|
@@ -432,6 +432,8 @@ var ComponentType$1;
|
|
|
432
432
|
ComponentType[ComponentType["LABEL"] = 39] = "LABEL";
|
|
433
433
|
ComponentType[ComponentType["BLANK"] = 40] = "BLANK";
|
|
434
434
|
ComponentType[ComponentType["RATE"] = 41] = "RATE";
|
|
435
|
+
ComponentType[ComponentType["EMPTY"] = 42] = "EMPTY";
|
|
436
|
+
ComponentType[ComponentType["DESCRIPTION"] = 43] = "DESCRIPTION";
|
|
435
437
|
})(ComponentType$1 || (ComponentType$1 = {}));
|
|
436
438
|
LX.ComponentType = ComponentType$1;
|
|
437
439
|
/**
|
|
@@ -733,6 +735,9 @@ class Button extends BaseComponent$1 {
|
|
|
733
735
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
734
736
|
wValue.style.width = `calc( 100% - ${realNameWidth})`;
|
|
735
737
|
};
|
|
738
|
+
this.onSetDisabled = (disabled) => {
|
|
739
|
+
wValue.disabled = disabled;
|
|
740
|
+
};
|
|
736
741
|
// In case of swap, set if a change has to be performed
|
|
737
742
|
this.setState = function (v, skipCallback) {
|
|
738
743
|
const swapInput = wValue.querySelector('input');
|
|
@@ -1470,6 +1475,12 @@ class NumberInput extends BaseComponent$1 {
|
|
|
1470
1475
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
1471
1476
|
container.style.width = options.inputWidth ?? `calc( 100% - ${realNameWidth})`;
|
|
1472
1477
|
};
|
|
1478
|
+
this.onSetDisabled = (disabled) => {
|
|
1479
|
+
vecinput.disabled = disabled;
|
|
1480
|
+
const slider = this.root.querySelector('input[type="range"]');
|
|
1481
|
+
if (slider)
|
|
1482
|
+
slider.disabled = disabled;
|
|
1483
|
+
};
|
|
1473
1484
|
this.setLimits = (newMin, newMax, newStep) => { };
|
|
1474
1485
|
var container = document.createElement('div');
|
|
1475
1486
|
container.className = 'lexnumber';
|
|
@@ -1500,7 +1511,7 @@ class NumberInput extends BaseComponent$1 {
|
|
|
1500
1511
|
if (!options.skipSlider && options.min !== undefined && options.max !== undefined) {
|
|
1501
1512
|
let sliderBox = LX.makeContainer(['100%', 'auto'], 'z-1 input-box', '', box);
|
|
1502
1513
|
let slider = document.createElement('input');
|
|
1503
|
-
slider.className = 'lexinputslider';
|
|
1514
|
+
slider.className = 'lexinputslider disabled:pointer-events-none disabled:opacity-50';
|
|
1504
1515
|
slider.min = options.min;
|
|
1505
1516
|
slider.max = options.max;
|
|
1506
1517
|
slider.step = options.step ?? 1;
|
|
@@ -1637,6 +1648,11 @@ class TextInput extends BaseComponent$1 {
|
|
|
1637
1648
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
1638
1649
|
container.style.width = options.inputWidth ?? `calc( 100% - ${realNameWidth})`;
|
|
1639
1650
|
};
|
|
1651
|
+
this.onSetDisabled = (disabled) => {
|
|
1652
|
+
const input = this.root.querySelector('input');
|
|
1653
|
+
if (input)
|
|
1654
|
+
input.disabled = disabled;
|
|
1655
|
+
};
|
|
1640
1656
|
this.valid = (v, matchField) => {
|
|
1641
1657
|
v = v ?? this.value();
|
|
1642
1658
|
if (!options.pattern)
|
|
@@ -1796,6 +1812,9 @@ class Select extends BaseComponent$1 {
|
|
|
1796
1812
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
1797
1813
|
container.style.width = options.inputWidth ?? `calc( 100% - ${realNameWidth})`;
|
|
1798
1814
|
};
|
|
1815
|
+
this.onSetDisabled = (disabled) => {
|
|
1816
|
+
selectedOption?.setDisabled(disabled);
|
|
1817
|
+
};
|
|
1799
1818
|
let container = document.createElement('div');
|
|
1800
1819
|
container.className = 'lexselect';
|
|
1801
1820
|
this.root.appendChild(container);
|
|
@@ -2070,6 +2089,13 @@ class ArrayInput extends BaseComponent$1 {
|
|
|
2070
2089
|
this._trigger(new IEvent$1(name, values, event), callback);
|
|
2071
2090
|
}
|
|
2072
2091
|
};
|
|
2092
|
+
this.onSetDisabled = (disabled) => {
|
|
2093
|
+
if (this.root.dataset['opened'] == 'true' && disabled) {
|
|
2094
|
+
this.root.dataset['opened'] = false;
|
|
2095
|
+
this.root.querySelector('.lexarrayitems').toggleAttribute('hidden', true);
|
|
2096
|
+
}
|
|
2097
|
+
toggleButton.setDisabled(disabled);
|
|
2098
|
+
};
|
|
2073
2099
|
// Add open array button
|
|
2074
2100
|
let container = document.createElement('div');
|
|
2075
2101
|
container.className = 'lexarray shrink-1 grow-1 ml-4';
|
|
@@ -2161,9 +2187,10 @@ class Card extends BaseComponent$1 {
|
|
|
2161
2187
|
const container = LX.makeContainer(['100%', 'auto'], 'lexcard max-w-sm flex flex-col gap-4 bg-card border-color rounded-xl py-6', '', this.root);
|
|
2162
2188
|
if (options.header) {
|
|
2163
2189
|
const hasAction = options.header.action !== undefined;
|
|
2190
|
+
const actionButtonOptions = options.header.action.options ?? {};
|
|
2164
2191
|
let header = LX.makeContainer(['100%', 'auto'], `flex ${hasAction ? 'flex-row gap-4' : 'flex-col gap-1'} px-6`, '', container);
|
|
2165
2192
|
if (hasAction) {
|
|
2166
|
-
const actionBtn = new Button(null, options.header.action.name, options.header.action.callback, { buttonClass: 'secondary' });
|
|
2193
|
+
const actionBtn = new Button(null, options.header.action.name, options.header.action.callback, { buttonClass: 'secondary', ...actionButtonOptions });
|
|
2167
2194
|
header.appendChild(actionBtn.root);
|
|
2168
2195
|
const titleDescBox = LX.makeContainer(['75%', 'auto'], `flex flex-col gap-1`, '');
|
|
2169
2196
|
header.prepend(titleDescBox);
|
|
@@ -2230,6 +2257,9 @@ class Checkbox extends BaseComponent$1 {
|
|
|
2230
2257
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
2231
2258
|
container.style.width = options.inputWidth ?? `calc( 100% - ${realNameWidth})`;
|
|
2232
2259
|
};
|
|
2260
|
+
this.onSetDisabled = (disabled) => {
|
|
2261
|
+
checkbox.disabled = disabled;
|
|
2262
|
+
};
|
|
2233
2263
|
let container = document.createElement('div');
|
|
2234
2264
|
container.className = 'flex items-center gap-2 my-0 mx-auto [&_span]:truncate [&_span]:flex-auto-fill';
|
|
2235
2265
|
this.root.appendChild(container);
|
|
@@ -2858,7 +2888,12 @@ class ColorInput extends BaseComponent$1 {
|
|
|
2858
2888
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
2859
2889
|
container.style.width = `calc( 100% - ${realNameWidth})`;
|
|
2860
2890
|
};
|
|
2861
|
-
|
|
2891
|
+
this.onSetDisabled = (disabled) => {
|
|
2892
|
+
textComponent.setDisabled(disabled);
|
|
2893
|
+
sampleContainer.classList.toggle('pointer-events-none', disabled);
|
|
2894
|
+
sampleContainer.classList.toggle('opacity-50', disabled);
|
|
2895
|
+
};
|
|
2896
|
+
let container = document.createElement('span');
|
|
2862
2897
|
container.className = 'lexcolor';
|
|
2863
2898
|
this.root.appendChild(container);
|
|
2864
2899
|
this.picker = new ColorPicker(value, {
|
|
@@ -3022,6 +3057,11 @@ class Counter extends BaseComponent$1 {
|
|
|
3022
3057
|
this._trigger(new IEvent$1(name, newValue, event), callback);
|
|
3023
3058
|
}
|
|
3024
3059
|
};
|
|
3060
|
+
this.onSetDisabled = (disabled) => {
|
|
3061
|
+
substrButton.setDisabled(disabled);
|
|
3062
|
+
addButton.setDisabled(disabled);
|
|
3063
|
+
input.disabled = disabled;
|
|
3064
|
+
};
|
|
3025
3065
|
this.count = value;
|
|
3026
3066
|
const min = options.min ?? 0;
|
|
3027
3067
|
const max = options.max ?? 100;
|
|
@@ -3767,6 +3807,12 @@ class DatePicker extends BaseComponent$1 {
|
|
|
3767
3807
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
3768
3808
|
container.style.width = `calc( 100% - ${realNameWidth})`;
|
|
3769
3809
|
};
|
|
3810
|
+
this.onSetDisabled = (disabled) => {
|
|
3811
|
+
const buttons = this.root.querySelectorAll('button');
|
|
3812
|
+
buttons.forEach((b) => {
|
|
3813
|
+
b.disabled = disabled;
|
|
3814
|
+
});
|
|
3815
|
+
};
|
|
3770
3816
|
const container = LX.makeContainer(['auto', 'auto'], 'lexdate flex flex-row');
|
|
3771
3817
|
this.root.appendChild(container);
|
|
3772
3818
|
if (!dateAsRange) {
|
|
@@ -4159,6 +4205,52 @@ class Dial extends BaseComponent$1 {
|
|
|
4159
4205
|
LX.CanvasDial = CanvasDial;
|
|
4160
4206
|
LX.Dial = Dial;
|
|
4161
4207
|
|
|
4208
|
+
// Empty.ts @jxarco
|
|
4209
|
+
/**
|
|
4210
|
+
* @class Empty
|
|
4211
|
+
* @description Empty Component
|
|
4212
|
+
*/
|
|
4213
|
+
class Empty extends BaseComponent$1 {
|
|
4214
|
+
constructor(name, options = {}) {
|
|
4215
|
+
options.hideName = true;
|
|
4216
|
+
super(ComponentType$1.EMPTY, name, null, options);
|
|
4217
|
+
this.root.classList.add('place-content-center');
|
|
4218
|
+
const container = LX.makeContainer(['100%', 'auto'], 'lexcard max-w-sm flex flex-col gap-4 bg-card border-color rounded-xl py-6', '', this.root);
|
|
4219
|
+
if (options.header) {
|
|
4220
|
+
let header = LX.makeContainer(['100%', 'auto'], `flex flex-col gap-4 px-6 items-center`, '', container);
|
|
4221
|
+
if (options.header.icon) {
|
|
4222
|
+
const icon = LX.makeIcon(options.header.icon, { iconClass: 'bg-secondary p-2 rounded-lg!', svgClass: 'lg' });
|
|
4223
|
+
header.appendChild(icon);
|
|
4224
|
+
}
|
|
4225
|
+
else if (options.header.avatar) {
|
|
4226
|
+
const avatar = new LX.Avatar(options.header.avatar);
|
|
4227
|
+
header.appendChild(avatar.root);
|
|
4228
|
+
}
|
|
4229
|
+
if (options.header.title) {
|
|
4230
|
+
LX.makeElement('div', 'text-center text-foreground leading-none font-medium', options.header.title, header);
|
|
4231
|
+
}
|
|
4232
|
+
if (options.header.description) {
|
|
4233
|
+
LX.makeElement('div', 'text-sm text-center text-balance text-muted-foreground', options.header.description, header);
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
if (options.actions) {
|
|
4237
|
+
const content = LX.makeContainer(['100%', 'auto'], 'flex flex-row gap-1 px-6 justify-center', '', container);
|
|
4238
|
+
for (let a of options.actions) {
|
|
4239
|
+
const action = new LX.Button(null, a.name, a.callback, { buttonClass: "sm outline", ...a.options });
|
|
4240
|
+
content.appendChild(action.root);
|
|
4241
|
+
}
|
|
4242
|
+
}
|
|
4243
|
+
if (options.footer) {
|
|
4244
|
+
const footer = LX.makeContainer(['100%', 'auto'], 'flex flex-col gap-1 px-6', '', container);
|
|
4245
|
+
const elements = [].concat(options.footer);
|
|
4246
|
+
for (let e of elements) {
|
|
4247
|
+
footer.appendChild(e.root ? e.root : e);
|
|
4248
|
+
}
|
|
4249
|
+
}
|
|
4250
|
+
}
|
|
4251
|
+
}
|
|
4252
|
+
LX.Empty = Empty;
|
|
4253
|
+
|
|
4162
4254
|
// FileInput.ts @jxarco
|
|
4163
4255
|
/**
|
|
4164
4256
|
* @class FileInput
|
|
@@ -4174,6 +4266,9 @@ class FileInput extends BaseComponent$1 {
|
|
|
4174
4266
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
4175
4267
|
input.style.width = `calc( 100% - ${realNameWidth})`;
|
|
4176
4268
|
};
|
|
4269
|
+
this.onSetDisabled = (disabled) => {
|
|
4270
|
+
input.disabled = disabled;
|
|
4271
|
+
};
|
|
4177
4272
|
// Create hidden input
|
|
4178
4273
|
let input = document.createElement('input');
|
|
4179
4274
|
input.className = 'lexfileinput';
|
|
@@ -4381,6 +4476,9 @@ class Layers extends BaseComponent$1 {
|
|
|
4381
4476
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
4382
4477
|
container.style.width = `calc( 100% - ${realNameWidth})`;
|
|
4383
4478
|
};
|
|
4479
|
+
this.onSetDisabled = (disabled) => {
|
|
4480
|
+
this.setLayers(value);
|
|
4481
|
+
};
|
|
4384
4482
|
const container = LX.makeElement('div', 'lexlayers grid', '', this.root);
|
|
4385
4483
|
const maxBits = options.maxBits ?? 16;
|
|
4386
4484
|
this.setLayers = (val) => {
|
|
@@ -4457,6 +4555,9 @@ class List extends BaseComponent$1 {
|
|
|
4457
4555
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
4458
4556
|
listContainer.style.width = `calc( 100% - ${realNameWidth})`;
|
|
4459
4557
|
};
|
|
4558
|
+
this.onSetDisabled = (disabled) => {
|
|
4559
|
+
this._updateValues(values);
|
|
4560
|
+
};
|
|
4460
4561
|
this._updateValues = (newValues) => {
|
|
4461
4562
|
values = newValues;
|
|
4462
4563
|
listContainer.innerHTML = '';
|
|
@@ -4924,16 +5025,19 @@ class Map2D extends BaseComponent$1 {
|
|
|
4924
5025
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
4925
5026
|
container.style.width = `calc( 100% - ${realNameWidth})`;
|
|
4926
5027
|
};
|
|
5028
|
+
this.onSetDisabled = (disabled) => {
|
|
5029
|
+
openerButton.setDisabled(disabled);
|
|
5030
|
+
};
|
|
4927
5031
|
var container = document.createElement('div');
|
|
4928
5032
|
container.className = 'lexmap2d';
|
|
4929
5033
|
this.root.appendChild(container);
|
|
4930
5034
|
this.map2d = new CanvasMap2D(points, callback, options);
|
|
4931
|
-
const
|
|
4932
|
-
const
|
|
4933
|
-
this._popover = new Popover(
|
|
5035
|
+
const icon = LX.makeIcon(options.mapIcon ?? 'SquareMousePointer');
|
|
5036
|
+
const openerButton = new Button(null, 'Open Map', () => {
|
|
5037
|
+
this._popover = new Popover(openerButton.root, [this.map2d]);
|
|
4934
5038
|
}, { buttonClass: `outline justify-between`, disabled: this.disabled });
|
|
4935
|
-
|
|
4936
|
-
container.appendChild(
|
|
5039
|
+
openerButton.root.querySelector('button').appendChild(icon);
|
|
5040
|
+
container.appendChild(openerButton.root);
|
|
4937
5041
|
LX.doAsync(this.onResize.bind(this));
|
|
4938
5042
|
}
|
|
4939
5043
|
}
|
|
@@ -5640,6 +5744,9 @@ class OTPInput extends BaseComponent$1 {
|
|
|
5640
5744
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
5641
5745
|
container.style.width = `calc( 100% - ${realNameWidth})`;
|
|
5642
5746
|
};
|
|
5747
|
+
this.onSetDisabled = (disabled) => {
|
|
5748
|
+
_refreshInput(value);
|
|
5749
|
+
};
|
|
5643
5750
|
const container = document.createElement('div');
|
|
5644
5751
|
container.className = 'lexotp flex flex-row items-center';
|
|
5645
5752
|
this.root.appendChild(container);
|
|
@@ -5653,7 +5760,7 @@ class OTPInput extends BaseComponent$1 {
|
|
|
5653
5760
|
for (let j = 0; j < g.length; ++j) {
|
|
5654
5761
|
let number = valueString[itemsCount++];
|
|
5655
5762
|
number = number == 'x' ? '' : number;
|
|
5656
|
-
const slotDom = LX.makeContainer(['36px', '30px'], 'lexotpslot border-t-color border-b-color border-l-color px-3 cursor-text select-none font-medium outline-none', number, container);
|
|
5763
|
+
const slotDom = LX.makeContainer(['36px', '30px'], 'lexotpslot content-center border-t-color border-b-color border-l-color px-3 cursor-text select-none font-medium outline-none', number, container);
|
|
5657
5764
|
slotDom.tabIndex = '1';
|
|
5658
5765
|
if (this.disabled) {
|
|
5659
5766
|
slotDom.classList.add('disabled');
|
|
@@ -6057,6 +6164,9 @@ class RangeInput extends BaseComponent$1 {
|
|
|
6057
6164
|
slider.style.setProperty('--range-fix-max-offset', `${diffMaxOffset}rem`);
|
|
6058
6165
|
}
|
|
6059
6166
|
};
|
|
6167
|
+
this.onSetDisabled = (disabled) => {
|
|
6168
|
+
slider.disabled = disabled;
|
|
6169
|
+
};
|
|
6060
6170
|
const container = document.createElement('div');
|
|
6061
6171
|
container.className = 'lexrange relative py-3';
|
|
6062
6172
|
this.root.appendChild(container);
|
|
@@ -6165,6 +6275,9 @@ class Rate extends BaseComponent$1 {
|
|
|
6165
6275
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
6166
6276
|
container.style.width = `calc( 100% - ${realNameWidth})`;
|
|
6167
6277
|
};
|
|
6278
|
+
this.onSetDisabled = (disabled) => {
|
|
6279
|
+
container.dataset['disabled'] = disabled.toString();
|
|
6280
|
+
};
|
|
6168
6281
|
const container = document.createElement('div');
|
|
6169
6282
|
container.className = 'lexrate relative data-[disabled=true]:pointer-events-none';
|
|
6170
6283
|
container.dataset['disabled'] = this.disabled.toString();
|
|
@@ -7308,6 +7421,9 @@ class Tags extends BaseComponent$1 {
|
|
|
7308
7421
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
7309
7422
|
tagsContainer.style.width = `calc( 100% - ${realNameWidth})`;
|
|
7310
7423
|
};
|
|
7424
|
+
this.onSetDisabled = (disabled) => {
|
|
7425
|
+
this.generateTags(arrayValue);
|
|
7426
|
+
};
|
|
7311
7427
|
// Show tags
|
|
7312
7428
|
const tagsContainer = document.createElement('div');
|
|
7313
7429
|
tagsContainer.className = 'inline-flex flex-wrap gap-1 bg-card/50 rounded-lg pad-xs [&_input]:w-2/3';
|
|
@@ -7376,6 +7492,11 @@ class TextArea extends BaseComponent$1 {
|
|
|
7376
7492
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
7377
7493
|
container.style.width = options.inputWidth ?? `calc( 100% - ${realNameWidth})`;
|
|
7378
7494
|
};
|
|
7495
|
+
this.onSetDisabled = (disabled) => {
|
|
7496
|
+
const textarea = this.root.querySelector('textarea');
|
|
7497
|
+
if (textarea)
|
|
7498
|
+
textarea.disabled = disabled;
|
|
7499
|
+
};
|
|
7379
7500
|
let container = document.createElement('div');
|
|
7380
7501
|
container.className = 'lextextarea';
|
|
7381
7502
|
container.style.display = 'flex';
|
|
@@ -7493,6 +7614,9 @@ class Toggle extends BaseComponent$1 {
|
|
|
7493
7614
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
7494
7615
|
container.style.width = options.inputWidth ?? `calc( 100% - ${realNameWidth})`;
|
|
7495
7616
|
};
|
|
7617
|
+
this.onSetDisabled = (disabled) => {
|
|
7618
|
+
toggle.disabled = disabled;
|
|
7619
|
+
};
|
|
7496
7620
|
var container = document.createElement('div');
|
|
7497
7621
|
container.className = 'flex flex-row gap-2 items-center';
|
|
7498
7622
|
this.root.appendChild(container);
|
|
@@ -7562,6 +7686,12 @@ class Vector extends BaseComponent$1 {
|
|
|
7562
7686
|
const realNameWidth = this.root.domName?.style.width ?? '0px';
|
|
7563
7687
|
container.style.width = `calc( 100% - ${realNameWidth})`;
|
|
7564
7688
|
};
|
|
7689
|
+
this.onSetDisabled = (disabled) => {
|
|
7690
|
+
const inputs = this.root.querySelectorAll('input');
|
|
7691
|
+
inputs.forEach((i) => {
|
|
7692
|
+
i.disabled = disabled;
|
|
7693
|
+
});
|
|
7694
|
+
};
|
|
7565
7695
|
this.setLimits = (newMin, newMax, newStep) => { };
|
|
7566
7696
|
const vectorInputs = [];
|
|
7567
7697
|
var container = document.createElement('div');
|
|
@@ -8306,6 +8436,19 @@ let Panel$2 = class Panel {
|
|
|
8306
8436
|
component.type = ComponentType$1.LABEL;
|
|
8307
8437
|
return component;
|
|
8308
8438
|
}
|
|
8439
|
+
/**
|
|
8440
|
+
* @method addDescription
|
|
8441
|
+
* @param {String} value Information string
|
|
8442
|
+
* @param {Object} options Text options
|
|
8443
|
+
*/
|
|
8444
|
+
addDescription(value, options = {}) {
|
|
8445
|
+
options.disabled = true;
|
|
8446
|
+
options.fitHeight = true;
|
|
8447
|
+
options.inputClass = LX.mergeClass('bg-none', options.inputClass);
|
|
8448
|
+
const component = this.addTextArea(null, value, null, options);
|
|
8449
|
+
component.type = ComponentType$1.DESCRIPTION;
|
|
8450
|
+
return component;
|
|
8451
|
+
}
|
|
8309
8452
|
/**
|
|
8310
8453
|
* @method addButton
|
|
8311
8454
|
* @param {String} name Component name
|
|
@@ -8344,18 +8487,22 @@ let Panel$2 = class Panel {
|
|
|
8344
8487
|
}
|
|
8345
8488
|
/**
|
|
8346
8489
|
* @method addCard
|
|
8347
|
-
* @param {String} name
|
|
8348
|
-
* @param {Object} options
|
|
8349
|
-
* text: Card text
|
|
8350
|
-
* link: Card link
|
|
8351
|
-
* title: Card dom title
|
|
8352
|
-
* src: url of the image
|
|
8353
|
-
* callback (Function): function to call on click
|
|
8490
|
+
* @param {String} name
|
|
8491
|
+
* @param {Object} options
|
|
8354
8492
|
*/
|
|
8355
8493
|
addCard(name, options = {}) {
|
|
8356
8494
|
const component = new Card(name, options);
|
|
8357
8495
|
return this._attachComponent(component);
|
|
8358
8496
|
}
|
|
8497
|
+
/**
|
|
8498
|
+
* @method addEmpty
|
|
8499
|
+
* @param {String} name
|
|
8500
|
+
* @param {Object} options
|
|
8501
|
+
*/
|
|
8502
|
+
addEmpty(name, options = {}) {
|
|
8503
|
+
const component = new Empty(name, options);
|
|
8504
|
+
return this._attachComponent(component);
|
|
8505
|
+
}
|
|
8359
8506
|
/**
|
|
8360
8507
|
* @method addForm
|
|
8361
8508
|
* @param {String} name Component name
|
|
@@ -11170,6 +11317,8 @@ function asTooltip(trigger, content, options = {}) {
|
|
|
11170
11317
|
// Watch for trigger being removed from the DOM before mouseleave fires
|
|
11171
11318
|
rafId = requestAnimationFrame(_watchConnection);
|
|
11172
11319
|
LX.doAsync(() => {
|
|
11320
|
+
if (!tooltipDom)
|
|
11321
|
+
return;
|
|
11173
11322
|
const position = [0, 0];
|
|
11174
11323
|
const offsetX = parseFloat(trigger.dataset['tooltipOffsetX'] ?? _offsetX);
|
|
11175
11324
|
const offsetY = parseFloat(trigger.dataset['tooltipOffsetY'] ?? _offsetY);
|
|
@@ -13335,7 +13484,9 @@ class Tour {
|
|
|
13335
13484
|
// using a fullscreen SVG with "rect" elements
|
|
13336
13485
|
_generateMask(reference) {
|
|
13337
13486
|
this.tourContainer.innerHTML = ''; // Clear previous content
|
|
13487
|
+
const scrollTop = document.scrollingElement?.scrollTop ?? 0;
|
|
13338
13488
|
this.tourMask = LX.makeContainer(['100%', '100%'], 'tour-mask absolute inset-0');
|
|
13489
|
+
this.tourMask.style.top = `${scrollTop}px`;
|
|
13339
13490
|
this.tourContainer.appendChild(this.tourMask);
|
|
13340
13491
|
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
13341
13492
|
svg.style.width = '100%';
|
|
@@ -13398,7 +13549,7 @@ class Tour {
|
|
|
13398
13549
|
// Reference Highlight
|
|
13399
13550
|
const refContainer = LX.makeContainer(['0', '0'], 'tour-ref-mask absolute');
|
|
13400
13551
|
refContainer.style.left = `${boundingX - hOffset - 1}px`;
|
|
13401
|
-
refContainer.style.top = `${boundingY - vOffset - 1}px`;
|
|
13552
|
+
refContainer.style.top = `${boundingY - vOffset - 1 + scrollTop}px`;
|
|
13402
13553
|
refContainer.style.width = `${boundingWidth + hOffset * 2 + 2}px`;
|
|
13403
13554
|
refContainer.style.height = `${boundingHeight + vOffset * 2 + 2}px`;
|
|
13404
13555
|
this.tourContainer.appendChild(refContainer);
|
|
@@ -14443,12 +14594,12 @@ class CodeDocument {
|
|
|
14443
14594
|
getText(separator = '\n') {
|
|
14444
14595
|
return this._lines.join(separator);
|
|
14445
14596
|
}
|
|
14446
|
-
setText(text) {
|
|
14597
|
+
setText(text, silent = false) {
|
|
14447
14598
|
this._lines = text.split(/\r?\n/);
|
|
14448
14599
|
if (this._lines.length === 0) {
|
|
14449
14600
|
this._lines = [''];
|
|
14450
14601
|
}
|
|
14451
|
-
if (this.onChange)
|
|
14602
|
+
if (!silent && this.onChange)
|
|
14452
14603
|
this.onChange(this);
|
|
14453
14604
|
}
|
|
14454
14605
|
getCharAt(line, col) {
|
|
@@ -14612,6 +14763,7 @@ class UndoManager {
|
|
|
14612
14763
|
_lastPushTime = 0;
|
|
14613
14764
|
_groupThresholdMs;
|
|
14614
14765
|
_maxSteps;
|
|
14766
|
+
_savedDepth = 0;
|
|
14615
14767
|
constructor(groupThresholdMs = 2000, maxSteps = 200) {
|
|
14616
14768
|
this._groupThresholdMs = groupThresholdMs;
|
|
14617
14769
|
this._maxSteps = maxSteps;
|
|
@@ -14683,11 +14835,19 @@ class UndoManager {
|
|
|
14683
14835
|
canRedo() {
|
|
14684
14836
|
return this._redoStack.length > 0;
|
|
14685
14837
|
}
|
|
14838
|
+
markSaved() {
|
|
14839
|
+
this._flush();
|
|
14840
|
+
this._savedDepth = this._undoStack.length;
|
|
14841
|
+
}
|
|
14842
|
+
isModified() {
|
|
14843
|
+
return this._undoStack.length !== this._savedDepth || this._pendingOps.length > 0;
|
|
14844
|
+
}
|
|
14686
14845
|
clear() {
|
|
14687
14846
|
this._undoStack.length = 0;
|
|
14688
14847
|
this._redoStack.length = 0;
|
|
14689
14848
|
this._pendingOps.length = 0;
|
|
14690
14849
|
this._lastPushTime = 0;
|
|
14850
|
+
this._savedDepth = 0;
|
|
14691
14851
|
}
|
|
14692
14852
|
_flush() {
|
|
14693
14853
|
if (this._pendingOps.length === 0)
|
|
@@ -15256,8 +15416,36 @@ LX.Area;
|
|
|
15256
15416
|
LX.Panel;
|
|
15257
15417
|
LX.Tabs;
|
|
15258
15418
|
LX.NodeTree;
|
|
15259
|
-
/** Matches hex color literals: #rgb #rgba #rrggbb #rrggbbaa */
|
|
15260
15419
|
const HEX_COLOR_RE = /#(?:[0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{4}|[0-9a-fA-F]{3})\b/g;
|
|
15420
|
+
const URL_REGEX = /(https?:\/\/[^\s"'<>)\]]+)/g;
|
|
15421
|
+
/**
|
|
15422
|
+
* Returns true if the string token at `idx` in the token list is a module import path.
|
|
15423
|
+
*/
|
|
15424
|
+
function isImportPath(tokens, idx) {
|
|
15425
|
+
const isWs = (t) => /^\s+$/.test(t.value);
|
|
15426
|
+
const isImportWord = (t) => t.value === 'require' || t.value === 'import';
|
|
15427
|
+
for (let i = idx - 1; i >= 0; i--) {
|
|
15428
|
+
const t = tokens[i];
|
|
15429
|
+
if (isWs(t))
|
|
15430
|
+
continue;
|
|
15431
|
+
if (t.type === 'keyword' && t.value === 'from')
|
|
15432
|
+
return true;
|
|
15433
|
+
if (isImportWord(t))
|
|
15434
|
+
return true;
|
|
15435
|
+
if (t.type === 'symbol' && t.value === '(') {
|
|
15436
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
15437
|
+
const t2 = tokens[j];
|
|
15438
|
+
if (isWs(t2))
|
|
15439
|
+
continue;
|
|
15440
|
+
if (isImportWord(t2))
|
|
15441
|
+
return true;
|
|
15442
|
+
break;
|
|
15443
|
+
}
|
|
15444
|
+
}
|
|
15445
|
+
break;
|
|
15446
|
+
}
|
|
15447
|
+
return false;
|
|
15448
|
+
}
|
|
15261
15449
|
/**
|
|
15262
15450
|
* Scans a raw token value for hex color literals and returns HTML with each
|
|
15263
15451
|
* color wrapped in a swatch span. Non-color text is HTML-escaped.
|
|
@@ -15300,6 +15488,15 @@ class ScrollBar {
|
|
|
15300
15488
|
this.thumb = LX.makeElement('div');
|
|
15301
15489
|
this.thumb.addEventListener('mousedown', (e) => this._onMouseDown(e));
|
|
15302
15490
|
this.root.appendChild(this.thumb);
|
|
15491
|
+
this.root.addEventListener('mousedown', (e) => {
|
|
15492
|
+
if (e.target === this.thumb)
|
|
15493
|
+
return;
|
|
15494
|
+
const clickPos = this._vertical ? e.offsetY : e.offsetX;
|
|
15495
|
+
const thumbSize = this._vertical ? this.thumb.offsetHeight : this.thumb.offsetWidth;
|
|
15496
|
+
const delta = (clickPos - thumbSize / 2) - this._thumbPos;
|
|
15497
|
+
this._onDrag?.(delta);
|
|
15498
|
+
this._onMouseDown(e); // continue as drag from new position
|
|
15499
|
+
});
|
|
15303
15500
|
}
|
|
15304
15501
|
setThumbRatio(ratio) {
|
|
15305
15502
|
this._thumbRatio = LX.clamp(ratio, 0, 1);
|
|
@@ -15423,6 +15620,7 @@ class CodeEditor {
|
|
|
15423
15620
|
onReady;
|
|
15424
15621
|
onCreateFile;
|
|
15425
15622
|
onCodeChange;
|
|
15623
|
+
onOpenPath;
|
|
15426
15624
|
onHoverSymbol;
|
|
15427
15625
|
_inputArea;
|
|
15428
15626
|
// State:
|
|
@@ -15503,6 +15701,7 @@ class CodeEditor {
|
|
|
15503
15701
|
this.onSelectTab = options.onSelectTab;
|
|
15504
15702
|
this.onReady = options.onReady;
|
|
15505
15703
|
this.onCodeChange = options.onCodeChange;
|
|
15704
|
+
this.onOpenPath = options.onOpenPath;
|
|
15506
15705
|
this.onHoverSymbol = options.onHoverSymbol;
|
|
15507
15706
|
this.language = Tokenizer.getLanguage(this.highlight) ?? Tokenizer.getLanguage('Plain Text');
|
|
15508
15707
|
this.symbolTable = new SymbolTable();
|
|
@@ -15713,19 +15912,31 @@ class CodeEditor {
|
|
|
15713
15912
|
this.codeArea.root.addEventListener('mousedown', this._onMouseDown.bind(this));
|
|
15714
15913
|
this.codeArea.root.addEventListener('contextmenu', this._onMouseDown.bind(this));
|
|
15715
15914
|
this.codeArea.root.addEventListener('mouseover', (e) => {
|
|
15716
|
-
const
|
|
15915
|
+
const target = e.target;
|
|
15916
|
+
const link = target.closest('.code-link');
|
|
15717
15917
|
if (link && e.ctrlKey)
|
|
15718
15918
|
link.classList.add('hovered');
|
|
15919
|
+
const path = target.closest('.code-path');
|
|
15920
|
+
if (path && e.ctrlKey)
|
|
15921
|
+
path.classList.add('hovered');
|
|
15719
15922
|
});
|
|
15720
15923
|
this.codeArea.root.addEventListener('mouseout', (e) => {
|
|
15721
|
-
const
|
|
15924
|
+
const target = e.target;
|
|
15925
|
+
const link = target.closest('.code-link');
|
|
15722
15926
|
if (link)
|
|
15723
15927
|
link.classList.remove('hovered');
|
|
15928
|
+
const path = target.closest('.code-path');
|
|
15929
|
+
if (path)
|
|
15930
|
+
path.classList.remove('hovered');
|
|
15724
15931
|
});
|
|
15725
15932
|
this.codeArea.root.addEventListener('mousemove', (e) => {
|
|
15726
|
-
const
|
|
15933
|
+
const target = e.target;
|
|
15934
|
+
const link = target.closest('.code-link');
|
|
15727
15935
|
if (link)
|
|
15728
15936
|
link.classList.toggle('hovered', e.ctrlKey);
|
|
15937
|
+
const path = target.closest('.code-path');
|
|
15938
|
+
if (path)
|
|
15939
|
+
path.classList.toggle('hovered', e.ctrlKey);
|
|
15729
15940
|
this._onCodeAreaMouseMove(e);
|
|
15730
15941
|
});
|
|
15731
15942
|
this.codeArea.root.addEventListener('mouseleave', () => {
|
|
@@ -15824,7 +16035,7 @@ class CodeEditor {
|
|
|
15824
16035
|
setText(text, language, detectLang = false) {
|
|
15825
16036
|
if (!this.currentTab)
|
|
15826
16037
|
return;
|
|
15827
|
-
this.doc.setText(this._normalizeText(text));
|
|
16038
|
+
this.doc.setText(this._normalizeText(text), true);
|
|
15828
16039
|
this.cursorSet.set(0, 0);
|
|
15829
16040
|
this.undoManager.clear();
|
|
15830
16041
|
this._lineStates = [];
|
|
@@ -16015,10 +16226,14 @@ class CodeEditor {
|
|
|
16015
16226
|
const codeTab = {
|
|
16016
16227
|
name,
|
|
16017
16228
|
dom,
|
|
16018
|
-
doc: new CodeDocument(
|
|
16229
|
+
doc: new CodeDocument((doc) => {
|
|
16230
|
+
this._setTabModified(name, true);
|
|
16231
|
+
this.onCodeChange?.(doc);
|
|
16232
|
+
}),
|
|
16019
16233
|
cursorSet: new CursorSet(),
|
|
16020
16234
|
undoManager: new UndoManager(),
|
|
16021
16235
|
language: langName,
|
|
16236
|
+
modified: false,
|
|
16022
16237
|
title: options.title ?? name
|
|
16023
16238
|
};
|
|
16024
16239
|
this._openedTabs[name] = codeTab;
|
|
@@ -16042,7 +16257,7 @@ class CodeEditor {
|
|
|
16042
16257
|
// Move into the sizer..
|
|
16043
16258
|
this.codeSizer.appendChild(dom);
|
|
16044
16259
|
if (options.text) {
|
|
16045
|
-
codeTab.doc.setText(options.text);
|
|
16260
|
+
codeTab.doc.setText(options.text, true);
|
|
16046
16261
|
codeTab.cursorSet.set(0, 0);
|
|
16047
16262
|
codeTab.undoManager.clear();
|
|
16048
16263
|
this._renderAllLines();
|
|
@@ -16133,7 +16348,7 @@ class CodeEditor {
|
|
|
16133
16348
|
title: options.title ?? name,
|
|
16134
16349
|
language: langName
|
|
16135
16350
|
});
|
|
16136
|
-
this.doc.setText(text);
|
|
16351
|
+
this.doc.setText(text, true);
|
|
16137
16352
|
this.setLanguage(langName, ext);
|
|
16138
16353
|
this.cursorSet.set(0, 0);
|
|
16139
16354
|
this.undoManager.clear();
|
|
@@ -16194,7 +16409,7 @@ class CodeEditor {
|
|
|
16194
16409
|
language: langName
|
|
16195
16410
|
});
|
|
16196
16411
|
if (results.length === 0) {
|
|
16197
|
-
this.doc.setText(processedText);
|
|
16412
|
+
this.doc.setText(processedText, true);
|
|
16198
16413
|
this.setLanguage(langName, ext);
|
|
16199
16414
|
this.cursorSet.set(0, 0);
|
|
16200
16415
|
this.undoManager.clear();
|
|
@@ -16236,6 +16451,30 @@ class CodeEditor {
|
|
|
16236
16451
|
}
|
|
16237
16452
|
}, 20);
|
|
16238
16453
|
}
|
|
16454
|
+
_findTabByPath(importPath) {
|
|
16455
|
+
// By now only uses base name
|
|
16456
|
+
const importBase = importPath.split('/').pop().replace(/\.\w+$/, '').toLowerCase();
|
|
16457
|
+
const allNames = new Set([
|
|
16458
|
+
...Object.keys(this._openedTabs),
|
|
16459
|
+
...Object.keys(this._loadedTabs),
|
|
16460
|
+
...Object.keys(this._storedTabs),
|
|
16461
|
+
]);
|
|
16462
|
+
for (const name of allNames) {
|
|
16463
|
+
const tabBase = name.split('/').pop().replace(/\.\w+$/, '').toLowerCase();
|
|
16464
|
+
if (tabBase === importBase)
|
|
16465
|
+
return name;
|
|
16466
|
+
}
|
|
16467
|
+
return null;
|
|
16468
|
+
}
|
|
16469
|
+
_setTabModified(name, modified) {
|
|
16470
|
+
const tab = this._openedTabs[name];
|
|
16471
|
+
if (!tab || tab.modified === modified)
|
|
16472
|
+
return;
|
|
16473
|
+
tab.modified = modified;
|
|
16474
|
+
const tabEl = this.tabs?.tabDOMs?.[name];
|
|
16475
|
+
if (tabEl)
|
|
16476
|
+
tabEl.toggleAttribute('data-modified', modified);
|
|
16477
|
+
}
|
|
16239
16478
|
_onSelectTab(isNewTabButton, event, name) {
|
|
16240
16479
|
if (this.disableEdition) {
|
|
16241
16480
|
return;
|
|
@@ -16461,7 +16700,6 @@ class CodeEditor {
|
|
|
16461
16700
|
const lineText = this.doc.getLine(lineIndex);
|
|
16462
16701
|
const result = Tokenizer.tokenizeLine(lineText, this.language, prevState);
|
|
16463
16702
|
const langClass = this.language.name.toLowerCase().replace(/[^a-z]/g, '');
|
|
16464
|
-
const URL_REGEX = /(https?:\/\/[^\s"'<>)\]]+)/g;
|
|
16465
16703
|
// Pre-compute which token index gets the bracket-highlight class
|
|
16466
16704
|
let bracketTokenIdx = -1;
|
|
16467
16705
|
if (lineIndex === this._bracketOpenLine) {
|
|
@@ -16489,15 +16727,20 @@ class CodeEditor {
|
|
|
16489
16727
|
const cls = TOKEN_CLASS_MAP[token.type];
|
|
16490
16728
|
const tokenCol = colOffset;
|
|
16491
16729
|
colOffset += token.value.length;
|
|
16730
|
+
// Inject content depending on type of token: color, url, path?
|
|
16492
16731
|
let content;
|
|
16493
16732
|
if (token.type === 'comment') {
|
|
16494
|
-
// Escape then inject clickable URL spans
|
|
16495
16733
|
const escaped = token.value
|
|
16496
16734
|
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
16497
16735
|
content = escaped.replace(URL_REGEX, `<span class="code-link" data-url="$1">$1</span>`);
|
|
16498
16736
|
}
|
|
16737
|
+
else if (token.type === 'string' && isImportPath(result.tokens, ti)) {
|
|
16738
|
+
const inner = token.value.slice(1, -1); // strip surrounding quotes
|
|
16739
|
+
const q = token.value[0];
|
|
16740
|
+
const escapedInner = inner.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
16741
|
+
content = `${q}<span class="code-path" data-path="${inner}">${escapedInner}</span>${q}`;
|
|
16742
|
+
}
|
|
16499
16743
|
else {
|
|
16500
|
-
// Escape and inject color swatches for hex color strings
|
|
16501
16744
|
content = injectColorSpans(token.value, lineIndex, tokenCol);
|
|
16502
16745
|
}
|
|
16503
16746
|
const bracketClass = ti === bracketTokenIdx ? ' code-bracket-active' : '';
|
|
@@ -16767,6 +17010,8 @@ class CodeEditor {
|
|
|
16767
17010
|
e.preventDefault();
|
|
16768
17011
|
if (this.onSave) {
|
|
16769
17012
|
this.onSave(this.getText(), this);
|
|
17013
|
+
this.undoManager.markSaved();
|
|
17014
|
+
this._setTabModified(this.currentTab.name, false);
|
|
16770
17015
|
}
|
|
16771
17016
|
return;
|
|
16772
17017
|
case 'z':
|
|
@@ -16789,6 +17034,17 @@ class CodeEditor {
|
|
|
16789
17034
|
e.preventDefault();
|
|
16790
17035
|
this._doPaste();
|
|
16791
17036
|
return;
|
|
17037
|
+
case 'home':
|
|
17038
|
+
e.preventDefault();
|
|
17039
|
+
this.cursorSet.set(0, 0);
|
|
17040
|
+
this._afterCursorMove();
|
|
17041
|
+
return;
|
|
17042
|
+
case 'end':
|
|
17043
|
+
e.preventDefault();
|
|
17044
|
+
const lastLine = this.doc.lineCount - 1;
|
|
17045
|
+
this.cursorSet.set(lastLine, this.doc.getLine(lastLine).length);
|
|
17046
|
+
this._afterCursorMove();
|
|
17047
|
+
return;
|
|
16792
17048
|
case ' ':
|
|
16793
17049
|
e.preventDefault();
|
|
16794
17050
|
// Also call user callback if provided
|
|
@@ -17561,6 +17817,8 @@ class CodeEditor {
|
|
|
17561
17817
|
}
|
|
17562
17818
|
this._rebuildLines();
|
|
17563
17819
|
this._afterCursorMove();
|
|
17820
|
+
if (this.currentTab)
|
|
17821
|
+
this._setTabModified(this.currentTab.name, this.undoManager.isModified());
|
|
17564
17822
|
}
|
|
17565
17823
|
}
|
|
17566
17824
|
_doRedo() {
|
|
@@ -17572,6 +17830,8 @@ class CodeEditor {
|
|
|
17572
17830
|
}
|
|
17573
17831
|
this._rebuildLines();
|
|
17574
17832
|
this._afterCursorMove();
|
|
17833
|
+
if (this.currentTab)
|
|
17834
|
+
this._setTabModified(this.currentTab.name, this.undoManager.isModified());
|
|
17575
17835
|
}
|
|
17576
17836
|
}
|
|
17577
17837
|
// Mouse input events:
|
|
@@ -17582,7 +17842,7 @@ class CodeEditor {
|
|
|
17582
17842
|
return;
|
|
17583
17843
|
if (this.autocomplete && this.autocomplete.contains(e.target))
|
|
17584
17844
|
return;
|
|
17585
|
-
// Ctrl+click: open link
|
|
17845
|
+
// Ctrl+click: open link or import path
|
|
17586
17846
|
if (e.ctrlKey && e.button === 0) {
|
|
17587
17847
|
const target = e.target;
|
|
17588
17848
|
const link = target.closest('.code-link');
|
|
@@ -17590,6 +17850,15 @@ class CodeEditor {
|
|
|
17590
17850
|
window.open(link.dataset.url, '_blank');
|
|
17591
17851
|
return;
|
|
17592
17852
|
}
|
|
17853
|
+
const pathEl = target.closest('.code-path');
|
|
17854
|
+
if (pathEl?.dataset.path) {
|
|
17855
|
+
const rawPath = pathEl.dataset.path;
|
|
17856
|
+
const tabName = this._findTabByPath(rawPath);
|
|
17857
|
+
if (tabName)
|
|
17858
|
+
this.loadTab(tabName);
|
|
17859
|
+
this.onOpenPath?.(rawPath, this);
|
|
17860
|
+
return;
|
|
17861
|
+
}
|
|
17593
17862
|
}
|
|
17594
17863
|
e.preventDefault(); // Prevent browser from stealing focus from _inputArea
|
|
17595
17864
|
this._wasPaired = false;
|
|
@@ -17637,18 +17906,55 @@ class CodeEditor {
|
|
|
17637
17906
|
}
|
|
17638
17907
|
this._afterCursorMove();
|
|
17639
17908
|
this._inputArea.focus();
|
|
17640
|
-
// Track mouse for drag selection
|
|
17641
|
-
|
|
17642
|
-
|
|
17643
|
-
|
|
17644
|
-
|
|
17645
|
-
const
|
|
17909
|
+
// Track mouse for drag selection (with auto-scroll when outside editor window/area)
|
|
17910
|
+
let lastMouseX = 0;
|
|
17911
|
+
let lastMouseY = 0;
|
|
17912
|
+
let rafId = null;
|
|
17913
|
+
const updateSelection = () => {
|
|
17914
|
+
const currentRect = this.codeContainer.getBoundingClientRect();
|
|
17915
|
+
const mx = lastMouseX - currentRect.left - this.xPadding;
|
|
17916
|
+
const my = lastMouseY - currentRect.top;
|
|
17917
|
+
const ml = LX.clamp(Math.floor(my / this.lineHeight), 0, this.doc.lineCount - 1);
|
|
17918
|
+
const mc = LX.clamp(Math.round(mx / this.charWidth), 0, this.doc.getLine(ml).length);
|
|
17646
17919
|
const sel = this.cursorSet.getPrimary();
|
|
17647
17920
|
sel.head = { line: ml, col: mc };
|
|
17648
17921
|
this._renderCursors();
|
|
17649
17922
|
this._renderSelections();
|
|
17650
17923
|
};
|
|
17924
|
+
const autoScroll = () => {
|
|
17925
|
+
const scrollerRect = this.codeScroller.getBoundingClientRect();
|
|
17926
|
+
const overshootY = lastMouseY < scrollerRect.top ? lastMouseY - scrollerRect.top
|
|
17927
|
+
: lastMouseY > scrollerRect.bottom ? lastMouseY - scrollerRect.bottom : 0;
|
|
17928
|
+
const overshootX = lastMouseX < scrollerRect.left ? lastMouseX - scrollerRect.left
|
|
17929
|
+
: lastMouseX > scrollerRect.right ? lastMouseX - scrollerRect.right : 0;
|
|
17930
|
+
if (overshootY === 0 && overshootX === 0) {
|
|
17931
|
+
rafId = null;
|
|
17932
|
+
return;
|
|
17933
|
+
}
|
|
17934
|
+
const speedY = Math.sign(overshootY) * Math.min(Math.abs(overshootY) * 0.3, 15);
|
|
17935
|
+
const speedX = Math.sign(overshootX) * Math.min(Math.abs(overshootX) * 0.3, 15);
|
|
17936
|
+
this.codeScroller.scrollTop += speedY;
|
|
17937
|
+
this.codeScroller.scrollLeft += speedX;
|
|
17938
|
+
this._syncScrollBars();
|
|
17939
|
+
updateSelection();
|
|
17940
|
+
rafId = requestAnimationFrame(autoScroll);
|
|
17941
|
+
};
|
|
17942
|
+
const onMouseMove = (me) => {
|
|
17943
|
+
lastMouseX = me.clientX;
|
|
17944
|
+
lastMouseY = me.clientY;
|
|
17945
|
+
updateSelection();
|
|
17946
|
+
const scrollerRect = this.codeScroller.getBoundingClientRect();
|
|
17947
|
+
const isOutside = me.clientY < scrollerRect.top || me.clientY > scrollerRect.bottom
|
|
17948
|
+
|| me.clientX < scrollerRect.left || me.clientX > scrollerRect.right;
|
|
17949
|
+
if (isOutside && rafId === null) {
|
|
17950
|
+
rafId = requestAnimationFrame(autoScroll);
|
|
17951
|
+
}
|
|
17952
|
+
};
|
|
17651
17953
|
const onMouseUp = () => {
|
|
17954
|
+
if (rafId !== null) {
|
|
17955
|
+
cancelAnimationFrame(rafId);
|
|
17956
|
+
rafId = null;
|
|
17957
|
+
}
|
|
17652
17958
|
document.removeEventListener('mousemove', onMouseMove);
|
|
17653
17959
|
document.removeEventListener('mouseup', onMouseUp);
|
|
17654
17960
|
};
|
|
@@ -17709,7 +18015,10 @@ class CodeEditor {
|
|
|
17709
18015
|
const suggestions = [];
|
|
17710
18016
|
const added = new Set();
|
|
17711
18017
|
const addSuggestion = (s) => {
|
|
17712
|
-
if (
|
|
18018
|
+
if (added.has(s.label)) {
|
|
18019
|
+
suggestions[suggestions.findIndex(x => x.label === s.label)] = s;
|
|
18020
|
+
}
|
|
18021
|
+
else {
|
|
17713
18022
|
suggestions.push(s);
|
|
17714
18023
|
added.add(s.label);
|
|
17715
18024
|
}
|
|
@@ -17722,9 +18031,12 @@ class CodeEditor {
|
|
|
17722
18031
|
return suggestion.label.toLowerCase().startsWith(w);
|
|
17723
18032
|
};
|
|
17724
18033
|
// Get first suggestions from symbol table
|
|
18034
|
+
const _skipKinds = new Set(['constructor-call', 'method-call']);
|
|
17725
18035
|
const allSymbols = this.symbolTable.getAllSymbols();
|
|
17726
18036
|
for (const symbol of allSymbols) {
|
|
17727
|
-
|
|
18037
|
+
if (_skipKinds.has(symbol.kind))
|
|
18038
|
+
continue;
|
|
18039
|
+
const s = { label: symbol.name, kind: symbol.kind, scope: symbol.scope };
|
|
17728
18040
|
if (filterSuggestion(s, word))
|
|
17729
18041
|
addSuggestion(s);
|
|
17730
18042
|
}
|
|
@@ -17758,7 +18070,7 @@ class CodeEditor {
|
|
|
17758
18070
|
// Render suggestions
|
|
17759
18071
|
suggestions.forEach((suggestion, index) => {
|
|
17760
18072
|
const item = document.createElement('pre');
|
|
17761
|
-
item.
|
|
18073
|
+
item.suggestionData = suggestion;
|
|
17762
18074
|
if (index === this._selectedAutocompleteIndex)
|
|
17763
18075
|
item.classList.add('selected');
|
|
17764
18076
|
const currSuggestionLabel = suggestion.label;
|
|
@@ -17787,11 +18099,15 @@ class CodeEditor {
|
|
|
17787
18099
|
break;
|
|
17788
18100
|
case 'type':
|
|
17789
18101
|
iconName = 'Type';
|
|
17790
|
-
iconClass = 'text-
|
|
18102
|
+
iconClass = 'text-purple-500';
|
|
17791
18103
|
break;
|
|
17792
18104
|
case 'function':
|
|
17793
18105
|
iconName = 'Function';
|
|
17794
|
-
iconClass = 'text-
|
|
18106
|
+
iconClass = 'text-teal-500';
|
|
18107
|
+
break;
|
|
18108
|
+
case 'constant':
|
|
18109
|
+
iconName = 'Pi';
|
|
18110
|
+
iconClass = 'text-rose-600';
|
|
17795
18111
|
break;
|
|
17796
18112
|
case 'method':
|
|
17797
18113
|
iconName = 'Box';
|
|
@@ -17805,14 +18121,6 @@ class CodeEditor {
|
|
|
17805
18121
|
iconName = 'Layers';
|
|
17806
18122
|
iconClass = 'text-blue-300';
|
|
17807
18123
|
break;
|
|
17808
|
-
case 'constructor-call':
|
|
17809
|
-
iconName = 'Hammer';
|
|
17810
|
-
iconClass = 'text-green-500';
|
|
17811
|
-
break;
|
|
17812
|
-
case 'method-call':
|
|
17813
|
-
iconName = 'Parentheses';
|
|
17814
|
-
iconClass = 'text-gray-400';
|
|
17815
|
-
break;
|
|
17816
18124
|
}
|
|
17817
18125
|
item.appendChild(LX.makeIcon(iconName, { iconClass: 'ml-1 mr-2', svgClass: 'sm ' + iconClass }));
|
|
17818
18126
|
// Highlight the written part
|
|
@@ -17827,7 +18135,13 @@ class CodeEditor {
|
|
|
17827
18135
|
var postWord = document.createElement('span');
|
|
17828
18136
|
postWord.textContent = currSuggestionLabel.substring(hIndex + word.length);
|
|
17829
18137
|
item.appendChild(postWord);
|
|
17830
|
-
if (suggestion.
|
|
18138
|
+
if (suggestion.detail) {
|
|
18139
|
+
const detail = document.createElement('span');
|
|
18140
|
+
detail.textContent = ` ${suggestion.detail}`;
|
|
18141
|
+
detail.className = 'kind text-muted-foreground text-xs! ml-2';
|
|
18142
|
+
item.appendChild(detail);
|
|
18143
|
+
}
|
|
18144
|
+
else if (suggestion.kind) {
|
|
17831
18145
|
const kind = document.createElement('span');
|
|
17832
18146
|
kind.textContent = ` (${suggestion.kind})`;
|
|
17833
18147
|
kind.className = 'kind text-muted-foreground text-xs! ml-2';
|
|
@@ -17873,9 +18187,12 @@ class CodeEditor {
|
|
|
17873
18187
|
* Insert the selected autocomplete word at cursor.
|
|
17874
18188
|
*/
|
|
17875
18189
|
_doAutocompleteWord() {
|
|
17876
|
-
const
|
|
17877
|
-
if (!
|
|
18190
|
+
const suggestion = this._getSelectedAutoCompleteSuggestion();
|
|
18191
|
+
if (!suggestion)
|
|
17878
18192
|
return;
|
|
18193
|
+
const text = suggestion.insertText ?? suggestion.label;
|
|
18194
|
+
const cursorOffset = suggestion.cursorOffset; // only valid in single line autocomplete
|
|
18195
|
+
const selectLength = suggestion.selectLength;
|
|
17879
18196
|
const cursor = this.cursorSet.getPrimary().head;
|
|
17880
18197
|
const { start, end } = this._getWordAtCursor();
|
|
17881
18198
|
const line = cursor.line;
|
|
@@ -17887,7 +18204,13 @@ class CodeEditor {
|
|
|
17887
18204
|
const insertOp = this.doc.insert(line, start, text);
|
|
17888
18205
|
const insertedLines = text.split(/\r?\n/);
|
|
17889
18206
|
if (insertedLines.length === 1) {
|
|
17890
|
-
|
|
18207
|
+
const cursorCol = start + (cursorOffset ?? text.length);
|
|
18208
|
+
this.cursorSet.set(line, cursorCol);
|
|
18209
|
+
if (selectLength) {
|
|
18210
|
+
const sel = this.cursorSet.getPrimary();
|
|
18211
|
+
sel.anchor = { line, col: cursorCol };
|
|
18212
|
+
sel.head = { line, col: cursorCol + selectLength };
|
|
18213
|
+
}
|
|
17891
18214
|
}
|
|
17892
18215
|
else {
|
|
17893
18216
|
this.cursorSet.set(line + insertedLines.length - 1, insertedLines[insertedLines.length - 1].length);
|
|
@@ -17898,11 +18221,11 @@ class CodeEditor {
|
|
|
17898
18221
|
this._afterCursorMove();
|
|
17899
18222
|
this._doHideAutocomplete();
|
|
17900
18223
|
}
|
|
17901
|
-
|
|
18224
|
+
_getSelectedAutoCompleteSuggestion() {
|
|
17902
18225
|
if (!this.autocomplete || !this._isAutoCompleteActive)
|
|
17903
18226
|
return null;
|
|
17904
18227
|
const pre = this.autocomplete.childNodes[this._selectedAutocompleteIndex];
|
|
17905
|
-
return pre.
|
|
18228
|
+
return pre.suggestionData;
|
|
17906
18229
|
}
|
|
17907
18230
|
_afterCursorMove() {
|
|
17908
18231
|
this._renderCursors();
|
|
@@ -18143,6 +18466,8 @@ class CodeEditor {
|
|
|
18143
18466
|
this._hoverWord = '';
|
|
18144
18467
|
}
|
|
18145
18468
|
_onCodeAreaMouseMove(e) {
|
|
18469
|
+
if (!this.currentTab)
|
|
18470
|
+
return;
|
|
18146
18471
|
// Only show hover when no button is pressed (no dragging)
|
|
18147
18472
|
if (e.buttons !== 0) {
|
|
18148
18473
|
this._clearHoverPopup();
|
|
@@ -19321,7 +19646,7 @@ class AssetView {
|
|
|
19321
19646
|
}
|
|
19322
19647
|
}
|
|
19323
19648
|
else if (isFolder) {
|
|
19324
|
-
that.
|
|
19649
|
+
that._requestEnterFolder(item);
|
|
19325
19650
|
return;
|
|
19326
19651
|
}
|
|
19327
19652
|
const onSelect = that._callbacks['select'];
|
|
@@ -19539,17 +19864,17 @@ class AssetView {
|
|
|
19539
19864
|
if (!this.prevData.length || !this.currentFolder)
|
|
19540
19865
|
return;
|
|
19541
19866
|
this.nextData.push(this.currentFolder);
|
|
19542
|
-
this._enterFolder(this.prevData.pop(), false);
|
|
19867
|
+
this._enterFolder(this.prevData.pop(), false, true);
|
|
19543
19868
|
}, { buttonClass: 'ghost', title: 'Go Back', tooltip: true, icon: 'ArrowLeft' });
|
|
19544
19869
|
panel.addButton(null, 'GoForwardButton', () => {
|
|
19545
19870
|
if (!this.nextData.length || !this.currentFolder)
|
|
19546
19871
|
return;
|
|
19547
|
-
this._enterFolder(this.nextData.pop());
|
|
19872
|
+
this._enterFolder(this.nextData.pop(), false, true);
|
|
19548
19873
|
}, { buttonClass: 'ghost', title: 'Go Forward', tooltip: true, icon: 'ArrowRight' });
|
|
19549
19874
|
panel.addButton(null, 'GoUpButton', () => {
|
|
19550
19875
|
const parentFolder = this.currentFolder?.parent;
|
|
19551
19876
|
if (parentFolder)
|
|
19552
|
-
this._enterFolder(parentFolder);
|
|
19877
|
+
this._enterFolder(parentFolder, false, true);
|
|
19553
19878
|
}, { buttonClass: 'ghost', title: 'Go Upper Folder', tooltip: true, icon: 'ArrowUp' });
|
|
19554
19879
|
panel.addButton(null, 'RefreshButton', () => {
|
|
19555
19880
|
this._refreshContent(undefined, undefined, true);
|
|
@@ -19589,7 +19914,7 @@ class AssetView {
|
|
|
19589
19914
|
this._updatePath();
|
|
19590
19915
|
}
|
|
19591
19916
|
else {
|
|
19592
|
-
this.
|
|
19917
|
+
this._requestEnterFolder(node.type === 'folder' ? node : node.parent);
|
|
19593
19918
|
this._previewAsset(node);
|
|
19594
19919
|
if (node.type !== 'folder') {
|
|
19595
19920
|
this.content.querySelectorAll('.lexassetitem').forEach((i) => i.classList.remove('selected'));
|
|
@@ -19961,12 +20286,41 @@ class AssetView {
|
|
|
19961
20286
|
this.toolsPanel.refresh();
|
|
19962
20287
|
this._refreshContent();
|
|
19963
20288
|
}
|
|
19964
|
-
|
|
20289
|
+
_requestEnterFolder(folderItem, storeCurrent = true) {
|
|
20290
|
+
if (!folderItem) {
|
|
20291
|
+
return;
|
|
20292
|
+
}
|
|
20293
|
+
const onBeforeEnterFolder = this._callbacks['beforeEnterFolder'];
|
|
20294
|
+
const onEnterFolder = this._callbacks['enterFolder'];
|
|
20295
|
+
const resolve = (...args) => {
|
|
20296
|
+
const child = this.currentData[0];
|
|
20297
|
+
const sameFolder = child?.parent?.metadata?.uid === folderItem.metadata?.uid;
|
|
20298
|
+
const mustRefresh = args[0] || !sameFolder;
|
|
20299
|
+
this._enterFolder(folderItem, storeCurrent, mustRefresh);
|
|
20300
|
+
const event = {
|
|
20301
|
+
type: 'enter-folder',
|
|
20302
|
+
to: folderItem,
|
|
20303
|
+
userInitiated: true
|
|
20304
|
+
};
|
|
20305
|
+
if (onEnterFolder)
|
|
20306
|
+
onEnterFolder(event, ...args);
|
|
20307
|
+
};
|
|
20308
|
+
if (onBeforeEnterFolder) {
|
|
20309
|
+
const event = {
|
|
20310
|
+
type: 'enter-folder',
|
|
20311
|
+
to: folderItem,
|
|
20312
|
+
userInitiated: true
|
|
20313
|
+
};
|
|
20314
|
+
onBeforeEnterFolder(event, resolve);
|
|
20315
|
+
}
|
|
20316
|
+
else {
|
|
20317
|
+
resolve();
|
|
20318
|
+
}
|
|
20319
|
+
}
|
|
20320
|
+
_enterFolder(folderItem, storeCurrent, mustRefresh) {
|
|
19965
20321
|
if (!folderItem) {
|
|
19966
20322
|
return;
|
|
19967
20323
|
}
|
|
19968
|
-
const child = this.currentData[0];
|
|
19969
|
-
const sameFolder = child?.parent?.metadata?.uid === folderItem.metadata?.uid;
|
|
19970
20324
|
if (storeCurrent) {
|
|
19971
20325
|
this.prevData.push(this.currentFolder ?? {
|
|
19972
20326
|
id: '/',
|
|
@@ -19975,17 +20329,6 @@ class AssetView {
|
|
|
19975
20329
|
metadata: {}
|
|
19976
20330
|
});
|
|
19977
20331
|
}
|
|
19978
|
-
let mustRefresh = !sameFolder;
|
|
19979
|
-
const onEnterFolder = this._callbacks['enterFolder'];
|
|
19980
|
-
if (onEnterFolder !== undefined) {
|
|
19981
|
-
const event = {
|
|
19982
|
-
type: 'enter_folder',
|
|
19983
|
-
to: folderItem,
|
|
19984
|
-
userInitiated: true
|
|
19985
|
-
};
|
|
19986
|
-
const r = await onEnterFolder(event);
|
|
19987
|
-
mustRefresh = mustRefresh || r;
|
|
19988
|
-
}
|
|
19989
20332
|
// Update this after the event since the user might have added or modified the data
|
|
19990
20333
|
this.currentFolder = folderItem;
|
|
19991
20334
|
this.currentData = this.currentFolder?.children ?? [];
|