refactor(editor): edgeless connector toolbar config extension (#10798)
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
EdgelessCRUDExtension,
|
EdgelessCRUDIdentifier,
|
||||||
EdgelessLegacySlotIdentifier,
|
EdgelessLegacySlotIdentifier,
|
||||||
} from '@blocksuite/affine-block-surface';
|
} from '@blocksuite/affine-block-surface';
|
||||||
import {
|
import {
|
||||||
@@ -172,7 +172,9 @@ const builtinSurfaceToolbarConfig = {
|
|||||||
const color = e.detail.value;
|
const color = e.detail.value;
|
||||||
for (const model of models) {
|
for (const model of models) {
|
||||||
const props = packColor(field, color);
|
const props = packColor(field, color);
|
||||||
ctx.std.get(EdgelessCRUDExtension).updateElement(model.id, props);
|
ctx.std
|
||||||
|
.get(EdgelessCRUDIdentifier)
|
||||||
|
.updateElement(model.id, props);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -230,7 +232,7 @@ const builtinSurfaceToolbarConfig = {
|
|||||||
const shadowType = e.detail;
|
const shadowType = e.detail;
|
||||||
for (const model of models) {
|
for (const model of models) {
|
||||||
const edgeless = model.props.edgeless;
|
const edgeless = model.props.edgeless;
|
||||||
ctx.std.get(EdgelessCRUDExtension).updateElement(model.id, {
|
ctx.std.get(EdgelessCRUDIdentifier).updateElement(model.id, {
|
||||||
edgeless: {
|
edgeless: {
|
||||||
...edgeless,
|
...edgeless,
|
||||||
style: {
|
style: {
|
||||||
@@ -270,7 +272,7 @@ const builtinSurfaceToolbarConfig = {
|
|||||||
const borderSize = value;
|
const borderSize = value;
|
||||||
for (const model of models) {
|
for (const model of models) {
|
||||||
const edgeless = model.props.edgeless;
|
const edgeless = model.props.edgeless;
|
||||||
ctx.std.get(EdgelessCRUDExtension).updateElement(model.id, {
|
ctx.std.get(EdgelessCRUDIdentifier).updateElement(model.id, {
|
||||||
edgeless: {
|
edgeless: {
|
||||||
...edgeless,
|
...edgeless,
|
||||||
style: {
|
style: {
|
||||||
@@ -286,7 +288,7 @@ const builtinSurfaceToolbarConfig = {
|
|||||||
const borderStyle = value;
|
const borderStyle = value;
|
||||||
for (const model of models) {
|
for (const model of models) {
|
||||||
const edgeless = model.props.edgeless;
|
const edgeless = model.props.edgeless;
|
||||||
ctx.std.get(EdgelessCRUDExtension).updateElement(model.id, {
|
ctx.std.get(EdgelessCRUDIdentifier).updateElement(model.id, {
|
||||||
edgeless: {
|
edgeless: {
|
||||||
...edgeless,
|
...edgeless,
|
||||||
style: {
|
style: {
|
||||||
@@ -328,7 +330,7 @@ const builtinSurfaceToolbarConfig = {
|
|||||||
const borderRadius = e.detail;
|
const borderRadius = e.detail;
|
||||||
for (const model of models) {
|
for (const model of models) {
|
||||||
const edgeless = model.props.edgeless;
|
const edgeless = model.props.edgeless;
|
||||||
ctx.std.get(EdgelessCRUDExtension).updateElement(model.id, {
|
ctx.std.get(EdgelessCRUDIdentifier).updateElement(model.id, {
|
||||||
edgeless: {
|
edgeless: {
|
||||||
...edgeless,
|
...edgeless,
|
||||||
style: {
|
style: {
|
||||||
|
|||||||
@@ -1,51 +1,372 @@
|
|||||||
import type { ToolbarModuleConfig } from '@blocksuite/affine-shared/services';
|
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
|
||||||
|
import {
|
||||||
|
packColor,
|
||||||
|
type PickColorEvent,
|
||||||
|
} from '@blocksuite/affine-components/color-picker';
|
||||||
|
import type { LineDetailType } from '@blocksuite/affine-components/edgeless-line-styles-panel';
|
||||||
|
import {
|
||||||
|
ConnectorElementModel,
|
||||||
|
type ConnectorElementProps,
|
||||||
|
type ConnectorLabelProps,
|
||||||
|
ConnectorMode,
|
||||||
|
DEFAULT_FRONT_ENDPOINT_STYLE,
|
||||||
|
DEFAULT_REAR_ENDPOINT_STYLE,
|
||||||
|
DefaultTheme,
|
||||||
|
LineWidth,
|
||||||
|
PointStyle,
|
||||||
|
resolveColor,
|
||||||
|
StrokeStyle,
|
||||||
|
} from '@blocksuite/affine-model';
|
||||||
|
import {
|
||||||
|
FeatureFlagService,
|
||||||
|
type ToolbarContext,
|
||||||
|
type ToolbarModuleConfig,
|
||||||
|
} from '@blocksuite/affine-shared/services';
|
||||||
|
import {
|
||||||
|
getMostCommonResolvedValue,
|
||||||
|
getMostCommonValue,
|
||||||
|
} from '@blocksuite/affine-shared/utils';
|
||||||
import {
|
import {
|
||||||
AddTextIcon,
|
AddTextIcon,
|
||||||
ConnectorCIcon,
|
ConnectorCIcon,
|
||||||
|
ConnectorEIcon,
|
||||||
|
ConnectorLIcon,
|
||||||
|
EndPointArrowIcon,
|
||||||
|
EndPointCircleIcon,
|
||||||
|
EndPointDiamondIcon,
|
||||||
|
EndPointTriangleIcon,
|
||||||
FlipDirectionIcon,
|
FlipDirectionIcon,
|
||||||
|
StartPointArrowIcon,
|
||||||
|
StartPointCircleIcon,
|
||||||
|
StartPointDiamondIcon,
|
||||||
StartPointIcon,
|
StartPointIcon,
|
||||||
|
StartPointTriangleIcon,
|
||||||
} from '@blocksuite/icons/lit';
|
} from '@blocksuite/icons/lit';
|
||||||
|
import { html } from 'lit';
|
||||||
|
import { styleMap } from 'lit/directives/style-map.js';
|
||||||
|
|
||||||
|
import { EdgelessRootBlockComponent } from '../..';
|
||||||
|
import { mountConnectorLabelEditor } from '../../utils/text';
|
||||||
|
import { LINE_STYLE_LIST } from './consts';
|
||||||
|
import type { MenuItem } from './types';
|
||||||
|
import { renderMenu } from './utils';
|
||||||
|
|
||||||
|
const FRONT_ENDPOINT_STYLE_LIST = [
|
||||||
|
{
|
||||||
|
value: PointStyle.None,
|
||||||
|
icon: StartPointIcon(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: PointStyle.Arrow,
|
||||||
|
icon: StartPointArrowIcon(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: PointStyle.Triangle,
|
||||||
|
icon: StartPointTriangleIcon(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: PointStyle.Circle,
|
||||||
|
icon: StartPointCircleIcon(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: PointStyle.Diamond,
|
||||||
|
icon: StartPointDiamondIcon(),
|
||||||
|
},
|
||||||
|
] as const satisfies MenuItem<PointStyle>[];
|
||||||
|
|
||||||
|
const REAR_ENDPOINT_STYLE_LIST = [
|
||||||
|
{
|
||||||
|
value: PointStyle.Diamond,
|
||||||
|
icon: EndPointDiamondIcon(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: PointStyle.Circle,
|
||||||
|
icon: EndPointCircleIcon(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: PointStyle.Triangle,
|
||||||
|
icon: EndPointTriangleIcon(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: PointStyle.Arrow,
|
||||||
|
icon: EndPointArrowIcon(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: PointStyle.None,
|
||||||
|
icon: StartPointIcon(),
|
||||||
|
},
|
||||||
|
] as const satisfies MenuItem<PointStyle>[];
|
||||||
|
|
||||||
|
const CONNECTOR_MODE_LIST = [
|
||||||
|
{
|
||||||
|
key: 'Curve',
|
||||||
|
value: ConnectorMode.Curve,
|
||||||
|
icon: ConnectorCIcon(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Elbowed',
|
||||||
|
value: ConnectorMode.Orthogonal,
|
||||||
|
icon: ConnectorEIcon(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Straight',
|
||||||
|
value: ConnectorMode.Straight,
|
||||||
|
icon: ConnectorLIcon(),
|
||||||
|
},
|
||||||
|
] as const satisfies MenuItem<ConnectorMode>[];
|
||||||
|
|
||||||
export const builtinConnectorToolbarConfig = {
|
export const builtinConnectorToolbarConfig = {
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
id: 'a.stroke-color',
|
id: 'a.stroke-color',
|
||||||
tooltip: 'Stroke style',
|
tooltip: 'Stroke style',
|
||||||
run() {},
|
content(ctx) {
|
||||||
|
const models = ctx.getSurfaceModelsByType(ConnectorElementModel);
|
||||||
|
if (!models.length) return null;
|
||||||
|
|
||||||
|
const enableCustomColor = ctx.std
|
||||||
|
.get(FeatureFlagService)
|
||||||
|
.getFlag('enable_color_picker');
|
||||||
|
const theme = ctx.themeProvider.edgelessTheme;
|
||||||
|
|
||||||
|
const firstModel = models[0];
|
||||||
|
const strokeWidth =
|
||||||
|
getMostCommonValue(models, 'strokeWidth') ?? LineWidth.Four;
|
||||||
|
const strokeStyle =
|
||||||
|
getMostCommonValue(models, 'strokeStyle') ?? StrokeStyle.Solid;
|
||||||
|
const stroke =
|
||||||
|
getMostCommonResolvedValue(models, 'stroke', stroke =>
|
||||||
|
resolveColor(stroke, theme)
|
||||||
|
) ?? resolveColor(DefaultTheme.connectorColor, theme);
|
||||||
|
|
||||||
|
const onPickColor = (e: PickColorEvent) => {
|
||||||
|
const field = 'stroke';
|
||||||
|
|
||||||
|
if (e.type === 'pick') {
|
||||||
|
const color = e.detail.value;
|
||||||
|
for (const model of models) {
|
||||||
|
const props = packColor(field, color);
|
||||||
|
ctx.std
|
||||||
|
.get(EdgelessCRUDIdentifier)
|
||||||
|
.updateElement(model.id, props);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const model of models) {
|
||||||
|
model[e.type === 'start' ? 'stash' : 'pop'](field);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPickStrokeStyle = (e: CustomEvent<LineDetailType>) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const { type, value } = e.detail;
|
||||||
|
|
||||||
|
if (type === 'size') {
|
||||||
|
updateModelsWith(ctx, models, 'strokeWidth', value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateModelsWith(ctx, models, 'strokeStyle', value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<edgeless-color-picker-button
|
||||||
|
class="stroke-color"
|
||||||
|
.label="${'Stroke style'}"
|
||||||
|
.pick=${onPickColor}
|
||||||
|
.color=${stroke}
|
||||||
|
.theme=${theme}
|
||||||
|
.hollowCircle=${true}
|
||||||
|
.originalColor=${firstModel.stroke}
|
||||||
|
.enableCustomColor=${enableCustomColor}
|
||||||
|
>
|
||||||
|
<affine-edgeless-line-styles-panel
|
||||||
|
slot="other"
|
||||||
|
style=${styleMap({
|
||||||
|
display: 'flex',
|
||||||
|
alignSelf: 'stretch',
|
||||||
|
gap: '8px',
|
||||||
|
})}
|
||||||
|
@select=${onPickStrokeStyle}
|
||||||
|
.lineSize=${strokeWidth}
|
||||||
|
.lineStyle=${strokeStyle}
|
||||||
|
></affine-edgeless-line-styles-panel>
|
||||||
|
<editor-toolbar-separator
|
||||||
|
slot="separator"
|
||||||
|
data-orientation="horizontal"
|
||||||
|
></editor-toolbar-separator>
|
||||||
|
</edgeless-color-picker-button>
|
||||||
|
`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'b.style',
|
id: 'b.style',
|
||||||
tooltip: 'Style',
|
// TODO(@fundon): should add a feature flag
|
||||||
run() {},
|
when: false,
|
||||||
|
content(ctx) {
|
||||||
|
const models = ctx.getSurfaceModelsByType(ConnectorElementModel);
|
||||||
|
if (!models.length) return null;
|
||||||
|
|
||||||
|
const field = 'rough';
|
||||||
|
const rough = getMostCommonValue(models, field) ?? false;
|
||||||
|
const onPick = (value: boolean) => {
|
||||||
|
updateModelsWith(ctx, models, field, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return renderMenu({
|
||||||
|
label: 'Style',
|
||||||
|
items: LINE_STYLE_LIST,
|
||||||
|
currentValue: rough,
|
||||||
|
onPick,
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'c.start-point-style',
|
id: 'c.endpoint-style',
|
||||||
icon: StartPointIcon(),
|
actions: [
|
||||||
tooltip: 'Start point style',
|
{
|
||||||
run() {},
|
id: 'a.start-point-style',
|
||||||
},
|
content(ctx) {
|
||||||
{
|
const models = ctx.getSurfaceModelsByType(ConnectorElementModel);
|
||||||
id: 'd.flip-direction',
|
if (!models.length) return null;
|
||||||
icon: FlipDirectionIcon(),
|
|
||||||
tooltip: 'Flip direction',
|
const field = 'frontEndpointStyle';
|
||||||
run() {},
|
const pointStyle =
|
||||||
},
|
getMostCommonValue(models, field) ?? DEFAULT_FRONT_ENDPOINT_STYLE;
|
||||||
{
|
const onPick = (value: PointStyle) => {
|
||||||
id: 'e.end-point-style',
|
updateModelsWith(ctx, models, field, value);
|
||||||
icon: StartPointIcon(),
|
};
|
||||||
tooltip: 'End point style',
|
|
||||||
run() {},
|
return renderMenu({
|
||||||
},
|
label: 'Start point style',
|
||||||
{
|
items: FRONT_ENDPOINT_STYLE_LIST,
|
||||||
id: 'f.connector-shape',
|
currentValue: pointStyle,
|
||||||
icon: ConnectorCIcon(),
|
onPick,
|
||||||
tooltip: 'Connector shape',
|
});
|
||||||
run() {},
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'b.flip-direction',
|
||||||
|
icon: FlipDirectionIcon(),
|
||||||
|
tooltip: 'Flip direction',
|
||||||
|
run(ctx) {
|
||||||
|
const models = ctx.getSurfaceModelsByType(ConnectorElementModel);
|
||||||
|
if (!models.length) return;
|
||||||
|
|
||||||
|
const frontEndpointStyle =
|
||||||
|
getMostCommonValue(models, 'frontEndpointStyle') ??
|
||||||
|
DEFAULT_FRONT_ENDPOINT_STYLE;
|
||||||
|
const rearEndpointStyle =
|
||||||
|
getMostCommonValue(models, 'rearEndpointStyle') ??
|
||||||
|
DEFAULT_REAR_ENDPOINT_STYLE;
|
||||||
|
|
||||||
|
if (frontEndpointStyle === rearEndpointStyle) return;
|
||||||
|
|
||||||
|
for (const model of models) {
|
||||||
|
ctx.std.get(EdgelessCRUDIdentifier).updateElement(model.id, {
|
||||||
|
frontEndpointStyle: rearEndpointStyle,
|
||||||
|
rearEndpointStyle: frontEndpointStyle,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'c.end-point-style',
|
||||||
|
content(ctx) {
|
||||||
|
const models = ctx.getSurfaceModelsByType(ConnectorElementModel);
|
||||||
|
if (!models.length) return null;
|
||||||
|
|
||||||
|
const field = 'rearEndpointStyle';
|
||||||
|
const pointStyle =
|
||||||
|
getMostCommonValue(models, field) ?? DEFAULT_REAR_ENDPOINT_STYLE;
|
||||||
|
const onPick = (value: PointStyle) => {
|
||||||
|
updateModelsWith(ctx, models, field, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return renderMenu({
|
||||||
|
label: 'End point style',
|
||||||
|
items: REAR_ENDPOINT_STYLE_LIST,
|
||||||
|
currentValue: pointStyle,
|
||||||
|
onPick,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'd.connector-shape',
|
||||||
|
content(ctx) {
|
||||||
|
const models = ctx.getSurfaceModelsByType(ConnectorElementModel);
|
||||||
|
if (!models.length) return null;
|
||||||
|
|
||||||
|
const field = 'mode';
|
||||||
|
const mode =
|
||||||
|
getMostCommonValue(models, field) ?? ConnectorMode.Orthogonal;
|
||||||
|
const onPick = (value: ConnectorMode) => {
|
||||||
|
updateModelsWith(ctx, models, field, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return renderMenu({
|
||||||
|
label: 'Shape',
|
||||||
|
tooltip: 'Connector shape',
|
||||||
|
items: CONNECTOR_MODE_LIST,
|
||||||
|
currentValue: mode,
|
||||||
|
onPick,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'g.add-text',
|
id: 'g.add-text',
|
||||||
icon: AddTextIcon(),
|
icon: AddTextIcon(),
|
||||||
run() {},
|
when(ctx) {
|
||||||
|
const models = ctx.getSurfaceModelsByType(ConnectorElementModel);
|
||||||
|
return models.length === 1 && !models[0].text;
|
||||||
|
},
|
||||||
|
run(ctx) {
|
||||||
|
const model = ctx.getCurrentModelByType(ConnectorElementModel);
|
||||||
|
if (!model) return;
|
||||||
|
|
||||||
|
const rootModel = ctx.store.root;
|
||||||
|
if (!rootModel) return;
|
||||||
|
|
||||||
|
// TODO(@fundon): it should be simple
|
||||||
|
const edgeless = ctx.view.getBlock(rootModel.id);
|
||||||
|
if (!ctx.matchBlock(edgeless, EdgelessRootBlockComponent)) {
|
||||||
|
console.error('edgeless view is not found.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mountConnectorLabelEditor(model, edgeless);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'g.text',
|
||||||
|
when(ctx) {
|
||||||
|
const models = ctx.getSurfaceModelsByType(ConnectorElementModel);
|
||||||
|
return models.length > 0 && !models.some(model => !model.text);
|
||||||
|
},
|
||||||
|
// TODO(@fundon): text actoins
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
|
when: ctx => ctx.getSurfaceModelsByType(ConnectorElementModel).length > 0,
|
||||||
} as const satisfies ToolbarModuleConfig;
|
} as const satisfies ToolbarModuleConfig;
|
||||||
|
|
||||||
|
function updateModelsWith<
|
||||||
|
T extends keyof Omit<ConnectorElementProps, keyof ConnectorLabelProps>,
|
||||||
|
>(
|
||||||
|
ctx: ToolbarContext,
|
||||||
|
models: ConnectorElementModel[],
|
||||||
|
field: T,
|
||||||
|
value: ConnectorElementProps[T]
|
||||||
|
) {
|
||||||
|
ctx.store.captureSync();
|
||||||
|
|
||||||
|
for (const model of models) {
|
||||||
|
ctx.std
|
||||||
|
.get(EdgelessCRUDIdentifier)
|
||||||
|
.updateElement(model.id, { [field]: value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { StyleGeneralIcon, StyleScribbleIcon } from '@blocksuite/icons/lit';
|
||||||
|
|
||||||
|
import type { MenuItem } from './types';
|
||||||
|
|
||||||
|
export const LINE_STYLE_LIST = [
|
||||||
|
{
|
||||||
|
key: 'General',
|
||||||
|
value: false,
|
||||||
|
icon: StyleGeneralIcon(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Scribbled',
|
||||||
|
value: true,
|
||||||
|
icon: StyleScribbleIcon(),
|
||||||
|
},
|
||||||
|
] as const satisfies MenuItem<boolean>[];
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { EdgelessFrameManagerIdentifier } from '@blocksuite/affine-block-frame';
|
import { EdgelessFrameManagerIdentifier } from '@blocksuite/affine-block-frame';
|
||||||
import { EdgelessCRUDExtension } from '@blocksuite/affine-block-surface';
|
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
|
||||||
import {
|
import {
|
||||||
packColor,
|
packColor,
|
||||||
type PickColorEvent,
|
type PickColorEvent,
|
||||||
@@ -159,7 +159,9 @@ const builtinSurfaceToolbarConfig = {
|
|||||||
const color = e.detail.value;
|
const color = e.detail.value;
|
||||||
for (const model of models) {
|
for (const model of models) {
|
||||||
const props = packColor(field, color);
|
const props = packColor(field, color);
|
||||||
ctx.std.get(EdgelessCRUDExtension).updateElement(model.id, props);
|
ctx.std
|
||||||
|
.get(EdgelessCRUDIdentifier)
|
||||||
|
.updateElement(model.id, props);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type { TemplateResult } from 'lit';
|
||||||
|
|
||||||
|
export type MenuItem<T> = {
|
||||||
|
key?: string;
|
||||||
|
value: T;
|
||||||
|
icon?: TemplateResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Menu<T> = {
|
||||||
|
label: string;
|
||||||
|
icon?: TemplateResult;
|
||||||
|
tooltip?: string;
|
||||||
|
items: MenuItem<T>[];
|
||||||
|
currentValue: T;
|
||||||
|
onPick: (value: T) => void;
|
||||||
|
};
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { ArrowDownSmallIcon } from '@blocksuite/icons/lit';
|
||||||
|
import { html } from 'lit';
|
||||||
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||||
|
import { repeat } from 'lit/directives/repeat.js';
|
||||||
|
|
||||||
|
import type { Menu, MenuItem } from './types';
|
||||||
|
|
||||||
|
export function renderCurrentMenuItemWith<T, F extends keyof MenuItem<T>>(
|
||||||
|
items: MenuItem<T>[],
|
||||||
|
currentValue: T,
|
||||||
|
field: F
|
||||||
|
) {
|
||||||
|
return items.find(({ value }) => value === currentValue)?.[field];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderMenu<T>({
|
||||||
|
label,
|
||||||
|
tooltip,
|
||||||
|
icon,
|
||||||
|
items,
|
||||||
|
currentValue,
|
||||||
|
onPick,
|
||||||
|
}: Menu<T>) {
|
||||||
|
return html`
|
||||||
|
<editor-menu-button
|
||||||
|
.button=${html`
|
||||||
|
<editor-icon-button
|
||||||
|
aria-label="${label}"
|
||||||
|
.tooltip="${tooltip ?? label}"
|
||||||
|
>
|
||||||
|
${icon ?? renderCurrentMenuItemWith(items, currentValue, 'icon')}
|
||||||
|
${ArrowDownSmallIcon()}
|
||||||
|
</editor-icon-button>
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
${renderMenuItems(items, currentValue, onPick)}
|
||||||
|
</editor-menu-button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderMenuItems<T>(
|
||||||
|
items: MenuItem<T>[],
|
||||||
|
currentValue: T,
|
||||||
|
onPick: (value: T) => void
|
||||||
|
) {
|
||||||
|
return repeat(
|
||||||
|
items,
|
||||||
|
item => item.value,
|
||||||
|
({ key, value, icon }) => html`
|
||||||
|
<editor-icon-button
|
||||||
|
aria-label="${ifDefined(key)}"
|
||||||
|
.tooltip="${ifDefined(key)}"
|
||||||
|
.active="${currentValue === value}"
|
||||||
|
.activeMode="${'background'}"
|
||||||
|
@click=${() => onPick(value)}
|
||||||
|
>
|
||||||
|
${icon}
|
||||||
|
</editor-icon-button>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -12,8 +12,8 @@ import {
|
|||||||
ConnectorEndpoint,
|
ConnectorEndpoint,
|
||||||
type ConnectorLabelProps,
|
type ConnectorLabelProps,
|
||||||
ConnectorMode,
|
ConnectorMode,
|
||||||
DEFAULT_FRONT_END_POINT_STYLE,
|
DEFAULT_FRONT_ENDPOINT_STYLE,
|
||||||
DEFAULT_REAR_END_POINT_STYLE,
|
DEFAULT_REAR_ENDPOINT_STYLE,
|
||||||
DefaultTheme,
|
DefaultTheme,
|
||||||
LineWidth,
|
LineWidth,
|
||||||
PointStyle,
|
PointStyle,
|
||||||
@@ -341,12 +341,12 @@ export class EdgelessChangeConnectorButton extends WithDisposable(LitElement) {
|
|||||||
const selectedStartPointStyle = getMostCommonEndpointStyle(
|
const selectedStartPointStyle = getMostCommonEndpointStyle(
|
||||||
elements,
|
elements,
|
||||||
ConnectorEndpoint.Front,
|
ConnectorEndpoint.Front,
|
||||||
DEFAULT_FRONT_END_POINT_STYLE
|
DEFAULT_FRONT_ENDPOINT_STYLE
|
||||||
);
|
);
|
||||||
const selectedEndPointStyle = getMostCommonEndpointStyle(
|
const selectedEndPointStyle = getMostCommonEndpointStyle(
|
||||||
elements,
|
elements,
|
||||||
ConnectorEndpoint.Rear,
|
ConnectorEndpoint.Rear,
|
||||||
DEFAULT_REAR_END_POINT_STYLE
|
DEFAULT_REAR_ENDPOINT_STYLE
|
||||||
);
|
);
|
||||||
const enableCustomColor = this.edgeless.doc
|
const enableCustomColor = this.edgeless.doc
|
||||||
.get(FeatureFlagService)
|
.get(FeatureFlagService)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
EdgelessCRUDExtension,
|
EdgelessCRUDIdentifier,
|
||||||
getSurfaceBlock,
|
getSurfaceBlock,
|
||||||
type SurfaceBlockModel,
|
type SurfaceBlockModel,
|
||||||
SurfaceElementModel,
|
SurfaceElementModel,
|
||||||
@@ -457,7 +457,7 @@ export class SurfaceRefBlockComponent extends BlockComponent<SurfaceRefBlockMode
|
|||||||
private readonly _disposable = new DisposableGroup();
|
private readonly _disposable = new DisposableGroup();
|
||||||
|
|
||||||
override mounted() {
|
override mounted() {
|
||||||
const crud = this.std.get(EdgelessCRUDExtension);
|
const crud = this.std.get(EdgelessCRUDIdentifier);
|
||||||
const { _disposable } = this;
|
const { _disposable } = this;
|
||||||
const surfaceModel = getSurfaceBlock(this.std.store);
|
const surfaceModel = getSurfaceBlock(this.std.store);
|
||||||
if (!surfaceModel) return;
|
if (!surfaceModel) return;
|
||||||
|
|||||||
@@ -17,17 +17,17 @@ export type LineDetailType =
|
|||||||
|
|
||||||
const LINE_STYLE_LIST = [
|
const LINE_STYLE_LIST = [
|
||||||
{
|
{
|
||||||
name: 'Solid',
|
key: 'Solid',
|
||||||
value: StrokeStyle.Solid,
|
value: StrokeStyle.Solid,
|
||||||
icon: StraightLineIcon(),
|
icon: StraightLineIcon(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Dash',
|
key: 'Dash',
|
||||||
value: StrokeStyle.Dash,
|
value: StrokeStyle.Dash,
|
||||||
icon: DashLineIcon(),
|
icon: DashLineIcon(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'None',
|
key: 'None',
|
||||||
value: StrokeStyle.None,
|
value: StrokeStyle.None,
|
||||||
icon: BanIcon(),
|
icon: BanIcon(),
|
||||||
},
|
},
|
||||||
@@ -63,7 +63,7 @@ export class EdgelessLineStylesPanel extends LitElement {
|
|||||||
${repeat(
|
${repeat(
|
||||||
LINE_STYLE_LIST.filter(item => lineStyles.includes(item.value)),
|
LINE_STYLE_LIST.filter(item => lineStyles.includes(item.value)),
|
||||||
item => item.value,
|
item => item.value,
|
||||||
({ name, icon, value }) => {
|
({ key, icon, value }) => {
|
||||||
const active = lineStyle === value;
|
const active = lineStyle === value;
|
||||||
const classInfo = {
|
const classInfo = {
|
||||||
'line-style-button': true,
|
'line-style-button': true,
|
||||||
@@ -74,7 +74,7 @@ export class EdgelessLineStylesPanel extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<editor-icon-button
|
<editor-icon-button
|
||||||
class=${classMap(classInfo)}
|
class=${classMap(classInfo)}
|
||||||
.tooltip="${name}"
|
.tooltip="${key}"
|
||||||
.withHover=${active}
|
.withHover=${active}
|
||||||
@click=${() => this.select({ type: 'style', value })}
|
@click=${() => this.select({ type: 'style', value })}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ export enum PointStyle {
|
|||||||
|
|
||||||
export const PointStyleMap = createEnumMap(PointStyle);
|
export const PointStyleMap = createEnumMap(PointStyle);
|
||||||
|
|
||||||
export const DEFAULT_FRONT_END_POINT_STYLE = PointStyle.None;
|
export const DEFAULT_FRONT_ENDPOINT_STYLE = PointStyle.None;
|
||||||
|
|
||||||
export const DEFAULT_REAR_END_POINT_STYLE = PointStyle.Arrow;
|
export const DEFAULT_REAR_ENDPOINT_STYLE = PointStyle.Arrow;
|
||||||
|
|
||||||
export const CONNECTOR_LABEL_MAX_WIDTH = 280;
|
export const CONNECTOR_LABEL_MAX_WIDTH = 280;
|
||||||
|
|
||||||
@@ -32,3 +32,5 @@ export enum ConnectorMode {
|
|||||||
Orthogonal,
|
Orthogonal,
|
||||||
Curve,
|
Curve,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_CONNECTOR_MODE = ConnectorMode.Curve;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import {
|
|||||||
CONNECTOR_LABEL_MAX_WIDTH,
|
CONNECTOR_LABEL_MAX_WIDTH,
|
||||||
ConnectorLabelOffsetAnchor,
|
ConnectorLabelOffsetAnchor,
|
||||||
ConnectorMode,
|
ConnectorMode,
|
||||||
|
DEFAULT_CONNECTOR_MODE,
|
||||||
DEFAULT_ROUGHNESS,
|
DEFAULT_ROUGHNESS,
|
||||||
FontFamily,
|
FontFamily,
|
||||||
FontStyle,
|
FontStyle,
|
||||||
@@ -460,7 +461,7 @@ export class ConnectorElementModel extends GfxPrimitiveElementModel<ConnectorEle
|
|||||||
accessor lableEditing: boolean = false;
|
accessor lableEditing: boolean = false;
|
||||||
|
|
||||||
@field()
|
@field()
|
||||||
accessor mode: ConnectorMode = ConnectorMode.Orthogonal;
|
accessor mode: ConnectorMode = DEFAULT_CONNECTOR_MODE;
|
||||||
|
|
||||||
@derive((path: PointLocation[], instance) => {
|
@derive((path: PointLocation[], instance) => {
|
||||||
const { x, y } = instance;
|
const { x, y } = instance;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
ColorSchema,
|
ColorSchema,
|
||||||
ConnectorMode,
|
ConnectorMode,
|
||||||
DEFAULT_FRONT_END_POINT_STYLE,
|
DEFAULT_CONNECTOR_MODE,
|
||||||
DEFAULT_REAR_END_POINT_STYLE,
|
DEFAULT_FRONT_ENDPOINT_STYLE,
|
||||||
|
DEFAULT_REAR_ENDPOINT_STYLE,
|
||||||
DEFAULT_ROUGHNESS,
|
DEFAULT_ROUGHNESS,
|
||||||
DefaultTheme,
|
DefaultTheme,
|
||||||
EdgelessTextZodSchema,
|
EdgelessTextZodSchema,
|
||||||
@@ -61,13 +62,13 @@ export const ConnectorSchema = z
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.default({
|
.default({
|
||||||
frontEndpointStyle: DEFAULT_FRONT_END_POINT_STYLE,
|
frontEndpointStyle: DEFAULT_FRONT_ENDPOINT_STYLE,
|
||||||
rearEndpointStyle: DEFAULT_REAR_END_POINT_STYLE,
|
rearEndpointStyle: DEFAULT_REAR_ENDPOINT_STYLE,
|
||||||
stroke: DefaultTheme.connectorColor,
|
stroke: DefaultTheme.connectorColor,
|
||||||
strokeStyle: StrokeStyle.Solid,
|
strokeStyle: StrokeStyle.Solid,
|
||||||
strokeWidth: LineWidth.Two,
|
strokeWidth: LineWidth.Two,
|
||||||
rough: false,
|
rough: false,
|
||||||
mode: ConnectorMode.Curve,
|
mode: DEFAULT_CONNECTOR_MODE,
|
||||||
labelStyle: {
|
labelStyle: {
|
||||||
color: DefaultTheme.black,
|
color: DefaultTheme.black,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
|
|||||||
Reference in New Issue
Block a user