feat(lecture-ppt): PPT/PDF 슬라이드 1·2·3단 그리드 보기 및 1단 시 너비 제한
Made-with: Cursor
This commit is contained in:
@@ -58,6 +58,7 @@
|
||||
- PPT 강의 등록/시청
|
||||
- `.pptx` 파일 업로드
|
||||
- 상세에서 슬라이드 이미지(PNG)만 표시(XML 텍스트 추출 목록은 표시하지 않음)
|
||||
- PPT/PDF(`.pdf`) 상세: **1단·2단·3단** 보기 전환(그리드), 선택값은 브라우저 `localStorage`에 저장
|
||||
- 목록 카드에 PPT 프리뷰(첫 슬라이드 제목 + 장수) 표시
|
||||
- macOS 환경에서는 `qlmanage` 기반 실제 썸네일(첫 장 이미지) 자동 생성
|
||||
- 썸네일 백그라운드 큐
|
||||
|
||||
@@ -1323,11 +1323,73 @@ button.ai-case-inline-link:hover {
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.ppt-tools-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 12px 20px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.slide-layout-toggle {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.slide-layout-toggle-label {
|
||||
font-size: 13px;
|
||||
color: #6b7280;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.slide-layout-btn {
|
||||
padding: 6px 12px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #d1d5db;
|
||||
background: #fff;
|
||||
color: #374151;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.slide-layout-btn:hover {
|
||||
background: #f9fafb;
|
||||
border-color: #9ca3af;
|
||||
}
|
||||
|
||||
.slide-layout-btn.is-active {
|
||||
background: #1f2937;
|
||||
color: #fff;
|
||||
border-color: #1f2937;
|
||||
}
|
||||
|
||||
.slide-list {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.slide-list.slide-list--cols-1 {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.slide-list.slide-list--cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.slide-list.slide-list--cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.slide-list.slide-list--cols-2,
|
||||
.slide-list.slide-list--cols-3 {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.slide-card {
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
@@ -1340,6 +1402,15 @@ button.ai-case-inline-link:hover {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.slide-list.slide-list--cols-2 .slide-card h2,
|
||||
.slide-list.slide-list--cols-3 .slide-card h2 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.slide-list.slide-list--cols-3 .slide-card {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.slide-card header p {
|
||||
margin: 4px 0 0;
|
||||
color: #4b5563;
|
||||
@@ -1351,6 +1422,13 @@ button.ai-case-inline-link:hover {
|
||||
overflow: hidden;
|
||||
background: #f3f4f6;
|
||||
}
|
||||
|
||||
.slide-list.slide-list--cols-1 .slide-image-wrap {
|
||||
max-width: 920px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.slide-image {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
|
||||
@@ -17,8 +17,14 @@
|
||||
<h1><%= lecture.title %></h1>
|
||||
<p class="description"><%= lecture.description || "설명이 없습니다." %></p>
|
||||
|
||||
<div class="ppt-tools">
|
||||
<div class="ppt-tools ppt-tools-row">
|
||||
<span>총 <b><%= slides.length %></b>장</span>
|
||||
<div class="slide-layout-toggle" role="group" aria-label="슬라이드 한 화면 열 개수">
|
||||
<span class="slide-layout-toggle-label">보기</span>
|
||||
<button type="button" class="slide-layout-btn" data-cols="1" aria-pressed="true">1단</button>
|
||||
<button type="button" class="slide-layout-btn" data-cols="2" aria-pressed="false">2단</button>
|
||||
<button type="button" class="slide-layout-btn" data-cols="3" aria-pressed="false">3단</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if (typeof slidesError !== 'undefined' && slidesError && (!slideImageUrls || slideImageUrls.length === 0)) { %>
|
||||
@@ -29,7 +35,7 @@
|
||||
<p class="empty">슬라이드 내용을 불러올 수 없습니다.</p>
|
||||
<% } %>
|
||||
|
||||
<section class="slide-list">
|
||||
<section class="slide-list slide-list--cols-1" id="slide-list">
|
||||
<% slides.forEach((slide, index) => { %>
|
||||
<article class="slide-card">
|
||||
<header>
|
||||
@@ -46,5 +52,40 @@
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
var KEY = "learningCenterSlideColumns";
|
||||
var list = document.getElementById("slide-list");
|
||||
var buttons = document.querySelectorAll(".slide-layout-btn");
|
||||
if (!list || !buttons.length) return;
|
||||
|
||||
function applyCols(n) {
|
||||
list.classList.remove("slide-list--cols-1", "slide-list--cols-2", "slide-list--cols-3");
|
||||
list.classList.add("slide-list--cols-" + n);
|
||||
buttons.forEach(function (btn) {
|
||||
var on = btn.getAttribute("data-cols") === String(n);
|
||||
btn.setAttribute("aria-pressed", on ? "true" : "false");
|
||||
btn.classList.toggle("is-active", on);
|
||||
});
|
||||
try {
|
||||
localStorage.setItem(KEY, String(n));
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
var saved = null;
|
||||
try {
|
||||
saved = localStorage.getItem(KEY);
|
||||
} catch (e) {}
|
||||
var initial = saved === "2" || saved === "3" ? saved : "1";
|
||||
applyCols(initial);
|
||||
|
||||
buttons.forEach(function (btn) {
|
||||
btn.addEventListener("click", function () {
|
||||
var n = btn.getAttribute("data-cols");
|
||||
if (n === "1" || n === "2" || n === "3") applyCols(n);
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user