elbe-ui 0.2.26 → 0.2.34

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.
Files changed (102) hide show
  1. package/dist/bit/bit.js +83 -0
  2. package/dist/bit/ctrl_bit.js +89 -0
  3. package/dist/elbe.css +621 -0
  4. package/dist/elbe.css.map +1 -0
  5. package/dist/index.d.ts +1549 -17
  6. package/dist/index.js +64 -18577
  7. package/dist/service/s_api.js +89 -0
  8. package/dist/ui/components/badge.d.ts +5 -5
  9. package/dist/ui/components/badge.js +53 -0
  10. package/dist/ui/components/base/box.d.ts +2564 -0
  11. package/dist/ui/components/base/box.js +30 -0
  12. package/dist/ui/components/base/card.d.ts +14 -0
  13. package/dist/ui/components/base/card.js +11 -0
  14. package/dist/ui/components/base/padded.js +28 -0
  15. package/dist/ui/components/button/button.d.ts +21 -0
  16. package/dist/ui/components/button/button.js +27 -0
  17. package/dist/ui/components/button/choose_button.d.ts +14 -0
  18. package/dist/ui/components/button/choose_button.js +11 -0
  19. package/dist/ui/components/button/icon_button.d.ts +17 -0
  20. package/dist/ui/components/button/icon_button.js +31 -0
  21. package/dist/ui/components/button/toggle_button.d.ts +10 -0
  22. package/dist/ui/components/button/toggle_button.js +11 -0
  23. package/dist/ui/components/dialog.js +14 -0
  24. package/dist/ui/components/error_view.js +26 -0
  25. package/dist/ui/components/input/checkbox.d.ts +1 -1
  26. package/dist/ui/components/input/checkbox.js +12 -0
  27. package/dist/ui/components/input/input_field.d.ts +1 -1
  28. package/dist/ui/components/input/input_field.js +31 -0
  29. package/dist/ui/components/input/range.d.ts +1 -1
  30. package/dist/ui/components/input/range.js +14 -0
  31. package/dist/ui/components/input/select.d.ts +1 -1
  32. package/dist/ui/components/input/select.js +8 -0
  33. package/dist/ui/components/input/text_area.d.ts +1 -1
  34. package/dist/ui/components/input/text_area.js +13 -0
  35. package/dist/ui/components/{flex.d.ts → layout/flex.d.ts} +1 -1
  36. package/dist/ui/components/layout/flex.js +23 -0
  37. package/dist/ui/components/{scaffold.d.ts → layout/scaffold.d.ts} +7 -3
  38. package/dist/ui/components/layout/scaffold.js +44 -0
  39. package/dist/ui/components/layout/scroll.d.ts +18 -0
  40. package/dist/ui/components/layout/scroll.js +20 -0
  41. package/dist/ui/components/layout/spaced.js +7 -0
  42. package/dist/ui/components/spinner.d.ts +10 -2
  43. package/dist/ui/components/spinner.js +48 -0
  44. package/dist/ui/components/text.d.ts +5 -5
  45. package/dist/ui/components/text.js +42 -0
  46. package/dist/ui/theme/color_theme.d.ts +2 -0
  47. package/dist/ui/theme/color_theme.js +63 -0
  48. package/dist/ui/theme/colors.d.ts +142 -0
  49. package/dist/ui/theme/colors.js +317 -0
  50. package/dist/ui/theme/geometry_theme.d.ts +16 -0
  51. package/dist/ui/theme/geometry_theme.js +38 -0
  52. package/dist/ui/theme/theme.d.ts +28 -0
  53. package/dist/ui/theme/theme.js +49 -0
  54. package/dist/ui/theme/type_theme.d.ts +38 -0
  55. package/dist/ui/theme/type_theme.js +98 -0
  56. package/dist/ui/util/confirm_dialog.js +46 -0
  57. package/dist/ui/util/error_view.js +8 -0
  58. package/dist/ui/util/toast.js +17 -0
  59. package/dist/ui/util/util.d.ts +2 -0
  60. package/dist/ui/util/util.js +39 -0
  61. package/package.json +14 -15
  62. package/dist/ui/color_theme.d.ts +0 -5
  63. package/dist/ui/components/box.d.ts +0 -1027
  64. package/dist/ui/components/button.d.ts +0 -23
  65. package/dist/ui/components/card.d.ts +0 -14
  66. package/dist/ui/components/icon_button.d.ts +0 -19
  67. package/dist/ui/components/toggle_button.d.ts +0 -12
  68. package/elbe.scss +0 -100
  69. package/src/bit/bit.tsx +0 -128
  70. package/src/bit/ctrl_bit.tsx +0 -112
  71. package/src/index.tsx +0 -29
  72. package/src/service/s_api.ts +0 -102
  73. package/src/ui/color_theme.ts +0 -24
  74. package/src/ui/components/badge.tsx +0 -78
  75. package/src/ui/components/box.tsx +0 -49
  76. package/src/ui/components/button.tsx +0 -61
  77. package/src/ui/components/card.tsx +0 -45
  78. package/src/ui/components/dialog.tsx +0 -51
  79. package/src/ui/components/error_view.tsx +0 -72
  80. package/src/ui/components/flex.tsx +0 -64
  81. package/src/ui/components/icon_button.tsx +0 -56
  82. package/src/ui/components/input/checkbox.tsx +0 -32
  83. package/src/ui/components/input/input_field.tsx +0 -57
  84. package/src/ui/components/input/range.tsx +0 -37
  85. package/src/ui/components/input/select.tsx +0 -29
  86. package/src/ui/components/input/text_area.tsx +0 -45
  87. package/src/ui/components/padded.tsx +0 -62
  88. package/src/ui/components/scaffold.tsx +0 -79
  89. package/src/ui/components/spinner.tsx +0 -11
  90. package/src/ui/components/text.tsx +0 -78
  91. package/src/ui/components/toggle_button.tsx +0 -52
  92. package/src/ui/components/util.tsx +0 -3
  93. package/src/ui/util/confirm_dialog.ts +0 -53
  94. package/src/ui/util/error_view.tsx +0 -16
  95. package/src/ui/util/toast.ts +0 -14
  96. package/src/ui/util/util.ts +0 -36
  97. package/style/color_style.scss +0 -149
  98. package/style/components.scss +0 -476
  99. package/style/root.scss +0 -50
  100. package/style/type_style.scss +0 -22
  101. /package/dist/ui/components/{padded.d.ts → base/padded.d.ts} +0 -0
  102. /package/dist/ui/components/{util.d.ts → layout/spaced.d.ts} +0 -0
@@ -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
- }
@@ -1,57 +0,0 @@
1
- import React from "preact/compat";
2
- import { applyProps, type ElbeProps } from "../box";
3
- import { _TextArea } from "./text_area";
4
-
5
- export type InputFieldProps = {
6
- label?: string;
7
- hint: string;
8
- readonly?: boolean;
9
- value: string | number;
10
- onInput?: (value: string) => void;
11
- } & ElbeProps;
12
-
13
- export class Field extends React.Component<
14
- InputFieldProps & {
15
- type?: "text" | "number" | "password" | "date" | "time" | "email";
16
- }
17
- > {
18
- static text = (p: InputFieldProps) => <Field {...p} type="text" />;
19
- static number = (p: InputFieldProps) => <Field {...p} type="number" />;
20
- static password = (p: InputFieldProps) => <Field {...p} type="password" />;
21
- static date = (p: InputFieldProps) => <Field {...p} type="date" />;
22
- static time = (p: InputFieldProps) => <Field {...p} type="time" />;
23
- static email = (p: InputFieldProps) => <Field {...p} type="email" />;
24
-
25
- static multiLine = _TextArea;
26
-
27
- render() {
28
- const { label, hint, readonly, type, value, onInput, ...elbe } = this.props;
29
-
30
- return (
31
- <div
32
- style={{
33
- width: "12rem !important",
34
- display: "flex",
35
- flexDirection: "column",
36
- alignItems: "stretch",
37
- }}
38
- data-tooltip={this.props?.tooltip}
39
- >
40
- <input
41
- type={this.props.type}
42
- {...applyProps(this.props, null, {
43
- width: "100%",
44
- })}
45
- size={5}
46
- label={this.props.label}
47
- placeholder={this.props.hint}
48
- value={this.props.value}
49
- onInput={(e) =>
50
- this.props.onInput && this.props.onInput(e.currentTarget.value)
51
- }
52
- readonly={this.props.readonly}
53
- />
54
- </div>
55
- );
56
- }
57
- }
@@ -1,37 +0,0 @@
1
- import { _ElbeErr } from "../../util/error_view";
2
- import { applyProps, type ElbeProps } from "../box";
3
-
4
- export function Range({
5
- value,
6
- onChange,
7
- min = 0,
8
- max = 100,
9
- step = 1,
10
- ...elbe
11
- }: {
12
- value: number;
13
- min?: number;
14
- step?: number;
15
- max?: number;
16
- onChange?: ((value: number) => void) | null;
17
- } & ElbeProps) {
18
- return min > max ? (
19
- _ElbeErr("Range: max is smaller than min")
20
- ) : (
21
- <input
22
- type="range"
23
- {...applyProps(elbe, null, {
24
- filter: onChange ? "" : "grayscale(1)",
25
- opacity: onChange ? "" : "0.5",
26
- cursor: onChange ? "pointer" : "not-allowed",
27
- width: "100%",
28
- })}
29
- min={min}
30
- max={max}
31
- step={step}
32
- disabled={!onChange}
33
- value={value}
34
- onInput={(e) => onChange?.(Number(e.currentTarget.value))}
35
- />
36
- );
37
- }
@@ -1,29 +0,0 @@
1
- import { applyProps, type ElbeProps } from "../box";
2
-
3
- export function Select({
4
- options,
5
- value,
6
- label,
7
- onChange,
8
- ...elbe
9
- }: {
10
- options: { key: string; label: string }[];
11
- value?: string;
12
- label?: string;
13
- onChange: (value: string) => any;
14
- } & ElbeProps) {
15
- return (
16
- <select
17
- {...applyProps(elbe)}
18
- value={value}
19
- label={label}
20
- onChange={(e) => onChange(e.currentTarget.value)}
21
- >
22
- {options.map(({ key, label }) => (
23
- <option key={key} value={key}>
24
- {label}
25
- </option>
26
- ))}
27
- </select>
28
- );
29
- }
@@ -1,45 +0,0 @@
1
- import { applyProps, type ElbeProps } from "../box";
2
-
3
- export function _TextArea({
4
- label,
5
- hint,
6
- readonly,
7
- rows = 4,
8
- maxLength,
9
- value,
10
- onInput,
11
- ...elbe
12
- }: {
13
- label?: string;
14
- hint: string;
15
- rows?: number;
16
- maxLength?: number;
17
- readonly?: boolean;
18
- value: string | number;
19
- onInput?: (value: string) => void;
20
- } & ElbeProps) {
21
- return (
22
- <div
23
- style={{
24
- width: "12rem !important",
25
- display: "flex",
26
- flexDirection: "column",
27
- alignItems: "stretch",
28
- }}
29
- data-tooltip={elbe.tooltip}
30
- >
31
- <textarea
32
- {...applyProps(elbe, null, { width: "100%" })}
33
- label={label}
34
- size={5}
35
- cols={5}
36
- placeholder={hint}
37
- rows={rows}
38
- maxLength={maxLength}
39
- value={value}
40
- onInput={(e) => onInput && onInput(e.currentTarget.value)}
41
- readonly={readonly}
42
- />
43
- </div>
44
- );
45
- }
@@ -1,62 +0,0 @@
1
- import React from "preact/compat";
2
-
3
- export type PaddedProps = {
4
- children: any;
5
- };
6
-
7
- export class Padded extends React.Component<
8
- PaddedProps & {
9
- top: number;
10
- right: number;
11
- bottom: number;
12
- left: number;
13
- }
14
- > {
15
- constructor(
16
- props: PaddedProps & {
17
- top: number;
18
- right: number;
19
- bottom: number;
20
- left: number;
21
- }
22
- ) {
23
- super(props);
24
- }
25
-
26
- static all = ({ amount = 1, ...p }: PaddedProps & { amount: number }) => (
27
- <Padded
28
- {...{ ...p, top: amount, right: amount, bottom: amount, left: amount }}
29
- />
30
- );
31
-
32
- static symmetric = ({
33
- vertical = 0,
34
- horizontal = 0,
35
- ...p
36
- }: PaddedProps & { vertical: number; horizontal: number }) => (
37
- <Padded
38
- {...{
39
- ...p,
40
- top: vertical,
41
- bottom: vertical,
42
- left: horizontal,
43
- right: horizontal,
44
- }}
45
- />
46
- );
47
-
48
- render() {
49
- return (
50
- <div
51
- style={{
52
- paddingTop: `${this.props.top}rem`,
53
- paddingRight: `${this.props.right}rem`,
54
- paddingBottom: `${this.props.bottom}rem`,
55
- paddingLeft: `${this.props.left}rem`,
56
- }}
57
- >
58
- {this.props.children}
59
- </div>
60
- );
61
- }
62
- }
@@ -1,79 +0,0 @@
1
- import { useEffect, useState } from "preact/compat";
2
- import { Column, IconButton, Icons, Row, Text } from "../..";
3
-
4
- type HeaderParams = {
5
- title?: string;
6
- back: null | "close" | "back";
7
- actions?: any;
8
- };
9
-
10
- /**
11
- * Header is a layout component that provides a header for a page.
12
- * It is used to create a consistent header for pages.
13
- * @param back - The back button type. If null, no back button is shown. If "close", a close button is shown. If "back", a back button is shown.
14
- * @param title - The title of the page.
15
- * @param actions - The actions to show on the right side of the header.
16
- */
17
- export function Header({ back, title, actions }: HeaderParams) {
18
- if (history.length == 0) back = null;
19
- const goBack = () => history.go(-1);
20
-
21
- const [isScrolledTop, setIsScrolled] = useState(false);
22
-
23
- useEffect(() => {
24
- const _handle = () => setIsScrolled(window.scrollY > 0);
25
- window.addEventListener("scroll", _handle);
26
- return () => {
27
- window.removeEventListener("scroll", _handle);
28
- };
29
- }, []);
30
-
31
- return (
32
- <div>
33
- <div style="height: 4.5rem"></div>
34
- <div
35
- class="header"
36
- style={isScrolledTop ? "" : "border-color: transparent"}
37
- >
38
- <div class="flex-1">
39
- {back === "close" ? (
40
- <IconButton.integrated icon={Icons.X} onTap={goBack} />
41
- ) : back === "back" ? (
42
- <IconButton.integrated icon={Icons.ArrowLeft} onTap={goBack} />
43
- ) : null}
44
- </div>
45
- <Text.h4 v={title} />
46
- <Row class="flex-1" gap={0.5} main="end">
47
- {actions}
48
- </Row>
49
- </div>
50
- </div>
51
- );
52
- }
53
-
54
- /**
55
- * Scaffold is a layout component that provides a header and a content area.
56
- * It is used to create a consistent layout for pages.
57
- */
58
- export function Scaffold({
59
- children,
60
- limited = false,
61
- gap = 1,
62
- ...header
63
- }: {
64
- limited?: boolean;
65
- children: any;
66
- gap?: number;
67
- } & HeaderParams) {
68
- return (
69
- <Column cross="stretch" gap={0}>
70
- <Header {...header} />
71
-
72
- <div class={`padded ${limited ? "base-limited" : ""}`}>
73
- <Column cross="stretch" gap={gap ?? 1}>
74
- {children}
75
- </Column>
76
- </div>
77
- </Column>
78
- );
79
- }