@basic-ui/core 0.0.52 → 0.0.54

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.
@@ -1,154 +1,154 @@
1
- import type { HTMLAttributes, ElementType, RefObject, ReactNode } from 'react';
2
- import {
3
- forwardRef,
4
- useRef,
5
- useEffect,
6
- useLayoutEffect,
7
- useMemo,
8
- memo,
9
- } from 'react';
10
- import type {
11
- Placement,
12
- Modifier,
13
- PositioningStrategy,
14
- Instance,
15
- Rect,
16
- } from '@popperjs/core';
17
- import { createPopper } from '@popperjs/core';
18
- import type { OffsetModifier } from '@popperjs/core/lib/modifiers/offset';
19
- import type { ArrowModifier } from '@popperjs/core/lib/modifiers/arrow';
20
-
21
- import { assignMultipleRefs } from '../utils/assign-ref';
22
- import type { PopperContextProps } from './context';
23
- import { PopperProvider } from './context';
24
- import { Portal } from '../Portal';
25
-
26
- const useEnhancedEffect =
27
- typeof window !== 'undefined' ? useLayoutEffect : useEffect;
28
-
29
- export type OffsetsFunction = (arg0: {
30
- popper: Rect;
31
- reference: Rect;
32
- placement: Placement;
33
- }) => [number | null | undefined, number | null | undefined];
34
-
35
- export interface PopperProps extends HTMLAttributes<HTMLDivElement> {
36
- as?: ElementType<any>;
37
- innerAs?: ElementType<any>;
38
- anchorEl: RefObject<HTMLElement>;
39
- children?: ReactNode;
40
- placement?: Placement;
41
- strategy?: PositioningStrategy;
42
- modifiers?: Array<Partial<Modifier<any, any>>>;
43
- usePortal?: boolean;
44
- portalSelector?: string;
45
- /**
46
- * Displaces the popper along the reference element.
47
- */
48
- skidding?: number;
49
- /**
50
- * Displaces the popper away from, or toward, the reference element in the direction of its placement. A positive number displaces it further away, while a negative number lets it overlap the reference.
51
- */
52
- distance?: number;
53
- /**
54
- * An optional function that must return a pair of [skidding, padding]. Useful for doing things like, displace the popper by 100%.
55
- */
56
- offsetFn?: OffsetsFunction;
57
- /**
58
- * If you don't want the arrow to reach the very edge of the popper (this is common if your popper has rounded corners using border-radius), you can apply some padding to it.
59
- */
60
- arrowPadding?: number;
61
- }
62
-
63
- const emptyModifiers: Array<Partial<Modifier<any, any>>> = [];
64
- export const Popper = memo(
65
- forwardRef<HTMLDivElement, PopperProps>(function Popper(
66
- {
67
- placement = 'bottom',
68
- strategy = 'absolute',
69
- as: Comp = 'div',
70
- innerAs,
71
- anchorEl,
72
- children,
73
- modifiers = emptyModifiers,
74
- usePortal = false,
75
- style = {},
76
- portalSelector = 'body',
77
- distance = 0,
78
- skidding = 0,
79
- arrowPadding = 5,
80
- offsetFn,
81
- ...props
82
- },
83
- forwardedRef
84
- ) {
85
- const arrowRef = useRef<HTMLSpanElement>(null);
86
-
87
- const popperRef = useRef<HTMLDivElement | null>(null);
88
- const popperEngineInstance = useRef<null | Instance>(null);
89
-
90
- const defaultModifiers: Array<Partial<Modifier<any, any>>> = useMemo(() => {
91
- const arrowModifier: Omit<ArrowModifier, 'enabled' | 'fn' | 'phase'> = {
92
- name: 'arrow',
93
- options: {
94
- element: '[data-popper-arrow]',
95
- padding: arrowPadding,
96
- },
97
- };
98
- const offsetModifier: Omit<OffsetModifier, 'enabled' | 'fn' | 'phase'> = {
99
- name: 'offset',
100
- options: {
101
- offset: offsetFn ?? [skidding, distance],
102
- },
103
- };
104
-
105
- return [arrowModifier, offsetModifier];
106
- }, [arrowPadding, distance, skidding, offsetFn]);
107
-
108
- useEnhancedEffect(() => {
109
- if (anchorEl && anchorEl.current && popperRef.current) {
110
- popperEngineInstance.current = createPopper(
111
- anchorEl.current,
112
- popperRef.current,
113
- {
114
- placement,
115
- strategy,
116
- modifiers: [...defaultModifiers, ...modifiers],
117
- }
118
- );
119
- }
120
-
121
- return () => {
122
- popperEngineInstance.current && popperEngineInstance.current.destroy();
123
- popperEngineInstance.current = null;
124
- };
125
- }, [anchorEl, modifiers, placement, strategy, defaultModifiers]);
126
-
127
- useEnhancedEffect(() => {
128
- popperEngineInstance.current?.forceUpdate();
129
- }, [props.hidden || props['aria-hidden']]);
130
-
131
- const contextValue: PopperContextProps = {
132
- arrowRef,
133
- };
134
-
135
- const ret = (
136
- <PopperProvider value={contextValue}>
137
- <Comp
138
- {...props}
139
- as={innerAs}
140
- ref={assignMultipleRefs(popperRef, forwardedRef)}
141
- style={{ position: 'fixed', left: -5000, top: -5000, ...style }}
142
- >
143
- {children}
144
- </Comp>
145
- </PopperProvider>
146
- );
147
-
148
- if (usePortal) {
149
- return <Portal selector={portalSelector}>{ret}</Portal>;
150
- }
151
-
152
- return ret;
153
- })
154
- );
1
+ import type { HTMLAttributes, ElementType, RefObject, ReactNode } from 'react';
2
+ import {
3
+ forwardRef,
4
+ useRef,
5
+ useEffect,
6
+ useLayoutEffect,
7
+ useMemo,
8
+ memo,
9
+ } from 'react';
10
+ import type {
11
+ Placement,
12
+ Modifier,
13
+ PositioningStrategy,
14
+ Instance,
15
+ Rect,
16
+ } from '@popperjs/core';
17
+ import { createPopper } from '@popperjs/core';
18
+ import type { OffsetModifier } from '@popperjs/core/lib/modifiers/offset';
19
+ import type { ArrowModifier } from '@popperjs/core/lib/modifiers/arrow';
20
+
21
+ import { assignMultipleRefs } from '../utils/assign-ref';
22
+ import type { PopperContextProps } from './context';
23
+ import { PopperProvider } from './context';
24
+ import { Portal } from '../Portal';
25
+
26
+ const useEnhancedEffect =
27
+ typeof window !== 'undefined' ? useLayoutEffect : useEffect;
28
+
29
+ export type OffsetsFunction = (arg0: {
30
+ popper: Rect;
31
+ reference: Rect;
32
+ placement: Placement;
33
+ }) => [number | null | undefined, number | null | undefined];
34
+
35
+ export interface PopperProps extends HTMLAttributes<HTMLDivElement> {
36
+ as?: ElementType<any>;
37
+ innerAs?: ElementType<any>;
38
+ anchorEl: RefObject<HTMLElement>;
39
+ children?: ReactNode;
40
+ placement?: Placement;
41
+ strategy?: PositioningStrategy;
42
+ modifiers?: Array<Partial<Modifier<any, any>>>;
43
+ usePortal?: boolean;
44
+ portalSelector?: string;
45
+ /**
46
+ * Displaces the popper along the reference element.
47
+ */
48
+ skidding?: number;
49
+ /**
50
+ * Displaces the popper away from, or toward, the reference element in the direction of its placement. A positive number displaces it further away, while a negative number lets it overlap the reference.
51
+ */
52
+ distance?: number;
53
+ /**
54
+ * An optional function that must return a pair of [skidding, padding]. Useful for doing things like, displace the popper by 100%.
55
+ */
56
+ offsetFn?: OffsetsFunction;
57
+ /**
58
+ * If you don't want the arrow to reach the very edge of the popper (this is common if your popper has rounded corners using border-radius), you can apply some padding to it.
59
+ */
60
+ arrowPadding?: number;
61
+ }
62
+
63
+ const emptyModifiers: Array<Partial<Modifier<any, any>>> = [];
64
+ export const Popper = memo(
65
+ forwardRef<HTMLDivElement, PopperProps>(function Popper(
66
+ {
67
+ placement = 'bottom',
68
+ strategy = 'absolute',
69
+ as: Comp = 'div',
70
+ innerAs,
71
+ anchorEl,
72
+ children,
73
+ modifiers = emptyModifiers,
74
+ usePortal = false,
75
+ style = {},
76
+ portalSelector,
77
+ distance = 0,
78
+ skidding = 0,
79
+ arrowPadding = 5,
80
+ offsetFn,
81
+ ...props
82
+ },
83
+ forwardedRef
84
+ ) {
85
+ const arrowRef = useRef<HTMLSpanElement>(null);
86
+
87
+ const popperRef = useRef<HTMLDivElement | null>(null);
88
+ const popperEngineInstance = useRef<null | Instance>(null);
89
+
90
+ const defaultModifiers: Array<Partial<Modifier<any, any>>> = useMemo(() => {
91
+ const arrowModifier: Omit<ArrowModifier, 'enabled' | 'fn' | 'phase'> = {
92
+ name: 'arrow',
93
+ options: {
94
+ element: '[data-popper-arrow]',
95
+ padding: arrowPadding,
96
+ },
97
+ };
98
+ const offsetModifier: Omit<OffsetModifier, 'enabled' | 'fn' | 'phase'> = {
99
+ name: 'offset',
100
+ options: {
101
+ offset: offsetFn ?? [skidding, distance],
102
+ },
103
+ };
104
+
105
+ return [arrowModifier, offsetModifier];
106
+ }, [arrowPadding, distance, skidding, offsetFn]);
107
+
108
+ useEnhancedEffect(() => {
109
+ if (anchorEl && anchorEl.current && popperRef.current) {
110
+ popperEngineInstance.current = createPopper(
111
+ anchorEl.current,
112
+ popperRef.current,
113
+ {
114
+ placement,
115
+ strategy,
116
+ modifiers: [...defaultModifiers, ...modifiers],
117
+ }
118
+ );
119
+ }
120
+
121
+ return () => {
122
+ popperEngineInstance.current && popperEngineInstance.current.destroy();
123
+ popperEngineInstance.current = null;
124
+ };
125
+ }, [anchorEl, modifiers, placement, strategy, defaultModifiers]);
126
+
127
+ useEnhancedEffect(() => {
128
+ popperEngineInstance.current?.forceUpdate();
129
+ }, [props.hidden || props['aria-hidden']]);
130
+
131
+ const contextValue: PopperContextProps = {
132
+ arrowRef,
133
+ };
134
+
135
+ const ret = (
136
+ <PopperProvider value={contextValue}>
137
+ <Comp
138
+ {...props}
139
+ as={innerAs}
140
+ ref={assignMultipleRefs(popperRef, forwardedRef)}
141
+ style={{ position: 'fixed', left: -5000, top: -5000, ...style }}
142
+ >
143
+ {children}
144
+ </Comp>
145
+ </PopperProvider>
146
+ );
147
+
148
+ if (usePortal) {
149
+ return <Portal selector={portalSelector}>{ret}</Portal>;
150
+ }
151
+
152
+ return ret;
153
+ })
154
+ );
@@ -1,20 +1,31 @@
1
- import type { FC, ReactNode } from 'react';
2
- import { createPortal } from 'react-dom';
3
-
4
- export interface PortalProps {
5
- children?: ReactNode;
6
- selector?: string;
7
- }
8
-
9
- export const Portal: FC<PortalProps> = ({ children, selector = 'body' }) => {
10
- if (typeof window === 'undefined') {
11
- return null;
12
- }
13
-
14
- const dom = document.querySelector(selector);
15
- if (dom) {
16
- return createPortal(<div data-portal="">{children}</div>, dom);
17
- }
18
-
19
- return null;
20
- };
1
+ import { useContext, type FC, type ReactNode } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+
4
+ import type { PortalSelectorFn } from './PortalSelectorProvider';
5
+ import { PortalSelectorContext } from './PortalSelectorProvider';
6
+
7
+ export interface PortalProps {
8
+ children?: ReactNode;
9
+ selector?: PortalSelectorFn;
10
+ }
11
+
12
+ export const Portal: FC<PortalProps> = ({
13
+ children,
14
+ selector: selectorProp,
15
+ }) => {
16
+ const selectorCtx = useContext(PortalSelectorContext);
17
+ if (typeof window === 'undefined') {
18
+ return null;
19
+ }
20
+
21
+ const selector = selectorProp || selectorCtx || 'body';
22
+ const dom =
23
+ typeof selector === 'string'
24
+ ? document.querySelector(selector)
25
+ : selector();
26
+ if (dom) {
27
+ return createPortal(<div data-portal="">{children}</div>, dom);
28
+ }
29
+
30
+ return null;
31
+ };
@@ -0,0 +1,24 @@
1
+ import { createContext } from 'react';
2
+ import type { ReactNode } from 'react';
3
+
4
+ export type PortalSelectorFn = string | (() => Element | null);
5
+ export const PortalSelectorContext = createContext<PortalSelectorFn | null>(
6
+ null
7
+ );
8
+ PortalSelectorContext.displayName = 'PortalSelectorContext';
9
+
10
+ export type PortalSelectorProviderProps = {
11
+ selector: PortalSelectorFn;
12
+ children?: ReactNode | ReactNode[];
13
+ };
14
+
15
+ export const PortalSelectorProvider = ({
16
+ selector,
17
+ children,
18
+ }: PortalSelectorProviderProps) => {
19
+ return (
20
+ <PortalSelectorContext.Provider value={selector}>
21
+ {children}
22
+ </PortalSelectorContext.Provider>
23
+ );
24
+ };
@@ -1 +1,6 @@
1
- export * from './Portal';
1
+ export * from './Portal';
2
+ export {
3
+ PortalSelectorProvider,
4
+ type PortalSelectorProviderProps,
5
+ type PortalSelectorFn,
6
+ } from './PortalSelectorProvider';