lexgui 0.1.45 → 0.2.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/build/components/codeeditor.js +129 -99
- package/build/lexgui.css +402 -144
- package/build/lexgui.js +8278 -7629
- package/build/lexgui.min.css +1 -0
- package/build/lexgui.min.js +1 -0
- package/build/lexgui.module.js +903 -274
- package/build/lexgui.module.min.js +1 -0
- package/changelog.md +29 -2
- package/demo.js +89 -839
- package/package.json +1 -1
package/build/lexgui.module.js
CHANGED
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
var LX = {
|
|
11
|
-
version: "0.
|
|
11
|
+
version: "0.2.0",
|
|
12
12
|
ready: false,
|
|
13
13
|
components: [], // specific pre-build components
|
|
14
|
-
signals: {} // events and triggers
|
|
14
|
+
signals: {}, // events and triggers
|
|
15
|
+
extraCommandbarEntries: [] // user specific entries for command bar
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
LX.MOUSE_LEFT_CLICK = 0;
|
|
@@ -32,34 +33,103 @@ LX.clamp = clamp;
|
|
|
32
33
|
LX.round = round;
|
|
33
34
|
LX.remapRange = remapRange;
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
// Timer that works everywhere (from litegraph.js)
|
|
37
|
+
if ( typeof performance != "undefined" )
|
|
38
|
+
{
|
|
39
|
+
LX.getTime = performance.now.bind( performance );
|
|
40
|
+
}
|
|
41
|
+
else if( typeof Date != "undefined" && Date.now )
|
|
42
|
+
{
|
|
43
|
+
LX.getTime = Date.now.bind( Date );
|
|
44
|
+
}
|
|
45
|
+
else if ( typeof process != "undefined" )
|
|
46
|
+
{
|
|
47
|
+
LX.getTime = function() {
|
|
48
|
+
var t = process.hrtime();
|
|
49
|
+
return t[ 0 ] * 0.001 + t[ 1 ] * 1e-6;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
else
|
|
53
|
+
{
|
|
54
|
+
LX.getTime = function() {
|
|
55
|
+
return new Date().getTime();
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let ASYNC_ENABLED = true;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @method doAsync
|
|
63
|
+
* @description Call a function asynchronously
|
|
64
|
+
* @param {Function} fn Function to call
|
|
65
|
+
* @param {Number} ms Time to wait until calling the function (in milliseconds)
|
|
66
|
+
*/
|
|
67
|
+
function doAsync( fn, ms ) {
|
|
68
|
+
if( ASYNC_ENABLED )
|
|
69
|
+
{
|
|
70
|
+
setTimeout( fn, ms ?? 0 );
|
|
71
|
+
}
|
|
72
|
+
else
|
|
73
|
+
{
|
|
74
|
+
fn();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
LX.doAsync = doAsync;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @method getSupportedDOMName
|
|
82
|
+
* @description Convert a text string to a valid DOM name
|
|
83
|
+
* @param {String} text Original text
|
|
84
|
+
*/
|
|
85
|
+
function getSupportedDOMName( text )
|
|
36
86
|
{
|
|
37
|
-
return
|
|
87
|
+
return text.replace(/\s/g, '').replaceAll('@', '_').replaceAll('+', '_plus_').replaceAll('.', '');
|
|
38
88
|
}
|
|
39
89
|
|
|
40
90
|
LX.getSupportedDOMName = getSupportedDOMName;
|
|
41
91
|
|
|
42
|
-
|
|
92
|
+
/**
|
|
93
|
+
* @method has
|
|
94
|
+
* @description Ask if LexGUI is using a specific component
|
|
95
|
+
* @param {String} componentName Name of the LexGUI component
|
|
96
|
+
*/
|
|
97
|
+
function has( componentName )
|
|
43
98
|
{
|
|
44
|
-
return (LX.components.indexOf(
|
|
99
|
+
return ( LX.components.indexOf( componentName ) > -1 );
|
|
45
100
|
}
|
|
46
101
|
|
|
47
102
|
LX.has = has;
|
|
48
103
|
|
|
49
|
-
|
|
104
|
+
/**
|
|
105
|
+
* @method getExtension
|
|
106
|
+
* @description Get a extension from a path/url/filename
|
|
107
|
+
* @param {String} name
|
|
108
|
+
*/
|
|
109
|
+
function getExtension( name )
|
|
50
110
|
{
|
|
51
|
-
return
|
|
111
|
+
return name.includes('.') ? name.split('.').pop() : null;
|
|
52
112
|
}
|
|
53
113
|
|
|
54
114
|
LX.getExtension = getExtension;
|
|
55
115
|
|
|
56
|
-
|
|
116
|
+
/**
|
|
117
|
+
* @method deepCopy
|
|
118
|
+
* @description Create a deep copy with no references from an object
|
|
119
|
+
* @param {Object} obj
|
|
120
|
+
*/
|
|
121
|
+
function deepCopy( obj )
|
|
57
122
|
{
|
|
58
|
-
return JSON.parse(JSON.stringify(
|
|
123
|
+
return JSON.parse( JSON.stringify( obj ) )
|
|
59
124
|
}
|
|
60
125
|
|
|
61
126
|
LX.deepCopy = deepCopy;
|
|
62
127
|
|
|
128
|
+
/**
|
|
129
|
+
* @method setTheme
|
|
130
|
+
* @description Set dark or light theme
|
|
131
|
+
* @param {String} colorScheme Name of the scheme
|
|
132
|
+
*/
|
|
63
133
|
function setTheme( colorScheme )
|
|
64
134
|
{
|
|
65
135
|
colorScheme = ( colorScheme == "light" ) ? "light" : "dark";
|
|
@@ -69,6 +139,12 @@ function setTheme( colorScheme )
|
|
|
69
139
|
|
|
70
140
|
LX.setTheme = setTheme;
|
|
71
141
|
|
|
142
|
+
/**
|
|
143
|
+
* @method setThemeColor
|
|
144
|
+
* @description Sets a new value for one of the main theme variables
|
|
145
|
+
* @param {String} colorName Name of the theme variable
|
|
146
|
+
* @param {String} color Color in rgba/hex
|
|
147
|
+
*/
|
|
72
148
|
function setThemeColor( colorName, color )
|
|
73
149
|
{
|
|
74
150
|
var r = document.querySelector( ':root' );
|
|
@@ -77,16 +153,22 @@ function setThemeColor( colorName, color )
|
|
|
77
153
|
|
|
78
154
|
LX.setThemeColor = setThemeColor;
|
|
79
155
|
|
|
156
|
+
/**
|
|
157
|
+
* @method getThemeColor
|
|
158
|
+
* @description Get the value for one of the main theme variables
|
|
159
|
+
* @param {String} colorName Name of the theme variable
|
|
160
|
+
*/
|
|
80
161
|
function getThemeColor( colorName )
|
|
81
162
|
{
|
|
82
163
|
const r = getComputedStyle( document.querySelector( ':root' ) );
|
|
83
164
|
const value = r.getPropertyValue( '--' + colorName );
|
|
165
|
+
const theme = document.documentElement.getAttribute( "data-theme" );
|
|
84
166
|
|
|
85
|
-
if( value.includes( "light-dark" )
|
|
167
|
+
if( value.includes( "light-dark" ) )
|
|
86
168
|
{
|
|
87
169
|
const currentScheme = r.getPropertyValue( "color-scheme" );
|
|
88
170
|
|
|
89
|
-
if(
|
|
171
|
+
if( currentScheme == "light" )
|
|
90
172
|
{
|
|
91
173
|
return value.substring( value.indexOf( '(' ) + 1, value.indexOf( ',' ) ).replace( /\s/g, '' );
|
|
92
174
|
}
|
|
@@ -101,7 +183,13 @@ function getThemeColor( colorName )
|
|
|
101
183
|
|
|
102
184
|
LX.getThemeColor = getThemeColor;
|
|
103
185
|
|
|
104
|
-
|
|
186
|
+
/**
|
|
187
|
+
* @method getBase64Image
|
|
188
|
+
* @description Convert an image to a base64 string
|
|
189
|
+
* @param {Image} img
|
|
190
|
+
*/
|
|
191
|
+
function getBase64Image( img )
|
|
192
|
+
{
|
|
105
193
|
var canvas = document.createElement( 'canvas' );
|
|
106
194
|
canvas.width = img.width;
|
|
107
195
|
canvas.height = img.height;
|
|
@@ -112,7 +200,13 @@ function getBase64Image( img ) {
|
|
|
112
200
|
|
|
113
201
|
LX.getBase64Image = getBase64Image;
|
|
114
202
|
|
|
115
|
-
|
|
203
|
+
/**
|
|
204
|
+
* @method hexToRgb
|
|
205
|
+
* @description Convert a hexadecimal string to a valid RGB color array
|
|
206
|
+
* @param {String} hexStr Hexadecimal color
|
|
207
|
+
*/
|
|
208
|
+
function hexToRgb( hexStr )
|
|
209
|
+
{
|
|
116
210
|
const red = parseInt( hexStr.substring( 1, 3 ), 16 ) / 255;
|
|
117
211
|
const green = parseInt( hexStr.substring( 3, 5 ), 16 ) / 255;
|
|
118
212
|
const blue = parseInt( hexStr.substring( 5, 7 ), 16 ) / 255;
|
|
@@ -121,7 +215,13 @@ function hexToRgb( hexStr ) {
|
|
|
121
215
|
|
|
122
216
|
LX.hexToRgb = hexToRgb;
|
|
123
217
|
|
|
124
|
-
|
|
218
|
+
/**
|
|
219
|
+
* @method rgbToHex
|
|
220
|
+
* @description Convert a RGB color array to a hexadecimal string
|
|
221
|
+
* @param {Array} rgb Array containing R, G, B, A*
|
|
222
|
+
*/
|
|
223
|
+
function rgbToHex( rgb )
|
|
224
|
+
{
|
|
125
225
|
let hex = "#";
|
|
126
226
|
for( let c of rgb ) {
|
|
127
227
|
c = Math.floor( c * 255 );
|
|
@@ -132,7 +232,14 @@ function rgbToHex( rgb ) {
|
|
|
132
232
|
|
|
133
233
|
LX.rgbToHex = rgbToHex;
|
|
134
234
|
|
|
135
|
-
|
|
235
|
+
/**
|
|
236
|
+
* @method measureRealWidth
|
|
237
|
+
* @description Measure the pixel width of a text
|
|
238
|
+
* @param {Object} value Text to measure
|
|
239
|
+
* @param {Number} paddingPlusMargin Padding offset
|
|
240
|
+
*/
|
|
241
|
+
function measureRealWidth( value, paddingPlusMargin = 8 )
|
|
242
|
+
{
|
|
136
243
|
var i = document.createElement( "span" );
|
|
137
244
|
i.className = "lexinputmeasure";
|
|
138
245
|
i.innerHTML = value;
|
|
@@ -144,7 +251,12 @@ function measureRealWidth( value, paddingPlusMargin = 8 ) {
|
|
|
144
251
|
|
|
145
252
|
LX.measureRealWidth = measureRealWidth;
|
|
146
253
|
|
|
147
|
-
|
|
254
|
+
/**
|
|
255
|
+
* @method simple_guidGenerator
|
|
256
|
+
* @description Get a random unique id
|
|
257
|
+
*/
|
|
258
|
+
function simple_guidGenerator()
|
|
259
|
+
{
|
|
148
260
|
var S4 = function() {
|
|
149
261
|
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
|
|
150
262
|
};
|
|
@@ -153,7 +265,21 @@ function simple_guidGenerator() {
|
|
|
153
265
|
|
|
154
266
|
LX.guidGenerator = simple_guidGenerator;
|
|
155
267
|
|
|
156
|
-
|
|
268
|
+
/**
|
|
269
|
+
* @method buildTextPattern
|
|
270
|
+
* @description Create a validation pattern using specific options
|
|
271
|
+
* @param {Object} options
|
|
272
|
+
* lowercase (Boolean): Text must contain a lowercase char
|
|
273
|
+
* uppercase (Boolean): Text must contain an uppercase char
|
|
274
|
+
* digit (Boolean): Text must contain a digit
|
|
275
|
+
* specialChar (Boolean): Text must contain a special char
|
|
276
|
+
* noSpaces (Boolean): Do not allow spaces in text
|
|
277
|
+
* minLength (Number): Text minimum length
|
|
278
|
+
* maxLength (Number): Text maximum length
|
|
279
|
+
* asRegExp (Boolean): Return pattern as Regular Expression instance
|
|
280
|
+
*/
|
|
281
|
+
function buildTextPattern( options = {} )
|
|
282
|
+
{
|
|
157
283
|
let patterns = [];
|
|
158
284
|
if ( options.lowercase ) patterns.push("(?=.*[a-z])");
|
|
159
285
|
if ( options.uppercase ) patterns.push("(?=.*[A-Z])");
|
|
@@ -170,67 +296,9 @@ function buildTextPattern( options = {} ) {
|
|
|
170
296
|
|
|
171
297
|
LX.buildTextPattern = buildTextPattern;
|
|
172
298
|
|
|
173
|
-
// Timer that works everywhere (from litegraph.js)
|
|
174
|
-
if (typeof performance != "undefined") {
|
|
175
|
-
LX.getTime = performance.now.bind(performance);
|
|
176
|
-
} else if (typeof Date != "undefined" && Date.now) {
|
|
177
|
-
LX.getTime = Date.now.bind(Date);
|
|
178
|
-
} else if (typeof process != "undefined") {
|
|
179
|
-
LX.getTime = function() {
|
|
180
|
-
var t = process.hrtime();
|
|
181
|
-
return t[0] * 0.001 + t[1] * 1e-6;
|
|
182
|
-
};
|
|
183
|
-
} else {
|
|
184
|
-
LX.getTime = function getTime() {
|
|
185
|
-
return new Date().getTime();
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
let ASYNC_ENABLED = true;
|
|
190
|
-
|
|
191
|
-
function doAsync( fn, ms ) {
|
|
192
|
-
if( ASYNC_ENABLED )
|
|
193
|
-
{
|
|
194
|
-
setTimeout( fn, ms ?? 0 );
|
|
195
|
-
}
|
|
196
|
-
else
|
|
197
|
-
{
|
|
198
|
-
fn();
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Math classes
|
|
203
|
-
|
|
204
|
-
class vec2 {
|
|
205
|
-
|
|
206
|
-
constructor( x, y ) {
|
|
207
|
-
this.x = x ?? 0;
|
|
208
|
-
this.y = y ?? ( x ?? 0 );
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
get xy() { return [ this.x, this.y ]; }
|
|
212
|
-
get yx() { return [ this.y, this.x ]; }
|
|
213
|
-
|
|
214
|
-
set ( x, y ) { this.x = x; this.y = y; }
|
|
215
|
-
add ( v, v0 = new vec2() ) { v0.set( this.x + v.x, this.y + v.y ); return v0; }
|
|
216
|
-
sub ( v, v0 = new vec2() ) { v0.set( this.x - v.x, this.y - v.y ); return v0; }
|
|
217
|
-
mul ( v, v0 = new vec2() ) { if( v.constructor == Number ) { v = new vec2( v ) } v0.set( this.x * v.x, this.y * v.y ); return v0; }
|
|
218
|
-
div ( v, v0 = new vec2() ) { if( v.constructor == Number ) { v = new vec2( v ) } v0.set( this.x / v.x, this.y / v.y ); return v0; }
|
|
219
|
-
abs ( v0 = new vec2() ) { v0.set( Math.abs( this.x ), Math.abs( this.y ) ); return v0; }
|
|
220
|
-
dot ( v ) { return this.x * v.x + this.y * v.y; }
|
|
221
|
-
len2 () { return this.dot( this ) }
|
|
222
|
-
len () { return Math.sqrt( this.len2() ); }
|
|
223
|
-
nrm ( v0 = new vec2() ) { v0.set( this.x, this.y ); return v0.mul( 1.0 / this.len(), v0 ); }
|
|
224
|
-
dst ( v ) { return v.sub( this ).len(); }
|
|
225
|
-
clp ( min, max, v0 = new vec2() ) { v0.set( clamp( this.x, min, max ), clamp( this.y, min, max ) ); return v0; }
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
LX.vec2 = vec2;
|
|
229
|
-
|
|
230
|
-
// Other utils
|
|
231
|
-
|
|
232
299
|
/**
|
|
233
300
|
* @method makeDraggable
|
|
301
|
+
* @description Allow an element to be dragged
|
|
234
302
|
* @param {Element} domEl
|
|
235
303
|
* @param {Object} options
|
|
236
304
|
* autoAdjust (Bool): Sets in a correct position at the beggining
|
|
@@ -238,8 +306,8 @@ LX.vec2 = vec2;
|
|
|
238
306
|
* onMove (Function): Called each move event
|
|
239
307
|
* onDragStart (Function): Called when drag event starts
|
|
240
308
|
*/
|
|
241
|
-
function makeDraggable( domEl, options = { } )
|
|
242
|
-
|
|
309
|
+
function makeDraggable( domEl, options = { } )
|
|
310
|
+
{
|
|
243
311
|
let offsetX = 0;
|
|
244
312
|
let offsetY = 0;
|
|
245
313
|
let currentTarget = null;
|
|
@@ -329,24 +397,166 @@ function makeDraggable( domEl, options = { } ) {
|
|
|
329
397
|
|
|
330
398
|
LX.makeDraggable = makeDraggable;
|
|
331
399
|
|
|
332
|
-
|
|
400
|
+
/**
|
|
401
|
+
* @method makeCodeSnippet
|
|
402
|
+
* @description Create a code snippet in a specific language
|
|
403
|
+
* @param {String} code
|
|
404
|
+
* @param {Array} size
|
|
405
|
+
* @param {Object} options
|
|
406
|
+
* language (String):
|
|
407
|
+
* windowMode (Boolean):
|
|
408
|
+
* lineNumbers (Boolean):
|
|
409
|
+
* firstLine (Number): TODO
|
|
410
|
+
* linesAdded (Array):
|
|
411
|
+
* linesRemoved (Array):
|
|
412
|
+
* tabName (String):
|
|
413
|
+
*/
|
|
414
|
+
function makeCodeSnippet( code, size, options = { } )
|
|
415
|
+
{
|
|
416
|
+
if( !LX.has('CodeEditor') )
|
|
417
|
+
{
|
|
418
|
+
console.error( "Import the CodeEditor component to create snippets!" );
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const snippet = document.createElement( "div" );
|
|
423
|
+
snippet.className = "lexcodesnippet";
|
|
424
|
+
snippet.style.width = size ? size[ 0 ] : "auto";
|
|
425
|
+
snippet.style.height = size ? size[ 1 ] : "auto";
|
|
426
|
+
const area = new Area( { noAppend: true } );
|
|
427
|
+
let editor = new LX.CodeEditor( area, {
|
|
428
|
+
skipInfo: true,
|
|
429
|
+
disableEdition: true,
|
|
430
|
+
allowAddScripts: false,
|
|
431
|
+
name: options.tabName,
|
|
432
|
+
// showTab: options.showTab ?? true
|
|
433
|
+
} );
|
|
434
|
+
editor.setText( code, options.language ?? "Plain Text" );
|
|
435
|
+
|
|
436
|
+
if( options.linesAdded )
|
|
437
|
+
{
|
|
438
|
+
const code = editor.root.querySelector( ".code" );
|
|
439
|
+
for( let l of options.linesAdded )
|
|
440
|
+
{
|
|
441
|
+
if( l.constructor == Number )
|
|
442
|
+
{
|
|
443
|
+
code.childNodes[ l - 1 ].classList.add( "added" );
|
|
444
|
+
}
|
|
445
|
+
else if( l.constructor == Array ) // It's a range
|
|
446
|
+
{
|
|
447
|
+
for( let i = ( l[0] - 1 ); i <= ( l[1] - 1 ); i++ )
|
|
448
|
+
{
|
|
449
|
+
code.childNodes[ i ].classList.add( "added" );
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if( options.linesRemoved )
|
|
456
|
+
{
|
|
457
|
+
const code = editor.root.querySelector( ".code" );
|
|
458
|
+
for( let l of options.linesRemoved )
|
|
459
|
+
{
|
|
460
|
+
if( l.constructor == Number )
|
|
461
|
+
{
|
|
462
|
+
code.childNodes[ l - 1 ].classList.add( "removed" );
|
|
463
|
+
}
|
|
464
|
+
else if( l.constructor == Array ) // It's a range
|
|
465
|
+
{
|
|
466
|
+
for( let i = ( l[0] - 1 ); i <= ( l[1] - 1 ); i++ )
|
|
467
|
+
{
|
|
468
|
+
code.childNodes[ i ].classList.add( "removed" );
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if( options.windowMode )
|
|
475
|
+
{
|
|
476
|
+
const windowActionButtons = document.createElement( "div" );
|
|
477
|
+
windowActionButtons.className = "lexwindowbuttons";
|
|
478
|
+
const aButton = document.createElement( "span" );
|
|
479
|
+
aButton.style.background = "#ee4f50";
|
|
480
|
+
const bButton = document.createElement( "span" );
|
|
481
|
+
bButton.style.background = "#f5b720";
|
|
482
|
+
const cButton = document.createElement( "span" );
|
|
483
|
+
cButton.style.background = "#53ca29";
|
|
484
|
+
windowActionButtons.appendChild( aButton );
|
|
485
|
+
windowActionButtons.appendChild( bButton );
|
|
486
|
+
windowActionButtons.appendChild( cButton );
|
|
487
|
+
const tabs = editor.root.querySelector( ".lexareatabs" );
|
|
488
|
+
tabs.prepend( windowActionButtons );
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if( !( options.lineNumbers ?? true ) )
|
|
492
|
+
{
|
|
493
|
+
editor.root.classList.add( "no-gutter" );
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
snippet.appendChild( area.root );
|
|
497
|
+
return snippet;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
LX.makeCodeSnippet = makeCodeSnippet;
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* @method registerCommandbarEntry
|
|
504
|
+
* @description Adds an extra command bar entry
|
|
505
|
+
* @param {String} name
|
|
506
|
+
* @param {Function} callback
|
|
507
|
+
*/
|
|
508
|
+
function registerCommandbarEntry( name, callback )
|
|
509
|
+
{
|
|
510
|
+
LX.extraCommandbarEntries.push( { name, callback } );
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
LX.registerCommandbarEntry = registerCommandbarEntry;
|
|
514
|
+
|
|
515
|
+
// Math classes
|
|
516
|
+
|
|
517
|
+
class vec2 {
|
|
518
|
+
|
|
519
|
+
constructor( x, y ) {
|
|
520
|
+
this.x = x ?? 0;
|
|
521
|
+
this.y = y ?? ( x ?? 0 );
|
|
522
|
+
}
|
|
333
523
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
524
|
+
get xy() { return [ this.x, this.y ]; }
|
|
525
|
+
get yx() { return [ this.y, this.x ]; }
|
|
526
|
+
|
|
527
|
+
set ( x, y ) { this.x = x; this.y = y; }
|
|
528
|
+
add ( v, v0 = new vec2() ) { v0.set( this.x + v.x, this.y + v.y ); return v0; }
|
|
529
|
+
sub ( v, v0 = new vec2() ) { v0.set( this.x - v.x, this.y - v.y ); return v0; }
|
|
530
|
+
mul ( v, v0 = new vec2() ) { if( v.constructor == Number ) { v = new vec2( v ) } v0.set( this.x * v.x, this.y * v.y ); return v0; }
|
|
531
|
+
div ( v, v0 = new vec2() ) { if( v.constructor == Number ) { v = new vec2( v ) } v0.set( this.x / v.x, this.y / v.y ); return v0; }
|
|
532
|
+
abs ( v0 = new vec2() ) { v0.set( Math.abs( this.x ), Math.abs( this.y ) ); return v0; }
|
|
533
|
+
dot ( v ) { return this.x * v.x + this.y * v.y; }
|
|
534
|
+
len2 () { return this.dot( this ) }
|
|
535
|
+
len () { return Math.sqrt( this.len2() ); }
|
|
536
|
+
nrm ( v0 = new vec2() ) { v0.set( this.x, this.y ); return v0.mul( 1.0 / this.len(), v0 ); }
|
|
537
|
+
dst ( v ) { return v.sub( this ).len(); }
|
|
538
|
+
clp ( min, max, v0 = new vec2() ) { v0.set( clamp( this.x, min, max ), clamp( this.y, min, max ) ); return v0; }
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
LX.vec2 = vec2;
|
|
542
|
+
|
|
543
|
+
function _createCommandbar( root )
|
|
544
|
+
{
|
|
545
|
+
let commandbar = document.createElement( "dialog" );
|
|
546
|
+
commandbar.className = "commandbar";
|
|
547
|
+
commandbar.tabIndex = -1;
|
|
548
|
+
root.appendChild( commandbar );
|
|
339
549
|
|
|
340
550
|
let allItems = [];
|
|
341
551
|
let hoverElId = null;
|
|
342
552
|
|
|
343
|
-
|
|
553
|
+
commandbar.addEventListener('keydown', function( e ) {
|
|
344
554
|
e.stopPropagation();
|
|
345
555
|
e.stopImmediatePropagation();
|
|
346
556
|
hoverElId = hoverElId ?? -1;
|
|
347
557
|
if( e.key == 'Escape' )
|
|
348
558
|
{
|
|
349
|
-
this.
|
|
559
|
+
this.close();
|
|
350
560
|
_resetBar( true );
|
|
351
561
|
}
|
|
352
562
|
else if( e.key == 'Enter' )
|
|
@@ -355,7 +565,7 @@ function create_global_searchbar( root ) {
|
|
|
355
565
|
if( el )
|
|
356
566
|
{
|
|
357
567
|
const isCheckbox = (el.item.type && el.item.type === 'checkbox');
|
|
358
|
-
this.
|
|
568
|
+
this.close();
|
|
359
569
|
if( isCheckbox )
|
|
360
570
|
{
|
|
361
571
|
el.item.checked = !el.item.checked;
|
|
@@ -370,7 +580,7 @@ function create_global_searchbar( root ) {
|
|
|
370
580
|
else if ( e.key == 'ArrowDown' && hoverElId < (allItems.length - 1) )
|
|
371
581
|
{
|
|
372
582
|
hoverElId++;
|
|
373
|
-
|
|
583
|
+
commandbar.querySelectorAll(".hovered").forEach(e => e.classList.remove('hovered'));
|
|
374
584
|
allItems[ hoverElId ].classList.add('hovered');
|
|
375
585
|
|
|
376
586
|
let dt = allItems[ hoverElId ].offsetHeight * (hoverElId + 1) - itemContainer.offsetHeight;
|
|
@@ -385,19 +595,19 @@ function create_global_searchbar( root ) {
|
|
|
385
595
|
} else if ( e.key == 'ArrowUp' && hoverElId > 0 )
|
|
386
596
|
{
|
|
387
597
|
hoverElId--;
|
|
388
|
-
|
|
598
|
+
commandbar.querySelectorAll(".hovered").forEach(e => e.classList.remove('hovered'));
|
|
389
599
|
allItems[ hoverElId ].classList.add('hovered');
|
|
390
600
|
}
|
|
391
601
|
});
|
|
392
602
|
|
|
393
|
-
|
|
603
|
+
commandbar.addEventListener('focusout', function( e ) {
|
|
394
604
|
if( e.relatedTarget == e.currentTarget )
|
|
395
605
|
{
|
|
396
606
|
return;
|
|
397
607
|
}
|
|
398
608
|
e.stopPropagation();
|
|
399
609
|
e.stopImmediatePropagation();
|
|
400
|
-
this.
|
|
610
|
+
this.close();
|
|
401
611
|
_resetBar( true );
|
|
402
612
|
});
|
|
403
613
|
|
|
@@ -406,9 +616,7 @@ function create_global_searchbar( root ) {
|
|
|
406
616
|
{
|
|
407
617
|
e.stopImmediatePropagation();
|
|
408
618
|
e.stopPropagation();
|
|
409
|
-
|
|
410
|
-
globalSearch.querySelector('input').focus();
|
|
411
|
-
_addElements( undefined );
|
|
619
|
+
LX.setCommandbarState( true );
|
|
412
620
|
}
|
|
413
621
|
else
|
|
414
622
|
{
|
|
@@ -486,22 +694,22 @@ function create_global_searchbar( root ) {
|
|
|
486
694
|
const isCheckbox = (i && i.type && i.type === 'checkbox');
|
|
487
695
|
if( isCheckbox )
|
|
488
696
|
{
|
|
489
|
-
searchItem.innerHTML = "<a class='fa fa-check'></a><span>" + p + t + "</span>"
|
|
697
|
+
searchItem.innerHTML = "<a class='fa fa-check'></a><span>" + ( p + t ) + "</span>"
|
|
490
698
|
}
|
|
491
699
|
else
|
|
492
700
|
{
|
|
493
|
-
searchItem.innerHTML = p + t;
|
|
701
|
+
searchItem.innerHTML = ( p + t );
|
|
494
702
|
}
|
|
495
703
|
searchItem.entry_name = t;
|
|
496
704
|
searchItem.callback = c;
|
|
497
705
|
searchItem.item = i;
|
|
498
706
|
searchItem.addEventListener('click', function(e) {
|
|
499
|
-
this.callback.call(window, this.entry_name);
|
|
500
|
-
|
|
707
|
+
this.callback.call( window, this.entry_name );
|
|
708
|
+
LX.setCommandbarState( false );
|
|
501
709
|
_resetBar( true );
|
|
502
710
|
});
|
|
503
711
|
searchItem.addEventListener('mouseenter', function(e) {
|
|
504
|
-
|
|
712
|
+
commandbar.querySelectorAll(".hovered").forEach(e => e.classList.remove('hovered'));
|
|
505
713
|
this.classList.add('hovered');
|
|
506
714
|
hoverElId = allItems.indexOf( this );
|
|
507
715
|
});
|
|
@@ -535,7 +743,7 @@ function create_global_searchbar( root ) {
|
|
|
535
743
|
_propagateAdd( c, filter, path );
|
|
536
744
|
};
|
|
537
745
|
|
|
538
|
-
|
|
746
|
+
commandbar._addElements = filter => {
|
|
539
747
|
|
|
540
748
|
_resetBar();
|
|
541
749
|
|
|
@@ -547,6 +755,16 @@ function create_global_searchbar( root ) {
|
|
|
547
755
|
}
|
|
548
756
|
}
|
|
549
757
|
|
|
758
|
+
for( let entry of LX.extraCommandbarEntries )
|
|
759
|
+
{
|
|
760
|
+
const name = entry.name;
|
|
761
|
+
if( !name.toLowerCase().includes( filter ) )
|
|
762
|
+
{
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
_addElement( name, entry.callback, "", {} );
|
|
766
|
+
}
|
|
767
|
+
|
|
550
768
|
if( LX.has('CodeEditor') )
|
|
551
769
|
{
|
|
552
770
|
const instances = LX.CodeEditor.getInstances();
|
|
@@ -564,7 +782,7 @@ function create_global_searchbar( root ) {
|
|
|
564
782
|
|
|
565
783
|
value += key + " <span class='lang-ext'>(" + languages[ l ].ext + ")</span>";
|
|
566
784
|
if( key.toLowerCase().includes( filter ) ) {
|
|
567
|
-
|
|
785
|
+
_addElement( value, () => {
|
|
568
786
|
for( let i of instances ) {
|
|
569
787
|
i._changeLanguage( l );
|
|
570
788
|
}
|
|
@@ -575,14 +793,14 @@ function create_global_searchbar( root ) {
|
|
|
575
793
|
}
|
|
576
794
|
|
|
577
795
|
input.addEventListener('input', function(e) {
|
|
578
|
-
_addElements( this.value.toLowerCase() );
|
|
796
|
+
commandbar._addElements( this.value.toLowerCase() );
|
|
579
797
|
});
|
|
580
798
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
799
|
+
commandbar.appendChild( header );
|
|
800
|
+
commandbar.appendChild( tabArea.root );
|
|
801
|
+
commandbar.appendChild( itemContainer );
|
|
584
802
|
|
|
585
|
-
return
|
|
803
|
+
return commandbar;
|
|
586
804
|
}
|
|
587
805
|
|
|
588
806
|
/**
|
|
@@ -592,6 +810,7 @@ function create_global_searchbar( root ) {
|
|
|
592
810
|
* id: Id of the main area
|
|
593
811
|
* skipRoot: Skip adding LX root container
|
|
594
812
|
* skipDefaultArea: Skip creation of main area
|
|
813
|
+
* strictViewport: Use only window area
|
|
595
814
|
*/
|
|
596
815
|
|
|
597
816
|
function init( options = { } )
|
|
@@ -614,16 +833,17 @@ function init( options = { } )
|
|
|
614
833
|
this.root = root;
|
|
615
834
|
this.container = document.body;
|
|
616
835
|
|
|
617
|
-
// this.modal.toggleAttribute( 'hidden', true );
|
|
618
|
-
// this.modal.toggle = function( force ) { this.toggleAttribute( 'hidden', force ); };
|
|
619
|
-
|
|
620
836
|
this.modal.classList.add( 'hiddenOpacity' );
|
|
621
837
|
this.modal.toggle = function( force ) { this.classList.toggle( 'hiddenOpacity', force ); };
|
|
622
838
|
|
|
623
839
|
if( options.container )
|
|
840
|
+
{
|
|
624
841
|
this.container = document.getElementById( options.container );
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
document.documentElement.setAttribute( "data-strictVP", ( options.strictViewport ?? true ) ? "true" : "false" );
|
|
625
845
|
|
|
626
|
-
this.
|
|
846
|
+
this.commandbar = _createCommandbar( this.container );
|
|
627
847
|
|
|
628
848
|
this.container.appendChild( modal );
|
|
629
849
|
|
|
@@ -667,16 +887,48 @@ function init( options = { } )
|
|
|
667
887
|
this.main_area = new Area( { id: options.id ?? 'mainarea' } );
|
|
668
888
|
}
|
|
669
889
|
|
|
670
|
-
window.matchMedia( "(prefers-color-scheme:
|
|
671
|
-
|
|
672
|
-
LX.
|
|
673
|
-
|
|
890
|
+
if( ( options.autoTheme ?? true ) && window.matchMedia && window.matchMedia( "(prefers-color-scheme: light)" ).matches )
|
|
891
|
+
{
|
|
892
|
+
LX.setTheme( "light" );
|
|
893
|
+
|
|
894
|
+
window.matchMedia( "(prefers-color-scheme: dark)" ).addEventListener( "change", event => {
|
|
895
|
+
LX.setTheme( event.matches ? "dark" : "light" );
|
|
896
|
+
});
|
|
897
|
+
}
|
|
674
898
|
|
|
675
899
|
return this.main_area;
|
|
676
900
|
}
|
|
677
901
|
|
|
678
902
|
LX.init = init;
|
|
679
903
|
|
|
904
|
+
/**
|
|
905
|
+
* @method setCommandbarState
|
|
906
|
+
* @param {Boolean} value
|
|
907
|
+
* @param {Boolean} resetEntries
|
|
908
|
+
*/
|
|
909
|
+
|
|
910
|
+
function setCommandbarState( value, resetEntries = true )
|
|
911
|
+
{
|
|
912
|
+
const cb = this.commandbar;
|
|
913
|
+
|
|
914
|
+
if( value )
|
|
915
|
+
{
|
|
916
|
+
cb.show();
|
|
917
|
+
cb.querySelector('input').focus();
|
|
918
|
+
|
|
919
|
+
if( resetEntries )
|
|
920
|
+
{
|
|
921
|
+
cb._addElements( undefined );
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
else
|
|
925
|
+
{
|
|
926
|
+
cb.close();
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
LX.setCommandbarState = setCommandbarState;
|
|
931
|
+
|
|
680
932
|
/**
|
|
681
933
|
* @method message
|
|
682
934
|
* @param {String} text
|
|
@@ -709,9 +961,9 @@ LX.message = message;
|
|
|
709
961
|
* @param {String} title (Optional)
|
|
710
962
|
* @param {*} options
|
|
711
963
|
* id: Id of the message dialog
|
|
712
|
-
*
|
|
713
|
-
* position
|
|
714
|
-
* size
|
|
964
|
+
* timeout (Number): Delay time before it closes automatically (ms). Default: [3000]
|
|
965
|
+
* position (Array): [x,y] Dialog position in screen. Default: [screen centered]
|
|
966
|
+
* size (Array): [width, height]
|
|
715
967
|
*/
|
|
716
968
|
|
|
717
969
|
function popup( text, title, options = {} )
|
|
@@ -721,7 +973,7 @@ function popup( text, title, options = {} )
|
|
|
721
973
|
throw("No message to show");
|
|
722
974
|
}
|
|
723
975
|
|
|
724
|
-
options.size = options.size ?? [ "
|
|
976
|
+
options.size = options.size ?? [ "max-content", "auto" ];
|
|
725
977
|
options.class = "lexpopup";
|
|
726
978
|
|
|
727
979
|
const time = options.timeout || 3000;
|
|
@@ -729,13 +981,9 @@ function popup( text, title, options = {} )
|
|
|
729
981
|
p.addTextArea( null, text, null, { disabled: true, fitHeight: true } );
|
|
730
982
|
}, options );
|
|
731
983
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
dialog.root.classList.add( 'fadeout' );
|
|
736
|
-
}, time - 1000 );
|
|
737
|
-
|
|
738
|
-
setTimeout( dialog.close, time );
|
|
984
|
+
setTimeout( () => {
|
|
985
|
+
dialog.close();
|
|
986
|
+
}, Math.max( time, 150 ) );
|
|
739
987
|
|
|
740
988
|
return dialog;
|
|
741
989
|
}
|
|
@@ -820,6 +1068,25 @@ function badge( text, className, options = {} )
|
|
|
820
1068
|
|
|
821
1069
|
LX.badge = badge;
|
|
822
1070
|
|
|
1071
|
+
/**
|
|
1072
|
+
* @method makeContainer
|
|
1073
|
+
* @param {Array} size
|
|
1074
|
+
* @param {String} className
|
|
1075
|
+
* @param {Object} overrideStyle
|
|
1076
|
+
*/
|
|
1077
|
+
|
|
1078
|
+
function makeContainer( size, className, overrideStyle = {} )
|
|
1079
|
+
{
|
|
1080
|
+
const container = document.createElement( "div" );
|
|
1081
|
+
container.className = "lexcontainer " + ( className ?? "" );
|
|
1082
|
+
container.style.width = size && size[ 0 ] ? size[ 0 ] : "100%";
|
|
1083
|
+
container.style.height = size && size[ 1 ] ? size[ 1 ] : "100%";
|
|
1084
|
+
Object.assign( container.style, overrideStyle );
|
|
1085
|
+
return container;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
LX.makeContainer = makeContainer;
|
|
1089
|
+
|
|
823
1090
|
/*
|
|
824
1091
|
* Events and Signals
|
|
825
1092
|
*/
|
|
@@ -1302,8 +1569,8 @@ class Area {
|
|
|
1302
1569
|
}
|
|
1303
1570
|
|
|
1304
1571
|
area1.root.style.width = "100%";
|
|
1305
|
-
area1.root.style.height = "calc( " + height1 + " - " + data + " )";
|
|
1306
|
-
area2.root.style.height = "calc( " + height2 + " - " + data + " )";
|
|
1572
|
+
area1.root.style.height = ( height1 == "auto" ? height1 : "calc( " + height1 + " - " + data + " )");
|
|
1573
|
+
area2.root.style.height = ( height2 == "auto" ? height2 : "calc( " + height2 + " - " + data + " )");
|
|
1307
1574
|
}
|
|
1308
1575
|
}
|
|
1309
1576
|
|
|
@@ -1903,7 +2170,7 @@ class Tabs {
|
|
|
1903
2170
|
|
|
1904
2171
|
area.root.classList.add( "lexareatabscontainer" );
|
|
1905
2172
|
|
|
1906
|
-
area.split({type: 'vertical', sizes: "auto", resize: false, top: 6});
|
|
2173
|
+
area.split({type: 'vertical', sizes: options.sizes ?? "auto", resize: false, top: 6});
|
|
1907
2174
|
area.sections[0].attach( container );
|
|
1908
2175
|
|
|
1909
2176
|
this.area = area.sections[1];
|
|
@@ -2590,6 +2857,17 @@ class Menubar {
|
|
|
2590
2857
|
const swapIcon = document.createElement( "a" );
|
|
2591
2858
|
swapIcon.className = data.swap + " swap-on lexicon";
|
|
2592
2859
|
button.appendChild( swapIcon );
|
|
2860
|
+
|
|
2861
|
+
button.swap = function() {
|
|
2862
|
+
const swapInput = this.querySelector( "input" );
|
|
2863
|
+
swapInput.checked = !swapInput.checked;
|
|
2864
|
+
};
|
|
2865
|
+
|
|
2866
|
+
// Set if swap has to be performed
|
|
2867
|
+
button.setState = function( v ) {
|
|
2868
|
+
const swapInput = this.querySelector( "input" );
|
|
2869
|
+
swapInput.checked = v;
|
|
2870
|
+
};
|
|
2593
2871
|
}
|
|
2594
2872
|
|
|
2595
2873
|
trigger.addEventListener("click", e => {
|
|
@@ -2758,6 +3036,7 @@ class Widget {
|
|
|
2758
3036
|
static FORM = 27;
|
|
2759
3037
|
static DIAL = 28;
|
|
2760
3038
|
static COUNTER = 29;
|
|
3039
|
+
static TABLE = 30;
|
|
2761
3040
|
|
|
2762
3041
|
static NO_CONTEXT_TYPES = [
|
|
2763
3042
|
Widget.BUTTON,
|
|
@@ -2848,6 +3127,7 @@ class Widget {
|
|
|
2848
3127
|
case Widget.FORM: return "Form";
|
|
2849
3128
|
case Widget.DIAL: return "Dial";
|
|
2850
3129
|
case Widget.COUNTER: return "Counter";
|
|
3130
|
+
case Widget.TABLE: return "Table";
|
|
2851
3131
|
case Widget.CUSTOM: return this.customName;
|
|
2852
3132
|
}
|
|
2853
3133
|
|
|
@@ -3870,8 +4150,8 @@ class Panel {
|
|
|
3870
4150
|
|
|
3871
4151
|
let searchIcon = document.createElement('a');
|
|
3872
4152
|
searchIcon.className = "fa-solid fa-magnifying-glass";
|
|
3873
|
-
element.appendChild(input);
|
|
3874
4153
|
element.appendChild(searchIcon);
|
|
4154
|
+
element.appendChild(input);
|
|
3875
4155
|
|
|
3876
4156
|
input.addEventListener("input", (e) => {
|
|
3877
4157
|
if(options.callback)
|
|
@@ -3923,26 +4203,28 @@ class Panel {
|
|
|
3923
4203
|
}
|
|
3924
4204
|
}
|
|
3925
4205
|
|
|
3926
|
-
|
|
3927
|
-
|
|
4206
|
+
_filterOptions( options, value ) {
|
|
4207
|
+
|
|
4208
|
+
// Push to right container
|
|
3928
4209
|
const emptyFilter = !value.length;
|
|
3929
4210
|
let filteredOptions = [];
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
4211
|
+
|
|
4212
|
+
// Add widgets
|
|
4213
|
+
for( let i = 0; i < options.length; i++ )
|
|
4214
|
+
{
|
|
4215
|
+
let o = options[ i ];
|
|
4216
|
+
if( !emptyFilter )
|
|
3934
4217
|
{
|
|
3935
|
-
let toCompare = (typeof o == 'string') ? o : o.value;
|
|
3936
|
-
;
|
|
4218
|
+
let toCompare = ( typeof o == 'string' ) ? o : o.value;
|
|
3937
4219
|
const filterWord = value.toLowerCase();
|
|
3938
4220
|
const name = toCompare.toLowerCase();
|
|
3939
|
-
if(!name.includes(filterWord)) continue;
|
|
4221
|
+
if( !name.includes( filterWord ) ) continue;
|
|
3940
4222
|
}
|
|
3941
|
-
|
|
3942
|
-
filteredOptions.push(o);
|
|
4223
|
+
|
|
4224
|
+
filteredOptions.push( o );
|
|
3943
4225
|
}
|
|
3944
4226
|
|
|
3945
|
-
this.refresh(filteredOptions);
|
|
4227
|
+
this.refresh( filteredOptions );
|
|
3946
4228
|
}
|
|
3947
4229
|
|
|
3948
4230
|
_trigger( event, callback ) {
|
|
@@ -4429,66 +4711,82 @@ class Panel {
|
|
|
4429
4711
|
* @param {Array} values Each of the {value, callback} items
|
|
4430
4712
|
* @param {*} options:
|
|
4431
4713
|
* float: Justify content (left, center, right) [center]
|
|
4714
|
+
* selected: Selected item by default by value
|
|
4432
4715
|
* noSelection: Buttons can be clicked, but they are not selectable
|
|
4433
4716
|
*/
|
|
4434
4717
|
|
|
4435
4718
|
addComboButtons( name, values, options = {} ) {
|
|
4436
4719
|
|
|
4437
|
-
let widget = this.create_widget(name, Widget.BUTTON, options);
|
|
4720
|
+
let widget = this.create_widget( name, Widget.BUTTON, options );
|
|
4438
4721
|
let element = widget.domEl;
|
|
4439
4722
|
|
|
4440
4723
|
let that = this;
|
|
4441
4724
|
let container = document.createElement('div');
|
|
4442
4725
|
container.className = "lexcombobuttons ";
|
|
4443
|
-
|
|
4726
|
+
|
|
4727
|
+
if( options.float )
|
|
4728
|
+
{
|
|
4729
|
+
container.className += options.float;
|
|
4730
|
+
}
|
|
4731
|
+
|
|
4444
4732
|
container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
|
|
4445
4733
|
|
|
4446
|
-
let
|
|
4734
|
+
let buttonsBox = document.createElement('div');
|
|
4735
|
+
buttonsBox.className = "lexcombobuttonsbox ";
|
|
4736
|
+
|
|
4737
|
+
let shouldSelect = !( options.noSelection ?? false );
|
|
4738
|
+
|
|
4447
4739
|
for( let b of values )
|
|
4448
4740
|
{
|
|
4449
|
-
if( !b.value )
|
|
4741
|
+
if( !b.value )
|
|
4742
|
+
{
|
|
4743
|
+
throw( "Set 'value' for each button!" );
|
|
4744
|
+
}
|
|
4450
4745
|
|
|
4451
4746
|
let buttonEl = document.createElement('button');
|
|
4452
4747
|
buttonEl.className = "lexbutton combo";
|
|
4453
4748
|
buttonEl.title = b.icon ? b.value : "";
|
|
4454
|
-
|
|
4455
|
-
buttonEl.classList.add(options.buttonClass);
|
|
4749
|
+
buttonEl.id = b.id ?? "";
|
|
4456
4750
|
|
|
4457
|
-
if(options.
|
|
4458
|
-
|
|
4751
|
+
if( options.buttonClass )
|
|
4752
|
+
{
|
|
4753
|
+
buttonEl.classList.add( options.buttonClass );
|
|
4754
|
+
}
|
|
4459
4755
|
|
|
4460
|
-
if(b.
|
|
4461
|
-
|
|
4756
|
+
if( shouldSelect && options.selected == b.value )
|
|
4757
|
+
{
|
|
4758
|
+
buttonEl.classList.add("selected");
|
|
4759
|
+
}
|
|
4462
4760
|
|
|
4463
|
-
buttonEl.innerHTML = (b.icon ? "<a class='" + b.icon +"'></a>" : "") + "<span>" + (b.icon ? "" : b.value) + "</span>";
|
|
4761
|
+
buttonEl.innerHTML = ( b.icon ? "<a class='" + b.icon +"'></a>" : "" ) + "<span>" + ( b.icon ? "" : b.value ) + "</span>";
|
|
4464
4762
|
|
|
4465
|
-
if(options.disabled)
|
|
4466
|
-
|
|
4763
|
+
if( options.disabled )
|
|
4764
|
+
{
|
|
4765
|
+
buttonEl.setAttribute( "disabled", true );
|
|
4766
|
+
}
|
|
4467
4767
|
|
|
4468
|
-
buttonEl.addEventListener("click", function(e) {
|
|
4469
|
-
if(
|
|
4768
|
+
buttonEl.addEventListener("click", function( e ) {
|
|
4769
|
+
if( shouldSelect )
|
|
4770
|
+
{
|
|
4470
4771
|
container.querySelectorAll('button').forEach( s => s.classList.remove('selected'));
|
|
4471
4772
|
this.classList.add('selected');
|
|
4472
4773
|
}
|
|
4473
|
-
that._trigger( new IEvent(name, b.value, e), b.callback );
|
|
4474
|
-
});
|
|
4475
4774
|
|
|
4476
|
-
|
|
4775
|
+
that._trigger( new IEvent( name, b.value, e ), b.callback );
|
|
4776
|
+
});
|
|
4477
4777
|
|
|
4478
|
-
|
|
4479
|
-
if(widget.name === undefined) {
|
|
4480
|
-
buttonEl.className += " noname";
|
|
4481
|
-
buttonEl.style.width = "100%";
|
|
4482
|
-
}
|
|
4778
|
+
buttonsBox.appendChild( buttonEl );
|
|
4483
4779
|
}
|
|
4484
4780
|
|
|
4485
4781
|
// Remove branch padding and margins
|
|
4486
|
-
if(widget.name
|
|
4782
|
+
if( !widget.name)
|
|
4783
|
+
{
|
|
4487
4784
|
element.className += " noname";
|
|
4488
4785
|
container.style.width = "100%";
|
|
4489
4786
|
}
|
|
4490
4787
|
|
|
4491
|
-
|
|
4788
|
+
container.appendChild( buttonsBox );
|
|
4789
|
+
element.appendChild( container );
|
|
4492
4790
|
|
|
4493
4791
|
return widget;
|
|
4494
4792
|
}
|
|
@@ -4740,6 +5038,8 @@ class Panel {
|
|
|
4740
5038
|
* filter: Add a search bar to the widget [false]
|
|
4741
5039
|
* disabled: Make the widget disabled [false]
|
|
4742
5040
|
* skipReset: Don't add the reset value button when value changes
|
|
5041
|
+
* placeholder: Placeholder for the filter input
|
|
5042
|
+
* emptyMsg: Custom message to show when no filtered results
|
|
4743
5043
|
*/
|
|
4744
5044
|
|
|
4745
5045
|
addDropdown( name, values, value, callback, options = {} ) {
|
|
@@ -4785,17 +5085,74 @@ class Panel {
|
|
|
4785
5085
|
let buttonName = value;
|
|
4786
5086
|
buttonName += "<a class='fa-solid fa-angle-down' style='float:right; margin-right: 3px;'></a>";
|
|
4787
5087
|
|
|
4788
|
-
this.queue(container);
|
|
5088
|
+
this.queue( container );
|
|
5089
|
+
|
|
5090
|
+
const _placeOptions = ( parent ) => {
|
|
4789
5091
|
|
|
4790
|
-
|
|
5092
|
+
console.log("Replacing container");
|
|
4791
5093
|
|
|
4792
|
-
|
|
4793
|
-
|
|
5094
|
+
const overflowContainer = parent.getParentArea();
|
|
5095
|
+
const rect = selectedOption.getBoundingClientRect();
|
|
5096
|
+
const nestedDialog = parent.parentElement.closest( "dialog" );
|
|
5097
|
+
|
|
5098
|
+
// Manage vertical aspect
|
|
5099
|
+
{
|
|
5100
|
+
const listHeight = parent.offsetHeight;
|
|
5101
|
+
let topPosition = rect.y;
|
|
5102
|
+
|
|
5103
|
+
let maxY = window.innerHeight;
|
|
5104
|
+
|
|
5105
|
+
if( overflowContainer )
|
|
5106
|
+
{
|
|
5107
|
+
const parentRect = overflowContainer.getBoundingClientRect();
|
|
5108
|
+
maxY = parentRect.y + parentRect.height;
|
|
5109
|
+
}
|
|
5110
|
+
|
|
5111
|
+
if( nestedDialog )
|
|
5112
|
+
{
|
|
5113
|
+
const rect = nestedDialog.getBoundingClientRect();
|
|
5114
|
+
topPosition -= rect.y;
|
|
5115
|
+
}
|
|
5116
|
+
|
|
5117
|
+
parent.style.top = ( topPosition + selectedOption.offsetHeight ) + 'px';
|
|
5118
|
+
|
|
5119
|
+
const showAbove = ( topPosition + listHeight ) > maxY;
|
|
5120
|
+
if( showAbove )
|
|
5121
|
+
{
|
|
5122
|
+
parent.style.top = ( topPosition - listHeight ) + 'px';
|
|
5123
|
+
parent.classList.add( "place-above" );
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
5126
|
+
|
|
5127
|
+
// Manage horizontal aspect
|
|
4794
5128
|
{
|
|
4795
|
-
const
|
|
4796
|
-
|
|
5129
|
+
const listWidth = parent.offsetWidth;
|
|
5130
|
+
let leftPosition = rect.x;
|
|
5131
|
+
|
|
5132
|
+
parent.style.minWidth = ( rect.width ) + 'px';
|
|
5133
|
+
|
|
5134
|
+
if( nestedDialog )
|
|
5135
|
+
{
|
|
5136
|
+
const rect = nestedDialog.getBoundingClientRect();
|
|
5137
|
+
leftPosition -= rect.x;
|
|
5138
|
+
}
|
|
5139
|
+
|
|
5140
|
+
parent.style.left = ( leftPosition ) + 'px';
|
|
5141
|
+
|
|
5142
|
+
let maxX = window.innerWidth;
|
|
5143
|
+
|
|
5144
|
+
if( overflowContainer )
|
|
5145
|
+
{
|
|
5146
|
+
const parentRect = overflowContainer.getBoundingClientRect();
|
|
5147
|
+
maxX = parentRect.x + parentRect.width;
|
|
5148
|
+
}
|
|
5149
|
+
|
|
5150
|
+
const showLeft = ( leftPosition + listWidth ) > maxX;
|
|
5151
|
+
if( showLeft )
|
|
5152
|
+
{
|
|
5153
|
+
parent.style.left = ( leftPosition - ( listWidth - rect.width ) ) + 'px';
|
|
5154
|
+
}
|
|
4797
5155
|
}
|
|
4798
|
-
return Math.max( maxWidth * 10, 80 );
|
|
4799
5156
|
};
|
|
4800
5157
|
|
|
4801
5158
|
let selectedOption = this.addButton( null, buttonName, ( value, event ) => {
|
|
@@ -4805,51 +5162,50 @@ class Panel {
|
|
|
4805
5162
|
return;
|
|
4806
5163
|
}
|
|
4807
5164
|
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
const listHeight = 26 * values.length;
|
|
4812
|
-
const rect = selectedOption.getBoundingClientRect();
|
|
4813
|
-
const topPosition = rect.y;
|
|
5165
|
+
listDialog.classList.remove( "place-above" );
|
|
5166
|
+
const opened = listDialog.hasAttribute( "open" );
|
|
4814
5167
|
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
if( this.mainContainer )
|
|
5168
|
+
if( !opened )
|
|
4818
5169
|
{
|
|
4819
|
-
|
|
4820
|
-
|
|
5170
|
+
listDialog.show();
|
|
5171
|
+
_placeOptions( listDialog );
|
|
5172
|
+
}
|
|
5173
|
+
else
|
|
5174
|
+
{
|
|
5175
|
+
listDialog.close();
|
|
4821
5176
|
}
|
|
4822
5177
|
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
const showAbove = ( topPosition + listHeight ) > maxY;
|
|
4826
|
-
if( showAbove )
|
|
5178
|
+
if( filter )
|
|
4827
5179
|
{
|
|
4828
|
-
|
|
4829
|
-
list.classList.add( "place-above" );
|
|
5180
|
+
filter.querySelector( "input" ).focus();
|
|
4830
5181
|
}
|
|
4831
5182
|
|
|
4832
|
-
|
|
4833
|
-
list.style.minWidth = (_getMaxListWidth()) + 'px';
|
|
4834
|
-
list.focus();
|
|
4835
|
-
}, { buttonClass: "array", skipInlineCount: true });
|
|
5183
|
+
}, { buttonClass: "array", skipInlineCount: true, disabled: options.disabled });
|
|
4836
5184
|
|
|
4837
5185
|
this.clearQueue();
|
|
4838
5186
|
|
|
4839
5187
|
selectedOption.style.width = "100%";
|
|
4840
5188
|
|
|
4841
5189
|
selectedOption.refresh = (v) => {
|
|
4842
|
-
if(selectedOption.querySelector("span").innerText == "")
|
|
5190
|
+
if( selectedOption.querySelector("span").innerText == "" )
|
|
5191
|
+
{
|
|
4843
5192
|
selectedOption.querySelector("span").innerText = v;
|
|
5193
|
+
}
|
|
4844
5194
|
else
|
|
5195
|
+
{
|
|
4845
5196
|
selectedOption.querySelector("span").innerHTML = selectedOption.querySelector("span").innerHTML.replaceAll(selectedOption.querySelector("span").innerText, v);
|
|
5197
|
+
}
|
|
4846
5198
|
}
|
|
4847
5199
|
|
|
4848
5200
|
// Add dropdown options container
|
|
5201
|
+
|
|
5202
|
+
const listDialog = document.createElement( 'dialog' );
|
|
5203
|
+
listDialog.className = "lexdropdownoptions";
|
|
5204
|
+
|
|
4849
5205
|
let list = document.createElement( 'ul' );
|
|
4850
5206
|
list.tabIndex = -1;
|
|
4851
5207
|
list.className = "lexoptions";
|
|
4852
|
-
|
|
5208
|
+
listDialog.appendChild( list )
|
|
4853
5209
|
|
|
4854
5210
|
list.addEventListener( 'focusout', function( e ) {
|
|
4855
5211
|
e.stopPropagation();
|
|
@@ -4867,97 +5223,118 @@ class Panel {
|
|
|
4867
5223
|
{
|
|
4868
5224
|
return;
|
|
4869
5225
|
}
|
|
4870
|
-
|
|
5226
|
+
listDialog.close();
|
|
4871
5227
|
});
|
|
4872
5228
|
|
|
4873
5229
|
// Add filter options
|
|
4874
5230
|
let filter = null;
|
|
4875
|
-
if(options.filter ?? false)
|
|
5231
|
+
if( options.filter ?? false )
|
|
4876
5232
|
{
|
|
4877
|
-
filter = this._addFilter("Search
|
|
4878
|
-
}
|
|
4879
|
-
|
|
4880
|
-
// Create option list to empty it easily..
|
|
4881
|
-
const listOptions = document.createElement('span');
|
|
4882
|
-
list.appendChild( listOptions );
|
|
5233
|
+
filter = this._addFilter( options.placeholder ?? "Search...", { container: list, callback: this._filterOptions.bind( list, values )} );
|
|
4883
5234
|
|
|
4884
|
-
|
|
4885
|
-
{
|
|
4886
|
-
list.prepend( filter );
|
|
4887
|
-
listOptions.style.height = "calc(100% - 25px)";
|
|
5235
|
+
list.appendChild( filter );
|
|
4888
5236
|
|
|
4889
5237
|
filter.addEventListener('focusout', function( e ) {
|
|
4890
5238
|
if (e.relatedTarget && e.relatedTarget.tagName == "UL" && e.relatedTarget.classList.contains("lexoptions"))
|
|
4891
5239
|
{
|
|
4892
5240
|
return;
|
|
4893
5241
|
}
|
|
4894
|
-
|
|
5242
|
+
listDialog.close();
|
|
4895
5243
|
});
|
|
4896
5244
|
}
|
|
4897
5245
|
|
|
5246
|
+
// Create option list to empty it easily..
|
|
5247
|
+
const listOptions = document.createElement('span');
|
|
5248
|
+
listOptions.style.height = "calc(100% - 25px)";
|
|
5249
|
+
list.appendChild( listOptions );
|
|
5250
|
+
|
|
4898
5251
|
// Add dropdown options list
|
|
4899
|
-
list.refresh = options => {
|
|
5252
|
+
list.refresh = ( options ) => {
|
|
4900
5253
|
|
|
4901
5254
|
// Empty list
|
|
4902
5255
|
listOptions.innerHTML = "";
|
|
4903
5256
|
|
|
4904
|
-
|
|
5257
|
+
if( !options.length )
|
|
5258
|
+
{
|
|
5259
|
+
let iValue = options.emptyMsg ?? "No options found.";
|
|
5260
|
+
|
|
5261
|
+
let option = document.createElement( "div" );
|
|
5262
|
+
option.className = "option";
|
|
5263
|
+
option.style.flexDirection = "unset";
|
|
5264
|
+
option.innerHTML = iValue;
|
|
5265
|
+
|
|
5266
|
+
let li = document.createElement( "li" );
|
|
5267
|
+
li.className = "lexdropdownitem empty";
|
|
5268
|
+
li.appendChild( option );
|
|
5269
|
+
|
|
5270
|
+
listOptions.appendChild( li );
|
|
5271
|
+
return;
|
|
5272
|
+
}
|
|
5273
|
+
|
|
5274
|
+
for( let i = 0; i < options.length; i++ )
|
|
4905
5275
|
{
|
|
4906
|
-
let iValue = options[i];
|
|
4907
|
-
let li = document.createElement(
|
|
4908
|
-
let option = document.createElement(
|
|
5276
|
+
let iValue = options[ i ];
|
|
5277
|
+
let li = document.createElement( "li" );
|
|
5278
|
+
let option = document.createElement( "div" );
|
|
4909
5279
|
option.className = "option";
|
|
4910
|
-
li.appendChild(option);
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
e.currentTarget.
|
|
4917
|
-
e.currentTarget.
|
|
5280
|
+
li.appendChild( option );
|
|
5281
|
+
|
|
5282
|
+
li.addEventListener( "click", e => {
|
|
5283
|
+
listDialog.close();
|
|
5284
|
+
const currentSelected = element.querySelector( ".lexoptions .selected" );
|
|
5285
|
+
if(currentSelected) currentSelected.classList.remove( "selected" );
|
|
5286
|
+
value = e.currentTarget.getAttribute( "value" );
|
|
5287
|
+
e.currentTarget.toggleAttribute( "hidden", false );
|
|
5288
|
+
e.currentTarget.classList.add( "selected" );
|
|
4918
5289
|
selectedOption.refresh(value);
|
|
4919
5290
|
|
|
4920
|
-
let btn = element.querySelector(".lexwidgetname .lexicon");
|
|
4921
|
-
if(btn) btn.style.display = (value != wValue.iValue ? "block" : "none");
|
|
4922
|
-
that._trigger( new IEvent(name, value, null), callback );
|
|
5291
|
+
let btn = element.querySelector( ".lexwidgetname .lexicon" );
|
|
5292
|
+
if( btn ) btn.style.display = (value != wValue.iValue ? "block" : "none");
|
|
5293
|
+
that._trigger( new IEvent( name, value, null ), callback );
|
|
4923
5294
|
|
|
4924
5295
|
// Reset filter
|
|
4925
|
-
if(filter)
|
|
5296
|
+
if( filter )
|
|
4926
5297
|
{
|
|
4927
|
-
filter.querySelector(
|
|
4928
|
-
this.
|
|
5298
|
+
filter.querySelector( "input" ).value = "";
|
|
5299
|
+
this._filterOptions.bind( list, values, "" )();
|
|
4929
5300
|
}
|
|
4930
5301
|
});
|
|
4931
5302
|
|
|
4932
5303
|
// Add string option
|
|
4933
|
-
if( iValue.constructor != Object )
|
|
4934
|
-
|
|
5304
|
+
if( iValue.constructor != Object )
|
|
5305
|
+
{
|
|
5306
|
+
option.style.flexDirection = "unset";
|
|
4935
5307
|
option.innerHTML = "</a><span>" + iValue + "</span><a class='fa-solid fa-check'>";
|
|
4936
5308
|
option.value = iValue;
|
|
4937
|
-
li.setAttribute("value", iValue);
|
|
5309
|
+
li.setAttribute( "value", iValue );
|
|
4938
5310
|
li.className = "lexdropdownitem";
|
|
4939
|
-
|
|
4940
|
-
if(iValue == value)
|
|
4941
|
-
|
|
5311
|
+
|
|
5312
|
+
if( iValue == value )
|
|
5313
|
+
{
|
|
5314
|
+
li.classList.add( "selected" );
|
|
4942
5315
|
wValue.innerHTML = iValue;
|
|
4943
5316
|
}
|
|
4944
5317
|
}
|
|
4945
|
-
else
|
|
5318
|
+
else
|
|
5319
|
+
{
|
|
4946
5320
|
// Add image option
|
|
4947
|
-
let img = document.createElement("img");
|
|
5321
|
+
let img = document.createElement( "img" );
|
|
4948
5322
|
img.src = iValue.src;
|
|
4949
|
-
li.setAttribute("value", iValue.value);
|
|
5323
|
+
li.setAttribute( "value", iValue.value );
|
|
4950
5324
|
li.className = "lexlistitem";
|
|
4951
5325
|
option.innerText = iValue.value;
|
|
4952
5326
|
option.className += " media";
|
|
4953
|
-
option.prepend(img);
|
|
5327
|
+
option.prepend( img );
|
|
4954
5328
|
|
|
4955
|
-
option.setAttribute("value", iValue.value);
|
|
4956
|
-
option.setAttribute("data-index", i);
|
|
4957
|
-
option.setAttribute("data-src", iValue.src);
|
|
4958
|
-
option.setAttribute("title", iValue.value);
|
|
4959
|
-
|
|
4960
|
-
|
|
5329
|
+
option.setAttribute( "value", iValue.value );
|
|
5330
|
+
option.setAttribute( "data-index", i );
|
|
5331
|
+
option.setAttribute( "data-src", iValue.src );
|
|
5332
|
+
option.setAttribute( "title", iValue.value );
|
|
5333
|
+
|
|
5334
|
+
if( value == iValue.value )
|
|
5335
|
+
{
|
|
5336
|
+
li.classList.add( "selected" );
|
|
5337
|
+
}
|
|
4961
5338
|
}
|
|
4962
5339
|
|
|
4963
5340
|
listOptions.appendChild( li );
|
|
@@ -4966,7 +5343,7 @@ class Panel {
|
|
|
4966
5343
|
|
|
4967
5344
|
list.refresh( values );
|
|
4968
5345
|
|
|
4969
|
-
container.appendChild(
|
|
5346
|
+
container.appendChild( listDialog );
|
|
4970
5347
|
element.appendChild( container );
|
|
4971
5348
|
|
|
4972
5349
|
// Remove branch padding and margins
|
|
@@ -7145,6 +7522,228 @@ class Panel {
|
|
|
7145
7522
|
|
|
7146
7523
|
return widget;
|
|
7147
7524
|
}
|
|
7525
|
+
|
|
7526
|
+
/**
|
|
7527
|
+
* @method addTable
|
|
7528
|
+
* @param {String} name Widget name
|
|
7529
|
+
* @param {Number} data Table data
|
|
7530
|
+
* @param {*} options:
|
|
7531
|
+
* head: Table headers (each of the headers per column)
|
|
7532
|
+
* body: Table body (data per row for each column)
|
|
7533
|
+
* rowActions: Allow to add actions per row
|
|
7534
|
+
* onMenuAction: Function callback to fill the "menu" context
|
|
7535
|
+
* selectable: Each row can be selected
|
|
7536
|
+
*/
|
|
7537
|
+
|
|
7538
|
+
addTable( name, data, options = { } ) {
|
|
7539
|
+
|
|
7540
|
+
if( !data )
|
|
7541
|
+
{
|
|
7542
|
+
throw( "Data is needed to create a table!" );
|
|
7543
|
+
}
|
|
7544
|
+
|
|
7545
|
+
let widget = this.create_widget( name, Widget.TABLE, options );
|
|
7546
|
+
|
|
7547
|
+
widget.onGetValue = () => {
|
|
7548
|
+
|
|
7549
|
+
};
|
|
7550
|
+
|
|
7551
|
+
widget.onSetValue = ( newValue, skipCallback ) => {
|
|
7552
|
+
|
|
7553
|
+
};
|
|
7554
|
+
|
|
7555
|
+
let element = widget.domEl;
|
|
7556
|
+
|
|
7557
|
+
const container = document.createElement('div');
|
|
7558
|
+
container.className = "lextable";
|
|
7559
|
+
container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
|
|
7560
|
+
|
|
7561
|
+
const table = document.createElement( 'table' );
|
|
7562
|
+
container.appendChild( table );
|
|
7563
|
+
|
|
7564
|
+
data.head = data.head ?? [];
|
|
7565
|
+
data.body = data.body ?? [];
|
|
7566
|
+
data.orderMap = { };
|
|
7567
|
+
data.checkMap = { };
|
|
7568
|
+
|
|
7569
|
+
function compareFn( idx, order, a, b) {
|
|
7570
|
+
if (a[idx] < b[idx]) return -order;
|
|
7571
|
+
else if (a[idx] > b[idx]) return order;
|
|
7572
|
+
return 0;
|
|
7573
|
+
}
|
|
7574
|
+
|
|
7575
|
+
widget.refreshTable = () => {
|
|
7576
|
+
|
|
7577
|
+
table.innerHTML = "";
|
|
7578
|
+
|
|
7579
|
+
// Head
|
|
7580
|
+
{
|
|
7581
|
+
const head = document.createElement( 'thead' );
|
|
7582
|
+
head.className = "lextablehead";
|
|
7583
|
+
table.appendChild( head );
|
|
7584
|
+
|
|
7585
|
+
const hrow = document.createElement( 'tr' );
|
|
7586
|
+
|
|
7587
|
+
if( options.selectable )
|
|
7588
|
+
{
|
|
7589
|
+
const th = document.createElement( 'th' );
|
|
7590
|
+
const input = document.createElement( 'input' );
|
|
7591
|
+
input.type = "checkbox";
|
|
7592
|
+
input.className = "lexcheckbox";
|
|
7593
|
+
input.checked = data.checkMap[ ":root" ] ?? false;
|
|
7594
|
+
th.appendChild( input );
|
|
7595
|
+
|
|
7596
|
+
input.addEventListener( 'change', function() {
|
|
7597
|
+
|
|
7598
|
+
data.checkMap[ ":root" ] = this.checked;
|
|
7599
|
+
|
|
7600
|
+
const body = table.querySelector( "tbody" );
|
|
7601
|
+
for( const el of body.childNodes )
|
|
7602
|
+
{
|
|
7603
|
+
data.checkMap[ el.getAttribute( "rowId" ) ] = this.checked;
|
|
7604
|
+
el.querySelector( "input" ).checked = this.checked;
|
|
7605
|
+
}
|
|
7606
|
+
});
|
|
7607
|
+
|
|
7608
|
+
hrow.appendChild( th );
|
|
7609
|
+
}
|
|
7610
|
+
|
|
7611
|
+
for( const headData of data.head )
|
|
7612
|
+
{
|
|
7613
|
+
const th = document.createElement( 'th' );
|
|
7614
|
+
th.innerHTML = `${ headData } <a class="fa-solid fa-sort"></a>`;
|
|
7615
|
+
|
|
7616
|
+
th.querySelector( 'a' ).addEventListener( 'click', () => {
|
|
7617
|
+
|
|
7618
|
+
if( !data.orderMap[ headData ] )
|
|
7619
|
+
{
|
|
7620
|
+
data.orderMap[ headData ] = 1;
|
|
7621
|
+
}
|
|
7622
|
+
|
|
7623
|
+
const idx = data.head.indexOf(headData);
|
|
7624
|
+
data.body = data.body.sort( compareFn.bind( this, idx,data.orderMap[ headData ] ) );
|
|
7625
|
+
data.orderMap[ headData ] = -data.orderMap[ headData ];
|
|
7626
|
+
|
|
7627
|
+
widget.refreshTable();
|
|
7628
|
+
|
|
7629
|
+
});
|
|
7630
|
+
|
|
7631
|
+
hrow.appendChild( th );
|
|
7632
|
+
}
|
|
7633
|
+
|
|
7634
|
+
// Add empty header column
|
|
7635
|
+
if( options.rowActions )
|
|
7636
|
+
{
|
|
7637
|
+
const th = document.createElement( 'th' );
|
|
7638
|
+
th.className = "sm";
|
|
7639
|
+
hrow.appendChild( th );
|
|
7640
|
+
}
|
|
7641
|
+
|
|
7642
|
+
head.appendChild( hrow );
|
|
7643
|
+
}
|
|
7644
|
+
|
|
7645
|
+
// Body
|
|
7646
|
+
{
|
|
7647
|
+
const body = document.createElement( 'tbody' );
|
|
7648
|
+
body.className = "lextablebody";
|
|
7649
|
+
table.appendChild( body );
|
|
7650
|
+
|
|
7651
|
+
for( let r = 0; r < data.body.length; ++r )
|
|
7652
|
+
{
|
|
7653
|
+
const bodyData = data.body[ r ];
|
|
7654
|
+
const row = document.createElement( 'tr' );
|
|
7655
|
+
const rowId = LX.getSupportedDOMName( bodyData.join( '-' ) );
|
|
7656
|
+
row.setAttribute( "rowId", rowId );
|
|
7657
|
+
|
|
7658
|
+
if( options.selectable )
|
|
7659
|
+
{
|
|
7660
|
+
const td = document.createElement( 'td' );
|
|
7661
|
+
const input = document.createElement( 'input' );
|
|
7662
|
+
input.type = "checkbox";
|
|
7663
|
+
input.className = "lexcheckbox";
|
|
7664
|
+
input.checked = data.checkMap[ rowId ];
|
|
7665
|
+
td.appendChild( input );
|
|
7666
|
+
|
|
7667
|
+
input.addEventListener( 'change', function() {
|
|
7668
|
+
data.checkMap[ rowId ] = this.checked;
|
|
7669
|
+
});
|
|
7670
|
+
|
|
7671
|
+
row.appendChild( td );
|
|
7672
|
+
}
|
|
7673
|
+
|
|
7674
|
+
for( const rowData of bodyData )
|
|
7675
|
+
{
|
|
7676
|
+
const td = document.createElement( 'td' );
|
|
7677
|
+
td.innerHTML = `${ rowData }`;
|
|
7678
|
+
row.appendChild( td );
|
|
7679
|
+
}
|
|
7680
|
+
|
|
7681
|
+
if( options.rowActions )
|
|
7682
|
+
{
|
|
7683
|
+
const td = document.createElement( 'td' );
|
|
7684
|
+
td.className = "sm";
|
|
7685
|
+
|
|
7686
|
+
const buttons = document.createElement( 'div' );
|
|
7687
|
+
buttons.className = "lextablebuttons";
|
|
7688
|
+
td.appendChild( buttons );
|
|
7689
|
+
|
|
7690
|
+
for( const action of options.rowActions )
|
|
7691
|
+
{
|
|
7692
|
+
const button = document.createElement( 'a' );
|
|
7693
|
+
button.className = "lexicon";
|
|
7694
|
+
|
|
7695
|
+
if( action == "delete" )
|
|
7696
|
+
{
|
|
7697
|
+
button.className += " fa-solid fa-trash-can";
|
|
7698
|
+
button.addEventListener( 'click', function() {
|
|
7699
|
+
// Don't need to refresh table..
|
|
7700
|
+
data.body.splice( r, 1 );
|
|
7701
|
+
row.remove();
|
|
7702
|
+
});
|
|
7703
|
+
}
|
|
7704
|
+
else if( action == "menu" )
|
|
7705
|
+
{
|
|
7706
|
+
button.className += " fa-solid fa-ellipsis";
|
|
7707
|
+
button.addEventListener( 'click', function( event ) {
|
|
7708
|
+
addContextMenu( null, event, c => {
|
|
7709
|
+
if( options.onMenuAction )
|
|
7710
|
+
{
|
|
7711
|
+
options.onMenuAction( c );
|
|
7712
|
+
return;
|
|
7713
|
+
}
|
|
7714
|
+
console.warn( "Using <Menu action> without action callbacks." );
|
|
7715
|
+
} );
|
|
7716
|
+
});
|
|
7717
|
+
}
|
|
7718
|
+
else // custom actions
|
|
7719
|
+
{
|
|
7720
|
+
console.assert( action.constructor == Object );
|
|
7721
|
+
button.className += ` ${ action.icon }`;
|
|
7722
|
+
}
|
|
7723
|
+
|
|
7724
|
+
buttons.appendChild( button );
|
|
7725
|
+
}
|
|
7726
|
+
|
|
7727
|
+
row.appendChild( td );
|
|
7728
|
+
}
|
|
7729
|
+
|
|
7730
|
+
body.appendChild( row );
|
|
7731
|
+
}
|
|
7732
|
+
}
|
|
7733
|
+
}
|
|
7734
|
+
|
|
7735
|
+
widget.refreshTable();
|
|
7736
|
+
|
|
7737
|
+
if( !widget.name )
|
|
7738
|
+
{
|
|
7739
|
+
element.className += " noname";
|
|
7740
|
+
container.style.width = "100%";
|
|
7741
|
+
}
|
|
7742
|
+
|
|
7743
|
+
element.appendChild( container );
|
|
7744
|
+
|
|
7745
|
+
return widget;
|
|
7746
|
+
}
|
|
7148
7747
|
}
|
|
7149
7748
|
|
|
7150
7749
|
LX.Panel = Panel;
|
|
@@ -7501,16 +8100,15 @@ class Dialog {
|
|
|
7501
8100
|
draggable = options.draggable ?? true,
|
|
7502
8101
|
modal = options.modal ?? false;
|
|
7503
8102
|
|
|
7504
|
-
|
|
7505
|
-
{
|
|
7506
|
-
LX.modal.toggle( false );
|
|
7507
|
-
}
|
|
7508
|
-
|
|
7509
|
-
var root = document.createElement('div');
|
|
8103
|
+
var root = document.createElement('dialog');
|
|
7510
8104
|
root.className = "lexdialog " + (options.class ?? "");
|
|
7511
8105
|
root.id = options.id ?? "dialog" + Dialog._last_id++;
|
|
7512
8106
|
LX.root.appendChild( root );
|
|
7513
8107
|
|
|
8108
|
+
doAsync( () => {
|
|
8109
|
+
modal ? root.showModal() : root.show();
|
|
8110
|
+
}, 10 );
|
|
8111
|
+
|
|
7514
8112
|
let that = this;
|
|
7515
8113
|
|
|
7516
8114
|
var titleDiv = document.createElement('div');
|
|
@@ -7592,18 +8190,17 @@ class Dialog {
|
|
|
7592
8190
|
|
|
7593
8191
|
if( !options.onclose )
|
|
7594
8192
|
{
|
|
7595
|
-
|
|
7596
|
-
|
|
8193
|
+
root.close();
|
|
8194
|
+
|
|
8195
|
+
doAsync( () => {
|
|
8196
|
+
that.panel.clear();
|
|
8197
|
+
root.remove();
|
|
8198
|
+
}, 150 );
|
|
7597
8199
|
}
|
|
7598
8200
|
else
|
|
7599
8201
|
{
|
|
7600
8202
|
options.onclose( this.root );
|
|
7601
8203
|
}
|
|
7602
|
-
|
|
7603
|
-
if( modal )
|
|
7604
|
-
{
|
|
7605
|
-
LX.modal.toggle( true );
|
|
7606
|
-
}
|
|
7607
8204
|
};
|
|
7608
8205
|
|
|
7609
8206
|
var closeButton = document.createElement( 'a' );
|
|
@@ -7718,21 +8315,29 @@ class PocketDialog extends Dialog {
|
|
|
7718
8315
|
options.draggable = options.draggable ?? false;
|
|
7719
8316
|
options.closable = options.closable ?? false;
|
|
7720
8317
|
|
|
8318
|
+
const dragMargin = 3;
|
|
8319
|
+
|
|
7721
8320
|
super( title, callback, options );
|
|
7722
8321
|
|
|
7723
8322
|
let that = this;
|
|
7724
8323
|
// Update margins on branch title closes/opens
|
|
7725
8324
|
LX.addSignal("@on_branch_closed", this.panel, closed => {
|
|
7726
8325
|
if( this.dock_pos == PocketDialog.BOTTOM )
|
|
7727
|
-
|
|
8326
|
+
{
|
|
8327
|
+
this.root.style.top = "calc(100% - " + (this.root.offsetHeight + dragMargin) + "px)";
|
|
8328
|
+
}
|
|
7728
8329
|
});
|
|
7729
8330
|
|
|
7730
8331
|
// Custom
|
|
7731
8332
|
this.root.classList.add( "pocket" );
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
8333
|
+
this.root.style.left = "unset";
|
|
8334
|
+
|
|
8335
|
+
if( !options.position )
|
|
8336
|
+
{
|
|
8337
|
+
this.root.style.right = dragMargin + "px";
|
|
8338
|
+
this.root.style.top = dragMargin + "px";
|
|
7735
8339
|
}
|
|
8340
|
+
|
|
7736
8341
|
this.panel.root.style.width = "calc( 100% - 12px )";
|
|
7737
8342
|
this.panel.root.style.height = "calc( 100% - 40px )";
|
|
7738
8343
|
this.dock_pos = PocketDialog.TOP;
|
|
@@ -7753,7 +8358,7 @@ class PocketDialog extends Dialog {
|
|
|
7753
8358
|
|
|
7754
8359
|
if( this.dock_pos == PocketDialog.BOTTOM )
|
|
7755
8360
|
that.root.style.top = this.root.classList.contains("minimized") ?
|
|
7756
|
-
"calc(100% - " + (that.title.offsetHeight + 6) + "px)" : "calc(100% - " + (that.root.offsetHeight +
|
|
8361
|
+
"calc(100% - " + (that.title.offsetHeight + 6) + "px)" : "calc(100% - " + (that.root.offsetHeight + dragMargin) + "px)";
|
|
7757
8362
|
});
|
|
7758
8363
|
|
|
7759
8364
|
if( !options.draggable )
|
|
@@ -7768,26 +8373,42 @@ class PocketDialog extends Dialog {
|
|
|
7768
8373
|
switch( t )
|
|
7769
8374
|
{
|
|
7770
8375
|
case 'b':
|
|
7771
|
-
this.root.style.top = "calc(100% - " + (this.root.offsetHeight +
|
|
8376
|
+
this.root.style.top = "calc(100% - " + (this.root.offsetHeight + dragMargin) + "px)";
|
|
7772
8377
|
break;
|
|
7773
8378
|
case 'l':
|
|
7774
|
-
this.root.style.
|
|
8379
|
+
this.root.style.right = "unset";
|
|
8380
|
+
this.root.style.left = options.position ? options.position[ 1 ] : ( dragMargin + "px" );
|
|
7775
8381
|
break;
|
|
7776
8382
|
}
|
|
7777
8383
|
}
|
|
7778
8384
|
}
|
|
7779
8385
|
|
|
7780
8386
|
this.root.classList.add('dockable');
|
|
7781
|
-
|
|
7782
|
-
|
|
8387
|
+
|
|
8388
|
+
this.title.addEventListener("keydown", function( e ) {
|
|
8389
|
+
if( !e.ctrlKey )
|
|
8390
|
+
{
|
|
8391
|
+
return;
|
|
8392
|
+
}
|
|
8393
|
+
|
|
8394
|
+
that.root.style.right = "unset";
|
|
8395
|
+
|
|
8396
|
+
if( e.key == 'ArrowLeft' )
|
|
8397
|
+
{
|
|
7783
8398
|
that.root.style.left = '0px';
|
|
7784
|
-
}
|
|
7785
|
-
|
|
7786
|
-
|
|
8399
|
+
}
|
|
8400
|
+
else if( e.key == 'ArrowRight' )
|
|
8401
|
+
{
|
|
8402
|
+
that.root.style.left = "calc(100% - " + (that.root.offsetWidth + dragMargin) + "px)";
|
|
8403
|
+
}
|
|
8404
|
+
else if( e.key == 'ArrowUp' )
|
|
8405
|
+
{
|
|
7787
8406
|
that.root.style.top = "0px";
|
|
7788
8407
|
that.dock_pos = PocketDialog.TOP;
|
|
7789
|
-
}
|
|
7790
|
-
|
|
8408
|
+
}
|
|
8409
|
+
else if( e.key == 'ArrowDown' )
|
|
8410
|
+
{
|
|
8411
|
+
that.root.style.top = "calc(100% - " + (that.root.offsetHeight + dragMargin) + "px)";
|
|
7791
8412
|
that.dock_pos = PocketDialog.BOTTOM;
|
|
7792
8413
|
}
|
|
7793
8414
|
});
|
|
@@ -9086,7 +9707,7 @@ class AssetView {
|
|
|
9086
9707
|
}
|
|
9087
9708
|
|
|
9088
9709
|
this.rightPanel.sameLine();
|
|
9089
|
-
this.rightPanel.addDropdown( "Filter", this.allowedTypes, this.allowedTypes[ 0 ], v => this._refreshContent.call(this, null, v), { width: "
|
|
9710
|
+
this.rightPanel.addDropdown( "Filter", this.allowedTypes, this.allowedTypes[ 0 ], v => this._refreshContent.call(this, null, v), { width: "30%", minWidth: "128px" } );
|
|
9090
9711
|
this.rightPanel.addText( null, this.searchValue ?? "", v => this._refreshContent.call(this, v, null), { placeholder: "Search assets.." } );
|
|
9091
9712
|
this.rightPanel.addButton( null, "<a class='fa fa-arrow-up-short-wide'></a>", on_sort.bind(this), { className: "micro", title: "Sort" } );
|
|
9092
9713
|
this.rightPanel.addButton( null, "<a class='fa-solid fa-grip'></a>", on_change_view.bind(this), { className: "micro", title: "View" } );
|
|
@@ -9864,6 +10485,14 @@ Element.prototype.getComputedSize = function() {
|
|
|
9864
10485
|
}
|
|
9865
10486
|
}
|
|
9866
10487
|
|
|
10488
|
+
Element.prototype.getParentArea = function() {
|
|
10489
|
+
let parent = this.parentElement;
|
|
10490
|
+
while( parent ) {
|
|
10491
|
+
if( parent.classList.contains( "lexarea" ) ) { return parent; }
|
|
10492
|
+
parent = parent.parentElement;
|
|
10493
|
+
}
|
|
10494
|
+
}
|
|
10495
|
+
|
|
9867
10496
|
LX.UTILS = {
|
|
9868
10497
|
getTime() { return new Date().getTime() },
|
|
9869
10498
|
compareThreshold( v, p, n, t ) { return Math.abs(v - p) >= t || Math.abs(v - n) >= t },
|