entangle-ui 0.8.2 → 0.9.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/CHANGELOG.md +77 -0
- package/dist/esm/assets/src/components/controls/Combobox/Combobox.css.ts.vanilla-B7B5ttkq.css +210 -0
- package/dist/esm/assets/src/components/controls/FileUploader/FileUploader.css.ts.vanilla-T4nRiI7s.css +194 -0
- package/dist/esm/assets/src/components/controls/MultiSelect/MultiSelect.css.ts.vanilla-CdYayqaF.css +311 -0
- package/dist/esm/assets/src/components/controls/TagInput/TagInput.css.ts.vanilla-hnkMOPp1.css +141 -0
- package/dist/esm/assets/src/components/data/DataTable/DataTable.css.ts.vanilla-CmRgtjIW.css +231 -0
- package/dist/esm/assets/src/components/feedback/Alert/{Alert.css.ts.vanilla-CRAI-xHx.css → Alert.css.ts.vanilla-CfCDsIEg.css} +2 -0
- package/dist/esm/assets/src/components/feedback/CommandPalette/CommandPalette.css.ts.vanilla-DGdrLKYZ.css +160 -0
- package/dist/esm/assets/src/components/feedback/Drawer/Drawer.css.ts.vanilla-CLPTOUrA.css +247 -0
- package/dist/esm/assets/src/components/feedback/Skeleton/SkeletonLayout.css.ts.vanilla-Db7bpqiI.css +75 -0
- package/dist/esm/assets/src/components/feedback/Stat/Stat.css.ts.vanilla-GBk3JAMB.css +69 -0
- package/dist/esm/assets/src/components/layout/Card/Card.css.ts.vanilla-Ducn1gUX.css +124 -0
- package/dist/esm/assets/src/components/navigation/Pagination/Pagination.css.ts.vanilla-CmlFyyjh.css +103 -0
- package/dist/esm/assets/src/components/primitives/HoverCard/HoverCard.css.ts.vanilla-BYT0qbLp.css +41 -0
- package/dist/esm/components/Icons/CloudUploadIcon.js +24 -0
- package/dist/esm/components/Icons/CloudUploadIcon.js.map +1 -0
- package/dist/esm/components/Icons/ExternalLinkIcon.js +26 -0
- package/dist/esm/components/Icons/ExternalLinkIcon.js.map +1 -0
- package/dist/esm/components/Icons/FirstIcon.js +23 -0
- package/dist/esm/components/Icons/FirstIcon.js.map +1 -0
- package/dist/esm/components/Icons/LastIcon.js +23 -0
- package/dist/esm/components/Icons/LastIcon.js.map +1 -0
- package/dist/esm/components/Icons/UnlinkIcon.js +26 -0
- package/dist/esm/components/Icons/UnlinkIcon.js.map +1 -0
- package/dist/esm/components/controls/Combobox/Combobox.css.js +20 -0
- package/dist/esm/components/controls/Combobox/Combobox.css.js.map +1 -0
- package/dist/esm/components/controls/Combobox/Combobox.js +354 -0
- package/dist/esm/components/controls/Combobox/Combobox.js.map +1 -0
- package/dist/esm/components/controls/FileUploader/FileUploader.css.js +20 -0
- package/dist/esm/components/controls/FileUploader/FileUploader.css.js.map +1 -0
- package/dist/esm/components/controls/FileUploader/FileUploader.js +264 -0
- package/dist/esm/components/controls/FileUploader/FileUploader.js.map +1 -0
- package/dist/esm/components/controls/MultiSelect/MultiSelect.css.js +23 -0
- package/dist/esm/components/controls/MultiSelect/MultiSelect.css.js.map +1 -0
- package/dist/esm/components/controls/MultiSelect/MultiSelect.js +269 -0
- package/dist/esm/components/controls/MultiSelect/MultiSelect.js.map +1 -0
- package/dist/esm/components/controls/Select/Select.js +5 -4
- package/dist/esm/components/controls/Select/Select.js.map +1 -1
- package/dist/esm/components/controls/TagInput/TagInput.css.js +12 -0
- package/dist/esm/components/controls/TagInput/TagInput.css.js.map +1 -0
- package/dist/esm/components/controls/TagInput/TagInput.js +189 -0
- package/dist/esm/components/controls/TagInput/TagInput.js.map +1 -0
- package/dist/esm/components/controls/TreeView/TreeNode.js +87 -1
- package/dist/esm/components/controls/TreeView/TreeNode.js.map +1 -1
- package/dist/esm/components/controls/VectorInput/VectorInput.js +87 -4
- package/dist/esm/components/controls/VectorInput/VectorInput.js.map +1 -1
- package/dist/esm/components/data/DataTable/DataTable.css.js +25 -0
- package/dist/esm/components/data/DataTable/DataTable.css.js.map +1 -0
- package/dist/esm/components/data/DataTable/DataTable.js +502 -0
- package/dist/esm/components/data/DataTable/DataTable.js.map +1 -0
- package/dist/esm/components/editor/ChatPanel/ChatCodeBlock.js +87 -5
- package/dist/esm/components/editor/ChatPanel/ChatCodeBlock.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatInput.js +87 -5
- package/dist/esm/components/editor/ChatPanel/ChatInput.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatMessage.js +87 -2
- package/dist/esm/components/editor/ChatPanel/ChatMessage.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertyRow.js +87 -3
- package/dist/esm/components/editor/PropertyInspector/PropertyRow.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertySection.js +87 -3
- package/dist/esm/components/editor/PropertyInspector/PropertySection.js.map +1 -1
- package/dist/esm/components/feedback/Alert/Alert.css.js +1 -1
- package/dist/esm/components/feedback/Alert/Alert.js +3 -2
- package/dist/esm/components/feedback/Alert/Alert.js.map +1 -1
- package/dist/esm/components/feedback/CommandPalette/CommandPalette.css.js +20 -0
- package/dist/esm/components/feedback/CommandPalette/CommandPalette.css.js.map +1 -0
- package/dist/esm/components/feedback/CommandPalette/CommandPalette.js +261 -0
- package/dist/esm/components/feedback/CommandPalette/CommandPalette.js.map +1 -0
- package/dist/esm/components/feedback/CommandPalette/fuzzySearch.js +86 -0
- package/dist/esm/components/feedback/CommandPalette/fuzzySearch.js.map +1 -0
- package/dist/esm/components/feedback/CommandPalette/useRecentItems.js +63 -0
- package/dist/esm/components/feedback/CommandPalette/useRecentItems.js.map +1 -0
- package/dist/esm/components/feedback/Dialog/DialogHeader.js +2 -1
- package/dist/esm/components/feedback/Dialog/DialogHeader.js.map +1 -1
- package/dist/esm/components/feedback/Drawer/Drawer.css.js +17 -0
- package/dist/esm/components/feedback/Drawer/Drawer.css.js.map +1 -0
- package/dist/esm/components/feedback/Drawer/Drawer.js +120 -0
- package/dist/esm/components/feedback/Drawer/Drawer.js.map +1 -0
- package/dist/esm/components/feedback/Drawer/useDrawerAnimation.js +74 -0
- package/dist/esm/components/feedback/Drawer/useDrawerAnimation.js.map +1 -0
- package/dist/esm/components/feedback/Skeleton/SkeletonLayout.css.js +18 -0
- package/dist/esm/components/feedback/Skeleton/SkeletonLayout.css.js.map +1 -0
- package/dist/esm/components/feedback/Skeleton/SkeletonLayout.js +95 -0
- package/dist/esm/components/feedback/Skeleton/SkeletonLayout.js.map +1 -0
- package/dist/esm/components/feedback/Stat/Stat.css.js +15 -0
- package/dist/esm/components/feedback/Stat/Stat.css.js.map +1 -0
- package/dist/esm/components/feedback/Stat/Stat.js +55 -0
- package/dist/esm/components/feedback/Stat/Stat.js.map +1 -0
- package/dist/esm/components/feedback/Toast/ToastItem.js +12 -15
- package/dist/esm/components/feedback/Toast/ToastItem.js.map +1 -1
- package/dist/esm/components/layout/Accordion/Accordion.js +2 -1
- package/dist/esm/components/layout/Accordion/Accordion.js.map +1 -1
- package/dist/esm/components/layout/Accordion/AccordionTrigger.js +2 -3
- package/dist/esm/components/layout/Accordion/AccordionTrigger.js.map +1 -1
- package/dist/esm/components/layout/Card/Card.css.js +18 -0
- package/dist/esm/components/layout/Card/Card.css.js.map +1 -0
- package/dist/esm/components/layout/Card/Card.js +66 -0
- package/dist/esm/components/layout/Card/Card.js.map +1 -0
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbEllipsis.js +1 -0
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbEllipsis.js.map +1 -1
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbItem.js +1 -0
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbItem.js.map +1 -1
- package/dist/esm/components/navigation/Breadcrumbs/Breadcrumbs.js +5 -0
- package/dist/esm/components/navigation/Breadcrumbs/Breadcrumbs.js.map +1 -1
- package/dist/esm/components/navigation/Pagination/Pagination.css.js +12 -0
- package/dist/esm/components/navigation/Pagination/Pagination.css.js.map +1 -0
- package/dist/esm/components/navigation/Pagination/Pagination.js +107 -0
- package/dist/esm/components/navigation/Pagination/Pagination.js.map +1 -0
- package/dist/esm/components/navigation/Pagination/usePagination.js +143 -0
- package/dist/esm/components/navigation/Pagination/usePagination.js.map +1 -0
- package/dist/esm/components/primitives/Avatar/Avatar.js +87 -1
- package/dist/esm/components/primitives/Avatar/Avatar.js.map +1 -1
- package/dist/esm/components/primitives/Badge/Badge.js +87 -1
- package/dist/esm/components/primitives/Badge/Badge.js.map +1 -1
- package/dist/esm/components/primitives/Checkbox/Checkbox.js +5 -2
- package/dist/esm/components/primitives/Checkbox/Checkbox.js.map +1 -1
- package/dist/esm/components/primitives/Collapsible/Collapsible.js +2 -3
- package/dist/esm/components/primitives/Collapsible/Collapsible.js.map +1 -1
- package/dist/esm/components/primitives/HoverCard/HoverCard.css.js +7 -0
- package/dist/esm/components/primitives/HoverCard/HoverCard.css.js.map +1 -0
- package/dist/esm/components/primitives/HoverCard/HoverCard.js +169 -0
- package/dist/esm/components/primitives/HoverCard/HoverCard.js.map +1 -0
- package/dist/esm/components/primitives/Icon/Icon.js +16 -2
- package/dist/esm/components/primitives/Icon/Icon.js.map +1 -1
- package/dist/esm/components/primitives/Link/Link.js +3 -3
- package/dist/esm/components/primitives/Link/Link.js.map +1 -1
- package/dist/esm/components/primitives/Popover/PopoverClose.js +2 -3
- package/dist/esm/components/primitives/Popover/PopoverClose.js.map +1 -1
- package/dist/esm/components/primitives/Radio/Radio.js +1 -1
- package/dist/esm/hooks/useBreakpoint/useBreakpoint.js +44 -0
- package/dist/esm/hooks/useBreakpoint/useBreakpoint.js.map +1 -0
- package/dist/esm/hooks/useDebounced/useDebouncedCallback.js +97 -0
- package/dist/esm/hooks/useDebounced/useDebouncedCallback.js.map +1 -0
- package/dist/esm/hooks/useDebounced/useDebouncedValue.js +35 -0
- package/dist/esm/hooks/useDebounced/useDebouncedValue.js.map +1 -0
- package/dist/esm/hooks/useIntersectionObserver/useIntersectionObserver.js +73 -0
- package/dist/esm/hooks/useIntersectionObserver/useIntersectionObserver.js.map +1 -0
- package/dist/esm/hooks/useListboxNav/useListboxNav.js +181 -0
- package/dist/esm/hooks/useListboxNav/useListboxNav.js.map +1 -0
- package/dist/esm/hooks/useMediaQuery/useMediaQuery.js +54 -0
- package/dist/esm/hooks/useMediaQuery/useMediaQuery.js.map +1 -0
- package/dist/esm/hooks/useThrottledCallback/useThrottledCallback.js +78 -0
- package/dist/esm/hooks/useThrottledCallback/useThrottledCallback.js.map +1 -0
- package/dist/esm/index.js +25 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/theme/breakpoints.js +27 -0
- package/dist/esm/theme/breakpoints.js.map +1 -0
- package/dist/esm/theme/index.js +1 -0
- package/dist/esm/theme/index.js.map +1 -1
- package/dist/tokens/tokens.dark.css +1 -1
- package/dist/tokens/tokens.json +1 -1
- package/dist/tokens/tokens.light.css +1 -1
- package/dist/types/components/Icons/CloudUploadIcon.d.ts +27 -0
- package/dist/types/components/Icons/ExternalLinkIcon.d.ts +29 -0
- package/dist/types/components/Icons/FirstIcon.d.ts +26 -0
- package/dist/types/components/Icons/LastIcon.d.ts +26 -0
- package/dist/types/components/Icons/UnlinkIcon.d.ts +29 -0
- package/dist/types/components/controls/Combobox/Combobox.d.ts +29 -0
- package/dist/types/components/controls/Combobox/Combobox.types.d.ts +109 -0
- package/dist/types/components/controls/FileUploader/FileUploader.d.ts +34 -0
- package/dist/types/components/controls/FileUploader/FileUploader.types.d.ts +94 -0
- package/dist/types/components/controls/MultiSelect/MultiSelect.d.ts +31 -0
- package/dist/types/components/controls/MultiSelect/MultiSelect.types.d.ts +85 -0
- package/dist/types/components/controls/TagInput/TagInput.d.ts +24 -0
- package/dist/types/components/controls/TagInput/TagInput.types.d.ts +100 -0
- package/dist/types/components/data/DataTable/DataTable.d.ts +8 -0
- package/dist/types/components/data/DataTable/DataTable.types.d.ts +159 -0
- package/dist/types/components/feedback/Alert/Alert.d.ts +1 -0
- package/dist/types/components/feedback/Alert/Alert.types.d.ts +7 -0
- package/dist/types/components/feedback/CommandPalette/CommandPalette.d.ts +29 -0
- package/dist/types/components/feedback/CommandPalette/CommandPalette.types.d.ts +61 -0
- package/dist/types/components/feedback/CommandPalette/fuzzySearch.d.ts +6 -0
- package/dist/types/components/feedback/Drawer/Drawer.d.ts +12 -0
- package/dist/types/components/feedback/Drawer/Drawer.types.d.ts +70 -0
- package/dist/types/components/feedback/Skeleton/Skeleton.types.d.ts +44 -1
- package/dist/types/components/feedback/Skeleton/SkeletonLayout.d.ts +314 -0
- package/dist/types/components/feedback/Stat/Stat.d.ts +23 -0
- package/dist/types/components/feedback/Stat/Stat.types.d.ts +38 -0
- package/dist/types/components/layout/Accordion/Accordion.types.d.ts +7 -0
- package/dist/types/components/layout/Card/Card.d.ts +12 -0
- package/dist/types/components/layout/Card/Card.types.d.ts +54 -0
- package/dist/types/components/navigation/Pagination/Pagination.d.ts +22 -0
- package/dist/types/components/navigation/Pagination/Pagination.types.d.ts +49 -0
- package/dist/types/components/primitives/Button/Button.d.ts +1 -1
- package/dist/types/components/primitives/HoverCard/HoverCard.d.ts +10 -0
- package/dist/types/components/primitives/HoverCard/HoverCard.types.d.ts +64 -0
- package/dist/types/components/primitives/Icon/Icon.d.ts +14 -1
- package/dist/types/components/primitives/IconButton/IconButton.d.ts +1 -1
- package/dist/types/hooks/useBreakpoint/useBreakpoint.d.ts +19 -0
- package/dist/types/hooks/useBreakpoint/useBreakpoint.types.d.ts +20 -0
- package/dist/types/hooks/useDebounced/useDebounced.types.d.ts +15 -0
- package/dist/types/hooks/useDebounced/useDebouncedCallback.d.ts +22 -0
- package/dist/types/hooks/useDebounced/useDebouncedValue.d.ts +16 -0
- package/dist/types/hooks/useIntersectionObserver/useIntersectionObserver.d.ts +22 -0
- package/dist/types/hooks/useIntersectionObserver/useIntersectionObserver.types.d.ts +22 -0
- package/dist/types/hooks/useListboxNav/useListboxNav.d.ts +75 -0
- package/dist/types/hooks/useMediaQuery/useMediaQuery.d.ts +19 -0
- package/dist/types/hooks/useMediaQuery/useMediaQuery.types.d.ts +6 -0
- package/dist/types/hooks/useThrottledCallback/useThrottledCallback.d.ts +23 -0
- package/dist/types/hooks/useThrottledCallback/useThrottledCallback.types.d.ts +13 -0
- package/dist/types/index.d.ts +43 -1
- package/dist/types/theme/breakpoints.d.ts +22 -0
- package/dist/types/theme/index.d.ts +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
import React, { useId, useRef, useState, useCallback, useMemo, useEffect, createContext, useContext } from 'react';
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
5
|
+
import { useFloating, autoUpdate, offset, flip, shift, useHover, safePolygon, useFocus, useInteractions } from '@floating-ui/react';
|
|
6
|
+
import { cx } from '../../../utils/cx.js';
|
|
7
|
+
import { hoverCardContentRecipe } from './HoverCard.css.js';
|
|
8
|
+
|
|
9
|
+
const HoverCardContext =
|
|
10
|
+
/*#__PURE__*/ createContext(null);
|
|
11
|
+
function useHoverCardContext() {
|
|
12
|
+
const ctx = useContext(HoverCardContext);
|
|
13
|
+
if (!ctx) {
|
|
14
|
+
throw new Error('HoverCard compound components must be used within <HoverCard>');
|
|
15
|
+
}
|
|
16
|
+
return ctx;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A floating content container anchored to a trigger and shown on hover.
|
|
20
|
+
* Cursor can move from the trigger onto the content without closing
|
|
21
|
+
* (safe-polygon based). Also opens on keyboard focus for accessibility.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* <HoverCard>
|
|
26
|
+
* <HoverCard.Trigger>
|
|
27
|
+
* <Link>@octocat</Link>
|
|
28
|
+
* </HoverCard.Trigger>
|
|
29
|
+
* <HoverCard.Content width={280}>
|
|
30
|
+
* <UserPreview user={...} />
|
|
31
|
+
* </HoverCard.Content>
|
|
32
|
+
* </HoverCard>
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
const HoverCardRoot = ({ open: openProp, defaultOpen = false, placement = 'bottom-start', offset: offset$1 = 8, openDelay = 400, closeDelay = 150, portal = true, disableSafePolygon = false, disabled = false, onOpenChange, children, }) => {
|
|
36
|
+
const autoId = useId();
|
|
37
|
+
const triggerRef = useRef(null);
|
|
38
|
+
const contentRef = useRef(null);
|
|
39
|
+
const [internalOpen, setInternalOpen] = useState(defaultOpen);
|
|
40
|
+
const isControlled = openProp !== undefined;
|
|
41
|
+
const isOpen = disabled ? false : isControlled ? openProp : internalOpen;
|
|
42
|
+
const handleOpenChange = useCallback((nextOpen) => {
|
|
43
|
+
if (disabled && nextOpen)
|
|
44
|
+
return;
|
|
45
|
+
if (!isControlled) {
|
|
46
|
+
setInternalOpen(nextOpen);
|
|
47
|
+
}
|
|
48
|
+
onOpenChange?.(nextOpen);
|
|
49
|
+
}, [disabled, isControlled, onOpenChange]);
|
|
50
|
+
const { refs, floatingStyles, context } = useFloating({
|
|
51
|
+
open: isOpen,
|
|
52
|
+
onOpenChange: handleOpenChange,
|
|
53
|
+
placement: placement,
|
|
54
|
+
middleware: [offset(offset$1), flip(), shift({ padding: 8 })],
|
|
55
|
+
whileElementsMounted: autoUpdate,
|
|
56
|
+
});
|
|
57
|
+
const hover = useHover(context, {
|
|
58
|
+
enabled: !disabled,
|
|
59
|
+
delay: { open: openDelay, close: closeDelay },
|
|
60
|
+
handleClose: disableSafePolygon ? null : safePolygon(),
|
|
61
|
+
move: false,
|
|
62
|
+
});
|
|
63
|
+
const focus = useFocus(context, { enabled: !disabled });
|
|
64
|
+
const { getReferenceProps, getFloatingProps } = useInteractions([
|
|
65
|
+
hover,
|
|
66
|
+
focus,
|
|
67
|
+
]);
|
|
68
|
+
const contextValue = useMemo(() => ({
|
|
69
|
+
isOpen,
|
|
70
|
+
hoverCardId: autoId,
|
|
71
|
+
portal,
|
|
72
|
+
triggerRef,
|
|
73
|
+
contentRef,
|
|
74
|
+
floatingRefs: refs,
|
|
75
|
+
floatingStyles,
|
|
76
|
+
floatingContext: context,
|
|
77
|
+
getReferenceProps,
|
|
78
|
+
getFloatingProps,
|
|
79
|
+
}), [
|
|
80
|
+
isOpen,
|
|
81
|
+
autoId,
|
|
82
|
+
portal,
|
|
83
|
+
refs,
|
|
84
|
+
floatingStyles,
|
|
85
|
+
context,
|
|
86
|
+
getReferenceProps,
|
|
87
|
+
getFloatingProps,
|
|
88
|
+
]);
|
|
89
|
+
return (jsx(HoverCardContext.Provider, { value: contextValue, children: children }));
|
|
90
|
+
};
|
|
91
|
+
HoverCardRoot.displayName = 'HoverCard';
|
|
92
|
+
const HoverCardTrigger = ({ children }) => {
|
|
93
|
+
const { isOpen, hoverCardId, triggerRef, floatingRefs, getReferenceProps } = useHoverCardContext();
|
|
94
|
+
const contentId = `hovercard-${hoverCardId}-content`;
|
|
95
|
+
const setRef = useCallback((node) => {
|
|
96
|
+
triggerRef.current = node;
|
|
97
|
+
floatingRefs.setReference(node);
|
|
98
|
+
}, [triggerRef, floatingRefs]);
|
|
99
|
+
if (!React.isValidElement(children))
|
|
100
|
+
return null;
|
|
101
|
+
const referenceProps = getReferenceProps();
|
|
102
|
+
return React.cloneElement(children, {
|
|
103
|
+
ref: setRef,
|
|
104
|
+
...referenceProps,
|
|
105
|
+
'aria-describedby': isOpen ? contentId : undefined,
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
HoverCardTrigger.displayName = 'HoverCard.Trigger';
|
|
109
|
+
const HoverCardContent = ({ children, width, maxHeight, padding = 'md', className, style, testId, ref: externalRef, ...rest }) => {
|
|
110
|
+
const { isOpen, contentRef, portal, hoverCardId, floatingRefs, floatingStyles, getFloatingProps, } = useHoverCardContext();
|
|
111
|
+
const [visible, setVisible] = useState(false);
|
|
112
|
+
const contentId = `hovercard-${hoverCardId}-content`;
|
|
113
|
+
const setRef = useCallback((node) => {
|
|
114
|
+
contentRef.current = node;
|
|
115
|
+
floatingRefs.setFloating(node);
|
|
116
|
+
if (typeof externalRef === 'function') {
|
|
117
|
+
externalRef(node);
|
|
118
|
+
}
|
|
119
|
+
else if (externalRef && typeof externalRef === 'object') {
|
|
120
|
+
externalRef.current =
|
|
121
|
+
node;
|
|
122
|
+
}
|
|
123
|
+
}, [contentRef, floatingRefs, externalRef]);
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (isOpen) {
|
|
126
|
+
const handle = requestAnimationFrame(() => {
|
|
127
|
+
setVisible(true);
|
|
128
|
+
});
|
|
129
|
+
return () => {
|
|
130
|
+
cancelAnimationFrame(handle);
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
setVisible(false);
|
|
134
|
+
return undefined;
|
|
135
|
+
}, [isOpen]);
|
|
136
|
+
if (!isOpen)
|
|
137
|
+
return null;
|
|
138
|
+
const dimensionStyles = {};
|
|
139
|
+
if (width != null) {
|
|
140
|
+
dimensionStyles.width =
|
|
141
|
+
typeof width === 'number' ? `${String(width)}px` : width;
|
|
142
|
+
}
|
|
143
|
+
if (maxHeight != null) {
|
|
144
|
+
dimensionStyles.maxHeight =
|
|
145
|
+
typeof maxHeight === 'number' ? `${String(maxHeight)}px` : maxHeight;
|
|
146
|
+
dimensionStyles.overflowY = 'auto';
|
|
147
|
+
}
|
|
148
|
+
const floatingProps = getFloatingProps();
|
|
149
|
+
const element = (jsx("div", { ref: setRef, role: "tooltip", id: contentId, className: cx(hoverCardContentRecipe({
|
|
150
|
+
padding,
|
|
151
|
+
visible: visible || undefined,
|
|
152
|
+
}), className), style: {
|
|
153
|
+
...floatingStyles,
|
|
154
|
+
...dimensionStyles,
|
|
155
|
+
...style,
|
|
156
|
+
}, "data-testid": testId, ...floatingProps, ...rest, children: children }));
|
|
157
|
+
if (portal && typeof document !== 'undefined') {
|
|
158
|
+
return createPortal(element, document.body);
|
|
159
|
+
}
|
|
160
|
+
return element;
|
|
161
|
+
};
|
|
162
|
+
HoverCardContent.displayName = 'HoverCard.Content';
|
|
163
|
+
const HoverCard = Object.assign(HoverCardRoot, {
|
|
164
|
+
Trigger: HoverCardTrigger,
|
|
165
|
+
Content: HoverCardContent,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
export { HoverCard };
|
|
169
|
+
//# sourceMappingURL=HoverCard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HoverCard.js","sources":["../../../../../../src/components/primitives/HoverCard/HoverCard.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;AAkCA;AACE;AAEF;AACE;;AAEE;;AAIF;AACF;AAEA;;;;;;;;;;;;;;;;AAgBG;AACH;AAaE;AACA;AACA;;AAGA;AACA;AAEA;;;;;;AAMI;;;AAMF;AACA;AACA;AACA;AACA;AACD;AAED;;;;AAIE;AACD;AACD;AAEA;;;AAGC;AAED;;AAGI;;;;AAIA;;AAEA;;;AAGD;;;;;;;;;AAUA;AAGH;AAKF;AAEA;AAEA;AACE;AAEA;AAEA;AAEI;AACA;AACF;AAIF;AAAqC;AAErC;AAEA;AAGI;AACA;;AAED;AAEL;AAEA;AAEA;AAWE;;AAWA;AAEA;AAEI;AACA;AACA;;;AAEO;AACJ;AACC;;;;;AAQJ;;AAEA;AACA;;AAEA;;;AAGF;AACF;AAEA;AAAa;;AAGb;AACE;AACE;;AAEJ;AACE;AACE;AACF;;AAGF;;;;AAWO;AAID;AACA;AACA;;AAUN;;;AAGA;AACF;AAEA;;AAQE;AACA;AACD;;"}
|
|
@@ -16,14 +16,28 @@ const STANDARD_COLORS = new Set([
|
|
|
16
16
|
'warning',
|
|
17
17
|
'error',
|
|
18
18
|
]);
|
|
19
|
+
const STANDARD_SIZES = new Set(['sm', 'md', 'lg']);
|
|
19
20
|
const Icon = /*#__PURE__*/ React.memo(({ children, size = 'md', color = 'primary', className, title, decorative = false, testId, ref, ...props }) => {
|
|
20
21
|
const isStandardColor = STANDARD_COLORS.has(color);
|
|
22
|
+
const isStandardSize = typeof size === 'string' && STANDARD_SIZES.has(size);
|
|
23
|
+
const resolvedSize = isStandardSize || size === undefined
|
|
24
|
+
? undefined
|
|
25
|
+
: typeof size === 'number'
|
|
26
|
+
? `${size}px`
|
|
27
|
+
: size;
|
|
28
|
+
const inlineStyle = {};
|
|
29
|
+
if (!isStandardColor)
|
|
30
|
+
inlineStyle.color = color;
|
|
31
|
+
if (resolvedSize !== undefined) {
|
|
32
|
+
inlineStyle.width = resolvedSize;
|
|
33
|
+
inlineStyle.height = resolvedSize;
|
|
34
|
+
}
|
|
21
35
|
return (jsxs("svg", { ref: ref, className: cx(iconRecipe({
|
|
22
|
-
size,
|
|
36
|
+
size: isStandardSize ? size : undefined,
|
|
23
37
|
color: isStandardColor
|
|
24
38
|
? color
|
|
25
39
|
: undefined,
|
|
26
|
-
}), className), style:
|
|
40
|
+
}), className), style: Object.keys(inlineStyle).length > 0 ? inlineStyle : undefined, viewBox: "0 0 24 24", fill: "none", "aria-hidden": decorative, role: decorative ? 'presentation' : 'img', "data-testid": testId, ...props, children: [title && !decorative && jsx("title", { children: title }), children] }));
|
|
27
41
|
});
|
|
28
42
|
Icon.displayName = 'Icon';
|
|
29
43
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Icon.js","sources":["../../../../../../src/components/primitives/Icon/Icon.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"Icon.js","sources":["../../../../../../src/components/primitives/Icon/Icon.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;AA6CA;;AAEG;AACH;;;;;;;;AAQC;AAED;AAEO;;AAaH;AAEA;AAEI;AACA;;;;AAKJ;AAAsB;AACtB;AACE;AACA;;;;AASM;AACE;AAQA;AACH;AAeT;AAGF;;"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
|
4
|
+
import { ExternalLinkIcon } from '../../Icons/ExternalLinkIcon.js';
|
|
4
5
|
import { vars } from '../../../theme/contract.css.js';
|
|
5
6
|
import { cx } from '../../../utils/cx.js';
|
|
6
|
-
import { linkHoverColorVar, linkColorVar, linkRecipe,
|
|
7
|
+
import { linkHoverColorVar, linkColorVar, linkRecipe, externalIconStyle, srOnlyStyle } from './Link.css.js';
|
|
7
8
|
|
|
8
9
|
const EXTERNAL_PROTOCOL_RE = /^https?:\/\//i;
|
|
9
10
|
/**
|
|
@@ -78,7 +79,6 @@ function stripNavProps(props) {
|
|
|
78
79
|
}
|
|
79
80
|
return cleaned;
|
|
80
81
|
}
|
|
81
|
-
const ExternalIcon = () => (jsxs("svg", { className: externalIconStyle, viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", focusable: "false", children: [jsx("path", { d: "M4 2h6v6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }), jsx("path", { d: "M10 2L5 7", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }), jsx("path", { d: "M9 7v3H2V3h3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })] }));
|
|
82
82
|
/**
|
|
83
83
|
* Styled anchor primitive.
|
|
84
84
|
*
|
|
@@ -151,7 +151,7 @@ function LinkImpl(props) {
|
|
|
151
151
|
size: resolvedSize,
|
|
152
152
|
disabled: isDisabled || undefined,
|
|
153
153
|
}), className);
|
|
154
|
-
return (jsxs(Component, { ...cleanedRest, ...hrefProp, ...externalAttrs, ...ariaLabelProp, ref: ref, className: linkClassName, style: { ...inlineVars, ...style }, "data-testid": testId, "data-disabled": isDisabled || undefined, "aria-disabled": isDisabled || undefined, children: [children, isExternal && (jsxs(Fragment, { children: [jsx(
|
|
154
|
+
return (jsxs(Component, { ...cleanedRest, ...hrefProp, ...externalAttrs, ...ariaLabelProp, ref: ref, className: linkClassName, style: { ...inlineVars, ...style }, "data-testid": testId, "data-disabled": isDisabled || undefined, "aria-disabled": isDisabled || undefined, children: [children, isExternal && (jsxs(Fragment, { children: [jsx(ExternalLinkIcon, { className: externalIconStyle, size: "0.75em", decorative: true }), !ariaLabel && (jsx("span", { className: srOnlyStyle, children: " (opens in new tab)" }))] }))] }));
|
|
155
155
|
}
|
|
156
156
|
const Link = LinkImpl;
|
|
157
157
|
Link.displayName = 'Link';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Link.js","sources":["../../../../../../src/components/primitives/Link/Link.tsx"],"sourcesContent":[null],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Link.js","sources":["../../../../../../src/components/primitives/Link/Link.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;AAgBA;AAEA;;;AAGG;AACH;AACE;AACA;AACA;;AAGF;AACE;AACA;AACA;;AAGF;AACE;AACE;AACA;AACD;AACD;AACE;AACA;AACD;AACD;AACE;AACA;AACD;;AAGH;AACE;AACA;AAAW;;AAEb;AAEA;;;;;;;AAOG;AACH;;;;;;;;;;;;;;;;;;;;AAqBA;;AAIE;AACE;;;AAGA;;AAEF;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AACH;AAGE;AAkBA;AACA;AACA;;;;;;;;AAWE;;;;;AAMD;;;;;AAMD;;AAGE;;;;;;;;;;AAcA;AACI;;AAEE;AACH;;AAGL;AAEI;AACA;AACA;;;AAMJ;AA4BF;AAiBO;AACP;;"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { useCallback } from 'react';
|
|
4
|
+
import { CloseIcon } from '../../Icons/CloseIcon.js';
|
|
4
5
|
import { usePopoverContext } from './Popover.js';
|
|
5
6
|
import { cx } from '../../../utils/cx.js';
|
|
6
7
|
import { closeButtonStyle } from './Popover.css.js';
|
|
7
8
|
|
|
8
|
-
// --- Close icon ---
|
|
9
|
-
const CloseIcon = () => (jsx("svg", { width: 10, height: 10, viewBox: "0 0 10 10", fill: "none", "aria-hidden": "true", children: jsx("path", { d: "M1.5 1.5L8.5 8.5M8.5 1.5L1.5 8.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }));
|
|
10
9
|
// --- Component ---
|
|
11
10
|
const PopoverClose = ({ children, className, style, testId, ref, ...rest }) => {
|
|
12
11
|
const { close } = usePopoverContext();
|
|
@@ -14,7 +13,7 @@ const PopoverClose = ({ children, className, style, testId, ref, ...rest }) => {
|
|
|
14
13
|
e.stopPropagation();
|
|
15
14
|
close();
|
|
16
15
|
}, [close]);
|
|
17
|
-
return (jsx("button", { ref: ref, type: "button", "aria-label": "Close", onClick: handleClick, className: cx(closeButtonStyle, className), style: style, "data-testid": testId, ...rest, children: children ?? jsx(CloseIcon, {}) }));
|
|
16
|
+
return (jsx("button", { ref: ref, type: "button", "aria-label": "Close", onClick: handleClick, className: cx(closeButtonStyle, className), style: style, "data-testid": testId, ...rest, children: children ?? jsx(CloseIcon, { size: "sm", decorative: true }) }));
|
|
18
17
|
};
|
|
19
18
|
PopoverClose.displayName = 'PopoverClose';
|
|
20
19
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PopoverClose.js","sources":["../../../../../../src/components/primitives/Popover/PopoverClose.tsx"],"sourcesContent":[null],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PopoverClose.js","sources":["../../../../../../src/components/primitives/Popover/PopoverClose.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;AASA;;AAUE;AAEA;;AAGI;AACF;;AAkBJ;AAEA;;"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { breakpoints, BREAKPOINT_ORDER } from '../../theme/breakpoints.js';
|
|
4
|
+
import { useMediaQuery } from '../useMediaQuery/useMediaQuery.js';
|
|
5
|
+
|
|
6
|
+
const SM_QUERY = `(min-width: ${breakpoints.sm}px)`;
|
|
7
|
+
const MD_QUERY = `(min-width: ${breakpoints.md}px)`;
|
|
8
|
+
const LG_QUERY = `(min-width: ${breakpoints.lg}px)`;
|
|
9
|
+
const XL_QUERY = `(min-width: ${breakpoints.xl}px)`;
|
|
10
|
+
function resolveCurrent(map) {
|
|
11
|
+
for (let i = BREAKPOINT_ORDER.length - 1; i >= 0; i -= 1) {
|
|
12
|
+
const name = BREAKPOINT_ORDER[i];
|
|
13
|
+
if (name && map[name])
|
|
14
|
+
return name;
|
|
15
|
+
}
|
|
16
|
+
return 'xs';
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Reactive named-breakpoint matcher.
|
|
20
|
+
*
|
|
21
|
+
* Returns booleans for each named breakpoint plus the largest matching
|
|
22
|
+
* `current`. Built on top of `useMediaQuery`. Breakpoint values come from
|
|
23
|
+
* `@/theme/breakpoints` and match the values used by `Stack` and other
|
|
24
|
+
* responsive layout components.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* const { md, current } = useBreakpoint();
|
|
29
|
+
* return md ? <Sidebar /> : <BottomSheet />;
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function useBreakpoint() {
|
|
33
|
+
const sm = useMediaQuery(SM_QUERY);
|
|
34
|
+
const md = useMediaQuery(MD_QUERY);
|
|
35
|
+
const lg = useMediaQuery(LG_QUERY);
|
|
36
|
+
const xl = useMediaQuery(XL_QUERY);
|
|
37
|
+
return useMemo(() => {
|
|
38
|
+
const map = { xs: true, sm, md, lg, xl };
|
|
39
|
+
return { ...map, current: resolveCurrent(map) };
|
|
40
|
+
}, [sm, md, lg, xl]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { useBreakpoint };
|
|
44
|
+
//# sourceMappingURL=useBreakpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBreakpoint.js","sources":["../../../../../src/hooks/useBreakpoint/useBreakpoint.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;;AAWA;AACA;AACA;AACA;AAEA;AACE;AACE;AACA;AAAuB;;AAEzB;AACF;AAEA;;;;;;;;;;;;;AAaG;;AAED;AACA;AACA;AACA;;AAGE;;;AAGJ;;"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useRef, useCallback, useMemo, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Wrap a function so rapid calls collapse into one. By default the call
|
|
6
|
+
* fires on the trailing edge — pass `leading: true` to also fire
|
|
7
|
+
* immediately. `maxWait` forces a call even when calls keep coming.
|
|
8
|
+
*
|
|
9
|
+
* The wrapper identity is stable; passing fresh inline functions across
|
|
10
|
+
* renders is safe and does not reset pending timers.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* const onResize = useDebouncedCallback(measure, 150);
|
|
15
|
+
* useEffect(() => {
|
|
16
|
+
* window.addEventListener('resize', onResize);
|
|
17
|
+
* return () => window.removeEventListener('resize', onResize);
|
|
18
|
+
* }, [onResize]);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
function useDebouncedCallback(fn, delay, options = {}) {
|
|
22
|
+
const { leading = false, maxWait } = options;
|
|
23
|
+
const fnRef = useRef(fn);
|
|
24
|
+
fnRef.current = fn;
|
|
25
|
+
const timerRef = useRef(null);
|
|
26
|
+
const maxTimerRef = useRef(null);
|
|
27
|
+
const lastArgsRef = useRef(null);
|
|
28
|
+
const lastInvokeRef = useRef(0);
|
|
29
|
+
const clearTimers = useCallback(() => {
|
|
30
|
+
if (timerRef.current !== null) {
|
|
31
|
+
clearTimeout(timerRef.current);
|
|
32
|
+
timerRef.current = null;
|
|
33
|
+
}
|
|
34
|
+
if (maxTimerRef.current !== null) {
|
|
35
|
+
clearTimeout(maxTimerRef.current);
|
|
36
|
+
maxTimerRef.current = null;
|
|
37
|
+
}
|
|
38
|
+
}, []);
|
|
39
|
+
const invoke = useCallback(() => {
|
|
40
|
+
const args = lastArgsRef.current;
|
|
41
|
+
lastArgsRef.current = null;
|
|
42
|
+
lastInvokeRef.current = Date.now();
|
|
43
|
+
clearTimers();
|
|
44
|
+
if (args)
|
|
45
|
+
fnRef.current(...args);
|
|
46
|
+
}, [clearTimers]);
|
|
47
|
+
const debounced = useMemo(() => {
|
|
48
|
+
const wrapper = (...args) => {
|
|
49
|
+
lastArgsRef.current = args;
|
|
50
|
+
const now = Date.now();
|
|
51
|
+
const isLeadingCall = leading && timerRef.current === null && maxTimerRef.current === null;
|
|
52
|
+
if (isLeadingCall) {
|
|
53
|
+
// Fire immediately, then suppress trailing for `delay` ms unless
|
|
54
|
+
// additional calls happen — in that case the trailing handler will
|
|
55
|
+
// pick them up.
|
|
56
|
+
lastArgsRef.current = null;
|
|
57
|
+
lastInvokeRef.current = now;
|
|
58
|
+
fnRef.current(...args);
|
|
59
|
+
}
|
|
60
|
+
if (timerRef.current !== null)
|
|
61
|
+
clearTimeout(timerRef.current);
|
|
62
|
+
timerRef.current = setTimeout(() => {
|
|
63
|
+
timerRef.current = null;
|
|
64
|
+
if (lastArgsRef.current !== null)
|
|
65
|
+
invoke();
|
|
66
|
+
else if (maxTimerRef.current !== null) {
|
|
67
|
+
clearTimeout(maxTimerRef.current);
|
|
68
|
+
maxTimerRef.current = null;
|
|
69
|
+
}
|
|
70
|
+
}, delay);
|
|
71
|
+
if (maxWait !== undefined && maxTimerRef.current === null) {
|
|
72
|
+
maxTimerRef.current = setTimeout(() => {
|
|
73
|
+
maxTimerRef.current = null;
|
|
74
|
+
if (lastArgsRef.current !== null)
|
|
75
|
+
invoke();
|
|
76
|
+
}, maxWait);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
wrapper.cancel = () => {
|
|
80
|
+
lastArgsRef.current = null;
|
|
81
|
+
clearTimers();
|
|
82
|
+
};
|
|
83
|
+
wrapper.flush = () => {
|
|
84
|
+
if (lastArgsRef.current !== null)
|
|
85
|
+
invoke();
|
|
86
|
+
else
|
|
87
|
+
clearTimers();
|
|
88
|
+
};
|
|
89
|
+
return wrapper;
|
|
90
|
+
}, [delay, leading, maxWait, invoke, clearTimers]);
|
|
91
|
+
// Cleanup on unmount.
|
|
92
|
+
useEffect(() => clearTimers, [clearTimers]);
|
|
93
|
+
return debounced;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { useDebouncedCallback };
|
|
97
|
+
//# sourceMappingURL=useDebouncedCallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDebouncedCallback.js","sources":["../../../../../src/hooks/useDebounced/useDebouncedCallback.ts"],"sourcesContent":[null],"names":[],"mappings":";;;AAQA;;;;;;;;;;;;;;;;AAgBG;AACG;;AAOJ;AACA;AAEA;AACA;AACA;AACA;AAEA;AACE;AACE;AACA;;AAEF;AACE;AACA;;;AAIJ;AACE;AACA;AACA;AACA;AACA;AAAU;AACZ;AAEA;AACE;AACE;AACA;AACA;;;;;AAOE;AACA;AACA;;AAGF;AAA+B;AAC/B;AACE;AACA;AAAkC;AAC7B;AACH;AACA;;;;AAKF;AACE;AACA;AAAkC;;;AAGxC;AAEA;AACE;AACA;AACF;AACA;AACE;AAAkC;;AAC7B;AACP;AAEA;AACF;;;AAKA;AACF;;"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns a copy of `value` that updates only after `delay` ms have elapsed
|
|
6
|
+
* without further changes (trailing-edge debounce).
|
|
7
|
+
*
|
|
8
|
+
* Typical use: feeding a search input value into an expensive query.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const [text, setText] = useState('');
|
|
13
|
+
* const debounced = useDebouncedValue(text, 200);
|
|
14
|
+
* useEffect(() => { search(debounced); }, [debounced]);
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
function useDebouncedValue(value, delay) {
|
|
18
|
+
const [debounced, setDebounced] = useState(value);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (delay <= 0) {
|
|
21
|
+
setDebounced(value);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const id = setTimeout(() => {
|
|
25
|
+
setDebounced(value);
|
|
26
|
+
}, delay);
|
|
27
|
+
return () => {
|
|
28
|
+
clearTimeout(id);
|
|
29
|
+
};
|
|
30
|
+
}, [value, delay]);
|
|
31
|
+
return debounced;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { useDebouncedValue };
|
|
35
|
+
//# sourceMappingURL=useDebouncedValue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDebouncedValue.js","sources":["../../../../../src/hooks/useDebounced/useDebouncedValue.ts"],"sourcesContent":[null],"names":[],"mappings":";;;AAIA;;;;;;;;;;;;AAYG;AACG;;;AAIF;;;;AAIA;;;AAGA;;AAEA;AACF;AAEA;AACF;;"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Observe whether an element intersects a root (defaults to the viewport).
|
|
6
|
+
* Returns a ref callback to attach to the observed element plus the latest
|
|
7
|
+
* `IntersectionObserverEntry`. SSR-safe: returns a no-op ref and `null`
|
|
8
|
+
* entry when `IntersectionObserver` is unavailable.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const { ref, isIntersecting } = useIntersectionObserver<HTMLDivElement>({
|
|
13
|
+
* rootMargin: '200px',
|
|
14
|
+
* });
|
|
15
|
+
* useEffect(() => {
|
|
16
|
+
* if (isIntersecting) loadMore();
|
|
17
|
+
* }, [isIntersecting]);
|
|
18
|
+
* return <div ref={ref} />;
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
function useIntersectionObserver(options = {}) {
|
|
22
|
+
const { root, rootMargin, threshold, enabled = true } = options;
|
|
23
|
+
const [entry, setEntry] = useState(null);
|
|
24
|
+
const observerRef = useRef(null);
|
|
25
|
+
const nodeRef = useRef(null);
|
|
26
|
+
const ref = useCallback(node => {
|
|
27
|
+
if (typeof IntersectionObserver === 'undefined') {
|
|
28
|
+
nodeRef.current = node;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (observerRef.current) {
|
|
32
|
+
observerRef.current.disconnect();
|
|
33
|
+
observerRef.current = null;
|
|
34
|
+
}
|
|
35
|
+
nodeRef.current = node;
|
|
36
|
+
if (!enabled || !node) {
|
|
37
|
+
setEntry(null);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const observer = new IntersectionObserver(entries => {
|
|
41
|
+
const next = entries[0];
|
|
42
|
+
if (next)
|
|
43
|
+
setEntry(next);
|
|
44
|
+
}, { root: root ?? null, rootMargin, threshold });
|
|
45
|
+
observer.observe(node);
|
|
46
|
+
observerRef.current = observer;
|
|
47
|
+
}, [root, rootMargin, threshold, enabled]);
|
|
48
|
+
// When toggled off via `enabled`, disconnect proactively so consumers don't
|
|
49
|
+
// have to detach the ref to stop observing.
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (enabled)
|
|
52
|
+
return;
|
|
53
|
+
if (observerRef.current) {
|
|
54
|
+
observerRef.current.disconnect();
|
|
55
|
+
observerRef.current = null;
|
|
56
|
+
}
|
|
57
|
+
setEntry(null);
|
|
58
|
+
}, [enabled]);
|
|
59
|
+
// Final cleanup on unmount.
|
|
60
|
+
useEffect(() => () => {
|
|
61
|
+
observerRef.current?.disconnect();
|
|
62
|
+
observerRef.current = null;
|
|
63
|
+
nodeRef.current = null;
|
|
64
|
+
}, []);
|
|
65
|
+
return {
|
|
66
|
+
ref,
|
|
67
|
+
entry,
|
|
68
|
+
isIntersecting: entry?.isIntersecting ?? false,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { useIntersectionObserver };
|
|
73
|
+
//# sourceMappingURL=useIntersectionObserver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIntersectionObserver.js","sources":["../../../../../src/hooks/useIntersectionObserver/useIntersectionObserver.ts"],"sourcesContent":[null],"names":[],"mappings":";;;AASA;;;;;;;;;;;;;;;;AAgBG;AACG;AAGJ;;AAGA;AACA;AAEA;AAEI;AACE;;;AAIF;AACE;AACA;;AAGF;AACA;;;;AAKA;AAEI;AACA;;AACF;AAGF;AACA;;;;;AAQF;;AACA;AACE;AACA;;;AAGJ;;AAGA;AAEI;AACA;AACA;;;;;AAQF;;AAEJ;;"}
|