@tendaui/components 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +176 -176
  3. package/button/_example/ghost.tsx +2 -5
  4. package/color-picker/utils/color-picker/cmyk.ts +89 -89
  5. package/color-picker/utils/color-picker/color.ts +467 -467
  6. package/color-picker/utils/color-picker/constants.ts +187 -187
  7. package/color-picker/utils/color-picker/draggable.ts +99 -99
  8. package/color-picker/utils/color-picker/format.ts +90 -90
  9. package/color-picker/utils/color-picker/gradient.ts +237 -237
  10. package/color-picker/utils/color-picker/index.ts +7 -7
  11. package/color-picker/utils/color-picker/types.ts +33 -33
  12. package/dialog/hooks/useDialogPosition.ts +35 -35
  13. package/form/type.ts +519 -519
  14. package/global-config/default-config.ts +95 -95
  15. package/global-config/locale/ar_KW.ts +259 -259
  16. package/global-config/locale/en_US.ts +265 -265
  17. package/global-config/locale/it_IT.ts +264 -264
  18. package/global-config/locale/ja_JP.ts +264 -264
  19. package/global-config/locale/ko_KR.ts +264 -264
  20. package/global-config/locale/ru_RU.ts +277 -277
  21. package/global-config/locale/zh_CN.ts +265 -265
  22. package/global-config/locale/zh_TW.ts +265 -265
  23. package/global-config/mobile/default-config.ts +6 -6
  24. package/global-config/mobile/locale/ar_KW.ts +112 -112
  25. package/global-config/mobile/locale/en_US.ts +113 -113
  26. package/global-config/mobile/locale/it_IT.ts +113 -113
  27. package/global-config/mobile/locale/ja_JP.ts +100 -100
  28. package/global-config/mobile/locale/ko_KR.ts +100 -100
  29. package/global-config/mobile/locale/ru_RU.ts +112 -112
  30. package/global-config/mobile/locale/zh_CN.ts +100 -100
  31. package/global-config/mobile/locale/zh_TW.ts +100 -100
  32. package/global-config/t.ts +111 -111
  33. package/input-number/InputNumber.tsx +124 -124
  34. package/input-number/defaultProps.ts +17 -17
  35. package/input-number/index.ts +9 -9
  36. package/input-number/style/css.js +1 -1
  37. package/input-number/style/index.js +1 -1
  38. package/input-number/useInputNumber.tsx +270 -270
  39. package/list/ListItem.tsx +36 -36
  40. package/list/ListItemMeta.tsx +40 -40
  41. package/list/defaultProps.ts +11 -11
  42. package/list/hooks/useListVirtualScroll.ts +82 -82
  43. package/list/style/css.js +1 -1
  44. package/list/style/index.js +1 -1
  45. package/locale/LocalReceiver.ts +55 -55
  46. package/locale/ar_KW.ts +7 -7
  47. package/locale/en_US.ts +7 -7
  48. package/locale/it_IT.ts +6 -6
  49. package/locale/ja_JP.ts +6 -6
  50. package/locale/ko_KR.ts +6 -6
  51. package/locale/ru_RU.ts +6 -6
  52. package/locale/zh_CN.ts +5 -5
  53. package/locale/zh_TW.ts +7 -7
  54. package/package.json +2 -2
  55. package/radio/_example/default.tsx +2 -2
  56. package/select/type.ts +382 -382
  57. package/select-input/type.ts +280 -280
  58. package/slider/SliderHandleButton.tsx +50 -50
  59. package/slider/defaultProps.ts +15 -15
  60. package/slider/style/css.js +1 -1
  61. package/slider/style/index.js +1 -1
  62. package/styles/_global.scss +40 -40
  63. package/styles/_vars.scss +374 -374
  64. package/styles/components/alert/_index.scss +175 -175
  65. package/styles/components/alert/_vars.scss +41 -41
  66. package/styles/components/badge/_index.scss +71 -71
  67. package/styles/components/badge/_vars.scss +26 -26
  68. package/styles/components/button/_index.scss +499 -499
  69. package/styles/components/button/_mixins.scss +40 -40
  70. package/styles/components/button/_vars.scss +121 -121
  71. package/styles/components/checkbox/_index.scss +158 -158
  72. package/styles/components/checkbox/_var.scss +59 -59
  73. package/styles/components/color-picker/_index.scss +586 -586
  74. package/styles/components/color-picker/_vars.scss +79 -79
  75. package/styles/components/dialog/_animate.scss +133 -133
  76. package/styles/components/dialog/_index.scss +310 -310
  77. package/styles/components/dialog/_vars.scss +60 -60
  78. package/styles/components/drawer/_index.scss +206 -206
  79. package/styles/components/drawer/_var.scss +55 -55
  80. package/styles/components/fireworks/_index.scss +86 -86
  81. package/styles/components/fireworks/_vars.scss +5 -5
  82. package/styles/components/form/_index.scss +175 -175
  83. package/styles/components/form/_mixins.scss +74 -74
  84. package/styles/components/form/_vars.scss +101 -101
  85. package/styles/components/input/_index.scss +350 -350
  86. package/styles/components/input/_mixins.scss +120 -120
  87. package/styles/components/input/_vars.scss +130 -130
  88. package/styles/components/input-number/_index.scss +327 -327
  89. package/styles/components/input-number/_vars.scss +56 -56
  90. package/styles/components/ip-input/_index.scss +277 -277
  91. package/styles/components/layout/_index.scss +47 -47
  92. package/styles/components/layout/_vars.scss +19 -19
  93. package/styles/components/layout/doc.scss +74 -74
  94. package/styles/components/list/_index.scss +172 -172
  95. package/styles/components/list/_vars.scss +42 -42
  96. package/styles/components/loading/_index.scss +113 -113
  97. package/styles/components/loading/_vars.scss +40 -40
  98. package/styles/components/notification/_index.scss +140 -140
  99. package/styles/components/notification/_mixins.scss +13 -13
  100. package/styles/components/notification/_vars.scss +60 -60
  101. package/styles/components/popup/_index.scss +78 -78
  102. package/styles/components/popup/_mixin.scss +149 -149
  103. package/styles/components/popup/_vars.scss +33 -33
  104. package/styles/components/radio/_index.scss +376 -376
  105. package/styles/components/radio/_vars.scss +89 -89
  106. package/styles/components/select/_index.scss +291 -291
  107. package/styles/components/select/_var.scss +64 -64
  108. package/styles/components/select-input/_index.scss +5 -5
  109. package/styles/components/select-input/_var.scss +4 -4
  110. package/styles/components/slider/_index.scss +241 -241
  111. package/styles/components/slider/_vars.scss +51 -51
  112. package/styles/components/switch/_index.scss +175 -175
  113. package/styles/components/switch/_vars.scss +63 -63
  114. package/styles/components/table/_index.scss +194 -194
  115. package/styles/components/table/_var.scss +52 -52
  116. package/styles/components/tabs/_index.scss +165 -165
  117. package/styles/components/tabs/_mixins.scss +11 -11
  118. package/styles/components/tabs/_vars.scss +72 -72
  119. package/styles/components/tag/_index.scss +317 -317
  120. package/styles/components/tag/_var.scss +86 -86
  121. package/styles/components/tag-input/_index.scss +164 -164
  122. package/styles/components/tag-input/_vars.scss +17 -17
  123. package/styles/mixins/_focus.scss +8 -8
  124. package/styles/mixins/_layout.scss +32 -32
  125. package/styles/mixins/_reset.scss +11 -11
  126. package/styles/mixins/_scrollbar.scss +32 -32
  127. package/styles/mixins/_text.scss +50 -50
  128. package/styles/themes/_dark.scss +169 -169
  129. package/styles/themes/_font.scss +69 -69
  130. package/styles/themes/_index.scss +5 -5
  131. package/styles/themes/_light.scss +170 -170
  132. package/styles/themes/_radius.scss +9 -9
  133. package/styles/themes/_size.scss +68 -68
  134. package/styles/utilities/_animation.scss +58 -58
  135. package/styles/utilities/_tips.scss +10 -10
  136. package/switch/_example/with-label.tsx +1 -1
  137. package/utils/input-number/large-number.ts +423 -423
  138. package/utils/input-number/number.ts +257 -257
  139. package/utils/log/index.ts +3 -3
  140. package/utils/log/log.ts +29 -29
  141. package/utils/log/types.ts +9 -9
  142. package/utils/style.ts +58 -58
@@ -1,90 +1,90 @@
1
- import Color from "./color";
2
- import { ALPHA_FORMAT_MAP, COLOR_FORMAT_INPUTS, FORMATS } from "./constants";
3
- import type { AlphaConvertibleFormat, BasicColorFormat, ColorFormat } from "./types";
4
-
5
- /**
6
- * 兜底处理用户传入的格式,例如:
7
- * - 传入 `RGB`, 但 `enableAlpha` ,则返回 `RGBA`
8
- */
9
- export const initColorFormat = (format: ColorFormat, enableAlpha: boolean) => {
10
- if (enableAlpha && format in ALPHA_FORMAT_MAP) {
11
- return format in ALPHA_FORMAT_MAP ? ALPHA_FORMAT_MAP[format as AlphaConvertibleFormat] : format;
12
- }
13
- return format as BasicColorFormat;
14
- };
15
-
16
- /**
17
- * 获取不同格式的输入输出值
18
- * - encode:将字符串转换为单独的颜色值,例如 `{r: 255, g: 255, b: 255}`
19
- * - decode:获取完整的颜色字符串,例如 `rgb(255, 255, 255)`
20
- */
21
- export const getColorFormatMap = (color: Color, type: "encode" | "decode") => {
22
- if (type === "encode") {
23
- return {
24
- HSV: color.getHsva(),
25
- HSVA: color.getHsva(),
26
- HSL: color.getHsla(),
27
- HSLA: color.getHsla(),
28
- RGB: color.getRgba(),
29
- RGBA: color.getRgba(),
30
- CMYK: color.getCmyk(),
31
- CSS: {
32
- css: color.css
33
- },
34
- HEX: {
35
- hex: color.hex
36
- },
37
- HEX8: {
38
- hex: color.hex8 // 为了减少转换 hex8 的 key 也对应 hex
39
- }
40
- };
41
- }
42
-
43
- // decode
44
- return color.getFormatsColorMap();
45
- };
46
-
47
- /**
48
- * 获取下拉框的格式选项
49
- */
50
- export const getColorFormatOptions = (enableAlpha: boolean) =>
51
- enableAlpha
52
- ? FORMATS.map((item) => (item in ALPHA_FORMAT_MAP ? ALPHA_FORMAT_MAP[item as AlphaConvertibleFormat] : item))
53
- : FORMATS;
54
-
55
- /**
56
- * 获取当前格式的输入框配置
57
- */
58
- export const getColorFormatInputs = (format: ColorFormat = "RGB", enableAlpha: boolean) => {
59
- let finalFormat;
60
-
61
- /* 为了减少 `ALPHA_FORMAT_MAP` 中的重复代码
62
- `RGBA/HEX8/HSLA/HSVA` 会被转换为 `RGB/HEX/HSL/HSV` 后再匹配
63
- 但在下一步会 push 一个代表透明度的输入框 */
64
- if (enableAlpha) {
65
- finalFormat =
66
- Object.keys(ALPHA_FORMAT_MAP).find(
67
- (key) => key in ALPHA_FORMAT_MAP && ALPHA_FORMAT_MAP[key as AlphaConvertibleFormat] === format
68
- ) || format;
69
- } else {
70
- finalFormat = format;
71
- }
72
-
73
- if (!COLOR_FORMAT_INPUTS[finalFormat as BasicColorFormat]) return [];
74
-
75
- const configs = [...COLOR_FORMAT_INPUTS[finalFormat as BasicColorFormat]];
76
-
77
- // CMYK 格式不支持透明度
78
- if (enableAlpha && format !== "CMYK") {
79
- configs.push({
80
- type: "inputNumber",
81
- key: "a",
82
- min: 0,
83
- max: 100,
84
- format: (value: number) => `${value}%`,
85
- flex: 1.15
86
- });
87
- }
88
-
89
- return configs;
90
- };
1
+ import Color from "./color";
2
+ import { ALPHA_FORMAT_MAP, COLOR_FORMAT_INPUTS, FORMATS } from "./constants";
3
+ import type { AlphaConvertibleFormat, BasicColorFormat, ColorFormat } from "./types";
4
+
5
+ /**
6
+ * 兜底处理用户传入的格式,例如:
7
+ * - 传入 `RGB`, 但 `enableAlpha` ,则返回 `RGBA`
8
+ */
9
+ export const initColorFormat = (format: ColorFormat, enableAlpha: boolean) => {
10
+ if (enableAlpha && format in ALPHA_FORMAT_MAP) {
11
+ return format in ALPHA_FORMAT_MAP ? ALPHA_FORMAT_MAP[format as AlphaConvertibleFormat] : format;
12
+ }
13
+ return format as BasicColorFormat;
14
+ };
15
+
16
+ /**
17
+ * 获取不同格式的输入输出值
18
+ * - encode:将字符串转换为单独的颜色值,例如 `{r: 255, g: 255, b: 255}`
19
+ * - decode:获取完整的颜色字符串,例如 `rgb(255, 255, 255)`
20
+ */
21
+ export const getColorFormatMap = (color: Color, type: "encode" | "decode") => {
22
+ if (type === "encode") {
23
+ return {
24
+ HSV: color.getHsva(),
25
+ HSVA: color.getHsva(),
26
+ HSL: color.getHsla(),
27
+ HSLA: color.getHsla(),
28
+ RGB: color.getRgba(),
29
+ RGBA: color.getRgba(),
30
+ CMYK: color.getCmyk(),
31
+ CSS: {
32
+ css: color.css
33
+ },
34
+ HEX: {
35
+ hex: color.hex
36
+ },
37
+ HEX8: {
38
+ hex: color.hex8 // 为了减少转换 hex8 的 key 也对应 hex
39
+ }
40
+ };
41
+ }
42
+
43
+ // decode
44
+ return color.getFormatsColorMap();
45
+ };
46
+
47
+ /**
48
+ * 获取下拉框的格式选项
49
+ */
50
+ export const getColorFormatOptions = (enableAlpha: boolean) =>
51
+ enableAlpha
52
+ ? FORMATS.map((item) => (item in ALPHA_FORMAT_MAP ? ALPHA_FORMAT_MAP[item as AlphaConvertibleFormat] : item))
53
+ : FORMATS;
54
+
55
+ /**
56
+ * 获取当前格式的输入框配置
57
+ */
58
+ export const getColorFormatInputs = (format: ColorFormat = "RGB", enableAlpha: boolean) => {
59
+ let finalFormat;
60
+
61
+ /* 为了减少 `ALPHA_FORMAT_MAP` 中的重复代码
62
+ `RGBA/HEX8/HSLA/HSVA` 会被转换为 `RGB/HEX/HSL/HSV` 后再匹配
63
+ 但在下一步会 push 一个代表透明度的输入框 */
64
+ if (enableAlpha) {
65
+ finalFormat =
66
+ Object.keys(ALPHA_FORMAT_MAP).find(
67
+ (key) => key in ALPHA_FORMAT_MAP && ALPHA_FORMAT_MAP[key as AlphaConvertibleFormat] === format
68
+ ) || format;
69
+ } else {
70
+ finalFormat = format;
71
+ }
72
+
73
+ if (!COLOR_FORMAT_INPUTS[finalFormat as BasicColorFormat]) return [];
74
+
75
+ const configs = [...COLOR_FORMAT_INPUTS[finalFormat as BasicColorFormat]];
76
+
77
+ // CMYK 格式不支持透明度
78
+ if (enableAlpha && format !== "CMYK") {
79
+ configs.push({
80
+ type: "inputNumber",
81
+ key: "a",
82
+ min: 0,
83
+ max: 100,
84
+ format: (value: number) => `${value}%`,
85
+ flex: 1.15
86
+ });
87
+ }
88
+
89
+ return configs;
90
+ };
@@ -1,237 +1,237 @@
1
- import { isString, isNull } from "lodash-es";
2
-
3
- /**
4
- * 用于反解析渐变字符串为对象
5
- * https://stackoverflow.com/questions/20215440/parse-css-gradient-rule-with-javascript-regex
6
- */
7
- import tinyColor from "tinycolor2";
8
-
9
- /**
10
- * Utility combine multiple regular expressions.
11
- *
12
- * @param {RegExp[]|string[]} regexpList List of regular expressions or strings.
13
- * @param {string} flags Normal RegExp flags.
14
- */
15
- const combineRegExp = (regexpList: (string | RegExp)[], flags: string): RegExp => {
16
- let source = "";
17
- for (let i = 0; i < regexpList.length; i++) {
18
- if (isString(regexpList[i])) {
19
- source += regexpList[i];
20
- } else {
21
- source += (regexpList[i] as RegExp).source;
22
- }
23
- }
24
- return new RegExp(source, flags);
25
- };
26
-
27
- interface RegExpLib {
28
- gradientSearch: RegExp;
29
- colorStopSearch: RegExp;
30
- }
31
-
32
- interface ColorStop {
33
- color: string;
34
- position?: string;
35
- }
36
-
37
- interface ParseGradientResult {
38
- original: string;
39
- colorStopList?: ColorStop[];
40
- line?: string;
41
- angle?: string;
42
- sideCorner?: string;
43
- }
44
-
45
- /**
46
- * Generate the required regular expressions once.
47
- *
48
- * Regular Expressions are easier to manage this way and can be well described.
49
- *
50
- * @result {object} Object containing regular expressions.
51
- */
52
- const generateRegExp = (): RegExpLib => {
53
- // Note any variables with "Capture" in name include capturing bracket set(s).
54
- const searchFlags = "gi"; // ignore case for angles, "rgb" etc
55
- const rAngle = /(?:[+-]?\d*\.?\d+)(?:deg|grad|rad|turn)/; // Angle +ive, -ive and angle types
56
- // optional 2nd part
57
- const rSideCornerCapture = /to\s+((?:(?:left|right|top|bottom)(?:\s+(?:top|bottom|left|right))?))/;
58
- const rComma = /\s*,\s*/; // Allow space around comma.
59
- const rColorHex = /#(?:[a-f0-9]{6}|[a-f0-9]{3})/; // 3 or 6 character form
60
- const rDigits3 = /\(\s*(?:\d{1,3}\s*,\s*){2}\d{1,3}\s*\)/;
61
- const // "(1, 2, 3)"
62
- rDigits4 = /\(\s*(?:\d{1,3}\s*,\s*){2}\d{1,3}\s*,\s*\d*\.?\d+\)/;
63
- const // "(1, 2, 3, 4)"
64
- rValue = /(?:[+-]?\d*\.?\d+)(?:%|[a-z]+)?/;
65
- const // ".9", "-5px", "100%".
66
- rKeyword = /[_a-z-][_a-z0-9-]*/;
67
- const // "red", "transparent".
68
- rColor = combineRegExp(
69
- ["(?:", rColorHex, "|", "(?:rgb|hsl)", rDigits3, "|", "(?:rgba|hsla)", rDigits4, "|", rKeyword, ")"],
70
- ""
71
- );
72
- const rColorStop = combineRegExp([rColor, "(?:\\s+", rValue, "(?:\\s+", rValue, ")?)?"], "");
73
- const // Single Color Stop, optional %, optional length.
74
- rColorStopList = combineRegExp(["(?:", rColorStop, rComma, ")*", rColorStop], "");
75
- const // List of color stops min 1.
76
- rLineCapture = combineRegExp(["(?:(", rAngle, ")|", rSideCornerCapture, ")"], "");
77
- const // Angle or SideCorner
78
- rGradientSearch = combineRegExp(["(?:(", rLineCapture, ")", rComma, ")?(", rColorStopList, ")"], searchFlags);
79
- const // Capture 1:"line", 2:"angle" (optional), 3:"side corner" (optional) and 4:"stop list".
80
- rColorStopSearch = combineRegExp(
81
- ["\\s*(", rColor, ")", "(?:\\s+", "(", rValue, "))?", "(?:", rComma, "\\s*)?"],
82
- searchFlags
83
- ); // Capture 1:"color" and 2:"position" (optional).
84
-
85
- return {
86
- gradientSearch: rGradientSearch,
87
- colorStopSearch: rColorStopSearch
88
- };
89
- };
90
-
91
- /**
92
- * Actually parse the input gradient parameters string into an object for reusability.
93
- *
94
- *
95
- * @note Really this only supports the standard syntax not historical versions, see MDN for details
96
- * https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient
97
- *
98
- * @param regExpLib
99
- * @param {string} input
100
- * @returns {object|undefined}
101
- */
102
- const parseGradient = (regExpLib: RegExpLib, input: string) => {
103
- let result: ParseGradientResult;
104
- let matchColorStop: any;
105
- let stopResult: ColorStop;
106
-
107
- // reset search position, because we reuse regex.
108
- regExpLib.gradientSearch.lastIndex = 0;
109
-
110
- const matchGradient = regExpLib.gradientSearch.exec(input);
111
- if (!isNull(matchGradient)) {
112
- result = {
113
- original: matchGradient[0],
114
- colorStopList: []
115
- };
116
-
117
- // Line (Angle or Side-Corner).
118
- if (matchGradient[1]) {
119
- result.line = matchGradient[1];
120
- }
121
- // Angle or undefined if side-corner.
122
- if (matchGradient[2]) {
123
- result.angle = matchGradient[2];
124
- }
125
- // Side-corner or undefined if angle.
126
- if (matchGradient[3]) {
127
- result.sideCorner = matchGradient[3];
128
- }
129
-
130
- // reset search position, because we reuse regex.
131
- regExpLib.colorStopSearch.lastIndex = 0;
132
-
133
- // Loop though all the color-stops.
134
- matchColorStop = regExpLib.colorStopSearch.exec(matchGradient[4]);
135
- while (!isNull(matchColorStop)) {
136
- stopResult = {
137
- color: matchColorStop[1]
138
- };
139
-
140
- // Position (optional).
141
- if (matchColorStop[2]) {
142
- stopResult.position = matchColorStop[2];
143
- }
144
- result.colorStopList.push(stopResult);
145
-
146
- // Continue searching from previous position.
147
- matchColorStop = regExpLib.colorStopSearch.exec(matchGradient[4]);
148
- }
149
- }
150
-
151
- // Can be undefined if match not found.
152
- return result;
153
- };
154
-
155
- export interface GradientColorPoint {
156
- id?: string;
157
- color?: string;
158
- left?: number;
159
- }
160
-
161
- export interface GradientColors {
162
- points: GradientColorPoint[];
163
- degree: number;
164
- }
165
-
166
- const REGEXP_LIB = generateRegExp();
167
- const REG_GRADIENT = /.*gradient\s*\(((?:\([^)]*\)|[^)(]*)*)\)/gim;
168
-
169
- /**
170
- * 验证是否是渐变字符串
171
- * @param input
172
- * @returns
173
- */
174
- export const isGradientColor = (input: string): null | RegExpExecArray => {
175
- REG_GRADIENT.lastIndex = 0;
176
- return REG_GRADIENT.exec(input);
177
- };
178
-
179
- // 边界字符串和角度关系
180
- const sideCornerDegreeMap = {
181
- top: 0,
182
- right: 90,
183
- bottom: 180,
184
- left: 270,
185
- "top left": 315,
186
- "left top": 315,
187
- "top right": 45,
188
- "right top": 45,
189
- "bottom left": 225,
190
- "left bottom": 225,
191
- "bottom right": 135,
192
- "right bottom": 135
193
- };
194
-
195
- /**
196
- * 解析渐变字符串为 GradientColors 对象
197
- * @param input
198
- * @returns
199
- */
200
- export const parseGradientString = (input: string): GradientColors | false => {
201
- const match = isGradientColor(input);
202
- if (!match) return false;
203
-
204
- const gradientColors: GradientColors = {
205
- points: [],
206
- degree: 0
207
- };
208
-
209
- const result: ParseGradientResult = parseGradient(REGEXP_LIB, match[1]);
210
- if (result.original.trim() !== match[1].trim()) return false;
211
-
212
- const points: GradientColorPoint[] = result.colorStopList.map(({ color, position }, index, array) => {
213
- const point = Object.create(null);
214
- point.color = tinyColor(color).toRgbString();
215
-
216
- let left = parseFloat(position);
217
- if (Number.isNaN(left)) {
218
- left = (index / (array.length - 1)) * 100;
219
- }
220
-
221
- point.left = left;
222
- return point;
223
- });
224
- gradientColors.points = points;
225
-
226
- let degree = parseInt(result.angle, 10);
227
- if (Number.isNaN(degree)) {
228
- /* 如果角度不存在,使用 CSS 渐变的默认逻辑(180 deg)
229
- https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_images/Using_CSS_gradients */
230
- degree = sideCornerDegreeMap[result.sideCorner as keyof typeof sideCornerDegreeMap] || 180;
231
- }
232
- gradientColors.degree = degree;
233
-
234
- return gradientColors;
235
- };
236
-
237
- export default parseGradientString;
1
+ import { isString, isNull } from "lodash-es";
2
+
3
+ /**
4
+ * 用于反解析渐变字符串为对象
5
+ * https://stackoverflow.com/questions/20215440/parse-css-gradient-rule-with-javascript-regex
6
+ */
7
+ import tinyColor from "tinycolor2";
8
+
9
+ /**
10
+ * Utility combine multiple regular expressions.
11
+ *
12
+ * @param {RegExp[]|string[]} regexpList List of regular expressions or strings.
13
+ * @param {string} flags Normal RegExp flags.
14
+ */
15
+ const combineRegExp = (regexpList: (string | RegExp)[], flags: string): RegExp => {
16
+ let source = "";
17
+ for (let i = 0; i < regexpList.length; i++) {
18
+ if (isString(regexpList[i])) {
19
+ source += regexpList[i];
20
+ } else {
21
+ source += (regexpList[i] as RegExp).source;
22
+ }
23
+ }
24
+ return new RegExp(source, flags);
25
+ };
26
+
27
+ interface RegExpLib {
28
+ gradientSearch: RegExp;
29
+ colorStopSearch: RegExp;
30
+ }
31
+
32
+ interface ColorStop {
33
+ color: string;
34
+ position?: string;
35
+ }
36
+
37
+ interface ParseGradientResult {
38
+ original: string;
39
+ colorStopList?: ColorStop[];
40
+ line?: string;
41
+ angle?: string;
42
+ sideCorner?: string;
43
+ }
44
+
45
+ /**
46
+ * Generate the required regular expressions once.
47
+ *
48
+ * Regular Expressions are easier to manage this way and can be well described.
49
+ *
50
+ * @result {object} Object containing regular expressions.
51
+ */
52
+ const generateRegExp = (): RegExpLib => {
53
+ // Note any variables with "Capture" in name include capturing bracket set(s).
54
+ const searchFlags = "gi"; // ignore case for angles, "rgb" etc
55
+ const rAngle = /(?:[+-]?\d*\.?\d+)(?:deg|grad|rad|turn)/; // Angle +ive, -ive and angle types
56
+ // optional 2nd part
57
+ const rSideCornerCapture = /to\s+((?:(?:left|right|top|bottom)(?:\s+(?:top|bottom|left|right))?))/;
58
+ const rComma = /\s*,\s*/; // Allow space around comma.
59
+ const rColorHex = /#(?:[a-f0-9]{6}|[a-f0-9]{3})/; // 3 or 6 character form
60
+ const rDigits3 = /\(\s*(?:\d{1,3}\s*,\s*){2}\d{1,3}\s*\)/;
61
+ const // "(1, 2, 3)"
62
+ rDigits4 = /\(\s*(?:\d{1,3}\s*,\s*){2}\d{1,3}\s*,\s*\d*\.?\d+\)/;
63
+ const // "(1, 2, 3, 4)"
64
+ rValue = /(?:[+-]?\d*\.?\d+)(?:%|[a-z]+)?/;
65
+ const // ".9", "-5px", "100%".
66
+ rKeyword = /[_a-z-][_a-z0-9-]*/;
67
+ const // "red", "transparent".
68
+ rColor = combineRegExp(
69
+ ["(?:", rColorHex, "|", "(?:rgb|hsl)", rDigits3, "|", "(?:rgba|hsla)", rDigits4, "|", rKeyword, ")"],
70
+ ""
71
+ );
72
+ const rColorStop = combineRegExp([rColor, "(?:\\s+", rValue, "(?:\\s+", rValue, ")?)?"], "");
73
+ const // Single Color Stop, optional %, optional length.
74
+ rColorStopList = combineRegExp(["(?:", rColorStop, rComma, ")*", rColorStop], "");
75
+ const // List of color stops min 1.
76
+ rLineCapture = combineRegExp(["(?:(", rAngle, ")|", rSideCornerCapture, ")"], "");
77
+ const // Angle or SideCorner
78
+ rGradientSearch = combineRegExp(["(?:(", rLineCapture, ")", rComma, ")?(", rColorStopList, ")"], searchFlags);
79
+ const // Capture 1:"line", 2:"angle" (optional), 3:"side corner" (optional) and 4:"stop list".
80
+ rColorStopSearch = combineRegExp(
81
+ ["\\s*(", rColor, ")", "(?:\\s+", "(", rValue, "))?", "(?:", rComma, "\\s*)?"],
82
+ searchFlags
83
+ ); // Capture 1:"color" and 2:"position" (optional).
84
+
85
+ return {
86
+ gradientSearch: rGradientSearch,
87
+ colorStopSearch: rColorStopSearch
88
+ };
89
+ };
90
+
91
+ /**
92
+ * Actually parse the input gradient parameters string into an object for reusability.
93
+ *
94
+ *
95
+ * @note Really this only supports the standard syntax not historical versions, see MDN for details
96
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient
97
+ *
98
+ * @param regExpLib
99
+ * @param {string} input
100
+ * @returns {object|undefined}
101
+ */
102
+ const parseGradient = (regExpLib: RegExpLib, input: string) => {
103
+ let result: ParseGradientResult;
104
+ let matchColorStop: any;
105
+ let stopResult: ColorStop;
106
+
107
+ // reset search position, because we reuse regex.
108
+ regExpLib.gradientSearch.lastIndex = 0;
109
+
110
+ const matchGradient = regExpLib.gradientSearch.exec(input);
111
+ if (!isNull(matchGradient)) {
112
+ result = {
113
+ original: matchGradient[0],
114
+ colorStopList: []
115
+ };
116
+
117
+ // Line (Angle or Side-Corner).
118
+ if (matchGradient[1]) {
119
+ result.line = matchGradient[1];
120
+ }
121
+ // Angle or undefined if side-corner.
122
+ if (matchGradient[2]) {
123
+ result.angle = matchGradient[2];
124
+ }
125
+ // Side-corner or undefined if angle.
126
+ if (matchGradient[3]) {
127
+ result.sideCorner = matchGradient[3];
128
+ }
129
+
130
+ // reset search position, because we reuse regex.
131
+ regExpLib.colorStopSearch.lastIndex = 0;
132
+
133
+ // Loop though all the color-stops.
134
+ matchColorStop = regExpLib.colorStopSearch.exec(matchGradient[4]);
135
+ while (!isNull(matchColorStop)) {
136
+ stopResult = {
137
+ color: matchColorStop[1]
138
+ };
139
+
140
+ // Position (optional).
141
+ if (matchColorStop[2]) {
142
+ stopResult.position = matchColorStop[2];
143
+ }
144
+ result.colorStopList.push(stopResult);
145
+
146
+ // Continue searching from previous position.
147
+ matchColorStop = regExpLib.colorStopSearch.exec(matchGradient[4]);
148
+ }
149
+ }
150
+
151
+ // Can be undefined if match not found.
152
+ return result;
153
+ };
154
+
155
+ export interface GradientColorPoint {
156
+ id?: string;
157
+ color?: string;
158
+ left?: number;
159
+ }
160
+
161
+ export interface GradientColors {
162
+ points: GradientColorPoint[];
163
+ degree: number;
164
+ }
165
+
166
+ const REGEXP_LIB = generateRegExp();
167
+ const REG_GRADIENT = /.*gradient\s*\(((?:\([^)]*\)|[^)(]*)*)\)/gim;
168
+
169
+ /**
170
+ * 验证是否是渐变字符串
171
+ * @param input
172
+ * @returns
173
+ */
174
+ export const isGradientColor = (input: string): null | RegExpExecArray => {
175
+ REG_GRADIENT.lastIndex = 0;
176
+ return REG_GRADIENT.exec(input);
177
+ };
178
+
179
+ // 边界字符串和角度关系
180
+ const sideCornerDegreeMap = {
181
+ top: 0,
182
+ right: 90,
183
+ bottom: 180,
184
+ left: 270,
185
+ "top left": 315,
186
+ "left top": 315,
187
+ "top right": 45,
188
+ "right top": 45,
189
+ "bottom left": 225,
190
+ "left bottom": 225,
191
+ "bottom right": 135,
192
+ "right bottom": 135
193
+ };
194
+
195
+ /**
196
+ * 解析渐变字符串为 GradientColors 对象
197
+ * @param input
198
+ * @returns
199
+ */
200
+ export const parseGradientString = (input: string): GradientColors | false => {
201
+ const match = isGradientColor(input);
202
+ if (!match) return false;
203
+
204
+ const gradientColors: GradientColors = {
205
+ points: [],
206
+ degree: 0
207
+ };
208
+
209
+ const result: ParseGradientResult = parseGradient(REGEXP_LIB, match[1]);
210
+ if (result.original.trim() !== match[1].trim()) return false;
211
+
212
+ const points: GradientColorPoint[] = result.colorStopList.map(({ color, position }, index, array) => {
213
+ const point = Object.create(null);
214
+ point.color = tinyColor(color).toRgbString();
215
+
216
+ let left = parseFloat(position);
217
+ if (Number.isNaN(left)) {
218
+ left = (index / (array.length - 1)) * 100;
219
+ }
220
+
221
+ point.left = left;
222
+ return point;
223
+ });
224
+ gradientColors.points = points;
225
+
226
+ let degree = parseInt(result.angle, 10);
227
+ if (Number.isNaN(degree)) {
228
+ /* 如果角度不存在,使用 CSS 渐变的默认逻辑(180 deg)
229
+ https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_images/Using_CSS_gradients */
230
+ degree = sideCornerDegreeMap[result.sideCorner as keyof typeof sideCornerDegreeMap] || 180;
231
+ }
232
+ gradientColors.degree = degree;
233
+
234
+ return gradientColors;
235
+ };
236
+
237
+ export default parseGradientString;
@@ -1,7 +1,7 @@
1
- export * from "./cmyk";
2
- export * from "./color";
3
- export * from "./constants";
4
- export * from "./draggable";
5
- export * from "./format";
6
- export * from "./gradient";
7
- export * from "./types";
1
+ export * from "./cmyk";
2
+ export * from "./color";
3
+ export * from "./constants";
4
+ export * from "./draggable";
5
+ export * from "./format";
6
+ export * from "./gradient";
7
+ export * from "./types";