@agnos-ui/core
Version:
Framework-agnostic headless component library.
306 lines (305 loc) • 9.99 kB
JavaScript
import { computed, asWritable, readable, writable, untrack } from "@amadeus-it-group/tansu";
import { focusElement } from "./services/focusElement.js";
import { createPointerdownPositionDirective } from "./services/pointerdownPosition.js";
import { portal } from "./services/portal.js";
import { siblingsInert } from "./services/siblingsInert.js";
import { createTransition } from "./services/transitions/baseTransitions.js";
import { b as bindDirective, i as createBrowserStoreDirective, s as mergeDirectives, g as createAttributesDirective, q as isBrowserHTMLElement, f as conditionalDirective, o as directiveSubscribe } from "./dom-Bb5ZCr_a.js";
import { noop } from "./utils/func.js";
import { a as revertScrollbars, r as removeScrollbars } from "./scrollbars-CcxSrnCx.js";
import { writablesForProps, bindableProp, stateStores, true$ } from "./utils/stores.js";
import { createWidgetFactory } from "./utils/widget.js";
import { b as typeBoolean, i as typeNumberOrNull, e as typeFunction, f as typeHTMLElementOrNull, j as typeString } from "./writables-DYGjj5T3.js";
import { g as getTextDirection } from "./textDirection-cNgt24LJ.js";
function getDrawerDefaultConfig() {
return {
...defaultDrawerConfig
};
}
const defaultDrawerConfig = {
animated: true,
ariaDescribedBy: "",
ariaLabelledBy: "",
backdropClass: "",
backdropTransition: noop,
className: "w-full",
visible: false,
container: typeof window !== "undefined" ? document.body : null,
transition: noop,
verticalTransition: noop,
onHidden: noop,
onShown: noop,
onSizeChange: noop,
onVisibleChange: noop,
onMinimizedChange: noop,
onMaximizedChange: noop,
onResizingChange: noop,
resizable: false,
backdrop: true,
bodyScroll: false,
size: null,
focusOnInit: true
};
const configValidator = {
animated: typeBoolean,
ariaDescribedBy: typeString,
ariaLabelledBy: typeString,
backdropClass: typeString,
backdropTransition: typeFunction,
className: typeString,
visible: typeBoolean,
transition: typeFunction,
verticalTransition: typeFunction,
container: typeHTMLElementOrNull,
onHidden: typeFunction,
onShown: typeFunction,
onSizeChange: typeFunction,
onVisibleChange: typeFunction,
onMinimizedChange: typeFunction,
onMaximizedChange: typeFunction,
onResizingChange: typeFunction,
resizable: typeBoolean,
backdrop: typeBoolean,
bodyScroll: typeBoolean,
size: typeNumberOrNull,
focusOnInit: typeBoolean
};
const createDrawer = createWidgetFactory("drawer", (config) => {
const [
{
backdrop$,
backdropTransition$,
backdropClass$,
bodyScroll$,
transition$,
verticalTransition$,
visible$: requestedVisible$,
container$,
className$,
size$: _dirtySize$,
animated$,
ariaDescribedBy$,
ariaLabelledBy$,
onHidden$,
onShown$,
onSizeChange$,
onVisibleChange$,
onMinimizedChange$,
onMaximizedChange$,
onResizingChange$,
focusOnInit$,
...stateProps
},
patch
] = writablesForProps(defaultDrawerConfig, config, configValidator);
const size$ = bindableProp(_dirtySize$, onSizeChange$, (value) => value ? Math.round(value) : value);
const isVertical$ = computed(() => {
const isVertical = ["block-start", "block-end"].some((placement) => className$().includes(placement));
return isVertical;
});
const transition = createTransition({
props: {
animated: animated$,
animatedOnInit: animated$,
transition: asWritable(computed(() => isVertical$() ? verticalTransition$() : transition$())),
visible: requestedVisible$,
onVisibleChange: onVisibleChange$,
onHidden: onHidden$,
onShown: onShown$
}
});
const drawerPortalDirective = bindDirective(
portal,
computed(() => ({ container: container$() }))
);
const { directive: drawerElementDirective, element$: drawerElement$ } = createBrowserStoreDirective();
const drawerAttributeDirective = mergeDirectives(
drawerElementDirective,
createAttributesDirective(() => ({
attributes: {
class: className$,
role: readable("dialog"),
"aria-describedby": ariaDescribedBy$,
"aria-labelledby": ariaLabelledBy$,
"aria-modal": readable("true"),
tabIndex: readable("-1")
},
styles: {
position: computed(() => {
const container = container$();
return container && isBrowserHTMLElement(container) && container !== document.body ? "relative" : "fixed";
}),
outline: readable("none")
},
events: {
keydown: async (e) => {
if (e.key === "Escape") {
await transition.api.hide();
}
}
}
}))
);
const backdropTransition = createTransition({
props: {
animated: animated$,
transition: backdropTransition$
}
});
const backdropPortalDirective = bindDirective(
portal,
computed(() => {
const container = container$();
const element = container ? transition.stores.element$() : void 0;
return {
container,
insertBefore: element?.parentElement === container ? element : void 0
};
})
);
const backdropHidden$ = computed(() => {
if (!bodyScroll$()) {
if (hidden$()) {
revertScrollbars();
} else {
removeScrollbars();
}
}
return !backdrop$() || hidden$();
});
const backdropAttributeDirective = createAttributesDirective(() => ({
attributes: {
class: backdropClass$,
"data-agnos-ignore-inert": true$
},
events: {
click: async () => await transition.api.hide()
}
}));
const direction$ = computed(() => ["inline-start", "block-start"].some((placement) => className$().includes(placement)) ? 1 : -1);
const isMinimized$ = writable(void 0);
const isMaximized$ = writable(void 0);
function setSize(size) {
const drawerElement = drawerElement$();
const isVertical = isVertical$();
size = Math.round(Math.max(0, size));
drawerElement.style[isVertical ? "height" : "width"] = `${size}px`;
const elementSize = Math.round(drawerElement[isVertical ? "offsetHeight" : "offsetWidth"]);
size$.set(elementSize);
isMinimized$.set(size != null && (size < elementSize || size === 0));
isMaximized$.set(!!size && size > elementSize);
return elementSize;
}
const splitterDirective = mergeDirectives(
createPointerdownPositionDirective((event) => {
const drawerElement = drawerElement$();
const isVertical = isVertical$();
const documentDirection = getTextDirection(drawerElement) === "ltr" ? 1 : -1;
const directionFactor = isVertical ? 1 : documentDirection;
const startSize = drawerElement[isVertical ? "offsetHeight" : "offsetWidth"];
const clientXorY = isVertical ? "clientY" : "clientX";
const startPos = event[clientXorY];
const direction = direction$();
let isResizing = false;
return {
onMove(event2) {
setSize(startSize + directionFactor * direction * (event2[clientXorY] - startPos));
if (!isResizing) {
isResizing = true;
onResizingChange$()(true);
}
},
onEnd() {
drawerElement.style[isVertical ? "height" : "width"] = "";
if (isResizing) {
onResizingChange$()(false);
isResizing = false;
}
}
};
}),
createAttributesDirective(() => ({
events: {
keydown: (e) => {
const { key } = e;
const size = size$() || drawerElement$()?.[isVertical$() ? "offsetHeight" : "offsetWidth"];
if (size != null) {
switch (key) {
case "ArrowLeft":
case "ArrowDown":
onResizingChange$()(true);
setSize(size - 10);
onResizingChange$()(false);
break;
case "ArrowRight":
case "ArrowUp":
onResizingChange$()(true);
setSize(size + 10);
onResizingChange$()(false);
break;
}
}
}
}
}))
);
const visible$ = transition.stores.visible$;
const transitioning$ = computed(() => transition.stores.transitioning$() || backdropTransition.stores.transitioning$());
const hidden$ = computed(() => !transitioning$() && !visible$());
const minSizeAction$ = computed(() => {
const isMinimized = isMinimized$();
if (isMinimized != null) {
untrack(() => onMinimizedChange$()(isMinimized));
}
});
const maxSizeAction$ = computed(() => {
const isMaximized = isMaximized$();
if (isMaximized != null) {
untrack(() => onMaximizedChange$()(isMaximized));
}
});
const action$ = computed(() => {
minSizeAction$();
maxSizeAction$();
});
const widget = {
...stateStores({
...stateProps,
backdropClass$,
className$,
container$,
size$,
visible$,
backdropHidden$,
hidden$,
isVertical$
}),
patch,
api: {
open: transition.api.show,
close: transition.api.hide
},
directives: {
drawerPortalDirective,
drawerDirective: mergeDirectives(
directiveSubscribe(action$),
drawerAttributeDirective,
bindDirective(
siblingsInert,
computed(() => !bodyScroll$())
),
// This directive must come after the attribute directive, to ensure that all the classes and attributes are applied for the transition
transition.directives.directive,
conditionalDirective(focusElement, focusOnInit$)
),
backdropPortalDirective,
backdropDirective: mergeDirectives(backdropTransition.directives.directive, backdropAttributeDirective),
splitterDirective
}
};
return widget;
});
export {
createDrawer as c,
getDrawerDefaultConfig as g
};