1 | import { ExtensionTagType } from '@remirror/core-constants';
|
2 | import type { EditorSchema, MarkExtensionSpec, MarkSpecOverride, NodeExtensionSpec, NodeSpecOverride, SchemaAttributes, Static } from '@remirror/core-types';
|
3 | import { AnyExtension, GetMarkNameUnion, GetNodeNameUnion, PlainExtension } from '../extension';
|
4 | import type { CreateExtensionPlugin } from '../types';
|
5 | /**
|
6 | * This is the schema extension which creates the schema and provides extra
|
7 | * attributes as defined in the manager or the extension settings.
|
8 | *
|
9 | * @remarks
|
10 | *
|
11 | * The schema is the most important part of the remirror editor. This is the
|
12 | * extension responsible for creating it, injecting extra attributes and
|
13 | * managing the plugin which is responsible for making sure dynamically created
|
14 | * attributes are updated.
|
15 | *
|
16 | * In order to add extra attributes the following would work.
|
17 | *
|
18 | * ```ts
|
19 | * import { RemirrorManager } from 'remirror';
|
20 | * import uuid from 'uuid';
|
21 | * import hash from 'made-up-hasher';
|
22 | *
|
23 | * const manager = RemirrorManager.create([], {
|
24 | * extraAttributes: [
|
25 | * {
|
26 | * identifiers: 'nodes',
|
27 | * attributes: {
|
28 | * awesome: {
|
29 | * default: 'awesome',
|
30 | * parseDOM: (domNode) => domNode.getAttribute('data-awesome'),
|
31 | * toDOM: (attrs) => ([ 'data-awesome', attrs.awesome ])
|
32 | * },
|
33 | * },
|
34 | * },
|
35 | * { identifiers: ['paragraph'], attributes: { id: { default: () => uuid() } } },
|
36 | * { identifiers: ['bold'], attributes: { hash: (mark) => hash(JSON.stringify(mark.attrs)) } },
|
37 | * ],
|
38 | * })
|
39 | * ```
|
40 | *
|
41 | * It is an array of identifiers and attributes. Setting the default to a
|
42 | * function allows you to set up a dynamic attribute which is updated with the
|
43 | * synchronous function that you provide to it.
|
44 | *
|
45 | * @category Builtin Extension
|
46 | */
|
47 | export declare class SchemaExtension extends PlainExtension {
|
48 | get name(): "schema";
|
49 | /**
|
50 | * The dynamic attributes for each node and mark extension.
|
51 | *
|
52 | * The structure will look like the following.
|
53 | *
|
54 | * ```ts
|
55 | * {
|
56 | * paragraph: { id: () => uid(), hash: (node) => hash(node) },
|
57 | * bold: { random: () => Math.random(), created: () => Date.now() },
|
58 | * };
|
59 | * ```
|
60 | *
|
61 | * This object is used by the created plugin to listen for changes to the doc,
|
62 | * and check for new nodes and marks which haven't yet applied the dynamic
|
63 | * attribute and add the attribute.
|
64 | */
|
65 | private readonly dynamicAttributes;
|
66 | /**
|
67 | * This method is responsible for creating, configuring and adding the
|
68 | * `schema` to the editor. `Schema` is a special type in ProseMirror editors
|
69 | * and with `remirror` it's all just handled for you.
|
70 | */
|
71 | onCreate(): void;
|
72 | /**
|
73 | * This creates the plugin that is used to automatically create the dynamic
|
74 | * attributes defined in the extra attributes object.
|
75 | */
|
76 | createPlugin(): CreateExtensionPlugin;
|
77 | /**
|
78 | * Add the schema and nodes to the manager and extension store.
|
79 | */
|
80 | private addSchema;
|
81 | /**
|
82 | * Check the dynamic nodes to see if the provided node:
|
83 | *
|
84 | * - a) is dynamic and therefore can be updated.
|
85 | * - b) has just been created and does not yet have a value for the dynamic
|
86 | * node.
|
87 | *
|
88 | * @param node - the node
|
89 | * @param pos - the node's position
|
90 | * @param tr - the mutable ProseMirror transaction which is applied to create
|
91 | * the next editor state
|
92 | */
|
93 | private checkAndUpdateDynamicNodes;
|
94 | /**
|
95 | * Loop through the dynamic marks to see if the provided node:
|
96 | *
|
97 | * - a) is wrapped by a matching mark.
|
98 | * - b) has just been added and doesn't yet have the dynamic attribute
|
99 | * applied.
|
100 | *
|
101 | * @param node - the node
|
102 | * @param pos - the node's position
|
103 | * @param tr - the mutable ProseMirror transaction which is applied to create
|
104 | * the next editor state.
|
105 | */
|
106 | private checkAndUpdateDynamicMarks;
|
107 | /**
|
108 | * Gather all the extra attributes that have been added by extensions.
|
109 | */
|
110 | private gatherExtraAttributes;
|
111 | }
|
112 | /**
|
113 | * With tags, you can select a specific sub selection of marks and nodes. This
|
114 | * will be the basis for adding advanced formatting to remirror.
|
115 | *
|
116 | * ```ts
|
117 | * import { ExtensionTag } from 'remirror';
|
118 | * import { createCoreManager, CorePreset } from 'remirror/extensions';
|
119 | * import { WysiwygPreset } from 'remirror/extensions';
|
120 | *
|
121 | * const manager = createCoreManager(() => [new WysiwygPreset(), new CorePreset()], {
|
122 | * extraAttributes: [
|
123 | * {
|
124 | * identifiers: {
|
125 | * tags: [ExtensionTag.NodeBlock],
|
126 | * type: 'node',
|
127 | * },
|
128 | * attributes: { role: 'presentation' },
|
129 | * },
|
130 | * ],
|
131 | * });
|
132 | * ```
|
133 | *
|
134 | * Each item in the tags array should be read as an `OR` so the following would
|
135 | * match `Tag1` OR `Tag2` OR `Tag3`.
|
136 | *
|
137 | * ```json
|
138 | * { tags: ["Tag1", "Tag2", "Tag3"] }
|
139 | * ```
|
140 | *
|
141 | * The `type` property (`mark | node`) is exclusive and limits the type of
|
142 | * extension names that will be matched. When `mark` is set it only matches with
|
143 | * marks.
|
144 | */
|
145 | export interface IdentifiersObject {
|
146 | /**
|
147 | * Determines how the array of tags are combined:
|
148 | *
|
149 | * - `all` - the extension only matches when all tags are present.
|
150 | * - `any` - the extension will match if it includes any of the specified
|
151 | * tags.
|
152 | *
|
153 | * This only affects the `tags` property.
|
154 | *
|
155 | * The saddest part about this property is that, as a UK resident, I've
|
156 | * succumbed to using the Americanized spelling instead of the Oxford
|
157 | * Dictionary defined spelling of `behaviour` 😢
|
158 | *
|
159 | * @defaultValue 'any'
|
160 | */
|
161 | behavior?: 'all' | 'any';
|
162 | /**
|
163 | * Will find relevant names based on the defined `behaviour`.
|
164 | */
|
165 | tags?: ExtensionTagType[];
|
166 | /**
|
167 | * Additional names to include. These will still be added even if the
|
168 | * extension name matches with `excludeTags` member.
|
169 | */
|
170 | names?: string[];
|
171 | /**
|
172 | * Whether to restrict by whether this is a [[`ProsemirrorNode`]] or a
|
173 | * [[`Mark`]]. Leave blank to accept all types.
|
174 | */
|
175 | type?: 'node' | 'mark';
|
176 | /**
|
177 | * Exclude these names from being matched.
|
178 | */
|
179 | excludeNames?: string[];
|
180 | /**
|
181 | * Exclude these tags from being matched. Will always exclude if any of the
|
182 | * tags
|
183 | */
|
184 | excludeTags?: string[];
|
185 | }
|
186 | /**
|
187 | * The extra identifiers that can be used.
|
188 | *
|
189 | * - `nodes` - match all nodes
|
190 | * - `marks` - match all marks
|
191 | * - `all` - match everything in the editor
|
192 | * - `string[]` - match the selected node and mark names
|
193 | * - [[`IdentifiersObject`]] - match by `ExtensionTag` and type name.
|
194 | */
|
195 | export type Identifiers = 'nodes' | 'marks' | 'all' | readonly string[] | IdentifiersObject;
|
196 | /**
|
197 | * The interface for adding extra attributes to multiple node and mark
|
198 | * extensions.
|
199 | */
|
200 | export interface IdentifierSchemaAttributes {
|
201 | /**
|
202 | * The nodes or marks to add extra attributes to.
|
203 | *
|
204 | * This can either be an array of the strings or the following specific
|
205 | * identifiers:
|
206 | *
|
207 | * - 'nodes' for all nodes
|
208 | * - 'marks' for all marks
|
209 | * - 'all' for all extensions which touch the schema.
|
210 | */
|
211 | identifiers: Identifiers;
|
212 | /**
|
213 | * The attributes to be added.
|
214 | */
|
215 | attributes: SchemaAttributes;
|
216 | }
|
217 | declare global {
|
218 | namespace Remirror {
|
219 | interface BaseExtension {
|
220 | /**
|
221 | * Allows the extension to create an extra attributes array that will be
|
222 | * added to the extra attributes.
|
223 | *
|
224 | * For example the `@remirror/extension-bidi` adds a `dir` attribute to
|
225 | * all node extensions which allows them to automatically infer whether
|
226 | * the text direction should be right-to-left, or left-to-right.
|
227 | */
|
228 | createSchemaAttributes?(): IdentifierSchemaAttributes[];
|
229 | }
|
230 | interface BaseExtensionOptions {
|
231 | /**
|
232 | * Inject additional attributes into the defined mark / node schema. This
|
233 | * can only be used for `NodeExtensions` and `MarkExtensions`.
|
234 | *
|
235 | * @remarks
|
236 | *
|
237 | * Sometimes you need to add additional attributes to a node or mark. This
|
238 | * property enables this without needing to create a new extension.
|
239 | *
|
240 | * This is only applied to the `MarkExtension` and `NodeExtension`.
|
241 | *
|
242 | * @defaultValue {}
|
243 | */
|
244 | extraAttributes?: Static<SchemaAttributes>;
|
245 | /**
|
246 | * When true will disable extra attributes for this instance of the
|
247 | * extension.
|
248 | *
|
249 | * @remarks
|
250 | *
|
251 | * This is only applied to the `MarkExtension` and `NodeExtension`.
|
252 | *
|
253 | * @defaultValue undefined
|
254 | */
|
255 | disableExtraAttributes?: Static<boolean>;
|
256 | /**
|
257 | * An override for the mark spec object. This only applies for
|
258 | * `MarkExtension`.
|
259 | */
|
260 | markOverride?: Static<MarkSpecOverride>;
|
261 | /**
|
262 | * An override object for a node spec object. This only applies to the
|
263 | * `NodeExtension`.
|
264 | */
|
265 | nodeOverride?: Static<NodeSpecOverride>;
|
266 | }
|
267 | interface ManagerSettings {
|
268 | /**
|
269 | * Allows for setting extra attributes on multiple nodes and marks by
|
270 | * their name or constructor. These attributes are automatically added and
|
271 | * retrieved from from the dom by prosemirror.
|
272 | *
|
273 | * @remarks
|
274 | *
|
275 | * An example is shown below.
|
276 | *
|
277 | * ```ts
|
278 | * import { RemirrorManager } from 'remirror';
|
279 | *
|
280 | * const managerSettings = {
|
281 | * extraAttributes: [
|
282 | * {
|
283 | * identifiers: ['blockquote', 'heading'],
|
284 | * attributes: { id: 'id', alignment: '0', },
|
285 | * }, {
|
286 | * identifiers: ['mention', 'codeBlock'],
|
287 | * attributes: { 'userId': { default: null } },
|
288 | * },
|
289 | * ]
|
290 | * };
|
291 | *
|
292 | * const manager = RemirrorManager.create([], { extraAttributes })
|
293 | * ```
|
294 | */
|
295 | extraAttributes?: IdentifierSchemaAttributes[];
|
296 | /**
|
297 | * Overrides for the mark.
|
298 | */
|
299 | markOverride?: Record<string, MarkSpecOverride>;
|
300 | /**
|
301 | * Overrides for the nodes.
|
302 | */
|
303 | nodeOverride?: Record<string, NodeSpecOverride>;
|
304 | /**
|
305 | * Perhaps you don't need extra attributes at all in the editor. This
|
306 | * allows you to disable extra attributes when set to true.
|
307 | *
|
308 | * @defaultValue undefined
|
309 | */
|
310 | disableExtraAttributes?: boolean;
|
311 | /**
|
312 | * Setting this to a value will override the default behaviour of the
|
313 | * `RemirrorManager`. It overrides the created schema and ignores the
|
314 | * specs created by all extensions within your editor.
|
315 | *
|
316 | * @remarks
|
317 | *
|
318 | * This is an advanced option and should only be used in cases where there
|
319 | * is a deeper understanding of `Prosemirror`. By setting this, please
|
320 | * note that a lot of functionality just won't work which is powered by
|
321 | * the `extraAttributes`.
|
322 | */
|
323 | schema?: EditorSchema;
|
324 | /**
|
325 | * The name of the default block node. This node will be given a higher
|
326 | * priority when being added to the schema.
|
327 | *
|
328 | * By default this is undefined and the default block node is assigned
|
329 | * based on the extension priorities.
|
330 | *
|
331 | * @defaultValue undefined
|
332 | */
|
333 | defaultBlockNode?: string;
|
334 | }
|
335 | interface ManagerStore<Extension extends AnyExtension> {
|
336 | /**
|
337 | * The nodes to place on the schema.
|
338 | */
|
339 | nodes: Record<GetNodeNameUnion<Extension> extends never ? string : GetNodeNameUnion<Extension>, NodeExtensionSpec>;
|
340 | /**
|
341 | * The marks to be added to the schema.
|
342 | */
|
343 | marks: Record<GetMarkNameUnion<Extension> extends never ? string : GetMarkNameUnion<Extension>, MarkExtensionSpec>;
|
344 | /**
|
345 | * The schema created by this extension manager.
|
346 | */
|
347 | schema: EditorSchema;
|
348 | /**
|
349 | * The name of the default block node. This is used by all internal
|
350 | * extension when toggling block nodes. It can also be used in other
|
351 | * cases.
|
352 | *
|
353 | * This can be updated via the manager settings when first creating the
|
354 | * editor.
|
355 | *
|
356 | * @defaultValue 'paragraph'
|
357 | */
|
358 | defaultBlockNode: string;
|
359 | }
|
360 | interface MarkExtension {
|
361 | /**
|
362 | * Provides access to the `MarkExtensionSpec`.
|
363 | */
|
364 | spec: MarkExtensionSpec;
|
365 | }
|
366 | interface NodeExtension {
|
367 | /**
|
368 | * Provides access to the `NodeExtensionSpec`.
|
369 | */
|
370 | spec: NodeExtensionSpec;
|
371 | }
|
372 | interface ExtensionStore {
|
373 | /**
|
374 | * The Prosemirror schema being used for the current editor.
|
375 | *
|
376 | * @remarks
|
377 | *
|
378 | * The value is created when the manager initializes. So it can be used in
|
379 | * `createCommands`, `createHelpers`, `createKeymap` and most of the
|
380 | * creator methods.
|
381 | */
|
382 | schema: EditorSchema;
|
383 | }
|
384 | interface StaticExtensionOptions {
|
385 | /**
|
386 | * When true will disable extra attributes for all instances of this
|
387 | * extension.
|
388 | *
|
389 | * @defaultValue false
|
390 | */
|
391 | readonly disableExtraAttributes?: boolean;
|
392 | }
|
393 | interface AllExtensions {
|
394 | schema: SchemaExtension;
|
395 | }
|
396 | }
|
397 | }
|