elbe-ui 0.2.26 → 0.2.30
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.
- package/dist/elbe.css +621 -0
- package/dist/index.d.ts +1548 -17
- package/dist/index.js +63 -18577
- package/package.json +15 -13
- package/dist/bit/bit.d.ts +0 -34
- package/dist/bit/ctrl_bit.d.ts +0 -30
- package/dist/service/s_api.d.ts +0 -30
- package/dist/ui/color_theme.d.ts +0 -5
- package/dist/ui/components/badge.d.ts +0 -25
- package/dist/ui/components/box.d.ts +0 -1027
- package/dist/ui/components/button.d.ts +0 -23
- package/dist/ui/components/card.d.ts +0 -14
- package/dist/ui/components/dialog.d.ts +0 -8
- package/dist/ui/components/error_view.d.ts +0 -15
- package/dist/ui/components/flex.d.ts +0 -11
- package/dist/ui/components/icon_button.d.ts +0 -19
- package/dist/ui/components/input/checkbox.d.ts +0 -6
- package/dist/ui/components/input/input_field.d.ts +0 -22
- package/dist/ui/components/input/range.d.ts +0 -8
- package/dist/ui/components/input/select.d.ts +0 -10
- package/dist/ui/components/input/text_area.d.ts +0 -10
- package/dist/ui/components/padded.d.ts +0 -25
- package/dist/ui/components/scaffold.d.ts +0 -23
- package/dist/ui/components/spinner.d.ts +0 -3
- package/dist/ui/components/text.d.ts +0 -33
- package/dist/ui/components/toggle_button.d.ts +0 -12
- package/dist/ui/components/util.d.ts +0 -3
- package/dist/ui/util/confirm_dialog.d.ts +0 -10
- package/dist/ui/util/error_view.d.ts +0 -1
- package/dist/ui/util/toast.d.ts +0 -5
- package/dist/ui/util/util.d.ts +0 -19
- package/elbe.scss +0 -100
- package/src/bit/bit.tsx +0 -128
- package/src/bit/ctrl_bit.tsx +0 -112
- package/src/index.tsx +0 -29
- package/src/service/s_api.ts +0 -102
- package/src/ui/color_theme.ts +0 -24
- package/src/ui/components/badge.tsx +0 -78
- package/src/ui/components/box.tsx +0 -49
- package/src/ui/components/button.tsx +0 -61
- package/src/ui/components/card.tsx +0 -45
- package/src/ui/components/dialog.tsx +0 -51
- package/src/ui/components/error_view.tsx +0 -72
- package/src/ui/components/flex.tsx +0 -64
- package/src/ui/components/icon_button.tsx +0 -56
- package/src/ui/components/input/checkbox.tsx +0 -32
- package/src/ui/components/input/input_field.tsx +0 -57
- package/src/ui/components/input/range.tsx +0 -37
- package/src/ui/components/input/select.tsx +0 -29
- package/src/ui/components/input/text_area.tsx +0 -45
- package/src/ui/components/padded.tsx +0 -62
- package/src/ui/components/scaffold.tsx +0 -79
- package/src/ui/components/spinner.tsx +0 -11
- package/src/ui/components/text.tsx +0 -78
- package/src/ui/components/toggle_button.tsx +0 -52
- package/src/ui/components/util.tsx +0 -3
- package/src/ui/util/confirm_dialog.ts +0 -53
- package/src/ui/util/error_view.tsx +0 -16
- package/src/ui/util/toast.ts +0 -14
- package/src/ui/util/util.ts +0 -36
- package/style/color_style.scss +0 -149
- package/style/components.scss +0 -476
- package/style/root.scss +0 -50
- package/style/type_style.scss +0 -22
package/src/index.tsx
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import * as Lucide from "lucide-react";
|
|
2
|
-
|
|
3
|
-
// exports
|
|
4
|
-
export * from "./bit/bit";
|
|
5
|
-
export * from "./bit/ctrl_bit";
|
|
6
|
-
export * from "./service/s_api";
|
|
7
|
-
export * from "./ui/color_theme";
|
|
8
|
-
export * from "./ui/components/badge";
|
|
9
|
-
export * from "./ui/components/box";
|
|
10
|
-
export * from "./ui/components/button";
|
|
11
|
-
export * from "./ui/components/card";
|
|
12
|
-
export * from "./ui/components/dialog";
|
|
13
|
-
export * from "./ui/components/flex";
|
|
14
|
-
export * from "./ui/components/icon_button";
|
|
15
|
-
export * from "./ui/components/input/checkbox";
|
|
16
|
-
export * from "./ui/components/input/input_field";
|
|
17
|
-
export * from "./ui/components/input/range";
|
|
18
|
-
export * from "./ui/components/input/select";
|
|
19
|
-
export * from "./ui/components/padded";
|
|
20
|
-
export * from "./ui/components/scaffold";
|
|
21
|
-
export * from "./ui/components/spinner";
|
|
22
|
-
export * from "./ui/components/text";
|
|
23
|
-
export * from "./ui/components/toggle_button";
|
|
24
|
-
export * from "./ui/components/util";
|
|
25
|
-
export * from "./ui/util/confirm_dialog";
|
|
26
|
-
export * from "./ui/util/toast";
|
|
27
|
-
export * from "./ui/util/util";
|
|
28
|
-
|
|
29
|
-
export const Icons = Lucide.icons;
|
package/src/service/s_api.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
export interface PostArgs {
|
|
2
|
-
path?: { [key: string]: string | number | boolean | undefined };
|
|
3
|
-
query?: { [key: string]: string | number | boolean | undefined };
|
|
4
|
-
body?: any;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const _noArgs: PostArgs = {};
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* ApiService is a simple wrapper around fetch that handles JSON serialization and error handling.
|
|
11
|
-
* to use it, you must first call `ApiService.init(apiURL)` with the base URL of your API.
|
|
12
|
-
*/
|
|
13
|
-
export class ApiService {
|
|
14
|
-
private static _i: ApiService | null = null;
|
|
15
|
-
public static get i(): ApiService {
|
|
16
|
-
if (!ApiService._i) throw "ApiService not initialized. Call ApiService.init(apiURL)";
|
|
17
|
-
return ApiService._i;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
private constructor(private apiURL: string) {}
|
|
21
|
-
|
|
22
|
-
static init(apiURL: string) {
|
|
23
|
-
if (ApiService._i) throw "ApiService already initialized";
|
|
24
|
-
ApiService._i = new ApiService(apiURL);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
private async _fetch(
|
|
28
|
-
p: string,
|
|
29
|
-
method: "GET" | "POST" | "DELETE",
|
|
30
|
-
{ path, query, body }: PostArgs
|
|
31
|
-
): Promise<any> {
|
|
32
|
-
try {
|
|
33
|
-
p = path
|
|
34
|
-
? p.replace(/:([a-zA-Z0-9_]+)/g, (m, p1) => {
|
|
35
|
-
const v = path[p1];
|
|
36
|
-
if (v === undefined)
|
|
37
|
-
throw { code: 400, message: `missing parameter ${p1}` };
|
|
38
|
-
return v?.toString() ?? "";
|
|
39
|
-
})
|
|
40
|
-
: p;
|
|
41
|
-
|
|
42
|
-
const queryStr =
|
|
43
|
-
query != null ? "?" + new URLSearchParams(query as any).toString() : "";
|
|
44
|
-
const response = await fetch(this.apiURL + p + queryStr, {
|
|
45
|
-
method,
|
|
46
|
-
credentials: "include",
|
|
47
|
-
headers: { "Content-Type": "application/json" },
|
|
48
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
49
|
-
});
|
|
50
|
-
if (response.ok) {
|
|
51
|
-
try {
|
|
52
|
-
return await response.json();
|
|
53
|
-
} catch (e) {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
let data = null;
|
|
58
|
-
try {
|
|
59
|
-
data = await response.clone().json();
|
|
60
|
-
} catch (e) {
|
|
61
|
-
data = await response.text();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
throw {
|
|
65
|
-
code: response.status,
|
|
66
|
-
message: data.message ?? "undefined error",
|
|
67
|
-
data,
|
|
68
|
-
} as ApiError;
|
|
69
|
-
} catch (e) {
|
|
70
|
-
rethrow(e, 0, "unknown error");
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async get(path: string, args?: PostArgs): Promise<any> {
|
|
75
|
-
return this._fetch(path, "GET", args || _noArgs);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async post(path: string, args: PostArgs): Promise<any> {
|
|
79
|
-
return this._fetch(path, "POST", args || _noArgs);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async delete(path: string, args: PostArgs): Promise<any> {
|
|
83
|
-
return this._fetch(path, "DELETE", args || _noArgs);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function rethrow(e: any, code: number, message: string): ApiError {
|
|
88
|
-
// if e implements the apiError interface, rethrow it:
|
|
89
|
-
if (e && e.code !== null && e.message !== null) throw e;
|
|
90
|
-
throw { code, message, data: e };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export interface ApiError {
|
|
94
|
-
code: number;
|
|
95
|
-
message: string;
|
|
96
|
-
data?: any;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function ifApiError(e: any): ApiError | null {
|
|
100
|
-
if (e && e.code !== null && e.message !== null) return e;
|
|
101
|
-
return null;
|
|
102
|
-
}
|
package/src/ui/color_theme.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export type ElbeColorStyles =
|
|
2
|
-
| "accent"
|
|
3
|
-
| "error"
|
|
4
|
-
| "warning"
|
|
5
|
-
| "success"
|
|
6
|
-
| "info";
|
|
7
|
-
|
|
8
|
-
export type ElbeColorManners = "major" | "minor" | "action" | "integrated";
|
|
9
|
-
|
|
10
|
-
export type ElbeColorThemes = "primary" | "secondary" | "inverse";
|
|
11
|
-
|
|
12
|
-
export type ElbeColorModes = "light" | "dark";
|
|
13
|
-
|
|
14
|
-
export type ElbeTypeStyles =
|
|
15
|
-
| "header-1"
|
|
16
|
-
| "header-2"
|
|
17
|
-
| "header-3"
|
|
18
|
-
| "header-4"
|
|
19
|
-
| "header-5"
|
|
20
|
-
| "header-6"
|
|
21
|
-
| "text-s"
|
|
22
|
-
| "text-m"
|
|
23
|
-
| "text-l"
|
|
24
|
-
| "code";
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import React from "preact/compat";
|
|
2
|
-
import type { ElbeColorStyles } from "../color_theme";
|
|
3
|
-
import type { ElbeChild, ElbeChildren } from "../util/util";
|
|
4
|
-
import type { ElbeProps } from "./box";
|
|
5
|
-
|
|
6
|
-
export type BadgeProps = {
|
|
7
|
-
count?: number;
|
|
8
|
-
message?: string;
|
|
9
|
-
child?: ElbeChild;
|
|
10
|
-
hidden?: boolean;
|
|
11
|
-
children?: ElbeChildren;
|
|
12
|
-
} & ElbeProps;
|
|
13
|
-
|
|
14
|
-
export function TestBadge(p: BadgeProps) {
|
|
15
|
-
return new Badge({ ...p, colorStyle: "accent" });
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class Badge extends React.Component<
|
|
19
|
-
BadgeProps & { colorStyle: ElbeColorStyles }
|
|
20
|
-
> {
|
|
21
|
-
constructor(props: BadgeProps & { colorStyle: ElbeColorStyles }) {
|
|
22
|
-
super(props);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
static accent(p: BadgeProps) {
|
|
26
|
-
return <Badge {...p} colorStyle="accent" />;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
static error(p: BadgeProps) {
|
|
30
|
-
return <Badge {...p} colorStyle="error" />;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
static warning(p: BadgeProps) {
|
|
34
|
-
return <Badge {...p} colorStyle="warning" />;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
static success(p: BadgeProps) {
|
|
38
|
-
return <Badge {...p} colorStyle="success" />;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
static info(p: BadgeProps) {
|
|
42
|
-
return <Badge {...p} colorStyle="info" />;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
render() {
|
|
46
|
-
return (
|
|
47
|
-
<div
|
|
48
|
-
style={{
|
|
49
|
-
position: "relative",
|
|
50
|
-
display: "inline-block",
|
|
51
|
-
}}
|
|
52
|
-
>
|
|
53
|
-
{this.props.child}
|
|
54
|
-
{this.props.children}
|
|
55
|
-
<div
|
|
56
|
-
class={`b ${this.props.colorStyle} ${this.props.class ?? ""}`}
|
|
57
|
-
style={{
|
|
58
|
-
position: "absolute",
|
|
59
|
-
top: "-0.25rem",
|
|
60
|
-
right: "-0.25rem",
|
|
61
|
-
minWidth: "1.5rem",
|
|
62
|
-
minHeight: "1.5rem",
|
|
63
|
-
padding: "0rem .4rem",
|
|
64
|
-
borderRadius: "3rem",
|
|
65
|
-
fontWeight: "bold",
|
|
66
|
-
display: "flex",
|
|
67
|
-
justifyContent: "center",
|
|
68
|
-
alignItems: "center",
|
|
69
|
-
visibility: this.props.hidden ? "hidden" : "visible",
|
|
70
|
-
...this.props.style,
|
|
71
|
-
}}
|
|
72
|
-
>
|
|
73
|
-
{this.props.message ?? this.props.count}
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { h } from "preact";
|
|
2
|
-
import type { ElbeColorModes, ElbeColorThemes } from "../color_theme";
|
|
3
|
-
import type { ElbeChildren } from "../util/util";
|
|
4
|
-
|
|
5
|
-
export type ElbeProps = {
|
|
6
|
-
class?: string;
|
|
7
|
-
style?: React.CSSProperties;
|
|
8
|
-
tooltip?: string;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export function applyProps(
|
|
12
|
-
p: ElbeProps,
|
|
13
|
-
classes?: string | null | (string | false | null | undefined)[],
|
|
14
|
-
style?: React.CSSProperties
|
|
15
|
-
) {
|
|
16
|
-
if (Array.isArray(classes)) {
|
|
17
|
-
classes = classes.filter((c) => c).join(" ");
|
|
18
|
-
}
|
|
19
|
-
return {
|
|
20
|
-
class: `${classes || ""} ${p.class || ""}`,
|
|
21
|
-
style: { ...(style ?? {}), ...(p.style ?? {}) },
|
|
22
|
-
...(p.tooltip ? { ["data-tooltip"]: p.tooltip } : {}),
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function Box({
|
|
27
|
-
mode,
|
|
28
|
-
scheme = "primary",
|
|
29
|
-
padding = 0,
|
|
30
|
-
margin = 0,
|
|
31
|
-
children,
|
|
32
|
-
...elbe
|
|
33
|
-
}: {
|
|
34
|
-
mode?: ElbeColorModes;
|
|
35
|
-
scheme?: ElbeColorThemes;
|
|
36
|
-
padding?: number;
|
|
37
|
-
margin?: number;
|
|
38
|
-
children: ElbeChildren;
|
|
39
|
-
} & ElbeProps) {
|
|
40
|
-
return h(
|
|
41
|
-
"div",
|
|
42
|
-
applyProps(elbe, [scheme, mode], {
|
|
43
|
-
padding: `${padding}rem`,
|
|
44
|
-
margin: `${margin}rem`,
|
|
45
|
-
...elbe.style,
|
|
46
|
-
}),
|
|
47
|
-
children
|
|
48
|
-
);
|
|
49
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import React from "preact/compat";
|
|
2
|
-
import type { ElbeColorManners, ElbeColorStyles } from "../color_theme";
|
|
3
|
-
import { _ElbeErr } from "../util/error_view";
|
|
4
|
-
import { applyProps, type ElbeProps } from "./box";
|
|
5
|
-
import type { IconChild } from "./icon_button";
|
|
6
|
-
|
|
7
|
-
export type ButtonProps = ElbeProps & {
|
|
8
|
-
colorStyle?: ElbeColorStyles;
|
|
9
|
-
onTap?: () => void;
|
|
10
|
-
} & (
|
|
11
|
-
| { icon?: IconChild; label: string }
|
|
12
|
-
| {
|
|
13
|
-
icon: IconChild;
|
|
14
|
-
label?: string;
|
|
15
|
-
}
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
export class Button extends React.Component<
|
|
19
|
-
ButtonProps & {
|
|
20
|
-
colorManner: ElbeColorManners;
|
|
21
|
-
}
|
|
22
|
-
> {
|
|
23
|
-
static major = (p: ButtonProps) => _btn(p, "major");
|
|
24
|
-
static minor = (p: ButtonProps) => _btn(p, "minor");
|
|
25
|
-
static action = (p: ButtonProps) => _btn(p, "action");
|
|
26
|
-
static integrated = (p: ButtonProps) => _btn(p, "integrated");
|
|
27
|
-
|
|
28
|
-
render() {
|
|
29
|
-
return _btn(this.props, this.props.colorManner);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function _btn(
|
|
34
|
-
{ colorStyle, onTap, icon, label, ...elbe }: ButtonProps,
|
|
35
|
-
colorManner: ElbeColorManners
|
|
36
|
-
) {
|
|
37
|
-
return label || icon ? (
|
|
38
|
-
<button
|
|
39
|
-
{...applyProps(
|
|
40
|
-
elbe,
|
|
41
|
-
[
|
|
42
|
-
"row",
|
|
43
|
-
"main-center",
|
|
44
|
-
"gap-half",
|
|
45
|
-
colorStyle ?? "accent",
|
|
46
|
-
colorManner,
|
|
47
|
-
!onTap && "disabled",
|
|
48
|
-
],
|
|
49
|
-
{
|
|
50
|
-
border: "none",
|
|
51
|
-
}
|
|
52
|
-
)}
|
|
53
|
-
onClick={() => onTap && onTap()}
|
|
54
|
-
>
|
|
55
|
-
{typeof icon === "function" ? icon({}) : icon}
|
|
56
|
-
{label && <span>{label}</span>}
|
|
57
|
-
</button>
|
|
58
|
-
) : (
|
|
59
|
-
_ElbeErr("Button requires either an icon or a message")
|
|
60
|
-
);
|
|
61
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ElbeColorManners,
|
|
3
|
-
ElbeColorModes,
|
|
4
|
-
ElbeColorStyles,
|
|
5
|
-
ElbeColorThemes,
|
|
6
|
-
} from "../color_theme";
|
|
7
|
-
import type { ElbeChildren } from "../util/util";
|
|
8
|
-
import { applyProps, type ElbeProps } from "./box";
|
|
9
|
-
|
|
10
|
-
export function Card({
|
|
11
|
-
mode,
|
|
12
|
-
colorScheme = "primary",
|
|
13
|
-
colorStyle,
|
|
14
|
-
colorManner,
|
|
15
|
-
padding = 1,
|
|
16
|
-
margin = 0,
|
|
17
|
-
onTap,
|
|
18
|
-
onLongTap,
|
|
19
|
-
children,
|
|
20
|
-
...elbe
|
|
21
|
-
}: {
|
|
22
|
-
mode?: ElbeColorModes;
|
|
23
|
-
colorScheme?: ElbeColorThemes;
|
|
24
|
-
colorStyle?: ElbeColorStyles;
|
|
25
|
-
colorManner?: ElbeColorManners;
|
|
26
|
-
padding?: number;
|
|
27
|
-
margin?: number;
|
|
28
|
-
onTap?: () => void;
|
|
29
|
-
onLongTap?: () => void;
|
|
30
|
-
children: ElbeChildren;
|
|
31
|
-
} & ElbeProps) {
|
|
32
|
-
return (
|
|
33
|
-
<div
|
|
34
|
-
{...applyProps(
|
|
35
|
-
elbe,
|
|
36
|
-
["card", colorScheme, colorStyle, colorManner, mode],
|
|
37
|
-
{ padding: `${padding}rem`, margin: `${margin}rem` }
|
|
38
|
-
)}
|
|
39
|
-
onClick={onTap}
|
|
40
|
-
onContextMenu={onLongTap}
|
|
41
|
-
>
|
|
42
|
-
{children}
|
|
43
|
-
</div>
|
|
44
|
-
);
|
|
45
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { X } from "lucide-react";
|
|
2
|
-
import type { ElbeChildren } from "../util/util";
|
|
3
|
-
import { Spaced } from "./util";
|
|
4
|
-
|
|
5
|
-
export function ElbeDialog({
|
|
6
|
-
title,
|
|
7
|
-
open,
|
|
8
|
-
onClose,
|
|
9
|
-
children,
|
|
10
|
-
_style,
|
|
11
|
-
}: {
|
|
12
|
-
_style?: string;
|
|
13
|
-
title: string;
|
|
14
|
-
open: boolean;
|
|
15
|
-
onClose: () => void;
|
|
16
|
-
children: ElbeChildren;
|
|
17
|
-
}) {
|
|
18
|
-
return (
|
|
19
|
-
<dialog
|
|
20
|
-
onClick={(e) => e.stopPropagation()}
|
|
21
|
-
open={open}
|
|
22
|
-
style={"text-align: start" + (_style ?? "")}
|
|
23
|
-
>
|
|
24
|
-
<div
|
|
25
|
-
class=" card plain-opaque padding-none"
|
|
26
|
-
style="max-width: 40rem; min-width: 10rem"
|
|
27
|
-
>
|
|
28
|
-
<div class="row cross-start padded">
|
|
29
|
-
<div class="flex-1 b" style="margin-top: 0.6rem; font-size: 1.2rem">
|
|
30
|
-
{title}
|
|
31
|
-
</div>
|
|
32
|
-
<button
|
|
33
|
-
class="integrated"
|
|
34
|
-
style="width: 3rem"
|
|
35
|
-
onClick={(e) => {
|
|
36
|
-
e.stopPropagation();
|
|
37
|
-
e.preventDefault();
|
|
38
|
-
onClose();
|
|
39
|
-
}}
|
|
40
|
-
>
|
|
41
|
-
<X />
|
|
42
|
-
</button>
|
|
43
|
-
</div>
|
|
44
|
-
<Spaced amount={0.5} />
|
|
45
|
-
<div class="padded" style="max-height: 80vh; overflow: auto">
|
|
46
|
-
{children}
|
|
47
|
-
</div>
|
|
48
|
-
</div>
|
|
49
|
-
</dialog>
|
|
50
|
-
);
|
|
51
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { useSignal } from "@preact/signals";
|
|
2
|
-
import { route } from "preact-router";
|
|
3
|
-
import { ElbeDialog, Icons } from "../..";
|
|
4
|
-
import { ifApiError, type ApiError } from "../../service/s_api";
|
|
5
|
-
|
|
6
|
-
export function ErrorView({
|
|
7
|
-
error,
|
|
8
|
-
retry,
|
|
9
|
-
debug,
|
|
10
|
-
}: {
|
|
11
|
-
error: any;
|
|
12
|
-
retry?: () => any;
|
|
13
|
-
debug?: boolean;
|
|
14
|
-
}) {
|
|
15
|
-
const apiError: ApiError = ifApiError(error) ?? {
|
|
16
|
-
code: 0,
|
|
17
|
-
message: "unknown error",
|
|
18
|
-
data: error,
|
|
19
|
-
};
|
|
20
|
-
return !debug ? (
|
|
21
|
-
<PrettyErrorView apiError={apiError} retry={retry} />
|
|
22
|
-
) : (
|
|
23
|
-
<div class="column padded card inverse cross-stretch">
|
|
24
|
-
<h3 style="margin: 0">ERROR: {apiError.code}</h3>
|
|
25
|
-
<p>{apiError.message}</p>
|
|
26
|
-
<pre>{JSON.stringify(apiError.data, null, 2)}</pre>
|
|
27
|
-
</div>
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function PrettyErrorView({
|
|
32
|
-
apiError,
|
|
33
|
-
retry,
|
|
34
|
-
labels = {
|
|
35
|
-
retry: "retry",
|
|
36
|
-
home: "go home",
|
|
37
|
-
details: "error details",
|
|
38
|
-
},
|
|
39
|
-
}: {
|
|
40
|
-
apiError: ApiError;
|
|
41
|
-
retry?: () => any;
|
|
42
|
-
labels?: { retry?: string; home?: string; details?: string };
|
|
43
|
-
}) {
|
|
44
|
-
const openSig = useSignal(false);
|
|
45
|
-
return (
|
|
46
|
-
<div class="column padded cross-center" style="margin: 1rem 0">
|
|
47
|
-
<Icons.OctagonAlert />
|
|
48
|
-
<h3 style="margin: 0">{apiError.code}</h3>
|
|
49
|
-
<span class="pointer" onClick={() => (openSig.value = true)}>
|
|
50
|
-
{apiError.message}
|
|
51
|
-
</span>
|
|
52
|
-
{retry && (
|
|
53
|
-
<button class="action" onClick={() => retry()}>
|
|
54
|
-
<Icons.RotateCcw /> {labels.retry ?? "retry"}
|
|
55
|
-
</button>
|
|
56
|
-
)}
|
|
57
|
-
{apiError.code === 404 && (
|
|
58
|
-
<button class="action" onClick={() => route("/")}>
|
|
59
|
-
<Icons.House />
|
|
60
|
-
{labels.home ?? "go home"}
|
|
61
|
-
</button>
|
|
62
|
-
)}
|
|
63
|
-
<ElbeDialog
|
|
64
|
-
title={labels.details ?? "error details"}
|
|
65
|
-
open={openSig.value}
|
|
66
|
-
onClose={() => (openSig.value = false)}
|
|
67
|
-
>
|
|
68
|
-
<pre class="card inverse">{JSON.stringify(apiError.data, null, 2)}</pre>
|
|
69
|
-
</ElbeDialog>
|
|
70
|
-
</div>
|
|
71
|
-
);
|
|
72
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { applyProps, type ElbeProps } from "./box";
|
|
2
|
-
|
|
3
|
-
export type FlexProps = {
|
|
4
|
-
children: any;
|
|
5
|
-
gap?: number;
|
|
6
|
-
// shorthand for cross="stretch"
|
|
7
|
-
stretch?: boolean;
|
|
8
|
-
main?:
|
|
9
|
-
| "start"
|
|
10
|
-
| "center"
|
|
11
|
-
| "end"
|
|
12
|
-
| "stretch"
|
|
13
|
-
| "space-between"
|
|
14
|
-
| "space-around"
|
|
15
|
-
| "space-evenly";
|
|
16
|
-
cross?:
|
|
17
|
-
| "start"
|
|
18
|
-
| "center"
|
|
19
|
-
| "end"
|
|
20
|
-
| "stretch"
|
|
21
|
-
| "space-between"
|
|
22
|
-
| "space-around"
|
|
23
|
-
| "space-evenly";
|
|
24
|
-
} & ElbeProps;
|
|
25
|
-
|
|
26
|
-
export function FlexSpace({}) {
|
|
27
|
-
return <div style="flex:1"></div>;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function Column({
|
|
31
|
-
gap = 1,
|
|
32
|
-
main = "start",
|
|
33
|
-
cross = "stretch",
|
|
34
|
-
stretch = false,
|
|
35
|
-
children,
|
|
36
|
-
...p
|
|
37
|
-
}: FlexProps) {
|
|
38
|
-
return _Flex(false, { gap, main, cross, stretch, children }, p);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function Row({
|
|
42
|
-
gap = 1,
|
|
43
|
-
main = "start",
|
|
44
|
-
cross,
|
|
45
|
-
stretch = false,
|
|
46
|
-
children,
|
|
47
|
-
...p
|
|
48
|
-
}: FlexProps) {
|
|
49
|
-
return _Flex(true, { gap, main, cross, stretch, children }, p);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function _Flex(row: boolean, p: FlexProps, elbe: ElbeProps) {
|
|
53
|
-
return (
|
|
54
|
-
<div
|
|
55
|
-
{...applyProps(elbe, row ? "row" : "column", {
|
|
56
|
-
justifyContent: p.main,
|
|
57
|
-
alignItems: p.cross || (p.stretch ? "stretch" : "center"),
|
|
58
|
-
gap: `${p.gap}rem`,
|
|
59
|
-
})}
|
|
60
|
-
>
|
|
61
|
-
{p.children}
|
|
62
|
-
</div>
|
|
63
|
-
);
|
|
64
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import React from "preact/compat";
|
|
2
|
-
import type { ElbeColorManners, ElbeColorStyles } from "../color_theme";
|
|
3
|
-
import type { ElbeChild } from "../util/util";
|
|
4
|
-
import { applyProps, type ElbeProps } from "./box";
|
|
5
|
-
|
|
6
|
-
export type IconChild = ElbeChild | ((_: any) => ElbeChild);
|
|
7
|
-
|
|
8
|
-
export type IconButtonProps = {
|
|
9
|
-
icon?: IconChild;
|
|
10
|
-
colorStyle?: ElbeColorStyles;
|
|
11
|
-
|
|
12
|
-
onTap?: () => void;
|
|
13
|
-
} & ElbeProps;
|
|
14
|
-
|
|
15
|
-
export class IconButton extends React.Component<
|
|
16
|
-
IconButtonProps & { colorManner?: ElbeColorManners }
|
|
17
|
-
> {
|
|
18
|
-
static major = (p: IconButtonProps) => _btn(p, "major");
|
|
19
|
-
static minor = (p: IconButtonProps) => _btn(p, "minor");
|
|
20
|
-
static action = (p: IconButtonProps) => _btn(p, "action");
|
|
21
|
-
static integrated = (p: IconButtonProps) => _btn(p, "integrated");
|
|
22
|
-
|
|
23
|
-
render() {
|
|
24
|
-
return _btn(this.props, this.props.colorManner);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function _btn(
|
|
29
|
-
{ icon, onTap, ...elbe }: IconButtonProps,
|
|
30
|
-
colorManner: ElbeColorManners = "major"
|
|
31
|
-
) {
|
|
32
|
-
return (
|
|
33
|
-
<button
|
|
34
|
-
{...applyProps(
|
|
35
|
-
elbe,
|
|
36
|
-
[
|
|
37
|
-
"row",
|
|
38
|
-
"main-center",
|
|
39
|
-
"gap-half",
|
|
40
|
-
elbe.colorStyle,
|
|
41
|
-
colorManner,
|
|
42
|
-
!onTap && "disabled",
|
|
43
|
-
],
|
|
44
|
-
{
|
|
45
|
-
border: "none",
|
|
46
|
-
borderRadius: "3rem",
|
|
47
|
-
height: "3rem",
|
|
48
|
-
width: "3rem",
|
|
49
|
-
}
|
|
50
|
-
)}
|
|
51
|
-
onClick={() => onTap && onTap()}
|
|
52
|
-
>
|
|
53
|
-
{typeof icon === "function" ? icon({}) : icon}
|
|
54
|
-
</button>
|
|
55
|
-
);
|
|
56
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { applyProps, type ElbeProps } from "../box";
|
|
2
|
-
|
|
3
|
-
export function Checkbox({
|
|
4
|
-
value,
|
|
5
|
-
label,
|
|
6
|
-
onChange,
|
|
7
|
-
...elbe
|
|
8
|
-
}: {
|
|
9
|
-
value: boolean;
|
|
10
|
-
label?: string;
|
|
11
|
-
onChange?: ((checked: boolean) => void) | null;
|
|
12
|
-
} & ElbeProps) {
|
|
13
|
-
return (
|
|
14
|
-
<div
|
|
15
|
-
class={`row ${onChange ? "" : "disabled"}`}
|
|
16
|
-
style={{
|
|
17
|
-
gap: ".75rem",
|
|
18
|
-
filter: onChange ? "" : "grayscale(1)",
|
|
19
|
-
opacity: onChange ? "" : "0.5",
|
|
20
|
-
}}
|
|
21
|
-
>
|
|
22
|
-
<input
|
|
23
|
-
type="checkbox"
|
|
24
|
-
{...applyProps(elbe)}
|
|
25
|
-
disabled={!onChange}
|
|
26
|
-
checked={value}
|
|
27
|
-
onChange={(e) => onChange?.(e.currentTarget.checked)}
|
|
28
|
-
/>
|
|
29
|
-
{label && <div style="margin-top: -.25rem">{label}</div>}
|
|
30
|
-
</div>
|
|
31
|
-
);
|
|
32
|
-
}
|