├── .eslintrc.cjs
├── .github
└── ISSUE_TEMPLATE
│ └── bug_report.md
├── .gitignore
├── CHANGELOG.md
├── CODEOWNERS
├── LICENSE
├── README.md
├── README.zh-CN.md
├── env.d.ts
├── index.html
├── package.json
├── public
├── MasterGo.png
├── MasterGo.svg
└── html-5.svg
├── shims-vue.d.ts
├── src
├── App.vue
├── example
│ └── html-mg
│ │ ├── index.html
│ │ ├── lib
│ │ └── main.ts
│ │ ├── manifest.json
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ ├── typings
│ │ ├── index.d.ts
│ │ └── shims-vue.d.ts
│ │ ├── ui
│ │ ├── App.vue
│ │ ├── component
│ │ │ ├── breadcrumb.vue
│ │ │ ├── calendar.vue
│ │ │ ├── image.vue
│ │ │ ├── index.vue
│ │ │ ├── menu.vue
│ │ │ ├── pageHeader.vue
│ │ │ └── table.vue
│ │ ├── global.scss
│ │ └── ui.ts
│ │ ├── vite.config.ts
│ │ └── yarn.lock
├── lib
│ ├── constant.ts
│ ├── getStyles.ts
│ ├── helpers
│ │ ├── autoLayout.ts
│ │ ├── bound.ts
│ │ ├── config.ts
│ │ ├── font.ts
│ │ ├── image.ts
│ │ ├── index.ts
│ │ ├── input.ts
│ │ ├── paints.ts
│ │ └── utils.ts
│ ├── index.d.ts
│ ├── index.ts
│ ├── mixins
│ │ ├── base.ts
│ │ ├── blend.ts
│ │ ├── constraints.ts
│ │ ├── container.ts
│ │ ├── corner.ts
│ │ ├── geometry.ts
│ │ ├── index.ts
│ │ ├── layout.ts
│ │ ├── scene.ts
│ │ └── shape.ts
│ ├── postProcess.ts
│ ├── render.ts
│ ├── transPseudo.ts
│ ├── transformFrame.ts
│ ├── transformRect.ts
│ ├── transformSvg.ts
│ └── transformText.ts
└── main.ts
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── yarn.lock
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | require('@rushstack/eslint-patch/modern-module-resolution')
3 |
4 | module.exports = {
5 | root: true,
6 | 'extends': [
7 | 'plugin:vue/vue3-essential',
8 | 'eslint:recommended',
9 | '@vue/eslint-config-typescript'
10 | ],
11 | parserOptions: {
12 | ecmaVersion: 'latest'
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: bug报告
3 | about: 创建一个报告帮助我们改进
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | ---
11 | name: bug报告
12 | about: 创建一个报告帮助我们改进
13 | title: ''
14 | labels: ''
15 |
16 | ---
17 |
18 |
19 |
20 | **描述问题**
21 | 清晰简洁地描述错误是什么.
22 |
23 | **复现步骤**
24 | 重现此问题的步骤:
25 | 1.
26 | 2.
27 | 3.
28 | 4.
29 |
30 | **期望结果**
31 | 清晰简洁地描述您期望的结果.
32 |
33 | **截图**
34 | 如果方便,请添加屏幕截图以帮助解释您的问题.
35 |
36 | **其他内容**
37 | 在此处添加有关问题的任何其内容.
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | .DS_Store
12 | dist
13 | /lib
14 | dist-ssr
15 | coverage
16 | *.local
17 | /scripts
18 | .npmrc
19 |
20 | /cypress/videos/
21 | /cypress/screenshots/
22 |
23 | # Editor directories and files
24 | .vscode
25 | .idea
26 | *.suo
27 | *.ntvs*
28 | *.njsproj
29 | *.sln
30 | *.sw?
31 | test-component
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # [1.10.0](https://github.com/mastergo-design/html-to-mastergo/compare/v1.9.1...v1.10.0) (2023-07-13)
2 |
3 |
4 |
5 | ## [1.9.1](https://github.com/mastergo-design/html-to-mastergo/compare/v1.8.1...v1.9.1) (2023-05-06)
6 |
7 |
8 | ### Bug Fixes
9 |
10 | * 变换矩阵兼容变换中心,修复变换后x和y计算错误的问题 ([dc3a2bd](https://github.com/mastergo-design/html-to-mastergo/commit/dc3a2bdaaddbedce3bbe6a253d4ba9197b581a3e))
11 | * input 高度计算问题 ([f6e89e6](https://github.com/mastergo-design/html-to-mastergo/commit/f6e89e6d19aad4c65c43eab339d95b0021cf4db7))
12 |
13 |
14 | ### Features
15 |
16 | * 文字支持省略 ([083d0a7](https://github.com/mastergo-design/html-to-mastergo/commit/083d0a7868e1aca5bb691013735c2ad2b07ea877))
17 |
18 |
19 |
20 | # [1.9.0](https://github.com/mastergo-design/html-to-mastergo/compare/v1.8.1...v1.9.0) (2023-05-05)
21 |
22 |
23 | ### Features
24 |
25 | * 文字支持省略 ([083d0a7](https://github.com/mastergo-design/html-to-mastergo/commit/083d0a7868e1aca5bb691013735c2ad2b07ea877))
26 |
27 |
28 |
29 | ## [1.8.1](https://github.com/mastergo-design/html-to-mastergo/compare/v1.8.0...v1.8.1) (2023-04-28)
30 |
31 |
32 | ### Bug Fixes
33 |
34 | * 修复input和textarea判断错误的问题 ([a43b0be](https://github.com/mastergo-design/html-to-mastergo/commit/a43b0be65772b1070bef64ba3eb703e5c77fe721))
35 |
36 |
37 | ### Features
38 |
39 | * 移除isMask属性, rang的detach后置 ([71e70f6](https://github.com/mastergo-design/html-to-mastergo/commit/71e70f655113dc45fbfa8a08a87dc9a231b4d71a))
40 | * 优化渲染代码 ([cff08dc](https://github.com/mastergo-design/html-to-mastergo/commit/cff08dc08a40ee98d984a24c57605ce457b23271))
41 |
42 |
43 |
44 | # [1.8.0](https://github.com/mastergo-design/html-to-mastergo/compare/v1.7.0...v1.8.0) (2023-04-25)
45 |
46 |
47 | ### Bug Fixes
48 |
49 | * 修复高斯和背景模糊取反的bug ([ef72e48](https://github.com/mastergo-design/html-to-mastergo/commit/ef72e4826f2e2680c833521a31e0dac97c1d49cf))
50 |
51 |
52 | ### Features
53 |
54 | * 取消判断翻转采用绝对值 ([a0d39f9](https://github.com/mastergo-design/html-to-mastergo/commit/a0d39f90549434b54fc6b787544cf481208bd26d))
55 | * 是否缩放改为绝对值判断 ([8bd3d0f](https://github.com/mastergo-design/html-to-mastergo/commit/8bd3d0f014e61132223a2561cd2d676b259d8db2))
56 | * 增加图层翻转判断 ([eadacfb](https://github.com/mastergo-design/html-to-mastergo/commit/eadacfbe1ec897896d47b5bf96c92cbfd1050555))
57 |
58 |
59 |
60 | # [1.7.0](https://github.com/mastergo-design/html-to-mastergo/compare/v1.6.3...v1.7.0) (2023-03-16)
61 |
62 |
63 | ### Features
64 |
65 | * 支持缩放和倾斜 ([c3d0a80](https://github.com/mastergo-design/html-to-mastergo/commit/c3d0a80e4d0711522483c29cc05ffb5503c31633))
66 |
67 |
68 |
69 | ## [1.6.3](https://github.com/mastergo-design/html-to-mastergo/compare/1.6.0...1.6.3) (2023-03-14)
70 |
71 |
72 | ### Bug Fixes
73 |
74 | * 忽略textArea的children ([9d7d574](https://github.com/mastergo-design/html-to-mastergo/commit/9d7d57428c2528ec50269af8672fb14b90412ae4))
75 | * 透明度增加默认值 ([edfab51](https://github.com/mastergo-design/html-to-mastergo/commit/edfab51b38a4da44f74a3acc6ce9727db06a697c))
76 |
77 |
78 | ### Features
79 |
80 | * 更新说明文档 ([ab42136](https://github.com/mastergo-design/html-to-mastergo/commit/ab421366bf826536824d95198dad5466c3b90be1))
81 | * 移除多余代码 ([df0e979](https://github.com/mastergo-design/html-to-mastergo/commit/df0e9799d583d4001617282afae9a8f7b1c5e9f5))
82 | * 整理类型文件 ([c387641](https://github.com/mastergo-design/html-to-mastergo/commit/c387641a8d9a9410e527901a2cb3ab723c094495))
83 | * 整理文件 ([8129e4f](https://github.com/mastergo-design/html-to-mastergo/commit/8129e4f2ae21e573f9fec9782dc1442f316fc955))
84 |
85 |
86 |
87 | ## [1.6.2](https://github.com/mastergo-design/html-to-mastergo/compare/1.6.0...1.6.2) (2023-03-13)
88 |
89 |
90 | ### Bug Fixes
91 |
92 | * 忽略textArea的children ([9d7d574](https://github.com/mastergo-design/html-to-mastergo/commit/9d7d57428c2528ec50269af8672fb14b90412ae4))
93 | * 透明度增加默认值 ([edfab51](https://github.com/mastergo-design/html-to-mastergo/commit/edfab51b38a4da44f74a3acc6ce9727db06a697c))
94 |
95 |
96 | ### Features
97 |
98 | * 整理类型文件 ([c387641](https://github.com/mastergo-design/html-to-mastergo/commit/c387641a8d9a9410e527901a2cb3ab723c094495))
99 |
100 |
101 |
102 | ## [1.6.1](https://github.com/mastergo-design/html-to-mastergo/compare/v1.0.0...v1.6.1) (2023-03-07)
103 |
104 |
105 | ### Bug Fixes
106 |
107 | * 移除无效示例代码 ([0860cd4](https://github.com/mastergo-design/html-to-mastergo/commit/0860cd4fa3a3ef0e165765689e21c948a7bd553e))
108 |
109 |
110 | ### Features
111 | * 整理类型文件 ([c387641](https://github.com/mastergo-design/html-to-mastergo/commit/c387641a8d9a9410e527901a2cb3ab723c094495))
112 |
113 |
114 |
115 | # [1.6.0](https://github.com/mastergo-design/html-to-mastergo/compare/v1.0.0...v1.6.0) (2023-03-07)
116 |
117 |
118 | ### Bug Fixes
119 |
120 | * 输入框判断增加tagName ([a266ab9](https://github.com/mastergo-design/html-to-mastergo/commit/a266ab91c69f9d8bf96e9c82eccb02ec1b0d8eb1))
121 | * 文字处理折行 ([22ac729](https://github.com/mastergo-design/html-to-mastergo/commit/22ac729f34f25a2b9f863e75fb74b1b33443a627))
122 | * 文字垂直居中问题 ([49af46f](https://github.com/mastergo-design/html-to-mastergo/commit/49af46f8c52683c6292b0f78607306a417bdc2cb))
123 | * 修改错误的blendmode映射 ([abeec7d](https://github.com/mastergo-design/html-to-mastergo/commit/abeec7de96b05dcef4abca681e47198da44956c7))
124 | * 移除无效示例代码 ([0860cd4](https://github.com/mastergo-design/html-to-mastergo/commit/0860cd4fa3a3ef0e165765689e21c948a7bd553e))
125 |
126 |
127 | ### Features
128 |
129 | * 处理不同的图片后缀格式, 增加报错 ([8ed11bb](https://github.com/mastergo-design/html-to-mastergo/commit/8ed11bb21d72406bd8be8fa27f2a0589582540c6))
130 | * 处理渐变 ([e06ebc0](https://github.com/mastergo-design/html-to-mastergo/commit/e06ebc02a9ca93cd68ab9f94e97fc2223a5fd6ea))
131 | * 处理伪元素 ([8242774](https://github.com/mastergo-design/html-to-mastergo/commit/8242774c9920340a8ac2e1677d3672bced841796))
132 | * 处理svg描边和填充 ([7fcdc2e](https://github.com/mastergo-design/html-to-mastergo/commit/7fcdc2e053bb800da9ddb812ddc8aa2939226e9c))
133 | * 处理svg行内样式的fill和stroke ([c8f7929](https://github.com/mastergo-design/html-to-mastergo/commit/c8f7929cf5d794ead075846904f6ba21861e20d6))
134 | * 兼容不同字体名 ([a647e81](https://github.com/mastergo-design/html-to-mastergo/commit/a647e81e8f6ff94dfe768e9f54501ee0ba767e41))
135 | * 宽高限制在0.01 ([e2462e7](https://github.com/mastergo-design/html-to-mastergo/commit/e2462e773db22a7ef47c94a96c1d4a834da9ff86))
136 | * 删除多余代码 ([26f3b58](https://github.com/mastergo-design/html-to-mastergo/commit/26f3b58063d975603034e9c66954bb8435baf4c0))
137 | * 适配文字字体 ([a40548c](https://github.com/mastergo-design/html-to-mastergo/commit/a40548cbbcaaf4eae2eeb95a7a65cf9b23f6d0f4))
138 | * 修改依赖 ([b31f1f3](https://github.com/mastergo-design/html-to-mastergo/commit/b31f1f3b8ca7b234a98d216cd35b7f1ad82a03c1))
139 | * 引入npm包 ([cc82a50](https://github.com/mastergo-design/html-to-mastergo/commit/cc82a5074e1b6f3a68a4523be843a26823884ad4))
140 | * 用bounding真实宽高替换styles的宽高 ([9cee9f4](https://github.com/mastergo-design/html-to-mastergo/commit/9cee9f473efa0d52f8b3f5c380cee99150423a89))
141 | * 优化自动布局, layout, 圆角属性 ([4a1c215](https://github.com/mastergo-design/html-to-mastergo/commit/4a1c21513db8b44bb12c5713cf3120a32662a678))
142 | * 优化demo ([09bc57b](https://github.com/mastergo-design/html-to-mastergo/commit/09bc57b4dadaaf17b719a568b47eda35e721c094))
143 | * 增加常量 ([163c5af](https://github.com/mastergo-design/html-to-mastergo/commit/163c5af0b73943c31263df4610a9f74e27a4e99d))
144 | * 增加输入框文字处理 ([bbd982b](https://github.com/mastergo-design/html-to-mastergo/commit/bbd982bd20472a86d85e504b37b33206860453c2))
145 | * 支持描边单边颜色 ([4e6c4cf](https://github.com/mastergo-design/html-to-mastergo/commit/4e6c4cf67ecb28d9d2caf8661a2363efed3b11d9))
146 | * 支持线性渐变 ([a2c9b36](https://github.com/mastergo-design/html-to-mastergo/commit/a2c9b36335c56282b689b80deb903b0566a3cb83))
147 | * clear project ([4c0a78c](https://github.com/mastergo-design/html-to-mastergo/commit/4c0a78ccdd0cbca690bcbd1660272496b1b99e3b))
148 |
149 |
150 |
151 | # [1.5.0](https://github.com/mastergo-design/html-to-mastergo/compare/v1.0.0...v1.5.0) (2023-02-24)
152 |
153 |
154 | ### Bug Fixes
155 |
156 | * 输入框判断增加tagName ([a266ab9](https://github.com/mastergo-design/html-to-mastergo/commit/a266ab91c69f9d8bf96e9c82eccb02ec1b0d8eb1))
157 | * 修改错误的blendmode映射 ([abeec7d](https://github.com/mastergo-design/html-to-mastergo/commit/abeec7de96b05dcef4abca681e47198da44956c7))
158 | * 移除无效示例代码 ([0860cd4](https://github.com/mastergo-design/html-to-mastergo/commit/0860cd4fa3a3ef0e165765689e21c948a7bd553e))
159 |
160 |
161 | ### Features
162 |
163 | * 处理不同的图片后缀格式, 增加报错 ([8ed11bb](https://github.com/mastergo-design/html-to-mastergo/commit/8ed11bb21d72406bd8be8fa27f2a0589582540c6))
164 | * 处理渐变 ([e06ebc0](https://github.com/mastergo-design/html-to-mastergo/commit/e06ebc02a9ca93cd68ab9f94e97fc2223a5fd6ea))
165 | * 处理伪元素 ([8242774](https://github.com/mastergo-design/html-to-mastergo/commit/8242774c9920340a8ac2e1677d3672bced841796))
166 | * 处理svg描边和填充 ([7fcdc2e](https://github.com/mastergo-design/html-to-mastergo/commit/7fcdc2e053bb800da9ddb812ddc8aa2939226e9c))
167 | * 处理svg行内样式的fill和stroke ([c8f7929](https://github.com/mastergo-design/html-to-mastergo/commit/c8f7929cf5d794ead075846904f6ba21861e20d6))
168 | * 宽高限制在0.01 ([e2462e7](https://github.com/mastergo-design/html-to-mastergo/commit/e2462e773db22a7ef47c94a96c1d4a834da9ff86))
169 | * 适配文字字体 ([a40548c](https://github.com/mastergo-design/html-to-mastergo/commit/a40548cbbcaaf4eae2eeb95a7a65cf9b23f6d0f4))
170 | * 修改依赖 ([b31f1f3](https://github.com/mastergo-design/html-to-mastergo/commit/b31f1f3b8ca7b234a98d216cd35b7f1ad82a03c1))
171 | * 引入npm包 ([cc82a50](https://github.com/mastergo-design/html-to-mastergo/commit/cc82a5074e1b6f3a68a4523be843a26823884ad4))
172 | * 用bounding真实宽高替换styles的宽高 ([9cee9f4](https://github.com/mastergo-design/html-to-mastergo/commit/9cee9f473efa0d52f8b3f5c380cee99150423a89))
173 | * 优化自动布局, layout, 圆角属性 ([4a1c215](https://github.com/mastergo-design/html-to-mastergo/commit/4a1c21513db8b44bb12c5713cf3120a32662a678))
174 | * 优化demo ([09bc57b](https://github.com/mastergo-design/html-to-mastergo/commit/09bc57b4dadaaf17b719a568b47eda35e721c094))
175 | * 增加常量 ([163c5af](https://github.com/mastergo-design/html-to-mastergo/commit/163c5af0b73943c31263df4610a9f74e27a4e99d))
176 | * 增加输入框文字处理 ([bbd982b](https://github.com/mastergo-design/html-to-mastergo/commit/bbd982bd20472a86d85e504b37b33206860453c2))
177 | * 支持线性渐变 ([a2c9b36](https://github.com/mastergo-design/html-to-mastergo/commit/a2c9b36335c56282b689b80deb903b0566a3cb83))
178 | * clear project ([4c0a78c](https://github.com/mastergo-design/html-to-mastergo/commit/4c0a78ccdd0cbca690bcbd1660272496b1b99e3b))
179 |
180 |
181 |
182 |
183 |
184 | # [1.4.0](https://github.com/mastergo-design/html-to-mastergo/compare/v1.0.0...v1.4.0) (2023-02-10)
185 |
186 |
187 | ### Bug Fixes
188 |
189 | * 输入框判断增加tagName ([a266ab9](https://github.com/mastergo-design/html-to-mastergo/commit/a266ab91c69f9d8bf96e9c82eccb02ec1b0d8eb1))
190 | * 修改错误的blendmode映射 ([abeec7d](https://github.com/mastergo-design/html-to-mastergo/commit/abeec7de96b05dcef4abca681e47198da44956c7))
191 |
192 |
193 | ### Features
194 |
195 | * 处理不同的图片后缀格式, 增加报错 ([8ed11bb](https://github.com/mastergo-design/html-to-mastergo/commit/8ed11bb21d72406bd8be8fa27f2a0589582540c6))
196 | * 处理伪元素 ([8242774](https://github.com/mastergo-design/html-to-mastergo/commit/8242774c9920340a8ac2e1677d3672bced841796))
197 | * 处理svg描边和填充 ([7fcdc2e](https://github.com/mastergo-design/html-to-mastergo/commit/7fcdc2e053bb800da9ddb812ddc8aa2939226e9c))
198 | * 处理svg行内样式的fill和stroke ([c8f7929](https://github.com/mastergo-design/html-to-mastergo/commit/c8f7929cf5d794ead075846904f6ba21861e20d6))
199 | * 宽高限制在0.01 ([e2462e7](https://github.com/mastergo-design/html-to-mastergo/commit/e2462e773db22a7ef47c94a96c1d4a834da9ff86))
200 |
201 |
202 | ## [1.3.2](https://github.com/mastergo-design/html-to-mastergo/compare/v1.0.0...v1.3.2) (2023-01-11)
203 |
204 |
205 | ### Bug Fixes
206 |
207 | * 输入框判断增加tagName ([a266ab9](https://github.com/mastergo-design/html-to-mastergo/commit/a266ab91c69f9d8bf96e9c82eccb02ec1b0d8eb1))
208 |
209 | ### Features
210 |
211 | * 处理伪元素 ([8242774](https://github.com/mastergo-design/html-to-mastergo/commit/8242774c9920340a8ac2e1677d3672bced841796))
212 | * 处理svg描边和填充 ([7fcdc2e](https://github.com/mastergo-design/html-to-mastergo/commit/7fcdc2e053bb800da9ddb812ddc8aa2939226e9c))
213 | * 处理svg行内样式的fill和stroke ([c8f7929](https://github.com/mastergo-design/html-to-mastergo/commit/c8f7929cf5d794ead075846904f6ba21861e20d6))
214 |
215 |
216 |
217 | ## [1.3.1](https://github.com/mastergo-design/html-to-mastergo/compare/v1.0.0...v1.3.1) (2023-01-10)
218 |
219 |
220 | ### Bug Fixes
221 |
222 | * 输入框判断增加tagName ([a266ab9](https://github.com/mastergo-design/html-to-mastergo/commit/a266ab91c69f9d8bf96e9c82eccb02ec1b0d8eb1))
223 | * 移除无效示例代码 ([0860cd4](https://github.com/mastergo-design/html-to-mastergo/commit/0860cd4fa3a3ef0e165765689e21c948a7bd553e))
224 |
225 |
226 | ### Features
227 |
228 | * 处理伪元素 ([8242774](https://github.com/mastergo-design/html-to-mastergo/commit/8242774c9920340a8ac2e1677d3672bced841796))
229 | * 处理svg描边和填充 ([7fcdc2e](https://github.com/mastergo-design/html-to-mastergo/commit/7fcdc2e053bb800da9ddb812ddc8aa2939226e9c))
230 | * 适配文字字体 ([a40548c](https://github.com/mastergo-design/html-to-mastergo/commit/a40548cbbcaaf4eae2eeb95a7a65cf9b23f6d0f4))
231 | * 修改依赖 ([b31f1f3](https://github.com/mastergo-design/html-to-mastergo/commit/b31f1f3b8ca7b234a98d216cd35b7f1ad82a03c1))
232 | * 引入npm包 ([cc82a50](https://github.com/mastergo-design/html-to-mastergo/commit/cc82a5074e1b6f3a68a4523be843a26823884ad4))
233 | * 用bounding真实宽高替换styles的宽高 ([9cee9f4](https://github.com/mastergo-design/html-to-mastergo/commit/9cee9f473efa0d52f8b3f5c380cee99150423a89))
234 | * 优化自动布局, layout, 圆角属性 ([4a1c215](https://github.com/mastergo-design/html-to-mastergo/commit/4a1c21513db8b44bb12c5713cf3120a32662a678))
235 | * 优化demo ([09bc57b](https://github.com/mastergo-design/html-to-mastergo/commit/09bc57b4dadaaf17b719a568b47eda35e721c094))
236 | * 增加输入框文字处理 ([bbd982b](https://github.com/mastergo-design/html-to-mastergo/commit/bbd982bd20472a86d85e504b37b33206860453c2))
237 | * clear project ([4c0a78c](https://github.com/mastergo-design/html-to-mastergo/commit/4c0a78ccdd0cbca690bcbd1660272496b1b99e3b))
238 |
239 |
240 |
241 | # [1.3.0](https://github.com/mastergo-design/html-to-mastergo/compare/v1.0.0...v1.3.0) (2023-01-10)
242 |
243 |
244 | ### Bug Fixes
245 |
246 | * 移除无效示例代码 ([0860cd4](https://github.com/mastergo-design/html-to-mastergo/commit/0860cd4fa3a3ef0e165765689e21c948a7bd553e))
247 |
248 |
249 | ### Features
250 |
251 | * 处理伪元素 ([8242774](https://github.com/mastergo-design/html-to-mastergo/commit/8242774c9920340a8ac2e1677d3672bced841796))
252 | * 处理svg描边和填充 ([7fcdc2e](https://github.com/mastergo-design/html-to-mastergo/commit/7fcdc2e053bb800da9ddb812ddc8aa2939226e9c))
253 | * 适配文字字体 ([a40548c](https://github.com/mastergo-design/html-to-mastergo/commit/a40548cbbcaaf4eae2eeb95a7a65cf9b23f6d0f4))
254 | * 修改依赖 ([b31f1f3](https://github.com/mastergo-design/html-to-mastergo/commit/b31f1f3b8ca7b234a98d216cd35b7f1ad82a03c1))
255 | * 引入npm包 ([cc82a50](https://github.com/mastergo-design/html-to-mastergo/commit/cc82a5074e1b6f3a68a4523be843a26823884ad4))
256 | * 用bounding真实宽高替换styles的宽高 ([9cee9f4](https://github.com/mastergo-design/html-to-mastergo/commit/9cee9f473efa0d52f8b3f5c380cee99150423a89))
257 | * 优化自动布局, layout, 圆角属性 ([4a1c215](https://github.com/mastergo-design/html-to-mastergo/commit/4a1c21513db8b44bb12c5713cf3120a32662a678))
258 | * 优化demo ([09bc57b](https://github.com/mastergo-design/html-to-mastergo/commit/09bc57b4dadaaf17b719a568b47eda35e721c094))
259 | * 增加输入框文字处理 ([bbd982b](https://github.com/mastergo-design/html-to-mastergo/commit/bbd982bd20472a86d85e504b37b33206860453c2))
260 | * clear project ([4c0a78c](https://github.com/mastergo-design/html-to-mastergo/commit/4c0a78ccdd0cbca690bcbd1660272496b1b99e3b))
261 |
262 |
263 |
264 | # [1.2.0](https://github.com/mastergo-design/html-to-mastergo/compare/v1.0.0...v1.2.0) (2023-01-06)
265 |
266 |
267 | ### Bug Fixes
268 |
269 | * 移除无效示例代码 ([0860cd4](https://github.com/mastergo-design/html-to-mastergo/commit/0860cd4fa3a3ef0e165765689e21c948a7bd553e))
270 |
271 |
272 | ### Features
273 |
274 | * 处理伪元素 ([8242774](https://github.com/mastergo-design/html-to-mastergo/commit/8242774c9920340a8ac2e1677d3672bced841796))
275 | * 适配文字字体 ([a40548c](https://github.com/mastergo-design/html-to-mastergo/commit/a40548cbbcaaf4eae2eeb95a7a65cf9b23f6d0f4))
276 | * 修改依赖 ([b31f1f3](https://github.com/mastergo-design/html-to-mastergo/commit/b31f1f3b8ca7b234a98d216cd35b7f1ad82a03c1))
277 | * 引入npm包 ([cc82a50](https://github.com/mastergo-design/html-to-mastergo/commit/cc82a5074e1b6f3a68a4523be843a26823884ad4))
278 | * 用bounding真实宽高替换styles的宽高 ([9cee9f4](https://github.com/mastergo-design/html-to-mastergo/commit/9cee9f473efa0d52f8b3f5c380cee99150423a89))
279 | * 优化自动布局, layout, 圆角属性 ([4a1c215](https://github.com/mastergo-design/html-to-mastergo/commit/4a1c21513db8b44bb12c5713cf3120a32662a678))
280 | * 优化demo ([09bc57b](https://github.com/mastergo-design/html-to-mastergo/commit/09bc57b4dadaaf17b719a568b47eda35e721c094))
281 | * 增加输入框文字处理 ([bbd982b](https://github.com/mastergo-design/html-to-mastergo/commit/bbd982bd20472a86d85e504b37b33206860453c2))
282 | * clear project ([4c0a78c](https://github.com/mastergo-design/html-to-mastergo/commit/4c0a78ccdd0cbca690bcbd1660272496b1b99e3b))
283 |
284 |
285 |
286 | # [1.1.0](https://github.com/mastergo-design/html-to-mastergo/compare/v1.0.0...v1.1.0) (2022-12-23)
287 |
288 |
289 | ### Features
290 |
291 | * 处理伪元素 ([8242774](https://github.com/mastergo-design/html-to-mastergo/commit/8242774c9920340a8ac2e1677d3672bced841796))
292 | * 适配文字字体 ([a40548c](https://github.com/mastergo-design/html-to-mastergo/commit/a40548cbbcaaf4eae2eeb95a7a65cf9b23f6d0f4))
293 | * 修改依赖 ([b31f1f3](https://github.com/mastergo-design/html-to-mastergo/commit/b31f1f3b8ca7b234a98d216cd35b7f1ad82a03c1))
294 | * 引入npm包 ([cc82a50](https://github.com/mastergo-design/html-to-mastergo/commit/cc82a5074e1b6f3a68a4523be843a26823884ad4))
295 | * 优化自动布局, layout, 圆角属性 ([4a1c215](https://github.com/mastergo-design/html-to-mastergo/commit/4a1c21513db8b44bb12c5713cf3120a32662a678))
296 | * clear project ([4c0a78c](https://github.com/mastergo-design/html-to-mastergo/commit/4c0a78ccdd0cbca690bcbd1660272496b1b99e3b))
297 |
298 |
299 |
300 | ## [1.0.3](https://github.com/mastergo-design/html-to-mastergo/compare/v1.0.0...v1.0.3) (2022-12-09)
301 |
302 |
303 | ### Features
304 |
305 | * 适配文字字体 ([a40548c](https://github.com/mastergo-design/html-to-mastergo/commit/a40548cbbcaaf4eae2eeb95a7a65cf9b23f6d0f4))
306 | * 修改依赖 ([b31f1f3](https://github.com/mastergo-design/html-to-mastergo/commit/b31f1f3b8ca7b234a98d216cd35b7f1ad82a03c1))
307 | * 引入npm包 ([cc82a50](https://github.com/mastergo-design/html-to-mastergo/commit/cc82a5074e1b6f3a68a4523be843a26823884ad4))
308 | * 优化自动布局, layout, 圆角属性 ([4a1c215](https://github.com/mastergo-design/html-to-mastergo/commit/4a1c21513db8b44bb12c5713cf3120a32662a678))
309 | * clear project ([4c0a78c](https://github.com/mastergo-design/html-to-mastergo/commit/4c0a78ccdd0cbca690bcbd1660272496b1b99e3b))
310 |
311 |
312 |
313 | ## [1.0.2](https://github.com/mastergo-design/html-to-mastergo/compare/v1.0.0...v1.0.2) (2022-12-09)
314 |
315 |
316 | ### Features
317 |
318 | * 修改依赖 ([b31f1f3](https://github.com/mastergo-design/html-to-mastergo/commit/b31f1f3b8ca7b234a98d216cd35b7f1ad82a03c1))
319 | * 引入npm包 ([cc82a50](https://github.com/mastergo-design/html-to-mastergo/commit/cc82a5074e1b6f3a68a4523be843a26823884ad4))
320 | * 优化自动布局, layout, 圆角属性 ([4a1c215](https://github.com/mastergo-design/html-to-mastergo/commit/4a1c21513db8b44bb12c5713cf3120a32662a678))
321 | * clear project ([4c0a78c](https://github.com/mastergo-design/html-to-mastergo/commit/4c0a78ccdd0cbca690bcbd1660272496b1b99e3b))
322 |
323 |
324 |
325 | ## [1.0.1](https://github.com/mastergo-design/html-to-mastergo/compare/v1.0.0...v1.0.1) (2022-12-09)
326 |
327 |
328 | ### Features
329 |
330 | * 修改依赖 ([b31f1f3](https://github.com/mastergo-design/html-to-mastergo/commit/b31f1f3b8ca7b234a98d216cd35b7f1ad82a03c1))
331 | * 引入npm包 ([cc82a50](https://github.com/mastergo-design/html-to-mastergo/commit/cc82a5074e1b6f3a68a4523be843a26823884ad4))
332 | * 优化自动布局, layout, 圆角属性 ([4a1c215](https://github.com/mastergo-design/html-to-mastergo/commit/4a1c21513db8b44bb12c5713cf3120a32662a678))
333 | * clear project ([4c0a78c](https://github.com/mastergo-design/html-to-mastergo/commit/4c0a78ccdd0cbca690bcbd1660272496b1b99e3b))
334 |
335 |
336 |
337 | # 1.0.0 (2022-10-28)
338 |
339 |
340 | ### Bug Fixes
341 |
342 | * readme url ([245c030](https://github.com/mastergo-design/html-to-mastergo/commit/245c0308a8ab79135a9698f7fbd35cb013839775))
343 |
344 |
345 | ### Features
346 |
347 | * 1. 字体加载异常处理 2. auto计算 3. display:none处理 ([8c5d2b6](https://github.com/mastergo-design/html-to-mastergo/commit/8c5d2b652d715910210035dcbccf9e828d91913c))
348 | * 1. 坐标计算逻辑调整 2. objectFit ([1ebea22](https://github.com/mastergo-design/html-to-mastergo/commit/1ebea227fe759dc07dfab0e23d1f5b772c1f4c96))
349 | * 1.github开源协议和issue模板,中英文readme 2. 优化发布npm脚本,整理冗余代码 ([f3ba0e3](https://github.com/mastergo-design/html-to-mastergo/commit/f3ba0e3dcfc8572a1929160f98a596c9dd9b3ee9))
350 | * 补充初始化项目依赖 ([2c92854](https://github.com/mastergo-design/html-to-mastergo/commit/2c92854259723a7eaffe2a834e60fb04dfa40612))
351 | * 处理图片 ([4c313aa](https://github.com/mastergo-design/html-to-mastergo/commit/4c313aa08bd99bb9e55b2d217fe5513d83f3ed47))
352 | * 处理图片, 特殊处理文字的textStyles ([f9ca7ab](https://github.com/mastergo-design/html-to-mastergo/commit/f9ca7ab9ee06bbf4ed182eb9533e958cc33f89a0))
353 | * 处理图片, 特殊处理文字的textStyles ([64d2acc](https://github.com/mastergo-design/html-to-mastergo/commit/64d2acc3512a3c8f4579e77a8dd0ca23af7ab01f))
354 | * 处理图片, 特殊处理文字的textStyles ([b13761e](https://github.com/mastergo-design/html-to-mastergo/commit/b13761ea1a7e9dfffb39de0e07a2ae4c4b3fd348))
355 | * 单独处理自动布局 ([3b04b16](https://github.com/mastergo-design/html-to-mastergo/commit/3b04b164cad9e42a25007c2890eaad6dfff05eaa))
356 | * 联调sdk ([ecae311](https://github.com/mastergo-design/html-to-mastergo/commit/ecae31118607695f38e146e8827b596d609260cb))
357 | * 配置发布脚本和vite打包设置 ([60ef023](https://github.com/mastergo-design/html-to-mastergo/commit/60ef023efffbba19c54d81e0035e8eade5638926))
358 | * 删除冗余代码 ([6703ebe](https://github.com/mastergo-design/html-to-mastergo/commit/6703ebe5324f4673ad3765d8dbb699a9c9d545c7))
359 | * 删除冗余代码 ([9840081](https://github.com/mastergo-design/html-to-mastergo/commit/9840081447318b4905317593a308d13806d14b3f))
360 | * 图层数据转换 ([eec6043](https://github.com/mastergo-design/html-to-mastergo/commit/eec604369e7e73e9decb669ddfab0f088a6936da))
361 | * 图片加载兼容错误 ([309cdc9](https://github.com/mastergo-design/html-to-mastergo/commit/309cdc913c56b0d7eec3df9fd9417e30fb951a47))
362 | * 图片属性支持 ([f1f92a1](https://github.com/mastergo-design/html-to-mastergo/commit/f1f92a15de79a089a6f3d320e36f5163bce6a9cb))
363 | * 修改readme, 增加git地址 ([1614808](https://github.com/mastergo-design/html-to-mastergo/commit/1614808923fcbd717645638d3e86030477a9ab61))
364 | * 修改regex ([0f46a92](https://github.com/mastergo-design/html-to-mastergo/commit/0f46a92dd85c0746ec1ec44cea41be3e75c4125b))
365 | * 一些基础属性的转换定义 ([beed691](https://github.com/mastergo-design/html-to-mastergo/commit/beed69126b4e2eac4b2b8fd95cf7c2af1094e49f))
366 | * 移除test ([ef485ea](https://github.com/mastergo-design/html-to-mastergo/commit/ef485ea930821ef59b1345700a78f69df775980c))
367 | * 增加插件示例 ([2efea0b](https://github.com/mastergo-design/html-to-mastergo/commit/2efea0b2f267cddca603d60e0e838ecc7d98d51f))
368 | * 增加MIT license ([b365474](https://github.com/mastergo-design/html-to-mastergo/commit/b3654744da58d47426915706eb209580ca9c85a2))
369 | * 整理代码 ([b5bcd96](https://github.com/mastergo-design/html-to-mastergo/commit/b5bcd9643d0882e666e0e40d400d65484f87b609))
370 | * 支持translate ([bc8e733](https://github.com/mastergo-design/html-to-mastergo/commit/bc8e733cc212b3ee8309eb1d0994347aa2a60515))
371 | * add changelog ([d401664](https://github.com/mastergo-design/html-to-mastergo/commit/d401664c5bfe2d17859ba99aba32ea74234011cc))
372 | * base64返回uint8Array ([1583277](https://github.com/mastergo-design/html-to-mastergo/commit/1583277784ad3517d6370805ca8dee7e570b21a0))
373 | * delete lib ([b47ef6e](https://github.com/mastergo-design/html-to-mastergo/commit/b47ef6e8f2d4aa58ff7966fe641f0a6a1074af30))
374 | * demo增加拖拽功能 ([bd32f6e](https://github.com/mastergo-design/html-to-mastergo/commit/bd32f6e27f439f9ed5b90cd87d75244a22d60567))
375 | * demo增加拖拽功能 ([f7680c9](https://github.com/mastergo-design/html-to-mastergo/commit/f7680c96ff33ef00db742bce7ed1bec4a21737db))
376 | * ignore lib ([685c8c6](https://github.com/mastergo-design/html-to-mastergo/commit/685c8c69ecc4e495c5dbcdd53a48d19237cd4375))
377 | * sdk代码整理 ([9c1e736](https://github.com/mastergo-design/html-to-mastergo/commit/9c1e736272fd3925cc1d38bf6c24b1f0fece9022))
378 | * svg和文字部分属性支持 ([2ed7dc2](https://github.com/mastergo-design/html-to-mastergo/commit/2ed7dc289110422b794fbfdf03d69bdd7d8bd1ac))
379 | * ui线程处理图片 ([049844e](https://github.com/mastergo-design/html-to-mastergo/commit/049844ed4388a6538ee09a2ec950298bb2102fd1))
380 |
381 |
382 |
383 | # 0.9.0 (2022-10-27)
384 |
385 |
386 | ### Features
387 |
388 | * 1. 字体加载异常处理 2. auto计算 3. display:none处理 ([8c5d2b6](https://github.com/mastergo-design/html-to-mastergo/commit/8c5d2b652d715910210035dcbccf9e828d91913c))
389 | * 1. 坐标计算逻辑调整 2. objectFit ([1ebea22](https://github.com/mastergo-design/html-to-mastergo/commit/1ebea227fe759dc07dfab0e23d1f5b772c1f4c96))
390 | * 1.github开源协议和issue模板,中英文readme 2. 优化发布npm脚本,整理冗余代码 ([f3ba0e3](https://github.com/mastergo-design/html-to-mastergo/commit/f3ba0e3dcfc8572a1929160f98a596c9dd9b3ee9))
391 | * 补充初始化项目依赖 ([2c92854](https://github.com/mastergo-design/html-to-mastergo/commit/2c92854259723a7eaffe2a834e60fb04dfa40612))
392 | * 处理图片 ([4c313aa](https://github.com/mastergo-design/html-to-mastergo/commit/4c313aa08bd99bb9e55b2d217fe5513d83f3ed47))
393 | * 处理图片, 特殊处理文字的textStyles ([f9ca7ab](https://github.com/mastergo-design/html-to-mastergo/commit/f9ca7ab9ee06bbf4ed182eb9533e958cc33f89a0))
394 | * 处理图片, 特殊处理文字的textStyles ([64d2acc](https://github.com/mastergo-design/html-to-mastergo/commit/64d2acc3512a3c8f4579e77a8dd0ca23af7ab01f))
395 | * 处理图片, 特殊处理文字的textStyles ([b13761e](https://github.com/mastergo-design/html-to-mastergo/commit/b13761ea1a7e9dfffb39de0e07a2ae4c4b3fd348))
396 | * 单独处理自动布局 ([3b04b16](https://github.com/mastergo-design/html-to-mastergo/commit/3b04b164cad9e42a25007c2890eaad6dfff05eaa))
397 | * 联调sdk ([ecae311](https://github.com/mastergo-design/html-to-mastergo/commit/ecae31118607695f38e146e8827b596d609260cb))
398 | * 配置发布脚本和vite打包设置 ([60ef023](https://github.com/mastergo-design/html-to-mastergo/commit/60ef023efffbba19c54d81e0035e8eade5638926))
399 | * 删除冗余代码 ([6703ebe](https://github.com/mastergo-design/html-to-mastergo/commit/6703ebe5324f4673ad3765d8dbb699a9c9d545c7))
400 | * 删除冗余代码 ([9840081](https://github.com/mastergo-design/html-to-mastergo/commit/9840081447318b4905317593a308d13806d14b3f))
401 | * 图层数据转换 ([eec6043](https://github.com/mastergo-design/html-to-mastergo/commit/eec604369e7e73e9decb669ddfab0f088a6936da))
402 | * 图片加载兼容错误 ([309cdc9](https://github.com/mastergo-design/html-to-mastergo/commit/309cdc913c56b0d7eec3df9fd9417e30fb951a47))
403 | * 图片属性支持 ([f1f92a1](https://github.com/mastergo-design/html-to-mastergo/commit/f1f92a15de79a089a6f3d320e36f5163bce6a9cb))
404 | * 修改readme, 增加git地址 ([1614808](https://github.com/mastergo-design/html-to-mastergo/commit/1614808923fcbd717645638d3e86030477a9ab61))
405 | * 修改regex ([0f46a92](https://github.com/mastergo-design/html-to-mastergo/commit/0f46a92dd85c0746ec1ec44cea41be3e75c4125b))
406 | * 一些基础属性的转换定义 ([beed691](https://github.com/mastergo-design/html-to-mastergo/commit/beed69126b4e2eac4b2b8fd95cf7c2af1094e49f))
407 | * 移除test ([ef485ea](https://github.com/mastergo-design/html-to-mastergo/commit/ef485ea930821ef59b1345700a78f69df775980c))
408 | * 增加插件示例 ([2efea0b](https://github.com/mastergo-design/html-to-mastergo/commit/2efea0b2f267cddca603d60e0e838ecc7d98d51f))
409 | * 增加MIT license ([b365474](https://github.com/mastergo-design/html-to-mastergo/commit/b3654744da58d47426915706eb209580ca9c85a2))
410 | * 整理代码 ([b5bcd96](https://github.com/mastergo-design/html-to-mastergo/commit/b5bcd9643d0882e666e0e40d400d65484f87b609))
411 | * 支持translate ([bc8e733](https://github.com/mastergo-design/html-to-mastergo/commit/bc8e733cc212b3ee8309eb1d0994347aa2a60515))
412 | * base64返回uint8Array ([1583277](https://github.com/mastergo-design/html-to-mastergo/commit/1583277784ad3517d6370805ca8dee7e570b21a0))
413 | * delete lib ([b47ef6e](https://github.com/mastergo-design/html-to-mastergo/commit/b47ef6e8f2d4aa58ff7966fe641f0a6a1074af30))
414 | * demo增加拖拽功能 ([bd32f6e](https://github.com/mastergo-design/html-to-mastergo/commit/bd32f6e27f439f9ed5b90cd87d75244a22d60567))
415 | * demo增加拖拽功能 ([f7680c9](https://github.com/mastergo-design/html-to-mastergo/commit/f7680c96ff33ef00db742bce7ed1bec4a21737db))
416 | * ignore lib ([685c8c6](https://github.com/mastergo-design/html-to-mastergo/commit/685c8c69ecc4e495c5dbcdd53a48d19237cd4375))
417 | * sdk代码整理 ([9c1e736](https://github.com/mastergo-design/html-to-mastergo/commit/9c1e736272fd3925cc1d38bf6c24b1f0fece9022))
418 | * svg和文字部分属性支持 ([2ed7dc2](https://github.com/mastergo-design/html-to-mastergo/commit/2ed7dc289110422b794fbfdf03d69bdd7d8bd1ac))
419 | * ui线程处理图片 ([049844e](https://github.com/mastergo-design/html-to-mastergo/commit/049844ed4388a6538ee09a2ec950298bb2102fd1))
420 |
421 |
422 |
423 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @mastergo-design
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 MasterGo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # html-mastergo
2 |
3 | [中文](./README.zh-CN.md) | **English**
4 |
5 | A library can convert html into MasterGo plugin data structures.
6 | ## Install
7 |
8 | ```shell
9 | yarn add @mastergo/html-mastergo | npm install @mastergo/html-mastergo
10 | ```
11 |
12 | ## Usage
13 |
14 | 1. Install [MasterGo client](https://mastergo.com/resource) and init a plugin project.
15 |
16 | 2. Build [UI](https://developers.mastergo.com/guide/setup.html#%E6%9E%84%E5%BB%BA%E7%94%A8%E6%88%B7%E7%95%8C%E9%9D%A2)(Click here [MasterGo-Plugin](https://developers.mastergo.com/) if having trouble in how to develop plugins in MasterGo)
17 |
18 | 3. Use the library
19 |
20 | ```typescript
21 | /** UI side **/
22 | import { htmlToMG, postProcess } from '@mastergo/html-mastergo';
23 | // any dom element
24 | const convert = async () => {
25 | const layerJson = await htmlToMG(document.body);
26 | // Not necessary, you can do anything you want to do with json processed by the function htmlToMG. This is just one way to do it.
27 | const processedJson = await postProcess(layerJson)
28 | // post data to plugin
29 | parent.postMessage({
30 | type: 'generate',
31 | data: processedJson
32 | }, '*')
33 | }
34 |
35 |
36 | /** Plugin side **/
37 | import { renderToMasterGo } from '@mastergo/html-mastergo';
38 | mg.ui.onmessage = (msg) => {
39 | const { data, type } = msg
40 | if (type === 'generate') {
41 | // traverse
42 | renderToMasterGo(data).then(root => {
43 | console.log('root node', root)
44 | })
45 | }
46 | }
47 | ```
48 |
49 | ## Limitations
50 |
51 | A few known limitations:
52 |
53 | - Not all element types are supported (e.g. iframes)
54 | - Not all CSS properties are supported or fully supported
55 | - Not all types of media are supported (video, animated gifs, etc)
56 | - All fonts must be uploaded to MasterGo, otherwise the default fonts will be used for rendering
57 |
58 | If you find any problems or have any feedback, please [Ask a Question](https://github.com/mastergo-design/html-to-mastergo/issues/new)
59 |
60 | ## Example
61 |
62 | [Example](./src/example/html-mg)
--------------------------------------------------------------------------------
/README.zh-CN.md:
--------------------------------------------------------------------------------
1 | # html-mastergo
2 |
3 | **中文** | [English](./README.md)
4 |
5 | 一款能将网站页面转成MasterGo插件数据结构的工具库。
6 |
7 |
8 |
9 | ## 安装
10 |
11 | ```shell
12 | yarn add @mastergo/html-mastergo | npm install @mastergo/html-mastergo
13 | ```
14 |
15 | ## 使用
16 |
17 | 1. 使用[mastergo客户端](https://mastergo.com/resource)初始化一个插件项目
18 |
19 | 2. 构建[UI界面](https://developers.mastergo.com/guide/setup.html#%E6%9E%84%E5%BB%BA%E7%94%A8%E6%88%B7%E7%95%8C%E9%9D%A2)(点击这里 [MasterGo-Plugin](https://developers.mastergo.com/) 查看如何开发一个MasterGo插件)
20 |
21 | 3. 使用该库
22 |
23 | ```typescript
24 | /** UI侧 **/
25 | import { htmlToMG, postProcess } from '@mastergo/html-mastergo';
26 | // 任意dom元素
27 | const convert = async () => {
28 | const layerJson = await htmlToMG(document.body);
29 | // 这一步不是必须的,你可以随意处理htmlToMG处理过的json,该函数只是提供了其中一种实现方式。
30 | const processedJson = await postProcess(layerJson)
31 | // post data to plugin
32 | parent.postMessage({
33 | type: 'generate',
34 | data: processedJson
35 | }, '*')
36 | }
37 |
38 |
39 | /** 插件侧 **/
40 | import { renderToMasterGo } from '@mastergo/html-mastergo';
41 | mg.ui.onmessage = (msg) => {
42 | const { data, type } = msg
43 | if (type === 'generate') {
44 | // 递归生成节点
45 | renderToMasterGo(data).then(root => {
46 | console.log('根节点', root)
47 | })
48 | }
49 | }
50 | ```
51 |
52 | ## 限制
53 |
54 | 一些已知的限制:
55 |
56 | - 并非所有元素类型都受支持(例如 iframe)
57 | - 并非所有 CSS 属性都受支持或完全受支持
58 | - 并非所有类型的媒体都受支持(视频、动画 GIF 等)
59 | - 所有字体都必须上传到 MasterGo,否则将使用默认字体
60 |
61 | 如果您发现任何问题或有任何反馈,请[提出问题](https://github.com/mastergo-design/html-to-mastergo/issues/new)
62 |
63 | ## 示例
64 |
65 | [Example](./src/example/html-mg)
--------------------------------------------------------------------------------
/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@mastergo/html-mastergo",
3 | "version": "1.10.0",
4 | "description": "Convert HTML to Mastergo code",
5 | "files": [
6 | "lib"
7 | ],
8 | "keywords": [
9 | "mastergo",
10 | "plugin",
11 | "html",
12 | "design"
13 | ],
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/mastergo-design/html-to-mastergo"
17 | },
18 | "main": "lib/index.js",
19 | "types": "lib/index.d.ts",
20 | "author": "Mastergo",
21 | "license": "MIT",
22 | "scripts": {
23 | "dev": "vite",
24 | "build": "run-p type-check build-only",
25 | "build:lib": "cross-env TARGET=lib vite build",
26 | "preview": "vite preview --port 4173",
27 | "build-only": "vite build",
28 | "type-check": "vue-tsc --noEmit",
29 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
30 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
31 | },
32 | "dependencies": {
33 | "@mastergo/plugin-typings": "^2.4.0",
34 | "transformation-matrix": "^2.15.0"
35 | },
36 | "devDependencies": {
37 | "@rushstack/eslint-patch": "^1.1.4",
38 | "@types/node": "^16.11.56",
39 | "@vitejs/plugin-vue": "^3.1.2",
40 | "@vue/eslint-config-typescript": "^11.0.0",
41 | "@vue/tsconfig": "^0.1.3",
42 | "ant-design-vue": "^3.2.12",
43 | "conventional-changelog-cli": "^2.1.1",
44 | "cross-env": "^7.0.3",
45 | "enquirer": "^2.3.6",
46 | "eslint": "^8.22.0",
47 | "eslint-plugin-vue": "^9.3.0",
48 | "execa": "^5.1.1",
49 | "npm-run-all": "^4.1.5",
50 | "sass": "^1.52.1",
51 | "semver": "^7.3.5",
52 | "typescript": "~4.7.4",
53 | "vite": "^3.0.9",
54 | "vite-plugin-dts": "^1.6.6",
55 | "vue": "^3.2.31",
56 | "vue-tsc": "^0.40.7"
57 | }
58 | }
--------------------------------------------------------------------------------
/public/MasterGo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mastergo-design/html-to-mastergo/1a5bc8a616e47d824e4e48e70fc452d76c9e235e/public/MasterGo.png
--------------------------------------------------------------------------------
/public/html-5.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import type { App, DefineComponent } from 'vue'
3 | const component: DefineComponent<{}, {}, any>
4 |
5 | export default component;
6 | }
7 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ content }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
115 |
116 |
123 |
--------------------------------------------------------------------------------
/src/example/html-mg/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/example/html-mg/lib/main.ts:
--------------------------------------------------------------------------------
1 | import { renderToMasterGo } from '../../../lib/index'
2 | console.clear();
3 |
4 | const main = async () => {
5 | mg.showUI(__html__);
6 | }
7 |
8 | main();
9 |
10 | // mg.on的callback不能用async修饰
11 | mg.on('drop', (evt: DropEvent) => {
12 | const { absoluteX, absoluteY, items } = evt
13 | try {
14 | renderToMasterGo(items).then(node => {
15 | setTimeout(() => {
16 | if (node) {
17 | node.x = absoluteX
18 | node.y = absoluteY
19 | }
20 | console.log('生成成功', node!.x)
21 | }, 100);
22 | })
23 | } catch (error) {
24 | console.error('生成失败', error)
25 | }
26 | })
--------------------------------------------------------------------------------
/src/example/html-mg/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "html-mg",
3 | "id": 73500308634644,
4 | "main": "dist/main.js",
5 | "ui": "dist/index.html"
6 | }
--------------------------------------------------------------------------------
/src/example/html-mg/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-plugin",
3 | "version": "0.1.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "scripts": {
7 | "dev": "NODE_ENV=development yarn dev:ui & yarn dev:main",
8 | "dev:ui": "cross-env TARGET=ui NODE_ENV=development vite build --mode development -w",
9 | "dev:main": "cross-env TARGET=main NODE_ENV=development vite build --mode development -w",
10 | "build": "yarn build:ui && yarn build:main && rm -rf ./dist/assets",
11 | "build:ui": "cross-env TARGET=ui vite build",
12 | "build:main": "cross-env TARGET=main vite build"
13 | },
14 | "devDependencies": {
15 | "@types/node": "^17.0.21",
16 | "@vitejs/plugin-vue": "^2.2.4",
17 | "cross-env": "^7.0.3",
18 | "sass": "^1.52.1",
19 | "unplugin-auto-import": "^0.8.6",
20 | "unplugin-vue-components": "^0.19.6",
21 | "vite": "^2.8.6",
22 | "vite-plugin-singlefile": "^0.7.1"
23 | },
24 | "dependencies": {
25 | "@mastergo/html-mastergo": "^1.6.0",
26 | "@mastergo/plugin-typings": "^1.20.0",
27 | "ant-design-vue": "^3.2.12",
28 | "dayjs": "^1.11.5",
29 | "element-plus": "^2.2.2",
30 | "vue": "^3.2.31"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/example/html-mg/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "sourceMap": false,
5 | "target": "es2015",
6 | "module": "esnext",
7 | "moduleResolution": "node",
8 | "allowJs": false,
9 | "strict": true,
10 | "noUnusedLocals": true,
11 | "resolveJsonModule": true,
12 | "esModuleInterop": true,
13 | "removeComments": false,
14 | "jsx": "preserve",
15 | "importHelpers": true,
16 | "lib": ["esnext", "dom"],
17 | "types": [
18 | "@mastergo/plugin-typings",
19 | "@mastergo/html-mastergo",
20 | "@types/node",
21 | ]
22 | },
23 | "include": [
24 | "./ui/**/*.ts",
25 | "./ui/**/*.vue",
26 | "./typings/*.d.ts",
27 | "./lib/**/*.ts",
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/src/example/html-mg/typings/index.d.ts:
--------------------------------------------------------------------------------
1 | interface PluginAPI {
2 | on(type: PluginEventType | 'drop', callback: CallableFunction): void
3 | }
4 |
5 | interface DropEvent {
6 | x: number // clientX
7 | y: number // clientY
8 | absoluteX: number // 画布x坐标
9 | absoluteY: number // 画布y坐标
10 | items: any
11 | }
12 |
--------------------------------------------------------------------------------
/src/example/html-mg/typings/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import type { App, DefineComponent } from 'vue'
3 | const component: DefineComponent<{}, {}, any>
4 |
5 | export default component;
6 | }
7 |
--------------------------------------------------------------------------------
/src/example/html-mg/ui/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ content }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
144 |
145 |
152 |
--------------------------------------------------------------------------------
/src/example/html-mg/ui/component/breadcrumb.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Home
4 | Application Center
5 | Application List
6 | An Application
7 |
8 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/src/example/html-mg/ui/component/calendar.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/src/example/html-mg/ui/component/image.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ fit }}
5 |
6 |
7 |
8 |
9 |
10 |
17 |
18 |
46 |
--------------------------------------------------------------------------------
/src/example/html-mg/ui/component/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
32 |
--------------------------------------------------------------------------------
/src/example/html-mg/ui/component/menu.vue:
--------------------------------------------------------------------------------
1 |
2 |
50 |
51 |
60 |
61 |
--------------------------------------------------------------------------------
/src/example/html-mg/ui/component/pageHeader.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Running
6 |
7 |
8 | Operation
9 | Operation
10 | Primary
11 |
12 |
13 |
14 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
--------------------------------------------------------------------------------
/src/example/html-mg/ui/component/table.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ text }}
6 |
7 |
8 | Header
9 | Footer
10 |
11 |
12 |
61 |
--------------------------------------------------------------------------------
/src/example/html-mg/ui/global.scss:
--------------------------------------------------------------------------------
1 |
2 | * {
3 | box-sizing: border-box;
4 | }
5 |
6 |
7 | html {
8 | background-color: #fff;
9 | }
10 |
11 | .ant-menu-item-group-title {
12 | height: unset !important;
13 | }
14 |
15 | body {
16 | margin: 0;
17 | font-family: 'PingFang SC', 'Helvetica Neue', Helvetica,
18 | 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
19 | }
--------------------------------------------------------------------------------
/src/example/html-mg/ui/ui.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 | import './global.scss'
3 | import Antd from 'ant-design-vue'
4 | import 'ant-design-vue/dist/antd.css';
5 | import App from './App.vue';
6 |
7 | const app = createApp(App);
8 | app.use(Antd).mount('#app')
--------------------------------------------------------------------------------
/src/example/html-mg/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { resolve } from 'path'
2 | import { defineConfig, BuildOptions } from 'vite'
3 | import vue from '@vitejs/plugin-vue'
4 | import { viteSingleFile } from "vite-plugin-singlefile"
5 | import AutoImport from 'unplugin-auto-import/vite'
6 | import Components from 'unplugin-vue-components/vite'
7 | import { ElementPlusResolver, AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
8 |
9 | const target = process.env.TARGET
10 |
11 | export default defineConfig(() => {
12 | const buildConfig = target === 'ui'
13 | ? {
14 | target: "esnext",
15 | assetsInlineLimit: 100000000,
16 | chunkSizeWarningLimit: 100000000,
17 | cssCodeSplit: false,
18 | brotliSize: false,
19 | rollupOptions: {
20 | inlineDynamicImports: true,
21 | output: {
22 | manualChunks: () => "ui.js",
23 | },
24 | },
25 | }
26 | : {
27 | lib: {
28 | entry: resolve(__dirname, './lib/main.ts'),
29 | name: 'myLib',
30 | formats: ['umd'],
31 | fileName: () => `main.js`
32 | },
33 | }
34 |
35 | return {
36 | plugins: [
37 | vue(),
38 | viteSingleFile(),
39 | AutoImport({
40 | resolvers: [ElementPlusResolver()],
41 | dts: false,
42 | }),
43 | Components({
44 | resolvers: [ElementPlusResolver(), AntDesignVueResolver()],
45 | dts: false,
46 | }),
47 | ],
48 | build: {
49 | ...buildConfig as BuildOptions,
50 | emptyOutDir: false,
51 | minify: process.env.NODE_ENV === 'development'? false : true,
52 | }
53 | }
54 | })
--------------------------------------------------------------------------------
/src/example/html-mg/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@ant-design/colors@^6.0.0":
6 | version "6.0.0"
7 | resolved "https://registry.npmmirror.com/@ant-design/colors/-/colors-6.0.0.tgz#9b9366257cffcc47db42b9d0203bb592c13c0298"
8 | integrity sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==
9 | dependencies:
10 | "@ctrl/tinycolor" "^3.4.0"
11 |
12 | "@ant-design/icons-svg@^4.2.1":
13 | version "4.2.1"
14 | resolved "https://registry.npmmirror.com/@ant-design/icons-svg/-/icons-svg-4.2.1.tgz#8630da8eb4471a4aabdaed7d1ff6a97dcb2cf05a"
15 | integrity sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw==
16 |
17 | "@ant-design/icons-vue@^6.1.0":
18 | version "6.1.0"
19 | resolved "https://registry.npmmirror.com/@ant-design/icons-vue/-/icons-vue-6.1.0.tgz#f9324fdc0eb4cea943cf626d2bf3db9a4ff4c074"
20 | integrity sha512-EX6bYm56V+ZrKN7+3MT/ubDkvJ5rK/O2t380WFRflDcVFgsvl3NLH7Wxeau6R8DbrO5jWR6DSTC3B6gYFp77AA==
21 | dependencies:
22 | "@ant-design/colors" "^6.0.0"
23 | "@ant-design/icons-svg" "^4.2.1"
24 |
25 | "@antfu/utils@^0.5.2":
26 | version "0.5.2"
27 | resolved "https://registry.npmmirror.com/@antfu/utils/-/utils-0.5.2.tgz#8c2d931ff927be0ebe740169874a3d4004ab414b"
28 | integrity sha512-CQkeV+oJxUazwjlHD0/3ZD08QWKuGQkhnrKo3e6ly5pd48VUpXbb77q0xMU4+vc2CkJnDS02Eq/M9ugyX20XZA==
29 |
30 | "@babel/parser@^7.16.4":
31 | version "7.19.3"
32 | resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.19.3.tgz#8dd36d17c53ff347f9e55c328710321b49479a9a"
33 | integrity sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==
34 |
35 | "@babel/runtime@^7.10.5":
36 | version "7.19.0"
37 | resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259"
38 | integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==
39 | dependencies:
40 | regenerator-runtime "^0.13.4"
41 |
42 | "@ctrl/tinycolor@^3.4.0", "@ctrl/tinycolor@^3.4.1":
43 | version "3.4.1"
44 | resolved "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz#75b4c27948c81e88ccd3a8902047bcd797f38d32"
45 | integrity sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==
46 |
47 | "@element-plus/icons-vue@^2.0.6":
48 | version "2.0.9"
49 | resolved "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.0.9.tgz#b7777c57534522e387303d194451d50ff549d49a"
50 | integrity sha512-okdrwiVeKBmW41Hkl0eMrXDjzJwhQMuKiBOu17rOszqM+LS/yBYpNQNV5Jvoh06Wc+89fMmb/uhzf8NZuDuUaQ==
51 |
52 | "@esbuild/linux-loong64@0.14.54":
53 | version "0.14.54"
54 | resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028"
55 | integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==
56 |
57 | "@floating-ui/core@^1.0.1":
58 | version "1.0.1"
59 | resolved "https://registry.npmmirror.com/@floating-ui/core/-/core-1.0.1.tgz#00e64d74e911602c8533957af0cce5af6b2e93c8"
60 | integrity sha512-bO37brCPfteXQfFY0DyNDGB3+IMe4j150KFQcgJ5aBP295p9nBGeHEs/p0czrRbtlHq4Px/yoPXO/+dOCcF4uA==
61 |
62 | "@floating-ui/dom@^1.0.1":
63 | version "1.0.2"
64 | resolved "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.0.2.tgz#c5184c52c6f50abd11052d71204f4be2d9245237"
65 | integrity sha512-5X9WSvZ8/fjy3gDu8yx9HAA4KG1lazUN2P4/VnaXLxTO9Dz53HI1oYoh1OlhqFNlHgGDiwFX5WhFCc2ljbW3yA==
66 | dependencies:
67 | "@floating-ui/core" "^1.0.1"
68 |
69 | "@mastergo/html-mastergo@^1.6.0":
70 | version "1.6.0"
71 | resolved "https://registry.npmjs.org/@mastergo/html-mastergo/-/html-mastergo-1.6.0.tgz#12668e5e14ad789e0c1fdc89ce8436241b2e768c"
72 | integrity sha512-OaNV1Baolm3KdfjYk+GhvgKgA/6AhSShV3IufkY+qCZKNjbhYWUB/YzaQgf1Gy9DQPtVkZg+ep0vwttPfLhIOQ==
73 | dependencies:
74 | "@mastergo/plugin-typings" "^1.12.0"
75 |
76 | "@mastergo/plugin-typings@^1.12.0":
77 | version "1.14.0"
78 | resolved "https://registry.npmjs.org/@mastergo/plugin-typings/-/plugin-typings-1.14.0.tgz#365e28e7e5fcc24e04c183f3da6c3bd293fd2e06"
79 | integrity sha512-bFqZMyr/5/sF9C6eI10BaKd9RM1XR9uh2rlG6n01nRW9y2uqhCikfB4vi0wA3NMcbXUB7eaN5+ioewQK5Lzkhg==
80 |
81 | "@mastergo/plugin-typings@^1.20.0":
82 | version "1.20.0"
83 | resolved "https://registry.npmmirror.com/@mastergo/plugin-typings/-/plugin-typings-1.20.0.tgz#d3c3508c1419f695aeaba98b18ee5773ff724987"
84 | integrity sha512-DFwEv5X5AWovdaj+5S2J+CUSbH+nZvg37yo26641iMciHZFHtPZn1lb6AfhKDS4lI8see/TCmwJhz2+KKyBTRg==
85 |
86 | "@nodelib/fs.scandir@2.1.5":
87 | version "2.1.5"
88 | resolved "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
89 | integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
90 | dependencies:
91 | "@nodelib/fs.stat" "2.0.5"
92 | run-parallel "^1.1.9"
93 |
94 | "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
95 | version "2.0.5"
96 | resolved "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
97 | integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
98 |
99 | "@nodelib/fs.walk@^1.2.3":
100 | version "1.2.8"
101 | resolved "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
102 | integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
103 | dependencies:
104 | "@nodelib/fs.scandir" "2.1.5"
105 | fastq "^1.6.0"
106 |
107 | "@popperjs/core@npm:@sxzz/popperjs-es@^2.11.7":
108 | version "2.11.7"
109 | resolved "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz#a7f69e3665d3da9b115f9e71671dae1b97e13671"
110 | integrity sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==
111 |
112 | "@rollup/plugin-node-resolve@^13.1.3":
113 | version "13.3.0"
114 | resolved "https://registry.npmmirror.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz#da1c5c5ce8316cef96a2f823d111c1e4e498801c"
115 | integrity sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==
116 | dependencies:
117 | "@rollup/pluginutils" "^3.1.0"
118 | "@types/resolve" "1.17.1"
119 | deepmerge "^4.2.2"
120 | is-builtin-module "^3.1.0"
121 | is-module "^1.0.0"
122 | resolve "^1.19.0"
123 |
124 | "@rollup/pluginutils@^3.1.0":
125 | version "3.1.0"
126 | resolved "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
127 | integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
128 | dependencies:
129 | "@types/estree" "0.0.39"
130 | estree-walker "^1.0.1"
131 | picomatch "^2.2.2"
132 |
133 | "@rollup/pluginutils@^4.1.1", "@rollup/pluginutils@^4.2.1":
134 | version "4.2.1"
135 | resolved "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
136 | integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==
137 | dependencies:
138 | estree-walker "^2.0.1"
139 | picomatch "^2.2.2"
140 |
141 | "@simonwep/pickr@~1.8.0":
142 | version "1.8.2"
143 | resolved "https://registry.npmmirror.com/@simonwep/pickr/-/pickr-1.8.2.tgz#96dc86675940d7cad63d69c22083dd1cbb9797cb"
144 | integrity sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==
145 | dependencies:
146 | core-js "^3.15.1"
147 | nanopop "^2.1.0"
148 |
149 | "@types/estree@0.0.39":
150 | version "0.0.39"
151 | resolved "https://registry.npmmirror.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
152 | integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
153 |
154 | "@types/lodash-es@^4.17.6":
155 | version "4.17.6"
156 | resolved "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.6.tgz#c2ed4c8320ffa6f11b43eb89e9eaeec65966a0a0"
157 | integrity sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==
158 | dependencies:
159 | "@types/lodash" "*"
160 |
161 | "@types/lodash@*", "@types/lodash@^4.14.182":
162 | version "4.14.186"
163 | resolved "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.186.tgz#862e5514dd7bd66ada6c70ee5fce844b06c8ee97"
164 | integrity sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==
165 |
166 | "@types/node@*":
167 | version "18.7.23"
168 | resolved "https://registry.npmmirror.com/@types/node/-/node-18.7.23.tgz#75c580983846181ebe5f4abc40fe9dfb2d65665f"
169 | integrity sha512-DWNcCHolDq0ZKGizjx2DZjR/PqsYwAcYUJmfMWqtVU2MBMG5Mo+xFZrhGId5r/O5HOuMPyQEcM6KUBp5lBZZBg==
170 |
171 | "@types/node@^17.0.21":
172 | version "17.0.45"
173 | resolved "https://registry.npmmirror.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190"
174 | integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==
175 |
176 | "@types/resolve@1.17.1":
177 | version "1.17.1"
178 | resolved "https://registry.npmmirror.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
179 | integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==
180 | dependencies:
181 | "@types/node" "*"
182 |
183 | "@types/web-bluetooth@^0.0.15":
184 | version "0.0.15"
185 | resolved "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.15.tgz#d60330046a6ed8a13b4a53df3813c44942ebdf72"
186 | integrity sha512-w7hEHXnPMEZ+4nGKl/KDRVpxkwYxYExuHOYXyzIzCDzEZ9ZCGMAewulr9IqJu2LR4N37fcnb1XVeuZ09qgOxhA==
187 |
188 | "@vitejs/plugin-vue@^2.2.4":
189 | version "2.3.4"
190 | resolved "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-2.3.4.tgz#966a6279060eb2d9d1a02ea1a331af071afdcf9e"
191 | integrity sha512-IfFNbtkbIm36O9KB8QodlwwYvTEsJb4Lll4c2IwB3VHc2gie2mSPtSzL0eYay7X2jd/2WX02FjSGTWR6OPr/zg==
192 |
193 | "@vue/compiler-core@3.2.40":
194 | version "3.2.40"
195 | resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.40.tgz#c785501f09536748121e937fb87605bbb1ada8e5"
196 | integrity sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==
197 | dependencies:
198 | "@babel/parser" "^7.16.4"
199 | "@vue/shared" "3.2.40"
200 | estree-walker "^2.0.2"
201 | source-map "^0.6.1"
202 |
203 | "@vue/compiler-dom@3.2.40":
204 | version "3.2.40"
205 | resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.40.tgz#c225418773774db536174d30d3f25ba42a33e7e4"
206 | integrity sha512-OZCNyYVC2LQJy4H7h0o28rtk+4v+HMQygRTpmibGoG9wZyomQiS5otU7qo3Wlq5UfHDw2RFwxb9BJgKjVpjrQw==
207 | dependencies:
208 | "@vue/compiler-core" "3.2.40"
209 | "@vue/shared" "3.2.40"
210 |
211 | "@vue/compiler-sfc@3.2.40":
212 | version "3.2.40"
213 | resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.40.tgz#61823283efc84d25d9d2989458f305d32a2ed141"
214 | integrity sha512-tzqwniIN1fu1PDHC3CpqY/dPCfN/RN1thpBC+g69kJcrl7mbGiHKNwbA6kJ3XKKy8R6JLKqcpVugqN4HkeBFFg==
215 | dependencies:
216 | "@babel/parser" "^7.16.4"
217 | "@vue/compiler-core" "3.2.40"
218 | "@vue/compiler-dom" "3.2.40"
219 | "@vue/compiler-ssr" "3.2.40"
220 | "@vue/reactivity-transform" "3.2.40"
221 | "@vue/shared" "3.2.40"
222 | estree-walker "^2.0.2"
223 | magic-string "^0.25.7"
224 | postcss "^8.1.10"
225 | source-map "^0.6.1"
226 |
227 | "@vue/compiler-ssr@3.2.40":
228 | version "3.2.40"
229 | resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.40.tgz#67df95a096c63e9ec4b50b84cc6f05816793629c"
230 | integrity sha512-80cQcgasKjrPPuKcxwuCx7feq+wC6oFl5YaKSee9pV3DNq+6fmCVwEEC3vvkf/E2aI76rIJSOYHsWSEIxK74oQ==
231 | dependencies:
232 | "@vue/compiler-dom" "3.2.40"
233 | "@vue/shared" "3.2.40"
234 |
235 | "@vue/reactivity-transform@3.2.40":
236 | version "3.2.40"
237 | resolved "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.40.tgz#dc24b9074b26f0d9dd2034c6349f5bb2a51c86ac"
238 | integrity sha512-HQUCVwEaacq6fGEsg2NUuGKIhUveMCjOk8jGHqLXPI2w6zFoPrlQhwWEaINTv5kkZDXKEnCijAp+4gNEHG03yw==
239 | dependencies:
240 | "@babel/parser" "^7.16.4"
241 | "@vue/compiler-core" "3.2.40"
242 | "@vue/shared" "3.2.40"
243 | estree-walker "^2.0.2"
244 | magic-string "^0.25.7"
245 |
246 | "@vue/reactivity@3.2.40":
247 | version "3.2.40"
248 | resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.40.tgz#ae65496f5b364e4e481c426f391568ed7d133cca"
249 | integrity sha512-N9qgGLlZmtUBMHF9xDT4EkD9RdXde1Xbveb+niWMXuHVWQP5BzgRmE3SFyUBBcyayG4y1lhoz+lphGRRxxK4RA==
250 | dependencies:
251 | "@vue/shared" "3.2.40"
252 |
253 | "@vue/runtime-core@3.2.40":
254 | version "3.2.40"
255 | resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.40.tgz#e814358bf1b0ff6d4a6b4f8f62d9f341964fb275"
256 | integrity sha512-U1+rWf0H8xK8aBUZhnrN97yoZfHbjgw/bGUzfgKPJl69/mXDuSg8CbdBYBn6VVQdR947vWneQBFzdhasyzMUKg==
257 | dependencies:
258 | "@vue/reactivity" "3.2.40"
259 | "@vue/shared" "3.2.40"
260 |
261 | "@vue/runtime-dom@3.2.40":
262 | version "3.2.40"
263 | resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.40.tgz#975119feac5ab703aa9bbbf37c9cc966602c8eab"
264 | integrity sha512-AO2HMQ+0s2+MCec8hXAhxMgWhFhOPJ/CyRXnmTJ6XIOnJFLrH5Iq3TNwvVcODGR295jy77I6dWPj+wvFoSYaww==
265 | dependencies:
266 | "@vue/runtime-core" "3.2.40"
267 | "@vue/shared" "3.2.40"
268 | csstype "^2.6.8"
269 |
270 | "@vue/server-renderer@3.2.40":
271 | version "3.2.40"
272 | resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.40.tgz#55eaac31f7105c3907e1895129bf4efb6b0ce393"
273 | integrity sha512-gtUcpRwrXOJPJ4qyBpU3EyxQa4EkV8I4f8VrDePcGCPe4O/hd0BPS7v9OgjIQob6Ap8VDz9G+mGTKazE45/95w==
274 | dependencies:
275 | "@vue/compiler-ssr" "3.2.40"
276 | "@vue/shared" "3.2.40"
277 |
278 | "@vue/shared@3.2.40":
279 | version "3.2.40"
280 | resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.40.tgz#e57799da2a930b975321981fcee3d1e90ed257ae"
281 | integrity sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ==
282 |
283 | "@vueuse/core@^9.1.0":
284 | version "9.3.0"
285 | resolved "https://registry.npmmirror.com/@vueuse/core/-/core-9.3.0.tgz#74d855bd19cb5eadd2edb30c871918fac881e8b8"
286 | integrity sha512-64Rna8IQDWpdrJxgitDg7yv1yTp41ZmvV8zlLEylK4QQLWAhz1OFGZDPZ8bU4lwcGgbEJ2sGi2jrdNh4LttUSQ==
287 | dependencies:
288 | "@types/web-bluetooth" "^0.0.15"
289 | "@vueuse/metadata" "9.3.0"
290 | "@vueuse/shared" "9.3.0"
291 | vue-demi "*"
292 |
293 | "@vueuse/metadata@9.3.0":
294 | version "9.3.0"
295 | resolved "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.3.0.tgz#c107fe77a577e1f221536cd1b291039c0c7c4bce"
296 | integrity sha512-GnnfjbzIPJIh9ngL9s9oGU1+Hx/h5/KFqTfJykzh/1xjaHkedV9g0MASpdmPZIP+ynNhKAcEfA6g5i8KXwtoMA==
297 |
298 | "@vueuse/shared@9.3.0":
299 | version "9.3.0"
300 | resolved "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.3.0.tgz#40fc138ba4e379c894075830aa2e15404aaa8a5b"
301 | integrity sha512-caGUWLY0DpPC6l31KxeUy6vPVNA0yKxx81jFYLoMpyP6cF84FG5Dkf69DfSUqL57wX8JcUkJDMnQaQIZPWFEQQ==
302 | dependencies:
303 | vue-demi "*"
304 |
305 | acorn@^8.7.1, acorn@^8.8.0:
306 | version "8.8.0"
307 | resolved "https://registry.npmmirror.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
308 | integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
309 |
310 | ansi-styles@^4.1.0:
311 | version "4.3.0"
312 | resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
313 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
314 | dependencies:
315 | color-convert "^2.0.1"
316 |
317 | ant-design-vue@^3.2.12:
318 | version "3.2.12"
319 | resolved "https://registry.npmmirror.com/ant-design-vue/-/ant-design-vue-3.2.12.tgz#996361982884b1d0d82186dba67962983bc1fbac"
320 | integrity sha512-CPsoWJ3t+sqq/EPINPXb4fC5/9iKkUdYOfK9M9kLKbXlRN3MAoVwWUbaFnUqc+ngtbEpn/d69hTF/Eh7MeWMhQ==
321 | dependencies:
322 | "@ant-design/colors" "^6.0.0"
323 | "@ant-design/icons-vue" "^6.1.0"
324 | "@babel/runtime" "^7.10.5"
325 | "@ctrl/tinycolor" "^3.4.0"
326 | "@simonwep/pickr" "~1.8.0"
327 | array-tree-filter "^2.1.0"
328 | async-validator "^4.0.0"
329 | dayjs "^1.10.5"
330 | dom-align "^1.12.1"
331 | dom-scroll-into-view "^2.0.0"
332 | lodash "^4.17.21"
333 | lodash-es "^4.17.15"
334 | resize-observer-polyfill "^1.5.1"
335 | scroll-into-view-if-needed "^2.2.25"
336 | shallow-equal "^1.0.0"
337 | vue-types "^3.0.0"
338 | warning "^4.0.0"
339 |
340 | anymatch@~3.1.2:
341 | version "3.1.2"
342 | resolved "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
343 | integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
344 | dependencies:
345 | normalize-path "^3.0.0"
346 | picomatch "^2.0.4"
347 |
348 | array-tree-filter@^2.1.0:
349 | version "2.1.0"
350 | resolved "https://registry.npmmirror.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz#873ac00fec83749f255ac8dd083814b4f6329190"
351 | integrity sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==
352 |
353 | async-validator@^4.0.0, async-validator@^4.2.5:
354 | version "4.2.5"
355 | resolved "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz#c96ea3332a521699d0afaaceed510a54656c6339"
356 | integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==
357 |
358 | balanced-match@^1.0.0:
359 | version "1.0.2"
360 | resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
361 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
362 |
363 | binary-extensions@^2.0.0:
364 | version "2.2.0"
365 | resolved "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
366 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
367 |
368 | brace-expansion@^2.0.1:
369 | version "2.0.1"
370 | resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
371 | integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
372 | dependencies:
373 | balanced-match "^1.0.0"
374 |
375 | braces@^3.0.2, braces@~3.0.2:
376 | version "3.0.2"
377 | resolved "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
378 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
379 | dependencies:
380 | fill-range "^7.0.1"
381 |
382 | builtin-modules@^3.3.0:
383 | version "3.3.0"
384 | resolved "https://registry.npmmirror.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
385 | integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
386 |
387 | chalk@^4.1.0:
388 | version "4.1.2"
389 | resolved "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
390 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
391 | dependencies:
392 | ansi-styles "^4.1.0"
393 | supports-color "^7.1.0"
394 |
395 | "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3:
396 | version "3.5.3"
397 | resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
398 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
399 | dependencies:
400 | anymatch "~3.1.2"
401 | braces "~3.0.2"
402 | glob-parent "~5.1.2"
403 | is-binary-path "~2.1.0"
404 | is-glob "~4.0.1"
405 | normalize-path "~3.0.0"
406 | readdirp "~3.6.0"
407 | optionalDependencies:
408 | fsevents "~2.3.2"
409 |
410 | color-convert@^2.0.1:
411 | version "2.0.1"
412 | resolved "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
413 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
414 | dependencies:
415 | color-name "~1.1.4"
416 |
417 | color-name@~1.1.4:
418 | version "1.1.4"
419 | resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
420 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
421 |
422 | compute-scroll-into-view@^1.0.17:
423 | version "1.0.17"
424 | resolved "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz#6a88f18acd9d42e9cf4baa6bec7e0522607ab7ab"
425 | integrity sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==
426 |
427 | core-js@^3.15.1:
428 | version "3.25.3"
429 | resolved "https://registry.npmmirror.com/core-js/-/core-js-3.25.3.tgz#cbc2be50b5ddfa7981837bd8c41639f27b166593"
430 | integrity sha512-y1hvKXmPHvm5B7w4ln1S4uc9eV/O5+iFExSRUimnvIph11uaizFR8LFMdONN8hG3P2pipUfX4Y/fR8rAEtcHcQ==
431 |
432 | cross-env@^7.0.3:
433 | version "7.0.3"
434 | resolved "https://registry.npmmirror.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
435 | integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
436 | dependencies:
437 | cross-spawn "^7.0.1"
438 |
439 | cross-spawn@^7.0.1:
440 | version "7.0.3"
441 | resolved "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
442 | integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
443 | dependencies:
444 | path-key "^3.1.0"
445 | shebang-command "^2.0.0"
446 | which "^2.0.1"
447 |
448 | csstype@^2.6.8:
449 | version "2.6.21"
450 | resolved "https://registry.npmmirror.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e"
451 | integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==
452 |
453 | dayjs@^1.10.5, dayjs@^1.11.3, dayjs@^1.11.5:
454 | version "1.11.5"
455 | resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.5.tgz#00e8cc627f231f9499c19b38af49f56dc0ac5e93"
456 | integrity sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==
457 |
458 | debug@^4.3.3, debug@^4.3.4:
459 | version "4.3.4"
460 | resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
461 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
462 | dependencies:
463 | ms "2.1.2"
464 |
465 | deepmerge@^4.2.2:
466 | version "4.2.2"
467 | resolved "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
468 | integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
469 |
470 | dom-align@^1.12.1:
471 | version "1.12.3"
472 | resolved "https://registry.npmmirror.com/dom-align/-/dom-align-1.12.3.tgz#a36d02531dae0eefa2abb0c4db6595250526f103"
473 | integrity sha512-Gj9hZN3a07cbR6zviMUBOMPdWxYhbMI+x+WS0NAIu2zFZmbK8ys9R79g+iG9qLnlCwpFoaB+fKy8Pdv470GsPA==
474 |
475 | dom-scroll-into-view@^2.0.0:
476 | version "2.0.1"
477 | resolved "https://registry.npmmirror.com/dom-scroll-into-view/-/dom-scroll-into-view-2.0.1.tgz#0decc8522801fd8d3f1c6ba355a74d382c5f989b"
478 | integrity sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==
479 |
480 | element-plus@^2.2.2:
481 | version "2.2.17"
482 | resolved "https://registry.npmmirror.com/element-plus/-/element-plus-2.2.17.tgz#abbb12c19dc029c95b0271822ea9a0e635704bc2"
483 | integrity sha512-MGwMIE/q+FFD3kgS23x8HIe5043tmD1cTRwjhIX9o6fim1avFnUkrsfYRvybbz4CkyqSb185EheZS5AUPpXh2g==
484 | dependencies:
485 | "@ctrl/tinycolor" "^3.4.1"
486 | "@element-plus/icons-vue" "^2.0.6"
487 | "@floating-ui/dom" "^1.0.1"
488 | "@popperjs/core" "npm:@sxzz/popperjs-es@^2.11.7"
489 | "@types/lodash" "^4.14.182"
490 | "@types/lodash-es" "^4.17.6"
491 | "@vueuse/core" "^9.1.0"
492 | async-validator "^4.2.5"
493 | dayjs "^1.11.3"
494 | escape-html "^1.0.3"
495 | lodash "^4.17.21"
496 | lodash-es "^4.17.21"
497 | lodash-unified "^1.0.2"
498 | memoize-one "^6.0.0"
499 | normalize-wheel-es "^1.2.0"
500 |
501 | es-module-lexer@^0.9.3:
502 | version "0.9.3"
503 | resolved "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
504 | integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==
505 |
506 | esbuild-android-64@0.14.54:
507 | version "0.14.54"
508 | resolved "https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be"
509 | integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==
510 |
511 | esbuild-android-arm64@0.14.54:
512 | version "0.14.54"
513 | resolved "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771"
514 | integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==
515 |
516 | esbuild-darwin-64@0.14.54:
517 | version "0.14.54"
518 | resolved "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25"
519 | integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==
520 |
521 | esbuild-darwin-arm64@0.14.54:
522 | version "0.14.54"
523 | resolved "https://registry.npmmirror.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73"
524 | integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==
525 |
526 | esbuild-freebsd-64@0.14.54:
527 | version "0.14.54"
528 | resolved "https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d"
529 | integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==
530 |
531 | esbuild-freebsd-arm64@0.14.54:
532 | version "0.14.54"
533 | resolved "https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48"
534 | integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==
535 |
536 | esbuild-linux-32@0.14.54:
537 | version "0.14.54"
538 | resolved "https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5"
539 | integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==
540 |
541 | esbuild-linux-64@0.14.54:
542 | version "0.14.54"
543 | resolved "https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652"
544 | integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==
545 |
546 | esbuild-linux-arm64@0.14.54:
547 | version "0.14.54"
548 | resolved "https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b"
549 | integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==
550 |
551 | esbuild-linux-arm@0.14.54:
552 | version "0.14.54"
553 | resolved "https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59"
554 | integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==
555 |
556 | esbuild-linux-mips64le@0.14.54:
557 | version "0.14.54"
558 | resolved "https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34"
559 | integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==
560 |
561 | esbuild-linux-ppc64le@0.14.54:
562 | version "0.14.54"
563 | resolved "https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e"
564 | integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==
565 |
566 | esbuild-linux-riscv64@0.14.54:
567 | version "0.14.54"
568 | resolved "https://registry.npmmirror.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8"
569 | integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==
570 |
571 | esbuild-linux-s390x@0.14.54:
572 | version "0.14.54"
573 | resolved "https://registry.npmmirror.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6"
574 | integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==
575 |
576 | esbuild-netbsd-64@0.14.54:
577 | version "0.14.54"
578 | resolved "https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81"
579 | integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==
580 |
581 | esbuild-openbsd-64@0.14.54:
582 | version "0.14.54"
583 | resolved "https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b"
584 | integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==
585 |
586 | esbuild-sunos-64@0.14.54:
587 | version "0.14.54"
588 | resolved "https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da"
589 | integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==
590 |
591 | esbuild-windows-32@0.14.54:
592 | version "0.14.54"
593 | resolved "https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31"
594 | integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==
595 |
596 | esbuild-windows-64@0.14.54:
597 | version "0.14.54"
598 | resolved "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4"
599 | integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==
600 |
601 | esbuild-windows-arm64@0.14.54:
602 | version "0.14.54"
603 | resolved "https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982"
604 | integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==
605 |
606 | esbuild@^0.14.27, esbuild@^0.14.30:
607 | version "0.14.54"
608 | resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2"
609 | integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==
610 | optionalDependencies:
611 | "@esbuild/linux-loong64" "0.14.54"
612 | esbuild-android-64 "0.14.54"
613 | esbuild-android-arm64 "0.14.54"
614 | esbuild-darwin-64 "0.14.54"
615 | esbuild-darwin-arm64 "0.14.54"
616 | esbuild-freebsd-64 "0.14.54"
617 | esbuild-freebsd-arm64 "0.14.54"
618 | esbuild-linux-32 "0.14.54"
619 | esbuild-linux-64 "0.14.54"
620 | esbuild-linux-arm "0.14.54"
621 | esbuild-linux-arm64 "0.14.54"
622 | esbuild-linux-mips64le "0.14.54"
623 | esbuild-linux-ppc64le "0.14.54"
624 | esbuild-linux-riscv64 "0.14.54"
625 | esbuild-linux-s390x "0.14.54"
626 | esbuild-netbsd-64 "0.14.54"
627 | esbuild-openbsd-64 "0.14.54"
628 | esbuild-sunos-64 "0.14.54"
629 | esbuild-windows-32 "0.14.54"
630 | esbuild-windows-64 "0.14.54"
631 | esbuild-windows-arm64 "0.14.54"
632 |
633 | escape-html@^1.0.3:
634 | version "1.0.3"
635 | resolved "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
636 | integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
637 |
638 | escape-string-regexp@^5.0.0:
639 | version "5.0.0"
640 | resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
641 | integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
642 |
643 | estree-walker@^1.0.1:
644 | version "1.0.1"
645 | resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
646 | integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
647 |
648 | estree-walker@^2.0.1, estree-walker@^2.0.2:
649 | version "2.0.2"
650 | resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
651 | integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
652 |
653 | fast-glob@^3.2.11:
654 | version "3.2.12"
655 | resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
656 | integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
657 | dependencies:
658 | "@nodelib/fs.stat" "^2.0.2"
659 | "@nodelib/fs.walk" "^1.2.3"
660 | glob-parent "^5.1.2"
661 | merge2 "^1.3.0"
662 | micromatch "^4.0.4"
663 |
664 | fastq@^1.6.0:
665 | version "1.13.0"
666 | resolved "https://registry.npmmirror.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
667 | integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==
668 | dependencies:
669 | reusify "^1.0.4"
670 |
671 | fill-range@^7.0.1:
672 | version "7.0.1"
673 | resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
674 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
675 | dependencies:
676 | to-regex-range "^5.0.1"
677 |
678 | fsevents@~2.3.2:
679 | version "2.3.2"
680 | resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
681 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
682 |
683 | function-bind@^1.1.1:
684 | version "1.1.1"
685 | resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
686 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
687 |
688 | glob-parent@^5.1.2, glob-parent@~5.1.2:
689 | version "5.1.2"
690 | resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
691 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
692 | dependencies:
693 | is-glob "^4.0.1"
694 |
695 | has-flag@^4.0.0:
696 | version "4.0.0"
697 | resolved "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
698 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
699 |
700 | has@^1.0.3:
701 | version "1.0.3"
702 | resolved "https://registry.npmmirror.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
703 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
704 | dependencies:
705 | function-bind "^1.1.1"
706 |
707 | immutable@^4.0.0:
708 | version "4.1.0"
709 | resolved "https://registry.npmmirror.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef"
710 | integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==
711 |
712 | is-binary-path@~2.1.0:
713 | version "2.1.0"
714 | resolved "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
715 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
716 | dependencies:
717 | binary-extensions "^2.0.0"
718 |
719 | is-builtin-module@^3.1.0:
720 | version "3.2.0"
721 | resolved "https://registry.npmmirror.com/is-builtin-module/-/is-builtin-module-3.2.0.tgz#bb0310dfe881f144ca83f30100ceb10cf58835e0"
722 | integrity sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==
723 | dependencies:
724 | builtin-modules "^3.3.0"
725 |
726 | is-core-module@^2.9.0:
727 | version "2.10.0"
728 | resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed"
729 | integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==
730 | dependencies:
731 | has "^1.0.3"
732 |
733 | is-extglob@^2.1.1:
734 | version "2.1.1"
735 | resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
736 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
737 |
738 | is-glob@^4.0.1, is-glob@~4.0.1:
739 | version "4.0.3"
740 | resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
741 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
742 | dependencies:
743 | is-extglob "^2.1.1"
744 |
745 | is-module@^1.0.0:
746 | version "1.0.0"
747 | resolved "https://registry.npmmirror.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
748 | integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==
749 |
750 | is-number@^7.0.0:
751 | version "7.0.0"
752 | resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
753 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
754 |
755 | is-plain-object@3.0.1:
756 | version "3.0.1"
757 | resolved "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b"
758 | integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==
759 |
760 | isexe@^2.0.0:
761 | version "2.0.0"
762 | resolved "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
763 | integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
764 |
765 | joycon@^3.0.1:
766 | version "3.1.1"
767 | resolved "https://registry.npmmirror.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03"
768 | integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==
769 |
770 | "js-tokens@^3.0.0 || ^4.0.0":
771 | version "4.0.0"
772 | resolved "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
773 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
774 |
775 | jsonc-parser@^3.0.0, jsonc-parser@^3.2.0:
776 | version "3.2.0"
777 | resolved "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76"
778 | integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==
779 |
780 | local-pkg@^0.4.1:
781 | version "0.4.2"
782 | resolved "https://registry.npmmirror.com/local-pkg/-/local-pkg-0.4.2.tgz#13107310b77e74a0e513147a131a2ba288176c2f"
783 | integrity sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==
784 |
785 | lodash-es@^4.17.15, lodash-es@^4.17.21:
786 | version "4.17.21"
787 | resolved "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
788 | integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
789 |
790 | lodash-unified@^1.0.2:
791 | version "1.0.2"
792 | resolved "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.2.tgz#bb2694db3533781e5cce984af60cfaea318b83c1"
793 | integrity sha512-OGbEy+1P+UT26CYi4opY4gebD8cWRDxAT6MAObIVQMiqYdxZr1g3QHWCToVsm31x2NkLS4K3+MC2qInaRMa39g==
794 |
795 | lodash@^4.17.21:
796 | version "4.17.21"
797 | resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
798 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
799 |
800 | loose-envify@^1.0.0:
801 | version "1.4.0"
802 | resolved "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
803 | integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
804 | dependencies:
805 | js-tokens "^3.0.0 || ^4.0.0"
806 |
807 | magic-string@^0.25.7:
808 | version "0.25.9"
809 | resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
810 | integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
811 | dependencies:
812 | sourcemap-codec "^1.4.8"
813 |
814 | magic-string@^0.26.2:
815 | version "0.26.6"
816 | resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.26.6.tgz#b61e417c9f40b7b53bf7e73c0a803258e20d25ee"
817 | integrity sha512-6d+3bFybzyQFJYSoRsl9ZC0wheze8M1LrQC7tNMRqXR4izUTDOLMd9BtSuExK9iAukFh+s5K0WAhc/dlQ+HKYA==
818 | dependencies:
819 | sourcemap-codec "^1.4.8"
820 |
821 | memoize-one@^6.0.0:
822 | version "6.0.0"
823 | resolved "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
824 | integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==
825 |
826 | merge2@^1.3.0:
827 | version "1.4.1"
828 | resolved "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
829 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
830 |
831 | micromatch@^4.0.4:
832 | version "4.0.5"
833 | resolved "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
834 | integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
835 | dependencies:
836 | braces "^3.0.2"
837 | picomatch "^2.3.1"
838 |
839 | minimatch@^5.1.0:
840 | version "5.1.0"
841 | resolved "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7"
842 | integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==
843 | dependencies:
844 | brace-expansion "^2.0.1"
845 |
846 | mlly@^0.5.14, mlly@^0.5.3:
847 | version "0.5.16"
848 | resolved "https://registry.npmmirror.com/mlly/-/mlly-0.5.16.tgz#adb7d39fed81396a50b43173c88589de314015c7"
849 | integrity sha512-LaJ8yuh4v0zEmge/g3c7jjFlhoCPfQn6RCjXgm9A0Qiuochq4BcuOxVfWmdnCoLTlg2MV+hqhOek+W2OhG0Lwg==
850 | dependencies:
851 | acorn "^8.8.0"
852 | pathe "^0.3.8"
853 | pkg-types "^0.3.5"
854 | ufo "^0.8.5"
855 |
856 | ms@2.1.2:
857 | version "2.1.2"
858 | resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
859 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
860 |
861 | nanoid@^3.3.4:
862 | version "3.3.4"
863 | resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
864 | integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
865 |
866 | nanopop@^2.1.0:
867 | version "2.2.0"
868 | resolved "https://registry.npmmirror.com/nanopop/-/nanopop-2.2.0.tgz#bd1c25588a7beaf68865bc2df19db4c58c77dcc9"
869 | integrity sha512-E9JaHcxh3ere8/BEZHAcnuD10RluTSPyTToBvoFWS9/7DcCx6gyKjbn7M7Bx7E1veCxCuY1iO6h4+gdAf1j73Q==
870 |
871 | normalize-path@^3.0.0, normalize-path@~3.0.0:
872 | version "3.0.0"
873 | resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
874 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
875 |
876 | normalize-wheel-es@^1.2.0:
877 | version "1.2.0"
878 | resolved "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz#0fa2593d619f7245a541652619105ab076acf09e"
879 | integrity sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==
880 |
881 | path-key@^3.1.0:
882 | version "3.1.1"
883 | resolved "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
884 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
885 |
886 | path-parse@^1.0.7:
887 | version "1.0.7"
888 | resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
889 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
890 |
891 | pathe@^0.3.0, pathe@^0.3.7, pathe@^0.3.8:
892 | version "0.3.9"
893 | resolved "https://registry.npmmirror.com/pathe/-/pathe-0.3.9.tgz#4baff768f37f03e3d9341502865fb93116f65191"
894 | integrity sha512-6Y6s0vT112P3jD8dGfuS6r+lpa0qqNrLyHPOwvXMnyNTQaYiwgau2DP3aNDsR13xqtGj7rrPo+jFUATpU6/s+g==
895 |
896 | picocolors@^1.0.0:
897 | version "1.0.0"
898 | resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
899 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
900 |
901 | picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1:
902 | version "2.3.1"
903 | resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
904 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
905 |
906 | pkg-types@^0.3.5:
907 | version "0.3.5"
908 | resolved "https://registry.npmmirror.com/pkg-types/-/pkg-types-0.3.5.tgz#ea01c7cf28da9e4c5b85de9b250de4b3dc2e9abc"
909 | integrity sha512-VkxCBFVgQhNHYk9subx+HOhZ4jzynH11ah63LZsprTKwPCWG9pfWBlkElWFbvkP9BVR0dP1jS9xPdhaHQNK74Q==
910 | dependencies:
911 | jsonc-parser "^3.2.0"
912 | mlly "^0.5.14"
913 | pathe "^0.3.7"
914 |
915 | postcss@^8.1.10, postcss@^8.4.13:
916 | version "8.4.16"
917 | resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c"
918 | integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==
919 | dependencies:
920 | nanoid "^3.3.4"
921 | picocolors "^1.0.0"
922 | source-map-js "^1.0.2"
923 |
924 | queue-microtask@^1.2.2:
925 | version "1.2.3"
926 | resolved "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
927 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
928 |
929 | readdirp@~3.6.0:
930 | version "3.6.0"
931 | resolved "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
932 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
933 | dependencies:
934 | picomatch "^2.2.1"
935 |
936 | regenerator-runtime@^0.13.4:
937 | version "0.13.9"
938 | resolved "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
939 | integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
940 |
941 | resize-observer-polyfill@^1.5.1:
942 | version "1.5.1"
943 | resolved "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
944 | integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
945 |
946 | resolve@^1.19.0, resolve@^1.22.0:
947 | version "1.22.1"
948 | resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
949 | integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
950 | dependencies:
951 | is-core-module "^2.9.0"
952 | path-parse "^1.0.7"
953 | supports-preserve-symlinks-flag "^1.0.0"
954 |
955 | reusify@^1.0.4:
956 | version "1.0.4"
957 | resolved "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
958 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
959 |
960 | rollup-plugin-esbuild@^4.8.2:
961 | version "4.10.1"
962 | resolved "https://registry.npmmirror.com/rollup-plugin-esbuild/-/rollup-plugin-esbuild-4.10.1.tgz#84a32419b56680149a0456442a71456e49f122c0"
963 | integrity sha512-/ymcRB283zjFp1JTBXO8ekxv0c9vRc2L6OTljghsLthQ4vqeDSDWa9BVz1tHiVrx6SbUnUpDPLC0K/MXK7j5TA==
964 | dependencies:
965 | "@rollup/pluginutils" "^4.1.1"
966 | debug "^4.3.3"
967 | es-module-lexer "^0.9.3"
968 | joycon "^3.0.1"
969 | jsonc-parser "^3.0.0"
970 |
971 | "rollup@>=2.59.0 <2.78.0":
972 | version "2.77.3"
973 | resolved "https://registry.npmmirror.com/rollup/-/rollup-2.77.3.tgz#8f00418d3a2740036e15deb653bed1a90ee0cc12"
974 | integrity sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==
975 | optionalDependencies:
976 | fsevents "~2.3.2"
977 |
978 | rollup@^2.70.1:
979 | version "2.79.1"
980 | resolved "https://registry.npmmirror.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7"
981 | integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==
982 | optionalDependencies:
983 | fsevents "~2.3.2"
984 |
985 | run-parallel@^1.1.9:
986 | version "1.2.0"
987 | resolved "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
988 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
989 | dependencies:
990 | queue-microtask "^1.2.2"
991 |
992 | sass@^1.52.1:
993 | version "1.55.0"
994 | resolved "https://registry.npmmirror.com/sass/-/sass-1.55.0.tgz#0c4d3c293cfe8f8a2e8d3b666e1cf1bff8065d1c"
995 | integrity sha512-Pk+PMy7OGLs9WaxZGJMn7S96dvlyVBwwtToX895WmCpAOr5YiJYEUJfiJidMuKb613z2xNWcXCHEuOvjZbqC6A==
996 | dependencies:
997 | chokidar ">=3.0.0 <4.0.0"
998 | immutable "^4.0.0"
999 | source-map-js ">=0.6.2 <2.0.0"
1000 |
1001 | scroll-into-view-if-needed@^2.2.25:
1002 | version "2.2.29"
1003 | resolved "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz#551791a84b7e2287706511f8c68161e4990ab885"
1004 | integrity sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg==
1005 | dependencies:
1006 | compute-scroll-into-view "^1.0.17"
1007 |
1008 | scule@^0.2.1:
1009 | version "0.2.1"
1010 | resolved "https://registry.npmmirror.com/scule/-/scule-0.2.1.tgz#0c1dc847b18e07219ae9a3832f2f83224e2079dc"
1011 | integrity sha512-M9gnWtn3J0W+UhJOHmBxBTwv8mZCan5i1Himp60t6vvZcor0wr+IM0URKmIglsWJ7bRujNAVVN77fp+uZaWoKg==
1012 |
1013 | shallow-equal@^1.0.0:
1014 | version "1.2.1"
1015 | resolved "https://registry.npmmirror.com/shallow-equal/-/shallow-equal-1.2.1.tgz#4c16abfa56043aa20d050324efa68940b0da79da"
1016 | integrity sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==
1017 |
1018 | shebang-command@^2.0.0:
1019 | version "2.0.0"
1020 | resolved "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
1021 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
1022 | dependencies:
1023 | shebang-regex "^3.0.0"
1024 |
1025 | shebang-regex@^3.0.0:
1026 | version "3.0.0"
1027 | resolved "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
1028 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
1029 |
1030 | "source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
1031 | version "1.0.2"
1032 | resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
1033 | integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
1034 |
1035 | source-map@^0.6.1:
1036 | version "0.6.1"
1037 | resolved "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
1038 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
1039 |
1040 | sourcemap-codec@^1.4.8:
1041 | version "1.4.8"
1042 | resolved "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
1043 | integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
1044 |
1045 | strip-literal@^0.4.0:
1046 | version "0.4.2"
1047 | resolved "https://registry.npmmirror.com/strip-literal/-/strip-literal-0.4.2.tgz#4f9fa6c38bb157b924e9ace7155ebf8a2342cbcf"
1048 | integrity sha512-pv48ybn4iE1O9RLgCAN0iU4Xv7RlBTiit6DKmMiErbs9x1wH6vXBs45tWc0H5wUIF6TLTrKweqkmYF/iraQKNw==
1049 | dependencies:
1050 | acorn "^8.8.0"
1051 |
1052 | supports-color@^7.1.0:
1053 | version "7.2.0"
1054 | resolved "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
1055 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
1056 | dependencies:
1057 | has-flag "^4.0.0"
1058 |
1059 | supports-preserve-symlinks-flag@^1.0.0:
1060 | version "1.0.0"
1061 | resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
1062 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
1063 |
1064 | to-regex-range@^5.0.1:
1065 | version "5.0.1"
1066 | resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
1067 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
1068 | dependencies:
1069 | is-number "^7.0.0"
1070 |
1071 | ufo@^0.8.5:
1072 | version "0.8.5"
1073 | resolved "https://registry.npmmirror.com/ufo/-/ufo-0.8.5.tgz#e367b4205ece9d9723f2fa54f887d43ed1bce5d0"
1074 | integrity sha512-e4+UtA5IRO+ha6hYklwj6r7BjiGMxS0O+UaSg9HbaTefg4kMkzj4tXzEBajRR+wkxf+golgAWKzLbytCUDMJAA==
1075 |
1076 | unimport@^0.2.7:
1077 | version "0.2.10"
1078 | resolved "https://registry.npmmirror.com/unimport/-/unimport-0.2.10.tgz#9ac0d003db40c750a8ecc0fd4317dc5e0500795e"
1079 | integrity sha512-HoQ0ZDzHJboRYJ4/YxtMAATxiya/s5C2UE6jLFNj1e0D6Qfq6Pw2P40L+ALCkjLwLhR8l2VROE7kba/AaFtdQg==
1080 | dependencies:
1081 | "@rollup/pluginutils" "^4.2.1"
1082 | escape-string-regexp "^5.0.0"
1083 | fast-glob "^3.2.11"
1084 | local-pkg "^0.4.1"
1085 | magic-string "^0.26.2"
1086 | mlly "^0.5.3"
1087 | pathe "^0.3.0"
1088 | scule "^0.2.1"
1089 | strip-literal "^0.4.0"
1090 | unplugin "^0.7.0"
1091 |
1092 | unplugin-auto-import@^0.8.6:
1093 | version "0.8.8"
1094 | resolved "https://registry.npmmirror.com/unplugin-auto-import/-/unplugin-auto-import-0.8.8.tgz#a8d3bbbe9af0fd84f729032d9c1198afd2326216"
1095 | integrity sha512-cVZ79zMR1v4VCZ9emFTUnltmazCc2B4hObyVrxJdlgJ2sK8qub6JfjFt38rCF6MVEddkHiWCU6wZR1qbdqe+ig==
1096 | dependencies:
1097 | "@antfu/utils" "^0.5.2"
1098 | "@rollup/pluginutils" "^4.2.1"
1099 | local-pkg "^0.4.1"
1100 | magic-string "^0.26.2"
1101 | unimport "^0.2.7"
1102 | unplugin "^0.7.0"
1103 |
1104 | unplugin-vue-components@^0.19.6:
1105 | version "0.19.9"
1106 | resolved "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-0.19.9.tgz#87e913f3cf8cd1d36c6f5b597ca7a46cddb6229b"
1107 | integrity sha512-i5mZtg85euPWZrGswFkoa9pf4WjKCP5qOjnwOyg3KOKVzFjnP3osCdrunQMjtoMKehTdz1vV6baZH8bZR4PNgg==
1108 | dependencies:
1109 | "@antfu/utils" "^0.5.2"
1110 | "@rollup/pluginutils" "^4.2.1"
1111 | chokidar "^3.5.3"
1112 | debug "^4.3.4"
1113 | fast-glob "^3.2.11"
1114 | local-pkg "^0.4.1"
1115 | magic-string "^0.26.2"
1116 | minimatch "^5.1.0"
1117 | resolve "^1.22.0"
1118 | unplugin "^0.7.0"
1119 |
1120 | unplugin@^0.7.0:
1121 | version "0.7.2"
1122 | resolved "https://registry.npmmirror.com/unplugin/-/unplugin-0.7.2.tgz#4127012fdc2c84ea4ce03ce75e3d4f54ea47bba1"
1123 | integrity sha512-m7thX4jP8l5sETpLdUASoDOGOcHaOVtgNyrYlToyQUvILUtEzEnngRBrHnAX3IKqooJVmXpoa/CwQ/QqzvGaHQ==
1124 | dependencies:
1125 | acorn "^8.7.1"
1126 | chokidar "^3.5.3"
1127 | webpack-sources "^3.2.3"
1128 | webpack-virtual-modules "^0.4.4"
1129 |
1130 | vite-plugin-singlefile@^0.7.1:
1131 | version "0.7.2"
1132 | resolved "https://registry.npmmirror.com/vite-plugin-singlefile/-/vite-plugin-singlefile-0.7.2.tgz#9c41ad1b90b90fd9723e548a6f22eb805802cf65"
1133 | integrity sha512-TLs5e2f7CzLD1kqvEYxP1d5ehrI1twVoDJwPeGLEMHNZR6M/M69CmRNiJQHfRUkb6uK0O/snaWsSDA7SgVi81A==
1134 | dependencies:
1135 | "@rollup/plugin-node-resolve" "^13.1.3"
1136 | chalk "^4.1.0"
1137 | esbuild "^0.14.30"
1138 | rollup "^2.70.1"
1139 | rollup-plugin-esbuild "^4.8.2"
1140 |
1141 | vite@^2.8.6:
1142 | version "2.9.15"
1143 | resolved "https://registry.npmmirror.com/vite/-/vite-2.9.15.tgz#2858dd5b2be26aa394a283e62324281892546f0b"
1144 | integrity sha512-fzMt2jK4vQ3yK56te3Kqpkaeq9DkcZfBbzHwYpobasvgYmP2SoAr6Aic05CsB4CzCZbsDv4sujX3pkEGhLabVQ==
1145 | dependencies:
1146 | esbuild "^0.14.27"
1147 | postcss "^8.4.13"
1148 | resolve "^1.22.0"
1149 | rollup ">=2.59.0 <2.78.0"
1150 | optionalDependencies:
1151 | fsevents "~2.3.2"
1152 |
1153 | vue-demi@*:
1154 | version "0.13.11"
1155 | resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz#7d90369bdae8974d87b1973564ad390182410d99"
1156 | integrity sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==
1157 |
1158 | vue-types@^3.0.0:
1159 | version "3.0.2"
1160 | resolved "https://registry.npmmirror.com/vue-types/-/vue-types-3.0.2.tgz#ec16e05d412c038262fc1efa4ceb9647e7fb601d"
1161 | integrity sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==
1162 | dependencies:
1163 | is-plain-object "3.0.1"
1164 |
1165 | vue@^3.2.31:
1166 | version "3.2.40"
1167 | resolved "https://registry.npmmirror.com/vue/-/vue-3.2.40.tgz#23f387f6f9b3a0767938db6751e4fb5900f0ee34"
1168 | integrity sha512-1mGHulzUbl2Nk3pfvI5aXYYyJUs1nm4kyvuz38u4xlQkLUn1i2R7nDbI4TufECmY8v1qNBHYy62bCaM+3cHP2A==
1169 | dependencies:
1170 | "@vue/compiler-dom" "3.2.40"
1171 | "@vue/compiler-sfc" "3.2.40"
1172 | "@vue/runtime-dom" "3.2.40"
1173 | "@vue/server-renderer" "3.2.40"
1174 | "@vue/shared" "3.2.40"
1175 |
1176 | warning@^4.0.0:
1177 | version "4.0.3"
1178 | resolved "https://registry.npmmirror.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
1179 | integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
1180 | dependencies:
1181 | loose-envify "^1.0.0"
1182 |
1183 | webpack-sources@^3.2.3:
1184 | version "3.2.3"
1185 | resolved "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
1186 | integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
1187 |
1188 | webpack-virtual-modules@^0.4.4:
1189 | version "0.4.5"
1190 | resolved "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.4.5.tgz#e476842dab5eafb7beb844aa2f747fc12ebbf6ec"
1191 | integrity sha512-8bWq0Iluiv9lVf9YaqWQ9+liNgXSHICm+rg544yRgGYaR8yXZTVBaHZkINZSB2yZSWo4b0F6MIxqJezVfOEAlg==
1192 |
1193 | which@^2.0.1:
1194 | version "2.0.2"
1195 | resolved "https://registry.npmmirror.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
1196 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
1197 | dependencies:
1198 | isexe "^2.0.0"
1199 |
--------------------------------------------------------------------------------
/src/lib/constant.ts:
--------------------------------------------------------------------------------
1 | import type { TargetProps } from './index.d';
2 |
3 | /**
4 | * 所有支持的css属性
5 | */
6 | export const targetProps: TargetProps = {
7 | visibility: 'visible',
8 | width: 'width',
9 | height: 'height',
10 | background: 'background',
11 | backgroundColor: 'backgroundColor',
12 | border: 'none',
13 | borderColor: 'borderColor',
14 | borderWidth: 'borderWidth',
15 | borderLeft: '',
16 | borderLeftWidth: '0px',
17 | borderTop: '',
18 | borderTopWidth: '0px',
19 | borderBottom: '1px solid rgba(255, 255, 255, 1)',
20 | borderBottomWidth: '0px',
21 | borderRight: '1px dashed rgba(255, 255, 255, 1)',
22 | borderRightWidth: '0px',
23 | borderStyle: 'borderStyle',
24 | opacity: '1',
25 | boxShadow: 'none',
26 | mixBlendMode: 'normal',
27 | backgroundBlendMode: 'normal',
28 | backdropFilter: 'none',
29 | filter: 'none',
30 | color: 'color',
31 | borderRadius: '0px',
32 | borderTopLeftRadius: 'borderTopLeftRadius',
33 | borderTopRightRadius: 'borderTopRightRadius',
34 | borderBottomLeftRadius: 'borderBottomLeftRadius',
35 | borderBottomRightRadius: 'borderBottomRightRadius',
36 | padding: 'padding',
37 | paddingTop: 'paddingTop',
38 | paddingRight: 'paddingRight',
39 | paddingBottom: 'paddingBottom',
40 | paddingLeft: 'paddingLeft',
41 | x: 'x',
42 | y: 'y',
43 | display: 'display',
44 | flexDirection: 'flexDirection',
45 | gap: 'gap',
46 | columnGap: 'columnGap',
47 | rowGap: 'rowGap',
48 | alignItems: 'alignItems',
49 | justifyContent: 'justifyContent',
50 | textAlign: 'textAlign',
51 | verticalAlign: 'baseline',
52 | lineHeight: 'lineHeight',
53 | fontSize: 'fontSize',
54 | fontFamily: 'fontFamily',
55 | fontStyle: 'fontStyle',
56 | fontWeight: '400',
57 | letterSpacing: 'normal',
58 | whiteSpace: 'normal',
59 | textDecorationLine: 'none',
60 | textIndent: '0px',
61 | textOverflow: 'ellipsis',
62 | webkitBoxOrient: 'vertical',
63 | webkitLineClamp: '2',
64 | backgroundImage: 'backgroundImage',
65 | backgroundSize: 'backgroundSize',
66 | backgroundRepeat: 'backgroundRepeat',
67 | margin: 'auto',
68 | marginTop: 'marginTop',
69 | marginRight: 'marginRight',
70 | marginBottom: 'marginBottom',
71 | marginLeft: 'marginLeft',
72 | objectFit: 'objectFit',
73 | top: 'top',
74 | left: 'left',
75 | right: 'right',
76 | bottom: 'bottom',
77 | inset: '0px',
78 | position: 'position',
79 | transform: 'transform',
80 | transformOrigin: '1px 1px',
81 | flexWrap: 'nowrap',
82 | overflow: 'visible',
83 | boxSizing: 'border-box',
84 | isPesudo: false,
85 | //填充
86 | fill: 'none',
87 | stroke: 'none',
88 | zIndex: 'auto',
89 | /************* 自定义字段 ********** */
90 | textAutoResize: 'NONE',
91 | isTextWrapped: false,
92 | isChildNodeStretched: false,
93 | alignContent: 'normal',
94 | };
95 |
96 | export const targetPropsList = Object.keys(targetProps);
--------------------------------------------------------------------------------
/src/lib/getStyles.ts:
--------------------------------------------------------------------------------
1 | import { targetProps } from './constant';
2 |
3 | /**
4 | * 获取目前支持解析的css属性
5 | */
6 | export const getStyles = (element: Element) => {
7 | const rawStyles = getComputedStyle(element) as any;
8 | return {...rawStyles};
9 | };
--------------------------------------------------------------------------------
/src/lib/helpers/autoLayout.ts:
--------------------------------------------------------------------------------
1 | export const autoLayoutKeys = [
2 | 'flexMode',
3 | 'itemSpacing',
4 | 'mainAxisAlignItems',
5 | 'crossAxisAlignItems',
6 | 'mainAxisSizingMode',
7 | 'crossAxisSizingMode',
8 | 'strokesIncludedInLayout',
9 | 'itemReverseZIndex',
10 | 'flexWrap',
11 | 'crossAxisSpacing',
12 | ] as const
13 |
14 | export type AutoLayoutData = { [T in (typeof autoLayoutKeys)[number]]: AutoLayout[T] }
15 |
16 | export const handleAutoLayout = (data: AutoLayoutData, node: FrameNode) => {
17 | // 先设置自动布局 其后自动布局相关属性才会生效
18 | node.flexMode = data.flexMode
19 | for (const key of autoLayoutKeys.slice(1)) {
20 | if (key in data) {
21 | //@ts-ignore
22 | node[key] = data[key]
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/src/lib/helpers/bound.ts:
--------------------------------------------------------------------------------
1 | export type Direction = "left" | "right" | "top" | "bottom";
2 |
3 | function getDirectionMostOfElements(direction: Direction, elements: Element[]) {
4 | if (elements.length === 1) {
5 | return elements[0];
6 | }
7 | return elements.reduce((memo, value) => {
8 | if (!memo) {
9 | return value;
10 | }
11 |
12 | const valueDirection = getBoundingClientRect(value)[direction];
13 | const memoDirection = getBoundingClientRect(memo)[direction];
14 |
15 | if (direction === "left" || direction === "top") {
16 | if (valueDirection < memoDirection) {
17 | return value;
18 | }
19 | } else {
20 | if (valueDirection > memoDirection) {
21 | return value;
22 | }
23 | }
24 | return memo;
25 | }, null as Element | null) as Element;
26 | }
27 |
28 | function getAggregateRectOfElements(elements: Element[]) {
29 | if (!elements.length) {
30 | return null;
31 | }
32 |
33 | const { top } = getBoundingClientRect(
34 | getDirectionMostOfElements("top", elements)
35 | );
36 | const { left } = getBoundingClientRect(
37 | getDirectionMostOfElements("left", elements)
38 | );
39 | const { bottom } = getBoundingClientRect(
40 | getDirectionMostOfElements("bottom", elements)
41 | );
42 | const { right } = getBoundingClientRect(
43 | getDirectionMostOfElements("right", elements)
44 | );
45 | const width = right - left;
46 | const height = bottom - top;
47 | return {
48 | top,
49 | left,
50 | bottom,
51 | right,
52 | width,
53 | height,
54 | };
55 | }
56 |
57 | export interface Dimensions
58 | extends Pick<
59 | DOMRect,
60 | "top" | "left" | "bottom" | "width" | "right" | "height" | 'x' | 'y'
61 | > {}
62 |
63 |
64 |
65 | export function getBoundingClientRect(el: Element): Dimensions {
66 |
67 | // const computed = getComputedStyle(el);
68 | // const display = computed.display;
69 | // if (display.includes("inline") && el.children.length) {
70 | // const elRect = el.getBoundingClientRect();
71 | // const aggregateRect = getAggregateRectOfElements(Array.from(el.children))!;
72 |
73 | // if (elRect.width > aggregateRect?.width) {
74 | // return {
75 | // ...aggregateRect,
76 | // width: elRect.width,
77 | // left: elRect.left,
78 | // right: elRect.right,
79 | // };
80 | // }
81 | // return aggregateRect;
82 | // }
83 |
84 | // 子元素的bound - 父元素的bound 为子元素的相对位置
85 | const parentBound = el.parentElement?.getBoundingClientRect()
86 | const bound = el.getBoundingClientRect()
87 | return {
88 | // bound属性不可枚举,对象延展符无效
89 | ...bound.toJSON(),
90 | left: parentBound? bound.left - parentBound.left : bound.left,
91 | top: parentBound? bound.top - parentBound.top : bound.top,
92 | };
93 | }
94 |
--------------------------------------------------------------------------------
/src/lib/helpers/config.ts:
--------------------------------------------------------------------------------
1 | import type { OptionalSettings } from '../index.d'
2 |
3 | /**
4 | * 解析设置
5 | */
6 | export const options: OptionalSettings = {
7 | absoluteBounds: false
8 | }
9 |
10 | export const updateOptions = (newOptions: OptionalSettings) => {
11 | Object.assign(options, { ...newOptions })
12 | }
--------------------------------------------------------------------------------
/src/lib/helpers/font.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 字重map
3 | */
4 | /**
5 | *
6 | 100 Thin (Hairline)
7 | 200 Extra Light (Ultra Light)
8 | 300 Light
9 | 400 Normal (Regular)
10 | 500 Medium
11 | 600 Semi Bold (Demi Bold)
12 | 700 Bold
13 | 800 Extra Bold (Ultra Bold)
14 | 900 Black (Heavy)
15 | 950 Extra Black (Ultra Black)
16 | */
17 | export const FONT_WEIGHTS = {
18 | '100': 'Thin',
19 | '200': 'UltraLight',
20 | '300': 'Light',
21 | '400': 'Regular',
22 | '500': 'Medium',
23 | '600': 'SemiBold',
24 | '700': 'Bold',
25 | '800': 'ExtraBold',
26 | '900': 'Heavy',
27 | '950': 'ExtraBlack'
28 | } as const
29 |
30 | // 把样式备选也映射一次
31 | export const FONT_WEHGHTS_BACKUPS = {
32 | 'thin': ['Thin','Hairline'],
33 | 'hairline': ['Thin','Hairline'],
34 | 'extralight': ['UltraLight','ExtraLight'],
35 | 'ultralight': ['UltraLight','ExtraLight'],
36 | 'light': ['Light'],
37 | 'regular': ['Regular', 'Normal'],
38 | 'normal': ['Regular', 'Normal'],
39 | 'medium': ['Medium'],
40 | 'semibold': ['SemiBold', 'DemiBold'],
41 | 'demibold': ['SemiBold', 'DemiBold'],
42 | 'bold': ['Bold'],
43 | 'extrabold': ['ExtraBold', 'UltraBold'],
44 | 'ultrabold': ['ExtraBold', 'UltraBold'],
45 | 'heavy': ['Heavy', 'Black'],
46 | 'black': ['Heavy', 'Black'],
47 | 'extrablack': ['ExtraBlack', 'UltraBlack'],
48 | 'ultrablack': ['ExtraBlack', 'UltraBlack'],
49 | }
50 |
51 | export const normalizeName = (str: string) =>
52 | str.toLowerCase().replace(/[^a-z]/gi, "");
53 |
54 | // 缓存起来
55 | const fontCache: { [key: string]: FontName | undefined } = {};
56 |
57 | /**
58 | * 默认字体 思源黑体
59 | */
60 | const defaultFont = {
61 | "family": "Source Han Sans CN",
62 | "style": "Regular"
63 | }
64 |
65 | /**
66 | *
67 | * @param family 字体名
68 | * @param style 样式名
69 | * @param cacheKey 需要被缓存的key
70 | * @returns
71 | */
72 | const loadFont = async (family: string, style: string, availableFonts: Record, cacheKey: string) => {
73 | //提取family-style
74 | const normalized = `${normalizeName(family)}-${normalizeName(style)}`;
75 | // 去可用字体里取找
76 | const fontName = availableFonts[normalized]
77 | if (fontName) {
78 | //有可用的 手动加载一次
79 | await mg.loadFontAsync(fontName);
80 | //存一下
81 | fontCache[cacheKey] = fontName;
82 | return fontName;
83 | }
84 | return null
85 | }
86 |
87 | /**
88 | * 匹配可用字体
89 | */
90 | export async function getMatchingFont(font: FontName, availableFonts: Record) {
91 | const { family: familyStr, style } = font
92 | //family-style
93 | const key = `${normalizeName(familyStr)}-${normalizeName(style)}`
94 | if (fontCache[key]) {
95 | return fontCache[key]
96 | }
97 |
98 | // Step 1
99 | // 不存在缓存 拆开找存在的
100 | const familySplit = familyStr.split(/\s*,\s*/);
101 | // 先去取所有可能的样式枚举
102 | const backups = FONT_WEHGHTS_BACKUPS[normalizeName(style) as keyof typeof FONT_WEHGHTS_BACKUPS] || []
103 |
104 | for (const family of familySplit) {
105 | // 先按style去匹配
106 | for (const backupStyle of backups) {
107 | const fontName = await loadFont(family, backupStyle, availableFonts, key)
108 | if (fontName) {
109 | return fontName
110 | }
111 | }
112 |
113 | // 可能存在family已经含样式的css 例如 PingFangSC-Medium这种style后缀, 所以需要取family中的样式
114 | const chunks = family.match(/(.+)-([a-z]*)$/i)
115 | const suffixFamily = chunks?.[1]
116 | const suffixStyle = chunks?.[2]
117 | if (suffixFamily && suffixStyle) {
118 | // 用family中的style去取一下map
119 | const backupsFromFamily = FONT_WEHGHTS_BACKUPS[normalizeName(suffixStyle) as keyof typeof FONT_WEHGHTS_BACKUPS] || []
120 | for (const backupStyle of backupsFromFamily) {
121 | const fontName = await loadFont(suffixFamily, backupStyle, availableFonts, key)
122 | if (fontName) {
123 | return fontName
124 | }
125 | }
126 | }
127 | }
128 |
129 | // Step2
130 | // 返回默认
131 | return defaultFont;
132 | }
133 |
134 |
--------------------------------------------------------------------------------
/src/lib/helpers/image.ts:
--------------------------------------------------------------------------------
1 | const mimeTypes = {
2 | jpg: 'jpeg',
3 | jpeg: 'jpeg',
4 | png: 'png',
5 | webp: 'webp',
6 | }
7 |
8 | /**
9 | * 处理uri
10 | * @param uri
11 | * @returns
12 | */
13 | const convertUriToBuffer = (paint: ImagePaint & { bytes?: Uint8Array }): Promise => {
14 | return new Promise((resolve, reject) => {
15 | try {
16 | const uri = paint.imageRef
17 | if (!uri || !uri.length) {
18 | throw new Error('url 错误')
19 | }
20 | // 后缀
21 | const chunks = uri.split('.')
22 | const ext = chunks[chunks.length - 1].match(/^(jpg|jpeg|png|webp|)*/i)?.[0].toLowerCase() || 'png'
23 | const image = new Image()
24 | image.crossOrigin = 'anonymous'
25 | image.onload = () => {
26 | const canvas = document.createElement('canvas')
27 | canvas.width = image.width
28 | canvas.height = image.height
29 | const context = canvas.getContext('2d')
30 | context!.drawImage(image, 0, 0)
31 | canvas.toBlob(function (blob) {
32 | const reader = new FileReader()
33 | reader.addEventListener('loadend', function() {
34 | paint.bytes = new Uint8Array(this.result as ArrayBuffer)
35 | resolve(paint);
36 | })
37 | reader.addEventListener('error', (e) => {
38 | console.error('读取图片失败', paint)
39 | reject(e)
40 | })
41 | reader.readAsArrayBuffer(blob!)
42 | }, `image/${mimeTypes[ext as keyof typeof mimeTypes]}`)
43 | }
44 | image.onerror = (e) => {
45 | console.error('转换图片失败', paint)
46 | reject(e)
47 | }
48 | image.src = uri
49 | } catch (error) {
50 | reject(error)
51 | }
52 | })
53 | }
54 |
55 | /**
56 | * 处理base64
57 | * @param base64
58 | * @returns
59 | */
60 | const convertBase64ToBuffer = (paint: ImagePaint & { bytes?: Uint8Array }) => {
61 | const base64 = paint.imageRef
62 | const str = window.atob(base64.split(',')[1]);
63 | const len = str.length;
64 | const bytes = new Uint8Array(len);
65 | for (let i = 0; i < len; i++) {
66 | bytes[i] = str.charCodeAt(i);
67 | }
68 | paint.bytes = bytes
69 | return Promise.resolve(paint)
70 | }
71 |
72 | export const convertImageToBuffer = (paint: ImagePaint) => {
73 | if (~paint.imageRef.indexOf('data:image')) {
74 | return convertBase64ToBuffer(paint)
75 | } else {
76 | return convertUriToBuffer(paint)
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/lib/helpers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './image'
2 | export * from './autoLayout'
3 | export * from './bound'
4 | export * from './font'
5 | export * from './input'
6 | export * from './paints'
7 | export * from './utils'
--------------------------------------------------------------------------------
/src/lib/helpers/input.ts:
--------------------------------------------------------------------------------
1 | import { TargetProps, ExtraNodeType } from '../index.d';
2 | import { getNumber, isInput, isTextWrapped } from '../helpers';
3 |
4 | export type PesudoInputText = {
5 | nodeType: ExtraNodeType.INPUT,
6 | styles: TargetProps,
7 | node: HTMLElement
8 | }
9 |
10 | /**
11 | * textArea中的文字会折行,需要计算多行逻辑
12 | */
13 | const calculateBoundOfTextInTextArea = (text: string, styles: TargetProps, textArea: HTMLTextAreaElement): { range: Range, remove: CallableFunction } => {
14 | const tempDiv = document.createElement('div');
15 | tempDiv.style.cssText = `
16 | position: absolute;
17 | left: -9999px;
18 | top: -9999px;
19 | width: ${textArea.offsetWidth}px;
20 | height: ${textArea.offsetHeight}px;
21 | font-size: ${styles.fontSize};
22 | font-family: ${styles.fontFamily};
23 | line-height: ${styles.lineHeight};
24 | padding: ${styles.padding};
25 | border: ${styles.border};
26 | white-space: pre-wrap;
27 | `;
28 | tempDiv.innerHTML = text.replace(/\n/g, '
');
29 | document.body.appendChild(tempDiv);
30 |
31 | // 获取文字的实际包围盒
32 | const range = document.createRange();
33 | range.selectNode(tempDiv.childNodes[0]);
34 | return {
35 | range,
36 | remove: () => {
37 | range.detach();
38 | document.body.removeChild(tempDiv);
39 | }
40 | }
41 | }
42 |
43 | const calculateBoundOfTextInInput = (text: string): { range: Range, remove: CallableFunction } => {
44 | // 获取文字的实际包围盒
45 | const dummy = document.createTextNode(text);
46 | // 插入dom计算
47 | document.body.append(dummy);
48 |
49 | const range = document.createRange();
50 | range.selectNode(dummy);
51 |
52 | return {
53 | range,
54 | remove: () => {
55 | range.detach();
56 | document.body.removeChild(dummy);
57 | }
58 | }
59 | }
60 |
61 | // 创建一个text来处理input中的value或者placeHolder
62 | export const createPesudoText = (input: HTMLInputElement | HTMLTextAreaElement, inputStyle: TargetProps): PesudoInputText | null => {
63 |
64 | let pseudoText: string;
65 |
66 | const { value, placeholder } = input as HTMLInputElement;
67 | if (!value && !placeholder) return null;
68 | if (value) {
69 | pseudoText = value;
70 | } else {
71 | pseudoText = placeholder;
72 | }
73 |
74 | const {
75 | paddingLeft,
76 | paddingRight,
77 | paddingTop,
78 | textIndent,
79 | width,
80 | height,
81 | } = inputStyle;
82 |
83 | // textNode节点不是element, 无法通过getComputedStyle获取, 所以延用input的样式
84 | const textStyles = {...inputStyle}
85 |
86 | // 是否是input
87 | const elementIsInput = isInput(input)
88 |
89 | const { range, remove } = elementIsInput? calculateBoundOfTextInInput(pseudoText) : calculateBoundOfTextInTextArea(pseudoText, textStyles, input as HTMLTextAreaElement)
90 |
91 | // 复制给textContent
92 | const rect = range.getBoundingClientRect();
93 |
94 | textStyles.width = `${getNumber(width) - getNumber(paddingLeft) - getNumber(textIndent) - getNumber(paddingRight)}px`
95 | // 这里高度延用输入框的 因为文字行高字号没有设置 计算高度会用系统默认字号和行高
96 | textStyles.height = `${rect.height}px`
97 | textStyles.lineHeight = `${rect.height}px`
98 |
99 | // 加上父元素的padding
100 | const x = `${getNumber(paddingLeft) + getNumber(textIndent)}px`
101 | // 一般来说 textArea的placeholder的居顶,input垂直居中
102 | // textArea.y = 0 + paddingTop input.y = (input.height - rect.height) / 2 + paddingTop
103 | let y = `${getNumber(paddingTop)}px`
104 | if (elementIsInput) {
105 | y = `${(getNumber(height) - rect.height) / 2}px`
106 | }
107 | textStyles.x = x
108 | textStyles.y = y
109 | textStyles.left = x
110 | textStyles.top = y
111 |
112 | // placeholder颜色
113 | // https://stackoverflow.com/questions/37410244/get-placeholder-color-using-javascript
114 | // 不再支持了 https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
115 | if (!value) {
116 | // placeholder默认色
117 | textStyles.color = 'rgba(0, 0, 0, 0.24)'
118 | }
119 |
120 | //折行 textArea是会折行
121 | if (!elementIsInput) {
122 | textStyles.isTextWrapped = isTextWrapped(range, getNumber(inputStyle.lineHeight))
123 | } else {
124 | // input不折行
125 | textStyles.isTextWrapped = false
126 | }
127 |
128 | // 处理完成后移除
129 | remove()
130 |
131 | return {
132 | nodeType: ExtraNodeType.INPUT,
133 | styles: textStyles,
134 | node: {
135 | textContent: pseudoText
136 | }
137 | } as any
138 | }
--------------------------------------------------------------------------------
/src/lib/helpers/paints.ts:
--------------------------------------------------------------------------------
1 | export const handlePaints = async (paints: Paint[]) => {
2 | return await Promise.allSettled(paints.map(async (paint: Paint & { bytes?: Uint8Array }) => {
3 | switch (paint.type) {
4 | case 'IMAGE': {
5 | // 图片
6 | const image = await mg.createImage(paint.bytes || new Uint8Array([]))
7 | delete paint.bytes
8 | return {
9 | ...paint,
10 | imageRef: image.href,
11 | }
12 | }
13 | }
14 | return paint
15 | }))
16 | }
17 |
18 | /**
19 | * 判断是否为图片填充
20 | */
21 | export const checkIfPaintIsImagePaint = (paint: Paint) => {
22 | return paint.type === 'IMAGE'
23 | }
--------------------------------------------------------------------------------
/src/lib/helpers/utils.ts:
--------------------------------------------------------------------------------
1 | import { TargetProps, ExtraNodeType } from "../index.d"
2 |
3 | const pesudoElts = ['::after', '::before']
4 |
5 | export type PesudoElt = {
6 | type: '::after' | '::before'
7 | nodeType: ExtraNodeType.PESUDO,
8 | styles: TargetProps
9 | }
10 |
11 | export type PesudoElts = Array
12 |
13 | /**
14 | *
15 | * 一般来说content都是none, 当content === '""'视为有效的伪元素
16 | */
17 | export const getPesudoElts = (element: HTMLElement): PesudoElts[] => {
18 | return pesudoElts.map(pesudoType => {
19 | const styles = getComputedStyle(element, pesudoType);
20 | return styles.content.trim() === '""'? {
21 | nodeType: ExtraNodeType.PESUDO,
22 | styles: {...styles, isPesudo: true},
23 | type: pesudoType
24 | } : null
25 | }).filter(styles => !!styles) as any
26 | }
27 |
28 | export const isInline = (display: string) => {
29 | return display?.includes('inline')
30 | }
31 |
32 | // 将css的px或%转为数字
33 | export const getNumber = (px: string) => {
34 | if (!px) return 0;
35 | const result = parseFloat(px);
36 | if (isNaN(result)) return 0;
37 | return result;
38 | }
39 |
40 | /**
41 | * 转换rgba
42 | */
43 | export const transColor = (color: string) => {
44 | const result = {
45 | r: 0,
46 | g: 0,
47 | b: 0,
48 | a: 1,
49 | };
50 | if (!color) {
51 | return result
52 | };
53 | const rgbaRaw = new RegExp(/rgb\((\s*\d*\.?\d+\s*,\s*\d*\.?\d+\s*,\s*\d*\.?\d+\s*)\)/).exec(color) || new RegExp(/rgba\((\s*\d*\.?\d+\s*,\s*\d*\.?\d+\s*,\s*\d*\.?\d+\s*,\s*\d*\.?\d+\s*)\)/).exec(color);
54 | if (!rgbaRaw) return result;
55 | const rgba = rgbaRaw[1]?.split(',');
56 | result.r = parseFloat(rgba[0]) / 255;
57 | result.g = parseFloat(rgba[1]) / 255;
58 | result.b = parseFloat(rgba[2]) / 255;
59 | result.a = parseFloat(rgba[3] ?? 1);
60 | return result;
61 | }
62 |
63 | /**
64 | * 根据zIndex排序
65 | */
66 | export const sortByZIndex = (nodes: {index: number}[]) => {
67 | nodes.sort((a, b) => {return a.index - b.index})
68 | }
69 |
70 | /**
71 | * 判断文本是否折行
72 | */
73 | export const isTextWrapped = (range: Range, lineHeight: number) => {
74 | // 重排太多了 性能会很差
75 | // let orgStyle = element.getAttribute('style') ?? '';
76 | // // 获取当前元素的 line-height 值
77 | // let lineHeight = window.getComputedStyle(element).lineHeight;
78 | // // 如果是 normal 就换为 px 值
79 | // if (lineHeight === 'normal') {
80 | // let temp = document.createElement('div');
81 | // temp.innerText = "字";
82 | // document.body.appendChild(temp);
83 | // lineHeight = temp.offsetHeight + 'px';
84 | // document.body.removeChild(temp);
85 | // }
86 | // // 让元素高度等于其 line-height 高度
87 | // element.style.height = lineHeight;
88 | // // 然后便可判断出内容是否超出其容器,即是否已换行
89 | // if (element.offsetHeight < element.scrollHeight) {
90 | // element.setAttribute('style', orgStyle);
91 | // return true
92 | // } else {
93 | // element.setAttribute('style', orgStyle);
94 | // return false
95 | // }
96 | const rects = Array.from(range.getClientRects())
97 | // 单行仍会返回多行,未解决,需要判断一下 https://bugs.chromium.org/p/chromium/issues/detail?id=612459
98 | const first = rects[0]
99 | if (!first) {
100 | return false
101 | }
102 | return rects.slice(1).some((rect) => {
103 | if (rect.y === first.y) {
104 | return false
105 | }
106 | if (isNaN(lineHeight)) {
107 | let temp = document.createElement('div');
108 | temp.innerText = "字";
109 | temp.style.position = 'fixed'
110 | document.body.appendChild(temp);
111 | lineHeight = temp.offsetHeight
112 | document.body.removeChild(temp);
113 | }
114 | // 因为该行文字包含了一些不同的字体或者字体大小,在不同的字符之间存在微小的空隙或者重叠, 导致存在了不同的rects
115 | const delta = Math.abs(Math.abs(rect.y - first.y) - lineHeight)
116 | if (delta >= 0) {
117 | return true
118 | }
119 | return false
120 | })
121 | }
122 |
123 | /**
124 | * 是否是svg
125 | */
126 | export const isSvg = (element: Element) => {
127 | return element.tagName === 'svg'
128 | }
129 |
130 | /**
131 | * 是否是input
132 | */
133 | export const isInput = (element: Element): boolean => {
134 | return element instanceof HTMLInputElement || element.tagName === 'INPUT'
135 | }
136 |
137 | /**
138 | * 是否是textArea
139 | */
140 | export const isTextArea = (element: Element): boolean => {
141 | return element instanceof HTMLTextAreaElement || element.tagName === 'TEXTAREA'
142 | }
143 |
144 | /**
145 | * 清除动画和变换
146 | */
147 | export const clearTransformAndTransition = async (styles: TargetProps, element: HTMLElement) => {
148 | let transition = element.style.transition
149 | let transform = element.style.transform
150 | if (styles.transform !== 'none') {
151 | element.style.transform = 'unset'
152 | element.style.transition = 'unset'
153 |
154 | // 等待元素transform还原
155 | await new Promise((resolve) => {
156 | setTimeout(() => {
157 | resolve(1)
158 | }, 50);
159 | });
160 | }
161 |
162 | return () => {
163 | if (styles.transform !== 'none') {
164 | element.style.transform = transform
165 | element.style.transition = transition
166 | }
167 | }
168 | }
169 |
170 | /**
171 | * 判断矩阵是否包含翻转
172 | */
173 | export function isFliped(transform: Transform): boolean {
174 | // 定义基准向量,比如 [1, 0] 和 [0, 1]
175 | const horizontal = {x: 1, y: 0}; // 水平向右的向量
176 | const vertical = {x: 0, y: 1}; // 垂直向上的向量
177 | const rotationTransform: Transform = [
178 | [transform[0][0], transform[0][1], 0],
179 | [transform[1][0], transform[1][1], 0],
180 | ];
181 | // 判断是否翻转, 叉乘结果为负数则翻转
182 | const th = convertVector(horizontal, rotationTransform);
183 | const tv = convertVector(vertical, rotationTransform);
184 | const fliped = th.x * tv.y - th.y * tv.x < 0;
185 | // 判断向量是否和基准向量方向相反
186 | return fliped;
187 | }
188 |
189 | // 坐标变换
190 | export const convertVector = (vector: Vector, transform: Transform): Vector => {
191 | const originCenterMatrix = [[vector.x], [vector.y], [1]];
192 | const transformMatrix = [...transform, [0, 0, 1]];
193 | const matrix = matrixProduct(transformMatrix, originCenterMatrix)
194 | const center = {
195 | x: matrix[0][0],
196 | y: matrix[1][0]
197 | }
198 | return center;
199 | }
200 | // 矩阵点乘
201 | export const matrixProduct = (vector1: number[][], vector2: number[][]): number[][] => {
202 | const result: number[][] = [];
203 |
204 | if(vector1.length === vector2[0].length || vector1[0].length === vector2.length){
205 | for (let i = 0; i < vector1.length; i++) {
206 | result[i] = [];
207 | for (let j = 0; j < vector2[0].length; j++) {
208 | let sum = 0;
209 | for (let k = 0; k < vector1[0].length; k++) {
210 | sum += vector1[i][k] * vector2[k][j];
211 | }
212 | result[i][j] = sum;
213 | }
214 | }
215 | }
216 | else {
217 | throw new Error(`vector matrix product error with vectors: , ${JSON.stringify(vector1)}, ${JSON.stringify(vector2)}`);
218 | }
219 | return result;
220 | }
--------------------------------------------------------------------------------
/src/lib/index.d.ts:
--------------------------------------------------------------------------------
1 |
2 | export declare function htmlToMG(html: HTMLElement, options?: OptionalSettings): Promise
3 | export declare function postProcess(TargetNode): Promise
4 | export declare function renderToMasterGo(root: TargetNode): Promise
5 |
6 | export type ValidNode = (FrameNode | TextNode | RectangleNode | PenNode) & { [key: string]: any }
7 |
8 | /**
9 | * 额外的属性
10 | */
11 | interface PlusAttributes {
12 | /**
13 | * 图层下标
14 | */
15 | index: number
16 | }
17 |
18 | /**
19 | * FrameNode定义的覆盖层
20 | */
21 | export interface IFrameNode extends Omit, PlusAttributes {
22 | type: 'FRAME'
23 | children: Array
24 | index: number
25 | }
26 |
27 | /**
28 | * RectangleNode定义的覆盖层
29 | */
30 | export interface IRectangleNode extends RectangleNode, PlusAttributes {
31 | type: 'RECTANGLE'
32 | index: number
33 | }
34 |
35 | /**
36 | * TextNode定义的覆盖层
37 | */
38 | export interface ITextNode extends TextNode, PlusAttributes {
39 | type: 'TEXT'
40 | textStyles: Array
41 | index: number
42 | }
43 |
44 | /**
45 | * svg图层数据
46 | */
47 | export interface ISvgNode extends PenNode {
48 | content: string
49 | index: number
50 | }
51 |
52 | /**
53 | * 转换的目标类型
54 | */
55 | export type TargetNode = IFrameNode | ITextNode | IRectangleNode | ISvgNode
56 |
57 | /**
58 | * 向下传递的属性
59 | */
60 | export interface PassTargetProps {
61 | // 坐标。注意,这俩是计算出来的,css里并没有
62 | x: string
63 | y: string
64 | }
65 |
66 | /**
67 | * 支持的属性
68 | */
69 | export interface TargetProps extends PassTargetProps {
70 |
71 | visibility: string
72 |
73 | width: string
74 | height: string
75 | background: string
76 | backgroundColor: string
77 | borderColor: string
78 |
79 | // 描边
80 | border: string
81 | borderWidth: string
82 | borderTop: string
83 | borderTopWidth: string
84 | borderBottom: string
85 | borderBottomWidth: string
86 | borderLeft: string
87 | borderLeftWidth: string
88 | borderRight: string
89 | borderRightWidth: string
90 | borderStyle: string
91 |
92 | //混合
93 | opacity: string
94 | boxShadow: string
95 | mixBlendMode: string
96 | // 背景图片的混合模式 目前画布没这个能力
97 | backgroundBlendMode: string
98 | filter: string | 'none'
99 | backdropFilter: string | 'none'
100 |
101 | backgroundImage: string
102 | backgroundSize: string
103 | backgroundRepeat: string
104 | objectFit: string
105 | top: string
106 | left: string
107 | right: string
108 | bottom: string
109 | inset: string
110 | position: string
111 | transform: string
112 | transformOrigin: string
113 |
114 | // 文字属性
115 | color: string
116 | textAlign: string
117 | verticalAlign: string
118 | lineHeight: string
119 | fontSize: string
120 | fontFamily: string
121 | fontStyle: string
122 | fontWeight: string
123 | letterSpacing: string
124 | textIndent: string
125 | /**
126 | * 装饰线 none | underline | line-through
127 | */
128 | textDecorationLine: string
129 | /**
130 | * 文字缩略
131 | */
132 | textOverflow: 'ellipsis' | 'clip'
133 | /**
134 | * 折行
135 | */
136 | whiteSpace: 'nowrap' | 'break-spaces' | 'normal'
137 | webkitLineClamp: string
138 | webkitBoxOrient: 'horizontal' | 'vertical'
139 |
140 | // 圆角
141 | borderRadius: string
142 | borderTopLeftRadius: string
143 | borderTopRightRadius: string
144 | borderBottomLeftRadius: string
145 | borderBottomRightRadius: string
146 |
147 | // 内边距
148 | padding: string
149 | paddingTop: string
150 | paddingRight: string
151 | paddingBottom: string
152 | paddingLeft: string
153 |
154 | // flex
155 | display: '-webkit-box' | 'flex' | 'block' | 'inline-block' | string
156 | flexDirection: string
157 | gap: string
158 | columnGap: string
159 | rowGap: string
160 | alignItems: string
161 | justifyContent: string
162 | flexWrap: 'wrap' | 'nowrap'
163 | alignContent: 'normal' | 'space-between'
164 |
165 | // 外边距
166 | margin: string
167 | marginTop: string
168 | marginRight: string
169 | marginBottom: string
170 | marginLeft: string
171 |
172 | //剪裁
173 | overflow: string
174 | //盒模型
175 | boxSizing: 'border-box' | 'content-box'
176 | //额外属性
177 | isPesudo: boolean
178 | //填充颜色
179 | fill: string
180 | //描边颜色
181 | stroke: string
182 | // z轴权重
183 | zIndex: string | 'auto'
184 |
185 | /********************** 自定义属性,css不存在,只解析中用到 ******************** */
186 | /**
187 | * 文字自适应模式
188 | */
189 | textAutoResize: TextNode['textAutoResize']
190 | /**
191 | * 文字是否折行
192 | */
193 | isTextWrapped: boolean | undefined
194 | /**
195 | * 子元素是否跟随缩放
196 | */
197 | isChildNodeStretched: boolean | undefined
198 | id?: string
199 | }
200 |
201 | /**
202 | * 处理元素类型
203 | */
204 | export enum ExtraNodeType {
205 | PESUDO = 'PESUDO',
206 | INPUT = 'INPUT',
207 | }
208 |
209 | /**
210 | * 解析设置
211 | */
212 | export type OptionalSettings = {
213 | /**
214 | * 是否导出完整尺寸
215 | * 当图层带有非overflow: visible属性时,是否开启剪裁, 默认为false
216 | */
217 | absoluteBounds?: boolean
218 | }
219 |
220 | export {}
--------------------------------------------------------------------------------
/src/lib/index.ts:
--------------------------------------------------------------------------------
1 | import { TargetNode, TargetProps, ExtraNodeType, OptionalSettings } from './index.d';
2 | import { getStyles } from './getStyles';
3 | import { transformText } from './transformText';
4 | import { transformRect } from './transformRect';
5 | import { transformFrame } from './transformFrame';
6 | import { transPseudo } from './transPseudo'
7 | import { transformSvg } from './transformSvg';
8 | import { getBoundingClientRect, getPesudoElts, PesudoElt, sortByZIndex, isTextWrapped, createPesudoText, PesudoInputText, isTextArea, isInput, isSvg, clearTransformAndTransition, getNumber } from './helpers'
9 | import { updateOptions } from './helpers/config'
10 | import { render as renderToMasterGo } from './render'
11 | import { postProcess } from './postProcess'
12 |
13 | const modifiedElements: CallableFunction[] = []
14 |
15 | // 如果元素经过transform 此时x y和 width height 会发生改变 这里计算会有错误,需要先重置一下transform
16 | const preProcess = async (styles: TargetProps, element: HTMLElement) => {
17 | const resetOriginalTransformAndTransition = await clearTransformAndTransition(styles, element)
18 | modifiedElements.push(resetOriginalTransformAndTransition);
19 | }
20 |
21 | /**
22 | * 处理Element节点
23 | */
24 | const processOneElement = async (element: HTMLElement, styles: TargetProps, parentStyles: TargetProps) => {
25 | // 先判空
26 | if (styles.display === 'none' || styles.opacity === '0') return null;
27 |
28 | await preProcess(styles, element)
29 |
30 | // 包围盒
31 | const bound = getBoundingClientRect(element)
32 | // 所有元素都可能要走这个逻辑
33 | if (styles.width === 'auto' || styles.height === 'auto') {
34 | const range = document.createRange();
35 | range.selectNode(element);
36 | const rect = range.getBoundingClientRect();
37 | styles.width = `${rect.width}px`;
38 | styles.height = `${rect.height}px`;
39 | range.detach();
40 | }
41 |
42 | styles.x = `${bound.left}px`
43 | styles.y = `${bound.top}px`
44 | styles.width = `${bound.width}px`
45 | styles.height = `${bound.height}px`
46 |
47 | // svg
48 | if (isSvg(element)) return transformSvg(element, styles, parentStyles);
49 |
50 | // 判断一下是否是输入框
51 | let textNode: PesudoInputText | null = null
52 | if (isInput(element) || isTextArea(element)) {
53 | // 输入框增加超出剪裁
54 | styles.overflow = 'hidden'
55 | textNode = createPesudoText(element as HTMLInputElement | HTMLTextAreaElement, styles)
56 | }
57 |
58 | // 取伪元素
59 | const pseudoElts = getPesudoElts(element)
60 |
61 | // 无子图层则当做矩形处理
62 | if (!element.hasChildNodes() && pseudoElts.length === 0 && !textNode) {
63 | return transformRect(element, styles, parentStyles);
64 | }
65 |
66 | /**
67 | * 处理当前图层属性
68 | */
69 | const result = transformFrame(element, styles, parentStyles);
70 | /**
71 | * 递归处理子图层
72 | * textArea不应该有childNodes https://github.com/facebook/react/pull/11639
73 | */
74 | result.children = [];
75 | let childNodes: (ChildNode | PesudoElt | PesudoInputText)[] = element.tagName !== 'TEXTAREA'? Array.from(element.childNodes ?? []) : []
76 |
77 | // 合并伪元素数组和输入框文字
78 | childNodes = childNodes.concat(pseudoElts as any).concat(textNode!)
79 | const convertedChildren = (await Promise.allSettled(childNodes.map(async (childNode) => {
80 | let child;
81 | if (!childNode) {
82 | return null
83 | }
84 | if (childNode.nodeType === Node.ELEMENT_NODE) {
85 | const childStyles = getStyles(childNode as Element);
86 | child = await processOneElement(childNode as HTMLElement, {
87 | ...childStyles,
88 | }, styles);
89 | } else if (childNode.nodeType === Node.TEXT_NODE) {
90 | //文字节点无法获取getComputedStyle,延用父元素的
91 | // 获取文字的实际包围盒
92 | const range = document.createRange();
93 | range.selectNode(childNode);
94 | const rect = range.getBoundingClientRect();
95 | // 判断文字是否折行
96 | const textWrapped = isTextWrapped(range, getNumber(styles.lineHeight))
97 | range.detach();
98 | child = transformText(childNode as any, {
99 | ...styles,
100 | isTextWrapped: textWrapped,
101 | width: `${rect.width}px`,
102 | height: `${rect.height}px`,
103 | // x和y为当前元素的bounding的x和y减去父节点的x和y
104 | x: `${rect.x - bound.x}px`,
105 | y: `${rect.y - bound.y}px`,
106 | left: `${rect.left}px`,
107 | right: `${rect.right}px`,
108 | top: `${rect.top}px`,
109 | bottom: `${rect.bottom}px`,
110 | }, styles);
111 | } else if ((childNode as PesudoElt).nodeType === ExtraNodeType.PESUDO) {
112 | // 伪元素
113 | child = transPseudo((childNode as PesudoElt).type, (childNode as PesudoElt).styles as TargetProps, styles)
114 | } else if (childNode.nodeType === ExtraNodeType.INPUT) {
115 | // 输入框文字按文字处理
116 | child = transformText(childNode.node, childNode.styles, Object.assign(childNode.styles, {isChildNodeStretched: styles.isChildNodeStretched}))
117 | }
118 | return child
119 | })))
120 | result.children = convertedChildren.filter(item => item?.status === 'fulfilled' && !!item?.value).map(item => (item as PromiseFulfilledResult).value)
121 | //子元素排序
122 | result.children?.length && sortByZIndex(result.children as any)
123 |
124 | return result;
125 | }
126 |
127 | const htmlToMG = async (html: HTMLElement, options?: OptionalSettings): Promise => {
128 | options && updateOptions(options)
129 | if (!getComputedStyle) throw new Error('getComputedStyle is not defined');
130 | try {
131 | // 收集被修改的元素数组
132 | console.log('转换前:', html);
133 | const result = await processOneElement(html, getStyles(html) as TargetProps, null as any);
134 | modifiedElements.forEach((clear) => {
135 | clear()
136 | })
137 | modifiedElements.length = 0
138 | console.log('转换结果', result);
139 | return result;
140 | } catch (error) {
141 | console.error('转换出错', error)
142 | }
143 | return null
144 | }
145 |
146 | export { htmlToMG, postProcess, renderToMasterGo }
--------------------------------------------------------------------------------
/src/lib/mixins/base.ts:
--------------------------------------------------------------------------------
1 | import { getNumber } from "../helpers";
2 | import type { TargetProps } from "../index.d";
3 |
4 | /**
5 | * 记录index做图层排序
6 | */
7 | export const transBase = (name: string, styles: TargetProps) => {
8 | return {
9 | name,
10 | index: getNumber(styles.zIndex)
11 | } as BaseNodeMixin & { index?: number };
12 | }
--------------------------------------------------------------------------------
/src/lib/mixins/blend.ts:
--------------------------------------------------------------------------------
1 | import type { TargetProps } from "../index.d";
2 | import { getNumber, transColor } from '../helpers';
3 |
4 | /**
5 | * css 没有 pass-through
6 | */
7 | const blendModeMap = {
8 | 'normal': 'NORMAL',
9 | 'multiply': 'MULTIPLY',
10 | 'screen': 'SCREEN',
11 | 'overlay': 'OVERLAY',
12 | 'darken': 'DARKEN',
13 | 'lighten': 'LIGHTEN',
14 | 'color-dodge': 'COLOR_DODGE',
15 | 'color-burn': 'COLOR_BURN',
16 | 'hard-light': 'HARD_LIGHT',
17 | 'soft-light': 'SOFT_LIGHT',
18 | 'difference': 'DIFFERENCE',
19 | 'exclusion': 'EXCLUSION',
20 | 'hue': 'HUE',
21 | 'saturation': 'SATURATION',
22 | 'color': 'COLOR',
23 | 'luminosity': 'LUMINOSITY',
24 | 'plus-lighter': 'PLUS_LIGHTER',
25 | 'plus-darker': 'PLUS_DARKER',
26 | } as const
27 |
28 | /**
29 | * 混合模式
30 | */
31 | const getBlendMode = (mode: keyof typeof blendModeMap): Effect['blendMode'] => {
32 | return blendModeMap[mode] || 'NORMAL'
33 | }
34 |
35 | /**
36 | * 处理阴影 阴影可以有多个 用逗号分割
37 | */
38 | const transBoxShadow = (styles: TargetProps): ShadowEffect[] => {
39 | let shadowEffects: ShadowEffect[] = []
40 |
41 | let boxShadow = styles.boxShadow
42 |
43 | if (boxShadow !== 'none') {
44 | const PARTS_REG = /\s(?![^(]*\))/;
45 | // 拆段
46 | const parts = boxShadow.split(PARTS_REG);
47 | const chunks = parts.reduce((cur, item) => {
48 | if (!/.*,$/.test(item)) {
49 | // 没到结尾
50 | cur[cur.length - 1].push(item)
51 | } else {
52 | // 去掉结尾,号
53 | cur[cur.length - 1].push(item.substring(0, item.length - 1))
54 | // 建个空的
55 | cur.push([])
56 | }
57 | return cur
58 | }, [[]] as Array).filter(arr => arr.length);
59 | // 多段
60 | shadowEffects = chunks.map((singleShadowArr: string[]) => {
61 | const shadow: {
62 | -readonly [T in keyof ShadowEffect]: ShadowEffect[T]
63 | } = {} as ShadowEffect
64 | // computed style把rgba放在前面了 类似于 'rgba(0, 0, 255, 0.2) 12px 12px 2px 1px'
65 |
66 | // 提取color
67 | shadow.color = transColor(singleShadowArr.find(item => /(rgba|rgb?\(.+?\))/.test(item)) || "rgba(0, 0, 0, 0.84)")!;
68 |
69 | // 取偏移 扩散和弧度
70 | const nums = parts
71 | .filter((n) => n !== "inset")
72 | .filter((n) => !/(rgba|rgb?\(.+?\))/.test(n))
73 | .map(getNumber);
74 |
75 | const [offsetX, offsetY, blurRadius, spreadRadius] = nums;
76 | shadow.offset = {
77 | x: offsetX || 0,
78 | y: offsetY || 0,
79 | }
80 | shadow.radius = blurRadius || 0
81 | shadow.spread = spreadRadius || 0
82 | // 内外阴影
83 | shadow.isVisible = true
84 | // 根据 inset区分内外阴影
85 | shadow.type = singleShadowArr.includes('inset')? 'INNER_SHADOW' : 'DROP_SHADOW'
86 | // 混合模式, 目前CSS没有对阴影的混合默认为normal
87 | shadow.blendMode = 'NORMAL'
88 |
89 | return shadow
90 | })
91 | }
92 |
93 | return shadowEffects
94 | }
95 |
96 | /**
97 | * 高斯模糊/背后图层模糊
98 | */
99 | const transBlur = (styles: TargetProps): BlurEffect[] => {
100 | let blurEffects: BlurEffect[] = []
101 | // 高斯模糊
102 | const filter = styles.filter
103 | // 背后图层模糊
104 | const backdropFilter = styles.backdropFilter
105 |
106 | const trans = (type: BlurEffect['type'], data: string): BlurEffect => {
107 | const blurEffect: {
108 | -readonly [T in keyof BlurEffect]: BlurEffect[T]
109 | } = {} as BlurEffect
110 | blurEffect.type = type
111 | blurEffect.isVisible = true
112 | // 特效 目前CSS没有对模糊的混合。默认为normal
113 | blurEffect.blendMode = 'NORMAL'
114 | blurEffect.radius = getNumber(data.match(/blur\((.+)\)/)?.[1] || '0')
115 | return blurEffect
116 | }
117 |
118 | // 目前只解析模糊 其他特效不解析
119 | if (filter !== 'none' && /blur\(.+\)/.test(filter)) {
120 | blurEffects.push(trans('LAYER_BLUR', filter))
121 | }
122 |
123 | if (backdropFilter !== 'none' && /blur\(.+\)/.test(backdropFilter)) {
124 | blurEffects.push(trans('BACKGROUND_BLUR', backdropFilter))
125 | }
126 |
127 | return blurEffects
128 | }
129 |
130 | /**
131 | * 处理特效
132 | */
133 | const transEffects = (styles: TargetProps): Effect[] => {
134 | const effects: Effect[] = []
135 |
136 | effects.push(...transBoxShadow(styles))
137 | // 根据 inset区分内外阴影
138 | return effects
139 | }
140 |
141 | export const transBlend = (styles: TargetProps) => {
142 | return {
143 | effects: [...transEffects(styles), ...transBlur(styles)],
144 | opacity: getNumber(styles.opacity) || 1,
145 | // 图层本身的混合模式也是mixBlendMode来影响
146 | blendMode: getBlendMode(styles.mixBlendMode as keyof typeof blendModeMap),
147 | // isMask css不存在 暂时不解析
148 | // isMask: false,
149 | } as Omit;
150 | };
--------------------------------------------------------------------------------
/src/lib/mixins/constraints.ts:
--------------------------------------------------------------------------------
1 | import type { TargetProps } from '../index.d';
2 |
3 | export const transConstraints = (styles: TargetProps, parentStyles?: TargetProps) => {
4 | const result = {} as ConstraintMixin
5 | // 约束
6 | if (parentStyles?.isChildNodeStretched) {
7 | // 自身也attach一下
8 | styles.isChildNodeStretched = parentStyles.isChildNodeStretched
9 | // 父元素有缩放,子元素约束改为跟随缩放
10 | result.constraints = {
11 | horizontal: 'SCALE',
12 | vertical: 'SCALE',
13 | }
14 | }
15 | return result
16 | }
--------------------------------------------------------------------------------
/src/lib/mixins/container.ts:
--------------------------------------------------------------------------------
1 | import type { TargetProps } from '../index.d';
2 | import {
3 | transLayout,
4 | transBase,
5 | transScene,
6 | transBlend,
7 | transGeometry,
8 | transRectangleCorner,
9 | transConstraints,
10 | } from './index';
11 | import { getNumber } from '../helpers';
12 | import { options } from '../helpers/config'
13 |
14 | // 可能会存x和y不同的方式 例如 'scroll hidden'
15 | const overFlowEnum = ['hidden', 'scroll', 'auto', 'clip', 'overlay']
16 |
17 | export const transContainer = (styles: TargetProps, parentStyles: TargetProps, name: string) => {
18 | const result = {} as DefaultContainerMixin;
19 | Object.assign(result, transLayout(styles, 'FRAME', parentStyles));
20 | Object.assign(result, transBase(name, styles));
21 | Object.assign(result, transScene(styles));
22 | Object.assign(result, transBlend(styles));
23 | Object.assign(result, transGeometry(styles, 'FRAME'));
24 | Object.assign(result, transRectangleCorner(styles));
25 | Object.assign(result, transConstraints(styles, parentStyles));
26 | return result;
27 | }
28 |
29 | const translateAlign = (cssAlign: string): AutoLayout['mainAxisAlignItems'] => {
30 | switch (cssAlign) {
31 | case 'flex-start':
32 | return 'FLEX_START';
33 | case 'flex-end':
34 | return 'FLEX_END';
35 | case 'center':
36 | return 'CENTER';
37 | case 'space-between':
38 | return 'SPACING_BETWEEN';
39 | default:
40 | return 'FLEX_START';
41 | }
42 | }
43 |
44 | /**
45 | * 子元素统一采用绝对定位
46 | *
47 | */
48 | const transAutoLayout = (styles: TargetProps): Partial => {
49 | const result = {} as AutoLayout;
50 | if (!['inline-flex', 'flex'].includes(styles.display)) {
51 | // 如果有padding则加自动布局 没有则不加
52 | if (getNumber(styles.paddingTop) || getNumber(styles.paddingBottom) || getNumber(styles.paddingLeft) || getNumber(styles.paddingRight)) {
53 | result.flexMode = 'VERTICAL'
54 | } else {
55 | return result
56 | }
57 | } else {
58 | const isHorizontal = styles.flexDirection === 'row';
59 | result.flexMode = isHorizontal ? 'HORIZONTAL' : 'VERTICAL';
60 | result.flexWrap = styles.flexWrap === 'wrap'? 'WRAP' : 'NO_WRAP';
61 | result.itemSpacing = getNumber(isHorizontal ? styles.rowGap : styles.columnGap);
62 | result.crossAxisSpacing = getNumber(isHorizontal ? styles.columnGap : styles.rowGap)
63 | result.mainAxisAlignItems = translateAlign(styles.justifyContent);
64 | result.crossAxisAlignItems = translateAlign(styles.alignItems) as AutoLayout['crossAxisAlignItems'];
65 | if (isHorizontal) {
66 | if (styles.width) result.mainAxisSizingMode = 'FIXED';
67 | if (styles.height) result.crossAxisSizingMode = 'FIXED';
68 | } else {
69 | if (styles.width) result.crossAxisSizingMode = 'FIXED';
70 | if (styles.height) result.mainAxisSizingMode = 'FIXED';
71 | }
72 | }
73 |
74 | result.paddingTop = getNumber(styles.paddingTop);
75 | result.paddingRight = getNumber(styles.paddingRight);
76 | result.paddingBottom = getNumber(styles.paddingBottom);
77 | result.paddingLeft = getNumber(styles.paddingLeft);
78 |
79 | // 描边是否包含在布局计算中
80 | result.strokesIncludedInLayout = styles.boxSizing === 'border-box'? true : false
81 |
82 | return result;
83 | }
84 |
85 | export const transFrameContainer = (styles: TargetProps) => {
86 | const result = {} as FrameContainerMixin;
87 | Object.assign(result, transAutoLayout(styles));
88 | if (options.absoluteBounds) {
89 | //完整尺寸
90 | result.clipsContent = false
91 | } else {
92 | //超出剪裁
93 | result.clipsContent = overFlowEnum.some(situation => styles.overflow.includes(situation));
94 | }
95 | return result;
96 | }
--------------------------------------------------------------------------------
/src/lib/mixins/corner.ts:
--------------------------------------------------------------------------------
1 | import type { TargetProps } from '../index.d';
2 | import { getNumber } from '../helpers';
3 |
4 | /**
5 | *
6 | * @param styles css样式
7 | * @param size 宽度或高度
8 | * @returns
9 | */
10 | const convertCorner = (borderRadius: TargetProps['borderRadius'], size: number) => {
11 | //px
12 | const pxMatches = borderRadius?.match(/^(\d+px)/)
13 | if (pxMatches) {
14 | return getNumber(pxMatches[0])
15 | }
16 |
17 | //百分比 百分比圆角计算为宽高乘百分比后的以r1 r2为半径的椭圆的弧度,默认只按正方形算,
18 | const percentMatches = borderRadius?.match(/^(\d+%)/);
19 | if (percentMatches) {
20 | const percent = getNumber(percentMatches[0])
21 | // 保留两位小数
22 | return parseFloat((size * percent / 100).toFixed(2))
23 | }
24 | return 0
25 | }
26 |
27 | /**
28 | * 普通圆角
29 | */
30 | export const transCorner = (styles: TargetProps) => {
31 | const result = {} as CornerMixin;
32 | result.cornerRadius = convertCorner(styles.borderRadius, getNumber(styles.height))
33 | }
34 |
35 | /**
36 | * 类矩形圆角
37 | */
38 | export const transRectangleCorner = (styles: TargetProps) => {
39 | const result = {} as RectangleCornerMixin;
40 | result.topLeftRadius = convertCorner(styles.borderTopLeftRadius, getNumber(styles.height)) || 0;
41 | result.topRightRadius = convertCorner(styles.borderTopRightRadius, getNumber(styles.height)) || 0;
42 | result.bottomLeftRadius = convertCorner(styles.borderBottomLeftRadius, getNumber(styles.height)) || 0;
43 | result.bottomRightRadius = convertCorner(styles.borderBottomRightRadius, getNumber(styles.height)) || 0;
44 | return result;
45 | }
--------------------------------------------------------------------------------
/src/lib/mixins/geometry.ts:
--------------------------------------------------------------------------------
1 | import type { TargetProps } from '../index.d';
2 | import { getNumber, transColor } from '../helpers';
3 | /**
4 | * 单边描边映射
5 | */
6 | const singleSideStroke = {
7 | borderTop: 'strokeTopWeight',
8 | borderBottom: 'strokeBottomWeight',
9 | borderLeft: 'strokeLeftWeight',
10 | borderRight: 'strokeRightWeight',
11 | } as const
12 |
13 | // 纯色
14 | const transSolidColor = (color: string) => {
15 | const result = {
16 | type: 'SOLID',
17 | isVisible: true,
18 | alpha: 1,
19 | blendMode: 'NORMAL',
20 | color: transColor(color),
21 | } as SolidPaint;
22 | return result;
23 | }
24 |
25 | // 指令角度映射
26 | const angleMap = {
27 | 'to top': 180,
28 | 'to bottom': 0,
29 | 'to left': 270,
30 | 'to right': 90,
31 | 'to left top': 315,
32 | 'to left bottom': 225,
33 | 'to right top': 45,
34 | 'to right bottom': 135,
35 | }
36 |
37 | /**
38 | * 计算渐变线的旋转逻辑的区间
39 | * 设旋转角度为α,当0° <= α < 当右对角线与中心线的夹角,渐变线与图层的两个交点落在x轴上
40 | * 当右对角线与中心线的夹角 < α < (90° + 右对角线与中心线夹角的余角),渐变线与图层的两个交点落在y轴上
41 | * 当 (90° + 右对角线与中心线夹角的余角) < α < (180° + 右对角线与中心线的夹角),渐变线与图层的两个交点落在x轴上
42 | * 当 (180° + 右对角线与中心线的夹角) < α < (270° + 右对角线与中心线夹角的余角),渐变线与图层的两个交点落在y轴上
43 | * 当 (270° + 右对角线与中心线夹角的余角) < α <= 360,渐变线与图层的两个交点落在x轴上
44 | * https://user-images.githubusercontent.com/13540489/219654132-1da0c87d-9a83-462d-8504-8653ec42bae0.png
45 | */
46 | const calculateGradientLineLenghtAndHandlePositionsByAngleAndRect = (rotate: number, width: number, height: number): { length: number, handlePositions: GradientPaint['gradientHandlePositions'] } => {
47 | // 四个角的渐变线可以直接获取
48 | if (rotate === 0) {
49 | return { length: height, handlePositions: [{ x: 0.5, y: 0 }, {x: 0.5, y: 1}]}
50 | } else if (rotate === 90) {
51 | return { length: height, handlePositions: [{ x: 0, y: 0.5 }, {x: 1, y: 0.5}]}
52 | } else if (rotate === 180) {
53 | return { length: height, handlePositions: [{ x: 0.5, y: 1 }, {x: 0.5, y: 0}]}
54 | } else if (rotate === 270) {
55 | return { length: height, handlePositions: [{ x: 1, y: 0.5 }, {x: 0, y: 0.5}]}
56 | }
57 | // 右对角线旋转角度
58 | const diagonalRotateAngle = Math.atan(width / height) * 180 / Math.PI
59 | // 余角
60 | const supplement = 90 - diagonalRotateAngle
61 |
62 | /**
63 | *
64 | * @param acuteAngle 中心点与渐变线相交于图层的点所在的边构成的垂直线,与渐变线之间的锐角
65 | * @param length1 夹角相邻图层的边长
66 | * @param length2 另一条边长
67 | */
68 | const calculation = (acuteAngle: number, length1: number, length2: number) => {
69 | // 单个旋转锐角的临边长为
70 | let side = 0.5 * length1;
71 | // 斜边长
72 | let hill = side / Math.cos(acuteAngle * Math.PI / 180)
73 | // 对边
74 | const opposite = side * Math.tan(acuteAngle * Math.PI / 180)
75 |
76 | // 超出的相似小三角形的斜边
77 | const overHill = 0.5 * length2 - opposite
78 | // 相似小三角形锐角的对边为
79 | const oppositeSimilar = Math.sin(acuteAngle * Math.PI / 180) * overHill
80 | // 渐变线长度 = (大三角形的斜边 + 相似小三角形锐角的对边) * 2
81 | const total = (oppositeSimilar + hill) * 2
82 |
83 | return total
84 | }
85 |
86 | // 默认从上到下
87 | let length = height
88 | // 渐变线起始点和终点的坐标
89 | let handlePositions: GradientPaint['gradientHandlePositions'] = [{ x: 0.5, y: 0 }, {x: 0.5, y: 1}]
90 | if (0 < rotate && rotate <= diagonalRotateAngle) {
91 | const acuteAngle = rotate
92 | length = calculation(acuteAngle, height, width)
93 | const sin = Math.sin(acuteAngle * Math.PI / 180) * length / 2
94 | const cos = Math.cos(acuteAngle * Math.PI / 180) * length / 2
95 | handlePositions[1].x = (0.5 * width + sin) / width
96 | handlePositions[1].y = 1 - (0.5 * height + cos) / height
97 | handlePositions[0].x = 1 - (0.5 * width + sin) / width
98 | handlePositions[0].y = (0.5 * height + cos) / height
99 | } else if (diagonalRotateAngle < rotate && rotate <= (90 + supplement)) {
100 | const acuteAngle = Math.abs(rotate - 90)
101 | length = calculation(acuteAngle, width, height)
102 | const sin = Math.sin(acuteAngle * Math.PI / 180) * length / 2
103 | const cos = Math.cos(acuteAngle * Math.PI / 180) * length / 2
104 | handlePositions[1].x = (0.5 * width + cos) / width
105 | handlePositions[1].y = (0.5 * height + sin) / height
106 | handlePositions[0].x = 1 - (0.5 * width + cos) / width
107 | handlePositions[0].y = 1 - (0.5 * height + sin) / height
108 | } else if ((90 + supplement) < rotate && rotate <= (180 + diagonalRotateAngle)) {
109 | const acuteAngle = Math.abs(180 - rotate)
110 | length = calculation(acuteAngle, height, width)
111 | const sin = Math.sin(acuteAngle * Math.PI / 180) * length / 2
112 | const cos = Math.cos(acuteAngle * Math.PI / 180) * length / 2
113 | if (rotate < 180) {
114 | handlePositions[1].x = (0.5 * width + sin) / width
115 | handlePositions[1].y = (0.5 * height + cos) / height
116 | handlePositions[0].x = 1 - (0.5 * width + sin) / width
117 | handlePositions[0].y = 1 - (0.5 * height + cos) / height
118 | } else {
119 | // 180度后调转头尾
120 | handlePositions[0].x = (0.5 * width + sin) / width
121 | handlePositions[0].y = (0.5 * height + cos) / height
122 | handlePositions[1].x = 1 - (0.5 * width + sin) / width
123 | handlePositions[1].y = 1 - (0.5 * height + cos) / height
124 | }
125 | } else if ((180 + diagonalRotateAngle) < rotate && rotate <= (270 + supplement)) {
126 | const acuteAngle = Math.abs(270 - rotate)
127 | length = calculation(acuteAngle, width, height)
128 | const sin = Math.sin(acuteAngle * Math.PI / 180) * length / 2
129 | const cos = Math.cos(acuteAngle * Math.PI / 180) * length / 2
130 | handlePositions[0].x = (0.5 * width + cos) / width
131 | handlePositions[0].y = (0.5 * height + sin) / height
132 | handlePositions[1].x = 1 - (0.5 * width + cos) / width
133 | handlePositions[1].y = 1 - (0.5 * height + sin) / height
134 | } else if ((270 + supplement) < rotate && rotate <= 360) {
135 | const acuteAngle = Math.abs(360 - rotate)
136 | length = calculation(acuteAngle, height, width)
137 | const sin = Math.sin(acuteAngle * Math.PI / 180) * length / 2
138 | const cos = Math.cos(acuteAngle * Math.PI / 180) * length / 2
139 | handlePositions[0].x = (0.5 * width + sin) / width
140 | handlePositions[0].y = (0.5 * height + cos) / height
141 | handlePositions[1].x = 1 - (0.5 * width + sin) / width
142 | handlePositions[1].y = 1 - (0.5 * height + cos) / height
143 | }
144 |
145 | if (180 <= rotate && rotate <= 270) {
146 | // 做相对于x轴的轴对称翻转(渲染引擎的规律,不知道为什么这么做)
147 | handlePositions.forEach(position => {
148 | if (position.y > 0.5) {
149 | position.y = position.y - 2 * (position.y - 0.5)
150 | } else {
151 | position.y = position.y + 2 * (0.5 - position.y)
152 | }
153 | })
154 | }
155 | return { length, handlePositions}
156 | }
157 |
158 | /**
159 | * css一定会保证至少有两个控制点,否则是无效的数据,所以我们要保证头尾需要是0和100
160 | */
161 | const completeGradientChunks = (chunks: Array, grandientLineLength: number) => {
162 | const completeChunks = [...chunks]
163 | const startPoint = completeChunks[0]
164 | const startChunks = startPoint.split(/(? {
190 | if (stop.includes('%')) {
191 | return Math.min(1, getNumber(stop) / 100)
192 | } else {
193 | return Math.min(1, getNumber(stop) / grandientLineLength)
194 | }
195 | }
196 |
197 | /**
198 | * 处理不完整数据 例如
199 | * 1. 颜色 + 单百分比
200 | * 百分比的话,除了要考虑非递增的情况,不用特殊处理。
201 | * 2. 颜色 + 双百分比
202 | * 系统会自动拆成两种相同颜色,不同百分比,注意处理成递增。
203 | * 3. 颜色 + px(双px浏览器自动分割)
204 | * 根据渐变线的长度转换成百分比和处理非递增。
205 | * 4. 颜色 无单位
206 | * 向前收集直到有单位的为止,均分百分段, 当出现不规则排序时直接忽略。
207 | * 5. 只有单位
208 | * 这种是改渐变的转折点,画布内不太好做,直接忽略
209 | */
210 | const handleGradientChunks = (chunks: Array, grandientLineLength: number) => {
211 |
212 | // 整理数据 保证头尾是0和100
213 | const completeChunks = completeGradientChunks(chunks, grandientLineLength)
214 |
215 | // 例如: ['rgba(0, 1, 2) 0%', 'rgba(0, 1, 2)', rgba(0, 1, 2), 'rgba(0, 1, 2) 100%']
216 | // 当前段中上一个明确有控制点值的位置
217 | let lastValuedStartPostion = 0
218 | // 当前段最后一个明确有控制点值的位置
219 | let lastValuedEndPosition = 1
220 | // 没有明确定义stops位置的数组
221 | const undefinedStops: any[] = [];
222 |
223 | // 遍历处理,转成插件格式
224 | return completeChunks.reduce((result: ColorStop[], colorAndPercent: string, idx: number) => {
225 | const stop: { -readonly[key in keyof ColorStop]: ColorStop[key] } = {
226 | color: {
227 | "r": 0.8470588326454163,
228 | "g": 0.8470588326454163,
229 | "b": 0.8470588326454163,
230 | "a": 1
231 | },
232 | position: 0
233 | }
234 | // 控制点颜色和(百分比/px)
235 | const ctrlPoints = colorAndPercent.split(/(? 1) {
248 | // 记录有明确的控制点的值
249 | lastValuedEndPosition = getNumber(currentCtrlPoints[1])
250 | break
251 | } else {
252 | // 占位
253 | undefinedStops.push('')
254 | }
255 | }
256 | }
257 | // 这个控制点在之前记录的片段内,可以直接计算
258 | // 上一个起始点 + 均分的值
259 | stop.position = lastValuedStartPostion + (lastValuedEndPosition - lastValuedStartPostion) / (undefinedStops.length + 1)
260 | // 减1
261 | undefinedStops.pop()
262 | } else {
263 | stop.position = convertCSSStopToPluginStop(pxOrPercent, grandientLineLength)
264 | // 记录上一个有值的
265 | lastValuedStartPostion = stop.position
266 | }
267 | result.push(stop)
268 | // 从低到高排序
269 | return result.sort((a, b) => a.position - b.position)
270 | }, [] as Array)
271 | }
272 | /**
273 | * 处理线性渐变
274 | * TODO:由于css的linear-gradient转折点可以乱序,这里不做排序处理了,需要元素自身渐变符合规范
275 | */
276 | const transLinearGradient = (background: TargetProps['background'], styles: TargetProps): GradientPaint => {
277 | // 渐变内容
278 | const gradient = background.match(/.*linear-gradient\((.*)\)\s/i)![1]
279 | // 分块处理
280 | let chunks = gradient.split(/(?<=\D)(?:,\s)/)
281 | // 角度
282 | let angle = 0
283 | // 旋转矩阵
284 | let transform: Transform = [[1, 0, 0], [0, 1, 0]];
285 | // 控制点
286 | let gradientStops = []
287 | if (chunks[0].match(/to|deg/)) {
288 | // 把旋转单独提取出来处理
289 | const rotate = chunks.splice(0, 1)[0]
290 | // 有方向
291 | if (rotate.includes('to')) {
292 | // 根据map取角度
293 | angle = angleMap[rotate as keyof typeof angleMap]
294 | } else {
295 | angle = getNumber(rotate) % 360
296 | }
297 | /**
298 | * CSS中的角度值,先将其转换为数学中的角度值,然后再计算旋转矩阵。具体来说,角度值乘以-1,再加上180度,得到数学中的角度值
299 | */
300 | transform = [[Math.cos((angle * Math.PI / 180)), Math.sin((angle * Math.PI / 180)), 0], [-Math.sin((angle * Math.PI / 180)), Math.cos((angle * Math.PI / 180)), 0]]
301 | }
302 | // 计算渐变线的长度
303 | const { length: grandientLineLength , handlePositions } = calculateGradientLineLenghtAndHandlePositionsByAngleAndRect(angle, getNumber(styles.width), getNumber(styles.height))!
304 | gradientStops = handleGradientChunks(chunks, grandientLineLength)
305 | return {
306 | type: 'GRADIENT_LINEAR',
307 | gradientStops,
308 | transform,
309 | gradientHandlePositions: handlePositions
310 | }
311 | }
312 |
313 | /**
314 | * 渐变
315 | * TODO: 圆锥径向处理
316 | */
317 | const transGradient = (background: TargetProps['background'], styles: TargetProps): GradientPaint => {
318 | if (background.includes('linear-gradient')) {
319 | return transLinearGradient(background, styles)
320 | // 线性
321 | } else if (background.includes('conic-gradient')) {
322 | // 圆锥
323 | return {
324 | type: 'GRADIENT_ANGULAR',
325 | transform: [[1, 0, 0], [0, 1, 0]],
326 | gradientStops: [
327 | {
328 | "position": 0,
329 | "color": {
330 | "r": 0.8470588326454163,
331 | "g": 0.8470588326454163,
332 | "b": 0.8470588326454163,
333 | "a": 1
334 | }
335 | },
336 | {
337 | "position": 1,
338 | "color": {
339 | "r": 0.8470588326454163,
340 | "g": 0.8470588326454163,
341 | "b": 0.8470588326454163,
342 | "a": 0
343 | }
344 | }
345 | ],
346 | }
347 | } else if (background.includes('radial-gradient')) {
348 | // 径向
349 | return {
350 | type: 'GRADIENT_RADIAL',
351 | transform: [[1, 0, 0], [0, 1, 0]],
352 | gradientStops: [
353 | {
354 | "position": 0,
355 | "color": {
356 | "r": 0.8470588326454163,
357 | "g": 0.8470588326454163,
358 | "b": 0.8470588326454163,
359 | "a": 1
360 | }
361 | },
362 | {
363 | "position": 1,
364 | "color": {
365 | "r": 0.8470588326454163,
366 | "g": 0.8470588326454163,
367 | "b": 0.8470588326454163,
368 | "a": 0
369 | }
370 | }
371 | ],
372 | }
373 | }
374 | return {
375 | type: 'GRADIENT_LINEAR',
376 | transform: [[1, 0, 0], [0, 1, 0]
377 | ],
378 | gradientStops: [
379 | {
380 | "position": 0,
381 | "color": {
382 | "r": 0.8470588326454163,
383 | "g": 0.8470588326454163,
384 | "b": 0.8470588326454163,
385 | "a": 1
386 | }
387 | },
388 | {
389 | "position": 1,
390 | "color": {
391 | "r": 0.8470588326454163,
392 | "g": 0.8470588326454163,
393 | "b": 0.8470588326454163,
394 | "a": 0
395 | }
396 | }
397 | ],
398 | gradientHandlePositions: [{x: 0.5, y: 0.5}, {x: 0.5, y: 1}],
399 | }
400 | }
401 |
402 | const transScaleMode = (size?: string, repeat?: string, objectFit?: string) => {
403 | if (repeat === 'no-repeat') return 'FILL';
404 | if (size === 'contain' || objectFit === 'contain') return 'FIT';
405 | if (size === 'cover' || objectFit === 'cover') return 'STRETCH';
406 | return 'FILL';
407 | }
408 |
409 | /**
410 | * 描边样式
411 | */
412 | const transStrokeStyle = (borderStyle: CSSStyleDeclaration['borderStyle']): GeometryMixin['strokeStyle'] => {
413 | switch (borderStyle) {
414 | case 'solid':
415 | return 'SOLID';
416 | case 'dashed':
417 | return 'DASH';
418 | default:
419 | return 'SOLID'
420 | }
421 | }
422 |
423 | interface ImageConfig {
424 | backgroundImage?: string;
425 | backgroundSize?: string;
426 | backgroundRepeat?: string;
427 | objectFit?: string;
428 | }
429 |
430 | /**
431 | * 处理非图片填充
432 | */
433 | const transFill = (color: string, styles: TargetProps) => {
434 | const result = [] as Paint[];
435 | if (color.match(/gradient/)) {
436 | // 单独处理非纯色背景(渐变)
437 | result.push(transGradient(color, styles))
438 | } else if (color) {
439 | result.push(transSolidColor(color))
440 | };
441 | return result;
442 | }
443 |
444 | /**
445 | * 处理图片渐变
446 | */
447 | const transImagePaint = ({ backgroundImage, backgroundRepeat, backgroundSize, objectFit }: ImageConfig): ImagePaint | null => {
448 | let result: ImagePaint| null = null;
449 | if (backgroundImage && backgroundImage !== 'none') {
450 | result = {
451 | type: 'IMAGE',
452 | isVisible: true,
453 | alpha: 1,
454 | blendMode: 'NORMAL',
455 | imageRef: backgroundImage,
456 | scaleMode: transScaleMode(backgroundSize, backgroundRepeat, objectFit),
457 | } as ImagePaint;
458 | }
459 | return result
460 | }
461 |
462 | type SingleSideStroke = {
463 | strokeWeight: number;
464 | color: string
465 | side: typeof singleSideStroke[keyof typeof singleSideStroke]
466 | }
467 |
468 | /**
469 | * 处理某一侧描边
470 | */
471 | const handleSingleSideBorder = (border: TargetProps['border'], side: typeof singleSideStroke[keyof typeof singleSideStroke]): SingleSideStroke => {
472 | const result: SingleSideStroke = {} as SingleSideStroke
473 | const args = border.split(/(? {
490 | // 是否存在不为0的边
491 | let hasNoneZeroStrokeWeight = false
492 | // 存不同描边的映射
493 | const strokeMap: { [key: string]: SingleSideStroke[] } = {};
494 | [
495 | { value: styles.borderTop, side: singleSideStroke['borderTop'] },
496 | { value: styles.borderBottom, side: singleSideStroke['borderBottom'] },
497 | { value: styles.borderLeft, side: singleSideStroke['borderLeft'] },
498 | { value: styles.borderRight, side: singleSideStroke['borderRight'] }
499 | ].forEach((border) => {
500 | const stroke = handleSingleSideBorder(border.value, border.side)
501 | if (stroke.strokeWeight > 0) {
502 | hasNoneZeroStrokeWeight = true
503 | }
504 | if (!strokeMap[stroke.color]) {
505 | strokeMap[stroke.color] = [stroke]
506 | } else {
507 | strokeMap[stroke.color].push(stroke)
508 | }
509 | })
510 | // 如果四条边都是0则不需要描边
511 | if (!hasNoneZeroStrokeWeight) {
512 | return []
513 | }
514 | // 当某种颜色所有边都是0则不单独增加描边,归入其他颜色,只将其设置为0
515 | let zeroWeightStrokes:SingleSideStroke[] = []
516 | const finalArr = Object.entries(strokeMap).filter(([_, strokes]) => {
517 | const isAllZero = strokes.every(item => item.strokeWeight === 0)
518 | if (isAllZero) {
519 | zeroWeightStrokes.push(...strokes)
520 | return false
521 | }
522 | return true
523 | })
524 | finalArr[0][1].push(...zeroWeightStrokes)
525 | return finalArr
526 | }
527 |
528 | //TODO: strokeDashes
529 | export const transGeometry = (styles: TargetProps, type: NodeType) => {
530 | // 当background-image可以多个值,用逗号隔开,不是图片的时候,置空,转去处理background属性,因为渐变也会被当成background-image
531 | // styles.backgroundImage = /url\("(.*)"\)/.exec(styles.backgroundImage)?.[1] || '';
532 | const images = styles.backgroundImage.split(/(?=url)(?![^(]*\))/).map(item => /url\("(.*)"\)/.exec(item)?.[1]).filter(item => !!item);
533 |
534 | let fillsConvertedByBg: Paint[] = []
535 | if (type === 'TEXT') {
536 | fillsConvertedByBg = transFill(styles.color, styles)
537 | } else {
538 | // 多段颜色
539 | styles.background.split(/(?<=border-box),*\s/).reduce((cur, background) => {
540 | cur.push(...transFill(background, styles))
541 | return cur
542 | }, fillsConvertedByBg)
543 | }
544 | // 填充
545 | const fills = [
546 | ...fillsConvertedByBg.reverse(),
547 | ...(images.map(item => transImagePaint({
548 | backgroundImage: item,
549 | backgroundRepeat: styles.backgroundRepeat,
550 | backgroundSize: styles.backgroundSize,
551 | objectFit: styles.objectFit,
552 | })).filter(item => !!item)).reverse()] as Paint[];
553 | const result = {} as GeometryMixin & RectangleStrokeWeightMixin;
554 | result.fills = fills;
555 | if (type !== 'TEXT') {
556 | result.strokeWeight = parseFloat(styles.borderWidth) || 0;
557 | const translatedStrokes = transStrokes(styles)
558 | const strokes = translatedStrokes.map((item) => {
559 | const [color, singleSideStrokes] = item
560 | const paint = transSolidColor(color)
561 | // 如果只有一条边则全部边都一种粗细,不需要单独设置单边粗细
562 | if (translatedStrokes.length > 1 || singleSideStrokes.some(({ strokeWeight }) => strokeWeight === 0)) {
563 | for (const { strokeWeight, side } of singleSideStrokes) {
564 | result[side] = strokeWeight
565 | }
566 | }
567 | return paint as SolidPaint
568 | });
569 | //文字节点由于复用父节点的样式 border不继承 只继承color
570 | result.strokes = strokes;
571 | result.strokeStyle = transStrokeStyle(styles.borderStyle)
572 | result.strokeAlign = styles.boxSizing === 'border-box'? 'INSIDE' : 'OUTSIDE';
573 | result.strokeCap = 'NONE';
574 | result.strokeJoin = 'MITER';
575 | }
576 |
577 | return result;
578 | }
--------------------------------------------------------------------------------
/src/lib/mixins/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 这个文件夹里的转换函数是和插件Mixin定义一一对应的
3 | */
4 |
5 | // 基础类型
6 | export { transBase } from './base';
7 | export { transScene } from './scene';
8 | export { transBlend } from './blend';
9 | export { transGeometry } from './geometry';
10 | export { transLayout } from './layout';
11 | export { transConstraints } from './constraints'
12 | export { transRectangleCorner } from './corner';
13 | export { transFrameContainer } from './container';
14 | // 复合类型
15 | export { transShape } from './shape';
16 | export { transContainer } from './container';
--------------------------------------------------------------------------------
/src/lib/mixins/layout.ts:
--------------------------------------------------------------------------------
1 | import type { TargetProps } from '../index.d';
2 | import { getNumber, isFliped } from '../helpers';
3 | import { fromObject, decomposeTSR, Matrix, applyToPoint } from 'transformation-matrix'
4 | // 最小值
5 | const MIN_VALUE = 0.01
6 |
7 | /**
8 | * 提取矩阵
9 | * @param transform
10 | * @returns
11 | */
12 | const extractTransform = (transform: string) => {
13 | const matches = transform.match(/^matrix\((.*)\)$/)
14 | if (!matches) {
15 | return null
16 | }
17 | const [a, b, c, d, e, f] = matches[1].split(',').map((item: String) => Number(item));
18 | return { a, b, c, d, e, f };
19 | }
20 |
21 | // 提取缩放中心
22 | const convertTransformOrigin = (styles: TargetProps): ScaleCenter => {
23 | const origins = styles.transformOrigin.split(' ')
24 | const height = getNumber(styles.height)
25 | const halfHeight = height / 2
26 | const width = getNumber(styles.width)
27 | const halfWidth = width / 2
28 | let x = getNumber(origins[0])
29 | let y = getNumber(origins[1])
30 |
31 | const map: { [key: string]: ScaleCenter} = {
32 | [`0-0`]: 'TOPLEFT',
33 | [`0-${halfHeight}`]: 'LEFT',
34 | [`0-${height}`]: 'BOTTOMLEFT',
35 | [`${halfWidth}-${halfHeight}`]: 'CENTER',
36 | [`${halfWidth}-0`]: 'TOP',
37 | [`${halfWidth}-${height}`]: 'BOTTOM',
38 | [`${width}-0`]: 'TOPRIGHT',
39 | [`${width}-${halfHeight}`]: 'RIGHT',
40 | [`${width}-${height}`]: 'BOTTOMRIGHT'
41 | }
42 | if (x < (halfWidth / 2)) {
43 | // 1/4 < x 归为左侧
44 | x = 0
45 | } else if ((halfWidth / 2) <= x && x <= (halfWidth + (halfWidth / 2))) {
46 | // 1/4 <= x <= 3/4 归为中间
47 | x = halfWidth
48 | } else {
49 | // 归为右侧
50 | x = width
51 | }
52 |
53 | if (y < (halfHeight / 2)) {
54 | // 1/4 < y 归为顶部
55 | y = 0
56 | } else if ((halfHeight / 2) <= y && y <= (halfHeight + (halfHeight / 2))) {
57 | // 1/4 <= y <= 3/4 归为中间
58 | y = halfHeight
59 | } else {
60 | // 归为底部
61 | y = height
62 | }
63 |
64 | return map[`${x}-${y}`]
65 | }
66 |
67 | /**
68 | * 解析矩阵 将scale剥离出来
69 | */
70 | const deconstructTransform = (transform: Matrix, styles: TargetProps) => {
71 | // 默认x和y都翻转
72 | const fliped = isFliped([[transform.a, transform.c, transform.e], [transform.d, transform.d, transform.f]])
73 | const { scale } = decomposeTSR(transform, fliped, false)
74 |
75 | if (scale.sx !== 1 || scale.sy !== 1) {
76 | // 元素有拉伸 子元素跟随缩放
77 | styles.isChildNodeStretched = true
78 | }
79 | }
80 |
81 | export const transLayout = (styles: TargetProps, type: NodeType, parentStyles?: TargetProps) => {
82 | const result = {} as LayoutMixin;
83 |
84 | // 如果有自动布局容器存在,子元素默认全部都是绝对定位 绝对定位应该布局 如果是ABSOLUTE 需要先设置 非ABSOLUTE的话 子图层设置旋转 h w x y可能不生效
85 | result.layoutPositioning = 'ABSOLUTE'
86 |
87 | result.width = styles.width === 'auto' ? MIN_VALUE : Math.max(MIN_VALUE, getNumber(styles.width));
88 | result.height = styles.height === 'auto' ? MIN_VALUE : Math.max(MIN_VALUE, getNumber(styles.height));
89 | result.x = getNumber(styles.x)
90 | result.y = getNumber(styles.y)
91 |
92 | // if (styles.position === 'absolute') {
93 | // // 处理绝对定位
94 | // result.x += getNumber(styles.borderLeftWidth || '0px')
95 | // result.y += getNumber(styles.borderTopWidth || '0px')
96 | // }
97 |
98 | if (styles.isPesudo) {
99 | // 伪类无法计算包围盒 要加上父元素border和position
100 | result.x += getNumber(parentStyles?.borderLeftWidth || '0px') + getNumber(styles.left) + getNumber(styles.marginLeft);;
101 | result.y += getNumber(parentStyles?.borderTopWidth || '0px') + getNumber(styles.top) + getNumber(styles.marginTop);;
102 | // if (styles.inset === '0px' && styles.margin === 'auto') {
103 | // // inset为0px 和 margin auto才会生效 水平垂直居中
104 | // result.y = (getNumber(parentStyles.height) - getNumber(styles.height)) / 2
105 | // result.x = (getNumber(parentStyles.width) - getNumber(styles.width)) / 2
106 | // }
107 | }
108 |
109 | if (type !== 'TEXT') {
110 | // 处理transform
111 | const matrix = extractTransform(styles.transform)
112 | if (matrix) {
113 | const { a, b, c, d, e, f } = matrix
114 | // 变换中心
115 | const center = styles.transformOrigin.split(' ').map(getNumber)
116 | // 相对于变换中心的原点变换
117 | const newPoint = applyToPoint({ a, b, c, d, e: 0, f: 0 }, {x: 0 - center[0], y: 0 - center[1]})
118 | result.relativeTransform = [[a, c, newPoint.x + center[0] + result.x + e], [b, d, newPoint.y + center[1] + result.y + f]]
119 | deconstructTransform(fromObject(matrix), styles);
120 | }
121 | }
122 |
123 | return result;
124 | };
--------------------------------------------------------------------------------
/src/lib/mixins/scene.ts:
--------------------------------------------------------------------------------
1 | import type { TargetProps } from "../index.d";
2 |
3 | export const transScene = (style: TargetProps): SceneNodeMixin => {
4 | return {
5 | isVisible: style.visibility !== 'hidden',
6 | isLocked: false,
7 | };
8 | }
--------------------------------------------------------------------------------
/src/lib/mixins/shape.ts:
--------------------------------------------------------------------------------
1 | import { TargetProps } from '../index.d';
2 | import {
3 | transBase,
4 | transScene,
5 | transBlend,
6 | transGeometry,
7 | transLayout,
8 | transConstraints,
9 | } from './index';
10 |
11 | export const transShape = (name: string, styles: TargetProps, parentStyles: TargetProps, nodeType: NodeType) => {
12 | return {
13 | ...transBase(name, styles),
14 | ...transScene(styles),
15 | ...transBlend(styles),
16 | ...transGeometry(styles, nodeType),
17 | ...transLayout(styles, nodeType, parentStyles),
18 | ...transConstraints(styles, parentStyles),
19 | } as DefaultShapeMixin & RectangleStrokeWeightMixin & ConstraintMixin;
20 | };
--------------------------------------------------------------------------------
/src/lib/postProcess.ts:
--------------------------------------------------------------------------------
1 | import { convertImageToBuffer } from './helpers'
2 |
3 |
4 | import type { TargetNode, IFrameNode } from './index.d';
5 | /**
6 | * 后置处理,主要将图片的src处理成buffer
7 | */
8 | export const postProcess = async (result: TargetNode): Promise => {
9 | // secondary operation
10 | const promises: any[] = []
11 |
12 | // traverse
13 | const step = (root: TargetNode & {[key: string] : any} | null) => {
14 | if (!root) {
15 | return null
16 | }
17 | try {
18 | const keys = Object.keys(root)
19 | for (const key of keys) {
20 | if (['fills', 'strokes'].includes(key)) {
21 | const paints = root[key]
22 | paints?.forEach((paint: ImagePaint) => {
23 | if (paint.type === 'IMAGE') {
24 | // image paint
25 | promises.push(convertImageToBuffer(paint))
26 | }
27 | })
28 | } else if (key === 'children' && (root as IFrameNode).children?.length) {
29 | for (const child of (root as IFrameNode).children) {
30 | step(child)
31 | }
32 | }
33 | }
34 | } catch (error) {
35 | console.error('error occured in secondary operation', error)
36 | }
37 | }
38 | step(result)
39 | await Promise.allSettled(promises)
40 | return result
41 | }
--------------------------------------------------------------------------------
/src/lib/render.ts:
--------------------------------------------------------------------------------
1 | import { autoLayoutKeys, handleAutoLayout, AutoLayoutData, handlePaints, getMatchingFont, normalizeName } from './helpers'
2 | import { ISvgNode, ITextNode, TargetNode, ValidNode } from './index.d';
3 |
4 | // 所有可用的字符map
5 | const fontMap: Record = {};
6 |
7 | type Root = TargetNode & { [key: string]: any }
8 |
9 | function hasSetter (obj: SceneNode, prop: string) {
10 | return !!Reflect.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), prop)?.set
11 | }
12 |
13 |
14 | /**
15 | * 处理容器
16 | */
17 | const generateFrame = async (node: Root, result: FrameNode & { [key: string]: any }) => {
18 | try {
19 | // 过滤出autoLayout相关属性, 单独处理
20 | const keys = Object.keys(node).filter(key => !autoLayoutKeys.includes(key as any))
21 |
22 | // 处理自动布局
23 | if (node.flexMode && node.flexMode !== 'NONE') {
24 |
25 | handleAutoLayout(node as AutoLayoutData, result)
26 | }
27 |
28 | // 赋值通用属性
29 | for(const key of keys){
30 | if(typeof node[key] !== 'function' && hasSetter(result, key)){
31 | // 处理paint
32 | if((['fills', 'strokes']).includes(key)) {
33 | result[key] = (await handlePaints(node[key])).filter(item => item.status === 'fulfilled').map(item => (item as PromiseFulfilledResult).value)
34 | } else {
35 | result[key] = node[key];
36 | }
37 | }
38 | }
39 |
40 | let indexOverZero: [SceneNode, number][] = []
41 |
42 | // 处理子节点
43 | await Promise.allSettled(node.children?.map((childNode: TargetNode, idx: number) => {
44 | return createLayer(childNode).then(child => {
45 | if (child) {
46 | //这里需要先append进去再修改子节点属性,不然某些会不生效 如layoutPositioning
47 | result.appendChild(child!);
48 | if (childNode.index > 0) {
49 | // 这里已经排好序 所以无须排序
50 | indexOverZero.push([child, idx])
51 | }
52 | return walk(childNode, child);
53 | }
54 | });
55 | }) || []);
56 | // 这里设置了自动布局会乱序 再重新改一下顺序
57 | indexOverZero.forEach(([node, correctIdx]) => {
58 | result.insertChild(correctIdx, node)
59 | })
60 |
61 | return result;
62 | } catch (error) {
63 | console.error('发生错误,', error)
64 | return null
65 | }
66 |
67 | }
68 |
69 | /**
70 | * 处理矩形
71 | */
72 | const generateRectangle = async (node: Root, result: RectangleNode & { [key: string]: any }) => {
73 | const keys = Object.keys(node)
74 |
75 | // 赋值通用属性
76 | for(const key of keys){
77 | try{
78 | if(typeof node[key] !== 'function' && hasSetter(result, key)){
79 | // 处理paint
80 | if((['fills', 'strokes']).includes(key)) {
81 | result[key] = (await handlePaints(node[key])).filter(item => item.status === 'fulfilled').map(item => (item as PromiseFulfilledResult).value)
82 | } else {
83 | result[key] = node[key];
84 | }
85 | }
86 | }
87 | catch (e){
88 | console.log(`Failed to set ${key} of node ${result?.name}`, e);
89 | }
90 | }
91 |
92 | return result
93 | }
94 |
95 | /**
96 | * 处理文字
97 | */
98 | const generateText = async (node: Root, result: TextNode & { [key: string]: any }) => {
99 |
100 | const textStyles = node.textStyles
101 | const textAutoResize = node.textAutoResize
102 | const layoutPositioning = node.layoutPositioning
103 | //@ts-ignore
104 | delete node.textStyles
105 | delete node.textAutoResize
106 | //@ts-ignore
107 | delete node.layoutPositioning
108 |
109 | const keys = Object.keys(node)
110 | result.layoutPositioning = layoutPositioning
111 |
112 | // 在修改完宽高后,单行模式会变成固定宽高 所以textAutoResize最后设置
113 |
114 | // 设置样式
115 | textStyles?.forEach(async (style: TextSegStyle) => {
116 | // 加载字体,取第一个可以加载的
117 | const fontName: FontName = style.textStyle.fontName
118 | const family = await getMatchingFont(fontName, fontMap)
119 | result.setRangeFontName(style.start, style.end, family as FontName);
120 | result.setRangeLineHeight(style.start, style.end, style.textStyle.lineHeight);
121 | result.setRangeFontSize(style.start, style.end, style.textStyle.fontSize);
122 | result.setRangeLetterSpacing(style.start, style.end, style.textStyle.letterSpacing)
123 | result.setRangeTextDecoration(style.start, style.end, style.textStyle.textDecoration)
124 | })
125 |
126 |
127 | // 赋值通用属性
128 | for(const key of keys){
129 | try{
130 | if(typeof node[key] !== 'function' && hasSetter(result, key)){
131 | // 处理paint
132 | if((['fills', 'strokes']).includes(key)) {
133 | result[key] = (await handlePaints(node[key])).filter(item => item.status === 'fulfilled').map(item => (item as PromiseFulfilledResult).value)
134 | } else {
135 | result[key] = node[key];
136 | }
137 | }
138 | }
139 | catch (e){
140 | console.log(`Failed to set ${key} of node ${result?.name}`, e);
141 | }
142 | }
143 | result.textAutoResize = textAutoResize
144 |
145 | return result;
146 | }
147 |
148 | /**
149 | * 处理svg
150 | */
151 | const generateSvg = async (node: Root, result: PenNode & { [key: string]: any }) => {
152 | const keys = Object.keys(node)
153 |
154 | // 赋值通用属性
155 | for(const key of keys){
156 | try{
157 | if(typeof node[key] !== 'function' && hasSetter(result, key)){
158 | // 处理paint
159 | if((['fills', 'strokes']).includes(key)) {
160 | result[key] = (await handlePaints(node[key])).filter(item => item.status === 'fulfilled').map(item => (item as PromiseFulfilledResult).value)
161 | } else {
162 | result[key] = node[key];
163 | }
164 | }
165 | }
166 | catch (e){
167 | console.log(`Failed to set ${key} of node ${result?.name}`, e);
168 | }
169 | }
170 |
171 | return result;
172 | }
173 |
174 | /**
175 | * 根据类型创建图层
176 | */
177 |
178 | async function createLayer(node: Root) {
179 | if (!node) {
180 | return null
181 | }
182 | switch (node?.type as NodeType) {
183 | case 'FRAME': {
184 | return mg.createFrame()
185 | }
186 |
187 | case 'RECTANGLE': {
188 | return mg.createRectangle()
189 | }
190 |
191 | case 'TEXT': {
192 | return mg.createText()
193 | }
194 |
195 | case 'PEN': {
196 | return await mg.createNodeFromSvgAsync(node.content)
197 | }
198 | }
199 | return null
200 | }
201 |
202 | const walk = async (treeNode: Root, layer: any) => {
203 | if (!treeNode || !layer) {
204 | return null
205 | }
206 | let root: ValidNode | null = {} as ValidNode
207 |
208 | // 旋转缩放倾斜会影响布局 需要后置处理 先提取出来
209 | const relativeTransform = treeNode.relativeTransform
210 | //@ts-ignore
211 | delete treeNode.relativeTransform
212 |
213 | switch (treeNode?.type as NodeType) {
214 | case 'FRAME': {
215 | root = await generateFrame(treeNode, layer)
216 | break;
217 | }
218 |
219 | case 'RECTANGLE': {
220 | root = await generateRectangle(treeNode, layer)
221 | break
222 | }
223 |
224 | case 'TEXT': {
225 | root = await generateText(treeNode as ITextNode, layer)
226 | break;
227 | }
228 |
229 | case 'PEN': {
230 | root = await generateSvg(treeNode as ISvgNode, layer);
231 | break;
232 | }
233 |
234 | default: {
235 | throw new Error('failed to convert, layer has unknown type.')
236 | }
237 | }
238 | requestAnimationFrame(() => {
239 | // 统一做变换
240 | relativeTransform && root && (root.relativeTransform = relativeTransform)
241 | })
242 | return root
243 |
244 | }
245 |
246 | /**
247 | *
248 | * @param root html-mastergo导出的并且经过postProcess处理的(一般来说是将图片的src生成UInt8Array的数据)json数据
249 | * @returns
250 | */
251 | export const render = async (root: TargetNode): Promise => {
252 | if (!mg) {
253 | throw new Error('Please invoke this function at plugin environment')
254 | }
255 | if (!Object.keys(fontMap).length) {
256 | // 统计可用字体
257 | const fontList = await mg.listAvailableFontsAsync();
258 | fontList.forEach(font => {
259 | const { family, style } = font.fontName
260 | fontMap[`${normalizeName(family)}-${normalizeName(style)}`] = font.fontName;
261 | });
262 | }
263 | // 根节点为页面下frame
264 | return await walk(root, mg.createFrame())
265 | }
266 |
--------------------------------------------------------------------------------
/src/lib/transPseudo.ts:
--------------------------------------------------------------------------------
1 | import type { IRectangleNode, TargetProps } from './index.d';
2 | import { transShape, transRectangleCorner } from './mixins';
3 |
4 | export const transPseudo = (pseudoType: '::before' | '::after', styles: TargetProps, parentStyles: TargetProps) => {
5 | if (styles.display === 'none') {
6 | return null
7 | }
8 |
9 | const result = {
10 | ...transShape(pseudoType, styles, parentStyles, 'RECTANGLE'),
11 | ...transRectangleCorner(styles),
12 | type: 'RECTANGLE'
13 | } as IRectangleNode;
14 |
15 | return result;
16 | };
--------------------------------------------------------------------------------
/src/lib/transformFrame.ts:
--------------------------------------------------------------------------------
1 | import type { IFrameNode, TargetProps } from './index.d';
2 | import {
3 | transContainer,
4 | transFrameContainer,
5 | } from './mixins';
6 |
7 | /**
8 | * 解析容器
9 | */
10 | export const transformFrame = (element: Element, styles: TargetProps, parentStyles: TargetProps) => {
11 | const result = {
12 | ...transContainer(styles, parentStyles, element.tagName || element.id),
13 | ...transFrameContainer(styles),
14 | } as IFrameNode;
15 | result.type = 'FRAME';
16 |
17 | return result;
18 | };
--------------------------------------------------------------------------------
/src/lib/transformRect.ts:
--------------------------------------------------------------------------------
1 | import type { IRectangleNode, TargetProps } from './index.d';
2 | import { transShape, transRectangleCorner } from './mixins';
3 |
4 | export const transformRect = (element: Element, styles: TargetProps, parentStyles: TargetProps) => {
5 | // 处理图片src
6 | const imgSrc = element.getAttribute('src');
7 | if (imgSrc && styles.backgroundImage === 'none') {
8 | styles.backgroundImage = `url("${imgSrc}")`;
9 | }
10 |
11 | const result = {
12 | ...transShape(element.tagName || element.id, styles, parentStyles, 'RECTANGLE'),
13 | ...transRectangleCorner(styles),
14 | type: 'RECTANGLE'
15 | } as IRectangleNode;
16 |
17 | return result;
18 | };
--------------------------------------------------------------------------------
/src/lib/transformSvg.ts:
--------------------------------------------------------------------------------
1 | import type { ISvgNode, TargetProps } from './index.d';
2 | import { transLayout, transConstraints } from './mixins';
3 |
4 | export const transformSvg = (node: Element, styles: TargetProps, parentStyles: TargetProps) => {
5 | const result = {} as ISvgNode;
6 |
7 | // 填充
8 | const fill = styles.fill;
9 | const stroke = styles.stroke;
10 | const color = styles.color;
11 |
12 | if ((node as HTMLElement).style.fill.toLowerCase() === 'currentcolor') {
13 | // fill优先级高
14 | (node as HTMLElement).style.fill = /rgba|rgb/.test(fill)? fill : color;
15 | }
16 | if ((node as HTMLElement).style.stroke.toLowerCase() === 'currentcolor') {
17 | // stroke优先级高
18 | (node as HTMLElement).style.stroke = /rgba|rgb/.test(stroke)? stroke : color;
19 | }
20 |
21 | result.type = 'PEN';
22 | let svgString = node.outerHTML
23 | // 替换填充
24 | svgString = svgString.replace(/(fill="currentColor"|fill="")/i, `fill="${/rgba|rgb/.test(fill)? fill : color}"`);
25 | //替换描边
26 | svgString = svgString.replace(/(stroke="currentColor"|stroke="")/i, `stroke="${/rgba|rgb/.test(stroke)? stroke : color}"`);
27 | // 获取svg string
28 | result.content = svgString
29 | Object.assign(result, transLayout(styles, 'PEN', parentStyles));
30 | Object.assign(result, transConstraints(styles, parentStyles));
31 |
32 | return result;
33 | }
--------------------------------------------------------------------------------
/src/lib/transformText.ts:
--------------------------------------------------------------------------------
1 | import type { TargetProps, ITextNode } from './index.d';
2 | import {
3 | transShape,
4 | } from './mixins';
5 | import { getNumber, FONT_WEIGHTS } from './helpers';
6 |
7 | // 水平对齐方式
8 | const transTextAlign = (align: string): TextNode['textAlignHorizontal'] => {
9 | switch (align) {
10 | case 'left':
11 | return 'LEFT';
12 | case 'center':
13 | return 'CENTER';
14 | case 'right':
15 | return 'RIGHT';
16 | case 'justify':
17 | return 'JUSTIFIED';
18 | default:
19 | return 'LEFT';
20 | }
21 | }
22 |
23 | //垂直对齐
24 | function transVerticalAlign(align: string): TextNode['textAlignVertical'] {
25 | switch (align) {
26 | case 'middle':
27 | return 'CENTER'
28 | case 'bottom':
29 | return 'BOTTOM'
30 | default:
31 | return 'TOP'
32 | }
33 | }
34 |
35 | //文字装饰
36 | function transTextDecoration(decoration: string): TextSegStyle['textStyle']['textDecoration'] {
37 | switch (decoration) {
38 | case 'underline':
39 | return 'UNDERLINE'
40 | case 'line-through':
41 | return 'STRIKETHROUGH'
42 | case 'node':
43 | default:
44 | return 'NONE'
45 | }
46 | }
47 |
48 | const transTextStyle = (fontStyle: string, fontWeight: keyof typeof FONT_WEIGHTS) => {
49 | // 获取字重
50 | const weight = FONT_WEIGHTS[fontWeight]
51 | // fontStyle
52 | let style = 'Regular'
53 | switch (fontStyle) {
54 | case 'italic':
55 | style = 'Italic';
56 | case 'oblique':
57 | style = 'Italic';
58 | default:
59 | style = 'Regular';
60 | }
61 | // mastergo style是 fontweight + style Regular不显示
62 | return style === 'Italic'? `${weight} ${style}` : weight
63 | }
64 |
65 | // 计算在画布中实际行高
66 | function calculateLineHeight(styles: TargetProps) {
67 | if (styles.boxSizing === 'border-box') {
68 | let lineHeight = styles.lineHeight === 'normal'? getNumber(styles.fontSize) * 1.14 : getNumber(styles.lineHeight)
69 | // 怪异盒模型 行高需要加上下borderWidth, mg中描边不参与高度计算
70 | return Math.min(getNumber(styles.height), lineHeight + getNumber(styles.borderTopWidth) + getNumber(styles.borderBottomWidth))
71 | } else {
72 | // 标准盒模型
73 | return getNumber(styles.lineHeight)
74 | }
75 | }
76 |
77 | //TODO: hyperlink
78 | export const transformText = (text: HTMLElement, styles: TargetProps, parentStyles: TargetProps) => {
79 | if (!text.textContent || styles.display === 'none') {
80 | // 非输入框
81 | return null;
82 | }
83 | const result = {} as ITextNode;
84 |
85 | const {
86 | overflow,
87 | textOverflow,
88 | whiteSpace,
89 | display,
90 | webkitBoxOrient,
91 | webkitLineClamp,
92 | isTextWrapped,
93 | textAlign,
94 | verticalAlign,
95 | fontFamily,
96 | fontSize,
97 | fontStyle,
98 | fontWeight,
99 | letterSpacing,
100 | textDecorationLine,
101 | color,
102 | } = styles
103 |
104 | result.characters = text.textContent || '';
105 | result.type = 'TEXT';
106 |
107 | // 文字模式 如果文字折行则不使用单行模式
108 | if (overflow === 'hidden' && textOverflow === 'ellipsis' && ((whiteSpace === 'nowrap') || (display === '-webkit-box' && webkitBoxOrient === 'vertical' && getNumber(webkitLineClamp) > 0))) {
109 | // 当有截断时,单行截断的文字使用range计算的width会有偏差 这里延用父元素的宽度,不再使用计算的文字宽度
110 | styles.width = parentStyles.width
111 | result.textAutoResize = 'TRUNCATE'
112 | } else if (isTextWrapped) {
113 | // 文字具有多行
114 | result.textAutoResize = 'NONE'
115 | } else {
116 | result.textAutoResize = 'WIDTH_AND_HEIGHT'
117 | }
118 | // 文字片段实际占据行高
119 | let lineHeight = getNumber(fontSize) * 1.14
120 | if (styles.lineHeight.endsWith('px')) {
121 | lineHeight = getNumber(styles.lineHeight)
122 | }
123 |
124 |
125 | //对齐
126 | result.textAlignHorizontal = transTextAlign(textAlign);
127 | result.textAlignVertical = transVerticalAlign(verticalAlign);
128 |
129 | //分段样式 默认一段
130 | result.textStyles = [{
131 | start: 0,
132 | end: result.characters.length,
133 | textStyle: {
134 | lineHeight: {
135 | unit: 'PIXELS',
136 | value: calculateLineHeight(styles),
137 | },
138 | fontSize: getNumber(fontSize),
139 | fontName: {
140 | family: fontFamily,
141 | style: transTextStyle(fontStyle, fontWeight as keyof typeof FONT_WEIGHTS),
142 | },
143 | letterSpacing: {
144 | value: getNumber(letterSpacing) || 0,
145 | unit: 'PIXELS',
146 | },
147 | textDecoration: transTextDecoration(textDecorationLine)
148 | },
149 | } as TextSegStyle];
150 |
151 | // 统一文字颜色和背景色的处理
152 | styles.backgroundColor = color;
153 | const shape = transShape(result.characters || '文字', styles, parentStyles, 'TEXT');
154 | Object.assign(result, shape);
155 |
156 | return result;
157 | };
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import Antd from 'ant-design-vue'
3 | import 'ant-design-vue/dist/antd.css';
4 | import App from './App.vue'
5 |
6 | const app = createApp(App)
7 |
8 | app.use(Antd)
9 | app.mount('#app')
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "moduleResolution": "Node",
7 | "strict": true,
8 | "jsx": "preserve",
9 | "sourceMap": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "esModuleInterop": true,
13 | "lib": [
14 | "ESNext",
15 | "DOM"
16 | ],
17 | "skipLibCheck": true,
18 | "types": [
19 | "@mastergo/plugin-typings",
20 | "@types/node",
21 | ]
22 | },
23 | "include": [
24 | "src/**/*.d.ts",
25 | "src/**/*.ts",
26 | "src/**/*.tsx",
27 | "src/**/*.vue",
28 | ],
29 | "references": [
30 | {
31 | "path": "./tsconfig.node.json"
32 | }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig, LibraryOptions, BuildOptions } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 | import { resolve } from 'path'
4 | import dts from 'vite-plugin-dts'
5 |
6 | const TARGET = process.env.TARGET
7 |
8 | // https://vitejs.dev/config/
9 | export default defineConfig(() => {
10 | const libConfig = TARGET === 'lib'? {
11 | outDir: 'lib',
12 | lib: {
13 | entry: resolve(__dirname, './src/lib/index.ts'),
14 | name: 'sdk',
15 | formats: ['umd', 'cjs', 'es'],
16 | fileName: 'index'
17 | } as LibraryOptions,
18 | }: {} as BuildOptions
19 |
20 | return {
21 | publicDir: TARGET === 'lib'? false: 'public',
22 | plugins: [
23 | dts({
24 | include: ['./src/lib/index.d.ts']
25 | }),
26 | vue(),
27 | ],
28 | build: {
29 | ...libConfig,
30 | } as BuildOptions,
31 | }
32 | })
33 |
--------------------------------------------------------------------------------