Skip to content

Instantly share code, notes, and snippets.

@Hansanghyeon
Created December 16, 2023 06:38
Show Gist options
  • Save Hansanghyeon/3ad3c0c6a7fa2b4605f50ca5748c9da4 to your computer and use it in GitHub Desktop.
Save Hansanghyeon/3ad3c0c6a7fa2b4605f50ca5748c9da4 to your computer and use it in GitHub Desktop.
simple flow (tpyescript)
import SimpleFlow from './simple-flow'
// SimpleFLow를 그릴것이다.
// SimpleFlow.drawFlow()
// // 어디다?
// const canvas = document.querySelector<HTMLElement>('#flow-canvas')
// // 어떻게?
// const { setState: setSimpleFlowSettings } = SimpleFlow.setting
// setSimpleFlowSettings({
// lineWidth: 2,
// lineSpacerWidth: 10,
// lineColour: '#000',
// canvasElm: canvas,
// })
// // 어떤것들을
// const flowElms = document.querySelectorAll<HTMLElement>('[data-flow-id]')
// // 그린다.
// SimpleFlow.createLines([...flowElms])
/* eslint-disable no-mixed-operators */
import { pipe, flow } from 'fp-ts/function'
import * as A from 'fp-ts/Array'
import * as O from 'fp-ts/Option'
import { createState } from './state'
interface TSimpleFlowSettings {
lineWidth: number
lineSpacerWidth: number
lineColour: string
canvasElm: HTMLElement
}
// BehaviorSubject 초기화
export const simpleFlowSettings = createState<TSimpleFlowSettings | null>(null)
interface TSimpleFlows {
id: string
from: HTMLElement
to: HTMLElement
path: string
}
const simpleFlows = createState<TSimpleFlows[] | null>(null)
export function createCanvasInner(paths: string[]) {
const pathsSrc = Object.values(paths).join('')
const canvasString = `
<svg class="simple-flow-line">
<defs>
<marker id="arrowhead" viewBox="0 0 10 10" refX="8" refY="5"
markerUnits="strokeWidth" markerWidth="8" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" stroke="none" fill="currentColor"/>
</marker>
</defs>
${pathsSrc}
</svg>
`
// canvasString to Element
const canvasElm = document.createElement('div')
// canvasElm의 스타일
// position absolute
// top: 0
// left: 0
// width: 100%
// height: 100%
// pointer-events: none
canvasElm.style.position = 'absolute'
canvasElm.style.top = '0'
canvasElm.style.left = '0'
canvasElm.style.width = '100%'
canvasElm.style.height = '100%'
canvasElm.style.pointerEvents = 'none'
canvasElm.innerHTML = canvasString
return canvasElm
}
export function createLine(thisElm: HTMLElement) {
const settings = simpleFlowSettings.getState()
// Assuming 'thisElm' and 'nextElm' are already defined as HTMLElements
const nextElm = document.querySelector<HTMLElement | undefined>(
`[data-flow-id="${thisElm.dataset.flowTo}"]`,
)
if (!nextElm) {
console.error(thisElm, 'nextElm is not defined')
return
}
// only continue if there's a next element.
if (nextElm.getBoundingClientRect()) {
// calculate all of the positions relative to the two objects
const thisElmRect = thisElm.getBoundingClientRect(),
nextElmRect = nextElm.getBoundingClientRect()
const thisParentPadding = thisElm.offsetHeight - thisElm.offsetHeight
const thisElmY =
thisElmRect.height / 2 + thisElm.offsetTop - thisParentPadding,
nextElmY = nextElm.offsetTop
const thisMiddle = thisElmRect.left + thisElmRect.width / 2,
nextMiddle = nextElmRect.left + nextElmRect.width / 2
const farLeftX = nextMiddle,
farRightX = thisMiddle,
lineInBetweenY = nextElmY - thisParentPadding / 2
// if the object is on the same line, the coords are different
// to if they're on separate lines.
const coords = `${thisMiddle}, ${thisElmY} ${farRightX}, ${thisElmY} ${farRightX}, ${lineInBetweenY} ${farLeftX}, ${lineInBetweenY} ${farLeftX}, ${nextElmY} ${nextMiddle}, ${nextElmY}`
console.log('🚀 ~ file: simple-flow.ts:55 ~ drawLine ~ coords:', coords)
// Assuming 'this.settings' and 'canvasElmPath' are already defined
const uniqKey = Object.keys(thisElm.dataset).filter((a) =>
Object.keys(nextElm.dataset).includes(a),
)
const path = `<path data-link="${uniqKey}" d="M ${coords}" style="fill:none;stroke: currentColor; stroke-width: ${settings.lineWidth}; stroke-linecap: round; marker-end:url(#arrowhead);" stroke-dasharray="4" />`
simpleFlows.subscribe((lines) => {
simpleFlows.setState(
pipe(
lines,
O.fromNullable,
O.fold(
() => null,
flow(
A.filter((a) => a.id !== uniqKey[0]),
A.append({
id: uniqKey[0],
from: thisElm,
to: nextElm,
path: path,
}),
),
),
),
)
})
}
}
export function drawFlow() {
// simpleFlowSettings.subscribe((settings) => {
// // settings.canvasElm 스타일을 추가해야한다.
// settings.canvasElm.style.position = 'relative'
// })
// simpleFlows.subscribe((lines) => {
// console.log('🚀 ~ file: simple-flow.ts:96 ~ drawFlow ~ lines', lines)
// })
// console.log(createCanvasInner(lines.map((a) => a.path)))
// settings.canvasElm.appendChild(createCanvasInner(lines.map((a) => a.path)))
}
function createLines(elms: HTMLElement[]) {
elms.forEach((elm) => {
createLine(elm)
})
}
const SimpleFlow = {
setting: simpleFlowSettings,
createLine,
createLines,
drawFlow,
}
export default SimpleFlow
import { BehaviorSubject } from 'rxjs'
// 상태를 생성하고 관리하는 제네릭 함수
export function createState<T>(initialValue: T | null) {
const state = new BehaviorSubject<T | null>(initialValue)
function getState() {
return state.getValue()
}
function setState(newState: T | null) {
state.next(newState)
}
function subscribe(callback: (state: T | null) => void) {
state.subscribe(callback)
}
return {
getState,
setState,
subscribe,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment