slidev-theme-gtlabo 1.0.0
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.
- package/LICENSE +21 -0
- package/README.md +298 -0
- package/components/Citation.vue +253 -0
- package/components/CitationListPage.vue +249 -0
- package/components/Header.vue +354 -0
- package/components/MathText.vue +557 -0
- package/components/SectionDivider.vue +24 -0
- package/components/SectionTitle.vue +70 -0
- package/components/SubSectionTitle.vue +67 -0
- package/components/TableOfContents.vue +349 -0
- package/components/TextColorBox.vue +97 -0
- package/layouts/cover.vue +69 -0
- package/layouts/default.vue +12 -0
- package/layouts/intro.vue +7 -0
- package/package.json +65 -0
- package/setup/shiki.ts +11 -0
- package/styles/index.ts +3 -0
- package/styles/layout.css +24 -0
- package/uno.config.ts +13 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="h-full flex flex-col">
|
|
3
|
+
<!-- ヘッダー部分(Headerコンポーネントを使用) -->
|
|
4
|
+
<Header
|
|
5
|
+
:chapter-data="{ title: '参考文献' }"
|
|
6
|
+
chapter="ref"
|
|
7
|
+
/>
|
|
8
|
+
|
|
9
|
+
<!-- 参考文献リスト -->
|
|
10
|
+
<div class="flex-1 overflow-y-auto px-4">
|
|
11
|
+
<div
|
|
12
|
+
v-if="citationsList.length > 0"
|
|
13
|
+
class="space-y-6"
|
|
14
|
+
>
|
|
15
|
+
<div
|
|
16
|
+
v-for="(citation, index) in citationsList"
|
|
17
|
+
:key="citation.key"
|
|
18
|
+
class="citation-item border-l-4 border-sky-500 pl-6 py-2"
|
|
19
|
+
>
|
|
20
|
+
<!-- 引用キー -->
|
|
21
|
+
<div class="text-lg font-semibold text-sky-700 mb-2">
|
|
22
|
+
[{{ citation.key }}]
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<!-- 引用情報 -->
|
|
26
|
+
<div class="text-gray-800 leading-relaxed">
|
|
27
|
+
<!-- 著者 -->
|
|
28
|
+
<span
|
|
29
|
+
v-if="citation.data.author"
|
|
30
|
+
class="font-medium"
|
|
31
|
+
>
|
|
32
|
+
{{ citation.data.author }}
|
|
33
|
+
</span>
|
|
34
|
+
|
|
35
|
+
<!-- タイトル -->
|
|
36
|
+
<span
|
|
37
|
+
v-if="citation.data.title"
|
|
38
|
+
class="italic"
|
|
39
|
+
>
|
|
40
|
+
<span v-if="citation.data.author">. </span>
|
|
41
|
+
"{{ citation.data.title }}"
|
|
42
|
+
</span>
|
|
43
|
+
|
|
44
|
+
<!-- 雑誌名 -->
|
|
45
|
+
<span
|
|
46
|
+
v-if="citation.data.journal"
|
|
47
|
+
class="font-medium"
|
|
48
|
+
>
|
|
49
|
+
<span v-if="citation.data.title || citation.data.author">. </span>
|
|
50
|
+
<em>{{ citation.data.journal }}</em>
|
|
51
|
+
</span>
|
|
52
|
+
|
|
53
|
+
<!-- 巻号 -->
|
|
54
|
+
<span v-if="citation.data.volume">
|
|
55
|
+
<span v-if="citation.data.journal">, </span>
|
|
56
|
+
Vol. {{ citation.data.volume }}
|
|
57
|
+
</span>
|
|
58
|
+
|
|
59
|
+
<span v-if="citation.data.number">
|
|
60
|
+
<span v-if="citation.data.volume">, </span>
|
|
61
|
+
<span v-else-if="citation.data.journal">, </span>
|
|
62
|
+
No. {{ citation.data.number }}
|
|
63
|
+
</span>
|
|
64
|
+
|
|
65
|
+
<!-- ページ -->
|
|
66
|
+
<span v-if="citation.data.pages">
|
|
67
|
+
<span v-if="citation.data.volume || citation.data.number">, </span>
|
|
68
|
+
<span v-else-if="citation.data.journal">, </span>
|
|
69
|
+
pp. {{ citation.data.pages }}
|
|
70
|
+
</span>
|
|
71
|
+
|
|
72
|
+
<!-- 出版社 -->
|
|
73
|
+
<span v-if="citation.data.publisher">
|
|
74
|
+
<span v-if="citation.data.journal || citation.data.pages">, </span>
|
|
75
|
+
{{ citation.data.publisher }}
|
|
76
|
+
</span>
|
|
77
|
+
|
|
78
|
+
<!-- 年 -->
|
|
79
|
+
<span
|
|
80
|
+
v-if="citation.data.year"
|
|
81
|
+
class="font-medium"
|
|
82
|
+
>
|
|
83
|
+
<span v-if="citation.data.author || citation.data.title || citation.data.journal || citation.data.publisher">, </span>
|
|
84
|
+
({{ citation.data.year }})
|
|
85
|
+
</span>
|
|
86
|
+
|
|
87
|
+
<!-- DOI -->
|
|
88
|
+
<div
|
|
89
|
+
v-if="citation.data.doi"
|
|
90
|
+
class="mt-2 text-sm text-blue-600"
|
|
91
|
+
>
|
|
92
|
+
DOI: <a
|
|
93
|
+
:href="`https://doi.org/${citation.data.doi}`"
|
|
94
|
+
target="_blank"
|
|
95
|
+
class="hover:underline"
|
|
96
|
+
>
|
|
97
|
+
{{ citation.data.doi }}
|
|
98
|
+
</a>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<!-- URL -->
|
|
102
|
+
<div
|
|
103
|
+
v-if="citation.data.url && !citation.data.doi"
|
|
104
|
+
class="mt-2 text-sm text-blue-600"
|
|
105
|
+
>
|
|
106
|
+
URL: <a
|
|
107
|
+
:href="citation.data.url"
|
|
108
|
+
target="_blank"
|
|
109
|
+
class="hover:underline break-all"
|
|
110
|
+
>
|
|
111
|
+
{{ citation.data.url }}
|
|
112
|
+
</a>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<!-- ISSN -->
|
|
116
|
+
<div
|
|
117
|
+
v-if="citation.data.issn"
|
|
118
|
+
class="mt-1 text-sm text-gray-600"
|
|
119
|
+
>
|
|
120
|
+
ISSN: {{ citation.data.issn }}
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<!-- 参考文献がない場合 -->
|
|
127
|
+
<div
|
|
128
|
+
v-else
|
|
129
|
+
class="text-center text-gray-500 mt-16"
|
|
130
|
+
>
|
|
131
|
+
<div class="text-xl">
|
|
132
|
+
参考文献が設定されていません
|
|
133
|
+
</div>
|
|
134
|
+
<div class="text-sm mt-2">
|
|
135
|
+
frontmatterの<code class="bg-gray-200 px-2 py-1 rounded">citations</code>セクションに参考文献を追加してください
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<!-- フッター(必要に応じて) -->
|
|
141
|
+
<div class="flex-shrink-0 mt-8 pt-4 border-t border-gray-300 px-4">
|
|
142
|
+
<div class="text-sm text-gray-600 text-center">
|
|
143
|
+
{{ citationsList.length }} 件の参考文献
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
</template>
|
|
148
|
+
|
|
149
|
+
<script setup>
|
|
150
|
+
import { computed } from 'vue'
|
|
151
|
+
import { useSlideContext } from '@slidev/client'
|
|
152
|
+
// Headerコンポーネントをimport
|
|
153
|
+
import Header from './Header.vue'
|
|
154
|
+
|
|
155
|
+
// Slidevコンテキストからconfigsにアクセス
|
|
156
|
+
const { $slidev } = useSlideContext()
|
|
157
|
+
const citations = $slidev.configs.citations || {}
|
|
158
|
+
|
|
159
|
+
// プロパティ定義(必要に応じて)
|
|
160
|
+
const props = defineProps({
|
|
161
|
+
// 表示スタイルのカスタマイズ用
|
|
162
|
+
style: {
|
|
163
|
+
type: String,
|
|
164
|
+
default: 'academic', // 'academic', 'ieee', 'apa' など
|
|
165
|
+
},
|
|
166
|
+
// 表示順序
|
|
167
|
+
sortBy: {
|
|
168
|
+
type: String,
|
|
169
|
+
default: 'key', // 'key', 'author', 'year' など
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
// 参考文献リストを生成
|
|
174
|
+
const citationsList = computed(() => {
|
|
175
|
+
if (!citations || typeof citations !== 'object') {
|
|
176
|
+
return []
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const list = Object.keys(citations).map(key => ({
|
|
180
|
+
key,
|
|
181
|
+
data: citations[key]
|
|
182
|
+
}))
|
|
183
|
+
|
|
184
|
+
// ソート処理
|
|
185
|
+
switch (props.sortBy) {
|
|
186
|
+
case 'author':
|
|
187
|
+
return list.sort((a, b) => {
|
|
188
|
+
const authorA = a.data.author || ''
|
|
189
|
+
const authorB = b.data.author || ''
|
|
190
|
+
return authorA.localeCompare(authorB)
|
|
191
|
+
})
|
|
192
|
+
case 'year':
|
|
193
|
+
return list.sort((a, b) => {
|
|
194
|
+
const yearA = parseInt(a.data.year) || 0
|
|
195
|
+
const yearB = parseInt(b.data.year) || 0
|
|
196
|
+
return yearB - yearA // 降順(新しい順)
|
|
197
|
+
})
|
|
198
|
+
case 'key':
|
|
199
|
+
default:
|
|
200
|
+
return list.sort((a, b) => a.key.localeCompare(b.key))
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// スタイル別のフォーマット関数(将来的な拡張用)
|
|
205
|
+
const formatCitation = (citation, style = 'academic') => {
|
|
206
|
+
// 現在は academic スタイルのみ実装
|
|
207
|
+
// 将来的にIEEE、APA等のスタイルを追加可能
|
|
208
|
+
return citation
|
|
209
|
+
}
|
|
210
|
+
</script>
|
|
211
|
+
|
|
212
|
+
<style scoped>
|
|
213
|
+
.citation-item {
|
|
214
|
+
transition: all 0.2s ease-in-out;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.citation-item:hover {
|
|
218
|
+
background-color: rgba(14, 165, 233, 0.05);
|
|
219
|
+
border-left-width: 6px;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* スクロールバーのスタイリング */
|
|
223
|
+
.overflow-y-auto {
|
|
224
|
+
scrollbar-width: thin;
|
|
225
|
+
scrollbar-color: rgba(14, 165, 233, 0.3) transparent;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.overflow-y-auto::-webkit-scrollbar {
|
|
229
|
+
width: 6px;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.overflow-y-auto::-webkit-scrollbar-track {
|
|
233
|
+
background: transparent;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.overflow-y-auto::-webkit-scrollbar-thumb {
|
|
237
|
+
background-color: rgba(14, 165, 233, 0.3);
|
|
238
|
+
border-radius: 3px;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.overflow-y-auto::-webkit-scrollbar-thumb:hover {
|
|
242
|
+
background-color: rgba(14, 165, 233, 0.5);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* コードブロックのスタイル */
|
|
246
|
+
code {
|
|
247
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
248
|
+
}
|
|
249
|
+
</style>
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex flex-col px-4 -mx-3 -mt-4 mb-2">
|
|
3
|
+
<div class="flex ">
|
|
4
|
+
<div class="flex flex-col gap-y-2 w-11/12">
|
|
5
|
+
<!-- プログレス部分 -->
|
|
6
|
+
<div class="flex items-center gap-2 overflow-x-auto">
|
|
7
|
+
<div
|
|
8
|
+
v-for="(chapter, index) in displayChapterList"
|
|
9
|
+
:key="chapter.key"
|
|
10
|
+
class="flex flex-col items-center min-w-35 py-1"
|
|
11
|
+
>
|
|
12
|
+
<!-- 章タイトル -->
|
|
13
|
+
<div class="flex flex-1 text-md font-semibold text-gray-800 text-left w-full whitespace-nowrap mb-2">
|
|
14
|
+
{{ chapter.title }}
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<!-- 進捗バー -->
|
|
18
|
+
<div class="flex flex-1 w-full">
|
|
19
|
+
<!-- セクションが複数ある場合 -->
|
|
20
|
+
<div
|
|
21
|
+
v-if="chapter.sections.length > 0"
|
|
22
|
+
class="flex w-full h-1 gap-0.8 rounded-full overflow-hidden"
|
|
23
|
+
>
|
|
24
|
+
<div
|
|
25
|
+
v-for="(section, sectionIndex) in chapter.sections"
|
|
26
|
+
:key="sectionIndex"
|
|
27
|
+
class="flex-1 h-full transition-all duration-200 ease-in-out cursor-help rounded-full"
|
|
28
|
+
:class="{
|
|
29
|
+
'bg-gray-700': isCompleted(index, sectionIndex),
|
|
30
|
+
'bg-blue-500': isCurrent(index, sectionIndex),
|
|
31
|
+
'bg-gray-300': !isCompleted(index, sectionIndex) && !isCurrent(index, sectionIndex)
|
|
32
|
+
}"
|
|
33
|
+
:style="{
|
|
34
|
+
width: getSectionWidth(chapter.sections.length) + '%'
|
|
35
|
+
}"
|
|
36
|
+
:title="getSectionTitle(chapter, sectionIndex)"
|
|
37
|
+
/>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- セクションがない場合(1節のみ) -->
|
|
41
|
+
<div
|
|
42
|
+
v-else
|
|
43
|
+
class="flex w-full h-1 bg-gray-300 rounded-full overflow-hidden"
|
|
44
|
+
>
|
|
45
|
+
<div
|
|
46
|
+
class="flex-1 h-full transition-all duration-200 ease-in-out cursor-help"
|
|
47
|
+
:class="{
|
|
48
|
+
'bg-gray-700': isCompleted(index, 0),
|
|
49
|
+
'bg-blue-500': isCurrent(index, 0),
|
|
50
|
+
'bg-gray-300': !isCompleted(index, 0) && !isCurrent(index, 0)
|
|
51
|
+
}"
|
|
52
|
+
:title="chapter.title"
|
|
53
|
+
/>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<!-- 章間の接続線(最後の章以外) -->
|
|
58
|
+
<div
|
|
59
|
+
v-if="index < displayChapterList.length - 1"
|
|
60
|
+
class="text-gray-400 transition-colors duration-200 mx-1"
|
|
61
|
+
:class="{ 'text-gray-700': isChapterCompleted(index) }"
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<!-- タイトル部分 -->
|
|
67
|
+
<div class="flex flex-col justify-center items-left">
|
|
68
|
+
<p
|
|
69
|
+
v-if="displayData"
|
|
70
|
+
class="text-4xl font-bold text-slate-800 inline-block py-4 "
|
|
71
|
+
>
|
|
72
|
+
{{ displayData.title }}
|
|
73
|
+
</p>
|
|
74
|
+
<p
|
|
75
|
+
v-else
|
|
76
|
+
class="text-4xl font-bold text-slate-800 inline-block py-4 bg-amber"
|
|
77
|
+
>
|
|
78
|
+
{{ isSection ? 'セクションが見つかりません' : '章が見つかりません' }}
|
|
79
|
+
</p>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<!-- ページカウンター -->
|
|
84
|
+
<div class="flex w-1/12 items-center justify-center text-gray-800">
|
|
85
|
+
<div class="border-3 border-stone-800 rounded-md flex flex-col w-14 divide-y-2 divide-stone-800">
|
|
86
|
+
<div class="flex flex-1 justify-center items-center text-4xl font-bold">
|
|
87
|
+
{{ adjustedCurrentPage }}
|
|
88
|
+
</div>
|
|
89
|
+
<div class="flex flex-1 justify-center items-center text-xl font-semibold max-h-6">
|
|
90
|
+
{{ adjustedTotal }}
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="flex w-4/5 bg-sky-600 h-1.5 rounded-full -mx-6 " />
|
|
96
|
+
</div>
|
|
97
|
+
</template>
|
|
98
|
+
|
|
99
|
+
<script setup>
|
|
100
|
+
import { computed } from 'vue'
|
|
101
|
+
import { useSlideContext } from '@slidev/client'
|
|
102
|
+
|
|
103
|
+
// Slidevコンテキストからconfigsにアクセス
|
|
104
|
+
const { $slidev } = useSlideContext()
|
|
105
|
+
const chapters = $slidev.configs.chapters || {}
|
|
106
|
+
|
|
107
|
+
const props = defineProps({
|
|
108
|
+
// タイトル用のプロパティ
|
|
109
|
+
chapter: {
|
|
110
|
+
type: String,
|
|
111
|
+
required: false
|
|
112
|
+
},
|
|
113
|
+
chapterData: {
|
|
114
|
+
type: Object,
|
|
115
|
+
required: false
|
|
116
|
+
},
|
|
117
|
+
currentSection: {
|
|
118
|
+
type: String,
|
|
119
|
+
required: false
|
|
120
|
+
},
|
|
121
|
+
// プログレス用のプロパティ(タイトル用と共通利用)
|
|
122
|
+
currentChapter: {
|
|
123
|
+
type: String,
|
|
124
|
+
required: false,
|
|
125
|
+
default: null
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
// セクションかどうかを判定
|
|
130
|
+
const isSection = computed(() => {
|
|
131
|
+
return !!props.currentSection
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// タイトル表示用のデータ
|
|
135
|
+
const displayData = computed(() => {
|
|
136
|
+
// chapterDataが直接渡された場合はそれを使用
|
|
137
|
+
if (props.chapterData) {
|
|
138
|
+
return props.chapterData
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// currentSectionが指定されている場合、セクションデータを取得
|
|
142
|
+
if (props.currentSection && (props.chapter || props.currentChapter)) {
|
|
143
|
+
const chapterKey = props.chapter || props.currentChapter
|
|
144
|
+
const chapterData = chapters[chapterKey]
|
|
145
|
+
if (chapterData?.sections?.[props.currentSection]) {
|
|
146
|
+
return chapterData.sections[props.currentSection]
|
|
147
|
+
}
|
|
148
|
+
return null
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// chapterキーが渡された場合は内部データから取得
|
|
152
|
+
const chapterKey = props.chapter || props.currentChapter
|
|
153
|
+
if (chapterKey && chapters[chapterKey]) {
|
|
154
|
+
return chapters[chapterKey]
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// どちらも渡されなかった場合はnull
|
|
158
|
+
return null
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
// 参考文献ページかどうかを判定
|
|
162
|
+
const isReferencePage = computed(() => {
|
|
163
|
+
const currentChapterKey = props.currentChapter || props.chapter
|
|
164
|
+
return currentChapterKey === 'ref'
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
// 現在のページがappendixかどうかを判定
|
|
168
|
+
const isCurrentPageAppendix = computed(() => {
|
|
169
|
+
const currentChapterKey = props.currentChapter || props.chapter
|
|
170
|
+
return currentChapterKey && currentChapterKey.startsWith('ap')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
// 章リストを生成(refを除外し、appendixの表示を条件分岐)
|
|
174
|
+
const chapterList = computed(() => {
|
|
175
|
+
if (!chapters || typeof chapters !== 'object') {
|
|
176
|
+
return []
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return Object.keys(chapters)
|
|
180
|
+
.filter(chapterKey => chapterKey !== 'ref') // refを除外
|
|
181
|
+
.map(chapterKey => {
|
|
182
|
+
const chapter = chapters[chapterKey]
|
|
183
|
+
const sections = chapter?.sections || {}
|
|
184
|
+
const sectionKeys = Object.keys(sections)
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
key: chapterKey,
|
|
188
|
+
title: chapter?.title || `章 ${chapterKey}`,
|
|
189
|
+
sections: sectionKeys,
|
|
190
|
+
sectionData: sections,
|
|
191
|
+
isAppendix: chapterKey.startsWith('ap')
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
// 表示する章リスト(appendixページかどうかで切り替え)
|
|
197
|
+
const displayChapterList = computed(() => {
|
|
198
|
+
if (isCurrentPageAppendix.value) {
|
|
199
|
+
// appendixページの場合はappendixのみ表示
|
|
200
|
+
return chapterList.value.filter(chapter => chapter.isAppendix)
|
|
201
|
+
} else {
|
|
202
|
+
// 通常ページの場合はappendixを除外
|
|
203
|
+
return chapterList.value.filter(chapter => !chapter.isAppendix)
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
// appendixの開始ページを計算
|
|
208
|
+
const appendixStartPage = computed(() => {
|
|
209
|
+
const totalPages = $slidev.nav.total
|
|
210
|
+
const appendixChapters = chapterList.value.filter(chapter => chapter.isAppendix)
|
|
211
|
+
|
|
212
|
+
console.log('appendixStartPage計算:')
|
|
213
|
+
console.log(' totalPages:', totalPages)
|
|
214
|
+
console.log(' appendixChapters:', appendixChapters)
|
|
215
|
+
|
|
216
|
+
if (appendixChapters.length === 0) {
|
|
217
|
+
console.log(' appendixなし - 結果:', totalPages + 1)
|
|
218
|
+
return totalPages + 1 // appendixがない場合は存在しないページ番号を返す
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// appendixの実際のセクション数を計算
|
|
222
|
+
const appendixSectionCount = appendixChapters.reduce((total, chapter) => {
|
|
223
|
+
const sectionCount = Math.max(1, chapter.sections.length)
|
|
224
|
+
console.log(` 章${chapter.key}: ${sectionCount}セクション`)
|
|
225
|
+
return total + sectionCount
|
|
226
|
+
}, 0)
|
|
227
|
+
|
|
228
|
+
console.log(' appendixSectionCount:', appendixSectionCount)
|
|
229
|
+
const result = totalPages - appendixSectionCount + 1
|
|
230
|
+
console.log(' appendixあり - 結果:', result)
|
|
231
|
+
return result
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
// 調整されたページ総数(常にappendix前まで、appendixがない場合は全体から表紙を除く)
|
|
235
|
+
const adjustedTotal = computed(() => {
|
|
236
|
+
// chapterList(全体)からappendixを検索
|
|
237
|
+
const appendixChapters = chapterList.value.filter(chapter => chapter.isAppendix)
|
|
238
|
+
|
|
239
|
+
// デバッグ用ログ
|
|
240
|
+
console.log('全chapterList:', chapterList.value)
|
|
241
|
+
console.log('appendixChapters:', appendixChapters)
|
|
242
|
+
console.log('appendixChapters.length:', appendixChapters.length)
|
|
243
|
+
console.log('$slidev.nav.total:', $slidev.nav.total)
|
|
244
|
+
console.log('appendixStartPage.value:', appendixStartPage.value)
|
|
245
|
+
|
|
246
|
+
if (appendixChapters.length === 0) {
|
|
247
|
+
// appendixがない場合は全体のページ数から表紙を除く
|
|
248
|
+
console.log('appendixなし - 計算結果:', $slidev.nav.total - 1)
|
|
249
|
+
return $slidev.nav.total - 1
|
|
250
|
+
} else {
|
|
251
|
+
// appendixがある場合はappendix開始前のページ数から表紙を除く
|
|
252
|
+
console.log('appendixあり - 計算結果:', appendixStartPage.value - 2)
|
|
253
|
+
return appendixStartPage.value - 2
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
// 表紙を除いた現在のページ番号
|
|
258
|
+
const adjustedCurrentPage = computed(() => {
|
|
259
|
+
return Math.max(1, $slidev.nav.currentPage - 1)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
// 現在の章のインデックスを取得(表示リスト内での位置)
|
|
263
|
+
const currentChapterIndex = computed(() => {
|
|
264
|
+
const currentChapterKey = props.currentChapter || props.chapter
|
|
265
|
+
if (!currentChapterKey) return -1
|
|
266
|
+
return displayChapterList.value.findIndex(chapter => chapter.key === currentChapterKey)
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
// 現在の節のインデックスを取得
|
|
270
|
+
const currentSectionIndex = computed(() => {
|
|
271
|
+
const currentChapterKey = props.currentChapter || props.chapter
|
|
272
|
+
if (!currentChapterKey || !props.currentSection) return -1
|
|
273
|
+
const chapter = displayChapterList.value[currentChapterIndex.value]
|
|
274
|
+
if (!chapter) return -1
|
|
275
|
+
return chapter.sections.findIndex(section => section === props.currentSection)
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
// セクションタイトルを取得(ツールチップ用)
|
|
279
|
+
const getSectionTitle = (chapter, sectionIndex) => {
|
|
280
|
+
if (chapter.sections.length === 0) {
|
|
281
|
+
return chapter.title
|
|
282
|
+
}
|
|
283
|
+
const sectionKey = chapter.sections[sectionIndex]
|
|
284
|
+
const sectionData = chapter.sectionData[sectionKey]
|
|
285
|
+
return sectionData?.title || `セクション ${sectionIndex + 1}`
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// セクションの幅を計算(%)
|
|
289
|
+
const getSectionWidth = (totalSections) => {
|
|
290
|
+
if (totalSections <= 1) return 100
|
|
291
|
+
return 100 / totalSections
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 指定されたセクションが完了しているかチェック
|
|
295
|
+
const isCompleted = (chapterIndex, sectionIndex) => {
|
|
296
|
+
// 参考文献ページの場合はすべて完了状態
|
|
297
|
+
if (isReferencePage.value) {
|
|
298
|
+
return true
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const currentIdx = currentChapterIndex.value
|
|
302
|
+
const currentSecIdx = currentSectionIndex.value
|
|
303
|
+
|
|
304
|
+
if (currentIdx === -1) return false
|
|
305
|
+
|
|
306
|
+
// 現在の章より前の章はすべて完了
|
|
307
|
+
if (chapterIndex < currentIdx) return true
|
|
308
|
+
|
|
309
|
+
// 現在の章の場合
|
|
310
|
+
if (chapterIndex === currentIdx) {
|
|
311
|
+
// セクションが指定されている場合
|
|
312
|
+
if (currentSecIdx >= 0) {
|
|
313
|
+
return sectionIndex < currentSecIdx
|
|
314
|
+
}
|
|
315
|
+
// セクションが指定されていない場合は何も完了していない
|
|
316
|
+
return false
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return false
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// 指定されたセクションが現在の位置かチェック
|
|
323
|
+
const isCurrent = (chapterIndex, sectionIndex) => {
|
|
324
|
+
// 参考文献ページの場合は何も現在位置にしない(すべて完了状態)
|
|
325
|
+
if (isReferencePage.value) {
|
|
326
|
+
return false
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const currentIdx = currentChapterIndex.value
|
|
330
|
+
const currentSecIdx = currentSectionIndex.value
|
|
331
|
+
|
|
332
|
+
if (currentIdx === -1) return false
|
|
333
|
+
if (chapterIndex !== currentIdx) return false
|
|
334
|
+
|
|
335
|
+
// セクションが指定されている場合
|
|
336
|
+
if (currentSecIdx >= 0) {
|
|
337
|
+
return sectionIndex === currentSecIdx
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// セクションが指定されていない場合は最初のセクションが現在位置
|
|
341
|
+
return sectionIndex === 0
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// 章全体が完了しているかチェック
|
|
345
|
+
const isChapterCompleted = (chapterIndex) => {
|
|
346
|
+
// 参考文献ページの場合はすべて完了状態
|
|
347
|
+
if (isReferencePage.value) {
|
|
348
|
+
return true
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const currentIdx = currentChapterIndex.value
|
|
352
|
+
return currentIdx > chapterIndex
|
|
353
|
+
}
|
|
354
|
+
</script>
|