UNPKG

14.2 kBTypeScriptView Raw
1import { ExtensionTagType } from '@remirror/core-constants';
2import type { EditorSchema, MarkExtensionSpec, MarkSpecOverride, NodeExtensionSpec, NodeSpecOverride, SchemaAttributes, Static } from '@remirror/core-types';
3import { AnyExtension, GetMarkNameUnion, GetNodeNameUnion, PlainExtension } from '../extension';
4import 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 */
47export 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 */
145export 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 */
195export type Identifiers = 'nodes' | 'marks' | 'all' | readonly string[] | IdentifiersObject;
196/**
197 * The interface for adding extra attributes to multiple node and mark
198 * extensions.
199 */
200export 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}
217declare 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}