@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,81 @@
1
+ import { type CalendarPickerProps } from "../calendar-picker/CalendarPicker";
2
+ /** `YYYY-MM-DD`, the one format that means the same thing everywhere. */
3
+ export declare function formatISODate(date: Date): string;
4
+ /** `YYYY-MM-DD HH:mm`, the same date with the minute it holds. */
5
+ export declare function formatISODateTime(date: Date): string;
6
+ /** `HH:mm` of a date, which is what a `type="time"` field holds. */
7
+ export declare function formatTime(date: Date): string;
8
+ /**
9
+ * Reads `YYYY-MM-DD` as a local date.
10
+ *
11
+ * `new Date('2026-01-31')` is UTC midnight, which is the day before in the
12
+ * Americas; the parts are given to the constructor separately so the date
13
+ * means the same day in every zone.
14
+ */
15
+ export declare function parseISODate(text: string): Date | null;
16
+ /**
17
+ * Reads `YYYY-MM-DD HH:mm` — or the same with a `T`, or with seconds, or with
18
+ * no time at all — as a local date.
19
+ *
20
+ * Lenient on the way in and strict on the way out: somebody typing a date into
21
+ * a field has a shape in their fingers, and rejecting `2026-01-31T09:00`
22
+ * because the separator is a `T` teaches them nothing. What the field *writes*
23
+ * is always one shape.
24
+ */
25
+ export declare function parseISODateTime(text: string): Date | null;
26
+ export interface DatePickerProps extends Pick<CalendarPickerProps, "min" | "max" | "isDisabled" | "weekStartsOn" | "locale"> {
27
+ /** The chosen date, or nothing chosen. */
28
+ value?: Date | null;
29
+ /** Told when a date is picked or typed. `null` when the field is emptied. */
30
+ onChange?: (date: Date | null) => void;
31
+ /** What the empty field says. */
32
+ placeholder?: string;
33
+ /** The field's accessible name. */
34
+ "aria-label"?: string;
35
+ /** Nothing can be picked or typed. */
36
+ disabled?: boolean;
37
+ /** The field's width. Primer's `medium` by default. */
38
+ size?: "small" | "medium" | "large";
39
+ /**
40
+ * Marks the field as holding something invalid, or as settled. Primer's own
41
+ * two, spelled out rather than imported from a path inside the package that
42
+ * not every consumer's module resolution can see.
43
+ */
44
+ validationStatus?: "error" | "success";
45
+ /**
46
+ * How a chosen date is written into the field. ISO `YYYY-MM-DD` by
47
+ * default, which is also the only shape the field parses back.
48
+ */
49
+ format?: (date: Date) => string;
50
+ /** An id for the field, for a label that names it from outside. */
51
+ id?: string;
52
+ /**
53
+ * Whether the field holds a time as well as a day.
54
+ *
55
+ * With it, the field reads and writes `YYYY-MM-DD HH:mm`, and the overlay
56
+ * carries a time beneath the calendar. Without it — the default — nothing
57
+ * about the field changes, because most dates are days.
58
+ */
59
+ withTime?: boolean;
60
+ /**
61
+ * Minutes between the options the minute column offers. Default: five.
62
+ *
63
+ * A minute the step does not land on — one read back from a stored value —
64
+ * is still offered, so choosing an hour never quietly moves it.
65
+ */
66
+ timeStep?: number;
67
+ /**
68
+ * The time a day picked from the calendar takes when the field held nothing
69
+ * yet. `"00:00"` by default — a window that starts "on the 3rd" starts at
70
+ * the beginning of the 3rd.
71
+ */
72
+ defaultTime?: string;
73
+ }
74
+ /**
75
+ * A date field with a calendar behind it.
76
+ *
77
+ * Controlled: `value` is the caller's, and every pick and every parsed entry
78
+ * arrives through `onChange`.
79
+ */
80
+ export declare function DatePicker({ value, onChange, placeholder, "aria-label": ariaLabel, disabled, size, validationStatus, format, id, min, max, isDisabled, weekStartsOn, locale, withTime, timeStep, defaultTime, }: DatePickerProps): import("react").JSX.Element;
81
+ export default DatePicker;
@@ -0,0 +1,204 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef, useState } from "react";
3
+ import { AnchoredOverlay, Box, Button, TextInput } from "@primer/react";
4
+ import { CalendarIcon } from "@primer/octicons-react";
5
+ import { CalendarPicker } from "../calendar-picker/CalendarPicker";
6
+ import { TimeColumns } from "./TimeColumns";
7
+ /** `YYYY-MM-DD`, the one format that means the same thing everywhere. */
8
+ export function formatISODate(date) {
9
+ const month = `${date.getMonth() + 1}`.padStart(2, "0");
10
+ const day = `${date.getDate()}`.padStart(2, "0");
11
+ return `${date.getFullYear()}-${month}-${day}`;
12
+ }
13
+ /** `YYYY-MM-DD HH:mm`, the same date with the minute it holds. */
14
+ export function formatISODateTime(date) {
15
+ const hours = `${date.getHours()}`.padStart(2, "0");
16
+ const minutes = `${date.getMinutes()}`.padStart(2, "0");
17
+ return `${formatISODate(date)} ${hours}:${minutes}`;
18
+ }
19
+ /** `HH:mm` of a date, which is what a `type="time"` field holds. */
20
+ export function formatTime(date) {
21
+ const hours = `${date.getHours()}`.padStart(2, "0");
22
+ const minutes = `${date.getMinutes()}`.padStart(2, "0");
23
+ return `${hours}:${minutes}`;
24
+ }
25
+ /**
26
+ * Reads `YYYY-MM-DD` as a local date.
27
+ *
28
+ * `new Date('2026-01-31')` is UTC midnight, which is the day before in the
29
+ * Americas; the parts are given to the constructor separately so the date
30
+ * means the same day in every zone.
31
+ */
32
+ export function parseISODate(text) {
33
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(text.trim());
34
+ if (!match) {
35
+ return null;
36
+ }
37
+ const [, year, month, day] = match;
38
+ const date = new Date(Number(year), Number(month) - 1, Number(day));
39
+ return isExactly(date, Number(year), Number(month), Number(day)) ? date : null;
40
+ }
41
+ /**
42
+ * Whether `date` holds exactly the fields it was built from.
43
+ *
44
+ * The `Date` constructor rolls an out-of-range field into the next one —
45
+ * `2026-02-31` becomes the 3rd of March, `24:00` the next day — instead of
46
+ * failing, so a typo would be committed as some other moment. A field that
47
+ * does not come back as written was not a real one.
48
+ */
49
+ function isExactly(date, year, month, day, hours = 0, minutes = 0, seconds = 0) {
50
+ return (!Number.isNaN(date.getTime()) &&
51
+ date.getFullYear() === year &&
52
+ date.getMonth() === month - 1 &&
53
+ date.getDate() === day &&
54
+ date.getHours() === hours &&
55
+ date.getMinutes() === minutes &&
56
+ date.getSeconds() === seconds);
57
+ }
58
+ /**
59
+ * Reads `YYYY-MM-DD HH:mm` — or the same with a `T`, or with seconds, or with
60
+ * no time at all — as a local date.
61
+ *
62
+ * Lenient on the way in and strict on the way out: somebody typing a date into
63
+ * a field has a shape in their fingers, and rejecting `2026-01-31T09:00`
64
+ * because the separator is a `T` teaches them nothing. What the field *writes*
65
+ * is always one shape.
66
+ */
67
+ export function parseISODateTime(text) {
68
+ const match = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{1,2}):(\d{2})(?::(\d{2}))?)?$/.exec(text.trim());
69
+ if (!match) {
70
+ return null;
71
+ }
72
+ const [, year, month, day, hours, minutes, seconds] = match;
73
+ const date = new Date(Number(year), Number(month) - 1, Number(day), Number(hours ?? 0), Number(minutes ?? 0), Number(seconds ?? 0));
74
+ return isExactly(date, Number(year), Number(month), Number(day), Number(hours ?? 0), Number(minutes ?? 0), Number(seconds ?? 0))
75
+ ? date
76
+ : null;
77
+ }
78
+ /**
79
+ * A date field with a calendar behind it.
80
+ *
81
+ * Controlled: `value` is the caller's, and every pick and every parsed entry
82
+ * arrives through `onChange`.
83
+ */
84
+ export function DatePicker({ value = null, onChange, placeholder, "aria-label": ariaLabel = "Date", disabled = false, size = "medium", validationStatus, format, id, min, max, isDisabled, weekStartsOn, locale, withTime = false, timeStep = 5, defaultTime = "00:00", }) {
85
+ // The two shapes, chosen once: a caller that passed a `format` keeps it,
86
+ // and a caller that asked for a time gets the one that carries a time.
87
+ const writeDate = format ?? (withTime ? formatISODateTime : formatISODate);
88
+ const readDate = withTime ? parseISODateTime : parseISODate;
89
+ const emptyText = placeholder ?? (withTime ? "YYYY-MM-DD HH:mm" : "YYYY-MM-DD");
90
+ const [open, setOpen] = useState(false);
91
+ const [text, setText] = useState(() => (value ? writeDate(value) : ""));
92
+ const anchorRef = useRef(null);
93
+ // The field follows the value, except while it is being typed into: a
94
+ // controlled rewrite mid-word moves the caret to the end.
95
+ const typing = useRef(false);
96
+ useEffect(() => {
97
+ if (!typing.current) {
98
+ setText(value ? writeDate(value) : "");
99
+ }
100
+ }, [value, writeDate]);
101
+ const commit = (next) => {
102
+ typing.current = false;
103
+ const trimmed = next.trim();
104
+ if (trimmed === "") {
105
+ onChange?.(null);
106
+ return;
107
+ }
108
+ const parsed = readDate(trimmed);
109
+ if (parsed) {
110
+ onChange?.(parsed);
111
+ setText(writeDate(parsed));
112
+ }
113
+ else {
114
+ // Unreadable: put back what is actually held, rather than keeping a
115
+ // date on screen that the caller never received.
116
+ setText(value ? writeDate(value) : "");
117
+ }
118
+ };
119
+ /** The day picked, wearing the time the field already held. */
120
+ const withHeldTime = (day) => {
121
+ const [hours, minutes] = (value ? formatTime(value) : defaultTime)
122
+ .split(":")
123
+ .map(Number);
124
+ const next = new Date(day);
125
+ next.setHours(hours || 0, minutes || 0, 0, 0);
126
+ return next;
127
+ };
128
+ /** The time the field holds right now, or the one a new pick starts from. */
129
+ const held = (() => {
130
+ const [hours, minutes] = (value ? formatTime(value) : defaultTime)
131
+ .split(":")
132
+ .map(Number);
133
+ return { hour: hours || 0, minute: minutes || 0 };
134
+ })();
135
+ /** The day the field holds, at the time just chosen. */
136
+ const atTime = (hour, minute) => {
137
+ const next = new Date(value ?? new Date());
138
+ next.setHours(hour, minute, 0, 0);
139
+ return next;
140
+ };
141
+ const calendar = useMemo(() => (_jsxs(Box, { sx: { p: 3 }, children: [_jsx(CalendarPicker, { value: value, defaultMonth: value ?? undefined, onChange: (date) => {
142
+ // A day picked while a time is held keeps that time: somebody
143
+ // moving a window from the 3rd to the 4th did not mean to move
144
+ // it to midnight.
145
+ const next = withTime ? withHeldTime(date) : date;
146
+ onChange?.(next);
147
+ setText(writeDate(next));
148
+ if (!withTime) {
149
+ setOpen(false);
150
+ }
151
+ }, min: min, max: max, isDisabled: isDisabled, weekStartsOn: weekStartsOn, locale: locale, "aria-label": `${ariaLabel}, choose a day` }), withTime && (_jsxs(Box, { sx: {
152
+ mt: 3,
153
+ pt: 3,
154
+ borderTop: "1px solid",
155
+ borderColor: "border.default",
156
+ }, children: [_jsx(TimeColumns, { hour: held.hour, minute: held.minute, step: timeStep, disabled: disabled, labelPrefix: ariaLabel, onChange: (hour, minute) => {
157
+ const next = atTime(hour, minute);
158
+ onChange?.(next);
159
+ setText(writeDate(next));
160
+ } }), _jsx(Box, { sx: {
161
+ display: "flex",
162
+ alignItems: "center",
163
+ justifyContent: "flex-end",
164
+ gap: 2,
165
+ mt: 2,
166
+ }, children: _jsx(Button, { size: "small", variant: "primary", onClick: () => setOpen(false), children: "Done" }) })] }))] })),
167
+ // `withHeldTime`, `held` and `atTime` all close over `value` and
168
+ // `defaultTime`, both of which are named here.
169
+ [
170
+ ariaLabel,
171
+ defaultTime,
172
+ disabled,
173
+ isDisabled,
174
+ locale,
175
+ max,
176
+ min,
177
+ onChange,
178
+ size,
179
+ timeStep,
180
+ value,
181
+ weekStartsOn,
182
+ withTime,
183
+ writeDate,
184
+ ]);
185
+ return (_jsxs(Box, { ref: anchorRef, sx: { display: "inline-block" }, children: [_jsx(AnchoredOverlay, { open: open, onOpen: () => !disabled && setOpen(true), onClose: () => setOpen(false),
186
+ // The field's own box is the anchor; `useRef` types its current as
187
+ // nullable, which is not how the overlay declares what it takes.
188
+ anchorRef: anchorRef,
189
+ // The field is the anchor; the button inside it does the opening.
190
+ renderAnchor: null, overlayProps: { role: "dialog", "aria-label": `${ariaLabel} calendar` }, children: calendar }), _jsx(TextInput, { id: id, "aria-label": ariaLabel, "aria-haspopup": "dialog", "aria-expanded": open, value: text, placeholder: emptyText, disabled: disabled, size: size, validationStatus: validationStatus, onChange: (event) => {
191
+ typing.current = true;
192
+ setText(event.target.value);
193
+ }, onBlur: (event) => commit(event.target.value), onKeyDown: (event) => {
194
+ if (event.key === "Enter") {
195
+ commit(event.target.value);
196
+ setOpen(false);
197
+ }
198
+ if (event.key === "ArrowDown" && !open) {
199
+ event.preventDefault();
200
+ setOpen(true);
201
+ }
202
+ }, trailingAction: _jsx(TextInput.Action, { onClick: () => !disabled && setOpen((was) => !was), icon: CalendarIcon, "aria-label": open ? "Close the calendar" : "Open the calendar", disabled: disabled }) })] }));
203
+ }
204
+ export default DatePicker;
@@ -0,0 +1,23 @@
1
+ /** `7` → `07`. Two digits, always, because a column of ragged numbers reads badly. */
2
+ export declare const pad: (value: number) => string;
3
+ export interface TimeColumnsProps {
4
+ /** The hour shown as chosen, 0–23. */
5
+ hour: number;
6
+ /** The minute shown as chosen, 0–59. */
7
+ minute: number;
8
+ /** Minutes between the options offered. 1 lists all sixty. */
9
+ step?: number;
10
+ /** Told when either column is used, with the whole time. */
11
+ onChange: (hour: number, minute: number) => void;
12
+ disabled?: boolean;
13
+ /** Names the columns for a screen reader: "Start date, hour". Not drawn —
14
+ * on screen the two columns are headed "Hour" and "Minute", and a whole
15
+ * field label repeated over them reads as noise. */
16
+ labelPrefix?: string;
17
+ }
18
+ /**
19
+ * The two columns, side by side, with the time they currently mean between
20
+ * them.
21
+ */
22
+ export declare function TimeColumns({ hour, minute, step, onChange, disabled, labelPrefix, }: TimeColumnsProps): import("react").JSX.Element;
23
+ export default TimeColumns;
@@ -0,0 +1,88 @@
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
+ * Hours and minutes, as two columns the reader scrolls.
8
+ *
9
+ * The browser's own `type="time"` control draws its dropdown itself: unstyled,
10
+ * unaware of the page's theme, and — because it is the platform's own popup —
11
+ * *over* whatever is already open. Beside a calendar that means the time
12
+ * dropdown covers the days, which is the one thing somebody choosing a moment
13
+ * needs to keep seeing.
14
+ *
15
+ * So the columns are drawn here, in Primer, inside the overlay that already
16
+ * exists. Not a second overlay: a menu anchored inside an `AnchoredOverlay`
17
+ * portals out of it, and the outer overlay's own click-away closes everything
18
+ * the moment the pointer lands in the inner one.
19
+ *
20
+ * @module components/date-picker/TimeColumns
21
+ */
22
+ import { useEffect, useRef } from "react";
23
+ import { ActionList, Box, Text } from "@primer/react";
24
+ /** `7` → `07`. Two digits, always, because a column of ragged numbers reads badly. */
25
+ export const pad = (value) => `${value}`.padStart(2, "0");
26
+ const HOURS = Array.from({ length: 24 }, (_, index) => index);
27
+ function Column({ label, ariaLabel, values, selected, onPick, disabled, }) {
28
+ const scroller = useRef(null);
29
+ const chosen = useRef(null);
30
+ // The chosen value, in view, the moment the column is drawn. A column that
31
+ // opens at midnight when it holds 17:00 makes the reader scroll to find out
32
+ // what it is already set to.
33
+ useEffect(() => {
34
+ const item = chosen.current;
35
+ if (!item) {
36
+ return;
37
+ }
38
+ // After layout, and against the scroller rather than whatever the list
39
+ // happens to be positioned against.
40
+ const frame = requestAnimationFrame(() => {
41
+ item.scrollIntoView({ block: "center", inline: "nearest" });
42
+ });
43
+ return () => cancelAnimationFrame(frame);
44
+ }, [selected]);
45
+ return (_jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsx(Text, { sx: {
46
+ display: "block",
47
+ fontSize: 0,
48
+ color: "fg.muted",
49
+ mb: 1,
50
+ textAlign: "center",
51
+ }, children: label }), _jsx(Box, { ref: scroller, sx: {
52
+ height: 132,
53
+ overflowY: "auto",
54
+ border: "1px solid",
55
+ borderColor: "border.default",
56
+ borderRadius: 2,
57
+ bg: "canvas.default",
58
+ // The scrollbar is the only hint that there is more below, so it
59
+ // stays: a hidden one turns the column into a list of six hours.
60
+ scrollbarWidth: "thin",
61
+ }, children: _jsx(ActionList, { selectionVariant: "single", "aria-label": ariaLabel, sx: { p: 1 }, children: values.map((value) => (_jsx(ActionList.Item, { ref: value === selected ? chosen : undefined, selected: value === selected, disabled: disabled, onSelect: () => onPick(value), sx: { justifyContent: "center", fontVariantNumeric: "tabular-nums" }, children: pad(value) }, value))) }) })] }));
62
+ }
63
+ /**
64
+ * The two columns, side by side, with the time they currently mean between
65
+ * them.
66
+ */
67
+ export function TimeColumns({ hour, minute, step = 5, onChange, disabled, labelPrefix, }) {
68
+ const every = Math.min(60, Math.max(1, Math.floor(step) || 1));
69
+ const minutes = Array.from({ length: Math.ceil(60 / every) }, (_, index) => index * every);
70
+ // A minute the step does not land on — 17:07 read back from a stored value —
71
+ // still has to be selectable, or choosing an hour would silently move it.
72
+ const shown = minutes.includes(minute)
73
+ ? minutes
74
+ : [...minutes, minute].sort((left, right) => left - right);
75
+ return (_jsxs(Box, { children: [_jsxs(Box, { sx: {
76
+ display: "flex",
77
+ alignItems: "baseline",
78
+ justifyContent: "space-between",
79
+ gap: 2,
80
+ mb: 1,
81
+ }, children: [_jsx(Text, { sx: { fontSize: 0, color: "fg.muted" }, children: "Time" }), _jsxs(Text, { sx: {
82
+ fontFamily: "mono",
83
+ fontSize: 1,
84
+ fontWeight: "bold",
85
+ fontVariantNumeric: "tabular-nums",
86
+ }, children: [pad(hour), ":", pad(minute)] })] }), _jsxs(Box, { sx: { display: "flex", gap: 2 }, children: [_jsx(Column, { label: "Hour", ariaLabel: labelPrefix ? `${labelPrefix}, hour` : "Hour", values: HOURS, selected: hour, disabled: disabled, onPick: (next) => onChange(next, minute) }), _jsx(Column, { label: "Minute", ariaLabel: labelPrefix ? `${labelPrefix}, minute` : "Minute", values: shown, selected: minute, disabled: disabled, onPick: (next) => onChange(hour, next) })] })] }));
87
+ }
88
+ export default TimeColumns;
@@ -0,0 +1 @@
1
+ export * from "./DatePicker";
@@ -0,0 +1,5 @@
1
+ /*
2
+ * Copyright (c) 2023-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ export * from "./DatePicker";
@@ -2,5 +2,5 @@ export type CircleIconProps = {
2
2
  color?: string;
3
3
  variant?: 'fg' | 'default' | 'muted' | 'onEmphasis' | 'subtle';
4
4
  };
5
- export declare const CircleIcon: ({ color, variant, }: CircleIconProps) => import("react/jsx-runtime").JSX.Element;
5
+ export declare const CircleIcon: ({ color, variant, }: CircleIconProps) => import("react").JSX.Element;
6
6
  export default CircleIcon;
@@ -1,4 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2021-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
2
6
  import { useTheme } from '@primer/react';
3
7
  import { CircleCurrentColorIcon } from '@datalayer/icons-react';
4
8
  export const CircleIcon = ({ color = 'white', variant = 'fg', }) => {
@@ -2,7 +2,7 @@ import type { StoryObj } from '@storybook/react';
2
2
  import { CircleIconProps } from '../..';
3
3
  declare const meta: {
4
4
  title: string;
5
- component: ({ color, variant, }: CircleIconProps) => import("react/jsx-runtime").JSX.Element;
5
+ component: ({ color, variant, }: CircleIconProps) => import("react").JSX.Element;
6
6
  tags: string[];
7
7
  parameters: {
8
8
  layout: string;
@@ -1,13 +1,18 @@
1
1
  export * from "./appearance/AppearanceControls";
2
2
  export * from "./appearance/AppearanceControlsWithStore";
3
3
  export * from "./appearance/AppearanceMenu";
4
+ export * from "./appearance/AppearanceMenuWithStore";
5
+ export * from "./appearance/CollaboratorPalette";
4
6
  export * from "./appearance/ColorModeCircle";
5
7
  export * from "./appearance/ThemePreviewCard";
6
8
  export * from "./appearance/ThemeCircle";
7
9
  export * from "./box/Box";
10
+ export * from "./calendar-picker";
8
11
  export * from "./card/Card";
9
12
  export * from "./color";
13
+ export * from "./color-picker";
10
14
  export * from "./content-loader/ContentLoader";
15
+ export * from "./date-picker";
11
16
  export * from "./closeable-flash/CloseableFlash";
12
17
  export * from "./icons/CircleIcon";
13
18
  export * from "./side-overlay/SideOverlay";
@@ -1,13 +1,22 @@
1
+ /*
2
+ * Copyright (c) 2021-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
1
5
  export * from "./appearance/AppearanceControls";
2
6
  export * from "./appearance/AppearanceControlsWithStore";
3
7
  export * from "./appearance/AppearanceMenu";
8
+ export * from "./appearance/AppearanceMenuWithStore";
9
+ export * from "./appearance/CollaboratorPalette";
4
10
  export * from "./appearance/ColorModeCircle";
5
11
  export * from "./appearance/ThemePreviewCard";
6
12
  export * from "./appearance/ThemeCircle";
7
13
  export * from "./box/Box";
14
+ export * from "./calendar-picker";
8
15
  export * from "./card/Card";
9
16
  export * from "./color";
17
+ export * from "./color-picker";
10
18
  export * from "./content-loader/ContentLoader";
19
+ export * from "./date-picker";
11
20
  export * from "./closeable-flash/CloseableFlash";
12
21
  export * from "./icons/CircleIcon";
13
22
  export * from "./side-overlay/SideOverlay";
@@ -18,5 +18,5 @@ export interface DatalayerTextProps extends Omit<SVGProps<SVGGElement>, 'ref'> {
18
18
  * - "DATA" = secondaryColor (gray in the original)
19
19
  * - "LAYER" = primaryColor (green accent in the original)
20
20
  */
21
- export declare const DatalayerText: ({ primaryColor, secondaryColor, sizeMultiplier, x, y, ...groupProps }: DatalayerTextProps) => JSX.Element;
21
+ export declare const DatalayerText: ({ primaryColor, secondaryColor, sizeMultiplier, x, y, ...groupProps }: DatalayerTextProps) => React.JSX.Element;
22
22
  export default DatalayerText;
@@ -1,4 +1,3 @@
1
- export * from "./DI";
2
1
  export * from "./DatalayerLogo";
3
2
  export * from "./DatalayerLogoText";
4
3
  export * from "./DatalayerText";
@@ -1,4 +1,7 @@
1
- export * from "./DI";
1
+ /*
2
+ * Copyright (c) 2021-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
2
5
  export * from "./DatalayerLogo";
3
6
  export * from "./DatalayerLogoText";
4
7
  export * from "./DatalayerText";
@@ -1,13 +1,13 @@
1
1
  export interface SideOverlayProps {
2
- closeButtonRef?: React.RefObject<HTMLButtonElement>;
3
- content: JSX.Element;
2
+ closeButtonRef?: React.RefObject<HTMLButtonElement | null>;
3
+ content: React.JSX.Element;
4
4
  direction?: 'left' | 'right';
5
- headingRef?: React.RefObject<HTMLHeadingElement>;
5
+ headingRef?: React.RefObject<HTMLElement | null>;
6
6
  isOpen?: boolean;
7
- openButtonRef?: React.RefObject<HTMLButtonElement>;
7
+ openButtonRef?: React.RefObject<HTMLButtonElement | null>;
8
8
  setIsOpen?: React.Dispatch<React.SetStateAction<boolean>>;
9
9
  width?: number | string;
10
10
  zIndex?: number;
11
11
  }
12
- export declare const SideOverlay: ({ width, direction, zIndex, ...rest }: SideOverlayProps) => import("react/jsx-runtime").JSX.Element;
12
+ export declare const SideOverlay: ({ width, direction, zIndex, ...rest }: SideOverlayProps) => import("react").JSX.Element;
13
13
  export default SideOverlay;
@@ -1,4 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2021-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
2
6
  import { useEffect, useState } from 'react';
3
7
  import { IconButton, Overlay as PrimerOverlay, ThemeProvider, useTheme } from "@primer/react";
4
8
  import { XIcon } from "@primer/octicons-react";
@@ -2,7 +2,7 @@ import type { StoryObj } from '@storybook/react';
2
2
  import { SideOverlayProps } from '../..';
3
3
  declare const meta: {
4
4
  title: string;
5
- component: ({ width, direction, zIndex, ...rest }: SideOverlayProps) => import("react/jsx-runtime").JSX.Element;
5
+ component: ({ width, direction, zIndex, ...rest }: SideOverlayProps) => import("react").JSX.Element;
6
6
  tags: string[];
7
7
  parameters: {
8
8
  layout: string;
@@ -1,4 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2021-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
2
6
  import { useState, useRef } from "react";
3
7
  import { Heading, Text, Box, Button } from "@primer/react";
4
8
  import { SideOverlay } from '../..';
@@ -19,5 +19,5 @@ type ConditionalProps = {
19
19
  step?: number;
20
20
  };
21
21
  export type SliderProps = CommonProps & ConditionalProps;
22
- export declare const Slider: ({ name, id, min, max, value, label, step, markers, displayValue, disabled, orientation, width, onChange, }: SliderProps) => import("react/jsx-runtime").JSX.Element;
22
+ export declare const Slider: ({ name, id, min, max, value, label, step, markers, displayValue, disabled, orientation, width, onChange, }: SliderProps) => import("react").JSX.Element;
23
23
  export default Slider;
@@ -1,4 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2021-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
2
6
  import { useEffect, useState } from "react";
3
7
  import { useTheme, Box, CounterLabel, FormControl } from "@primer/react";
4
8
  export const Slider = ({ name = "slider", id = "slider", min = 0, max = 10, value = 5, label = "label", step = 1, markers = false, displayValue = true, disabled = false, orientation = "horizontal", width = "200px", onChange, }) => {
@@ -2,7 +2,7 @@ import type { StoryObj } from '@storybook/react';
2
2
  import { SliderProps } from '../../index';
3
3
  declare const meta: {
4
4
  title: string;
5
- component: ({ name, id, min, max, value, label, step, markers, displayValue, disabled, orientation, width, onChange, }: SliderProps) => import("react/jsx-runtime").JSX.Element;
5
+ component: ({ name, id, min, max, value, label, step, markers, displayValue, disabled, orientation, width, onChange, }: SliderProps) => import("react").JSX.Element;
6
6
  tags: string[];
7
7
  parameters: {
8
8
  layout: string;
@@ -1,4 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2021-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
2
6
  import { useState } from 'react';
3
7
  import { Box, CounterLabel } from "@primer/react";
4
8
  import { Slider } from '../../index';
@@ -15,5 +15,5 @@ export interface SlidingPanelProps {
15
15
  panelSize?: number | string;
16
16
  fullWidth?: boolean;
17
17
  }
18
- export declare const SlidingPanel: ({ isOpen, message, details, onDismiss, position, variant, durationMs, transitionMs, zIndex, offset, panelSize, fullWidth, }: SlidingPanelProps) => import("react/jsx-runtime").JSX.Element | null;
18
+ export declare const SlidingPanel: ({ isOpen, message, details, onDismiss, position, variant, durationMs, transitionMs, zIndex, offset, panelSize, fullWidth, }: SlidingPanelProps) => import("react").JSX.Element | null;
19
19
  export default SlidingPanel;
@@ -1,4 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2021-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
2
6
  import { useEffect, useMemo, useState } from 'react';
3
7
  import { IconButton } from '@primer/react';
4
8
  import { XIcon } from '@primer/octicons-react';
@@ -169,7 +173,19 @@ export const SlidingPanel = ({ isOpen, message, details, onDismiss, position = '
169
173
  display: 'flex',
170
174
  pointerEvents: 'none',
171
175
  ...anchorStyles,
172
- }, children: _jsxs(Box, { role: variant === 'error' || variant === 'danger' ? 'alert' : 'status', "aria-live": variant === 'error' || variant === 'danger' ? 'assertive' : 'polite', onClick: onDismiss, sx: {
176
+ }, children: _jsxs(Box, { role: variant === 'error' || variant === 'danger' ? 'alert' : 'status', "aria-live": variant === 'error' || variant === 'danger' ? 'assertive' : 'polite', onClick: onDismiss
177
+ ? event => {
178
+ // A tap on the panel dismisses it — but not a click on a
179
+ // link, button or field in its content, nor the end of a
180
+ // text selection: those are someone using what it says.
181
+ const target = event.target;
182
+ if (target.closest('a, button, input, select, textarea, label, [role="button"], [contenteditable="true"]') ||
183
+ (window.getSelection()?.toString() ?? '') !== '') {
184
+ return;
185
+ }
186
+ onDismiss();
187
+ }
188
+ : undefined, sx: {
173
189
  pointerEvents: 'auto',
174
190
  position: 'relative',
175
191
  display: 'flex',
@@ -2,7 +2,7 @@ import type { StoryObj } from '@storybook/react';
2
2
  import { type SlidingPanelProps } from './SlidingPanel';
3
3
  declare const meta: {
4
4
  title: string;
5
- component: ({ isOpen, message, details, onDismiss, position, variant, durationMs, transitionMs, zIndex, offset, panelSize, fullWidth, }: SlidingPanelProps) => import("react/jsx-runtime").JSX.Element | null;
5
+ component: ({ isOpen, message, details, onDismiss, position, variant, durationMs, transitionMs, zIndex, offset, panelSize, fullWidth, }: SlidingPanelProps) => import("react").JSX.Element | null;
6
6
  tags: string[];
7
7
  parameters: {
8
8
  layout: string;