@ckeditor/ckeditor5-widget 47.1.0 → 47.2.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@ CKEditor 5 widget API
2
2
  ========================================
3
3
 
4
4
  [![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-widget.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-widget)
5
- [![Coverage Status](https://coveralls.io/repos/github/ckeditor/ckeditor5/badge.svg?branch=master)](https://coveralls.io/github/ckeditor/ckeditor5?branch=master)
5
+ [![codecov](https://codecov.io/gh/ckeditor/ckeditor5/branch/master/graph/badge.svg)](https://codecov.io/gh/ckeditor/ckeditor5)
6
6
  [![CircleCI](https://circleci.com/gh/ckeditor/ckeditor5.svg?style=shield)](https://app.circleci.com/pipelines/github/ckeditor/ckeditor5?branch=master)
7
7
 
8
8
  This package implements the widget API for CKEditor 5.
package/dist/index.js CHANGED
@@ -860,6 +860,10 @@ const PLUGIN_DISABLED_EDITING_ROOT_CLASS = 'ck-widget__type-around_disabled';
860
860
  const modelSelection = model.document.selection;
861
861
  const schema = model.schema;
862
862
  const editingView = editor.editing.view;
863
+ // Selection expanding/shrinking is handled without the fake caret by the widget plugin.
864
+ if (domEventData.shiftKey) {
865
+ return;
866
+ }
863
867
  const keyCode = domEventData.keyCode;
864
868
  const isForward = isForwardArrowKeyCode(keyCode, editor.locale.contentLanguageDirection);
865
869
  const selectedViewElement = editingView.document.selection.getSelectedElement();
@@ -1318,11 +1322,6 @@ function injectFakeCaret(wrapperDomElement) {
1318
1322
  return;
1319
1323
  }
1320
1324
  const isForward = arrowDownPressed;
1321
- // Navigation is in the opposite direction than the selection direction so this is shrinking of the selection.
1322
- // Selection for sure will not approach any object.
1323
- if (expandSelection && selectionWillShrink(selection, isForward)) {
1324
- return;
1325
- }
1326
1325
  // Find a range between selection and closest limit element.
1327
1326
  const range = findTextRangeFromSelection(editing, selection, isForward);
1328
1327
  // There is no selection position inside the limit element.
@@ -1371,7 +1370,7 @@ function injectFakeCaret(wrapperDomElement) {
1371
1370
  */ function findTextRangeFromSelection(editing, selection, isForward) {
1372
1371
  const model = editing.model;
1373
1372
  if (isForward) {
1374
- const startPosition = selection.isCollapsed ? selection.focus : selection.getLastPosition();
1373
+ const startPosition = selection.focus;
1375
1374
  const endPosition = getNearestNonInlineLimit(model, startPosition, 'forward');
1376
1375
  // There is no limit element, browser should handle this.
1377
1376
  if (!endPosition) {
@@ -1383,7 +1382,7 @@ function injectFakeCaret(wrapperDomElement) {
1383
1382
  return model.createRange(startPosition, lastRangePosition);
1384
1383
  }
1385
1384
  } else {
1386
- const endPosition = selection.isCollapsed ? selection.focus : selection.getFirstPosition();
1385
+ const endPosition = selection.focus;
1387
1386
  const startPosition = getNearestNonInlineLimit(model, endPosition, 'backward');
1388
1387
  // There is no limit element, browser should handle this.
1389
1388
  if (!startPosition) {
@@ -1481,9 +1480,6 @@ function injectFakeCaret(wrapperDomElement) {
1481
1480
  }
1482
1481
  return true;
1483
1482
  }
1484
- function selectionWillShrink(selection, isForward) {
1485
- return !selection.isCollapsed && selection.isBackward == isForward;
1486
- }
1487
1483
 
1488
1484
  /**
1489
1485
  * The widget plugin. It enables base support for widgets.
@@ -1654,6 +1650,7 @@ function selectionWillShrink(selection, isForward) {
1654
1650
  evt.stop();
1655
1651
  }
1656
1652
  }, {
1653
+ context: (node)=>node.is('editableElement'),
1657
1654
  priority: 'low'
1658
1655
  });
1659
1656
  // Add the information about the keystrokes to the accessibility database.
@@ -1792,53 +1789,70 @@ function selectionWillShrink(selection, isForward) {
1792
1789
  const model = this.editor.model;
1793
1790
  const schema = model.schema;
1794
1791
  const modelSelection = model.document.selection;
1795
- const objectElement = modelSelection.getSelectedElement();
1792
+ const selectedElement = modelSelection.getSelectedElement();
1796
1793
  const direction = getLocalizedArrowKeyCodeDirection(keyCode, this.editor.locale.contentLanguageDirection);
1797
1794
  const isForward = direction == 'down' || direction == 'right';
1798
1795
  const isVerticalNavigation = direction == 'up' || direction == 'down';
1799
- // If object element is selected.
1800
- if (objectElement && schema.isObject(objectElement)) {
1801
- const position = isForward ? modelSelection.getLastPosition() : modelSelection.getFirstPosition();
1802
- const newRange = schema.getNearestSelectionRange(position, isForward ? 'forward' : 'backward');
1803
- if (newRange) {
1796
+ // Collapsing a non-collapsed selection.
1797
+ if (!domEventData.shiftKey && !modelSelection.isCollapsed) {
1798
+ // If object element is selected or object is at the edge of selection.
1799
+ if (hasObjectAtEdge(modelSelection, schema)) {
1800
+ const position = isForward ? modelSelection.getLastPosition() : modelSelection.getFirstPosition();
1801
+ const newRange = schema.getNearestSelectionRange(position, isForward ? 'forward' : 'backward');
1802
+ if (newRange) {
1803
+ model.change((writer)=>{
1804
+ writer.setSelection(newRange);
1805
+ });
1806
+ domEventData.preventDefault();
1807
+ eventInfo.stop();
1808
+ }
1809
+ }
1810
+ // Else is handled by the browser.
1811
+ return;
1812
+ }
1813
+ // Adjust selection for fake caret and for selection direction when single object is selected.
1814
+ const originalSelection = getModelSelectionAdjusted(model, isForward);
1815
+ // Clone current selection to use it as a probe. We must leave default selection as it is so it can return
1816
+ // to its current state after undo.
1817
+ const probe = model.createSelection(originalSelection);
1818
+ model.modifySelection(probe, {
1819
+ direction: isForward ? 'forward' : 'backward'
1820
+ });
1821
+ // The selection didn't change so there is nothing there.
1822
+ if (probe.isEqual(originalSelection)) {
1823
+ return;
1824
+ }
1825
+ // Move probe one step further to make it visually recognizable.
1826
+ if (probe.focus.isTouching(originalSelection.focus)) {
1827
+ model.modifySelection(probe, {
1828
+ direction: isForward ? 'forward' : 'backward'
1829
+ });
1830
+ }
1831
+ const lastSelectedNode = isForward ? originalSelection.focus.nodeBefore : originalSelection.focus.nodeAfter;
1832
+ const nodeBeforeProbe = probe.focus.nodeBefore;
1833
+ const nodeAfterProbe = probe.focus.nodeAfter;
1834
+ const lastProbeNode = isForward ? nodeBeforeProbe : nodeAfterProbe;
1835
+ if (domEventData.shiftKey) {
1836
+ // Expand selection from a selected object or include object in selection.
1837
+ if (selectedElement && schema.isObject(selectedElement) || lastProbeNode && schema.isObject(lastProbeNode) || lastSelectedNode && schema.isObject(lastSelectedNode)) {
1804
1838
  model.change((writer)=>{
1805
- writer.setSelection(newRange);
1839
+ writer.setSelection(probe);
1806
1840
  });
1807
1841
  domEventData.preventDefault();
1808
1842
  eventInfo.stop();
1809
1843
  }
1810
- return;
1811
- }
1812
- // Handle collapsing of the selection when there is any widget on the edge of selection.
1813
- // This is needed because browsers have problems with collapsing such selection.
1814
- if (!modelSelection.isCollapsed && !domEventData.shiftKey) {
1815
- const firstPosition = modelSelection.getFirstPosition();
1816
- const lastPosition = modelSelection.getLastPosition();
1817
- const firstSelectedNode = firstPosition.nodeAfter;
1818
- const lastSelectedNode = lastPosition.nodeBefore;
1819
- if (firstSelectedNode && schema.isObject(firstSelectedNode) || lastSelectedNode && schema.isObject(lastSelectedNode)) {
1844
+ } else {
1845
+ // Select an object when moving caret over it.
1846
+ if (lastProbeNode && schema.isObject(lastProbeNode)) {
1847
+ if (schema.isInline(lastProbeNode) && isVerticalNavigation) {
1848
+ return;
1849
+ }
1820
1850
  model.change((writer)=>{
1821
- writer.setSelection(isForward ? lastPosition : firstPosition);
1851
+ writer.setSelection(lastProbeNode, 'on');
1822
1852
  });
1823
1853
  domEventData.preventDefault();
1824
1854
  eventInfo.stop();
1825
1855
  }
1826
- return;
1827
- }
1828
- // Return if not collapsed.
1829
- if (!modelSelection.isCollapsed) {
1830
- return;
1831
- }
1832
- // If selection is next to object element.
1833
- const objectElementNextToSelection = this._getObjectElementNextToSelection(isForward);
1834
- if (objectElementNextToSelection && schema.isObject(objectElementNextToSelection)) {
1835
- // Do not select an inline widget while handling up/down arrow.
1836
- if (schema.isInline(objectElementNextToSelection) && isVerticalNavigation) {
1837
- return;
1838
- }
1839
- this._setSelectionOverElement(objectElementNextToSelection);
1840
- domEventData.preventDefault();
1841
- eventInfo.stop();
1842
1856
  }
1843
1857
  }
1844
1858
  /**
@@ -1919,7 +1933,7 @@ function selectionWillShrink(selection, isForward) {
1919
1933
  return null;
1920
1934
  }
1921
1935
  const objectElement = forward ? probe.focus.nodeBefore : probe.focus.nodeAfter;
1922
- if (!!objectElement && schema.isObject(objectElement)) {
1936
+ if (objectElement && schema.isObject(objectElement)) {
1923
1937
  return objectElement;
1924
1938
  }
1925
1939
  return null;
@@ -1980,12 +1994,8 @@ function selectionWillShrink(selection, isForward) {
1980
1994
  direction
1981
1995
  })){
1982
1996
  const item = nextPosition.parent;
1983
- // Ignore currently selected editable or widget.
1984
- if (item == editableElement || item == selectedElement) {
1985
- continue;
1986
- }
1987
- // Some widget along the way.
1988
- if (isWidget(item)) {
1997
+ // Some widget along the way except the currently selected one.
1998
+ if (isWidget(item) && item != selectedElement) {
1989
1999
  const modelElement = editing.mapper.toModelElement(item);
1990
2000
  // Do not select inline widgets.
1991
2001
  if (!model.schema.isBlock(modelElement)) {
@@ -1996,16 +2006,26 @@ function selectionWillShrink(selection, isForward) {
1996
2006
  return model.createRangeOn(modelElement);
1997
2007
  }
1998
2008
  } else if (item.is('editableElement')) {
2009
+ // Ignore the current editable for text selection,
2010
+ // but use it when widget was selected to be able to jump after the widget.
2011
+ if (item == editableElement && !selectedElement) {
2012
+ continue;
2013
+ }
1999
2014
  const modelPosition = editing.mapper.toModelPosition(nextPosition);
2000
- let newRange = model.schema.getNearestSelectionRange(modelPosition, direction);
2015
+ const newRange = model.schema.getNearestSelectionRange(modelPosition, direction);
2001
2016
  // There is nothing to select so just jump to the next one.
2002
2017
  if (!newRange) {
2003
2018
  continue;
2004
2019
  }
2020
+ // In the same editable while widget was selected - do not select the editable content.
2021
+ if (item == editableElement && selectedElement) {
2022
+ return newRange;
2023
+ }
2005
2024
  // Select the content of editable element when iterating over sibling editable elements
2006
2025
  // or going deeper into nested widgets.
2007
2026
  if (compareArrays(editablePath, item.getPath()) != 'extension') {
2008
- newRange = model.createRangeIn(modelPosition.parent);
2027
+ // Find a limit element closest to the new selection range.
2028
+ return model.createRangeIn(model.schema.getLimitElement(newRange));
2009
2029
  }
2010
2030
  return newRange;
2011
2031
  }
@@ -2034,6 +2054,32 @@ function selectionWillShrink(selection, isForward) {
2034
2054
  return true;
2035
2055
  }
2036
2056
  }
2057
+ /**
2058
+ * Returns true if there is an object on an edge of the given selection.
2059
+ */ function hasObjectAtEdge(modelSelection, schema) {
2060
+ const firstPosition = modelSelection.getFirstPosition();
2061
+ const lastPosition = modelSelection.getLastPosition();
2062
+ const firstSelectedNode = firstPosition.nodeAfter;
2063
+ const lastSelectedNode = lastPosition.nodeBefore;
2064
+ return !!firstSelectedNode && schema.isObject(firstSelectedNode) || !!lastSelectedNode && schema.isObject(lastSelectedNode);
2065
+ }
2066
+ /**
2067
+ * Returns new instance of the model selection adjusted for fake caret and selection direction on widgets.
2068
+ */ function getModelSelectionAdjusted(model, isForward) {
2069
+ const modelSelection = model.document.selection;
2070
+ const selectedElement = modelSelection.getSelectedElement();
2071
+ // Adjust selection for fake caret.
2072
+ const typeAroundFakeCaretPosition = getTypeAroundFakeCaretPosition(modelSelection);
2073
+ if (selectedElement && typeAroundFakeCaretPosition == 'before') {
2074
+ return model.createSelection(selectedElement, 'before');
2075
+ } else if (selectedElement && typeAroundFakeCaretPosition == 'after') {
2076
+ return model.createSelection(selectedElement, 'after');
2077
+ }
2078
+ // Make a copy of selection with adjusted direction for object selected.
2079
+ return model.createSelection(modelSelection.getRanges(), {
2080
+ backward: !!selectedElement && model.schema.isObject(selectedElement) ? !isForward : modelSelection.isBackward
2081
+ });
2082
+ }
2037
2083
  /**
2038
2084
  * Finds the closest ancestor element that is either an editable element or a widget.
2039
2085
  *