Skip to content

Commit

Permalink
Merge pull request #1729 from DevCloudFE/dev
Browse files Browse the repository at this point in the history
Update main from dev
  • Loading branch information
GreatZPP authored Sep 4, 2023
2 parents e50a3c8 + 32b174a commit f5ec1a0
Show file tree
Hide file tree
Showing 24 changed files with 498 additions and 85 deletions.
3 changes: 2 additions & 1 deletion packages/devui-vue/devui/editor-md/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { App } from 'vue';
import EditorMd from './src/editor-md';
import MdRender from './src/components/md-render';
export * from './src/editor-md-types';
export * from './src/plugins/checkbox';

export { EditorMd, MdRender };

Expand All @@ -12,5 +13,5 @@ export default {
install(app: App): void {
app.component(EditorMd.name, EditorMd);
app.component(MdRender.name, MdRender);
}
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useEditorMdRender, useMdRenderWatcher } from '../composables/use-editor
export default defineComponent({
name: 'DMdRender',
props: mdRenderProps,
emits: ['mdRenderChange', 'mdCheckedEvent'],
emits: ['mdRenderChange', 'checkedChange'],
setup(props: MdRenderProps, ctx: SetupContext) {
const { previewRef, renderService, onPreviewClick, setContainerContent } = useEditorMdRender(props, ctx);
useMdRenderWatcher(props, renderService, setContainerContent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,18 @@ export default defineComponent({
const editorIns = getEditorIns();
if (config.id === 'fullscreen') {
showFullscreen.value = !showFullscreen.value;
} else {
config.handler?.(editorIns, config.params);
if (window) {
const event = new Event('resize');
window.dispatchEvent(event);
}
}
config.handler?.(editorIns, config.params);
};

return () => (
<>
{config.type === 'button' && (
<Tooltip position={['top', 'bottom']} content={getTooltipContent(config.name, config.shortKey)}>
<Tooltip position={['top', 'bottom']} content={getTooltipContent(config.name, config.shortKey)} hide-after={1000}>
<span
class="md-toolbar-item"
onClick={onToolbarItemClick}
Expand All @@ -50,7 +53,7 @@ export default defineComponent({
{{
default: () => (
<span>
<Tooltip position={['top']} content={getTooltipContent(config.name)}>
<Tooltip position={showFullscreen.value ? ['right'] : ['top']} content={getTooltipContent(config.name)} hide-after={1000}>
<span class="md-toolbar-item" onClick={() => config.handler?.()} innerHTML={config.icon}></span>
</Tooltip>
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export function useEditorMdRender(props: MdRenderProps, ctx: SetupContext) {
const result = previewRef.value.querySelectorAll('input');
const index = [...result].filter((el: any) => el.type === 'checkbox').findIndex((item: any) => item === e.target);
const checkContent = setChecked(e.target.checked, index);
ctx.emit('mdCheckedEvent', checkContent);
ctx.emit('checkedChange', checkContent);
}
};

Expand Down
103 changes: 90 additions & 13 deletions packages/devui-vue/devui/editor-md/src/composables/use-editor-md.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { EditorMdProps, Mode } from '../editor-md-types';
import { DEFAULT_TOOLBARS } from '../toolbar-config';
import { parseHTMLStringToDomList } from '../utils';
import { refreshEditorCursor, _enforceMaxLength } from './helper';
import { throttle } from 'lodash';

export function useEditorMd(props: EditorMdProps, ctx: SetupContext) {
const {
Expand All @@ -24,17 +25,22 @@ export function useEditorMd(props: EditorMdProps, ctx: SetupContext) {
const toolbars = reactive(cloneDeep(DEFAULT_TOOLBARS));
const editorRef = ref();
const renderRef = ref();
const overlayRef = ref();
const cursorRef = ref();
const isHintShow = ref();
const previewHtmlList: Ref<any[]> = ref([]);
let editorIns: any;
const hintShow = false;
const isHintLoading = false;
let canPreviewScrollView = false;
const hintList: any[] = [];

/* 快速提示 */
let hintList: any[] = [];
let activeIndex = -1;
let cursorHint = '';
let cursorHintEnd = -1;
let cursorHintStart = -1;
let prefix: any;
let hintShow = false;

let CodeMirror: any;
const prefixes = computed(() => {
const result: string[] = [];
Expand Down Expand Up @@ -102,7 +108,7 @@ export function useEditorMd(props: EditorMdProps, ctx: SetupContext) {
};

const onChecked = (e: string) => {
ctx.emit('mdCheckedEvent', e);
ctx.emit('checkedChange', e);
};

const scrollToFocusItem = () => {
Expand Down Expand Up @@ -144,6 +150,53 @@ export function useEditorMd(props: EditorMdProps, ctx: SetupContext) {
activeIndex = -1;
};

let timer: any;
const attachOverlay = () => {
timer = setTimeout(() => {
cursorRef.value = editorRef.value?.parentNode.querySelector('.CodeMirror-cursor') || undefined;
overlayRef.value.updatePosition();
isHintShow.value = true;
hintShow = true;
});
};

const hideHint = () => {
clearTimeout(timer);
isHintShow.value = false;
};

const showHint = () => {
if (hintShow) {
hideHint();
}
attachOverlay();
};

const getHintList = () => {
let handler;
if (typeof hintConfig.value[prefix] === 'function') {
handler = hintConfig.value[prefix];
} else if (hintConfig.value[prefix] && typeof hintConfig.value[prefix].handler === 'function') {
handler = hintConfig.value[prefix].handler;
}

const callback = (replaceText: string) => {
const cursor = editorIns.getCursor();
const endCh = cursorHintEnd;
const startCh = cursorHintStart;
if (editorIns.getLine(cursor.line).length === cursor.ch) {
editorIns.replaceRange(replaceText + ' ', { line: cursor.line, ch: startCh }, { line: cursor.line, ch: endCh });
} else {
editorIns.replaceRange(replaceText, { line: cursor.line, ch: startCh }, { line: cursor.line, ch: endCh });
editorIns.setCursor(cursor.line, editorIns.getCursor().ch + 1);
}
editorIns.focus();
hideHint();
};

handler && handler({ prefix, cursorHint, callback });
};

const cursorActivityHandler = () => {
const cursor = editorIns.getCursor();
let i = prefixes.value.length;
Expand All @@ -153,18 +206,18 @@ export function useEditorMd(props: EditorMdProps, ctx: SetupContext) {
if (selection) {
return;
}
let prefix = '';
let nowPrefix = '';
let hint = '';
while (i >= 1) {
i--;
prefix = prefixes.value[i];
const startPos = value.lastIndexOf(prefix, cursor.ch);
nowPrefix = prefixes.value[i];
const startPos = value.lastIndexOf(nowPrefix, cursor.ch);
const endPos = value.indexOf(' ', cursor.ch) > -1 ? value.indexOf(' ', cursor.ch) : value.length;
hint = value.slice(startPos, cursor.ch);
if (
(startPos > 0 && value[startPos - 1] !== ' ') ||
startPos < 0 ||
!hint.includes(prefix) ||
!hint.includes(nowPrefix) ||
hint.endsWith(' ') ||
isImgRegx.test(hint)
) {
Expand All @@ -179,10 +232,20 @@ export function useEditorMd(props: EditorMdProps, ctx: SetupContext) {
break;
}
}
// todo
// if (cursorHintStart > -1 && hint[0]) {
// currentHintConfig
// }
if (cursorHintStart > -1 && hint[0]) {
const spacePosition = value.lastIndexOf(' ', cursor.ch);
if (spacePosition > cursorHintStart) {
return;
}
/* cursor元素将动态变更,设置settimeout保持其可以获取到值 */
setTimeout(() => {
showHint();
getHintList();
});
} else {
hintList = [];
hideHint();
}
};

const onChange = debounce(
Expand Down Expand Up @@ -228,9 +291,20 @@ export function useEditorMd(props: EditorMdProps, ctx: SetupContext) {
}
}

editorIns.setOption('extraKeys', shortKeys);
editorIns.setOption(
'extraKeys',
Object.assign({
Esc: () => {
hideHint();
},
}),
shortKeys
);

editorIns.on('beforeChange', _enforceMaxLength);

editorIns.on('cursorActivity', throttle(cursorActivityHandler, ((hintConfig.value && hintConfig.value.throttleTime) as number) || 300));

editorIns.setSize('auto', '100%');
refreshEditorCursor();
editorIns.setCursor(editorIns.lineCount(), 0);
Expand Down Expand Up @@ -322,9 +396,12 @@ export function useEditorMd(props: EditorMdProps, ctx: SetupContext) {

return {
editorRef,
overlayRef,
cursorRef,
renderRef,
toolbars,
previewHtmlList,
isHintShow,
getEditorIns,
onPaste,
previewContentChange,
Expand Down
7 changes: 6 additions & 1 deletion packages/devui-vue/devui/editor-md/src/editor-md-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ const commonProps = {
},
};

export interface HintConfigItem {
handler: (objs: { callback: (replaceText: string) => void; cursorHint: string; prefix: string }) => void;
}

export const editorMdProps = {
...commonProps,
modelValue: {
Expand Down Expand Up @@ -115,7 +119,8 @@ export const editorMdProps = {
default: 10,
},
hintConfig: {
type: Object as PropType<Record<string, any>>,
type: Object as PropType<Record<string, HintConfigItem | number>>,
default: {},
},
customHintReplaceFn: {
type: Function as PropType<(prefix: string, row: any) => string>,
Expand Down
4 changes: 4 additions & 0 deletions packages/devui-vue/devui/editor-md/src/editor-md.scss
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ $font-family: helvetica, arial, 'PingFang', 'Microsoft YaHei', 'Hiragino Sans GB
.CodeMirror-empty pre.CodeMirror-placeholder.CodeMirror-line-like {
color: $devui-line;
}

input[type='checkbox'] + label {
margin-left: 6px;
}
}

.dp-editor-md-preview-container.dp-md-view {
Expand Down
19 changes: 16 additions & 3 deletions packages/devui-vue/devui/editor-md/src/editor-md.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import Toolbar from './components/toolbar';
import MdRender from './components/md-render';
import { locale } from './utils';
import './editor-md.scss';
import { FlexibleOverlay } from '../../overlay';

export default defineComponent({
name: 'DEditorMd',
props: editorMdProps,
emits: ['update:modelValue', 'mdCheckedEvent', 'selectHint', 'afterEditorInit', 'contentChange', 'previewContentChange', 'imageUpload'],
emits: ['update:modelValue', 'checkedChange', 'selectHint', 'afterEditorInit', 'contentChange', 'previewContentChange', 'imageUpload'],
setup(props: EditorMdProps, ctx: SetupContext) {
const {
mode,
Expand All @@ -36,7 +37,10 @@ export default defineComponent({

const {
editorRef,
overlayRef,
cursorRef,
renderRef,
isHintShow,
toolbars,
previewHtmlList,
onPaste,
Expand Down Expand Up @@ -64,7 +68,8 @@ export default defineComponent({
class={[
'dp-md-container',
{ 'dp-md-readonly': mode.value === 'readonly', 'dp-md-editonly': mode.value === 'editonly', 'dp-md-dark': isDarkMode.value },
]} onPaste={onPaste}>
]}
onPaste={onPaste}>
<div class="dp-md-toolbar-container">
<Toolbar />
</div>
Expand All @@ -75,6 +80,14 @@ export default defineComponent({
<textarea ref={editorRef} placeholder={placeholder.value}>
{modelValue.value}
</textarea>
<FlexibleOverlay
ref={overlayRef}
v-model={isHintShow.value}
origin={cursorRef.value || undefined}
align="start"
position={['bottom-start']}>
{ctx.slots?.hintTemplate?.()}
</FlexibleOverlay>
{Boolean(maxlength?.value) && (
<div class="dp-md-count">
{modelValue.value.length || 0}/{maxlength.value}
Expand All @@ -94,7 +107,7 @@ export default defineComponent({
disable-render
md-plugins={mdPlugins.value}
onMdRenderChange={previewContentChange}
onMdCheckedEvent={onChecked}
onCheckedChange={onChecked}
onScroll={onPreviewScroll}
onMouseover={onPreviewMouseover}
onMouseout={onPreviewMouseout}>
Expand Down
Loading

0 comments on commit f5ec1a0

Please sign in to comment.