@trackunit/react-core-hooks 1.12.69 → 1.14.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/index.cjs.js CHANGED
@@ -230,6 +230,99 @@ const useFilterBarContext = () => {
230
230
  return context;
231
231
  };
232
232
 
233
+ const useStandaloneGeolocation = ({ enabled, requestOnMount, }) => {
234
+ const [position, setPosition] = react.useState(null);
235
+ const queryGeolocationPermission = react.useCallback(async () => {
236
+ if (!enabled || typeof navigator === "undefined") {
237
+ return null;
238
+ }
239
+ try {
240
+ const status = await navigator.permissions.query({ name: "geolocation" });
241
+ return status.state;
242
+ }
243
+ catch {
244
+ return null;
245
+ }
246
+ }, [enabled]);
247
+ const getCurrentPosition = react.useCallback(async () => {
248
+ if (!enabled || typeof navigator === "undefined") {
249
+ return null;
250
+ }
251
+ return await new Promise(resolve => {
252
+ try {
253
+ navigator.geolocation.getCurrentPosition(currentPosition => {
254
+ resolve([currentPosition.coords.longitude, currentPosition.coords.latitude]);
255
+ }, () => {
256
+ resolve(null);
257
+ });
258
+ }
259
+ catch {
260
+ resolve(null);
261
+ }
262
+ });
263
+ }, [enabled]);
264
+ const getPosition = react.useCallback(async (options) => {
265
+ const prompt = options?.prompt !== false;
266
+ if (!prompt) {
267
+ const permissionState = await queryGeolocationPermission();
268
+ if (permissionState !== "granted") {
269
+ return null;
270
+ }
271
+ }
272
+ const currentPosition = await getCurrentPosition();
273
+ if (currentPosition !== null) {
274
+ setPosition(currentPosition);
275
+ }
276
+ return currentPosition;
277
+ }, [getCurrentPosition, queryGeolocationPermission]);
278
+ react.useEffect(() => {
279
+ if (!enabled || requestOnMount !== true) {
280
+ return;
281
+ }
282
+ void queryGeolocationPermission()
283
+ .then(permissionState => {
284
+ if (permissionState !== "granted") {
285
+ return null;
286
+ }
287
+ return getCurrentPosition();
288
+ })
289
+ .then(currentPosition => {
290
+ if (currentPosition !== null) {
291
+ setPosition(currentPosition);
292
+ }
293
+ });
294
+ }, [enabled, getCurrentPosition, queryGeolocationPermission, requestOnMount]);
295
+ return react.useMemo(() => ({
296
+ position,
297
+ getPosition,
298
+ }), [getPosition, position]);
299
+ };
300
+ /**
301
+ * Hook providing geolocation capabilities.
302
+ *
303
+ * In the host, geolocation is resolved directly via the browser API.
304
+ * In Iris Apps, requests are proxied to the host via the iframe bridge,
305
+ * so permission is only requested once at the host level.
306
+ *
307
+ * @example
308
+ * ```ts
309
+ * const { position, getPosition } = useGeolocation();
310
+ *
311
+ * // User-initiated request (e.g. "My Location" button)
312
+ * onClick: () => {
313
+ * void getPosition().then(pos => { if (pos) fitBounds(pos); });
314
+ * }
315
+ * ```
316
+ */
317
+ const useGeolocation = (options) => {
318
+ const context = react.useContext(reactCoreContextsApi.GeolocationContext);
319
+ const standaloneGeolocation = useStandaloneGeolocation({
320
+ enabled: context === null,
321
+ requestOnMount: options?.requestOnMount,
322
+ });
323
+ return context ?? standaloneGeolocation;
324
+ };
325
+
233
326
  /**
234
327
  * This is a hook to use the TokenContext.
235
328
  *
@@ -1109,6 +1202,7 @@ exports.useExportDataContext = useExportDataContext;
1109
1202
  exports.useFeatureBranchQueryString = useFeatureBranchQueryString;
1110
1203
  exports.useFeatureFlags = useFeatureFlags;
1111
1204
  exports.useFilterBarContext = useFilterBarContext;
1205
+ exports.useGeolocation = useGeolocation;
1112
1206
  exports.useHasAccessTo = useHasAccessTo;
1113
1207
  exports.useImageUploader = useImageUploader;
1114
1208
  exports.useIrisAppId = useIrisAppId;
package/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
- import { AnalyticsContext, AssetSortingContext, ConfirmationDialogContext, EnvironmentContext, ErrorHandlingContext, ExportDataContext, FeatureFlagContext, FilterBarContext, TokenContext, ModalDialogContext, NavigationContext, OemBrandingContext, UserSubscriptionContext, TimeRangeContext, ToastContext, CurrentUserContext, CurrentUserPreferenceContext, WidgetConfigContext } from '@trackunit/react-core-contexts-api';
2
- import { useContext, useMemo, useState, useCallback, useReducer, useEffect, useRef } from 'react';
1
+ import { AnalyticsContext, AssetSortingContext, ConfirmationDialogContext, EnvironmentContext, ErrorHandlingContext, ExportDataContext, FeatureFlagContext, FilterBarContext, GeolocationContext, TokenContext, ModalDialogContext, NavigationContext, OemBrandingContext, UserSubscriptionContext, TimeRangeContext, ToastContext, CurrentUserContext, CurrentUserPreferenceContext, WidgetConfigContext } from '@trackunit/react-core-contexts-api';
2
+ import { useContext, useMemo, useState, useCallback, useEffect, useReducer, useRef } from 'react';
3
3
  import { isEqual } from 'es-toolkit';
4
4
  import { AssetRuntime, CustomerRuntime, EventRuntime, ParamsRuntime, SiteRuntime, WidgetConfigRuntime } from '@trackunit/iris-app-runtime-core';
5
5
 
@@ -228,6 +228,99 @@ const useFilterBarContext = () => {
228
228
  return context;
229
229
  };
230
230
 
231
+ const useStandaloneGeolocation = ({ enabled, requestOnMount, }) => {
232
+ const [position, setPosition] = useState(null);
233
+ const queryGeolocationPermission = useCallback(async () => {
234
+ if (!enabled || typeof navigator === "undefined") {
235
+ return null;
236
+ }
237
+ try {
238
+ const status = await navigator.permissions.query({ name: "geolocation" });
239
+ return status.state;
240
+ }
241
+ catch {
242
+ return null;
243
+ }
244
+ }, [enabled]);
245
+ const getCurrentPosition = useCallback(async () => {
246
+ if (!enabled || typeof navigator === "undefined") {
247
+ return null;
248
+ }
249
+ return await new Promise(resolve => {
250
+ try {
251
+ navigator.geolocation.getCurrentPosition(currentPosition => {
252
+ resolve([currentPosition.coords.longitude, currentPosition.coords.latitude]);
253
+ }, () => {
254
+ resolve(null);
255
+ });
256
+ }
257
+ catch {
258
+ resolve(null);
259
+ }
260
+ });
261
+ }, [enabled]);
262
+ const getPosition = useCallback(async (options) => {
263
+ const prompt = options?.prompt !== false;
264
+ if (!prompt) {
265
+ const permissionState = await queryGeolocationPermission();
266
+ if (permissionState !== "granted") {
267
+ return null;
268
+ }
269
+ }
270
+ const currentPosition = await getCurrentPosition();
271
+ if (currentPosition !== null) {
272
+ setPosition(currentPosition);
273
+ }
274
+ return currentPosition;
275
+ }, [getCurrentPosition, queryGeolocationPermission]);
276
+ useEffect(() => {
277
+ if (!enabled || requestOnMount !== true) {
278
+ return;
279
+ }
280
+ void queryGeolocationPermission()
281
+ .then(permissionState => {
282
+ if (permissionState !== "granted") {
283
+ return null;
284
+ }
285
+ return getCurrentPosition();
286
+ })
287
+ .then(currentPosition => {
288
+ if (currentPosition !== null) {
289
+ setPosition(currentPosition);
290
+ }
291
+ });
292
+ }, [enabled, getCurrentPosition, queryGeolocationPermission, requestOnMount]);
293
+ return useMemo(() => ({
294
+ position,
295
+ getPosition,
296
+ }), [getPosition, position]);
297
+ };
298
+ /**
299
+ * Hook providing geolocation capabilities.
300
+ *
301
+ * In the host, geolocation is resolved directly via the browser API.
302
+ * In Iris Apps, requests are proxied to the host via the iframe bridge,
303
+ * so permission is only requested once at the host level.
304
+ *
305
+ * @example
306
+ * ```ts
307
+ * const { position, getPosition } = useGeolocation();
308
+ *
309
+ * // User-initiated request (e.g. "My Location" button)
310
+ * onClick: () => {
311
+ * void getPosition().then(pos => { if (pos) fitBounds(pos); });
312
+ * }
313
+ * ```
314
+ */
315
+ const useGeolocation = (options) => {
316
+ const context = useContext(GeolocationContext);
317
+ const standaloneGeolocation = useStandaloneGeolocation({
318
+ enabled: context === null,
319
+ requestOnMount: options?.requestOnMount,
320
+ });
321
+ return context ?? standaloneGeolocation;
322
+ };
323
+
231
324
  /**
232
325
  * This is a hook to use the TokenContext.
233
326
  *
@@ -1087,4 +1180,4 @@ const useWidgetConfig = () => {
1087
1180
  return result;
1088
1181
  };
1089
1182
 
1090
- export { fetchAssetBlobUrl, useAnalytics, useAssetRuntime, useAssetSorting, useConfirmationDialog, useCurrentUser, useCurrentUserFavoriteAdvancedSensors, useCurrentUserFavoriteInsights, useCurrentUserLanguage, useCurrentUserSystemOfMeasurement, useCurrentUserTimeZonePreference, useCustomerRuntime, useEnvironment, useErrorHandler, useErrorHandlerOrNull, useEventRuntime, useExportDataContext, useFeatureBranchQueryString, useFeatureFlags, useFilterBarContext, useHasAccessTo, useImageUploader, useIrisAppId, useIrisAppImage, useIrisAppName, useModalDialogContext, useNavigateInHost, useOemBrandingContext, useSiteRuntime, useTimeRange, useToast, useToken, useUserPermission, useUserSubscription, useWidgetConfig, useWidgetConfigAsync };
1183
+ export { fetchAssetBlobUrl, useAnalytics, useAssetRuntime, useAssetSorting, useConfirmationDialog, useCurrentUser, useCurrentUserFavoriteAdvancedSensors, useCurrentUserFavoriteInsights, useCurrentUserLanguage, useCurrentUserSystemOfMeasurement, useCurrentUserTimeZonePreference, useCustomerRuntime, useEnvironment, useErrorHandler, useErrorHandlerOrNull, useEventRuntime, useExportDataContext, useFeatureBranchQueryString, useFeatureFlags, useFilterBarContext, useGeolocation, useHasAccessTo, useImageUploader, useIrisAppId, useIrisAppImage, useIrisAppName, useModalDialogContext, useNavigateInHost, useOemBrandingContext, useSiteRuntime, useTimeRange, useToast, useToken, useUserPermission, useUserSubscription, useWidgetConfig, useWidgetConfigAsync };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-core-hooks",
3
- "version": "1.12.69",
3
+ "version": "1.14.0",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -9,9 +9,9 @@
9
9
  "dependencies": {
10
10
  "react": "19.0.0",
11
11
  "es-toolkit": "^1.39.10",
12
- "@trackunit/iris-app-runtime-core": "1.13.61",
13
- "@trackunit/iris-app-runtime-core-api": "1.12.58",
14
- "@trackunit/react-core-contexts-api": "1.13.59"
12
+ "@trackunit/iris-app-runtime-core": "1.14.0",
13
+ "@trackunit/iris-app-runtime-core-api": "1.13.0",
14
+ "@trackunit/react-core-contexts-api": "1.14.0"
15
15
  },
16
16
  "module": "./index.esm.js",
17
17
  "main": "./index.cjs.js",
@@ -0,0 +1,23 @@
1
+ import { type GeolocationContextValue } from "@trackunit/react-core-contexts-api";
2
+ type UseGeolocationOptions = Readonly<{
3
+ requestOnMount?: boolean;
4
+ }>;
5
+ /**
6
+ * Hook providing geolocation capabilities.
7
+ *
8
+ * In the host, geolocation is resolved directly via the browser API.
9
+ * In Iris Apps, requests are proxied to the host via the iframe bridge,
10
+ * so permission is only requested once at the host level.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const { position, getPosition } = useGeolocation();
15
+ *
16
+ * // User-initiated request (e.g. "My Location" button)
17
+ * onClick: () => {
18
+ * void getPosition().then(pos => { if (pos) fitBounds(pos); });
19
+ * }
20
+ * ```
21
+ */
22
+ export declare const useGeolocation: (options?: UseGeolocationOptions) => GeolocationContextValue;
23
+ export {};
package/src/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export * from "./exportData/useExportDataContext";
7
7
  export * from "./featureFlags/useFeatureFlags";
8
8
  export * from "./fetchAssetBlobUrl";
9
9
  export * from "./filterBar/useFilterBarContext";
10
+ export * from "./geolocation/useGeolocation";
10
11
  export * from "./images/useImageUploader";
11
12
  export * from "./images/useIrisAppImage";
12
13
  export * from "./modalDialog/useModalDialogContext";