@olenbetong/appframe-ds 0.2.0 → 0.3.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.
Files changed (102) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/es/AfLookup/BoundLookup.d.ts +9 -0
  3. package/es/AfLookup/BoundLookup.js +16 -0
  4. package/es/AfLookup/Combobox.css +78 -0
  5. package/es/AfLookup/Combobox.d.ts +15 -0
  6. package/es/AfLookup/Combobox.js +124 -0
  7. package/es/AfLookup/Lookup.css +104 -0
  8. package/es/AfLookup/Lookup.d.ts +34 -0
  9. package/es/AfLookup/Lookup.js +161 -0
  10. package/es/AfLookup/index.d.ts +3 -0
  11. package/es/AfLookup/index.js +3 -0
  12. package/es/binding/BoundCheckbox.d.ts +9 -0
  13. package/es/binding/BoundCheckbox.js +13 -0
  14. package/es/binding/BoundDatePicker.d.ts +7 -0
  15. package/es/binding/BoundDatePicker.js +12 -0
  16. package/es/binding/BoundDateTimePicker.d.ts +7 -0
  17. package/es/binding/BoundDateTimePicker.js +14 -0
  18. package/es/binding/BoundInput.d.ts +6 -0
  19. package/es/binding/BoundInput.js +12 -0
  20. package/es/binding/BoundNumericTextField.d.ts +20 -0
  21. package/es/binding/BoundNumericTextField.js +58 -0
  22. package/es/binding/BoundSelect.d.ts +9 -0
  23. package/es/binding/BoundSelect.js +13 -0
  24. package/es/binding/BoundSwitch.d.ts +9 -0
  25. package/es/binding/BoundSwitch.js +13 -0
  26. package/es/binding/BoundTextField.d.ts +7 -0
  27. package/es/binding/BoundTextField.js +22 -0
  28. package/es/binding/CancelButton.d.ts +3 -0
  29. package/es/binding/CancelButton.js +14 -0
  30. package/es/binding/CancelIconButton.d.ts +2 -0
  31. package/es/binding/CancelIconButton.js +15 -0
  32. package/es/binding/DataEditToolbar.d.ts +13 -0
  33. package/es/binding/DataEditToolbar.js +28 -0
  34. package/es/binding/DataObjectSelect.d.ts +50 -0
  35. package/es/binding/DataObjectSelect.js +26 -0
  36. package/es/binding/DeleteButton.d.ts +8 -0
  37. package/es/binding/DeleteButton.js +14 -0
  38. package/es/binding/DeleteIconButton.d.ts +10 -0
  39. package/es/binding/DeleteIconButton.js +15 -0
  40. package/es/binding/RefreshButton.d.ts +3 -0
  41. package/es/binding/RefreshButton.js +14 -0
  42. package/es/binding/RefreshIconButton.d.ts +2 -0
  43. package/es/binding/RefreshIconButton.js +16 -0
  44. package/es/binding/RefreshRowIconButton.d.ts +7 -0
  45. package/es/binding/RefreshRowIconButton.js +15 -0
  46. package/es/binding/SaveButton.d.ts +3 -0
  47. package/es/binding/SaveButton.js +14 -0
  48. package/es/binding/SaveIconButton.d.ts +2 -0
  49. package/es/binding/SaveIconButton.js +15 -0
  50. package/es/binding/index.d.ts +19 -0
  51. package/es/binding/index.js +19 -0
  52. package/es/index.d.ts +3 -0
  53. package/es/index.js +3 -0
  54. package/es/layout/AppLayoutDesktop.css +65 -0
  55. package/es/layout/AppLayoutDesktop.d.ts +3 -0
  56. package/es/layout/AppLayoutDesktop.js +19 -0
  57. package/es/layout/AppLayoutMobile.css +75 -0
  58. package/es/layout/AppLayoutMobile.d.ts +3 -0
  59. package/es/layout/AppLayoutMobile.js +18 -0
  60. package/es/layout/AppMenu.css +67 -0
  61. package/es/layout/AppMenu.d.ts +8 -0
  62. package/es/layout/AppMenu.js +36 -0
  63. package/es/layout/AppMenuItem.css +97 -0
  64. package/es/layout/AppMenuItem.d.ts +50 -0
  65. package/es/layout/AppMenuItem.js +14 -0
  66. package/es/layout/AppMenuSubheader.css +18 -0
  67. package/es/layout/AppMenuSubheader.d.ts +4 -0
  68. package/es/layout/AppMenuSubheader.js +6 -0
  69. package/es/layout/ErrorBoundary.css +101 -0
  70. package/es/layout/ErrorBoundary.d.ts +30 -0
  71. package/es/layout/ErrorBoundary.js +81 -0
  72. package/es/layout/PageContainer.css +29 -0
  73. package/es/layout/PageContainer.d.ts +7 -0
  74. package/es/layout/PageContainer.js +6 -0
  75. package/es/layout/PartnerToggle.d.ts +5 -0
  76. package/es/layout/PartnerToggle.js +9 -0
  77. package/es/layout/constants.d.ts +2 -0
  78. package/es/layout/constants.js +2 -0
  79. package/es/layout/index.css +65 -0
  80. package/es/layout/index.d.ts +33 -0
  81. package/es/layout/index.js +31 -0
  82. package/package.json +14 -9
  83. package/src/index.ts +1 -0
  84. package/src/layout/AppLayoutDesktop.css +65 -0
  85. package/src/layout/AppLayoutDesktop.tsx +45 -0
  86. package/src/layout/AppLayoutMobile.css +75 -0
  87. package/src/layout/AppLayoutMobile.tsx +50 -0
  88. package/src/layout/AppMenu.css +67 -0
  89. package/src/layout/AppMenu.tsx +75 -0
  90. package/src/layout/AppMenuItem.css +97 -0
  91. package/src/layout/AppMenuItem.tsx +80 -0
  92. package/src/layout/AppMenuSubheader.css +18 -0
  93. package/src/layout/AppMenuSubheader.tsx +14 -0
  94. package/src/layout/ErrorBoundary.css +101 -0
  95. package/src/layout/ErrorBoundary.tsx +162 -0
  96. package/src/layout/PageContainer.css +29 -0
  97. package/src/layout/PageContainer.tsx +19 -0
  98. package/src/layout/PartnerToggle.tsx +23 -0
  99. package/src/layout/constants.ts +2 -0
  100. package/src/layout/index.css +65 -0
  101. package/src/layout/index.tsx +62 -0
  102. package/tsconfig.build.tsbuildinfo +1 -0
@@ -0,0 +1,75 @@
1
+ .LayoutSmall-root:has(> .ObPageContainer-fullHeight, > .app-view) {
2
+ display: grid;
3
+ grid-template-rows: auto 1fr;
4
+ height: 100dvh;
5
+ overflow: hidden;
6
+ }
7
+
8
+ .partner-nav {
9
+ color: white;
10
+ }
11
+
12
+ /* Minimal toggle-only bar shown when there's a sidebar menu but no server AppBar */
13
+ .LayoutSmall-menuBar {
14
+ display: flex;
15
+ align-items: center;
16
+ height: var(--navbar-height, 3.5rem);
17
+ }
18
+
19
+ .LayoutSmall-toggle {
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ background: transparent;
24
+ border: none;
25
+ color: inherit;
26
+ cursor: pointer;
27
+ border-radius: 50%;
28
+ height: var(--navbar-height, 3.5rem);
29
+ width: var(--navbar-height, 3.5rem);
30
+ font-size: 1.5rem;
31
+ flex-shrink: 0;
32
+ }
33
+
34
+ .LayoutSmall-toggle:hover {
35
+ background-color: rgb(0 0 0 / 0.06);
36
+ }
37
+
38
+ /* Drawer overlay */
39
+ .LayoutSmall-drawerOverlay {
40
+ position: fixed;
41
+ inset: 0;
42
+ background-color: rgb(0 0 0 / 50%);
43
+ z-index: 1199;
44
+ opacity: 0;
45
+ pointer-events: none;
46
+ transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1);
47
+ }
48
+
49
+ .LayoutSmall-drawerOverlay.open {
50
+ opacity: 1;
51
+ pointer-events: auto;
52
+ }
53
+
54
+ /* Drawer panel */
55
+ .LayoutSmall-drawer {
56
+ position: fixed;
57
+ top: 0;
58
+ left: 0;
59
+ bottom: 0;
60
+ max-width: 90%;
61
+ width: var(--navbar-drawer-width, 20rem);
62
+ z-index: 1200;
63
+ background-color: var(--ds-color-neutral-background-default, #fff);
64
+ box-shadow:
65
+ rgb(0 0 0 / 20%) 0px 8px 10px -5px,
66
+ rgb(0 0 0 / 14%) 0px 16px 24px 2px,
67
+ rgb(0 0 0 / 12%) 0px 6px 30px 5px;
68
+ transform: translateX(-100%);
69
+ transition: transform 225ms cubic-bezier(0, 0, 0.2, 1);
70
+ overflow: hidden;
71
+ }
72
+
73
+ .LayoutSmall-drawer.open {
74
+ transform: translateX(0);
75
+ }
@@ -0,0 +1,3 @@
1
+ import "./AppLayoutMobile.css";
2
+ import type { AppLayoutProps } from "./index.js";
3
+ export declare function AppLayoutMobile({ AppMenuProps, children, menu, partner }: AppLayoutProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import "./AppLayoutMobile.css";
3
+ import { useEffect, useState } from "react";
4
+ import { useLocation } from "react-router";
5
+ import { AppMenu } from "./AppMenu.js";
6
+ import { PartnerToggle } from "./PartnerToggle.js";
7
+ function Toggle({ onToggle }) {
8
+ return (_jsx("button", { type: "button", className: "LayoutSmall-toggle", onClick: onToggle, "aria-label": "Toggle menu", children: "\u2630" }));
9
+ }
10
+ export function AppLayoutMobile({ AppMenuProps, children, menu, partner }) {
11
+ let [drawerOpen, setDrawerOpen] = useState(false);
12
+ let location = useLocation();
13
+ // biome-ignore lint/correctness/useExhaustiveDependencies: close drawer on navigation
14
+ useEffect(() => {
15
+ setDrawerOpen(false);
16
+ }, [location]);
17
+ return (_jsxs("div", { className: "LayoutSmall-root", children: [partner && menu && (_jsx(PartnerToggle, { open: drawerOpen, onClick: () => setDrawerOpen((c) => !c) })), !partner && menu && (_jsx("div", { className: "LayoutSmall-menuBar", children: _jsx(Toggle, { onToggle: () => setDrawerOpen((c) => !c) }) })), menu && (_jsxs(_Fragment, { children: [_jsx("div", { className: `LayoutSmall-drawerOverlay${drawerOpen ? " open" : ""}`, onClick: () => setDrawerOpen(false), "aria-hidden": "true" }), _jsx("div", { className: `LayoutSmall-drawer AppMenu-drawer${drawerOpen ? " open" : ""}`, children: _jsx(AppMenu, { ...AppMenuProps, children: menu }) })] })), children] }));
18
+ }
@@ -0,0 +1,67 @@
1
+ .AppMenu-header {
2
+ padding: 1rem;
3
+ }
4
+
5
+ .AppMenu-header .AppMenu-title {
6
+ font-weight: 500;
7
+ letter-spacing: -0.02rem;
8
+ font-size: var(--ds-font-size-xl, 1.5rem);
9
+ margin: 0;
10
+ }
11
+
12
+ .AppMenu-header .AppMenu-subTitle {
13
+ font-size: var(--ds-font-size-sm, 1.1rem);
14
+ margin: 0;
15
+ color: var(--ds-color-neutral-text-subtle, #555);
16
+ }
17
+
18
+ .AppMenu-divider {
19
+ border: none;
20
+ border-top: 1px solid var(--ds-color-neutral-border-default, #dfdfdf);
21
+ margin: 0;
22
+ }
23
+
24
+ .AppMenu-pendingCount {
25
+ color: var(--ds-color-neutral-text-subtle, #666) !important;
26
+ font-size: 0.8em;
27
+ font-weight: 700;
28
+ padding-right: 0.5rem;
29
+ }
30
+
31
+ .MenuItems-root {
32
+ overflow-y: auto;
33
+ scrollbar-width: thin;
34
+ scrollbar-color: var(--ds-color-neutral-border-default, #dfdfdf) transparent;
35
+ }
36
+
37
+ .MenuItems-root::-webkit-scrollbar {
38
+ width: 0.4rem;
39
+ }
40
+
41
+ .MenuItems-root::-webkit-scrollbar-button {
42
+ display: none;
43
+ }
44
+
45
+ .MenuItems-root::-webkit-scrollbar-thumb {
46
+ width: 0.4rem;
47
+ background-color: var(--ds-color-neutral-border-default, #dfdfdf);
48
+ }
49
+
50
+ .MenuItems-root .AppMenuList-nested .AppMenuItem-root {
51
+ padding-left: 3rem;
52
+ padding-block: 0.25rem;
53
+ }
54
+
55
+ .MenuItems-root .AppMenuList-nested .AppMenuItem-root .AppMenuItem-text {
56
+ font-size: 0.9rem;
57
+ }
58
+
59
+ .MenuItems-collapse {
60
+ margin-bottom: -0.5rem;
61
+ margin-top: -0.5rem;
62
+ }
63
+
64
+ .MenuItem-noWrap {
65
+ overflow: hidden;
66
+ white-space: nowrap;
67
+ }
@@ -0,0 +1,8 @@
1
+ import "./AppMenu.css";
2
+ import type React from "react";
3
+ export type AppMenuProps = Pick<React.HTMLProps<HTMLElement>, "aria-label" | "aria-labelledby" | "className" | "id"> & {
4
+ children?: React.ReactNode[] | React.ReactNode;
5
+ headerPrimary?: string;
6
+ headerSecondary?: string;
7
+ };
8
+ export declare function AppMenu({ children, className, headerPrimary, headerSecondary, ...props }: AppMenuProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import "./AppMenu.css";
3
+ import { getLocalizedString } from "@olenbetong/appframe-core";
4
+ import clsx from "clsx";
5
+ function MenuHeader({ primary, secondary }) {
6
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "AppMenu-header", children: [_jsx("h1", { className: "AppMenu-title", id: "NavigationHeader", children: primary }), secondary && _jsx("p", { className: "AppMenu-subTitle", children: secondary })] }), _jsx("hr", { className: "AppMenu-divider" })] }));
7
+ }
8
+ function handleKeyDown(evt) {
9
+ let nextElement = null;
10
+ let appMenu = evt.currentTarget;
11
+ if (!document.activeElement || !(appMenu instanceof HTMLElement) || !appMenu.contains(document.activeElement))
12
+ return;
13
+ let links = Array.from(appMenu.querySelectorAll("a, button"));
14
+ let currentIndex = links.indexOf(document.activeElement);
15
+ if (evt.key === "ArrowDown" || evt.key === "ArrowRight") {
16
+ if (currentIndex < links.length) {
17
+ nextElement = links[currentIndex + 1];
18
+ }
19
+ }
20
+ else if (evt.key === "ArrowUp" || evt.key === "ArrowLeft") {
21
+ if (currentIndex > 0) {
22
+ nextElement = links[currentIndex - 1];
23
+ }
24
+ }
25
+ else if (evt.key === "Home" || evt.key === "End") {
26
+ nextElement = links.at(evt.key === "Home" ? 0 : -1);
27
+ }
28
+ if (nextElement instanceof HTMLElement) {
29
+ evt.preventDefault();
30
+ nextElement.scrollIntoView({ block: "nearest" });
31
+ nextElement.focus();
32
+ }
33
+ }
34
+ export function AppMenu({ children, className, headerPrimary, headerSecondary, ...props }) {
35
+ return (_jsxs("nav", { "aria-labelledby": headerPrimary ? "NavigationHeader" : undefined, "aria-label": headerPrimary ? undefined : getLocalizedString("Application pages"), className: clsx(className, "MenuItems-root"), onKeyDown: handleKeyDown, ...props, children: [headerPrimary && _jsx(MenuHeader, { primary: headerPrimary, secondary: headerSecondary }), children] }));
36
+ }
@@ -0,0 +1,97 @@
1
+ .AppMenuItem-root {
2
+ display: flex;
3
+ align-items: center;
4
+ position: relative;
5
+ border-radius: var(--ds-border-radius-md, 0.25rem);
6
+ overflow: hidden;
7
+ margin: 0.5rem;
8
+ padding: 0.5rem;
9
+ width: calc(100% - 1rem);
10
+ box-sizing: border-box;
11
+ text-decoration: none;
12
+ color: var(--ds-color-neutral-text-default, rgba(0, 0, 0, 0.87));
13
+ background: transparent;
14
+ border: none;
15
+ cursor: pointer;
16
+ font-family: inherit;
17
+ font-size: inherit;
18
+ text-align: left;
19
+ }
20
+
21
+ @media (pointer: fine) {
22
+ .AppMenuItem-root {
23
+ margin: 0.2rem 0.5rem;
24
+ }
25
+ }
26
+
27
+ .AppMenuItem-icon {
28
+ display: flex;
29
+ align-items: center;
30
+ min-width: 2.5rem;
31
+ color: inherit;
32
+ flex-shrink: 0;
33
+ }
34
+
35
+ .AppMenuItem-text {
36
+ flex: 1;
37
+ font-weight: 500;
38
+ color: var(--ds-color-neutral-text-default, rgba(0, 0, 0, 0.87));
39
+ min-width: 0;
40
+ overflow: hidden;
41
+ text-overflow: ellipsis;
42
+ white-space: nowrap;
43
+ }
44
+
45
+ .AppMenuItem-info {
46
+ margin-left: auto;
47
+ flex-shrink: 0;
48
+ }
49
+
50
+ .AppMenuItem-root::before {
51
+ background-color: #000;
52
+ opacity: 0;
53
+ transition:
54
+ opacity 30ms linear,
55
+ background-color 30ms linear;
56
+ content: "";
57
+ position: absolute;
58
+ top: 0;
59
+ left: 0;
60
+ height: 100%;
61
+ width: 100%;
62
+ pointer-events: none;
63
+ }
64
+
65
+ .AppMenuItem-root:hover,
66
+ .AppMenuItem-root:focus,
67
+ .AppMenuItem-root:active {
68
+ text-decoration: none;
69
+ outline: none;
70
+ }
71
+
72
+ .AppMenuItem-root:hover::before {
73
+ opacity: 0.04;
74
+ }
75
+
76
+ .AppMenuItem-root:active::before {
77
+ opacity: 0.16;
78
+ }
79
+
80
+ .AppMenuItem-root.active::before,
81
+ .AppMenuItem-root.active:hover::before {
82
+ background-color: var(--ds-color-brand-base-default, var(--brand-primary, #0062ba));
83
+ opacity: 0.16;
84
+ }
85
+
86
+ .AppMenuItem-root.active:hover::before {
87
+ opacity: 0.2;
88
+ }
89
+
90
+ .AppMenuItem-root.active:active::before {
91
+ opacity: 0.24;
92
+ }
93
+
94
+ .AppMenuItem-root.active .AppMenuItem-text,
95
+ .AppMenuItem-root.active .AppMenuItem-icon {
96
+ color: var(--ds-color-brand-base-default, var(--brand-primary, #0062ba));
97
+ }
@@ -0,0 +1,50 @@
1
+ import "./AppMenuItem.css";
2
+ import type { To } from "react-router";
3
+ import { type NavLinkProps } from "react-router";
4
+ import type React from "react";
5
+ export type AppMenuItemProps = Omit<NavLinkProps, "className"> & {
6
+ children: React.ReactNode | React.ReactNode[];
7
+ info?: React.ReactNode | React.ReactNode[];
8
+ icon?: React.ReactNode;
9
+ onClick?: React.MouseEventHandler<HTMLAnchorElement>;
10
+ to: To;
11
+ end?: boolean;
12
+ className?: string;
13
+ };
14
+ export declare const AppMenuItem: React.ForwardRefExoticComponent<Omit<NavLinkProps, "className"> & {
15
+ children: React.ReactNode | React.ReactNode[];
16
+ info?: React.ReactNode | React.ReactNode[];
17
+ icon?: React.ReactNode;
18
+ onClick?: React.MouseEventHandler<HTMLAnchorElement>;
19
+ to: To;
20
+ end?: boolean;
21
+ className?: string;
22
+ } & React.RefAttributes<HTMLAnchorElement>>;
23
+ export type AppMenuItemExternalLinkProps = Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "href"> & {
24
+ children: React.ReactNode | React.ReactNode[];
25
+ info?: React.ReactNode | React.ReactNode[];
26
+ icon?: React.ReactNode;
27
+ href: string;
28
+ target?: string;
29
+ };
30
+ export declare const AppMenuItemExternalLink: React.ForwardRefExoticComponent<Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "href"> & {
31
+ children: React.ReactNode | React.ReactNode[];
32
+ info?: React.ReactNode | React.ReactNode[];
33
+ icon?: React.ReactNode;
34
+ href: string;
35
+ target?: string;
36
+ } & React.RefAttributes<HTMLAnchorElement>>;
37
+ export type AppMenuItemButtonProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onClick"> & {
38
+ active?: boolean;
39
+ children: React.ReactNode | React.ReactNode[];
40
+ info?: React.ReactNode | React.ReactNode[];
41
+ icon?: React.ReactNode;
42
+ onClick?: React.MouseEventHandler<HTMLButtonElement>;
43
+ };
44
+ export declare const AppMenuItemButton: React.ForwardRefExoticComponent<Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onClick"> & {
45
+ active?: boolean;
46
+ children: React.ReactNode | React.ReactNode[];
47
+ info?: React.ReactNode | React.ReactNode[];
48
+ icon?: React.ReactNode;
49
+ onClick?: React.MouseEventHandler<HTMLButtonElement>;
50
+ } & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import "./AppMenuItem.css";
3
+ import clsx from "clsx";
4
+ import { forwardRef } from "react";
5
+ import { NavLink } from "react-router";
6
+ export const AppMenuItem = forwardRef(function AppMenuItem({ children, className, end = true, info, to, icon, ...props }, ref) {
7
+ return (_jsxs(NavLink, { viewTransition: true, to: to, className: clsx("AppMenuItem-root", className), end: end, ...props, ref: ref, children: [icon && _jsx("span", { className: "AppMenuItem-icon", children: icon }), _jsx("span", { className: "AppMenuItem-text", children: children }), info && _jsx("span", { className: "AppMenuItem-info", children: info })] }));
8
+ });
9
+ export const AppMenuItemExternalLink = forwardRef(function AppMenuItemExternalLink({ children, className, href, info, icon, target, ...props }, ref) {
10
+ return (_jsxs("a", { href: href, className: clsx("AppMenuItem-root", className), target: target, ...props, ref: ref, children: [icon && _jsx("span", { className: "AppMenuItem-icon", children: icon }), _jsx("span", { className: "AppMenuItem-text", children: children }), info && _jsx("span", { className: "AppMenuItem-info", children: info })] }));
11
+ });
12
+ export const AppMenuItemButton = forwardRef(function AppMenuItemButton({ children, className, active = false, info, onClick, icon, ...props }, ref) {
13
+ return (_jsxs("button", { type: "button", onClick: onClick, className: clsx("AppMenuItem-root", className, active && "active"), ...props, ref: ref, children: [icon && _jsx("span", { className: "AppMenuItem-icon", children: icon }), _jsx("span", { className: "AppMenuItem-text", children: children }), info && _jsx("span", { className: "AppMenuItem-info", children: info })] }));
14
+ });
@@ -0,0 +1,18 @@
1
+ .AppMenuSubheader-root {
2
+ font-size: var(--ds-font-size-xs, 0.75rem);
3
+ font-weight: 600;
4
+ letter-spacing: 0.08em;
5
+ text-transform: uppercase;
6
+ padding: 0.5rem 1rem;
7
+ color: var(--ds-color-neutral-text-subtle, #555);
8
+ background-color: var(--ds-color-neutral-background-default, transparent);
9
+ position: sticky;
10
+ top: 0;
11
+ z-index: 1;
12
+ }
13
+
14
+ @media (pointer: fine) {
15
+ .AppMenuSubheader-root {
16
+ line-height: 36px;
17
+ }
18
+ }
@@ -0,0 +1,4 @@
1
+ import "./AppMenuSubheader.css";
2
+ import type React from "react";
3
+ export type AppMenuSubheaderProps = React.HTMLAttributes<HTMLDivElement>;
4
+ export declare function AppMenuSubheader({ children, className, ...props }: AppMenuSubheaderProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import "./AppMenuSubheader.css";
3
+ import clsx from "clsx";
4
+ export function AppMenuSubheader({ children, className, ...props }) {
5
+ return (_jsx("div", { className: clsx("AppMenuSubheader-root", className), ...props, children: children }));
6
+ }
@@ -0,0 +1,101 @@
1
+ .AppErrorBoundary-root {
2
+ height: 100vh;
3
+ display: flex;
4
+ align-items: center;
5
+ justify-content: center;
6
+ }
7
+
8
+ .AppErrorBoundary-inner {
9
+ max-width: 36rem;
10
+ width: 100%;
11
+ margin: 0 auto;
12
+ padding: 1rem;
13
+ text-align: center;
14
+ }
15
+
16
+ .AppErrorBoundary-image {
17
+ opacity: 0.6;
18
+ max-height: 30vh;
19
+ max-width: 50vw;
20
+ }
21
+
22
+ .AppErrorBoundary-title {
23
+ margin-top: var(--ds-size-6, 1.5rem);
24
+ font-size: var(--ds-font-size-6, 1.5rem);
25
+ font-weight: 700;
26
+ }
27
+
28
+ .AppErrorBoundary-alert {
29
+ margin-block: var(--ds-size-6, 1.5rem);
30
+ background: var(--ds-color-danger-surface-default, #fde8e8);
31
+ color: var(--ds-color-danger-text-default, #c01c2c);
32
+ border: 1px solid var(--ds-color-danger-border-default, #f5a6ae);
33
+ border-radius: var(--ds-border-radius-md, 0.25rem);
34
+ padding: var(--ds-size-3, 0.75rem) var(--ds-size-4, 1rem);
35
+ text-align: left;
36
+ }
37
+
38
+ .AppErrorBoundary-message {
39
+ margin-bottom: var(--ds-size-6, 1.5rem);
40
+ }
41
+
42
+ .AppErrorBoundary-reload {
43
+ appearance: none;
44
+ border: 1px solid currentColor;
45
+ border-radius: var(--ds-border-radius-md, 0.25rem);
46
+ background: transparent;
47
+ color: inherit;
48
+ cursor: pointer;
49
+ font: inherit;
50
+ font-size: var(--ds-font-size-3, 1rem);
51
+ padding: var(--ds-size-2, 0.5rem) var(--ds-size-4, 1rem);
52
+ margin-block: var(--ds-size-6, 1.5rem);
53
+ }
54
+
55
+ .AppErrorBoundary-reload:hover {
56
+ background: rgb(0 0 0 / 0.05);
57
+ }
58
+
59
+ .ErrorAlertFallback-root {
60
+ display: flex;
61
+ align-items: flex-start;
62
+ gap: var(--ds-size-2, 0.5rem);
63
+ background: var(--ds-color-danger-surface-default, #fde8e8);
64
+ color: var(--ds-color-danger-text-default, #c01c2c);
65
+ border: 1px solid var(--ds-color-danger-border-default, #f5a6ae);
66
+ border-radius: var(--ds-border-radius-md, 0.25rem);
67
+ padding: var(--ds-size-3, 0.75rem) var(--ds-size-4, 1rem);
68
+ }
69
+
70
+ .ErrorAlertFallback-content {
71
+ flex: 1;
72
+ }
73
+
74
+ .ErrorAlertFallback-title {
75
+ font-weight: 600;
76
+ margin-bottom: var(--ds-size-1, 0.25rem);
77
+ }
78
+
79
+ .ErrorAlertFallback-reload {
80
+ appearance: none;
81
+ background: transparent;
82
+ border: none;
83
+ color: inherit;
84
+ cursor: pointer;
85
+ font: inherit;
86
+ font-weight: 600;
87
+ padding: 0;
88
+ text-decoration: underline;
89
+ flex-shrink: 0;
90
+ }
91
+
92
+ .RouteErrorPage-stack {
93
+ background: rgb(0 0 0 / 0.07);
94
+ border: 2px solid rgb(0 0 0 / 0.15);
95
+ border-radius: var(--ds-border-radius-md, 0.25rem);
96
+ padding: var(--ds-size-4, 1rem);
97
+ overflow-x: auto;
98
+ white-space: pre-wrap;
99
+ word-break: break-all;
100
+ font-size: var(--ds-font-size-1, 0.75rem);
101
+ }
@@ -0,0 +1,30 @@
1
+ import "./ErrorBoundary.css";
2
+ import type { DataObject } from "@olenbetong/appframe-data";
3
+ import { Component } from "react";
4
+ export type AppErrorBoundaryProps = {
5
+ children?: React.ReactNode;
6
+ message?: string | ((error: unknown) => string);
7
+ imgSrc?: string;
8
+ image?: React.ReactNode;
9
+ };
10
+ type AppErrorBoundaryState = {
11
+ hasError: boolean;
12
+ error: unknown;
13
+ };
14
+ export declare class AppErrorBoundary extends Component<AppErrorBoundaryProps, AppErrorBoundaryState> {
15
+ constructor(props: AppErrorBoundaryProps);
16
+ static getDerivedStateFromError(error: unknown): AppErrorBoundaryState;
17
+ componentDidCatch(error: unknown, errorInfo: unknown): void;
18
+ render(): React.ReactNode;
19
+ }
20
+ export type ErrorAlertFallbackProps = {
21
+ error: unknown;
22
+ className?: string;
23
+ };
24
+ export declare function ErrorAlertFallback({ error, className }: ErrorAlertFallbackProps): import("react/jsx-runtime").JSX.Element;
25
+ export declare function RouteErrorPage(): import("react/jsx-runtime").JSX.Element;
26
+ export declare function AsyncErrorAlert(): import("react/jsx-runtime").JSX.Element;
27
+ export declare function DataObjectErrorAlert({ dataObject }: {
28
+ dataObject: DataObject<any>;
29
+ }): import("react/jsx-runtime").JSX.Element | null;
30
+ export {};
@@ -0,0 +1,81 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import "./ErrorBoundary.css";
3
+ import { getLocalizedString } from "@olenbetong/appframe-core";
4
+ import { useError } from "@olenbetong/appframe-react";
5
+ import { Component, useEffect } from "react";
6
+ import { useAsyncError, useRouteError } from "react-router";
7
+ import { PageContainer } from "./PageContainer.js";
8
+ function parseError(error) {
9
+ let message = "Unspecified error";
10
+ let stackTrace = null;
11
+ let title = getLocalizedString("Unexpected application error");
12
+ if (typeof error === "string") {
13
+ message = error;
14
+ }
15
+ else if (error && typeof error === "object") {
16
+ const err = error;
17
+ if (err.status) {
18
+ title = `${err.status} ${err.statusText}`;
19
+ message = err.message ?? "";
20
+ }
21
+ else if (err.message) {
22
+ message = err.message;
23
+ if (err.type) {
24
+ message = `${err.type}: ${message}`;
25
+ }
26
+ }
27
+ else {
28
+ message = String(error);
29
+ }
30
+ if (err.stack || err.stackTrace) {
31
+ stackTrace = err.stack ?? err.stackTrace;
32
+ }
33
+ }
34
+ return { message, stackTrace, title };
35
+ }
36
+ export class AppErrorBoundary extends Component {
37
+ constructor(props) {
38
+ super(props);
39
+ this.state = { hasError: false, error: null };
40
+ }
41
+ static getDerivedStateFromError(error) {
42
+ return { hasError: true, error };
43
+ }
44
+ componentDidCatch(error, errorInfo) {
45
+ console.error(error, errorInfo);
46
+ }
47
+ render() {
48
+ if (this.state.hasError) {
49
+ const { imgSrc, message, image } = this.props;
50
+ const { message: errorMessage } = parseError(this.state.error);
51
+ const customMessage = typeof message === "function" ? message(this.state.error) : message;
52
+ return (_jsx("div", { className: "AppErrorBoundary-root", children: _jsxs("div", { className: "AppErrorBoundary-inner", children: [image ?? (_jsx("img", { alt: "", src: imgSrc ?? "/file/site/graphic/error-broken-robot.svg", className: "AppErrorBoundary-image" })), _jsx("h1", { className: "AppErrorBoundary-title", children: getLocalizedString("Something went terribly wrong!") }), _jsx("div", { className: "AppErrorBoundary-alert", children: errorMessage }), _jsx("p", { className: "AppErrorBoundary-message", children: customMessage ??
53
+ getLocalizedString("Sometimes it is enough to simply refresh the page. If the error persists, contact the IT department.") }), _jsx("button", { type: "button", className: "AppErrorBoundary-reload", onClick: () => window.location.reload(), children: getLocalizedString("Refresh page") })] }) }));
54
+ }
55
+ return this.props.children;
56
+ }
57
+ }
58
+ export function ErrorAlertFallback({ error, className }) {
59
+ const message = error instanceof Error ? error.message : String(error);
60
+ return (_jsxs("div", { className: `ErrorAlertFallback-root${className ? ` ${className}` : ""}`, children: [_jsxs("div", { className: "ErrorAlertFallback-content", children: [_jsx("p", { className: "ErrorAlertFallback-title", children: getLocalizedString("An error occured. Try refreshing the page") }), _jsx("p", { children: message })] }), _jsx("button", { type: "button", className: "ErrorAlertFallback-reload", onClick: () => window.location.reload(), children: getLocalizedString("Reload page") })] }));
61
+ }
62
+ // ── RouteErrorPage ────────────────────────────────────────────────────────────
63
+ export function RouteErrorPage() {
64
+ const error = useRouteError();
65
+ const { message, stackTrace, title } = parseError(error);
66
+ useEffect(() => {
67
+ globalThis.af?.common?.logError?.(error);
68
+ }, [error]);
69
+ return (_jsxs(PageContainer, { className: "flow", children: [_jsx("h1", { children: title }), message && _jsx("p", { children: message }), stackTrace && _jsx("pre", { className: "RouteErrorPage-stack", children: stackTrace })] }));
70
+ }
71
+ // ── AsyncErrorAlert ───────────────────────────────────────────────────────────
72
+ export function AsyncErrorAlert() {
73
+ const error = useAsyncError();
74
+ const message = typeof error === "string" ? error : error?.message;
75
+ return _jsx("div", { className: "ErrorAlertFallback-root", children: message });
76
+ }
77
+ // ── DataObjectErrorAlert ──────────────────────────────────────────────────────
78
+ export function DataObjectErrorAlert({ dataObject }) {
79
+ const error = useError(dataObject);
80
+ return error ? _jsx("div", { className: "ErrorAlertFallback-root", children: error }) : null;
81
+ }
@@ -0,0 +1,29 @@
1
+ @media screen {
2
+ .ObPageContainer-root {
3
+ max-height: 100%;
4
+ overflow: auto;
5
+ height: auto;
6
+ }
7
+
8
+ .ObPageContainer-root.ObPageContainer-fullHeight {
9
+ height: 100%;
10
+ }
11
+
12
+ .ObPageContainer-inner {
13
+ margin-left: 0;
14
+ height: auto;
15
+ padding: 1rem;
16
+ box-sizing: border-box;
17
+ max-width: 100%;
18
+ }
19
+
20
+ .ObPageContainer-fullHeight .ObPageContainer-inner {
21
+ height: 100%;
22
+ }
23
+ }
24
+
25
+ @media screen and (min-width: 600px) {
26
+ .ObPageContainer-inner {
27
+ padding: 1rem 1.5rem;
28
+ }
29
+ }
@@ -0,0 +1,7 @@
1
+ import "./PageContainer.css";
2
+ import type React from "react";
3
+ export type PageContainerProps = React.HTMLAttributes<HTMLDivElement> & {
4
+ fullHeight?: boolean;
5
+ maxWidth?: "xs" | "sm" | "md" | "lg" | "xl" | false;
6
+ };
7
+ export declare function PageContainer({ children, className, fullHeight, maxWidth, ...props }: PageContainerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import "./PageContainer.css";
3
+ import clsx from "clsx";
4
+ export function PageContainer({ children, className, fullHeight, maxWidth, ...props }) {
5
+ return (_jsx("div", { className: clsx("ObPageContainer-root", fullHeight && "ObPageContainer-fullHeight"), children: _jsx("div", { className: clsx("ObPageContainer-inner", className), ...props, children: children }) }));
6
+ }
@@ -0,0 +1,5 @@
1
+ import type React from "react";
2
+ export declare function PartnerToggle({ onClick, open, }: {
3
+ open: boolean;
4
+ onClick?: React.MouseEventHandler<HTMLButtonElement>;
5
+ }): React.ReactNode;