관리자 팝오버 표면 공통화
This commit is contained in:
@@ -648,14 +648,18 @@ watch(() => props.member, () => {
|
||||
<circle cx="12" cy="12.001" r="4.5" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" />
|
||||
</svg>
|
||||
</button>
|
||||
<div v-if="actionMenuOpen" class="admin-member-form__action-popover absolute right-0 top-12 z-20 grid w-52 overflow-hidden rounded-xl border border-line bg-white py-2 text-sm text-[#3f4650] shadow-[0_16px_44px_rgba(15,23,42,0.16)]">
|
||||
<AdminPopoverSurface
|
||||
v-if="actionMenuOpen"
|
||||
root-class="admin-member-form__action-popover absolute right-0 top-12 z-20 grid w-52 rounded-xl border-line py-2 text-sm text-[#3f4650]"
|
||||
clipped
|
||||
>
|
||||
<button class="admin-member-form__action-item px-4 py-2.5 text-left hover:bg-[#f3f5f7]" type="button" @click="openPasswordModal">
|
||||
비밀번호 변경
|
||||
</button>
|
||||
<button class="admin-member-form__action-item px-4 py-2.5 text-left text-[#d21a26] hover:bg-[#fff1f2]" type="button" @click="openDeleteModal">
|
||||
멤버 삭제
|
||||
</button>
|
||||
</div>
|
||||
</AdminPopoverSurface>
|
||||
</div>
|
||||
<button v-else class="admin-member-form__action-icon flex h-11 w-11 items-center justify-center rounded-md border border-line bg-white text-[#3f4650]" type="button" aria-label="멤버 작업" disabled>
|
||||
<svg class="h-5 w-5" viewBox="0 0 24 24" aria-hidden="true">
|
||||
|
||||
@@ -843,9 +843,9 @@ defineExpose({
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
<AdminPopoverSurface
|
||||
v-if="isPageSectionSuggestionsOpen && hasPageSectionSuggestions"
|
||||
class="admin-page-form__section-suggestions absolute left-0 right-0 top-[calc(100%+0.25rem)] z-20 max-h-48 overflow-y-auto rounded border border-[#e3e6e8] bg-white py-1 shadow-lg"
|
||||
root-class="admin-page-form__section-suggestions absolute left-0 right-0 top-[calc(100%+0.25rem)] z-20 max-h-48 overflow-y-auto rounded border-[#e3e6e8] py-1"
|
||||
>
|
||||
<button
|
||||
v-for="(option, index) in pageSectionSuggestionOptions"
|
||||
@@ -859,13 +859,14 @@ defineExpose({
|
||||
<span class="truncate">{{ option.name }}</span>
|
||||
<span v-if="option.isSelected" class="text-xs text-[#8e9cac]">선택됨</span>
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
</AdminPopoverSurface>
|
||||
<AdminPopoverSurface
|
||||
v-else-if="isPageSectionSuggestionsOpen && pageSectionInput.trim()"
|
||||
class="admin-page-form__section-empty absolute left-0 right-0 top-[calc(100%+0.25rem)] z-20 rounded border border-[#e3e6e8] bg-white px-3 py-2 text-xs text-[#7c8b9a] shadow-lg"
|
||||
as="p"
|
||||
root-class="admin-page-form__section-empty absolute left-0 right-0 top-[calc(100%+0.25rem)] z-20 rounded border-[#e3e6e8] px-3 py-2 text-xs text-[#7c8b9a]"
|
||||
>
|
||||
일치하는 섹션이 없습니다.
|
||||
</p>
|
||||
</AdminPopoverSurface>
|
||||
</div>
|
||||
<span class="admin-page-form__help text-xs text-[#7c8b9a]">
|
||||
섹션은 네비게이션의 페이지 섹션에서 추가합니다.
|
||||
|
||||
41
components/admin/AdminPopoverSurface.vue
Normal file
41
components/admin/AdminPopoverSurface.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
/** 렌더링할 HTML 태그 */
|
||||
as: {
|
||||
type: String,
|
||||
default: 'div'
|
||||
},
|
||||
/** 팝오버 접근성 role */
|
||||
role: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
/** 팝오버에 추가할 화면별 클래스 */
|
||||
rootClass: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
/** 팝오버에 적용할 인라인 스타일 */
|
||||
surfaceStyle: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
/** 기본 overflow 숨김 사용 여부 */
|
||||
clipped: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component
|
||||
:is="props.as"
|
||||
class="admin-popover-surface border bg-white shadow-[0_18px_50px_rgba(15,23,42,0.14)]"
|
||||
:class="[clipped ? 'overflow-hidden' : '', rootClass]"
|
||||
:style="surfaceStyle"
|
||||
:role="role || undefined"
|
||||
>
|
||||
<slot />
|
||||
</component>
|
||||
</template>
|
||||
@@ -2281,9 +2281,9 @@ defineExpose({
|
||||
<p class="mt-1 text-xs text-[#8e9cac]">
|
||||
{{ selectedTags.length }} / {{ postTagLimit }}개 선택됨
|
||||
</p>
|
||||
<div
|
||||
<AdminPopoverSurface
|
||||
v-if="hasTagSuggestions"
|
||||
class="admin-post-form__tag-suggestions absolute left-0 right-0 top-full z-40 mt-1 max-h-64 overflow-y-auto rounded-lg border border-[#d7dce0] bg-white py-1 text-sm shadow-[0_18px_50px_rgba(15,23,42,0.14)]"
|
||||
root-class="admin-post-form__tag-suggestions absolute left-0 right-0 top-full z-40 mt-1 max-h-64 overflow-y-auto rounded-lg border-[#d7dce0] py-1 text-sm"
|
||||
role="listbox"
|
||||
>
|
||||
<button
|
||||
@@ -2300,7 +2300,7 @@ defineExpose({
|
||||
<span class="admin-post-form__tag-suggestion-name min-w-0 truncate font-semibold text-[#15171a]">{{ tag.name }}</span>
|
||||
<span class="admin-post-form__tag-suggestion-meta shrink-0 text-xs text-muted">{{ getTagSuggestionMeta(tag) }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</AdminPopoverSurface>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -150,15 +150,16 @@ onBeforeUnmount(() => {
|
||||
</svg>
|
||||
</button>
|
||||
<Teleport to="body">
|
||||
<div
|
||||
<AdminPopoverSurface
|
||||
v-if="isOpen"
|
||||
class="admin-row-more-menu__popover fixed z-[80] overflow-hidden rounded-xl border border-[#e2e5e9] bg-white py-2 text-sm text-[#3f4650] shadow-[0_18px_50px_rgba(15,23,42,0.16)]"
|
||||
:style="popoverStyle"
|
||||
root-class="admin-row-more-menu__popover fixed z-[80] rounded-xl border-[#e2e5e9] py-2 text-sm text-[#3f4650]"
|
||||
:surface-style="popoverStyle"
|
||||
clipped
|
||||
role="menu"
|
||||
data-admin-row-menu
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</AdminPopoverSurface>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -190,6 +190,7 @@
|
||||
| components/admin/AdminUnsavedChangesModal.vue | 관리자 편집 화면 공통 미저장 변경사항 이탈 확인 모달(상단 40px 위치) |
|
||||
| components/admin/AdminConfirmModal.vue | 관리자 공통 확인 모달(제목·본문 슬롯·확인/취소 버튼, primary/danger 톤) |
|
||||
| components/admin/AdminModalFrame.vue | 관리자 공통 일반 모달 프레임(오버레이, dialog 접근성 속성, 기본 패널 스타일, 바깥 클릭 닫기) |
|
||||
| components/admin/AdminPopoverSurface.vue | 관리자 공통 팝오버 표면(border·shadow·radius 기반 스타일, 동적 태그·role·인라인 위치 스타일 전달) |
|
||||
| components/admin/AdminToast.vue | 관리자 공통 우측 상단 토스트 표시(성공·오류·안내 톤, 화면별 상단 위치 조정) |
|
||||
| components/admin/AdminSearchInput.vue | 관리자 공통 검색 입력(돋보기 아이콘, IME 검색 이벤트 전달, 외부 focus/select 노출) |
|
||||
| components/admin/AdminSelectInput.vue | 관리자 공통 select 입력(문자·숫자 옵션 값, 기본 필터·정렬 옵션, chevron 아이콘, `v-model` 전달) |
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
- `components/admin/AdminPostForm.vue`: 태그 입력, 대표 이미지 선택, 발행/초안 저장 상태, 우측 설정 패널 책임을 분리.
|
||||
- `pages/admin/media/index.vue`: 미디어 목록, 폴더 트리, 상세 모달, 재생성 액션을 작은 단위로 분리.
|
||||
- 관리자 UI 공통 컴포넌트 정리.
|
||||
- 필터 팝오버의 radius·shadow·z-index·색상 규칙을 공통화.
|
||||
- 멤버 고급 필터와 설정 화면 select 입력의 공통 컴포넌트 적용 범위 검토.
|
||||
- 최근 브라우저 회귀 테스트 추가.
|
||||
- 태그 추천 선택 후 다음 태그 후보가 포커스 해제 없이 유지되는지 확인.
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# 업데이트 이력
|
||||
|
||||
## v1.11.12
|
||||
|
||||
- 관리자 팝오버 표면 스타일을 공용 `AdminPopoverSurface` 컴포넌트로 분리.
|
||||
- 글쓰기 태그 추천, 게시글 태그 필터, 페이지 섹션 추천, 멤버 작업 메뉴, 행 더보기 메뉴의 반복 border·shadow·radius 마크업 정리.
|
||||
|
||||
## v1.11.11
|
||||
|
||||
- 관리자 일반 모달 오버레이·패널을 공용 `AdminModalFrame` 컴포넌트로 분리.
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sori.studio",
|
||||
"version": "1.11.11",
|
||||
"version": "1.11.12",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sori.studio",
|
||||
"version": "1.11.11",
|
||||
"version": "1.11.12",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sori.studio",
|
||||
"version": "1.11.11",
|
||||
"version": "1.11.12",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"imports": {
|
||||
|
||||
@@ -1074,10 +1074,10 @@ watch(postPageCount, (pageCount) => {
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
<AdminPopoverSurface
|
||||
v-if="isTagFilterOpen && hasTagFilterSuggestions"
|
||||
id="admin-posts-tag-filter-list"
|
||||
class="admin-posts__tag-filter-list absolute left-0 right-0 top-[calc(100%+0.25rem)] z-30 max-h-64 overflow-y-auto rounded-lg border border-[#d7dce0] bg-white py-1 text-sm shadow-[0_18px_50px_rgba(15,23,42,0.14)]"
|
||||
root-class="admin-posts__tag-filter-list absolute left-0 right-0 top-[calc(100%+0.25rem)] z-30 max-h-64 overflow-y-auto rounded-lg border-[#d7dce0] py-1 text-sm"
|
||||
role="listbox"
|
||||
>
|
||||
<button
|
||||
@@ -1094,13 +1094,14 @@ watch(postPageCount, (pageCount) => {
|
||||
<span class="min-w-0 truncate">{{ option.name }}</span>
|
||||
<span v-if="option.slug === tagFilter" class="shrink-0 text-xs text-[#8e9cac]">선택됨</span>
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
</AdminPopoverSurface>
|
||||
<AdminPopoverSurface
|
||||
v-else-if="isTagFilterOpen && tagFilterSearch.trim()"
|
||||
class="admin-posts__tag-filter-empty absolute left-0 right-0 top-[calc(100%+0.25rem)] z-30 rounded border border-[#d7dce0] bg-white px-3 py-2 text-xs text-muted shadow-[0_18px_50px_rgba(15,23,42,0.14)]"
|
||||
as="p"
|
||||
root-class="admin-posts__tag-filter-empty absolute left-0 right-0 top-[calc(100%+0.25rem)] z-30 rounded border-[#d7dce0] px-3 py-2 text-xs text-muted"
|
||||
>
|
||||
일치하는 태그가 없습니다.
|
||||
</p>
|
||||
</AdminPopoverSurface>
|
||||
</div>
|
||||
<AdminSelectInput
|
||||
v-model="featuredFilter"
|
||||
|
||||
Reference in New Issue
Block a user