@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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @lblod/ember-rdfa-editor-lblod-plugins
2
2
 
3
+ ## 38.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#674](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/674) [`9ded434`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/9ded4345c52e1bc6f22bb3fbae8dfd96c5d6e6be) Thanks [@piemonkey](https://github.com/piemonkey)! - Cache snippets for insertion when not doing a text search
8
+
9
+ - [#674](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/674) [`13576c5`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/13576c5bd749b40bdc33d26c0b31c3a0a11b96b9) Thanks [@piemonkey](https://github.com/piemonkey)! - Query for snippet list names instead of storing names when inserting
10
+
11
+ - [#674](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/674) [`b107980`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/b10798018e487503613fa3ba4473c91cde20dbef) Thanks [@piemonkey](https://github.com/piemonkey)! - No longer store snippet list names on snippet nodes. Passing this when creating snippets or placeholders is now deprecated.
12
+
13
+ - [#670](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/670) [`530dafc`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/530dafcf0723bf7f06715633563ae73d19ec9f68) Thanks [@piemonkey](https://github.com/piemonkey)! - Make endpoint arg for location variable config required in the typescript type, which matches the documentation, and the reality of handling locations without a source attribute
14
+
15
+ - [#674](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/674) [`f5e1c0d`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/f5e1c0dd13b2d3af6ca2f47ad8789247c2e94af4) Thanks [@piemonkey](https://github.com/piemonkey)! - Cache snippet list names to avoid excess queries
16
+
17
+ - [#673](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/673) [`8913ca5`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/8913ca57dfa9e3ccfabdea405dc7cd38acfb1459) Thanks [@piemonkey](https://github.com/piemonkey)! - Add contextual actions for filling in codelist values. This introduces new contextual actions for
18
+ the variable-plugin, so the import for place description actions has moved to
19
+ `@lblod/ember-rdfa-editor-lblod-plugins/plugins/variable-plugin/contextual-actions/place-description`.
20
+ The old import location remains for backwards compatibility.
21
+
22
+ ### Patch Changes
23
+
24
+ - [#673](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/673) [`e2b9436`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/e2b943657850d63673457233a7aa34676fe42ad3) Thanks [@piemonkey](https://github.com/piemonkey)! - Fix bug in location description context actions where one cache was used for both zonal and non-zonal options
25
+
26
+ - [#673](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/673) [`8a844bd`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/8a844bd88a2bc1786034136bb02c3d7891ef57ac) Thanks [@piemonkey](https://github.com/piemonkey)! - Keep context menu open after selecting codelist options
27
+
28
+ - [#673](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/673) [`593e95e`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/593e95e4ae870719bc48cb14d3759d7f0f10041e) Thanks [@piemonkey](https://github.com/piemonkey)! - bump editor to [@lblod](https://github.com/lblod/ember-rdfa-editor/releases/tag/@lblod/ember-rdfa-editor@13.16.0)
29
+
3
30
  ## 38.0.3
4
31
 
5
32
  ### Patch Changes
@@ -13,6 +13,7 @@ import getClassnamesFromNode from '@lblod/ember-rdfa-editor/utils/get-classnames
13
13
  interface Signature {
14
14
  Args: Pick<EmberNodeArgs, 'node' | 'selectNode'> & {
15
15
  insertSnippet: () => void;
16
+ snippetListNames: string[];
16
17
  };
17
18
  }
18
19
 
@@ -22,19 +23,16 @@ export default class SnippetPluginPlaceholder extends Component<Signature> {
22
23
  get node() {
23
24
  return this.args.node;
24
25
  }
25
- get listNames() {
26
- return this.args.node.attrs.snippetListNames;
27
- }
28
26
  get config(): SnippetPluginConfig {
29
27
  return this.node.attrs.config;
30
28
  }
31
29
  get isSingleList() {
32
- return this.listNames.length === 1;
30
+ return this.args.snippetListNames.length === 1;
33
31
  }
34
32
  get alertTitle() {
35
33
  if (this.isSingleList) {
36
34
  return this.intl.t('snippet-plugin.placeholder.title-single', {
37
- listName: this.listNames[0],
35
+ listName: this.args.snippetListNames[0],
38
36
  });
39
37
  } else {
40
38
  return this.intl.t('snippet-plugin.placeholder.title-multiple');
@@ -58,7 +56,7 @@ export default class SnippetPluginPlaceholder extends Component<Signature> {
58
56
  <p class='say-snippet-placeholder__title'>{{this.alertTitle}}</p>
59
57
  {{#unless this.isSingleList}}
60
58
  <ul class='say-snippet-placeholder__list'>
61
- {{#each this.listNames as |listName|}}
59
+ {{#each @snippetListNames as |listName|}}
62
60
  <li>{{listName}}</li>
63
61
  {{/each}}
64
62
  </ul>
@@ -4,7 +4,9 @@ import SearchModal from '../search-modal';
4
4
  import { tracked } from '@glimmer/tracking';
5
5
  import { action } from '@ember/object';
6
6
  import { TemplateOnlyComponent } from '@ember/component/template-only';
7
+ import { service } from '@ember/service';
7
8
  import t from 'ember-intl/helpers/t';
9
+ import { trackedFunction } from 'reactiveweb/function';
8
10
  import AuIcon, {
9
11
  type AuIconSignature,
10
12
  } from '@appuniversum/ember-appuniversum/components/au-icon';
@@ -33,8 +35,12 @@ import { createSnippetPlaceholder } from '@lblod/ember-rdfa-editor-lblod-plugins
33
35
  import { hasDecendant } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/has-descendant';
34
36
  import SnippetPlaceholder from '@lblod/ember-rdfa-editor-lblod-plugins/components/snippet-plugin/nodes/placeholder';
35
37
  import { getSnippetListUrisFromNode } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/snippet-plugin/utils/rdfa-predicate';
36
- import { Snippet } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/snippet-plugin';
38
+ import {
39
+ Snippet,
40
+ SnippetPluginConfig,
41
+ } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/snippet-plugin';
37
42
  import getClassnamesFromNode from '@lblod/ember-rdfa-editor/utils/get-classnames-from-node';
43
+ import SnippetFetchService from '@lblod/ember-rdfa-editor-lblod-plugins/services/snippet-fetch-service';
38
44
 
39
45
  interface ButtonSig {
40
46
  Args: {
@@ -64,8 +70,11 @@ interface Signature {
64
70
  }
65
71
 
66
72
  export default class SnippetNode extends Component<Signature> {
73
+ @service declare snippetFetchService: SnippetFetchService;
74
+
67
75
  @tracked showModal: boolean = false;
68
76
  @tracked mode: string = '';
77
+
69
78
  get controller() {
70
79
  return this.args.controller;
71
80
  }
@@ -87,6 +96,12 @@ export default class SnippetNode extends Component<Signature> {
87
96
  get allowMultipleSnippets() {
88
97
  return this.node.attrs.allowMultipleSnippets as boolean;
89
98
  }
99
+ get snippetListUris() {
100
+ return getSnippetListUrisFromNode(this.node);
101
+ }
102
+ get config(): SnippetPluginConfig {
103
+ return this.node.attrs.config;
104
+ }
90
105
 
91
106
  @action
92
107
  closeModal() {
@@ -131,8 +146,8 @@ export default class SnippetNode extends Component<Signature> {
131
146
  const node = createSnippetPlaceholder({
132
147
  listProperties: {
133
148
  placeholderId: this.node.attrs.placeholderId,
134
- listUris: getSnippetListUrisFromNode(this.node),
135
- names: this.node.attrs.snippetListNames,
149
+ listUris: this.snippetListUris,
150
+ names: this.snippetListNames,
136
151
  importedResources: this.node.attrs.importedResources,
137
152
  },
138
153
  schema: this.schema,
@@ -202,8 +217,8 @@ export default class SnippetNode extends Component<Signature> {
202
217
  title: snippet.title ?? '',
203
218
  listProperties: {
204
219
  placeholderId: this.node.attrs.placeholderId,
205
- listUris: getSnippetListUrisFromNode(this.node),
206
- names: this.node.attrs.snippetListNames,
220
+ listUris: this.snippetListUris,
221
+ names: this.snippetListNames,
207
222
  importedResources: this.node.attrs.importedResources,
208
223
  },
209
224
  range: { start, end },
@@ -216,12 +231,23 @@ export default class SnippetNode extends Component<Signature> {
216
231
  return getClassnamesFromNode(this.args.node);
217
232
  }
218
233
 
234
+ loadedListNames = trackedFunction(this, async () => {
235
+ return this.snippetFetchService.getListNames(
236
+ this.config,
237
+ this.snippetListUris,
238
+ );
239
+ });
240
+ get snippetListNames() {
241
+ return this.loadedListNames.value ?? [];
242
+ }
243
+
219
244
  <template>
220
245
  {{#if this.isPlaceholder}}
221
246
  <SnippetPlaceholder
222
247
  @node={{@node}}
223
248
  @selectNode={{@selectNode}}
224
249
  @insertSnippet={{this.editFragment}}
250
+ @snippetListNames={{this.snippetListNames}}
225
251
  />
226
252
  {{else}}
227
253
  <div class='{{this.class}} say-snippet-card'>
@@ -268,10 +294,10 @@ export default class SnippetNode extends Component<Signature> {
268
294
  <SearchModal
269
295
  @open={{this.showModal}}
270
296
  @closeModal={{this.closeModal}}
271
- @config={{this.node.attrs.config}}
297
+ @config={{this.config}}
272
298
  @onInsert={{this.onInsert}}
273
- @snippetListUris={{getSnippetListUrisFromNode this.node}}
274
- @snippetListNames={{this.node.attrs.snippetListNames}}
299
+ @snippetListUris={{this.snippetListUris}}
300
+ @snippetListNames={{this.snippetListNames}}
275
301
  />
276
302
  </template>
277
303
  }
@@ -1,7 +1,7 @@
1
- import pagination from '@lblod/ember-rdfa-editor-lblod-plugins/helpers/pagination';
2
1
  import Component from '@glimmer/component';
3
2
  import { assert } from '@ember/debug';
4
3
  import { action } from '@ember/object';
4
+ import { service } from '@ember/service';
5
5
  import { restartableTask, timeout } from 'ember-concurrency';
6
6
  import { task as trackedTask } from 'reactiveweb/ember-concurrency';
7
7
  import { tracked } from '@glimmer/tracking';
@@ -13,10 +13,11 @@ import AuMainContainer from '@appuniversum/ember-appuniversum/components/au-main
13
13
  import AuHeading from '@appuniversum/ember-appuniversum/components/au-heading';
14
14
  import AuLabel from '@appuniversum/ember-appuniversum/components/au-label';
15
15
 
16
+ import { Option } from '@lblod/ember-rdfa-editor/utils/option';
17
+ import pagination from '@lblod/ember-rdfa-editor-lblod-plugins/helpers/pagination';
16
18
  import AuNativeInput from '@lblod/ember-rdfa-editor-lblod-plugins/components/au-native-input';
17
19
  import Loading from '@lblod/ember-rdfa-editor-lblod-plugins/components/common/search/loading';
18
20
  import AlertLoadError from '@lblod/ember-rdfa-editor-lblod-plugins/components/common/search/alert-load-error';
19
- import { fetchSnippets } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/snippet-plugin/utils/fetch-data';
20
21
  import {
21
22
  Snippet,
22
23
  SnippetPluginConfig,
@@ -24,17 +25,20 @@ import {
24
25
  import PreviewList from '@lblod/ember-rdfa-editor-lblod-plugins/components/common/documents/preview-list';
25
26
  import AlertNoItems from '@lblod/ember-rdfa-editor-lblod-plugins/components/common/search/alert-no-items';
26
27
  import PaginationView from '@lblod/ember-rdfa-editor-lblod-plugins/components/pagination/pagination-view';
28
+ import SnippetFetchService from '@lblod/ember-rdfa-editor-lblod-plugins/services/snippet-fetch-service';
27
29
 
28
30
  interface Args {
29
31
  config: SnippetPluginConfig;
30
- snippetListUris: string[] | undefined;
31
- snippetListNames: string[] | undefined;
32
+ snippetListUris: Option<string[]>;
33
+ snippetListNames: Option<string[]>;
32
34
  closeModal: () => void;
33
35
  open: boolean;
34
36
  onInsert: (snippet: Snippet) => void;
35
37
  }
36
38
 
37
39
  export default class SnippetPluginSearchModalComponent extends Component<Args> {
40
+ @service declare snippetFetchService: SnippetFetchService;
41
+
38
42
  // Filtering
39
43
  @tracked inputSearchText: string | null = null;
40
44
 
@@ -75,12 +79,14 @@ export default class SnippetPluginSearchModalComponent extends Component<Args> {
75
79
  }
76
80
 
77
81
  snippetsSearch = restartableTask(async () => {
78
- await timeout(500);
82
+ if (this.searchText) {
83
+ await timeout(500);
84
+ }
79
85
 
80
86
  const abortController = new AbortController();
81
87
 
82
88
  try {
83
- const queryResult = await fetchSnippets({
89
+ const queryResult = await this.snippetFetchService.getSnippets({
84
90
  endpoint: this.args.config.endpoint,
85
91
  abortSignal: abortController.signal,
86
92
  filter: {
@@ -28,7 +28,6 @@ export default class SnippetInsertRdfaComponent extends Component<Sig> {
28
28
  return {
29
29
  listUris,
30
30
  placeholderId: activeNode.attrs.placeholderId,
31
- names: activeNode.attrs.snippetListNames,
32
31
  importedResources: activeNode.attrs.importedResources,
33
32
  };
34
33
  }
@@ -50,7 +49,6 @@ export default class SnippetInsertRdfaComponent extends Component<Sig> {
50
49
  return {
51
50
  listUris,
52
51
  placeholderId: parentNode.node.attrs.placeholderId,
53
- names: parentNode.node.attrs.snippetListNames,
54
52
  importedResources: parentNode.node.attrs.importedResources,
55
53
  };
56
54
  }
@@ -1,7 +1,6 @@
1
1
  import { Command } from '@lblod/ember-rdfa-editor';
2
2
  import { addProperty, removeProperty } from '@lblod/ember-rdfa-editor/commands';
3
- import { sayDataFactory } from '@lblod/ember-rdfa-editor/core/say-data-factory';
4
- import { SNIPPET_LIST_RDFA_PREDICATE } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/snippet-plugin/utils/rdfa-predicate';
3
+ import { tripleForSnippetListUri } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/snippet-plugin/utils/rdfa-predicate';
5
4
  import { type OutgoingTriple } from '@lblod/ember-rdfa-editor/core/rdfa-processor';
6
5
  import { type ResolvedPNode } from '@lblod/ember-rdfa-editor/utils/_private/types';
7
6
  import {
@@ -47,20 +46,12 @@ export const updateSnippetPlaceholder = ({
47
46
 
48
47
  addProperty({
49
48
  resource,
50
- property: {
51
- predicate: SNIPPET_LIST_RDFA_PREDICATE.prefixed,
52
- object: sayDataFactory.namedNode(list.uri),
53
- },
49
+ property: tripleForSnippetListUri(list.uri),
54
50
  transaction,
55
51
  })(newState, (newTransaction) => {
56
52
  transaction = newTransaction;
57
53
  });
58
54
  });
59
- transaction = transaction.setNodeAttribute(
60
- node.pos,
61
- 'snippetListNames',
62
- newSnippetLists.map((list) => list.label),
63
- );
64
55
  transaction = transaction.setNodeAttribute(
65
56
  node.pos,
66
57
  'importedResources',
@@ -33,7 +33,8 @@ export type ImportedResourceMap = Record<string, Option<string>>;
33
33
  export type SnippetListProperties = {
34
34
  placeholderId: string;
35
35
  listUris: string[];
36
- names: string[];
36
+ /** @deprecated Passing snippet list names is no longer necessary */
37
+ names?: string[];
37
38
  importedResources: ImportedResourceMap;
38
39
  };
39
40
 
@@ -81,7 +81,6 @@ export function createSnippetPlaceholder({
81
81
  return schema.nodes.snippet_placeholder.create({
82
82
  rdfaNodeType: 'resource',
83
83
  placeholderId: listProps.placeholderId,
84
- snippetListNames: listProps.names,
85
84
  subject: mappingResource,
86
85
  properties: [
87
86
  {
@@ -106,7 +105,6 @@ const emberNodeConfig = (config: SnippetPluginConfig): EmberNodeConfig => ({
106
105
  ...rdfaAttrSpec({ rdfaAware: true }),
107
106
  typeof: { default: EXT('SnippetPlaceholder') },
108
107
  placeholderId: { default: '' },
109
- snippetListNames: { default: [] },
110
108
  importedResources: { default: {} },
111
109
  allowMultipleSnippets: { default: false },
112
110
  config: {
@@ -117,13 +115,11 @@ const emberNodeConfig = (config: SnippetPluginConfig): EmberNodeConfig => ({
117
115
  classNames: ['say-snippet'],
118
116
  serialize(node, editorState) {
119
117
  const t = getTranslationFunction(editorState);
120
- const listNames = node.attrs.snippetListNames as string[];
121
118
  return renderRdfaAware({
122
119
  renderable: node,
123
120
  tag: 'div',
124
121
  attrs: {
125
122
  class: `${getClassnamesFromNode(node)} say-snippet-placeholder`,
126
- 'data-list-names': listNames && JSON.stringify(listNames),
127
123
  'data-imported-resources': JSON.stringify(node.attrs.importedResources),
128
124
  'data-allow-multiple-snippets': node.attrs.allowMultipleSnippets,
129
125
  },
@@ -149,20 +145,12 @@ const emberNodeConfig = (config: SnippetPluginConfig): EmberNodeConfig => ({
149
145
  EXT('SnippetPlaceholder'),
150
146
  )
151
147
  ) {
152
- let snippetListNames = jsonParse(
153
- node.getAttribute('data-list-names'),
154
- );
155
- if (!snippetListNames) {
156
- // We might have an older version which is comma separated
157
- snippetListNames = node.getAttribute('data-list-names')?.split(',');
158
- }
159
148
  return {
160
149
  ...rdfaAttrs,
161
150
  // Generate a placeholderId any time we deserialise, this way we don't need to handle
162
151
  // generating new ids whenever we re-use parts of a document (e.g. copy-paste or
163
152
  // placeholders inside snippets)
164
153
  placeholderId: uuidv4(),
165
- snippetListNames,
166
154
  importedResources: jsonParse(
167
155
  node.getAttribute('data-imported-resources'),
168
156
  ),
@@ -84,12 +84,7 @@ export function createSnippet({
84
84
  content,
85
85
  title,
86
86
  allowMultipleSnippets,
87
- listProperties: {
88
- listUris,
89
- names: snippetListNames,
90
- importedResources,
91
- placeholderId,
92
- },
87
+ listProperties: { listUris, importedResources, placeholderId },
93
88
  }: CreateSnippetArgs): [PNode, Map<string, OutgoingTriple[]>] {
94
89
  // Replace instances of linked to uris with the resources that exist in the outer document.
95
90
  let replacedContent = content;
@@ -116,7 +111,6 @@ export function createSnippet({
116
111
  {
117
112
  placeholderId,
118
113
  properties,
119
- snippetListNames,
120
114
  title,
121
115
  subject: `http://data.lblod.info/snippets/${uuidv4()}`,
122
116
  importedResources,
@@ -169,7 +163,6 @@ const emberNodeConfig = (options: SnippetPluginConfig): EmberNodeConfig => ({
169
163
  },
170
164
  rdfaNodeType: { default: 'resource' },
171
165
  placeholderId: { default: '' },
172
- snippetListNames: { default: [] },
173
166
  importedResources: { default: {} },
174
167
  title: { default: '' },
175
168
  config: { default: options },
@@ -180,13 +173,11 @@ const emberNodeConfig = (options: SnippetPluginConfig): EmberNodeConfig => ({
180
173
  content: options.allowedContent || DEFAULT_CONTENT_STRING,
181
174
  classNames: ['say-snippet'],
182
175
  serialize(node) {
183
- const listNames = node.attrs.snippetListNames as string[];
184
176
  return renderRdfaAware({
185
177
  renderable: node,
186
178
  tag: 'div',
187
179
  attrs: {
188
180
  'data-snippet-placeholder-id': node.attrs.placeholderId,
189
- 'data-list-names': listNames && JSON.stringify(listNames),
190
181
  'data-imported-resources': JSON.stringify(node.attrs.importedResources),
191
182
  'data-snippet-title': node.attrs.title,
192
183
  'data-allow-multiple-snippets': node.attrs.allowMultipleSnippets,
@@ -225,7 +216,6 @@ const emberNodeConfig = (options: SnippetPluginConfig): EmberNodeConfig => ({
225
216
  ...rdfaAttrs,
226
217
  properties,
227
218
  placeholderId,
228
- snippetListNames: jsonParse(node.getAttribute('data-list-names')),
229
219
  importedResources: jsonParse(
230
220
  node.getAttribute('data-imported-resources'),
231
221
  ),
@@ -7,7 +7,7 @@ import {
7
7
  } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/sparql-helpers';
8
8
  import { Snippet, SnippetList, SnippetListArgs } from '../index';
9
9
 
10
- type Filter = { name?: string; snippetListUris?: string[] };
10
+ export type SnippetFilter = { name?: string; snippetListUris?: string[] };
11
11
  export type OrderBy =
12
12
  | 'label'
13
13
  | 'created-on'
@@ -15,9 +15,9 @@ export type OrderBy =
15
15
  | '-created-on'
16
16
  | ''
17
17
  | null;
18
- type Pagination = { pageNumber: number; pageSize: number };
18
+ export type Pagination = { pageNumber: number; pageSize: number };
19
19
 
20
- const buildSnippetCountQuery = ({ name, snippetListUris }: Filter) => {
20
+ const buildSnippetCountQuery = ({ name, snippetListUris }: SnippetFilter) => {
21
21
  return /* sparql */ `
22
22
  PREFIX schema: <http://schema.org/>
23
23
  PREFIX dct: <http://purl.org/dc/terms/>
@@ -52,7 +52,7 @@ const buildSnippetCountQuery = ({ name, snippetListUris }: Filter) => {
52
52
  `;
53
53
  };
54
54
 
55
- // const buildSnippetListCountQuery = ({ name }: Filter) => {
55
+ // const buildSnippetListCountQuery = ({ name }: SnippetFilter) => {
56
56
  // return `
57
57
  // PREFIX ext: <http://mu.semte.ch/vocabularies/ext/>
58
58
  // SELECT (COUNT(?snippetLists) AS ?count)
@@ -72,7 +72,7 @@ const buildSnippetFetchQuery = ({
72
72
  filter: { name, snippetListUris },
73
73
  pagination: { pageSize, pageNumber },
74
74
  }: {
75
- filter: Filter;
75
+ filter: SnippetFilter;
76
76
  pagination: Pagination;
77
77
  }) => {
78
78
  return /* sparql */ `
@@ -138,7 +138,7 @@ const buildSnippetListFetchQuery = ({
138
138
  filter: { name },
139
139
  orderBy,
140
140
  }: {
141
- filter: Filter;
141
+ filter: SnippetFilter;
142
142
  orderBy: OrderBy;
143
143
  }) => {
144
144
  return /* sparql */ `
@@ -161,6 +161,21 @@ const buildSnippetListFetchQuery = ({
161
161
  `;
162
162
  };
163
163
 
164
+ const buildSnippetListNameFetchQuery = (snippetListUris: string[]) => {
165
+ return /* sparql */ `
166
+ PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
167
+ PREFIX say: <https://say.data.gift/ns/>
168
+
169
+ SELECT DISTINCT ?snippetList ?snippetListName WHERE {
170
+ ?snippetList a say:SnippetList;
171
+ skos:prefLabel ?snippetListName.
172
+ FILTER (?snippetList IN (${snippetListUris
173
+ .map(sparqlEscapeUri)
174
+ .join(', ')}))
175
+ }
176
+ `;
177
+ };
178
+
164
179
  export const fetchSnippets = async ({
165
180
  endpoint,
166
181
  abortSignal,
@@ -169,7 +184,7 @@ export const fetchSnippets = async ({
169
184
  }: {
170
185
  endpoint: string;
171
186
  abortSignal: AbortSignal;
172
- filter: Filter;
187
+ filter: SnippetFilter;
173
188
  pagination: Pagination;
174
189
  }) => {
175
190
  if (!filter.snippetListUris?.length) {
@@ -196,14 +211,13 @@ export const fetchSnippets = async ({
196
211
  abortSignal,
197
212
  });
198
213
 
199
- const results = queryResult.results.bindings.map(
200
- (binding) =>
201
- new Snippet({
202
- title: binding.title?.value,
203
- createdOn: binding.createdOn?.value,
204
- content: binding.content?.value,
205
- }),
206
- );
214
+ const results = queryResult.results.bindings.map((binding) => {
215
+ return new Snippet({
216
+ title: binding.title?.value,
217
+ createdOn: binding.createdOn?.value,
218
+ content: binding.content?.value,
219
+ });
220
+ });
207
221
 
208
222
  return { totalCount, results };
209
223
  };
@@ -216,7 +230,7 @@ export const fetchSnippetLists = async ({
216
230
  }: {
217
231
  endpoint: string;
218
232
  abortSignal: AbortSignal;
219
- filter: Filter;
233
+ filter: SnippetFilter;
220
234
  orderBy: OrderBy;
221
235
  }) => {
222
236
  // We don't currently use this count, so skip the query, but we'll need it if we make pagination
@@ -263,3 +277,28 @@ export const fetchSnippetLists = async ({
263
277
 
264
278
  return { results };
265
279
  };
280
+
281
+ export const fetchSnippetListNames = async ({
282
+ endpoint,
283
+ abortSignal,
284
+ snippetListUris,
285
+ }: {
286
+ endpoint: string;
287
+ abortSignal: AbortSignal;
288
+ snippetListUris: string[];
289
+ }) => {
290
+ const queryResult = await executeQuery<
291
+ BindingObject<{ snippetList: string; snippetListName: string }>
292
+ >({
293
+ endpoint,
294
+ query: buildSnippetListNameFetchQuery(snippetListUris),
295
+ abortSignal,
296
+ });
297
+
298
+ return new Map(
299
+ queryResult.results.bindings.map((binding) => [
300
+ binding.snippetList.value,
301
+ binding.snippetListName.value,
302
+ ]),
303
+ );
304
+ };
@@ -0,0 +1,132 @@
1
+ import { v4 as uuidv4 } from 'uuid';
2
+ import { EditorState } from '@lblod/ember-rdfa-editor';
3
+ import { getActiveEditableNode } from '@lblod/ember-rdfa-editor/plugins/editable-node';
4
+ import { isRdfaAttrs } from '@lblod/ember-rdfa-editor/core/rdfa-types';
5
+ import { ContextualActionGroup } from '@lblod/ember-rdfa-editor/plugins/contextual-actions';
6
+ import { ResolvedPNode } from '@lblod/ember-rdfa-editor/utils/_private/types';
7
+ import { getTranslationFunction } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/translation';
8
+ import { updateCodelistVariableCommand } from '../utils/codelist-utils';
9
+ import {
10
+ type CodeListOptions,
11
+ fetchCodeListOptions,
12
+ CodeListOption,
13
+ } from '../utils/fetch-data';
14
+ import { CodelistAttrs } from '../variables';
15
+
16
+ const codelistGroupId = 'codelist-db36f85d-edbf-45dc-85fc-9fb67649fe00';
17
+
18
+ const codelistOptionsCache: Map<string, CodeListOptions> = new Map();
19
+
20
+ function getSource(codelistAttrs: CodelistAttrs, fallback?: string) {
21
+ if (codelistAttrs) {
22
+ const source = codelistAttrs['source'];
23
+ if (source && source !== 'UNKNOWN') {
24
+ return source;
25
+ }
26
+ }
27
+ return fallback;
28
+ }
29
+
30
+ type GetContextualActionsAttrs = {
31
+ endpoint?: string;
32
+ };
33
+
34
+ function humanReadableLabel(label: string) {
35
+ return label.replace(/\$\{[^}]+\}/g, '…');
36
+ }
37
+
38
+ async function getCodelistOptionsCached(
39
+ activeNode: ResolvedPNode,
40
+ { endpoint }: GetContextualActionsAttrs,
41
+ ) {
42
+ const codelistAttrs = activeNode.value.attrs as CodelistAttrs | undefined;
43
+ const source = codelistAttrs && getSource(codelistAttrs, endpoint);
44
+ const codelistUri = codelistAttrs?.codelist;
45
+
46
+ let result = codelistUri && codelistOptionsCache.get(codelistUri);
47
+ if (!result && codelistAttrs?.hardcodedOptionList) {
48
+ result = {
49
+ type: '',
50
+ options: codelistAttrs.hardcodedOptionList,
51
+ } as CodeListOptions;
52
+ }
53
+ if (!result && source && codelistUri) {
54
+ result = await fetchCodeListOptions(source, codelistUri);
55
+ codelistOptionsCache.set(codelistUri, result);
56
+ if (!result) {
57
+ console.warn(
58
+ `Failed to fetch codelist options for ${codelistUri} from ${source}`,
59
+ );
60
+ }
61
+ }
62
+
63
+ return result || { type: '', options: [] };
64
+ }
65
+
66
+ export function getContextualActions(attrs: GetContextualActionsAttrs) {
67
+ return async function (state: EditorState, searchQuery?: string) {
68
+ const activeNode = getActiveEditableNode(state);
69
+ if (!activeNode) return [];
70
+ const result = await getCodelistOptionsCached(activeNode, attrs);
71
+ const isMultiSelect = activeNode.value.attrs['selectionStyle'] === 'multi';
72
+ const selectedOptions: CodeListOption[] = [];
73
+ activeNode.value.content.forEach((contentNode) => {
74
+ selectedOptions.push({
75
+ uri: contentNode.attrs.subject,
76
+ label: contentNode.textContent,
77
+ });
78
+ });
79
+
80
+ return result.options
81
+ .filter(
82
+ (option) =>
83
+ !searchQuery ||
84
+ option.label.toLowerCase().includes(searchQuery.toLowerCase()),
85
+ )
86
+ .map((option) => {
87
+ const selected = selectedOptions.some(
88
+ (selOption) => selOption.uri === option.uri,
89
+ );
90
+ const newOptions = selected
91
+ ? selectedOptions.filter((selOption) => selOption.uri !== option.uri)
92
+ : isMultiSelect
93
+ ? [...selectedOptions, option]
94
+ : option;
95
+ return {
96
+ id: uuidv4(),
97
+ label: humanReadableLabel(option.label),
98
+ group: codelistGroupId,
99
+ command: updateCodelistVariableCommand(activeNode, newOptions),
100
+ selected,
101
+ };
102
+ });
103
+ };
104
+ }
105
+
106
+ function contextualGroupIsVisible(state: EditorState) {
107
+ const activeNode = getActiveEditableNode(state);
108
+ const isCodelist =
109
+ activeNode &&
110
+ isRdfaAttrs(activeNode.value.attrs) &&
111
+ activeNode?.value.type.name === 'codelist';
112
+
113
+ return isCodelist;
114
+ }
115
+
116
+ export function getCodelistActionGroups(attrs: GetContextualActionsAttrs) {
117
+ return function (state: EditorState): ContextualActionGroup[] {
118
+ return contextualGroupIsVisible(state)
119
+ ? [
120
+ {
121
+ id: codelistGroupId,
122
+ label: getTranslationFunction(state)(
123
+ 'variable.codelist.label',
124
+ 'Codelijst',
125
+ ),
126
+ keepOpen: true,
127
+ getActions: getContextualActions(attrs),
128
+ },
129
+ ]
130
+ : [];
131
+ };
132
+ }