Files
start-sdk/lib/config/builder/value.ts

275 lines
6.6 KiB
TypeScript

import { BuilderExtract, IBuilder } from "./builder"
import { Config } from "./config"
import { List } from "./list"
import { Variants } from "./variants"
import {
InputSpec,
Pattern,
ValueSpec,
ValueSpecColor,
ValueSpecDatetime,
ValueSpecList,
ValueSpecNumber,
ValueSpecSelect,
ValueSpecText,
ValueSpecTextarea,
} from "../configTypes"
import { guardAll } from "../../util"
import { DefaultString } from "../configTypes"
import { _ } from "../../util"
type RequiredDefault<A> =
| false
| {
default: A | null
}
function requiredLikeToAbove<Input extends RequiredDefault<A>, A>(
requiredLike: Input,
) {
// prettier-ignore
return {
required: (typeof requiredLike === 'object' ? true : requiredLike) as (
Input extends { default: unknown} ? true:
Input extends true ? true :
false
),
default:(typeof requiredLike === 'object' ? requiredLike.default : null) as (
Input extends { default: infer Default } ? Default :
null
)
};
}
/**
* A value is going to be part of the form in the FE of the OS.
* Something like a boolean, a string, a number, etc.
* in the fe it will ask for the name of value, and use the rest of the value to determine how to render it.
* While writing with a value, you will start with `Value.` then let the IDE suggest the rest.
* for things like string, the options are going to be in {}.
* Keep an eye out for another config builder types as params.
* Note, usually this is going to be used in a `Config` {@link Config} builder.
```ts
const username = Value.string({
name: "Username",
default: "bitcoin",
description: "The username for connecting to Bitcoin over RPC.",
warning: null,
required: true,
masked: true,
placeholder: null,
pattern: "^[a-zA-Z0-9_]+$",
patternDescription: "Must be alphanumeric (can contain underscore).",
});
```
*/
export class Value<A extends ValueSpec> extends IBuilder<A> {
static toggle(a: {
name: string
description?: string | null
warning?: string | null
default?: boolean | null
}) {
return new Value({
description: null,
warning: null,
default: null,
type: "toggle" as const,
...a,
})
}
static text<Required extends RequiredDefault<DefaultString>>(a: {
name: string
description?: string | null
warning?: string | null
required: Required
/** Default = false */
masked?: boolean
placeholder?: string | null
minLength?: number | null
maxLength?: number | null
patterns?: Pattern[]
/** Default = 'text' */
inputmode?: ValueSpecText["inputmode"]
}) {
return new Value({
type: "text" as const,
description: null,
warning: null,
masked: false,
placeholder: null,
minLength: null,
maxLength: null,
patterns: [],
inputmode: "text",
...a,
...requiredLikeToAbove(a.required),
})
}
static textarea(a: {
name: string
description?: string | null
warning?: string | null
required: boolean
minLength?: number | null
maxLength?: number | null
placeholder?: string | null
}) {
return new Value({
description: null,
warning: null,
minLength: null,
maxLength: null,
placeholder: null,
type: "textarea" as const,
...a,
} as ValueSpecTextarea)
}
static number<Required extends RequiredDefault<number>>(a: {
name: string
description?: string | null
warning?: string | null
required: Required
min?: number | null
max?: number | null
/** Default = '1' */
step?: string | null
integer: boolean
units?: string | null
placeholder?: string | null
}) {
return new Value({
type: "number" as const,
description: null,
warning: null,
min: null,
max: null,
step: null,
units: null,
placeholder: null,
...a,
...requiredLikeToAbove(a.required),
})
}
static color<Required extends RequiredDefault<string>>(a: {
name: string
description?: string | null
warning?: string | null
required: Required
}) {
return new Value({
type: "color" as const,
description: null,
warning: null,
...a,
...requiredLikeToAbove(a.required),
})
}
static datetime<Required extends RequiredDefault<string>>(a: {
name: string
description?: string | null
warning?: string | null
required: Required
/** Default = 'datetime-local' */
inputmode?: ValueSpecDatetime["inputmode"]
min?: string | null
max?: string | null
step?: string | null
}) {
return new Value({
type: "datetime" as const,
description: null,
warning: null,
inputmode: "datetime-local",
min: null,
max: null,
step: null,
...a,
...requiredLikeToAbove(a.required),
})
}
static select<
Required extends RequiredDefault<string>,
B extends Record<string, string>,
>(a: {
name: string
description?: string | null
warning?: string | null
required: Required
values: B
}) {
return new Value({
description: null,
warning: null,
type: "select" as const,
...a,
...requiredLikeToAbove(a.required),
})
}
static multiselect<Values extends Record<string, string>>(a: {
name: string
description?: string | null
warning?: string | null
default: string[]
values: Values
minLength?: number | null
maxLength?: number | null
}) {
return new Value({
type: "multiselect" as const,
minLength: null,
maxLength: null,
warning: null,
description: null,
...a,
})
}
static object<Spec extends Config<InputSpec>>(
a: {
name: string
description?: string | null
warning?: string | null
},
previousSpec: Spec,
) {
const spec = previousSpec.build() as BuilderExtract<Spec>
return new Value({
type: "object" as const,
description: null,
warning: null,
...a,
spec,
})
}
static union<
Required extends RequiredDefault<string>,
V extends Variants<{ [key: string]: { name: string; spec: InputSpec } }>,
>(
a: {
name: string
description?: string | null
warning?: string | null
required: Required
default?: string | null
},
aVariants: V,
) {
const variants = aVariants.build() as BuilderExtract<V>
return new Value({
type: "union" as const,
description: null,
warning: null,
...a,
variants,
...requiredLikeToAbove(a.required),
})
}
static list<A extends ValueSpecList>(a: List<A>) {
return new Value(a.build())
}
public validator() {
return guardAll(this.a)
}
}