This commit is contained in:
Chris Trevino 2023-08-30 10:45:29 -07:00
Родитель 8a5be5d935
Коммит f0bac2d00d
112 изменённых файлов: 10386 добавлений и 9853 удалений

Просмотреть файл

@ -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"]
}

10
.vscode/extensions.json поставляемый
Просмотреть файл

@ -1,7 +1,7 @@
{
"recommendations": [
"arcanis.vscode-zipfs",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
"recommendations": [
"arcanis.vscode-zipfs",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}

42
.vscode/settings.json поставляемый
Просмотреть файл

@ -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"
}
}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

550
.yarn/plugins/@yarnpkg/plugin-version.cjs поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

786
.yarn/releases/yarn-3.2.1.cjs поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

891
.yarn/releases/yarn-4.0.0-rc.50.cjs поставляемый Executable file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -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

Просмотреть файл

@ -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>&mdash;</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'

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше