chore(codestyle): cleanup and reformat
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"extends": ["react-app", "react-app/jest"]
|
"extends": ["react-app", "react-app/jest", "prettier"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
var fs = __importStar(require("fs-extra"));
|
|
||||||
var child_process_1 = require("child_process");
|
var child_process_1 = require("child_process");
|
||||||
|
var fs = __importStar(require("fs-extra"));
|
||||||
var paths = __importStar(require("./paths"));
|
var paths = __importStar(require("./paths"));
|
||||||
// Do this as the first thing so that any code reading it knows the right env.
|
// Do this as the first thing so that any code reading it knows the right env.
|
||||||
process.env.BABEL_ENV = "production";
|
process.env.BABEL_ENV = "production";
|
||||||
|
|||||||
@@ -83,16 +83,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.createRadar = void 0;
|
exports.createRadar = void 0;
|
||||||
var fs_extra_1 = require("fs-extra");
|
|
||||||
var fs_1 = require("fs");
|
|
||||||
var path = __importStar(require("path"));
|
|
||||||
var front_matter_1 = __importDefault(require("front-matter"));
|
var front_matter_1 = __importDefault(require("front-matter"));
|
||||||
// @ts-ignore esModuleInterop is activated in tsconfig.scripts.json, but IDE typescript uses default typescript config
|
var fs_1 = require("fs");
|
||||||
var marked_1 = require("marked");
|
var fs_extra_1 = require("fs-extra");
|
||||||
var highlight_js_1 = __importDefault(require("highlight.js"));
|
var highlight_js_1 = __importDefault(require("highlight.js"));
|
||||||
var file_1 = require("./file");
|
var marked_1 = require("marked");
|
||||||
|
var path = __importStar(require("path"));
|
||||||
var model_1 = require("../../src/model");
|
var model_1 = require("../../src/model");
|
||||||
var paths_1 = require("../paths");
|
var paths_1 = require("../paths");
|
||||||
|
var file_1 = require("./file");
|
||||||
marked_1.marked.setOptions({
|
marked_1.marked.setOptions({
|
||||||
highlight: function (code) { return highlight_js_1.default.highlightAuto(code).value; },
|
highlight: function (code) { return highlight_js_1.default.highlightAuto(code).value; },
|
||||||
});
|
});
|
||||||
@@ -180,7 +179,7 @@ var createItems = function (revisions) {
|
|||||||
return __assign(__assign({}, items), (_a = {}, _a[revision.name] = addRevisionToItem(items[revision.name], revision), _a));
|
return __assign(__assign({}, items), (_a = {}, _a[revision.name] = addRevisionToItem(items[revision.name], revision), _a));
|
||||||
}, {});
|
}, {});
|
||||||
return Object.values(itemMap)
|
return Object.values(itemMap)
|
||||||
.map(function (item) { return (__assign(__assign({}, item), { "title": item.title || item.name })); })
|
.map(function (item) { return (__assign(__assign({}, item), { title: item.title || item.name })); })
|
||||||
.sort(function (x, y) { return (x.name > y.name ? 1 : -1); });
|
.sort(function (x, y) { return (x.name > y.name ? 1 : -1); });
|
||||||
};
|
};
|
||||||
var ignoreEmptyRevisionBody = function (revision, item) {
|
var ignoreEmptyRevisionBody = function (revision, item) {
|
||||||
@@ -208,7 +207,10 @@ var addRevisionToItem = function (item, revision) {
|
|||||||
return newItem;
|
return newItem;
|
||||||
};
|
};
|
||||||
var revisionCreatesNewHistoryEntry = function (revision, item) {
|
var revisionCreatesNewHistoryEntry = function (revision, item) {
|
||||||
return revision.body.trim() !== "" || (typeof revision.ring !== "undefined" && revision.ring !== item.ring) || (typeof revision.quadrant !== "undefined" && revision.quadrant !== item.quadrant);
|
return (revision.body.trim() !== "" ||
|
||||||
|
(typeof revision.ring !== "undefined" && revision.ring !== item.ring) ||
|
||||||
|
(typeof revision.quadrant !== "undefined" &&
|
||||||
|
revision.quadrant !== item.quadrant));
|
||||||
};
|
};
|
||||||
var flagItem = function (items, allReleases) {
|
var flagItem = function (items, allReleases) {
|
||||||
return items.map(function (item) {
|
return items.map(function (item) {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.appNodeModules = exports.appYarnLock = exports.appPublic = exports.appBuild = exports.appRdJson = exports.templateNodeModules = exports.templateBuild = exports.template = exports.radarJson = void 0;
|
exports.appNodeModules = exports.appYarnLock = exports.appPublic = exports.appBuild = exports.appRdJson = exports.templateNodeModules = exports.templateBuild = exports.template = exports.radarJson = void 0;
|
||||||
var path_1 = require("path");
|
|
||||||
var fs_1 = require("fs");
|
var fs_1 = require("fs");
|
||||||
|
var path_1 = require("path");
|
||||||
exports.radarJson = "rd.json";
|
exports.radarJson = "rd.json";
|
||||||
var appDirectory = (0, fs_1.realpathSync)(process.cwd());
|
var appDirectory = (0, fs_1.realpathSync)(process.cwd());
|
||||||
var resolveApp = function (relativePath) {
|
var resolveApp = function (relativePath) {
|
||||||
|
|||||||
389
package-lock.json
generated
389
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "aoe_technology_radar",
|
"name": "aoe_technology_radar",
|
||||||
"version": "3.3.2",
|
"version": "3.3.3",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "aoe_technology_radar",
|
"name": "aoe_technology_radar",
|
||||||
"version": "3.3.2",
|
"version": "3.3.3",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apideck/better-ajv-errors": "0.3.6",
|
"@apideck/better-ajv-errors": "0.3.6",
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
"@types/d3": "7.4.0",
|
"@types/d3": "7.4.0",
|
||||||
"@types/fs-extra": "9.0.13",
|
"@types/fs-extra": "9.0.13",
|
||||||
"@types/jest": "29.0.1",
|
"@types/jest": "29.0.1",
|
||||||
"@types/node": "17.0.33",
|
|
||||||
"@types/react": "18.0.19",
|
"@types/react": "18.0.19",
|
||||||
"@types/react-dom": "18.0.6",
|
"@types/react-dom": "18.0.6",
|
||||||
"@types/sanitize-html": "2.6.2",
|
"@types/sanitize-html": "2.6.2",
|
||||||
@@ -48,9 +47,14 @@
|
|||||||
"aoe_technology_radar-generateJson": "dist_scripts/scripts/generateJson.js"
|
"aoe_technology_radar-generateJson": "dist_scripts/scripts/generateJson.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@trivago/prettier-plugin-sort-imports": "3.3.0",
|
||||||
|
"@types/marked": "4.0.7",
|
||||||
|
"@types/node": "18.7.17",
|
||||||
"@typescript-eslint/parser": "5.37.0",
|
"@typescript-eslint/parser": "5.37.0",
|
||||||
"eslint": "8.23.1",
|
"eslint": "8.23.1",
|
||||||
|
"eslint-config-prettier": "8.5.0",
|
||||||
"eslint-config-react-app": "7.0.1",
|
"eslint-config-react-app": "7.0.1",
|
||||||
|
"eslint-plugin-prettier": "4.2.1",
|
||||||
"eslint-plugin-react": "7.31.8",
|
"eslint-plugin-react": "7.31.8",
|
||||||
"yallist": "4.0.0"
|
"yallist": "4.0.0"
|
||||||
}
|
}
|
||||||
@@ -3319,6 +3323,141 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@trivago/prettier-plugin-sort-imports": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-1y44bVZuIN0RsS3oIiGd5k8Vm3IZXYZnp4VsP2Z/S5L9WAOw43HE2clso66M2S/dDeJ+8sKPqnHsEfh39Vjs3w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/core": "7.17.8",
|
||||||
|
"@babel/generator": "7.17.7",
|
||||||
|
"@babel/parser": "7.17.8",
|
||||||
|
"@babel/traverse": "7.17.3",
|
||||||
|
"@babel/types": "7.17.0",
|
||||||
|
"javascript-natural-sort": "0.7.1",
|
||||||
|
"lodash": "4.17.21"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"prettier": "2.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/core": {
|
||||||
|
"version": "7.17.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz",
|
||||||
|
"integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@ampproject/remapping": "^2.1.0",
|
||||||
|
"@babel/code-frame": "^7.16.7",
|
||||||
|
"@babel/generator": "^7.17.7",
|
||||||
|
"@babel/helper-compilation-targets": "^7.17.7",
|
||||||
|
"@babel/helper-module-transforms": "^7.17.7",
|
||||||
|
"@babel/helpers": "^7.17.8",
|
||||||
|
"@babel/parser": "^7.17.8",
|
||||||
|
"@babel/template": "^7.16.7",
|
||||||
|
"@babel/traverse": "^7.17.3",
|
||||||
|
"@babel/types": "^7.17.0",
|
||||||
|
"convert-source-map": "^1.7.0",
|
||||||
|
"debug": "^4.1.0",
|
||||||
|
"gensync": "^1.0.0-beta.2",
|
||||||
|
"json5": "^2.1.2",
|
||||||
|
"semver": "^6.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/babel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/generator": {
|
||||||
|
"version": "7.17.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
|
||||||
|
"integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/types": "^7.17.0",
|
||||||
|
"jsesc": "^2.5.1",
|
||||||
|
"source-map": "^0.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/parser": {
|
||||||
|
"version": "7.17.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz",
|
||||||
|
"integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"parser": "bin/babel-parser.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/traverse": {
|
||||||
|
"version": "7.17.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz",
|
||||||
|
"integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/code-frame": "^7.16.7",
|
||||||
|
"@babel/generator": "^7.17.3",
|
||||||
|
"@babel/helper-environment-visitor": "^7.16.7",
|
||||||
|
"@babel/helper-function-name": "^7.16.7",
|
||||||
|
"@babel/helper-hoist-variables": "^7.16.7",
|
||||||
|
"@babel/helper-split-export-declaration": "^7.16.7",
|
||||||
|
"@babel/parser": "^7.17.3",
|
||||||
|
"@babel/types": "^7.17.0",
|
||||||
|
"debug": "^4.1.0",
|
||||||
|
"globals": "^11.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/types": {
|
||||||
|
"version": "7.17.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
|
||||||
|
"integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-validator-identifier": "^7.16.7",
|
||||||
|
"to-fast-properties": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/globals": {
|
||||||
|
"version": "11.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||||
|
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/semver": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@trivago/prettier-plugin-sort-imports/node_modules/source-map": {
|
||||||
|
"version": "0.5.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||||
|
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@trysound/sax": {
|
"node_modules/@trysound/sax": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
||||||
@@ -3916,15 +4055,21 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
||||||
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
|
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/marked": {
|
||||||
|
"version": "4.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.7.tgz",
|
||||||
|
"integrity": "sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/mime": {
|
"node_modules/@types/mime": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||||
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
|
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "17.0.33",
|
"version": "18.7.17",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.33.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.17.tgz",
|
||||||
"integrity": "sha512-miWq2m2FiQZmaHfdZNcbpp9PuXg34W5JZ5CrJ/BaS70VuhoJENBEQybeiYSaPBRNq6KQGnjfEnc/F3PN++D+XQ=="
|
"integrity": "sha512-0UyfUnt02zIuqp7yC8RYtDkp/vo8bFaQ13KkSEvUAohPOAlnVNbj5Fi3fgPSuwzakS+EvvnnZ4x9y7i6ASaSPQ=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/parse-json": {
|
"node_modules/@types/parse-json": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
@@ -7292,6 +7437,18 @@
|
|||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-config-prettier": {
|
||||||
|
"version": "8.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
|
||||||
|
"integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"eslint-config-prettier": "bin/cli.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint-config-react-app": {
|
"node_modules/eslint-config-react-app": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz",
|
||||||
@@ -7483,6 +7640,27 @@
|
|||||||
"node": ">=6.0"
|
"node": ">=6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-plugin-prettier": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"prettier-linter-helpers": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": ">=7.28.0",
|
||||||
|
"prettier": ">=2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"eslint-config-prettier": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint-plugin-react": {
|
"node_modules/eslint-plugin-react": {
|
||||||
"version": "7.31.8",
|
"version": "7.31.8",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz",
|
||||||
@@ -7958,6 +8136,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-diff": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/fast-glob": {
|
"node_modules/fast-glob": {
|
||||||
"version": "3.2.11",
|
"version": "3.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
||||||
@@ -9589,6 +9773,12 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/javascript-natural-sort": {
|
||||||
|
"version": "0.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
|
||||||
|
"integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/jest": {
|
"node_modules/jest": {
|
||||||
"version": "27.5.1",
|
"version": "27.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
|
||||||
@@ -12942,6 +13132,34 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin-prettier.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prettier-linter-helpers": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fast-diff": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pretty-bytes": {
|
"node_modules/pretty-bytes": {
|
||||||
"version": "5.6.0",
|
"version": "5.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
|
||||||
@@ -18345,6 +18563,109 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||||
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="
|
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="
|
||||||
},
|
},
|
||||||
|
"@trivago/prettier-plugin-sort-imports": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-1y44bVZuIN0RsS3oIiGd5k8Vm3IZXYZnp4VsP2Z/S5L9WAOw43HE2clso66M2S/dDeJ+8sKPqnHsEfh39Vjs3w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/core": "7.17.8",
|
||||||
|
"@babel/generator": "7.17.7",
|
||||||
|
"@babel/parser": "7.17.8",
|
||||||
|
"@babel/traverse": "7.17.3",
|
||||||
|
"@babel/types": "7.17.0",
|
||||||
|
"javascript-natural-sort": "0.7.1",
|
||||||
|
"lodash": "4.17.21"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/core": {
|
||||||
|
"version": "7.17.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz",
|
||||||
|
"integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@ampproject/remapping": "^2.1.0",
|
||||||
|
"@babel/code-frame": "^7.16.7",
|
||||||
|
"@babel/generator": "^7.17.7",
|
||||||
|
"@babel/helper-compilation-targets": "^7.17.7",
|
||||||
|
"@babel/helper-module-transforms": "^7.17.7",
|
||||||
|
"@babel/helpers": "^7.17.8",
|
||||||
|
"@babel/parser": "^7.17.8",
|
||||||
|
"@babel/template": "^7.16.7",
|
||||||
|
"@babel/traverse": "^7.17.3",
|
||||||
|
"@babel/types": "^7.17.0",
|
||||||
|
"convert-source-map": "^1.7.0",
|
||||||
|
"debug": "^4.1.0",
|
||||||
|
"gensync": "^1.0.0-beta.2",
|
||||||
|
"json5": "^2.1.2",
|
||||||
|
"semver": "^6.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@babel/generator": {
|
||||||
|
"version": "7.17.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
|
||||||
|
"integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/types": "^7.17.0",
|
||||||
|
"jsesc": "^2.5.1",
|
||||||
|
"source-map": "^0.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@babel/parser": {
|
||||||
|
"version": "7.17.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz",
|
||||||
|
"integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@babel/traverse": {
|
||||||
|
"version": "7.17.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz",
|
||||||
|
"integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/code-frame": "^7.16.7",
|
||||||
|
"@babel/generator": "^7.17.3",
|
||||||
|
"@babel/helper-environment-visitor": "^7.16.7",
|
||||||
|
"@babel/helper-function-name": "^7.16.7",
|
||||||
|
"@babel/helper-hoist-variables": "^7.16.7",
|
||||||
|
"@babel/helper-split-export-declaration": "^7.16.7",
|
||||||
|
"@babel/parser": "^7.17.3",
|
||||||
|
"@babel/types": "^7.17.0",
|
||||||
|
"debug": "^4.1.0",
|
||||||
|
"globals": "^11.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@babel/types": {
|
||||||
|
"version": "7.17.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
|
||||||
|
"integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/helper-validator-identifier": "^7.16.7",
|
||||||
|
"to-fast-properties": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"version": "11.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||||
|
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.5.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||||
|
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@trysound/sax": {
|
"@trysound/sax": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
||||||
@@ -18901,15 +19222,21 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
||||||
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
|
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
|
||||||
},
|
},
|
||||||
|
"@types/marked": {
|
||||||
|
"version": "4.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.7.tgz",
|
||||||
|
"integrity": "sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/mime": {
|
"@types/mime": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||||
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
|
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "17.0.33",
|
"version": "18.7.17",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.33.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.17.tgz",
|
||||||
"integrity": "sha512-miWq2m2FiQZmaHfdZNcbpp9PuXg34W5JZ5CrJ/BaS70VuhoJENBEQybeiYSaPBRNq6KQGnjfEnc/F3PN++D+XQ=="
|
"integrity": "sha512-0UyfUnt02zIuqp7yC8RYtDkp/vo8bFaQ13KkSEvUAohPOAlnVNbj5Fi3fgPSuwzakS+EvvnnZ4x9y7i6ASaSPQ=="
|
||||||
},
|
},
|
||||||
"@types/parse-json": {
|
"@types/parse-json": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
@@ -21450,6 +21777,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"eslint-config-prettier": {
|
||||||
|
"version": "8.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
|
||||||
|
"integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"eslint-config-react-app": {
|
"eslint-config-react-app": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz",
|
||||||
@@ -21599,6 +21933,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"eslint-plugin-prettier": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"prettier-linter-helpers": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"eslint-plugin-react": {
|
"eslint-plugin-react": {
|
||||||
"version": "7.31.8",
|
"version": "7.31.8",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz",
|
||||||
@@ -21861,6 +22204,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||||
},
|
},
|
||||||
|
"fast-diff": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"fast-glob": {
|
"fast-glob": {
|
||||||
"version": "3.2.11",
|
"version": "3.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
||||||
@@ -23013,6 +23362,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"javascript-natural-sort": {
|
||||||
|
"version": "0.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
|
||||||
|
"integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"version": "27.5.1",
|
"version": "27.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
|
||||||
@@ -25338,6 +25693,22 @@
|
|||||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||||
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
|
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
|
||||||
},
|
},
|
||||||
|
"prettier": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"prettier-linter-helpers": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fast-diff": "^1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"pretty-bytes": {
|
"pretty-bytes": {
|
||||||
"version": "5.6.0",
|
"version": "5.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
"@types/d3": "7.4.0",
|
"@types/d3": "7.4.0",
|
||||||
"@types/fs-extra": "9.0.13",
|
"@types/fs-extra": "9.0.13",
|
||||||
"@types/jest": "29.0.1",
|
"@types/jest": "29.0.1",
|
||||||
"@types/node": "17.0.33",
|
|
||||||
"@types/react": "18.0.19",
|
"@types/react": "18.0.19",
|
||||||
"@types/react-dom": "18.0.6",
|
"@types/react-dom": "18.0.6",
|
||||||
"@types/sanitize-html": "2.6.2",
|
"@types/sanitize-html": "2.6.2",
|
||||||
@@ -57,9 +56,14 @@
|
|||||||
"yaml": "2.1.1"
|
"yaml": "2.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@trivago/prettier-plugin-sort-imports": "3.3.0",
|
||||||
|
"@types/marked": "4.0.7",
|
||||||
|
"@types/node": "18.7.17",
|
||||||
"@typescript-eslint/parser": "5.37.0",
|
"@typescript-eslint/parser": "5.37.0",
|
||||||
"eslint": "8.23.1",
|
"eslint": "8.23.1",
|
||||||
|
"eslint-config-prettier": "8.5.0",
|
||||||
"eslint-config-react-app": "7.0.1",
|
"eslint-config-react-app": "7.0.1",
|
||||||
|
"eslint-plugin-prettier": "4.2.1",
|
||||||
"eslint-plugin-react": "7.31.8",
|
"eslint-plugin-react": "7.31.8",
|
||||||
"yallist": "4.0.0"
|
"yallist": "4.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
importOrder: ["^[./]"],
|
||||||
importOrderSeparation: true,
|
importOrderSeparation: true,
|
||||||
importOrderSortSpecifiers: true,
|
importOrderSortSpecifiers: true,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import * as fs from "fs-extra";
|
|
||||||
import { spawn } from "child_process";
|
import { spawn } from "child_process";
|
||||||
|
import * as fs from "fs-extra";
|
||||||
|
|
||||||
import * as paths from "./paths";
|
import * as paths from "./paths";
|
||||||
|
|
||||||
// Do this as the first thing so that any code reading it knows the right env.
|
// Do this as the first thing so that any code reading it knows the right env.
|
||||||
@@ -52,7 +52,7 @@ const buildTemplate = () => {
|
|||||||
|
|
||||||
buildTemplate().then(() => {
|
buildTemplate().then(() => {
|
||||||
fs.copySync(paths.templateBuild, paths.appBuild);
|
fs.copySync(paths.templateBuild, paths.appBuild);
|
||||||
fs.ensureDirSync(paths.appPublic)
|
fs.ensureDirSync(paths.appPublic);
|
||||||
fs.copySync(paths.appPublic, paths.appBuild);
|
fs.copySync(paths.appPublic, paths.appBuild);
|
||||||
console.log(`${paths.appBuild} was created and can be deployed.`);
|
console.log(`${paths.appBuild} was created and can be deployed.`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
import { copyFileSync, existsSync, mkdirSync, readFileSync } from "fs";
|
||||||
|
|
||||||
import { copyFileSync, mkdirSync, existsSync, readFileSync } from "fs";
|
|
||||||
import { createRadar } from "./generateJson/radar";
|
import { createRadar } from "./generateJson/radar";
|
||||||
|
|
||||||
// Do this as the first thing so that any code reading it knows the right env.
|
// Do this as the first thing so that any code reading it knows the right env.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import * as fs from "fs-extra";
|
import * as fs from "fs-extra";
|
||||||
|
|
||||||
import * as paths from "./paths";
|
import * as paths from "./paths";
|
||||||
|
|
||||||
// Do this as the first thing so that any code reading it knows the right env.
|
// Do this as the first thing so that any code reading it knows the right env.
|
||||||
|
|||||||
@@ -1,32 +1,39 @@
|
|||||||
import { readFile } from "fs-extra";
|
|
||||||
import { readFileSync } from "fs";
|
|
||||||
import * as path from "path";
|
|
||||||
import frontMatter from "front-matter";
|
import frontMatter from "front-matter";
|
||||||
// @ts-ignore esModuleInterop is activated in tsconfig.scripts.json, but IDE typescript uses default typescript config
|
import { readFileSync } from "fs";
|
||||||
import { marked } from "marked";
|
import { readFile } from "fs-extra";
|
||||||
import highlight from "highlight.js";
|
import highlight from "highlight.js";
|
||||||
|
import { marked } from "marked";
|
||||||
|
import * as path from "path";
|
||||||
|
|
||||||
import { radarPath, getAllMarkdownFiles } from "./file";
|
import {
|
||||||
import { Item, Revision, ItemAttributes, Radar, FlagType } from "../../src/model";
|
FlagType,
|
||||||
|
Item,
|
||||||
|
ItemAttributes,
|
||||||
|
Radar,
|
||||||
|
Revision,
|
||||||
|
} from "../../src/model";
|
||||||
import { appBuild } from "../paths";
|
import { appBuild } from "../paths";
|
||||||
|
import { getAllMarkdownFiles, radarPath } from "./file";
|
||||||
|
|
||||||
import type { ConfigData } from "../../src/config";
|
import type { ConfigData } from "../../src/config";
|
||||||
|
|
||||||
type FMAttributes = ItemAttributes;
|
|
||||||
|
|
||||||
marked.setOptions({
|
marked.setOptions({
|
||||||
highlight: (code: any) => highlight.highlightAuto(code).value,
|
highlight: (code: any) => highlight.highlightAuto(code).value,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const createRadar = async (): Promise<Radar> => {
|
export const createRadar = async (): Promise<Radar> => {
|
||||||
const fileNames = await getAllMarkdownFiles(radarPath());
|
const fileNames = await getAllMarkdownFiles(radarPath());
|
||||||
const revisions: (Revision|undefined)[] = await createRevisionsFromFiles(fileNames);
|
const revisions: (Revision | undefined)[] = await createRevisionsFromFiles(
|
||||||
const filterdRevisions : Revision[] = revisions.filter(r => r !== undefined) as Revision[];
|
fileNames
|
||||||
|
);
|
||||||
|
const filterdRevisions: Revision[] = revisions.filter(
|
||||||
|
(r) => r !== undefined
|
||||||
|
) as Revision[];
|
||||||
const allReleases = getAllReleases(filterdRevisions);
|
const allReleases = getAllReleases(filterdRevisions);
|
||||||
const items = createItems(filterdRevisions);
|
const items = createItems(filterdRevisions);
|
||||||
const flaggedItems = flagItem(items, allReleases);
|
const flaggedItems = flagItem(items, allReleases);
|
||||||
|
|
||||||
items.forEach(item => checkAttributes(item.name, item))
|
items.forEach((item) => checkAttributes(item.name, item));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: flaggedItems,
|
items: flaggedItems,
|
||||||
@@ -34,7 +41,7 @@ export const createRadar = async (): Promise<Radar> => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkAttributes = (fileName: string, attributes: FMAttributes) => {
|
const checkAttributes = (fileName: string, attributes: ItemAttributes) => {
|
||||||
const rawConf = readFileSync(path.resolve(appBuild, "config.json"), "utf-8");
|
const rawConf = readFileSync(path.resolve(appBuild, "config.json"), "utf-8");
|
||||||
const config = JSON.parse(rawConf) as ConfigData;
|
const config = JSON.parse(rawConf) as ConfigData;
|
||||||
|
|
||||||
@@ -61,16 +68,14 @@ const checkAttributes = (fileName: string, attributes: FMAttributes) => {
|
|||||||
} else {
|
} else {
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createRevisionsFromFiles = (fileNames: string[]) => {
|
const createRevisionsFromFiles = (fileNames: string[]) => {
|
||||||
const publicUrl = process.env.PUBLIC_URL;
|
const publicUrl = process.env.PUBLIC_URL;
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
fileNames.map(
|
fileNames.map((fileName) =>
|
||||||
(fileName) =>
|
readFile(fileName, "utf8").then((data) => {
|
||||||
readFile(fileName, "utf8").then(data => {
|
const fm = frontMatter<ItemAttributes>(data);
|
||||||
const fm = frontMatter<FMAttributes>(data);
|
|
||||||
let html = marked(fm.body.replace(/\]\(\//g, `](${publicUrl}/`));
|
let html = marked(fm.body.replace(/\]\(\//g, `](${publicUrl}/`));
|
||||||
html = html.replace(
|
html = html.replace(
|
||||||
/a href="http/g,
|
/a href="http/g,
|
||||||
@@ -120,7 +125,7 @@ const createItems = (revisions: Revision[]) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return Object.values(itemMap)
|
return Object.values(itemMap)
|
||||||
.map((item) => ({ ...item, "title": item.title || item.name }))
|
.map((item) => ({ ...item, title: item.title || item.name }))
|
||||||
.sort((x, y) => (x.name > y.name ? 1 : -1));
|
.sort((x, y) => (x.name > y.name ? 1 : -1));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -162,7 +167,12 @@ const addRevisionToItem = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const revisionCreatesNewHistoryEntry = (revision: Revision, item: Item) => {
|
const revisionCreatesNewHistoryEntry = (revision: Revision, item: Item) => {
|
||||||
return revision.body.trim() !== "" || (typeof revision.ring !== "undefined" && revision.ring !== item.ring) || (typeof revision.quadrant !== "undefined" && revision.quadrant !== item.quadrant);
|
return (
|
||||||
|
revision.body.trim() !== "" ||
|
||||||
|
(typeof revision.ring !== "undefined" && revision.ring !== item.ring) ||
|
||||||
|
(typeof revision.quadrant !== "undefined" &&
|
||||||
|
revision.quadrant !== item.quadrant)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const flagItem = (items: Item[], allReleases: string[]) =>
|
const flagItem = (items: Item[], allReleases: string[]) =>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { resolve } from "path";
|
|
||||||
import { realpathSync } from "fs";
|
import { realpathSync } from "fs";
|
||||||
|
import { resolve } from "path";
|
||||||
|
|
||||||
export const radarJson = "rd.json";
|
export const radarJson = "rd.json";
|
||||||
const appDirectory = realpathSync(process.cwd());
|
const appDirectory = realpathSync(process.cwd());
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import React from "react";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import Header from "./Header/Header";
|
import React from "react";
|
||||||
import Footer from "./Footer/Footer";
|
|
||||||
import Router from "./Router";
|
|
||||||
import {
|
import {
|
||||||
BrowserRouter,
|
BrowserRouter,
|
||||||
Routes,
|
|
||||||
Route,
|
|
||||||
Navigate,
|
Navigate,
|
||||||
useParams,
|
Route,
|
||||||
|
Routes,
|
||||||
useLocation,
|
useLocation,
|
||||||
|
useParams,
|
||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
import { Item } from "../model";
|
|
||||||
import { Messages, MessagesProvider } from "../context/MessagesContext";
|
|
||||||
import { ConfigData } from "../config";
|
import { ConfigData } from "../config";
|
||||||
|
import { Messages, MessagesProvider } from "../context/MessagesContext";
|
||||||
|
import { Item } from "../model";
|
||||||
|
import Footer from "./Footer/Footer";
|
||||||
|
import Header from "./Header/Header";
|
||||||
|
import Router from "./Router";
|
||||||
|
|
||||||
const useFetch = <D extends unknown>(url: string): D | undefined => {
|
const useFetch = <D extends unknown>(url: string): D | undefined => {
|
||||||
const [data, setData] = React.useState<D>();
|
const [data, setData] = React.useState<D>();
|
||||||
@@ -35,7 +36,7 @@ const useFetch = <D extends unknown>(url: string): D | undefined => {
|
|||||||
const useQuery = () => new URLSearchParams(useLocation().search);
|
const useQuery = () => new URLSearchParams(useLocation().search);
|
||||||
|
|
||||||
const usePage = (params: Record<string, string | undefined>) => {
|
const usePage = (params: Record<string, string | undefined>) => {
|
||||||
return (params['*'] || '').replace(".html", "");
|
return (params["*"] || "").replace(".html", "");
|
||||||
};
|
};
|
||||||
|
|
||||||
const RouterWithPageParam = ({
|
const RouterWithPageParam = ({
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { MouseEventHandler } from "react";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import React, { MouseEventHandler } from "react";
|
||||||
|
|
||||||
import "./badge.scss";
|
import "./badge.scss";
|
||||||
|
|
||||||
type BadgeProps = {
|
type BadgeProps = {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import React from "react";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
import "./branding.scss";
|
import "./branding.scss";
|
||||||
|
|
||||||
type BrandingProps = {
|
type BrandingProps = {
|
||||||
logoContent: React.ReactNode;
|
logoContent: React.ReactNode;
|
||||||
modifier?: "backlink" | "logo" | "content" | "footer";
|
modifier?: "backlink" | "logo" | "content" | "footer";
|
||||||
|
|||||||
@@ -1,45 +1,43 @@
|
|||||||
import React, { useRef, useLayoutEffect } from 'react';
|
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
|
import React, { useLayoutEffect, useRef } from "react";
|
||||||
|
|
||||||
export const YAxis: React.FC<{
|
export const YAxis: React.FC<{
|
||||||
scale: d3.ScaleLinear<number, number>
|
scale: d3.ScaleLinear<number, number>;
|
||||||
}> = ({ scale }) => {
|
}> = ({ scale }) => {
|
||||||
|
|
||||||
const ref = useRef<SVGSVGElement>(null);
|
const ref = useRef<SVGSVGElement>(null);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (ref.current == null) {
|
if (ref.current == null) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const axisGenerator = d3.axisLeft(scale).ticks(6);
|
const axisGenerator = d3.axisLeft(scale).ticks(6);
|
||||||
d3.select(ref.current)
|
d3.select(ref.current)
|
||||||
.attr('class', 'y-axis')
|
.attr("class", "y-axis")
|
||||||
.call(axisGenerator)
|
.call(axisGenerator)
|
||||||
.call(g => g.selectAll('.tick text').remove())
|
.call((g) => g.selectAll(".tick text").remove())
|
||||||
.call(g => g.selectAll('.tick line').remove())
|
.call((g) => g.selectAll(".tick line").remove())
|
||||||
.call(g => g.selectAll('.domain').remove());
|
.call((g) => g.selectAll(".domain").remove());
|
||||||
}, [scale]);
|
}, [scale]);
|
||||||
|
|
||||||
return <g ref={ref} />;
|
return <g ref={ref} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const XAxis: React.FC<{
|
export const XAxis: React.FC<{
|
||||||
scale: d3.ScaleLinear<number, number>
|
scale: d3.ScaleLinear<number, number>;
|
||||||
}> = ({ scale }) => {
|
}> = ({ scale }) => {
|
||||||
|
|
||||||
const ref = useRef<SVGSVGElement>(null);
|
const ref = useRef<SVGSVGElement>(null);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (ref.current == null) {
|
if (ref.current == null) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const axisGenerator = d3.axisBottom(scale).ticks(6);
|
const axisGenerator = d3.axisBottom(scale).ticks(6);
|
||||||
d3.select(ref.current)
|
d3.select(ref.current)
|
||||||
.attr('class', 'x-axis')
|
.attr("class", "x-axis")
|
||||||
.call(axisGenerator)
|
.call(axisGenerator)
|
||||||
.call(g => g.selectAll('.tick text').remove())
|
.call((g) => g.selectAll(".tick text").remove())
|
||||||
.call(g => g.selectAll('.tick line').remove())
|
.call((g) => g.selectAll(".tick line").remove())
|
||||||
.call(g => g.selectAll('.domain').remove());
|
.call((g) => g.selectAll(".domain").remove());
|
||||||
}, [scale]);
|
}, [scale]);
|
||||||
|
|
||||||
return <g ref={ref} />;
|
return <g ref={ref} />;
|
||||||
|
|||||||
@@ -1,57 +1,73 @@
|
|||||||
import React from 'react';
|
import { ScaleLinear } from "d3";
|
||||||
import { ScaleLinear } from 'd3';
|
import React from "react";
|
||||||
import { FlagType, Item, Blip, Point } from '../../model';
|
|
||||||
import Link from '../Link/Link';
|
import { ConfigData } from "../../config";
|
||||||
import { NewBlip, ChangedBlip, DefaultBlip } from './BlipShapes';
|
import { Blip, FlagType, Item, Point } from "../../model";
|
||||||
import { ConfigData } from '../../config';
|
import Link from "../Link/Link";
|
||||||
|
import { ChangedBlip, DefaultBlip, NewBlip } from "./BlipShapes";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
See https://medium.com/create-code/build-a-radar-diagram-with-d3-js-9db6458a9248
|
See https://medium.com/create-code/build-a-radar-diagram-with-d3-js-9db6458a9248
|
||||||
for a good explanation of formulas used to calculate various things in this component
|
for a good explanation of formulas used to calculate various things in this component
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function generateCoordinates(blip: Blip, xScale: ScaleLinear<number, number>, yScale: ScaleLinear<number, number>, config: ConfigData): Point {
|
function generateCoordinates(
|
||||||
|
blip: Blip,
|
||||||
|
xScale: ScaleLinear<number, number>,
|
||||||
|
yScale: ScaleLinear<number, number>,
|
||||||
|
config: ConfigData
|
||||||
|
): Point {
|
||||||
const pi = Math.PI,
|
const pi = Math.PI,
|
||||||
ringRadius = config.chartConfig.ringsAttributes[blip.ringPosition].radius,
|
ringRadius = config.chartConfig.ringsAttributes[blip.ringPosition].radius,
|
||||||
previousRingRadius = blip.ringPosition === 0 ? 0 : config.chartConfig.ringsAttributes[blip.ringPosition - 1].radius,
|
previousRingRadius =
|
||||||
|
blip.ringPosition === 0
|
||||||
|
? 0
|
||||||
|
: config.chartConfig.ringsAttributes[blip.ringPosition - 1].radius,
|
||||||
ringPadding = 0.7;
|
ringPadding = 0.7;
|
||||||
|
|
||||||
// radian between 0 and 90 degrees
|
// radian between 0 and 90 degrees
|
||||||
const randomDegree = ((Math.random() * 90) * pi) / 180;
|
const randomDegree = (Math.random() * 90 * pi) / 180;
|
||||||
// random distance from the centre of the radar, but within given ring. Also, with some "padding" so the points don't touch ring borders.
|
// random distance from the centre of the radar, but within given ring. Also, with some "padding" so the points don't touch ring borders.
|
||||||
const radius = randomBetween(previousRingRadius + ringPadding, ringRadius - ringPadding);
|
const radius = randomBetween(
|
||||||
|
previousRingRadius + ringPadding,
|
||||||
|
ringRadius - ringPadding
|
||||||
|
);
|
||||||
/*
|
/*
|
||||||
Multiples of PI/2. To apply the calculated position to the specific quadrant.
|
Multiples of PI/2. To apply the calculated position to the specific quadrant.
|
||||||
Order here is counter-clockwise, so we need to "invert" quadrant positions (i.e. swap 2 with 4)
|
Order here is counter-clockwise, so we need to "invert" quadrant positions (i.e. swap 2 with 4)
|
||||||
*/
|
*/
|
||||||
const shift = pi * [1, 4, 3, 2][blip.quadrantPosition - 1] / 2;
|
const shift = (pi * [1, 4, 3, 2][blip.quadrantPosition - 1]) / 2;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: xScale(Math.cos(randomDegree + shift) * radius),
|
x: xScale(Math.cos(randomDegree + shift) * radius),
|
||||||
y: yScale(Math.sin(randomDegree + shift) * radius)
|
y: yScale(Math.sin(randomDegree + shift) * radius),
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
function randomBetween (min: number, max: number): number {
|
function randomBetween(min: number, max: number): number {
|
||||||
return Math.random() * (max - min) + min;
|
return Math.random() * (max - min) + min;
|
||||||
};
|
}
|
||||||
|
|
||||||
function distanceBetween(point1: Point, point2: Point): number {
|
function distanceBetween(point1: Point, point2: Point): number {
|
||||||
const a = point2.x - point1.x;
|
const a = point2.x - point1.x;
|
||||||
const b = point2.y - point1.y;
|
const b = point2.y - point1.y;
|
||||||
return Math.sqrt((a * a) + (b * b));
|
return Math.sqrt(a * a + b * b);
|
||||||
};
|
}
|
||||||
|
|
||||||
function renderBlip(blip: Blip, index: number, config: ConfigData): JSX.Element {
|
function renderBlip(
|
||||||
|
blip: Blip,
|
||||||
|
index: number,
|
||||||
|
config: ConfigData
|
||||||
|
): JSX.Element {
|
||||||
const props = {
|
const props = {
|
||||||
blip,
|
blip,
|
||||||
className: 'blip',
|
className: "blip",
|
||||||
fill: blip.colour,
|
fill: blip.colour,
|
||||||
'data-background-color': blip.colour,
|
"data-background-color": blip.colour,
|
||||||
'data-text-color': blip.txtColour,
|
"data-text-color": blip.txtColour,
|
||||||
'data-tip': blip.title,
|
"data-tip": blip.title,
|
||||||
key: index
|
key: index,
|
||||||
}
|
};
|
||||||
switch (blip.flag) {
|
switch (blip.flag) {
|
||||||
case FlagType.new:
|
case FlagType.new:
|
||||||
return <NewBlip {...props} config={config} />;
|
return <NewBlip {...props} config={config} />;
|
||||||
@@ -60,15 +76,14 @@ function renderBlip(blip: Blip, index: number, config: ConfigData): JSX.Element
|
|||||||
default:
|
default:
|
||||||
return <DefaultBlip {...props} config={config} />;
|
return <DefaultBlip {...props} config={config} />;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const BlipPoints: React.FC<{
|
const BlipPoints: React.FC<{
|
||||||
items: Item[]
|
items: Item[];
|
||||||
xScale:ScaleLinear<number, number>
|
xScale: ScaleLinear<number, number>;
|
||||||
yScale:ScaleLinear<number, number>
|
yScale: ScaleLinear<number, number>;
|
||||||
config:ConfigData
|
config: ConfigData;
|
||||||
}> = ({items, xScale, yScale, config}) => {
|
}> = ({ items, xScale, yScale, config }) => {
|
||||||
|
|
||||||
const blips: Blip[] = items.reduce((list: Blip[], item: Item) => {
|
const blips: Blip[] = items.reduce((list: Blip[], item: Item) => {
|
||||||
if (!item.ring || !item.quadrant) {
|
if (!item.ring || !item.quadrant) {
|
||||||
// skip the blip if it doesn't have a ring or quadrant assigned
|
// skip the blip if it doesn't have a ring or quadrant assigned
|
||||||
@@ -79,12 +94,13 @@ const BlipPoints: React.FC<{
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
let blip: Blip = { ...item,
|
let blip: Blip = {
|
||||||
|
...item,
|
||||||
quadrantPosition: quadrantConfig.position,
|
quadrantPosition: quadrantConfig.position,
|
||||||
ringPosition: config.rings.findIndex(r => r === item.ring),
|
ringPosition: config.rings.findIndex((r) => r === item.ring),
|
||||||
colour: quadrantConfig.colour,
|
colour: quadrantConfig.colour,
|
||||||
txtColour: quadrantConfig.txtColour,
|
txtColour: quadrantConfig.txtColour,
|
||||||
coordinates: {x: 0, y: 0},
|
coordinates: { x: 0, y: 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
let point: Point;
|
let point: Point;
|
||||||
@@ -92,19 +108,24 @@ const BlipPoints: React.FC<{
|
|||||||
let distanceBetweenCheck: boolean;
|
let distanceBetweenCheck: boolean;
|
||||||
do {
|
do {
|
||||||
const localpoint = generateCoordinates(blip, xScale, yScale, config);
|
const localpoint = generateCoordinates(blip, xScale, yScale, config);
|
||||||
point = localpoint
|
point = localpoint;
|
||||||
counter++;
|
counter++;
|
||||||
/*
|
/*
|
||||||
Generate position of the new blip until it has a satisfactory distance to every other blip (so that they don't touch each other)
|
Generate position of the new blip until it has a satisfactory distance to every other blip (so that they don't touch each other)
|
||||||
and quadrant borders (so that they don't overlap quadrants)
|
and quadrant borders (so that they don't overlap quadrants)
|
||||||
This feels pretty inefficient, but good enough for now.
|
This feels pretty inefficient, but good enough for now.
|
||||||
*/
|
*/
|
||||||
distanceBetweenCheck = list.some(b => distanceBetween(localpoint, b.coordinates) < config.chartConfig.blipSize + config.chartConfig.blipSize / 2)
|
distanceBetweenCheck = list.some(
|
||||||
} while (counter < 100
|
(b) =>
|
||||||
&& (Math.abs(point.x - xScale(0)) < 15
|
distanceBetween(localpoint, b.coordinates) <
|
||||||
|| Math.abs(point.y - yScale(0)) < 15
|
config.chartConfig.blipSize + config.chartConfig.blipSize / 2
|
||||||
|| distanceBetweenCheck
|
);
|
||||||
));
|
} while (
|
||||||
|
counter < 100 &&
|
||||||
|
(Math.abs(point.x - xScale(0)) < 15 ||
|
||||||
|
Math.abs(point.y - yScale(0)) < 15 ||
|
||||||
|
distanceBetweenCheck)
|
||||||
|
);
|
||||||
|
|
||||||
blip.coordinates = point;
|
blip.coordinates = point;
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { ConfigData } from '../../config';
|
|
||||||
import { Blip } from '../../model';
|
import { ConfigData } from "../../config";
|
||||||
|
import { Blip } from "../../model";
|
||||||
|
|
||||||
type VisualBlipProps = {
|
type VisualBlipProps = {
|
||||||
className: string,
|
className: string;
|
||||||
fill: string,
|
fill: string;
|
||||||
'data-background-color': string,
|
"data-background-color": string;
|
||||||
'data-text-color': string,
|
"data-text-color": string;
|
||||||
'data-tip': string,
|
"data-tip": string;
|
||||||
key: number
|
key: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const ChangedBlip: React.FC<
|
export const ChangedBlip: React.FC<
|
||||||
{blip: Blip, config: ConfigData} & VisualBlipProps
|
{ blip: Blip; config: ConfigData } & VisualBlipProps
|
||||||
> = ({blip, config, ...props}) => {
|
> = ({ blip, config, ...props }) => {
|
||||||
const centeredX = blip.coordinates.x - config.chartConfig.blipSize/2,
|
const centeredX = blip.coordinates.x - config.chartConfig.blipSize / 2,
|
||||||
centeredY = blip.coordinates.y - config.chartConfig.blipSize/2;
|
centeredY = blip.coordinates.y - config.chartConfig.blipSize / 2;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<rect
|
<rect
|
||||||
@@ -31,10 +32,10 @@ export const ChangedBlip: React.FC<
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const NewBlip: React.FC<
|
export const NewBlip: React.FC<
|
||||||
{blip: Blip, config: ConfigData} & VisualBlipProps
|
{ blip: Blip; config: ConfigData } & VisualBlipProps
|
||||||
> = ({blip, config, ...props}) => {
|
> = ({ blip, config, ...props }) => {
|
||||||
const centeredX = blip.coordinates.x - config.chartConfig.blipSize/2,
|
const centeredX = blip.coordinates.x - config.chartConfig.blipSize / 2,
|
||||||
centeredY = blip.coordinates.y - config.chartConfig.blipSize/2;
|
centeredY = blip.coordinates.y - config.chartConfig.blipSize / 2;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The below is a predefined path of a triangle with rounded corners.
|
The below is a predefined path of a triangle with rounded corners.
|
||||||
@@ -51,8 +52,8 @@ export const NewBlip: React.FC<
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DefaultBlip: React.FC<
|
export const DefaultBlip: React.FC<
|
||||||
{blip: Blip, config: ConfigData} & VisualBlipProps
|
{ blip: Blip; config: ConfigData } & VisualBlipProps
|
||||||
> = ({blip, config, ...props}) => {
|
> = ({ blip, config, ...props }) => {
|
||||||
return (
|
return (
|
||||||
<circle
|
<circle
|
||||||
r={config.chartConfig.blipSize / 2}
|
r={config.chartConfig.blipSize / 2}
|
||||||
|
|||||||
@@ -1,38 +1,48 @@
|
|||||||
import React from 'react';
|
import * as d3 from "d3";
|
||||||
import * as d3 from 'd3';
|
import React from "react";
|
||||||
import { QuadrantConfig } from '../../model';
|
|
||||||
import { ConfigData } from '../../config';
|
|
||||||
|
|
||||||
function arcPath(quadrantPosition: number, ringPosition: number, xScale: d3.ScaleLinear<number, number>, config: ConfigData) {
|
import { ConfigData } from "../../config";
|
||||||
const startAngle = quadrantPosition === 1 ?
|
import { QuadrantConfig } from "../../model";
|
||||||
3 * Math.PI / 2
|
|
||||||
: (quadrantPosition - 2) * Math.PI / 2
|
function arcPath(
|
||||||
const endAngle = quadrantPosition === 1 ?
|
quadrantPosition: number,
|
||||||
4 * Math.PI / 2
|
ringPosition: number,
|
||||||
: (quadrantPosition -1) * Math.PI / 2
|
xScale: d3.ScaleLinear<number, number>,
|
||||||
|
config: ConfigData
|
||||||
|
) {
|
||||||
|
const startAngle =
|
||||||
|
quadrantPosition === 1
|
||||||
|
? (3 * Math.PI) / 2
|
||||||
|
: ((quadrantPosition - 2) * Math.PI) / 2;
|
||||||
|
const endAngle =
|
||||||
|
quadrantPosition === 1
|
||||||
|
? (4 * Math.PI) / 2
|
||||||
|
: ((quadrantPosition - 1) * Math.PI) / 2;
|
||||||
const arcAttrs = config.chartConfig.ringsAttributes[ringPosition],
|
const arcAttrs = config.chartConfig.ringsAttributes[ringPosition],
|
||||||
ringRadiusPx = xScale(arcAttrs.radius) - xScale(0),
|
ringRadiusPx = xScale(arcAttrs.radius) - xScale(0),
|
||||||
arc = d3.arc();
|
arc = d3.arc();
|
||||||
|
|
||||||
return arc({
|
return (
|
||||||
|
arc({
|
||||||
innerRadius: ringRadiusPx - arcAttrs.arcWidth,
|
innerRadius: ringRadiusPx - arcAttrs.arcWidth,
|
||||||
outerRadius: ringRadiusPx,
|
outerRadius: ringRadiusPx,
|
||||||
startAngle,
|
startAngle,
|
||||||
endAngle
|
endAngle,
|
||||||
}) || undefined;
|
}) || undefined
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const QuadrantRings: React.FC<{
|
const QuadrantRings: React.FC<{
|
||||||
quadrant: QuadrantConfig
|
quadrant: QuadrantConfig;
|
||||||
xScale: d3.ScaleLinear<number, number>
|
xScale: d3.ScaleLinear<number, number>;
|
||||||
config: ConfigData
|
config: ConfigData;
|
||||||
}> = ({ quadrant, xScale, config}) => {
|
}> = ({ quadrant, xScale, config }) => {
|
||||||
// order from top-right clockwise
|
// order from top-right clockwise
|
||||||
const gradientAttributes = [
|
const gradientAttributes = [
|
||||||
{x: 0, y: 0, cx: 1, cy: 1, r: 1},
|
{ x: 0, y: 0, cx: 1, cy: 1, r: 1 },
|
||||||
{x: xScale(0), y: 0, cx: 0, cy: 1, r: 1},
|
{ x: xScale(0), y: 0, cx: 0, cy: 1, r: 1 },
|
||||||
{x: xScale(0), y: xScale(0), cx: 0, cy: 0, r: 1},
|
{ x: xScale(0), y: xScale(0), cx: 0, cy: 0, r: 1 },
|
||||||
{x: 0, y: xScale(0), cx: 1, cy: 0, r: 1}
|
{ x: 0, y: xScale(0), cx: 1, cy: 0, r: 1 },
|
||||||
];
|
];
|
||||||
const gradientId = `${quadrant.position}-radial-gradient`,
|
const gradientId = `${quadrant.position}-radial-gradient`,
|
||||||
quadrantSize = config.chartConfig.size / 2;
|
quadrantSize = config.chartConfig.size / 2;
|
||||||
@@ -41,9 +51,16 @@ const QuadrantRings: React.FC<{
|
|||||||
<g className="quadrant-ring">
|
<g className="quadrant-ring">
|
||||||
{/* Definition of the quadrant gradient */}
|
{/* Definition of the quadrant gradient */}
|
||||||
<defs>
|
<defs>
|
||||||
<radialGradient id={gradientId} {...gradientAttributes[quadrant.position - 1]}>
|
<radialGradient
|
||||||
|
id={gradientId}
|
||||||
|
{...gradientAttributes[quadrant.position - 1]}
|
||||||
|
>
|
||||||
<stop offset="0%" stopColor={quadrant.colour}></stop>
|
<stop offset="0%" stopColor={quadrant.colour}></stop>
|
||||||
<stop offset="100%" stopColor={quadrant.colour} stopOpacity="0"></stop>
|
<stop
|
||||||
|
offset="100%"
|
||||||
|
stopColor={quadrant.colour}
|
||||||
|
stopOpacity="0"
|
||||||
|
></stop>
|
||||||
</radialGradient>
|
</radialGradient>
|
||||||
</defs>
|
</defs>
|
||||||
|
|
||||||
@@ -54,7 +71,7 @@ const QuadrantRings: React.FC<{
|
|||||||
x={gradientAttributes[quadrant.position - 1].x}
|
x={gradientAttributes[quadrant.position - 1].x}
|
||||||
y={gradientAttributes[quadrant.position - 1].y}
|
y={gradientAttributes[quadrant.position - 1].y}
|
||||||
fill={`url(#${gradientId})`}
|
fill={`url(#${gradientId})`}
|
||||||
style={{opacity: 0.5}}
|
style={{ opacity: 0.5 }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Rings' arcs */}
|
{/* Rings' arcs */}
|
||||||
@@ -63,12 +80,13 @@ const QuadrantRings: React.FC<{
|
|||||||
key={index}
|
key={index}
|
||||||
fill={quadrant.colour}
|
fill={quadrant.colour}
|
||||||
d={arcPath(quadrant.position, index, xScale, config)}
|
d={arcPath(quadrant.position, index, xScale, config)}
|
||||||
style={{transform: `translate(${quadrantSize}px, ${quadrantSize}px)`}}
|
style={{
|
||||||
|
transform: `translate(${quadrantSize}px, ${quadrantSize}px)`,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default QuadrantRings;
|
export default QuadrantRings;
|
||||||
|
|||||||
@@ -1,36 +1,49 @@
|
|||||||
import React from 'react';
|
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
import ReactTooltip from 'react-tooltip';
|
import React from "react";
|
||||||
import { Item } from '../../model';
|
import ReactTooltip from "react-tooltip";
|
||||||
import { YAxis, XAxis } from './Axes';
|
|
||||||
import QuadrantRings from './QuadrantRings';
|
|
||||||
import BlipPoints from './BlipPoints';
|
|
||||||
|
|
||||||
import './chart.scss';
|
import { ConfigData } from "../../config";
|
||||||
import { ConfigData } from '../../config';
|
import { Item } from "../../model";
|
||||||
|
import { XAxis, YAxis } from "./Axes";
|
||||||
|
import BlipPoints from "./BlipPoints";
|
||||||
|
import QuadrantRings from "./QuadrantRings";
|
||||||
|
import "./chart.scss";
|
||||||
|
|
||||||
const RingLabel: React.FC<{
|
const RingLabel: React.FC<{
|
||||||
ring: string
|
ring: string;
|
||||||
xScale: d3.ScaleLinear<number, number>
|
xScale: d3.ScaleLinear<number, number>;
|
||||||
yScale: d3.ScaleLinear<number, number>
|
yScale: d3.ScaleLinear<number, number>;
|
||||||
config: ConfigData
|
config: ConfigData;
|
||||||
}> = ({ring, xScale, yScale, config}) => {
|
}> = ({ ring, xScale, yScale, config }) => {
|
||||||
const ringIndex = config.rings.findIndex(r => r === ring)
|
const ringIndex = config.rings.findIndex((r) => r === ring);
|
||||||
|
|
||||||
const ringRadius = config.chartConfig.ringsAttributes[ringIndex].radius,
|
const ringRadius = config.chartConfig.ringsAttributes[ringIndex].radius,
|
||||||
previousRingRadius = ringIndex === 0 ? 0 : config.chartConfig.ringsAttributes[ringIndex - 1].radius,
|
previousRingRadius =
|
||||||
|
ringIndex === 0
|
||||||
|
? 0
|
||||||
|
: config.chartConfig.ringsAttributes[ringIndex - 1].radius,
|
||||||
// middle point in between two ring arcs
|
// middle point in between two ring arcs
|
||||||
distanceFromCentre = previousRingRadius + (ringRadius - previousRingRadius) / 2;
|
distanceFromCentre =
|
||||||
|
previousRingRadius + (ringRadius - previousRingRadius) / 2;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g className="ring-label">
|
<g className="ring-label">
|
||||||
{/* Right hand-side label */}
|
{/* Right hand-side label */}
|
||||||
<text x={xScale(distanceFromCentre)} y={yScale(0)} textAnchor="middle" dy=".35em">
|
<text
|
||||||
|
x={xScale(distanceFromCentre)}
|
||||||
|
y={yScale(0)}
|
||||||
|
textAnchor="middle"
|
||||||
|
dy=".35em"
|
||||||
|
>
|
||||||
{ring}
|
{ring}
|
||||||
</text>
|
</text>
|
||||||
{/* Left hand-side label */}
|
{/* Left hand-side label */}
|
||||||
<text x={xScale(-distanceFromCentre)} y={yScale(0)} textAnchor="middle" dy=".35em">
|
<text
|
||||||
|
x={xScale(-distanceFromCentre)}
|
||||||
|
y={yScale(0)}
|
||||||
|
textAnchor="middle"
|
||||||
|
dy=".35em"
|
||||||
|
>
|
||||||
{ring}
|
{ring}
|
||||||
</text>
|
</text>
|
||||||
</g>
|
</g>
|
||||||
@@ -38,40 +51,59 @@ const RingLabel: React.FC<{
|
|||||||
};
|
};
|
||||||
|
|
||||||
const RadarChart: React.FC<{
|
const RadarChart: React.FC<{
|
||||||
items: Item[]
|
items: Item[];
|
||||||
config: ConfigData
|
config: ConfigData;
|
||||||
}> = ({ items, config }) => {
|
}> = ({ items, config }) => {
|
||||||
|
const xScale = d3
|
||||||
const xScale = d3.scaleLinear()
|
.scaleLinear()
|
||||||
.domain(config.chartConfig.scale)
|
.domain(config.chartConfig.scale)
|
||||||
.range([0, config.chartConfig.size]);
|
.range([0, config.chartConfig.size]);
|
||||||
const yScale = d3.scaleLinear()
|
const yScale = d3
|
||||||
|
.scaleLinear()
|
||||||
.domain(config.chartConfig.scale)
|
.domain(config.chartConfig.scale)
|
||||||
.range([config.chartConfig.size, 0]);
|
.range([config.chartConfig.size, 0]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="chart" style={{maxWidth: `${config.chartConfig.size}px`}}>
|
<div className="chart" style={{ maxWidth: `${config.chartConfig.size}px` }}>
|
||||||
<svg viewBox={`0 0 ${config.chartConfig.size} ${config.chartConfig.size}`}>
|
<svg
|
||||||
|
viewBox={`0 0 ${config.chartConfig.size} ${config.chartConfig.size}`}
|
||||||
|
>
|
||||||
<g transform={`translate(${xScale(0)}, 0)`}>
|
<g transform={`translate(${xScale(0)}, 0)`}>
|
||||||
<YAxis scale={yScale}/>
|
<YAxis scale={yScale} />
|
||||||
</g>
|
</g>
|
||||||
<g transform={`translate(0, ${yScale(0)})`}>
|
<g transform={`translate(0, ${yScale(0)})`}>
|
||||||
<XAxis scale={xScale}/>
|
<XAxis scale={xScale} />
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
{Object.values(config.quadrantsMap).map((value, index) => (
|
{Object.values(config.quadrantsMap).map((value, index) => (
|
||||||
<QuadrantRings key={index} quadrant={value} xScale={xScale} config={config} />
|
<QuadrantRings
|
||||||
|
key={index}
|
||||||
|
quadrant={value}
|
||||||
|
xScale={xScale}
|
||||||
|
config={config}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{Array.from(config.rings).map((ring: string, index) => (
|
{Array.from(config.rings).map((ring: string, index) => (
|
||||||
<RingLabel key={index} ring={ring} xScale={xScale} yScale={yScale} config={config} />
|
<RingLabel
|
||||||
|
key={index}
|
||||||
|
ring={ring}
|
||||||
|
xScale={xScale}
|
||||||
|
yScale={yScale}
|
||||||
|
config={config}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<BlipPoints items={items} xScale={xScale} yScale={yScale} config={config} />
|
<BlipPoints
|
||||||
|
items={items}
|
||||||
|
xScale={xScale}
|
||||||
|
yScale={yScale}
|
||||||
|
config={config}
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<ReactTooltip className="tooltip" offset={{top: -5}}/>
|
<ReactTooltip className="tooltip" offset={{ top: -5 }} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default RadarChart;
|
export default RadarChart;
|
||||||
|
|||||||
@@ -1,16 +1,28 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { Item } from "../../model";
|
import { Item } from "../../model";
|
||||||
import "./editButton.scss";
|
import "./editButton.scss";
|
||||||
|
|
||||||
type EditButtonProps = {
|
type EditButtonProps = {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
item: Item & { release? : string }
|
item: Item & { release?: string };
|
||||||
title?: string;
|
title?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function EditButton({baseUrl, item, title}: React.PropsWithChildren<EditButtonProps>) {
|
export default function EditButton({
|
||||||
|
baseUrl,
|
||||||
|
item,
|
||||||
|
title,
|
||||||
|
}: React.PropsWithChildren<EditButtonProps>) {
|
||||||
const href = `${baseUrl}/${item.release}/${item.name}.md`;
|
const href = `${baseUrl}/${item.release}/${item.name}.md`;
|
||||||
return (
|
return (
|
||||||
<a className="icon-link" href={href} target="_blank" rel="noopener noreferrer">{title || 'Edit'}</a>
|
<a
|
||||||
|
className="icon-link"
|
||||||
|
href={href}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{title || "Edit"}
|
||||||
|
</a>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
import "./fadeable.scss";
|
import "./fadeable.scss";
|
||||||
|
|
||||||
type FadeableProps = {
|
type FadeableProps = {
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import React from "react";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { assetUrl, getItemPageNames, isMobileViewport } from "../../config";
|
||||||
|
import { useMessages } from "../../context/MessagesContext";
|
||||||
|
import { Item } from "../../model";
|
||||||
|
import { sanitize } from "../../sanitize";
|
||||||
import Branding from "../Branding/Branding";
|
import Branding from "../Branding/Branding";
|
||||||
import FooterEnd from "../FooterEnd/FooterEnd";
|
import FooterEnd from "../FooterEnd/FooterEnd";
|
||||||
import { assetUrl, getItemPageNames, isMobileViewport } from "../../config";
|
|
||||||
import { Item } from "../../model";
|
|
||||||
import "./footer.scss";
|
import "./footer.scss";
|
||||||
import { useMessages } from "../../context/MessagesContext";
|
|
||||||
import { sanitize } from "../../sanitize";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
items: Item[];
|
items: Item[];
|
||||||
@@ -35,7 +36,10 @@ const Footer: React.FC<Props> = ({ items, pageName }) => {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="footnote" dangerouslySetInnerHTML={sanitize(footerFootnote)}></div>
|
<div
|
||||||
|
className="footnote"
|
||||||
|
dangerouslySetInnerHTML={sanitize(footerFootnote)}
|
||||||
|
></div>
|
||||||
</Branding>
|
</Branding>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
import React from "react";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { useMessages } from "../../context/MessagesContext";
|
||||||
import SocialLink from "../SocialLink/SocialLink";
|
import SocialLink from "../SocialLink/SocialLink";
|
||||||
import "./footerend.scss";
|
import "./footerend.scss";
|
||||||
import { useMessages } from "../../context/MessagesContext";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
modifier?: "in-sidebar";
|
modifier?: "in-sidebar";
|
||||||
}
|
}
|
||||||
|
|
||||||
const FooterEnd: React.FC<Props> = ({ modifier }) => {
|
const FooterEnd: React.FC<Props> = ({ modifier }) => {
|
||||||
const { socialLinksLabel, socialLinks, legalInformationLink, legalInformationLabel } = useMessages();
|
const {
|
||||||
|
socialLinksLabel,
|
||||||
|
socialLinks,
|
||||||
|
legalInformationLink,
|
||||||
|
legalInformationLabel,
|
||||||
|
} = useMessages();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -21,7 +27,7 @@ const FooterEnd: React.FC<Props> = ({ modifier }) => {
|
|||||||
{socialLinks && (
|
{socialLinks && (
|
||||||
<>
|
<>
|
||||||
<div className="footer-social__label">
|
<div className="footer-social__label">
|
||||||
<p>{socialLinksLabel ?? 'Follow us:' }</p>
|
<p>{socialLinksLabel ?? "Follow us:"}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="footer-social__links">
|
<div className="footer-social__links">
|
||||||
{socialLinks.map(({ href, iconName }) => (
|
{socialLinks.map(({ href, iconName }) => (
|
||||||
@@ -40,7 +46,7 @@ const FooterEnd: React.FC<Props> = ({ modifier }) => {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
{legalInformationLabel || 'Legal Information'}
|
{legalInformationLabel || "Legal Information"}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import React, { useState, useRef } from "react";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import qs from "query-string";
|
||||||
|
import React, { useRef, useState } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
import { radarNameShort } from "../../config";
|
||||||
|
import { useMessages } from "../../context/MessagesContext";
|
||||||
import Branding from "../Branding/Branding";
|
import Branding from "../Branding/Branding";
|
||||||
import Link from "../Link/Link";
|
import Link from "../Link/Link";
|
||||||
import LogoLink from "../LogoLink/LogoLink";
|
import LogoLink from "../LogoLink/LogoLink";
|
||||||
import Search from "../Search/Search";
|
import Search from "../Search/Search";
|
||||||
import { radarNameShort } from "../../config";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
import qs from "query-string";
|
|
||||||
import { useMessages } from "../../context/MessagesContext";
|
|
||||||
|
|
||||||
export default function Header({ pageName }: { pageName: string }) {
|
export default function Header({ pageName }: { pageName: string }) {
|
||||||
const [searchOpen, setSearchOpen] = useState(false);
|
const [searchOpen, setSearchOpen] = useState(false);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from "react";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
import "./headline-group.scss";
|
import "./headline-group.scss";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import "./hero-headline.scss";
|
import "./hero-headline.scss";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import React from "react";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import Link from "../Link/Link";
|
import React from "react";
|
||||||
import Flag from "../Flag/Flag";
|
|
||||||
import { Item as mItem } from "../../model";
|
import { Item as mItem } from "../../model";
|
||||||
|
import Flag from "../Flag/Flag";
|
||||||
|
import Link from "../Link/Link";
|
||||||
import "./item.scss";
|
import "./item.scss";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
import { featuredOnly, Item as mItem, nonFeaturedOnly } from "../../model";
|
||||||
import Item from "../Item/Item";
|
import Item from "../Item/Item";
|
||||||
import { featuredOnly, nonFeaturedOnly, Item as mItem } from "../../model";
|
|
||||||
import "./item-list.scss";
|
import "./item-list.scss";
|
||||||
|
|
||||||
type ItemListProps = {
|
type ItemListProps = {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import Badge from "../Badge/Badge";
|
|
||||||
import { formatRelease } from "../../date";
|
import { formatRelease } from "../../date";
|
||||||
import { Revision } from "../../model";
|
import { Revision } from "../../model";
|
||||||
|
import Badge from "../Badge/Badge";
|
||||||
|
|
||||||
export default function ItemRevision({
|
export default function ItemRevision({
|
||||||
revision,
|
revision,
|
||||||
dateFormat,
|
dateFormat,
|
||||||
}: {
|
}: {
|
||||||
revision: Revision;
|
revision: Revision;
|
||||||
dateFormat?: string
|
dateFormat?: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="item-revision">
|
<div className="item-revision">
|
||||||
|
|||||||
@@ -1,25 +1,31 @@
|
|||||||
|
import { useMessages } from "../../context/MessagesContext";
|
||||||
|
import { Revision } from "../../model";
|
||||||
import HeadlineGroup from "../HeadlineGroup/HeadlineGroup";
|
import HeadlineGroup from "../HeadlineGroup/HeadlineGroup";
|
||||||
import ItemRevision from "../ItemRevision/ItemRevision";
|
import ItemRevision from "../ItemRevision/ItemRevision";
|
||||||
import { Revision } from "../../model";
|
|
||||||
import "./item-revisions.scss";
|
import "./item-revisions.scss";
|
||||||
import { useMessages } from "../../context/MessagesContext";
|
|
||||||
|
|
||||||
export default function ItemRevisions({
|
export default function ItemRevisions({
|
||||||
revisions,
|
revisions,
|
||||||
dateFormat,
|
dateFormat,
|
||||||
}: {
|
}: {
|
||||||
revisions: Revision[];
|
revisions: Revision[];
|
||||||
dateFormat?: string
|
dateFormat?: string;
|
||||||
}) {
|
}) {
|
||||||
const { revisionsText } = useMessages();
|
const { revisionsText } = useMessages();
|
||||||
return (
|
return (
|
||||||
<div className="item-revisions">
|
<div className="item-revisions">
|
||||||
<HeadlineGroup secondary>
|
<HeadlineGroup secondary>
|
||||||
<h4 className="headline headline--dark">{revisionsText ?? 'Revisions:'}</h4>
|
<h4 className="headline headline--dark">
|
||||||
|
{revisionsText ?? "Revisions:"}
|
||||||
|
</h4>
|
||||||
</HeadlineGroup>
|
</HeadlineGroup>
|
||||||
|
|
||||||
{revisions.map((revision) => (
|
{revisions.map((revision) => (
|
||||||
<ItemRevision key={revision.release} revision={revision} dateFormat={dateFormat} />
|
<ItemRevision
|
||||||
|
key={revision.release}
|
||||||
|
revision={revision}
|
||||||
|
dateFormat={dateFormat}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link as RLink } from "react-router-dom";
|
import { Link as RLink } from "react-router-dom";
|
||||||
|
|
||||||
import "./link.scss";
|
import "./link.scss";
|
||||||
|
|
||||||
type LinkProps = {
|
type LinkProps = {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import React from "react";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import Link from "../Link/Link";
|
import React from "react";
|
||||||
|
|
||||||
import { assetUrl, radarNameShort } from "../../config";
|
import { assetUrl, radarNameShort } from "../../config";
|
||||||
|
import Link from "../Link/Link";
|
||||||
import "./logo-link.scss";
|
import "./logo-link.scss";
|
||||||
|
|
||||||
export default function LogoLink({ small = false }: { small?: boolean }) {
|
export default function LogoLink({ small = false }: { small?: boolean }) {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
|
||||||
import Fadeable from "../Fadeable/Fadeable";
|
|
||||||
import SetTitle from "../SetTitle";
|
|
||||||
import { radarName } from "../../config";
|
import { radarName } from "../../config";
|
||||||
import { useMessages } from "../../context/MessagesContext";
|
import { useMessages } from "../../context/MessagesContext";
|
||||||
import { sanitize } from "../../sanitize";
|
import { sanitize } from "../../sanitize";
|
||||||
|
import Fadeable from "../Fadeable/Fadeable";
|
||||||
|
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||||
|
import SetTitle from "../SetTitle";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
leaving: boolean;
|
leaving: boolean;
|
||||||
@@ -15,11 +16,19 @@ const PageHelp: React.FC<Props> = ({ leaving, onLeave }) => {
|
|||||||
const { pageHelp } = useMessages();
|
const { pageHelp } = useMessages();
|
||||||
|
|
||||||
if (pageHelp) {
|
if (pageHelp) {
|
||||||
const { paragraphs, quadrants, rings, sourcecodeLink, headlinePrefix, quadrantsPreDescription, ringsPreDescription } = pageHelp;
|
const {
|
||||||
const title = `${headlinePrefix || 'How to use the'} ${radarName}`;
|
paragraphs,
|
||||||
|
quadrants,
|
||||||
|
rings,
|
||||||
|
sourcecodeLink,
|
||||||
|
headlinePrefix,
|
||||||
|
quadrantsPreDescription,
|
||||||
|
ringsPreDescription,
|
||||||
|
} = pageHelp;
|
||||||
|
const title = `${headlinePrefix || "How to use the"} ${radarName}`;
|
||||||
return (
|
return (
|
||||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||||
<SetTitle title={title}/>
|
<SetTitle title={title} />
|
||||||
<HeroHeadline>{title}</HeroHeadline>
|
<HeroHeadline>{title}</HeroHeadline>
|
||||||
<div className="fullpage-content">
|
<div className="fullpage-content">
|
||||||
{paragraphs.map(({ headline, values }) => (
|
{paragraphs.map(({ headline, values }) => (
|
||||||
@@ -27,10 +36,12 @@ const PageHelp: React.FC<Props> = ({ leaving, onLeave }) => {
|
|||||||
<h3>{headline}</h3>
|
<h3>{headline}</h3>
|
||||||
{values.map((element, index) => {
|
{values.map((element, index) => {
|
||||||
return (
|
return (
|
||||||
<p key={index} dangerouslySetInnerHTML={sanitize(element)}></p>
|
<p
|
||||||
)
|
key={index}
|
||||||
})
|
dangerouslySetInnerHTML={sanitize(element)}
|
||||||
}
|
></p>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
@@ -38,16 +49,25 @@ const PageHelp: React.FC<Props> = ({ leaving, onLeave }) => {
|
|||||||
<ul>
|
<ul>
|
||||||
{quadrants.map(({ name, description }) => (
|
{quadrants.map(({ name, description }) => (
|
||||||
<li key={name}>
|
<li key={name}>
|
||||||
<strong>{name}:</strong> <span dangerouslySetInnerHTML={sanitize(description, {})}></span>
|
<strong>{name}:</strong>{" "}
|
||||||
|
<span
|
||||||
|
dangerouslySetInnerHTML={sanitize(description, {})}
|
||||||
|
></span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>{ringsPreDescription ?? "Each of the items is classified in one of these rings:"}</p>
|
<p>
|
||||||
|
{ringsPreDescription ??
|
||||||
|
"Each of the items is classified in one of these rings:"}
|
||||||
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
{rings.map(({ name, description }) => (
|
{rings.map(({ name, description }) => (
|
||||||
<li key={name}>
|
<li key={name}>
|
||||||
<strong>{name}:</strong> <span dangerouslySetInnerHTML={sanitize(description, {})}></span>
|
<strong>{name}:</strong>{" "}
|
||||||
|
<span
|
||||||
|
dangerouslySetInnerHTML={sanitize(description, {})}
|
||||||
|
></span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
|
import { MomentInput } from "moment";
|
||||||
|
|
||||||
|
import { ConfigData, radarName, radarNameShort } from "../../config";
|
||||||
|
import { useMessages } from "../../context/MessagesContext";
|
||||||
import { formatRelease } from "../../date";
|
import { formatRelease } from "../../date";
|
||||||
import { featuredOnly, Item, HomepageOption } from "../../model";
|
import { HomepageOption, Item, featuredOnly } from "../../model";
|
||||||
|
import Fadeable from "../Fadeable/Fadeable";
|
||||||
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||||
import QuadrantGrid from "../QuadrantGrid/QuadrantGrid";
|
import QuadrantGrid from "../QuadrantGrid/QuadrantGrid";
|
||||||
import RadarGrid from '../RadarGrid/RadarGrid';
|
import RadarGrid from "../RadarGrid/RadarGrid";
|
||||||
import Fadeable from "../Fadeable/Fadeable";
|
|
||||||
import SetTitle from "../SetTitle";
|
import SetTitle from "../SetTitle";
|
||||||
import { ConfigData, radarName, radarNameShort } from "../../config";
|
|
||||||
import { MomentInput } from "moment";
|
|
||||||
import { useMessages } from "../../context/MessagesContext";
|
|
||||||
|
|
||||||
type PageIndexProps = {
|
type PageIndexProps = {
|
||||||
leaving: boolean;
|
leaving: boolean;
|
||||||
@@ -25,12 +26,16 @@ export default function PageIndex({
|
|||||||
releases,
|
releases,
|
||||||
}: PageIndexProps) {
|
}: PageIndexProps) {
|
||||||
const { pageIndex } = useMessages();
|
const { pageIndex } = useMessages();
|
||||||
const publishedLabel = pageIndex?.publishedLabel || 'Published';
|
const publishedLabel = pageIndex?.publishedLabel || "Published";
|
||||||
|
|
||||||
const newestRelease = releases.slice(-1)[0];
|
const newestRelease = releases.slice(-1)[0];
|
||||||
const numberOfReleases = releases.length;
|
const numberOfReleases = releases.length;
|
||||||
const showChart = config.homepageContent === HomepageOption.chart || config.homepageContent === HomepageOption.both;
|
const showChart =
|
||||||
const showColumns = config.homepageContent === HomepageOption.columns || config.homepageContent === HomepageOption.both;
|
config.homepageContent === HomepageOption.chart ||
|
||||||
|
config.homepageContent === HomepageOption.both;
|
||||||
|
const showColumns =
|
||||||
|
config.homepageContent === HomepageOption.columns ||
|
||||||
|
config.homepageContent === HomepageOption.both;
|
||||||
return (
|
return (
|
||||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||||
<SetTitle title={radarNameShort} />
|
<SetTitle title={radarNameShort} />
|
||||||
@@ -39,9 +44,7 @@ export default function PageIndex({
|
|||||||
{radarName}
|
{radarName}
|
||||||
</HeroHeadline>
|
</HeroHeadline>
|
||||||
</div>
|
</div>
|
||||||
{showChart && (
|
{showChart && <RadarGrid items={featuredOnly(items)} config={config} />}
|
||||||
<RadarGrid items={featuredOnly(items)} config={config} />
|
|
||||||
)}
|
|
||||||
{showColumns && (
|
{showColumns && (
|
||||||
<QuadrantGrid items={featuredOnly(items)} config={config} />
|
<QuadrantGrid items={featuredOnly(items)} config={config} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
import { ConfigData, translate } from "../../config";
|
||||||
|
import { useMessages } from "../../context/MessagesContext";
|
||||||
|
import { Item, groupByQuadrants } from "../../model";
|
||||||
import Badge from "../Badge/Badge";
|
import Badge from "../Badge/Badge";
|
||||||
import EditButton from "../EditButton/EditButton";
|
import EditButton from "../EditButton/EditButton";
|
||||||
import ItemList from "../ItemList/ItemList";
|
|
||||||
import Link from "../Link/Link";
|
|
||||||
import FooterEnd from "../FooterEnd/FooterEnd";
|
import FooterEnd from "../FooterEnd/FooterEnd";
|
||||||
import SetTitle from "../SetTitle";
|
import ItemList from "../ItemList/ItemList";
|
||||||
import ItemRevisions from "../ItemRevisions/ItemRevisions";
|
import ItemRevisions from "../ItemRevisions/ItemRevisions";
|
||||||
import { useAnimations } from "./useAnimations";
|
import Link from "../Link/Link";
|
||||||
|
import SetTitle from "../SetTitle";
|
||||||
import "./item-page.scss";
|
import "./item-page.scss";
|
||||||
import { ConfigData, translate } from "../../config";
|
import { useAnimations } from "./useAnimations";
|
||||||
import {
|
|
||||||
groupByQuadrants,
|
|
||||||
Item,
|
|
||||||
} from "../../model";
|
|
||||||
import { useMessages } from "../../context/MessagesContext";
|
|
||||||
|
|
||||||
const getItem = (pageName: string, items: Item[]) => {
|
const getItem = (pageName: string, items: Item[]) => {
|
||||||
const [quadrantName, itemName] = pageName.split("/");
|
const [quadrantName, itemName] = pageName.split("/");
|
||||||
@@ -35,9 +33,15 @@ type Props = {
|
|||||||
onLeave: () => void;
|
onLeave: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PageItem: React.FC<Props> = ({ pageName, items, config, leaving, onLeave }) => {
|
const PageItem: React.FC<Props> = ({
|
||||||
|
pageName,
|
||||||
|
items,
|
||||||
|
config,
|
||||||
|
leaving,
|
||||||
|
onLeave,
|
||||||
|
}) => {
|
||||||
const { pageItem } = useMessages();
|
const { pageItem } = useMessages();
|
||||||
const quadrantOverview = pageItem?.quadrantOverview || 'Quadrant Overview';
|
const quadrantOverview = pageItem?.quadrantOverview || "Quadrant Overview";
|
||||||
|
|
||||||
const itemsInRing = getItemsInRing(pageName, items);
|
const itemsInRing = getItemsInRing(pageName, items);
|
||||||
|
|
||||||
@@ -48,7 +52,13 @@ const PageItem: React.FC<Props> = ({ pageName, items, config, leaving, onLeave }
|
|||||||
});
|
});
|
||||||
|
|
||||||
const item = getItem(pageName, items);
|
const item = getItem(pageName, items);
|
||||||
const editButton = config.editLink ? <EditButton baseUrl={config.editLink.radarLink} item={item} title={config.editLink.title}/> : null
|
const editButton = config.editLink ? (
|
||||||
|
<EditButton
|
||||||
|
baseUrl={config.editLink.radarLink}
|
||||||
|
item={item}
|
||||||
|
title={config.editLink.title}
|
||||||
|
/>
|
||||||
|
) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -119,7 +129,10 @@ const PageItem: React.FC<Props> = ({ pageName, items, config, leaving, onLeave }
|
|||||||
dangerouslySetInnerHTML={{ __html: item.body }}
|
dangerouslySetInnerHTML={{ __html: item.body }}
|
||||||
/>
|
/>
|
||||||
{item.revisions.length > 1 && (
|
{item.revisions.length > 1 && (
|
||||||
<ItemRevisions revisions={item.revisions.slice(1)} dateFormat={config.dateFormat} />
|
<ItemRevisions
|
||||||
|
revisions={item.revisions.slice(1)}
|
||||||
|
dateFormat={config.dateFormat}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useState, useMemo } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Animation,
|
Animation,
|
||||||
AnimationStates,
|
AnimationStates,
|
||||||
@@ -13,18 +14,14 @@ interface Props {
|
|||||||
onLeave: () => void;
|
onLeave: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useAnimations = ({
|
export const useAnimations = ({ itemsInRing, leaving, onLeave }: Props) => {
|
||||||
itemsInRing,
|
|
||||||
leaving,
|
|
||||||
onLeave,
|
|
||||||
}: Props) => {
|
|
||||||
type AnimationConfig = {
|
type AnimationConfig = {
|
||||||
background: Animation;
|
background: Animation;
|
||||||
navHeader: Animation;
|
navHeader: Animation;
|
||||||
text: Animation;
|
text: Animation;
|
||||||
items: Animation[];
|
items: Animation[];
|
||||||
footer: Animation;
|
footer: Animation;
|
||||||
}
|
};
|
||||||
|
|
||||||
type AnimationNames = keyof AnimationConfig;
|
type AnimationNames = keyof AnimationConfig;
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
|
import { ConfigData, translate } from "../../config";
|
||||||
|
import { Item, groupByQuadrants } from "../../model";
|
||||||
import Badge from "../Badge/Badge";
|
import Badge from "../Badge/Badge";
|
||||||
import EditButton from "../EditButton/EditButton";
|
import EditButton from "../EditButton/EditButton";
|
||||||
import ItemList from "../ItemList/ItemList";
|
|
||||||
import Link from "../Link/Link";
|
|
||||||
import Fadeable from "../Fadeable/Fadeable";
|
import Fadeable from "../Fadeable/Fadeable";
|
||||||
import SetTitle from "../SetTitle";
|
import ItemList from "../ItemList/ItemList";
|
||||||
import ItemRevisions from "../ItemRevisions/ItemRevisions";
|
import ItemRevisions from "../ItemRevisions/ItemRevisions";
|
||||||
|
import Link from "../Link/Link";
|
||||||
import { ConfigData, translate } from "../../config";
|
import SetTitle from "../SetTitle";
|
||||||
import { groupByQuadrants, Item } from "../../model";
|
|
||||||
|
|
||||||
type PageItemMobileProps = {
|
type PageItemMobileProps = {
|
||||||
pageName: string;
|
pageName: string;
|
||||||
@@ -40,7 +39,13 @@ export default function PageItemMobile({
|
|||||||
|
|
||||||
const item = getItem(pageName, items);
|
const item = getItem(pageName, items);
|
||||||
const itemsInRing = getItemsInRing(pageName, items);
|
const itemsInRing = getItemsInRing(pageName, items);
|
||||||
const editButton = config.editLink ? <EditButton baseUrl={config.editLink.radarLink} item={item} title={config.editLink.title}/> : null
|
const editButton = config.editLink ? (
|
||||||
|
<EditButton
|
||||||
|
baseUrl={config.editLink.radarLink}
|
||||||
|
item={item}
|
||||||
|
title={config.editLink.title}
|
||||||
|
/>
|
||||||
|
) : null;
|
||||||
return (
|
return (
|
||||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||||
<SetTitle title={item.title} />
|
<SetTitle title={item.title} />
|
||||||
@@ -50,7 +55,9 @@ export default function PageItemMobile({
|
|||||||
<div className="mobile-item-page__header">
|
<div className="mobile-item-page__header">
|
||||||
<div className="split">
|
<div className="split">
|
||||||
<div className="split__left">
|
<div className="split__left">
|
||||||
<h3 className="headline">{translate(config, item.quadrant)}</h3>
|
<h3 className="headline">
|
||||||
|
{translate(config, item.quadrant)}
|
||||||
|
</h3>
|
||||||
<h1 className="hero-headline hero-headline--inverse">
|
<h1 className="hero-headline hero-headline--inverse">
|
||||||
{item.title}
|
{item.title}
|
||||||
</h1>
|
</h1>
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import HeadlineGroup from "../HeadlineGroup/HeadlineGroup";
|
|
||||||
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
|
||||||
import Badge from "../Badge/Badge";
|
|
||||||
import Link from "../Link/Link";
|
|
||||||
import Search from "../Search/Search";
|
|
||||||
import Fadeable from "../Fadeable/Fadeable";
|
|
||||||
import SetTitle from "../SetTitle";
|
|
||||||
import Flag from "../Flag/Flag";
|
|
||||||
import { groupByFirstLetter, Item } from "../../model";
|
|
||||||
import { ConfigData, translate } from "../../config";
|
import { ConfigData, translate } from "../../config";
|
||||||
import { useMessages } from "../../context/MessagesContext";
|
import { useMessages } from "../../context/MessagesContext";
|
||||||
|
import { Item, groupByFirstLetter } from "../../model";
|
||||||
|
import Badge from "../Badge/Badge";
|
||||||
|
import Fadeable from "../Fadeable/Fadeable";
|
||||||
|
import Flag from "../Flag/Flag";
|
||||||
|
import HeadlineGroup from "../HeadlineGroup/HeadlineGroup";
|
||||||
|
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||||
|
import Link from "../Link/Link";
|
||||||
|
import Search from "../Search/Search";
|
||||||
|
import SetTitle from "../SetTitle";
|
||||||
|
|
||||||
const containsSearchTerm = (text = "", term = "") => {
|
const containsSearchTerm = (text = "", term = "") => {
|
||||||
// TODO search refinement
|
// TODO search refinement
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
|
||||||
import HeadlineGroup from "../HeadlineGroup/HeadlineGroup";
|
|
||||||
import QuadrantSection from "../QuadrantSection/QuadrantSection";
|
|
||||||
import Fadeable from "../Fadeable/Fadeable";
|
|
||||||
import SetTitle from "../SetTitle";
|
|
||||||
|
|
||||||
import { ConfigData, translate } from "../../config";
|
import { ConfigData, translate } from "../../config";
|
||||||
import { featuredOnly, groupByQuadrants, Item } from "../../model";
|
import { Item, featuredOnly, groupByQuadrants } from "../../model";
|
||||||
|
import Fadeable from "../Fadeable/Fadeable";
|
||||||
|
import HeadlineGroup from "../HeadlineGroup/HeadlineGroup";
|
||||||
|
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||||
|
import QuadrantSection from "../QuadrantSection/QuadrantSection";
|
||||||
|
import SetTitle from "../SetTitle";
|
||||||
|
|
||||||
type PageQuadrantProps = {
|
type PageQuadrantProps = {
|
||||||
leaving: boolean;
|
leaving: boolean;
|
||||||
|
|||||||
@@ -1,21 +1,39 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { groupByQuadrants, Item, Group } from "../../model";
|
|
||||||
import { ConfigData } from "../../config";
|
import { ConfigData } from "../../config";
|
||||||
|
import { Group, Item, groupByQuadrants } from "../../model";
|
||||||
import QuadrantSection from "../QuadrantSection/QuadrantSection";
|
import QuadrantSection from "../QuadrantSection/QuadrantSection";
|
||||||
import "./quadrant-grid.scss";
|
import "./quadrant-grid.scss";
|
||||||
const renderQuadrant = (quadrantName: string, groups: Group, config: ConfigData) => {
|
|
||||||
|
const renderQuadrant = (
|
||||||
|
quadrantName: string,
|
||||||
|
groups: Group,
|
||||||
|
config: ConfigData
|
||||||
|
) => {
|
||||||
return (
|
return (
|
||||||
<div key={quadrantName} className="quadrant-grid__quadrant">
|
<div key={quadrantName} className="quadrant-grid__quadrant">
|
||||||
<QuadrantSection quadrantName={quadrantName} groups={groups} config={config} />
|
<QuadrantSection
|
||||||
|
quadrantName={quadrantName}
|
||||||
|
groups={groups}
|
||||||
|
config={config}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function QuadrantGrid({ items, config }: { items: Item[], config: ConfigData }) {
|
export default function QuadrantGrid({
|
||||||
|
items,
|
||||||
|
config,
|
||||||
|
}: {
|
||||||
|
items: Item[];
|
||||||
|
config: ConfigData;
|
||||||
|
}) {
|
||||||
const groups = groupByQuadrants(items);
|
const groups = groupByQuadrants(items);
|
||||||
return (
|
return (
|
||||||
<div className="quadrant-grid">
|
<div className="quadrant-grid">
|
||||||
{Object.keys(config.quadrants).map((quadrantName: string) => renderQuadrant(quadrantName, groups, config))}
|
{Object.keys(config.quadrants).map((quadrantName: string) =>
|
||||||
|
renderQuadrant(quadrantName, groups, config)
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { translate, ConfigData } from "../../config";
|
import { ConfigData, translate } from "../../config";
|
||||||
import Badge from "../Badge/Badge";
|
|
||||||
import Link from "../Link/Link";
|
|
||||||
import ItemList from "../ItemList/ItemList";
|
|
||||||
import Flag from "../Flag/Flag";
|
|
||||||
import { Group } from "../../model";
|
import { Group } from "../../model";
|
||||||
|
import Badge from "../Badge/Badge";
|
||||||
|
import Flag from "../Flag/Flag";
|
||||||
|
import ItemList from "../ItemList/ItemList";
|
||||||
|
import Link from "../Link/Link";
|
||||||
import "./quadrant-section.scss";
|
import "./quadrant-section.scss";
|
||||||
|
|
||||||
const renderList = (
|
const renderList = (
|
||||||
ringName: string,
|
ringName: string,
|
||||||
quadrantName: string,
|
quadrantName: string,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import RadarChart from "../Chart/RadarChart";
|
|
||||||
import { ConfigData } from "../../config";
|
import { ConfigData } from "../../config";
|
||||||
import { Item, QuadrantConfig } from "../../model";
|
import { Item, QuadrantConfig } from "../../model";
|
||||||
|
import RadarChart from "../Chart/RadarChart";
|
||||||
import "./radar-grid.scss";
|
|
||||||
import Link from "../Link/Link";
|
import Link from "../Link/Link";
|
||||||
|
import "./radar-grid.scss";
|
||||||
|
|
||||||
const QuadrantLabel: React.FC<{
|
const QuadrantLabel: React.FC<{
|
||||||
quadrantConfig: QuadrantConfig;
|
quadrantConfig: QuadrantConfig;
|
||||||
@@ -19,7 +19,10 @@ const QuadrantLabel: React.FC<{
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="quadrant-label" style={stylesMap[quadrantConfig.position - 1]}>
|
<div
|
||||||
|
className="quadrant-label"
|
||||||
|
style={stylesMap[quadrantConfig.position - 1]}
|
||||||
|
>
|
||||||
<div className="split">
|
<div className="split">
|
||||||
<div className="split__left">
|
<div className="split__left">
|
||||||
<small>Quadrant {quadrantConfig.position}</small>
|
<small>Quadrant {quadrantConfig.position}</small>
|
||||||
@@ -65,7 +68,12 @@ const RadarGrid: React.FC<{ items: Item[]; config: ConfigData }> = ({
|
|||||||
<div className="radar-grid">
|
<div className="radar-grid">
|
||||||
<RadarChart items={items} config={config} />
|
<RadarChart items={items} config={config} />
|
||||||
{Object.entries(config.quadrantsMap).map(([name, quadrant], index) => (
|
{Object.entries(config.quadrantsMap).map(([name, quadrant], index) => (
|
||||||
<QuadrantLabel key={index} quadrantConfig={quadrant} quadrantName={name} quadrantLabel={config.quadrants[name]} />
|
<QuadrantLabel
|
||||||
|
key={index}
|
||||||
|
quadrantConfig={quadrant}
|
||||||
|
quadrantName={name}
|
||||||
|
quadrantLabel={config.quadrants[name]}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
<Legend />
|
<Legend />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import PageIndex from "./PageIndex/PageIndex";
|
|
||||||
import PageOverview from "./PageOverview/PageOverview";
|
|
||||||
import PageHelp from "./PageHelp/PageHelp";
|
|
||||||
import PageQuadrant from "./PageQuadrant/PageQuadrant";
|
|
||||||
import PageItem from "./PageItem/PageItem";
|
|
||||||
import PageItemMobile from "./PageItemMobile/PageItemMobile";
|
|
||||||
import { ConfigData, getItemPageNames, isMobileViewport } from "../config";
|
import { ConfigData, getItemPageNames, isMobileViewport } from "../config";
|
||||||
import { Item } from "../model";
|
import { Item } from "../model";
|
||||||
|
import PageHelp from "./PageHelp/PageHelp";
|
||||||
|
import PageIndex from "./PageIndex/PageIndex";
|
||||||
|
import PageItem from "./PageItem/PageItem";
|
||||||
|
import PageItemMobile from "./PageItemMobile/PageItemMobile";
|
||||||
|
import PageOverview from "./PageOverview/PageOverview";
|
||||||
|
import PageQuadrant from "./PageQuadrant/PageQuadrant";
|
||||||
|
|
||||||
type RouterProps = {
|
type RouterProps = {
|
||||||
pageName: string;
|
pageName: string;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { FormEvent } from "react";
|
|
||||||
import { useMessages } from "../../context/MessagesContext";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import React, { FormEvent } from "react";
|
||||||
|
|
||||||
|
import { useMessages } from "../../context/MessagesContext";
|
||||||
import "./search.scss";
|
import "./search.scss";
|
||||||
|
|
||||||
type SearchProps = {
|
type SearchProps = {
|
||||||
@@ -52,7 +53,7 @@ function Search(
|
|||||||
<span className={classNames("search__button", { "is-open": open })}>
|
<span className={classNames("search__button", { "is-open": open })}>
|
||||||
<button type="submit" className="button">
|
<button type="submit" className="button">
|
||||||
<span className="icon icon--search button__icon" />
|
<span className="icon icon--search button__icon" />
|
||||||
{ searchLabel }
|
{searchLabel}
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
{closable && (
|
{closable && (
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
import { radarName } from "../config";
|
import { radarName } from "../config";
|
||||||
|
|
||||||
type SetTitleProps = {
|
type SetTitleProps = {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
|
FaExternalLinkAlt,
|
||||||
FaFacebookF,
|
FaFacebookF,
|
||||||
FaTwitter,
|
|
||||||
FaLinkedinIn,
|
|
||||||
FaXing,
|
|
||||||
FaYoutube,
|
|
||||||
FaGithub,
|
FaGithub,
|
||||||
FaInstagram,
|
FaInstagram,
|
||||||
FaExternalLinkAlt
|
FaLinkedinIn,
|
||||||
|
FaTwitter,
|
||||||
|
FaXing,
|
||||||
|
FaYoutube,
|
||||||
} from "react-icons/fa";
|
} from "react-icons/fa";
|
||||||
|
|
||||||
const icons = {
|
const icons = {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {Item, HomepageOption, QuadrantConfig} from './model';
|
import { HomepageOption, Item, QuadrantConfig } from "./model";
|
||||||
|
|
||||||
export interface ConfigData {
|
export interface ConfigData {
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
@@ -7,17 +7,17 @@ export interface ConfigData {
|
|||||||
showEmptyRings: boolean;
|
showEmptyRings: boolean;
|
||||||
quadrantsMap: { [quadrant: string]: QuadrantConfig };
|
quadrantsMap: { [quadrant: string]: QuadrantConfig };
|
||||||
chartConfig: {
|
chartConfig: {
|
||||||
size: number,
|
size: number;
|
||||||
scale: number[],
|
scale: number[];
|
||||||
blipSize: number,
|
blipSize: number;
|
||||||
ringsAttributes: {radius: number, arcWidth: number}[]
|
ringsAttributes: { radius: number; arcWidth: number }[];
|
||||||
};
|
};
|
||||||
homepageContent: HomepageOption;
|
homepageContent: HomepageOption;
|
||||||
dateFormat?: string;
|
dateFormat?: string;
|
||||||
editLink?: {
|
editLink?: {
|
||||||
radarLink: string,
|
radarLink: string;
|
||||||
title?: string
|
title?: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const radarName =
|
export const radarName =
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { createContext, FC, useContext } from "react";
|
import { FC, createContext, useContext } from "react";
|
||||||
|
|
||||||
import { Props as SocialLink } from "../../components/SocialLink/SocialLink";
|
import { Props as SocialLink } from "../../components/SocialLink/SocialLink";
|
||||||
|
|
||||||
interface Quadrant {
|
interface Quadrant {
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
|
||||||
import { formatRelease } from "./date";
|
import { formatRelease } from "./date";
|
||||||
|
|
||||||
describe("formatRelease", () => {
|
describe("formatRelease", () => {
|
||||||
it("should format a date object using default output format", () => {
|
it("should format a date object using default output format", () => {
|
||||||
expect(formatRelease(moment('2022-01-05'))).toEqual('January 2022')
|
expect(formatRelease(moment("2022-01-05"))).toEqual("January 2022");
|
||||||
});
|
});
|
||||||
it("should format a date object using a custom output format", () => {
|
it("should format a date object using a custom output format", () => {
|
||||||
expect(formatRelease(moment('2022-01-05'), 'DD.MM.YYYY')).toEqual('05.01.2022')
|
expect(formatRelease(moment("2022-01-05"), "DD.MM.YYYY")).toEqual(
|
||||||
|
"05.01.2022"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,5 +3,7 @@ import moment from "moment";
|
|||||||
const isoDateToMoment = (isoDate: moment.MomentInput) =>
|
const isoDateToMoment = (isoDate: moment.MomentInput) =>
|
||||||
moment(isoDate, "YYYY-MM-DD");
|
moment(isoDate, "YYYY-MM-DD");
|
||||||
|
|
||||||
export const formatRelease = (isoDate: moment.MomentInput, format: string = "MMMM YYYY") =>
|
export const formatRelease = (
|
||||||
isoDateToMoment(isoDate).format(format);
|
isoDate: moment.MomentInput,
|
||||||
|
format: string = "MMMM YYYY"
|
||||||
|
) => isoDateToMoment(isoDate).format(format);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import App from "./components/App";
|
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
|
|
||||||
|
import App from "./components/App";
|
||||||
import "./index.scss";
|
import "./index.scss";
|
||||||
|
|
||||||
const container = document.getElementById("root")!;
|
const container = document.getElementById("root")!;
|
||||||
|
|||||||
36
src/model.ts
36
src/model.ts
@@ -1,7 +1,7 @@
|
|||||||
export enum HomepageOption {
|
export enum HomepageOption {
|
||||||
chart = "chart",
|
chart = "chart",
|
||||||
columns = "columns",
|
columns = "columns",
|
||||||
both = "both"
|
both = "both",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ItemAttributes = {
|
export type ItemAttributes = {
|
||||||
@@ -14,9 +14,9 @@ export type ItemAttributes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export enum FlagType {
|
export enum FlagType {
|
||||||
new = 'new',
|
new = "new",
|
||||||
changed = 'changed',
|
changed = "changed",
|
||||||
default = 'default'
|
default = "default",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Item = ItemAttributes & {
|
export type Item = ItemAttributes & {
|
||||||
@@ -28,12 +28,12 @@ export type Item = ItemAttributes & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Blip = Item & {
|
export type Blip = Item & {
|
||||||
quadrantPosition: number
|
quadrantPosition: number;
|
||||||
ringPosition: number
|
ringPosition: number;
|
||||||
colour: string
|
colour: string;
|
||||||
txtColour: string
|
txtColour: string;
|
||||||
coordinates: Point
|
coordinates: Point;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Revision = ItemAttributes & {
|
export type Revision = ItemAttributes & {
|
||||||
body: string;
|
body: string;
|
||||||
@@ -46,11 +46,11 @@ export type Quadrant = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type QuadrantConfig = {
|
export type QuadrantConfig = {
|
||||||
colour: string,
|
colour: string;
|
||||||
txtColour: string,
|
txtColour: string;
|
||||||
position: number,
|
position: number;
|
||||||
description: string
|
description: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Radar = {
|
export type Radar = {
|
||||||
items: Item[];
|
items: Item[];
|
||||||
@@ -67,9 +67,9 @@ export const nonFeaturedOnly = (items: Item[]) =>
|
|||||||
items.filter((item) => !item.featured);
|
items.filter((item) => !item.featured);
|
||||||
|
|
||||||
export type Point = {
|
export type Point = {
|
||||||
x: number,
|
x: number;
|
||||||
y: number
|
y: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const groupByQuadrants = (items: Item[]): Group =>
|
export const groupByQuadrants = (items: Item[]): Group =>
|
||||||
items.reduce(
|
items.reduce(
|
||||||
|
|||||||
@@ -2,17 +2,22 @@ import { sanitize } from "./sanitize";
|
|||||||
|
|
||||||
describe("sanitize", () => {
|
describe("sanitize", () => {
|
||||||
it("should sanitize the string input to HTML output", () => {
|
it("should sanitize the string input to HTML output", () => {
|
||||||
let res = sanitize('foo');
|
let res = sanitize("foo");
|
||||||
expect(res.__html).toEqual("foo");
|
expect(res.__html).toEqual("foo");
|
||||||
res = sanitize('<a href="https://example.org">Example.org</a>');
|
res = sanitize('<a href="https://example.org">Example.org</a>');
|
||||||
expect(res.__html).toEqual("<a href=\"https://example.org\">Example.org</a>");
|
expect(res.__html).toEqual('<a href="https://example.org">Example.org</a>');
|
||||||
});
|
});
|
||||||
it("should not sanitize not allowed tags", () => {
|
it("should not sanitize not allowed tags", () => {
|
||||||
let res = sanitize('Before <iframe src="https://example.org"></iframe> After');
|
let res = sanitize(
|
||||||
|
'Before <iframe src="https://example.org"></iframe> After'
|
||||||
|
);
|
||||||
expect(res.__html).toEqual("Before After");
|
expect(res.__html).toEqual("Before After");
|
||||||
});
|
});
|
||||||
it("should accept options for rendering", () => {
|
it("should accept options for rendering", () => {
|
||||||
let res = sanitize('<a href="https://example.org" target="_blank">Example.org</a>', { allowedAttributes: { a: ['href']}});
|
let res = sanitize(
|
||||||
expect(res.__html).toEqual("<a href=\"https://example.org\">Example.org</a>");
|
'<a href="https://example.org" target="_blank">Example.org</a>',
|
||||||
|
{ allowedAttributes: { a: ["href"] } }
|
||||||
|
);
|
||||||
|
expect(res.__html).toEqual('<a href="https://example.org">Example.org</a>');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import sanitizeHtml from 'sanitize-html';
|
import sanitizeHtml from "sanitize-html";
|
||||||
|
|
||||||
const defaultSanitizeOptions = {
|
const defaultSanitizeOptions = {
|
||||||
allowedTags: ['b', 'i', 'em', 'strong', 'a', 'ul', 'ol', 'li'],
|
allowedTags: ["b", "i", "em", "strong", "a", "ul", "ol", "li"],
|
||||||
allowedAttributes: {
|
allowedAttributes: {
|
||||||
'a': ['href', 'target']
|
a: ["href", "target"],
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
export const sanitize = (dirty: string, options: sanitizeHtml.IOptions = defaultSanitizeOptions) => ({
|
export const sanitize = (
|
||||||
__html: sanitizeHtml(
|
dirty: string,
|
||||||
dirty,
|
options: sanitizeHtml.IOptions = defaultSanitizeOptions
|
||||||
options
|
) => ({
|
||||||
)
|
__html: sanitizeHtml(dirty, options),
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user