build tooling update
This commit is contained in:
Родитель
8a5be5d935
Коммит
f0bac2d00d
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"extends": "@essex/eslint-config",
|
||||
"extends": "@essex/eslint-config/rome",
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/no-non-null-assertion": 0,
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
#!/bin/sh
|
||||
if [ -z "$husky_skip_init" ]; then
|
||||
debug () {
|
||||
[ "$HUSKY_DEBUG" = "1" ] && echo "husky (debug) - $1"
|
||||
}
|
||||
|
||||
readonly hook_name="$(basename "$0")"
|
||||
debug "starting $hook_name..."
|
||||
|
||||
if [ "$HUSKY" = "0" ]; then
|
||||
debug "HUSKY env variable is set to 0, skipping hook"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -f ~/.huskyrc ]; then
|
||||
debug "sourcing ~/.huskyrc"
|
||||
. ~/.huskyrc
|
||||
fi
|
||||
|
||||
export readonly husky_skip_init=1
|
||||
sh -e "$0" "$@"
|
||||
exitCode="$?"
|
||||
|
||||
if [ $exitCode != 0 ]; then
|
||||
echo "husky - $hook_name hook exited with code $exitCode (error)"
|
||||
exit $exitCode
|
||||
fi
|
||||
|
||||
exit 0
|
||||
fi
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"hooks": {
|
||||
"pre-commit": "yarn essex pre-commit"
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"*": ["yarn essex prettify --staged"],
|
||||
"*.md": ["yarn essex lint --docs-only"],
|
||||
"*.{js,jsx,ts,tsx}": ["yarn essex lint --fix --staged"]
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"arcanis.vscode-zipfs",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
"recommendations": [
|
||||
"arcanis.vscode-zipfs",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
{
|
||||
"search.exclude": {
|
||||
"**/.yarn": true,
|
||||
"**/.pnp.*": true
|
||||
},
|
||||
"eslint.nodePath": ".yarn/sdks",
|
||||
"prettier.prettierPath": ".yarn/sdks/prettier/index.js",
|
||||
"typescript.tsdk": ".yarn/sdks/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"*.tsx": "${capture}.ts, ${capture}.hooks.ts, ${capture}.hooks.tsx, ${capture}.stories.tsx, ${capture}.spec.tsx, ${capture}.base.ts, ${capture}.base.tsx, ${capture}.types.ts, ${capture}.styles.ts, ${capture}.styles.tsx, ${capture}.utils.ts, ${capture}.utils.tsx, ${capture}.module.scss, ${capture}.module.css, ${capture}.md, ${capture}.css",
|
||||
"*.ts": "${capture}.ts, ${capture}.hooks.ts, ${capture}.hooks.tsx, ${capture}.stories.tsx, ${capture}.spec.tsx, ${capture}.base.ts, ${capture}.base.tsx, ${capture}.types.ts, ${capture}.styles.ts, ${capture}.styles.tsx, ${capture}.utils.ts, ${capture}.utils.tsx, ${capture}.module.scss, ${capture}.module.css, ${capture}.md",
|
||||
"*.js": "${capture}.js.map, ${capture}.min.js, ${capture}.d.ts",
|
||||
"*.jsx": "${capture}.js",
|
||||
"package.json": ".npmignore, turbo.json, tsconfig.json, tsconfig.*.json, jest.config.js, .huskyrc.json, .lintstagedrc.json, .editorconfig",
|
||||
".yarnrc.yml": "yarn.lock, .pnp.cjs, .pnp.loader.mjs",
|
||||
".eslintrc": ".eslintignore",
|
||||
".gitignore": ".gitattributes",
|
||||
".prettierrc": ".prettierignore",
|
||||
"README.md": "SECURITY.md, SUPPORT.md, CODE_OF_CONDUCT.md, LICENSE"
|
||||
}
|
||||
"search.exclude": {
|
||||
"**/.yarn": true,
|
||||
"**/.pnp.*": true
|
||||
},
|
||||
"eslint.nodePath": ".yarn/sdks",
|
||||
"prettier.prettierPath": ".yarn/sdks/prettier/index.js",
|
||||
"typescript.tsdk": ".yarn/sdks/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"*.tsx": "${capture}.ts, ${capture}.hooks.ts, ${capture}.hooks.tsx, ${capture}.stories.tsx, ${capture}.spec.tsx, ${capture}.base.ts, ${capture}.base.tsx, ${capture}.types.ts, ${capture}.styles.ts, ${capture}.styles.tsx, ${capture}.utils.ts, ${capture}.utils.tsx, ${capture}.module.scss, ${capture}.module.css, ${capture}.md, ${capture}.css",
|
||||
"*.ts": "${capture}.ts, ${capture}.hooks.ts, ${capture}.hooks.tsx, ${capture}.stories.tsx, ${capture}.spec.tsx, ${capture}.base.ts, ${capture}.base.tsx, ${capture}.types.ts, ${capture}.styles.ts, ${capture}.styles.tsx, ${capture}.utils.ts, ${capture}.utils.tsx, ${capture}.module.scss, ${capture}.module.css, ${capture}.md",
|
||||
"*.js": "${capture}.js.map, ${capture}.min.js, ${capture}.d.ts",
|
||||
"*.jsx": "${capture}.js",
|
||||
"package.json": ".npmignore, turbo.json, tsconfig.json, tsconfig.*.json, jest.config.js, .huskyrc.json, .lintstagedrc.json, .editorconfig",
|
||||
".yarnrc.yml": "yarn.lock, .pnp.cjs, .pnp.loader.mjs",
|
||||
".eslintrc": ".eslintignore",
|
||||
".gitignore": ".gitattributes",
|
||||
".prettierrc": ".prettierignore",
|
||||
"README.md": "SECURITY.md, SUPPORT.md, CODE_OF_CONDUCT.md, LICENSE"
|
||||
}
|
||||
}
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
14
.yarnrc.yml
14
.yarnrc.yml
|
@ -1,3 +1,7 @@
|
|||
compressionLevel: mixed
|
||||
|
||||
enableGlobalCache: false
|
||||
|
||||
packageExtensions:
|
||||
"@essex/webpack-config@*":
|
||||
peerDependencies:
|
||||
|
@ -10,12 +14,4 @@ packageExtensions:
|
|||
dependencies:
|
||||
react-is: ^16
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
|
||||
spec: "@yarnpkg/plugin-typescript"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-version.cjs
|
||||
spec: "@yarnpkg/plugin-version"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.2.1.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.0.0-rc.50.cjs
|
||||
|
|
65
package.json
65
package.json
|
@ -3,11 +3,19 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"_ci_packages": "turbo run ci",
|
||||
"_fix_packages": "turbo run fix",
|
||||
"_check_packages": "turbo run check",
|
||||
"clean": "turbo run clean --parallel",
|
||||
"build": "turbo run build",
|
||||
"bundle": "turbo run bundle",
|
||||
"start": "turbo run start",
|
||||
"ci": "turbo run ci",
|
||||
"rome_check": "rome ci .",
|
||||
"rome_fix": "rome check . --apply-unsafe",
|
||||
"ci": "run-s _ci_packages rome_check",
|
||||
"fix": "run-s _fix_packages rome_fix format",
|
||||
"check": "run-s _check_packages rome_check",
|
||||
"format": "rome format . --write",
|
||||
"prettify": "essex prettify",
|
||||
"deploy": "essex deploy --type azure-blob-storage --storageAccount $STORAGE_ACCOUNT --storageAccountKey $STORAGE_KEY --verbose",
|
||||
"is_clean": "git diff-index HEAD --quiet || git status",
|
||||
|
@ -17,38 +25,37 @@
|
|||
"packages/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@essex/eslint-config": "^20.3.5",
|
||||
"@essex/eslint-plugin": "^20.3.7",
|
||||
"@essex/jest-config": "^21.0.12",
|
||||
"@essex/prettier-config": "^18.0.2",
|
||||
"@essex/scripts": "^22.0.4",
|
||||
"@fluentui/font-icons-mdl2": "^8.4.0",
|
||||
"@fluentui/react": "^8.70.0",
|
||||
"@fluentui/react-hooks": "^8.5.5",
|
||||
"@fluentui/utilities": "^8.8.3",
|
||||
"@types/jest": "^27.5.1",
|
||||
"@types/node": "^17.0.35",
|
||||
"@types/react": "^18.0.9",
|
||||
"@types/react-dom": "^18.0.5",
|
||||
"@essex/eslint-config": "^21.0.1",
|
||||
"@essex/eslint-plugin": "^21.0.1",
|
||||
"@essex/jest-config": "^22.0.0",
|
||||
"@essex/prettier-config": "^18.0.7",
|
||||
"@essex/scripts": "^26.0.0",
|
||||
"@fluentui/font-icons-mdl2": "^8.5.24",
|
||||
"@fluentui/react": "^8.111.1",
|
||||
"@fluentui/react-hooks": "^8.6.29",
|
||||
"@fluentui/utilities": "^8.13.18",
|
||||
"@types/jest": "^29.5.4",
|
||||
"@types/node": "^20.5.7",
|
||||
"@types/react": "^18.2.21",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@types/react-rnd": "^8.0.0",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"core-js": "^3.22.7",
|
||||
"eslint": "^8.16.0",
|
||||
"eslint-import-resolver-node": "^0.3.6",
|
||||
"husky": "^7.0.4",
|
||||
"lint-staged": "^11.2.6",
|
||||
"core-js": "^3.32.1",
|
||||
"eslint": "^8.48.0",
|
||||
"eslint-import-resolver-node": "^0.3.9",
|
||||
"ncp": "^2.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"office-ui-fabric-react": "^7.185.7",
|
||||
"prettier": "^2.6.2",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"office-ui-fabric-react": "^7.204.0",
|
||||
"prettier": "^2.8.8",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-rnd": "^10.3.7",
|
||||
"react-router-dom": "^5.3.1",
|
||||
"regenerator-runtime": "^0.13.9",
|
||||
"tslib": "^2.4.0",
|
||||
"turbo": "^1.2.14",
|
||||
"typescript": "^4.7.2"
|
||||
"react-router-dom": "^5.3.4",
|
||||
"regenerator-runtime": "^0.14.0",
|
||||
"rome": "^12.1.3",
|
||||
"tslib": "^2.6.2",
|
||||
"turbo": "^1.10.13",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
|
@ -56,5 +63,5 @@
|
|||
"not ie 11",
|
||||
"not op_mini all"
|
||||
],
|
||||
"packageManager": "yarn@3.2.1"
|
||||
"packageManager": "yarn@4.0.0-rc.50"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
"scripts": {
|
||||
"clean": "essex clean",
|
||||
"build": "essex build --skipExportCheck --docs",
|
||||
"lint": "essex lint --fix"
|
||||
"fix": "essex fix",
|
||||
"check": "essex check"
|
||||
},
|
||||
"dependencies": {
|
||||
"@essex/toolbox": "3.0.8",
|
||||
|
@ -26,12 +27,12 @@
|
|||
"d3-format": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@essex/scripts": "^22.0.4",
|
||||
"@essex/tsconfig-base": "^1.0.2",
|
||||
"@essex/scripts": "^26.0.0",
|
||||
"@essex/tsconfig-base": "^3.0.0",
|
||||
"@types/d3-format": "^3.0.1",
|
||||
"@types/node": "^17.0.35",
|
||||
"@types/react": "^18.0.9",
|
||||
"react": "^18.1.0"
|
||||
"@types/node": "^20.5.7",
|
||||
"@types/react": "^18.2.21",
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": "*",
|
||||
|
|
|
@ -135,7 +135,7 @@ export class TableCollection<T> {
|
|||
}
|
||||
map(callback: Callback<T>, ordered = false): any[] {
|
||||
const output: T[] = []
|
||||
this.scan(idx => {
|
||||
this.scan((idx) => {
|
||||
const n = new this._Ctor(this._table, idx)
|
||||
if (idx === undefined) return
|
||||
output.push(callback(n, idx))
|
||||
|
@ -217,9 +217,9 @@ export class TableCollection<T> {
|
|||
// note that we assume provided indices are already ordered
|
||||
if (this._indices) {
|
||||
const data = this._table.data()
|
||||
let cont = true
|
||||
const stop = () => (cont = false)
|
||||
this._indices.every(idx => {
|
||||
const cont = true
|
||||
const stop = () => cont === false
|
||||
this._indices.every((idx) => {
|
||||
callback(idx, data, stop)
|
||||
return cont
|
||||
})
|
||||
|
@ -248,7 +248,7 @@ export class TableCollection<T> {
|
|||
acc[col] = val
|
||||
return acc
|
||||
}, {})
|
||||
stop && stop()
|
||||
stop?.()
|
||||
}
|
||||
},
|
||||
true,
|
||||
|
|
|
@ -44,7 +44,7 @@ function hashNodeField(nodes: ColumnTable, field: string) {
|
|||
const hash: any = {}
|
||||
const id = nodes.getter('node.id')
|
||||
const cid = nodes.getter(field)
|
||||
nodes.scan(idx => (hash[id(idx)] = cid(idx)))
|
||||
nodes.scan((idx) => hash[id(idx)] === cid(idx))
|
||||
return hash
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ export function filterEdgesToNodes(
|
|||
|
||||
// note the manual hash: op.has does NOT work with Maps
|
||||
const nodeIds: any = {}
|
||||
nodes.forEach(node => (nodeIds[node.id] = true))
|
||||
nodes.forEach((node) => nodeIds[node.id] === true)
|
||||
return edges
|
||||
.params({
|
||||
nodeIds,
|
||||
|
|
|
@ -43,14 +43,14 @@ export function rename(table: ColumnTable, prefix: string, exclude?: string[]) {
|
|||
}
|
||||
|
||||
export function hasColumn(table: ColumnTable, column: string) {
|
||||
return table.columnNames().some(name => name === column)
|
||||
return table.columnNames().some((name) => name === column)
|
||||
}
|
||||
|
||||
export function columnTypes(table: ColumnTable) {
|
||||
if (table.numRows() === 0) {
|
||||
return []
|
||||
}
|
||||
return table.columnNames().map(name => ({
|
||||
return table.columnNames().map((name) => ({
|
||||
name,
|
||||
type: typeof table.get(name, 0),
|
||||
}))
|
||||
|
@ -85,7 +85,7 @@ function ensureColumn(
|
|||
return table
|
||||
}
|
||||
let fixed
|
||||
variants.some(variant => {
|
||||
variants.some((variant) => {
|
||||
if (hasColumn(table, variant)) {
|
||||
fixed = table.select(all(), {
|
||||
[variant]: name,
|
||||
|
@ -104,7 +104,7 @@ function ensureColumn(
|
|||
* @param table - table to ensure has a standard node id column
|
||||
*/
|
||||
function ensureNodeId(table: ColumnTable) {
|
||||
return ensureColumn(table, 'node.id', ['id', 'ID', 'nodeId'], table => {
|
||||
return ensureColumn(table, 'node.id', ['id', 'ID', 'nodeId'], (table) => {
|
||||
// just pick the first - this is risky, but sometimes we don't have a header at all
|
||||
const column = table.columnNames()[0]
|
||||
return table.select(all(), {
|
||||
|
@ -124,7 +124,7 @@ function ensureCommunityId(table: ColumnTable) {
|
|||
table,
|
||||
'community.id',
|
||||
['node.community', 'cid', 'community', 'clusterId'],
|
||||
table => {
|
||||
(table) => {
|
||||
return table.derive({
|
||||
'community.id': () => '0',
|
||||
})
|
||||
|
@ -137,7 +137,7 @@ function ensureParentCommunityId(table: ColumnTable) {
|
|||
table,
|
||||
'community.pid',
|
||||
['pid', 'parentCluster', 'parent'],
|
||||
table => {
|
||||
(table) => {
|
||||
return table
|
||||
.params({
|
||||
pid: ROOT_COMMUNITY_ID,
|
||||
|
@ -163,7 +163,7 @@ function fixPid(table: ColumnTable) {
|
|||
}
|
||||
|
||||
function ensureX(table: ColumnTable) {
|
||||
return ensureColumn(table, 'node.x', ['x', 'X'], table => {
|
||||
return ensureColumn(table, 'node.x', ['x', 'X'], (table) => {
|
||||
return table.derive({
|
||||
'node.x': () => Math.random(),
|
||||
})
|
||||
|
@ -171,7 +171,7 @@ function ensureX(table: ColumnTable) {
|
|||
}
|
||||
|
||||
function ensureY(table: ColumnTable) {
|
||||
return ensureColumn(table, 'node.y', ['y', 'Y'], table => {
|
||||
return ensureColumn(table, 'node.y', ['y', 'Y'], (table) => {
|
||||
return table.derive({
|
||||
'node.y': () => Math.random(),
|
||||
})
|
||||
|
@ -179,15 +179,20 @@ function ensureY(table: ColumnTable) {
|
|||
}
|
||||
|
||||
function ensureD(table: ColumnTable) {
|
||||
return ensureColumn(table, 'node.d', ['d', 'D', 'size', 'weight'], table => {
|
||||
return table.derive({
|
||||
'node.d': () => 1,
|
||||
})
|
||||
})
|
||||
return ensureColumn(
|
||||
table,
|
||||
'node.d',
|
||||
['d', 'D', 'size', 'weight'],
|
||||
(table) => {
|
||||
return table.derive({
|
||||
'node.d': () => 1,
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
function ensureNodeLabel(table: ColumnTable) {
|
||||
return ensureColumn(table, 'node.label', ['label', 'name'], table => {
|
||||
return ensureColumn(table, 'node.label', ['label', 'name'], (table) => {
|
||||
return table.derive({
|
||||
'node.label': (d: any) => d['node.id'],
|
||||
})
|
||||
|
@ -195,7 +200,7 @@ function ensureNodeLabel(table: ColumnTable) {
|
|||
}
|
||||
|
||||
function ensureEdgeSource(table: ColumnTable) {
|
||||
return ensureColumn(table, 'edge.source', ['source', 'src'], table => {
|
||||
return ensureColumn(table, 'edge.source', ['source', 'src'], (table) => {
|
||||
return table.derive({
|
||||
'edge.source': () => '0',
|
||||
})
|
||||
|
@ -203,7 +208,7 @@ function ensureEdgeSource(table: ColumnTable) {
|
|||
}
|
||||
|
||||
function ensureEdgeTarget(table: ColumnTable) {
|
||||
return ensureColumn(table, 'edge.target', ['target', 'tgt'], table => {
|
||||
return ensureColumn(table, 'edge.target', ['target', 'tgt'], (table) => {
|
||||
return table.derive({
|
||||
'edge.target': () => '1',
|
||||
})
|
||||
|
@ -211,7 +216,7 @@ function ensureEdgeTarget(table: ColumnTable) {
|
|||
}
|
||||
|
||||
function ensureEdgeWeight(table: ColumnTable) {
|
||||
return ensureColumn(table, 'edge.weight', ['weight', 'value'], table => {
|
||||
return ensureColumn(table, 'edge.weight', ['weight', 'value'], (table) => {
|
||||
return table.derive({
|
||||
'edge.weight': () => 1,
|
||||
})
|
||||
|
@ -219,7 +224,7 @@ function ensureEdgeWeight(table: ColumnTable) {
|
|||
}
|
||||
|
||||
function ensureEdgeId(table: ColumnTable) {
|
||||
return ensureColumn(table, 'edge.id', ['id', 'edgeId'], table => {
|
||||
return ensureColumn(table, 'edge.id', ['id', 'edgeId'], (table) => {
|
||||
return table.derive({
|
||||
'edge.id': (d: any) => `${d['edge.source']}-${d['edge.target']}`,
|
||||
})
|
||||
|
@ -278,7 +283,7 @@ const prefixes = {
|
|||
// used for filtering views, etc.
|
||||
// this will find any unprefixed columns and add the specified one to them
|
||||
function prefixRemaining(table: ColumnTable, prefix: string) {
|
||||
const columns = table.columnNames(name => {
|
||||
const columns = table.columnNames((name) => {
|
||||
const pref = name.split('.')[0]
|
||||
return !prefixes[pref]
|
||||
})
|
||||
|
@ -326,7 +331,7 @@ export function initializeNodeTable(table: ColumnTable, fromEdges = false) {
|
|||
ensureD,
|
||||
normalizeXY,
|
||||
normalizeD,
|
||||
table => prefixRemaining(table, 'node'),
|
||||
(table) => prefixRemaining(table, 'node'),
|
||||
checkAndAddChildCount,
|
||||
checkAndAddNodeCount,
|
||||
])
|
||||
|
@ -342,14 +347,14 @@ export function initializeEdgeTable(table: ColumnTable) {
|
|||
ensureEdgeTarget,
|
||||
ensureEdgeId,
|
||||
ensureEdgeWeight,
|
||||
table => prefixRemaining(table, 'edge'),
|
||||
(table) => prefixRemaining(table, 'edge'),
|
||||
])
|
||||
}
|
||||
|
||||
export function initializeCommunityTable(table: ColumnTable) {
|
||||
return chain(table, [
|
||||
ensureCommunityId,
|
||||
table => prefixRemaining(table, 'community'),
|
||||
(table) => prefixRemaining(table, 'community'),
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -366,7 +371,7 @@ export function joinNodeCommunityTables(
|
|||
ensureCommunityId,
|
||||
ensureParentCommunityId,
|
||||
fixPid,
|
||||
table => prefixRemaining(table, 'community'),
|
||||
(table) => prefixRemaining(table, 'community'),
|
||||
])
|
||||
|
||||
const joined = joinWithReplace(nodes, safe, [leftKey, rightKey])
|
||||
|
@ -497,17 +502,17 @@ export function listColumnDefinitions(
|
|||
if (table.numRows() === 0) {
|
||||
return []
|
||||
}
|
||||
return table.columnNames().map(name => ({
|
||||
return table.columnNames().map((name) => ({
|
||||
name,
|
||||
type: name.split('.')[0] as 'node' | 'community',
|
||||
dataType: typeof table.get(name, 0),
|
||||
readOnly: readOnlyNames && readOnlyNames.has(name),
|
||||
readOnly: readOnlyNames?.has(name),
|
||||
}))
|
||||
}
|
||||
|
||||
export function listColumnNames(table: ColumnTable): string[] {
|
||||
const defs = listColumnDefinitions(table)
|
||||
return defs.map(d => d.name)
|
||||
return defs.map((d) => d.name)
|
||||
}
|
||||
|
||||
export function findGroupIndices(
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
import type { Histogram } from '@essex/toolbox'
|
||||
import { histogram } from '@essex/toolbox'
|
||||
import { one, TableCollection } from '@graph-drilldown/arquero'
|
||||
import { TableCollection, one } from '@graph-drilldown/arquero'
|
||||
import type { ColumnStats } from '@graph-drilldown/types'
|
||||
// eslint-disable-next-line
|
||||
import { op } from 'arquero'
|
||||
|
@ -60,7 +60,7 @@ function checkWhole(numbers?: number[]): boolean {
|
|||
if (!numbers) {
|
||||
return false
|
||||
}
|
||||
return numbers.every(n => Number.isInteger(n))
|
||||
return numbers.every((n) => Number.isInteger(n))
|
||||
}
|
||||
|
||||
export function getColumnHistogram(
|
||||
|
@ -119,7 +119,7 @@ export function binTableColumn(table: ColumnTable, column: string): any[] {
|
|||
// fill the bins
|
||||
table.scan((idx: number | undefined) => {
|
||||
const value = table.get(column, idx)
|
||||
const binIndex = bins.findIndex(bin => value >= bin.x0 && value < bin.x1)
|
||||
const binIndex = bins.findIndex((bin) => value >= bin.x0 && value < bin.x1)
|
||||
// bin maxes are exclusive except for the last bin
|
||||
// https://github.com/d3/d3-array/blob/v2.8.0/README.md#_bin
|
||||
const bin = binIndex < 0 ? bins.length - 1 : binIndex
|
||||
|
|
|
@ -17,23 +17,24 @@
|
|||
"scripts": {
|
||||
"clean": "essex clean",
|
||||
"build": "essex build --skipExportCheck --docs",
|
||||
"lint": "essex lint --fix"
|
||||
"fix": "essex fix",
|
||||
"check": "essex check"
|
||||
},
|
||||
"dependencies": {
|
||||
"@essex/hooks": "^4.0.12",
|
||||
"@essex/hooks": "^4.0.21",
|
||||
"@graph-drilldown/arquero": "workspace:packages/arquero",
|
||||
"@graph-drilldown/hooks": "workspace:packages/hooks",
|
||||
"@graph-drilldown/types": "workspace:packages/types",
|
||||
"ahooks": "^3.4.0",
|
||||
"ahooks": "^3.7.8",
|
||||
"d3-brush": "3.0.0",
|
||||
"d3-format": "^3.1.0",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-selection": "3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@essex/scripts": "^22.0.4",
|
||||
"@essex/tsconfig-base": "^1.0.2",
|
||||
"@fluentui/react": "^8.70.0",
|
||||
"@essex/scripts": "^26.0.0",
|
||||
"@essex/tsconfig-base": "^3.0.0",
|
||||
"@fluentui/react": "^8.111.1",
|
||||
"@thematic/color": "^2.0.8",
|
||||
"@thematic/core": "^2.1.1",
|
||||
"@thematic/d3": "^2.0.9",
|
||||
|
@ -43,11 +44,11 @@
|
|||
"@types/d3-format": "^3.0.1",
|
||||
"@types/d3-scale": "^4.0.2",
|
||||
"@types/d3-selection": "^3.0.2",
|
||||
"@types/node": "^17.0.35",
|
||||
"@types/react": "^18.0.9",
|
||||
"@types/node": "^20.5.7",
|
||||
"@types/react": "^18.2.21",
|
||||
"arquero": "^4.8.8",
|
||||
"react": "^18.1.0",
|
||||
"styled-components": "^5.3.5"
|
||||
"react": "^18.2.0",
|
||||
"styled-components": "^6.0.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@fluentui/react": ">= 8",
|
||||
|
@ -60,7 +61,7 @@
|
|||
"@types/react": ">= 17",
|
||||
"arquero": ">= 4",
|
||||
"react": ">= 17",
|
||||
"styled-components": ">= 5"
|
||||
"styled-components": ">= 6"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
|
|
|
@ -20,10 +20,10 @@ import { ThematicPalettePicker } from './ThematicPalettePicker.js'
|
|||
* - Palette: a thematic-bound named color (so it auto-updates with theme changes)
|
||||
* - Scale: field-bound to a thematic scale with domain, range, etc.
|
||||
*/
|
||||
export const ColorSelector: React.FC<ColorSelectorProps> = props => {
|
||||
export const ColorSelector: React.FC<ColorSelectorProps> = (props) => {
|
||||
const { encoding, onChange } = props
|
||||
const handlePivotLinkClick = useCallback(
|
||||
item => {
|
||||
(item) => {
|
||||
onChange({
|
||||
binding: item.props.itemKey,
|
||||
})
|
||||
|
|
|
@ -27,7 +27,7 @@ export const FixedPicker: React.FC<ColorSelectorProps> = ({
|
|||
<ColorPicker
|
||||
color={encoding.value || 'none'}
|
||||
onChange={handlePickerChange}
|
||||
alphaType="none"
|
||||
alphaType='none'
|
||||
/>
|
||||
</Container>
|
||||
)
|
||||
|
|
|
@ -75,7 +75,7 @@ export const ScaledPicker: React.FC<ColorSelectorProps> = ({
|
|||
|
||||
function useFieldDropdownOptions(table: ColumnTable) {
|
||||
return useMemo(() => {
|
||||
return table.columnNames().map(key => ({
|
||||
return table.columnNames().map((key) => ({
|
||||
key,
|
||||
text: key,
|
||||
}))
|
||||
|
|
|
@ -54,13 +54,13 @@ export const DomainBrush: React.FC<DomainBrushProps> = ({
|
|||
)
|
||||
const handleMinChange = useCallback(
|
||||
(_, v: string | undefined) => {
|
||||
onChange && onChange([v ? Number.parseFloat(v) : domain[0], domain[1]])
|
||||
onChange?.([v ? Number.parseFloat(v) : domain[0], domain[1]])
|
||||
},
|
||||
[onChange, domain],
|
||||
)
|
||||
const handleMaxChange = useCallback(
|
||||
(_, v: string | undefined) => {
|
||||
onChange && onChange([domain[0], v ? Number.parseFloat(v) : domain[1]])
|
||||
onChange?.([domain[0], v ? Number.parseFloat(v) : domain[1]])
|
||||
},
|
||||
[onChange, domain],
|
||||
)
|
||||
|
@ -78,7 +78,7 @@ export const DomainBrush: React.FC<DomainBrushProps> = ({
|
|||
// TOOD: it would be nice to use a synchronized internal brush state
|
||||
// to update current displayed bounds visually before setting the encoding
|
||||
const handleBrushEnd = useCallback(
|
||||
newdomain => onChange && onChange(newdomain),
|
||||
(newdomain) => onChange?.(newdomain),
|
||||
[onChange],
|
||||
)
|
||||
|
||||
|
@ -109,7 +109,7 @@ export const DomainBrush: React.FC<DomainBrushProps> = ({
|
|||
<TextContainer>
|
||||
<TextItem>
|
||||
<TextField
|
||||
label="min"
|
||||
label='min'
|
||||
styles={TEXT_STYLES}
|
||||
value={`${flo}`}
|
||||
onChange={handleMinChange}
|
||||
|
@ -118,7 +118,7 @@ export const DomainBrush: React.FC<DomainBrushProps> = ({
|
|||
</TextItem>
|
||||
<TextItem>
|
||||
<TextField
|
||||
label="max"
|
||||
label='max'
|
||||
styles={TEXT_STYLES}
|
||||
value={`${fhi}`}
|
||||
onChange={handleMaxChange}
|
||||
|
|
|
@ -109,7 +109,7 @@ export const Sparkbar: React.FC<SparkbarProps> = memo(function Sparkbar({
|
|||
|
||||
const theme = useThematic()
|
||||
const ref = useRef(null)
|
||||
const handleClick = useCallback(d => onClick && onClick(d), [onClick])
|
||||
const handleClick = useCallback((d) => onClick?.(d), [onClick])
|
||||
const nodataFn = useCallback(
|
||||
(d: unknown) => {
|
||||
if (nodata) {
|
||||
|
@ -120,12 +120,12 @@ export const Sparkbar: React.FC<SparkbarProps> = memo(function Sparkbar({
|
|||
[nodata],
|
||||
)
|
||||
const [hovered, setHovered] = useState<any>(null)
|
||||
const handleHover = useCallback(d => setHovered(d), [])
|
||||
const handleHover = useCallback((d) => setHovered(d), [])
|
||||
const [barGroup, setBarGroup] = useState<any>()
|
||||
const [brushSelection, setBrushSelection] = useState<any>()
|
||||
|
||||
const handleBrushEnd = useCallback(
|
||||
event => {
|
||||
(event) => {
|
||||
if (onBrushEnd) {
|
||||
if (event?.sourceEvent) {
|
||||
const { selection } = event
|
||||
|
|
|
@ -6,10 +6,7 @@ import type { DataFile } from '@graph-drilldown/types'
|
|||
import { useCallback, useState } from 'react'
|
||||
|
||||
export function useRowHandling(onClick) {
|
||||
const onRowClick = useCallback(
|
||||
(file: DataFile) => onClick && onClick(file),
|
||||
[onClick],
|
||||
)
|
||||
const onRowClick = useCallback((file: DataFile) => onClick?.(file), [onClick])
|
||||
const [hovered, setHovered] = useState<DataFile | undefined>()
|
||||
const onRowHover = useCallback((file?) => setHovered(file), [setHovered])
|
||||
return {
|
||||
|
|
|
@ -39,7 +39,7 @@ export const FileTable: React.FC<FileTableProps> = memo(function FileTable({
|
|||
</thead>
|
||||
<tbody>
|
||||
{files.length > 0 ? (
|
||||
files.map(file => {
|
||||
files.map((file) => {
|
||||
const background =
|
||||
selected === file
|
||||
? theme.application().accent().hex()
|
||||
|
|
|
@ -78,6 +78,7 @@ export const Dashes: React.FC<DashesProps> = ({
|
|||
}
|
||||
return (
|
||||
<svg width={width} height={height}>
|
||||
<title>Header Legend Dashes</title>
|
||||
<g>{dashes}</g>
|
||||
</svg>
|
||||
)
|
||||
|
|
|
@ -71,6 +71,7 @@ export const Dots: React.FC<DotsProps> = ({
|
|||
}
|
||||
return (
|
||||
<svg width={width} height={height}>
|
||||
<title>Header Legend</title>
|
||||
<g>{dots}</g>
|
||||
</svg>
|
||||
)
|
||||
|
|
|
@ -46,7 +46,7 @@ export function useLegendNumericScale(
|
|||
function useSizeScale(encoding, count) {
|
||||
const scale = useNumericEncodingScale(encoding)
|
||||
return useCallback(
|
||||
index => {
|
||||
(index) => {
|
||||
const [min, max] = encoding.domain
|
||||
const steps = (max - min) / count
|
||||
const v = steps * index + min
|
||||
|
|
|
@ -22,7 +22,7 @@ export const NumericRangeText: React.FC<NumericRangeTextProps> = ({
|
|||
const mid = (max - min) / 2
|
||||
const fmt = format(`.${precision}f`)
|
||||
const values = mid === 0 ? [max] : [min, mid, max]
|
||||
return values.map(v => fmt(v))
|
||||
return values.map((v) => fmt(v))
|
||||
}, [domain, precision])
|
||||
|
||||
return (
|
||||
|
|
|
@ -30,7 +30,7 @@ export const VerticalNominalChips: React.FC<NominalChipsProps> = ({
|
|||
const items = useMemo(
|
||||
() =>
|
||||
[...uniques]
|
||||
.map(v => `${v}`)
|
||||
.map((v) => `${v}`)
|
||||
.sort((a: any, b: any) => a.localeCompare(b))
|
||||
.slice(0, maxItems),
|
||||
[uniques, maxItems],
|
||||
|
@ -79,6 +79,7 @@ export const VerticalNominalChips: React.FC<NominalChipsProps> = ({
|
|||
}, [theme, scale, height, items, uniques])
|
||||
return (
|
||||
<svg width={width} height={totalHeight}>
|
||||
<title>Nominal Legend</title>
|
||||
{rows}
|
||||
</svg>
|
||||
)
|
||||
|
|
|
@ -34,18 +34,16 @@ export const NumericDomainEditor: React.FC<NumericDomainEditorProps> = ({
|
|||
const fmt = useMemo(() => format(`.${precision}f`), [precision])
|
||||
|
||||
const handleTypeChange = useCallback(
|
||||
scaleType =>
|
||||
onChange &&
|
||||
onChange({
|
||||
(scaleType) =>
|
||||
onChange?.({
|
||||
scaleType,
|
||||
}),
|
||||
[onChange],
|
||||
)
|
||||
|
||||
const handleDomainChange = useCallback(
|
||||
domain =>
|
||||
onChange &&
|
||||
onChange({
|
||||
(domain) =>
|
||||
onChange?.({
|
||||
domain,
|
||||
}),
|
||||
[onChange],
|
||||
|
|
|
@ -26,11 +26,10 @@ export const NumericRangeEditor: React.FC<NumericRangeEditorProps> = ({
|
|||
precision = 1,
|
||||
}) => {
|
||||
const handleRangeChange = useCallback(
|
||||
d => {
|
||||
onChange &&
|
||||
onChange({
|
||||
range: d,
|
||||
})
|
||||
(d) => {
|
||||
onChange?.({
|
||||
range: d,
|
||||
})
|
||||
},
|
||||
[onChange],
|
||||
)
|
||||
|
|
|
@ -20,7 +20,7 @@ export const FixedPicker: React.FC<NumericSelectorProps> = ({
|
|||
const [rangeValue, setRangeValue] = useState(encoding.value)
|
||||
|
||||
const useDebounce = useDebounceFn(
|
||||
value => {
|
||||
(value) => {
|
||||
onChange({ value })
|
||||
},
|
||||
{
|
||||
|
|
|
@ -18,10 +18,10 @@ import { ScaledPicker } from './ScaledPicker.js'
|
|||
* - Palette: a thematic-bound named color (so it auto-updates with theme changes)
|
||||
* - Scale: field-bound to a thematic scale with domain, range, etc.
|
||||
*/
|
||||
export const NumericSelector: React.FC<NumericSelectorProps> = props => {
|
||||
export const NumericSelector: React.FC<NumericSelectorProps> = (props) => {
|
||||
const { encoding, onChange } = props
|
||||
const handlePivotLinkClick = useCallback(
|
||||
item => {
|
||||
(item) => {
|
||||
onChange({
|
||||
binding: item.props.itemKey,
|
||||
})
|
||||
|
|
|
@ -70,7 +70,7 @@ function useFieldDropdownOptions(table: ColumnTable) {
|
|||
return useMemo(() => {
|
||||
const types = columnTypes(table)
|
||||
return types
|
||||
.filter(t => t.type === 'number')
|
||||
.filter((t) => t.type === 'number')
|
||||
.map(({ name }) => ({
|
||||
key: name,
|
||||
text: name,
|
||||
|
|
|
@ -21,10 +21,7 @@ export const ToggleHeader: React.FC<ToggleHeaderProps> = ({
|
|||
disabled,
|
||||
onChange,
|
||||
}) => {
|
||||
const handleChange = useCallback(
|
||||
(_, v) => onChange && onChange(v),
|
||||
[onChange],
|
||||
)
|
||||
const handleChange = useCallback((_, v) => onChange?.(v), [onChange])
|
||||
return (
|
||||
<Container>
|
||||
<Title>{title}</Title>
|
||||
|
@ -33,8 +30,8 @@ export const ToggleHeader: React.FC<ToggleHeaderProps> = ({
|
|||
<Toggle
|
||||
disabled={disabled}
|
||||
styles={toggleStyles}
|
||||
onText="on"
|
||||
offText="off"
|
||||
onText='on'
|
||||
offText='off'
|
||||
checked={checked}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
|
|
@ -17,26 +17,27 @@
|
|||
"scripts": {
|
||||
"clean": "essex clean",
|
||||
"build": "essex build --skipExportCheck --docs",
|
||||
"lint": "essex lint --fix"
|
||||
"fix": "essex fix",
|
||||
"check": "essex check"
|
||||
},
|
||||
"dependencies": {
|
||||
"@essex/hooks": "^4.0.12",
|
||||
"@essex/hooks": "^4.0.21",
|
||||
"@graph-drilldown/types": "workspace:packages/types",
|
||||
"d3-scale": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@essex/scripts": "^22.0.4",
|
||||
"@essex/tsconfig-base": "^1.0.2",
|
||||
"@fluentui/react": "^8.70.0",
|
||||
"@essex/scripts": "^26.0.0",
|
||||
"@essex/tsconfig-base": "^3.0.0",
|
||||
"@fluentui/react": "^8.111.1",
|
||||
"@thematic/color": "^2.0.8",
|
||||
"@thematic/core": "^2.1.1",
|
||||
"@thematic/fluent": "^3.0.9",
|
||||
"@thematic/react": "^2.0.9",
|
||||
"@types/d3-scale": "^4.0.2",
|
||||
"@types/node": "^17.0.35",
|
||||
"@types/react": "^18.0.9",
|
||||
"react": "^18.1.0",
|
||||
"styled-components": "^5.3.5"
|
||||
"@types/node": "^20.5.7",
|
||||
"@types/react": "^18.2.21",
|
||||
"react": "^18.2.0",
|
||||
"styled-components": "^6.0.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@fluentui/react": ">= 8",
|
||||
|
@ -47,7 +48,7 @@
|
|||
"@types/node": "*",
|
||||
"@types/react": ">= 17",
|
||||
"react": ">= 17",
|
||||
"styled-components": ">= 5"
|
||||
"styled-components": ">= 6"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
|
|
|
@ -17,17 +17,18 @@
|
|||
"scripts": {
|
||||
"clean": "essex clean",
|
||||
"build": "essex build --skipExportCheck --docs",
|
||||
"lint": "essex lint --fix"
|
||||
"fix": "essex fix",
|
||||
"check": "essex check"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@essex/scripts": "^22.0.4",
|
||||
"@essex/tsconfig-base": "^1.0.2",
|
||||
"@essex/scripts": "^26.0.0",
|
||||
"@essex/tsconfig-base": "^3.0.0",
|
||||
"@thematic/core": "^2.1.1",
|
||||
"@types/node": "^17.0.35",
|
||||
"@types/react": "^18.0.9",
|
||||
"@types/node": "^20.5.7",
|
||||
"@types/react": "^18.2.21",
|
||||
"arquero": "^4.8.8",
|
||||
"react": "^18.1.0",
|
||||
"typescript": "^4.7.2"
|
||||
"react": "^18.2.0",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@thematic/core": ">= 2",
|
||||
|
|
|
@ -9,18 +9,19 @@
|
|||
"start:client": "yarn start",
|
||||
"start": "essex serve",
|
||||
"bundle": "essex bundle && shx cp -r public/* build/",
|
||||
"lint": "essex lint --fix"
|
||||
"fix": "essex fix",
|
||||
"check": "essex check"
|
||||
},
|
||||
"dependencies": {
|
||||
"@essex/arquero-react": "^1.0.6",
|
||||
"@essex/arquero-react": "^1.2.1",
|
||||
"@essex/hierarchy-browser": "4.0.11",
|
||||
"@essex/hooks": "^4.0.12",
|
||||
"@essex/hooks": "^4.0.21",
|
||||
"@essex/themed-components": "^2.2.7",
|
||||
"@essex/toolbox": "3.0.8",
|
||||
"@fluentui/font-icons-mdl2": "^8.4.0",
|
||||
"@fluentui/react": "^8.70.0",
|
||||
"@fluentui/react-hooks": "^8.5.5",
|
||||
"@fluentui/utilities": "^8.8.3",
|
||||
"@fluentui/font-icons-mdl2": "^8.5.24",
|
||||
"@fluentui/react": "^8.111.1",
|
||||
"@fluentui/react-hooks": "^8.6.29",
|
||||
"@fluentui/utilities": "^8.13.18",
|
||||
"@graph-drilldown/arquero": "workspace:packages/arquero",
|
||||
"@graph-drilldown/components": "workspace:packages/components",
|
||||
"@graph-drilldown/hooks": "workspace:packages/hooks",
|
||||
|
@ -36,53 +37,52 @@
|
|||
"@thematic/d3": "^2.0.9",
|
||||
"@thematic/fluent": "^3.0.9",
|
||||
"@thematic/react": "^2.0.9",
|
||||
"ahooks": "^3.4.0",
|
||||
"ahooks": "^3.7.8",
|
||||
"arquero": "^4.8.8",
|
||||
"core-js": "^3.22.7",
|
||||
"core-js": "^3.32.1",
|
||||
"d3-dsv": "^3.0.1",
|
||||
"d3-format": "^3.1.0",
|
||||
"d3-scale": "^4.0.2",
|
||||
"office-ui-fabric-react": "^7.185.7",
|
||||
"office-ui-fabric-react": "^7.204.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"query-string": "^7.1.1",
|
||||
"react": "^18.1.0",
|
||||
"query-string": "^7.1.3",
|
||||
"react": "^18.2.0",
|
||||
"react-animate-height": "^2.1.2",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dropzone": "^11.7.1",
|
||||
"react-if": "^4.1.4",
|
||||
"react-if": "^4.1.5",
|
||||
"react-infinite-scroller": "^1.2.6",
|
||||
"react-rnd": "^10.3.7",
|
||||
"react-router-dom": "^5.3.1",
|
||||
"recoil": "^0.7.3-alpha.2",
|
||||
"regenerator-runtime": "^0.13.9",
|
||||
"styled-components": "^5.3.5",
|
||||
"tslib": "^2.4.0"
|
||||
"react-router-dom": "^5.3.4",
|
||||
"recoil": "^0.7.7",
|
||||
"regenerator-runtime": "^0.14.0",
|
||||
"styled-components": "^6.0.7",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@essex/scripts": "^22.0.4",
|
||||
"@essex/webpack-config": "^21.0.4",
|
||||
"@essex/scripts": "^26.0.0",
|
||||
"@essex/webpack-config": "^22.0.1",
|
||||
"@types/d3-dsv": "^3.0.0",
|
||||
"@types/d3-format": "^3.0.1",
|
||||
"@types/d3-scale": "^4.0.2",
|
||||
"@types/dotenv-webpack": "^7.0.3",
|
||||
"@types/node": "^17.0.35",
|
||||
"@types/node": "^20.5.7",
|
||||
"@types/prettier": "^2.6.1",
|
||||
"@types/prop-types": "^15.7.5",
|
||||
"@types/query-string": "^6.3.0",
|
||||
"@types/react": "^18.0.9",
|
||||
"@types/react-dom": "^18.0.5",
|
||||
"@types/react": "^18.2.21",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@types/react-infinite-scroller": "^1.2.3",
|
||||
"@types/react-rnd": "^8.0.0",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/styled-components": "^5.1.25",
|
||||
"@typescript-eslint/eslint-plugin": "^5.26.0",
|
||||
"@typescript-eslint/parser": "^5.26.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.5.0",
|
||||
"@typescript-eslint/parser": "^6.5.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"ncp": "^2.0.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
"shx": "^0.3.4",
|
||||
"typescript": "^4.7.2",
|
||||
"typescript": "^5.2.2",
|
||||
"url-loader": "^4.1.1"
|
||||
},
|
||||
"browserslist": [
|
||||
|
|
|
@ -8,11 +8,11 @@ import { LazyCachingSwitch } from '~/components/LazyCachingSwitch'
|
|||
import { DataManagerPage, GraphViewerPage } from '~/pages'
|
||||
import { HashRouter, Route } from '~/react-patch/react-router-dom'
|
||||
|
||||
import { CommandBar } from './components/CommandBar'
|
||||
import { Footer } from './components/Footer'
|
||||
import { DataContext } from './DataContext'
|
||||
import { StateContext } from './StateContext'
|
||||
import { StyleContext } from './StyleContext'
|
||||
import { CommandBar } from './components/CommandBar'
|
||||
import { Footer } from './components/Footer'
|
||||
|
||||
export const App: React.FC = () => {
|
||||
return (
|
||||
|
@ -24,8 +24,8 @@ export const App: React.FC = () => {
|
|||
<CommandBar />
|
||||
<Main>
|
||||
<LazyCachingSwitch>
|
||||
<Route path="/" component={GraphViewerPage} />
|
||||
<Route path="/files" component={DataManagerPage} />
|
||||
<Route path='/' component={GraphViewerPage} />
|
||||
<Route path='/files' component={DataManagerPage} />
|
||||
</LazyCachingSwitch>
|
||||
<Footer />
|
||||
</Main>
|
||||
|
|
|
@ -68,7 +68,7 @@ export const ModalContainer: React.FC<ModalContainerProps> = memo(
|
|||
<IconButton
|
||||
styles={iconButtonStyles}
|
||||
iconProps={cancelIcon}
|
||||
ariaLabel="Close popup modal"
|
||||
ariaLabel='Close popup modal'
|
||||
onClick={onDismiss}
|
||||
/>
|
||||
</Header>
|
||||
|
|
|
@ -20,7 +20,7 @@ export const UploadModal: React.FC = () => {
|
|||
</Files>
|
||||
<Reset>
|
||||
{hasData ? (
|
||||
<DefaultButton text="Clear all" onClick={doClearAll} />
|
||||
<DefaultButton text='Clear all' onClick={doClearAll} />
|
||||
) : null}
|
||||
</Reset>
|
||||
</Container>
|
||||
|
|
|
@ -22,22 +22,20 @@ export const ImageSettings: React.FC<ImageSettingsProps> = ({
|
|||
}) => {
|
||||
const handleFilenameChange = useCallback(
|
||||
(e, value) => {
|
||||
onChange &&
|
||||
onChange({
|
||||
...settings,
|
||||
filename: value,
|
||||
})
|
||||
onChange?.({
|
||||
...settings,
|
||||
filename: value,
|
||||
})
|
||||
},
|
||||
[onChange, settings],
|
||||
)
|
||||
|
||||
const handleSizeChange = useCallback(
|
||||
value => {
|
||||
onChange &&
|
||||
onChange({
|
||||
...settings,
|
||||
size: value,
|
||||
})
|
||||
(value) => {
|
||||
onChange?.({
|
||||
...settings,
|
||||
size: value,
|
||||
})
|
||||
},
|
||||
[onChange, settings],
|
||||
)
|
||||
|
@ -61,8 +59,8 @@ export const ImageSettings: React.FC<ImageSettingsProps> = ({
|
|||
label={'Size (px)'}
|
||||
value={settings.size}
|
||||
onChange={handleSizeChange}
|
||||
incrementButtonAriaLabel="increment size (px)"
|
||||
decrementButtonAriaLabel="decrement size (px)"
|
||||
incrementButtonAriaLabel='increment size (px)'
|
||||
decrementButtonAriaLabel='decrement size (px)'
|
||||
/>
|
||||
</Size>
|
||||
</Container>
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
*/
|
||||
import type { GraphRenderer } from '@graspologic/renderer'
|
||||
import { ThematicProvider, useThematic } from '@thematic/react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { useRecoilBridgeAcrossReactRoots_UNSTABLE } from 'recoil'
|
||||
|
||||
import { GraphViewer } from '~/components/GraphViewer'
|
||||
|
@ -29,10 +29,12 @@ export function useCreateRenderer() {
|
|||
|
||||
const RecoilBridge = useRecoilBridgeAcrossReactRoots_UNSTABLE()
|
||||
|
||||
const domNode = useMemo(() => document.createElement('div'), [])
|
||||
const root = useMemo(() => createRoot(domNode), [domNode])
|
||||
|
||||
useEffect(() => {
|
||||
const domNode = document.createElement('div')
|
||||
if (create) {
|
||||
ReactDOM.render(
|
||||
root.render(
|
||||
<RecoilBridge>
|
||||
<ThematicProvider theme={theme}>
|
||||
<GraphViewer
|
||||
|
@ -42,15 +44,23 @@ export function useCreateRenderer() {
|
|||
/>
|
||||
</ThematicProvider>
|
||||
</RecoilBridge>,
|
||||
domNode,
|
||||
)
|
||||
}
|
||||
return () => {
|
||||
// trash the node on unmount to prevent memory leaks
|
||||
ReactDOM.unmountComponentAtNode(domNode)
|
||||
root.unmount()
|
||||
domNode.parentNode?.removeChild(domNode)
|
||||
}
|
||||
}, [create, theme, data, cameraBounds, setRenderer, RecoilBridge])
|
||||
}, [
|
||||
create,
|
||||
theme,
|
||||
data,
|
||||
cameraBounds,
|
||||
setRenderer,
|
||||
RecoilBridge,
|
||||
domNode,
|
||||
root,
|
||||
])
|
||||
|
||||
return {
|
||||
renderer,
|
||||
|
|
|
@ -16,7 +16,7 @@ export function useImageSettings() {
|
|||
DEFAULT_IMAGE_SETTINGS,
|
||||
)
|
||||
|
||||
const onSettingsChange = useCallback(s => setImageSettings(s), [])
|
||||
const onSettingsChange = useCallback((s) => setImageSettings(s), [])
|
||||
|
||||
return {
|
||||
settings,
|
||||
|
|
|
@ -55,8 +55,8 @@ const Link: FC<{
|
|||
</LinkDiv>
|
||||
) : (
|
||||
<LinkA
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
href={href}
|
||||
style={style}
|
||||
className={className}
|
||||
|
|
|
@ -27,8 +27,8 @@ const fns = {
|
|||
|
||||
export async function fetchDSVTable(url: string): Promise<ColumnTable> {
|
||||
return fetch(url)
|
||||
.then(res => res.text())
|
||||
.then(content => parseDSVTable(url, content))
|
||||
.then((res) => res.text())
|
||||
.then((content) => parseDSVTable(url, content))
|
||||
}
|
||||
|
||||
export function parseDSVTable(filename: string, content: string): ColumnTable {
|
||||
|
|
|
@ -11,7 +11,7 @@ import { AUTOLAYOUT_URL } from '~/constants'
|
|||
* @param edges
|
||||
*/
|
||||
function edgesToPOST(edges: EdgeCollection) {
|
||||
return edges.map(e => ({
|
||||
return edges.map((e) => ({
|
||||
source: e.source,
|
||||
target: e.target,
|
||||
weight: e.weight,
|
||||
|
@ -43,9 +43,9 @@ export async function umapLayout(edges: EdgeCollection) {
|
|||
mode: 'cors',
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(json =>
|
||||
json.positions.map(p => ({
|
||||
.then((res) => res.json())
|
||||
.then((json) =>
|
||||
json.positions.map((p) => ({
|
||||
id: p.node_id,
|
||||
x: p.x,
|
||||
y: p.y,
|
||||
|
|
|
@ -19,6 +19,6 @@ export async function fetchUrl(url: string): Promise<ColumnTable> {
|
|||
case 'tsv':
|
||||
return fetchDSVTable(url)
|
||||
default:
|
||||
throw new Error(`Unsupported file type: ` + ext)
|
||||
throw new Error(`Unsupported file type: ${ext}`)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
* Copyright (c) Microsoft. All rights reserved.
|
||||
* Licensed under the MIT license. See LICENSE file in the project.
|
||||
*/
|
||||
export const extension = path => path.match(/\.([a-z]+)/)[1]
|
||||
export const extension = (path) => path.match(/\.([a-z]+)/)[1]
|
||||
|
|
|
@ -17,8 +17,9 @@ export function useDrop(
|
|||
reader.onabort = () => console.log('file reading was aborted')
|
||||
reader.onerror = () => console.log('file reading has failed')
|
||||
reader.onload = () => {
|
||||
/* eslint-disable-next-line @typescript-eslint/no-base-to-string */
|
||||
const text = reader.result ? reader.result.toString() : ''
|
||||
onFileLoad && onFileLoad(text, type, name)
|
||||
onFileLoad?.(text, type, name)
|
||||
}
|
||||
reader.readAsBinaryString(file)
|
||||
})
|
||||
|
|
|
@ -8,8 +8,8 @@ import {
|
|||
Camera,
|
||||
Edges,
|
||||
HighlightHoveredNode,
|
||||
Nodes,
|
||||
NodeSetHighlight,
|
||||
Nodes,
|
||||
} from '@graspologic/react'
|
||||
import type { GraphRenderer } from '@graspologic/renderer'
|
||||
import { useCallback } from 'react'
|
||||
|
@ -94,7 +94,7 @@ export const GraphViewer = ({
|
|||
)
|
||||
|
||||
const handleInitialize = useCallback(
|
||||
renderer => onRendererInitialized && onRendererInitialized(renderer),
|
||||
(renderer) => onRendererInitialized?.(renderer),
|
||||
[onRendererInitialized],
|
||||
)
|
||||
|
||||
|
@ -136,12 +136,12 @@ export const GraphViewer = ({
|
|||
maxRadius={nodeRange[1]}
|
||||
/>
|
||||
<NodeSetHighlight
|
||||
key={`hovered`}
|
||||
key={'hovered'}
|
||||
vertexIds={hoveredNodeIds}
|
||||
color={hoverColor}
|
||||
/>
|
||||
<NodeSetHighlight
|
||||
key={`selected`}
|
||||
key={'selected'}
|
||||
vertexIds={selectedNode ? [selectedNode.id] : []}
|
||||
color={hoverColor}
|
||||
/>
|
||||
|
|
|
@ -27,7 +27,7 @@ export const LazyCachingSwitch: any = withRouter(({ location, children }) => {
|
|||
let isDirty = false
|
||||
// first route match wins, mimicking behavior from Switch
|
||||
let foundFirst = false
|
||||
Children.forEach(children, child => {
|
||||
Children.forEach(children, (child) => {
|
||||
if (!foundFirst && isValidElement(child)) {
|
||||
const props = child.props as any
|
||||
const { path } = props
|
||||
|
@ -50,7 +50,7 @@ export const LazyCachingSwitch: any = withRouter(({ location, children }) => {
|
|||
|
||||
// second iteration creates a set of rendered components from the cache, hidden if not matched
|
||||
const rendered = useMemo(() => {
|
||||
return Children.map(children, child => {
|
||||
return Children.map(children, (child) => {
|
||||
if (isValidElement(child)) {
|
||||
const props = child.props as any
|
||||
const { path } = props
|
||||
|
|
|
@ -91,7 +91,7 @@ export const QuickDrop: React.FC<QuickDropProps> = ({
|
|||
) : null}
|
||||
<Reset>
|
||||
{hasData && compact ? (
|
||||
<DefaultButton text="Clear all" onClick={doClearAll} />
|
||||
<DefaultButton text='Clear all' onClick={doClearAll} />
|
||||
) : null}
|
||||
</Reset>
|
||||
</Container>
|
||||
|
|
|
@ -14,7 +14,7 @@ export interface RouterLinkIconButtonProps
|
|||
to: any
|
||||
}
|
||||
|
||||
const LinkButton: React.FC<RouterLinkIconButtonProps> = props => {
|
||||
const LinkButton: React.FC<RouterLinkIconButtonProps> = (props) => {
|
||||
const { history, location, match, staticContext, to, ...buttonProps } = props
|
||||
const handleClick = useCallback(() => {
|
||||
history.push(to)
|
||||
|
|
|
@ -44,7 +44,7 @@ export function useSortHandling(communities: CommunityCollection) {
|
|||
}, [communities, sort])
|
||||
|
||||
const onSortClick = useCallback(
|
||||
column => {
|
||||
(column) => {
|
||||
if (sort.field === column.field) {
|
||||
setSort({
|
||||
...sort,
|
||||
|
|
|
@ -57,8 +57,10 @@ export function useFileManagement(): {
|
|||
|
||||
const doAddFile = useCallback(
|
||||
(dataFile: DataFile) => {
|
||||
addFile(dataFile)
|
||||
addTable(dataFile.table!, dataFile.tableType!)
|
||||
if (dataFile.table != null && dataFile.tableType != null) {
|
||||
addFile(dataFile)
|
||||
addTable(dataFile.table, dataFile.tableType)
|
||||
}
|
||||
},
|
||||
[addTable, addFile],
|
||||
)
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
} from '~/state'
|
||||
import { useVisibleNodeMap } from '~/state/caches'
|
||||
import { useNodeOpacityEncoding } from '~/state/vis/nodeOpacity'
|
||||
import { ViewType } from '~/types'
|
||||
import type { ViewType } from '~/types'
|
||||
|
||||
import { useColorizer, useRange, useWeighter } from './graspologic'
|
||||
|
||||
|
@ -31,13 +31,13 @@ export function useNodeCount() {
|
|||
}
|
||||
|
||||
export function useNodeIds(nodes?: NodeCollection) {
|
||||
return useMemo(() => (nodes ? nodes.map(node => node.id) : []), [nodes])
|
||||
return useMemo(() => (nodes ? nodes.map((node) => node.id) : []), [nodes])
|
||||
}
|
||||
|
||||
export function useNodeColumns(nodes?: NodeCollection) {
|
||||
const ids = useMemo(() => {
|
||||
if (nodes) {
|
||||
return nodes.map(node => {
|
||||
return nodes.map((node) => {
|
||||
const attrs = { x: node.x, y: node.y, d: node.d }
|
||||
return { id: node.id, attrs }
|
||||
})
|
||||
|
@ -78,14 +78,12 @@ export function useNodePositions(
|
|||
duration?: number,
|
||||
): NodePositioner {
|
||||
return useMemo(() => {
|
||||
const positions =
|
||||
view === ViewType.SingleGraph ? positionMaps[0] : positionMaps[1]
|
||||
return {
|
||||
duration,
|
||||
x: id => positions[id!]?.x || 0,
|
||||
y: id => positions[id!]?.y || 0,
|
||||
x: (id) => id?.[id]?.x || 0,
|
||||
y: (id) => id?.[id]?.y || 0,
|
||||
}
|
||||
}, [positionMaps, view, duration])
|
||||
}, [duration])
|
||||
}
|
||||
|
||||
// for a list of communities, get a map of [cid]: nodepositions[]
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
* Licensed under the MIT license. See LICENSE file in the project.
|
||||
*/
|
||||
import {
|
||||
findNodesCollectionForCommunity,
|
||||
NodeCollection,
|
||||
findNodesCollectionForCommunity,
|
||||
} from '@graph-drilldown/arquero'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
|
||||
|
|
|
@ -11,7 +11,10 @@ function mount(): void {
|
|||
try {
|
||||
const container = document.getElementById('root')
|
||||
initializeIcons(undefined, { disableWarnings: true })
|
||||
const root = createRoot(container!)
|
||||
if (container == null) {
|
||||
throw new Error('could not find root container')
|
||||
}
|
||||
const root = createRoot(container)
|
||||
root.render(<App />)
|
||||
} catch (err) {
|
||||
console.error('error rendering application', err)
|
||||
|
|
|
@ -13,7 +13,7 @@ import type ColumnTable from 'arquero/dist/types/table/column-table'
|
|||
* @param nodes
|
||||
*/
|
||||
export async function layoutGrid(nodes: ColumnTable): Promise<ColumnTable> {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
const ranked = nodes
|
||||
.params({
|
||||
rows: nodes.numRows(),
|
||||
|
|
|
@ -8,7 +8,7 @@ import type ColumnTable from 'arquero/dist/types/table/column-table'
|
|||
* @param table
|
||||
*/
|
||||
export async function layoutRandom(nodes: ColumnTable): Promise<ColumnTable> {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
resolve(
|
||||
nodes.derive({
|
||||
'node.x': () => Math.random(),
|
||||
|
|
|
@ -14,7 +14,7 @@ import { umapLayout } from '../api'
|
|||
*/
|
||||
export async function layoutUmap(edges: ColumnTable) {
|
||||
const positions = await umapLayout(new EdgeCollection(edges))
|
||||
const transformed = positions.map(n => ({
|
||||
const transformed = positions.map((n) => ({
|
||||
'node.id': n.id,
|
||||
'node.x': n.x,
|
||||
'node.y': n.y,
|
||||
|
|
|
@ -23,12 +23,12 @@ export const MainPanel: React.FC = () => {
|
|||
/>
|
||||
<Reset>
|
||||
{hasData ? (
|
||||
<DefaultButton text="Clear all" onClick={doClearAll} />
|
||||
<DefaultButton text='Clear all' onClick={doClearAll} />
|
||||
) : null}
|
||||
</Reset>
|
||||
</Files>
|
||||
|
||||
{selectedFile && selectedFile.table ? (
|
||||
{selectedFile?.table ? (
|
||||
<Viewer>
|
||||
<ArqueroDetailsList
|
||||
table={selectedFile.table}
|
||||
|
|
|
@ -2,21 +2,21 @@
|
|||
* Copyright (c) Microsoft. All rights reserved.
|
||||
* Licensed under the MIT license. See LICENSE file in the project.
|
||||
*/
|
||||
import { memo, Suspense, useMemo, useRef, useState } from 'react'
|
||||
import { Suspense, memo, useMemo, useRef, useState } from 'react'
|
||||
import type { RndResizeStartCallback } from 'react-rnd'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import { GraphPanel } from './components/GraphPanel'
|
||||
import { LeftSidePanel } from './components/LeftSidePanel'
|
||||
import { ResizableBrowser } from './components/ResizableBrowser'
|
||||
import { RightSidePanel } from './components/RightSidePanel'
|
||||
import { StyledSpinnner } from './components/StyledSpinner'
|
||||
import {
|
||||
CSSFilter,
|
||||
useGraphDimensions,
|
||||
useLayoutStyle,
|
||||
useResizeHandlers,
|
||||
} from './GraphViewerPage.hooks'
|
||||
import { GraphPanel } from './components/GraphPanel'
|
||||
import { LeftSidePanel } from './components/LeftSidePanel'
|
||||
import { ResizableBrowser } from './components/ResizableBrowser'
|
||||
import { RightSidePanel } from './components/RightSidePanel'
|
||||
import { StyledSpinnner } from './components/StyledSpinner'
|
||||
|
||||
export const GraphViewerPage: React.FC = memo(function GraphViewerPage() {
|
||||
const ref = useRef(null)
|
||||
|
|
|
@ -17,7 +17,7 @@ export const ColumnEditor = () => {
|
|||
<tr>
|
||||
<th>column</th>
|
||||
<th>data type</th>
|
||||
<th></th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -38,7 +38,7 @@ export const ColumnEditorRow: React.FC<ColumnEditorRowProps> = ({
|
|||
[column, onDeleteRequested, deletionDisabled],
|
||||
)
|
||||
const handleVisibleClick = useCallback(
|
||||
() => onVisibilityChange && onVisibilityChange(column, browserVisible),
|
||||
() => onVisibilityChange?.(column, browserVisible),
|
||||
[onVisibilityChange, browserVisible, column],
|
||||
)
|
||||
const iconName = useMemo(
|
||||
|
@ -62,7 +62,7 @@ export const ColumnEditorRow: React.FC<ColumnEditorRowProps> = ({
|
|||
tabIndex={tabIndex}
|
||||
>
|
||||
<IconButton
|
||||
title="delete"
|
||||
title='delete'
|
||||
iconProps={{ iconName: 'delete' }}
|
||||
disabled={column.readOnly || column.name === encoding.field}
|
||||
/>
|
||||
|
@ -75,7 +75,7 @@ export const ColumnEditorRow: React.FC<ColumnEditorRowProps> = ({
|
|||
onKeyDown={handleVisibleClick}
|
||||
tabIndex={index}
|
||||
>
|
||||
<IconButton title="toggle view" iconProps={{ iconName }} />
|
||||
<IconButton title='toggle view' iconProps={{ iconName }} />
|
||||
</Cell>
|
||||
</td>
|
||||
) : null}
|
||||
|
|
|
@ -23,7 +23,7 @@ export function useColumnConfig(
|
|||
return useMemo(() => {
|
||||
const w = width / columns.length - 20 // padding
|
||||
const mw = w < MIN_COLUMN_WIDTH ? MIN_COLUMN_WIDTH : w
|
||||
return columns.map(name => ({
|
||||
return columns.map((name) => ({
|
||||
key: name,
|
||||
name,
|
||||
fieldName: name,
|
||||
|
@ -56,7 +56,7 @@ function sortColumns(columns: string[]) {
|
|||
'community.nodeCount': true,
|
||||
'community.childCount': true,
|
||||
}
|
||||
const alpha = [...columns.sort()].filter(s => !defs[s])
|
||||
const alpha = [...columns.sort()].filter((s) => !defs[s])
|
||||
return [...Object.keys(defs), ...alpha]
|
||||
}
|
||||
|
||||
|
@ -74,10 +74,10 @@ function useColumnArray(
|
|||
const split = col.split('.')
|
||||
const [prefix, value] = split
|
||||
const hidden = hiddenFields
|
||||
? hiddenFields.find(name => name === value)
|
||||
? hiddenFields.find((name) => name === value)
|
||||
: false
|
||||
const colType = colAttribute
|
||||
? colAttribute.find(name => name === prefix)
|
||||
? colAttribute.find((name) => name === prefix)
|
||||
: true
|
||||
if (colType && !hidden) {
|
||||
acc.push(col)
|
||||
|
|
|
@ -27,6 +27,7 @@ export const Bar: React.FC<CellComponentProps> = ({
|
|||
const size = sizeScale(value) || 0
|
||||
return (
|
||||
<svg width={width} height={height}>
|
||||
<title>Community Bar</title>
|
||||
<rect
|
||||
width={size}
|
||||
height={height}
|
||||
|
|
|
@ -38,7 +38,7 @@ export const Cell: React.FC<CellProps> = ({
|
|||
|
||||
const style = useMemo(() => {
|
||||
const markType = Mark[mark].toLocaleLowerCase()
|
||||
if (styles && styles[markType]) {
|
||||
if (styles?.[markType]) {
|
||||
return styles[markType]
|
||||
}
|
||||
return {
|
||||
|
|
|
@ -19,6 +19,7 @@ export const Circle: React.FC<CellComponentProps> = ({
|
|||
<>
|
||||
{value}
|
||||
<svg width={size * 2} height={size * 2}>
|
||||
<title>Community Cell</title>
|
||||
<circle
|
||||
cx={size}
|
||||
cy={size}
|
||||
|
|
|
@ -33,7 +33,7 @@ export const CommunityList = ({ communities, style }: CommunityListProps) => {
|
|||
<Table>
|
||||
<thead>
|
||||
<tr>
|
||||
{columns.map(c => (
|
||||
{columns.map((c) => (
|
||||
<Th
|
||||
key={`comm-th-${c.header}`}
|
||||
onClick={() => onSortClick(c)}
|
||||
|
@ -45,7 +45,7 @@ export const CommunityList = ({ communities, style }: CommunityListProps) => {
|
|||
</thead>
|
||||
<tbody>
|
||||
{sorted.map(
|
||||
comm => (
|
||||
(comm) => (
|
||||
<CommunityRow
|
||||
key={`community-row-${comm.id}`}
|
||||
community={comm}
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
import type { Color } from '@thematic/color'
|
||||
|
||||
export enum Mark {
|
||||
None,
|
||||
Circle,
|
||||
Rect,
|
||||
None = 0,
|
||||
Circle = 1,
|
||||
Rect = 2,
|
||||
}
|
||||
|
||||
export interface Column {
|
||||
|
|
|
@ -51,7 +51,7 @@ export const CommunityRow = ({
|
|||
style={styles.tableRow || {}}
|
||||
selected={selected}
|
||||
>
|
||||
{columns.map(c => {
|
||||
{columns.map((c) => {
|
||||
return (
|
||||
<Cell
|
||||
key={`comm-row-col-${c.header}-${community.id}`}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { useCallback, useMemo } from 'react'
|
|||
import { useDataBoundColorScale } from '~/hooks/graph'
|
||||
|
||||
import { BAR_HEIGHT } from '../CommunityList.styles'
|
||||
import type { Column} from '../CommunityList.types';
|
||||
import type { Column } from '../CommunityList.types'
|
||||
import { Mark } from '../CommunityList.types'
|
||||
|
||||
export function useDynamicColumn(
|
||||
|
|
|
@ -15,12 +15,12 @@ export function useRowHandling() {
|
|||
} = useSelection()
|
||||
|
||||
const onHover = useCallback(
|
||||
community => onHoverCommunity(community?.id),
|
||||
(community) => onHoverCommunity(community?.id),
|
||||
[onHoverCommunity],
|
||||
)
|
||||
|
||||
const onClick = useCallback(
|
||||
community => {
|
||||
(community) => {
|
||||
onResetSelection()
|
||||
onSelectCommunity(community?.id)
|
||||
},
|
||||
|
|
|
@ -28,7 +28,7 @@ export const GraphPanel: React.FC<GraphPanelProps> = memo(function GraphPanel({
|
|||
const cameraBounds = useDynamicCameraBounds()
|
||||
|
||||
const handleRendererInitialized = useCallback(
|
||||
renderer => {
|
||||
(renderer) => {
|
||||
if (onRendererReady) {
|
||||
onRendererReady(renderer)
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ function useCommunitySizes(ids: string[]): NavTreeArray[] {
|
|||
const byCommunity = useGroupedByCommunityTable()
|
||||
const byParent = useGroupedByParentTable()
|
||||
return useMemo(() => {
|
||||
return ids.map(id => {
|
||||
return ids.map((id) => {
|
||||
const nodes = findNodesCollectionForCommunity(id, byParent, byCommunity)
|
||||
return { id, size: nodes.size }
|
||||
})
|
||||
|
@ -74,7 +74,7 @@ function nodeColumns(
|
|||
const columnNames = nodes.table.columnNames()
|
||||
const { offset, count } = loadParams
|
||||
const values = nodes.page(
|
||||
node => nodeToEntityDetail(node, columnNames),
|
||||
(node) => nodeToEntityDetail(node, columnNames),
|
||||
offset,
|
||||
count,
|
||||
)
|
||||
|
@ -162,7 +162,7 @@ function getNeighborIds(counts: ColumnTable, communityId: string) {
|
|||
} as INeighborCommunityDetail)
|
||||
}
|
||||
if (output.length > max) {
|
||||
stop && stop()
|
||||
stop?.()
|
||||
}
|
||||
},
|
||||
true,
|
||||
|
|
|
@ -74,7 +74,7 @@ export const HierarchyBrowserPanel: React.FC = memo(
|
|||
visibleColumns: attrArray,
|
||||
controls: { showLevel: false, showFilter: false },
|
||||
styles: { cardOverview, table },
|
||||
} as ISettings),
|
||||
}) as ISettings,
|
||||
[attrArray],
|
||||
)
|
||||
|
||||
|
@ -107,7 +107,7 @@ function findParentId(params: ILoadParams, communities: ICommunityDetail[]) {
|
|||
return ROOT_COMMUNITY_ID
|
||||
}
|
||||
const currentIndex = communities.findIndex(
|
||||
c => c.communityId === params.communityId,
|
||||
(c) => c.communityId === params.communityId,
|
||||
)
|
||||
const parentIndex = currentIndex + 1
|
||||
const id = communities[parentIndex].communityId
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
import type { NodeCollection } from '@graph-drilldown/arquero'
|
||||
import type { GraphContainer, InputGraph } from '@graspologic/graph'
|
||||
import { Nodes, NodeSetHighlight } from '@graspologic/react'
|
||||
import { NodeSetHighlight, Nodes } from '@graspologic/react'
|
||||
import { useCallback } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
|
@ -65,12 +65,12 @@ export const Minimap = ({
|
|||
>
|
||||
<Nodes color={colorize} minRadius={minRadius} maxRadius={maxRadius} />
|
||||
<NodeSetHighlight
|
||||
key={`selected`}
|
||||
key={'selected'}
|
||||
vertexIds={selectedNodeIds}
|
||||
color={selectedColor}
|
||||
/>
|
||||
<NodeSetHighlight
|
||||
key={`hovered`}
|
||||
key={'hovered'}
|
||||
vertexIds={hoveredNodeIds}
|
||||
color={hoverColor}
|
||||
/>
|
||||
|
|
|
@ -26,7 +26,7 @@ export const PivotContent: React.FC<PivotContentProps> = memo(
|
|||
function PivotContent({ setSelectedKey, selectedKey }) {
|
||||
const handleLinkClick = useCallback(
|
||||
(item?: PivotItem | undefined) => {
|
||||
if (item && item.props.itemKey) {
|
||||
if (item?.props.itemKey) {
|
||||
setSelectedKey(item.props.itemKey as BrowserOptions)
|
||||
}
|
||||
},
|
||||
|
@ -35,18 +35,18 @@ export const PivotContent: React.FC<PivotContentProps> = memo(
|
|||
return (
|
||||
<Container>
|
||||
<Pivot
|
||||
aria-label="Community table and Hierarchy browser pivot"
|
||||
aria-label='Community table and Hierarchy browser pivot'
|
||||
selectedKey={selectedKey}
|
||||
onLinkClick={handleLinkClick}
|
||||
headersOnly={true}
|
||||
styles={styles}
|
||||
>
|
||||
<PivotItem
|
||||
headerText="Hierarchy browser"
|
||||
headerText='Hierarchy browser'
|
||||
itemKey={BrowserOptions.Browser}
|
||||
/>
|
||||
<PivotItem
|
||||
headerText="Community table"
|
||||
headerText='Community table'
|
||||
itemKey={BrowserOptions.Table}
|
||||
/>
|
||||
</Pivot>
|
||||
|
|
|
@ -96,7 +96,7 @@ export const ResizableBrowser: React.FC<GraphPanelProps> = memo(
|
|||
<StyledRnd
|
||||
size={{ width, height: position.height }}
|
||||
position={{ x: 0, y: position.y }}
|
||||
bounds="parent"
|
||||
bounds='parent'
|
||||
onResizeStop={handleResizeStop}
|
||||
disableDragging={true}
|
||||
enableResizing={enabledState}
|
||||
|
@ -108,8 +108,8 @@ export const ResizableBrowser: React.FC<GraphPanelProps> = memo(
|
|||
iconProps={{
|
||||
iconName,
|
||||
}}
|
||||
text="Resize viewer"
|
||||
title="Resize viewer"
|
||||
text='Resize viewer'
|
||||
title='Resize viewer'
|
||||
onClick={handleClick}
|
||||
/>
|
||||
<PivotContent
|
||||
|
|
|
@ -9,7 +9,7 @@ import { SPINNER_STYLE } from '~/styles'
|
|||
export const StyledSpinnner = () => {
|
||||
return (
|
||||
<Spinner
|
||||
label="Loading graph..."
|
||||
label='Loading graph...'
|
||||
styles={SPINNER_STYLE}
|
||||
size={SpinnerSize.large}
|
||||
/>
|
||||
|
|
|
@ -19,7 +19,7 @@ export const EdgeOpacityControls = () => {
|
|||
return (
|
||||
<Container>
|
||||
<NumericSelector
|
||||
label="edge opacity"
|
||||
label='edge opacity'
|
||||
table={table}
|
||||
encoding={encoding}
|
||||
onChange={updateEncoding}
|
||||
|
|
|
@ -19,7 +19,7 @@ export const EdgeSizeControls = () => {
|
|||
return (
|
||||
<Container>
|
||||
<NumericSelector
|
||||
label="edge size"
|
||||
label='edge size'
|
||||
table={table}
|
||||
encoding={encoding}
|
||||
onChange={updateEncoding}
|
||||
|
|
|
@ -16,7 +16,7 @@ export const NodeOpacityControls = () => {
|
|||
return (
|
||||
<Container>
|
||||
<NumericSelector
|
||||
label="node opacity"
|
||||
label='node opacity'
|
||||
table={table}
|
||||
encoding={encoding}
|
||||
onChange={updateEncoding}
|
||||
|
|
|
@ -16,7 +16,7 @@ export const NodeSizeControls = () => {
|
|||
return (
|
||||
<Container>
|
||||
<NumericSelector
|
||||
label="node size"
|
||||
label='node size'
|
||||
table={table}
|
||||
encoding={encoding}
|
||||
onChange={updateEncoding}
|
||||
|
|
|
@ -31,8 +31,8 @@ export const BreadcrumbPanel: React.FC<BreadcrumbPanelProps> = ({ styles }) => {
|
|||
const setNavState = useSetNavigationState()
|
||||
const navState = useNavigationState()
|
||||
useEffect(() => {
|
||||
setNavState(ids => {
|
||||
const index = ids.findIndex(c => c === selectedCommunity)
|
||||
setNavState((ids) => {
|
||||
const index = ids.findIndex((c) => c === selectedCommunity)
|
||||
if (index >= 0) {
|
||||
const sliced = ids.slice(0, index + 1)
|
||||
return sliced
|
||||
|
@ -42,7 +42,7 @@ export const BreadcrumbPanel: React.FC<BreadcrumbPanelProps> = ({ styles }) => {
|
|||
}, [selectedCommunity, setNavState])
|
||||
|
||||
const crumbs = useMemo((): IBreadcrumb[] => {
|
||||
return navState.map(id => {
|
||||
return navState.map((id) => {
|
||||
if (id === ROOT_COMMUNITY_ID) {
|
||||
return DEFAULT_CRUMBS
|
||||
}
|
||||
|
@ -54,14 +54,14 @@ export const BreadcrumbPanel: React.FC<BreadcrumbPanelProps> = ({ styles }) => {
|
|||
}, [navState])
|
||||
|
||||
const handleBreadcrumbClick = useCallback(
|
||||
item => {
|
||||
(item) => {
|
||||
item.key === 'root' ? onResetSelection() : onSelectCommunity(item.key)
|
||||
},
|
||||
[onSelectCommunity, onResetSelection],
|
||||
)
|
||||
|
||||
const items = useMemo(() => {
|
||||
return crumbs.map(c => ({
|
||||
return crumbs.map((c) => ({
|
||||
...c,
|
||||
onClick: handleBreadcrumbClick,
|
||||
}))
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
|
||||
import {
|
||||
CommunityCollection,
|
||||
NodeCollection,
|
||||
listColumnDefinitions,
|
||||
listColumnNames,
|
||||
NodeCollection,
|
||||
} from '@graph-drilldown/arquero'
|
||||
import { useDebounceFn } from 'ahooks'
|
||||
import { op, table } from 'arquero'
|
||||
|
@ -172,9 +172,9 @@ export function useSearchableTable(nodes: ColumnTable) {
|
|||
if (nodes.numRows() > 0) {
|
||||
const def = listColumnDefinitions(nodes)
|
||||
const columns = def
|
||||
.filter(d => d.dataType === 'string')
|
||||
.filter(d => d.name !== 'community.pid')
|
||||
.map(d => d.name)
|
||||
.filter((d) => d.dataType === 'string')
|
||||
.filter((d) => d.name !== 'community.pid')
|
||||
.map((d) => d.name)
|
||||
|
||||
return nodes.select(columns)
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ export function useSearchableTable(nodes: ColumnTable) {
|
|||
|
||||
function kFormatter(num: number): string {
|
||||
return Math.abs(num) > 999
|
||||
? (Math.abs(num) / 1000).toFixed(1) + 'k'
|
||||
? `${(Math.abs(num) / 1000).toFixed(1)}k`
|
||||
: `${Math.sign(num) * Math.abs(num)}`
|
||||
}
|
||||
|
||||
|
@ -229,7 +229,7 @@ const getMatchingValuesByRow = (
|
|||
): [CommunityCollection, NodeCollection, string | undefined] => {
|
||||
console.time('match')
|
||||
const matches: SearchByIndex[] = []
|
||||
nodes.scan(row => {
|
||||
nodes.scan((row) => {
|
||||
const o = columns.reduce(
|
||||
(acc, col) => {
|
||||
const [value, isInSearch] = getColumnByRow(nodes, col, row, searchValue)
|
||||
|
@ -253,8 +253,8 @@ const getMatchingValuesByRow = (
|
|||
const [nodeids, nodeCommIds, communityIds] = matches.reduce(
|
||||
(acc, d) => {
|
||||
if (d.matchColumns.includes('node.id')) {
|
||||
const nodeid = d['node.id']!
|
||||
if (!seen.has(nodeid)) {
|
||||
const nodeid = d['node.id']
|
||||
if (nodeid != null && !seen.has(nodeid)) {
|
||||
acc[1].push(d['community.id'])
|
||||
seen.add(nodeid)
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ export const SearchPanelHeader = ({
|
|||
return (
|
||||
<Container>
|
||||
<SearchBox
|
||||
placeholder="Search graph"
|
||||
placeholder='Search graph'
|
||||
styles={searchBoxStyle}
|
||||
disabled={disabled}
|
||||
onClear={onClear}
|
||||
|
@ -48,7 +48,7 @@ export const SearchPanelHeader = ({
|
|||
<IconButton
|
||||
iconProps={!isSearching ? searchIcon : {}}
|
||||
styles={searchButtonStyle}
|
||||
title="Search"
|
||||
title='Search'
|
||||
ariaLabel={'Search'}
|
||||
disabled={disabled}
|
||||
onClick={() => onSearch()}
|
||||
|
|
|
@ -49,7 +49,7 @@ export const SearchResults: React.FC<SearchResultsProps> = ({
|
|||
const { sorted } = useSortHandling(communities)
|
||||
|
||||
const handleCommunityHover = useCallback(
|
||||
community => onHoverCommunity(community?.id),
|
||||
(community) => onHoverCommunity(community?.id),
|
||||
[onHoverCommunity],
|
||||
)
|
||||
|
||||
|
@ -73,7 +73,7 @@ export const SearchResults: React.FC<SearchResultsProps> = ({
|
|||
[nodes, onSelectNode, onSelectCommunity, onResetSelection, selectedNode],
|
||||
)
|
||||
const handleCommunityClick = useCallback(
|
||||
community => {
|
||||
(community) => {
|
||||
onResetSelection()
|
||||
if (community && community.id !== selectedCommunity) {
|
||||
onSelectCommunity(community.id)
|
||||
|
@ -86,7 +86,7 @@ export const SearchResults: React.FC<SearchResultsProps> = ({
|
|||
communities,
|
||||
0,
|
||||
0,
|
||||
col => col.field === 'community.id',
|
||||
(col) => col.field === 'community.id',
|
||||
)
|
||||
|
||||
const searchText = useSearchResultsText(communities, nodes)
|
||||
|
@ -102,7 +102,7 @@ export const SearchResults: React.FC<SearchResultsProps> = ({
|
|||
<Pivot aria-label={'Community or node selection'}>
|
||||
{communities && communities.size > 0 ? (
|
||||
<PivotItem
|
||||
headerText="Communities"
|
||||
headerText='Communities'
|
||||
headerButtonProps={{
|
||||
'data-order': 1,
|
||||
'data-title': 'community matches',
|
||||
|
@ -113,7 +113,7 @@ export const SearchResults: React.FC<SearchResultsProps> = ({
|
|||
<Table tabIndex={0}>
|
||||
<tbody>
|
||||
{sorted && columns.length > 0
|
||||
? sorted.map(comm => {
|
||||
? sorted.map((comm) => {
|
||||
return (
|
||||
<CommunityRow
|
||||
community={comm}
|
||||
|
@ -136,7 +136,7 @@ export const SearchResults: React.FC<SearchResultsProps> = ({
|
|||
|
||||
{nodes && nodes.size > 0 ? (
|
||||
<PivotItem
|
||||
headerText="Nodes"
|
||||
headerText='Nodes'
|
||||
headerButtonProps={{
|
||||
'data-order': 2,
|
||||
'data-title': 'node matches',
|
||||
|
@ -147,7 +147,7 @@ export const SearchResults: React.FC<SearchResultsProps> = ({
|
|||
<Table tabIndex={0}>
|
||||
<tbody>
|
||||
{nodes
|
||||
? nodes.map(node => {
|
||||
? nodes.map((node) => {
|
||||
const nodeid = node.get('node.id')
|
||||
const selected = nodeid === selectedNode?.id
|
||||
return (
|
||||
|
|
|
@ -17,7 +17,7 @@ export const ColumnEditorPanel = () => {
|
|||
<ColumnEditor />
|
||||
<Reset>
|
||||
{hasData ? (
|
||||
<DefaultButton text="Clear all" onClick={doClearAll} />
|
||||
<DefaultButton text='Clear all' onClick={doClearAll} />
|
||||
) : null}
|
||||
</Reset>
|
||||
</Content>
|
||||
|
|
|
@ -65,11 +65,11 @@ export const HierarchyNav: React.FC<IHierarchyNav> = memo(
|
|||
const colorStyle = i === 0 ? highlight : normal
|
||||
const className = i === reverseList.length - 1 ? 'tree' : 'nested'
|
||||
const ranking = reverseList.length - 1 - i
|
||||
const content = (
|
||||
return (
|
||||
<ul className={className}>
|
||||
<li>
|
||||
<NestedContent
|
||||
className="tree_label"
|
||||
className='tree_label'
|
||||
id={`list-item-${ranking}`}
|
||||
onClick={() => handleListClick(item, i)}
|
||||
onKeyDown={() => handleListClick(item, i)}
|
||||
|
@ -93,8 +93,6 @@ export const HierarchyNav: React.FC<IHierarchyNav> = memo(
|
|||
</li>
|
||||
</ul>
|
||||
)
|
||||
prevContent = content
|
||||
return prevContent
|
||||
}, undefined as any)
|
||||
}, [reverseList, highlight, normal, handleListClick])
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import type ColumnTable from 'arquero/dist/types/table/column-table'
|
|||
import { memo, useCallback, useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import { executeLayout, Layout as LayoutType } from '~/layout'
|
||||
import { Layout as LayoutType, executeLayout } from '~/layout'
|
||||
import {
|
||||
useBigTable,
|
||||
useEdgeTable,
|
||||
|
@ -75,7 +75,7 @@ export const Layout: React.FC = memo(function Layout() {
|
|||
{features.enableSmallMultiples ? (
|
||||
<Toggle
|
||||
inlineLabel
|
||||
label="Community isolation"
|
||||
label='Community isolation'
|
||||
checked={graphView === ViewType.SmallMultiple}
|
||||
onChange={handleViewChanged}
|
||||
/>
|
||||
|
|
|
@ -33,7 +33,7 @@ export const NodeDetailsPanel = () => {
|
|||
return (
|
||||
<Content height={height}>
|
||||
{details ? (
|
||||
Object.entries(details).map(entry => (
|
||||
Object.entries(details).map((entry) => (
|
||||
<Field key={`node-details-${entry[0]}`}>
|
||||
<Name>{entry[0]}</Name>
|
||||
<Value>{entry[1]}</Value>
|
||||
|
|
|
@ -18,7 +18,7 @@ export const NodeHoverHeader: React.FC = () => {
|
|||
return <Unset>—</Unset>
|
||||
}
|
||||
const node = nodeMap.get(id)
|
||||
const label = node && node.get('node.label')
|
||||
const label = node?.get('node.label')
|
||||
return <Fixed>{label || nodeId}</Fixed>
|
||||
}, [nodeId, nodeMap])
|
||||
return (
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
* Copyright (c) Microsoft. All rights reserved.
|
||||
* Licensed under the MIT license. See LICENSE file in the project.
|
||||
*/
|
||||
import { HashRouter as HashRouterOriginal, Route as RouteOriginal} from 'react-router-dom'
|
||||
import {
|
||||
HashRouter as HashRouterOriginal,
|
||||
Route as RouteOriginal,
|
||||
} from 'react-router-dom'
|
||||
|
||||
export const HashRouter = HashRouterOriginal as any
|
||||
export const Route = RouteOriginal as any
|
||||
export const Route = RouteOriginal as any
|
||||
|
|
|
@ -96,13 +96,11 @@ export function useInternedMinimapGraph(): GraphContainer {
|
|||
|
||||
const visibleNodeMapState = selectorFamily<Map<string, Node>, string>({
|
||||
key: 'visible-nodes-map',
|
||||
get:
|
||||
cid =>
|
||||
({ get }) => {
|
||||
const table = get(communityNodesTableState(cid))
|
||||
const nodes = new NodeCollection(table)
|
||||
return nodes.toMap()
|
||||
},
|
||||
get: (cid) => ({ get }) => {
|
||||
const table = get(communityNodesTableState(cid))
|
||||
const nodes = new NodeCollection(table)
|
||||
return nodes.toMap()
|
||||
},
|
||||
})
|
||||
|
||||
export function useVisibleNodeMap(cid: string) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (c) Microsoft. All rights reserved.
|
||||
* Licensed under the MIT license. See LICENSE file in the project.
|
||||
*/
|
||||
import { findGroupIndices, NodeCollection } from '@graph-drilldown/arquero'
|
||||
import { NodeCollection, findGroupIndices } from '@graph-drilldown/arquero'
|
||||
import type { Node } from '@graph-drilldown/types'
|
||||
import { atom, selector, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче