list.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module list/list
  7. */
  8. import ListEngine from './listengine';
  9. import numberedListIcon from '../theme/icons/numberedlist.svg';
  10. import bulletedListIcon from '../theme/icons/bulletedlist.svg';
  11. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  12. import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
  13. /**
  14. * The list feature. It introduces the `numberedList` and `bulletedList` buttons that
  15. * allow to convert paragraphs to and from list items and indent or outdent them.
  16. *
  17. * See also {@link module:list/listengine~ListEngine}.
  18. *
  19. * @extends module:core/plugin~Plugin
  20. */
  21. export default class List extends Plugin {
  22. /**
  23. * @inheritDoc
  24. */
  25. static get requires() {
  26. return [ ListEngine ];
  27. }
  28. /**
  29. * @inheritDoc
  30. */
  31. static get pluginName() {
  32. return 'List';
  33. }
  34. /**
  35. * @inheritDoc
  36. */
  37. init() {
  38. // Create two buttons and link them with numberedList and bulletedList commands.
  39. const t = this.editor.t;
  40. this._addButton( 'numberedList', t( 'Numbered List' ), numberedListIcon );
  41. this._addButton( 'bulletedList', t( 'Bulleted List' ), bulletedListIcon );
  42. // Overwrite default Enter key behavior.
  43. // If Enter key is pressed with selection collapsed in empty list item, outdent it instead of breaking it.
  44. this.listenTo( this.editor.editing.view, 'enter', ( evt, data ) => {
  45. const doc = this.editor.model.document;
  46. const positionParent = doc.selection.getLastPosition().parent;
  47. if ( doc.selection.isCollapsed && positionParent.name == 'listItem' && positionParent.isEmpty ) {
  48. this.editor.execute( 'outdentList' );
  49. data.preventDefault();
  50. evt.stop();
  51. }
  52. } );
  53. // Overwrite default Backspace key behavior.
  54. // If Backspace key is pressed with selection collapsed on first position in first list item, outdent it. #83
  55. this.listenTo( this.editor.editing.view, 'delete', ( evt, data ) => {
  56. // Check conditions from those that require less computations like those immediately available.
  57. if ( data.direction !== 'backward' ) {
  58. return;
  59. }
  60. const selection = this.editor.model.document.selection;
  61. if ( !selection.isCollapsed ) {
  62. return;
  63. }
  64. const firstPosition = selection.getFirstPosition();
  65. if ( !firstPosition.isAtStart ) {
  66. return;
  67. }
  68. const positionParent = firstPosition.parent;
  69. if ( positionParent.name !== 'listItem' ) {
  70. return;
  71. }
  72. const previousIsAListItem = positionParent.previousSibling && positionParent.previousSibling.name === 'listItem';
  73. if ( previousIsAListItem ) {
  74. return;
  75. }
  76. this.editor.execute( 'outdentList' );
  77. data.preventDefault();
  78. evt.stop();
  79. }, { priority: 'high' } );
  80. const getCommandExecuter = commandName => {
  81. return ( data, cancel ) => {
  82. const command = this.editor.commands.get( commandName );
  83. if ( command.isEnabled ) {
  84. this.editor.execute( commandName );
  85. cancel();
  86. }
  87. };
  88. };
  89. this.editor.keystrokes.set( 'Tab', getCommandExecuter( 'indentList' ) );
  90. this.editor.keystrokes.set( 'Shift+Tab', getCommandExecuter( 'outdentList' ) );
  91. }
  92. /**
  93. * Helper method for initializing a button and linking it with an appropriate command.
  94. *
  95. * @private
  96. * @param {String} commandName The name of the command.
  97. * @param {Object} label The button label.
  98. * @param {String} icon The source of the icon.
  99. */
  100. _addButton( commandName, label, icon ) {
  101. const editor = this.editor;
  102. const command = editor.commands.get( commandName );
  103. editor.ui.componentFactory.add( commandName, locale => {
  104. const buttonView = new ButtonView( locale );
  105. buttonView.set( {
  106. label,
  107. icon,
  108. tooltip: true
  109. } );
  110. // Bind button model to command.
  111. buttonView.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
  112. // Execute command.
  113. this.listenTo( buttonView, 'execute', () => editor.execute( commandName ) );
  114. return buttonView;
  115. } );
  116. }
  117. }