refactor(editor): edgeless toolbar linked doc custom config extension (#10883)
This commit is contained in:
@@ -314,6 +314,7 @@ function renderActionItem(action: ToolbarAction, context: ToolbarContext) {
|
|||||||
?active=${typeof action.active === 'function'
|
?active=${typeof action.active === 'function'
|
||||||
? action.active(context)
|
? action.active(context)
|
||||||
: action.active}
|
: action.active}
|
||||||
|
?disabled=${action.disabled}
|
||||||
.tooltip=${action.tooltip}
|
.tooltip=${action.tooltip}
|
||||||
@click=${() => action.run?.(context)}
|
@click=${() => action.run?.(context)}
|
||||||
>
|
>
|
||||||
@@ -336,6 +337,7 @@ function renderMenuActionItem(action: ToolbarAction, context: ToolbarContext) {
|
|||||||
?active=${typeof action.active === 'function'
|
?active=${typeof action.active === 'function'
|
||||||
? action.active(context)
|
? action.active(context)
|
||||||
: action.active}
|
: action.active}
|
||||||
|
?disabled=${action.disabled}
|
||||||
.tooltip=${ifDefined(action.tooltip)}
|
.tooltip=${ifDefined(action.tooltip)}
|
||||||
@click=${() => action.run?.(context)}
|
@click=${() => action.run?.(context)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -56,9 +56,11 @@ import {
|
|||||||
FeatureFlagService,
|
FeatureFlagService,
|
||||||
GenerateDocUrlProvider,
|
GenerateDocUrlProvider,
|
||||||
isRemovedUserInfo,
|
isRemovedUserInfo,
|
||||||
|
OpenDocExtensionIdentifier,
|
||||||
type OpenDocMode,
|
type OpenDocMode,
|
||||||
type ToolbarAction,
|
type ToolbarAction,
|
||||||
type ToolbarActionGroup,
|
type ToolbarActionGenerator,
|
||||||
|
type ToolbarActionGroupGenerator,
|
||||||
type ToolbarContext,
|
type ToolbarContext,
|
||||||
type ToolbarModuleConfig,
|
type ToolbarModuleConfig,
|
||||||
ToolbarModuleExtension,
|
ToolbarModuleExtension,
|
||||||
@@ -479,70 +481,138 @@ function createExternalLinkableToolbarConfig(
|
|||||||
|
|
||||||
const openDocActions = [
|
const openDocActions = [
|
||||||
{
|
{
|
||||||
id: 'open-in-active-view',
|
mode: 'open-in-active-view',
|
||||||
|
id: 'a.open-in-active-view',
|
||||||
label: I18n['com.affine.peek-view-controls.open-doc'](),
|
label: I18n['com.affine.peek-view-controls.open-doc'](),
|
||||||
icon: ExpandFullIcon(),
|
icon: ExpandFullIcon(),
|
||||||
|
when: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'open-in-new-view',
|
mode: 'open-in-new-view',
|
||||||
|
id: 'b.open-in-new-view',
|
||||||
label: I18n['com.affine.peek-view-controls.open-doc-in-split-view'](),
|
label: I18n['com.affine.peek-view-controls.open-doc-in-split-view'](),
|
||||||
icon: SplitViewIcon(),
|
icon: SplitViewIcon(),
|
||||||
when: () => BUILD_CONFIG.isElectron,
|
when: BUILD_CONFIG.isElectron,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'open-in-new-tab',
|
mode: 'open-in-new-tab',
|
||||||
|
id: 'c.open-in-new-tab',
|
||||||
label: I18n['com.affine.peek-view-controls.open-doc-in-new-tab'](),
|
label: I18n['com.affine.peek-view-controls.open-doc-in-new-tab'](),
|
||||||
icon: OpenInNewIcon(),
|
icon: OpenInNewIcon(),
|
||||||
|
when: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'open-in-center-peek',
|
mode: 'open-in-center-peek',
|
||||||
|
id: 'd.open-in-center-peek',
|
||||||
label: I18n['com.affine.peek-view-controls.open-doc-in-center-peek'](),
|
label: I18n['com.affine.peek-view-controls.open-doc-in-center-peek'](),
|
||||||
icon: CenterPeekIcon(),
|
icon: CenterPeekIcon(),
|
||||||
|
when: true,
|
||||||
},
|
},
|
||||||
] as const satisfies ToolbarAction[];
|
] as const satisfies (Pick<ToolbarAction, 'id' | 'label' | 'icon' | 'when'> & {
|
||||||
|
mode: OpenDocMode;
|
||||||
|
})[];
|
||||||
|
|
||||||
function createOpenDocActionGroup(
|
function createOpenDocActions(
|
||||||
klass:
|
ctx: ToolbarContext,
|
||||||
| typeof EmbedLinkedDocBlockComponent
|
target:
|
||||||
| typeof EmbedSyncedDocBlockComponent
|
| EmbedLinkedDocBlockComponent
|
||||||
|
| EmbedSyncedDocBlockComponent
|
||||||
|
| AffineReference,
|
||||||
|
isSameDoc: boolean,
|
||||||
|
actions = openDocActions
|
||||||
) {
|
) {
|
||||||
return {
|
return actions
|
||||||
placement: ActionPlacement.Start,
|
.filter(action => action.when)
|
||||||
id: 'A.open-doc',
|
.map<ToolbarActionGenerator>(action => {
|
||||||
actions: openDocActions,
|
const openMode = action.mode;
|
||||||
content(ctx) {
|
const shouldOpenInCenterPeek = openMode === 'open-in-center-peek';
|
||||||
const block = ctx.getCurrentBlockByType(klass);
|
const shouldOpenInActiveView = openMode === 'open-in-active-view';
|
||||||
if (!block) return null;
|
|
||||||
|
|
||||||
const actions = this.actions
|
|
||||||
.map<ToolbarAction>(action => {
|
|
||||||
const shouldOpenInCenterPeek = action.id === 'open-in-center-peek';
|
|
||||||
const shouldOpenInActiveView = action.id === 'open-in-active-view';
|
|
||||||
const allowed =
|
|
||||||
typeof action.when === 'function'
|
|
||||||
? action.when(ctx)
|
|
||||||
: (action.when ?? true);
|
|
||||||
return {
|
return {
|
||||||
...action,
|
...action,
|
||||||
disabled: shouldOpenInActiveView
|
generate(ctx) {
|
||||||
? block.model.props.pageId === ctx.store.id
|
const disabled = shouldOpenInActiveView ? isSameDoc : false;
|
||||||
: false,
|
|
||||||
when:
|
const when =
|
||||||
allowed && (shouldOpenInCenterPeek ? isPeekable(block) : true),
|
ctx.std.get(OpenDocExtensionIdentifier).isAllowed(openMode) &&
|
||||||
run: shouldOpenInCenterPeek
|
(shouldOpenInCenterPeek ? isPeekable(target) : true);
|
||||||
? (_ctx: ToolbarContext) => peek(block)
|
|
||||||
: (_ctx: ToolbarContext) =>
|
const run = shouldOpenInCenterPeek
|
||||||
block.open({
|
? (_ctx: ToolbarContext) => peek(target)
|
||||||
openMode: action.id as OpenDocMode,
|
: (_ctx: ToolbarContext) => target.open({ openMode });
|
||||||
}),
|
|
||||||
|
return { disabled, when, run };
|
||||||
|
},
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter(action => {
|
.filter(action => {
|
||||||
if (typeof action.when === 'function') return action.when(ctx);
|
if (typeof action.when === 'function') return action.when(ctx);
|
||||||
return action.when ?? true;
|
return action.when ?? true;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOpenDocActionGroup(
|
||||||
|
klass:
|
||||||
|
| typeof EmbedLinkedDocBlockComponent
|
||||||
|
| typeof EmbedSyncedDocBlockComponent
|
||||||
|
): ToolbarAction {
|
||||||
|
return {
|
||||||
|
placement: ActionPlacement.Start,
|
||||||
|
id: 'A.open-doc',
|
||||||
|
content(ctx) {
|
||||||
|
const block = ctx.getCurrentBlockByType(klass);
|
||||||
|
if (!block) return null;
|
||||||
|
|
||||||
|
return renderOpenDocMenu(
|
||||||
|
ctx,
|
||||||
|
block,
|
||||||
|
block.model.props.pageId === ctx.store.id
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEdgelessOpenDocActionGroup(
|
||||||
|
klass:
|
||||||
|
| typeof EmbedLinkedDocBlockComponent
|
||||||
|
| typeof EmbedSyncedDocBlockComponent
|
||||||
|
): ToolbarActionGroupGenerator {
|
||||||
|
return {
|
||||||
|
placement: ActionPlacement.More,
|
||||||
|
id: 'Z.c.open-doc',
|
||||||
|
generate(ctx) {
|
||||||
|
const block = ctx.getCurrentBlockByType(klass);
|
||||||
|
if (!block) return null;
|
||||||
|
|
||||||
|
const actions = createOpenDocActions(
|
||||||
|
ctx,
|
||||||
|
block,
|
||||||
|
block.model.props.pageId === ctx.store.id
|
||||||
|
).map(action => ({ ...action, ...action.generate(ctx) }));
|
||||||
|
|
||||||
|
return { actions };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderOpenDocMenu(
|
||||||
|
ctx: ToolbarContext,
|
||||||
|
target:
|
||||||
|
| EmbedLinkedDocBlockComponent
|
||||||
|
| EmbedSyncedDocBlockComponent
|
||||||
|
| AffineReference,
|
||||||
|
isSameDoc: boolean
|
||||||
|
) {
|
||||||
|
const actions = createOpenDocActions(ctx, target, isSameDoc).map(action => ({
|
||||||
|
...action,
|
||||||
|
...action.generate(ctx),
|
||||||
|
}));
|
||||||
|
if (!actions.length) return null;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
|
${keyed(
|
||||||
|
target,
|
||||||
|
html`
|
||||||
<editor-menu-button
|
<editor-menu-button
|
||||||
.contentPadding="${'8px'}"
|
.contentPadding="${'8px'}"
|
||||||
.button=${html`
|
.button=${html`
|
||||||
@@ -558,9 +628,7 @@ function createOpenDocActionGroup(
|
|||||||
({ label, icon, run, disabled }) => html`
|
({ label, icon, run, disabled }) => html`
|
||||||
<editor-menu-action
|
<editor-menu-action
|
||||||
aria-label=${ifDefined(label)}
|
aria-label=${ifDefined(label)}
|
||||||
?disabled=${ifDefined(
|
?disabled=${ifDefined(disabled)}
|
||||||
typeof disabled === 'function' ? disabled(ctx) : disabled
|
|
||||||
)}
|
|
||||||
@click=${() => run?.(ctx)}
|
@click=${() => run?.(ctx)}
|
||||||
>
|
>
|
||||||
${icon}<span class="label">${label}</span>
|
${icon}<span class="label">${label}</span>
|
||||||
@@ -569,9 +637,9 @@ function createOpenDocActionGroup(
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</editor-menu-button>
|
</editor-menu-button>
|
||||||
|
`
|
||||||
|
)}
|
||||||
`;
|
`;
|
||||||
},
|
|
||||||
} satisfies ToolbarActionGroup<ToolbarAction>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const embedLinkedDocToolbarConfig = {
|
const embedLinkedDocToolbarConfig = {
|
||||||
@@ -737,77 +805,17 @@ const inlineReferenceToolbarConfig = {
|
|||||||
{
|
{
|
||||||
placement: ActionPlacement.Start,
|
placement: ActionPlacement.Start,
|
||||||
id: 'A.open-doc',
|
id: 'A.open-doc',
|
||||||
actions: openDocActions,
|
|
||||||
content(ctx) {
|
content(ctx) {
|
||||||
const target = ctx.message$.peek()?.element;
|
const target = ctx.message$.peek()?.element;
|
||||||
if (!(target instanceof AffineReference)) return null;
|
if (!(target instanceof AffineReference)) return null;
|
||||||
|
|
||||||
const actions = this.actions
|
return renderOpenDocMenu(
|
||||||
.map<ToolbarAction>(action => {
|
ctx,
|
||||||
const shouldOpenInCenterPeek = action.id === 'open-in-center-peek';
|
|
||||||
const shouldOpenInActiveView = action.id === 'open-in-active-view';
|
|
||||||
const allowed =
|
|
||||||
typeof action.when === 'function'
|
|
||||||
? action.when(ctx)
|
|
||||||
: (action.when ?? true);
|
|
||||||
return {
|
|
||||||
...action,
|
|
||||||
disabled: shouldOpenInActiveView
|
|
||||||
? target.referenceInfo.pageId === ctx.store.id
|
|
||||||
: false,
|
|
||||||
when:
|
|
||||||
allowed && (shouldOpenInCenterPeek ? isPeekable(target) : true),
|
|
||||||
run: shouldOpenInCenterPeek
|
|
||||||
? (_ctx: ToolbarContext) => peek(target)
|
|
||||||
: (_ctx: ToolbarContext) =>
|
|
||||||
target.open({
|
|
||||||
openMode: action.id as OpenDocMode,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.filter(action => {
|
|
||||||
if (typeof action.when === 'function') return action.when(ctx);
|
|
||||||
return action.when ?? true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return html`${keyed(
|
|
||||||
target,
|
target,
|
||||||
html`
|
target.referenceInfo.pageId === ctx.store.id
|
||||||
<editor-menu-button
|
);
|
||||||
.contentPadding="${'8px'}"
|
},
|
||||||
.button=${html`
|
|
||||||
<editor-icon-button
|
|
||||||
aria-label="Open doc"
|
|
||||||
.tooltip=${'Open doc'}
|
|
||||||
>
|
|
||||||
${OpenInNewIcon()} ${ArrowDownSmallIcon()}
|
|
||||||
</editor-icon-button>
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<div data-size="small" data-orientation="vertical">
|
|
||||||
${repeat(
|
|
||||||
actions,
|
|
||||||
action => action.id,
|
|
||||||
({ label, icon, run, disabled }) => html`
|
|
||||||
<editor-menu-action
|
|
||||||
aria-label=${ifDefined(label)}
|
|
||||||
?disabled=${ifDefined(
|
|
||||||
typeof disabled === 'function'
|
|
||||||
? disabled(ctx)
|
|
||||||
: disabled
|
|
||||||
)}
|
|
||||||
@click=${() => run?.(ctx)}
|
|
||||||
>
|
|
||||||
${icon}<span class="label">${label}</span>
|
|
||||||
</editor-menu-action>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</editor-menu-button>
|
|
||||||
`
|
|
||||||
)}`;
|
|
||||||
},
|
},
|
||||||
} satisfies ToolbarActionGroup<ToolbarAction>,
|
|
||||||
{
|
{
|
||||||
id: 'b.copy-link-and-edit',
|
id: 'b.copy-link-and-edit',
|
||||||
actions: [
|
actions: [
|
||||||
@@ -1016,7 +1024,14 @@ export const createCustomToolbarExtension = (
|
|||||||
|
|
||||||
ToolbarModuleExtension({
|
ToolbarModuleExtension({
|
||||||
id: BlockFlavourIdentifier('custom:affine:surface:embed-linked-doc'),
|
id: BlockFlavourIdentifier('custom:affine:surface:embed-linked-doc'),
|
||||||
config: embedLinkedDocToolbarConfig,
|
config: {
|
||||||
|
actions: [
|
||||||
|
embedLinkedDocToolbarConfig.actions,
|
||||||
|
createEdgelessOpenDocActionGroup(EmbedLinkedDocBlockComponent),
|
||||||
|
].flat(),
|
||||||
|
|
||||||
|
when: ctx => ctx.getSurfaceModels().length === 1,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
ToolbarModuleExtension({
|
ToolbarModuleExtension({
|
||||||
@@ -1026,7 +1041,14 @@ export const createCustomToolbarExtension = (
|
|||||||
|
|
||||||
ToolbarModuleExtension({
|
ToolbarModuleExtension({
|
||||||
id: BlockFlavourIdentifier('custom:affine:surface:embed-synced-doc'),
|
id: BlockFlavourIdentifier('custom:affine:surface:embed-synced-doc'),
|
||||||
config: embedSyncedDocToolbarConfig,
|
config: {
|
||||||
|
actions: [
|
||||||
|
embedSyncedDocToolbarConfig.actions,
|
||||||
|
createEdgelessOpenDocActionGroup(EmbedSyncedDocBlockComponent),
|
||||||
|
].flat(),
|
||||||
|
|
||||||
|
when: ctx => ctx.getSurfaceModels().length === 1,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
ToolbarModuleExtension({
|
ToolbarModuleExtension({
|
||||||
|
|||||||
Reference in New Issue
Block a user