2023-05-10 05:53:37 +02:00
|
|
|
import { TranslatedString } from "@gnu-taler/taler-util";
|
|
|
|
import { Fragment, VNode, h } from "preact";
|
|
|
|
import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js";
|
|
|
|
import { useField } from "./useField.js";
|
|
|
|
|
|
|
|
function classNames(...classes: string[]): string {
|
|
|
|
return classes.filter(Boolean).join(" ");
|
|
|
|
}
|
|
|
|
const memoryOptions = [
|
|
|
|
{ name: "4 GB", inStock: true },
|
|
|
|
{ name: "8 GB", inStock: true },
|
|
|
|
{ name: "16 GB", inStock: true },
|
|
|
|
{ name: "32 GB", inStock: true },
|
|
|
|
{ name: "64 GB", inStock: true },
|
|
|
|
{ name: "128 GB", inStock: false },
|
|
|
|
];
|
|
|
|
|
|
|
|
export interface Choice {
|
|
|
|
label: TranslatedString;
|
|
|
|
description?: TranslatedString;
|
|
|
|
value: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function InputChoiceStacked(
|
|
|
|
props: {
|
|
|
|
choices: Choice[];
|
|
|
|
} & UIFormProps<string>,
|
|
|
|
): VNode {
|
|
|
|
const {
|
|
|
|
choices,
|
|
|
|
name,
|
|
|
|
label,
|
|
|
|
tooltip,
|
|
|
|
placeholder,
|
|
|
|
required,
|
|
|
|
before,
|
|
|
|
after,
|
|
|
|
converter,
|
|
|
|
} = props;
|
|
|
|
const { value, onChange, state, isDirty } = useField(name);
|
|
|
|
if (state.hidden) {
|
|
|
|
return <Fragment />;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2023-05-10 20:01:56 +02:00
|
|
|
<div class="sm:col-span-6">
|
2023-05-10 05:53:37 +02:00
|
|
|
<LabelWithTooltipMaybeRequired
|
|
|
|
label={label}
|
|
|
|
required={required}
|
|
|
|
tooltip={tooltip}
|
|
|
|
/>
|
|
|
|
<fieldset class="mt-2">
|
|
|
|
{value !== undefined && !required && (
|
|
|
|
<div class="flex mb-2 items-center ">
|
|
|
|
<div class="flex-auto">
|
|
|
|
<button
|
|
|
|
type="button"
|
|
|
|
onClick={() => {
|
|
|
|
onChange(undefined!);
|
|
|
|
}}
|
|
|
|
class="block rounded-md bg-white px-3 py-2 text-center text-sm font-semibold shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
|
|
|
|
>
|
|
|
|
Cancel
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
|
|
|
|
<div class="space-y-4">
|
|
|
|
{choices.map((choice) => {
|
|
|
|
let clazz =
|
|
|
|
"border relative block cursor-pointer rounded-lg bg-white px-6 py-4 shadow-sm focus:outline-none sm:flex sm:justify-between";
|
|
|
|
if (choice.value === value) {
|
|
|
|
clazz +=
|
|
|
|
" border-transparent border-indigo-600 ring-2 ring-indigo-600";
|
|
|
|
} else {
|
|
|
|
clazz += " border-gray-300";
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<label class={clazz}>
|
|
|
|
<input
|
|
|
|
type="radio"
|
|
|
|
name="server-size"
|
2023-05-10 20:01:56 +02:00
|
|
|
defaultValue={choice.value}
|
2023-05-10 05:53:37 +02:00
|
|
|
onClick={(e) => {
|
|
|
|
onChange(choice.value);
|
|
|
|
}}
|
|
|
|
class="sr-only"
|
|
|
|
aria-labelledby="server-size-0-label"
|
|
|
|
aria-describedby="server-size-0-description-0 server-size-0-description-1"
|
|
|
|
/>
|
|
|
|
<span class="flex items-center">
|
|
|
|
<span class="flex flex-col text-sm">
|
|
|
|
<span
|
|
|
|
id="server-size-0-label"
|
|
|
|
class="font-medium text-gray-900"
|
|
|
|
>
|
|
|
|
{choice.label}
|
|
|
|
</span>
|
|
|
|
{choice.description !== undefined && (
|
|
|
|
<span
|
|
|
|
id="server-size-0-description-0"
|
|
|
|
class="text-gray-500"
|
|
|
|
>
|
|
|
|
<span class="block sm:inline">
|
|
|
|
{choice.description}
|
|
|
|
</span>
|
|
|
|
</span>
|
|
|
|
)}
|
|
|
|
</span>
|
|
|
|
</span>
|
|
|
|
</label>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
</fieldset>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|