@lblod/ember-rdfa-editor-lblod-plugins 8.0.1 → 8.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.
@@ -2,16 +2,12 @@ matrix:
2
2
  scenario:
3
3
  - ember-lts-3.28
4
4
 
5
- pipeline:
6
- ${scenario}:
5
+ steps:
6
+ - name: ${scenario}
7
7
  image: danlynn/ember-cli:4.8.0
8
8
  commands:
9
9
  - npm ci
10
- - npx ember try:one ${scenario}
10
+ - ember try:one ${scenario} --skip-cleanup=true
11
11
 
12
12
  when:
13
- event:
14
- - pull_request
15
-
16
- depends_on:
17
- - test
13
+ branch: [ master ]
@@ -1,23 +1,52 @@
1
-
2
- pipeline:
1
+ steps:
2
+ changelog:
3
+ image: danlynn/ember-cli:4.8.0
4
+ commands:
5
+ - git fetch origin master
6
+ - git diff -wb --name-only origin/master..HEAD | grep CHANGELOG.md
7
+ when:
8
+ event: pull_request
3
9
  install:
4
10
  image: danlynn/ember-cli:4.8.0
5
11
  commands:
6
12
  - npm ci
13
+ # ember-try already runs ember test, so we don't need another test step
7
14
  lint-js:
8
15
  image: danlynn/ember-cli:4.8.0
9
- group: lint
16
+ group: check
10
17
  commands:
11
18
  - npm run lint:js
12
19
  lint-hbs:
13
20
  image: danlynn/ember-cli:4.8.0
14
- group: lint
21
+ group: check
15
22
  commands:
16
23
  - npm run lint:hbs
17
24
  test:
18
25
  image: danlynn/ember-cli:4.8.0
26
+ group: check
19
27
  commands:
20
- - npm run test:ember
28
+ - ember test
29
+
30
+
31
+ release:
32
+ image: plugins/npm
33
+ settings:
34
+ token:
35
+ from_secret: npm_access_token
36
+ when:
37
+ event: tag
38
+ ref: refs/tags/v*
39
+
40
+ push-tagged-build:
41
+ image: plugins/docker
42
+ settings:
43
+ repo: lblod/ember-rdfa-editor-lblod-plugins
44
+ tags: "${CI_COMMIT_TAG##v}"
45
+ purge: true
46
+ secrets: [ docker_username, docker_password ]
47
+ when:
48
+ event: tag
49
+ ref: refs/tags/v*
50
+ # according to docs, this also runs on prs targetting master
21
51
  when:
22
- event:
23
- - pull_request
52
+ branch: [ master, hotfix/* ]
package/CHANGELOG.md CHANGED
@@ -7,11 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [8.2.0] - 2023-06-26
11
+ ### Added
12
+ - Add a toggle to show the number as words in a number variable
13
+
14
+ ### Dependencies
15
+ - Bumps `sass` from 1.56.1 to 1.63.6
16
+ - Bumps `@types/ember` from 4.0.2 to 4.0.4
17
+ - Bumps `@types/ember-data__store` from 4.0.2 to 4.0.3
18
+ - Bumps `@types/ember__template` from 4.0.1 to 4.0.2
19
+ - Bumps `@types/rdfjs__parser-n3` from 1.1.5 to 2.0.1
20
+
21
+ ## [8.1.0] - 2023-06-22
22
+ ### Added
23
+ - Numbers inputted into a number variable are validated on defined min/max and if it is a number
24
+ - Add toggle for the user to show number variable as words
25
+
26
+ ### Fixed
27
+ - Fixed woodpecker builds crashing on syntax errors
28
+ - Use dutch language in static version of table of contents
29
+ - fix typo "Vlaams Codex" → "Vlaamse Codex"
30
+ - add missing argument to citation card in dummy app
31
+ - correct erroneous arguments to AuAlert
32
+
33
+ ### Changed
34
+ - remove the unnecessary type and add the html-safe tag at the rendering site
35
+
36
+ ### Removed
37
+ - remove ensure-changelog github action
38
+
39
+ ### Dependencies
40
+ - Bumps `@types/rdfjs__dataset` from 2.0.0 to 2.0.2
41
+ - Bumps `@types/ember__array` from 4.0.3 to 4.0.4
42
+ - Bumps `@typescript-eslint/parser` from 5.45.0 to 5.60.0
43
+ - Bumps `@types/ember__engine` from 4.0.4 to 4.0.5
44
+ - Bumps `typescript` to 5.0.4
45
+ - Bumps `prosemirror-dev-tools` from 3.1.0 to 4.0.0
46
+
10
47
  ## [8.0.1] - 2023-06-15
11
48
  ### Fixed
12
49
  - Change problematic type in citation that made it to break with new ember
13
50
 
14
-
15
51
  ## [8.0.0] - 2023-06-15
16
52
  ### Fixed
17
53
  - Bump `@lblod/ember-rdfa-editor` package to fix annotation not present for some structures
@@ -473,7 +509,7 @@ add onclick handler to pencil icon in variable plugin
473
509
 
474
510
  # Changelog
475
511
 
476
- [unreleased]: https://github.com/lblod/ember-rdfa-editor-lblod-plugins/compare/v8.0.1...HEAD
512
+ [unreleased]: https://github.com/lblod/ember-rdfa-editor-lblod-plugins/compare/v8.2.0...HEAD
477
513
  [8.0.0]: https://github.com/lblod/ember-rdfa-editor-lblod-plugins/compare/v8.0.0...v8.0.1
478
514
  [8.0.0]: https://github.com/lblod/ember-rdfa-editor-lblod-plugins/compare/v7.1.0...v8.0.0
479
515
  [7.1.0]: https://github.com/lblod/ember-rdfa-editor-lblod-plugins/compare/v7.0.0...v7.1.0
@@ -489,3 +525,5 @@ add onclick handler to pencil icon in variable plugin
489
525
  [3.0.0]: https://github.com/lblod/ember-rdfa-editor-lblod-plugins/compare/v2.1.2...v3.0.0
490
526
  [2.1.2]: https://github.com/lblod/ember-rdfa-editor-lblod-plugins/compare/v2.1.1...v2.1.2
491
527
  [2.1.1]: https://github.com/lblod/ember-rdfa-editor-lblod-plugins/compare/v2.1.0...v2.1.1
528
+ [8.2.0]: https://github.com/lblod/ember-rdfa-editor-lblod-plugins/compare/v8.1.0...v8.2.0
529
+ [8.1.0]: https://github.com/lblod/ember-rdfa-editor-lblod-plugins/compare/v8.0.1...v8.1.0
@@ -3,7 +3,7 @@
3
3
  {{@article.number}} ({{@article.dateInForce}} - {{if @article.dateNoLongerInForce @article.dateNoLongerInForce "…"}})
4
4
  </AuHeading>
5
5
  <p>
6
- {{@article.content}}
6
+ {{html-safe @article.content}}
7
7
  </p>
8
8
  <AuButton
9
9
  @skin="primary"
@@ -1,8 +1,8 @@
1
1
  <AuAlert
2
- @alertTitle="{{t "citaten-plugin.alert.error-title"}}"
3
- @alertSkin="error"
4
- @alertIcon="alert-triangle"
5
- @alertSize="{{if @fullSize "default" "small"}}"
2
+ @title="{{t "citaten-plugin.alert.error-title"}}"
3
+ @skin="error"
4
+ @icon="alert-triangle"
5
+ @size="{{if @fullSize "default" "small"}}"
6
6
  @closable={{false}}
7
7
  ...attributes>
8
8
  <p>{{t "citaten-plugin.alert.error-intro"}}</p>
@@ -1,8 +1,8 @@
1
1
  <AuAlert
2
- @alertTitle="{{t "citaten-plugin.alert.no-results"}}"
3
- @alertSkin="warning"
4
- @alertIcon="cross"
5
- @alertSize="{{if @fullSize "default" "small"}}"
2
+ @title="{{t "citaten-plugin.alert.no-results"}}"
3
+ @skin="warning"
4
+ @icon="cross"
5
+ @size="{{if @fullSize "default" "small"}}"
6
6
  @closable={{false}}
7
7
  class="{{unless @fullSize "au-u-margin-small"}}"
8
8
  ...attributes>
@@ -1,8 +1,6 @@
1
1
  import Component from '@glimmer/component';
2
2
  import { action } from '@ember/object';
3
3
  import { inject as service } from '@ember/service';
4
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
5
- // @ts-ignore
6
4
  import { localCopy } from 'tracked-toolbox';
7
5
  import Intl from 'ember-intl/services/intl';
8
6
 
@@ -13,7 +11,6 @@ type Args = {
13
11
 
14
12
  export default class RdfaDatePluginDateTimePicker extends Component<Args> {
15
13
  @service declare intl: Intl;
16
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call
17
14
  @localCopy('args.value') declare date?: Date;
18
15
 
19
16
  get hours() {
@@ -0,0 +1,61 @@
1
+ <Velcro
2
+ @placement='bottom-start'
3
+ @offsetOptions={{hash mainAxis=3}} as |velcro|
4
+ >
5
+ <AuPill
6
+ @icon='pencil'
7
+ @iconAlignment='right'
8
+ class='variable'
9
+ {{velcro.hook}}
10
+ {{on 'click' this.selectThisNode}}
11
+ >
12
+ {{#if this.formattedNumber}}
13
+ {{this.formattedNumber}}
14
+ {{else}}
15
+ <span class='mark-highlight-manual'>
16
+ {{t 'variable.number.placeholder'}}
17
+ </span>
18
+ {{/if}}
19
+ {{#if @node.attrs.label}}
20
+ <span class='label'>
21
+ ({{@node.attrs.label}})
22
+ </span>
23
+ {{/if}}
24
+ </AuPill>
25
+ {{#if this.selected}}
26
+ <div class='say-tooltip say-number-tooltip' {{velcro.loop}}>
27
+ {{! AuCard gives weird behaviour when using velcro without div (cause is position:fixed in css)}}
28
+ <AuCard
29
+ @shadow={{true}}
30
+ @size='small'
31
+ id='number-input-tooltip'
32
+ @standOut={{true}} as |card|
33
+ >
34
+ <card.content>
35
+ {{!-- template-lint-disable no-down-event-binding --}}
36
+ <AuInput
37
+ @value={{this.inputNumber}}
38
+ placeholder={{t 'variable.number.type-number'}}
39
+ {{did-insert this.focus}}
40
+ {{on 'input' this.onInputNumberChange}}
41
+ {{on 'keyup' this.leaveOnEnter}}
42
+ {{on 'keyup' this.leaveWithArrows}}
43
+ {{on 'keydown' this.setPosBeforeKeypress}}
44
+ />
45
+ <AuToggleSwitch
46
+ @identifier="writtenNumber"
47
+ @name="writtenNumber"
48
+ @checked={{this.writtenNumber}}
49
+ @onChange={{this.changeWrittenNumber}}
50
+ @label={{t 'variable.number.written-number-label'}}
51
+ />
52
+ {{#if this.errorMessage}}
53
+ <AuHelpText @small={{true}} @error={{true}}>
54
+ {{this.errorMessage}}
55
+ </AuHelpText>
56
+ {{/if}}
57
+ </card.content>
58
+ </AuCard>
59
+ </div>
60
+ {{/if}}
61
+ </Velcro>
@@ -0,0 +1,173 @@
1
+ import Component from '@glimmer/component';
2
+ import {
3
+ DecorationSource,
4
+ NodeSelection,
5
+ PNode,
6
+ SayController,
7
+ SayView,
8
+ TextSelection,
9
+ } from '@lblod/ember-rdfa-editor';
10
+ import { action } from '@ember/object';
11
+ import { tracked } from '@glimmer/tracking';
12
+ import { service } from '@ember/service';
13
+ import intlService from 'ember-intl/services/intl';
14
+ import { localCopy } from 'tracked-toolbox';
15
+ import {
16
+ MAXIMUM_VALUE_PNODE_KEY,
17
+ MINIMUM_VALUE_PNODE_KEY,
18
+ } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/variable-plugin/utils/constants';
19
+ import { isBlank } from '@ember/utils';
20
+ import n2words from 'n2words';
21
+
22
+ type Args = {
23
+ getPos: () => number | undefined;
24
+ node: PNode;
25
+ updateAttribute: (attr: string, value: unknown) => void;
26
+ controller: SayController;
27
+ view: SayView;
28
+ selected: boolean;
29
+ contentDecorations?: DecorationSource;
30
+ };
31
+ export default class VariableNumberPluginNumberComponent extends Component<Args> {
32
+ @localCopy('args.node.attrs.value', '') declare inputNumber: string;
33
+ @localCopy('args.node.attrs.writtenNumber', false)
34
+ declare writtenNumber: boolean;
35
+ @tracked errorMessage = '';
36
+ @service declare intl: intlService;
37
+ cursorPositionKeyDown: number | null = null;
38
+
39
+ focus(element: HTMLInputElement) {
40
+ element.focus();
41
+ }
42
+
43
+ get node() {
44
+ return this.args.node;
45
+ }
46
+ get formattedNumber() {
47
+ const value = this.node.attrs.value as string;
48
+
49
+ if (Number.isNaN(Number(value)) || value === null || value === '') {
50
+ return value;
51
+ }
52
+ if (!this.writtenNumber) {
53
+ return value;
54
+ } else {
55
+ return n2words(Number(value), { lang: 'nl' });
56
+ }
57
+ }
58
+
59
+ get selected() {
60
+ return this.args.selected;
61
+ }
62
+
63
+ get minValue() {
64
+ return this.node.attrs[MINIMUM_VALUE_PNODE_KEY] as number;
65
+ }
66
+
67
+ get maxValue() {
68
+ return this.node.attrs[MAXIMUM_VALUE_PNODE_KEY] as number;
69
+ }
70
+
71
+ @action onInputNumberChange(event: InputEvent) {
72
+ this.inputNumber = (event.target as HTMLInputElement).value;
73
+ this.validateAndSave();
74
+ }
75
+
76
+ @action
77
+ changeWrittenNumber() {
78
+ this.args.updateAttribute('writtenNumber', !this.writtenNumber);
79
+ }
80
+
81
+ validateAndSave() {
82
+ if (isBlank(this.inputNumber)) {
83
+ this.errorMessage = '';
84
+ this.args.updateAttribute('value', '');
85
+ return;
86
+ }
87
+
88
+ const number = Number(this.inputNumber);
89
+ if (Number.isNaN(number)) {
90
+ this.errorMessage = this.intl.t('variable.number.error-not-number');
91
+ return;
92
+ }
93
+ const validMinimum = !this.minValue || number >= this.minValue;
94
+ const validMaximum = !this.maxValue || number <= this.maxValue;
95
+
96
+ if (!validMinimum || !validMaximum) {
97
+ if (this.minValue && this.maxValue) {
98
+ this.errorMessage = this.intl.t(
99
+ 'variable.number.error-number-between',
100
+ { minValue: this.minValue, maxValue: this.maxValue }
101
+ );
102
+ } else if (this.minValue) {
103
+ this.errorMessage = this.intl.t('variable.number.error-number-above', {
104
+ minValue: this.minValue,
105
+ });
106
+ } else if (this.maxValue) {
107
+ this.errorMessage = this.intl.t('variable.number.error-number-below', {
108
+ maxValue: this.maxValue,
109
+ });
110
+ }
111
+ return;
112
+ }
113
+
114
+ this.errorMessage = '';
115
+ this.args.updateAttribute('value', this.inputNumber);
116
+ }
117
+
118
+ @action
119
+ selectThisNode() {
120
+ const tr = this.args.controller.activeEditorState.tr;
121
+ tr.setSelection(
122
+ NodeSelection.create(
123
+ this.args.controller.activeEditorState.doc,
124
+ this.args.getPos() as number
125
+ )
126
+ );
127
+ this.args.controller.activeEditorView.dispatch(tr);
128
+ }
129
+
130
+ @action
131
+ leaveOnEnter(event: KeyboardEvent) {
132
+ if (event.key === 'Enter') {
133
+ this.selectAfterNode();
134
+ }
135
+ }
136
+
137
+ @action setPosBeforeKeypress(event: KeyboardEvent) {
138
+ if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {
139
+ this.cursorPositionKeyDown = (
140
+ event.target as HTMLInputElement
141
+ ).selectionStart;
142
+ }
143
+ }
144
+ @action leaveWithArrows(event: KeyboardEvent) {
145
+ const finalPos = (event.target as HTMLInputElement).value.length;
146
+ if (event.key === 'ArrowRight' && this.cursorPositionKeyDown === finalPos) {
147
+ this.selectAfterNode();
148
+ }
149
+ if (event.key === 'ArrowLeft' && this.cursorPositionKeyDown === 0) {
150
+ this.selectBeforeNode();
151
+ }
152
+ this.cursorPositionKeyDown = null;
153
+ }
154
+
155
+ setSelectionAt(pos: number) {
156
+ const tr = this.args.controller.activeEditorState.tr;
157
+ tr.setSelection(
158
+ TextSelection.create(this.args.controller.activeEditorState.doc, pos)
159
+ );
160
+ this.args.controller.focus();
161
+ this.args.controller.activeEditorView.dispatch(tr);
162
+ }
163
+
164
+ selectAfterNode() {
165
+ this.setSelectionAt(
166
+ (this.args.getPos() as number) + this.args.node.nodeSize
167
+ );
168
+ }
169
+
170
+ selectBeforeNode() {
171
+ this.setSelectionAt(this.args.getPos() as number);
172
+ }
173
+ }
@@ -1,11 +1,9 @@
1
1
  import { LEGISLATION_TYPE_CONCEPTS } from './legislation-types';
2
2
  import { warn } from '@ember/debug';
3
- import { htmlSafe } from '@ember/template';
4
3
  import {
5
4
  Option,
6
5
  optionMapOr,
7
6
  } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/option';
8
- type SafeString = ReturnType<typeof htmlSafe>;
9
7
 
10
8
  interface DecisionArgs {
11
9
  uri: string;
@@ -54,7 +52,7 @@ interface ArticleArgs {
54
52
  export class Article {
55
53
  uri: string;
56
54
  number?: number;
57
- content: SafeString | null;
55
+ content: string | null;
58
56
  dateInForce: string | null;
59
57
  dateNoLongerInForce: string | null;
60
58
 
@@ -67,7 +65,7 @@ export class Article {
67
65
  }: ArticleArgs) {
68
66
  this.uri = uri;
69
67
  this.number = number;
70
- this.content = optionMapOr(null, htmlSafe, content);
68
+ this.content = content;
71
69
  this.dateInForce = dateInForce;
72
70
  this.dateNoLongerInForce = dateNoLongerInForce;
73
71
  }
@@ -33,14 +33,14 @@ export const emberNodeConfig: (
33
33
  'data-ember-node': 'table-of-contents',
34
34
  class: 'table-of-contents',
35
35
  },
36
- ['h3', {}, 'Table Of Contents'],
36
+ ['h3', {}, 'Inhoudstafel'],
37
37
  ];
38
38
  }
39
39
 
40
40
  return [
41
41
  'div',
42
42
  { 'data-ember-node': 'table-of-contents', class: 'table-of-contents' },
43
- ['h3', {}, 'Table Of Contents'],
43
+ ['h3', {}, 'Inhoudstafel'],
44
44
  createTableOfContents(entries),
45
45
  ];
46
46
  },
@@ -14,10 +14,13 @@ import {
14
14
  MAXIMUM_VALUE_PNODE_KEY,
15
15
  MINIMUM_VALUE_HTML_ATTRIBUTE_KEY,
16
16
  MINIMUM_VALUE_PNODE_KEY,
17
+ WRITTEN_NUMBER_HTML_ATTRIBUTE_KEY,
18
+ WRITTEN_NUMBER_PNODE_KEY,
17
19
  } from './utils/constants';
18
- import { PNode } from '@lblod/ember-rdfa-editor';
20
+ import { Attrs, DOMOutputSpec, PNode } from '@lblod/ember-rdfa-editor';
21
+ import n2words from 'n2words';
19
22
 
20
- const CONTENT_SELECTOR = `span[property~='${EXT('content').prefixed}'],
23
+ export const CONTENT_SELECTOR = `span[property~='${EXT('content').prefixed}'],
21
24
  span[property~='${EXT('content').full}']`;
22
25
 
23
26
  export const getHTMLNodeExtraAttributes = ({
@@ -33,6 +36,10 @@ export const getHTMLNodeExtraAttributes = ({
33
36
  node.getAttribute(MINIMUM_VALUE_HTML_ATTRIBUTE_KEY) ?? null,
34
37
  [MAXIMUM_VALUE_PNODE_KEY]:
35
38
  node.getAttribute(MAXIMUM_VALUE_HTML_ATTRIBUTE_KEY) ?? null,
39
+ [WRITTEN_NUMBER_PNODE_KEY]:
40
+ node.getAttribute(WRITTEN_NUMBER_HTML_ATTRIBUTE_KEY) === 'true'
41
+ ? true
42
+ : false,
36
43
  };
37
44
  }
38
45
 
@@ -52,13 +59,145 @@ export const getPNodeExtraAttributes = ({
52
59
  (node.attrs[MINIMUM_VALUE_PNODE_KEY] as string) ?? null,
53
60
  [MAXIMUM_VALUE_HTML_ATTRIBUTE_KEY]:
54
61
  (node.attrs[MAXIMUM_VALUE_PNODE_KEY] as string) ?? null,
62
+ [WRITTEN_NUMBER_HTML_ATTRIBUTE_KEY]: String(
63
+ node.attrs[WRITTEN_NUMBER_PNODE_KEY] ?? false
64
+ ),
55
65
  };
56
66
  }
57
67
 
58
68
  return {};
59
69
  };
60
70
 
61
- const emberNodeConfig: EmberNodeConfig = {
71
+ export const contentToDom = ({
72
+ content,
73
+ type,
74
+ node,
75
+ }: {
76
+ content: string;
77
+ type: string;
78
+ node: PNode;
79
+ }) => {
80
+ if (type === 'number') {
81
+ if (
82
+ node.attrs[WRITTEN_NUMBER_PNODE_KEY] ||
83
+ Number.isNaN(Number(content)) ||
84
+ content === null ||
85
+ content === ''
86
+ ) {
87
+ return n2words(Number(content), { lang: 'nl' });
88
+ } else {
89
+ return content;
90
+ }
91
+ } else {
92
+ return content;
93
+ }
94
+ };
95
+
96
+ export const parseAttributes = (node: HTMLElement): false | Attrs => {
97
+ if (
98
+ hasRDFaAttribute(node, 'typeof', EXT('Mapping')) &&
99
+ node.querySelector(CONTENT_SELECTOR)
100
+ ) {
101
+ const variableInstance = [...node.children]
102
+ .find((el) => hasRDFaAttribute(el, 'property', EXT('instance')))
103
+ ?.getAttribute('resource');
104
+ const mappingResource = node.getAttribute('resource');
105
+ const codelistSpan = [...node.children].find((el) =>
106
+ hasRDFaAttribute(el, 'property', EXT('codelist'))
107
+ );
108
+ const codelistResource =
109
+ codelistSpan?.getAttribute('resource') ??
110
+ codelistSpan?.getAttribute('content');
111
+ const source = [...node.children]
112
+ .find((el) => hasRDFaAttribute(el, 'property', DCT('source')))
113
+ ?.getAttribute('resource');
114
+ const type = [...node.children]
115
+ .find((el) => hasRDFaAttribute(el, 'property', DCT('type')))
116
+ ?.getAttribute('content');
117
+ const label = node.getAttribute('data-label') || type;
118
+ const datatype = [...node.children]
119
+ .find((el) => hasRDFaAttribute(el, 'property', EXT('content')))
120
+ ?.getAttribute('datatype');
121
+ if (!mappingResource || !type) {
122
+ return false;
123
+ }
124
+ return {
125
+ variableInstance:
126
+ variableInstance ?? `http://data.lblod.info/variables/${uuidv4()}`,
127
+ mappingResource,
128
+ codelistResource,
129
+ source,
130
+ type,
131
+ datatype,
132
+ label,
133
+ ...getHTMLNodeExtraAttributes({ type, node }),
134
+ };
135
+ }
136
+
137
+ return false;
138
+ };
139
+
140
+ export const attributesToDOM = (node: PNode, content = null): DOMOutputSpec => {
141
+ const {
142
+ mappingResource,
143
+ codelistResource,
144
+ variableInstance,
145
+ type,
146
+ datatype,
147
+ source,
148
+ label,
149
+ } = node.attrs;
150
+
151
+ const sourceSpan = source
152
+ ? [
153
+ [
154
+ 'span',
155
+ {
156
+ property: DCT('source').prefixed,
157
+ resource: source as string,
158
+ },
159
+ ],
160
+ ]
161
+ : [];
162
+ const codelistResourceSpan = codelistResource
163
+ ? [
164
+ [
165
+ 'span',
166
+ {
167
+ property: EXT('codelist').prefixed, //becomes EXT('instance')
168
+ resource: codelistResource as string,
169
+ },
170
+ ],
171
+ ]
172
+ : [];
173
+ return [
174
+ 'span',
175
+ {
176
+ resource: mappingResource as string,
177
+ typeof: EXT('Mapping').prefixed,
178
+ 'data-label': label as string,
179
+ ...getPNodeExtraAttributes({ node, type: type as string }),
180
+ },
181
+ [
182
+ 'span',
183
+ { property: EXT('instance'), resource: variableInstance as string },
184
+ ],
185
+ ['span', { property: DCT('type').prefixed, content: type as string }],
186
+ ...sourceSpan,
187
+ ...codelistResourceSpan,
188
+ [
189
+ 'span',
190
+ {
191
+ property: EXT('content').prefixed,
192
+ content: content ? content : '',
193
+ ...(!!datatype && { datatype: datatype as string }),
194
+ },
195
+ content ? contentToDom({ content, type: type as string, node }) : 0,
196
+ ],
197
+ ];
198
+ };
199
+
200
+ export const emberNodeConfig: EmberNodeConfig = {
62
201
  name: 'variable',
63
202
  componentPath: 'variable-plugin/variable',
64
203
  inline: true,
@@ -93,111 +232,23 @@ const emberNodeConfig: EmberNodeConfig = {
93
232
  maximumValue: {
94
233
  default: null,
95
234
  },
235
+ writtenNumber: {
236
+ default: false,
237
+ },
96
238
  },
97
239
  toDOM: (node) => {
98
- const {
99
- mappingResource,
100
- codelistResource,
101
- variableInstance,
102
- type,
103
- datatype,
104
- source,
105
- label,
106
- } = node.attrs;
107
-
108
- const sourceSpan = source
109
- ? [
110
- [
111
- 'span',
112
- {
113
- property: DCT('source').prefixed,
114
- resource: source as string,
115
- },
116
- ],
117
- ]
118
- : [];
119
- const codelistResourceSpan = codelistResource
120
- ? [
121
- [
122
- 'span',
123
- {
124
- property: EXT('codelist').prefixed, //becomes EXT('instance')
125
- resource: codelistResource as string,
126
- },
127
- ],
128
- ]
129
- : [];
130
- return [
131
- 'span',
132
- {
133
- resource: mappingResource as string,
134
- typeof: EXT('Mapping').prefixed,
135
- 'data-label': label as string,
136
- ...getPNodeExtraAttributes({ node, type: type as string }),
137
- },
138
- [
139
- 'span',
140
- { property: EXT('instance'), resource: variableInstance as string },
141
- ],
142
- ['span', { property: DCT('type').prefixed, content: type as string }],
143
- ...sourceSpan,
144
- ...codelistResourceSpan,
145
- [
146
- 'span',
147
- {
148
- property: EXT('content').prefixed,
149
- ...(!!datatype && { datatype: datatype as string }),
150
- },
151
- 0,
152
- ],
153
- ];
240
+ return attributesToDOM(node);
154
241
  },
155
242
  parseDOM: [
156
243
  {
157
244
  tag: 'span',
158
245
  getAttrs: (node: HTMLElement) => {
159
- if (
160
- hasRDFaAttribute(node, 'typeof', EXT('Mapping')) &&
161
- node.querySelector(CONTENT_SELECTOR)
162
- ) {
163
- const variableInstance = [...node.children]
164
- .find((el) => hasRDFaAttribute(el, 'property', EXT('instance')))
165
- ?.getAttribute('resource');
166
- const mappingResource = node.getAttribute('resource');
167
- const codelistSpan = [...node.children].find((el) =>
168
- hasRDFaAttribute(el, 'property', EXT('codelist'))
169
- );
170
- const codelistResource =
171
- codelistSpan?.getAttribute('resource') ??
172
- codelistSpan?.getAttribute('content');
173
- const source = [...node.children]
174
- .find((el) => hasRDFaAttribute(el, 'property', DCT('source')))
175
- ?.getAttribute('resource');
176
- const type = [...node.children]
177
- .find((el) => hasRDFaAttribute(el, 'property', DCT('type')))
178
- ?.getAttribute('content');
179
- const label = node.getAttribute('data-label') || type;
180
- const datatype = [...node.children]
181
- .find((el) => hasRDFaAttribute(el, 'property', EXT('content')))
182
- ?.getAttribute('datatype');
183
- if (!mappingResource || !type) {
184
- return false;
185
- }
186
- return {
187
- variableInstance:
188
- variableInstance ??
189
- `http://data.lblod.info/variables/${uuidv4()}`,
190
- mappingResource,
191
- codelistResource,
192
- source,
193
- type,
194
- datatype,
195
- label,
196
- ...getHTMLNodeExtraAttributes({ type, node }),
197
- };
246
+ const attr = parseAttributes(node);
247
+ if (attr && attr.type !== 'number') {
248
+ return attr;
249
+ } else {
250
+ return false;
198
251
  }
199
-
200
- return false;
201
252
  },
202
253
  contentElement: CONTENT_SELECTOR,
203
254
  },
@@ -0,0 +1,50 @@
1
+ import {
2
+ createEmberNodeSpec,
3
+ createEmberNodeView,
4
+ EmberNodeConfig,
5
+ } from '@lblod/ember-rdfa-editor/utils/ember-node';
6
+ import {
7
+ attributesToDOM,
8
+ emberNodeConfig as baseVariableConfig,
9
+ CONTENT_SELECTOR,
10
+ parseAttributes,
11
+ } from './nodes';
12
+ import { PNode } from '@lblod/ember-rdfa-editor';
13
+ import { hasRDFaAttribute } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/namespace';
14
+ import { EXT } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
15
+
16
+ const emberNodeConfig = (): EmberNodeConfig => {
17
+ const { attrs, ...baseConfig } = baseVariableConfig;
18
+ return {
19
+ ...baseConfig,
20
+ attrs: { ...attrs, value: { default: null } },
21
+ componentPath: 'variable-number/number',
22
+ leafText: (node: PNode) => {
23
+ const { value } = node.attrs;
24
+ return value as string;
25
+ },
26
+ parseDOM: [
27
+ {
28
+ tag: 'span',
29
+ getAttrs: (node: HTMLElement) => {
30
+ const attrs = parseAttributes(node);
31
+ if (attrs && attrs.type === 'number') {
32
+ const content = [...node.children]
33
+ .find((el) => hasRDFaAttribute(el, 'property', EXT('content')))
34
+ ?.getAttribute('content');
35
+ return { ...attrs, value: content };
36
+ } else {
37
+ return false;
38
+ }
39
+ },
40
+ contentElement: CONTENT_SELECTOR,
41
+ },
42
+ ],
43
+ toDOM: (node) => {
44
+ return attributesToDOM(node, node.attrs.value);
45
+ },
46
+ };
47
+ };
48
+
49
+ export const number = createEmberNodeSpec(emberNodeConfig());
50
+ export const numberView = createEmberNodeView(emberNodeConfig());
@@ -42,17 +42,15 @@ export const DEFAULT_VARIABLE_TYPES: Record<string, VariableType> = {
42
42
  const mappingURI = `http://data.lblod.info/mappings/${uuidv4()}`;
43
43
  const variableInstance = `http://data.lblod.info/variables/${uuidv4()}`;
44
44
 
45
- return schema.node(
46
- 'variable',
47
- {
45
+ return unwrap(
46
+ schema.nodes.number.createAndFill({
48
47
  mappingResource: mappingURI,
49
48
  variableInstance,
50
49
  type: 'number',
51
50
  datatype: XSD('integer').prefixed,
52
51
  label,
53
52
  ...attributes,
54
- },
55
- schema.node('placeholder', { placeholderText: 'number' })
53
+ })
56
54
  );
57
55
  },
58
56
  },
@@ -117,6 +115,8 @@ export const DEFAULT_VARIABLE_TYPES: Record<string, VariableType> = {
117
115
 
118
116
  export const MINIMUM_VALUE_PNODE_KEY = 'minimumValue';
119
117
  export const MAXIMUM_VALUE_PNODE_KEY = 'maximumValue';
118
+ export const WRITTEN_NUMBER_PNODE_KEY = 'writtenNumber';
120
119
 
121
120
  export const MINIMUM_VALUE_HTML_ATTRIBUTE_KEY = 'data-minimum-value';
122
121
  export const MAXIMUM_VALUE_HTML_ATTRIBUTE_KEY = 'data-maximum-value';
122
+ export const WRITTEN_NUMBER_HTML_ATTRIBUTE_KEY = 'data-written-number';
@@ -0,0 +1 @@
1
+ export { default } from '@lblod/ember-rdfa-editor-lblod-plugins/components/variable-number/number';
@@ -99,3 +99,17 @@
99
99
  width: 100%;
100
100
  }
101
101
  }
102
+
103
+ // show tooltips above everything else
104
+ .say-tooltip {
105
+ z-index: 3;
106
+ }
107
+
108
+ .say-number-tooltip {
109
+ width: 200px;
110
+
111
+ // AuInput @type='number' overrides width to be 100px, we have to override it again
112
+ input[type='number'] {
113
+ width: 100%;
114
+ }
115
+ }
@@ -0,0 +1,36 @@
1
+ import Component from '@glimmer/component';
2
+ import { DecorationSource, PNode, SayController, SayView } from '@lblod/ember-rdfa-editor';
3
+ import intlService from 'ember-intl/services/intl';
4
+ type Args = {
5
+ getPos: () => number | undefined;
6
+ node: PNode;
7
+ updateAttribute: (attr: string, value: unknown) => void;
8
+ controller: SayController;
9
+ view: SayView;
10
+ selected: boolean;
11
+ contentDecorations?: DecorationSource;
12
+ };
13
+ export default class VariableNumberPluginNumberComponent extends Component<Args> {
14
+ inputNumber: string;
15
+ writtenNumber: boolean;
16
+ errorMessage: string;
17
+ intl: intlService;
18
+ cursorPositionKeyDown: number | null;
19
+ focus(element: HTMLInputElement): void;
20
+ get node(): PNode;
21
+ get formattedNumber(): string;
22
+ get selected(): boolean;
23
+ get minValue(): number;
24
+ get maxValue(): number;
25
+ onInputNumberChange(event: InputEvent): void;
26
+ changeWrittenNumber(): void;
27
+ validateAndSave(): void;
28
+ selectThisNode(): void;
29
+ leaveOnEnter(event: KeyboardEvent): void;
30
+ setPosBeforeKeypress(event: KeyboardEvent): void;
31
+ leaveWithArrows(event: KeyboardEvent): void;
32
+ setSelectionAt(pos: number): void;
33
+ selectAfterNode(): void;
34
+ selectBeforeNode(): void;
35
+ }
36
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lblod/ember-rdfa-editor-lblod-plugins",
3
- "version": "8.0.1",
3
+ "version": "8.2.0",
4
4
  "description": "Ember addon providing lblod specific plugins for the ember-rdfa-editor",
5
5
  "keywords": [
6
6
  "ember-addon",
@@ -39,7 +39,7 @@
39
39
  "@rdfjs/dataset": "^2.0.1",
40
40
  "@rdfjs/parser-n3": "^2.0.1",
41
41
  "@types/rdf-validate-shacl": "^0.4.0",
42
- "@types/rdfjs__parser-n3": "^1.1.5",
42
+ "@types/rdfjs__parser-n3": "^2.0.1",
43
43
  "buffer": "^6.0.3",
44
44
  "codemirror": "^6.0.1",
45
45
  "date-fns": "^2.29.3",
@@ -53,6 +53,7 @@
53
53
  "ember-resources": "^5.6.2",
54
54
  "ember-velcro": "^1.1.0",
55
55
  "fetch-sparql-endpoint": "^3.0.0",
56
+ "n2words": "^1.16.4",
56
57
  "process": "0.11.10",
57
58
  "rdf-ext": "^2.1.0",
58
59
  "rdf-validate-shacl": "^0.4.5",
@@ -137,12 +138,12 @@
137
138
  "loader.js": "^4.7.0",
138
139
  "npm-run-all": "^4.1.5",
139
140
  "prettier": "^2.7.1",
140
- "prosemirror-dev-tools": "^3.1.0",
141
+ "prosemirror-dev-tools": "^4.0.0",
141
142
  "qunit": "^2.19.2",
142
143
  "qunit-dom": "^2.0.0",
143
144
  "release-it": "^15.5.0",
144
145
  "sass": "^1.49.7",
145
- "typescript": "^4.9.3",
146
+ "typescript": "~5.0.4",
146
147
  "webpack": "^5.74.0"
147
148
  },
148
149
  "peerDependencies": {
@@ -150,6 +151,11 @@
150
151
  "@lblod/ember-rdfa-editor": "^3.8.0",
151
152
  "ember-concurrency": "^2.3.7"
152
153
  },
154
+ "overrides": {
155
+ "ember-intl": {
156
+ "typescript": "^5.0.4"
157
+ }
158
+ },
153
159
  "engines": {
154
160
  "node": ">= 18"
155
161
  },
@@ -1,6 +1,4 @@
1
- import { htmlSafe } from '@ember/template';
2
1
  import { Option } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/option';
3
- type SafeString = ReturnType<typeof htmlSafe>;
4
2
  interface DecisionArgs {
5
3
  uri: string;
6
4
  legislationTypeUri: Option<string>;
@@ -30,7 +28,7 @@ interface ArticleArgs {
30
28
  export declare class Article {
31
29
  uri: string;
32
30
  number?: number;
33
- content: SafeString | null;
31
+ content: string | null;
34
32
  dateInForce: string | null;
35
33
  dateNoLongerInForce: string | null;
36
34
  constructor({ uri, number, content, dateInForce, dateNoLongerInForce, }: ArticleArgs);
@@ -1,13 +1,17 @@
1
- import { PNode } from '@lblod/ember-rdfa-editor';
1
+ import { EmberNodeConfig } from '@lblod/ember-rdfa-editor/utils/ember-node';
2
+ import { Attrs, DOMOutputSpec, PNode } from '@lblod/ember-rdfa-editor';
3
+ export declare const CONTENT_SELECTOR: string;
2
4
  export declare const getHTMLNodeExtraAttributes: ({ node, type, }: {
3
5
  node: HTMLElement;
4
6
  type: string;
5
7
  }) => {
6
8
  minimumValue: string | null;
7
9
  maximumValue: string | null;
10
+ writtenNumber: boolean;
8
11
  } | {
9
12
  minimumValue?: undefined;
10
13
  maximumValue?: undefined;
14
+ writtenNumber?: undefined;
11
15
  };
12
16
  export declare const getPNodeExtraAttributes: ({ node, type, }: {
13
17
  node: PNode;
@@ -15,9 +19,19 @@ export declare const getPNodeExtraAttributes: ({ node, type, }: {
15
19
  }) => {
16
20
  "data-minimum-value": string;
17
21
  "data-maximum-value": string;
22
+ "data-written-number": string;
18
23
  } | {
19
24
  "data-minimum-value"?: undefined;
20
25
  "data-maximum-value"?: undefined;
26
+ "data-written-number"?: undefined;
21
27
  };
28
+ export declare const contentToDom: ({ content, type, node, }: {
29
+ content: string;
30
+ type: string;
31
+ node: PNode;
32
+ }) => string;
33
+ export declare const parseAttributes: (node: HTMLElement) => false | Attrs;
34
+ export declare const attributesToDOM: (node: PNode, content?: null) => DOMOutputSpec;
35
+ export declare const emberNodeConfig: EmberNodeConfig;
22
36
  export declare const variable: import("prosemirror-model").NodeSpec;
23
37
  export declare const variableView: (controller: import("@lblod/ember-rdfa-editor").SayController) => import("prosemirror-view").NodeViewConstructor;
@@ -0,0 +1,2 @@
1
+ export declare const number: import("prosemirror-model").NodeSpec;
2
+ export declare const numberView: (controller: import("@lblod/ember-rdfa-editor").SayController) => import("prosemirror-view").NodeViewConstructor;
@@ -14,5 +14,7 @@ export type VariableType = {
14
14
  export declare const DEFAULT_VARIABLE_TYPES: Record<string, VariableType>;
15
15
  export declare const MINIMUM_VALUE_PNODE_KEY = "minimumValue";
16
16
  export declare const MAXIMUM_VALUE_PNODE_KEY = "maximumValue";
17
+ export declare const WRITTEN_NUMBER_PNODE_KEY = "writtenNumber";
17
18
  export declare const MINIMUM_VALUE_HTML_ATTRIBUTE_KEY = "data-minimum-value";
18
19
  export declare const MAXIMUM_VALUE_HTML_ATTRIBUTE_KEY = "data-maximum-value";
20
+ export declare const WRITTEN_NUMBER_HTML_ATTRIBUTE_KEY = "data-written-number";
@@ -179,6 +179,16 @@ date-plugin:
179
179
  required: 'This field cannot be empty'
180
180
  fractions: 'Fractions of a second, such as S or T, are not supported.'
181
181
  unknown: 'Invalid format'
182
+ variable:
183
+ number:
184
+ placeholder: number
185
+ type-number: Type a number...
186
+ error-not-integer: Only integers are allowed
187
+ error-not-number: Only numbers are allowed
188
+ error-number-between: The number should be between {minValue} and {maxValue}
189
+ error-number-above: The number should be bigger than {minValue}
190
+ error-number-below: The number should be smaller than {maxValue}
191
+ written-number-label: Show as words
182
192
  variable-plugin:
183
193
  insert-variable: Insert variable
184
194
  button: Insert
@@ -94,7 +94,7 @@ citaten-plugin:
94
94
  details: Details
95
95
  refer-whole: Verwijs naar volledig document
96
96
  refer-article: Verwijs naar artikel
97
- lookup-codex: Bekijk in Vlaams Codex
97
+ lookup-codex: Bekijk in Vlaamse Codex
98
98
  alert:
99
99
  loading: Laden…
100
100
  no-results: Geen resultaten gevonden
@@ -191,7 +191,16 @@ table-of-contents-plugin:
191
191
  title: Inhoudstafel
192
192
  toggle: Toon Inhoudstafel
193
193
 
194
-
194
+ variable:
195
+ number:
196
+ placeholder: nummer
197
+ type-number: typ een nummer...
198
+ error-not-integer: Enkel gehele getallen zijn toegelaten
199
+ error-not-number: Enkel getallen zijn toegelaten
200
+ error-number-between: Het getal moet tussen {minValue} en {maxValue} liggen
201
+ error-number-above: Het getal moet groter dan {minValue} zijn
202
+ error-number-below: Het getal moet kleiner dan {maxValue} zijn
203
+ written-number-label: Getal voluit schrijven
195
204
  variable-plugin:
196
205
  insert-variable: Voeg variabele in
197
206
  button: Voeg in
@@ -223,4 +232,4 @@ generic-rdfa-variable:
223
232
  insert: RDFA invoegen
224
233
  modal:
225
234
  insert: Invoegen
226
- cancel: Terug
235
+ cancel: Terug
package/types/global.d.ts CHANGED
@@ -5,3 +5,16 @@ declare module '@lblod/ember-rdfa-editor-lblod-plugins/templates/*' {
5
5
  const tmpl: TemplateFactory;
6
6
  export default tmpl;
7
7
  }
8
+ declare module 'n2words' {
9
+ export default function n2words(
10
+ number: number,
11
+ options: { lang: string }
12
+ ): string;
13
+ }
14
+
15
+ declare module 'tracked-toolbox' {
16
+ export function localCopy<C extends Component = Component, T = unknown>(
17
+ memo: UsefulPropsOf<C>,
18
+ initializer?: T | (() => T)
19
+ ): PropertyDecorator;
20
+ }
@@ -1,22 +0,0 @@
1
- pipeline:
2
- install:
3
- image: danlynn/ember-cli:4.8.0
4
- commands:
5
- - npm ci
6
- release:
7
- image: plugins/npm
8
- settings:
9
- token:
10
- from_secret: npm_access_token
11
- push-tagged-build:
12
- image: plugins/docker
13
- settings:
14
- repo: lblod/ember-rdfa-editor-lblod-plugins
15
- tags: "${CI_COMMIT_TAG##v}"
16
- purge: true
17
- secrets: [ docker_username, docker_password ]
18
- # Pipeline level conditions aren't supported yet:
19
- # https://github.com/woodpecker-ci/woodpecker/issues/283
20
- when:
21
- event: tag
22
- tag: v*