expose sidebars sizing, v0.1.4
Some checks failed
continuous-integration/drone/push Build encountered an error

This commit is contained in:
2026-02-24 10:53:29 +01:00
parent 83d9bc78f0
commit 01bbaebe5b
4 changed files with 89 additions and 10 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "@panic/web-core", "name": "@panic/web-core",
"version": "0.1.3", "version": "0.1.4",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"description": "Core auth and utilities for panic.haus web applications", "description": "Core auth and utilities for panic.haus web applications",
"type": "module", "type": "module",

View File

@@ -18,6 +18,13 @@ const SIDEBAR_MIN_WIDTH = 220;
const SIDEBAR_MAX_WIDTH = 420; const SIDEBAR_MAX_WIDTH = 420;
const SIDEBAR_COLLAPSED_WIDTH = 56; const SIDEBAR_COLLAPSED_WIDTH = 56;
export type LeftMenuSizing = {
defaultWidth?: number;
minWidth?: number;
maxWidth?: number;
collapsedWidth?: number;
};
export type LeftMenuRenderState = { export type LeftMenuRenderState = {
collapsed: boolean; collapsed: boolean;
mobileOpen: boolean; mobileOpen: boolean;
@@ -53,10 +60,40 @@ type LeftMenuProviderProps = {
children: ReactNode; children: ReactNode;
defaultContent: LeftMenuContent; defaultContent: LeftMenuContent;
closeOnPathname?: string; closeOnPathname?: string;
sizing?: LeftMenuSizing;
}; };
const LeftMenuContext = createContext<LeftMenuContextValue | undefined>(undefined); const LeftMenuContext = createContext<LeftMenuContextValue | undefined>(undefined);
function readSizingValue(value: number | undefined, fallback: number): number {
if (!Number.isFinite(value) || value == null || value <= 0) {
return fallback;
}
return value;
}
function resolveLeftMenuSizing(sizing: LeftMenuSizing | undefined) {
const requestedMinWidth = readSizingValue(sizing?.minWidth, SIDEBAR_MIN_WIDTH);
const requestedMaxWidth = readSizingValue(sizing?.maxWidth, SIDEBAR_MAX_WIDTH);
const minWidth = Math.min(requestedMinWidth, requestedMaxWidth);
const maxWidth = Math.max(requestedMinWidth, requestedMaxWidth);
const requestedDefaultWidth = readSizingValue(sizing?.defaultWidth, SIDEBAR_DEFAULT_WIDTH);
const defaultWidth = Math.min(maxWidth, Math.max(minWidth, requestedDefaultWidth));
const requestedCollapsedWidth = readSizingValue(
sizing?.collapsedWidth,
SIDEBAR_COLLAPSED_WIDTH,
);
const collapsedWidth = Math.min(minWidth, requestedCollapsedWidth);
return {
defaultWidth,
minWidth,
maxWidth,
collapsedWidth,
};
}
function readStoredCollapsed(): boolean { function readStoredCollapsed(): boolean {
if (!globalThis.window) { if (!globalThis.window) {
return false; return false;
@@ -69,7 +106,9 @@ export function LeftMenuProvider({
children, children,
defaultContent, defaultContent,
closeOnPathname, closeOnPathname,
sizing,
}: Readonly<LeftMenuProviderProps>) { }: Readonly<LeftMenuProviderProps>) {
const { defaultWidth, minWidth, maxWidth, collapsedWidth } = resolveLeftMenuSizing(sizing);
const [collapsed, setCollapsed] = useState<boolean>(() => readStoredCollapsed()); const [collapsed, setCollapsed] = useState<boolean>(() => readStoredCollapsed());
const [mobileOpen, setMobileOpen] = useState(false); const [mobileOpen, setMobileOpen] = useState(false);
const [content, setContent] = useState<LeftMenuContent>(defaultContent); const [content, setContent] = useState<LeftMenuContent>(defaultContent);
@@ -162,9 +201,9 @@ export function LeftMenuProvider({
const { width, startResize } = useSidePanelMachine({ const { width, startResize } = useSidePanelMachine({
storageKey: SIDEBAR_WIDTH_KEY, storageKey: SIDEBAR_WIDTH_KEY,
defaultWidth: SIDEBAR_DEFAULT_WIDTH, defaultWidth,
minWidth: SIDEBAR_MIN_WIDTH, minWidth,
maxWidth: SIDEBAR_MAX_WIDTH, maxWidth,
resizeAxis: 'from-left', resizeAxis: 'from-left',
resizingBodyClass: 'auth-sidebar-resizing', resizingBodyClass: 'auth-sidebar-resizing',
isOpen: mobileOpen, isOpen: mobileOpen,
@@ -177,9 +216,9 @@ export function LeftMenuProvider({
const desktopMenuStyle = useMemo<LeftMenuStyle>( const desktopMenuStyle = useMemo<LeftMenuStyle>(
() => ({ () => ({
'--auth-sidebar-width': `${collapsed ? SIDEBAR_COLLAPSED_WIDTH : width}px`, '--auth-sidebar-width': `${collapsed ? collapsedWidth : width}px`,
}), }),
[collapsed, width], [collapsed, collapsedWidth, width],
); );
const value = useMemo<LeftMenuContextValue>( const value = useMemo<LeftMenuContextValue>(

View File

@@ -14,6 +14,12 @@ const RIGHT_SIDEBAR_DEFAULT_WIDTH = 320;
const RIGHT_SIDEBAR_MIN_WIDTH = 260; const RIGHT_SIDEBAR_MIN_WIDTH = 260;
const RIGHT_SIDEBAR_MAX_WIDTH = 480; const RIGHT_SIDEBAR_MAX_WIDTH = 480;
export type RightSidebarSizing = {
defaultWidth?: number;
minWidth?: number;
maxWidth?: number;
};
export type RightSidebarContent = { export type RightSidebarContent = {
title: string; title: string;
content: ReactNode; content: ReactNode;
@@ -39,15 +45,44 @@ type RightSidebarProviderProps = {
children: ReactNode; children: ReactNode;
closeOnPathname?: string; closeOnPathname?: string;
onMobileOpenRequest?: () => void; onMobileOpenRequest?: () => void;
sizing?: RightSidebarSizing;
}; };
const RightSidebarContext = createContext<RightSidebarContextValue | undefined>(undefined); const RightSidebarContext = createContext<RightSidebarContextValue | undefined>(undefined);
function readSizingValue(value: number | undefined, fallback: number): number {
if (!Number.isFinite(value) || value == null || value <= 0) {
return fallback;
}
return value;
}
function resolveRightSidebarSizing(sizing: RightSidebarSizing | undefined) {
const requestedMinWidth = readSizingValue(sizing?.minWidth, RIGHT_SIDEBAR_MIN_WIDTH);
const requestedMaxWidth = readSizingValue(sizing?.maxWidth, RIGHT_SIDEBAR_MAX_WIDTH);
const minWidth = Math.min(requestedMinWidth, requestedMaxWidth);
const maxWidth = Math.max(requestedMinWidth, requestedMaxWidth);
const requestedDefaultWidth = readSizingValue(
sizing?.defaultWidth,
RIGHT_SIDEBAR_DEFAULT_WIDTH,
);
const defaultWidth = Math.min(maxWidth, Math.max(minWidth, requestedDefaultWidth));
return {
defaultWidth,
minWidth,
maxWidth,
};
}
export function RightSidebarProvider({ export function RightSidebarProvider({
children, children,
closeOnPathname, closeOnPathname,
onMobileOpenRequest, onMobileOpenRequest,
sizing,
}: Readonly<RightSidebarProviderProps>) { }: Readonly<RightSidebarProviderProps>) {
const { defaultWidth, minWidth, maxWidth } = resolveRightSidebarSizing(sizing);
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [content, setContent] = useState<RightSidebarContent | null>(null); const [content, setContent] = useState<RightSidebarContent | null>(null);
@@ -92,9 +127,9 @@ export function RightSidebarProvider({
const { width, startResize } = useSidePanelMachine({ const { width, startResize } = useSidePanelMachine({
storageKey: RIGHT_SIDEBAR_WIDTH_KEY, storageKey: RIGHT_SIDEBAR_WIDTH_KEY,
defaultWidth: RIGHT_SIDEBAR_DEFAULT_WIDTH, defaultWidth,
minWidth: RIGHT_SIDEBAR_MIN_WIDTH, minWidth,
maxWidth: RIGHT_SIDEBAR_MAX_WIDTH, maxWidth,
resizeAxis: 'from-right', resizeAxis: 'from-right',
resizingBodyClass: 'auth-right-sidebar-resizing', resizingBodyClass: 'auth-right-sidebar-resizing',
isOpen, isOpen,

View File

@@ -35,10 +35,15 @@ export { LeftMenuProvider, useLeftMenu } from './contexts/LeftMenuContext';
export type { export type {
LeftMenuContent, LeftMenuContent,
LeftMenuRenderState, LeftMenuRenderState,
LeftMenuSizing,
LeftMenuStyle, LeftMenuStyle,
} from './contexts/LeftMenuContext'; } from './contexts/LeftMenuContext';
export { RightSidebarProvider, useRightSidebar } from './contexts/RightSidebarContext'; export { RightSidebarProvider, useRightSidebar } from './contexts/RightSidebarContext';
export type { RightSidebarContent, RightSidebarStyle } from './contexts/RightSidebarContext'; export type {
RightSidebarContent,
RightSidebarSizing,
RightSidebarStyle,
} from './contexts/RightSidebarContext';
export { formatDate, capitalize, splitAndCapitalize } from './utils/formatting'; export { formatDate, capitalize, splitAndCapitalize } from './utils/formatting';
export type { SplitMode } from './utils/formatting'; export type { SplitMode } from './utils/formatting';