@datalayer/primer-addons 1.0.5 → 1.0.7

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.
@@ -5,3 +5,4 @@ export * from "./closeable-flash/CloseableFlash";
5
5
  export * from "./icons/CircleIcon";
6
6
  export * from "./overlay/Overlay";
7
7
  export * from "./slider/Slider";
8
+ export * from "./toolbar";
@@ -5,3 +5,4 @@ export * from "./closeable-flash/CloseableFlash";
5
5
  export * from "./icons/CircleIcon";
6
6
  export * from "./overlay/Overlay";
7
7
  export * from "./slider/Slider";
8
+ export * from "./toolbar";
@@ -0,0 +1,3 @@
1
+ import type { FloatingToolbarProps } from './types';
2
+ export declare function FloatingToolbar({ items, extraItems, anchorElement, isVisible, className, disabled, ariaLabel, portalContainer, }: FloatingToolbarProps): import("react").ReactPortal | null;
3
+ export default FloatingToolbar;
@@ -0,0 +1,126 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2025-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * FloatingToolbar - A floating toolbar that positions itself near a selection or anchor.
8
+ *
9
+ * Uses createPortal and absolute positioning to float above content.
10
+ * Designed to be used as a text-selection floating format bar, or an inline AI action bar.
11
+ *
12
+ * Extensible: consumers register items via the `items` and `extraItems` props.
13
+ *
14
+ * Usage:
15
+ * ```tsx
16
+ * <FloatingToolbar
17
+ * isVisible={hasSelection}
18
+ * anchorElement={floatingAnchor}
19
+ * items={formatItems}
20
+ * extraItems={aiItems}
21
+ * />
22
+ * ```
23
+ *
24
+ * @module components/toolbar/FloatingToolbar
25
+ */
26
+ import { useMemo, useRef, useCallback, useEffect } from 'react';
27
+ import { createPortal } from 'react-dom';
28
+ import { Box } from '../box/Box';
29
+ import { ToolbarRenderer } from './ToolbarRenderer';
30
+ /**
31
+ * Position the floating element relative to a DOMRect within an anchor element.
32
+ */
33
+ function setFloatingPosition(targetRect, floatingElem, anchorElem, verticalGap = 10, horizontalOffset = 5) {
34
+ const scrollerElem = anchorElem.parentElement;
35
+ if (targetRect === null || !scrollerElem) {
36
+ floatingElem.style.opacity = '0';
37
+ floatingElem.style.transform = 'translateY(-4px)';
38
+ floatingElem.style.top = '-10000px';
39
+ floatingElem.style.left = '-10000px';
40
+ return;
41
+ }
42
+ const floatingElemRect = floatingElem.getBoundingClientRect();
43
+ const anchorElementRect = anchorElem.getBoundingClientRect();
44
+ const editorScrollerRect = scrollerElem.getBoundingClientRect();
45
+ let top = targetRect.top - floatingElemRect.height - verticalGap;
46
+ let left = targetRect.left -
47
+ horizontalOffset -
48
+ (anchorElementRect.left - editorScrollerRect.left);
49
+ // Keep within anchor bounds
50
+ if (top < anchorElementRect.top) {
51
+ top = targetRect.bottom + verticalGap;
52
+ }
53
+ if (left + floatingElemRect.width > editorScrollerRect.right) {
54
+ left = editorScrollerRect.right - floatingElemRect.width - horizontalOffset;
55
+ }
56
+ floatingElem.style.opacity = '1';
57
+ floatingElem.style.transform = 'translateY(0)';
58
+ floatingElem.style.top = `${top}px`;
59
+ floatingElem.style.left = `${left}px`;
60
+ }
61
+ export function FloatingToolbar({ items, extraItems, anchorElement, isVisible, className, disabled, ariaLabel = 'Floating toolbar', portalContainer, }) {
62
+ const floatingRef = useRef(null);
63
+ const allItems = useMemo(() => {
64
+ if (!extraItems?.length)
65
+ return items;
66
+ return [...items, ...extraItems];
67
+ }, [items, extraItems]);
68
+ const updatePosition = useCallback(() => {
69
+ const floatingElem = floatingRef.current;
70
+ if (!floatingElem || !anchorElement)
71
+ return;
72
+ const nativeSelection = window.getSelection();
73
+ if (!nativeSelection || nativeSelection.isCollapsed || !isVisible) {
74
+ floatingElem.style.opacity = '0';
75
+ floatingElem.style.transform = 'translateY(-4px)';
76
+ return;
77
+ }
78
+ const range = nativeSelection.getRangeAt(0);
79
+ const rangeRect = range.getBoundingClientRect();
80
+ setFloatingPosition(rangeRect, floatingElem, anchorElement);
81
+ }, [anchorElement, isVisible]);
82
+ // Update position on scroll and resize
83
+ useEffect(() => {
84
+ if (!isVisible)
85
+ return;
86
+ const update = () => updatePosition();
87
+ window.addEventListener('resize', update);
88
+ const scrollerElem = anchorElement?.parentElement;
89
+ if (scrollerElem) {
90
+ scrollerElem.addEventListener('scroll', update);
91
+ }
92
+ // Initial position
93
+ update();
94
+ return () => {
95
+ window.removeEventListener('resize', update);
96
+ if (scrollerElem) {
97
+ scrollerElem.removeEventListener('scroll', update);
98
+ }
99
+ };
100
+ }, [isVisible, updatePosition, anchorElement]);
101
+ if (!isVisible || allItems.length === 0) {
102
+ return null;
103
+ }
104
+ const container = portalContainer || anchorElement || document.body;
105
+ const toolbar = (_jsx(Box, { ref: floatingRef, className: className, role: "toolbar", "aria-label": ariaLabel, sx: {
106
+ display: 'flex',
107
+ alignItems: 'center',
108
+ gap: '2px',
109
+ px: 1,
110
+ py: '4px',
111
+ bg: 'canvas.overlay',
112
+ border: '1px solid',
113
+ borderColor: 'border.default',
114
+ borderRadius: 2,
115
+ boxShadow: 'shadow.large',
116
+ position: 'absolute',
117
+ zIndex: 10,
118
+ top: '-10000px',
119
+ left: '-10000px',
120
+ opacity: 0,
121
+ transition: 'opacity 0.15s ease, transform 0.15s ease',
122
+ transform: 'translateY(-4px)',
123
+ }, children: _jsx(ToolbarRenderer, { items: allItems, disabled: disabled, size: "small" }) }));
124
+ return createPortal(toolbar, container);
125
+ }
126
+ export default FloatingToolbar;
@@ -0,0 +1,3 @@
1
+ import type { ToolbarProps } from './types';
2
+ export declare function Toolbar({ items, extraItems, className, disabled, ariaLabel, }: ToolbarProps): import("react/jsx-runtime").JSX.Element;
3
+ export default Toolbar;
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2025-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * Toolbar - Fixed/sticky toolbar component.
8
+ *
9
+ * An extensible toolbar that renders ToolbarItems using Primer React components.
10
+ * Designed to be used as a rich text editor toolbar or any fixed toolbar.
11
+ *
12
+ * Usage:
13
+ * ```tsx
14
+ * <Toolbar
15
+ * items={[
16
+ * { key: 'bold', type: 'button', icon: BoldIcon, ariaLabel: 'Bold', onClick: () => {} },
17
+ * { key: 'divider-1', type: 'divider' },
18
+ * { key: 'heading', type: 'dropdown', ariaLabel: 'Heading', label: 'Normal', options: [...] },
19
+ * ]}
20
+ * extraItems={pluginItems}
21
+ * />
22
+ * ```
23
+ *
24
+ * @module components/toolbar/Toolbar
25
+ */
26
+ import { useMemo } from 'react';
27
+ import { Box } from '../box/Box';
28
+ import { ToolbarRenderer } from './ToolbarRenderer';
29
+ export function Toolbar({ items, extraItems, className, disabled, ariaLabel = 'Editor toolbar', }) {
30
+ const allItems = useMemo(() => {
31
+ if (!extraItems?.length)
32
+ return items;
33
+ return [...items, ...extraItems];
34
+ }, [items, extraItems]);
35
+ return (_jsx(Box, { className: className, role: "toolbar", "aria-label": ariaLabel, sx: {
36
+ display: 'flex',
37
+ alignItems: 'center',
38
+ flexWrap: 'wrap',
39
+ gap: '2px',
40
+ px: 2,
41
+ py: 1,
42
+ bg: 'canvas.default',
43
+ borderBottom: '1px solid',
44
+ borderColor: 'border.default',
45
+ position: 'sticky',
46
+ top: 0,
47
+ zIndex: 2,
48
+ }, children: _jsx(ToolbarRenderer, { items: allItems, disabled: disabled, size: "medium" }) }));
49
+ }
50
+ export default Toolbar;
@@ -0,0 +1,8 @@
1
+ import type { ToolbarButtonItem } from './types';
2
+ export interface ToolbarButtonProps {
3
+ item: ToolbarButtonItem;
4
+ /** Size variant: 'small' for floating toolbar, 'medium' for fixed toolbar */
5
+ size?: 'small' | 'medium';
6
+ }
7
+ export declare function ToolbarButton({ item, size }: ToolbarButtonProps): import("react/jsx-runtime").JSX.Element;
8
+ export default ToolbarButton;
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2025-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * ToolbarButton - A single button in a Toolbar.
8
+ *
9
+ * Renders a native <button> with a direct Octicon icon.
10
+ * Avoids Primer's IconButton and Tooltip components to prevent invariant
11
+ * violations in Primer v37 (Tooltip requires a native interactive child,
12
+ * and IconButton's internal Tooltip conflicts with external wrappers).
13
+ *
14
+ * Uses the native HTML `title` attribute for tooltip behaviour.
15
+ *
16
+ * @module components/toolbar/ToolbarButton
17
+ */
18
+ import { isValidElement } from 'react';
19
+ export function ToolbarButton({ item, size = 'medium' }) {
20
+ const { ariaLabel, title, icon, label, isActive, onClick, disabled } = item;
21
+ // Resolve icon → React element.
22
+ // Octicon components use React.forwardRef, which returns an *object*
23
+ // (typeof === 'object'), NOT a function. So we check isValidElement first
24
+ // (already-instantiated JSX), then treat anything else as a component type.
25
+ let iconElement = null;
26
+ if (icon) {
27
+ if (isValidElement(icon)) {
28
+ iconElement = icon;
29
+ }
30
+ else {
31
+ // Component type: function component, forwardRef, or memo wrapper
32
+ const IconComp = icon;
33
+ iconElement = _jsx(IconComp, { size: 16 });
34
+ }
35
+ }
36
+ else if (label) {
37
+ // Fallback: render text label (e.g. "x₂" for subscript)
38
+ iconElement = _jsx("span", { style: { fontSize: 12, fontWeight: 600, lineHeight: 1 }, children: label });
39
+ }
40
+ const btnSize = size === 'small' ? 28 : 32;
41
+ return (_jsx("button", { type: "button", "aria-label": ariaLabel, title: title, onClick: onClick, disabled: disabled, style: {
42
+ display: 'inline-flex',
43
+ alignItems: 'center',
44
+ justifyContent: 'center',
45
+ width: btnSize,
46
+ height: btnSize,
47
+ padding: 0,
48
+ margin: 0,
49
+ border: 'none',
50
+ borderRadius: 6,
51
+ cursor: disabled ? 'not-allowed' : 'pointer',
52
+ background: isActive ? 'var(--bgColor-accent-muted, rgba(9,105,218,0.1))' : 'transparent',
53
+ color: isActive ? 'var(--fgColor-accent, #0969da)' : 'var(--fgColor-muted, #656d76)',
54
+ opacity: disabled ? 0.5 : 1,
55
+ lineHeight: 1,
56
+ }, onMouseEnter: (e) => {
57
+ if (!disabled) {
58
+ e.currentTarget.style.background = 'var(--bgColor-neutral-muted, rgba(175,184,193,0.2))';
59
+ e.currentTarget.style.color = 'var(--fgColor-default, #1f2328)';
60
+ }
61
+ }, onMouseLeave: (e) => {
62
+ e.currentTarget.style.background = isActive
63
+ ? 'var(--bgColor-accent-muted, rgba(9,105,218,0.1))'
64
+ : 'transparent';
65
+ e.currentTarget.style.color = isActive
66
+ ? 'var(--fgColor-accent, #0969da)'
67
+ : 'var(--fgColor-muted, #656d76)';
68
+ }, children: iconElement }));
69
+ }
70
+ export default ToolbarButton;
@@ -0,0 +1,6 @@
1
+ export interface ToolbarDividerProps {
2
+ /** Orientation: vertical for horizontal toolbars, horizontal for vertical */
3
+ orientation?: 'vertical' | 'horizontal';
4
+ }
5
+ export declare function ToolbarDivider({ orientation }: ToolbarDividerProps): import("react/jsx-runtime").JSX.Element;
6
+ export default ToolbarDivider;
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2025-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * ToolbarDivider - A vertical divider between toolbar items.
8
+ *
9
+ * @module components/toolbar/ToolbarDivider
10
+ */
11
+ import { Box } from '../box/Box';
12
+ export function ToolbarDivider({ orientation = 'vertical' }) {
13
+ return (_jsx(Box, { sx: {
14
+ ...(orientation === 'vertical'
15
+ ? {
16
+ width: '1px',
17
+ height: '20px',
18
+ mx: 1,
19
+ }
20
+ : {
21
+ height: '1px',
22
+ width: '100%',
23
+ my: 1,
24
+ }),
25
+ bg: 'border.muted',
26
+ flexShrink: 0,
27
+ } }));
28
+ }
29
+ export default ToolbarDivider;
@@ -0,0 +1,8 @@
1
+ import type { ToolbarDropdownItem } from './types';
2
+ export interface ToolbarDropdownProps {
3
+ item: ToolbarDropdownItem;
4
+ /** Size variant */
5
+ size?: 'small' | 'medium';
6
+ }
7
+ export declare function ToolbarDropdown({ item, size }: ToolbarDropdownProps): import("react/jsx-runtime").JSX.Element;
8
+ export default ToolbarDropdown;
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2025-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * ToolbarDropdown - A dropdown menu in a Toolbar.
8
+ *
9
+ * Uses Primer React ActionMenu for accessible dropdown menus.
10
+ *
11
+ * @module components/toolbar/ToolbarDropdown
12
+ */
13
+ import { isValidElement } from 'react';
14
+ import { ActionMenu, ActionList, Text } from '@primer/react';
15
+ import { Box } from '../box/Box';
16
+ function renderIcon(icon) {
17
+ if (!icon)
18
+ return null;
19
+ if (typeof icon === 'function') {
20
+ const IconComp = icon;
21
+ return _jsx(IconComp, { size: 16 });
22
+ }
23
+ if (isValidElement(icon)) {
24
+ return icon;
25
+ }
26
+ return null;
27
+ }
28
+ export function ToolbarDropdown({ item, size = 'medium' }) {
29
+ const { ariaLabel, icon, label, options, disabled } = item;
30
+ return (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Button, { "aria-label": ariaLabel, disabled: disabled, variant: "invisible", size: size, sx: {
31
+ color: 'fg.muted',
32
+ fontWeight: 'normal',
33
+ fontSize: size === 'small' ? 0 : 1,
34
+ px: 2,
35
+ '&:hover:not([disabled])': {
36
+ bg: 'neutral.muted',
37
+ color: 'fg.default',
38
+ },
39
+ }, children: _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [renderIcon(icon), label && (_jsx(Text, { sx: { fontSize: size === 'small' ? 0 : 1 }, children: label }))] }) }), _jsx(ActionMenu.Overlay, { width: "auto", children: _jsx(ActionList, { children: options.map(option => (_jsxs(ActionList.Item, { onSelect: option.onClick, disabled: option.disabled, active: option.isActive, children: [option.icon && (_jsx(ActionList.LeadingVisual, { children: renderIcon(option.icon) })), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%', gap: 3 }, children: [_jsx(Text, { children: option.label }), option.shortcut && (_jsx(Text, { sx: { color: 'fg.subtle', fontSize: 0, fontFamily: 'mono' }, children: option.shortcut }))] })] }, option.key))) }) })] }));
40
+ }
41
+ export default ToolbarDropdown;
@@ -0,0 +1,11 @@
1
+ import type { ToolbarItem } from './types';
2
+ export interface ToolbarRendererProps {
3
+ items: ToolbarItem[];
4
+ disabled?: boolean;
5
+ size?: 'small' | 'medium';
6
+ }
7
+ /**
8
+ * Sort items by order, then render each one according to its type.
9
+ */
10
+ export declare function ToolbarRenderer({ items, disabled, size }: ToolbarRendererProps): import("react/jsx-runtime").JSX.Element;
11
+ export default ToolbarRenderer;
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2025-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * ToolbarRenderer - Renders an array of ToolbarItems.
8
+ *
9
+ * Shared logic between Toolbar and FloatingToolbar.
10
+ *
11
+ * @module components/toolbar/ToolbarRenderer
12
+ */
13
+ import { Fragment } from 'react';
14
+ import { ToolbarButton } from './ToolbarButton';
15
+ import { ToolbarDropdown } from './ToolbarDropdown';
16
+ import { ToolbarDivider } from './ToolbarDivider';
17
+ /**
18
+ * Sort items by order, then render each one according to its type.
19
+ */
20
+ export function ToolbarRenderer({ items, disabled, size = 'medium' }) {
21
+ const sorted = [...items]
22
+ .filter(item => !item.hidden)
23
+ .sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
24
+ return (_jsx(_Fragment, { children: sorted.map(item => {
25
+ const itemDisabled = disabled || item.disabled;
26
+ switch (item.type) {
27
+ case 'button':
28
+ return (_jsx(Fragment, { children: _jsx(ToolbarButton, { item: { ...item, disabled: itemDisabled }, size: size }) }, item.key));
29
+ case 'dropdown':
30
+ return (_jsx(Fragment, { children: _jsx(ToolbarDropdown, { item: { ...item, disabled: itemDisabled }, size: size }) }, item.key));
31
+ case 'divider':
32
+ return (_jsx(Fragment, { children: _jsx(ToolbarDivider, {}) }, item.key));
33
+ case 'custom':
34
+ return _jsx(Fragment, { children: item.render() }, item.key);
35
+ default:
36
+ return null;
37
+ }
38
+ }) }));
39
+ }
40
+ export default ToolbarRenderer;
@@ -0,0 +1,7 @@
1
+ export * from './types';
2
+ export * from './Toolbar';
3
+ export * from './FloatingToolbar';
4
+ export * from './ToolbarButton';
5
+ export * from './ToolbarDropdown';
6
+ export * from './ToolbarDivider';
7
+ export * from './ToolbarRenderer';
@@ -0,0 +1,11 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ export * from './types';
6
+ export * from './Toolbar';
7
+ export * from './FloatingToolbar';
8
+ export * from './ToolbarButton';
9
+ export * from './ToolbarDropdown';
10
+ export * from './ToolbarDivider';
11
+ export * from './ToolbarRenderer';
@@ -0,0 +1,134 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { IconProps } from '@primer/octicons-react';
3
+ /**
4
+ * Toolbar item types that can be registered in a toolbar.
5
+ */
6
+ export type ToolbarItemType = 'button' | 'dropdown' | 'divider' | 'custom';
7
+ /**
8
+ * Base toolbar item definition.
9
+ */
10
+ export interface ToolbarItemBase {
11
+ /** Unique key for the item */
12
+ key: string;
13
+ /** Item type */
14
+ type: ToolbarItemType;
15
+ /** Sort order (lower = earlier). Default: 100 */
16
+ order?: number;
17
+ /** Group name for logical grouping */
18
+ group?: string;
19
+ /** Whether the item is disabled */
20
+ disabled?: boolean;
21
+ /** Whether to hide the item */
22
+ hidden?: boolean;
23
+ }
24
+ /**
25
+ * Toolbar button item definition.
26
+ */
27
+ export interface ToolbarButtonItem extends ToolbarItemBase {
28
+ type: 'button';
29
+ /** Accessible label */
30
+ ariaLabel: string;
31
+ /** Tooltip text */
32
+ title?: string;
33
+ /** Icon component (Octicon or custom) */
34
+ icon?: React.ComponentType<IconProps> | ReactNode;
35
+ /** Text label */
36
+ label?: string;
37
+ /** Whether the button is in an active/pressed state */
38
+ isActive?: boolean;
39
+ /** Click handler */
40
+ onClick: () => void;
41
+ /** Keyboard shortcut hint (e.g., "⌘B") */
42
+ shortcut?: string;
43
+ }
44
+ /**
45
+ * Dropdown option for ToolbarDropdownItem.
46
+ */
47
+ export interface ToolbarDropdownOption {
48
+ /** Unique key */
49
+ key: string;
50
+ /** Display label */
51
+ label: string;
52
+ /** Icon component or element */
53
+ icon?: React.ComponentType<IconProps> | ReactNode;
54
+ /** Whether this option is active/selected */
55
+ isActive?: boolean;
56
+ /** Click handler */
57
+ onClick: () => void;
58
+ /** Keyboard shortcut hint */
59
+ shortcut?: string;
60
+ /** Whether the option is disabled */
61
+ disabled?: boolean;
62
+ }
63
+ /**
64
+ * Toolbar dropdown item definition.
65
+ */
66
+ export interface ToolbarDropdownItem extends ToolbarItemBase {
67
+ type: 'dropdown';
68
+ /** Accessible label */
69
+ ariaLabel: string;
70
+ /** Tooltip text */
71
+ title?: string;
72
+ /** Icon component or element for the trigger */
73
+ icon?: React.ComponentType<IconProps> | ReactNode;
74
+ /** Text label for the trigger */
75
+ label?: string;
76
+ /** Dropdown options */
77
+ options: ToolbarDropdownOption[];
78
+ }
79
+ /**
80
+ * Toolbar divider item definition.
81
+ */
82
+ export interface ToolbarDividerItem extends ToolbarItemBase {
83
+ type: 'divider';
84
+ }
85
+ /**
86
+ * Toolbar custom item definition.
87
+ */
88
+ export interface ToolbarCustomItem extends ToolbarItemBase {
89
+ type: 'custom';
90
+ /** Custom render function */
91
+ render: () => ReactNode;
92
+ }
93
+ /**
94
+ * Union of all toolbar item types.
95
+ */
96
+ export type ToolbarItem = ToolbarButtonItem | ToolbarDropdownItem | ToolbarDividerItem | ToolbarCustomItem;
97
+ /**
98
+ * Props for the extensible Toolbar component.
99
+ */
100
+ export interface ToolbarProps {
101
+ /** Array of toolbar items to render */
102
+ items: ToolbarItem[];
103
+ /** Additional CSS class name */
104
+ className?: string;
105
+ /** Whether the entire toolbar is disabled */
106
+ disabled?: boolean;
107
+ /** Additional items to append (for extensibility) */
108
+ extraItems?: ToolbarItem[];
109
+ /** Aria label for the toolbar */
110
+ ariaLabel?: string;
111
+ }
112
+ /**
113
+ * Props for the extensible FloatingToolbar component.
114
+ */
115
+ export interface FloatingToolbarProps {
116
+ /** Array of toolbar items to render */
117
+ items: ToolbarItem[];
118
+ /** The anchor element to position relative to */
119
+ anchorElement?: HTMLElement | null;
120
+ /** Whether the floating toolbar is visible */
121
+ isVisible: boolean;
122
+ /** Additional CSS class name */
123
+ className?: string;
124
+ /** Whether the entire toolbar is disabled */
125
+ disabled?: boolean;
126
+ /** Additional items to append (for extensibility) */
127
+ extraItems?: ToolbarItem[];
128
+ /** Aria label for the toolbar */
129
+ ariaLabel?: string;
130
+ /** Callback when the toolbar requests to close */
131
+ onClose?: () => void;
132
+ /** Portal container element. Defaults to document.body */
133
+ portalContainer?: HTMLElement;
134
+ }
@@ -0,0 +1,5 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ export {};
package/package.json CHANGED
@@ -1,16 +1,19 @@
1
1
  {
2
2
  "name": "@datalayer/primer-addons",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "license": "BSD-3-Clause",
5
5
  "scripts": {
6
- "dev": "vite",
7
- "start": "vite",
8
6
  "build": "tsc && vite build",
9
- "watch": "tsc -w",
7
+ "build-storybook": "storybook build",
8
+ "build:lib": "npm run clean:lib&& tsc -b",
9
+ "clean:lib": "rimraf lib tsconfig.tsbuildinfo",
10
+ "dev": "vite",
10
11
  "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
11
12
  "preview": "vite preview",
13
+ "start": "vite",
12
14
  "storybook": "storybook dev -p 6006",
13
- "build-storybook": "storybook build"
15
+ "watch": "tsc -w",
16
+ "watch:lib": "tsc -b -w"
14
17
  },
15
18
  "main": "lib/index.js",
16
19
  "files": [
@@ -1,14 +0,0 @@
1
- import type { StoryObj } from '@storybook/react';
2
- import { SliderProps } from '../../../index';
3
- declare const meta: {
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;
6
- tags: string[];
7
- parameters: {
8
- layout: string;
9
- };
10
- };
11
- export default meta;
12
- type Story = StoryObj<typeof meta>;
13
- export declare const SliderDay: Story;
14
- export declare const SliderNight: Story;
@@ -1,47 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { ThemeProvider, BaseStyles, Box, CounterLabel } from "@primer/react";
4
- import { Slider } from '../../../index';
5
- const meta = {
6
- title: 'Components/Slider',
7
- component: Slider,
8
- // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs
9
- tags: ['autodocs'],
10
- parameters: {
11
- // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
12
- layout: 'fullscreen',
13
- },
14
- };
15
- export default meta;
16
- const ThemedSlider = (props) => {
17
- const [value, setValue] = useState(50);
18
- return (_jsx(ThemeProvider, { colorMode: props.colorMode, children: _jsx(BaseStyles, { children: _jsxs(Box, { p: 3, bg: "canvas.default", children: [_jsx(Box, { children: _jsx(Slider, { ...props, value: value, onChange: setValue }) }), _jsx(Box, { children: _jsx(CounterLabel, { sx: { ml: 2 }, children: value }) })] }) }) }));
19
- };
20
- export const SliderDay = {
21
- args: {
22
- name: "slider-day",
23
- id: "slider-day",
24
- min: 0,
25
- max: 100,
26
- step: 10,
27
- value: 50,
28
- onChange: (value) => {
29
- console.log('Slider value changed:', value);
30
- },
31
- },
32
- render: (args) => _jsx(ThemedSlider, { ...args, colorMode: "day" })
33
- };
34
- export const SliderNight = {
35
- args: {
36
- name: "slider-night",
37
- id: "slider-night",
38
- min: 0,
39
- max: 100,
40
- step: 10,
41
- value: 50,
42
- onChange: (value) => {
43
- console.log('Slider value changed:', value);
44
- },
45
- },
46
- render: (args) => _jsx(ThemedSlider, { ...args, colorMode: "night" })
47
- };