Created
December 16, 2023 06:38
-
-
Save Hansanghyeon/3ad3c0c6a7fa2b4605f50ca5748c9da4 to your computer and use it in GitHub Desktop.
simple flow (tpyescript)
This file contains hidden or 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
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]) |
This file contains hidden or 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
/* 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 |
This file contains hidden or 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
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