├── .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 | 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 | 13 | 14 | 144 | 145 | 152 | -------------------------------------------------------------------------------- /src/example/html-mg/ui/component/breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /src/example/html-mg/ui/component/calendar.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/example/html-mg/ui/component/image.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 17 | 18 | 46 | -------------------------------------------------------------------------------- /src/example/html-mg/ui/component/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 32 | -------------------------------------------------------------------------------- /src/example/html-mg/ui/component/menu.vue: -------------------------------------------------------------------------------- 1 | 51 | 60 | 61 | -------------------------------------------------------------------------------- /src/example/html-mg/ui/component/pageHeader.vue: -------------------------------------------------------------------------------- 1 | 27 | 30 | 31 | -------------------------------------------------------------------------------- /src/example/html-mg/ui/component/table.vue: -------------------------------------------------------------------------------- 1 | 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 | --------------------------------------------------------------------------------