tablepropertiesview.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  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 table/tableproperties/ui/tablepropertiesview
  7. */
  8. import View from '@ckeditor/ckeditor5-ui/src/view';
  9. import ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection';
  10. import submitHandler from '@ckeditor/ckeditor5-ui/src/bindings/submithandler';
  11. import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler';
  12. import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
  13. import FocusCycler from '@ckeditor/ckeditor5-ui/src/focuscycler';
  14. import LabeledView from '@ckeditor/ckeditor5-ui/src/labeledview/labeledview';
  15. import { createLabeledInputText, createLabeledDropdown } from '@ckeditor/ckeditor5-ui/src/labeledview/utils';
  16. import LabelView from '@ckeditor/ckeditor5-ui/src/label/labelview';
  17. import { addListToDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils';
  18. import ToolbarView from '@ckeditor/ckeditor5-ui/src/toolbar/toolbarview';
  19. import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
  20. import {
  21. getBorderStyleLabels,
  22. fillAlignmentToolbar,
  23. getBorderStyleDefinitions
  24. } from '../../ui/utils';
  25. import checkIcon from '@ckeditor/ckeditor5-core/theme/icons/check.svg';
  26. import cancelIcon from '@ckeditor/ckeditor5-core/theme/icons/cancel.svg';
  27. import objectLeftIcon from '@ckeditor/ckeditor5-core/theme/icons/object-left.svg';
  28. import objectRightIcon from '@ckeditor/ckeditor5-core/theme/icons/object-right.svg';
  29. import objectCenterIcon from '@ckeditor/ckeditor5-core/theme/icons/object-center.svg';
  30. import '../../../theme/form.css';
  31. import '../../../theme/tableform.css';
  32. import '../../../theme/tableproperties.css';
  33. import FormRowView from '../../ui/formrowview';
  34. import FormHeaderView from '../../ui/formheaderview';
  35. const ALIGNMENT_ICONS = {
  36. left: objectLeftIcon,
  37. center: objectCenterIcon,
  38. right: objectRightIcon
  39. };
  40. /**
  41. * The class representing a table properties form, allowing users to customize
  42. * certain style aspects of a table, for instance, border, padding, text alignment, etc..
  43. *
  44. * @extends module:ui/view~View
  45. */
  46. export default class TablePropertiesView extends View {
  47. /**
  48. * @inheritDoc
  49. */
  50. constructor( locale ) {
  51. super( locale );
  52. const { borderStyleDropdown, borderWidthInput, borderColorInput, borderRowLabel } = this._createBorderFields();
  53. const { widthInput, operatorLabel, heightInput, dimensionsLabel } = this._createDimensionFields();
  54. const { alignmentToolbar, alignmentLabel } = this._createAlignmentFields();
  55. const { saveButtonView, cancelButtonView } = this._createActionButtons();
  56. /**
  57. * Tracks information about the DOM focus in the form.
  58. *
  59. * @readonly
  60. * @member {module:utils/focustracker~FocusTracker}
  61. */
  62. this.focusTracker = new FocusTracker();
  63. /**
  64. * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
  65. *
  66. * @readonly
  67. * @member {module:utils/keystrokehandler~KeystrokeHandler}
  68. */
  69. this.keystrokes = new KeystrokeHandler();
  70. /**
  71. * A collection of child views in the form.
  72. *
  73. * @readonly
  74. * @type {module:ui/viewcollection~ViewCollection}
  75. */
  76. this.children = this.createCollection();
  77. this.set( {
  78. /**
  79. * The value of the border style.
  80. *
  81. * @observable
  82. * @default 'none'
  83. * @member #borderStyle
  84. */
  85. borderStyle: 'none',
  86. /**
  87. * The value of the border width style.
  88. *
  89. * @observable
  90. * @default ''
  91. * @member #borderWidth
  92. */
  93. borderWidth: '',
  94. /**
  95. * The value of the border color style.
  96. *
  97. * @observable
  98. * @default ''
  99. * @member #borderColor
  100. */
  101. borderColor: '',
  102. /**
  103. * The value of the background color style.
  104. *
  105. * @observable
  106. * @default ''
  107. * @member #backgroundColor
  108. */
  109. backgroundColor: '',
  110. /**
  111. * The value of the table width style.
  112. *
  113. * @observable
  114. * @default ''
  115. * @member #width
  116. */
  117. width: '',
  118. /**
  119. * The value of the table height style.
  120. *
  121. * @observable
  122. * @default ''
  123. * @member #height
  124. */
  125. height: '',
  126. /**
  127. * The value of the table alignment style.
  128. *
  129. * @observable
  130. * @default 'center'
  131. * @member #alignment
  132. */
  133. alignment: 'center',
  134. } );
  135. /**
  136. * A dropdown that allows selecting the style of the table border.
  137. *
  138. * @readonly
  139. * @member {module:ui/dropdown/dropdownview~DropdownView}
  140. */
  141. this.borderStyleDropdown = borderStyleDropdown;
  142. /**
  143. * An input that allows specifying the width of the table border.
  144. *
  145. * @readonly
  146. * @member {module:ui/inputtext/inputtextview~InputTextView}
  147. */
  148. this.borderWidthInput = borderWidthInput;
  149. /**
  150. * An input that allows specifying the color of the table border.
  151. *
  152. * @readonly
  153. * @member {module:ui/inputtext/inputtextview~InputTextView}
  154. */
  155. this.borderColorInput = borderColorInput;
  156. /**
  157. * An input that allows specifying the table background color.
  158. *
  159. * @readonly
  160. * @member {module:ui/inputtext/inputtextview~InputTextView}
  161. */
  162. this.backgroundInput = this._createBackgroundField();
  163. /**
  164. * An input that allows specifying the table width.
  165. *
  166. * @readonly
  167. * @member {module:ui/inputtext/inputtextview~InputTextView}
  168. */
  169. this.widthInput = widthInput;
  170. /**
  171. * An input that allows specifying the table height.
  172. *
  173. * @readonly
  174. * @member {module:ui/inputtext/inputtextview~InputTextView}
  175. */
  176. this.heightInput = heightInput;
  177. /**
  178. * A toolbar with buttons that allow changing the alignment of an entire table.
  179. *
  180. * @readonly
  181. * @member {module:ui/toolbar/toolbar~ToolbarView}
  182. */
  183. this.alignmentToolbar = alignmentToolbar;
  184. /**
  185. * The "Save" button view.
  186. *
  187. * @member {module:ui/button/buttonview~ButtonView}
  188. */
  189. this.saveButtonView = saveButtonView;
  190. /**
  191. * The "Cancel" button view.
  192. *
  193. * @member {module:ui/button/buttonview~ButtonView}
  194. */
  195. this.cancelButtonView = cancelButtonView;
  196. /**
  197. * A collection of views that can be focused in the form.
  198. *
  199. * @readonly
  200. * @protected
  201. * @member {module:ui/viewcollection~ViewCollection}
  202. */
  203. this._focusables = new ViewCollection();
  204. /**
  205. * Helps cycling over {@link #_focusables} in the form.
  206. *
  207. * @readonly
  208. * @protected
  209. * @member {module:ui/focuscycler~FocusCycler}
  210. */
  211. this._focusCycler = new FocusCycler( {
  212. focusables: this._focusables,
  213. focusTracker: this.focusTracker,
  214. keystrokeHandler: this.keystrokes,
  215. actions: {
  216. // Navigate form fields backwards using the Shift + Tab keystroke.
  217. focusPrevious: 'shift + tab',
  218. // Navigate form fields forwards using the Tab key.
  219. focusNext: 'tab'
  220. }
  221. } );
  222. // Form header.
  223. this.children.add( this._createHeaderView() );
  224. // Border row.
  225. this.children.add( new FormRowView( locale, {
  226. labelView: borderRowLabel,
  227. children: [
  228. borderRowLabel,
  229. borderStyleDropdown,
  230. borderColorInput,
  231. borderWidthInput
  232. ],
  233. class: 'ck-table-form__border-row'
  234. } ) );
  235. // Background row.
  236. this.children.add( new FormRowView( locale, {
  237. children: [
  238. this.backgroundInput
  239. ]
  240. } ) );
  241. this.children.add( new FormRowView( locale, {
  242. children: [
  243. // Dimensions row.
  244. new FormRowView( locale, {
  245. labelView: dimensionsLabel,
  246. children: [
  247. dimensionsLabel,
  248. widthInput,
  249. operatorLabel,
  250. heightInput
  251. ],
  252. class: 'ck-table-properties-form__dimensions-row'
  253. } ),
  254. // Alignment row.
  255. new FormRowView( locale, {
  256. labelView: alignmentLabel,
  257. children: [
  258. alignmentLabel,
  259. alignmentToolbar
  260. ],
  261. class: 'ck-table-properties-form__alignment-row'
  262. } )
  263. ]
  264. } ) );
  265. // Action row.
  266. this.children.add( new FormRowView( locale, {
  267. children: [
  268. this.saveButtonView,
  269. this.cancelButtonView,
  270. ],
  271. class: 'ck-table-form__action-row'
  272. } ) );
  273. this.setTemplate( {
  274. tag: 'form',
  275. attributes: {
  276. class: [
  277. 'ck',
  278. 'ck-form',
  279. 'ck-table-form',
  280. 'ck-table-properties-form'
  281. ],
  282. // https://github.com/ckeditor/ckeditor5-link/issues/90
  283. tabindex: '-1'
  284. },
  285. children: this.children
  286. } );
  287. }
  288. /**
  289. * @inheritDoc
  290. */
  291. render() {
  292. super.render();
  293. // Enable the "submit" event for this view. It can be triggered by the #saveButtonView
  294. // which is of the "submit" DOM "type".
  295. submitHandler( {
  296. view: this
  297. } );
  298. [
  299. this.borderStyleDropdown,
  300. this.borderWidthInput,
  301. this.borderColorInput,
  302. this.backgroundInput,
  303. this.widthInput,
  304. this.heightInput,
  305. this.alignmentToolbar,
  306. this.saveButtonView,
  307. this.cancelButtonView,
  308. ].forEach( view => {
  309. // Register the view as focusable.
  310. this._focusables.add( view );
  311. // Register the view in the focus tracker.
  312. this.focusTracker.add( view.element );
  313. } );
  314. // Mainly for closing using "Esc" and navigation using "Tab".
  315. this.keystrokes.listenTo( this.element );
  316. }
  317. /**
  318. * Focuses the fist focusable field in the form.
  319. */
  320. focus() {
  321. this._focusCycler.focusFirst();
  322. }
  323. /**
  324. * Creates the header of the form with a localized label.
  325. *
  326. * @private
  327. * @returns {module:table/ui/formheaderview~FormHeaderView}
  328. */
  329. _createHeaderView() {
  330. const locale = this.locale;
  331. const t = this.t;
  332. return new FormHeaderView( locale, {
  333. label: t( 'Table properties' )
  334. } );
  335. }
  336. /**
  337. * Creates the following form fields:
  338. *
  339. * * {@link #borderStyleDropdown},
  340. * * {@link #borderWidthInput},
  341. * * {@link #borderColorInput}.
  342. *
  343. * @private
  344. * @returns {Object.<String,module:ui/view~View>}
  345. */
  346. _createBorderFields() {
  347. const locale = this.locale;
  348. const t = this.t;
  349. // -- Group label ---------------------------------------------
  350. const borderRowLabel = new LabelView( locale );
  351. borderRowLabel.text = t( 'Border' );
  352. // -- Style ---------------------------------------------------
  353. const styleLabels = getBorderStyleLabels( this.t );
  354. const borderStyleDropdown = new LabeledView( locale, createLabeledDropdown );
  355. borderStyleDropdown.set( {
  356. label: t( 'Style' ),
  357. class: 'ck-table-form__border-style'
  358. } );
  359. borderStyleDropdown.view.buttonView.set( {
  360. isOn: false,
  361. withText: true,
  362. tooltip: t( 'Style' )
  363. } );
  364. borderStyleDropdown.view.buttonView.bind( 'label' ).to( this, 'borderStyle', value => {
  365. return styleLabels[ value ];
  366. } );
  367. borderStyleDropdown.view.on( 'execute', evt => {
  368. this.borderStyle = evt.source._borderStyleValue;
  369. } );
  370. addListToDropdown( borderStyleDropdown.view, getBorderStyleDefinitions( this ) );
  371. // -- Width ---------------------------------------------------
  372. const borderWidthInput = new LabeledView( locale, createLabeledInputText );
  373. borderWidthInput.set( {
  374. label: t( 'Width' ),
  375. class: 'ck-table-form__border-width',
  376. } );
  377. borderWidthInput.view.bind( 'value' ).to( this, 'borderWidth' );
  378. borderWidthInput.bind( 'isEnabled' ).to( this, 'borderStyle', value => {
  379. return value !== 'none';
  380. } );
  381. borderWidthInput.view.on( 'input', () => {
  382. this.borderWidth = borderWidthInput.view.element.value;
  383. } );
  384. // -- Color ---------------------------------------------------
  385. const borderColorInput = new LabeledView( locale, createLabeledInputText );
  386. borderColorInput.label = t( 'Color' );
  387. borderColorInput.view.bind( 'value' ).to( this, 'borderColor' );
  388. borderColorInput.bind( 'isEnabled' ).to( this, 'borderStyle', value => {
  389. return value !== 'none';
  390. } );
  391. borderColorInput.view.on( 'input', () => {
  392. this.borderColor = borderColorInput.view.element.value;
  393. } );
  394. return {
  395. borderRowLabel,
  396. borderStyleDropdown,
  397. borderColorInput,
  398. borderWidthInput,
  399. };
  400. }
  401. /**
  402. * Creates the following form fields:
  403. *
  404. * * {@link #backgroundInput}.
  405. *
  406. * @private
  407. * @returns {module:ui/labeledview/labeledview~LabeledView}
  408. */
  409. _createBackgroundField() {
  410. const locale = this.locale;
  411. const t = this.t;
  412. const backgroundInput = new LabeledView( locale, createLabeledInputText );
  413. backgroundInput.set( {
  414. label: t( 'Background' ),
  415. class: 'ck-table-properties-form__background',
  416. } );
  417. backgroundInput.view.bind( 'value' ).to( this, 'backgroundColor' );
  418. backgroundInput.view.on( 'input', () => {
  419. this.backgroundColor = backgroundInput.view.element.value;
  420. } );
  421. return backgroundInput;
  422. }
  423. /**
  424. * Creates the following form fields:
  425. *
  426. * * {@link #widthInput}.
  427. * * {@link #heightInput}.
  428. *
  429. * @private
  430. * @returns {module:ui/labeledview/labeledview~LabeledView}
  431. */
  432. _createDimensionFields() {
  433. const locale = this.locale;
  434. const t = this.t;
  435. // -- Label ---------------------------------------------------
  436. const dimensionsLabel = new LabelView( locale );
  437. dimensionsLabel.text = t( 'Dimensions' );
  438. // -- Width ---------------------------------------------------
  439. const widthInput = new LabeledView( locale, createLabeledInputText );
  440. widthInput.set( {
  441. label: t( 'Width' ),
  442. class: 'ck-table-properties-form__width',
  443. } );
  444. widthInput.view.bind( 'value' ).to( this, 'width' );
  445. widthInput.view.on( 'input', () => {
  446. this.width = widthInput.view.element.value;
  447. } );
  448. // -- Operator ---------------------------------------------------
  449. const operatorLabel = new View( locale );
  450. operatorLabel.setTemplate( { text: ' × ' } );
  451. // -- Height ---------------------------------------------------
  452. const heightInput = new LabeledView( locale, createLabeledInputText );
  453. heightInput.set( {
  454. label: t( 'Height' ),
  455. class: 'ck-table-properties-form__height',
  456. } );
  457. heightInput.view.bind( 'value' ).to( this, 'height' );
  458. heightInput.view.on( 'input', () => {
  459. this.height = heightInput.view.element.value;
  460. } );
  461. return {
  462. dimensionsLabel,
  463. widthInput,
  464. operatorLabel,
  465. heightInput
  466. };
  467. }
  468. /**
  469. * Creates the following form fields:
  470. *
  471. * * {@link #alignmentToolbar},
  472. *
  473. * @private
  474. * @returns {Object.<String,module:ui/view~View>}
  475. */
  476. _createAlignmentFields() {
  477. const locale = this.locale;
  478. const t = this.t;
  479. // -- Label ---------------------------------------------------
  480. const alignmentLabel = new LabelView( locale );
  481. alignmentLabel.text = t( 'Alignment' );
  482. // -- Toolbar ---------------------------------------------------
  483. const alignmentToolbar = new ToolbarView( locale );
  484. alignmentToolbar.set( {
  485. isCompact: true,
  486. ariaLabel: t( 'Table alignment toolbar' )
  487. } );
  488. fillAlignmentToolbar( {
  489. view: this,
  490. icons: ALIGNMENT_ICONS,
  491. toolbar: alignmentToolbar,
  492. labels: this._alignmentLabels,
  493. propertyName: 'alignment'
  494. } );
  495. return {
  496. alignmentLabel,
  497. alignmentToolbar
  498. };
  499. }
  500. /**
  501. * Creates the following form controls:
  502. *
  503. * * {@link #saveButtonView},
  504. * * {@link #cancelButtonView}.
  505. *
  506. * @private
  507. * @returns {Object.<String,module:ui/view~View>}
  508. */
  509. _createActionButtons() {
  510. const locale = this.locale;
  511. const t = this.t;
  512. const saveButtonView = new ButtonView( locale );
  513. const cancelButtonView = new ButtonView( locale );
  514. saveButtonView.set( {
  515. label: t( 'Save' ),
  516. icon: checkIcon,
  517. class: 'ck-button-save',
  518. type: 'submit',
  519. withText: true,
  520. } );
  521. cancelButtonView.set( {
  522. label: t( 'Cancel' ),
  523. icon: cancelIcon,
  524. class: 'ck-button-cancel',
  525. type: 'cancel',
  526. withText: true,
  527. } );
  528. cancelButtonView.delegate( 'execute' ).to( this, 'cancel' );
  529. return {
  530. saveButtonView, cancelButtonView
  531. };
  532. }
  533. /**
  534. * Provides localized labels for {@link #alignmentToolbar} buttons.
  535. *
  536. * @private
  537. * @type {Object.<String,String>}
  538. */
  539. get _alignmentLabels() {
  540. const t = this.t;
  541. return {
  542. left: t( 'Align table to the left' ),
  543. center: t( 'Center the table' ),
  544. right: t( 'Align table to the right' )
  545. };
  546. }
  547. }