update: 优化toolbar显示
This commit is contained in:
parent
b5f805654a
commit
a234f94ecf
|
@ -114,17 +114,10 @@ export const pluginConfig: { [key: string]: PluginOptions } = {
|
|||
[ToolbarPlugin.pluginName]: {
|
||||
popup: {
|
||||
items: [
|
||||
['undo', 'redo'],
|
||||
['bold', 'strikethrough', 'fontcolor'],
|
||||
{
|
||||
icon: 'text',
|
||||
items: [
|
||||
'bold',
|
||||
'italic',
|
||||
'strikethrough',
|
||||
'underline',
|
||||
'backcolor',
|
||||
'moremark',
|
||||
],
|
||||
items: ['italic', 'underline', 'backcolor', 'moremark'],
|
||||
},
|
||||
[
|
||||
{
|
||||
|
|
|
@ -17,6 +17,7 @@ import Selection from '../selection';
|
|||
import { $ } from '../node';
|
||||
import NativeEvent from './native-event';
|
||||
import ChangeRange from './range';
|
||||
import { Range } from 'src';
|
||||
|
||||
class ChangeModel implements ChangeInterface {
|
||||
private engine: EngineInterface;
|
||||
|
@ -249,8 +250,8 @@ class ChangeModel implements ChangeInterface {
|
|||
);
|
||||
}
|
||||
|
||||
getOriginValue() {
|
||||
const { container, schema, conversion } = this.engine;
|
||||
getOriginValue(container: NodeInterface = this.engine.container) {
|
||||
const { schema, conversion } = this.engine;
|
||||
return new Parser(
|
||||
container.get<HTMLElement>()?.outerHTML || '',
|
||||
this.engine,
|
||||
|
@ -268,12 +269,16 @@ class ChangeModel implements ChangeInterface {
|
|||
if (options.ignoreCursor || this.isComposing()) {
|
||||
value = this.getOriginValue();
|
||||
} else {
|
||||
const range = this.range.get();
|
||||
let range = this.range.get();
|
||||
let selection;
|
||||
const container = this.engine.container.clone(true);
|
||||
if (!range.inCard()) {
|
||||
const path = range.toPath(true);
|
||||
if (!path) return this.getOriginValue();
|
||||
range = Range.fromPath(this.engine, path, true, container);
|
||||
selection = range.createSelection();
|
||||
}
|
||||
value = this.getOriginValue();
|
||||
value = this.getOriginValue(container);
|
||||
selection?.move();
|
||||
}
|
||||
return value;
|
||||
|
|
|
@ -830,8 +830,7 @@ class Mark implements MarkModelInterface {
|
|||
nodeApi.unwrap(children);
|
||||
}
|
||||
});
|
||||
nodeApi.wrap(targetNode, mark);
|
||||
return targetNode;
|
||||
return nodeApi.wrap(targetNode, mark);
|
||||
} else if (node.name !== mark.name) {
|
||||
node.remove();
|
||||
}
|
||||
|
@ -1099,6 +1098,12 @@ class Mark implements MarkModelInterface {
|
|||
) {
|
||||
started = false;
|
||||
return false;
|
||||
} else if (
|
||||
selection?.focus &&
|
||||
result.next()?.equal(selection.focus)
|
||||
) {
|
||||
started = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (typeof result !== 'undefined') return true;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
</template>
|
||||
<button
|
||||
:class="['toolbar-button',className,{'toolbar-button-active': active,'toolbar-button-disabled':disabled}]"
|
||||
ref="element"
|
||||
@click="triggerClick"
|
||||
@mousedown="triggerMouseDown"
|
||||
@mouseenter="triggerMouseEnter"
|
||||
|
@ -33,6 +34,7 @@ export default defineComponent({
|
|||
},
|
||||
props:buttonProps,
|
||||
setup(props){
|
||||
const element = ref<HTMLButtonElement | undefined>()
|
||||
let hotkey = props.hotkey;
|
||||
//默认获取插件的热键
|
||||
if (props.engine && (hotkey === true || hotkey === undefined)) {
|
||||
|
@ -51,7 +53,8 @@ export default defineComponent({
|
|||
iconIsHtml:/^<.*>/.test(props.icon?.trim() || ""),
|
||||
isMobile,
|
||||
visible,
|
||||
hotkeyText:hotkey
|
||||
hotkeyText:hotkey,
|
||||
element
|
||||
}
|
||||
},
|
||||
data(){
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
:on-click="toggleDropdown"
|
||||
:disabled="disabled"
|
||||
:placement="placement"
|
||||
ref="targetRef"
|
||||
>
|
||||
<template #icon>
|
||||
<span class="colorpicker-button-dropdown-empty" />
|
||||
|
@ -43,7 +44,6 @@
|
|||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onUnmounted, ref, watch } from 'vue'
|
||||
import { $ } from '@aomao/engine'
|
||||
import { colorProps } from '../../types'
|
||||
import { useRight } from '../../hooks';
|
||||
import AmButton from '../button.vue'
|
||||
|
@ -62,7 +62,7 @@ export default defineComponent({
|
|||
|
||||
const buttonRef = ref<HTMLDivElement | null>(null)
|
||||
const isRight = useRight(buttonRef)
|
||||
|
||||
const targetRef = ref<typeof AmButton | undefined>(undefined)
|
||||
const currentColor = ref(props.defaultActiveColor);
|
||||
|
||||
const getContent = () => {
|
||||
|
@ -88,24 +88,25 @@ export default defineComponent({
|
|||
};
|
||||
|
||||
const showDropdown = () => {
|
||||
setTimeout(() => {
|
||||
document.addEventListener('click', hideDropdown);
|
||||
}, 10);
|
||||
visible.value = true
|
||||
};
|
||||
|
||||
const hideDropdown = (event?: MouseEvent) => {
|
||||
if (event?.target && $(event.target).closest('.toolbar-dropdown-list').length > 0)
|
||||
return;
|
||||
document.removeEventListener('click', hideDropdown);
|
||||
if(event && targetRef.value?.element && targetRef.value.element.contains(event.target as Node)) return;
|
||||
visible.value = false
|
||||
};
|
||||
|
||||
watch(() => visible.value, (value,oldValue) => {
|
||||
if(value) document.addEventListener('click', hideDropdown);
|
||||
else document.removeEventListener('click', hideDropdown);
|
||||
})
|
||||
|
||||
const triggerClick = (event:MouseEvent) => {
|
||||
triggerSelect(currentColor.value,event)
|
||||
}
|
||||
|
||||
const triggerSelect = (color: string, event: MouseEvent) => {
|
||||
hideDropdown()
|
||||
currentColor.value = color;
|
||||
buttonContent.value = typeof props.content === 'string'
|
||||
? props.content
|
||||
|
@ -139,7 +140,8 @@ export default defineComponent({
|
|||
currentColor,
|
||||
triggerSelect,
|
||||
triggerClick,
|
||||
toggleDropdown
|
||||
toggleDropdown,
|
||||
targetRef
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -41,7 +41,6 @@ export default defineComponent({
|
|||
let { hotkey } = item
|
||||
//默认获取插件的热键
|
||||
if (props.engine && (hotkey === true || hotkey === undefined)) {
|
||||
console.log(item)
|
||||
hotkey = autoGetHotkey(
|
||||
props.engine,
|
||||
command && !Array.isArray(command) ? command.name : props.name,
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
:active="visible"
|
||||
:disabled="disabled"
|
||||
:placement="placement"
|
||||
ref="targetRef"
|
||||
>
|
||||
<template #default>
|
||||
<slot :item="content">
|
||||
|
@ -56,7 +57,8 @@ export default defineComponent({
|
|||
setup(props,cxt){
|
||||
const valuesVar = ref<string | number | string[]>("")
|
||||
let buttonContent = ref<DropdownListItem | {icon?:string,content?:string} | undefined>(undefined)
|
||||
|
||||
const visible = ref(false)
|
||||
const targetRef = ref<typeof AmButton | undefined>(undefined)
|
||||
const buttonRef = ref<HTMLDivElement | null>(null)
|
||||
const isRight = useRight(buttonRef)
|
||||
|
||||
|
@ -95,50 +97,52 @@ export default defineComponent({
|
|||
valuesVar.value = values ||
|
||||
(props.icon || props.content ? '' : defaultItem?.key || '')
|
||||
}
|
||||
|
||||
const triggerMouseDown = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
}
|
||||
const triggerClick = (event: MouseEvent) =>{
|
||||
event.preventDefault();
|
||||
if (props.disabled) {
|
||||
return;
|
||||
}
|
||||
if (visible.value) {
|
||||
hide();
|
||||
} else {
|
||||
show();
|
||||
}
|
||||
}
|
||||
const show = () => {
|
||||
visible.value = true
|
||||
}
|
||||
const hide = (event?: MouseEvent) => {
|
||||
if(event && targetRef.value?.element && targetRef.value.element.contains(event.target as Node)) return;
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
const triggerSelect = (event: MouseEvent, key: string) => {
|
||||
hide()
|
||||
if (props.onSelect) props.onSelect(event, key);
|
||||
}
|
||||
update(props.values)
|
||||
watch(() => ({...props}), (newProps) => update(newProps.values))
|
||||
watch(() => visible.value, (value,oldValue) => {
|
||||
if(value) document.addEventListener('click', hide);
|
||||
else document.removeEventListener('click', hide);
|
||||
})
|
||||
return {
|
||||
buttonRef,
|
||||
isRight,
|
||||
buttonContent,
|
||||
valuesVar
|
||||
valuesVar,
|
||||
triggerMouseDown,
|
||||
triggerClick,
|
||||
show,
|
||||
hide,
|
||||
triggerSelect,
|
||||
visible,
|
||||
targetRef
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
visible:false
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
triggerMouseDown(event: MouseEvent){
|
||||
event.preventDefault();
|
||||
},
|
||||
triggerClick(event: MouseEvent){
|
||||
event.preventDefault();
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
if (this.visible) {
|
||||
this.hide();
|
||||
} else {
|
||||
this.show();
|
||||
}
|
||||
},
|
||||
show(){
|
||||
setTimeout(() => {
|
||||
document.addEventListener('click', this.hide);
|
||||
}, 10);
|
||||
this.visible = true
|
||||
},
|
||||
hide(){
|
||||
document.removeEventListener('click', this.hide);
|
||||
this.visible = false
|
||||
},
|
||||
|
||||
triggerSelect(event: MouseEvent, key: string){
|
||||
this.hide()
|
||||
if (this.onSelect) this.onSelect(event, key);
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
@ -148,8 +152,9 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
.toolbar-dropdown .toolbar-dropdown-trigger {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.toolbar-dropdown .toolbar-dropdown-trigger .toolbar-dropdown-button-text {
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
<div v-if="!!icon || !!content" class="editor-toolbar-group">
|
||||
<am-popover
|
||||
:get-popup-container="getPopupContainer"
|
||||
trigger="click"
|
||||
overlay-class-name="editor-toolbar-popover"
|
||||
:arrow-point-at-center="true"
|
||||
:placement="isMobile ? 'topRight' : undefined"
|
||||
|
@ -64,11 +63,11 @@ export default defineComponent({
|
|||
</script>
|
||||
<style>
|
||||
.editor-toolbar-group {
|
||||
padding: 4px 8px;
|
||||
padding: 4px;
|
||||
width: auto;
|
||||
border-left: 1px solid #e8e8e8;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.editor-toolbar .editor-toolbar-group:nth-child(1) {
|
||||
|
|
|
@ -30,7 +30,6 @@ export default class Popup {
|
|||
window.addEventListener('resize', this.onSelect);
|
||||
this.#editor.scrollNode?.on('scroll', this.onSelect);
|
||||
document.addEventListener('mousedown', this.hide);
|
||||
this.#editor.on('blur', this.hide);
|
||||
}
|
||||
|
||||
onSelect = () => {
|
||||
|
@ -137,13 +136,20 @@ export default class Popup {
|
|||
|
||||
showContent(callback?: () => void) {
|
||||
const result = this.#editor.trigger('toolbar-render', this.#options);
|
||||
if (this.#vm) this.#vm.unmount();
|
||||
this.#vm = createApp(typeof result === 'object' ? result : Toolbar, {
|
||||
...this.#options,
|
||||
engine: this.#editor,
|
||||
popup: true,
|
||||
});
|
||||
this.#vm.mount(this.#root.get<HTMLDivElement>()!);
|
||||
let content = Toolbar;
|
||||
if (typeof result === 'object') {
|
||||
this.#vm?.unmount();
|
||||
this.#vm = undefined;
|
||||
content = result;
|
||||
}
|
||||
if (!this.#vm) {
|
||||
this.#vm = createApp(content, {
|
||||
...this.#options,
|
||||
engine: this.#editor,
|
||||
popup: true,
|
||||
});
|
||||
this.#vm.mount(this.#root.get<HTMLDivElement>()!);
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (callback) callback();
|
||||
}, 200);
|
||||
|
@ -174,8 +180,10 @@ export default class Popup {
|
|||
window.removeEventListener('resize', this.onSelect);
|
||||
this.#editor.scrollNode?.off('scroll', this.onSelect);
|
||||
document.removeEventListener('mousedown', this.hide);
|
||||
this.#editor.off('blur', this.hide);
|
||||
if (this.#vm) this.#vm.unmount();
|
||||
if (this.#vm) {
|
||||
this.#vm.unmount();
|
||||
this.#vm = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
export type { GroupItemProps };
|
||||
|
|
|
@ -26,115 +26,118 @@ export type ButtonProps = {
|
|||
onMouseLeave?: (event: React.MouseEvent) => void;
|
||||
};
|
||||
|
||||
const ToolbarButton: React.FC<ButtonProps> = (props) => {
|
||||
const { name, engine, command } = props;
|
||||
const ToolbarButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
(props, ref) => {
|
||||
const { name, engine, command } = props;
|
||||
|
||||
const [tooltipVisible, setTooltipVisible] = useState(false);
|
||||
const [tooltipVisible, setTooltipVisible] = useState(false);
|
||||
|
||||
const onClick = (event: React.MouseEvent) => {
|
||||
const { command, onClick, disabled, autoExecute } = props;
|
||||
const onClick = (event: React.MouseEvent) => {
|
||||
const { command, onClick, disabled, autoExecute } = props;
|
||||
|
||||
const nodeName = (event.target as Node).nodeName;
|
||||
if (nodeName !== 'INPUT' && nodeName !== 'TEXTAREA')
|
||||
event.preventDefault();
|
||||
const nodeName = (event.target as Node).nodeName;
|
||||
if (nodeName !== 'INPUT' && nodeName !== 'TEXTAREA')
|
||||
event.preventDefault();
|
||||
|
||||
if (disabled) return;
|
||||
if (onClick && onClick(event) === false) return;
|
||||
if (autoExecute !== false) {
|
||||
let commandName = name;
|
||||
let commandArgs = [];
|
||||
if (command) {
|
||||
if (!Array.isArray(command)) {
|
||||
commandName = command.name;
|
||||
commandArgs = command.args;
|
||||
} else {
|
||||
commandArgs = command;
|
||||
if (disabled) return;
|
||||
if (onClick && onClick(event) === false) return;
|
||||
if (autoExecute !== false) {
|
||||
let commandName = name;
|
||||
let commandArgs = [];
|
||||
if (command) {
|
||||
if (!Array.isArray(command)) {
|
||||
commandName = command.name;
|
||||
commandArgs = command.args;
|
||||
} else {
|
||||
commandArgs = command;
|
||||
}
|
||||
}
|
||||
engine?.command.execute(commandName, ...commandArgs);
|
||||
}
|
||||
engine?.command.execute(commandName, ...commandArgs);
|
||||
};
|
||||
|
||||
const onMouseDown = (event: React.MouseEvent) => {
|
||||
event.preventDefault();
|
||||
const { onMouseDown, disabled } = props;
|
||||
if (disabled) return;
|
||||
if (onMouseDown) onMouseDown(event);
|
||||
|
||||
setTooltipVisible(false);
|
||||
};
|
||||
|
||||
const onMouseEnter = (event: React.MouseEvent) => {
|
||||
const { onMouseEnter } = props;
|
||||
if (onMouseEnter) {
|
||||
onMouseEnter(event);
|
||||
}
|
||||
setTooltipVisible(true);
|
||||
};
|
||||
|
||||
const onMouseLeave = (event: React.MouseEvent) => {
|
||||
const { onMouseLeave } = props;
|
||||
if (onMouseLeave) {
|
||||
onMouseLeave(event);
|
||||
}
|
||||
setTooltipVisible(false);
|
||||
};
|
||||
|
||||
const renderButton = () => {
|
||||
const { icon, content, className, active, disabled } = props;
|
||||
return (
|
||||
<button
|
||||
className={classnames('toolbar-button', className, {
|
||||
'toolbar-button-active': active,
|
||||
'toolbar-button-disabled': disabled,
|
||||
})}
|
||||
ref={ref}
|
||||
onClick={onClick}
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
{typeof icon === 'string' ? (
|
||||
<span className={`data-icon data-icon-${icon}`} />
|
||||
) : (
|
||||
icon
|
||||
)}
|
||||
{typeof content === 'function' ? content() : content}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
let title = props.title ? (
|
||||
<div className="toolbar-tooltip-title">{props.title}</div>
|
||||
) : null;
|
||||
let hotkey = props.hotkey;
|
||||
//默认获取插件的热键
|
||||
if (engine && (hotkey === true || hotkey === undefined)) {
|
||||
hotkey = autoGetHotkey(
|
||||
engine,
|
||||
command && !Array.isArray(command) ? command.name : name,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseDown = (event: React.MouseEvent) => {
|
||||
event.preventDefault();
|
||||
const { onMouseDown, disabled } = props;
|
||||
if (disabled) return;
|
||||
if (onMouseDown) onMouseDown(event);
|
||||
|
||||
setTooltipVisible(false);
|
||||
};
|
||||
|
||||
const onMouseEnter = (event: React.MouseEvent) => {
|
||||
const { onMouseEnter } = props;
|
||||
if (onMouseEnter) {
|
||||
onMouseEnter(event);
|
||||
if (typeof hotkey === 'string' && hotkey !== '') {
|
||||
title = (
|
||||
<>
|
||||
{title}
|
||||
<div className="toolbar-tooltip-hotkey">
|
||||
{formatHotkey(hotkey)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
setTooltipVisible(true);
|
||||
};
|
||||
|
||||
const onMouseLeave = (event: React.MouseEvent) => {
|
||||
const { onMouseLeave } = props;
|
||||
if (onMouseLeave) {
|
||||
onMouseLeave(event);
|
||||
}
|
||||
setTooltipVisible(false);
|
||||
};
|
||||
|
||||
const renderButton = () => {
|
||||
const { icon, content, className, active, disabled } = props;
|
||||
return (
|
||||
<button
|
||||
className={classnames('toolbar-button', className, {
|
||||
'toolbar-button-active': active,
|
||||
'toolbar-button-disabled': disabled,
|
||||
})}
|
||||
onClick={onClick}
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
return title && !isMobile ? (
|
||||
<Tooltip
|
||||
placement={props.placement || 'bottom'}
|
||||
title={title}
|
||||
visible={tooltipVisible}
|
||||
>
|
||||
{typeof icon === 'string' ? (
|
||||
<span className={`data-icon data-icon-${icon}`} />
|
||||
) : (
|
||||
icon
|
||||
)}
|
||||
{typeof content === 'function' ? content() : content}
|
||||
</button>
|
||||
{renderButton()}
|
||||
</Tooltip>
|
||||
) : (
|
||||
renderButton()
|
||||
);
|
||||
};
|
||||
|
||||
let title = props.title ? (
|
||||
<div className="toolbar-tooltip-title">{props.title}</div>
|
||||
) : null;
|
||||
let hotkey = props.hotkey;
|
||||
//默认获取插件的热键
|
||||
if (engine && (hotkey === true || hotkey === undefined)) {
|
||||
hotkey = autoGetHotkey(
|
||||
engine,
|
||||
command && !Array.isArray(command) ? command.name : name,
|
||||
);
|
||||
}
|
||||
if (typeof hotkey === 'string' && hotkey !== '') {
|
||||
title = (
|
||||
<>
|
||||
{title}
|
||||
<div className="toolbar-tooltip-hotkey">
|
||||
{formatHotkey(hotkey)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return title && !isMobile ? (
|
||||
<Tooltip
|
||||
placement={props.placement || 'bottom'}
|
||||
title={title}
|
||||
visible={tooltipVisible}
|
||||
>
|
||||
{renderButton()}
|
||||
</Tooltip>
|
||||
) : (
|
||||
renderButton()
|
||||
);
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export default ToolbarButton;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import React, { useEffect, useState, useRef, useCallback } from 'react';
|
||||
import classNames from 'classnames-es-ts';
|
||||
import { $ } from '@aomao/engine';
|
||||
import type { EngineInterface, Placement } from '@aomao/engine';
|
||||
import { useRight } from '../hooks';
|
||||
import Button from '../button';
|
||||
|
@ -52,16 +51,10 @@ const ColorButton: React.FC<ColorButtonProps> = ({
|
|||
),
|
||||
);
|
||||
const [currentColor, setCurrentColor] = useState(defaultActiveColor);
|
||||
|
||||
const targetRef = useRef<HTMLButtonElement | null>(null);
|
||||
const buttonRef = useRef<HTMLDivElement | null>(null);
|
||||
const isRight = useRight(buttonRef);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
document.removeEventListener('click', hideDropdown);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setButtonContent(
|
||||
typeof content === 'string'
|
||||
|
@ -76,7 +69,6 @@ const ColorButton: React.FC<ColorButtonProps> = ({
|
|||
|
||||
const toggleDropdown = (event: React.MouseEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (pickerVisible) {
|
||||
hideDropdown();
|
||||
} else {
|
||||
|
@ -84,26 +76,30 @@ const ColorButton: React.FC<ColorButtonProps> = ({
|
|||
}
|
||||
};
|
||||
|
||||
const showDropdown = () => {
|
||||
setTimeout(() => {
|
||||
document.addEventListener('click', hideDropdown);
|
||||
}, 10);
|
||||
setPickerVisible(true);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (pickerVisible) document.addEventListener('click', hideDropdown);
|
||||
return () => {
|
||||
document.removeEventListener('click', hideDropdown);
|
||||
};
|
||||
}, [pickerVisible]);
|
||||
|
||||
const hideDropdown = (event?: MouseEvent) => {
|
||||
const showDropdown = useCallback(() => {
|
||||
setPickerVisible(true);
|
||||
}, []);
|
||||
|
||||
const hideDropdown = useCallback((event?: MouseEvent) => {
|
||||
if (
|
||||
event?.target &&
|
||||
$(event.target).closest('.toolbar-dropdown-list').length > 0
|
||||
event &&
|
||||
targetRef.current &&
|
||||
targetRef.current.contains(event.target as Node)
|
||||
)
|
||||
return;
|
||||
document.removeEventListener('click', hideDropdown);
|
||||
setPickerVisible(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const triggerSelect = (color: string, event: React.MouseEvent) => {
|
||||
setCurrentColor(color);
|
||||
|
||||
hideDropdown();
|
||||
if (autoExecute !== false) {
|
||||
let commandName = name;
|
||||
let commandArgs = [color, defaultColor];
|
||||
|
@ -153,6 +149,7 @@ const ColorButton: React.FC<ColorButtonProps> = ({
|
|||
content={<span className="data-icon data-icon-arrow" />}
|
||||
onClick={toggleDropdown}
|
||||
placement={placement}
|
||||
ref={targetRef}
|
||||
/>
|
||||
</div>
|
||||
{pickerVisible && (
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
}
|
||||
|
||||
.toolbar-dropdown .toolbar-dropdown-trigger {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.toolbar-dropdown .toolbar-dropdown-trigger .toolbar-dropdown-button-text {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useRef } from 'react';
|
||||
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
||||
import classnames from 'classnames-es-ts';
|
||||
import type { EngineInterface, Placement } from '@aomao/engine';
|
||||
import Button from '../button';
|
||||
|
@ -46,6 +46,7 @@ const Dropdown: React.FC<DropdownProps> = ({
|
|||
const [visible, setVisible] = useState(false);
|
||||
|
||||
const buttonRef = useRef<HTMLDivElement | null>(null);
|
||||
const targetRef = useRef<HTMLButtonElement | null>(null);
|
||||
const isRight = useRight(buttonRef);
|
||||
|
||||
const toggle = (event: React.MouseEvent) => {
|
||||
|
@ -61,19 +62,29 @@ const Dropdown: React.FC<DropdownProps> = ({
|
|||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) document.addEventListener('click', hide);
|
||||
return () => {
|
||||
document.removeEventListener('click', hide);
|
||||
};
|
||||
}, [visible]);
|
||||
|
||||
const show = () => {
|
||||
setTimeout(() => {
|
||||
document.addEventListener('click', hide);
|
||||
}, 10);
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
const hide = () => {
|
||||
document.removeEventListener('click', hide);
|
||||
const hide = useCallback((event?: MouseEvent) => {
|
||||
if (
|
||||
event &&
|
||||
targetRef.current &&
|
||||
targetRef.current.contains(event.target as Node)
|
||||
)
|
||||
return;
|
||||
setVisible(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const triggerSelect = (event: React.MouseEvent, key: string) => {
|
||||
hide();
|
||||
if (onSelect) onSelect(event, key);
|
||||
};
|
||||
|
||||
|
@ -147,6 +158,7 @@ const Dropdown: React.FC<DropdownProps> = ({
|
|||
active={visible}
|
||||
disabled={disabled}
|
||||
placement={placement}
|
||||
ref={targetRef}
|
||||
/>
|
||||
</div>
|
||||
{visible && (
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
.editor-toolbar-group {
|
||||
padding: 4px 8px;
|
||||
padding: 4px;
|
||||
width: auto;
|
||||
border-left: 1px solid #e8e8e8;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.editor-toolbar .editor-toolbar-group:nth-child(1) {
|
||||
|
|
|
@ -30,7 +30,6 @@ export default class Popup {
|
|||
window.addEventListener('resize', this.onSelect);
|
||||
this.#editor.scrollNode?.on('scroll', this.onSelect);
|
||||
document.addEventListener('mousedown', this.hide);
|
||||
this.#editor.on('blur', this.hide);
|
||||
}
|
||||
|
||||
onSelect = () => {
|
||||
|
@ -180,7 +179,6 @@ export default class Popup {
|
|||
window.removeEventListener('resize', this.onSelect);
|
||||
this.#editor.scrollNode?.off('scroll', this.onSelect);
|
||||
document.removeEventListener('mousedown', this.hide);
|
||||
this.#editor.off('blur', this.hide);
|
||||
}
|
||||
}
|
||||
export type { GroupItemProps };
|
||||
|
|
Loading…
Reference in New Issue