Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mieweb/ui",
"version": "0.5.0",
"version": "0.6.1",
"description": "A themeable, accessible React component library built with Tailwind CSS",
"author": "Medical Informatics Engineering, Inc.",
"license": "SEE LICENSE IN LICENSE",
Expand Down
46 changes: 45 additions & 1 deletion src/components/DateRangePicker/DateRangePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ export interface DateRangePickerProps {
placeholder?: string;
/** Custom className */
className?: string;
/**
* Horizontal alignment of the desktop popup relative to the trigger.
* - `'start'` (default historical behavior): popup left edge aligns with trigger left edge.
* - `'end'`: popup right edge aligns with trigger right edge.
* - `'auto'`: starts as `'start'`, then automatically flips to `'end'` if the popup
* would overflow the right edge of the viewport. Recommended for triggers placed
* in the right side of a layout (e.g. page header action slots).
*/
align?: 'start' | 'end' | 'auto';
/** Whether to show the preset sidebar in the calendar popup (default: true) */
showPresets?: boolean;
/** Display variant: desktop (default), mobile (bottom sheet), or responsive (auto-adapts at md breakpoint) */
Expand Down Expand Up @@ -316,6 +325,7 @@ export function DateRangePicker({
activePreset,
placeholder = 'Pick a date range',
className,
align = 'auto',
showPresets = true,
variant = 'desktop',
labels = {},
Expand All @@ -342,6 +352,13 @@ export function DateRangePicker({
const calendarRef = React.useRef<HTMLDivElement>(null);
const triggerRef = React.useRef<HTMLButtonElement>(null);

// Resolved horizontal alignment for the desktop popup. Starts at the
// requested value and may flip to 'end' under 'auto' when the popup
// would overflow the viewport on the right.
const [resolvedAlign, setResolvedAlign] = React.useState<'start' | 'end'>(
align === 'end' ? 'end' : 'start'
);

const isMobileVariant = variant === 'mobile';
const isResponsive = variant === 'responsive';

Expand Down Expand Up @@ -392,6 +409,32 @@ export function DateRangePicker({
}
}, [isMobileVariant, isCalendarOpen]);

// Resolve popup horizontal alignment. For explicit 'start' or 'end' we
// honor the consumer's choice. For 'auto' we measure the trigger and pick
// 'end' when right-aligning would keep more of the popup on-screen — this
// prevents the calendar from spilling past the right edge of the viewport
// when the trigger sits in a right-aligned header slot.
React.useLayoutEffect(() => {
if (isMobileVariant || !isCalendarOpen) return;
if (align === 'start' || align === 'end') {
setResolvedAlign(align);
return;
}
if (typeof window === 'undefined') return;
const trigger = triggerRef.current;
if (!trigger) return;
const rect = trigger.getBoundingClientRect();
// Approximate popup width: preset sidebar (200px) + dual calendar panel
// (~640px including padding). Slight overestimate is fine — we just want
// to flip when 'start' alignment would clearly overflow.
const estimatedPopupWidth = showPresets ? 840 : 640;
const margin = 8;
const overflowsRight =
rect.left + estimatedPopupWidth > window.innerWidth - margin;
const fitsLeftAligned = rect.right - estimatedPopupWidth >= margin;
setResolvedAlign(overflowsRight && fitsLeftAligned ? 'end' : 'start');
}, [align, isCalendarOpen, isMobileVariant, showPresets]);
Comment on lines +417 to +436

const handlePresetSelect = (presetKey: string) => {
const range = calculateDateRange(presetKey);
setRangeStart(range.start);
Expand Down Expand Up @@ -769,7 +812,8 @@ export function DateRangePicker({
<div
ref={calendarRef}
className={cn(
'absolute top-full left-0 z-50 mt-1',
'absolute top-full z-50 mt-1',
resolvedAlign === 'end' ? 'right-0' : 'left-0',
'bg-background border-border rounded-lg border shadow-lg'
)}
role="dialog"
Expand Down
Loading