Skip to content

Commit 1cfa62d

Browse files
committed
feat(vue-playground,vuepress-plugin-sandbox): add theme config supports
1 parent eefd440 commit 1cfa62d

File tree

9 files changed

+154
-95
lines changed

9 files changed

+154
-95
lines changed

packages/vue-playground/src/playground/components/editor.vue

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const files = computed(() => Object.values(store.state.files))
3434
const activeFile = computed(() => store.state.activeFile)
3535
const themeName = computed(() => unref(theme)?.['--editor-theme-name'])
3636
37-
const {getEditor, onChange, isDark, toggleDark, disposeEditor} = useMonaco(editorEl, {
37+
const {getEditor, onChange, disposeEditor} = useMonaco(editorEl, {
3838
themeName,
3939
lifeCycle,
4040
files,
@@ -52,8 +52,6 @@ const handleChange = debounce(({newCode, activeFile}: {newCode: string; activeFi
5252
5353
defineExpose({
5454
getEditor,
55-
isDark,
56-
toggleDark,
5755
disposeEditor
5856
} as EditorExpose)
5957
Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import {MaybeRef} from '@vueuse/core'
2-
import {InjectionKey} from 'vue'
3-
import {Store} from '../core'
4-
import {PlaygroundTheme} from './utils/types-helper'
1+
import type {MaybeRef} from '@vueuse/core'
2+
import type {InjectionKey} from 'vue'
3+
import type {Store} from '../core'
4+
import type {PlaygroundTheme} from './utils/types-helper'
55

66
export const PLAYGROUND_COMPONENT_NAME = 'Playground' as const
77
export const FILE_BASE_URL = 'file:///root/' as const
@@ -12,3 +12,68 @@ export const STORE_INJECT_KEY = '__store__' as unknown as InjectionKey<Store>
1212
export const CLEAR_CONSOLE_INJECT_KEY = '__clear_console__' as unknown as InjectionKey<MaybeRef<boolean>>
1313
export const SHOW_IMPORT_MAP_INJECT_KEY = '__show_import_map__' as unknown as InjectionKey<MaybeRef<boolean>>
1414
export const THEME_INJECT_KEY = '__theme__' as unknown as InjectionKey<MaybeRef<PlaygroundTheme>>
15+
export const SHOW_DARK_MODE_INJECT_KEY = '__show_dark_mode__' as unknown as InjectionKey<MaybeRef<boolean>>
16+
17+
export const DEFAULT_THEME_COLOR = '#42b883'
18+
19+
export const GET_LIGHT_THEME = (theme?: PlaygroundTheme) => {
20+
const themeColor = theme?.['--theme-color'] || DEFAULT_THEME_COLOR
21+
return {
22+
'--editor-theme-name': 'vitesse-light',
23+
'--theme-color': themeColor,
24+
'--border-color': '#eaecef',
25+
'--bg-color': '#fff',
26+
'--toolbar-bg-color': '##eeeeee',
27+
'--toolbar-text-color': '#2c3e50',
28+
'--toolbar-icon-bg-color': '#fff',
29+
'--toolbar-icon-color': '#999',
30+
'--toolbar-icon-active-color': themeColor,
31+
'--preview-bg-color': '#fff',
32+
'--preview-text-color': '#2c3e50',
33+
'--file-manager-bg-color': '#fff',
34+
'--file-manager-text-color': '#94a3b8',
35+
'--file-manager-active-bg-color': '#fff',
36+
'--file-manager-active-text-color': themeColor,
37+
'--file-manager-right-float-bg': 'linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 25%)',
38+
'--message-warn-text-color': '#695f1b',
39+
'--message-warn-bg-color': '#f7f0cd',
40+
'--message-warn-border-color': '#695f1b',
41+
'--message-error-text-color': '#f00',
42+
'--message-error-bg-color': '#ffd7d7',
43+
'--message-error-border-color': '#f00',
44+
'--message-dismiss-text-color': '#fff',
45+
'--message-dismiss-bg-color': '#f00',
46+
...theme
47+
} as PlaygroundTheme
48+
}
49+
50+
export const GET_DARK_THEME = (theme?: PlaygroundTheme) => {
51+
const themeColor = theme?.['--theme-color'] || DEFAULT_THEME_COLOR
52+
return {
53+
'--editor-theme-name': 'vitesse-dark',
54+
'--theme-color': themeColor,
55+
'--border-color': '#3e4c5a',
56+
'--bg-color': '#22272e',
57+
'--toolbar-bg-color': '#262c34',
58+
'--toolbar-text-color': '#adbac7',
59+
'--toolbar-icon-bg-color': '#22272e',
60+
'--toolbar-icon-color': '#adbac7',
61+
'--toolbar-icon-active-color': themeColor,
62+
'--preview-bg-color': '#22272e',
63+
'--preview-text-color': '#fff',
64+
'--file-manager-bg-color': '#22272e',
65+
'--file-manager-text-color': '#94a3b8',
66+
'--file-manager-active-bg-color': '#22272e',
67+
'--file-manager-active-text-color': themeColor,
68+
'--file-manager-right-float-bg': 'linear-gradient(90deg, rgba(34, 39, 46, 0) 0%, rgba(34, 39, 46, 1) 25%)',
69+
'--message-warn-text-color': '#695f1b',
70+
'--message-warn-bg-color': '#f7f0cd',
71+
'--message-warn-border-color': '#695f1b',
72+
'--message-error-text-color': '#f00',
73+
'--message-error-bg-color': '#ffd7d7',
74+
'--message-error-border-color': '#f00',
75+
'--message-dismiss-text-color': '#fff',
76+
'--message-dismiss-bg-color': '#f00',
77+
...theme
78+
} as PlaygroundTheme
79+
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import {useDark, useToggle} from '@vueuse/core'
22

3-
export const isDark = useDark()
3+
export const isDark = useDark({
4+
// do not add .dark class name to html
5+
// eslint-disable-next-line @typescript-eslint/no-empty-function
6+
onChanged: () => {}
7+
})
48
export const toggleDark = useToggle(isDark)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type {PropType, ExtractPropTypes} from 'vue'
2+
import type {File} from '../core'
3+
import type {PlaygroundLifeCycle, PlaygroundThemes} from './utils/types-helper'
4+
5+
export const playgroundProps = () =>
6+
({
7+
files: {
8+
type: Array as PropType<File[]>,
9+
default: () => []
10+
},
11+
lifeCycle: {
12+
type: Object as PropType<PlaygroundLifeCycle>,
13+
default: () => ({})
14+
},
15+
themes: {
16+
type: Object as PropType<PlaygroundThemes>,
17+
default: () => ({})
18+
}
19+
} as const)
20+
21+
export type PlaygroundProps = Readonly<ExtractPropTypes<ReturnType<typeof playgroundProps>>>

packages/vue-playground/src/playground/playground.vue

Lines changed: 40 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,35 @@ export default defineComponent({
44
})
55
</script>
66
<script lang="ts" setup>
7-
import {computed, CSSProperties, defineComponent, onMounted, PropType, provide, ref} from 'vue'
7+
import {computed, CSSProperties, defineComponent, inject, onMounted, provide, ref, unref, watch} from 'vue'
88
import {ReplStore} from '../core'
99
import Toolbar from './components/toolbar.vue'
1010
import Editor from './components/editor.vue'
1111
import Preview from './components/preview.vue'
1212
import Portal from './components/portal'
13-
import {PLAYGROUND_COMPONENT_NAME, SHOW_IMPORT_MAP_INJECT_KEY, STORE_INJECT_KEY, THEME_INJECT_KEY} from './constants'
1413
import {
15-
EditorExpose,
16-
LayoutDirection,
17-
PlaygroundExpose,
18-
PlaygroundOptions,
19-
PlaygroundTheme,
20-
PreviewExpose
21-
} from './utils/types-helper'
22-
import {isDark} from './hooks'
14+
GET_DARK_THEME,
15+
GET_LIGHT_THEME,
16+
PLAYGROUND_COMPONENT_NAME,
17+
SHOW_DARK_MODE_INJECT_KEY,
18+
SHOW_IMPORT_MAP_INJECT_KEY,
19+
STORE_INJECT_KEY,
20+
THEME_INJECT_KEY
21+
} from './constants'
22+
import {EditorExpose, LayoutDirection, PlaygroundExpose, PlaygroundTheme, PreviewExpose} from './utils/types-helper'
23+
import {isDark, toggleDark} from './hooks'
24+
import {playgroundProps} from './playground.type'
2325
24-
const props = defineProps({
25-
options: {
26-
type: Object as PropType<PlaygroundOptions>,
27-
default: () => ({})
28-
}
29-
})
26+
const props = defineProps(playgroundProps())
3027
3128
const previewRef = ref<PreviewExpose>()
3229
const editorRef = ref<EditorExpose>()
3330
3431
const store = new ReplStore({
35-
initFiles: props.options.files
32+
initFiles: props.files
3633
})
3734
35+
const showDarkMode = inject(SHOW_DARK_MODE_INJECT_KEY, undefined)
3836
const showCode = ref(true)
3937
const fullScreen = ref(false)
4038
const layoutDirection = ref<LayoutDirection>('EditorBottomPreviewTop')
@@ -61,63 +59,30 @@ const contentStyle = computed<CSSProperties>(() => {
6159
})
6260
6361
const theme = computed<CSSProperties>(() => {
64-
const themeColor = '#42b883'
65-
const lightTheme: PlaygroundTheme = {
66-
'--editor-theme-name': 'vitesse-light',
67-
'--theme-color': themeColor,
68-
'--border-color': '#eaecef',
69-
'--bg-color': '#fff',
70-
'--toolbar-bg-color': '##eeeeee',
71-
'--toolbar-text-color': '#2c3e50',
72-
'--toolbar-icon-bg-color': '#fff',
73-
'--toolbar-icon-color': '#999',
74-
'--toolbar-icon-active-color': themeColor,
75-
'--preview-bg-color': '#fff',
76-
'--preview-text-color': '#2c3e50',
77-
'--file-manager-bg-color': '#fff',
78-
'--file-manager-text-color': '#94a3b8',
79-
'--file-manager-active-bg-color': '#fff',
80-
'--file-manager-active-text-color': themeColor,
81-
'--file-manager-right-float-bg': 'linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 25%)',
82-
'--message-warn-text-color': '#695f1b',
83-
'--message-warn-bg-color': '#f7f0cd',
84-
'--message-warn-border-color': '#695f1b',
85-
'--message-error-text-color': '#f00',
86-
'--message-error-bg-color': '#ffd7d7',
87-
'--message-error-border-color': '#f00',
88-
'--message-dismiss-text-color': '#fff',
89-
'--message-dismiss-bg-color': '#f00'
90-
}
62+
const lightTheme: PlaygroundTheme = GET_LIGHT_THEME(props?.themes?.light)
63+
const darkTheme: PlaygroundTheme = GET_DARK_THEME(props?.themes?.dark)
64+
return isDark.value ? darkTheme : lightTheme
65+
})
9166
92-
const darkTheme: PlaygroundTheme = {
93-
'--editor-theme-name': 'vitesse-dark',
94-
'--theme-color': themeColor,
95-
'--border-color': '#3e4c5a',
96-
'--bg-color': '#22272e',
97-
'--toolbar-bg-color': '#262c34',
98-
'--toolbar-text-color': '#adbac7',
99-
'--toolbar-icon-bg-color': '#22272e',
100-
'--toolbar-icon-color': '#adbac7',
101-
'--toolbar-icon-active-color': themeColor,
102-
'--preview-bg-color': '#22272e',
103-
'--preview-text-color': '#fff',
104-
'--file-manager-bg-color': '#22272e',
105-
'--file-manager-text-color': '#94a3b8',
106-
'--file-manager-active-bg-color': '#22272e',
107-
'--file-manager-active-text-color': themeColor,
108-
'--file-manager-right-float-bg': 'linear-gradient(90deg, rgba(34, 39, 46, 0) 0%, rgba(34, 39, 46, 1) 25%)',
109-
'--message-warn-text-color': '#695f1b',
110-
'--message-warn-bg-color': '#f7f0cd',
111-
'--message-warn-border-color': '#695f1b',
112-
'--message-error-text-color': '#f00',
113-
'--message-error-bg-color': '#ffd7d7',
114-
'--message-error-border-color': '#f00',
115-
'--message-dismiss-text-color': '#fff',
116-
'--message-dismiss-bg-color': '#f00'
67+
watch(
68+
isDark,
69+
val => {
70+
props?.lifeCycle?.onDarkModeChange?.(val)
71+
},
72+
{
73+
immediate: true
11774
}
75+
)
11876
119-
return isDark.value ? darkTheme : lightTheme
120-
})
77+
watch(
78+
() => unref(showDarkMode),
79+
val => {
80+
if (val !== null && val !== undefined) toggleDark(val)
81+
},
82+
{
83+
immediate: true
84+
}
85+
)
12186
12287
provide(STORE_INJECT_KEY, store)
12388
provide(SHOW_IMPORT_MAP_INJECT_KEY, true)
@@ -129,7 +94,9 @@ onMounted(() => {
12994
defineExpose({
13095
store,
13196
preview: previewRef,
132-
editor: editorRef
97+
editor: editorRef,
98+
isDark,
99+
toggleDark
133100
} as PlaygroundExpose)
134101
</script>
135102
<template>
@@ -157,7 +124,7 @@ defineExpose({
157124
<Editor
158125
v-show="showCode"
159126
ref="EditorRef"
160-
:life-cycle="options.lifeCycle"
127+
:life-cycle="lifeCycle"
161128
:style="{
162129
borderTop: layoutDirection === 'EditorBottomPreviewTop' ? '1px solid var(--border-color)' : 'none',
163130
borderRight: layoutDirection === 'EditorLeftPreviewRight' ? '1px solid var(--border-color)' : 'none'

packages/vue-playground/src/playground/utils/types-helper.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
22
import type {CSSProperties, Ref} from 'vue'
33
import type {File, ReplStore} from '../../core'
4+
import type {PlaygroundProps} from '../playground.type'
45

56
export type {editor as MonacoEditor} from 'monaco-editor'
67

@@ -31,10 +32,7 @@ export interface TsLib {
3132
filePath?: string
3233
}
3334

34-
export interface PlaygroundOptions {
35-
files: File[]
36-
lifeCycle?: PlaygroundLifeCycle
37-
}
35+
export type PlaygroundOptions = Partial<PlaygroundProps>
3836

3937
export interface PlaygroundLifeCycle {
4038
beforeLoadMonaco?: () => MaybePromise<void>
@@ -49,13 +47,12 @@ export interface PlaygroundLifeCycle {
4947
onCodeChange?: (event: {activeFile: File; newCode: string}) => MaybePromise<void>
5048
beforeDestroyEditor?: (monaco: Monaco, editor: IStandaloneCodeEditor) => MaybePromise<void>
5149
afterDestroyEditor?: (monaco: Monaco) => MaybePromise<void>
50+
onDarkModeChange?: (darkMode: boolean) => MaybePromise<void>
5251
}
5352

5453
export interface EditorExpose {
5554
getEditor: () => IStandaloneCodeEditor
56-
isDark: Ref<boolean>
5755
disposeEditor: Ref<() => void>
58-
toggleDark: (preIsDark?: boolean | undefined) => void
5956
}
6057

6158
export interface PreviewExpose {
@@ -67,6 +64,8 @@ export interface PlaygroundExpose {
6764
store: ReplStore
6865
preview: Ref<PreviewExpose>
6966
editor: Ref<EditorExpose>
67+
isDark: Ref<boolean>
68+
toggleDark: (preIsDark?: boolean | undefined) => void
7069
}
7170

7271
export interface PlaygroundTheme extends CSSProperties {
@@ -96,3 +95,8 @@ export interface PlaygroundTheme extends CSSProperties {
9695
'--message-dismiss-text-color'?: string
9796
'--message-dismiss-bg-color'?: string
9897
}
98+
99+
export interface PlaygroundThemes {
100+
light?: PlaygroundTheme
101+
dark?: PlaygroundTheme
102+
}

packages/vuepress-plugin-sandbox/src/enhanceAppFile.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,11 @@ export default defineClientAppEnhance(async ({app}) => {
2626
app.component(SANDBOX_COMPONENT_NAME, defaultProps => {
2727
if (!Playground) return null
2828
const ClientOnly = resolveComponent('ClientOnly')
29-
const playgroundOptions = self?.loadSandbox?.(defaultProps?.options) || defaultProps?.options
29+
const playgroundOptions = self?.loadSandbox?.(defaultProps) || defaultProps
3030

3131
return h(ClientOnly, {}, () =>
3232
h(Playground as ConcreteComponent, {
33-
...defaultProps,
34-
options: playgroundOptions
33+
...playgroundOptions
3534
})
3635
)
3736
})

packages/vuepress-plugin-sandbox/src/node/plugin.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import markdownItContainer from 'markdown-it-container'
66
import type {MarkdownEnv} from '@vuepress/markdown'
77
import type * as Token from 'markdown-it/lib/token'
88
import type * as Renderer from 'markdown-it/lib/renderer'
9-
import {File, PlaygroundLifeCycle} from 'vue-playground'
9+
import {File, PlaygroundLifeCycle, PlaygroundOptions, PlaygroundThemes} from 'vue-playground'
1010
import * as base64 from 'js-base64'
1111

1212
const pathResolve = (..._path: string[]) => path.resolve(__dirname, ..._path)
@@ -23,12 +23,13 @@ export interface DemoPluginOptions {
2323
demoCodeMark?: string
2424
defaultDescription?: string
2525
lifeCycle?: PlaygroundLifeCycle
26+
themes?: PlaygroundThemes
2627
}
2728

2829
export type RenderPlaceFunction = (description: string, codeBlockTokens?: Token[]) => string
2930

3031
export const sandboxPlugin = (options: DemoPluginOptions = {}): Plugin => {
31-
const {demoCodeMark = 'demo', defaultDescription = '', lifeCycle} = options
32+
const {demoCodeMark = 'demo', defaultDescription = '', lifeCycle, themes} = options
3233

3334
const START_TYPE = `container_${demoCodeMark}_open`
3435
const END_TYPE = `container_${demoCodeMark}_close`
@@ -50,12 +51,12 @@ export const sandboxPlugin = (options: DemoPluginOptions = {}): Plugin => {
5051

5152
console.log('files....', files)
5253

53-
const options = {files}
54+
const options: PlaygroundOptions = {files, themes}
5455
const optionsBase64 = base64.encode(JSON.stringify(options))
5556

5657
return `<div class="demo-container ${demoCodeMark}">${
5758
des ? `<p class="demo-container-title">${des}</p>` : ''
58-
}<playground :options="JSON.parse(base64.decode('${optionsBase64}'))">\n`
59+
}<playground v-bind="JSON.parse(base64.decode('${optionsBase64}'))">\n`
5960
}
6061

6162
const renderAfter: RenderPlaceFunction = () => '</playground></div>\n'

0 commit comments

Comments
 (0)