@odoo/o-spreadsheet 16.4.0-alpha.2 → 16.4.0-alpha.3

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.
@@ -5219,14 +5219,6 @@
5219
5219
  }
5220
5220
  }
5221
5221
  }
5222
- function getElementScrollTop(el) {
5223
- return el?.scrollTop || 0;
5224
- }
5225
- function setElementScrollTop(el, scroll) {
5226
- if (!el)
5227
- return;
5228
- el.scrollTop = scroll;
5229
- }
5230
5222
  function getOpenedMenus() {
5231
5223
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
5232
5224
  }
@@ -17944,7 +17936,7 @@
17944
17936
  return !!this.state.labelsDispatchResult?.isCancelledBecause(33 /* CommandResult.InvalidLabelRange */);
17945
17937
  }
17946
17938
  onUpdateDataSetsHaveTitle(ev) {
17947
- this.props.updateChart({
17939
+ this.props.updateChart(this.props.figureId, {
17948
17940
  dataSetsHaveTitle: ev.target.checked,
17949
17941
  });
17950
17942
  }
@@ -17957,7 +17949,7 @@
17957
17949
  }
17958
17950
  onDataSeriesConfirmed() {
17959
17951
  this.dataSeriesRanges = spreadRange(this.dataSeriesRanges);
17960
- this.state.datasetDispatchResult = this.props.updateChart({
17952
+ this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, {
17961
17953
  dataSets: this.dataSeriesRanges,
17962
17954
  });
17963
17955
  }
@@ -17972,7 +17964,7 @@
17972
17964
  this.labelRange = ranges[0];
17973
17965
  }
17974
17966
  onLabelRangeConfirmed() {
17975
- this.state.labelsDispatchResult = this.props.updateChart({
17967
+ this.state.labelsDispatchResult = this.props.updateChart(this.props.figureId, {
17976
17968
  labelRange: this.labelRange,
17977
17969
  });
17978
17970
  }
@@ -17980,7 +17972,7 @@
17980
17972
  return this.labelRange || "";
17981
17973
  }
17982
17974
  onUpdateAggregated(ev) {
17983
- this.props.updateChart({
17975
+ this.props.updateChart(this.props.figureId, {
17984
17976
  aggregated: ev.target.checked,
17985
17977
  });
17986
17978
  }
@@ -18010,12 +18002,12 @@
18010
18002
  class BarConfigPanel extends LineBarPieConfigPanel {
18011
18003
  static template = "o-spreadsheet-BarConfigPanel";
18012
18004
  onUpdateStacked(ev) {
18013
- this.props.updateChart({
18005
+ this.props.updateChart(this.props.figureId, {
18014
18006
  stacked: ev.target.checked,
18015
18007
  });
18016
18008
  }
18017
18009
  onUpdateAggregated(ev) {
18018
- this.props.updateChart({
18010
+ this.props.updateChart(this.props.figureId, {
18019
18011
  aggregated: ev.target.checked,
18020
18012
  });
18021
18013
  }
@@ -18352,17 +18344,17 @@
18352
18344
  this.state.fillColorTool = !this.state.fillColorTool;
18353
18345
  }
18354
18346
  updateBackgroundColor(color) {
18355
- this.props.updateChart({
18347
+ this.props.updateChart(this.props.figureId, {
18356
18348
  background: color,
18357
18349
  });
18358
18350
  }
18359
18351
  updateTitle(ev) {
18360
- this.props.updateChart({
18352
+ this.props.updateChart(this.props.figureId, {
18361
18353
  title: ev.target.value,
18362
18354
  });
18363
18355
  }
18364
18356
  updateSelect(attr, ev) {
18365
- this.props.updateChart({
18357
+ this.props.updateChart(this.props.figureId, {
18366
18358
  [attr]: ev.target.value,
18367
18359
  });
18368
18360
  }
@@ -18395,7 +18387,7 @@
18395
18387
  this.dataRange = ranges[0];
18396
18388
  }
18397
18389
  updateDataRange() {
18398
- this.state.dataRangeDispatchResult = this.props.updateChart({
18390
+ this.state.dataRangeDispatchResult = this.props.updateChart(this.props.figureId, {
18399
18391
  dataRange: this.dataRange,
18400
18392
  });
18401
18393
  }
@@ -18456,12 +18448,12 @@
18456
18448
  }
18457
18449
  updateBackgroundColor(color) {
18458
18450
  this.state.openedMenu = undefined;
18459
- this.props.updateChart({
18451
+ this.props.updateChart(this.props.figureId, {
18460
18452
  background: color,
18461
18453
  });
18462
18454
  }
18463
18455
  updateTitle(ev) {
18464
- this.props.updateChart({
18456
+ this.props.updateChart(this.props.figureId, {
18465
18457
  title: ev.target.value,
18466
18458
  });
18467
18459
  }
@@ -18526,7 +18518,7 @@
18526
18518
  }
18527
18519
  }
18528
18520
  updateSectionRule(sectionRule) {
18529
- this.state.sectionRuleDispatchResult = this.props.updateChart({
18521
+ this.state.sectionRuleDispatchResult = this.props.updateChart(this.props.figureId, {
18530
18522
  sectionRule,
18531
18523
  });
18532
18524
  }
@@ -18550,17 +18542,17 @@
18550
18542
  return false;
18551
18543
  }
18552
18544
  onUpdateLabelsAsText(ev) {
18553
- this.props.updateChart({
18545
+ this.props.updateChart(this.props.figureId, {
18554
18546
  labelsAsText: ev.target.checked,
18555
18547
  });
18556
18548
  }
18557
18549
  onUpdateStacked(ev) {
18558
- this.props.updateChart({
18550
+ this.props.updateChart(this.props.figureId, {
18559
18551
  stacked: ev.target.checked,
18560
18552
  });
18561
18553
  }
18562
18554
  onUpdateAggregated(ev) {
18563
- this.props.updateChart({
18555
+ this.props.updateChart(this.props.figureId, {
18564
18556
  aggregated: ev.target.checked,
18565
18557
  });
18566
18558
  }
@@ -18596,7 +18588,7 @@
18596
18588
  this.keyValue = ranges[0];
18597
18589
  }
18598
18590
  updateKeyValueRange() {
18599
- this.state.keyValueDispatchResult = this.props.updateChart({
18591
+ this.state.keyValueDispatchResult = this.props.updateChart(this.props.figureId, {
18600
18592
  keyValue: this.keyValue,
18601
18593
  });
18602
18594
  }
@@ -18607,7 +18599,7 @@
18607
18599
  this.baseline = ranges[0];
18608
18600
  }
18609
18601
  updateBaselineRange() {
18610
- this.state.baselineDispatchResult = this.props.updateChart({
18602
+ this.state.baselineDispatchResult = this.props.updateChart(this.props.figureId, {
18611
18603
  baseline: this.baseline,
18612
18604
  });
18613
18605
  }
@@ -18615,7 +18607,7 @@
18615
18607
  return this.baseline || "";
18616
18608
  }
18617
18609
  updateBaselineMode(ev) {
18618
- this.props.updateChart({ baselineMode: ev.target.value });
18610
+ this.props.updateChart(this.props.figureId, { baselineMode: ev.target.value });
18619
18611
  }
18620
18612
  }
18621
18613
  ScorecardChartConfigPanel.props = {
@@ -18634,12 +18626,12 @@
18634
18626
  owl.useExternalListener(window, "click", this.closeMenus);
18635
18627
  }
18636
18628
  updateTitle(ev) {
18637
- this.props.updateChart({
18629
+ this.props.updateChart(this.props.figureId, {
18638
18630
  title: ev.target.value,
18639
18631
  });
18640
18632
  }
18641
18633
  updateBaselineDescr(ev) {
18642
- this.props.updateChart({ baselineDescr: ev.target.value });
18634
+ this.props.updateChart(this.props.figureId, { baselineDescr: ev.target.value });
18643
18635
  }
18644
18636
  toggleColorPicker(colorPickerId) {
18645
18637
  if (this.state.openedColorPicker === colorPickerId) {
@@ -18652,13 +18644,13 @@
18652
18644
  setColor(color, colorPickerId) {
18653
18645
  switch (colorPickerId) {
18654
18646
  case "backgroundColor":
18655
- this.props.updateChart({ background: color });
18647
+ this.props.updateChart(this.props.figureId, { background: color });
18656
18648
  break;
18657
18649
  case "baselineColorDown":
18658
- this.props.updateChart({ baselineColorDown: color });
18650
+ this.props.updateChart(this.props.figureId, { baselineColorDown: color });
18659
18651
  break;
18660
18652
  case "baselineColorUp":
18661
- this.props.updateChart({ baselineColorUp: color });
18653
+ this.props.updateChart(this.props.figureId, { baselineColorUp: color });
18662
18654
  break;
18663
18655
  }
18664
18656
  this.closeMenus();
@@ -18723,13 +18715,9 @@
18723
18715
  class ChartPanel extends owl.Component {
18724
18716
  static template = "o-spreadsheet-ChartPanel";
18725
18717
  state;
18726
- shouldUpdateChart = true;
18727
18718
  get figureId() {
18728
18719
  return this.state.figureId;
18729
18720
  }
18730
- get sheetId() {
18731
- return this.state.sheetId;
18732
- }
18733
18721
  setup() {
18734
18722
  const selectedFigureId = this.env.model.getters.getSelectedFigureId();
18735
18723
  if (!selectedFigureId) {
@@ -18739,17 +18727,11 @@
18739
18727
  this.state = owl.useState({
18740
18728
  panel: "configuration",
18741
18729
  figureId: selectedFigureId,
18742
- sheetId: this.env.model.getters.getActiveSheetId(),
18743
18730
  });
18744
18731
  owl.onWillUpdateProps(() => {
18745
18732
  const selectedFigureId = this.env.model.getters.getSelectedFigureId();
18746
18733
  if (selectedFigureId && selectedFigureId !== this.state.figureId) {
18747
18734
  this.state.figureId = selectedFigureId;
18748
- this.state.sheetId = this.env.model.getters.getActiveSheetId();
18749
- this.shouldUpdateChart = false;
18750
- }
18751
- else {
18752
- this.shouldUpdateChart = true;
18753
18735
  }
18754
18736
  if (!this.env.model.getters.isChartDefined(this.figureId)) {
18755
18737
  this.props.onCloseSidePanel();
@@ -18757,8 +18739,8 @@
18757
18739
  }
18758
18740
  });
18759
18741
  }
18760
- updateChart(updateDefinition) {
18761
- if (!this.shouldUpdateChart) {
18742
+ updateChart(figureId, updateDefinition) {
18743
+ if (figureId !== this.figureId) {
18762
18744
  return;
18763
18745
  }
18764
18746
  const definition = {
@@ -18767,8 +18749,8 @@
18767
18749
  };
18768
18750
  return this.env.model.dispatch("UPDATE_CHART", {
18769
18751
  definition,
18770
- id: this.figureId,
18771
- sheetId: this.sheetId,
18752
+ id: figureId,
18753
+ sheetId: this.env.model.getters.getFigureSheetId(figureId),
18772
18754
  });
18773
18755
  }
18774
18756
  onTypeChange(type) {
@@ -18780,7 +18762,7 @@
18780
18762
  this.env.model.dispatch("UPDATE_CHART", {
18781
18763
  definition,
18782
18764
  id: this.figureId,
18783
- sheetId: this.sheetId,
18765
+ sheetId: this.env.model.getters.getFigureSheetId(this.figureId),
18784
18766
  });
18785
18767
  }
18786
18768
  get chartPanel() {
@@ -20576,12 +20558,23 @@
20576
20558
  */
20577
20559
  selectRange(start, end) {
20578
20560
  let selection = window.getSelection();
20579
- this.removeSelection();
20580
- let range = document.createRange();
20561
+ const { start: currentStart, end: currentEnd } = this.getCurrentSelection();
20562
+ if (currentStart === start && currentEnd === end) {
20563
+ return;
20564
+ }
20565
+ const currentRange = selection.getRangeAt(0);
20566
+ let range;
20567
+ if (this.el.contains(currentRange.startContainer)) {
20568
+ range = currentRange;
20569
+ }
20570
+ else {
20571
+ range = document.createRange();
20572
+ selection.removeAllRanges();
20573
+ selection.addRange(range);
20574
+ }
20581
20575
  if (start == end && start === 0) {
20582
20576
  range.setStart(this.el, 0);
20583
20577
  range.setEnd(this.el, 0);
20584
- selection.addRange(range);
20585
20578
  }
20586
20579
  else {
20587
20580
  const textLength = this.getText().length;
@@ -20597,7 +20590,6 @@
20597
20590
  let startNode = this.findChildAtCharacterIndex(start);
20598
20591
  let endNode = this.findChildAtCharacterIndex(end);
20599
20592
  range.setStart(startNode.node, startNode.offset);
20600
- selection.addRange(range);
20601
20593
  selection.extend(endNode.node, endNode.offset);
20602
20594
  }
20603
20595
  }
@@ -20637,26 +20629,54 @@
20637
20629
  return { node: previous, offset: usedCharacters };
20638
20630
  }
20639
20631
  /**
20640
- * Sets (or Replaces all) the text inside the root element in the form of distinctive
20632
+ * Sets (or Replaces all) the text inside the root element in the form of distinctive paragraphs and
20641
20633
  * span for each element provided in `contents`.
20642
20634
  *
20635
+ * The function will apply the diff between the current content and the new content to avoid the systematic
20636
+ * destruction of DOM elements which interferes with IME[1]
20637
+ *
20638
+ * Each line of text will be encapsulated in a paragraph element.
20643
20639
  * Each span will have its own fontcolor and specific class if provided in the HtmlContent object.
20640
+ *
20641
+ * [1] https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor
20644
20642
  */
20645
20643
  setText(contents) {
20646
- this.el.innerHTML = "";
20647
20644
  if (contents.length === 0) {
20645
+ this.removeAll();
20648
20646
  return;
20649
20647
  }
20650
- for (const line of contents) {
20651
- const p = document.createElement("p");
20652
- // Empty line
20653
- if (line.length === 0 || line.every((content) => !content.value && !content.class)) {
20654
- p.appendChild(document.createElement("br"));
20655
- this.el.appendChild(p);
20656
- continue;
20648
+ const childElements = Array.from(this.el.childNodes);
20649
+ const contentLength = contents.length;
20650
+ for (let i = 0; i < contentLength; i++) {
20651
+ const line = contents[i];
20652
+ const childElement = childElements[i];
20653
+ let newChild = false;
20654
+ let p;
20655
+ if (childElement && childElement.nodeName === "P") {
20656
+ p = childElement;
20657
20657
  }
20658
- for (const content of line) {
20658
+ else {
20659
+ newChild = true;
20660
+ p = document.createElement("p");
20661
+ }
20662
+ const lineLength = line.length;
20663
+ const existingChildren = Array.from(p.childNodes);
20664
+ for (let j = 0; j < lineLength; j++) {
20665
+ const content = line[j];
20666
+ const child = existingChildren[j];
20667
+ // child nodes can be multiple types of nodes: Span, Text, Div, etc...
20668
+ // We can only modify a node in place if it has the same type as the content
20669
+ // that we would insert, which are spans.
20670
+ // Otherwise, it means that the node has been input by the user, through the keyboard or a copy/paste
20671
+ // @ts-ignore (somehow required because jest does not like child.tagName despite the prior check)
20672
+ const childIsSpan = child && "tagName" in child && child.tagName === "SPAN";
20673
+ if (childIsSpan && compareContentToSpanElement(content, child)) {
20674
+ continue;
20675
+ }
20676
+ // this is an empty line in the content
20659
20677
  if (!content.value && !content.class) {
20678
+ if (child)
20679
+ p.removeChild(child);
20660
20680
  continue;
20661
20681
  }
20662
20682
  const span = document.createElement("span");
@@ -20665,9 +20685,38 @@
20665
20685
  if (content.class) {
20666
20686
  span.classList.add(content.class);
20667
20687
  }
20688
+ if (child) {
20689
+ p.replaceChild(span, child);
20690
+ }
20691
+ else {
20692
+ p.appendChild(span);
20693
+ }
20694
+ }
20695
+ if (existingChildren.length > lineLength) {
20696
+ for (let i = lineLength; i < existingChildren.length; i++) {
20697
+ p.removeChild(existingChildren[i]);
20698
+ }
20699
+ }
20700
+ // Empty line
20701
+ if (!p.hasChildNodes()) {
20702
+ const span = document.createElement("span");
20703
+ span.appendChild(document.createElement("br"));
20668
20704
  p.appendChild(span);
20669
20705
  }
20670
- this.el.appendChild(p);
20706
+ // replace p if necessary
20707
+ if (newChild) {
20708
+ if (childElement) {
20709
+ this.el.replaceChild(p, childElement);
20710
+ }
20711
+ else {
20712
+ this.el.appendChild(p);
20713
+ }
20714
+ }
20715
+ }
20716
+ if (childElements.length > contentLength) {
20717
+ for (let i = contentLength; i < childElements.length; i++) {
20718
+ this.el.removeChild(childElements[i]);
20719
+ }
20671
20720
  }
20672
20721
  }
20673
20722
  scrollSelectionIntoView() {
@@ -20677,13 +20726,6 @@
20677
20726
  const element = focusedNode instanceof HTMLElement ? focusedNode : focusedNode.parentElement;
20678
20727
  element?.scrollIntoView({ block: "nearest" });
20679
20728
  }
20680
- /**
20681
- * remove the current selection of the user
20682
- * */
20683
- removeSelection() {
20684
- let selection = window.getSelection();
20685
- selection.removeAllRanges();
20686
- }
20687
20729
  removeAll() {
20688
20730
  if (this.el) {
20689
20731
  while (this.el.firstChild) {
@@ -20813,6 +20855,14 @@
20813
20855
  }
20814
20856
  return text;
20815
20857
  }
20858
+ }
20859
+ function compareContentToSpanElement(content, node) {
20860
+ const contentColor = content.color ? toHex(content.color) : "";
20861
+ const nodeColor = node.style?.color ? toHex(node.style.color) : "";
20862
+ const sameColor = contentColor === nodeColor;
20863
+ const sameClass = deepEquals([content.class], [...node.classList]);
20864
+ const sameContent = node.innerText === content.value;
20865
+ return sameColor && sameClass && sameContent;
20816
20866
  }
20817
20867
 
20818
20868
  // -----------------------------------------------------------------------------
@@ -20958,7 +21008,6 @@
20958
21008
  functionDescription: {},
20959
21009
  argToFocus: 0,
20960
21010
  });
20961
- isKeyStillDown = false;
20962
21011
  compositionActive = false;
20963
21012
  get assistantStyle() {
20964
21013
  if (this.props.delimitation && this.props.rect) {
@@ -20993,21 +21042,18 @@
20993
21042
  F2: () => console.warn("Not implemented"),
20994
21043
  F4: this.processF4Key,
20995
21044
  Tab: (ev) => this.processTabKey(ev),
21045
+ " ": (ev) => this.processSpaceKey(ev),
20996
21046
  };
20997
21047
  setup() {
20998
21048
  owl.onMounted(() => {
20999
21049
  const el = this.composerRef.el;
21000
21050
  this.contentHelper.updateEl(el);
21001
- this.processContent();
21002
- this.contentHelper.scrollSelectionIntoView();
21003
21051
  });
21004
21052
  owl.onWillUnmount(() => {
21005
21053
  this.props.onComposerUnmounted?.();
21006
21054
  });
21007
- owl.onPatched(() => {
21008
- if (!this.isKeyStillDown) {
21009
- this.processContent();
21010
- }
21055
+ owl.useEffect(() => {
21056
+ this.processContent();
21011
21057
  });
21012
21058
  }
21013
21059
  // ---------------------------------------------------------------------------
@@ -21060,32 +21106,25 @@
21060
21106
  return;
21061
21107
  }
21062
21108
  }
21063
- else {
21064
- // when completing with tab, if there is no value to complete, the active cell will be moved to the right.
21065
- // we can't let the model think that it is for a ref selection.
21066
- // todo: check if this can be removed someday
21067
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
21068
- }
21069
21109
  const direction = ev.shiftKey ? "left" : "right";
21070
21110
  this.env.model.dispatch("STOP_EDITION");
21071
21111
  this.env.model.selection.moveAnchorCell(direction, 1);
21072
21112
  }
21113
+ processSpaceKey(ev) {
21114
+ if (ev.ctrlKey) {
21115
+ ev.preventDefault();
21116
+ ev.stopPropagation();
21117
+ this.showAutocomplete("");
21118
+ this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
21119
+ }
21120
+ }
21073
21121
  processEnterKey(ev) {
21074
21122
  ev.preventDefault();
21075
21123
  ev.stopPropagation();
21076
21124
  if (ev.altKey || ev.ctrlKey) {
21077
- const selection = this.contentHelper.getCurrentSelection();
21078
- const currentContent = this.env.model.getters.getCurrentContent();
21079
- const content = currentContent.slice(0, selection.start) + NEWLINE + currentContent.slice(selection.end);
21080
- this.env.model.dispatch("SET_CURRENT_CONTENT", {
21081
- content,
21082
- selection: { start: selection.start + 1, end: selection.start + 1 },
21083
- });
21084
- this.processContent();
21085
- this.contentHelper.scrollSelectionIntoView();
21125
+ this.composerRef.el?.dispatchEvent(new InputEvent("input", { inputType: "insertParagraph", bubbles: true, isComposing: false }));
21086
21126
  return;
21087
21127
  }
21088
- this.isKeyStillDown = false;
21089
21128
  if (this.autoCompleteState.showProvider) {
21090
21129
  const autoCompleteValue = this.autoCompleteState.values[this.autoCompleteState.selectedIndex]?.text;
21091
21130
  if (autoCompleteValue) {
@@ -21118,56 +21157,42 @@
21118
21157
  else {
21119
21158
  ev.stopPropagation();
21120
21159
  }
21121
- const { start, end } = this.contentHelper.getCurrentSelection();
21122
- if (!this.env.model.getters.isSelectingForComposer() &&
21123
- !(ev.key === "Enter" && (ev.altKey || ev.ctrlKey))) {
21124
- this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", { start, end });
21125
- this.isKeyStillDown = true;
21126
- }
21127
21160
  }
21128
21161
  /*
21129
21162
  * Triggered automatically by the content-editable between the keydown and key up
21130
21163
  * */
21131
- onInput() {
21164
+ onInput(ev) {
21132
21165
  if (this.props.focus === "inactive" || !this.shouldProcessInputEvents) {
21133
21166
  return;
21134
21167
  }
21168
+ let content = this.contentHelper.getText();
21169
+ let selection = this.contentHelper.getCurrentSelection();
21170
+ if (ev.inputType === "insertParagraph") {
21171
+ const start = Math.min(selection.start, selection.end);
21172
+ const end = Math.max(selection.start, selection.end);
21173
+ content = content.slice(0, start) + NEWLINE + content.slice(end);
21174
+ selection = { start: start + 1, end: start + 1 };
21175
+ }
21135
21176
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
21136
21177
  this.env.model.dispatch("SET_CURRENT_CONTENT", {
21137
- content: this.contentHelper.getText(),
21138
- selection: this.contentHelper.getCurrentSelection(),
21178
+ content,
21179
+ selection,
21139
21180
  });
21181
+ this.processTokenAtCursor();
21140
21182
  }
21141
- onKeyup(ev) {
21142
- this.isKeyStillDown = false;
21143
- if (this.props.focus === "inactive" ||
21144
- ["Control", "Alt", "Shift", "Tab", "Enter", "F4"].includes(ev.key)) {
21145
- return;
21146
- }
21147
- if (this.autoCompleteState.showProvider && ["ArrowUp", "ArrowDown"].includes(ev.key)) {
21148
- return; // already processed in keydown
21149
- }
21150
- if (this.env.model.getters.isSelectingForComposer() &&
21151
- ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(ev.key)) {
21152
- return; // already processed in keydown
21153
- }
21154
- ev.preventDefault();
21155
- ev.stopPropagation();
21156
- this.autoCompleteState.showProvider = false;
21157
- if (ev.ctrlKey && ev.key === " ") {
21158
- this.showAutocomplete("");
21159
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
21160
- return;
21161
- }
21162
- const { start: oldStart, end: oldEnd } = this.env.model.getters.getComposerSelection();
21163
- const { start, end } = this.contentHelper.getCurrentSelection();
21164
- if (start !== oldStart || end !== oldEnd) {
21165
- this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", this.contentHelper.getCurrentSelection());
21183
+ onKeyup() {
21184
+ if (this.contentHelper.el === document.activeElement) {
21185
+ const { start: oldStart, end: oldEnd } = this.env.model.getters.getComposerSelection();
21186
+ const { start, end } = this.contentHelper.getCurrentSelection();
21187
+ if (start !== oldStart || end !== oldEnd) {
21188
+ this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", { start, end });
21189
+ }
21166
21190
  }
21167
- this.processTokenAtCursor();
21168
- this.processContent();
21169
21191
  }
21170
21192
  showAutocomplete(searchTerm) {
21193
+ if (!this.env.model.getters.getCurrentContent().startsWith("=")) {
21194
+ return;
21195
+ }
21171
21196
  this.autoCompleteState.showProvider = true;
21172
21197
  let values = Object.entries(functionRegistry.content)
21173
21198
  .filter(([_, { hidden }]) => !hidden)
@@ -21187,13 +21212,6 @@
21187
21212
  this.autoCompleteState.values = values.slice(0, 10);
21188
21213
  this.autoCompleteState.selectedIndex = 0;
21189
21214
  }
21190
- onMousedown(ev) {
21191
- if (ev.button > 0) {
21192
- // not main button, probably a context menu
21193
- return;
21194
- }
21195
- this.contentHelper.removeSelection();
21196
- }
21197
21215
  onClick() {
21198
21216
  if (this.env.model.getters.isReadonly()) {
21199
21217
  return;
@@ -21206,9 +21224,6 @@
21206
21224
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
21207
21225
  this.processTokenAtCursor();
21208
21226
  }
21209
- onBlur() {
21210
- this.isKeyStillDown = false;
21211
- }
21212
21227
  // ---------------------------------------------------------------------------
21213
21228
  // Private
21214
21229
  // ---------------------------------------------------------------------------
@@ -21216,22 +21231,19 @@
21216
21231
  if (this.compositionActive) {
21217
21232
  return;
21218
21233
  }
21219
- const oldScroll = getElementScrollTop(this.composerRef.el);
21220
- this.contentHelper.removeAll(); // removes the content of the composer, to be added just after
21221
21234
  this.shouldProcessInputEvents = false;
21222
21235
  if (this.props.focus !== "inactive") {
21223
21236
  this.contentHelper.el.focus();
21224
- this.contentHelper.selectRange(0, 0); // move the cursor inside the composer at 0 0.
21225
21237
  }
21226
21238
  const content = this.getContentLines();
21239
+ this.contentHelper.setText(content);
21227
21240
  if (content.length !== 0 && content.length[0] !== 0) {
21228
- this.contentHelper.setText(content);
21229
- const { start, end } = this.env.model.getters.getComposerSelection();
21230
21241
  if (this.props.focus !== "inactive") {
21231
21242
  // Put the cursor back where it was before the rendering
21243
+ const { start, end } = this.env.model.getters.getComposerSelection();
21232
21244
  this.contentHelper.selectRange(start, end);
21233
21245
  }
21234
- setElementScrollTop(this.composerRef.el, oldScroll);
21246
+ this.contentHelper.scrollSelectionIntoView();
21235
21247
  }
21236
21248
  this.shouldProcessInputEvents = true;
21237
21249
  }
@@ -21486,8 +21498,8 @@
21486
21498
  height: el.clientHeight,
21487
21499
  };
21488
21500
  this.composerState.delimitation = {
21489
- width: el.parentElement.clientWidth,
21490
- height: el.parentElement.clientHeight,
21501
+ width: this.props.gridDims.width,
21502
+ height: this.props.gridDims.height,
21491
21503
  };
21492
21504
  });
21493
21505
  owl.onWillUpdateProps(() => {
@@ -23205,7 +23217,7 @@
23205
23217
  shiftingMode: "none",
23206
23218
  });
23207
23219
  onResizeHighlight(isLeft, isTop) {
23208
- const activeSheet = this.env.model.getters.getActiveSheet();
23220
+ const activeSheetId = this.env.model.getters.getActiveSheetId();
23209
23221
  this.highlightState.shiftingMode = "isResizing";
23210
23222
  const z = this.props.zone;
23211
23223
  const pivotCol = isLeft ? z.right : z.left;
@@ -23214,11 +23226,10 @@
23214
23226
  let lastRow = isTop ? z.top : z.bottom;
23215
23227
  let currentZone = z;
23216
23228
  this.env.model.dispatch("START_CHANGE_HIGHLIGHT", {
23217
- range: this.env.model.getters.getRangeDataFromZone(activeSheet.id, currentZone),
23229
+ range: this.env.model.getters.getRangeDataFromZone(activeSheetId, currentZone),
23218
23230
  });
23219
23231
  const mouseMove = (col, row) => {
23220
23232
  if (lastCol !== col || lastRow !== row) {
23221
- const activeSheetId = this.env.model.getters.getActiveSheetId();
23222
23233
  lastCol = clip(col === -1 ? lastCol : col, 0, this.env.model.getters.getNumberCols(activeSheetId) - 1);
23223
23234
  lastRow = clip(row === -1 ? lastRow : row, 0, this.env.model.getters.getNumberRows(activeSheetId) - 1);
23224
23235
  let newZone = {
@@ -23230,7 +23241,7 @@
23230
23241
  newZone = this.env.model.getters.expandZone(activeSheetId, newZone);
23231
23242
  if (!isEqual(newZone, currentZone)) {
23232
23243
  this.env.model.dispatch("CHANGE_HIGHLIGHT", {
23233
- range: this.env.model.getters.getRangeDataFromZone(activeSheet.id, newZone),
23244
+ range: this.env.model.getters.getRangeFromZone(activeSheetId, this.env.model.getters.getUnboundedZone(activeSheetId, newZone)).rangeData,
23234
23245
  });
23235
23246
  currentZone = newZone;
23236
23247
  }
@@ -23277,7 +23288,7 @@
23277
23288
  newZone = this.env.model.getters.expandZone(activeSheetId, newZone);
23278
23289
  if (!isEqual(newZone, currentZone)) {
23279
23290
  this.env.model.dispatch("CHANGE_HIGHLIGHT", {
23280
- range: this.env.model.getters.getRangeDataFromZone(activeSheetId, newZone),
23291
+ range: this.env.model.getters.getRangeFromZone(activeSheetId, this.env.model.getters.getUnboundedZone(activeSheetId, newZone)).rangeData,
23281
23292
  });
23282
23293
  currentZone = newZone;
23283
23294
  }
@@ -24599,6 +24610,16 @@
24599
24610
  63: "333333",
24600
24611
  64: "000000",
24601
24612
  65: "FFFFFF", // system background
24613
+ };
24614
+ const IMAGE_MIMETYPE_EXTENSION_MAPPING = {
24615
+ "image/avif": "avif",
24616
+ "image/bmp": "bmp",
24617
+ "image/gif": "gif",
24618
+ "image/vnd.microsoft.icon": "ico",
24619
+ "image/jpeg": "jpeg",
24620
+ "image/png": "png",
24621
+ "image/tiff": "tiff",
24622
+ "image/webp": "webp",
24602
24623
  };
24603
24624
 
24604
24625
  /**
@@ -27081,6 +27102,11 @@
27081
27102
  <Override ContentType="${contentType}" PartName="${partName}" />
27082
27103
  `;
27083
27104
  }
27105
+ function createDefaultXMLElement(extension, contentType) {
27106
+ return escapeXml /*xml*/ `
27107
+ <Default Extension="${extension}" ContentType="${contentType}" />
27108
+ `;
27109
+ }
27084
27110
  function joinXmlNodes(xmlNodes) {
27085
27111
  return new XMLString(xmlNodes.join("\n"));
27086
27112
  }
@@ -31522,6 +31548,7 @@
31522
31548
  "getMerge",
31523
31549
  "getMergesInZone",
31524
31550
  "isSingleCellOrMerge",
31551
+ "getSelectionRangeString",
31525
31552
  ];
31526
31553
  nextId = 1;
31527
31554
  merges = {};
@@ -31609,6 +31636,26 @@
31609
31636
  .map((mergeId) => this.getMergeById(sheetId, mergeId))
31610
31637
  .filter(isDefined$1);
31611
31638
  }
31639
+ /**
31640
+ * Same as `getRangeString` but add all necessary merge to the range to make it a valid selection
31641
+ */
31642
+ getSelectionRangeString(range, forSheetId) {
31643
+ const rangeImpl = RangeImpl.fromRange(range, this.getters);
31644
+ const expandedZone = this.getters.expandZone(rangeImpl.sheetId, rangeImpl.zone);
31645
+ const expandedRange = rangeImpl.clone({
31646
+ zone: {
31647
+ ...expandedZone,
31648
+ bottom: rangeImpl.isFullCol ? undefined : expandedZone.bottom,
31649
+ right: rangeImpl.isFullRow ? undefined : expandedZone.right,
31650
+ },
31651
+ });
31652
+ const rangeString = this.getters.getRangeString(expandedRange, forSheetId);
31653
+ if (this.isSingleCellOrMerge(rangeImpl.sheetId, rangeImpl.zone)) {
31654
+ const { sheetName, xc } = splitReference(rangeString);
31655
+ return `${sheetName !== undefined ? getCanonicalSheetName(sheetName) + "!" : ""}${xc.split(":")[0]}`;
31656
+ }
31657
+ return rangeString;
31658
+ }
31612
31659
  /**
31613
31660
  * Return true if the zone intersects an existing merge:
31614
31661
  * if they have at least a common cell
@@ -31941,12 +31988,12 @@
31941
31988
  }
31942
31989
  static getters = [
31943
31990
  "getRangeString",
31944
- "getSelectionRangeString",
31945
31991
  "getRangeFromSheetXC",
31946
31992
  "createAdaptedRanges",
31947
31993
  "getRangeDataFromXc",
31948
31994
  "getRangeDataFromZone",
31949
31995
  "getRangeFromRangeData",
31996
+ "getRangeFromZone",
31950
31997
  ];
31951
31998
  // ---------------------------------------------------------------------------
31952
31999
  // Command Handling
@@ -32195,21 +32242,6 @@
32195
32242
  const rangeInterface = { prefixSheet, zone, sheetId, invalidSheetName, parts };
32196
32243
  return new RangeImpl(rangeInterface, this.getters.getSheetSize).orderZone();
32197
32244
  }
32198
- /**
32199
- * Same as `getRangeString` but add all necessary merge to the range to make it a valid selection
32200
- */
32201
- getSelectionRangeString(range, forSheetId) {
32202
- const rangeImpl = RangeImpl.fromRange(range, this.getters);
32203
- const expandedZone = this.getters.expandZone(rangeImpl.sheetId, rangeImpl.zone);
32204
- const expandedRange = rangeImpl.clone({
32205
- zone: {
32206
- ...expandedZone,
32207
- bottom: rangeImpl.isFullCol ? undefined : expandedZone.bottom,
32208
- right: rangeImpl.isFullRow ? undefined : expandedZone.right,
32209
- },
32210
- });
32211
- return this.getRangeString(expandedRange, forSheetId);
32212
- }
32213
32245
  /**
32214
32246
  * Gets the string that represents the range as it is at the moment of the call.
32215
32247
  * The string will be prefixed with the sheet name if the call specified a sheet id in `forSheetId`
@@ -32266,6 +32298,17 @@
32266
32298
  getRangeDataFromZone(sheetId, zone) {
32267
32299
  return { _sheetId: sheetId, _zone: zone };
32268
32300
  }
32301
+ getRangeFromZone(sheetId, zone) {
32302
+ return new RangeImpl({
32303
+ sheetId,
32304
+ zone,
32305
+ parts: [
32306
+ { colFixed: false, rowFixed: false },
32307
+ { colFixed: false, rowFixed: false },
32308
+ ],
32309
+ prefixSheet: false,
32310
+ }, this.getters.getSheetSize);
32311
+ }
32269
32312
  getRangeFromRangeData(data) {
32270
32313
  const rangeInterface = {
32271
32314
  prefixSheet: false,
@@ -32351,6 +32394,7 @@
32351
32394
  "getPaneDivisions",
32352
32395
  "checkZonesExistInSheet",
32353
32396
  "getCommandZones",
32397
+ "getUnboundedZone",
32354
32398
  ];
32355
32399
  sheetIdsMapName = {};
32356
32400
  orderedSheetIds = [];
@@ -32676,6 +32720,16 @@
32676
32720
  right: this.getNumberCols(sheetId) - 1,
32677
32721
  };
32678
32722
  }
32723
+ getUnboundedZone(sheetId, zone) {
32724
+ const isFullRow = zone.left === 0 && zone.right === this.getNumberCols(sheetId) - 1;
32725
+ const isFullCol = zone.top === 0 && zone.bottom === this.getNumberRows(sheetId) - 1;
32726
+ return {
32727
+ ...zone,
32728
+ bottom: isFullCol ? undefined : zone.bottom,
32729
+ // cannot be unbounded in the 2 dimensions at once
32730
+ right: isFullRow && !isFullCol ? undefined : zone.right,
32731
+ };
32732
+ }
32679
32733
  getPaneDivisions(sheetId) {
32680
32734
  return this.getSheet(sheetId).panes;
32681
32735
  }
@@ -38726,11 +38780,11 @@
38726
38780
  return 0 /* CommandResult.Success */;
38727
38781
  }
38728
38782
  handleEvent(event) {
38729
- const xc = zoneToXc(event.anchor.zone);
38730
38783
  const inputSheetId = this.activeSheet;
38731
38784
  const sheetId = this.getters.getActiveSheetId();
38732
- const sheetName = this.getters.getSheetName(sheetId);
38733
- this.add([sheetId === inputSheetId ? xc : `${getCanonicalSheetName(sheetName)}!${xc}`]);
38785
+ const zone = event.anchor.zone;
38786
+ const range = this.getters.getRangeFromZone(sheetId, event.type === "HeadersSelected" ? this.getters.getUnboundedZone(sheetId, zone) : zone);
38787
+ this.add([this.getters.getSelectionRangeString(range, inputSheetId)]);
38734
38788
  }
38735
38789
  handle(cmd) {
38736
38790
  switch (cmd.type) {
@@ -41188,12 +41242,20 @@
41188
41242
  if (this.mode !== "selecting") {
41189
41243
  return;
41190
41244
  }
41245
+ const sheetId = this.getters.getActiveSheetId();
41246
+ let unboundedZone;
41247
+ if (event.type === "HeadersSelected") {
41248
+ unboundedZone = this.getters.getUnboundedZone(sheetId, event.anchor.zone);
41249
+ }
41250
+ else {
41251
+ unboundedZone = event.anchor.zone;
41252
+ }
41191
41253
  switch (event.mode) {
41192
41254
  case "newAnchor":
41193
- this.insertSelectedRange(event.anchor.zone);
41255
+ this.insertSelectedRange(unboundedZone);
41194
41256
  break;
41195
41257
  default:
41196
- this.replaceSelectedRanges(event.anchor.zone);
41258
+ this.replaceSelectedRanges(unboundedZone);
41197
41259
  break;
41198
41260
  }
41199
41261
  }
@@ -41250,7 +41312,9 @@
41250
41312
  }
41251
41313
  break;
41252
41314
  case "START_CHANGE_HIGHLIGHT":
41315
+ // FIXME: thiws whole ordeal could be handled with the Selection Processor which would extend the feature to selection inputs
41253
41316
  this.dispatch("STOP_COMPOSER_RANGE_SELECTION");
41317
+ // FIXME: we should check range SheetId compared to this.activeSheetId r maybe not have a sheetId in the payload ??
41254
41318
  const range = this.getters.getRangeFromRangeData(cmd.range);
41255
41319
  const previousRefToken = this.currentTokens
41256
41320
  .filter((token) => token.type === "REFERENCE")
@@ -41604,14 +41668,11 @@
41604
41668
  const ref = this.getZoneReference(zone);
41605
41669
  this.replaceText(ref, this.selectionInitialStart, this.selectionEnd);
41606
41670
  }
41607
- getZoneReference(zone, fixedParts = [{ colFixed: false, rowFixed: false }]) {
41671
+ getZoneReference(zone) {
41672
+ const inputSheetId = this.getters.getCurrentEditedCell().sheetId;
41608
41673
  const sheetId = this.getters.getActiveSheetId();
41609
- let selectedXc = this.getters.zoneToXC(sheetId, zone, fixedParts);
41610
- if (this.getters.getCurrentEditedCell().sheetId !== this.getters.getActiveSheetId()) {
41611
- const sheetName = getCanonicalSheetName(this.getters.getSheetName(this.getters.getActiveSheetId()));
41612
- selectedXc = `${sheetName}!${selectedXc}`;
41613
- }
41614
- return selectedXc;
41674
+ const range = this.getters.getRangeFromZone(sheetId, zone);
41675
+ return this.getters.getSelectionRangeString(range, inputSheetId);
41615
41676
  }
41616
41677
  getRangeReference(range, fixedParts = [{ colFixed: false, rowFixed: false }]) {
41617
41678
  let _fixedParts = [...fixedParts];
@@ -42465,7 +42526,7 @@
42465
42526
  const file = await this.getImageFromUser();
42466
42527
  const path = await this.fileStore.upload(file);
42467
42528
  const size = await this.getImageSize(path);
42468
- return { path, size };
42529
+ return { path, size, mimetype: file.type };
42469
42530
  }
42470
42531
  getImageFromUser() {
42471
42532
  return new Promise((resolve, reject) => {
@@ -47532,8 +47593,7 @@
47532
47593
  // Figures and Charts
47533
47594
  let drawingNode = escapeXml ``;
47534
47595
  const drawingRelIds = [];
47535
- const charts = sheet.charts;
47536
- for (const chart of charts) {
47596
+ for (const chart of sheet.charts) {
47537
47597
  const xlsxChartId = convertChartId(chart.id);
47538
47598
  const chartRelId = addRelsToFile(construct.relsFiles, `xl/drawings/_rels/drawing${sheetIndex}.xml.rels`, {
47539
47599
  target: `../charts/chart${xlsxChartId}.xml`,
@@ -47542,20 +47602,27 @@
47542
47602
  drawingRelIds.push(chartRelId);
47543
47603
  files.push(createXMLFile(createChart(chart, sheetIndex, data), `xl/charts/chart${xlsxChartId}.xml`, "chart"));
47544
47604
  }
47545
- const images = sheet.images;
47546
- for (const image of images) {
47605
+ for (const image of sheet.images) {
47606
+ const mimeType = image.data.mimetype;
47607
+ if (mimeType === undefined)
47608
+ continue;
47609
+ const extension = IMAGE_MIMETYPE_EXTENSION_MAPPING[mimeType];
47610
+ // only support exporting images with mimetypes specified in the mapping
47611
+ if (extension === undefined)
47612
+ continue;
47547
47613
  const xlsxImageId = convertImageId(image.id);
47614
+ let imageFileName = `image${xlsxImageId}.${extension}`;
47548
47615
  const imageRelId = addRelsToFile(construct.relsFiles, `xl/drawings/_rels/drawing${sheetIndex}.xml.rels`, {
47549
- target: `../media/image${xlsxImageId}`,
47616
+ target: `../media/${imageFileName}`,
47550
47617
  type: XLSX_RELATION_TYPE.image,
47551
47618
  });
47552
47619
  drawingRelIds.push(imageRelId);
47553
47620
  files.push({
47554
- path: `xl/media/image${xlsxImageId}`,
47555
- imagePath: image.data.path,
47621
+ path: `xl/media/${imageFileName}`,
47622
+ imageSrc: image.data.path,
47556
47623
  });
47557
47624
  }
47558
- const drawings = [...charts, ...images];
47625
+ const drawings = [...sheet.charts, ...sheet.images];
47559
47626
  if (drawings.length) {
47560
47627
  const drawingRelId = addRelsToFile(construct.relsFiles, `xl/worksheets/_rels/sheet${sheetIndex}.xml.rels`, {
47561
47628
  target: `../drawings/drawing${sheetIndex}.xml`,
@@ -47674,6 +47741,8 @@
47674
47741
  }
47675
47742
  function createContentTypes(files) {
47676
47743
  const overrideNodes = [];
47744
+ // hard-code supported image mimetypes
47745
+ const imageDefaultNodes = Object.entries(IMAGE_MIMETYPE_EXTENSION_MAPPING).map(([mimetype, extension]) => createDefaultXMLElement(extension, mimetype));
47677
47746
  for (const file of files) {
47678
47747
  if ("contentType" in file && file.contentType) {
47679
47748
  overrideNodes.push(createOverride("/" + file.path, CONTENT_TYPES[file.contentType]));
@@ -47689,6 +47758,7 @@
47689
47758
  ];
47690
47759
  const xml = escapeXml /*xml*/ `
47691
47760
  <Types xmlns="${NAMESPACE["Types"]}">
47761
+ ${joinXmlNodes(Object.values(imageDefaultNodes))}
47692
47762
  <Default ${formatAttributes(relsAttributes)} />
47693
47763
  <Default ${formatAttributes(xmlAttributes)} />
47694
47764
  ${joinXmlNodes(overrideNodes)}
@@ -47780,7 +47850,7 @@
47780
47850
  this.coreGetters.getRangeDataFromXc = this.range.getRangeDataFromXc.bind(this.range);
47781
47851
  this.coreGetters.getRangeDataFromZone = this.range.getRangeDataFromZone.bind(this.range);
47782
47852
  this.coreGetters.getRangeFromRangeData = this.range.getRangeFromRangeData.bind(this.range);
47783
- this.coreGetters.getSelectionRangeString = this.range.getSelectionRangeString.bind(this.range);
47853
+ this.coreGetters.getRangeFromZone = this.range.getRangeFromZone.bind(this.range);
47784
47854
  this.getters = {
47785
47855
  isReadonly: () => this.config.mode === "readonly" || this.config.mode === "dashboard",
47786
47856
  isDashboard: () => this.config.mode === "dashboard",
@@ -48303,9 +48373,9 @@
48303
48373
  Object.defineProperty(exports, '__esModule', { value: true });
48304
48374
 
48305
48375
 
48306
- __info__.version = '16.4.0-alpha.2';
48307
- __info__.date = '2023-05-26T13:40:49.561Z';
48308
- __info__.hash = '82328ad';
48376
+ __info__.version = '16.4.0-alpha.3';
48377
+ __info__.date = '2023-06-02T10:13:30.127Z';
48378
+ __info__.hash = '059d66a';
48309
48379
 
48310
48380
 
48311
48381
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);