@lblod/ember-rdfa-editor-lblod-plugins 38.3.0 → 39.0.0-dev.6a3569a13725aa2d7abe9f04a28855273f0788e2

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 (26) hide show
  1. package/.changeset/clean-trains-punch.md +5 -0
  2. package/CHANGELOG.md +24 -0
  3. package/addon/components/lmb-plugin/{insert.ts → insert.gts} +28 -1
  4. package/addon/components/lmb-plugin/list.gts +85 -0
  5. package/addon/components/lmb-plugin/search-modal.gts +347 -0
  6. package/addon/components/variable-plugin/date/floating-date-container.gts +1 -1
  7. package/addon/plugins/lmb-plugin/index.ts +2 -1
  8. package/addon/plugins/lmb-plugin/utils/fetchElectees.ts +132 -20
  9. package/addon/plugins/location-plugin/node.ts +4 -4
  10. package/addon/plugins/variable-plugin/contextual-actions/person.ts +1 -1
  11. package/addon/plugins/variable-plugin/variables/address.ts +5 -4
  12. package/addon/plugins/variable-plugin/variables/date.ts +10 -3
  13. package/addon/plugins/variable-plugin/variables/number.ts +3 -1
  14. package/addon/plugins/variable-plugin/variables/person.ts +2 -1
  15. package/declarations/addon/components/lmb-plugin/insert.d.ts +1 -1
  16. package/declarations/addon/components/lmb-plugin/list.d.ts +17 -1
  17. package/declarations/addon/components/lmb-plugin/search-modal.d.ts +18 -11
  18. package/declarations/addon/plugins/lmb-plugin/index.d.ts +2 -1
  19. package/declarations/addon/plugins/lmb-plugin/utils/fetchElectees.d.ts +19 -3
  20. package/package.json +1 -1
  21. package/translations/en-US.yaml +1 -0
  22. package/translations/nl-BE.yaml +1 -0
  23. package/addon/components/lmb-plugin/insert.hbs +0 -18
  24. package/addon/components/lmb-plugin/list.hbs +0 -60
  25. package/addon/components/lmb-plugin/search-modal.hbs +0 -100
  26. package/addon/components/lmb-plugin/search-modal.ts +0 -189
@@ -0,0 +1,5 @@
1
+ ---
2
+ '@lblod/ember-rdfa-editor-lblod-plugins': patch
3
+ ---
4
+
5
+ Fix variable serialization to include label
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @lblod/ember-rdfa-editor-lblod-plugins
2
2
 
3
+ ## 39.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#682](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/682) [`370378d`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/370378d9718bf1d757e5026134eecce579280503) Thanks [@elpoelma](https://github.com/elpoelma)! - LMB-plugin: breaking change to the `LMBPluginConfig` interface:
8
+ the `defaultAdminUnit` now expects an object of the shape
9
+
10
+ ```ts
11
+ {
12
+ uri: string;
13
+ label: string;
14
+ }
15
+ ```
16
+
17
+ The administrative unit input field of the `lmb-plugin` `SearchModal` component is now implement through a dynamic `PowerSelect` component rather than a free-text input field.
18
+
19
+ ### Minor Changes
20
+
21
+ - [#682](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/682) [`370378d`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/370378d9718bf1d757e5026134eecce579280503) Thanks [@elpoelma](https://github.com/elpoelma)! - LMB-plugin: improve SPARQL query performance
22
+
23
+ ### Patch Changes
24
+
25
+ - [#679](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/679) [`e08902b`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/e08902b088d4e649abe0f4396341e581d8949fa2) Thanks [@lagartoverde](https://github.com/lagartoverde)! - Fix floating window position on date variable
26
+
3
27
  ## 38.3.0
4
28
 
5
29
  ### Minor Changes
@@ -15,6 +15,10 @@ import { service } from '@ember/service';
15
15
  import IntlService from 'ember-intl/services/intl';
16
16
  import { Person } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/variable-plugin/variables';
17
17
  import { createPersonVariable } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/variable-plugin/actions/create-person-variable';
18
+ import AuButton from '@appuniversum/ember-appuniversum/components/au-button';
19
+ import SearchModal from './search-modal';
20
+ import { on } from '@ember/modifier';
21
+ import t from 'ember-intl/helpers/t';
18
22
 
19
23
  interface Args {
20
24
  controller: SayController;
@@ -27,7 +31,10 @@ export default class LmbPluginInsertComponent extends Component<Args> {
27
31
  AddIcon = AddIcon;
28
32
 
29
33
  get showModal() {
30
- return getLmbModalsPluginState(this.controller.mainEditorState)?.modalOpen;
34
+ return (
35
+ getLmbModalsPluginState(this.controller.mainEditorState)?.modalOpen ??
36
+ false
37
+ );
31
38
  }
32
39
 
33
40
  get controller() {
@@ -66,4 +73,24 @@ export default class LmbPluginInsertComponent extends Component<Args> {
66
73
  view: this.controller.mainEditorView,
67
74
  });
68
75
  }
76
+
77
+ <template>
78
+ <li class='au-c-list__item'>
79
+ <AuButton
80
+ @icon={{this.AddIcon}}
81
+ @iconAlignment='left'
82
+ @skin='link'
83
+ {{on 'click' this.openModal}}
84
+ >
85
+ {{t 'lmb-plugin.insert.title'}}
86
+ </AuButton>
87
+ </li>
88
+
89
+ <SearchModal
90
+ @open={{this.showModal}}
91
+ @closeModal={{this.closeModal}}
92
+ @config={{@config}}
93
+ @onInsert={{this.onInsert}}
94
+ />
95
+ </template>
69
96
  }
@@ -0,0 +1,85 @@
1
+ import type { TOC } from '@ember/component/template-only';
2
+ import AuTable from '@appuniversum/ember-appuniversum/components/au-table';
3
+ import SortableTableHeader from '@lblod/ember-rdfa-editor-lblod-plugins/components/common/sort/sortable-table-header';
4
+ import { SearchSort } from './search-modal';
5
+ import t from 'ember-intl/helpers/t';
6
+ import Electee from '@lblod/ember-rdfa-editor-lblod-plugins/models/electee';
7
+ import AuButton from '@appuniversum/ember-appuniversum/components/au-button';
8
+ import { fn } from '@ember/helper';
9
+ import { on } from '@ember/modifier';
10
+ import AuLoader from '@appuniversum/ember-appuniversum/components/au-loader';
11
+ import { TaskInstance } from 'reactiveweb/ember-concurrency';
12
+
13
+ interface Signature {
14
+ Args: {
15
+ sort: SearchSort;
16
+ setSort: (sort: SearchSort) => void;
17
+ services: TaskInstance<{ results: Electee[]; totalCount: number }>;
18
+ insert: (electee: Electee) => unknown;
19
+ };
20
+ }
21
+
22
+ const List: TOC<Signature> = <template>
23
+ <AuTable>
24
+ <:header>
25
+ <tr class='au-c-data-table__header-title'>
26
+ <th>
27
+ <SortableTableHeader
28
+ @field='fullName'
29
+ @label={{t 'lmb-plugin.modal.fields.name'}}
30
+ @sort={{@sort}}
31
+ @setSort={{@setSort}}
32
+ />
33
+ </th>
34
+ <th>
35
+ <SortableTableHeader
36
+ @field='kandidatenlijst'
37
+ @label={{t 'lmb-plugin.modal.fields.kandidatenlijst'}}
38
+ @sort={{@sort}}
39
+ @setSort={{@setSort}}
40
+ />
41
+ </th>
42
+ <th />
43
+ </tr>
44
+ </:header>
45
+ <:body>
46
+ {{#if @services.isRunning}}
47
+ <tr>
48
+ <td colspan='100%'>
49
+ <AuLoader @centered={{true}}>
50
+ {{t 'common.search.loading'}}
51
+ </AuLoader>
52
+ </td>
53
+ </tr>
54
+ {{else}}
55
+ {{#if @services.value.totalCount}}
56
+ {{#each @services.value.results as |row|}}
57
+ <tr>
58
+ <td>{{row.fullName}}</td>
59
+ <td>
60
+ {{#if row.kandidatenlijst}}
61
+ {{row.kandidatenlijst}}
62
+ {{else}}
63
+ <span class='au-u-italic'>{{t 'common.not-applicable'}}</span>
64
+ {{/if}}
65
+ </td>
66
+ <td class='au-u-text-center'>
67
+ <AuButton {{on 'click' (fn @insert row)}}>
68
+ {{t 'lmb-plugin.modal.insert'}}
69
+ </AuButton>
70
+ </td>
71
+ </tr>
72
+ {{/each}}
73
+ {{else}}
74
+ <tr>
75
+ <td colspan='100%'>
76
+ {{t 'common.search.no-results'}}
77
+ </td>
78
+ </tr>
79
+ {{/if}}
80
+ {{/if}}
81
+ </:body>
82
+ </AuTable>
83
+ </template>;
84
+
85
+ export default List;
@@ -0,0 +1,347 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { assert } from '@ember/debug';
4
+ import { action } from '@ember/object';
5
+ import { restartableTask, timeout } from 'ember-concurrency';
6
+ import { task as trackedTask } from 'reactiveweb/ember-concurrency';
7
+ import { LmbPluginConfig } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/lmb-plugin';
8
+
9
+ import Electee from '@lblod/ember-rdfa-editor-lblod-plugins/models/electee';
10
+ import {
11
+ FetchMandateesArgs,
12
+ fetchAdministrativeUnits,
13
+ fetchElectees,
14
+ } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/lmb-plugin/utils/fetchElectees';
15
+ import {
16
+ BESTUURSPERIODES,
17
+ BestuursperiodeLabel,
18
+ BestuursperiodeURI,
19
+ } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
20
+ import { isSome } from '@lblod/ember-rdfa-editor/utils/_private/option';
21
+ import type { AdministrativeUnit } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/worship-plugin';
22
+ import { localCopy } from 'tracked-toolbox';
23
+ import AuMainContainer from '@appuniversum/ember-appuniversum/components/au-main-container';
24
+ import AuModal from '@appuniversum/ember-appuniversum/components/au-modal';
25
+ import AuHeading from '@appuniversum/ember-appuniversum/components/au-heading';
26
+ import AuLabel from '@appuniversum/ember-appuniversum/components/au-label';
27
+ import AuNativeInput from '../au-native-input';
28
+ import PowerSelect from 'ember-power-select/components/power-select';
29
+ import perform from 'ember-concurrency/helpers/perform';
30
+ import List from './list';
31
+ import pagination from '@lblod/ember-rdfa-editor-lblod-plugins/helpers/pagination';
32
+ import PaginationView from '../pagination/pagination-view';
33
+ import { not } from 'ember-truth-helpers';
34
+ import t from 'ember-intl/helpers/t';
35
+ import AlertLoadError from '../common/search/alert-load-error';
36
+ import { on } from '@ember/modifier';
37
+ export type SearchSort = [keyof Electee, 'ASC' | 'DESC'] | false;
38
+
39
+ interface Signature {
40
+ Args: {
41
+ config: LmbPluginConfig;
42
+ open: boolean;
43
+ closeModal: () => void;
44
+ onInsert: (electee: Electee) => void;
45
+ };
46
+ }
47
+
48
+ interface AdminPeriodOption {
49
+ label: BestuursperiodeLabel;
50
+ uri: BestuursperiodeURI;
51
+ }
52
+
53
+ export default class SearchModal extends Component<Signature> {
54
+ // Display
55
+ @tracked error: unknown;
56
+ @tracked inputSearchText: string | null = null;
57
+ @tracked sort: SearchSort = false;
58
+
59
+ // Pagination
60
+ @tracked pageNumber = 0;
61
+ @tracked pageSize = 20;
62
+ @tracked totalCount = 0;
63
+
64
+ // Admin periods
65
+ @tracked selectedAdminPeriod: AdminPeriodOption;
66
+ adminPeriods: AdminPeriodOption[];
67
+ // Admin units
68
+ @localCopy('args.config.defaultAdminUnit')
69
+ selectedAdministrativeUnit?: AdministrativeUnit;
70
+ // tracks whether the user has just typed a character
71
+ // doesn't need to be reactive
72
+ typing = false;
73
+ constructor(owner: unknown, args: Signature['Args']) {
74
+ super(owner, args);
75
+ this.adminPeriods = Object.entries(BESTUURSPERIODES).map(
76
+ ([key, value]: [BestuursperiodeLabel, BestuursperiodeURI]) => ({
77
+ label: key,
78
+ uri: value,
79
+ }),
80
+ );
81
+ this.selectedAdminPeriod =
82
+ this.adminPeriods.find(
83
+ (entry) =>
84
+ isSome(args.config.defaultPeriod) &&
85
+ entry.label === args.config.defaultPeriod,
86
+ ) ?? this.adminPeriods[this.adminPeriods.length - 1];
87
+ }
88
+
89
+ get config() {
90
+ return this.args.config;
91
+ }
92
+ selectAdminPeriod = (value: AdminPeriodOption) => {
93
+ this.selectedAdminPeriod = value;
94
+ this.pageNumber = 0;
95
+ };
96
+
97
+ @action
98
+ async closeModal() {
99
+ this.typing = false;
100
+ this.inputSearchText = null;
101
+ this.sort = false;
102
+ await this.servicesResource.cancel();
103
+ this.args.closeModal();
104
+ }
105
+
106
+ search = restartableTask(
107
+ async ({
108
+ endpoint,
109
+ searchString,
110
+ page,
111
+ pageSize,
112
+ sort,
113
+ period,
114
+ administrativeUnit,
115
+ }: FetchMandateesArgs) => {
116
+ // debounce, but only when the input fields are being used
117
+ if (this.typing) {
118
+ this.typing = false;
119
+ await timeout(250);
120
+ }
121
+
122
+ if (!this.args.open) {
123
+ return {
124
+ results: [],
125
+ totalCount: 0,
126
+ };
127
+ }
128
+
129
+ const abortController = new AbortController();
130
+
131
+ try {
132
+ const result = await fetchElectees({
133
+ endpoint,
134
+ searchString,
135
+ page,
136
+ pageSize,
137
+ sort,
138
+ period,
139
+ administrativeUnit,
140
+ abortSignal: abortController.signal,
141
+ });
142
+ const { count, electees } = result;
143
+
144
+ return {
145
+ results: electees,
146
+ totalCount: count,
147
+ };
148
+ } catch (err) {
149
+ console.error('Got an error fetching electees', err);
150
+ this.error = err;
151
+ } finally {
152
+ abortController.abort();
153
+ }
154
+ return {
155
+ results: [],
156
+ totalCount: 0,
157
+ };
158
+ },
159
+ );
160
+
161
+ servicesResource = trackedTask<{ results: Electee[]; totalCount: number }>(
162
+ this,
163
+ this.search,
164
+ () => [
165
+ {
166
+ endpoint: this.args.config.endpoint,
167
+ searchString: this.inputSearchText ?? '',
168
+ sort: this.sort,
169
+ page: this.pageNumber,
170
+ pageSize: this.pageSize,
171
+ open: this.args.open,
172
+ period: this.selectedAdminPeriod.uri,
173
+ administrativeUnit: this.selectedAdministrativeUnit,
174
+ } satisfies Partial<FetchMandateesArgs> & { open: boolean },
175
+ ],
176
+ );
177
+
178
+ searchAdministrativeUnits = restartableTask(async (search: string) => {
179
+ await timeout(200);
180
+
181
+ const abortController = new AbortController();
182
+ try {
183
+ const units = await fetchAdministrativeUnits({
184
+ endpoint: this.config.endpoint,
185
+ searchString: search,
186
+ lmbPeriod: this.selectedAdminPeriod.uri,
187
+ abortSignal: abortController.signal,
188
+ });
189
+
190
+ return units;
191
+ } catch (err) {
192
+ // ember-power-select doesn't seem to have a way to display errors.
193
+ console.error(
194
+ 'Error occurred when searching for administrative units',
195
+ err,
196
+ );
197
+ // We just re-throw to keep TS happy, ember-concurrency just swallows it.
198
+ throw err;
199
+ } finally {
200
+ abortController.abort();
201
+ }
202
+ });
203
+
204
+ selectAdministrativeUnit = (administrativeUnit: AdministrativeUnit) => {
205
+ this.selectedAdministrativeUnit = administrativeUnit;
206
+ this.pageNumber = 0;
207
+ };
208
+
209
+ @action
210
+ setSort(sort: SearchSort) {
211
+ this.sort = sort;
212
+ }
213
+ @action
214
+ setInputSearchText(event: InputEvent) {
215
+ assert(
216
+ 'inputSearchText must be bound to an input element',
217
+ event.target instanceof HTMLInputElement,
218
+ );
219
+ this.typing = true;
220
+
221
+ this.inputSearchText = event.target.value;
222
+ this.pageNumber = 0;
223
+ }
224
+ @action
225
+ previousPage() {
226
+ --this.pageNumber;
227
+ }
228
+
229
+ @action
230
+ nextPage() {
231
+ ++this.pageNumber;
232
+ }
233
+ @action
234
+ async onInsert(electee: Electee) {
235
+ this.args.onInsert(electee);
236
+ await this.closeModal();
237
+ }
238
+
239
+ <template>
240
+ <AuModal
241
+ class='worship-modal'
242
+ @modalOpen={{@open}}
243
+ @closeModal={{this.closeModal}}
244
+ @title={{t 'lmb-plugin.modal.title'}}
245
+ @size='large'
246
+ @padding='none'
247
+ as |modal|
248
+ >
249
+ <modal.Body>
250
+ <AuMainContainer class='worship-modal--main-container' as |mc|>
251
+ <mc.sidebar>
252
+ <div class='au-c-sidebar'>
253
+ <div class='au-c-sidebar__content au-u-padding'>
254
+ <AuHeading
255
+ @level='3'
256
+ @skin='4'
257
+ class='au-u-padding-bottom-small'
258
+ >
259
+ {{t 'lmb-plugin.modal.search.title'}}
260
+ </AuHeading>
261
+ <AuLabel class='au-margin-bottom-small' for='searchTerm'>
262
+ {{t 'lmb-plugin.modal.fields.name'}}
263
+ </AuLabel>
264
+ <AuNativeInput
265
+ @type='text'
266
+ @width='block'
267
+ id='searchTerm'
268
+ value={{this.inputSearchText}}
269
+ placeholder={{t 'lmb-plugin.modal.nameSearch.placeholder'}}
270
+ {{on 'input' this.setInputSearchText}}
271
+ />
272
+ <AuLabel class='au-margin-bottom-small' for='periodSelect'>
273
+ {{t 'lmb-plugin.modal.fields.period'}}
274
+ </AuLabel>
275
+ <PowerSelect
276
+ @allowClear={{false}}
277
+ @searchEnabled={{false}}
278
+ @options={{this.adminPeriods}}
279
+ @selected={{this.selectedAdminPeriod}}
280
+ @onChange={{this.selectAdminPeriod}}
281
+ as |period|
282
+ >
283
+ {{period.label}}
284
+ </PowerSelect>
285
+
286
+ <AuLabel class='au-margin-bottom-small' for='searchAdminUnit'>
287
+ {{t 'lmb-plugin.modal.fields.adminUnit'}}
288
+ </AuLabel>
289
+ <PowerSelect
290
+ @allowClear={{true}}
291
+ @searchEnabled={{true}}
292
+ @search={{perform this.searchAdministrativeUnits}}
293
+ @selected={{this.selectedAdministrativeUnit}}
294
+ @onChange={{this.selectAdministrativeUnit}}
295
+ @loadingMessage={{t 'common.search.loading'}}
296
+ @placeholder={{t
297
+ 'lmb-plugin.modal.adminUnitSearch.placeholder'
298
+ }}
299
+ @searchMessage={{t 'common.search.type-to-search'}}
300
+ as |unit|
301
+ >
302
+ {{unit.label}}
303
+ </PowerSelect>
304
+
305
+ </div>
306
+ </div>
307
+ </mc.sidebar>
308
+ <mc.content @scroll={{true}}>
309
+ <div class='worship-modal--list-container'>
310
+ {{#if this.error}}
311
+ <AlertLoadError @error={{this.error}} />
312
+ {{else}}
313
+ <List
314
+ @services={{this.servicesResource}}
315
+ @sort={{this.sort}}
316
+ @setSort={{this.setSort}}
317
+ @insert={{this.onInsert}}
318
+ />
319
+ {{/if}}
320
+ </div>
321
+ {{#if this.servicesResource.value.totalCount}}
322
+ {{#let
323
+ (pagination
324
+ page=this.pageNumber
325
+ pageSize=this.pageSize
326
+ count=this.servicesResource.value.totalCount
327
+ )
328
+ as |pg|
329
+ }}
330
+ <PaginationView
331
+ @totalCount={{pg.count}}
332
+ @rangeStart={{pg.pageStart}}
333
+ @rangeEnd={{pg.pageEnd}}
334
+ @onNextPage={{this.nextPage}}
335
+ @onPreviousPage={{this.previousPage}}
336
+ @isFirstPage={{not pg.hasPreviousPage}}
337
+ @isLastPage={{not pg.hasNextPage}}
338
+ />
339
+ {{/let}}
340
+ {{/if}}
341
+
342
+ </mc.content>
343
+ </AuMainContainer>
344
+ </modal.Body>
345
+ </AuModal>
346
+ </template>
347
+ }
@@ -36,7 +36,7 @@ export default class FloatingDateContainer extends Component<Signature> {
36
36
 
37
37
  return NodeSelection.create(
38
38
  this.editorState.doc,
39
- this.selectedDateNode.pos - 1,
39
+ this.selectedDateNode.pos,
40
40
  );
41
41
  }
42
42
 
@@ -8,11 +8,12 @@ import {
8
8
  EditorView,
9
9
  Command,
10
10
  } from '@lblod/ember-rdfa-editor';
11
+ import type { AdministrativeUnit } from '../worship-plugin';
11
12
 
12
13
  export type LmbPluginConfig = {
13
14
  endpoint: string;
14
15
  defaultPeriod?: BestuursperiodeLabel;
15
- defaultAdminUnit?: string;
16
+ defaultAdminUnit?: AdministrativeUnit;
16
17
  };
17
18
 
18
19
  export function createPersonNode(controller: SayController, person: Person) {