vuepress-plugin-md-power 1.0.0-rc.102 → 1.0.0-rc.103

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,10 +1,10 @@
1
1
  <script setup lang="ts">
2
2
  import { defineAsyncComponent, shallowRef } from 'vue'
3
3
  import { useCodeRepl } from '../composables/codeRepl.js'
4
- import IconClose from './IconClose.vue'
5
- import IconConsole from './IconConsole.vue'
6
- import IconRun from './IconRun.vue'
7
- import Loading from './Loading.vue'
4
+ import IconClose from './icons/IconClose.vue'
5
+ import IconConsole from './icons/IconConsole.vue'
6
+ import IconRun from './icons/IconRun.vue'
7
+ import Loading from './icons/Loading.vue'
8
8
 
9
9
  defineProps<{
10
10
  editable?: boolean
@@ -0,0 +1,245 @@
1
+ <script setup lang="ts">
2
+ import { useStorage } from '@vueuse/core'
3
+ import { onMounted, ref, shallowRef, watch } from 'vue'
4
+
5
+ interface TabProps extends Record<string, unknown> {
6
+ id: string
7
+ }
8
+ const props = withDefaults(defineProps<{
9
+ id: string
10
+ tabId?: string
11
+ active?: number
12
+ data: TabProps[]
13
+ }>(), { active: 0, tabId: '' })
14
+
15
+ const CODE_TAB_STORE_NAME = 'VUEPRESS_CODE_TAB_STORE'
16
+ const codeTabStore = useStorage<Record<string, string>>(CODE_TAB_STORE_NAME, {})
17
+
18
+ // Index of current active item
19
+ const activeIndex = ref(props.active)
20
+
21
+ // Refs of the tab buttons
22
+ const tabRefs = shallowRef<HTMLUListElement[]>([])
23
+
24
+ // Update store
25
+ function updateStore(): void {
26
+ if (props.tabId)
27
+ codeTabStore.value[props.tabId] = props.data[activeIndex.value].id
28
+ }
29
+
30
+ // Activate next tab
31
+ function activateNext(index = activeIndex.value): void {
32
+ activeIndex.value = index < tabRefs.value.length - 1 ? index + 1 : 0
33
+ tabRefs.value[activeIndex.value].focus()
34
+ }
35
+
36
+ // Activate previous tab
37
+ function activatePrev(index = activeIndex.value): void {
38
+ activeIndex.value = index > 0 ? index - 1 : tabRefs.value.length - 1
39
+ tabRefs.value[activeIndex.value].focus()
40
+ }
41
+
42
+ // Handle keyboard event
43
+ function keyboardHandler(event: KeyboardEvent, index: number): void {
44
+ if (event.key === ' ' || event.key === 'Enter') {
45
+ event.preventDefault()
46
+ activeIndex.value = index
47
+ }
48
+ else if (event.key === 'ArrowRight') {
49
+ event.preventDefault()
50
+ activateNext()
51
+ }
52
+ else if (event.key === 'ArrowLeft') {
53
+ event.preventDefault()
54
+ activatePrev()
55
+ }
56
+
57
+ if (props.tabId)
58
+ codeTabStore.value[props.tabId] = props.data[activeIndex.value].id
59
+ }
60
+
61
+ function getInitialIndex(): number {
62
+ if (props.tabId) {
63
+ const valueIndex = props.data.findIndex(
64
+ ({ id }) => codeTabStore.value[props.tabId] === id,
65
+ )
66
+
67
+ if (valueIndex !== -1)
68
+ return valueIndex
69
+ }
70
+
71
+ return props.active
72
+ }
73
+
74
+ onMounted(() => {
75
+ activeIndex.value = getInitialIndex()
76
+
77
+ watch(
78
+ () => codeTabStore.value[props.tabId],
79
+ (newValue, oldValue) => {
80
+ if (props.tabId && newValue !== oldValue) {
81
+ const index = props.data.findIndex(({ id }) => id === newValue)
82
+
83
+ if (index !== -1)
84
+ activeIndex.value = index
85
+ }
86
+ },
87
+ )
88
+ })
89
+ function onTabNavClick(index: number): void {
90
+ activeIndex.value = index
91
+ updateStore()
92
+ }
93
+ </script>
94
+
95
+ <template>
96
+ <div v-if="data.length" class="vp-code-tabs">
97
+ <div class="vp-code-tabs-nav" role="tablist">
98
+ <button
99
+ v-for="(item, index) in data"
100
+ :key="index"
101
+ :ref="(el) => el && (tabRefs[index] = el as HTMLUListElement)"
102
+ class="vp-code-tab-nav" :class="{ active: index === activeIndex }"
103
+ type="button" role="tab"
104
+ :aria-controls="`codetab-${id}-${index}`"
105
+ :aria-selected="index === activeIndex"
106
+ @click="() => onTabNavClick(index)"
107
+ @keydown="(e) => keyboardHandler(e, index)"
108
+ >
109
+ <slot :name="`title${index}`" :value="item.id" :is-active="index === activeIndex" />
110
+ </button>
111
+ </div>
112
+ <div
113
+ v-for="(item, index) in data"
114
+ :id="`codetab-${id}-${index}`" :key="index"
115
+ class="vp-code-tab" :class="{ active: index === activeIndex }"
116
+ role="tabpanel" :aria-expanded="index === activeIndex"
117
+ >
118
+ <div class="vp-code-tab-title">
119
+ <slot :name="`title${index}`" :value="item.id" :is-active="index === activeIndex" />
120
+ </div>
121
+ <slot :name="`tab${index}`" :value="item.id" :is-active="index === activeIndex" />
122
+ </div>
123
+ </div>
124
+ </template>
125
+
126
+ <style>
127
+ .vp-code-tabs-nav {
128
+ padding: 0 12px;
129
+ margin: 16px 0 0;
130
+ overflow: auto hidden;
131
+ white-space: nowrap;
132
+ list-style: none;
133
+ background-color: var(--vp-code-tab-bg);
134
+ border-radius: 6px 6px 0 0;
135
+ box-shadow: inset 0 -1px var(--vp-code-tab-divider);
136
+ transition: background-color var(--vp-t-color), box-shadow var(--vp-t-color);
137
+ }
138
+
139
+ @media print {
140
+ .vp-code-tabs-nav {
141
+ display: none;
142
+ }
143
+ }
144
+
145
+ @media (max-width: 639px) {
146
+ .vp-code-tabs-nav {
147
+ margin: 16px -24px 0;
148
+ border-radius: 0;
149
+ }
150
+
151
+ .vp-doc li .vp-code-tabs-nav {
152
+ border-top-left-radius: 6px;
153
+ }
154
+ }
155
+
156
+ .vp-code-tab-nav {
157
+ position: relative;
158
+ padding: 0 12px;
159
+ font-size: 14px;
160
+ font-weight: 500;
161
+ line-height: 48px;
162
+ color: var(--vp-code-tab-text-color);
163
+ white-space: nowrap;
164
+ border-bottom: 1px solid transparent;
165
+ transition: color var(--vp-t-color);
166
+ }
167
+
168
+ .vp-code-tab-nav:hover {
169
+ color: var(--vp-code-tab-hover-text-color);
170
+ }
171
+
172
+ .vp-code-tab-nav::after {
173
+ position: absolute;
174
+ right: 8px;
175
+ bottom: -1px;
176
+ left: 8px;
177
+ z-index: 1;
178
+ display: block;
179
+ width: auto;
180
+ height: 2px;
181
+ content: "";
182
+ background: transparent;
183
+ border-radius: 2px;
184
+ transition: background var(--vp-t-color);
185
+ }
186
+
187
+ .vp-code-tab-nav:focus-visible {
188
+ outline: none;
189
+ }
190
+
191
+ .vp-code-tab-nav.active {
192
+ color: var(--vp-code-tab-active-text-color);
193
+ background: transparent;
194
+ }
195
+
196
+ .vp-code-tab-nav.active::after {
197
+ background: var(--vp-code-tab-active-bar-color);
198
+ }
199
+
200
+ .vp-code-tab-nav .vp-icon {
201
+ width: 18px;
202
+ height: 18px;
203
+ margin-left: 0;
204
+ }
205
+
206
+ .vp-code-tab-nav span {
207
+ vertical-align: middle;
208
+ }
209
+
210
+ @media (max-width: 419px) {
211
+ .hint-container .vp-code-tabs-nav {
212
+ margin: 0.85rem -0.75rem 0 -1rem;
213
+ }
214
+ }
215
+
216
+ .vp-code-tab {
217
+ display: none;
218
+ }
219
+
220
+ @media print {
221
+ .vp-code-tab {
222
+ display: block;
223
+ }
224
+ }
225
+
226
+ .vp-code-tab.active {
227
+ display: block;
228
+ }
229
+
230
+ .vp-doc .vp-code-tab div[class*="language-"] {
231
+ margin-top: 0;
232
+ border-top-left-radius: 0;
233
+ border-top-right-radius: 0;
234
+ }
235
+
236
+ .vp-code-tab-title {
237
+ display: none;
238
+ }
239
+
240
+ @media print {
241
+ .vp-code-tab-title {
242
+ display: block;
243
+ }
244
+ }
245
+ </style>
@@ -12,7 +12,7 @@ const el = ref<HTMLElement>()
12
12
 
13
13
  function toggle(e: HTMLElementEventMap['click']) {
14
14
  const target = e.target as HTMLElement
15
- if (target.matches('.comment'))
15
+ if (target.matches('.comment') || e.currentTarget === target)
16
16
  return
17
17
  active.value = !active.value
18
18
  }
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { ReplitTokenMeta } from '../../shared/index.js'
3
3
  import { computed, getCurrentInstance, ref } from 'vue'
4
- import Loading from './Loading.vue'
4
+ import Loading from './icons/Loading.vue'
5
5
 
6
6
  const props = defineProps<ReplitTokenMeta>()
7
7
 
@@ -0,0 +1,277 @@
1
+ <script setup lang="ts">
2
+ import { useStorage } from '@vueuse/core'
3
+ import { onMounted, ref, shallowRef, watch } from 'vue'
4
+
5
+ interface TabProps extends Record<string, unknown> {
6
+ id: string
7
+ }
8
+ const props = withDefaults(defineProps<{
9
+ id: string
10
+ tabId?: string
11
+ active?: number
12
+ data: TabProps[]
13
+ }>(), { active: 0, tabId: '' })
14
+
15
+ const TAB_STORE_NAME = 'VUEPRESS_TAB_STORE'
16
+
17
+ const tabStore = useStorage<Record<string, string>>(TAB_STORE_NAME, {})
18
+
19
+ // Index of current active item
20
+ const activeIndex = ref(props.active)
21
+
22
+ // Refs of the tab buttons
23
+ const tabRefs = shallowRef<HTMLUListElement[]>([])
24
+
25
+ // Update store
26
+ function updateStore(): void {
27
+ if (props.tabId)
28
+ tabStore.value[props.tabId] = props.data[activeIndex.value].id
29
+ }
30
+
31
+ // Activate next tab
32
+ function activateNext(index = activeIndex.value): void {
33
+ activeIndex.value = index < tabRefs.value.length - 1 ? index + 1 : 0
34
+ tabRefs.value[activeIndex.value].focus()
35
+ }
36
+
37
+ // Activate previous tab
38
+ function activatePrev(index = activeIndex.value): void {
39
+ activeIndex.value = index > 0 ? index - 1 : tabRefs.value.length - 1
40
+ tabRefs.value[activeIndex.value].focus()
41
+ }
42
+
43
+ // Handle keyboard event
44
+ function keyboardHandler(event: KeyboardEvent, index: number): void {
45
+ if (event.key === ' ' || event.key === 'Enter') {
46
+ event.preventDefault()
47
+ activeIndex.value = index
48
+ }
49
+ else if (event.key === 'ArrowRight') {
50
+ event.preventDefault()
51
+ activateNext()
52
+ }
53
+ else if (event.key === 'ArrowLeft') {
54
+ event.preventDefault()
55
+ activatePrev()
56
+ }
57
+
58
+ updateStore()
59
+ }
60
+
61
+ function getInitialIndex(): number {
62
+ if (props.tabId) {
63
+ const valueIndex = props.data.findIndex(
64
+ ({ id }) => tabStore.value[props.tabId] === id,
65
+ )
66
+
67
+ if (valueIndex !== -1)
68
+ return valueIndex
69
+ }
70
+
71
+ return props.active
72
+ }
73
+
74
+ onMounted(() => {
75
+ activeIndex.value = getInitialIndex()
76
+
77
+ watch(
78
+ () => tabStore.value[props.tabId],
79
+ (newValue, oldValue) => {
80
+ if (props.tabId && newValue !== oldValue) {
81
+ const index = props.data.findIndex(({ id }) => id === newValue)
82
+
83
+ if (index !== -1)
84
+ activeIndex.value = index
85
+ }
86
+ },
87
+ )
88
+ })
89
+
90
+ function onTabNavClick(index: number): void {
91
+ activeIndex.value = index
92
+ updateStore()
93
+ }
94
+ </script>
95
+
96
+ <template>
97
+ <div v-if="data.length" class="vp-tabs">
98
+ <div class="vp-tabs-nav" role="tablist">
99
+ <button
100
+ v-for="(item, index) in data"
101
+ :key="index"
102
+ :ref="(el) => el && (tabRefs[index] = el as HTMLUListElement)"
103
+ class="vp-tab-nav" :class="{ active: index === activeIndex }"
104
+ type="button" role="tab"
105
+ :aria-controls="`tab-${id}-${index}`"
106
+ :aria-selected="index === activeIndex"
107
+ @click="() => onTabNavClick(index)"
108
+ @keydown="(e) => keyboardHandler(e, index)"
109
+ >
110
+ <slot :name="`title${index}`" :value="item.id" :is-active="index === activeIndex" />
111
+ </button>
112
+ </div>
113
+ <div
114
+ v-for="(item, index) in data"
115
+ :id="`tab-${id}-${index}`" :key="index"
116
+ class="vp-tab" :class="{ active: index === activeIndex }"
117
+ role="tabpanel" :aria-expanded="index === activeIndex"
118
+ >
119
+ <div class="vp-tab-title">
120
+ <slot :name="`title${index}`" :value="item.id" :is-active="index === activeIndex" />
121
+ </div>
122
+ <slot :name="`tab${index}`" :value="item.id" :is-active="index === activeIndex" />
123
+ </div>
124
+ </div>
125
+ </template>
126
+
127
+ <style>
128
+ .vp-tabs {
129
+ margin: 16px 0;
130
+ overflow: hidden;
131
+ border: 1px solid var(--vp-c-divider);
132
+ border-radius: 6px;
133
+ transition: border var(--vp-t-color);
134
+ }
135
+
136
+ @media (max-width: 419px) {
137
+ .vp-tabs {
138
+ margin: 16px -24px;
139
+ border: none;
140
+ border-bottom: 1px solid var(--vp-c-divider);
141
+ border-radius: 0;
142
+ }
143
+ }
144
+
145
+ .vp-doc .vp-tabs-nav {
146
+ padding: 0 12px;
147
+ overflow-x: auto;
148
+ white-space: nowrap;
149
+ background-color: var(--vp-code-tab-bg);
150
+ box-shadow: inset 0 -1px var(--vp-code-tab-divider);
151
+ transition: background-color var(--vp-t-color), box-shadow var(--vp-t-color);
152
+ }
153
+
154
+ @media print {
155
+ .vp-doc .vp-tabs-nav {
156
+ display: none;
157
+ }
158
+ }
159
+
160
+ .vp-doc .vp-tab-nav {
161
+ position: relative;
162
+ padding: 0 12px;
163
+ font-size: 14px;
164
+ font-weight: 500;
165
+ line-height: 48px;
166
+ color: var(--vp-code-tab-text-color);
167
+ white-space: nowrap;
168
+ border-bottom: 1px solid transparent;
169
+ transition: color var(--vp-t-color);
170
+ }
171
+
172
+ .vp-doc .vp-tab-nav:hover {
173
+ color: var(--vp-code-tab-text-hover-color);
174
+ }
175
+
176
+ .vp-doc .vp-tab-nav::after {
177
+ position: absolute;
178
+ right: 8px;
179
+ bottom: -1px;
180
+ left: 8px;
181
+ z-index: 1;
182
+ display: block;
183
+ width: auto;
184
+ height: 2px;
185
+ content: "";
186
+ background: transparent;
187
+ border-radius: 2px;
188
+ transition: background var(--vp-t-color);
189
+ }
190
+
191
+ .vp-doc .vp-tab-nav.active {
192
+ color: var(--vp-code-tab-active-text-color);
193
+ background: transparent;
194
+ }
195
+
196
+ .vp-doc .vp-tab-nav.active::after {
197
+ background: var(--vp-code-tab-active-bar-color);
198
+ }
199
+
200
+ .vp-doc .vp-tab {
201
+ display: none;
202
+ padding: 16px;
203
+ }
204
+
205
+ .vp-doc .vp-tab :nth-child(2) {
206
+ margin-top: 0;
207
+ }
208
+
209
+ .vp-doc .vp-tab :last-child {
210
+ margin-bottom: 0;
211
+ }
212
+
213
+ .vp-doc .vp-tab.active {
214
+ display: block;
215
+ }
216
+
217
+ .vp-doc .vp-tab-title {
218
+ display: none;
219
+ padding: 4px;
220
+ font-weight: 500;
221
+ color: var(--vp-code-tab-text-color);
222
+ border-top: 1px solid var(--vp-c-divider);
223
+ transition: color var(--vp-t-color);
224
+ }
225
+
226
+ .vp-doc .vp-tab:nth-child(n+2) .vp-tab-title {
227
+ border-top: none;
228
+ }
229
+
230
+ @media print {
231
+ .vp-doc .vp-tab-title {
232
+ display: block;
233
+ }
234
+ }
235
+
236
+ .vp-doc .hint-container .vp-tabs {
237
+ margin: 8px 0;
238
+ }
239
+
240
+ @media (max-width: 419px) {
241
+ .vp-doc .hint-container .vp-tabs {
242
+ margin: 8px -16px;
243
+ }
244
+ }
245
+
246
+ .vp-doc .hint-container .vp-tab-nav {
247
+ line-height: 40px;
248
+ }
249
+
250
+ .vp-doc .hint-container.info .vp-tabs .vp-tabs-nav {
251
+ background: var(--vp-custom-block-info-code-bg);
252
+ }
253
+
254
+ .vp-doc .hint-container.note .vp-tabs .vp-tabs-nav {
255
+ background: var(--vp-custom-block-note-code-bg);
256
+ }
257
+
258
+ .vp-doc .hint-container.tip .vp-tabs .vp-tabs-nav {
259
+ background: var(--vp-custom-block-tip-code-bg);
260
+ }
261
+
262
+ .vp-doc .hint-container.warning .vp-tabs .vp-tabs-nav {
263
+ background: var(--vp-custom-block-warning-code-bg);
264
+ }
265
+
266
+ .vp-doc .hint-container.danger .vp-tabs .vp-tabs-nav {
267
+ background: var(--vp-custom-block-danger-code-bg);
268
+ }
269
+
270
+ .vp-doc .hint-container.caution .vp-tabs .vp-tabs-nav {
271
+ background: var(--vp-custom-block-caution-code-bg);
272
+ }
273
+
274
+ .vp-doc .hint-container.important .vp-tabs .vp-tabs-nav {
275
+ background: var(--vp-custom-block-important-code-bg);
276
+ }
277
+ </style>
@@ -48,12 +48,19 @@ interface CodeSandboxTokenMeta extends SizeOptions {
48
48
  console?: boolean;
49
49
  }
50
50
 
51
+ interface CodeTabsOptions {
52
+ icon?: boolean | {
53
+ named?: false | string[];
54
+ extensions?: false | string[];
55
+ };
56
+ }
57
+
58
+ type FileTreeIconMode = 'simple' | 'colored';
59
+ interface FileTreeOptions {
60
+ icon?: FileTreeIconMode;
61
+ }
62
+
51
63
  interface IconsOptions {
52
- /**
53
- * The prefix of the icon className
54
- * @default 'vp-mdi'
55
- */
56
- prefix?: string;
57
64
  /**
58
65
  * The size of the icon
59
66
  * @default '1em'
@@ -140,6 +147,10 @@ interface ReplEditorData {
140
147
  }
141
148
 
142
149
  interface MarkdownPowerPluginOptions {
150
+ /**
151
+ * 配置代码块分组
152
+ */
153
+ codeTabs?: CodeTabsOptions;
143
154
  /**
144
155
  * 是否启用 PDF 嵌入语法
145
156
  *
@@ -219,7 +230,7 @@ interface MarkdownPowerPluginOptions {
219
230
  *
220
231
  * @default false
221
232
  */
222
- fileTree?: boolean;
233
+ fileTree?: boolean | FileTreeOptions;
223
234
  /**
224
235
  * 是否启用 caniuse 嵌入语法
225
236
  *
@@ -284,4 +295,4 @@ declare function resolveImageSize(app: App, url: string, remote?: boolean): Prom
284
295
 
285
296
  declare function markdownPowerPlugin(options?: MarkdownPowerPluginOptions): Plugin;
286
297
 
287
- export { type BilibiliTokenMeta, type CanIUseMode, type CanIUseOptions, type CanIUseTokenMeta, type CodeSandboxTokenMeta, type CodepenTokenMeta, type IconsOptions, type JSFiddleTokenMeta, type MarkdownPowerPluginOptions, type PDFEmbedType, type PDFOptions, type PDFTokenMeta, type PlotOptions, type ReplEditorData, type ReplOptions, type ReplitTokenMeta, type SizeOptions, type ThemeOptions, type VideoOptions, type YoutubeTokenMeta, markdownPowerPlugin, resolveImageSize };
298
+ export { type BilibiliTokenMeta, type CanIUseMode, type CanIUseOptions, type CanIUseTokenMeta, type CodeSandboxTokenMeta, type CodeTabsOptions, type CodepenTokenMeta, type FileTreeIconMode, type FileTreeOptions, type IconsOptions, type JSFiddleTokenMeta, type MarkdownPowerPluginOptions, type PDFEmbedType, type PDFOptions, type PDFTokenMeta, type PlotOptions, type ReplEditorData, type ReplOptions, type ReplitTokenMeta, type SizeOptions, type ThemeOptions, type VideoOptions, type YoutubeTokenMeta, markdownPowerPlugin, resolveImageSize };