@kobalte/core
Version:
Unstyled components and primitives for building accessible web apps and design systems with SolidJS.
351 lines (344 loc) • 10.8 kB
JavaScript
import { createFocusScope } from './ISKHZMHS.js';
import { createHideOutside } from './455ZADO2.js';
import { DismissableLayer } from './BASJUNIE.js';
import { createDisclosureState } from './7LCANGHD.js';
import { ButtonRoot } from './7OVKXYPU.js';
import { createRegisterId } from './E4R2EMM4.js';
import { Polymorphic } from './6Y7B2NEO.js';
import { __export } from './5ZKAE4VZ.js';
import { createComponent, mergeProps, memo, Portal } from 'solid-js/web';
import { mergeDefaultProps, mergeRefs, createGenerateId, callHandler, contains, focusWithoutScrolling } from '@kobalte/utils';
import { createContext, useContext, splitProps, createEffect, onCleanup, Show, createUniqueId, createSignal } from 'solid-js';
import createPreventScroll from 'solid-prevent-scroll';
import { combineStyle } from '@solid-primitives/props';
import createPresence from 'solid-presence';
// src/dialog/index.tsx
var dialog_exports = {};
__export(dialog_exports, {
CloseButton: () => DialogCloseButton,
Content: () => DialogContent,
Description: () => DialogDescription,
Dialog: () => Dialog,
Overlay: () => DialogOverlay,
Portal: () => DialogPortal,
Root: () => DialogRoot,
Title: () => DialogTitle,
Trigger: () => DialogTrigger,
useDialogContext: () => useDialogContext
});
var DialogContext = createContext();
function useDialogContext() {
const context = useContext(DialogContext);
if (context === void 0) {
throw new Error("[kobalte]: `useDialogContext` must be used within a `Dialog` component");
}
return context;
}
// src/dialog/dialog-close-button.tsx
function DialogCloseButton(props) {
const context = useDialogContext();
const [local, others] = splitProps(props, ["aria-label", "onClick"]);
const onClick = (e) => {
callHandler(e, local.onClick);
context.close();
};
return createComponent(ButtonRoot, mergeProps({
get ["aria-label"]() {
return local["aria-label"] || context.translations().dismiss;
},
onClick
}, others));
}
function DialogContent(props) {
let ref;
const context = useDialogContext();
const mergedProps = mergeDefaultProps({
id: context.generateId("content")
}, props);
const [local, others] = splitProps(mergedProps, ["ref", "onOpenAutoFocus", "onCloseAutoFocus", "onPointerDownOutside", "onFocusOutside", "onInteractOutside"]);
let hasInteractedOutside = false;
let hasPointerDownOutside = false;
const onPointerDownOutside = (e) => {
local.onPointerDownOutside?.(e);
if (context.modal() && e.detail.isContextMenu) {
e.preventDefault();
}
};
const onFocusOutside = (e) => {
local.onFocusOutside?.(e);
if (context.modal()) {
e.preventDefault();
}
};
const onInteractOutside = (e) => {
local.onInteractOutside?.(e);
if (context.modal()) {
return;
}
if (!e.defaultPrevented) {
hasInteractedOutside = true;
if (e.detail.originalEvent.type === "pointerdown") {
hasPointerDownOutside = true;
}
}
if (contains(context.triggerRef(), e.target)) {
e.preventDefault();
}
if (e.detail.originalEvent.type === "focusin" && hasPointerDownOutside) {
e.preventDefault();
}
};
const onCloseAutoFocus = (e) => {
local.onCloseAutoFocus?.(e);
if (context.modal()) {
e.preventDefault();
focusWithoutScrolling(context.triggerRef());
} else {
if (!e.defaultPrevented) {
if (!hasInteractedOutside) {
focusWithoutScrolling(context.triggerRef());
}
e.preventDefault();
}
hasInteractedOutside = false;
hasPointerDownOutside = false;
}
};
createHideOutside({
isDisabled: () => !(context.isOpen() && context.modal()),
targets: () => ref ? [ref] : []
});
createPreventScroll({
element: () => ref ?? null,
enabled: () => context.contentPresent() && context.preventScroll()
});
createFocusScope({
trapFocus: () => context.isOpen() && context.modal(),
onMountAutoFocus: local.onOpenAutoFocus,
onUnmountAutoFocus: onCloseAutoFocus
}, () => ref);
createEffect(() => onCleanup(context.registerContentId(others.id)));
return createComponent(Show, {
get when() {
return context.contentPresent();
},
get children() {
return createComponent(DismissableLayer, mergeProps({
ref(r$) {
const _ref$ = mergeRefs((el) => {
context.setContentRef(el);
ref = el;
}, local.ref);
typeof _ref$ === "function" && _ref$(r$);
},
role: "dialog",
tabIndex: -1,
get disableOutsidePointerEvents() {
return memo(() => !!context.modal())() && context.isOpen();
},
get excludedElements() {
return [context.triggerRef];
},
get ["aria-labelledby"]() {
return context.titleId();
},
get ["aria-describedby"]() {
return context.descriptionId();
},
get ["data-expanded"]() {
return context.isOpen() ? "" : void 0;
},
get ["data-closed"]() {
return !context.isOpen() ? "" : void 0;
},
onPointerDownOutside,
onFocusOutside,
onInteractOutside,
get onDismiss() {
return context.close;
}
}, others));
}
});
}
function DialogDescription(props) {
const context = useDialogContext();
const mergedProps = mergeDefaultProps({
id: context.generateId("description")
}, props);
const [local, others] = splitProps(mergedProps, ["id"]);
createEffect(() => onCleanup(context.registerDescriptionId(local.id)));
return createComponent(Polymorphic, mergeProps({
as: "p",
get id() {
return local.id;
}
}, others));
}
function DialogOverlay(props) {
const context = useDialogContext();
const [local, others] = splitProps(props, ["ref", "style", "onPointerDown"]);
const onPointerDown = (e) => {
callHandler(e, local.onPointerDown);
if (e.target === e.currentTarget) {
e.preventDefault();
}
};
return createComponent(Show, {
get when() {
return context.overlayPresent();
},
get children() {
return createComponent(Polymorphic, mergeProps({
as: "div",
ref(r$) {
const _ref$ = mergeRefs(context.setOverlayRef, local.ref);
typeof _ref$ === "function" && _ref$(r$);
},
get style() {
return combineStyle({
"pointer-events": "auto"
}, local.style);
},
get ["data-expanded"]() {
return context.isOpen() ? "" : void 0;
},
get ["data-closed"]() {
return !context.isOpen() ? "" : void 0;
},
onPointerDown
}, others));
}
});
}
function DialogPortal(props) {
const context = useDialogContext();
return createComponent(Show, {
get when() {
return context.contentPresent() || context.overlayPresent();
},
get children() {
return createComponent(Portal, props);
}
});
}
// src/dialog/dialog.intl.ts
var DIALOG_INTL_TRANSLATIONS = {
// `aria-label` of Dialog.CloseButton.
dismiss: "Dismiss"
};
// src/dialog/dialog-root.tsx
function DialogRoot(props) {
const defaultId = `dialog-${createUniqueId()}`;
const mergedProps = mergeDefaultProps({
id: defaultId,
modal: true,
translations: DIALOG_INTL_TRANSLATIONS
}, props);
const [contentId, setContentId] = createSignal();
const [titleId, setTitleId] = createSignal();
const [descriptionId, setDescriptionId] = createSignal();
const [overlayRef, setOverlayRef] = createSignal();
const [contentRef, setContentRef] = createSignal();
const [triggerRef, setTriggerRef] = createSignal();
const disclosureState = createDisclosureState({
open: () => mergedProps.open,
defaultOpen: () => mergedProps.defaultOpen,
onOpenChange: (isOpen) => mergedProps.onOpenChange?.(isOpen)
});
const shouldMount = () => mergedProps.forceMount || disclosureState.isOpen();
const {
present: overlayPresent
} = createPresence({
show: shouldMount,
element: () => overlayRef() ?? null
});
const {
present: contentPresent
} = createPresence({
show: shouldMount,
element: () => contentRef() ?? null
});
const context = {
translations: () => mergedProps.translations ?? DIALOG_INTL_TRANSLATIONS,
isOpen: disclosureState.isOpen,
modal: () => mergedProps.modal ?? true,
preventScroll: () => mergedProps.preventScroll ?? context.modal(),
contentId,
titleId,
descriptionId,
triggerRef,
overlayRef,
setOverlayRef,
contentRef,
setContentRef,
overlayPresent,
contentPresent,
close: disclosureState.close,
toggle: disclosureState.toggle,
setTriggerRef,
generateId: createGenerateId(() => mergedProps.id),
registerContentId: createRegisterId(setContentId),
registerTitleId: createRegisterId(setTitleId),
registerDescriptionId: createRegisterId(setDescriptionId)
};
return createComponent(DialogContext.Provider, {
value: context,
get children() {
return mergedProps.children;
}
});
}
function DialogTitle(props) {
const context = useDialogContext();
const mergedProps = mergeDefaultProps({
id: context.generateId("title")
}, props);
const [local, others] = splitProps(mergedProps, ["id"]);
createEffect(() => onCleanup(context.registerTitleId(local.id)));
return createComponent(Polymorphic, mergeProps({
as: "h2",
get id() {
return local.id;
}
}, others));
}
function DialogTrigger(props) {
const context = useDialogContext();
const [local, others] = splitProps(props, ["ref", "onClick"]);
const onClick = (e) => {
callHandler(e, local.onClick);
context.toggle();
};
return createComponent(ButtonRoot, mergeProps({
ref(r$) {
const _ref$ = mergeRefs(context.setTriggerRef, local.ref);
typeof _ref$ === "function" && _ref$(r$);
},
"aria-haspopup": "dialog",
get ["aria-expanded"]() {
return context.isOpen();
},
get ["aria-controls"]() {
return memo(() => !!context.isOpen())() ? context.contentId() : void 0;
},
get ["data-expanded"]() {
return context.isOpen() ? "" : void 0;
},
get ["data-closed"]() {
return !context.isOpen() ? "" : void 0;
},
onClick
}, others));
}
// src/dialog/index.tsx
var Dialog = Object.assign(DialogRoot, {
CloseButton: DialogCloseButton,
Content: DialogContent,
Description: DialogDescription,
Overlay: DialogOverlay,
Portal: DialogPortal,
Title: DialogTitle,
Trigger: DialogTrigger
});
export { Dialog, DialogCloseButton, DialogContent, DialogDescription, DialogOverlay, DialogPortal, DialogRoot, DialogTitle, DialogTrigger, dialog_exports, useDialogContext };