Commit 7db0c5c49f23a4ab4958b3f73d47516deafa6166
1 parent
10cd4fcd
fix: add an example of markdown embedded in the form #138
Showing
16 changed files
with
463 additions
and
354 deletions
CHANGELOG.zh_CN.md
src/components/Container/src/ScrollContainer.vue
src/components/Markdown/src/index.vue
@@ -2,14 +2,25 @@ | @@ -2,14 +2,25 @@ | ||
2 | <div class="markdown" ref="wrapRef" /> | 2 | <div class="markdown" ref="wrapRef" /> |
3 | </template> | 3 | </template> |
4 | <script lang="ts"> | 4 | <script lang="ts"> |
5 | - import { defineComponent, ref, onMounted, unref, onUnmounted, nextTick, watchEffect } from 'vue'; | 5 | + import { |
6 | + defineComponent, | ||
7 | + ref, | ||
8 | + onMounted, | ||
9 | + unref, | ||
10 | + onUnmounted, | ||
11 | + nextTick, | ||
12 | + // watch, | ||
13 | + computed, | ||
14 | + } from 'vue'; | ||
6 | import Vditor from 'vditor'; | 15 | import Vditor from 'vditor'; |
7 | import 'vditor/dist/index.css'; | 16 | import 'vditor/dist/index.css'; |
8 | 17 | ||
9 | import { propTypes } from '/@/utils/propTypes'; | 18 | import { propTypes } from '/@/utils/propTypes'; |
19 | + import { useLocale } from '/@/hooks/web/useLocale'; | ||
10 | 20 | ||
21 | + type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined; | ||
11 | export default defineComponent({ | 22 | export default defineComponent({ |
12 | - emits: ['update:value'], | 23 | + emits: ['change'], |
13 | props: { | 24 | props: { |
14 | height: propTypes.number.def(360), | 25 | height: propTypes.number.def(360), |
15 | value: propTypes.string.def(''), | 26 | value: propTypes.string.def(''), |
@@ -19,17 +30,42 @@ | @@ -19,17 +30,42 @@ | ||
19 | const vditorRef = ref<Nullable<Vditor>>(null); | 30 | const vditorRef = ref<Nullable<Vditor>>(null); |
20 | const initedRef = ref(false); | 31 | const initedRef = ref(false); |
21 | 32 | ||
33 | + const lang = ref<Lang>(); | ||
34 | + | ||
35 | + const { getLang } = useLocale(); | ||
36 | + | ||
37 | + const getCurrentLang = computed((): 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' => { | ||
38 | + switch (unref(getLang)) { | ||
39 | + case 'en': | ||
40 | + lang.value = 'en_US'; | ||
41 | + break; | ||
42 | + case 'ja': | ||
43 | + lang.value = 'ja_JP'; | ||
44 | + break; | ||
45 | + case 'ko': | ||
46 | + lang.value = 'ko_KR'; | ||
47 | + break; | ||
48 | + default: | ||
49 | + lang.value = 'zh_CN'; | ||
50 | + } | ||
51 | + return lang.value; | ||
52 | + }); | ||
22 | function init() { | 53 | function init() { |
23 | const wrapEl = unref(wrapRef); | 54 | const wrapEl = unref(wrapRef); |
24 | if (!wrapEl) return; | 55 | if (!wrapEl) return; |
25 | const bindValue = { ...attrs, ...props }; | 56 | const bindValue = { ...attrs, ...props }; |
26 | vditorRef.value = new Vditor(wrapEl, { | 57 | vditorRef.value = new Vditor(wrapEl, { |
58 | + lang: unref(getCurrentLang), | ||
27 | mode: 'sv', | 59 | mode: 'sv', |
28 | preview: { | 60 | preview: { |
29 | actions: [], | 61 | actions: [], |
30 | }, | 62 | }, |
31 | input: (v) => { | 63 | input: (v) => { |
32 | - emit('update:value', v); | 64 | + // emit('update:value', v); |
65 | + emit('change', v); | ||
66 | + }, | ||
67 | + blur: () => { | ||
68 | + unref(vditorRef)?.setValue(props.value); | ||
33 | }, | 69 | }, |
34 | ...bindValue, | 70 | ...bindValue, |
35 | cache: { | 71 | cache: { |
@@ -39,14 +75,20 @@ | @@ -39,14 +75,20 @@ | ||
39 | initedRef.value = true; | 75 | initedRef.value = true; |
40 | } | 76 | } |
41 | 77 | ||
42 | - watchEffect(() => { | ||
43 | - nextTick(() => { | ||
44 | - const vditor = unref(vditorRef); | ||
45 | - if (unref(initedRef) && props.value && vditor) { | ||
46 | - vditor.setValue(props.value); | ||
47 | - } | ||
48 | - }); | ||
49 | - }); | 78 | + // watch( |
79 | + // () => props.value, | ||
80 | + // () => { | ||
81 | + // nextTick(() => { | ||
82 | + // const vditor = unref(vditorRef); | ||
83 | + // if (unref(initedRef) && props.value && vditor) { | ||
84 | + // vditor.setValue(props.value); | ||
85 | + // } | ||
86 | + // }); | ||
87 | + // }, | ||
88 | + // { | ||
89 | + // immediate: true, | ||
90 | + // } | ||
91 | + // ); | ||
50 | 92 | ||
51 | onMounted(() => { | 93 | onMounted(() => { |
52 | nextTick(() => { | 94 | nextTick(() => { |
src/components/Scrollbar/index.ts
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | import { withInstall } from '../util'; | 5 | import { withInstall } from '../util'; |
6 | 6 | ||
7 | import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; | 7 | import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; |
8 | -export const Scrollbar = createAsyncComponent(() => import('./src/Scrollbar')); | 8 | +export const Scrollbar = createAsyncComponent(() => import('./src/index.vue')); |
9 | 9 | ||
10 | withInstall(Scrollbar); | 10 | withInstall(Scrollbar); |
11 | 11 |
src/components/Scrollbar/src/Bar.tsx deleted
100644 → 0
1 | -import type { PropType } from 'vue'; | ||
2 | - | ||
3 | -import { renderThumbStyle, BAR_MAP } from './util'; | ||
4 | -import { defineComponent, computed, unref, inject, Ref, reactive, ref, onBeforeUnmount } from 'vue'; | ||
5 | -import { on, off } from '/@/utils/domUtils'; | ||
6 | - | ||
7 | -export default defineComponent({ | ||
8 | - name: 'Bar', | ||
9 | - props: { | ||
10 | - vertical: { | ||
11 | - type: Boolean as PropType<boolean>, | ||
12 | - default: false, | ||
13 | - }, | ||
14 | - size: String as PropType<string>, | ||
15 | - move: Number as PropType<number>, | ||
16 | - }, | ||
17 | - setup(props) { | ||
18 | - const thumbRef = ref<Nullable<HTMLDivElement>>(null); | ||
19 | - const elRef = ref<Nullable<HTMLDivElement>>(null); | ||
20 | - const commonState = reactive<Indexable>({}); | ||
21 | - const getBarRef = computed(() => { | ||
22 | - return BAR_MAP[props.vertical ? 'vertical' : 'horizontal']; | ||
23 | - }); | ||
24 | - const parentElRef = inject('scroll-bar-wrap') as Ref<Nullable<HTMLDivElement>>; | ||
25 | - | ||
26 | - function clickThumbHandler(e: any) { | ||
27 | - const { ctrlKey, button, currentTarget } = e; | ||
28 | - // prevent click event of right button | ||
29 | - if (ctrlKey || button === 2 || !currentTarget) { | ||
30 | - return; | ||
31 | - } | ||
32 | - startDrag(e); | ||
33 | - const bar = unref(getBarRef); | ||
34 | - commonState[bar.axis] = | ||
35 | - currentTarget[bar.offset] - | ||
36 | - (e[bar.client as keyof typeof e] - currentTarget.getBoundingClientRect()[bar.direction]); | ||
37 | - } | ||
38 | - | ||
39 | - function clickTrackHandler(e: any) { | ||
40 | - const bar = unref(getBarRef); | ||
41 | - const offset = Math.abs(e.target.getBoundingClientRect()[bar.direction] - e[bar.client]); | ||
42 | - const thumbEl = unref(thumbRef) as any; | ||
43 | - const parentEl = unref(parentElRef) as any; | ||
44 | - const el = unref(elRef) as any; | ||
45 | - if (!thumbEl || !el || !parentEl) return; | ||
46 | - const thumbHalf = thumbEl[bar.offset] / 2; | ||
47 | - const thumbPositionPercentage = ((offset - thumbHalf) * 100) / el[bar.offset]; | ||
48 | - parentEl[bar.scroll] = (thumbPositionPercentage * parentEl[bar.scrollSize]) / 100; | ||
49 | - } | ||
50 | - | ||
51 | - function startDrag(e: Event) { | ||
52 | - e.stopImmediatePropagation(); | ||
53 | - commonState.cursorDown = true; | ||
54 | - | ||
55 | - on(document, 'mousemove', mouseMoveDocumentHandler); | ||
56 | - on(document, 'mouseup', mouseUpDocumentHandler); | ||
57 | - document.onselectstart = () => false; | ||
58 | - } | ||
59 | - | ||
60 | - function mouseMoveDocumentHandler(e: any) { | ||
61 | - if (commonState.cursorDown === false) return; | ||
62 | - const bar = unref(getBarRef); | ||
63 | - const prevPage = commonState[bar.axis]; | ||
64 | - const el = unref(elRef) as any; | ||
65 | - const parentEl = unref(parentElRef) as any; | ||
66 | - const thumbEl = unref(thumbRef) as any; | ||
67 | - if (!prevPage || !el || !thumbEl || !parentEl) return; | ||
68 | - const rect = el.getBoundingClientRect() as any; | ||
69 | - const offset = (rect[bar.direction] - e[bar.client]) * -1; | ||
70 | - const thumbClickPosition = thumbEl[bar.offset] - prevPage; | ||
71 | - const thumbPositionPercentage = ((offset - thumbClickPosition) * 100) / el[bar.offset]; | ||
72 | - | ||
73 | - parentEl[bar.scroll] = (thumbPositionPercentage * parentEl[bar.scrollSize]) / 100; | ||
74 | - } | ||
75 | - | ||
76 | - function mouseUpDocumentHandler() { | ||
77 | - const bar = unref(getBarRef); | ||
78 | - commonState.cursorDown = false; | ||
79 | - commonState[bar.axis] = 0; | ||
80 | - off(document, 'mousemove', mouseMoveDocumentHandler); | ||
81 | - document.onselectstart = null; | ||
82 | - } | ||
83 | - | ||
84 | - onBeforeUnmount(() => { | ||
85 | - off(document, 'mouseup', mouseUpDocumentHandler); | ||
86 | - }); | ||
87 | - return () => { | ||
88 | - const bar = unref(getBarRef); | ||
89 | - const { size, move } = props; | ||
90 | - return ( | ||
91 | - <div | ||
92 | - class={['scrollbar__bar', 'is-' + bar.key]} | ||
93 | - onMousedown={clickTrackHandler} | ||
94 | - ref={elRef} | ||
95 | - > | ||
96 | - <div | ||
97 | - ref={thumbRef} | ||
98 | - class="scrollbar__thumb" | ||
99 | - onMousedown={clickThumbHandler} | ||
100 | - style={renderThumbStyle({ size, move, bar })} | ||
101 | - /> | ||
102 | - </div> | ||
103 | - ); | ||
104 | - }; | ||
105 | - }, | ||
106 | -}); |
src/components/Scrollbar/src/Scrollbar.tsx deleted
100644 → 0
1 | -import { addResizeListener, removeResizeListener } from '/@/utils/event/resizeEvent'; | ||
2 | -import scrollbarWidth from '/@/utils/scrollbarWidth'; | ||
3 | -import { toObject } from './util'; | ||
4 | -import Bar from './Bar'; | ||
5 | -import { isString } from '/@/utils/is'; | ||
6 | -import { | ||
7 | - defineComponent, | ||
8 | - PropType, | ||
9 | - unref, | ||
10 | - reactive, | ||
11 | - ref, | ||
12 | - provide, | ||
13 | - onMounted, | ||
14 | - nextTick, | ||
15 | - onBeforeUnmount, | ||
16 | -} from 'vue'; | ||
17 | -import { getSlot } from '/@/utils/helper/tsxHelper'; | ||
18 | -import './index.less'; | ||
19 | -import { useExpose } from '/@/hooks/core/useExpose'; | ||
20 | -import { ScrollbarType } from './types'; | ||
21 | - | ||
22 | -export default defineComponent({ | ||
23 | - name: 'Scrollbar', | ||
24 | - props: { | ||
25 | - native: Boolean as PropType<boolean>, | ||
26 | - wrapStyle: { | ||
27 | - type: Object as PropType<any>, | ||
28 | - }, | ||
29 | - wrapClass: { type: String as PropType<string>, required: false }, | ||
30 | - viewClass: { type: String as PropType<string> }, | ||
31 | - viewStyle: { type: Object as PropType<any> }, | ||
32 | - noresize: Boolean as PropType<boolean>, | ||
33 | - tag: { | ||
34 | - type: String as PropType<string>, | ||
35 | - default: 'div', | ||
36 | - }, | ||
37 | - }, | ||
38 | - setup(props, { slots }) { | ||
39 | - const resizeRef = ref<Nullable<HTMLDivElement>>(null); | ||
40 | - const wrapElRef = ref<Nullable<HTMLDivElement>>(null); | ||
41 | - provide('scroll-bar-wrap', wrapElRef); | ||
42 | - const state = reactive({ | ||
43 | - sizeWidth: '0', | ||
44 | - sizeHeight: '0', | ||
45 | - moveX: 0, | ||
46 | - moveY: 0, | ||
47 | - }); | ||
48 | - | ||
49 | - function handleScroll() { | ||
50 | - const warpEl = unref(wrapElRef); | ||
51 | - if (!warpEl) return; | ||
52 | - const { scrollTop, scrollLeft, clientHeight, clientWidth } = warpEl; | ||
53 | - | ||
54 | - state.moveY = (scrollTop * 100) / clientHeight; | ||
55 | - state.moveX = (scrollLeft * 100) / clientWidth; | ||
56 | - } | ||
57 | - function update() { | ||
58 | - const warpEl = unref(wrapElRef); | ||
59 | - if (!warpEl) return; | ||
60 | - const { scrollHeight, scrollWidth, clientHeight, clientWidth } = warpEl; | ||
61 | - const heightPercentage = (clientHeight * 100) / scrollHeight; | ||
62 | - const widthPercentage = (clientWidth * 100) / scrollWidth; | ||
63 | - | ||
64 | - state.sizeHeight = heightPercentage < 100 ? heightPercentage + '%' : ''; | ||
65 | - state.sizeWidth = widthPercentage < 100 ? widthPercentage + '%' : ''; | ||
66 | - } | ||
67 | - | ||
68 | - onMounted(() => { | ||
69 | - useExpose<ScrollbarType>({ | ||
70 | - wrap: unref(wrapElRef), | ||
71 | - }); | ||
72 | - const { native, noresize } = props; | ||
73 | - const resizeEl = unref(resizeRef); | ||
74 | - const warpEl = unref(wrapElRef); | ||
75 | - if (native || !resizeEl || !warpEl) return; | ||
76 | - nextTick(update); | ||
77 | - if (!noresize) { | ||
78 | - addResizeListener(resizeEl, update); | ||
79 | - addResizeListener(warpEl, update); | ||
80 | - } | ||
81 | - }); | ||
82 | - onBeforeUnmount(() => { | ||
83 | - const { native, noresize } = props; | ||
84 | - const resizeEl = unref(resizeRef); | ||
85 | - const warpEl = unref(wrapElRef); | ||
86 | - if (native || !resizeEl || !warpEl) return; | ||
87 | - if (!noresize) { | ||
88 | - removeResizeListener(resizeEl, update); | ||
89 | - removeResizeListener(warpEl, update); | ||
90 | - } | ||
91 | - }); | ||
92 | - return () => { | ||
93 | - const { native, tag, viewClass, viewStyle, wrapClass, wrapStyle } = props; | ||
94 | - let style: any = wrapStyle; | ||
95 | - const gutter = scrollbarWidth(); | ||
96 | - | ||
97 | - if (gutter) { | ||
98 | - const gutterWith = `-${gutter}px`; | ||
99 | - const gutterStyle = `margin-bottom: ${gutterWith}; margin-right: ${gutterWith};`; | ||
100 | - | ||
101 | - if (Array.isArray(wrapStyle)) { | ||
102 | - style = toObject(wrapStyle); | ||
103 | - style.marginRight = style.marginBottom = gutterWith; | ||
104 | - } else if (isString(wrapStyle)) { | ||
105 | - style += gutterStyle; | ||
106 | - } else { | ||
107 | - style = gutterStyle; | ||
108 | - } | ||
109 | - } | ||
110 | - | ||
111 | - const Tag = tag as any; | ||
112 | - const view = ( | ||
113 | - <Tag class={['scrollbar__view', viewClass]} style={viewStyle} ref={resizeRef}> | ||
114 | - {getSlot(slots)} | ||
115 | - </Tag> | ||
116 | - ); | ||
117 | - const wrap = ( | ||
118 | - <div | ||
119 | - ref={wrapElRef} | ||
120 | - style={style} | ||
121 | - onScroll={handleScroll} | ||
122 | - class={[wrapClass, 'scrollbar__wrap', gutter ? '' : 'scrollbar__wrap--hidden-default']} | ||
123 | - > | ||
124 | - {[view]} | ||
125 | - </div> | ||
126 | - ); | ||
127 | - let nodes: any[] = []; | ||
128 | - const { moveX, sizeWidth, moveY, sizeHeight } = state; | ||
129 | - if (!native) { | ||
130 | - nodes = [ | ||
131 | - wrap, | ||
132 | - /* eslint-disable */ | ||
133 | - <Bar move={moveX} size={sizeWidth}></Bar>, | ||
134 | - <Bar vertical move={moveY} size={sizeHeight}></Bar>, | ||
135 | - ]; | ||
136 | - } else { | ||
137 | - nodes = [ | ||
138 | - <div ref="wrap" class={[wrapClass, 'scrollbar__wrap']} style={style}> | ||
139 | - {[view]} | ||
140 | - </div>, | ||
141 | - ]; | ||
142 | - } | ||
143 | - return <div class="scrollbar">{nodes}</div>; | ||
144 | - }; | ||
145 | - }, | ||
146 | -}); |
src/components/Scrollbar/src/bar.ts
0 → 100644
1 | +import { | ||
2 | + defineComponent, | ||
3 | + h, | ||
4 | + computed, | ||
5 | + ref, | ||
6 | + getCurrentInstance, | ||
7 | + onUnmounted, | ||
8 | + inject, | ||
9 | + Ref, | ||
10 | +} from 'vue'; | ||
11 | +import { on, off } from '/@/utils/domUtils'; | ||
12 | + | ||
13 | +import { renderThumbStyle, BAR_MAP } from './util'; | ||
14 | + | ||
15 | +export default defineComponent({ | ||
16 | + name: 'Bar', | ||
17 | + | ||
18 | + props: { | ||
19 | + vertical: Boolean, | ||
20 | + size: String, | ||
21 | + move: Number, | ||
22 | + }, | ||
23 | + | ||
24 | + setup(props) { | ||
25 | + const instance = getCurrentInstance(); | ||
26 | + const thumb = ref<any>(null); | ||
27 | + const wrap = inject('scroll-bar-wrap', {} as Ref<Nullable<HTMLElement>>) as any; | ||
28 | + const bar = computed(() => { | ||
29 | + return BAR_MAP[props.vertical ? 'vertical' : 'horizontal']; | ||
30 | + }); | ||
31 | + const barStore = ref<Indexable>({}); | ||
32 | + const cursorDown = ref<any>(null); | ||
33 | + const clickThumbHandler = (e: any) => { | ||
34 | + // prevent click event of right button | ||
35 | + if (e.ctrlKey || e.button === 2) { | ||
36 | + return; | ||
37 | + } | ||
38 | + startDrag(e); | ||
39 | + barStore.value[bar.value.axis] = | ||
40 | + e.currentTarget[bar.value.offset] - | ||
41 | + (e[bar.value.client] - e.currentTarget.getBoundingClientRect()[bar.value.direction]); | ||
42 | + }; | ||
43 | + | ||
44 | + const clickTrackHandler = (e: any) => { | ||
45 | + const offset = Math.abs( | ||
46 | + e.target.getBoundingClientRect()[bar.value.direction] - e[bar.value.client] | ||
47 | + ); | ||
48 | + const thumbHalf = thumb.value[bar.value.offset] / 2; | ||
49 | + const thumbPositionPercentage = | ||
50 | + ((offset - thumbHalf) * 100) / instance?.vnode.el?.[bar.value.offset]; | ||
51 | + | ||
52 | + wrap.value[bar.value.scroll] = | ||
53 | + (thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100; | ||
54 | + }; | ||
55 | + const startDrag = (e: any) => { | ||
56 | + e.stopImmediatePropagation(); | ||
57 | + cursorDown.value = true; | ||
58 | + on(document, 'mousemove', mouseMoveDocumentHandler); | ||
59 | + on(document, 'mouseup', mouseUpDocumentHandler); | ||
60 | + document.onselectstart = () => false; | ||
61 | + }; | ||
62 | + | ||
63 | + const mouseMoveDocumentHandler = (e: any) => { | ||
64 | + if (cursorDown.value === false) return; | ||
65 | + const prevPage = barStore.value[bar.value.axis]; | ||
66 | + | ||
67 | + if (!prevPage) return; | ||
68 | + | ||
69 | + const offset = | ||
70 | + (instance?.vnode.el?.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) * | ||
71 | + -1; | ||
72 | + const thumbClickPosition = thumb.value[bar.value.offset] - prevPage; | ||
73 | + const thumbPositionPercentage = | ||
74 | + ((offset - thumbClickPosition) * 100) / instance?.vnode.el?.[bar.value.offset]; | ||
75 | + wrap.value[bar.value.scroll] = | ||
76 | + (thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100; | ||
77 | + }; | ||
78 | + | ||
79 | + function mouseUpDocumentHandler() { | ||
80 | + cursorDown.value = false; | ||
81 | + barStore.value[bar.value.axis] = 0; | ||
82 | + off(document, 'mousemove', mouseMoveDocumentHandler); | ||
83 | + document.onselectstart = null; | ||
84 | + } | ||
85 | + | ||
86 | + onUnmounted(() => { | ||
87 | + off(document, 'mouseup', mouseUpDocumentHandler); | ||
88 | + }); | ||
89 | + | ||
90 | + return () => | ||
91 | + h( | ||
92 | + 'div', | ||
93 | + { | ||
94 | + class: ['scrollbar__bar', 'is-' + bar.value.key], | ||
95 | + onMousedown: clickTrackHandler, | ||
96 | + }, | ||
97 | + h('div', { | ||
98 | + ref: thumb, | ||
99 | + class: 'scrollbar__thumb', | ||
100 | + onMousedown: clickThumbHandler, | ||
101 | + style: renderThumbStyle({ | ||
102 | + size: props.size, | ||
103 | + move: props.move, | ||
104 | + bar: bar.value, | ||
105 | + }), | ||
106 | + }) | ||
107 | + ); | ||
108 | + }, | ||
109 | +}); |
src/components/Scrollbar/src/index.less deleted
100644 → 0
1 | -.scrollbar { | ||
2 | - position: relative; | ||
3 | - overflow: hidden; | ||
4 | - | ||
5 | - &__wrap { | ||
6 | - height: 100%; | ||
7 | - overflow: scroll; | ||
8 | - | ||
9 | - &--hidden-default { | ||
10 | - scrollbar-width: none; | ||
11 | - | ||
12 | - &::-webkit-scrollbar { | ||
13 | - width: 0; | ||
14 | - height: 0; | ||
15 | - } | ||
16 | - } | ||
17 | - } | ||
18 | - | ||
19 | - &__thumb { | ||
20 | - position: relative; | ||
21 | - display: block; | ||
22 | - width: 0; | ||
23 | - height: 0; | ||
24 | - cursor: pointer; | ||
25 | - background-color: rgba(144, 147, 153, 0.3); | ||
26 | - border-radius: inherit; | ||
27 | - transition: 0.3s background-color; | ||
28 | - | ||
29 | - &:hover { | ||
30 | - background-color: rgba(144, 147, 153, 0.5); | ||
31 | - } | ||
32 | - } | ||
33 | - | ||
34 | - &__bar { | ||
35 | - position: absolute; | ||
36 | - right: 2px; | ||
37 | - bottom: 2px; | ||
38 | - z-index: 1; | ||
39 | - border-radius: 4px; | ||
40 | - opacity: 0; | ||
41 | - -webkit-transition: opacity 80ms ease; | ||
42 | - transition: opacity 80ms ease; | ||
43 | - | ||
44 | - &.is-vertical { | ||
45 | - top: 2px; | ||
46 | - width: 5px; | ||
47 | - | ||
48 | - & > div { | ||
49 | - width: 100%; | ||
50 | - } | ||
51 | - } | ||
52 | - | ||
53 | - &.is-horizontal { | ||
54 | - left: 2px; | ||
55 | - height: 5px; | ||
56 | - | ||
57 | - & > div { | ||
58 | - height: 100%; | ||
59 | - } | ||
60 | - } | ||
61 | - } | ||
62 | -} | ||
63 | - | ||
64 | -.scrollbar:active > .scrollbar__bar, | ||
65 | -.scrollbar:focus > .scrollbar__bar, | ||
66 | -.scrollbar:hover > .scrollbar__bar { | ||
67 | - opacity: 1; | ||
68 | - transition: opacity 180ms ease; | ||
69 | -} |
src/components/Scrollbar/src/index.vue
0 → 100644
1 | +<template> | ||
2 | + <div class="scrollbar"> | ||
3 | + <div | ||
4 | + ref="wrap" | ||
5 | + :class="[wrapClass, 'scrollbar__wrap', native ? '' : 'scrollbar__wrap--hidden-default']" | ||
6 | + :style="style" | ||
7 | + @scroll="handleScroll" | ||
8 | + > | ||
9 | + <component :is="tag" ref="resize" :class="['scrollbar__view', viewClass]" :style="viewStyle"> | ||
10 | + <slot></slot> | ||
11 | + </component> | ||
12 | + </div> | ||
13 | + <template v-if="!native"> | ||
14 | + <bar :move="moveX" :size="sizeWidth" /> | ||
15 | + <bar vertical :move="moveY" :size="sizeHeight" /> | ||
16 | + </template> | ||
17 | + </div> | ||
18 | +</template> | ||
19 | +<script lang="ts"> | ||
20 | + import { addResizeListener, removeResizeListener } from '/@/utils/event/resizeEvent'; | ||
21 | + | ||
22 | + import { toObject } from './util'; | ||
23 | + import { | ||
24 | + defineComponent, | ||
25 | + ref, | ||
26 | + onMounted, | ||
27 | + onBeforeUnmount, | ||
28 | + nextTick, | ||
29 | + provide, | ||
30 | + computed, | ||
31 | + } from 'vue'; | ||
32 | + import Bar from './bar'; | ||
33 | + | ||
34 | + export default defineComponent({ | ||
35 | + name: 'Scrollbar', | ||
36 | + components: { Bar }, | ||
37 | + props: { | ||
38 | + native: { | ||
39 | + type: Boolean, | ||
40 | + default: false, | ||
41 | + }, | ||
42 | + wrapStyle: { | ||
43 | + type: [String, Array], | ||
44 | + default: '', | ||
45 | + }, | ||
46 | + wrapClass: { | ||
47 | + type: [String, Array], | ||
48 | + default: '', | ||
49 | + }, | ||
50 | + viewClass: { | ||
51 | + type: [String, Array], | ||
52 | + default: '', | ||
53 | + }, | ||
54 | + viewStyle: { | ||
55 | + type: [String, Array], | ||
56 | + default: '', | ||
57 | + }, | ||
58 | + noresize: Boolean, // 如果 container 尺寸不会发生变化,最好设置它可以优化性能 | ||
59 | + tag: { | ||
60 | + type: String, | ||
61 | + default: 'div', | ||
62 | + }, | ||
63 | + }, | ||
64 | + setup(props) { | ||
65 | + const sizeWidth = ref('0'); | ||
66 | + const sizeHeight = ref('0'); | ||
67 | + const moveX = ref(0); | ||
68 | + const moveY = ref(0); | ||
69 | + const wrap = ref<any>(null); | ||
70 | + const resize = ref<any>(null); | ||
71 | + | ||
72 | + provide('scroll-bar-wrap', wrap); | ||
73 | + | ||
74 | + const handleScroll = () => { | ||
75 | + if (!props.native) { | ||
76 | + moveY.value = (wrap.value.scrollTop * 100) / wrap.value.clientHeight; | ||
77 | + moveX.value = (wrap.value.scrollLeft * 100) / wrap.value.clientWidth; | ||
78 | + } | ||
79 | + }; | ||
80 | + | ||
81 | + const update = () => { | ||
82 | + if (!wrap.value) return; | ||
83 | + | ||
84 | + const heightPercentage = (wrap.value.clientHeight * 100) / wrap.value.scrollHeight; | ||
85 | + const widthPercentage = (wrap.value.clientWidth * 100) / wrap.value.scrollWidth; | ||
86 | + | ||
87 | + sizeHeight.value = heightPercentage < 100 ? heightPercentage + '%' : ''; | ||
88 | + sizeWidth.value = widthPercentage < 100 ? widthPercentage + '%' : ''; | ||
89 | + }; | ||
90 | + | ||
91 | + onMounted(() => { | ||
92 | + if (props.native) return; | ||
93 | + nextTick(update); | ||
94 | + !props.noresize && addResizeListener(resize.value, update); | ||
95 | + }); | ||
96 | + | ||
97 | + onBeforeUnmount(() => { | ||
98 | + if (props.native) return; | ||
99 | + !props.noresize && removeResizeListener(resize.value, update); | ||
100 | + }); | ||
101 | + const style = computed(() => { | ||
102 | + let style: any = props.wrapStyle; | ||
103 | + if (Array.isArray(props.wrapStyle)) { | ||
104 | + style = toObject(props.wrapStyle); | ||
105 | + } | ||
106 | + return style; | ||
107 | + }); | ||
108 | + return { | ||
109 | + moveX, | ||
110 | + moveY, | ||
111 | + sizeWidth, | ||
112 | + sizeHeight, | ||
113 | + style, | ||
114 | + wrap, | ||
115 | + resize, | ||
116 | + update, | ||
117 | + handleScroll, | ||
118 | + }; | ||
119 | + }, | ||
120 | + }); | ||
121 | +</script> | ||
122 | +<style lang="less"> | ||
123 | + .scrollbar { | ||
124 | + position: relative; | ||
125 | + height: 100%; | ||
126 | + overflow: hidden; | ||
127 | + | ||
128 | + &__wrap { | ||
129 | + height: 100%; | ||
130 | + overflow: scroll; | ||
131 | + | ||
132 | + &--hidden-default { | ||
133 | + scrollbar-width: none; | ||
134 | + | ||
135 | + &::-webkit-scrollbar { | ||
136 | + display: none; | ||
137 | + width: 0; | ||
138 | + height: 0; | ||
139 | + opacity: 0; | ||
140 | + } | ||
141 | + } | ||
142 | + } | ||
143 | + | ||
144 | + &__thumb { | ||
145 | + position: relative; | ||
146 | + display: block; | ||
147 | + width: 0; | ||
148 | + height: 0; | ||
149 | + cursor: pointer; | ||
150 | + background-color: rgba(144, 147, 153, 0.3); | ||
151 | + border-radius: inherit; | ||
152 | + transition: 0.3s background-color; | ||
153 | + | ||
154 | + &:hover { | ||
155 | + background-color: rgba(144, 147, 153, 0.5); | ||
156 | + } | ||
157 | + } | ||
158 | + | ||
159 | + &__bar { | ||
160 | + position: absolute; | ||
161 | + right: 2px; | ||
162 | + bottom: 2px; | ||
163 | + z-index: 1; | ||
164 | + border-radius: 4px; | ||
165 | + opacity: 0; | ||
166 | + -webkit-transition: opacity 80ms ease; | ||
167 | + transition: opacity 80ms ease; | ||
168 | + | ||
169 | + &.is-vertical { | ||
170 | + top: 2px; | ||
171 | + width: 6px; | ||
172 | + | ||
173 | + & > div { | ||
174 | + width: 100%; | ||
175 | + } | ||
176 | + } | ||
177 | + | ||
178 | + &.is-horizontal { | ||
179 | + left: 2px; | ||
180 | + height: 6px; | ||
181 | + | ||
182 | + & > div { | ||
183 | + height: 100%; | ||
184 | + } | ||
185 | + } | ||
186 | + } | ||
187 | + } | ||
188 | + | ||
189 | + .scrollbar:active > .scrollbar__bar, | ||
190 | + .scrollbar:focus > .scrollbar__bar, | ||
191 | + .scrollbar:hover > .scrollbar__bar { | ||
192 | + opacity: 1; | ||
193 | + transition: opacity 340ms ease-out; | ||
194 | + } | ||
195 | +</style> |
src/components/Transition/src/ExpandTransition.vue
@@ -59,6 +59,8 @@ | @@ -59,6 +59,8 @@ | ||
59 | if (el.scrollHeight !== 0) { | 59 | if (el.scrollHeight !== 0) { |
60 | // for safari: add class after set height, or it will jump to zero height suddenly, weired | 60 | // for safari: add class after set height, or it will jump to zero height suddenly, weired |
61 | addClass(el, 'collapse-transition'); | 61 | addClass(el, 'collapse-transition'); |
62 | + // in vue3.0.4, transitionProperty is set 'none' to avoid 'v-leave-from' issue | ||
63 | + el.style.transitionProperty = 'height'; | ||
62 | el.style.height = 0; | 64 | el.style.height = 0; |
63 | el.style.paddingTop = 0; | 65 | el.style.paddingTop = 0; |
64 | el.style.paddingBottom = 0; | 66 | el.style.paddingBottom = 0; |
src/locales/types.ts
src/router/menus/modules/demo/comp.ts
@@ -180,6 +180,16 @@ const menu: MenuModule = { | @@ -180,6 +180,16 @@ const menu: MenuModule = { | ||
180 | { | 180 | { |
181 | path: 'markdown', | 181 | path: 'markdown', |
182 | name: t('routes.demo.editor.markdown'), | 182 | name: t('routes.demo.editor.markdown'), |
183 | + children: [ | ||
184 | + { | ||
185 | + path: 'index', | ||
186 | + name: t('routes.demo.editor.tinymceBasic'), | ||
187 | + }, | ||
188 | + { | ||
189 | + path: 'editor', | ||
190 | + name: t('routes.demo.editor.tinymceForm'), | ||
191 | + }, | ||
192 | + ], | ||
183 | }, | 193 | }, |
184 | { | 194 | { |
185 | path: 'tinymce', | 195 | path: 'tinymce', |
src/router/routes/modules/demo/comp.ts
@@ -288,12 +288,32 @@ const comp: AppRouteModule = { | @@ -288,12 +288,32 @@ const comp: AppRouteModule = { | ||
288 | children: [ | 288 | children: [ |
289 | { | 289 | { |
290 | path: 'markdown', | 290 | path: 'markdown', |
291 | + component: getParentLayout('MarkdownDemo'), | ||
291 | name: 'MarkdownDemo', | 292 | name: 'MarkdownDemo', |
292 | - component: () => import('/@/views/demo/editor/Markdown.vue'), | ||
293 | meta: { | 293 | meta: { |
294 | title: t('routes.demo.editor.markdown'), | 294 | title: t('routes.demo.editor.markdown'), |
295 | }, | 295 | }, |
296 | + redirect: '/comp/editor/markdown/index', | ||
297 | + children: [ | ||
298 | + { | ||
299 | + path: 'index', | ||
300 | + name: 'MarkDownBasicDemo', | ||
301 | + component: () => import('/@/views/demo/editor/markdown/index.vue'), | ||
302 | + meta: { | ||
303 | + title: t('routes.demo.editor.tinymceBasic'), | ||
304 | + }, | ||
305 | + }, | ||
306 | + { | ||
307 | + path: 'editor', | ||
308 | + name: 'MarkDownFormDemo', | ||
309 | + component: () => import('/@/views/demo/editor/markdown/Editor.vue'), | ||
310 | + meta: { | ||
311 | + title: t('routes.demo.editor.tinymceForm'), | ||
312 | + }, | ||
313 | + }, | ||
314 | + ], | ||
296 | }, | 315 | }, |
316 | + | ||
297 | { | 317 | { |
298 | path: 'tinymce', | 318 | path: 'tinymce', |
299 | component: getParentLayout('TinymceDemo'), | 319 | component: getParentLayout('TinymceDemo'), |
src/views/demo/editor/markdown/Editor.vue
0 → 100644
1 | +<template> | ||
2 | + <div class="m-4"> | ||
3 | + <CollapseContainer title="MarkDown表单"> | ||
4 | + <BasicForm | ||
5 | + :labelWidth="100" | ||
6 | + :schemas="schemas" | ||
7 | + :actionColOptions="{ span: 24 }" | ||
8 | + @submit="handleSubmit" | ||
9 | + > | ||
10 | + </BasicForm> | ||
11 | + </CollapseContainer> | ||
12 | + </div> | ||
13 | +</template> | ||
14 | +<script lang="ts"> | ||
15 | + import { defineComponent, h } from 'vue'; | ||
16 | + import { BasicForm, FormSchema } from '/@/components/Form/index'; | ||
17 | + import { CollapseContainer } from '/@/components/Container/index'; | ||
18 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
19 | + import { MarkDown } from '/@/components/Markdown'; | ||
20 | + | ||
21 | + const schemas: FormSchema[] = [ | ||
22 | + { | ||
23 | + field: 'title', | ||
24 | + component: 'Input', | ||
25 | + label: 'title', | ||
26 | + defaultValue: '标题', | ||
27 | + rules: [{ required: true }], | ||
28 | + }, | ||
29 | + { | ||
30 | + field: 'markdown', | ||
31 | + component: 'Input', | ||
32 | + label: 'markdown', | ||
33 | + defaultValue: 'defaultValue', | ||
34 | + rules: [{ required: true, trigger: 'blur' }], | ||
35 | + render: ({ model, field }) => { | ||
36 | + return h(MarkDown, { | ||
37 | + value: model[field], | ||
38 | + onChange: (value: string) => { | ||
39 | + model[field] = value; | ||
40 | + }, | ||
41 | + }); | ||
42 | + }, | ||
43 | + }, | ||
44 | + ]; | ||
45 | + export default defineComponent({ | ||
46 | + components: { BasicForm, CollapseContainer }, | ||
47 | + setup() { | ||
48 | + const { createMessage } = useMessage(); | ||
49 | + | ||
50 | + return { | ||
51 | + schemas, | ||
52 | + handleSubmit: (values: any) => { | ||
53 | + createMessage.success('click search,values:' + JSON.stringify(values)); | ||
54 | + }, | ||
55 | + }; | ||
56 | + }, | ||
57 | + }); | ||
58 | +</script> |
src/views/demo/editor/Markdown.vue renamed to src/views/demo/editor/markdown/index.vue
1 | <template> | 1 | <template> |
2 | <div class="p-4"> | 2 | <div class="p-4"> |
3 | <a-button @click="toggleTheme" class="mb-2" type="primary">黑暗主题</a-button> | 3 | <a-button @click="toggleTheme" class="mb-2" type="primary">黑暗主题</a-button> |
4 | - <MarkDown v-model:value="value" ref="markDownRef" /> | 4 | + <MarkDown :value="value" @change="handleChange" ref="markDownRef" /> |
5 | </div> | 5 | </div> |
6 | </template> | 6 | </template> |
7 | <script lang="ts"> | 7 | <script lang="ts"> |
@@ -23,10 +23,16 @@ | @@ -23,10 +23,16 @@ | ||
23 | const vditor = markDown.getVditor(); | 23 | const vditor = markDown.getVditor(); |
24 | vditor.setTheme('dark'); | 24 | vditor.setTheme('dark'); |
25 | } | 25 | } |
26 | + | ||
27 | + function handleChange(v: string) { | ||
28 | + valueRef.value = v; | ||
29 | + } | ||
30 | + | ||
26 | return { | 31 | return { |
27 | value: valueRef, | 32 | value: valueRef, |
28 | toggleTheme, | 33 | toggleTheme, |
29 | markDownRef, | 34 | markDownRef, |
35 | + handleChange, | ||
30 | }; | 36 | }; |
31 | }, | 37 | }, |
32 | }); | 38 | }); |
tsconfig.json
@@ -15,16 +15,11 @@ | @@ -15,16 +15,11 @@ | ||
15 | "noUnusedLocals": true, | 15 | "noUnusedLocals": true, |
16 | "noUnusedParameters": true, | 16 | "noUnusedParameters": true, |
17 | "experimentalDecorators": true, | 17 | "experimentalDecorators": true, |
18 | - "lib": [ | ||
19 | - "dom", | ||
20 | - "esnext" | ||
21 | - ], | 18 | + "lib": ["dom", "esnext"], |
22 | "incremental": true, | 19 | "incremental": true, |
23 | "skipLibCheck": true, | 20 | "skipLibCheck": true, |
24 | "paths": { | 21 | "paths": { |
25 | - "/@/*": [ | ||
26 | - "src/*" | ||
27 | - ] | 22 | + "/@/*": ["src/*"] |
28 | } | 23 | } |
29 | }, | 24 | }, |
30 | "plugins": [ | 25 | "plugins": [ |
@@ -32,15 +27,6 @@ | @@ -32,15 +27,6 @@ | ||
32 | "name": "@vuedx/typescript-plugin-vue" | 27 | "name": "@vuedx/typescript-plugin-vue" |
33 | } | 28 | } |
34 | ], | 29 | ], |
35 | - "include": [ | ||
36 | - "src/**/*.ts", | ||
37 | - "src/**/*.d.ts", | ||
38 | - "src/**/*.tsx", | ||
39 | - "src/**/*.vue" | ||
40 | - ], | ||
41 | - "exclude": [ | ||
42 | - "node_modules", | ||
43 | - "dist", | ||
44 | - "**/*.js" | ||
45 | - ] | 30 | + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], |
31 | + "exclude": ["node_modules", "dist", "**/*.js"] | ||
46 | } | 32 | } |