@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.
- package/README.md +14 -0
- package/lib/components/appearance/AppearanceMenuWithStore.d.ts +26 -0
- package/lib/components/appearance/AppearanceMenuWithStore.js +21 -0
- package/lib/components/appearance/CollaboratorPalette.d.ts +65 -0
- package/lib/components/appearance/CollaboratorPalette.js +160 -0
- package/lib/components/appearance/index.d.ts +2 -0
- package/lib/components/appearance/index.js +2 -0
- package/lib/components/box/Box.d.ts +17 -5
- package/lib/components/box/Box.js +4 -7
- package/lib/components/box/sx.js +1 -2
- package/lib/components/calendar-picker/CalendarPicker.d.ts +35 -0
- package/lib/components/calendar-picker/CalendarPicker.js +146 -0
- package/lib/components/calendar-picker/index.d.ts +1 -0
- package/lib/components/calendar-picker/index.js +5 -0
- package/lib/components/card/Card.d.ts +6 -1
- package/lib/components/card/Card.js +1 -1
- package/lib/components/closeable-flash/CloseableFlash.d.ts +23 -1
- package/lib/components/closeable-flash/CloseableFlash.js +52 -4
- package/lib/components/color/ColorSwatch.d.ts +1 -1
- package/lib/components/color-picker/ColorPicker.d.ts +97 -0
- package/lib/components/color-picker/ColorPicker.js +358 -0
- package/lib/components/color-picker/index.d.ts +1 -0
- package/lib/components/color-picker/index.js +5 -0
- package/lib/components/content-loader/ContentLoader.d.ts +1 -1
- package/lib/components/content-loader/ContentLoader.js +4 -0
- package/lib/components/content-loader/ContentLoader.stories.d.ts +1 -1
- package/lib/components/date-picker/DatePicker.d.ts +81 -0
- package/lib/components/date-picker/DatePicker.js +204 -0
- package/lib/components/date-picker/TimeColumns.d.ts +23 -0
- package/lib/components/date-picker/TimeColumns.js +88 -0
- package/lib/components/date-picker/index.d.ts +1 -0
- package/lib/components/date-picker/index.js +5 -0
- package/lib/components/icons/CircleIcon.d.ts +1 -1
- package/lib/components/icons/CircleIcon.js +4 -0
- package/lib/components/icons/CircleIcon.stories.d.ts +1 -1
- package/lib/components/index.d.ts +5 -0
- package/lib/components/index.js +9 -0
- package/lib/components/logo/DatalayerText.d.ts +1 -1
- package/lib/components/logo/index.d.ts +0 -1
- package/lib/components/logo/index.js +4 -1
- package/lib/components/side-overlay/SideOverlay.d.ts +5 -5
- package/lib/components/side-overlay/SideOverlay.js +4 -0
- package/lib/components/side-overlay/SideOverlay.stories.d.ts +1 -1
- package/lib/components/side-overlay/SideOverlay.stories.js +4 -0
- package/lib/components/slider/Slider.d.ts +1 -1
- package/lib/components/slider/Slider.js +4 -0
- package/lib/components/slider/Slider.stories.d.ts +1 -1
- package/lib/components/slider/Slider.stories.js +4 -0
- package/lib/components/sliding-panel/SlidingPanel.d.ts +1 -1
- package/lib/components/sliding-panel/SlidingPanel.js +17 -1
- package/lib/components/sliding-panel/SlidingPanel.stories.d.ts +1 -1
- package/lib/components/sliding-panel/SlidingPanel.stories.js +4 -0
- package/lib/components/toolbar/Toolbar.d.ts +2 -2
- package/lib/components/toolbar/Toolbar.js +0 -0
- package/lib/components/toolbar/ToolbarButton.d.ts +3 -1
- package/lib/components/toolbar/ToolbarButton.js +54 -22
- package/lib/components/toolbar/ToolbarDivider.d.ts +1 -1
- package/lib/components/toolbar/ToolbarDropdown.d.ts +3 -3
- package/lib/components/toolbar/ToolbarDropdown.js +66 -35
- package/lib/components/toolbar/ToolbarRenderer.d.ts +20 -3
- package/lib/components/toolbar/ToolbarRenderer.js +16 -16
- package/lib/components/toolbar/types.d.ts +8 -8
- package/lib/index.js +4 -0
- package/lib/reactor/AppearancePlugin.d.ts +31 -0
- package/lib/reactor/AppearancePlugin.js +63 -0
- package/lib/reactor/ThemePlugin.d.ts +5 -0
- package/lib/reactor/ThemePlugin.js +98 -0
- package/lib/reactor/index.d.ts +30 -0
- package/lib/reactor/index.js +34 -0
- package/lib/reactor/page-layout/PageLayout.d.ts +148 -0
- package/lib/reactor/page-layout/PageLayout.js +326 -0
- package/lib/reactor/page-layout/PageLayoutPlugin.d.ts +59 -0
- package/lib/reactor/page-layout/PageLayoutPlugin.js +105 -0
- package/lib/reactor/page-layout/PagePanelToggle.d.ts +21 -0
- package/lib/reactor/page-layout/PagePanelToggle.js +29 -0
- package/lib/reactor/page-layout/SlotPageLayout.d.ts +36 -0
- package/lib/reactor/page-layout/SlotPageLayout.js +25 -0
- package/lib/reactor/page-layout/panelState.d.ts +28 -0
- package/lib/reactor/page-layout/panelState.js +49 -0
- package/lib/story-helpers.d.ts +2 -2
- package/lib/theme/DatalayerBrandThemeProvider.d.ts +1 -1
- package/lib/theme/DatalayerThemeProvider.d.ts +5 -5
- package/lib/theme/DatalayerThemeProvider.js +119 -58
- package/lib/theme/css/createThemeCSSVars.d.ts +1 -0
- package/lib/theme/css/createThemeCSSVars.js +9 -0
- package/lib/theme/fontStacks.d.ts +2 -0
- package/lib/theme/fontStacks.js +6 -0
- package/lib/theme/index.d.ts +2 -0
- package/lib/theme/index.js +2 -0
- package/lib/theme/palettes/ColorPalette.d.ts +3 -3
- package/lib/theme/palettes/ColorPalette.js +1 -2
- package/lib/theme/palettes/index.js +1 -2
- package/lib/theme/portableTheme.d.ts +32 -0
- package/lib/theme/portableTheme.js +45 -0
- package/lib/utils/Helper.d.ts +2 -2
- package/lib/utils/Portals.js +3 -1
- package/lib/utils/Styles.d.ts +1 -1
- package/package.json +21 -14
- package/lib/components/logo/DI.d.ts +0 -19
- 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;
|