From 13924915e8c2de7e59f7e852aecf64c057666e9b Mon Sep 17 00:00:00 2001 From: Ravi Raja Merugu Date: Wed, 21 Aug 2024 12:27:51 +0530 Subject: [PATCH] Node and Links Styling stories added (#16) * :selected state added to link * link styling - color by group added * link styling - color by group added * arrow head pointiness fixes * color by group styling for links * color by group styling for nodes * color by group styling for nodes * node styling updates * node styling updates: * node and link styling updates --- src/renderer/primitives/arrows/arrowHead.ts | 5 +- .../primitives/arrows/arrowTriangle.ts | 4 +- src/renderer/primitives/image.ts | 18 +-- src/renderer/primitives/label.ts | 37 ++--- src/renderer/primitives/links/straightLine.ts | 3 +- src/renderer/primitives/nodes/circle.ts | 2 - src/renderer/shapes/abstract.ts | 4 + src/renderer/shapes/constants.ts | 2 + src/renderer/shapes/links/base.ts | 53 +++++-- src/renderer/shapes/links/defaults.ts | 13 +- .../shapes/links/lines/straightLine.ts | 15 +- src/renderer/shapes/links/utils.ts | 6 +- src/renderer/shapes/nodes/base.ts | 76 ++++------ src/renderer/shapes/nodes/circle/defaults.ts | 12 +- src/renderer/types/styles.ts | 2 +- src/store/store.ts | 27 ++-- src/stories/features/links/data/linkStates.ts | 89 ------------ src/stories/features/links/data/linkTypes.ts | 131 ------------------ src/stories/features/links/link.stories.ts | 38 ----- src/stories/features/links/states/code.ts | 51 +++++++ .../features/links/states/index.stories.ts | 36 +++++ .../features/links/styling/colorByGroup.ts | 98 +++++++++++++ .../links/styling/colorIndividualLinks.ts | 50 +++++++ .../features/links/styling/index.stories.ts | 41 ++++++ .../links/styling/label-formatting.ts | 92 ++++++++++++ .../nodes/styling/codes/colorByGroup.ts | 73 ++++++++++ .../nodes/styling/codes/label-formatting.ts | 96 +++++++++++++ .../nodes/styling/codes/label-styling.ts | 40 ------ .../styling/codes/style-individual-nodes.ts | 77 ++++++++++ .../features/nodes/styling/index.stories.ts | 41 ++++-- 30 files changed, 796 insertions(+), 436 deletions(-) delete mode 100644 src/stories/features/links/data/linkStates.ts delete mode 100644 src/stories/features/links/data/linkTypes.ts delete mode 100644 src/stories/features/links/link.stories.ts create mode 100644 src/stories/features/links/states/code.ts create mode 100644 src/stories/features/links/states/index.stories.ts create mode 100644 src/stories/features/links/styling/colorByGroup.ts create mode 100644 src/stories/features/links/styling/colorIndividualLinks.ts create mode 100644 src/stories/features/links/styling/index.stories.ts create mode 100644 src/stories/features/links/styling/label-formatting.ts create mode 100644 src/stories/features/nodes/styling/codes/colorByGroup.ts create mode 100644 src/stories/features/nodes/styling/codes/label-formatting.ts delete mode 100644 src/stories/features/nodes/styling/codes/label-styling.ts create mode 100644 src/stories/features/nodes/styling/codes/style-individual-nodes.ts diff --git a/src/renderer/primitives/arrows/arrowHead.ts b/src/renderer/primitives/arrows/arrowHead.ts index 29a7fff4..20f61c95 100644 --- a/src/renderer/primitives/arrows/arrowHead.ts +++ b/src/renderer/primitives/arrows/arrowHead.ts @@ -14,13 +14,16 @@ const createArrowHeadPoints = () => { } const drawArrowHeadShape = ( props: DrawArrowPrimitiveType) => { + /* + deprecated + */ console.debug("drawArrowHeadShape", props); // draw arrow // gfx = gfx ? gfx : new Graphics() const gfx = new Graphics() const points = createArrowHeadPoints() // const points = [0, 0, 10, -5, 6.666666666666667, 0, 10, 5, 0, 0] - gfx.lineStyle(props.thickness, props.color, props.opacity); + gfx.lineStyle(props.thickness, props.color, props.opacity, alignment); gfx.beginFill(props.color, props.opacity ); gfx.drawPolygon(points); gfx.endFill(); diff --git a/src/renderer/primitives/arrows/arrowTriangle.ts b/src/renderer/primitives/arrows/arrowTriangle.ts index 1b9248d7..554d6446 100644 --- a/src/renderer/primitives/arrows/arrowTriangle.ts +++ b/src/renderer/primitives/arrows/arrowTriangle.ts @@ -7,7 +7,7 @@ export interface DrawArrowPrimitiveType extends ILinkShapeStyle { // gfx: Graphics } -const drawArrowTriangleShape = ( props: DrawArrowPrimitiveType, size: number = 5, gfx?: Graphics) => { +const drawArrowTriangleShape = ( props: DrawArrowPrimitiveType, size: number = 2, gfx?: Graphics) => { console.debug("drawArrowHeadShape", props); // draw arrow gfx = gfx ? gfx : new Graphics() @@ -24,7 +24,7 @@ const drawArrowTriangleShape = ( props: DrawArrowPrimitiveType, size: number = 5 gfx.lineTo(arrowPoint1X, arrowPoint1Y); gfx.lineTo(arrowPoint2X, arrowPoint2Y); gfx.lineTo(props.endPoint.x, props.endPoint.y); - gfx.stroke({color: props.color}); + gfx.stroke({color: props.color, width: props.thickness, alignment: 0.66}); gfx.fill({color: props.color}) return gfx; } diff --git a/src/renderer/primitives/image.ts b/src/renderer/primitives/image.ts index cbfe633e..1031eedf 100644 --- a/src/renderer/primitives/image.ts +++ b/src/renderer/primitives/image.ts @@ -8,15 +8,15 @@ import { Assets} from "pixi.js"; // } const drawImageShape = (imageUrl: string, resolution: number= window.devicePixelRatio) => { - const extras: any = { - data: { - resolution: resolution, - // resourceOptions: { - // scale: window.devicePixelRatio - // } - } - } - console.log("drawImage extra", extras, imageUrl) + // const extras: any = { + // data: { + // resolution: resolution, + // // resourceOptions: { + // // scale: window.devicePixelRatio + // // } + // } + // } + console.log("drawImage extra", imageUrl) return Assets.load({ src: imageUrl, data: { diff --git a/src/renderer/primitives/label.ts b/src/renderer/primitives/label.ts index 14d96ded..de6783b3 100644 --- a/src/renderer/primitives/label.ts +++ b/src/renderer/primitives/label.ts @@ -10,40 +10,35 @@ export interface LabelPrimitiveType extends IShapeLabelStyle { const drawLabelShape = (props: LabelPrimitiveType, resolution: number = window.devicePixelRatio * 2) => { // console.log("===drawLabelShape props", props) const labelGfx = new Graphics(); - // if (!props.label){ // // if label is not found, not point it rendering, so return // return // } // add text - const textStyle = new HTMLTextStyle({ - // fontFamily: props?.text.font.family, - // fontSize: props?.text.font.size, - // fill: props?.text.color, - // align: "center" - - + const textStyle = new TextStyle({ fontFamily: props?.text.font.family, fontSize: props?.text.font.size, fill: props?.text.color, align: props?.text.font.align, fontWeight: props?.text.font.weight, fontStyle: props?.text.font.style, - lineHeight: props.text.font.lineHeight + lineHeight: props.text.font.lineHeight, + wordWrap: props.text.font.wordWrap, + wordWrapWidth: props.text.font.wordWrapWidth, }) - const text = new HTMLText( {text:props.label, style: textStyle}); - text.resolution = resolution ; + const text = new HTMLText({ text: props.label, style: textStyle }); + text.resolution = resolution; text.label = NodeContainerChildNames.labelText const textBounds = text.getBounds(); // Get the size of the text box - if (props?.background?.color){ + if (props?.background?.color) { // console.log("===drawLabelShape props.background.color", props.background.color) // add background; TODO- move this to rectangle primitive later const textBackground = new Graphics(); - textBackground.lineStyle(props.border.thickness, props.border.color); + textBackground.lineStyle(props.border.thickness, props.border.color, props.border.opacity || 1); textBackground.beginFill( props?.background.color, @@ -66,20 +61,16 @@ const drawLabelShape = (props: LabelPrimitiveType, resolution: number = window.d textBackground.y = text.y // add background and text to gfx labelGfx.addChild(textBackground) + } - // const mask = new Graphics(); - // mask.rect( - // // props.padding * -1 , props.padding * -1, - // 0, 0, - // textBounds.width + (props.padding * 2) , - // textBounds.height // + props.padding - // ); // Draw rectangle behind the text - // // mask.fill(0xffffff); - // mask.fill("#222222") - // text.mask = mask + + + + // labelGfx.addChild(mask) labelGfx.addChild(text) + return labelGfx } diff --git a/src/renderer/primitives/links/straightLine.ts b/src/renderer/primitives/links/straightLine.ts index 6e2bbc85..28d48872 100644 --- a/src/renderer/primitives/links/straightLine.ts +++ b/src/renderer/primitives/links/straightLine.ts @@ -13,9 +13,10 @@ const drawStraightLineShape = ( props: DrawLinkPrimitiveType) => { console.debug("drawStraightLineShape", props); const shapeName = new Graphics(); // draw the path + // shapeName.lineStyle({ width: props.thickness, color: props.color }); // Thickness 5, Red color shapeName.moveTo(props.startPoint.x, props.startPoint.y); shapeName.lineTo(props.endPoint.x, props.endPoint.y); - shapeName.stroke({color: props.color}); + shapeName.stroke({color: props.color, width: props.thickness}); shapeName.fill({color: props.color}) shapeName.interactive = true; // TODO - FIX this hitarea diff --git a/src/renderer/primitives/nodes/circle.ts b/src/renderer/primitives/nodes/circle.ts index 78d4bf0c..30b4290b 100644 --- a/src/renderer/primitives/nodes/circle.ts +++ b/src/renderer/primitives/nodes/circle.ts @@ -11,8 +11,6 @@ export interface DrawCirclePrimitiveType { const drawCircleShape = (props: DrawCirclePrimitiveType) => { console.debug("drawCircle", props); const shapeName = new Graphics(); - - shapeName.circle(0, 0, props.size); if (props.background){ const alpha = props.background.color === "transparent" ? 0: props.background.opacity diff --git a/src/renderer/shapes/abstract.ts b/src/renderer/shapes/abstract.ts index fb4c5181..5aa3d038 100644 --- a/src/renderer/shapes/abstract.ts +++ b/src/renderer/shapes/abstract.ts @@ -291,6 +291,7 @@ export abstract class LinkShapeAbstract extends ShapeAbstract { // this.triggerUnHovered(event); this.triggerUnHighlighted(event, setNeighborsToo); this.triggerDefault(event); + this.triggerUnSelected(event) // if (setNeighborsToo) { // // this.triggerUnHoveredOnNeighbors(event) // this.triggerUnHighlightedOnNeighbors(event) @@ -309,6 +310,9 @@ export abstract class LinkShapeAbstract extends ShapeAbstract { // this.triggerHighlightedOnNeighbors(event) // } } + else if (stateName === ":selected") { + this.triggerSelected(event) + } else if (stateName === ":muted") { this.triggerMuted(event) } diff --git a/src/renderer/shapes/constants.ts b/src/renderer/shapes/constants.ts index 016a26f6..09396de3 100644 --- a/src/renderer/shapes/constants.ts +++ b/src/renderer/shapes/constants.ts @@ -26,6 +26,8 @@ export enum LinkContainerChildNames { shapeLine = 'shapeLine', shapeHoveredBorder = 'shapeHoveredBorder', shapeHighlightedBorder = 'shapeHighlightedBorder', + shapeSelectedBorder = 'shapeSelectedBorder', + // label label = 'label', labelBackground = 'labelBackground', diff --git a/src/renderer/shapes/links/base.ts b/src/renderer/shapes/links/base.ts index f59f68c3..c202cea9 100644 --- a/src/renderer/shapes/links/base.ts +++ b/src/renderer/shapes/links/base.ts @@ -59,13 +59,37 @@ export class LinkShapeBase extends LinkShapeAbstract { this.containerGfx.alpha = 1; this.containerGfx.visible = true } - - triggerSelected = (event?: PIXI.FederatedPointerEvent) => { - // console.log(`Selected triggered on link - ${this.data.id}`); + + triggerSelected = (event?: PIXI.FederatedPointerEvent, setNeighborsToo: boolean = false) => { + console.debug(`Selected triggered on link - ${this.data.id}`); + this.moveToFrontLayer(); + + if (this.shapeGfx) { + const selectedBorder = this.shapeGfx.getChildByName(LinkContainerChildNames.shapeSelectedBorder); + if (selectedBorder) { + selectedBorder.visible = true + } + } + if (setNeighborsToo) { + this.artBoard.canvas.dataStore.nodes.get(this.data.source.id)?.gfxInstance?.triggerSelected() + this.artBoard.canvas.dataStore.nodes.get(this.data.target.id)?.gfxInstance?.triggerSelected() + } } - triggerUnSelected = (event?: PIXI.FederatedPointerEvent) => { - // console.log(`UnSelected triggered on link - ${this.data.id}`); + triggerUnSelected = (event?: PIXI.FederatedPointerEvent, setNeighborsToo: boolean = false) => { + console.debug(`UnSelected triggered on link - ${this.data.id}`); + this.moveToFrontLayer(); + + if (this.shapeGfx) { + const selectedBorder = this.shapeGfx.getChildByName(LinkContainerChildNames.shapeSelectedBorder); + if (selectedBorder) { + selectedBorder.visible = false + } + } + if (setNeighborsToo) { + this.artBoard.canvas.dataStore.nodes.get(this.data.source.id)?.gfxInstance?.triggerUnSelected() + this.artBoard.canvas.dataStore.nodes.get(this.data.target.id)?.gfxInstance?.triggerUnSelected() + } } triggerHighlighted = (event?: PIXI.FederatedPointerEvent, setNeighborsToo: boolean = false) => { @@ -129,7 +153,7 @@ export class LinkShapeBase extends LinkShapeAbstract { .on('pointerdown', (event) => { this.dragData = event.data this.artBoard.canvas.dataStore.addToHighlightedLinks(this.data) - this.setState(":highlighted", true, event) + this.setState(":selected", true, event) }) .on("pointermove", (event) => { event.stopPropagation() @@ -164,8 +188,7 @@ export class LinkShapeBase extends LinkShapeAbstract { labelGfx.visible = this.data.isLabelVisible - - // this.containerGfx.addChild(labelGfx) + this.containerGfx.addChild(labelGfx) return labelGfx } // else { @@ -183,7 +206,7 @@ export class LinkShapeBase extends LinkShapeAbstract { // draw shapeName if (renderShape) { - if (this.shapeGfx){ // if already exist + if (this.shapeGfx) { // if already exist this.shapeGfx.removeChildren(); } this.shapeGfx = this.drawShape(); @@ -191,22 +214,28 @@ export class LinkShapeBase extends LinkShapeAbstract { } // draw label if (renderLabel) { - if (this.labelGfx){ // if already exist + if (this.labelGfx) { // if already exist this.labelGfx.destroy(); } this.labelGfx = this.drawLabel(); - if(this.labelGfx){ + if (this.labelGfx) { this.containerGfx.addChild(this.labelGfx); // const textBg = this.labelGfx?.getChildByName(LinkContainerChildNames.labelBackground); // if (textBg) { // textBg.visible = true // } + + + + } } if (renderShape) { - if (this.labelGfx){ + if (this.labelGfx) { this.calcLabelPosition(this.labelGfx, this.shapeGfx) + + } } diff --git a/src/renderer/shapes/links/defaults.ts b/src/renderer/shapes/links/defaults.ts index 8a99ac99..c6fa71ca 100644 --- a/src/renderer/shapes/links/defaults.ts +++ b/src/renderer/shapes/links/defaults.ts @@ -10,7 +10,7 @@ export const LinkStyleDefaults: ILinkStyle = { }, label: { background: { - color: "#555555", + color: "#222222", opacity: 0.5 }, padding: 3, @@ -23,7 +23,9 @@ export const LinkStyleDefaults: ILinkStyle = { color: "#999999", font: { size: 10, - family: "Arial" + family: "Arial", + wordWrap: true, + wordWrapWidth: 100, } } }, @@ -43,6 +45,13 @@ export const LinkStyleDefaults: ILinkStyle = { thickness: 3, color: 0xfeeb77 } + }, + ":selected": { + shape: { + opacity: 0.6, + thickness: 5, + color: 0xf11b77 + } } } diff --git a/src/renderer/shapes/links/lines/straightLine.ts b/src/renderer/shapes/links/lines/straightLine.ts index 17c32303..e7d597a7 100644 --- a/src/renderer/shapes/links/lines/straightLine.ts +++ b/src/renderer/shapes/links/lines/straightLine.ts @@ -15,22 +15,29 @@ class StraightLine extends LinkShapeBase{ const { startPoint, endPoint } = this.calcStartAndEndPoints(); this.shapeGfx.removeChildren(); - this.shapeGfx.name = LinkContainerChildNames.shapeName + this.shapeGfx.label = LinkContainerChildNames.shapeName // draw path const shapeLine = drawStraightLineShape({ startPoint, endPoint, ...this.data.style.shape }) - shapeLine.name = LinkContainerChildNames.shapeLine + shapeLine.label = LinkContainerChildNames.shapeLine drawArrowTriangleShape({ startPoint, endPoint, ...this.data.style.shape }, 10, shapeLine) this.shapeGfx.addChild(shapeLine) // shapeName hoveredBorder const shapeHighlightedBorder = drawStraightLineShape({ startPoint, endPoint, ...this.data.style.states[':highlighted'].shape }) - shapeHighlightedBorder.name = LinkContainerChildNames.shapeHighlightedBorder + shapeHighlightedBorder.label = LinkContainerChildNames.shapeHighlightedBorder shapeHighlightedBorder.visible = false drawArrowTriangleShape({ startPoint, endPoint, ...this.data.style.states[':highlighted'].shape }, 12, shapeHighlightedBorder) this.shapeGfx.addChild(shapeHighlightedBorder) + // shapeName hoveredBorder + const shapeSelectedBorder = drawStraightLineShape({ startPoint, endPoint, ...this.data.style.states[':selected'].shape }) + shapeSelectedBorder.label = LinkContainerChildNames.shapeSelectedBorder + shapeSelectedBorder.visible = false + drawArrowTriangleShape({ startPoint, endPoint, ...this.data.style.states[':selected'].shape }, 12, shapeSelectedBorder) + this.shapeGfx.addChild(shapeSelectedBorder) + return this.shapeGfx } @@ -82,7 +89,7 @@ class StraightLine extends LinkShapeBase{ // line color and thickness // console.log("calcStartAndEndPoints", JSON.stringify(this.data)) console.debug("====calcStartAndEndPoints", this.data, this) - const arrowPadding = 3; + const arrowPadding = 0; const endPoint = getContactPointOnCircle( this.data.source, this.data.target, diff --git a/src/renderer/shapes/links/utils.ts b/src/renderer/shapes/links/utils.ts index 78f8e9a7..c4bd0844 100644 --- a/src/renderer/shapes/links/utils.ts +++ b/src/renderer/shapes/links/utils.ts @@ -13,12 +13,12 @@ export const getContactPointOnCircle = ( // console.debug("====target", target, padding) // getCirclePont - const nodeRadius = target?.style?.size + padding ? target?.style?.size : 20 + const nodeRadius = target?.style?.size ? target?.style?.size + padding : 20 // console.debug("===nodeRadius", nodeRadius) const arrowheadLength = 0; const angle = Math.atan2(target.y - source.y, target.x - source.x); - let x = target.x - Math.cos(angle) * (nodeRadius + arrowheadLength); - let y = target.y - Math.sin(angle) * (nodeRadius + arrowheadLength); + const x : number = target.x - Math.cos(angle) * (nodeRadius + arrowheadLength); + const y: number = target.y - Math.sin(angle) * (nodeRadius + arrowheadLength); return new Point(x, y) }; diff --git a/src/renderer/shapes/nodes/base.ts b/src/renderer/shapes/nodes/base.ts index c177a571..d09b4b76 100644 --- a/src/renderer/shapes/nodes/base.ts +++ b/src/renderer/shapes/nodes/base.ts @@ -10,7 +10,7 @@ import { NodeContainerChildNames } from "../constants"; export const ZIndexOrder = { DATA_LAYER_LINKS: 4, DATA_LAYER_NODES: 5, - + FRONT_LAYER_LINKS: 9, FRONT_LAYER_NODES: 10, @@ -32,7 +32,7 @@ export class NodeShapeBase extends NodeShapeAbstract { declare drawShape declare drawLabel - + constructor(data: CanvasNode, artBoard: ArtBoard) { @@ -131,7 +131,7 @@ export class NodeShapeBase extends NodeShapeAbstract { shapeHighlightedBorder.visible = true } - if (this.labelGfx){ + if (this.labelGfx) { const textBg = this.labelGfx.getChildByName(NodeContainerChildNames.labelBackground); if (textBg) { textBg.visible = true @@ -139,13 +139,13 @@ export class NodeShapeBase extends NodeShapeAbstract { } } - if (setNeighborsToo){ + if (setNeighborsToo) { this.data.neighbors.nodes.forEach((node: CanvasNode) => { node.gfxInstance?.setState(":highlighted", false, event) }) this.data.neighbors.links.forEach((link: CanvasLink) => { link.gfxInstance?.setState(":highlighted", false, event) - }); + }); } } @@ -157,7 +157,7 @@ export class NodeShapeBase extends NodeShapeAbstract { if (shapeHighlightedBorder) { shapeHighlightedBorder.visible = false } - if (this.labelGfx){ + if (this.labelGfx) { const textBg = this.labelGfx.getChildByName(NodeContainerChildNames.labelBackground); if (textBg) { textBg.visible = false @@ -167,8 +167,8 @@ export class NodeShapeBase extends NodeShapeAbstract { } - if (setNeighborsToo){ - + if (setNeighborsToo) { + this.data.neighbors.nodes.forEach((node: CanvasNode) => { node.gfxInstance?.setState(":default", false, event) }) @@ -200,7 +200,7 @@ export class NodeShapeBase extends NodeShapeAbstract { this.artBoard.canvas.dataStore.moveNodeTo(this.data.id, newPoint.x, newPoint.y, event) // remove interactions on neighbors this.artBoard.canvas.dataStore.getNeighborLinks(this.data.id).forEach((link: CanvasLink) => { - if (link.gfxInstance){ + if (link.gfxInstance) { link.gfxInstance.removeInteractionTriggers() } }) @@ -213,12 +213,9 @@ export class NodeShapeBase extends NodeShapeAbstract { // console.log("onDragEnd triggered") event.stopPropagation() this.dragData = null - // if (!this.data.isDraggable) return - this.containerGfx.parent.off('pointermove', this.onDragMove); this.artBoard.canvas.dataStore.getNeighborLinks(this.data.id).forEach((link: CanvasLink) => { - if (link.gfxInstance) - link.gfxInstance.setupInteractionTriggers() + if (link.gfxInstance) { link.gfxInstance.setupInteractionTriggers() } }) this.triggerUnHighlighted(event, true) @@ -227,36 +224,19 @@ export class NodeShapeBase extends NodeShapeAbstract { pointerOver = (event: PIXI.FederatedPointerEvent) => { console.log("====pointerOver", this.data.id, this.data.state, this.data.isHoverable, !this.data.isHoverable) event.stopPropagation(); - if(this.data.state === ":muted") return - + if (this.data.state === ":muted") return if (this.dragData) return - - if (this.data.isHoverable){ - this.setState(":highlighted", true, event) - } - // if (this.data.isHoverable || this.data.isSelectable){ - // } + if (this.data.isHoverable) { this.setState(":highlighted", true, event) } } pointerDown = (event: PIXI.FederatedPointerEvent) => { console.log("pointerdown", this.data.id, this.data.state) event.stopPropagation(); - // if(this.data.state === ":muted") return - if(this.data.state === ":muted") return - + if (this.data.state === ":muted") return // if (this.dragData) return - if (this.data.isSelectable) { // disable clicks - // if (!this.data.isDraggable) return - console.log("clicked", this.data.id) - this.setState(":selected", true, event) - } - if (this.data.isHoverable){ - this.artBoard.canvas.dataStore.addToHighlightedNodes(this.data) - } - if (this.data.isDraggable) { - this.onDragStart(event) - } - // if (!this.data.isDraggable) return // disable drags + if (this.data.isSelectable) { console.log("Clicked", this.data.id); this.setState(":selected", true, event) } + if (this.data.isHoverable) { this.artBoard.canvas.dataStore.addToHighlightedNodes(this.data) } + if (this.data.isDraggable) { this.onDragStart(event) } } // Function to check if the pointer is within the bounds of the parent sprite @@ -273,26 +253,22 @@ export class NodeShapeBase extends NodeShapeAbstract { pointerout = (event: PIXI.FederatedPointerEvent) => { // if (!this.data.isHoverable) return event.stopPropagation(); - if(this.data.state === ":muted") return + if (this.data.state === ":muted") return if ([":highlighted", ":selected"].includes(this.data.state) && this.isPointerInBounds(event, this.containerGfx)) return - this.setState(":default", true, event) } pointerUp = (event: PIXI.FederatedPointerEvent) => { - // if (!this.data.isSelectable) return - // const pointerPosition = event.data.global; - // console.log("pointerup", this.data.id, this.data.state) console.log("un clicked", this.data.id) event.stopPropagation(); - if(this.data.state === ":muted") return - if (this.isPointerInBounds(event, this.containerGfx)) { + if (this.data.state === ":muted") return + // if (this.isPointerInBounds(event, this.containerGfx)) { + // this.setState(":highlighted", true, event) + // } else { this.setState(":highlighted", true, event) - } else { - this.setState(":highlighted", true, event) - } - this.onDragEnd(event) + // } this.artBoard.canvas.dataStore.removeFromHighlightedNodes(this.data) + this.onDragEnd(event) } @@ -356,11 +332,11 @@ export class NodeShapeBase extends NodeShapeAbstract { this.draw(renderShape, renderLabel); } - reDrawNeighbors(){ - this.data.neighbors.links.forEach((link: CanvasLink)=>{ + reDrawNeighbors() { + this.data.neighbors.links.forEach((link: CanvasLink) => { link.gfxInstance?.redraw() }) - this.data.neighbors.nodes.forEach((node: CanvasNode)=>{ + this.data.neighbors.nodes.forEach((node: CanvasNode) => { node.gfxInstance?.redraw() }) } diff --git a/src/renderer/shapes/nodes/circle/defaults.ts b/src/renderer/shapes/nodes/circle/defaults.ts index c6f14456..55baee63 100644 --- a/src/renderer/shapes/nodes/circle/defaults.ts +++ b/src/renderer/shapes/nodes/circle/defaults.ts @@ -26,8 +26,7 @@ export const NodeStyleDefaults: INodeStyle = { weight: "bold", align: "center", style: "normal", - // wordWrap: true, - // wordWrapWidth: 200, + lineHeight: 5, } }, @@ -49,9 +48,12 @@ export const NodeStyleDefaults: INodeStyle = { text: { color: "#efefef", font: { - size: 12, - family: "Arial" - } + size: 10, + family: "Arial", + wordWrap: true, + wordWrapWidth: 300, + }, + } }, diff --git a/src/renderer/types/styles.ts b/src/renderer/types/styles.ts index 612059ca..17b529ed 100644 --- a/src/renderer/types/styles.ts +++ b/src/renderer/types/styles.ts @@ -6,7 +6,7 @@ export type IBorderStyle = 'solid' | 'dotted'; /* generic */ export interface IShapeBgStyle { color: string | number - opacity: number + opacity?: number } export interface IShapeBorderStyle { diff --git a/src/store/store.ts b/src/store/store.ts index eaf21172..b4b93a37 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -1,5 +1,5 @@ -import { FederatedPointerEvent } from "pixi.js" -import { ILinkStyle, INodeStyle, IShapeState } from "../renderer/types" +import { Dict, FederatedPointerEvent } from "pixi.js" +import { ILinkStyle, INodeShapeStyle, INodeStyle, IShapeState } from "../renderer/types" import { CanvasLink } from "./graph/links" import { CanvasNode } from "./graph/nodes" import { filterLinksOfNode } from "./graph/utils" @@ -8,6 +8,7 @@ import { IDataStoreListeners } from "./events/types" import { GraphCanvas } from "../canvas" import { deepMerge } from "../utils/merge" import stc from "string-to-color"; +import { NodeStyleDefaults } from "../renderer/shapes/nodes/circle/defaults" /** @@ -122,35 +123,31 @@ export class DataStore implements IDataStore { // if (size > style.size * 2){ // return style.size * 2 // } - return 12 - // return size + // return 12 + return size } generateNodeStyle(node: ICanvasNode) { // const nodeStyles = this.canvas.options.styles?.nodes || {}; let style: INodeStyle; + console.log("===generateNodeStyle node.id", node.id, this.canvas.options.styles) const nodeStyles = this.canvas.options.styles?.nodes || {} + // const nodeStyles = this.canvas.options.styles?.nodes || {}; + const defaultNodeStyle: INodeStyle = this.canvas.options.styles?.defaultNodeStyle || NodeStyleDefaults as INodeStyle; // console.log("====this.canvas.options.extraSettings.nodeColorBasedOn", this.canvas.options.extraSettings?.nodeColorBasedOn, node.id, node.style) // P3 - color by group - if (this.canvas.options.extraSettings?.nodeColorBasedOn === "group") { - style = deepMerge(this.canvas.options.styles?.defaultNodeStyle || {}, { shape: { background: { color: stc(node.group) } } }) - // console.log("====nodeColorBasedOn", style) - } else { - style = this.canvas.options.styles?.defaultNodeStyle - } - + style = this.canvas.options.extraSettings?.nodeColorBasedOn === "group" + ? deepMerge(defaultNodeStyle, { shape: { background: { color: stc(node.group) } } }) + : defaultNodeStyle; // P2 - style defined in the nodeStyleFromICanvasOptions ie., use defined in ICanvasOptions style = deepMerge(style, nodeStyles[node.group] || {}) - // P1 - this has the highest priority, + console.log("=====style node.id", node.id, style, nodeStyles[node.group], nodeStyles) style = deepMerge(style, node?.style || {}); - - if (this.canvas.options.extraSettings?.nodeSizeBasedOn === "degree") { const nodeSize = this.getNodeSizeBasedOnDegree(node, style); - // console.log("nodeSize", nodeSize); style = deepMerge(style, { size: nodeSize }) } diff --git a/src/stories/features/links/data/linkStates.ts b/src/stories/features/links/data/linkStates.ts deleted file mode 100644 index 1bfaad77..00000000 --- a/src/stories/features/links/data/linkStates.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { ICanvasNode, ICanvasLink } from "../../../../store"; - -export const linkStateData: { "nodes": ICanvasNode[], "links": ICanvasLink[] } = { - nodes: [{ - id: '1', - group: 'Person', - // label: 'Ravi', - - x: 100, - y: 200, - style: { size: 2 }, - }, - { - id: '2', - group: 'Project', - // label: 'Graph Canvas', - - x: 450, - y: 200, - style: { size: 2 }, - }, - - { - id: '5', - group: 'Person', - // label: 'Ravi', - - x: 100, - y: 300, - style: { size: 2 }, - }, - { - id: '6', - group: 'Project', - // label: 'Graph Canvas', - - x: 450, - y: 300, - style: { size: 2 }, - }, - { - id: '7', - group: 'Person', - // label: 'Ravi', - - x: 100, - y: 400, - style: { size: 2 }, - }, - { - id: '8', - group: 'Project', - // label: 'Graph Canvas', - - x: 450, - y: 400, - style: { size: 2 }, - } - ], - links: [ - { - id: '1-2', - group: 'authored', - label: 'default', - source: '1', - target: '2', - shapeName: 'straightLine', - }, - - { - id: '5-6', - group: 'authored', - label: 'highlighted', - source: '5', - target: '6', - shapeName: 'straightLine', - state: ":highlighted" - }, - { - id: '7-8', - group: 'authored', - label: 'muted', - source: '7', - target: '8', - shapeName: 'straightLine', - state: ":muted" - } - ] -} \ No newline at end of file diff --git a/src/stories/features/links/data/linkTypes.ts b/src/stories/features/links/data/linkTypes.ts deleted file mode 100644 index a63a8f01..00000000 --- a/src/stories/features/links/data/linkTypes.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { ICanvasLink, ICanvasNode } from "../../../../store" - -export const linkTypesData: { "nodes": ICanvasNode[], "links": ICanvasLink[] } = { - nodes: [ - - { - id: '1', - group: 'Person', - // label: 'Ravi', - - x: 100, - y: 100, - style: { size: 2 }, - }, - { - id: '2', - group: 'Project', - // label: 'Graph Canvas', - - x: 450, - y: 100, - style: { size: 2 }, - }, - { - id: '3', - group: 'Person', - // label: 'Ravi', - - x: 100, - y: 200, - style: { size: 2 }, - }, - { - id: '4', - group: 'Project', - // label: 'Graph Canvas', - - x: 450, - y: 200, - style: { size: 2 }, - }, - { - id: '5', - group: 'Person', - // label: 'Ravi', - - x: 100, - y: 300, - style: { size: 2 }, - }, - { - id: '6', - group: 'Project', - // label: 'Graph Canvas', - - x: 450, - y: 300, - style: { size: 2 }, - }, - { - id: '7', - group: 'Person', - // label: 'Ravi', - - x: 100, - y: 400, - style: { size: 2 }, - }, - { - id: '8', - group: 'Project', - // label: 'Graph Canvas', - - x: 450, - y: 400, - style: { size: 2 }, - }, - // { - // id: '5', - // group: 'Person', - // // label: 'Ravi', - // - // x: 100, - // y: 400, - // style :{size: 2}, - // }, - // { - // id: '6', - // group: 'Project', - // // label: 'Graph Canvas', - // - // x: 450, - // y: 400, - // style :{size: 2}, - // } - ], - links: [ - { - id: '1-2', - group: 'authored', - label: 'straightLine', - source: '1', - target: '2', - shapeName: 'straightLine', - }, - { - id: '3-4', - group: 'authored', - label: 'bezierCurvedLine', - source: '3', - target: '4', - shapeName: 'bezierCurvedLine', - }, - { - id: '5-6', - group: 'authored', - label: 'curvedLine', - source: '5', - target: '6', - shapeName: 'curvedLine', - }, - { - id: '7-8', - group: 'authored', - label: 'loopLine', - source: '7', - target: '8', - shapeName: 'loopLine', - } - ] -} \ No newline at end of file diff --git a/src/stories/features/links/link.stories.ts b/src/stories/features/links/link.stories.ts deleted file mode 100644 index 110f9b69..00000000 --- a/src/stories/features/links/link.stories.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/html'; -import { linkTypesData } from './data/linkTypes'; -import { createCanvas } from '../../utils/canvas'; -import { linkStateData } from './data/linkStates'; -import { notImplementedPage } from '../../utils/notImplementedPage'; - - -const meta = { - title: 'Features/Links', - render: () => createCanvas(linkTypesData.nodes, linkTypesData.links) , - parameters: { - layout: 'fullscreen', - } - -} satisfies Meta; - -export default meta; - -export const States: StoryObj = { - name : "States", - render: () => createCanvas(linkStateData.nodes, linkStateData.links) , -}; - -export const Types: StoryObj = { - name : "Types", - render: () => notImplementedPage('Types') , -}; - - -export const ParallelLinks: StoryObj = { - name : "Parallel links", - render: () => notImplementedPage('ParallelLinks') , -}; - -export const labels: StoryObj = { - name : "Styling options", - render: () => notImplementedPage('Styling') , -}; \ No newline at end of file diff --git a/src/stories/features/links/states/code.ts b/src/stories/features/links/states/code.ts new file mode 100644 index 00000000..aca301dd --- /dev/null +++ b/src/stories/features/links/states/code.ts @@ -0,0 +1,51 @@ + + + +import { GraphCanvas } from "../../../../canvas"; +import { ICanvasLink, ICanvasNode } from "../../../../store"; +import { onStoryDown } from "../../../utils/storyDown"; + +export default () => { + + const canvasDiv = document.getElementById("graphCanvas") as HTMLCanvasElement + + const canvas = new GraphCanvas({ + viewElement: canvasDiv, + debugMode: true + }); + + const nodes: ICanvasNode[] = [ + { id: "1", group: "Person", x: 100, y: 100, style: { size: 1 } }, + { id: "2", group: "Project", x: 450, y: 100, style: { size: 1 } }, + + { id: "3", group: "Project", x: 100, y: 200, style: { size: 1 } }, + { id: "4", group: "Project", x: 450, y: 200, style: { size: 1 } }, + + { id: "5", group: "Person", x: 100, y: 300, style: { size: 1 } }, + { id: "6", group: "Project", x: 450, y: 300, style: { size: 1 } }, + + { id: "7", group: "Person", x: 100, y: 400, style: { size: 1 } }, + { id: "8", group: "Project", x: 450, y: 400, style: { size: 1 } }, + + ] + + const links: ICanvasLink[] = [ + { id: "1-2", group: "authored", label: "default", source: "1", target: "2", shapeName: "straightLine" }, + { id: "3-4", group: "authored", label: "highlighted", source: "3", target: "4", shapeName: "straightLine", state: ":highlighted" }, + { id: "5-6", group: "authored", label: "selected", source: "5", target: "6", shapeName: "straightLine", state: ":selected" }, + { id: "7-8", group: "authored", label: "muted", source: "7", target: "8", shapeName: "straightLine", state: ":muted" } + ] + + + canvas.artBoard.init().then(() => { + canvas.dataStore.add(nodes, links) + canvas.artBoard.camera.fitView(); + }) + + + onStoryDown(() => { + canvas.artBoard.renderer.destroy(); + }); + +} + diff --git a/src/stories/features/links/states/index.stories.ts b/src/stories/features/links/states/index.stories.ts new file mode 100644 index 00000000..66fb248a --- /dev/null +++ b/src/stories/features/links/states/index.stories.ts @@ -0,0 +1,36 @@ +import type { Meta, StoryObj } from '@storybook/html'; +import renderTemplate from '../../../utils/render'; +import linkStatesExamplePlay from "./code" +import linkStatesExampleCode from "./code?raw" +import { notImplementedPage } from '../../../utils/notImplementedPage'; + + +const meta = { + title: 'Features/Links', +} satisfies Meta; + +export default meta; + +export const States: StoryObj = { + name: "States", + render: () => renderTemplate() , + play: linkStatesExamplePlay, + parameters: { + storySource : { + source: linkStatesExampleCode + } + } +}; + + +export const Types: StoryObj = { + name : "Types", + render: () => notImplementedPage('Link Types') , +}; + + +export const ParallelLinks: StoryObj = { + name : "Parallel links", + render: () => notImplementedPage('ParallelLinks') , +}; + diff --git a/src/stories/features/links/styling/colorByGroup.ts b/src/stories/features/links/styling/colorByGroup.ts new file mode 100644 index 00000000..b40deb13 --- /dev/null +++ b/src/stories/features/links/styling/colorByGroup.ts @@ -0,0 +1,98 @@ + + + +import { GraphCanvas } from "../../../../canvas"; +import ArtBoardToolBar from "../../../../plugins/toolbar"; +import { ICanvasLink, ICanvasNode } from "../../../../store"; +import { onStoryDown } from "../../../utils/storyDown"; + +export default () => { + + const canvasDiv = document.getElementById("graphCanvas") as HTMLCanvasElement + + const canvas = new GraphCanvas({ + viewElement: canvasDiv, + debugMode: true, + styles: { + // nodes: { + // Person: { + // // size: 10, + // shape: { + // background: { + // color: "#C0C78C" + // } + // } + // }, + // }, + links: { + follows: { + shape: { + color: "#C0C78C", + } + }, + contributing_to: { + shape: { + opacity: 0.5, + thickness: 5, + color: "#BEADFA", + }, + label: { + // background: { + // color: "transparent", + // opacity: 0.5 + // }, + padding: 3, + border: { + thickness: 0.5, + color: "#999999", + }, + text: { + color: "#ffffff", + font: { + size: 14, + family: "Arial" + } + } + } + } + } + } + }); + + const nodes: ICanvasNode[] = [ + { id: '1', group: 'Person', label: 'Person-1', x: 100, y: 100 }, + { id: '2', group: 'Person', label: 'Person-2', x: 450, y: 100 }, + { id: '3', group: 'Person', label: 'Person-3', x: 100, y: 450 }, + { id: '4', group: 'Person', label: 'Person-4', x: 450, y: 450 }, + { id: '5', group: 'Project', label: 'Project', x: (100+450)/2, y: (100+450)/2, image: 'https://icons.getbootstrap.com/assets/icons/building.svg' }, + { id: '6', group: 'Person', label: 'Person-6', x: 750, y: 450 } + ]; + + const links: ICanvasLink[] = [ + { id: '1-2', group: 'follows', label: 'follows', source: '1', target: '2' }, + { id: '2-4', group: 'follows', label: 'follows', source: '2', target: '4' }, + { id: '3-4', group: 'follows', label: 'follows', source: '3', target: '4' }, + { id: '4-6', group: 'follows', label: 'follows', source: '4', target: '6' }, + { id: '1-5', group: 'contributing_to', label: 'contributing_to', source: '1', target: '5' }, + { id: '2-5', group: 'contributing_to', label: 'contributing_to', source: '2', target: '5' }, + { id: '4-5', group: 'contributing_to', label: 'contributing_to', source: '4', target: '5' } + ]; + + + const toolbar = new ArtBoardToolBar(canvas.artBoard); + const toolBarHTMLDiv = toolbar.render() + canvasDiv.parentNode?.appendChild(toolBarHTMLDiv) + + + canvas.artBoard.init().then(() => { + canvas.dataStore.add(nodes, links) + canvas.artBoard.camera.fitView(); + }) + + + onStoryDown(() => { + canvas.artBoard.renderer.destroy(); + }); + +} + diff --git a/src/stories/features/links/styling/colorIndividualLinks.ts b/src/stories/features/links/styling/colorIndividualLinks.ts new file mode 100644 index 00000000..266d3c74 --- /dev/null +++ b/src/stories/features/links/styling/colorIndividualLinks.ts @@ -0,0 +1,50 @@ + + + +import { GraphCanvas } from "../../../../canvas"; +import { ICanvasLink, ICanvasNode } from "../../../../store"; +import { onStoryDown } from "../../../utils/storyDown"; + +export default () => { + + const canvasDiv = document.getElementById("graphCanvas") as HTMLCanvasElement + + const canvas = new GraphCanvas({ + viewElement: canvasDiv, + debugMode: true + }); + + const nodes: ICanvasNode[] = [ + { id: '1', group: 'Person', label: 'Person-1', x: 100, y: 100, image: 'https://icons.getbootstrap.com/assets/icons/person.svg' }, + { id: '2', group: 'Person', label: 'Person-2', x: 450, y: 100 }, + { id: '3', group: 'Person', label: 'Person-3', x: 100, y: 450 }, + { id: '4', group: 'Person', label: 'Person-4', x: 450, y: 450 }, + { id: '5', group: 'Project', label: 'Project', x: (100+450)/2, y: (100+450)/2, image: 'https://icons.getbootstrap.com/assets/icons/building.svg' }, + { id: '6', group: 'Person', label: 'Person-6', x: 750, y: 450 } + ]; + + const links: ICanvasLink[] = [ + { id: '1-2', group: 'follows', label: 'pink thick arrow', source: '1', target: '2', style: {shape: {color: "#ff00ff", thickness: 5}} }, + { id: '2-4', group: 'follows', label: 'follows', source: '2', target: '4', }, + { id: '3-4', group: 'follows', label: 'red large text, word wrapped label', source: '3', target: '4', + style: {label: {text: {color: "#ff0000",font: {size: 16, wordWrapWidth: 150,}}}} + }, + { id: '4-6', group: 'follows', label: 'italic, bold formatted text here', source: '4', target: '6', }, + { id: '1-5', group: 'contributing_to', label: 'contributing_to', source: '1', target: '5' }, + { id: '2-5', group: 'contributing_to', label: 'contributing_to', source: '2', target: '5' }, + { id: '4-5', group: 'contributing_to', label: 'contributing_to', source: '4', target: '5' } + ]; + + + canvas.artBoard.init().then(() => { + canvas.dataStore.add(nodes, links) + canvas.artBoard.camera.fitView(); + }) + + + onStoryDown(() => { + canvas.artBoard.renderer.destroy(); + }); + +} + diff --git a/src/stories/features/links/styling/index.stories.ts b/src/stories/features/links/styling/index.stories.ts new file mode 100644 index 00000000..7963c1f7 --- /dev/null +++ b/src/stories/features/links/styling/index.stories.ts @@ -0,0 +1,41 @@ +import type { Meta, StoryObj } from '@storybook/html'; +import renderTemplate from '../../../utils/render'; + +import colorByGroupExamplePlay from "./colorByGroup" +import colotByGroupExampleCode from "./colorByGroup?raw" + +import colorIndividualLinkExamplePlay from "./colorIndividualLinks" +import colorIndividualLinkExampleCode from "./colorIndividualLinks?raw" + + +const meta = { + title: 'Features/Links/Styling', +} satisfies Meta; + +export default meta; + +export const Types: StoryObj = { + name : "style by group", + render: () => renderTemplate() , + play: colorByGroupExamplePlay, + parameters: { + storySource : { + source: colotByGroupExampleCode + } + } +}; + + + + +export const colorIndividualLink: StoryObj = { + name : "style individual links", + render: () => renderTemplate() , + play: colorIndividualLinkExamplePlay, + parameters: { + storySource : { + source: colorIndividualLinkExampleCode + } + } +}; + diff --git a/src/stories/features/links/styling/label-formatting.ts b/src/stories/features/links/styling/label-formatting.ts new file mode 100644 index 00000000..c7527a6a --- /dev/null +++ b/src/stories/features/links/styling/label-formatting.ts @@ -0,0 +1,92 @@ +import { GraphCanvas } from "../../../../canvas"; +import { ICanvasLink, ICanvasNode } from "../../../../store"; +import { onStoryDown } from "../../../utils/storyDown"; + + +export default () => { + + const canvasDiv = document.getElementById("graphCanvas") as HTMLCanvasElement + + const canvas = new GraphCanvas({ + viewElement: canvasDiv, + debugMode: true, + styles: { + // nodes: { + // Project: { + // label: { + // text: { + // color: "#A02334" + // } + // } + // } + // } + } + }); + + + // https://www.toptal.com/designers/htmlarrows/symbols/ +// const nodes: ICanvasNode[] = [ +// {id: '1', group: 'DemoNode', label: 'simple label', x: -100, y: -100, }, +// {id: '3', group: 'DemoNode', label: '

Heading here

This is formated italic strong text.

', x: -100 , y: -300, }, +// // {id: '4', group: 'DemoNode', label: 'image:jpg', x: -100, y: -300, }, +// // {id: '5', group: 'DemoNode', label: 'icon:Unicode', x: 100, y: -100, icon: '\u2729' }, //'\uf007'} +// // {id: '6', group: 'DemoNode', label: 'icon:HTMLCode', x: 100, y: -200, icon: '%' },//'\uf007'} +// ] + +// const links: ICanvasLink[] = [] + + + const nodes: ICanvasNode[] = [ + { id: '1', group: 'Person', label: 'Person-1', x: 100, y: 100, image: 'https://icons.getbootstrap.com/assets/icons/person.svg', + style:{ + label: { + text: { + color: "#A02334" + } + } + } + }, + { id: '2', group: 'Person', label: 'Person-2', x: 450, y: 100, + style:{ + label: { + text: { + color: "#51829B" + } + } + } + + }, + { id: '3', group: 'Person', label: 'Person-3', x: 100, y: 450 }, + { id: '4', group: 'Person', label: 'Person-4', x: 450, y: 450 }, + { id: '5', group: 'Project', label: '

Invana Studio

Opensource network based visualizer. formated text text.

', + x: (100+450)/2, y: (100+450)/2, + image: 'https://icons.getbootstrap.com/assets/icons/building.svg', + }, + { id: '6', group: 'Person', label: 'Person-6', x: 750, y: 450 } + ]; + + const links: ICanvasLink[] = [ + { id: '1-2', group: 'follows', label: 'follows', source: '1', target: '2' }, + { id: '2-4', group: 'follows', label: 'follows', source: '2', target: '4' }, + { id: '3-4', group: 'follows', label: 'follows', source: '3', target: '4' }, + { id: '4-6', group: 'follows', label: 'follows', source: '4', target: '6' }, + { id: '1-5', group: 'contributing_to', label: 'contributing_to', source: '1', target: '5' }, + { id: '2-5', group: 'contributing_to', label: 'contributing_to', source: '2', target: '5' }, + { id: '4-5', group: 'contributing_to', label: 'contributing_to', source: '4', target: '5' } + ]; + + + + canvas.artBoard.init().then(() => { + + canvas.dataStore.add(nodes, links) + canvas.artBoard.camera.fitView(); + + }) + + + onStoryDown(() => { + canvas.artBoard.renderer.destroy(); + }); + +} \ No newline at end of file diff --git a/src/stories/features/nodes/styling/codes/colorByGroup.ts b/src/stories/features/nodes/styling/codes/colorByGroup.ts new file mode 100644 index 00000000..c8ba22f6 --- /dev/null +++ b/src/stories/features/nodes/styling/codes/colorByGroup.ts @@ -0,0 +1,73 @@ +import { GraphCanvas } from "../../../../../canvas"; +import ArtBoardToolBar from "../../../../../plugins/toolbar"; +import { ICanvasLink, ICanvasNode } from "../../../../../store"; +import { onStoryDown } from "../../../../utils/storyDown"; + + +export default () => { + + const canvasDiv = document.getElementById("graphCanvas") as HTMLCanvasElement + + const canvas = new GraphCanvas({ + viewElement: canvasDiv, + debugMode: true, + styles: { + nodes: { + Person: { + size: 10, + shape: { + background: { + color: "#C0C78C" + } + } + }, + Project: { + size: 30, + shape: { + background: { + color: "#BEADFA" + } + } + } + }, + + } + }); + + const nodes: ICanvasNode[] = [ + { id: '1', group: 'Person', label: 'Person-1', x: 100, y: 100 }, + { id: '2', group: 'Person', label: 'Person-2', x: 450, y: 100 }, + { id: '3', group: 'Person', label: 'Person-3', x: 100, y: 450 }, + { id: '4', group: 'Person', label: 'Person-4', x: 450, y: 450 }, + { id: '5', group: 'Project', label: 'Project', x: (100+450)/2, y: (100+450)/2, image: 'https://icons.getbootstrap.com/assets/icons/building.svg' }, + { id: '6', group: 'Person', label: 'Person-6', x: 750, y: 450 } + ]; + + const links: ICanvasLink[] = [ + { id: '1-2', group: 'follows', label: 'follows', source: '1', target: '2' }, + { id: '2-4', group: 'follows', label: 'follows', source: '2', target: '4' }, + { id: '3-4', group: 'follows', label: 'follows', source: '3', target: '4' }, + { id: '4-6', group: 'follows', label: 'follows', source: '4', target: '6' }, + { id: '1-5', group: 'contributing_to', label: 'contributing_to', source: '1', target: '5' }, + { id: '2-5', group: 'contributing_to', label: 'contributing_to', source: '2', target: '5' }, + { id: '4-5', group: 'contributing_to', label: 'contributing_to', source: '4', target: '5' } + ]; + + + const toolbar = new ArtBoardToolBar(canvas.artBoard); + const toolBarHTMLDiv = toolbar.render() + canvasDiv.parentNode?.appendChild(toolBarHTMLDiv) + + + canvas.artBoard.init().then(() => { + canvas.dataStore.add(nodes, links) + canvas.artBoard.camera.fitView(); + }) + + + onStoryDown(() => { + canvas.artBoard.renderer.destroy(); + }); + +} + diff --git a/src/stories/features/nodes/styling/codes/label-formatting.ts b/src/stories/features/nodes/styling/codes/label-formatting.ts new file mode 100644 index 00000000..8a53efe4 --- /dev/null +++ b/src/stories/features/nodes/styling/codes/label-formatting.ts @@ -0,0 +1,96 @@ +import { GraphCanvas } from "../../../../../canvas"; +import { ICanvasLink, ICanvasNode } from "../../../../../store"; +import { onStoryDown } from "../../../../utils/storyDown"; + + + +export default () => { + + const canvasDiv = document.getElementById("graphCanvas") as HTMLCanvasElement + + const canvas = new GraphCanvas({ + viewElement: canvasDiv, + debugMode: true, + styles: { + // nodes: { + // Project: { + // label: { + // text: { + // color: "#A02334" + // } + // } + // } + // } + } + }); + + + // https://www.toptal.com/designers/htmlarrows/symbols/ +// const nodes: ICanvasNode[] = [ +// {id: '1', group: 'DemoNode', label: 'simple label', x: -100, y: -100, }, +// {id: '3', group: 'DemoNode', label: '

Heading here

This is formated italic strong text.

', x: -100 , y: -300, }, +// // {id: '4', group: 'DemoNode', label: 'image:jpg', x: -100, y: -300, }, +// // {id: '5', group: 'DemoNode', label: 'icon:Unicode', x: 100, y: -100, icon: '\u2729' }, //'\uf007'} +// // {id: '6', group: 'DemoNode', label: 'icon:HTMLCode', x: 100, y: -200, icon: '%' },//'\uf007'} +// ] + +// const links: ICanvasLink[] = [] + + + const nodes: ICanvasNode[] = [ + { id: '1', group: 'Person', label: 'Person-1', x: 100, y: 100, image: 'https://icons.getbootstrap.com/assets/icons/person.svg', + style:{ + label: { + text: { + color: "#A02334" + } + } + } + }, + { id: '2', group: 'Person', label: 'blue text, formatted and word wrapped ', x: 450, y: 100, + style:{ + label: { + text: { + color: "#51829B", + font: { + wordWrapWidth: 160 + } + } + } + } + + }, + { id: '3', group: 'Person', label: 'Person-3', x: 100, y: 450 }, + { id: '4', group: 'Person', label: 'Person-4', x: 450, y: 450 }, + { id: '5', group: 'Project', label: '

Invana Studio

Opensource network based visualizer. formated text text.

', + x: (100+450)/2, y: (100+450)/2, + image: 'https://icons.getbootstrap.com/assets/icons/building.svg', + }, + { id: '6', group: 'Person', label: 'Person-6', x: 750, y: 450 } + ]; + + const links: ICanvasLink[] = [ + { id: '1-2', group: 'follows', label: 'follows', source: '1', target: '2' }, + { id: '2-4', group: 'follows', label: 'follows', source: '2', target: '4' }, + { id: '3-4', group: 'follows', label: 'follows', source: '3', target: '4' }, + { id: '4-6', group: 'follows', label: 'follows', source: '4', target: '6' }, + { id: '1-5', group: 'contributing_to', label: 'contributing_to', source: '1', target: '5' }, + { id: '2-5', group: 'contributing_to', label: 'contributing_to', source: '2', target: '5' }, + { id: '4-5', group: 'contributing_to', label: 'contributing_to', source: '4', target: '5' } + ]; + + + + canvas.artBoard.init().then(() => { + + canvas.dataStore.add(nodes, links) + canvas.artBoard.camera.fitView(); + + }) + + + onStoryDown(() => { + canvas.artBoard.renderer.destroy(); + }); + +} \ No newline at end of file diff --git a/src/stories/features/nodes/styling/codes/label-styling.ts b/src/stories/features/nodes/styling/codes/label-styling.ts deleted file mode 100644 index ecfc4459..00000000 --- a/src/stories/features/nodes/styling/codes/label-styling.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { GraphCanvas } from "../../../../../canvas"; -import { ICanvasLink, ICanvasNode } from "../../../../../store"; -import { onStoryDown } from "../../../../utils/storyDown"; - - - -export default () => { - - const canvasDiv = document.getElementById("graphCanvas") as HTMLCanvasElement - - const canvas = new GraphCanvas({ - viewElement: canvasDiv, - debugMode: true - }); - - - // https://www.toptal.com/designers/htmlarrows/symbols/ - const nodes: ICanvasNode[] = [ - {id: '1', group: 'DemoNode', label: 'simple label', x: -100, y: -100, }, - {id: '3', group: 'DemoNode', label: '

Heading here

This is formated italic strong text.

', x: -100 , y: -300, }, - // {id: '4', group: 'DemoNode', label: 'image:jpg', x: -100, y: -300, }, - // {id: '5', group: 'DemoNode', label: 'icon:Unicode', x: 100, y: -100, icon: '\u2729' }, //'\uf007'} - // {id: '6', group: 'DemoNode', label: 'icon:HTMLCode', x: 100, y: -200, icon: '%' },//'\uf007'} -] - - const links: ICanvasLink[] = [] - - canvas.artBoard.init().then(() => { - - canvas.dataStore.add(nodes, links) - canvas.artBoard.camera.fitView(); - - }) - - - onStoryDown(() => { - canvas.artBoard.renderer.destroy(); - }); - -} \ No newline at end of file diff --git a/src/stories/features/nodes/styling/codes/style-individual-nodes.ts b/src/stories/features/nodes/styling/codes/style-individual-nodes.ts new file mode 100644 index 00000000..8c4cfb79 --- /dev/null +++ b/src/stories/features/nodes/styling/codes/style-individual-nodes.ts @@ -0,0 +1,77 @@ +import { GraphCanvas } from "../../../../../canvas"; +import { ICanvasLink, ICanvasNode } from "../../../../../store"; +import { onStoryDown } from "../../../../utils/storyDown"; + + + +export default () => { + + const canvasDiv = document.getElementById("graphCanvas") as HTMLCanvasElement + + const canvas = new GraphCanvas({ + viewElement: canvasDiv, + debugMode: true, + styles: { + // nodes: { + // Project: { + // label: { + // text: { + // color: "#A02334" + // } + // } + // } + // } + } + }); + + + // https://www.toptal.com/designers/htmlarrows/symbols/ +// const nodes: ICanvasNode[] = [ +// {id: '1', group: 'DemoNode', label: 'simple label', x: -100, y: -100, }, +// {id: '3', group: 'DemoNode', label: '

Heading here

This is formated italic strong text.

', x: -100 , y: -300, }, +// // {id: '4', group: 'DemoNode', label: 'image:jpg', x: -100, y: -300, }, +// // {id: '5', group: 'DemoNode', label: 'icon:Unicode', x: 100, y: -100, icon: '\u2729' }, //'\uf007'} +// // {id: '6', group: 'DemoNode', label: 'icon:HTMLCode', x: 100, y: -200, icon: '%' },//'\uf007'} +// ] + +// const links: ICanvasLink[] = [] + + + const nodes: ICanvasNode[] = [ + { id: '1', group: 'Person', label: 'Big pink opaque Node', x: 100, y: 100, style: {size: 30, shape: {background: {color: "#ff00ff", opacity: 0.5}}, label: {text: {color :"#ff00ff"}} }}, + { id: '2', group: 'Person', label: 'Person-2', x: 450, y: 100, }, + { id: '3', group: 'Person', label: 'Person-3', x: 100, y: 450 }, + { id: '4', group: 'Person', label: 'Person-4', x: 450, y: 450 }, + { id: '5', group: 'Project', label: '

Small blue formated text here

', + x: (100+450)/2, y: (100+450)/2, + style: {size: 12, label: {text: {color :"#0000ff"}} }, + image: 'https://icons.getbootstrap.com/assets/icons/building.svg', + }, + { id: '6', group: 'Person', label: 'Person-6', x: 750, y: 450 } + ]; + + const links: ICanvasLink[] = [ + { id: '1-2', group: 'follows', label: 'follows', source: '1', target: '2' }, + { id: '2-4', group: 'follows', label: 'follows', source: '2', target: '4' }, + { id: '3-4', group: 'follows', label: 'follows', source: '3', target: '4' }, + { id: '4-6', group: 'follows', label: 'follows', source: '4', target: '6' }, + { id: '1-5', group: 'contributing_to', label: 'contributing_to', source: '1', target: '5' }, + { id: '2-5', group: 'contributing_to', label: 'contributing_to', source: '2', target: '5' }, + { id: '4-5', group: 'contributing_to', label: 'contributing_to', source: '4', target: '5' } + ]; + + + + canvas.artBoard.init().then(() => { + + canvas.dataStore.add(nodes, links) + canvas.artBoard.camera.fitView(); + + }) + + + onStoryDown(() => { + canvas.artBoard.renderer.destroy(); + }); + +} \ No newline at end of file diff --git a/src/stories/features/nodes/styling/index.stories.ts b/src/stories/features/nodes/styling/index.stories.ts index 588c0df7..f55184ff 100644 --- a/src/stories/features/nodes/styling/index.stories.ts +++ b/src/stories/features/nodes/styling/index.stories.ts @@ -1,14 +1,17 @@ import type { Meta, StoryObj } from '@storybook/html'; -import { notImplementedPage } from '../../../utils/notImplementedPage'; import renderTemplate from '../../../utils/render'; - import nodeImagesExamplePlay from "./codes/with-images-icons" import nodeImagesExampleCode from "./codes/with-images-icons?raw" +import styleIndividualNodesExamplePlay from "./codes/style-individual-nodes" +import styleIndividualNodesExampleCode from "./codes/style-individual-nodes?raw" + +import colorByGroupExamplePlay from "./codes/colorByGroup" +import colotByGroupExampleCode from "./codes/colorByGroup?raw" -import nodeLabelExamplePlay from "./codes/label-styling" -import nodeLabelExampleCode from "./codes/label-styling?raw" +import labelFormattingExamplePlay from "./codes/label-formatting" +import labelFormattingExampleCode from "./codes/label-formatting?raw" const meta = { @@ -30,13 +33,25 @@ export const WithImageAndIcon: StoryObj = { }; -export const Styling: StoryObj = { - name : "label", +export const labelFormattingByGroup: StoryObj = { + name : "label: formatting", + render: () => renderTemplate() , + play: labelFormattingExamplePlay, + parameters: { + storySource : { + source: labelFormattingExampleCode + } + } +}; + + +export const colorByGroup: StoryObj = { + name : "style by group", render: () => renderTemplate() , - play: nodeLabelExamplePlay, + play: colorByGroupExamplePlay, parameters: { storySource : { - source: nodeLabelExampleCode + source: colotByGroupExampleCode } } }; @@ -44,3 +59,13 @@ export const Styling: StoryObj = { +export const styleIndividualNodes: StoryObj = { + name : "style individual nodes", + render: () => renderTemplate() , + play: styleIndividualNodesExamplePlay, + parameters: { + storySource : { + source: styleIndividualNodesExampleCode + } + } +};