@lblod/ember-rdfa-editor-lblod-plugins 19.3.1 → 20.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/.dockerignore +3 -0
  2. package/CHANGELOG.md +14 -0
  3. package/addon/components/besluit-topic-plugin/besluit-topic-toolbar-dropdown.ts +8 -10
  4. package/addon/components/besluit-type-plugin/toolbar-dropdown.ts +4 -20
  5. package/addon/components/decision-plugin/decision-plugin-card.gts +231 -0
  6. package/addon/components/decision-plugin/insert-article.gts +81 -0
  7. package/addon/components/hover-tooltip.gts +68 -0
  8. package/addon/components/prosemirror-editor.hbs +15 -0
  9. package/addon/components/prosemirror-editor.ts +98 -0
  10. package/addon/components/roadsign-regulation-plugin/roadsign-regulation-card.ts +16 -19
  11. package/addon/components/roadsign-regulation-plugin/roadsigns-modal.ts +23 -3
  12. package/addon/components/structure-plugin/_private/control-card.gts +192 -0
  13. package/addon/components/structure-plugin/_private/structure.gts +159 -0
  14. package/addon/plugins/besluit-topic-plugin/utils/helpers.ts +19 -10
  15. package/addon/plugins/decision-plugin/commands/insert-article-command.ts +82 -0
  16. package/addon/plugins/decision-plugin/commands/insert-article-container.ts +33 -35
  17. package/addon/plugins/decision-plugin/commands/insert-description.ts +31 -28
  18. package/addon/plugins/decision-plugin/commands/insert-motivation.ts +106 -106
  19. package/addon/plugins/decision-plugin/commands/insert-title.ts +30 -28
  20. package/addon/plugins/decision-plugin/utils/build-article-structure.ts +44 -0
  21. package/addon/plugins/roadsign-regulation-plugin/index.ts +1 -0
  22. package/addon/plugins/structure-plugin/move-structure.ts +120 -0
  23. package/addon/plugins/structure-plugin/node.ts +148 -0
  24. package/addon/plugins/structure-plugin/recalculate-structure-numbers.ts +28 -0
  25. package/addon/utils/find-insertion-pos-in-node.ts +21 -0
  26. package/addon/utils/nested-prosemirror.ts +111 -0
  27. package/app/styles/structure-plugin.scss +53 -0
  28. package/declarations/addon/components/besluit-topic-plugin/besluit-topic-toolbar-dropdown.d.ts +2 -1
  29. package/declarations/addon/components/besluit-type-plugin/toolbar-dropdown.d.ts +1 -2
  30. package/declarations/addon/components/decision-plugin/decision-plugin-card.d.ts +20 -11
  31. package/declarations/addon/components/decision-plugin/insert-article.d.ts +23 -0
  32. package/declarations/addon/components/hover-tooltip.d.ts +26 -9
  33. package/declarations/addon/components/prosemirror-editor.d.ts +23 -0
  34. package/declarations/addon/components/roadsign-regulation-plugin/roadsigns-modal.d.ts +6 -0
  35. package/declarations/addon/components/structure-plugin/_private/control-card.d.ts +22 -0
  36. package/declarations/addon/components/structure-plugin/_private/structure.d.ts +32 -0
  37. package/declarations/addon/plugins/besluit-topic-plugin/utils/helpers.d.ts +2 -2
  38. package/declarations/addon/plugins/decision-plugin/commands/insert-article-command.d.ts +8 -0
  39. package/declarations/addon/plugins/decision-plugin/commands/insert-article-container.d.ts +4 -2
  40. package/declarations/addon/plugins/decision-plugin/commands/insert-description.d.ts +3 -2
  41. package/declarations/addon/plugins/decision-plugin/commands/insert-motivation.d.ts +3 -2
  42. package/declarations/addon/plugins/decision-plugin/commands/insert-title.d.ts +3 -2
  43. package/declarations/addon/plugins/decision-plugin/utils/build-article-structure.d.ts +2 -0
  44. package/declarations/addon/plugins/roadsign-regulation-plugin/index.d.ts +1 -0
  45. package/declarations/addon/plugins/structure-plugin/move-structure.d.ts +2 -0
  46. package/declarations/addon/plugins/structure-plugin/node.d.ts +4 -0
  47. package/declarations/addon/plugins/structure-plugin/recalculate-structure-numbers.d.ts +3 -0
  48. package/declarations/addon/utils/find-insertion-pos-in-node.d.ts +3 -0
  49. package/declarations/addon/utils/nested-prosemirror.d.ts +23 -0
  50. package/package.json +10 -5
  51. package/pnpm-lock.yaml +73 -14
  52. package/translations/en-US.yaml +9 -0
  53. package/translations/nl-BE.yaml +10 -3
  54. package/types/ember-power-select/components/power-select.d.ts +3 -3
  55. package/addon/components/decision-plugin/decision-plugin-card.hbs +0 -55
  56. package/addon/components/decision-plugin/decision-plugin-card.ts +0 -71
  57. package/addon/components/hover-tooltip.hbs +0 -12
  58. package/addon/components/hover-tooltip.ts +0 -41
@@ -1,26 +1,26 @@
1
- import {
2
- EditorState,
3
- NodeSelection,
4
- Transaction,
5
- } from '@lblod/ember-rdfa-editor';
6
- import { isNone } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/option';
7
- import { transactionCompliesWithShapes } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/validation/utils/transaction-complies-with-shapes';
8
- import { findInsertionPosInAncestorOfType } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/find-insertion-pos-in-ancestor-of-type';
1
+ import { EditorState, Transaction } from '@lblod/ember-rdfa-editor';
2
+ import { NodeWithPos } from '@curvenote/prosemirror-utils';
3
+ import { v4 as uuid } from 'uuid';
4
+ import { addPropertyToNode } from '@lblod/ember-rdfa-editor/utils/rdfa-utils';
5
+ import { ELI } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
6
+ import { SayDataFactory } from '@lblod/ember-rdfa-editor/core/say-data-factory';
7
+ import { transactionCombinator } from '@lblod/ember-rdfa-editor/utils/transaction-utils';
9
8
 
10
9
  interface InsertDescriptionArgs {
11
10
  placeholderText: string;
12
- validateShapes?: Set<string>;
11
+ decisionLocation: NodeWithPos;
13
12
  }
14
13
 
15
14
  export default function insertDescription({
16
15
  placeholderText,
17
- validateShapes,
16
+ decisionLocation,
18
17
  }: InsertDescriptionArgs) {
19
18
  return function (state: EditorState, dispatch?: (tr: Transaction) => void) {
20
- const { selection, schema } = state;
19
+ const { schema } = state;
20
+ const descriptionId = uuid();
21
21
  const nodeToInsert = schema.node(
22
- 'description',
23
- {},
22
+ 'block_rdfa',
23
+ { rdfaNodeType: 'literal', __rdfaId: descriptionId },
24
24
  schema.node(
25
25
  'paragraph',
26
26
  null,
@@ -29,23 +29,26 @@ export default function insertDescription({
29
29
  }),
30
30
  ),
31
31
  );
32
- const insertionPos = findInsertionPosInAncestorOfType(
33
- selection,
34
- schema.nodes.besluit,
35
- nodeToInsert,
36
- );
37
- if (isNone(insertionPos)) {
38
- return false;
39
- }
40
32
  const tr = state.tr;
41
33
 
42
- tr.replaceRangeWith(insertionPos, insertionPos, nodeToInsert);
43
- if (!transactionCompliesWithShapes(state, tr, validateShapes)) {
44
- return false;
45
- }
46
- if (dispatch) {
47
- tr.setSelection(NodeSelection.create(tr.doc, insertionPos + 2));
48
- dispatch(tr.scrollIntoView());
34
+ tr.replaceSelectionWith(nodeToInsert);
35
+
36
+ const factory = new SayDataFactory();
37
+ const { transaction: newTr, result } = transactionCombinator<boolean>(
38
+ state,
39
+ tr,
40
+ )([
41
+ addPropertyToNode({
42
+ resource: decisionLocation.node.attrs.subject,
43
+ property: {
44
+ predicate: ELI('description').full,
45
+ object: factory.literalNode(descriptionId),
46
+ },
47
+ }),
48
+ ]);
49
+ if (dispatch && result.every((ok) => ok)) {
50
+ // newTr.setSelection(NodeSelection.create(newTr.doc, insertionPos + 2));
51
+ dispatch(newTr.scrollIntoView());
49
52
  }
50
53
  return true;
51
54
  };
@@ -1,135 +1,135 @@
1
- import {
2
- Command,
3
- EditorState,
4
- NodeSelection,
5
- Transaction,
6
- } from '@lblod/ember-rdfa-editor';
7
- import { isNone } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/option';
8
- import { transactionCompliesWithShapes } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/validation/utils/transaction-complies-with-shapes';
9
- import { findInsertionPosInAncestorOfType } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/find-insertion-pos-in-ancestor-of-type';
1
+ import { Command, EditorState, Transaction } from '@lblod/ember-rdfa-editor';
10
2
  import IntlService from 'ember-intl/services/intl';
11
3
  import { getTranslationFunction } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/translation';
4
+ import { BESLUIT } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
5
+ import { NodeWithPos } from '@curvenote/prosemirror-utils';
6
+ import { v4 as uuid } from 'uuid';
7
+ import { addPropertyToNode } from '@lblod/ember-rdfa-editor/utils/_private/rdfa-utils';
8
+ import { SayDataFactory } from '@lblod/ember-rdfa-editor/core/say-data-factory';
9
+ import { transactionCombinator } from '@lblod/ember-rdfa-editor/utils/transaction-utils';
12
10
 
13
11
  interface InsertMotivationArgs {
14
12
  intl: IntlService;
15
- validateShapes?: Set<string>;
13
+ decisionLocation: NodeWithPos;
16
14
  }
17
15
 
18
16
  export default function insertMotivation({
19
17
  intl,
20
- validateShapes,
18
+ decisionLocation,
21
19
  }: InsertMotivationArgs): Command {
22
20
  return function (state: EditorState, dispatch?: (tr: Transaction) => void) {
23
21
  const translationWithDocLang = getTranslationFunction(state);
24
- const { selection, schema } = state;
25
- const nodeToInsert = schema.node('motivering', {}, [
26
- schema.node(
27
- 'paragraph',
28
- null,
29
- schema.node('placeholder', {
30
- placeholderText: translationWithDocLang(
31
- 'besluit-plugin.placeholder.government-body',
32
- intl.t('besluit-plugin.placeholder.government-body'),
33
- ),
34
- }),
35
- ),
36
- schema.node(
37
- 'heading',
38
- {
39
- level: 5,
40
- },
41
- [schema.text(intl.t('besluit-plugin.text.authority'))],
42
- ),
43
- schema.node('bullet_list', null, [
44
- schema.node('list_item', null, [
45
- schema.node('paragraph', null, [
46
- schema.node('placeholder', {
47
- placeholderText: translationWithDocLang(
48
- 'besluit-plugin.placeholder.legal-jurisdiction',
49
- intl.t('besluit-plugin.placeholder.legal-jurisdiction'),
50
- ),
51
- }),
22
+ const { schema } = state;
23
+ const decisionNode = decisionLocation;
24
+ const motivationId = uuid();
25
+ const nodeToInsert = schema.node(
26
+ 'block_rdfa',
27
+ { rdfaNodeType: 'literal', __rdfaId: motivationId },
28
+ [
29
+ schema.node(
30
+ 'paragraph',
31
+ null,
32
+ schema.node('placeholder', {
33
+ placeholderText: translationWithDocLang(
34
+ 'besluit-plugin.placeholder.government-body',
35
+ intl.t('besluit-plugin.placeholder.government-body'),
36
+ ),
37
+ }),
38
+ ),
39
+ schema.node(
40
+ 'heading',
41
+ {
42
+ level: 5,
43
+ },
44
+ [schema.text(intl.t('besluit-plugin.text.authority'))],
45
+ ),
46
+ schema.node('bullet_list', null, [
47
+ schema.node('list_item', null, [
48
+ schema.node('paragraph', null, [
49
+ schema.node('placeholder', {
50
+ placeholderText: translationWithDocLang(
51
+ 'besluit-plugin.placeholder.legal-jurisdiction',
52
+ intl.t('besluit-plugin.placeholder.legal-jurisdiction'),
53
+ ),
54
+ }),
55
+ ]),
52
56
  ]),
53
57
  ]),
54
- ]),
55
- schema.node(
56
- 'heading',
57
- {
58
- level: 5,
59
- },
60
- [
61
- schema.text(
62
- translationWithDocLang(
63
- 'besluit-plugin.text.legal-context',
64
- intl.t('besluit-plugin.text.legal-context'),
65
- ),
66
- ),
67
- ],
68
- ),
69
- schema.node('bullet_list', null, [
70
- schema.node('list_item', null, [
71
- schema.node('paragraph', null, [
72
- schema.node('placeholder', {
73
- placeholderText: translationWithDocLang(
74
- 'besluit-plugin.placeholder.insert-legal-context',
75
- intl.t('besluit-plugin.placeholder.insert-legal-context'),
58
+ schema.node(
59
+ 'heading',
60
+ {
61
+ level: 5,
62
+ },
63
+ [
64
+ schema.text(
65
+ translationWithDocLang(
66
+ 'besluit-plugin.text.legal-context',
67
+ intl.t('besluit-plugin.text.legal-context'),
76
68
  ),
77
- }),
69
+ ),
70
+ ],
71
+ ),
72
+ schema.node('bullet_list', null, [
73
+ schema.node('list_item', null, [
74
+ schema.node('paragraph', null, [
75
+ schema.node('placeholder', {
76
+ placeholderText: translationWithDocLang(
77
+ 'besluit-plugin.placeholder.insert-legal-context',
78
+ intl.t('besluit-plugin.placeholder.insert-legal-context'),
79
+ ),
80
+ }),
81
+ ]),
78
82
  ]),
79
83
  ]),
80
- ]),
81
- schema.node(
82
- 'heading',
83
- {
84
- level: 5,
85
- },
86
- [
87
- schema.text(
88
- translationWithDocLang(
89
- 'besluit-plugin.text.factual-context',
90
- intl.t('besluit-plugin.text.factual-context'),
91
- ),
92
- ),
93
- ],
94
- ),
95
- schema.node('bullet_list', null, [
96
- schema.node('list_item', null, [
97
- schema.node('paragraph', null, [
98
- schema.node('placeholder', {
99
- placeholderText: translationWithDocLang(
100
- 'besluit-plugin.placeholder.insert-factual-context',
101
- intl.t('besluit-plugin.placeholder.insert-factual-context'),
84
+ schema.node(
85
+ 'heading',
86
+ {
87
+ level: 5,
88
+ },
89
+ [
90
+ schema.text(
91
+ translationWithDocLang(
92
+ 'besluit-plugin.text.factual-context',
93
+ intl.t('besluit-plugin.text.factual-context'),
102
94
  ),
103
- }),
95
+ ),
96
+ ],
97
+ ),
98
+ schema.node('bullet_list', null, [
99
+ schema.node('list_item', null, [
100
+ schema.node('paragraph', null, [
101
+ schema.node('placeholder', {
102
+ placeholderText: translationWithDocLang(
103
+ 'besluit-plugin.placeholder.insert-factual-context',
104
+ intl.t('besluit-plugin.placeholder.insert-factual-context'),
105
+ ),
106
+ }),
107
+ ]),
104
108
  ]),
105
109
  ]),
106
- ]),
107
- ]);
108
- // how the offset between the insertion point and the point where the cursor should end up
109
- const cursorOffset = 2;
110
-
111
- const insertionPos = findInsertionPosInAncestorOfType(
112
- selection,
113
- schema.nodes.besluit,
114
- nodeToInsert,
110
+ ],
115
111
  );
116
- if (isNone(insertionPos)) {
117
- return false;
118
- }
119
112
  const tr = state.tr;
113
+ tr.replaceSelectionWith(nodeToInsert);
114
+ const factory = new SayDataFactory();
120
115
 
121
- tr.replaceRangeWith(insertionPos, insertionPos, nodeToInsert);
122
- if (!transactionCompliesWithShapes(state, tr, validateShapes)) {
116
+ const { transaction: newTr, result } = transactionCombinator<boolean>(
117
+ state,
118
+ tr,
119
+ )([
120
+ addPropertyToNode({
121
+ resource: decisionNode.node.attrs.subject,
122
+ property: {
123
+ predicate: BESLUIT('motivering').full,
124
+ object: factory.literalNode(motivationId),
125
+ },
126
+ }),
127
+ ]);
128
+ if (result.some((success) => !success)) {
123
129
  return false;
124
130
  }
125
131
  if (dispatch) {
126
- const selectionPos = tr.doc.resolve(insertionPos + cursorOffset);
127
- // const targetPos = tr.doc.resolve(insertionPos + cursorOffset + 1);
128
- // TODO figure out why I cant just set a nodeSelection here
129
- tr.setSelection(
130
- new NodeSelection(tr.doc.resolve(selectionPos.posAtIndex(0))),
131
- );
132
- dispatch(tr);
132
+ dispatch(newTr);
133
133
  }
134
134
  return true;
135
135
  };
@@ -1,26 +1,26 @@
1
- import { isNone } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/option';
2
- import {
3
- EditorState,
4
- NodeSelection,
5
- Transaction,
6
- } from '@lblod/ember-rdfa-editor';
7
- import { transactionCompliesWithShapes } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/validation/utils/transaction-complies-with-shapes';
8
- import { findInsertionPosInAncestorOfType } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/find-insertion-pos-in-ancestor-of-type';
1
+ import { EditorState, Transaction } from '@lblod/ember-rdfa-editor';
2
+ import { NodeWithPos } from '@curvenote/prosemirror-utils';
3
+ import { v4 as uuid } from 'uuid';
4
+ import { addPropertyToNode } from '@lblod/ember-rdfa-editor/utils/rdfa-utils';
5
+ import { ELI } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
6
+ import { SayDataFactory } from '@lblod/ember-rdfa-editor/core/say-data-factory';
7
+ import { transactionCombinator } from '@lblod/ember-rdfa-editor/utils/transaction-utils';
9
8
 
10
9
  interface InsertTitleArgs {
11
10
  placeholderText: string;
12
- validateShapes?: Set<string>;
11
+ decisionLocation: NodeWithPos;
13
12
  }
14
13
 
15
14
  export default function insertTitle({
16
15
  placeholderText,
17
- validateShapes,
16
+ decisionLocation,
18
17
  }: InsertTitleArgs) {
19
18
  return function (state: EditorState, dispatch?: (tr: Transaction) => void) {
20
- const { selection, schema } = state;
19
+ const { schema } = state;
20
+ const titleId = uuid();
21
21
  const nodeToInsert = schema.node(
22
- 'besluit_title',
23
- {},
22
+ 'block_rdfa',
23
+ { rdfaNodeType: 'literal', __rdfaId: titleId },
24
24
  schema.node(
25
25
  'paragraph',
26
26
  null,
@@ -30,23 +30,25 @@ export default function insertTitle({
30
30
  ),
31
31
  );
32
32
 
33
- const insertionPos = findInsertionPosInAncestorOfType(
34
- selection,
35
- schema.nodes.besluit,
36
- nodeToInsert,
37
- );
38
- if (isNone(insertionPos)) {
39
- return false;
40
- }
41
33
  const tr = state.tr;
42
- tr.replaceRangeWith(insertionPos, insertionPos, nodeToInsert);
34
+ tr.replaceSelectionWith(nodeToInsert);
43
35
 
44
- if (!transactionCompliesWithShapes(state, tr, validateShapes)) {
45
- return false;
46
- }
47
- if (dispatch) {
48
- tr.setSelection(NodeSelection.create(tr.doc, insertionPos + 2));
49
- dispatch(tr.scrollIntoView());
36
+ const factory = new SayDataFactory();
37
+ const { transaction: newTr, result } = transactionCombinator<boolean>(
38
+ state,
39
+ tr,
40
+ )([
41
+ addPropertyToNode({
42
+ resource: decisionLocation.node.attrs.subject,
43
+ property: {
44
+ predicate: ELI('title').full,
45
+ object: factory.literalNode(titleId),
46
+ },
47
+ }),
48
+ ]);
49
+ if (dispatch && result.every((ok) => ok)) {
50
+ // newTr.setSelection(NodeSelection.create(newTr.doc, insertionPos + 2));
51
+ dispatch(newTr.scrollIntoView());
50
52
  }
51
53
  return true;
52
54
  };
@@ -0,0 +1,44 @@
1
+ import { Schema } from '@lblod/ember-rdfa-editor';
2
+ import {
3
+ BESLUIT,
4
+ RDF,
5
+ } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
6
+ import { OutgoingTriple } from '@lblod/ember-rdfa-editor/core/rdfa-processor';
7
+ import { SayDataFactory } from '@lblod/ember-rdfa-editor/core/say-data-factory';
8
+ import { v4 as uuid } from 'uuid';
9
+
10
+ export function buildArticleStructure(
11
+ schema: Schema,
12
+ uriGenerator?: () => string,
13
+ ) {
14
+ let articleResource: string;
15
+ if (uriGenerator) {
16
+ articleResource = uriGenerator();
17
+ } else {
18
+ const articleId = uuid();
19
+ articleResource = `http://data.lblod.info/artikels/--ref-uuid4-${articleId}`;
20
+ }
21
+ const factory = new SayDataFactory();
22
+ return schema.node(
23
+ 'structure',
24
+ {
25
+ rdfaNodeType: 'resource',
26
+ properties: [
27
+ {
28
+ predicate: RDF('type').full,
29
+ object: factory.namedNode(BESLUIT('Artikel').full),
30
+ },
31
+ ] satisfies OutgoingTriple[],
32
+ hasTitle: true,
33
+ title: '',
34
+ structureName: 'Artikel',
35
+ headerTag: 'h5',
36
+ subject: articleResource,
37
+ },
38
+ schema.node(
39
+ 'paragraph',
40
+ {},
41
+ schema.node('placeholder', { placeholderText: 'Voeg inhoud artikel in' }),
42
+ ),
43
+ );
44
+ }
@@ -1,4 +1,5 @@
1
1
  export type RoadsignRegulationPluginOptions = {
2
2
  endpoint: string;
3
3
  imageBaseUrl: string;
4
+ articleUriGenrator?: () => string;
4
5
  };
@@ -0,0 +1,120 @@
1
+ import {
2
+ Command,
3
+ PNode,
4
+ ResolvedPos,
5
+ TextSelection,
6
+ } from '@lblod/ember-rdfa-editor';
7
+ import { findAncestorOfType } from '../article-structure-plugin/utils/structure';
8
+ import {
9
+ findNodePosDown,
10
+ findNodePosUp,
11
+ } from '@lblod/ember-rdfa-editor/utils/position-utils';
12
+ import {
13
+ isNone,
14
+ unwrap,
15
+ } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/option';
16
+ import { recalculateNumbers } from './recalculate-structure-numbers';
17
+ import { transactionCombinator } from '@lblod/ember-rdfa-editor/utils/transaction-utils';
18
+
19
+ export function moveStructure(direction: 'up' | 'down'): Command {
20
+ return (state, dispatch) => {
21
+ const { doc, selection, schema } = state;
22
+ const structure = findAncestorOfType(selection, schema.nodes['structure']);
23
+ if (!structure) {
24
+ return false;
25
+ }
26
+ const { pos, node } = structure;
27
+ const currentPos = node.attrs['number'];
28
+ if (direction === 'up') {
29
+ if (currentPos <= 1) {
30
+ return false;
31
+ }
32
+ const previousStructurePos =
33
+ findNodePosUp(doc, pos, ($pos: ResolvedPos) => {
34
+ const node = $pos.nodeAfter;
35
+ if (node) {
36
+ return node.type.name === 'structure';
37
+ }
38
+ return false;
39
+ }).next().value ?? null;
40
+ if (isNone(previousStructurePos)) {
41
+ return false;
42
+ } else {
43
+ if (dispatch) {
44
+ const transaction = state.tr;
45
+ // we are moving the structure up, so if we delete it first, it won't affect the positions
46
+ // before it
47
+ transaction.delete(pos, pos + node.nodeSize);
48
+
49
+ // previousStructurePos is the position right BEFORE the previous structure,
50
+ // so inserting there is correct
51
+ transaction.insert(previousStructurePos, node);
52
+ // after updating we have to recalculate the numbers
53
+ const { transaction: newTr } = transactionCombinator(
54
+ state,
55
+ transaction,
56
+ )([recalculateNumbers]);
57
+
58
+ // previousStructurePos should now point to the position right before our moved structure
59
+ // so we can simply add 1 to get the first position inside of it, and for the end
60
+ // we add the nodesize and subtract one
61
+ newTr.setSelection(
62
+ TextSelection.create(
63
+ newTr.doc,
64
+ previousStructurePos + 1,
65
+ previousStructurePos + node.nodeSize - 1,
66
+ ),
67
+ );
68
+ dispatch(newTr);
69
+ }
70
+ return true;
71
+ }
72
+ } else {
73
+ // findNodePosUp and findNodePosDown have a different interface (historic reasons)
74
+ const nextStructureParentPos =
75
+ findNodePosDown(
76
+ doc,
77
+ doc.resolve(pos + node.nodeSize),
78
+ (parent: PNode) => {
79
+ return parent.type.name === 'structure';
80
+ },
81
+ ).next().value ?? null;
82
+ if (isNone(nextStructureParentPos)) {
83
+ return false;
84
+ }
85
+ // findNodePosDown has a weird quirk where it can only find positions IN nodes
86
+ // cause the filter function only receives the position's parent
87
+ // so we need to step back one here (or make a way more complicated filter)
88
+ const nextStructurePos = nextStructureParentPos - 1;
89
+ if (dispatch) {
90
+ const transaction = state.tr;
91
+ const $structurePos = doc.resolve(nextStructurePos);
92
+ const nextStructureNode = unwrap($structurePos.nodeAfter);
93
+
94
+ transaction.delete(pos, pos + node.nodeSize);
95
+ const insertPos = transaction.mapping.map(
96
+ nextStructurePos + nextStructureNode.nodeSize,
97
+ );
98
+ transaction.insert(insertPos, node);
99
+
100
+ const { transaction: newTr } = transactionCombinator(
101
+ state,
102
+ transaction,
103
+ )([recalculateNumbers]);
104
+
105
+ // since we've deleted something before the insert position, we have to map
106
+ // the positions through the transaction to find the moved node's new position
107
+ newTr.setSelection(
108
+ TextSelection.create(
109
+ newTr.doc,
110
+ insertPos + 1,
111
+ insertPos + node.nodeSize - 1,
112
+ ),
113
+ );
114
+ dispatch(newTr);
115
+ }
116
+
117
+ return true;
118
+ }
119
+ };
120
+ }