[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">></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">
+ >
+ </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.