elbe-ui 1.0.8 → 2.0.0
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 +150 -562
- package/dist/elbe.css.map +1 -1
- package/dist/index.d.ts +188 -46
- package/dist/index.js +8 -6
- package/dist/ui/app/app.d.ts +12 -0
- package/dist/ui/{components/layout/app_base.js → app/app.js} +24 -31
- package/dist/ui/app/app_ctxt.d.ts +35 -0
- package/dist/ui/app/app_ctxt.js +10 -0
- package/dist/ui/components/badge.d.ts +3 -3
- package/dist/ui/components/badge.js +16 -5
- package/dist/ui/components/banner.d.ts +3 -3
- package/dist/ui/components/banner.js +1 -1
- package/dist/ui/components/base/box.d.ts +82 -35
- package/dist/ui/components/base/box.js +56 -8
- package/dist/ui/components/base/card.d.ts +11 -12
- package/dist/ui/components/base/card.js +36 -19
- package/dist/ui/components/base/padded.d.ts +2 -4
- package/dist/ui/components/base/state_builder.d.ts +9 -0
- package/dist/ui/components/base/state_builder.js +33 -0
- package/dist/ui/components/button/button.d.ts +8 -6
- package/dist/ui/components/button/button.js +27 -16
- package/dist/ui/components/button/choose_button.d.ts +5 -3
- package/dist/ui/components/button/choose_button.js +9 -6
- package/dist/ui/components/button/icon_button.d.ts +6 -5
- package/dist/ui/components/button/icon_button.js +33 -23
- package/dist/ui/components/button/toggle_button.d.ts +5 -5
- package/dist/ui/components/button/toggle_button.js +13 -7
- package/dist/ui/components/dialog/dialog.d.ts +13 -0
- package/dist/ui/components/dialog/dialog.js +69 -0
- package/dist/ui/components/dialog/dialog_ctx.d.ts +34 -0
- package/dist/ui/components/dialog/dialog_ctx.js +44 -0
- package/dist/ui/components/dialog/dialogs/_alert.d.ts +7 -0
- package/dist/ui/components/dialog/dialogs/_alert.js +9 -0
- package/dist/ui/components/dialog/dialogs/_confirm.d.ts +9 -0
- package/dist/ui/components/dialog/dialogs/_confirm.js +21 -0
- package/dist/ui/components/error_view.js +3 -3
- package/dist/ui/components/footer.js +7 -7
- package/dist/ui/components/input/_labeled_input.d.ts +21 -0
- package/dist/ui/components/input/_labeled_input.js +21 -0
- package/dist/ui/components/input/checkbox.d.ts +4 -2
- package/dist/ui/components/input/checkbox.js +16 -8
- package/dist/ui/components/input/range.d.ts +5 -3
- package/dist/ui/components/input/range.js +20 -13
- package/dist/ui/components/input/select.d.ts +4 -3
- package/dist/ui/components/input/select.js +13 -40
- package/dist/ui/components/input/switch.d.ts +4 -2
- package/dist/ui/components/input/switch.js +30 -40
- package/dist/ui/components/input/text/input_field.d.ts +14 -19
- package/dist/ui/components/input/text/input_field.js +63 -61
- package/dist/ui/components/input/text/single_line.d.ts +11 -9
- package/dist/ui/components/input/text/single_line.js +15 -21
- package/dist/ui/components/layout/flex.d.ts +7 -3
- package/dist/ui/components/layout/flex.js +15 -12
- package/dist/ui/components/layout/header.d.ts +2 -2
- package/dist/ui/components/layout/header.js +25 -18
- package/dist/ui/components/layout/menu.js +74 -49
- package/dist/ui/components/layout/scroll.d.ts +1 -0
- package/dist/ui/components/layout/scroll.js +3 -1
- package/dist/ui/components/layout/toolbar.js +9 -6
- package/dist/ui/components/link.d.ts +4 -2
- package/dist/ui/components/link.js +24 -11
- package/dist/ui/components/progress_bar.d.ts +2 -2
- package/dist/ui/components/progress_bar.js +12 -9
- package/dist/ui/components/spinner.d.ts +2 -7
- package/dist/ui/components/spinner.js +15 -16
- package/dist/ui/components/text.d.ts +5 -6
- package/dist/ui/components/text.js +34 -21
- package/dist/ui/components/tooltip.d.ts +5 -0
- package/dist/ui/components/tooltip.js +48 -0
- package/dist/ui/theme/subthemes/_theme_geometry.d.ts +9 -0
- package/dist/ui/theme/subthemes/_theme_geometry.js +22 -0
- package/dist/ui/theme/subthemes/_theme_menu.d.ts +9 -0
- package/dist/ui/theme/subthemes/_theme_menu.js +12 -0
- package/dist/ui/theme/subthemes/_theme_motion.d.ts +9 -0
- package/dist/ui/theme/subthemes/_theme_motion.js +13 -0
- package/dist/ui/theme/subthemes/_theme_toast.d.ts +8 -0
- package/dist/ui/theme/subthemes/_theme_toast.js +11 -0
- package/dist/ui/theme/subthemes/_theme_type.d.ts +49 -0
- package/dist/ui/theme/subthemes/_theme_type.js +73 -0
- package/dist/ui/theme/subthemes/color/_seed.d.ts +2 -0
- package/dist/ui/theme/subthemes/color/_seed.js +105 -0
- package/dist/ui/theme/subthemes/color/_theme_color.d.ts +865 -0
- package/dist/ui/theme/subthemes/color/_theme_color.js +83 -0
- package/dist/ui/theme/subthemes/color/colors/_color_contrast.d.ts +284 -0
- package/dist/ui/theme/subthemes/color/colors/_color_contrast.js +17 -0
- package/dist/ui/theme/subthemes/color/colors/_color_kind.d.ts +242 -0
- package/dist/ui/theme/subthemes/color/colors/_color_kind.js +45 -0
- package/dist/ui/theme/subthemes/color/colors/_color_layer.d.ts +226 -0
- package/dist/ui/theme/subthemes/color/colors/_color_layer.js +95 -0
- package/dist/ui/theme/subthemes/color/colors/_color_manner.d.ts +280 -0
- package/dist/ui/theme/subthemes/color/colors/_color_manner.js +17 -0
- package/dist/ui/theme/subthemes/color/colors/_color_mode.d.ts +269 -0
- package/dist/ui/theme/subthemes/color/colors/_color_mode.js +16 -0
- package/dist/ui/theme/subthemes/color/colors/_color_rgba.d.ts +166 -0
- package/dist/ui/theme/subthemes/color/colors/_color_rgba.js +86 -0
- package/dist/ui/theme/subthemes/color/colors/_color_scheme.d.ts +256 -0
- package/dist/ui/theme/subthemes/color/colors/_color_scheme.js +17 -0
- package/dist/ui/theme/subthemes/color/colors/_color_state.d.ts +252 -0
- package/dist/ui/theme/subthemes/color/colors/_color_state.js +49 -0
- package/dist/ui/theme/subthemes/color/colors/_colordef.d.ts +22 -0
- package/dist/ui/theme/subthemes/color/colors/_colordef.js +34 -0
- package/dist/ui/theme/subthemes/color/colors/colors.d.ts +511 -0
- package/dist/ui/theme/subthemes/color/colors/colors.js +24 -0
- package/dist/ui/theme/theme.d.ts +984 -30
- package/dist/ui/theme/theme.js +14 -82
- package/dist/ui/theme/theme_context.d.ts +15 -17
- package/dist/ui/theme/theme_context.js +79 -18
- package/dist/ui/util/_util.d.ts +1 -0
- package/dist/ui/util/_util.js +3 -0
- package/dist/ui/util/error_view.js +3 -3
- package/dist/ui/util/merge_deep.d.ts +1 -0
- package/dist/ui/util/merge_deep.js +18 -0
- package/dist/ui/util/root.d.ts +17 -0
- package/dist/ui/util/root.js +23 -0
- package/dist/ui/util/toast/_toast.d.ts +5 -0
- package/dist/ui/util/toast/_toast.js +7 -0
- package/dist/ui/util/toast/toast_ctx.d.ts +28 -0
- package/dist/ui/util/toast/toast_ctx.js +62 -0
- package/dist/ui/util/toast/toast_legacy.d.ts +5 -0
- package/dist/ui/util/{toast.js → toast/toast_legacy.js} +4 -4
- package/dist/ui/util/util.d.ts +8 -0
- package/dist/ui/util/util.js +54 -1
- package/package.json +10 -8
- package/dist/ui/components/dialog.d.ts +0 -10
- package/dist/ui/components/dialog.js +0 -35
- package/dist/ui/components/layout/app_base.d.ts +0 -15
- package/dist/ui/components/layout/ctx_app_base.d.ts +0 -19
- package/dist/ui/components/layout/ctx_app_base.js +0 -12
- package/dist/ui/theme/color_theme.d.ts +0 -2
- package/dist/ui/theme/color_theme.js +0 -92
- package/dist/ui/theme/colors.d.ts +0 -133
- package/dist/ui/theme/colors.js +0 -309
- package/dist/ui/theme/geometry_theme.d.ts +0 -16
- package/dist/ui/theme/geometry_theme.js +0 -28
- package/dist/ui/theme/seed.d.ts +0 -53
- package/dist/ui/theme/seed.js +0 -4
- package/dist/ui/theme/type_theme.d.ts +0 -38
- package/dist/ui/theme/type_theme.js +0 -73
- package/dist/ui/util/confirm_dialog.d.ts +0 -10
- package/dist/ui/util/confirm_dialog.js +0 -46
- package/dist/ui/util/toast.d.ts +0 -5
package/dist/ui/theme/theme.js
CHANGED
|
@@ -1,83 +1,15 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect } from "react";
|
|
3
1
|
import "../../elbe.css";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
asCss() {
|
|
20
|
-
const inner = [this.color, this.type, this.geometry]
|
|
21
|
-
.map((s) => s.asCss())
|
|
22
|
-
.join("\n");
|
|
23
|
-
return `.elbe {${inner}}`;
|
|
24
|
-
}
|
|
25
|
-
static fromSeed(seed) {
|
|
26
|
-
return new ElbeThemeData(ColorTheme.generate(seed.color), TypeTheme.generate(seed.type), GeometryTheme.generate(seed.geometry));
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
export function ElbeTheme(p) {
|
|
30
|
-
var _a;
|
|
31
|
-
const theme = "theme" in p ? p.theme : ElbeThemeData.fromSeed((_a = p.seed) !== null && _a !== void 0 ? _a : {});
|
|
32
|
-
const config = makeThemeConfig(p);
|
|
33
|
-
_addTooltip();
|
|
34
|
-
return (_jsxs("div", { ["type-label"]: "elbe-theme", style: {
|
|
35
|
-
width: "100%",
|
|
36
|
-
}, className: `elbe ${config.dark ? "dark" : ""} ${config.highVis ? "highvis" : ""} ${config.reducedMotion ? "reduced_motion" : ""}`, children: [p.todoOverlay && _jsx(ToDo.Overlay, {}), _jsx("style", { children: theme.asCss() }), _jsx("style", { children: _configCss(theme, config) }), _jsx(ThemeConfigContext.Provider, { value: config, children: _jsx(ThemeContext.Provider, { value: theme, children: p.children }) })] }));
|
|
37
|
-
}
|
|
38
|
-
function _addTooltip() {
|
|
39
|
-
return useEffect(() => {
|
|
40
|
-
const _gap = 8;
|
|
41
|
-
const onHover = (e) => {
|
|
42
|
-
var _a;
|
|
43
|
-
const target = (_a = e.target) === null || _a === void 0 ? void 0 : _a.closest("[data-tooltip]");
|
|
44
|
-
if (!target)
|
|
45
|
-
return;
|
|
46
|
-
// remove existing tooltip
|
|
47
|
-
const existingTooltip = document.querySelector("._elbe_tooltip");
|
|
48
|
-
if (existingTooltip)
|
|
49
|
-
existingTooltip.remove();
|
|
50
|
-
// create new tooltip
|
|
51
|
-
let tooltip = document.createElement("div");
|
|
52
|
-
tooltip.className = "_elbe_tooltip";
|
|
53
|
-
tooltip.textContent = target.getAttribute("data-tooltip");
|
|
54
|
-
document.body.appendChild(tooltip);
|
|
55
|
-
const rect = target.getBoundingClientRect();
|
|
56
|
-
let top = rect.top - tooltip.offsetHeight - _gap;
|
|
57
|
-
let left = rect.left + rect.width / 2 - tooltip.offsetWidth / 2;
|
|
58
|
-
// Adjust if out of viewport
|
|
59
|
-
if (top < 0)
|
|
60
|
-
top = rect.bottom + _gap;
|
|
61
|
-
if (left < 0)
|
|
62
|
-
left = _gap;
|
|
63
|
-
if (left + tooltip.offsetWidth > window.innerWidth)
|
|
64
|
-
left = window.innerWidth - tooltip.offsetWidth - _gap;
|
|
65
|
-
tooltip.style.top = `${top}px`;
|
|
66
|
-
tooltip.style.left = `${left}px`;
|
|
67
|
-
// remove tooltip when target is removed or mouse leaves
|
|
68
|
-
const observer = new MutationObserver(() => {
|
|
69
|
-
if (!document.body.contains(target) ||
|
|
70
|
-
target === null) {
|
|
71
|
-
tooltip.remove();
|
|
72
|
-
observer.disconnect();
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
76
|
-
target.addEventListener("mouseleave", () => tooltip.remove(), {
|
|
77
|
-
once: true,
|
|
78
|
-
});
|
|
79
|
-
};
|
|
80
|
-
document.addEventListener("mouseover", onHover);
|
|
81
|
-
return () => document.removeEventListener("mouseover", onHover);
|
|
82
|
-
});
|
|
83
|
-
}
|
|
2
|
+
import { geometryThemeData } from "./subthemes/_theme_geometry";
|
|
3
|
+
import { menuThemeData } from "./subthemes/_theme_menu";
|
|
4
|
+
import { motionThemeData } from "./subthemes/_theme_motion";
|
|
5
|
+
import { toastThemeData } from "./subthemes/_theme_toast";
|
|
6
|
+
import { typeThemeData } from "./subthemes/_theme_type";
|
|
7
|
+
import { colorThemeData } from "./subthemes/color/_theme_color";
|
|
8
|
+
export const elbeCoreThemes = {
|
|
9
|
+
motion: motionThemeData,
|
|
10
|
+
geometry: geometryThemeData,
|
|
11
|
+
type: typeThemeData,
|
|
12
|
+
color: colorThemeData,
|
|
13
|
+
toast: toastThemeData,
|
|
14
|
+
menu: menuThemeData,
|
|
15
|
+
};
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import { ElbeChildren } from "../..";
|
|
2
|
+
import { ElbeThemeConfig, ElbeThemeContextData, ElbeThemeData, ElbeThemeSeed } from "./theme";
|
|
3
|
+
export declare function makeThemeContext<T extends ElbeThemeData = {}>(p: {
|
|
4
|
+
definitions?: T;
|
|
5
|
+
config?: Partial<ElbeThemeConfig<T>>;
|
|
6
|
+
seed?: Partial<ElbeThemeSeed<T>>;
|
|
7
|
+
}): {
|
|
8
|
+
useTheme: () => ElbeThemeContextData<T>;
|
|
9
|
+
WithTheme: (p: {
|
|
10
|
+
children: ElbeChildren;
|
|
11
|
+
theme?: ElbeThemeContextData<T>;
|
|
12
|
+
seed?: Partial<ElbeThemeSeed<T>>;
|
|
13
|
+
themeConfig?: ElbeThemeConfig<T> | null;
|
|
14
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
13
15
|
};
|
|
14
|
-
export
|
|
15
|
-
export declare function _configCss(t: ElbeThemeData, c: ElbeThemeConfig): string;
|
|
16
|
-
export declare const ThemeContext: import("react").Context<ElbeThemeData>;
|
|
17
|
-
export declare function useThemeConfig(): ElbeThemeConfig;
|
|
18
|
-
export declare function useTheme(): ElbeThemeData;
|
|
16
|
+
export type ElbeThemeContext = ReturnType<typeof makeThemeContext>;
|
|
@@ -1,25 +1,86 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useMemo } from "react";
|
|
3
|
+
import { deepMerge } from "../util/merge_deep";
|
|
4
|
+
import { dictMap, throwError, tryOrNull } from "../util/util";
|
|
5
|
+
import { elbeCoreThemes, } from "./theme";
|
|
6
|
+
//const DEMO_TypeData = elbeCoreThemes.type.fromSeed(elbeCoreThemes.type.seed);
|
|
7
|
+
//const DEMO_ColorData = elbeCoreThemes.color.fromSeed(elbeCoreThemes.color.seed);
|
|
8
|
+
//const DEMO_ColorComp = elbeCoreThemes.color.compute(DEMO_ColorData);
|
|
9
|
+
export function makeThemeContext(p) {
|
|
4
10
|
var _a, _b, _c, _d;
|
|
11
|
+
const allDefinitions = Object.assign(Object.assign({}, elbeCoreThemes), ((_a = p.definitions) !== null && _a !== void 0 ? _a : {}));
|
|
12
|
+
const config = _configFromSeed(Object.assign(Object.assign({}, elbeCoreThemes), ((_b = p.definitions) !== null && _b !== void 0 ? _b : {})), (_c = p.config) !== null && _c !== void 0 ? _c : {}, (_d = p.seed) !== null && _d !== void 0 ? _d : {});
|
|
13
|
+
const computedContext = _computeContext(allDefinitions, config);
|
|
14
|
+
const ElbeThemeContext = createContext(computedContext);
|
|
15
|
+
function useMaybeTheme() {
|
|
16
|
+
return tryOrNull(() => useContext(ElbeThemeContext));
|
|
17
|
+
}
|
|
18
|
+
function useTheme() {
|
|
19
|
+
var _a;
|
|
20
|
+
return (_a = useMaybeTheme()) !== null && _a !== void 0 ? _a : throwError("No theme data");
|
|
21
|
+
}
|
|
22
|
+
function WithTheme(p) {
|
|
23
|
+
const theme = useTheme();
|
|
24
|
+
const newTheme = useMemo(() => {
|
|
25
|
+
var _a, _b;
|
|
26
|
+
if (p.seed) {
|
|
27
|
+
const newConfig = _configFromSeed(allDefinitions, {}, //p.themeConfig ?? theme.themeConfig,
|
|
28
|
+
p.seed);
|
|
29
|
+
return _computeContext(allDefinitions, newConfig);
|
|
30
|
+
}
|
|
31
|
+
return ((_a = p.theme) !== null && _a !== void 0 ? _a : _computeContext(allDefinitions, (_b = p.themeConfig) !== null && _b !== void 0 ? _b : theme.themeConfig));
|
|
32
|
+
}, [allDefinitions, p.theme, p.themeConfig, theme.themeConfig, p.seed]);
|
|
33
|
+
const css = useMemo(() => {
|
|
34
|
+
var _a, _b, _c;
|
|
35
|
+
let css = {};
|
|
36
|
+
for (let key in newTheme.themeDefinitions) {
|
|
37
|
+
const def = newTheme.themeDefinitions[key];
|
|
38
|
+
const subCss = def.asCss((_a = newTheme.theme[key]) !== null && _a !== void 0 ? _a : {});
|
|
39
|
+
const subCssContext = _toCssVars((_b = def.asCssContext) === null || _b === void 0 ? void 0 : _b.call(def, (_c = newTheme.theme[key]) !== null && _c !== void 0 ? _c : {}), key);
|
|
40
|
+
css = Object.assign(Object.assign(Object.assign({}, css), subCss), subCssContext);
|
|
41
|
+
}
|
|
42
|
+
return css;
|
|
43
|
+
}, [newTheme]);
|
|
44
|
+
return (_jsx(ElbeThemeContext.Provider, { value: newTheme, children: _jsx("div", { style: Object.assign({ display: "contents" }, css), children: p.children }) }));
|
|
45
|
+
}
|
|
5
46
|
return {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
reducedMotion: (_c = p.reducedMotion) !== null && _c !== void 0 ? _c : false,
|
|
9
|
-
scale: (_d = p.scale) !== null && _d !== void 0 ? _d : 1,
|
|
47
|
+
useTheme,
|
|
48
|
+
WithTheme,
|
|
10
49
|
};
|
|
11
50
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
51
|
+
function _configFromSeed(defs, config, seed) {
|
|
52
|
+
var _a, _b;
|
|
53
|
+
const conf = {};
|
|
54
|
+
for (let key in defs) {
|
|
55
|
+
if (key === "with")
|
|
56
|
+
continue;
|
|
57
|
+
//if (!(key in seed)) continue;
|
|
58
|
+
const fullSeed = deepMerge(defs[key].seed, (_a = seed === null || seed === void 0 ? void 0 : seed[key]) !== null && _a !== void 0 ? _a : {});
|
|
59
|
+
conf[key] = Object.assign(Object.assign({}, defs[key].fromSeed(fullSeed)), ((_b = config === null || config === void 0 ? void 0 : config[key]) !== null && _b !== void 0 ? _b : {}));
|
|
60
|
+
}
|
|
61
|
+
return conf;
|
|
18
62
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
63
|
+
function _with(definitions, config, worker) {
|
|
64
|
+
const partConf = worker(Object.assign({}, config));
|
|
65
|
+
const newConf = Object.assign(Object.assign({}, config), partConf);
|
|
66
|
+
return _computeContext(definitions, newConf);
|
|
22
67
|
}
|
|
23
|
-
|
|
24
|
-
return
|
|
68
|
+
function _computeContext(definitions, config) {
|
|
69
|
+
return {
|
|
70
|
+
themeDefinitions: definitions,
|
|
71
|
+
themeConfig: config,
|
|
72
|
+
theme: dictMap(config, (v, k) => definitions[k].compute(v)),
|
|
73
|
+
useWith: (worker, dependencies) => useMemo(() => {
|
|
74
|
+
return _with(definitions, config, worker);
|
|
75
|
+
}, [...dependencies, config]),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function _toCssVars(p, prefix) {
|
|
79
|
+
if (!p)
|
|
80
|
+
return {};
|
|
81
|
+
let cssVars = {};
|
|
82
|
+
for (let key in p) {
|
|
83
|
+
cssVars = Object.assign(Object.assign({}, cssVars), { [`--elbe-context-${prefix}-${key}`]: p[key] });
|
|
84
|
+
}
|
|
85
|
+
return cssVars;
|
|
25
86
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function elevatedShadow(dark: boolean): string;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Icons } from "../..";
|
|
2
|
+
import { Column, Icons, Row } from "../..";
|
|
3
3
|
export function _ElbeErr(msg) {
|
|
4
|
-
return (_jsxs(
|
|
4
|
+
return (_jsxs(Row, { style: {
|
|
5
5
|
background: "#ee0044",
|
|
6
6
|
color: "white",
|
|
7
7
|
borderRadius: "4px",
|
|
8
8
|
textAlign: "left",
|
|
9
9
|
padding: ".5rem",
|
|
10
|
-
}, children: [_jsx(Icons.CircleX, {}), _jsxs(
|
|
10
|
+
}, children: [_jsx(Icons.CircleX, {}), _jsxs(Column, { gap: 0, children: [_jsx("b", { className: "text-s", children: "elbe error" }), _jsx("span", { style: { marginTop: "-.125rem" }, children: msg })] })] }));
|
|
11
11
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function deepMerge<T extends Dict<any>>(original: T, toMerge: Partial<T>, depth?: number): T;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function deepMerge(original, toMerge, depth = 5000) {
|
|
2
|
+
const output = {};
|
|
3
|
+
const keys = Object.keys(toMerge !== null && toMerge !== void 0 ? toMerge : {}).concat(Object.keys(original !== null && original !== void 0 ? original : {}));
|
|
4
|
+
function isPlainObject(v) {
|
|
5
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
6
|
+
}
|
|
7
|
+
for (const key of new Set(keys)) {
|
|
8
|
+
const vOriginal = original === null || original === void 0 ? void 0 : original[key];
|
|
9
|
+
const vToMerge = toMerge === null || toMerge === void 0 ? void 0 : toMerge[key];
|
|
10
|
+
if (isPlainObject(vOriginal) && isPlainObject(vToMerge) && depth > 0) {
|
|
11
|
+
output[key] = deepMerge(vOriginal, vToMerge, depth - 1);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
output[key] = vToMerge !== undefined ? vToMerge : vOriginal;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return output;
|
|
18
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ElbeChild } from "./types";
|
|
2
|
+
export declare const elbeDomElements: {
|
|
3
|
+
readonly elbe_app: {
|
|
4
|
+
readonly zindex: 0;
|
|
5
|
+
};
|
|
6
|
+
readonly elbe_dialog: {
|
|
7
|
+
readonly zindex: 1000;
|
|
8
|
+
};
|
|
9
|
+
readonly elbe_tooltip: {
|
|
10
|
+
readonly zindex: 2000;
|
|
11
|
+
};
|
|
12
|
+
readonly elbe_toast: {
|
|
13
|
+
readonly zindex: 3000;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export declare function getRootElement(id: keyof typeof elbeDomElements): HTMLElement;
|
|
17
|
+
export declare function renderElbe(children: ElbeChild): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createRoot } from "react-dom/client";
|
|
2
|
+
export const elbeDomElements = {
|
|
3
|
+
elbe_app: { zindex: 0 },
|
|
4
|
+
elbe_dialog: { zindex: 1000 },
|
|
5
|
+
elbe_tooltip: { zindex: 2000 },
|
|
6
|
+
elbe_toast: { zindex: 3000 },
|
|
7
|
+
};
|
|
8
|
+
export function getRootElement(id) {
|
|
9
|
+
const existing = document.getElementById(id);
|
|
10
|
+
if (existing)
|
|
11
|
+
return existing;
|
|
12
|
+
console.info(`ELBE: root element '${id}' not found. Creating...`);
|
|
13
|
+
const el = document.createElement("div");
|
|
14
|
+
el.id = id;
|
|
15
|
+
el.style.zIndex = `${elbeDomElements[id].zindex}`;
|
|
16
|
+
el.setAttribute("note", "auto-created by elbe-ui");
|
|
17
|
+
document.body.appendChild(el);
|
|
18
|
+
return el;
|
|
19
|
+
}
|
|
20
|
+
export function renderElbe(children) {
|
|
21
|
+
const rootEl = getRootElement("elbe_app");
|
|
22
|
+
createRoot(rootEl).render(children);
|
|
23
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Icons, Row, Text } from "../../..";
|
|
3
|
+
import { Card } from "../../components/base/card";
|
|
4
|
+
import { Icon, IconButton } from "../../components/button/icon_button";
|
|
5
|
+
export function _Toast(p) {
|
|
6
|
+
return (_jsx(Card, { elevated: true, padding: 0, scheme: "inverse", manner: p.model.kind ? "minor" : "plain", kind: p.model.kind, className: "toast-animated", style: { pointerEvents: "auto" }, children: _jsxs(Row, { cross: "center", gap: 0.5, style: { minHeight: "3.5rem", paddingLeft: "1rem" }, children: [p.model.icon && _jsx(Icon, { icon: p.model.icon }), _jsx(Text, { flex: true, bold: true, v: p.model.message, style: { padding: ".5rem 0" } }), p.onClose && (_jsx(IconButton, { manner: p.model.kind ? "minor" : "plain", transparent: true, ariaLabel: "dismiss the toast", icon: Icons.X, onTap: () => p.onClose && p.onClose() }))] }) }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { IconChild } from "../../components/button/icon_button";
|
|
2
|
+
import { ColorSelection } from "../../theme/subthemes/color/colors/colors";
|
|
3
|
+
import { ElbeChildren } from "../types";
|
|
4
|
+
/**
|
|
5
|
+
* Options for showing a toast notification.
|
|
6
|
+
* If you set `duration` to null, the toast will not auto-dismiss.
|
|
7
|
+
* You have to set `dismissible` to true to allow manual dismissal in that case.
|
|
8
|
+
*/
|
|
9
|
+
export type ToastOptions = {
|
|
10
|
+
icon?: IconChild;
|
|
11
|
+
kind?: ColorSelection.KindsAlert;
|
|
12
|
+
duration?: number | null;
|
|
13
|
+
dismissible?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type ToastModel = ToastOptions & {
|
|
16
|
+
message: string;
|
|
17
|
+
};
|
|
18
|
+
export type ToastCtrl = {
|
|
19
|
+
showToast: (message: string, options?: ToastOptions) => void;
|
|
20
|
+
};
|
|
21
|
+
export declare function useToast(): ToastCtrl;
|
|
22
|
+
/**
|
|
23
|
+
* must be inside of a theme provider (such as `WithTheme`)
|
|
24
|
+
* @returns a context provider for toast messages
|
|
25
|
+
*/
|
|
26
|
+
export declare function ToastProvider(p: {
|
|
27
|
+
children: ElbeChildren;
|
|
28
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// ToastContext.js
|
|
3
|
+
import { createContext, useContext, useMemo, useState } from "react";
|
|
4
|
+
import ReactDOM from "react-dom";
|
|
5
|
+
import { useApp } from "../../app/app_ctxt";
|
|
6
|
+
import { getRootElement } from "../root";
|
|
7
|
+
import { _Toast } from "./_toast";
|
|
8
|
+
const ToastContext = createContext({
|
|
9
|
+
showToast: () => console.error("toast context was not initialized"),
|
|
10
|
+
});
|
|
11
|
+
export function useToast() {
|
|
12
|
+
return useContext(ToastContext);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* must be inside of a theme provider (such as `WithTheme`)
|
|
16
|
+
* @returns a context provider for toast messages
|
|
17
|
+
*/
|
|
18
|
+
export function ToastProvider(p) {
|
|
19
|
+
const { _appThemeContext } = useApp();
|
|
20
|
+
const { theme } = _appThemeContext.useTheme();
|
|
21
|
+
const rootDOM = useMemo(() => getRootElement("elbe_toast"), []);
|
|
22
|
+
const [toasts, setToasts] = useState([]);
|
|
23
|
+
const [vert, hori] = useMemo(() => {
|
|
24
|
+
var _a;
|
|
25
|
+
const alignment = (_a = theme.toast.alignment) !== null && _a !== void 0 ? _a : "bottom_center";
|
|
26
|
+
return alignment.split("_");
|
|
27
|
+
}, [theme.toast.alignment]);
|
|
28
|
+
function addToast(toast) {
|
|
29
|
+
var _a, _b;
|
|
30
|
+
const id = Date.now() + "";
|
|
31
|
+
setToasts([...toasts, Object.assign(Object.assign({}, toast), { id })]);
|
|
32
|
+
if (toast.duration === null && toast.dismissible === true)
|
|
33
|
+
return;
|
|
34
|
+
setTimeout(() => removeToast(id), (_b = (_a = toast.duration) !== null && _a !== void 0 ? _a : theme.toast.duration) !== null && _b !== void 0 ? _b : 3000);
|
|
35
|
+
}
|
|
36
|
+
function removeToast(id) {
|
|
37
|
+
setToasts((t) => t.filter((toast) => toast.id !== id));
|
|
38
|
+
}
|
|
39
|
+
return (_jsxs(ToastContext.Provider, { value: {
|
|
40
|
+
showToast: (message, options) => addToast(Object.assign(Object.assign({}, (options !== null && options !== void 0 ? options : {})), { message })),
|
|
41
|
+
}, children: [p.children, ReactDOM.createPortal(_jsx("div", { className: "elbe", style: {
|
|
42
|
+
background: "transparent",
|
|
43
|
+
position: "fixed",
|
|
44
|
+
zIndex: 2001,
|
|
45
|
+
top: 0,
|
|
46
|
+
left: 0,
|
|
47
|
+
bottom: 0,
|
|
48
|
+
right: 0,
|
|
49
|
+
display: "flex",
|
|
50
|
+
flexDirection: "column",
|
|
51
|
+
justifyContent: vert === "top" ? "start" : "end",
|
|
52
|
+
alignItems: hori,
|
|
53
|
+
pointerEvents: "none",
|
|
54
|
+
}, children: _jsx("div", { style: {
|
|
55
|
+
display: "flex",
|
|
56
|
+
gap: ".5rem",
|
|
57
|
+
flexDirection: vert === "top" ? "column-reverse" : "column",
|
|
58
|
+
alignItems: "stretch",
|
|
59
|
+
width: "40rem",
|
|
60
|
+
margin: "1rem 2rem",
|
|
61
|
+
}, children: toasts.map((toast) => (_jsx(_Toast, { model: toast, onClose: toast.dismissible ? () => removeToast(toast.id) : undefined }, toast.id))) }) }), rootDOM)] }));
|
|
62
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @param message the message to
|
|
2
|
+
* @deprecated use the new `const {showToast} = useToast()` hook based model instead
|
|
3
|
+
* @param message the message you want to display
|
|
4
4
|
*/
|
|
5
5
|
export function showToast(message) {
|
|
6
6
|
// find the 'elbe' element
|
|
7
|
-
const elbe = document.
|
|
7
|
+
const elbe = document.getElementById("elbe-app");
|
|
8
8
|
if (!elbe) {
|
|
9
|
-
console.warn("could not show toast, no base element with '
|
|
9
|
+
console.warn("could not show legacy toast, no base element with '#elbe-app' found");
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
const toast = document.createElement("div");
|
package/dist/ui/util/util.d.ts
CHANGED
|
@@ -37,3 +37,11 @@ export type LayoutModeInfo = {
|
|
|
37
37
|
*/
|
|
38
38
|
export declare function useLayoutMode(): LayoutModeInfo;
|
|
39
39
|
export declare function useSiteScroll(): number;
|
|
40
|
+
export declare function throwError(message: string): never;
|
|
41
|
+
export declare function tryOrNull<T>(w: () => T | null): T | null;
|
|
42
|
+
export declare function omit<T extends Dict<any>>(obj: T, ...keys: string[]): T;
|
|
43
|
+
export declare function dictMap<T extends Dict<any>, U extends Dict<any>>(obj: T, f: (v: T[keyof T], k: keyof T) => U[keyof U] | undefined): U;
|
|
44
|
+
export declare function dictWithoutUndefined<T extends Dict<any>>(obj?: T): {
|
|
45
|
+
[key in keyof T]: Exclude<T[key], undefined>;
|
|
46
|
+
};
|
|
47
|
+
export declare function hash(v: any): string;
|
package/dist/ui/util/util.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
|
-
import { showToast } from "
|
|
2
|
+
import { showToast } from "./toast/toast_legacy";
|
|
3
3
|
export function clamp(value, min, max) {
|
|
4
4
|
return Math.min(Math.max(value, min), max);
|
|
5
5
|
}
|
|
@@ -88,3 +88,56 @@ export function useSiteScroll() {
|
|
|
88
88
|
}, []);
|
|
89
89
|
return scroll;
|
|
90
90
|
}
|
|
91
|
+
export function throwError(message) {
|
|
92
|
+
throw new Error(message);
|
|
93
|
+
}
|
|
94
|
+
export function tryOrNull(w) {
|
|
95
|
+
try {
|
|
96
|
+
return w();
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
console.warn(e);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export function omit(obj, ...keys) {
|
|
104
|
+
const res = Object.assign({}, obj);
|
|
105
|
+
for (const key of keys) {
|
|
106
|
+
if (key in obj)
|
|
107
|
+
delete res[key];
|
|
108
|
+
}
|
|
109
|
+
return res;
|
|
110
|
+
}
|
|
111
|
+
export function dictMap(obj, f) {
|
|
112
|
+
const res = {};
|
|
113
|
+
for (const key in obj) {
|
|
114
|
+
const v = f(obj[key], key);
|
|
115
|
+
if (v !== undefined)
|
|
116
|
+
res[key] = v;
|
|
117
|
+
}
|
|
118
|
+
return res;
|
|
119
|
+
}
|
|
120
|
+
export function dictWithoutUndefined(obj) {
|
|
121
|
+
if (!obj)
|
|
122
|
+
return {};
|
|
123
|
+
const res = {};
|
|
124
|
+
for (const key in obj) {
|
|
125
|
+
const v = obj[key];
|
|
126
|
+
if (v !== undefined)
|
|
127
|
+
res[key] = v;
|
|
128
|
+
}
|
|
129
|
+
return res;
|
|
130
|
+
}
|
|
131
|
+
/// A simple hash function to create a hash from any value
|
|
132
|
+
export function hash(v) {
|
|
133
|
+
const s = JSON.stringify(v);
|
|
134
|
+
let hash = 0, i, chr;
|
|
135
|
+
if (s.length === 0)
|
|
136
|
+
return hash.toString();
|
|
137
|
+
for (i = 0; i < s.length; i++) {
|
|
138
|
+
chr = s.charCodeAt(i);
|
|
139
|
+
hash = (hash << 5) - hash + chr;
|
|
140
|
+
hash |= 0; // Convert to 32bit integer
|
|
141
|
+
}
|
|
142
|
+
return hash.toString();
|
|
143
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "elbe-ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"author": "Robin Naumann",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -21,22 +21,24 @@
|
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build:ts": "tsc",
|
|
23
23
|
"build:dts": "tsc --declaration",
|
|
24
|
-
"build:css": "sass -q style/elbe.scss dist/elbe.css",
|
|
24
|
+
"build:css": "sass -q style/elbe-util.scss dist/elbe.css",
|
|
25
25
|
"build": "rm -rf ./dist && bun run build:ts && bun run build:dts && bun run build:css ",
|
|
26
26
|
"dev": "bun run build && (cd example && bun run dev)",
|
|
27
27
|
"pub": "bun run build && npm publish"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@types/bun": "
|
|
31
|
-
"sass": "^1.
|
|
30
|
+
"@types/bun": "^1.3.4",
|
|
31
|
+
"sass": "^1.97.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"typescript": "^5.
|
|
34
|
+
"typescript": "^5.9.3"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
+
"@types/react-dom": "^19.2.3",
|
|
37
38
|
"colors-convert": "^1.4.1",
|
|
38
|
-
"lucide-react": "^0.
|
|
39
|
-
"react": "^19.2.
|
|
40
|
-
"
|
|
39
|
+
"lucide-react": "^0.561.0",
|
|
40
|
+
"react": "^19.2.3",
|
|
41
|
+
"react-dom": "^19.2.3",
|
|
42
|
+
"wouter": "^3.8.1"
|
|
41
43
|
}
|
|
42
44
|
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { ElbeChildren } from "../util/types";
|
|
2
|
-
export declare function ElbeDialog({ title, open, onClose, children, _style, barrierDismissible, maxWidth, }: {
|
|
3
|
-
_style?: React.CSSProperties;
|
|
4
|
-
title: string;
|
|
5
|
-
open: boolean;
|
|
6
|
-
onClose: () => void;
|
|
7
|
-
children: ElbeChildren;
|
|
8
|
-
barrierDismissible?: boolean;
|
|
9
|
-
maxWidth?: number;
|
|
10
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { X } from "lucide-react";
|
|
3
|
-
import { useEffect } from "react";
|
|
4
|
-
import { IconButton } from "./button/icon_button";
|
|
5
|
-
import { Spaced } from "./layout/spaced";
|
|
6
|
-
export function ElbeDialog({ title, open, onClose, children, _style, barrierDismissible, maxWidth, }) {
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
if (open) {
|
|
9
|
-
document.body.style.overflow = "hidden";
|
|
10
|
-
}
|
|
11
|
-
else {
|
|
12
|
-
document.body.style.overflow = "";
|
|
13
|
-
}
|
|
14
|
-
return () => {
|
|
15
|
-
document.body.style.overflow = "";
|
|
16
|
-
};
|
|
17
|
-
}, [open]);
|
|
18
|
-
return (_jsx("dialog", { onClick: (e) => {
|
|
19
|
-
e.stopPropagation();
|
|
20
|
-
e.preventDefault();
|
|
21
|
-
if (barrierDismissible)
|
|
22
|
-
onClose();
|
|
23
|
-
}, open: open, style: Object.assign({ textAlign: "start" }, (_style !== null && _style !== void 0 ? _style : {})), children: _jsxs("div", { className: "elbe_dialog_base primary card plain-opaque padding-none", style: {
|
|
24
|
-
maxWidth: `min(${maxWidth !== null && maxWidth !== void 0 ? maxWidth : 40}rem, 100%)`,
|
|
25
|
-
minWidth: "10rem",
|
|
26
|
-
}, children: [_jsxs("div", { className: "row cross-center padded main-between", children: [_jsx("div", { className: "body-l b", children: title }), _jsx(IconButton.plain, { ariaLabel: "Close", icon: X, onTap: (e) => {
|
|
27
|
-
e.stopPropagation();
|
|
28
|
-
e.preventDefault();
|
|
29
|
-
onClose();
|
|
30
|
-
} })] }), _jsx(Spaced, { amount: 0.5 }), _jsx("div", { className: "padded", style: {
|
|
31
|
-
paddingTop: 0,
|
|
32
|
-
maxHeight: "80vh",
|
|
33
|
-
overflow: "auto",
|
|
34
|
-
}, children: children })] }) }));
|
|
35
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { ElbeChild, ElbeRoute, HeaderLogos } from "../../..";
|
|
2
|
-
export type AppBaseProps = HeaderLogos & {
|
|
3
|
-
globalActions?: ElbeChild[];
|
|
4
|
-
children: ElbeRoute | ElbeRoute[];
|
|
5
|
-
hashBasedRouting?: boolean;
|
|
6
|
-
};
|
|
7
|
-
/**
|
|
8
|
-
* app base is a layout component that provides an optional side menu and a content area.
|
|
9
|
-
* it is designed to be used as a base for other components and is
|
|
10
|
-
* used to create a consistent layout for pages. You can also pass global actions
|
|
11
|
-
* that will be displayed in the header of all pages.
|
|
12
|
-
*
|
|
13
|
-
* Provide `wouter.Route` or `MenuRoute` components as children to define the routes and menu items.
|
|
14
|
-
*/
|
|
15
|
-
export declare function AppBase(p: AppBaseProps): import("react/jsx-runtime").JSX.Element;
|