slidev-theme-gtlabo 2.0.4 → 2.1.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.
- package/components/Citation.vue +72 -89
- package/components/CitationListPage.vue +17 -5
- package/components/Header.vue +60 -14
- package/components/SectionDivider.vue +26 -11
- package/package.json +4 -4
- package/components/ChapterItem.vue +0 -93
package/components/Citation.vue
CHANGED
|
@@ -1,34 +1,29 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<span>
|
|
3
3
|
<!-- インライン引用番号 -->
|
|
4
|
-
<sup class="text-blue-600 font-semibold">
|
|
4
|
+
<sup class="text-blue-600 font-semibold cursor-help" :title="formattedCitation">
|
|
5
5
|
[{{ citationNumber }}]
|
|
6
6
|
</sup>
|
|
7
|
-
|
|
8
|
-
<!-- このコンポーネント専用の引用情報表示エリア -->
|
|
9
|
-
<div
|
|
10
|
-
v-if="shouldShowCitation"
|
|
11
|
-
class="fixed bottom-0 left-0 right-0 bg-white border-t-2 border-gray-300 shadow-lg z-40 max-h-32 overflow-y-auto"
|
|
12
|
-
style="pointer-events: none;"
|
|
13
|
-
>
|
|
14
|
-
<div class="max-w-7xl mx-auto px-4 py-2">
|
|
15
|
-
<div class="space-y-1">
|
|
16
|
-
<div class="text-xs text-gray-800">
|
|
17
|
-
<span class="font-semibold">[{{ citationNumber }}]</span>
|
|
18
|
-
{{ formattedCitation }}
|
|
19
|
-
</div>
|
|
20
|
-
</div>
|
|
21
|
-
</div>
|
|
22
|
-
</div>
|
|
23
7
|
</span>
|
|
24
8
|
</template>
|
|
25
9
|
|
|
26
10
|
<script setup>
|
|
27
|
-
import { computed, onMounted, onUnmounted,
|
|
11
|
+
import { computed, onMounted, onUnmounted, inject } from 'vue'
|
|
28
12
|
import { useSlideContext } from '@slidev/client'
|
|
29
13
|
|
|
30
|
-
const { $slidev } = useSlideContext()
|
|
31
|
-
|
|
14
|
+
const { $slidev, $page } = useSlideContext()
|
|
15
|
+
|
|
16
|
+
// 方法1: frontmatterから取得(従来通り)
|
|
17
|
+
const frontmatterCitations = $slidev.configs.citations || {}
|
|
18
|
+
|
|
19
|
+
// 方法2: injectから取得(外部ファイル対応)
|
|
20
|
+
const injectedCitations = inject('citations', {})
|
|
21
|
+
|
|
22
|
+
// 両方をマージ(frontmatter優先)
|
|
23
|
+
const citations = computed(() => ({
|
|
24
|
+
...injectedCitations,
|
|
25
|
+
...frontmatterCitations
|
|
26
|
+
}))
|
|
32
27
|
|
|
33
28
|
const props = defineProps({
|
|
34
29
|
id: {
|
|
@@ -40,12 +35,25 @@ const props = defineProps({
|
|
|
40
35
|
// グローバル引用管理の初期化
|
|
41
36
|
if (!window.citationManager) {
|
|
42
37
|
window.citationManager = {
|
|
43
|
-
citations: new Map(),
|
|
44
|
-
citationKeys:
|
|
45
|
-
|
|
38
|
+
citations: new Map(), // id -> { number, data, formatted }
|
|
39
|
+
citationKeys: [], // frontmatterのキーの順番
|
|
40
|
+
pageCitations: new Map(), // pageNumber -> Set of citation ids
|
|
41
|
+
initialized: false,
|
|
42
|
+
listeners: new Set() // 更新通知用リスナー
|
|
46
43
|
}
|
|
47
44
|
}
|
|
48
45
|
|
|
46
|
+
// 更新を通知する関数
|
|
47
|
+
const notifyListeners = () => {
|
|
48
|
+
window.citationManager.listeners.forEach(listener => {
|
|
49
|
+
try {
|
|
50
|
+
listener()
|
|
51
|
+
} catch (e) {
|
|
52
|
+
// リスナーエラーを無視
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
49
57
|
// frontmatterの順番でcitation番号を事前に割り当て
|
|
50
58
|
const initializeCitations = () => {
|
|
51
59
|
if (!window.citationManager.initialized) {
|
|
@@ -55,7 +63,7 @@ const initializeCitations = () => {
|
|
|
55
63
|
if (!window.citationManager.citations.has(key)) {
|
|
56
64
|
const data = citations[key]
|
|
57
65
|
const newCitation = {
|
|
58
|
-
number: index + 1,
|
|
66
|
+
number: index + 1,
|
|
59
67
|
data: data,
|
|
60
68
|
formatted: formatCitation(data)
|
|
61
69
|
}
|
|
@@ -67,18 +75,9 @@ const initializeCitations = () => {
|
|
|
67
75
|
}
|
|
68
76
|
}
|
|
69
77
|
|
|
70
|
-
const isMounted = ref(false)
|
|
71
|
-
|
|
72
|
-
// 現在のページ番号を取得
|
|
73
|
-
const currentPage = computed(() => {
|
|
74
|
-
const page = $slidev.nav.currentPage
|
|
75
|
-
return page
|
|
76
|
-
})
|
|
77
|
-
|
|
78
78
|
// 引用データを取得
|
|
79
79
|
const citationData = computed(() => {
|
|
80
|
-
|
|
81
|
-
return data
|
|
80
|
+
return citations[props.id] || null
|
|
82
81
|
})
|
|
83
82
|
|
|
84
83
|
// 引用番号を取得
|
|
@@ -87,7 +86,6 @@ const citationNumber = computed(() => {
|
|
|
87
86
|
return '?'
|
|
88
87
|
}
|
|
89
88
|
|
|
90
|
-
// 初期化を確実に実行
|
|
91
89
|
initializeCitations()
|
|
92
90
|
|
|
93
91
|
const citation = window.citationManager.citations.get(props.id)
|
|
@@ -112,12 +110,6 @@ const formattedCitation = computed(() => {
|
|
|
112
110
|
return formatCitation(citationData.value)
|
|
113
111
|
})
|
|
114
112
|
|
|
115
|
-
// 引用を表示すべきかどうか
|
|
116
|
-
const shouldShowCitation = computed(() => {
|
|
117
|
-
const shouldShow = isMounted.value && citationData.value !== null
|
|
118
|
-
return shouldShow
|
|
119
|
-
})
|
|
120
|
-
|
|
121
113
|
// 引用をフォーマット
|
|
122
114
|
const formatCitation = (data) => {
|
|
123
115
|
if (!data) {
|
|
@@ -126,22 +118,18 @@ const formatCitation = (data) => {
|
|
|
126
118
|
|
|
127
119
|
let citation = ''
|
|
128
120
|
|
|
129
|
-
// 著者
|
|
130
121
|
if (data.author) {
|
|
131
122
|
citation += data.author
|
|
132
123
|
}
|
|
133
124
|
|
|
134
|
-
// タイトル
|
|
135
125
|
if (data.title) {
|
|
136
126
|
citation += citation ? `, "${data.title}"` : `"${data.title}"`
|
|
137
127
|
}
|
|
138
128
|
|
|
139
|
-
// ジャーナル
|
|
140
129
|
if (data.journal) {
|
|
141
130
|
citation += citation ? `, ${data.journal}` : data.journal
|
|
142
131
|
}
|
|
143
132
|
|
|
144
|
-
// ボリューム・ナンバー
|
|
145
133
|
if (data.volume && data.number) {
|
|
146
134
|
citation += `, Vol. ${data.volume}, No. ${data.number}`
|
|
147
135
|
} else if (data.volume) {
|
|
@@ -150,82 +138,77 @@ const formatCitation = (data) => {
|
|
|
150
138
|
citation += `, No. ${data.number}`
|
|
151
139
|
}
|
|
152
140
|
|
|
153
|
-
// ページ
|
|
154
141
|
if (data.pages) {
|
|
155
142
|
citation += `, pp. ${data.pages}`
|
|
156
143
|
}
|
|
157
144
|
|
|
158
|
-
// 年
|
|
159
145
|
if (data.year) {
|
|
160
146
|
citation += citation ? ` (${data.year})` : data.year
|
|
161
147
|
}
|
|
162
148
|
|
|
163
|
-
// 出版社
|
|
164
149
|
if (data.publisher) {
|
|
165
150
|
citation += citation ? `, ${data.publisher}` : data.publisher
|
|
166
151
|
}
|
|
167
152
|
|
|
168
|
-
// URL
|
|
169
153
|
if (data.url) {
|
|
170
154
|
citation += citation ? `, ${data.url}` : data.url
|
|
171
155
|
}
|
|
172
156
|
|
|
173
|
-
// ISSN
|
|
174
157
|
if (data.issn) {
|
|
175
158
|
citation += `, ISSN: ${data.issn}`
|
|
176
159
|
}
|
|
177
160
|
|
|
178
|
-
|
|
179
|
-
|
|
161
|
+
return citation || '引用情報が不完全です'
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 現在のページに引用を登録
|
|
165
|
+
const registerCitation = () => {
|
|
166
|
+
// $page はこのコンポーネントが配置されているスライドの番号(静的)
|
|
167
|
+
const page = $page
|
|
168
|
+
if (!page) return
|
|
169
|
+
|
|
170
|
+
if (!window.citationManager.pageCitations.has(page)) {
|
|
171
|
+
window.citationManager.pageCitations.set(page, new Set())
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const pageSet = window.citationManager.pageCitations.get(page)
|
|
175
|
+
if (!pageSet.has(props.id)) {
|
|
176
|
+
pageSet.add(props.id)
|
|
177
|
+
notifyListeners()
|
|
178
|
+
}
|
|
180
179
|
}
|
|
181
180
|
|
|
182
|
-
//
|
|
183
|
-
const
|
|
184
|
-
|
|
181
|
+
// 現在のページから引用を解除
|
|
182
|
+
const unregisterCitation = () => {
|
|
183
|
+
const page = $page
|
|
184
|
+
if (!page) return
|
|
185
|
+
|
|
186
|
+
const pageSet = window.citationManager.pageCitations.get(page)
|
|
187
|
+
if (pageSet) {
|
|
188
|
+
pageSet.delete(props.id)
|
|
189
|
+
if (pageSet.size === 0) {
|
|
190
|
+
window.citationManager.pageCitations.delete(page)
|
|
191
|
+
}
|
|
192
|
+
notifyListeners()
|
|
193
|
+
}
|
|
185
194
|
}
|
|
186
195
|
|
|
187
|
-
// コンポーネントのマウント時
|
|
188
196
|
onMounted(() => {
|
|
189
|
-
isMounted.value = true
|
|
190
|
-
|
|
191
|
-
// 初期化を確実に実行
|
|
192
197
|
initializeCitations()
|
|
193
|
-
|
|
194
|
-
if (citationData.value) {
|
|
195
|
-
// 引用番号を取得(副作用でglobal stateに保存される)
|
|
196
|
-
const _ = citationNumber.value
|
|
197
|
-
debugGlobalState()
|
|
198
|
-
}
|
|
198
|
+
registerCitation()
|
|
199
199
|
})
|
|
200
200
|
|
|
201
|
-
// コンポーネントのアンマウント時
|
|
202
201
|
onUnmounted(() => {
|
|
203
|
-
|
|
204
|
-
debugGlobalState()
|
|
202
|
+
unregisterCitation()
|
|
205
203
|
})
|
|
206
|
-
|
|
207
|
-
// 開発用:グローバル状態確認用の関数をwindowに追加
|
|
208
|
-
if (typeof window !== 'undefined') {
|
|
209
|
-
window.debugCitationState = debugGlobalState
|
|
210
|
-
}
|
|
211
204
|
</script>
|
|
212
205
|
|
|
213
206
|
<style scoped>
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
background: #f1f1f1;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
::-webkit-scrollbar-thumb {
|
|
224
|
-
background: #c1c1c1;
|
|
225
|
-
border-radius: 2px;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
::-webkit-scrollbar-thumb:hover {
|
|
229
|
-
background: #a8a8a8;
|
|
207
|
+
sup {
|
|
208
|
+
font-size: 0.75em;
|
|
209
|
+
line-height: 0;
|
|
210
|
+
position: relative;
|
|
211
|
+
vertical-align: baseline;
|
|
212
|
+
top: -0.5em;
|
|
230
213
|
}
|
|
231
214
|
</style>
|
|
@@ -147,12 +147,23 @@
|
|
|
147
147
|
</template>
|
|
148
148
|
|
|
149
149
|
<script setup>
|
|
150
|
-
import { computed } from 'vue'
|
|
150
|
+
import { computed, inject } from 'vue'
|
|
151
151
|
import { useSlideContext } from '@slidev/client'
|
|
152
152
|
|
|
153
153
|
// Slidevコンテキストからconfigsにアクセス
|
|
154
154
|
const { $slidev } = useSlideContext()
|
|
155
|
-
|
|
155
|
+
|
|
156
|
+
// 方法1: frontmatterから取得(従来通り)
|
|
157
|
+
const frontmatterCitations = $slidev.configs.citations || {}
|
|
158
|
+
|
|
159
|
+
// 方法2: injectから取得(外部ファイル対応)
|
|
160
|
+
const injectedCitations = inject('citations', {})
|
|
161
|
+
|
|
162
|
+
// 両方をマージ(frontmatter優先)
|
|
163
|
+
const citations = computed(() => ({
|
|
164
|
+
...injectedCitations,
|
|
165
|
+
...frontmatterCitations
|
|
166
|
+
}))
|
|
156
167
|
|
|
157
168
|
// プロパティ定義
|
|
158
169
|
const props = defineProps({
|
|
@@ -177,16 +188,17 @@ const props = defineProps({
|
|
|
177
188
|
|
|
178
189
|
// 参考文献リストを生成(frontmatterの順番を維持)
|
|
179
190
|
const citationsList = computed(() => {
|
|
180
|
-
|
|
191
|
+
const citationsData = citations.value
|
|
192
|
+
if (!citationsData || typeof citationsData !== 'object') {
|
|
181
193
|
return []
|
|
182
194
|
}
|
|
183
195
|
|
|
184
196
|
// frontmatterの記述順でキーを取得
|
|
185
|
-
const originalKeys = Object.keys(
|
|
197
|
+
const originalKeys = Object.keys(citationsData)
|
|
186
198
|
|
|
187
199
|
const list = originalKeys.map((key, index) => ({
|
|
188
200
|
key,
|
|
189
|
-
data:
|
|
201
|
+
data: citationsData[key],
|
|
190
202
|
number: index + 1 // frontmatterの順番に基づく番号
|
|
191
203
|
}))
|
|
192
204
|
|
package/components/Header.vue
CHANGED
|
@@ -97,12 +97,25 @@
|
|
|
97
97
|
</template>
|
|
98
98
|
|
|
99
99
|
<script setup>
|
|
100
|
-
import { computed } from 'vue'
|
|
100
|
+
import { computed ,inject } from 'vue'
|
|
101
101
|
import { useSlideContext } from '@slidev/client'
|
|
102
102
|
|
|
103
103
|
// Slidevコンテキストからconfigsにアクセス
|
|
104
104
|
const { $slidev } = useSlideContext()
|
|
105
|
-
|
|
105
|
+
|
|
106
|
+
// 方法1: frontmatterから取得(従来通り)
|
|
107
|
+
const frontmatterChapters = $slidev.configs.chapters || {}
|
|
108
|
+
|
|
109
|
+
// 方法2: injectから取得(外部ファイル対応)
|
|
110
|
+
const injectedChapters = inject('chapters', {})
|
|
111
|
+
|
|
112
|
+
// 両方をマージ(frontmatter優先)
|
|
113
|
+
const chapters = computed(() => ({
|
|
114
|
+
...injectedChapters,
|
|
115
|
+
...frontmatterChapters
|
|
116
|
+
}))
|
|
117
|
+
|
|
118
|
+
console.log('Header2.vue - chapters:', chapters.value)
|
|
106
119
|
|
|
107
120
|
const props = defineProps({
|
|
108
121
|
// タイトル用のプロパティ
|
|
@@ -133,28 +146,45 @@ const isSection = computed(() => {
|
|
|
133
146
|
|
|
134
147
|
// タイトル表示用のデータ
|
|
135
148
|
const displayData = computed(() => {
|
|
149
|
+
console.log('=== displayData 計算開始 ===')
|
|
150
|
+
console.log('props.chapterData:', props.chapterData)
|
|
151
|
+
console.log('props.currentSection:', props.currentSection)
|
|
152
|
+
console.log('props.chapter:', props.chapter)
|
|
153
|
+
console.log('props.currentChapter:', props.currentChapter)
|
|
154
|
+
console.log('chapters.value:', chapters.value)
|
|
155
|
+
|
|
136
156
|
// chapterDataが直接渡された場合はそれを使用
|
|
137
157
|
if (props.chapterData) {
|
|
158
|
+
console.log('→ chapterData を使用')
|
|
138
159
|
return props.chapterData
|
|
139
160
|
}
|
|
140
161
|
|
|
141
162
|
// currentSectionが指定されている場合、セクションデータを取得
|
|
142
163
|
if (props.currentSection && (props.chapter || props.currentChapter)) {
|
|
143
164
|
const chapterKey = props.chapter || props.currentChapter
|
|
144
|
-
|
|
165
|
+
console.log('→ セクション検索: chapterKey =', chapterKey)
|
|
166
|
+
|
|
167
|
+
const chapterData = chapters.value[chapterKey]
|
|
168
|
+
console.log('→ chapterData:', chapterData)
|
|
169
|
+
console.log('→ sections:', chapterData?.sections)
|
|
170
|
+
console.log('→ 検索するsection:', props.currentSection)
|
|
171
|
+
console.log('→ sectionData:', chapterData?.sections?.[props.currentSection])
|
|
172
|
+
|
|
145
173
|
if (chapterData?.sections?.[props.currentSection]) {
|
|
146
174
|
return chapterData.sections[props.currentSection]
|
|
147
175
|
}
|
|
176
|
+
console.log('→ セクションが見つかりません')
|
|
148
177
|
return null
|
|
149
178
|
}
|
|
150
179
|
|
|
151
180
|
// chapterキーが渡された場合は内部データから取得
|
|
152
181
|
const chapterKey = props.chapter || props.currentChapter
|
|
153
|
-
if (chapterKey && chapters[chapterKey]) {
|
|
154
|
-
|
|
182
|
+
if (chapterKey && chapters.value[chapterKey]) {
|
|
183
|
+
console.log('→ 章データを使用')
|
|
184
|
+
return chapters.value[chapterKey]
|
|
155
185
|
}
|
|
156
186
|
|
|
157
|
-
|
|
187
|
+
console.log('→ 何も見つかりません')
|
|
158
188
|
return null
|
|
159
189
|
})
|
|
160
190
|
|
|
@@ -172,14 +202,21 @@ const isCurrentPageAppendix = computed(() => {
|
|
|
172
202
|
|
|
173
203
|
// 章リストを生成(refを除外し、appendixの表示を条件分岐)
|
|
174
204
|
const chapterList = computed(() => {
|
|
175
|
-
|
|
205
|
+
const chaptersData = chapters.value
|
|
206
|
+
console.log('=== chapterList 計算 ===')
|
|
207
|
+
console.log('chaptersData:', chaptersData)
|
|
208
|
+
console.log('typeof chaptersData:', typeof chaptersData)
|
|
209
|
+
console.log('Object.keys:', chaptersData ? Object.keys(chaptersData) : 'null')
|
|
210
|
+
|
|
211
|
+
if (!chaptersData || typeof chaptersData !== 'object') {
|
|
212
|
+
console.log('→ chaptersData が無効')
|
|
176
213
|
return []
|
|
177
214
|
}
|
|
178
215
|
|
|
179
|
-
|
|
180
|
-
.filter(chapterKey => chapterKey !== 'ref')
|
|
216
|
+
const result = Object.keys(chaptersData)
|
|
217
|
+
.filter(chapterKey => chapterKey !== 'ref')
|
|
181
218
|
.map(chapterKey => {
|
|
182
|
-
const chapter =
|
|
219
|
+
const chapter = chaptersData[chapterKey]
|
|
183
220
|
const sections = chapter?.sections || {}
|
|
184
221
|
const sectionKeys = Object.keys(sections)
|
|
185
222
|
|
|
@@ -191,17 +228,26 @@ const chapterList = computed(() => {
|
|
|
191
228
|
isAppendix: chapterKey.startsWith('ap')
|
|
192
229
|
}
|
|
193
230
|
})
|
|
231
|
+
|
|
232
|
+
console.log('chapterList result:', result)
|
|
233
|
+
return result
|
|
194
234
|
})
|
|
195
235
|
|
|
196
236
|
// 表示する章リスト(appendixページかどうかで切り替え)
|
|
197
237
|
const displayChapterList = computed(() => {
|
|
238
|
+
console.log('=== displayChapterList 計算 ===')
|
|
239
|
+
console.log('isCurrentPageAppendix:', isCurrentPageAppendix.value)
|
|
240
|
+
console.log('chapterList:', chapterList.value)
|
|
241
|
+
|
|
242
|
+
let result
|
|
198
243
|
if (isCurrentPageAppendix.value) {
|
|
199
|
-
|
|
200
|
-
return chapterList.value.filter(chapter => chapter.isAppendix)
|
|
244
|
+
result = chapterList.value.filter(chapter => chapter.isAppendix)
|
|
201
245
|
} else {
|
|
202
|
-
|
|
203
|
-
return chapterList.value.filter(chapter => !chapter.isAppendix)
|
|
246
|
+
result = chapterList.value.filter(chapter => !chapter.isAppendix)
|
|
204
247
|
}
|
|
248
|
+
|
|
249
|
+
console.log('displayChapterList result:', result)
|
|
250
|
+
return result
|
|
205
251
|
})
|
|
206
252
|
|
|
207
253
|
// appendixの開始ページを計算(SectionDivider考慮版)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- components/
|
|
1
|
+
<!-- components/SectionDivider.vue -->
|
|
2
2
|
<template>
|
|
3
3
|
<div class="table-of-contents-container flex flex-col justify-start bg-white overflow-hidden relative">
|
|
4
4
|
<!-- 厳密な背景グリッド - Swiss Styleの基礎 -->
|
|
@@ -160,12 +160,23 @@
|
|
|
160
160
|
</template>
|
|
161
161
|
|
|
162
162
|
<script setup>
|
|
163
|
-
import { computed } from 'vue'
|
|
163
|
+
import { computed, inject } from 'vue'
|
|
164
164
|
import { useSlideContext } from '@slidev/client'
|
|
165
165
|
|
|
166
166
|
// Slidevコンテキストからconfigsにアクセス
|
|
167
167
|
const { $slidev } = useSlideContext()
|
|
168
|
-
|
|
168
|
+
|
|
169
|
+
// 方法1: frontmatterから取得(従来通り)
|
|
170
|
+
const frontmatterChapters = $slidev.configs.chapters || {}
|
|
171
|
+
|
|
172
|
+
// 方法2: injectから取得(外部ファイル対応)
|
|
173
|
+
const injectedChapters = inject('chapters', {})
|
|
174
|
+
|
|
175
|
+
// 両方をマージ(frontmatter優先)
|
|
176
|
+
const chapters = computed(() => ({
|
|
177
|
+
...injectedChapters,
|
|
178
|
+
...frontmatterChapters
|
|
179
|
+
}))
|
|
169
180
|
|
|
170
181
|
const props = defineProps({
|
|
171
182
|
nextChapter: {
|
|
@@ -188,7 +199,8 @@ const props = defineProps({
|
|
|
188
199
|
|
|
189
200
|
// セクションの重複を除去する関数
|
|
190
201
|
const getUniqueSections = (chapterKey) => {
|
|
191
|
-
const
|
|
202
|
+
const chaptersData = chapters.value
|
|
203
|
+
const chapter = chaptersData[chapterKey]
|
|
192
204
|
if (!chapter?.sections) return []
|
|
193
205
|
|
|
194
206
|
const sections = chapter.sections
|
|
@@ -221,14 +233,15 @@ const isAppendixSection = computed(() => {
|
|
|
221
233
|
|
|
222
234
|
// 通常の章リスト(refと付録を除外)
|
|
223
235
|
const regularChapters = computed(() => {
|
|
224
|
-
|
|
236
|
+
const chaptersData = chapters.value
|
|
237
|
+
if (!chaptersData || typeof chaptersData !== 'object') {
|
|
225
238
|
return []
|
|
226
239
|
}
|
|
227
240
|
|
|
228
|
-
return Object.keys(
|
|
241
|
+
return Object.keys(chaptersData)
|
|
229
242
|
.filter(chapterKey => chapterKey !== 'ref' && !chapterKey.startsWith('ap'))
|
|
230
243
|
.map(chapterKey => {
|
|
231
|
-
const chapter =
|
|
244
|
+
const chapter = chaptersData[chapterKey]
|
|
232
245
|
const sections = chapter?.sections || {}
|
|
233
246
|
const sectionKeys = Object.keys(sections)
|
|
234
247
|
|
|
@@ -244,14 +257,15 @@ const regularChapters = computed(() => {
|
|
|
244
257
|
|
|
245
258
|
// 付録の章リスト
|
|
246
259
|
const appendixChapters = computed(() => {
|
|
247
|
-
|
|
260
|
+
const chaptersData = chapters.value
|
|
261
|
+
if (!chaptersData || typeof chaptersData !== 'object') {
|
|
248
262
|
return []
|
|
249
263
|
}
|
|
250
264
|
|
|
251
|
-
return Object.keys(
|
|
265
|
+
return Object.keys(chaptersData)
|
|
252
266
|
.filter(chapterKey => chapterKey.startsWith('ap'))
|
|
253
267
|
.map(chapterKey => {
|
|
254
|
-
const chapter =
|
|
268
|
+
const chapter = chaptersData[chapterKey]
|
|
255
269
|
const sections = chapter?.sections || {}
|
|
256
270
|
const sectionKeys = Object.keys(sections)
|
|
257
271
|
|
|
@@ -267,7 +281,8 @@ const appendixChapters = computed(() => {
|
|
|
267
281
|
|
|
268
282
|
// セクションタイトルを取得
|
|
269
283
|
const getSectionTitle = (chapterKey, sectionKey) => {
|
|
270
|
-
const
|
|
284
|
+
const chaptersData = chapters.value
|
|
285
|
+
const chapter = chaptersData[chapterKey]
|
|
271
286
|
const sectionData = chapter?.sections?.[sectionKey]
|
|
272
287
|
return sectionData?.title || `セクション ${sectionKey}`
|
|
273
288
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slidev-theme-gtlabo",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "A Slidev theme for laboratory presentations with customizable components",
|
|
5
5
|
"author": "mksmkss",
|
|
6
6
|
"license": "MIT",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"main": "index.js",
|
|
13
13
|
"files": [
|
|
14
14
|
"components",
|
|
15
|
-
"layouts",
|
|
15
|
+
"layouts",
|
|
16
16
|
"styles",
|
|
17
17
|
"setup",
|
|
18
18
|
"uno.config.ts",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
],
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "https://github.com/mksmkss/slidev-theme-gtlabo.git"
|
|
23
|
+
"url": "git+https://github.com/mksmkss/slidev-theme-gtlabo.git"
|
|
24
24
|
},
|
|
25
25
|
"homepage": "https://github.com/mksmkss/slidev-theme-gtlabo",
|
|
26
26
|
"bugs": {
|
|
@@ -62,4 +62,4 @@
|
|
|
62
62
|
"aspectRatio": "4/3"
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
}
|
|
65
|
+
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
<!-- components/ChapterItem.vue -->
|
|
2
|
-
<template>
|
|
3
|
-
<div
|
|
4
|
-
class="flex flex-col"
|
|
5
|
-
:class="isNext ? 'border-sky-500 border-l-8 pl-6' : 'border-slate-200 border-l-4 pl-7'"
|
|
6
|
-
>
|
|
7
|
-
<!-- 次の章インジケーター - sky-500アクセント -->
|
|
8
|
-
<div
|
|
9
|
-
v-if="isNext"
|
|
10
|
-
:class="nextBadgeClasses"
|
|
11
|
-
class="bg-sky-500 text-white font-bold uppercase tracking-widest leading-none"
|
|
12
|
-
>
|
|
13
|
-
NEXT
|
|
14
|
-
</div>
|
|
15
|
-
|
|
16
|
-
<!-- 章番号とタイトル - 左揃え、明確なヒエラルキー -->
|
|
17
|
-
<div :class="chapterHeaderClasses">
|
|
18
|
-
<div class="flex items-end space-x-3 w-full">
|
|
19
|
-
<div :class="chapterNumberClasses" class="flex text-slate-900 font-bold tabular-nums leading-none">
|
|
20
|
-
{{ chapterNumber }}
|
|
21
|
-
</div>
|
|
22
|
-
<div class="flex-1">
|
|
23
|
-
<div class="border-b border-slate-900 mb-2">
|
|
24
|
-
<span :class="chapterTitleClasses" class="text-slate-900 font-bold leading-tight">
|
|
25
|
-
{{ chapter.title }}
|
|
26
|
-
</span>
|
|
27
|
-
</div>
|
|
28
|
-
</div>
|
|
29
|
-
</div>
|
|
30
|
-
</div>
|
|
31
|
-
|
|
32
|
-
<!-- セクション一覧 - 機能的な情報構造 -->
|
|
33
|
-
<div :class="sectionListClasses">
|
|
34
|
-
<div
|
|
35
|
-
v-for="(sectionData, sectionIndex) in chapter.uniqueSections"
|
|
36
|
-
:key="`${chapter.key}-${sectionIndex}`"
|
|
37
|
-
class="flex items-start"
|
|
38
|
-
>
|
|
39
|
-
<span class="w-1 h-1 bg-slate-400 mr-2 mt-1.5 flex-shrink-0 rounded-full"></span>
|
|
40
|
-
<span :class="sectionTextClasses" class="text-slate-600 leading-snug">
|
|
41
|
-
{{ sectionData.title }}
|
|
42
|
-
</span>
|
|
43
|
-
</div>
|
|
44
|
-
</div>
|
|
45
|
-
</div>
|
|
46
|
-
</template>
|
|
47
|
-
|
|
48
|
-
<script setup>
|
|
49
|
-
import { computed } from 'vue'
|
|
50
|
-
|
|
51
|
-
const props = defineProps({
|
|
52
|
-
chapter: {
|
|
53
|
-
type: Object,
|
|
54
|
-
required: true
|
|
55
|
-
},
|
|
56
|
-
index: {
|
|
57
|
-
type: Number,
|
|
58
|
-
required: true
|
|
59
|
-
},
|
|
60
|
-
isNext: {
|
|
61
|
-
type: Boolean,
|
|
62
|
-
default: false
|
|
63
|
-
},
|
|
64
|
-
isAppendix: {
|
|
65
|
-
type: Boolean,
|
|
66
|
-
default: false
|
|
67
|
-
},
|
|
68
|
-
size: {
|
|
69
|
-
type: String,
|
|
70
|
-
default: 'md'
|
|
71
|
-
},
|
|
72
|
-
config: {
|
|
73
|
-
type: Object,
|
|
74
|
-
required: true
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
// 章番号の生成(通常章は "01", "02"、付録は "A1", "A2")
|
|
79
|
-
const chapterNumber = computed(() => {
|
|
80
|
-
if (props.isAppendix) {
|
|
81
|
-
return `A${props.index + 1}`
|
|
82
|
-
}
|
|
83
|
-
return String(props.index + 1).padStart(2, '0')
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
// 動的クラス
|
|
87
|
-
const chapterHeaderClasses = computed(() => props.config.chapterHeader)
|
|
88
|
-
const chapterNumberClasses = computed(() => props.config.chapterNumber)
|
|
89
|
-
const chapterTitleClasses = computed(() => props.config.chapterTitle)
|
|
90
|
-
const sectionListClasses = computed(() => props.config.sectionList)
|
|
91
|
-
const sectionTextClasses = computed(() => props.config.sectionText)
|
|
92
|
-
const nextBadgeClasses = computed(() => props.config.nextBadge)
|
|
93
|
-
</script>
|