indentblock.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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. schema.setAttributeProperties( 'blockIndent', { isFormatting: true } );
  76. indentCommand.registerChildCommand( editor.commands.get( 'indentBlock' ) );
  77. outdentCommand.registerChildCommand( editor.commands.get( 'outdentBlock' ) );
  78. }
  79. /**
  80. * Setups conversion for using offset indents.
  81. *
  82. * @private
  83. */
  84. _setupConversionUsingOffset() {
  85. const conversion = this.editor.conversion;
  86. const locale = this.editor.locale;
  87. const marginProperty = locale.contentLanguageDirection === 'rtl' ? 'margin-right' : 'margin-left';
  88. conversion.for( 'upcast' ).attributeToAttribute( {
  89. view: {
  90. styles: {
  91. [ marginProperty ]: /[\s\S]+/
  92. }
  93. },
  94. model: {
  95. key: 'blockIndent',
  96. value: viewElement => viewElement.getStyle( marginProperty )
  97. }
  98. } );
  99. conversion.for( 'downcast' ).attributeToAttribute( {
  100. model: 'blockIndent',
  101. view: modelAttributeValue => {
  102. return {
  103. key: 'style',
  104. value: {
  105. [ marginProperty ]: modelAttributeValue
  106. }
  107. };
  108. }
  109. } );
  110. }
  111. /**
  112. * Setups conversion for using classes.
  113. *
  114. * @param {Array.<String>} classes
  115. * @private
  116. */
  117. _setupConversionUsingClasses( classes ) {
  118. const definition = {
  119. model: {
  120. key: 'blockIndent',
  121. values: []
  122. },
  123. view: {}
  124. };
  125. for ( const className of classes ) {
  126. definition.model.values.push( className );
  127. definition.view[ className ] = {
  128. key: 'class',
  129. value: [ className ]
  130. };
  131. }
  132. this.editor.conversion.attributeToAttribute( definition );
  133. }
  134. }
  135. /**
  136. * The configuration of the {@link module:indent/indentblock~IndentBlock block indentation feature}.
  137. *
  138. * Read more in {@link module:indent/indentblock~IndentBlockConfig}.
  139. *
  140. * @member {module:indent/indentblock~IndentBlockConfig} module:core/editor/editorconfig~EditorConfig#indentBlock
  141. */
  142. /**
  143. * The configuration of the block indentation feature.
  144. *
  145. * If no {@link module:indent/indentblock~IndentBlockConfig#classes} are set, the block indentation feature will use
  146. * {@link module:indent/indentblock~IndentBlockConfig#offset} and {@link module:indent/indentblock~IndentBlockConfig#unit} to
  147. * create indentation steps.
  148. *
  149. * ClassicEditor
  150. * .create( editorElement, {
  151. * indentBlock: {
  152. * offset: 2,
  153. * unit: 'em'
  154. * }
  155. * } )
  156. * .then( ... )
  157. * .catch( ... );
  158. *
  159. * Alternatively, the block indentation feature may set one of defined {@link module:indent/indentblock~IndentBlockConfig#classes} as
  160. * indentation steps:
  161. *
  162. * ClassicEditor
  163. * .create( editorElement, {
  164. * indentBlock: {
  165. * classes: [
  166. * 'indent-a', // The first step - smallest indentation.
  167. * 'indent-b',
  168. * 'indent-c',
  169. * 'indent-d',
  170. * 'indent-e' // The last step - biggest indentation.
  171. * ]
  172. * }
  173. * } )
  174. * .then( ... )
  175. * .catch( ... );
  176. *
  177. * In the example above only 5 indentation steps will be available.
  178. *
  179. * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
  180. *
  181. * @interface IndentBlockConfig
  182. */
  183. /**
  184. * The size of indentation {@link module:indent/indentblock~IndentBlockConfig#unit units} for each indentation step.
  185. *
  186. * @default 40
  187. * @member {Number} module:indent/indentblock~IndentBlockConfig#offset
  188. */
  189. /**
  190. * The unit used for indentation {@link module:indent/indentblock~IndentBlockConfig#offset}.
  191. *
  192. * @default 'px'
  193. * @member {String} module:indent/indentblock~IndentBlockConfig#unit
  194. */
  195. /**
  196. * 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.
  197. * The {@link module:indent/indentblock~IndentBlockConfig#unit `indentBlock.unit`} and
  198. * {@link module:indent/indentblock~IndentBlockConfig#offset `indentBlock.offset`} properties will be used instead.
  199. *
  200. * @default undefined
  201. * @member {Array.<String>|undefined} module:indent/indentblock~IndentBlockConfig#classes
  202. */