import { buildCodecForObject, buildCodecForUnion, Codec, codecForBoolean, codecForConstString, codecForLazy, codecForList, codecForNumber, codecForString, codecForTimestamp, codecOptional, Integer, TalerProtocolTimestamp, } from "@gnu-taler/taler-util"; export type FormConfiguration = DoubleColumnForm; export type DoubleColumnForm = { type: "double-column"; design: DoubleColumnFormSection[]; // behavior?: (form: Partial) => FormState; }; export type DoubleColumnFormSection = { title: string; description?: string; fields: UIFormElementConfig[]; }; // export interface BaseForm { // state: TalerExchangeApi.AmlState; // threshold: AmountJson; // } export type UIFormElementConfig = | UIFormElementGroup | UIFormElementCaption | UIFormFieldAbsoluteTime | UIFormFieldAmount | UIFormFieldArray | UIFormFieldChoiseHorizontal | UIFormFieldChoiseStacked | UIFormFieldFile | UIFormFieldInteger | UIFormFieldSelectMultiple | UIFormFieldSelectOne | UIFormFieldText | UIFormFieldTextArea | UIFormFieldToggle; type UIFormFieldAbsoluteTime = { type: "absoluteTimeText"; max?: TalerProtocolTimestamp; min?: TalerProtocolTimestamp; pattern: string; } & UIFormFieldBaseConfig; type UIFormFieldAmount = { type: "amount"; max?: Integer; min?: Integer; currency: string; } & UIFormFieldBaseConfig; type UIFormFieldArray = { type: "array"; // id of the field shown when the array is collapsed labelFieldId: UIHandlerId; fields: UIFormElementConfig[]; } & UIFormFieldBaseConfig; type UIFormElementCaption = { type: "caption" } & UIFieldElementDescription; type UIFormElementGroup = { type: "group"; fields: UIFormElementConfig[]; } & UIFieldElementDescription; type UIFormFieldChoiseHorizontal = { type: "choiceHorizontal"; choices: Array; } & UIFormFieldBaseConfig; type UIFormFieldChoiseStacked = { type: "choiceStacked"; choices: Array; } & UIFormFieldBaseConfig; type UIFormFieldFile = { type: "file"; maxBytes?: Integer; minBytes?: Integer; // comma-separated list of one or more file types // https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept#unique_file_type_specifiers accept?: string; } & UIFormFieldBaseConfig; type UIFormFieldInteger = { type: "integer"; max?: Integer; min?: Integer; } & UIFormFieldBaseConfig; interface SelectUiChoice { label: string; description?: string; value: string; } type UIFormFieldSelectMultiple = { type: "selectMultiple"; max?: Integer; min?: Integer; unique?: boolean; choices: Array; } & UIFormFieldBaseConfig; type UIFormFieldSelectOne = { type: "selectOne"; choices: Array; } & UIFormFieldBaseConfig; type UIFormFieldText = { type: "text" } & UIFormFieldBaseConfig; type UIFormFieldTextArea = { type: "textArea" } & UIFormFieldBaseConfig; type UIFormFieldToggle = { type: "toggle" } & UIFormFieldBaseConfig; export type UIFieldElementDescription = { /* label if the field, visible for the user */ label: string; /* long text to be shown on user demand */ tooltip?: string; /* short text to be shown close to the field, usually below and dimmer*/ help?: string; /* name of the field, useful for a11y */ name: string; /* if the field should be initially hidden */ hidden?: boolean; /* ui element to show before */ addonBeforeId?: string; /* ui element to show after */ addonAfterId?: string; }; export type UIFormFieldBaseConfig = UIFieldElementDescription & { /* example to be shown inside the field */ placeholder?: string; /* show a mark as required */ required?: boolean; /* readonly and dim */ disabled?: boolean; /* conversion id to convert the string into the value type the id should be known to the ui impl */ converterId?: string; /* property id of the form */ id: UIHandlerId; }; declare const __handlerId: unique symbol; export type UIHandlerId = string & { [__handlerId]: true }; // FIXME: validate well formed ui field id const codecForUiFieldId = codecForString as () => Codec; const codecForUIFormFieldBaseDescriptionTemplate = < T extends UIFieldElementDescription, >() => buildCodecForObject() .property("addonAfterId", codecOptional(codecForString())) .property("addonBeforeId", codecOptional(codecForString())) .property("hidden", codecOptional(codecForBoolean())) .property("help", codecOptional(codecForString())) .property("label", codecForString()) .property("name", codecForString()) .property("tooltip", codecOptional(codecForString())); const codecForUIFormFieldBaseConfigTemplate = < T extends UIFormFieldBaseConfig, >() => codecForUIFormFieldBaseDescriptionTemplate() .property("id", codecForUiFieldId()) .property("converterId", codecOptional(codecForString())) .property("disabled", codecOptional(codecForBoolean())) .property("required", codecOptional(codecForBoolean())) .property("placeholder", codecOptional(codecForString())); const codecForUiFormFieldAbsoluteTime = (): Codec => codecForUIFormFieldBaseConfigTemplate() .property("type", codecForConstString("absoluteTimeText")) .property("pattern", codecForString()) .property("max", codecOptional(codecForTimestamp)) .property("min", codecOptional(codecForTimestamp)) .build("UIFormFieldAbsoluteTime"); const codecForUiFormFieldAmount = (): Codec => codecForUIFormFieldBaseConfigTemplate() .property("type", codecForConstString("amount")) .property("currency", codecForString()) .property("max", codecOptional(codecForNumber())) .property("min", codecOptional(codecForNumber())) .build("UIFormFieldAmount"); const codecForUiFormFieldArray = (): Codec => codecForUIFormFieldBaseConfigTemplate() .property("type", codecForConstString("array")) .property("labelFieldId", codecForUiFieldId()) .property("tooltip", codecOptional(codecForString())) // eslint-disable-next-line @typescript-eslint/no-use-before-define .property("fields", codecForList(codecForUiFormField())) .build("UIFormFieldArray"); const codecForUiFormFieldCaption = (): Codec => codecForUIFormFieldBaseDescriptionTemplate() .property("type", codecForConstString("caption")) .build("UIFormFieldCaption"); const codecForUiFormSelectUiChoice = (): Codec => buildCodecForObject() .property("description", codecOptional(codecForString())) .property("label", codecForString()) .property("value", codecForString()) .build("SelectUiChoice"); const codecForUiFormFieldChoiceHorizontal = (): Codec => codecForUIFormFieldBaseConfigTemplate() .property("type", codecForConstString("choiceHorizontal")) .property("choices", codecForList(codecForUiFormSelectUiChoice())) .build("UIFormFieldChoiseHorizontal"); const codecForUiFormFieldChoiceStacked = (): Codec => codecForUIFormFieldBaseConfigTemplate() .property("type", codecForConstString("choiceStacked")) .property("choices", codecForList(codecForUiFormSelectUiChoice())) .build("UIFormFieldChoiseStacked"); const codecForUiFormFieldFile = (): Codec => codecForUIFormFieldBaseConfigTemplate() .property("type", codecForConstString("file")) .property("accept", codecOptional(codecForString())) .property("maxBytes", codecOptional(codecForNumber())) .property("minBytes", codecOptional(codecForNumber())) .build("UIFormFieldFile"); const codecForUiFormFieldGroup = (): Codec => codecForUIFormFieldBaseDescriptionTemplate() .property("type", codecForConstString("group")) // eslint-disable-next-line @typescript-eslint/no-use-before-define .property("fields", codecForList(codecForUiFormField())) .build("UiFormFieldGroup"); const codecForUiFormFieldInteger = (): Codec => codecForUIFormFieldBaseConfigTemplate() .property("type", codecForConstString("integer")) // .property("properties", codecForUIFormFieldBaseConfig()) .property("max", codecOptional(codecForNumber())) .property("min", codecOptional(codecForNumber())) .build("UIFormFieldInteger"); const codecForUiFormFieldSelectMultiple = (): Codec => codecForUIFormFieldBaseConfigTemplate() .property("type", codecForConstString("selectMultiple")) .property("max", codecOptional(codecForNumber())) .property("min", codecOptional(codecForNumber())) .property("unique", codecOptional(codecForBoolean())) .property("choices", codecForList(codecForUiFormSelectUiChoice())) .build("UiFormFieldSelectMultiple"); const codecForUiFormFieldSelectOne = (): Codec => codecForUIFormFieldBaseConfigTemplate() .property("type", codecForConstString("selectOne")) .property("choices", codecForList(codecForUiFormSelectUiChoice())) .build("UIFormFieldSelectOne"); const codecForUiFormFieldText = (): Codec => codecForUIFormFieldBaseConfigTemplate() .property("type", codecForConstString("text")) .build("UIFormFieldText"); const codecForUiFormFieldTextArea = (): Codec => codecForUIFormFieldBaseConfigTemplate() .property("type", codecForConstString("textArea")) .build("UIFormFieldTextArea"); const codecForUiFormFieldToggle = (): Codec => codecForUIFormFieldBaseConfigTemplate() .property("type", codecForConstString("toggle")) .build("UIFormFieldToggle"); const codecForUiFormField = (): Codec => buildCodecForUnion() .discriminateOn("type") .alternative("array", codecForLazy(codecForUiFormFieldArray)) .alternative("group", codecForLazy(codecForUiFormFieldGroup)) .alternative("absoluteTimeText", codecForUiFormFieldAbsoluteTime()) .alternative("amount", codecForUiFormFieldAmount()) .alternative("caption", codecForUiFormFieldCaption()) .alternative("choiceHorizontal", codecForUiFormFieldChoiceHorizontal()) .alternative("choiceStacked", codecForUiFormFieldChoiceStacked()) .alternative("file", codecForUiFormFieldFile()) .alternative("integer", codecForUiFormFieldInteger()) .alternative("selectMultiple", codecForUiFormFieldSelectMultiple()) .alternative("selectOne", codecForUiFormFieldSelectOne()) .alternative("text", codecForUiFormFieldText()) .alternative("textArea", codecForUiFormFieldTextArea()) .alternative("toggle", codecForUiFormFieldToggle()) .build("UIFormField"); const codecForDoubleColumnFormSection = (): Codec => buildCodecForObject() .property("title", codecForString()) .property("description", codecOptional(codecForString())) .property("fields", codecForList(codecForUiFormField())) .build("DoubleColumnFormSection"); const codecForDoubleColumnForm = (): Codec => buildCodecForObject() .property("type", codecForConstString("double-column")) .property("design", codecForList(codecForDoubleColumnFormSection())) .build("DoubleColumnForm"); const codecForFormConfiguration = (): Codec => buildCodecForUnion() .discriminateOn("type") .alternative("double-column", codecForDoubleColumnForm()) .build("FormConfiguration"); const codecForFormMetadata = (): Codec => buildCodecForObject() .property("label", codecForString()) .property("id", codecForString()) .property("version", codecForNumber()) .property("config", codecForFormConfiguration()) .build("FormMetadata"); export const codecForUIForms = (): Codec => buildCodecForObject() .property("forms", codecForList(codecForFormMetadata())) .build("UiForms"); export type FormMetadata = { label: string; id: string; version: number; config: FormConfiguration; }; export interface UiForms { // Where libeufin backend is localted // default: window.origin without "webui/" forms: Array; }