indentblock.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /**
  6. * @module indent/indentblock
  7. */
  8. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  9. import IndentBlockCommand from './indentblockcommand';
  10. import IndentUsingOffset from './indentcommandbehavior/indentusingoffset';
  11. import IndentUsingClasses from './indentcommandbehavior/indentusingclasses';
  12. import { addMarginRules } from '@ckeditor/ckeditor5-engine/src/view/styles/margin';
  13. /**
  14. * The block indentation feature.
  15. *
  16. * It registers the `'indentBlock'` and `'outdentBlock'` commands.
  17. *
  18. * If the plugin {@link module:indent/indent~Indent} is defined, it also attaches the `'indentBlock'` and `'outdentBlock'` commands to
  19. * the `'indent'` and `'outdent'` commands.
  20. *
  21. * @extends module:core/plugin~Plugin
  22. */
  23. export default class IndentBlock extends Plugin {
  24. /**
  25. * @inheritDoc
  26. */
  27. constructor( editor ) {
  28. super( editor );
  29. editor.config.define( 'indentBlock', {
  30. offset: 40,
  31. unit: 'px'
  32. } );
  33. }
  34. /**
  35. * @inheritDoc
  36. */
  37. static get pluginName() {
  38. return 'IndentBlock';
  39. }
  40. /**
  41. * @inheritDoc
  42. */
  43. init() {
  44. const editor = this.editor;
  45. const configuration = editor.config.get( 'indentBlock' );
  46. const useOffsetConfig = !configuration.classes || !configuration.classes.length;
  47. const indentConfig = Object.assign( { direction: 'forward' }, configuration );
  48. const outdentConfig = Object.assign( { direction: 'backward' }, configuration );
  49. if ( useOffsetConfig ) {
  50. editor.data.addStyleProcessorRules( addMarginRules );
  51. this._setupConversionUsingOffset( editor.conversion );
  52. editor.commands.add( 'indentBlock', new IndentBlockCommand( editor, new IndentUsingOffset( indentConfig ) ) );
  53. editor.commands.add( 'outdentBlock', new IndentBlockCommand( editor, new IndentUsingOffset( outdentConfig ) ) );
  54. } else {
  55. this._setupConversionUsingClasses( configuration.classes );
  56. editor.commands.add( 'indentBlock', new IndentBlockCommand( editor, new IndentUsingClasses( indentConfig ) ) );
  57. editor.commands.add( 'outdentBlock', new IndentBlockCommand( editor, new IndentUsingClasses( outdentConfig ) ) );
  58. }
  59. }
  60. /**
  61. * @inheritDoc
  62. */
  63. afterInit() {
  64. const editor = this.editor;
  65. const schema = editor.model.schema;
  66. const indentCommand = editor.commands.get( 'indent' );
  67. const outdentCommand = editor.commands.get( 'outdent' );
  68. // Enable block indentation by default in paragraph and default headings.
  69. const knownElements = [ 'paragraph', 'heading1', 'heading2', 'heading3', 'heading4', 'heading5', 'heading6' ];
  70. knownElements.forEach( elementName => {
  71. if ( schema.isRegistered( elementName ) ) {
  72. schema.extend( elementName, { allowAttributes: 'blockIndent' } );
  73. }
  74. } );
  75. indentCommand.registerChildCommand( editor.commands.get( 'indentBlock' ) );
  76. outdentCommand.registerChildCommand( editor.commands.get( 'outdentBlock' ) );
  77. }
  78. /**
  79. * Setups conversion for using offset indents.
  80. *
  81. * @private
  82. */
  83. _setupConversionUsingOffset() {
  84. const conversion = this.editor.conversion;
  85. const locale = this.editor.locale;
  86. const marginProperty = locale.contentLanguageDirection === 'rtl' ? 'margin-right' : 'margin-left';
  87. conversion.for( 'upcast' ).attributeToAttribute( {
  88. view: {
  89. styles: {
  90. [ marginProperty ]: /[\s\S]+/
  91. }
  92. },
  93. model: {
  94. key: 'blockIndent',
  95. value: viewElement => viewElement.getStyle( marginProperty )
  96. }
  97. } );
  98. conversion.for( 'downcast' ).attributeToAttribute( {
  99. model: 'blockIndent',
  100. view: modelAttributeValue => {
  101. return {
  102. key: 'style',
  103. value: {
  104. [ marginProperty ]: modelAttributeValue
  105. }
  106. };
  107. }
  108. } );
  109. }
  110. /**
  111. * Setups conversion for using classes.
  112. *
  113. * @param {Array.<String>} classes
  114. * @private
  115. */
  116. _setupConversionUsingClasses( classes ) {
  117. const definition = {
  118. model: {
  119. key: 'blockIndent',
  120. values: []
  121. },
  122. view: {}
  123. };
  124. for ( const className of classes ) {
  125. definition.model.values.push( className );
  126. definition.view[ className ] = {
  127. key: 'class',
  128. value: [ className ]
  129. };
  130. }
  131. this.editor.conversion.attributeToAttribute( definition );
  132. }
  133. }
  134. /**
  135. * The configuration of the {@link module:indent/indentblock~IndentBlock block indentation feature}.
  136. *
  137. * Read more in {@link module:indent/indentblock~IndentBlockConfig}.
  138. *
  139. * @member {module:indent/indentblock~IndentBlockConfig} module:core/editor/editorconfig~EditorConfig#indentBlock
  140. */
  141. /**
  142. * The configuration of the block indentation feature.
  143. *
  144. * If no {@link module:indent/indentblock~IndentBlockConfig#classes} are set, the block indentation feature will use
  145. * {@link module:indent/indentblock~IndentBlockConfig#offset} and {@link module:indent/indentblock~IndentBlockConfig#unit} to
  146. * create indentation steps.
  147. *
  148. * ClassicEditor
  149. * .create( editorElement, {
  150. * indentBlock: {
  151. * offset: 2,
  152. * unit: 'em'
  153. * }
  154. * } )
  155. * .then( ... )
  156. * .catch( ... );
  157. *
  158. * Alternatively, the block indentation feature may set one of defined {@link module:indent/indentblock~IndentBlockConfig#classes} as
  159. * indentation steps:
  160. *
  161. * ClassicEditor
  162. * .create( editorElement, {
  163. * indentBlock: {
  164. * classes: [
  165. * 'indent-a', // The first step - smallest indentation.
  166. * 'indent-b',
  167. * 'indent-c',
  168. * 'indent-d',
  169. * 'indent-e' // The last step - biggest indentation.
  170. * ]
  171. * }
  172. * } )
  173. * .then( ... )
  174. * .catch( ... );
  175. *
  176. * In the example above only 5 indentation steps will be available.
  177. *
  178. * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
  179. *
  180. * @interface IndentBlockConfig
  181. */
  182. /**
  183. * The size of indentation {@link module:indent/indentblock~IndentBlockConfig#unit units} for each indentation step.
  184. *
  185. * @default 40
  186. * @member {Number} module:indent/indentblock~IndentBlockConfig#offset
  187. */
  188. /**
  189. * The unit used for indentation {@link module:indent/indentblock~IndentBlockConfig#offset}.
  190. *
  191. * @default 'px'
  192. * @member {String} module:indent/indentblock~IndentBlockConfig#unit
  193. */
  194. /**
  195. * An optional list of classes to use for indenting the editor content. If not set or set to an empty array, no classes will be used.
  196. * The {@link module:indent/indentblock~IndentBlockConfig#unit `indentBlock.unit`} and
  197. * {@link module:indent/indentblock~IndentBlockConfig#offset `indentBlock.offset`} properties will be used instead.
  198. *
  199. * @default undefined
  200. * @member {Array.<String>|undefined} module:indent/indentblock~IndentBlockConfig#classes
  201. */