mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-12 17:08:14 +00:00
focusTracker 100%
This commit is contained in:
parent
e21a74a15e
commit
eb26a10b09
@ -1,6 +1,5 @@
|
||||
{
|
||||
"entry": {
|
||||
"index": "src/vision/index.ts",
|
||||
"vision-preset": "../vision-preset/src/index.ts",
|
||||
"react-simulator-renderer": "../react-simulator-renderer/src/index.ts"
|
||||
},
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { obx, autorun, computed, getPublicPath, hotkey } from '@ali/lowcode-editor-core';
|
||||
import { obx, autorun, computed, getPublicPath, hotkey, focusTracker } from '@ali/lowcode-editor-core';
|
||||
import { ISimulatorHost, Component, NodeInstance, ComponentInstance } from '../simulator';
|
||||
import Viewport from './viewport';
|
||||
import { createSimulator } from './create-simulator';
|
||||
@ -214,6 +214,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
|
||||
// bind hotkey & clipboard
|
||||
hotkey.mount(this._contentWindow);
|
||||
focusTracker.mount(this._contentWindow);
|
||||
clipboard.injectCopyPaster(this._contentDocument);
|
||||
// TODO: dispose the bindings
|
||||
}
|
||||
|
||||
138
packages/editor-core/src/utils/focus-tracker.ts
Normal file
138
packages/editor-core/src/utils/focus-tracker.ts
Normal file
@ -0,0 +1,138 @@
|
||||
export class FocusTracker {
|
||||
mount(win: Window) {
|
||||
const checkDown = (e: MouseEvent) => {
|
||||
if (this.checkModalDown(e)) {
|
||||
return;
|
||||
}
|
||||
if (this.first && !this.first.internalCheckInRange(e)) {
|
||||
this.internalSuspenseItem(this.first);
|
||||
this.first.internalTriggerBlur();
|
||||
}
|
||||
};
|
||||
win.document.addEventListener('mousedown', checkDown, true);
|
||||
return () => {
|
||||
win.document.removeEventListener('mousedown', checkDown, true);
|
||||
};
|
||||
}
|
||||
private actives: Focusable[] = [];
|
||||
get first() {
|
||||
return this.actives[0];
|
||||
}
|
||||
private modals: Array<{ checkDown: (e: MouseEvent) => boolean; checkOpen: () => boolean }> = [];
|
||||
addModal(checkDown: (e: MouseEvent) => boolean, checkOpen: () => boolean) {
|
||||
this.modals.push({
|
||||
checkDown,
|
||||
checkOpen,
|
||||
});
|
||||
}
|
||||
private checkModalOpen(): boolean {
|
||||
return this.modals.some(item => item.checkOpen());
|
||||
}
|
||||
private checkModalDown(e: MouseEvent): boolean {
|
||||
return this.modals.some(item => item.checkDown(e));
|
||||
}
|
||||
execSave() {
|
||||
// has Modal return;
|
||||
if (this.checkModalOpen()) {
|
||||
return;
|
||||
}
|
||||
// catch
|
||||
if (this.first) {
|
||||
this.first.internalTriggerSave();
|
||||
}
|
||||
}
|
||||
execEsc() {
|
||||
if (this.first) {
|
||||
this.internalSuspenseItem(this.first);
|
||||
this.first.internalTriggerEsc();
|
||||
}
|
||||
}
|
||||
create(config: FocusableConfig) {
|
||||
return new Focusable(this, config);
|
||||
}
|
||||
internalActiveItem(item: Focusable) {
|
||||
const first = this.actives[0];
|
||||
if (first === item) {
|
||||
return;
|
||||
}
|
||||
const i = this.actives.indexOf(item);
|
||||
if (i > -1) {
|
||||
this.actives.splice(i, 1);
|
||||
}
|
||||
this.actives.unshift(item);
|
||||
if (!item.isModal && first) {
|
||||
// trigger Blur
|
||||
first.internalTriggerBlur();
|
||||
}
|
||||
// trigger onActive
|
||||
item.internalTriggerActive();
|
||||
}
|
||||
internalSuspenseItem(item: Focusable) {
|
||||
const i = this.actives.indexOf(item);
|
||||
if (i > -1) {
|
||||
this.actives.splice(i, 1);
|
||||
this.first?.internalTriggerActive();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface FocusableConfig {
|
||||
range: HTMLElement | ((e: MouseEvent) => boolean);
|
||||
modal?: boolean; // 模态窗口级别
|
||||
onEsc?: () => void;
|
||||
onBlur?: () => void;
|
||||
onSave?: () => void;
|
||||
onActive?: () => void;
|
||||
}
|
||||
|
||||
export class Focusable {
|
||||
readonly isModal: boolean;
|
||||
constructor(private tracker: FocusTracker, private config: FocusableConfig) {
|
||||
this.isModal = config.modal == null ? false : config.modal;
|
||||
}
|
||||
active() {
|
||||
this.tracker.internalActiveItem(this);
|
||||
}
|
||||
suspense() {
|
||||
this.tracker.internalSuspenseItem(this);
|
||||
}
|
||||
purge() {
|
||||
this.tracker.internalSuspenseItem(this);
|
||||
}
|
||||
internalCheckInRange(e: MouseEvent) {
|
||||
const { range } = this.config;
|
||||
if (!range) {
|
||||
return false;
|
||||
}
|
||||
if (typeof range === 'function') {
|
||||
return range(e);
|
||||
}
|
||||
return range.contains(e.target as HTMLElement);
|
||||
}
|
||||
internalTriggerBlur() {
|
||||
if (this.config.onBlur) {
|
||||
this.config.onBlur();
|
||||
}
|
||||
}
|
||||
internalTriggerSave() {
|
||||
if (this.config.onSave) {
|
||||
this.config.onSave();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
internalTriggerEsc() {
|
||||
if (this.config.onEsc) {
|
||||
this.config.onEsc();
|
||||
}
|
||||
}
|
||||
internalTriggerActive() {
|
||||
if (this.config.onActive) {
|
||||
this.config.onActive();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const focusTracker = new FocusTracker();
|
||||
|
||||
focusTracker.mount(window);
|
||||
@ -1,95 +0,0 @@
|
||||
import { hotkey } from '../hotkey';
|
||||
|
||||
class FocusingManager {
|
||||
deploy() {
|
||||
// in
|
||||
hotkey.bind('esc', () => {
|
||||
// do esc
|
||||
});
|
||||
hotkey.bind(['command + s', 'ctrl + s'], () => {
|
||||
// do save
|
||||
// do esc
|
||||
});
|
||||
}
|
||||
private actives: Focusable[] = [];
|
||||
send(e: MouseEvent) {
|
||||
// if keyborad event check is esc or
|
||||
}
|
||||
addModalCheck(check) {
|
||||
|
||||
}
|
||||
create(config: FocusableConfig) {
|
||||
|
||||
}
|
||||
internalActiveItem(item: Focusable) {
|
||||
const first = this.actives[0];
|
||||
if (first === item) {
|
||||
return;
|
||||
}
|
||||
const i = this.actives.indexOf(item);
|
||||
if (i > -1) {
|
||||
this.actives.splice(i, 1);
|
||||
}
|
||||
this.actives.unshift(item);
|
||||
if (!item.isModal && first) {
|
||||
// trigger Blur
|
||||
first.internalTriggerBlur();
|
||||
}
|
||||
// trigger onActive
|
||||
}
|
||||
internalSuspenceItem(item: Focusable) {
|
||||
const i = this.actives.indexOf(item);
|
||||
if (i > -1) {
|
||||
this.actives.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface FocusableConfig {
|
||||
range: HTMLElement | ((e: MouseEvent) => boolean);
|
||||
modal?: boolean;
|
||||
onEsc?: () => void;
|
||||
onBlur?: () => void;
|
||||
onSave?: () => void;
|
||||
}
|
||||
|
||||
class Focusable {
|
||||
readonly isModal: boolean;
|
||||
constructor(private manager: FocusingManager, private config: FocusableConfig) {
|
||||
this.isModal = config.modal == null ? false : config.modal;
|
||||
}
|
||||
active() {
|
||||
this.manager.internalActiveItem(this);
|
||||
}
|
||||
suspence() {
|
||||
this.manager.internalSuspenceItem(this);
|
||||
}
|
||||
purge() {
|
||||
this.manager.internalSuspenceItem(this);
|
||||
}
|
||||
internalCheckInRange(e: MouseEvent) {
|
||||
const { range } = this.config;
|
||||
if (!range) {
|
||||
return false;
|
||||
}
|
||||
if (typeof range === 'function') {
|
||||
return range(e);
|
||||
}
|
||||
return range.contains(e.target as HTMLElement);
|
||||
}
|
||||
internalTriggerBlur() {
|
||||
if (this.config.onBlur) {
|
||||
this.config.onBlur();
|
||||
}
|
||||
}
|
||||
internalTriggerSave() {
|
||||
if (this.config.onSave) {
|
||||
this.config.onSave();
|
||||
}
|
||||
}
|
||||
internalTriggerEsc() {
|
||||
if (this.config.onEsc) {
|
||||
this.config.onEsc();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,3 +2,4 @@ export * from './get-public-path';
|
||||
export * from './goldlog';
|
||||
export * from './obx';
|
||||
export * from './request';
|
||||
export * from './focus-tracker';
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component, Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from '@ali/lowcode-editor-core';
|
||||
import { observer, Focusable, focusTracker } from '@ali/lowcode-editor-core';
|
||||
import { Button, Icon } from '@alifd/next';
|
||||
import Area from '../area';
|
||||
import Panel from '../widget/panel';
|
||||
@ -12,7 +12,7 @@ export default class LeftFloatPane extends Component<{ area: Area<any, Panel> }>
|
||||
}
|
||||
|
||||
private dispose?: () => void;
|
||||
// private focusing?: FocusingItem;
|
||||
private focusing?: Focusable;
|
||||
private shell: HTMLElement | null = null;
|
||||
componentDidMount() {
|
||||
const { area } = this.props;
|
||||
@ -22,30 +22,26 @@ export default class LeftFloatPane extends Component<{ area: Area<any, Panel> }>
|
||||
area.skeleton.editor.removeListener('designer.dragstart', triggerClose);
|
||||
}
|
||||
|
||||
/*
|
||||
this.focusing = focusingTrack.create(this.shell!, {
|
||||
this.focusing = focusTracker.create({
|
||||
range: this.shell!,
|
||||
onEsc: () => {
|
||||
this.props.area.setVisible(false);
|
||||
},
|
||||
onBlur: () => {
|
||||
this.props.area.setVisible(false);
|
||||
},
|
||||
// modal: boolean
|
||||
});
|
||||
*/
|
||||
|
||||
this.onEffect();
|
||||
}
|
||||
|
||||
onEffect() {
|
||||
/*
|
||||
const { area } = this.props;
|
||||
if (area.visible) {
|
||||
this.focusing?.active();
|
||||
} else {
|
||||
this.focusing?.suspense();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
@ -53,7 +49,7 @@ export default class LeftFloatPane extends Component<{ area: Area<any, Panel> }>
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// this.focusing?.purge();
|
||||
this.focusing?.purge();
|
||||
this.dispose?.();
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { isObject } from './is-object';
|
||||
|
||||
export function isPlainObject(value: any): value is object {
|
||||
export function isPlainObject(value: any): value is any {
|
||||
if (!isObject(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user