Feature: support provider

This commit is contained in:
Dreamacro 2019-12-13 10:17:17 +08:00
parent 6b41cbe93c
commit 83d1bcc848
30 changed files with 683 additions and 225 deletions

379
package-lock.json generated
View File

@ -5,9 +5,9 @@
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@babel/cli": { "@babel/cli": {
"version": "7.7.4", "version": "7.7.5",
"resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.7.4.tgz", "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.7.5.tgz",
"integrity": "sha512-O7mmzaWdm+VabWQmxuM8hqNrWGGihN83KfhPUzp2lAW4kzIMwBxujXkZbD4fMwKMYY9FXTbDvXsJqU+5XHXi4A==", "integrity": "sha512-y2YrMGXM3NUyu1Myg0pxg+Lx6g8XhEyvLHYNRwTBV6fDek3H7Io6b7N/LXscLs4HWn4HxMdy7f2rM1rTMp2mFg==",
"dev": true, "dev": true,
"requires": { "requires": {
"chokidar": "^2.1.8", "chokidar": "^2.1.8",
@ -39,15 +39,15 @@
} }
}, },
"@babel/core": { "@babel/core": {
"version": "7.7.4", "version": "7.7.5",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.4.tgz", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.5.tgz",
"integrity": "sha512-+bYbx56j4nYBmpsWtnPUsKW3NdnYxbqyfrP2w9wILBuHzdfIKz9prieZK0DFPyIzkjYVUe4QkusGL07r5pXznQ==", "integrity": "sha512-M42+ScN4+1S9iB6f+TL7QBpoQETxbclx+KNoKJABghnKYE+fMzSGqst0BZJc8CpI625bwPwYgUyRvxZ+0mZzpw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/code-frame": "^7.5.5", "@babel/code-frame": "^7.5.5",
"@babel/generator": "^7.7.4", "@babel/generator": "^7.7.4",
"@babel/helpers": "^7.7.4", "@babel/helpers": "^7.7.4",
"@babel/parser": "^7.7.4", "@babel/parser": "^7.7.5",
"@babel/template": "^7.7.4", "@babel/template": "^7.7.4",
"@babel/traverse": "^7.7.4", "@babel/traverse": "^7.7.4",
"@babel/types": "^7.7.4", "@babel/types": "^7.7.4",
@ -214,9 +214,9 @@
} }
}, },
"@babel/helper-module-transforms": { "@babel/helper-module-transforms": {
"version": "7.7.4", "version": "7.7.5",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.7.4.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.7.5.tgz",
"integrity": "sha512-ehGBu4mXrhs0FxAqN8tWkzF8GSIGAiEumu4ONZ/hD9M88uHcD+Yu2ttKfOCgwzoesJOJrtQh7trI5YPbRtMmnA==", "integrity": "sha512-A7pSxyJf1gN5qXVcidwLWydjftUN878VkalhXX5iQDuGyiGK3sOrrKKHF4/A4fwHtnsotv/NipwAeLzY4KQPvw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-module-imports": "^7.7.4", "@babel/helper-module-imports": "^7.7.4",
@ -330,9 +330,9 @@
} }
}, },
"@babel/parser": { "@babel/parser": {
"version": "7.7.4", "version": "7.7.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz",
"integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==",
"dev": true "dev": true
}, },
"@babel/plugin-proposal-async-generator-functions": { "@babel/plugin-proposal-async-generator-functions": {
@ -599,23 +599,23 @@
} }
}, },
"@babel/plugin-transform-modules-amd": { "@babel/plugin-transform-modules-amd": {
"version": "7.7.4", "version": "7.7.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.4.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.5.tgz",
"integrity": "sha512-/542/5LNA18YDtg1F+QHvvUSlxdvjZoD/aldQwkq+E3WCkbEjNSN9zdrOXaSlfg3IfGi22ijzecklF/A7kVZFQ==", "integrity": "sha512-CT57FG4A2ZUNU1v+HdvDSDrjNWBrtCmSH6YbbgN3Lrf0Di/q/lWRxZrE72p3+HCCz9UjfZOEBdphgC0nzOS6DQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-module-transforms": "^7.7.4", "@babel/helper-module-transforms": "^7.7.5",
"@babel/helper-plugin-utils": "^7.0.0", "@babel/helper-plugin-utils": "^7.0.0",
"babel-plugin-dynamic-import-node": "^2.3.0" "babel-plugin-dynamic-import-node": "^2.3.0"
} }
}, },
"@babel/plugin-transform-modules-commonjs": { "@babel/plugin-transform-modules-commonjs": {
"version": "7.7.4", "version": "7.7.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.4.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.5.tgz",
"integrity": "sha512-k8iVS7Jhc367IcNF53KCwIXtKAH7czev866ThsTgy8CwlXjnKZna2VHwChglzLleYrcHz1eQEIJlGRQxB53nqA==", "integrity": "sha512-9Cq4zTFExwFhQI6MT1aFxgqhIsMWQWDVwOgLzl7PTWJHsNaqFvklAU+Oz6AQLAS0dJKTwZSOCo20INwktxpi3Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-module-transforms": "^7.7.4", "@babel/helper-module-transforms": "^7.7.5",
"@babel/helper-plugin-utils": "^7.0.0", "@babel/helper-plugin-utils": "^7.0.0",
"@babel/helper-simple-access": "^7.7.4", "@babel/helper-simple-access": "^7.7.4",
"babel-plugin-dynamic-import-node": "^2.3.0" "babel-plugin-dynamic-import-node": "^2.3.0"
@ -731,9 +731,9 @@
} }
}, },
"@babel/plugin-transform-regenerator": { "@babel/plugin-transform-regenerator": {
"version": "7.7.4", "version": "7.7.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.4.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.5.tgz",
"integrity": "sha512-e7MWl5UJvmPEwFJTwkBlPmqixCtr9yAASBqff4ggXTNicZiwbF8Eefzm6NVgfiBp7JdAGItecnctKTgH44q2Jw==", "integrity": "sha512-/8I8tPvX2FkuEyWbjRCt4qTAgZK0DVy8QRguhA524UH48RfGJy94On2ri+dCuwOpcerPRl9O4ebQkRcVzIaGBw==",
"dev": true, "dev": true,
"requires": { "requires": {
"regenerator-transform": "^0.14.0" "regenerator-transform": "^0.14.0"
@ -806,9 +806,9 @@
} }
}, },
"@babel/preset-env": { "@babel/preset-env": {
"version": "7.7.4", "version": "7.7.6",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.4.tgz", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.6.tgz",
"integrity": "sha512-Dg+ciGJjwvC1NIe/DGblMbcGq1HOtKbw8RLl4nIjlfcILKEOkWT/vRqPpumswABEBVudii6dnVwrBtzD7ibm4g==", "integrity": "sha512-k5hO17iF/Q7tR9Jv8PdNBZWYW6RofxhnxKjBMc0nG4JTaWvOTiPoO/RLFwAKcA4FpmuBFm6jkoqaRJLGi0zdaQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-module-imports": "^7.7.4", "@babel/helper-module-imports": "^7.7.4",
@ -839,8 +839,8 @@
"@babel/plugin-transform-function-name": "^7.7.4", "@babel/plugin-transform-function-name": "^7.7.4",
"@babel/plugin-transform-literals": "^7.7.4", "@babel/plugin-transform-literals": "^7.7.4",
"@babel/plugin-transform-member-expression-literals": "^7.7.4", "@babel/plugin-transform-member-expression-literals": "^7.7.4",
"@babel/plugin-transform-modules-amd": "^7.7.4", "@babel/plugin-transform-modules-amd": "^7.7.5",
"@babel/plugin-transform-modules-commonjs": "^7.7.4", "@babel/plugin-transform-modules-commonjs": "^7.7.5",
"@babel/plugin-transform-modules-systemjs": "^7.7.4", "@babel/plugin-transform-modules-systemjs": "^7.7.4",
"@babel/plugin-transform-modules-umd": "^7.7.4", "@babel/plugin-transform-modules-umd": "^7.7.4",
"@babel/plugin-transform-named-capturing-groups-regex": "^7.7.4", "@babel/plugin-transform-named-capturing-groups-regex": "^7.7.4",
@ -848,7 +848,7 @@
"@babel/plugin-transform-object-super": "^7.7.4", "@babel/plugin-transform-object-super": "^7.7.4",
"@babel/plugin-transform-parameters": "^7.7.4", "@babel/plugin-transform-parameters": "^7.7.4",
"@babel/plugin-transform-property-literals": "^7.7.4", "@babel/plugin-transform-property-literals": "^7.7.4",
"@babel/plugin-transform-regenerator": "^7.7.4", "@babel/plugin-transform-regenerator": "^7.7.5",
"@babel/plugin-transform-reserved-words": "^7.7.4", "@babel/plugin-transform-reserved-words": "^7.7.4",
"@babel/plugin-transform-shorthand-properties": "^7.7.4", "@babel/plugin-transform-shorthand-properties": "^7.7.4",
"@babel/plugin-transform-spread": "^7.7.4", "@babel/plugin-transform-spread": "^7.7.4",
@ -858,7 +858,7 @@
"@babel/plugin-transform-unicode-regex": "^7.7.4", "@babel/plugin-transform-unicode-regex": "^7.7.4",
"@babel/types": "^7.7.4", "@babel/types": "^7.7.4",
"browserslist": "^4.6.0", "browserslist": "^4.6.0",
"core-js-compat": "^3.1.1", "core-js-compat": "^3.4.7",
"invariant": "^2.2.2", "invariant": "^2.2.2",
"js-levenshtein": "^1.1.3", "js-levenshtein": "^1.1.3",
"semver": "^5.5.0" "semver": "^5.5.0"
@ -1383,9 +1383,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "12.12.14", "version": "12.12.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.14.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.17.tgz",
"integrity": "sha512-u/SJDyXwuihpwjXy7hOOghagLEV1KdAST6syfnOk6QZAMzZuWZqXy5aYYZbh8Jdpd4escVFP0MvftHNDb9pruA==", "integrity": "sha512-Is+l3mcHvs47sKy+afn2O1rV4ldZFU7W8101cNlOd+MRbjM4Onida8jSZnJdTe/0Pcf25g9BNIUsuugmE6puHA==",
"dev": true "dev": true
}, },
"@types/parse-json": { "@types/parse-json": {
@ -1408,9 +1408,9 @@
"optional": true "optional": true
}, },
"@types/react": { "@types/react": {
"version": "16.9.13", "version": "16.9.16",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.13.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.16.tgz",
"integrity": "sha512-LikzRslbiufJYHyzbHSW0GrAiff8QYLMBFeZmSxzCYGXKxi8m/1PHX+rsVOwhr7mJNq+VIu2Dhf7U6mjFERK6w==", "integrity": "sha512-dQ3wlehuBbYlfvRXfF5G+5TbZF3xqgkikK7DWAsQXe2KnzV+kjD4W2ea+ThCrKASZn9h98bjjPzoTYzfRqyBkw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/prop-types": "*", "@types/prop-types": "*",
@ -2662,12 +2662,6 @@
"safe-buffer": "^5.1.1" "safe-buffer": "^5.1.1"
} }
}, },
"bluebird": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz",
"integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==",
"dev": true
},
"bmp-js": { "bmp-js": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
@ -3571,13 +3565,47 @@
"dev": true "dev": true
}, },
"core-js-compat": { "core-js-compat": {
"version": "3.4.5", "version": "3.5.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.4.5.tgz", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.5.0.tgz",
"integrity": "sha512-rYVvzvKJDKoefdAC+q6VP63vp5hMmeVONCi9pVUbU1qRrtVrmAk/nPhnRg+i+XFd775m1hpG2Yd5RY3X45ccuw==", "integrity": "sha512-E7iJB72svRjJTnm9HDvujzNVMCm3ZcDYEedkJ/sDTNsy/0yooCd9Cg7GSzE7b4e0LfIkjijdB1tqg0pGwxWeWg==",
"dev": true, "dev": true,
"requires": { "requires": {
"browserslist": "^4.7.3", "browserslist": "^4.8.2",
"semver": "^6.3.0" "semver": "^6.3.0"
},
"dependencies": {
"browserslist": {
"version": "4.8.2",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.2.tgz",
"integrity": "sha512-+M4oeaTplPm/f1pXDw84YohEv7B1i/2Aisei8s4s6k3QsoSHa7i5sz8u/cGQkkatCPxMASKxPualR4wwYgVboA==",
"dev": true,
"requires": {
"caniuse-lite": "^1.0.30001015",
"electron-to-chromium": "^1.3.322",
"node-releases": "^1.1.42"
}
},
"caniuse-lite": {
"version": "1.0.30001015",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001015.tgz",
"integrity": "sha512-/xL2AbW/XWHNu1gnIrO8UitBGoFthcsDgU9VLK1/dpsoxbaD5LscHozKze05R6WLsBvLhqv78dAPozMFQBYLbQ==",
"dev": true
},
"electron-to-chromium": {
"version": "1.3.322",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz",
"integrity": "sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA==",
"dev": true
},
"node-releases": {
"version": "1.1.42",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.42.tgz",
"integrity": "sha512-OQ/ESmUqGawI2PRX+XIRao44qWYBBfN54ImQYdWVTQqUckuejOg76ysSqDBK8NG3zwySRVnX36JwDQ6x+9GxzA==",
"dev": true,
"requires": {
"semver": "^6.3.0"
}
}
} }
}, },
"core-util-is": { "core-util-is": {
@ -3685,9 +3713,9 @@
"dev": true "dev": true
}, },
"css-loader": { "css-loader": {
"version": "3.2.0", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.2.0.tgz", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.3.2.tgz",
"integrity": "sha512-QTF3Ud5H7DaZotgdcJjGMvyDj5F3Pn1j/sC6VBEOVp94cbwqyIBdcs/quzj4MC1BKQSrTpQznegH/5giYbhnCQ==", "integrity": "sha512-4XSiURS+YEK2fQhmSaM1onnUm0VKWNf6WWBYjkp9YbSDGCBTVZ5XOM6Gkxo8tLgQlzkZOBJvk9trHlDk4gjEYg==",
"dev": true, "dev": true,
"requires": { "requires": {
"camelcase": "^5.3.1", "camelcase": "^5.3.1",
@ -3695,29 +3723,67 @@
"icss-utils": "^4.1.1", "icss-utils": "^4.1.1",
"loader-utils": "^1.2.3", "loader-utils": "^1.2.3",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
"postcss": "^7.0.17", "postcss": "^7.0.23",
"postcss-modules-extract-imports": "^2.0.0", "postcss-modules-extract-imports": "^2.0.0",
"postcss-modules-local-by-default": "^3.0.2", "postcss-modules-local-by-default": "^3.0.2",
"postcss-modules-scope": "^2.1.0", "postcss-modules-scope": "^2.1.1",
"postcss-modules-values": "^3.0.0", "postcss-modules-values": "^3.0.0",
"postcss-value-parser": "^4.0.0", "postcss-value-parser": "^4.0.2",
"schema-utils": "^2.0.0" "schema-utils": "^2.6.0"
}, },
"dependencies": { "dependencies": {
"postcss-value-parser": { "ajv": {
"version": "4.0.2", "version": "6.10.2",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
"integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==", "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
"dev": true
},
"schema-utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.1.0.tgz",
"integrity": "sha512-g6SViEZAfGNrToD82ZPUjq52KUPDYc+fN5+g6Euo5mLokl/9Yx14z0Cu4RR1m55HtBXejO0sBt+qw79axN+Fiw==",
"dev": true, "dev": true,
"requires": { "requires": {
"ajv": "^6.1.0", "fast-deep-equal": "^2.0.1",
"ajv-keywords": "^3.1.0" "fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"ajv-keywords": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
"integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
"dev": true
},
"postcss": {
"version": "7.0.24",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.24.tgz",
"integrity": "sha512-Xl0XvdNWg+CblAXzNvbSOUvgJXwSjmbAKORqyw9V2AlHrm1js2gFw9y3jibBAhpKZi8b5JzJCVh/FyzPsTtgTA==",
"dev": true,
"requires": {
"chalk": "^2.4.2",
"source-map": "^0.6.1",
"supports-color": "^6.1.0"
}
},
"schema-utils": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.1.tgz",
"integrity": "sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==",
"dev": true,
"requires": {
"ajv": "^6.10.2",
"ajv-keywords": "^3.4.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
},
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
} }
} }
} }
@ -4988,8 +5054,7 @@
"fast-deep-equal": { "fast-deep-equal": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
"dev": true
}, },
"fast-glob": { "fast-glob": {
"version": "2.2.7", "version": "2.2.7",
@ -6887,9 +6952,9 @@
} }
}, },
"immer": { "immer": {
"version": "5.0.0", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/immer/-/immer-5.0.0.tgz", "resolved": "https://registry.npmjs.org/immer/-/immer-5.0.1.tgz",
"integrity": "sha512-G7gRqKbi9NE025XVyqyTV98dxUOtdKvu/P1QRaVZfA55aEcXgjbxPdm+TlWdcSMNPKijlaHNz61DGPyelouRlA==" "integrity": "sha512-KFHV1ivrBmPCVRhjy9oBooypnPfJ876NTrWXMNoUhXFAaWWAViVqZ4l6HxPST52qcN82qqsR38/pCGYRWP5W7w=="
}, },
"import-cwd": { "import-cwd": {
"version": "2.1.0", "version": "2.1.0",
@ -8263,24 +8328,6 @@
"minipass": "^3.0.0" "minipass": "^3.0.0"
} }
}, },
"mississippi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
"integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
"dev": true,
"requires": {
"concat-stream": "^1.5.0",
"duplexify": "^3.4.2",
"end-of-stream": "^1.1.0",
"flush-write-stream": "^1.0.0",
"from2": "^2.1.0",
"parallel-transform": "^1.1.0",
"pump": "^3.0.0",
"pumpify": "^1.3.3",
"stream-each": "^1.1.0",
"through2": "^2.0.0"
}
},
"mixin-deep": { "mixin-deep": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
@ -9491,20 +9538,12 @@
"postcss": "^7.0.16", "postcss": "^7.0.16",
"postcss-selector-parser": "^6.0.2", "postcss-selector-parser": "^6.0.2",
"postcss-value-parser": "^4.0.0" "postcss-value-parser": "^4.0.0"
},
"dependencies": {
"postcss-value-parser": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz",
"integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==",
"dev": true
}
} }
}, },
"postcss-modules-scope": { "postcss-modules-scope": {
"version": "2.1.0", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz", "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.1.tgz",
"integrity": "sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==", "integrity": "sha512-OXRUPecnHCg8b9xWvldG/jUpRIGPNRka0r4D4j0ESUU2/5IOnpsjfPPmDprM3Ih8CgZ8FXjWqaniK5v4rWt3oQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"postcss": "^7.0.6", "postcss": "^7.0.6",
@ -10646,9 +10685,9 @@
} }
}, },
"serialize-javascript": { "serialize-javascript": {
"version": "2.1.0", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.0.tgz", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
"integrity": "sha512-a/mxFfU00QT88umAJQsNWOnUKckhNCqOl028N48e7wFmo2/EHpTo9Wso+iJJCMrQnmFvcjto5RJdAHEvVhcyUQ==", "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
"dev": true "dev": true
}, },
"serve-index": { "serve-index": {
@ -12151,9 +12190,9 @@
} }
}, },
"stylelint-webpack-plugin": { "stylelint-webpack-plugin": {
"version": "1.1.1", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/stylelint-webpack-plugin/-/stylelint-webpack-plugin-1.1.1.tgz", "resolved": "https://registry.npmjs.org/stylelint-webpack-plugin/-/stylelint-webpack-plugin-1.1.2.tgz",
"integrity": "sha512-1yBV1Bhfsl5Sdl1xJUUYveA21bUAqP4nEwtCmtv9x4AsR5SxMaBnYWS31vVr4OqmYn0qKB8hn9lmQqeXv5FOJA==", "integrity": "sha512-PvxFM8z614xNZW+opA733X8NkROCkH1ZkZZ7EBWwm7J+7Rwk/bIHiAqUqlM4VueXECPsCjrzxqDZnCE+EOYZxQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"arrify": "^2.0.1", "arrify": "^2.0.1",
@ -12312,6 +12351,14 @@
} }
} }
}, },
"swr": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/swr/-/swr-0.1.13.tgz",
"integrity": "sha512-5MjXCs1hWwkyPBCQ84Hcq0vlijj19I1R64Ma0q/xeF1MN0AZB+Kj5HUrIyVSDDUubvtq0KuaewVlhcytEHiqGw==",
"requires": {
"fast-deep-equal": "2.0.1"
}
},
"table": { "table": {
"version": "5.4.6", "version": "5.4.6",
"resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
@ -12417,9 +12464,9 @@
} }
}, },
"terser": { "terser": {
"version": "4.3.8", "version": "4.4.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.3.8.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.2.tgz",
"integrity": "sha512-otmIRlRVmLChAWsnSFNO0Bfk6YySuBp6G9qrHiJwlLDd4mxe2ta4sjI7TzIR+W1nBMjilzrMcPOz9pSusgx3hQ==", "integrity": "sha512-Uufrsvhj9O1ikwgITGsZ5EZS6qPokUOkCegS7fYOdGTv+OA90vndUbU6PEjr5ePqHfNUbGyMO7xyIZv2MhsALQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"commander": "^2.20.0", "commander": "^2.20.0",
@ -12436,18 +12483,18 @@
} }
}, },
"terser-webpack-plugin": { "terser-webpack-plugin": {
"version": "2.2.1", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.2.1.tgz", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.0.tgz",
"integrity": "sha512-jwdauV5Al7zopR6OAYvIIRcxXCSvLjZjr7uZE8l2tIWb/ryrGN48sJftqGf5k9z09tWhajx53ldp0XPI080YnA==", "integrity": "sha512-yez0HdpDf/iQVYGf+e/o8ZYWLb1g9d1nRRi5FIOZ4KfXbfSPT259UoqxPiSLhCnr0mlDoh+bucpYQSFbU0cEsQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"cacache": "^13.0.1", "cacache": "^13.0.1",
"find-cache-dir": "^3.0.0", "find-cache-dir": "^3.1.0",
"jest-worker": "^24.9.0", "jest-worker": "^24.9.0",
"schema-utils": "^2.5.0", "schema-utils": "^2.6.1",
"serialize-javascript": "^2.1.0", "serialize-javascript": "^2.1.2",
"source-map": "^0.6.1", "source-map": "^0.6.1",
"terser": "^4.3.9", "terser": "^4.4.2",
"webpack-sources": "^1.4.3" "webpack-sources": "^1.4.3"
}, },
"dependencies": { "dependencies": {
@ -12470,9 +12517,9 @@
"dev": true "dev": true
}, },
"find-cache-dir": { "find-cache-dir": {
"version": "3.0.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.0.0.tgz", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.2.0.tgz",
"integrity": "sha512-t7ulV1fmbxh5G9l/492O1p5+EBbr3uwpt6odhFTMc+nWyhmbloe+ja9BZ8pIBtqFWhOmCWVjx+pTW4zDkFoclw==", "integrity": "sha512-1JKclkYYsf1q9WIJKLZa9S9muC+08RIjzAlLrK4QcYLJMS6mk9yombQ9qf+zJ7H9LS800k0s44L4sDq9VYzqyg==",
"dev": true, "dev": true,
"requires": { "requires": {
"commondir": "^1.0.1", "commondir": "^1.0.1",
@ -12533,9 +12580,9 @@
} }
}, },
"schema-utils": { "schema-utils": {
"version": "2.5.0", "version": "2.6.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.5.0.tgz", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.1.tgz",
"integrity": "sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ==", "integrity": "sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==",
"dev": true, "dev": true,
"requires": { "requires": {
"ajv": "^6.10.2", "ajv": "^6.10.2",
@ -12547,17 +12594,6 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true "dev": true
},
"terser": {
"version": "4.3.9",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.3.9.tgz",
"integrity": "sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA==",
"dev": true,
"requires": {
"commander": "^2.20.0",
"source-map": "~0.6.1",
"source-map-support": "~0.5.12"
}
} }
} }
}, },
@ -12891,9 +12927,9 @@
} }
}, },
"typescript": { "typescript": {
"version": "3.7.2", "version": "3.7.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz",
"integrity": "sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==", "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==",
"dev": true "dev": true
}, },
"uglify-js": { "uglify-js": {
@ -13447,6 +13483,32 @@
"ssri": "^6.0.1", "ssri": "^6.0.1",
"unique-filename": "^1.1.1", "unique-filename": "^1.1.1",
"y18n": "^4.0.0" "y18n": "^4.0.0"
},
"dependencies": {
"bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"dev": true
},
"mississippi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
"integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
"dev": true,
"requires": {
"concat-stream": "^1.5.0",
"duplexify": "^3.4.2",
"end-of-stream": "^1.1.0",
"flush-write-stream": "^1.0.0",
"from2": "^2.1.0",
"parallel-transform": "^1.1.0",
"pump": "^3.0.0",
"pumpify": "^1.3.3",
"stream-each": "^1.1.0",
"through2": "^2.0.0"
}
}
} }
}, },
"lru-cache": { "lru-cache": {
@ -13458,12 +13520,6 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"serialize-javascript": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
"integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
"dev": true
},
"source-map": { "source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@ -13480,20 +13536,42 @@
} }
}, },
"terser-webpack-plugin": { "terser-webpack-plugin": {
"version": "1.4.1", "version": "1.4.3",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
"integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==", "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
"dev": true, "dev": true,
"requires": { "requires": {
"cacache": "^12.0.2", "cacache": "^12.0.2",
"find-cache-dir": "^2.1.0", "find-cache-dir": "^2.1.0",
"is-wsl": "^1.1.0", "is-wsl": "^1.1.0",
"schema-utils": "^1.0.0", "schema-utils": "^1.0.0",
"serialize-javascript": "^1.7.0", "serialize-javascript": "^2.1.2",
"source-map": "^0.6.1", "source-map": "^0.6.1",
"terser": "^4.1.2", "terser": "^4.1.2",
"webpack-sources": "^1.4.0", "webpack-sources": "^1.4.0",
"worker-farm": "^1.7.0" "worker-farm": "^1.7.0"
},
"dependencies": {
"terser": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.4.2.tgz",
"integrity": "sha512-Uufrsvhj9O1ikwgITGsZ5EZS6qPokUOkCegS7fYOdGTv+OA90vndUbU6PEjr5ePqHfNUbGyMO7xyIZv2MhsALQ==",
"dev": true,
"requires": {
"commander": "^2.20.0",
"source-map": "~0.6.1",
"source-map-support": "~0.5.12"
}
},
"worker-farm": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
"integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
"dev": true,
"requires": {
"errno": "~0.1.7"
}
}
} }
}, },
"yallist": { "yallist": {
@ -13880,15 +13958,6 @@
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
"dev": true "dev": true
}, },
"worker-farm": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
"integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
"dev": true,
"requires": {
"errno": "~0.1.7"
}
},
"wrap-ansi": { "wrap-ansi": {
"version": "5.1.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",

View File

@ -28,15 +28,15 @@
"contributors:generate": "all-contributors generate" "contributors:generate": "all-contributors generate"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.7.4", "@babel/cli": "^7.7.5",
"@babel/core": "^7.7.4", "@babel/core": "^7.7.5",
"@babel/preset-env": "^7.7.4", "@babel/preset-env": "^7.7.6",
"@babel/preset-react": "^7.7.4", "@babel/preset-react": "^7.7.4",
"@hot-loader/react-dom": "^16.11.0", "@hot-loader/react-dom": "^16.11.0",
"@types/classnames": "^2.2.8", "@types/classnames": "^2.2.8",
"@types/lodash-es": "^4.17.3", "@types/lodash-es": "^4.17.3",
"@types/node": "^12.12.14", "@types/node": "^12.12.17",
"@types/react": "^16.9.13", "@types/react": "^16.9.16",
"@types/react-dom": "^16.9.4", "@types/react-dom": "^16.9.4",
"@types/react-router-dom": "^5.1.3", "@types/react-router-dom": "^5.1.3",
"@types/react-virtualized-auto-sizer": "^1.0.0", "@types/react-virtualized-auto-sizer": "^1.0.0",
@ -46,7 +46,7 @@
"awesome-typescript-loader": "^5.2.1", "awesome-typescript-loader": "^5.2.1",
"babel-loader": "^8.0.6", "babel-loader": "^8.0.6",
"babel-preset-minify": "^0.5.1", "babel-preset-minify": "^0.5.1",
"css-loader": "^3.2.0", "css-loader": "^3.3.2",
"file-loader": "^5.0.2", "file-loader": "^5.0.2",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"image-webpack-loader": "^6.0.0", "image-webpack-loader": "^6.0.0",
@ -59,12 +59,12 @@
"style-loader": "^1.0.1", "style-loader": "^1.0.1",
"stylelint": "^12.0.0", "stylelint": "^12.0.0",
"stylelint-config-standard": "^19.0.0", "stylelint-config-standard": "^19.0.0",
"stylelint-webpack-plugin": "^1.1.1", "stylelint-webpack-plugin": "^1.1.2",
"terser-webpack-plugin": "^2.2.1", "terser-webpack-plugin": "^2.3.0",
"tslint": "^5.20.1", "tslint": "^5.20.1",
"tslint-config-standard": "^9.0.0", "tslint-config-standard": "^9.0.0",
"tslint-loader": "^3.6.0", "tslint-loader": "^3.6.0",
"typescript": "^3.7.2", "typescript": "^3.7.3",
"webpack": "^4.41.2", "webpack": "^4.41.2",
"webpack-cli": "^3.3.10", "webpack-cli": "^3.3.10",
"webpack-dev-middleware": "^3.7.2", "webpack-dev-middleware": "^3.7.2",
@ -77,7 +77,7 @@
"classnames": "^2.2.6", "classnames": "^2.2.6",
"dayjs": "^1.8.17", "dayjs": "^1.8.17",
"eventemitter3": "^4.0.0", "eventemitter3": "^4.0.0",
"immer": "^5.0.0", "immer": "^5.0.1",
"lodash-es": "^4.17.15", "lodash-es": "^4.17.15",
"react": "^16.12.0", "react": "^16.12.0",
"react-dom": "^16.12.0", "react-dom": "^16.12.0",
@ -86,6 +86,7 @@
"react-virtualized-auto-sizer": "^1.0.2", "react-virtualized-auto-sizer": "^1.0.2",
"react-window": "^1.8.5", "react-window": "^1.8.5",
"semver": "^6.3.0", "semver": "^6.3.0",
"swr": "^0.1.13",
"timeago.js": "^4.0.1", "timeago.js": "^4.0.1",
"unstated-next": "^1.1.0", "unstated-next": "^1.1.0",
"use-immer": "^0.3.5" "use-immer": "^0.3.5"

View File

@ -5,7 +5,7 @@ import './style.scss'
interface CardProps extends BaseComponentProps {} interface CardProps extends BaseComponentProps {}
export const Card: React.SFC<CardProps> = props => { export function Card (props: CardProps) {
const { className, style, children } = props const { className, style, children } = props
return ( return (
<div className={classnames('card', className)} style={style}> <div className={classnames('card', className)} style={style}>

View File

@ -12,7 +12,7 @@ interface ColProps extends BaseComponentProps {
span?: number span?: number
} }
export const Col: React.SFC<ColProps> = props => { export function Col (props: ColProps) {
const { const {
offset = 0, offset = 0,
order = 0, order = 0,

View File

@ -8,7 +8,7 @@ interface HeaderProps extends BaseComponentProps {
title: string title: string
} }
export const Header: React.SFC<HeaderProps> = props => { export function Header (props: HeaderProps) {
const { title, children, className, style } = props const { title, children, className, style } = props
return <header className={classnames('header', className)} style={style}> return <header className={classnames('header', className)} style={style}>

View File

@ -12,7 +12,7 @@ interface IconProps extends BaseComponentProps {
onClick?: React.FormEventHandler onClick?: React.FormEventHandler
} }
export const Icon: React.SFC<IconProps> = props => { export function Icon (props: IconProps) {
const { type, size = 14, className: cn, style: s } = props const { type, size = 14, className: cn, style: s } = props
const className = classnames('clash-iconfont', `icon-${type}`, cn) const className = classnames('clash-iconfont', `icon-${type}`, cn)
const style: React.CSSProperties = { fontSize: size, ...s } const style: React.CSSProperties = { fontSize: size, ...s }

View File

@ -0,0 +1,31 @@
import * as React from 'react'
import classnames from 'classnames'
import { BaseComponentProps } from '@models/BaseProps'
import './style.scss'
interface SpinnerProps extends BaseComponentProps {}
export function Spinner (props: SpinnerProps) {
const classname = classnames('spinner', props.className)
return (
<div className={classname}>
<div className="spinner-circle">
<div className="spinner-inner"></div>
</div>
<div className="spinner-circle">
<div className="spinner-inner"></div>
</div>
<div className="spinner-circle">
<div className="spinner-inner"></div>
</div>
<div className="spinner-circle">
<div className="spinner-inner"></div>
</div>
<div className="spinner-circle">
<div className="spinner-inner"></div>
</div>
</div>
)
}

View File

@ -0,0 +1,61 @@
@import '~@styles/variables';
.spinner {
position: relative;
width: 80px;
height: 80px;
border-radius: 100%;
animation: spinner 5s infinite linear;
}
.spinner-circle {
position: absolute;
width: 100%;
height: 100%;
transform-origin: 48% 48%;
}
.spinner-inner {
width: 100%;
height: 100%;
border-radius: 100%;
border: 5px solid transparentize($color-primary-dark, 0.3);
border-right: none;
border-top: none;
background-clip: padding-box;
box-shadow: inset 0 0 10px transparentize($color-primary-dark, 0.85);
}
@keyframes spinner {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.spinner-circle:nth-of-type(0) {
transform: rotate(0deg);
}
.spinner-circle:nth-of-type(0) .spinner-inner {
animation: spinner 2s infinite linear;
}
.spinner-circle:nth-of-type(1) {
transform: rotate(70deg);
}
.spinner-circle:nth-of-type(1) .spinner-inner {
animation: spinner 2s infinite linear;
}
.spinner-circle:nth-of-type(2) {
transform: rotate(140deg);
}
.spinner-circle:nth-of-type(2) .spinner-inner {
animation: spinner 2s infinite linear;
}

View File

@ -0,0 +1,35 @@
import React, { useState } from 'react'
import classnames from 'classnames'
import { BaseComponentProps } from '@models/BaseProps'
import { Spinner } from './Spinner'
import './style.scss'
interface LoadingProps extends BaseComponentProps {
visible: boolean
}
export function Loading (props: LoadingProps) {
const classname = classnames('loading', 'visible')
return props.visible
? (
<div className={classname}>
<Spinner />
</div>
)
: null
}
export function useLoading (initial: boolean) {
const [visible, setVisible] = useState(initial)
function hide () {
setVisible(false)
}
function show () {
setVisible(true)
}
return { visible, hide, show }
}

View File

@ -0,0 +1,13 @@
.loading {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba($color: #fff, $alpha: 0.9);
box-shadow: inset 0 0 80px rgba(0, 0, 0, 0.1);
z-index: 1000;
}

View File

@ -14,7 +14,7 @@ interface RowProps extends BaseComponentProps {
justify?: 'start' | 'end' | 'center' | 'space-around' | 'space-between' justify?: 'start' | 'end' | 'center' | 'space-around' | 'space-between'
} }
export const Row: React.SFC<RowProps> = props => { export function Row (props: RowProps) {
const { const {
gutter = 24, gutter = 24,
align = 'top', align = 'top',

View File

@ -139,7 +139,7 @@ interface OptionProps extends BaseComponentProps {
onClick?: (e: React.MouseEvent<HTMLLIElement>) => void onClick?: (e: React.MouseEvent<HTMLLIElement>) => void
} }
export const Option: React.SFC<OptionProps> = props => { export function Option (props: OptionProps) {
const { className: cn, style, key, disabled = false, children, onClick = () => {} } = props const { className: cn, style, key, disabled = false, children, onClick = () => {} } = props
const className = classnames('option', { disabled }, cn) const className = classnames('option', { disabled }, cn)

View File

@ -0,0 +1,18 @@
import * as React from 'react'
import classnames from 'classnames'
import { BaseComponentProps } from '@models/BaseProps'
import './style.scss'
interface TagProps extends BaseComponentProps {
color?: string
}
export function Tag (props: TagProps) {
const { color, className: cn, style: s } = props
const className = classnames('tag', cn)
const style: React.CSSProperties = { color, ...s }
const spanProps = { ...props, className, style }
return <span {...spanProps}>{ props.children }</span>
}

View File

@ -0,0 +1,14 @@
@import '~@styles/variables';
.tag {
display: flex;
align-items: center;
height: 24px;
font-size: 12px;
padding: 0 12px;
text-align: center;
background-color: #fff;
border: 2px solid $color-primary-dark;
color: $color-primary-dark;
border-radius: 12px;
}

View File

@ -13,3 +13,5 @@ export * from './Alert'
export * from './Button' export * from './Button'
export * from './Message' export * from './Message'
export * from './Checkbox' export * from './Checkbox'
export * from './Tag'
export * from './Loading'

View File

@ -1,7 +1,7 @@
import * as React from 'react' import * as React from 'react'
import { containers } from '@stores' import { containers } from '@stores'
import { changeProxySelected, Group as IGroup } from '@lib/request' import { changeProxySelected, Group as IGroup } from '@lib/request'
import { Tags } from '@components' import { Tags, Tag } from '@components'
import './style.scss' import './style.scss'
interface GroupProps { interface GroupProps {
@ -22,7 +22,7 @@ export function Group (props: GroupProps) {
<div className="proxy-group"> <div className="proxy-group">
<div className="proxy-group-part"> <div className="proxy-group-part">
<span className="proxy-group-name">{ config.name }</span> <span className="proxy-group-name">{ config.name }</span>
<span className="proxy-group-type">{ config.type }</span> <Tag className="proxy-group-type">{ config.type }</Tag>
</div> </div>
<div className="proxy-group-tags-container"> <div className="proxy-group-tags-container">
<Tags <Tags

View File

@ -21,17 +21,6 @@
width: 120px; width: 120px;
} }
.proxy-group-type {
display: block;
height: 24px;
line-height: 24px;
width: 100px;
text-align: center;
background-color: $color-primary-dark;
color: #fff;
border-radius: 3px;
}
.proxies-group-card { .proxies-group-card {
padding: 0; padding: 0;
} }

View File

@ -0,0 +1,60 @@
import * as React from 'react'
import { format } from 'timeago.js'
import { Card, Tag, Icon, Loading, useLoading } from '@components'
import { containers } from '@stores'
import { Provider as IProvider, Proxy as IProxy, updateProvider, healthCheckProvider } from '@lib/request'
import { Proxy } from '../Proxy'
import './style.scss'
interface ProvidersProps {
provider: IProvider
}
export function Provider (props: ProvidersProps) {
const { fetch } = containers.useData()
const { useTranslation, lang } = containers.useI18n()
const { provider } = props
const { t } = useTranslation('Proxies')
const { visible, hide, show } = useLoading(false)
function handleHealthChech () {
show()
healthCheckProvider(provider.name).then(() => fetch()).finally(() => hide())
}
function handleUpdate () {
show()
updateProvider(provider.name).then(() => fetch()).finally(() => hide())
}
return (
<Card className="proxy-provider">
<Loading visible={visible} />
<div className="proxy-provider-header">
<div className="proxy-provider-header-part">
<span className="proxy-provider-name">{ provider.name }</span>
<Tag>{ provider.vehicleType }</Tag>
</div>
<div className="proxy-provider-header-part">
{
provider.updatedAt &&
<span className="proxy-provider-update">{ `${t('providerUpdateTime')}: ${format(new Date(provider.updatedAt), lang)}`}</span>
}
<Icon className="proxy-provider-icon healthcheck" type="healthcheck" size={18} onClick={handleHealthChech} />
<Icon className="proxy-provider-icon" type="update" size={18} onClick={handleUpdate} />
</div>
</div>
<ul className="proxies-list">
{
provider.proxies.map((p: IProxy) => (
<li key={p.name}>
<Proxy className="proxy-provider-item" config={p} />
</li>
))
}
</ul>
</Card>
)
}

View File

@ -0,0 +1,62 @@
@import '~@styles/variables';
.proxy-provider {
position: relative;
display: flex;
flex-direction: column;
font-size: 16px;
padding: 20px;
color: $color-black-light;
}
.proxy-provider-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.proxy-provider-header-part {
display: flex;
align-items: center;
}
.proxy-provider-name {
margin-right: 24px;
}
.proxy-provider-proxies {
list-style: none;
}
.proxy-provider-item {
box-shadow: 0 0 24px 0 rgba(44, 138, 248, 0.2);
&:hover {
box-shadow: 0 0 24px 0 rgba(84, 117, 154, 0.4);
}
}
.proxy-provider-update {
line-height: 14px;
font-size: 14px;
}
.proxy-provider-icon {
margin-left: 20px;
cursor: pointer;
&.healthcheck {
color: $color-red;
}
}
@media (max-width: 768px) {
.proxy-provider-header {
flex-direction: column;
align-items: flex-start;
}
.proxy-provider-header-part {
margin: 6px 0;
}
}

View File

@ -1,2 +1,3 @@
export * from './Proxy' export * from './Proxy'
export * from './Group' export * from './Group'
export * from './Provider'

View File

@ -1,10 +1,12 @@
import React, { useLayoutEffect, useState, useMemo } from 'react' import React, { useMemo } from 'react'
import useSWR from 'swr'
import EE from '@lib/event' import EE from '@lib/event'
import { useRound } from '@lib/hook'
import { Card, Header, Icon } from '@components' import { Card, Header, Icon } from '@components'
import { containers } from '@stores' import { containers } from '@stores'
import * as API from '@lib/request' import * as API from '@lib/request'
import { Proxy, Group } from './components' import { Proxy, Group, Provider } from './components'
import './style.scss' import './style.scss'
enum sortType { enum sortType {
@ -29,15 +31,15 @@ export default function Proxies () {
const { data, fetch } = containers.useData() const { data, fetch } = containers.useData()
const { useTranslation } = containers.useI18n() const { useTranslation } = containers.useI18n()
const { t } = useTranslation('Proxies') const { t } = useTranslation('Proxies')
useSWR('data', fetch)
useLayoutEffect(() => {
fetch()
}, [])
function handleNotitySpeedTest () { function handleNotitySpeedTest () {
EE.notifySpeedTest() EE.notifySpeedTest()
} }
const [sort, setSort] = useState(sortType.None) const { current: sort, next } = useRound(
[sortType.None, sortType.Asc, sortType.Desc]
)
const proxies = useMemo(() => { const proxies = useMemo(() => {
switch (sort) { switch (sort) {
case sortType.Desc: case sortType.Desc:
@ -48,42 +50,61 @@ export default function Proxies () {
return data.proxy.slice() return data.proxy.slice()
} }
}, [sort, data]) }, [sort, data])
function handleSort () { const handleSort = next
setSort((sort + 1) % 3)
}
return ( return (
<div className="page"> <div className="page">
<div className="proxies-container"> {
<Header title={t('groupTitle')} /> data.proxyGroup.length !== 0 &&
<Card className="proxies-group-card"> <div className="proxies-container">
<ul className="proxies-group-list"> <Header title={t('groupTitle')} />
<Card className="proxies-group-card">
<ul className="proxies-group-list">
{
data.proxyGroup.map(p => (
<li className="proxies-group-item" key={p.name}>
<Group config={p} />
</li>
))
}
</ul>
</Card>
</div>
}
{
data.proxyProviders.length !== 0 &&
<div className="proxies-container">
<Header title={t('providerTitle')} />
<ul className="proxies-providers-list">
{ {
data.proxyGroup.map(p => ( data.proxyProviders.map(p => (
<li className="proxies-group-item" key={p.name}> <li className="proxies-providers-item" key={p.name}>
<Group config={p} /> <Provider provider={p} />
</li> </li>
)) ))
} }
</ul> </ul>
</Card> </div>
</div> }
<div className="proxies-container"> {
<Header title={t('title')}> proxies.length !== 0 &&
<Icon className="proxies-action-icon" type={sortMap[sort]} onClick={handleSort} size={20} /> <div className="proxies-container">
<Icon className="proxies-action-icon" type="speed" size={20} /> <Header title={t('title')}>
<span className="proxies-speed-test" onClick={handleNotitySpeedTest}>{t('speedTestText')}</span> <Icon className="proxies-action-icon" type={sortMap[sort]} onClick={handleSort} size={20} />
</Header> <Icon className="proxies-action-icon" type="speed" size={20} />
<ul className="proxies-list"> <span className="proxies-speed-test" onClick={handleNotitySpeedTest}>{t('speedTestText')}</span>
{ </Header>
proxies.map(p => ( <ul className="proxies-list">
<li key={p.name}> {
<Proxy config={p} /> proxies.map(p => (
</li> <li key={p.name}>
)) <Proxy config={p} />
} </li>
</ul> ))
</div> }
</ul>
</div>
}
</div> </div>
) )
} }

View File

@ -7,7 +7,6 @@
display: flex; display: flex;
margin-right: calc(-1 * var(--gap)); margin-right: calc(-1 * var(--gap));
margin-top: 20px; margin-top: 20px;
padding-bottom: 100px;
flex-wrap: wrap; flex-wrap: wrap;
align-content: flex-start; align-content: flex-start;
list-style: none; list-style: none;
@ -69,6 +68,14 @@
cursor: pointer; cursor: pointer;
} }
.proxies-providers-item {
margin: 20px 0;
}
.proxies-providers-list {
list-style: none;
}
@media (max-width: 768px) { @media (max-width: 768px) {
.proxies-group-card { .proxies-group-card {
margin: 12px 0; margin: 12px 0;

View File

@ -84,6 +84,8 @@ export default {
tls: 'TLS' tls: 'TLS'
}, },
groupTitle: 'Policy Group', groupTitle: 'Policy Group',
providerTitle: 'Providers',
providerUpdateTime: 'Last updated at',
expandText: 'Expand', expandText: 'Expand',
collapseText: 'Collapse', collapseText: 'Collapse',
speedTestText: 'Speed Test' speedTestText: 'Speed Test'

View File

@ -84,6 +84,8 @@ export default {
tls: 'TLS' tls: 'TLS'
}, },
groupTitle: '策略组', groupTitle: '策略组',
providerTitle: '代理集',
providerUpdateTime: '最后更新于',
expandText: '展开', expandText: '展开',
collapseText: '收起', collapseText: '收起',
speedTestText: '测速' speedTestText: '测速'

View File

@ -1,7 +1,7 @@
import { Draft } from 'immer' import { Draft } from 'immer'
import { useImmer } from 'use-immer' import { useImmer } from 'use-immer'
import { createContainer } from 'unstated-next' import { createContainer } from 'unstated-next'
import { useRef, useEffect } from 'react' import { useRef, useEffect, useState, useMemo } from 'react'
import { noop } from '@lib/helper' import { noop } from '@lib/helper'
@ -71,3 +71,19 @@ export function composeContainer<T, C extends containerFn<T>, U extends { [key:
}, {} as { [K in keyof U]: U[K] }) }, {} as { [K in keyof U]: U[K] })
} }
} }
export function useRound<T> (list: T[], defidx = 0) {
if (list.length < 2) {
throw new Error('List requires at least two elements')
}
const [state, setState] = useState(defidx)
function next () {
setState((state + 1) % list.length)
}
const current = useMemo(() => list[state], [state])
return { current, next }
}

View File

@ -31,6 +31,20 @@ export interface Proxies {
} }
} }
export interface Provider {
name: string
proxies: Array<Group | Proxy>
type: 'Proxy' | 'Rule'
vehicleType: 'HTTP' | 'File' | 'Compatible'
updatedAt?: string
}
export interface ProxyProviders {
providers: {
[key: string]: Provider
}
}
interface History { interface History {
time: string time: string
delay: number delay: number
@ -107,6 +121,33 @@ export async function updateRules () {
return req.put<void>('rules') return req.put<void>('rules')
} }
export async function getProxyProviders () {
const req = await getInstance()
return req.get<ProxyProviders>('providers/proxies', {
validateStatus (status) {
// compatible old version
return (status >= 200 && status < 300) || status === 404
}
})
// compatible old version
.then(resp => {
if (resp.status === 404) {
resp.data = { providers: {} }
}
return resp
})
}
export async function updateProvider (name: string) {
const req = await getInstance()
return req.put<void>(`providers/proxies/${name}`)
}
export async function healthCheckProvider (name: string) {
const req = await getInstance()
return req.get<void>(`providers/proxies/${name}/healthcheck`)
}
export async function getProxies () { export async function getProxies () {
const req = await getInstance() const req = await getInstance()
return req.get<Proxies>('proxies') return req.get<Proxies>('proxies')

View File

@ -115,5 +115,7 @@ export interface Data {
proxyGroup?: API.Group[] proxyGroup?: API.Group[]
proxyProviders?: API.Provider[]
rules?: API.Rule[] rules?: API.Rule[]
} }

View File

@ -12,6 +12,7 @@ function useData () {
general: {}, general: {},
proxy: [], proxy: [],
proxyGroup: [], proxyGroup: [],
proxyProviders: [],
rules: [] rules: []
}) })
@ -26,12 +27,12 @@ function useData () {
} }
async function fetch () { async function fetch () {
const [resp, err] = await to(Promise.all([API.getConfig(), API.getProxies(), API.getRules()])) const [resp, err] = await to(Promise.all([API.getConfig(), API.getProxies(), API.getRules(), API.getProxyProviders()]))
if (err && (!err.response || err.response.status === 401)) { if (err && (!err.response || err.response.status === 401)) {
show() show()
} }
const [{ data: general }, rawProxies, rules] = resp const [{ data: general }, rawProxies, rules, proxyProviders] = resp
set('general', { set('general', {
port: general.port, port: general.port,
@ -52,9 +53,15 @@ function useData () {
.map(key => ({ ...rawProxies.data.proxies[key], name: key })) .map(key => ({ ...rawProxies.data.proxies[key], name: key }))
const [proxy, groups] = partition(proxies, proxy => !policyGroup.has(proxy.type)) const [proxy, groups] = partition(proxies, proxy => !policyGroup.has(proxy.type))
const providers = Object.keys(proxyProviders.data.providers)
.map<API.Provider>(name => proxyProviders.data.providers[name])
.filter(pd => pd.name !== 'default')
.filter(pd => pd.vehicleType !== 'Compatible')
set({ set({
proxy: proxy as API.Proxy[], proxy: proxy as API.Proxy[],
proxyGroup: general.mode === 'Global' ? [proxyList] : groups as API.Group[], proxyGroup: general.mode === 'Global' ? [proxyList] : groups as API.Group[],
proxyProviders: providers,
rules: rules.data.rules rules: rules.data.rules
}) })

View File

@ -56,9 +56,9 @@ body {
} }
.page { .page {
padding: 20px 35px 30px 0; padding: 20px 35px 30px 20px;
width: 100%; width: 100%;
height: 100vh; min-height: 100vh;
margin: 0 auto; margin: 0 auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -6,7 +6,7 @@
@font-face { @font-face {
font-family: "clash-iconfont"; font-family: "clash-iconfont";
src: url('//at.alicdn.com/t/font_841708_w4jpsvny2x.ttf?t=1572182107550') format('truetype'); src: url('//at.alicdn.com/t/font_841708_ok9czskbhel.ttf?t=1576162884356') format('truetype');
} }
.clash-iconfont { .clash-iconfont {
@ -18,6 +18,10 @@
color: $color-primary-dark; color: $color-primary-dark;
} }
.icon-update::before { content: "\e66f"; }
.icon-healthcheck::before { content: "\e63c"; }
.icon-speed::before { content: "\e61b"; } .icon-speed::before { content: "\e61b"; }
.icon-close::before { content: "\e602"; } .icon-close::before { content: "\e602"; }