-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* chore: konva, react-konva 설치 * feat: space 페이지 및 space node 초안 작성 * fix: space 텍스트 중앙 앵커 처리 개선 * test: space node에 대한 storybook 작성 * fix: space node 내 텍스트가 항상 중앙 정렬되도록 개선 - 텍스트 변화 시 중앙정렬이 다시 되지 않는 문제 수정 * test: storybook에서 konva node를 감싸는 Stage 크기 반응형으로 개선
- Loading branch information
Showing
8 changed files
with
259 additions
and
46 deletions.
There are no files selected for viewing
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 |
---|---|---|
@@ -1,42 +1,13 @@ | ||
#root { | ||
max-width: 1280px; | ||
margin: 0 auto; | ||
padding: 2rem; | ||
text-align: center; | ||
} | ||
|
||
.logo { | ||
height: 6em; | ||
padding: 1.5em; | ||
will-change: filter; | ||
transition: filter 300ms; | ||
} | ||
.logo:hover { | ||
filter: drop-shadow(0 0 2em #646cffaa); | ||
} | ||
.logo.react:hover { | ||
filter: drop-shadow(0 0 2em #61dafbaa); | ||
} | ||
|
||
@keyframes logo-spin { | ||
from { | ||
transform: rotate(0deg); | ||
} | ||
to { | ||
transform: rotate(360deg); | ||
} | ||
} | ||
|
||
@media (prefers-reduced-motion: no-preference) { | ||
a:nth-of-type(2) .logo { | ||
animation: logo-spin infinite 20s linear; | ||
} | ||
html, | ||
body { | ||
width: 100%; | ||
height: 100%; | ||
padding: 0; | ||
margin: 0; | ||
} | ||
|
||
.card { | ||
padding: 2em; | ||
} | ||
|
||
.read-the-docs { | ||
color: #888; | ||
#root { | ||
width: 100%; | ||
min-height: 100%; | ||
height: 100%; | ||
} |
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
50 changes: 50 additions & 0 deletions
50
packages/frontend/src/components/space/SpaceNode.stories.tsx
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,50 @@ | ||
import { useEffect, useState } from "react"; | ||
import { Layer, Stage } from "react-konva"; | ||
|
||
import type { Meta, StoryObj } from "@storybook/react"; | ||
|
||
import SpaceNode from "./SpaceNode.tsx"; | ||
|
||
export default { | ||
component: SpaceNode, | ||
tags: ["autodocs"], | ||
decorators: [ | ||
(Story, { canvasElement }) => { | ||
// TODO: Konva Node를 위한 decorator 별도로 분리 必 | ||
const [size, setSize] = useState(() => ({ | ||
width: Math.max(canvasElement.clientWidth, 256), | ||
height: Math.max(canvasElement.clientHeight, 256), | ||
})); | ||
|
||
const { width, height } = size; | ||
|
||
useEffect(() => { | ||
const observer = new ResizeObserver((entries) => { | ||
entries.forEach((entry) => { | ||
const { width, height } = entry.contentRect; | ||
setSize({ width, height }); | ||
}); | ||
}); | ||
|
||
observer.observe(canvasElement); | ||
return () => observer.unobserve(canvasElement); | ||
}, [canvasElement]); | ||
|
||
return ( | ||
<Stage width={width} height={height} draggable> | ||
<Layer offsetX={-width / 2} offsetY={-height / 2}> | ||
<Story /> | ||
</Layer> | ||
</Stage> | ||
); | ||
}, | ||
], | ||
} satisfies Meta<typeof SpaceNode>; | ||
|
||
export const Normal: StoryObj = { | ||
args: { | ||
label: "HelloWorld", | ||
x: 0, | ||
y: 0, | ||
}, | ||
}; |
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,51 @@ | ||
import { forwardRef, useEffect, useRef, useState } from "react"; | ||
import { Circle, Group, Text } from "react-konva"; | ||
|
||
import Konva from "konva"; | ||
|
||
// FIXME: 이런 동작이 많이 필요할 것 같아 별도의 파일로 분리할 것 | ||
function TextWithCenteredAnchor(props: Konva.TextConfig) { | ||
const ref = useRef<Konva.Text>(null); | ||
|
||
const [offsetX, setOffsetX] = useState<number | undefined>(undefined); | ||
const [offsetY, setOffsetY] = useState<number | undefined>(undefined); | ||
|
||
useEffect(() => { | ||
if (!ref.current || props.offset !== undefined) { | ||
return; | ||
} | ||
|
||
if (props.offsetX === undefined) { | ||
setOffsetX(ref.current.width() / 2); | ||
} | ||
|
||
if (props.offsetY === undefined) { | ||
setOffsetY(ref.current.height() / 2); | ||
} | ||
}, [props]); | ||
|
||
return <Text ref={ref} offsetX={offsetX} offsetY={offsetY} {...props} />; | ||
} | ||
|
||
export interface SpaceNodeProps { | ||
label?: string; | ||
x: number; | ||
y: number; | ||
} | ||
const SpaceNode = forwardRef<Konva.Group, SpaceNodeProps>( | ||
({ label, x, y }, ref) => { | ||
// TODO: 색상에 대해 정하기, 크기에 대해 정하기 | ||
const fillColor = "royalblue"; | ||
const textColor = "white"; | ||
|
||
return ( | ||
<Group ref={ref} x={x} y={y}> | ||
<Circle x={0} y={0} radius={48} fill={fillColor} /> | ||
<TextWithCenteredAnchor x={0} y={0} text={label} fill={textColor} /> | ||
</Group> | ||
); | ||
}, | ||
); | ||
SpaceNode.displayName = "SpaceNode"; | ||
|
||
export default SpaceNode; |
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,49 @@ | ||
import React, { useEffect, useState } from "react"; | ||
import { Layer, Stage } from "react-konva"; | ||
|
||
import SpaceNode from "./SpaceNode.tsx"; | ||
|
||
interface SpaceViewProps { | ||
autofitTo?: Element | React.RefObject<Element>; | ||
} | ||
|
||
export default function SpaceView({ autofitTo }: SpaceViewProps) { | ||
const [stageSize, setStageSize] = useState({ width: 0, height: 0 }); | ||
|
||
useEffect(() => { | ||
if (!autofitTo) { | ||
return undefined; | ||
} | ||
|
||
const containerRef = | ||
"current" in autofitTo ? autofitTo : { current: autofitTo }; | ||
|
||
function resizeStage() { | ||
const container = containerRef.current; | ||
|
||
if (!container) { | ||
return; | ||
} | ||
|
||
const width = container.clientWidth; | ||
const height = container.clientHeight; | ||
|
||
setStageSize({ width, height }); | ||
} | ||
|
||
resizeStage(); | ||
|
||
window.addEventListener("resize", resizeStage); | ||
return () => { | ||
window.removeEventListener("resize", resizeStage); | ||
}; | ||
}, [autofitTo]); | ||
|
||
return ( | ||
<Stage width={stageSize.width} height={stageSize.height} draggable> | ||
<Layer offsetX={-stageSize.width / 2} offsetY={-stageSize.height / 2}> | ||
<SpaceNode label="HEAD NODE" x={0} y={0} /> | ||
</Layer> | ||
</Stage> | ||
); | ||
} |
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,28 @@ | ||
import { useRef } from "react"; | ||
import { useParams } from "react-router-dom"; | ||
|
||
import SpaceView from "@/components/space/SpaceView.tsx"; | ||
|
||
interface SpacePageParams extends Record<string, string | undefined> { | ||
entrySpaceId?: string; | ||
} | ||
|
||
export default function SpacePage() { | ||
const { entrySpaceId } = useParams<SpacePageParams>(); | ||
|
||
if (!entrySpaceId) { | ||
throw new Error(""); | ||
} | ||
|
||
const containerRef = useRef<HTMLDivElement>(null); | ||
|
||
return ( | ||
<div | ||
className="text-sm" | ||
style={{ width: "100%", height: "100%" }} | ||
ref={containerRef} | ||
> | ||
<SpaceView autofitTo={containerRef} /> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.