@lblod/ember-rdfa-editor-lblod-plugins 38.0.3 → 38.1.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.
- package/CHANGELOG.md +27 -0
- package/addon/components/snippet-plugin/nodes/placeholder.gts +4 -6
- package/addon/components/snippet-plugin/nodes/snippet.gts +34 -8
- package/addon/components/snippet-plugin/search-modal.gts +12 -6
- package/addon/components/snippet-plugin/snippet-insert-rdfa.gts +0 -2
- package/addon/plugins/snippet-plugin/commands/update-snippet-placeholder.ts +2 -11
- package/addon/plugins/snippet-plugin/index.ts +2 -1
- package/addon/plugins/snippet-plugin/nodes/snippet-placeholder.ts +0 -12
- package/addon/plugins/snippet-plugin/nodes/snippet.ts +1 -11
- package/addon/plugins/snippet-plugin/utils/fetch-data.ts +55 -16
- package/addon/plugins/variable-plugin/contextual-actions/codelist.ts +132 -0
- package/addon/plugins/variable-plugin/contextual-actions/index.ts +6 -217
- package/addon/plugins/variable-plugin/contextual-actions/place-description.ts +222 -0
- package/addon/plugins/variable-plugin/utils/codelist-utils.ts +46 -25
- package/addon/services/snippet-fetch-service.ts +76 -0
- package/app/services/snippet-fetch-service.js +1 -0
- package/declarations/addon/components/snippet-plugin/nodes/placeholder.d.ts +1 -1
- package/declarations/addon/components/snippet-plugin/nodes/snippet.d.ts +7 -1
- package/declarations/addon/components/snippet-plugin/search-modal.d.ts +5 -2
- package/declarations/addon/plugins/location-plugin/node.d.ts +1 -1
- package/declarations/addon/plugins/locked-placeholder-plugin/nodes/block-locked-placeholder.d.ts +1 -1
- package/declarations/addon/plugins/locked-placeholder-plugin/nodes/inline-locked-placeholder.d.ts +1 -1
- package/declarations/addon/plugins/mandatee-table-plugin/node.d.ts +1 -1
- package/declarations/addon/plugins/snippet-plugin/index.d.ts +2 -1
- package/declarations/addon/plugins/snippet-plugin/nodes/snippet-placeholder.d.ts +1 -1
- package/declarations/addon/plugins/snippet-plugin/nodes/snippet.d.ts +2 -2
- package/declarations/addon/plugins/snippet-plugin/utils/fetch-data.d.ts +9 -5
- package/declarations/addon/plugins/structure-plugin/node.d.ts +2 -2
- package/declarations/addon/plugins/table-of-contents-plugin/nodes/table-of-contents.d.ts +1 -1
- package/declarations/addon/plugins/template-comments-plugin/node.d.ts +1 -1
- package/declarations/addon/plugins/variable-plugin/contextual-actions/codelist.d.ts +14 -0
- package/declarations/addon/plugins/variable-plugin/contextual-actions/index.d.ts +5 -15
- package/declarations/addon/plugins/variable-plugin/contextual-actions/place-description.d.ts +15 -0
- package/declarations/addon/plugins/variable-plugin/utils/codelist-utils.d.ts +3 -1
- package/declarations/addon/plugins/variable-plugin/variables/address.d.ts +1 -1
- package/declarations/addon/plugins/variable-plugin/variables/autofilled.d.ts +1 -1
- package/declarations/addon/plugins/variable-plugin/variables/codelist.d.ts +1 -1
- package/declarations/addon/plugins/variable-plugin/variables/date.d.ts +1 -1
- package/declarations/addon/plugins/variable-plugin/variables/legacy-codelist.d.ts +1 -1
- package/declarations/addon/plugins/variable-plugin/variables/location.d.ts +1 -1
- package/declarations/addon/plugins/variable-plugin/variables/number.d.ts +1 -1
- package/declarations/addon/plugins/variable-plugin/variables/person.d.ts +1 -1
- package/declarations/addon/plugins/variable-plugin/variables/text.d.ts +1 -1
- package/declarations/addon/services/snippet-fetch-service.d.ts +20 -0
- package/package.json +3 -3
- package/pnpm-workspace.yaml +1 -0
|
@@ -1,217 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import { findParentNode } from '@curvenote/prosemirror-utils';
|
|
8
|
-
import {
|
|
9
|
-
getOutgoingTriple,
|
|
10
|
-
hasOutgoingNamedNodeTriple,
|
|
11
|
-
} from '@lblod/ember-rdfa-editor-lblod-plugins/utils/namespace';
|
|
12
|
-
import {
|
|
13
|
-
EXT,
|
|
14
|
-
MOBILITEIT,
|
|
15
|
-
RDF,
|
|
16
|
-
} from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
|
|
17
|
-
import {
|
|
18
|
-
ZONALITY_OPTIONS,
|
|
19
|
-
ZONALITY_OPTIONS_LEGACY,
|
|
20
|
-
} from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/roadsign-regulation-plugin/constants';
|
|
21
|
-
import { ProseParser } from '@lblod/ember-rdfa-editor';
|
|
22
|
-
import { wrapVariableInHighlight } from '../utils/codelist-utils';
|
|
23
|
-
import { getActiveEditableNode } from '@lblod/ember-rdfa-editor/plugins/editable-node';
|
|
24
|
-
import { isRdfaAttrs } from '@lblod/ember-rdfa-editor/core/rdfa-types';
|
|
25
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
26
|
-
import { getTranslationFunction } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/translation';
|
|
27
|
-
import { type CodeListOptions } from '../utils/fetch-data';
|
|
28
|
-
import { ContextualActionGroup } from '@lblod/ember-rdfa-editor/plugins/contextual-actions';
|
|
29
|
-
|
|
30
|
-
const plaatsbepalingGroupId =
|
|
31
|
-
'plaatsbepaling-1d8563d6-bfd8-487f-a2a0-6d7a6ab01cb5';
|
|
32
|
-
|
|
33
|
-
const placeDescriptionCodelistCache: {
|
|
34
|
-
placeDescriptions: CodeListOptions | null;
|
|
35
|
-
} = {
|
|
36
|
-
placeDescriptions: null,
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
function getSelectedLocation(state: EditorState) {
|
|
40
|
-
const { selection } = state;
|
|
41
|
-
if (
|
|
42
|
-
selection instanceof NodeSelection &&
|
|
43
|
-
selection.node.type === state.schema.nodes.location
|
|
44
|
-
) {
|
|
45
|
-
return {
|
|
46
|
-
node: selection.node,
|
|
47
|
-
pos: selection.from,
|
|
48
|
-
};
|
|
49
|
-
} else {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function getSource(state: EditorState, fallback?: string) {
|
|
55
|
-
const selectedLocation = getSelectedLocation(state);
|
|
56
|
-
if (selectedLocation) {
|
|
57
|
-
const { node } = selectedLocation;
|
|
58
|
-
const source = node.attrs.source;
|
|
59
|
-
if (source) {
|
|
60
|
-
return source;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return fallback;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function getIsZonal(state: EditorState) {
|
|
68
|
-
const { selection } = state;
|
|
69
|
-
const mobilityMeasureNode = findParentNode((node) =>
|
|
70
|
-
hasOutgoingNamedNodeTriple(
|
|
71
|
-
node.attrs,
|
|
72
|
-
RDF('type'),
|
|
73
|
-
MOBILITEIT('Mobiliteitsmaatregel'),
|
|
74
|
-
),
|
|
75
|
-
)(selection)?.node;
|
|
76
|
-
if (!mobilityMeasureNode) {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
const zonalityTriple = getOutgoingTriple(
|
|
80
|
-
mobilityMeasureNode.attrs,
|
|
81
|
-
EXT('zonality'),
|
|
82
|
-
);
|
|
83
|
-
if (!zonalityTriple) {
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
return (
|
|
87
|
-
zonalityTriple.object.value === ZONALITY_OPTIONS.ZONAL ||
|
|
88
|
-
zonalityTriple.object.value === ZONALITY_OPTIONS_LEGACY.ZONAL
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
type GetContextualActionsAttrs = {
|
|
93
|
-
zonalLocationCodelistUri: string;
|
|
94
|
-
nonZonalLocationCodelistUri: string;
|
|
95
|
-
endpoint?: string;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
function humanReadableLabel(label: string) {
|
|
99
|
-
return label.replace(/\$\{[^}]+\}/g, '…');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function createInsertPlaceDescriptionCommand(label: string) {
|
|
103
|
-
return (state: EditorState, dispatch?: (tr: Transaction) => void) => {
|
|
104
|
-
if (dispatch) {
|
|
105
|
-
let htmlToInsert = label;
|
|
106
|
-
htmlToInsert = wrapVariableInHighlight(htmlToInsert);
|
|
107
|
-
const domParser = new DOMParser();
|
|
108
|
-
const htmlNode = domParser.parseFromString(htmlToInsert, 'text/html');
|
|
109
|
-
const contentFragment = ProseParser.fromSchema(state.schema).parseSlice(
|
|
110
|
-
htmlNode,
|
|
111
|
-
{
|
|
112
|
-
preserveWhitespace: false,
|
|
113
|
-
},
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
const tr = state.tr;
|
|
117
|
-
const startPos = tr.selection.from;
|
|
118
|
-
tr.replaceSelection(contentFragment);
|
|
119
|
-
|
|
120
|
-
let highlightPos: number | null = null;
|
|
121
|
-
|
|
122
|
-
contentFragment.content.descendants((child, pos) => {
|
|
123
|
-
if (highlightPos !== null) {
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
if (child.type.name === state.schema.nodes.placeholder.name) {
|
|
127
|
-
highlightPos = startPos + pos;
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
return true;
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
if (highlightPos !== null) {
|
|
134
|
-
tr.setSelection(NodeSelection.create(tr.doc, highlightPos));
|
|
135
|
-
}
|
|
136
|
-
dispatch(tr);
|
|
137
|
-
}
|
|
138
|
-
return true;
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async function getPlaceDescriptionsCached(
|
|
143
|
-
state: EditorState,
|
|
144
|
-
{
|
|
145
|
-
zonalLocationCodelistUri,
|
|
146
|
-
nonZonalLocationCodelistUri,
|
|
147
|
-
endpoint,
|
|
148
|
-
}: GetContextualActionsAttrs,
|
|
149
|
-
) {
|
|
150
|
-
const source = getSource(state, endpoint);
|
|
151
|
-
const isZonal = getIsZonal(state);
|
|
152
|
-
let options;
|
|
153
|
-
if (!placeDescriptionCodelistCache.placeDescriptions) {
|
|
154
|
-
options = await fetchCodeListOptions(
|
|
155
|
-
source,
|
|
156
|
-
isZonal ? zonalLocationCodelistUri : nonZonalLocationCodelistUri,
|
|
157
|
-
);
|
|
158
|
-
placeDescriptionCodelistCache.placeDescriptions = options;
|
|
159
|
-
} else {
|
|
160
|
-
options = placeDescriptionCodelistCache.placeDescriptions;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return options;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export function getContextualActions(attrs: GetContextualActionsAttrs) {
|
|
167
|
-
return async function (state: EditorState, searchQuery?: string) {
|
|
168
|
-
const result = await getPlaceDescriptionsCached(state, attrs);
|
|
169
|
-
return result.options
|
|
170
|
-
.filter(
|
|
171
|
-
(option) =>
|
|
172
|
-
!searchQuery ||
|
|
173
|
-
option.label.toLowerCase().includes(searchQuery.toLowerCase()),
|
|
174
|
-
)
|
|
175
|
-
.map((option) => {
|
|
176
|
-
return {
|
|
177
|
-
id: uuidv4(),
|
|
178
|
-
label: humanReadableLabel(option.label),
|
|
179
|
-
group: plaatsbepalingGroupId,
|
|
180
|
-
command: createInsertPlaceDescriptionCommand(option.label),
|
|
181
|
-
};
|
|
182
|
-
});
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function contextualGroupIsVisible(state: EditorState) {
|
|
187
|
-
const activeNode = getActiveEditableNode(state);
|
|
188
|
-
const isPlaatsbepaling =
|
|
189
|
-
activeNode &&
|
|
190
|
-
isRdfaAttrs(activeNode.value.attrs) &&
|
|
191
|
-
activeNode.value.attrs.backlinks.some((value) =>
|
|
192
|
-
MOBILITEIT('plaatsbepaling').matches(value.predicate),
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
const isEmptySelection = state.selection.empty;
|
|
196
|
-
return (
|
|
197
|
-
(activeNode?.value.type.name === 'location' || isPlaatsbepaling) &&
|
|
198
|
-
isEmptySelection
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export function getContextualActionGroups(attrs: GetContextualActionsAttrs) {
|
|
203
|
-
return function (state: EditorState): ContextualActionGroup[] {
|
|
204
|
-
return contextualGroupIsVisible(state)
|
|
205
|
-
? [
|
|
206
|
-
{
|
|
207
|
-
id: plaatsbepalingGroupId,
|
|
208
|
-
label: getTranslationFunction(state)(
|
|
209
|
-
'variable.location.label',
|
|
210
|
-
'Plaatsbeschrijving',
|
|
211
|
-
),
|
|
212
|
-
getActions: getContextualActions(attrs),
|
|
213
|
-
},
|
|
214
|
-
]
|
|
215
|
-
: [];
|
|
216
|
-
};
|
|
217
|
-
}
|
|
1
|
+
export {
|
|
2
|
+
/** @deprecated these imports have been moved to contextual-actions/place-description.ts */
|
|
3
|
+
getContextualActions,
|
|
4
|
+
/** @deprecated these imports have been moved to contextual-actions/place-description.ts */
|
|
5
|
+
getPlaceDescriptionActionGroups as getContextualActionGroups,
|
|
6
|
+
} from './place-description';
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EditorState,
|
|
3
|
+
NodeSelection,
|
|
4
|
+
Transaction,
|
|
5
|
+
} from '@lblod/ember-rdfa-editor';
|
|
6
|
+
import { fetchCodeListOptions } from '../utils/fetch-data';
|
|
7
|
+
import { findParentNode } from '@curvenote/prosemirror-utils';
|
|
8
|
+
import {
|
|
9
|
+
getOutgoingTriple,
|
|
10
|
+
hasOutgoingNamedNodeTriple,
|
|
11
|
+
} from '@lblod/ember-rdfa-editor-lblod-plugins/utils/namespace';
|
|
12
|
+
import {
|
|
13
|
+
EXT,
|
|
14
|
+
MOBILITEIT,
|
|
15
|
+
RDF,
|
|
16
|
+
} from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
|
|
17
|
+
import {
|
|
18
|
+
ZONALITY_OPTIONS,
|
|
19
|
+
ZONALITY_OPTIONS_LEGACY,
|
|
20
|
+
} from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/roadsign-regulation-plugin/constants';
|
|
21
|
+
import { ProseParser } from '@lblod/ember-rdfa-editor';
|
|
22
|
+
import { wrapVariableInHighlight } from '../utils/codelist-utils';
|
|
23
|
+
import { getActiveEditableNode } from '@lblod/ember-rdfa-editor/plugins/editable-node';
|
|
24
|
+
import { isRdfaAttrs } from '@lblod/ember-rdfa-editor/core/rdfa-types';
|
|
25
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
26
|
+
import { getTranslationFunction } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/translation';
|
|
27
|
+
import { type CodeListOptions } from '../utils/fetch-data';
|
|
28
|
+
import { ContextualActionGroup } from '@lblod/ember-rdfa-editor/plugins/contextual-actions';
|
|
29
|
+
|
|
30
|
+
const plaatsbepalingGroupId =
|
|
31
|
+
'plaatsbepaling-1d8563d6-bfd8-487f-a2a0-6d7a6ab01cb5';
|
|
32
|
+
|
|
33
|
+
const placeDescriptionCodelistCache: {
|
|
34
|
+
zonal: CodeListOptions | null;
|
|
35
|
+
nonZonal: CodeListOptions | null;
|
|
36
|
+
} = {
|
|
37
|
+
zonal: null,
|
|
38
|
+
nonZonal: null,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function getSelectedLocation(state: EditorState) {
|
|
42
|
+
const { selection } = state;
|
|
43
|
+
if (
|
|
44
|
+
selection instanceof NodeSelection &&
|
|
45
|
+
selection.node.type === state.schema.nodes.location
|
|
46
|
+
) {
|
|
47
|
+
return {
|
|
48
|
+
node: selection.node,
|
|
49
|
+
pos: selection.from,
|
|
50
|
+
};
|
|
51
|
+
} else {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getSource(state: EditorState, fallback: string) {
|
|
57
|
+
const selectedLocation = getSelectedLocation(state);
|
|
58
|
+
if (selectedLocation) {
|
|
59
|
+
const { node } = selectedLocation;
|
|
60
|
+
const source = node.attrs.source as string | undefined;
|
|
61
|
+
if (source) {
|
|
62
|
+
return source;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return fallback;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getIsZonal(state: EditorState) {
|
|
70
|
+
const { selection } = state;
|
|
71
|
+
const mobilityMeasureNode = findParentNode((node) =>
|
|
72
|
+
hasOutgoingNamedNodeTriple(
|
|
73
|
+
node.attrs,
|
|
74
|
+
RDF('type'),
|
|
75
|
+
MOBILITEIT('Mobiliteitsmaatregel'),
|
|
76
|
+
),
|
|
77
|
+
)(selection)?.node;
|
|
78
|
+
if (!mobilityMeasureNode) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const zonalityTriple = getOutgoingTriple(
|
|
82
|
+
mobilityMeasureNode.attrs,
|
|
83
|
+
EXT('zonality'),
|
|
84
|
+
);
|
|
85
|
+
if (!zonalityTriple) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return (
|
|
89
|
+
zonalityTriple.object.value === ZONALITY_OPTIONS.ZONAL ||
|
|
90
|
+
zonalityTriple.object.value === ZONALITY_OPTIONS_LEGACY.ZONAL
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
type GetContextualActionsAttrs = {
|
|
95
|
+
zonalLocationCodelistUri: string;
|
|
96
|
+
nonZonalLocationCodelistUri: string;
|
|
97
|
+
endpoint: string;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
function humanReadableLabel(label: string) {
|
|
101
|
+
return label.replace(/\$\{[^}]+\}/g, '…');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function createInsertPlaceDescriptionCommand(label: string) {
|
|
105
|
+
return (state: EditorState, dispatch?: (tr: Transaction) => void) => {
|
|
106
|
+
if (dispatch) {
|
|
107
|
+
let htmlToInsert = label;
|
|
108
|
+
htmlToInsert = wrapVariableInHighlight(htmlToInsert);
|
|
109
|
+
const domParser = new DOMParser();
|
|
110
|
+
const htmlNode = domParser.parseFromString(htmlToInsert, 'text/html');
|
|
111
|
+
const contentFragment = ProseParser.fromSchema(state.schema).parseSlice(
|
|
112
|
+
htmlNode,
|
|
113
|
+
{
|
|
114
|
+
preserveWhitespace: false,
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const tr = state.tr;
|
|
119
|
+
const startPos = tr.selection.from;
|
|
120
|
+
tr.replaceSelection(contentFragment);
|
|
121
|
+
|
|
122
|
+
let highlightPos: number | null = null;
|
|
123
|
+
|
|
124
|
+
contentFragment.content.descendants((child, pos) => {
|
|
125
|
+
if (highlightPos !== null) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
if (child.type.name === state.schema.nodes.placeholder.name) {
|
|
129
|
+
highlightPos = startPos + pos;
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (highlightPos !== null) {
|
|
136
|
+
tr.setSelection(NodeSelection.create(tr.doc, highlightPos));
|
|
137
|
+
}
|
|
138
|
+
dispatch(tr);
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function getPlaceDescriptionsCached(
|
|
145
|
+
state: EditorState,
|
|
146
|
+
{
|
|
147
|
+
zonalLocationCodelistUri,
|
|
148
|
+
nonZonalLocationCodelistUri,
|
|
149
|
+
endpoint,
|
|
150
|
+
}: GetContextualActionsAttrs,
|
|
151
|
+
) {
|
|
152
|
+
const source = getSource(state, endpoint);
|
|
153
|
+
const isZonal = getIsZonal(state);
|
|
154
|
+
const zonality = isZonal ? 'zonal' : 'nonZonal';
|
|
155
|
+
let options;
|
|
156
|
+
if (!placeDescriptionCodelistCache[zonality]) {
|
|
157
|
+
options = await fetchCodeListOptions(
|
|
158
|
+
source,
|
|
159
|
+
isZonal ? zonalLocationCodelistUri : nonZonalLocationCodelistUri,
|
|
160
|
+
);
|
|
161
|
+
placeDescriptionCodelistCache[zonality] = options;
|
|
162
|
+
} else {
|
|
163
|
+
options = placeDescriptionCodelistCache[zonality];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return options;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function getContextualActions(attrs: GetContextualActionsAttrs) {
|
|
170
|
+
return async function (state: EditorState, searchQuery?: string) {
|
|
171
|
+
const result = await getPlaceDescriptionsCached(state, attrs);
|
|
172
|
+
return result.options
|
|
173
|
+
.filter(
|
|
174
|
+
(option) =>
|
|
175
|
+
!searchQuery ||
|
|
176
|
+
option.label.toLowerCase().includes(searchQuery.toLowerCase()),
|
|
177
|
+
)
|
|
178
|
+
.map((option) => {
|
|
179
|
+
return {
|
|
180
|
+
id: uuidv4(),
|
|
181
|
+
label: humanReadableLabel(option.label),
|
|
182
|
+
group: plaatsbepalingGroupId,
|
|
183
|
+
command: createInsertPlaceDescriptionCommand(option.label),
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function contextualGroupIsVisible(state: EditorState) {
|
|
190
|
+
const activeNode = getActiveEditableNode(state);
|
|
191
|
+
const isPlaatsbepaling =
|
|
192
|
+
activeNode &&
|
|
193
|
+
isRdfaAttrs(activeNode.value.attrs) &&
|
|
194
|
+
activeNode.value.attrs.backlinks.some((value) =>
|
|
195
|
+
MOBILITEIT('plaatsbepaling').matches(value.predicate),
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const isEmptySelection = state.selection.empty;
|
|
199
|
+
return (
|
|
200
|
+
(activeNode?.value.type.name === 'location' || isPlaatsbepaling) &&
|
|
201
|
+
isEmptySelection
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function getPlaceDescriptionActionGroups(
|
|
206
|
+
attrs: GetContextualActionsAttrs,
|
|
207
|
+
) {
|
|
208
|
+
return function (state: EditorState): ContextualActionGroup[] {
|
|
209
|
+
return contextualGroupIsVisible(state)
|
|
210
|
+
? [
|
|
211
|
+
{
|
|
212
|
+
id: plaatsbepalingGroupId,
|
|
213
|
+
label: getTranslationFunction(state)(
|
|
214
|
+
'variable.location.label',
|
|
215
|
+
'Plaatsbeschrijving',
|
|
216
|
+
),
|
|
217
|
+
getActions: getContextualActions(attrs),
|
|
218
|
+
},
|
|
219
|
+
]
|
|
220
|
+
: [];
|
|
221
|
+
};
|
|
222
|
+
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
// This file contains helpers for both the codelist and location variable types
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
EditorState,
|
|
5
|
+
PNode,
|
|
6
|
+
ProseParser,
|
|
7
|
+
SayController,
|
|
8
|
+
Transaction,
|
|
9
|
+
} from '@lblod/ember-rdfa-editor';
|
|
10
|
+
import { ResolvedPNode } from '@lblod/ember-rdfa-editor/utils/_private/types';
|
|
3
11
|
import { CodeListOption } from './fetch-data';
|
|
4
|
-
import { PNode, ProseParser, SayController } from '@lblod/ember-rdfa-editor';
|
|
5
12
|
import { createCodelistOptionNode } from '../actions/create-codelist-variable';
|
|
6
13
|
import { unwrap } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/option';
|
|
7
14
|
|
|
@@ -21,6 +28,40 @@ export function wrapVariableInHighlight(text: string) {
|
|
|
21
28
|
);
|
|
22
29
|
}
|
|
23
30
|
|
|
31
|
+
export function updateCodelistVariableCommand(
|
|
32
|
+
selectedCodelist: ResolvedPNode,
|
|
33
|
+
selectedOption: CodeListOption | CodeListOption[],
|
|
34
|
+
) {
|
|
35
|
+
return (state: EditorState, dispatch?: (tr: Transaction) => void) => {
|
|
36
|
+
const selectedOptions = Array.isArray(selectedOption)
|
|
37
|
+
? selectedOption
|
|
38
|
+
: [selectedOption];
|
|
39
|
+
const variableInstance = selectedCodelist.value.attrs[
|
|
40
|
+
'variableInstance'
|
|
41
|
+
] as string | undefined;
|
|
42
|
+
const pointerTarget = selectedCodelist.value.attrs['__rdfaId'] as
|
|
43
|
+
| string
|
|
44
|
+
| undefined;
|
|
45
|
+
const codelistOptionNodes = selectedOptions.map((option) =>
|
|
46
|
+
createCodelistOptionNode({
|
|
47
|
+
schema: state.schema,
|
|
48
|
+
text: option.label,
|
|
49
|
+
subject: option.uri,
|
|
50
|
+
variableInstance,
|
|
51
|
+
pointsToNode: pointerTarget,
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
const range = {
|
|
55
|
+
from: selectedCodelist.pos + 1,
|
|
56
|
+
to: selectedCodelist.pos + selectedCodelist.value.nodeSize - 1,
|
|
57
|
+
};
|
|
58
|
+
if (dispatch) {
|
|
59
|
+
dispatch(state.tr.replaceWith(range.from, range.to, codelistOptionNodes));
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
24
65
|
export function updateCodelistVariable(
|
|
25
66
|
selectedCodelist: {
|
|
26
67
|
node: PNode;
|
|
@@ -29,31 +70,11 @@ export function updateCodelistVariable(
|
|
|
29
70
|
selectedOption: CodeListOption | CodeListOption[],
|
|
30
71
|
controller: SayController,
|
|
31
72
|
) {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const variableInstance = selectedCodelist.node.attrs['variableInstance'] as
|
|
36
|
-
| string
|
|
37
|
-
| undefined;
|
|
38
|
-
const pointerTarget = selectedCodelist.node.attrs['__rdfaId'] as
|
|
39
|
-
| string
|
|
40
|
-
| undefined;
|
|
41
|
-
const codelistOptionNodes = selectedOptions.map((option) =>
|
|
42
|
-
createCodelistOptionNode({
|
|
43
|
-
schema: controller.schema,
|
|
44
|
-
text: option.label,
|
|
45
|
-
subject: option.uri,
|
|
46
|
-
variableInstance,
|
|
47
|
-
pointsToNode: pointerTarget,
|
|
48
|
-
}),
|
|
73
|
+
const command = updateCodelistVariableCommand(
|
|
74
|
+
{ value: selectedCodelist.node, pos: selectedCodelist.pos },
|
|
75
|
+
selectedOption,
|
|
49
76
|
);
|
|
50
|
-
|
|
51
|
-
from: selectedCodelist.pos + 1,
|
|
52
|
-
to: selectedCodelist.pos + selectedCodelist.node.nodeSize - 1,
|
|
53
|
-
};
|
|
54
|
-
controller.withTransaction((tr) => {
|
|
55
|
-
return tr.replaceWith(range.from, range.to, codelistOptionNodes);
|
|
56
|
-
});
|
|
77
|
+
controller.doCommand(command, { view: controller.mainEditorView });
|
|
57
78
|
}
|
|
58
79
|
|
|
59
80
|
/**
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import Service from '@ember/service';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import {
|
|
4
|
+
fetchSnippetListNames,
|
|
5
|
+
fetchSnippets,
|
|
6
|
+
Pagination,
|
|
7
|
+
SnippetFilter,
|
|
8
|
+
} from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/snippet-plugin/utils/fetch-data';
|
|
9
|
+
import {
|
|
10
|
+
Snippet,
|
|
11
|
+
SnippetPluginConfig,
|
|
12
|
+
} from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/snippet-plugin';
|
|
13
|
+
|
|
14
|
+
export default class SnippetFetchService extends Service {
|
|
15
|
+
@tracked listNameCache = new Map<string, string>();
|
|
16
|
+
@tracked snippetPageCache = new Map<
|
|
17
|
+
string,
|
|
18
|
+
{ totalCount: number; pages: Map<number, Snippet[]> }
|
|
19
|
+
>();
|
|
20
|
+
|
|
21
|
+
async getListNames(
|
|
22
|
+
config: SnippetPluginConfig,
|
|
23
|
+
listUris: string[],
|
|
24
|
+
): Promise<string[]> {
|
|
25
|
+
if (listUris.every((uri) => this.listNameCache.has(uri))) {
|
|
26
|
+
return listUris.map((uri) => this.listNameCache.get(uri) as string);
|
|
27
|
+
}
|
|
28
|
+
const abortController = new AbortController();
|
|
29
|
+
const listNames = await fetchSnippetListNames({
|
|
30
|
+
endpoint: config.endpoint,
|
|
31
|
+
abortSignal: abortController.signal,
|
|
32
|
+
snippetListUris: listUris,
|
|
33
|
+
});
|
|
34
|
+
listNames.forEach((name, uri) => this.listNameCache.set(uri, name));
|
|
35
|
+
|
|
36
|
+
return [...listNames.values()];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async getSnippets({
|
|
40
|
+
endpoint,
|
|
41
|
+
abortSignal,
|
|
42
|
+
filter,
|
|
43
|
+
pagination,
|
|
44
|
+
}: {
|
|
45
|
+
endpoint: string;
|
|
46
|
+
abortSignal: AbortSignal;
|
|
47
|
+
filter: SnippetFilter;
|
|
48
|
+
pagination: Pagination;
|
|
49
|
+
}) {
|
|
50
|
+
if (filter.name || !filter.snippetListUris?.length) {
|
|
51
|
+
// Filtering, so just search, to avoid mess of re-implementing local filtering to match that
|
|
52
|
+
// of the server. Undefined or empty uri lists are ignored by the fetch function, so pass
|
|
53
|
+
// those through too.
|
|
54
|
+
return fetchSnippets({ endpoint, abortSignal, filter, pagination });
|
|
55
|
+
}
|
|
56
|
+
const cacheKey = filter.snippetListUris.join('---');
|
|
57
|
+
let cachedPages = this.snippetPageCache.get(cacheKey);
|
|
58
|
+
if (!cachedPages) {
|
|
59
|
+
cachedPages = { totalCount: -1, pages: new Map() };
|
|
60
|
+
this.snippetPageCache.set(cacheKey, cachedPages);
|
|
61
|
+
}
|
|
62
|
+
const cachedPage = cachedPages.pages.get(pagination.pageNumber);
|
|
63
|
+
if (!cachedPage) {
|
|
64
|
+
const fetched = await fetchSnippets({
|
|
65
|
+
endpoint,
|
|
66
|
+
abortSignal,
|
|
67
|
+
filter,
|
|
68
|
+
pagination,
|
|
69
|
+
});
|
|
70
|
+
cachedPages.totalCount = fetched.totalCount;
|
|
71
|
+
cachedPages.pages.set(pagination.pageNumber, fetched.results);
|
|
72
|
+
return fetched;
|
|
73
|
+
}
|
|
74
|
+
return { totalCount: cachedPages.totalCount, results: cachedPage };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@lblod/ember-rdfa-editor-lblod-plugins/services/snippet-fetch-service';
|
|
@@ -5,12 +5,12 @@ import { type SnippetPluginConfig } from '@lblod/ember-rdfa-editor-lblod-plugins
|
|
|
5
5
|
interface Signature {
|
|
6
6
|
Args: Pick<EmberNodeArgs, 'node' | 'selectNode'> & {
|
|
7
7
|
insertSnippet: () => void;
|
|
8
|
+
snippetListNames: string[];
|
|
8
9
|
};
|
|
9
10
|
}
|
|
10
11
|
export default class SnippetPluginPlaceholder extends Component<Signature> {
|
|
11
12
|
intl: IntlService;
|
|
12
13
|
get node(): import("prosemirror-model").Node;
|
|
13
|
-
get listNames(): any;
|
|
14
14
|
get config(): SnippetPluginConfig;
|
|
15
15
|
get isSingleList(): boolean;
|
|
16
16
|
get alertTitle(): string;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import Component from '@glimmer/component';
|
|
2
2
|
import { type EmberNodeArgs } from '@lblod/ember-rdfa-editor/utils/_private/ember-node';
|
|
3
3
|
import { type PNode, type Selection, Slice } from '@lblod/ember-rdfa-editor';
|
|
4
|
-
import { Snippet } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/snippet-plugin';
|
|
4
|
+
import { Snippet, SnippetPluginConfig } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/snippet-plugin';
|
|
5
|
+
import SnippetFetchService from '@lblod/ember-rdfa-editor-lblod-plugins/services/snippet-fetch-service';
|
|
5
6
|
interface Signature {
|
|
6
7
|
Args: EmberNodeArgs;
|
|
7
8
|
Blocks: {
|
|
@@ -9,6 +10,7 @@ interface Signature {
|
|
|
9
10
|
};
|
|
10
11
|
}
|
|
11
12
|
export default class SnippetNode extends Component<Signature> {
|
|
13
|
+
snippetFetchService: SnippetFetchService;
|
|
12
14
|
showModal: boolean;
|
|
13
15
|
mode: string;
|
|
14
16
|
get controller(): import("@lblod/ember-rdfa-editor").SayController;
|
|
@@ -17,6 +19,8 @@ export default class SnippetNode extends Component<Signature> {
|
|
|
17
19
|
get node(): PNode;
|
|
18
20
|
get isPlaceholder(): boolean;
|
|
19
21
|
get allowMultipleSnippets(): boolean;
|
|
22
|
+
get snippetListUris(): string[];
|
|
23
|
+
get config(): SnippetPluginConfig;
|
|
20
24
|
closeModal(): void;
|
|
21
25
|
openModal(): void;
|
|
22
26
|
addFragment(): void;
|
|
@@ -27,5 +31,7 @@ export default class SnippetNode extends Component<Signature> {
|
|
|
27
31
|
get isActive(): boolean;
|
|
28
32
|
onInsert(snippet: Snippet): void;
|
|
29
33
|
get class(): string;
|
|
34
|
+
loadedListNames: import("reactiveweb/function").State<Promise<string[]>>;
|
|
35
|
+
get snippetListNames(): string[];
|
|
30
36
|
}
|
|
31
37
|
export {};
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import Component from '@glimmer/component';
|
|
2
|
+
import { Option } from '@lblod/ember-rdfa-editor/utils/option';
|
|
2
3
|
import { Snippet, SnippetPluginConfig } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/snippet-plugin';
|
|
4
|
+
import SnippetFetchService from '@lblod/ember-rdfa-editor-lblod-plugins/services/snippet-fetch-service';
|
|
3
5
|
interface Args {
|
|
4
6
|
config: SnippetPluginConfig;
|
|
5
|
-
snippetListUris: string[]
|
|
6
|
-
snippetListNames: string[]
|
|
7
|
+
snippetListUris: Option<string[]>;
|
|
8
|
+
snippetListNames: Option<string[]>;
|
|
7
9
|
closeModal: () => void;
|
|
8
10
|
open: boolean;
|
|
9
11
|
onInsert: (snippet: Snippet) => void;
|
|
10
12
|
}
|
|
11
13
|
export default class SnippetPluginSearchModalComponent extends Component<Args> {
|
|
14
|
+
snippetFetchService: SnippetFetchService;
|
|
12
15
|
inputSearchText: string | null;
|
|
13
16
|
error: unknown;
|
|
14
17
|
pageNumber: number;
|
|
@@ -9,4 +9,4 @@ export interface LocationPluginConfig {
|
|
|
9
9
|
additionalRDFTypes?: NamedNode[];
|
|
10
10
|
}
|
|
11
11
|
export declare const osloLocation: (config: LocationPluginConfig) => import("@lblod/ember-rdfa-editor/core/say-node-spec").default;
|
|
12
|
-
export declare const osloLocationView: (config: LocationPluginConfig) => (controller: import("@lblod/ember-rdfa-editor").SayController) => import("@lblod/ember-rdfa-editor
|
|
12
|
+
export declare const osloLocationView: (config: LocationPluginConfig) => (controller: import("@lblod/ember-rdfa-editor").SayController) => import("@lblod/ember-rdfa-editor").NodeViewConstructor;
|
package/declarations/addon/plugins/locked-placeholder-plugin/nodes/block-locked-placeholder.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const blockLockedPlaceholder: import("@lblod/ember-rdfa-editor/core/say-node-spec").default;
|
|
2
|
-
export declare const blockLockedPlaceholderView: (controller: import("@lblod/ember-rdfa-editor").SayController) => import("@lblod/ember-rdfa-editor
|
|
2
|
+
export declare const blockLockedPlaceholderView: (controller: import("@lblod/ember-rdfa-editor").SayController) => import("@lblod/ember-rdfa-editor").NodeViewConstructor;
|