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 api = ['state', 'setLink', 'handleChange', 'box', 'handleMove', 'handleClickOutside', 'removeClickOutside', 'handleClick', 'shouldShow']
|
||||||
export const renderless = (
|
export const renderless = (
|
||||||
props,
|
props,
|
||||||
|
@ -47,74 +48,6 @@ export const renderless = (
|
||||||
editable: true,
|
editable: true,
|
||||||
injectCSS: false,
|
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 box = ref(null)
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
editor: null,
|
editor: null,
|
||||||
|
@ -126,16 +59,16 @@ export const renderless = (
|
||||||
state.editor = editor
|
state.editor = editor
|
||||||
const api = {
|
const api = {
|
||||||
state,
|
state,
|
||||||
setLink,
|
setLink: setLink(editor),
|
||||||
handleChange,
|
handleChange: handleChange(editor),
|
||||||
// table处理函数
|
// table处理函数
|
||||||
box,
|
box,
|
||||||
handleMove,
|
handleMove: handleMove(state, box),
|
||||||
handleClickOutside,
|
handleClickOutside: handleClickOutside(state, box),
|
||||||
removeClickOutside,
|
removeClickOutside: removeClickOutside(state, box),
|
||||||
handleClick,
|
handleClick: handleClick(state, box),
|
||||||
// bubble 菜单
|
// bubble 菜单
|
||||||
shouldShow,
|
shouldShow: shouldShow,
|
||||||
}
|
}
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
state.editor.destroy()
|
state.editor.destroy()
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
|
@import './vars.less';
|
||||||
|
|
||||||
.tiny-rich-text-editor {
|
.tiny-rich-text-editor {
|
||||||
|
.component-css-vars-rich-text-editor();
|
||||||
|
|
||||||
width: 500px;
|
width: 500px;
|
||||||
height: 350px;
|
height: 350px;
|
||||||
margin: 0 auto;
|
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;
|
border-radius: 8px 8px 0 0;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
outline: 3px solid black;
|
outline: 3px solid var(--ti-rich-text-editor-box-outline-hover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-area {
|
.button-area {
|
||||||
|
@ -26,18 +30,18 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #d2e4ff;
|
background-color: var(--ti-rich-text-editor-button-hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button:disabled {
|
button:disabled {
|
||||||
// color: rgba(34, 47, 62, .5);
|
// 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;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.is-active {
|
button.is-active {
|
||||||
background-color: #d2e4ff;
|
background-color: var(--ti-rich-text-editor-button-active);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tiny-svg {
|
.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