@varlet/cli 1.23.11 → 1.23.12-alpha.25
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/package.json +2 -2
- package/site/components/code-example/CodeExample.vue +107 -0
- package/site/components/code-example/codeExample.less +23 -0
- package/site/components/code-example/index.ts +10 -0
- package/site/components/icon/icon.less +1 -0
- package/site/components/loading/props.ts +2 -2
- package/site/components/snackbar/Snackbar.vue +38 -0
- package/site/components/snackbar/core.vue +117 -0
- package/site/components/snackbar/index.tsx +270 -0
- package/site/components/snackbar/props.ts +94 -0
- package/site/components/snackbar/snackbar.less +135 -0
- package/site/components/utils/elements.ts +9 -0
- package/site/components/utils/shared.ts +3 -0
- package/site/mobile/App.vue +6 -6
- package/site/module.d.ts +4 -0
- package/site/pc/App.vue +8 -10
- package/site/pc/components/AppHeader.vue +8 -9
- package/site/pc/components/AppSidebar.vue +5 -5
- package/site/pc/main.ts +8 -2
- package/site/tsconfig.json +1 -6
- package/site/useProgress.ts +6 -2
- package/site/utils.ts +4 -3
- package/tsconfig.json +0 -2
- package/varlet.default.config.js +13 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@varlet/cli",
|
|
3
|
-
"version": "1.23.
|
|
3
|
+
"version": "1.23.12-alpha.25+4a1d7746",
|
|
4
4
|
"description": "cli of varlet",
|
|
5
5
|
"bin": {
|
|
6
6
|
"varlet-cli": "./lib/index.js"
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"dev": "tsc --watch",
|
|
32
32
|
"build": "tsc"
|
|
33
33
|
},
|
|
34
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "4a1d774686bd66a53827bfb64a8495be97f9cf33",
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@babel/core": "^7.14.8",
|
|
37
37
|
"@babel/preset-env": "^7.14.8",
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="var-site-code-example">
|
|
3
|
+
<div class="var-site-code-example__toolbar">
|
|
4
|
+
<var-site-button text round @click="toggle" v-if="fold">
|
|
5
|
+
<var-site-icon name="xml" size="18" />
|
|
6
|
+
</var-site-button>
|
|
7
|
+
|
|
8
|
+
<var-site-button
|
|
9
|
+
:id="`clip-trigger-${cid}`"
|
|
10
|
+
:data-clipboard-target="`#clip-target-${cid}`"
|
|
11
|
+
text
|
|
12
|
+
round
|
|
13
|
+
v-if="clipboard"
|
|
14
|
+
>
|
|
15
|
+
<var-site-icon name="content-copy" size="18" />
|
|
16
|
+
</var-site-button>
|
|
17
|
+
</div>
|
|
18
|
+
<div
|
|
19
|
+
:id="`clip-target-${cid}`"
|
|
20
|
+
class="var-site-code-example__code"
|
|
21
|
+
ref="code"
|
|
22
|
+
:style="{
|
|
23
|
+
height: height >= 0 ? `${height}px` : undefined,
|
|
24
|
+
}"
|
|
25
|
+
>
|
|
26
|
+
<slot/>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script lang="ts">
|
|
32
|
+
import Icon from '../icon'
|
|
33
|
+
import Button from '../button'
|
|
34
|
+
import Snackbar from '../snackbar'
|
|
35
|
+
import Clipboard from 'clipboard'
|
|
36
|
+
import config from '@config'
|
|
37
|
+
import { defineComponent, nextTick, ref, onMounted } from 'vue'
|
|
38
|
+
import { doubleRaf } from '../utils/elements'
|
|
39
|
+
import { get } from 'lodash-es'
|
|
40
|
+
import { getPCLocationInfo } from '../../utils'
|
|
41
|
+
import type { Ref } from 'vue'
|
|
42
|
+
|
|
43
|
+
let clipId = 0
|
|
44
|
+
const offset = 10
|
|
45
|
+
|
|
46
|
+
export default defineComponent({
|
|
47
|
+
name: 'VarSiteCodeExample',
|
|
48
|
+
components: {
|
|
49
|
+
[Button.name]: Button,
|
|
50
|
+
[Icon.name]: Icon
|
|
51
|
+
},
|
|
52
|
+
setup() {
|
|
53
|
+
const code: Ref<HTMLElement | null> = ref(null)
|
|
54
|
+
const cid: Ref<number> = ref(clipId++)
|
|
55
|
+
const fold: Ref = ref(get(config, 'pc.fold'))
|
|
56
|
+
const clipboard: Ref = ref(get(config, 'pc.clipboard', {}))
|
|
57
|
+
const height: Ref<number> = ref(fold.value?.defaultFold ? fold.value?.foldHeight : -1)
|
|
58
|
+
|
|
59
|
+
const toggle = async () => {
|
|
60
|
+
const foldHeight = fold.value.foldHeight
|
|
61
|
+
|
|
62
|
+
if (height.value === foldHeight) {
|
|
63
|
+
height.value = -1
|
|
64
|
+
await nextTick()
|
|
65
|
+
const { offsetHeight } = code.value as HTMLElement
|
|
66
|
+
|
|
67
|
+
height.value = foldHeight
|
|
68
|
+
await doubleRaf()
|
|
69
|
+
|
|
70
|
+
if (offsetHeight - foldHeight < offset) {
|
|
71
|
+
Snackbar(get(config, `pc.fold.message.${getPCLocationInfo().language}`))
|
|
72
|
+
height.value = foldHeight
|
|
73
|
+
} else {
|
|
74
|
+
height.value = offsetHeight
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
const { offsetHeight } = code.value as HTMLElement
|
|
78
|
+
|
|
79
|
+
height.value = offsetHeight
|
|
80
|
+
await doubleRaf()
|
|
81
|
+
height.value = foldHeight
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
onMounted(() => {
|
|
86
|
+
const trigger = new Clipboard(`#clip-trigger-${cid.value}`)
|
|
87
|
+
|
|
88
|
+
trigger.on('success', () => {
|
|
89
|
+
Snackbar.success(clipboard.value[getPCLocationInfo().language])
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
code,
|
|
95
|
+
height,
|
|
96
|
+
cid,
|
|
97
|
+
fold,
|
|
98
|
+
clipboard,
|
|
99
|
+
toggle
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
<style lang="less">
|
|
106
|
+
@import "./codeExample";
|
|
107
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.var-site-code-example {
|
|
2
|
+
margin-top: 12px;
|
|
3
|
+
position: relative;
|
|
4
|
+
|
|
5
|
+
&__toolbar {
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: center;
|
|
8
|
+
position: absolute;
|
|
9
|
+
z-index: 1;
|
|
10
|
+
right: 10px;
|
|
11
|
+
top: 20px;
|
|
12
|
+
|
|
13
|
+
button {
|
|
14
|
+
color: white !important;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
&__code {
|
|
19
|
+
transition: all .25s;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
border-radius: 4px;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { PropType } from 'vue'
|
|
2
2
|
|
|
3
|
-
type LoadingType = 'circle' | 'wave' | 'cube' | 'rect' | 'disappear'
|
|
3
|
+
export type LoadingType = 'circle' | 'wave' | 'cube' | 'rect' | 'disappear'
|
|
4
4
|
|
|
5
|
-
type LoadingSize = 'normal' | 'mini' | 'small' | 'large'
|
|
5
|
+
export type LoadingSize = 'normal' | 'mini' | 'small' | 'large'
|
|
6
6
|
|
|
7
7
|
export function typeValidator(type: string): boolean {
|
|
8
8
|
return ['circle', 'wave', 'cube', 'rect', 'disappear'].includes(type)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<teleport :to="teleport" :disabled="disabled">
|
|
3
|
+
<transition name="var-site-snackbar-fade" @after-enter="onOpened" @after-leave="onClosed">
|
|
4
|
+
<var-site-snackbar-core v-bind="$props" class="var-site-snackbar-transition">
|
|
5
|
+
<slot>{{ content }}</slot>
|
|
6
|
+
<template #action>
|
|
7
|
+
<slot name="action" />
|
|
8
|
+
</template>
|
|
9
|
+
</var-site-snackbar-core>
|
|
10
|
+
</transition>
|
|
11
|
+
</teleport>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script>
|
|
15
|
+
import VarSiteSnackbarCore from './core'
|
|
16
|
+
import { defineComponent } from 'vue'
|
|
17
|
+
import { useTeleport } from '../utils/components'
|
|
18
|
+
import { props } from './props'
|
|
19
|
+
|
|
20
|
+
export default defineComponent({
|
|
21
|
+
name: 'VarSiteSnackbar',
|
|
22
|
+
components: {
|
|
23
|
+
VarSiteSnackbarCore,
|
|
24
|
+
},
|
|
25
|
+
props,
|
|
26
|
+
setup() {
|
|
27
|
+
const { disabled } = useTeleport()
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
disabled,
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<style lang="less">
|
|
37
|
+
@import '../styles/common';
|
|
38
|
+
</style>
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="var-site-snackbar" :style="{ pointerEvents: isForbidClick ? 'auto' : 'none', zIndex }" v-show="show">
|
|
3
|
+
<div :class="snackbarClass" :style="{ zIndex }">
|
|
4
|
+
<div class="var-site-snackbar__content" :class="[contentClass]">
|
|
5
|
+
<slot>{{ content }}</slot>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="var-site-snackbar__action">
|
|
8
|
+
<var-site-icon v-if="iconName" :name="iconName" />
|
|
9
|
+
<var-site-loading v-if="type === 'loading'" :type="loadingType" :size="loadingSize" />
|
|
10
|
+
<slot name="action" />
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script lang="ts">
|
|
17
|
+
import { defineComponent, watch, ref, onMounted, computed } from 'vue'
|
|
18
|
+
import VarSiteLoading from '../loading'
|
|
19
|
+
import VarSiteIcon from '../icon'
|
|
20
|
+
import { useZIndex } from '../context/zIndex'
|
|
21
|
+
import { props } from './props'
|
|
22
|
+
import { useLock } from '../context/lock'
|
|
23
|
+
import { SNACKBAR_TYPE } from './index'
|
|
24
|
+
import type { Ref, ComputedRef } from 'vue'
|
|
25
|
+
import type { SnackbarType } from './index'
|
|
26
|
+
|
|
27
|
+
const ICON_TYPE_DICT: Record<SnackbarType, string> = {
|
|
28
|
+
success: 'checkbox-marked-circle',
|
|
29
|
+
warning: 'warning',
|
|
30
|
+
info: 'information',
|
|
31
|
+
error: 'error',
|
|
32
|
+
loading: '',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default defineComponent({
|
|
36
|
+
name: 'VarSiteSnackbarCore',
|
|
37
|
+
components: {
|
|
38
|
+
VarSiteLoading,
|
|
39
|
+
VarSiteIcon,
|
|
40
|
+
},
|
|
41
|
+
props,
|
|
42
|
+
setup(props) {
|
|
43
|
+
const timer: Ref = ref(null)
|
|
44
|
+
const { zIndex } = useZIndex(() => props.show, 1)
|
|
45
|
+
|
|
46
|
+
useLock(props, 'show', 'lockScroll')
|
|
47
|
+
|
|
48
|
+
const snackbarClass: ComputedRef<string> = computed(() => {
|
|
49
|
+
const { position, vertical, type } = props
|
|
50
|
+
|
|
51
|
+
const baseClass = `var-site-snackbar__wrapper var-site-snackbar__wrapper-${position} var-site-elevation--4`
|
|
52
|
+
const verticalClass = vertical ? ' var-site-snackbar__vertical' : ''
|
|
53
|
+
const typeClass = type && SNACKBAR_TYPE.includes(type) ? ` var-site-snackbar__wrapper-${type}` : ''
|
|
54
|
+
|
|
55
|
+
return `${baseClass}${verticalClass}${typeClass}`
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const isForbidClick: ComputedRef<boolean> = computed(() => props.type === 'loading' || props.forbidClick)
|
|
59
|
+
|
|
60
|
+
const iconName: ComputedRef<string> = computed(() => {
|
|
61
|
+
if (!props.type) return ''
|
|
62
|
+
|
|
63
|
+
return ICON_TYPE_DICT[props.type]
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const updateAfterDuration = () => {
|
|
67
|
+
timer.value = setTimeout(() => {
|
|
68
|
+
props.type !== 'loading' && props['onUpdate:show']?.(false)
|
|
69
|
+
}, props.duration)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
watch(
|
|
73
|
+
() => props.show,
|
|
74
|
+
(show) => {
|
|
75
|
+
if (show) {
|
|
76
|
+
props.onOpen?.()
|
|
77
|
+
updateAfterDuration()
|
|
78
|
+
} else if (show === false) {
|
|
79
|
+
clearTimeout(timer.value)
|
|
80
|
+
props.onClose?.()
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
watch(
|
|
86
|
+
() => props._update,
|
|
87
|
+
() => {
|
|
88
|
+
clearTimeout(timer.value)
|
|
89
|
+
updateAfterDuration()
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
onMounted(() => {
|
|
94
|
+
if (props.show) {
|
|
95
|
+
props.onOpen?.()
|
|
96
|
+
updateAfterDuration()
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
zIndex,
|
|
102
|
+
snackbarClass,
|
|
103
|
+
iconName,
|
|
104
|
+
isForbidClick,
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<style lang="less">
|
|
111
|
+
@import '../styles/common';
|
|
112
|
+
@import '../styles/elevation';
|
|
113
|
+
@import '../loading/loading';
|
|
114
|
+
@import '../button/button';
|
|
115
|
+
@import '../icon/icon';
|
|
116
|
+
@import './snackbar';
|
|
117
|
+
</style>
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import VarSiteSnackbarCore from './core.vue'
|
|
2
|
+
import VarSiteSnackbar from './Snackbar.vue'
|
|
3
|
+
import context from '../context'
|
|
4
|
+
import { reactive, TransitionGroup } from 'vue'
|
|
5
|
+
import { mountInstance } from '../utils/components'
|
|
6
|
+
import { isPlainObject, toNumber } from '../utils/shared'
|
|
7
|
+
import type { LoadingType, LoadingSize } from '../loading/props'
|
|
8
|
+
import type { App, Component, TeleportProps } from 'vue'
|
|
9
|
+
|
|
10
|
+
export type SnackbarType = 'success' | 'warning' | 'info' | 'error' | 'loading'
|
|
11
|
+
|
|
12
|
+
export const SNACKBAR_TYPE: Array<SnackbarType> = ['loading', 'success', 'warning', 'info', 'error']
|
|
13
|
+
|
|
14
|
+
interface SnackbarHandel {
|
|
15
|
+
clear: () => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface SnackbarOptions {
|
|
19
|
+
type?: SnackbarType
|
|
20
|
+
content?: string
|
|
21
|
+
position?: 'top' | 'center' | 'bottom'
|
|
22
|
+
loadingType?: LoadingType
|
|
23
|
+
loadingSize?: LoadingSize
|
|
24
|
+
lockScroll?: boolean
|
|
25
|
+
contentClass?: string
|
|
26
|
+
duration?: number
|
|
27
|
+
vertical?: boolean
|
|
28
|
+
show?: boolean
|
|
29
|
+
forbidClick?: boolean
|
|
30
|
+
onOpen?: () => void
|
|
31
|
+
onClose?: () => void
|
|
32
|
+
onOpened?: () => void
|
|
33
|
+
onClosed?: () => void
|
|
34
|
+
// internal
|
|
35
|
+
teleport?: TeleportProps['to']
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface UniqSnackbarOptions {
|
|
39
|
+
id: number
|
|
40
|
+
reactiveSnackOptions: SnackbarOptions
|
|
41
|
+
_update?: string
|
|
42
|
+
animationEnd?: boolean
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface Snackbar {
|
|
46
|
+
(options: SnackbarOptions | string): SnackbarHandel
|
|
47
|
+
|
|
48
|
+
install(app: App): void
|
|
49
|
+
|
|
50
|
+
allowMultiple(bool: boolean): void
|
|
51
|
+
|
|
52
|
+
success(options: SnackbarOptions | string): SnackbarHandel
|
|
53
|
+
|
|
54
|
+
warning(options: SnackbarOptions | string): SnackbarHandel
|
|
55
|
+
|
|
56
|
+
info(options: SnackbarOptions | string): SnackbarHandel
|
|
57
|
+
|
|
58
|
+
error(options: SnackbarOptions | string): SnackbarHandel
|
|
59
|
+
|
|
60
|
+
loading(options: SnackbarOptions | string): SnackbarHandel
|
|
61
|
+
|
|
62
|
+
clear(): void
|
|
63
|
+
|
|
64
|
+
Component: Component
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let sid = 0
|
|
68
|
+
let isMount = false
|
|
69
|
+
let unmount: () => void
|
|
70
|
+
let isAllowMultiple = false
|
|
71
|
+
let uniqSnackbarOptions: Array<UniqSnackbarOptions> = reactive<UniqSnackbarOptions[]>([])
|
|
72
|
+
|
|
73
|
+
const defaultOption: Partial<Record<keyof SnackbarOptions, any>> = {
|
|
74
|
+
type: undefined,
|
|
75
|
+
content: '',
|
|
76
|
+
position: 'top',
|
|
77
|
+
duration: 3000,
|
|
78
|
+
vertical: false,
|
|
79
|
+
contentClass: undefined,
|
|
80
|
+
loadingType: 'circle',
|
|
81
|
+
loadingSize: 'normal',
|
|
82
|
+
lockScroll: false,
|
|
83
|
+
teleport: 'body',
|
|
84
|
+
forbidClick: false,
|
|
85
|
+
onOpen: () => {},
|
|
86
|
+
onOpened: () => {},
|
|
87
|
+
onClose: () => {},
|
|
88
|
+
onClosed: () => {},
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const transitionGroupProps: any = {
|
|
92
|
+
name: 'var-site-snackbar-fade',
|
|
93
|
+
tag: 'div',
|
|
94
|
+
class: 'var-site-transition-group',
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const TransitionGroupHost = {
|
|
98
|
+
setup: function() {
|
|
99
|
+
return () => {
|
|
100
|
+
const snackbarList = uniqSnackbarOptions.map(({ id, reactiveSnackOptions, _update }: UniqSnackbarOptions) => {
|
|
101
|
+
const transitionGroupEl = document.querySelector('.var-site-transition-group')
|
|
102
|
+
if (reactiveSnackOptions.forbidClick || reactiveSnackOptions.type === 'loading') {
|
|
103
|
+
;(transitionGroupEl as HTMLElement).classList.add('var-site-pointer-auto')
|
|
104
|
+
} else {
|
|
105
|
+
;(transitionGroupEl as HTMLElement).classList.remove('var-site-pointer-auto')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (isAllowMultiple) reactiveSnackOptions.position = 'top'
|
|
109
|
+
|
|
110
|
+
const position = isAllowMultiple ? 'relative' : 'absolute' // avoid stylelint value-keyword-case error
|
|
111
|
+
|
|
112
|
+
const style = {
|
|
113
|
+
position,
|
|
114
|
+
...getTop(reactiveSnackOptions.position)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<VarSiteSnackbarCore
|
|
119
|
+
{...reactiveSnackOptions}
|
|
120
|
+
key={id}
|
|
121
|
+
style={style}
|
|
122
|
+
data-id={id}
|
|
123
|
+
_update={_update}
|
|
124
|
+
v-model={[reactiveSnackOptions.show, 'show']}
|
|
125
|
+
/>
|
|
126
|
+
)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
const zindex = context.zIndex // avoid stylelint value-keyword-case error
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<TransitionGroup
|
|
133
|
+
{...transitionGroupProps}
|
|
134
|
+
style={{ zIndex: zindex }}
|
|
135
|
+
onAfterEnter={opened}
|
|
136
|
+
onAfterLeave={removeUniqOption}
|
|
137
|
+
>
|
|
138
|
+
{snackbarList}
|
|
139
|
+
</TransitionGroup>
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const Snackbar: Snackbar = function (options: SnackbarOptions | string): SnackbarHandel {
|
|
146
|
+
const snackOptions: SnackbarOptions = isPlainObject(options) ? options : { content: options }
|
|
147
|
+
const reactiveSnackOptions: SnackbarOptions = reactive<SnackbarOptions>({
|
|
148
|
+
...defaultOption,
|
|
149
|
+
...snackOptions,
|
|
150
|
+
})
|
|
151
|
+
reactiveSnackOptions.show = true
|
|
152
|
+
|
|
153
|
+
if (!isMount) {
|
|
154
|
+
isMount = true
|
|
155
|
+
unmount = mountInstance(TransitionGroupHost).unmountInstance
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const { length } = uniqSnackbarOptions
|
|
159
|
+
const uniqSnackbarOptionItem: UniqSnackbarOptions = {
|
|
160
|
+
id: sid++,
|
|
161
|
+
reactiveSnackOptions,
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (length === 0 || isAllowMultiple) {
|
|
165
|
+
addUniqOption(uniqSnackbarOptionItem)
|
|
166
|
+
} else {
|
|
167
|
+
const _update = `update-${sid}`
|
|
168
|
+
updateUniqOption(reactiveSnackOptions, _update)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
clear() {
|
|
173
|
+
if (!isAllowMultiple && uniqSnackbarOptions.length) {
|
|
174
|
+
uniqSnackbarOptions[0].reactiveSnackOptions.show = false
|
|
175
|
+
} else {
|
|
176
|
+
reactiveSnackOptions.show = false
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
} as Snackbar
|
|
181
|
+
|
|
182
|
+
SNACKBAR_TYPE.forEach((type) => {
|
|
183
|
+
Snackbar[type] = (options: SnackbarOptions | string): SnackbarHandel => {
|
|
184
|
+
if (isPlainObject(options)) {
|
|
185
|
+
options.type = type
|
|
186
|
+
} else {
|
|
187
|
+
options = {
|
|
188
|
+
content: options,
|
|
189
|
+
type,
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return Snackbar(options)
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
Snackbar.install = function (app: App) {
|
|
197
|
+
app.component(VarSiteSnackbar.name, VarSiteSnackbar)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
Snackbar.allowMultiple = function (bool = false) {
|
|
201
|
+
if (bool !== isAllowMultiple) {
|
|
202
|
+
uniqSnackbarOptions.forEach((option: UniqSnackbarOptions) => {
|
|
203
|
+
option.reactiveSnackOptions.show = false
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
isAllowMultiple = bool
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
Snackbar.clear = function () {
|
|
211
|
+
uniqSnackbarOptions.forEach((option: UniqSnackbarOptions) => {
|
|
212
|
+
option.reactiveSnackOptions.show = false
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
Snackbar.Component = VarSiteSnackbar
|
|
217
|
+
|
|
218
|
+
function opened(element: HTMLElement): void {
|
|
219
|
+
const id = element.getAttribute('data-id')
|
|
220
|
+
const option = uniqSnackbarOptions.find((option) => option.id === toNumber(id))
|
|
221
|
+
if (option) option.reactiveSnackOptions.onOpened?.()
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function removeUniqOption(element: HTMLElement): void {
|
|
225
|
+
element.parentElement && element.parentElement.classList.remove('var-site-pointer-auto')
|
|
226
|
+
const id = element.getAttribute('data-id')
|
|
227
|
+
|
|
228
|
+
const option = uniqSnackbarOptions.find((option) => option.id === toNumber(id))
|
|
229
|
+
if (option) {
|
|
230
|
+
option.animationEnd = true
|
|
231
|
+
option.reactiveSnackOptions.onClosed?.()
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const isAllAnimationEnd = uniqSnackbarOptions.every((option) => option.animationEnd)
|
|
235
|
+
|
|
236
|
+
if (isAllAnimationEnd) {
|
|
237
|
+
unmount?.()
|
|
238
|
+
uniqSnackbarOptions = reactive<UniqSnackbarOptions[]>([])
|
|
239
|
+
isMount = false
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function addUniqOption(uniqSnackbarOptionItem: UniqSnackbarOptions) {
|
|
244
|
+
uniqSnackbarOptions.push(uniqSnackbarOptionItem)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function updateUniqOption(reactiveSnackOptions: SnackbarOptions, _update: string) {
|
|
248
|
+
const [firstOption] = uniqSnackbarOptions
|
|
249
|
+
|
|
250
|
+
firstOption.reactiveSnackOptions = {
|
|
251
|
+
...firstOption.reactiveSnackOptions,
|
|
252
|
+
...reactiveSnackOptions,
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
firstOption._update = _update
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function getTop(position = 'top') {
|
|
259
|
+
if (position === 'bottom') return { [position]: '5%' }
|
|
260
|
+
|
|
261
|
+
return { top: position === 'top' ? '5%' : '45%' }
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
VarSiteSnackbar.install = function (app: App) {
|
|
265
|
+
app.component(VarSiteSnackbar.name, VarSiteSnackbar)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export const _SnackbarComponent = VarSiteSnackbar
|
|
269
|
+
|
|
270
|
+
export default Snackbar
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { pickProps } from '../utils/components'
|
|
2
|
+
import { props as loadingProps } from '../loading/props'
|
|
3
|
+
import { SNACKBAR_TYPE, SnackbarType } from './index'
|
|
4
|
+
import type { PropType, TeleportProps } from 'vue'
|
|
5
|
+
|
|
6
|
+
export function positionValidator(position: string): boolean {
|
|
7
|
+
const validPositions = ['top', 'center', 'bottom']
|
|
8
|
+
return validPositions.includes(position)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function typeValidator(type: SnackbarType): boolean {
|
|
12
|
+
return SNACKBAR_TYPE.includes(type)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const props = {
|
|
16
|
+
type: {
|
|
17
|
+
type: String as PropType<SnackbarType>,
|
|
18
|
+
validator: typeValidator,
|
|
19
|
+
},
|
|
20
|
+
// snackbar显示的位置
|
|
21
|
+
position: {
|
|
22
|
+
type: String,
|
|
23
|
+
default: 'top',
|
|
24
|
+
validator: positionValidator,
|
|
25
|
+
},
|
|
26
|
+
// content内容
|
|
27
|
+
content: {
|
|
28
|
+
type: String,
|
|
29
|
+
},
|
|
30
|
+
// 为snackbar content添加自定义类名
|
|
31
|
+
contentClass: {
|
|
32
|
+
type: String,
|
|
33
|
+
},
|
|
34
|
+
// snackbar 持续时间
|
|
35
|
+
duration: {
|
|
36
|
+
type: Number,
|
|
37
|
+
default: 3000,
|
|
38
|
+
},
|
|
39
|
+
// 是否将消息条内容堆叠在操作(按钮)之上
|
|
40
|
+
vertical: {
|
|
41
|
+
type: Boolean,
|
|
42
|
+
default: false,
|
|
43
|
+
},
|
|
44
|
+
// 加载动画类型
|
|
45
|
+
loadingType: pickProps(loadingProps, 'type'),
|
|
46
|
+
// 加载动画尺寸
|
|
47
|
+
loadingSize: pickProps(loadingProps, 'size'),
|
|
48
|
+
// 是否禁止滚动穿透
|
|
49
|
+
lockScroll: {
|
|
50
|
+
type: Boolean,
|
|
51
|
+
default: false,
|
|
52
|
+
},
|
|
53
|
+
// 控制组件可见还是隐藏
|
|
54
|
+
show: {
|
|
55
|
+
type: Boolean,
|
|
56
|
+
default: false,
|
|
57
|
+
},
|
|
58
|
+
// teleport
|
|
59
|
+
teleport: {
|
|
60
|
+
type: String as PropType<TeleportProps['to']>,
|
|
61
|
+
default: 'body',
|
|
62
|
+
},
|
|
63
|
+
// 是否禁止点击背景
|
|
64
|
+
forbidClick: {
|
|
65
|
+
type: Boolean,
|
|
66
|
+
default: false,
|
|
67
|
+
},
|
|
68
|
+
// 打开时的回调函数
|
|
69
|
+
onOpen: {
|
|
70
|
+
type: Function,
|
|
71
|
+
default: () => {},
|
|
72
|
+
},
|
|
73
|
+
// 打开动画结束时的回调
|
|
74
|
+
onOpened: {
|
|
75
|
+
type: Function,
|
|
76
|
+
default: () => {},
|
|
77
|
+
},
|
|
78
|
+
// 关闭时的回调函数
|
|
79
|
+
onClose: {
|
|
80
|
+
type: Function,
|
|
81
|
+
default: () => {},
|
|
82
|
+
},
|
|
83
|
+
// 关闭动画结束时的回调
|
|
84
|
+
onClosed: {
|
|
85
|
+
type: Function,
|
|
86
|
+
default: () => {},
|
|
87
|
+
},
|
|
88
|
+
'onUpdate:show': {
|
|
89
|
+
type: Function,
|
|
90
|
+
},
|
|
91
|
+
_update: {
|
|
92
|
+
type: String,
|
|
93
|
+
},
|
|
94
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
@site-snackbar-width: 256px;
|
|
2
|
+
@site-snackbar-color: rgba(255, 255, 255, 0.87);
|
|
3
|
+
@site-snackbar-border-radius: 4px;
|
|
4
|
+
@site-snackbar-background: #333;
|
|
5
|
+
@site-snackbar-font-size: var(--site-font-size-md);
|
|
6
|
+
@site-snackbar-margin: 6px 24px;
|
|
7
|
+
@site-snackbar-border-color: currentColor;
|
|
8
|
+
@site-snackbar-success-background: var(--site-color-success);
|
|
9
|
+
@site-snackbar-info-background: var(--site-color-info);
|
|
10
|
+
@site-snackbar-error-background: var(--site-color-danger);
|
|
11
|
+
@site-snackbar-warning-background: var(--site-color-warning);
|
|
12
|
+
@site-snackbar-content-padding: 14px 16px;
|
|
13
|
+
@site-snackbar-action-margin: 0 16px 0 0;
|
|
14
|
+
|
|
15
|
+
:root {
|
|
16
|
+
--site-snackbar-width: @site-snackbar-width;
|
|
17
|
+
--site-snackbar-color: @site-snackbar-color;
|
|
18
|
+
--site-snackbar-border-radius: @site-snackbar-border-radius;
|
|
19
|
+
--site-snackbar-background: @site-snackbar-background;
|
|
20
|
+
--site-snackbar-font-size: @site-snackbar-font-size;
|
|
21
|
+
--site-snackbar-margin: @site-snackbar-margin;
|
|
22
|
+
--site-snackbar-border-color: @site-snackbar-border-color;
|
|
23
|
+
--site-snackbar-success-background: @site-snackbar-success-background;
|
|
24
|
+
--site-snackbar-info-background: @site-snackbar-info-background;
|
|
25
|
+
--site-snackbar-error-background: @site-snackbar-error-background;
|
|
26
|
+
--site-snackbar-warning-background: @site-snackbar-warning-background;
|
|
27
|
+
--site-snackbar-content-padding: @site-snackbar-content-padding;
|
|
28
|
+
--site-snackbar-action-margin: @site-snackbar-action-margin;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.var-site-transition-group {
|
|
32
|
+
position: fixed;
|
|
33
|
+
left: 0;
|
|
34
|
+
right: 0;
|
|
35
|
+
top: 0;
|
|
36
|
+
bottom: 0;
|
|
37
|
+
pointer-events: none;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.var-site-pointer-auto {
|
|
41
|
+
pointer-events: auto;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.var-site {
|
|
45
|
+
&-snackbar {
|
|
46
|
+
display: flex;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
align-items: baseline;
|
|
49
|
+
left: 0;
|
|
50
|
+
right: 0;
|
|
51
|
+
bottom: 0;
|
|
52
|
+
transition: all 0.15s var(--site-cubic-bezier);
|
|
53
|
+
|
|
54
|
+
&__wrapper {
|
|
55
|
+
width: var(--site-snackbar-width);
|
|
56
|
+
display: flex;
|
|
57
|
+
border-radius: var(--site-snackbar-border-radius);
|
|
58
|
+
color: var(--site-snackbar-color);
|
|
59
|
+
background: var(--site-snackbar-background);
|
|
60
|
+
font-size: var(--site-snackbar-font-size);
|
|
61
|
+
margin: var(--site-snackbar-margin);
|
|
62
|
+
align-items: center;
|
|
63
|
+
border-color: var(--site-snackbar-border-color);
|
|
64
|
+
pointer-events: auto;
|
|
65
|
+
transition: 0.3s var(--site-cubic-bezier);
|
|
66
|
+
|
|
67
|
+
&-success {
|
|
68
|
+
background: var(--site-snackbar-success-background);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
&-info {
|
|
72
|
+
background: var(--site-snackbar-info-background);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
&-warning {
|
|
76
|
+
background: var(--site-snackbar-warning-background);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
&-error {
|
|
80
|
+
background: var(--site-snackbar-error-background);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
&__content {
|
|
85
|
+
flex-grow: 1;
|
|
86
|
+
padding: var(--site-snackbar-content-padding);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
&__action {
|
|
90
|
+
margin: var(--site-snackbar-action-margin);
|
|
91
|
+
display: flex;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
&__vertical {
|
|
95
|
+
flex-direction: column;
|
|
96
|
+
align-items: flex-start;
|
|
97
|
+
|
|
98
|
+
.var-site-snackbar__action {
|
|
99
|
+
align-self: flex-end;
|
|
100
|
+
margin-bottom: 8px;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
&-fade-leave-active {
|
|
105
|
+
position: absolute;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
&-fade-enter-from,
|
|
109
|
+
&-fade-leave-to {
|
|
110
|
+
opacity: 0;
|
|
111
|
+
transform: translateY(-30px);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
&-snackbar-transition {
|
|
116
|
+
top: 0;
|
|
117
|
+
position: fixed;
|
|
118
|
+
|
|
119
|
+
.var-site-snackbar__wrapper {
|
|
120
|
+
position: absolute;
|
|
121
|
+
|
|
122
|
+
&-top {
|
|
123
|
+
top: 5%;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
&-center {
|
|
127
|
+
top: 45%;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
&-bottom {
|
|
131
|
+
bottom: 5%;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isNumber, isString, toNumber, kebabCase } from './shared'
|
|
2
2
|
import type { StyleVars } from '../../utils'
|
|
3
|
+
import { requestAnimationFrame } from '@varlet/ui/src/utils/elements'
|
|
3
4
|
|
|
4
5
|
export function getLeft(element: HTMLElement): number {
|
|
5
6
|
const { left } = element.getBoundingClientRect()
|
|
@@ -81,3 +82,11 @@ export function formatStyleVars(styleVars: StyleVars | null) {
|
|
|
81
82
|
return styles
|
|
82
83
|
}, {} as StyleVars)
|
|
83
84
|
}
|
|
85
|
+
|
|
86
|
+
export function doubleRaf() {
|
|
87
|
+
return new Promise((resolve) => {
|
|
88
|
+
requestAnimationFrame(() => {
|
|
89
|
+
requestAnimationFrame(resolve)
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
}
|
|
@@ -12,6 +12,9 @@ export const toNumber = (val: number | string | boolean | undefined | null): num
|
|
|
12
12
|
return val
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
export const isPlainObject = (val: unknown): val is Record<string, any> =>
|
|
16
|
+
Object.prototype.toString.call(val) === '[object Object]'
|
|
17
|
+
|
|
15
18
|
export function kebabCase(str: string): string {
|
|
16
19
|
const reg = /([^-])([A-Z])/g
|
|
17
20
|
|
package/site/mobile/App.vue
CHANGED
|
@@ -86,7 +86,6 @@
|
|
|
86
86
|
</template>
|
|
87
87
|
|
|
88
88
|
<script lang="ts">
|
|
89
|
-
// @ts-ignore
|
|
90
89
|
import config from '@config'
|
|
91
90
|
import { computed, ComputedRef, defineComponent, ref, Ref, watch } from 'vue'
|
|
92
91
|
import { useRoute } from 'vue-router'
|
|
@@ -98,7 +97,7 @@ import {
|
|
|
98
97
|
removeEmpty,
|
|
99
98
|
setThemes,
|
|
100
99
|
watchLang,
|
|
101
|
-
watchThemes
|
|
100
|
+
watchThemes,
|
|
102
101
|
} from '../utils'
|
|
103
102
|
import { get } from 'lodash-es'
|
|
104
103
|
|
|
@@ -112,11 +111,12 @@ export default defineComponent({
|
|
|
112
111
|
const languages: Ref<Record<string, string>> = ref(get(config, 'mobile.header.i18n'))
|
|
113
112
|
const nonEmptyLanguages: ComputedRef<Record<string, string>> = computed(() => removeEmpty(languages.value))
|
|
114
113
|
const redirect = get(config, 'mobile.redirect', '')
|
|
114
|
+
const themesKey = get(config, 'themesKey')
|
|
115
115
|
const github: Ref<string> = ref(get(config, 'mobile.header.github'))
|
|
116
116
|
const darkMode: Ref<string> = ref(get(config, 'mobile.header.darkMode'))
|
|
117
|
-
const currentThemes = ref(getBrowserThemes())
|
|
117
|
+
const currentThemes = ref(getBrowserThemes(themesKey))
|
|
118
118
|
|
|
119
|
-
const changeLanguage = (lang) => {
|
|
119
|
+
const changeLanguage = (lang: string) => {
|
|
120
120
|
language.value = lang
|
|
121
121
|
showMenu.value = false
|
|
122
122
|
window.location.href = `./mobile.html#${route.path}?language=${language.value}&replace=${route.query.replace}`
|
|
@@ -136,7 +136,7 @@ export default defineComponent({
|
|
|
136
136
|
|
|
137
137
|
const toGithub = () => {
|
|
138
138
|
if (inIframe() && !isPhone()) {
|
|
139
|
-
window.top
|
|
139
|
+
window.top!.open(github.value)
|
|
140
140
|
} else {
|
|
141
141
|
window.location.href = github.value
|
|
142
142
|
}
|
|
@@ -161,7 +161,7 @@ export default defineComponent({
|
|
|
161
161
|
const setCurrentThemes = (themes: 'themes' | 'darkThemes') => {
|
|
162
162
|
currentThemes.value = themes
|
|
163
163
|
setThemes(config, currentThemes.value)
|
|
164
|
-
window.localStorage.setItem(
|
|
164
|
+
window.localStorage.setItem(themesKey, currentThemes.value)
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
const toggleTheme = () => {
|
package/site/module.d.ts
ADDED
package/site/pc/App.vue
CHANGED
|
@@ -29,11 +29,10 @@
|
|
|
29
29
|
</template>
|
|
30
30
|
|
|
31
31
|
<script lang="ts">
|
|
32
|
-
// @ts-ignore
|
|
33
32
|
import config from '@config'
|
|
34
|
-
import AppMobile from './components/AppMobile'
|
|
35
|
-
import AppHeader from './components/AppHeader'
|
|
36
|
-
import AppSidebar from './components/AppSidebar'
|
|
33
|
+
import AppMobile from './components/AppMobile.vue'
|
|
34
|
+
import AppHeader from './components/AppHeader.vue'
|
|
35
|
+
import AppSidebar from './components/AppSidebar.vue'
|
|
37
36
|
import { defineComponent, nextTick, onMounted, ref, Ref, watch } from 'vue'
|
|
38
37
|
import { useRoute } from 'vue-router'
|
|
39
38
|
import { get } from 'lodash-es'
|
|
@@ -52,7 +51,6 @@ export default defineComponent({
|
|
|
52
51
|
AppSidebar
|
|
53
52
|
},
|
|
54
53
|
setup() {
|
|
55
|
-
// config
|
|
56
54
|
const defaultLanguage = get(config, 'defaultLanguage')
|
|
57
55
|
const menu: Ref<Menu[]> = ref(get(config, 'pc.menu', []))
|
|
58
56
|
const useMobile = ref(get(config, 'useMobile'))
|
|
@@ -79,7 +77,7 @@ export default defineComponent({
|
|
|
79
77
|
|
|
80
78
|
nextTick(() => {
|
|
81
79
|
const children = document
|
|
82
|
-
.querySelector('.varlet-site-sidebar')
|
|
80
|
+
.querySelector('.varlet-site-sidebar')!
|
|
83
81
|
.getElementsByClassName('var-site-cell')
|
|
84
82
|
const index = menu.value.findIndex((item) => item.doc === menuName)
|
|
85
83
|
|
|
@@ -93,7 +91,7 @@ export default defineComponent({
|
|
|
93
91
|
}
|
|
94
92
|
|
|
95
93
|
const handleSidebarChange = (menu: Menu) => {
|
|
96
|
-
doc.value
|
|
94
|
+
doc.value!.scrollTop = 0
|
|
97
95
|
componentName.value = getComponentNameByMenuName(menu.doc)
|
|
98
96
|
menuName.value = menu.doc
|
|
99
97
|
}
|
|
@@ -287,17 +285,17 @@ iframe {
|
|
|
287
285
|
}
|
|
288
286
|
|
|
289
287
|
pre {
|
|
290
|
-
margin:
|
|
288
|
+
margin: 10px 0 0;
|
|
291
289
|
}
|
|
292
290
|
|
|
293
291
|
code {
|
|
294
292
|
position: relative;
|
|
295
293
|
display: block;
|
|
296
|
-
padding: 16px;
|
|
294
|
+
padding: 10px 16px;
|
|
297
295
|
overflow-x: auto;
|
|
298
296
|
font-size: 13px;
|
|
299
297
|
font-family: Consolas, Monaco, monospace;
|
|
300
|
-
line-height:
|
|
298
|
+
line-height: 31px;
|
|
301
299
|
white-space: pre-wrap;
|
|
302
300
|
word-wrap: break-word;
|
|
303
301
|
color: #fff;
|
|
@@ -61,15 +61,14 @@
|
|
|
61
61
|
</template>
|
|
62
62
|
|
|
63
63
|
<script lang="ts">
|
|
64
|
-
// @ts-ignore
|
|
65
64
|
import config from '@config'
|
|
66
|
-
import { ref, computed } from 'vue'
|
|
65
|
+
import { ref, computed, defineComponent } from 'vue'
|
|
67
66
|
import { get } from 'lodash-es'
|
|
68
67
|
import { getBrowserThemes, getPCLocationInfo, removeEmpty, setThemes, watchThemes } from '../../utils'
|
|
69
68
|
import { useRouter } from 'vue-router'
|
|
70
69
|
import type { Ref, ComputedRef } from 'vue'
|
|
71
70
|
|
|
72
|
-
export default {
|
|
71
|
+
export default defineComponent({
|
|
73
72
|
name: 'AppHeader',
|
|
74
73
|
props: {
|
|
75
74
|
language: {
|
|
@@ -77,19 +76,19 @@ export default {
|
|
|
77
76
|
},
|
|
78
77
|
},
|
|
79
78
|
setup() {
|
|
80
|
-
// config
|
|
81
79
|
const title: Ref<string> = ref(get(config, 'title'))
|
|
82
80
|
const logo: Ref<string> = ref(get(config, 'logo'))
|
|
81
|
+
const themesKey = get(config, 'themesKey')
|
|
83
82
|
const languages: Ref<Record<string, string>> = ref(get(config, 'pc.header.i18n'))
|
|
84
83
|
const github: Ref<Record<string, string>> = ref(get(config, 'pc.header.github'))
|
|
85
84
|
const darkMode: Ref<Record<string, string>> = ref(get(config, 'pc.header.darkMode'))
|
|
86
|
-
const currentThemes = ref(getBrowserThemes())
|
|
85
|
+
const currentThemes = ref(getBrowserThemes(themesKey))
|
|
87
86
|
|
|
88
87
|
const isOpenMenu: Ref<boolean> = ref(false)
|
|
89
88
|
const router = useRouter()
|
|
90
89
|
const nonEmptyLanguages: ComputedRef<Record<string, string>> = computed(() => removeEmpty(languages.value))
|
|
91
90
|
|
|
92
|
-
const handleLanguageChange = (language) => {
|
|
91
|
+
const handleLanguageChange = (language: string) => {
|
|
93
92
|
const { menuName } = getPCLocationInfo()
|
|
94
93
|
router.replace(`/${language}/${menuName}`)
|
|
95
94
|
isOpenMenu.value = false
|
|
@@ -98,7 +97,7 @@ export default {
|
|
|
98
97
|
const setCurrentThemes = (themes: 'themes' | 'darkThemes') => {
|
|
99
98
|
currentThemes.value = themes
|
|
100
99
|
setThemes(config, currentThemes.value)
|
|
101
|
-
window.localStorage.setItem(
|
|
100
|
+
window.localStorage.setItem(themesKey, currentThemes.value)
|
|
102
101
|
}
|
|
103
102
|
|
|
104
103
|
const getThemesMessage = () => ({ action: 'themesChange', from: 'pc', data: currentThemes.value })
|
|
@@ -106,7 +105,7 @@ export default {
|
|
|
106
105
|
const toggleTheme = () => {
|
|
107
106
|
setCurrentThemes(currentThemes.value === 'darkThemes' ? 'themes' : 'darkThemes')
|
|
108
107
|
window.postMessage(getThemesMessage(), '*')
|
|
109
|
-
;(document.getElementById('mobile') as HTMLIFrameElement).contentWindow
|
|
108
|
+
;(document.getElementById('mobile') as HTMLIFrameElement).contentWindow!.postMessage(getThemesMessage(), '*')
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
watchThemes((themes, from) => {
|
|
@@ -129,7 +128,7 @@ export default {
|
|
|
129
128
|
toggleTheme,
|
|
130
129
|
}
|
|
131
130
|
},
|
|
132
|
-
}
|
|
131
|
+
})
|
|
133
132
|
</script>
|
|
134
133
|
|
|
135
134
|
<style scoped lang="less">
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
// @ts-ignore
|
|
28
28
|
import config from '@config'
|
|
29
29
|
import { MenuTypes } from '../../utils'
|
|
30
|
-
import { reactive, ref } from 'vue'
|
|
30
|
+
import { reactive, ref, defineComponent } from 'vue'
|
|
31
31
|
import type { PropType } from 'vue'
|
|
32
|
-
import type { Menu } from '../App'
|
|
32
|
+
import type { Menu } from '../App.vue'
|
|
33
33
|
import { get } from 'lodash-es'
|
|
34
34
|
|
|
35
|
-
export default {
|
|
35
|
+
export default defineComponent({
|
|
36
36
|
name: 'AppSidebar',
|
|
37
37
|
props: {
|
|
38
38
|
menu: {
|
|
@@ -50,7 +50,7 @@ export default {
|
|
|
50
50
|
const menuTypes = reactive(MenuTypes)
|
|
51
51
|
const themes = ref(get(config, 'themes'))
|
|
52
52
|
|
|
53
|
-
const changeRoute = (item) => {
|
|
53
|
+
const changeRoute = (item: Menu) => {
|
|
54
54
|
if (item.type === MenuTypes.TITLE || props.menuName === item.doc) {
|
|
55
55
|
return
|
|
56
56
|
}
|
|
@@ -64,7 +64,7 @@ export default {
|
|
|
64
64
|
changeRoute
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
-
}
|
|
67
|
+
})
|
|
68
68
|
</script>
|
|
69
69
|
|
|
70
70
|
<style scoped lang="less">
|
package/site/pc/main.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import App from './App.vue'
|
|
2
2
|
import '@varlet/touch-emulator'
|
|
3
|
-
// @ts-ignore
|
|
4
3
|
import routes from '@pc-routes'
|
|
5
|
-
// @ts-ignore
|
|
6
4
|
import config from '@config'
|
|
7
5
|
|
|
8
6
|
import Icon from '../components/icon'
|
|
9
7
|
import Cell from '../components/cell'
|
|
10
8
|
import Ripple from '../components/ripple'
|
|
9
|
+
import CodeExample from '../components/code-example'
|
|
10
|
+
import Snackbar from '../components/snackbar'
|
|
11
|
+
|
|
11
12
|
import '../components/styles/common.less'
|
|
12
13
|
import '../components/styles/elevation.less'
|
|
13
14
|
|
|
@@ -20,6 +21,8 @@ const defaultLanguage = get(config, 'defaultLanguage')
|
|
|
20
21
|
const redirect = get(config, 'pc.redirect')
|
|
21
22
|
const mobileRedirect = get(config, 'mobile.redirect')
|
|
22
23
|
|
|
24
|
+
Snackbar.allowMultiple(true)
|
|
25
|
+
|
|
23
26
|
redirect &&
|
|
24
27
|
routes.push({
|
|
25
28
|
path: '/:pathMatch(.*)*',
|
|
@@ -85,4 +88,7 @@ createApp(App)
|
|
|
85
88
|
.use(Ripple)
|
|
86
89
|
// @ts-ignore
|
|
87
90
|
.use(Icon)
|
|
91
|
+
// @ts-ignore
|
|
92
|
+
.use(CodeExample)
|
|
93
|
+
.use(Snackbar)
|
|
88
94
|
.mount('#app')
|
package/site/tsconfig.json
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"outDir": "./lib",
|
|
4
3
|
"target": "es5",
|
|
5
|
-
"module": "commonjs",
|
|
6
|
-
"sourceMap": false,
|
|
7
4
|
"strict": true,
|
|
8
5
|
"downlevelIteration": true,
|
|
9
|
-
"declaration": true,
|
|
10
6
|
"skipLibCheck": true,
|
|
11
7
|
"esModuleInterop": true,
|
|
12
8
|
"jsx": "preserve",
|
|
13
9
|
"lib": ["esnext", "dom"]
|
|
14
|
-
}
|
|
15
|
-
"include": ["*"]
|
|
10
|
+
}
|
|
16
11
|
}
|
package/site/useProgress.ts
CHANGED
|
@@ -7,11 +7,15 @@ import { getBrowserThemes, mountInstance, watchThemes } from './utils'
|
|
|
7
7
|
import { get } from 'lodash-es'
|
|
8
8
|
|
|
9
9
|
function getColor(themes?: 'themes' | 'darkThemes') {
|
|
10
|
-
|
|
10
|
+
const themesKey = get(config, 'themesKey')
|
|
11
|
+
|
|
12
|
+
return get(config, `${themes ?? getBrowserThemes(themesKey)}.color-progress`)
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
function getTrackColor(themes?: 'themes' | 'darkThemes') {
|
|
14
|
-
|
|
16
|
+
const themesKey = get(config, 'themesKey')
|
|
17
|
+
|
|
18
|
+
return get(config, `${themes ?? getBrowserThemes(themesKey)}.color-progress-track`)
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
export function useProgress() {
|
package/site/utils.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { onMounted, onUnmounted } from 'vue'
|
|
2
2
|
import { get } from 'lodash-es'
|
|
3
3
|
import { formatStyleVars } from './components/utils/elements'
|
|
4
|
+
import config from '@config'
|
|
4
5
|
|
|
5
6
|
export * from './components/utils/components'
|
|
6
7
|
export * from './components/utils/elements'
|
|
@@ -138,12 +139,12 @@ export function setThemes(config: Record<string, any>, name: 'themes' | 'darkThe
|
|
|
138
139
|
StyleProvider(styleVars)
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
export function getBrowserThemes(): 'darkThemes' | 'themes' {
|
|
142
|
-
let currentThemes = window.localStorage.getItem(
|
|
142
|
+
export function getBrowserThemes(themes = 'VARLET_THEMES'): 'darkThemes' | 'themes' {
|
|
143
|
+
let currentThemes = window.localStorage.getItem(themes) as 'darkThemes' | 'themes'
|
|
143
144
|
|
|
144
145
|
if (!currentThemes) {
|
|
145
146
|
currentThemes = window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'darkThemes' : 'themes'
|
|
146
|
-
window.localStorage.setItem(
|
|
147
|
+
window.localStorage.setItem(themes, currentThemes)
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
return currentThemes
|
package/tsconfig.json
CHANGED
package/varlet.default.config.js
CHANGED
|
@@ -4,6 +4,7 @@ module.exports = {
|
|
|
4
4
|
host: 'localhost',
|
|
5
5
|
port: 8080,
|
|
6
6
|
title: 'VARLET',
|
|
7
|
+
themesKey: 'VARLET_THEMES',
|
|
7
8
|
logo: 'https://varlet.gitee.io/varlet-ui/varlet_icon.png',
|
|
8
9
|
defaultLanguage: 'zh-CN',
|
|
9
10
|
highlight: {
|
|
@@ -31,6 +32,18 @@ module.exports = {
|
|
|
31
32
|
github: 'https://github.com/haoziqaq/varlet',
|
|
32
33
|
darkMode: true,
|
|
33
34
|
},
|
|
35
|
+
clipboard: {
|
|
36
|
+
'zh-CN': '代码已复制到剪切板',
|
|
37
|
+
'en-US': 'The code has been copied to the clipboard',
|
|
38
|
+
},
|
|
39
|
+
fold: {
|
|
40
|
+
message: {
|
|
41
|
+
'zh-CN': '已显示完整代码',
|
|
42
|
+
'en-US': 'Complete code displayed',
|
|
43
|
+
},
|
|
44
|
+
defaultFold: true,
|
|
45
|
+
foldHeight: 60,
|
|
46
|
+
},
|
|
34
47
|
},
|
|
35
48
|
mobile: {
|
|
36
49
|
redirect: '/home',
|