Skip to content

Commit

Permalink
Merge branch '1.5.0-exaforce' into half-donut-2
Browse files Browse the repository at this point in the history
  • Loading branch information
curran authored Dec 9, 2024
2 parents 11471e2 + 44c5a25 commit 90a6f80
Show file tree
Hide file tree
Showing 25 changed files with 5,441 additions and 2,928 deletions.
8,159 changes: 5,323 additions & 2,836 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "unovis",
"version": "1.4.4",
"version": "1.5.0-exf.3",
"license": "Apache-2.0",
"private": true,
"workspaces": [
Expand Down Expand Up @@ -72,4 +72,4 @@
"lint-staged": {
"*.{js,ts,jsx,tsx,svelte}": "eslint --quiet --cache --fix"
}
}
}
4 changes: 2 additions & 2 deletions packages/angular/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@unovis/angular",
"description": "Modular data visualization framework for React, Angular, Svelte, and vanilla TypeScript or JavaScript",
"version": "1.4.4",
"version": "1.5.0-exf.3",
"repository": {
"type": "git",
"url": "https://github.com/f5/unovis.git",
Expand Down Expand Up @@ -44,7 +44,7 @@
"tslib": "^2.3.1"
},
"peerDependencies": {
"@unovis/ts": "1.4.4",
"@unovis/ts": "1.5.0-exf.3",
"@angular/common": "12 - 18",
"@angular/compiler": "12 - 18",
"@angular/core": "12 - 18"
Expand Down
4 changes: 2 additions & 2 deletions packages/react/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@unovis/react",
"description": "Modular data visualization framework for React, Angular, Svelte, and vanilla TypeScript or JavaScript",
"version": "1.4.4",
"version": "1.5.0-exf.3",
"repository": {
"type": "git",
"url": "https://github.com/f5/unovis.git",
Expand Down Expand Up @@ -35,7 +35,7 @@
"publish:dist": "rm -rf dist/.cache; cp ./{LICENSE,README.md,package.json} ./dist; cd ./dist; npm publish"
},
"peerDependencies": {
"@unovis/ts": "1.4.4",
"@unovis/ts": "1.5.0-exf.3",
"react": ">=16.8.0 || ^17 || ^18",
"react-dom": ">=16.8.0 || ^17 || ^18"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { JSX } from 'solid-js'
import { colors, Scale, Scatter } from '@unovis/ts'
import { VisAxis, VisBulletLegend, VisScatter, VisTooltip, VisXYContainer } from '@unovis/solid'
import { colors, Scale, Scatter } from '@unovis/ts'
import { JSX } from 'solid-js'
import { categories, data, DataRecord, shapes, sumCategories } from './data'

import { data, DataRecord, shapes, categories, sumCategories } from './data'

const ShapedScatterPlot = (): JSX.Element => {
const shapeScale = Scale.scaleOrdinal(shapes).domain(categories)
const colorScale = Scale.scaleOrdinal(colors).domain(categories)

// scatter props
const x = (d: DataRecord) => +new Date(d.date)
const x = (d: DataRecord) => +(new Date(`01-${d.date}`))
const y = (d: DataRecord) => d.trainedParam
const color = (d: DataRecord) => colorScale(sumCategories(d.owner))
const shape = (d: DataRecord) => shapeScale(sumCategories(d.owner))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
import { Scale, Scatter, StringAccessor, colors } from '@unovis/ts'
import { Scale, Scatter, colors } from '@unovis/ts'
import { data, DataRecord, shapes, categories, sumCategories } from './data'

const shapeScale = Scale.scaleOrdinal(shapes).domain(categories)
Expand All @@ -12,7 +12,7 @@ const colorScale = Scale.scaleOrdinal(colors).domain(categories)
export class ShapedScatterPlotComponent {
data = data

getX = (d: DataRecord): number => +(new Date(d.date))
getX = (d: DataRecord): number => +(new Date(`01-${d.date}`))
getY = (d: DataRecord): number => d.trainedParam
getColor = (d: DataRecord): string => colorScale(sumCategories(d.owner))
getShape = (d: DataRecord): string => shapeScale(sumCategories(d.owner))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
const colorScale = Scale.scaleOrdinal(colors).domain(categories)
// scatter props
const x = (d: DataRecord) => +(new Date(d.date))
const x = (d: DataRecord) => +(new Date(`01-${d.date}`))
const y = (d: DataRecord) => d.trainedParam
const color = (d: DataRecord) => colorScale(sumCategories(d.owner))
const shape = (d: DataRecord) => shapeScale(sumCategories(d.owner))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Axis, BulletLegend, Position, Scale, Scatter, Tooltip, XYContainer, colors } from '@unovis/ts'
import { Axis, BulletLegend, Scale, Scatter, Tooltip, XYContainer, colors } from '@unovis/ts'
import { data, DataRecord, shapes, categories, sumCategories } from './data'

const shapeScale = Scale.scaleOrdinal(shapes).domain(categories)
Expand All @@ -14,7 +14,7 @@ const legend = new BulletLegend(container, {
})

const scatter = new Scatter<DataRecord>({
x: (d: DataRecord) => +(new Date(d.date)),
x: (d: DataRecord) => +(new Date(`01-${d.date}`)),
y: (d: DataRecord) => d.trainedParam,
color: (d: DataRecord) => colorScale(sumCategories(d.owner)),
shape: (d: DataRecord) => shapeScale(sumCategories(d.owner)),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useCallback } from 'react'
import { Position, Scale, Scatter, colors } from '@unovis/ts'
import { VisAxis, VisBulletLegend, VisScatter, VisTooltip, VisXYContainer } from '@unovis/react'
import { data, DataRecord, shapes, categories, sumCategories } from './data'
import { colors, Scale, Scatter } from '@unovis/ts'
import React, { useCallback } from 'react'
import { categories, data, DataRecord, shapes, sumCategories } from './data'

const shapeScale = Scale.scaleOrdinal(shapes).domain(categories)
const colorScale = Scale.scaleOrdinal(colors).domain(categories)
Expand All @@ -20,7 +20,7 @@ export default function ShapedScatterPlot (): JSX.Element {
<VisBulletLegend items={legendItems}/>
<VisXYContainer data={data} height={'60vh'} scaleByDomain={true}>
<VisScatter
x={useCallback((d: DataRecord) => +(new Date(d.date)), [])}
x={useCallback((d: DataRecord) => +(new Date(`01-${d.date}`)), [])}
y={useCallback((d: DataRecord) => d.trainedParam, [])}
shape={useCallback((d: DataRecord) => shapeScale(sumCategories(d.owner)), [])}
size={15}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<script setup lang="ts">
import { Position, Scale, Scatter, colors } from '@unovis/ts'
import { Scale, Scatter, colors } from '@unovis/ts'
import { VisXYContainer, VisScatter, VisAxis, VisTooltip, VisBulletLegend } from '@unovis/vue'
import { data, DataRecord, shapes, categories, sumCategories } from './data'
const shapeScale = Scale.scaleOrdinal(shapes).domain(categories)
const colorScale = Scale.scaleOrdinal(colors).domain(categories)
// scatter props
const x = (d: DataRecord) => +(new Date(d.date))
const x = (d: DataRecord) => +(new Date(`01-${d.date}`))
const y = (d: DataRecord) => d.trainedParam
const color = (d: DataRecord) => colorScale(sumCategories(d.owner))
const shape = (d: DataRecord) => shapeScale(sumCategories(d.owner))
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@unovis/shared",
"description": "Modular data visualization framework for React, Angular, Svelte, and vanilla TypeScript or JavaScript",
"version": "1.4.4",
"version": "1.5.0-exf.3",
"repository": {
"type": "git",
"url": "https://github.com/f5/unovis.git",
Expand Down
4 changes: 2 additions & 2 deletions packages/solid/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@unovis/solid",
"description": "Modular data visualization framework for React, Angular, Svelte, Vue, Solid and vanilla TypeScript or JavaScript",
"version": "1.4.4",
"version": "1.5.0-exf.3",
"repository": {
"type": "git",
"url": "https://github.com/f5/unovis.git",
Expand Down Expand Up @@ -34,7 +34,7 @@
"gallery": "vite"
},
"peerDependencies": {
"@unovis/ts": "1.4.4",
"@unovis/ts": "1.5.0-exf.3",
"solid-js": "^1.9.0"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions packages/svelte/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@unovis/svelte",
"description": "Modular data visualization framework for React, Angular, Svelte, and vanilla TypeScript or JavaScript",
"version": "1.4.4",
"version": "1.5.0-exf.3",
"repository": {
"type": "git",
"url": "https://github.com/f5/unovis.git",
Expand Down Expand Up @@ -43,7 +43,7 @@
"publish:dist": "rm -rf dist/.cache; cp ./{LICENSE,README.md,package.json} ./dist; cd ./dist; npm publish"
},
"peerDependencies": {
"@unovis/ts": "1.4.4",
"@unovis/ts": "1.5.0-exf.3",
"svelte": "^3.48.0 || ^4.0.0"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/ts/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@unovis/ts",
"description": "Modular data visualization framework for React, Angular, Svelte, and vanilla TypeScript or JavaScript",
"version": "1.4.4",
"version": "1.5.0-exf.3",
"repository": {
"type": "git",
"url": "https://github.com/f5/unovis.git",
Expand Down
20 changes: 19 additions & 1 deletion packages/ts/src/components/graph/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import { D3DragEvent } from 'd3-drag'
import { D3ZoomEvent, ZoomTransform } from 'd3-zoom'
import { Selection } from 'd3-selection'

// Utils
import { isEqual } from 'utils/data'

// Config
import { ComponentConfigInterface, ComponentDefaultConfig } from 'core/component/config'

// Types
import { TrimMode } from 'types/text'
import { GraphInputLink, GraphInputNode } from 'types/graph'
import { GraphInputLink, GraphInputNode, GraphInputData } from 'types/graph'
import { BooleanAccessor, ColorAccessor, NumericAccessor, StringAccessor, GenericAccessor } from 'types/accessor'

// Local Types
Expand Down Expand Up @@ -145,6 +148,8 @@ export interface GraphConfigInterface<N extends GraphInputNode, L extends GraphI
* `1.5` - very curve.
* Default: `0` */
linkCurvature?: NumericAccessor<L>;
/** Highlight links on hover. Default: `true` */
linkHighlightOnHover?: boolean;
/** Set selected link by its unique id. Default: `undefined` */
selectedLinkId?: number | string;

Expand Down Expand Up @@ -261,6 +266,14 @@ export interface GraphConfigInterface<N extends GraphInputNode, L extends GraphI
width: number,
height: number
) => void;

/** Determines whether the component should update when new data is provided.
* This function takes the previous and new data as parameters and returns a boolean
* indicating whether the update should proceed. Useful for fine-grained control over
* update behavior when your data has a complex nested structure.
* By default the `isEqual` function from Unovis will be used to do the comparison.
*/
shouldDataUpdate?: (prevData: GraphInputData<N, L>, nextData: GraphInputData<N, L>) => boolean;
}

export const GraphDefaultConfig: GraphConfigInterface<GraphInputNode, GraphInputLink> = {
Expand Down Expand Up @@ -316,6 +329,7 @@ export const GraphDefaultConfig: GraphConfigInterface<GraphInputNode, GraphInput
linkNeighborSpacing: 8,
linkDisabled: false,
linkCurvature: 0,
linkHighlightOnHover: true,
selectedLinkId: undefined,
nodeGaugeAnimDuration: 1500,

Expand Down Expand Up @@ -361,4 +375,8 @@ export const GraphDefaultConfig: GraphConfigInterface<GraphInputNode, GraphInput
onNodeSelectionBrush: undefined,
onNodeSelectionDrag: undefined,
onRenderComplete: undefined,

shouldDataUpdate: (prevData: GraphInputData<GraphInputNode, GraphInputLink>, nextData: GraphInputData<GraphInputNode, GraphInputLink>): boolean => {
return !isEqual(prevData, nextData)
},
}
18 changes: 6 additions & 12 deletions packages/ts/src/components/graph/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ComponentCore } from 'core/component'
import { GraphDataModel } from 'data-models/graph'

// Types
import { GraphInputLink, GraphInputNode } from 'types/graph'
import { GraphInputLink, GraphInputNode, GraphInputData } from 'types/graph'
import { Spacing } from 'types/spacing'

// Utils
Expand Down Expand Up @@ -43,7 +43,7 @@ export class Graph<
N extends GraphInputNode,
L extends GraphInputLink,
> extends ComponentCore<
{nodes: N[]; links?: L[]},
GraphInputData<N, L>,
GraphConfigInterface<N, L>
> {
static selectors = {
Expand All @@ -58,6 +58,7 @@ export class Graph<
dimmedNode: nodeSelectors.greyedOutNode,
link: linkSelectors.gLink,
linkLine: linkSelectors.link,
linkLabel: linkSelectors.linkLabelGroup,
dimmedLink: linkSelectors.greyedOutLink,
panel: panelSelectors.gPanel,
panelRect: panelSelectors.panel,
Expand Down Expand Up @@ -161,9 +162,9 @@ export class Graph<
this._getLinkArrowDefId = this._getLinkArrowDefId.bind(this)
}

setData (data: {nodes: N[]; links?: L[]}): void {
setData (data: GraphInputData<N, L>): void {
const { config } = this
if (isEqual(this.datamodel.data, data)) return
if (!config.shouldDataUpdate(this.datamodel.data, data)) return

this.datamodel.nodeSort = config.nodeSort
this.datamodel.data = data
Expand Down Expand Up @@ -295,13 +296,6 @@ export class Graph<
if (disableZoom) this.g.on('.zoom', null)
else this.g.call(this._zoomBehavior).on('dblclick.zoom', null)

// While the graph is animating we disable pointer events on the graph group
if (animDuration) { this._graphGroup.attr('pointer-events', 'none') }
smartTransition(this._graphGroup, animDuration)
.on('end interrupt', () => {
this._graphGroup.attr('pointer-events', null)
})

// We need to set up events and attributes again because the rendering might have been delayed by the layout
// calculation and they were not set up properly (see the render function of `ComponentCore`)
this._setUpComponentEventsThrottled()
Expand Down Expand Up @@ -643,7 +637,7 @@ export class Graph<
private _onLinkMouseOver (d: GraphLink<N, L>): void {
if (this._isDragging) return

d._state.hovered = true
if (this.config.linkHighlightOnHover) d._state.hovered = true
this._updateNodesLinksPartial()
}

Expand Down
2 changes: 0 additions & 2 deletions packages/ts/src/components/graph/modules/link/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export const linkSupport = css`
fill: none;
stroke-linecap: round;
pointer-events: stroke;
stroke-width: var(--vis-graph-link-support-stroke-width);
stroke-opacity: 0;
stroke: var(--vis-graph-link-stroke-color);
Expand Down Expand Up @@ -108,7 +107,6 @@ export const flowCircle = css`

export const linkLabelGroup = css`
label: label-group;
pointer-events: all;
`

export const linkLabelBackground = css`
Expand Down
8 changes: 8 additions & 0 deletions packages/ts/src/types/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ export interface GraphInputLink {
target: number | string | GraphInputNode;
}

export type GraphInputData<
N extends GraphInputNode = GraphInputNode,
L extends GraphInputLink = GraphInputLink,
> = {
nodes: N[];
links?: L[];
}

export type GraphNodeCore<N extends GraphInputNode, L extends GraphInputLink> = N & {
// eslint-disable-next-line no-use-before-define
links: GraphLinkCore<N, L>[];
Expand Down
19 changes: 13 additions & 6 deletions packages/ts/src/utils/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,20 @@ export const isEmpty = <T>(obj: T): boolean => {
}

// Based on https://github.com/maplibre/maplibre-gl-js/blob/e78ad7944ef768e67416daa4af86b0464bd0f617/src/style-spec/util/deep_equal.ts, 3-Clause BSD license
export const isEqual = (a?: unknown | null, b?: unknown | null, visited: Set<any> = new Set()): boolean => {
export const isEqual = (
a: unknown | null | undefined,
b: unknown | null | undefined,
skipKeys: string[] = [],
visited: Set<any> = new Set()
): boolean => {
if (Array.isArray(a)) {
if (!Array.isArray(b) || a.length !== b.length) return false

if (visited.has(a)) return true
else visited.add(a)

for (let i = 0; i < a.length; i++) {
if (!isEqual(a[i], b[i], visited)) return false
if (!isEqual(a[i], b[i], skipKeys, visited)) return false
}

return true
Expand All @@ -44,14 +49,16 @@ export const isEqual = (a?: unknown | null, b?: unknown | null, visited: Set<any
if (!(typeof b === 'object')) return false
if (a === b) return true

const keys = Object.keys(a)
if (keys.length !== Object.keys(b).length) return false
const keysA = Object.keys(a).filter(key => !skipKeys.includes(key))
const keysB = Object.keys(b).filter(key => !skipKeys.includes(key))

if (keysA.length !== keysB.length) return false

if (visited.has(a)) return true
else visited.add(a)

for (const key in a) {
if (!isEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key], visited)) return false
for (const key of keysA) {
if (!isEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key], skipKeys, visited)) return false
}

return true
Expand Down
Loading

0 comments on commit 90a6f80

Please sign in to comment.