@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.
Files changed (46) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/addon/components/snippet-plugin/nodes/placeholder.gts +4 -6
  3. package/addon/components/snippet-plugin/nodes/snippet.gts +34 -8
  4. package/addon/components/snippet-plugin/search-modal.gts +12 -6
  5. package/addon/components/snippet-plugin/snippet-insert-rdfa.gts +0 -2
  6. package/addon/plugins/snippet-plugin/commands/update-snippet-placeholder.ts +2 -11
  7. package/addon/plugins/snippet-plugin/index.ts +2 -1
  8. package/addon/plugins/snippet-plugin/nodes/snippet-placeholder.ts +0 -12
  9. package/addon/plugins/snippet-plugin/nodes/snippet.ts +1 -11
  10. package/addon/plugins/snippet-plugin/utils/fetch-data.ts +55 -16
  11. package/addon/plugins/variable-plugin/contextual-actions/codelist.ts +132 -0
  12. package/addon/plugins/variable-plugin/contextual-actions/index.ts +6 -217
  13. package/addon/plugins/variable-plugin/contextual-actions/place-description.ts +222 -0
  14. package/addon/plugins/variable-plugin/utils/codelist-utils.ts +46 -25
  15. package/addon/services/snippet-fetch-service.ts +76 -0
  16. package/app/services/snippet-fetch-service.js +1 -0
  17. package/declarations/addon/components/snippet-plugin/nodes/placeholder.d.ts +1 -1
  18. package/declarations/addon/components/snippet-plugin/nodes/snippet.d.ts +7 -1
  19. package/declarations/addon/components/snippet-plugin/search-modal.d.ts +5 -2
  20. package/declarations/addon/plugins/location-plugin/node.d.ts +1 -1
  21. package/declarations/addon/plugins/locked-placeholder-plugin/nodes/block-locked-placeholder.d.ts +1 -1
  22. package/declarations/addon/plugins/locked-placeholder-plugin/nodes/inline-locked-placeholder.d.ts +1 -1
  23. package/declarations/addon/plugins/mandatee-table-plugin/node.d.ts +1 -1
  24. package/declarations/addon/plugins/snippet-plugin/index.d.ts +2 -1
  25. package/declarations/addon/plugins/snippet-plugin/nodes/snippet-placeholder.d.ts +1 -1
  26. package/declarations/addon/plugins/snippet-plugin/nodes/snippet.d.ts +2 -2
  27. package/declarations/addon/plugins/snippet-plugin/utils/fetch-data.d.ts +9 -5
  28. package/declarations/addon/plugins/structure-plugin/node.d.ts +2 -2
  29. package/declarations/addon/plugins/table-of-contents-plugin/nodes/table-of-contents.d.ts +1 -1
  30. package/declarations/addon/plugins/template-comments-plugin/node.d.ts +1 -1
  31. package/declarations/addon/plugins/variable-plugin/contextual-actions/codelist.d.ts +14 -0
  32. package/declarations/addon/plugins/variable-plugin/contextual-actions/index.d.ts +5 -15
  33. package/declarations/addon/plugins/variable-plugin/contextual-actions/place-description.d.ts +15 -0
  34. package/declarations/addon/plugins/variable-plugin/utils/codelist-utils.d.ts +3 -1
  35. package/declarations/addon/plugins/variable-plugin/variables/address.d.ts +1 -1
  36. package/declarations/addon/plugins/variable-plugin/variables/autofilled.d.ts +1 -1
  37. package/declarations/addon/plugins/variable-plugin/variables/codelist.d.ts +1 -1
  38. package/declarations/addon/plugins/variable-plugin/variables/date.d.ts +1 -1
  39. package/declarations/addon/plugins/variable-plugin/variables/legacy-codelist.d.ts +1 -1
  40. package/declarations/addon/plugins/variable-plugin/variables/location.d.ts +1 -1
  41. package/declarations/addon/plugins/variable-plugin/variables/number.d.ts +1 -1
  42. package/declarations/addon/plugins/variable-plugin/variables/person.d.ts +1 -1
  43. package/declarations/addon/plugins/variable-plugin/variables/text.d.ts +1 -1
  44. package/declarations/addon/services/snippet-fetch-service.d.ts +20 -0
  45. package/package.json +3 -3
  46. package/pnpm-workspace.yaml +1 -0
@@ -1,217 +1,6 @@
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
- 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 selectedOptions = Array.isArray(selectedOption)
33
- ? selectedOption
34
- : [selectedOption];
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
- const range = {
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[] | undefined;
6
- snippetListNames: string[] | undefined;
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/utils/ember-node").SayNodeViewConstructor;
12
+ export declare const osloLocationView: (config: LocationPluginConfig) => (controller: import("@lblod/ember-rdfa-editor").SayController) => import("@lblod/ember-rdfa-editor").NodeViewConstructor;
@@ -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/utils/ember-node").SayNodeViewConstructor;
2
+ export declare const blockLockedPlaceholderView: (controller: import("@lblod/ember-rdfa-editor").SayController) => import("@lblod/ember-rdfa-editor").NodeViewConstructor;