lexgui 0.7.4 → 0.7.6
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/extensions/codeeditor.js +119 -27
- package/build/extensions/videoeditor.js +12 -1
- package/build/lexgui.css +24 -11
- package/build/lexgui.js +72 -23
- package/build/lexgui.min.css +1 -1
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +72 -23
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +30 -1
- package/examples/code-editor.html +4 -0
- package/package.json +1 -1
|
@@ -319,6 +319,25 @@ class CodeEditor {
|
|
|
319
319
|
|
|
320
320
|
constructor( area, options = {} ) {
|
|
321
321
|
|
|
322
|
+
if( options.filesAsync )
|
|
323
|
+
{
|
|
324
|
+
options.files = [ ...options.filesAsync ];
|
|
325
|
+
|
|
326
|
+
return (async () => {
|
|
327
|
+
await this._init( area, options );
|
|
328
|
+
// Constructors return `this` implicitly, but this is an IIFE, so
|
|
329
|
+
// return `this` explicitly (else we'd return an empty object).
|
|
330
|
+
return this;
|
|
331
|
+
})();
|
|
332
|
+
}
|
|
333
|
+
else
|
|
334
|
+
{
|
|
335
|
+
this._init( area, options );
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async _init( area, options ) {
|
|
340
|
+
|
|
322
341
|
window.editor = this;
|
|
323
342
|
|
|
324
343
|
CodeEditor.__instances.push( this );
|
|
@@ -338,7 +357,7 @@ class CodeEditor {
|
|
|
338
357
|
|
|
339
358
|
let panel = new LX.Panel();
|
|
340
359
|
|
|
341
|
-
panel.addTitle( "EXPLORER" );
|
|
360
|
+
panel.addTitle( options.explorerName ?? "EXPLORER" );
|
|
342
361
|
|
|
343
362
|
let sceneData = {
|
|
344
363
|
'id': 'WORKSPACE',
|
|
@@ -403,11 +422,11 @@ class CodeEditor {
|
|
|
403
422
|
|
|
404
423
|
if( !this.disableEdition )
|
|
405
424
|
{
|
|
406
|
-
this.tabs.root.addEventListener( 'dblclick', (e) => {
|
|
425
|
+
this.tabs.root.parentElement.addEventListener( 'dblclick', (e) => {
|
|
407
426
|
if( options.allowAddScripts ?? true )
|
|
408
427
|
{
|
|
409
428
|
e.preventDefault();
|
|
410
|
-
this.
|
|
429
|
+
this._onCreateNewFile();
|
|
411
430
|
}
|
|
412
431
|
} );
|
|
413
432
|
}
|
|
@@ -1232,6 +1251,7 @@ class CodeEditor {
|
|
|
1232
1251
|
const s = getComputedStyle( r );
|
|
1233
1252
|
this.fontSize = parseInt( s.getPropertyValue( "--code-editor-font-size" ) );
|
|
1234
1253
|
this.charWidth = this._measureChar( "a", true );
|
|
1254
|
+
this.processLines();
|
|
1235
1255
|
}
|
|
1236
1256
|
|
|
1237
1257
|
LX.emit( "@font-size", this.fontSize );
|
|
@@ -1257,18 +1277,22 @@ class CodeEditor {
|
|
|
1257
1277
|
|
|
1258
1278
|
if( options.allowAddScripts ?? true )
|
|
1259
1279
|
{
|
|
1260
|
-
this.
|
|
1280
|
+
this.onCreateFile = options.onCreateFile;
|
|
1281
|
+
this.addTab( "+", false, "Create file" );
|
|
1261
1282
|
}
|
|
1262
1283
|
|
|
1263
1284
|
if( options.files )
|
|
1264
1285
|
{
|
|
1265
1286
|
console.assert( options.files.constructor === Array, "_files_ must be an Array!" );
|
|
1266
1287
|
const numFiles = options.files.length;
|
|
1288
|
+
const loadAsync = ( options.filesAsync !== undefined );
|
|
1267
1289
|
let filesLoaded = 0;
|
|
1268
|
-
|
|
1269
1290
|
for( let url of options.files )
|
|
1270
1291
|
{
|
|
1271
|
-
|
|
1292
|
+
const finalUrl = url.constructor === Array ? url[ 0 ] : url;
|
|
1293
|
+
const finalFileName = url.constructor === Array ? url[ 1 ] : url;
|
|
1294
|
+
|
|
1295
|
+
await this.loadFile( finalUrl, { filename: finalFileName, async: loadAsync, callback: ( name, text ) => {
|
|
1272
1296
|
filesLoaded++;
|
|
1273
1297
|
if( filesLoaded == numFiles )
|
|
1274
1298
|
{
|
|
@@ -1276,7 +1300,7 @@ class CodeEditor {
|
|
|
1276
1300
|
|
|
1277
1301
|
if( options.onFilesLoaded )
|
|
1278
1302
|
{
|
|
1279
|
-
options.onFilesLoaded( this, numFiles );
|
|
1303
|
+
options.onFilesLoaded( this.loadedTabs, numFiles );
|
|
1280
1304
|
}
|
|
1281
1305
|
}
|
|
1282
1306
|
}});
|
|
@@ -1418,15 +1442,12 @@ class CodeEditor {
|
|
|
1418
1442
|
const _innerAddTab = ( text, name, title ) => {
|
|
1419
1443
|
|
|
1420
1444
|
// Remove Carriage Return in some cases and sub tabs using spaces
|
|
1421
|
-
text = text.replaceAll( '\r', '' );
|
|
1422
|
-
text = text.replaceAll( /\t|\\t/g, ' '.repeat( this.tabSpaces ) );
|
|
1445
|
+
text = text.replaceAll( '\r', '' ).replaceAll( /\t|\\t/g, ' '.repeat( this.tabSpaces ) );
|
|
1423
1446
|
|
|
1424
1447
|
// Set current text and language
|
|
1425
|
-
|
|
1426
1448
|
const lines = text.split( '\n' );
|
|
1427
1449
|
|
|
1428
1450
|
// Add item in the explorer if used
|
|
1429
|
-
|
|
1430
1451
|
if( this.useFileExplorer || this.skipTabs )
|
|
1431
1452
|
{
|
|
1432
1453
|
this._tabStorage[ name ] = {
|
|
@@ -1462,13 +1483,20 @@ class CodeEditor {
|
|
|
1462
1483
|
|
|
1463
1484
|
if( file.constructor == String )
|
|
1464
1485
|
{
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
LX.request({ url: filename, success: text => {
|
|
1468
|
-
const name = filename.substring(filename.lastIndexOf( '/' ) + 1);
|
|
1469
|
-
_innerAddTab( text, name, filename );
|
|
1470
|
-
} });
|
|
1486
|
+
const filename = file;
|
|
1487
|
+
const name = options.filename ?? filename.substring(filename.lastIndexOf( '/' ) + 1);
|
|
1471
1488
|
|
|
1489
|
+
if( options.async ?? false )
|
|
1490
|
+
{
|
|
1491
|
+
const text = await this._requestFileAsync( filename, "text" );
|
|
1492
|
+
_innerAddTab( text, name, options.filename ?? filename );
|
|
1493
|
+
}
|
|
1494
|
+
else
|
|
1495
|
+
{
|
|
1496
|
+
LX.request({ url: filename, success: text => {
|
|
1497
|
+
_innerAddTab( text, name, options.filename ?? filename );
|
|
1498
|
+
} });
|
|
1499
|
+
}
|
|
1472
1500
|
}
|
|
1473
1501
|
else // File Blob
|
|
1474
1502
|
{
|
|
@@ -1828,10 +1856,23 @@ class CodeEditor {
|
|
|
1828
1856
|
|
|
1829
1857
|
this.processFocus( false );
|
|
1830
1858
|
|
|
1831
|
-
LX.
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
});
|
|
1859
|
+
new LX.DropdownMenu( e.target, [
|
|
1860
|
+
{ name: "Create file", icon: "FilePlus", callback: this._onCreateNewFile.bind( this ) },
|
|
1861
|
+
{ name: "Load file", icon: "FileUp", callback: this.loadTabFromFile.bind( this ) },
|
|
1862
|
+
], { side: "bottom", align: "start" });
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
_onCreateNewFile() {
|
|
1866
|
+
|
|
1867
|
+
let options = {};
|
|
1868
|
+
|
|
1869
|
+
if( this.onCreateFile )
|
|
1870
|
+
{
|
|
1871
|
+
options = this.onCreateFile( this );
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
const name = options.name ?? "unnamed.js";
|
|
1875
|
+
this.addTab( name, true, name, { language: options.language ?? "JavaScript" } );
|
|
1835
1876
|
}
|
|
1836
1877
|
|
|
1837
1878
|
_onSelectTab( isNewTabButton, event, name ) {
|
|
@@ -1868,18 +1909,38 @@ class CodeEditor {
|
|
|
1868
1909
|
{
|
|
1869
1910
|
this._changeLanguageFromExtension( LX.getExtension( name ) );
|
|
1870
1911
|
}
|
|
1912
|
+
|
|
1913
|
+
this.processLines();
|
|
1871
1914
|
}
|
|
1872
1915
|
|
|
1873
1916
|
_onContextMenuTab( isNewTabButton, event, name, ) {
|
|
1874
1917
|
|
|
1875
1918
|
if( isNewTabButton )
|
|
1919
|
+
{
|
|
1876
1920
|
return;
|
|
1921
|
+
}
|
|
1877
1922
|
|
|
1878
|
-
LX.
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1923
|
+
new LX.DropdownMenu( event.target, [
|
|
1924
|
+
{ name: "Close", kbd: "MWB", callback: () => { this.tabs.delete( name ) } },
|
|
1925
|
+
{ name: "Close Others", callback: () => {
|
|
1926
|
+
for( const [ key, data ] of Object.entries( this.tabs.tabs ) )
|
|
1927
|
+
{
|
|
1928
|
+
if( key === '+' || key === name ) continue;
|
|
1929
|
+
this.tabs.delete( key )
|
|
1930
|
+
}
|
|
1931
|
+
} },
|
|
1932
|
+
{ name: "Close All", callback: () => {
|
|
1933
|
+
for( const [ key, data ] of Object.entries( this.tabs.tabs ) )
|
|
1934
|
+
{
|
|
1935
|
+
if( key === '+' ) continue;
|
|
1936
|
+
this.tabs.delete( key )
|
|
1937
|
+
}
|
|
1938
|
+
} },
|
|
1939
|
+
null,
|
|
1940
|
+
{ name: "Copy Path", icon: "Copy", callback: () => {
|
|
1941
|
+
navigator.clipboard.writeText( this.openedTabs[ name ].path ?? "" );
|
|
1942
|
+
} }
|
|
1943
|
+
], { side: "bottom", align: "start", event });
|
|
1883
1944
|
}
|
|
1884
1945
|
|
|
1885
1946
|
addTab( name, selected, title, options = {} ) {
|
|
@@ -1900,6 +1961,7 @@ class CodeEditor {
|
|
|
1900
1961
|
// Create code content
|
|
1901
1962
|
let code = document.createElement( 'div' );
|
|
1902
1963
|
Object.assign( code, {
|
|
1964
|
+
path: options.path ?? "",
|
|
1903
1965
|
className: 'code',
|
|
1904
1966
|
lines: [ "" ],
|
|
1905
1967
|
language: options.language ?? "Plain Text",
|
|
@@ -2132,7 +2194,7 @@ class CodeEditor {
|
|
|
2132
2194
|
document.body.appendChild( input );
|
|
2133
2195
|
input.click();
|
|
2134
2196
|
input.addEventListener('change', e => {
|
|
2135
|
-
if
|
|
2197
|
+
if( e.target.files[ 0 ] )
|
|
2136
2198
|
{
|
|
2137
2199
|
this.loadFile( e.target.files[ 0 ] );
|
|
2138
2200
|
}
|
|
@@ -5565,6 +5627,36 @@ s
|
|
|
5565
5627
|
delete this._lastResult;
|
|
5566
5628
|
delete this._scopeStack;
|
|
5567
5629
|
}
|
|
5630
|
+
|
|
5631
|
+
async _requestFileAsync( url, dataType, nocache ) {
|
|
5632
|
+
return new Promise( (resolve, reject) => {
|
|
5633
|
+
dataType = dataType ?? "arraybuffer";
|
|
5634
|
+
const mimeType = dataType === "arraybuffer" ? "application/octet-stream" : undefined;
|
|
5635
|
+
var xhr = new XMLHttpRequest();
|
|
5636
|
+
xhr.open( 'GET', url, true );
|
|
5637
|
+
xhr.responseType = dataType;
|
|
5638
|
+
if( mimeType )
|
|
5639
|
+
xhr.overrideMimeType( mimeType );
|
|
5640
|
+
if( nocache )
|
|
5641
|
+
xhr.setRequestHeader('Cache-Control', 'no-cache');
|
|
5642
|
+
xhr.onload = function(load)
|
|
5643
|
+
{
|
|
5644
|
+
var response = this.response;
|
|
5645
|
+
if( this.status != 200)
|
|
5646
|
+
{
|
|
5647
|
+
var err = "Error " + this.status;
|
|
5648
|
+
reject(err);
|
|
5649
|
+
return;
|
|
5650
|
+
}
|
|
5651
|
+
resolve( response );
|
|
5652
|
+
};
|
|
5653
|
+
xhr.onerror = function(err) {
|
|
5654
|
+
reject(err);
|
|
5655
|
+
};
|
|
5656
|
+
xhr.send();
|
|
5657
|
+
return xhr;
|
|
5658
|
+
});
|
|
5659
|
+
}
|
|
5568
5660
|
}
|
|
5569
5661
|
|
|
5570
5662
|
CodeEditor.languages = {
|
|
@@ -530,7 +530,10 @@ class VideoEditor {
|
|
|
530
530
|
this.isResizing = true;
|
|
531
531
|
}
|
|
532
532
|
});
|
|
533
|
-
});
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
this.onChangeStart = null;
|
|
536
|
+
this.onChangeEnd = null;
|
|
534
537
|
}
|
|
535
538
|
|
|
536
539
|
resizeCropArea(event) {
|
|
@@ -714,6 +717,10 @@ class VideoEditor {
|
|
|
714
717
|
if(this.onSetTime) {
|
|
715
718
|
this.onSetTime(t);
|
|
716
719
|
}
|
|
720
|
+
|
|
721
|
+
if(this.onChangeStart) {
|
|
722
|
+
this.onChangeStart(t);
|
|
723
|
+
}
|
|
717
724
|
}
|
|
718
725
|
|
|
719
726
|
_setEndValue ( x ) {
|
|
@@ -733,6 +740,10 @@ class VideoEditor {
|
|
|
733
740
|
if(this.onSetTime) {
|
|
734
741
|
this.onSetTime(t);
|
|
735
742
|
}
|
|
743
|
+
|
|
744
|
+
if(this.onChangeEnd) {
|
|
745
|
+
this.onChangeEnd(t);
|
|
746
|
+
}
|
|
736
747
|
}
|
|
737
748
|
|
|
738
749
|
getStartTime ( ) {
|
package/build/lexgui.css
CHANGED
|
@@ -4933,33 +4933,37 @@ ul.lexassetscontent {
|
|
|
4933
4933
|
.codebasearea .lexareatabs {
|
|
4934
4934
|
padding: 0px;
|
|
4935
4935
|
margin: 0px;
|
|
4936
|
+
gap: 0;
|
|
4937
|
+
background-color: var(--global-color-primary);
|
|
4936
4938
|
}
|
|
4937
4939
|
|
|
4938
4940
|
.codebasearea .lexareatab {
|
|
4939
4941
|
padding: 5px;
|
|
4940
4942
|
border-radius: 0px !important;
|
|
4941
4943
|
margin: 0px !important;
|
|
4942
|
-
border:
|
|
4943
|
-
|
|
4944
|
-
border-bottom:
|
|
4945
|
-
background-color: var(--global-color-primary) !important;
|
|
4944
|
+
border: none;
|
|
4945
|
+
background-color: var(--global-color-secondary) !important;
|
|
4946
|
+
border-bottom: 1px solid transparent;
|
|
4946
4947
|
transition: none;
|
|
4947
4948
|
display: flex !important;
|
|
4948
4949
|
}
|
|
4949
4950
|
|
|
4950
4951
|
.codebasearea .lexareatab:hover {
|
|
4951
|
-
background-color: var(--global-color-
|
|
4952
|
+
background-color: var(--global-color-tertiary) !important;
|
|
4953
|
+
}
|
|
4954
|
+
|
|
4955
|
+
.codebasearea .lexareatab:first-child {
|
|
4956
|
+
border-top-left-radius: 8px !important;
|
|
4952
4957
|
}
|
|
4953
4958
|
|
|
4954
4959
|
.codebasearea .lexareatab:last-child {
|
|
4955
|
-
border-right:
|
|
4960
|
+
border-top-right-radius: 8px !important;
|
|
4956
4961
|
}
|
|
4957
4962
|
|
|
4958
4963
|
.codebasearea .lexareatab.selected {
|
|
4959
|
-
background-color: var(--global-color-secondary) !important;
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
border-bottom: none;
|
|
4964
|
+
background-color: light-dark(var(--global-color-secondary), var(--global-medium-background)) !important;
|
|
4965
|
+
border-bottom: 1px solid var(--global-color-accent);
|
|
4966
|
+
color: var(--global-text-primary) !important;
|
|
4963
4967
|
}
|
|
4964
4968
|
|
|
4965
4969
|
.codebasearea .lexareatab i {
|
|
@@ -6105,6 +6109,10 @@ ul.lexassetscontent {
|
|
|
6105
6109
|
.items-center { place-items: center }
|
|
6106
6110
|
.items-end { place-items: end }
|
|
6107
6111
|
|
|
6112
|
+
.align-items-start { align-items: start }
|
|
6113
|
+
.align-items-center { align-items: center }
|
|
6114
|
+
.align-items-end { align-items: end }
|
|
6115
|
+
|
|
6108
6116
|
.justify-start { justify-content: start }
|
|
6109
6117
|
.justify-center { justify-content: center }
|
|
6110
6118
|
.justify-end { justify-content: end }
|
|
@@ -6316,6 +6324,8 @@ ul.lexassetscontent {
|
|
|
6316
6324
|
.font-bold { font-weight: 700 }
|
|
6317
6325
|
.font-extrabold { font-weight: 800 }
|
|
6318
6326
|
|
|
6327
|
+
.font-code { font-family: var(--global-code-font) }
|
|
6328
|
+
|
|
6319
6329
|
.tracking-tighter { letter-spacing: -0.05em }
|
|
6320
6330
|
.tracking-tight { letter-spacing: -0.025em }
|
|
6321
6331
|
.tracking-normal { letter-spacing: 0em }
|
|
@@ -6327,7 +6337,11 @@ ul.lexassetscontent {
|
|
|
6327
6337
|
|
|
6328
6338
|
.uppercase { text-transform: uppercase }
|
|
6329
6339
|
.capitalize { text-transform: capitalize }
|
|
6340
|
+
|
|
6330
6341
|
.decoration-none { text-decoration: none }
|
|
6342
|
+
.text-underline { text-decoration: underline }
|
|
6343
|
+
|
|
6344
|
+
.hover\:text-underline:hover { text-decoration: underline }
|
|
6331
6345
|
|
|
6332
6346
|
/* Width / Height */
|
|
6333
6347
|
|
|
@@ -6358,7 +6372,6 @@ ul.lexassetscontent {
|
|
|
6358
6372
|
.w-16 { width: 4rem } /* 64px */
|
|
6359
6373
|
.w-32 { width: 8rem } /* 128px */
|
|
6360
6374
|
|
|
6361
|
-
|
|
6362
6375
|
.h-full { height: 100% }
|
|
6363
6376
|
.h-screen { height: 100vh }
|
|
6364
6377
|
.h-auto { height: auto }
|
package/build/lexgui.js
CHANGED
|
@@ -14,7 +14,7 @@ console.warn( 'Script _build/lexgui.js_ is depracated and will be removed soon.
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
const LX = {
|
|
17
|
-
version: "0.7.
|
|
17
|
+
version: "0.7.6",
|
|
18
18
|
ready: false,
|
|
19
19
|
extensions: [], // Store extensions used
|
|
20
20
|
signals: {}, // Events and triggers
|
|
@@ -543,16 +543,30 @@ async function init( options = { } )
|
|
|
543
543
|
this.main_area = new LX.Area( { id: options.id ?? 'mainarea' } );
|
|
544
544
|
}
|
|
545
545
|
|
|
546
|
-
|
|
546
|
+
// Initial or automatic changes don't force color scheme
|
|
547
|
+
// to be stored in localStorage
|
|
548
|
+
|
|
549
|
+
this._onChangeSystemTheme = function( event ) {
|
|
550
|
+
const storedcolorScheme = localStorage.getItem( "lxColorScheme" );
|
|
551
|
+
if( storedcolorScheme ) return;
|
|
552
|
+
LX.setTheme( event.matches ? "dark" : "light", false );
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
this._mqlPrefersDarkScheme = window.matchMedia ? window.matchMedia("(prefers-color-scheme: dark)") : null;
|
|
556
|
+
|
|
557
|
+
const storedcolorScheme = localStorage.getItem( "lxColorScheme" );
|
|
558
|
+
if( storedcolorScheme )
|
|
547
559
|
{
|
|
548
|
-
|
|
560
|
+
LX.setTheme( storedcolorScheme );
|
|
561
|
+
}
|
|
562
|
+
else if( this._mqlPrefersDarkScheme && ( options.autoTheme ?? true ) )
|
|
563
|
+
{
|
|
564
|
+
if( window.matchMedia( "(prefers-color-scheme: light)" ).matches )
|
|
549
565
|
{
|
|
550
|
-
LX.setTheme( "light" );
|
|
566
|
+
LX.setTheme( "light", false );
|
|
551
567
|
}
|
|
552
568
|
|
|
553
|
-
|
|
554
|
-
LX.setTheme( event.matches ? "dark" : "light" );
|
|
555
|
-
});
|
|
569
|
+
this._mqlPrefersDarkScheme.addEventListener( "change", this._onChangeSystemTheme );
|
|
556
570
|
}
|
|
557
571
|
|
|
558
572
|
return this.main_area;
|
|
@@ -1099,6 +1113,7 @@ class DropdownMenu {
|
|
|
1099
1113
|
this.alignOffset = options.alignOffset ?? 0;
|
|
1100
1114
|
this.avoidCollisions = options.avoidCollisions ?? true;
|
|
1101
1115
|
this.onBlur = options.onBlur;
|
|
1116
|
+
this.event = options.event;
|
|
1102
1117
|
this.inPlace = false;
|
|
1103
1118
|
|
|
1104
1119
|
this.root = document.createElement( "div" );
|
|
@@ -1389,11 +1404,11 @@ class DropdownMenu {
|
|
|
1389
1404
|
_adjustPosition() {
|
|
1390
1405
|
|
|
1391
1406
|
const position = [ 0, 0 ];
|
|
1407
|
+
const rect = this._trigger.getBoundingClientRect();
|
|
1392
1408
|
|
|
1393
1409
|
// Place menu using trigger position and user options
|
|
1410
|
+
if( !this.event )
|
|
1394
1411
|
{
|
|
1395
|
-
const rect = this._trigger.getBoundingClientRect();
|
|
1396
|
-
|
|
1397
1412
|
let alignWidth = true;
|
|
1398
1413
|
|
|
1399
1414
|
switch( this.side )
|
|
@@ -1435,11 +1450,11 @@ class DropdownMenu {
|
|
|
1435
1450
|
if( alignWidth ) { position[ 0 ] += this.alignOffset; }
|
|
1436
1451
|
else { position[ 1 ] += this.alignOffset; }
|
|
1437
1452
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1453
|
+
// Offset position based on event
|
|
1454
|
+
else
|
|
1440
1455
|
{
|
|
1441
|
-
position[ 0 ] =
|
|
1442
|
-
position[ 1 ] =
|
|
1456
|
+
position[ 0 ] = this.event.x;
|
|
1457
|
+
position[ 1 ] = this.event.y;
|
|
1443
1458
|
}
|
|
1444
1459
|
|
|
1445
1460
|
if( this._parent instanceof HTMLDialogElement )
|
|
@@ -1449,6 +1464,12 @@ class DropdownMenu {
|
|
|
1449
1464
|
position[ 1 ] -= parentRect.y;
|
|
1450
1465
|
}
|
|
1451
1466
|
|
|
1467
|
+
if( this.avoidCollisions )
|
|
1468
|
+
{
|
|
1469
|
+
position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - this.root.offsetWidth - this._windowPadding );
|
|
1470
|
+
position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - this.root.offsetHeight - this._windowPadding );
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1452
1473
|
this.root.style.left = `${ position[ 0 ] }px`;
|
|
1453
1474
|
this.root.style.top = `${ position[ 1 ] }px`;
|
|
1454
1475
|
this.inPlace = true;
|
|
@@ -2545,8 +2566,9 @@ class Tabs {
|
|
|
2545
2566
|
});
|
|
2546
2567
|
|
|
2547
2568
|
// Attach content
|
|
2548
|
-
|
|
2549
|
-
this.root.
|
|
2569
|
+
const indexOffset = options.indexOffset ?? -1;
|
|
2570
|
+
tabEl.childIndex = ( this.root.childElementCount + indexOffset );
|
|
2571
|
+
this.root.insertChildAtIndex( tabEl, tabEl.childIndex + 1 );
|
|
2550
2572
|
this.area.attach( contentEl );
|
|
2551
2573
|
this.tabDOMs[ name ] = tabEl;
|
|
2552
2574
|
this.tabs[ name ] = content;
|
|
@@ -4971,11 +4993,13 @@ LX.deepCopy = deepCopy;
|
|
|
4971
4993
|
* @method setTheme
|
|
4972
4994
|
* @description Set dark or light theme
|
|
4973
4995
|
* @param {String} colorScheme Name of the scheme
|
|
4996
|
+
* @param {Boolean} storeLocal Store in localStorage
|
|
4974
4997
|
*/
|
|
4975
|
-
function setTheme( colorScheme )
|
|
4998
|
+
function setTheme( colorScheme, storeLocal = true )
|
|
4976
4999
|
{
|
|
4977
5000
|
colorScheme = ( colorScheme == "light" ) ? "light" : "dark";
|
|
4978
5001
|
document.documentElement.setAttribute( "data-theme", colorScheme );
|
|
5002
|
+
if( storeLocal ) localStorage.setItem( "lxColorScheme", colorScheme );
|
|
4979
5003
|
LX.emit( "@on_new_color_scheme", colorScheme );
|
|
4980
5004
|
}
|
|
4981
5005
|
|
|
@@ -5004,6 +5028,26 @@ function switchTheme()
|
|
|
5004
5028
|
|
|
5005
5029
|
LX.switchTheme = switchTheme;
|
|
5006
5030
|
|
|
5031
|
+
/**
|
|
5032
|
+
* @method setSystemTheme
|
|
5033
|
+
* @description Sets back the system theme
|
|
5034
|
+
*/
|
|
5035
|
+
function setSystemTheme()
|
|
5036
|
+
{
|
|
5037
|
+
const currentTheme = ( window.matchMedia && window.matchMedia( "(prefers-color-scheme: light)" ).matches ) ? "light" : "dark";
|
|
5038
|
+
setTheme( currentTheme );
|
|
5039
|
+
localStorage.removeItem( "lxColorScheme" );
|
|
5040
|
+
|
|
5041
|
+
// Reapply listener
|
|
5042
|
+
if( this._mqlPrefersDarkScheme )
|
|
5043
|
+
{
|
|
5044
|
+
this._mqlPrefersDarkScheme.removeEventListener( "change", this._onChangeSystemTheme );
|
|
5045
|
+
this._mqlPrefersDarkScheme.addEventListener( "change", this._onChangeSystemTheme );
|
|
5046
|
+
}
|
|
5047
|
+
}
|
|
5048
|
+
|
|
5049
|
+
LX.setSystemTheme = setSystemTheme;
|
|
5050
|
+
|
|
5007
5051
|
/**
|
|
5008
5052
|
* @method setThemeColor
|
|
5009
5053
|
* @description Sets a new value for one of the main theme variables
|
|
@@ -8986,7 +9030,7 @@ class TextInput extends BaseComponent {
|
|
|
8986
9030
|
|
|
8987
9031
|
this.valid = ( v ) => {
|
|
8988
9032
|
v = v ?? this.value();
|
|
8989
|
-
if(
|
|
9033
|
+
if( ( wValue.pattern ?? "" ) == "" ) return true;
|
|
8990
9034
|
const regexp = new RegExp( wValue.pattern );
|
|
8991
9035
|
return regexp.test( v );
|
|
8992
9036
|
};
|
|
@@ -9725,19 +9769,21 @@ class Form extends BaseComponent {
|
|
|
9725
9769
|
|
|
9726
9770
|
const primaryButton = new LX.Button( null, options.primaryActionName ?? "Submit", ( value, event ) => {
|
|
9727
9771
|
|
|
9772
|
+
const errors = [];
|
|
9773
|
+
|
|
9728
9774
|
for( let entry in data )
|
|
9729
9775
|
{
|
|
9730
9776
|
let entryData = data[ entry ];
|
|
9731
9777
|
|
|
9732
9778
|
if( !entryData.textComponent.valid() )
|
|
9733
9779
|
{
|
|
9734
|
-
|
|
9780
|
+
errors.push( { type: "input_not_valid", entry } );
|
|
9735
9781
|
}
|
|
9736
9782
|
}
|
|
9737
9783
|
|
|
9738
9784
|
if( callback )
|
|
9739
9785
|
{
|
|
9740
|
-
callback( container.formData, event );
|
|
9786
|
+
callback( container.formData, errors, event );
|
|
9741
9787
|
}
|
|
9742
9788
|
}, { width: "100%", minWidth: "0", buttonClass: options.primaryButtonClass ?? "contrast" } );
|
|
9743
9789
|
|
|
@@ -15942,8 +15988,7 @@ class Sidebar {
|
|
|
15942
15988
|
return;
|
|
15943
15989
|
}
|
|
15944
15990
|
|
|
15945
|
-
|
|
15946
|
-
if( f ) f.call( this, key, item.value, e );
|
|
15991
|
+
let value = undefined;
|
|
15947
15992
|
|
|
15948
15993
|
if( isCollapsable )
|
|
15949
15994
|
{
|
|
@@ -15953,14 +15998,18 @@ class Sidebar {
|
|
|
15953
15998
|
{
|
|
15954
15999
|
item.value = !item.value;
|
|
15955
16000
|
item.checkbox.set( item.value, true );
|
|
16001
|
+
value = item.value;
|
|
15956
16002
|
}
|
|
15957
|
-
|
|
15958
|
-
if( options.swap && !( e.target instanceof HTMLInputElement ) )
|
|
16003
|
+
else if( options.swap && !( e.target instanceof HTMLInputElement ) )
|
|
15959
16004
|
{
|
|
15960
16005
|
const swapInput = itemDom.querySelector( "input" );
|
|
15961
16006
|
swapInput.checked = !swapInput.checked;
|
|
16007
|
+
value = swapInput.checked;
|
|
15962
16008
|
}
|
|
15963
16009
|
|
|
16010
|
+
const f = options.callback;
|
|
16011
|
+
if( f ) f.call( this, key, value ?? entry, e );
|
|
16012
|
+
|
|
15964
16013
|
// Manage selected
|
|
15965
16014
|
if( this.displaySelected && !options.skipSelection )
|
|
15966
16015
|
{
|