feat: improve active-toolbar
This commit is contained in:
1 parent
4b2d964921
commit
fd19725ab6
3 files changed
+167
-123
No files matched your search
Generated
+24
-24
@@ -10,12 +10,12 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "^0.26.24",
|
||||
"@plait/common": "^0.66.1",
|
||||
"@plait/core": "^0.66.1",
|
||||
"@plait/draw": "^0.66.1",
|
||||
"@plait/layouts": "^0.66.1",
|
||||
"@plait/mind": "^0.66.1",
|
||||
"@plait/text-plugins": "^0.66.1",
|
||||
"@plait/common": "^0.67.0",
|
||||
"@plait/core": "^0.67.0",
|
||||
"@plait/draw": "^0.67.0",
|
||||
"@plait/layouts": "^0.67.0",
|
||||
"@plait/mind": "^0.67.0",
|
||||
"@plait/text-plugins": "^0.67.0",
|
||||
"@radix-ui/react-popover": "^1.0.3",
|
||||
"ahooks": "^3.8.0",
|
||||
"browser-fs-access": "^0.35.0",
|
||||
@@ -3927,17 +3927,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@plait/common": {
|
||||
"version": "0.66.1",
|
||||
"resolved": "https://registry.npmjs.org/@plait/common/-/common-0.66.1.tgz",
|
||||
"integrity": "sha512-nnMUsO2n1bcdcUYITV+BFR5ymcnfnDLjCQJYN04bHT/F41BQMwDhL48mqxaPGwHoS4yZKGBPIasnrFP2Dmoicw==",
|
||||
"version": "0.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@plait/common/-/common-0.67.0.tgz",
|
||||
"integrity": "sha512-rHjIxtU+OKKDZ2YdN7tk2/w6+w+dmT51NaCaYhNWJnRp6delT9WITlazpmCdhIJcHuCoVwOMSBG31CkD8HMLkA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@plait/core": {
|
||||
"version": "0.66.1",
|
||||
"resolved": "https://registry.npmjs.org/@plait/core/-/core-0.66.1.tgz",
|
||||
"integrity": "sha512-ie/0SW0iy+hRJr3VuR1tZtJvvKIX6y1kVvALiraIKlZAeK1rmx2jajTvd5SQIES6giEeig/ekhUEKfN/sx5CUA==",
|
||||
"version": "0.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@plait/core/-/core-0.67.0.tgz",
|
||||
"integrity": "sha512-Yh3MczAmvATeoAmPewXDWwZKiGUklWzzEbU3RqWWN47hxsefGjweJ5TYhCLgDDWAfj3NpRD/eYMH4+KtsE4qXg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
@@ -3950,33 +3950,33 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@plait/draw": {
|
||||
"version": "0.66.1",
|
||||
"resolved": "https://registry.npmjs.org/@plait/draw/-/draw-0.66.1.tgz",
|
||||
"integrity": "sha512-X/wX2mtWUonXAZmQWkKE30U9FZeZYG4ixXHOMD/M2mU1U79LB/sktaqVZOGoDUB++2Ab+dXLdeyXM9OqFQR0eQ==",
|
||||
"version": "0.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@plait/draw/-/draw-0.67.0.tgz",
|
||||
"integrity": "sha512-QBq1VswNAOlszuHVs+LbaLYozOBZvsZAd8spaCjaAKkJNc745ZnQfCKUsVz5QsgkmR6thPvPU68lncMjPbskyQ==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@plait/layouts": {
|
||||
"version": "0.66.1",
|
||||
"resolved": "https://registry.npmjs.org/@plait/layouts/-/layouts-0.66.1.tgz",
|
||||
"integrity": "sha512-lSTzA5ipj0fQbdgdjrkKXQGjst6LCX65ZqfxT5b/lkWqTfY9+Wsl2w9vzL0VlyXX+VWUDTnWAY6cWP72gvH9SA==",
|
||||
"version": "0.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@plait/layouts/-/layouts-0.67.0.tgz",
|
||||
"integrity": "sha512-IWtiieUF1oIH3Tps+8kuJp2Dkxc8KItxtPtM1IHr79NbY23T5zfOzQGIyx0Wh77HWwKa+EIj9MYHeGSQOzcv3w==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@plait/mind": {
|
||||
"version": "0.66.1",
|
||||
"resolved": "https://registry.npmjs.org/@plait/mind/-/mind-0.66.1.tgz",
|
||||
"integrity": "sha512-NFSqUnH2yvYAKhwaAjB8c0w5AJt3wjBQPeS1hAGM1AK8r0I7W38CbXhj1spuC1P38Hg32hAuVLcya+/KhAhS+w==",
|
||||
"version": "0.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@plait/mind/-/mind-0.67.0.tgz",
|
||||
"integrity": "sha512-XYMobd9WuF/S5p/qMuYYDd94M4CjGeIWwsMr5DqbGAN/e/5XyUw8RMU/nsvVasoOOvxmykaXB0ri0Ptyjyp6dQ==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@plait/text-plugins": {
|
||||
"version": "0.66.1",
|
||||
"resolved": "https://registry.npmjs.org/@plait/text-plugins/-/text-plugins-0.66.1.tgz",
|
||||
"integrity": "sha512-A13FUCOG9OKU2alp/Zmf7MHcx3Xbm6iIVpRNVIQRp5/ko9n7eEzVS4LjkFqpBXx/77lhdTVROIiGFlyv3udOXA==",
|
||||
"version": "0.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@plait/text-plugins/-/text-plugins-0.67.0.tgz",
|
||||
"integrity": "sha512-b4k1YyNxOk4h0WlMyktEZdJUOTASA8+c1IDfBTaxXNJUA+5EwFgsAA//tNF3SEwbeCn0ZBQ3VuVl3RcyQxUosw==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
|
||||
+6
-6
@@ -10,12 +10,12 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "^0.26.24",
|
||||
"@plait/common": "^0.66.1",
|
||||
"@plait/core": "^0.66.1",
|
||||
"@plait/draw": "^0.66.1",
|
||||
"@plait/layouts": "^0.66.1",
|
||||
"@plait/mind": "^0.66.1",
|
||||
"@plait/text-plugins": "^0.66.1",
|
||||
"@plait/common": "^0.67.0",
|
||||
"@plait/core": "^0.67.0",
|
||||
"@plait/draw": "^0.67.0",
|
||||
"@plait/layouts": "^0.67.0",
|
||||
"@plait/mind": "^0.67.0",
|
||||
"@plait/text-plugins": "^0.67.0",
|
||||
"@radix-ui/react-popover": "^1.0.3",
|
||||
"ahooks": "^3.8.0",
|
||||
"browser-fs-access": "^0.35.0",
|
||||
|
||||
@@ -3,6 +3,8 @@ import { BackgroundColorIcon, FontColorIcon, StrokeIcon } from '../icons';
|
||||
import {
|
||||
ATTACHED_ELEMENT_CLASS_NAME,
|
||||
getSelectedElements,
|
||||
isDragging,
|
||||
isMovingElements,
|
||||
isSelectionMoving,
|
||||
Path,
|
||||
PlaitBoard,
|
||||
@@ -11,7 +13,7 @@ import {
|
||||
SELECTION_RECTANGLE_CLASS_NAME,
|
||||
Transforms,
|
||||
} from '@plait/core';
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useBoard } from '@plait/react-board';
|
||||
import { flip, offset, useFloating } from '@floating-ui/react';
|
||||
import { Island } from '../island';
|
||||
@@ -43,14 +45,14 @@ export type DrawActiveProps = {};
|
||||
export const ActiveToolbar: React.FC<DrawActiveProps> = ({}) => {
|
||||
const board = useBoard();
|
||||
const selectedElements = getSelectedElements(board);
|
||||
const isOpen = selectedElements.length > 0 && !isSelectionMoving(board);
|
||||
|
||||
const { viewport, selection } = board;
|
||||
const [movingOrDragging, setMovingOrDragging] = useState(false);
|
||||
const movingOrDraggingRef = useRef(movingOrDragging);
|
||||
const open = selectedElements.length > 0 && !isSelectionMoving(board);
|
||||
const { viewport, selection, children } = board;
|
||||
const { refs, floatingStyles } = useFloating({
|
||||
placement: 'top',
|
||||
middleware: [offset(32), flip()],
|
||||
});
|
||||
|
||||
let state: {
|
||||
fill: string | undefined;
|
||||
strokeColor?: string;
|
||||
@@ -61,8 +63,7 @@ export const ActiveToolbar: React.FC<DrawActiveProps> = ({}) => {
|
||||
} = {
|
||||
fill: 'red',
|
||||
};
|
||||
|
||||
if (isOpen) {
|
||||
if (open && !movingOrDragging) {
|
||||
const hasFill = selectedElements.some((value) =>
|
||||
hasFillProperty(board, value)
|
||||
);
|
||||
@@ -71,9 +72,8 @@ export const ActiveToolbar: React.FC<DrawActiveProps> = ({}) => {
|
||||
);
|
||||
state = { ...getElementState(board), hasFill, hasFontColor: hasText };
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
if (open) {
|
||||
let selectionG = PlaitBoard.getBoardContainer(board).querySelector(
|
||||
`.${SELECTION_RECTANGLE_BOUNDING_CLASS_NAME}`
|
||||
);
|
||||
@@ -82,104 +82,148 @@ export const ActiveToolbar: React.FC<DrawActiveProps> = ({}) => {
|
||||
`.${SELECTION_RECTANGLE_CLASS_NAME}`
|
||||
);
|
||||
}
|
||||
refs.setPositionReference(selectionG);
|
||||
if (selectionG) {
|
||||
const rect = selectionG?.getBoundingClientRect();
|
||||
refs.setPositionReference({
|
||||
getBoundingClientRect() {
|
||||
return {
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
top: rect.top,
|
||||
left: rect.left,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [viewport, selection]);
|
||||
}, [viewport, selection, children, movingOrDragging]);
|
||||
|
||||
useEffect(() => {
|
||||
movingOrDraggingRef.current = movingOrDragging;
|
||||
}, [movingOrDragging]);
|
||||
|
||||
useEffect(() => {
|
||||
const { pointerUp, pointerMove } = board;
|
||||
|
||||
board.pointerMove = (event: PointerEvent) => {
|
||||
if (
|
||||
(isMovingElements(board) || isDragging(board)) &&
|
||||
!movingOrDraggingRef.current
|
||||
) {
|
||||
setMovingOrDragging(true);
|
||||
}
|
||||
pointerMove(event);
|
||||
};
|
||||
|
||||
board.pointerUp = (event: PointerEvent) => {
|
||||
if (
|
||||
movingOrDraggingRef.current &&
|
||||
(isMovingElements(board) || isDragging(board))
|
||||
) {
|
||||
setMovingOrDragging(false);
|
||||
}
|
||||
pointerUp(event);
|
||||
};
|
||||
|
||||
return () => {
|
||||
board.pointerUp = pointerUp;
|
||||
board.pointerMove = pointerMove;
|
||||
};
|
||||
}, [board]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isOpen && (
|
||||
<div ref={refs.setFloating} style={floatingStyles}>
|
||||
<Island
|
||||
padding={1}
|
||||
className={classNames(
|
||||
'active-toolbar',
|
||||
ATTACHED_ELEMENT_CLASS_NAME
|
||||
)}
|
||||
>
|
||||
<Stack.Row gap={1}>
|
||||
{state.hasFontColor && (
|
||||
<ActiveFontColorToolButton
|
||||
key={0}
|
||||
currentColor={state.fontColor}
|
||||
title={`Font Color`}
|
||||
fontColorIcon={
|
||||
<FontColorIcon currentColor={state.marks?.color} />
|
||||
}
|
||||
onSelect={(selectedColor: string) => {
|
||||
TextTransforms.setTextColor(
|
||||
board,
|
||||
selectedColor,
|
||||
undefined,
|
||||
getSelectedTableCellsEditor(board)
|
||||
);
|
||||
}}
|
||||
></ActiveFontColorToolButton>
|
||||
)}
|
||||
<ActiveColorToolButton
|
||||
key={1}
|
||||
currentColor={state.strokeColor}
|
||||
title={`Stroke`}
|
||||
transparentIcon={StrokeIcon}
|
||||
{open && !movingOrDragging && (
|
||||
<Island
|
||||
padding={1}
|
||||
className={classNames('active-toolbar', ATTACHED_ELEMENT_CLASS_NAME)}
|
||||
ref={refs.setFloating}
|
||||
style={floatingStyles}
|
||||
>
|
||||
<Stack.Row gap={1}>
|
||||
{state.hasFontColor && (
|
||||
<ActiveFontColorToolButton
|
||||
key={0}
|
||||
currentColor={state.fontColor}
|
||||
title={`Font Color`}
|
||||
fontColorIcon={
|
||||
<FontColorIcon currentColor={state.marks?.color} />
|
||||
}
|
||||
onSelect={(selectedColor: string) => {
|
||||
console.log(`selectedColor: ${selectedColor}`);
|
||||
PropertyTransforms.setStrokeColor(board, selectedColor, {
|
||||
TextTransforms.setTextColor(
|
||||
board,
|
||||
selectedColor,
|
||||
undefined,
|
||||
getSelectedTableCellsEditor(board)
|
||||
);
|
||||
}}
|
||||
></ActiveFontColorToolButton>
|
||||
)}
|
||||
<ActiveColorToolButton
|
||||
key={1}
|
||||
currentColor={state.strokeColor}
|
||||
title={`Stroke`}
|
||||
transparentIcon={StrokeIcon}
|
||||
onSelect={(selectedColor: string) => {
|
||||
console.log(`selectedColor: ${selectedColor}`);
|
||||
PropertyTransforms.setStrokeColor(board, selectedColor, {
|
||||
getMemorizeKey,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<label
|
||||
className={classNames('stroke-label', 'color-label', {
|
||||
'color-white': state.fill === '#FFFFFF',
|
||||
})}
|
||||
style={{ borderColor: state.strokeColor }}
|
||||
></label>
|
||||
</ActiveColorToolButton>
|
||||
{state.hasFill && (
|
||||
<ActiveColorToolButton
|
||||
key={2}
|
||||
currentColor={state.fill}
|
||||
title={`Fill Color`}
|
||||
transparentIcon={BackgroundColorIcon}
|
||||
onSelect={(selectedColor: string) => {
|
||||
PropertyTransforms.setFillColor(board, selectedColor, {
|
||||
getMemorizeKey,
|
||||
callback: (element: PlaitElement, path: Path) => {
|
||||
const tableElement =
|
||||
PlaitDrawElement.isElementByTable(element);
|
||||
if (tableElement) {
|
||||
DrawTransforms.setTableFill(
|
||||
board,
|
||||
element,
|
||||
selectedColor,
|
||||
path
|
||||
);
|
||||
} else {
|
||||
if (isDrawElementClosed(element as PlaitDrawElement)) {
|
||||
Transforms.setNode(
|
||||
board,
|
||||
{ fill: selectedColor },
|
||||
path
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<label
|
||||
className={classNames('stroke-label', 'color-label', {
|
||||
className={classNames('fill-label', 'color-label', {
|
||||
'color-white': state.fill === '#FFFFFF',
|
||||
})}
|
||||
style={{ borderColor: state.strokeColor }}
|
||||
style={{ backgroundColor: state.fill }}
|
||||
></label>
|
||||
</ActiveColorToolButton>
|
||||
{state.hasFill && (
|
||||
<ActiveColorToolButton
|
||||
key={2}
|
||||
currentColor={state.fill}
|
||||
title={`Fill Color`}
|
||||
transparentIcon={BackgroundColorIcon}
|
||||
onSelect={(selectedColor: string) => {
|
||||
PropertyTransforms.setFillColor(board, selectedColor, {
|
||||
getMemorizeKey,
|
||||
callback: (element: PlaitElement, path: Path) => {
|
||||
const tableElement =
|
||||
PlaitDrawElement.isElementByTable(element);
|
||||
if (tableElement) {
|
||||
DrawTransforms.setTableFill(
|
||||
board,
|
||||
element,
|
||||
selectedColor,
|
||||
path
|
||||
);
|
||||
} else {
|
||||
if (
|
||||
isDrawElementClosed(element as PlaitDrawElement)
|
||||
) {
|
||||
Transforms.setNode(
|
||||
board,
|
||||
{ fill: selectedColor },
|
||||
path
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<label
|
||||
className={classNames('fill-label', 'color-label', {
|
||||
'color-white': state.fill === '#FFFFFF',
|
||||
})}
|
||||
style={{ backgroundColor: state.fill }}
|
||||
></label>
|
||||
</ActiveColorToolButton>
|
||||
)}
|
||||
</Stack.Row>
|
||||
</Island>
|
||||
</div>
|
||||
)}
|
||||
</Stack.Row>
|
||||
</Island>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user