-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #251 from PrefectHQ/convert-classes-to-factories
Refactor services into factories and replace mitt with custom events factory
- Loading branch information
Showing
30 changed files
with
639 additions
and
779 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { differenceInMilliseconds, millisecondsInSecond } from 'date-fns' | ||
import { Graphics } from 'pixi.js' | ||
import { DEFAULT_TIME_COLUMN_SIZE_PIXELS } from '@/consts' | ||
import { RunGraphNode } from '@/models/RunGraph' | ||
import { waitForConfig } from '@/objects/config' | ||
|
||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type | ||
export async function nodeBoxFactory() { | ||
const config = await waitForConfig() | ||
const box = new Graphics() | ||
|
||
async function render(node: RunGraphNode): Promise<Graphics> { | ||
const { background } = config.styles.node(node) | ||
|
||
const right = node.start_time | ||
const left = node.end_time ?? new Date() | ||
const seconds = differenceInMilliseconds(left, right) / millisecondsInSecond | ||
const boxWidth = seconds * DEFAULT_TIME_COLUMN_SIZE_PIXELS | ||
const boxHeight = config.styles.nodeHeight - config.styles.nodeMargin * 2 | ||
|
||
box.clear() | ||
box.lineStyle(1, 0x0, 1, 2) | ||
box.beginFill(background) | ||
box.drawRoundedRect(0, 0, boxWidth, boxHeight, 4) | ||
box.endFill() | ||
|
||
return await box | ||
} | ||
|
||
return { | ||
box, | ||
render, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// need to use any for function arguments | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
type Handler<T = any> = (...payload: T[]) => void | ||
type Events = Record<string, unknown> | ||
|
||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type | ||
export function eventsFactory<T extends Events>() { | ||
type Event = keyof T | ||
type Handlers = Set<Handler> | ||
const all = new Map<Event, Handlers>() | ||
|
||
function on<E extends Event>(event: E, handler: Handler<T[E]>): () => void { | ||
const existing = all.get(event) | ||
|
||
if (existing) { | ||
existing.add(handler) | ||
} else { | ||
all.set(event, new Set([handler])) | ||
} | ||
|
||
return () => off(event, handler) | ||
} | ||
|
||
function once<E extends Event>(event: E, handler: Handler<T[E]>): void { | ||
const callback: Handler<T[E]> = (args) => { | ||
off(event, callback) | ||
handler(args) | ||
} | ||
|
||
on(event, callback) | ||
} | ||
|
||
function off<E extends Event>(event: E, handler: Handler<T[E]>): void { | ||
all.get(event)?.delete(handler) | ||
} | ||
|
||
function emit<E extends Event>(event: undefined extends T[E] ? E : never): void | ||
function emit<E extends Event>(event: E, payload: T[E]): void | ||
function emit<E extends Event>(event: E, payload?: T[E]): void { | ||
all.get(event)?.forEach(handler => handler(payload)) | ||
} | ||
|
||
function clear(): void { | ||
all.clear() | ||
} | ||
|
||
return { | ||
on, | ||
off, | ||
once, | ||
emit, | ||
clear, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { BitmapText, Container, Graphics } from 'pixi.js' | ||
import { DEFAULT_NODE_CONTAINER_NAME } from '@/consts' | ||
import { nodeBoxFactory } from '@/factories/box' | ||
import { nodeLabelFactory } from '@/factories/label' | ||
import { Pixels } from '@/models/layout' | ||
import { RunGraphNode } from '@/models/RunGraph' | ||
import { waitForConfig } from '@/objects/config' | ||
|
||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type | ||
export async function flowRunContainerFactory() { | ||
const container = new Container() | ||
const { label, render: renderLabel } = await nodeLabelFactory() | ||
const { box, render: renderBox } = await nodeBoxFactory() | ||
|
||
container.addChild(box) | ||
container.addChild(label) | ||
|
||
container.name = DEFAULT_NODE_CONTAINER_NAME | ||
container.eventMode = 'static' | ||
container.cursor = 'pointer' | ||
|
||
async function render(node: RunGraphNode): Promise<Container> { | ||
const label = await renderLabel(node) | ||
const box = await renderBox(node) | ||
|
||
label.position = await getLabelPosition(label, box) | ||
|
||
return container | ||
} | ||
|
||
async function getLabelPosition(label: BitmapText, box: Graphics): Promise<Pixels> { | ||
const config = await waitForConfig() | ||
|
||
// todo: this should probably be nodePadding | ||
const margin = config.styles.nodeMargin | ||
const inside = box.width > margin + label.width + margin | ||
const y = box.height / 2 - label.height | ||
|
||
if (inside) { | ||
return { | ||
x: margin, | ||
y, | ||
} | ||
} | ||
|
||
return { | ||
x: box.width + margin, | ||
y, | ||
} | ||
} | ||
|
||
return { | ||
container, | ||
render, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { BitmapText } from 'pixi.js' | ||
import { RunGraphNode } from '@/models/RunGraph' | ||
import { waitForFonts } from '@/objects/fonts' | ||
|
||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type | ||
export async function nodeLabelFactory() { | ||
const { inter } = await waitForFonts() | ||
const label = inter('', { | ||
fontSize: 12, | ||
}) | ||
|
||
async function render(node: RunGraphNode): Promise<BitmapText> { | ||
label.text = node.label | ||
|
||
return await label | ||
} | ||
|
||
return { | ||
label, | ||
render, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { Container, Ticker } from 'pixi.js' | ||
import { flowRunContainerFactory } from '@/factories/flowRun' | ||
import { taskRunContainerFactory } from '@/factories/taskRun' | ||
import { RunGraphNode } from '@/models/RunGraph' | ||
|
||
export type NodeContainerFactory = Awaited<ReturnType<typeof nodeContainerFactory>> | ||
|
||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type | ||
export async function nodeContainerFactory(node: RunGraphNode) { | ||
const { container, render: renderNode } = await getNodeFactory(node) | ||
const cacheKey: string | null = null | ||
|
||
async function render(node: RunGraphNode): Promise<Container> { | ||
const currentCacheKey = getNodeCacheKey(node) | ||
|
||
if (currentCacheKey === cacheKey) { | ||
return container | ||
} | ||
|
||
await renderNode(node) | ||
|
||
if (!node.end_time) { | ||
Ticker.shared.addOnce(() => render(node)) | ||
} | ||
|
||
return container | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type | ||
async function getNodeFactory(node: RunGraphNode) { | ||
const { kind } = node | ||
|
||
switch (kind) { | ||
case 'task-run': | ||
return await taskRunContainerFactory() | ||
case 'flow-run': | ||
return await flowRunContainerFactory() | ||
default: | ||
const exhaustive: never = kind | ||
throw new Error(`switch does not have case for value: ${exhaustive}`) | ||
} | ||
} | ||
|
||
function getNodeCacheKey(node: RunGraphNode): string { | ||
const keys = Object.keys(node).sort((keyA, keyB) => keyA.localeCompare(keyB)) as (keyof RunGraphNode)[] | ||
const values = keys.map(key => { | ||
const value = node[key] ?? new Date() | ||
|
||
return value.toString() | ||
}) | ||
|
||
return values.join(',') | ||
} | ||
|
||
return { | ||
render, | ||
container, | ||
} | ||
} |
Oops, something went wrong.