원클릭으로
implement-modus-modal-with-refs
Create Modus modals using the forwardRef + useImperativeHandle pattern for programmatic control
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Create Modus modals using the forwardRef + useImperativeHandle pattern for programmatic control
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | implement-modus-modal-with-refs |
| description | Create Modus modals using the forwardRef + useImperativeHandle pattern for programmatic control |
Create Modus modal components using the forwardRef + useImperativeHandle pattern for programmatic control.
Use this skill when:
Modus modals require:
forwardRef to expose methods to parent componentsuseImperativeHandle to define the API (openModal, closeModal)querySelector("dialog") to access the native dialog elementSee src/components/ModusModal.tsx for the complete implementation.
import { ModusWcModal } from "@trimble-oss/moduswebcomponents-react";
import type { ReactNode } from "react";
import { useRef, useEffect, forwardRef, useImperativeHandle } from "react";
/**
* Props for the ModusModal component.
*/
interface ModusModalProps {
/** A unique identifier for the modal. */
modalId: string;
/** The ARIA label for the modal. */
ariaLabel?: string;
/** The type of backdrop for the modal. */
backdrop?: 'default' | 'static';
/** The position of the modal. */
position?: 'top' | 'center' | 'bottom';
/** Whether the modal should be fullscreen. */
fullscreen?: boolean;
/** Whether to show the fullscreen toggle button. */
showFullscreenToggle?: boolean;
/** Whether to show the close button. */
showClose?: boolean;
/** A custom CSS class to apply to the modal. */
customClass?: string;
/** The header content of the modal. */
header?: ReactNode;
/** The main content of the modal. */
children: ReactNode;
/** The footer content of the modal. */
footer?: ReactNode;
/** A callback function to handle the close event. */
onClose?: () => void;
}
/**
* A ref object for the ModusModal component.
*/
export interface ModusModalRef {
/** Opens the modal. */
openModal: () => void;
/** Closes the modal. */
closeModal: () => void;
}
/**
* Renders a Modus modal component.
* @param {ModusModalProps} props - The component props.
* @param {React.Ref<ModusModalRef>} ref - The ref object for the modal.
* @returns {JSX.Element} The rendered modal component.
*/
const ModusModal = forwardRef<ModusModalRef, ModusModalProps>(
(
{
modalId,
ariaLabel,
backdrop = 'default',
position = 'center',
fullscreen = false,
showFullscreenToggle = false,
showClose = true,
customClass,
header,
children,
footer,
onClose,
},
ref
) => {
const modalRef = useRef<HTMLModusWcModalElement>(null);
const openModal = () => {
if (modalRef.current) {
const dialog = modalRef.current.querySelector(
"dialog"
) as HTMLDialogElement;
if (dialog) {
dialog.showModal();
}
}
};
const closeModal = () => {
if (modalRef.current) {
const dialog = modalRef.current.querySelector(
"dialog"
) as HTMLDialogElement;
if (dialog) {
dialog.close();
}
}
};
useImperativeHandle(ref, () => ({
openModal,
closeModal,
}));
// Handle modal events
useEffect(() => {
const modal = modalRef.current;
if (modal) {
const handleClose = () => {
onClose?.();
};
const dialogElement = modal.querySelector("dialog");
if (dialogElement) {
dialogElement.addEventListener("close", handleClose);
return () => {
dialogElement.removeEventListener("close", handleClose);
};
}
}
}, [onClose]);
return (
<ModusWcModal
ref={modalRef}
modal-id={modalId}
aria-label={ariaLabel}
backdrop={backdrop}
position={position}
fullscreen={fullscreen}
show-fullscreen-toggle={showFullscreenToggle}
show-close={showClose}
custom-class={customClass}
>
{header && <div slot="header">{header}</div>}
<div slot="content">{children}</div>
{footer && <div slot="footer">{footer}</div>}
</ModusWcModal>
);
}
);
ModusModal.displayName = "ModusModal";
export default ModusModal;
import { useRef } from "react";
import ModusButton from "./components/ModusButton";
import ModusModal, { type ModusModalRef } from "./components/ModusModal";
function MyComponent() {
const modalRef = useRef<ModusModalRef>(null);
return (
<>
<ModusButton
onButtonClick={() => {
modalRef.current?.openModal();
}}
>
Open Modal
</ModusButton>
<ModusModal
ref={modalRef}
modalId="my-modal"
ariaLabel="Example modal"
onClose={() => {
console.log("Modal closed");
}}
header={
<div className="text-xl font-semibold text-foreground">
Modal Title
</div>
}
footer={
<div className="flex gap-2">
<ModusButton
variant="borderless"
onButtonClick={() => {
modalRef.current?.closeModal();
}}
>
Cancel
</ModusButton>
<ModusButton
onButtonClick={() => {
modalRef.current?.closeModal();
}}
>
Confirm
</ModusButton>
</div>
}
>
<div className="text-sm text-foreground opacity-80">
Modal content goes here.
</div>
</ModusModal>
</>
);
}
const ModusModal = forwardRef<ModusModalRef, ModusModalProps>(
(props, ref) => {
// Component implementation
}
);
useImperativeHandle(ref, () => ({
openModal,
closeModal,
}));
const openModal = () => {
if (modalRef.current) {
const dialog = modalRef.current.querySelector(
"dialog"
) as HTMLDialogElement;
if (dialog) {
dialog.showModal();
}
}
};
Critical: Always use querySelector("dialog") - don't try to access dialog methods directly on the web component.
Modus modals use slots for content:
<ModusWcModal>
{header && <div slot="header">{header}</div>}
<div slot="content">{children}</div>
{footer && <div slot="footer">{footer}</div>}
</ModusWcModal>
Listen to the native dialog close event:
useEffect(() => {
const modal = modalRef.current;
if (modal) {
const dialogElement = modal.querySelector("dialog");
if (dialogElement) {
dialogElement.addEventListener("close", handleClose);
return () => {
dialogElement.removeEventListener("close", handleClose);
};
}
}
}, [onClose]);
<ModusModal
ref={modalRef}
modalId="centered-modal"
position="center"
// ... other props
/>
<ModusModal
ref={modalRef}
modalId="top-modal"
position="top"
// ... other props
/>
<ModusModal
ref={modalRef}
modalId="bottom-modal"
position="bottom"
// ... other props
/>
<ModusModal
ref={modalRef}
modalId="fullscreen-modal"
fullscreen={true}
showFullscreenToggle={true}
// ... other props
/>
<ModusModal
ref={modalRef}
modalId="static-modal"
backdrop="static"
// ... other props
/>
displayName for forwardRef componentsquerySelector("dialog"), not direct property accessmodalRef.current before useclose event on dialog element, not web componentopenModal() is calledcloseModal() is calledonClose callback fires when modal closessrc/components/ModusModal.tsx - Complete implementationsrc/demos/modal-demo/page.tsx - Usage examples.cursor/rules/modus-modal-implementation-react.mdc - Detailed documentationScaffold form components with proper Modus input integration, event handling, validation, and checkbox bug handling
Scaffold a new Modus wrapper component following established SolidJS patterns with proper TypeScript interfaces, event handling, and cleanup
Debug and fix common event handling problems with Modus web components
Apply the critical checkbox value inversion workaround when working with ModusCheckbox components
Create Modus modals using the callback ref pattern for programmatic control
Help with correct Modus icon usage patterns including naming conventions, sizing, and accessibility