uni-nvue-styler.es.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import { hyphenate, capitalize } from '@vue/shared';
  2. function createDecl(prop, value, important, raws, source) {
  3. const decl = {
  4. type: 'decl',
  5. prop,
  6. value: value.toString(),
  7. raws,
  8. source,
  9. };
  10. if (important) {
  11. decl.important = true;
  12. }
  13. return decl;
  14. }
  15. const backgroundColor = 'backgroundColor';
  16. const backgroundImage = 'backgroundImage';
  17. const transformBackground = (decl) => {
  18. const { value, important, raws, source } = decl;
  19. if (/^#?\S+$/.test(value) || /^rgba?(.+)$/.test(value)) {
  20. return [createDecl(backgroundColor, value, important, raws, source)];
  21. }
  22. else if (/^linear-gradient(.+)$/.test(value)) {
  23. return [createDecl(backgroundImage, value, important, raws, source)];
  24. }
  25. return [decl];
  26. };
  27. const borderWidth = 'Width';
  28. const borderStyle = 'Style';
  29. const borderColor = 'Color';
  30. function createTransformBorder(options) {
  31. return (decl) => {
  32. const { prop, value, important, raws, source } = decl;
  33. const splitResult = value.replace(/\s*,\s*/g, ',').split(/\s+/);
  34. const result = [
  35. /^[\d\.]+\S*|^(thin|medium|thick)$/,
  36. /^(solid|dashed|dotted|none)$/,
  37. /\S+/,
  38. ].map((item) => {
  39. const index = splitResult.findIndex((str) => item.test(str));
  40. return index < 0 ? null : splitResult.splice(index, 1)[0];
  41. });
  42. if (splitResult.length) {
  43. return [decl];
  44. }
  45. return [
  46. createDecl(prop + borderWidth, (result[0] || (options.type === 'uvue' ? 'medium' : '0')).trim(), important, raws, source),
  47. createDecl(prop + borderStyle, (result[1] || (options.type === 'uvue' ? 'none' : 'solid')).trim(), important, raws, source),
  48. createDecl(prop + borderColor, (result[2] || '#000000').trim(), important, raws, source),
  49. ];
  50. };
  51. }
  52. const borderTop = 'borderTop';
  53. const borderRight = 'borderRight';
  54. const borderBottom = 'borderBottom';
  55. const borderLeft = 'borderLeft';
  56. const transformBorderColor = (decl) => {
  57. const { prop, value, important, raws, source } = decl;
  58. let property = hyphenate(prop).split('-')[1];
  59. {
  60. property = capitalize(property);
  61. }
  62. const splitResult = value.replace(/\s*,\s*/g, ',').split(/\s+/);
  63. switch (splitResult.length) {
  64. case 1:
  65. return [decl];
  66. case 2:
  67. splitResult.push(splitResult[0], splitResult[1]);
  68. break;
  69. case 3:
  70. splitResult.push(splitResult[1]);
  71. break;
  72. }
  73. return [
  74. createDecl(borderTop + property, splitResult[0], important, raws, source),
  75. createDecl(borderRight + property, splitResult[1], important, raws, source),
  76. createDecl(borderBottom + property, splitResult[2], important, raws, source),
  77. createDecl(borderLeft + property, splitResult[3], important, raws, source),
  78. ];
  79. };
  80. const borderTopLeftRadius = 'borderTopLeftRadius';
  81. const borderTopRightRadius = 'borderTopRightRadius';
  82. const borderBottomRightRadius = 'borderBottomRightRadius';
  83. const borderBottomLeftRadius = 'borderBottomLeftRadius';
  84. const transformBorderRadius = (decl) => {
  85. const { value, important, raws, source } = decl;
  86. const splitResult = value.split(/\s+/);
  87. if (value.includes('/')) {
  88. return [decl];
  89. }
  90. switch (splitResult.length) {
  91. case 1:
  92. return [decl];
  93. case 2:
  94. splitResult.push(splitResult[0], splitResult[1]);
  95. break;
  96. case 3:
  97. splitResult.push(splitResult[1]);
  98. break;
  99. }
  100. return [
  101. createDecl(borderTopLeftRadius, splitResult[0], important, raws, source),
  102. createDecl(borderTopRightRadius, splitResult[1], important, raws, source),
  103. createDecl(borderBottomRightRadius, splitResult[2], important, raws, source),
  104. createDecl(borderBottomLeftRadius, splitResult[3], important, raws, source),
  105. ];
  106. };
  107. const transformBorderStyle = transformBorderColor;
  108. const transformBorderWidth = transformBorderColor;
  109. const flexDirection = 'flexDirection';
  110. const flexWrap = 'flexWrap';
  111. const transformFlexFlow = (decl) => {
  112. const { value, important, raws, source } = decl;
  113. const splitResult = value.split(/\s+/);
  114. const result = [
  115. /^(column|column-reverse|row|row-reverse)$/,
  116. /^(nowrap|wrap|wrap-reverse)$/,
  117. ].map((item) => {
  118. const index = splitResult.findIndex((str) => item.test(str));
  119. return index < 0 ? null : splitResult.splice(index, 1)[0];
  120. });
  121. if (splitResult.length) {
  122. return [decl];
  123. }
  124. return [
  125. createDecl(flexDirection, result[0] || 'column', important, raws, source),
  126. createDecl(flexWrap, result[1] || 'nowrap', important, raws, source),
  127. ];
  128. };
  129. const top = 'Top';
  130. const right = 'Right';
  131. const bottom = 'Bottom';
  132. const left = 'Left';
  133. const createTransformBox = (type) => {
  134. return (decl) => {
  135. const { value, important, raws, source } = decl;
  136. const splitResult = value.split(/\s+/);
  137. switch (splitResult.length) {
  138. case 1:
  139. splitResult.push(splitResult[0], splitResult[0], splitResult[0]);
  140. break;
  141. case 2:
  142. splitResult.push(splitResult[0], splitResult[1]);
  143. break;
  144. case 3:
  145. splitResult.push(splitResult[1]);
  146. break;
  147. }
  148. return [
  149. createDecl(type + top, splitResult[0], important, raws, source),
  150. createDecl(type + right, splitResult[1], important, raws, source),
  151. createDecl(type + bottom, splitResult[2], important, raws, source),
  152. createDecl(type + left, splitResult[3], important, raws, source),
  153. ];
  154. };
  155. };
  156. const transformMargin = createTransformBox('margin');
  157. const transformPadding = createTransformBox('padding');
  158. const transitionProperty = 'transitionProperty';
  159. const transitionDuration = 'transitionDuration';
  160. const transitionTimingFunction = 'transitionTimingFunction';
  161. const transitionDelay = 'transitionDelay';
  162. const transformTransition = (decl) => {
  163. const CHUNK_REGEXP = /^(\S*)?\s*(\d*\.?\d+(?:ms|s)?)?\s*(\S*)?\s*(\d*\.?\d+(?:ms|s)?)?$/;
  164. const { value, important, raws, source } = decl;
  165. const result = [];
  166. const match = value.match(CHUNK_REGEXP);
  167. if (!match) {
  168. return result;
  169. }
  170. match[1] &&
  171. result.push(createDecl(transitionProperty, match[1], important, raws, source));
  172. match[2] &&
  173. result.push(createDecl(transitionDuration, match[2], important, raws, source));
  174. match[3] &&
  175. result.push(createDecl(transitionTimingFunction, match[3], important, raws, source));
  176. match[4] &&
  177. result.push(createDecl(transitionDelay, match[4], important, raws, source));
  178. return result;
  179. };
  180. function getDeclTransforms(options) {
  181. const transformBorder = createTransformBorder(options);
  182. const styleMap = {
  183. transition: transformTransition,
  184. border: transformBorder,
  185. background: transformBackground,
  186. borderTop: transformBorder,
  187. borderRight: transformBorder,
  188. borderBottom: transformBorder,
  189. borderLeft: transformBorder,
  190. borderStyle: transformBorderStyle,
  191. borderWidth: transformBorderWidth,
  192. borderColor: transformBorderColor,
  193. borderRadius: transformBorderRadius,
  194. // uvue已经支持这些简写属性,不需要展开
  195. // margin,padding继续展开,确保样式的优先级
  196. margin: transformMargin,
  197. padding: transformPadding,
  198. /* eslint-disable no-restricted-syntax */
  199. ...(options.type !== 'uvue'
  200. ? {
  201. flexFlow: transformFlexFlow,
  202. }
  203. : {}),
  204. };
  205. let result = {};
  206. {
  207. result = styleMap;
  208. }
  209. return result;
  210. }
  211. let DeclTransforms;
  212. const expanded = Symbol('expanded');
  213. function expand(options) {
  214. const plugin = {
  215. postcssPlugin: 'nvue:expand',
  216. Declaration(decl) {
  217. if (decl[expanded]) {
  218. return;
  219. }
  220. if (!DeclTransforms) {
  221. DeclTransforms = getDeclTransforms(options);
  222. }
  223. const transform = DeclTransforms[decl.prop];
  224. if (transform) {
  225. const res = transform(decl);
  226. const isSame = res.length === 1 && res[0] === decl;
  227. if (!isSame) {
  228. decl.replaceWith(res);
  229. }
  230. }
  231. decl[expanded] = true;
  232. },
  233. };
  234. return plugin;
  235. }
  236. export { expand };