From 1b526ace4ffbbb9138c937c0ba9117ecc5a35a03 Mon Sep 17 00:00:00 2001 From: lee0jae330 Date: Thu, 5 Dec 2024 02:26:07 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=99=80=20chore:=20=EB=A1=9C=EB=94=A9?= =?UTF-8?q?=20=EC=8B=9C=20=EC=8A=A4=EC=BC=88=EB=A0=88=ED=86=A4=20UI=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EB=AC=B4=ED=95=9C=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EC=BB=A4=EC=8A=A4=ED=85=80=20=ED=9B=85?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/client/src/shared/hooks/index.ts | 1 + .../src/shared/hooks/useInfiniteScroll.ts | 29 ++++++++++++ .../WorkspaceContainer/WorkspaceContainer.tsx | 44 +++++++------------ 3 files changed, 46 insertions(+), 28 deletions(-) create mode 100644 apps/client/src/shared/hooks/useInfiniteScroll.ts diff --git a/apps/client/src/shared/hooks/index.ts b/apps/client/src/shared/hooks/index.ts index 1ce64c92..94bde057 100644 --- a/apps/client/src/shared/hooks/index.ts +++ b/apps/client/src/shared/hooks/index.ts @@ -15,3 +15,4 @@ export { useCssOptionItem } from './css/useCssOptionItem'; export { workspaceKeys } from './query-key/workspaceKeys'; export { usePreventLeaveWorkspacePage } from './usePreventLeaveWorkspacePage'; +export { useInfiniteScroll } from './useInfiniteScroll'; diff --git a/apps/client/src/shared/hooks/useInfiniteScroll.ts b/apps/client/src/shared/hooks/useInfiniteScroll.ts new file mode 100644 index 00000000..5f839454 --- /dev/null +++ b/apps/client/src/shared/hooks/useInfiniteScroll.ts @@ -0,0 +1,29 @@ +import { useEffect, useRef } from 'react'; + +type useInfiniteScrollProps = { + intersectionCallback: IntersectionObserverCallback; +}; + +export const useInfiniteScroll = ({ intersectionCallback }: useInfiniteScrollProps) => { + const targetRef = useRef(null); + + const option = { + root: null, + rootMargin: '0px', + threshold: 0.5, + }; + + useEffect(() => { + if (!targetRef.current) { + return; + } + const observer = new IntersectionObserver(intersectionCallback, option); + observer.observe(targetRef.current); + return () => { + if (targetRef.current) { + observer.unobserve(targetRef.current); + } + }; + }, [intersectionCallback]); + return targetRef; +}; diff --git a/apps/client/src/widgets/home/WorkspaceContainer/WorkspaceContainer.tsx b/apps/client/src/widgets/home/WorkspaceContainer/WorkspaceContainer.tsx index 16e6cf67..491b760a 100644 --- a/apps/client/src/widgets/home/WorkspaceContainer/WorkspaceContainer.tsx +++ b/apps/client/src/widgets/home/WorkspaceContainer/WorkspaceContainer.tsx @@ -1,9 +1,8 @@ import { EmptyWorkspace, WorkspaceGrid, WorkspaceHeader, WorkspaceList } from '@/widgets'; -import { useEffect, useRef } from 'react'; +import { useGetWorkspaceList, useInfiniteScroll } from '@/shared/hooks'; import { SkeletonWorkspaceList } from '@/shared/ui'; import { WorkspaceLoadError } from '@/entities'; -import { useGetWorkspaceList } from '@/shared/hooks'; /** * @@ -14,36 +13,25 @@ export const WorkspaceContainer = () => { const { hasNextPage, fetchNextPage, isPending, isFetchingNextPage, isError, workspaceList } = useGetWorkspaceList(); - const nextFetchTargetRef = useRef(null); - - useEffect(() => { - const options = { - root: null, - rootMargin: '0px', - threshold: 0.5, - }; - const fetchCallback: IntersectionObserverCallback = (entries, observer) => { - entries.forEach((entry) => { - if (entry.isIntersecting && hasNextPage) { - fetchNextPage(); - observer.unobserve(entry.target); - } - }); - }; - const observer = new IntersectionObserver(fetchCallback, options); - if (nextFetchTargetRef.current) { - observer.observe(nextFetchTargetRef.current); - } - return () => { - if (nextFetchTargetRef.current) { - observer.unobserve(nextFetchTargetRef.current); + const fetchCallback: IntersectionObserverCallback = (entries, observer) => { + entries.forEach((entry) => { + if (entry.isIntersecting && hasNextPage) { + fetchNextPage(); + observer.unobserve(entry.target); } - }; - }, [workspaceList]); + }); + }; + + const nextFetchTargetRef = useInfiniteScroll({ intersectionCallback: fetchCallback }); return (
+ {isPending && ( + + + + )} {isError ? ( ) : ( @@ -53,7 +41,7 @@ export const WorkspaceContainer = () => { ) : ( - {(isPending || isFetchingNextPage) && } + {isFetchingNextPage && } )) )} From 850ec26201b6a5d537fd6ce75c1630cea23aeaaf Mon Sep 17 00:00:00 2001 From: lee0jae330 Date: Thu, 5 Dec 2024 02:26:53 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=BD=94=EB=93=9C=20hi?= =?UTF-8?q?gh-lighter=20storybook=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 +-- .../CodeContent/CodeContent.stories.tsx | 44 ++++++++++ .../{ => CodeContent}/CodeContent.tsx | 10 ++- .../CodeViewer/CodeViewer.stories.tsx | 82 +++++++++++++++++++ .../{ => CodeViewer}/CodeViewer.tsx | 15 ++-- .../LineNumbers/LineNumber.stories.tsx | 22 +++++ .../{ => LineNumbers}/LineNumbers.tsx | 7 +- .../src/shared/code-highlighter/index.ts | 2 +- 8 files changed, 179 insertions(+), 15 deletions(-) create mode 100644 apps/client/src/shared/code-highlighter/components/CodeContent/CodeContent.stories.tsx rename apps/client/src/shared/code-highlighter/components/{ => CodeContent}/CodeContent.tsx (91%) create mode 100644 apps/client/src/shared/code-highlighter/components/CodeViewer/CodeViewer.stories.tsx rename apps/client/src/shared/code-highlighter/components/{ => CodeViewer}/CodeViewer.tsx (75%) create mode 100644 apps/client/src/shared/code-highlighter/components/LineNumbers/LineNumber.stories.tsx rename apps/client/src/shared/code-highlighter/components/{ => LineNumbers}/LineNumbers.tsx (86%) diff --git a/README.md b/README.md index 0bdbbf8c..a61b24d5 100644 --- a/README.md +++ b/README.md @@ -60,15 +60,14 @@

hello world!

``` -웹 개발 초보자들에게 코드를 작성하는 것은 어렵고 부담스럽게 느껴질 수 있습니다.
-하지만 BooLock에서는 블록을 조립하며 쉽고 재미있게 자신만의 웹사이트를 만들 수 있습니다.
+웹 개발 초보자들에게 코드를 작성하는 것은 어렵고 부담스럽게 느껴질 수 있습니다.
+하지만 BooLock에서는 블록을 조립하며 쉽고 재미있게 자신만의 웹사이트를 만들 수 있습니다.
-BooLock은 HTML과 CSS를 블록코딩 방식으로 학습할 수 있는 플랫폼입니다.
-코드를 몰라도 블록을 조립하는 간단한 방식으로 원하는 웹사이트를 제작하고, 이를 통해 웹 개발의 기초를 재미있게 배울 수 있습니다.
+BooLock은 HTML과 CSS를 블록코딩 방식으로 학습할 수 있는 플랫폼입니다.
+코드를 몰라도 블록을 조립하는 간단한 방식으로 원하는 웹사이트를 제작하고, 이를 통해 웹 개발의 기초를 재미있게 배울 수 있습니다.
블록들이 모여 만들어진 나만의 웹페이지를 주변 사람들과 공유해보세요. ❤️ -
# 🐥 주요기능 @@ -184,7 +183,7 @@ BooLock은 HTML과 CSS를 블록코딩 방식으로 학습할 수 있는 플랫 storybook description - Blockly의 Flyout 내부에 DOM 요소(input, button)를 동적으로 생성하여 사용자 인터페이스를 구성했습니다. 입력값은 중복 확인과 유효성 검사를 거쳐 CSS_ 접두사를 붙여 블록으로 등록되며, 즉시 Flyout에 반영됩니다. 생성된 블록은 우클릭 시 "블록 삭제" 옵션으로 관련된 모든 인스턴스를 삭제할 수 있습니다. Reset CSS 체크박스와 툴팁을 추가해 기본 스타일 초기화와 사용자 경험을 개선했습니다. + Blockly의 Flyout 내부에 DOM 요소(input, button)를 동적으로 생성하여 사용자 인터페이스를 구성했습니다. 입력값은 중복 확인과 유효성 검사를 거쳐 CSS\_ 접두사를 붙여 블록으로 등록되며, 즉시 Flyout에 반영됩니다. 생성된 블록은 우클릭 시 "블록 삭제" 옵션으로 관련된 모든 인스턴스를 삭제할 수 있습니다. Reset CSS 체크박스와 툴팁을 추가해 기본 스타일 초기화와 사용자 경험을 개선했습니다. > **👇 더 자세한 기록 및 과정 확인하기 👇**
> [Blockly 커스텀](https://www.notion.so/Blockly-14077ba9a1b680ccafcbd5b3781bf390?pvs=21) @@ -268,6 +267,7 @@ React의 SPA 구조로 인해 검색 엔진이 페이지 정보를 인식하지 > **👇 더 자세한 기록 및 과정 확인하기 👇**
> [사용자 가이드](https://www.notion.so/51679d3e288b485f9a0ea9068eb621f5?pvs=21)
### 스토리북 테스트 + [**Storybook 배포 링크**](https://boolock.site/)
diff --git a/apps/client/src/shared/code-highlighter/components/CodeContent/CodeContent.stories.tsx b/apps/client/src/shared/code-highlighter/components/CodeContent/CodeContent.stories.tsx new file mode 100644 index 00000000..54641cec --- /dev/null +++ b/apps/client/src/shared/code-highlighter/components/CodeContent/CodeContent.stories.tsx @@ -0,0 +1,44 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import { CodeContent } from './CodeContent'; + +const meta: Meta = { + title: 'shared/code-highlighter/CodeContent', + component: CodeContent, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + code: ` + + +
+

title

+

content

+
+ + `, + codeLineList: [ + '', + ' ', + ' ', + '
', + '

title

', + '

content

', + '
', + ' ', + '', + ], + selectedBlockStartLine: 5, + selectedBlockLength: 7, + selectedBlockType: 'BOOLOCK_SYSTEM_html', + }, +}; diff --git a/apps/client/src/shared/code-highlighter/components/CodeContent.tsx b/apps/client/src/shared/code-highlighter/components/CodeContent/CodeContent.tsx similarity index 91% rename from apps/client/src/shared/code-highlighter/components/CodeContent.tsx rename to apps/client/src/shared/code-highlighter/components/CodeContent/CodeContent.tsx index ded836c6..c58cd515 100644 --- a/apps/client/src/shared/code-highlighter/components/CodeContent.tsx +++ b/apps/client/src/shared/code-highlighter/components/CodeContent/CodeContent.tsx @@ -1,5 +1,6 @@ -import styles from '../styles/CodeViewer.module.css'; -import { useState, useEffect } from 'react'; +import { useEffect, useState } from 'react'; + +import styles from '../../styles/CodeViewer.module.css'; type CodeViewerProps = { code: string; @@ -9,6 +10,11 @@ type CodeViewerProps = { selectedBlockType?: string | null; }; +/** + * + * @description + * 변환된 HTML, CSS 코드를 보여주는 컴포넌트 + */ export const CodeContent = ({ code, codeLineList, diff --git a/apps/client/src/shared/code-highlighter/components/CodeViewer/CodeViewer.stories.tsx b/apps/client/src/shared/code-highlighter/components/CodeViewer/CodeViewer.stories.tsx new file mode 100644 index 00000000..2a7c1d07 --- /dev/null +++ b/apps/client/src/shared/code-highlighter/components/CodeViewer/CodeViewer.stories.tsx @@ -0,0 +1,82 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import { CodeViewer } from './CodeViewer'; + +const meta: Meta = { + title: 'shared/code-highlighter/CodeViewer', + component: CodeViewer, + parameters: { + layout: 'fullscreen', + }, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + code: ` + + +
+

title

+

content

+
+ + `, + type: 'html', + theme: 'light', + }, +}; + +export const DarkThemeHTML: Story = { + args: { + code: ` + + +
+

title

+

content

+
+ + `, + type: 'html', + theme: 'dark', + }, +}; + +export const LightThemeCss: Story = { + args: { + code: `.title { + background-color: red; + } + .content { + font-size: 16px; + font-weight: bold; + font-family: 'Arial'; + color: #000; + } + `, + type: 'css', + theme: 'light', + }, +}; + +export const DarkThemeCss: Story = { + args: { + code: `.title { + background-color: red; + } + .content { + font-size: 16px; + font-weight: bold; + font-family: 'Arial'; + color: #000; + } + `, + type: 'css', + theme: 'dark', + }, +}; diff --git a/apps/client/src/shared/code-highlighter/components/CodeViewer.tsx b/apps/client/src/shared/code-highlighter/components/CodeViewer/CodeViewer.tsx similarity index 75% rename from apps/client/src/shared/code-highlighter/components/CodeViewer.tsx rename to apps/client/src/shared/code-highlighter/components/CodeViewer/CodeViewer.tsx index e29d86b2..490635ad 100644 --- a/apps/client/src/shared/code-highlighter/components/CodeViewer.tsx +++ b/apps/client/src/shared/code-highlighter/components/CodeViewer/CodeViewer.tsx @@ -1,8 +1,8 @@ -import { CodeContent } from './CodeContent'; -import { LineNumbers } from './LineNumbers'; -import { parseHighlightCss } from '../utils/parseHighlightCss'; -import { parseHighlightHtml } from '../utils/parseHighlightHtml'; -import styles from '../styles/CodeViewer.module.css'; +import { CodeContent } from '../CodeContent/CodeContent'; +import { LineNumbers } from '../LineNumbers/LineNumbers'; +import { parseHighlightCss } from '../../utils/parseHighlightCss'; +import { parseHighlightHtml } from '../../utils/parseHighlightHtml'; +import styles from '../../styles/CodeViewer.module.css'; import { useCoachMarkStore } from '@/shared/store/useCoachMarkStore'; type CodeViewerProps = { @@ -14,6 +14,11 @@ type CodeViewerProps = { selectedBlockType?: string | null; }; +/** + * + * @description + * 변환된 HTML, CSS 코드를 줄 수와 함께 보여주는 컴포넌트 + */ export const CodeViewer = ({ code, type, diff --git a/apps/client/src/shared/code-highlighter/components/LineNumbers/LineNumber.stories.tsx b/apps/client/src/shared/code-highlighter/components/LineNumbers/LineNumber.stories.tsx new file mode 100644 index 00000000..c996d2c3 --- /dev/null +++ b/apps/client/src/shared/code-highlighter/components/LineNumbers/LineNumber.stories.tsx @@ -0,0 +1,22 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import { LineNumbers } from './LineNumbers'; + +const meta: Meta = { + title: 'shared/code-highlighter/LineNumbers', + component: LineNumbers, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + codeLineList: ['line1', 'line2', 'line3'], + }, +}; diff --git a/apps/client/src/shared/code-highlighter/components/LineNumbers.tsx b/apps/client/src/shared/code-highlighter/components/LineNumbers/LineNumbers.tsx similarity index 86% rename from apps/client/src/shared/code-highlighter/components/LineNumbers.tsx rename to apps/client/src/shared/code-highlighter/components/LineNumbers/LineNumbers.tsx index 19f0df11..69a77d28 100644 --- a/apps/client/src/shared/code-highlighter/components/LineNumbers.tsx +++ b/apps/client/src/shared/code-highlighter/components/LineNumbers/LineNumbers.tsx @@ -1,10 +1,15 @@ +import styles from '../../styles/CodeViewer.module.css'; import { useState } from 'react'; -import styles from '../styles/CodeViewer.module.css'; type LineNumbersProps = { codeLineList: string[]; }; +/** + * + * @description + * 코드의 줄 수를 표시하는 컴포넌트 + */ export const LineNumbers = ({ codeLineList }: LineNumbersProps) => { const [hoveredLineNumber, setHoveredLineNumber] = useState(null); diff --git a/apps/client/src/shared/code-highlighter/index.ts b/apps/client/src/shared/code-highlighter/index.ts index 96758797..45187a73 100644 --- a/apps/client/src/shared/code-highlighter/index.ts +++ b/apps/client/src/shared/code-highlighter/index.ts @@ -1 +1 @@ -export { CodeViewer } from './components/CodeViewer'; +export { CodeViewer } from './components/CodeViewer/CodeViewer'; From 97e7eafe9cda314041d353bab4d90bad37743cde Mon Sep 17 00:00:00 2001 From: lee0jae330 Date: Thu, 5 Dec 2024 02:43:52 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=99=80=20chore:=20let=20->=20const?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/client/src/shared/hooks/usePreventLeaveWorkspacePage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/shared/hooks/usePreventLeaveWorkspacePage.ts b/apps/client/src/shared/hooks/usePreventLeaveWorkspacePage.ts index 34c4bb2c..de4c4adf 100644 --- a/apps/client/src/shared/hooks/usePreventLeaveWorkspacePage.ts +++ b/apps/client/src/shared/hooks/usePreventLeaveWorkspacePage.ts @@ -4,7 +4,7 @@ import { useWorkspaceChangeStatusStore } from '@/shared/store'; export const usePreventLeaveWorkspacePage = () => { const { isBlockChanged, isCssChanged } = useWorkspaceChangeStatusStore(); - let blocker = useBlocker( + const blocker = useBlocker( ({ currentLocation, nextLocation }) => currentLocation.pathname !== nextLocation.pathname && (isBlockChanged || isCssChanged) );