8
0

border.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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 engine/view/styles/border
  7. */
  8. import { getShorthandValues, getBoxSidesValueReducer, getBoxSidesValues, isLength, isLineStyle } from './utils';
  9. /**
  10. * Adds a border CSS styles processing rules.
  11. *
  12. * editor.data.addStyleProcessorRules( addBorderRules );
  13. *
  14. * This rules merges all [border](https://developer.mozilla.org/en-US/docs/Web/CSS/border) styles notation shorthands:
  15. *
  16. * - border
  17. * - border-top
  18. * - border-right
  19. * - border-bottom
  20. * - border-left
  21. * - border-color
  22. * - border-style
  23. * - border-width
  24. *
  25. * and all corresponding longhand forms (like `border-top-color`, `border-top-style`, etc).
  26. *
  27. * It does not handle other shorthands (like `border-radius` or `border-image`).
  28. *
  29. * The normalized model stores border values as:
  30. *
  31. * const styles = {
  32. * border: {
  33. * color: { top, right, bottom, left },
  34. * style: { top, right, bottom, left },
  35. * width: { top, right, bottom, left },
  36. * }
  37. * };
  38. *
  39. * The `border` value is reduced to a 4 values for each box edge (even if they could be further reduces to a single
  40. * `border:<width> <style> <color>` style.
  41. *
  42. * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor
  43. */
  44. export function addBorderRules( stylesProcessor ) {
  45. stylesProcessor.setNormalizer( 'border', borderNormalizer );
  46. // Border-position shorthands.
  47. stylesProcessor.setNormalizer( 'border-top', getBorderPositionNormalizer( 'top' ) );
  48. stylesProcessor.setNormalizer( 'border-right', getBorderPositionNormalizer( 'right' ) );
  49. stylesProcessor.setNormalizer( 'border-bottom', getBorderPositionNormalizer( 'bottom' ) );
  50. stylesProcessor.setNormalizer( 'border-left', getBorderPositionNormalizer( 'left' ) );
  51. // Border-property shorthands.
  52. stylesProcessor.setNormalizer( 'border-color', getBorderPropertyNormalizer( 'color' ) );
  53. stylesProcessor.setNormalizer( 'border-width', getBorderPropertyNormalizer( 'width' ) );
  54. stylesProcessor.setNormalizer( 'border-style', getBorderPropertyNormalizer( 'style' ) );
  55. // Border longhands.
  56. stylesProcessor.setNormalizer( 'border-top-color', getBorderPropertyPositionNormalizer( 'color', 'top' ) );
  57. stylesProcessor.setNormalizer( 'border-top-style', getBorderPropertyPositionNormalizer( 'style', 'top' ) );
  58. stylesProcessor.setNormalizer( 'border-top-width', getBorderPropertyPositionNormalizer( 'width', 'top' ) );
  59. stylesProcessor.setNormalizer( 'border-right-color', getBorderPropertyPositionNormalizer( 'color', 'right' ) );
  60. stylesProcessor.setNormalizer( 'border-right-style', getBorderPropertyPositionNormalizer( 'style', 'right' ) );
  61. stylesProcessor.setNormalizer( 'border-right-width', getBorderPropertyPositionNormalizer( 'width', 'right' ) );
  62. stylesProcessor.setNormalizer( 'border-bottom-color', getBorderPropertyPositionNormalizer( 'color', 'bottom' ) );
  63. stylesProcessor.setNormalizer( 'border-bottom-style', getBorderPropertyPositionNormalizer( 'style', 'bottom' ) );
  64. stylesProcessor.setNormalizer( 'border-bottom-width', getBorderPropertyPositionNormalizer( 'width', 'bottom' ) );
  65. stylesProcessor.setNormalizer( 'border-left-color', getBorderPropertyPositionNormalizer( 'color', 'left' ) );
  66. stylesProcessor.setNormalizer( 'border-left-style', getBorderPropertyPositionNormalizer( 'style', 'left' ) );
  67. stylesProcessor.setNormalizer( 'border-left-width', getBorderPropertyPositionNormalizer( 'width', 'left' ) );
  68. stylesProcessor.setExtractor( 'border-top', getBorderPositionExtractor( 'top' ) );
  69. stylesProcessor.setExtractor( 'border-right', getBorderPositionExtractor( 'right' ) );
  70. stylesProcessor.setExtractor( 'border-bottom', getBorderPositionExtractor( 'bottom' ) );
  71. stylesProcessor.setExtractor( 'border-left', getBorderPositionExtractor( 'left' ) );
  72. stylesProcessor.setExtractor( 'border-top-color', 'border.color.top' );
  73. stylesProcessor.setExtractor( 'border-right-color', 'border.color.right' );
  74. stylesProcessor.setExtractor( 'border-bottom-color', 'border.color.bottom' );
  75. stylesProcessor.setExtractor( 'border-left-color', 'border.color.left' );
  76. stylesProcessor.setExtractor( 'border-top-width', 'border.width.top' );
  77. stylesProcessor.setExtractor( 'border-right-width', 'border.width.right' );
  78. stylesProcessor.setExtractor( 'border-bottom-width', 'border.width.bottom' );
  79. stylesProcessor.setExtractor( 'border-left-width', 'border.width.left' );
  80. stylesProcessor.setExtractor( 'border-top-style', 'border.style.top' );
  81. stylesProcessor.setExtractor( 'border-right-style', 'border.style.right' );
  82. stylesProcessor.setExtractor( 'border-bottom-style', 'border.style.bottom' );
  83. stylesProcessor.setExtractor( 'border-left-style', 'border.style.left' );
  84. stylesProcessor.setReducer( 'border-color', getBoxSidesValueReducer( 'border-color' ) );
  85. stylesProcessor.setReducer( 'border-style', getBoxSidesValueReducer( 'border-style' ) );
  86. stylesProcessor.setReducer( 'border-width', getBoxSidesValueReducer( 'border-width' ) );
  87. stylesProcessor.setReducer( 'border-top', getBorderPositionReducer( 'top' ) );
  88. stylesProcessor.setReducer( 'border-right', getBorderPositionReducer( 'right' ) );
  89. stylesProcessor.setReducer( 'border-bottom', getBorderPositionReducer( 'bottom' ) );
  90. stylesProcessor.setReducer( 'border-left', getBorderPositionReducer( 'left' ) );
  91. stylesProcessor.setReducer( 'border', borderReducer );
  92. stylesProcessor.setStyleRelation( 'border', [
  93. 'border-color', 'border-style', 'border-width',
  94. 'border-top', 'border-right', 'border-bottom', 'border-left',
  95. 'border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color',
  96. 'border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style',
  97. 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width'
  98. ] );
  99. stylesProcessor.setStyleRelation( 'border-color', [
  100. 'border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color'
  101. ] );
  102. stylesProcessor.setStyleRelation( 'border-style', [
  103. 'border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style'
  104. ] );
  105. stylesProcessor.setStyleRelation( 'border-width', [
  106. 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width'
  107. ] );
  108. stylesProcessor.setStyleRelation( 'border-top', [ 'border-top-color', 'border-top-style', 'border-top-width' ] );
  109. stylesProcessor.setStyleRelation( 'border-right', [ 'border-right-color', 'border-right-style', 'border-right-width' ] );
  110. stylesProcessor.setStyleRelation( 'border-bottom', [ 'border-bottom-color', 'border-bottom-style', 'border-bottom-width' ] );
  111. stylesProcessor.setStyleRelation( 'border-left', [ 'border-left-color', 'border-left-style', 'border-left-width' ] );
  112. }
  113. function borderNormalizer( value ) {
  114. const { color, style, width } = normalizeBorderShorthand( value );
  115. return {
  116. path: 'border',
  117. value: {
  118. color: getBoxSidesValues( color ),
  119. style: getBoxSidesValues( style ),
  120. width: getBoxSidesValues( width )
  121. }
  122. };
  123. }
  124. function getBorderPositionNormalizer( side ) {
  125. return value => {
  126. const { color, style, width } = normalizeBorderShorthand( value );
  127. const border = {};
  128. if ( color !== undefined ) {
  129. border.color = { [ side ]: color };
  130. }
  131. if ( style !== undefined ) {
  132. border.style = { [ side ]: style };
  133. }
  134. if ( width !== undefined ) {
  135. border.width = { [ side ]: width };
  136. }
  137. return {
  138. path: 'border',
  139. value: border
  140. };
  141. };
  142. }
  143. function getBorderPropertyNormalizer( propertyName ) {
  144. return value => {
  145. return {
  146. path: 'border',
  147. value: toBorderPropertyShorthand( value, propertyName )
  148. };
  149. };
  150. }
  151. function toBorderPropertyShorthand( value, property ) {
  152. return {
  153. [ property ]: getBoxSidesValues( value )
  154. };
  155. }
  156. function getBorderPropertyPositionNormalizer( property, side ) {
  157. return value => {
  158. return {
  159. path: 'border',
  160. value: {
  161. [ property ]: {
  162. [ side ]: value
  163. }
  164. }
  165. };
  166. };
  167. }
  168. function getBorderPositionExtractor( which ) {
  169. return ( name, styles ) => {
  170. if ( styles.border ) {
  171. return extractBorderPosition( styles.border, which );
  172. }
  173. };
  174. }
  175. function extractBorderPosition( border, which ) {
  176. const value = {};
  177. if ( border.width && border.width[ which ] ) {
  178. value.width = border.width[ which ];
  179. }
  180. if ( border.style && border.style[ which ] ) {
  181. value.style = border.style[ which ];
  182. }
  183. if ( border.color && border.color[ which ] ) {
  184. value.color = border.color[ which ];
  185. }
  186. return value;
  187. }
  188. function normalizeBorderShorthand( string ) {
  189. const result = {};
  190. const parts = getShorthandValues( string );
  191. for ( const part of parts ) {
  192. if ( isLength( part ) || /thin|medium|thick/.test( part ) ) {
  193. result.width = part;
  194. } else if ( isLineStyle( part ) ) {
  195. result.style = part;
  196. } else {
  197. result.color = part;
  198. }
  199. }
  200. return result;
  201. }
  202. function borderReducer( value ) {
  203. const reduced = [];
  204. reduced.push( ...reduceBorderPosition( extractBorderPosition( value, 'top' ), 'top' ) );
  205. reduced.push( ...reduceBorderPosition( extractBorderPosition( value, 'right' ), 'right' ) );
  206. reduced.push( ...reduceBorderPosition( extractBorderPosition( value, 'bottom' ), 'bottom' ) );
  207. reduced.push( ...reduceBorderPosition( extractBorderPosition( value, 'left' ), 'left' ) );
  208. return reduced;
  209. }
  210. function getBorderPositionReducer( which ) {
  211. return value => reduceBorderPosition( value, which );
  212. }
  213. function reduceBorderPosition( value, which ) {
  214. const reduced = [];
  215. if ( value && value.width !== undefined ) {
  216. reduced.push( value.width );
  217. }
  218. if ( value && value.style !== undefined ) {
  219. reduced.push( value.style );
  220. }
  221. if ( value && value.color !== undefined ) {
  222. reduced.push( value.color );
  223. }
  224. if ( reduced.length ) {
  225. return [ [ `border-${ which }`, reduced.join( ' ' ) ] ];
  226. }
  227. return [];
  228. }