@olenbetong/appframe-ds 0.4.1 → 0.5.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/CHANGELOG.md +10 -0
- package/package.json +1 -1
- package/scripts/copyAssets.mjs +8 -0
- package/src/index.ts +3 -0
- package/src/layout/AppLayoutDesktop.tsx +3 -0
- package/src/layout/AppLayoutMobile.tsx +3 -0
- package/src/layout/AppMenu.css +1 -0
- package/src/layout/PageContainer.tsx +6 -0
- package/src/layout/index.tsx +5 -0
- package/src/list/ItemList.css +30 -0
- package/src/list/ItemList.tsx +52 -0
- package/src/list/ItemListItem.tsx +41 -0
- package/src/list/ItemListItemText.tsx +23 -0
- package/src/list/index.ts +3 -0
- package/src/page/Page.css +45 -0
- package/src/page/Page.tsx +70 -0
- package/src/page/PageBlock.css +59 -0
- package/src/page/PageBlock.tsx +78 -0
- package/src/page/PageHeader.css +148 -0
- package/src/page/PageHeader.tsx +184 -0
- package/src/page/index.ts +3 -0
- package/src/paper/Paper.css +35 -0
- package/src/paper/Paper.tsx +50 -0
- package/src/paper/index.ts +1 -0
- package/es/AfLookup/BoundLookup.d.ts +0 -10
- package/es/AfLookup/BoundLookup.js +0 -16
- package/es/AfLookup/Combobox.css +0 -78
- package/es/AfLookup/Combobox.d.ts +0 -32
- package/es/AfLookup/Combobox.js +0 -76
- package/es/AfLookup/Lookup.css +0 -125
- package/es/AfLookup/Lookup.d.ts +0 -35
- package/es/AfLookup/Lookup.js +0 -59
- package/es/AfLookup/LookupEdit.d.ts +0 -21
- package/es/AfLookup/LookupEdit.js +0 -49
- package/es/AfLookup/LookupGrid.css +0 -57
- package/es/AfLookup/LookupGrid.d.ts +0 -13
- package/es/AfLookup/LookupGrid.js +0 -27
- package/es/AfLookup/index.d.ts +0 -5
- package/es/AfLookup/index.js +0 -5
- package/es/binding/BoundCheckbox.d.ts +0 -9
- package/es/binding/BoundCheckbox.js +0 -13
- package/es/binding/BoundDatePicker.d.ts +0 -7
- package/es/binding/BoundDatePicker.js +0 -12
- package/es/binding/BoundDateTimePicker.d.ts +0 -7
- package/es/binding/BoundDateTimePicker.js +0 -14
- package/es/binding/BoundInput.d.ts +0 -6
- package/es/binding/BoundInput.js +0 -12
- package/es/binding/BoundNumericTextField.d.ts +0 -20
- package/es/binding/BoundNumericTextField.js +0 -58
- package/es/binding/BoundSelect.d.ts +0 -9
- package/es/binding/BoundSelect.js +0 -13
- package/es/binding/BoundSwitch.d.ts +0 -9
- package/es/binding/BoundSwitch.js +0 -13
- package/es/binding/BoundTextField.d.ts +0 -7
- package/es/binding/BoundTextField.js +0 -22
- package/es/binding/CancelButton.d.ts +0 -3
- package/es/binding/CancelButton.js +0 -14
- package/es/binding/CancelIconButton.d.ts +0 -2
- package/es/binding/CancelIconButton.js +0 -15
- package/es/binding/DataEditToolbar.d.ts +0 -13
- package/es/binding/DataEditToolbar.js +0 -28
- package/es/binding/DataObjectSelect.d.ts +0 -50
- package/es/binding/DataObjectSelect.js +0 -26
- package/es/binding/DeleteButton.d.ts +0 -8
- package/es/binding/DeleteButton.js +0 -14
- package/es/binding/DeleteIconButton.d.ts +0 -10
- package/es/binding/DeleteIconButton.js +0 -15
- package/es/binding/RefreshButton.d.ts +0 -3
- package/es/binding/RefreshButton.js +0 -14
- package/es/binding/RefreshIconButton.d.ts +0 -2
- package/es/binding/RefreshIconButton.js +0 -16
- package/es/binding/RefreshRowIconButton.d.ts +0 -7
- package/es/binding/RefreshRowIconButton.js +0 -15
- package/es/binding/SaveButton.d.ts +0 -3
- package/es/binding/SaveButton.js +0 -14
- package/es/binding/SaveIconButton.d.ts +0 -2
- package/es/binding/SaveIconButton.js +0 -15
- package/es/binding/index.d.ts +0 -19
- package/es/binding/index.js +0 -19
- package/es/index.d.ts +0 -3
- package/es/index.js +0 -3
- package/es/layout/AppLayoutDesktop.css +0 -65
- package/es/layout/AppLayoutDesktop.d.ts +0 -3
- package/es/layout/AppLayoutDesktop.js +0 -19
- package/es/layout/AppLayoutMobile.css +0 -75
- package/es/layout/AppLayoutMobile.d.ts +0 -3
- package/es/layout/AppLayoutMobile.js +0 -18
- package/es/layout/AppMenu.css +0 -67
- package/es/layout/AppMenu.d.ts +0 -8
- package/es/layout/AppMenu.js +0 -36
- package/es/layout/AppMenuItem.css +0 -97
- package/es/layout/AppMenuItem.d.ts +0 -50
- package/es/layout/AppMenuItem.js +0 -14
- package/es/layout/AppMenuSubheader.css +0 -18
- package/es/layout/AppMenuSubheader.d.ts +0 -4
- package/es/layout/AppMenuSubheader.js +0 -6
- package/es/layout/ErrorBoundary.css +0 -101
- package/es/layout/ErrorBoundary.d.ts +0 -30
- package/es/layout/ErrorBoundary.js +0 -81
- package/es/layout/PageContainer.css +0 -29
- package/es/layout/PageContainer.d.ts +0 -7
- package/es/layout/PageContainer.js +0 -6
- package/es/layout/PartnerToggle.d.ts +0 -5
- package/es/layout/PartnerToggle.js +0 -9
- package/es/layout/constants.d.ts +0 -2
- package/es/layout/constants.js +0 -2
- package/es/layout/index.css +0 -65
- package/es/layout/index.d.ts +0 -33
- package/es/layout/index.js +0 -31
- package/tsconfig.build.tsbuildinfo +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @olenbetong/appframe-ds
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- a118bef: Add Aksel-inspired `Page` primitives (`Page`, `Page.Block`, `Page.Header`) replacing the now-deprecated `AppLayout`, `AppLayoutDesktop`, `AppLayoutMobile` and `PageContainer`. `Page` supports both normal document scrolling and full-viewport app layouts (`layout="app"`, replacing `fullHeight`), `Page.Block` handles max-width/gutters/centering, and `Page.Header` comes in `appbar` (with responsive app menu drawer) and `plain` variants — both prepared for the upcoming sidebar-free site layout.
|
|
8
|
+
|
|
9
|
+
Add a `Paper` surface component (MUI `Paper` equivalent): neutral surface background, rounded corners and `--ds-shadow-*` elevation, with an optional `border` for an outlined variant. Pages using the `Page` primitive get a tinted body background (`--ds-color-neutral-background-tinted`) so surfaces stand out, matching the old site design.
|
|
10
|
+
|
|
11
|
+
Add `ItemList` components (`ItemList`, `ItemList.Item`, `ItemList.ItemText`) equivalent to MUI `List`/`ListItem`/`ListItemText`, for flex-row lists with dividers and primary/secondary text — typically rendered inside a `Paper` (`<Paper as={ItemList}>`).
|
|
12
|
+
|
|
3
13
|
## 0.4.1
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
package/package.json
CHANGED
package/scripts/copyAssets.mjs
CHANGED
|
@@ -12,9 +12,17 @@ const files = [
|
|
|
12
12
|
["src/layout/index.css", "es/layout/index.css"],
|
|
13
13
|
["src/layout/ErrorBoundary.css", "es/layout/ErrorBoundary.css"],
|
|
14
14
|
["src/layout/PageContainer.css", "es/layout/PageContainer.css"],
|
|
15
|
+
["src/page/Page.css", "es/page/Page.css"],
|
|
16
|
+
["src/page/PageBlock.css", "es/page/PageBlock.css"],
|
|
17
|
+
["src/page/PageHeader.css", "es/page/PageHeader.css"],
|
|
18
|
+
["src/list/ItemList.css", "es/list/ItemList.css"],
|
|
19
|
+
["src/paper/Paper.css", "es/paper/Paper.css"],
|
|
15
20
|
];
|
|
16
21
|
|
|
17
22
|
mkdirSync("es/layout", { recursive: true });
|
|
23
|
+
mkdirSync("es/list", { recursive: true });
|
|
24
|
+
mkdirSync("es/page", { recursive: true });
|
|
25
|
+
mkdirSync("es/paper", { recursive: true });
|
|
18
26
|
|
|
19
27
|
for (let [src, dest] of files) {
|
|
20
28
|
cpSync(src, dest);
|
package/src/index.ts
CHANGED
|
@@ -21,6 +21,9 @@ function LoadingFallback() {
|
|
|
21
21
|
);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* @deprecated Use the `Page` primitives instead (`Page`, `Page.Header`, `Page.Block`).
|
|
26
|
+
*/
|
|
24
27
|
export function AppLayoutDesktop({ AppMenuProps, children, menu, partner }: AppLayoutProps) {
|
|
25
28
|
let [open, setOpen] = useState(true);
|
|
26
29
|
|
|
@@ -15,6 +15,9 @@ function Toggle({ onToggle }: { onToggle: () => void }) {
|
|
|
15
15
|
);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* @deprecated Use the `Page` primitives instead (`Page`, `Page.Header`, `Page.Block`).
|
|
20
|
+
*/
|
|
18
21
|
export function AppLayoutMobile({ AppMenuProps, children, menu, partner }: AppLayoutProps) {
|
|
19
22
|
let [drawerOpen, setDrawerOpen] = useState(false);
|
|
20
23
|
let location = useLocation();
|
package/src/layout/AppMenu.css
CHANGED
|
@@ -8,6 +8,12 @@ export type PageContainerProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
8
8
|
maxWidth?: "xs" | "sm" | "md" | "lg" | "xl" | false;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* @deprecated Use `Page.Block` instead. `maxWidth` mapping: `xs`/`sm` →
|
|
13
|
+
* `width="text"`, `md` → `"md"`, `lg` → `"lg"`, `xl` → `"xl"`, `false` → omit
|
|
14
|
+
* `width`. Replace `fullHeight` with `<Page layout="app">` and
|
|
15
|
+
* `<Page.Block grow overflow="auto">`.
|
|
16
|
+
*/
|
|
11
17
|
export function PageContainer({ children, className, fullHeight, maxWidth, ...props }: PageContainerProps) {
|
|
12
18
|
return (
|
|
13
19
|
<div className={clsx("ObPageContainer-root", fullHeight && "ObPageContainer-fullHeight")}>
|
package/src/layout/index.tsx
CHANGED
|
@@ -54,6 +54,11 @@ export * from "./ErrorBoundary.js";
|
|
|
54
54
|
export * from "./PageContainer.js";
|
|
55
55
|
export * from "./PartnerToggle.js";
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* @deprecated Use the `Page` primitives instead (`Page`, `Page.Header`,
|
|
59
|
+
* `Page.Block`). `<AppLayout menu={...} title={...}>` becomes
|
|
60
|
+
* `<Page layout="app"><Page.Header title={...} menu={...} />...</Page>`.
|
|
61
|
+
*/
|
|
57
62
|
export function AppLayout(props: AppLayoutProps) {
|
|
58
63
|
let isSmall = useIsMobile();
|
|
59
64
|
let Component = isSmall ? AppLayoutMobile : AppLayoutDesktop;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
.ObItemList-root {
|
|
2
|
+
list-style: none;
|
|
3
|
+
margin: 0;
|
|
4
|
+
padding: 0;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.ObItemList-dividers > .ObItemListItem-root:not(:last-child) {
|
|
8
|
+
border-bottom: var(--ds-border-width-default, 1px) solid var(--ds-color-neutral-border-subtle, #dfdfdf);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.ObItemListItem-root {
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
gap: var(--ds-size-3, 0.75rem);
|
|
15
|
+
padding: var(--ds-size-2, 0.5rem) var(--ds-size-3, 0.75rem);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.ObItemListItem-disablePadding {
|
|
19
|
+
padding: 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.ObItemListItemText-root {
|
|
23
|
+
flex: 1 1 auto;
|
|
24
|
+
min-width: 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.ObItemListItemText-secondary {
|
|
28
|
+
font-size: 0.875rem;
|
|
29
|
+
color: var(--ds-color-neutral-text-subtle, #666);
|
|
30
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import "./ItemList.css";
|
|
2
|
+
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import type React from "react";
|
|
5
|
+
|
|
6
|
+
import { ItemListItem } from "./ItemListItem.js";
|
|
7
|
+
import { ItemListItemText } from "./ItemListItemText.js";
|
|
8
|
+
|
|
9
|
+
export type ItemListProps = React.HTMLAttributes<HTMLElement> & {
|
|
10
|
+
/**
|
|
11
|
+
* Overrides the rendered element (e.g. `"ol"`, `"menu"`)
|
|
12
|
+
* @default "ul"
|
|
13
|
+
*/
|
|
14
|
+
as?: React.ElementType;
|
|
15
|
+
children?: React.ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* Renders a divider between items
|
|
18
|
+
* @default true
|
|
19
|
+
*/
|
|
20
|
+
dividers?: boolean;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function ItemListComponent({ as: Component = "ul", children, className, dividers = true, ...props }: ItemListProps) {
|
|
24
|
+
return (
|
|
25
|
+
<Component className={clsx("ObItemList-root", dividers && "ObItemList-dividers", className)} {...props}>
|
|
26
|
+
{children}
|
|
27
|
+
</Component>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* List of flex-row items, equivalent to MUI `List`/`ListItem`. Named
|
|
33
|
+
* `ItemList` to avoid clashing with the designsystemet `List` component
|
|
34
|
+
* (which renders regular text lists). Typically used inside a `Paper`.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```jsx
|
|
38
|
+
* <Paper as={ItemList}>
|
|
39
|
+
* {records.map((record) => (
|
|
40
|
+
* <ItemList.Item key={record.PrimKey}>
|
|
41
|
+
* <img src="..." alt="" />
|
|
42
|
+
* <ItemList.ItemText primary={record.Title} secondary={record.Slug} />
|
|
43
|
+
* <Button>...</Button>
|
|
44
|
+
* </ItemList.Item>
|
|
45
|
+
* ))}
|
|
46
|
+
* </Paper>
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export const ItemList = Object.assign(ItemListComponent, {
|
|
50
|
+
Item: ItemListItem,
|
|
51
|
+
ItemText: ItemListItemText,
|
|
52
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import clsx from "clsx";
|
|
2
|
+
import type React from "react";
|
|
3
|
+
|
|
4
|
+
export type ItemListItemProps = React.HTMLAttributes<HTMLElement> & {
|
|
5
|
+
/**
|
|
6
|
+
* Overrides the rendered element
|
|
7
|
+
* @default "li"
|
|
8
|
+
*/
|
|
9
|
+
as?: React.ElementType;
|
|
10
|
+
children?: React.ReactNode;
|
|
11
|
+
ref?: React.Ref<HTMLElement>;
|
|
12
|
+
/**
|
|
13
|
+
* Removes the inline/block padding, e.g. when the item content is a
|
|
14
|
+
* link or button that should span the full row (add the padding on the
|
|
15
|
+
* interactive child instead so the whole row is clickable).
|
|
16
|
+
* @default false
|
|
17
|
+
*/
|
|
18
|
+
disablePadding?: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A single flex-row item inside an `ItemList`. Children are laid out
|
|
23
|
+
* horizontally with a standard gap; use `ItemList.ItemText` for the
|
|
24
|
+
* primary/secondary text block.
|
|
25
|
+
*/
|
|
26
|
+
export function ItemListItem({
|
|
27
|
+
as: Component = "li",
|
|
28
|
+
children,
|
|
29
|
+
className,
|
|
30
|
+
disablePadding,
|
|
31
|
+
...props
|
|
32
|
+
}: ItemListItemProps) {
|
|
33
|
+
return (
|
|
34
|
+
<Component
|
|
35
|
+
className={clsx("ObItemListItem-root", disablePadding && "ObItemListItem-disablePadding", className)}
|
|
36
|
+
{...props}
|
|
37
|
+
>
|
|
38
|
+
{children}
|
|
39
|
+
</Component>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import clsx from "clsx";
|
|
2
|
+
import type React from "react";
|
|
3
|
+
|
|
4
|
+
export type ItemListItemTextProps = React.HTMLAttributes<HTMLElement> & {
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
/** Primary line. `children` can be used instead. */
|
|
7
|
+
primary?: React.ReactNode;
|
|
8
|
+
/** Secondary line, rendered smaller with subtle text color */
|
|
9
|
+
secondary?: React.ReactNode;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Text block inside an `ItemList.Item`. Fills the remaining row space and
|
|
14
|
+
* renders a primary and an optional secondary line, like MUI `ListItemText`.
|
|
15
|
+
*/
|
|
16
|
+
export function ItemListItemText({ children, className, primary, secondary, ...props }: ItemListItemTextProps) {
|
|
17
|
+
return (
|
|
18
|
+
<div className={clsx("ObItemListItemText-root", className)} {...props}>
|
|
19
|
+
<div className="ObItemListItemText-primary">{primary ?? children}</div>
|
|
20
|
+
{secondary != null && <div className="ObItemListItemText-secondary">{secondary}</div>}
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
@media screen {
|
|
2
|
+
/*
|
|
3
|
+
* Pages get a tinted background so surfaces (Paper, drawer, dialogs —
|
|
4
|
+
* which use neutral-background/surface-default) stand out, matching the
|
|
5
|
+
* old site design. Scoped to Page usage so the token itself is untouched.
|
|
6
|
+
*/
|
|
7
|
+
body:has(.ObPage-root) {
|
|
8
|
+
background-color: var(--ds-color-neutral-background-tinted, #f3f3f4);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.ObPage-root {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.ObPage-document {
|
|
18
|
+
min-height: 100%;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.ObPage-app {
|
|
22
|
+
height: 100vh;
|
|
23
|
+
height: 100dvh;
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Offset content when a docked Page.Header drawer is open (desktop) */
|
|
28
|
+
.ObPage-root:has(.ObPageHeader-drawer.ObPageHeader-drawerDocked.ObPageHeader-drawerOpen) {
|
|
29
|
+
padding-left: var(--ob-page-drawer-width, 20rem);
|
|
30
|
+
transition: padding-left 225ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@media print {
|
|
35
|
+
.ObPage-root,
|
|
36
|
+
.ObPage-app {
|
|
37
|
+
display: block;
|
|
38
|
+
height: auto;
|
|
39
|
+
overflow: visible;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.ObPage-root:has(.ObPageHeader-drawer.ObPageHeader-drawerDocked.ObPageHeader-drawerOpen) {
|
|
43
|
+
padding-left: 0;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import "./Page.css";
|
|
2
|
+
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import type React from "react";
|
|
5
|
+
|
|
6
|
+
import { PageBlock } from "./PageBlock.js";
|
|
7
|
+
import { PageHeader } from "./PageHeader.js";
|
|
8
|
+
|
|
9
|
+
export type PageLayout = "document" | "app";
|
|
10
|
+
|
|
11
|
+
export type PageProps = React.HTMLAttributes<HTMLElement> & {
|
|
12
|
+
/**
|
|
13
|
+
* Overrides the rendered element
|
|
14
|
+
* @default "div"
|
|
15
|
+
*/
|
|
16
|
+
as?: React.ElementType;
|
|
17
|
+
children?: React.ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* Top-level layout strategy for the page.
|
|
20
|
+
*
|
|
21
|
+
* - `"document"` (default): normal document flow — the page grows with its
|
|
22
|
+
* content and the document scrolls.
|
|
23
|
+
* - `"app"`: the page fills the available viewport height with
|
|
24
|
+
* `overflow: hidden`, and scrolling is delegated down the tree (use
|
|
25
|
+
* `Page.Block` with `grow`/`overflow="auto"`, or utilities like
|
|
26
|
+
* `.scroll-view`). Replaces the old `PageContainer` `fullHeight` prop.
|
|
27
|
+
*
|
|
28
|
+
* @default "document"
|
|
29
|
+
*/
|
|
30
|
+
layout?: PageLayout;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function PageComponent({ as: Component = "div", children, className, layout = "document", ...props }: PageProps) {
|
|
34
|
+
return (
|
|
35
|
+
<Component
|
|
36
|
+
className={clsx("ObPage-root", layout === "app" ? "ObPage-app" : "ObPage-document", className)}
|
|
37
|
+
{...props}
|
|
38
|
+
>
|
|
39
|
+
{children}
|
|
40
|
+
</Component>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Top-level layout primitive for application pages, inspired by the Aksel
|
|
46
|
+
* `Page` primitives. Composes with `Page.Header` and `Page.Block`.
|
|
47
|
+
*
|
|
48
|
+
* Works both in the current sidebar-based site layout and the upcoming
|
|
49
|
+
* toolbar-based (sidebar-free) site layout.
|
|
50
|
+
*
|
|
51
|
+
* @example Document-style page (content scrolls with the document)
|
|
52
|
+
* ```jsx
|
|
53
|
+
* <Page>
|
|
54
|
+
* <Page.Header variant="plain" title="Report issue" />
|
|
55
|
+
* <Page.Block as="main" width="text" gutters>...</Page.Block>
|
|
56
|
+
* </Page>
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @example App-style page (fills the viewport, scrolling delegated down the tree)
|
|
60
|
+
* ```jsx
|
|
61
|
+
* <Page layout="app">
|
|
62
|
+
* <Page.Header title="Products" menu={<ProductsMenu />} />
|
|
63
|
+
* <Page.Block as="main" gutters grow overflow="auto">...</Page.Block>
|
|
64
|
+
* </Page>
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export const Page = Object.assign(PageComponent, {
|
|
68
|
+
Block: PageBlock,
|
|
69
|
+
Header: PageHeader,
|
|
70
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
.ObPageBlock-root {
|
|
2
|
+
box-sizing: border-box;
|
|
3
|
+
width: 100%;
|
|
4
|
+
margin-inline: auto;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.ObPageBlock-text {
|
|
8
|
+
max-width: calc(36rem + 2 * var(--ob-page-gutter, 0rem));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.ObPageBlock-md {
|
|
12
|
+
max-width: 48rem;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.ObPageBlock-lg {
|
|
16
|
+
max-width: 64rem;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.ObPageBlock-xl {
|
|
20
|
+
max-width: 80rem;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.ObPageBlock-2xl {
|
|
24
|
+
max-width: 90rem;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.ObPageBlock-gutters {
|
|
28
|
+
--ob-page-gutter: 1rem;
|
|
29
|
+
|
|
30
|
+
padding-inline: var(--ob-page-gutter);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@media screen and (min-width: 600px) {
|
|
34
|
+
.ObPageBlock-gutters {
|
|
35
|
+
--ob-page-gutter: 1.5rem;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@media screen {
|
|
40
|
+
.ObPageBlock-grow {
|
|
41
|
+
flex: 1 1 auto;
|
|
42
|
+
min-height: 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.ObPageBlock-overflowAuto {
|
|
46
|
+
overflow: auto;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.ObPageBlock-overflowHidden {
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@media print {
|
|
55
|
+
.ObPageBlock-overflowAuto,
|
|
56
|
+
.ObPageBlock-overflowHidden {
|
|
57
|
+
overflow: visible;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import "./PageBlock.css";
|
|
2
|
+
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import type React from "react";
|
|
5
|
+
|
|
6
|
+
export const pageBlockWidths = ["text", "md", "lg", "xl", "2xl"] as const;
|
|
7
|
+
|
|
8
|
+
export type PageBlockWidth = (typeof pageBlockWidths)[number];
|
|
9
|
+
|
|
10
|
+
export type PageBlockProps = React.HTMLAttributes<HTMLElement> & {
|
|
11
|
+
/**
|
|
12
|
+
* Overrides the rendered element (e.g. `"main"`, `"header"`, `"footer"`)
|
|
13
|
+
* @default "div"
|
|
14
|
+
*/
|
|
15
|
+
as?: React.ElementType;
|
|
16
|
+
children?: React.ReactNode;
|
|
17
|
+
/**
|
|
18
|
+
* Fills the remaining space of the parent `Page`. Combine with
|
|
19
|
+
* `overflow="auto"` inside `<Page layout="app">` to make the block the
|
|
20
|
+
* scroll container.
|
|
21
|
+
* @default false
|
|
22
|
+
*/
|
|
23
|
+
grow?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Adds a standardised responsive inline padding
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
gutters?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Overflow behaviour of the block. Only relevant inside
|
|
31
|
+
* `<Page layout="app">`, where scrolling is delegated down the tree.
|
|
32
|
+
*/
|
|
33
|
+
overflow?: "auto" | "hidden";
|
|
34
|
+
/**
|
|
35
|
+
* Predefined max-width. Content is horizontally centered.
|
|
36
|
+
*
|
|
37
|
+
* - `text`: 36rem (576px) — recommended line-length for running text
|
|
38
|
+
* - `md`: 48rem (768px)
|
|
39
|
+
* - `lg`: 64rem (1024px)
|
|
40
|
+
* - `xl`: 80rem (1280px)
|
|
41
|
+
* - `2xl`: 90rem (1440px)
|
|
42
|
+
*
|
|
43
|
+
* @default max-width: 100%
|
|
44
|
+
*/
|
|
45
|
+
width?: PageBlockWidth;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Layout block for defining max-width, gutters and horizontal centering.
|
|
50
|
+
* Replaces the deprecated `PageContainer` (`maxWidth` mapping: `xs`/`sm` →
|
|
51
|
+
* `text`, `md` → `md`, `lg` → `lg`, `xl` → `xl`, `false` → no `width`).
|
|
52
|
+
*/
|
|
53
|
+
export function PageBlock({
|
|
54
|
+
as: Component = "div",
|
|
55
|
+
children,
|
|
56
|
+
className,
|
|
57
|
+
grow,
|
|
58
|
+
gutters,
|
|
59
|
+
overflow,
|
|
60
|
+
width,
|
|
61
|
+
...props
|
|
62
|
+
}: PageBlockProps) {
|
|
63
|
+
return (
|
|
64
|
+
<Component
|
|
65
|
+
className={clsx(
|
|
66
|
+
"ObPageBlock-root",
|
|
67
|
+
width && `ObPageBlock-${width}`,
|
|
68
|
+
gutters && "ObPageBlock-gutters",
|
|
69
|
+
grow && "ObPageBlock-grow",
|
|
70
|
+
overflow && `ObPageBlock-overflow${overflow === "auto" ? "Auto" : "Hidden"}`,
|
|
71
|
+
className,
|
|
72
|
+
)}
|
|
73
|
+
{...props}
|
|
74
|
+
>
|
|
75
|
+
{children}
|
|
76
|
+
</Component>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
.ObPageHeader-root {
|
|
2
|
+
flex: 0 0 auto;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/* --- appbar variant --- */
|
|
6
|
+
|
|
7
|
+
.ObPageHeader-appbar {
|
|
8
|
+
background-color: var(--ds-color-brand-base-default, #0062ba);
|
|
9
|
+
color: var(--ds-color-brand-base-contrast-default, #fff);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.ObPageHeader-appbar .ObPageHeader-bar {
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
gap: 0.5rem;
|
|
16
|
+
min-height: var(--ob-page-appbar-height, 3.5rem);
|
|
17
|
+
padding-inline: 0.5rem 1rem;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.ObPageHeader-title {
|
|
21
|
+
flex: 1 1 auto;
|
|
22
|
+
margin: 0;
|
|
23
|
+
font-size: var(--ds-font-size-lg, 1.25rem);
|
|
24
|
+
font-weight: 500;
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
text-overflow: ellipsis;
|
|
27
|
+
white-space: nowrap;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.ObPageHeader-actions {
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
gap: 0.5rem;
|
|
34
|
+
margin-left: auto;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.ObPageHeader-title + .ObPageHeader-actions {
|
|
38
|
+
margin-left: 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.ObPageHeader-toggle {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
background: transparent;
|
|
46
|
+
border: none;
|
|
47
|
+
color: inherit;
|
|
48
|
+
cursor: pointer;
|
|
49
|
+
border-radius: 50%;
|
|
50
|
+
height: 2.75rem;
|
|
51
|
+
width: 2.75rem;
|
|
52
|
+
font-size: 1.5rem;
|
|
53
|
+
flex-shrink: 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.ObPageHeader-toggle:hover {
|
|
57
|
+
background-color: rgb(255 255 255 / 0.12);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* --- plain variant --- */
|
|
61
|
+
|
|
62
|
+
.ObPageHeader-plainInner {
|
|
63
|
+
display: flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
gap: 0.5rem;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.ObPageHeader-plainTitle {
|
|
69
|
+
margin: 0;
|
|
70
|
+
font-size: var(--ds-font-size-xl, 1.5rem);
|
|
71
|
+
font-weight: 500;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/*
|
|
75
|
+
* Reserved slot for the app-launcher and user profile menu in the future
|
|
76
|
+
* sidebar-free site layout. Hidden under the current sidebar layout; the
|
|
77
|
+
* activation mechanism is decided together with the new website template.
|
|
78
|
+
*/
|
|
79
|
+
.ObPageHeader-end {
|
|
80
|
+
display: none;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* --- drawer --- */
|
|
84
|
+
|
|
85
|
+
@media screen {
|
|
86
|
+
.ObPageHeader-drawer {
|
|
87
|
+
position: fixed;
|
|
88
|
+
top: 0;
|
|
89
|
+
left: 0;
|
|
90
|
+
bottom: 0;
|
|
91
|
+
width: var(--ob-page-drawer-width, 20rem);
|
|
92
|
+
max-width: 90%;
|
|
93
|
+
box-sizing: border-box;
|
|
94
|
+
background-color: var(--ds-color-neutral-background-default, #fff);
|
|
95
|
+
color: var(--ds-color-neutral-text-default, #1e2b3c);
|
|
96
|
+
transform: translateX(-100%);
|
|
97
|
+
transition: transform 225ms cubic-bezier(0, 0, 0.2, 1);
|
|
98
|
+
overflow: hidden;
|
|
99
|
+
display: flex;
|
|
100
|
+
flex-direction: column;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.ObPageHeader-drawer > .MenuItems-root {
|
|
104
|
+
flex: 1 1 auto;
|
|
105
|
+
min-height: 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.ObPageHeader-drawer.ObPageHeader-drawerOpen {
|
|
109
|
+
transform: translateX(0);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.ObPageHeader-drawerDocked {
|
|
113
|
+
border-right: 1px solid var(--ds-color-neutral-border-subtle, #dfdfdf);
|
|
114
|
+
z-index: 1100;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.ObPageHeader-drawerOverlayMode {
|
|
118
|
+
z-index: 1200;
|
|
119
|
+
box-shadow:
|
|
120
|
+
rgb(0 0 0 / 20%) 0px 8px 10px -5px,
|
|
121
|
+
rgb(0 0 0 / 14%) 0px 16px 24px 2px,
|
|
122
|
+
rgb(0 0 0 / 12%) 0px 6px 30px 5px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.ObPageHeader-drawerOverlay {
|
|
126
|
+
position: fixed;
|
|
127
|
+
inset: 0;
|
|
128
|
+
background-color: rgb(0 0 0 / 50%);
|
|
129
|
+
z-index: 1199;
|
|
130
|
+
opacity: 0;
|
|
131
|
+
pointer-events: none;
|
|
132
|
+
transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.ObPageHeader-drawerOverlay.ObPageHeader-drawerOpen {
|
|
136
|
+
opacity: 1;
|
|
137
|
+
pointer-events: auto;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@media print {
|
|
142
|
+
.ObPageHeader-appbar .ObPageHeader-bar,
|
|
143
|
+
.ObPageHeader-drawer,
|
|
144
|
+
.ObPageHeader-drawerOverlay,
|
|
145
|
+
.ObPageHeader-toggle {
|
|
146
|
+
display: none !important;
|
|
147
|
+
}
|
|
148
|
+
}
|