diff --git a/src/pages/insta/InstaCards.css b/src/pages/insta/InstaCards.css index 2100a84..1945aa0 100644 --- a/src/pages/insta/InstaCards.css +++ b/src/pages/insta/InstaCards.css @@ -1,5 +1,8 @@ /* ── InstaCards ──────────────────────────────────────────────────────────── */ -.ic { max-width: 1100px; margin: 0 auto; padding: 24px 16px 80px; } +.ic { max-width: 1100px; margin: 0 auto; padding: 16px 12px 80px; } +@media (min-width: 768px) { + .ic { padding: 24px 16px 80px; } +} /* 헤더 */ .ic-header { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; } @@ -24,10 +27,12 @@ .ic-spinner { width: 14px; height: 14px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: ic-spin .6s linear infinite; display: inline-block; } @keyframes ic-spin { to { transform: rotate(360deg); } } -/* 레이아웃: 모바일 1컬럼, 데스크탑 2컬럼 */ -.ic-layout { display: grid; grid-template-columns: 1fr; gap: 20px; } +/* 레이아웃: 모바일 1컬럼, 데스크탑 2컬럼. + minmax(0, 1fr)로 자식 overflow가 부모를 밀어내지 않도록 함 */ +.ic-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 20px; } +.ic-layout > * { min-width: 0; } @media (min-width: 768px) { - .ic-layout { grid-template-columns: 320px 1fr; } + .ic-layout { grid-template-columns: 320px minmax(0, 1fr); } } /* 섹션 카드 */ @@ -54,8 +59,15 @@ .ic-keyword-row__meta { font-size: 0.72rem; color: rgba(255,255,255,.35); white-space: nowrap; } .ic-keyword-row__score { font-size: 0.75rem; font-weight: 700; color: #ec4899; min-width: 36px; text-align: right; } -/* 슬레이트 그리드 */ -.ic-slates-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; } +/* 슬레이트 그리드 — 모바일 2칸 강제, 데스크탑 auto-fill */ +.ic-slates-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} +@media (min-width: 640px) { + .ic-slates-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; } +} .ic-slate-card { background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.06); border-radius: 10px; overflow: hidden; cursor: pointer; transition: border-color .15s; } .ic-slate-card:hover { border-color: rgba(236,72,153,.4); } .ic-slate-card--active { border-color: #ec4899; } @@ -73,14 +85,90 @@ .ic-status-badge--sent { background: rgba(16,185,129,.15); color: #10b981; } .ic-status-badge--failed { background: rgba(239,68,68,.12); color: #ef4444; } -/* 슬레이트 상세 패널 */ -.ic-detail { margin-top: 20px; padding: 16px; background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.06); border-radius: 12px; } +/* 슬레이트 상세 패널 — min-width: 0으로 자식 overflow가 부모 밀지 않게 */ +.ic-detail { + margin-top: 20px; padding: 16px; + background: rgba(255,255,255,.04); + border: 1px solid rgba(255,255,255,.06); border-radius: 12px; + min-width: 0; max-width: 100%; +} .ic-detail__header { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; flex-wrap: wrap; } -.ic-detail__title { font-size: 1rem; font-weight: 700; color: var(--text-primary, #e4e4e7); flex: 1; } +.ic-detail__title { font-size: 1rem; font-weight: 700; color: var(--text-primary, #e4e4e7); flex: 1; min-width: 0; } .ic-detail__actions { display: flex; gap: 8px; } -.ic-pages-strip { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 8px; margin-bottom: 14px; scroll-snap-type: x mandatory; } -.ic-page-img { width: 120px; flex-shrink: 0; aspect-ratio: 4/5; border-radius: 6px; object-fit: cover; scroll-snap-align: start; border: 1px solid rgba(255,255,255,.08); background: rgba(255,255,255,.04); } +/* ── pages strip wrapper (chevron + fade + indicator 캐러셀) ── */ +.ic-pages-wrap { + position: relative; + margin-bottom: 14px; + min-width: 0; +} +.ic-pages-strip { + display: flex; + gap: 8px; + overflow-x: auto; + scroll-snap-type: x mandatory; + scroll-behavior: smooth; + padding: 4px 48px 12px; + -webkit-overflow-scrolling: touch; + /* 양옆 fade로 "더 있다" affordance */ + mask-image: linear-gradient(to right, + transparent 0, #000 48px, #000 calc(100% - 48px), transparent 100%); + -webkit-mask-image: linear-gradient(to right, + transparent 0, #000 48px, #000 calc(100% - 48px), transparent 100%); +} +.ic-pages-strip::-webkit-scrollbar { height: 6px; } +.ic-pages-strip::-webkit-scrollbar-thumb { background: rgba(236,72,153,.4); border-radius: 3px; } +.ic-pages-strip::-webkit-scrollbar-track { background: transparent; } + +.ic-page-img { + width: clamp(140px, 42vw, 220px); + flex-shrink: 0; + aspect-ratio: 4/5; + border-radius: 8px; + object-fit: cover; + scroll-snap-align: center; + border: 2px solid rgba(255,255,255,.08); + background: rgba(255,255,255,.04); + cursor: pointer; + transition: transform .15s, border-color .15s; +} +.ic-page-img.is-active { + border-color: #ec4899; + transform: scale(1.03); +} + +.ic-pages-nav { + position: absolute; + top: calc(50% - 6px); + transform: translateY(-50%); + width: 40px; height: 40px; + border-radius: 50%; border: 0; + background: rgba(0,0,0,.65); color: #fff; + font-size: 24px; font-weight: 700; line-height: 1; + cursor: pointer; z-index: 2; + display: flex; align-items: center; justify-content: center; + backdrop-filter: blur(4px); + transition: opacity .15s, background .15s; +} +.ic-pages-nav:hover:not(:disabled) { background: rgba(236,72,153,.9); } +.ic-pages-nav:disabled { opacity: .2; cursor: not-allowed; } +.ic-pages-nav--prev { left: 0; } +.ic-pages-nav--next { right: 0; } + +.ic-pages-indicator { + display: inline-flex; align-items: baseline; gap: 4px; + margin: 4px auto 0; + padding: 4px 12px; + background: rgba(255,255,255,.06); + border-radius: 99px; + font-size: 0.78rem; + color: rgba(255,255,255,.7); + font-variant-numeric: tabular-nums; +} +.ic-pages-indicator-row { display: flex; justify-content: center; } +.ic-pages-indicator__current { color: #ec4899; font-weight: 700; } +.ic-pages-indicator__sep { opacity: 0.5; } +.ic-pages-indicator__total { opacity: 0.7; } .ic-caption-box { background: rgba(255,255,255,.03); border-radius: 8px; padding: 12px; margin-bottom: 10px; } .ic-caption-box__label { font-size: 0.7rem; font-weight: 700; color: rgba(255,255,255,.4); text-transform: uppercase; margin-bottom: 6px; } diff --git a/src/pages/insta/InstaCards.jsx b/src/pages/insta/InstaCards.jsx index eb1425b..f4b3f9c 100644 --- a/src/pages/insta/InstaCards.jsx +++ b/src/pages/insta/InstaCards.jsx @@ -689,6 +689,91 @@ function SlatesPanel({ selectedId, onSelect }) { ); } +/* ══════════════════════ 페이지 스트립 (chevron + indicator) ═══════════ */ +function PagesStrip({ slateId, pageCount }) { + const stripRef = useRef(null); + const [activePage, setActivePage] = useState(1); + + const scrollToPage = useCallback((pageNo) => { + const strip = stripRef.current; + if (!strip) return; + const next = Math.max(1, Math.min(pageCount, pageNo)); + const child = strip.children[next - 1]; + if (child) { + child.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' }); + setActivePage(next); + } + }, [pageCount]); + + // 스크롤/드래그 시 가운데 카드 감지 + const onScroll = useCallback(() => { + const strip = stripRef.current; + if (!strip) return; + const rect = strip.getBoundingClientRect(); + const centerX = rect.left + rect.width / 2; + let best = 1, bestDist = Infinity; + Array.from(strip.children).forEach((child, i) => { + const cRect = child.getBoundingClientRect(); + const cCenter = cRect.left + cRect.width / 2; + const dist = Math.abs(cCenter - centerX); + if (dist < bestDist) { bestDist = dist; best = i + 1; } + }); + if (best !== activePage) setActivePage(best); + }, [activePage]); + + // 키보드 ←/→ + useEffect(() => { + const onKey = (e) => { + if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return; + if (e.key === 'ArrowLeft') { scrollToPage(activePage - 1); e.preventDefault(); } + else if (e.key === 'ArrowRight') { scrollToPage(activePage + 1); e.preventDefault(); } + }; + window.addEventListener('keydown', onKey); + return () => window.removeEventListener('keydown', onKey); + }, [activePage, scrollToPage]); + + return ( +