files for config

This commit is contained in:
Matt Hill
2023-03-21 12:00:15 -06:00
committed by Aiden McClelland
parent e76ccba2f7
commit dacd5d3e6b
8 changed files with 103 additions and 42 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "embassy-os", "name": "embassy-os",
"version": "0.3.4", "version": "0.3.4.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
@@ -49,7 +49,7 @@
"patch-db-client": "file: ../../../patch-db/client", "patch-db-client": "file: ../../../patch-db/client",
"pbkdf2": "^3.1.2", "pbkdf2": "^3.1.2",
"rxjs": "^7.5.6", "rxjs": "^7.5.6",
"start-sdk": "^0.4.0-lib0.alpha6", "start-sdk": "^0.4.0-lib0.alpha8",
"swiper": "^8.2.4", "swiper": "^8.2.4",
"ts-matches": "^5.2.1", "ts-matches": "^5.2.1",
"tslib": "^2.3.0", "tslib": "^2.3.0",
@@ -13771,9 +13771,9 @@
} }
}, },
"node_modules/start-sdk": { "node_modules/start-sdk": {
"version": "0.4.0-lib0.alpha6", "version": "0.4.0-lib0.alpha8",
"resolved": "https://registry.npmjs.org/start-sdk/-/start-sdk-0.4.0-lib0.alpha6.tgz", "resolved": "https://registry.npmjs.org/start-sdk/-/start-sdk-0.4.0-lib0.alpha8.tgz",
"integrity": "sha512-DshqK1GFbxojoRK8/RjPm0+xmNZ0Ac41JhhwhqvpSIdbk7l4vkLm38KIOVJ9nAFw592Ob9vu1NHKySuYOXgQ8Q==", "integrity": "sha512-qErlv8ikV8nYqyCxxSN856dUwddGK5OOwTXk62IiJPxY3si03P1NQ3MnFch6Vx3NXiOmKeNNqw4/bj26TdUWRA==",
"dependencies": { "dependencies": {
"@iarna/toml": "^2.2.5", "@iarna/toml": "^2.2.5",
"lodash": "^4.17.21", "lodash": "^4.17.21",

View File

@@ -74,7 +74,7 @@
"patch-db-client": "file: ../../../patch-db/client", "patch-db-client": "file: ../../../patch-db/client",
"pbkdf2": "^3.1.2", "pbkdf2": "^3.1.2",
"rxjs": "^7.5.6", "rxjs": "^7.5.6",
"start-sdk": "^0.4.0-lib0.alpha6", "start-sdk": "^0.4.0-lib0.alpha8",
"swiper": "^8.2.4", "swiper": "^8.2.4",
"ts-matches": "^5.2.1", "ts-matches": "^5.2.1",
"tslib": "^2.3.0", "tslib": "^2.3.0",

View File

@@ -12,7 +12,7 @@ export class FormLabelComponent {
name: string name: string
new: boolean new: boolean
edited: boolean edited: boolean
description?: string description: string | null
required?: boolean required?: boolean
newOptions?: boolean newOptions?: boolean
} }
@@ -22,7 +22,7 @@ export class FormLabelComponent {
async presentAlertDescription() { async presentAlertDescription() {
const alert = await this.alertCtrl.create({ const alert = await this.alertCtrl.create({
header: this.data.name, header: this.data.name,
message: this.data.description, message: this.data.description!,
buttons: [ buttons: [
{ {
text: 'OK', text: 'OK',

View File

@@ -1,10 +1,10 @@
<ion-item-group [formGroup]="formGroup"> <ion-item-group [formGroup]="formGroup">
<div *ngFor="let entry of formGroup.controls | keyvalue: asIsOrder"> <div *ngFor="let entry of formGroup.controls | keyvalue : asIsOrder">
<div *ngIf="objectSpec[entry.key] as spec"> <div *ngIf="objectSpec[entry.key] as spec">
<!-- string or number --> <!-- string or number -->
<ng-container *ngIf="spec.type === 'string' || spec.type === 'number'"> <ng-container *ngIf="spec.type === 'string' || spec.type === 'number'">
<!-- label --> <!-- label -->
<h4 class="input-label"> <p class="input-label">
<form-label <form-label
[data]="{ [data]="{
name: spec.name, name: spec.name,
@@ -14,11 +14,11 @@
required: !spec.nullable required: !spec.nullable
}" }"
></form-label> ></form-label>
</h4> </p>
<ion-item [color]="(theme$ | async) === 'Light' ? 'light' : 'dark'"> <ion-item [color]="(theme$ | async) === 'Light' ? 'light' : 'dark'">
<ion-textarea <ion-textarea
*ngIf="spec.type === 'string' && spec.textarea; else notTextArea" *ngIf="spec.type === 'string' && spec.textarea; else notTextArea"
[placeholder]="spec.placeholder || 'Enter ' + spec.name" [placeholder]="spec.placeholder"
[formControlName]="entry.key" [formControlName]="entry.key"
(ionFocus)="presentAlertChangeWarning(entry.key, spec)" (ionFocus)="presentAlertChangeWarning(entry.key, spec)"
(ionChange)="handleInputChange()" (ionChange)="handleInputChange()"
@@ -34,7 +34,7 @@
!unmasked[entry.key] !unmasked[entry.key]
" "
[inputmode]="spec.type === 'number' ? 'tel' : 'text'" [inputmode]="spec.type === 'number' ? 'tel' : 'text'"
[placeholder]="spec.placeholder || 'Enter ' + spec.name" [placeholder]="spec.placeholder"
[formControlName]="entry.key" [formControlName]="entry.key"
(ionFocus)="presentAlertChangeWarning(entry.key, spec)" (ionFocus)="presentAlertChangeWarning(entry.key, spec)"
(ionChange)="handleInputChange()" (ionChange)="handleInputChange()"
@@ -63,20 +63,24 @@
</ion-note> </ion-note>
</ion-item> </ion-item>
<p class="error-message"> <p class="error-message">
<span *ngIf="(formGroup | getControl: entry.key).errors as errors"> <span *ngIf="(formGroup | getControl : entry.key).errors as errors">
{{ errors | getError: $any(spec)['pattern-description'] }} {{ errors | getError : $any(spec)['pattern-description'] }}
</span> </span>
</p> </p>
</ng-container> </ng-container>
<!-- boolean or enum --> <!-- boolean, enum, or file -->
<ion-item <ion-item
*ngIf="spec.type === 'boolean' || spec.type === 'enum'" *ngIf="
spec.type === 'boolean' ||
spec.type === 'enum' ||
spec.type === 'file'
"
style="--padding-start: 0" style="--padding-start: 0"
> >
<ion-button <ion-button
*ngIf="spec.description" *ngIf="spec.description as desc"
fill="clear" fill="clear"
(click)="presentAlertBoolEnumDescription($event, spec)" (click.stop)="presentAlertDescription(spec.name, desc)"
style="--padding-start: 0" style="--padding-start: 0"
> >
<ion-icon <ion-icon
@@ -126,6 +130,35 @@
{{ spec['value-names'][option] }} {{ spec['value-names'][option] }}
</ion-select-option> </ion-select-option>
</ion-select> </ion-select>
<div
*ngIf="spec.type === 'file' && formGroup.get(entry.key) as control"
slot="end"
>
<ion-button
*ngIf="!control.value; else hasFile"
strong
color="dark"
size="small"
>
<label for="upload-file">Browse...</label>
<input
type="file"
[accept]="spec.extensions.join(',')"
style="position: absolute; opacity: 0; height: 100%"
id="upload-file"
(change)="handleFileInput($event, control)"
/>
</ion-button>
<ng-template #hasFile>
<div class="inline">
<p class="ion-padding-end">{{ control.value.name }}</p>
<div style="cursor: pointer" (click)="clearFile(control)">
<ion-icon name="close"></ion-icon>
</div>
</div>
</ng-template>
</div>
</ion-item> </ion-item>
<!-- object --> <!-- object -->
<ng-container *ngIf="spec.type === 'object'"> <ng-container *ngIf="spec.type === 'object'">
@@ -159,7 +192,7 @@
<!-- body --> <!-- body -->
<tui-expand <tui-expand
[expanded]="objectDisplay[entry.key].expanded" [expanded]="objectDisplay[entry.key].expanded"
[id]="objectId | toElementId: entry.key" [id]="objectId | toElementId : entry.key"
> >
<div class="nested-wrapper"> <div class="nested-wrapper">
<form-object <form-object
@@ -209,7 +242,7 @@
</ion-button> </ion-button>
</ion-item-divider> </ion-item-divider>
<p class="error-message" style="margin-bottom: 8px"> <p class="error-message" style="margin-bottom: 8px">
<span *ngIf="(formGroup | getControl: entry.key).errors as errors"> <span *ngIf="(formGroup | getControl : entry.key).errors as errors">
{{ errors | getError }} {{ errors | getError }}
</span> </span>
</p> </p>
@@ -257,7 +290,7 @@
<tui-expand <tui-expand
style="padding-left: 24px" style="padding-left: 24px"
[expanded]="objectListDisplay[entry.key][i].expanded" [expanded]="objectListDisplay[entry.key][i].expanded"
[id]="objectId | toElementId: entry.key:i" [id]="objectId | toElementId : entry.key : i"
> >
<form-object <form-object
*ngIf="spec.subtype === 'object'" *ngIf="spec.subtype === 'object'"
@@ -294,7 +327,7 @@
<!-- string or number --> <!-- string or number -->
<div <div
*ngIf="spec.subtype === 'string' || spec.subtype === 'number'" *ngIf="spec.subtype === 'string' || spec.subtype === 'number'"
[id]="objectId | toElementId: entry.key:i" [id]="objectId | toElementId : entry.key : i"
> >
<ion-item <ion-item
[color]="(theme$ | async) === 'Light' ? 'light' : 'dark'" [color]="(theme$ | async) === 'Light' ? 'light' : 'dark'"
@@ -320,10 +353,10 @@
<p class="error-message"> <p class="error-message">
<span <span
*ngIf=" *ngIf="
(formGroup | getControl: entry.key:i).errors as errors (formGroup | getControl : entry.key : i).errors as errors
" "
> >
{{ errors | getError: $any(spec)['pattern-description'] }} {{ errors | getError : $any(spec)['pattern-description'] }}
</span> </span>
</p> </p>
</div> </div>
@@ -356,14 +389,14 @@
(click)="presentModalEnumList(entry.key, $any(spec), formArr.value)" (click)="presentModalEnumList(entry.key, $any(spec), formArr.value)"
> >
<ion-label style="white-space: nowrap !important"> <ion-label style="white-space: nowrap !important">
<h2>{{ formArr.value | toEnumListDisplay: $any(spec.spec) }}</h2> <h2>{{ formArr.value | toEnumListDisplay : $any(spec.spec) }}</h2>
</ion-label> </ion-label>
<ion-button slot="end" fill="clear" color="light"> <ion-button slot="end" fill="clear" color="light">
<ion-icon slot="icon-only" name="chevron-down"></ion-icon> <ion-icon slot="icon-only" name="chevron-down"></ion-icon>
</ion-button> </ion-button>
</ion-item> </ion-item>
<p class="error-message"> <p class="error-message">
<span *ngIf="(formGroup | getControl: entry.key).errors as errors"> <span *ngIf="(formGroup | getControl : entry.key).errors as errors">
{{ errors | getError }} {{ errors | getError }}
</span> </span>
</p> </p>

View File

@@ -7,14 +7,18 @@ import {
inject, inject,
SimpleChanges, SimpleChanges,
} from '@angular/core' } from '@angular/core'
import { FormArray, UntypedFormArray, UntypedFormGroup } from '@angular/forms' import {
AbstractControl,
FormArray,
UntypedFormArray,
UntypedFormGroup,
} from '@angular/forms'
import { AlertButton, AlertController, ModalController } from '@ionic/angular' import { AlertButton, AlertController, ModalController } from '@ionic/angular'
import { import {
InputSpec, InputSpec,
ListValueSpecOf, ListValueSpecOf,
ValueSpec, ValueSpec,
ValueSpecBoolean, ValueSpecBoolean,
ValueSpecEnum,
ValueSpecList, ValueSpecList,
ValueSpecListOf, ValueSpecListOf,
ValueSpecUnion, ValueSpecUnion,
@@ -236,13 +240,15 @@ export class FormObjectComponent {
await alert.present() await alert.present()
} }
async presentAlertBoolEnumDescription( handleFileInput(e: any, control: AbstractControl) {
event: Event, control.patchValue(e.target.files[0])
spec: ValueSpecBoolean | ValueSpecEnum, }
) {
event.stopPropagation()
const { name, description } = spec
clearFile(control: AbstractControl) {
control.patchValue(null)
}
async presentAlertDescription(name: string, description: string) {
const alert = await this.alertCtrl.create({ const alert = await this.alertCtrl.create({
header: name, header: name,
message: description || '', message: description || '',

View File

@@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
import { UntypedFormGroup } from '@angular/forms' import { UntypedFormGroup } from '@angular/forms'
import { v4 } from 'uuid' import { v4 } from 'uuid'
import { FormService } from 'src/app/services/form.service' import { FormService } from 'src/app/services/form.service'
import { ValueSpecUnion } from 'src/app/pkg-config/config-types' import { ValueSpecUnion } from 'start-sdk/types/config-types'
@Component({ @Component({
selector: 'form-union', selector: 'form-union',

View File

@@ -843,6 +843,15 @@ export module Mock {
warning: 'Chain will have to resync!', warning: 'Chain will have to resync!',
default: true, default: true,
}, },
document: {
name: 'Needed File',
type: 'file',
description: 'A file we need',
placeholder: 'Testing placeholder',
warning: 'Testing warning',
nullable: false,
extensions: ['.png'],
},
'object-list': { 'object-list': {
name: 'Object List', name: 'Object List',
type: 'list', type: 'list',

View File

@@ -18,6 +18,7 @@ import {
UniqueBy, UniqueBy,
ValueSpec, ValueSpec,
ValueSpecEnum, ValueSpecEnum,
ValueSpecFile,
ValueSpecList, ValueSpecList,
ValueSpecNumber, ValueSpecNumber,
ValueSpecObject, ValueSpecObject,
@@ -102,31 +103,33 @@ export class FormService {
let value: any let value: any
switch (spec.type) { switch (spec.type) {
case 'string': case 'string':
validators = stringValidators(spec)
if (currentValue !== undefined) { if (currentValue !== undefined) {
value = currentValue value = currentValue
} else { } else {
value = spec.default ? getDefaultString(spec.default) : null value = spec.default ? getDefaultString(spec.default) : null
} }
return this.formBuilder.control(value, validators) return this.formBuilder.control(value, stringValidators(spec))
case 'number': case 'number':
validators = numberValidators(spec)
if (currentValue !== undefined) { if (currentValue !== undefined) {
value = currentValue value = currentValue
} else { } else {
value = spec.default || null value = spec.default || null
} }
return this.formBuilder.control(value, validators) return this.formBuilder.control(value, numberValidators(spec))
case 'object': case 'object':
return this.getFormGroup(spec.spec, [], currentValue) return this.getFormGroup(spec.spec, [], currentValue)
case 'list': case 'list':
validators = listValidators(spec)
const mapped = ( const mapped = (
Array.isArray(currentValue) ? currentValue : (spec.default as any[]) Array.isArray(currentValue) ? currentValue : (spec.default as any[])
).map(entry => { ).map(entry => {
return this.getListItem(spec, entry) return this.getListItem(spec, entry)
}) })
return this.formBuilder.array(mapped, validators) return this.formBuilder.array(mapped, listValidators(spec))
case 'file':
return this.formBuilder.control(
currentValue || null,
fileValidators(spec),
)
case 'union': case 'union':
const currentSelection = currentValue?.[spec.tag.id] const currentSelection = currentValue?.[spec.tag.id]
const isValid = !!spec.variants[currentSelection] const isValid = !!spec.variants[currentSelection]
@@ -204,6 +207,16 @@ function listValidators(spec: ValueSpecList): ValidatorFn[] {
return validators return validators
} }
function fileValidators(spec: ValueSpecFile): ValidatorFn[] {
const validators: ValidatorFn[] = []
if (!spec.nullable) {
validators.push(Validators.required)
}
return validators
}
export function numberInRange(stringRange: string): ValidatorFn { export function numberInRange(stringRange: string): ValidatorFn {
return control => { return control => {
const value = control.value const value = control.value