Merge pull request #32313 from backstage/bui-popover
BUI - Add new Popover component
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Added new Popover component for Backstage UI with automatic overflow handling, and full placement support. Also introduced `--bui-shadow` token for consistent elevation styling across overlay components (Popover, Tooltip, Menu).
|
||||
File diff suppressed because one or more lines are too long
@@ -100,6 +100,7 @@
|
||||
--bui-ring: #1f5493;
|
||||
--bui-scrollbar: #a0a0a03b;
|
||||
--bui-scrollbar-thumb: #a0a0a0;
|
||||
--bui-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;
|
||||
--bui-animate-pulse: pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;
|
||||
}
|
||||
|
||||
@@ -161,6 +162,7 @@
|
||||
--bui-ring: #1f5493;
|
||||
--bui-scrollbar: #3636363a;
|
||||
--bui-scrollbar-thumb: #575757;
|
||||
--bui-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import type { MenuProps as MenuProps_2 } from 'react-aria-components';
|
||||
import type { MenuSectionProps as MenuSectionProps_2 } from 'react-aria-components';
|
||||
import type { MenuTriggerProps as MenuTriggerProps_2 } from 'react-aria-components';
|
||||
import type { ModalOverlayProps } from 'react-aria-components';
|
||||
import type { PopoverProps } from 'react-aria-components';
|
||||
import { PopoverProps as PopoverProps_2 } from 'react-aria-components';
|
||||
import type { RadioGroupProps as RadioGroupProps_2 } from 'react-aria-components';
|
||||
import type { RadioProps as RadioProps_2 } from 'react-aria-components';
|
||||
import { ReactElement } from 'react';
|
||||
@@ -1025,7 +1025,7 @@ export interface MenuAutocompleteListBoxProps<T>
|
||||
// (undocumented)
|
||||
placeholder?: string;
|
||||
// (undocumented)
|
||||
placement?: PopoverProps['placement'];
|
||||
placement?: PopoverProps_2['placement'];
|
||||
// (undocumented)
|
||||
virtualized?: boolean;
|
||||
}
|
||||
@@ -1041,7 +1041,7 @@ export interface MenuAutocompleteProps<T>
|
||||
// (undocumented)
|
||||
placeholder?: string;
|
||||
// (undocumented)
|
||||
placement?: PopoverProps['placement'];
|
||||
placement?: PopoverProps_2['placement'];
|
||||
// (undocumented)
|
||||
virtualized?: boolean;
|
||||
}
|
||||
@@ -1106,7 +1106,7 @@ export interface MenuListBoxProps<T>
|
||||
// (undocumented)
|
||||
maxWidth?: string;
|
||||
// (undocumented)
|
||||
placement?: PopoverProps['placement'];
|
||||
placement?: PopoverProps_2['placement'];
|
||||
// (undocumented)
|
||||
virtualized?: boolean;
|
||||
}
|
||||
@@ -1120,7 +1120,7 @@ export interface MenuProps<T>
|
||||
// (undocumented)
|
||||
maxWidth?: string;
|
||||
// (undocumented)
|
||||
placement?: PopoverProps['placement'];
|
||||
placement?: PopoverProps_2['placement'];
|
||||
// (undocumented)
|
||||
virtualized?: boolean;
|
||||
}
|
||||
@@ -1206,6 +1206,26 @@ export interface PaginationOptions {
|
||||
showPageSizeOptions?: boolean;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const Popover: ForwardRefExoticComponent<
|
||||
PopoverProps & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
|
||||
// @public
|
||||
export const PopoverDefinition: {
|
||||
readonly classNames: {
|
||||
readonly root: 'bui-Popover';
|
||||
readonly arrow: 'bui-PopoverArrow';
|
||||
readonly content: 'bui-PopoverContent';
|
||||
};
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface PopoverProps extends Omit<PopoverProps_2, 'children'> {
|
||||
children: React.ReactNode;
|
||||
hideArrow?: boolean;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface QueryOptions<TFilter> {
|
||||
// (undocumented)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
.bui-MenuPopover {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: var(--bui-shadow);
|
||||
border: 1px solid var(--bui-border);
|
||||
border-radius: var(--bui-radius-2);
|
||||
background: var(--bui-bg-surface-1);
|
||||
|
||||
@@ -18,13 +18,124 @@
|
||||
|
||||
@layer components {
|
||||
.bui-Popover {
|
||||
margin-right: 12px;
|
||||
box-shadow: var(--bui-shadow);
|
||||
border-radius: var(--bui-radius-3);
|
||||
background: var(--bui-bg-surface-1);
|
||||
border: 1px solid var(--bui-gray-3);
|
||||
forced-color-adjust: none;
|
||||
outline: none;
|
||||
max-height: inherit;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* fixes FF gap */
|
||||
transform: translate3d(0, 0, 0);
|
||||
transition: transform 200ms, opacity 200ms;
|
||||
|
||||
&[data-entering],
|
||||
&[data-exiting] {
|
||||
transform: var(--origin);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&[data-placement='top'] {
|
||||
--origin: translateY(4px);
|
||||
|
||||
&:has(.bui-PopoverArrow) {
|
||||
margin-bottom: var(--bui-space-3);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-placement='right'] {
|
||||
--origin: translateX(-4px);
|
||||
|
||||
&:has(.bui-PopoverArrow) {
|
||||
margin-left: var(--bui-space-3);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-placement='bottom'] {
|
||||
--origin: translateY(-4px);
|
||||
|
||||
&:has(.bui-PopoverArrow) {
|
||||
margin-top: var(--bui-space-3);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-placement='left'] {
|
||||
--origin: translateX(4px);
|
||||
|
||||
&:has(.bui-PopoverArrow) {
|
||||
margin-right: var(--bui-space-3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bui-PopoverContent {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
background-color: var(--bui-bg-surface-1);
|
||||
border: 1px solid var(--bui-border);
|
||||
border-radius: var(--bui-radius-3);
|
||||
padding-block: var(--bui-space-1);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: var(--bui-space-4);
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.bui-PopoverArrow {
|
||||
& svg {
|
||||
display: block;
|
||||
|
||||
/* The popover is rendered overlaying the main
|
||||
popover element by 1px. This causes the borders
|
||||
to overlap, which causes minor visual artifacts
|
||||
with transparent border colors. To mitigate this,
|
||||
we split the stroke and fill across separate
|
||||
elements in order to guarantee that the stroke is
|
||||
always overlaying a consistent color. */
|
||||
path:nth-child(1) {
|
||||
fill: var(--bui-bg-surface-1);
|
||||
}
|
||||
|
||||
path:nth-child(2) {
|
||||
fill: var(--bui-gray-3);
|
||||
}
|
||||
|
||||
/* The arrow svg overlaps the popover by 2px, so we
|
||||
need to adjust the margins accordingly. */
|
||||
--popover-arrow-overlap: -2px;
|
||||
}
|
||||
|
||||
&[data-placement='top'] svg {
|
||||
margin-top: var(--popover-arrow-overlap);
|
||||
}
|
||||
|
||||
&[data-placement='bottom'] svg {
|
||||
margin-bottom: var(--popover-arrow-overlap);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
&[data-placement='right'] svg {
|
||||
margin-right: var(--popover-arrow-overlap);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&[data-placement='left'] svg {
|
||||
margin-left: var(--popover-arrow-overlap);
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
.bui-Popover {
|
||||
background: var(--bui-bg-surface-2);
|
||||
border: 1px solid var(--bui-gray-4);
|
||||
}
|
||||
|
||||
.bui-PopoverArrow {
|
||||
svg path:nth-child(1) {
|
||||
fill: var(--bui-bg-surface-2);
|
||||
}
|
||||
|
||||
svg path:nth-child(2) {
|
||||
fill: var(--bui-gray-4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import preview from '../../../../../.storybook/preview';
|
||||
import { Popover } from './Popover';
|
||||
import { Button } from '../Button/Button';
|
||||
import { DialogTrigger } from '../Dialog/Dialog';
|
||||
import { Text } from '../Text/Text';
|
||||
import { Flex } from '../Flex/Flex';
|
||||
|
||||
const meta = preview.meta({
|
||||
title: 'Backstage UI/Popover',
|
||||
component: Popover,
|
||||
parameters: { layout: 'centered' },
|
||||
argTypes: {
|
||||
isOpen: {
|
||||
control: { type: 'boolean' },
|
||||
},
|
||||
hideArrow: {
|
||||
control: { type: 'boolean' },
|
||||
},
|
||||
placement: {
|
||||
control: { type: 'select' },
|
||||
options: [
|
||||
'top',
|
||||
'top start',
|
||||
'top end',
|
||||
'bottom',
|
||||
'bottom start',
|
||||
'bottom end',
|
||||
'left',
|
||||
'left start',
|
||||
'left end',
|
||||
'right',
|
||||
'right start',
|
||||
'right end',
|
||||
],
|
||||
},
|
||||
offset: {
|
||||
control: { type: 'number' },
|
||||
},
|
||||
},
|
||||
render: ({ children, isOpen, hideArrow, placement, offset }) => (
|
||||
<DialogTrigger>
|
||||
<Button>Open Popover</Button>
|
||||
<Popover
|
||||
isOpen={isOpen}
|
||||
hideArrow={hideArrow}
|
||||
placement={placement}
|
||||
offset={offset}
|
||||
>
|
||||
{children ?? <Text>This is a popover</Text>}
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
),
|
||||
});
|
||||
|
||||
export const Default = meta.story({
|
||||
args: {
|
||||
children: <Text>This is a popover</Text>,
|
||||
},
|
||||
});
|
||||
|
||||
export const IsOpen = Default.extend({
|
||||
args: {
|
||||
isOpen: true,
|
||||
},
|
||||
});
|
||||
|
||||
export const HideArrow = Default.extend({
|
||||
args: {
|
||||
isOpen: true,
|
||||
hideArrow: true,
|
||||
},
|
||||
});
|
||||
|
||||
export const TopPlacement = Default.extend({
|
||||
args: {
|
||||
isOpen: true,
|
||||
placement: 'top',
|
||||
},
|
||||
});
|
||||
|
||||
export const RightPlacement = Default.extend({
|
||||
args: {
|
||||
isOpen: true,
|
||||
placement: 'right',
|
||||
},
|
||||
});
|
||||
|
||||
export const BottomPlacement = Default.extend({
|
||||
args: {
|
||||
isOpen: true,
|
||||
placement: 'bottom',
|
||||
},
|
||||
});
|
||||
|
||||
export const LeftPlacement = Default.extend({
|
||||
args: {
|
||||
isOpen: true,
|
||||
placement: 'left',
|
||||
},
|
||||
});
|
||||
|
||||
export const AllPlacements = Default.extend({
|
||||
parameters: {
|
||||
controls: {
|
||||
exclude: ['placement'],
|
||||
},
|
||||
},
|
||||
args: {
|
||||
isOpen: true,
|
||||
},
|
||||
render: ({ isOpen, hideArrow }) => {
|
||||
return (
|
||||
<DialogTrigger>
|
||||
<Button>Open Popovers</Button>
|
||||
<Popover isOpen={isOpen} placement="top" hideArrow={hideArrow}>
|
||||
<Text>Top placement</Text>
|
||||
</Popover>
|
||||
<Popover isOpen={isOpen} placement="right" hideArrow={hideArrow}>
|
||||
<Text>Right placement</Text>
|
||||
</Popover>
|
||||
<Popover isOpen={isOpen} placement="bottom" hideArrow={hideArrow}>
|
||||
<Text>Bottom placement</Text>
|
||||
</Popover>
|
||||
<Popover isOpen={isOpen} placement="left" hideArrow={hideArrow}>
|
||||
<Text>Left placement</Text>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const AllPlacementsNoArrow = Default.extend({
|
||||
parameters: {
|
||||
controls: {
|
||||
exclude: ['placement', 'hideArrow'],
|
||||
},
|
||||
},
|
||||
args: {
|
||||
isOpen: true,
|
||||
hideArrow: true,
|
||||
},
|
||||
render: ({ isOpen }) => {
|
||||
return (
|
||||
<DialogTrigger>
|
||||
<Button>Open Popovers</Button>
|
||||
<Popover isOpen={isOpen} placement="top" hideArrow>
|
||||
<Text>Top placement</Text>
|
||||
</Popover>
|
||||
<Popover isOpen={isOpen} placement="right" hideArrow>
|
||||
<Text>Right placement</Text>
|
||||
</Popover>
|
||||
<Popover isOpen={isOpen} placement="bottom" hideArrow>
|
||||
<Text>Bottom placement</Text>
|
||||
</Popover>
|
||||
<Popover isOpen={isOpen} placement="left" hideArrow>
|
||||
<Text>Left placement</Text>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const WithRichContent = Default.extend({
|
||||
args: {
|
||||
isOpen: true,
|
||||
},
|
||||
render: ({ isOpen, hideArrow, placement }) => (
|
||||
<DialogTrigger>
|
||||
<Button>Open Popover</Button>
|
||||
<Popover isOpen={isOpen} hideArrow={hideArrow} placement={placement}>
|
||||
<Flex direction="column" gap="3" style={{ width: '280px' }}>
|
||||
<Text style={{ fontWeight: 'bold' }}>Popover Title</Text>
|
||||
<Text>
|
||||
This is a popover with rich content. It can contain multiple
|
||||
elements and formatted text.
|
||||
</Text>
|
||||
<Flex gap="2" justify="end">
|
||||
<Button variant="tertiary" size="small">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="primary" size="small">
|
||||
Confirm
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
),
|
||||
});
|
||||
|
||||
export const CustomOffset = Default.extend({
|
||||
args: {
|
||||
isOpen: true,
|
||||
offset: 20,
|
||||
placement: 'bottom',
|
||||
},
|
||||
});
|
||||
|
||||
export const NonModal = Default.extend({
|
||||
args: {
|
||||
isOpen: true,
|
||||
},
|
||||
render: ({ isOpen, hideArrow, placement }) => (
|
||||
<DialogTrigger>
|
||||
<Button>Open Non-Modal Popover</Button>
|
||||
<Popover
|
||||
isOpen={isOpen}
|
||||
hideArrow={hideArrow}
|
||||
placement={placement}
|
||||
isNonModal
|
||||
>
|
||||
<Text>
|
||||
This is a non-modal popover. You can interact with other elements on
|
||||
the page while it's open.
|
||||
</Text>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
),
|
||||
});
|
||||
|
||||
export const WithLongContent = Default.extend({
|
||||
args: {
|
||||
isOpen: true,
|
||||
},
|
||||
render: ({ isOpen, hideArrow, placement }) => (
|
||||
<DialogTrigger>
|
||||
<Button>Open Popover</Button>
|
||||
<Popover isOpen={isOpen} hideArrow={hideArrow} placement={placement}>
|
||||
<Flex direction="column" gap="3" style={{ width: '320px' }}>
|
||||
<Text style={{ fontWeight: 'bold' }}>Long Content Example</Text>
|
||||
<Text>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
|
||||
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
|
||||
aliquip ex ea commodo consequat.
|
||||
</Text>
|
||||
<Text>
|
||||
Duis aute irure dolor in reprehenderit in voluptate velit esse
|
||||
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
|
||||
cupidatat non proident, sunt in culpa qui officia deserunt mollit
|
||||
anim id est laborum.
|
||||
</Text>
|
||||
<Text>
|
||||
Sed ut perspiciatis unde omnis iste natus error sit voluptatem
|
||||
accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
|
||||
quae ab illo inventore veritatis et quasi architecto beatae vitae
|
||||
dicta sunt explicabo.
|
||||
</Text>
|
||||
<Text>
|
||||
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut
|
||||
fugit, sed quia consequuntur magni dolores eos qui ratione
|
||||
voluptatem sequi nesciunt.
|
||||
</Text>
|
||||
<Text>
|
||||
Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet,
|
||||
consectetur, adipisci velit, sed quia non numquam eius modi tempora
|
||||
incidunt ut labore et dolore magnam aliquam quaerat voluptatem.
|
||||
</Text>
|
||||
</Flex>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
),
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
import { OverlayArrow, Popover as AriaPopover } from 'react-aria-components';
|
||||
import clsx from 'clsx';
|
||||
import { PopoverProps } from './types';
|
||||
import { useStyles } from '../../hooks/useStyles';
|
||||
import { PopoverDefinition } from './definition';
|
||||
import styles from './Popover.module.css';
|
||||
|
||||
/**
|
||||
* A popover component built on React Aria Components that displays floating
|
||||
* content anchored to a trigger element.
|
||||
*
|
||||
* @remarks
|
||||
* The Popover component supports multiple placements (top, right, bottom, left),
|
||||
* automatic viewport-constrained scrolling, and conditional arrow rendering. It
|
||||
* automatically handles positioning, collision detection, and ARIA attributes for
|
||||
* accessibility. Content is automatically padded and scrollable when it exceeds
|
||||
* available space.
|
||||
*
|
||||
* @example
|
||||
* Basic usage with DialogTrigger:
|
||||
* ```tsx
|
||||
* <DialogTrigger>
|
||||
* <Button>Open Popover</Button>
|
||||
* <Popover>
|
||||
* <Text>Popover content</Text>
|
||||
* </Popover>
|
||||
* </DialogTrigger>
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* With custom placement and no arrow:
|
||||
* ```tsx
|
||||
* <DialogTrigger>
|
||||
* <Button>Open</Button>
|
||||
* <Popover placement="right" hideArrow>
|
||||
* <Text>Content without arrow</Text>
|
||||
* </Popover>
|
||||
* </DialogTrigger>
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const Popover = forwardRef<HTMLDivElement, PopoverProps>(
|
||||
(props, ref) => {
|
||||
const { classNames, cleanedProps } = useStyles(PopoverDefinition, props);
|
||||
const { className, children, hideArrow, ...rest } = cleanedProps;
|
||||
|
||||
return (
|
||||
<AriaPopover
|
||||
className={clsx(classNames.root, styles[classNames.root], className)}
|
||||
{...rest}
|
||||
ref={ref}
|
||||
>
|
||||
{({ trigger }) => (
|
||||
<>
|
||||
{!hideArrow &&
|
||||
trigger !== 'MenuTrigger' &&
|
||||
trigger !== 'SubmenuTrigger' && (
|
||||
<OverlayArrow
|
||||
className={clsx(classNames.arrow, styles[classNames.arrow])}
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M10.3356 7.39793L15.1924 3.02682C15.9269 2.36577 16.8801 2 17.8683 2H20V7.94781e-07L1.74846e-07 -9.53674e-07L0 2L1.4651 2C2.4532 2 3.4064 2.36577 4.1409 3.02682L8.9977 7.39793C9.378 7.7402 9.9553 7.74021 10.3356 7.39793Z" />
|
||||
<path d="M11.0046 8.14124C10.2439 8.82575 9.08939 8.82578 8.32869 8.14122L3.47189 3.77011C2.92109 3.27432 2.20619 2.99999 1.46509 2.99999L4.10999 3L8.99769 7.39793C9.37799 7.7402 9.95529 7.7402 10.3356 7.39793L15.2226 3L17.8683 2.99999C17.1271 2.99999 16.4122 3.27432 15.8614 3.77011L11.0046 8.14124Z" />
|
||||
</svg>
|
||||
</OverlayArrow>
|
||||
)}
|
||||
<div
|
||||
className={clsx(classNames.content, styles[classNames.content])}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</AriaPopover>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Popover.displayName = 'Popover';
|
||||
@@ -23,5 +23,7 @@ import type { ComponentDefinition } from '../../types';
|
||||
export const PopoverDefinition = {
|
||||
classNames: {
|
||||
root: 'bui-Popover',
|
||||
arrow: 'bui-PopoverArrow',
|
||||
content: 'bui-PopoverContent',
|
||||
},
|
||||
} as const satisfies ComponentDefinition;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,4 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { Popover } from './Popover';
|
||||
export type { PopoverProps } from './types';
|
||||
export { PopoverDefinition } from './definition';
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { PopoverProps as AriaPopoverProps } from 'react-aria-components';
|
||||
|
||||
/**
|
||||
* Properties for {@link Popover}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface PopoverProps extends Omit<AriaPopoverProps, 'children'> {
|
||||
/**
|
||||
* The content to display inside the popover.
|
||||
* Content is automatically wrapped with padding and scroll behavior.
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
|
||||
/**
|
||||
* Whether to hide the arrow pointing to the trigger element.
|
||||
* Arrow is also automatically hidden for MenuTrigger and SubmenuTrigger contexts.
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
hideArrow?: boolean;
|
||||
}
|
||||
@@ -18,8 +18,7 @@
|
||||
|
||||
@layer components {
|
||||
.bui-Tooltip {
|
||||
box-shadow: 0 10px 15px -3px rgba(0 0 0 / 0.1),
|
||||
0 4px 6px -4px rgba(0 0 0 / 0.1);
|
||||
box-shadow: var(--bui-shadow);
|
||||
border-radius: 4px;
|
||||
background: var(--bui-bg-surface-1);
|
||||
border: 1px solid var(--bui-gray-3);
|
||||
|
||||
@@ -146,6 +146,10 @@
|
||||
--bui-scrollbar: #a0a0a03b;
|
||||
--bui-scrollbar-thumb: #a0a0a0;
|
||||
|
||||
/* Shadows */
|
||||
--bui-shadow: 0 10px 15px -3px rgba(0 0 0 / 0.1),
|
||||
0 4px 6px -4px rgba(0 0 0 / 0.1);
|
||||
|
||||
--bui-animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
@@ -226,5 +230,8 @@
|
||||
--bui-ring: #1f5493;
|
||||
--bui-scrollbar: #3636363a;
|
||||
--bui-scrollbar-thumb: #575757;
|
||||
|
||||
/* Shadows */
|
||||
--bui-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ export * from './components/Text';
|
||||
export * from './components/TextField';
|
||||
export * from './components/Tooltip';
|
||||
export * from './components/Menu';
|
||||
export * from './components/Popover';
|
||||
export * from './components/SearchField';
|
||||
export * from './components/Link';
|
||||
export * from './components/Select';
|
||||
|
||||
Reference in New Issue
Block a user