@trackunit/react-components 1.17.48 → 1.17.49
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/index.cjs.js +82 -0
- package/index.esm.js +82 -1
- package/package.json +5 -5
- package/src/hooks/useGeolocation.d.ts +51 -0
- package/src/index.d.ts +1 -0
package/index.cjs.js
CHANGED
|
@@ -8672,6 +8672,87 @@ const useElevatedState = (initialState, customState) => {
|
|
|
8672
8672
|
return react.useMemo(() => customState ?? [fallbackValue, fallbackSetter], [customState, fallbackValue, fallbackSetter]);
|
|
8673
8673
|
};
|
|
8674
8674
|
|
|
8675
|
+
// ============================================================================
|
|
8676
|
+
// Permission Helper
|
|
8677
|
+
// ============================================================================
|
|
8678
|
+
/**
|
|
8679
|
+
* Query the current geolocation permission state without triggering a prompt.
|
|
8680
|
+
* Returns `null` when the Permissions API is unavailable or the browser throws.
|
|
8681
|
+
*/
|
|
8682
|
+
const queryGeolocationPermission = async () => {
|
|
8683
|
+
try {
|
|
8684
|
+
const status = await navigator.permissions.query({ name: "geolocation" });
|
|
8685
|
+
return status.state;
|
|
8686
|
+
}
|
|
8687
|
+
catch {
|
|
8688
|
+
return null;
|
|
8689
|
+
}
|
|
8690
|
+
};
|
|
8691
|
+
// ============================================================================
|
|
8692
|
+
// Position Helper
|
|
8693
|
+
// ============================================================================
|
|
8694
|
+
/** Wrap `getCurrentPosition` in a Promise that resolves to `GeoJsonPosition | null`. */
|
|
8695
|
+
const getCurrentPosition = () => new Promise(resolve => {
|
|
8696
|
+
if (typeof navigator === "undefined") {
|
|
8697
|
+
resolve(null);
|
|
8698
|
+
return;
|
|
8699
|
+
}
|
|
8700
|
+
navigator.geolocation.getCurrentPosition(pos => resolve([pos.coords.longitude, pos.coords.latitude]), () => resolve(null));
|
|
8701
|
+
});
|
|
8702
|
+
/**
|
|
8703
|
+
* Lightweight geolocation hook.
|
|
8704
|
+
*
|
|
8705
|
+
* By default does nothing on mount — the consumer decides when (and how) to
|
|
8706
|
+
* request a position via `getPosition`. Set `requestOnMount: true` to
|
|
8707
|
+
* automatically populate `position` when permission is already granted.
|
|
8708
|
+
*
|
|
8709
|
+
* @example
|
|
8710
|
+
* ```ts
|
|
8711
|
+
* // Auto-populate position if permission is already granted
|
|
8712
|
+
* const { position, getPosition } = useGeolocation({ requestOnMount: true });
|
|
8713
|
+
*
|
|
8714
|
+
* // User-initiated request (e.g. "My Location" button)
|
|
8715
|
+
* onClick: () => {
|
|
8716
|
+
* void getPosition().then(pos => { if (pos) fitBounds(pos); });
|
|
8717
|
+
* }
|
|
8718
|
+
* ```
|
|
8719
|
+
*/
|
|
8720
|
+
const useGeolocation = (options) => {
|
|
8721
|
+
const [position, setPosition] = react.useState(null);
|
|
8722
|
+
const getPosition = react.useCallback(async (opts) => {
|
|
8723
|
+
const prompt = opts?.prompt !== false;
|
|
8724
|
+
if (!prompt) {
|
|
8725
|
+
const permissionState = await queryGeolocationPermission();
|
|
8726
|
+
if (permissionState !== "granted")
|
|
8727
|
+
return null;
|
|
8728
|
+
}
|
|
8729
|
+
const pos = await getCurrentPosition();
|
|
8730
|
+
if (pos !== null) {
|
|
8731
|
+
setPosition(pos);
|
|
8732
|
+
}
|
|
8733
|
+
return pos;
|
|
8734
|
+
}, []);
|
|
8735
|
+
useWatch({
|
|
8736
|
+
value: options?.requestOnMount ?? false,
|
|
8737
|
+
onChange: () => {
|
|
8738
|
+
void queryGeolocationPermission()
|
|
8739
|
+
.then(state => {
|
|
8740
|
+
if (state !== "granted")
|
|
8741
|
+
return;
|
|
8742
|
+
return getCurrentPosition();
|
|
8743
|
+
})
|
|
8744
|
+
.then(pos => {
|
|
8745
|
+
if (pos !== null && pos !== undefined) {
|
|
8746
|
+
setPosition(pos);
|
|
8747
|
+
}
|
|
8748
|
+
});
|
|
8749
|
+
},
|
|
8750
|
+
immediate: true,
|
|
8751
|
+
skip: options?.requestOnMount !== true,
|
|
8752
|
+
});
|
|
8753
|
+
return react.useMemo(() => ({ position, getPosition }), [position, getPosition]);
|
|
8754
|
+
};
|
|
8755
|
+
|
|
8675
8756
|
/**
|
|
8676
8757
|
* The useHover hook returns a onMouseEnter, onMouseLeave and a boolean indicating whether the element is being hovered.
|
|
8677
8758
|
* The boolean will be true if the element is being hovered, and false if it is not.
|
|
@@ -9653,6 +9734,7 @@ exports.useDebounce = useDebounce;
|
|
|
9653
9734
|
exports.useDevicePixelRatio = useDevicePixelRatio;
|
|
9654
9735
|
exports.useElevatedReducer = useElevatedReducer;
|
|
9655
9736
|
exports.useElevatedState = useElevatedState;
|
|
9737
|
+
exports.useGeolocation = useGeolocation;
|
|
9656
9738
|
exports.useGridAreas = useGridAreas;
|
|
9657
9739
|
exports.useHover = useHover;
|
|
9658
9740
|
exports.useInfiniteScroll = useInfiniteScroll;
|
package/index.esm.js
CHANGED
|
@@ -8670,6 +8670,87 @@ const useElevatedState = (initialState, customState) => {
|
|
|
8670
8670
|
return useMemo(() => customState ?? [fallbackValue, fallbackSetter], [customState, fallbackValue, fallbackSetter]);
|
|
8671
8671
|
};
|
|
8672
8672
|
|
|
8673
|
+
// ============================================================================
|
|
8674
|
+
// Permission Helper
|
|
8675
|
+
// ============================================================================
|
|
8676
|
+
/**
|
|
8677
|
+
* Query the current geolocation permission state without triggering a prompt.
|
|
8678
|
+
* Returns `null` when the Permissions API is unavailable or the browser throws.
|
|
8679
|
+
*/
|
|
8680
|
+
const queryGeolocationPermission = async () => {
|
|
8681
|
+
try {
|
|
8682
|
+
const status = await navigator.permissions.query({ name: "geolocation" });
|
|
8683
|
+
return status.state;
|
|
8684
|
+
}
|
|
8685
|
+
catch {
|
|
8686
|
+
return null;
|
|
8687
|
+
}
|
|
8688
|
+
};
|
|
8689
|
+
// ============================================================================
|
|
8690
|
+
// Position Helper
|
|
8691
|
+
// ============================================================================
|
|
8692
|
+
/** Wrap `getCurrentPosition` in a Promise that resolves to `GeoJsonPosition | null`. */
|
|
8693
|
+
const getCurrentPosition = () => new Promise(resolve => {
|
|
8694
|
+
if (typeof navigator === "undefined") {
|
|
8695
|
+
resolve(null);
|
|
8696
|
+
return;
|
|
8697
|
+
}
|
|
8698
|
+
navigator.geolocation.getCurrentPosition(pos => resolve([pos.coords.longitude, pos.coords.latitude]), () => resolve(null));
|
|
8699
|
+
});
|
|
8700
|
+
/**
|
|
8701
|
+
* Lightweight geolocation hook.
|
|
8702
|
+
*
|
|
8703
|
+
* By default does nothing on mount — the consumer decides when (and how) to
|
|
8704
|
+
* request a position via `getPosition`. Set `requestOnMount: true` to
|
|
8705
|
+
* automatically populate `position` when permission is already granted.
|
|
8706
|
+
*
|
|
8707
|
+
* @example
|
|
8708
|
+
* ```ts
|
|
8709
|
+
* // Auto-populate position if permission is already granted
|
|
8710
|
+
* const { position, getPosition } = useGeolocation({ requestOnMount: true });
|
|
8711
|
+
*
|
|
8712
|
+
* // User-initiated request (e.g. "My Location" button)
|
|
8713
|
+
* onClick: () => {
|
|
8714
|
+
* void getPosition().then(pos => { if (pos) fitBounds(pos); });
|
|
8715
|
+
* }
|
|
8716
|
+
* ```
|
|
8717
|
+
*/
|
|
8718
|
+
const useGeolocation = (options) => {
|
|
8719
|
+
const [position, setPosition] = useState(null);
|
|
8720
|
+
const getPosition = useCallback(async (opts) => {
|
|
8721
|
+
const prompt = opts?.prompt !== false;
|
|
8722
|
+
if (!prompt) {
|
|
8723
|
+
const permissionState = await queryGeolocationPermission();
|
|
8724
|
+
if (permissionState !== "granted")
|
|
8725
|
+
return null;
|
|
8726
|
+
}
|
|
8727
|
+
const pos = await getCurrentPosition();
|
|
8728
|
+
if (pos !== null) {
|
|
8729
|
+
setPosition(pos);
|
|
8730
|
+
}
|
|
8731
|
+
return pos;
|
|
8732
|
+
}, []);
|
|
8733
|
+
useWatch({
|
|
8734
|
+
value: options?.requestOnMount ?? false,
|
|
8735
|
+
onChange: () => {
|
|
8736
|
+
void queryGeolocationPermission()
|
|
8737
|
+
.then(state => {
|
|
8738
|
+
if (state !== "granted")
|
|
8739
|
+
return;
|
|
8740
|
+
return getCurrentPosition();
|
|
8741
|
+
})
|
|
8742
|
+
.then(pos => {
|
|
8743
|
+
if (pos !== null && pos !== undefined) {
|
|
8744
|
+
setPosition(pos);
|
|
8745
|
+
}
|
|
8746
|
+
});
|
|
8747
|
+
},
|
|
8748
|
+
immediate: true,
|
|
8749
|
+
skip: options?.requestOnMount !== true,
|
|
8750
|
+
});
|
|
8751
|
+
return useMemo(() => ({ position, getPosition }), [position, getPosition]);
|
|
8752
|
+
};
|
|
8753
|
+
|
|
8673
8754
|
/**
|
|
8674
8755
|
* The useHover hook returns a onMouseEnter, onMouseLeave and a boolean indicating whether the element is being hovered.
|
|
8675
8756
|
* The boolean will be true if the element is being hovered, and false if it is not.
|
|
@@ -9514,4 +9595,4 @@ const useWindowActivity = ({ onFocus, onBlur, skip = false } = { onBlur: undefin
|
|
|
9514
9595
|
return useMemo(() => ({ focused }), [focused]);
|
|
9515
9596
|
};
|
|
9516
9597
|
|
|
9517
|
-
export { ActionRenderer, Alert, Badge, Breadcrumb, BreadcrumbContainer, Button, Card, CardBody, CardFooter, CardHeader, Collapse, CompletionStatusIndicator, CopyableText, DEFAULT_SKELETON_PREFERENCE_CARD_PROPS, DetailsList, EmptyState, EmptyValue, ExternalLink, GridAreas, Heading, Highlight, HorizontalOverflowScroller, Icon, IconButton, Indicator, KPI, KPICard, KPICardSkeleton, KPISkeleton, List, ListItem, MenuDivider, MenuItem, MenuList, MoreMenu, Notice, PackageNameStoryComponent, Page, PageContent, PageHeader, PageHeaderKpiMetrics, PageHeaderSecondaryActions, PageHeaderTitle, Pagination, Polygon, Popover, PopoverContent, PopoverTitle, PopoverTrigger, Portal, PreferenceCard, PreferenceCardSkeleton, Prompt, ROLE_CARD, SectionHeader, SegmentedValueBar, Sidebar, SkeletonBlock, SkeletonLabel, SkeletonLines, Spacer, Spinner, StarButton, Tab, TabContent, TabList, Tabs, Tag, Text, ToggleGroup, Tooltip, TrendIndicator, TrendIndicators, ValueBar, ZStack, createGrid, cvaButton, cvaButtonPrefixSuffix, cvaButtonSpinner, cvaButtonSpinnerContainer, cvaClickable, cvaContainerStyles, cvaContentContainer, cvaContentWrapper, cvaDescriptionCard, cvaIconBackground, cvaIconButton, cvaImgStyles, cvaIndicator, cvaIndicatorIcon, cvaIndicatorIconBackground, cvaIndicatorLabel, cvaIndicatorPing, cvaInputContainer, cvaInteractableItem, cvaList, cvaListContainer, cvaListItem$1 as cvaListItem, cvaMenu, cvaMenuItem, cvaMenuItemLabel, cvaMenuItemPrefix, cvaMenuItemStyle, cvaMenuItemSuffix, cvaMenuList, cvaMenuListDivider, cvaMenuListItem, cvaMenuListMultiSelect, cvaPageHeader, cvaPageHeaderContainer, cvaPageHeaderHeading, cvaPreferenceCard, cvaTitleCard, cvaToggleGroup, cvaToggleGroupWithSlidingBackground, cvaToggleItem, cvaToggleItemContent, cvaToggleItemText, cvaZStackContainer, cvaZStackItem, defaultPageSize, docs, getDevicePixelRatio, getValueBarColorByValue, iconColorNames, iconPalette, noPagination, preferenceCardGrid, useClickOutside, useContainerBreakpoints, useContinuousTimeout, useCopyToClipboard, useCustomEncoding, useDebounce, useDevicePixelRatio, useElevatedReducer, useElevatedState, useGridAreas, useHover, useInfiniteScroll, useIsFirstRender, useIsFullscreen, useIsTextTruncated, useKeyboardShortcut, useList, useListItemHeight, useLocalStorage, useLocalStorageReducer, useMeasure, useMergeRefs, useModifierKey, useOverflowItems, usePopoverContext, usePrevious, usePrompt, useRandomCSSLengths, useRelayPagination, useResize, useScrollBlock, useScrollDetection, useSelfUpdatingRef, useTextSearch, useTimeout, useViewportBreakpoints, useWatch, useWindowActivity };
|
|
9598
|
+
export { ActionRenderer, Alert, Badge, Breadcrumb, BreadcrumbContainer, Button, Card, CardBody, CardFooter, CardHeader, Collapse, CompletionStatusIndicator, CopyableText, DEFAULT_SKELETON_PREFERENCE_CARD_PROPS, DetailsList, EmptyState, EmptyValue, ExternalLink, GridAreas, Heading, Highlight, HorizontalOverflowScroller, Icon, IconButton, Indicator, KPI, KPICard, KPICardSkeleton, KPISkeleton, List, ListItem, MenuDivider, MenuItem, MenuList, MoreMenu, Notice, PackageNameStoryComponent, Page, PageContent, PageHeader, PageHeaderKpiMetrics, PageHeaderSecondaryActions, PageHeaderTitle, Pagination, Polygon, Popover, PopoverContent, PopoverTitle, PopoverTrigger, Portal, PreferenceCard, PreferenceCardSkeleton, Prompt, ROLE_CARD, SectionHeader, SegmentedValueBar, Sidebar, SkeletonBlock, SkeletonLabel, SkeletonLines, Spacer, Spinner, StarButton, Tab, TabContent, TabList, Tabs, Tag, Text, ToggleGroup, Tooltip, TrendIndicator, TrendIndicators, ValueBar, ZStack, createGrid, cvaButton, cvaButtonPrefixSuffix, cvaButtonSpinner, cvaButtonSpinnerContainer, cvaClickable, cvaContainerStyles, cvaContentContainer, cvaContentWrapper, cvaDescriptionCard, cvaIconBackground, cvaIconButton, cvaImgStyles, cvaIndicator, cvaIndicatorIcon, cvaIndicatorIconBackground, cvaIndicatorLabel, cvaIndicatorPing, cvaInputContainer, cvaInteractableItem, cvaList, cvaListContainer, cvaListItem$1 as cvaListItem, cvaMenu, cvaMenuItem, cvaMenuItemLabel, cvaMenuItemPrefix, cvaMenuItemStyle, cvaMenuItemSuffix, cvaMenuList, cvaMenuListDivider, cvaMenuListItem, cvaMenuListMultiSelect, cvaPageHeader, cvaPageHeaderContainer, cvaPageHeaderHeading, cvaPreferenceCard, cvaTitleCard, cvaToggleGroup, cvaToggleGroupWithSlidingBackground, cvaToggleItem, cvaToggleItemContent, cvaToggleItemText, cvaZStackContainer, cvaZStackItem, defaultPageSize, docs, getDevicePixelRatio, getValueBarColorByValue, iconColorNames, iconPalette, noPagination, preferenceCardGrid, useClickOutside, useContainerBreakpoints, useContinuousTimeout, useCopyToClipboard, useCustomEncoding, useDebounce, useDevicePixelRatio, useElevatedReducer, useElevatedState, useGeolocation, useGridAreas, useHover, useInfiniteScroll, useIsFirstRender, useIsFullscreen, useIsTextTruncated, useKeyboardShortcut, useList, useListItemHeight, useLocalStorage, useLocalStorageReducer, useMeasure, useMergeRefs, useModifierKey, useOverflowItems, usePopoverContext, usePrevious, usePrompt, useRandomCSSLengths, useRelayPagination, useResize, useScrollBlock, useScrollDetection, useSelfUpdatingRef, useTextSearch, useTimeout, useViewportBreakpoints, useWatch, useWindowActivity };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-components",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.49",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"engines": {
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
"@floating-ui/react": "^0.26.25",
|
|
15
15
|
"string-ts": "^2.0.0",
|
|
16
16
|
"tailwind-merge": "^2.0.0",
|
|
17
|
-
"@trackunit/ui-design-tokens": "1.11.
|
|
18
|
-
"@trackunit/css-class-variance-utilities": "1.11.
|
|
19
|
-
"@trackunit/shared-utils": "1.13.
|
|
20
|
-
"@trackunit/ui-icons": "1.11.
|
|
17
|
+
"@trackunit/ui-design-tokens": "1.11.60",
|
|
18
|
+
"@trackunit/css-class-variance-utilities": "1.11.61",
|
|
19
|
+
"@trackunit/shared-utils": "1.13.61",
|
|
20
|
+
"@trackunit/ui-icons": "1.11.59",
|
|
21
21
|
"@tanstack/react-router": "1.114.29",
|
|
22
22
|
"es-toolkit": "^1.39.10",
|
|
23
23
|
"@tanstack/react-virtual": "3.13.12",
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
type GeoJsonPosition = [number, number] | [number, number, number];
|
|
2
|
+
type UseGeolocationReturn = Readonly<{
|
|
3
|
+
/** Last successfully resolved position as [longitude, latitude], or null if no position has been obtained yet */
|
|
4
|
+
position: GeoJsonPosition | null;
|
|
5
|
+
/**
|
|
6
|
+
* Get the current geographic position.
|
|
7
|
+
*
|
|
8
|
+
* - `prompt: true` (default) — calls `getCurrentPosition` directly, which may
|
|
9
|
+
* show the browser permission dialog if permission hasn't been granted yet.
|
|
10
|
+
* Intended for user-initiated actions (e.g. "My Location" button).
|
|
11
|
+
*
|
|
12
|
+
* - `prompt: false` — checks the permission state first and only retrieves the
|
|
13
|
+
* position if permission is already `"granted"`. Never triggers a prompt.
|
|
14
|
+
* Returns `null` when permission is not granted or the API is unavailable.
|
|
15
|
+
*
|
|
16
|
+
* On success, also updates the `position` state so dependent renders pick it up.
|
|
17
|
+
*/
|
|
18
|
+
getPosition: (options?: Readonly<{
|
|
19
|
+
prompt?: boolean;
|
|
20
|
+
}>) => Promise<GeoJsonPosition | null>;
|
|
21
|
+
}>;
|
|
22
|
+
type UseGeolocationOptions = Readonly<{
|
|
23
|
+
/**
|
|
24
|
+
* When `true`, passively checks geolocation permission on mount and
|
|
25
|
+
* populates `position` if permission is already `"granted"`.
|
|
26
|
+
* Never triggers a browser prompt.
|
|
27
|
+
*
|
|
28
|
+
* @default false
|
|
29
|
+
*/
|
|
30
|
+
requestOnMount?: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Lightweight geolocation hook.
|
|
34
|
+
*
|
|
35
|
+
* By default does nothing on mount — the consumer decides when (and how) to
|
|
36
|
+
* request a position via `getPosition`. Set `requestOnMount: true` to
|
|
37
|
+
* automatically populate `position` when permission is already granted.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* // Auto-populate position if permission is already granted
|
|
42
|
+
* const { position, getPosition } = useGeolocation({ requestOnMount: true });
|
|
43
|
+
*
|
|
44
|
+
* // User-initiated request (e.g. "My Location" button)
|
|
45
|
+
* onClick: () => {
|
|
46
|
+
* void getPosition().then(pos => { if (pos) fitBounds(pos); });
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare const useGeolocation: (options?: UseGeolocationOptions) => UseGeolocationReturn;
|
|
51
|
+
export {};
|
package/src/index.d.ts
CHANGED
|
@@ -116,6 +116,7 @@ export * from "./hooks/useDebounce";
|
|
|
116
116
|
export * from "./hooks/useDevicePixelRatio";
|
|
117
117
|
export * from "./hooks/useElevatedReducer";
|
|
118
118
|
export * from "./hooks/useElevatedState";
|
|
119
|
+
export * from "./hooks/useGeolocation";
|
|
119
120
|
export * from "./hooks/useHover";
|
|
120
121
|
export * from "./hooks/useInfiniteScroll";
|
|
121
122
|
export * from "./hooks/useIsFirstRender";
|