mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 18:31:52 +00:00
update form to latest sdk
This commit is contained in:
committed by
Aiden McClelland
parent
f83ae27352
commit
94cdaf5314
10
frontend/package-lock.json
generated
10
frontend/package-lock.json
generated
@@ -49,7 +49,7 @@
|
||||
"patch-db-client": "file: ../../../patch-db/client",
|
||||
"pbkdf2": "^3.1.2",
|
||||
"rxjs": "^7.5.6",
|
||||
"start-sdk": "^0.4.0-lib0.charlie20",
|
||||
"start-sdk": "^0.4.0-lib0.charlie33",
|
||||
"swiper": "^8.2.4",
|
||||
"ts-matches": "^5.2.1",
|
||||
"tslib": "^2.3.0",
|
||||
@@ -6220,7 +6220,6 @@
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
||||
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -13733,11 +13732,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/start-sdk": {
|
||||
"version": "0.4.0-lib0.charlie20",
|
||||
"resolved": "https://registry.npmjs.org/start-sdk/-/start-sdk-0.4.0-lib0.charlie20.tgz",
|
||||
"integrity": "sha512-TzvD5rfDnHDqhv/R1bJG+Fg8zvSCJZ3QhmHrf868ZM54ABTh/5Qn7TRXdS0b5CgrhHy3/uwZi/5G3SyWNLrDoQ==",
|
||||
"version": "0.4.0-lib0.charlie33",
|
||||
"resolved": "https://registry.npmjs.org/start-sdk/-/start-sdk-0.4.0-lib0.charlie33.tgz",
|
||||
"integrity": "sha512-Lx3QAuRCZTA6zXdjsKN3LjCTY1NQgEHY9uOyxKSTnWtaVXdOSp1GqiZYs3c7/ZStPzzTZ19kSnCcVBE125lUHA==",
|
||||
"dependencies": {
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"deepmerge": "^4.3.1",
|
||||
"lodash": "^4.17.21",
|
||||
"ts-matches": "^5.4.1",
|
||||
"yaml": "^2.2.1"
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
"patch-db-client": "file: ../../../patch-db/client",
|
||||
"pbkdf2": "^3.1.2",
|
||||
"rxjs": "^7.5.6",
|
||||
"start-sdk": "^0.4.0-lib0.charlie20",
|
||||
"start-sdk": "^0.4.0-lib0.charlie33",
|
||||
"swiper": "^8.2.4",
|
||||
"ts-matches": "^5.2.1",
|
||||
"tslib": "^2.3.0",
|
||||
|
||||
@@ -276,53 +276,57 @@ export class BackupDrivesStatusComponent {
|
||||
|
||||
const CifsSpec: InputSpec = {
|
||||
hostname: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
name: 'Hostname',
|
||||
description:
|
||||
'The hostname of your target device on the Local Area Network.',
|
||||
inputmode: 'text',
|
||||
placeholder: `e.g. 'My Computer' OR 'my-computer.local'`,
|
||||
pattern: '^[a-zA-Z0-9._-]+( [a-zA-Z0-9]+)*$',
|
||||
patternDescription: `Must be a valid hostname. e.g. 'My Computer' OR 'my-computer.local'`,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
patterns: [],
|
||||
required: true,
|
||||
masked: false,
|
||||
default: null,
|
||||
warning: null,
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
name: 'Path',
|
||||
description: `On Windows, this is the fully qualified path to the shared folder, (e.g. /Desktop/my-folder).\n\n On Linux and Mac, this is the literal name of the shared folder (e.g. my-shared-folder).`,
|
||||
inputmode: 'text',
|
||||
placeholder: 'e.g. my-shared-folder or /Desktop/my-folder',
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
required: true,
|
||||
masked: false,
|
||||
default: null,
|
||||
warning: null,
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
name: 'Username',
|
||||
description: `On Linux, this is the samba username you created when sharing the folder.\n\n On Mac and Windows, this is the username of the user who is sharing the folder.`,
|
||||
inputmode: 'text',
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
required: true,
|
||||
masked: false,
|
||||
default: null,
|
||||
warning: null,
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
name: 'Password',
|
||||
description: `On Linux, this is the samba password you created when sharing the folder.\n\n On Mac and Windows, this is the password of the user who is sharing the folder.`,
|
||||
inputmode: 'text',
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
patterns: [],
|
||||
required: false,
|
||||
masked: true,
|
||||
default: null,
|
||||
|
||||
@@ -24,9 +24,12 @@
|
||||
type="text"
|
||||
class="input"
|
||||
[class.input_redacted]="
|
||||
spec.type === 'string' && control.value && spec.masked && !unmasked
|
||||
spec.type === 'text' && control.value && spec.masked && !unmasked
|
||||
"
|
||||
[inputmode]="spec.type === 'string' ? spec.inputmode : 'tel'"
|
||||
[inputmode]="spec.type === 'text' ? spec.inputmode : 'tel'"
|
||||
[minlength]="spec.type === 'number' ? null : spec.minLength"
|
||||
[maxlength]="spec.type === 'number' ? null : spec.maxLength"
|
||||
[step]="spec.type === 'number' ? spec.step : null"
|
||||
[placeholder]="spec.placeholder"
|
||||
[formControl]="control"
|
||||
(ionFocus)="warning.onChange(name, spec)"
|
||||
@@ -34,7 +37,7 @@
|
||||
></ion-input>
|
||||
</ng-template>
|
||||
<ion-button
|
||||
*ngIf="spec.type === 'string' && spec.masked"
|
||||
*ngIf="spec.type === 'text' && spec.masked"
|
||||
slot="end"
|
||||
fill="clear"
|
||||
color="light"
|
||||
|
||||
@@ -10,7 +10,7 @@ import { THEME } from '@start9labs/shared'
|
||||
})
|
||||
export class FormInputComponent {
|
||||
@Input() name!: string
|
||||
@Input() spec!: ValueSpecOf<'string' | 'textarea' | 'number'>
|
||||
@Input() spec!: ValueSpecOf<'text' | 'textarea' | 'number'>
|
||||
@Input() control!: FormControl
|
||||
|
||||
@Output() onInputChange = new EventEmitter<void>()
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
></form-label>
|
||||
<!-- boolean -->
|
||||
<ion-toggle
|
||||
*ngIf="spec.type === 'boolean'"
|
||||
*ngIf="spec.type === 'toggle'"
|
||||
formWarning
|
||||
#warning="formWarning"
|
||||
slot="end"
|
||||
|
||||
@@ -8,7 +8,7 @@ import { ValueSpecOf } from 'start-sdk/lib/config/configTypes'
|
||||
styleUrls: ['./form-select.component.scss'],
|
||||
})
|
||||
export class FormSelectComponent {
|
||||
@Input() spec!: ValueSpecOf<'boolean' | 'select' | 'multiselect'>
|
||||
@Input() spec!: ValueSpecOf<'toggle' | 'select' | 'multiselect'>
|
||||
@Input() control!: FormControl
|
||||
@Input() name!: string
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<!-- string or number -->
|
||||
<form-input
|
||||
*ngIf="
|
||||
spec.type === 'string' ||
|
||||
spec.type === 'text' ||
|
||||
spec.type === 'textarea' ||
|
||||
spec.type === 'number'
|
||||
"
|
||||
@@ -22,7 +22,7 @@
|
||||
<!-- boolean, select or multiselect -->
|
||||
<form-select
|
||||
*ngIf="
|
||||
spec.type === 'boolean' ||
|
||||
spec.type === 'toggle' ||
|
||||
spec.type === 'select' ||
|
||||
spec.type === 'multiselect'
|
||||
"
|
||||
@@ -67,7 +67,7 @@
|
||||
name: spec.name,
|
||||
description: spec.description || null,
|
||||
edited: entry.value.dirty,
|
||||
required: !!(spec.range | toRange).min
|
||||
required: !!spec.minLength
|
||||
}"
|
||||
></form-label>
|
||||
<ion-button
|
||||
@@ -153,9 +153,7 @@
|
||||
</ng-container>
|
||||
<!-- string or number -->
|
||||
<div
|
||||
*ngIf="
|
||||
spec.spec.type === 'string' || spec.spec.type === 'number'
|
||||
"
|
||||
*ngIf="spec.spec.type === 'text' || spec.spec.type === 'number'"
|
||||
[id]="objectId | toElementId : entry.key : i"
|
||||
>
|
||||
<ion-item
|
||||
@@ -164,7 +162,7 @@
|
||||
<ion-input
|
||||
type="text"
|
||||
[inputmode]="
|
||||
spec.spec.type === 'string' ? spec.spec.inputmode : 'tel'
|
||||
spec.spec.type === 'text' ? spec.spec.inputmode : 'tel'
|
||||
"
|
||||
[placeholder]="
|
||||
$any(spec.spec).placeholder || 'Enter ' + spec.name
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
InputSpec,
|
||||
ListValueSpecOf,
|
||||
ValueSpec,
|
||||
ValueSpecBoolean,
|
||||
ValueSpecToggle,
|
||||
ValueSpecList,
|
||||
ValueSpecUnion,
|
||||
} from 'start-sdk/lib/config/configTypes'
|
||||
@@ -135,7 +135,7 @@ export class FormObjectComponent {
|
||||
})
|
||||
}
|
||||
|
||||
handleBooleanChange(key: string, spec: ValueSpecBoolean) {
|
||||
handleBooleanChange(key: string, spec: ValueSpecToggle) {
|
||||
if (spec.warning) {
|
||||
const current = this.formGroup.get(key)?.value
|
||||
const cancelFn = () => this.formGroup.get(key)?.setValue(!current)
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ValueSpecBoolean } from 'start-sdk/lib/config/configTypes'
|
||||
import { Control } from '../control'
|
||||
|
||||
@Component({
|
||||
selector: 'form-boolean',
|
||||
templateUrl: './form-boolean.component.html',
|
||||
styleUrls: ['./form-boolean.component.scss'],
|
||||
})
|
||||
export class FormBooleanComponent extends Control<ValueSpecBoolean, boolean> {}
|
||||
@@ -1,8 +1,8 @@
|
||||
<ng-container [ngSwitch]="spec.type">
|
||||
<form-string *ngSwitchCase="'string'"></form-string>
|
||||
<form-text *ngSwitchCase="'text'"></form-text>
|
||||
<form-number *ngSwitchCase="'number'"></form-number>
|
||||
<form-text *ngSwitchCase="'textarea'"></form-text>
|
||||
<form-boolean *ngSwitchCase="'boolean'"></form-boolean>
|
||||
<form-toggle *ngSwitchCase="'toggle'"></form-toggle>
|
||||
<form-select *ngSwitchCase="'select'"></form-select>
|
||||
<form-multiselect *ngSwitchCase="'multiselect'"></form-multiselect>
|
||||
<form-file *ngSwitchCase="'file'"></form-file>
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
} from '@taiga-ui/core'
|
||||
import { TUI_VALIDATION_ERRORS } from '@taiga-ui/kit'
|
||||
import { filter, takeUntil } from 'rxjs'
|
||||
import { ValueSpec, ValueSpecString } from 'start-sdk/lib/config/configTypes'
|
||||
import { ValueSpec, ValueSpecText } from 'start-sdk/lib/config/configTypes'
|
||||
import { ERRORS } from '../form-group/form-group.component'
|
||||
|
||||
@Component({
|
||||
@@ -26,9 +26,9 @@ import { ERRORS } from '../form-group/form-group.component'
|
||||
{
|
||||
provide: TUI_VALIDATION_ERRORS,
|
||||
deps: [FormControlComponent],
|
||||
useFactory: (control: FormControlComponent<ValueSpecString, string>) => ({
|
||||
useFactory: (control: FormControlComponent<ValueSpecText, string>) => ({
|
||||
required: 'Required',
|
||||
pattern: () => control.spec.patternDescription,
|
||||
patterns: () => control.spec.patterns.map(p => p.regex), // @TODO Alex
|
||||
}),
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<!-- @TODO Alex look at "attr.step", is this correct? -->
|
||||
<tui-input-number
|
||||
[tuiHintContent]="spec.description"
|
||||
[tuiTextfieldPostfix]="spec.units || ''"
|
||||
[precision]="Infinity"
|
||||
[decimal]="spec.integral ? 'never' : 'not-zero'"
|
||||
[decimal]="spec.integer ? 'never' : 'not-zero'"
|
||||
[min]="min"
|
||||
[max]="max"
|
||||
[attr.step]="spec.step"
|
||||
[(ngModel)]="value"
|
||||
(focusedChange)="onFocus($event)"
|
||||
>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ValueSpecNumber } from 'start-sdk/lib/config/configTypes'
|
||||
import { Range } from 'src/app/util/config-utilities'
|
||||
import { Control } from '../control'
|
||||
|
||||
@Component({
|
||||
@@ -9,17 +8,14 @@ import { Control } from '../control'
|
||||
})
|
||||
export class FormNumberComponent extends Control<ValueSpecNumber, number> {
|
||||
protected readonly Infinity = Infinity
|
||||
private range = Range.from(this.spec.range)
|
||||
|
||||
get min(): number {
|
||||
const min = this.range.min || -Infinity
|
||||
|
||||
return this.range.minInclusive || !this.spec.integral ? min : min + 1
|
||||
if (typeof this.spec.min !== 'number') return -Infinity
|
||||
return this.spec.min
|
||||
}
|
||||
|
||||
get max(): number {
|
||||
const max = this.range.max || Infinity
|
||||
|
||||
return this.range.maxInclusive || !this.spec.integral ? max : max - 1
|
||||
if (typeof this.spec.max !== 'number') return Infinity
|
||||
return this.spec.max
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<tui-input
|
||||
[tuiTextfieldCustomContent]="spec.masked ? toggle : ''"
|
||||
[tuiHintContent]="spec.description"
|
||||
[(ngModel)]="value"
|
||||
(focusedChange)="onFocus($event)"
|
||||
>
|
||||
{{ spec.name }}
|
||||
<span *ngIf="spec.required">*</span>
|
||||
<input
|
||||
tuiTextfield
|
||||
[class.masked]="spec.masked && masked"
|
||||
[placeholder]="spec.placeholder || ''"
|
||||
[attr.inputmode]="spec.inputmode"
|
||||
/>
|
||||
</tui-input>
|
||||
<ng-template #toggle>
|
||||
<button
|
||||
tuiIconButton
|
||||
type="button"
|
||||
appearance="icon"
|
||||
title="Toggle masking"
|
||||
size="xs"
|
||||
class="toggle"
|
||||
[icon]="masked ? 'tuiIconEye' : 'tuiIconEyeOff'"
|
||||
(click)="masked = !masked"
|
||||
></button>
|
||||
</ng-template>
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ValueSpecString } from 'start-sdk/lib/config/configTypes'
|
||||
import { Control } from '../control'
|
||||
|
||||
@Component({
|
||||
selector: 'form-string',
|
||||
templateUrl: './form-string.component.html',
|
||||
styleUrls: ['./form-string.component.scss'],
|
||||
})
|
||||
export class FormStringComponent extends Control<ValueSpecString, string> {
|
||||
masked = true
|
||||
}
|
||||
@@ -1,11 +1,29 @@
|
||||
<tui-text-area
|
||||
<tui-input
|
||||
[tuiTextfieldCustomContent]="spec.masked ? toggle : ''"
|
||||
[tuiHintContent]="spec.description"
|
||||
[expandable]="true"
|
||||
[rows]="6"
|
||||
[(ngModel)]="value"
|
||||
(focusedChange)="onFocus($event)"
|
||||
>
|
||||
{{ spec.name }}
|
||||
<span *ngIf="spec.required">*</span>
|
||||
<textarea tuiTextfield [placeholder]="spec.placeholder || ''"></textarea>
|
||||
</tui-text-area>
|
||||
<input
|
||||
tuiTextfield
|
||||
[class.masked]="spec.masked && masked"
|
||||
[placeholder]="spec.placeholder || ''"
|
||||
[minLength]="spec.minLength"
|
||||
[maxLength]="spec.maxLength"
|
||||
[attr.inputmode]="spec.inputmode"
|
||||
/>
|
||||
</tui-input>
|
||||
<ng-template #toggle>
|
||||
<button
|
||||
tuiIconButton
|
||||
type="button"
|
||||
appearance="icon"
|
||||
title="Toggle masking"
|
||||
size="xs"
|
||||
class="toggle"
|
||||
[icon]="masked ? 'tuiIconEye' : 'tuiIconEyeOff'"
|
||||
(click)="masked = !masked"
|
||||
></button>
|
||||
</ng-template>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ValueSpecTextarea } from 'start-sdk/lib/config/configTypes'
|
||||
import { ValueSpecText } from 'start-sdk/lib/config/configTypes'
|
||||
import { Control } from '../control'
|
||||
|
||||
@Component({
|
||||
selector: 'form-text',
|
||||
templateUrl: './form-text.component.html',
|
||||
styleUrls: ['./form-text.component.scss'],
|
||||
})
|
||||
export class FormTextComponent extends Control<ValueSpecTextarea, string> {}
|
||||
export class FormTextComponent extends Control<ValueSpecText, string> {
|
||||
masked = true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<tui-text-area
|
||||
[tuiHintContent]="spec.description"
|
||||
[expandable]="true"
|
||||
[rows]="6"
|
||||
[(ngModel)]="value"
|
||||
(focusedChange)="onFocus($event)"
|
||||
>
|
||||
{{ spec.name }}
|
||||
<span *ngIf="spec.required">*</span>
|
||||
<textarea tuiTextfield [placeholder]="spec.placeholder || ''"></textarea>
|
||||
</tui-text-area>
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ValueSpecTextarea } from 'start-sdk/lib/config/configTypes'
|
||||
import { Control } from '../control'
|
||||
|
||||
@Component({
|
||||
selector: 'form-textarea',
|
||||
templateUrl: './form-textarea.component.html',
|
||||
})
|
||||
export class FormTextareaComponent extends Control<ValueSpecTextarea, string> {}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ValueSpecToggle } from 'start-sdk/lib/config/configTypes'
|
||||
import { Control } from '../control'
|
||||
|
||||
@Component({
|
||||
selector: 'form-toggle',
|
||||
templateUrl: './form-toggle.component.html',
|
||||
styleUrls: ['./form-toggle.component.scss'],
|
||||
})
|
||||
export class FormToggleComponent extends Control<ValueSpecToggle, boolean> {}
|
||||
@@ -27,9 +27,9 @@ import {
|
||||
} from '@taiga-ui/kit'
|
||||
|
||||
import { FormGroupComponent } from './form-group/form-group.component'
|
||||
import { FormStringComponent } from './form-string/form-string.component'
|
||||
import { FormBooleanComponent } from './form-boolean/form-boolean.component'
|
||||
import { FormTextComponent } from './form-text/form-text.component'
|
||||
import { FormToggleComponent } from './form-toggle/form-toggle.component'
|
||||
import { FormTextareaComponent } from './form-textarea/form-textarea.component'
|
||||
import { FormNumberComponent } from './form-number/form-number.component'
|
||||
import { FormSelectComponent } from './form-select/form-select.component'
|
||||
import { FormFileComponent } from './form-file/form-file.component'
|
||||
@@ -70,9 +70,9 @@ import { ControlDirective } from './control.directive'
|
||||
declarations: [
|
||||
FormGroupComponent,
|
||||
FormControlComponent,
|
||||
FormStringComponent,
|
||||
FormBooleanComponent,
|
||||
FormTextComponent,
|
||||
FormToggleComponent,
|
||||
FormTextareaComponent,
|
||||
FormNumberComponent,
|
||||
FormSelectComponent,
|
||||
FormMultiselectComponent,
|
||||
|
||||
@@ -270,14 +270,20 @@ function getMarketplaceValueSpec(): ValueSpecObject {
|
||||
warning: null,
|
||||
spec: {
|
||||
url: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
name: 'URL',
|
||||
description: 'A fully-qualified URL of the custom registry',
|
||||
inputmode: 'url',
|
||||
required: true,
|
||||
masked: false,
|
||||
pattern: `https?:\/\/[a-zA-Z0-9][a-zA-Z0-9-\.]+[a-zA-Z0-9]\.[^\s]{2,}`,
|
||||
patternDescription: 'Must be a valid URL',
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
patterns: [
|
||||
{
|
||||
regex: `https?:\/\/[a-zA-Z0-9][a-zA-Z0-9-\.]+[a-zA-Z0-9]\.[^\s]{2,}`,
|
||||
description: 'Must be a valid URL',
|
||||
},
|
||||
],
|
||||
placeholder: 'e.g. https://example.org',
|
||||
default: null,
|
||||
warning: null,
|
||||
|
||||
@@ -219,7 +219,7 @@ const SAMPLE_INSTUCTIONS = `# Create Instructions using Markdown! :)`
|
||||
|
||||
const SAMPLE_CONFIG: InputSpec = {
|
||||
'sample-string': {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
name: 'Example String Input',
|
||||
inputmode: 'text',
|
||||
required: true,
|
||||
@@ -227,8 +227,14 @@ const SAMPLE_CONFIG: InputSpec = {
|
||||
// optional
|
||||
description: 'Example description for required string input.',
|
||||
placeholder: 'Enter string value',
|
||||
pattern: '^[a-zA-Z0-9! _]+$',
|
||||
patternDescription: 'Must be alphanumeric (may contain underscore).',
|
||||
patterns: [
|
||||
{
|
||||
regex: '^[a-zA-Z0-9! _]+$',
|
||||
description: 'Must be alphanumeric (may contain underscore).',
|
||||
},
|
||||
],
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
default: null,
|
||||
warning: null,
|
||||
},
|
||||
@@ -236,8 +242,10 @@ const SAMPLE_CONFIG: InputSpec = {
|
||||
type: 'number',
|
||||
name: 'Example Number Input',
|
||||
required: true,
|
||||
range: '[5,1000000]',
|
||||
integral: true,
|
||||
min: 5,
|
||||
max: 1000000,
|
||||
step: '5',
|
||||
integer: true,
|
||||
// optional
|
||||
warning: 'Example warning to display when changing this number value.',
|
||||
units: 'ms',
|
||||
@@ -246,7 +254,7 @@ const SAMPLE_CONFIG: InputSpec = {
|
||||
default: null,
|
||||
},
|
||||
'sample-boolean': {
|
||||
type: 'boolean',
|
||||
type: 'toggle',
|
||||
name: 'Example Boolean Toggle',
|
||||
// optional
|
||||
description: 'Example description for boolean toggle',
|
||||
@@ -264,7 +272,8 @@ const SAMPLE_CONFIG: InputSpec = {
|
||||
// optional
|
||||
warning: 'Example warning to display when changing this select value.',
|
||||
description: 'Example description for select select',
|
||||
range: '[0, 2)',
|
||||
minLength: null,
|
||||
maxLength: 2,
|
||||
default: ['red'],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -21,33 +21,40 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
const basicInfo = devData['basic-info']
|
||||
return {
|
||||
id: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
name: 'ID',
|
||||
description: 'The package identifier used by the OS',
|
||||
placeholder: 'e.g. bitcoind',
|
||||
required: true,
|
||||
masked: false,
|
||||
pattern: '^([a-z][a-z0-9]*)(-[a-z0-9]+)*$',
|
||||
patternDescription: 'Must be kebab case',
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
patterns: [
|
||||
{
|
||||
regex: '^([a-z][a-z0-9]*)(-[a-z0-9]+)*$',
|
||||
description: 'Must be kebab case',
|
||||
},
|
||||
],
|
||||
default: basicInfo?.id || '',
|
||||
warning: null,
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
name: 'Service Name',
|
||||
description: 'A human readable service title',
|
||||
placeholder: 'e.g. Bitcoin Core',
|
||||
required: true,
|
||||
masked: false,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
patterns: [],
|
||||
default: basicInfo ? basicInfo.title : devData.name,
|
||||
warning: null,
|
||||
},
|
||||
'service-version-number': {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
name: 'Service Version',
|
||||
description:
|
||||
@@ -55,8 +62,14 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
placeholder: 'e.g. 0.1.2.3',
|
||||
required: true,
|
||||
masked: false,
|
||||
pattern: '^([0-9]+).([0-9]+).([0-9]+).([0-9]+)$',
|
||||
patternDescription: 'Must be valid Emver version',
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
patterns: [
|
||||
{
|
||||
regex: '^([0-9]+).([0-9]+).([0-9]+).([0-9]+)$',
|
||||
description: 'Must be valid Emver version',
|
||||
},
|
||||
],
|
||||
default: basicInfo?.['service-version-number'] || '',
|
||||
warning: null,
|
||||
},
|
||||
@@ -67,7 +80,7 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
warning: null,
|
||||
spec: {
|
||||
short: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
name: 'Short Description',
|
||||
description:
|
||||
@@ -76,8 +89,9 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
required: true,
|
||||
masked: false,
|
||||
default: basicInfo?.description?.short || '',
|
||||
pattern: '^.{1,320}$',
|
||||
patternDescription: 'Must be shorter than 320 characters',
|
||||
minLength: null,
|
||||
maxLength: 320,
|
||||
patterns: [],
|
||||
warning: null,
|
||||
},
|
||||
long: {
|
||||
@@ -85,22 +99,25 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
name: 'Long Description',
|
||||
description: `This description will display with additional details in the service's individual marketplace page`,
|
||||
placeholder: null,
|
||||
minLength: 20,
|
||||
maxLength: 1000,
|
||||
required: true,
|
||||
warning: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
'release-notes': {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
name: 'Release Notes',
|
||||
description:
|
||||
'Markdown supported release notes for this version of this service.',
|
||||
placeholder: 'e.g. Markdown _release notes_ for **Bitcoin Core**',
|
||||
required: true,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
masked: false,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
default: basicInfo?.['release-notes'] || '',
|
||||
warning: null,
|
||||
},
|
||||
@@ -124,53 +141,57 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
default: 'mit',
|
||||
},
|
||||
'wrapper-repo': {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'url',
|
||||
name: 'Wrapper Repo',
|
||||
description:
|
||||
'The Start9 wrapper repository URL for the package. This repo contains the manifest file (this), any scripts necessary for configuration, backups, actions, or health checks',
|
||||
placeholder: 'e.g. www.github.com/example',
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
required: true,
|
||||
masked: false,
|
||||
default: basicInfo?.['wrapper-repo'] || '',
|
||||
warning: null,
|
||||
},
|
||||
'upstream-repo': {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'url',
|
||||
name: 'Upstream Repo',
|
||||
description: 'The original project repository URL',
|
||||
placeholder: 'e.g. www.github.com/example',
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
required: false,
|
||||
masked: false,
|
||||
default: basicInfo?.['upstream-repo'] || '',
|
||||
warning: null,
|
||||
},
|
||||
'support-site': {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'url',
|
||||
name: 'Support Site',
|
||||
description: 'URL to the support site / channel for the project',
|
||||
placeholder: 'e.g. start9.com/support',
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
required: false,
|
||||
masked: false,
|
||||
default: basicInfo?.['support-site'] || '',
|
||||
warning: null,
|
||||
},
|
||||
'marketing-site': {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'url',
|
||||
name: 'Website',
|
||||
description: 'URL to the marketing site / channel for the project',
|
||||
placeholder: 'e.g. start9.com',
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
required: false,
|
||||
masked: false,
|
||||
default: basicInfo?.['marketing-site'] || '',
|
||||
|
||||
@@ -342,28 +342,35 @@ function getWifiValueSpec(
|
||||
warning: null,
|
||||
spec: {
|
||||
ssid: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
name: 'Network SSID',
|
||||
description: null,
|
||||
inputmode: 'text',
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
required: true,
|
||||
masked: false,
|
||||
default: ssid || null,
|
||||
warning: null,
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
name: 'Password',
|
||||
description: null,
|
||||
inputmode: 'text',
|
||||
placeholder: null,
|
||||
required: needsPW,
|
||||
masked: true,
|
||||
pattern: '^.{8,}$',
|
||||
patternDescription: 'Must be longer than 8 characters',
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
patterns: [
|
||||
{
|
||||
regex: '^.{8,}$',
|
||||
description: 'Must be longer than 8 characters',
|
||||
},
|
||||
],
|
||||
default: null,
|
||||
warning: null,
|
||||
},
|
||||
|
||||
@@ -656,7 +656,7 @@ export module Mock {
|
||||
version: 2,
|
||||
data: {
|
||||
lndconnect: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'This is some information about the thing.',
|
||||
copyable: true,
|
||||
@@ -670,7 +670,7 @@ export module Mock {
|
||||
description: 'This is a nested thing metric',
|
||||
value: {
|
||||
'Last Name': {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'The last name of the user',
|
||||
copyable: true,
|
||||
@@ -679,7 +679,7 @@ export module Mock {
|
||||
value: 'Hill',
|
||||
},
|
||||
Age: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'The age of the user',
|
||||
copyable: false,
|
||||
@@ -688,7 +688,7 @@ export module Mock {
|
||||
value: '35',
|
||||
},
|
||||
Password: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'A secret password',
|
||||
copyable: true,
|
||||
@@ -699,7 +699,7 @@ export module Mock {
|
||||
},
|
||||
},
|
||||
'Another Value': {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'Some more information about the service.',
|
||||
copyable: false,
|
||||
@@ -732,15 +732,16 @@ export module Mock {
|
||||
name: 'External',
|
||||
spec: {
|
||||
'p2p-host': {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
name: 'Public Address',
|
||||
description: 'The public address of your Bitcoin Core server',
|
||||
required: true,
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
minLength: 4,
|
||||
maxLength: 20,
|
||||
patterns: [],
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -750,8 +751,10 @@ export module Mock {
|
||||
description:
|
||||
'The port that your Bitcoin Core P2P server is bound to',
|
||||
required: true,
|
||||
range: '[0,65535]',
|
||||
integral: true,
|
||||
min: 0,
|
||||
max: 65535,
|
||||
integer: true,
|
||||
step: '1',
|
||||
default: 8333,
|
||||
placeholder: null,
|
||||
warning: null,
|
||||
@@ -778,36 +781,50 @@ export module Mock {
|
||||
spec: {
|
||||
rpcuser2: {
|
||||
name: 'RPC Username',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'rpc username',
|
||||
warning: null,
|
||||
placeholder: null,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
required: true,
|
||||
default: 'defaultrpcusername',
|
||||
pattern: '^[a-zA-Z]+$',
|
||||
patternDescription: 'must contain only letters.',
|
||||
patterns: [
|
||||
{
|
||||
regex: '^[a-zA-Z]+$',
|
||||
description: 'must contain only letters.',
|
||||
},
|
||||
],
|
||||
masked: false,
|
||||
},
|
||||
rpcuser: {
|
||||
name: 'RPC Username',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'rpc username',
|
||||
warning: null,
|
||||
placeholder: null,
|
||||
required: true,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
default: 'defaultrpcusername',
|
||||
pattern: '^[a-zA-Z]+$',
|
||||
patternDescription: 'must contain only letters.',
|
||||
patterns: [
|
||||
{
|
||||
regex: '^[a-zA-Z]+$',
|
||||
description: 'must contain only letters.',
|
||||
},
|
||||
],
|
||||
masked: false,
|
||||
},
|
||||
rpcpass: {
|
||||
name: 'RPC User Password',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'rpc password',
|
||||
placeholder: null,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
warning: null,
|
||||
required: true,
|
||||
default: {
|
||||
@@ -815,24 +832,24 @@ export module Mock {
|
||||
len: 20,
|
||||
},
|
||||
masked: true,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
},
|
||||
rpcpass2: {
|
||||
name: 'RPC User Password',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'rpc password',
|
||||
warning: null,
|
||||
placeholder: null,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
required: true,
|
||||
default: {
|
||||
charset: 'a-z,A-Z,2-9',
|
||||
len: 20,
|
||||
},
|
||||
masked: true,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -843,12 +860,14 @@ export module Mock {
|
||||
type: 'textarea',
|
||||
description: 'Your personal bio',
|
||||
placeholder: 'Tell the world about yourself',
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
warning: null,
|
||||
required: false,
|
||||
},
|
||||
testnet: {
|
||||
name: 'Testnet',
|
||||
type: 'boolean',
|
||||
type: 'toggle',
|
||||
description:
|
||||
'<ul><li>determines whether your node is running on testnet or mainnet</li></ul><script src="fake"></script>',
|
||||
warning: 'Chain will have to resync!',
|
||||
@@ -867,7 +886,8 @@ export module Mock {
|
||||
type: 'list',
|
||||
description: 'This is a list of objects, like users or something',
|
||||
warning: null,
|
||||
range: '[0,4]',
|
||||
minLength: null,
|
||||
maxLength: 4,
|
||||
default: [
|
||||
{
|
||||
'first-name': 'Admin',
|
||||
@@ -889,29 +909,36 @@ export module Mock {
|
||||
spec: {
|
||||
'first-name': {
|
||||
name: 'First Name',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'User first name',
|
||||
required: false,
|
||||
masked: false,
|
||||
minLength: 4,
|
||||
maxLength: 15,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
'last-name': {
|
||||
name: 'Last Name',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'User first name',
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
required: false,
|
||||
default: {
|
||||
charset: 'a-g,2-9',
|
||||
len: 12,
|
||||
},
|
||||
pattern: '^[a-zA-Z]+$',
|
||||
patternDescription: 'must contain only letters.',
|
||||
patterns: [
|
||||
{
|
||||
regex: '^[a-zA-Z]+$',
|
||||
description: 'must contain only letters.',
|
||||
},
|
||||
],
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
warning: null,
|
||||
@@ -921,9 +948,11 @@ export module Mock {
|
||||
type: 'number',
|
||||
description: 'The age of the user',
|
||||
required: false,
|
||||
integral: false,
|
||||
integer: false,
|
||||
warning: 'User must be at least 18.',
|
||||
range: '[18,*)',
|
||||
min: 18,
|
||||
max: null,
|
||||
step: null,
|
||||
units: null,
|
||||
placeholder: null,
|
||||
default: null,
|
||||
@@ -949,7 +978,8 @@ export module Mock {
|
||||
type: 'multiselect',
|
||||
description: 'how you want to be notified',
|
||||
warning: null,
|
||||
range: '(1,3]',
|
||||
minLength: 2,
|
||||
maxLength: 3,
|
||||
values: {
|
||||
email: 'EEEEmail',
|
||||
text: 'Texxxt',
|
||||
@@ -962,14 +992,16 @@ export module Mock {
|
||||
'favorite-number': {
|
||||
name: 'Favorite Number',
|
||||
type: 'number',
|
||||
integral: false,
|
||||
integer: false,
|
||||
description: 'Your favorite number of all time',
|
||||
placeholder: null,
|
||||
warning:
|
||||
'Once you set this number, it can never be changed without severe consequences.',
|
||||
required: false,
|
||||
default: 7,
|
||||
range: '(-100,100]',
|
||||
min: -99,
|
||||
max: 100,
|
||||
step: 'all',
|
||||
units: 'BTC',
|
||||
},
|
||||
'unlucky-numbers': {
|
||||
@@ -979,12 +1011,15 @@ export module Mock {
|
||||
warning: null,
|
||||
spec: {
|
||||
type: 'number',
|
||||
integral: false,
|
||||
range: '[-100,200)',
|
||||
integer: false,
|
||||
min: -10,
|
||||
max: 10,
|
||||
step: null,
|
||||
units: null,
|
||||
placeholder: null,
|
||||
},
|
||||
range: '[0,10]',
|
||||
minLength: null,
|
||||
maxLength: 10,
|
||||
default: [2, 3],
|
||||
},
|
||||
rpcsettings: {
|
||||
@@ -1001,27 +1036,29 @@ export module Mock {
|
||||
spec: {
|
||||
law1: {
|
||||
name: 'First Law',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'the first law',
|
||||
required: false,
|
||||
masked: false,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
law2: {
|
||||
name: 'Second Law',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'the second law',
|
||||
required: false,
|
||||
masked: false,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1032,7 +1069,8 @@ export module Mock {
|
||||
type: 'list',
|
||||
description: 'the people who make the rules',
|
||||
warning: null,
|
||||
range: '[0,2]',
|
||||
minLength: 1,
|
||||
maxLength: 3,
|
||||
default: [],
|
||||
spec: {
|
||||
type: 'object',
|
||||
@@ -1041,30 +1079,37 @@ export module Mock {
|
||||
spec: {
|
||||
rulemakername: {
|
||||
name: 'Rulemaker Name',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'the name of the rule maker',
|
||||
required: true,
|
||||
minLength: null,
|
||||
maxLength: 30,
|
||||
default: {
|
||||
charset: 'a-g,2-9',
|
||||
len: 12,
|
||||
},
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
warning: null,
|
||||
},
|
||||
rulemakerip: {
|
||||
name: 'Rulemaker IP',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'the ip of the rule maker',
|
||||
required: true,
|
||||
default: '192.168.1.0',
|
||||
pattern:
|
||||
'^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$',
|
||||
patternDescription: 'may only contain numbers and periods',
|
||||
minLength: 4,
|
||||
maxLength: 20,
|
||||
patterns: [
|
||||
{
|
||||
regex:
|
||||
'^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$',
|
||||
description: 'may only contain numbers and periods',
|
||||
},
|
||||
],
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
warning: null,
|
||||
@@ -1074,22 +1119,30 @@ export module Mock {
|
||||
},
|
||||
rpcuser: {
|
||||
name: 'RPC Username',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'rpc username',
|
||||
required: true,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
default: 'defaultrpcusername',
|
||||
pattern: '^[a-zA-Z]+$',
|
||||
patternDescription: 'must contain only letters.',
|
||||
patterns: [
|
||||
{
|
||||
regex: '^[a-zA-Z]+$',
|
||||
description: 'must contain only letters.',
|
||||
},
|
||||
],
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
warning: null,
|
||||
},
|
||||
rpcpass: {
|
||||
name: 'RPC User Password',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'rpc password',
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
required: true,
|
||||
default: {
|
||||
charset: 'a-z,A-Z,2-9',
|
||||
@@ -1097,8 +1150,7 @@ export module Mock {
|
||||
},
|
||||
masked: true,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
warning: null,
|
||||
},
|
||||
},
|
||||
@@ -1115,14 +1167,20 @@ export module Mock {
|
||||
name: 'Dummy',
|
||||
spec: {
|
||||
name: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
name: 'Name',
|
||||
description: null,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
required: true,
|
||||
masked: false,
|
||||
pattern: '^[a-zA-Z]+$',
|
||||
patternDescription: 'Must contain only letters.',
|
||||
patterns: [
|
||||
{
|
||||
regex: '^[a-zA-Z]+$',
|
||||
description: 'must contain only letters.',
|
||||
},
|
||||
],
|
||||
placeholder: null,
|
||||
warning: null,
|
||||
default: null,
|
||||
@@ -1140,28 +1198,35 @@ export module Mock {
|
||||
warning: null,
|
||||
spec: {
|
||||
name: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
name: 'Name',
|
||||
description: null,
|
||||
required: true,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
masked: false,
|
||||
pattern: '^[a-zA-Z]+$',
|
||||
patternDescription: 'Must contain only letters.',
|
||||
patterns: [
|
||||
{
|
||||
regex: '^[a-zA-Z]+$',
|
||||
description: 'must contain only letters.',
|
||||
},
|
||||
],
|
||||
placeholder: null,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
name: 'Email',
|
||||
description: null,
|
||||
required: true,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1169,27 +1234,29 @@ export module Mock {
|
||||
},
|
||||
'public-domain': {
|
||||
name: 'Public Domain',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'the public address of the node',
|
||||
required: true,
|
||||
default: 'bitcoinnode.com',
|
||||
pattern: '.*',
|
||||
patternDescription: 'anything',
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
patterns: [],
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
warning: null,
|
||||
},
|
||||
'private-domain': {
|
||||
name: 'Private Domain',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'the private address of the node',
|
||||
required: true,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
masked: true,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1200,27 +1267,30 @@ export module Mock {
|
||||
port: {
|
||||
name: 'Port',
|
||||
type: 'number',
|
||||
integral: true,
|
||||
integer: true,
|
||||
description:
|
||||
'the default port for your Bitcoin node. default: 8333, testnet: 18333, regtest: 18444',
|
||||
warning: null,
|
||||
required: true,
|
||||
range: '(0, 9998]',
|
||||
min: 1,
|
||||
max: 9999,
|
||||
step: '1',
|
||||
units: null,
|
||||
placeholder: null,
|
||||
default: null,
|
||||
},
|
||||
'favorite-slogan': {
|
||||
name: 'Favorite Slogan',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description:
|
||||
'You most favorite slogan in the whole world, used for paying you.',
|
||||
required: false,
|
||||
masked: true,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1231,16 +1301,23 @@ export module Mock {
|
||||
'external ip addresses that are authorized to access your Bitcoin node',
|
||||
warning:
|
||||
'Any IP you allow here will have RPC access to your Bitcoin node.',
|
||||
range: '[1,10]',
|
||||
minLength: 1,
|
||||
maxLength: 10,
|
||||
default: ['192.168.1.1'],
|
||||
spec: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
pattern:
|
||||
'((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|((^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$)|(^[a-z2-7]{16}\\.onion$)|(^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$))',
|
||||
patternDescription: 'must be a valid ipv4, ipv6, or domain name',
|
||||
minLength: 4,
|
||||
maxLength: 20,
|
||||
patterns: [
|
||||
{
|
||||
regex:
|
||||
'((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|((^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$)|(^[a-z2-7]{16}\\.onion$)|(^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$))',
|
||||
description: 'must be a valid ipv4, ipv6, or domain name',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
rpcauth: {
|
||||
@@ -1248,15 +1325,17 @@ export module Mock {
|
||||
type: 'list',
|
||||
description: 'api keys that are authorized to access your Bitcoin node.',
|
||||
warning: null,
|
||||
range: '[0,*)',
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
default: [],
|
||||
spec: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
masked: false,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
},
|
||||
},
|
||||
'more-advanced': {
|
||||
@@ -1280,40 +1359,43 @@ export module Mock {
|
||||
spec: {
|
||||
law1: {
|
||||
name: 'First Law',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'the first law',
|
||||
required: false,
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
law2: {
|
||||
name: 'Second Law',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'the second law',
|
||||
required: false,
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
law4: {
|
||||
name: 'Fourth Law',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'the fourth law',
|
||||
required: false,
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1322,7 +1404,8 @@ export module Mock {
|
||||
type: 'list',
|
||||
description: 'the third law',
|
||||
warning: null,
|
||||
range: '[0,2]',
|
||||
minLength: null,
|
||||
maxLength: 2,
|
||||
default: [],
|
||||
spec: {
|
||||
type: 'object',
|
||||
@@ -1331,7 +1414,7 @@ export module Mock {
|
||||
spec: {
|
||||
lawname: {
|
||||
name: 'Law Name',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'the name of the law maker',
|
||||
required: true,
|
||||
@@ -1341,21 +1424,27 @@ export module Mock {
|
||||
},
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
warning: null,
|
||||
},
|
||||
lawagency: {
|
||||
name: 'Law agency',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'the ip of the law maker',
|
||||
required: true,
|
||||
default: '192.168.1.0',
|
||||
pattern:
|
||||
'^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$',
|
||||
patternDescription:
|
||||
'may only contain numbers and periods',
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
patterns: [
|
||||
{
|
||||
regex:
|
||||
'^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$',
|
||||
description: 'may only contain numbers and periods',
|
||||
},
|
||||
],
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
warning: null,
|
||||
@@ -1365,14 +1454,15 @@ export module Mock {
|
||||
},
|
||||
law5: {
|
||||
name: 'Fifth Law',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'the fifth law',
|
||||
required: false,
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
patterns: [],
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1383,7 +1473,8 @@ export module Mock {
|
||||
type: 'list',
|
||||
description: 'the people who make the rules',
|
||||
warning: null,
|
||||
range: '[0,2]',
|
||||
minLength: null,
|
||||
maxLength: 2,
|
||||
default: [],
|
||||
spec: {
|
||||
type: 'object',
|
||||
@@ -1392,7 +1483,7 @@ export module Mock {
|
||||
spec: {
|
||||
rulemakername: {
|
||||
name: 'Rulemaker Name',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'the name of the rule maker',
|
||||
required: true,
|
||||
@@ -1402,20 +1493,27 @@ export module Mock {
|
||||
},
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
warning: null,
|
||||
},
|
||||
rulemakerip: {
|
||||
name: 'Rulemaker IP',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'the ip of the rule maker',
|
||||
required: true,
|
||||
default: '192.168.1.0',
|
||||
pattern:
|
||||
'^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$',
|
||||
patternDescription: 'may only contain numbers and periods',
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
patterns: [
|
||||
{
|
||||
regex:
|
||||
'^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$',
|
||||
description: 'may only contain numbers and periods',
|
||||
},
|
||||
],
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
warning: null,
|
||||
@@ -1425,20 +1523,26 @@ export module Mock {
|
||||
},
|
||||
rpcuser: {
|
||||
name: 'RPC Username',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'rpc username',
|
||||
required: true,
|
||||
default: 'defaultrpcusername',
|
||||
pattern: '^[a-zA-Z]+$',
|
||||
patternDescription: 'must contain only letters.',
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
patterns: [
|
||||
{
|
||||
regex: '^[a-zA-Z]+$',
|
||||
description: 'must contain only letters.',
|
||||
},
|
||||
],
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
warning: null,
|
||||
},
|
||||
rpcpass: {
|
||||
name: 'RPC User Password',
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
description: 'rpc password',
|
||||
required: true,
|
||||
@@ -1448,8 +1552,9 @@ export module Mock {
|
||||
},
|
||||
masked: true,
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
patterns: [],
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
warning: null,
|
||||
},
|
||||
},
|
||||
@@ -1550,15 +1655,21 @@ export module Mock {
|
||||
group: null,
|
||||
'input-spec': {
|
||||
reason: {
|
||||
type: 'string',
|
||||
type: 'text',
|
||||
inputmode: 'text',
|
||||
name: 'Re-sync Reason',
|
||||
description: 'Your reason for re-syncing. Why are you doing this?',
|
||||
placeholder: null,
|
||||
required: true,
|
||||
masked: false,
|
||||
pattern: '^[a-zA-Z]+$',
|
||||
patternDescription: 'Must contain only letters.',
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
patterns: [
|
||||
{
|
||||
regex: '^[a-zA-Z]+$',
|
||||
description: 'must contain only letters.',
|
||||
},
|
||||
],
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
|
||||
@@ -7,14 +7,14 @@ import {
|
||||
ValidatorFn,
|
||||
Validators,
|
||||
} from '@angular/forms'
|
||||
import { getDefaultString, Range } from '../util/config-utilities'
|
||||
import { getDefaultString } from '../util/config-utilities'
|
||||
import {
|
||||
InputSpec,
|
||||
isValueSpecListOf,
|
||||
ListValueSpecNumber,
|
||||
ListValueSpecObject,
|
||||
ListValueSpecOf,
|
||||
ListValueSpecString,
|
||||
ListValueSpecText,
|
||||
UniqueBy,
|
||||
ValueSpec,
|
||||
ValueSpecSelect,
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
ValueSpecList,
|
||||
ValueSpecNumber,
|
||||
ValueSpecObject,
|
||||
ValueSpecString,
|
||||
ValueSpecText,
|
||||
ValueSpecUnion,
|
||||
unionSelectKey,
|
||||
ValueSpecTextarea,
|
||||
@@ -76,7 +76,7 @@ export class FormService {
|
||||
|
||||
getListItem(spec: ValueSpecList, entry?: any) {
|
||||
const listItemValidators = getListItemValidators(spec)
|
||||
if (isValueSpecListOf(spec, 'string')) {
|
||||
if (isValueSpecListOf(spec, 'text')) {
|
||||
return this.formBuilder.control(entry, listItemValidators)
|
||||
} else if (isValueSpecListOf(spec, 'number')) {
|
||||
return this.formBuilder.control(entry, listItemValidators)
|
||||
@@ -106,7 +106,7 @@ export class FormService {
|
||||
): UntypedFormGroup | UntypedFormArray | UntypedFormControl {
|
||||
let value: any
|
||||
switch (spec.type) {
|
||||
case 'string':
|
||||
case 'text':
|
||||
if (currentValue !== undefined) {
|
||||
value = currentValue
|
||||
} else {
|
||||
@@ -145,7 +145,7 @@ export class FormService {
|
||||
spec,
|
||||
isValid ? currentSelection : spec.default,
|
||||
)
|
||||
case 'boolean':
|
||||
case 'toggle':
|
||||
value = currentValue === undefined ? spec.default : currentValue
|
||||
return this.formBuilder.control(value)
|
||||
case 'select':
|
||||
@@ -161,7 +161,7 @@ export class FormService {
|
||||
}
|
||||
|
||||
function getListItemValidators(spec: ValueSpecList) {
|
||||
if (isValueSpecListOf(spec, 'string')) {
|
||||
if (isValueSpecListOf(spec, 'text')) {
|
||||
return stringValidators(spec.spec)
|
||||
} else if (isValueSpecListOf(spec, 'number')) {
|
||||
return numberValidators(spec.spec)
|
||||
@@ -169,16 +169,18 @@ function getListItemValidators(spec: ValueSpecList) {
|
||||
}
|
||||
|
||||
function stringValidators(
|
||||
spec: ValueSpecString | ListValueSpecString,
|
||||
spec: ValueSpecText | ListValueSpecText,
|
||||
): ValidatorFn[] {
|
||||
const validators: ValidatorFn[] = []
|
||||
|
||||
if ((spec as ValueSpecString).required) {
|
||||
if ((spec as ValueSpecText).required) {
|
||||
validators.push(Validators.required)
|
||||
}
|
||||
|
||||
if (spec.pattern) {
|
||||
validators.push(Validators.pattern(spec.pattern))
|
||||
validators.push(textLengthInRange(spec.minLength, spec.maxLength))
|
||||
|
||||
if (spec.patterns.length) {
|
||||
spec.patterns.forEach(p => validators.push(Validators.pattern(p.regex)))
|
||||
}
|
||||
|
||||
return validators
|
||||
@@ -205,11 +207,11 @@ function numberValidators(
|
||||
validators.push(Validators.required)
|
||||
}
|
||||
|
||||
if (spec.integral) {
|
||||
if (spec.integer) {
|
||||
validators.push(isInteger())
|
||||
}
|
||||
|
||||
validators.push(numberInRange(spec.range))
|
||||
validators.push(numberInRange(spec.min, spec.max))
|
||||
|
||||
return validators
|
||||
}
|
||||
@@ -226,13 +228,13 @@ function selectValidators(spec: ValueSpecSelect): ValidatorFn[] {
|
||||
|
||||
function multiselectValidators(spec: ValueSpecMultiselect): ValidatorFn[] {
|
||||
const validators: ValidatorFn[] = []
|
||||
validators.push(listInRange(spec.range))
|
||||
validators.push(listInRange(spec.minLength, spec.maxLength))
|
||||
return validators
|
||||
}
|
||||
|
||||
function listValidators(spec: ValueSpecList): ValidatorFn[] {
|
||||
const validators: ValidatorFn[] = []
|
||||
validators.push(listInRange(spec.range))
|
||||
validators.push(listInRange(spec.minLength, spec.maxLength))
|
||||
validators.push(listItemIssue())
|
||||
return validators
|
||||
}
|
||||
@@ -247,16 +249,20 @@ function fileValidators(spec: ValueSpecFile): ValidatorFn[] {
|
||||
return validators
|
||||
}
|
||||
|
||||
export function numberInRange(stringRange: string = ''): ValidatorFn {
|
||||
export function numberInRange(
|
||||
min: number | null,
|
||||
max: number | null,
|
||||
): ValidatorFn {
|
||||
return control => {
|
||||
const value = control.value
|
||||
if (!value) return null
|
||||
try {
|
||||
Range.from(stringRange).checkIncludes(value)
|
||||
return null
|
||||
} catch (e: any) {
|
||||
return { numberNotInRange: `Number must be ${e.message}` }
|
||||
}
|
||||
if (typeof value !== 'number') return null
|
||||
if (min && value < min)
|
||||
return {
|
||||
numberNotInRange: `Number must be greater than or equal to ${min}`,
|
||||
}
|
||||
if (max && value > max)
|
||||
return { numberNotInRange: `Number must be less than or equal to ${max}` }
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,14 +278,38 @@ export function isInteger(): ValidatorFn {
|
||||
: { numberNotInteger: 'Must be an integer' }
|
||||
}
|
||||
|
||||
export function listInRange(stringRange: string = ''): ValidatorFn {
|
||||
export function listInRange(
|
||||
minLength: number | null,
|
||||
maxLength: number | null,
|
||||
): ValidatorFn {
|
||||
return control => {
|
||||
try {
|
||||
Range.from(stringRange).checkIncludes(control.value.length)
|
||||
return null
|
||||
} catch (e: any) {
|
||||
return { listNotInRange: `List must be ${e.message}` }
|
||||
}
|
||||
const length = control.value.length
|
||||
if (minLength && length < minLength)
|
||||
return {
|
||||
listNotInRange: `List must contain at least ${minLength} entries`,
|
||||
}
|
||||
if (maxLength && length > maxLength)
|
||||
return {
|
||||
listNotInRange: `List cannot contain more than ${maxLength} entries`,
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function textLengthInRange(
|
||||
minLength: number | null,
|
||||
maxLength: number | null,
|
||||
): ValidatorFn {
|
||||
return control => {
|
||||
const value = control.value
|
||||
if (value === null || value === undefined) return null
|
||||
|
||||
const length = value.length
|
||||
if (minLength && length < minLength)
|
||||
return { listNotInRange: `Must be at least ${minLength} characters` }
|
||||
if (maxLength && length > maxLength)
|
||||
return { listNotInRange: `Cannot be great than ${maxLength} characters` }
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +365,7 @@ export function listUnique(spec: ValueSpecList): ValidatorFn {
|
||||
function listItemEquals(spec: ValueSpecList, val1: any, val2: any): boolean {
|
||||
// TODO: fix types
|
||||
switch (spec.spec.type) {
|
||||
case 'string':
|
||||
case 'text':
|
||||
case 'number':
|
||||
return val1 == val2
|
||||
case 'object':
|
||||
@@ -348,10 +378,10 @@ function listItemEquals(spec: ValueSpecList, val1: any, val2: any): boolean {
|
||||
|
||||
function itemEquals(spec: ValueSpec, val1: any, val2: any): boolean {
|
||||
switch (spec.type) {
|
||||
case 'string':
|
||||
case 'text':
|
||||
case 'textarea':
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
case 'toggle':
|
||||
case 'select':
|
||||
return val1 == val2
|
||||
case 'object':
|
||||
@@ -536,7 +566,7 @@ export function convertValuesRecursive(
|
||||
control.setValue(
|
||||
control.value || control.value === 0 ? Number(control.value) : null,
|
||||
)
|
||||
} else if (valueSpec.type === 'string' || valueSpec.type === 'textarea') {
|
||||
} else if (valueSpec.type === 'text' || valueSpec.type === 'textarea') {
|
||||
if (!control.value) control.setValue(null)
|
||||
} else if (valueSpec.type === 'object') {
|
||||
convertValuesRecursive(valueSpec.spec, group.get(key) as UntypedFormGroup)
|
||||
@@ -553,7 +583,7 @@ export function convertValuesRecursive(
|
||||
controls.forEach(control => {
|
||||
control.setValue(control.value ? Number(control.value) : null)
|
||||
})
|
||||
} else if (valueSpec.spec.type === 'string') {
|
||||
} else if (valueSpec.spec.type === 'text') {
|
||||
controls.forEach(control => {
|
||||
if (!control.value) control.setValue(null)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user