feat: 右键代码移到core,添加右键区块全屏操作 (#1524)
Reviewed-on: daoyoucloud/tachybase#1524 Reviewed-by: sealday <zhanglin@daoyoucloud.com> Co-authored-by: wjh <wwwjh0710@163.com> Co-committed-by: wjh <wwwjh0710@163.com>
This commit is contained in:
parent
f25fb09230
commit
f1cfe2259e
@ -61,6 +61,7 @@
|
||||
"react-router": "^6.11.2",
|
||||
"react-router-dom": "^6.25.1",
|
||||
"react-to-print": "^2.14.7",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"sanitize-html": "2.10.0",
|
||||
"use-deep-compare-effect": "^1.8.1"
|
||||
},
|
||||
|
@ -21,6 +21,7 @@ import { AppSchemaComponentProvider } from './AppSchemaComponentProvider';
|
||||
import { AppComponent, BlankComponent, defaultAppComponents } from './components';
|
||||
import { NoticeManager } from './NoticesManager';
|
||||
import type { Plugin } from './Plugin';
|
||||
import { PluginContextMenu, PluginItemsOptions } from './PluginContextMenu';
|
||||
import { PluginManager, PluginType } from './PluginManager';
|
||||
import { PluginSettingOptions, PluginSettingsManager } from './PluginSettingsManager';
|
||||
import { ComponentTypeAndString, RouterManager, RouterOptions } from './RouterManager';
|
||||
@ -58,6 +59,7 @@ export interface ApplicationOptions {
|
||||
loadRemotePlugins?: boolean;
|
||||
devDynamicImport?: DevDynamicImport;
|
||||
dataSourceManager?: DataSourceManagerOptions;
|
||||
pluginMenuItems?: Record<string, PluginItemsOptions>;
|
||||
}
|
||||
|
||||
export class Application {
|
||||
@ -82,6 +84,7 @@ export class Application {
|
||||
public schemaSettingsManager: SchemaSettingsManager;
|
||||
public dataSourceManager: DataSourceManager;
|
||||
public noticeManager: NoticeManager;
|
||||
public pluginContextMenu: PluginContextMenu;
|
||||
|
||||
public name: string;
|
||||
|
||||
@ -128,6 +131,7 @@ export class Application {
|
||||
this.pluginSettingsManager = new PluginSettingsManager(options.pluginSettings, this);
|
||||
this.addRoutes();
|
||||
this.name = this.options.name || getSubAppName(options.publicPath) || 'main';
|
||||
this.pluginContextMenu = new PluginContextMenu(options.pluginMenuItems, this);
|
||||
}
|
||||
|
||||
private initRequireJs() {
|
||||
|
35
packages/core/client/src/application/PluginContextMenu.ts
Normal file
35
packages/core/client/src/application/PluginContextMenu.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import type { Application } from './Application';
|
||||
|
||||
export interface PluginItemsOptions {
|
||||
name?: string;
|
||||
useLoadMethod?: (any) => methodOptionsType;
|
||||
}
|
||||
|
||||
export interface methodOptionsType {
|
||||
title: any;
|
||||
actionProps: any;
|
||||
children?: any;
|
||||
}
|
||||
|
||||
export class PluginContextMenu {
|
||||
protected menuItems: Record<string, PluginItemsOptions> = {};
|
||||
public app: Application;
|
||||
|
||||
constructor(_pluginMenuItems: Record<string, PluginItemsOptions>, app: Application) {
|
||||
this.app = app;
|
||||
Object.entries(_pluginMenuItems || {}).forEach(([name, PluginItemsOptions]) => {
|
||||
this.add(name, PluginItemsOptions);
|
||||
});
|
||||
}
|
||||
|
||||
get() {
|
||||
return this.menuItems;
|
||||
}
|
||||
|
||||
add(name: string, options: PluginItemsOptions) {
|
||||
this.menuItems[name] = {
|
||||
name: name,
|
||||
...options,
|
||||
};
|
||||
}
|
||||
}
|
@ -11,3 +11,4 @@ export * from './PluginSettingsManager';
|
||||
export * from './NoticesManager';
|
||||
export * from './hoc';
|
||||
export { ApplicationContext } from './context';
|
||||
export * from './PluginContextMenu';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useDesignable, useHotkeys } from '@tachybase/client';
|
||||
import { useContextMenu, useDesignable, useHotkeys } from '@tachybase/client';
|
||||
|
||||
import {
|
||||
CalculatorOutlined,
|
||||
@ -10,7 +10,6 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
import { FloatButton } from 'antd';
|
||||
|
||||
import { useContextMenu } from '../context-menu/useContextMenu';
|
||||
import { useCalculator } from './calculator/CalculatorProvider';
|
||||
import { useSearchAndJump } from './search-and-jump';
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { css } from '@tachybase/client';
|
||||
|
||||
import { CalculatorDisplay } from './CalculatorDisplay';
|
||||
import { CalculatorKey } from './CalculatorKey';
|
@ -1,4 +1,4 @@
|
||||
import { createStyles } from '@tachybase/client';
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css }) => {
|
||||
return {
|
@ -1,5 +1,4 @@
|
||||
import { Plugin } from '@tachybase/client';
|
||||
|
||||
import { Plugin } from '../../application/Plugin';
|
||||
import { AssistantProvider } from './Assistant.provider';
|
||||
import { CalculatorProvider } from './calculator/CalculatorProvider';
|
||||
import { SearchAndJumpProvider } from './search-and-jump';
|
@ -0,0 +1,42 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { useApp } from '../../application';
|
||||
import { ContextMenu, ContextMenuItem, ContextMenuTrigger } from '../../schema-component/common/context-menu';
|
||||
import { ContextMenuContext } from './useContextMenu';
|
||||
|
||||
export const ContextMenuProvider = ({ children }) => {
|
||||
const [enable, setEnable] = useState(true);
|
||||
const contextItems = useApp().pluginContextMenu.get();
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 });
|
||||
const menuItems = {
|
||||
actions: [],
|
||||
childrens: [],
|
||||
};
|
||||
Object.values(contextItems).forEach((item) => {
|
||||
const { actionProps, children: nodeChildren, title } = item.useLoadMethod({ enable, setEnable, position });
|
||||
menuItems.actions.push({ title, actionProps });
|
||||
menuItems.childrens.push(nodeChildren);
|
||||
});
|
||||
|
||||
return (
|
||||
<ContextMenuContext.Provider
|
||||
value={{
|
||||
contextMenuEnabled: enable,
|
||||
setContextMenuEnable: setEnable,
|
||||
position,
|
||||
}}
|
||||
>
|
||||
<ContextMenu id="my-context-menu-1">
|
||||
{menuItems.actions.map((item, index) => (
|
||||
<ContextMenuItem {...item.actionProps} key={index}>
|
||||
{item.title}
|
||||
</ContextMenuItem>
|
||||
))}
|
||||
</ContextMenu>
|
||||
<ContextMenuTrigger disable={!enable} id="my-context-menu-1" position={position} setPosition={setPosition}>
|
||||
{menuItems.childrens.map((children) => children && children())}
|
||||
{children}
|
||||
</ContextMenuTrigger>
|
||||
</ContextMenuContext.Provider>
|
||||
);
|
||||
};
|
@ -0,0 +1,145 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
|
||||
import { css } from '@emotion/css';
|
||||
import { App } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useDesignable } from '../../schema-component';
|
||||
import { useJoystick } from './useJoystick';
|
||||
|
||||
export const dragAssistant = {
|
||||
name: 'dragAssistant',
|
||||
useLoadMethod: () => {
|
||||
const [enableDragable, setEnableDragable] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const ref = useRef<HTMLDivElement>() as any;
|
||||
useJoystick(ref);
|
||||
return {
|
||||
title: t('Drag Assistant'),
|
||||
actionProps: {
|
||||
onClick: () => {
|
||||
setEnableDragable((enable) => !enable);
|
||||
},
|
||||
},
|
||||
children() {
|
||||
return (
|
||||
enableDragable && (
|
||||
<div
|
||||
ref={ref}
|
||||
className={css`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 30;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(141, 141, 113, 0.274);
|
||||
`}
|
||||
></div>
|
||||
)
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const designerMode = {
|
||||
name: 'designerMode',
|
||||
useLoadMethod: () => {
|
||||
const { designable, setDesignable } = useDesignable() as any;
|
||||
const { t } = useTranslation();
|
||||
return {
|
||||
title: t('Designer Mode'),
|
||||
actionProps: {
|
||||
onClick: () => {
|
||||
setDesignable(!designable);
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const fullScreen = {
|
||||
name: 'fullScreen',
|
||||
useLoadMethod: ({ position }) => {
|
||||
const { t } = useTranslation();
|
||||
const { message } = App.useApp();
|
||||
return {
|
||||
title: t('Full Screen'),
|
||||
actionProps: {
|
||||
onClick: () => {
|
||||
checkedAutoPage(position, message, t);
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const disableRightMenu = {
|
||||
name: 'disableRightMenu',
|
||||
useLoadMethod: ({ enable, setEnable }) => {
|
||||
return {
|
||||
title: 'Disable Right Menu',
|
||||
actionProps: {
|
||||
onClick: () => {
|
||||
setEnable(!enable);
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const checkedAutoPage = (position, message, t) => {
|
||||
const layoutSider = document.querySelector('.ant-layout-sider');
|
||||
const layoutContent = document.querySelector('.ant-layout-content');
|
||||
const sibling = layoutContent.previousElementSibling;
|
||||
if (!sibling.classList.contains('autoPage')) {
|
||||
const element = document.elementFromPoint(position?.x, position?.y);
|
||||
const blockElement = element.closest('.ant-card');
|
||||
if (blockElement) {
|
||||
//需要展示的区块
|
||||
const copyBlockElement = blockElement.cloneNode(true);
|
||||
copyBlockElement.style.width = '100%';
|
||||
copyBlockElement.style.height = '90vh';
|
||||
copyBlockElement.style.overflow = 'auto';
|
||||
copyBlockElement.style.backgroundColor = '#ffffff';
|
||||
|
||||
const autoNode = document.createElement('div');
|
||||
autoNode.style.marginTop = '2.5%';
|
||||
autoNode.style.width = '100%';
|
||||
autoNode.className = 'autoPage';
|
||||
//退出按钮
|
||||
const exitNode = document.createElement('div');
|
||||
exitNode.style.width = '100%';
|
||||
exitNode.style.height = '3vh';
|
||||
exitNode.style.textAlign = 'center';
|
||||
exitNode.style.lineHeight = '3vh';
|
||||
exitNode.style.backgroundColor = '#e6e6e6';
|
||||
exitNode.textContent = '退出全屏';
|
||||
autoNode.appendChild(exitNode);
|
||||
autoNode.appendChild(copyBlockElement);
|
||||
autoNode.addEventListener('click', () => {
|
||||
removeNode(layoutSider);
|
||||
});
|
||||
const fragment = document.createDocumentFragment();
|
||||
fragment.appendChild(autoNode);
|
||||
layoutSider.style.display = 'none';
|
||||
layoutContent.style.display = 'none';
|
||||
layoutContent?.parentNode.insertBefore(fragment, layoutContent);
|
||||
} else {
|
||||
message.warning(t('There are no full screen blocks available at the current location'));
|
||||
}
|
||||
} else {
|
||||
removeNode(layoutSider);
|
||||
}
|
||||
};
|
||||
|
||||
const removeNode = (layoutSider) => {
|
||||
const layoutContent = document.querySelector('.ant-layout-content');
|
||||
const sibling = layoutContent.previousElementSibling;
|
||||
layoutSider.style.display = 'block';
|
||||
layoutContent.style.display = 'block';
|
||||
if (sibling.classList.contains('autoPage')) sibling.parentNode.removeChild(sibling);
|
||||
};
|
15
packages/core/client/src/built-in/context-menu/index.ts
Normal file
15
packages/core/client/src/built-in/context-menu/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Plugin } from '../../application/Plugin';
|
||||
import { ContextMenuProvider } from './ContextMenu.provider';
|
||||
import { designerMode, disableRightMenu, dragAssistant, fullScreen } from './ContextMenuItemsProps';
|
||||
|
||||
export * from './useContextMenu';
|
||||
|
||||
export class PluginContextMenu extends Plugin {
|
||||
async load() {
|
||||
this.app.use(ContextMenuProvider);
|
||||
this.app.pluginContextMenu.add(dragAssistant.name, dragAssistant);
|
||||
this.app.pluginContextMenu.add(designerMode.name, designerMode);
|
||||
this.app.pluginContextMenu.add(fullScreen.name, fullScreen);
|
||||
this.app.pluginContextMenu.add(disableRightMenu.name, disableRightMenu);
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@ import React, { createContext, useContext } from 'react';
|
||||
interface ContextMenuContext {
|
||||
contextMenuEnabled: boolean;
|
||||
setContextMenuEnable: (enabled: boolean) => void;
|
||||
position: any;
|
||||
setPosition: (any) => void;
|
||||
}
|
||||
|
||||
export const ContextMenuContext = createContext<Partial<ContextMenuContext>>({});
|
@ -22,7 +22,9 @@ import { BlockTemplateDetails, BlockTemplatePage } from '../schema-templates';
|
||||
import { CurrentUserProvider, CurrentUserSettingsMenuProvider } from '../user';
|
||||
import { ACLPlugin } from './acl';
|
||||
import { AdminLayoutPlugin } from './admin-layout';
|
||||
import { PluginAssistant } from './assistant';
|
||||
import { PluginBuiltInCollections } from './built-in-collections';
|
||||
import { PluginContextMenu } from './context-menu';
|
||||
import { RemoteDocumentTitlePlugin } from './document-title';
|
||||
import { LocalePlugin } from './locale/LocalePlugin';
|
||||
import { PinnedListPlugin } from './pinned-list';
|
||||
@ -30,6 +32,7 @@ import { PMPlugin } from './pm';
|
||||
import { SystemSettingsPlugin } from './system-settings';
|
||||
|
||||
export { AdminProvider, NoticeArea } from './admin-layout';
|
||||
export * from './context-menu';
|
||||
|
||||
interface AppStatusProps {
|
||||
error: Error;
|
||||
@ -349,5 +352,7 @@ export class BuiltInPlugin extends Plugin {
|
||||
await this.app.pm.add(RemoteDocumentTitlePlugin, { name: 'remote-document-title' });
|
||||
await this.app.pm.add(PMPlugin, { name: 'builtin-pm' });
|
||||
await this.app.pm.add(CollectionPlugin, { name: 'builtin-collection' });
|
||||
await this.app.pm.add(PluginContextMenu);
|
||||
await this.app.pm.add(PluginAssistant);
|
||||
}
|
||||
}
|
||||
|
@ -838,5 +838,10 @@
|
||||
"The current return type is not a document type":"The current return type is not a document type",
|
||||
"Test Data":"Test Data",
|
||||
"Add associations": "Add associations",
|
||||
"DatePicker":"DatePicker"
|
||||
"DatePicker":"DatePicker",
|
||||
"Drag Assistant":"Drag Assistant",
|
||||
"Disable Right Menu":"Disable Right Menu",
|
||||
"Full Screen":"Full Screen",
|
||||
"Designer Mode":"Designer Mode",
|
||||
"There are no full screen blocks available at the current location":"There are no full screen blocks available at the current location"
|
||||
}
|
||||
|
@ -951,5 +951,10 @@
|
||||
"Field Component":"字段组件",
|
||||
"Custom filter":"自定义筛选",
|
||||
"Parent Association field":"父级关联字段",
|
||||
"DatePicker":"日期选择器"
|
||||
"DatePicker":"日期选择器",
|
||||
"Drag Assistant":"拖动助手",
|
||||
"Disable Right Menu":"禁用右键菜单",
|
||||
"Full Screen":"切换全屏",
|
||||
"Designer Mode":"设计者模式",
|
||||
"There are no full screen blocks available at the current location":"当前位置没有可全屏区块"
|
||||
}
|
||||
|
@ -37,13 +37,29 @@ function ContextMenu({
|
||||
if (onHide) onHide();
|
||||
};
|
||||
|
||||
const handleMouseLeave = useCallback((e) => {
|
||||
const handleMouseLeave = useCallback(
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
onMouseLeave(e);
|
||||
|
||||
if (hideOnLeave) callHideEvent(id);
|
||||
});
|
||||
},
|
||||
[
|
||||
children,
|
||||
id,
|
||||
appendTo,
|
||||
hideOnLeave,
|
||||
onMouseLeave,
|
||||
onHide,
|
||||
onShow,
|
||||
preventHideOnScroll,
|
||||
preventHideOnResize,
|
||||
attributes,
|
||||
className,
|
||||
animation,
|
||||
],
|
||||
);
|
||||
|
||||
const clickOutsideCallback = (event) => {
|
||||
if (contextMenuEl.current && !contextMenuEl.current.contains(event.target)) {
|
||||
@ -151,9 +167,7 @@ function ContextMenu({
|
||||
<ContextComponent />
|
||||
);
|
||||
}
|
||||
|
||||
export default ContextMenu;
|
||||
|
||||
ContextMenu.defaultProps = {
|
||||
appendTo: null,
|
||||
hideOnLeave: false,
|
@ -1,19 +1,19 @@
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import React, { createContext, useCallback, useRef } from 'react';
|
||||
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { callHideEvent } from './registerEvent';
|
||||
|
||||
function ContextMenuItem({ children, onClick, disabled, preventClose, attributes, className }) {
|
||||
export function ContextMenuItem({ children, onClick, disabled, preventClose, attributes, className, ...props }) {
|
||||
const contextMenuItem = useRef(null);
|
||||
|
||||
const handleClickEvent = useCallback((e) => {
|
||||
const handleClickEvent = useCallback(
|
||||
(e) => {
|
||||
if (disabled) return;
|
||||
onClick(e);
|
||||
|
||||
if (!preventClose) callHideEvent('ID_NOT_REQUIRED');
|
||||
});
|
||||
|
||||
},
|
||||
[children, onClick, disabled, preventClose, attributes, className],
|
||||
);
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
@ -23,6 +23,7 @@ function ContextMenuItem({ children, onClick, disabled, preventClose, attributes
|
||||
},
|
||||
...className.split(' '),
|
||||
)}
|
||||
{...props}
|
||||
onClick={handleClickEvent}
|
||||
{...attributes}
|
||||
ref={contextMenuItem}
|
||||
@ -33,7 +34,6 @@ function ContextMenuItem({ children, onClick, disabled, preventClose, attributes
|
||||
}
|
||||
|
||||
export default ContextMenuItem;
|
||||
|
||||
ContextMenuItem.defaultProps = {
|
||||
disabled: false,
|
||||
preventClose: false,
|
@ -0,0 +1,74 @@
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { callHideEvent, callShowEvent } from './registerEvent';
|
||||
|
||||
export function ContextMenuTrigger({
|
||||
children,
|
||||
id,
|
||||
disableWhileShiftPressed,
|
||||
attributes,
|
||||
disable,
|
||||
className,
|
||||
position,
|
||||
setPosition,
|
||||
}) {
|
||||
const menuTrigger = useRef(null);
|
||||
|
||||
const handleContextMenu = useCallback(
|
||||
(e) => {
|
||||
if (disable) return;
|
||||
if (disableWhileShiftPressed && e.nativeEvent.shiftKey) {
|
||||
callHideEvent(id);
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const { clientX, clientY } = e.nativeEvent;
|
||||
const minPx = 50;
|
||||
if (
|
||||
clientX - position.x > minPx ||
|
||||
position.x - clientX > minPx ||
|
||||
clientY - position.y > minPx ||
|
||||
position.y - clientY > minPx
|
||||
) {
|
||||
setPosition({
|
||||
x: clientX,
|
||||
y: clientY,
|
||||
});
|
||||
}
|
||||
const opts = {
|
||||
position: {
|
||||
clientY,
|
||||
clientX,
|
||||
},
|
||||
id,
|
||||
};
|
||||
|
||||
callShowEvent(opts);
|
||||
},
|
||||
[children, id, disableWhileShiftPressed, attributes, disable, className],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('menu-trigger', ...className.split(' '))}
|
||||
ref={menuTrigger}
|
||||
{...attributes}
|
||||
onContextMenu={(e) => handleContextMenu(e)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ContextMenuTrigger;
|
||||
ContextMenuTrigger.defaultProps = {
|
||||
attributes: {},
|
||||
disable: false,
|
||||
renderTag: 'div',
|
||||
disableWhileShiftPressed: false,
|
||||
className: '',
|
||||
};
|
@ -2,7 +2,7 @@ import { uniqueId } from './helper';
|
||||
|
||||
const events = {};
|
||||
|
||||
let activeEvent = {};
|
||||
let activeEvent = {} as any;
|
||||
|
||||
const registerEvent = (id, showMenu, hideMenu) => {
|
||||
const _ = uniqueId();
|
@ -4,7 +4,7 @@ import classnames from 'classnames';
|
||||
|
||||
import ContextMenuItem from './contextMenuItem';
|
||||
|
||||
function Submenu({ children, title, attributes, className }) {
|
||||
export function Submenu({ children, title, attributes, className }) {
|
||||
const [submenuStyle, setSubmenuStyle] = useState(null);
|
||||
const submenuEl = useRef(null);
|
||||
const submenuItem = useRef(null);
|
||||
@ -39,7 +39,7 @@ function Submenu({ children, title, attributes, className }) {
|
||||
}
|
||||
|
||||
setSubmenuStyle(style);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const hideSubmenu = useCallback(() => {
|
||||
const style = {
|
||||
@ -48,7 +48,7 @@ function Submenu({ children, title, attributes, className }) {
|
||||
};
|
||||
|
||||
setSubmenuStyle(style);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -69,7 +69,6 @@ function Submenu({ children, title, attributes, className }) {
|
||||
}
|
||||
|
||||
export default Submenu;
|
||||
|
||||
Submenu.defaultProps = {
|
||||
title: 'Sub Menu',
|
||||
className: '',
|
@ -1,2 +1,3 @@
|
||||
export * from './dnd-context';
|
||||
export * from './sortable-item';
|
||||
export * from './context-menu';
|
||||
|
@ -1,51 +0,0 @@
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { callHideEvent, callShowEvent } from './registerEvent';
|
||||
|
||||
function ContextMenuTrigger({ children, id, disableWhileShiftPressed, attributes, disable, className }) {
|
||||
const menuTrigger = useRef(null);
|
||||
|
||||
const handleContextMenu = useCallback((e) => {
|
||||
if (disable) return;
|
||||
if (disableWhileShiftPressed && e.nativeEvent.shiftKey) {
|
||||
callHideEvent(id);
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const { clientX, clientY } = e.nativeEvent;
|
||||
const opts = {
|
||||
position: {
|
||||
clientY,
|
||||
clientX,
|
||||
},
|
||||
id,
|
||||
};
|
||||
|
||||
callShowEvent(opts);
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('menu-trigger', ...className.split(' '))}
|
||||
ref={menuTrigger}
|
||||
{...attributes}
|
||||
onContextMenu={(e) => handleContextMenu(e)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ContextMenuTrigger;
|
||||
|
||||
ContextMenuTrigger.defaultProps = {
|
||||
attributes: {},
|
||||
disable: false,
|
||||
renderTag: 'div',
|
||||
disableWhileShiftPressed: false,
|
||||
className: '',
|
||||
};
|
@ -1,46 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface ContextMenu {
|
||||
id: string;
|
||||
appendTo?: string;
|
||||
animation?: string;
|
||||
hideOnLeave?: boolean;
|
||||
attributes?: object;
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
preventHideOnResize?: boolean;
|
||||
preventHideOnScroll?: boolean;
|
||||
onShow?: { (event: any): void };
|
||||
onHide?: { (event: any): void };
|
||||
onMouseLeave?: { (event: any): void };
|
||||
}
|
||||
|
||||
export interface ContextMenuItem {
|
||||
disabled?: boolean;
|
||||
preventClose?: boolean;
|
||||
disableWhileShiftPressed?: boolean;
|
||||
attributes?: object;
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
onClick?: { (event: any): void };
|
||||
}
|
||||
|
||||
export interface ContextMenuTrigger {
|
||||
id: string;
|
||||
attributes?: object;
|
||||
disable?: boolean;
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export interface Submenu {
|
||||
title: string;
|
||||
attributes?: object;
|
||||
className?: boolean;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export const ContextMenu: React.FunctionComponent<ContextMenu>;
|
||||
export const ContextMenuItem: React.FunctionComponent<ContextMenuItem>;
|
||||
export const ContextMenuTrigger: React.FunctionComponent<ContextMenuTrigger>;
|
||||
export const Submenu: React.FunctionComponent<Submenu>;
|
@ -1,58 +0,0 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { css, useDesignable } from '@tachybase/client';
|
||||
|
||||
import { ContextMenu, ContextMenuItem, ContextMenuTrigger } from '../../components/context-menu';
|
||||
import { ContextMenuContext } from './useContextMenu';
|
||||
import { useJoystick } from './useJoystick';
|
||||
|
||||
export const ContextMenuProvider = ({ children }) => {
|
||||
const [enable, setEnable] = useState(true);
|
||||
const [enableDragable, setEnableDragable] = useState(false);
|
||||
const { designable, setDesignable } = useDesignable();
|
||||
const ref = useRef<HTMLDivElement>();
|
||||
useJoystick(ref);
|
||||
return (
|
||||
<ContextMenuContext.Provider value={{ contextMenuEnabled: enable, setContextMenuEnable: setEnable }}>
|
||||
<ContextMenu id="my-context-menu-1">
|
||||
<ContextMenuItem
|
||||
onClick={() => {
|
||||
setEnableDragable((enable) => !enable);
|
||||
}}
|
||||
>
|
||||
拖动助手
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem
|
||||
onClick={() => {
|
||||
setDesignable(!designable);
|
||||
}}
|
||||
>
|
||||
设计者模式
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem
|
||||
onClick={() => {
|
||||
setEnable(!enable);
|
||||
}}
|
||||
>
|
||||
禁用右键菜单
|
||||
</ContextMenuItem>
|
||||
</ContextMenu>
|
||||
<ContextMenuTrigger disable={!enable} id="my-context-menu-1">
|
||||
{enableDragable && (
|
||||
<div
|
||||
ref={ref}
|
||||
className={css`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
background-color: rgba(141, 141, 113, 0.274);
|
||||
`}
|
||||
></div>
|
||||
)}
|
||||
{children}
|
||||
</ContextMenuTrigger>
|
||||
</ContextMenuContext.Provider>
|
||||
);
|
||||
};
|
@ -1,9 +0,0 @@
|
||||
import { Plugin } from '@tachybase/client';
|
||||
|
||||
import { ContextMenuProvider } from './ContextMenu.provider';
|
||||
|
||||
export class PluginContextMenu extends Plugin {
|
||||
async load() {
|
||||
this.app.use(ContextMenuProvider);
|
||||
}
|
||||
}
|
@ -14,9 +14,7 @@ import {
|
||||
Expression,
|
||||
SignatureInput,
|
||||
} from './components';
|
||||
import { PluginAssistant } from './features/assistant';
|
||||
import { PluginGroupBlock } from './features/block-group';
|
||||
import { PluginContextMenu } from './features/context-menu';
|
||||
import { PluginCustomComponents } from './features/custom-components';
|
||||
import { DepartmentsPlugin } from './features/departments';
|
||||
import { EmbedPlugin } from './features/embed';
|
||||
@ -63,8 +61,6 @@ export class PluginCoreClient extends Plugin {
|
||||
await this.app.pm.add(DepartmentsPlugin);
|
||||
await this.app.pm.add(PluginPageStyle);
|
||||
await this.app.pm.add(PluginHeraVersion);
|
||||
await this.app.pm.add(PluginContextMenu);
|
||||
await this.app.pm.add(PluginAssistant);
|
||||
await this.app.pm.add(PluginPDF);
|
||||
await this.app.pm.add(PluginOutbound);
|
||||
// await this.app.pm.add(PluginModeHighlight);
|
||||
|
@ -638,6 +638,9 @@ importers:
|
||||
react-to-print:
|
||||
specifier: ^2.14.7
|
||||
version: 2.14.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react-transition-group:
|
||||
specifier: ^4.4.5
|
||||
version: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
sanitize-html:
|
||||
specifier: 2.10.0
|
||||
version: 2.10.0
|
||||
@ -8762,43 +8765,36 @@ packages:
|
||||
resolution: {integrity: sha512-p9rGKYkHdFMzhckOTFubfxgyIO1vw//7IIjBBRVzyZebWlzRLeNhqxuSaZ7kCEKVkm/kuC9fVRW9HkC/zNRG2w==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.14.1':
|
||||
resolution: {integrity: sha512-nDY6Yz5xS/Y4M2i9JLQd3Rofh5OR8Bn8qe3Mv/qCVpHFlwtZSBYSPaU4mrGazWkXrdQ98GB//H0BirGR/SKFSw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-powerpc64le-gnu@4.14.1':
|
||||
resolution: {integrity: sha512-im7HE4VBL+aDswvcmfx88Mp1soqL9OBsdDBU8NqDEYtkri0qV0THhQsvZtZeNNlLeCUQ16PZyv7cqutjDF35qw==}
|
||||
cpu: [ppc64le]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.14.1':
|
||||
resolution: {integrity: sha512-RWdiHuAxWmzPJgaHJdpvUUlDz8sdQz4P2uv367T2JocdDa98iRw2UjIJ4QxSyt077mXZT2X6pKfT2iYtVEvOFw==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.14.1':
|
||||
resolution: {integrity: sha512-VMgaGQ5zRX6ZqV/fas65/sUGc9cPmsntq2FiGmayW9KMNfWVG/j0BAqImvU4KTeOOgYSf1F+k6at1UfNONuNjA==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.14.1':
|
||||
resolution: {integrity: sha512-9Q7DGjZN+hTdJomaQ3Iub4m6VPu1r94bmK2z3UeWP3dGUecRC54tmVu9vKHTm1bOt3ASoYtEz6JSRLFzrysKlA==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.14.1':
|
||||
resolution: {integrity: sha512-JNEG/Ti55413SsreTguSx0LOVKX902OfXIKVg+TCXO6Gjans/k9O6ww9q3oLGjNDaTLxM+IHFMeXy/0RXL5R/g==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.14.1':
|
||||
resolution: {integrity: sha512-ryS22I9y0mumlLNwDFYZRDFLwWh3aKaC72CWjFcFvxK0U6v/mOkM5Up1bTbCRAhv3kEIwW2ajROegCIQViUCeA==}
|
||||
@ -9933,28 +9929,24 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@umijs/es-module-parser-linux-arm64-musl@0.0.7':
|
||||
resolution: {integrity: sha512-cqQffARWkmQ3n1RYNKZR3aD6X8YaP6u1maASjDgPQOpZMAlv/OSDrM/7iGujWTs0PD0haockNG9/DcP6lgPHMw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@umijs/es-module-parser-linux-x64-gnu@0.0.7':
|
||||
resolution: {integrity: sha512-PHrKHtT665Za0Ydjch4ACrNpRU+WIIden12YyF1CtMdhuLDSoU6UfdhF3NoDbgEUcXVDX/ftOqmj0SbH3R1uew==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@umijs/es-module-parser-linux-x64-musl@0.0.7':
|
||||
resolution: {integrity: sha512-cyZvUK5lcECLWzLp/eU1lFlCETcz+LEb+wrdARQSST1dgoIGZsT4cqM1WzYmdZNk3o883tiZizLt58SieEiHBQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@umijs/es-module-parser-win32-arm64-msvc@0.0.7':
|
||||
resolution: {integrity: sha512-V7WxnUI88RboSl0RWLNQeKBT7EDW35fW6Tn92zqtoHHxrhAIL9DtDyvC8REP4qTxeZ6Oej/Ax5I6IjsLx3yTOg==}
|
||||
@ -9995,14 +9987,12 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@umijs/mako-linux-x64-musl@0.7.5':
|
||||
resolution: {integrity: sha512-sfVOpUC1UIxHaUNrj7RahYeTaSrC97XEOqAxEAbeMG9tBKYOV7azGREJPsdePyGFdjF9mfsW69ljAuo6+MBxmQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@umijs/mako@0.7.5':
|
||||
resolution: {integrity: sha512-0mk/uNoltnX8d2ZD1Zi8/2En+zw69dPtxo1bpAyVDoruGI4djoi+u99s2vI9EORK+LGtTNuj8Sa/uyopKtsuPA==}
|
||||
@ -13605,7 +13595,6 @@ packages:
|
||||
|
||||
glob@5.0.15:
|
||||
resolution: {integrity: sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
|
||||
glob@7.1.6:
|
||||
resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==}
|
||||
@ -13613,7 +13602,6 @@ packages:
|
||||
|
||||
glob@7.1.7:
|
||||
resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
|
||||
glob@7.2.0:
|
||||
resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==}
|
||||
@ -14875,56 +14863,48 @@ packages:
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.26.0:
|
||||
resolution: {integrity: sha512-iJmZM7fUyVjH+POtdiCtExG+67TtPUTer7K/5A8DIfmPfrmeGvzfRyBltGhQz13Wi15K1lf2cPYoRaRh6vcwNA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-arm64-musl@1.22.1:
|
||||
resolution: {integrity: sha512-MCV6RuRpzXbunvzwY644iz8cw4oQxvW7oer9xPkdadYqlEyiJJ6wl7FyJOH7Q6ZYH4yjGAUCvxDBxPbnDu9ZVg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-linux-arm64-musl@1.26.0:
|
||||
resolution: {integrity: sha512-XxoEL++tTkyuvu+wq/QS8bwyTXZv2y5XYCMcWL45b8XwkiS8eEEEej9BkMGSRwxa5J4K+LDeIhLrS23CpQyfig==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-linux-x64-gnu@1.22.1:
|
||||
resolution: {integrity: sha512-RjNgpdM20VUXgV7us/VmlO3Vn2ZRiDnc3/bUxCVvySZWPiVPprpqW/QDWuzkGa+NCUf6saAM5CLsZLSxncXJwg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-x64-gnu@1.26.0:
|
||||
resolution: {integrity: sha512-1dkTfZQAYLj8MUSkd6L/+TWTG8V6Kfrzfa0T1fSlXCXQHrt1HC1/UepXHtKHDt/9yFwyoeayivxXAsApVxn6zA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-x64-musl@1.22.1:
|
||||
resolution: {integrity: sha512-ZgO4C7Rd6Hv/5MnyY2KxOYmIlzk4rplVolDt3NbkNR8DndnyX0Q5IR4acJWNTBICQ21j3zySzKbcJaiJpk/4YA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-linux-x64-musl@1.26.0:
|
||||
resolution: {integrity: sha512-yX3Rk9m00JGCUzuUhFEojY+jf/6zHs3XU8S8Vk+FRbnr4St7cjyMXdNjuA2LjiT8e7j8xHRCH8hyZ4H/btRE4A==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.26.0:
|
||||
resolution: {integrity: sha512-X/597/cFnCogy9VItj/+7Tgu5VLbAtDF7KZDPdSw0MaL6FL940th1y3HiOzFIlziVvAtbo0RB3NAae1Oofr+Tw==}
|
||||
@ -16935,7 +16915,6 @@ packages:
|
||||
engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
|
||||
deprecated: |-
|
||||
You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.
|
||||
|
||||
(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)
|
||||
|
||||
qrcode.react@3.1.0:
|
||||
|
Loading…
Reference in New Issue
Block a user