Skip to content

Commit

Permalink
Improve prosemirror dropdown menu (foundryvtt#14690)
Browse files Browse the repository at this point in the history
  • Loading branch information
In3luki authored and CarlosFdez committed May 29, 2024
1 parent 8e57dc2 commit 6018278
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 107 deletions.
13 changes: 13 additions & 0 deletions src/module/actor/sheet/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { createSelfEffectMessage } from "@module/chat-message/helpers.ts";
import { createSheetTags, maintainFocusInRender, processTagifyInSubmitData } from "@module/sheet/helpers.ts";
import { eventToRollMode, eventToRollParams } from "@scripts/sheet-util.ts";
import { DamageRoll } from "@system/damage/roll.ts";
import { ProseMirrorMenuPF2e } from "@system/prosemirror-menu.ts";
import type { StatisticRollParameters } from "@system/statistic/statistic.ts";
import {
BasicConstructorOptions,
Expand Down Expand Up @@ -1330,6 +1331,18 @@ abstract class ActorSheetPF2e<TActor extends ActorPF2e> extends ActorSheet<TActo

return data;
}

protected override _configureProseMirrorPlugins(
name: string,
options: { remove?: boolean },
): Record<string, ProseMirror.Plugin> {
const plugins = super._configureProseMirrorPlugins(name, options);
plugins.menu = ProseMirrorMenuPF2e.build(foundry.prosemirror.defaultSchema, {
destroyOnSave: options.remove,
onSave: () => this.saveEditor(name, options),
});
return plugins;
}
}

interface ActorSheetPF2e<TActor extends ActorPF2e> extends ActorSheet<TActor, ItemPF2e> {
Expand Down
13 changes: 13 additions & 0 deletions src/module/item/base/sheet/sheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
TraitTagifyEntry,
} from "@module/sheet/helpers.ts";
import { InlineRollLinks } from "@scripts/ui/inline-roll-links.ts";
import { ProseMirrorMenuPF2e } from "@system/prosemirror-menu.ts";
import {
BasicConstructorOptions,
LanguageSelector,
Expand Down Expand Up @@ -298,6 +299,18 @@ class ItemSheetPF2e<TItem extends ItemPF2e> extends ItemSheet<TItem, ItemSheetOp
return super.close(options);
}

protected override _configureProseMirrorPlugins(
name: string,
options: { remove?: boolean },
): Record<string, ProseMirror.Plugin> {
const plugins = super._configureProseMirrorPlugins(name, options);
plugins.menu = ProseMirrorMenuPF2e.build(foundry.prosemirror.defaultSchema, {
destroyOnSave: options.remove,
onSave: () => this.saveEditor(name, options),
});
return plugins;
}

/* -------------------------------------------- */
/* Event Listeners and Handlers */
/* -------------------------------------------- */
Expand Down
13 changes: 13 additions & 0 deletions src/module/journal-entry/sheet.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ProseMirrorMenuPF2e } from "@system/prosemirror-menu.ts";
import type * as TinyMCE from "tinymce";

class JournalSheetPF2e<TJournalEntry extends JournalEntry> extends JournalSheet<TJournalEntry> {
Expand Down Expand Up @@ -26,6 +27,18 @@ class JournalSheetPF2e<TJournalEntry extends JournalEntry> extends JournalSheet<
}
return sheetData;
}

protected override _configureProseMirrorPlugins(
name: string,
options: { remove?: boolean },
): Record<string, ProseMirror.Plugin> {
const plugins = super._configureProseMirrorPlugins(name, options);
plugins.menu = ProseMirrorMenuPF2e.build(foundry.prosemirror.defaultSchema, {
destroyOnSave: options.remove,
onSave: () => this.saveEditor(name, options),
});
return plugins;
}
}

class JournalTextTinyMCESheetPF2e<
Expand Down
173 changes: 173 additions & 0 deletions src/module/system/prosemirror-menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
class ProseMirrorMenuPF2e extends foundry.prosemirror.ProseMirrorMenu {
protected override _getDropDownMenus(): Record<string, ProseMirrorDropDownConfig> {
const menus = super._getDropDownMenus();
const toggleMark = foundry.prosemirror.commands.toggleMark;
const wrapIn = foundry.prosemirror.commands.wrapIn;

if ("format" in menus) {
menus.format.entries.push({
action: "pf2e",
title: "PF2e",
children: [
{
action: "pf2e-action-glyph",
title: "Icons 1 2 3 F R",
mark: this.schema.marks.span,
attrs: { _preserve: { class: "action-glyph" } },
priority: 1,
cmd: toggleMark(this.schema.marks.span, {
_preserve: { class: "action-glyph" },
}),
},
{
action: "pf2e-inline-header",
title: "Inline Header",
class: "level4",
node: this.schema.nodes.heading,
attrs: { _preserve: { class: "inline-header" }, level: 4 },
priority: 1,
cmd: () => {
this._toggleTextBlock(this.schema.nodes.heading, {
attrs: { _preserve: { class: "inline-header" }, level: 4 },
});
return true;
},
},
{
action: "pf2e-info-block",
title: "Info Block",
node: this.schema.nodes.section,
attrs: { _preserve: { class: "info" } },
priority: 1,
cmd: () => {
this._toggleBlock(this.schema.nodes.section, wrapIn, {
attrs: { _preserve: { class: "info" } },
});
return true;
},
},
{
action: "pf2e-stat-block",
title: "Stat Block",
node: this.schema.nodes.section,
attrs: { _preserve: { class: "statblock" } },
priority: 1,
cmd: () => {
this._toggleBlock(this.schema.nodes.section, wrapIn, {
attrs: { _preserve: { class: "statblock" } },
});
return true;
},
},
{
action: "pf2e-traits",
title: "Trait",
node: this.schema.nodes.section,
attrs: { _preserve: { class: "traits" } },
priority: 1,
cmd: () => {
this._toggleBlock(this.schema.nodes.section, wrapIn, {
attrs: { _preserve: { class: "traits" } },
});
return true;
},
},
{
action: "pf2e-written-note",
title: "Written Note",
node: this.schema.nodes.paragraph,
attrs: { _preserve: { class: "message" } },
priority: 1,
cmd: () => {
this._toggleTextBlock(this.schema.nodes.paragraph, {
attrs: { _preserve: { class: "message" } },
});
return true;
},
},
{
action: "pf2e-gm-text-block",
title: "GM Text Block",
node: this.schema.nodes.div,
attrs: { _preserve: { "data-visibility": "gm" } },
priority: 1,
cmd: () => {
this._toggleBlock(this.schema.nodes.div, wrapIn, {
attrs: { _preserve: { "data-visibility": "gm" } },
});
return true;
},
},
{
action: "pf2e-gm-text-inline",
title: "GM Text Inline",
mark: this.schema.marks.span,
attrs: { _preserve: { "data-visibility": "gm" } },
priority: 1,
cmd: toggleMark(this.schema.marks.span, {
_preserve: { "data-visibility": "gm" },
}),
},
],
});
}
return menus;
}

protected override _isMarkActive(item: ProseMirrorMenuItem): boolean {
if (!item.action.startsWith("pf2e-")) return super._isMarkActive(item);

// This is the same as the super method except the `attr._preserve` property
// is not removed from marks
const state = this.view.state;
const { from, $from, to, empty } = state.selection;
const markCompare = (mark: ProseMirror.Mark) => {
if (mark.type !== item.mark) return false;
// R.isDeepEqual returns false here so we use the foundry helper
if (item.attrs) return fu.objectsEqual(mark.attrs, item.attrs);
return true;
};
if (empty) return $from.marks().some(markCompare);
let active = false;
state.doc.nodesBetween(from, to, (node) => {
if (node.marks.some(markCompare)) active = true;
return !active;
});
return active;
}

protected override _isNodeActive(item: ProseMirrorMenuItem): boolean {
if (!item.action.startsWith("pf2e-")) return super._isNodeActive(item);

// Same as the super method except the call to `this.#hasAncestor`
const state = this.view.state;
const { $from, $to, empty } = state.selection;
const sameParent = empty || $from.sameParent($to);
if (!sameParent) return false;
return state.doc.nodeAt($from.pos)?.type === item.node || this.#hasAncestor($from, item.node, item.attrs);
}

/** A reimplementation of Foundry's `ResolvedPos.prototype.hasAncestor` extension that keeps the
* `attrs._preserve` property when comparing nodes
*/
#hasAncestor(pos: ProseMirror.ResolvedPos, other?: ProseMirror.NodeType, attrs?: Record<string, unknown>): boolean {
if (!pos.depth || !other) return false;
for (let i = pos.depth; i > 0; i--) {
// Depth 0 is the root document, so we don't need to test that.
const node = pos.node(i);
if (node.type === other) {
if (attrs) return fu.objectsEqual(node.attrs, attrs);
return true;
}
}
return false;
}

protected override _onAction(event: MouseEvent): void {
super._onAction(event);
// Return focus to the editor after the command was executed
this.view.focus();
}
}

export { ProseMirrorMenuPF2e };
2 changes: 0 additions & 2 deletions src/scripts/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { I18nInit } from "./i18n-init.ts";
import { Init } from "./init.ts";
import { LightingRefresh } from "./lighting-refresh.ts";
import { Load } from "./load.ts";
import { GetProseMirrorMenuDropDowns } from "./prosemirror-menu.ts";
import { Ready } from "./ready.ts";
import { RenderChatPopout } from "./render-chat-popout.ts";
import { RenderCombatTrackerConfig } from "./render-combat-tracker-config.ts";
Expand All @@ -33,7 +32,6 @@ export const HooksPF2e = {
DiceSoNiceReady,
DiceSoNiceRollStart,
DropCanvasData,
GetProseMirrorMenuDropDowns,
GetSceneControlButtons,
I18nInit,
Init,
Expand Down
103 changes: 0 additions & 103 deletions src/scripts/hooks/prosemirror-menu.ts

This file was deleted.

4 changes: 2 additions & 2 deletions types/foundry/common/prosemirror/menu.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export class ProseMirrorMenu extends ProseMirrorPlugin {
* @param [options] Additional options to configure behaviour.
* @param [options.attrs] Attributes for the node.
*/
_toggleBlock(
protected _toggleBlock(
node: ProseMirror.NodeType,
wrap: (node: ProseMirror.NodeType, attrs?: object | null) => ProseMirror.Command,
options?: { attrs?: Record<string, unknown> | null },
Expand All @@ -144,7 +144,7 @@ export class ProseMirrorMenu extends ProseMirrorPlugin {
* @param [options] Additional options to configure behaviour.
* @param [options.attrs] Attributes for the node.
*/
_toggleTextBlock(node: ProseMirror.NodeType, options?: { attrs?: Record<string, unknown> | null }): void;
protected _toggleTextBlock(node: ProseMirror.NodeType, options?: { attrs?: Record<string, unknown> | null }): void;
}

declare global {
Expand Down
Loading

0 comments on commit 6018278

Please sign in to comment.