@sproutsocial/seeds-react-panel 0.3.7 → 1.0.0
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +27 -0
- package/dist/esm/index.js +48 -29
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +48 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/Panel.stories.tsx +106 -51
- package/src/Panel.tsx +25 -16
- package/src/PanelContent.tsx +20 -12
- package/src/PanelTypes.ts +15 -0
- package/src/__tests__/Panel.test.tsx +89 -9
- package/src/styles.ts +13 -3
package/.turbo/turbo-build.log
CHANGED
|
@@ -8,14 +8,14 @@ $ tsup --dts
|
|
|
8
8
|
[34mCLI[39m Cleaning output folder
|
|
9
9
|
[34mCJS[39m Build start
|
|
10
10
|
[34mESM[39m Build start
|
|
11
|
-
[32mCJS[39m [1mdist/index.js [22m[
|
|
12
|
-
[32mCJS[39m [1mdist/index.js.map [22m[
|
|
13
|
-
[32mCJS[39m ⚡️ Build success in
|
|
14
|
-
[32mESM[39m [1mdist/esm/index.js [22m[
|
|
15
|
-
[32mESM[39m [1mdist/esm/index.js.map [22m[
|
|
16
|
-
[32mESM[39m ⚡️ Build success in
|
|
11
|
+
[32mCJS[39m [1mdist/index.js [22m[32m15.02 KB[39m
|
|
12
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m28.30 KB[39m
|
|
13
|
+
[32mCJS[39m ⚡️ Build success in 85ms
|
|
14
|
+
[32mESM[39m [1mdist/esm/index.js [22m[32m11.33 KB[39m
|
|
15
|
+
[32mESM[39m [1mdist/esm/index.js.map [22m[32m27.96 KB[39m
|
|
16
|
+
[32mESM[39m ⚡️ Build success in 85ms
|
|
17
17
|
[34mDTS[39m Build start
|
|
18
|
-
[32mDTS[39m ⚡️ Build success in
|
|
19
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
20
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[
|
|
21
|
-
Done in 5.
|
|
18
|
+
[32mDTS[39m ⚡️ Build success in 4107ms
|
|
19
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m9.03 KB[39m
|
|
20
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m9.03 KB[39m
|
|
21
|
+
Done in 5.72s.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @sproutsocial/seeds-react-panel
|
|
2
2
|
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 54918f6: Add a `headerless` prop to `Panel`. When set, the panel renders children flush —
|
|
8
|
+
no built-in header/close button or content padding — and lets the children own
|
|
9
|
+
overflow scrolling. Use it for content that brings its own chrome; provide
|
|
10
|
+
`aria-label` for an accessible name. Defaults to `false`.
|
|
11
|
+
|
|
12
|
+
**Breaking:** `Panel` now automatically wraps children in `PanelContent` (padding,
|
|
13
|
+
scroll, flex layout). Consumers that previously wrapped children in `Panel.Content`
|
|
14
|
+
explicitly must remove the wrapper to avoid double-nesting.
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 54918f6: Fix side `Panel` overflowing its parent when given a vertical margin. The
|
|
19
|
+
container previously hardcoded `height: 100%`, which fills the parent's content
|
|
20
|
+
box but ignores the element's own margin — so a `mb`/`mt` pushed the panel's
|
|
21
|
+
edge (and any gap) off-screen in a `100vh` layout. It now uses
|
|
22
|
+
`align-self: stretch`, filling the flex-row parent's height while honoring its
|
|
23
|
+
own top/bottom margins, so spacing overrides (`mt`, `mb`, `mr`, `ml`, …) behave
|
|
24
|
+
correctly.
|
|
25
|
+
- 54918f6: Fix the mobile bottom-sheet footer falling below the fold. On mobile, `Panel` now
|
|
26
|
+
wraps children in `Panel.Content` (matching the desktop path) so the body scrolls
|
|
27
|
+
within the sheet, and the footer is lifted clear of the Drawer popup's 3rem
|
|
28
|
+
overscroll "bleed" so its actions stay fully visible above the fold.
|
|
29
|
+
|
|
3
30
|
## 0.3.7
|
|
4
31
|
|
|
5
32
|
### Patch Changes
|
package/dist/esm/index.js
CHANGED
|
@@ -103,7 +103,7 @@ var PanelContainer = styled.aside`
|
|
|
103
103
|
${(props) => props.$direction === "bottom" ? css`
|
|
104
104
|
width: 100%;
|
|
105
105
|
` : css`
|
|
106
|
-
|
|
106
|
+
align-self: stretch;
|
|
107
107
|
`}
|
|
108
108
|
|
|
109
109
|
${COMMON}
|
|
@@ -121,27 +121,41 @@ var PanelInner = styled.div`
|
|
|
121
121
|
overflow: hidden;
|
|
122
122
|
`;
|
|
123
123
|
var Content = styled(Box2)`
|
|
124
|
-
overflow-y: auto;
|
|
124
|
+
overflow-y: ${({ $scroll = true }) => $scroll ? "auto" : "hidden"};
|
|
125
125
|
`;
|
|
126
126
|
var Footer = styled(Box2)`
|
|
127
127
|
flex: 0 0 auto;
|
|
128
128
|
overflow: hidden;
|
|
129
|
+
/*
|
|
130
|
+
* On mobile the Panel renders inside the Drawer's bottom sheet, whose popup
|
|
131
|
+
* deliberately bleeds 3rem (--bleed) below the viewport for elastic
|
|
132
|
+
* overscroll (see seeds-react-drawer/src/styles.ts). A footer pinned to the
|
|
133
|
+
* end of that popup would land inside the bleed and clip below the fold, so
|
|
134
|
+
* we lift it by the inherited --bleed; the gap left behind shows the popup's
|
|
135
|
+
* own background, so there is no visible seam. On desktop --bleed is unset,
|
|
136
|
+
* so the 0px fallback leaves the footer untouched.
|
|
137
|
+
*/
|
|
138
|
+
margin-bottom: var(--bleed, 0px);
|
|
129
139
|
`;
|
|
130
140
|
|
|
131
141
|
// src/PanelContent.tsx
|
|
132
142
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
133
|
-
var PanelContent = ({ children, ...rest }) =>
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
var PanelContent = ({ children, ...rest }) => {
|
|
144
|
+
const { headerless } = usePanelContext();
|
|
145
|
+
return /* @__PURE__ */ jsx3(
|
|
146
|
+
Content,
|
|
147
|
+
{
|
|
148
|
+
flex: "1 1 auto",
|
|
149
|
+
minHeight: "0",
|
|
150
|
+
p: headerless ? 0 : 450,
|
|
151
|
+
$scroll: !headerless,
|
|
152
|
+
color: "text.body",
|
|
153
|
+
"data-panel-content": "",
|
|
154
|
+
...rest,
|
|
155
|
+
children
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
};
|
|
145
159
|
PanelContent.displayName = "Panel.Content";
|
|
146
160
|
|
|
147
161
|
// src/PanelFooter.tsx
|
|
@@ -221,6 +235,7 @@ var PanelComponent = ({
|
|
|
221
235
|
children,
|
|
222
236
|
closeButtonLabel,
|
|
223
237
|
header,
|
|
238
|
+
headerless = false,
|
|
224
239
|
footer,
|
|
225
240
|
title,
|
|
226
241
|
titleId,
|
|
@@ -245,17 +260,28 @@ var PanelComponent = ({
|
|
|
245
260
|
const { isPanelOpen, closePanel } = panelContext;
|
|
246
261
|
const isMobile = useIsMobile(mobileBreakpoint);
|
|
247
262
|
const enrichedValue = React6.useMemo(
|
|
248
|
-
() => ({ ...panelContext, closeButtonLabel }),
|
|
249
|
-
[panelContext, closeButtonLabel]
|
|
263
|
+
() => ({ ...panelContext, closeButtonLabel, headerless }),
|
|
264
|
+
[panelContext, closeButtonLabel, headerless]
|
|
250
265
|
);
|
|
251
266
|
const prevChildrenRef = React6.useRef(null);
|
|
252
267
|
if (isPanelOpen) {
|
|
253
268
|
prevChildrenRef.current = children;
|
|
254
269
|
}
|
|
255
270
|
const renderedChildren = isPanelOpen ? children : prevChildrenRef.current;
|
|
256
|
-
const effectiveAriaLabelledBy = ariaLabelledByProp ?? (header == null ? titleId : void 0);
|
|
271
|
+
const effectiveAriaLabelledBy = ariaLabelledByProp ?? (!headerless && header == null ? titleId : void 0);
|
|
257
272
|
if (isMobile) {
|
|
258
|
-
const shouldRenderActionsHeader = header == null && actionsInHeader === true && !!actions && actions.length > 0;
|
|
273
|
+
const shouldRenderActionsHeader = !headerless && header == null && actionsInHeader === true && !!actions && actions.length > 0;
|
|
274
|
+
let defaultMobileHeader = null;
|
|
275
|
+
if (!headerless) {
|
|
276
|
+
defaultMobileHeader = shouldRenderActionsHeader ? /* @__PURE__ */ jsx6(
|
|
277
|
+
PanelMobileActionsHeader,
|
|
278
|
+
{
|
|
279
|
+
title,
|
|
280
|
+
titleId,
|
|
281
|
+
actions
|
|
282
|
+
}
|
|
283
|
+
) : /* @__PURE__ */ jsx6(Drawer2.Header, { title, id: titleId });
|
|
284
|
+
}
|
|
259
285
|
return /* @__PURE__ */ jsx6(PanelContext.Provider, { value: enrichedValue, children: /* @__PURE__ */ jsxs3(
|
|
260
286
|
Drawer2,
|
|
261
287
|
{
|
|
@@ -275,15 +301,8 @@ var PanelComponent = ({
|
|
|
275
301
|
"aria-labelledby": effectiveAriaLabelledBy,
|
|
276
302
|
"aria-label": ariaLabel,
|
|
277
303
|
children: [
|
|
278
|
-
header ??
|
|
279
|
-
|
|
280
|
-
{
|
|
281
|
-
title,
|
|
282
|
-
titleId,
|
|
283
|
-
actions
|
|
284
|
-
}
|
|
285
|
-
) : /* @__PURE__ */ jsx6(Drawer2.Header, { title, id: titleId })),
|
|
286
|
-
children,
|
|
304
|
+
header ?? defaultMobileHeader,
|
|
305
|
+
/* @__PURE__ */ jsx6(PanelContent, { children }),
|
|
287
306
|
footer
|
|
288
307
|
]
|
|
289
308
|
}
|
|
@@ -302,8 +321,8 @@ var PanelComponent = ({
|
|
|
302
321
|
"aria-labelledby": effectiveAriaLabelledBy,
|
|
303
322
|
"aria-label": ariaLabel,
|
|
304
323
|
children: /* @__PURE__ */ jsxs3(PanelInner, { $direction: direction, $width: width, children: [
|
|
305
|
-
header ?? /* @__PURE__ */ jsx6(PanelHeader, { title, id: titleId }),
|
|
306
|
-
renderedChildren,
|
|
324
|
+
header ?? (headerless ? null : /* @__PURE__ */ jsx6(PanelHeader, { title, id: titleId })),
|
|
325
|
+
/* @__PURE__ */ jsx6(PanelContent, { children: renderedChildren }),
|
|
307
326
|
footer
|
|
308
327
|
] })
|
|
309
328
|
}
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/Panel.tsx","../../src/PanelContext.tsx","../../src/PanelHeader.tsx","../../src/PanelCloseButton.tsx","../../src/PanelContent.tsx","../../src/styles.ts","../../src/PanelFooter.tsx","../../src/PanelMobileActionsHeader.tsx","../../src/PanelProvider.tsx","../../src/PanelTypes.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport Drawer from \"@sproutsocial/seeds-react-drawer\";\nimport { useIsMobile } from \"@sproutsocial/seeds-react-hooks\";\nimport { usePanelContext, PanelContext } from \"./PanelContext\";\nimport { PanelHeader } from \"./PanelHeader\";\nimport { PanelContent } from \"./PanelContent\";\nimport { PanelCloseButton } from \"./PanelCloseButton\";\nimport { PanelFooter } from \"./PanelFooter\";\nimport { PanelMobileActionsHeader } from \"./PanelMobileActionsHeader\";\nimport { PanelContainer, PanelInner } from \"./styles\";\nimport type { TypePanelProps, TypePanelContext } from \"./PanelTypes\";\n\nconst PanelComponent = ({\n children,\n closeButtonLabel,\n header,\n footer,\n title,\n titleId,\n direction = \"right\",\n width = 384,\n gap = 4,\n mobileBreakpoint,\n zIndex,\n id,\n snapPoints,\n defaultSnapPoint,\n snapPoint,\n onSnapPointChange,\n snapToSequentialPoints,\n actions,\n actionsInHeader,\n \"aria-labelledby\": ariaLabelledByProp,\n \"aria-label\": ariaLabel,\n ...rest\n}: TypePanelProps) => {\n const panelContext = usePanelContext();\n const { isPanelOpen, closePanel } = panelContext;\n const isMobile = useIsMobile(mobileBreakpoint);\n\n // Expose closeButtonLabel through context so PanelCloseButton can read it\n // without prop drilling — same pattern as DrawerContext.\n const enrichedValue = React.useMemo<TypePanelContext>(\n () => ({ ...panelContext, closeButtonLabel }),\n [panelContext, closeButtonLabel]\n );\n\n // Freeze the last-rendered children during the collapse so consumers that\n // conditionally render content don't produce an empty panel shell while the\n // flex-basis animation runs.\n const prevChildrenRef = React.useRef<React.ReactNode>(null);\n if (isPanelOpen) {\n prevChildrenRef.current = children;\n }\n const renderedChildren = isPanelOpen ? children : prevChildrenRef.current;\n\n // When the consumer relies on the default header, its title element renders\n // with `id={titleId}`. Auto-wire `aria-labelledby` so the dialog/aside has an\n // accessible name without forcing every caller to repeat the id.\n const effectiveAriaLabelledBy =\n ariaLabelledByProp ?? (header == null ? titleId : undefined);\n\n if (isMobile) {\n // With `actionsInHeader`, Drawer suppresses the floating rail and expects\n // the consumer to render the actions inside a custom header. Auto-compose\n // one (title + action pills + close pill) when the consumer hasn't\n // supplied their own header; otherwise we'd drop `actions` on the floor.\n const shouldRenderActionsHeader =\n header == null &&\n actionsInHeader === true &&\n !!actions &&\n actions.length > 0;\n\n return (\n <PanelContext.Provider value={enrichedValue}>\n <Drawer\n {...rest}\n open={isPanelOpen}\n onClose={closePanel}\n direction=\"bottom\"\n closeButtonLabel={closeButtonLabel}\n zIndex={zIndex}\n snapPoints={snapPoints}\n defaultSnapPoint={defaultSnapPoint}\n snapPoint={snapPoint}\n onSnapPointChange={onSnapPointChange}\n snapToSequentialPoints={snapToSequentialPoints}\n actions={actions}\n actionsInHeader={actionsInHeader}\n aria-labelledby={effectiveAriaLabelledBy}\n aria-label={ariaLabel}\n >\n {header ??\n (shouldRenderActionsHeader ? (\n <PanelMobileActionsHeader\n title={title}\n titleId={titleId}\n actions={actions!}\n />\n ) : (\n <Drawer.Header title={title} id={titleId} />\n ))}\n {children}\n {footer}\n </Drawer>\n </PanelContext.Provider>\n );\n }\n\n return (\n <PanelContext.Provider value={enrichedValue}>\n <PanelContainer\n {...rest}\n $isOpen={isPanelOpen}\n $direction={direction}\n $width={width}\n $gap={gap}\n data-qa-panel={id}\n data-qa-panel-isopen={isPanelOpen}\n aria-labelledby={effectiveAriaLabelledBy}\n aria-label={ariaLabel}\n >\n <PanelInner $direction={direction} $width={width}>\n {header ?? <PanelHeader title={title} id={titleId} />}\n {renderedChildren}\n {footer}\n </PanelInner>\n </PanelContainer>\n </PanelContext.Provider>\n );\n};\n\nPanelComponent.Header = PanelHeader;\nPanelComponent.Content = PanelContent;\nPanelComponent.CloseButton = PanelCloseButton;\nPanelComponent.Footer = PanelFooter;\n\nexport const Panel = PanelComponent;\nexport default PanelComponent;\n","import * as React from \"react\";\nimport type { TypePanelContext } from \"./PanelTypes\";\n\nexport const PanelContext = React.createContext<TypePanelContext | null>(null);\n\nexport const usePanelContext = (): TypePanelContext => {\n const ctx = React.useContext(PanelContext);\n if (!ctx) {\n throw new Error(\n \"usePanelContext must be used within a <PanelProvider>. Wrap the consuming tree in <PanelProvider> to provide isPanelOpen / togglePanel.\"\n );\n }\n return ctx;\n};\n","import Box from \"@sproutsocial/seeds-react-box\";\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport { usePanelContext } from \"./PanelContext\";\nimport { PanelCloseButton } from \"./PanelCloseButton\";\nimport type { TypePanelHeaderProps } from \"./PanelTypes\";\n\nexport const PanelHeader = ({\n title = \"\",\n id = undefined,\n children,\n render,\n ...rest\n}: TypePanelHeaderProps) => {\n const panelContext = usePanelContext();\n\n if (render) {\n return render(panelContext);\n }\n\n return (\n <Box\n display=\"flex\"\n flex=\"0 0 auto\"\n justifyContent=\"space-between\"\n alignItems=\"center\"\n p={400}\n borderBottom=\"1px solid\"\n borderColor=\"container.border.base\"\n {...rest}\n >\n {children || (\n <>\n <Text.Headline id={id}>{title}</Text.Headline>\n <PanelCloseButton />\n </>\n )}\n </Box>\n );\n};\n\nPanelHeader.displayName = \"Panel.Header\";\n","import * as React from \"react\";\nimport Button from \"@sproutsocial/seeds-react-button\";\nimport Icon from \"@sproutsocial/seeds-react-icon\";\nimport { usePanelContext } from \"./PanelContext\";\nimport type { TypePanelCloseButtonProps } from \"./PanelTypes\";\n\nexport const PanelCloseButton = (props: TypePanelCloseButtonProps) => {\n const panelContext = usePanelContext();\n\n if (props.render) {\n return props.render(panelContext) ?? null;\n }\n\n return (\n <Button\n appearance=\"pill\"\n aria-label={panelContext.closeButtonLabel}\n onClick={panelContext.closePanel}\n {...props}\n >\n {props.children || <Icon aria-hidden name=\"x-outline\" />}\n </Button>\n );\n};\n\nPanelCloseButton.displayName = \"Panel.CloseButton\";\n","import * as React from \"react\";\nimport { Content } from \"./styles\";\nimport type { TypePanelContentProps } from \"./PanelTypes\";\n\nexport const PanelContent = ({ children, ...rest }: TypePanelContentProps) => (\n <Content\n flex=\"1 1 auto\"\n minHeight=\"0\"\n p={450}\n color=\"text.body\"\n data-panel-content=\"\"\n {...rest}\n >\n {children}\n </Content>\n);\n\nPanelContent.displayName = \"Panel.Content\";\n","import styled, { css } from \"styled-components\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeSystemCommonProps } from \"@sproutsocial/seeds-react-system-props\";\nimport { MOTION_DURATION_MEDIUM } from \"@sproutsocial/seeds-motion/unitless\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport type { PanelDirection } from \"./PanelTypes\";\n\ninterface PanelContainerProps extends TypeSystemCommonProps {\n $isOpen: boolean;\n $direction: PanelDirection;\n $width: number;\n $gap: number;\n}\n\nconst gapSide = (direction: PanelDirection): \"left\" | \"right\" | \"top\" => {\n if (direction === \"right\") return \"left\";\n if (direction === \"left\") return \"right\";\n return \"top\";\n};\n\nexport const PanelContainer = styled.aside<PanelContainerProps>`\n background: ${(props) => props.theme.colors.container.background.base};\n flex-grow: 0;\n flex-shrink: 0;\n overflow: hidden;\n transition: flex-basis ${MOTION_DURATION_MEDIUM}s ease-in-out,\n margin ${MOTION_DURATION_MEDIUM}s ease-in-out;\n flex-basis: ${(props) => (props.$isOpen ? `${props.$width}px` : \"0px\")};\n margin-${(props) => gapSide(props.$direction)}: ${(props) =>\n props.$isOpen && props.$gap ? `${props.$gap}px` : \"0px\"};\n border-radius: ${({ theme }) => theme.radii[800]};\n\n ${(props) =>\n props.$direction === \"bottom\"\n ? css`\n width: 100%;\n `\n : css`\n height: 100%;\n `}\n\n ${COMMON}\n`;\n\nexport const PanelInner = styled.div<{\n $direction: PanelDirection;\n $width: number;\n}>`\n ${(props) =>\n props.$direction === \"bottom\"\n ? css`\n min-height: ${props.$width}px;\n width: 100%;\n `\n : css`\n min-width: ${props.$width}px;\n height: 100%;\n `}\n display: flex;\n flex-direction: column;\n overflow: hidden;\n`;\n\nexport const Content = styled(Box)`\n overflow-y: auto;\n`;\n\nexport const Footer = styled(Box)`\n flex: 0 0 auto;\n overflow: hidden;\n`;\n","import * as React from \"react\";\nimport { Footer } from \"./styles\";\nimport type { TypePanelFooterProps } from \"./PanelTypes\";\n\nexport const PanelFooter = ({ children, ...rest }: TypePanelFooterProps) => (\n <Footer p={450} data-panel-footer=\"\" {...rest}>\n {children}\n </Footer>\n);\n\nPanelFooter.displayName = \"Panel.Footer\";\n","import * as React from \"react\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport Button from \"@sproutsocial/seeds-react-button\";\nimport Drawer from \"@sproutsocial/seeds-react-drawer\";\nimport Icon from \"@sproutsocial/seeds-react-icon\";\n// eslint-disable-next-line import/no-deprecated\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport type { TypeDrawerActionProps } from \"@sproutsocial/seeds-react-drawer\";\n\ninterface TypePanelMobileActionsHeaderProps {\n title?: string;\n titleId?: string;\n actions: TypeDrawerActionProps[];\n}\n\n/**\n * Mobile bottom-sheet header used by Panel when `actionsInHeader` is set and\n * no custom `header` is provided. Renders the title on the left and the\n * forwarded actions plus a close button as pills on the right — mirrors the\n * pattern in `seeds-react-peek-in/PeekIn.tsx`. The Drawer's floating rail is\n * suppressed in this combination (`actionsInHeader` opts out), so without this\n * header the actions would render nowhere.\n */\nexport const PanelMobileActionsHeader = ({\n title,\n titleId,\n actions,\n}: TypePanelMobileActionsHeaderProps) => (\n <Drawer.Header\n render={(ctx) => (\n <Box\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"space-between\"\n pt={400}\n px={450}\n flex=\"0 0 auto\"\n >\n <Text\n as=\"h2\"\n fontSize={400}\n fontWeight=\"semibold\"\n color=\"text.headline\"\n id={titleId}\n >\n {title}\n </Text>\n <Box display=\"flex\" alignItems=\"center\" gap={200}>\n {actions.map((action, i) => (\n <Button\n key={i}\n appearance=\"pill\"\n aria-label={action[\"aria-label\"]}\n onClick={action.onClick}\n disabled={action.disabled}\n >\n {action.iconName && <Icon aria-hidden name={action.iconName} />}\n </Button>\n ))}\n <Button\n appearance=\"pill\"\n aria-label={ctx.closeButtonLabel}\n onClick={ctx.onClose}\n >\n <Icon aria-hidden name=\"x-outline\" />\n </Button>\n </Box>\n </Box>\n )}\n />\n);\n\nPanelMobileActionsHeader.displayName = \"PanelMobileActionsHeader\";\n","import * as React from \"react\";\nimport { PanelContext } from \"./PanelContext\";\nimport type { TypePanelContext, TypePanelProviderProps } from \"./PanelTypes\";\n\nexport const PanelProvider = ({\n children,\n defaultOpen = false,\n open: controlledOpen,\n onOpenChange,\n}: TypePanelProviderProps) => {\n const [internalOpen, setInternalOpen] = React.useState(defaultOpen);\n const isControlled = controlledOpen !== undefined;\n const isPanelOpen = isControlled ? controlledOpen : internalOpen;\n\n const setOpen = React.useCallback(\n (next: boolean) => {\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange]\n );\n\n const openPanel = React.useCallback(() => setOpen(true), [setOpen]);\n const closePanel = React.useCallback(() => setOpen(false), [setOpen]);\n const togglePanel = React.useCallback(\n () => setOpen(!isPanelOpen),\n [setOpen, isPanelOpen]\n );\n\n const value = React.useMemo<TypePanelContext>(\n () => ({ isPanelOpen, openPanel, closePanel, togglePanel }),\n [isPanelOpen, openPanel, closePanel, togglePanel]\n );\n\n return (\n <PanelContext.Provider value={value}>{children}</PanelContext.Provider>\n );\n};\n","import * as React from \"react\";\nimport type {\n TypeSystemCommonProps,\n TypeStyledComponentsCommonProps,\n} from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeBoxProps } from \"@sproutsocial/seeds-react-box\";\nimport type { TypeButtonProps } from \"@sproutsocial/seeds-react-button\";\nimport type {\n TypeDrawerSnapPoint,\n TypeDrawerActionProps,\n} from \"@sproutsocial/seeds-react-drawer\";\n\nexport type { TypeDrawerActionProps as TypePanelActionProps };\n\nexport type PanelDirection = \"left\" | \"right\" | \"bottom\";\n\nexport interface TypePanelContext {\n isPanelOpen: boolean;\n togglePanel: () => void;\n openPanel: () => void;\n closePanel: () => void;\n closeButtonLabel?: string;\n}\n\nexport interface TypePanelProviderProps {\n children: React.ReactNode;\n /** Initial open state for uncontrolled usage. */\n defaultOpen?: boolean;\n /** Controlled open state. When provided, the provider becomes controlled. */\n open?: boolean;\n /** Called whenever the open state changes. Required for controlled usage. */\n onOpenChange?: (open: boolean) => void;\n}\n\nexport interface TypePanelProps\n extends TypeStyledComponentsCommonProps,\n TypeSystemCommonProps,\n Omit<React.ComponentPropsWithoutRef<\"div\">, \"color\"> {\n children: React.ReactNode;\n\n /** Label for the close button. Usually \"Close\". */\n closeButtonLabel: string;\n\n /**\n * Custom header content. When provided, replaces the auto-rendered default\n * header entirely — `title` and `titleId` are ignored.\n */\n header?: React.ReactNode;\n\n /** Custom footer content. Not rendered when omitted. */\n footer?: React.ReactNode;\n\n /** Title shown in the auto-rendered default header. Ignored when `header` is provided. */\n title?: string;\n\n /**\n * Sets the `id` on the title element of the auto-rendered header. When the\n * default header is used, Panel automatically wires `aria-labelledby` to this\n * id so the dialog/aside has an accessible name. Pass an explicit\n * `aria-labelledby` to override (e.g. when pointing at a different element).\n * Ignored when `header` is provided — supply your own `aria-labelledby` in\n * that case.\n */\n titleId?: string;\n\n /** Side the panel anchors to on desktop. Defaults to \"right\". */\n direction?: PanelDirection;\n\n /**\n * Width (px) when direction is \"left\" or \"right\", or height (px) when\n * direction is \"bottom\". Defaults to 384 to match the existing web-app-core\n * Panel.\n */\n width?: number;\n\n /**\n * Visual gap (px) between the panel and the adjacent content. Applied as\n * margin on the side facing the content (left when direction=\"right\",\n * right when direction=\"left\", top when direction=\"bottom\"). Animates with\n * the panel's open/close transition and collapses to 0 when closed so the\n * gap does not remain visible alongside a zero-width panel. Defaults to 0.\n */\n gap?: number;\n\n /**\n * Forwarded to `useIsMobile`. Accepts a px number or any CSS media-query\n * length. Below this width, the panel renders as an overlay bottom sheet\n * (via seeds-react-drawer). Defaults to the theme's `breakpoints.sm`.\n */\n mobileBreakpoint?: number | string;\n\n /** Applies only to the mobile bottom-sheet rendering. */\n zIndex?: number;\n\n /** Optional id used for data-qa-panel attributes. */\n id?: string;\n\n /**\n * Snap points the panel can rest at when rendered as a mobile bottom sheet.\n * Numbers 0–1 are viewport-height fractions; numbers > 1 are pixels; strings\n * accept `px`/`rem` (e.g. `\"480px\"`, `\"30rem\"`). Order matters — the last\n * entry is the \"expanded\" state. Ignored on the desktop side-panel rendering.\n */\n snapPoints?: TypeDrawerSnapPoint[];\n\n /** Initial snap point for uncontrolled use. Mobile only. */\n defaultSnapPoint?: TypeDrawerSnapPoint | null;\n\n /** Controlled active snap point. Pair with `onSnapPointChange`. Mobile only. */\n snapPoint?: TypeDrawerSnapPoint | null;\n\n /** Fires when the active snap point changes. Mobile only. */\n onSnapPointChange?: (snapPoint: TypeDrawerSnapPoint | null) => void;\n\n /**\n * When true, fast swipes can't skip past adjacent snap points. Mobile only.\n */\n snapToSequentialPoints?: boolean;\n\n /**\n * Action buttons shown in a floating rail above the panel on mobile (the\n * bottom-sheet rendering). Ignored on desktop side-panel rendering — pass\n * actions through `header`/`footer` slots there. The rail owns the close\n * button so the default header omits its built-in close affordance.\n */\n actions?: TypeDrawerActionProps[];\n\n /**\n * Opt out of the floating mobile rail and render the `actions` inline in the\n * header as pill buttons instead. Useful for nested bottom sheets and\n * snap-point layouts where the floating rail would collide with surrounding\n * UI. Defaults to `false`.\n *\n * When `header` is omitted, Panel auto-composes a mobile header that renders\n * the title, the `actions` as pills, and a close-button pill. When `header`\n * is provided, the consumer is responsible for rendering the actions inside\n * their own header — `actions` is forwarded for context only.\n */\n actionsInHeader?: boolean;\n}\n\nexport interface TypePanelHeaderProps extends TypeBoxProps {\n title?: string;\n /**\n * When children are provided, `<Panel.CloseButton />` is NOT rendered\n * automatically. Include it yourself to preserve keyboard accessibility.\n */\n children?: React.ReactNode;\n /** Render-prop override that receives the parent panel context. */\n render?: React.FC<TypePanelContext>;\n}\n\nexport interface TypePanelContentProps extends TypeBoxProps {\n children?: React.ReactNode;\n}\n\nexport interface TypePanelFooterProps extends TypeBoxProps {\n children?: React.ReactNode;\n}\n\nexport interface TypePanelCloseButtonProps\n extends Omit<TypeButtonProps, \"children\"> {\n /** Render-prop override that receives the parent panel context. */\n render?: React.FC<TypePanelContext>;\n children?: React.ReactNode;\n}\n","import Panel from \"./Panel\";\n\nexport default Panel;\nexport { Panel };\nexport { PanelContext, usePanelContext } from \"./PanelContext\";\nexport { PanelProvider } from \"./PanelProvider\";\nexport { PanelHeader } from \"./PanelHeader\";\nexport { PanelContent } from \"./PanelContent\";\nexport { PanelCloseButton } from \"./PanelCloseButton\";\nexport { PanelFooter } from \"./PanelFooter\";\nexport * from \"./PanelTypes\";\n"],"mappings":"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","names":["React","Drawer","jsx","Box","jsx","jsx","Box","Button","Icon","Text","jsx","jsxs","jsx","jsxs","Drawer","React","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../../src/Panel.tsx","../../src/PanelContext.tsx","../../src/PanelHeader.tsx","../../src/PanelCloseButton.tsx","../../src/PanelContent.tsx","../../src/styles.ts","../../src/PanelFooter.tsx","../../src/PanelMobileActionsHeader.tsx","../../src/PanelProvider.tsx","../../src/PanelTypes.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport Drawer from \"@sproutsocial/seeds-react-drawer\";\nimport { useIsMobile } from \"@sproutsocial/seeds-react-hooks\";\nimport { usePanelContext, PanelContext } from \"./PanelContext\";\nimport { PanelHeader } from \"./PanelHeader\";\nimport { PanelContent } from \"./PanelContent\";\nimport { PanelCloseButton } from \"./PanelCloseButton\";\nimport { PanelFooter } from \"./PanelFooter\";\nimport { PanelMobileActionsHeader } from \"./PanelMobileActionsHeader\";\nimport { PanelContainer, PanelInner } from \"./styles\";\nimport type { TypePanelProps, TypePanelContext } from \"./PanelTypes\";\n\nconst PanelComponent = ({\n children,\n closeButtonLabel,\n header,\n headerless = false,\n footer,\n title,\n titleId,\n direction = \"right\",\n width = 384,\n gap = 4,\n mobileBreakpoint,\n zIndex,\n id,\n snapPoints,\n defaultSnapPoint,\n snapPoint,\n onSnapPointChange,\n snapToSequentialPoints,\n actions,\n actionsInHeader,\n \"aria-labelledby\": ariaLabelledByProp,\n \"aria-label\": ariaLabel,\n ...rest\n}: TypePanelProps) => {\n const panelContext = usePanelContext();\n const { isPanelOpen, closePanel } = panelContext;\n const isMobile = useIsMobile(mobileBreakpoint);\n\n // Expose closeButtonLabel through context so PanelCloseButton can read it\n // without prop drilling — same pattern as DrawerContext.\n const enrichedValue = React.useMemo<TypePanelContext>(\n () => ({ ...panelContext, closeButtonLabel, headerless }),\n [panelContext, closeButtonLabel, headerless]\n );\n\n // Freeze the last-rendered children during the collapse so consumers that\n // conditionally render content don't produce an empty panel shell while the\n // flex-basis animation runs.\n const prevChildrenRef = React.useRef<React.ReactNode>(null);\n if (isPanelOpen) {\n prevChildrenRef.current = children;\n }\n const renderedChildren = isPanelOpen ? children : prevChildrenRef.current;\n\n // When the consumer relies on the default header, its title element renders\n // with `id={titleId}`. Auto-wire `aria-labelledby` so the dialog/aside has an\n // accessible name without forcing every caller to repeat the id.\n const effectiveAriaLabelledBy =\n ariaLabelledByProp ?? (!headerless && header == null ? titleId : undefined);\n\n if (isMobile) {\n // With `actionsInHeader`, Drawer suppresses the floating rail and expects\n // the consumer to render the actions inside a custom header. Auto-compose\n // one (title + action pills + close pill) when the consumer hasn't\n // supplied their own header; otherwise we'd drop `actions` on the floor.\n const shouldRenderActionsHeader =\n !headerless &&\n header == null &&\n actionsInHeader === true &&\n !!actions &&\n actions.length > 0;\n\n // Headerless suppresses the built-in header entirely; the children own all\n // chrome in that mode.\n let defaultMobileHeader: React.ReactNode = null;\n if (!headerless) {\n defaultMobileHeader = shouldRenderActionsHeader ? (\n <PanelMobileActionsHeader\n title={title}\n titleId={titleId}\n actions={actions!}\n />\n ) : (\n <Drawer.Header title={title} id={titleId} />\n );\n }\n\n return (\n <PanelContext.Provider value={enrichedValue}>\n <Drawer\n {...rest}\n open={isPanelOpen}\n onClose={closePanel}\n direction=\"bottom\"\n closeButtonLabel={closeButtonLabel}\n zIndex={zIndex}\n snapPoints={snapPoints}\n defaultSnapPoint={defaultSnapPoint}\n snapPoint={snapPoint}\n onSnapPointChange={onSnapPointChange}\n snapToSequentialPoints={snapToSequentialPoints}\n actions={actions}\n actionsInHeader={actionsInHeader}\n aria-labelledby={effectiveAriaLabelledBy}\n aria-label={ariaLabel}\n >\n {header ?? defaultMobileHeader}\n <PanelContent>{children}</PanelContent>\n {footer}\n </Drawer>\n </PanelContext.Provider>\n );\n }\n\n return (\n <PanelContext.Provider value={enrichedValue}>\n <PanelContainer\n {...rest}\n $isOpen={isPanelOpen}\n $direction={direction}\n $width={width}\n $gap={gap}\n data-qa-panel={id}\n data-qa-panel-isopen={isPanelOpen}\n aria-labelledby={effectiveAriaLabelledBy}\n aria-label={ariaLabel}\n >\n <PanelInner $direction={direction} $width={width}>\n {header ??\n (headerless ? null : <PanelHeader title={title} id={titleId} />)}\n <PanelContent>{renderedChildren}</PanelContent>\n {footer}\n </PanelInner>\n </PanelContainer>\n </PanelContext.Provider>\n );\n};\n\nPanelComponent.Header = PanelHeader;\nPanelComponent.Content = PanelContent;\nPanelComponent.CloseButton = PanelCloseButton;\nPanelComponent.Footer = PanelFooter;\n\nexport const Panel = PanelComponent;\nexport default PanelComponent;\n","import * as React from \"react\";\nimport type { TypePanelContext } from \"./PanelTypes\";\n\nexport const PanelContext = React.createContext<TypePanelContext | null>(null);\n\nexport const usePanelContext = (): TypePanelContext => {\n const ctx = React.useContext(PanelContext);\n if (!ctx) {\n throw new Error(\n \"usePanelContext must be used within a <PanelProvider>. Wrap the consuming tree in <PanelProvider> to provide isPanelOpen / togglePanel.\"\n );\n }\n return ctx;\n};\n","import Box from \"@sproutsocial/seeds-react-box\";\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport { usePanelContext } from \"./PanelContext\";\nimport { PanelCloseButton } from \"./PanelCloseButton\";\nimport type { TypePanelHeaderProps } from \"./PanelTypes\";\n\nexport const PanelHeader = ({\n title = \"\",\n id = undefined,\n children,\n render,\n ...rest\n}: TypePanelHeaderProps) => {\n const panelContext = usePanelContext();\n\n if (render) {\n return render(panelContext);\n }\n\n return (\n <Box\n display=\"flex\"\n flex=\"0 0 auto\"\n justifyContent=\"space-between\"\n alignItems=\"center\"\n p={400}\n borderBottom=\"1px solid\"\n borderColor=\"container.border.base\"\n {...rest}\n >\n {children || (\n <>\n <Text.Headline id={id}>{title}</Text.Headline>\n <PanelCloseButton />\n </>\n )}\n </Box>\n );\n};\n\nPanelHeader.displayName = \"Panel.Header\";\n","import * as React from \"react\";\nimport Button from \"@sproutsocial/seeds-react-button\";\nimport Icon from \"@sproutsocial/seeds-react-icon\";\nimport { usePanelContext } from \"./PanelContext\";\nimport type { TypePanelCloseButtonProps } from \"./PanelTypes\";\n\nexport const PanelCloseButton = (props: TypePanelCloseButtonProps) => {\n const panelContext = usePanelContext();\n\n if (props.render) {\n return props.render(panelContext) ?? null;\n }\n\n return (\n <Button\n appearance=\"pill\"\n aria-label={panelContext.closeButtonLabel}\n onClick={panelContext.closePanel}\n {...props}\n >\n {props.children || <Icon aria-hidden name=\"x-outline\" />}\n </Button>\n );\n};\n\nPanelCloseButton.displayName = \"Panel.CloseButton\";\n","import * as React from \"react\";\nimport { Content } from \"./styles\";\nimport { usePanelContext } from \"./PanelContext\";\nimport type { TypePanelContentProps } from \"./PanelTypes\";\n\nexport const PanelContent = ({ children, ...rest }: TypePanelContentProps) => {\n // In headerless mode the content renders flush and defers overflow scrolling\n // to its children, so the panel can host content that owns its own chrome.\n const { headerless } = usePanelContext();\n\n return (\n <Content\n flex=\"1 1 auto\"\n minHeight=\"0\"\n p={headerless ? 0 : 450}\n $scroll={!headerless}\n color=\"text.body\"\n data-panel-content=\"\"\n {...rest}\n >\n {children}\n </Content>\n );\n};\n\nPanelContent.displayName = \"Panel.Content\";\n","import styled, { css } from \"styled-components\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeSystemCommonProps } from \"@sproutsocial/seeds-react-system-props\";\nimport { MOTION_DURATION_MEDIUM } from \"@sproutsocial/seeds-motion/unitless\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport type { PanelDirection } from \"./PanelTypes\";\n\ninterface PanelContainerProps extends TypeSystemCommonProps {\n $isOpen: boolean;\n $direction: PanelDirection;\n $width: number;\n $gap: number;\n}\n\nconst gapSide = (direction: PanelDirection): \"left\" | \"right\" | \"top\" => {\n if (direction === \"right\") return \"left\";\n if (direction === \"left\") return \"right\";\n return \"top\";\n};\n\nexport const PanelContainer = styled.aside<PanelContainerProps>`\n background: ${(props) => props.theme.colors.container.background.base};\n flex-grow: 0;\n flex-shrink: 0;\n overflow: hidden;\n transition: flex-basis ${MOTION_DURATION_MEDIUM}s ease-in-out,\n margin ${MOTION_DURATION_MEDIUM}s ease-in-out;\n flex-basis: ${(props) => (props.$isOpen ? `${props.$width}px` : \"0px\")};\n margin-${(props) => gapSide(props.$direction)}: ${(props) =>\n props.$isOpen && props.$gap ? `${props.$gap}px` : \"0px\"};\n border-radius: ${({ theme }) => theme.radii[800]};\n\n ${(props) =>\n props.$direction === \"bottom\"\n ? css`\n width: 100%;\n `\n : css`\n align-self: stretch;\n `}\n\n ${COMMON}\n`;\n\nexport const PanelInner = styled.div<{\n $direction: PanelDirection;\n $width: number;\n}>`\n ${(props) =>\n props.$direction === \"bottom\"\n ? css`\n min-height: ${props.$width}px;\n width: 100%;\n `\n : css`\n min-width: ${props.$width}px;\n height: 100%;\n `}\n display: flex;\n flex-direction: column;\n overflow: hidden;\n`;\n\nexport const Content = styled(Box)<{ $scroll?: boolean }>`\n overflow-y: ${({ $scroll = true }) => ($scroll ? \"auto\" : \"hidden\")};\n`;\n\nexport const Footer = styled(Box)`\n flex: 0 0 auto;\n overflow: hidden;\n /*\n * On mobile the Panel renders inside the Drawer's bottom sheet, whose popup\n * deliberately bleeds 3rem (--bleed) below the viewport for elastic\n * overscroll (see seeds-react-drawer/src/styles.ts). A footer pinned to the\n * end of that popup would land inside the bleed and clip below the fold, so\n * we lift it by the inherited --bleed; the gap left behind shows the popup's\n * own background, so there is no visible seam. On desktop --bleed is unset,\n * so the 0px fallback leaves the footer untouched.\n */\n margin-bottom: var(--bleed, 0px);\n`;\n","import * as React from \"react\";\nimport { Footer } from \"./styles\";\nimport type { TypePanelFooterProps } from \"./PanelTypes\";\n\nexport const PanelFooter = ({ children, ...rest }: TypePanelFooterProps) => (\n <Footer p={450} data-panel-footer=\"\" {...rest}>\n {children}\n </Footer>\n);\n\nPanelFooter.displayName = \"Panel.Footer\";\n","import * as React from \"react\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport Button from \"@sproutsocial/seeds-react-button\";\nimport Drawer from \"@sproutsocial/seeds-react-drawer\";\nimport Icon from \"@sproutsocial/seeds-react-icon\";\n// eslint-disable-next-line import/no-deprecated\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport type { TypeDrawerActionProps } from \"@sproutsocial/seeds-react-drawer\";\n\ninterface TypePanelMobileActionsHeaderProps {\n title?: string;\n titleId?: string;\n actions: TypeDrawerActionProps[];\n}\n\n/**\n * Mobile bottom-sheet header used by Panel when `actionsInHeader` is set and\n * no custom `header` is provided. Renders the title on the left and the\n * forwarded actions plus a close button as pills on the right — mirrors the\n * pattern in `seeds-react-peek-in/PeekIn.tsx`. The Drawer's floating rail is\n * suppressed in this combination (`actionsInHeader` opts out), so without this\n * header the actions would render nowhere.\n */\nexport const PanelMobileActionsHeader = ({\n title,\n titleId,\n actions,\n}: TypePanelMobileActionsHeaderProps) => (\n <Drawer.Header\n render={(ctx) => (\n <Box\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"space-between\"\n pt={400}\n px={450}\n flex=\"0 0 auto\"\n >\n <Text\n as=\"h2\"\n fontSize={400}\n fontWeight=\"semibold\"\n color=\"text.headline\"\n id={titleId}\n >\n {title}\n </Text>\n <Box display=\"flex\" alignItems=\"center\" gap={200}>\n {actions.map((action, i) => (\n <Button\n key={i}\n appearance=\"pill\"\n aria-label={action[\"aria-label\"]}\n onClick={action.onClick}\n disabled={action.disabled}\n >\n {action.iconName && <Icon aria-hidden name={action.iconName} />}\n </Button>\n ))}\n <Button\n appearance=\"pill\"\n aria-label={ctx.closeButtonLabel}\n onClick={ctx.onClose}\n >\n <Icon aria-hidden name=\"x-outline\" />\n </Button>\n </Box>\n </Box>\n )}\n />\n);\n\nPanelMobileActionsHeader.displayName = \"PanelMobileActionsHeader\";\n","import * as React from \"react\";\nimport { PanelContext } from \"./PanelContext\";\nimport type { TypePanelContext, TypePanelProviderProps } from \"./PanelTypes\";\n\nexport const PanelProvider = ({\n children,\n defaultOpen = false,\n open: controlledOpen,\n onOpenChange,\n}: TypePanelProviderProps) => {\n const [internalOpen, setInternalOpen] = React.useState(defaultOpen);\n const isControlled = controlledOpen !== undefined;\n const isPanelOpen = isControlled ? controlledOpen : internalOpen;\n\n const setOpen = React.useCallback(\n (next: boolean) => {\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange]\n );\n\n const openPanel = React.useCallback(() => setOpen(true), [setOpen]);\n const closePanel = React.useCallback(() => setOpen(false), [setOpen]);\n const togglePanel = React.useCallback(\n () => setOpen(!isPanelOpen),\n [setOpen, isPanelOpen]\n );\n\n const value = React.useMemo<TypePanelContext>(\n () => ({ isPanelOpen, openPanel, closePanel, togglePanel }),\n [isPanelOpen, openPanel, closePanel, togglePanel]\n );\n\n return (\n <PanelContext.Provider value={value}>{children}</PanelContext.Provider>\n );\n};\n","import * as React from \"react\";\nimport type {\n TypeSystemCommonProps,\n TypeStyledComponentsCommonProps,\n} from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeBoxProps } from \"@sproutsocial/seeds-react-box\";\nimport type { TypeButtonProps } from \"@sproutsocial/seeds-react-button\";\nimport type {\n TypeDrawerSnapPoint,\n TypeDrawerActionProps,\n} from \"@sproutsocial/seeds-react-drawer\";\n\nexport type { TypeDrawerActionProps as TypePanelActionProps };\n\nexport type PanelDirection = \"left\" | \"right\" | \"bottom\";\n\nexport interface TypePanelContext {\n isPanelOpen: boolean;\n togglePanel: () => void;\n openPanel: () => void;\n closePanel: () => void;\n closeButtonLabel?: string;\n /**\n * Set by `Panel` when rendered headerless. `Panel.Content` reads this to\n * render flush (no padding) and defer overflow scrolling to its children.\n */\n headerless?: boolean;\n}\n\nexport interface TypePanelProviderProps {\n children: React.ReactNode;\n /** Initial open state for uncontrolled usage. */\n defaultOpen?: boolean;\n /** Controlled open state. When provided, the provider becomes controlled. */\n open?: boolean;\n /** Called whenever the open state changes. Required for controlled usage. */\n onOpenChange?: (open: boolean) => void;\n}\n\nexport interface TypePanelProps\n extends TypeStyledComponentsCommonProps,\n TypeSystemCommonProps,\n Omit<React.ComponentPropsWithoutRef<\"div\">, \"color\"> {\n children: React.ReactNode;\n\n /** Label for the close button. Usually \"Close\". */\n closeButtonLabel: string;\n\n /**\n * Custom header content. When provided, replaces the auto-rendered default\n * header entirely — `title` and `titleId` are ignored.\n */\n header?: React.ReactNode;\n\n /** Custom footer content. Not rendered when omitted. */\n footer?: React.ReactNode;\n\n /**\n * Renders children flush, without the built-in header/close button or content\n * padding, and lets the children own overflow scrolling. Use when the content\n * provides its own chrome (e.g. an embedded agent UI). Provide `aria-label`\n * (or `aria-labelledby`) for an accessible name, since no header title is\n * rendered. `title`, `titleId`, and `closeButtonLabel` are ignored in this\n * mode. Defaults to false.\n */\n headerless?: boolean;\n\n /** Title shown in the auto-rendered default header. Ignored when `header` is provided. */\n title?: string;\n\n /**\n * Sets the `id` on the title element of the auto-rendered header. When the\n * default header is used, Panel automatically wires `aria-labelledby` to this\n * id so the dialog/aside has an accessible name. Pass an explicit\n * `aria-labelledby` to override (e.g. when pointing at a different element).\n * Ignored when `header` is provided — supply your own `aria-labelledby` in\n * that case.\n */\n titleId?: string;\n\n /** Side the panel anchors to on desktop. Defaults to \"right\". */\n direction?: PanelDirection;\n\n /**\n * Width (px) when direction is \"left\" or \"right\", or height (px) when\n * direction is \"bottom\". Defaults to 384 to match the existing web-app-core\n * Panel.\n */\n width?: number;\n\n /**\n * Visual gap (px) between the panel and the adjacent content. Applied as\n * margin on the side facing the content (left when direction=\"right\",\n * right when direction=\"left\", top when direction=\"bottom\"). Animates with\n * the panel's open/close transition and collapses to 0 when closed so the\n * gap does not remain visible alongside a zero-width panel. Defaults to 0.\n */\n gap?: number;\n\n /**\n * Forwarded to `useIsMobile`. Accepts a px number or any CSS media-query\n * length. Below this width, the panel renders as an overlay bottom sheet\n * (via seeds-react-drawer). Defaults to the theme's `breakpoints.sm`.\n */\n mobileBreakpoint?: number | string;\n\n /** Applies only to the mobile bottom-sheet rendering. */\n zIndex?: number;\n\n /** Optional id used for data-qa-panel attributes. */\n id?: string;\n\n /**\n * Snap points the panel can rest at when rendered as a mobile bottom sheet.\n * Numbers 0–1 are viewport-height fractions; numbers > 1 are pixels; strings\n * accept `px`/`rem` (e.g. `\"480px\"`, `\"30rem\"`). Order matters — the last\n * entry is the \"expanded\" state. Ignored on the desktop side-panel rendering.\n */\n snapPoints?: TypeDrawerSnapPoint[];\n\n /** Initial snap point for uncontrolled use. Mobile only. */\n defaultSnapPoint?: TypeDrawerSnapPoint | null;\n\n /** Controlled active snap point. Pair with `onSnapPointChange`. Mobile only. */\n snapPoint?: TypeDrawerSnapPoint | null;\n\n /** Fires when the active snap point changes. Mobile only. */\n onSnapPointChange?: (snapPoint: TypeDrawerSnapPoint | null) => void;\n\n /**\n * When true, fast swipes can't skip past adjacent snap points. Mobile only.\n */\n snapToSequentialPoints?: boolean;\n\n /**\n * Action buttons shown in a floating rail above the panel on mobile (the\n * bottom-sheet rendering). Ignored on desktop side-panel rendering — pass\n * actions through `header`/`footer` slots there. The rail owns the close\n * button so the default header omits its built-in close affordance.\n */\n actions?: TypeDrawerActionProps[];\n\n /**\n * Opt out of the floating mobile rail and render the `actions` inline in the\n * header as pill buttons instead. Useful for nested bottom sheets and\n * snap-point layouts where the floating rail would collide with surrounding\n * UI. Defaults to `false`.\n *\n * When `header` is omitted, Panel auto-composes a mobile header that renders\n * the title, the `actions` as pills, and a close-button pill. When `header`\n * is provided, the consumer is responsible for rendering the actions inside\n * their own header — `actions` is forwarded for context only.\n */\n actionsInHeader?: boolean;\n}\n\nexport interface TypePanelHeaderProps extends TypeBoxProps {\n title?: string;\n /**\n * When children are provided, `<Panel.CloseButton />` is NOT rendered\n * automatically. Include it yourself to preserve keyboard accessibility.\n */\n children?: React.ReactNode;\n /** Render-prop override that receives the parent panel context. */\n render?: React.FC<TypePanelContext>;\n}\n\nexport interface TypePanelContentProps extends TypeBoxProps {\n children?: React.ReactNode;\n}\n\nexport interface TypePanelFooterProps extends TypeBoxProps {\n children?: React.ReactNode;\n}\n\nexport interface TypePanelCloseButtonProps\n extends Omit<TypeButtonProps, \"children\"> {\n /** Render-prop override that receives the parent panel context. */\n render?: React.FC<TypePanelContext>;\n children?: React.ReactNode;\n}\n","import Panel from \"./Panel\";\n\nexport default Panel;\nexport { Panel };\nexport { PanelContext, usePanelContext } from \"./PanelContext\";\nexport { PanelProvider } from \"./PanelProvider\";\nexport { PanelHeader } from \"./PanelHeader\";\nexport { PanelContent } from \"./PanelContent\";\nexport { PanelCloseButton } from \"./PanelCloseButton\";\nexport { PanelFooter } from \"./PanelFooter\";\nexport * from \"./PanelTypes\";\n"],"mappings":"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","names":["React","Drawer","jsx","Box","jsx","jsx","Box","Button","Icon","Text","jsx","jsxs","jsx","jsxs","Drawer","React","jsx"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -13,6 +13,11 @@ interface TypePanelContext {
|
|
|
13
13
|
openPanel: () => void;
|
|
14
14
|
closePanel: () => void;
|
|
15
15
|
closeButtonLabel?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Set by `Panel` when rendered headerless. `Panel.Content` reads this to
|
|
18
|
+
* render flush (no padding) and defer overflow scrolling to its children.
|
|
19
|
+
*/
|
|
20
|
+
headerless?: boolean;
|
|
16
21
|
}
|
|
17
22
|
interface TypePanelProviderProps {
|
|
18
23
|
children: React.ReactNode;
|
|
@@ -34,6 +39,15 @@ interface TypePanelProps extends TypeStyledComponentsCommonProps, TypeSystemComm
|
|
|
34
39
|
header?: React.ReactNode;
|
|
35
40
|
/** Custom footer content. Not rendered when omitted. */
|
|
36
41
|
footer?: React.ReactNode;
|
|
42
|
+
/**
|
|
43
|
+
* Renders children flush, without the built-in header/close button or content
|
|
44
|
+
* padding, and lets the children own overflow scrolling. Use when the content
|
|
45
|
+
* provides its own chrome (e.g. an embedded agent UI). Provide `aria-label`
|
|
46
|
+
* (or `aria-labelledby`) for an accessible name, since no header title is
|
|
47
|
+
* rendered. `title`, `titleId`, and `closeButtonLabel` are ignored in this
|
|
48
|
+
* mode. Defaults to false.
|
|
49
|
+
*/
|
|
50
|
+
headerless?: boolean;
|
|
37
51
|
/** Title shown in the auto-rendered default header. Ignored when `header` is provided. */
|
|
38
52
|
title?: string;
|
|
39
53
|
/**
|
|
@@ -131,7 +145,7 @@ interface TypePanelCloseButtonProps extends Omit<TypeButtonProps, "children"> {
|
|
|
131
145
|
}
|
|
132
146
|
|
|
133
147
|
declare const PanelComponent: {
|
|
134
|
-
({ children, closeButtonLabel, header, footer, title, titleId, direction, width, gap, mobileBreakpoint, zIndex, id, snapPoints, defaultSnapPoint, snapPoint, onSnapPointChange, snapToSequentialPoints, actions, actionsInHeader, "aria-labelledby": ariaLabelledByProp, "aria-label": ariaLabel, ...rest }: TypePanelProps): react_jsx_runtime.JSX.Element;
|
|
148
|
+
({ children, closeButtonLabel, header, headerless, footer, title, titleId, direction, width, gap, mobileBreakpoint, zIndex, id, snapPoints, defaultSnapPoint, snapPoint, onSnapPointChange, snapToSequentialPoints, actions, actionsInHeader, "aria-labelledby": ariaLabelledByProp, "aria-label": ariaLabel, ...rest }: TypePanelProps): react_jsx_runtime.JSX.Element;
|
|
135
149
|
Header: {
|
|
136
150
|
({ title, id, children, render, ...rest }: TypePanelHeaderProps): string | number | boolean | Iterable<React.ReactNode> | react_jsx_runtime.JSX.Element | null | undefined;
|
|
137
151
|
displayName: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,11 @@ interface TypePanelContext {
|
|
|
13
13
|
openPanel: () => void;
|
|
14
14
|
closePanel: () => void;
|
|
15
15
|
closeButtonLabel?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Set by `Panel` when rendered headerless. `Panel.Content` reads this to
|
|
18
|
+
* render flush (no padding) and defer overflow scrolling to its children.
|
|
19
|
+
*/
|
|
20
|
+
headerless?: boolean;
|
|
16
21
|
}
|
|
17
22
|
interface TypePanelProviderProps {
|
|
18
23
|
children: React.ReactNode;
|
|
@@ -34,6 +39,15 @@ interface TypePanelProps extends TypeStyledComponentsCommonProps, TypeSystemComm
|
|
|
34
39
|
header?: React.ReactNode;
|
|
35
40
|
/** Custom footer content. Not rendered when omitted. */
|
|
36
41
|
footer?: React.ReactNode;
|
|
42
|
+
/**
|
|
43
|
+
* Renders children flush, without the built-in header/close button or content
|
|
44
|
+
* padding, and lets the children own overflow scrolling. Use when the content
|
|
45
|
+
* provides its own chrome (e.g. an embedded agent UI). Provide `aria-label`
|
|
46
|
+
* (or `aria-labelledby`) for an accessible name, since no header title is
|
|
47
|
+
* rendered. `title`, `titleId`, and `closeButtonLabel` are ignored in this
|
|
48
|
+
* mode. Defaults to false.
|
|
49
|
+
*/
|
|
50
|
+
headerless?: boolean;
|
|
37
51
|
/** Title shown in the auto-rendered default header. Ignored when `header` is provided. */
|
|
38
52
|
title?: string;
|
|
39
53
|
/**
|
|
@@ -131,7 +145,7 @@ interface TypePanelCloseButtonProps extends Omit<TypeButtonProps, "children"> {
|
|
|
131
145
|
}
|
|
132
146
|
|
|
133
147
|
declare const PanelComponent: {
|
|
134
|
-
({ children, closeButtonLabel, header, footer, title, titleId, direction, width, gap, mobileBreakpoint, zIndex, id, snapPoints, defaultSnapPoint, snapPoint, onSnapPointChange, snapToSequentialPoints, actions, actionsInHeader, "aria-labelledby": ariaLabelledByProp, "aria-label": ariaLabel, ...rest }: TypePanelProps): react_jsx_runtime.JSX.Element;
|
|
148
|
+
({ children, closeButtonLabel, header, headerless, footer, title, titleId, direction, width, gap, mobileBreakpoint, zIndex, id, snapPoints, defaultSnapPoint, snapPoint, onSnapPointChange, snapToSequentialPoints, actions, actionsInHeader, "aria-labelledby": ariaLabelledByProp, "aria-label": ariaLabel, ...rest }: TypePanelProps): react_jsx_runtime.JSX.Element;
|
|
135
149
|
Header: {
|
|
136
150
|
({ title, id, children, render, ...rest }: TypePanelHeaderProps): string | number | boolean | Iterable<React.ReactNode> | react_jsx_runtime.JSX.Element | null | undefined;
|
|
137
151
|
displayName: string;
|
package/dist/index.js
CHANGED
|
@@ -147,7 +147,7 @@ var PanelContainer = import_styled_components.default.aside`
|
|
|
147
147
|
${(props) => props.$direction === "bottom" ? import_styled_components.css`
|
|
148
148
|
width: 100%;
|
|
149
149
|
` : import_styled_components.css`
|
|
150
|
-
|
|
150
|
+
align-self: stretch;
|
|
151
151
|
`}
|
|
152
152
|
|
|
153
153
|
${import_seeds_react_system_props.COMMON}
|
|
@@ -165,27 +165,41 @@ var PanelInner = import_styled_components.default.div`
|
|
|
165
165
|
overflow: hidden;
|
|
166
166
|
`;
|
|
167
167
|
var Content = (0, import_styled_components.default)(import_seeds_react_box2.default)`
|
|
168
|
-
overflow-y: auto;
|
|
168
|
+
overflow-y: ${({ $scroll = true }) => $scroll ? "auto" : "hidden"};
|
|
169
169
|
`;
|
|
170
170
|
var Footer = (0, import_styled_components.default)(import_seeds_react_box2.default)`
|
|
171
171
|
flex: 0 0 auto;
|
|
172
172
|
overflow: hidden;
|
|
173
|
+
/*
|
|
174
|
+
* On mobile the Panel renders inside the Drawer's bottom sheet, whose popup
|
|
175
|
+
* deliberately bleeds 3rem (--bleed) below the viewport for elastic
|
|
176
|
+
* overscroll (see seeds-react-drawer/src/styles.ts). A footer pinned to the
|
|
177
|
+
* end of that popup would land inside the bleed and clip below the fold, so
|
|
178
|
+
* we lift it by the inherited --bleed; the gap left behind shows the popup's
|
|
179
|
+
* own background, so there is no visible seam. On desktop --bleed is unset,
|
|
180
|
+
* so the 0px fallback leaves the footer untouched.
|
|
181
|
+
*/
|
|
182
|
+
margin-bottom: var(--bleed, 0px);
|
|
173
183
|
`;
|
|
174
184
|
|
|
175
185
|
// src/PanelContent.tsx
|
|
176
186
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
177
|
-
var PanelContent = ({ children, ...rest }) =>
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
187
|
+
var PanelContent = ({ children, ...rest }) => {
|
|
188
|
+
const { headerless } = usePanelContext();
|
|
189
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
190
|
+
Content,
|
|
191
|
+
{
|
|
192
|
+
flex: "1 1 auto",
|
|
193
|
+
minHeight: "0",
|
|
194
|
+
p: headerless ? 0 : 450,
|
|
195
|
+
$scroll: !headerless,
|
|
196
|
+
color: "text.body",
|
|
197
|
+
"data-panel-content": "",
|
|
198
|
+
...rest,
|
|
199
|
+
children
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
};
|
|
189
203
|
PanelContent.displayName = "Panel.Content";
|
|
190
204
|
|
|
191
205
|
// src/PanelFooter.tsx
|
|
@@ -265,6 +279,7 @@ var PanelComponent = ({
|
|
|
265
279
|
children,
|
|
266
280
|
closeButtonLabel,
|
|
267
281
|
header,
|
|
282
|
+
headerless = false,
|
|
268
283
|
footer,
|
|
269
284
|
title,
|
|
270
285
|
titleId,
|
|
@@ -289,17 +304,28 @@ var PanelComponent = ({
|
|
|
289
304
|
const { isPanelOpen, closePanel } = panelContext;
|
|
290
305
|
const isMobile = (0, import_seeds_react_hooks.useIsMobile)(mobileBreakpoint);
|
|
291
306
|
const enrichedValue = React6.useMemo(
|
|
292
|
-
() => ({ ...panelContext, closeButtonLabel }),
|
|
293
|
-
[panelContext, closeButtonLabel]
|
|
307
|
+
() => ({ ...panelContext, closeButtonLabel, headerless }),
|
|
308
|
+
[panelContext, closeButtonLabel, headerless]
|
|
294
309
|
);
|
|
295
310
|
const prevChildrenRef = React6.useRef(null);
|
|
296
311
|
if (isPanelOpen) {
|
|
297
312
|
prevChildrenRef.current = children;
|
|
298
313
|
}
|
|
299
314
|
const renderedChildren = isPanelOpen ? children : prevChildrenRef.current;
|
|
300
|
-
const effectiveAriaLabelledBy = ariaLabelledByProp ?? (header == null ? titleId : void 0);
|
|
315
|
+
const effectiveAriaLabelledBy = ariaLabelledByProp ?? (!headerless && header == null ? titleId : void 0);
|
|
301
316
|
if (isMobile) {
|
|
302
|
-
const shouldRenderActionsHeader = header == null && actionsInHeader === true && !!actions && actions.length > 0;
|
|
317
|
+
const shouldRenderActionsHeader = !headerless && header == null && actionsInHeader === true && !!actions && actions.length > 0;
|
|
318
|
+
let defaultMobileHeader = null;
|
|
319
|
+
if (!headerless) {
|
|
320
|
+
defaultMobileHeader = shouldRenderActionsHeader ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
321
|
+
PanelMobileActionsHeader,
|
|
322
|
+
{
|
|
323
|
+
title,
|
|
324
|
+
titleId,
|
|
325
|
+
actions
|
|
326
|
+
}
|
|
327
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_seeds_react_drawer2.default.Header, { title, id: titleId });
|
|
328
|
+
}
|
|
303
329
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PanelContext.Provider, { value: enrichedValue, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
304
330
|
import_seeds_react_drawer2.default,
|
|
305
331
|
{
|
|
@@ -319,15 +345,8 @@ var PanelComponent = ({
|
|
|
319
345
|
"aria-labelledby": effectiveAriaLabelledBy,
|
|
320
346
|
"aria-label": ariaLabel,
|
|
321
347
|
children: [
|
|
322
|
-
header ??
|
|
323
|
-
|
|
324
|
-
{
|
|
325
|
-
title,
|
|
326
|
-
titleId,
|
|
327
|
-
actions
|
|
328
|
-
}
|
|
329
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_seeds_react_drawer2.default.Header, { title, id: titleId })),
|
|
330
|
-
children,
|
|
348
|
+
header ?? defaultMobileHeader,
|
|
349
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PanelContent, { children }),
|
|
331
350
|
footer
|
|
332
351
|
]
|
|
333
352
|
}
|
|
@@ -346,8 +365,8 @@ var PanelComponent = ({
|
|
|
346
365
|
"aria-labelledby": effectiveAriaLabelledBy,
|
|
347
366
|
"aria-label": ariaLabel,
|
|
348
367
|
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(PanelInner, { $direction: direction, $width: width, children: [
|
|
349
|
-
header ?? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PanelHeader, { title, id: titleId }),
|
|
350
|
-
renderedChildren,
|
|
368
|
+
header ?? (headerless ? null : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PanelHeader, { title, id: titleId })),
|
|
369
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PanelContent, { children: renderedChildren }),
|
|
351
370
|
footer
|
|
352
371
|
] })
|
|
353
372
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/Panel.tsx","../src/PanelContext.tsx","../src/PanelHeader.tsx","../src/PanelCloseButton.tsx","../src/PanelContent.tsx","../src/styles.ts","../src/PanelFooter.tsx","../src/PanelMobileActionsHeader.tsx","../src/PanelProvider.tsx","../src/PanelTypes.ts"],"sourcesContent":["import Panel from \"./Panel\";\n\nexport default Panel;\nexport { Panel };\nexport { PanelContext, usePanelContext } from \"./PanelContext\";\nexport { PanelProvider } from \"./PanelProvider\";\nexport { PanelHeader } from \"./PanelHeader\";\nexport { PanelContent } from \"./PanelContent\";\nexport { PanelCloseButton } from \"./PanelCloseButton\";\nexport { PanelFooter } from \"./PanelFooter\";\nexport * from \"./PanelTypes\";\n","import * as React from \"react\";\nimport Drawer from \"@sproutsocial/seeds-react-drawer\";\nimport { useIsMobile } from \"@sproutsocial/seeds-react-hooks\";\nimport { usePanelContext, PanelContext } from \"./PanelContext\";\nimport { PanelHeader } from \"./PanelHeader\";\nimport { PanelContent } from \"./PanelContent\";\nimport { PanelCloseButton } from \"./PanelCloseButton\";\nimport { PanelFooter } from \"./PanelFooter\";\nimport { PanelMobileActionsHeader } from \"./PanelMobileActionsHeader\";\nimport { PanelContainer, PanelInner } from \"./styles\";\nimport type { TypePanelProps, TypePanelContext } from \"./PanelTypes\";\n\nconst PanelComponent = ({\n children,\n closeButtonLabel,\n header,\n footer,\n title,\n titleId,\n direction = \"right\",\n width = 384,\n gap = 4,\n mobileBreakpoint,\n zIndex,\n id,\n snapPoints,\n defaultSnapPoint,\n snapPoint,\n onSnapPointChange,\n snapToSequentialPoints,\n actions,\n actionsInHeader,\n \"aria-labelledby\": ariaLabelledByProp,\n \"aria-label\": ariaLabel,\n ...rest\n}: TypePanelProps) => {\n const panelContext = usePanelContext();\n const { isPanelOpen, closePanel } = panelContext;\n const isMobile = useIsMobile(mobileBreakpoint);\n\n // Expose closeButtonLabel through context so PanelCloseButton can read it\n // without prop drilling — same pattern as DrawerContext.\n const enrichedValue = React.useMemo<TypePanelContext>(\n () => ({ ...panelContext, closeButtonLabel }),\n [panelContext, closeButtonLabel]\n );\n\n // Freeze the last-rendered children during the collapse so consumers that\n // conditionally render content don't produce an empty panel shell while the\n // flex-basis animation runs.\n const prevChildrenRef = React.useRef<React.ReactNode>(null);\n if (isPanelOpen) {\n prevChildrenRef.current = children;\n }\n const renderedChildren = isPanelOpen ? children : prevChildrenRef.current;\n\n // When the consumer relies on the default header, its title element renders\n // with `id={titleId}`. Auto-wire `aria-labelledby` so the dialog/aside has an\n // accessible name without forcing every caller to repeat the id.\n const effectiveAriaLabelledBy =\n ariaLabelledByProp ?? (header == null ? titleId : undefined);\n\n if (isMobile) {\n // With `actionsInHeader`, Drawer suppresses the floating rail and expects\n // the consumer to render the actions inside a custom header. Auto-compose\n // one (title + action pills + close pill) when the consumer hasn't\n // supplied their own header; otherwise we'd drop `actions` on the floor.\n const shouldRenderActionsHeader =\n header == null &&\n actionsInHeader === true &&\n !!actions &&\n actions.length > 0;\n\n return (\n <PanelContext.Provider value={enrichedValue}>\n <Drawer\n {...rest}\n open={isPanelOpen}\n onClose={closePanel}\n direction=\"bottom\"\n closeButtonLabel={closeButtonLabel}\n zIndex={zIndex}\n snapPoints={snapPoints}\n defaultSnapPoint={defaultSnapPoint}\n snapPoint={snapPoint}\n onSnapPointChange={onSnapPointChange}\n snapToSequentialPoints={snapToSequentialPoints}\n actions={actions}\n actionsInHeader={actionsInHeader}\n aria-labelledby={effectiveAriaLabelledBy}\n aria-label={ariaLabel}\n >\n {header ??\n (shouldRenderActionsHeader ? (\n <PanelMobileActionsHeader\n title={title}\n titleId={titleId}\n actions={actions!}\n />\n ) : (\n <Drawer.Header title={title} id={titleId} />\n ))}\n {children}\n {footer}\n </Drawer>\n </PanelContext.Provider>\n );\n }\n\n return (\n <PanelContext.Provider value={enrichedValue}>\n <PanelContainer\n {...rest}\n $isOpen={isPanelOpen}\n $direction={direction}\n $width={width}\n $gap={gap}\n data-qa-panel={id}\n data-qa-panel-isopen={isPanelOpen}\n aria-labelledby={effectiveAriaLabelledBy}\n aria-label={ariaLabel}\n >\n <PanelInner $direction={direction} $width={width}>\n {header ?? <PanelHeader title={title} id={titleId} />}\n {renderedChildren}\n {footer}\n </PanelInner>\n </PanelContainer>\n </PanelContext.Provider>\n );\n};\n\nPanelComponent.Header = PanelHeader;\nPanelComponent.Content = PanelContent;\nPanelComponent.CloseButton = PanelCloseButton;\nPanelComponent.Footer = PanelFooter;\n\nexport const Panel = PanelComponent;\nexport default PanelComponent;\n","import * as React from \"react\";\nimport type { TypePanelContext } from \"./PanelTypes\";\n\nexport const PanelContext = React.createContext<TypePanelContext | null>(null);\n\nexport const usePanelContext = (): TypePanelContext => {\n const ctx = React.useContext(PanelContext);\n if (!ctx) {\n throw new Error(\n \"usePanelContext must be used within a <PanelProvider>. Wrap the consuming tree in <PanelProvider> to provide isPanelOpen / togglePanel.\"\n );\n }\n return ctx;\n};\n","import Box from \"@sproutsocial/seeds-react-box\";\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport { usePanelContext } from \"./PanelContext\";\nimport { PanelCloseButton } from \"./PanelCloseButton\";\nimport type { TypePanelHeaderProps } from \"./PanelTypes\";\n\nexport const PanelHeader = ({\n title = \"\",\n id = undefined,\n children,\n render,\n ...rest\n}: TypePanelHeaderProps) => {\n const panelContext = usePanelContext();\n\n if (render) {\n return render(panelContext);\n }\n\n return (\n <Box\n display=\"flex\"\n flex=\"0 0 auto\"\n justifyContent=\"space-between\"\n alignItems=\"center\"\n p={400}\n borderBottom=\"1px solid\"\n borderColor=\"container.border.base\"\n {...rest}\n >\n {children || (\n <>\n <Text.Headline id={id}>{title}</Text.Headline>\n <PanelCloseButton />\n </>\n )}\n </Box>\n );\n};\n\nPanelHeader.displayName = \"Panel.Header\";\n","import * as React from \"react\";\nimport Button from \"@sproutsocial/seeds-react-button\";\nimport Icon from \"@sproutsocial/seeds-react-icon\";\nimport { usePanelContext } from \"./PanelContext\";\nimport type { TypePanelCloseButtonProps } from \"./PanelTypes\";\n\nexport const PanelCloseButton = (props: TypePanelCloseButtonProps) => {\n const panelContext = usePanelContext();\n\n if (props.render) {\n return props.render(panelContext) ?? null;\n }\n\n return (\n <Button\n appearance=\"pill\"\n aria-label={panelContext.closeButtonLabel}\n onClick={panelContext.closePanel}\n {...props}\n >\n {props.children || <Icon aria-hidden name=\"x-outline\" />}\n </Button>\n );\n};\n\nPanelCloseButton.displayName = \"Panel.CloseButton\";\n","import * as React from \"react\";\nimport { Content } from \"./styles\";\nimport type { TypePanelContentProps } from \"./PanelTypes\";\n\nexport const PanelContent = ({ children, ...rest }: TypePanelContentProps) => (\n <Content\n flex=\"1 1 auto\"\n minHeight=\"0\"\n p={450}\n color=\"text.body\"\n data-panel-content=\"\"\n {...rest}\n >\n {children}\n </Content>\n);\n\nPanelContent.displayName = \"Panel.Content\";\n","import styled, { css } from \"styled-components\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeSystemCommonProps } from \"@sproutsocial/seeds-react-system-props\";\nimport { MOTION_DURATION_MEDIUM } from \"@sproutsocial/seeds-motion/unitless\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport type { PanelDirection } from \"./PanelTypes\";\n\ninterface PanelContainerProps extends TypeSystemCommonProps {\n $isOpen: boolean;\n $direction: PanelDirection;\n $width: number;\n $gap: number;\n}\n\nconst gapSide = (direction: PanelDirection): \"left\" | \"right\" | \"top\" => {\n if (direction === \"right\") return \"left\";\n if (direction === \"left\") return \"right\";\n return \"top\";\n};\n\nexport const PanelContainer = styled.aside<PanelContainerProps>`\n background: ${(props) => props.theme.colors.container.background.base};\n flex-grow: 0;\n flex-shrink: 0;\n overflow: hidden;\n transition: flex-basis ${MOTION_DURATION_MEDIUM}s ease-in-out,\n margin ${MOTION_DURATION_MEDIUM}s ease-in-out;\n flex-basis: ${(props) => (props.$isOpen ? `${props.$width}px` : \"0px\")};\n margin-${(props) => gapSide(props.$direction)}: ${(props) =>\n props.$isOpen && props.$gap ? `${props.$gap}px` : \"0px\"};\n border-radius: ${({ theme }) => theme.radii[800]};\n\n ${(props) =>\n props.$direction === \"bottom\"\n ? css`\n width: 100%;\n `\n : css`\n height: 100%;\n `}\n\n ${COMMON}\n`;\n\nexport const PanelInner = styled.div<{\n $direction: PanelDirection;\n $width: number;\n}>`\n ${(props) =>\n props.$direction === \"bottom\"\n ? css`\n min-height: ${props.$width}px;\n width: 100%;\n `\n : css`\n min-width: ${props.$width}px;\n height: 100%;\n `}\n display: flex;\n flex-direction: column;\n overflow: hidden;\n`;\n\nexport const Content = styled(Box)`\n overflow-y: auto;\n`;\n\nexport const Footer = styled(Box)`\n flex: 0 0 auto;\n overflow: hidden;\n`;\n","import * as React from \"react\";\nimport { Footer } from \"./styles\";\nimport type { TypePanelFooterProps } from \"./PanelTypes\";\n\nexport const PanelFooter = ({ children, ...rest }: TypePanelFooterProps) => (\n <Footer p={450} data-panel-footer=\"\" {...rest}>\n {children}\n </Footer>\n);\n\nPanelFooter.displayName = \"Panel.Footer\";\n","import * as React from \"react\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport Button from \"@sproutsocial/seeds-react-button\";\nimport Drawer from \"@sproutsocial/seeds-react-drawer\";\nimport Icon from \"@sproutsocial/seeds-react-icon\";\n// eslint-disable-next-line import/no-deprecated\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport type { TypeDrawerActionProps } from \"@sproutsocial/seeds-react-drawer\";\n\ninterface TypePanelMobileActionsHeaderProps {\n title?: string;\n titleId?: string;\n actions: TypeDrawerActionProps[];\n}\n\n/**\n * Mobile bottom-sheet header used by Panel when `actionsInHeader` is set and\n * no custom `header` is provided. Renders the title on the left and the\n * forwarded actions plus a close button as pills on the right — mirrors the\n * pattern in `seeds-react-peek-in/PeekIn.tsx`. The Drawer's floating rail is\n * suppressed in this combination (`actionsInHeader` opts out), so without this\n * header the actions would render nowhere.\n */\nexport const PanelMobileActionsHeader = ({\n title,\n titleId,\n actions,\n}: TypePanelMobileActionsHeaderProps) => (\n <Drawer.Header\n render={(ctx) => (\n <Box\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"space-between\"\n pt={400}\n px={450}\n flex=\"0 0 auto\"\n >\n <Text\n as=\"h2\"\n fontSize={400}\n fontWeight=\"semibold\"\n color=\"text.headline\"\n id={titleId}\n >\n {title}\n </Text>\n <Box display=\"flex\" alignItems=\"center\" gap={200}>\n {actions.map((action, i) => (\n <Button\n key={i}\n appearance=\"pill\"\n aria-label={action[\"aria-label\"]}\n onClick={action.onClick}\n disabled={action.disabled}\n >\n {action.iconName && <Icon aria-hidden name={action.iconName} />}\n </Button>\n ))}\n <Button\n appearance=\"pill\"\n aria-label={ctx.closeButtonLabel}\n onClick={ctx.onClose}\n >\n <Icon aria-hidden name=\"x-outline\" />\n </Button>\n </Box>\n </Box>\n )}\n />\n);\n\nPanelMobileActionsHeader.displayName = \"PanelMobileActionsHeader\";\n","import * as React from \"react\";\nimport { PanelContext } from \"./PanelContext\";\nimport type { TypePanelContext, TypePanelProviderProps } from \"./PanelTypes\";\n\nexport const PanelProvider = ({\n children,\n defaultOpen = false,\n open: controlledOpen,\n onOpenChange,\n}: TypePanelProviderProps) => {\n const [internalOpen, setInternalOpen] = React.useState(defaultOpen);\n const isControlled = controlledOpen !== undefined;\n const isPanelOpen = isControlled ? controlledOpen : internalOpen;\n\n const setOpen = React.useCallback(\n (next: boolean) => {\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange]\n );\n\n const openPanel = React.useCallback(() => setOpen(true), [setOpen]);\n const closePanel = React.useCallback(() => setOpen(false), [setOpen]);\n const togglePanel = React.useCallback(\n () => setOpen(!isPanelOpen),\n [setOpen, isPanelOpen]\n );\n\n const value = React.useMemo<TypePanelContext>(\n () => ({ isPanelOpen, openPanel, closePanel, togglePanel }),\n [isPanelOpen, openPanel, closePanel, togglePanel]\n );\n\n return (\n <PanelContext.Provider value={value}>{children}</PanelContext.Provider>\n );\n};\n","import * as React from \"react\";\nimport type {\n TypeSystemCommonProps,\n TypeStyledComponentsCommonProps,\n} from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeBoxProps } from \"@sproutsocial/seeds-react-box\";\nimport type { TypeButtonProps } from \"@sproutsocial/seeds-react-button\";\nimport type {\n TypeDrawerSnapPoint,\n TypeDrawerActionProps,\n} from \"@sproutsocial/seeds-react-drawer\";\n\nexport type { TypeDrawerActionProps as TypePanelActionProps };\n\nexport type PanelDirection = \"left\" | \"right\" | \"bottom\";\n\nexport interface TypePanelContext {\n isPanelOpen: boolean;\n togglePanel: () => void;\n openPanel: () => void;\n closePanel: () => void;\n closeButtonLabel?: string;\n}\n\nexport interface TypePanelProviderProps {\n children: React.ReactNode;\n /** Initial open state for uncontrolled usage. */\n defaultOpen?: boolean;\n /** Controlled open state. When provided, the provider becomes controlled. */\n open?: boolean;\n /** Called whenever the open state changes. Required for controlled usage. */\n onOpenChange?: (open: boolean) => void;\n}\n\nexport interface TypePanelProps\n extends TypeStyledComponentsCommonProps,\n TypeSystemCommonProps,\n Omit<React.ComponentPropsWithoutRef<\"div\">, \"color\"> {\n children: React.ReactNode;\n\n /** Label for the close button. Usually \"Close\". */\n closeButtonLabel: string;\n\n /**\n * Custom header content. When provided, replaces the auto-rendered default\n * header entirely — `title` and `titleId` are ignored.\n */\n header?: React.ReactNode;\n\n /** Custom footer content. Not rendered when omitted. */\n footer?: React.ReactNode;\n\n /** Title shown in the auto-rendered default header. Ignored when `header` is provided. */\n title?: string;\n\n /**\n * Sets the `id` on the title element of the auto-rendered header. When the\n * default header is used, Panel automatically wires `aria-labelledby` to this\n * id so the dialog/aside has an accessible name. Pass an explicit\n * `aria-labelledby` to override (e.g. when pointing at a different element).\n * Ignored when `header` is provided — supply your own `aria-labelledby` in\n * that case.\n */\n titleId?: string;\n\n /** Side the panel anchors to on desktop. Defaults to \"right\". */\n direction?: PanelDirection;\n\n /**\n * Width (px) when direction is \"left\" or \"right\", or height (px) when\n * direction is \"bottom\". Defaults to 384 to match the existing web-app-core\n * Panel.\n */\n width?: number;\n\n /**\n * Visual gap (px) between the panel and the adjacent content. Applied as\n * margin on the side facing the content (left when direction=\"right\",\n * right when direction=\"left\", top when direction=\"bottom\"). Animates with\n * the panel's open/close transition and collapses to 0 when closed so the\n * gap does not remain visible alongside a zero-width panel. Defaults to 0.\n */\n gap?: number;\n\n /**\n * Forwarded to `useIsMobile`. Accepts a px number or any CSS media-query\n * length. Below this width, the panel renders as an overlay bottom sheet\n * (via seeds-react-drawer). Defaults to the theme's `breakpoints.sm`.\n */\n mobileBreakpoint?: number | string;\n\n /** Applies only to the mobile bottom-sheet rendering. */\n zIndex?: number;\n\n /** Optional id used for data-qa-panel attributes. */\n id?: string;\n\n /**\n * Snap points the panel can rest at when rendered as a mobile bottom sheet.\n * Numbers 0–1 are viewport-height fractions; numbers > 1 are pixels; strings\n * accept `px`/`rem` (e.g. `\"480px\"`, `\"30rem\"`). Order matters — the last\n * entry is the \"expanded\" state. Ignored on the desktop side-panel rendering.\n */\n snapPoints?: TypeDrawerSnapPoint[];\n\n /** Initial snap point for uncontrolled use. Mobile only. */\n defaultSnapPoint?: TypeDrawerSnapPoint | null;\n\n /** Controlled active snap point. Pair with `onSnapPointChange`. Mobile only. */\n snapPoint?: TypeDrawerSnapPoint | null;\n\n /** Fires when the active snap point changes. Mobile only. */\n onSnapPointChange?: (snapPoint: TypeDrawerSnapPoint | null) => void;\n\n /**\n * When true, fast swipes can't skip past adjacent snap points. Mobile only.\n */\n snapToSequentialPoints?: boolean;\n\n /**\n * Action buttons shown in a floating rail above the panel on mobile (the\n * bottom-sheet rendering). Ignored on desktop side-panel rendering — pass\n * actions through `header`/`footer` slots there. The rail owns the close\n * button so the default header omits its built-in close affordance.\n */\n actions?: TypeDrawerActionProps[];\n\n /**\n * Opt out of the floating mobile rail and render the `actions` inline in the\n * header as pill buttons instead. Useful for nested bottom sheets and\n * snap-point layouts where the floating rail would collide with surrounding\n * UI. Defaults to `false`.\n *\n * When `header` is omitted, Panel auto-composes a mobile header that renders\n * the title, the `actions` as pills, and a close-button pill. When `header`\n * is provided, the consumer is responsible for rendering the actions inside\n * their own header — `actions` is forwarded for context only.\n */\n actionsInHeader?: boolean;\n}\n\nexport interface TypePanelHeaderProps extends TypeBoxProps {\n title?: string;\n /**\n * When children are provided, `<Panel.CloseButton />` is NOT rendered\n * automatically. Include it yourself to preserve keyboard accessibility.\n */\n children?: React.ReactNode;\n /** Render-prop override that receives the parent panel context. */\n render?: React.FC<TypePanelContext>;\n}\n\nexport interface TypePanelContentProps extends TypeBoxProps {\n children?: React.ReactNode;\n}\n\nexport interface TypePanelFooterProps extends TypeBoxProps {\n children?: React.ReactNode;\n}\n\nexport interface TypePanelCloseButtonProps\n extends Omit<TypeButtonProps, \"children\"> {\n /** Render-prop override that receives the parent panel context. */\n render?: React.FC<TypePanelContext>;\n children?: React.ReactNode;\n}\n"],"mappings":"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","names":["React","import_seeds_react_drawer","React","Button","Icon","import_jsx_runtime","Box","Text","React","import_seeds_react_box","styled","Box","import_jsx_runtime","React","import_jsx_runtime","React","import_seeds_react_box","import_seeds_react_button","import_seeds_react_icon","import_seeds_react_text","import_jsx_runtime","Drawer","Box","Text","Button","Icon","import_jsx_runtime","Drawer","React","import_jsx_runtime","React"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/Panel.tsx","../src/PanelContext.tsx","../src/PanelHeader.tsx","../src/PanelCloseButton.tsx","../src/PanelContent.tsx","../src/styles.ts","../src/PanelFooter.tsx","../src/PanelMobileActionsHeader.tsx","../src/PanelProvider.tsx","../src/PanelTypes.ts"],"sourcesContent":["import Panel from \"./Panel\";\n\nexport default Panel;\nexport { Panel };\nexport { PanelContext, usePanelContext } from \"./PanelContext\";\nexport { PanelProvider } from \"./PanelProvider\";\nexport { PanelHeader } from \"./PanelHeader\";\nexport { PanelContent } from \"./PanelContent\";\nexport { PanelCloseButton } from \"./PanelCloseButton\";\nexport { PanelFooter } from \"./PanelFooter\";\nexport * from \"./PanelTypes\";\n","import * as React from \"react\";\nimport Drawer from \"@sproutsocial/seeds-react-drawer\";\nimport { useIsMobile } from \"@sproutsocial/seeds-react-hooks\";\nimport { usePanelContext, PanelContext } from \"./PanelContext\";\nimport { PanelHeader } from \"./PanelHeader\";\nimport { PanelContent } from \"./PanelContent\";\nimport { PanelCloseButton } from \"./PanelCloseButton\";\nimport { PanelFooter } from \"./PanelFooter\";\nimport { PanelMobileActionsHeader } from \"./PanelMobileActionsHeader\";\nimport { PanelContainer, PanelInner } from \"./styles\";\nimport type { TypePanelProps, TypePanelContext } from \"./PanelTypes\";\n\nconst PanelComponent = ({\n children,\n closeButtonLabel,\n header,\n headerless = false,\n footer,\n title,\n titleId,\n direction = \"right\",\n width = 384,\n gap = 4,\n mobileBreakpoint,\n zIndex,\n id,\n snapPoints,\n defaultSnapPoint,\n snapPoint,\n onSnapPointChange,\n snapToSequentialPoints,\n actions,\n actionsInHeader,\n \"aria-labelledby\": ariaLabelledByProp,\n \"aria-label\": ariaLabel,\n ...rest\n}: TypePanelProps) => {\n const panelContext = usePanelContext();\n const { isPanelOpen, closePanel } = panelContext;\n const isMobile = useIsMobile(mobileBreakpoint);\n\n // Expose closeButtonLabel through context so PanelCloseButton can read it\n // without prop drilling — same pattern as DrawerContext.\n const enrichedValue = React.useMemo<TypePanelContext>(\n () => ({ ...panelContext, closeButtonLabel, headerless }),\n [panelContext, closeButtonLabel, headerless]\n );\n\n // Freeze the last-rendered children during the collapse so consumers that\n // conditionally render content don't produce an empty panel shell while the\n // flex-basis animation runs.\n const prevChildrenRef = React.useRef<React.ReactNode>(null);\n if (isPanelOpen) {\n prevChildrenRef.current = children;\n }\n const renderedChildren = isPanelOpen ? children : prevChildrenRef.current;\n\n // When the consumer relies on the default header, its title element renders\n // with `id={titleId}`. Auto-wire `aria-labelledby` so the dialog/aside has an\n // accessible name without forcing every caller to repeat the id.\n const effectiveAriaLabelledBy =\n ariaLabelledByProp ?? (!headerless && header == null ? titleId : undefined);\n\n if (isMobile) {\n // With `actionsInHeader`, Drawer suppresses the floating rail and expects\n // the consumer to render the actions inside a custom header. Auto-compose\n // one (title + action pills + close pill) when the consumer hasn't\n // supplied their own header; otherwise we'd drop `actions` on the floor.\n const shouldRenderActionsHeader =\n !headerless &&\n header == null &&\n actionsInHeader === true &&\n !!actions &&\n actions.length > 0;\n\n // Headerless suppresses the built-in header entirely; the children own all\n // chrome in that mode.\n let defaultMobileHeader: React.ReactNode = null;\n if (!headerless) {\n defaultMobileHeader = shouldRenderActionsHeader ? (\n <PanelMobileActionsHeader\n title={title}\n titleId={titleId}\n actions={actions!}\n />\n ) : (\n <Drawer.Header title={title} id={titleId} />\n );\n }\n\n return (\n <PanelContext.Provider value={enrichedValue}>\n <Drawer\n {...rest}\n open={isPanelOpen}\n onClose={closePanel}\n direction=\"bottom\"\n closeButtonLabel={closeButtonLabel}\n zIndex={zIndex}\n snapPoints={snapPoints}\n defaultSnapPoint={defaultSnapPoint}\n snapPoint={snapPoint}\n onSnapPointChange={onSnapPointChange}\n snapToSequentialPoints={snapToSequentialPoints}\n actions={actions}\n actionsInHeader={actionsInHeader}\n aria-labelledby={effectiveAriaLabelledBy}\n aria-label={ariaLabel}\n >\n {header ?? defaultMobileHeader}\n <PanelContent>{children}</PanelContent>\n {footer}\n </Drawer>\n </PanelContext.Provider>\n );\n }\n\n return (\n <PanelContext.Provider value={enrichedValue}>\n <PanelContainer\n {...rest}\n $isOpen={isPanelOpen}\n $direction={direction}\n $width={width}\n $gap={gap}\n data-qa-panel={id}\n data-qa-panel-isopen={isPanelOpen}\n aria-labelledby={effectiveAriaLabelledBy}\n aria-label={ariaLabel}\n >\n <PanelInner $direction={direction} $width={width}>\n {header ??\n (headerless ? null : <PanelHeader title={title} id={titleId} />)}\n <PanelContent>{renderedChildren}</PanelContent>\n {footer}\n </PanelInner>\n </PanelContainer>\n </PanelContext.Provider>\n );\n};\n\nPanelComponent.Header = PanelHeader;\nPanelComponent.Content = PanelContent;\nPanelComponent.CloseButton = PanelCloseButton;\nPanelComponent.Footer = PanelFooter;\n\nexport const Panel = PanelComponent;\nexport default PanelComponent;\n","import * as React from \"react\";\nimport type { TypePanelContext } from \"./PanelTypes\";\n\nexport const PanelContext = React.createContext<TypePanelContext | null>(null);\n\nexport const usePanelContext = (): TypePanelContext => {\n const ctx = React.useContext(PanelContext);\n if (!ctx) {\n throw new Error(\n \"usePanelContext must be used within a <PanelProvider>. Wrap the consuming tree in <PanelProvider> to provide isPanelOpen / togglePanel.\"\n );\n }\n return ctx;\n};\n","import Box from \"@sproutsocial/seeds-react-box\";\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport { usePanelContext } from \"./PanelContext\";\nimport { PanelCloseButton } from \"./PanelCloseButton\";\nimport type { TypePanelHeaderProps } from \"./PanelTypes\";\n\nexport const PanelHeader = ({\n title = \"\",\n id = undefined,\n children,\n render,\n ...rest\n}: TypePanelHeaderProps) => {\n const panelContext = usePanelContext();\n\n if (render) {\n return render(panelContext);\n }\n\n return (\n <Box\n display=\"flex\"\n flex=\"0 0 auto\"\n justifyContent=\"space-between\"\n alignItems=\"center\"\n p={400}\n borderBottom=\"1px solid\"\n borderColor=\"container.border.base\"\n {...rest}\n >\n {children || (\n <>\n <Text.Headline id={id}>{title}</Text.Headline>\n <PanelCloseButton />\n </>\n )}\n </Box>\n );\n};\n\nPanelHeader.displayName = \"Panel.Header\";\n","import * as React from \"react\";\nimport Button from \"@sproutsocial/seeds-react-button\";\nimport Icon from \"@sproutsocial/seeds-react-icon\";\nimport { usePanelContext } from \"./PanelContext\";\nimport type { TypePanelCloseButtonProps } from \"./PanelTypes\";\n\nexport const PanelCloseButton = (props: TypePanelCloseButtonProps) => {\n const panelContext = usePanelContext();\n\n if (props.render) {\n return props.render(panelContext) ?? null;\n }\n\n return (\n <Button\n appearance=\"pill\"\n aria-label={panelContext.closeButtonLabel}\n onClick={panelContext.closePanel}\n {...props}\n >\n {props.children || <Icon aria-hidden name=\"x-outline\" />}\n </Button>\n );\n};\n\nPanelCloseButton.displayName = \"Panel.CloseButton\";\n","import * as React from \"react\";\nimport { Content } from \"./styles\";\nimport { usePanelContext } from \"./PanelContext\";\nimport type { TypePanelContentProps } from \"./PanelTypes\";\n\nexport const PanelContent = ({ children, ...rest }: TypePanelContentProps) => {\n // In headerless mode the content renders flush and defers overflow scrolling\n // to its children, so the panel can host content that owns its own chrome.\n const { headerless } = usePanelContext();\n\n return (\n <Content\n flex=\"1 1 auto\"\n minHeight=\"0\"\n p={headerless ? 0 : 450}\n $scroll={!headerless}\n color=\"text.body\"\n data-panel-content=\"\"\n {...rest}\n >\n {children}\n </Content>\n );\n};\n\nPanelContent.displayName = \"Panel.Content\";\n","import styled, { css } from \"styled-components\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeSystemCommonProps } from \"@sproutsocial/seeds-react-system-props\";\nimport { MOTION_DURATION_MEDIUM } from \"@sproutsocial/seeds-motion/unitless\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport type { PanelDirection } from \"./PanelTypes\";\n\ninterface PanelContainerProps extends TypeSystemCommonProps {\n $isOpen: boolean;\n $direction: PanelDirection;\n $width: number;\n $gap: number;\n}\n\nconst gapSide = (direction: PanelDirection): \"left\" | \"right\" | \"top\" => {\n if (direction === \"right\") return \"left\";\n if (direction === \"left\") return \"right\";\n return \"top\";\n};\n\nexport const PanelContainer = styled.aside<PanelContainerProps>`\n background: ${(props) => props.theme.colors.container.background.base};\n flex-grow: 0;\n flex-shrink: 0;\n overflow: hidden;\n transition: flex-basis ${MOTION_DURATION_MEDIUM}s ease-in-out,\n margin ${MOTION_DURATION_MEDIUM}s ease-in-out;\n flex-basis: ${(props) => (props.$isOpen ? `${props.$width}px` : \"0px\")};\n margin-${(props) => gapSide(props.$direction)}: ${(props) =>\n props.$isOpen && props.$gap ? `${props.$gap}px` : \"0px\"};\n border-radius: ${({ theme }) => theme.radii[800]};\n\n ${(props) =>\n props.$direction === \"bottom\"\n ? css`\n width: 100%;\n `\n : css`\n align-self: stretch;\n `}\n\n ${COMMON}\n`;\n\nexport const PanelInner = styled.div<{\n $direction: PanelDirection;\n $width: number;\n}>`\n ${(props) =>\n props.$direction === \"bottom\"\n ? css`\n min-height: ${props.$width}px;\n width: 100%;\n `\n : css`\n min-width: ${props.$width}px;\n height: 100%;\n `}\n display: flex;\n flex-direction: column;\n overflow: hidden;\n`;\n\nexport const Content = styled(Box)<{ $scroll?: boolean }>`\n overflow-y: ${({ $scroll = true }) => ($scroll ? \"auto\" : \"hidden\")};\n`;\n\nexport const Footer = styled(Box)`\n flex: 0 0 auto;\n overflow: hidden;\n /*\n * On mobile the Panel renders inside the Drawer's bottom sheet, whose popup\n * deliberately bleeds 3rem (--bleed) below the viewport for elastic\n * overscroll (see seeds-react-drawer/src/styles.ts). A footer pinned to the\n * end of that popup would land inside the bleed and clip below the fold, so\n * we lift it by the inherited --bleed; the gap left behind shows the popup's\n * own background, so there is no visible seam. On desktop --bleed is unset,\n * so the 0px fallback leaves the footer untouched.\n */\n margin-bottom: var(--bleed, 0px);\n`;\n","import * as React from \"react\";\nimport { Footer } from \"./styles\";\nimport type { TypePanelFooterProps } from \"./PanelTypes\";\n\nexport const PanelFooter = ({ children, ...rest }: TypePanelFooterProps) => (\n <Footer p={450} data-panel-footer=\"\" {...rest}>\n {children}\n </Footer>\n);\n\nPanelFooter.displayName = \"Panel.Footer\";\n","import * as React from \"react\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport Button from \"@sproutsocial/seeds-react-button\";\nimport Drawer from \"@sproutsocial/seeds-react-drawer\";\nimport Icon from \"@sproutsocial/seeds-react-icon\";\n// eslint-disable-next-line import/no-deprecated\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport type { TypeDrawerActionProps } from \"@sproutsocial/seeds-react-drawer\";\n\ninterface TypePanelMobileActionsHeaderProps {\n title?: string;\n titleId?: string;\n actions: TypeDrawerActionProps[];\n}\n\n/**\n * Mobile bottom-sheet header used by Panel when `actionsInHeader` is set and\n * no custom `header` is provided. Renders the title on the left and the\n * forwarded actions plus a close button as pills on the right — mirrors the\n * pattern in `seeds-react-peek-in/PeekIn.tsx`. The Drawer's floating rail is\n * suppressed in this combination (`actionsInHeader` opts out), so without this\n * header the actions would render nowhere.\n */\nexport const PanelMobileActionsHeader = ({\n title,\n titleId,\n actions,\n}: TypePanelMobileActionsHeaderProps) => (\n <Drawer.Header\n render={(ctx) => (\n <Box\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"space-between\"\n pt={400}\n px={450}\n flex=\"0 0 auto\"\n >\n <Text\n as=\"h2\"\n fontSize={400}\n fontWeight=\"semibold\"\n color=\"text.headline\"\n id={titleId}\n >\n {title}\n </Text>\n <Box display=\"flex\" alignItems=\"center\" gap={200}>\n {actions.map((action, i) => (\n <Button\n key={i}\n appearance=\"pill\"\n aria-label={action[\"aria-label\"]}\n onClick={action.onClick}\n disabled={action.disabled}\n >\n {action.iconName && <Icon aria-hidden name={action.iconName} />}\n </Button>\n ))}\n <Button\n appearance=\"pill\"\n aria-label={ctx.closeButtonLabel}\n onClick={ctx.onClose}\n >\n <Icon aria-hidden name=\"x-outline\" />\n </Button>\n </Box>\n </Box>\n )}\n />\n);\n\nPanelMobileActionsHeader.displayName = \"PanelMobileActionsHeader\";\n","import * as React from \"react\";\nimport { PanelContext } from \"./PanelContext\";\nimport type { TypePanelContext, TypePanelProviderProps } from \"./PanelTypes\";\n\nexport const PanelProvider = ({\n children,\n defaultOpen = false,\n open: controlledOpen,\n onOpenChange,\n}: TypePanelProviderProps) => {\n const [internalOpen, setInternalOpen] = React.useState(defaultOpen);\n const isControlled = controlledOpen !== undefined;\n const isPanelOpen = isControlled ? controlledOpen : internalOpen;\n\n const setOpen = React.useCallback(\n (next: boolean) => {\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange]\n );\n\n const openPanel = React.useCallback(() => setOpen(true), [setOpen]);\n const closePanel = React.useCallback(() => setOpen(false), [setOpen]);\n const togglePanel = React.useCallback(\n () => setOpen(!isPanelOpen),\n [setOpen, isPanelOpen]\n );\n\n const value = React.useMemo<TypePanelContext>(\n () => ({ isPanelOpen, openPanel, closePanel, togglePanel }),\n [isPanelOpen, openPanel, closePanel, togglePanel]\n );\n\n return (\n <PanelContext.Provider value={value}>{children}</PanelContext.Provider>\n );\n};\n","import * as React from \"react\";\nimport type {\n TypeSystemCommonProps,\n TypeStyledComponentsCommonProps,\n} from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeBoxProps } from \"@sproutsocial/seeds-react-box\";\nimport type { TypeButtonProps } from \"@sproutsocial/seeds-react-button\";\nimport type {\n TypeDrawerSnapPoint,\n TypeDrawerActionProps,\n} from \"@sproutsocial/seeds-react-drawer\";\n\nexport type { TypeDrawerActionProps as TypePanelActionProps };\n\nexport type PanelDirection = \"left\" | \"right\" | \"bottom\";\n\nexport interface TypePanelContext {\n isPanelOpen: boolean;\n togglePanel: () => void;\n openPanel: () => void;\n closePanel: () => void;\n closeButtonLabel?: string;\n /**\n * Set by `Panel` when rendered headerless. `Panel.Content` reads this to\n * render flush (no padding) and defer overflow scrolling to its children.\n */\n headerless?: boolean;\n}\n\nexport interface TypePanelProviderProps {\n children: React.ReactNode;\n /** Initial open state for uncontrolled usage. */\n defaultOpen?: boolean;\n /** Controlled open state. When provided, the provider becomes controlled. */\n open?: boolean;\n /** Called whenever the open state changes. Required for controlled usage. */\n onOpenChange?: (open: boolean) => void;\n}\n\nexport interface TypePanelProps\n extends TypeStyledComponentsCommonProps,\n TypeSystemCommonProps,\n Omit<React.ComponentPropsWithoutRef<\"div\">, \"color\"> {\n children: React.ReactNode;\n\n /** Label for the close button. Usually \"Close\". */\n closeButtonLabel: string;\n\n /**\n * Custom header content. When provided, replaces the auto-rendered default\n * header entirely — `title` and `titleId` are ignored.\n */\n header?: React.ReactNode;\n\n /** Custom footer content. Not rendered when omitted. */\n footer?: React.ReactNode;\n\n /**\n * Renders children flush, without the built-in header/close button or content\n * padding, and lets the children own overflow scrolling. Use when the content\n * provides its own chrome (e.g. an embedded agent UI). Provide `aria-label`\n * (or `aria-labelledby`) for an accessible name, since no header title is\n * rendered. `title`, `titleId`, and `closeButtonLabel` are ignored in this\n * mode. Defaults to false.\n */\n headerless?: boolean;\n\n /** Title shown in the auto-rendered default header. Ignored when `header` is provided. */\n title?: string;\n\n /**\n * Sets the `id` on the title element of the auto-rendered header. When the\n * default header is used, Panel automatically wires `aria-labelledby` to this\n * id so the dialog/aside has an accessible name. Pass an explicit\n * `aria-labelledby` to override (e.g. when pointing at a different element).\n * Ignored when `header` is provided — supply your own `aria-labelledby` in\n * that case.\n */\n titleId?: string;\n\n /** Side the panel anchors to on desktop. Defaults to \"right\". */\n direction?: PanelDirection;\n\n /**\n * Width (px) when direction is \"left\" or \"right\", or height (px) when\n * direction is \"bottom\". Defaults to 384 to match the existing web-app-core\n * Panel.\n */\n width?: number;\n\n /**\n * Visual gap (px) between the panel and the adjacent content. Applied as\n * margin on the side facing the content (left when direction=\"right\",\n * right when direction=\"left\", top when direction=\"bottom\"). Animates with\n * the panel's open/close transition and collapses to 0 when closed so the\n * gap does not remain visible alongside a zero-width panel. Defaults to 0.\n */\n gap?: number;\n\n /**\n * Forwarded to `useIsMobile`. Accepts a px number or any CSS media-query\n * length. Below this width, the panel renders as an overlay bottom sheet\n * (via seeds-react-drawer). Defaults to the theme's `breakpoints.sm`.\n */\n mobileBreakpoint?: number | string;\n\n /** Applies only to the mobile bottom-sheet rendering. */\n zIndex?: number;\n\n /** Optional id used for data-qa-panel attributes. */\n id?: string;\n\n /**\n * Snap points the panel can rest at when rendered as a mobile bottom sheet.\n * Numbers 0–1 are viewport-height fractions; numbers > 1 are pixels; strings\n * accept `px`/`rem` (e.g. `\"480px\"`, `\"30rem\"`). Order matters — the last\n * entry is the \"expanded\" state. Ignored on the desktop side-panel rendering.\n */\n snapPoints?: TypeDrawerSnapPoint[];\n\n /** Initial snap point for uncontrolled use. Mobile only. */\n defaultSnapPoint?: TypeDrawerSnapPoint | null;\n\n /** Controlled active snap point. Pair with `onSnapPointChange`. Mobile only. */\n snapPoint?: TypeDrawerSnapPoint | null;\n\n /** Fires when the active snap point changes. Mobile only. */\n onSnapPointChange?: (snapPoint: TypeDrawerSnapPoint | null) => void;\n\n /**\n * When true, fast swipes can't skip past adjacent snap points. Mobile only.\n */\n snapToSequentialPoints?: boolean;\n\n /**\n * Action buttons shown in a floating rail above the panel on mobile (the\n * bottom-sheet rendering). Ignored on desktop side-panel rendering — pass\n * actions through `header`/`footer` slots there. The rail owns the close\n * button so the default header omits its built-in close affordance.\n */\n actions?: TypeDrawerActionProps[];\n\n /**\n * Opt out of the floating mobile rail and render the `actions` inline in the\n * header as pill buttons instead. Useful for nested bottom sheets and\n * snap-point layouts where the floating rail would collide with surrounding\n * UI. Defaults to `false`.\n *\n * When `header` is omitted, Panel auto-composes a mobile header that renders\n * the title, the `actions` as pills, and a close-button pill. When `header`\n * is provided, the consumer is responsible for rendering the actions inside\n * their own header — `actions` is forwarded for context only.\n */\n actionsInHeader?: boolean;\n}\n\nexport interface TypePanelHeaderProps extends TypeBoxProps {\n title?: string;\n /**\n * When children are provided, `<Panel.CloseButton />` is NOT rendered\n * automatically. Include it yourself to preserve keyboard accessibility.\n */\n children?: React.ReactNode;\n /** Render-prop override that receives the parent panel context. */\n render?: React.FC<TypePanelContext>;\n}\n\nexport interface TypePanelContentProps extends TypeBoxProps {\n children?: React.ReactNode;\n}\n\nexport interface TypePanelFooterProps extends TypeBoxProps {\n children?: React.ReactNode;\n}\n\nexport interface TypePanelCloseButtonProps\n extends Omit<TypeButtonProps, \"children\"> {\n /** Render-prop override that receives the parent panel context. */\n render?: React.FC<TypePanelContext>;\n children?: React.ReactNode;\n}\n"],"mappings":"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","names":["React","import_seeds_react_drawer","React","Button","Icon","import_jsx_runtime","Box","Text","React","import_seeds_react_box","styled","Box","import_jsx_runtime","React","import_jsx_runtime","React","import_seeds_react_box","import_seeds_react_button","import_seeds_react_icon","import_seeds_react_text","import_jsx_runtime","Drawer","Box","Text","Button","Icon","import_jsx_runtime","Drawer","React","import_jsx_runtime","React"]}
|
package/package.json
CHANGED
package/src/Panel.stories.tsx
CHANGED
|
@@ -40,12 +40,10 @@ const MainContent = () => (
|
|
|
40
40
|
);
|
|
41
41
|
|
|
42
42
|
const PanelBody = () => (
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
</Text.BodyCopy>
|
|
48
|
-
</Panel.Content>
|
|
43
|
+
<Text.BodyCopy as="p">
|
|
44
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam ac consectetur
|
|
45
|
+
tortor.
|
|
46
|
+
</Text.BodyCopy>
|
|
49
47
|
);
|
|
50
48
|
|
|
51
49
|
const meta: Meta<typeof Panel> = {
|
|
@@ -94,6 +92,27 @@ export const LeftSide: Story = {
|
|
|
94
92
|
),
|
|
95
93
|
};
|
|
96
94
|
|
|
95
|
+
export const WithMargin: Story = {
|
|
96
|
+
render: () => (
|
|
97
|
+
<PanelProvider defaultOpen>
|
|
98
|
+
<LayoutFrame>
|
|
99
|
+
<MainContent />
|
|
100
|
+
<Panel
|
|
101
|
+
closeButtonLabel="Close"
|
|
102
|
+
title="Panel title"
|
|
103
|
+
titleId="panel-header"
|
|
104
|
+
aria-labelledby="panel-header"
|
|
105
|
+
mt={200}
|
|
106
|
+
mb={200}
|
|
107
|
+
mr={200}
|
|
108
|
+
>
|
|
109
|
+
<PanelBody />
|
|
110
|
+
</Panel>
|
|
111
|
+
</LayoutFrame>
|
|
112
|
+
</PanelProvider>
|
|
113
|
+
),
|
|
114
|
+
};
|
|
115
|
+
|
|
97
116
|
export const CustomWidth: Story = {
|
|
98
117
|
render: () => (
|
|
99
118
|
<PanelProvider defaultOpen>
|
|
@@ -127,12 +146,48 @@ export const CustomHeader: Story = {
|
|
|
127
146
|
</Panel.Header>
|
|
128
147
|
}
|
|
129
148
|
>
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
149
|
+
<Text.BodyCopy as="p">
|
|
150
|
+
When you supply a custom header via the header prop, you are
|
|
151
|
+
responsible for rendering Panel.CloseButton yourself.
|
|
152
|
+
</Text.BodyCopy>
|
|
153
|
+
</Panel>
|
|
154
|
+
</LayoutFrame>
|
|
155
|
+
</PanelProvider>
|
|
156
|
+
),
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export const Headerless: Story = {
|
|
160
|
+
render: () => (
|
|
161
|
+
<PanelProvider defaultOpen>
|
|
162
|
+
<LayoutFrame>
|
|
163
|
+
<MainContent />
|
|
164
|
+
<Panel closeButtonLabel="Close" headerless aria-label="Assistant">
|
|
165
|
+
{/*
|
|
166
|
+
* In headerless mode the children own all chrome and overflow — no
|
|
167
|
+
* built-in header, padding, or close button. The auto-wrapped
|
|
168
|
+
* Panel.Content renders flush (p=0) and is bounded to the panel
|
|
169
|
+
* height, so the children supply their own flex column + scroll
|
|
170
|
+
* region. Here a fixed bar stays put while the body scrolls flush to
|
|
171
|
+
* the panel edges.
|
|
172
|
+
*/}
|
|
173
|
+
<Box display="flex" flexDirection="column" height="100%">
|
|
174
|
+
<Box
|
|
175
|
+
flex="0 0 auto"
|
|
176
|
+
bg="container.background.base"
|
|
177
|
+
borderBottom="1px solid"
|
|
178
|
+
borderColor="container.border.base"
|
|
179
|
+
p={400}
|
|
180
|
+
>
|
|
181
|
+
<Text.Headline as="h2">Custom chrome</Text.Headline>
|
|
182
|
+
</Box>
|
|
183
|
+
<Box flex="1 1 auto" minHeight={0} overflow="auto" p={400}>
|
|
184
|
+
{Array.from({ length: 30 }, (_, i) => (
|
|
185
|
+
<Text.BodyCopy key={i} as="p">
|
|
186
|
+
Scrollable line {i + 1}
|
|
187
|
+
</Text.BodyCopy>
|
|
188
|
+
))}
|
|
189
|
+
</Box>
|
|
190
|
+
</Box>
|
|
136
191
|
</Panel>
|
|
137
192
|
</LayoutFrame>
|
|
138
193
|
</PanelProvider>
|
|
@@ -149,20 +204,20 @@ export const WithFooter: Story = {
|
|
|
149
204
|
title="Panel title"
|
|
150
205
|
titleId="panel-header"
|
|
151
206
|
aria-labelledby="panel-header"
|
|
207
|
+
footer={
|
|
208
|
+
<Panel.Footer>
|
|
209
|
+
<Box display="flex" justifyContent="flex-end" gap={300}>
|
|
210
|
+
<Button appearance="secondary">Cancel</Button>
|
|
211
|
+
<Button appearance="primary">Save</Button>
|
|
212
|
+
</Box>
|
|
213
|
+
</Panel.Footer>
|
|
214
|
+
}
|
|
152
215
|
>
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
))}
|
|
159
|
-
</Panel.Content>
|
|
160
|
-
<Panel.Footer>
|
|
161
|
-
<Box display="flex" justifyContent="flex-end" gap={300}>
|
|
162
|
-
<Button appearance="secondary">Cancel</Button>
|
|
163
|
-
<Button appearance="primary">Save</Button>
|
|
164
|
-
</Box>
|
|
165
|
-
</Panel.Footer>
|
|
216
|
+
{Array.from({ length: 20 }, (_, i) => (
|
|
217
|
+
<Text.BodyCopy key={i} as="p">
|
|
218
|
+
Scrollable content line {i + 1}
|
|
219
|
+
</Text.BodyCopy>
|
|
220
|
+
))}
|
|
166
221
|
</Panel>
|
|
167
222
|
</LayoutFrame>
|
|
168
223
|
</PanelProvider>
|
|
@@ -221,20 +276,20 @@ export const MobileWithFooter: Story = {
|
|
|
221
276
|
title="Panel title"
|
|
222
277
|
titleId="panel-header"
|
|
223
278
|
aria-labelledby="panel-header"
|
|
279
|
+
footer={
|
|
280
|
+
<Panel.Footer>
|
|
281
|
+
<Box display="flex" justifyContent="flex-end" gap={300}>
|
|
282
|
+
<Button appearance="secondary">Cancel</Button>
|
|
283
|
+
<Button appearance="primary">Save</Button>
|
|
284
|
+
</Box>
|
|
285
|
+
</Panel.Footer>
|
|
286
|
+
}
|
|
224
287
|
>
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
))}
|
|
231
|
-
</Panel.Content>
|
|
232
|
-
<Panel.Footer>
|
|
233
|
-
<Box display="flex" justifyContent="flex-end" gap={300}>
|
|
234
|
-
<Button appearance="secondary">Cancel</Button>
|
|
235
|
-
<Button appearance="primary">Save</Button>
|
|
236
|
-
</Box>
|
|
237
|
-
</Panel.Footer>
|
|
288
|
+
{Array.from({ length: 20 }, (_, i) => (
|
|
289
|
+
<Text.BodyCopy key={i} as="p">
|
|
290
|
+
Scrollable content line {i + 1}
|
|
291
|
+
</Text.BodyCopy>
|
|
292
|
+
))}
|
|
238
293
|
</Panel>
|
|
239
294
|
</LayoutFrame>
|
|
240
295
|
</PanelProvider>
|
|
@@ -316,20 +371,20 @@ export const CustomHeaderAndFooter: Story = {
|
|
|
316
371
|
<Panel.CloseButton />
|
|
317
372
|
</Panel.Header>
|
|
318
373
|
}
|
|
374
|
+
footer={
|
|
375
|
+
<Panel.Footer>
|
|
376
|
+
<Box display="flex" justifyContent="flex-end" gap={300}>
|
|
377
|
+
<Button appearance="secondary">Cancel</Button>
|
|
378
|
+
<Button appearance="primary">Save</Button>
|
|
379
|
+
</Box>
|
|
380
|
+
</Panel.Footer>
|
|
381
|
+
}
|
|
319
382
|
>
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
))}
|
|
326
|
-
</Panel.Content>
|
|
327
|
-
<Panel.Footer>
|
|
328
|
-
<Box display="flex" justifyContent="flex-end" gap={300}>
|
|
329
|
-
<Button appearance="secondary">Cancel</Button>
|
|
330
|
-
<Button appearance="primary">Save</Button>
|
|
331
|
-
</Box>
|
|
332
|
-
</Panel.Footer>
|
|
383
|
+
{Array.from({ length: 20 }, (_, i) => (
|
|
384
|
+
<Text.BodyCopy key={i} as="p">
|
|
385
|
+
Scrollable content line {i + 1}
|
|
386
|
+
</Text.BodyCopy>
|
|
387
|
+
))}
|
|
333
388
|
</Panel>
|
|
334
389
|
</LayoutFrame>
|
|
335
390
|
</PanelProvider>
|
package/src/Panel.tsx
CHANGED
|
@@ -14,6 +14,7 @@ const PanelComponent = ({
|
|
|
14
14
|
children,
|
|
15
15
|
closeButtonLabel,
|
|
16
16
|
header,
|
|
17
|
+
headerless = false,
|
|
17
18
|
footer,
|
|
18
19
|
title,
|
|
19
20
|
titleId,
|
|
@@ -41,8 +42,8 @@ const PanelComponent = ({
|
|
|
41
42
|
// Expose closeButtonLabel through context so PanelCloseButton can read it
|
|
42
43
|
// without prop drilling — same pattern as DrawerContext.
|
|
43
44
|
const enrichedValue = React.useMemo<TypePanelContext>(
|
|
44
|
-
() => ({ ...panelContext, closeButtonLabel }),
|
|
45
|
-
[panelContext, closeButtonLabel]
|
|
45
|
+
() => ({ ...panelContext, closeButtonLabel, headerless }),
|
|
46
|
+
[panelContext, closeButtonLabel, headerless]
|
|
46
47
|
);
|
|
47
48
|
|
|
48
49
|
// Freeze the last-rendered children during the collapse so consumers that
|
|
@@ -58,7 +59,7 @@ const PanelComponent = ({
|
|
|
58
59
|
// with `id={titleId}`. Auto-wire `aria-labelledby` so the dialog/aside has an
|
|
59
60
|
// accessible name without forcing every caller to repeat the id.
|
|
60
61
|
const effectiveAriaLabelledBy =
|
|
61
|
-
ariaLabelledByProp ?? (header == null ? titleId : undefined);
|
|
62
|
+
ariaLabelledByProp ?? (!headerless && header == null ? titleId : undefined);
|
|
62
63
|
|
|
63
64
|
if (isMobile) {
|
|
64
65
|
// With `actionsInHeader`, Drawer suppresses the floating rail and expects
|
|
@@ -66,11 +67,27 @@ const PanelComponent = ({
|
|
|
66
67
|
// one (title + action pills + close pill) when the consumer hasn't
|
|
67
68
|
// supplied their own header; otherwise we'd drop `actions` on the floor.
|
|
68
69
|
const shouldRenderActionsHeader =
|
|
70
|
+
!headerless &&
|
|
69
71
|
header == null &&
|
|
70
72
|
actionsInHeader === true &&
|
|
71
73
|
!!actions &&
|
|
72
74
|
actions.length > 0;
|
|
73
75
|
|
|
76
|
+
// Headerless suppresses the built-in header entirely; the children own all
|
|
77
|
+
// chrome in that mode.
|
|
78
|
+
let defaultMobileHeader: React.ReactNode = null;
|
|
79
|
+
if (!headerless) {
|
|
80
|
+
defaultMobileHeader = shouldRenderActionsHeader ? (
|
|
81
|
+
<PanelMobileActionsHeader
|
|
82
|
+
title={title}
|
|
83
|
+
titleId={titleId}
|
|
84
|
+
actions={actions!}
|
|
85
|
+
/>
|
|
86
|
+
) : (
|
|
87
|
+
<Drawer.Header title={title} id={titleId} />
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
74
91
|
return (
|
|
75
92
|
<PanelContext.Provider value={enrichedValue}>
|
|
76
93
|
<Drawer
|
|
@@ -90,17 +107,8 @@ const PanelComponent = ({
|
|
|
90
107
|
aria-labelledby={effectiveAriaLabelledBy}
|
|
91
108
|
aria-label={ariaLabel}
|
|
92
109
|
>
|
|
93
|
-
{header ??
|
|
94
|
-
|
|
95
|
-
<PanelMobileActionsHeader
|
|
96
|
-
title={title}
|
|
97
|
-
titleId={titleId}
|
|
98
|
-
actions={actions!}
|
|
99
|
-
/>
|
|
100
|
-
) : (
|
|
101
|
-
<Drawer.Header title={title} id={titleId} />
|
|
102
|
-
))}
|
|
103
|
-
{children}
|
|
110
|
+
{header ?? defaultMobileHeader}
|
|
111
|
+
<PanelContent>{children}</PanelContent>
|
|
104
112
|
{footer}
|
|
105
113
|
</Drawer>
|
|
106
114
|
</PanelContext.Provider>
|
|
@@ -121,8 +129,9 @@ const PanelComponent = ({
|
|
|
121
129
|
aria-label={ariaLabel}
|
|
122
130
|
>
|
|
123
131
|
<PanelInner $direction={direction} $width={width}>
|
|
124
|
-
{header ??
|
|
125
|
-
|
|
132
|
+
{header ??
|
|
133
|
+
(headerless ? null : <PanelHeader title={title} id={titleId} />)}
|
|
134
|
+
<PanelContent>{renderedChildren}</PanelContent>
|
|
126
135
|
{footer}
|
|
127
136
|
</PanelInner>
|
|
128
137
|
</PanelContainer>
|
package/src/PanelContent.tsx
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { Content } from "./styles";
|
|
3
|
+
import { usePanelContext } from "./PanelContext";
|
|
3
4
|
import type { TypePanelContentProps } from "./PanelTypes";
|
|
4
5
|
|
|
5
|
-
export const PanelContent = ({ children, ...rest }: TypePanelContentProps) =>
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
export const PanelContent = ({ children, ...rest }: TypePanelContentProps) => {
|
|
7
|
+
// In headerless mode the content renders flush and defers overflow scrolling
|
|
8
|
+
// to its children, so the panel can host content that owns its own chrome.
|
|
9
|
+
const { headerless } = usePanelContext();
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<Content
|
|
13
|
+
flex="1 1 auto"
|
|
14
|
+
minHeight="0"
|
|
15
|
+
p={headerless ? 0 : 450}
|
|
16
|
+
$scroll={!headerless}
|
|
17
|
+
color="text.body"
|
|
18
|
+
data-panel-content=""
|
|
19
|
+
{...rest}
|
|
20
|
+
>
|
|
21
|
+
{children}
|
|
22
|
+
</Content>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
17
25
|
|
|
18
26
|
PanelContent.displayName = "Panel.Content";
|
package/src/PanelTypes.ts
CHANGED
|
@@ -20,6 +20,11 @@ export interface TypePanelContext {
|
|
|
20
20
|
openPanel: () => void;
|
|
21
21
|
closePanel: () => void;
|
|
22
22
|
closeButtonLabel?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Set by `Panel` when rendered headerless. `Panel.Content` reads this to
|
|
25
|
+
* render flush (no padding) and defer overflow scrolling to its children.
|
|
26
|
+
*/
|
|
27
|
+
headerless?: boolean;
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
export interface TypePanelProviderProps {
|
|
@@ -50,6 +55,16 @@ export interface TypePanelProps
|
|
|
50
55
|
/** Custom footer content. Not rendered when omitted. */
|
|
51
56
|
footer?: React.ReactNode;
|
|
52
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Renders children flush, without the built-in header/close button or content
|
|
60
|
+
* padding, and lets the children own overflow scrolling. Use when the content
|
|
61
|
+
* provides its own chrome (e.g. an embedded agent UI). Provide `aria-label`
|
|
62
|
+
* (or `aria-labelledby`) for an accessible name, since no header title is
|
|
63
|
+
* rendered. `title`, `titleId`, and `closeButtonLabel` are ignored in this
|
|
64
|
+
* mode. Defaults to false.
|
|
65
|
+
*/
|
|
66
|
+
headerless?: boolean;
|
|
67
|
+
|
|
53
68
|
/** Title shown in the auto-rendered default header. Ignored when `header` is provided. */
|
|
54
69
|
title?: string;
|
|
55
70
|
|
|
@@ -19,11 +19,7 @@ const renderPanel = ({
|
|
|
19
19
|
defaultOpen = false,
|
|
20
20
|
direction,
|
|
21
21
|
width,
|
|
22
|
-
children =
|
|
23
|
-
<Panel.Content>
|
|
24
|
-
<p>Panel Content</p>
|
|
25
|
-
</Panel.Content>
|
|
26
|
-
),
|
|
22
|
+
children = <p>Panel Content</p>,
|
|
27
23
|
title = "Panel Header",
|
|
28
24
|
titleId = "panel-1-header",
|
|
29
25
|
closeButtonLabel = "close button",
|
|
@@ -97,7 +93,7 @@ describe("Panel (desktop / inline mode)", () => {
|
|
|
97
93
|
);
|
|
98
94
|
});
|
|
99
95
|
|
|
100
|
-
it("Panel.Content
|
|
96
|
+
it("auto-wraps children in Panel.Content (data-panel-content)", () => {
|
|
101
97
|
renderPanel({ defaultOpen: true });
|
|
102
98
|
expect(
|
|
103
99
|
screen.getByText(/panel content/i).closest("[data-panel-content]")
|
|
@@ -138,6 +134,51 @@ describe("Panel (desktop / inline mode)", () => {
|
|
|
138
134
|
renderPanel({ defaultOpen: false });
|
|
139
135
|
expect(screen.getByRole("complementary")).toHaveStyle("flex-basis: 0px");
|
|
140
136
|
});
|
|
137
|
+
|
|
138
|
+
describe("headerless", () => {
|
|
139
|
+
it("suppresses the built-in header and close button", () => {
|
|
140
|
+
renderPanel({ defaultOpen: true, headerless: true });
|
|
141
|
+
|
|
142
|
+
expect(screen.queryByText("Panel Header")).not.toBeInTheDocument();
|
|
143
|
+
expect(screen.queryByLabelText("close button")).not.toBeInTheDocument();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("still auto-wraps children in Panel.Content", () => {
|
|
147
|
+
renderPanel({ defaultOpen: true, headerless: true });
|
|
148
|
+
|
|
149
|
+
expect(screen.getByText(/panel content/i)).toBeInTheDocument();
|
|
150
|
+
expect(
|
|
151
|
+
screen.getByText(/panel content/i).closest("[data-panel-content]")
|
|
152
|
+
).toBeInTheDocument();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("still opens and closes via context", async () => {
|
|
156
|
+
const { user } = renderPanel({ defaultOpen: false, headerless: true });
|
|
157
|
+
const panel = screen.getByRole("complementary");
|
|
158
|
+
expect(panel).toHaveAttribute("data-qa-panel-isopen", "false");
|
|
159
|
+
|
|
160
|
+
await act(async () => {
|
|
161
|
+
await user.click(screen.getByText("toggle"));
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
await waitFor(() => {
|
|
165
|
+
expect(panel).toHaveAttribute("data-qa-panel-isopen", "true");
|
|
166
|
+
});
|
|
167
|
+
expect(screen.getByText(/panel content/i)).toBeInTheDocument();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("takes its accessible name from aria-label without a dangling aria-labelledby", () => {
|
|
171
|
+
renderPanel({
|
|
172
|
+
defaultOpen: true,
|
|
173
|
+
headerless: true,
|
|
174
|
+
"aria-label": "Assistant",
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const panel = screen.getByRole("complementary");
|
|
178
|
+
expect(panel).toHaveAttribute("aria-label", "Assistant");
|
|
179
|
+
expect(panel).not.toHaveAttribute("aria-labelledby");
|
|
180
|
+
});
|
|
181
|
+
});
|
|
141
182
|
});
|
|
142
183
|
|
|
143
184
|
describe("Panel (mobile / drawer mode)", () => {
|
|
@@ -303,6 +344,47 @@ describe("Panel (mobile / drawer mode)", () => {
|
|
|
303
344
|
// The consumer owns rendering — Panel must not duplicate the action pill.
|
|
304
345
|
expect(screen.queryByLabelText("Expand")).not.toBeInTheDocument();
|
|
305
346
|
});
|
|
347
|
+
|
|
348
|
+
it("suppresses the default Drawer header when headerless", () => {
|
|
349
|
+
renderPanel({ defaultOpen: true, headerless: true });
|
|
350
|
+
|
|
351
|
+
expect(screen.queryByText("Panel Header")).not.toBeInTheDocument();
|
|
352
|
+
expect(screen.getByText(/panel content/i)).toBeInTheDocument();
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it("wraps children in the scrollable Panel.Content region", () => {
|
|
356
|
+
renderPanel({ defaultOpen: true });
|
|
357
|
+
|
|
358
|
+
expect(
|
|
359
|
+
screen.getByText(/panel content/i).closest("[data-panel-content]")
|
|
360
|
+
).toBeInTheDocument();
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("renders the footer outside the scroll region so it stays pinned", () => {
|
|
364
|
+
renderPanel({
|
|
365
|
+
defaultOpen: true,
|
|
366
|
+
footer: <Panel.Footer>footer content</Panel.Footer>,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const footer = screen.getByText("footer content");
|
|
370
|
+
expect(footer).toBeInTheDocument();
|
|
371
|
+
// The footer must be a sibling of the scroll region, not nested inside it,
|
|
372
|
+
// or it would scroll away with the content and fall below the fold.
|
|
373
|
+
expect(footer.closest("[data-panel-content]")).not.toBeInTheDocument();
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it("lifts the footer above the Drawer's bottom-sheet bleed", () => {
|
|
377
|
+
renderPanel({
|
|
378
|
+
defaultOpen: true,
|
|
379
|
+
footer: <Panel.Footer>footer content</Panel.Footer>,
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// The footer compensates for the Drawer popup's --bleed (3rem of overscroll
|
|
383
|
+
// below the viewport) so its actions stay fully visible above the fold.
|
|
384
|
+
expect(
|
|
385
|
+
screen.getByText("footer content").closest("[data-panel-footer]")
|
|
386
|
+
).toHaveStyleRule("margin-bottom", "var(--bleed,0px)");
|
|
387
|
+
});
|
|
306
388
|
});
|
|
307
389
|
|
|
308
390
|
describe("PanelProvider (controlled)", () => {
|
|
@@ -320,9 +402,7 @@ describe("PanelProvider (controlled)", () => {
|
|
|
320
402
|
>
|
|
321
403
|
<ToggleButton />
|
|
322
404
|
<Panel closeButtonLabel="close button">
|
|
323
|
-
<
|
|
324
|
-
<p>controlled content</p>
|
|
325
|
-
</Panel.Content>
|
|
405
|
+
<p>controlled content</p>
|
|
326
406
|
</Panel>
|
|
327
407
|
</PanelProvider>
|
|
328
408
|
);
|
package/src/styles.ts
CHANGED
|
@@ -36,7 +36,7 @@ export const PanelContainer = styled.aside<PanelContainerProps>`
|
|
|
36
36
|
width: 100%;
|
|
37
37
|
`
|
|
38
38
|
: css`
|
|
39
|
-
|
|
39
|
+
align-self: stretch;
|
|
40
40
|
`}
|
|
41
41
|
|
|
42
42
|
${COMMON}
|
|
@@ -61,11 +61,21 @@ export const PanelInner = styled.div<{
|
|
|
61
61
|
overflow: hidden;
|
|
62
62
|
`;
|
|
63
63
|
|
|
64
|
-
export const Content = styled(Box)
|
|
65
|
-
overflow-y: auto;
|
|
64
|
+
export const Content = styled(Box)<{ $scroll?: boolean }>`
|
|
65
|
+
overflow-y: ${({ $scroll = true }) => ($scroll ? "auto" : "hidden")};
|
|
66
66
|
`;
|
|
67
67
|
|
|
68
68
|
export const Footer = styled(Box)`
|
|
69
69
|
flex: 0 0 auto;
|
|
70
70
|
overflow: hidden;
|
|
71
|
+
/*
|
|
72
|
+
* On mobile the Panel renders inside the Drawer's bottom sheet, whose popup
|
|
73
|
+
* deliberately bleeds 3rem (--bleed) below the viewport for elastic
|
|
74
|
+
* overscroll (see seeds-react-drawer/src/styles.ts). A footer pinned to the
|
|
75
|
+
* end of that popup would land inside the bleed and clip below the fold, so
|
|
76
|
+
* we lift it by the inherited --bleed; the gap left behind shows the popup's
|
|
77
|
+
* own background, so there is no visible seam. On desktop --bleed is unset,
|
|
78
|
+
* so the 0px fallback leaves the footer untouched.
|
|
79
|
+
*/
|
|
80
|
+
margin-bottom: var(--bleed, 0px);
|
|
71
81
|
`;
|