@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.
Files changed (110) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/package.json +1 -1
  3. package/scripts/copyAssets.mjs +8 -0
  4. package/src/index.ts +3 -0
  5. package/src/layout/AppLayoutDesktop.tsx +3 -0
  6. package/src/layout/AppLayoutMobile.tsx +3 -0
  7. package/src/layout/AppMenu.css +1 -0
  8. package/src/layout/PageContainer.tsx +6 -0
  9. package/src/layout/index.tsx +5 -0
  10. package/src/list/ItemList.css +30 -0
  11. package/src/list/ItemList.tsx +52 -0
  12. package/src/list/ItemListItem.tsx +41 -0
  13. package/src/list/ItemListItemText.tsx +23 -0
  14. package/src/list/index.ts +3 -0
  15. package/src/page/Page.css +45 -0
  16. package/src/page/Page.tsx +70 -0
  17. package/src/page/PageBlock.css +59 -0
  18. package/src/page/PageBlock.tsx +78 -0
  19. package/src/page/PageHeader.css +148 -0
  20. package/src/page/PageHeader.tsx +184 -0
  21. package/src/page/index.ts +3 -0
  22. package/src/paper/Paper.css +35 -0
  23. package/src/paper/Paper.tsx +50 -0
  24. package/src/paper/index.ts +1 -0
  25. package/es/AfLookup/BoundLookup.d.ts +0 -10
  26. package/es/AfLookup/BoundLookup.js +0 -16
  27. package/es/AfLookup/Combobox.css +0 -78
  28. package/es/AfLookup/Combobox.d.ts +0 -32
  29. package/es/AfLookup/Combobox.js +0 -76
  30. package/es/AfLookup/Lookup.css +0 -125
  31. package/es/AfLookup/Lookup.d.ts +0 -35
  32. package/es/AfLookup/Lookup.js +0 -59
  33. package/es/AfLookup/LookupEdit.d.ts +0 -21
  34. package/es/AfLookup/LookupEdit.js +0 -49
  35. package/es/AfLookup/LookupGrid.css +0 -57
  36. package/es/AfLookup/LookupGrid.d.ts +0 -13
  37. package/es/AfLookup/LookupGrid.js +0 -27
  38. package/es/AfLookup/index.d.ts +0 -5
  39. package/es/AfLookup/index.js +0 -5
  40. package/es/binding/BoundCheckbox.d.ts +0 -9
  41. package/es/binding/BoundCheckbox.js +0 -13
  42. package/es/binding/BoundDatePicker.d.ts +0 -7
  43. package/es/binding/BoundDatePicker.js +0 -12
  44. package/es/binding/BoundDateTimePicker.d.ts +0 -7
  45. package/es/binding/BoundDateTimePicker.js +0 -14
  46. package/es/binding/BoundInput.d.ts +0 -6
  47. package/es/binding/BoundInput.js +0 -12
  48. package/es/binding/BoundNumericTextField.d.ts +0 -20
  49. package/es/binding/BoundNumericTextField.js +0 -58
  50. package/es/binding/BoundSelect.d.ts +0 -9
  51. package/es/binding/BoundSelect.js +0 -13
  52. package/es/binding/BoundSwitch.d.ts +0 -9
  53. package/es/binding/BoundSwitch.js +0 -13
  54. package/es/binding/BoundTextField.d.ts +0 -7
  55. package/es/binding/BoundTextField.js +0 -22
  56. package/es/binding/CancelButton.d.ts +0 -3
  57. package/es/binding/CancelButton.js +0 -14
  58. package/es/binding/CancelIconButton.d.ts +0 -2
  59. package/es/binding/CancelIconButton.js +0 -15
  60. package/es/binding/DataEditToolbar.d.ts +0 -13
  61. package/es/binding/DataEditToolbar.js +0 -28
  62. package/es/binding/DataObjectSelect.d.ts +0 -50
  63. package/es/binding/DataObjectSelect.js +0 -26
  64. package/es/binding/DeleteButton.d.ts +0 -8
  65. package/es/binding/DeleteButton.js +0 -14
  66. package/es/binding/DeleteIconButton.d.ts +0 -10
  67. package/es/binding/DeleteIconButton.js +0 -15
  68. package/es/binding/RefreshButton.d.ts +0 -3
  69. package/es/binding/RefreshButton.js +0 -14
  70. package/es/binding/RefreshIconButton.d.ts +0 -2
  71. package/es/binding/RefreshIconButton.js +0 -16
  72. package/es/binding/RefreshRowIconButton.d.ts +0 -7
  73. package/es/binding/RefreshRowIconButton.js +0 -15
  74. package/es/binding/SaveButton.d.ts +0 -3
  75. package/es/binding/SaveButton.js +0 -14
  76. package/es/binding/SaveIconButton.d.ts +0 -2
  77. package/es/binding/SaveIconButton.js +0 -15
  78. package/es/binding/index.d.ts +0 -19
  79. package/es/binding/index.js +0 -19
  80. package/es/index.d.ts +0 -3
  81. package/es/index.js +0 -3
  82. package/es/layout/AppLayoutDesktop.css +0 -65
  83. package/es/layout/AppLayoutDesktop.d.ts +0 -3
  84. package/es/layout/AppLayoutDesktop.js +0 -19
  85. package/es/layout/AppLayoutMobile.css +0 -75
  86. package/es/layout/AppLayoutMobile.d.ts +0 -3
  87. package/es/layout/AppLayoutMobile.js +0 -18
  88. package/es/layout/AppMenu.css +0 -67
  89. package/es/layout/AppMenu.d.ts +0 -8
  90. package/es/layout/AppMenu.js +0 -36
  91. package/es/layout/AppMenuItem.css +0 -97
  92. package/es/layout/AppMenuItem.d.ts +0 -50
  93. package/es/layout/AppMenuItem.js +0 -14
  94. package/es/layout/AppMenuSubheader.css +0 -18
  95. package/es/layout/AppMenuSubheader.d.ts +0 -4
  96. package/es/layout/AppMenuSubheader.js +0 -6
  97. package/es/layout/ErrorBoundary.css +0 -101
  98. package/es/layout/ErrorBoundary.d.ts +0 -30
  99. package/es/layout/ErrorBoundary.js +0 -81
  100. package/es/layout/PageContainer.css +0 -29
  101. package/es/layout/PageContainer.d.ts +0 -7
  102. package/es/layout/PageContainer.js +0 -6
  103. package/es/layout/PartnerToggle.d.ts +0 -5
  104. package/es/layout/PartnerToggle.js +0 -9
  105. package/es/layout/constants.d.ts +0 -2
  106. package/es/layout/constants.js +0 -2
  107. package/es/layout/index.css +0 -65
  108. package/es/layout/index.d.ts +0 -33
  109. package/es/layout/index.js +0 -31
  110. 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olenbetong/appframe-ds",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Components that bind Designsystemet components to Appframe data objects",
5
5
  "type": "module",
6
6
  "types": "./es/index.d.ts",
@@ -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
@@ -1,3 +1,6 @@
1
1
  export * from "./AfLookup/index.js";
2
2
  export * from "./binding/index.js";
3
3
  export * from "./layout/index.js";
4
+ export * from "./list/index.js";
5
+ export * from "./page/index.js";
6
+ export * from "./paper/index.js";
@@ -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();
@@ -7,6 +7,7 @@
7
7
  letter-spacing: -0.02rem;
8
8
  font-size: var(--ds-font-size-xl, 1.5rem);
9
9
  margin: 0;
10
+ color: var(--ds-color-neutral-text-default, #1e2b3c);
10
11
  }
11
12
 
12
13
  .AppMenu-header .AppMenu-subTitle {
@@ -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")}>
@@ -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,3 @@
1
+ export * from "./ItemList.js";
2
+ export * from "./ItemListItem.js";
3
+ export * from "./ItemListItemText.js";
@@ -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
+ }