@defra/forms-model 3.0.161 → 3.0.162

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/forms-model",
3
- "version": "3.0.161",
3
+ "version": "3.0.162",
4
4
  "description": "A hapi plugin providing the model for Defra forms",
5
5
  "homepage": "https://github.com/DEFRA/forms-designer/tree/main/model#readme",
6
6
  "repository": {
@@ -1,553 +0,0 @@
1
- import { ComponentTypes } from '~/src/components/component-types.js'
2
- import { getExpression } from '~/src/conditions/inline-condition-operators.js'
3
- import {
4
- AbstractConditionValue,
5
- valueFrom
6
- } from '~/src/conditions/inline-condition-values.js'
7
-
8
- export const coordinators = {
9
- AND: 'and',
10
- OR: 'or'
11
- }
12
-
13
- export class ConditionsModel {
14
- #groupedConditions
15
- #userGroupedConditions
16
- #conditionName
17
-
18
- constructor() {
19
- this.#groupedConditions = []
20
- this.#userGroupedConditions = []
21
- }
22
-
23
- clone() {
24
- const toReturn = new ConditionsModel()
25
- toReturn.#groupedConditions = this.#groupedConditions.map((it) =>
26
- it.clone()
27
- )
28
- toReturn.#userGroupedConditions = this.#userGroupedConditions.map((it) =>
29
- it.clone()
30
- )
31
- toReturn.#conditionName = this.#conditionName
32
- return toReturn
33
- }
34
-
35
- clear() {
36
- this.#userGroupedConditions = []
37
- this.#groupedConditions = []
38
- this.#conditionName = undefined
39
- return this
40
- }
41
-
42
- set name(name) {
43
- this.#conditionName = name
44
- }
45
-
46
- get name() {
47
- return this.#conditionName
48
- }
49
-
50
- add(condition) {
51
- const coordinatorExpected = this.#userGroupedConditions.length !== 0
52
- if (condition.getCoordinator() && !coordinatorExpected) {
53
- throw Error('No coordinator allowed on the first condition')
54
- } else if (!condition.getCoordinator() && coordinatorExpected) {
55
- throw Error('Coordinator must be present on subsequent conditions')
56
- }
57
- this.#userGroupedConditions.push(condition)
58
- this.#groupedConditions = this._applyGroups(this.#userGroupedConditions)
59
- return this
60
- }
61
-
62
- replace(index, condition) {
63
- const coordinatorExpected = index !== 0
64
- if (condition.getCoordinator() && !coordinatorExpected) {
65
- throw Error('No coordinator allowed on the first condition')
66
- } else if (!condition.getCoordinator() && coordinatorExpected) {
67
- throw Error('Coordinator must be present on subsequent conditions')
68
- } else if (index >= this.#userGroupedConditions.length) {
69
- throw Error(
70
- `Cannot replace condition ${index} as no such condition exists`
71
- )
72
- }
73
- this.#userGroupedConditions.splice(index, 1, condition)
74
- this.#groupedConditions = this._applyGroups(this.#userGroupedConditions)
75
- return this
76
- }
77
-
78
- remove(indexes) {
79
- this.#userGroupedConditions = this.#userGroupedConditions
80
- .filter((_condition, index) => !indexes.includes(index))
81
- .map((condition, index) =>
82
- index === 0 ? condition.asFirstCondition() : condition
83
- )
84
-
85
- this.#groupedConditions = this._applyGroups(this.#userGroupedConditions)
86
- return this
87
- }
88
-
89
- addGroups(groupDefs) {
90
- this.#userGroupedConditions = this._group(
91
- this.#userGroupedConditions,
92
- groupDefs
93
- )
94
- this.#groupedConditions = this._applyGroups(this.#userGroupedConditions)
95
- return this
96
- }
97
-
98
- splitGroup(index) {
99
- this.#userGroupedConditions = this._ungroup(
100
- this.#userGroupedConditions,
101
- index
102
- )
103
- this.#groupedConditions = this._applyGroups(this.#userGroupedConditions)
104
- return this
105
- }
106
-
107
- moveEarlier(index) {
108
- if (index > 0 && index < this.#userGroupedConditions.length) {
109
- this.#userGroupedConditions.splice(
110
- index - 1,
111
- 0,
112
- this.#userGroupedConditions.splice(index, 1)[0]
113
- )
114
- if (index === 1) {
115
- this.switchCoordinators()
116
- }
117
- this.#groupedConditions = this._applyGroups(this.#userGroupedConditions)
118
- }
119
- return this
120
- }
121
-
122
- moveLater(index) {
123
- if (index >= 0 && index < this.#userGroupedConditions.length - 1) {
124
- this.#userGroupedConditions.splice(
125
- index + 1,
126
- 0,
127
- this.#userGroupedConditions.splice(index, 1)[0]
128
- )
129
- if (index === 0) {
130
- this.switchCoordinators()
131
- }
132
- this.#groupedConditions = this._applyGroups(this.#userGroupedConditions)
133
- }
134
- return this
135
- }
136
-
137
- switchCoordinators() {
138
- this.#userGroupedConditions[1].setCoordinator(
139
- this.#userGroupedConditions[0].getCoordinator()
140
- )
141
- this.#userGroupedConditions[0].setCoordinator(undefined)
142
- }
143
-
144
- get asPerUserGroupings() {
145
- return [...this.#userGroupedConditions]
146
- }
147
-
148
- get hasConditions() {
149
- return this.#userGroupedConditions.length > 0
150
- }
151
-
152
- get lastIndex() {
153
- return this.#userGroupedConditions.length - 1
154
- }
155
-
156
- toPresentationString() {
157
- return this.#groupedConditions
158
- .map((condition) => toPresentationString(condition))
159
- .join(' ')
160
- }
161
-
162
- toExpression() {
163
- return this.#groupedConditions
164
- .map((condition) => toExpression(condition))
165
- .join(' ')
166
- }
167
-
168
- _applyGroups(userGroupedConditions) {
169
- const correctedUserGroups = userGroupedConditions.map((condition) =>
170
- condition instanceof ConditionGroup && condition.conditions.length > 2
171
- ? new ConditionGroup(
172
- this._group(
173
- condition.conditions,
174
- this._autoGroupDefs(condition.conditions)
175
- )
176
- )
177
- : condition
178
- )
179
- return this._group(
180
- correctedUserGroups,
181
- this._autoGroupDefs(correctedUserGroups)
182
- )
183
- }
184
-
185
- _group(conditions, groupDefs) {
186
- return conditions.reduce((groups, condition, index, conditions) => {
187
- const groupDef = groupDefs.find((groupDef) => groupDef.contains(index))
188
- if (groupDef) {
189
- if (groupDef.startsWith(index)) {
190
- const groupConditions = groupDef.applyTo(conditions)
191
- groups.push(new ConditionGroup(groupConditions))
192
- }
193
- } else {
194
- groups.push(condition)
195
- }
196
- return groups
197
- }, [])
198
- }
199
-
200
- _ungroup(conditions, splitIndex) {
201
- if (conditions[splitIndex].isGroup()) {
202
- const copy = [...conditions]
203
- copy.splice(
204
- splitIndex,
205
- 1,
206
- ...conditions[splitIndex].getGroupedConditions()
207
- )
208
- return copy
209
- }
210
- return conditions
211
- }
212
-
213
- _autoGroupDefs(conditions) {
214
- const orPositions: number[] = []
215
- conditions.forEach((condition, index) => {
216
- if (condition.getCoordinator() === coordinators.OR) {
217
- orPositions.push(index)
218
- }
219
- })
220
- const hasAnd = !!conditions.find(
221
- (condition) => condition.getCoordinator() === coordinators.AND
222
- )
223
- const hasOr = orPositions.length > 0
224
- if (hasAnd && hasOr) {
225
- let start = 0
226
- const groupDefs: GroupDef[] = []
227
- orPositions.forEach((position, index) => {
228
- if (start < position - 1) {
229
- groupDefs.push(new GroupDef(start, position - 1))
230
- }
231
- const thisIsTheLastOr = orPositions.length === index + 1
232
- const thereAreMoreConditions = conditions.length - 1 > position
233
- if (thisIsTheLastOr && thereAreMoreConditions) {
234
- groupDefs.push(new GroupDef(position, conditions.length - 1))
235
- }
236
- start = position
237
- })
238
- return groupDefs
239
- }
240
- return []
241
- }
242
-
243
- toJSON() {
244
- const name = this.#conditionName
245
- const conditions = this.#userGroupedConditions
246
- return {
247
- name,
248
- conditions: conditions.map((it) => it.clone())
249
- }
250
- }
251
-
252
- static from(obj) {
253
- if (obj instanceof ConditionsModel) {
254
- return obj
255
- }
256
- const toReturn = new ConditionsModel()
257
- toReturn.#conditionName = obj.name
258
- toReturn.#userGroupedConditions = obj.conditions.map((it) =>
259
- conditionFrom(it)
260
- )
261
- toReturn.#groupedConditions = toReturn._applyGroups(
262
- toReturn.#userGroupedConditions
263
- )
264
- return toReturn
265
- }
266
- }
267
-
268
- function conditionFrom(it) {
269
- if (it.conditions) {
270
- return new ConditionGroup(
271
- it.conditions.map((condition) => conditionFrom(condition))
272
- )
273
- }
274
- if (it.conditionName) {
275
- return new ConditionRef(
276
- it.conditionName,
277
- it.conditionDisplayName,
278
- it.coordinator
279
- )
280
- }
281
- return new Condition(
282
- Field.from(it.field),
283
- it.operator,
284
- valueFrom(it.value),
285
- it.coordinator
286
- )
287
- }
288
-
289
- export class GroupDef {
290
- first
291
- last
292
-
293
- constructor(first, last) {
294
- if (typeof first !== 'number' || typeof last !== 'number') {
295
- throw Error(`Cannot construct a group from ${first} and ${last}`)
296
- } else if (first >= last) {
297
- throw Error(`Last (${last}) must be greater than first (${first})`)
298
- }
299
- this.first = first
300
- this.last = last
301
- }
302
-
303
- contains(index) {
304
- return this.first <= index && this.last >= index
305
- }
306
-
307
- startsWith(index) {
308
- return this.first === index
309
- }
310
-
311
- applyTo(conditions) {
312
- return [...conditions].splice(this.first, this.last - this.first + 1)
313
- }
314
- }
315
-
316
- class ConditionGroup {
317
- conditions
318
-
319
- constructor(conditions) {
320
- if (!Array.isArray(conditions) || conditions.length < 2) {
321
- throw Error('Cannot construct a condition group from a single condition')
322
- }
323
- this.conditions = conditions
324
- }
325
-
326
- coordinatorString() {
327
- return this.conditions[0].coordinatorString()
328
- }
329
-
330
- conditionString() {
331
- const copy = [...this.conditions]
332
- copy.splice(0, 1)
333
- return `(${this.conditions[0].conditionString()} ${copy
334
- .map((condition) => toPresentationString(condition))
335
- .join(' ')})`
336
- }
337
-
338
- conditionExpression() {
339
- const copy = [...this.conditions]
340
- copy.splice(0, 1)
341
- return `(${this.conditions[0].conditionExpression()} ${copy
342
- .map((condition) => toExpression(condition))
343
- .join(' ')})`
344
- }
345
-
346
- asFirstCondition() {
347
- this.conditions[0].asFirstCondition()
348
- return this
349
- }
350
-
351
- getCoordinator() {
352
- return this.conditions[0].getCoordinator()
353
- }
354
-
355
- setCoordinator(coordinator) {
356
- this.conditions[0].setCoordinator(coordinator)
357
- }
358
-
359
- isGroup() {
360
- return true
361
- }
362
-
363
- getGroupedConditions() {
364
- return this.conditions.map((condition) => condition.clone())
365
- }
366
-
367
- clone() {
368
- return new ConditionGroup(
369
- this.conditions.map((condition) => condition.clone())
370
- )
371
- }
372
- }
373
-
374
- export function toPresentationString(condition) {
375
- return `${condition.coordinatorString()}${condition.conditionString()}`
376
- }
377
-
378
- export function toExpression(condition) {
379
- return `${condition.coordinatorString()}${condition.conditionExpression()}`
380
- }
381
-
382
- export class Field {
383
- name
384
- type
385
- display
386
-
387
- constructor(name, type, display) {
388
- if (!name || typeof name !== 'string') {
389
- throw Error(`name ${name} is not valid`)
390
- }
391
- if (!ComponentTypes.find((componentType) => componentType.name === type)) {
392
- throw Error(`type ${type} is not valid`)
393
- }
394
- if (!display || typeof display !== 'string') {
395
- throw Error(`display ${display} is not valid`)
396
- }
397
- this.name = name
398
- this.type = type
399
- this.display = display
400
- }
401
-
402
- static from(obj) {
403
- return new Field(obj.name, obj.type, obj.display)
404
- }
405
- }
406
-
407
- class AbstractCondition {
408
- coordinator
409
-
410
- constructor(coordinator) {
411
- if (coordinator && !Object.values(coordinators).includes(coordinator)) {
412
- throw Error(`coordinator ${coordinator} is not a valid coordinator`)
413
- }
414
- this.coordinator = coordinator
415
- }
416
-
417
- coordinatorString() {
418
- return this.coordinator ? `${this.coordinator} ` : ''
419
- }
420
-
421
- getCoordinator() {
422
- return this.coordinator
423
- }
424
-
425
- setCoordinator(coordinator) {
426
- this.coordinator = coordinator
427
- }
428
-
429
- isGroup() {
430
- return false
431
- }
432
-
433
- getGroupedConditions() {
434
- return [this]
435
- }
436
-
437
- _asFirstCondition() {
438
- delete this.coordinator
439
- }
440
-
441
- asFirstCondition() {
442
- throw Error(
443
- 'Implement on the subclass (Why do we have to have this method here at all?!)'
444
- )
445
- }
446
-
447
- clone() {
448
- throw Error(
449
- 'Implement on the subclass (Why do we have to have this method here at all?!)'
450
- )
451
- }
452
-
453
- conditionString() {
454
- throw Error(
455
- 'Implement on the subclass (Why do we have to have this method here at all?!)'
456
- )
457
- }
458
-
459
- conditionExpression() {
460
- throw Error(
461
- 'Implement on the subclass (Why do we have to have this method here at all?!)'
462
- )
463
- }
464
- }
465
-
466
- export class Condition extends AbstractCondition {
467
- field
468
- operator
469
- value
470
-
471
- constructor(field, operator, value, coordinator) {
472
- super(coordinator)
473
- if (!(field instanceof Field)) {
474
- throw Error(`field ${field} is not a valid Field object`)
475
- }
476
- if (typeof operator !== 'string') {
477
- throw Error(`operator ${operator} is not a valid operator`)
478
- }
479
- if (!(value instanceof AbstractConditionValue)) {
480
- throw Error(`value ${value} is not a valid value type`)
481
- }
482
- this.field = field
483
- this.operator = operator
484
- this.value = value
485
- }
486
-
487
- asFirstCondition() {
488
- this._asFirstCondition()
489
- return this
490
- }
491
-
492
- conditionString() {
493
- return `'${this.field.display}' ${
494
- this.operator
495
- } '${this.value.toPresentationString()}'`
496
- }
497
-
498
- conditionExpression() {
499
- return getExpression(
500
- this.field.type,
501
- this.field.name,
502
- this.operator,
503
- this.value
504
- )
505
- }
506
-
507
- clone() {
508
- return new Condition(
509
- Field.from(this.field),
510
- this.operator,
511
- this.value.clone(),
512
- this.coordinator
513
- )
514
- }
515
- }
516
-
517
- export class ConditionRef extends AbstractCondition {
518
- conditionName
519
- conditionDisplayName
520
-
521
- constructor(conditionName, conditionDisplayName, coordinator) {
522
- super(coordinator)
523
- if (typeof conditionName !== 'string') {
524
- throw Error(`condition name ${conditionName} is not valid`)
525
- }
526
- if (typeof conditionDisplayName !== 'string') {
527
- throw Error(`condition display name ${conditionDisplayName} is not valid`)
528
- }
529
- this.conditionName = conditionName
530
- this.conditionDisplayName = conditionDisplayName
531
- }
532
-
533
- asFirstCondition() {
534
- this._asFirstCondition()
535
- return this
536
- }
537
-
538
- conditionString() {
539
- return `'${this.conditionDisplayName}'`
540
- }
541
-
542
- conditionExpression() {
543
- return this.conditionName
544
- }
545
-
546
- clone() {
547
- return new ConditionRef(
548
- this.conditionName,
549
- this.conditionDisplayName,
550
- this.coordinator
551
- )
552
- }
553
- }
@@ -1,166 +0,0 @@
1
- import {
2
- ConditionValue,
3
- dateDirections,
4
- dateTimeUnits,
5
- dateUnits,
6
- RelativeTimeValue,
7
- timeUnits
8
- } from '~/src/conditions/inline-condition-values.js'
9
-
10
- const defaultOperators = {
11
- is: inline('=='),
12
- 'is not': inline('!=')
13
- }
14
-
15
- function withDefaults(param) {
16
- return Object.assign({}, param, defaultOperators)
17
- }
18
-
19
- const textBasedFieldCustomisations = {
20
- 'is longer than': lengthIs('>'),
21
- 'is shorter than': lengthIs('<'),
22
- 'has length': lengthIs('==')
23
- }
24
-
25
- const absoluteDateTimeOperators = {
26
- is: absoluteDateTime('=='),
27
- 'is not': absoluteDateTime('!='),
28
- 'is before': absoluteDateTime('<'),
29
- 'is after': absoluteDateTime('>')
30
- }
31
-
32
- const relativeTimeOperators = (units) => ({
33
- 'is at least': relativeTime('<=', '>=', units),
34
- 'is at most': relativeTime('>=', '<=', units),
35
- 'is less than': relativeTime('>', '<', units),
36
- 'is more than': relativeTime('<', '>', units)
37
- })
38
-
39
- export const customOperators = {
40
- CheckboxesField: {
41
- contains: reverseInline('in'),
42
- 'does not contain': not(reverseInline('in'))
43
- },
44
- NumberField: withDefaults({
45
- 'is at least': inline('>='),
46
- 'is at most': inline('<='),
47
- 'is less than': inline('<'),
48
- 'is more than': inline('>')
49
- }),
50
- DateField: Object.assign(
51
- {},
52
- absoluteDateTimeOperators,
53
- relativeTimeOperators(dateUnits)
54
- ),
55
- TimeField: Object.assign(
56
- {},
57
- absoluteDateTimeOperators,
58
- relativeTimeOperators(timeUnits)
59
- ),
60
- DatePartsField: Object.assign(
61
- {},
62
- absoluteDateTimeOperators,
63
- relativeTimeOperators(dateUnits)
64
- ),
65
- DateTimeField: Object.assign(
66
- {},
67
- absoluteDateTimeOperators,
68
- relativeTimeOperators(dateTimeUnits)
69
- ),
70
- DateTimePartsField: Object.assign(
71
- {},
72
- absoluteDateTimeOperators,
73
- relativeTimeOperators(dateTimeUnits)
74
- ),
75
- TextField: withDefaults(textBasedFieldCustomisations),
76
- MultilineTextField: withDefaults(textBasedFieldCustomisations),
77
- EmailAddressField: withDefaults(textBasedFieldCustomisations)
78
- }
79
-
80
- export function getOperatorNames(fieldType) {
81
- return Object.keys(getConditionals(fieldType)).sort()
82
- }
83
-
84
- export function getExpression(fieldType, fieldName, operator, value) {
85
- return getConditionals(fieldType)[operator].expression(
86
- { type: fieldType, name: fieldName },
87
- value
88
- )
89
- }
90
-
91
- export function getOperatorConfig(fieldType, operator) {
92
- return getConditionals(fieldType)[operator]
93
- }
94
-
95
- function getConditionals(fieldType) {
96
- return customOperators[fieldType] || defaultOperators
97
- }
98
-
99
- function inline(operator) {
100
- return {
101
- expression: (field, value) =>
102
- `${field.name} ${operator} ${formatValue(field.type, value.value)}`
103
- }
104
- }
105
-
106
- function lengthIs(operator) {
107
- return {
108
- expression: (field, value) =>
109
- `length(${field.name}) ${operator} ${value.value}`
110
- }
111
- }
112
-
113
- function reverseInline(operator) {
114
- return {
115
- expression: (field, value) =>
116
- `${formatValue(field.type, value.value)} ${operator} ${field.name}`
117
- }
118
- }
119
-
120
- function not(operatorDefinition) {
121
- return {
122
- expression: (field, value) =>
123
- `not (${operatorDefinition.expression(field, value)})`
124
- }
125
- }
126
-
127
- function formatValue(fieldType, value) {
128
- if (fieldType === 'NumberField' || fieldType === 'YesNoField') {
129
- return value
130
- }
131
- return `'${value}'`
132
- }
133
-
134
- export const absoluteDateOrTimeOperatorNames = Object.keys(
135
- absoluteDateTimeOperators
136
- )
137
- export const relativeDateOrTimeOperatorNames = Object.keys(
138
- relativeTimeOperators(dateTimeUnits)
139
- )
140
-
141
- function absoluteDateTime(operator) {
142
- return {
143
- expression: (field, value) => {
144
- if (value instanceof ConditionValue) {
145
- return `${field.name} ${operator} '${value.toExpression()}'`
146
- }
147
- throw Error('only Value types are supported')
148
- }
149
- }
150
- }
151
-
152
- function relativeTime(pastOperator, futureOperator, units) {
153
- return {
154
- units,
155
- expression: (field, value) => {
156
- if (value instanceof RelativeTimeValue) {
157
- const operator =
158
- value.direction === dateDirections.PAST
159
- ? pastOperator
160
- : futureOperator
161
- return `${field.name} ${operator} ${value.toExpression()}`
162
- }
163
- throw Error('time shift requires a TimeShiftValue')
164
- }
165
- }
166
- }
@@ -1,153 +0,0 @@
1
- const conditionValueFactories = {}
2
-
3
- class Registration {
4
- type
5
-
6
- constructor(type, factory) {
7
- conditionValueFactories[type] = factory
8
- this.type = type
9
- }
10
- }
11
-
12
- export class AbstractConditionValue {
13
- type
14
-
15
- constructor(registration) {
16
- if (new.target === AbstractConditionValue) {
17
- throw new TypeError('Cannot construct ConditionValue instances directly')
18
- }
19
- if (!(registration instanceof Registration)) {
20
- throw new TypeError(
21
- 'You must register your value type! Call registerValueType!'
22
- )
23
- }
24
- this.type = registration.type
25
- }
26
-
27
- toPresentationString() {}
28
- toExpression() {}
29
- }
30
-
31
- const valueType = registerValueType('Value', (obj) => ConditionValue.from(obj))
32
- export class ConditionValue extends AbstractConditionValue {
33
- value
34
- display
35
-
36
- constructor(value, display) {
37
- super(valueType)
38
- if (!value || typeof value !== 'string') {
39
- throw Error(`value ${value} is not valid`)
40
- }
41
- if (display && typeof display !== 'string') {
42
- throw Error(`display ${display} is not valid`)
43
- }
44
- this.value = value
45
- this.display = display || value
46
- }
47
-
48
- toPresentationString() {
49
- return this.display
50
- }
51
-
52
- toExpression() {
53
- return this.value
54
- }
55
-
56
- static from(obj) {
57
- return new ConditionValue(obj.value, obj.display)
58
- }
59
-
60
- clone() {
61
- return ConditionValue.from(this)
62
- }
63
- }
64
-
65
- export const dateDirections = {
66
- FUTURE: 'in the future',
67
- PAST: 'in the past'
68
- }
69
-
70
- export const dateUnits = {
71
- YEARS: { display: 'year(s)', value: 'years' },
72
- MONTHS: { display: 'month(s)', value: 'months' },
73
- DAYS: { display: 'day(s)', value: 'days' }
74
- }
75
- export const timeUnits = {
76
- HOURS: { display: 'hour(s)', value: 'hours' },
77
- MINUTES: { display: 'minute(s)', value: 'minutes' },
78
- SECONDS: { display: 'second(s)', value: 'seconds' }
79
- }
80
- export const dateTimeUnits = Object.assign({}, dateUnits, timeUnits)
81
-
82
- export const relativeTimeValueType = registerValueType('RelativeTime', (obj) =>
83
- RelativeTimeValue.from(obj)
84
- )
85
- export class RelativeTimeValue extends AbstractConditionValue {
86
- timePeriod
87
- timeUnit
88
- direction
89
- timeOnly
90
-
91
- constructor(timePeriod, timeUnit, direction, timeOnly = false) {
92
- super(relativeTimeValueType)
93
- if (typeof timePeriod !== 'string') {
94
- throw Error(`time period ${timePeriod} is not valid`)
95
- }
96
- if (
97
- !Object.values(dateTimeUnits)
98
- .map((it) => it.value)
99
- .includes(timeUnit)
100
- ) {
101
- throw Error(`time unit ${timeUnit} is not valid`)
102
- }
103
- if (!Object.values(dateDirections).includes(direction)) {
104
- throw Error(`direction ${direction} is not valid`)
105
- }
106
- this.timePeriod = timePeriod
107
- this.timeUnit = timeUnit
108
- this.direction = direction
109
- this.timeOnly = timeOnly
110
- }
111
-
112
- toPresentationString() {
113
- return `${this.timePeriod} ${this.timeUnit} ${this.direction}`
114
- }
115
-
116
- toExpression() {
117
- const timePeriod =
118
- this.direction === dateDirections.PAST
119
- ? 0 - Number(this.timePeriod)
120
- : this.timePeriod
121
- return this.timeOnly
122
- ? `timeForComparison(${timePeriod}, '${this.timeUnit}')`
123
- : `dateForComparison(${timePeriod}, '${this.timeUnit}')`
124
- }
125
-
126
- static from(obj) {
127
- return new RelativeTimeValue(
128
- obj.timePeriod,
129
- obj.timeUnit,
130
- obj.direction,
131
- obj.timeOnly
132
- )
133
- }
134
-
135
- clone() {
136
- return RelativeTimeValue.from(this)
137
- }
138
- }
139
-
140
- /**
141
- * All value types should call this, and should be located in this file.
142
- * Furthermore the types should be registered without the classes needing to be instantiated.
143
- *
144
- * Otherwise we can't guarantee they've been registered for deserialization before
145
- * valueFrom is called
146
- */
147
- function registerValueType(type, factory) {
148
- return new Registration(type, factory)
149
- }
150
-
151
- export function valueFrom(obj) {
152
- return conditionValueFactories[obj.type](obj)
153
- }