@datalayer/primer-addons 1.0.21 → 1.0.23

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 (100) hide show
  1. package/README.md +14 -0
  2. package/lib/components/appearance/AppearanceMenuWithStore.d.ts +26 -0
  3. package/lib/components/appearance/AppearanceMenuWithStore.js +21 -0
  4. package/lib/components/appearance/CollaboratorPalette.d.ts +65 -0
  5. package/lib/components/appearance/CollaboratorPalette.js +160 -0
  6. package/lib/components/appearance/index.d.ts +2 -0
  7. package/lib/components/appearance/index.js +2 -0
  8. package/lib/components/box/Box.d.ts +17 -5
  9. package/lib/components/box/Box.js +4 -7
  10. package/lib/components/box/sx.js +1 -2
  11. package/lib/components/calendar-picker/CalendarPicker.d.ts +35 -0
  12. package/lib/components/calendar-picker/CalendarPicker.js +146 -0
  13. package/lib/components/calendar-picker/index.d.ts +1 -0
  14. package/lib/components/calendar-picker/index.js +5 -0
  15. package/lib/components/card/Card.d.ts +6 -1
  16. package/lib/components/card/Card.js +1 -1
  17. package/lib/components/closeable-flash/CloseableFlash.d.ts +23 -1
  18. package/lib/components/closeable-flash/CloseableFlash.js +52 -4
  19. package/lib/components/color/ColorSwatch.d.ts +1 -1
  20. package/lib/components/color-picker/ColorPicker.d.ts +97 -0
  21. package/lib/components/color-picker/ColorPicker.js +358 -0
  22. package/lib/components/color-picker/index.d.ts +1 -0
  23. package/lib/components/color-picker/index.js +5 -0
  24. package/lib/components/content-loader/ContentLoader.d.ts +1 -1
  25. package/lib/components/content-loader/ContentLoader.js +4 -0
  26. package/lib/components/content-loader/ContentLoader.stories.d.ts +1 -1
  27. package/lib/components/date-picker/DatePicker.d.ts +81 -0
  28. package/lib/components/date-picker/DatePicker.js +204 -0
  29. package/lib/components/date-picker/TimeColumns.d.ts +23 -0
  30. package/lib/components/date-picker/TimeColumns.js +88 -0
  31. package/lib/components/date-picker/index.d.ts +1 -0
  32. package/lib/components/date-picker/index.js +5 -0
  33. package/lib/components/icons/CircleIcon.d.ts +1 -1
  34. package/lib/components/icons/CircleIcon.js +4 -0
  35. package/lib/components/icons/CircleIcon.stories.d.ts +1 -1
  36. package/lib/components/index.d.ts +5 -0
  37. package/lib/components/index.js +9 -0
  38. package/lib/components/logo/DatalayerText.d.ts +1 -1
  39. package/lib/components/logo/index.d.ts +0 -1
  40. package/lib/components/logo/index.js +4 -1
  41. package/lib/components/side-overlay/SideOverlay.d.ts +5 -5
  42. package/lib/components/side-overlay/SideOverlay.js +4 -0
  43. package/lib/components/side-overlay/SideOverlay.stories.d.ts +1 -1
  44. package/lib/components/side-overlay/SideOverlay.stories.js +4 -0
  45. package/lib/components/slider/Slider.d.ts +1 -1
  46. package/lib/components/slider/Slider.js +4 -0
  47. package/lib/components/slider/Slider.stories.d.ts +1 -1
  48. package/lib/components/slider/Slider.stories.js +4 -0
  49. package/lib/components/sliding-panel/SlidingPanel.d.ts +1 -1
  50. package/lib/components/sliding-panel/SlidingPanel.js +17 -1
  51. package/lib/components/sliding-panel/SlidingPanel.stories.d.ts +1 -1
  52. package/lib/components/sliding-panel/SlidingPanel.stories.js +4 -0
  53. package/lib/components/toolbar/Toolbar.d.ts +2 -2
  54. package/lib/components/toolbar/Toolbar.js +0 -0
  55. package/lib/components/toolbar/ToolbarButton.d.ts +3 -1
  56. package/lib/components/toolbar/ToolbarButton.js +54 -22
  57. package/lib/components/toolbar/ToolbarDivider.d.ts +1 -1
  58. package/lib/components/toolbar/ToolbarDropdown.d.ts +3 -3
  59. package/lib/components/toolbar/ToolbarDropdown.js +66 -35
  60. package/lib/components/toolbar/ToolbarRenderer.d.ts +20 -3
  61. package/lib/components/toolbar/ToolbarRenderer.js +16 -16
  62. package/lib/components/toolbar/types.d.ts +8 -8
  63. package/lib/index.js +4 -0
  64. package/lib/reactor/AppearancePlugin.d.ts +31 -0
  65. package/lib/reactor/AppearancePlugin.js +63 -0
  66. package/lib/reactor/ThemePlugin.d.ts +5 -0
  67. package/lib/reactor/ThemePlugin.js +98 -0
  68. package/lib/reactor/index.d.ts +30 -0
  69. package/lib/reactor/index.js +34 -0
  70. package/lib/reactor/page-layout/PageLayout.d.ts +148 -0
  71. package/lib/reactor/page-layout/PageLayout.js +326 -0
  72. package/lib/reactor/page-layout/PageLayoutPlugin.d.ts +59 -0
  73. package/lib/reactor/page-layout/PageLayoutPlugin.js +105 -0
  74. package/lib/reactor/page-layout/PagePanelToggle.d.ts +21 -0
  75. package/lib/reactor/page-layout/PagePanelToggle.js +29 -0
  76. package/lib/reactor/page-layout/SlotPageLayout.d.ts +36 -0
  77. package/lib/reactor/page-layout/SlotPageLayout.js +25 -0
  78. package/lib/reactor/page-layout/panelState.d.ts +28 -0
  79. package/lib/reactor/page-layout/panelState.js +49 -0
  80. package/lib/story-helpers.d.ts +2 -2
  81. package/lib/theme/DatalayerBrandThemeProvider.d.ts +1 -1
  82. package/lib/theme/DatalayerThemeProvider.d.ts +5 -5
  83. package/lib/theme/DatalayerThemeProvider.js +119 -58
  84. package/lib/theme/css/createThemeCSSVars.d.ts +1 -0
  85. package/lib/theme/css/createThemeCSSVars.js +9 -0
  86. package/lib/theme/fontStacks.d.ts +2 -0
  87. package/lib/theme/fontStacks.js +6 -0
  88. package/lib/theme/index.d.ts +2 -0
  89. package/lib/theme/index.js +2 -0
  90. package/lib/theme/palettes/ColorPalette.d.ts +3 -3
  91. package/lib/theme/palettes/ColorPalette.js +1 -2
  92. package/lib/theme/palettes/index.js +1 -2
  93. package/lib/theme/portableTheme.d.ts +32 -0
  94. package/lib/theme/portableTheme.js +45 -0
  95. package/lib/utils/Helper.d.ts +2 -2
  96. package/lib/utils/Portals.js +3 -1
  97. package/lib/utils/Styles.d.ts +1 -1
  98. package/package.json +21 -14
  99. package/lib/components/logo/DI.d.ts +0 -19
  100. package/lib/components/logo/DI.js +0 -20
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The reactor-facing corner of primer-addons.
3
+ *
4
+ * Deliberately NOT re-exported from the package's main barrel: importing
5
+ * anything from here pulls `@datalayer/reactor` into the bundle, and most
6
+ * consumers of primer-addons are pages with no reactor at all. A host that
7
+ * wants the plugins imports them by path:
8
+ *
9
+ * ```ts
10
+ * import { AppearancePlugin, ThemePlugin } from '@datalayer/primer-addons/lib/reactor';
11
+ * ```
12
+ *
13
+ * Three plugins. `ThemePlugin` keeps Primer's portals in the application's
14
+ * color mode and toggles that mode by command. `AppearancePlugin` puts the
15
+ * appearance menu — color mode, theme, the theme's description and a
16
+ * preview of it — in a slot, the header by default, and brings the first
17
+ * along as a dependency. `PageLayoutPlugin` arranges a host's slots as a
18
+ * page: the work on a centred sheet, a band above it, a side panel opened
19
+ * from the header — and the layout, its toggle and its signals are exported
20
+ * on their own for a host that wires the parts itself, as the Loop does.
21
+ *
22
+ * @module reactor
23
+ */
24
+ export { ThemePlugin, THEME_PLUGIN_NAME, TOGGLE_COLOR_MODE_COMMAND, } from "./ThemePlugin";
25
+ export { AppearancePlugin, APPEARANCE_PLUGIN_NAME, APPEARANCE_MENU_COMPONENT_ID, type AppearancePluginConfig, } from "./AppearancePlugin";
26
+ export { PageLayoutPlugin, PAGE_LAYOUT_PLUGIN_NAME, PageLayoutSlots, type PageLayoutPluginConfig, } from "./page-layout/PageLayoutPlugin";
27
+ export { PageLayout, PAGE_SHEET_WIDTH, PAGE_PANEL_WIDTH, PAGE_SIZE_FORMATS, resolvePageSize, type PageLayoutProps, type PageSize, type PageSizeFormat, } from "./page-layout/PageLayout";
28
+ export { PagePanelToggle, type PagePanelToggleProps, } from "./page-layout/PagePanelToggle";
29
+ export { SlotPageLayout, SlotPanelToggle, type SlotPageLayoutProps, } from "./page-layout/SlotPageLayout";
30
+ export { pageLayoutPanelOpen, pageLayoutSheet, pageLayoutActivity, openPagePanel, closePagePanel, togglePagePanel, } from "./page-layout/panelState";
@@ -0,0 +1,34 @@
1
+ /*
2
+ * Copyright (c) 2023-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * The reactor-facing corner of primer-addons.
7
+ *
8
+ * Deliberately NOT re-exported from the package's main barrel: importing
9
+ * anything from here pulls `@datalayer/reactor` into the bundle, and most
10
+ * consumers of primer-addons are pages with no reactor at all. A host that
11
+ * wants the plugins imports them by path:
12
+ *
13
+ * ```ts
14
+ * import { AppearancePlugin, ThemePlugin } from '@datalayer/primer-addons/lib/reactor';
15
+ * ```
16
+ *
17
+ * Three plugins. `ThemePlugin` keeps Primer's portals in the application's
18
+ * color mode and toggles that mode by command. `AppearancePlugin` puts the
19
+ * appearance menu — color mode, theme, the theme's description and a
20
+ * preview of it — in a slot, the header by default, and brings the first
21
+ * along as a dependency. `PageLayoutPlugin` arranges a host's slots as a
22
+ * page: the work on a centred sheet, a band above it, a side panel opened
23
+ * from the header — and the layout, its toggle and its signals are exported
24
+ * on their own for a host that wires the parts itself, as the Loop does.
25
+ *
26
+ * @module reactor
27
+ */
28
+ export { ThemePlugin, THEME_PLUGIN_NAME, TOGGLE_COLOR_MODE_COMMAND, } from "./ThemePlugin";
29
+ export { AppearancePlugin, APPEARANCE_PLUGIN_NAME, APPEARANCE_MENU_COMPONENT_ID, } from "./AppearancePlugin";
30
+ export { PageLayoutPlugin, PAGE_LAYOUT_PLUGIN_NAME, PageLayoutSlots, } from "./page-layout/PageLayoutPlugin";
31
+ export { PageLayout, PAGE_SHEET_WIDTH, PAGE_PANEL_WIDTH, PAGE_SIZE_FORMATS, resolvePageSize, } from "./page-layout/PageLayout";
32
+ export { PagePanelToggle, } from "./page-layout/PagePanelToggle";
33
+ export { SlotPageLayout, SlotPanelToggle, } from "./page-layout/SlotPageLayout";
34
+ export { pageLayoutPanelOpen, pageLayoutSheet, pageLayoutActivity, openPagePanel, closePagePanel, togglePagePanel, } from "./page-layout/panelState";
@@ -0,0 +1,148 @@
1
+ /**
2
+ * The page layout: the work on a centred sheet, like a document.
3
+ *
4
+ * A quiet canvas fills the view. On it, one sheet — the page, at a reading
5
+ * width, with the margins and the shadow a page has — and a band docked
6
+ * directly above the canvas — or, when the host asks for it, floating over
7
+ * the canvas as a draggable card — like the toolbar of a document
8
+ * application, with chips directly under it — or standing in the side panel
9
+ * under its content, the page bare. With no page to show, the side panel's
10
+ * content is the page: the same sheet, holding it, and no side panel. While
11
+ * something works in the page, a small line at the top of the sheet says
12
+ * what it is doing — "Analyst is adding a cell…" — so the change is seen
13
+ * where it happens. The panel is a column on the right that opens when it
14
+ * is wanted — on either side by `panelSide`, and by `panelMode` a column, an
15
+ * overlay over the page's edge or a small window in its corner; closed, the
16
+ * page is all there is.
17
+ *
18
+ * The sheet is a reading column by default, as tall as what is on it; asked
19
+ * for a paper (`pageSize`: letter, A4, or a free width and height), it takes
20
+ * the paper's width and is at least the paper's height.
21
+ *
22
+ * The band holds one **mount point**, and that is what gives its content its
23
+ * width: the same `sheetWidth` column the sheet gets, centred the same way,
24
+ * inside the same canvas padding. So the band and the page below it are one
25
+ * column with one pair of edges, and they stay one when the panel opens and
26
+ * narrows the canvas — because the band lives inside the page column, not
27
+ * across the whole view. A floating card sizes itself, which is why it never
28
+ * lines up with anything. It hangs at the top of the canvas, over the chips
29
+ * and the sheet, which start where they would with no band at all: the flow
30
+ * keeps no room for the card. What it covers is a drag away, and a strip of
31
+ * canvas held clear for it was a blank band before the page — worse than a
32
+ * card over the first lines.
33
+ *
34
+ * Nothing here decides what the parts do. A host wires them; this arranges
35
+ * them. It grew up as the Loop's page layout, where the page is a notebook or
36
+ * a document, the band is the composer and the panel is the conversation;
37
+ * the names here are the general ones.
38
+ *
39
+ * @module reactor/page-layout/PageLayout
40
+ */
41
+ import { type JSX, type ReactNode } from "react";
42
+ /** A page reads best at about this width; wider and lines run too long. */
43
+ export declare const PAGE_SHEET_WIDTH = 920;
44
+ /** The side panel, when open. */
45
+ export declare const PAGE_PANEL_WIDTH = 400;
46
+ /**
47
+ * How the sheet is sized: `free`, or as a sheet of paper.
48
+ *
49
+ * `free` is the reading column — `PAGE_SHEET_WIDTH` wide, or the width the
50
+ * host gives, as tall as its content. `letter` and `a4` are the papers, at
51
+ * CSS's 96 dots to the inch: the sheet takes their width and is at least
52
+ * their height, so a short document still reads as a page rather than as a
53
+ * box shrunk around three lines.
54
+ */
55
+ export type PageSizeFormat = "free" | "letter" | "a4";
56
+ /** The sheet's size: a format, and free width and height over it. */
57
+ export type PageSize = {
58
+ /** `free` unless said otherwise. */
59
+ format?: PageSizeFormat;
60
+ /** The sheet's width in px. Over a paper format, replaces the paper's. */
61
+ width?: number;
62
+ /**
63
+ * The sheet's least height in px; content past it grows the sheet. Over a
64
+ * paper format, replaces the paper's. Omitted under `free`: the content's.
65
+ */
66
+ height?: number;
67
+ };
68
+ /** The papers, in CSS px (96 to the inch): US Letter 8.5 × 11 in, A4 210 × 297 mm. */
69
+ export declare const PAGE_SIZE_FORMATS: Record<Exclude<PageSizeFormat, "free">, {
70
+ width: number;
71
+ height: number;
72
+ }>;
73
+ /**
74
+ * The width and least height a `PageSize` asks for: the format's, with any
75
+ * free width or height over it; `sheetWidth` and the content's height when
76
+ * neither says.
77
+ */
78
+ export declare function resolvePageSize(size: PageSize | undefined, sheetWidth?: number): {
79
+ width: number;
80
+ height?: number;
81
+ };
82
+ export type PageLayoutProps = {
83
+ /** The page: what lies on the sheet. */
84
+ page?: ReactNode;
85
+ /**
86
+ * Whether there is a page on screen.
87
+ *
88
+ * When false, the panel's content lies on the sheet instead, and the page
89
+ * is kept mounted out of sight — whatever lives in it keeps running —
90
+ * rather than unmounted. Defaults to whether `page` was given.
91
+ */
92
+ hasPage?: boolean;
93
+ /** The side panel's content: a conversation, an inspector, a list. */
94
+ panel?: ReactNode;
95
+ /**
96
+ * The band above the page: a composer, a toolbar. Docked in a band at the
97
+ * sheet's width, or floating over the canvas, by `bandMode`. Nothing when
98
+ * omitted — an empty band is a strip of chrome that does nothing.
99
+ */
100
+ band?: ReactNode;
101
+ /** Chips under the band, above the sheet: openers, quick actions. */
102
+ chips?: ReactNode;
103
+ /** A strip above the canvas: a picker, a row of tabs. */
104
+ picker?: ReactNode;
105
+ /** Rendered after the canvas, positioned by itself: a transient notice. */
106
+ transient?: ReactNode;
107
+ /**
108
+ * What is happening in the page, pinned to the sheet's top edge while set.
109
+ * Omitted, the layout reads {@link pageLayoutActivity}.
110
+ */
111
+ activity?: string;
112
+ /**
113
+ * `docked` (the default): the band above the canvas, at the sheet's width.
114
+ * `floating`: the band's content is a draggable card at the top of the
115
+ * canvas, positioned by itself against this layout's root, over the chips
116
+ * and the sheet — which start at the top of the canvas as if there were
117
+ * no band; the card covers what it covers. `panel`: the band stands in the
118
+ * side panel, under the panel's content, with the chips above it; the
119
+ * page has no band over it at all, and the panel is on screen for as
120
+ * long as the band is in it, whatever the toggle says.
121
+ */
122
+ bandMode?: "docked" | "floating" | "panel";
123
+ /**
124
+ * Where the panel stands, when open. `docked` (the default): a column
125
+ * beside the page, narrowing it. `overlay`: over the page's right edge at
126
+ * full height, the page untouched under it. `popup`: a small window in
127
+ * the page's bottom-right corner.
128
+ */
129
+ panelMode?: "docked" | "overlay" | "popup";
130
+ /**
131
+ * Which side the panel stands on: `right` (the default), or `left`, where
132
+ * a navigation sidebar belongs. An overlay and a popup follow the same
133
+ * side, so a left panel slides in over the page's left edge.
134
+ */
135
+ panelSide?: "left" | "right";
136
+ /** The sheet's width; {@link PAGE_SHEET_WIDTH} by default. */
137
+ sheetWidth?: number;
138
+ /**
139
+ * The sheet's size: `free` (the default) at `sheetWidth`, or a paper —
140
+ * `letter`, `a4` — whose width the sheet takes and whose height it is at
141
+ * least; a `width` or `height` given here comes before either.
142
+ */
143
+ pageSize?: PageSize;
144
+ /** The panel's width when open; {@link PAGE_PANEL_WIDTH} by default. */
145
+ panelWidth?: number;
146
+ };
147
+ export declare function PageLayout({ page, hasPage, panel, band, chips, picker, transient, activity, bandMode, panelMode, panelSide, sheetWidth, pageSize, panelWidth, }: PageLayoutProps): JSX.Element;
148
+ export default PageLayout;
@@ -0,0 +1,326 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2023-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * The page layout: the work on a centred sheet, like a document.
8
+ *
9
+ * A quiet canvas fills the view. On it, one sheet — the page, at a reading
10
+ * width, with the margins and the shadow a page has — and a band docked
11
+ * directly above the canvas — or, when the host asks for it, floating over
12
+ * the canvas as a draggable card — like the toolbar of a document
13
+ * application, with chips directly under it — or standing in the side panel
14
+ * under its content, the page bare. With no page to show, the side panel's
15
+ * content is the page: the same sheet, holding it, and no side panel. While
16
+ * something works in the page, a small line at the top of the sheet says
17
+ * what it is doing — "Analyst is adding a cell…" — so the change is seen
18
+ * where it happens. The panel is a column on the right that opens when it
19
+ * is wanted — on either side by `panelSide`, and by `panelMode` a column, an
20
+ * overlay over the page's edge or a small window in its corner; closed, the
21
+ * page is all there is.
22
+ *
23
+ * The sheet is a reading column by default, as tall as what is on it; asked
24
+ * for a paper (`pageSize`: letter, A4, or a free width and height), it takes
25
+ * the paper's width and is at least the paper's height.
26
+ *
27
+ * The band holds one **mount point**, and that is what gives its content its
28
+ * width: the same `sheetWidth` column the sheet gets, centred the same way,
29
+ * inside the same canvas padding. So the band and the page below it are one
30
+ * column with one pair of edges, and they stay one when the panel opens and
31
+ * narrows the canvas — because the band lives inside the page column, not
32
+ * across the whole view. A floating card sizes itself, which is why it never
33
+ * lines up with anything. It hangs at the top of the canvas, over the chips
34
+ * and the sheet, which start where they would with no band at all: the flow
35
+ * keeps no room for the card. What it covers is a drag away, and a strip of
36
+ * canvas held clear for it was a blank band before the page — worse than a
37
+ * card over the first lines.
38
+ *
39
+ * Nothing here decides what the parts do. A host wires them; this arranges
40
+ * them. It grew up as the Loop's page layout, where the page is a notebook or
41
+ * a document, the band is the composer and the panel is the conversation;
42
+ * the names here are the general ones.
43
+ *
44
+ * @module reactor/page-layout/PageLayout
45
+ */
46
+ import { useEffect } from "react";
47
+ import { Box, Text } from "@primer/react";
48
+ import { useSignalValue } from "@datalayer/reactor/react";
49
+ import { pageLayoutActivity, pageLayoutPanelOpen, pageLayoutSheet, } from "./panelState";
50
+ /** A page reads best at about this width; wider and lines run too long. */
51
+ export const PAGE_SHEET_WIDTH = 920;
52
+ /** The side panel, when open. */
53
+ export const PAGE_PANEL_WIDTH = 400;
54
+ /** The papers, in CSS px (96 to the inch): US Letter 8.5 × 11 in, A4 210 × 297 mm. */
55
+ export const PAGE_SIZE_FORMATS = {
56
+ letter: { width: 816, height: 1056 },
57
+ a4: { width: 794, height: 1123 },
58
+ };
59
+ /**
60
+ * The width and least height a `PageSize` asks for: the format's, with any
61
+ * free width or height over it; `sheetWidth` and the content's height when
62
+ * neither says.
63
+ */
64
+ export function resolvePageSize(size, sheetWidth = PAGE_SHEET_WIDTH) {
65
+ const paper = size?.format && size.format !== "free"
66
+ ? PAGE_SIZE_FORMATS[size.format]
67
+ : undefined;
68
+ return {
69
+ width: size?.width ?? paper?.width ?? sheetWidth,
70
+ height: size?.height ?? paper?.height,
71
+ };
72
+ }
73
+ /**
74
+ * The canvas' own gutters, shared by the sheet and the band.
75
+ *
76
+ * One constant rather than two literals: the two are only aligned as long as
77
+ * they are equal, and a column that lines up by coincidence stops lining up
78
+ * the first time one of them is edited.
79
+ */
80
+ const CANVAS_PX = [2, 3, 4];
81
+ /** What is happening in the page, pinned to the sheet's top edge. */
82
+ function ActivityLine({ label }) {
83
+ return (_jsxs(Box, { "data-page-activity": "", role: "status", sx: {
84
+ position: "sticky",
85
+ top: 0,
86
+ zIndex: 2,
87
+ alignSelf: "flex-start",
88
+ display: "inline-flex",
89
+ alignItems: "center",
90
+ gap: 2,
91
+ px: 2,
92
+ py: "4px",
93
+ borderRadius: "999px",
94
+ border: "1px solid",
95
+ borderColor: "accent.muted",
96
+ bg: "accent.subtle",
97
+ color: "accent.fg",
98
+ fontSize: 0,
99
+ fontWeight: "semibold",
100
+ whiteSpace: "nowrap",
101
+ boxShadow: "0 4px 12px -6px rgba(0,0,0,0.3)",
102
+ }, children: [_jsx(Box, { as: "span", "aria-hidden": "true", sx: {
103
+ width: 7,
104
+ height: 7,
105
+ borderRadius: "50%",
106
+ bg: "accent.fg",
107
+ animation: "page-activity-dot 1.1s ease-in-out infinite",
108
+ "@keyframes page-activity-dot": {
109
+ "0%, 100%": { opacity: 1 },
110
+ "50%": { opacity: 0.3 },
111
+ },
112
+ } }), _jsx(Text, { children: label })] }));
113
+ }
114
+ export function PageLayout({ page, hasPage = page !== undefined && page !== null, panel, band, chips, picker, transient, activity, bandMode = "docked", panelMode = "docked", panelSide = "right", sheetWidth = PAGE_SHEET_WIDTH, pageSize, panelWidth = PAGE_PANEL_WIDTH, }) {
115
+ const bandInPanel = bandMode === "panel";
116
+ const hasBand = band !== undefined && band !== null;
117
+ // One width for the sheet, the band's mount and the chips: the column.
118
+ const { width: pageWidth, height: pageHeight } = resolvePageSize(pageSize, sheetWidth);
119
+ const panelOpen = useSignalValue(pageLayoutPanelOpen);
120
+ // A panel holding the band is on screen whatever the toggle says: closing
121
+ // it would take the composer with it.
122
+ const panelShown = panelOpen || (bandInPanel && hasBand);
123
+ const announced = useSignalValue(pageLayoutActivity);
124
+ const activityLabel = activity ?? announced;
125
+ /*
126
+ * No page — a host without one, or a view that hid it — and the panel's
127
+ * content is what lies on the sheet. Told to the parts that only make
128
+ * sense beside a page, through the shared signal.
129
+ */
130
+ const sheet = hasPage ? "page" : "panel";
131
+ useEffect(() => {
132
+ pageLayoutSheet.value = sheet;
133
+ }, [sheet]);
134
+ return (_jsxs(Box, { "data-page-layout": "", sx: {
135
+ // The positioned ancestor the out-of-sight parts park against.
136
+ position: "relative",
137
+ flex: "1 1 auto",
138
+ minHeight: 0,
139
+ display: "flex",
140
+ flexDirection: "column",
141
+ }, children: [picker, _jsxs(Box, { sx: {
142
+ flex: "1 1 auto",
143
+ minHeight: 0,
144
+ display: "flex",
145
+ // The canvas the sheet lies on: a step darker than the page, so the
146
+ // sheet reads as paper rather than as a bordered region of the
147
+ // same surface.
148
+ bg: "canvas.inset",
149
+ }, children: [_jsxs(Box, { sx: {
150
+ flex: "1 1 auto",
151
+ minWidth: 0,
152
+ minHeight: 0,
153
+ display: "flex",
154
+ flexDirection: "column",
155
+ }, children: [band && bandMode === "docked" ? (_jsx(Box, { "data-page-band": "", "data-page-prompt-dock": "", sx: {
156
+ flex: "0 0 auto",
157
+ display: "flex",
158
+ justifyContent: "center",
159
+ px: CANVAS_PX,
160
+ // The content brings its own padding, its own border and
161
+ // its own surface; the band only has to carry them the full
162
+ // width of the canvas and close with a rule.
163
+ bg: "canvas.subtle",
164
+ borderBottom: "1px solid",
165
+ borderColor: "border.default",
166
+ }, children: _jsx(Box, { sx: { width: "100%", maxWidth: pageWidth, minWidth: 0 }, children: band }) })) : null, _jsxs(Box, { "data-page-canvas": "", sx: {
167
+ flex: "1 1 auto",
168
+ minWidth: 0,
169
+ minHeight: 0,
170
+ // A page's sheet grows with its content and the canvas
171
+ // scrolls; the panel's sheet fills the canvas and scrolls
172
+ // inside, so a conversation keeps following its stream.
173
+ overflowY: hasPage ? "auto" : "hidden",
174
+ overflowX: "hidden",
175
+ display: "flex",
176
+ flexDirection: "column",
177
+ alignItems: "center",
178
+ // The same under a floating card: the chips and the sheet
179
+ // start at the top, and the card hangs over them.
180
+ pt: 3,
181
+ pb: 6,
182
+ px: CANVAS_PX,
183
+ }, children: [band && chips && !bandInPanel ? (_jsx(Box, { sx: {
184
+ width: "100%",
185
+ maxWidth: pageWidth,
186
+ mb: 3,
187
+ display: "flex",
188
+ justifyContent: "center",
189
+ "& > *": { justifyContent: "center" },
190
+ }, children: chips })) : null, activityLabel ? (_jsx(Box, { sx: {
191
+ width: "100%",
192
+ maxWidth: pageWidth,
193
+ display: "flex",
194
+ justifyContent: "flex-end",
195
+ mb: 2,
196
+ }, children: _jsx(ActivityLine, { label: activityLabel }) })) : null, _jsx(Box, { "data-page-sheet": "", sx: {
197
+ width: "100%",
198
+ maxWidth: pageWidth,
199
+ /*
200
+ A page's sheet is at least as tall as the canvas, and grows
201
+ past it with its content (`1 0 auto`: grow, never shrink).
202
+ It used to be `0 0 auto`, its content's height only — so
203
+ when the layout was given more room (full screen, a taller
204
+ frame, a resized window) the canvas grew and the page did
205
+ not, leaving an empty band of canvas under a short sheet.
206
+ Flex does the following: nothing is measured, so any
207
+ change in the layout's height reaches the page at once.
208
+ */
209
+ flex: hasPage ? "1 0 auto" : "1 1 auto",
210
+ bg: "canvas.default",
211
+ border: "1px solid",
212
+ borderColor: "border.default",
213
+ borderRadius: "10px",
214
+ boxShadow: "0 1px 2px rgba(0,0,0,0.06), 0 24px 48px -28px rgba(0,0,0,0.35)",
215
+ // The page's own margins. A page fills the sheet and reads
216
+ // against these, as text on paper does; a panel's content
217
+ // brings its own gutters, so it sits closer.
218
+ px: hasPage ? [3, 4, "56px"] : [2, 3, 4],
219
+ py: hasPage ? [3, 4, 4] : 2,
220
+ // What is on the sheet positions its hidden siblings
221
+ // against this.
222
+ position: "relative",
223
+ display: "flex",
224
+ // A paper's height when the size names one: the sheet is at
225
+ // least that tall, and grows past it with its content.
226
+ minHeight: hasPage ? (pageHeight ?? 480) : 0,
227
+ /*
228
+ The page fills the sheet, not the viewport: the sheet grows
229
+ with its content and the canvas scrolls, which is what makes
230
+ it a page.
231
+ */
232
+ "& > *": { flex: "1 1 auto", minWidth: 0 },
233
+ }, children: hasPage ? page : panel }), hasPage || !page ? null : (_jsx(Box, { "aria-hidden": "true", sx: {
234
+ position: "absolute",
235
+ inset: 0,
236
+ visibility: "hidden",
237
+ pointerEvents: "none",
238
+ zIndex: -1,
239
+ display: "flex",
240
+ }, children: page }))] })] }), !hasPage || !panel ? null : (_jsxs(Box, { "data-page-panel": panelShown ? "" : undefined, "data-page-panel-mode": panelMode, "aria-hidden": panelShown ? undefined : "true", sx: {
241
+ minWidth: 0,
242
+ minHeight: 0,
243
+ display: "flex",
244
+ flexDirection: "column",
245
+ bg: "canvas.default",
246
+ borderColor: "border.default",
247
+ ...(!panelShown
248
+ ? {
249
+ // Out of sight, and out of the way of the pointer, while
250
+ // whatever is in it goes on running. No `z-index` here:
251
+ // it would make a stacking context, and the composer —
252
+ // which lives in this column whether the panel is open or
253
+ // not — has to be able to float over the page from it.
254
+ position: "absolute",
255
+ inset: 0,
256
+ visibility: "hidden",
257
+ pointerEvents: "none",
258
+ overflow: "visible",
259
+ }
260
+ : panelMode === "docked"
261
+ ? {
262
+ flex: `0 0 ${panelWidth}px`,
263
+ maxWidth: "45%",
264
+ order: panelSide === "left" ? -1 : undefined,
265
+ [panelSide === "left" ? "borderRight" : "borderLeft"]: "1px solid",
266
+ // Over the canvas, and beside the band rather than under
267
+ // it: the band belongs to the page column.
268
+ zIndex: 1,
269
+ }
270
+ : panelMode === "overlay"
271
+ ? {
272
+ // Over the page's right edge, the page untouched
273
+ // under it; against the root, as the float is.
274
+ position: "absolute",
275
+ top: 0,
276
+ bottom: 0,
277
+ [panelSide]: 0,
278
+ width: panelWidth,
279
+ maxWidth: "85%",
280
+ [panelSide === "left" ? "borderRight" : "borderLeft"]: "1px solid",
281
+ boxShadow: "shadow.large",
282
+ zIndex: 5,
283
+ }
284
+ : {
285
+ // A window in the page's corner.
286
+ position: "absolute",
287
+ [panelSide]: 16,
288
+ bottom: 16,
289
+ width: panelWidth,
290
+ maxWidth: "calc(100% - 32px)",
291
+ height: "min(70%, 560px)",
292
+ border: "1px solid",
293
+ borderRadius: 2,
294
+ boxShadow: "shadow.large",
295
+ overflow: "hidden",
296
+ zIndex: 5,
297
+ }),
298
+ }, children: [_jsx(Box, { sx: {
299
+ flex: "1 1 auto",
300
+ minHeight: 0,
301
+ display: "flex",
302
+ flexDirection: "column",
303
+ "& > *": { flex: "1 1 auto", minHeight: 0 },
304
+ }, children: panel }), hasBand && bandMode !== "docked" ? (_jsxs(Box, { "data-page-panel-band": bandInPanel ? "" : undefined, sx: bandInPanel
305
+ ? {
306
+ flex: "0 0 auto",
307
+ borderTop: "1px solid",
308
+ borderColor: "border.default",
309
+ // Visible even while the column itself is hidden —
310
+ // which it is until something opens the panel.
311
+ visibility: "visible",
312
+ pointerEvents: "auto",
313
+ "& > *": { justifyContent: "center" },
314
+ }
315
+ : {
316
+ /* The card positions itself against this layout's
317
+ root; this only has to stay out of its way, and
318
+ stay visible when the column around it is not. */
319
+ position: "absolute",
320
+ inset: 0,
321
+ visibility: "visible",
322
+ pointerEvents: "none",
323
+ "& > *": { pointerEvents: "auto" },
324
+ }, children: [bandInPanel ? chips : null, band] })) : null] }))] }), transient] }));
325
+ }
326
+ export default PageLayout;
@@ -0,0 +1,59 @@
1
+ import type { ReactorReactOutput } from "@datalayer/reactor/react";
2
+ import type { Icon } from "@primer/octicons-react";
3
+ import type { PageSize } from "./PageLayout";
4
+ export declare const PAGE_LAYOUT_PLUGIN_NAME = "@datalayer/primer-page-layout";
5
+ /** The slots the plugin opens, by their default names. */
6
+ export declare const PageLayoutSlots: {
7
+ readonly page: "page";
8
+ readonly band: "page-band";
9
+ readonly panel: "page-panel";
10
+ readonly chips: "page-chips";
11
+ };
12
+ export type PageLayoutPluginConfig = {
13
+ /** The slot the layout renders into. */
14
+ slot: string;
15
+ /** Its place among that slot's components; lower first. */
16
+ order: number;
17
+ /** The slot the panel toggle renders into. Empty for no toggle. */
18
+ headerSlot: string;
19
+ /**
20
+ * The toggle's place among the header's components. Fifty by default:
21
+ * after a view selector, before an appearance menu at a hundred.
22
+ */
23
+ toggleOrder: number;
24
+ /** What the toggle's label calls the panel: "Show the conversation". */
25
+ panelName: string;
26
+ /** The toggle's glyph; the sidebar octicon by default. */
27
+ toggleIcon?: Icon;
28
+ /** The slot for what lies on the sheet. */
29
+ pageSlot: string;
30
+ /** The slot for the band above the page. */
31
+ bandSlot: string;
32
+ /** The slot for the side panel. */
33
+ panelSlot: string;
34
+ /** The slot for the chips under the band. */
35
+ chipsSlot: string;
36
+ /**
37
+ * Whether the band is docked at the sheet's width, floats over the canvas,
38
+ * or stands in the side panel under its content.
39
+ */
40
+ band: "docked" | "floating" | "panel";
41
+ /** Where the panel stands: beside the page, over its edge, or in its corner. */
42
+ panel: "docked" | "overlay" | "popup";
43
+ /** Which side it stands on. `right` by default; `left` for a nav sidebar. */
44
+ panelSide: "left" | "right";
45
+ /** Whether the panel is open when the page first draws. */
46
+ panelOpen: boolean;
47
+ /** The sheet's width, in pixels. */
48
+ sheetWidth?: number;
49
+ /**
50
+ * The sheet's size: free (the default) at `sheetWidth`, or a paper —
51
+ * `{ format: "letter" }`, `{ format: "a4" }` — with a free `width` or
52
+ * `height` over it.
53
+ */
54
+ pageSize?: PageSize;
55
+ /** The open panel's width, in pixels. */
56
+ panelWidth?: number;
57
+ };
58
+ export declare const PageLayoutPlugin: import("@datalayer/reactor").ReactorPlugin<PageLayoutPluginConfig, unknown, ReactorReactOutput>;
59
+ export default PageLayoutPlugin;