@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.
- package/LICENSE +21 -21
- package/README.md +176 -176
- package/button/_example/ghost.tsx +2 -5
- package/color-picker/utils/color-picker/cmyk.ts +89 -89
- package/color-picker/utils/color-picker/color.ts +467 -467
- package/color-picker/utils/color-picker/constants.ts +187 -187
- package/color-picker/utils/color-picker/draggable.ts +99 -99
- package/color-picker/utils/color-picker/format.ts +90 -90
- package/color-picker/utils/color-picker/gradient.ts +237 -237
- package/color-picker/utils/color-picker/index.ts +7 -7
- package/color-picker/utils/color-picker/types.ts +33 -33
- package/dialog/hooks/useDialogPosition.ts +35 -35
- package/form/type.ts +519 -519
- package/global-config/default-config.ts +95 -95
- package/global-config/locale/ar_KW.ts +259 -259
- package/global-config/locale/en_US.ts +265 -265
- package/global-config/locale/it_IT.ts +264 -264
- package/global-config/locale/ja_JP.ts +264 -264
- package/global-config/locale/ko_KR.ts +264 -264
- package/global-config/locale/ru_RU.ts +277 -277
- package/global-config/locale/zh_CN.ts +265 -265
- package/global-config/locale/zh_TW.ts +265 -265
- package/global-config/mobile/default-config.ts +6 -6
- package/global-config/mobile/locale/ar_KW.ts +112 -112
- package/global-config/mobile/locale/en_US.ts +113 -113
- package/global-config/mobile/locale/it_IT.ts +113 -113
- package/global-config/mobile/locale/ja_JP.ts +100 -100
- package/global-config/mobile/locale/ko_KR.ts +100 -100
- package/global-config/mobile/locale/ru_RU.ts +112 -112
- package/global-config/mobile/locale/zh_CN.ts +100 -100
- package/global-config/mobile/locale/zh_TW.ts +100 -100
- package/global-config/t.ts +111 -111
- package/input-number/InputNumber.tsx +124 -124
- package/input-number/defaultProps.ts +17 -17
- package/input-number/index.ts +9 -9
- package/input-number/style/css.js +1 -1
- package/input-number/style/index.js +1 -1
- package/input-number/useInputNumber.tsx +270 -270
- package/list/ListItem.tsx +36 -36
- package/list/ListItemMeta.tsx +40 -40
- package/list/defaultProps.ts +11 -11
- package/list/hooks/useListVirtualScroll.ts +82 -82
- package/list/style/css.js +1 -1
- package/list/style/index.js +1 -1
- package/locale/LocalReceiver.ts +55 -55
- package/locale/ar_KW.ts +7 -7
- package/locale/en_US.ts +7 -7
- package/locale/it_IT.ts +6 -6
- package/locale/ja_JP.ts +6 -6
- package/locale/ko_KR.ts +6 -6
- package/locale/ru_RU.ts +6 -6
- package/locale/zh_CN.ts +5 -5
- package/locale/zh_TW.ts +7 -7
- package/package.json +2 -2
- package/radio/_example/default.tsx +2 -2
- package/select/type.ts +382 -382
- package/select-input/type.ts +280 -280
- package/slider/SliderHandleButton.tsx +50 -50
- package/slider/defaultProps.ts +15 -15
- package/slider/style/css.js +1 -1
- package/slider/style/index.js +1 -1
- package/styles/_global.scss +40 -40
- package/styles/_vars.scss +374 -374
- package/styles/components/alert/_index.scss +175 -175
- package/styles/components/alert/_vars.scss +41 -41
- package/styles/components/badge/_index.scss +71 -71
- package/styles/components/badge/_vars.scss +26 -26
- package/styles/components/button/_index.scss +499 -499
- package/styles/components/button/_mixins.scss +40 -40
- package/styles/components/button/_vars.scss +121 -121
- package/styles/components/checkbox/_index.scss +158 -158
- package/styles/components/checkbox/_var.scss +59 -59
- package/styles/components/color-picker/_index.scss +586 -586
- package/styles/components/color-picker/_vars.scss +79 -79
- package/styles/components/dialog/_animate.scss +133 -133
- package/styles/components/dialog/_index.scss +310 -310
- package/styles/components/dialog/_vars.scss +60 -60
- package/styles/components/drawer/_index.scss +206 -206
- package/styles/components/drawer/_var.scss +55 -55
- package/styles/components/fireworks/_index.scss +86 -86
- package/styles/components/fireworks/_vars.scss +5 -5
- package/styles/components/form/_index.scss +175 -175
- package/styles/components/form/_mixins.scss +74 -74
- package/styles/components/form/_vars.scss +101 -101
- package/styles/components/input/_index.scss +350 -350
- package/styles/components/input/_mixins.scss +120 -120
- package/styles/components/input/_vars.scss +130 -130
- package/styles/components/input-number/_index.scss +327 -327
- package/styles/components/input-number/_vars.scss +56 -56
- package/styles/components/ip-input/_index.scss +277 -277
- package/styles/components/layout/_index.scss +47 -47
- package/styles/components/layout/_vars.scss +19 -19
- package/styles/components/layout/doc.scss +74 -74
- package/styles/components/list/_index.scss +172 -172
- package/styles/components/list/_vars.scss +42 -42
- package/styles/components/loading/_index.scss +113 -113
- package/styles/components/loading/_vars.scss +40 -40
- package/styles/components/notification/_index.scss +140 -140
- package/styles/components/notification/_mixins.scss +13 -13
- package/styles/components/notification/_vars.scss +60 -60
- package/styles/components/popup/_index.scss +78 -78
- package/styles/components/popup/_mixin.scss +149 -149
- package/styles/components/popup/_vars.scss +33 -33
- package/styles/components/radio/_index.scss +376 -376
- package/styles/components/radio/_vars.scss +89 -89
- package/styles/components/select/_index.scss +291 -291
- package/styles/components/select/_var.scss +64 -64
- package/styles/components/select-input/_index.scss +5 -5
- package/styles/components/select-input/_var.scss +4 -4
- package/styles/components/slider/_index.scss +241 -241
- package/styles/components/slider/_vars.scss +51 -51
- package/styles/components/switch/_index.scss +175 -175
- package/styles/components/switch/_vars.scss +63 -63
- package/styles/components/table/_index.scss +194 -194
- package/styles/components/table/_var.scss +52 -52
- package/styles/components/tabs/_index.scss +165 -165
- package/styles/components/tabs/_mixins.scss +11 -11
- package/styles/components/tabs/_vars.scss +72 -72
- package/styles/components/tag/_index.scss +317 -317
- package/styles/components/tag/_var.scss +86 -86
- package/styles/components/tag-input/_index.scss +164 -164
- package/styles/components/tag-input/_vars.scss +17 -17
- package/styles/mixins/_focus.scss +8 -8
- package/styles/mixins/_layout.scss +32 -32
- package/styles/mixins/_reset.scss +11 -11
- package/styles/mixins/_scrollbar.scss +32 -32
- package/styles/mixins/_text.scss +50 -50
- package/styles/themes/_dark.scss +169 -169
- package/styles/themes/_font.scss +69 -69
- package/styles/themes/_index.scss +5 -5
- package/styles/themes/_light.scss +170 -170
- package/styles/themes/_radius.scss +9 -9
- package/styles/themes/_size.scss +68 -68
- package/styles/utilities/_animation.scss +58 -58
- package/styles/utilities/_tips.scss +10 -10
- package/switch/_example/with-label.tsx +1 -1
- package/utils/input-number/large-number.ts +423 -423
- package/utils/input-number/number.ts +257 -257
- package/utils/log/index.ts +3 -3
- package/utils/log/log.ts +29 -29
- package/utils/log/types.ts +9 -9
- 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";
|