타임라인용 RecentUpdatesTable 구성
변경 요약
- 문서 전체의
Added(작성) / Modified(수정)이력을 한눈에 볼 수 있는 표 컴포넌트 추가. - 슬러그가
timeline인 페이지에서만 대용량 표(상한 무제한)를 렌더링하고, 그 외 페이지에는 노출하지 않음.
변경된 파일
quartz/components/RecentUpdatesTable.tsxquartz/components/styles/recentUpdatesTable.scssquartz/components/index.tsquartz.layout.ts
주요 구현 포인트
- 정렬: 수정일 내림차순 + 제목 알파벳 보조 정렬.
- 열 구성:
Title / Path / Added / Modified(반응형 테이블 래퍼 포함). - Path 표기: 슬러그에서 파일명을 제외한 경로만 표시(없으면 em dash).
- 조건부 렌더링:
page.fileData.slug === 'timeline'일 때만 표를 렌더. - 접근성: 각 셀에
data-label을 부여해 모바일에서 헤더-셀 매핑이 유지되며, 값이 없을 때는aria-hidden="true"로 시각적 대시를 출력. - 반응형:
.table-wrapper { overflow-x: auto; }로 좁은 화면에서 가로 스크롤 허용.
왜 변경했는가
- “최근 글” 위젯을 보완해, 사이트 전체의 생성/수정 흐름을 투명하게 공개하려는 목적.
- 글감 관리/운영 점검 시 유용한 시스템 뷰 제공.
핵심 코드
아래는 실제 반영된 코드 중 핵심 발췌본입니다. 전체 구현은 각 파일에서 확인하세요.
1) RecentUpdatesTable 컴포넌트
quartz/components/RecentUpdatesTable.tsx 의 옵션, 경로 정규화, 테이블 구조입니다.
// quartz/components/RecentUpdatesTable.tsx:1
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { QuartzPluginData } from "../plugins/vfile"
import { byDateAndAlphabetical } from "./PageList"
import { GlobalConfiguration } from "../cfg"
import { i18n } from "../i18n"
import { classNames } from "../util/lang"
import { resolveRelative } from "../util/path"
import { Date } from "./Date"
import style from "./styles/recentUpdatesTable.scss"
interface Options {
title?: string
limit: number
filter: (file: QuartzPluginData) => boolean
sort: (a: QuartzPluginData, b: QuartzPluginData) => number
showPath: boolean
}
const defaultOptions = (cfg: GlobalConfiguration): Options => ({
title: i18n(cfg.locale).components.recentNotes.title,
limit: 10,
filter: () => true,
sort: byDateAndAlphabetical(cfg),
showPath: true,
})
function formatPath(slug?: string): string {
if (!slug) {
return ""
}
const cleaned = slug.replace(/\\/g, "/")
const trimmed = cleaned.replace(/\/index$/, "")
const withoutTrailingSlash = trimmed.replace(/\/$/, "")
const segments = withoutTrailingSlash.split("/").filter(Boolean)
if (segments.length <= 1) {
return ""
}
segments.pop()
return segments.join("/")
}
export default ((userOpts?: Partial<Options>) => {
const RecentUpdatesTable: QuartzComponent = ({
allFiles,
fileData,
displayClass,
cfg,
}: QuartzComponentProps) => {
const opts = { ...defaultOptions(cfg), ...userOpts }
const pages = allFiles.filter(opts.filter).sort(opts.sort).slice(0, opts.limit)
return (
<div class={classNames(displayClass, "recent-updates-table")}>
{opts.title && <h3>{opts.title}</h3>}
<div class="table-wrapper">
<table>
<thead>
<tr>
<th scope="col">Title</th>
{opts.showPath && <th scope="col">Path</th>}
<th scope="col">Added</th>
<th scope="col">Modified</th>
</tr>
</thead>
<tbody>
{pages.map((page) => {
const title = page.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title
const created = page.dates?.created
const modified = page.dates?.modified
const pathLabel = opts.showPath ? formatPath(page.slug) : ""
return (
<tr key={page.slug}>
<td data-label="Title">
<a href={resolveRelative(fileData.slug!, page.slug!)} class="internal">
{title}
</a>
</td>
{opts.showPath && (
<td class="path-cell" data-label="Path">
{pathLabel ? <span>{pathLabel}</span> : <span aria-hidden="true">—</span>}
</td>
)}
<td class="date-cell" data-label="Added">
{created ? (
<Date date={created} locale={cfg.locale} />
) : (
<span aria-hidden="true">—</span>
)}
</td>
<td class="date-cell" data-label="Modified">
{modified ? (
<Date date={modified} locale={cfg.locale} />
) : created ? (
<Date date={created} locale={cfg.locale} />
) : (
<span aria-hidden="true">—</span>
)}
</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
)
}
RecentUpdatesTable.css = style
return RecentUpdatesTable
}) satisfies QuartzComponentConstructor설정 포인트
limit: 기본 10. 타임라인 페이지에서는 사실상 무제한(큰 값)으로 재설정.sort: 기본값은byDateAndAlphabetical, 타임라인에서는sortByModified로 명시적 최신 수정일 우선.formatPath: 파일명을 제외한 디렉토리 경로만 표시해 Path 칼럼의 밀도를 낮춤.
2) 레이아웃 연결 및 정렬 기준
quartz.layout.ts에서 정렬 기준, 조건부 렌더링, 이중 날짜 표기를 설정했습니다.
// quartz.layout.ts:1
import { PageLayout, SharedLayout } from "./quartz/cfg"
import * as Component from "./quartz/components"
import { QuartzPluginData } from "./quartz/plugins/vfile"
const sortByModified: (a: QuartzPluginData, b: QuartzPluginData) => number = (a, b) => {
const aModified = a.dates?.modified?.getTime?.() ?? 0
const bModified = b.dates?.modified?.getTime?.() ?? 0
if (aModified === bModified) {
const aTitle = a.frontmatter?.title?.toLowerCase?.() ?? ""
const bTitle = b.frontmatter?.title?.toLowerCase?.() ?? ""
return aTitle.localeCompare(bTitle)
}
return bModified - aModified
}
export const sharedPageComponents: SharedLayout = {
head: Component.Head(),
header: [],
afterBody: [
Component.ConditionalRender({
component: Component.RecentUpdatesTable({
title: "All Content",
limit: 999999999,
sort: sortByModified,
}),
condition: (page) => page.fileData.slug === "timeline",
}),
// ... 생략 ...
Component.RecentNotes({ title: "Recently Added", limit: 5, linkToMore: "timeline" }),
],
// ... 생략 ...
}
export const defaultContentPageLayout: PageLayout = {
beforeBody: [
Component.Breadcrumbs(),
Component.ContentMeta({
dateTypes: ["modified", "created"],
dateLabels: { created: "Created", modified: "Updated" },
}),
Component.TagList(),
],
// ... 생략 ...
}핵심 요약
sortByModified: 최신 수정 → 동률 시 제목 알파벳 정렬.ConditionalRender: 슬러그가timeline일 때만 표 렌더링.ContentMeta: 본문/목록 레이아웃 모두에서Updated와Created를 함께 표시.
3) 컴포넌트 등록
quartz/components/index.ts에 컴포넌트를 등록해 레이아웃에서 사용할 수 있게 했습니다.
// quartz/components/index.ts:1
import RecentUpdatesTable from "./RecentUpdatesTable"
export {
// ... 생략 ...
RecentUpdatesTable,
// ... 생략 ...
}4) 스타일
모바일 스크롤, 날짜/경로 셀 처리, 테이블 타이포그래피를 정의했습니다.
// quartz/components/styles/recentUpdatesTable.scss:1
.recent-updates-table {
margin: 0.5rem 0 1rem;
& > h3 {
margin: 0.5rem 0;
font-size: 1rem;
}
.table-wrapper {
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 1rem;
}
th,
td {
padding: 0.5rem 0.5rem;
text-align: left;
vertical-align: middle;
}
thead {
border-bottom: 1px solid var(--lightgray);
}
tbody {
tr { border-bottom: 1px solid var(--lightgray); }
.date-cell { white-space: nowrap; color: var(--secondary); }
.path-cell { font-size: 1rem; color: var(--secondary); word-break: break-all; }
}
}운영 체크리스트
- 문서가 많을 경우 테이블이 길어질 수 있으므로, 모바일 가독성을 위해 폭/스크롤 동작을 함께 점검.
RecentNotes의 더보기 링크를timeline으로 연결하여 탐색 흐름을 자연스럽게 유지.quartz.layout.ts의linkToMore: "timeline"설정 확인.
- 타임라인 페이지가 아닌 곳에서 표가 보이지 않는지 재확인(
ConditionalRender조건 검증). Updated/Created이중 표기가 의도대로 출력되는지 대표 문서 2~3개로 확인.
관련 커밋
3ad7489feat(layout): surface timeline updates table and dual meta dates

