Skip to content
This repository was archived by the owner on Sep 20, 2024. It is now read-only.

Commit 0e85cf3

Browse files
committed
feat(docs): add live code editor and highlighter
1 parent 1bd92a9 commit 0e85cf3

File tree

15 files changed

+549
-55
lines changed

15 files changed

+549
-55
lines changed

website/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"prism-theme-vars": "^0.1.4",
1919
"vue": ">=3.0.5",
2020
"vue-composable": "^1.0.0-beta.23",
21+
"vue-live": "2.0.0-alpha.7",
2122
"vue-prism-editor": "^2.0.0-alpha.2",
2223
"vue-router": "^4.0.6"
2324
},
@@ -34,6 +35,7 @@
3435
"remark-autolink-headings": "^6.0.1",
3536
"remark-frontmatter": "^3.0.0",
3637
"remark-gfm": "^1.0.0",
38+
"remark-mdx-code-meta": "^1.0.0",
3739
"remark-slug": "^6.0.0",
3840
"typescript": "^4.1.3",
3941
"vite": "2.1.5",
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<script lang="tsx">
2+
import { defineComponent, renderSlot, SetupContext } from 'vue'
3+
import { BoxProps } from '@chakra-ui/vue-next'
4+
import { DeepPartial } from '@chakra-ui/vue-system'
5+
6+
export const CopyButton = (props: any, { slots }: SetupContext) => {
7+
return (
8+
<c-button
9+
size="sm"
10+
position="absolute"
11+
textTransform="uppercase"
12+
colorScheme="teal"
13+
fontSize="xs"
14+
height="24px"
15+
top={0}
16+
zIndex="1"
17+
right="1.25em"
18+
{...props}
19+
>
20+
{renderSlot(slots, 'default')}
21+
</c-button>
22+
)
23+
}
24+
25+
export const EditableNotice = (props: DeepPartial<BoxProps>) => {
26+
return (
27+
<c-box
28+
position="absolute"
29+
width="full"
30+
top="-1.25em"
31+
left="0"
32+
roundedTop="8px"
33+
bg="#011627"
34+
py="2"
35+
zIndex="0"
36+
letterSpacing="wide"
37+
color="gray.400"
38+
fontSize="xs"
39+
fontWeight="semibold"
40+
textAlign="center"
41+
textTransform="uppercase"
42+
pointerEvents="none"
43+
{...props}
44+
>
45+
Editable Example
46+
</c-box>
47+
)
48+
}
49+
50+
export const CodeContainer = (
51+
props: DeepPartial<BoxProps>,
52+
{ slots }: SetupContext
53+
) => (
54+
<c-box p="3" pt="5" rounded="8px" my="8" bg="#011627" {...props}>
55+
{slots}
56+
</c-box>
57+
)
58+
59+
export default defineComponent({
60+
props: {
61+
code: String,
62+
language: String,
63+
live: { type: [Boolean, String], default: false },
64+
},
65+
setup(props, { slots }) {
66+
return () => {
67+
return (
68+
// live
69+
<c-box class="code-block" pos="relative">
70+
{props.live && <code-editor code={props.code} />}
71+
{!props.live && (
72+
<CodeContainer overflow="hidden">
73+
<code-highlight code={props.code} language={props.language} />
74+
<CopyButton top="4">COPY</CopyButton>
75+
</CodeContainer>
76+
)}
77+
</c-box>
78+
)
79+
}
80+
},
81+
})
82+
</script>
83+
<style>
84+
.VueLive-error {
85+
background: inherit !important;
86+
}
87+
.prism-editor__textarea,
88+
.prism-editor__editor {
89+
padding: 10px !important;
90+
font-size: 14px !important;
91+
overflow-x: auto !important;
92+
font-family: SF Mono, Menlo, monospace !important;
93+
}
94+
</style>
Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
<template>
2-
<chakra.pre>
3-
<code>
4-
<slot />
5-
</code>
6-
</chakra.pre>
2+
<VueLive :code="code" :components="scope" :layout="CodeEditorLayout" />
73
</template>
4+
5+
<script setup lang="ts">
6+
import { defineProps } from 'vue'
7+
import CodeEditorLayout from './CodeEditorLayout.vue'
8+
import 'vue-live/lib/vue-live.esm.css'
9+
// @ts-ignore
10+
import { VueLive } from 'vue-live'
11+
12+
import scope from './VueLiveScope'
13+
14+
// @ts-ignore
15+
// prettier-ignore
16+
const props = defineProps<{ code: string }>()
17+
</script>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<template>
2+
<c-box
3+
class="my-vuelive-preview"
4+
font-family="body"
5+
mt="5"
6+
p="3"
7+
:border-width="1"
8+
border-radius="12px"
9+
>
10+
<slot name="preview"></slot>
11+
</c-box>
12+
<c-box pos="relative">
13+
<code-container>
14+
<c-box
15+
font-size="14"
16+
overflow-x="auto"
17+
font-family="SF Mono, Menlo, monospace"
18+
>
19+
<slot name="editor"></slot>
20+
</c-box>
21+
<copy-button>copy</copy-button>
22+
<editable-notice></editable-notice>
23+
</code-container>
24+
</c-box>
25+
</template>
26+
<script setup lang="ts">
27+
import { defineProps } from 'vue'
28+
import { CopyButton, EditableNotice, CodeContainer } from './CodeBlock.vue'
29+
30+
// this props are required to surpress the warning (`Extraneous non-props attributes`...)
31+
// @ts-ignore
32+
// prettier-ignore
33+
const props = defineProps<{ code: string, language: string, prismLang: string, requires: any, dataScope:any, components:any }>()
34+
</script>
35+
<style></style>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<template>
2+
<prism-editor
3+
class="code-higlighter"
4+
v-model="code"
5+
:highlight="highlighter"
6+
readonly
7+
></prism-editor>
8+
</template>
9+
10+
<script setup lang="ts">
11+
import { computed, defineProps } from 'vue'
12+
13+
import { PrismEditor } from 'vue-prism-editor'
14+
import 'vue-prism-editor/dist/prismeditor.min.css'
15+
16+
// @ts-ignore
17+
import { highlight, languages } from 'prismjs/components/prism-core'
18+
import 'prismjs/components/prism-clike'
19+
import 'prismjs/components/prism-javascript'
20+
import 'prismjs/components/prism-bash'
21+
import 'prismjs/components/prism-jsx'
22+
23+
// @ts-ignore
24+
// prettier-ignore
25+
const props = defineProps<{ code: string, language: string }>()
26+
27+
const lang = computed(() => {
28+
if (props.language === 'vue') {
29+
return 'html'
30+
}
31+
return props.language
32+
})
33+
34+
const highlighter = (code: string) => {
35+
return highlight(code.trim(), languages?.[lang.value]) // languages.<insert language> to return html with markup
36+
}
37+
</script>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import * as Chakra from '@chakra-ui/vue-next'
2+
3+
const VueLiveScope = {
4+
...Chakra,
5+
}
6+
7+
export default VueLiveScope
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<template>
2+
<code-block :live="isLive" :code="code" :language="language"></code-block>
3+
</template>
4+
5+
<script lang="ts">
6+
import { computed, defineComponent } from 'vue'
7+
8+
import '../../css/night-owl.css'
9+
10+
export default defineComponent({
11+
props: {
12+
code: String,
13+
live: { type: [Boolean, String], default: false },
14+
language: String,
15+
},
16+
inheritAttrs: false,
17+
setup(props) {
18+
const isLive = computed(() => {
19+
if (props.live === 'false') return false
20+
const isTrue = props.live === true || props.live === 'true'
21+
return isTrue
22+
})
23+
return {
24+
isLive,
25+
}
26+
},
27+
})
28+
</script>

website/src/docs-theme/components/MdxComponents.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { h, renderSlot, SetupContext } from 'vue'
2+
import { trim } from 'lodash'
23
/**
34
* MDX Components
45
*
@@ -58,17 +59,28 @@ export const MdxComponents = {
5859
renderSlot(context.slots, 'default')
5960
),
6061
code: (props: { className: string }, context: SetupContext) => {
61-
// inlineCode work around
62-
const isInlineCode = !props.className?.includes('language-')
63-
if (isInlineCode) return h('MdxInlineCode', props, context.slots)
64-
return h('CodeEditor', props, context.slots)
62+
return h('MdxInlineCode', props, context.slots)
63+
},
64+
pre: (props: { live?: boolean }, context: any) => {
65+
// props comes from remark-mdx-code-meta automatically
66+
67+
// new lines `\n` doesnt work with custom components so we pass as a prop and render it with `v-html`
68+
const pre = context?.slots?.default?.()
69+
const defaultSlot = pre?.[0]?.children.default()[0] || ''
70+
const code = trim(defaultSlot)
71+
const realProps = pre?.[0]?.props
72+
73+
// get language from classname eg. language-bash => bash
74+
const [_, language] = realProps?.className?.split('-')
75+
76+
const comp = h(
77+
'MdxPre',
78+
{ ...props, ...realProps, language, code },
79+
// no need for slot
80+
{ default: '' }
81+
)
82+
return comp
6583
},
66-
pre: (props: any, context: SetupContext) =>
67-
h(
68-
'c-box',
69-
{ my: '2em', borderRadius: 'sm', ...props },
70-
renderSlot(context.slots, 'default')
71-
),
7284
kbd: 'CKbd',
7385
// todo: use <Cbr /> instead of <br reset />
7486
CBr: ({ reset, ...props }: { reset: Boolean }, context: SetupContext) => {

website/src/docs-theme/components/TableOfContents.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import { useColorModeValue } from '@chakra-ui/vue-next'
5959
import type { Heading } from '@/docs-theme/utils/get-headings'
6060
import { useToc } from '../hooks/useToc'
6161
import { useRoute } from 'vue-router'
62+
import { tryOnMounted } from '@vueuse/core'
6263
6364
// @ts-ignore
6465
const props = defineProps<{
@@ -67,7 +68,7 @@ const props = defineProps<{
6768
const { hash } = useRoute()
6869
const { activeTocId: activeId, scrollToHash } = useToc()
6970
70-
onMounted(() => {
71+
tryOnMounted(() => {
7172
scrollToHash(hash)
7273
})
7374
</script>

0 commit comments

Comments
 (0)