@wordup-md/nuxt-layer-shadcn-unocss 0.2.13 → 0.2.14
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/.playground/content/3.components/2.docs/icon.md +2 -2
- package/.playground/content/_menu-aside.md +6 -0
- package/.playground/uno.config.ts +1 -0
- package/assets/css/mdc-editor.css +16 -10
- package/components/content/Icon.vue +1 -0
- package/components/content/Menu.vue +48 -14
- package/components/content/ProseCodeInline.vue +3 -3
- package/components/content/ProseSection.vue +11 -2
- package/components/content/ReadMore.vue +9 -4
- package/components/content/global/AlertEditor.vue +7 -4
- package/components/content/global/FieldGroupEditor.vue +1 -1
- package/components/content/global/ReadMoreEditor.vue +44 -13
- package/components/content/global/TabsEditor.vue +56 -6
- package/components/layout/Aside.vue +51 -43
- package/components/layout/AsideMenuItem.vue +103 -0
- package/components/layout/AsideTree2.vue +28 -0
- package/components/layout/AsideTreeItem.vue +1 -1
- package/components/layout/PrevNextButton.vue +1 -1
- package/components/layout/SearchButton.vue +1 -1
- package/components/layout/Title.vue +23 -5
- package/components/layout/Toc.vue +4 -3
- package/components/layout/TocTree.vue +26 -8
- package/composables/useConfig.ts +2 -1
- package/index.d.ts +19 -13
- package/lib/utils.ts +6 -0
- package/nuxt.config.ts +35 -5
- package/package.json +14 -20
- package/pages/[...slug].vue +0 -1
- package/plugins/wordup.ts +78 -3
|
@@ -67,7 +67,7 @@ You can put iconify icons, emojis and urls in `icon`. Smart icon will render the
|
|
|
67
67
|
<div class="space-x-2">
|
|
68
68
|
|
|
69
69
|
:icon{name="/logo.svg"}
|
|
70
|
-
:icon{name="https://
|
|
70
|
+
:icon{name="https://img.icons8.com/color/48/vue-js.png" size=40}
|
|
71
71
|
|
|
72
72
|
</div>
|
|
73
73
|
::
|
|
@@ -85,7 +85,7 @@ You can put iconify icons, emojis and urls in `icon`. Smart icon will render the
|
|
|
85
85
|
|
|
86
86
|
**URL**
|
|
87
87
|
:icon{name="/logo.svg"}
|
|
88
|
-
:icon{name="https://
|
|
88
|
+
:icon{name="https://img.icons8.com/color/48/vue-js.png" size=40} # Keep care of CORS origin restrictions.
|
|
89
89
|
```
|
|
90
90
|
::
|
|
91
91
|
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/* & > p:not(:has(span)):not(:first-child) { */
|
|
12
|
-
&
|
|
13
|
-
|
|
12
|
+
& p:not(:first-child) {
|
|
13
|
+
--at-apply: "leading-7 [&:not(:first-child)]:mt-6";
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
& h2.prose-h2:first-child {
|
|
@@ -34,16 +34,22 @@
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
& code {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
--at-apply: "px-1.5 text-sm font-mono font-medium rounded-md inline-block border bg-muted/50";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
& pre {
|
|
41
|
+
--at-apply: "p-3 rounded-lg border bg-muted/30! text-card-foreground shadow-sm overflow-x-auto";
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
& code {
|
|
44
|
+
--at-apply: "bg-transparent p-0 border-none";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
[in-stack="true"] pre {
|
|
49
|
+
--at-apply: "rounded-none border-b-none border-l-none border-r-none shadow-none";
|
|
50
|
+
}
|
|
51
|
+
.FRONTMATTER pre {
|
|
52
|
+
--at-apply: "rounded-none border-none shadow-none bg-transparent";
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
& > div {
|
|
@@ -1,27 +1,61 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import type { NavItem } from '@nuxt/content'
|
|
3
3
|
|
|
4
|
-
const { items, title } = defineProps<{
|
|
4
|
+
const { items, title, removeCurrentPage } = defineProps<{
|
|
5
5
|
title?: string
|
|
6
|
-
items: NavItem[]
|
|
6
|
+
items: NavItem[] | string[]
|
|
7
|
+
removeCurrentPage?: boolean
|
|
7
8
|
}>()
|
|
8
9
|
|
|
9
10
|
const { navigation } = useContent()
|
|
11
|
+
const { navDirFromPath, navPageFromPath } = useContentHelpers()
|
|
12
|
+
const route = useRoute()
|
|
10
13
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const links = computed(() => {
|
|
15
|
+
const _links = []
|
|
16
|
+
for (const item of items) {
|
|
17
|
+
if (typeof item === 'string') {
|
|
18
|
+
if (item.endsWith('/')) {
|
|
19
|
+
const path = item.replace(/\/$/, '')
|
|
20
|
+
const dir = navDirFromPath(path, navigation.value || [])
|
|
21
|
+
const _dir = removeCurrentPage ? dir?.filter(d => d._path !== route.path) : dir
|
|
22
|
+
if (_dir) {
|
|
23
|
+
_links.push(..._dir)
|
|
24
|
+
}
|
|
17
25
|
}
|
|
26
|
+
else {
|
|
27
|
+
_links.push(navPageFromPath(item as unknown as string, navigation.value || []))
|
|
28
|
+
}
|
|
29
|
+
// _links.push({
|
|
30
|
+
// ...navigation.value?.find(n => item === n._path),
|
|
31
|
+
// _path: item,
|
|
32
|
+
// })
|
|
18
33
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
34
|
+
else {
|
|
35
|
+
_links.push({
|
|
36
|
+
...item,
|
|
37
|
+
_path: item.to || item._path,
|
|
38
|
+
})
|
|
23
39
|
}
|
|
24
|
-
}
|
|
40
|
+
}
|
|
41
|
+
return _links
|
|
42
|
+
// return items?.map((item) => {
|
|
43
|
+
// console.log(
|
|
44
|
+
// item, navigation.value, navigation.value?.find(n => item === n._path),
|
|
45
|
+
// navDirFromPath(item as unknown as string, navigation.value || []),
|
|
46
|
+
// )
|
|
47
|
+
// if (typeof item === 'string') {
|
|
48
|
+
// return {
|
|
49
|
+
// ...navigation.value?.find(n => item === n._path),
|
|
50
|
+
// _path: item,
|
|
51
|
+
// }
|
|
52
|
+
// }
|
|
53
|
+
|
|
54
|
+
// return {
|
|
55
|
+
// ...item,
|
|
56
|
+
// _path: item.to || item._path,
|
|
57
|
+
// }
|
|
58
|
+
// })
|
|
25
59
|
})
|
|
26
60
|
</script>
|
|
27
61
|
|
|
@@ -35,7 +69,7 @@ const _links = computed(() => {
|
|
|
35
69
|
</p>
|
|
36
70
|
|
|
37
71
|
<MenuItem
|
|
38
|
-
v-for="link in
|
|
72
|
+
v-for="link in links"
|
|
39
73
|
:key="link._path"
|
|
40
74
|
:link="link"
|
|
41
75
|
/>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<code class="inline-code
|
|
2
|
+
<code class="inline-code px-1.5 text-sm font-mono font-medium rounded-md inline-block border bg-muted/50">
|
|
3
3
|
<slot />
|
|
4
4
|
</code>
|
|
5
5
|
</template>
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
line-height: 1.25rem;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
p .inline-code {
|
|
17
|
+
/* p .inline-code {
|
|
18
18
|
vertical-align: -0.05rem;
|
|
19
|
-
}
|
|
19
|
+
} */
|
|
20
20
|
|
|
21
21
|
strong .inline-code {
|
|
22
22
|
font-weight: inherit;
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { cn } from '@/lib/utils'
|
|
3
|
+
import Slugger from 'github-slugger'
|
|
4
|
+
|
|
5
|
+
const slugger = new Slugger()
|
|
3
6
|
|
|
4
7
|
// Don't inherit attrs, we want to use it with cn
|
|
5
8
|
defineOptions({
|
|
6
9
|
inheritAttrs: false,
|
|
7
10
|
})
|
|
8
11
|
|
|
9
|
-
defineProps<{
|
|
12
|
+
const props = defineProps<{
|
|
10
13
|
title?: string
|
|
11
14
|
}>()
|
|
12
15
|
|
|
16
|
+
const id = computed(() => slugger.slug(props.title ?? ''))
|
|
17
|
+
|
|
13
18
|
defineSlots()
|
|
14
19
|
</script>
|
|
15
20
|
|
|
@@ -20,6 +25,7 @@ defineSlots()
|
|
|
20
25
|
'relative p-6 mt-6',
|
|
21
26
|
classToString($attrs.class),
|
|
22
27
|
)"
|
|
28
|
+
:aria-labelledby="id"
|
|
23
29
|
>
|
|
24
30
|
<div>
|
|
25
31
|
<div
|
|
@@ -32,7 +38,10 @@ defineSlots()
|
|
|
32
38
|
/>
|
|
33
39
|
</div>
|
|
34
40
|
|
|
35
|
-
<ProseH2
|
|
41
|
+
<ProseH2
|
|
42
|
+
v-if="title"
|
|
43
|
+
:id
|
|
44
|
+
>
|
|
36
45
|
{{ title }}
|
|
37
46
|
</ProseH2>
|
|
38
47
|
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
:target
|
|
5
5
|
:icon
|
|
6
6
|
>
|
|
7
|
-
|
|
7
|
+
<slot>
|
|
8
|
+
{{ $t(prefix) }} <span class="font-semibold">{{ computedTitle }}</span>
|
|
9
|
+
</slot>
|
|
8
10
|
</Alert>
|
|
9
11
|
</template>
|
|
10
12
|
|
|
@@ -13,11 +15,11 @@ const {
|
|
|
13
15
|
prefix = 'Read more at',
|
|
14
16
|
title,
|
|
15
17
|
to,
|
|
16
|
-
icon = 'lucide:
|
|
18
|
+
icon = 'lucide:info',
|
|
17
19
|
} = defineProps<{
|
|
18
20
|
prefix?: string
|
|
19
21
|
title?: string
|
|
20
|
-
to
|
|
22
|
+
to?: string
|
|
21
23
|
target?: Target
|
|
22
24
|
icon?: string
|
|
23
25
|
}>()
|
|
@@ -27,11 +29,14 @@ const computedTitle = computed<string>(
|
|
|
27
29
|
if (title)
|
|
28
30
|
return title
|
|
29
31
|
|
|
32
|
+
if (!to)
|
|
33
|
+
return ''
|
|
34
|
+
|
|
30
35
|
try {
|
|
31
36
|
if (to?.startsWith('http'))
|
|
32
37
|
return to.replace('http://', '').replace('https://', '').replace(/\/$/, '')
|
|
33
38
|
|
|
34
|
-
return useBreadcrumb(to).filter(x => x.title !== '').map(x => x.title).join(' > ')
|
|
39
|
+
return useBreadcrumb(to).filter(x => x.title !== '').map(x => x.title.replace(/#.*$/, '')).join(' > ')
|
|
35
40
|
}
|
|
36
41
|
catch {
|
|
37
42
|
return to
|
|
@@ -4,14 +4,17 @@
|
|
|
4
4
|
v-model="mdcAttrs.type"
|
|
5
5
|
chevron
|
|
6
6
|
tooltip="Type"
|
|
7
|
-
placement="bottom-
|
|
7
|
+
placement="bottom-start"
|
|
8
8
|
:items="types"
|
|
9
|
-
class="absolute
|
|
9
|
+
class="absolute left-3 top--2 z-10 opacity-0 border rounded pl-2 pr-1 transition-opacity group-hover:opacity-100 bg-background"
|
|
10
10
|
>
|
|
11
11
|
<span class="text-xs font-mono capitalize">{{ model.type }}</span>
|
|
12
12
|
</UiEditorDropdown>
|
|
13
13
|
|
|
14
|
-
<Alert
|
|
14
|
+
<Alert
|
|
15
|
+
v-bind="model"
|
|
16
|
+
no-click
|
|
17
|
+
>
|
|
15
18
|
<p
|
|
16
19
|
:ref="props.contentRef"
|
|
17
20
|
class="[&>div]:min-h-5.5"
|
|
@@ -26,7 +29,7 @@ import type { VueNodeViewProps } from 'prosekit/vue'
|
|
|
26
29
|
const props = defineProps<VueNodeViewProps>()
|
|
27
30
|
const { getComponentProps } = useMdcEditor()
|
|
28
31
|
|
|
29
|
-
const types = ['default', 'info', 'success', 'warning', 'danger', '
|
|
32
|
+
const types = ['default', 'info', 'success', 'warning', 'danger', 'example', 'secondary']
|
|
30
33
|
|
|
31
34
|
const { model, mdcAttrs } = getComponentProps(props, {
|
|
32
35
|
id: {
|
|
@@ -3,28 +3,32 @@
|
|
|
3
3
|
class="group relative"
|
|
4
4
|
contenteditable="false"
|
|
5
5
|
>
|
|
6
|
-
<
|
|
6
|
+
<ReadMore
|
|
7
7
|
v-bind="{ ...model, title: undefined, noClick: true }"
|
|
8
8
|
class="[&_span.w-full]:flex [&_span.w-full]:items-center"
|
|
9
9
|
>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
<input
|
|
11
|
+
ref="prefixInput"
|
|
12
|
+
v-model="mdcAttrs.prefix"
|
|
13
|
+
name="prefix"
|
|
14
|
+
class="inline-block outline-none focus-within:bg-muted py-1 my--1"
|
|
15
|
+
:style="{ width: `${inputWidth}px` }"
|
|
16
|
+
@input="updateInputWidth"
|
|
17
|
+
>
|
|
18
|
+
<!-- <div
|
|
15
19
|
contenteditable="true"
|
|
16
20
|
class="inline-block w-min outline-none hover:bg-muted/50 focus-within:bg-muted whitespace-nowrap mr-2"
|
|
17
21
|
@input="updatePrefix"
|
|
18
22
|
v-text="model.prefix"
|
|
19
|
-
/>
|
|
23
|
+
/> -->
|
|
20
24
|
|
|
21
25
|
<UiEditorLinkInput
|
|
22
|
-
class="inline-flex flex-1 my--2 [&>div]:w-full"
|
|
26
|
+
class="inline-flex flex-1 my--2 [&>div]:w-full ml--1"
|
|
23
27
|
dropdown-classes="DROPDOWN_MENU absolute mt-8 !w-[500px]"
|
|
24
28
|
:model-value="model.to"
|
|
25
29
|
@update:model-value="updateLink"
|
|
26
30
|
/>
|
|
27
|
-
</
|
|
31
|
+
</ReadMore>
|
|
28
32
|
</div>
|
|
29
33
|
</template>
|
|
30
34
|
|
|
@@ -43,7 +47,7 @@ const { mdcAttrs, model } = getComponentProps(props, {
|
|
|
43
47
|
},
|
|
44
48
|
icon: {
|
|
45
49
|
type: String,
|
|
46
|
-
default: 'lucide:
|
|
50
|
+
default: 'lucide:info',
|
|
47
51
|
},
|
|
48
52
|
prefix: {
|
|
49
53
|
type: String,
|
|
@@ -60,11 +64,38 @@ const { mdcAttrs, model } = getComponentProps(props, {
|
|
|
60
64
|
},
|
|
61
65
|
})
|
|
62
66
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
const prefixInput = useTemplateRef<HTMLInputElement>('prefixInput')
|
|
68
|
+
const inputWidth = ref(100)
|
|
69
|
+
|
|
70
|
+
function updateInputWidth() {
|
|
71
|
+
if (!prefixInput.value) return
|
|
72
|
+
|
|
73
|
+
// Create a temporary span to measure text width
|
|
74
|
+
const measure = document.createElement('span')
|
|
75
|
+
measure.style.visibility = 'hidden'
|
|
76
|
+
measure.style.position = 'absolute'
|
|
77
|
+
measure.style.whiteSpace = 'pre'
|
|
78
|
+
measure.style.font = window.getComputedStyle(prefixInput.value).font
|
|
79
|
+
measure.textContent = prefixInput.value.value || prefixInput.value.placeholder || ''
|
|
80
|
+
|
|
81
|
+
document.body.appendChild(measure)
|
|
82
|
+
inputWidth.value = Math.max(measure.offsetWidth, 20) // Add padding, min 20px
|
|
83
|
+
document.body.removeChild(measure)
|
|
66
84
|
}
|
|
85
|
+
|
|
67
86
|
function updateLink(to: string) {
|
|
68
87
|
mdcAttrs.to = to
|
|
69
88
|
}
|
|
89
|
+
|
|
90
|
+
watch(() => mdcAttrs.prefix, () => {
|
|
91
|
+
nextTick(() => {
|
|
92
|
+
updateInputWidth()
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
onMounted(() => {
|
|
97
|
+
nextTick(() => {
|
|
98
|
+
updateInputWidth()
|
|
99
|
+
})
|
|
100
|
+
})
|
|
70
101
|
</script>
|
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
<
|
|
4
|
-
<div
|
|
5
|
-
|
|
2
|
+
<render />
|
|
3
|
+
<!-- <Tabs v-bind="model">
|
|
4
|
+
<div
|
|
5
|
+
v-for="(slot, index) in props.node.value.children ?? []"
|
|
6
|
+
:key="index"
|
|
7
|
+
:label="slot.attrs?.attributes?.label"
|
|
8
|
+
:icon="slot.attrs?.attributes?.icon"
|
|
9
|
+
>
|
|
10
|
+
{{ slot.attrs }}
|
|
11
|
+
<component
|
|
12
|
+
:is="slot.type"
|
|
13
|
+
:label="slot.attrs.label"
|
|
14
|
+
/>
|
|
15
|
+
</div>
|
|
16
|
+
</Tabs> -->
|
|
6
17
|
</template>
|
|
7
18
|
|
|
8
19
|
<script lang="ts" setup>
|
|
9
20
|
import type { VueNodeViewProps } from 'prosekit/vue'
|
|
21
|
+
import Tabs from '../Tabs.vue'
|
|
10
22
|
|
|
11
23
|
// import { useMdcEditor } from '#imports'
|
|
12
24
|
|
|
13
25
|
const props = defineProps<VueNodeViewProps>()
|
|
14
|
-
|
|
15
|
-
const variants = ['separate', 'card']
|
|
26
|
+
const variants = ['separate', 'card', 'line', 'combobox']
|
|
16
27
|
|
|
17
28
|
const { getComponentProps } = useMdcEditor()
|
|
18
29
|
const { model } = getComponentProps(props, {
|
|
@@ -34,5 +45,44 @@ const { model } = getComponentProps(props, {
|
|
|
34
45
|
type: Boolean,
|
|
35
46
|
default: true,
|
|
36
47
|
},
|
|
48
|
+
sync: {
|
|
49
|
+
type: String,
|
|
50
|
+
},
|
|
51
|
+
disableSearch: {
|
|
52
|
+
type: Boolean,
|
|
53
|
+
default: false,
|
|
54
|
+
},
|
|
55
|
+
searchPlaceholder: {
|
|
56
|
+
type: String,
|
|
57
|
+
default: 'Search Tab...',
|
|
58
|
+
},
|
|
59
|
+
searchEmpty: {
|
|
60
|
+
type: String,
|
|
61
|
+
default: 'No tab found.',
|
|
62
|
+
},
|
|
37
63
|
})
|
|
64
|
+
|
|
65
|
+
function render() {
|
|
66
|
+
// const slots = _slots?.default?.() || []
|
|
67
|
+
// console.log('slots', slots)
|
|
68
|
+
// const slotsData = slots.map((slot, index) => ({
|
|
69
|
+
// label: slot.props?.label || slot.props?.filename || '',
|
|
70
|
+
// index,
|
|
71
|
+
// }))
|
|
72
|
+
|
|
73
|
+
// <div
|
|
74
|
+
// v-for="(slot, index) in props.node.value.children ?? []"
|
|
75
|
+
// :key="index"
|
|
76
|
+
// :label="slot.attrs?.attributes?.label"
|
|
77
|
+
// :icon="slot.attrs?.attributes?.icon"
|
|
78
|
+
// >
|
|
79
|
+
|
|
80
|
+
return h(
|
|
81
|
+
Tabs,
|
|
82
|
+
model,
|
|
83
|
+
() => props.node.value.children.map(child => h('div', { label: child.attrs?.attributes?.label, icon: child.attrs?.attributes?.icon })),
|
|
84
|
+
|
|
85
|
+
// () => [h('div', { label: 'Tab 1' }), h('div', { label: 'Tab 2' })],
|
|
86
|
+
)
|
|
87
|
+
}
|
|
38
88
|
</script>
|
|
@@ -4,57 +4,50 @@
|
|
|
4
4
|
class="app__aside relative overflow-hidden text-sm mt-6 pr-6 md:pr-4 h-[calc(100vh-var(--header-height)-var(--banner-height)-7rem)]"
|
|
5
5
|
type="hover"
|
|
6
6
|
>
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
v-if="config.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
:key="link.id"
|
|
7
|
+
<template v-if="!tree?.children.length">
|
|
8
|
+
<LayoutHeaderNavMobile
|
|
9
|
+
v-if="isMobile"
|
|
10
|
+
class="mb-5 border-b pb-2"
|
|
11
|
+
/>
|
|
12
|
+
|
|
13
|
+
<LayoutSearchButton v-if="config.search.inAside" />
|
|
14
|
+
|
|
15
|
+
<ul
|
|
16
|
+
v-if="config.aside.useLevel"
|
|
17
|
+
class="flex flex-col gap-1 border-b pb-4"
|
|
19
18
|
>
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
:class="[path.startsWith(link._path) && 'bg-muted !text-primary']"
|
|
19
|
+
<li
|
|
20
|
+
v-for="link in navigation || nav"
|
|
21
|
+
:key="link.id"
|
|
24
22
|
>
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class="self-center"
|
|
29
|
-
:size="16"
|
|
30
|
-
/>
|
|
31
|
-
{{ link.title }}
|
|
23
|
+
<LayoutAsideMenuItem v-bind="{ ...link, children: [] }" />
|
|
24
|
+
</li>
|
|
25
|
+
</ul>
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
:links
|
|
50
|
-
:level="0"
|
|
51
|
-
:class="[config.aside.useLevel ? 'pt-4' : 'pt-1']"
|
|
27
|
+
<LayoutAsideTree
|
|
28
|
+
:links
|
|
29
|
+
:level="0"
|
|
30
|
+
:class="[config.aside.useLevel ? 'pt-4' : 'pt-1']"
|
|
31
|
+
/>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<MDCRenderer
|
|
35
|
+
v-else
|
|
36
|
+
:body="tree"
|
|
37
|
+
class="min-h-10"
|
|
38
|
+
:class="{ 'flex flex-col gap-1': !!tree }"
|
|
39
|
+
:components="{
|
|
40
|
+
'aside-menu-item': AsideMenuItem,
|
|
41
|
+
'aside-tree-2': AsideTree2,
|
|
42
|
+
}"
|
|
52
43
|
/>
|
|
53
44
|
</UiScrollArea>
|
|
54
45
|
</template>
|
|
55
46
|
|
|
56
47
|
<script setup lang="ts">
|
|
57
48
|
import type { NavItem } from '@nuxt/content'
|
|
49
|
+
import AsideMenuItem from './AsideMenuItem.vue'
|
|
50
|
+
import AsideTree2 from './AsideTree2.vue'
|
|
58
51
|
|
|
59
52
|
const { isMobile, navigation, level = 0 } = defineProps<{
|
|
60
53
|
isMobile: boolean
|
|
@@ -79,5 +72,20 @@ const links = computed(() => {
|
|
|
79
72
|
return navigation || nav.value
|
|
80
73
|
})
|
|
81
74
|
|
|
82
|
-
const path = computed(() => useRoute().path)
|
|
75
|
+
// const path = computed(() => useRoute().path)
|
|
76
|
+
|
|
77
|
+
const menu = '/_menu-aside'
|
|
78
|
+
const { data: count } = await useAsyncData(menu + '-count', () => queryContent(menu).count())
|
|
79
|
+
|
|
80
|
+
let tree = undefined
|
|
81
|
+
if (count.value) {
|
|
82
|
+
const { data } = await useAsyncData(menu, () =>
|
|
83
|
+
queryContent(menu).findOne(),
|
|
84
|
+
)
|
|
85
|
+
tree = await useMdcMenu(data, {
|
|
86
|
+
menuComponentName: 'aside-menu-item',
|
|
87
|
+
submenuComponentName: 'aside-tree-2',
|
|
88
|
+
})
|
|
89
|
+
console.log('aime', tree)
|
|
90
|
+
}
|
|
83
91
|
</script>
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { BadgeVariants } from '@/components/ui/badge'
|
|
3
|
+
import type { NavItem } from '@nuxt/content'
|
|
4
|
+
|
|
5
|
+
defineProps<{
|
|
6
|
+
to?: string
|
|
7
|
+
_path?: string
|
|
8
|
+
target?: string
|
|
9
|
+
icon?: string
|
|
10
|
+
title?: string
|
|
11
|
+
navBadges?: {
|
|
12
|
+
variant: BadgeVariants['variant']
|
|
13
|
+
type: BadgeVariants['type']
|
|
14
|
+
size: BadgeVariants['size']
|
|
15
|
+
value: string
|
|
16
|
+
}[]
|
|
17
|
+
redirect?: string
|
|
18
|
+
children?: NavItem[]
|
|
19
|
+
|
|
20
|
+
}>()
|
|
21
|
+
|
|
22
|
+
const { path } = useRoute()
|
|
23
|
+
const { folderStyle: defaultFolderStyle } = useConfig().value.aside
|
|
24
|
+
// const folderStyle = computed(() => link.sidebar?.style ?? defaultFolderStyle)
|
|
25
|
+
const folderStyle = computed(() => defaultFolderStyle)
|
|
26
|
+
const isOpen = ref(false)
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<template>
|
|
30
|
+
<div v-if="$slots.content">
|
|
31
|
+
<template v-if="folderStyle === 'group'">
|
|
32
|
+
<div
|
|
33
|
+
class="mt-4 flex items-center gap-2 rounded-md px-2 text-base font-semibold outline-none"
|
|
34
|
+
:class="['h-8']"
|
|
35
|
+
>
|
|
36
|
+
<LayoutAsideTreeItemButton :link="{ title: title ?? '', _path: _path ?? '', icon, navBadges }" />
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div class="flex flex-col gap-1 py-1">
|
|
40
|
+
<slot name="content" />
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<template v-else>
|
|
45
|
+
<button
|
|
46
|
+
class="flex w-full cursor-pointer items-center gap-2 rounded-md p-2 text-left text-sm font-medium text-foreground/80 hover:bg-muted hover:text-primary"
|
|
47
|
+
:class="['h-8']"
|
|
48
|
+
@click="isOpen = !isOpen"
|
|
49
|
+
>
|
|
50
|
+
<NuxtIcon
|
|
51
|
+
v-if="folderStyle === 'tree'"
|
|
52
|
+
name="lucide:chevron-down"
|
|
53
|
+
class="transition-transform"
|
|
54
|
+
:class="[!isOpen && '-rotate-90']"
|
|
55
|
+
/>
|
|
56
|
+
<LayoutAsideTreeItemButton :link="{ title: title ?? '', _path: _path ?? '', icon, navBadges }" />
|
|
57
|
+
<NuxtIcon
|
|
58
|
+
v-if="folderStyle === 'default'"
|
|
59
|
+
name="lucide:chevron-down"
|
|
60
|
+
class="ml-auto transition-transform"
|
|
61
|
+
:class="[!isOpen && '-rotate-90']"
|
|
62
|
+
/>
|
|
63
|
+
</button>
|
|
64
|
+
<div v-show="isOpen">
|
|
65
|
+
<ul
|
|
66
|
+
class="flex flex-col gap-1 py-1"
|
|
67
|
+
:class="['mx-3.5 border-l px-2.5']"
|
|
68
|
+
>
|
|
69
|
+
<slot name="content" />
|
|
70
|
+
</ul>
|
|
71
|
+
</div>
|
|
72
|
+
</template>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<NuxtLink
|
|
76
|
+
v-else
|
|
77
|
+
:to="redirect ?? _path ?? to"
|
|
78
|
+
class="flex h-8 items-center gap-2 rounded-md p-2 text-sm text-foreground/80 hover:bg-muted hover:text-primary"
|
|
79
|
+
:class="[(_path && path.startsWith(_path) || to && path.startsWith(to)) && 'bg-muted !text-primary']"
|
|
80
|
+
>
|
|
81
|
+
<Icon
|
|
82
|
+
v-if="icon"
|
|
83
|
+
:name="icon"
|
|
84
|
+
class="self-center"
|
|
85
|
+
:size="16"
|
|
86
|
+
/>
|
|
87
|
+
|
|
88
|
+
{{ title }}
|
|
89
|
+
|
|
90
|
+
<span
|
|
91
|
+
v-for="(badge, i) in navBadges"
|
|
92
|
+
:key="i"
|
|
93
|
+
>
|
|
94
|
+
<Badge
|
|
95
|
+
:variant="badge.variant"
|
|
96
|
+
:type="badge.type"
|
|
97
|
+
:size="badge.size ?? 'sm'"
|
|
98
|
+
>
|
|
99
|
+
{{ badge.value }}
|
|
100
|
+
</Badge>
|
|
101
|
+
</span>
|
|
102
|
+
</NuxtLink>
|
|
103
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<NuxtLink
|
|
3
|
+
:to
|
|
4
|
+
class="flex items-center gap-2 rounded-md p-2 text-sm text-foreground/80 hover:bg-muted hover:text-primary"
|
|
5
|
+
>
|
|
6
|
+
<Icon
|
|
7
|
+
v-if="icon"
|
|
8
|
+
:name="icon"
|
|
9
|
+
class="self-center"
|
|
10
|
+
:size="16"
|
|
11
|
+
/>
|
|
12
|
+
{{ title }}
|
|
13
|
+
</NuxtLink>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup lang="ts">
|
|
17
|
+
import type { NavItem } from '@nuxt/content'
|
|
18
|
+
|
|
19
|
+
defineProps<{
|
|
20
|
+
to?: string
|
|
21
|
+
target?: string
|
|
22
|
+
showLinkIcon?: boolean
|
|
23
|
+
icon?: string
|
|
24
|
+
title?: string
|
|
25
|
+
description?: string
|
|
26
|
+
tag?: string
|
|
27
|
+
}>()
|
|
28
|
+
</script>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<div v-if="link.children">
|
|
5
5
|
<template v-if="folderStyle === 'group'">
|
|
6
6
|
<div
|
|
7
|
-
class="mt-
|
|
7
|
+
class="mt-4 flex items-center gap-2 rounded-md px-2 text-base font-semibold outline-none"
|
|
8
8
|
:class="[link.navTruncate !== false && 'h-8']"
|
|
9
9
|
>
|
|
10
10
|
<LayoutAsideTreeItemButton :link />
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<NuxtLink
|
|
3
3
|
v-if="prevNext && prevNext._path"
|
|
4
4
|
:to="prevNext._path"
|
|
5
|
-
class="mb-6 space-y-2 rounded-lg border p-4 transition-all hover:bg-muted/50"
|
|
5
|
+
class="inline-block w-full mb-6 space-y-2 rounded-lg border p-4 transition-all hover:bg-muted/50"
|
|
6
6
|
>
|
|
7
7
|
<div class="flex flex-row gap-3">
|
|
8
8
|
<div
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<UiButton
|
|
4
4
|
v-if="style === 'input'"
|
|
5
5
|
variant="outline"
|
|
6
|
-
class="h-8 w-full self-center rounded-md px-1.5 font-normal bg-
|
|
6
|
+
class="h-8 w-full self-center rounded-md px-1.5 font-normal bg-muted/50 text-muted-foreground hover:text-accent-foreground"
|
|
7
7
|
:class="[inAside ? 'mb-4' : 'md:w-32 lg:w-40']"
|
|
8
8
|
@click="isOpen = true"
|
|
9
9
|
>
|
|
@@ -16,15 +16,33 @@
|
|
|
16
16
|
|
|
17
17
|
<div
|
|
18
18
|
v-if="badges"
|
|
19
|
-
class="flex gap-2 pt-4"
|
|
19
|
+
class="flex items-center gap-2 pt-4"
|
|
20
20
|
>
|
|
21
|
-
<
|
|
21
|
+
<template
|
|
22
22
|
v-for="(badge, i) in badges"
|
|
23
23
|
:key="i"
|
|
24
|
-
:to="badge.to"
|
|
25
|
-
:target="badge.target"
|
|
26
24
|
>
|
|
25
|
+
<template v-if="badge.to">
|
|
26
|
+
<NuxtLink
|
|
27
|
+
:to="badge.to"
|
|
28
|
+
:target="badge.target"
|
|
29
|
+
>
|
|
30
|
+
<UiBadge
|
|
31
|
+
:variant="badge.variant || 'secondary'"
|
|
32
|
+
:type="badge.type"
|
|
33
|
+
class="gap-1 rounded-md"
|
|
34
|
+
>
|
|
35
|
+
<Icon
|
|
36
|
+
:size="12"
|
|
37
|
+
:name="badge.icon || 'lucide:external-link'"
|
|
38
|
+
/>
|
|
39
|
+
{{ badge.value }}
|
|
40
|
+
</UiBadge>
|
|
41
|
+
</NuxtLink>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
27
44
|
<UiBadge
|
|
45
|
+
v-else
|
|
28
46
|
:variant="badge.variant || 'secondary'"
|
|
29
47
|
:type="badge.type"
|
|
30
48
|
class="gap-1 rounded-md"
|
|
@@ -36,7 +54,7 @@
|
|
|
36
54
|
/>
|
|
37
55
|
{{ badge.value }}
|
|
38
56
|
</UiBadge>
|
|
39
|
-
</
|
|
57
|
+
</template>
|
|
40
58
|
</div>
|
|
41
59
|
|
|
42
60
|
<div
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
v-if="links"
|
|
22
22
|
class="pt-5 text-muted-foreground"
|
|
23
23
|
>
|
|
24
|
-
<
|
|
24
|
+
<NuxtLinkLocale
|
|
25
25
|
v-for="(link, i) in links"
|
|
26
26
|
:key="i"
|
|
27
|
-
:to="link.to"
|
|
27
|
+
:to="localePath(link.to)"
|
|
28
28
|
:target="link.target"
|
|
29
29
|
class="flex w-full gap-1 underline-offset-4 hover:underline [&:not(:first-child)]:pt-3"
|
|
30
30
|
>
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
class="ml-auto self-center text-muted-foreground"
|
|
41
41
|
size="13"
|
|
42
42
|
/>
|
|
43
|
-
</
|
|
43
|
+
</NuxtLinkLocale>
|
|
44
44
|
</div>
|
|
45
45
|
</div>
|
|
46
46
|
<!-- <div class="flex-grow" /> -->
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
defineProps<{ isSmall?: boolean }>()
|
|
77
77
|
|
|
78
78
|
const { toc } = useContent()
|
|
79
|
+
const { localePath } = useI18nDocs()
|
|
79
80
|
const { title, links: configLinks } = useConfig().value.toc
|
|
80
81
|
const { border } = useConfig().value.header
|
|
81
82
|
const isOpen = ref(false)
|
|
@@ -3,15 +3,25 @@
|
|
|
3
3
|
<li
|
|
4
4
|
v-for="link in links"
|
|
5
5
|
:key="link.id"
|
|
6
|
-
class="pt-2"
|
|
6
|
+
class="relative pt-2"
|
|
7
7
|
>
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
<div class="flex items-center">
|
|
9
|
+
<div
|
|
10
|
+
v-if="level === 0 && progressBar"
|
|
11
|
+
class="absolute md:ml-1.5 left-0 top-0 bottom-0 w-px transition-colors duration-300 ease-in-out"
|
|
12
|
+
:class="isActive(link) ? 'bg-primary' : 'bg-border'"
|
|
13
|
+
/>
|
|
14
|
+
<NuxtLink
|
|
15
|
+
:to="`#${link.id}`"
|
|
16
|
+
class="text-muted-foreground transition-all hover:text-primary"
|
|
17
|
+
:class="{
|
|
18
|
+
'pl-4.5 md:pl-5.5': progressBar,
|
|
19
|
+
'text-primary': isActive(link),
|
|
20
|
+
}"
|
|
21
|
+
>
|
|
22
|
+
{{ link.text }}
|
|
23
|
+
</NuxtLink>
|
|
24
|
+
</div>
|
|
15
25
|
<TocTree
|
|
16
26
|
v-if="link.children"
|
|
17
27
|
:links="(link.children || []).filter((x: any) => x.id !== 'hide-toc')"
|
|
@@ -29,8 +39,16 @@ defineProps<{
|
|
|
29
39
|
level: number
|
|
30
40
|
}>()
|
|
31
41
|
|
|
42
|
+
const config = useConfig()
|
|
43
|
+
const progressBar = config.value.toc.progressBar
|
|
44
|
+
|
|
32
45
|
const { activeHeadings, updateHeadings } = useScrollspy()
|
|
33
46
|
|
|
47
|
+
function isActive(link: TocLink) {
|
|
48
|
+
return activeHeadings.value.includes(link.id)
|
|
49
|
+
|| link.children?.some(child => activeHeadings.value.includes(child.id))
|
|
50
|
+
}
|
|
51
|
+
|
|
34
52
|
onMounted(() =>
|
|
35
53
|
updateHeadings([
|
|
36
54
|
...document.querySelectorAll('.page-content h1'),
|
package/composables/useConfig.ts
CHANGED
|
@@ -152,6 +152,7 @@ const defaultConfig: DefaultConfig = {
|
|
|
152
152
|
toc: {
|
|
153
153
|
enable: true,
|
|
154
154
|
enableInMobile: false,
|
|
155
|
+
progressBar: true,
|
|
155
156
|
title: 'On This Page',
|
|
156
157
|
links: [],
|
|
157
158
|
carbonAds: {
|
|
@@ -177,10 +178,10 @@ export function useConfig() {
|
|
|
177
178
|
const appConfig = computed(() => useAppConfig()?.shadcnDocs || {})
|
|
178
179
|
const { navKeyFromPath } = useContentHelpers()
|
|
179
180
|
const { navigation, page } = useContent()
|
|
180
|
-
const route = useRoute()
|
|
181
181
|
|
|
182
182
|
return computed(
|
|
183
183
|
() => {
|
|
184
|
+
const route = useRoute()
|
|
184
185
|
const processedConfig = customDefu(appConfig.value, defaultConfig)
|
|
185
186
|
const header = processedConfig.header
|
|
186
187
|
const banner = processedConfig.banner
|
package/index.d.ts
CHANGED
|
@@ -491,6 +491,12 @@ interface DefaultConfig {
|
|
|
491
491
|
* @default false
|
|
492
492
|
*/
|
|
493
493
|
enableInMobile: boolean
|
|
494
|
+
/**
|
|
495
|
+
* Show progress bar in table of contents
|
|
496
|
+
*
|
|
497
|
+
* @default true
|
|
498
|
+
*/
|
|
499
|
+
progressBar: boolean
|
|
494
500
|
/**
|
|
495
501
|
* Table of contents title
|
|
496
502
|
*
|
|
@@ -595,19 +601,19 @@ interface DefaultConfig {
|
|
|
595
601
|
*
|
|
596
602
|
* @values 'zinc', 'slate', 'stone', 'gray', 'neutral', 'red', 'rose', 'orange', 'green', 'blue', 'yellow', 'violet'
|
|
597
603
|
*/
|
|
598
|
-
type Color
|
|
599
|
-
| 'zinc'
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
604
|
+
type Color
|
|
605
|
+
= | 'zinc'
|
|
606
|
+
| 'slate'
|
|
607
|
+
| 'stone'
|
|
608
|
+
| 'gray'
|
|
609
|
+
| 'neutral'
|
|
610
|
+
| 'red'
|
|
611
|
+
| 'rose'
|
|
612
|
+
| 'orange'
|
|
613
|
+
| 'green'
|
|
614
|
+
| 'blue'
|
|
615
|
+
| 'yellow'
|
|
616
|
+
| 'violet'
|
|
611
617
|
|
|
612
618
|
/**
|
|
613
619
|
* HTML anchor target attribute values
|
package/lib/utils.ts
ADDED
package/nuxt.config.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dirname, join } from 'node:path'
|
|
1
|
+
import { dirname, join, resolve } from 'node:path'
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
|
|
4
4
|
const currentDir = dirname(fileURLToPath(import.meta.url))
|
|
@@ -14,11 +14,37 @@ export default defineNuxtConfig({
|
|
|
14
14
|
'@nuxt/icon',
|
|
15
15
|
'@nuxtjs/color-mode',
|
|
16
16
|
'@nuxtjs/i18n',
|
|
17
|
-
'@unpress/nuxt-module',
|
|
18
|
-
// '../../../unpress/packages/nuxt-module/src/module',
|
|
19
17
|
// 'nuxt-og-image',
|
|
20
18
|
],
|
|
21
19
|
|
|
20
|
+
$development: {
|
|
21
|
+
modules: [
|
|
22
|
+
'../../../unpress/packages/nuxt-module/src/module',
|
|
23
|
+
],
|
|
24
|
+
alias: {
|
|
25
|
+
'@unpress/mdc-editor': r('../../unpress/packages/mdc-editor/src/module'),
|
|
26
|
+
'@unpress/nuxt-module': r('../../unpress/packages/nuxt-module/src/module'),
|
|
27
|
+
},
|
|
28
|
+
vite: {
|
|
29
|
+
server: {
|
|
30
|
+
fs: {
|
|
31
|
+
allow: [
|
|
32
|
+
// Allow serving files from the project root
|
|
33
|
+
currentDir,
|
|
34
|
+
// Allow serving files from the monorepo root (parent directories)
|
|
35
|
+
resolve(currentDir, '../../..'),
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
$production: {
|
|
43
|
+
modules: [
|
|
44
|
+
'@unpress/nuxt-module',
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
|
|
22
48
|
components: {
|
|
23
49
|
dirs: [
|
|
24
50
|
{
|
|
@@ -45,8 +71,8 @@ export default defineNuxtConfig({
|
|
|
45
71
|
documentDriven: true,
|
|
46
72
|
highlight: {
|
|
47
73
|
theme: {
|
|
48
|
-
default: '
|
|
49
|
-
dark: '
|
|
74
|
+
default: 'one-dark-pro',
|
|
75
|
+
dark: 'one-dark-pro',
|
|
50
76
|
},
|
|
51
77
|
preload: ['json', 'js', 'ts', 'html', 'css', 'vue', 'diff', 'shell', 'markdown', 'yaml', 'bash', 'ini'],
|
|
52
78
|
},
|
|
@@ -123,3 +149,7 @@ export default defineNuxtConfig({
|
|
|
123
149
|
componentDir: join(currentDir, './components/ui'),
|
|
124
150
|
},
|
|
125
151
|
})
|
|
152
|
+
|
|
153
|
+
function r(p: string) {
|
|
154
|
+
return resolve(__dirname, p)
|
|
155
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordup-md/nuxt-layer-shadcn-unocss",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.14",
|
|
5
5
|
"author": "Emmanuel Salomon <emmanuel.salomon@gmail.com>",
|
|
6
6
|
"main": "./nuxt.config.ts",
|
|
7
7
|
"repository": {
|
|
@@ -16,19 +16,6 @@
|
|
|
16
16
|
"wordup-md"
|
|
17
17
|
],
|
|
18
18
|
"license": "MIT",
|
|
19
|
-
"scripts": {
|
|
20
|
-
"dev": "nuxi dev .playground",
|
|
21
|
-
"dev:root": "nuxi dev",
|
|
22
|
-
"prepare": "nuxt prepare .playground",
|
|
23
|
-
"build": "nuxt build .playground",
|
|
24
|
-
"generate": "nuxt generate .playground",
|
|
25
|
-
"preview": "nuxt preview .playground",
|
|
26
|
-
"lint": "eslint .",
|
|
27
|
-
"build:json-doc": "tsx scripts/buildContentDoc-cli.ts",
|
|
28
|
-
"bump": "pnpm build:json-doc && bumpp --skip-install --commit --push --tag",
|
|
29
|
-
"release": "pnpm bump && npm publish",
|
|
30
|
-
"clean": "find . -name 'node_modules' -type d -prune -exec rm -rf '{}' + && find . -name '.nuxt' -type d -prune -exec rm -rf '{}' + && find . -name '.output' -type d -prune -exec rm -rf '{}' + && find . -name 'dist' -prune -exec rm -rf '{}' +"
|
|
31
|
-
},
|
|
32
19
|
"devDependencies": {
|
|
33
20
|
"@nuxt/eslint": "latest",
|
|
34
21
|
"@nuxt/kit": "^3.17.2",
|
|
@@ -58,7 +45,7 @@
|
|
|
58
45
|
"@nuxtjs/color-mode": "^3.5.2",
|
|
59
46
|
"@nuxtjs/i18n": "^9.5.4",
|
|
60
47
|
"@tanstack/vue-table": "^8.21.3",
|
|
61
|
-
"@unpress/nuxt-module": "0.1.13-beta.
|
|
48
|
+
"@unpress/nuxt-module": "0.1.13-beta.6",
|
|
62
49
|
"class-variance-authority": "^0.7.1",
|
|
63
50
|
"clsx": "^2.1.1",
|
|
64
51
|
"nuxt": "3.17.2",
|
|
@@ -67,9 +54,16 @@
|
|
|
67
54
|
"tailwind-merge": "^3.2.0"
|
|
68
55
|
},
|
|
69
56
|
"packageManager": "pnpm@10.4.1",
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
|
|
73
|
-
|
|
57
|
+
"scripts": {
|
|
58
|
+
"dev": "nuxi dev .playground",
|
|
59
|
+
"dev:root": "nuxi dev",
|
|
60
|
+
"build": "nuxt build .playground",
|
|
61
|
+
"generate": "nuxt generate .playground",
|
|
62
|
+
"preview": "nuxt preview .playground",
|
|
63
|
+
"lint": "eslint .",
|
|
64
|
+
"build:json-doc": "tsx scripts/buildContentDoc-cli.ts",
|
|
65
|
+
"bump": "pnpm build:json-doc && bumpp --skip-install --commit --push --tag",
|
|
66
|
+
"release": "pnpm bump && npm publish",
|
|
67
|
+
"clean": "find . -name 'node_modules' -type d -prune -exec rm -rf '{}' + && find . -name '.nuxt' -type d -prune -exec rm -rf '{}' + && find . -name '.output' -type d -prune -exec rm -rf '{}' + && find . -name 'dist' -prune -exec rm -rf '{}' +"
|
|
74
68
|
}
|
|
75
|
-
}
|
|
69
|
+
}
|
package/pages/[...slug].vue
CHANGED
package/plugins/wordup.ts
CHANGED
|
@@ -37,9 +37,84 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
37
37
|
transform() {
|
|
38
38
|
return { type: 'containerComponent', attrs: { componentName: 'read-more' } }
|
|
39
39
|
},
|
|
40
|
-
onSelect: () =>
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
onSelect: () => {
|
|
41
|
+
editor.commands.insertContainerComponent({
|
|
42
|
+
name: 'read-more',
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// Set cursor on the input of the component after insertion
|
|
46
|
+
// Use setTimeout to wait for the DOM update
|
|
47
|
+
nextTick(() => {
|
|
48
|
+
const { view, state } = editor
|
|
49
|
+
if (!view || !state) return
|
|
50
|
+
|
|
51
|
+
// Get the current selection position
|
|
52
|
+
const { $anchor } = state.selection
|
|
53
|
+
const pos = $anchor.pos
|
|
54
|
+
// Find the inserted containerComponent node
|
|
55
|
+
// Walk backwards to find the node we just inserted
|
|
56
|
+
let node = null
|
|
57
|
+
let nodePos = -1
|
|
58
|
+
|
|
59
|
+
// Check if we're inside a containerComponent
|
|
60
|
+
for (let depth = $anchor.depth; depth > 0; depth--) {
|
|
61
|
+
const nodeAtDepth = $anchor.node(depth)
|
|
62
|
+
if (nodeAtDepth.type.name === 'containerComponent'
|
|
63
|
+
&& nodeAtDepth.attrs.componentName === 'read-more') {
|
|
64
|
+
node = nodeAtDepth
|
|
65
|
+
nodePos = $anchor.before(depth)
|
|
66
|
+
break
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// If not found, search forward from current position
|
|
71
|
+
if (!node) {
|
|
72
|
+
const doc = state.doc
|
|
73
|
+
for (let i = Math.max(0, pos - 50); i < Math.min(doc.content.size, pos + 50); i++) {
|
|
74
|
+
const $pos = doc.resolve(i)
|
|
75
|
+
for (let depth = $pos.depth; depth > 0; depth--) {
|
|
76
|
+
const nodeAtDepth = $pos.node(depth)
|
|
77
|
+
if (nodeAtDepth.type.name === 'containerComponent'
|
|
78
|
+
&& nodeAtDepth.attrs.componentName === 'read-more') {
|
|
79
|
+
node = nodeAtDepth
|
|
80
|
+
nodePos = $pos.before(depth)
|
|
81
|
+
break
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (node) break
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const prevNode = view.domAtPos(nodePos - 1)
|
|
89
|
+
// get next html element with js
|
|
90
|
+
setTimeout(() => {
|
|
91
|
+
prevNode.node.nextElementSibling?.querySelector('input.INLINE_MENU_LINK_INPUT')?.focus()
|
|
92
|
+
}, 100)
|
|
93
|
+
// const node = editor.state.doc.nodeAt(nodePos)
|
|
94
|
+
// if (node && node.type.name === 'read-more') {
|
|
95
|
+
// const input = node.firstChild?.firstChild?.firstChild as HTMLInputElement
|
|
96
|
+
// if (input) {
|
|
97
|
+
// input.focus()
|
|
98
|
+
// }
|
|
99
|
+
// }
|
|
100
|
+
// if (node && nodePos >= 0) {
|
|
101
|
+
// // Set cursor inside the component (at the start of its content)
|
|
102
|
+
// // Position is after the opening tag, typically nodePos + 1
|
|
103
|
+
// const insidePos = nodePos + 1
|
|
104
|
+
|
|
105
|
+
// // Import TextSelection dynamically and set selection
|
|
106
|
+
// import('prosemirror-state').then(({ TextSelection }) => {
|
|
107
|
+
// const tr = state.tr
|
|
108
|
+
// const selection = TextSelection.create(tr.doc, insidePos)
|
|
109
|
+
// tr.setSelection(selection)
|
|
110
|
+
// view.dispatch(tr)
|
|
111
|
+
|
|
112
|
+
// // Focus the editor
|
|
113
|
+
// view.focus()
|
|
114
|
+
// })
|
|
115
|
+
// }
|
|
116
|
+
})
|
|
117
|
+
},
|
|
43
118
|
})
|
|
44
119
|
|
|
45
120
|
config.components.push({
|