Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import useEditorStore from "store/useEditorStore";
* 클릭 시 왼쪽 정렬과 가운데 정렬을 토글
*/
export default function AlignIcon() {
const { editor, align, toggleAlign } = useEditorStore();
const editor = useEditorStore((state) => state.editor);
const align = useEditorStore((state) => state.align);
const toggleAlign = useEditorStore((state) => state.toggleAlign);

/**
* 텍스트 정렬 상태를 토글하고 에디터의 블록들을 업데이트하는 함수
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface EmojiIconProps {
* 클릭 시 이모지 선택 모달을 열고 블록 인덱스를 업데이트
*/
export default function EmojiIcon({ handleBlockIndex }: EmojiIconProps) {
const { toggleModal } = useEditorStore();
const toggleModal = useEditorStore((state) => state.toggleModal);

const handleIconClick = () => {
handleBlockIndex();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface LineIconProps {
* 클릭 시 선 그리기 모달을 열고 블록 인덱스를 업데이트
*/
export default function LineIcon({ handleBlockIndex }: LineIconProps) {
const { toggleModal } = useEditorStore();
const toggleModal = useEditorStore((state) => state.toggleModal);

const handleIconClick = () => {
handleBlockIndex();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface PlaceIconProps {
* 클릭 시 장소 선택 모달을 열고 블록 인덱스를 업데이트
*/
export default function PlaceIcon({ handleBlockIndex }: PlaceIconProps) {
const { toggleModal } = useEditorStore();
const toggleModal = useEditorStore((state) => state.toggleModal);

const handleIconClick = () => {
handleBlockIndex();
Expand Down
2 changes: 1 addition & 1 deletion src/components/Editor/EditorContent/EditorContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import useEditorStore from "@/store/useEditorStore";
* 드래그&드롭, 실행취소 기능을 포함한 에디터 인스턴스를 생성
*/
const EditorContent = memo(() => {
const { setEditor } = useEditorStore();
const setEditor = useEditorStore((state) => state.setEditor);
const editorInstanceRef = useRef<EditorJS | null>(null);

useEffect(() => {
Expand Down
31 changes: 2 additions & 29 deletions src/components/Editor/EditorSection/EditorSection.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { useRef } from "react";
import * as S from "./EditorSection.style";
import EditorToolbar from "@/components/Editor/EditorToolbar/EditorToolbar";
import EditorContent from "@/components/Editor/EditorContent/EditorContent";
Expand All @@ -9,38 +9,11 @@ import EditorContent from "@/components/Editor/EditorContent/EditorContent";
*/
export default function EditorSection() {
const editorSectionRef = useRef<HTMLDivElement>(null);
const [toolbarTop, setToolbarTop] = useState(487);

useEffect(() => {
/**
* 스크롤 이벤트 핸들러
* 에디터 섹션의 위치에 따라 툴바의 위치를 동적으로 조정
*/
const handleScroll = () => {
if (editorSectionRef.current) {
const rect = editorSectionRef.current.getBoundingClientRect();

if (rect.top > 0) {
setToolbarTop(rect.top + 40);
}

if (rect.top <= 0) {
setToolbarTop(40);
}
}
};

window.addEventListener("scroll", handleScroll);

return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);

return (
<S.EditorSectionContainer ref={editorSectionRef}>
<EditorContent />
<EditorToolbar toolbarTop={toolbarTop} />
<EditorToolbar editorSectionRef={editorSectionRef} />
</S.EditorSectionContainer>
);
}
5 changes: 4 additions & 1 deletion src/components/Editor/EditorToolModal/EditorToolModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ interface Props {
* 이모지, 구분선, 장소 등의 추가 옵션을 제공하는 모달 창을 관리
*/
export default function EditorToolModal({ top, children }: Props) {
const { activeModal, closeModal } = useEditorStore();
const activeModal = useEditorStore((state) => state.activeModal);
const closeModal = useEditorStore((state) => state.closeModal);

/**
* 모달 외부 클릭 감지 훅 사용
Expand All @@ -29,6 +30,8 @@ export default function EditorToolModal({ top, children }: Props) {
}
);

if (!activeModal) return null;

return (
<S.EditorToolModalContainer $top={top} ref={$ref}>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export default function EmojiModal({ addBlock }: EmojiIconProps) {
const [page, setPage] = useState(0);
const ITEMS_PER_PAGE = 150;

const { closeModal } = useEditorStore();
const activeModal = useEditorStore((state) => state.activeModal);
const closeModal = useEditorStore((state) => state.closeModal);

const handleEmojiClick = (emojiData: Emoji) => {
addBlock("emoji", {
Expand All @@ -36,6 +37,8 @@ export default function EmojiModal({ addBlock }: EmojiIconProps) {
(page + 1) * ITEMS_PER_PAGE
);

if (activeModal !== "emoji") return null;

return (
<S.EmojiModalWrapper>
<S.EmojiListWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ interface LineIconProps {
* 다양한 스타일의 구분선을 제공하고 선택한 구분선을 에디터에 추가
*/
export default function LineModal({ addBlock }: LineIconProps) {
const { closeModal } = useEditorStore();
const activeModal = useEditorStore((state) => state.activeModal);
const closeModal = useEditorStore((state) => state.closeModal);

const handleLineClick = (line: Line) => {
const lineData = {
Expand All @@ -23,6 +24,8 @@ export default function LineModal({ addBlock }: LineIconProps) {
closeModal();
};

if (activeModal !== "line") return null;

return (
<S.LineModalWrapper>
{LineData.map((line, index) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export default function PlaceModal({ addBlock }: PlaceIconProps) {
{ name: string; id: string; address: string; url: string }[]
>([]);

const { closeModal } = useEditorStore();
const activeModal = useEditorStore((state) => state.activeModal);
const closeModal = useEditorStore((state) => state.closeModal);

/**
* 장소 검색 처리 함수
Expand Down Expand Up @@ -63,6 +64,8 @@ export default function PlaceModal({ addBlock }: PlaceIconProps) {
handleSearch(searchTerm);
}, [searchTerm]);

if (activeModal !== "place") return null;

return (
<S.PlaceModalWrapper>
<S.PlaceInputWrapper>
Expand Down
53 changes: 42 additions & 11 deletions src/components/Editor/EditorToolbar/EditorToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,24 @@ import {

import FixedToolbar from "@/components/Common/FixedToolbar/FixedToolbar";
import useEditorStore from "@/store/useEditorStore";
import { RefObject, useEffect, useState } from "react";

interface Props {
toolbarTop: number;
editorSectionRef: RefObject<HTMLDivElement>;
}

/**
* 에디터의 툴바 컴포넌트
* 블록을 추가할 수 있는 도구들을 제공
*/
export default function EditorToolbar({ toolbarTop }: Props) {
const { editor, activeModal, currentBlockIndex, setCurrentBlockIndex } =
useEditorStore();
export default function EditorToolbar({ editorSectionRef }: Props) {
const [toolbarTop, setToolbarTop] = useState(487);

const editor = useEditorStore((state) => state.editor);
const currentBlockIndex = useEditorStore((state) => state.currentBlockIndex);
const setCurrentBlockIndex = useEditorStore(
(state) => state.setCurrentBlockIndex
);

/**
* 현재 블록의 인덱스를 처리하는 함수
Expand All @@ -53,6 +59,7 @@ export default function EditorToolbar({ toolbarTop }: Props) {
const shouldUseFirstBlock =
firstBlock?.name === "paragraph" && firstBlock.isEmpty;
setCurrentBlockIndex(shouldUseFirstBlock ? 0 : 1);

return;
}

Expand Down Expand Up @@ -80,15 +87,39 @@ export default function EditorToolbar({ toolbarTop }: Props) {
}
};

useEffect(() => {
/**
* 스크롤 이벤트 핸들러
* 에디터 섹션의 위치에 따라 툴바의 위치를 동적으로 조정
*/
const handleScroll = () => {
if (editorSectionRef.current) {
const rect = editorSectionRef.current.getBoundingClientRect();

if (rect.top > 0) {
setToolbarTop(rect.top + 40);
}

if (rect.top <= 0) {
setToolbarTop(40);
}
}
};

window.addEventListener("scroll", handleScroll);

return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);

return (
<>
{activeModal && (
<EditorToolModal top={toolbarTop}>
{activeModal === "place" && <PlaceModal addBlock={addBlock} />}
{activeModal === "emoji" && <EmojiModal addBlock={addBlock} />}
{activeModal === "line" && <LineModal addBlock={addBlock} />}
</EditorToolModal>
)}
<EditorToolModal top={toolbarTop}>
<PlaceModal addBlock={addBlock} />
<EmojiModal addBlock={addBlock} />
<LineModal addBlock={addBlock} />
</EditorToolModal>
<FixedToolbar position={{ top: toolbarTop, right: 15 }}>
<ImageIcon handleBlockIndex={handleBlockIndex} addBlock={addBlock} />
<GroupImageIcon
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import useTitleStore from "@/store/useTitleStore";
*/
export default function SubtitleTextInput() {
const setSubtitleText = useTitleStore((state) => state.setSubtitleText);
const titleImage = useTitleStore((state) => state.titleCoverImage);
const titleCoverColor = useTitleStore((state) => state.titleCoverColor);
const hasTitleBackground = useTitleStore((state) => state.hasTitleBackground);

return (
<ContentEditable
Expand All @@ -20,15 +19,17 @@ export default function SubtitleTextInput() {
fontWeight={300}
onChange={setSubtitleText}
fontColor={
titleImage || titleCoverColor
hasTitleBackground
? COMMON_THEME.white_primary
: COMMON_THEME.black_primary
}
placeholderColor={
titleCoverColor ? COMMON_THEME.white_primary : COMMON_THEME.gray_primary
hasTitleBackground
? COMMON_THEME.white_primary
: COMMON_THEME.gray_primary
}
cursorColor={
titleImage || titleCoverColor
hasTitleBackground
? COMMON_THEME.white_primary
: COMMON_THEME.black_primary
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import styled from "styled-components";

export const TitleInputWrapper = styled.div<{
$align: "bottom" | "center";
$hasBackground: boolean;
}>`
display: flex;
flex-direction: column;
gap: 16px;
position: absolute;
left: 0;
width: 100%;
z-index: 100;
transition: all ease-in-out 0.2s;

${({ $align, $hasBackground }) =>
$align === "bottom" &&
!$hasBackground &&
`
bottom: 50px;


`}

${({ $align, $hasBackground }) =>
$align === "bottom" &&
$hasBackground &&
`
bottom: 70px;


`}

${({ $align }) =>
$align === "center" &&
`
bottom: 50%;
text-align: center;

`}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ReactNode } from "react";
import * as S from "./TitleInputContainer.style";
import useTitleStore from "@/store/useTitleStore";

function TitleInputContainer({ children }: { children: ReactNode }) {
const hasTitleBackground = useTitleStore((state) => state.hasTitleBackground);

const titleAlign = useTitleStore((state) => state.alignment);

return (
<S.TitleInputWrapper
$align={titleAlign}
$hasBackground={hasTitleBackground}
>
{children}
</S.TitleInputWrapper>
);
}

export default TitleInputContainer;
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
import * as S from "./TitleInputWrapper.style";
import TitleTextInput from "@/components/Title/TitleInput/TitleTextInput/TitleTextInput";
import SubtitleTextInput from "@/components/Title/TitleInput/SubtitleTextInput/SubtitleTextInput";

import useTitleStore from "@/store/useTitleStore";
import TitleInputContainer from "@/components/Title/TitleInput/TitleInputWrapper/TitleInputContainer/TitleInputContainer";

/**
* 제목과 부제목 입력 필드를 감싸는 컨테이너 컴포넌트
* 배경 유무와 정렬 상태에 따라 스타일이 변경됨
*/
export default function TitleInputWrapper() {
const titleImage = useTitleStore((state) => state.titleCoverImage);
const titleCoverColor = useTitleStore((state) => state.titleCoverColor);
const titleAlign = useTitleStore((state) => state.alignment);

return (
<S.TitleInputWrapper
$align={titleAlign}
$hasBackground={!!titleImage || !!titleCoverColor}
>
<TitleInputContainer>
<TitleTextInput />
<SubtitleTextInput />
</S.TitleInputWrapper>
</TitleInputContainer>
);
}
Loading