@lblod/ember-rdfa-editor-lblod-plugins 28.1.0 → 28.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @lblod/ember-rdfa-editor-lblod-plugins
2
2
 
3
+ ## 28.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#553](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/553) [`50831e0`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/50831e01cfa5d8101c72e8cc3d98b304a8b86646) Thanks [@piemonkey](https://github.com/piemonkey)! - Add re-usable component for selecting a decision type
8
+
9
+ ### Patch Changes
10
+
11
+ - [#553](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/553) [`03f4ce0`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/03f4ce0864550cfc947f8ff7504aeac8b0300ae9) Thanks [@piemonkey](https://github.com/piemonkey)! - Fix undo behaviour when changing decision type
12
+
3
13
  ## 28.1.0
4
14
 
5
15
  ### Minor Changes
@@ -0,0 +1,68 @@
1
+ import Component from '@glimmer/component';
2
+ import { type BesluitType } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/fetchBesluitTypes';
3
+ import BesluitTypeSelect from '@lblod/ember-rdfa-editor-lblod-plugins/components/besluit-type-plugin/besluit-type-select';
4
+ import { type BesluitTypeInstance } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/besluit-type-instances';
5
+
6
+ interface Sig {
7
+ Args: {
8
+ types: BesluitType[];
9
+ selectedType?: BesluitTypeInstance;
10
+ setType: (selected: BesluitTypeInstance) => void;
11
+ };
12
+ Element: HTMLDivElement;
13
+ }
14
+
15
+ export default class BesluitTypePluginBesluitTypeSelectComponent extends Component<Sig> {
16
+ updateParentType = (selected: BesluitType) => {
17
+ this.args.setType({ parent: selected });
18
+ };
19
+
20
+ updateSubType = (selected: BesluitType) => {
21
+ if (this.args.selectedType) {
22
+ this.args.setType({
23
+ parent: this.args.selectedType.parent,
24
+ subType: selected,
25
+ });
26
+ }
27
+ };
28
+
29
+ updateSubSubType = (selected: BesluitType) => {
30
+ if (this.args.selectedType) {
31
+ this.args.setType({
32
+ parent: this.args.selectedType.parent,
33
+ subType: this.args.selectedType.subType,
34
+ subSubType: selected,
35
+ });
36
+ }
37
+ };
38
+
39
+ <template>
40
+ <div ...attributes>
41
+ <BesluitTypeSelect
42
+ @fieldNameTranslation='besluit-type-plugin.dt'
43
+ @besluitTypes={{@types}}
44
+ @onchange={{this.updateParentType}}
45
+ @selected={{@selectedType.parent}}
46
+ @showWarningWhenEmpty={{false}}
47
+ />
48
+ {{#if @selectedType.parent.subTypes.length}}
49
+ <BesluitTypeSelect
50
+ @fieldNameTranslation='besluit-type-plugin.sub-type'
51
+ @besluitTypes={{@selectedType.parent.subTypes}}
52
+ @onchange={{this.updateSubType}}
53
+ @selected={{@selectedType.subType}}
54
+ @showWarningWhenEmpty={{true}}
55
+ />
56
+ {{/if}}
57
+ {{#if @selectedType.subType.subTypes.length}}
58
+ <BesluitTypeSelect
59
+ @fieldNameTranslation='besluit-type-plugin.sub-type'
60
+ @besluitTypes={{@selectedType.subType.subTypes}}
61
+ @onchange={{this.updateSubSubType}}
62
+ @selected={{@selectedType.subSubType}}
63
+ @showWarningWhenEmpty={{true}}
64
+ />
65
+ {{/if}}
66
+ </div>
67
+ </template>
68
+ }
@@ -0,0 +1,80 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import t from 'ember-intl/helpers/t';
4
+ import PowerSelect from 'ember-power-select/components/power-select';
5
+ import { v4 as uuidv4 } from 'uuid';
6
+ import AuAlert from '@appuniversum/ember-appuniversum/components/au-alert';
7
+ import AuLabel from '@appuniversum/ember-appuniversum/components/au-label';
8
+ import { AlertTriangleIcon } from '@appuniversum/ember-appuniversum/components/icons/alert-triangle';
9
+ import { type BesluitType } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/fetchBesluitTypes';
10
+
11
+ interface Sig {
12
+ Args: {
13
+ fieldNameTranslation: string;
14
+ besluitTypes: BesluitType[];
15
+ selected?: BesluitType;
16
+ showWarningWhenEmpty: boolean;
17
+ onchange: (selected: BesluitType) => void;
18
+ };
19
+ Element: HTMLDivElement;
20
+ }
21
+
22
+ export default class BesluitTypePluginBesluitTypeSelectComponent extends Component<Sig> {
23
+ @tracked besluitTypes;
24
+
25
+ constructor(parent: unknown, args: Sig['Args']) {
26
+ super(parent, args);
27
+ this.besluitTypes = this.args.besluitTypes.sort((a, b) =>
28
+ a.label > b.label ? 1 : -1,
29
+ );
30
+ }
31
+
32
+ get showWarning() {
33
+ return this.args.showWarningWhenEmpty && !this.args.selected;
34
+ }
35
+
36
+ search = (term: string) => {
37
+ const lowerTerm = term.toLowerCase();
38
+ return this.args.besluitTypes.filter((besluitType) =>
39
+ besluitType.label.toLowerCase().includes(lowerTerm),
40
+ );
41
+ };
42
+
43
+ <template>
44
+ <div ...attributes>
45
+ {{#let (uuidv4) as |id|}}
46
+ <AuLabel for={{id}}>
47
+ {{t @fieldNameTranslation}}
48
+ </AuLabel>
49
+ <PowerSelect
50
+ id={{id}}
51
+ @renderInPlace={{true}}
52
+ @searchEnabled={{true}}
53
+ @searchMessage={{t 'besluit-type-plugin.search-message'}}
54
+ @noMatchesMessage={{t 'besluit-type-plugin.no-matches-message'}}
55
+ @search={{this.search}}
56
+ @options={{this.besluitTypes}}
57
+ @selected={{@selected}}
58
+ @onChange={{@onchange}}
59
+ as |besluitType|
60
+ >
61
+ {{besluitType.label}}
62
+ </PowerSelect>
63
+ {{/let}}
64
+ <p class='au-u-muted au-u-margin-tiny au-u-margin-bottom-small'>
65
+ {{@selected.definition}}
66
+ </p>
67
+ {{#if this.showWarning}}
68
+ <AuAlert
69
+ @icon={{AlertTriangleIcon}}
70
+ @title={{t 'besluit-type-plugin.alert-title'}}
71
+ @skin='warning'
72
+ @size='small'
73
+ class='au-u-margin-bottom-none au-u-margin-top-small'
74
+ >
75
+ <p>{{t 'besluit-type-plugin.alert-body'}}</p>
76
+ </AuAlert>
77
+ {{/if}}
78
+ </div>
79
+ </template>
80
+ }
@@ -0,0 +1,191 @@
1
+ import { tracked } from '@glimmer/tracking';
2
+ import Component from '@glimmer/component';
3
+ import { on } from '@ember/modifier';
4
+ // eslint-disable-next-line ember/no-at-ember-render-modifiers
5
+ import didUpdate from '@ember/render-modifiers/modifiers/did-update';
6
+ import { trackedFunction } from 'reactiveweb/function';
7
+ import t from 'ember-intl/helpers/t';
8
+ import AuPill from '@appuniversum/ember-appuniversum/components/au-pill';
9
+ import AuModal from '@appuniversum/ember-appuniversum/components/au-modal';
10
+ import AuLinkExternal from '@appuniversum/ember-appuniversum/components/au-link-external';
11
+ import AuAlert from '@appuniversum/ember-appuniversum/components/au-alert';
12
+ import { AlertTriangleIcon } from '@appuniversum/ember-appuniversum/components/icons/alert-triangle';
13
+ import { CrossIcon } from '@appuniversum/ember-appuniversum/components/icons/cross';
14
+ import { MailIcon } from '@appuniversum/ember-appuniversum/components/icons/mail';
15
+ import { CircleXIcon } from '@appuniversum/ember-appuniversum/components/icons/circle-x';
16
+ import { SayController } from '@lblod/ember-rdfa-editor';
17
+ import fetchBesluitTypes from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/fetchBesluitTypes';
18
+ import { BesluitTypePluginOptions } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin';
19
+ import { getCurrentBesluitRange } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-topic-plugin/utils/helpers';
20
+ import {
21
+ BesluitTypeInstance,
22
+ checkBesluitTypeInstance,
23
+ mostSpecificBesluitType,
24
+ } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/besluit-type-instances';
25
+ import BesluitTypeForm from '@lblod/ember-rdfa-editor-lblod-plugins/components/besluit-type-plugin/besluit-type-form';
26
+ import { setBesluitType } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/set-besluit-type';
27
+
28
+ type Args = {
29
+ controller: SayController;
30
+ options: BesluitTypePluginOptions;
31
+ };
32
+
33
+ export default class EditorPluginsToolbarDropdownComponent extends Component<Args> {
34
+ @tracked selectedTypeInstance?: BesluitTypeInstance;
35
+ @tracked cardExpanded = false;
36
+
37
+ get controller() {
38
+ return this.args.controller;
39
+ }
40
+
41
+ types = trackedFunction(this, async () => {
42
+ const types = await fetchBesluitTypes(
43
+ this.args.options.classificatieUri,
44
+ this.args.options.endpoint,
45
+ );
46
+ return types;
47
+ });
48
+
49
+ get currentBesluitRange() {
50
+ return getCurrentBesluitRange(this.controller);
51
+ }
52
+
53
+ get showCard() {
54
+ return !!this.currentBesluitRange;
55
+ }
56
+
57
+ get selectedType() {
58
+ return (
59
+ this.selectedTypeInstance &&
60
+ mostSpecificBesluitType(this.selectedTypeInstance)
61
+ );
62
+ }
63
+
64
+ toggleCard = () => {
65
+ this.cardExpanded = !this.cardExpanded;
66
+ };
67
+
68
+ setType = (type: BesluitTypeInstance) => {
69
+ this.selectedTypeInstance = type;
70
+ this.insertIfValid();
71
+ };
72
+
73
+ updateBesluitTypes = () => {
74
+ if (!this.types.isFinished || !this.currentBesluitRange) {
75
+ return;
76
+ }
77
+ if (!this.types.value) {
78
+ console.warn('Request for besluit types failed');
79
+ return;
80
+ }
81
+ const typeInstance = checkBesluitTypeInstance(
82
+ this.controller.mainEditorState,
83
+ this.types.value,
84
+ );
85
+ if (typeInstance) {
86
+ this.selectedTypeInstance = typeInstance;
87
+ this.cardExpanded = false;
88
+ } else {
89
+ this.cardExpanded = true;
90
+ }
91
+ };
92
+
93
+ insertIfValid() {
94
+ this.controller.doCommand((state, dispatch) => {
95
+ if (!this.selectedTypeInstance || !dispatch) {
96
+ return false;
97
+ }
98
+ const { result, transaction } = setBesluitType(
99
+ state,
100
+ this.selectedTypeInstance,
101
+ );
102
+ if (result.every((ok) => ok)) {
103
+ dispatch(transaction);
104
+ return true;
105
+ }
106
+ return false;
107
+ });
108
+ }
109
+
110
+ <template>
111
+ <div
112
+ {{didUpdate this.updateBesluitTypes @controller.mainEditorState}}
113
+ {{didUpdate this.updateBesluitTypes this.types.value}}
114
+ >
115
+ {{#if this.showCard}}
116
+ {{#if this.types.isError}}
117
+ <AuPill
118
+ @skin='error'
119
+ @icon={{CircleXIcon}}
120
+ @iconAlignment='left'
121
+ class='au-c-pill--link besluit-toolbar-pill'
122
+ {{on 'click' this.toggleCard}}
123
+ title={{t 'besluit-type-plugin.insert-dt'}}
124
+ >
125
+ {{t 'besluit-type-plugin.error-short'}}
126
+ </AuPill>
127
+ {{else}}
128
+ {{#if this.selectedType.label}}
129
+ <AuPill
130
+ @skin='link'
131
+ {{on 'click' this.toggleCard}}
132
+ title={{t 'besluit-type-plugin.insert-dt'}}
133
+ >
134
+ {{t 'besluit-type-plugin.dt'}}:
135
+ {{this.selectedType.label}}
136
+ </AuPill>
137
+ {{else}}
138
+ <AuPill
139
+ @icon={{AlertTriangleIcon}}
140
+ @iconAlignment='left'
141
+ @skin='link'
142
+ {{on 'click' this.toggleCard}}
143
+ title={{t 'besluit-type-plugin.insert-dt'}}
144
+ >
145
+ {{t 'besluit-type-plugin.insert-dt'}}
146
+ </AuPill>
147
+ {{/if}}
148
+ {{/if}}
149
+ {{/if}}
150
+ {{#if this.cardExpanded}}
151
+ <AuModal
152
+ @title={{t 'besluit-type-plugin.insert-dt'}}
153
+ @closeModal={{this.toggleCard}}
154
+ @modalOpen={{true}}
155
+ class='au-c-modal--overflow'
156
+ as |Modal|
157
+ >
158
+ <Modal.Body>
159
+ {{#if this.types.isError}}
160
+ <AuAlert
161
+ @title={{t 'besluit-type-plugin.error-title'}}
162
+ @skin='error'
163
+ @icon={{CrossIcon}}
164
+ >
165
+ <p>
166
+ {{t 'besluit-type-plugin.error-first-body'}}
167
+ {{! template-lint-disable no-bare-strings }}
168
+ <AuLinkExternal
169
+ href='mailto:gelinktnotuleren@vlaanderen.be'
170
+ @icon={{MailIcon}}
171
+ @iconAlignment='left'
172
+ >
173
+ GelinktNotuleren@vlaanderen.be
174
+ </AuLinkExternal>
175
+ {{! template-lint-enable no-bare-strings }}
176
+ {{t 'besluit-type-plugin.error-rest-body'}}
177
+ </p>
178
+ </AuAlert>
179
+ {{else if this.types.value}}
180
+ <BesluitTypeForm
181
+ @types={{this.types.value}}
182
+ @selectedType={{this.selectedTypeInstance}}
183
+ @setType={{this.setType}}
184
+ />
185
+ {{/if}}
186
+ </Modal.Body>
187
+ </AuModal>
188
+ {{/if}}
189
+ </div>
190
+ </template>
191
+ }
@@ -0,0 +1,100 @@
1
+ import { type EditorState } from '@lblod/ember-rdfa-editor';
2
+ import { getOutgoingTripleList } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/namespace';
3
+ import { getCurrentBesluitRange } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-topic-plugin/utils/helpers';
4
+ import { RDF } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
5
+ import { type BesluitType } from './fetchBesluitTypes';
6
+ import { type NamedNodeTriple } from '@lblod/ember-rdfa-editor/core/rdfa-processor';
7
+
8
+ export interface BesluitTypeInstance {
9
+ parent: BesluitType;
10
+ subType?: BesluitType;
11
+ subSubType?: BesluitType;
12
+ }
13
+
14
+ export function extractBesluitTypeUris(editorState: EditorState): string[] {
15
+ const besluitRange = getCurrentBesluitRange(editorState);
16
+ if (!besluitRange) {
17
+ return [];
18
+ }
19
+ return getOutgoingTripleList(besluitRange.node.attrs, RDF('type'))
20
+ .filter(
21
+ (type) =>
22
+ type.object.termType === 'NamedNode' &&
23
+ type.object.value.includes(
24
+ 'https://data.vlaanderen.be/id/concept/BesluitType/',
25
+ ),
26
+ )
27
+ .map((type: NamedNodeTriple) => type?.object.value);
28
+ }
29
+
30
+ /**
31
+ * Finds a decision type in the document and checks that it represents a valid decision type
32
+ * according to the list of types passed in.
33
+ */
34
+ export function checkBesluitTypeInstance(
35
+ editorState: EditorState,
36
+ types: BesluitType[],
37
+ ): BesluitTypeInstance | false {
38
+ const besluitTypes = extractBesluitTypeUris(editorState);
39
+
40
+ if (besluitTypes.length === 0) {
41
+ return false;
42
+ } else if (besluitTypes.length !== 1) {
43
+ console.warn(
44
+ "More than one decision type found for document, we don't support this and will just take the first one",
45
+ besluitTypes,
46
+ );
47
+ }
48
+ const typeHierarchy = findBesluitTypeHierarchy(types, besluitTypes[0]);
49
+
50
+ return (
51
+ !!typeHierarchy && {
52
+ parent: typeHierarchy[0],
53
+ subType: typeHierarchy[1],
54
+ subSubType: typeHierarchy[2],
55
+ }
56
+ );
57
+ }
58
+
59
+ function findBesluitTypeHierarchy(
60
+ types: BesluitType[],
61
+ uri: string,
62
+ ): BesluitType[] | undefined {
63
+ for (const type of types) {
64
+ if (type.uri === uri) {
65
+ return [type];
66
+ } else if (type.subTypes?.length) {
67
+ const subTypes = findBesluitTypeHierarchy(type.subTypes, uri);
68
+ if (subTypes) {
69
+ return [type, ...subTypes];
70
+ }
71
+ }
72
+ }
73
+ return;
74
+ }
75
+
76
+ /**
77
+ * Checks that the passed type instance has all of the necessary sub-types selected (and no more)
78
+ */
79
+ export function isValidTypeChoice({
80
+ parent,
81
+ subType,
82
+ subSubType,
83
+ }: BesluitTypeInstance) {
84
+ if (!parent.subTypes || parent.subTypes.length === 0) {
85
+ return !subType && !subSubType;
86
+ }
87
+ if (!subType) {
88
+ return false;
89
+ }
90
+ if (!subType.subTypes || subType.subTypes.length === 0) {
91
+ return !subSubType;
92
+ }
93
+ return !!subSubType;
94
+ }
95
+
96
+ export function mostSpecificBesluitType(
97
+ typeInstance: BesluitTypeInstance,
98
+ ): BesluitType {
99
+ return typeInstance.subSubType ?? typeInstance.subType ?? typeInstance.parent;
100
+ }
@@ -0,0 +1,57 @@
1
+ import { type EditorState } from '@lblod/ember-rdfa-editor';
2
+ import {
3
+ transactionCombinator,
4
+ type TransactionCombinatorResult,
5
+ } from '@lblod/ember-rdfa-editor/utils/transaction-utils';
6
+ import {
7
+ addPropertyToNode,
8
+ removePropertyFromNode,
9
+ } from '@lblod/ember-rdfa-editor/utils/rdfa-utils';
10
+ import { sayDataFactory } from '@lblod/ember-rdfa-editor/core/say-data-factory';
11
+ import { getCurrentBesluitURI } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-topic-plugin/utils/helpers';
12
+ import {
13
+ extractBesluitTypeUris,
14
+ isValidTypeChoice,
15
+ mostSpecificBesluitType,
16
+ type BesluitTypeInstance,
17
+ } from './besluit-type-instances';
18
+ import { RDF } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
19
+
20
+ export function setBesluitType(
21
+ initialState: EditorState,
22
+ typeInstance: BesluitTypeInstance,
23
+ ): TransactionCombinatorResult<boolean> {
24
+ const transaction = initialState.tr;
25
+
26
+ const resource = getCurrentBesluitURI(initialState);
27
+ if (!resource || !isValidTypeChoice(typeInstance)) {
28
+ return {
29
+ result: [false],
30
+ initialState,
31
+ transaction,
32
+ transactions: [transaction],
33
+ };
34
+ }
35
+ const existingTypeUris = extractBesluitTypeUris(initialState);
36
+ const monads = existingTypeUris.map((existingUri) => {
37
+ return removePropertyFromNode({
38
+ resource,
39
+ property: {
40
+ predicate: RDF('type').full,
41
+ object: sayDataFactory.namedNode(existingUri),
42
+ },
43
+ });
44
+ });
45
+ monads.push(
46
+ addPropertyToNode({
47
+ resource,
48
+ property: {
49
+ predicate: RDF('type').full,
50
+ object: sayDataFactory.namedNode(
51
+ mostSpecificBesluitType(typeInstance).uri,
52
+ ),
53
+ },
54
+ }),
55
+ );
56
+ return transactionCombinator<boolean>(initialState)(monads);
57
+ }
@@ -0,0 +1 @@
1
+ export { default } from '@lblod/ember-rdfa-editor-lblod-plugins/components/besluit-type-plugin/besluit-type-form';
@@ -0,0 +1,17 @@
1
+ import Component from '@glimmer/component';
2
+ import { type BesluitType } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/fetchBesluitTypes';
3
+ import { type BesluitTypeInstance } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/besluit-type-instances';
4
+ interface Sig {
5
+ Args: {
6
+ types: BesluitType[];
7
+ selectedType?: BesluitTypeInstance;
8
+ setType: (selected: BesluitTypeInstance) => void;
9
+ };
10
+ Element: HTMLDivElement;
11
+ }
12
+ export default class BesluitTypePluginBesluitTypeSelectComponent extends Component<Sig> {
13
+ updateParentType: (selected: BesluitType) => void;
14
+ updateSubType: (selected: BesluitType) => void;
15
+ updateSubSubType: (selected: BesluitType) => void;
16
+ }
17
+ export {};
@@ -1,12 +1,19 @@
1
1
  import Component from '@glimmer/component';
2
- import { BesluitType } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/fetchBesluitTypes';
3
- type Args = {
4
- besluitTypes: BesluitType[];
5
- };
6
- export default class BesluitTypePluginBesluitTypeSelectComponent extends Component<Args> {
7
- AlertTriangleIcon: import("@ember/component/template-only").TOC<import("@appuniversum/ember-appuniversum/components/icons/alert-triangle").AlertTriangleIconSignature>;
2
+ import { type BesluitType } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/fetchBesluitTypes';
3
+ interface Sig {
4
+ Args: {
5
+ fieldNameTranslation: string;
6
+ besluitTypes: BesluitType[];
7
+ selected?: BesluitType;
8
+ showWarningWhenEmpty: boolean;
9
+ onchange: (selected: BesluitType) => void;
10
+ };
11
+ Element: HTMLDivElement;
12
+ }
13
+ export default class BesluitTypePluginBesluitTypeSelectComponent extends Component<Sig> {
8
14
  besluitTypes: BesluitType[];
9
- constructor(parent: unknown, args: Args);
10
- search(term: string): BesluitType[];
15
+ constructor(parent: unknown, args: Sig['Args']);
16
+ get showWarning(): boolean;
17
+ search: (term: string) => BesluitType[];
11
18
  }
12
19
  export {};
@@ -1,42 +1,22 @@
1
1
  import Component from '@glimmer/component';
2
2
  import { SayController } from '@lblod/ember-rdfa-editor';
3
- import { BesluitType } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/fetchBesluitTypes';
4
3
  import { BesluitTypePluginOptions } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin';
4
+ import { BesluitTypeInstance } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/besluit-type-instances';
5
5
  type Args = {
6
6
  controller: SayController;
7
7
  options: BesluitTypePluginOptions;
8
8
  };
9
9
  export default class EditorPluginsToolbarDropdownComponent extends Component<Args> {
10
- CircleIcon: import("@ember/component/template-only").TOC<import("@appuniversum/ember-appuniversum/components/icons/circle-x").CircleXIconSignature>;
11
- MailIcon: import("@ember/component/template-only").TOC<import("@appuniversum/ember-appuniversum/components/icons/mail").MailIconSignature>;
12
- CrossIcon: import("@ember/component/template-only").TOC<import("@appuniversum/ember-appuniversum/components/icons/cross").CrossIconSignature>;
13
- AlertTriangleIcon: import("@ember/component/template-only").TOC<import("@appuniversum/ember-appuniversum/components/icons/alert-triangle").AlertTriangleIconSignature>;
14
- /**
15
- * Actual besluit type selected
16
- * @property besluitType
17
- * @type BesluitType
18
- * @private
19
- */
20
- besluitType?: BesluitType;
21
- previousBesluitType?: string;
22
- besluit?: BesluitType;
23
- subBesluit?: BesluitType;
24
- subSubBesluit?: BesluitType;
10
+ selectedTypeInstance?: BesluitTypeInstance;
25
11
  cardExpanded: boolean;
26
- constructor(parent: unknown, args: Args);
27
12
  get controller(): SayController;
28
- get doc(): import("prosemirror-model").Node;
29
- types: import("reactiveweb/function").State<Promise<BesluitType[]>>;
13
+ types: import("reactiveweb/function").State<Promise<import("@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/fetchBesluitTypes").BesluitType[]>>;
30
14
  get currentBesluitRange(): import("@lblod/ember-rdfa-editor/plugins/datastore").ElementPNode | undefined;
31
- get currentBesluitURI(): string | undefined;
32
15
  get showCard(): boolean;
33
- updateBesluitTypes(): void;
34
- updateBesluitType(selected: BesluitType): void;
35
- updateBesluitSubType(selected: BesluitType): void;
36
- updateBesluitSubSubType(selected: BesluitType): void;
37
- findBesluitTypeParent(besluitType?: BesluitType, array?: BesluitType[] | null, parent?: BesluitType): BesluitType | undefined;
38
- findBesluitTypeByURI(uri: string, types?: BesluitType[] | null): BesluitType | undefined;
39
- insert(): void;
40
- toggleCard(): void;
16
+ get selectedType(): import("@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/fetchBesluitTypes").BesluitType | undefined;
17
+ toggleCard: () => void;
18
+ setType: (type: BesluitTypeInstance) => void;
19
+ updateBesluitTypes: () => void;
20
+ insertIfValid(): void;
41
21
  }
42
22
  export {};
@@ -0,0 +1,18 @@
1
+ import { type EditorState } from '@lblod/ember-rdfa-editor';
2
+ import { type BesluitType } from './fetchBesluitTypes';
3
+ export interface BesluitTypeInstance {
4
+ parent: BesluitType;
5
+ subType?: BesluitType;
6
+ subSubType?: BesluitType;
7
+ }
8
+ export declare function extractBesluitTypeUris(editorState: EditorState): string[];
9
+ /**
10
+ * Finds a decision type in the document and checks that it represents a valid decision type
11
+ * according to the list of types passed in.
12
+ */
13
+ export declare function checkBesluitTypeInstance(editorState: EditorState, types: BesluitType[]): BesluitTypeInstance | false;
14
+ /**
15
+ * Checks that the passed type instance has all of the necessary sub-types selected (and no more)
16
+ */
17
+ export declare function isValidTypeChoice({ parent, subType, subSubType, }: BesluitTypeInstance): boolean;
18
+ export declare function mostSpecificBesluitType(typeInstance: BesluitTypeInstance): BesluitType;
@@ -0,0 +1,4 @@
1
+ import { type EditorState } from '@lblod/ember-rdfa-editor';
2
+ import { type TransactionCombinatorResult } from '@lblod/ember-rdfa-editor/utils/transaction-utils';
3
+ import { type BesluitTypeInstance } from './besluit-type-instances';
4
+ export declare function setBesluitType(initialState: EditorState, typeInstance: BesluitTypeInstance): TransactionCombinatorResult<boolean>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lblod/ember-rdfa-editor-lblod-plugins",
3
- "version": "28.1.0",
3
+ "version": "28.2.0",
4
4
  "description": "Ember addon providing lblod specific plugins for the ember-rdfa-editor",
5
5
  "keywords": [
6
6
  "ember-addon",
@@ -89,6 +89,7 @@ citaten-plugin:
89
89
 
90
90
  besluit-type-plugin:
91
91
  dt: 'Decision type'
92
+ sub-type: 'Sub type'
92
93
  insert-dt: 'Select a decision type'
93
94
  search-message: 'Type to search'
94
95
  no-matches-message: 'No results'
@@ -89,6 +89,7 @@ citaten-plugin:
89
89
 
90
90
  besluit-type-plugin:
91
91
  dt: 'Besluittype'
92
+ sub-type: 'Subtype'
92
93
  insert-dt: 'Selecteer een besluittype'
93
94
  search-message: 'Typ om te zoeken'
94
95
  no-matches-message: 'Geen resultaten'
@@ -1,32 +0,0 @@
1
- {{! @glint-nocheck: not typesafe yet }}
2
- <div ...attributes>
3
- <PowerSelect
4
- @renderInPlace={{true}}
5
- @searchEnabled={{true}}
6
- @searchMessage={{t 'besluit-type-plugin.search-message'}}
7
- @noMatchesMessage={{t 'besluit-type-plugin.no-matches-message'}}
8
- @search={{this.search}}
9
- @options={{this.besluitTypes}}
10
- @selected={{@selected}}
11
- @onChange={{@onchange}}
12
- as |besluitType|
13
- >
14
- {{besluitType.label}}
15
- </PowerSelect>
16
- <p class='au-u-muted au-u-margin-tiny au-u-margin-bottom-small'>
17
- {{@selected.definition}}
18
- </p>
19
- {{#if @showWarningWhenEmpty}}
20
- {{#unless @selected}}
21
- <AuAlert
22
- @icon={{this.AlertTriangleIcon}}
23
- @title={{t 'besluit-type-plugin.alert-title'}}
24
- @skin='warning'
25
- @size='small'
26
- class='au-u-margin-bottom-none au-u-margin-top-small'
27
- >
28
- <p>{{t 'besluit-type-plugin.alert-body'}}</p>
29
- </AuAlert>
30
- {{/unless}}
31
- {{/if}}
32
- </div>
@@ -1,29 +0,0 @@
1
- import Component from '@glimmer/component';
2
- import { action } from '@ember/object';
3
- import { tracked } from '@glimmer/tracking';
4
- import { BesluitType } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/fetchBesluitTypes';
5
- import { AlertTriangleIcon } from '@appuniversum/ember-appuniversum/components/icons/alert-triangle';
6
-
7
- type Args = {
8
- besluitTypes: BesluitType[];
9
- };
10
-
11
- export default class BesluitTypePluginBesluitTypeSelectComponent extends Component<Args> {
12
- AlertTriangleIcon = AlertTriangleIcon;
13
-
14
- @tracked besluitTypes;
15
- constructor(parent: unknown, args: Args) {
16
- super(parent, args);
17
- this.besluitTypes = this.args.besluitTypes.sort((a, b) =>
18
- a.label > b.label ? 1 : -1,
19
- );
20
- }
21
-
22
- @action
23
- search(term: string) {
24
- const lowerTerm = term.toLowerCase();
25
- return this.args.besluitTypes.filter((besluitType) =>
26
- besluitType.label.toLowerCase().includes(lowerTerm),
27
- );
28
- }
29
- }
@@ -1,102 +0,0 @@
1
- {{! @glint-nocheck: not typesafe yet }}
2
- <div
3
- {{did-update this.updateBesluitTypes @controller.mainEditorState}}
4
- {{did-update this.updateBesluitTypes this.types.value}}
5
- >
6
- {{#if this.showCard}}
7
- {{#if this.types.isError}}
8
- <AuPill
9
- @skin='error'
10
- @icon={{this.CircleXIcon}}
11
- @iconAlignment='left'
12
- class='au-c-pill--link besluit-toolbar-pill'
13
- {{on 'click' this.toggleCard}}
14
- title={{t 'besluit-type-plugin.insert-dt'}}
15
- >
16
- {{t 'besluit-type-plugin.error-short'}}
17
- </AuPill>
18
- {{else}}
19
- {{#if this.besluit.label}}
20
- <AuPill
21
- @skin='link'
22
- {{on 'click' this.toggleCard}}
23
- title={{t 'besluit-type-plugin.insert-dt'}}
24
- >
25
- {{t 'besluit-type-plugin.dt'}}:
26
- {{this.besluit.label}}
27
- </AuPill>
28
- {{else}}
29
- <AuPill
30
- @icon={{this.AlertTriangleIcon}}
31
- @iconAlignment='left'
32
- @skin='link'
33
- {{on 'click' this.toggleCard}}
34
- title={{t 'besluit-type-plugin.insert-dt'}}
35
- >
36
- {{t 'besluit-type-plugin.insert-dt'}}
37
- </AuPill>
38
- {{/if}}
39
- {{/if}}
40
- {{/if}}
41
- {{#if this.cardExpanded}}
42
- <AuModal
43
- @title={{t 'besluit-type-plugin.insert-dt'}}
44
- @closeModal={{this.toggleCard}}
45
- @modalOpen={{true}}
46
- @size='default'
47
- class='au-c-modal--overflow'
48
- as |Modal|
49
- >
50
- <Modal.Body>
51
- {{#if this.types.isError}}
52
- <AuAlert
53
- @title={{t 'besluit-type-plugin.error-title'}}
54
- @skin='error'
55
- @icon={{this.CrossIcon}}
56
- >
57
- <p>
58
- {{t 'besluit-type-plugin.error-first-body'}}
59
- {{! template-lint-disable no-bare-strings }}
60
- <AuLinkExternal
61
- href='mailto:gelinktnotuleren@vlaanderen.be'
62
- @icon={{this.MailIcon}}
63
- @iconAlignment='left'
64
- >
65
- GelinktNotuleren@vlaanderen.be
66
- </AuLinkExternal>
67
- {{! template-lint-enable no-bare-strings }}
68
- {{t 'besluit-type-plugin.error-rest-body'}}
69
- </p>
70
- </AuAlert>
71
- {{else}}
72
- <BesluitTypePlugin::BesluitTypeSelect
73
- @besluitTypes={{this.types.value}}
74
- @onchange={{this.updateBesluitType}}
75
- @selected={{this.besluit}}
76
- @showWarningWhenEmpty={{false}}
77
- />
78
- {{#if this.besluit.subTypes.length}}
79
- <AuHr @size='large' />
80
- <BesluitTypePlugin::BesluitTypeSelect
81
- @besluitTypes={{this.besluit.subTypes}}
82
- @onchange={{this.updateBesluitSubType}}
83
- @selected={{this.subBesluit}}
84
- @showWarningWhenEmpty={{true}}
85
- class='au-u-padding-left au-u-padding-right'
86
- />
87
- {{/if}}
88
- {{#if this.subBesluit.subTypes.length}}
89
- <AuHr @size='large' />
90
- <BesluitTypePlugin::BesluitTypeSelect
91
- @besluitTypes={{this.subBesluit.subTypes}}
92
- @onchange={{this.updateBesluitSubSubType}}
93
- @selected={{this.subSubBesluit}}
94
- @showWarningWhenEmpty={{true}}
95
- class='au-u-padding-left au-u-padding-right'
96
- />
97
- {{/if}}
98
- {{/if}}
99
- </Modal.Body>
100
- </AuModal>
101
- {{/if}}
102
- </div>
@@ -1,223 +0,0 @@
1
- import { tracked } from '@glimmer/tracking';
2
- import Component from '@glimmer/component';
3
- import { action } from '@ember/object';
4
- import { addProperty, removeProperty } from '@lblod/ember-rdfa-editor/commands';
5
- import { SayController } from '@lblod/ember-rdfa-editor';
6
- import { sayDataFactory } from '@lblod/ember-rdfa-editor/core/say-data-factory';
7
- import fetchBesluitTypes, {
8
- BesluitType,
9
- } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin/utils/fetchBesluitTypes';
10
- import { trackedFunction } from 'reactiveweb/function';
11
- import { BesluitTypePluginOptions } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-type-plugin';
12
- import { RDF } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
13
- import { getOutgoingTripleList } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/namespace';
14
- import { AlertTriangleIcon } from '@appuniversum/ember-appuniversum/components/icons/alert-triangle';
15
- import { CrossIcon } from '@appuniversum/ember-appuniversum/components/icons/cross';
16
- import { MailIcon } from '@appuniversum/ember-appuniversum/components/icons/mail';
17
- import { CircleXIcon } from '@appuniversum/ember-appuniversum/components/icons/circle-x';
18
- import {
19
- getCurrentBesluitRange,
20
- getCurrentBesluitURI,
21
- } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/besluit-topic-plugin/utils/helpers';
22
-
23
- type Args = {
24
- controller: SayController;
25
- options: BesluitTypePluginOptions;
26
- };
27
-
28
- export default class EditorPluginsToolbarDropdownComponent extends Component<Args> {
29
- CircleIcon = CircleXIcon;
30
- MailIcon = MailIcon;
31
- CrossIcon = CrossIcon;
32
- AlertTriangleIcon = AlertTriangleIcon;
33
- /**
34
- * Actual besluit type selected
35
- * @property besluitType
36
- * @type BesluitType
37
- * @private
38
- */
39
- @tracked besluitType?: BesluitType;
40
- @tracked previousBesluitType?: string;
41
-
42
- //used to update selections since the other vars dont seem to work in octane
43
- @tracked besluit?: BesluitType;
44
- @tracked subBesluit?: BesluitType;
45
- @tracked subSubBesluit?: BesluitType;
46
-
47
- @tracked cardExpanded = false;
48
-
49
- constructor(parent: unknown, args: Args) {
50
- super(parent, args);
51
- }
52
-
53
- get controller() {
54
- return this.args.controller;
55
- }
56
-
57
- get doc() {
58
- return this.controller.mainEditorState.doc;
59
- }
60
-
61
- types = trackedFunction(this, async () => {
62
- const types = await fetchBesluitTypes(
63
- this.args.options.classificatieUri,
64
- this.args.options.endpoint,
65
- );
66
- return types;
67
- });
68
-
69
- get currentBesluitRange() {
70
- return getCurrentBesluitRange(this.controller);
71
- }
72
-
73
- get currentBesluitURI() {
74
- return getCurrentBesluitURI(this.controller);
75
- }
76
-
77
- get showCard() {
78
- return !!this.currentBesluitRange;
79
- }
80
-
81
- @action
82
- updateBesluitTypes() {
83
- if (!this.types.isFinished || !this.currentBesluitRange) {
84
- return;
85
- }
86
- if (!this.types.value) {
87
- console.warn('Request for besluit types failed');
88
- return;
89
- }
90
- const besluit = this.currentBesluitRange;
91
- const besluitTypes = getOutgoingTripleList(besluit.node.attrs, RDF('type'));
92
- const besluitTypeRelevant = besluitTypes.find(
93
- (type) =>
94
- type.object.termType === 'NamedNode' &&
95
- type.object.value.includes(
96
- 'https://data.vlaanderen.be/id/concept/BesluitType/',
97
- ),
98
- )?.object.value;
99
-
100
- if (besluitTypeRelevant) {
101
- this.previousBesluitType = besluitTypeRelevant;
102
- const besluitType = this.findBesluitTypeByURI(besluitTypeRelevant);
103
-
104
- const firstAncestor = this.findBesluitTypeParent(besluitType);
105
- const secondAncestor = this.findBesluitTypeParent(firstAncestor);
106
- if (firstAncestor && secondAncestor) {
107
- this.besluit = secondAncestor;
108
- this.subBesluit = firstAncestor;
109
- this.subSubBesluit = besluitType;
110
- } else if (firstAncestor) {
111
- this.besluit = firstAncestor;
112
- this.subBesluit = besluitType;
113
- this.subSubBesluit = undefined;
114
- } else {
115
- this.besluit = besluitType;
116
- this.subBesluit = undefined;
117
- this.subSubBesluit = undefined;
118
- }
119
- this.cardExpanded = false;
120
- } else {
121
- this.cardExpanded = true;
122
- this.besluit = undefined;
123
- this.subBesluit = undefined;
124
- this.subSubBesluit = undefined;
125
- }
126
- }
127
-
128
- @action
129
- updateBesluitType(selected: BesluitType) {
130
- this.besluit = selected;
131
- this.besluitType = selected;
132
- this.subBesluit = undefined;
133
- this.subSubBesluit = undefined;
134
- if (!selected.subTypes || !selected.subTypes.length) this.insert();
135
- }
136
-
137
- @action
138
- updateBesluitSubType(selected: BesluitType) {
139
- this.subBesluit = selected;
140
- this.besluitType = selected;
141
- this.subSubBesluit = undefined;
142
- if (!selected.subTypes || !selected.subTypes.length) this.insert();
143
- }
144
-
145
- @action
146
- updateBesluitSubSubType(selected: BesluitType) {
147
- this.subSubBesluit = selected;
148
- this.besluitType = selected;
149
- if (!selected.subTypes || !selected.subTypes.length) this.insert();
150
- }
151
-
152
- findBesluitTypeParent(
153
- besluitType?: BesluitType,
154
- array: BesluitType[] | null = this.types.value,
155
- parent?: BesluitType,
156
- ): BesluitType | undefined {
157
- if (!besluitType || !array) {
158
- return;
159
- }
160
- for (const type of array) {
161
- if (type == besluitType) {
162
- return parent;
163
- } else if (type.subTypes?.length) {
164
- parent = type;
165
- return this.findBesluitTypeParent(besluitType, type.subTypes, parent);
166
- }
167
- }
168
- return;
169
- }
170
-
171
- findBesluitTypeByURI(
172
- uri: string,
173
- types = this.types.value,
174
- ): BesluitType | undefined {
175
- if (uri && types) {
176
- for (const besluitType of types) {
177
- if (besluitType.uri === uri) {
178
- return besluitType;
179
- } else if (besluitType.subTypes && besluitType.subTypes.length) {
180
- const subType = this.findBesluitTypeByURI(uri, besluitType.subTypes);
181
- if (subType) {
182
- return subType;
183
- }
184
- }
185
- }
186
- }
187
- return;
188
- }
189
-
190
- insert() {
191
- const resource = this.currentBesluitURI;
192
- if (this.besluitType && resource) {
193
- this.cardExpanded = false;
194
- if (this.previousBesluitType) {
195
- this.controller.doCommand(
196
- removeProperty({
197
- resource,
198
- property: {
199
- predicate: RDF('type').full,
200
- object: sayDataFactory.namedNode(this.previousBesluitType),
201
- },
202
- }),
203
- { view: this.controller.mainEditorView },
204
- );
205
- }
206
- this.controller.doCommand(
207
- addProperty({
208
- resource,
209
- property: {
210
- predicate: RDF('type').full,
211
- object: sayDataFactory.namedNode(this.besluitType.uri),
212
- },
213
- }),
214
- { view: this.controller.mainEditorView },
215
- );
216
- }
217
- }
218
-
219
- @action
220
- toggleCard() {
221
- this.cardExpanded = !this.cardExpanded;
222
- }
223
- }