tablecellpropertiesview.js 18 KB

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