Skip to content

Commit

Permalink
Merge pull request #273 from PrefectHQ/borders
Browse files Browse the repository at this point in the history
Add border to sub node toggle when positioned outside of the node
  • Loading branch information
pleek91 authored Oct 26, 2023
2 parents 291fb7c + 76279e1 commit e0191c0
Show file tree
Hide file tree
Showing 11 changed files with 254 additions and 95 deletions.
22 changes: 0 additions & 22 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
"eslint": "8.52.0",
"postcss": "8.4.31",
"tailwindcss": "3.3.3",
"ts-essentials": "^9.4.1",
"tsc-alias": "1.8.8",
"typescript": "5.2.2",
"vite": "4.5.0",
Expand Down
5 changes: 3 additions & 2 deletions src/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export const DEFAULT_TIME_COLUMN_SPAN_SECONDS = 1
export const DEFAULT_TIME_COLUMN_SIZE_PIXELS = 20
export const DEFAULT_LINEAR_COLUMN_SIZE_PIXELS = 200
export const DEFAULT_LABEL_CULLING_THRESHOLD = 0.2
export const DEFAULT_EDGE_MINIMUM_BEZIER = 64
export const DEFAULT_EDGE_POINTS = 20
export const DEFAULT_TEXTURE_RESOLUTION = 10
export const DEFAULT_EDGE_POINTS = 20
export const DEFAULT_EDGE_MINIMUM_BEZIER = 64
40 changes: 11 additions & 29 deletions src/factories/arrow.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import { useSubscription } from '@prefecthq/vue-compositions'
import { Graphics, Rectangle, Sprite, Texture } from 'pixi.js'
import { waitForApplication } from '@/objects'

export type ArrowStyle = {
size: number,
radius?: number,
stroke?: number,
rotate?: number,
}
import { Sprite } from 'pixi.js'
import { CornerStyle, getCornerTexture } from '@/textures/corner'

export enum ArrowDirection {
Up = 0,
Expand All @@ -16,33 +8,23 @@ export enum ArrowDirection {
Right = 90
}

async function getArrowTexture({ size, stroke = 1, radius = 0 }: ArrowStyle): Promise<Texture> {
const application = await waitForApplication()

const graphic = new Graphics()
graphic.lineStyle(stroke, '#fff', 1, 0)
graphic.drawRoundedRect(0, 0, size * 2, size * 2, radius)

const arrow = application.renderer.generateTexture(graphic, {
// drew a rounded rectangle and then just using one corner as the "arrow"
region: new Rectangle(0, 0, size, size),

// manually bumping up the resolution to keep the border radius from being blurry
resolution: 10,
})

return arrow
export type ArrowStyle = CornerStyle & {
rotate?: number,
}

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function arrowFactory() {
const arrow = new Sprite()

async function render(style: ArrowStyle): Promise<Sprite> {
const texture = (await useSubscription(getArrowTexture, [style]).promise()).response
arrow.texture = texture

const { rotate = 0 } = style
const cornerStyles: CornerStyle = {
size: style.size,
radius: style.radius,
stroke: style.stroke,
}
const texture = await getCornerTexture(cornerStyles)
arrow.texture = texture

arrow.anchor.set(0.5, 0.5)
// texture is the corner of a rectangle so 45 deg defaults the arrow to pointing up
Expand Down
153 changes: 153 additions & 0 deletions src/factories/border.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { ColorSource, Container, Sprite, Texture } from 'pixi.js'
import { CornerStyle, getCornerTexture } from '@/textures/corner'
import { getPixelTexture } from '@/textures/pixel'

export type BorderStyle = {
width: number,
height: number,
stroke: number,
radius?: number,
color?: ColorSource,
}

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function borderFactory() {
const container = new Container()
const topLeft = new Sprite()
const topRight = new Sprite()
const bottomLeft = new Sprite()
const bottomRight = new Sprite()
const left = new Sprite()
const right = new Sprite()
const top = new Sprite()
const bottom = new Sprite()

topLeft.name = 'border-corner-top-left'
topRight.name = 'border-corner-top-right'
bottomLeft.name = 'border-corner-bottom-left'
bottomRight.name = 'border-corner-bottom-right'
left.name = 'border-corner-left'
right.name = 'border-corner-right'
top.name = 'border-corner-top'
bottom.name = 'border-corner-bottom'

topRight.anchor.x = 1
topRight.scale.x = -1
bottomLeft.anchor.y = 1
bottomLeft.scale.y = -1
bottomRight.anchor.x = 1
bottomRight.scale.x = -1
bottomRight.anchor.y = 1
bottomRight.scale.y = -1

container.addChild(topLeft)
container.addChild(topRight)
container.addChild(bottomLeft)
container.addChild(bottomRight)
container.addChild(left)
container.addChild(right)
container.addChild(top)
container.addChild(bottom)

async function render(style: BorderStyle): Promise<Container> {
const { radius = 0, color = '#fff', stroke, width, height } = style
const size = radius * 2

const cornerStyle: CornerStyle = {
size,
radius,
stroke,
}

const corner = await getCornerTexture(cornerStyle)
const pixel = await getPixelTexture()

updateCorners({
texture: corner,
width,
height,
size,
})

updateBorders({
texture: pixel,
width,
height,
size,
stroke,
})

setTint(color)

return container
}

type UpdateCorners = {
texture: Texture,
width: number,
height: number,
size: number,
}

function updateCorners({ texture, width, height, size }: UpdateCorners): void {
topLeft.texture = texture
topRight.texture = texture
bottomLeft.texture = texture
bottomRight.texture = texture

topLeft.position.set(0, 0)
topRight.position.set(width - size, 0)
bottomLeft.position.set(0, height - size)
bottomRight.position.set(width - size, height - size)
}

type UpdateBorders = {
texture: Texture,
width: number,
height: number,
size: number,
stroke: number,
}

function updateBorders({ texture, size, width, height, stroke }: UpdateBorders): void {
const sidesHeight = height - size * 2
const topAndBottomWidth = width - size * 2

top.texture = texture
left.texture = texture
right.texture = texture
bottom.texture = texture

left.position.set(0, size)
left.height = sidesHeight
left.width = stroke

right.position.set(width - stroke, size)
right.height = sidesHeight
right.width = stroke

top.position.set(size, 0)
top.width = topAndBottomWidth
top.height = stroke

bottom.position.set(size, height - stroke)
bottom.width = topAndBottomWidth
bottom.height = stroke
}

function setTint(color: ColorSource): void {
topLeft.tint = color
topRight.tint = color
bottomLeft.tint = color
bottomRight.tint = color
top.tint = color
left.tint = color
right.tint = color
bottom.tint = color
}

return {
border: container,
render,
}
}
48 changes: 40 additions & 8 deletions src/factories/nodeArrowButton.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,65 @@
import { Container, ColorMatrixFilter } from 'pixi.js'
import { ArrowDirection, ArrowStyle, arrowFactory } from '@/factories/arrow'
import { BarStyle, barFactory } from '@/factories/bar'
import { Container, ColorMatrixFilter, ColorSource } from 'pixi.js'
import { ArrowDirection, arrowFactory } from '@/factories/arrow'
import { barFactory } from '@/factories/bar'
import { borderFactory } from '@/factories/border'
import { waitForConfig } from '@/objects/config'

type NodeArrowBarStyles = {
arrow: Omit<ArrowStyle, 'rotate'>,
button: BarStyle,
inside: boolean,
background: ColorSource,
isOpen: boolean,
}

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export async function nodeArrowButtonFactory() {
const config = await waitForConfig()
const container = new Container()
const { arrow, render: renderArrow } = await arrowFactory()
const { bar, render: renderBar } = await barFactory()
const { border, render: renderBorder } = await borderFactory()
const filter = new ColorMatrixFilter()

container.eventMode = 'static'
container.cursor = 'pointer'
container.addChild(bar)
container.addChild(arrow)
container.addChild(border)

container.on('mouseover', onMouseover)
container.on('mouseout', onMouseout)

bar.filters = [filter]

async function render({ arrow: arrowStyles, button: buttonStyles, isOpen }: NodeArrowBarStyles): Promise<Container> {
const rotate = isOpen ? ArrowDirection.Up : ArrowDirection.Down
const arrow = await renderArrow({ ...arrowStyles, rotate })
border.visible = false

async function render({ inside, isOpen, background }: NodeArrowBarStyles): Promise<Container> {
const arrowStyles = {
size: 10,
stroke: 2,
rotate: isOpen ? ArrowDirection.Up : ArrowDirection.Down,
}

const arrow = await renderArrow(arrowStyles)

const buttonStyles = {
width: config.styles.nodeToggleSize,
height: config.styles.nodeToggleSize,
background: inside ? background : '#333',
radius: config.styles.nodeToggleBorderRadius,
}

const bar = await renderBar(buttonStyles)

const border = await renderBorder({
width: buttonStyles.width,
height: buttonStyles.height,
radius: buttonStyles.radius,
stroke: 1,
color: config.styles.nodeToggleBorderColor,
})

border.alpha = inside ? 0 : 1

const middle = {
y: bar.height / 2,
x: bar.width / 2,
Expand All @@ -45,10 +75,12 @@ export async function nodeArrowButtonFactory() {

function onMouseover(): void {
filter.brightness(0.5, false)
border.visible = true
}

function onMouseout(): void {
filter.brightness(1, false)
border.visible = false
}

return {
Expand Down
Loading

0 comments on commit e0191c0

Please sign in to comment.