@primer/react
Version:
An implementation of GitHub's Primer Design System using React
271 lines • 9.38 kB
TypeScript
import React from 'react';
import type { ResponsiveValue } from '../hooks/useResponsiveValue';
import type { FCWithSlotMarker, WithSlotMarker } from '../utils/types';
import { type PaneWidthValue, type PaneWidth, type CustomWidthOptions } from './usePaneWidth';
declare const SPACING_MAP: {
none: number;
condensed: number;
normal: (number | null)[];
};
export type PageLayoutProps = {
/** The maximum width of the page container */
containerWidth?: 'full' | 'medium' | 'large' | 'xlarge';
/** The spacing between the outer edges of the page container and the viewport */
padding?: keyof typeof SPACING_MAP;
rowGap?: keyof typeof SPACING_MAP;
columnGap?: keyof typeof SPACING_MAP;
/** Private prop to allow SplitPageLayout to customize slot components */
_slotsConfig?: Record<'header' | 'footer' | 'sidebar', React.ElementType>;
className?: string;
style?: React.CSSProperties;
};
export type PageLayoutHeaderProps = {
/**
* A unique label for the rendered banner landmark
*/
'aria-label'?: React.AriaAttributes['aria-label'];
/**
* An id to an element which uniquely labels the rendered banner landmark
*/
'aria-labelledby'?: React.AriaAttributes['aria-labelledby'];
padding?: keyof typeof SPACING_MAP;
divider?: 'none' | 'line' | ResponsiveValue<'none' | 'line', 'none' | 'line' | 'filled'>;
/**
* @deprecated Use the `divider` prop with a responsive value instead.
*
* Before:
* ```
* divider="line"
* dividerWhenNarrow="filled"
* ```
*
* After:
* ```
* divider={{regular: 'line', narrow: 'filled'}}
* ```
*/
dividerWhenNarrow?: 'inherit' | 'none' | 'line' | 'filled';
hidden?: boolean | ResponsiveValue<boolean>;
className?: string;
style?: React.CSSProperties;
};
export type PageLayoutContentProps = {
/**
* Provide an optional element type for the outermost element rendered by the component.
* @default 'main'
*/
as?: React.ElementType;
/**
* A unique label for the rendered main landmark
*/
'aria-label'?: React.AriaAttributes['aria-label'];
/**
* An id to an element which uniquely labels the rendered main landmark
*/
'aria-labelledby'?: React.AriaAttributes['aria-labelledby'];
width?: 'full' | 'medium' | 'large' | 'xlarge';
padding?: keyof typeof SPACING_MAP;
hidden?: boolean | ResponsiveValue<boolean>;
className?: string;
style?: React.CSSProperties;
};
export type PageLayoutPaneBaseProps = {
position?: 'start' | 'end' | ResponsiveValue<'start' | 'end'>;
/**
* @deprecated Use the `position` prop with a responsive value instead.
*
* Before:
* ```
* position="start"
* positionWhenNarrow="end"
* ```
*
* After:
* ```
* position={{regular: 'start', narrow: 'end'}}
* ```
*/
positionWhenNarrow?: 'inherit' | 'start' | 'end';
'aria-labelledby'?: string;
'aria-label'?: string;
/**
* The width of the pane.
* - Named sizes: `'small'` | `'medium'` | `'large'`
* - Custom object: `{min: string, default: string, max: string}`
*
* When `resizable` is enabled, this defines the default width and constraints
* (min/max bounds for dragging). Use `currentWidth` to control the displayed width.
*/
width?: PaneWidthValue;
/**
* Minimum width of the pane in pixels. Only used with named `width` sizes.
* Ignored when `width` is a custom object (use `width.min` instead).
*/
minWidth?: number;
/**
* localStorage key used to persist the pane width across sessions.
* Only applies when `resizable` is `true` and no `onResizeEnd` callback is provided.
* @default 'paneWidth'
*/
widthStorageKey?: string;
padding?: keyof typeof SPACING_MAP;
divider?: 'none' | 'line' | ResponsiveValue<'none' | 'line', 'none' | 'line' | 'filled'>;
/**
* @deprecated Use the `divider` prop with a responsive value instead.
*
* Before:
* ```
* divider="line"
* dividerWhenNarrow="filled"
* ```
*
* After:
* ```
* divider={{regular: 'line', narrow: 'filled'}}
* ```
*/
dividerWhenNarrow?: 'inherit' | 'none' | 'line' | 'filled';
sticky?: boolean;
offsetHeader?: string | number;
hidden?: boolean | ResponsiveValue<boolean>;
/**
* Enable resizable pane behavior.
* When `true`, the pane may be resized by the user via drag or keyboard.
* Uses localStorage persistence by default unless `onResizeEnd` is provided.
*
* Note: With default localStorage persistence in SSR, the server-rendered
* width may differ from the stored client width, causing a brief layout
* shift on hydration. Use `onResizeEnd` with server-aware storage to avoid this.
*/
resizable?: boolean;
id?: string;
className?: string;
style?: React.CSSProperties;
};
export type PageLayoutPaneProps = PageLayoutPaneBaseProps & ({
/**
* Callback fired when a resize operation ends (drag release or keyboard key up).
* When provided, this callback is used instead of localStorage persistence.
*/
onResizeEnd: (width: number) => void;
/**
* Current/controlled width value in pixels.
* When provided, this is used as the current pane width instead of internal state.
* The `width` prop still defines the default used when resetting (e.g., double-click).
* Pass `undefined` when the persisted value has not loaded yet (e.g., async fetch).
*/
currentWidth: number | undefined;
} | {
onResizeEnd?: never;
currentWidth?: never;
});
declare const Pane: React.ForwardRefExoticComponent<React.PropsWithChildren<PageLayoutPaneProps> & React.RefAttributes<HTMLDivElement>>;
export type PageLayoutSidebarProps = {
/**
* A unique label for the sidebar region
*/
'aria-label'?: React.AriaAttributes['aria-label'];
/**
* An id to an element which uniquely labels the sidebar region
*/
'aria-labelledby'?: React.AriaAttributes['aria-labelledby'];
/**
* Position of the sidebar relative to the page layout
* @default 'start'
*/
position?: 'start' | 'end';
/**
* Width configuration for the sidebar
*/
width?: PaneWidth | CustomWidthOptions;
/**
* Minimum width of the sidebar when resizable
* @default 256
*/
minWidth?: number;
/**
* Whether the sidebar can be resized
* @default false
*/
resizable?: boolean;
/**
* localStorage key used to persist the sidebar width across sessions.
* Only applies when `resizable` is `true`.
* When omitted, localStorage is not used.
*/
widthStorageKey?: string;
/**
* Padding inside the sidebar
*/
padding?: keyof typeof SPACING_MAP;
/**
* Divider style between sidebar and content
*/
divider?: 'none' | 'line';
/**
* Whether the sidebar sticks to the viewport when scrolling.
* When enabled, the sidebar uses `position: sticky` with `top: 0` and `height: 100vh`.
* @default false
*/
sticky?: boolean;
/**
* Controls sidebar behavior at narrow viewport widths (below 768px).
* - `'default'`: the sidebar retains its normal inline layout.
* - `'fullscreen'`: the sidebar expands to cover the full viewport like a dialog overlay.
* @default 'default'
*/
responsiveVariant?: 'default' | 'fullscreen';
/**
* Whether the sidebar is hidden
*/
hidden?: boolean | ResponsiveValue<boolean>;
/**
* Optional id for the sidebar element
*/
id?: string;
className?: string;
style?: React.CSSProperties;
};
declare const Sidebar: React.ForwardRefExoticComponent<PageLayoutSidebarProps & {
children?: React.ReactNode | undefined;
} & React.RefAttributes<HTMLDivElement>>;
export type PageLayoutFooterProps = {
/**
* A unique label for the rendered contentinfo landmark
*/
'aria-label'?: React.AriaAttributes['aria-label'];
/**
* An id to an element which uniquely labels the rendered contentinfo landmark
*/
'aria-labelledby'?: React.AriaAttributes['aria-labelledby'];
padding?: keyof typeof SPACING_MAP;
divider?: 'none' | 'line' | ResponsiveValue<'none' | 'line', 'none' | 'line' | 'filled'>;
/**
* @deprecated Use the `divider` prop with a responsive value instead.
*
* Before:
* ```
* divider="line"
* dividerWhenNarrow="filled"
* ```
*
* After:
* ```
* divider={{regular: 'line', narrow: 'filled'}}
* ```
*/
dividerWhenNarrow?: 'inherit' | 'none' | 'line' | 'filled';
hidden?: boolean | ResponsiveValue<boolean>;
className?: string;
style?: React.CSSProperties;
};
export declare const PageLayout: React.FC<React.PropsWithChildren<PageLayoutProps>> & {
__SLOT__: symbol;
Header: FCWithSlotMarker<React.PropsWithChildren<PageLayoutHeaderProps>>;
Content: FCWithSlotMarker<React.PropsWithChildren<PageLayoutContentProps>>;
Pane: WithSlotMarker<typeof Pane>;
Sidebar: WithSlotMarker<typeof Sidebar>;
Footer: FCWithSlotMarker<React.PropsWithChildren<PageLayoutFooterProps>>;
};
export {};
//# sourceMappingURL=PageLayout.d.ts.map