mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-18 05:31:31 +03:00
Merge pull request #64 from toeverything/feat/frame-background-color
feat: support set frame background color
This commit is contained in:
commit
fbe9add924
@ -11,6 +11,7 @@ import { StrokeLineStyleConfig } from './stroke-line-style-config';
|
||||
import { Group, UnGroup } from './GroupOperation';
|
||||
import { DeleteShapes } from './DeleteOperation';
|
||||
import { Lock, Unlock } from './LockOperation';
|
||||
import { FrameFillColorConfig } from './FrameFillColorConfig';
|
||||
|
||||
export const CommandPanel: FC<{ app: TldrawApp }> = ({ app }) => {
|
||||
const state = app.useStore();
|
||||
@ -51,6 +52,13 @@ export const CommandPanel: FC<{ app: TldrawApp }> = ({ app }) => {
|
||||
shapes={config.fill.selectedShapes}
|
||||
/>
|
||||
) : null,
|
||||
frameFill: config.frameFill.selectedShapes.length ? (
|
||||
<FrameFillColorConfig
|
||||
key="fill"
|
||||
app={app}
|
||||
shapes={config.frameFill.selectedShapes}
|
||||
/>
|
||||
) : null,
|
||||
font: config.font.selectedShapes.length ? (
|
||||
<FontSizeConfig
|
||||
key="font"
|
||||
|
@ -0,0 +1,89 @@
|
||||
import type { FC } from 'react';
|
||||
import type { TldrawApp } from '@toeverything/components/board-state';
|
||||
import type { TDShape } from '@toeverything/components/board-types';
|
||||
import {
|
||||
Popover,
|
||||
Tooltip,
|
||||
IconButton,
|
||||
useTheme,
|
||||
} from '@toeverything/components/ui';
|
||||
import {
|
||||
ShapeColorNoneIcon,
|
||||
ShapeColorDuotoneIcon,
|
||||
} from '@toeverything/components/icons';
|
||||
import { countBy, maxBy } from '@toeverything/utils';
|
||||
import { getShapeIds } from './utils';
|
||||
import { Palette } from '../palette';
|
||||
|
||||
interface BorderColorConfigProps {
|
||||
app: TldrawApp;
|
||||
shapes: TDShape[];
|
||||
}
|
||||
|
||||
type ColorType = 'none' | string;
|
||||
|
||||
const _colors: ColorType[] = [
|
||||
'rgba(255, 133, 137, 0.5)',
|
||||
'rgba(255, 159, 101, 0.5)',
|
||||
'rgba(255, 251, 69, 0.5)',
|
||||
'rgba(64, 255, 138, 0.5)',
|
||||
'rgba(26, 252, 255, 0.5)',
|
||||
'rgba(198, 156, 255, 0.5)',
|
||||
'rgba(255, 143, 224, 0.5)',
|
||||
'rgba(152, 172, 189, 0.5)',
|
||||
'rgba(216, 226, 248, 0.5)',
|
||||
];
|
||||
|
||||
const _getIconRenderColor = (shapes: TDShape[]) => {
|
||||
const counted = countBy(shapes, shape => shape.style.fill);
|
||||
const max = maxBy(Object.entries(counted), ([c, n]) => n);
|
||||
return max[0];
|
||||
};
|
||||
|
||||
export const FrameFillColorConfig: FC<BorderColorConfigProps> = ({
|
||||
app,
|
||||
shapes,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const setFillColor = (color: ColorType) => {
|
||||
app.style(
|
||||
{ fill: color, isFilled: color !== 'none' },
|
||||
getShapeIds(shapes)
|
||||
);
|
||||
};
|
||||
|
||||
const iconColor = _getIconRenderColor(shapes);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
trigger="hover"
|
||||
placement="bottom-start"
|
||||
content={
|
||||
<Palette
|
||||
colors={_colors}
|
||||
selected={iconColor}
|
||||
onSelect={setFillColor}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Tooltip content="Frame Background Color" placement="top-start">
|
||||
<IconButton>
|
||||
{iconColor === 'none' ? (
|
||||
<ShapeColorNoneIcon />
|
||||
) : (
|
||||
<ShapeColorDuotoneIcon
|
||||
style={{
|
||||
color: iconColor,
|
||||
border:
|
||||
iconColor === '#FFFFFF'
|
||||
? `1px solid ${theme.affine.palette.tagHover}`
|
||||
: 0,
|
||||
borderRadius: '5px',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Popover>
|
||||
);
|
||||
};
|
@ -7,6 +7,7 @@ interface Config {
|
||||
type:
|
||||
| 'stroke'
|
||||
| 'fill'
|
||||
| 'frameFill'
|
||||
| 'font'
|
||||
| 'group'
|
||||
| 'ungroup'
|
||||
@ -22,6 +23,10 @@ const _createInitConfig = (): Record<Config['type'], Config> => {
|
||||
type: 'fill',
|
||||
selectedShapes: [],
|
||||
},
|
||||
frameFill: {
|
||||
type: 'frameFill',
|
||||
selectedShapes: [],
|
||||
},
|
||||
stroke: {
|
||||
type: 'stroke',
|
||||
selectedShapes: [],
|
||||
@ -64,6 +69,7 @@ const _isSupportStroke = (shape: TDShape): boolean => {
|
||||
TDShapeType.Pencil,
|
||||
TDShapeType.Laser,
|
||||
TDShapeType.Highlight,
|
||||
TDShapeType.Draw,
|
||||
TDShapeType.Arrow,
|
||||
TDShapeType.Line,
|
||||
].some(type => type === shape.type);
|
||||
@ -91,6 +97,10 @@ const _isSupportFont = (shape: TDShape): boolean => {
|
||||
].some(type => type === shape.type);
|
||||
};
|
||||
|
||||
const _isSupportFrameFill = (shape: TDShape): boolean => {
|
||||
return shape.type === TDShapeType.Frame;
|
||||
};
|
||||
|
||||
export const useConfig = (app: TldrawApp): Record<Config['type'], Config> => {
|
||||
const state = app.useStore();
|
||||
const selectedShapes = TLDR.get_selected_shapes(state, app.currentPageId);
|
||||
@ -105,6 +115,9 @@ export const useConfig = (app: TldrawApp): Record<Config['type'], Config> => {
|
||||
if (_isSupportFont(cur)) {
|
||||
acc.font.selectedShapes.push(cur);
|
||||
}
|
||||
if (_isSupportFrameFill(cur)) {
|
||||
acc.frameFill.selectedShapes.push(cur);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
_createInitConfig()
|
||||
|
@ -1,12 +1,9 @@
|
||||
import * as React from 'react';
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
import { Utils, SVGContainer } from '@tldraw/core';
|
||||
import {
|
||||
FrameShape,
|
||||
DashStyle,
|
||||
TDShapeType,
|
||||
TDMeta,
|
||||
GHOSTED_OPACITY,
|
||||
LABEL_POINT,
|
||||
} from '@toeverything/components/board-types';
|
||||
import { TDShapeUtil } from '../TDShapeUtil';
|
||||
import {
|
||||
@ -14,14 +11,13 @@ import {
|
||||
getShapeStyle,
|
||||
getBoundsRectangle,
|
||||
transformRectangle,
|
||||
getFontStyle,
|
||||
transformSingleRectangle,
|
||||
} from '../shared';
|
||||
import { DrawFrame } from './components/draw-frame';
|
||||
import { Frame } from './components/Frame';
|
||||
import { styled } from '@toeverything/components/ui';
|
||||
|
||||
type T = FrameShape;
|
||||
type E = HTMLDivElement;
|
||||
type E = SVGSVGElement;
|
||||
|
||||
export class FrameUtil extends TDShapeUtil<T, E> {
|
||||
type = TDShapeType.Frame as const;
|
||||
@ -56,10 +52,7 @@ export class FrameUtil extends TDShapeUtil<T, E> {
|
||||
(
|
||||
{
|
||||
shape,
|
||||
isEditing,
|
||||
isBinding,
|
||||
isSelected,
|
||||
isGhost,
|
||||
meta,
|
||||
bounds,
|
||||
events,
|
||||
@ -70,21 +63,20 @@ export class FrameUtil extends TDShapeUtil<T, E> {
|
||||
) => {
|
||||
const { id, size, style } = shape;
|
||||
return (
|
||||
<FullWrapper ref={ref} {...events}>
|
||||
<SVGContainer
|
||||
id={shape.id + '_svg'}
|
||||
opacity={1}
|
||||
fill={'#fff'}
|
||||
>
|
||||
<DrawFrame
|
||||
id={id}
|
||||
style={style}
|
||||
size={size}
|
||||
isSelected={isSelected}
|
||||
isDarkMode={meta.isDarkMode}
|
||||
/>
|
||||
</SVGContainer>
|
||||
</FullWrapper>
|
||||
<SVGContainer
|
||||
ref={ref}
|
||||
{...events}
|
||||
id={shape.id + '_svg'}
|
||||
opacity={1}
|
||||
>
|
||||
<Frame
|
||||
id={id}
|
||||
style={style}
|
||||
size={size}
|
||||
isSelected={isSelected}
|
||||
isDarkMode={meta.isDarkMode}
|
||||
/>
|
||||
</SVGContainer>
|
||||
);
|
||||
}
|
||||
);
|
||||
@ -121,27 +113,9 @@ export class FrameUtil extends TDShapeUtil<T, E> {
|
||||
override transform = transformRectangle;
|
||||
|
||||
override transformSingle = transformSingleRectangle;
|
||||
|
||||
override hitTestPoint = (shape: T, point: number[]): boolean => {
|
||||
return false;
|
||||
};
|
||||
|
||||
override hitTestLineSegment = (
|
||||
shape: T,
|
||||
A: number[],
|
||||
B: number[]
|
||||
): boolean => {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
const FullWrapper = styled('div')({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
'.tl-fill-hitarea': {
|
||||
fill: '#F7F9FF',
|
||||
},
|
||||
'.tl-stroke-hitarea': {
|
||||
fill: '#F7F9FF',
|
||||
},
|
||||
});
|
@ -0,0 +1,54 @@
|
||||
import * as React from 'react';
|
||||
import { BINDING_DISTANCE } from '@toeverything/components/board-types';
|
||||
import type { ShapeStyles } from '@toeverything/components/board-types';
|
||||
import { getShapeStyle } from '../../shared';
|
||||
|
||||
interface RectangleSvgProps {
|
||||
id: string;
|
||||
style: ShapeStyles;
|
||||
isSelected: boolean;
|
||||
size: number[];
|
||||
isDarkMode: boolean;
|
||||
}
|
||||
|
||||
export const Frame = React.memo(function DashedRectangle({
|
||||
id,
|
||||
style,
|
||||
size,
|
||||
isSelected,
|
||||
isDarkMode,
|
||||
}: RectangleSvgProps) {
|
||||
const { strokeWidth, fill } = getShapeStyle(style, isDarkMode);
|
||||
|
||||
const _fill = fill && fill !== 'none' ? fill : '#F7F9FF';
|
||||
|
||||
const sw = 1 + strokeWidth * 1.618;
|
||||
|
||||
const w = Math.max(0, size[0] - sw / 2);
|
||||
const h = Math.max(0, size[1] - sw / 2);
|
||||
|
||||
return (
|
||||
<>
|
||||
<rect
|
||||
className={
|
||||
isSelected || style.isFilled
|
||||
? 'tl-fill-hitarea'
|
||||
: 'tl-stroke-hitarea'
|
||||
}
|
||||
x={sw / 2}
|
||||
y={sw / 2}
|
||||
width={w}
|
||||
height={h}
|
||||
strokeWidth={BINDING_DISTANCE}
|
||||
/>
|
||||
<rect
|
||||
x={sw / 2}
|
||||
y={sw / 2}
|
||||
width={w}
|
||||
height={h}
|
||||
fill={_fill}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
@ -1,42 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { BINDING_DISTANCE } from '@toeverything/components/board-types';
|
||||
import type { ShapeStyles } from '@toeverything/components/board-types';
|
||||
import { getShapeStyle } from '../../shared';
|
||||
|
||||
interface RectangleSvgProps {
|
||||
id: string;
|
||||
style: ShapeStyles;
|
||||
isSelected: boolean;
|
||||
size: number[];
|
||||
isDarkMode: boolean;
|
||||
}
|
||||
|
||||
export const DrawFrame = React.memo(function DashedRectangle({
|
||||
id,
|
||||
style,
|
||||
size,
|
||||
isSelected,
|
||||
isDarkMode,
|
||||
}: RectangleSvgProps) {
|
||||
const { stroke, strokeWidth, fill } = getShapeStyle(style, isDarkMode);
|
||||
|
||||
const sw = 1 + strokeWidth * 1.618;
|
||||
|
||||
const w = Math.max(0, size[0] - sw / 2);
|
||||
const h = Math.max(0, size[1] - sw / 2);
|
||||
|
||||
return (
|
||||
<rect
|
||||
className={
|
||||
isSelected || style.isFilled
|
||||
? 'tl-fill-hitarea'
|
||||
: 'tl-stroke-hitarea'
|
||||
}
|
||||
x={sw / 2}
|
||||
y={sw / 2}
|
||||
width={w}
|
||||
height={h}
|
||||
strokeWidth={BINDING_DISTANCE}
|
||||
/>
|
||||
);
|
||||
});
|
@ -1 +1 @@
|
||||
export * from './frame-util';
|
||||
export * from './FrameUtil';
|
||||
|
Loading…
Reference in New Issue
Block a user