From fdbe623e1060efc4b074d213a96e8f5a2ab7498b Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Wed, 20 Sep 2023 15:16:28 -0300
Subject: more ui stuff, moved forms to util

---
 packages/web-util/src/forms/InputArray.tsx | 183 +++++++++++++++++++++++++++++
 1 file changed, 183 insertions(+)
 create mode 100644 packages/web-util/src/forms/InputArray.tsx

(limited to 'packages/web-util/src/forms/InputArray.tsx')

diff --git a/packages/web-util/src/forms/InputArray.tsx b/packages/web-util/src/forms/InputArray.tsx
new file mode 100644
index 000000000..00379bed6
--- /dev/null
+++ b/packages/web-util/src/forms/InputArray.tsx
@@ -0,0 +1,183 @@
+import { Fragment, VNode, h } from "preact";
+import { useEffect, useState } from "preact/hooks";
+import { FormProvider, InputArrayFieldState } from "./FormProvider.js";
+import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js";
+import { RenderAllFieldsByUiConfig, UIFormField } from "./forms.js";
+import { useField } from "./useField.js";
+import { TranslatedString } from "@gnu-taler/taler-util";
+
+function Option({
+  label,
+  disabled,
+  isFirst,
+  isLast,
+  isSelected,
+  onClick,
+}: {
+  label: TranslatedString;
+  isFirst?: boolean;
+  isLast?: boolean;
+  isSelected?: boolean;
+  disabled?: boolean;
+  onClick: () => void;
+}): VNode {
+  let clazz = "relative flex border p-4 focus:outline-none disabled:text-grey";
+  if (isFirst) {
+    clazz += " rounded-tl-md rounded-tr-md ";
+  }
+  if (isLast) {
+    clazz += " rounded-bl-md rounded-br-md ";
+  }
+  if (isSelected) {
+    clazz += " z-10 border-indigo-200 bg-indigo-50 ";
+  } else {
+    clazz += " border-gray-200";
+  }
+  if (disabled) {
+    clazz +=
+      " cursor-not-allowed bg-gray-50 text-gray-500 ring-gray-200  text-gray";
+  } else {
+    clazz += " cursor-pointer";
+  }
+  return (
+    <label class={clazz}>
+      <input
+        type="radio"
+        name="privacy-setting"
+        checked={isSelected}
+        disabled={disabled}
+        onClick={onClick}
+        class="mt-0.5 h-4 w-4 shrink-0 text-indigo-600 disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500 disabled:ring-gray-200  focus:ring-indigo-600"
+        aria-labelledby="privacy-setting-0-label"
+        aria-describedby="privacy-setting-0-description"
+      />
+      <span class="ml-3 flex flex-col">
+        <span
+          id="privacy-setting-0-label"
+          disabled
+          class="block text-sm font-medium"
+        >
+          {label}
+        </span>
+        {/* <!-- Checked: "text-indigo-700", Not Checked: "text-gray-500" --> */}
+        {/* <span
+        id="privacy-setting-0-description"
+        class="block text-sm"
+      >
+        This project would be available to anyone who has the link
+      </span> */}
+      </span>
+    </label>
+  );
+}
+
+export function InputArray<T extends object, K extends keyof T>(
+  props: {
+    fields: UIFormField[];
+    labelField: string;
+  } & UIFormProps<T, K>,
+): VNode {
+  const { fields, labelField, name, label, required, tooltip } = props;
+  const { value, onChange, state } = useField<T, K>(name);
+  const list = (value ?? []) as Array<Record<string, string | undefined>>;
+  const [selectedIndex, setSelected] = useState<number | undefined>(undefined);
+  const selected =
+    selectedIndex === undefined ? undefined : list[selectedIndex];
+
+  return (
+    <div class="sm:col-span-6">
+      <LabelWithTooltipMaybeRequired
+        label={label}
+        required={required}
+        tooltip={tooltip}
+      />
+
+      <div class="-space-y-px rounded-md bg-white ">
+        {list.map((v, idx) => {
+          return (
+            <Option
+              label={v[labelField] as TranslatedString}
+              isSelected={selectedIndex === idx}
+              isLast={idx === list.length - 1}
+              disabled={selectedIndex !== undefined && selectedIndex !== idx}
+              isFirst={idx === 0}
+              onClick={() => {
+                setSelected(selectedIndex === idx ? undefined : idx);
+              }}
+            />
+          );
+        })}
+        <div class="pt-2">
+          <Option
+            label={"Add..." as TranslatedString}
+            isSelected={selectedIndex === list.length}
+            isLast
+            isFirst
+            disabled={
+              selectedIndex !== undefined && selectedIndex !== list.length
+            }
+            onClick={() => {
+              setSelected(
+                selectedIndex === list.length ? undefined : list.length,
+              );
+            }}
+          />
+        </div>
+      </div>
+      {selectedIndex !== undefined && (
+        /**
+         * This form provider act as a substate of the parent form
+         * Consider creating an InnerFormProvider since not every feature is expected
+         */
+        <FormProvider
+          initialValue={selected}
+          computeFormState={(v) => {
+            // current state is ignored
+            // the state is defined by the parent form
+
+            // elements should be present in the state object since this is expected to be an array
+            //@ts-ignore
+            return state.elements[selectedIndex];
+          }}
+          onSubmit={(v) => {
+            const newValue = [...list];
+            newValue.splice(selectedIndex, 1, v);
+            onChange(newValue as T[K]);
+            setSelected(undefined);
+          }}
+          onUpdate={(v) => {
+            const newValue = [...list];
+            newValue.splice(selectedIndex, 1, v);
+            onChange(newValue as T[K]);
+          }}
+        >
+          <div class="px-4 py-6">
+            <div class="grid grid-cols-1 gap-y-8 ">
+              <RenderAllFieldsByUiConfig fields={fields} />
+            </div>
+          </div>
+        </FormProvider>
+      )}
+      {selectedIndex !== undefined && (
+        <div class="flex items-center pt-3">
+          <div class="flex-auto">
+            {selected !== undefined && (
+              <button
+                type="button"
+                onClick={() => {
+                  const newValue = [...list];
+                  newValue.splice(selectedIndex, 1);
+                  onChange(newValue as T[K]);
+                  setSelected(undefined);
+                }}
+                class="block rounded-md bg-red-600 px-3 py-2 text-center text-sm  text-white shadow-sm hover:bg-red-500 "
+              >
+                Remove
+              </button>
+            )}
+          </div>
+        </div>
+      )}
+    </div>
+  );
+}
-- 
cgit v1.2.3

