elbe-ui 0.4.3 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,24 +1,41 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { useSignal } from "@preact/signals";
3
+ import { icons } from "lucide-react";
3
4
  import { route } from "preact-router";
4
- import { Icons } from "../..";
5
- import { ifApiError } from "../../service/s_api";
6
- import { ElbeDialog } from "./dialog";
5
+ import { ElbeDialog, Icon, Icons, toElbeError, } from "../..";
6
+ import { _maybeL10n } from "../util/l10n/_l10n_util";
7
7
  export function ErrorView({ error, retry, debug, }) {
8
- var _a;
9
- const apiError = (_a = ifApiError(error)) !== null && _a !== void 0 ? _a : {
10
- code: 0,
11
- message: "unknown error",
12
- data: error,
13
- };
14
- return !debug ? (_jsx(PrettyErrorView, { apiError: apiError, retry: retry })) : (_jsxs("div", { class: "column padded card inverse cross-stretch", children: [_jsxs("h3", { style: "margin: 0", children: ["ERROR: ", apiError.code] }), _jsx("p", { children: apiError.message }), _jsx("pre", { children: JSON.stringify(apiError.data, null, 2) })] }));
8
+ const apiError = toElbeError(error);
9
+ return !debug ? (_jsx(PrettyErrorView, { error: apiError, retry: retry })) : (_jsxs("div", { class: "column padded card inverse cross-stretch", children: [_jsxs("h3", { style: "margin: 0", children: ["ERROR: ", apiError.code] }), _jsx("p", { children: apiError.message }), _jsx("pre", { children: JSON.stringify(apiError, null, 2) })] }));
15
10
  }
16
- export function PrettyErrorView({ apiError, retry, labels = {
17
- retry: "retry",
18
- home: "go home",
19
- details: "error details",
11
+ export function PrettyErrorView({ error, retry, labels = {
12
+ retry: {
13
+ en: "retry",
14
+ de: "erneut versuchen",
15
+ es: "intentar de nuevo",
16
+ fr: "réessayer",
17
+ it: "riprovare",
18
+ pt: "tentar novamente",
19
+ },
20
+ home: {
21
+ en: "go home",
22
+ de: "zur Startseite",
23
+ es: "a la página de inicio",
24
+ fr: "aller à l'accueil",
25
+ it: "torna alla home",
26
+ pt: "voltar para a página inicial",
27
+ },
28
+ details: {
29
+ en: "error details",
30
+ de: "Fehlerdetails",
31
+ es: "detalles del error",
32
+ fr: "détails de l'erreur",
33
+ it: "dettagli dell'errore",
34
+ pt: "detalhes do erro",
35
+ },
20
36
  }, }) {
21
- var _a, _b, _c;
37
+ var _a, _b, _c, _d, _e, _f;
38
+ const l10n = _maybeL10n();
22
39
  const openSig = useSignal(false);
23
- return (_jsxs("div", { class: "column padded cross-center", style: "margin: 1rem 0", children: [_jsx(Icons.OctagonAlert, {}), _jsx("h3", { style: "margin: 0", children: apiError.code }), _jsx("span", { class: "pointer", onClick: () => (openSig.value = true), children: apiError.message }), retry && (_jsxs("button", { class: "action", onClick: () => retry(), children: [_jsx(Icons.RotateCcw, {}), " ", (_a = labels.retry) !== null && _a !== void 0 ? _a : "retry"] })), apiError.code === 404 && (_jsxs("button", { class: "action", onClick: () => route("/"), children: [_jsx(Icons.House, {}), (_b = labels.home) !== null && _b !== void 0 ? _b : "go home"] })), _jsx(ElbeDialog, { title: (_c = labels.details) !== null && _c !== void 0 ? _c : "error details", open: openSig.value, onClose: () => (openSig.value = false), children: _jsx("pre", { class: "card inverse", children: JSON.stringify(apiError.data, null, 2) }) })] }));
40
+ return (_jsxs("div", { class: "column padded cross-center", style: "margin: 1rem 0", children: [_jsx(Icon, { icon: (_a = error.icon) !== null && _a !== void 0 ? _a : icons.OctagonAlert }), _jsx("h4", { style: "margin: 0", children: (_b = l10n === null || l10n === void 0 ? void 0 : l10n.inline(error.message)) !== null && _b !== void 0 ? _b : "error" }), _jsx("span", { class: "pointer", onClick: () => (openSig.value = true), children: (_c = l10n === null || l10n === void 0 ? void 0 : l10n.inline(error.description)) !== null && _c !== void 0 ? _c : "" }), retry && (_jsxs("button", { class: "action", onClick: () => retry(), children: [_jsx(Icons.RotateCcw, {}), " ", (_d = l10n === null || l10n === void 0 ? void 0 : l10n.inline(labels.retry)) !== null && _d !== void 0 ? _d : "retry"] })), error.code === 404 && (_jsxs("button", { class: "action", onClick: () => route("/"), children: [_jsx(Icons.House, {}), (_e = l10n === null || l10n === void 0 ? void 0 : l10n.inline(labels.home)) !== null && _e !== void 0 ? _e : "go home"] })), _jsx(ElbeDialog, { title: (_f = l10n === null || l10n === void 0 ? void 0 : l10n.inline(labels.details)) !== null && _f !== void 0 ? _f : "error details", open: openSig.value, onClose: () => (openSig.value = false), children: _jsx("pre", { class: "card inverse", children: `code: ${error.code}\n\n` + JSON.stringify(error.details, null, 2) }) })] }));
24
41
  }
@@ -5,12 +5,13 @@ export type FooterLink = {
5
5
  href: string;
6
6
  icon?: IconChild;
7
7
  };
8
- export declare function Footer({ left, right, copyright, version, legal, }: {
9
- left: (FooterLink | React.ReactNode)[];
8
+ export declare function Footer({ left, right, copyright, version, legal, marginTop, }: {
9
+ left?: (FooterLink | React.ReactNode)[];
10
10
  right?: (FooterLink | React.ReactNode)[];
11
11
  copyright?: string | React.ReactNode;
12
12
  version?: string;
13
13
  legal?: FooterLink;
14
+ marginTop?: number;
14
15
  }): React.JSX.Element;
15
16
  export declare function _WrittenIn({ city, href, }: {
16
17
  city?: string;
@@ -1,13 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { HeartIcon } from "lucide-react";
3
- import { Card, Column, FlexSpace, Link, Row } from "../..";
4
- export function Footer({ left, right, copyright, version, legal, }) {
3
+ import { Card, Column, FlexSpace, Link, Row, useLayoutMode, } from "../..";
4
+ export function Footer({ left, right, copyright, version, legal, marginTop, }) {
5
+ const layoutMode = useLayoutMode();
5
6
  return (_jsx(Card, { scheme: "secondary", sharp: true, role: "contentinfo", ariaLabel: "footer", style: {
6
7
  borderLeft: "none",
7
8
  borderRight: "none",
8
9
  borderBottom: "none",
10
+ borderTopLeftRadius: layoutMode === "wide" ? "var(--g-radius)" : null,
9
11
  color: "color-mix(in srgb, var(--c-context-front) 60%, transparent)",
10
- }, children: _jsxs(Column, { gap: 0.5, children: [_jsxs(Row, { main: "space-between", cross: "start", children: [_jsx(Column, { gap: 0.5, flex: 1, cross: "start", children: left.map((item) => (item.label ? _jsx(_Link, Object.assign({}, item)) : item)) }), right && (_jsx(Column, { gap: 0.5, flex: 1, cross: "end", children: right.map((item) => item.label ? _jsx(_Link, Object.assign({}, item)) : item) }))] }), (copyright || version || legal) && _jsx("hr", { style: { opacity: 0.7 } }), (copyright || version || legal) && (_jsxs(Row, { children: [copyright &&
12
+ marginTop: `${marginTop !== null && marginTop !== void 0 ? marginTop : 0}rem`,
13
+ }, children: _jsxs(Column, { gap: 0.5, children: [_jsxs(Row, { main: "space-between", cross: "start", children: [left && (_jsx(Column, { gap: 0.5, flex: 1, cross: "start", children: left.map((item) => item.label ? _jsx(_Link, Object.assign({}, item)) : item) })), right && (_jsx(Column, { gap: 0.5, flex: 1, cross: "end", children: right.map((item) => item.label ? _jsx(_Link, Object.assign({}, item)) : item) }))] }), (left || right) && (copyright || version || legal) && (_jsx("hr", { style: { opacity: 0.7 } })), (copyright || version || legal) && (_jsxs(Row, { children: [copyright &&
11
14
  (typeof copyright === "string" ? _jsx("b", { children: copyright }) : copyright), version && _jsx(_Version, { version: version }), _jsx(FlexSpace, {}), legal && _jsx(_Link, Object.assign({}, legal))] }))] }) }));
12
15
  }
13
16
  function _Version({ version }) {
@@ -28,10 +28,10 @@ export function Header(p) {
28
28
  borderColor: tConfig.highVis || scroll ? null : "transparent",
29
29
  gap: "1rem",
30
30
  zIndex: 80,
31
- }, children: [p.leading === "back" && _backBtn, p.leading === "close" && _closeBtn, p.leading && p.leading !== "back" && p.leading !== "close"
31
+ }, children: [p.leading && p.leading !== "back" && p.leading !== "close"
32
32
  ? p.leading
33
33
  : appBase &&
34
- layoutMode != "wide" && (_jsx(IconButton.plain, { ariaLabel: "open/close menu", onTap: () => appBase.setMenuOpen(!appBase.menuOpen), icon: MenuIcon })), _jsx(_Logo, { logo: (_a = p.logo) !== null && _a !== void 0 ? _a : appBase === null || appBase === void 0 ? void 0 : appBase.icons.logo, logoDark: (_b = p.logoDark) !== null && _b !== void 0 ? _b : appBase === null || appBase === void 0 ? void 0 : appBase.icons.logoDark, lMargin: 0.5 }), typeof p.title === "string" ? (_jsx(Text.h3, { style: {
34
+ layoutMode != "wide" && (_jsx(IconButton.plain, { ariaLabel: "open/close menu", onTap: () => appBase.setMenuOpen(!appBase.menuOpen), icon: MenuIcon })), p.leading === "back" && _backBtn, p.leading === "close" && _closeBtn, _jsx(_Logo, { logo: (_a = p.logo) !== null && _a !== void 0 ? _a : appBase === null || appBase === void 0 ? void 0 : appBase.icons.logo, logoDark: (_b = p.logoDark) !== null && _b !== void 0 ? _b : appBase === null || appBase === void 0 ? void 0 : appBase.icons.logoDark, lMargin: 0.5 }), typeof p.title === "string" ? (_jsx(Text.h3, { style: {
35
35
  marginLeft: !appBase || layoutMode === "wide" ? ".5rem" : 0,
36
36
  }, align: p.centerTitle ? "center" : "start", flex: 1, v: p.title })) : (_jsx("div", { style: { flex: 1 }, children: p.title })), _jsx(_Toolbar, { actions: [...((_c = p.actions) !== null && _c !== void 0 ? _c : []), ...((_d = appBase === null || appBase === void 0 ? void 0 : appBase.globalActions) !== null && _d !== void 0 ? _d : [])] }), layoutMode === "wide" && (_jsx(_Logo, { logo: (_e = p.endLogo) !== null && _e !== void 0 ? _e : appBase === null || appBase === void 0 ? void 0 : appBase.icons.endLogo, logoDark: (_f = p.endLogoDark) !== null && _f !== void 0 ? _f : appBase === null || appBase === void 0 ? void 0 : appBase.icons.endLogoDark, rMargin: 0.5 }))] }));
37
37
  }
@@ -0,0 +1,30 @@
1
+ import { Maybe } from "../util";
2
+ import { L10nInlinePlain } from "./l10n";
3
+ export type _LocaleID = `${string}_${string}` | `${string}`;
4
+ export type _LocaleIDEasy = `${string}_${string}_easy` | _LocaleID;
5
+ export type _L10nData = {
6
+ [key: string]: any;
7
+ };
8
+ export declare function _bestMatch(selection: _L10nSelection, locales: _LocaleIDEasy[]): _LocaleIDEasy | null;
9
+ type _Locale = {
10
+ full: string;
11
+ lang: string;
12
+ region: string | null;
13
+ easy: boolean;
14
+ };
15
+ export declare function _locale(l: string): _Locale;
16
+ export interface _L10nSelection {
17
+ locale: string;
18
+ easy: boolean;
19
+ }
20
+ export interface _L10nState<T extends _L10nData> extends _L10nSelection {
21
+ /** the current locale */
22
+ c: T;
23
+ setLocale: (locale: string) => void;
24
+ setEasyLang: (easyLang: boolean) => void;
25
+ inline: (value: Maybe<L10nInlinePlain | string>) => string;
26
+ }
27
+ export declare const _L10nContext: import("preact").Context<_L10nState<any> | null>;
28
+ export declare function _maybeL10n<T extends _L10nData>(): _L10nState<T> | null;
29
+ export declare function _useL10n<T extends _L10nData>(): _L10nState<T>;
30
+ export {};
@@ -0,0 +1,40 @@
1
+ import { createContext, useContext } from "preact/compat";
2
+ export function _bestMatch(selection, locales) {
3
+ if (locales.length === 0)
4
+ return null;
5
+ const loc = _locale(selection.locale.replaceAll("-", "_"));
6
+ const asLocales = locales.map((l) => _locale(l));
7
+ // filter by language
8
+ const langMatch = asLocales.filter((l) => loc.lang === l.lang);
9
+ // filter by easy
10
+ const easyMatch = langMatch.filter((l) => l.easy === selection.easy);
11
+ // filter by region
12
+ const regionMatch = easyMatch.filter((l) => l.region === loc.region);
13
+ if (regionMatch.length > 0)
14
+ return regionMatch[0].full;
15
+ if (easyMatch.length > 0)
16
+ return easyMatch[0].full;
17
+ if (langMatch.length > 0)
18
+ return langMatch[0].full;
19
+ return null;
20
+ }
21
+ export function _locale(l) {
22
+ const parts = l.split("_");
23
+ const easy = parts.length > 2 && parts[2] === "easy";
24
+ const region = parts.length > 1 ? parts[1].toLowerCase() : null;
25
+ const lang = parts[0].toLowerCase();
26
+ return { full: l, lang, region, easy };
27
+ }
28
+ export const _L10nContext = createContext(null);
29
+ export function _maybeL10n() {
30
+ const ctx = useContext(_L10nContext);
31
+ if (!ctx)
32
+ return null;
33
+ return ctx;
34
+ }
35
+ export function _useL10n() {
36
+ const ctx = useContext(_L10nContext);
37
+ if (!ctx)
38
+ throw new Error("useL10n must be used within a L10n");
39
+ return ctx;
40
+ }
@@ -1,18 +1,9 @@
1
1
  import React from "preact/compat";
2
2
  import { ElbeChildren } from "../types";
3
- type _LocaleID = `${string}_${string}` | `${string}`;
4
- type _LocaleIDEasy = `${string}_${string}_easy` | _LocaleID;
5
- type _L10nData = {
6
- [key: string]: any;
3
+ import { _L10nData, _L10nState, _LocaleID, _LocaleIDEasy } from "./_l10n_util";
4
+ export type L10nInlinePlain = {
5
+ [key: _LocaleIDEasy]: string;
7
6
  };
8
- export interface _L10nState<T extends _L10nData> {
9
- /** the current locale */
10
- c: T;
11
- locale: string;
12
- easyLang: boolean;
13
- setLocale: (locale: string) => void;
14
- setEasyLang: (easyLang: boolean) => void;
15
- }
16
7
  type _L10nProps = {
17
8
  children: ElbeChildren;
18
9
  initialLocale?: _LocaleID;
@@ -1,13 +1,6 @@
1
1
  import { jsx as _jsx } from "preact/jsx-runtime";
2
- import { createContext } from "preact";
3
- import { useContext, useEffect, useState } from "preact/hooks";
4
- const _L10nContext = createContext(null);
5
- function _useL10n() {
6
- const ctx = useContext(_L10nContext);
7
- if (!ctx)
8
- throw new Error("useL10n must be used within a L10n");
9
- return ctx;
10
- }
2
+ import { useEffect, useState } from "preact/hooks";
3
+ import { _bestMatch, _L10nContext, _useL10n, } from "./_l10n_util";
11
4
  /**
12
5
  * L10nBase is a function that creates a localization context provider and a hook to use the localization context.
13
6
  * @param fallback this is the fallback locale, which is used if no other locale is found. It is the basis for the locale type and must thus be complete.
@@ -15,7 +8,7 @@ function _useL10n() {
15
8
  * @returns an object with the L10n component and the useL10n hook.
16
9
  */
17
10
  export function makeL10n(fallback, supported) {
18
- if (Object.keys(supported).length === 0) {
11
+ if (Object.keys(fallback).length === 0) {
19
12
  throw new Error("L10nBase: No fallback locales provided");
20
13
  }
21
14
  const fallbackLocale = Object.keys(fallback)[0];
@@ -24,45 +17,30 @@ export function makeL10n(fallback, supported) {
24
17
  const useL10n = (_useL10n);
25
18
  const L10n = (p) => {
26
19
  var _a, _b;
27
- const [loc, setLoc] = useState((_a = p.initialLocale) !== null && _a !== void 0 ? _a : navigator.language);
28
- const [easy, setEasy] = useState((_b = p.initialEasyLang) !== null && _b !== void 0 ? _b : false);
20
+ const [locale, setLocale] = useState({
21
+ locale: (_a = p.initialLocale) !== null && _a !== void 0 ? _a : navigator.language,
22
+ easy: (_b = p.initialEasyLang) !== null && _b !== void 0 ? _b : false,
23
+ });
29
24
  const [currentLang, setCurrentLang] = useState(fallbackValue);
30
25
  useEffect(() => {
31
26
  var _a;
32
- const match = _bestMatch(loc, easy, Object.keys(supportedLocales));
27
+ const match = _bestMatch(locale, Object.keys(supportedLocales));
33
28
  setCurrentLang(Object.assign(Object.assign({}, fallbackValue), ((_a = supportedLocales[match !== null && match !== void 0 ? match : ""]) !== null && _a !== void 0 ? _a : {})));
34
- }, [loc, easy]);
35
- return (_jsx(_L10nContext.Provider, { value: {
36
- c: currentLang,
37
- locale: loc,
38
- easyLang: easy,
39
- setLocale: (l) => setLoc(l),
40
- setEasyLang: (e) => setEasy(e),
41
- }, children: p.children }));
29
+ }, [locale]);
30
+ return (_jsx(_L10nContext.Provider, { value: Object.assign(Object.assign({}, locale), { c: currentLang, setLocale: (l) => setLocale(Object.assign(Object.assign({}, locale), { locale: l })), setEasyLang: (e) => setLocale(Object.assign(Object.assign({}, locale), { easy: e })), inline: _l10nInlineResolver(locale) }), children: p.children }));
42
31
  };
43
32
  return { L10n, useL10n };
44
33
  }
45
- function _locale(l) {
46
- const parts = l.split("_");
47
- const easy = parts.length > 2 && parts[2] === "easy";
48
- const region = parts.length > 1 ? parts[1].toLowerCase() : null;
49
- const lang = parts[0].toLowerCase();
50
- return { full: l, lang, region, easy };
51
- }
52
- function _bestMatch(locale, easy, locales) {
53
- const loc = _locale(locale.replaceAll("-", "_"));
54
- const asLocales = locales.map((l) => _locale(l));
55
- // filter by language
56
- const langMatch = asLocales.filter((l) => loc.lang === l.lang);
57
- // filter by easy
58
- const easyMatch = langMatch.filter((l) => l.easy === easy);
59
- // filter by region
60
- const regionMatch = easyMatch.filter((l) => l.region === loc.region);
61
- if (regionMatch.length > 0)
62
- return regionMatch[0].full;
63
- if (easyMatch.length > 0)
64
- return easyMatch[0].full;
65
- if (langMatch.length > 0)
66
- return langMatch[0].full;
67
- return null;
34
+ function _l10nInlineResolver(locale) {
35
+ return (value) => {
36
+ var _a, _b, _c, _d;
37
+ if (!value)
38
+ return "";
39
+ if (typeof value === "string")
40
+ return value;
41
+ const locales = Object.keys(value);
42
+ const bestMatch = _bestMatch(locale, locales);
43
+ const anyEnglish = (_b = (_a = locales.find((l) => l.startsWith("en"))) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : "en";
44
+ return (_d = (_c = value[bestMatch !== null && bestMatch !== void 0 ? bestMatch : anyEnglish]) !== null && _c !== void 0 ? _c : value[locales[0]]) !== null && _d !== void 0 ? _d : "";
45
+ };
68
46
  }
@@ -1,4 +1,6 @@
1
1
  import { int } from "../..";
2
+ export type Maybe<T> = T | null | undefined;
3
+ export type PromiseOr<T> = Promise<T> | T;
2
4
  export declare function clamp(value: number, min: number, max: number): number;
3
5
  export declare function classString(classes: (string | false | null | undefined)[]): string;
4
6
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elbe-ui",
3
- "version": "0.4.3",
3
+ "version": "0.4.7",
4
4
  "author": "Robin Naumann",
5
5
  "license": "MIT",
6
6
  "repository": {