feat: searchJump and calculator
Reviewed-on: daoyoucloud/tachybase#1246
This commit is contained in:
parent
c484545c39
commit
6af6e9a1f8
@ -19,6 +19,7 @@ import {
|
||||
SchemaComponent,
|
||||
useACLRoleContext,
|
||||
useAdminSchemaUid,
|
||||
useApp,
|
||||
useDocumentTitle,
|
||||
useRequest,
|
||||
useSystemSettings,
|
||||
@ -403,6 +404,7 @@ const SetThemeOfHeaderSubmenu = ({ children }) => {
|
||||
export const InternalAdminLayout = (props: any) => {
|
||||
const sideMenuRef = useRef<HTMLDivElement>();
|
||||
const result = useSystemSettings();
|
||||
const app = useApp();
|
||||
const params = useParams<any>();
|
||||
const { styles } = useStyles();
|
||||
return (
|
||||
@ -411,7 +413,12 @@ export const InternalAdminLayout = (props: any) => {
|
||||
<Layout.Header className={styles.header}>
|
||||
<div className={styles.headerA}>
|
||||
<div className={styles.headerB}>
|
||||
<div className={styles.titleContainer}>
|
||||
<div
|
||||
className={styles.titleContainer}
|
||||
onClick={() => {
|
||||
location.href = app.adminUrl;
|
||||
}}
|
||||
>
|
||||
<img className={styles.logo} src={result?.data?.data?.logo?.url} />
|
||||
<h1 className={styles.title}>{result?.data?.data?.title}</h1>
|
||||
</div>
|
||||
|
@ -48,6 +48,7 @@ export * from './modules/blocks/data-blocks/form';
|
||||
export * from './modules/blocks/data-blocks/table';
|
||||
export * from './modules/blocks/data-blocks/table-selector';
|
||||
export * from 'react-i18next';
|
||||
export { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
export * from './modules/blocks/useParentRecordCommon';
|
||||
|
||||
|
@ -1,60 +1,45 @@
|
||||
import React from 'react';
|
||||
import { css, useDesignable } from '@tachybase/client';
|
||||
import { useDesignable, useHotkeys } from '@tachybase/client';
|
||||
|
||||
import { CalculatorOutlined, CommentOutlined, HighlightOutlined, ToolOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
CalculatorOutlined,
|
||||
CommentOutlined,
|
||||
HighlightOutlined,
|
||||
SearchOutlined,
|
||||
ToolOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { FloatButton } from 'antd';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
import { useContextMenu } from '../context-menu/useContextMenu';
|
||||
import { CalculatorWrapper } from './calculator/Calculator';
|
||||
import { useCalculator } from './calculator/CalculatorProvider';
|
||||
import { useSearchAndJump } from './search-and-jump';
|
||||
|
||||
export const AssistantProvider = ({ children }) => {
|
||||
const { designable, setDesignable } = useDesignable();
|
||||
const { visible, setVisible } = useCalculator();
|
||||
const { setOpen } = useSearchAndJump();
|
||||
|
||||
// 快捷键切换编辑状态
|
||||
useHotkeys('Ctrl+Shift+U', () => setDesignable(!designable), [designable]);
|
||||
useHotkeys('Ctrl+K', () => setOpen((open) => !open), []);
|
||||
useHotkeys('Cmd+K', () => setOpen((open) => !open), []);
|
||||
|
||||
const { contextMenuEnabled, setContextMenuEnable } = useContextMenu();
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<FloatButton.Group trigger="hover" type="default" style={{ right: 24, zIndex: 1250 }} icon={<ToolOutlined />}>
|
||||
<FloatButton icon={<SearchOutlined />} onClick={() => setOpen(true)} />
|
||||
<FloatButton
|
||||
icon={<HighlightOutlined />}
|
||||
type={designable ? 'primary' : 'default'}
|
||||
onClick={() => setDesignable(!designable)}
|
||||
/>
|
||||
<FloatButton
|
||||
type={visible ? 'primary' : 'default'}
|
||||
icon={<CalculatorOutlined />}
|
||||
onClick={() => {
|
||||
createPortal(
|
||||
// <div
|
||||
// className={css`
|
||||
// position: fixed;
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
// width: 1000px;
|
||||
// height: 1000px;
|
||||
// z-index: 9999;
|
||||
// background-color: red;
|
||||
// `}
|
||||
// >
|
||||
// <div />
|
||||
// {/* <CalculatorWrapper /> */}
|
||||
// </div>,
|
||||
<div
|
||||
className={css`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 9000;
|
||||
display: 'flex';
|
||||
flex-direction: column;
|
||||
`}
|
||||
>
|
||||
dsfasdfasfasdfadsf
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
alert('---');
|
||||
setVisible((visible) => !visible);
|
||||
}}
|
||||
/>
|
||||
<FloatButton icon={<CommentOutlined />} />
|
||||
|
@ -5,28 +5,36 @@ export const AutoScalingText = ({ children }) => {
|
||||
const [scale, setScale] = useState<number>(1);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current) {
|
||||
return;
|
||||
}
|
||||
const observer = new ResizeObserver((entries) => {
|
||||
const node = entries[0].target as HTMLDivElement;
|
||||
const parentNode = node.parentNode as HTMLDivElement;
|
||||
|
||||
const availableWidth = parentNode.offsetWidth;
|
||||
const actualWidth = node.offsetWidth;
|
||||
const actualScale = availableWidth / actualWidth;
|
||||
|
||||
setScale((scale) => {
|
||||
if (actualScale < 1) {
|
||||
return actualScale;
|
||||
} else if (scale < 1) {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
});
|
||||
const node = ref.current;
|
||||
const parentNode = node.parentNode;
|
||||
|
||||
// @ts-ignore
|
||||
const availableWidth = parentNode.offsetWidth;
|
||||
const actualWidth = node.offsetWidth;
|
||||
const actualScale = availableWidth / actualWidth;
|
||||
|
||||
if (scale === actualScale) return;
|
||||
|
||||
if (actualScale < 1) {
|
||||
setScale(actualScale);
|
||||
} else if (scale < 1) {
|
||||
setScale(1);
|
||||
}
|
||||
}, [scale]);
|
||||
observer.observe(node);
|
||||
return () => observer.unobserve(node);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="auto-scaling-text" style={{ transform: `scale(${scale},${scale})` }} ref={ref}>
|
||||
<div
|
||||
className="auto-scaling-text"
|
||||
style={{
|
||||
transition: 'transform 0.5s ease',
|
||||
transform: `scale(${scale},${scale})`,
|
||||
}}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { css } from '@tachybase/client';
|
||||
|
||||
import { CalculatorDisplay } from './CalculatorDisplay';
|
||||
import { CalculatorKey } from './CalculatorKey';
|
||||
@ -12,48 +13,33 @@ const CalculatorOperations = {
|
||||
'=': (prevValue, nextValue) => nextValue,
|
||||
};
|
||||
|
||||
class Calculator extends React.Component {
|
||||
state = {
|
||||
value: null,
|
||||
displayValue: '0',
|
||||
operator: null,
|
||||
waitingForOperand: false,
|
||||
const Calculator = React.forwardRef<{ className: string }, any>(({ className }, ref) => {
|
||||
const [value, setValue] = useState(null);
|
||||
const [displayValue, setDisplayValue] = useState('0');
|
||||
const [operator, setOperator] = useState(null);
|
||||
const [waitingForOperand, setWaitingForOperand] = useState(false);
|
||||
|
||||
const clearAll = () => {
|
||||
setValue(null);
|
||||
setDisplayValue('0');
|
||||
setOperator(null);
|
||||
setWaitingForOperand(false);
|
||||
};
|
||||
|
||||
clearAll() {
|
||||
this.setState({
|
||||
value: null,
|
||||
displayValue: '0',
|
||||
operator: null,
|
||||
waitingForOperand: false,
|
||||
});
|
||||
}
|
||||
const clearDisplay = () => {
|
||||
setDisplayValue('0');
|
||||
};
|
||||
|
||||
clearDisplay() {
|
||||
this.setState({
|
||||
displayValue: '0',
|
||||
});
|
||||
}
|
||||
const clearLastChar = () => {
|
||||
setDisplayValue(displayValue.substring(0, displayValue.length - 1) || '0');
|
||||
};
|
||||
|
||||
clearLastChar() {
|
||||
const { displayValue } = this.state;
|
||||
|
||||
this.setState({
|
||||
displayValue: displayValue.substring(0, displayValue.length - 1) || '0',
|
||||
});
|
||||
}
|
||||
|
||||
toggleSign() {
|
||||
const { displayValue } = this.state;
|
||||
const toggleSign = () => {
|
||||
const newValue = parseFloat(displayValue) * -1;
|
||||
setDisplayValue(String(newValue));
|
||||
};
|
||||
|
||||
this.setState({
|
||||
displayValue: String(newValue),
|
||||
});
|
||||
}
|
||||
|
||||
inputPercent() {
|
||||
const { displayValue } = this.state;
|
||||
const inputPercent = () => {
|
||||
const currentValue = parseFloat(displayValue);
|
||||
|
||||
if (currentValue === 0) return;
|
||||
@ -61,187 +47,155 @@ class Calculator extends React.Component {
|
||||
const fixedDigits = displayValue.replace(/^-?\d*\.?/, '');
|
||||
const newValue = parseFloat(displayValue) / 100;
|
||||
|
||||
this.setState({
|
||||
displayValue: String(newValue.toFixed(fixedDigits.length + 2)),
|
||||
});
|
||||
}
|
||||
|
||||
inputDot() {
|
||||
const { displayValue } = this.state;
|
||||
setDisplayValue(String(newValue.toFixed(fixedDigits.length + 2)));
|
||||
};
|
||||
|
||||
const inputDot = () => {
|
||||
if (!/\./.test(displayValue)) {
|
||||
this.setState({
|
||||
displayValue: displayValue + '.',
|
||||
waitingForOperand: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
inputDigit(digit) {
|
||||
const { displayValue, waitingForOperand } = this.state;
|
||||
|
||||
if (waitingForOperand) {
|
||||
this.setState({
|
||||
displayValue: String(digit),
|
||||
waitingForOperand: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
displayValue: displayValue === '0' ? String(digit) : displayValue + digit,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
performOperation(nextOperator) {
|
||||
const { value, displayValue, operator } = this.state;
|
||||
const inputValue = parseFloat(displayValue);
|
||||
|
||||
if (value == null) {
|
||||
this.setState({
|
||||
value: inputValue,
|
||||
});
|
||||
} else if (operator) {
|
||||
const currentValue = value || 0;
|
||||
const newValue = CalculatorOperations[operator](currentValue, inputValue);
|
||||
|
||||
this.setState({
|
||||
value: newValue,
|
||||
displayValue: String(newValue),
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
waitingForOperand: true,
|
||||
operator: nextOperator,
|
||||
});
|
||||
}
|
||||
|
||||
handleKeyDown = (event) => {
|
||||
let { key } = event;
|
||||
|
||||
if (key === 'Enter') key = '=';
|
||||
|
||||
if (/\d/.test(key)) {
|
||||
event.preventDefault();
|
||||
this.inputDigit(parseInt(key, 10));
|
||||
} else if (key in CalculatorOperations) {
|
||||
event.preventDefault();
|
||||
this.performOperation(key);
|
||||
} else if (key === '.') {
|
||||
event.preventDefault();
|
||||
this.inputDot();
|
||||
} else if (key === '%') {
|
||||
event.preventDefault();
|
||||
this.inputPercent();
|
||||
} else if (key === 'Backspace') {
|
||||
event.preventDefault();
|
||||
this.clearLastChar();
|
||||
} else if (key === 'Clear') {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.state.displayValue !== '0') {
|
||||
this.clearDisplay();
|
||||
} else {
|
||||
this.clearAll();
|
||||
}
|
||||
setDisplayValue(displayValue + '.');
|
||||
setWaitingForOperand(false);
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('keydown', this.handleKeyDown);
|
||||
}
|
||||
const inputDigit = (digit) => {
|
||||
if (waitingForOperand) {
|
||||
setDisplayValue(String(digit));
|
||||
setWaitingForOperand(false);
|
||||
} else {
|
||||
setDisplayValue(displayValue === '0' ? String(digit) : displayValue + digit);
|
||||
}
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keydown', this.handleKeyDown);
|
||||
}
|
||||
const performOperation = (nextOperator) => {
|
||||
const inputValue = parseFloat(displayValue);
|
||||
|
||||
render() {
|
||||
const { displayValue } = this.state;
|
||||
if (value == null) {
|
||||
setValue(inputValue);
|
||||
} else if (operator) {
|
||||
const currentValue = value || 0;
|
||||
const newValue = CalculatorOperations[operator](currentValue, inputValue);
|
||||
setValue(newValue);
|
||||
setDisplayValue(String(newValue));
|
||||
}
|
||||
setWaitingForOperand(true);
|
||||
setOperator(nextOperator);
|
||||
};
|
||||
|
||||
const clearDisplay = displayValue !== '0';
|
||||
const clearText = clearDisplay ? 'C' : 'AC';
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event) => {
|
||||
let { key } = event;
|
||||
|
||||
return (
|
||||
<div className={this.props.className}>
|
||||
<div className="calculator">
|
||||
<CalculatorDisplay value={displayValue} />
|
||||
<div className="calculator-keypad">
|
||||
<div className="input-keys">
|
||||
<div className="function-keys">
|
||||
<CalculatorKey
|
||||
className="key-clear"
|
||||
onPress={() => (clearDisplay ? this.clearDisplay() : this.clearAll())}
|
||||
>
|
||||
{clearText}
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-sign" onPress={() => this.toggleSign()}>
|
||||
±
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-percent" onPress={() => this.inputPercent()}>
|
||||
%
|
||||
</CalculatorKey>
|
||||
</div>
|
||||
<div className="digit-keys">
|
||||
<CalculatorKey className="key-0" onPress={() => this.inputDigit(0)}>
|
||||
0
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-dot" onPress={() => this.inputDot()}>
|
||||
●
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-1" onPress={() => this.inputDigit(1)}>
|
||||
1
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-2" onPress={() => this.inputDigit(2)}>
|
||||
2
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-3" onPress={() => this.inputDigit(3)}>
|
||||
3
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-4" onPress={() => this.inputDigit(4)}>
|
||||
4
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-5" onPress={() => this.inputDigit(5)}>
|
||||
5
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-6" onPress={() => this.inputDigit(6)}>
|
||||
6
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-7" onPress={() => this.inputDigit(7)}>
|
||||
7
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-8" onPress={() => this.inputDigit(8)}>
|
||||
8
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-9" onPress={() => this.inputDigit(9)}>
|
||||
9
|
||||
</CalculatorKey>
|
||||
</div>
|
||||
</div>
|
||||
<div className="operator-keys">
|
||||
<CalculatorKey className="key-divide" onPress={() => this.performOperation('/')}>
|
||||
÷
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-multiply" onPress={() => this.performOperation('*')}>
|
||||
×
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-subtract" onPress={() => this.performOperation('-')}>
|
||||
−
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-add" onPress={() => this.performOperation('+')}>
|
||||
+
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-equals" onPress={() => this.performOperation('=')}>
|
||||
=
|
||||
</CalculatorKey>
|
||||
</div>
|
||||
if (key === 'Enter') key = '=';
|
||||
|
||||
if (/\d/.test(key)) {
|
||||
event.preventDefault();
|
||||
inputDigit(parseInt(key, 10));
|
||||
} else if (key in CalculatorOperations) {
|
||||
event.preventDefault();
|
||||
performOperation(key);
|
||||
} else if (key === '.') {
|
||||
event.preventDefault();
|
||||
inputDot();
|
||||
} else if (key === '%') {
|
||||
event.preventDefault();
|
||||
inputPercent();
|
||||
} else if (key === 'Backspace') {
|
||||
event.preventDefault();
|
||||
clearLastChar();
|
||||
} else if (key === 'Clear') {
|
||||
event.preventDefault();
|
||||
|
||||
if (displayValue !== '0') {
|
||||
clearDisplay();
|
||||
} else {
|
||||
clearAll();
|
||||
}
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
});
|
||||
|
||||
const clearKey = displayValue !== '0';
|
||||
const clearText = clearKey ? 'C' : 'AC';
|
||||
|
||||
return (
|
||||
<div className="calculator" ref={ref as any}>
|
||||
<CalculatorDisplay value={displayValue} />
|
||||
<div className="calculator-keypad">
|
||||
<div className="input-keys">
|
||||
<div className="function-keys">
|
||||
<CalculatorKey className="key-clear" onPress={() => (clearKey ? clearDisplay() : clearAll())}>
|
||||
{clearText}
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-sign" onPress={() => toggleSign()}>
|
||||
±
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-percent" onPress={() => inputPercent()}>
|
||||
%
|
||||
</CalculatorKey>
|
||||
</div>
|
||||
<div className="digit-keys">
|
||||
<CalculatorKey className="key-0" onPress={() => inputDigit(0)}>
|
||||
0
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-dot" onPress={() => inputDot()}>
|
||||
●
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-1" onPress={() => inputDigit(1)}>
|
||||
1
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-2" onPress={() => inputDigit(2)}>
|
||||
2
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-3" onPress={() => inputDigit(3)}>
|
||||
3
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-4" onPress={() => inputDigit(4)}>
|
||||
4
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-5" onPress={() => inputDigit(5)}>
|
||||
5
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-6" onPress={() => inputDigit(6)}>
|
||||
6
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-7" onPress={() => inputDigit(7)}>
|
||||
7
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-8" onPress={() => inputDigit(8)}>
|
||||
8
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-9" onPress={() => inputDigit(9)}>
|
||||
9
|
||||
</CalculatorKey>
|
||||
</div>
|
||||
</div>
|
||||
<div className="operator-keys">
|
||||
<CalculatorKey className="key-divide" onPress={() => performOperation('/')}>
|
||||
÷
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-multiply" onPress={() => performOperation('*')}>
|
||||
×
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-subtract" onPress={() => performOperation('-')}>
|
||||
−
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-add" onPress={() => performOperation('+')}>
|
||||
+
|
||||
</CalculatorKey>
|
||||
<CalculatorKey className="key-equals" onPress={() => performOperation('=')}>
|
||||
=
|
||||
</CalculatorKey>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const CalculatorWrapper = () => {
|
||||
export const CalculatorWrapper = React.forwardRef((props, ref) => {
|
||||
const { styles } = useStyles();
|
||||
return <Calculator className={styles.container} />;
|
||||
};
|
||||
return <Calculator ref={ref as any} className={styles.container} />;
|
||||
});
|
||||
|
@ -0,0 +1,38 @@
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
import { CalculatorWrapper } from './Calculator';
|
||||
import { Draggable } from './Draggable';
|
||||
import { useStyles } from './style';
|
||||
|
||||
export interface CalculatorProps {
|
||||
visible: any;
|
||||
setVisible: any;
|
||||
}
|
||||
|
||||
export const CalculatorContext = createContext<Partial<CalculatorProps>>({});
|
||||
export const useCalculator = () => {
|
||||
return useContext(CalculatorContext);
|
||||
};
|
||||
|
||||
export const CalculatorProvider = ({ children }) => {
|
||||
const [visible, setVisible] = useState<boolean>(false);
|
||||
const { styles } = useStyles();
|
||||
return (
|
||||
<CalculatorContext.Provider value={{ visible, setVisible }}>
|
||||
{children}
|
||||
|
||||
{visible
|
||||
? createPortal(
|
||||
<div className={styles.container}>
|
||||
<Draggable>
|
||||
<CalculatorWrapper />
|
||||
</Draggable>
|
||||
</div>,
|
||||
document.body,
|
||||
)
|
||||
: null}
|
||||
</CalculatorContext.Provider>
|
||||
);
|
||||
};
|
@ -0,0 +1,59 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
|
||||
export const Draggable = ({
|
||||
children,
|
||||
updateTransform = (transformStr, tx, ty, tdom) => {
|
||||
tdom.style.transform = transformStr;
|
||||
},
|
||||
}) => {
|
||||
const ref = useRef<any>();
|
||||
const positionRef = useRef({
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
dx: 0,
|
||||
dy: 0,
|
||||
tx: 0,
|
||||
ty: 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const node = ref.current;
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
const start = (event) => {
|
||||
if (event.button !== 0) {
|
||||
//只允许左键,右键问题在于不选择conextmenu就不会触发mouseup事件
|
||||
return;
|
||||
}
|
||||
document.addEventListener('mousemove', docMove);
|
||||
positionRef.current.startX = event.pageX - positionRef.current.dx;
|
||||
positionRef.current.startY = event.pageY - positionRef.current.dy;
|
||||
};
|
||||
const docMove = (event) => {
|
||||
const tx = event.pageX - positionRef.current.startX;
|
||||
const ty = event.pageY - positionRef.current.startY;
|
||||
const transformStr = `translate(${tx}px,${ty}px)`;
|
||||
updateTransform(transformStr, tx, ty, node);
|
||||
positionRef.current.dx = tx;
|
||||
positionRef.current.dy = ty;
|
||||
};
|
||||
const docMouseUp = (event) => {
|
||||
document.removeEventListener('mousemove', docMove);
|
||||
};
|
||||
|
||||
node.addEventListener('mousedown', start);
|
||||
//用document移除对mousemove事件的监听
|
||||
document.addEventListener('mouseup', docMouseUp);
|
||||
return () => {
|
||||
node.removeEventListener('mousedown', start);
|
||||
document.removeEventListener('mouseup', docMouseUp);
|
||||
};
|
||||
}, [updateTransform]);
|
||||
|
||||
const newStyle = { ...children.props.style, cursor: 'move', userSelect: 'none' };
|
||||
return React.cloneElement(React.Children.only(children), {
|
||||
ref,
|
||||
style: newStyle,
|
||||
});
|
||||
};
|
@ -3,6 +3,14 @@ import { createStyles } from '@tachybase/client';
|
||||
export const useStyles = createStyles(({ css }) => {
|
||||
return {
|
||||
container: css`
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 320px;
|
||||
height: 520px;
|
||||
z-index: 1350;
|
||||
|
||||
button {
|
||||
display: block;
|
||||
background: none;
|
||||
@ -20,20 +28,6 @@ export const useStyles = createStyles(({ css }) => {
|
||||
box-shadow: inset 0px 0px 80px 0px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
height: 100vh;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#app {
|
||||
width: 320px;
|
||||
height: 520px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.calculator {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -1,9 +1,13 @@
|
||||
import { Plugin } from '@tachybase/client';
|
||||
|
||||
import { AssistantProvider } from './Assistant.provider';
|
||||
import { CalculatorProvider } from './calculator/CalculatorProvider';
|
||||
import { SearchAndJumpProvider } from './search-and-jump';
|
||||
|
||||
export class PluginAssistant extends Plugin {
|
||||
async load() {
|
||||
this.app.use(SearchAndJumpProvider);
|
||||
this.app.use(CalculatorProvider);
|
||||
this.app.use(AssistantProvider);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { Input, InputRef, Modal } from 'antd';
|
||||
|
||||
export const SearchAndJumpContext = createContext({
|
||||
open: false,
|
||||
setOpen: (open: boolean | ((open: boolean) => boolean)) => {},
|
||||
});
|
||||
|
||||
export const SearchAndJumpProvider = ({ children }) => {
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<SearchAndJumpContext.Provider value={{ open, setOpen }}>
|
||||
{children}
|
||||
<SearchAndJump open={open} setOpen={setOpen} />
|
||||
</SearchAndJumpContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useSearchAndJump = () => {
|
||||
return useContext(SearchAndJumpContext);
|
||||
};
|
||||
|
||||
export const SearchAndJump = ({ open, setOpen }) => {
|
||||
const inputRef = useRef<InputRef>();
|
||||
useEffect(() => {
|
||||
if (open && inputRef.current) {
|
||||
setTimeout(() => {
|
||||
inputRef.current.focus();
|
||||
}, 0);
|
||||
}
|
||||
}, [open, inputRef]);
|
||||
|
||||
const handleOk = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={<Input style={{ width: '100%' }} autoFocus ref={inputRef} />}
|
||||
closable={false}
|
||||
open={open}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
footer={null}
|
||||
>
|
||||
<p></p>
|
||||
</Modal>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue
Block a user