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 | 2 | <div class="markdown" ref="wrapRef" /> |
3 | 3 | </template> |
4 | 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 | 15 | import Vditor from 'vditor'; |
7 | 16 | import 'vditor/dist/index.css'; |
8 | 17 | |
9 | 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 | 22 | export default defineComponent({ |
12 | - emits: ['update:value'], | |
23 | + emits: ['change'], | |
13 | 24 | props: { |
14 | 25 | height: propTypes.number.def(360), |
15 | 26 | value: propTypes.string.def(''), |
... | ... | @@ -19,17 +30,42 @@ |
19 | 30 | const vditorRef = ref<Nullable<Vditor>>(null); |
20 | 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 | 53 | function init() { |
23 | 54 | const wrapEl = unref(wrapRef); |
24 | 55 | if (!wrapEl) return; |
25 | 56 | const bindValue = { ...attrs, ...props }; |
26 | 57 | vditorRef.value = new Vditor(wrapEl, { |
58 | + lang: unref(getCurrentLang), | |
27 | 59 | mode: 'sv', |
28 | 60 | preview: { |
29 | 61 | actions: [], |
30 | 62 | }, |
31 | 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 | 70 | ...bindValue, |
35 | 71 | cache: { |
... | ... | @@ -39,14 +75,20 @@ |
39 | 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 | 93 | onMounted(() => { |
52 | 94 | nextTick(() => { |
... | ... |
src/components/Scrollbar/index.ts
... | ... | @@ -5,7 +5,7 @@ |
5 | 5 | import { withInstall } from '../util'; |
6 | 6 | |
7 | 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 | 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 | 59 | if (el.scrollHeight !== 0) { |
60 | 60 | // for safari: add class after set height, or it will jump to zero height suddenly, weired |
61 | 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 | 64 | el.style.height = 0; |
63 | 65 | el.style.paddingTop = 0; |
64 | 66 | el.style.paddingBottom = 0; |
... | ... |
src/locales/types.ts
src/router/menus/modules/demo/comp.ts
... | ... | @@ -180,6 +180,16 @@ const menu: MenuModule = { |
180 | 180 | { |
181 | 181 | path: 'markdown', |
182 | 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 | 195 | path: 'tinymce', |
... | ... |
src/router/routes/modules/demo/comp.ts
... | ... | @@ -288,12 +288,32 @@ const comp: AppRouteModule = { |
288 | 288 | children: [ |
289 | 289 | { |
290 | 290 | path: 'markdown', |
291 | + component: getParentLayout('MarkdownDemo'), | |
291 | 292 | name: 'MarkdownDemo', |
292 | - component: () => import('/@/views/demo/editor/Markdown.vue'), | |
293 | 293 | meta: { |
294 | 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 | 318 | path: 'tinymce', |
299 | 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 | 1 | <template> |
2 | 2 | <div class="p-4"> |
3 | 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 | 5 | </div> |
6 | 6 | </template> |
7 | 7 | <script lang="ts"> |
... | ... | @@ -23,10 +23,16 @@ |
23 | 23 | const vditor = markDown.getVditor(); |
24 | 24 | vditor.setTheme('dark'); |
25 | 25 | } |
26 | + | |
27 | + function handleChange(v: string) { | |
28 | + valueRef.value = v; | |
29 | + } | |
30 | + | |
26 | 31 | return { |
27 | 32 | value: valueRef, |
28 | 33 | toggleTheme, |
29 | 34 | markDownRef, |
35 | + handleChange, | |
30 | 36 | }; |
31 | 37 | }, |
32 | 38 | }); |
... | ... |
tsconfig.json
... | ... | @@ -15,16 +15,11 @@ |
15 | 15 | "noUnusedLocals": true, |
16 | 16 | "noUnusedParameters": true, |
17 | 17 | "experimentalDecorators": true, |
18 | - "lib": [ | |
19 | - "dom", | |
20 | - "esnext" | |
21 | - ], | |
18 | + "lib": ["dom", "esnext"], | |
22 | 19 | "incremental": true, |
23 | 20 | "skipLibCheck": true, |
24 | 21 | "paths": { |
25 | - "/@/*": [ | |
26 | - "src/*" | |
27 | - ] | |
22 | + "/@/*": ["src/*"] | |
28 | 23 | } |
29 | 24 | }, |
30 | 25 | "plugins": [ |
... | ... | @@ -32,15 +27,6 @@ |
32 | 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 | } |
... | ... |