feat: implement disabled, immutable and generate (#2280)

* feat: implement `disabled`, `immutable` and `generate`

* chore: remove unnecessary code

* chore: add generate to textarea and implement immutable

* no generate for textarea

---------

Co-authored-by: Matt Hill <matthewonthemoon@gmail.com>
This commit is contained in:
Alex Inkin
2023-05-22 23:40:06 +04:00
committed by Aiden McClelland
parent 4d1c7a3884
commit 8aa19e6420
26 changed files with 215 additions and 85 deletions

View File

@@ -22,17 +22,14 @@
"@maskito/angular": "^0.10.0",
"@maskito/core": "^0.10.0",
"@materia-ui/ngx-monaco-editor": "^6.0.0",
"@ng-web-apis/common": "^2.0.0",
"@ng-web-apis/mutation-observer": "^2.0.0",
"@ng-web-apis/resize-observer": "^2.0.0",
"@start9labs/argon2": "^0.1.0",
"@start9labs/emver": "^0.1.5",
"@start9labs/start-sdk": "0.4.0-rev0.lib0.rc4",
"@taiga-ui/addon-charts": "3.27.0",
"@taiga-ui/cdk": "3.27.0",
"@taiga-ui/core": "3.27.0",
"@taiga-ui/icons": "3.27.0",
"@taiga-ui/kit": "3.27.0",
"@start9labs/start-sdk": "0.4.0-rev0.lib0.rc5",
"@taiga-ui/addon-charts": "3.28.0",
"@taiga-ui/cdk": "3.28.0",
"@taiga-ui/core": "3.28.0",
"@taiga-ui/icons": "3.28.0",
"@taiga-ui/kit": "3.28.0",
"angular-svg-round-progressbar": "^9.0.0",
"ansi-to-html": "^0.7.2",
"base64-js": "^1.5.1",
@@ -97,14 +94,14 @@
"uuid": "8.3.2"
},
"devDependencies": {
"@types/node": "18.15.0",
"@types/node": "16.4.13",
"@types/uuid": "8.3.1",
"husky": "^4.3.8",
"lint-staged": "^13.2.0",
"prettier": "^2.8.4",
"ts-node": "10.9.0",
"lint-staged": "^12.3.7",
"prettier": "^2.6.1",
"ts-node": "10.2.0",
"tslint": "6.1.3",
"typescript": "4.8.4"
"typescript": "4.3.5"
},
"peerDependencies": {
"rxjs": ">=7.0.0"
@@ -3831,9 +3828,9 @@
"integrity": "sha512-1dhiG03VkfEwSLx/JPKVms6srAbYFQgwfSGhwpUKMDliMXuAHGVaueStmqzVxn3JpH/HEVz0QW8w/PXHqjdiIg=="
},
"node_modules/@start9labs/start-sdk": {
"version": "0.4.0-rev0.lib0.rc4",
"resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-rev0.lib0.rc4.tgz",
"integrity": "sha512-ouOpplLYCFFj5PutVtN5S0TI1UsdXA5nauOrFqKvLyhxazGjHprCpQ/fVHNSspPl/+ujNb7upzFxdZnWOE2oXw==",
"version": "0.4.0-rev0.lib0.rc5",
"resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-rev0.lib0.rc5.tgz",
"integrity": "sha512-2hAJE1id0VgpU8DJt/I+m/IEePmnspzF8BxUoLO3C+ZgyOZU1tEri1f9QCsS6OLn3J11xPlpY1VuSjP5CyHC+Q==",
"dependencies": {
"@iarna/toml": "^2.2.5",
"ts-matches": "^5.4.1",
@@ -3861,9 +3858,9 @@
}
},
"node_modules/@taiga-ui/addon-charts": {
"version": "3.27.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-3.27.0.tgz",
"integrity": "sha512-PZMwRl8pcbF1UcRXzrnzF6rcdg6ZMHSdiF7Q2VUO8Q39GFguyYNYIFdkRHOLvh1wbsXQKoSxho72RN2yeEybCA==",
"version": "3.28.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-3.28.0.tgz",
"integrity": "sha512-ZLsOKrEfni8T+ppteJLULooRqtmvP8aZ0cf7WUEEjEeNR05out6eh8a3uHsnx241HI/or8b4OVKHbTmiFm9Mzg==",
"dependencies": {
"tslib": ">=2.0.0"
},
@@ -3871,22 +3868,22 @@
"@angular/common": ">=12.0.0",
"@angular/core": ">=12.0.0",
"@ng-web-apis/common": ">=2.0.0",
"@taiga-ui/cdk": ">=3.27.0",
"@taiga-ui/core": ">=3.27.0",
"@taiga-ui/cdk": ">=3.28.0",
"@taiga-ui/core": ">=3.28.0",
"@tinkoff/ng-polymorpheus": ">=4.0.0"
}
},
"node_modules/@taiga-ui/cdk": {
"version": "3.27.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-3.27.0.tgz",
"integrity": "sha512-53XLDaQzStpjTV7a4X8658YVlaG7bp1JG4cgIamexylXwkWdsHa9o9KnFFOgsGO5I7heiQ2+kotKPWg7sgUwuQ==",
"version": "3.28.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-3.28.0.tgz",
"integrity": "sha512-U9LTaiaHABanwxssPyutqiK1I8aUKX8ZpJ3CpMvhxszHC3zMYp4/N3RvxYfI8Mb2sqeLR8D+x85EElbWQIxRkA==",
"dependencies": {
"@ng-web-apis/common": "2.1.0",
"@ng-web-apis/mutation-observer": "2.0.0",
"@ng-web-apis/resize-observer": "2.0.0",
"@tinkoff/ng-event-plugins": "3.1.0",
"@tinkoff/ng-polymorpheus": "4.1.0",
"tslib": "2.5.0"
"tslib": "2.5.2"
},
"optionalDependencies": {
"ng-morph": "2.2.4",
@@ -3901,11 +3898,11 @@
}
},
"node_modules/@taiga-ui/core": {
"version": "3.27.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-3.27.0.tgz",
"integrity": "sha512-kXODpMjhxR+4YcdEFVpVaC++G7scMCSuSKPuXXoOCWtEZsQTp/pvSCCxcg951/lLRyh0MkzvEHyz7a8BKikgog==",
"version": "3.28.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-3.28.0.tgz",
"integrity": "sha512-7P62xmja4kpEwVe43zgMfSg1UmYzkdMjNr4DF1S1zU8u0gKQGYHcUFQL1hqTJk6W50xSXVyi4tlWKKCXMvEd5Q==",
"dependencies": {
"@taiga-ui/i18n": "^3.27.0",
"@taiga-ui/i18n": "^3.28.0",
"tslib": ">=2.0.0"
},
"peerDependencies": {
@@ -3917,17 +3914,17 @@
"@angular/router": ">=12.0.0",
"@ng-web-apis/common": ">=2.0.0",
"@ng-web-apis/mutation-observer": ">=2.0.0",
"@taiga-ui/cdk": ">=3.27.0",
"@taiga-ui/i18n": ">=3.27.0",
"@taiga-ui/cdk": ">=3.28.0",
"@taiga-ui/i18n": ">=3.28.0",
"@tinkoff/ng-event-plugins": ">=3.1.0",
"@tinkoff/ng-polymorpheus": ">=4.0.0",
"rxjs": ">=6.0.0"
}
},
"node_modules/@taiga-ui/i18n": {
"version": "3.27.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-3.27.0.tgz",
"integrity": "sha512-orOoo4CeecBc4GVMFcMhwvYo83wsudgtbnEbmFecE2NZO3wdntjOGE/TNpVM28JinO3uL5yabgDTd3UaxK6NSw==",
"version": "3.28.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-3.28.0.tgz",
"integrity": "sha512-wf+BNATdoXhL5MnriF6iQ6xZtCGkD8wTwzosf4zsh7cGoDAPuV5pVWQRVRGGHgjE6gVJhSDdhCBGiM0QqdM3TQ==",
"dependencies": {
"tslib": ">=2.0.0"
},
@@ -3937,17 +3934,17 @@
}
},
"node_modules/@taiga-ui/icons": {
"version": "3.27.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-3.27.0.tgz",
"integrity": "sha512-uXMe4B3cMgJ1qLfezsrOxvsHD9Bw6y39921GFMvlpeIwSEnXMc/rn1wEQpyd6Qo1Ib9AfFWHRDhBa7NPGnXllA==",
"version": "3.28.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-3.28.0.tgz",
"integrity": "sha512-TzQEKgRLP5f+wGsDLMqnBUYPhCN/jgRzQbOWZPIrl+CzaYQTbsFRo1YlKEfMO3Wk55R8QBKv0qpj35+i2Q8Mmg==",
"dependencies": {
"tslib": "^2.2.0"
}
},
"node_modules/@taiga-ui/kit": {
"version": "3.27.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-3.27.0.tgz",
"integrity": "sha512-2YYiku5wXCr1XeqZHnOgLTH4o3rW3EsCx5O8FRSy2LCtkGFLfLemV7E8x1WQqYzOlTW7cCa2goo+K1NMrUWfMQ==",
"version": "3.28.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-3.28.0.tgz",
"integrity": "sha512-jLi/mmIS7kqG1FEY7LT+1uH76pEAiWZsZEQH+3rOwvEGaQBjLE73OPf83f/swaYtFm/DgJemMNnfEMYu661DYA==",
"dependencies": {
"@maskito/angular": "0.11.1",
"@maskito/core": "0.11.1",
@@ -3964,9 +3961,9 @@
"@ng-web-apis/common": ">=2.0.0",
"@ng-web-apis/mutation-observer": ">=2.0.0",
"@ng-web-apis/resize-observer": ">=2.0.0",
"@taiga-ui/cdk": ">=3.27.0",
"@taiga-ui/core": ">=3.27.0",
"@taiga-ui/i18n": ">=3.27.0",
"@taiga-ui/cdk": ">=3.28.0",
"@taiga-ui/core": ">=3.28.0",
"@taiga-ui/i18n": ">=3.28.0",
"@tinkoff/ng-polymorpheus": ">=4.0.0",
"rxjs": ">=6.0.0"
}
@@ -14517,9 +14514,9 @@
}
},
"node_modules/tslib": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz",
"integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA=="
},
"node_modules/tslint": {
"version": "6.1.3",

View File

@@ -47,16 +47,13 @@
"@maskito/angular": "^0.10.0",
"@maskito/core": "^0.10.0",
"@materia-ui/ngx-monaco-editor": "^6.0.0",
"@ng-web-apis/common": "^2.0.0",
"@ng-web-apis/mutation-observer": "^2.0.0",
"@ng-web-apis/resize-observer": "^2.0.0",
"@start9labs/argon2": "^0.1.0",
"@start9labs/emver": "^0.1.5",
"@taiga-ui/addon-charts": "3.27.0",
"@taiga-ui/cdk": "3.27.0",
"@taiga-ui/core": "3.27.0",
"@taiga-ui/icons": "3.27.0",
"@taiga-ui/kit": "3.27.0",
"@taiga-ui/addon-charts": "3.28.0",
"@taiga-ui/cdk": "3.28.0",
"@taiga-ui/core": "3.28.0",
"@taiga-ui/icons": "3.28.0",
"@taiga-ui/kit": "3.28.0",
"angular-svg-round-progressbar": "^9.0.0",
"ansi-to-html": "^0.7.2",
"base64-js": "^1.5.1",
@@ -78,7 +75,7 @@
"patch-db-client": "file: ../../../patch-db/client",
"pbkdf2": "^3.1.2",
"rxjs": "^7.5.6",
"@start9labs/start-sdk": "0.4.0-rev0.lib0.rc4",
"@start9labs/start-sdk": "0.4.0-rev0.lib0.rc5",
"swiper": "^8.2.4",
"ts-matches": "^5.2.1",
"tslib": "^2.3.0",

View File

@@ -62,6 +62,7 @@ export * from './util/base-64'
export * from './util/copy-to-clipboard'
export * from './util/get-new-entries'
export * from './util/get-pkg-id'
export * from './util/invert'
export * from './util/misc.util'
export * from './util/rpc.util'
export * from './util/to-local-iso-string'

View File

@@ -0,0 +1,12 @@
export function invert<
T extends string | number | symbol,
D extends string | number | symbol,
>(obj: Record<T, D>): Record<D, T> {
const result = {} as Record<D, T>
for (const key in obj) {
result[obj[key]] = key
}
return result
}

View File

@@ -6,6 +6,7 @@ import { TUI_DATE_FORMAT, TUI_DATE_SEPARATOR } from '@taiga-ui/cdk'
import {
tuiButtonOptionsProvider,
tuiNumberFormatProvider,
tuiTextfieldOptionsProvider,
} from '@taiga-ui/core'
import {
TUI_DATE_TIME_VALUE_TRANSFORMER,
@@ -33,6 +34,7 @@ export const APP_PROVIDERS: Provider[] = [
IonNav,
tuiNumberFormatProvider({ decimalSeparator: '.', thousandSeparator: '' }),
tuiButtonOptionsProvider({ size: 'm' }),
tuiTextfieldOptionsProvider({ hintOnDisabled: true }),
{
provide: TUI_DATE_FORMAT,
useValue: 'MDY',

View File

@@ -14,6 +14,13 @@ export abstract class Control<Spec extends ValueSpec, Value> {
return this.control.spec
}
// TODO: Properly handle already set immutable value
get readOnly(): boolean {
return (
!!this.value && !!this.control.control?.pristine && this.control.immutable
)
}
get value(): Value | null {
return this.control.value
}

View File

@@ -1,8 +1,8 @@
<div class="label">
{{ spec.name }}
<tui-tooltip
*ngIf="spec.description"
[content]="spec.description"
*ngIf="spec.description || spec.disabled"
[content]="spec | hint"
></tui-tooltip>
<button
tuiLink

View File

@@ -37,8 +37,9 @@ export class FormArrayComponent {
get canAdd(): boolean {
return (
!this.spec.maxLength ||
this.spec.maxLength >= this.array.control.controls.length
!this.spec.disabled &&
(!this.spec.maxLength ||
this.spec.maxLength >= this.array.control.controls.length)
)
}

View File

@@ -2,7 +2,9 @@
[maskito]="mask"
[tuiTextfieldCustomContent]="color"
[tuiTextfieldCleaner]="false"
[tuiHintContent]="spec.description"
[tuiHintContent]="spec | hint"
[readOnly]="readOnly"
[disabled]="!!spec.disabled"
[pseudoInvalid]="invalid"
[(ngModel)]="value"
(focusedChange)="onFocus($event)"
@@ -13,6 +15,7 @@
<ng-template #color>
<div class="wrapper" [style.color]="value">
<input
*ngIf="!readOnly && !spec.disabled"
type="color"
class="color"
tabindex="-1"

View File

@@ -10,8 +10,13 @@
<form-toggle *ngSwitchCase="'toggle'"></form-toggle>
</ng-container>
<tui-error [error]="order | tuiFieldError | async"></tui-error>
<ng-template *ngIf="spec.warning" #warning let-completeWith="completeWith">
<ng-template
*ngIf="spec.warning || immutable"
#warning
let-completeWith="completeWith"
>
{{ spec.warning }}
<p *ngIf="immutable">This value cannot be changed once set!</p>
<div class="buttons">
<button
tuiButton

View File

@@ -39,6 +39,10 @@ export class FormControlComponent<
readonly order = ERRORS
private readonly alerts = inject(TuiAlertService)
get immutable(): boolean {
return 'immutable' in this.spec && this.spec.immutable
}
onFocus(focused: boolean) {
this.focused = focused
this.updateFocused(focused)

View File

@@ -1,6 +1,9 @@
<ng-container [ngSwitch]="spec.inputmode" [tuiHintContent]="spec.description">
<tui-input-time
*ngSwitchCase="'time'"
[tuiHintContent]="spec | hint"
[disabled]="!!spec.disabled"
[readOnly]="readOnly"
[pseudoInvalid]="invalid"
[ngModel]="getTime(value)"
(ngModelChange)="value = $event?.toString() || null"
@@ -11,6 +14,9 @@
</tui-input-time>
<tui-input-date
*ngSwitchCase="'date'"
[tuiHintContent]="spec | hint"
[disabled]="!!spec.disabled"
[readOnly]="readOnly"
[pseudoInvalid]="invalid"
[min]="spec.min ? (spec.min | tuiMapper : getLimit)[0] : min"
[max]="spec.max ? (spec.max | tuiMapper : getLimit)[0] : max"
@@ -22,6 +28,9 @@
</tui-input-date>
<tui-input-date-time
*ngSwitchCase="'datetime-local'"
[tuiHintContent]="spec | hint"
[disabled]="!!spec.disabled"
[readOnly]="readOnly"
[pseudoInvalid]="invalid"
[min]="spec.min ? (spec.min | tuiMapper : getLimit) : min"
[max]="spec.max ? (spec.max | tuiMapper : getLimit) : max"

View File

@@ -1,5 +1,7 @@
<tui-multi-select
[tuiHintContent]="spec.description"
[tuiHintContent]="spec | hint"
[disabled]="disabled"
[readOnly]="readOnly"
[pseudoInvalid]="invalid"
[editable]="false"
[disabledItemHandler]="disabledItemHandler"

View File

@@ -2,6 +2,7 @@ import { Component } from '@angular/core'
import { ValueSpecMultiselect } from '@start9labs/start-sdk/lib/config/configTypes'
import { Control } from '../control'
import { tuiPure } from '@taiga-ui/cdk'
import { invert } from '@start9labs/shared'
@Component({
selector: 'form-multiselect',
@@ -11,13 +12,26 @@ export class FormMultiselectComponent extends Control<
ValueSpecMultiselect,
readonly string[]
> {
readonly items = Object.values(this.spec.values)
private readonly inverted = invert(this.spec.values)
readonly disabledItemHandler = (item: string): boolean =>
private readonly isDisabled = (item: string) =>
Array.isArray(this.spec.disabled) &&
this.spec.disabled.includes(this.inverted[item])
private readonly isExceedingLimit = (item: string) =>
!!this.spec.maxLength &&
this.selected.length >= this.spec.maxLength &&
!this.selected.includes(item)
readonly disabledItemHandler = (item: string): boolean =>
this.isDisabled(item) || this.isExceedingLimit(item)
readonly items = Object.values(this.spec.values)
get disabled(): boolean {
return typeof this.spec.disabled === 'string'
}
get selected(): string[] {
return this.memoize(this.value)
}

View File

@@ -1,5 +1,7 @@
<tui-input-number
[tuiHintContent]="spec.description"
[tuiHintContent]="spec | hint"
[disabled]="!!spec.disabled"
[readOnly]="readOnly"
[tuiTextfieldPostfix]="spec.units || ''"
[pseudoInvalid]="invalid"
[precision]="Infinity"

View File

@@ -1,5 +1,7 @@
<tui-select
[tuiHintContent]="spec.description"
[tuiHintContent]="spec | hint"
[disabled]="disabled"
[readOnly]="readOnly"
[tuiTextfieldCleaner]="!spec.required"
[pseudoInvalid]="invalid"
[(ngModel)]="selected"
@@ -9,7 +11,7 @@
<span *ngIf="spec.required">*</span>
<select
tuiSelect
[labels]="[spec.warning ? ' ' + spec.warning : '']"
[items]="[items]"
[items]="items"
[disabledItemHandler]="disabledItemHandler"
></select>
</tui-select>

View File

@@ -1,5 +1,6 @@
import { Component } from '@angular/core'
import { ValueSpecSelect } from '@start9labs/start-sdk/lib/config/configTypes'
import { invert } from '@start9labs/shared'
import { Control } from '../control'
@Component({
@@ -7,15 +8,23 @@ import { Control } from '../control'
templateUrl: './form-select.component.html',
})
export class FormSelectComponent extends Control<ValueSpecSelect, string> {
private readonly inverted = invert(this.spec.values)
readonly items = Object.values(this.spec.values)
readonly disabledItemHandler = (item: string) =>
Array.isArray(this.spec.disabled) &&
this.spec.disabled.includes(this.inverted[item])
get disabled(): boolean {
return typeof this.spec.disabled === 'string'
}
get selected(): string | null {
return this.value && this.spec.values[this.value]
}
set selected(value: string | null) {
this.value =
Object.entries(this.spec.values).find(([_, v]) => value === v)?.[0] ??
null
this.value = (value && this.inverted[value]) || null
}
}

View File

@@ -1,6 +1,8 @@
<tui-input
[tuiTextfieldCustomContent]="spec.masked ? toggle : ''"
[tuiHintContent]="spec.description"
[tuiHintContent]="spec | hint"
[disabled]="!!spec.disabled"
[readOnly]="readOnly"
[pseudoInvalid]="invalid"
[(ngModel)]="value"
(focusedChange)="onFocus($event)"
@@ -17,13 +19,24 @@
/>
</tui-input>
<ng-template #toggle>
<button
*ngIf="spec.generate"
tuiIconButton
type="button"
appearance="icon"
title="Generate"
size="xs"
class="button"
icon="tuiIconRefreshCcw"
(click)="generate()"
></button>
<button
tuiIconButton
type="button"
appearance="icon"
title="Toggle masking"
size="xs"
class="toggle"
class="button"
[icon]="masked ? 'tuiIconEye' : 'tuiIconEyeOff'"
(click)="masked = !masked"
></button>

View File

@@ -1,6 +1,6 @@
.toggle {
.button {
pointer-events: auto;
margin-left: auto;
margin-left: 0.25rem;
}
.masked {

View File

@@ -1,6 +1,7 @@
import { Component } from '@angular/core'
import { ValueSpecText } from '@start9labs/start-sdk/lib/config/configTypes'
import { Control } from '../control'
import { getDefaultString } from '../../../util/config-utilities'
@Component({
selector: 'form-text',
@@ -9,4 +10,8 @@ import { Control } from '../control'
})
export class FormTextComponent extends Control<ValueSpecText, string> {
masked = true
generate() {
this.value = getDefaultString(this.spec.generate || '')
}
}

View File

@@ -1,5 +1,7 @@
<tui-text-area
[tuiHintContent]="spec.description"
[tuiHintContent]="spec | hint"
[disabled]="!!spec.disabled"
[readOnly]="readOnly"
[pseudoInvalid]="invalid"
[expandable]="true"
[rows]="6"

View File

@@ -1,10 +1,11 @@
{{ spec.name }}
<tui-tooltip
*ngIf="spec.description"
[content]="spec.description"
*ngIf="spec.description || spec.disabled"
[tuiHintContent]="spec | hint"
></tui-tooltip>
<tui-toggle
size="l"
[disabled]="!!spec.disabled || readOnly"
[(ngModel)]="value"
(focusedChange)="onFocus($event)"
></tui-toggle>

View File

@@ -48,6 +48,7 @@ import { MustachePipe } from './mustache.pipe'
import { ControlDirective } from './control.directive'
import { FormColorComponent } from './form-color/form-color.component'
import { FormDatetimeComponent } from './form-datetime/form-datetime.component'
import { HintPipe } from './hint.pipe'
@NgModule({
imports: [
@@ -98,6 +99,7 @@ import { FormDatetimeComponent } from './form-datetime/form-datetime.component'
FormObjectComponent,
FormArrayComponent,
MustachePipe,
HintPipe,
ControlDirective,
],
exports: [FormGroupComponent],

View File

@@ -0,0 +1,21 @@
import { Pipe, PipeTransform } from '@angular/core'
import { ValueSpec } from '@start9labs/start-sdk/lib/config/configTypes'
@Pipe({
name: 'hint',
})
export class HintPipe implements PipeTransform {
transform(spec: ValueSpec): string {
const hint = []
if (spec.description) {
hint.push(spec.description)
}
if ('disabled' in spec && typeof spec.disabled === 'string') {
hint.push(`Disabled: ${spec.disabled}`)
}
return hint.join('\n\n')
}
}

View File

@@ -717,6 +717,18 @@ export module Mock {
),
}),
),
users: Value.multiselect({
name: 'Users',
default: [],
maxLength: 2,
disabled: ['matt'],
values: {
matt: 'Matt Hill',
alex: 'Alex Inkin',
blue: 'Blue J',
lucy: 'Lucy',
},
}),
advanced: Value.object(
{
name: 'Advanced',
@@ -900,19 +912,19 @@ export module Mock {
},
),
),
'random-enum': Value.select({
name: 'Random Enum',
'random-select': Value.select({
name: 'Random select',
description: 'This is not even real.',
warning: 'Be careful changing this!',
required: {
default: 'null',
default: null,
},
values: {
null: 'null',
option1: 'option1',
option2: 'option2',
option3: 'option3',
},
disabled: ['option2'],
}),
'favorite-number':
/* TODO: Convert range for this value ((-100,100])*/ Value.number({
@@ -1037,8 +1049,13 @@ export module Mock {
description: 'Options<ul><li>Item 1</li><li>Item 2</li></ul>',
warning: 'Careful changing this',
required: { default: 'internal' },
disabled: ['fake'],
},
Variants.of({
fake: {
name: 'Fake',
spec: Config.of({}),
},
internal: {
name: 'Internal',
spec: Config.of({}),
@@ -1113,6 +1130,10 @@ export module Mock {
}),
'favorite-slogan': Value.text({
name: 'Favorite Slogan',
generate: {
charset: 'a-z,A-Z,2-9',
len: 20,
},
required: false,
description:
'You most favorite slogan in the whole world, used for paying you.',

View File

@@ -52,8 +52,6 @@ export class FormService {
): ValueSpecSelect {
return {
...spec,
// TODO: implement disabled
disabled: false,
type: 'select',
default: selection,
values: Object.fromEntries(