8
0

liststylesui.js 8.3 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 list/liststylesui
  7. */
  8. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  9. import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
  10. import SplitButtonView from '@ckeditor/ckeditor5-ui/src/dropdown/button/splitbuttonview';
  11. import {
  12. createDropdown,
  13. addToolbarToDropdown
  14. } from '@ckeditor/ckeditor5-ui/src/dropdown/utils';
  15. import bulletedListIcon from '../theme/icons/bulletedlist.svg';
  16. import numberedListIcon from '../theme/icons/numberedlist.svg';
  17. import listStyleDiscIcon from '../theme/icons/liststyledisc.svg';
  18. import listStyleCircleIcon from '../theme/icons/liststylecircle.svg';
  19. import listStyleSquareIcon from '../theme/icons/liststylesquare.svg';
  20. import listStyleDecimalIcon from '../theme/icons/liststyledecimal.svg';
  21. import listStyleDecimalWithLeadingZeroIcon from '../theme/icons/liststyledecimalleadingzero.svg';
  22. import listStyleLowerRomanIcon from '../theme/icons/liststylelowerroman.svg';
  23. import listStyleUpperRomanIcon from '../theme/icons/liststyleupperroman.svg';
  24. import listStyleLowerLatinIcon from '../theme/icons/liststylelowerlatin.svg';
  25. import listStyleUpperLatinIcon from '../theme/icons/liststyleupperlatin.svg';
  26. import '../theme/liststyles.css';
  27. /**
  28. * The list styles UI plugin. It introduces the extended `'bulletedList'` and `'numberedList'` toolbar
  29. * buttons that allow users change styles of individual lists in the content.
  30. *
  31. * **Note**: Buttons introduces by this plugin override implementations from the {@link module:list/listui~ListUI}
  32. * (because they share the same names).
  33. *
  34. * @extends module:core/plugin~Plugin
  35. */
  36. export default class ListStylesUI extends Plugin {
  37. /**
  38. * @inheritDoc
  39. */
  40. static get pluginName() {
  41. return 'ListStylesUI';
  42. }
  43. init() {
  44. const editor = this.editor;
  45. const t = editor.locale.t;
  46. editor.ui.componentFactory.add( 'bulletedList', getSplitButtonCreator( {
  47. editor,
  48. parentCommandName: 'bulletedList',
  49. buttonLabel: t( 'Bulleted List' ),
  50. buttonIcon: bulletedListIcon,
  51. toolbarAriaLabel: t( 'Bulleted list styles toolbar' ),
  52. styleDefinitions: [
  53. {
  54. label: t( 'Toggle the disc list style' ),
  55. tooltip: t( 'Disc' ),
  56. type: 'disc',
  57. icon: listStyleDiscIcon
  58. },
  59. {
  60. label: t( 'Toggle the circle list style' ),
  61. tooltip: t( 'Circle' ),
  62. type: 'circle',
  63. icon: listStyleCircleIcon
  64. },
  65. {
  66. label: t( 'Toggle the square list style' ),
  67. tooltip: t( 'Square' ),
  68. type: 'square',
  69. icon: listStyleSquareIcon
  70. }
  71. ]
  72. } ) );
  73. editor.ui.componentFactory.add( 'numberedList', getSplitButtonCreator( {
  74. editor,
  75. parentCommandName: 'numberedList',
  76. buttonLabel: t( 'Numbered List' ),
  77. buttonIcon: numberedListIcon,
  78. toolbarAriaLabel: t( 'Numbered list styles toolbar' ),
  79. styleDefinitions: [
  80. {
  81. label: t( 'Toggle the decimal list style' ),
  82. tooltip: t( 'Decimal' ),
  83. type: 'decimal',
  84. icon: listStyleDecimalIcon
  85. },
  86. {
  87. label: t( 'Toggle the decimal with leading zero list style' ),
  88. tooltip: t( 'Decimal with leading zero' ),
  89. type: 'decimal-leading-zero',
  90. icon: listStyleDecimalWithLeadingZeroIcon
  91. },
  92. {
  93. label: t( 'Toggle the lower–roman list style' ),
  94. tooltip: t( 'Lower–roman' ),
  95. type: 'lower-roman',
  96. icon: listStyleLowerRomanIcon
  97. },
  98. {
  99. label: t( 'Toggle the upper–roman list style' ),
  100. tooltip: t( 'Upper-roman' ),
  101. type: 'upper-roman',
  102. icon: listStyleUpperRomanIcon
  103. },
  104. {
  105. label: t( 'Toggle the lower–latin list style' ),
  106. tooltip: t( 'Lower-latin' ),
  107. type: 'lower-latin',
  108. icon: listStyleLowerLatinIcon
  109. },
  110. {
  111. label: t( 'Toggle the upper–latin list style' ),
  112. tooltip: t( 'Upper-latin' ),
  113. type: 'upper-latin',
  114. icon: listStyleUpperLatinIcon
  115. }
  116. ]
  117. } ) );
  118. }
  119. }
  120. // A helper that returns a function that creates a split button with a toolbar in the dropdown,
  121. // which in turn contains buttons allowing users to change list styles in the context of the current selection.
  122. //
  123. // @param {Object} options
  124. // @param {module:core/editor/editor~Editor} options.editor
  125. // @param {'bulletedList'|'numberedList'} options.parentCommandName The name of the higher-order editor command associated with
  126. // the set of particular list styles (e.g. "bulletedList" for "disc", "circle", and "square" styles).
  127. // @param {String} options.buttonLabel Label of the main part of the split button.
  128. // @param {String} options.buttonIcon The SVG string of an icon for the main part of the split button.
  129. // @param {String} options.toolbarAriaLabel The ARIA label for the toolbar in the split button dropdown.
  130. // @param {Object} options.styleDefinitions Definitions of the style buttons.
  131. // @returns {Function} A function that can be passed straight into {@link module:ui/componentfactory~ComponentFactory#add}.
  132. function getSplitButtonCreator( { editor, parentCommandName, buttonLabel, buttonIcon, toolbarAriaLabel, styleDefinitions } ) {
  133. const parentCommand = editor.commands.get( parentCommandName );
  134. const listStylesCommand = editor.commands.get( 'listStyles' );
  135. // @param {module:utils/locale~Locale} locale
  136. // @returns {module:ui/dropdown/dropdownview~DropdownView}
  137. return locale => {
  138. const dropdownView = createDropdown( locale, SplitButtonView );
  139. const splitButtonView = dropdownView.buttonView;
  140. const styleButtonCreator = getStyleButtonCreator( { editor, parentCommandName, listStylesCommand } );
  141. addToolbarToDropdown( dropdownView, styleDefinitions.map( styleButtonCreator ) );
  142. dropdownView.bind( 'isEnabled' ).to( parentCommand );
  143. dropdownView.toolbarView.ariaLabel = toolbarAriaLabel;
  144. dropdownView.class = 'ck-list-styles-dropdown';
  145. splitButtonView.on( 'execute', () => {
  146. editor.execute( parentCommandName );
  147. editor.editing.view.focus();
  148. } );
  149. splitButtonView.set( {
  150. label: buttonLabel,
  151. icon: buttonIcon,
  152. tooltip: true,
  153. isToggleable: true
  154. } );
  155. splitButtonView.bind( 'isOn' ).to( parentCommand, 'value', value => !!value );
  156. return dropdownView;
  157. };
  158. }
  159. // A helper that returns a function (factory) that creates individual buttons used by users to change styles
  160. // of lists.
  161. //
  162. // @param {Object} options
  163. // @param {module:core/editor/editor~Editor} options.editor
  164. // @param {module:list/liststylescommand~ListStylesCommand} options.listStylesCommand The instance of the `ListStylesCommand` class.
  165. // @param {'bulletedList'|'numberedList'} options.parentCommandName The name of the higher-order command associated with a
  166. // particular list style (e.g. "bulletedList" is associated with "square" and "numberedList" is associated with "roman").
  167. // @returns {Function} A function that can be passed straight into {@link module:ui/componentfactory~ComponentFactory#add}.
  168. function getStyleButtonCreator( { editor, listStylesCommand, parentCommandName } ) {
  169. const locale = editor.locale;
  170. const parentCommand = editor.commands.get( parentCommandName );
  171. // @param {String} label The label of the style button.
  172. // @param {String} type The type of the style button (e.g. "roman" or "circle").
  173. // @param {String} icon The SVG string of an icon of the style button.
  174. // @param {String} tooltip The tooltip text of the button (shorter than verbose label).
  175. // @returns {module:ui/button/buttonview~ButtonView}
  176. return ( { label, type, icon, tooltip } ) => {
  177. const button = new ButtonView( locale );
  178. button.set( { label, icon, tooltip } );
  179. listStylesCommand.on( 'change:value', () => {
  180. button.isOn = listStylesCommand.value === type;
  181. } );
  182. button.on( 'execute', () => {
  183. // If the content the selection is anchored to is a list, let's change its style.
  184. if ( parentCommand.value ) {
  185. // If the current list style is not set in the model or the style is different than the
  186. // one to be applied, simply apply the new style.
  187. if ( listStylesCommand.value !== type ) {
  188. editor.execute( 'listStyles', { type } );
  189. }
  190. // If the style was the same, remove it (the button works as an off toggle).
  191. else {
  192. editor.execute( 'listStyles', { type: 'default' } );
  193. }
  194. }
  195. // If the content the selection is anchored to is not a list, let's create a list of a desired style.
  196. else {
  197. editor.execute( parentCommandName );
  198. editor.execute( 'listStyles', { type } );
  199. }
  200. editor.editing.view.focus();
  201. } );
  202. return button;
  203. };
  204. }