@corefoundry-labs/forge-ui 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +78 -0
- package/dist/components/layout/ThemeScope/ThemeScope.d.ts +11 -0
- package/dist/components/layout/ThemeScope/ThemeScope.d.ts.map +1 -0
- package/dist/components/layout/ThemeScope/ThemeScope.js +13 -0
- package/dist/components/layout/ThemeScope/index.d.ts +3 -0
- package/dist/components/layout/ThemeScope/index.d.ts.map +1 -0
- package/dist/components/layout/ThemeScope/index.js +1 -0
- package/dist/componentsV2/actions/ButtonV2/ButtonV2.d.ts +21 -0
- package/dist/componentsV2/actions/ButtonV2/ButtonV2.d.ts.map +1 -0
- package/dist/componentsV2/actions/ButtonV2/ButtonV2.js +17 -0
- package/dist/componentsV2/actions/ButtonV2/ButtonV2.module.css +158 -0
- package/dist/componentsV2/actions/ButtonV2/index.d.ts +3 -0
- package/dist/componentsV2/actions/ButtonV2/index.d.ts.map +1 -0
- package/dist/componentsV2/actions/ButtonV2/index.js +1 -0
- package/dist/componentsV2/data-display/AvatarV2/AvatarV2.d.ts +36 -0
- package/dist/componentsV2/data-display/AvatarV2/AvatarV2.d.ts.map +1 -0
- package/dist/componentsV2/data-display/AvatarV2/AvatarV2.js +23 -0
- package/dist/componentsV2/data-display/AvatarV2/AvatarV2.module.css +156 -0
- package/dist/componentsV2/data-display/AvatarV2/index.d.ts +3 -0
- package/dist/componentsV2/data-display/AvatarV2/index.d.ts.map +1 -0
- package/dist/componentsV2/data-display/AvatarV2/index.js +1 -0
- package/dist/componentsV2/feedback/AlertV2/AlertV2.d.ts +25 -0
- package/dist/componentsV2/feedback/AlertV2/AlertV2.d.ts.map +1 -0
- package/dist/componentsV2/feedback/AlertV2/AlertV2.js +16 -0
- package/dist/componentsV2/feedback/AlertV2/AlertV2.module.css +144 -0
- package/dist/componentsV2/feedback/AlertV2/index.d.ts +3 -0
- package/dist/componentsV2/feedback/AlertV2/index.d.ts.map +1 -0
- package/dist/componentsV2/feedback/AlertV2/index.js +1 -0
- package/dist/componentsV2/feedback/FeedbackStateV2/FeedbackStateV2.d.ts +13 -0
- package/dist/componentsV2/feedback/FeedbackStateV2/FeedbackStateV2.d.ts.map +1 -0
- package/dist/componentsV2/feedback/FeedbackStateV2/FeedbackStateV2.js +18 -0
- package/dist/componentsV2/feedback/FeedbackStateV2/FeedbackStateV2.module.css +102 -0
- package/dist/componentsV2/feedback/FeedbackStateV2/index.d.ts +3 -0
- package/dist/componentsV2/feedback/FeedbackStateV2/index.d.ts.map +1 -0
- package/dist/componentsV2/feedback/FeedbackStateV2/index.js +1 -0
- package/dist/componentsV2/feedback/ProgressV2/ProgressV2.d.ts +35 -0
- package/dist/componentsV2/feedback/ProgressV2/ProgressV2.d.ts.map +1 -0
- package/dist/componentsV2/feedback/ProgressV2/ProgressV2.js +15 -0
- package/dist/componentsV2/feedback/ProgressV2/ProgressV2.module.css +161 -0
- package/dist/componentsV2/feedback/ProgressV2/index.d.ts +3 -0
- package/dist/componentsV2/feedback/ProgressV2/index.d.ts.map +1 -0
- package/dist/componentsV2/feedback/ProgressV2/index.js +1 -0
- package/dist/componentsV2/feedback/SkeletonV2/SkeletonV2.d.ts +20 -0
- package/dist/componentsV2/feedback/SkeletonV2/SkeletonV2.d.ts.map +1 -0
- package/dist/componentsV2/feedback/SkeletonV2/SkeletonV2.js +8 -0
- package/dist/componentsV2/feedback/SkeletonV2/SkeletonV2.module.css +92 -0
- package/dist/componentsV2/feedback/SkeletonV2/index.d.ts +3 -0
- package/dist/componentsV2/feedback/SkeletonV2/index.d.ts.map +1 -0
- package/dist/componentsV2/feedback/SkeletonV2/index.js +1 -0
- package/dist/componentsV2/feedback/SnackbarV2/SnackbarV2.d.ts +35 -0
- package/dist/componentsV2/feedback/SnackbarV2/SnackbarV2.d.ts.map +1 -0
- package/dist/componentsV2/feedback/SnackbarV2/SnackbarV2.js +90 -0
- package/dist/componentsV2/feedback/SnackbarV2/SnackbarV2.module.css +162 -0
- package/dist/componentsV2/feedback/SnackbarV2/index.d.ts +3 -0
- package/dist/componentsV2/feedback/SnackbarV2/index.d.ts.map +1 -0
- package/dist/componentsV2/feedback/SnackbarV2/index.js +1 -0
- package/dist/componentsV2/forms/CheckboxV2/CheckboxV2.d.ts +10 -0
- package/dist/componentsV2/forms/CheckboxV2/CheckboxV2.d.ts.map +1 -0
- package/dist/componentsV2/forms/CheckboxV2/CheckboxV2.js +18 -0
- package/dist/componentsV2/forms/CheckboxV2/CheckboxV2.module.css +137 -0
- package/dist/componentsV2/forms/CheckboxV2/index.d.ts +3 -0
- package/dist/componentsV2/forms/CheckboxV2/index.d.ts.map +1 -0
- package/dist/componentsV2/forms/CheckboxV2/index.js +1 -0
- package/dist/componentsV2/forms/SelectV2/SelectV2.d.ts +10 -0
- package/dist/componentsV2/forms/SelectV2/SelectV2.d.ts.map +1 -0
- package/dist/componentsV2/forms/SelectV2/SelectV2.js +7 -0
- package/dist/componentsV2/forms/SelectV2/SelectV2.module.css +90 -0
- package/dist/componentsV2/forms/SelectV2/index.d.ts +3 -0
- package/dist/componentsV2/forms/SelectV2/index.d.ts.map +1 -0
- package/dist/componentsV2/forms/SelectV2/index.js +1 -0
- package/dist/componentsV2/forms/SwitchV2/SwitchV2.d.ts +8 -0
- package/dist/componentsV2/forms/SwitchV2/SwitchV2.d.ts.map +1 -0
- package/dist/componentsV2/forms/SwitchV2/SwitchV2.js +8 -0
- package/dist/componentsV2/forms/SwitchV2/SwitchV2.module.css +129 -0
- package/dist/componentsV2/forms/SwitchV2/index.d.ts +3 -0
- package/dist/componentsV2/forms/SwitchV2/index.d.ts.map +1 -0
- package/dist/componentsV2/forms/SwitchV2/index.js +1 -0
- package/dist/componentsV2/forms/TextInputV2/TextInputV2.d.ts +10 -0
- package/dist/componentsV2/forms/TextInputV2/TextInputV2.d.ts.map +1 -0
- package/dist/componentsV2/forms/TextInputV2/TextInputV2.js +7 -0
- package/dist/componentsV2/forms/TextInputV2/TextInputV2.module.css +64 -0
- package/dist/componentsV2/forms/TextInputV2/index.d.ts +3 -0
- package/dist/componentsV2/forms/TextInputV2/index.d.ts.map +1 -0
- package/dist/componentsV2/forms/TextInputV2/index.js +1 -0
- package/dist/componentsV2/forms/selectionControl.d.ts +14 -0
- package/dist/componentsV2/forms/selectionControl.d.ts.map +1 -0
- package/dist/componentsV2/forms/selectionControl.js +1 -0
- package/dist/componentsV2/navigation/TabsV2/TabsV2.d.ts +30 -0
- package/dist/componentsV2/navigation/TabsV2/TabsV2.d.ts.map +1 -0
- package/dist/componentsV2/navigation/TabsV2/TabsV2.js +54 -0
- package/dist/componentsV2/navigation/TabsV2/TabsV2.module.css +128 -0
- package/dist/componentsV2/navigation/TabsV2/index.d.ts +3 -0
- package/dist/componentsV2/navigation/TabsV2/index.d.ts.map +1 -0
- package/dist/componentsV2/navigation/TabsV2/index.js +1 -0
- package/dist/componentsV2/overlays/DialogV2/DialogV2.d.ts +22 -0
- package/dist/componentsV2/overlays/DialogV2/DialogV2.d.ts.map +1 -0
- package/dist/componentsV2/overlays/DialogV2/DialogV2.js +77 -0
- package/dist/componentsV2/overlays/DialogV2/DialogV2.module.css +91 -0
- package/dist/componentsV2/overlays/DialogV2/index.d.ts +3 -0
- package/dist/componentsV2/overlays/DialogV2/index.d.ts.map +1 -0
- package/dist/componentsV2/overlays/DialogV2/index.js +1 -0
- package/dist/componentsV2/overlays/DropdownMenuV2/DropdownMenuV2.d.ts +40 -0
- package/dist/componentsV2/overlays/DropdownMenuV2/DropdownMenuV2.d.ts.map +1 -0
- package/dist/componentsV2/overlays/DropdownMenuV2/DropdownMenuV2.js +115 -0
- package/dist/componentsV2/overlays/DropdownMenuV2/DropdownMenuV2.module.css +122 -0
- package/dist/componentsV2/overlays/DropdownMenuV2/index.d.ts +3 -0
- package/dist/componentsV2/overlays/DropdownMenuV2/index.d.ts.map +1 -0
- package/dist/componentsV2/overlays/DropdownMenuV2/index.js +1 -0
- package/dist/componentsV2/overlays/PopoverV2/PopoverV2.d.ts +46 -0
- package/dist/componentsV2/overlays/PopoverV2/PopoverV2.d.ts.map +1 -0
- package/dist/componentsV2/overlays/PopoverV2/PopoverV2.js +41 -0
- package/dist/componentsV2/overlays/PopoverV2/PopoverV2.module.css +63 -0
- package/dist/componentsV2/overlays/PopoverV2/index.d.ts +3 -0
- package/dist/componentsV2/overlays/PopoverV2/index.d.ts.map +1 -0
- package/dist/componentsV2/overlays/PopoverV2/index.js +1 -0
- package/dist/componentsV2/overlays/TooltipV2/TooltipV2.d.ts +18 -0
- package/dist/componentsV2/overlays/TooltipV2/TooltipV2.d.ts.map +1 -0
- package/dist/componentsV2/overlays/TooltipV2/TooltipV2.js +66 -0
- package/dist/componentsV2/overlays/TooltipV2/TooltipV2.module.css +69 -0
- package/dist/componentsV2/overlays/TooltipV2/index.d.ts +3 -0
- package/dist/componentsV2/overlays/TooltipV2/index.d.ts.map +1 -0
- package/dist/componentsV2/overlays/TooltipV2/index.js +1 -0
- package/dist/componentsV2/overlays/layer.d.ts +29 -0
- package/dist/componentsV2/overlays/layer.d.ts.map +1 -0
- package/dist/componentsV2/overlays/layer.js +224 -0
- package/dist/componentsV2/overlays/position.d.ts +32 -0
- package/dist/componentsV2/overlays/position.d.ts.map +1 -0
- package/dist/componentsV2/overlays/position.js +42 -0
- package/dist/componentsV2/status/BadgeV2/BadgeV2.d.ts +18 -0
- package/dist/componentsV2/status/BadgeV2/BadgeV2.d.ts.map +1 -0
- package/dist/componentsV2/status/BadgeV2/BadgeV2.js +7 -0
- package/dist/componentsV2/status/BadgeV2/BadgeV2.module.css +94 -0
- package/dist/componentsV2/status/BadgeV2/index.d.ts +3 -0
- package/dist/componentsV2/status/BadgeV2/index.d.ts.map +1 -0
- package/dist/componentsV2/status/BadgeV2/index.js +1 -0
- package/dist/componentsV2/surfaces/CardV2/CardV2.d.ts +28 -0
- package/dist/componentsV2/surfaces/CardV2/CardV2.d.ts.map +1 -0
- package/dist/componentsV2/surfaces/CardV2/CardV2.js +16 -0
- package/dist/componentsV2/surfaces/CardV2/CardV2.module.css +84 -0
- package/dist/componentsV2/surfaces/CardV2/index.d.ts +3 -0
- package/dist/componentsV2/surfaces/CardV2/index.d.ts.map +1 -0
- package/dist/componentsV2/surfaces/CardV2/index.js +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -0
- package/package.json +16 -5
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import '@corefoundry-labs/forge-tokens/theme-v2/tokens.css';
|
|
2
|
+
import { type AriaRole, type HTMLAttributes, type ReactNode, type RefObject } from 'react';
|
|
3
|
+
import { type OverlayTrigger } from '../layer';
|
|
4
|
+
import type { OverlayAlign, OverlayPlacement } from '../position';
|
|
5
|
+
export type PopoverV2Width = 'compact' | 'regular';
|
|
6
|
+
export type PopoverV2Placement = OverlayPlacement;
|
|
7
|
+
export type PopoverV2Align = OverlayAlign;
|
|
8
|
+
type PopoverV2Base = Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'role' | 'popover' | 'aria-label' | 'aria-labelledby'> & {
|
|
9
|
+
description?: ReactNode;
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
width?: PopoverV2Width;
|
|
12
|
+
placement?: PopoverV2Placement;
|
|
13
|
+
align?: PopoverV2Align;
|
|
14
|
+
role?: AriaRole;
|
|
15
|
+
initialFocus?: 'content' | 'none';
|
|
16
|
+
};
|
|
17
|
+
type PopoverV2Name = {
|
|
18
|
+
title: ReactNode;
|
|
19
|
+
'aria-label'?: undefined;
|
|
20
|
+
'aria-labelledby'?: undefined;
|
|
21
|
+
} | {
|
|
22
|
+
title?: undefined;
|
|
23
|
+
'aria-label': string;
|
|
24
|
+
'aria-labelledby'?: undefined;
|
|
25
|
+
} | {
|
|
26
|
+
title?: undefined;
|
|
27
|
+
'aria-label'?: undefined;
|
|
28
|
+
'aria-labelledby': string;
|
|
29
|
+
};
|
|
30
|
+
type PopoverV2Anchor = {
|
|
31
|
+
trigger: OverlayTrigger;
|
|
32
|
+
anchorRef?: undefined;
|
|
33
|
+
open?: boolean;
|
|
34
|
+
defaultOpen?: boolean;
|
|
35
|
+
onOpenChange?: (open: boolean) => void;
|
|
36
|
+
} | {
|
|
37
|
+
trigger?: undefined;
|
|
38
|
+
anchorRef: RefObject<HTMLElement | null>;
|
|
39
|
+
open: boolean;
|
|
40
|
+
defaultOpen?: undefined;
|
|
41
|
+
onOpenChange: (open: boolean) => void;
|
|
42
|
+
};
|
|
43
|
+
export type PopoverV2Props = PopoverV2Base & PopoverV2Name & PopoverV2Anchor;
|
|
44
|
+
export declare const PopoverV2: import("react").ForwardRefExoticComponent<PopoverV2Props & import("react").RefAttributes<HTMLDivElement>>;
|
|
45
|
+
export {};
|
|
46
|
+
//# sourceMappingURL=PopoverV2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PopoverV2.d.ts","sourceRoot":"","sources":["../../../../src/componentsV2/overlays/PopoverV2/PopoverV2.tsx"],"names":[],"mappings":"AAAA,OAAO,oDAAoD,CAAC;AAC5D,OAAO,EAML,KAAK,QAAQ,EACb,KAAK,cAAc,EAEnB,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAQL,KAAK,cAAc,EACpB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGlE,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,CAAC;AACnD,MAAM,MAAM,kBAAkB,GAAG,gBAAgB,CAAC;AAClD,MAAM,MAAM,cAAc,GAAG,YAAY,CAAC;AAE1C,KAAK,aAAa,GAAG,IAAI,CACvB,cAAc,CAAC,cAAc,CAAC,EAC9B,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,iBAAiB,CAChE,GAAG;IACF,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,YAAY,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;CACnC,CAAC;AAEF,KAAK,aAAa,GACd;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,YAAY,CAAC,EAAE,SAAS,CAAC;IAAC,iBAAiB,CAAC,EAAE,SAAS,CAAA;CAAE,GAC7E;IAAE,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,SAAS,CAAA;CAAE,GAC1E;IAAE,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,YAAY,CAAC,EAAE,SAAS,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/E,KAAK,eAAe,GAChB;IACE,OAAO,EAAE,cAAc,CAAC;IACxB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC,GACD;IACE,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACzC,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC,CAAC;AAEN,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,aAAa,GAAG,eAAe,CAAC;AAE7E,eAAO,MAAM,SAAS,2GAuFpB,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import '@corefoundry-labs/forge-tokens/theme-v2/tokens.css';
|
|
3
|
+
import { cloneElement, forwardRef, useId, useImperativeHandle, useRef, } from 'react';
|
|
4
|
+
import { chain, focusableIn, useAnchoredPanel, useDismiss, useEscapeLayer, useOpenState, useTriggerRef, } from '../layer';
|
|
5
|
+
import styles from './PopoverV2.module.css';
|
|
6
|
+
export const PopoverV2 = forwardRef(function PopoverV2({ trigger, anchorRef: externalAnchor, open: openProp, defaultOpen, onOpenChange, title, description, children, width = 'compact', placement = 'bottom', align = 'start', role = 'dialog', initialFocus = 'content', id, className, 'aria-label': label, 'aria-labelledby': labelledBy, ...props }, ref) {
|
|
7
|
+
const generated = useId();
|
|
8
|
+
const panelId = id ?? generated;
|
|
9
|
+
const [open, setOpen] = useOpenState(openProp, defaultOpen, onOpenChange);
|
|
10
|
+
const panelRef = useRef(null);
|
|
11
|
+
const triggerRef = useRef(null);
|
|
12
|
+
const anchorRef = externalAnchor ?? triggerRef;
|
|
13
|
+
const attachTrigger = useTriggerRef(triggerRef, trigger?.props.ref);
|
|
14
|
+
useImperativeHandle(ref, () => panelRef.current);
|
|
15
|
+
const close = () => setOpen(false);
|
|
16
|
+
useEscapeLayer(open, panelRef, close);
|
|
17
|
+
useDismiss(open, panelRef, anchorRef, close);
|
|
18
|
+
useAnchoredPanel({
|
|
19
|
+
open,
|
|
20
|
+
anchorRef,
|
|
21
|
+
panelRef,
|
|
22
|
+
placement,
|
|
23
|
+
align,
|
|
24
|
+
restoreFocus: true,
|
|
25
|
+
onShown: () => {
|
|
26
|
+
const panel = panelRef.current;
|
|
27
|
+
if (initialFocus === 'none' || !panel)
|
|
28
|
+
return;
|
|
29
|
+
(focusableIn(panel)[0] ?? panel).focus({ preventScroll: true });
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
const titleId = `${panelId}-title`;
|
|
33
|
+
return (_jsxs(_Fragment, { children: [trigger &&
|
|
34
|
+
cloneElement(trigger, {
|
|
35
|
+
ref: attachTrigger,
|
|
36
|
+
'aria-haspopup': role === 'dialog' ? 'dialog' : undefined,
|
|
37
|
+
'aria-expanded': open,
|
|
38
|
+
'aria-controls': role === 'dialog' ? undefined : panelId,
|
|
39
|
+
onClick: chain(trigger.props.onClick, (_event) => setOpen(!open)),
|
|
40
|
+
}), _jsxs("div", { ...props, ref: panelRef, id: panelId, role: role, popover: "manual", tabIndex: -1, "aria-label": label, "aria-labelledby": labelledBy ?? (title != null ? titleId : undefined), "data-state": open ? 'open' : 'closed', "data-width": width, className: [styles.popover, styles[width], className].filter(Boolean).join(' '), children: [title != null && (_jsxs(_Fragment, { children: [_jsx("div", { id: titleId, className: styles.title, children: title }), _jsx("div", { className: styles.separator, "aria-hidden": "true" })] })), description != null && _jsx("div", { className: styles.description, children: description }), children] })] }));
|
|
41
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
.popover {
|
|
2
|
+
--overlay-offset: var(--cf-v2-popover-offset);
|
|
3
|
+
position: fixed;
|
|
4
|
+
inset: auto;
|
|
5
|
+
box-sizing: border-box;
|
|
6
|
+
max-width: calc(100vw - 2 * var(--cf-v2-overlay-viewport-margin));
|
|
7
|
+
max-height: calc(100dvh - 2 * var(--cf-v2-overlay-viewport-margin));
|
|
8
|
+
margin: 0;
|
|
9
|
+
padding: var(--cf-v2-popover-padding-block) var(--cf-v2-popover-padding-inline);
|
|
10
|
+
overflow: auto;
|
|
11
|
+
border: var(--cf-v2-popover-border-width) solid var(--cf-v2-outline);
|
|
12
|
+
border-radius: var(--cf-v2-popover-radius);
|
|
13
|
+
background: var(--cf-v2-surface-container);
|
|
14
|
+
color: var(--cf-v2-on-surface);
|
|
15
|
+
font-family: inherit;
|
|
16
|
+
font-size: var(--cf-v2-popover-body-font-size);
|
|
17
|
+
line-height: normal;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.popover[data-state='open'] {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
gap: var(--cf-v2-popover-gap);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.popover[data-state='closed'] {
|
|
27
|
+
display: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.popover:focus {
|
|
31
|
+
outline: none;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.popover:focus-visible {
|
|
35
|
+
outline: var(--cf-v2-popover-focus-width) solid var(--cf-v2-focus-ring);
|
|
36
|
+
outline-offset: calc(-1 * var(--cf-v2-popover-focus-width));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.compact {
|
|
40
|
+
width: var(--cf-v2-popover-width-compact);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.regular {
|
|
44
|
+
width: var(--cf-v2-popover-width-regular);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.title {
|
|
48
|
+
font-size: var(--cf-v2-popover-title-font-size);
|
|
49
|
+
font-weight: var(--cf-v2-popover-title-font-weight);
|
|
50
|
+
overflow-wrap: anywhere;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.separator {
|
|
54
|
+
flex: none;
|
|
55
|
+
height: var(--cf-v2-popover-border-width);
|
|
56
|
+
background: var(--cf-v2-outline);
|
|
57
|
+
opacity: var(--cf-v2-popover-separator-opacity);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.description {
|
|
61
|
+
color: color-mix(in srgb, var(--cf-v2-on-surface) calc(var(--cf-v2-popover-description-opacity) * 100%), transparent);
|
|
62
|
+
overflow-wrap: anywhere;
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/componentsV2/overlays/PopoverV2/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PopoverV2 } from './PopoverV2';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import '@corefoundry-labs/forge-tokens/theme-v2/tokens.css';
|
|
2
|
+
import { type HTMLAttributes } from 'react';
|
|
3
|
+
import { type OverlayTrigger } from '../layer';
|
|
4
|
+
import type { OverlayPlacement } from '../position';
|
|
5
|
+
export type TooltipV2Placement = OverlayPlacement;
|
|
6
|
+
export type TooltipV2Props = Omit<HTMLAttributes<HTMLDivElement>, 'content' | 'children' | 'role' | 'popover'> & {
|
|
7
|
+
content: string;
|
|
8
|
+
children: OverlayTrigger;
|
|
9
|
+
placement?: TooltipV2Placement;
|
|
10
|
+
delay?: number;
|
|
11
|
+
};
|
|
12
|
+
export declare const TooltipV2: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "role" | "content" | "popover" | "children"> & {
|
|
13
|
+
content: string;
|
|
14
|
+
children: OverlayTrigger;
|
|
15
|
+
placement?: TooltipV2Placement;
|
|
16
|
+
delay?: number;
|
|
17
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
18
|
+
//# sourceMappingURL=TooltipV2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TooltipV2.d.ts","sourceRoot":"","sources":["../../../../src/componentsV2/overlays/TooltipV2/TooltipV2.tsx"],"names":[],"mappings":"AAAA,OAAO,oDAAoD,CAAC;AAC5D,OAAO,EASL,KAAK,cAAc,EAEpB,MAAM,OAAO,CAAC;AACf,OAAO,EAAqE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAClH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,MAAM,MAAM,kBAAkB,GAAG,gBAAgB,CAAC;AAElD,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC,GAAG;IAC/G,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,cAAc,CAAC;IACzB,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAKF,eAAO,MAAM,SAAS;aATX,MAAM;cACL,cAAc;gBACZ,kBAAkB;YACtB,MAAM;kDA+Fd,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import '@corefoundry-labs/forge-tokens/theme-v2/tokens.css';
|
|
3
|
+
import { cloneElement, forwardRef, useEffect, useId, useImperativeHandle, useRef, useState, } from 'react';
|
|
4
|
+
import { chain, useAnchoredPanel, useEscapeLayer, useLatest, useTriggerRef } from '../layer';
|
|
5
|
+
import styles from './TooltipV2.module.css';
|
|
6
|
+
const hoverGrace = 120;
|
|
7
|
+
let visible = null;
|
|
8
|
+
export const TooltipV2 = forwardRef(function TooltipV2({ content, children, placement = 'top', delay = 300, id, className, onPointerEnter, onPointerLeave, ...props }, ref) {
|
|
9
|
+
const generated = useId();
|
|
10
|
+
const tooltipId = id ?? generated;
|
|
11
|
+
const [open, setOpen] = useState(false);
|
|
12
|
+
const panelRef = useRef(null);
|
|
13
|
+
const anchorRef = useRef(null);
|
|
14
|
+
const sources = useRef({ hover: false, focus: false });
|
|
15
|
+
const timer = useRef(undefined);
|
|
16
|
+
useImperativeHandle(ref, () => panelRef.current);
|
|
17
|
+
const dismiss = () => {
|
|
18
|
+
sources.current = { hover: false, focus: false };
|
|
19
|
+
clearTimeout(timer.current);
|
|
20
|
+
if (visible === self)
|
|
21
|
+
visible = null;
|
|
22
|
+
setOpen(false);
|
|
23
|
+
};
|
|
24
|
+
const self = useLatest(dismiss);
|
|
25
|
+
useEffect(() => () => {
|
|
26
|
+
clearTimeout(timer.current);
|
|
27
|
+
if (visible === self)
|
|
28
|
+
visible = null;
|
|
29
|
+
}, [self]);
|
|
30
|
+
const track = (source, active) => {
|
|
31
|
+
sources.current[source] = active;
|
|
32
|
+
const wanted = sources.current.hover || sources.current.focus;
|
|
33
|
+
clearTimeout(timer.current);
|
|
34
|
+
timer.current = setTimeout(() => {
|
|
35
|
+
if (wanted && visible !== self) {
|
|
36
|
+
visible?.current();
|
|
37
|
+
visible = self;
|
|
38
|
+
}
|
|
39
|
+
else if (!wanted && visible === self) {
|
|
40
|
+
visible = null;
|
|
41
|
+
}
|
|
42
|
+
setOpen(wanted);
|
|
43
|
+
}, wanted ? delay : hoverGrace);
|
|
44
|
+
};
|
|
45
|
+
const enter = (event) => {
|
|
46
|
+
if (event.pointerType !== 'touch')
|
|
47
|
+
track('hover', true);
|
|
48
|
+
};
|
|
49
|
+
const leave = () => track('hover', false);
|
|
50
|
+
useEscapeLayer(open, panelRef, dismiss);
|
|
51
|
+
useAnchoredPanel({ open, anchorRef, panelRef, placement, align: 'center' });
|
|
52
|
+
const trigger = children.props;
|
|
53
|
+
const attachTrigger = useTriggerRef(anchorRef, trigger.ref);
|
|
54
|
+
return (_jsxs(_Fragment, { children: [cloneElement(children, {
|
|
55
|
+
ref: attachTrigger,
|
|
56
|
+
'aria-describedby': [trigger['aria-describedby'], tooltipId].filter(Boolean).join(' '),
|
|
57
|
+
onPointerEnter: chain(trigger.onPointerEnter, enter),
|
|
58
|
+
onPointerLeave: chain(trigger.onPointerLeave, leave),
|
|
59
|
+
onPointerDown: chain(trigger.onPointerDown, dismiss),
|
|
60
|
+
onFocus: chain(trigger.onFocus, (event) => {
|
|
61
|
+
if (event.currentTarget.matches(':focus-visible'))
|
|
62
|
+
track('focus', true);
|
|
63
|
+
}),
|
|
64
|
+
onBlur: chain(trigger.onBlur, () => track('focus', false)),
|
|
65
|
+
}), _jsxs("div", { ...props, ref: panelRef, id: tooltipId, role: "tooltip", popover: "manual", "data-state": open ? 'open' : 'closed', onPointerEnter: chain(onPointerEnter, enter), onPointerLeave: chain(onPointerLeave, leave), className: [styles.tooltip, className].filter(Boolean).join(' '), children: [content, _jsx("svg", { className: styles.arrow, viewBox: "0 0 12 9", "aria-hidden": "true", children: _jsx("path", { d: "M0 9 6 0l6 9" }) })] })] }));
|
|
66
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
.tooltip {
|
|
2
|
+
--overlay-offset: var(--cf-v2-tooltip-offset);
|
|
3
|
+
--tooltip-arrow-shift: calc(
|
|
4
|
+
(var(--cf-v2-tooltip-arrow-height) - var(--cf-v2-tooltip-arrow-width)) / 2 - var(--cf-v2-tooltip-border-width)
|
|
5
|
+
);
|
|
6
|
+
position: fixed;
|
|
7
|
+
inset: auto;
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
width: max-content;
|
|
10
|
+
max-width: min(var(--cf-v2-tooltip-max-width), calc(100vw - 2 * var(--cf-v2-overlay-viewport-margin)));
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: var(--cf-v2-tooltip-padding-block) var(--cf-v2-tooltip-padding-inline);
|
|
13
|
+
overflow: visible;
|
|
14
|
+
border: var(--cf-v2-tooltip-border-width) solid var(--cf-v2-outline);
|
|
15
|
+
border-radius: var(--cf-v2-tooltip-radius);
|
|
16
|
+
background: var(--cf-v2-surface-container);
|
|
17
|
+
color: var(--cf-v2-on-surface);
|
|
18
|
+
font-family: inherit;
|
|
19
|
+
font-size: var(--cf-v2-tooltip-font-size);
|
|
20
|
+
font-weight: var(--cf-v2-tooltip-font-weight);
|
|
21
|
+
line-height: normal;
|
|
22
|
+
text-align: center;
|
|
23
|
+
overflow-wrap: anywhere;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.tooltip[data-state='closed'] {
|
|
27
|
+
display: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.arrow {
|
|
31
|
+
position: absolute;
|
|
32
|
+
width: var(--cf-v2-tooltip-arrow-width);
|
|
33
|
+
height: var(--cf-v2-tooltip-arrow-height);
|
|
34
|
+
overflow: visible;
|
|
35
|
+
fill: var(--cf-v2-surface-container);
|
|
36
|
+
stroke: var(--cf-v2-outline);
|
|
37
|
+
stroke-width: var(--cf-v2-tooltip-border-width);
|
|
38
|
+
pointer-events: none;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.tooltip[data-placement='bottom'] .arrow,
|
|
42
|
+
.tooltip[data-placement='top'] .arrow {
|
|
43
|
+
left: calc(var(--overlay-arrow) - var(--cf-v2-tooltip-border-width));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.tooltip[data-placement='left'] .arrow,
|
|
47
|
+
.tooltip[data-placement='right'] .arrow {
|
|
48
|
+
top: calc(var(--overlay-arrow) - var(--cf-v2-tooltip-border-width));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.tooltip[data-placement='bottom'] .arrow {
|
|
52
|
+
bottom: calc(100% - var(--cf-v2-tooltip-border-width));
|
|
53
|
+
transform: translateX(-50%);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.tooltip[data-placement='top'] .arrow {
|
|
57
|
+
top: calc(100% - var(--cf-v2-tooltip-border-width));
|
|
58
|
+
transform: translateX(-50%) rotate(180deg);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.tooltip[data-placement='left'] .arrow {
|
|
62
|
+
left: calc(100% + var(--tooltip-arrow-shift));
|
|
63
|
+
transform: translateY(-50%) rotate(90deg);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.tooltip[data-placement='right'] .arrow {
|
|
67
|
+
right: calc(100% + var(--tooltip-arrow-shift));
|
|
68
|
+
transform: translateY(-50%) rotate(-90deg);
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/componentsV2/overlays/TooltipV2/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TooltipV2 } from './TooltipV2';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactElement, type Ref, type RefObject } from 'react';
|
|
2
|
+
import { type OverlayAlign, type OverlayPlacement } from './position';
|
|
3
|
+
export type OverlayTriggerProps = HTMLAttributes<HTMLElement> & {
|
|
4
|
+
ref?: Ref<HTMLElement>;
|
|
5
|
+
};
|
|
6
|
+
export type OverlayTrigger = ReactElement<OverlayTriggerProps>;
|
|
7
|
+
type NodeRef = RefObject<HTMLElement | null>;
|
|
8
|
+
export declare function focusableIn(root: HTMLElement): HTMLElement[];
|
|
9
|
+
export declare function useTriggerRef(target: RefObject<HTMLElement | null>, external: Ref<HTMLElement> | undefined): (node: HTMLElement | null) => () => void;
|
|
10
|
+
export declare function chain<E>(...handlers: (((event: E) => void) | undefined)[]): (event: E) => void;
|
|
11
|
+
export declare function useLatest<T>(value: T): RefObject<T>;
|
|
12
|
+
export declare function useOpenLifecycle(open: boolean, onOpen: () => () => void): void;
|
|
13
|
+
export declare function returnFocus(element: Element | null): void;
|
|
14
|
+
export declare function useEscapeLayer(active: boolean, nodeRef: NodeRef, onEscape: () => void): void;
|
|
15
|
+
export declare function useDismiss(active: boolean, panelRef: NodeRef, anchorRef: NodeRef, onDismiss: () => void): void;
|
|
16
|
+
export declare function useOpenState(open: boolean | undefined, defaultOpen: boolean | undefined, onOpenChange: ((open: boolean) => void) | undefined): readonly [boolean, (next: boolean) => void];
|
|
17
|
+
type AnchoredPanel = {
|
|
18
|
+
open: boolean;
|
|
19
|
+
anchorRef: NodeRef;
|
|
20
|
+
panelRef: NodeRef;
|
|
21
|
+
placement: OverlayPlacement;
|
|
22
|
+
align: OverlayAlign;
|
|
23
|
+
restoreFocus?: boolean;
|
|
24
|
+
onShown?: () => void;
|
|
25
|
+
};
|
|
26
|
+
export declare function useAnchoredPanel({ open, anchorRef, panelRef, placement, align, restoreFocus, onShown, }: AnchoredPanel): void;
|
|
27
|
+
export declare function lockScroll(): () => void;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=layer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer.d.ts","sourceRoot":"","sources":["../../../src/componentsV2/overlays/layer.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,GAAG,EACR,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAAmB,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEvF,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,WAAW,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,CAAA;CAAE,CAAC;AAC3F,MAAM,MAAM,cAAc,GAAG,YAAY,CAAC,mBAAmB,CAAC,CAAC;AAE/D,KAAK,OAAO,GAAG,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;AAe7C,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,iBAI5C;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,WAAW,CAAC,GAAG,SAAS,UAEhG,WAAW,GAAG,IAAI,gBAa5B;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC,EAAE,IAChE,OAAO,CAAC,UAGjB;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,gBAMpC;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,IAAI,QAcvE;AAID,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,QAQlD;AAkBD,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,QAWrF;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,QAiBvG;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,OAAO,GAAG,SAAS,EACzB,WAAW,EAAE,OAAO,GAAG,SAAS,EAChC,YAAY,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,6BAO1C,OAAO,WAOjB;AAED,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,SAAS,EACT,KAAK,EACL,YAAoB,EACpB,OAAO,GACR,EAAE,aAAa,QA6Df;AAKD,wBAAgB,UAAU,eAoBzB"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { useCallback, useLayoutEffect, useRef, useState, } from 'react';
|
|
2
|
+
import { computePosition } from './position';
|
|
3
|
+
const focusableSelector = [
|
|
4
|
+
'a[href]',
|
|
5
|
+
'button:not([disabled])',
|
|
6
|
+
'input:not([disabled]):not([type="hidden"])',
|
|
7
|
+
'select:not([disabled])',
|
|
8
|
+
'textarea:not([disabled])',
|
|
9
|
+
'summary',
|
|
10
|
+
'iframe',
|
|
11
|
+
'[contenteditable]:not([contenteditable="false"])',
|
|
12
|
+
'[tabindex]',
|
|
13
|
+
].join(',');
|
|
14
|
+
export function focusableIn(root) {
|
|
15
|
+
return [...root.querySelectorAll(focusableSelector)].filter((element) => element.tabIndex >= 0 && element.checkVisibility?.() !== false);
|
|
16
|
+
}
|
|
17
|
+
export function useTriggerRef(target, external) {
|
|
18
|
+
return useCallback((node) => {
|
|
19
|
+
target.current = node;
|
|
20
|
+
const cleanup = typeof external === 'function' ? external(node) : undefined;
|
|
21
|
+
if (external && typeof external !== 'function')
|
|
22
|
+
external.current = node;
|
|
23
|
+
return () => {
|
|
24
|
+
target.current = null;
|
|
25
|
+
if (typeof cleanup === 'function')
|
|
26
|
+
cleanup();
|
|
27
|
+
else if (typeof external === 'function')
|
|
28
|
+
external(null);
|
|
29
|
+
else if (external)
|
|
30
|
+
external.current = null;
|
|
31
|
+
};
|
|
32
|
+
}, [target, external]);
|
|
33
|
+
}
|
|
34
|
+
export function chain(...handlers) {
|
|
35
|
+
return (event) => {
|
|
36
|
+
for (const handler of handlers)
|
|
37
|
+
handler?.(event);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function useLatest(value) {
|
|
41
|
+
const latest = useRef(value);
|
|
42
|
+
useLayoutEffect(() => {
|
|
43
|
+
latest.current = value;
|
|
44
|
+
});
|
|
45
|
+
return latest;
|
|
46
|
+
}
|
|
47
|
+
export function useOpenLifecycle(open, onOpen) {
|
|
48
|
+
const opener = useLatest(onOpen);
|
|
49
|
+
const teardown = useRef(null);
|
|
50
|
+
const finish = useCallback(() => {
|
|
51
|
+
teardown.current?.();
|
|
52
|
+
teardown.current = null;
|
|
53
|
+
}, []);
|
|
54
|
+
useLayoutEffect(() => {
|
|
55
|
+
if (open)
|
|
56
|
+
teardown.current = opener.current();
|
|
57
|
+
else
|
|
58
|
+
finish();
|
|
59
|
+
}, [open, opener, finish]);
|
|
60
|
+
useLayoutEffect(() => finish, [finish]);
|
|
61
|
+
}
|
|
62
|
+
const anchors = new WeakMap();
|
|
63
|
+
export function returnFocus(element) {
|
|
64
|
+
let target = element;
|
|
65
|
+
while (target instanceof HTMLElement) {
|
|
66
|
+
target.focus({ preventScroll: true });
|
|
67
|
+
if (document.activeElement === target)
|
|
68
|
+
return;
|
|
69
|
+
const panel = target.closest('[popover]');
|
|
70
|
+
target = panel ? (anchors.get(panel)?.() ?? null) : null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const layers = [];
|
|
74
|
+
function topLayer() {
|
|
75
|
+
return [...layers]
|
|
76
|
+
.reverse()
|
|
77
|
+
.find((layer) => !layers.some((other) => other !== layer && layer.node()?.contains(other.node())));
|
|
78
|
+
}
|
|
79
|
+
function onDocumentKeyDown(event) {
|
|
80
|
+
if (event.key !== 'Escape' || event.defaultPrevented)
|
|
81
|
+
return;
|
|
82
|
+
const layer = topLayer();
|
|
83
|
+
if (!layer)
|
|
84
|
+
return;
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
layer.escape();
|
|
87
|
+
}
|
|
88
|
+
export function useEscapeLayer(active, nodeRef, onEscape) {
|
|
89
|
+
const latest = useLatest(onEscape);
|
|
90
|
+
useLayoutEffect(() => {
|
|
91
|
+
if (!active)
|
|
92
|
+
return;
|
|
93
|
+
const layer = { node: () => nodeRef.current, escape: () => latest.current() };
|
|
94
|
+
if (layers.push(layer) === 1)
|
|
95
|
+
document.addEventListener('keydown', onDocumentKeyDown);
|
|
96
|
+
return () => {
|
|
97
|
+
layers.splice(layers.indexOf(layer), 1);
|
|
98
|
+
if (layers.length === 0)
|
|
99
|
+
document.removeEventListener('keydown', onDocumentKeyDown);
|
|
100
|
+
};
|
|
101
|
+
}, [active, nodeRef, latest]);
|
|
102
|
+
}
|
|
103
|
+
export function useDismiss(active, panelRef, anchorRef, onDismiss) {
|
|
104
|
+
const latest = useLatest(onDismiss);
|
|
105
|
+
useLayoutEffect(() => {
|
|
106
|
+
if (!active)
|
|
107
|
+
return;
|
|
108
|
+
const dismiss = (event) => {
|
|
109
|
+
const target = event.target;
|
|
110
|
+
if (!(target instanceof Node))
|
|
111
|
+
return;
|
|
112
|
+
if (panelRef.current?.contains(target) || anchorRef.current?.contains(target))
|
|
113
|
+
return;
|
|
114
|
+
latest.current();
|
|
115
|
+
};
|
|
116
|
+
document.addEventListener('pointerdown', dismiss, true);
|
|
117
|
+
document.addEventListener('focusin', dismiss);
|
|
118
|
+
return () => {
|
|
119
|
+
document.removeEventListener('pointerdown', dismiss, true);
|
|
120
|
+
document.removeEventListener('focusin', dismiss);
|
|
121
|
+
};
|
|
122
|
+
}, [active, panelRef, anchorRef, latest]);
|
|
123
|
+
}
|
|
124
|
+
export function useOpenState(open, defaultOpen, onOpenChange) {
|
|
125
|
+
const [internal, setInternal] = useState(defaultOpen ?? false);
|
|
126
|
+
const latest = useLatest(onOpenChange);
|
|
127
|
+
const controlled = open !== undefined;
|
|
128
|
+
const current = controlled ? open : internal;
|
|
129
|
+
const change = useCallback((next) => {
|
|
130
|
+
if (!controlled)
|
|
131
|
+
setInternal(next);
|
|
132
|
+
latest.current?.(next);
|
|
133
|
+
}, [controlled, latest]);
|
|
134
|
+
return [current, change];
|
|
135
|
+
}
|
|
136
|
+
export function useAnchoredPanel({ open, anchorRef, panelRef, placement, align, restoreFocus = false, onShown, }) {
|
|
137
|
+
const shown = useLatest(onShown);
|
|
138
|
+
const [attempt, retry] = useState(0);
|
|
139
|
+
useOpenLifecycle(open, () => {
|
|
140
|
+
const panel = panelRef.current;
|
|
141
|
+
const previous = document.activeElement;
|
|
142
|
+
if (panel)
|
|
143
|
+
anchors.set(panel, () => anchorRef.current);
|
|
144
|
+
panel?.showPopover?.();
|
|
145
|
+
return () => {
|
|
146
|
+
const active = document.activeElement;
|
|
147
|
+
const lost = !active || active === document.body || panel?.contains(active);
|
|
148
|
+
panel?.hidePopover?.();
|
|
149
|
+
if (!restoreFocus || !lost)
|
|
150
|
+
return;
|
|
151
|
+
const target = previous instanceof HTMLElement && previous !== document.body ? previous : anchorRef.current;
|
|
152
|
+
returnFocus(target);
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
useLayoutEffect(() => {
|
|
156
|
+
const panel = panelRef.current;
|
|
157
|
+
const anchor = anchorRef.current;
|
|
158
|
+
if (!open || !panel)
|
|
159
|
+
return;
|
|
160
|
+
if (!anchor) {
|
|
161
|
+
if (attempt === 0)
|
|
162
|
+
retry(1);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const update = () => {
|
|
166
|
+
const styles = getComputedStyle(panel);
|
|
167
|
+
const { width, height } = panel.getBoundingClientRect();
|
|
168
|
+
const { left, top, placement: side, arrow } = computePosition({
|
|
169
|
+
anchor: anchor.getBoundingClientRect(),
|
|
170
|
+
panel: { width, height },
|
|
171
|
+
viewport: { width: document.documentElement.clientWidth, height: window.innerHeight },
|
|
172
|
+
placement,
|
|
173
|
+
align,
|
|
174
|
+
offset: parseFloat(styles.getPropertyValue('--overlay-offset')) || 0,
|
|
175
|
+
margin: parseFloat(styles.getPropertyValue('--cf-v2-overlay-viewport-margin')) || 0,
|
|
176
|
+
rtl: styles.direction === 'rtl',
|
|
177
|
+
});
|
|
178
|
+
panel.style.left = `${left}px`;
|
|
179
|
+
panel.style.top = `${top}px`;
|
|
180
|
+
panel.style.setProperty('--overlay-arrow', `${arrow}px`);
|
|
181
|
+
panel.dataset.placement = side;
|
|
182
|
+
};
|
|
183
|
+
update();
|
|
184
|
+
window.addEventListener('resize', update);
|
|
185
|
+
window.addEventListener('scroll', update, true);
|
|
186
|
+
const observer = typeof ResizeObserver === 'undefined' ? undefined : new ResizeObserver(update);
|
|
187
|
+
observer?.observe(panel);
|
|
188
|
+
observer?.observe(anchor);
|
|
189
|
+
return () => {
|
|
190
|
+
window.removeEventListener('resize', update);
|
|
191
|
+
window.removeEventListener('scroll', update, true);
|
|
192
|
+
observer?.disconnect();
|
|
193
|
+
};
|
|
194
|
+
}, [open, panelRef, anchorRef, placement, align, attempt]);
|
|
195
|
+
useLayoutEffect(() => {
|
|
196
|
+
if (open)
|
|
197
|
+
shown.current?.();
|
|
198
|
+
}, [open, shown]);
|
|
199
|
+
}
|
|
200
|
+
let locks = 0;
|
|
201
|
+
let unlock = () => { };
|
|
202
|
+
export function lockScroll() {
|
|
203
|
+
if (locks === 0) {
|
|
204
|
+
const root = document.documentElement;
|
|
205
|
+
const { body } = document;
|
|
206
|
+
const gutter = window.innerWidth - root.clientWidth;
|
|
207
|
+
const overflow = root.style.overflow;
|
|
208
|
+
const padding = body.style.paddingInlineEnd;
|
|
209
|
+
const current = parseFloat(getComputedStyle(body).paddingInlineEnd) || 0;
|
|
210
|
+
root.style.overflow = 'hidden';
|
|
211
|
+
if (gutter > 0)
|
|
212
|
+
body.style.paddingInlineEnd = `${current + gutter}px`;
|
|
213
|
+
unlock = () => {
|
|
214
|
+
root.style.overflow = overflow;
|
|
215
|
+
body.style.paddingInlineEnd = padding;
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
locks += 1;
|
|
219
|
+
return () => {
|
|
220
|
+
locks -= 1;
|
|
221
|
+
if (locks === 0)
|
|
222
|
+
unlock();
|
|
223
|
+
};
|
|
224
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type OverlayPlacement = 'top' | 'bottom' | 'left' | 'right';
|
|
2
|
+
export type OverlayAlign = 'start' | 'center' | 'end';
|
|
3
|
+
type Box = {
|
|
4
|
+
left: number;
|
|
5
|
+
top: number;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
};
|
|
9
|
+
export type PositionInput = {
|
|
10
|
+
anchor: Box;
|
|
11
|
+
panel: {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
};
|
|
15
|
+
viewport: {
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
};
|
|
19
|
+
placement: OverlayPlacement;
|
|
20
|
+
align: OverlayAlign;
|
|
21
|
+
offset: number;
|
|
22
|
+
margin: number;
|
|
23
|
+
rtl?: boolean;
|
|
24
|
+
};
|
|
25
|
+
export declare function computePosition({ anchor, panel, viewport, placement, align, offset, margin, rtl }: PositionInput): {
|
|
26
|
+
left: number;
|
|
27
|
+
top: number;
|
|
28
|
+
placement: OverlayPlacement;
|
|
29
|
+
arrow: number;
|
|
30
|
+
};
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=position.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"position.d.ts","sourceRoot":"","sources":["../../../src/componentsV2/overlays/position.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AACnE,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEtD,KAAK,GAAG,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExE,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,GAAG,CAAC;IACZ,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,SAAS,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAgBF,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,GAAW,EAAE,EAAE,aAAa;;;;;EAgCxH"}
|