@lblod/ember-rdfa-editor-lblod-plugins 7.0.0 → 8.0.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 (43) hide show
  1. package/CHANGELOG.md +35 -2
  2. package/README.md +34 -10
  3. package/addon/components/decision-plugin/decision-plugin-card.hbs +22 -6
  4. package/addon/components/decision-plugin/decision-plugin-card.ts +37 -18
  5. package/addon/components/generic-rdfa-variable/insert-menu.hbs +30 -0
  6. package/addon/components/generic-rdfa-variable/insert-menu.ts +97 -0
  7. package/addon/components/rdfa-date-plugin/date.hbs +9 -0
  8. package/addon/components/rdfa-date-plugin/date.ts +55 -0
  9. package/addon/components/variable-plugin/insert-variable-card.hbs +19 -0
  10. package/addon/components/variable-plugin/insert-variable-card.ts +26 -5
  11. package/addon/components/variable-plugin/number-settings.hbs +31 -0
  12. package/addon/components/variable-plugin/template-variable-card.hbs +3 -0
  13. package/addon/components/variable-plugin/template-variable-card.ts +3 -0
  14. package/addon/components/variable-plugin/variable.hbs +7 -1
  15. package/addon/components/variable-plugin/variable.ts +39 -0
  16. package/addon/plugins/decision-plugin/commands/insert-motivation.ts +59 -6
  17. package/addon/plugins/decision-plugin/utils/validation-rules.ts +11 -0
  18. package/addon/plugins/rdfa-date-plugin/nodes/date.ts +150 -114
  19. package/addon/plugins/validation/README.md +1 -1
  20. package/addon/plugins/validation/index.ts +2 -17
  21. package/addon/plugins/variable-plugin/nodes.ts +68 -3
  22. package/addon/plugins/variable-plugin/utils/constants.ts +29 -15
  23. package/addon/plugins/variable-plugin/utils/fetch-data.ts +9 -5
  24. package/app/components/generic-rdfa-variable/insert-menu.js +1 -0
  25. package/app/components/rdfa-date-plugin/date.js +1 -0
  26. package/app/components/variable-plugin/number-settings.js +1 -0
  27. package/app/styles/besluit-plugin.scss +1 -1
  28. package/app/styles/generic-rdfa-variable.scss +20 -0
  29. package/app/styles/variable-plugin.scss +31 -0
  30. package/components/decision-plugin/decision-plugin-card.d.ts +4 -2
  31. package/components/generic-rdfa-variable/insert-menu.d.ts +17 -0
  32. package/components/rdfa-date-plugin/date.d.ts +16 -0
  33. package/components/variable-plugin/insert-variable-card.d.ts +4 -0
  34. package/components/variable-plugin/template-variable-card.d.ts +1 -0
  35. package/components/variable-plugin/variable.d.ts +11 -0
  36. package/package.json +8 -4
  37. package/plugins/decision-plugin/commands/insert-motivation.d.ts +3 -2
  38. package/plugins/decision-plugin/utils/validation-rules.d.ts +10 -0
  39. package/plugins/rdfa-date-plugin/nodes/date.d.ts +2 -3
  40. package/plugins/variable-plugin/nodes.d.ts +22 -1
  41. package/plugins/variable-plugin/utils/constants.d.ts +11 -2
  42. package/translations/en-US.yaml +29 -1
  43. package/translations/nl-BE.yaml +29 -1
@@ -8,16 +8,17 @@ import { v4 as uuid } from 'uuid';
8
8
  import { isNone } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/option';
9
9
  import { transactionCompliesWithShapes } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/validation/utils/transaction-complies-with-shapes';
10
10
  import { findInsertionPosInAncestorOfType } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/find-insertion-pos-in-ancestor-of-type';
11
+ import IntlService from 'ember-intl/services/intl';
11
12
 
12
13
  interface InsertMotivationArgs {
13
- placeholderText?: string;
14
+ intl: IntlService;
14
15
  validateShapes?: Set<string>;
15
16
  }
16
17
 
17
18
  export default function insertMotivation({
19
+ intl,
18
20
  validateShapes,
19
- placeholderText = 'Insert motivation',
20
- }: InsertMotivationArgs = {}): Command {
21
+ }: InsertMotivationArgs): Command {
21
22
  return function (state: EditorState, dispatch?: (tr: Transaction) => void) {
22
23
  const { selection, schema } = state;
23
24
  const nodeToInsert = schema.node('motivering', { __rdfaId: uuid() }, [
@@ -25,9 +26,63 @@ export default function insertMotivation({
25
26
  'paragraph',
26
27
  null,
27
28
  schema.node('placeholder', {
28
- placeholderText,
29
+ placeholderText: intl.t('besluit-plugin.placeholder.government-body'),
29
30
  })
30
31
  ),
32
+ schema.node(
33
+ 'heading',
34
+ {
35
+ level: 5,
36
+ },
37
+ [schema.text(intl.t('besluit-plugin.text.authority'))]
38
+ ),
39
+ schema.node('bullet_list', null, [
40
+ schema.node('list_item', null, [
41
+ schema.node('paragraph', null, [
42
+ schema.node('placeholder', {
43
+ placeholderText: intl.t(
44
+ 'besluit-plugin.placeholder.legal-jurisdiction'
45
+ ),
46
+ }),
47
+ ]),
48
+ ]),
49
+ ]),
50
+ schema.node(
51
+ 'heading',
52
+ {
53
+ level: 5,
54
+ },
55
+ [schema.text(intl.t('besluit-plugin.text.legal-context'))]
56
+ ),
57
+ schema.node('bullet_list', null, [
58
+ schema.node('list_item', null, [
59
+ schema.node('paragraph', null, [
60
+ schema.node('placeholder', {
61
+ placeholderText: intl.t(
62
+ 'besluit-plugin.placeholder.insert-legal-context'
63
+ ),
64
+ }),
65
+ ]),
66
+ ]),
67
+ ]),
68
+ schema.node(
69
+ 'heading',
70
+ {
71
+ level: 5,
72
+ },
73
+ [schema.text(intl.t('besluit-plugin.text.factual-context'))]
74
+ ),
75
+ schema.node('bullet_list', null, [
76
+ schema.node('list_item', null, [
77
+ schema.node('paragraph', null, [
78
+ schema.node('placeholder', {
79
+ placeholderText: intl.t(
80
+ 'besluit-plugin.placeholder.insert-factual-context'
81
+ ),
82
+ }),
83
+ ]),
84
+ ]),
85
+ ]),
31
86
  ]);
32
87
  // how the offset between the insertion point and the point where the cursor should end up
33
88
  const cursorOffset = 2;
@@ -47,8 +102,6 @@ export default function insertMotivation({
47
102
  return false;
48
103
  }
49
104
  if (dispatch) {
50
- console.log('inserts in ', insertionPos);
51
- console.log('selection on ', insertionPos + cursorOffset);
52
105
  const selectionPos = tr.doc.resolve(insertionPos + cursorOffset);
53
106
  // const targetPos = tr.doc.resolve(insertionPos + cursorOffset + 1);
54
107
  // TODO figure out why I cant just set a nodeSelection here
@@ -0,0 +1,11 @@
1
+ import { Schema } from '@lblod/ember-rdfa-editor';
2
+
3
+ export const atLeastOneArticleContainer = (schema: Schema) => ({
4
+ name: 'at-least-one-article-container',
5
+ focusNodeType: schema.nodes.besluit,
6
+ path: ['article_container'],
7
+ message: 'Document must contain at least one article container.',
8
+ constraints: {
9
+ minCount: 1,
10
+ },
11
+ });
@@ -1,4 +1,8 @@
1
- import { NodeSpec } from '@lblod/ember-rdfa-editor';
1
+ import {
2
+ createEmberNodeSpec,
3
+ createEmberNodeView,
4
+ EmberNodeConfig,
5
+ } from '@lblod/ember-rdfa-editor/utils/ember-node';
2
6
  import {
3
7
  DCT,
4
8
  EXT,
@@ -7,128 +11,160 @@ import {
7
11
  import { hasRDFaAttribute } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/namespace';
8
12
  import { DateOptions } from '..';
9
13
  import { formatDate, validateDateFormat } from '../utils';
14
+ import { PNode } from '@lblod/ember-rdfa-editor';
10
15
 
11
- const date: (options: DateOptions) => NodeSpec = (options) => {
12
- return {
13
- group: 'inline',
14
- inline: true,
15
- attrs: {
16
- mappingResource: {
17
- default: null,
18
- },
19
- value: {},
20
- format: {
21
- default: options.formats[0].dateFormat,
22
- },
23
- onlyDate: {
24
- default: true,
25
- },
26
- custom: {
27
- default: false,
28
- },
16
+ const emberNodeConfig = (options: DateOptions): EmberNodeConfig => ({
17
+ name: 'date',
18
+ group: 'inline',
19
+ componentPath: 'rdfa-date-plugin/date',
20
+ inline: true,
21
+ selectable: true,
22
+ draggable: false,
23
+ atom: true,
24
+ defining: false,
25
+ options,
26
+ attrs: {
27
+ mappingResource: {
28
+ default: null,
29
29
  },
30
- selectable: true,
31
- leafText: (node) => {
32
- const { value, onlyDate, format } = node.attrs;
33
- const humanReadableDate = value
34
- ? formatDate(new Date(value), format)
35
- : onlyDate
36
- ? options.placeholder.insertDate
37
- : options.placeholder.insertDateTime;
38
- return humanReadableDate;
30
+ humanReadableDate: {
31
+ default: options.placeholder.insertDate,
39
32
  },
40
- toDOM: (node) => {
41
- const { value, onlyDate, format, mappingResource, custom } = node.attrs;
42
- const datatype = onlyDate ? XSD('date') : XSD('dateTime');
43
- let humanReadableDate: string;
44
- if (value) {
45
- if (validateDateFormat(format).type === 'ok') {
46
- humanReadableDate = formatDate(new Date(value), format);
47
- } else {
48
- humanReadableDate = 'Ongeldig formaat';
49
- }
50
- } else {
51
- humanReadableDate = onlyDate
52
- ? options.placeholder.insertDate
53
- : options.placeholder.insertDateTime;
54
- }
55
- const dateAttrs = {
56
- datatype: datatype.prefixed,
57
- property: EXT('content').prefixed,
58
- 'data-format': format as string,
59
- 'data-custom': custom ? 'true' : 'false',
60
- ...(!!value && { content: value as string }),
61
- };
62
- if (mappingResource) {
63
- return [
64
- 'span',
65
- {
66
- resource: mappingResource as string,
67
- typeof: EXT('Mapping').prefixed,
68
- class: 'date',
69
- },
70
- ['span', { property: DCT('type').prefixed, content: 'date' }],
71
- ['span', dateAttrs, humanReadableDate],
72
- ];
33
+ value: {},
34
+ format: {
35
+ default: options.formats[0].dateFormat,
36
+ },
37
+ onlyDate: {
38
+ default: true,
39
+ },
40
+ custom: {
41
+ default: false,
42
+ },
43
+ label: {
44
+ default: '',
45
+ },
46
+ },
47
+ leafText: (node: PNode) => {
48
+ const { value, onlyDate, format } = node.attrs;
49
+ const humanReadableDate = value
50
+ ? formatDate(new Date(value), format)
51
+ : onlyDate
52
+ ? options.placeholder.insertDate
53
+ : options.placeholder.insertDateTime;
54
+ return humanReadableDate;
55
+ },
56
+ toDOM: (node) => {
57
+ const { value, onlyDate, format, mappingResource, custom, label } =
58
+ node.attrs;
59
+ const datatype = onlyDate ? XSD('date') : XSD('dateTime');
60
+ let humanReadableDate: string;
61
+ if (value) {
62
+ if (validateDateFormat(format).type === 'ok') {
63
+ humanReadableDate = formatDate(new Date(value), format);
73
64
  } else {
74
- return ['span', { class: 'date', ...dateAttrs }, humanReadableDate];
65
+ humanReadableDate = 'Ongeldig formaat';
75
66
  }
67
+ } else {
68
+ humanReadableDate = (onlyDate as boolean)
69
+ ? options.placeholder.insertDate
70
+ : options.placeholder.insertDateTime;
71
+ }
72
+ const dateAttrs = {
73
+ datatype: datatype.prefixed,
74
+ property: EXT('content').prefixed,
75
+ 'data-format': format as string,
76
+ 'data-custom': custom ? 'true' : 'false',
77
+ ...(!!value && { content: value as string }),
78
+ };
79
+ if (mappingResource) {
80
+ return [
81
+ 'span',
82
+ {
83
+ resource: mappingResource as string,
84
+ typeof: EXT('Mapping').prefixed,
85
+ class: 'date',
86
+ 'data-label': label as string,
87
+ },
88
+ ['span', { property: DCT('type').prefixed, content: 'date' }],
89
+ ['span', dateAttrs, humanReadableDate],
90
+ ];
91
+ } else {
92
+ return ['span', { class: 'date', ...dateAttrs }, humanReadableDate];
93
+ }
94
+ },
95
+ parseDOM: [
96
+ {
97
+ tag: 'span',
98
+ getAttrs: (node: HTMLElement) => {
99
+ if (
100
+ hasRDFaAttribute(node, 'datatype', XSD('date')) ||
101
+ hasRDFaAttribute(node, 'datatype', XSD('dateTime'))
102
+ ) {
103
+ const onlyDate = hasRDFaAttribute(node, 'datatype', XSD('date'));
104
+ return {
105
+ value: node.getAttribute('content') ?? new Date().toISOString(),
106
+ onlyDate,
107
+ format: node.dataset.format,
108
+ custom: node.dataset.custom === 'true',
109
+ };
110
+ }
111
+ return false;
112
+ },
76
113
  },
77
- parseDOM: [
78
- {
79
- tag: 'span',
80
- getAttrs: (node: HTMLElement) => {
81
- if (
82
- hasRDFaAttribute(node, 'datatype', XSD('date')) ||
83
- hasRDFaAttribute(node, 'datatype', XSD('dateTime'))
84
- ) {
85
- const onlyDate = hasRDFaAttribute(node, 'datatype', XSD('date'));
114
+ {
115
+ tag: 'span',
116
+ getAttrs: (node: HTMLElement) => {
117
+ if (hasRDFaAttribute(node, 'typeof', EXT('Mapping'))) {
118
+ const mappingResource = node.getAttribute('resource');
119
+ if (!mappingResource) {
120
+ return false;
121
+ }
122
+ const variableType = [...node.children]
123
+ .find((el) => hasRDFaAttribute(el, 'property', DCT('type')))
124
+ ?.getAttribute('content');
125
+ const datatype = [...node.children]
126
+ .find((el) => hasRDFaAttribute(el, 'property', EXT('content')))
127
+ ?.getAttribute('datatype');
128
+ if (variableType === 'date' && datatype) {
129
+ const onlyDate = !![...node.children].find((el) =>
130
+ hasRDFaAttribute(el, 'datatype', XSD('date'))
131
+ );
132
+ const dateNode = [...node.children].find((el) =>
133
+ hasRDFaAttribute(el, 'property', EXT('content'))
134
+ ) as HTMLElement | undefined;
135
+ let humanReadableDate: string;
136
+ const value = dateNode?.getAttribute('content');
137
+ const format = dateNode?.dataset.format;
138
+ if (value && format) {
139
+ if (validateDateFormat(format).type === 'ok') {
140
+ humanReadableDate = formatDate(new Date(value), format);
141
+ } else {
142
+ humanReadableDate = 'Ongeldig formaat';
143
+ }
144
+ } else {
145
+ humanReadableDate = onlyDate
146
+ ? options.placeholder.insertDate
147
+ : options.placeholder.insertDateTime;
148
+ }
149
+ const label = node.getAttribute('data-label') || variableType;
86
150
  return {
87
- value: node.getAttribute('content') ?? new Date().toISOString(),
151
+ mappingResource,
88
152
  onlyDate,
89
- format: node.dataset.format,
90
- custom: node.dataset.custom === 'true',
153
+ humanReadableDate,
154
+ value: value,
155
+ format: format,
156
+ custom: dateNode?.dataset.custom === 'true',
157
+ label,
91
158
  };
92
159
  }
93
- return false;
94
- },
95
- },
96
- {
97
- tag: 'span',
98
- getAttrs: (node: HTMLElement) => {
99
- if (hasRDFaAttribute(node, 'typeof', EXT('Mapping'))) {
100
- const mappingResource = node.getAttribute('resource');
101
- if (!mappingResource) {
102
- return false;
103
- }
104
- const variableType = [...node.children]
105
- .find((el) => hasRDFaAttribute(el, 'property', DCT('type')))
106
- ?.getAttribute('content');
107
- const datatype = [...node.children]
108
- .find((el) => hasRDFaAttribute(el, 'property', EXT('content')))
109
- ?.getAttribute('datatype');
110
- if (variableType === 'date' && datatype) {
111
- const onlyDate = !![...node.children].find((el) =>
112
- hasRDFaAttribute(el, 'datatype', XSD('date'))
113
- );
114
- const dateNode = [...node.children].find((el) =>
115
- hasRDFaAttribute(el, 'property', EXT('content'))
116
- ) as HTMLElement | undefined;
117
- return {
118
- mappingResource,
119
- onlyDate,
120
- value:
121
- dateNode?.getAttribute('content') ?? new Date().toISOString(),
122
- format: dateNode?.dataset.format,
123
- custom: dateNode?.dataset.custom === 'true',
124
- };
125
- }
126
- }
127
- return false;
128
- },
160
+ }
161
+ return false;
129
162
  },
130
- ],
131
- };
132
- };
163
+ },
164
+ ],
165
+ });
133
166
 
134
- export default date;
167
+ export const date = (options: DateOptions) =>
168
+ createEmberNodeSpec(emberNodeConfig(options));
169
+ export const dateView = (options: DateOptions) =>
170
+ createEmberNodeView(emberNodeConfig(options));
@@ -77,7 +77,7 @@ position in the path
77
77
 
78
78
 
79
79
 
80
- ### `minCount: number`
80
+ ### minCount
81
81
 
82
82
  - type: `number`
83
83
  - meaning: the specified shape must occur at least as many times as the specified amount in the focusNode
@@ -141,13 +141,7 @@ export function validation(
141
141
  const schemaChanged = oldState.schema !== newState.schema;
142
142
  if (schemaChanged) {
143
143
  const spec = compileSpec(configurator, newState.schema);
144
- const newValidation = doValidation(
145
- tr,
146
- oldPluginState,
147
- oldState,
148
- newState,
149
- spec
150
- );
144
+ const newValidation = doValidation(newState, spec);
151
145
  tr.setMeta('validated', newValidation);
152
146
  tr.setMeta('firstPass', false);
153
147
  return { spec, report: newValidation };
@@ -166,13 +160,7 @@ export function validation(
166
160
  tr.setMeta('firstPass', false);
167
161
  return cachedValidation;
168
162
  }
169
- const newValidation = doValidation(
170
- tr,
171
- oldPluginState,
172
- oldState,
173
- newState,
174
- spec
175
- );
163
+ const newValidation = doValidation(newState, spec);
176
164
  tr.setMeta('validated', newValidation);
177
165
  tr.setMeta('firstPass', false);
178
166
  return { spec, report: newValidation };
@@ -230,9 +218,6 @@ interface ValidationContext {
230
218
  * @param spec
231
219
  */
232
220
  function doValidation(
233
- tr: Transaction,
234
- oldPluginState: ValidationState,
235
- oldState: EditorState,
236
221
  newState: EditorState,
237
222
  spec: ValidationSpec
238
223
  ): ValidationReport {
@@ -9,10 +9,55 @@ import {
9
9
  EmberNodeConfig,
10
10
  } from '@lblod/ember-rdfa-editor/utils/ember-node';
11
11
  import { v4 as uuidv4 } from 'uuid';
12
+ import {
13
+ MAXIMUM_VALUE_HTML_ATTRIBUTE_KEY,
14
+ MAXIMUM_VALUE_PNODE_KEY,
15
+ MINIMUM_VALUE_HTML_ATTRIBUTE_KEY,
16
+ MINIMUM_VALUE_PNODE_KEY,
17
+ } from './utils/constants';
18
+ import { PNode } from '@lblod/ember-rdfa-editor';
12
19
 
13
- const CONTENT_SELECTOR = `span[property~='${EXT('content').prefixed}'],
20
+ const CONTENT_SELECTOR = `span[property~='${EXT('content').prefixed}'],
14
21
  span[property~='${EXT('content').full}']`;
15
22
 
23
+ export const getHTMLNodeExtraAttributes = ({
24
+ node,
25
+ type,
26
+ }: {
27
+ node: HTMLElement;
28
+ type: string;
29
+ }) => {
30
+ if (type === 'number') {
31
+ return {
32
+ [MINIMUM_VALUE_PNODE_KEY]:
33
+ node.getAttribute(MINIMUM_VALUE_HTML_ATTRIBUTE_KEY) ?? null,
34
+ [MAXIMUM_VALUE_PNODE_KEY]:
35
+ node.getAttribute(MAXIMUM_VALUE_HTML_ATTRIBUTE_KEY) ?? null,
36
+ };
37
+ }
38
+
39
+ return {};
40
+ };
41
+
42
+ export const getPNodeExtraAttributes = ({
43
+ node,
44
+ type,
45
+ }: {
46
+ node: PNode;
47
+ type: string;
48
+ }) => {
49
+ if (type === 'number') {
50
+ return {
51
+ [MINIMUM_VALUE_HTML_ATTRIBUTE_KEY]:
52
+ (node.attrs[MINIMUM_VALUE_PNODE_KEY] as string) ?? null,
53
+ [MAXIMUM_VALUE_HTML_ATTRIBUTE_KEY]:
54
+ (node.attrs[MAXIMUM_VALUE_PNODE_KEY] as string) ?? null,
55
+ };
56
+ }
57
+
58
+ return {};
59
+ };
60
+
16
61
  const emberNodeConfig: EmberNodeConfig = {
17
62
  name: 'variable',
18
63
  componentPath: 'variable-plugin/variable',
@@ -39,6 +84,15 @@ const emberNodeConfig: EmberNodeConfig = {
39
84
  datatype: {
40
85
  default: null,
41
86
  },
87
+ label: {
88
+ default: '',
89
+ },
90
+ minimumValue: {
91
+ default: null,
92
+ },
93
+ maximumValue: {
94
+ default: null,
95
+ },
42
96
  },
43
97
  toDOM: (node) => {
44
98
  const {
@@ -48,12 +102,17 @@ const emberNodeConfig: EmberNodeConfig = {
48
102
  type,
49
103
  datatype,
50
104
  source,
105
+ label,
51
106
  } = node.attrs;
107
+
52
108
  const sourceSpan = source
53
109
  ? [
54
110
  [
55
111
  'span',
56
- { property: DCT('source').prefixed, resource: source as string },
112
+ {
113
+ property: DCT('source').prefixed,
114
+ resource: source as string,
115
+ },
57
116
  ],
58
117
  ]
59
118
  : [];
@@ -73,6 +132,8 @@ const emberNodeConfig: EmberNodeConfig = {
73
132
  {
74
133
  resource: mappingResource as string,
75
134
  typeof: EXT('Mapping').prefixed,
135
+ 'data-label': label as string,
136
+ ...getPNodeExtraAttributes({ node, type: type as string }),
76
137
  },
77
138
  [
78
139
  'span',
@@ -115,10 +176,11 @@ const emberNodeConfig: EmberNodeConfig = {
115
176
  const type = [...node.children]
116
177
  .find((el) => hasRDFaAttribute(el, 'property', DCT('type')))
117
178
  ?.getAttribute('content');
179
+ const label = node.getAttribute('data-label') || type;
118
180
  const datatype = [...node.children]
119
181
  .find((el) => hasRDFaAttribute(el, 'property', EXT('content')))
120
182
  ?.getAttribute('datatype');
121
- if (!mappingResource) {
183
+ if (!mappingResource || !type) {
122
184
  return false;
123
185
  }
124
186
  return {
@@ -130,8 +192,11 @@ const emberNodeConfig: EmberNodeConfig = {
130
192
  source,
131
193
  type,
132
194
  datatype,
195
+ label,
196
+ ...getHTMLNodeExtraAttributes({ type, node }),
133
197
  };
134
198
  }
199
+
135
200
  return false;
136
201
  },
137
202
  contentElement: CONTENT_SELECTOR,