Skip to content

Commit e6a8320

Browse files
authored
fix(Placeholder): create new decorations on every EditorProps.decorations() call (#62)
1 parent a56372a commit e6a8320

File tree

1 file changed

+31
-18
lines changed
  • src/extensions/behavior/Placeholder

1 file changed

+31
-18
lines changed

src/extensions/behavior/Placeholder/index.ts

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const placeholderNeeded = (node: Node) => {
5252
};
5353

5454
const addDecoration = (
55-
decorationsMap: Record<number, Decoration | PluginKey>,
55+
widgetsMap: WidgetsMap,
5656
node: Node,
5757
pos: number,
5858
parent: Node | null,
@@ -63,10 +63,10 @@ const addDecoration = (
6363
const decorationPosition = pos + node.childCount + 1;
6464

6565
// Не добавляем декорацию если на этой позиции уже есть плейсхолдер
66-
if (!placeholderSpec || decorationsMap[decorationPosition]) return;
66+
if (!placeholderSpec || widgetsMap[decorationPosition]) return;
6767

6868
if (placeholderSpec.customPlugin) {
69-
decorationsMap[decorationPosition] = placeholderSpec.customPlugin;
69+
widgetsMap[decorationPosition] = placeholderSpec.customPlugin;
7070

7171
return;
7272
}
@@ -76,19 +76,28 @@ const addDecoration = (
7676
globalState.hasFocus = true;
7777
}
7878

79-
decorationsMap[decorationPosition] = Decoration.widget(
80-
decorationPosition,
81-
createPlaceholder(node, parent, focus),
82-
);
79+
widgetsMap[decorationPosition] = {
80+
pos: decorationPosition,
81+
toDOM: createPlaceholder(node, parent, focus),
82+
};
8383
};
8484

8585
type ApplyGlobalState = {hasFocus: boolean};
8686

87+
type DecoWidgetParameters = Parameters<typeof Decoration.widget>;
88+
type WidgetSpec = {
89+
pos: DecoWidgetParameters[0];
90+
toDOM: DecoWidgetParameters[1];
91+
spec?: DecoWidgetParameters[2];
92+
};
93+
8794
type PlaceholderPluginState = {
88-
decorations: Decoration[];
95+
widgets: WidgetSpec[];
8996
hasFocus: boolean;
9097
};
9198

99+
type WidgetsMap = Record<number, WidgetSpec | PluginKey>;
100+
92101
const pluginKey = new PluginKey<PlaceholderPluginState>('placeholder_plugin');
93102

94103
export const Placeholder: ExtensionAuto = (builder) => {
@@ -106,8 +115,13 @@ export const Placeholder: ExtensionAuto = (builder) => {
106115
return attrs;
107116
},
108117
decorations(state) {
109-
const {decorations} = pluginKey.getState(state)!;
110-
return DecorationSet.create(state.doc, decorations);
118+
const {widgets} = pluginKey.getState(state)!;
119+
return DecorationSet.create(
120+
state.doc,
121+
widgets.map((widget) =>
122+
Decoration.widget(widget.pos, widget.toDOM, widget.spec),
123+
),
124+
);
111125
},
112126
},
113127
state: {
@@ -120,15 +134,14 @@ export const Placeholder: ExtensionAuto = (builder) => {
120134

121135
function applyState(state: EditorState): PlaceholderPluginState {
122136
const globalState: ApplyGlobalState = {hasFocus: false};
123-
const decorationsMap: Record<number, Decoration | PluginKey> = {};
137+
const widgetsMap: WidgetsMap = {};
124138
const {selection} = state;
125139
const cursorPos = isTextSelection(selection) ? selection.$cursor?.pos : null;
126140

127141
getPlaceholderPluginKeys(state.schema).forEach((f) => {
128142
// Используем find потому что при помощи него можно проитерировать по DecorationSet.
129143
f.getState(state)?.find(undefined, undefined, (spec) => {
130-
decorationsMap[spec.pos] = f;
131-
144+
widgetsMap[spec.pos] = f;
132145
return false;
133146
});
134147
});
@@ -138,7 +151,7 @@ function applyState(state: EditorState): PlaceholderPluginState {
138151
const placeholderSpec = node.type.spec.placeholder;
139152

140153
if (placeholderSpec && placeholderSpec.alwaysVisible && placeholderNeeded(node)) {
141-
addDecoration(decorationsMap, node, pos, parent, cursorPos, globalState);
154+
addDecoration(widgetsMap, node, pos, parent, cursorPos, globalState);
142155
}
143156
};
144157

@@ -159,14 +172,14 @@ function applyState(state: EditorState): PlaceholderPluginState {
159172
) {
160173
const {node, pos, depth} = parentNode;
161174
const parent = depth > 0 ? state.selection.$from.node(depth - 1) : null;
162-
addDecoration(decorationsMap, node, pos, parent, cursorPos, globalState);
175+
addDecoration(widgetsMap, node, pos, parent, cursorPos, globalState);
163176
}
164177

165-
const decorations = Object.values(decorationsMap).filter(
178+
const widgets = Object.values(widgetsMap).filter(
166179
(decoration) => !(decoration instanceof PluginKey),
167-
) as Decoration[];
180+
) as WidgetSpec[];
168181

169-
return {decorations, hasFocus: globalState.hasFocus};
182+
return {widgets, hasFocus: globalState.hasFocus};
170183
}
171184

172185
declare module 'prosemirror-model' {

0 commit comments

Comments
 (0)