-
Notifications
You must be signed in to change notification settings - Fork 1
Feat/sw 67 - 검색 필터링 기능 구현 #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8d7b6f9
af34c65
74e52ef
cddfad4
c3343fa
b228a81
56fb247
9e4b938
db6d505
dca9bda
c30d827
db17097
d811abe
c8a0f3b
f8e3545
625c673
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,6 +12,7 @@ import { useCreateBookmark, useAnalyzeUrl } from '@/lib/api/bookmarkApi'; | |||||||||||||||||||||||||||||||||||||
| import { useAnalyzeLink } from '@/lib/api/linkAnalysisApi'; | ||||||||||||||||||||||||||||||||||||||
| import { useAuthStore } from '@/lib/store/authStore'; | ||||||||||||||||||||||||||||||||||||||
| import type { LinkAnalysisResponse } from '@/lib/types/linkAnalysis'; | ||||||||||||||||||||||||||||||||||||||
| import { FOLDER_TYPE } from '@/lib/types/folder'; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // 디자인 시안에서 추출한 커스텀 테마 매핑 | ||||||||||||||||||||||||||||||||||||||
| const theme = { | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -61,7 +62,7 @@ export function SaveLinkDialog() { | |||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // --- API 연동 (React Query Hooks) --- | ||||||||||||||||||||||||||||||||||||||
| const memberId = useAuthStore((s) => s.member?.memberId); | ||||||||||||||||||||||||||||||||||||||
| const { data: folders } = useFolders(memberId); // 기존 폴더 목록 조회 | ||||||||||||||||||||||||||||||||||||||
| const { data: folders, isLoading: isFoldersLoading } = useFolders(memberId); // 기존 폴더 목록 조회 | ||||||||||||||||||||||||||||||||||||||
| const { data: tagsData } = useTags(memberId); // 기존 태그 목록 조회 | ||||||||||||||||||||||||||||||||||||||
| const createBookmarkMutation = useCreateBookmark(); // 북마크 생성 API 연동 | ||||||||||||||||||||||||||||||||||||||
| const createFolderMutation = useCreateFolder(); // 폴더 생성 API 연동 | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -277,7 +278,19 @@ export function SaveLinkDialog() { | |||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||
| saveBookmark(selectedFolderId); | ||||||||||||||||||||||||||||||||||||||
| // Quick Save 경로: 폴더 미지정 시 미분류(UNORGANIZED) 폴더로 저장 | ||||||||||||||||||||||||||||||||||||||
| const fallbackFolderId = | ||||||||||||||||||||||||||||||||||||||
| selectedFolderId | ||||||||||||||||||||||||||||||||||||||
| ?? folders?.find((f) => f.folderType === FOLDER_TYPE.UNORGANIZED)?.memberFolderId | ||||||||||||||||||||||||||||||||||||||
| ?? null; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // [추가] 폴더 목록 로딩이 끝났음에도 불구하고 대상 폴더를 결정할 수 없는 경우 예외 처리 | ||||||||||||||||||||||||||||||||||||||
| if (fallbackFolderId === null) { | ||||||||||||||||||||||||||||||||||||||
| alert('폴더 정보를 불러오는 중이거나 폴더가 존재하지 않습니다. 잠시 후 다시 시도해주세요.'); | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| saveBookmark(fallbackFolderId); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+281
to
294
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# 백엔드가 memberFolderId === null을 어떻게 처리하는지 확인
rg -nP -C3 'memberFolderId' --type=java
# useFolders 훅 동작과 enabled 조건 확인
fd -e ts -e tsx folderApi --exec cat {}Repository: Searchweb-Dev/Searchweb-Back Length of output: 29207 🏁 Script executed: # Find and read SaveLinkDialog.tsx to understand the Save button logic
fd -e tsx SaveLinkDialog --exec wc -l {} \;Repository: Searchweb-Dev/Searchweb-Back Length of output: 129 🏁 Script executed: # Check the SaveLinkDialog component, focusing on Save button conditions
fd -e tsx SaveLinkDialog --exec cat -n {} \; | head -400Repository: Searchweb-Dev/Searchweb-Back Length of output: 20366 🏁 Script executed: # Continue reading SaveLinkDialog.tsx from line 400 onwards to find Save button logic
sed -n '400,550p' frontend/src/components/dialogs/SaveLinkDialog.tsxRepository: Searchweb-Dev/Searchweb-Back Length of output: 9454 🏁 Script executed: # Search for the Save button and its disabled attribute
rg -n "disabled|Save" frontend/src/components/dialogs/SaveLinkDialog.tsx | tail -20Repository: Searchweb-Dev/Searchweb-Back Length of output: 2133
저장 버튼은 URL 유효성과 API 진행 중 여부만 체크하며, 폴더 목록 로딩 상태를 확인하지 않습니다. 현재 백엔드는 다음 중 하나를 권장합니다:
제안 변경 } else {
// Quick Save 경로: 폴더 미지정 시 미분류(UNORGANIZED) 폴더로 저장
const fallbackFolderId =
selectedFolderId
?? folders?.find((f) => f.folderType === FOLDER_TYPE.UNORGANIZED)?.memberFolderId
?? null;
+ if (fallbackFolderId === null) {
+ console.warn('Folders not loaded or UNORGANIZED folder not found');
+ return;
+ }
saveBookmark(fallbackFolderId);
}또는 Save 버튼의 disabled={
!url.trim()
|| !(url.startsWith('http://') || url.startsWith('https://'))
|| createBookmarkMutation.isPending
|| createFolderMutation.isPending
+ || (!pendingNewFolderName && !folders)
}이렇게 하면 폴더 목록이 로딩 중일 때는 Save 버튼이 비활성화됩니다. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
@@ -398,7 +411,7 @@ export function SaveLinkDialog() { | |||||||||||||||||||||||||||||||||||||
| <span className="material-symbols-outlined !text-[13px] !leading-none block text-white font-bold">link</span> | ||||||||||||||||||||||||||||||||||||||
| </span> | ||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||
| <span className="text-[11px] font-bold text-slate-700 dark:text-gray-100 tracking-tight pr-1">복사한 링크 붙여넣기</span> | ||||||||||||||||||||||||||||||||||||||
| <span className="text-[11px] font-bold text-slate-700 dark:text-gray-100 tracking-tight pr-1">Paste copied link</span> | ||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||
| {/* 부드럽고 존재감 있는 곡선형 SVG 꼬리표 - 크기 확대 및 실루엣 최적화 */} | ||||||||||||||||||||||||||||||||||||||
| <svg | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -432,7 +445,7 @@ export function SaveLinkDialog() { | |||||||||||||||||||||||||||||||||||||
| {url.trim() && !(url.startsWith('http://') || url.startsWith('https://')) && ( | ||||||||||||||||||||||||||||||||||||||
| <p className="text-[10px] text-red-500 dark:text-red-400 font-medium flex items-center gap-1 mt-1 ml-10"> | ||||||||||||||||||||||||||||||||||||||
| <span className="material-symbols-outlined !text-[12px]">error</span> | ||||||||||||||||||||||||||||||||||||||
| URL은 http:// 또는 https://로 시작해야 합니다. | ||||||||||||||||||||||||||||||||||||||
| URL must start with http:// or https:// | ||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -818,7 +831,7 @@ export function SaveLinkDialog() { | |||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||||
| onClick={handleSave} | ||||||||||||||||||||||||||||||||||||||
| disabled={!url.trim() || !(url.startsWith('http://') || url.startsWith('https://')) || createBookmarkMutation.isPending || createFolderMutation.isPending} | ||||||||||||||||||||||||||||||||||||||
| disabled={!url.trim() || !(url.startsWith('http://') || url.startsWith('https://')) || createBookmarkMutation.isPending || createFolderMutation.isPending || isFoldersLoading} | ||||||||||||||||||||||||||||||||||||||
| className={`${styles.btnGradient} text-xs font-bold px-6 py-2.5 rounded-xl transition-all flex items-center gap-2 transform hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed shadow-lg`} | ||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||
| {(createBookmarkMutation.isPending || createFolderMutation.isPending) ? 'Saving...' : 'Save to Workspace'} | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,8 +2,10 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import React, { useEffect, useRef, useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { createPortal } from 'react-dom'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { FolderResponse } from '@/lib/types/folder'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { FOLDER_TYPE, type FolderResponse } from '@/lib/types/folder'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useFolderStore } from '@/lib/store/folderStore'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useLinkStore } from '@/lib/store/linkStore'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useUIStore } from '@/lib/store/uiStore'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { FolderEditModal, FolderDeleteModal, FolderMoveModal } from './FolderManagementModals'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface FolderCardProps { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -15,8 +17,13 @@ const MENU_OFFSET_Y = 4; // 버튼과 메뉴 사이의 상하 간격 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function FolderCard({ folder, color }: FolderCardProps) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const setSelectedFolderId = useFolderStore((s) => s.setSelectedFolderId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const currentSelectedFolderId = useFolderStore((s) => s.selectedFolderId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const searchQuery = useFolderStore((s) => s.searchQuery); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const setLinkSearchQuery = useLinkStore((s) => s.setSearchQuery); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [showMenu, setShowMenu] = useState(false); // 드롭다운 메뉴 표시 여부 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [modalType, setModalType] = useState<'edit' | 'delete' | 'move' | null>(null); // 현재 열린 모달 타입 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 시스템 폴더(미분류)는 Move/Delete 불가 (Rename 만 허용) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isSystemFolder = folder.folderType === FOLDER_TYPE.UNORGANIZED; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // DOM 참조 (메뉴 외부 클릭 감지 및 위치 계산용) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const menuRef = useRef<HTMLDivElement>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -71,7 +78,20 @@ export function FolderCard({ folder, color }: FolderCardProps) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 카드 클릭 시 폴더 선택 처리 (메뉴가 열려있지 않을 때만) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleCardClick = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!showMenu) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setSelectedFolderId(folder.memberFolderId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 1. 이미 선택된 폴더를 다시 클릭하면 선택 해제, 아니면 해당 폴더 선택 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (currentSelectedFolderId === folder.memberFolderId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setSelectedFolderId(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setSelectedFolderId(folder.memberFolderId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 2. 선택 시에만 수행: 기존 폴더 검색어가 있다면 링크 검색어로 전이 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (searchQuery) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setLinkSearchQuery(searchQuery); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 3. 선택 시에만 수행: 우측 패널이 닫혀있다면 자동으로 열어줌 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useUIStore.getState().toggleRightPanel(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
79
to
96
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 선택 해제 시에도 우측 패널을 강제로 여는 동작 확인이 필요합니다.
♻️ 제안 변경 const handleCardClick = () => {
if (!showMenu) {
- // 1. 이미 선택된 폴더를 다시 클릭하면 선택 해제, 아니면 해당 폴더 선택
if (currentSelectedFolderId === folder.memberFolderId) {
setSelectedFolderId(null);
+ return;
- } else {
- setSelectedFolderId(folder.memberFolderId);
}
-
- // 2. 기존 폴더 검색어가 있다면 링크 검색어로 전이 (사용자 요청: 클릭 시 링크 필터링되도록)
+ setSelectedFolderId(folder.memberFolderId);
if (searchQuery) {
setLinkSearchQuery(searchQuery);
}
-
- // 3. 우측 패널이 닫혀있다면 자동으로 열어줌
useUIStore.getState().toggleRightPanel(true);
}
};📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -84,10 +104,16 @@ export function FolderCard({ folder, color }: FolderCardProps) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 메뉴 항목 클릭 시 모달 열기 및 메뉴 닫기 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleMenuItemClick = (type: 'edit' | 'delete' | 'move', e: React.MouseEvent) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| e.stopPropagation(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 시스템 폴더는 Move/Delete 를 허용하지 않음 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isSystemFolder && (type === 'move' || type === 'delete')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setModalType(type); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setShowMenu(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isSelected = currentSelectedFolderId === folder.memberFolderId; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -100,10 +126,14 @@ export function FolderCard({ folder, color }: FolderCardProps) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleCardClick(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className={`bg-white dark:bg-card-dark rounded-lg p-2.5 border border-gray-200/70 dark:border-white/5 shadow-sm hover:shadow-md hover:border-purple-300 dark:hover:border-white/10 transition-all duration-300 group cursor-pointer h-[90px] flex flex-col justify-between focus:ring-1 focus:ring-purple-300 dark:focus:ring-purple-500 outline-none hover:bg-purple-50/30 dark:hover:bg-white/[0.03] relative overflow-visible ${color || ''}`} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className={`bg-white dark:bg-card-dark rounded-lg p-2.5 border transition-all duration-300 group cursor-pointer h-[90px] flex flex-col justify-between outline-none hover:bg-purple-50/30 dark:hover:bg-white/[0.03] relative overflow-visible ${ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isSelected | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? 'border-purple-500 ring-1 ring-purple-500/20 shadow-md' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : 'border-gray-200/70 dark:border-white/5 shadow-sm hover:shadow-md hover:border-purple-300 dark:hover:border-white/10' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } ${color || ''}`} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="flex justify-between items-start"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="p-1.5 bg-purple-50 dark:bg-white/5 text-gray-400 dark:text-gray-400 group-hover:dark:text-white transition-colors rounded-md flex items-center justify-center w-8 h-8"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className={`p-1.5 bg-purple-50 dark:bg-white/5 text-gray-400 dark:text-gray-400 group-hover:dark:text-white transition-colors rounded-md flex items-center justify-center w-8 h-8`}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span className="material-symbols-outlined text-[16px]">folder_open</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -148,15 +178,17 @@ export function FolderCard({ folder, color }: FolderCardProps) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={(e) => handleMenuItemClick('move', e)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="w-full text-left px-3 py-2 text-[11px] text-gray-700 dark:text-white hover:bg-purple-50 dark:hover:bg-purple-600/20 flex items-center gap-2.5 transition-colors" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={isSystemFolder} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="w-full text-left px-3 py-2 text-[11px] text-gray-700 dark:text-white hover:bg-purple-50 dark:hover:bg-purple-600/20 flex items-center gap-2.5 transition-colors disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent dark:disabled:hover:bg-transparent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span className="material-symbols-outlined !text-[16px] !leading-none">drive_file_move</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span className="font-medium">Move</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="h-px bg-gray-50 dark:bg-white/8 my-0.5" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={(e) => handleMenuItemClick('delete', e)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="w-full text-left px-3 py-2 text-[11px] text-rose-500 dark:text-red-400 hover:bg-rose-50 dark:hover:bg-purple-600/20 flex items-center gap-2.5 transition-colors" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={isSystemFolder} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="w-full text-left px-3 py-2 text-[11px] text-rose-500 dark:text-red-400 hover:bg-rose-50 dark:hover:bg-purple-600/20 flex items-center gap-2.5 transition-colors disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent dark:disabled:hover:bg-transparent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span className="material-symbols-outlined !text-[16px] !leading-none text-rose-500 dark:text-red-400">delete</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span className="font-medium">Delete</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
비활성화된 버튼에도 포인터 커서가 적용됩니다.
button/[role="button"]에 무조건cursor-pointer를 적용하면disabled또는aria-disabled="true"상태의 버튼에서도 포인터 커서가 표시되어, 클릭 가능한 요소처럼 보이는 UX 문제가 발생합니다. (예:SaveLinkDialog의 Save 버튼이 disabled 상태일 때)♻️ 제안 변경
📝 Committable suggestion
🧰 Tools
🪛 Biome (2.4.12)
[error] 169-169: Tailwind-specific syntax is disabled.
(parse)
🤖 Prompt for AI Agents