forked from opentiny/tiny-vue
chore: merge editor-dev to dev (#403)
* feat(rich-text-editor): add new component rich-text-editor (#346) * feat(rich-text-editor): add new components rich-text-editor --------- Co-authored-by: 常浩-BJS21325 <changhao01@youdao> * style(rich-text-editor): adjust style, fit Highlight, Default, Disabled (#357) * style(rich-text-editor): adjust style, fit Highlight, Default, Disabled * fix(rich-text-editor): Space format changed from four to two * fix(rich-text-editor): add svg file and support high light and disabled and default --------- Co-authored-by: 常浩-BJS21325 <changhao01@youdao> * feat(rich-text-editor): add text alignment, task list, table interaction (#372) * feat(rich-text-editor): add text alignment, task list, table interaction --------- Co-authored-by: 常浩-BJS21325 <changhao01@youdao> * perf(rich-text-editor): split pure function from vue.ts to index.ts (#395) Co-authored-by: 常浩-BJS21325 <changhao01@youdao> * fix(rich-text-editor): fix conflict --------- Co-authored-by: Caesar-ch <74941512+Caesar-ch@users.noreply.github.com> Co-authored-by: 常浩-BJS21325 <changhao01@youdao>
This commit is contained in:
parent
59c73952bd
commit
c0cd7cdb4e
|
@ -0,0 +1,81 @@
|
|||
export const handleChange = (editor) => {
|
||||
return (event) => {
|
||||
const file = event.target.files[0]
|
||||
if (!file.type.match("image.*")) {
|
||||
console.log("请选择图片文件!")
|
||||
return
|
||||
}
|
||||
const reader = new FileReader()
|
||||
reader.onload = function (e) {
|
||||
editor.value.chain().focus().setImage({ src: e.target?.result }).run()
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
}
|
||||
export const setLink = (editor) => {
|
||||
return () => {
|
||||
const previousUrl = editor.value.getAttributes('link').href
|
||||
const url = window.prompt('URL', previousUrl)
|
||||
if (url === null) {
|
||||
return
|
||||
}
|
||||
if (url === '') {
|
||||
editor.value
|
||||
.chain()
|
||||
.focus()
|
||||
.extendMarkRange('link')
|
||||
.unsetLink()
|
||||
.run()
|
||||
return
|
||||
}
|
||||
editor.value
|
||||
.chain()
|
||||
.focus()
|
||||
.extendMarkRange('link')
|
||||
.setLink({ href: url })
|
||||
.run()
|
||||
}
|
||||
}
|
||||
// table 处理逻辑
|
||||
export const handleMove = (state, box) => {
|
||||
return (e) => {
|
||||
let { x, y } = box.value.getBoundingClientRect()
|
||||
state.flagX = Math.ceil((e.x - x) / 30) // 后期改变30就可以
|
||||
state.flagY = Math.ceil((e.y - y) / 30)
|
||||
}
|
||||
}
|
||||
export const handleClickOutside = (state, box) => {
|
||||
return (e) => {
|
||||
if (!box.value?.contains(e.target)) {
|
||||
state.isShow = false
|
||||
removeClickOutside(state, box)()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
export const removeClickOutside = (state, box) => {
|
||||
return () => {
|
||||
window.removeEventListener('click', handleClickOutside(state, box))
|
||||
}
|
||||
}
|
||||
export const handleClick = (state, box) => {
|
||||
return (e) => {
|
||||
e.stopPropagation();
|
||||
if (state.isShow) {
|
||||
if (state.flagX && state.flagY) {
|
||||
state.editor.chain().focus().insertTable({ rows: state.flagX, cols: state.flagY, withHeaderRow: true }).run()
|
||||
}
|
||||
state.flagX = 0
|
||||
state.flagY = 0
|
||||
removeClickOutside(state, box)()
|
||||
} else {
|
||||
window.addEventListener('click', handleClickOutside(state, box))
|
||||
}
|
||||
state.isShow = !state.isShow
|
||||
}
|
||||
}
|
||||
// bubble菜单
|
||||
export const shouldShow = ({ editor, view, state, oldState, from, to }) => {
|
||||
// 仅在无序列表选中的时候才显示 气泡菜单
|
||||
return editor.isActive("table");
|
||||
};
|
|
@ -1,3 +1,4 @@
|
|||
import { handleChange, setLink, handleMove, handleClickOutside, removeClickOutside, handleClick, shouldShow } from './index'
|
||||
export const api = ['state', 'setLink', 'handleChange', 'box', 'handleMove', 'handleClickOutside', 'removeClickOutside', 'handleClick', 'shouldShow']
|
||||
export const renderless = (
|
||||
props,
|
||||
|
@ -47,74 +48,6 @@ export const renderless = (
|
|||
editable: true,
|
||||
injectCSS: false,
|
||||
})
|
||||
const handleChange = (event) => {
|
||||
const file = event.target.files[0]
|
||||
if (!file.type.match("image.*")) {
|
||||
console.log("请选择图片文件!")
|
||||
return
|
||||
}
|
||||
const reader = new FileReader()
|
||||
reader.onload = function (e) {
|
||||
editor.value.chain().focus().setImage({ src: e.target?.result }).run()
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
const setLink = () => {
|
||||
const previousUrl = editor.value.getAttributes('link').href
|
||||
const url = window.prompt('URL', previousUrl)
|
||||
if (url === null) {
|
||||
return
|
||||
}
|
||||
if (url === '') {
|
||||
editor.value
|
||||
.chain()
|
||||
.focus()
|
||||
.extendMarkRange('link')
|
||||
.unsetLink()
|
||||
.run()
|
||||
return
|
||||
}
|
||||
editor.value
|
||||
.chain()
|
||||
.focus()
|
||||
.extendMarkRange('link')
|
||||
.setLink({ href: url })
|
||||
.run()
|
||||
}
|
||||
// table 处理逻辑
|
||||
const handleMove = (e) => {
|
||||
let { x, y } = box.value.getBoundingClientRect()
|
||||
state.flagX = Math.ceil((e.x - x) / 30) // 后期改变30就可以
|
||||
state.flagY = Math.ceil((e.y - y) / 30)
|
||||
}
|
||||
const handleClickOutside = (e) => {
|
||||
if (!box.value?.contains(e.target)) {
|
||||
state.isShow = false
|
||||
removeClickOutside()
|
||||
}
|
||||
}
|
||||
const removeClickOutside = () => {
|
||||
window.removeEventListener('click', handleClickOutside)
|
||||
}
|
||||
const handleClick = (e) => {
|
||||
e.stopPropagation();
|
||||
if (state.isShow) {
|
||||
if (state.flagX && state.flagY) {
|
||||
state.editor.chain().focus().insertTable({ rows: state.flagX, cols: state.flagY, withHeaderRow: true }).run()
|
||||
}
|
||||
state.flagX = 0
|
||||
state.flagY = 0
|
||||
removeClickOutside()
|
||||
} else {
|
||||
window.addEventListener('click', handleClickOutside)
|
||||
}
|
||||
state.isShow = !state.isShow
|
||||
}
|
||||
// bubble菜单
|
||||
const shouldShow = ({ editor, view, state, oldState, from, to }) => {
|
||||
// 仅在无序列表选中的时候才显示 气泡菜单
|
||||
return editor.isActive("table");
|
||||
};
|
||||
const box = ref(null)
|
||||
const state = reactive({
|
||||
editor: null,
|
||||
|
@ -126,16 +59,16 @@ export const renderless = (
|
|||
state.editor = editor
|
||||
const api = {
|
||||
state,
|
||||
setLink,
|
||||
handleChange,
|
||||
setLink: setLink(editor),
|
||||
handleChange: handleChange(editor),
|
||||
// table处理函数
|
||||
box,
|
||||
handleMove,
|
||||
handleClickOutside,
|
||||
removeClickOutside,
|
||||
handleClick,
|
||||
handleMove: handleMove(state, box),
|
||||
handleClickOutside: handleClickOutside(state, box),
|
||||
removeClickOutside: removeClickOutside(state, box),
|
||||
handleClick: handleClick(state, box),
|
||||
// bubble 菜单
|
||||
shouldShow,
|
||||
shouldShow: shouldShow,
|
||||
}
|
||||
onBeforeUnmount(() => {
|
||||
state.editor.destroy()
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
@import './vars.less';
|
||||
|
||||
.tiny-rich-text-editor {
|
||||
.component-css-vars-rich-text-editor();
|
||||
|
||||
width: 500px;
|
||||
height: 350px;
|
||||
margin: 0 auto;
|
||||
outline: 3px solid rgb(203, 203, 203);
|
||||
outline: 3px solid var(--ti-rich-text-editor-box-outline-color);
|
||||
border-radius: 8px 8px 0 0;
|
||||
|
||||
&:hover {
|
||||
outline: 3px solid black;
|
||||
outline: 3px solid var(--ti-rich-text-editor-box-outline-hover-color);
|
||||
}
|
||||
|
||||
.button-area {
|
||||
|
@ -26,18 +30,18 @@
|
|||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #d2e4ff;
|
||||
background-color: var(--ti-rich-text-editor-button-hover);
|
||||
}
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
// color: rgba(34, 47, 62, .5);
|
||||
background-color: rgba(203, 203, 203, .3);
|
||||
background-color: var(--ti-rich-text-editor-button-disabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
button.is-active {
|
||||
background-color: #d2e4ff;
|
||||
background-color: var(--ti-rich-text-editor-button-active);
|
||||
}
|
||||
|
||||
.tiny-svg {
|
||||
|
|
|
@ -1 +1,9 @@
|
|||
.component-css-vars-roles() {}
|
||||
.component-css-vars-rich-text-editor() {
|
||||
--ti-rich-text-editor-box-outline-color: var(--ti-common-color-line-normal);
|
||||
--ti-rich-text-editor-box-outline-hover-color: var(--ti-common-color-line-hover);
|
||||
--ti-rich-text-editor-button-hover: var(--ti-base-color-brand-8);
|
||||
--ti-rich-text-editor-button-disabled: var(--ti-common-color-primary-disabled-bgcolor);
|
||||
--ti-rich-text-editor-button-active: var(--ti-base-color-brand-8);
|
||||
--ti-rich-text-editor-poplist-item-selected-bg-color: var(--ti-common-color-selected-background);
|
||||
--ti-rich-text-editor-poplist-item-selected-text-color: var(--ti-common-color-selected-text-color);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue