@lblod/ember-rdfa-editor-lblod-plugins 24.3.2 → 25.1.0-dev.bf9bca801660c11d81ecbcc0cc8dd91084a18702

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 (57) hide show
  1. package/.changeset/purple-bobcats-lick.md +15 -0
  2. package/CHANGELOG.md +28 -0
  3. package/README.md +12 -3
  4. package/addon/components/citation-plugin/citation-insert.ts +14 -7
  5. package/addon/components/lmb-plugin/list.hbs +10 -2
  6. package/addon/components/lmb-plugin/search-modal.hbs +28 -2
  7. package/addon/components/lmb-plugin/search-modal.ts +109 -71
  8. package/addon/components/mandatee-table-plugin/node.gts +1 -1
  9. package/addon/components/snippet-plugin/nodes/placeholder.gts +2 -2
  10. package/addon/components/snippet-plugin/nodes/snippet.gts +54 -11
  11. package/addon/components/snippet-plugin/search-modal.ts +3 -4
  12. package/addon/components/snippet-plugin/snippet-insert-placeholder.gts +4 -4
  13. package/addon/components/snippet-plugin/snippet-insert-rdfa.gts +7 -10
  14. package/addon/components/snippet-plugin/snippet-insert.gts +16 -19
  15. package/addon/components/snippet-plugin/snippet-list/snippet-list-modal.hbs +1 -1
  16. package/addon/components/snippet-plugin/snippet-list/snippet-list-modal.ts +7 -9
  17. package/addon/components/snippet-plugin/snippet-list/snippet-list-view.hbs +1 -1
  18. package/addon/components/snippet-plugin/snippet-list/snippet-list-view.ts +5 -9
  19. package/addon/components/snippet-plugin/snippet-list-select.gts +2 -2
  20. package/addon/components/variable-plugin/date/date-time-picker.ts +2 -2
  21. package/addon/plugins/citation-plugin/index.ts +28 -6
  22. package/addon/plugins/lmb-plugin/index.ts +3 -0
  23. package/addon/plugins/lmb-plugin/utils/fetchMandatees.ts +107 -8
  24. package/addon/plugins/snippet-plugin/commands/insert-snippet.ts +5 -8
  25. package/addon/plugins/snippet-plugin/commands/update-snippet-placeholder.ts +2 -2
  26. package/addon/plugins/snippet-plugin/index.ts +8 -7
  27. package/addon/plugins/snippet-plugin/nodes/snippet-placeholder.ts +59 -16
  28. package/addon/plugins/snippet-plugin/nodes/snippet.ts +29 -11
  29. package/addon/plugins/snippet-plugin/utils/fetch-data.ts +8 -8
  30. package/addon/plugins/snippet-plugin/utils/rdfa-predicate.ts +17 -3
  31. package/addon/utils/constants.ts +11 -0
  32. package/addon/utils/has-descendant.ts +19 -0
  33. package/app/styles/mandatee-table-plugin.scss +5 -0
  34. package/declarations/addon/components/lmb-plugin/search-modal.d.ts +16 -4
  35. package/declarations/addon/components/snippet-plugin/nodes/placeholder.d.ts +1 -1
  36. package/declarations/addon/components/snippet-plugin/nodes/snippet.d.ts +2 -0
  37. package/declarations/addon/components/snippet-plugin/search-modal.d.ts +1 -1
  38. package/declarations/addon/components/snippet-plugin/snippet-insert-rdfa.d.ts +2 -6
  39. package/declarations/addon/components/snippet-plugin/snippet-insert.d.ts +3 -7
  40. package/declarations/addon/components/snippet-plugin/snippet-list/snippet-list-modal.d.ts +3 -3
  41. package/declarations/addon/components/snippet-plugin/snippet-list/snippet-list-view.d.ts +2 -2
  42. package/declarations/addon/components/snippet-plugin/snippet-list-select.d.ts +1 -1
  43. package/declarations/addon/plugins/citation-plugin/index.d.ts +7 -2
  44. package/declarations/addon/plugins/lmb-plugin/index.d.ts +3 -0
  45. package/declarations/addon/plugins/lmb-plugin/utils/fetchMandatees.d.ts +14 -3
  46. package/declarations/addon/plugins/snippet-plugin/commands/insert-snippet.d.ts +3 -4
  47. package/declarations/addon/plugins/snippet-plugin/index.d.ts +6 -2
  48. package/declarations/addon/plugins/snippet-plugin/nodes/snippet-placeholder.d.ts +11 -2
  49. package/declarations/addon/plugins/snippet-plugin/nodes/snippet.d.ts +4 -5
  50. package/declarations/addon/plugins/snippet-plugin/utils/fetch-data.d.ts +1 -1
  51. package/declarations/addon/plugins/snippet-plugin/utils/rdfa-predicate.d.ts +8 -2
  52. package/declarations/addon/utils/constants.d.ts +7 -0
  53. package/declarations/addon/utils/has-descendant.d.ts +2 -0
  54. package/package.json +3 -3
  55. package/pnpm-lock.yaml +6 -6
  56. package/translations/en-US.yaml +6 -0
  57. package/translations/nl-BE.yaml +7 -1
@@ -0,0 +1,15 @@
1
+ ---
2
+ '@lblod/ember-rdfa-editor-lblod-plugins': minor
3
+ ---
4
+
5
+ Extension of configuration options of `citation-plugin`.
6
+ It is now possible to pass a configuration object with the type `CitationPluginNodeConditionConfig` to an instance of the citation plugin.
7
+ This type of config allows you to specify a condition on which an active/context node needs to match.
8
+ ```ts
9
+ interface CitationPluginNodeConditionConfig {
10
+ type: 'node-condition';
11
+ regex?: RegExp;
12
+
13
+ activeInNode(node: PNode): boolean;
14
+ }
15
+ ```
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @lblod/ember-rdfa-editor-lblod-plugins
2
2
 
3
+ ## 25.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#502](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/502) [`9beff10`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/9beff10b99a5825a4eb9be94128c2efb822ac982) Thanks [@abeforgit](https://github.com/abeforgit)! - Improved LMB-plugin
8
+
9
+ - Fixes missing data due to 10000 triple limit of query
10
+ - does all pagination, sorting and searching through the query
11
+ - adds filters for administrative period and unit
12
+
13
+ ## 25.0.0
14
+
15
+ ### Major Changes
16
+
17
+ - [#499](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/499) [`3255c70`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/3255c7021008a801eb5b07ebd49a6e557467b4ba) Thanks [@elpoelma](https://github.com/elpoelma)! - Update `@lblod/ember-rdfa-editor` peerdep requirement to version [10.7.2](https://github.com/lblod/ember-rdfa-editor/releases/tag/v10.7.2)
18
+
19
+ ### Minor Changes
20
+
21
+ - [#495](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/495) [`046b6b7`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/046b6b7c65b1e9144b7c469bbb2d968652639117) Thanks [@piemonkey](https://github.com/piemonkey)! - When removing last snippet of a 'group', replace the placeholder instead of completely deleting
22
+
23
+ ### Patch Changes
24
+
25
+ - [#495](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/495) [`6547270`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/65472703c65a05feb6793a973e3785228822e1a1) Thanks [@piemonkey](https://github.com/piemonkey)! - Fix bug with snippet list names containing a `,` displaying as multiple lists
26
+
27
+ - [#499](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/499) [`3255c70`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/3255c7021008a801eb5b07ebd49a6e557467b4ba) Thanks [@elpoelma](https://github.com/elpoelma)! - Fix margins of mandatee table warning message
28
+
29
+ - [#500](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/500) [`0b336ad`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/0b336addbf4287620d471515aa75c2d62dc2627d) Thanks [@lagartoverde](https://github.com/lagartoverde)! - Fix bug when setting a date with a day bigger than the number of days in the current month
30
+
3
31
  ## 24.3.2
4
32
 
5
33
  ### Patch Changes
package/README.md CHANGED
@@ -335,9 +335,11 @@ Make `this.citationPlugin` a tracked reference to the plugin created with the fu
335
335
 
336
336
  Configuration:
337
337
 
338
- - type: it can be 'nodes' or 'ranges' if nodes is selected you are expected to pass the `activeInNodeTypes` function, otherwise you should pass the `activeInRanges` function
339
- - activeInNodeTypes: it's a function that gets the prosemirror schema and the state of the actual instance of the editor and returns a `Set` of nodetypes where the plugin should be active
340
- - activeInRanges: it's a function that gets the state of the actual instance of the editor and returns an array of ranges for the plugin to be active in, for example `[[0,50], [70,100]]`
338
+ - type: it can either be 'nodes', 'node-condition, or 'ranges' if nodes is selected you are expected to pass the `activeInNodeTypes` function, otherwise you should pass the `activeInRanges` function
339
+ * if 'nodes' is selected, you are expected to pass the `activeInNodeTypes` function. It's a function that gets the prosemirror schema and the state of the actual instance of the editor and returns a `Set` of nodetypes where the plugin should be active
340
+ * if 'node-condition' is selected, you are expected to pass the `activeInNode` function. It's a function which expects an instance of a prosemirror node and returns whether it should be active in that node.
341
+ * if 'ranges' is selected, you are expected to pass the `activeInRanges` function. It's a function that gets the state of the actual instance of the editor and returns an array of ranges for the plugin to be active in, for example `[[0,50], [70,100]]`
342
+
341
343
  - regex: you can provide your custom regex to detect citations, if not the default one will be used
342
344
 
343
345
  A common usecase is to have the plugin active in the entire document. Here's how to do that using each configuration type:
@@ -354,6 +356,13 @@ const configA = {
354
356
  };
355
357
 
356
358
  const configB = {
359
+ type: 'node-condition',
360
+ activeInNodeTypes(node) {
361
+ return node.type === schema.nodes.doc;
362
+ },
363
+ };
364
+
365
+ const configC = {
357
366
  type: 'ranges',
358
367
  activeInRanges(state) {
359
368
  // a node's nodeSize follows the Prosemirror definition
@@ -14,7 +14,10 @@ import {
14
14
  LEGISLATION_TYPES,
15
15
  } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/citation-plugin/utils/types';
16
16
  import { unwrap } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/option';
17
- import { findParentNodeOfType } from '@curvenote/prosemirror-utils';
17
+ import {
18
+ findParentNode,
19
+ findParentNodeOfType,
20
+ } from '@curvenote/prosemirror-utils';
18
21
  import { Article } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/citation-plugin/utils/article';
19
22
  import { LegalDocument } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/citation-plugin/utils/legal-documents';
20
23
  import { AddIcon } from '@appuniversum/ember-appuniversum/components/icons/add';
@@ -62,18 +65,17 @@ export default class EditorPluginsCitationInsertComponent extends Component<Args
62
65
  return true;
63
66
  }
64
67
  const { selection } = this.controller.mainEditorState;
65
- if (this.config.type === 'ranges') {
66
- const ranges = this.config.activeInRanges(
67
- this.controller.mainEditorState,
68
- );
68
+ const config = this.config;
69
+ if (config.type === 'ranges') {
70
+ const ranges = config.activeInRanges(this.controller.mainEditorState);
69
71
  for (const range of ranges) {
70
72
  if (selection.from > range[0] && selection.from < range[1]) {
71
73
  return false;
72
74
  }
73
75
  }
74
76
  return true;
75
- } else {
76
- const nodeTypes = this.config.activeInNodeTypes(
77
+ } else if (config.type === 'nodes') {
78
+ const nodeTypes = config.activeInNodeTypes(
77
79
  this.controller.schema,
78
80
  this.controller.mainEditorState,
79
81
  );
@@ -84,6 +86,11 @@ export default class EditorPluginsCitationInsertComponent extends Component<Args
84
86
  return false;
85
87
  }
86
88
  return !findParentNodeOfType([...nodeTypes])(selection);
89
+ } else {
90
+ if (config.activeInNode(this.controller.mainEditorState.doc)) {
91
+ return false;
92
+ }
93
+ return !findParentNode((node) => config.activeInNode(node))(selection);
87
94
  }
88
95
  }
89
96
 
@@ -37,7 +37,15 @@
37
37
  </tr>
38
38
  </:header>
39
39
  <:body>
40
- {{#unless @services.isRunning}}
40
+ {{#if @services.isRunning}}
41
+ <tr>
42
+ <td colspan='5'>
43
+ <AuLoader @centered={{true}}>
44
+ {{t 'common.search.loading'}}
45
+ </AuLoader>
46
+ </td>
47
+ </tr>
48
+ {{else}}
41
49
  {{#if @services.value.totalCount}}
42
50
  {{#each @services.value.results as |row|}}
43
51
  <tr>
@@ -53,6 +61,6 @@
53
61
  </tr>
54
62
  {{/each}}
55
63
  {{/if}}
56
- {{/unless}}
64
+ {{/if}}
57
65
  </:body>
58
66
  </AuTable>
@@ -21,16 +21,42 @@
21
21
  {{t 'lmb-plugin.modal.search.title'}}
22
22
  </AuHeading>
23
23
  <AuLabel class='au-margin-bottom-small' for='searchTerm'>
24
- {{t 'worship-plugin.modal.fields.name'}}
24
+ {{t 'lmb-plugin.modal.fields.name'}}
25
25
  </AuLabel>
26
26
  <AuNativeInput
27
27
  @type='text'
28
28
  @width='block'
29
29
  id='searchTerm'
30
30
  value={{this.searchText}}
31
- placeholder={{t 'worship-plugin.modal.search.placeholder'}}
31
+ placeholder={{t 'lmb-plugin.modal.nameSearch.placeholder'}}
32
32
  {{on 'input' this.setInputSearchText}}
33
33
  />
34
+ <AuLabel class='au-margin-bottom-small' for='periodSelect'>
35
+ {{t 'lmb-plugin.modal.fields.period'}}
36
+ </AuLabel>
37
+ <PowerSelect
38
+ @allowClear={{false}}
39
+ @searchEnabled={{false}}
40
+ @options={{this.adminPeriods}}
41
+ @selected={{this.selectedAdminPeriod}}
42
+ @onChange={{this.selectAdminPeriod}}
43
+ as |period|
44
+ >
45
+ {{period.label}}
46
+ </PowerSelect>
47
+
48
+ <AuLabel class='au-margin-bottom-small' for='searchAdminUnit'>
49
+ {{t 'lmb-plugin.modal.fields.adminUnit'}}
50
+ </AuLabel>
51
+ <AuNativeInput
52
+ @type='text'
53
+ @width='block'
54
+ id='searchAdminUnit'
55
+ value={{this.adminUnitSearch}}
56
+ placeholder={{t 'lmb-plugin.modal.adminUnitSearch.placeholder'}}
57
+ {{on 'input' this.setAdminUnitSearch}}
58
+ />
59
+
34
60
  </div>
35
61
  </div>
36
62
  </mc.sidebar>
@@ -2,13 +2,22 @@ import Component from '@glimmer/component';
2
2
  import { tracked } from '@glimmer/tracking';
3
3
  import { assert } from '@ember/debug';
4
4
  import { action } from '@ember/object';
5
- import { restartableTask } from 'ember-concurrency';
5
+ import { restartableTask, timeout } from 'ember-concurrency';
6
6
  import { task as trackedTask } from 'reactiveweb/ember-concurrency';
7
7
  import { LmbPluginConfig } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/lmb-plugin';
8
8
 
9
9
  import Mandatee from '@lblod/ember-rdfa-editor-lblod-plugins/models/mandatee';
10
- import { fetchMandatees } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/lmb-plugin/utils/fetchMandatees';
11
- type SearchSort = [keyof Mandatee, 'ASC' | 'DESC'] | false;
10
+ import {
11
+ FetchMandateesArgs,
12
+ fetchMandatees,
13
+ } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/lmb-plugin/utils/fetchMandatees';
14
+ import {
15
+ BESTUURSPERIODES,
16
+ BestuursperiodeLabel,
17
+ BestuursperiodeURI,
18
+ } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
19
+ import { isSome } from '@lblod/ember-rdfa-editor/utils/_private/option';
20
+ export type SearchSort = [keyof Mandatee, 'ASC' | 'DESC'] | false;
12
21
 
13
22
  interface Args {
14
23
  config: LmbPluginConfig;
@@ -16,6 +25,10 @@ interface Args {
16
25
  closeModal: () => void;
17
26
  onInsert: (mandatee: Mandatee) => void;
18
27
  }
28
+ interface AdminPeriodOption {
29
+ label: BestuursperiodeLabel;
30
+ uri: BestuursperiodeURI;
31
+ }
19
32
 
20
33
  export default class LmbPluginSearchModalComponent extends Component<Args> {
21
34
  // Display
@@ -28,96 +41,120 @@ export default class LmbPluginSearchModalComponent extends Component<Args> {
28
41
  @tracked pageSize = 20;
29
42
  @tracked totalCount = 0;
30
43
 
44
+ // Admin periods
45
+ @tracked selectedAdminPeriod: AdminPeriodOption;
46
+ adminPeriods: AdminPeriodOption[];
47
+ // Admin units
48
+ @tracked adminUnitSearch: string;
49
+ // tracks whether the user has just typed a character
50
+ // doesn't need to be reactive
51
+ typing = false;
52
+ constructor(owner: unknown, args: Args) {
53
+ super(owner, args);
54
+ this.adminPeriods = Object.entries(BESTUURSPERIODES).map(
55
+ ([key, value]: [BestuursperiodeLabel, BestuursperiodeURI]) => ({
56
+ label: key,
57
+ uri: value,
58
+ }),
59
+ );
60
+ this.selectedAdminPeriod =
61
+ this.adminPeriods.find(
62
+ (entry) =>
63
+ isSome(args.config.defaultPeriod) &&
64
+ entry.label === args.config.defaultPeriod,
65
+ ) ?? this.adminPeriods[this.adminPeriods.length - 1];
66
+ this.adminUnitSearch = args.config.defaultAdminUnit ?? '';
67
+ }
68
+
31
69
  get config() {
32
70
  return this.args.config;
33
71
  }
72
+ selectAdminPeriod = (value: AdminPeriodOption) => {
73
+ this.selectedAdminPeriod = value;
74
+ this.pageNumber = 0;
75
+ };
76
+ setAdminUnitSearch = (event: InputEvent) => {
77
+ this.typing = true;
78
+ assert(
79
+ 'setAdminUnitSearch must be bound to an input element',
80
+ event.target instanceof HTMLInputElement,
81
+ );
82
+ this.adminUnitSearch = event.target.value;
83
+ this.pageNumber = 0;
84
+ };
34
85
 
35
86
  @action
36
87
  async closeModal() {
88
+ this.typing = false;
37
89
  this.inputSearchText = null;
38
90
  this.sort = false;
39
91
  await this.servicesResource.cancel();
40
92
  this.args.closeModal();
41
93
  }
42
94
 
43
- fetchData = restartableTask(async () => {
44
- return fetchMandatees({ endpoint: this.args.config.endpoint });
45
- });
46
-
47
95
  // TODO Either make this a trackedFunction or do filtering on the query and correctly pass an
48
96
  // AbortController
49
- search = restartableTask(async () => {
50
- if (!this.args.open) {
51
- return {
52
- results: [],
53
- totalCount: 0,
54
- };
55
- }
97
+ search = restartableTask(
98
+ async ({
99
+ endpoint,
100
+ searchString,
101
+ page,
102
+ pageSize,
103
+ sort,
104
+ period,
105
+ adminUnitSearch,
106
+ }: FetchMandateesArgs) => {
107
+ // debounce, but only when the input fields are being used
108
+ if (this.typing) {
109
+ this.typing = false;
110
+ await timeout(250);
111
+ }
112
+
113
+ if (!this.args.open) {
114
+ return {
115
+ results: [],
116
+ totalCount: 0,
117
+ };
118
+ }
56
119
 
57
- // Can't do what I want, so if the user modifies the filter before resolving the query will run again
58
- if (!this.fetchData.lastComplete) {
59
120
  try {
60
- await this.fetchData.perform();
121
+ const result = await fetchMandatees({
122
+ endpoint,
123
+ searchString,
124
+ page,
125
+ pageSize,
126
+ sort,
127
+ period,
128
+ adminUnitSearch,
129
+ });
130
+ const { count, mandatees } = result;
131
+
132
+ return {
133
+ results: mandatees,
134
+ totalCount: count,
135
+ };
61
136
  } catch (err) {
62
137
  console.error('Got an error fetching LMB data', err);
63
138
  this.error = err;
64
139
  }
65
- }
66
-
67
- if (!this.fetchData.lastComplete?.value) return;
68
- let mandatees: Mandatee[] = [...this.fetchData.lastComplete.value];
69
-
70
- if (this.inputSearchText) {
71
- mandatees = mandatees?.filter((mandatee: Mandatee) =>
72
- mandatee.fullName
73
- .toLowerCase()
74
- .includes(this.inputSearchText?.toLowerCase() as string),
75
- );
76
- }
77
-
78
- if (this.sort) {
79
- const [key, sortingDirection] = this.sort;
80
- mandatees = mandatees.sort((a: Mandatee, b: Mandatee) => {
81
- if (key === 'fullName') {
82
- if (a.lastName === b.lastName) {
83
- if (a.firstName > b.firstName) {
84
- return sortingDirection === 'ASC' ? 1 : -1;
85
- } else {
86
- return sortingDirection === 'ASC' ? -1 : 1;
87
- }
88
- } else {
89
- if (a.lastName > b.lastName) {
90
- return sortingDirection === 'ASC' ? 1 : -1;
91
- } else {
92
- return sortingDirection === 'ASC' ? -1 : 1;
93
- }
94
- }
95
- } else if (a[key] > b[key]) {
96
- return sortingDirection === 'ASC' ? 1 : -1;
97
- } else {
98
- return sortingDirection === 'ASC' ? -1 : 1;
99
- }
100
- });
101
- }
102
-
103
- const totalCount = mandatees?.length;
104
-
105
- mandatees = mandatees?.slice(
106
- this.pageSize * this.pageNumber,
107
- this.pageSize * (this.pageNumber + 1),
108
- );
109
- return {
110
- results: mandatees,
111
- totalCount,
112
- };
113
- });
140
+ return {
141
+ results: [],
142
+ totalCount: 0,
143
+ };
144
+ },
145
+ );
114
146
 
115
147
  servicesResource = trackedTask(this, this.search, () => [
116
- this.inputSearchText,
117
- this.sort,
118
- this.pageNumber,
119
- this.pageSize,
120
- this.args.open,
148
+ {
149
+ endpoint: this.args.config.endpoint,
150
+ searchString: this.inputSearchText ?? '',
151
+ sort: this.sort,
152
+ page: this.pageNumber,
153
+ pageSize: this.pageSize,
154
+ open: this.args.open,
155
+ period: this.selectedAdminPeriod.uri,
156
+ adminUnitSearch: this.adminUnitSearch,
157
+ } satisfies Partial<FetchMandateesArgs> & { open: boolean },
121
158
  ]);
122
159
 
123
160
  @action
@@ -130,6 +167,7 @@ export default class LmbPluginSearchModalComponent extends Component<Args> {
130
167
  'inputSearchText must be bound to an input element',
131
168
  event.target instanceof HTMLInputElement,
132
169
  );
170
+ this.typing = true;
133
171
 
134
172
  this.inputSearchText = event.target.value;
135
173
  this.pageNumber = 0;
@@ -45,7 +45,7 @@ export default class MandateeTableNode extends Component<Sig> {
45
45
  />
46
46
  <div>
47
47
  <h6 class='say-mandatee-table__title'>{{this.title}}</h6>
48
- <p>
48
+ <p class='say-mandatee-table__warning'>
49
49
  {{this.warning}}
50
50
  </p>
51
51
  </div>
@@ -8,13 +8,13 @@ import { service } from '@ember/service';
8
8
  import IntlService from 'ember-intl/services/intl';
9
9
 
10
10
  interface Signature {
11
- Args: EmberNodeArgs;
11
+ Args: Pick<EmberNodeArgs, 'node' | 'selectNode'>;
12
12
  }
13
13
 
14
14
  export default class SnippetPluginPlaceholder extends Component<Signature> {
15
15
  @service declare intl: IntlService;
16
16
  get listNames() {
17
- return this.args.node.attrs.listNames;
17
+ return this.args.node.attrs.snippetListNames;
18
18
  }
19
19
  get isSingleList() {
20
20
  return this.listNames.length === 1;
@@ -28,6 +28,8 @@ import insertSnippet from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/snippe
28
28
  import { isNone } from '@lblod/ember-rdfa-editor/utils/_private/option';
29
29
  import { transactionCombinator } from '@lblod/ember-rdfa-editor/utils/transaction-utils';
30
30
  import { recalculateNumbers } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/structure-plugin/recalculate-structure-numbers';
31
+ import { createSnippetPlaceholder } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/snippet-plugin/nodes/snippet-placeholder';
32
+ import { hasDecendant } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/has-descendant';
31
33
 
32
34
  interface ButtonSig {
33
35
  Args: {
@@ -62,6 +64,15 @@ export default class SnippetNode extends Component<Signature> {
62
64
  get controller() {
63
65
  return this.args.controller;
64
66
  }
67
+ get schema() {
68
+ return this.controller.schema;
69
+ }
70
+ get snippetOrPlaceholder() {
71
+ return [
72
+ this.schema.nodes.snippet,
73
+ this.schema.nodes.snippet_placeholder,
74
+ ].filter(Boolean);
75
+ }
65
76
  get node() {
66
77
  return this.args.node;
67
78
  }
@@ -94,17 +105,46 @@ export default class SnippetNode extends Component<Signature> {
94
105
  deleteFragment() {
95
106
  const position = this.args.getPos();
96
107
  if (position !== undefined) {
97
- this.controller.withTransaction((tr) => {
98
- return transactionCombinator(
99
- this.controller.mainEditorState,
100
- tr.deleteRange(position, position + this.node.nodeSize),
101
- )([recalculateNumbers]).transaction;
102
- });
108
+ const matchingSnippetExists = hasDecendant(
109
+ this.controller.mainEditorState.doc,
110
+ (node) =>
111
+ node !== this.node &&
112
+ this.snippetOrPlaceholder.includes(node.type) &&
113
+ node.attrs.placeholderId === this.node.attrs.placeholderId,
114
+ );
115
+ if (matchingSnippetExists) {
116
+ this.controller.withTransaction((tr) => {
117
+ return transactionCombinator(
118
+ this.controller.mainEditorState,
119
+ tr.deleteRange(position, position + this.node.nodeSize),
120
+ )([recalculateNumbers]).transaction;
121
+ });
122
+ } else {
123
+ const node = createSnippetPlaceholder({
124
+ listProperties: {
125
+ placeholderId: this.node.attrs.placeholderId,
126
+ listIds: this.node.attrs.snippetListIds,
127
+ names: this.node.attrs.snippetListNames,
128
+ importedResources: this.node.attrs.importedResources,
129
+ },
130
+ schema: this.schema,
131
+ allowMultipleSnippets: this.allowMultipleSnippets,
132
+ });
133
+
134
+ this.args.controller.withTransaction(
135
+ (tr) =>
136
+ transactionCombinator(
137
+ this.controller.mainEditorState,
138
+ tr.replaceWith(position, position + this.node.nodeSize, node),
139
+ )([recalculateNumbers]).transaction,
140
+ { view: this.args.controller.mainEditorView },
141
+ );
142
+ }
103
143
  }
104
144
  }
105
145
  createSliceFromElement(element: Element) {
106
146
  return new Slice(
107
- ProseParser.fromSchema(this.controller.schema).parse(element, {
147
+ ProseParser.fromSchema(this.schema).parse(element, {
108
148
  preserveWhitespace: true,
109
149
  }).content,
110
150
  0,
@@ -127,7 +167,6 @@ export default class SnippetNode extends Component<Signature> {
127
167
  @action
128
168
  onInsert(content: string, title: string) {
129
169
  this.closeModal();
130
- const assignedSnippetListsIds = this.node.attrs.assignedSnippetListsIds;
131
170
  let start = 0;
132
171
  let end = 0;
133
172
  const pos = this.args.getPos();
@@ -147,8 +186,12 @@ export default class SnippetNode extends Component<Signature> {
147
186
  insertSnippet({
148
187
  content,
149
188
  title,
150
- assignedSnippetListsIds,
151
- importedResources: this.node.attrs.importedResources,
189
+ listProperties: {
190
+ placeholderId: this.node.attrs.placeholderId,
191
+ listIds: this.node.attrs.snippetListIds,
192
+ names: this.node.attrs.snippetListNames,
193
+ importedResources: this.node.attrs.importedResources,
194
+ },
152
195
  range: { start, end },
153
196
  allowMultipleSnippets: this.allowMultipleSnippets,
154
197
  }),
@@ -189,7 +232,7 @@ export default class SnippetNode extends Component<Signature> {
189
232
  @closeModal={{this.closeModal}}
190
233
  @config={{this.node.attrs.config}}
191
234
  @onInsert={{this.onInsert}}
192
- @assignedSnippetListsIds={{this.node.attrs.assignedSnippetListsIds}}
235
+ @snippetListIds={{this.node.attrs.snippetListIds}}
193
236
  />
194
237
  </template>
195
238
  }
@@ -11,7 +11,7 @@ import { SnippetPluginConfig } from '@lblod/ember-rdfa-editor-lblod-plugins/plug
11
11
 
12
12
  interface Args {
13
13
  config: SnippetPluginConfig;
14
- assignedSnippetListsIds: string[] | undefined;
14
+ snippetListIds: string[] | undefined;
15
15
  closeModal: () => void;
16
16
  open: boolean;
17
17
  onInsert: (content: string, title: string) => void;
@@ -64,8 +64,7 @@ export default class SnippetPluginSearchModalComponent extends Component<Args> {
64
64
  abortSignal: abortController.signal,
65
65
  filter: {
66
66
  name: this.inputSearchText ?? undefined,
67
- assignedSnippetListIds:
68
- this.args.assignedSnippetListsIds ?? undefined,
67
+ snippetListIds: this.args.snippetListIds ?? undefined,
69
68
  },
70
69
  pagination: {
71
70
  pageNumber: this.pageNumber,
@@ -88,7 +87,7 @@ export default class SnippetPluginSearchModalComponent extends Component<Args> {
88
87
  this.inputSearchText,
89
88
  this.pageNumber,
90
89
  this.pageSize,
91
- this.args.assignedSnippetListsIds,
90
+ this.args.snippetListIds,
92
91
  ]);
93
92
 
94
93
  @action
@@ -43,11 +43,11 @@ export default class SnippetPluginSnippetInsertPlaceholder extends Component<Sig
43
43
  allowMultipleSnippets: boolean,
44
44
  ) {
45
45
  if (lists) {
46
- const node = createSnippetPlaceholder(
46
+ const node = createSnippetPlaceholder({
47
47
  lists,
48
- this.args.controller.schema,
48
+ schema: this.args.controller.schema,
49
49
  allowMultipleSnippets,
50
- );
50
+ });
51
51
 
52
52
  this.args.controller.withTransaction(
53
53
  (tr) => {
@@ -72,7 +72,7 @@ export default class SnippetPluginSnippetInsertPlaceholder extends Component<Sig
72
72
  </li>
73
73
  <SnippetListModal
74
74
  @config={{@config}}
75
- @assignedSnippetListsIds={{empty}}
75
+ @snippetListIds={{empty}}
76
76
  @allowMultipleSnippets={{false}}
77
77
  @onSaveSnippetLists={{this.insertPlaceholder}}
78
78
  @open={{this.isModalOpen}}
@@ -3,7 +3,7 @@ import Component from '@glimmer/component';
3
3
 
4
4
  import { SayController } from '@lblod/ember-rdfa-editor';
5
5
  import {
6
- type ImportedResourceMap,
6
+ type SnippetListProperties,
7
7
  type SnippetPluginConfig,
8
8
  } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/snippet-plugin';
9
9
  import { findParentNodeClosestToPos } from '@curvenote/prosemirror-utils';
@@ -23,13 +23,7 @@ interface Sig {
23
23
  }
24
24
 
25
25
  export default class SnippetInsertRdfaComponent extends Component<Sig> {
26
- get disableInsert() {
27
- return (this.snippetListProperties?.listIds.length ?? 0) === 0;
28
- }
29
-
30
- get snippetListProperties():
31
- | { listIds: string[]; importedResources: ImportedResourceMap }
32
- | undefined {
26
+ get listProperties(): SnippetListProperties | undefined {
33
27
  const activeNode = this.args.node.value;
34
28
  const activeNodeSnippetListIds = getSnippetListIdsProperties(activeNode);
35
29
 
@@ -38,6 +32,8 @@ export default class SnippetInsertRdfaComponent extends Component<Sig> {
38
32
  listIds: getAssignedSnippetListsIdsFromProperties(
39
33
  activeNodeSnippetListIds,
40
34
  ),
35
+ placeholderId: activeNode.attrs.placeholderId,
36
+ names: activeNode.attrs.snippetListNames,
41
37
  importedResources: activeNode.attrs.importedResources,
42
38
  };
43
39
  }
@@ -58,6 +54,8 @@ export default class SnippetInsertRdfaComponent extends Component<Sig> {
58
54
  if (properties.length > 0) {
59
55
  return {
60
56
  listIds: getAssignedSnippetListsIdsFromProperties(properties),
57
+ placeholderId: parentNode.node.attrs.placeholderId,
58
+ names: parentNode.node.attrs.snippetListNames,
61
59
  importedResources: parentNode.node.attrs.importedResources,
62
60
  };
63
61
  }
@@ -79,8 +77,7 @@ export default class SnippetInsertRdfaComponent extends Component<Sig> {
79
77
  <SnippetInsert
80
78
  @config={{@config}}
81
79
  @controller={{@controller}}
82
- @snippetListProperties={{this.snippetListProperties}}
83
- @disabled={{this.disableInsert}}
80
+ @listProperties={{this.listProperties}}
84
81
  @allowMultipleSnippets={{this.allowMultipleSnippets}}
85
82
  />
86
83
  </template>