lexgui 8.2.0 → 8.2.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.
Files changed (43) hide show
  1. package/build/components/Avatar.d.ts +15 -15
  2. package/build/components/NodeTree.d.ts +51 -51
  3. package/build/components/Vector.d.ts +10 -10
  4. package/build/core/Event.d.ts +6 -6
  5. package/build/core/Namespace.js +1 -1
  6. package/build/core/Namespace.js.map +1 -1
  7. package/build/core/Panel.d.ts +538 -538
  8. package/build/extensions/AssetView.d.ts +137 -137
  9. package/build/extensions/AssetView.js +5 -6
  10. package/build/extensions/AssetView.js.map +1 -1
  11. package/build/extensions/CodeEditor.d.ts +358 -358
  12. package/build/extensions/CodeEditor.js +7 -7
  13. package/build/extensions/CodeEditor.js.map +1 -1
  14. package/build/extensions/DocMaker.js +1 -0
  15. package/build/extensions/DocMaker.js.map +1 -1
  16. package/build/extensions/GraphEditor.js +2754 -2754
  17. package/build/extensions/Timeline.d.ts +668 -668
  18. package/build/extensions/Timeline.js +2 -2
  19. package/build/extensions/Timeline.js.map +1 -1
  20. package/build/extensions/VideoEditor.d.ts +37 -15
  21. package/build/extensions/VideoEditor.js +287 -166
  22. package/build/extensions/VideoEditor.js.map +1 -1
  23. package/build/index.css.d.ts +3 -3
  24. package/build/index.d.ts +57 -57
  25. package/build/lexgui.all.js +327 -185
  26. package/build/lexgui.all.js.map +1 -1
  27. package/build/lexgui.all.min.js +1 -1
  28. package/build/lexgui.all.module.js +327 -185
  29. package/build/lexgui.all.module.js.map +1 -1
  30. package/build/lexgui.all.module.min.js +1 -1
  31. package/build/lexgui.css +213 -220
  32. package/build/lexgui.js +25 -4
  33. package/build/lexgui.js.map +1 -1
  34. package/build/lexgui.min.css +1 -1
  35. package/build/lexgui.min.js +1 -1
  36. package/build/lexgui.module.js +25 -4
  37. package/build/lexgui.module.js.map +1 -1
  38. package/build/lexgui.module.min.js +1 -1
  39. package/changelog.md +23 -1
  40. package/examples/all-components.html +3 -4
  41. package/examples/code-editor.html +11 -0
  42. package/examples/dialogs.html +13 -2
  43. package/package.json +1 -1
@@ -16,7 +16,7 @@
16
16
  exports.LX = g$2.LX;
17
17
  if (!exports.LX) {
18
18
  exports.LX = {
19
- version: '8.2',
19
+ version: '8.2.1',
20
20
  ready: false,
21
21
  extensions: [], // Store extensions used
22
22
  extraCommandbarEntries: [], // User specific entries for command bar
@@ -7619,14 +7619,14 @@
7619
7619
  if (options.id) {
7620
7620
  root.id = options.id;
7621
7621
  }
7622
- root.className = exports.LX.mergeClass('lexbranch w-full rounded-lg my-0 mx-auto', options.className);
7622
+ root.className = exports.LX.mergeClass('lexbranch bg-secondary/50 dark:bg-card text-secondary-foreground dark:text-card-foreground w-full rounded-lg my-0 mx-auto', options.className);
7623
7623
  var that = this;
7624
7624
  this.closed = options.closed ?? false;
7625
7625
  this.root = root;
7626
7626
  this.components = [];
7627
7627
  this.panel = null;
7628
7628
  // Create element
7629
- const title = exports.LX.makeElement('div', 'lexbranchtitle flex cursor-pointer select-none pad-lg bg-card text-card-foreground text-lg', '', root);
7629
+ const title = exports.LX.makeElement('div', 'lexbranchtitle flex cursor-pointer select-none pad-lg text-lg', '', root);
7630
7630
  if (options.icon) {
7631
7631
  const branchIcon = exports.LX.makeIcon(options.icon, { iconClass: 'mr-2' });
7632
7632
  title.appendChild(branchIcon);
@@ -7634,7 +7634,7 @@
7634
7634
  title.innerHTML += name || 'Branch';
7635
7635
  const collapseIcon = exports.LX.makeIcon('Right', { iconClass: 'switch-branch-button', svgClass: 'sm' });
7636
7636
  title.appendChild(collapseIcon);
7637
- var branchContent = exports.LX.makeElement('div', 'lexbranchcontent pad-xs bg-card', '', root);
7637
+ var branchContent = exports.LX.makeElement('div', 'lexbranchcontent pad-xs', '', root);
7638
7638
  branchContent.id = name.replace(/\s/g, '');
7639
7639
  this.content = branchContent;
7640
7640
  this._addBranchSeparator();
@@ -13443,6 +13443,27 @@
13443
13443
  }
13444
13444
  exports.LX.signals[name].push(obj);
13445
13445
  };
13446
+ /**
13447
+ * @method removeSignal
13448
+ * @param {String} name
13449
+ * @param {Object} targetObj
13450
+ */
13451
+ exports.LX.removeSignal = function (name, targetObj) {
13452
+ const data = exports.LX.signals[name];
13453
+ if (!data) {
13454
+ return;
13455
+ }
13456
+ if (!targetObj) {
13457
+ delete exports.LX.signals[name];
13458
+ return;
13459
+ }
13460
+ for (let i = 0; i < data.length; ++i) {
13461
+ if (data[i] == targetObj) {
13462
+ data.splice(i, 1);
13463
+ break;
13464
+ }
13465
+ }
13466
+ };
13446
13467
  /**
13447
13468
  * @method emitSignal
13448
13469
  * @param {String} name
@@ -14449,9 +14470,8 @@
14449
14470
  _subscribeTreeEvents(tree) {
14450
14471
  // If some of these events we don't have to call "resolve" since the AV itself
14451
14472
  // will update the data and refresh when necessary
14452
- tree.on("select", (event, resolve) => {
14453
- if (event.items.length > 1) // Do nothing if multiple selection
14454
- {
14473
+ tree.on('select', (event, resolve) => {
14474
+ if (event.items.length > 1) { // Do nothing if multiple selection
14455
14475
  return;
14456
14476
  }
14457
14477
  const node = event.items[0];
@@ -14475,7 +14495,7 @@
14475
14495
  this.selectedItem = node;
14476
14496
  }
14477
14497
  });
14478
- tree.on("beforeMove", (event, resolve) => {
14498
+ tree.on('beforeMove', (event, resolve) => {
14479
14499
  const onBeforeNodeDragged = this._callbacks['beforeNodeDragged'];
14480
14500
  const onNodeDragged = this._callbacks['nodeDragged'];
14481
14501
  const node = event.items[0];
@@ -14517,11 +14537,11 @@
14517
14537
  av_resolve();
14518
14538
  }
14519
14539
  });
14520
- tree.on("beforeDelete", (event, resolve) => {
14540
+ tree.on('beforeDelete', (event, resolve) => {
14521
14541
  const node = event.items[0];
14522
14542
  this._requestDeleteItem(node);
14523
14543
  });
14524
- tree.on("beforeRename", (event, resolve) => {
14544
+ tree.on('beforeRename', (event, resolve) => {
14525
14545
  const node = event.items[0];
14526
14546
  this._requestRenameItem(node, event.newName, true);
14527
14547
  });
@@ -16010,11 +16030,11 @@
16010
16030
  rename: false,
16011
16031
  skipDefaultIcon: true
16012
16032
  });
16013
- this.explorer.on("dblClick", (event) => {
16033
+ this.explorer.on('dblClick', (event) => {
16014
16034
  const node = event.items[0];
16015
16035
  this.loadTab(node.id);
16016
16036
  });
16017
- this.explorer.on("delete", (event) => {
16037
+ this.explorer.on('delete', (event) => {
16018
16038
  const node = event.items[0];
16019
16039
  this.closeTab(node.id);
16020
16040
  });
@@ -16830,12 +16850,12 @@
16830
16850
  this.codeArea.root.style.height = `calc(100% - ${this._fullVerticalOffset}px)`;
16831
16851
  // Process lines on finish computing final sizes
16832
16852
  this.processLines();
16853
+ this._preparedAt = performance.now();
16854
+ if (this.onReady) {
16855
+ this.onReady(this);
16856
+ }
16857
+ console.log(`[LX.CodeEditor] Ready! (font size: ${this.fontSize}px)`);
16833
16858
  }, 50);
16834
- if (this.onReady) {
16835
- this.onReady(this);
16836
- }
16837
- this._preparedAt = performance.now();
16838
- console.log(`[LX.CodeEditor] Ready! (font size: ${this.fontSize}px)`);
16839
16859
  }
16840
16860
  // Clear signals
16841
16861
  clear() {
@@ -20829,6 +20849,7 @@
20829
20849
  console.log('Copied!');
20830
20850
  }
20831
20851
  }
20852
+ exports.LX.DocMaker = DocMaker;
20832
20853
 
20833
20854
  // GraphEditor.ts @jxarco
20834
20855
  if (!exports.LX) {
@@ -24148,7 +24169,7 @@
24148
24169
  treeTracks = this.generateSelectedItemsTreeData();
24149
24170
  }
24150
24171
  this.trackTreesComponent = p.addTree(null, treeTracks, { filter: false, rename: false, draggable: false });
24151
- this.trackTreesComponent.on("select", (event, resolve) => {
24172
+ this.trackTreesComponent.on('select', (event, resolve) => {
24152
24173
  const node = event.items[0];
24153
24174
  if (!event.domEvent.shiftKey) {
24154
24175
  this.deselectAllTracks(false); // no need to update left panel
@@ -24158,7 +24179,7 @@
24158
24179
  this.setTrackSelection(node.trackData.trackIdx, flag, false, false); // do callback, do not update left panel
24159
24180
  }
24160
24181
  });
24161
- this.trackTreesComponent.on("visibleChanged", (event, resolve) => {
24182
+ this.trackTreesComponent.on('visibleChanged', (event, resolve) => {
24162
24183
  const node = event.items[0];
24163
24184
  if (node.trackData) {
24164
24185
  this.setTrackState(node.trackData.trackIdx, node.visible, false, false); // do not update left panel
@@ -27775,17 +27796,22 @@
27775
27796
  duration = 1.0;
27776
27797
  canvas;
27777
27798
  ctx;
27799
+ options;
27778
27800
  markerWidth = 8;
27779
27801
  markerHeight;
27780
27802
  offset;
27781
27803
  lineWidth;
27782
27804
  lineHeight;
27783
- position;
27805
+ linePosition;
27784
27806
  startX;
27785
27807
  endX;
27786
27808
  currentX;
27787
27809
  hovering;
27788
27810
  dragging;
27811
+ _onMouseUpListener;
27812
+ _onMouseMoveListener;
27813
+ _mouseDownCanvasRect = null;
27814
+ updateTheme;
27789
27815
  onChangeCurrent;
27790
27816
  onChangeStart;
27791
27817
  onChangeEnd;
@@ -27793,46 +27819,65 @@
27793
27819
  onMouse;
27794
27820
  constructor(area, type, options = {}) {
27795
27821
  this.type = type ?? TimeBar.TIMEBAR_PLAY;
27822
+ this.options = options ?? {};
27796
27823
  this.duration = options.duration ?? this.duration;
27797
27824
  // Create canvas
27798
27825
  this.canvas = document.createElement('canvas');
27826
+ this.canvas.style.borderRadius = '6px';
27799
27827
  this.canvas.width = area.size[0];
27800
27828
  this.canvas.height = area.size[1];
27801
27829
  area.attach(this.canvas);
27802
27830
  this.ctx = this.canvas.getContext('2d');
27803
27831
  this.markerWidth = options.markerWidth ?? this.markerWidth;
27804
- this.markerHeight = options.markerHeight ?? (this.canvas.height * 0.5);
27805
- this.offset = options.offset || (this.markerWidth * 0.5 + 5);
27832
+ this.markerHeight = (options.markerHeight ?? 0.5) * this.canvas.height;
27833
+ const defaultOffset = this.markerWidth * 0.5 + 5;
27834
+ if (typeof (options.offset) == 'number') {
27835
+ this.offset = new vec2(options.offset, options.offset);
27836
+ }
27837
+ else if (Array.isArray(options.offset)) {
27838
+ this.offset = new vec2(options.offset[0] ?? defaultOffset, options.offset[1] ?? defaultOffset);
27839
+ }
27840
+ else {
27841
+ this.offset = new vec2(defaultOffset, defaultOffset);
27842
+ }
27806
27843
  // dimensions of line (not canvas)
27807
- this.lineWidth = this.canvas.width - this.offset * 2;
27844
+ this.lineWidth = this.canvas.width - this.offset.x * 2;
27808
27845
  this.lineHeight = options.barHeight ?? 5;
27809
- this.position = new vec2(this.offset, this.canvas.height * 0.5 - this.lineHeight * 0.5);
27810
- this.startX = this.position.x;
27811
- this.endX = this.position.x + this.lineWidth;
27846
+ this.linePosition = new vec2(this.offset.x, this.canvas.height * 0.5 - this.lineHeight * 0.5);
27847
+ this.startX = this.linePosition.x;
27848
+ this.endX = this.linePosition.x + this.lineWidth;
27812
27849
  this.currentX = this.startX;
27813
27850
  this._draw();
27851
+ function updateTheme() {
27852
+ TimeBar.BACKGROUND_COLOR = exports.LX.getCSSVariable('secondary');
27853
+ TimeBar.COLOR = exports.LX.getCSSVariable('accent');
27854
+ TimeBar.ACTIVE_COLOR = exports.LX.getCSSVariable('color-blue-400');
27855
+ }
27856
+ this.updateTheme = updateTheme.bind(this);
27857
+ exports.LX.addSignal('@on_new_color_scheme', this.updateTheme);
27814
27858
  this.updateTheme();
27815
- exports.LX.addSignal('@on_new_color_scheme', () => {
27816
- // Retrieve again the color using LX.getCSSVariable, which checks the applied theme
27817
- this.updateTheme();
27818
- });
27859
+ // prepare event listeners' functions
27860
+ this._onMouseUpListener = this.onMouseUp.bind(this);
27861
+ this._onMouseMoveListener = this.onMouseMove.bind(this);
27819
27862
  this.canvas.onmousedown = (e) => this.onMouseDown(e);
27820
- this.canvas.onmousemove = (e) => this.onMouseMove(e);
27821
- this.canvas.onmouseup = (e) => this.onMouseUp(e);
27863
+ this.canvas.onmousemove = (e) => {
27864
+ if (this.dragging)
27865
+ return; // already handled by _onMouseMoveListener
27866
+ this.onMouseMove(e);
27867
+ };
27822
27868
  }
27823
- updateTheme() {
27824
- TimeBar.BACKGROUND_COLOR = exports.LX.getCSSVariable('secondary');
27825
- TimeBar.COLOR = exports.LX.getCSSVariable('accent');
27826
- TimeBar.ACTIVE_COLOR = exports.LX.getCSSVariable('color-blue-400');
27869
+ unbind() {
27870
+ removeEventListener('mousemove', this._onMouseMoveListener);
27871
+ removeEventListener('mouseup', this._onMouseUpListener);
27827
27872
  }
27828
27873
  setDuration(duration) {
27829
27874
  this.duration = duration;
27830
27875
  }
27831
27876
  xToTime(x) {
27832
- return ((x - this.offset) / (this.lineWidth)) * this.duration;
27877
+ return ((x - this.offset.x) / (this.lineWidth)) * this.duration;
27833
27878
  }
27834
27879
  timeToX(time) {
27835
- return (time / this.duration) * (this.lineWidth) + this.offset;
27880
+ return (time / this.duration) * (this.lineWidth) + this.offset.x;
27836
27881
  }
27837
27882
  setCurrentTime(time) {
27838
27883
  this.currentX = this.timeToX(time);
@@ -27877,10 +27922,10 @@
27877
27922
  ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
27878
27923
  // Draw background timeline
27879
27924
  ctx.fillStyle = TimeBar.COLOR;
27880
- ctx.fillRect(this.position.x, this.position.y, this.lineWidth, this.lineHeight);
27925
+ ctx.fillRect(this.linePosition.x, this.linePosition.y, this.lineWidth, this.lineHeight);
27881
27926
  // Draw background trimed timeline
27882
27927
  ctx.fillStyle = TimeBar.ACTIVE_COLOR;
27883
- ctx.fillRect(this.startX, this.position.y, this.endX - this.startX, this.lineHeight);
27928
+ ctx.fillRect(this.startX, this.linePosition.y, this.endX - this.startX, this.lineHeight);
27884
27929
  ctx.restore();
27885
27930
  // Min-Max time markers
27886
27931
  this._drawTrimMarker('start', this.startX, { color: null, fillColor: TimeBar.ACTIVE_COLOR || '#5f88c9' });
@@ -27917,9 +27962,9 @@
27917
27962
  ctx.shadowBlur = 0;
27918
27963
  }
27919
27964
  _drawTimeMarker(name, x, options = {}) {
27920
- let y = this.offset;
27965
+ let y = this.offset.y;
27921
27966
  const w = options.width ? options.width : (this.dragging == name ? 6 : 4);
27922
- const h = this.canvas.height - this.offset * 2;
27967
+ this.canvas.height - this.offset.y * 2;
27923
27968
  let ctx = this.ctx;
27924
27969
  if (!ctx)
27925
27970
  return;
@@ -27934,15 +27979,14 @@
27934
27979
  ctx.fillStyle = ctx.strokeStyle = 'white';
27935
27980
  ctx.beginPath();
27936
27981
  ctx.moveTo(x, y);
27937
- ctx.lineTo(x, y + h * 0.5);
27982
+ ctx.lineTo(x, this.linePosition.y + this.lineHeight * 0.5);
27938
27983
  ctx.stroke();
27939
27984
  ctx.closePath();
27940
27985
  ctx.fillStyle = ctx.strokeStyle = options.fillColor || '#111'; // "#FFF";
27941
- y -= this.offset + 8;
27942
27986
  // Current time ball grab
27943
27987
  ctx.fillStyle = options.fillColor || '#e5e5e5';
27944
27988
  ctx.beginPath();
27945
- ctx.roundRect(x - w * 0.5, y + this.offset, w, w, 5);
27989
+ ctx.roundRect(x - w * 0.5, y - w * 0.5, w, w, 5);
27946
27990
  ctx.fill();
27947
27991
  ctx.shadowBlur = 0;
27948
27992
  }
@@ -27964,13 +28008,11 @@
27964
28008
  const y = e.offsetY;
27965
28009
  // Check if some marker is clicked
27966
28010
  const threshold = this.markerWidth;
28011
+ const startDist = Math.abs(this.startX - x);
28012
+ const endDist = Math.abs(this.endX - x);
27967
28013
  // grab trim markers only from the bottom
27968
- if (Math.abs(this.startX - x) < threshold && this.position.y < y) {
27969
- this.dragging = 'start';
27970
- canvas.style.cursor = 'grabbing';
27971
- }
27972
- else if (Math.abs(this.endX - x) < threshold && this.position.y < y) {
27973
- this.dragging = 'end';
28014
+ if ((startDist < threshold || endDist < threshold) && this.linePosition.y < y) {
28015
+ this.dragging = (startDist < endDist || x < this.startX) ? 'start' : 'end';
27974
28016
  canvas.style.cursor = 'grabbing';
27975
28017
  }
27976
28018
  else {
@@ -27987,9 +28029,14 @@
27987
28029
  }
27988
28030
  this.onSetCurrentValue(this.currentX);
27989
28031
  }
28032
+ this._mouseDownCanvasRect = canvas.getBoundingClientRect(); // cache this to avoid stalls during mousemove
28033
+ window.addEventListener('mousemove', this._onMouseMoveListener);
28034
+ window.addEventListener('mouseup', this._onMouseUpListener);
27990
28035
  this._draw();
27991
28036
  }
27992
28037
  onMouseUp(e) {
28038
+ window.removeEventListener('mousemove', this._onMouseMoveListener);
28039
+ window.removeEventListener('mouseup', this._onMouseUpListener);
27993
28040
  if (this.onMouse) {
27994
28041
  this.onMouse(e);
27995
28042
  }
@@ -28012,17 +28059,17 @@
28012
28059
  e.preventDefault();
28013
28060
  const canvas = this.canvas;
28014
28061
  // Process mouse
28015
- const x = e.target == canvas ? e.offsetX : e.clientX - canvas.offsetLeft;
28016
- e.target == canvas ? e.offsetY : e.clientY - canvas.offsetTop;
28062
+ const x = e.target == canvas ? e.offsetX : (e.clientX - this._mouseDownCanvasRect.left);
28063
+ e.target == canvas ? e.offsetY : (e.clientY - this._mouseDownCanvasRect.top);
28017
28064
  if (this.dragging) {
28018
28065
  switch (this.dragging) {
28019
28066
  case 'start':
28020
- this.startX = Math.max(this.position.x, Math.min(this.endX, x));
28067
+ this.startX = Math.max(this.linePosition.x, Math.min(this.endX, x));
28021
28068
  this.currentX = this.startX;
28022
28069
  this.onSetStartValue(this.startX);
28023
28070
  break;
28024
28071
  case 'end':
28025
- this.endX = Math.max(this.startX, Math.min(this.position.x + this.lineWidth, x));
28072
+ this.endX = Math.max(this.startX, Math.min(this.linePosition.x + this.lineWidth, x));
28026
28073
  this.currentX = this.endX;
28027
28074
  this.onSetEndValue(this.endX);
28028
28075
  break;
@@ -28056,15 +28103,18 @@
28056
28103
  resize(size) {
28057
28104
  this.canvas.width = Math.max(0, size[0]);
28058
28105
  this.canvas.height = Math.max(0, size[1]);
28059
- let newWidth = size[0] - this.offset * 2;
28106
+ this.markerHeight = (this.options.markerHeight ?? 0.5) * this.canvas.height;
28107
+ let newWidth = size[0] - this.offset.x * 2;
28060
28108
  newWidth = newWidth < 0.00001 ? 0.00001 : newWidth; // actual width of the line = canvas.width - offsetleft - offsetRight
28061
- const startRatio = (this.startX - this.offset) / this.lineWidth;
28062
- const currentRatio = (this.currentX - this.offset) / this.lineWidth;
28063
- const endRatio = (this.endX - this.offset) / this.lineWidth;
28109
+ const startRatio = (this.startX - this.offset.x) / this.lineWidth;
28110
+ const currentRatio = (this.currentX - this.offset.x) / this.lineWidth;
28111
+ const endRatio = (this.endX - this.offset.x) / this.lineWidth;
28064
28112
  this.lineWidth = newWidth;
28065
- this.startX = Math.min(Math.max(newWidth * startRatio, 0), newWidth) + this.offset;
28066
- this.currentX = Math.min(Math.max(newWidth * currentRatio, 0), newWidth) + this.offset;
28067
- this.endX = Math.min(Math.max(newWidth * endRatio, 0), newWidth) + this.offset;
28113
+ this.linePosition.x = this.offset.x;
28114
+ this.linePosition.y = this.canvas.height * 0.5 - this.lineHeight * 0.5;
28115
+ this.startX = Math.min(Math.max(newWidth * startRatio, 0), newWidth) + this.offset.x;
28116
+ this.currentX = Math.min(Math.max(newWidth * currentRatio, 0), newWidth) + this.offset.x;
28117
+ this.endX = Math.min(Math.max(newWidth * endRatio, 0), newWidth) + this.offset.x;
28068
28118
  this._draw();
28069
28119
  }
28070
28120
  }
@@ -28085,10 +28135,7 @@
28085
28135
  playing = false;
28086
28136
  videoReady = false;
28087
28137
  controls = true;
28088
- startTimeString = '0:0';
28089
- endTimeString = '0:0';
28090
28138
  speed = 1.0;
28091
- currentTime = 0.0;
28092
28139
  startTime = 0.0;
28093
28140
  endTime = 0.0;
28094
28141
  requestId;
@@ -28099,27 +28146,29 @@
28099
28146
  crop = false;
28100
28147
  dragOffsetX = 0.0;
28101
28148
  dragOffsetY = 0.0;
28102
- currentTimeString = '';
28103
- timebar;
28149
+ timebar = null;
28104
28150
  mainArea;
28105
28151
  cropArea; // HTMLElement with normCoord attribute;
28152
+ videoArea;
28106
28153
  controlsArea;
28107
- controlsPanelLeft;
28108
- controlsPanelRight;
28109
- controlsCurrentPanel;
28154
+ controlsComponents;
28110
28155
  onChangeCurrent;
28111
28156
  onChangeStart;
28112
28157
  onChangeEnd;
28113
28158
  onKeyUp;
28114
28159
  onSetTime;
28115
28160
  onVideoLoaded;
28116
- onCropArea;
28117
28161
  onResize;
28162
+ onCropArea;
28118
28163
  onChangeSpeed;
28164
+ onChangeState;
28165
+ onChangeLoop;
28119
28166
  _updateTime = true;
28120
28167
  _onCropMouseUp;
28121
28168
  _onCropMouseMove;
28122
- resize;
28169
+ resize = null;
28170
+ resizeControls = null;
28171
+ resizeVideo = null;
28123
28172
  constructor(area, options = {}) {
28124
28173
  this.options = options ?? {};
28125
28174
  this.speed = options.speed ?? this.speed;
@@ -28162,120 +28211,42 @@
28162
28211
  videoArea.root.classList.add('lexvideoeditor');
28163
28212
  }
28164
28213
  videoArea.root.style.position = 'relative';
28214
+ this.videoArea = videoArea;
28165
28215
  this.controlsArea = controlsArea;
28166
- // Create playing timeline area and attach panels
28167
- let [topArea, bottomArea] = controlsArea.split({ type: 'vertical', sizes: ['50%', null], minimizable: false, resize: false });
28168
- bottomArea.setSize([bottomArea.size[0], 40]);
28169
- let [leftArea, controlsRight] = bottomArea.split({ type: 'horizontal', sizes: ['92%', null], minimizable: false, resize: false });
28170
- let [controlsLeft, timeBarArea] = leftArea.split({ type: 'horizontal', sizes: ['10%', null], minimizable: false, resize: false });
28171
- topArea.root.classList.add('lexbar');
28172
- bottomArea.root.classList.add('lexbar');
28173
- this.controlsCurrentPanel = new exports.LX.Panel({ className: 'lexcontrolspanel lextime' });
28174
- this.controlsCurrentPanel.refresh = () => {
28175
- this.controlsCurrentPanel.clear();
28176
- this.controlsCurrentPanel.addLabel(this.currentTimeString, { float: 'center' });
28177
- };
28178
- topArea.root.classList.add('lexflexarea');
28179
- topArea.attach(this.controlsCurrentPanel);
28180
- this.controlsCurrentPanel.refresh();
28181
- const style = getComputedStyle(bottomArea.root);
28182
- let padding = Number(style.getPropertyValue('padding').replace('px', ''));
28183
- this.timebar = new TimeBar(timeBarArea, TimeBar.TIMEBAR_TRIM, { offset: padding });
28184
- // Create controls panel (play/pause button and start time)
28185
- this.controlsPanelLeft = new exports.LX.Panel({ className: 'lexcontrolspanel' });
28186
- this.controlsPanelLeft.refresh = () => {
28187
- this.controlsPanelLeft.clear();
28188
- this.controlsPanelLeft.sameLine();
28189
- let playbtn = this.controlsPanelLeft.addButton(null, 'PlayButton', (v) => {
28190
- this.playing = v;
28191
- if (this.playing) {
28192
- if (this.video.currentTime + 0.000001 >= this.endTime) {
28193
- this.video.currentTime = this.startTime;
28194
- }
28195
- this.video.play();
28196
- }
28197
- else {
28198
- this.video.pause();
28199
- }
28200
- }, { icon: 'Play@solid', swap: 'Pause@solid', hideName: true, title: 'Play', tooltip: true, className: 'justify-center' });
28201
- playbtn.setState(this.playing, true);
28202
- this.controlsPanelLeft.addButton(null, '', (v, e) => {
28203
- const panel = new exports.LX.Panel();
28204
- panel.addRange('Speed', this.speed, (v) => {
28205
- this.speed = v;
28206
- this.video.playbackRate = v;
28207
- if (this.onChangeSpeed) {
28208
- this.onChangeSpeed(v);
28209
- }
28210
- }, { min: 0, max: 2.5, step: 0.01, hideName: true });
28211
- new exports.LX.Popover(e.target, [panel], { align: 'start', side: 'top', sideOffset: 12 });
28212
- }, { icon: 'Timer@solid', title: 'Speed', tooltip: true, className: 'justify-center' });
28213
- this.controlsPanelLeft.addButton(null, 'Loop', (v) => {
28214
- this.loop = v;
28215
- }, { title: 'Loop', tooltip: true, icon: ('Repeat@solid'), className: `justify-center`, selectable: true, selected: this.loop });
28216
- this.controlsPanelLeft.addLabel(this.startTimeString, { width: '100px' });
28217
- this.controlsPanelLeft.endLine();
28218
- let availableWidth = leftArea.root.clientWidth - controlsLeft.root.clientWidth;
28219
- this.timebar.resize([availableWidth, timeBarArea.root.clientHeight]);
28220
- };
28221
- this.controlsPanelLeft.refresh();
28222
- controlsLeft.root.style.minWidth = 'fit-content';
28223
- // controlsLeft.root.classList.add();
28224
- controlsLeft.attach(this.controlsPanelLeft);
28225
- // Create right controls panel (ens time)
28226
- this.controlsPanelRight = new exports.LX.Panel({ className: 'lexcontrolspanel' });
28227
- this.controlsPanelRight.refresh = () => {
28228
- this.controlsPanelRight.clear();
28229
- this.controlsPanelRight.addLabel(this.endTimeString, { width: 100 });
28216
+ this.controlsComponents = {
28217
+ timebar: null,
28218
+ playBtn: null,
28219
+ speedBtn: null,
28220
+ loopBtn: null,
28221
+ trimStartText: null,
28222
+ trimEndText: null,
28223
+ curTimeText: null,
28224
+ resetCropBtn: null
28230
28225
  };
28231
- this.controlsPanelRight.refresh();
28232
- controlsRight.root.style.minWidth = 'fit-content';
28233
- controlsRight.attach(this.controlsPanelRight);
28234
- this.timebar.onChangeCurrent = this._setCurrentTime.bind(this);
28235
- this.timebar.onChangeStart = this._setStartTime.bind(this);
28236
- this.timebar.onChangeEnd = this._setEndTime.bind(this);
28237
- this.resize = () => {
28238
- bottomArea.setSize([this.controlsArea.root.clientWidth, 40]);
28239
- let availableWidth = this.controlsArea.root.clientWidth - controlsLeft.root.clientWidth
28240
- - controlsRight.root.clientWidth;
28241
- this.timebar.resize([availableWidth, timeBarArea.root.clientHeight]);
28226
+ this.createControls();
28227
+ this.resizeVideo = () => {
28242
28228
  this.moveCropArea(this.cropArea.normCoords.x, this.cropArea.normCoords.y, true);
28243
28229
  this.resizeCropArea(this.cropArea.normCoords.w, this.cropArea.normCoords.h, true);
28244
28230
  if (this.onResize) {
28245
28231
  this.onResize([videoArea.root.clientWidth, videoArea.root.clientHeight]);
28246
28232
  }
28247
28233
  };
28234
+ this.resize = () => {
28235
+ this.resizeVideo();
28236
+ this.resizeControls();
28237
+ };
28248
28238
  area.onresize = this.resize.bind(this);
28249
28239
  window.addEventListener('resize', area.onresize);
28250
28240
  this.onKeyUp = (e) => {
28251
28241
  if (this.controls && e.key == ' ') {
28252
28242
  e.preventDefault();
28253
28243
  e.stopPropagation();
28254
- this.playing = !this.playing;
28255
- if (this.playing) {
28256
- if (this.video.currentTime + 0.000001 >= this.endTime) {
28257
- this.video.currentTime = this.startTime;
28258
- }
28259
- this.video.play();
28260
- }
28261
- else {
28262
- this.video.pause();
28263
- }
28264
- this.controlsPanelLeft.refresh();
28244
+ // do not skip callback
28245
+ this.controlsComponents.playBtn?.setState(!this.playing, false);
28265
28246
  }
28266
28247
  };
28267
28248
  window.addEventListener('keyup', this.onKeyUp);
28268
- const parent = controlsArea.parentElement ? controlsArea.parentElement : controlsArea.root.parentElement;
28269
- // Add canvas event listeneres
28270
- parent.addEventListener('mousedown', (e) => {
28271
- // if( this.controls) {
28272
- // this.timebar.onMouseDown(e);
28273
- // }
28274
- });
28275
28249
  this._onCropMouseUp = (event) => {
28276
- // if(this.controls) {
28277
- // this.timebar.onMouseUp(event);
28278
- // }
28279
28250
  event.preventDefault();
28280
28251
  event.stopPropagation();
28281
28252
  if ((this.isDragging || this.isResizing) && this.onCropArea) {
@@ -28287,9 +28258,6 @@
28287
28258
  document.removeEventListener('mousemove', this._onCropMouseMove); // self destroy. Added during mouseDown on cropArea and handles
28288
28259
  };
28289
28260
  this._onCropMouseMove = (event) => {
28290
- // if(this.controls) {
28291
- // this.timebar.onMouseMove(event);
28292
- // }
28293
28261
  window.getSelection()?.removeAllRanges();
28294
28262
  event.preventDefault();
28295
28263
  event.stopPropagation();
@@ -28350,6 +28318,182 @@
28350
28318
  this.onChangeStart = null;
28351
28319
  this.onChangeEnd = null;
28352
28320
  }
28321
+ createControls(options = null) {
28322
+ const controlsArea = this.controlsArea;
28323
+ options = options ?? this.options;
28324
+ // clear area. Signals are not cleared !!! (not a problem if there are no signals)
28325
+ while (controlsArea.root.children.length) {
28326
+ controlsArea.root.children[0].remove();
28327
+ }
28328
+ controlsArea.sections.length = 0;
28329
+ // start trimming text
28330
+ this.controlsComponents.trimStartText = new exports.LX.TextInput(null, this.timeToString(this.startTime), null, { width: '100px',
28331
+ title: 'Trimmed Start Time', disabled: true, inputClass: 'bg-none' });
28332
+ this.controlsComponents.trimEndText = new exports.LX.TextInput(null, this.timeToString(this.endTime), null, { width: '100px',
28333
+ title: 'Trimmed End Time', disabled: true, inputClass: 'bg-none' });
28334
+ this.controlsComponents.curTimeText = new exports.LX.TextInput(null, this.video.currentTime, null, { title: 'Current Time', float: 'center',
28335
+ disabled: true, inputClass: 'bg-none' });
28336
+ // reset crop area
28337
+ this.controlsComponents.resetCropBtn = new exports.LX.Button('ResetCrop', null, (v) => {
28338
+ this.moveCropArea(0, 0, true);
28339
+ this.resizeCropArea(1, 1, true);
28340
+ if (this.onCropArea) {
28341
+ this.onCropArea(this.getCroppedArea());
28342
+ }
28343
+ }, { width: '40px', title: 'Reset Crop Area', icon: 'Crop@solid', hideName: true,
28344
+ className: 'justify-center' + (this.crop ? '' : ' hidden') });
28345
+ // play button
28346
+ this.controlsComponents.playBtn = new exports.LX.Button('Play', '', (v) => {
28347
+ this.playing = v;
28348
+ if (this.playing) {
28349
+ if (this.video.currentTime + 0.000001 >= this.endTime) {
28350
+ this.video.currentTime = this.startTime;
28351
+ }
28352
+ this.video.play();
28353
+ }
28354
+ else {
28355
+ this.video.pause();
28356
+ }
28357
+ if (this.onChangeState) {
28358
+ this.onChangeState(v);
28359
+ }
28360
+ }, { width: '40px', title: 'Play/Pause', icon: 'Play@solid', swap: 'Pause@solid', hideName: true, className: 'justify-center' });
28361
+ this.controlsComponents.playBtn.setState(this.playing, true);
28362
+ // speed button
28363
+ this.controlsComponents.speedBtn = new exports.LX.Button('Speed', '', (v, e) => {
28364
+ const panel = new exports.LX.Panel();
28365
+ panel.addRange('Speed', this.speed, (v) => {
28366
+ this.speed = v;
28367
+ this.video.playbackRate = v;
28368
+ if (this.onChangeSpeed) {
28369
+ this.onChangeSpeed(v);
28370
+ }
28371
+ }, { min: 0, max: 2.5, step: 0.01, hideName: true });
28372
+ new exports.LX.Popover(e.target, [panel], { align: 'start', side: 'top', sideOffset: 12 });
28373
+ }, { width: '40px', title: 'Speed', hideName: true, icon: 'Timer@solid', className: 'justify-center' });
28374
+ // loop button
28375
+ this.controlsComponents.loopBtn = new exports.LX.Button('', 'Loop', (v) => {
28376
+ this.loop = v;
28377
+ if (this.onChangeLoop) {
28378
+ this.onChangeLoop(v);
28379
+ }
28380
+ }, { width: '40px', hideName: true, title: 'Loop', icon: 'Repeat@solid', className: `justify-center`, selectable: true,
28381
+ selected: this.loop });
28382
+ let timeBarArea = null;
28383
+ if (typeof (options.controlsLayout) == 'function') {
28384
+ timeBarArea = options.controlsLayout;
28385
+ }
28386
+ else if (options.controlsLayout == 1) {
28387
+ timeBarArea = this._createControlsLayout_1();
28388
+ }
28389
+ else {
28390
+ timeBarArea = this._createControlsLayout_0();
28391
+ }
28392
+ if (this.timebar) {
28393
+ this.timebar.unbind();
28394
+ }
28395
+ this.timebar = this.controlsComponents.timebar = new TimeBar(timeBarArea, TimeBar.TIMEBAR_TRIM, { offset: [12, null] });
28396
+ this.timebar.onChangeCurrent = this._setCurrentTime.bind(this);
28397
+ this.timebar.onChangeStart = this._setStartTime.bind(this);
28398
+ this.timebar.onChangeEnd = this._setEndTime.bind(this);
28399
+ let duration = 1;
28400
+ if (this.video.duration !== Infinity && !isNaN(this.video.duration)) {
28401
+ duration = this.video.duration;
28402
+ }
28403
+ this.timebar.setDuration(duration);
28404
+ this.timebar.setEndTime(this.endTime);
28405
+ this.timebar.setStartTime(this.startTime);
28406
+ this.timebar.setCurrentTime(this.startTime);
28407
+ this.resizeControls();
28408
+ }
28409
+ /**
28410
+ * Creates the areas where components will be.
28411
+ * Attaches all (desired) components of controlsComponents except the timebar
28412
+ * @returns {Area} for the timebar
28413
+ * Layout:
28414
+ * |--------------------------timebar--------------------------|
28415
+ * play speed loop resetCrop curTime trimStart / trimEnd
28416
+ */
28417
+ _createControlsLayout_1() {
28418
+ const controlsArea = this.controlsArea;
28419
+ // Create playing timeline area and attach panels
28420
+ let [timeBarArea, bottomArea] = controlsArea.split({ type: 'vertical', sizes: ['50%', null], minimizable: false, resize: false });
28421
+ bottomArea.root.classList.add('relative');
28422
+ let separator = document.createElement('p');
28423
+ separator.style.alignContent = 'center';
28424
+ separator.innerText = '/';
28425
+ let trimDiv = exports.LX.makeContainer(['fit-content', '100%'], 'relative flex flex-row pb-2', null, bottomArea, { float: 'right' });
28426
+ trimDiv.appendChild(this.controlsComponents.trimStartText.root);
28427
+ trimDiv.appendChild(separator);
28428
+ trimDiv.appendChild(this.controlsComponents.trimEndText.root);
28429
+ this.controlsComponents.trimStartText.root.querySelector('input').classList.add('text-end');
28430
+ this.controlsComponents.trimStartText.root.classList.add('top-0', 'bottom-0');
28431
+ this.controlsComponents.trimEndText.root.classList.add('top-0', 'bottom-0');
28432
+ // current time
28433
+ let curTimeDiv = exports.LX.makeContainer(['100%', '100%'], 'absolute top-0 left-0 flex flex-row justify-center items-center pb-2', null, bottomArea, {});
28434
+ curTimeDiv.appendChild(this.controlsComponents.curTimeText.root);
28435
+ // Buttons
28436
+ const buttonsPanel = bottomArea.addPanel({ className: 'absolute top-0 left-0 flex flex-row pl-4 pr-4 pt-1 pb-2' });
28437
+ buttonsPanel.root.classList.remove('pad-md');
28438
+ buttonsPanel._attachComponent(this.controlsComponents.playBtn);
28439
+ buttonsPanel._attachComponent(this.controlsComponents.speedBtn);
28440
+ buttonsPanel._attachComponent(this.controlsComponents.loopBtn);
28441
+ buttonsPanel._attachComponent(this.controlsComponents.resetCropBtn);
28442
+ this.controlsComponents.playBtn.root.classList.add('pl-0');
28443
+ this.controlsComponents.resetCropBtn.root.classList.add('pr-0');
28444
+ // timebar
28445
+ timeBarArea.root.classList.add('p-4', 'pb-0');
28446
+ this.resizeControls = () => {
28447
+ const style = getComputedStyle(timeBarArea.root);
28448
+ let pleft = parseFloat(style.paddingLeft);
28449
+ let pright = parseFloat(style.paddingRight);
28450
+ let ptop = parseFloat(style.paddingTop);
28451
+ let pbot = parseFloat(style.paddingBottom);
28452
+ // assuming timeBarArea will not overflow
28453
+ this.timebar.resize([timeBarArea.root.clientWidth - pleft - pright, timeBarArea.root.clientHeight - ptop - pbot]);
28454
+ };
28455
+ return timeBarArea;
28456
+ }
28457
+ /**
28458
+ * Creates the areas where components will be.
28459
+ * Attaches all (desired) components of controlsComponents except the timebar
28460
+ * @returns {Area} for the timebar
28461
+ * Layout:
28462
+ * curTime
28463
+ * play speed loop trimStart |---timebar---| trimend
28464
+ */
28465
+ _createControlsLayout_0() {
28466
+ const controlsArea = this.controlsArea;
28467
+ // Create playing timeline area and attach panels
28468
+ let [topArea, bottomArea] = controlsArea.split({ type: 'vertical', sizes: ['50%', null], minimizable: false, resize: false });
28469
+ bottomArea.setSize([bottomArea.size[0], 40]);
28470
+ let [leftArea, controlsRight] = bottomArea.split({ type: 'horizontal', sizes: ['92%', null], minimizable: false, resize: false });
28471
+ let [controlsLeft, timeBarArea] = leftArea.split({ type: 'horizontal', sizes: ['10%', null], minimizable: false, resize: false });
28472
+ const controlsCurrentPanel = topArea.addPanel({ className: 'flex' });
28473
+ controlsCurrentPanel._attachComponent(this.controlsComponents.curTimeText);
28474
+ // Create controls panel (play/pause button and start time)
28475
+ controlsLeft.root.classList.add('min-w-fit');
28476
+ const controlsPanelLeft = controlsLeft.addPanel({ className: 'lexcontrolspanel p-0 pl-2' });
28477
+ controlsPanelLeft.root.classList.remove('pad-md');
28478
+ controlsPanelLeft.sameLine();
28479
+ controlsPanelLeft._attachComponent(this.controlsComponents.playBtn);
28480
+ controlsPanelLeft._attachComponent(this.controlsComponents.speedBtn);
28481
+ controlsPanelLeft._attachComponent(this.controlsComponents.loopBtn);
28482
+ controlsPanelLeft._attachComponent(this.controlsComponents.trimStartText);
28483
+ controlsPanelLeft.endLine();
28484
+ // Create right controls panel (end time)
28485
+ controlsRight.root.classList.add('min-w-fit');
28486
+ const controlsPanelRight = controlsRight.addPanel({ className: 'lexcontrolspanel p-0' });
28487
+ controlsPanelRight.root.classList.remove('pad-md');
28488
+ controlsPanelRight._attachComponent(this.controlsComponents.trimEndText);
28489
+ this.resizeControls = () => {
28490
+ bottomArea.setSize([this.controlsArea.root.clientWidth, 40]);
28491
+ let availableWidth = this.controlsArea.root.clientWidth - controlsLeft.root.clientWidth
28492
+ - controlsRight.root.clientWidth;
28493
+ this.timebar.resize([availableWidth, timeBarArea.root.clientHeight]);
28494
+ };
28495
+ return timeBarArea;
28496
+ }
28353
28497
  setCropAreaHandles(flags) {
28354
28498
  // remove existing resizer handles
28355
28499
  const resizers = this.cropArea.getElementsByClassName('resize-handle');
@@ -28478,18 +28622,12 @@
28478
28622
  this.video.currentTime = this.startTime;
28479
28623
  this.timebar.setCurrentTime(this.video.currentTime);
28480
28624
  };
28481
- this.timebar.startX = this.timebar.position.x;
28482
- this.timebar.endX = this.timebar.position.x + this.timebar.lineWidth;
28483
28625
  this.startTime = 0;
28484
28626
  this.endTime = this.video.duration;
28485
28627
  this.timebar.setDuration(this.endTime);
28486
28628
  this.timebar.setEndTime(this.video.duration);
28487
28629
  this.timebar.setStartTime(this.startTime);
28488
28630
  this.timebar.setCurrentTime(this.startTime);
28489
- // this.timebar.setStartValue( this.timebar.startX);
28490
- // this.timebar.currentX = this._timeToX( this.video.currentTime);
28491
- // this.timebar.setCurrentValue( this.timebar.currentX);
28492
- // this.timebar.update( this.timebar.currentX );
28493
28631
  // only have one update on flight
28494
28632
  if (!this.requestId) {
28495
28633
  this._update();
@@ -28522,7 +28660,7 @@
28522
28660
  this.video.pause();
28523
28661
  if (!this.loop) {
28524
28662
  this.playing = false;
28525
- this.controlsPanelLeft.refresh();
28663
+ this.controlsComponents.playBtn?.setState(false, true); // skip callback
28526
28664
  }
28527
28665
  else {
28528
28666
  this.video.currentTime = this.startTime;
@@ -28548,8 +28686,7 @@
28548
28686
  if (this.video.currentTime != t && this._updateTime) {
28549
28687
  this.video.currentTime = t;
28550
28688
  }
28551
- this.currentTimeString = this.timeToString(t);
28552
- this.controlsCurrentPanel.refresh();
28689
+ this.controlsComponents.curTimeText?.set(this.timeToString(t));
28553
28690
  if (this.onSetTime) {
28554
28691
  this.onSetTime(t);
28555
28692
  }
@@ -28559,8 +28696,7 @@
28559
28696
  }
28560
28697
  _setStartTime(t) {
28561
28698
  this.startTime = this.video.currentTime = t;
28562
- this.startTimeString = this.timeToString(t);
28563
- this.controlsPanelLeft.refresh();
28699
+ this.controlsComponents.trimStartText?.set(this.timeToString(t));
28564
28700
  if (this.onSetTime) {
28565
28701
  this.onSetTime(t);
28566
28702
  }
@@ -28570,8 +28706,7 @@
28570
28706
  }
28571
28707
  _setEndTime(t) {
28572
28708
  this.endTime = this.video.currentTime = t;
28573
- this.endTimeString = this.timeToString(t);
28574
- this.controlsPanelRight.refresh();
28709
+ this.controlsComponents.trimEndText?.set(this.timeToString(t));
28575
28710
  if (this.onSetTime) {
28576
28711
  this.onSetTime(t);
28577
28712
  }
@@ -28592,7 +28727,9 @@
28592
28727
  return this.cropArea.getBoundingClientRect();
28593
28728
  }
28594
28729
  showCropArea() {
28730
+ this.crop = true;
28595
28731
  this.cropArea.classList.remove('hidden');
28732
+ this.controlsComponents.resetCropBtn?.root.classList.remove('hidden');
28596
28733
  const nodes = this.cropArea.parentElement?.childNodes ?? [];
28597
28734
  const rect = this.cropArea.getBoundingClientRect();
28598
28735
  for (let i = 0; i < nodes.length; i++) {
@@ -28605,7 +28742,9 @@
28605
28742
  }
28606
28743
  }
28607
28744
  hideCropArea() {
28745
+ this.crop = false;
28608
28746
  this.cropArea.classList.add('hidden');
28747
+ this.controlsComponents.resetCropBtn?.root.classList.add('hidden');
28609
28748
  const nodes = this.cropArea.parentElement?.childNodes ?? [];
28610
28749
  for (let i = 0; i < nodes.length; i++) {
28611
28750
  const node = nodes[i];
@@ -28633,8 +28772,11 @@
28633
28772
  this.stopUpdates();
28634
28773
  this.video.pause();
28635
28774
  this.playing = false;
28636
- this.controlsPanelLeft.refresh();
28775
+ this.controlsComponents.playBtn?.setState(false, true); // skip callback
28637
28776
  this.video.src = '';
28777
+ if (this.timebar) {
28778
+ this.timebar.unbind();
28779
+ }
28638
28780
  window.removeEventListener('keyup', this.onKeyUp);
28639
28781
  document.removeEventListener('mouseup', this._onCropMouseUp);
28640
28782
  document.removeEventListener('mousemove', this._onCropMouseMove);