diff --git a/Source/CommandForm/fields/CalendarField.tsx b/Source/CommandForm/fields/CalendarField.tsx index 8275345..40a6235 100644 --- a/Source/CommandForm/fields/CalendarField.tsx +++ b/Source/CommandForm/fields/CalendarField.tsx @@ -3,6 +3,7 @@ import { asCommandFormField, WrappedFieldProps } from '@cratis/arc.react/commands'; import { Calendar, type CalendarProps } from 'primereact/calendar'; +import { useOverlayZIndex } from '../../useOverlayZIndex'; import React from 'react'; /** @@ -57,25 +58,32 @@ interface CalendarFieldComponentProps extends WrappedFieldProps { * ``` */ export const CalendarField = asCommandFormField( - (props) => ( - props.onChange(e.value ?? null)} - onBlur={props.onBlur} - invalid={props.invalid} - placeholder={props.placeholder} - dateFormat={props.dateFormat} - showIcon={props.showIcon} - showTime={props.showTime} - hourFormat={props.hourFormat} - minDate={props.minDate} - maxDate={props.maxDate} - className={props.className ? `w-full ${props.className}` : 'w-full'} - pt={props.pt} - ptOptions={props.ptOptions} - unstyled={props.unstyled} - /> - ), + (props) => { + // Same overlay treatment as the Dropdown wrapper: the panel must escape + // dialog scroll containers and stay above modal dialogs. + useOverlayZIndex('p-datepicker'); + + return ( + props.onChange(e.value ?? null)} + onBlur={props.onBlur} + invalid={props.invalid} + placeholder={props.placeholder} + dateFormat={props.dateFormat} + showIcon={props.showIcon} + showTime={props.showTime} + hourFormat={props.hourFormat} + minDate={props.minDate} + maxDate={props.maxDate} + appendTo={document.body} + className={props.className ? `w-full ${props.className}` : 'w-full'} + pt={props.pt} + ptOptions={props.ptOptions} + unstyled={props.unstyled} + /> + ); + }, { defaultValue: null, extractValue: (e: unknown) => e instanceof Date ? e : null diff --git a/Source/CommandForm/fields/DropdownField.tsx b/Source/CommandForm/fields/DropdownField.tsx index 41aa19c..0036c59 100644 --- a/Source/CommandForm/fields/DropdownField.tsx +++ b/Source/CommandForm/fields/DropdownField.tsx @@ -1,9 +1,10 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -import { Dropdown, type DropdownProps } from 'primereact/dropdown'; +import { type DropdownProps } from 'primereact/dropdown'; import React from 'react'; import { asCommandFormField, WrappedFieldProps } from '@cratis/arc.react/commands'; +import { Dropdown } from '../../Dropdown'; /** * Component-level props for {@link DropdownField}. @@ -41,6 +42,9 @@ interface DropdownFieldComponentProps extends WrappedFieldProps * holds the bound value and which one holds the visible label. See * {@link InputTextField} for the full `value={c => c.prop}` binding model. * + * Renders through the library's own {@link Dropdown} wrapper so the panel + * escapes dialog scroll containers and stays above modal dialogs. + * * ```tsx * c.country} * options={countries} diff --git a/Source/CommandForm/fields/MultiSelectField.tsx b/Source/CommandForm/fields/MultiSelectField.tsx index 60e69f5..07edfa8 100644 --- a/Source/CommandForm/fields/MultiSelectField.tsx +++ b/Source/CommandForm/fields/MultiSelectField.tsx @@ -4,6 +4,7 @@ import { asCommandFormField, WrappedFieldProps } from '@cratis/arc.react/commands'; import { MultiSelect, type MultiSelectProps } from 'primereact/multiselect'; import React from 'react'; +import { useOverlayZIndex } from '../../useOverlayZIndex'; /** * Component-level props for {@link MultiSelectField}. @@ -59,26 +60,33 @@ interface MultiSelectFieldComponentProps extends WrappedFieldProps( - (props) => ( - | undefined }) => props.onChange(e.value ?? [])} - onBlur={props.onBlur} - options={props.options} - optionValue={props.optionValue} - optionLabel={props.optionLabel} - placeholder={props.placeholder} - display={props.display} - maxSelectedLabels={props.maxSelectedLabels} - filter={props.filter} - showClear={props.showClear} - invalid={props.invalid} - className={props.className ? `w-full ${props.className}` : 'w-full'} - pt={props.pt} - ptOptions={props.ptOptions} - unstyled={props.unstyled} - /> - ), + (props) => { + // Same overlay treatment as the Dropdown wrapper: the panel must escape + // dialog scroll containers and stay above modal dialogs. + useOverlayZIndex('p-multiselect-panel'); + + return ( + | undefined }) => props.onChange(e.value ?? [])} + onBlur={props.onBlur} + options={props.options} + optionValue={props.optionValue} + optionLabel={props.optionLabel} + placeholder={props.placeholder} + display={props.display} + maxSelectedLabels={props.maxSelectedLabels} + filter={props.filter} + showClear={props.showClear} + invalid={props.invalid} + appendTo={document.body} + className={props.className ? `w-full ${props.className}` : 'w-full'} + pt={props.pt} + ptOptions={props.ptOptions} + unstyled={props.unstyled} + /> + ); + }, { defaultValue: [], extractValue: (e: unknown) => {