slidev-theme-gtlabo 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,72 +1,126 @@
1
- <!-- components/SectionDivider.vue -->
1
+ <!-- components/TableOfContents.vue -->
2
2
  <template>
3
- <div class="table-of-contents-container flex flex-col justify-start bg-white overflow-hidden relative">
4
- <!-- 厳密な背景グリッド - Swiss Styleの基礎 -->
5
- <div class="absolute inset-0 z-0 pointer-events-none opacity-10"
6
- style="background-image: linear-gradient(#0f172a 1px, transparent 1px), linear-gradient(to right, #0f172a 1px, transparent 1px); background-size: 50px 50px;">
7
- </div>
8
-
9
- <!-- 非対称の幾何学的構成要素 - 左上の強調 -->
10
- <div class="absolute top-0 left-0 w-2 h-32 bg-slate-900 z-5"></div>
11
- <div class="absolute top-0 left-0 w-32 h-2 bg-slate-900 z-5"></div>
12
-
13
- <!-- 非対称の幾何学的構成要素 - 右下のアクセント -->
14
- <div class="absolute bottom-0 right-0 w-48 h-2 bg-slate-900 z-5"></div>
15
- <div class="absolute bottom-12 right-0 w-1 h-48 bg-slate-900 opacity-30 z-5"></div>
16
-
17
- <!-- 右側の装飾バー群 - cover.vueと同じスタイル -->
18
- <div class="absolute right-0 top-0 w-1/4 h-full z-0 pointer-events-none">
19
- <div class="absolute right-0 top-0 w-full h-full bg-gradient-to-l from-slate-200 to-slate-200/10" />
20
- <div class="absolute right-[15%] top-0 w-32 h-[35%] bg-sky-600/10" />
21
- <div class="absolute right-0 bottom-0 w-2 h-[40%] bg-slate-900" />
22
- </div>
23
-
24
- <!-- 機能的なsky-500のアクセントブロック -->
25
- <div class="absolute right-0 top-1/4 w-[12%] h-24 bg-sky-500/30 z-5"></div>
26
-
27
- <!-- ヘッダー - 付録セクション用 -->
3
+ <div class="table-of-contents-container flex flex-col justify-center bg-theme-color">
4
+ <!-- ヘッダー -->
28
5
  <div
29
6
  v-if="isAppendixSection"
30
7
  :class="headerClasses"
31
- class="relative z-10"
32
8
  >
33
- <div class="flex items-start space-x-6">
34
- <div :class="headerTitleClasses" class="font-bold uppercase tracking-widest text-slate-800 leading-none">
35
- APPENDIX
36
- </div>
37
- </div>
9
+ <p :class="headerTitleClasses">
10
+ 付録
11
+ </p>
38
12
  </div>
39
13
 
40
- <!-- メインコンテンツ - 厳密なグリッドベース -->
41
- <div :class="mainContentClasses" class="relative z-10">
14
+ <!-- メインコンテンツ -->
15
+ <div :class="mainContentClasses">
42
16
  <div class="max-w-full w-full">
43
- <!-- 共通の目次表示 -->
44
- <div :class="gridClasses">
45
- <ChapterItem
46
- v-for="(chapter, index) in displayChapters"
17
+ <!-- 通常の目次表示 -->
18
+ <div
19
+ v-if="!isAppendixSection"
20
+ :class="gridClasses"
21
+ >
22
+ <div
23
+ v-for="(chapter, index) in regularChapters"
24
+ :key="chapter.key"
25
+ class="flex flex-col"
26
+ :class="{ 'relative': isNextChapter(chapter.key) }"
27
+ >
28
+ <!-- 次の章の強調表示 -->
29
+ <div
30
+ v-if="isNextChapter(chapter.key)"
31
+ :class="highlightBorderClasses"
32
+ />
33
+ <div
34
+ v-if="isNextChapter(chapter.key)"
35
+ :class="nextBadgeClasses"
36
+ >
37
+ NEXT
38
+ </div>
39
+
40
+ <!-- 章番号とタイトル -->
41
+ <div :class="chapterHeaderClasses">
42
+ <div :class="chapterNumberClasses">
43
+ {{ String(index + 1).padStart(2, '0') }}
44
+ </div>
45
+ <div class="flex-1 border-b border-white pb-2">
46
+ <span :class="chapterTitleClasses">
47
+ {{ chapter.title }}
48
+ </span>
49
+ </div>
50
+ </div>
51
+
52
+ <!-- セクション一覧(重複除去済み) -->
53
+ <div :class="sectionListClasses">
54
+ <div
55
+ v-for="(sectionData, sectionIndex) in chapter.uniqueSections"
56
+ :key="`${chapter.key}-${sectionIndex}`"
57
+ :class="sectionItemClasses"
58
+ >
59
+ <span :class="sectionTextClasses">
60
+ {{ sectionData.title }}
61
+ </span>
62
+ </div>
63
+ </div>
64
+ </div>
65
+ </div>
66
+
67
+ <!-- 付録の目次表示 -->
68
+ <div
69
+ v-else
70
+ :class="gridClasses"
71
+ >
72
+ <div
73
+ v-for="(chapter, index) in appendixChapters"
47
74
  :key="chapter.key"
48
- :chapter="chapter"
49
- :index="index"
50
- :is-next="isNextChapter(chapter.key)"
51
- :is-appendix="isAppendixSection"
52
- :size="size"
53
- :config="currentConfig"
54
- />
75
+ class="flex flex-col"
76
+ :class="{ 'relative': isNextChapter(chapter.key) }"
77
+ >
78
+ <!-- 次の章の強調表示 -->
79
+ <div
80
+ v-if="isNextChapter(chapter.key)"
81
+ :class="highlightBorderClasses"
82
+ />
83
+ <div
84
+ v-if="isNextChapter(chapter.key)"
85
+ :class="nextBadgeClasses"
86
+ >
87
+ NEXT
88
+ </div>
89
+
90
+ <!-- 付録番号とタイトル -->
91
+ <div :class="chapterHeaderClasses">
92
+ <div :class="appendixNumberClasses">
93
+ A{{ index + 1 }}
94
+ </div>
95
+ <div class="flex-1 border-b border-white pb-2">
96
+ <h2 :class="chapterTitleClasses">
97
+ {{ chapter.title }}
98
+ </h2>
99
+ </div>
100
+ </div>
101
+
102
+ <!-- セクション一覧(重複除去済み) -->
103
+ <div :class="sectionListClasses">
104
+ <div
105
+ v-for="(sectionData, sectionIndex) in chapter.uniqueSections"
106
+ :key="`${chapter.key}-${sectionIndex}`"
107
+ :class="sectionItemClasses"
108
+ >
109
+ <span :class="sectionTextClasses">
110
+ {{ sectionData.title }}
111
+ </span>
112
+ </div>
113
+ </div>
114
+ </div>
55
115
  </div>
56
116
  </div>
57
117
  </div>
58
-
59
- <!-- タイポグラフィックな署名 - 客観性の表明 -->
60
- <div class="absolute bottom-4 left-6 text-[10px] font-mono text-slate-800 uppercase tracking-widest z-20">
61
- CONTENTS
62
- </div>
63
118
  </div>
64
119
  </template>
65
-
120
+
66
121
  <script setup>
67
122
  import { computed } from 'vue'
68
123
  import { useSlideContext } from '@slidev/client'
69
- import ChapterItem from './ChapterItem.vue'
70
124
 
71
125
  // Slidevコンテキストからconfigsにアクセス
72
126
  const { $slidev } = useSlideContext()
@@ -76,7 +130,7 @@ const props = defineProps({
76
130
  nextChapter: {
77
131
  type: String,
78
132
  required: true,
79
- description: '次に始まる章のキー(例: "c2", "c3", "ap1")'
133
+ description: '次に始まる章のキー'
80
134
  },
81
135
  currentChapter: {
82
136
  type: String,
@@ -124,14 +178,14 @@ const isAppendixSection = computed(() => {
124
178
  return props.nextChapter.startsWith('ap')
125
179
  })
126
180
 
127
- // 章リストを取得する共通関数
128
- const getChapterList = (filterFn) => {
181
+ // 通常の章リスト(refと付録を除外)
182
+ const regularChapters = computed(() => {
129
183
  if (!chapters || typeof chapters !== 'object') {
130
184
  return []
131
185
  }
132
186
 
133
187
  return Object.keys(chapters)
134
- .filter(filterFn)
188
+ .filter(chapterKey => chapterKey !== 'ref' && !chapterKey.startsWith('ap'))
135
189
  .map(chapterKey => {
136
190
  const chapter = chapters[chapterKey]
137
191
  const sections = chapter?.sections || {}
@@ -142,102 +196,117 @@ const getChapterList = (filterFn) => {
142
196
  title: chapter?.title || `章 ${chapterKey}`,
143
197
  sections: sectionKeys,
144
198
  sectionData: sections,
145
- uniqueSections: getUniqueSections(chapterKey)
199
+ uniqueSections: getUniqueSections(chapterKey) // 重複除去済みセクション
146
200
  }
147
201
  })
148
- }
149
-
150
- // 通常の章リスト(refと付録を除外)
151
- const regularChapters = computed(() =>
152
- getChapterList(chapterKey => chapterKey !== 'ref' && !chapterKey.startsWith('ap'))
153
- )
202
+ })
154
203
 
155
204
  // 付録の章リスト
156
- const appendixChapters = computed(() =>
157
- getChapterList(chapterKey => chapterKey.startsWith('ap'))
158
- )
205
+ const appendixChapters = computed(() => {
206
+ if (!chapters || typeof chapters !== 'object') {
207
+ return []
208
+ }
209
+
210
+ return Object.keys(chapters)
211
+ .filter(chapterKey => chapterKey.startsWith('ap'))
212
+ .map(chapterKey => {
213
+ const chapter = chapters[chapterKey]
214
+ const sections = chapter?.sections || {}
215
+ const sectionKeys = Object.keys(sections)
216
+
217
+ return {
218
+ key: chapterKey,
219
+ title: chapter?.title || `付録 ${chapterKey}`,
220
+ sections: sectionKeys,
221
+ sectionData: sections,
222
+ uniqueSections: getUniqueSections(chapterKey) // 重複除去済みセクション
223
+ }
224
+ })
225
+ })
159
226
 
160
- // 表示する章リスト(条件に応じて切り替え)
161
- const displayChapters = computed(() =>
162
- isAppendixSection.value ? appendixChapters.value : regularChapters.value
163
- )
227
+ // セクションタイトルを取得
228
+ const getSectionTitle = (chapterKey, sectionKey) => {
229
+ const chapter = chapters[chapterKey]
230
+ const sectionData = chapter?.sections?.[sectionKey]
231
+ return sectionData?.title || `セクション ${sectionKey}`
232
+ }
164
233
 
165
234
  // 次の章かどうかを判定
166
235
  const isNextChapter = (chapterKey) => {
167
236
  return props.nextChapter === chapterKey
168
237
  }
169
238
 
170
- // サイズ設定
239
+ // サイズ設定に基づく動的スタイル
171
240
  const sizeConfigs = {
172
241
  xs: {
173
242
  columns: 4,
174
- headerPadding: 'pt-3 pb-1 px-6',
175
- headerTitle: 'text-lg',
176
- mainPadding: 'px-6 py-6',
177
- gridGap: 'gap-x-4 gap-y-4',
243
+ headerPadding: 'pt-6 pb-3 px-4 ',
244
+ headerTitle: 'text-2xl',
245
+ mainPadding: 'px-4 py-4',
246
+ gridGap: 'gap-x-4 gap-y-2',
178
247
  chapterNumber: 'text-2xl',
179
- chapterTitle: 'text-[11px]',
180
- chapterHeader: 'mb-2',
181
- sectionList: 'mt-2 space-y-1',
182
- sectionText: 'text-[9px]',
183
- appendixNumber: 'text-xl',
184
- nextBadge: 'mb-2 px-1.5 py-0.5 text-[8px]'
248
+ chapterTitle: 'text-sm font-bold',
249
+ chapterHeader: 'mb-1',
250
+ sectionList: 'ml-8 space-y-0.5',
251
+ sectionText: 'text-xs',
252
+ appendixNumber: 'text-lg',
253
+ nextBadge: 'px-1 py-0.5 text-xs'
185
254
  },
186
255
  sm: {
187
256
  columns: 3,
188
- headerPadding: 'pt-6 pb-2 px-8',
189
- headerTitle: 'text-xl',
190
- mainPadding: 'px-8 py-8',
191
- gridGap: 'gap-x-6 gap-y-5',
257
+ headerPadding: 'pt-8 pb-4 px-6',
258
+ headerTitle: 'text-3xl',
259
+ mainPadding: 'px-6 py-6',
260
+ gridGap: 'gap-x-6 gap-y-3',
192
261
  chapterNumber: 'text-3xl',
193
- chapterTitle: 'text-xs',
194
- chapterHeader: 'mb-2.5',
195
- sectionList: 'mt-2.5 space-y-1',
196
- sectionText: 'text-[10px]',
262
+ chapterTitle: 'text-base',
263
+ chapterHeader: 'mb-2',
264
+ sectionList: 'ml-10 space-y-1',
265
+ sectionText: 'text-sm',
197
266
  appendixNumber: 'text-2xl',
198
- nextBadge: 'mb-2.5 px-2 py-0.5 text-[9px]'
267
+ nextBadge: 'px-2 py-1 text-xs'
199
268
  },
200
269
  md: {
201
270
  columns: 2,
202
- headerPadding: 'pt-12 px-12',
203
- headerTitle: 'text-3xl',
204
- mainPadding: 'px-12 py-14',
205
- gridGap: 'gap-x-12 gap-y-6',
271
+ headerPadding: 'pt-14 pb-8 px-10',
272
+ headerTitle: 'text-5xl',
273
+ mainPadding: 'px-6 py-40',
274
+ gridGap: 'gap-x-12 gap-y-2',
206
275
  chapterNumber: 'text-6xl',
207
276
  chapterTitle: 'text-2xl',
208
- chapterHeader: 'mb-3',
209
- sectionList: 'mt-3 space-y-1.5',
277
+ chapterHeader: 'mb-4',
278
+ sectionList: 'ml-20 space-y-2',
210
279
  sectionText: 'text-lg',
211
280
  appendixNumber: 'text-4xl',
212
- nextBadge: 'mb-3 px-2.5 py-1 text-md'
281
+ nextBadge: 'px-3 py-1 text-sm'
213
282
  },
214
283
  lg: {
215
284
  columns: 2,
216
- headerPadding: 'pt-8 pb-4 px-16',
217
- headerTitle: 'text-4xl',
218
- mainPadding: 'px-16 py-18',
219
- gridGap: 'gap-x-16 gap-y-8',
285
+ headerPadding: 'pt-16 pb-10 px-12',
286
+ headerTitle: 'text-6xl',
287
+ mainPadding: 'px-16 py-16',
288
+ gridGap: 'gap-x-20 gap-y-10',
220
289
  chapterNumber: 'text-7xl',
221
- chapterTitle: 'text-xl',
222
- chapterHeader: 'mb-4',
223
- sectionList: 'mt-4 space-y-2',
224
- sectionText: 'text-base',
290
+ chapterTitle: 'text-3xl',
291
+ chapterHeader: 'mb-6',
292
+ sectionList: 'ml-24 space-y-3',
293
+ sectionText: 'text-xl',
225
294
  appendixNumber: 'text-5xl',
226
- nextBadge: 'mb-4 px-3 py-1.5 text-xs'
295
+ nextBadge: 'px-4 py-2 text-base'
227
296
  },
228
297
  xl: {
229
298
  columns: 1,
230
- headerPadding: 'pt-10 pb-5 px-20',
231
- headerTitle: 'text-6xl',
232
- mainPadding: 'px-20 py-24',
233
- gridGap: 'gap-x-20 gap-y-12',
234
- chapterNumber: 'text-8xl',
235
- chapterTitle: 'text-3xl',
236
- chapterHeader: 'mb-6',
237
- sectionList: 'mt-6 space-y-3',
238
- sectionText: 'text-xl',
299
+ headerPadding: 'pt-20 pb-12 px-16',
300
+ headerTitle: 'text-8xl',
301
+ mainPadding: 'px-20 py-20',
302
+ gridGap: 'gap-x-24 gap-y-12',
303
+ chapterNumber: 'text-9xl',
304
+ chapterTitle: 'text-5xl',
305
+ chapterHeader: 'mb-8',
306
+ sectionList: 'ml-32 space-y-4',
307
+ sectionText: 'text-3xl',
239
308
  appendixNumber: 'text-7xl',
240
- nextBadge: 'mb-6 px-4 py-2 text-sm'
309
+ nextBadge: 'px-5 py-2 text-lg'
241
310
  }
242
311
  }
243
312
 
@@ -245,14 +314,24 @@ const currentConfig = computed(() => sizeConfigs[props.size])
245
314
 
246
315
  // 動的クラス
247
316
  const headerClasses = computed(() => `text-left ${currentConfig.value.headerPadding}`)
248
- const headerTitleClasses = computed(() => currentConfig.value.headerTitle)
317
+ const headerTitleClasses = computed(() => `${currentConfig.value.headerTitle} font-bold text-white`)
249
318
  const mainContentClasses = computed(() => `flex-1 ${currentConfig.value.mainPadding}`)
250
319
  const gridClasses = computed(() => `grid grid-cols-${currentConfig.value.columns} ${currentConfig.value.gridGap}`)
320
+ const chapterHeaderClasses = computed(() => `flex items-center ${currentConfig.value.chapterHeader} relative z-5`)
321
+ const chapterNumberClasses = computed(() => `${currentConfig.value.chapterNumber} font-bold text-white mr-2`)
322
+ const chapterTitleClasses = computed(() => `${currentConfig.value.chapterTitle} font-bold text-white`)
323
+ const sectionListClasses = computed(() => currentConfig.value.sectionList)
324
+ const sectionItemClasses = computed(() => 'flex items-start')
325
+ const sectionTextClasses = computed(() => `text-white ${currentConfig.value.sectionText} leading-relaxed`)
326
+ const appendixNumberClasses = computed(() => `${currentConfig.value.appendixNumber} font-bold text-white mr-6 pb-2`)
327
+ const highlightBorderClasses = computed(() => 'absolute -inset-2 border-4 border-yellow-400 rounded-lg opacity-80')
328
+ const nextBadgeClasses = computed(() => `absolute -top-2 -right-2 bg-yellow-400 text-black ${currentConfig.value.nextBadge} rounded-full font-bold z-10`)
251
329
  </script>
252
330
 
253
- <style scoped>
331
+ <style>
254
332
  /* このコンポーネント専用のスタイル */
255
333
  .table-of-contents-container {
334
+ /* ページ全体をカバーする背景 */
256
335
  position: absolute !important;
257
336
  top: 0 !important;
258
337
  left: 50% !important;
@@ -264,15 +343,8 @@ const gridClasses = computed(() => `grid grid-cols-${currentConfig.value.columns
264
343
  z-index: 0 !important;
265
344
  }
266
345
 
267
- /* タイポグラフィの最適化 - Swiss Styleの核心 */
268
- div, span, h2, p {
269
- font-feature-settings: "palt", "kern";
270
- -webkit-font-smoothing: antialiased;
271
- -moz-osx-font-smoothing: grayscale;
272
- }
273
-
274
- /* タブラーナンバーの最適化 */
275
- .tabular-nums {
276
- font-variant-numeric: tabular-nums;
346
+ /* 背景色を確実に適用 */
347
+ .table-of-contents-container.bg-theme-color {
348
+ background-color: rgba(2, 132, 199, 1) !important;
277
349
  }
278
350
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slidev-theme-gtlabo",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "A Slidev theme for laboratory presentations with customizable components",
5
5
  "author": "mksmkss",
6
6
  "license": "MIT",
@@ -1,350 +0,0 @@
1
- <!-- components/TableOfContents.vue -->
2
- <template>
3
- <div class="table-of-contents-container flex flex-col justify-center bg-theme-color">
4
- <!-- ヘッダー -->
5
- <div
6
- v-if="isAppendixSection"
7
- :class="headerClasses"
8
- >
9
- <p :class="headerTitleClasses">
10
- 付録
11
- </p>
12
- </div>
13
-
14
- <!-- メインコンテンツ -->
15
- <div :class="mainContentClasses">
16
- <div class="max-w-full w-full">
17
- <!-- 通常の目次表示 -->
18
- <div
19
- v-if="!isAppendixSection"
20
- :class="gridClasses"
21
- >
22
- <div
23
- v-for="(chapter, index) in regularChapters"
24
- :key="chapter.key"
25
- class="flex flex-col"
26
- :class="{ 'relative': isNextChapter(chapter.key) }"
27
- >
28
- <!-- 次の章の強調表示 -->
29
- <div
30
- v-if="isNextChapter(chapter.key)"
31
- :class="highlightBorderClasses"
32
- />
33
- <div
34
- v-if="isNextChapter(chapter.key)"
35
- :class="nextBadgeClasses"
36
- >
37
- NEXT
38
- </div>
39
-
40
- <!-- 章番号とタイトル -->
41
- <div :class="chapterHeaderClasses">
42
- <div :class="chapterNumberClasses">
43
- {{ String(index + 1).padStart(2, '0') }}
44
- </div>
45
- <div class="flex-1 border-b border-white pb-2">
46
- <span :class="chapterTitleClasses">
47
- {{ chapter.title }}
48
- </span>
49
- </div>
50
- </div>
51
-
52
- <!-- セクション一覧(重複除去済み) -->
53
- <div :class="sectionListClasses">
54
- <div
55
- v-for="(sectionData, sectionIndex) in chapter.uniqueSections"
56
- :key="`${chapter.key}-${sectionIndex}`"
57
- :class="sectionItemClasses"
58
- >
59
- <span :class="sectionTextClasses">
60
- {{ sectionData.title }}
61
- </span>
62
- </div>
63
- </div>
64
- </div>
65
- </div>
66
-
67
- <!-- 付録の目次表示 -->
68
- <div
69
- v-else
70
- :class="gridClasses"
71
- >
72
- <div
73
- v-for="(chapter, index) in appendixChapters"
74
- :key="chapter.key"
75
- class="flex flex-col"
76
- :class="{ 'relative': isNextChapter(chapter.key) }"
77
- >
78
- <!-- 次の章の強調表示 -->
79
- <div
80
- v-if="isNextChapter(chapter.key)"
81
- :class="highlightBorderClasses"
82
- />
83
- <div
84
- v-if="isNextChapter(chapter.key)"
85
- :class="nextBadgeClasses"
86
- >
87
- NEXT
88
- </div>
89
-
90
- <!-- 付録番号とタイトル -->
91
- <div :class="chapterHeaderClasses">
92
- <div :class="appendixNumberClasses">
93
- A{{ index + 1 }}
94
- </div>
95
- <div class="flex-1 border-b border-white pb-2">
96
- <h2 :class="chapterTitleClasses">
97
- {{ chapter.title }}
98
- </h2>
99
- </div>
100
- </div>
101
-
102
- <!-- セクション一覧(重複除去済み) -->
103
- <div :class="sectionListClasses">
104
- <div
105
- v-for="(sectionData, sectionIndex) in chapter.uniqueSections"
106
- :key="`${chapter.key}-${sectionIndex}`"
107
- :class="sectionItemClasses"
108
- >
109
- <span :class="sectionTextClasses">
110
- {{ sectionData.title }}
111
- </span>
112
- </div>
113
- </div>
114
- </div>
115
- </div>
116
- </div>
117
- </div>
118
- </div>
119
- </template>
120
-
121
- <script setup>
122
- import { computed } from 'vue'
123
- import { useSlideContext } from '@slidev/client'
124
-
125
- // Slidevコンテキストからconfigsにアクセス
126
- const { $slidev } = useSlideContext()
127
- const chapters = $slidev.configs.chapters || {}
128
-
129
- const props = defineProps({
130
- nextChapter: {
131
- type: String,
132
- required: true,
133
- description: '次に始まる章のキー'
134
- },
135
- currentChapter: {
136
- type: String,
137
- required: false,
138
- description: '現在の章のキー(進捗表示用)'
139
- },
140
- size: {
141
- type: String,
142
- default: 'md',
143
- validator: (value) => ['xs', 'sm', 'md', 'lg', 'xl'].includes(value),
144
- description: '目次のサイズ(xs, sm, md, lg, xl)'
145
- }
146
- })
147
-
148
- // セクションの重複を除去する関数
149
- const getUniqueSections = (chapterKey) => {
150
- const chapter = chapters[chapterKey]
151
- if (!chapter?.sections) return []
152
-
153
- const sections = chapter.sections
154
- const sectionKeys = Object.keys(sections)
155
- const uniqueSections = []
156
- let previousTitle = null
157
-
158
- for (const sectionKey of sectionKeys) {
159
- const sectionData = sections[sectionKey]
160
- const currentTitle = sectionData?.title || `セクション ${sectionKey}`
161
-
162
- // 前のセクションと同じタイトルでない場合のみ追加
163
- if (currentTitle !== previousTitle) {
164
- uniqueSections.push({
165
- key: sectionKey,
166
- title: currentTitle,
167
- originalData: sectionData
168
- })
169
- previousTitle = currentTitle
170
- }
171
- }
172
-
173
- return uniqueSections
174
- }
175
-
176
- // 次の章が付録かどうかを判定
177
- const isAppendixSection = computed(() => {
178
- return props.nextChapter.startsWith('ap')
179
- })
180
-
181
- // 通常の章リスト(refと付録を除外)
182
- const regularChapters = computed(() => {
183
- if (!chapters || typeof chapters !== 'object') {
184
- return []
185
- }
186
-
187
- return Object.keys(chapters)
188
- .filter(chapterKey => chapterKey !== 'ref' && !chapterKey.startsWith('ap'))
189
- .map(chapterKey => {
190
- const chapter = chapters[chapterKey]
191
- const sections = chapter?.sections || {}
192
- const sectionKeys = Object.keys(sections)
193
-
194
- return {
195
- key: chapterKey,
196
- title: chapter?.title || `章 ${chapterKey}`,
197
- sections: sectionKeys,
198
- sectionData: sections,
199
- uniqueSections: getUniqueSections(chapterKey) // 重複除去済みセクション
200
- }
201
- })
202
- })
203
-
204
- // 付録の章リスト
205
- const appendixChapters = computed(() => {
206
- if (!chapters || typeof chapters !== 'object') {
207
- return []
208
- }
209
-
210
- return Object.keys(chapters)
211
- .filter(chapterKey => chapterKey.startsWith('ap'))
212
- .map(chapterKey => {
213
- const chapter = chapters[chapterKey]
214
- const sections = chapter?.sections || {}
215
- const sectionKeys = Object.keys(sections)
216
-
217
- return {
218
- key: chapterKey,
219
- title: chapter?.title || `付録 ${chapterKey}`,
220
- sections: sectionKeys,
221
- sectionData: sections,
222
- uniqueSections: getUniqueSections(chapterKey) // 重複除去済みセクション
223
- }
224
- })
225
- })
226
-
227
- // セクションタイトルを取得
228
- const getSectionTitle = (chapterKey, sectionKey) => {
229
- const chapter = chapters[chapterKey]
230
- const sectionData = chapter?.sections?.[sectionKey]
231
- return sectionData?.title || `セクション ${sectionKey}`
232
- }
233
-
234
- // 次の章かどうかを判定
235
- const isNextChapter = (chapterKey) => {
236
- return props.nextChapter === chapterKey
237
- }
238
-
239
- // サイズ設定に基づく動的スタイル
240
- const sizeConfigs = {
241
- xs: {
242
- columns: 4,
243
- headerPadding: 'pt-6 pb-3 px-4 ',
244
- headerTitle: 'text-2xl',
245
- mainPadding: 'px-4 py-4',
246
- gridGap: 'gap-x-4 gap-y-2',
247
- chapterNumber: 'text-2xl',
248
- chapterTitle: 'text-sm font-bold',
249
- chapterHeader: 'mb-1',
250
- sectionList: 'ml-8 space-y-0.5',
251
- sectionText: 'text-xs',
252
- appendixNumber: 'text-lg',
253
- nextBadge: 'px-1 py-0.5 text-xs'
254
- },
255
- sm: {
256
- columns: 3,
257
- headerPadding: 'pt-8 pb-4 px-6',
258
- headerTitle: 'text-3xl',
259
- mainPadding: 'px-6 py-6',
260
- gridGap: 'gap-x-6 gap-y-3',
261
- chapterNumber: 'text-3xl',
262
- chapterTitle: 'text-base',
263
- chapterHeader: 'mb-2',
264
- sectionList: 'ml-10 space-y-1',
265
- sectionText: 'text-sm',
266
- appendixNumber: 'text-2xl',
267
- nextBadge: 'px-2 py-1 text-xs'
268
- },
269
- md: {
270
- columns: 2,
271
- headerPadding: 'pt-14 pb-8 px-10',
272
- headerTitle: 'text-5xl',
273
- mainPadding: 'px-6 py-40',
274
- gridGap: 'gap-x-12 gap-y-2',
275
- chapterNumber: 'text-6xl',
276
- chapterTitle: 'text-2xl',
277
- chapterHeader: 'mb-4',
278
- sectionList: 'ml-20 space-y-2',
279
- sectionText: 'text-lg',
280
- appendixNumber: 'text-4xl',
281
- nextBadge: 'px-3 py-1 text-sm'
282
- },
283
- lg: {
284
- columns: 2,
285
- headerPadding: 'pt-16 pb-10 px-12',
286
- headerTitle: 'text-6xl',
287
- mainPadding: 'px-16 py-16',
288
- gridGap: 'gap-x-20 gap-y-10',
289
- chapterNumber: 'text-7xl',
290
- chapterTitle: 'text-3xl',
291
- chapterHeader: 'mb-6',
292
- sectionList: 'ml-24 space-y-3',
293
- sectionText: 'text-xl',
294
- appendixNumber: 'text-5xl',
295
- nextBadge: 'px-4 py-2 text-base'
296
- },
297
- xl: {
298
- columns: 1,
299
- headerPadding: 'pt-20 pb-12 px-16',
300
- headerTitle: 'text-8xl',
301
- mainPadding: 'px-20 py-20',
302
- gridGap: 'gap-x-24 gap-y-12',
303
- chapterNumber: 'text-9xl',
304
- chapterTitle: 'text-5xl',
305
- chapterHeader: 'mb-8',
306
- sectionList: 'ml-32 space-y-4',
307
- sectionText: 'text-3xl',
308
- appendixNumber: 'text-7xl',
309
- nextBadge: 'px-5 py-2 text-lg'
310
- }
311
- }
312
-
313
- const currentConfig = computed(() => sizeConfigs[props.size])
314
-
315
- // 動的クラス
316
- const headerClasses = computed(() => `text-left ${currentConfig.value.headerPadding}`)
317
- const headerTitleClasses = computed(() => `${currentConfig.value.headerTitle} font-bold text-white`)
318
- const mainContentClasses = computed(() => `flex-1 ${currentConfig.value.mainPadding}`)
319
- const gridClasses = computed(() => `grid grid-cols-${currentConfig.value.columns} ${currentConfig.value.gridGap}`)
320
- const chapterHeaderClasses = computed(() => `flex items-center ${currentConfig.value.chapterHeader} relative z-5`)
321
- const chapterNumberClasses = computed(() => `${currentConfig.value.chapterNumber} font-bold text-white mr-2`)
322
- const chapterTitleClasses = computed(() => `${currentConfig.value.chapterTitle} font-bold text-white`)
323
- const sectionListClasses = computed(() => currentConfig.value.sectionList)
324
- const sectionItemClasses = computed(() => 'flex items-start')
325
- const sectionTextClasses = computed(() => `text-white ${currentConfig.value.sectionText} leading-relaxed`)
326
- const appendixNumberClasses = computed(() => `${currentConfig.value.appendixNumber} font-bold text-white mr-6 pb-2`)
327
- const highlightBorderClasses = computed(() => 'absolute -inset-2 border-4 border-yellow-400 rounded-lg opacity-80')
328
- const nextBadgeClasses = computed(() => `absolute -top-2 -right-2 bg-yellow-400 text-black ${currentConfig.value.nextBadge} rounded-full font-bold z-10`)
329
- </script>
330
-
331
- <style>
332
- /* このコンポーネント専用のスタイル */
333
- .table-of-contents-container {
334
- /* ページ全体をカバーする背景 */
335
- position: absolute !important;
336
- top: 0 !important;
337
- left: 50% !important;
338
- transform: translateX(-50%) !important;
339
- width: 100% !important;
340
- height: 100% !important;
341
- margin: 0 !important;
342
- box-sizing: border-box !important;
343
- z-index: 0 !important;
344
- }
345
-
346
- /* 背景色を確実に適用 */
347
- .table-of-contents-container.bg-theme-color {
348
- background-color: rgba(2, 132, 199, 1) !important;
349
- }
350
- </style>