Skip to content

Commit 3823eeb

Browse files
committed
fix: add content bug
1 parent 304d354 commit 3823eeb

File tree

10 files changed

+97
-53
lines changed

10 files changed

+97
-53
lines changed

packages/markdown-editor/src/components/modal/delete-sortable-item-modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const DeleteSortableItemModal: React.FC = () => {
3939
<X size={24} />
4040
</button>
4141
</div>
42-
<p className="nx-text-gray-600 dark:nx-text-gray-300">{`${action.title ?? '이'} 항목을 삭제하시겠습니까?`}</p>
42+
<p className="nx-text-gray-600 dark:nx-text-gray-300">{`${action.title ?? '이 항목'} 삭제하시겠습니까?`}</p>
4343
{action.childrenCount > 0 && (
4444
<p className="nx-mt-1 nx-text-sm nx-text-gray-600 dark:nx-text-gray-300">
4545
하위 항목도 모두 함께 삭제됩니다.

packages/markdown-editor/src/components/sidebar/sidebar-header/control-icon.tsx renamed to packages/markdown-editor/src/components/sidebar/sidebar-header/control-icons.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,22 @@ import { PageType } from '@/contexts/sidebar'
22
import { NewFolderIcon } from '@/nextra/icons/new-folder'
33
import { NewPageIcon } from '@/nextra/icons/new-page'
44
import { SeparatorIcon } from '@/nextra/icons/separator'
5+
import { MouseEvent } from 'react'
56

67
type Props = {
78
className: string
89
type: PageType
910
onClick: (type: PageType) => void
1011
}
1112

12-
export const ControlIcon = ({ className, type, onClick }: Props) => {
13+
export const ControlIcons = ({ className, type, onClick }: Props) => {
1314
if (type === '') return
15+
const handleClick = (e: MouseEvent<HTMLSpanElement>) => {
16+
e.stopPropagation()
17+
onClick(type)
18+
}
1419
return (
15-
<span className={className} onClick={() => onClick(type)}>
20+
<span className={className} onClick={(e) => handleClick(e)}>
1621
{type === 'page' && <NewPageIcon />}
1722
{type === 'folder' && <NewFolderIcon />}
1823
{type === 'separator' && <SeparatorIcon />}

packages/markdown-editor/src/components/sidebar/sidebar-header/control-input.tsx

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,17 @@ type Props = {
1414
}
1515

1616
export function ControlInput({ type }: Props): ReactElement {
17-
const sidebar = useSidebar()
18-
const { isAddAction, isEditAction } = useSidebar()
19-
const [title, setTitle] = useState<string | null>(null)
17+
const {
18+
isActionActive,
19+
actionInfo,
20+
isAddAction,
21+
isEditAction,
22+
originSortableItems,
23+
setSortableItems,
24+
reset,
25+
} = useSidebar()
26+
27+
const [input, setInput] = useState<string | null>(null)
2028
const [error, setError] = useState('')
2129

2230
const nameMapper: Record<PageType, string> = {
@@ -31,78 +39,83 @@ export function ControlInput({ type }: Props): ReactElement {
3139

3240
// validate title
3341
useEffect(() => {
34-
if (title === null || title === '') return
35-
if (forbiddenCharsRegex.test(title)) {
42+
if (input === null || input === '') return
43+
if (forbiddenCharsRegex.test(input)) {
3644
setError(`${typeName} 이름에 사용해서는 안 되는 기호가 포함되어 있습니다.`)
37-
} else if (title === '') {
45+
} else if (input === '') {
3846
setError(`${typeName} 이름을 입력해야 합니다.`)
39-
} else if (title.startsWith(' ')) {
47+
} else if (input.startsWith(' ')) {
4048
setError(`${typeName} 이름은 공백으로 시작할 수 없습니다.`)
4149
} else {
4250
setError('')
4351
}
44-
}, [title])
52+
}, [input])
4553

4654
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
4755
e.preventDefault()
4856
// 두개 이상의 공백은 하나의 공백으로 변경
4957
const value = e.target.value.replace(/\s\s+/g, ' ').trimStart()
50-
setTitle(value)
58+
setInput(value)
5159
}
5260

53-
const onOutsideClick = () => {
54-
if (!sidebar.isActionActive) return
55-
if (title) {
61+
const onOutsideClick = (e?: MouseEvent) => {
62+
e?.preventDefault()
63+
if (!isActionActive) return
64+
if (actionInfo === null) return
65+
if (!isAddAction(actionInfo)) return
66+
67+
if (input) {
5668
dispatchEvent()
5769
} else {
58-
sidebar.setSortableItems(sidebar.originSortableItems) // cancel
70+
setSortableItems(originSortableItems) // cancel
5971
}
60-
sidebar.reset()
61-
setTitle('')
72+
reset()
73+
setInput('')
6274
}
6375

6476
const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
6577
if (e.key !== 'Enter') return
6678
e.preventDefault()
6779
dispatchEvent()
68-
sidebar.reset()
80+
reset()
81+
setInput('')
6982
}
7083

7184
const dispatchEvent = useDebouncedCallback(
7285
useCallback(() => {
7386
if (type === '') return
74-
if (title === null) return
75-
if (title.trim() === '') return
87+
if (input === null) return
88+
if (input.trim() === '') return
7689
if (error) return
77-
if (sidebar.actionInfo === null) return
90+
if (actionInfo === null) return
7891

79-
if (isAddAction(sidebar.actionInfo)) {
80-
const { parentUrlSlug, bookUrlSlug, index, type } = sidebar.actionInfo
92+
if (isAddAction(actionInfo)) {
93+
const { parentUrlSlug, bookUrlSlug, index, type } = actionInfo
8194
const event = new CustomEvent<CustomEventDetail['createItemEvent']>(
8295
nextraCustomEventName.createItemEvent,
8396
{
84-
detail: { title, parentUrlSlug, index, bookUrlSlug, type },
97+
detail: { title: input, parentUrlSlug, index, bookUrlSlug, type },
8598
},
8699
)
87100
window.dispatchEvent(event)
88101
}
89102

90-
if (isEditAction(sidebar.actionInfo)) {
91-
const { pageUrlSlug } = sidebar.actionInfo
103+
if (isEditAction(actionInfo)) {
104+
const { pageUrlSlug } = actionInfo
92105
const event = new CustomEvent<CustomEventDetail['updateItemEvent']>(
93106
nextraCustomEventName.updateItemEvent,
94107
{
95-
detail: { title, pageUrlSlug },
108+
detail: { title: input, pageUrlSlug },
96109
},
97110
)
98111
window.dispatchEvent(event)
99112
}
100-
}, [title, error, sidebar.actionInfo, type]),
113+
}, [input, error, actionInfo, type]),
101114
1000,
102115
{ leading: true },
103116
)
104117

105-
const { ref } = useOutsideClick<HTMLDivElement>(onOutsideClick)
118+
const { ref } = useOutsideClick<HTMLDivElement>((e) => onOutsideClick(e))
106119
if (type === '') return <></>
107120
return (
108121
<div
@@ -125,7 +138,7 @@ export function ControlInput({ type }: Props): ReactElement {
125138
'nx-bg-gray-100 nx-text-gray-600 ',
126139
'dark:nx-bg-primary-100/5 dark:nx-text-gray-400',
127140
)}
128-
value={title === null ? '' : title}
141+
value={input === null ? '' : input}
129142
onChange={onChange}
130143
onKeyDown={onKeyDown}
131144
autoFocus={true}

packages/markdown-editor/src/components/sidebar/sidebar-header/control-menu.tsx renamed to packages/markdown-editor/src/components/sidebar/sidebar-header/control-option.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const style = {
1919
svg: cn('nx-mr-2'),
2020
}
2121

22-
export const ControlMenu = forwardRef<HTMLDivElement, Props>(
22+
export const ControlOption = forwardRef<HTMLDivElement, Props>(
2323
({ isOpen, isIndex, position, onEdit, onDelete }, ref) => {
2424
return (
2525
<div
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export { SidebarHeader } from './sidebar-header'
22
export { CollapseAllIcon } from './collapse-all-icon'
3-
export { ControlIcon } from './control-icon'
4-
export { ControlMenu } from './control-menu'
3+
export { ControlIcons } from './control-icons'
4+
export { ControlOption } from './control-option'
55
export { ControlInput } from './control-input'

packages/markdown-editor/src/components/sidebar/sidebar-header/sidebar-header.tsx

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import cn from 'clsx'
2-
import { ControlIcon } from './control-icon'
2+
import { ControlIcons } from './control-icons'
33
import { CollapseAllIcon } from './collapse-all-icon'
44
import { PageType, useSidebar } from '@/contexts/sidebar'
55
import { useUrlSlug } from '@/hooks/use-url-slug'
66
import { useRef } from 'react'
77
import { SortableItem } from '@/nextra/normalize-pages'
8-
import { findFolder } from './utils'
8+
import { findFolder } from '../utils'
99

1010
type Props = {
1111
showSidebar: boolean
@@ -18,33 +18,45 @@ const style = cn(
1818
)
1919

2020
export function SidebarHeader({ showSidebar }: Props) {
21-
const sidebar = useSidebar()
21+
const {
22+
isActionActive,
23+
originSortableItems,
24+
setOriginSortableItems,
25+
setSortableItems,
26+
sortableItems,
27+
reset,
28+
setIsActionActive,
29+
setActionInfo,
30+
} = useSidebar()
2231
const { bookUrlSlug, pageUrlSlug, fullUrlSlug } = useUrlSlug()
2332

2433
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
2534

2635
const onClickIcon = (type: PageType) => {
2736
if (type === '') return
28-
if (sidebar.isActionActive) {
29-
sidebar.setSortableItems(sidebar.originSortableItems)
30-
sidebar.reset()
37+
if (isActionActive) {
38+
console.log('isCancel')
39+
if (originSortableItems.length > 0) {
40+
setSortableItems(originSortableItems)
41+
}
42+
reset()
3143
return
3244
}
3345

3446
// init
35-
timeoutRef.current = setTimeout(() => sidebar.setIsActionActive(true), 100)
36-
sidebar.setOriginSortableItems(sidebar.sortableItems) // 취소 되었을때 원래 데이터로 복구하기 위함
47+
timeoutRef.current = setTimeout(() => setIsActionActive(true), 200)
48+
setOriginSortableItems(sortableItems) // 취소 되었을때 원래 데이터로 복구하기 위함
3749

3850
// remove code
3951
let targetRoute = pageUrlSlug
40-
const isFolder = !!findFolder(sidebar.sortableItems, fullUrlSlug)
52+
const isFolder = !!findFolder(sortableItems, fullUrlSlug)
4153

4254
// 폴더 타입이 아닐 경우 부모 폴더로 targetRoute를 변경
4355
if (!isFolder) {
4456
targetRoute = targetRoute.split('/').slice(0, -1).join('/') || '/'
4557
}
4658

47-
const coppeidPageMap = structuredClone(sidebar.sortableItems)
59+
const coppeidPageMap = structuredClone(sortableItems)
4860

4961
function addNewFolderToItem(sortableItems: SortableItem[]) {
5062
for (const item of sortableItems) {
@@ -87,7 +99,7 @@ export function SidebarHeader({ showSidebar }: Props) {
8799
}
88100

89101
const removeBookUrlSlug = addToTop ? '' : item?.route.replace(bookUrlSlug, '')
90-
sidebar.setActionInfo<'add'>({
102+
setActionInfo<'add'>({
91103
action: 'add',
92104
parentUrlSlug: removeBookUrlSlug ?? '',
93105
index,
@@ -101,7 +113,7 @@ export function SidebarHeader({ showSidebar }: Props) {
101113
item.children.push(newItem)
102114
}
103115

104-
sidebar.setSortableItems(coppeidPageMap)
116+
setSortableItems(coppeidPageMap)
105117
break
106118
}
107119

@@ -123,9 +135,9 @@ export function SidebarHeader({ showSidebar }: Props) {
123135
showSidebar ? 'nx-block' : 'nx-hidden',
124136
)}
125137
>
126-
<ControlIcon className={style} onClick={onClickIcon} type="page" />
127-
<ControlIcon className={style} onClick={onClickIcon} type="folder" />
128-
<ControlIcon className={style} onClick={onClickIcon} type="separator" />
138+
<ControlIcons className={style} onClick={onClickIcon} type="page" />
139+
<ControlIcons className={style} onClick={onClickIcon} type="folder" />
140+
<ControlIcons className={style} onClick={onClickIcon} type="separator" />
129141
<CollapseAllIcon className={style} />
130142
</div>
131143
)

packages/markdown-editor/src/components/sidebar/sortable-tree/sortable-item.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { classes, indentStyle } from '../style'
99
import type { SortableItemProps } from './types'
1010
import useOutsideClick from '@/hooks/use-outside-click'
1111
import { createPortal } from 'react-dom'
12-
import { ControlMenu, ControlInput } from '../sidebar-header'
12+
import { ControlOption, ControlInput } from '../sidebar-header'
1313
import { useModal } from '@/contexts/modal'
1414
import { CustomEventDetail } from '@/types'
1515
import { nextraCustomEventName } from '@/index'
@@ -24,6 +24,7 @@ export const SortableItem = forwardRef<HTMLDivElement, SortableItemProps>((props
2424
setActionInfo,
2525
actionInfo,
2626
isEditAction,
27+
reset,
2728
} = useSidebar()
2829
const { onOpen: onOpenModal, onClose: onCloseModal, isConfirm, mode } = useModal()
2930
const [mousePosition, setMousePosition] = useState({ top: 0, left: 0 })
@@ -76,6 +77,7 @@ export const SortableItem = forwardRef<HTMLDivElement, SortableItemProps>((props
7677
if (!isDeleteTarget) return
7778
onDispatchDeleteEvent()
7879
onCloseModal()
80+
reset()
7981
}, [isConfirm])
8082

8183
useEffect(() => {
@@ -159,7 +161,7 @@ export const SortableItem = forwardRef<HTMLDivElement, SortableItemProps>((props
159161
onContextMenu={onOpenMenu}
160162
>
161163
{createPortal(
162-
<ControlMenu
164+
<ControlOption
163165
ref={menuRef}
164166
isOpen={showMenuId === item.id}
165167
position={mousePosition}

packages/markdown-editor/src/components/sidebar/sidebar-header/utils/index.ts renamed to packages/markdown-editor/src/components/sidebar/utils/findFolder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Folder, PageMapItem } from '@/nextra/types'
1+
import type { Folder, PageMapItem } from '@/nextra/types'
22

33
export function findFolder(pageMap: PageMapItem[], route: string): Folder | undefined {
44
const folders = pageMap.filter((page) => page.kind === 'Folder')

packages/markdown-editor/src/components/sidebar/utils/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { customListSortingStrategy } from './customListSortingStrategy'
77
import { dropAnimation } from './dropAnimation'
88
import { keyboardCoordinates } from './keyborardCoordinates'
99
import { normalizePageToTreeData } from './normalizePageToTreeData'
10+
import { findFolder } from './findFolder'
1011

1112
export const MAX_DEPTH = 3
1213

@@ -284,4 +285,5 @@ export {
284285
customListSortingStrategy,
285286
dropAnimation,
286287
keyboardCoordinates,
288+
findFolder,
287289
}

packages/markdown-editor/src/contexts/sidebar.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type Sidebar = {
5151
setShowMenuId: (value: string | null) => void
5252
isAddAction: (args: any) => boolean
5353
isEditAction: (args: any) => boolean
54+
isDeleteAction: (args: any) => boolean
5455
}
5556

5657
let collapsedTree = new Set<string>()
@@ -85,6 +86,7 @@ const SidebarContext = createContext<Sidebar>({
8586
setShowMenuId: () => {},
8687
isAddAction: () => false,
8788
isEditAction: () => false,
89+
isDeleteAction: () => false,
8890
})
8991

9092
export function useSidebar() {
@@ -115,7 +117,10 @@ export const SidebarProvider = ({ children }: { children: ReactNode }): ReactEle
115117
setFolding(value)
116118
}
117119

118-
function setActionInformation<T = 'add' | 'edit'>(args: ActionInfo<T>): void {
120+
function setActionInformation<T = 'add' | 'edit' | 'delete'>(args: ActionInfo<T>): void {
121+
if (!isActionActive) {
122+
setIsActionActive(true)
123+
}
119124
setActionInfo(args as any)
120125
}
121126

@@ -127,6 +132,10 @@ export const SidebarProvider = ({ children }: { children: ReactNode }): ReactEle
127132
return args.action === 'edit'
128133
}
129134

135+
function isDeleteAction(args: any): args is DeleteActionInfo {
136+
return args.action === 'delete'
137+
}
138+
130139
const value: Sidebar = {
131140
originSortableItems,
132141
setOriginSortableItems,
@@ -147,6 +156,7 @@ export const SidebarProvider = ({ children }: { children: ReactNode }): ReactEle
147156
setShowMenuId,
148157
isAddAction,
149158
isEditAction,
159+
isDeleteAction,
150160
}
151161

152162
return <SidebarContext.Provider value={value}>{children}</SidebarContext.Provider>

0 commit comments

Comments
 (0)