gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] 01/04: lang switcher in the header and single column


From: gnunet
Subject: [taler-wallet-core] 01/04: lang switcher in the header and single column design
Date: Thu, 02 Jan 2025 22:22:41 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

commit 7566e666deca5a8697805fa4a550d4a7883f48c1
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Thu Jan 2 17:46:37 2025 -0300

    lang switcher in the header and single column design
---
 packages/web-util/src/components/Header.tsx       | 426 ++++++++++++++--------
 packages/web-util/src/components/LangSelector.tsx | 174 +++++----
 packages/web-util/src/forms/forms.ts              |   4 +-
 packages/web-util/src/forms/ui-form.ts            |  14 +-
 4 files changed, 399 insertions(+), 219 deletions(-)

diff --git a/packages/web-util/src/components/Header.tsx 
b/packages/web-util/src/components/Header.tsx
index 29f4a4949..793709860 100644
--- a/packages/web-util/src/components/Header.tsx
+++ b/packages/web-util/src/components/Header.tsx
@@ -1,5 +1,9 @@
 import { useState } from "preact/hooks";
-import { LangSelector, useNotifications, useTranslationContext } from 
"../index.browser.js";
+import {
+  LangSelector,
+  useNotifications,
+  useTranslationContext,
+} from "../index.browser.js";
 import { ComponentChildren, Fragment, VNode, h } from "preact";
 import logo from "../assets/logo-2021.svg";
 
@@ -11,173 +15,303 @@ interface Props {
   children?: ComponentChildren;
   onLogout: (() => void) | undefined;
   sites: Array<Array<string>>;
-  supportedLangs: string[]
+  supportedLangs: string[];
 }
 
-export function Header({ title, profileURL, notificationURL, iconLinkURL, 
sites, onLogout, children }: Props): VNode {
+export function Header({
+  title,
+  profileURL,
+  notificationURL,
+  iconLinkURL,
+  sites,
+  onLogout,
+  children,
+}: Props): VNode {
   const { i18n } = useTranslationContext();
-  const [open, setOpen] = useState(false)
+  const [open, setOpen] = useState(false);
   const ns = useNotifications();
 
-  return <Fragment>
-    <header class="bg-indigo-600 w-full mx-auto px-2 border-b 
border-opacity-25 border-indigo-400">
-      <div class="flex flex-row h-16 items-center ">
-        <div class="flex px-2 justify-start">
-          <div class="flex-shrink-0 bg-white rounded-lg">
-            <a href={iconLinkURL ?? "#"} name="logo">
-              <img
-                class="h-8 w-auto"
-                src={logo}
-                alt="GNU Taler"
-                style={{ height: "1.5rem", margin: ".5rem" }}
-              />
-            </a>
+  return (
+    <Fragment>
+      <header class="bg-indigo-600 w-full mx-auto px-2 border-b 
border-opacity-25 border-indigo-400">
+        <div class="flex flex-row h-16 items-center ">
+          <div class="flex px-2 justify-start">
+            <div class="flex-shrink-0 bg-white rounded-lg">
+              <a href={iconLinkURL ?? "#"} name="logo">
+                <img
+                  class="h-8 w-auto"
+                  src={logo}
+                  alt="GNU Taler"
+                  style={{ height: "1.5rem", margin: ".5rem" }}
+                />
+              </a>
+            </div>
+            <span class="flex items-center text-white text-lg font-bold ml-4">
+              {title}
+            </span>
           </div>
-          <span class="flex items-center text-white text-lg font-bold ml-4">
-            {title}
-          </span>
-        </div>
-        <div class="flex-1 ml-6 ">
-          <div class="flex flex-1 space-x-4">
-            {sites.map((site) => {
-              if (site.length !== 2) return;
-              const [name, url] = site
-              return <a href={url} name={`site header ${name}`} class="hidden 
sm:block text-white hover:bg-indigo-500 hover:bg-opacity-75 rounded-md py-2 
px-3 text-sm font-medium">{name}</a>
-            })}
+          <div class="flex-1 ml-6 ">
+            <div class="flex flex-1 space-x-4">
+              {sites.map((site) => {
+                if (site.length !== 2) return;
+                const [name, url] = site;
+                return (
+                  <a
+                    href={url}
+                    name={`site header ${name}`}
+                    class="hidden sm:block text-white hover:bg-indigo-500 
hover:bg-opacity-75 rounded-md py-2 px-3 text-sm font-medium"
+                  >
+                    {name}
+                  </a>
+                );
+              })}
+            </div>
           </div>
-        </div>
-        <div class="flex justify-end">
-          {!notificationURL ? undefined :
-            <a href={notificationURL} name="notifications" class="relative 
inline-flex items-center justify-center rounded-md bg-indigo-600 p-1 mr-2 
text-indigo-200 hover:bg-indigo-500 hover:bg-opacity-75 hover:text-white 
focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 
focus:ring-offset-indigo-600" aria-controls="mobile-menu" aria-expanded="false">
-              <span class="absolute -inset-0.5"></span>
-              <span class="sr-only"><i18n.Translate>Show 
notifications</i18n.Translate></span>
-              {ns.length > 0 ?
-                <svg xmlns="http://www.w3.org/2000/svg"; viewBox="0 0 24 24" 
fill="currentColor" class="w-10 h-10">
-                  <path d="M5.85 3.5a.75.75 0 0 0-1.117-1 9.719 9.719 0 0 
0-2.348 4.876.75.75 0 0 0 1.479.248A8.219 8.219 0 0 1 5.85 3.5ZM19.267 
2.5a.75.75 0 1 0-1.118 1 8.22 8.22 0 0 1 1.987 4.124.75.75 0 0 0 1.48-.248A9.72 
9.72 0 0 0 19.266 2.5Z" />
-                  <path fill-rule="evenodd" d="M12 2.25A6.75 6.75 0 0 0 5.25 
9v.75a8.217 8.217 0 0 1-2.119 5.52.75.75 0 0 0 .298 1.206c1.544.57 3.16.99 
4.831 1.243a3.75 3.75 0 1 0 7.48 0 24.583 24.583 0 0 0 4.83-1.244.75.75 0 0 0 
.298-1.205 8.217 8.217 0 0 1-2.118-5.52V9A6.75 6.75 0 0 0 12 2.25ZM9.75 
18c0-.034 0-.067.002-.1a25.05 25.05 0 0 0 4.496 0l.002.1a2.25 2.25 0 1 1-4.5 
0Z" clip-rule="evenodd" />
-                </svg>
-                :
-                <svg xmlns="http://www.w3.org/2000/svg"; fill="none" viewBox="0 
0 24 24" stroke-width="1.5" stroke="currentColor" class="w-10 h-10">
-                  <path stroke-linecap="round" stroke-linejoin="round" 
d="M14.857 17.082a23.848 23.848 0 0 0 5.454-1.31A8.967 8.967 0 0 1 18 9.75V9A6 
6 0 0 0 6 9v.75a8.967 8.967 0 0 1-2.312 6.022c1.733.64 3.56 1.085 5.455 
1.31m5.714 0a24.255 24.255 0 0 1-5.714 0m5.714 0a3 3 0 1 1-5.714 0" />
+          <div class="flex justify-end">
+            {!notificationURL ? undefined : (
+              <a
+                href={notificationURL}
+                name="notifications"
+                class="relative inline-flex items-center justify-center 
rounded-md bg-indigo-600 p-1 mr-2 text-indigo-200 hover:bg-indigo-500 
hover:bg-opacity-75 hover:text-white focus:outline-none focus:ring-2 
focus:ring-white focus:ring-offset-2 focus:ring-offset-indigo-600"
+                aria-controls="mobile-menu"
+                aria-expanded="false"
+              >
+                <span class="absolute -inset-0.5"></span>
+                <span class="sr-only">
+                  <i18n.Translate>Show notifications</i18n.Translate>
+                </span>
+                {ns.length > 0 ? (
+                  <svg
+                    xmlns="http://www.w3.org/2000/svg";
+                    viewBox="0 0 24 24"
+                    fill="currentColor"
+                    class="w-10 h-10"
+                  >
+                    <path d="M5.85 3.5a.75.75 0 0 0-1.117-1 9.719 9.719 0 0 
0-2.348 4.876.75.75 0 0 0 1.479.248A8.219 8.219 0 0 1 5.85 3.5ZM19.267 
2.5a.75.75 0 1 0-1.118 1 8.22 8.22 0 0 1 1.987 4.124.75.75 0 0 0 1.48-.248A9.72 
9.72 0 0 0 19.266 2.5Z" />
+                    <path
+                      fill-rule="evenodd"
+                      d="M12 2.25A6.75 6.75 0 0 0 5.25 9v.75a8.217 8.217 0 0 
1-2.119 5.52.75.75 0 0 0 .298 1.206c1.544.57 3.16.99 4.831 1.243a3.75 3.75 0 1 
0 7.48 0 24.583 24.583 0 0 0 4.83-1.244.75.75 0 0 0 .298-1.205 8.217 8.217 0 0 
1-2.118-5.52V9A6.75 6.75 0 0 0 12 2.25ZM9.75 18c0-.034 0-.067.002-.1a25.05 
25.05 0 0 0 4.496 0l.002.1a2.25 2.25 0 1 1-4.5 0Z"
+                      clip-rule="evenodd"
+                    />
+                  </svg>
+                ) : (
+                  <svg
+                    xmlns="http://www.w3.org/2000/svg";
+                    fill="none"
+                    viewBox="0 0 24 24"
+                    stroke-width="1.5"
+                    stroke="currentColor"
+                    class="w-10 h-10"
+                  >
+                    <path
+                      stroke-linecap="round"
+                      stroke-linejoin="round"
+                      d="M14.857 17.082a23.848 23.848 0 0 0 5.454-1.31A8.967 
8.967 0 0 1 18 9.75V9A6 6 0 0 0 6 9v.75a8.967 8.967 0 0 1-2.312 6.022c1.733.64 
3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 0 1-5.714 0m5.714 0a3 3 0 1 
1-5.714 0"
+                    />
+                  </svg>
+                )}
+              </a>
+            )}
+            {!profileURL ? undefined : (
+              <a
+                href={profileURL}
+                name="profile"
+                class="relative inline-flex items-center justify-center 
rounded-md bg-indigo-600 p-1 mr-2 text-indigo-200 hover:bg-indigo-500 
hover:bg-opacity-75 hover:text-white focus:outline-none focus:ring-2 
focus:ring-white focus:ring-offset-2 focus:ring-offset-indigo-600"
+                aria-controls="mobile-menu"
+                aria-expanded="false"
+              >
+                <span class="absolute -inset-0.5"></span>
+                <span class="sr-only">
+                  <i18n.Translate>Open profile</i18n.Translate>
+                </span>
+                <svg
+                  xmlns="http://www.w3.org/2000/svg";
+                  fill="none"
+                  viewBox="0 0 24 24"
+                  stroke-width="1.5"
+                  stroke="currentColor"
+                  class="w-10 h-10"
+                >
+                  <path
+                    stroke-linecap="round"
+                    stroke-linejoin="round"
+                    d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 
0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 
21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
+                  />
                 </svg>
-              }
-            </a>
-          }
-          {!profileURL ? undefined :
-            <a href={profileURL} name="profile" class="relative inline-flex 
items-center justify-center rounded-md bg-indigo-600 p-1 mr-2 text-indigo-200 
hover:bg-indigo-500 hover:bg-opacity-75 hover:text-white focus:outline-none 
focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-indigo-600" 
aria-controls="mobile-menu" aria-expanded="false">
+              </a>
+            )}
+            <LangSelector type="icon" />
+            <button
+              type="button"
+              name="toggle sidebar"
+              class="relative inline-flex items-center justify-center 
rounded-md bg-indigo-600 p-1 text-indigo-200 hover:bg-indigo-500 
hover:bg-opacity-75 hover:text-white focus:outline-none focus:ring-2 
focus:ring-white focus:ring-offset-2 focus:ring-offset-indigo-600"
+              aria-controls="mobile-menu"
+              aria-expanded="false"
+              onClick={(e) => {
+                setOpen(!open);
+              }}
+            >
               <span class="absolute -inset-0.5"></span>
-              <span class="sr-only"><i18n.Translate>Open 
profile</i18n.Translate></span>
-              <svg xmlns="http://www.w3.org/2000/svg"; fill="none" viewBox="0 0 
24 24" stroke-width="1.5" stroke="currentColor" class="w-10 h-10">
-                <path stroke-linecap="round" stroke-linejoin="round" 
d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 
2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 
0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
+              <span class="sr-only">
+                <i18n.Translate>Open settings</i18n.Translate>
+              </span>
+              <svg
+                class="block h-10 w-10"
+                fill="none"
+                viewBox="0 0 24 24"
+                stroke-width="2"
+                stroke="currentColor"
+                aria-hidden="true"
+              >
+                <path
+                  stroke-linecap="round"
+                  stroke-linejoin="round"
+                  d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
+                />
               </svg>
-            </a>
-          }
-          <button type="button" name="toggle sidebar" class="relative 
inline-flex items-center justify-center rounded-md bg-indigo-600 p-1 
text-indigo-200 hover:bg-indigo-500 hover:bg-opacity-75 hover:text-white 
focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 
focus:ring-offset-indigo-600" aria-controls="mobile-menu" aria-expanded="false"
-            onClick={(e) => {
-              setOpen(!open)
-            }}>
-            <span class="absolute -inset-0.5"></span>
-            <span class="sr-only"><i18n.Translate>Open 
settings</i18n.Translate></span>
-            <svg class="block h-10 w-10" fill="none" viewBox="0 0 24 24" 
stroke-width="2" stroke="currentColor" aria-hidden="true">
-              <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 
6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
-            </svg>
-          </button>
+            </button>
+          </div>
         </div>
-      </div>
-    </header>
+      </header>
 
-    {
-      open &&
-      <div class="relative z-10" name="sidebar overlay" 
aria-labelledby="slide-over-title" role="dialog" aria-modal="true"
-        onClick={() => {
-          setOpen(false)
-        }}>
-        <div class="fixed inset-0"></div>
-
-        <div class="fixed inset-0 overflow-hidden">
-          <div class="absolute inset-0 overflow-hidden">
-            <div class="pointer-events-none fixed inset-y-0 right-0 flex 
max-w-full pl-10">
-              <div class="pointer-events-auto w-screen max-w-md" >
-                <div class="flex h-full flex-col overflow-y-scroll bg-white 
py-6 shadow-xl" onClick={(e) => {
-                  //do not trigger close if clicking inside the sidebar
-                  e.stopPropagation();
-                }}>
-                  <div class="px-4 sm:px-6" >
-                    <div class="flex items-start justify-between" >
-                      <h2 class="text-base font-semibold leading-6 
text-gray-900" id="slide-over-title">
-                        <i18n.Translate>Menu</i18n.Translate>
-                      </h2>
-                      <div class="ml-3 flex h-7 items-center">
-                        <button type="button" name="close sidebar" 
class="relative rounded-md bg-white text-gray-400 hover:text-gray-500 
focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
-                          onClick={(e) => {
-                            setOpen(false)
-                          }}
+      {open && (
+        <div
+          class="relative z-10"
+          name="sidebar overlay"
+          aria-labelledby="slide-over-title"
+          role="dialog"
+          aria-modal="true"
+          onClick={() => {
+            setOpen(false);
+          }}
+        >
+          <div class="fixed inset-0"></div>
 
+          <div class="fixed inset-0 overflow-hidden">
+            <div class="absolute inset-0 overflow-hidden">
+              <div class="pointer-events-none fixed inset-y-0 right-0 flex 
max-w-full pl-10">
+                <div class="pointer-events-auto w-screen max-w-md">
+                  <div
+                    class="flex h-full flex-col overflow-y-scroll bg-white 
py-6 shadow-xl"
+                    onClick={(e) => {
+                      //do not trigger close if clicking inside the sidebar
+                      e.stopPropagation();
+                    }}
+                  >
+                    <div class="px-4 sm:px-6">
+                      <div class="flex items-start justify-between">
+                        <h2
+                          class="text-base font-semibold leading-6 
text-gray-900"
+                          id="slide-over-title"
                         >
-                          <span class="absolute -inset-2.5"></span>
-                          <span class="sr-only">
-                            <i18n.Translate>Close panel</i18n.Translate>
-                          </span>
-                          <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" 
stroke-width="1.5" stroke="currentColor" aria-hidden="true">
-                            <path stroke-linecap="round" 
stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
-                          </svg>
-                        </button>
+                          <i18n.Translate>Menu</i18n.Translate>
+                        </h2>
+                        <div class="ml-3 flex h-7 items-center">
+                          <button
+                            type="button"
+                            name="close sidebar"
+                            class="relative rounded-md bg-white text-gray-400 
hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 
focus:ring-offset-2"
+                            onClick={(e) => {
+                              setOpen(false);
+                            }}
+                          >
+                            <span class="absolute -inset-2.5"></span>
+                            <span class="sr-only">
+                              <i18n.Translate>Close panel</i18n.Translate>
+                            </span>
+                            <svg
+                              class="h-6 w-6"
+                              fill="none"
+                              viewBox="0 0 24 24"
+                              stroke-width="1.5"
+                              stroke="currentColor"
+                              aria-hidden="true"
+                            >
+                              <path
+                                stroke-linecap="round"
+                                stroke-linejoin="round"
+                                d="M6 18L18 6M6 6l12 12"
+                              />
+                            </svg>
+                          </button>
+                        </div>
                       </div>
                     </div>
-                  </div>
-                  <div class="relative mt-6 flex-1 px-4 sm:px-6">
-                    <nav class="flex flex-1 flex-col" aria-label="Sidebar">
-                      <ul role="list" class="flex flex-1 flex-col gap-y-7">
-                        {onLogout ?
+                    <div class="relative mt-6 flex-1 px-4 sm:px-6">
+                      <nav class="flex flex-1 flex-col" aria-label="Sidebar">
+                        <ul role="list" class="flex flex-1 flex-col gap-y-7">
+                          {onLogout ? (
+                            <li>
+                              <a
+                                href="#"
+                                name="logout"
+                                class="text-gray-700 hover:text-indigo-600 
hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 text-sm leading-6 
font-semibold"
+                                onClick={() => {
+                                  onLogout();
+                                  setOpen(false);
+                                }}
+                              >
+                                <svg
+                                  class="h-6 w-6 shrink-0 text-indigo-600"
+                                  fill="none"
+                                  viewBox="0 0 24 24"
+                                  stroke-width="1.5"
+                                  stroke="currentColor"
+                                  aria-hidden="true"
+                                >
+                                  <path
+                                    stroke-linecap="round"
+                                    stroke-linejoin="round"
+                                    d="M2.25 12l8.954-8.955c.44-.439 
1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 
1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 
1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"
+                                  />
+                                </svg>
+                                <i18n.Translate>Log out</i18n.Translate>
+                              </a>
+                            </li>
+                          ) : undefined}
                           <li>
-                            <a href="#"
-                              name="logout"
-                              class="text-gray-700 hover:text-indigo-600 
hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 text-sm leading-6 
font-semibold"
-                              onClick={() => {
-                                onLogout();
-                                setOpen(false)
-                              }}
-                            >
-                              <svg class="h-6 w-6 shrink-0 text-indigo-600" 
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" 
aria-hidden="true">
-                                <path stroke-linecap="round" 
stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 
0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 
1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 
1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
-                              </svg>
-                              <i18n.Translate>Log out</i18n.Translate>
-                            </a>
-                          </li>
-                          : undefined}
-                        <li>
-                          <LangSelector />
-                        </li>
-                        {/* CHILDREN */}
-                        {children}
-                        {/* /CHILDREN */}
-                        {sites.length > 0 ?
-                          <li class="block sm:hidden">
-                            <div class="text-xs font-semibold leading-6 
text-gray-400">
-                              <i18n.Translate>Sites</i18n.Translate>
-                            </div>
-                            <ul role="list" class="space-y-1">
-                              {sites.map(([name, url]) => {
-                                return <li>
-                                  <a href={url} name={`site ${name}`} 
target="_blank" rel="noopener noreferrer" class="text-gray-700 
hover:text-indigo-600 hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 
text-sm leading-6 font-semibold">
-                                    <span class="flex h-6 w-6 shrink-0 
items-center justify-center rounded-lg border text-[0.625rem] font-medium 
bg-white text-gray-400 border-gray-200 group-hover:border-indigo-600 
group-hover:text-indigo-600">&gt;</span>
-                                    <span class="truncate">{name}</span>
-                                  </a>
-                                </li>
-                              })}
-                            </ul>
+                            <LangSelector />
                           </li>
-                          : undefined
-                        }
-                      </ul>
-                    </nav>
+                          {/* CHILDREN */}
+                          {children}
+                          {/* /CHILDREN */}
+                          {sites.length > 0 ? (
+                            <li class="block sm:hidden">
+                              <div class="text-xs font-semibold leading-6 
text-gray-400">
+                                <i18n.Translate>Sites</i18n.Translate>
+                              </div>
+                              <ul role="list" class="space-y-1">
+                                {sites.map(([name, url]) => {
+                                  return (
+                                    <li>
+                                      <a
+                                        href={url}
+                                        name={`site ${name}`}
+                                        target="_blank"
+                                        rel="noopener noreferrer"
+                                        class="text-gray-700 
hover:text-indigo-600 hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 
text-sm leading-6 font-semibold"
+                                      >
+                                        <span class="flex h-6 w-6 shrink-0 
items-center justify-center rounded-lg border text-[0.625rem] font-medium 
bg-white text-gray-400 border-gray-200 group-hover:border-indigo-600 
group-hover:text-indigo-600">
+                                          &gt;
+                                        </span>
+                                        <span class="truncate">{name}</span>
+                                      </a>
+                                    </li>
+                                  );
+                                })}
+                              </ul>
+                            </li>
+                          ) : undefined}
+                        </ul>
+                      </nav>
+                    </div>
                   </div>
                 </div>
               </div>
             </div>
           </div>
         </div>
-      </div>
-    }
-  </Fragment >
+      )}
+    </Fragment>
+  );
 }
diff --git a/packages/web-util/src/components/LangSelector.tsx 
b/packages/web-util/src/components/LangSelector.tsx
index 98b924b92..b08a1bbd2 100644
--- a/packages/web-util/src/components/LangSelector.tsx
+++ b/packages/web-util/src/components/LangSelector.tsx
@@ -46,8 +46,11 @@ function getLangName(s: keyof LangsNames | string): string {
   return String(s);
 }
 
-export function LangSelector({}: {}): VNode {
-  const [updatingLang, setUpdatingLang] = useState(false);
+export function LangSelector({
+  type = "select",
+}: {
+  type?: "select" | "icon";
+}): VNode {
   const { lang, changeLanguage, completeness, supportedLang } =
     useTranslationContext();
   const [hidden, setHidden] = useState(true);
@@ -67,78 +70,109 @@ export function LangSelector({}: {}): VNode {
     };
   }, []);
   return (
-    <div>
-      <div class="relative mt-2">
-        <button
-          type="button"
-          class="relative w-full cursor-default rounded-md bg-white py-1.5 
pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 
focus:outline-none focus:ring-2 focus:ring-indigo-600 sm:text-sm sm:leading-6"
-          aria-haspopup="listbox"
-          aria-expanded="true"
+    <Fragment>
+      {(function () {
+        switch (type) {
+          case "select": {
+            return (
+              <button
+                type="button"
+                class="relative w-full cursor-default rounded-md bg-white 
py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset 
ring-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-600 sm:text-sm 
sm:leading-6"
+                aria-haspopup="listbox"
+                aria-expanded="true"
+                aria-labelledby="listbox-label"
+                onClick={(e) => {
+                  setHidden(!hidden);
+                  e.stopPropagation();
+                }}
+              >
+                <span class="flex items-center">
+                  <img
+                    alt="language"
+                    class="h-5 w-5 flex-shrink-0 rounded-full"
+                    src={langIcon}
+                  />
+                  <span class="ml-3 block truncate">{getLangName(lang)}</span>
+                </span>
+                <span class="pointer-events-none absolute inset-y-0 right-0 
flex items-center pr-2">
+                  <svg
+                    class="h-5 w-5 text-gray-400"
+                    viewBox="0 0 20 20"
+                    fill="currentColor"
+                    aria-hidden="true"
+                  >
+                    <path
+                      fill-rule="evenodd"
+                      d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 
1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 
9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 
0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z"
+                      clip-rule="evenodd"
+                    />
+                  </svg>
+                </span>
+              </button>
+            );
+          }
+          case "icon": {
+            return (
+              <button
+                type="button"
+                class="relative w-full cursor-default rounded-md bg-white p-2 
text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 
focus:outline-none focus:ring-2 focus:ring-indigo-600"
+                onClick={(e) => {
+                  setHidden(!hidden);
+                  e.stopPropagation();
+                }}
+              >
+                <div class="flex">
+                  <img
+                    alt="language"
+                    class="h-7 w-7 flex-shrink-0 rounded-full"
+                    src={langIcon}
+                  />
+                  {/* {lang} */}
+                </div>
+              </button>
+            );
+          }
+        }
+      })()}
+
+      {!hidden && (
+        <ul
+          class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md 
bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 
focus:outline-none sm:text-sm"
+          style={type === "icon" ? { right: 0, width: 200 } : {}}
+          tabIndex={-1}
+          role="listbox"
           aria-labelledby="listbox-label"
-          onClick={(e) => {
-            setHidden(!hidden);
-            e.stopPropagation();
-          }}
+          aria-activedescendant="listbox-option-3"
         >
-          <span class="flex items-center">
-            <img
-              alt="language"
-              class="h-5 w-5 flex-shrink-0 rounded-full"
-              src={langIcon}
-            />
-            <span class="ml-3 block truncate">{getLangName(lang)}</span>
-          </span>
-          <span class="pointer-events-none absolute inset-y-0 right-0 flex 
items-center pr-2">
-            <svg
-              class="h-5 w-5 text-gray-400"
-              viewBox="0 0 20 20"
-              fill="currentColor"
-              aria-hidden="true"
-            >
-              <path
-                fill-rule="evenodd"
-                d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 
4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 
0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 
0l-3.25-3.5a.75.75 0 01.04-1.06z"
-                clip-rule="evenodd"
-              />
-            </svg>
-          </span>
-        </button>
-
-        {!hidden && (
-          <ul
-            class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md 
bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 
focus:outline-none sm:text-sm"
-            tabIndex={-1}
-            role="listbox"
-            aria-labelledby="listbox-label"
-            aria-activedescendant="listbox-option-3"
-          >
-            {Object.keys(supportedLang)
-              .filter((l) => l !== lang)
-              .map((lang) => (
-                <li
-                  class="text-gray-900 hover:bg-indigo-600 hover:text-white 
cursor-pointer relative select-none py-2 pl-3 pr-9"
-                  role="option"
-                  onClick={() => {
-                    changeLanguage(lang);
-                    setUpdatingLang(false);
-                    setHidden(true);
-                  }}
-                >
-                  <span class="font-normal truncate flex justify-between ">
-                    <span>{getLangName(lang)}</span>
-                    <span>{(completeness as any)[lang]}%</span>
-                  </span>
+          {Object.keys(supportedLang)
+            .filter((l) => l !== lang)
+            .map((lang) => (
+              <li
+                class="text-gray-900 hover:bg-indigo-600 hover:text-white 
cursor-pointer relative select-none py-2 pl-3 pr-9"
+                role="option"
+                onClick={() => {
+                  changeLanguage(lang);
+                  setHidden(true);
+                }}
+              >
+                <span class="font-normal truncate flex justify-between ">
+                  <span>{getLangName(lang)}</span>
+                  <span>{(completeness as any)[lang]}%</span>
+                </span>
 
-                  <span class="text-indigo-600 absolute inset-y-0 right-0 flex 
items-center pr-4">
-                    {/* <svg class="h-5 w-5" viewBox="0 0 20 20" 
fill="currentColor" aria-hidden="true">
+                <span class="text-indigo-600 absolute inset-y-0 right-0 flex 
items-center pr-4">
+                  {/* <svg class="h-5 w-5" viewBox="0 0 20 20" 
fill="currentColor" aria-hidden="true">
                         <path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 
01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 
3.893 7.48-9.817a.75.75 0 011.05-.143z" clip-rule="evenodd" />
                       </svg> */}
-                  </span>
-                </li>
-              ))}
-          </ul>
-        )}
-      </div>
-    </div>
+                </span>
+              </li>
+            ))}
+        </ul>
+      )}
+      {/* <div>
+        <div class="relative mt-2">
+        </div>
+    </div> */}
+    </Fragment>
   );
 }
diff --git a/packages/web-util/src/forms/forms.ts 
b/packages/web-util/src/forms/forms.ts
index 1759aa56e..e43459aa4 100644
--- a/packages/web-util/src/forms/forms.ts
+++ b/packages/web-util/src/forms/forms.ts
@@ -182,7 +182,7 @@ export function convertUiField(
           type: config.type,
           properties: {
             ...converBaseFieldsProps(i18n_, config),
-            url: config.url
+            url: config.url,
           },
         };
         return resp;
@@ -351,7 +351,7 @@ function converInputFieldsProps(
   getConverterById: GetConverterById,
 ) {
   const names = p.id.split(".");
-  console.log("NAMES", names, getValueDeeper2(form, names), form)
+  // console.log("NAMES", names, getValueDeeper2(form, names), form)
   return {
     converter: getConverterById(p.converterId, p),
     handler: getValueDeeper2(form, names),
diff --git a/packages/web-util/src/forms/ui-form.ts 
b/packages/web-util/src/forms/ui-form.ts
index a3da71b32..ad5604ef7 100644
--- a/packages/web-util/src/forms/ui-form.ts
+++ b/packages/web-util/src/forms/ui-form.ts
@@ -19,7 +19,7 @@ import {
 } from "@gnu-taler/taler-util";
 import { InternationalizationAPI } from "../index.browser.js";
 
-export type FormConfiguration = DoubleColumnForm;
+export type FormConfiguration = DoubleColumnForm | SingleColumnForm;
 
 export type DoubleColumnForm = {
   type: "double-column";
@@ -27,6 +27,11 @@ export type DoubleColumnForm = {
   // behavior?: (form: Partial<T>) => FormState<T>;
 };
 
+export type SingleColumnForm = {
+  type: "single-column";
+  fields: UIFormElementConfig[];
+};
+
 export type DoubleColumnFormSection = {
   title: string;
   description?: string;
@@ -352,10 +357,17 @@ const codecForDoubleColumnForm = (): 
Codec<DoubleColumnForm> =>
     .property("design", codecForList(codecForDoubleColumnFormSection()))
     .build("DoubleColumnForm");
 
+const codecForSingleColumnForm = (): Codec<SingleColumnForm> =>
+  buildCodecForObject<SingleColumnForm>()
+    .property("type", codecForConstString("single-column"))
+    .property("fields", codecForList(codecForUiFormField()))
+    .build("SingleColumnForm");
+
 const codecForFormConfiguration = (): Codec<FormConfiguration> =>
   buildCodecForUnion<FormConfiguration>()
     .discriminateOn("type")
     .alternative("double-column", codecForDoubleColumnForm())
+    .alternative("single-column", codecForSingleColumnForm())
     .build<FormConfiguration>("FormConfiguration");
 
 const codecForFormMetadata = (): Codec<FormMetadata> =>

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]