From dcb2e91a9c31dab3a4c5ad4e229c53f5f98feca3 Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Thu, 24 Oct 2019 21:23:15 +0800 Subject: [PATCH] feat(component): add scale ,zoom, popup, marker map method --- .storybook/config.ts | 1 - .storybook/l7.css | 397 +++++++++++++++++ .storybook/webpack.config.js | 8 +- babel.config.js | 2 +- package-lock.json | 231 ++++++++++ package.json | 8 +- packages/component/.gitignore | 3 + packages/component/package.json | 33 ++ packages/component/src/control/BaseControl.ts | 96 +++++ packages/component/src/control/layer.ts | 47 ++ packages/component/src/control/scale.ts | 104 +++++ packages/component/src/control/zoom.ts | 111 +++++ packages/component/src/css/l7.css | 397 +++++++++++++++++ packages/component/src/index.ts | 6 + packages/component/src/marker.ts | 186 ++++++++ packages/component/src/popup.ts | 210 +++++++++ packages/component/src/utils/anchor.ts | 36 ++ packages/component/tsconfig.build.json | 9 + packages/core/package.json | 4 +- packages/core/src/index.ts | 4 + packages/core/src/inversify.config.ts | 13 +- .../core/src/services/asset/FontService.ts | 243 +++++++++++ .../core/src/services/asset/IFontService.ts | 48 +++ .../core/src/services/asset/IIconService.ts | 4 + .../core/src/services/asset/IconService.ts | 68 +-- .../src/services/component/ControlService.ts | 75 ++++ .../src/services/component/IControlService.ts | 33 ++ .../src/services/component/IMarkerService.ts | 13 + .../src/services/component/IPopUpService.ts | 12 + packages/core/src/services/map/IMapService.ts | 30 ++ .../core/src/services/scene/ISceneService.ts | 5 + .../core/src/services/scene/SceneService.ts | 29 +- packages/core/src/types.ts | 2 + packages/core/src/utils/dom.ts | 2 + packages/core/src/utils/font_util.ts | 53 ++- packages/core/typings/index.d.ts | 4 +- packages/layers/src/core/BaseLayer.ts | 17 + packages/layers/src/point/point.ts | 135 +++--- .../layers/src/point/shaders/image_frag.glsl | 4 +- .../layers/src/point/shaders/text_frag.glsl | 27 ++ .../layers/src/point/shaders/text_vert.glsl | 33 ++ packages/maps/package.json | 1 + packages/maps/src/amap/index.ts | 57 ++- packages/maps/src/mapbox/index.ts | 95 ++++- packages/maps/typings/index.d.ts | 19 +- packages/scene/package.json | 1 + packages/scene/src/index.ts | 67 ++- packages/source/package.json | 2 + packages/source/src/index.ts | 2 + packages/source/src/source.ts | 1 - packages/source/src/transform/grid.ts | 9 +- packages/source/src/transform/hexagon.ts | 60 +++ packages/source/src/transform/statistics.ts | 6 + packages/utils/src/dom.ts | 143 +++++++ packages/utils/src/event.ts | 8 + packages/utils/src/geo.ts | 49 ++- packages/utils/src/index.ts | 4 + packages/utils/src/lru_cache.ts | 86 ++++ stories/MapAdaptor/components/css/l7.css | 400 ++++++++++++++++++ .../MapAdaptor/components/images/layers.svg | 1 + stories/MapAdaptor/components/pointImage.tsx | 47 +- yarn.lock | 299 +++++++++++-- 62 files changed, 3895 insertions(+), 205 deletions(-) create mode 100644 .storybook/l7.css create mode 100644 packages/component/.gitignore create mode 100644 packages/component/package.json create mode 100644 packages/component/src/control/BaseControl.ts create mode 100644 packages/component/src/control/layer.ts create mode 100644 packages/component/src/control/scale.ts create mode 100644 packages/component/src/control/zoom.ts create mode 100644 packages/component/src/css/l7.css create mode 100644 packages/component/src/index.ts create mode 100644 packages/component/src/marker.ts create mode 100644 packages/component/src/popup.ts create mode 100644 packages/component/src/utils/anchor.ts create mode 100644 packages/component/tsconfig.build.json create mode 100644 packages/core/src/services/asset/FontService.ts create mode 100644 packages/core/src/services/asset/IFontService.ts create mode 100644 packages/core/src/services/component/ControlService.ts create mode 100644 packages/core/src/services/component/IControlService.ts create mode 100644 packages/core/src/services/component/IMarkerService.ts create mode 100644 packages/core/src/services/component/IPopUpService.ts create mode 100644 packages/layers/src/point/shaders/text_frag.glsl create mode 100644 packages/layers/src/point/shaders/text_vert.glsl create mode 100644 packages/source/src/transform/hexagon.ts create mode 100644 packages/utils/src/dom.ts create mode 100644 packages/utils/src/event.ts create mode 100644 packages/utils/src/lru_cache.ts create mode 100644 stories/MapAdaptor/components/css/l7.css create mode 100644 stories/MapAdaptor/components/images/layers.svg diff --git a/.storybook/config.ts b/.storybook/config.ts index d7a9af2d7c..f16ac5f448 100644 --- a/.storybook/config.ts +++ b/.storybook/config.ts @@ -2,7 +2,6 @@ import { configure, addParameters } from '@storybook/react'; import '@storybook/addon-console'; import { create } from '@storybook/theming'; import '!style-loader!css-loader!sass-loader!./iframe.scss'; - addParameters({ options: { isFullscreen: false, diff --git a/.storybook/l7.css b/.storybook/l7.css new file mode 100644 index 0000000000..329ae393df --- /dev/null +++ b/.storybook/l7.css @@ -0,0 +1,397 @@ +.l7-marker { + position: absolute !important; + top: 0; + left: 0; + z-index: 5; +} +.l7-popup-anchor-top, +.l7-popup-anchor-top-left, +.l7-popup-anchor-top-right { + -webkit-flex-direction: column; + flex-direction: column; +} + +.l7-popup-anchor-bottom, +.l7-popup-anchor-bottom-left, +.l7-popup-anchor-bottom-right { + -webkit-flex-direction: column-reverse; + flex-direction: column-reverse; +} + +.l7-popup-anchor-left { + -webkit-flex-direction: row; + flex-direction: row; +} + +.l7-popup-anchor-right { + -webkit-flex-direction: row-reverse; + flex-direction: row-reverse; +} +.l7-popup { + position: absolute; + top: 0; + left: 0; + display: -webkit-flex; + display: flex; + will-change: transform; + pointer-events: none; + z-index: 5; +} +.l7-popup-tip { + width: 0; + height: 0; + border: 10px solid transparent; + z-index: 1; +} +.l7-popup-anchor-top .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-top: none; + border-bottom-color: #fff; +} + +.l7-popup-anchor-top-left .l7-popup-tip { + -webkit-align-self: flex-start; + align-self: flex-start; + border-top: none; + border-left: none; + border-bottom-color: #fff; +} + +.l7-popup-anchor-top-right .l7-popup-tip { + -webkit-align-self: flex-end; + align-self: flex-end; + border-top: none; + border-right: none; + border-bottom-color: #fff; +} + +.l7-popup-anchor-bottom .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-bottom: none; + border-top-color: #fff; +} + +.l7-popup-anchor-bottom-left .l7-popup-tip { + -webkit-align-self: flex-start; + align-self: flex-start; + border-bottom: none; + border-left: none; + border-top-color: #fff; +} + +.l7-popup-anchor-bottom-right .l7-popup-tip { + -webkit-align-self: flex-end; + align-self: flex-end; + border-bottom: none; + border-right: none; + border-top-color: #fff; +} + +.l7-popup-anchor-left .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-left: none; + border-right-color: #fff; +} + +.l7-popup-anchor-right .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-right: none; + border-left-color: #fff; +} + +.l7-popup-close-button { + position: absolute; + right: 0; + top: 0; + border: 0; + padding: 0; + font-size: 25px; + line-height: 20px; + border-radius: 0 3px 0 0; + cursor: pointer; + background-color: transparent; +} + +.l7-popup-close-button:hover { + background-color: rgba(0, 0, 0, 0.05); +} + +.l7-popup-content { + position: relative; + background: #fff; + border-radius: 3px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + padding: 10px 10px 15px; + pointer-events: auto; +} + +.l7-popup-anchor-top-left .l7-popup-content { + border-top-left-radius: 0; +} + +.l7-popup-anchor-top-right .l7-popup-content { + border-top-right-radius: 0; +} + +.l7-popup-anchor-bottom-left .l7-popup-content { + border-bottom-left-radius: 0; +} + +.l7-popup-anchor-bottom-right .l7-popup-content { + border-bottom-right-radius: 0; +} + +.l7-popup-track-pointer { + display: none; +} + +.l7-popup-track-pointer * { + pointer-events: none; + user-select: none; +} + +.l7-map:hover .l7-popup-track-pointer { + display: flex; +} + +.l7-map:active .l7-popup-track-pointer { + display: none; +} + +.l7-popup-close-button { + position: absolute; + right: 0; + top: 0; + border: 0; + border-radius: 0 3px 0 0; + cursor: pointer; + background-color: transparent; +} + +.l7-popup-close-button:hover { + background-color: rgba(0, 0, 0, 0.05); +} + +.l7-popup-content { + position: relative; + background: #fff; + border-radius: 3px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + padding: 10px 10px 15px; + pointer-events: auto; +} + +/* general toolbar styles */ + +.l7-bar { + box-shadow: 0 1px 5px rgba(0,0,0,0.65); + border-radius: 4px; + } +.l7-bar a, +.l7-bar a:hover { + background-color: #fff; + border-bottom: 1px solid #ccc; + width: 26px; + height: 26px; + line-height: 26px; + display: block; + text-align: center; + text-decoration: none; + color: black; + } +.l7-bar a, +.l7-control-layers-toggle { + background-position: 50% 50%; + background-repeat: no-repeat; + display: block; + } +.l7-bar a:hover { + background-color: #f4f4f4; + } +.l7-bar a:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } +.l7-bar a:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom: none; + } +.l7-bar a.l7-disabled { + cursor: default; + background-color: #f4f4f4; + color: #bbb; + } + + +/* control positioning */ + +.l7-control-container { + font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; +} +.l7-control-hide { + display: none; +} +.l7-control { + position: relative; + z-index: 800; + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ + pointer-events: auto; + } +.l7-top, +.l7-bottom { + position: absolute; + z-index: 1000; + pointer-events: none; + } +.l7-top { + top: 0; + } +.l7-right { + right: 0; + } +.l7-bottom { + bottom: 0; + } +.l7-left { + left: 0; + } +.l7-control { + float: left; + clear: both; + } +.l7-right .l7-control { + float: right; + } +.l7-top .l7-control { + margin-top: 10px; + } +.l7-bottom .l7-control { + margin-bottom: 10px; + } +.l7-left .l7-control { + margin-left: 10px; + } +.l7-right .l7-control { + margin-right: 10px; + } + + /* attribution and scale controls */ + +.l7-control-container .l7-control-attribution { + background: #fff; + background: rgba(255, 255, 255, 0.7); + margin: 0; + } +.l7-control-attribution, +.l7-control-scale-line { + padding: 0 5px; + color: #333; + } +.l7-control-attribution a { + text-decoration: none; + } +.l7-control-attribution a:hover { + text-decoration: underline; + } +.l7-container .l7-control-attribution, +.l7-container .l7-control-scale { + font-size: 11px; + } +.l7-left .l7-control-scale { + margin-left: 5px; + } +.l7-bottom .l7-control-scale { + margin-bottom: 5px; + } +.l7-control-scale-line { + border: 2px solid #777; + border-top: none; + line-height: 1.1; + padding: 2px 5px 1px; + font-size: 11px; + white-space: nowrap; + overflow: hidden; + -moz-box-sizing: border-box; + box-sizing: border-box; + + background: #fff; + background: rgba(255, 255, 255, 0.5); + } +.l7-control-scale-line:not(:first-child) { + border-top: 2px solid #777; + border-bottom: none; + margin-top: -2px; + } +.l7-control-scale-line:not(:first-child):not(:last-child) { + border-bottom: 2px solid #777; + } + +.l7-touch .l7-control-attribution, +.l7-touch .l7-control-layers, +.l7-touch .l7-bar { + box-shadow: none; + } +.l7-touch .l7-control-layers, +.l7-touch .l7-bar { + border: 2px solid rgba(0,0,0,0.2); + background-clip: padding-box; + } + + + /* layers control */ + +.l7-control-layers { + box-shadow: 0 1px 5px rgba(0,0,0,0.4); + background: #fff; + border-radius: 5px; + } +.l7-control-layers-toggle { + background-image: url(../images/layers.svg); + width: 36px; + height: 36px; + } +.l7-retina .l7-control-layers-toggle { + background-image: url(../images/layers.svg); + background-size: 26px 26px; + } +.l7-touch .l7-control-layers-toggle { + width: 44px; + height: 44px; + } +.l7-control-layers .l7-control-layers-list, +.l7-control-layers-expanded .l7-control-layers-toggle { + display: none; + } +.l7-control-layers-expanded .l7-control-layers-list { + display: block; + position: relative; + } +.l7-control-layers-expanded { + padding: 6px 10px 6px 6px; + color: #333; + background: #fff; + } +.l7-control-layers-scrollbar { + overflow-y: scroll; + overflow-x: hidden; + padding-right: 5px; + } +.l7-control-layers-selector { + margin-top: 2px; + position: relative; + top: 1px; + } +.l7-control-layers label { + display: block; + } +.l7-control-layers-separator { + height: 0; + border-top: 1px solid #ddd; + margin: 5px -10px 5px -6px; + } + diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js index 389897c06c..2d3b00e6e0 100644 --- a/.storybook/webpack.config.js +++ b/.storybook/webpack.config.js @@ -27,9 +27,13 @@ module.exports = ({ config }) => { }, ], enforce: 'pre', - }); + },{ + test: /\.stories\.css?$/, + use: ['style-loader', 'css-loader'], + }, + ); config.resolve.extensions.push('.ts', '.tsx', '.js', '.glsl'); return config; -}; \ No newline at end of file +}; diff --git a/babel.config.js b/babel.config.js index 88a51c171b..1375b8ad63 100644 --- a/babel.config.js +++ b/babel.config.js @@ -77,4 +77,4 @@ module.exports = (api) => { }, ignore: ['node_modules'], }; -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index d41df644ce..1fc576f4f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5524,6 +5524,16 @@ "@babel/plugin-syntax-typescript": "^7.3.3" } }, + "babel-plugin-css-modules-transform": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-css-modules-transform/-/babel-plugin-css-modules-transform-1.6.2.tgz", + "integrity": "sha512-zBsI54N5n979vfYpqFzQ6oRwEiVcmLH5REyaincNW+Ecl52nvRsQPYIbDcJzHePrXI20YSRUw6G/qbPwZZDgfg==", + "dev": true, + "requires": { + "css-modules-require-hook": "^4.0.6", + "mkdirp": "^0.5.1" + } + }, "babel-plugin-dynamic-import-node": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", @@ -8289,6 +8299,84 @@ } } }, + "css-modules-require-hook": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/css-modules-require-hook/-/css-modules-require-hook-4.2.3.tgz", + "integrity": "sha1-Z5LKQSsV4j5vm+agfc739Xf/kE0=", + "dev": true, + "requires": { + "debug": "^2.2.0", + "generic-names": "^1.0.1", + "glob-to-regexp": "^0.3.0", + "icss-replace-symbols": "^1.0.2", + "lodash": "^4.3.0", + "postcss": "^6.0.1", + "postcss-modules-extract-imports": "^1.0.0", + "postcss-modules-local-by-default": "^1.0.1", + "postcss-modules-resolve-imports": "^1.3.0", + "postcss-modules-scope": "^1.0.0", + "postcss-modules-values": "^1.1.1", + "seekout": "^1.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "postcss-modules-extract-imports": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", + "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==", + "dev": true, + "requires": { + "postcss": "^6.0.1" + } + }, + "postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dev": true, + "requires": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "css-select": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", @@ -8307,6 +8395,57 @@ "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", "dev": true }, + "css-selector-tokenizer": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", + "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", + "dev": true, + "requires": { + "cssesc": "^0.1.0", + "fastparse": "^1.1.1", + "regexpu-core": "^1.0.0" + }, + "dependencies": { + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "dev": true + }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + }, + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + } + } + }, "css-to-react-native": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.3.2.tgz", @@ -9927,6 +10066,12 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, "fastq": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz", @@ -11038,6 +11183,41 @@ "globule": "^1.0.0" } }, + "generic-names": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-1.0.3.tgz", + "integrity": "sha1-LXhqEhruUIh2eWk56OO/+DbCCRc=", + "dev": true, + "requires": { + "loader-utils": "^0.2.16" + }, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + } + } + }, "genfun": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", @@ -16836,6 +17016,51 @@ } } }, + "postcss-modules-resolve-imports": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-resolve-imports/-/postcss-modules-resolve-imports-1.3.0.tgz", + "integrity": "sha1-OY0wALla6WlCDN9M2D+oBn8cXq4=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "icss-utils": "^3.0.1", + "minimist": "^1.2.0" + }, + "dependencies": { + "icss-utils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-3.0.1.tgz", + "integrity": "sha1-7nDTroysOMa+XtkehRsn7tNDrQ8=", + "dev": true, + "requires": { + "postcss": "^6.0.2" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "postcss-modules-scope": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz", @@ -18969,6 +19194,12 @@ } } }, + "seekout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/seekout/-/seekout-1.0.2.tgz", + "integrity": "sha1-CbqfG9W0b7sTRxjrGaaDgsuxuck=", + "dev": true + }, "select": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", diff --git a/package.json b/package.json index 087b86a377..9fbc63461e 100644 --- a/package.json +++ b/package.json @@ -27,11 +27,13 @@ "babel-jest": "^24.8.0", "babel-loader": "^8.0.6", "babel-plugin-const-enum": "^0.0.2", + "babel-plugin-css-modules-transform": "^1.6.2", "babel-plugin-inline-import": "^3.0.0", + "babel-plugin-transform-postcss": "^0.3.0", "clean-webpack-plugin": "^0.1.19", "commitizen": "^4.0.3", "copy-webpack-plugin": "^4.5.2", - "css-loader": "^3.0.0", + "css-loader": "^3.2.0", "cz-conventional-changelog": "^3.0.2", "dat.gui": "^0.7.2", "enzyme": "^3.6.0", @@ -45,6 +47,8 @@ "lint-staged": "^9.2.4", "node-sass": "^4.12.0", "npm-run-all": "^4.1.5", + "postcss": "^7.0.18", + "postcss-plugin": "^1.0.0", "prettier": "^1.14.2", "raw-loader": "^1.0.0", "react": "^16.8.6", @@ -52,7 +56,7 @@ "react-dom": "^16.8.6", "rimraf": "^2.6.2", "sass-loader": "^7.1.0", - "style-loader": "^0.23.1", + "style-loader": "^1.0.0", "styled-components": "^3.4.6", "stylelint": "^9.5.0", "stylelint-config-recommended": "^2.1.0", diff --git a/packages/component/.gitignore b/packages/component/.gitignore new file mode 100644 index 0000000000..ffe6b3e902 --- /dev/null +++ b/packages/component/.gitignore @@ -0,0 +1,3 @@ +lib +esm +dist diff --git a/packages/component/package.json b/packages/component/package.json new file mode 100644 index 0000000000..f787d917e7 --- /dev/null +++ b/packages/component/package.json @@ -0,0 +1,33 @@ +{ + "name": "@l7/component", + "version": "0.0.1", + "description": "", + "main": "/dist/index.js", + "module": "esm/index.js", + "types": "esm/index.d.ts", + "sideEffects": true, + "files": [ + "lib", + "esm", + "README.md" + ], + "scripts": { + "tsc": "tsc --project tsconfig.build.json", + "build": "BABEL_ENV=build babel src --root-mode upward --out-dir dist --source-maps --extensions .ts,.tsx --delete-dir-on-start --no-comments", + "watch": "BABEL_ENV=build babel src --watch --root-mode upward --out-dir dist --source-maps --extensions .ts,.tsx --delete-dir-on-start --no-comments", + "lint:ts": "run-p -c lint:ts-*", + "test": "jest" + }, + "author": "lzxue", + "license": "ISC", + "dependencies": { + "@l7/core": "0.0.1", + "@l7/utils": "0.0.1", + "@turf/distance": "^6.0.1", + "eventemitter3": "^3.1.0", + "inversify": "^5.0.1", + "inversify-inject-decorators": "^3.1.0", + "inversify-logging": "^0.2.1" + }, + "devDependencies": {} +} diff --git a/packages/component/src/control/BaseControl.ts b/packages/component/src/control/BaseControl.ts new file mode 100644 index 0000000000..17fe8b01cc --- /dev/null +++ b/packages/component/src/control/BaseControl.ts @@ -0,0 +1,96 @@ +import { IControlService, IMapService, lazyInject, TYPES } from '@l7/core'; +import { DOM } from '@l7/utils'; +import { EventEmitter } from 'eventemitter3'; + +export enum PositionType { + 'TOPRIGHT' = 'topright', + 'TOPLEFT' = 'topleft', + 'BOTTOMRIGHT' = 'bottomright', + 'BOTTOMLEFT' = 'bottomleft', +} +export type PositionName = + | 'topright' + | 'topleft' + | 'bottomright' + | 'bottomleft'; +export interface IControlOption { + position: PositionName; + [key: string]: any; +} +export default class Control extends EventEmitter { + public controlOption: IControlOption; + protected mapsService: IMapService; + @lazyInject(TYPES.IControlService) + private readonly controlService: IControlService; + private container: HTMLElement; + private isShow: boolean; + + constructor(cfg?: Partial) { + super(); + this.controlOption = { + ...this.getDefault(), + ...(cfg || {}), + }; + } + public getDefault() { + return { + position: PositionType.TOPRIGHT, + }; + } + public setPosition(position: PositionName) { + const controlService = this.controlService; + if (controlService) { + controlService.removeControl(this); + } + this.controlOption.position = position; + if (controlService) { + controlService.addControl(this, this.mapsService); + } + return this; + } + public addTo(mapService: IMapService) { + this.remove(); + this.isShow = true; + this.mapsService = mapService; + this.container = this.onAdd(mapService); + const container = this.container; + const pos = this.controlOption.position; + const corner = this.controlService.controlCorners[pos]; + DOM.addClass(container, 'l7-control'); + + if (pos.indexOf('bottom') !== -1) { + corner.insertBefore(container, corner.firstChild); + } else { + corner.appendChild(container); + } + return this; + } + public onAdd(Map: IMapService): HTMLElement { + throw new Error('Method not implemented.'); + } + public hide() { + const container = this.container; + DOM.addClass(container, 'l7-control-hide'); + this.isShow = false; + } + public show() { + const container = this.container; + DOM.removeClass(container, 'l7-control-hide'); + this.isShow = true; + } + public remove() { + if (!this.mapsService) { + return this; + } + DOM.remove(this.container); + } + public _refocusOnMap(e: MouseEvent) { + // if map exists and event is not a keyboard event + if (this.mapsService && e && e.screenX > 0 && e.screenY > 0) { + const container = this.mapsService.getContainer(); + if (container !== null) { + container.focus(); + } + } + } +} diff --git a/packages/component/src/control/layer.ts b/packages/component/src/control/layer.ts new file mode 100644 index 0000000000..ff8b233c95 --- /dev/null +++ b/packages/component/src/control/layer.ts @@ -0,0 +1,47 @@ +import { IMapService } from '@l7/core'; +import { bindAll, DOM, lnglatDistance } from '@l7/utils'; +import Control, { + IControlOption, + PositionName, + PositionType, +} from './BaseControl'; +export interface ILayerControlOption extends IControlOption { + collapsed: boolean; + autoZIndex: boolean; + hideSingleBase: boolean; + sortLayers: boolean; +} +export default class Layers extends Control { + private layerControlInputs: any[]; + private layers: any[]; + private lastZIndex: number; + private handlingClick: boolean; + + constructor(cfg: Partial) { + super(cfg); + this.layerControlInputs = []; + this.layers = []; + this.lastZIndex = 0; + this.handlingClick = false; + // const baseLayers = this.get('baseLayers'); + // const overlays = this.get('overlayers'); + // for (const i in baseLayers) { + // this._addLayer(baseLayers[i], i); + // } + + // for (const i in overlays) { + // this._addLayer(overlays[i], i, true); + // } + // bindAll([ '_checkDisabledLayers', '_onLayerChange', 'collapse', 'extend', 'expand', '_onInputClick' ], this); + } + + public getDefault() { + return { + collapsed: true, + position: PositionType.TOPRIGHT, + autoZIndex: true, + hideSingleBase: false, + sortLayers: false, + }; + } +} diff --git a/packages/component/src/control/scale.ts b/packages/component/src/control/scale.ts new file mode 100644 index 0000000000..345f1ae220 --- /dev/null +++ b/packages/component/src/control/scale.ts @@ -0,0 +1,104 @@ +import { IMapService } from '@l7/core'; +import { bindAll, DOM, lnglatDistance } from '@l7/utils'; +import Control, { IControlOption, PositionType } from './BaseControl'; +export interface IScaleControlOption extends IControlOption { + maxWidth: number; + metric: boolean; + updateWhenIdle: boolean; + imperial: boolean; +} +export default class Scale extends Control { + private mScale: HTMLElement; + private iScale: HTMLElement; + constructor(cfg?: Partial) { + super(cfg); + bindAll(['update'], this); + } + + public getDefault() { + return { + position: PositionType.BOTTOMLEFT, + maxWidth: 100, + metric: true, + updateWhenIdle: false, + imperial: false, + }; + } + + public onAdd(MapService: IMapService) { + const className = 'l7-control-scale'; + const container = DOM.create('div', className); + this.addScales(className + '-line', container); + const { updateWhenIdle } = this.controlOption; + // TODO: 高德地图和MapBox地图事件不一致问题 + this.mapsService.on(updateWhenIdle ? 'moveend' : 'mapmove', this.update); + this.update(); + + return container; + } + public onRemove(MapService: IMapService) { + const { updateWhenIdle } = this.controlOption; + this.mapsService.off(updateWhenIdle ? 'moveend' : 'mapmove', this.update); + } + public update() { + const mapsService = this.mapsService; + const { maxWidth } = this.controlOption; + const y = mapsService.getSize()[1] / 2; + + const p1 = mapsService.containerToLngLat([0, y]); + const p2 = mapsService.containerToLngLat([maxWidth, y]); + const maxMeters = lnglatDistance([p1.lng, p1.lat], [p2.lng, p2.lat]); + this.updateScales(maxMeters); + } + public updateScales(maxMeters: number) { + const { metric, imperial } = this.controlOption; + if (metric && maxMeters) { + this.updateMetric(maxMeters); + } + if (imperial && maxMeters) { + this.updateImperial(maxMeters); + } + } + private updateMetric(maxMeters: number) { + const meters = this.getRoundNum(maxMeters); + const label = meters < 1000 ? meters + ' m' : meters / 1000 + ' km'; + this.updateScale(this.mScale, label, meters / maxMeters); + } + private updateImperial(maxMeters: number) { + const maxFeet = maxMeters * 3.2808399; + let maxMiles: number; + let miles: number; + let feet: number; + + if (maxFeet > 5280) { + maxMiles = maxFeet / 5280; + miles = this.getRoundNum(maxMiles); + this.updateScale(this.iScale, miles + ' mi', miles / maxMiles); + } else { + feet = this.getRoundNum(maxFeet); + this.updateScale(this.iScale, feet + ' ft', feet / maxFeet); + } + } + private updateScale(scale: HTMLElement, text: string, ratio: number) { + const { maxWidth } = this.controlOption; + scale.style.width = Math.round(maxWidth * ratio) + 'px'; + scale.innerHTML = text; + } + private getRoundNum(num: number) { + const pow10 = Math.pow(10, (Math.floor(num) + '').length - 1); + let d = num / pow10; + + d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 3 ? 3 : d >= 2 ? 2 : 1; + + return pow10 * d; + } + private addScales(className: string, container: HTMLElement) { + const { metric, imperial } = this.controlOption; + if (metric) { + this.mScale = DOM.create('div', className, container); + } + if (imperial) { + this.iScale = DOM.create('div', className, container); + } + } +} diff --git a/packages/component/src/control/zoom.ts b/packages/component/src/control/zoom.ts new file mode 100644 index 0000000000..d5b64c8136 --- /dev/null +++ b/packages/component/src/control/zoom.ts @@ -0,0 +1,111 @@ +import { IMapService } from '@l7/core'; +import { bindAll, DOM } from '@l7/utils'; +import Control, { IControlOption, PositionType } from './BaseControl'; +export interface IZoomControlOption extends IControlOption { + zoomInText: string; + zoomInTitle: string; + zoomOutText: string; + zoomOutTitle: string; +} +export default class Zoom extends Control { + private disabled: boolean; + private zoomInButton: HTMLElement; + private zoomOutButton: HTMLElement; + + constructor(cfg?: Partial) { + super(cfg); + bindAll(['updateDisabled', 'zoomIn', 'zoomOut'], this); + } + public getDefault() { + return { + position: PositionType.TOPLEFT, + zoomInText: '+', + zoomInTitle: 'Zoom in', + zoomOutText: '−', + zoomOutTitle: 'Zoom out', + }; + } + + public onAdd(MapService: IMapService) { + const zoomName = 'l7-control-zoom'; + const container = DOM.create('div', zoomName + ' l7-bar'); + + this.zoomInButton = this.createButton( + this.controlOption.zoomInText, + this.controlOption.zoomInTitle, + zoomName + '-in', + container, + this.zoomIn, + ); + this.zoomOutButton = this.createButton( + this.controlOption.zoomOutText, + this.controlOption.zoomOutTitle, + zoomName + '-out', + container, + this.zoomOut, + ); + this.mapsService.on('zoomend', this.updateDisabled); + this.mapsService.on('zoomchange', this.updateDisabled); + this.updateDisabled(); + return container; + } + + public onRemove() { + this.mapsService.off('zoomend', this.updateDisabled); + this.mapsService.off('zoomchange', this.updateDisabled); + } + + public disable() { + this.disabled = true; + this.updateDisabled(); + return this; + } + + public enable() { + this.disabled = false; + this.updateDisabled(); + return this; + } + + private zoomIn() { + if ( + !this.disabled && + this.mapsService.getZoom() < this.mapsService.getMaxZoom() + ) { + this.mapsService.zoomIn(); + } + } + private zoomOut() { + if ( + !this.disabled && + this.mapsService.getZoom() > this.mapsService.getMinZoom() + ) { + this.mapsService.zoomOut(); + } + } + private createButton( + html: string, + tile: string, + className: string, + container: HTMLElement, + fn: (...arg: any[]) => any, + ) { + const link = DOM.create('a', className, container) as HTMLLinkElement; + link.innerHTML = html; + link.href = '#'; + link.addEventListener('click', fn); + return link; + } + private updateDisabled() { + const mapsService = this.mapsService; + const className = 'l7-disabled'; + DOM.removeClass(this.zoomInButton, className); + DOM.removeClass(this.zoomOutButton, className); + if (this.disabled || mapsService.getZoom() <= mapsService.getMinZoom()) { + DOM.addClass(this.zoomOutButton, className); + } + if (this.disabled || mapsService.getZoom() >= mapsService.getMaxZoom()) { + DOM.addClass(this.zoomInButton, className); + } + } +} diff --git a/packages/component/src/css/l7.css b/packages/component/src/css/l7.css new file mode 100644 index 0000000000..329ae393df --- /dev/null +++ b/packages/component/src/css/l7.css @@ -0,0 +1,397 @@ +.l7-marker { + position: absolute !important; + top: 0; + left: 0; + z-index: 5; +} +.l7-popup-anchor-top, +.l7-popup-anchor-top-left, +.l7-popup-anchor-top-right { + -webkit-flex-direction: column; + flex-direction: column; +} + +.l7-popup-anchor-bottom, +.l7-popup-anchor-bottom-left, +.l7-popup-anchor-bottom-right { + -webkit-flex-direction: column-reverse; + flex-direction: column-reverse; +} + +.l7-popup-anchor-left { + -webkit-flex-direction: row; + flex-direction: row; +} + +.l7-popup-anchor-right { + -webkit-flex-direction: row-reverse; + flex-direction: row-reverse; +} +.l7-popup { + position: absolute; + top: 0; + left: 0; + display: -webkit-flex; + display: flex; + will-change: transform; + pointer-events: none; + z-index: 5; +} +.l7-popup-tip { + width: 0; + height: 0; + border: 10px solid transparent; + z-index: 1; +} +.l7-popup-anchor-top .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-top: none; + border-bottom-color: #fff; +} + +.l7-popup-anchor-top-left .l7-popup-tip { + -webkit-align-self: flex-start; + align-self: flex-start; + border-top: none; + border-left: none; + border-bottom-color: #fff; +} + +.l7-popup-anchor-top-right .l7-popup-tip { + -webkit-align-self: flex-end; + align-self: flex-end; + border-top: none; + border-right: none; + border-bottom-color: #fff; +} + +.l7-popup-anchor-bottom .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-bottom: none; + border-top-color: #fff; +} + +.l7-popup-anchor-bottom-left .l7-popup-tip { + -webkit-align-self: flex-start; + align-self: flex-start; + border-bottom: none; + border-left: none; + border-top-color: #fff; +} + +.l7-popup-anchor-bottom-right .l7-popup-tip { + -webkit-align-self: flex-end; + align-self: flex-end; + border-bottom: none; + border-right: none; + border-top-color: #fff; +} + +.l7-popup-anchor-left .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-left: none; + border-right-color: #fff; +} + +.l7-popup-anchor-right .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-right: none; + border-left-color: #fff; +} + +.l7-popup-close-button { + position: absolute; + right: 0; + top: 0; + border: 0; + padding: 0; + font-size: 25px; + line-height: 20px; + border-radius: 0 3px 0 0; + cursor: pointer; + background-color: transparent; +} + +.l7-popup-close-button:hover { + background-color: rgba(0, 0, 0, 0.05); +} + +.l7-popup-content { + position: relative; + background: #fff; + border-radius: 3px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + padding: 10px 10px 15px; + pointer-events: auto; +} + +.l7-popup-anchor-top-left .l7-popup-content { + border-top-left-radius: 0; +} + +.l7-popup-anchor-top-right .l7-popup-content { + border-top-right-radius: 0; +} + +.l7-popup-anchor-bottom-left .l7-popup-content { + border-bottom-left-radius: 0; +} + +.l7-popup-anchor-bottom-right .l7-popup-content { + border-bottom-right-radius: 0; +} + +.l7-popup-track-pointer { + display: none; +} + +.l7-popup-track-pointer * { + pointer-events: none; + user-select: none; +} + +.l7-map:hover .l7-popup-track-pointer { + display: flex; +} + +.l7-map:active .l7-popup-track-pointer { + display: none; +} + +.l7-popup-close-button { + position: absolute; + right: 0; + top: 0; + border: 0; + border-radius: 0 3px 0 0; + cursor: pointer; + background-color: transparent; +} + +.l7-popup-close-button:hover { + background-color: rgba(0, 0, 0, 0.05); +} + +.l7-popup-content { + position: relative; + background: #fff; + border-radius: 3px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + padding: 10px 10px 15px; + pointer-events: auto; +} + +/* general toolbar styles */ + +.l7-bar { + box-shadow: 0 1px 5px rgba(0,0,0,0.65); + border-radius: 4px; + } +.l7-bar a, +.l7-bar a:hover { + background-color: #fff; + border-bottom: 1px solid #ccc; + width: 26px; + height: 26px; + line-height: 26px; + display: block; + text-align: center; + text-decoration: none; + color: black; + } +.l7-bar a, +.l7-control-layers-toggle { + background-position: 50% 50%; + background-repeat: no-repeat; + display: block; + } +.l7-bar a:hover { + background-color: #f4f4f4; + } +.l7-bar a:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } +.l7-bar a:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom: none; + } +.l7-bar a.l7-disabled { + cursor: default; + background-color: #f4f4f4; + color: #bbb; + } + + +/* control positioning */ + +.l7-control-container { + font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; +} +.l7-control-hide { + display: none; +} +.l7-control { + position: relative; + z-index: 800; + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ + pointer-events: auto; + } +.l7-top, +.l7-bottom { + position: absolute; + z-index: 1000; + pointer-events: none; + } +.l7-top { + top: 0; + } +.l7-right { + right: 0; + } +.l7-bottom { + bottom: 0; + } +.l7-left { + left: 0; + } +.l7-control { + float: left; + clear: both; + } +.l7-right .l7-control { + float: right; + } +.l7-top .l7-control { + margin-top: 10px; + } +.l7-bottom .l7-control { + margin-bottom: 10px; + } +.l7-left .l7-control { + margin-left: 10px; + } +.l7-right .l7-control { + margin-right: 10px; + } + + /* attribution and scale controls */ + +.l7-control-container .l7-control-attribution { + background: #fff; + background: rgba(255, 255, 255, 0.7); + margin: 0; + } +.l7-control-attribution, +.l7-control-scale-line { + padding: 0 5px; + color: #333; + } +.l7-control-attribution a { + text-decoration: none; + } +.l7-control-attribution a:hover { + text-decoration: underline; + } +.l7-container .l7-control-attribution, +.l7-container .l7-control-scale { + font-size: 11px; + } +.l7-left .l7-control-scale { + margin-left: 5px; + } +.l7-bottom .l7-control-scale { + margin-bottom: 5px; + } +.l7-control-scale-line { + border: 2px solid #777; + border-top: none; + line-height: 1.1; + padding: 2px 5px 1px; + font-size: 11px; + white-space: nowrap; + overflow: hidden; + -moz-box-sizing: border-box; + box-sizing: border-box; + + background: #fff; + background: rgba(255, 255, 255, 0.5); + } +.l7-control-scale-line:not(:first-child) { + border-top: 2px solid #777; + border-bottom: none; + margin-top: -2px; + } +.l7-control-scale-line:not(:first-child):not(:last-child) { + border-bottom: 2px solid #777; + } + +.l7-touch .l7-control-attribution, +.l7-touch .l7-control-layers, +.l7-touch .l7-bar { + box-shadow: none; + } +.l7-touch .l7-control-layers, +.l7-touch .l7-bar { + border: 2px solid rgba(0,0,0,0.2); + background-clip: padding-box; + } + + + /* layers control */ + +.l7-control-layers { + box-shadow: 0 1px 5px rgba(0,0,0,0.4); + background: #fff; + border-radius: 5px; + } +.l7-control-layers-toggle { + background-image: url(../images/layers.svg); + width: 36px; + height: 36px; + } +.l7-retina .l7-control-layers-toggle { + background-image: url(../images/layers.svg); + background-size: 26px 26px; + } +.l7-touch .l7-control-layers-toggle { + width: 44px; + height: 44px; + } +.l7-control-layers .l7-control-layers-list, +.l7-control-layers-expanded .l7-control-layers-toggle { + display: none; + } +.l7-control-layers-expanded .l7-control-layers-list { + display: block; + position: relative; + } +.l7-control-layers-expanded { + padding: 6px 10px 6px 6px; + color: #333; + background: #fff; + } +.l7-control-layers-scrollbar { + overflow-y: scroll; + overflow-x: hidden; + padding-right: 5px; + } +.l7-control-layers-selector { + margin-top: 2px; + position: relative; + top: 1px; + } +.l7-control-layers label { + display: block; + } +.l7-control-layers-separator { + height: 0; + border-top: 1px solid #ddd; + margin: 5px -10px 5px -6px; + } + diff --git a/packages/component/src/index.ts b/packages/component/src/index.ts new file mode 100644 index 0000000000..b31d6fbd0b --- /dev/null +++ b/packages/component/src/index.ts @@ -0,0 +1,6 @@ +import Control from './control/BaseControl'; +import Scale from './control/scale'; +import Zoom from './control/zoom'; +import Marker from './marker'; +import Popup from './popup'; +export { Control, Scale, Zoom, Marker, Popup }; diff --git a/packages/component/src/marker.ts b/packages/component/src/marker.ts new file mode 100644 index 0000000000..1501072486 --- /dev/null +++ b/packages/component/src/marker.ts @@ -0,0 +1,186 @@ +import { ILngLat, IMapService, IMarkerScene, IPopup } from '@l7/core'; +import { bindAll, DOM } from '@l7/utils'; +import { anchorTranslate, anchorType, applyAnchorClass } from './utils/anchor'; +// marker 支持 dragger 未完成 + +export interface IMarkerOption { + element: HTMLElement | undefined; + anchor: anchorType; + color: string; + offset: number[]; + draggable: boolean; +} +export default class Marker { + private markerOption: IMarkerOption; + private defaultMarker: boolean; + private popup: IPopup; // TODO: POPup + private mapservice: IMapService; + private lngLat: ILngLat; + private scene: IMarkerScene; + constructor(option?: Partial) { + this.markerOption = { + ...this.getDefault(), + ...option, + }; + bindAll(['update', 'onMove', 'onUp', 'addDragHandler', 'onMapClick'], this); + this.init(); + } + + public getDefault() { + return { + element: undefined, // DOM element + anchor: anchorType.BOTTOM, + offset: [0, 0], + color: '#5B8FF9', + draggable: false, + }; + } + + public addTo(scene: IMarkerScene) { + this.scene = scene; + const mapService = scene.getMapService(); + const { element, draggable } = this.markerOption; + this.remove(); + this.mapservice = mapService; + mapService.getMarkerContainer().appendChild(element as HTMLElement); + mapService.on('camerachange', this.update); + this.setDraggable(draggable); + this.update(); + return this; + } + + public remove() { + if (this.mapservice) { + this.mapservice.off('click', this.onMapClick); + this.mapservice.off('move', this.update); + this.mapservice.off('moveend', this.update); + this.mapservice.off('mousedown', this.addDragHandler); + this.mapservice.off('touchstart', this.addDragHandler); + this.mapservice.off('mouseup', this.onUp); + this.mapservice.off('touchend', this.onUp); + delete this.mapservice; + } + const { element } = this.markerOption; + if (element) { + DOM.remove(element); + } + if (this.popup) { + this.popup.remove(); + } + return this; + } + + public setLnglat(lngLat: ILngLat) { + this.lngLat = lngLat; + if (this.popup) { + this.popup.setLnglat(this.lngLat); + } + return this; + } + + public getLnglat(): ILngLat { + return this.lngLat; + } + + public getElement(): HTMLElement { + return this.markerOption.element as HTMLElement; + } + + public togglePopup() { + const popup = this.popup; + if (!popup) { + return this; + } else if (popup.isOpen()) { + popup.remove(); + } else { + popup.addTo(this.scene); + } + return this; + } + + public getPopup() { + return this.popup; + } + + public getOffset(): number[] { + return this.markerOption.offset; + } + + public setDraggable(draggable: boolean) { + throw new Error('Method not implemented.'); + } + + public isDraggable() { + return this.markerOption.draggable; + } + + private update() { + if (!this.mapservice) { + return; + } + const { element, anchor } = this.markerOption; + this.updatePosition(); + DOM.setTransform(element as HTMLElement, `${anchorTranslate[anchor]}`); + } + + private onMapClick(e: MouseEvent) { + const { element } = this.markerOption; + if (this.popup && element) { + this.togglePopup(); + } + } + + private updatePosition() { + if (!this.mapservice) { + return; + } + const { element } = this.markerOption; + const { lng, lat } = this.lngLat; + const pos = this.mapservice.lngLatToContainer([lng, lat]); + if (element) { + element.style.left = pos.x + 'px'; + element.style.top = pos.y + 'px'; + } + } + + private init() { + let { element } = this.markerOption; + const { color, anchor } = this.markerOption; + if (!element) { + this.defaultMarker = true; + element = DOM.create('div'); + this.markerOption.element = element; + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttributeNS(null, 'display', 'block'); + svg.setAttributeNS(null, 'height', '48px'); + svg.setAttributeNS(null, 'width', '48px'); + svg.setAttributeNS(null, 'viewBox', '0 0 1024 1024'); + + const path = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'path', + ); + path.setAttributeNS( + null, + 'd', + 'M512 490.666667C453.12 490.666667 405.333333 442.88 405.333333 384 405.333333 325.12 453.12 277.333333 512 277.333333 570.88 277.333333 618.666667 325.12 618.666667 384 618.666667 442.88 570.88 490.666667 512 490.666667M512 85.333333C346.88 85.333333 213.333333 218.88 213.333333 384 213.333333 608 512 938.666667 512 938.666667 512 938.666667 810.666667 608 810.666667 384 810.666667 218.88 677.12 85.333333 512 85.333333Z', + ); + path.setAttributeNS(null, 'fill', color); + svg.appendChild(path); + element.appendChild(svg); + } + DOM.addClass(element, 'l7-marker'); + element.addEventListener('click', (e: MouseEvent) => { + this.onMapClick(e); + }); + applyAnchorClass(element, anchor, 'marker'); + } + + private addDragHandler(e: MouseEvent) { + throw new Error('Method not implemented.'); + } + + private onUp(e: MouseEvent) { + throw new Error('Method not implemented.'); + } +} diff --git a/packages/component/src/popup.ts b/packages/component/src/popup.ts new file mode 100644 index 0000000000..8e0dd2ed35 --- /dev/null +++ b/packages/component/src/popup.ts @@ -0,0 +1,210 @@ +import { ILngLat, IMapService, IMarkerScene, IPopup } from '@l7/core'; +import { bindAll, DOM } from '@l7/utils'; +import { EventEmitter } from 'eventemitter3'; +import { anchorTranslate, anchorType, applyAnchorClass } from './utils/anchor'; + +/** colse event */ + +export interface IPopupOption { + closeButton: boolean; + closeOnClick: boolean; + maxWidth: string; + anchor: anchorType; + className: string; + offsets: number[]; +} +export default class Popup extends EventEmitter implements IPopup { + private popupOption: IPopupOption; + private mapservice: IMapService; + private lngLat: ILngLat; + private content: HTMLElement; + private closeButton: HTMLElement; + private timeoutInstance: any; + private container: HTMLElement; + private tip: HTMLElement; + + constructor(cfg?: Partial) { + super(); + this.popupOption = { + ...this.getdefault(), + ...cfg, + }; + bindAll(['update', 'onClickClose', 'remove'], this); + } + + public addTo(scene: IMarkerScene) { + const mapService = scene.getMapService(); + this.mapservice = mapService; + this.mapservice.on('camerachange', this.update); + this.update(); + if (this.popupOption.closeOnClick) { + this.timeoutInstance = setTimeout(() => { + this.mapservice.on('click', this.onClickClose); + }, 30); + } + return this; + } + + public setHTML(html: string) { + const frag = window.document.createDocumentFragment(); + const temp = window.document.createElement('body'); + let child: ChildNode | null; + temp.innerHTML = html; + while (true) { + child = temp.firstChild; + if (!child) { + break; + } + frag.appendChild(child); + } + + return this.setDOMContent(frag); + } + + public setLnglat(lngLat: ILngLat): this { + this.lngLat = lngLat; + if (this.mapservice) { + this.mapservice.on('camerachange', this.update); + } + this.update(); + return this; + } + public getLnglat(): ILngLat { + return this.lngLat; + } + public setText(text: string) { + return this.setDOMContent(window.document.createTextNode(text)); + } + + public setMaxWidth(maxWidth: string): this { + this.popupOption.maxWidth = maxWidth; + this.update(); + return this; + } + + public setDOMContent(htmlNode: ChildNode | DocumentFragment) { + this.createContent(); + this.content.appendChild(htmlNode); + this.update(); + return this; + } + + // 移除popup + public remove() { + if (this.content) { + this.removeDom(this.content); + } + + if (this.container) { + this.removeDom(this.container); + delete this.container; + } + if (this.mapservice) { + // TODO: mapbox AMap 事件同步 + this.mapservice.off('camerachange', this.update); + this.mapservice.off('click', this.onClickClose); + delete this.mapservice; + } + clearTimeout(this.timeoutInstance); + this.emit('close'); + return this; + } + public isOpen() { + return !!this.mapservice; + } + + private createContent() { + if (this.content) { + DOM.remove(this.content); + } + this.content = DOM.create('div', 'l7-popup-content', this.container); + if (this.popupOption.closeButton) { + this.closeButton = DOM.create( + 'button', + 'l7-popup-close-button', + this.content, + ); + // this.closeButton.type = 'button'; + this.closeButton.setAttribute('aria-label', 'Close popup'); + this.closeButton.innerHTML = '×'; + this.closeButton.addEventListener('click', this.onClickClose); + } + } + + private creatDom(tagName: string, className: string, container: HTMLElement) { + const el = window.document.createElement(tagName); + if (className !== undefined) { + el.className = className; + } + if (container) { + container.appendChild(el); + } + return el; + } + + private removeDom(node: ChildNode) { + if (node.parentNode) { + node.parentNode.removeChild(node); + } + } + + private getdefault() { + return { + closeButton: true, + closeOnClick: true, + maxWidth: '240px', + offsets: [0, 0], + anchor: anchorType.BOTTOM, + className: '', + }; + } + + private onClickClose() { + this.remove(); + } + + private update() { + const hasPosition = this.lngLat; + const { className, maxWidth, anchor } = this.popupOption; + if (!this.mapservice || !hasPosition || !this.content) { + return; + } + const markerContainer = this.mapservice.getMarkerContainer(); + if (!this.container && markerContainer) { + this.container = this.creatDom( + 'div', + 'l7-popup', + markerContainer.parentNode as HTMLElement, + ); + + this.tip = this.creatDom('div', 'l7-popup-tip', this.container); + this.container.appendChild(this.content); + if (className) { + className + .split(' ') + .forEach((name) => this.container.classList.add(name)); + } + this.container.addEventListener('mousedown', (e) => { + e.stopPropagation(); + }); + } + if (maxWidth && this.container.style.maxWidth !== maxWidth) { + this.container.style.maxWidth = maxWidth; + } + + this.updatePosition(); + DOM.setTransform(this.container, `${anchorTranslate[anchor]}`); + applyAnchorClass(this.container, anchor, 'popup'); + } + + private updatePosition() { + if (!this.mapservice) { + return; + } + const { lng, lat } = this.lngLat; + const { offsets } = this.popupOption; + const pos = this.mapservice.lngLatToContainer([lng, lat]); + this.container.style.left = pos.x + offsets[0] + 'px'; + this.container.style.top = pos.y - offsets[1] + 'px'; + } +} diff --git a/packages/component/src/utils/anchor.ts b/packages/component/src/utils/anchor.ts new file mode 100644 index 0000000000..2b1e301c51 --- /dev/null +++ b/packages/component/src/utils/anchor.ts @@ -0,0 +1,36 @@ +export enum anchorType { + 'CENTER' = 'center', + 'TOP' = 'top', + 'TOP-LEFT' = 'top-left', + 'TOP-RIGHT' = 'top-right', + 'BOTTOM' = 'bottom', + 'BOTTOM-LEFT' = 'bottom-left', + 'LEFT' = 'left', + 'RIGHT' = 'right', +} + +export const anchorTranslate = { + center: 'translate(-50%,-50%)', + top: 'translate(-50%,0)', + 'top-left': 'translate(0,0)', + 'top-right': 'translate(-100%,0)', + bottom: 'translate(-50%,-100%)', + 'bottom-left': 'translate(0,-100%)', + 'bottom-right': 'translate(-100%,-100%)', + left: 'translate(0,-50%)', + right: 'translate(-100%,-50%)', +}; + +export function applyAnchorClass( + element: HTMLElement, + anchor: string, + prefix: string, +) { + const classList = element.classList; + for (const key in anchorTranslate) { + if (anchorTranslate.hasOwnProperty(key)) { + classList.remove(`l7-${prefix}-anchor-${key}`); + } + } + classList.add(`l7-${prefix}-anchor-${anchor}`); +} diff --git a/packages/component/tsconfig.build.json b/packages/component/tsconfig.build.json new file mode 100644 index 0000000000..2209f262e6 --- /dev/null +++ b/packages/component/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "declarationDir": "./dist", + "rootDir": "./src", + "baseUrl": "./" + }, + "include": ["./src"] +} diff --git a/packages/core/package.json b/packages/core/package.json index ca6fb3029d..c71a64610e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -19,8 +19,9 @@ "author": "xiaoiver", "license": "ISC", "dependencies": { - "@l7/source": "0.0.1", + "@mapbox/tiny-sdf": "^1.1.1", "eventemitter3": "^3.1.0", + "@l7/utils": "0.0.1", "gl-matrix": "^3.1.0", "hammerjs": "^2.0.8", "inversify": "^5.0.1", @@ -29,6 +30,7 @@ "probe.gl": "^3.1.1", "reflect-metadata": "^0.1.13", "tapable": "^2.0.0-beta.8", + "mapbox-gl": "^1.2.1", "viewport-mercator-project": "^6.2.1" }, "devDependencies": { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 45d90ad78e..6096497be4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -52,6 +52,10 @@ export * from './services/config/IConfigService'; export * from './services/scene/ISceneService'; export * from './services/shader/IShaderModuleService'; export * from './services/asset/IIconService'; +export * from './services/asset/IFontService'; +export * from './services/component/IControlService'; +export * from './services/component/IMarkerService'; +export * from './services/component/IPopUpService'; /** 全部渲染服务接口 */ export * from './services/renderer/IAttribute'; diff --git a/packages/core/src/inversify.config.ts b/packages/core/src/inversify.config.ts index 30e69bcc00..44c4375616 100644 --- a/packages/core/src/inversify.config.ts +++ b/packages/core/src/inversify.config.ts @@ -6,8 +6,10 @@ import getDecorators from 'inversify-inject-decorators'; import { TYPES } from './types'; /** Service interfaces */ +import { IFontService } from './services/asset/IFontService'; import { IIconService } from './services/asset/IIconService'; import { ICameraService } from './services/camera/ICameraService'; +import { IControlService } from './services/component/IControlService'; import { IGlobalConfigService } from './services/config/IConfigService'; import { ICoordinateSystemService } from './services/coordinate/ICoordinateSystemService'; import { IInteractionService } from './services/interaction/IInteractionService'; @@ -16,8 +18,10 @@ import { ILogService } from './services/log/ILogService'; import { IShaderModuleService } from './services/shader/IShaderModuleService'; /** Service implements */ +import FontService from './services/asset/FontService'; import IconService from './services/asset/IconService'; import CameraService from './services/camera/CameraService'; +import ControlService from './services/component/ControlService'; import GlobalConfigService from './services/config/ConfigService'; import CoordinateSystemService from './services/coordinate/CoordinateSystemService'; import InteractionService from './services/interaction/InteractionService'; @@ -25,7 +29,6 @@ import LayerService from './services/layer/LayerService'; import LayerStyleService from './services/layer/LayerStyleService'; import LogService from './services/log/LogService'; import ShaderModuleService from './services/shader/ShaderModuleService'; - // @see https://github.com/inversify/InversifyJS/blob/master/wiki/container_api.md#defaultscope const container = new Container(); @@ -64,6 +67,14 @@ container .bind(TYPES.IInteractionService) .to(InteractionService) .inSingletonScope(); +container + .bind(TYPES.IFontService) + .to(FontService) + .inSingletonScope(); +container + .bind(TYPES.IControlService) + .to(ControlService) + .inSingletonScope(); // @see https://github.com/inversify/InversifyJS/blob/master/wiki/inheritance.md#what-can-i-do-when-my-base-class-is-provided-by-a-third-party-module decorate(injectable(), EventEmitter); diff --git a/packages/core/src/services/asset/FontService.ts b/packages/core/src/services/asset/FontService.ts new file mode 100644 index 0000000000..045e5787af --- /dev/null +++ b/packages/core/src/services/asset/FontService.ts @@ -0,0 +1,243 @@ +import { LRUCache } from '@l7/utils'; +// @ts-ignore +import TinySDF from '@mapbox/tiny-sdf'; +import { inject, injectable } from 'inversify'; +import { buildMapping } from '../../utils/font_util'; +import { + IFontAtlas, + IFontMapping, + IFontMappingItem, + IFontOptions, + IFontService, +} from './IFontService'; +export const DEFAULT_CHAR_SET = getDefaultCharacterSet(); +export const DEFAULT_FONT_FAMILY = 'sans-serif'; +export const DEFAULT_FONT_WEIGHT = 'normal'; +export const DEFAULT_FONT_SIZE = 24; +export const DEFAULT_BUFFER = 3; +export const DEFAULT_CUTOFF = 0.25; +export const DEFAULT_RADIUS = 8; +const MAX_CANVAS_WIDTH = 1024; +const BASELINE_SCALE = 1.0; +const HEIGHT_SCALE = 1.0; +const CACHE_LIMIT = 3; +const VALID_PROPS = [ + 'fontFamily', + 'fontWeight', + 'characterSet', + 'fontSize', + 'sdf', + 'buffer', + 'cutoff', + 'radius', +]; +function getDefaultCharacterSet() { + const charSet = []; + for (let i = 32; i < 128; i++) { + charSet.push(String.fromCharCode(i)); + } + return charSet; +} + +function setTextStyle( + ctx: CanvasRenderingContext2D, + fontFamily: string, + fontSize: number, + fontWeight: string, +) { + ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`; + ctx.fillStyle = 'black'; + ctx.textBaseline = 'middle'; + // ctx.textAlign = 'left'; +} +function populateAlphaChannel(alphaChannel: number[], imageData: ImageData) { + // populate distance value from tinySDF to image alpha channel + for (let i = 0; i < alphaChannel.length; i++) { + imageData.data[4 * i + 3] = alphaChannel[i]; + } +} + +@injectable() +export default class FontService implements IFontService { + public fontAtlas: IFontAtlas; + private fontOptions: IFontOptions; + private key: string; + private cache: LRUCache = new LRUCache(CACHE_LIMIT); + public init() { + this.fontOptions = { + fontFamily: DEFAULT_FONT_FAMILY, + fontWeight: DEFAULT_FONT_WEIGHT, + characterSet: DEFAULT_CHAR_SET, + fontSize: DEFAULT_FONT_SIZE, + buffer: DEFAULT_BUFFER, + sdf: true, + cutoff: DEFAULT_CUTOFF, + radius: DEFAULT_RADIUS, + }; + this.key = ''; + } + + public get scale() { + return HEIGHT_SCALE; + } + + public get canvas(): HTMLCanvasElement { + const data = this.cache.get(this.key); + return data && data.data; + } + + public get mapping(): IFontMapping { + const data = this.cache.get(this.key); + return data && data.mapping; + } + + public setFontOptions(option: Partial) { + this.fontOptions = { + ...this.fontOptions, + ...option, + }; + + // const oldKey = this.key; + this.key = this.getKey(); + + const charSet = this.getNewChars(this.key, this.fontOptions.characterSet); + const cachedFontAtlas = this.cache.get(this.key); + + if (cachedFontAtlas && charSet.length === 0) { + // update texture with cached fontAtlas + return; + } + // update fontAtlas with new settings + const fontAtlas = this.generateFontAtlas( + this.key, + charSet, + cachedFontAtlas, + ); + this.fontAtlas = fontAtlas; + + // update cache + this.cache.set(this.key, fontAtlas); + } + + public destroy(): void { + this.cache.clear(); + } + + private generateFontAtlas( + key: string, + characterSet: string[], + cachedFontAtlas: IFontAtlas, + ): IFontAtlas { + const { + fontFamily, + fontWeight, + fontSize, + buffer, + sdf, + radius, + cutoff, + } = this.fontOptions; + let canvas = cachedFontAtlas && cachedFontAtlas.data; + if (!canvas) { + canvas = document.createElement('canvas'); + canvas.width = MAX_CANVAS_WIDTH; + } + const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; + setTextStyle(ctx, fontFamily, fontSize, fontWeight); + + // 1. build mapping + const { mapping, canvasHeight, xOffset, yOffset } = buildMapping({ + getFontWidth: (char) => ctx.measureText(char).width, + fontHeight: fontSize * HEIGHT_SCALE, + buffer, + characterSet, + maxCanvasWidth: MAX_CANVAS_WIDTH, + ...(cachedFontAtlas && { + mapping: cachedFontAtlas.mapping, + xOffset: cachedFontAtlas.xOffset, + yOffset: cachedFontAtlas.yOffset, + }), + }); + + // 2. update canvas + // copy old canvas data to new canvas only when height changed + if (canvas.height !== canvasHeight) { + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + canvas.height = canvasHeight; + ctx.putImageData(imageData, 0, 0); + } + setTextStyle(ctx, fontFamily, fontSize, fontWeight); + + // 3. layout characters + if (sdf) { + const tinySDF = new TinySDF( + fontSize, + buffer, + radius, + cutoff, + fontFamily, + fontWeight, + ); + // used to store distance values from tinySDF + // tinySDF.size equals `fontSize + buffer * 2` + const imageData = ctx.getImageData(0, 0, tinySDF.size, tinySDF.size); + + for (const char of characterSet) { + populateAlphaChannel(tinySDF.draw(char), imageData); + // 考虑到描边,需要保留 sdf 的 buffer,不能像 deck.gl 一样直接减去 + ctx.putImageData(imageData, mapping[char].x, mapping[char].y); + } + } else { + for (const char of characterSet) { + ctx.fillText( + char, + mapping[char].x, + mapping[char].y + fontSize * BASELINE_SCALE, + ); + } + } + return { + xOffset, + yOffset, + mapping, + data: canvas, + width: canvas.width, + height: canvas.height, + }; + } + + private getKey() { + const { + fontFamily, + fontWeight, + fontSize, + buffer, + sdf, + radius, + cutoff, + } = this.fontOptions; + if (sdf) { + return `${fontFamily} ${fontWeight} ${fontSize} ${buffer} ${radius} ${cutoff}`; + } + return `${fontFamily} ${fontWeight} ${fontSize} ${buffer}`; + } + + private getNewChars(key: string, characterSet: string[]): string[] { + const cachedFontAtlas = this.cache.get(key); + if (!cachedFontAtlas) { + return characterSet; + } + + const newChars: string[] = []; + const cachedMapping = cachedFontAtlas.mapping; + const cachedCharSet = new Set(Object.keys(cachedMapping)); + const charSet = new Set(characterSet); + charSet.forEach((char: string) => { + if (!cachedCharSet.has(char)) { + newChars.push(char); + } + }); + + return newChars; + } +} diff --git a/packages/core/src/services/asset/IFontService.ts b/packages/core/src/services/asset/IFontService.ts new file mode 100644 index 0000000000..208988c535 --- /dev/null +++ b/packages/core/src/services/asset/IFontService.ts @@ -0,0 +1,48 @@ +export interface IFontOptions { + fontFamily: string; + fontWeight: string; + characterSet: string[]; + fontSize: number; + buffer: number; + sdf: boolean; + cutoff: number; + radius: number; +} +export interface IFontMappingOption { + characterSet: string[]; + getFontWidth: (char: string, i: number) => number; + fontHeight: number; + buffer: number; + maxCanvasWidth: number; + mapping: IFontMapping; + xOffset: number; + yOffset: number; +} +export interface IFontMappingItem { + x: number; + y: number; + width: number; + height: number; + advance: number; +} +export interface IFontMapping { + [key: string]: IFontMappingItem; + [key: number]: IFontMappingItem; +} +export interface IFontAtlas { + xOffset: number; + yOffset: number; + mapping: IFontMapping; + data: HTMLCanvasElement; + width: number; + height: number; +} +export interface IFontService { + mapping: IFontMapping; + fontAtlas: IFontAtlas; + canvas: HTMLCanvasElement; + scale: number; + init(): void; + setFontOptions(option: Partial): void; + destroy(): void; +} diff --git a/packages/core/src/services/asset/IIconService.ts b/packages/core/src/services/asset/IIconService.ts index bf5883573f..5327519aa4 100644 --- a/packages/core/src/services/asset/IIconService.ts +++ b/packages/core/src/services/asset/IIconService.ts @@ -1,5 +1,7 @@ +import EventEmitter from 'eventemitter3'; import { ITexture2D } from '../renderer/ITexture2D'; export type IImage = HTMLImageElement | File | string; +export type Listener = (...args: any[]) => void; export interface IIconValue { x: number; y: number; @@ -16,6 +18,7 @@ export interface IICONMap { } export interface IIconService { canvasHeight: number; + on(event: string, fn: EventEmitter.ListenerFn, context?: any): this; init(): void; addImage(id: string, image: IImage): void; hasImage(id: string): boolean; @@ -23,4 +26,5 @@ export interface IIconService { getTexture(): ITexture2D; getIconMap(): IICONMap; getCanvas(): HTMLCanvasElement; + destroy(): void; } diff --git a/packages/core/src/services/asset/IconService.ts b/packages/core/src/services/asset/IconService.ts index 3cd9f5887e..f165bfeee2 100644 --- a/packages/core/src/services/asset/IconService.ts +++ b/packages/core/src/services/asset/IconService.ts @@ -1,8 +1,7 @@ +import { EventEmitter } from 'eventemitter3'; import { inject, injectable } from 'inversify'; import { TYPES } from '../../types'; import { buildIconMaping } from '../../utils/font_util'; -import { gl } from '../renderer/gl'; -import { IRendererService } from '../renderer/IRendererService'; import { ITexture2D } from '../renderer/ITexture2D'; import { IIcon, @@ -15,7 +14,7 @@ const BUFFER = 3; const MAX_CANVAS_WIDTH = 1024; const imageSize = 64; @injectable() -export default class IconService implements IIconService { +export default class IconService extends EventEmitter implements IIconService { public canvasHeight: number; private textrure: ITexture2D; private canvas: HTMLCanvasElement; @@ -31,23 +30,20 @@ export default class IconService implements IIconService { public addImage(id: string, image: IImage) { let imagedata = new Image(); + if (this.hasImage(id)) { + throw new Error('Image Id already exists'); + } + this.loadImage(image).then((img) => { imagedata = img as HTMLImageElement; + this.iconData.push({ + id, + image: imagedata, + width: imageSize, + height: imageSize, + }); + this.update(); }); - this.iconData.push({ - id, - image: imagedata, - width: imageSize, - height: imageSize, - }); - const { mapping, canvasHeight } = buildIconMaping( - this.iconData, - BUFFER, - MAX_CANVAS_WIDTH, - ); - this.iconMap = mapping; - this.canvasHeight = canvasHeight; - this.updateIconAtlas(); } public getTexture(): ITexture2D { @@ -57,15 +53,32 @@ export default class IconService implements IIconService { public getIconMap() { return this.iconMap; } + public getCanvas() { return this.canvas; } public hasImage(id: string): boolean { - throw new Error('Method not implemented.'); + return this.iconMap.hasOwnProperty(id); } + public removeImage(id: string): void { - throw new Error('Method not implemented.'); + if (this.hasImage(id)) { + this.iconData = this.iconData.filter((icon) => { + return icon.id !== id; + }); + delete this.iconMap[id]; + this.update(); + } + } + public destroy(): void { + this.iconData = []; + this.iconMap = {}; + } + private update() { + this.updateIconMap(); + this.updateIconAtlas(); + this.emit('imageUpdate'); } private updateIconAtlas() { @@ -75,13 +88,16 @@ export default class IconService implements IIconService { const { x, y, image } = this.iconMap[item]; this.ctx.drawImage(image, x, y, imageSize, imageSize); }); - // const { createTexture2D } = this.rendererService; - // this.textrure = createTexture2D({ - // data: this.canvas, - // width: this.canvas.width, - // height: this.canvasHeight, - // mag: gl.LINEAR, - // }); + } + + private updateIconMap() { + const { mapping, canvasHeight } = buildIconMaping( + this.iconData, + BUFFER, + MAX_CANVAS_WIDTH, + ); + this.iconMap = mapping; + this.canvasHeight = canvasHeight; } private loadImage(url: IImage) { diff --git a/packages/core/src/services/component/ControlService.ts b/packages/core/src/services/component/ControlService.ts new file mode 100644 index 0000000000..6775b1fb06 --- /dev/null +++ b/packages/core/src/services/component/ControlService.ts @@ -0,0 +1,75 @@ +import { DOM } from '@l7/utils'; +import { inject, injectable } from 'inversify'; +import { IMapService } from '../map/IMapService'; +import { + IControl, + IControlCorners, + IControlService, + IControlServiceCfg, +} from './IControlService'; + +@injectable() +export default class ControlService implements IControlService { + public container: HTMLElement; + public controlCorners: IControlCorners; + public controlContainer: HTMLElement; + private controls: IControl[] = []; + public init(cfg: IControlServiceCfg) { + this.container = cfg.container; + this.initControlPos(); + } + + public addControl(ctr: IControl, mapService: IMapService): void { + ctr.addTo(mapService); // scene对象 + this.controls.push(ctr); + } + + public removeControl(ctr: IControl): this { + const index = this.controls.indexOf(ctr); + if (index > -1) { + this.controls.splice(index, 1); + } + ctr.remove(); + return this; + } + + public destroy(): void { + for (const ctr of this.controls) { + ctr.remove(); + } + this.controls = []; + this.clearControlPos(); + } + + private initControlPos() { + const corners: IControlCorners = (this.controlCorners = {}); + const l = 'l7-'; + const container = (this.controlContainer = DOM.create( + 'div', + l + 'control-container', + this.container, + )); + + function createCorner(vSide: string, hSide: string) { + const className = l + vSide + ' ' + l + hSide; + + corners[vSide + hSide] = DOM.create('div', className, container); + } + + createCorner('top', 'left'); + createCorner('top', 'right'); + createCorner('bottom', 'left'); + createCorner('bottom', 'right'); + } + + private clearControlPos() { + for (const i in this.controlCorners) { + if (this.controlCorners[i]) { + DOM.remove(this.controlCorners[i]); + } + } + DOM.remove(this.controlContainer); + delete this.controlCorners; + delete this.controlContainer; + } +} diff --git a/packages/core/src/services/component/IControlService.ts b/packages/core/src/services/component/IControlService.ts new file mode 100644 index 0000000000..2a86f66c41 --- /dev/null +++ b/packages/core/src/services/component/IControlService.ts @@ -0,0 +1,33 @@ +import { IMapService } from '../map/IMapService'; +export enum PositionType { + 'TOPRIGHT' = 'topright', + 'TOPLEFT' = 'topleft', + 'BOTTOMRIGHT' = 'bottomright', + 'BOTTOMLEFT' = 'bottomleft', +} +export interface IControlOption { + position: PositionType; +} +export interface IControlServiceCfg { + container: HTMLElement; +} +export interface IControlCorners { + [key: string]: HTMLElement; +} +export interface IControl { + setPosition(pos: PositionType): void; + addTo(map: IMapService): void; + onAdd(map: IMapService): HTMLElement; + hide(): void; + show(): void; + remove(): void; +} +export interface IControlService { + container: HTMLElement; + controlCorners: IControlCorners; + controlContainer: HTMLElement; + init(cfg: IControlServiceCfg): void; + addControl(ctr: IControl, map: IMapService): void; + removeControl(ctr: IControl): void; + destroy(): void; +} diff --git a/packages/core/src/services/component/IMarkerService.ts b/packages/core/src/services/component/IMarkerService.ts new file mode 100644 index 0000000000..a20a190a2e --- /dev/null +++ b/packages/core/src/services/component/IMarkerService.ts @@ -0,0 +1,13 @@ +import { ILngLat, IMapService } from '../map/IMapService'; +export interface IMarkerScene { + getMapService(): IMapService; + [key: string]: any; +} +export interface IMarker { + addTo(scene: IMarkerScene): void; + remove(): void; + setLnglat(lngLat: ILngLat): this; + getLnglat(): ILngLat; + getElement(): HTMLElement; + togglePopup(): this; +} diff --git a/packages/core/src/services/component/IPopUpService.ts b/packages/core/src/services/component/IPopUpService.ts new file mode 100644 index 0000000000..af4d3a1c36 --- /dev/null +++ b/packages/core/src/services/component/IPopUpService.ts @@ -0,0 +1,12 @@ +import { ILngLat, IMapService } from '../map/IMapService'; +import { IMarkerScene } from './IMarkerService'; +export interface IPopup { + addTo(scene: IMarkerScene): this; + remove(): void; + setLnglat(lngLat: ILngLat): this; + getLnglat(): ILngLat; + setHTML(html: string): this; + setText(text: string): this; + setMaxWidth(maxWidth: string): this; + isOpen(): boolean; +} diff --git a/packages/core/src/services/map/IMapService.ts b/packages/core/src/services/map/IMapService.ts index afe47e6e0e..8fa957541e 100644 --- a/packages/core/src/services/map/IMapService.ts +++ b/packages/core/src/services/map/IMapService.ts @@ -1,3 +1,4 @@ +import { Map } from 'mapbox-gl'; import { IViewport } from '../camera/ICameraService'; export type Point = [number, number]; export type Bounds = [[number, number], [number, number]]; @@ -10,9 +11,22 @@ export interface IPoint { y: number; } export interface IMapService { + map: AMap.Map | Map; init(config: Partial): void; onCameraChanged(callback: (viewport: IViewport) => void): void; + // init map + addMarkerContainer(): void; + getMarkerContainer(): HTMLElement; + // MapEvent // 定义事件类型 + + on(type: string, hander: (...args: any[]) => void): void; + off(type: string, hander: (...args: any[]) => void): void; + // get dom + getContainer(): HTMLElement | null; + getSize(): [number, number]; // get map status method + getMinZoom(): number; + getMaxZoom(): number; getZoom(): number; getCenter(): ILngLat; getPitch(): number; @@ -52,6 +66,11 @@ export interface IMapConfig { */ id: string; + /** + * 地图 + */ + token?: string; + /** * 中心点 */ @@ -76,6 +95,17 @@ export interface IMapConfig { * 底图样式 */ style?: string; + /** + * 最小缩放等级 + */ + minZoom?: number; + + /** + * 最大缩放等级 + */ + maxZoom?: number; + + attributionControl?: boolean; } /** diff --git a/packages/core/src/services/scene/ISceneService.ts b/packages/core/src/services/scene/ISceneService.ts index e47ab8adbc..b9ead753fa 100644 --- a/packages/core/src/services/scene/ISceneService.ts +++ b/packages/core/src/services/scene/ISceneService.ts @@ -1,11 +1,16 @@ +import { EventEmitter } from 'eventemitter3'; import { IImage } from '../asset/IIconService'; import { ILayer } from '../layer/ILayerService'; import { IMapConfig } from '../map/IMapService'; import { IRenderConfig } from '../renderer/IRendererService'; export interface ISceneService { + on(type: string, hander: (...args: any[]) => void): void; + off(type: string, hander: (...args: any[]) => void): void; + removeAllListeners(event?: string): this; init(config: IMapConfig & IRenderConfig): void; addLayer(layer: ILayer): void; render(): void; destroy(): void; } +export const SceneEventList = ['loaded', 'resize', 'destroy']; diff --git a/packages/core/src/services/scene/SceneService.ts b/packages/core/src/services/scene/SceneService.ts index 62b58dbc6f..c59e98e055 100644 --- a/packages/core/src/services/scene/SceneService.ts +++ b/packages/core/src/services/scene/SceneService.ts @@ -3,8 +3,10 @@ import { inject, injectable } from 'inversify'; import { AsyncParallelHook, AsyncSeriesHook } from 'tapable'; import { TYPES } from '../../types'; import { createRendererContainer } from '../../utils/dom'; +import { IFontService } from '../asset/IFontService'; import { IIconService, IImage } from '../asset/IIconService'; import { ICameraService, IViewport } from '../camera/ICameraService'; +import { IControlService } from '../component/IControlService'; import { IGlobalConfig, IGlobalConfigService } from '../config/IConfigService'; import { IInteractionService } from '../interaction/IInteractionService'; import { ILayer, ILayerService } from '../layer/ILayerService'; @@ -18,11 +20,18 @@ import { ISceneService } from './ISceneService'; */ @injectable() export default class Scene extends EventEmitter implements ISceneService { - @inject(TYPES.IIconService) - public readonly iconService: IIconService; /** * 使用各种 Service */ + @inject(TYPES.IIconService) + private readonly iconService: IIconService; + + @inject(TYPES.IFontService) + private readonly fontService: IFontService; + + @inject(TYPES.IControlService) + private readonly controlService: IControlService; + @inject(TYPES.ILogService) private readonly logger: ILogService; @@ -78,8 +87,18 @@ export default class Scene extends EventEmitter implements ISceneService { public init(globalConfig: IGlobalConfig) { this.configService.setAndCheckConfig(globalConfig); - // 初始化资源管理 字体,图片 + + // 初始化资源管理 图片 this.iconService.init(); + // 字体资源 + this.fontService.init(); + + this.controlService.init({ + container: document.getElementById( + this.configService.getConfig().id || 'map', + ) as HTMLElement, + }); + /** * 初始化底图 */ @@ -137,6 +156,8 @@ export default class Scene extends EventEmitter implements ISceneService { if (!this.inited) { // 首次渲染需要等待底图、相机初始化 await this.hooks.init.promise(this.configService.getConfig()); + // 初始化marker 容器 + this.map.addMarkerContainer(); this.emit('loaded'); this.inited = true; @@ -153,6 +174,8 @@ export default class Scene extends EventEmitter implements ISceneService { this.layerService.clean(); this.configService.reset(); this.interactionService.destroy(); + this.controlService.destroy(); + this.removeAllListeners(); window.removeEventListener('resize', this.handleWindowResized, false); } private handleWindowResized = () => { diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index b6af81e25e..f661636692 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -10,7 +10,9 @@ const TYPES = { IRendererService: Symbol.for('IRendererService'), IShaderModuleService: Symbol.for('IShaderModuleService'), IIconService: Symbol.for('IIconService'), + IFontService: Symbol.for('IFontService'), IInteractionService: Symbol.for('IInteractionService'), + IControlService: Symbol.for('IControlService'), /** multi-pass */ ClearPass: Symbol.for('ClearPass'), diff --git a/packages/core/src/utils/dom.ts b/packages/core/src/utils/dom.ts index b1cf167857..70cf45cf09 100644 --- a/packages/core/src/utils/dom.ts +++ b/packages/core/src/utils/dom.ts @@ -1,3 +1,5 @@ +const docStyle = window.document.documentElement.style; +type ELType = HTMLElement | SVGElement; export function createRendererContainer(domId: string): HTMLDivElement | null { const $wrapper = document.getElementById(domId); diff --git a/packages/core/src/utils/font_util.ts b/packages/core/src/utils/font_util.ts index 68b9260645..e53d327d83 100644 --- a/packages/core/src/utils/font_util.ts +++ b/packages/core/src/utils/font_util.ts @@ -1,10 +1,49 @@ -import { - IIcon, - IICONMap, - IIconService, - IIconValue, - IImage, -} from '../services/asset/IIconService'; +import { IFontMappingOption } from '../services/asset/IFontService'; +import { IIcon, IICONMap } from '../services/asset/IIconService'; +/** + * tiny-sdf 中每个 glyph 的宽度(加上 buffer 24 + 3 + 3 = 30) + */ +const glyphSizeInSDF = 30; +export function buildMapping({ + characterSet, + getFontWidth, + fontHeight, + buffer, + maxCanvasWidth, + mapping = {}, + xOffset = 0, + yOffset = 0, +}: IFontMappingOption) { + let row = 0; + let x = xOffset; + Array.from(characterSet).forEach((char: string, i: number) => { + if (!mapping[char]) { + const width = getFontWidth(char, i); + if (x + glyphSizeInSDF > maxCanvasWidth) { + x = 0; + row++; + } + mapping[char] = { + x, + y: yOffset + row * glyphSizeInSDF, + width: glyphSizeInSDF, + height: glyphSizeInSDF, + advance: width, + }; + x += glyphSizeInSDF; + } + }); + + const rowHeight = fontHeight + buffer * 2; + + return { + mapping, + xOffset: x, + yOffset: yOffset + row * rowHeight, + canvasHeight: nextPowOfTwo(yOffset + (row + 1) * rowHeight), + }; +} + export function buildIconMaping( icons: IIcon[], buffer: number, diff --git a/packages/core/typings/index.d.ts b/packages/core/typings/index.d.ts index 5fe9be665d..790cf4872b 100644 --- a/packages/core/typings/index.d.ts +++ b/packages/core/typings/index.d.ts @@ -1,3 +1,4 @@ + declare module 'probe.gl' { class Log { constructor(options: { id: string }); @@ -8,4 +9,5 @@ declare module 'probe.gl' { info(message: string): () => any; error(message: string): () => any; } -} \ No newline at end of file +} +/// diff --git a/packages/layers/src/core/BaseLayer.ts b/packages/layers/src/core/BaseLayer.ts index a757000890..c82c8ca740 100644 --- a/packages/layers/src/core/BaseLayer.ts +++ b/packages/layers/src/core/BaseLayer.ts @@ -1,4 +1,5 @@ import { + IFontService, IGlobalConfigService, IIconService, ILayer, @@ -6,6 +7,7 @@ import { ILayerPlugin, ILayerStyleAttribute, ILayerStyleOptions, + IMapService, IModel, IMultiPassRenderer, IRendererService, @@ -59,9 +61,13 @@ export default class BaseLayer implements ILayer { public styleAttributes: { [key: string]: Required; } = {}; + @lazyInject(TYPES.IIconService) protected readonly iconService: IIconService; + @lazyInject(TYPES.IFontService) + protected readonly fontService: IFontService; + protected layerSource: Source; private encodedData: Array<{ [key: string]: unknown }>; @@ -73,6 +79,9 @@ export default class BaseLayer implements ILayer { @lazyInject(TYPES.IRendererService) private readonly rendererService: IRendererService; + @lazyInject(TYPES.IMapService) + private readonly map: IMapService; + constructor(initializationOptions: Partial) { this.initializationOptions = initializationOptions; } @@ -150,6 +159,14 @@ export default class BaseLayer implements ILayer { } return this; } + /** + * zoom to layer Bounds + */ + public fitBounds(): void { + const source = this.getSource(); + const extent = source.extent; + this.map.fitBounds([[extent[0], extent[1]], [extent[2], extent[3]]]); + } public destroy() { this.models.forEach((model) => model.destroy()); diff --git a/packages/layers/src/point/point.ts b/packages/layers/src/point/point.ts index bcd55c97be..db71ef2727 100644 --- a/packages/layers/src/point/point.ts +++ b/packages/layers/src/point/point.ts @@ -61,70 +61,87 @@ export default class PointLayer extends BaseLayer { data: this.getEncodedData(), iconMap: this.iconService.getIconMap(), }); - this.models.push( - createModel({ - attributes: { - a_Position: createAttribute({ - buffer: createBuffer({ - data: buffer.attributes.positions, - type: gl.FLOAT, + this.fontService.setFontOptions({ + characterSet: ['人', '之', '初'], + fontFamily: 'sans-serif', + fontWeight: 'normal', + }); + this.iconService.on('imageUpdate', () => { + this.models.push( + createModel({ + attributes: { + a_Position: createAttribute({ + buffer: createBuffer({ + data: buffer.attributes.positions, + type: gl.FLOAT, + }), + size: 3, }), - size: 3, - }), - a_normal: createAttribute({ - buffer: createBuffer({ - data: buffer.attributes.normals, - type: gl.FLOAT, + a_normal: createAttribute({ + buffer: createBuffer({ + data: buffer.attributes.normals, + type: gl.FLOAT, + }), + size: 3, }), - size: 3, - }), - a_color: createAttribute({ - buffer: createBuffer({ - data: buffer.attributes.colors, - type: gl.FLOAT, + a_color: createAttribute({ + buffer: createBuffer({ + data: buffer.attributes.colors, + type: gl.FLOAT, + }), + size: 4, }), - size: 4, - }), - a_size: createAttribute({ - buffer: createBuffer({ - data: buffer.attributes.sizes, - type: gl.FLOAT, + a_size: createAttribute({ + buffer: createBuffer({ + data: buffer.attributes.sizes, + type: gl.FLOAT, + }), + size: 1, }), - size: 1, - }), - a_uv: createAttribute({ - buffer: createBuffer({ - data: buffer.attributes.uv, - type: gl.FLOAT, + a_uv: createAttribute({ + buffer: createBuffer({ + data: buffer.attributes.uv, + type: gl.FLOAT, + }), + size: 2, }), - size: 2, - }), - // a_shape: createAttribute({ - // buffer: createBuffer({ - // data: buffer.attributes.miters, - // type: gl.FLOAT, - // }), - // size: 3, + // a_shape: createAttribute({ + // buffer: createBuffer({ + // data: buffer.attributes.miters, + // type: gl.FLOAT, + // }), + // size: 3, + // }), + }, + uniforms: { + ...uniforms, + u_opacity: this.styleOption.opacity as number, + u_texture: createTexture2D({ + data: this.iconService.getCanvas(), + width: 1024, + height: this.iconService.canvasHeight, + }), + }, + fs, + vs, + depth: { enable: false }, + blend: { + enable: true, + func: { + srcRGB: gl.SRC_ALPHA, + srcAlpha: 1, + dstRGB: gl.ONE_MINUS_SRC_ALPHA, + dstAlpha: 1, + }, + }, + primitive: gl.POINTS, + count: buffer.verticesCount, + // elements: createElements({ + // data: buffer.indexArray, + // type: gl.UNSIGNED_INT, // }), - }, - uniforms: { - ...uniforms, - u_opacity: this.styleOption.opacity as number, - u_texture: createTexture2D({ - data: this.iconService.getCanvas(), - width: 1024, - height: this.iconService.canvasHeight, - }), - }, - fs, - vs, - primitive: gl.POINTS, - count: buffer.verticesCount, - // elements: createElements({ - // data: buffer.indexArray, - // type: gl.UNSIGNED_INT, - // }), - }), - ); + }), + ); + }); } } diff --git a/packages/layers/src/point/shaders/image_frag.glsl b/packages/layers/src/point/shaders/image_frag.glsl index c492ed2a4c..e93d519f3b 100644 --- a/packages/layers/src/point/shaders/image_frag.glsl +++ b/packages/layers/src/point/shaders/image_frag.glsl @@ -2,8 +2,8 @@ uniform sampler2D u_texture; varying vec4 v_color; varying vec2 v_uv; void main(){ - vec2 pos= v_uv + gl_PointCoord / vec2(1024.,128.)*64.; - pos.y= 1.- pos.y; + vec2 pos= v_uv + gl_PointCoord / vec2(1024.,128.)* 64.; +// pos.y= 1.- pos.y; vec4 textureColor=texture2D(u_texture,pos); if(v_color == vec4(0.)){ gl_FragColor= textureColor; diff --git a/packages/layers/src/point/shaders/text_frag.glsl b/packages/layers/src/point/shaders/text_frag.glsl new file mode 100644 index 0000000000..c10cd7a96a --- /dev/null +++ b/packages/layers/src/point/shaders/text_frag.glsl @@ -0,0 +1,27 @@ +uniform sampler2D u_sdf_map; +uniform float u_gamma_scale : 0.5; +uniform float u_font_size : 24; +uniform float u_opacity : 1.0; +uniform vec4 u_stroke : [0, 0, 0, 1]; +uniform float u_strokeWidth : 2.0; +uniform float u_halo_blur : 0.5; + +varying vec4 v_color; +varying vec2 v_uv; +varying float v_gamma_scale; + +void main() { + // get sdf from atlas + float dist = texture2D(u_sdf_map, v_uv).a; + + float fontScale = u_font_size / 24.0; + + lowp float buff = (6.0 - u_strokeWidth / fontScale) / SDF_PX; + highp float gamma = (u_halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / (fontScale * u_gamma_scale); + + highp float gamma_scaled = gamma * v_gamma_scale; + + highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist); + + gl_FragColor = mix(v_color * u_opacity, u_stroke, smoothstep(0., 0.5, 1. - dist)) * alpha; +} \ No newline at end of file diff --git a/packages/layers/src/point/shaders/text_vert.glsl b/packages/layers/src/point/shaders/text_vert.glsl new file mode 100644 index 0000000000..a183729a2b --- /dev/null +++ b/packages/layers/src/point/shaders/text_vert.glsl @@ -0,0 +1,33 @@ +attribute vec3 a_Position; +attribute vec2 a_tex; +attribute vec2 a_offset; +attribute vec4 a_color; +attribute float a_size; + +uniform vec2 u_sdf_map_size; +uniform vec2 u_viewport_size; + +uniform float u_activeId : 0; +uniform vec4 u_activeColor : [1.0, 0.0, 0.0, 1.0]; + +varying vec2 v_uv; +varying float v_gamma_scale; +varying vec4 v_color; +#pragma include "projection" +void main() { + v_color = a_color; + v_uv = a_tex / u_sdf_map_size; + + // 文本缩放比例 + float fontScale = a_size / 24.; + + vec4 project_pos = project_position(vec4(a_Position, 1.0)); + + vec4 projected_position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0)); + + gl_Position = vec4(projected_position.xy / projected_position.w + + a_offset * fontScale / u_viewport_size * 2., 0.0, 1.0); + v_gamma_scale = gl_Position.w; + + +} diff --git a/packages/maps/package.json b/packages/maps/package.json index 18658b5e47..ecb4f0c73e 100644 --- a/packages/maps/package.json +++ b/packages/maps/package.json @@ -24,6 +24,7 @@ "gl-matrix": "^3.1.0", "inversify": "^5.0.1", "mapbox-gl": "^1.2.1", + "@l7/utils": "0.0.1", "viewport-mercator-project": "^6.2.1" }, "devDependencies": { diff --git a/packages/maps/src/amap/index.ts b/packages/maps/src/amap/index.ts index b97f894e67..1438bbeb0e 100644 --- a/packages/maps/src/amap/index.ts +++ b/packages/maps/src/amap/index.ts @@ -14,6 +14,7 @@ import { Point, TYPES, } from '@l7/core'; +import { DOM } from '@l7/utils'; import { inject, injectable } from 'inversify'; import Viewport from './Viewport'; @@ -21,21 +22,53 @@ const AMAP_API_KEY: string = '15cd8a57710d40c9b7c0e3cc120f1200'; const AMAP_VERSION: string = '1.4.8'; const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; -/// - /** * AMapService */ @injectable() export default class AMapService implements IMapService { + public map: AMap.Map & IAMapInstance; + @inject(TYPES.ICoordinateSystemService) private readonly coordinateSystemService: ICoordinateSystemService; - - private map: AMap.Map; + private markerContainer: HTMLElement; private viewport: Viewport; private cameraChangedCallback: (viewport: IViewport) => void; + + // init + public addMarkerContainer(): void { + const mapContainer = this.map.getContainer(); + if (mapContainer !== null) { + const amap = mapContainer.getElementsByClassName( + 'amap-maps', + )[0] as HTMLElement; + this.markerContainer = DOM.create('div', 'l7_marker', amap); + } + } + public getMarkerContainer(): HTMLElement { + return this.markerContainer; + } + + // map event + public on(type: string, handle: (...args: any[]) => void): void { + this.map.on(type, handle); + } + + public off(type: string, handle: (...args: any[]) => void): void { + this.map.off(type, handle); + } + + public getContainer(): HTMLElement | null { + return this.map.getContainer(); + } + + public getSize(): [number, number] { + const size = this.map.getSize(); + return [size.getWidth(), size.getHeight()]; + } + public getZoom(): number { return this.map.getZoom(); } @@ -46,12 +79,15 @@ export default class AMapService implements IMapService { lat: center.getLat(), }; } + public getPitch(): number { return this.map.getPitch(); } + public getRotation(): number { return this.map.getRotation(); } + public getBounds(): Bounds { // @ts-ignore const amapBound = this.map.getBounds().toBounds(); @@ -59,8 +95,17 @@ export default class AMapService implements IMapService { const SW = amapBound.getSouthWest(); return [[NE.getLng(), NE.getLat()], [SW.getLng(), SW.getLat()]]; } - public setRotation(rotation: number): number { - return this.setRotation(rotation); + + public getMinZoom(): number { + const zooms = this.map.get('zooms') as [number, number]; + return zooms[0]; + } + public getMaxZoom(): number { + const zooms = this.map.get('zooms') as [number, number]; + return zooms[1]; + } + public setRotation(rotation: number): void { + return this.map.setRotation(rotation); } public zoomIn(): void { diff --git a/packages/maps/src/mapbox/index.ts b/packages/maps/src/mapbox/index.ts index a07232cd3e..80676a1b03 100644 --- a/packages/maps/src/mapbox/index.ts +++ b/packages/maps/src/mapbox/index.ts @@ -12,9 +12,16 @@ import { IViewport, TYPES, } from '@l7/core'; +import { DOM } from '@l7/utils'; import { inject, injectable } from 'inversify'; -import mapboxgl, { Map } from 'mapbox-gl'; +import mapboxgl, { IControl, Map } from 'mapbox-gl'; import Viewport from './Viewport'; +const EventMap: { + [key: string]: any; +} = { + mapmove: 'move', + camerachange: 'move', +}; mapboxgl.accessToken = 'pk.eyJ1IjoieGlhb2l2ZXIiLCJhIjoiY2pxcmc5OGNkMDY3cjQzbG42cXk5NTl3YiJ9.hUC5Chlqzzh0FFd_aEc-uQ'; @@ -25,76 +32,130 @@ const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; */ @injectable() export default class MapboxService implements IMapService { + public map: Map & IMapboxInstance; @inject(TYPES.ICoordinateSystemService) private readonly coordinateSystemService: ICoordinateSystemService; - - private map: Map & IMapboxInstance; private viewport: Viewport; + private markerContainer: HTMLElement; private cameraChangedCallback: (viewport: IViewport) => void; + + // init + public addMarkerContainer(): void { + const container = this.map.getCanvasContainer(); + this.markerContainer = DOM.create('div', 'l7_marker', container); + } + + public getMarkerContainer(): HTMLElement { + return this.markerContainer; + } + + // map event + public on(type: string, handle: (...args: any[]) => void): void { + this.map.on(EventMap[type] || type, handle); + } + public off(type: string, handle: (...args: any[]) => void): void { + this.map.off(EventMap[type] || type, handle); + } + + public getContainer(): HTMLElement | null { + return this.map.getContainer(); + } + + public getSize(): [number, number] { + const size = this.map.transform; + return [size.width, size.height]; + } + // get mapStatus method + public getZoom(): number { return this.map.getZoom(); } + public getCenter(): ILngLat { return this.map.getCenter(); } + public getPitch(): number { return this.map.getPitch(); } + public getRotation(): number { return this.map.getBearing(); } + public getBounds(): Bounds { return this.map.getBounds().toArray() as Bounds; } + + public getMinZoom(): number { + return this.map.getMinZoom(); + } + + public getMaxZoom(): number { + return this.map.getMaxZoom(); + } + public setRotation(rotation: number): void { this.map.setBearing(rotation); } + public zoomIn(): void { this.map.zoomIn(); } + public zoomOut(): void { this.map.zoomOut(); } + public panTo(p: [number, number]): void { this.map.panTo(p); } + public panBy(pixel: [number, number]): void { this.panTo(pixel); } + public fitBounds(bound: Bounds): void { this.map.fitBounds(bound); } + public setMaxZoom(max: number): void { this.map.setMaxZoom(max); } + public setMinZoom(min: number): void { this.map.setMinZoom(min); } + public setZoomAndCenter(zoom: number, center: [number, number]): void { this.map.flyTo({ zoom, center, }); } + public setMapStyle(style: string): void { this.map.setStyle(style); } + // TODO: 计算像素坐标 public pixelToLngLat(pixel: [number, number]): ILngLat { return this.map.unproject(pixel); } + public lngLatToPixel(lnglat: [number, number]): IPoint { return this.map.project(lnglat); } + public containerToLngLat(pixel: [number, number]): ILngLat { - throw new Error('Method not implemented.'); + return this.map.unproject(pixel); } + public lngLatToContainer(lnglat: [number, number]): IPoint { - throw new Error('Method not implemented.'); + return this.map.project(lnglat); } public async init(mapConfig: IMapConfig): Promise { - const { id, ...rest } = mapConfig; - + const { id, attributionControl = false, ...rest } = mapConfig; this.viewport = new Viewport(); /** @@ -104,9 +165,9 @@ export default class MapboxService implements IMapService { // @ts-ignore this.map = new mapboxgl.Map({ container: id, + attributionControl, ...rest, }); - this.map.on('move', this.handleCameraChanged); // 不同于高德地图,需要手动触发首次渲染 @@ -117,12 +178,16 @@ export default class MapboxService implements IMapService { 'https://api.tiles.mapbox.com/mapbox-gl-js/v1.2.1/mapbox-gl.css'; $link.rel = 'stylesheet'; document.head.appendChild($link); + this.removeLogoControl(); } public onCameraChanged(callback: (viewport: IViewport) => void): void { this.cameraChangedCallback = callback; } - + // 同步不同底图的配置项 + private initMapConig(): void { + throw new Error('Method not implemented.'); + } private handleCameraChanged = () => { // @see https://github.com/mapbox/mapbox-gl-js/issues/2572 const { lat, lng } = this.map.getCenter().wrap(); @@ -150,4 +215,16 @@ export default class MapboxService implements IMapService { this.cameraChangedCallback(this.viewport); }; + private removeLogoControl(): void { + // @ts-ignore + const controls = this.map._controls as IControl[]; + const logoCtr = controls.find((ctr: IControl) => { + if (ctr.hasOwnProperty('_updateLogo')) { + return true; + } + }); + if (logoCtr) { + this.map.removeControl(logoCtr); + } + } } diff --git a/packages/maps/typings/index.d.ts b/packages/maps/typings/index.d.ts index c546b11945..e4dcf73132 100644 --- a/packages/maps/typings/index.d.ts +++ b/packages/maps/typings/index.d.ts @@ -1,4 +1,7 @@ /// + +import { IControl } from 'mapbox-gl'; + interface Window { onLoad: () => void; } @@ -8,23 +11,21 @@ interface IAMapEvent { near: number; far: number; height: number; - pitch: number; - rotation: number; + pitch: number; + rotation: number; aspect: number; - position: {x: number; y: number;}; - } + position: { x: number; y: number }; + }; } interface IAMapInstance { - on(eventName: string, handler: (event: IAMapEvent) => void): void; - getZoom(): number; - getCenter(): {lat: number; lng: number}; - [key:string]: Function; + get(key: string): unknown; } interface IMapboxInstance { + _controls: IControl[]; transform: { width: number; height: number; - } + }; } diff --git a/packages/scene/package.json b/packages/scene/package.json index a397dcc44f..09fde486c7 100644 --- a/packages/scene/package.json +++ b/packages/scene/package.json @@ -22,6 +22,7 @@ "@l7/core": "^0.0.1", "@l7/maps": "^0.0.1", "@l7/renderer": "^0.0.1", + "mapbox-gl": "^1.2.1", "inversify": "^5.0.1", "inversify-inject-decorators": "^3.1.0", "reflect-metadata": "^0.1.13" diff --git a/packages/scene/src/index.ts b/packages/scene/src/index.ts index 88e7a55662..efcc52c75a 100644 --- a/packages/scene/src/index.ts +++ b/packages/scene/src/index.ts @@ -2,24 +2,29 @@ import { Bounds, container, IconService, + IControl, + IControlService, IIconService, IImage, ILayer, ILngLat, IMapConfig, IMapService, + IMarker, IPoint, IRenderConfig, IRendererService, ISceneService, MapType, Point, + SceneEventList, SceneService, TYPES, } from '@l7/core'; import { AMapService, MapboxService } from '@l7/maps'; import { ReglRendererService } from '@l7/renderer'; import { inject, injectable } from 'inversify'; +import { Map } from 'mapbox-gl'; // 绑定渲染引擎服务 container @@ -39,10 +44,12 @@ container * scene.render(); */ class Scene { - @inject(TYPES.IIconService) + public map: AMap.Map | Map; protected readonly iconService: IIconService; private sceneService: ISceneService; private mapService: IMapService; + private controlService: IControlService; + public constructor(config: IMapConfig & IRenderConfig) { const { type = MapType.amap } = config; @@ -55,6 +62,7 @@ class Scene { } else { throw new Error('不支持的地图服务'); } + // this.mapService = mapService; // DEMO 中切换底图实现时,需要重新绑定底图服务 // @see https://github.com/inversify/InversifyJS/blob/master/wiki/container_api.md#containerrebindserviceidentifier-serviceidentifier @@ -75,6 +83,12 @@ class Scene { this.sceneService.init(config); this.mapService = container.get(TYPES.IMapService); this.iconService = container.get(TYPES.IIconService); + this.controlService = container.get(TYPES.IControlService); + this.map = this.mapService.map; // 暴露原生map方法 + } + + public getMapService(): IMapService { + return this.mapService; } public addLayer(layer: ILayer): void { @@ -84,25 +98,65 @@ class Scene { public render(): void { this.sceneService.render(); } + // asset method public addImage(id: string, img: IImage) { - // this.sceneService. this.iconService.addImage(id, img); } + + public hasImage(id: string) { + this.iconService.hasImage(id); + } + + public removeImage(id: string) { + this.iconService.removeImage(id); + } + + // map control method + public addControl(ctr: IControl) { + this.controlService.addControl(ctr, this.mapService); + } + + public removeControl(ctr: IControl) { + this.controlService.removeControl(ctr); + } + + // marker + public addMarker(marker: IMarker) { + marker.addTo(this); + } + // map envent; + + public on(type: string, handle: (...args: any[]) => void): void { + SceneEventList.indexOf(type) === -1 + ? this.mapService.on(type, handle) + : this.sceneService.on(type, handle); + } + + public off(type: string, handle: (...args: any[]) => void): void { + SceneEventList.indexOf(type) === -1 + ? this.mapService.off(type, handle) + : this.sceneService.off(type, handle); + } + // map method public getZoom(): number { return this.mapService.getZoom(); } + public getCenter(): ILngLat { return this.mapService.getCenter(); } + public getPitch(): number { return this.mapService.getPitch(); } + public getRotation(): number { return this.mapService.getRotation(); } + public getBounds(): Bounds { return this.mapService.getBounds(); } @@ -115,21 +169,27 @@ class Scene { public zoomIn(): void { this.mapService.zoomIn(); } + public zoomOut(): void { this.mapService.zoomOut(); } + public panTo(p: Point): void { this.mapService.panTo(p); } + public panBy(pixel: Point): void { this.mapService.panTo(pixel); } + public fitBounds(bound: Bounds): void { this.mapService.fitBounds(bound); } + public setZoomAndCenter(zoom: number, center: Point): void { this.mapService.setZoomAndCenter(zoom, center); } + public setMapStyle(style: string): void { this.mapService.setMapStyle(style); } @@ -138,12 +198,15 @@ class Scene { public pixelToLngLat(pixel: Point): ILngLat { return this.mapService.pixelToLngLat(pixel); } + public lngLatToPixel(lnglat: Point): IPoint { return this.mapService.lngLatToPixel(lnglat); } + public containerToLngLat(pixel: Point): ILngLat { return this.mapService.containerToLngLat(pixel); } + public lngLatToContainer(lnglat: Point): IPoint { return this.mapService.lngLatToContainer(lnglat); } diff --git a/packages/source/package.json b/packages/source/package.json index 71f3469431..c96f31b7bd 100644 --- a/packages/source/package.json +++ b/packages/source/package.json @@ -28,6 +28,7 @@ "@turf/invariant": "^6.1.2", "@turf/meta": "^6.0.2", "d3-dsv": "^1.1.1", + "d3-hexbin": "^0.2.2", "eventemitter3": "^3.1.0", "gl-matrix": "^3.1.0", "inversify": "^5.0.1", @@ -40,6 +41,7 @@ }, "devDependencies": { "@types/d3-dsv": "^1.0.36", + "@types/d3-hexbin": "^0.2.3", "@types/gl-matrix": "^2.4.5", "@types/lodash": "^4.14.138", "@types/viewport-mercator-project": "^6.1.0" diff --git a/packages/source/src/index.ts b/packages/source/src/index.ts index a307b5857a..4b009de5ad 100644 --- a/packages/source/src/index.ts +++ b/packages/source/src/index.ts @@ -6,6 +6,7 @@ import json from './parser/json'; import Source from './source'; import { cluster } from './transform/cluster'; import { aggregatorToGrid } from './transform/grid'; +import { pointToHexbin } from './transform/hexagon'; export default Source; registerParser('geojson', geojson); registerParser('image', image); @@ -13,6 +14,7 @@ registerParser('csv', csv); registerParser('json', json); registerTransform('cluster', cluster); registerTransform('grid', aggregatorToGrid); +registerTransform('hexagon', pointToHexbin); export { getTransform, registerTransform, diff --git a/packages/source/src/source.ts b/packages/source/src/source.ts index 48ac92e7fb..9caa6e2534 100644 --- a/packages/source/src/source.ts +++ b/packages/source/src/source.ts @@ -41,7 +41,6 @@ export default class Source extends EventEmitter { }); this.init(); } - private excuteParser(): void { const parser = this.parser; const type: string = parser.type || 'geojson'; diff --git a/packages/source/src/transform/grid.ts b/packages/source/src/transform/grid.ts index b55581f347..54aada184d 100644 --- a/packages/source/src/transform/grid.ts +++ b/packages/source/src/transform/grid.ts @@ -2,13 +2,8 @@ * 生成四边形热力图 */ import { IParserCfg, IParserData, ISourceCFG, ITransform } from '@l7/core'; -import { max, mean, min, sum } from './statistics'; -const statMap: { [key: string]: any } = { - min, - max, - mean, - sum, -}; +import { statMap } from './statistics'; + interface IGridHash { [key: string]: any; } diff --git a/packages/source/src/transform/hexagon.ts b/packages/source/src/transform/hexagon.ts new file mode 100644 index 0000000000..03dbedd4b5 --- /dev/null +++ b/packages/source/src/transform/hexagon.ts @@ -0,0 +1,60 @@ +import { aProjectFlat, unProjectFlat } from '@l7/utils'; +import { hexbin } from 'd3-hexbin'; +const R_EARTH = 6378000; +import { + IParseDataItem, + IParserCfg, + IParserData, + ISourceCFG, + ITransform, +} from '@l7/core'; +import { statMap } from './statistics'; +interface IHexBinItem extends Array { + x: number; + y: number; + [key: string]: any; +} +interface IRawData { + coordinates: [number, number]; + [key: string]: any; +} +export function pointToHexbin(data: IParserData, option: ITransform) { + const dataArray = data.dataArray; + const { size = 10 } = option; + const pixlSize = ((size / (2 * Math.PI * R_EARTH)) * (256 << 20)) / 2; + const screenPoints: IRawData[] = dataArray.map((point: IParseDataItem) => { + const [x, y] = aProjectFlat(point.coordinates); + return { + ...point, + coordinates: [x, y], + }; + }); + + const newHexbin = hexbin() + .radius(pixlSize) + .x((d: IRawData) => d.coordinates[0]) + .y((d: IRawData) => d.coordinates[1]); + const hexbinBins = newHexbin(screenPoints); + + const result: IParserData = { + dataArray: hexbinBins.map((hex: IHexBinItem, index: number) => { + if (option.field && option.method) { + const columns = getColumn(hex, option.field); + hex[option.method] = statMap[option.method](columns); + } + return { + [option.method]: hex[option.method], + count: hex.length, + coordinates: unProjectFlat([hex.x, hex.y]), + _id: index + 1, + }; + }), + radius: pixlSize, + }; + return result; +} +function getColumn(data: IHexBinItem, columnName: string) { + return data.map((item: IRawData) => { + return item[columnName]; + }); +} diff --git a/packages/source/src/transform/statistics.ts b/packages/source/src/transform/statistics.ts index e3da89891c..d6ab63bb0d 100644 --- a/packages/source/src/transform/statistics.ts +++ b/packages/source/src/transform/statistics.ts @@ -69,3 +69,9 @@ function mean(x: number[]) { } export { sum, max, min, mean }; +export const statMap: { [key: string]: any } = { + min, + max, + mean, + sum, +}; diff --git a/packages/utils/src/dom.ts b/packages/utils/src/dom.ts new file mode 100644 index 0000000000..1591fc640b --- /dev/null +++ b/packages/utils/src/dom.ts @@ -0,0 +1,143 @@ +const docStyle = window.document.documentElement.style; +type ELType = HTMLElement | SVGElement; +export function createRendererContainer(domId: string): HTMLDivElement | null { + const $wrapper = document.getElementById(domId); + + if ($wrapper) { + const $container = document.createElement('div'); + $container.style.cssText += ` + position: absolute; + top: 0; + z-index:10; + height: 100%; + width: 100%; + pointer-events: none; + `; + $container.id = 'l7_canvaslayer'; + $wrapper.appendChild($container); + return $container; + } + + return null; +} + +export function trim(str: string) { + return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ''); +} + +// @function splitWords(str: String): String[] +// Trims and splits the string on whitespace and returns the array of parts. +export function splitWords(str: string) { + return trim(str).split(/\s+/); +} + +function testProp(props: string[]): string { + if (!docStyle) { + return props[0]; + } + for (const i in props) { + if (props[i] && props[i] in docStyle) { + return props[i]; + } + } + + return props[0]; +} +export function create( + tagName: string, + className?: string, + container?: HTMLElement, +) { + const el = document.createElement(tagName); + el.className = className || ''; + + if (container) { + container.appendChild(el); + } + return el; +} +// @function remove(el: HTMLElement) +// Removes `el` from its parent element +export function remove(el: ELType) { + const parent = el.parentNode; + if (parent) { + parent.removeChild(el); + } +} + +// @function addClass(el: HTMLElement, name: String) +// Adds `name` to the element's class attribute. +export function addClass(el: ELType, name: string) { + if (el.classList !== undefined) { + const classes = splitWords(name); + for (let i = 0, len = classes.length; i < len; i++) { + el.classList.add(classes[i]); + } + } else if (!hasClass(el, name)) { + const className = getClass(el); + setClass(el, (className ? className + ' ' : '') + name); + } +} + +// @function removeClass(el: HTMLElement, name: String) +// Removes `name` from the element's class attribute. +export function removeClass(el: ELType, name: string) { + if (el.classList !== undefined) { + el.classList.remove(name); + } else { + setClass( + el, + trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')), + ); + } +} + +// @function hasClass(el: HTMLElement, name: String): Boolean +// Returns `true` if the element's class attribute contains `name`. +export function hasClass(el: ELType, name: string) { + if (el.classList !== undefined) { + return el.classList.contains(name); + } + const className = getClass(el); + return ( + className.length > 0 && + new RegExp('(^|\\s)' + name + '(\\s|$)').test(className) + ); +} + +// @function setClass(el: HTMLElement, name: String) +// Sets the element's class. +export function setClass(el: ELType, name: string) { + if (el instanceof HTMLElement) { + el.className = name; + } else { + // in case of SVG element + el.className.baseVal = name; + } +} + +// @function getClass(el: HTMLElement): String +// Returns the element's class. +export function getClass(el: ELType) { + // Check if the element is an SVGElementInstance and use the correspondingElement instead + // (Required for linked SVG elements in IE11.) + if (el instanceof SVGElement) { + el = el.correspondingElement; + } + return el.className.baseVal === undefined + ? el.className + : el.className.baseVal; +} + +export function empty(el: ELType) { + while (el.firstChild) { + el.removeChild(el.firstChild); + } +} + +const transformProp = testProp(['transform', 'WebkitTransform']); + +export function setTransform(el: ELType, value: string) { + // @ts-ignore + el.style[transformProp] = value; +} diff --git a/packages/utils/src/event.ts b/packages/utils/src/event.ts new file mode 100644 index 0000000000..3662f8ebd7 --- /dev/null +++ b/packages/utils/src/event.ts @@ -0,0 +1,8 @@ +export function bindAll(fns: string[], context: any) { + fns.forEach((fn) => { + if (!context[fn]) { + return; + } + context[fn] = context[fn].bind(context); + }); +} diff --git a/packages/utils/src/geo.ts b/packages/utils/src/geo.ts index 851cc4b62a..1bf0a81ca7 100644 --- a/packages/utils/src/geo.ts +++ b/packages/utils/src/geo.ts @@ -1,4 +1,12 @@ -import { BBox } from '@turf/helpers'; +import { + BBox, + Coord, + degreesToRadians, + isObject, + radiansToLength, + Units, +} from '@turf/helpers'; + const originShift = (2 * Math.PI * 6378137) / 2.0; export type Point = [number, number] | [number, number, number]; /** @@ -6,7 +14,7 @@ export type Point = [number, number] | [number, number, number]; * @param {dataArray} data 地理坐标数据 * @return {Array} dataExtent */ -export function extent(data: any[]) { +export function extent(data: any[]): BBox { const dataExtent: BBox = [Infinity, Infinity, -Infinity, -Infinity]; data.forEach((item) => { const { coordinates } = item; @@ -150,7 +158,40 @@ export function aProjectFlat(lnglat: number[]) { const b = 0.5; const c = -0.5 / Math.PI; d = 0.5; - x = scale * (a * x + b) - 215440491; - y = scale * (c * y + d) - 106744817; + x = scale * (a * x + b); + y = scale * (c * y + d); return [parseInt(x.toString(), 10), parseInt(y.toString(), 10)]; } +export function unProjectFlat(px: number[]): [number, number] { + const a = 0.5 / Math.PI; + const b = 0.5; + const c = -0.5 / Math.PI; + let d = 0.5; + const scale = 256 << 20; + let [x, y] = px; + x = (x / scale - b) / a; + y = (y / scale - d) / c; + y = (Math.atan(Math.pow(Math.E, y)) - Math.PI / 4) * 2; + d = Math.PI / 180; + const lat = y / d; + const lng = x / d; + return [lng, lat]; +} +export function lnglatDistance( + coordinates1: [number, number], + coordinates2: [number, number], + units?: Units, +): number { + const dLat = degreesToRadians(coordinates2[1] - coordinates1[1]); + const dLon = degreesToRadians(coordinates2[0] - coordinates1[0]); + const lat1 = degreesToRadians(coordinates1[1]); + const lat2 = degreesToRadians(coordinates2[1]); + const a = + Math.pow(Math.sin(dLat / 2), 2) + + Math.pow(Math.sin(dLon / 2), 2) * Math.cos(lat1) * Math.cos(lat2); + + return radiansToLength( + 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)), + (units = 'meters'), + ); +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index e581a9d4e1..d3e73b4277 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,3 +1,7 @@ export { djb2hash, BKDRHash } from './hash'; +import * as DOM from './dom'; export * from './fetchData'; export * from './geo'; +export * from './lru_cache'; +export * from './event'; +export { DOM }; diff --git a/packages/utils/src/lru_cache.ts b/packages/utils/src/lru_cache.ts new file mode 100644 index 0000000000..4299f87412 --- /dev/null +++ b/packages/utils/src/lru_cache.ts @@ -0,0 +1,86 @@ +/** + * LRU Cache class with limit + * + * Update order for each get/set operation + * Delete oldest when reach given limit + */ +type callback = (...args: any[]) => void; +interface ICache { + [key: string]: any; +} +export class LRUCache { + private limit: number; + private cache: ICache; + private destroy: (value: any, key: string) => void; + private order: any[]; + constructor(limit = 50, destroy?: callback) { + this.limit = limit; + this.destroy = destroy || this.defaultDestroy; + this.order = []; + this.clear(); + } + + public clear() { + this.order.forEach((key) => { + this.delete(key); + }); + this.cache = {}; + // access/update order, first item is oldest, last item is newest + this.order = []; + } + + public get(key: string) { + const value = this.cache[key]; + if (value) { + // update order + this.deleteOrder(key); + this.appendOrder(key); + } + return value; + } + + public set(key: string, value: any) { + if (!this.cache[key]) { + // if reach limit, delete the oldest + if (Object.keys(this.cache).length === this.limit) { + this.delete(this.order[0]); + } + + this.cache[key] = value; + this.appendOrder(key); + } else { + // if found in cache, delete the old one, insert new one to the first of list + this.delete(key); + + this.cache[key] = value; + this.appendOrder(key); + } + } + + public delete(key: string) { + const value = this.cache[key]; + if (value) { + this.deleteCache(key); + this.deleteOrder(key); + this.destroy(value, key); + } + } + + private deleteCache(key: string) { + delete this.cache[key]; + } + + private deleteOrder(key: string) { + const index = this.order.findIndex((o) => o === key); + if (index >= 0) { + this.order.splice(index, 1); + } + } + + private appendOrder(key: string) { + this.order.push(key); + } + private defaultDestroy(value: any, key: string) { + return null; + } +} diff --git a/stories/MapAdaptor/components/css/l7.css b/stories/MapAdaptor/components/css/l7.css new file mode 100644 index 0000000000..1d573ca53b --- /dev/null +++ b/stories/MapAdaptor/components/css/l7.css @@ -0,0 +1,400 @@ +.l7-marker { + position: absolute !important; + top: 0; + left: 0; + z-index: 5; +} +.l7-popup-anchor-top, +.l7-popup-anchor-top-left, +.l7-popup-anchor-top-right { + -webkit-flex-direction: column; + flex-direction: column; +} + +.l7-popup-anchor-bottom, +.l7-popup-anchor-bottom-left, +.l7-popup-anchor-bottom-right { + -webkit-flex-direction: column-reverse; + flex-direction: column-reverse; +} + +.l7-popup-anchor-left { + -webkit-flex-direction: row; + flex-direction: row; +} + +.l7-popup-anchor-right { + -webkit-flex-direction: row-reverse; + flex-direction: row-reverse; +} +.l7-popup { + position: absolute; + top: 0; + left: 0; + display: -webkit-flex; + display: flex; + will-change: transform; + pointer-events: none; + z-index: 5; +} +.l7-popup-tip { + width: 0; + height: 0; + border: 10px solid transparent; + z-index: 1; +} +.l7-popup-anchor-top .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-top: none; + border-bottom-color: #fff; +} + +.l7-popup-anchor-top-left .l7-popup-tip { + -webkit-align-self: flex-start; + align-self: flex-start; + border-top: none; + border-left: none; + border-bottom-color: #fff; +} + +.l7-popup-anchor-top-right .l7-popup-tip { + -webkit-align-self: flex-end; + align-self: flex-end; + border-top: none; + border-right: none; + border-bottom-color: #fff; +} + +.l7-popup-anchor-bottom .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-bottom: none; + border-top-color: #fff; +} + +.l7-popup-anchor-bottom-left .l7-popup-tip { + -webkit-align-self: flex-start; + align-self: flex-start; + border-bottom: none; + border-left: none; + border-top-color: #fff; +} + +.l7-popup-anchor-bottom-right .l7-popup-tip { + -webkit-align-self: flex-end; + align-self: flex-end; + border-bottom: none; + border-right: none; + border-top-color: #fff; +} + +.l7-popup-anchor-left .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-left: none; + border-right-color: #fff; +} + +.l7-popup-anchor-right .l7-popup-tip { + -webkit-align-self: center; + align-self: center; + border-right: none; + border-left-color: #fff; +} + +.l7-popup-close-button { + position: absolute; + right: 0; + top: 0; + border: 0; + padding: 0; + font-size: 25px; + line-height: 20px; + border-radius: 0 3px 0 0; + cursor: pointer; + background-color: transparent; +} + +.l7-popup-close-button:hover { + background-color: rgba(0, 0, 0, 0.05); +} + +.l7-popup-content { + position: relative; + background: #fff; + border-radius: 3px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + padding: 10px 10px 15px; + pointer-events: auto; +} + +.l7-popup-anchor-top-left .l7-popup-content { + border-top-left-radius: 0; +} + +.l7-popup-anchor-top-right .l7-popup-content { + border-top-right-radius: 0; +} + +.l7-popup-anchor-bottom-left .l7-popup-content { + border-bottom-left-radius: 0; +} + +.l7-popup-anchor-bottom-right .l7-popup-content { + border-bottom-right-radius: 0; +} + +.l7-popup-track-pointer { + display: none; +} + +.l7-popup-track-pointer * { + pointer-events: none; + user-select: none; +} + +.l7-map:hover .l7-popup-track-pointer { + display: flex; +} + +.l7-map:active .l7-popup-track-pointer { + display: none; +} + +.l7-popup-close-button { + position: absolute; + right: 0; + top: 0; + border: 0; + border-radius: 0 3px 0 0; + cursor: pointer; + background-color: transparent; +} + +.l7-popup-close-button:hover { + background-color: rgba(0, 0, 0, 0.05); +} + +.l7-popup-content { + position: relative; + background: #fff; + border-radius: 3px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + padding: 10px 10px 15px; + pointer-events: auto; +} + +/* general toolbar styles */ + +.l7-bar { + box-shadow: 0 1px 5px rgba(0,0,0,0.65); + border-radius: 4px; + } +.l7-bar a, +.l7-bar a:hover { + background-color: #fff; + width: 36px; + height: 36px; + line-height: 30px; + font-size: 30px; + display: block; + text-align: center; + text-decoration: none; + color: #8E9DAB; + } +.l7-bar a, +.l7-control-layers-toggle { + background-position: 50% 50%; + background-repeat: no-repeat; + display: block; + } +.l7-bar a:hover { + background-color: #f4f4f4; + } +.l7-bar a:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } +.l7-bar a:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom: none; + } +.l7-bar a.l7-disabled { + cursor: default; + background-color: #f4f4f4; + color: #bbb; + } + + +/* control positioning */ + +.l7-control-container { + font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; +} +.l7-control-hide { + display: none; +} +.l7-control { + position: relative; + z-index: 800; + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ + pointer-events: auto; + } +.l7-top, +.l7-bottom { + position: absolute; + z-index: 1000; + pointer-events: none; + } +.l7-top { + top: 0; + } +.l7-right { + right: 0; + } +.l7-bottom { + bottom: 0; + } +.l7-left { + left: 0; + } +.l7-control { + float: left; + clear: both; + } +.l7-right .l7-control { + float: right; + } +.l7-top .l7-control { + margin-top: 10px; + } +.l7-bottom .l7-control { + margin-bottom: 10px; + } +.l7-left .l7-control { + margin-left: 10px; + } +.l7-right .l7-control { + margin-right: 10px; + } + + /* attribution and scale controls */ + +.l7-control-container .l7-control-attribution { + background: #fff; + background: rgba(255, 255, 255, 0.7); + margin: 0; + } +.l7-control-attribution, +.l7-control-scale-line { + padding: 0 5px; + color: #333; + } +.l7-control-attribution a { + text-decoration: none; + } +.l7-control-attribution a:hover { + text-decoration: underline; + } +.l7-container .l7-control-attribution, +.l7-container .l7-control-scale { + font-size: 11px; + padding: 5px 5px 2px 5px; + background: rgba(255, 255, 255, 0.7); + } +.l7-left .l7-control-scale { + margin-left: 5px; + } +.l7-bottom .l7-control-scale { + margin-bottom: 5px; + } +.l7-control-scale-line { + border: 2px solid #8E9DAB; + border-top: none; + color: #8e9dab; + line-height: 1.1; + padding: 2px 5px 1px; + font-size: 11px; + white-space: nowrap; + overflow: hidden; + -moz-box-sizing: border-box; + box-sizing: border-box; + + background: #fff; + background: rgba(255, 255, 255, 0.8); + } +.l7-control-scale-line:not(:first-child) { + border-top: 2px solid #777; + border-bottom: none; + margin-top: -2px; + } +.l7-control-scale-line:not(:first-child):not(:last-child) { + border-bottom: 2px solid #777; + } + +.l7-touch .l7-control-attribution, +.l7-touch .l7-control-layers, +.l7-touch .l7-bar { + box-shadow: none; + } +.l7-touch .l7-control-layers, +.l7-touch .l7-bar { + border: 2px solid rgba(0,0,0,0.2); + background-clip: padding-box; + } + + + /* layers control */ + +.l7-control-layers { + box-shadow: 0 1px 5px rgba(0,0,0,0.4); + background: #fff; + border-radius: 5px; + } +.l7-control-layers-toggle { + background-image: url(../images/layers.svg); + width: 36px; + height: 36px; + } +.l7-retina .l7-control-layers-toggle { + background-image: url(../images/layers.svg); + background-size: 26px 26px; + } +.l7-touch .l7-control-layers-toggle { + width: 44px; + height: 44px; + } +.l7-control-layers .l7-control-layers-list, +.l7-control-layers-expanded .l7-control-layers-toggle { + display: none; + } +.l7-control-layers-expanded .l7-control-layers-list { + display: block; + position: relative; + } +.l7-control-layers-expanded { + padding: 6px 10px 6px 6px; + color: #333; + background: #fff; + } +.l7-control-layers-scrollbar { + overflow-y: scroll; + overflow-x: hidden; + padding-right: 5px; + } +.l7-control-layers-selector { + margin-top: 2px; + position: relative; + top: 1px; + } +.l7-control-layers label { + display: block; + } +.l7-control-layers-separator { + height: 0; + border-top: 1px solid #ddd; + margin: 5px -10px 5px -6px; + } + diff --git a/stories/MapAdaptor/components/images/layers.svg b/stories/MapAdaptor/components/images/layers.svg new file mode 100644 index 0000000000..615f4172d7 --- /dev/null +++ b/stories/MapAdaptor/components/images/layers.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/stories/MapAdaptor/components/pointImage.tsx b/stories/MapAdaptor/components/pointImage.tsx index 2e63242142..a9d4cdd306 100644 --- a/stories/MapAdaptor/components/pointImage.tsx +++ b/stories/MapAdaptor/components/pointImage.tsx @@ -1,3 +1,5 @@ +import '!style-loader!css-loader!./css/l7.css'; +import { Marker, Popup, Scale, Zoom } from '@l7/component'; import { Point } from '@l7/layers'; import { Scene } from '@l7/scene'; import * as React from 'react'; @@ -15,7 +17,7 @@ export default class PointImage extends React.Component { id: 'map', pitch: 0, type: 'mapbox', - style: 'mapbox://styles/mapbox/streets-v9', + style: 'mapbox://styles/mapbox/dark-v10', zoom: 1, }); scene.addImage( @@ -23,26 +25,41 @@ export default class PointImage extends React.Component { 'https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*kzTMQqS2QdUAAAAAAAAAAABkARQnAQ', ); const pointLayer = new Point({}); - const p1 = { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - properties: {}, - geometry: { - type: 'Point', - coordinates: [83.671875, 44.84029065139799], - }, - }, - ], - }; + + // console.log(zoomControl); + // pointLayer .source(data) // .color('blue') .shape('00') - .size(14); + .size(40); scene.addLayer(pointLayer); scene.render(); + scene.on('loaded', () => { + const zoomControl = new Zoom({ + position: 'bottomright', + }); + const scaleControl = new Scale(); + const popup = new Popup({ + offsets: [0, 20], + }) + .setLnglat({ + lng: 120.19382669582967, + lat: 30.258134, + }) + .setText('hello') + .addTo(scene); + + const maker = new Marker(); + maker + .setLnglat({ + lng: 120.19382669582967, + lat: 30.258134, + }) + .addTo(scene); + scene.addControl(zoomControl); + scene.addControl(scaleControl); + }); } public render() { diff --git a/yarn.lock b/yarn.lock index 77e340c5fa..f394d1ab61 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2057,7 +2057,7 @@ resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2" integrity sha1-ioP5M1x4YO/6Lu7KJUMyqgru2PI= -"@mapbox/tiny-sdf@^1.1.0": +"@mapbox/tiny-sdf@^1.1.0", "@mapbox/tiny-sdf@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@mapbox/tiny-sdf/-/tiny-sdf-1.1.1.tgz#16a20c470741bfe9191deb336f46e194da4a91ff" integrity sha512-Ihn1nZcGIswJ5XGbgFAvVumOgWpvIjBX9jiRlIl46uQG9vJOF51ViBYHF95rEZupuyQbEmhLaDPLQlU7fUTsBg== @@ -2690,12 +2690,20 @@ "@svgr/plugin-svgo" "^4.3.1" loader-utils "^1.2.3" +"@turf/distance@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@turf/distance/-/distance-6.0.1.tgz#0761f28784286e7865a427c4e7e3593569c2dea8" + integrity sha512-q7t7rWIWfkg7MP1Vt4uLjSEhe5rPfCO2JjpKmk7JC+QZKEQkuvHEqy3ejW1iC7Kw5ZcZNR3qdMGGz+6HnVwqvg== + dependencies: + "@turf/helpers" "6.x" + "@turf/invariant" "6.x" + "@turf/helpers@6.x", "@turf/helpers@^6.1.4": version "6.1.4" resolved "https://registry.yarnpkg.com/@turf/helpers/-/helpers-6.1.4.tgz#d6fd7ebe6782dd9c87dca5559bda5c48ae4c3836" integrity sha512-vJvrdOZy1ngC7r3MDA7zIGSoIgyrkWcGnNIEaqn/APmw+bVLF2gAW7HIsdTxd12s5wQMqEpqIQrmrbRRZ0xC7g== -"@turf/invariant@^6.1.2": +"@turf/invariant@6.x", "@turf/invariant@^6.1.2": version "6.1.2" resolved "https://registry.yarnpkg.com/@turf/invariant/-/invariant-6.1.2.tgz#6013ed6219f9ac2edada9b31e1dfa5918eb0a2f7" integrity sha512-WU08Ph8j0J2jVGlQCKChXoCtI50BB3yEH21V++V0T4cR1T27HKCxkehV2sYMwTierfMBgjwSwDIsxnR4/2mWXg== @@ -2769,6 +2777,11 @@ resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-1.0.36.tgz#e91129d7c02b1b814838d001e921e8b9a67153d0" integrity sha512-jbIWQ27QJcBNMZbQv0NSQMHnBDCmxghAxePxgyiPH1XPCRkOsTBei7jcdi3fDrUCGpCV3lKrSZFSlOkhUQVClA== +"@types/d3-hexbin@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@types/d3-hexbin/-/d3-hexbin-0.2.3.tgz#75a86a3d2e782ca3070ebcce789abdb88036abda" + integrity sha512-R/mmx2FJucHACRvGFoI+6jpr3mOAD6wuU2OjuCixxGRPHdmQNlMtBGBwxTmTs//kJvBzk9/vMhn47QOcY8Afrg== + "@types/d3-scale@^2.1.1": version "2.1.1" resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-2.1.1.tgz#405e58771ec6ae7b8f7b4178ee1887620759e8f7" @@ -3761,6 +3774,14 @@ babel-plugin-const-enum@^0.0.2: "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-typescript" "^7.3.3" +babel-plugin-css-modules-transform@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/babel-plugin-css-modules-transform/-/babel-plugin-css-modules-transform-1.6.2.tgz#eecf4889637bf1c56cda25ee21df060775d1bd22" + integrity sha512-zBsI54N5n979vfYpqFzQ6oRwEiVcmLH5REyaincNW+Ecl52nvRsQPYIbDcJzHePrXI20YSRUw6G/qbPwZZDgfg== + dependencies: + css-modules-require-hook "^4.0.6" + mkdirp "^0.5.1" + babel-plugin-dynamic-import-node@2.3.0, babel-plugin-dynamic-import-node@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" @@ -3950,6 +3971,14 @@ babel-plugin-transform-minify-booleans@^6.9.4: resolved "https://registry.yarnpkg.com/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.9.4.tgz#acbb3e56a3555dd23928e4b582d285162dd2b198" integrity sha1-rLs+VqNVXdI5KOS1gtKFFi3SsZg= +babel-plugin-transform-postcss@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-postcss/-/babel-plugin-transform-postcss-0.3.0.tgz#1f2e5d047bbd0ce84ac443c4715003d3796ee237" + integrity sha512-Sn4coeHvw3PCc22KVWtrkFFTh/K3G1ZCl26O3HZyLBgna987oHqTjJui+ofVUmglaWqydmFiEQd999uXWrmQLQ== + dependencies: + debug "^2.6.0" + postcss-load-config "^1.1.0" + babel-plugin-transform-property-literals@^6.9.4: version "6.9.4" resolved "https://registry.yarnpkg.com/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.9.4.tgz#98c1d21e255736573f93ece54459f6ce24985d39" @@ -5000,7 +5029,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@~1.6.0: +concat-stream@^1.5.0, concat-stream@~1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -5267,6 +5296,19 @@ corejs-upgrade-webpack-plugin@^2.0.0: resolve-from "^5.0.0" webpack "^4.38.0" +cosmiconfig@^2.1.0, cosmiconfig@^2.1.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-2.2.2.tgz#6173cebd56fac042c1f4390edf7af6c07c7cb892" + integrity sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A== + dependencies: + is-directory "^0.3.1" + js-yaml "^3.4.3" + minimist "^1.2.0" + object-assign "^4.1.0" + os-homedir "^1.0.1" + parse-json "^2.2.0" + require-from-string "^1.1.0" + cosmiconfig@^5.0.0, cosmiconfig@^5.1.0, cosmiconfig@^5.2.0, cosmiconfig@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" @@ -5404,7 +5446,7 @@ css-loader@^2.1.1: postcss-value-parser "^3.3.0" schema-utils "^1.0.0" -css-loader@^3.0.0: +css-loader@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.2.0.tgz#bb570d89c194f763627fcf1f80059c6832d009b2" integrity sha512-QTF3Ud5H7DaZotgdcJjGMvyDj5F3Pn1j/sC6VBEOVp94cbwqyIBdcs/quzj4MC1BKQSrTpQznegH/5giYbhnCQ== @@ -5422,6 +5464,24 @@ css-loader@^3.0.0: postcss-value-parser "^4.0.0" schema-utils "^2.0.0" +css-modules-require-hook@^4.0.6: + version "4.2.3" + resolved "https://registry.yarnpkg.com/css-modules-require-hook/-/css-modules-require-hook-4.2.3.tgz#6792ca412b15e23e6f9be6a07dcef7f577ff904d" + integrity sha1-Z5LKQSsV4j5vm+agfc739Xf/kE0= + dependencies: + debug "^2.2.0" + generic-names "^1.0.1" + glob-to-regexp "^0.3.0" + icss-replace-symbols "^1.0.2" + lodash "^4.3.0" + postcss "^6.0.1" + postcss-modules-extract-imports "^1.0.0" + postcss-modules-local-by-default "^1.0.1" + postcss-modules-resolve-imports "^1.3.0" + postcss-modules-scope "^1.0.0" + postcss-modules-values "^1.1.1" + seekout "^1.0.1" + css-select-base-adapter@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" @@ -5447,6 +5507,15 @@ css-select@^2.0.0: domutils "^1.7.0" nth-check "^1.0.2" +css-selector-tokenizer@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz#a177271a8bca5019172f4f891fc6eed9cbf68d5d" + integrity sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA== + dependencies: + cssesc "^0.1.0" + fastparse "^1.1.1" + regexpu-core "^1.0.0" + css-to-react-native@^2.0.3: version "2.3.2" resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.3.2.tgz#e75e2f8f7aa385b4c3611c52b074b70a002f2e7d" @@ -5492,6 +5561,11 @@ csscolorparser@~1.0.2: resolved "https://registry.yarnpkg.com/csscolorparser/-/csscolorparser-1.0.3.tgz#b34f391eea4da8f3e98231e2ccd8df9c041f171b" integrity sha1-s085HupNqPPpgjHizNjfnAQfFxs= +cssesc@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" + integrity sha1-yBSQPkViM3GgR3tAEJqq++6t27Q= + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -5591,6 +5665,11 @@ d3-format@1: resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.1.tgz#c45f74b17c5a290c072a4ba7039dd19662cd5ce6" integrity sha512-TUswGe6hfguUX1CtKxyG2nymO+1lyThbkS1ifLX0Sr+dOQtAD5gkrffpHnx+yHNKUZ0Bmg5T4AjUQwugPDrm0g== +d3-hexbin@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/d3-hexbin/-/d3-hexbin-0.2.2.tgz#9c5837dacfd471ab05337a9e91ef10bfc4f98831" + integrity sha1-nFg32s/UcasFM3qeke8Qv8T5iDE= + d3-interpolate@1: version "1.3.2" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.3.2.tgz#417d3ebdeb4bc4efcc8fd4361c55e4040211fd68" @@ -6721,6 +6800,11 @@ fast-levenshtein@~2.0.4: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fastparse@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" + integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== + fastq@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.6.0.tgz#4ec8a38f4ac25f21492673adb7eae9cfef47d1c2" @@ -7148,6 +7232,13 @@ gaze@^1.0.0: dependencies: globule "^1.0.0" +generic-names@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/generic-names/-/generic-names-1.0.3.tgz#2d786a121aee508876796939e8e3bff836c20917" + integrity sha1-LXhqEhruUIh2eWk56OO/+DbCCRc= + dependencies: + loader-utils "^0.2.16" + genfun@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" @@ -7895,20 +7986,20 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -husky@^3.0.4: - version "3.0.5" - resolved "https://registry.yarnpkg.com/husky/-/husky-3.0.5.tgz#d7db27c346645a8dc52df02aa534a377ad7925e0" - integrity sha512-cKd09Jy9cDyNIvAdN2QQAP/oA21sle4FWXjIMDttailpLAYZuBE7WaPmhrkj+afS8Sj9isghAtFvWSQ0JiwOHg== +husky@^3.0.9: + version "3.0.9" + resolved "https://registry.yarnpkg.com/husky/-/husky-3.0.9.tgz#a2c3e9829bfd6b4957509a9500d2eef5dbfc8044" + integrity sha512-Yolhupm7le2/MqC1VYLk/cNmYxsSsqKkTyBhzQHhPK1jFnC89mmmNVuGtLNabjDI6Aj8UNIr0KpRNuBkiC4+sg== dependencies: chalk "^2.4.2" + ci-info "^2.0.0" cosmiconfig "^5.2.1" execa "^1.0.0" get-stdin "^7.0.0" - is-ci "^2.0.0" opencollective-postinstall "^2.0.2" pkg-dir "^4.2.0" please-upgrade-node "^3.2.0" - read-pkg "^5.1.1" + read-pkg "^5.2.0" run-node "^1.0.0" slash "^3.0.0" @@ -7919,11 +8010,18 @@ iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv- dependencies: safer-buffer ">= 2.1.2 < 3" -icss-replace-symbols@^1.1.0: +icss-replace-symbols@^1.0.2, icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= +icss-utils@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-3.0.1.tgz#ee70d3ae8cac38c6be5ed91e851b27eed343ad0f" + integrity sha1-7nDTroysOMa+XtkehRsn7tNDrQ8= + dependencies: + postcss "^6.0.2" + icss-utils@^4.0.0, icss-utils@^4.1.0, icss-utils@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" @@ -9050,7 +9148,7 @@ js-tokens@^3.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-yaml@^3.13.1: +js-yaml@^3.13.1, js-yaml@^3.4.3: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -9565,7 +9663,7 @@ lodash@4.17.14: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw== -lodash@4.17.15, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@~4.17.10: +lodash@4.17.15, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.10: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -10892,7 +10990,7 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -os-homedir@^1.0.0: +os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= @@ -10921,11 +11019,6 @@ os-name@^3.0.0: macos-release "^2.2.0" windows-release "^3.1.0" -os-shim@^0.1.2: - version "0.1.3" - resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" - integrity sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc= - os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -11456,6 +11549,16 @@ postcss-less@^3.1.0: dependencies: postcss "^7.0.14" +postcss-load-config@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-1.2.0.tgz#539e9afc9ddc8620121ebf9d8c3673e0ce50d28a" + integrity sha1-U56a/J3chiASHr+djDZz4M5Q0oo= + dependencies: + cosmiconfig "^2.1.0" + object-assign "^4.1.0" + postcss-load-options "^1.2.0" + postcss-load-plugins "^2.3.0" + postcss-load-config@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.0.tgz#c84d692b7bb7b41ddced94ee62e8ab31b417b003" @@ -11464,6 +11567,22 @@ postcss-load-config@^2.0.0: cosmiconfig "^5.0.0" import-cwd "^2.0.0" +postcss-load-options@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-load-options/-/postcss-load-options-1.2.0.tgz#b098b1559ddac2df04bc0bb375f99a5cfe2b6d8c" + integrity sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw= + dependencies: + cosmiconfig "^2.1.0" + object-assign "^4.1.0" + +postcss-load-plugins@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz#745768116599aca2f009fad426b00175049d8d92" + integrity sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI= + dependencies: + cosmiconfig "^2.1.1" + object-assign "^4.1.0" + postcss-loader@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" @@ -11487,6 +11606,13 @@ postcss-media-query-parser@^0.2.3: resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244" integrity sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ= +postcss-modules-extract-imports@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz#dc87e34148ec7eab5f791f7cd5849833375b741a" + integrity sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw== + dependencies: + postcss "^6.0.1" + postcss-modules-extract-imports@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" @@ -11494,6 +11620,14 @@ postcss-modules-extract-imports@^2.0.0: dependencies: postcss "^7.0.5" +postcss-modules-local-by-default@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" + integrity sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk= + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" + postcss-modules-local-by-default@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz#dd9953f6dd476b5fd1ef2d8830c8929760b56e63" @@ -11513,6 +11647,23 @@ postcss-modules-local-by-default@^3.0.2: postcss-selector-parser "^6.0.2" postcss-value-parser "^4.0.0" +postcss-modules-resolve-imports@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/postcss-modules-resolve-imports/-/postcss-modules-resolve-imports-1.3.0.tgz#398d3000b95ae969420cdf4cd83fa8067f1c5eae" + integrity sha1-OY0wALla6WlCDN9M2D+oBn8cXq4= + dependencies: + css-selector-tokenizer "^0.7.0" + icss-utils "^3.0.1" + minimist "^1.2.0" + +postcss-modules-scope@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" + integrity sha1-1upkmUx5+XtipytCb75gVqGUu5A= + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" + postcss-modules-scope@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz#ad3f5bf7856114f6fcab901b0502e2a2bc39d4eb" @@ -11521,6 +11672,14 @@ postcss-modules-scope@^2.1.0: postcss "^7.0.6" postcss-selector-parser "^6.0.0" +postcss-modules-values@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" + integrity sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA= + dependencies: + icss-replace-symbols "^1.1.0" + postcss "^6.0.1" + postcss-modules-values@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz#479b46dc0c5ca3dc7fa5270851836b9ec7152f64" @@ -11537,6 +11696,13 @@ postcss-modules-values@^3.0.0: icss-utils "^4.0.0" postcss "^7.0.6" +postcss-plugin@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postcss-plugin/-/postcss-plugin-1.0.0.tgz#f763814565b87b93e13449fcf9d75941c566b070" + integrity sha1-92OBRWW4e5PhNEn8+ddZQcVmsHA= + dependencies: + postcss "^6.0.8" + postcss-reporter@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/postcss-reporter/-/postcss-reporter-6.0.1.tgz#7c055120060a97c8837b4e48215661aafb74245f" @@ -11607,6 +11773,15 @@ postcss-value-parser@^4.0.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9" integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ== +postcss@^6.0.1, postcss@^6.0.2, postcss@^6.0.8: + version "6.0.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" + integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag== + dependencies: + chalk "^2.4.1" + source-map "^0.6.1" + supports-color "^5.4.0" + postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.13, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.5, postcss@^7.0.6, postcss@^7.0.7: version "7.0.17" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.17.tgz#4da1bdff5322d4a0acaab4d87f3e782436bad31f" @@ -11616,20 +11791,20 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.13, postcss@^7.0.14, postcss@^7.0.1 source-map "^0.6.1" supports-color "^6.1.0" +postcss@^7.0.18: + version "7.0.18" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.18.tgz#4b9cda95ae6c069c67a4d933029eddd4838ac233" + integrity sha512-/7g1QXXgegpF+9GJj4iN7ChGF40sYuGYJ8WZu8DZWnmhQ/G36hfdk3q9LBJmoK+lZ+yzZ5KYpOoxq7LF1BxE8g== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + potpack@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.1.tgz#d1b1afd89e4c8f7762865ec30bd112ab767e2ebf" integrity sha512-15vItUAbViaYrmaB/Pbw7z6qX2xENbFSTA7Ii4tgbPtasxm5v6ryKhKtL91tpWovDJzTiZqdwzhcFBCwiMVdVw== -pre-commit@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/pre-commit/-/pre-commit-1.2.2.tgz#dbcee0ee9de7235e57f79c56d7ce94641a69eec6" - integrity sha1-287g7p3nI15X95xW186UZBpp7sY= - dependencies: - cross-spawn "^5.0.1" - spawn-sync "^1.0.15" - which "1.2.x" - prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -12356,7 +12531,7 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -read-pkg@^5.1.1: +read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== @@ -12521,7 +12696,7 @@ regenerate-unicode-properties@^8.1.0: dependencies: regenerate "^1.4.0" -regenerate@^1.4.0: +regenerate@^1.2.1, regenerate@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== @@ -12573,6 +12748,15 @@ regexp.prototype.flags@^1.2.0: dependencies: define-properties "^1.1.2" +regexpu-core@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" + integrity sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs= + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + regexpu-core@^4.5.4: version "4.5.5" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.5.tgz#aaffe61c2af58269b3e516b61a73790376326411" @@ -12585,11 +12769,23 @@ regexpu-core@^4.5.4: unicode-match-property-ecmascript "^1.0.4" unicode-match-property-value-ecmascript "^1.1.0" +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= + regjsgen@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA== +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw= + dependencies: + jsesc "~0.5.0" + regjsparser@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c" @@ -12742,6 +12938,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" + integrity sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg= + require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" @@ -13042,6 +13243,14 @@ schema-utils@^2.0.0: ajv "^6.10.2" ajv-keywords "^3.4.1" +schema-utils@^2.0.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.5.0.tgz#8f254f618d402cc80257486213c8970edfd7c22f" + integrity sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ== + dependencies: + ajv "^6.10.2" + ajv-keywords "^3.4.1" + scss-tokenizer@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" @@ -13050,6 +13259,11 @@ scss-tokenizer@^0.2.3: js-base64 "^2.1.8" source-map "^0.4.2" +seekout@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/seekout/-/seekout-1.0.2.tgz#09ba9f1bd5b46fbb134718eb19a68382cbb1b9c9" + integrity sha1-CbqfG9W0b7sTRxjrGaaDgsuxuck= + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -13479,14 +13693,6 @@ space-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.4.tgz#27910835ae00d0adfcdbd0ad7e611fb9544351fa" integrity sha512-UyhMSmeIqZrQn2UdjYpxEkwY9JUrn8pP+7L4f91zRzOQuI8MF1FGLfYU9DKCYeLdo7LXMxwrX5zKFy7eeeVHuA== -spawn-sync@^1.0.15: - version "1.0.15" - resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" - integrity sha1-sAeZVX63+wyDdsKdROih6mfldHY= - dependencies: - concat-stream "^1.4.7" - os-shim "^0.1.2" - spdx-correct@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" @@ -13882,6 +14088,14 @@ style-loader@^0.23.1: loader-utils "^1.1.0" schema-utils "^1.0.0" +style-loader@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.0.0.tgz#1d5296f9165e8e2c85d24eee0b7caf9ec8ca1f82" + integrity sha512-B0dOCFwv7/eY31a5PCieNwMgMhVGFe9w+rh7s/Bx8kfFkrth9zfTZquoYvdw8URgiqxObQKcpW51Ugz1HjfdZw== + dependencies: + loader-utils "^1.2.3" + schema-utils "^2.0.1" + style-search@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" @@ -14025,7 +14239,7 @@ supports-color@^3.2.3: dependencies: has-flag "^1.0.0" -supports-color@^5.2.0, supports-color@^5.3.0: +supports-color@^5.2.0, supports-color@^5.3.0, supports-color@^5.4.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -15173,13 +15387,6 @@ which@1, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: dependencies: isexe "^2.0.0" -which@1.2.x: - version "1.2.14" - resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" - integrity sha1-mofEN48D6CfOyvGs31bHNsAcFOU= - dependencies: - isexe "^2.0.0" - wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"