@mappedin/dynamic-focus 6.0.1-beta.55 → 6.0.1-beta.57
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/README.md +141 -2
- package/lib/dynamic-focus.iife.js.map +7 -0
- package/lib/esm/chunk-AZ35WLJ5.js +1207 -0
- package/lib/esm/chunk-AZ35WLJ5.js.map +7 -0
- package/lib/esm/index.d.ts +6 -1
- package/lib/esm/index.js +3 -1201
- package/lib/esm/index.js.map +4 -4
- package/lib/esm/react/index.d.ts +259 -0
- package/lib/esm/react/index.js +77 -0
- package/lib/esm/react/index.js.map +7 -0
- package/lib/rn/index-rn.d.ts +4 -0
- package/lib/rn/index-rn.js +278 -0
- package/lib/rn/index-rn.js.map +7 -0
- package/lib/rn/logger.d.ts +10 -0
- package/lib/rn/rn/extension-augmentation.d.ts +15 -0
- package/lib/rn/rn/extension-source.d.ts +6 -0
- package/lib/rn/rn/use-dynamic-focus-events.d.ts +20 -0
- package/lib/rn/rn/use-dynamic-focus-registration.d.ts +31 -0
- package/lib/rn/rn/use-dynamic-focus.d.ts +91 -0
- package/lib/rn/rn/utils/facade-hydration.d.ts +8 -0
- package/lib/rn/types.d.ts +100 -0
- package/package.json +49 -12
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/rn/use-dynamic-focus.ts
|
|
5
|
+
import { useState, useCallback as useCallback2, useContext as useContext2, useRef } from "react";
|
|
6
|
+
import { MappedinContext as MappedinContext2 } from "@mappedin/react-native-sdk";
|
|
7
|
+
|
|
8
|
+
// src/rn/use-dynamic-focus-events.ts
|
|
9
|
+
import { useCallback, useContext } from "react";
|
|
10
|
+
|
|
11
|
+
// ../packages/common/Mappedin.Logger.ts
|
|
12
|
+
var MI_ERROR_LABEL = "[MappedinJS]";
|
|
13
|
+
function createLogger(name = "", { prefix = MI_ERROR_LABEL } = {}) {
|
|
14
|
+
const label = `${prefix}${name ? `-${name}` : ""}`;
|
|
15
|
+
const rnDebug = /* @__PURE__ */ __name((type, args) => {
|
|
16
|
+
if (typeof window !== "undefined" && window.rnDebug) {
|
|
17
|
+
const processed = args.map((arg) => {
|
|
18
|
+
if (arg instanceof Error && arg.stack) {
|
|
19
|
+
return `${arg.message}
|
|
20
|
+
${arg.stack}`;
|
|
21
|
+
}
|
|
22
|
+
return arg;
|
|
23
|
+
});
|
|
24
|
+
window.rnDebug(`${name} ${type}: ${processed.join(" ")}`);
|
|
25
|
+
}
|
|
26
|
+
}, "rnDebug");
|
|
27
|
+
return {
|
|
28
|
+
logState: false ? 3 /* SILENT */ : 0 /* LOG */,
|
|
29
|
+
log(...args) {
|
|
30
|
+
if (this.logState <= 0 /* LOG */) {
|
|
31
|
+
console.log(label, ...args);
|
|
32
|
+
rnDebug("log", args);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
warn(...args) {
|
|
36
|
+
if (this.logState <= 1 /* WARN */) {
|
|
37
|
+
console.warn(label, ...args);
|
|
38
|
+
rnDebug("warn", args);
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
error(...args) {
|
|
42
|
+
if (this.logState <= 2 /* ERROR */) {
|
|
43
|
+
console.error(label, ...args);
|
|
44
|
+
rnDebug("error", args);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
// It's a bit tricky to prepend [MappedinJs] to assert and time because of how the output is structured in the console, so it is left out for simplicity
|
|
48
|
+
assert(...args) {
|
|
49
|
+
console.assert(...args);
|
|
50
|
+
},
|
|
51
|
+
time(label2) {
|
|
52
|
+
console.time(label2);
|
|
53
|
+
},
|
|
54
|
+
timeEnd(label2) {
|
|
55
|
+
console.timeEnd(label2);
|
|
56
|
+
},
|
|
57
|
+
setLevel(level) {
|
|
58
|
+
if (0 /* LOG */ <= level && level <= 3 /* SILENT */) {
|
|
59
|
+
this.logState = level;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
__name(createLogger, "createLogger");
|
|
65
|
+
var Logger = createLogger();
|
|
66
|
+
|
|
67
|
+
// src/logger.ts
|
|
68
|
+
var Logger2 = createLogger("", { prefix: "[DynamicFocus]" });
|
|
69
|
+
|
|
70
|
+
// src/rn/use-dynamic-focus-events.ts
|
|
71
|
+
import {
|
|
72
|
+
MappedinContext,
|
|
73
|
+
useEventCallback,
|
|
74
|
+
createEventSetupScript,
|
|
75
|
+
createEventCleanupScript
|
|
76
|
+
} from "@mappedin/react-native-sdk";
|
|
77
|
+
|
|
78
|
+
// src/rn/utils/facade-hydration.ts
|
|
79
|
+
function hydrateFacades(mapData, facades) {
|
|
80
|
+
return facades.map((facadeData) => {
|
|
81
|
+
if (facadeData && facadeData.id) {
|
|
82
|
+
const hydratedFacade = mapData.getById("facade", facadeData.id);
|
|
83
|
+
return hydratedFacade || facadeData;
|
|
84
|
+
}
|
|
85
|
+
return facadeData;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
__name(hydrateFacades, "hydrateFacades");
|
|
89
|
+
|
|
90
|
+
// src/rn/use-dynamic-focus-registration.ts
|
|
91
|
+
import { useRegisterExtension } from "@mappedin/react-native-sdk";
|
|
92
|
+
|
|
93
|
+
// src/rn/extension-source.ts
|
|
94
|
+
var EXTENSION_SOURCE_PLACEHOLDER = `
|
|
95
|
+
// Define require function that uses bridge.modules for external dependencies
|
|
96
|
+
globalThis.require = globalThis.require || ((id) => {
|
|
97
|
+
if (window.bridge?.modules?.[id]) {
|
|
98
|
+
return window.bridge.modules[id];
|
|
99
|
+
}
|
|
100
|
+
throw new Error('Module not found: ' + id);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
"use strict";var DynamicFocus=(()=>{var J=Object.defineProperty;var Yt=Object.getOwnPropertyDescriptor;var \$t=Object.getOwnPropertyNames;var Ht=Object.prototype.hasOwnProperty;var xt=a=>{throw TypeError(a)};var qt=(a,t,e)=>t in a?J(a,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[t]=e;var n=(a,t)=>J(a,"name",{value:t,configurable:!0}),rt=(a=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(a,{get:(t,e)=>(typeof require<"u"?require:t)[e]}):a)(function(a){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+a+'" is not supported')});var Wt=(a,t)=>{for(var e in t)J(a,e,{get:t[e],enumerable:!0})},Kt=(a,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of \$t(t))!Ht.call(a,s)&&s!==e&&J(a,s,{get:()=>t[s],enumerable:!(i=Yt(t,s))||i.enumerable});return a};var zt=a=>Kt(J({},"__esModule",{value:!0}),a);var S=(a,t,e)=>qt(a,typeof t!="symbol"?t+"":t,e),mt=(a,t,e)=>t.has(a)||xt("Cannot "+e);var o=(a,t,e)=>(mt(a,t,"read from private field"),e?e.call(a):t.get(a)),d=(a,t,e)=>t.has(a)?xt("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(a):t.set(a,e),l=(a,t,e,i)=>(mt(a,t,"write to private field"),i?i.call(a,e):t.set(a,e),e),v=(a,t,e)=>(mt(a,t,"access private method"),e);var oo={};Wt(oo,{DynamicFocus:()=>ht});var jt=rt("@mappedin/mappedin-js");function Vt(a,t){if(a==null||t==null)return a===t;if(a.length!==t.length)return!1;for(let e=0;e<a.length;e++)if(a[e]!==t[e])return!1;return!0}n(Vt,"arraysEqual");var pt=class pt{constructor(){S(this,"_subscribers",{});S(this,"_destroyed",!1)}publish(t,e){!this._subscribers||!this._subscribers[t]||this._destroyed||this._subscribers[t].forEach(function(i){typeof i=="function"&&i(e)})}on(t,e){(!this._subscribers||this._destroyed)&&(this._subscribers={}),this._subscribers[t]=this._subscribers[t]||[],this._subscribers[t].push(e)}off(t,e){if(!this._subscribers||this._subscribers[t]==null||this._destroyed)return;let i=this._subscribers[t].indexOf(e);i!==-1&&this._subscribers[t].splice(i,1)}destroy(){this._destroyed=!0,this._subscribers={}}};n(pt,"PubSub");var nt=pt;var U=rt("@mappedin/mappedin-js");var k,X,V,y,m,E,T,j,G,Ft=class Ft{constructor(t,e){S(this,"__type","building");d(this,k);d(this,X,new Map);d(this,V,new Map);d(this,y,[]);d(this,m);d(this,E);d(this,T);S(this,"defaultFloor");S(this,"activeFloor");S(this,"excluded",!1);d(this,j);d(this,G);l(this,k,t),l(this,X,new Map(t.floors.map(i=>[i.id,i]))),l(this,V,new Map(t.floors.map(i=>[i.elevation,i]))),l(this,y,Array.from(o(this,V).values()).sort((i,s)=>i.elevation-s.elevation)),this.defaultFloor=t.defaultFloor,this.activeFloor=this.defaultFloor,l(this,m,e)}get id(){return o(this,k).id}get name(){return o(this,k).name}get floors(){return o(this,k).floors}get floorStack(){return o(this,k)}get facade(){return o(this,k).facade}get isCurrentFloorStack(){return this.id===o(this,m).currentFloorStack.id}get isIndoor(){let t=o(this,m).getState(this.facade);return this.isCurrentFloorStack||t?.visible===!1&&t?.opacity===0}get canSetFloor(){return this.isIndoor||!this.excluded}get maxElevation(){return o(this,j)==null&&l(this,j,Math.max(...o(this,V).keys())),o(this,j)}get minElevation(){return o(this,G)==null&&l(this,G,Math.min(...o(this,V).keys())),o(this,G)}has(t){return U.Floor.is(t)?o(this,X).has(t.id):U.FloorStack.is(t)?t.id===o(this,k).id:U.Facade.is(t)?t.id===this.facade.id:!1}get aboveGroundFloors(){return o(this,y).filter(t=>t.elevation>=0)}expandFacade(){if(!o(this,m).options.multiFloorView?.enabled||!this.facade||this.facade.__type!=="facade"||this.facade.spaces.length<1)return;let t=o(this,m).options.multiFloorView.floorGap??10;if(!o(this,E)){l(this,E,new Map);for(let p of this.facade.spaces){let h=o(this,m).getState(p),b=h?.altitude??0,N=h?.height??0;o(this,E).has(b)||o(this,E).set(b,[]),o(this,E).get(b).push({space:p,originalAltitude:b,originalHeight:N})}}let e=Array.from(o(this,E).keys()).sort((p,h)=>p-h),i=e.length===1,s=i?t*(this.aboveGroundFloors.length-1):t,c=0;e.forEach(p=>{let h=o(this,E).get(p);for(let{space:b}of h)o(this,m).updateState(b,{height:i?s:t,altitude:c});i||(c+=t)})}collapseFacade(){if(o(this,E)){for(let t of o(this,E).values())for(let{space:e,originalAltitude:i,originalHeight:s}of t)o(this,m).updateState(e,{height:s,altitude:i});l(this,E,void 0)}}getFloorByElevation(t){return o(this,V).get(t)}getNearestFloorByElevation(t){let e=this.getFloorByElevation(t);if(e)return e;if(t>=0){if(t>this.maxElevation)return o(this,y)[o(this,y).length-1];for(let i=o(this,y).length-1;i>=0;i--){let s=o(this,y)[i];if(s.elevation<=t)return s}}else{if(t<this.minElevation)return o(this,y)[0];for(let i of o(this,y))if(i.elevation>=t)return i}}getHighestFloor(){return o(this,y)[o(this,y).length-1]}cancelAnimation(){o(this,T)&&(o(this,T).animation.cancel(),l(this,T,void 0))}async animateFacade(t,e={duration:150}){let i=o(this,T)?.state||o(this,m).getState(this.facade);if(!i||!t||i?.opacity===t.opacity&&i?.visible===t.visible)return{result:"completed"};this.cancelAnimation();let{opacity:s,visible:c}=t;if(o(this,m).updateState(this.facade,{visible:!0}),s!==void 0){l(this,T,{animation:o(this,m).animateState(this.facade,{opacity:s},{duration:e.duration}),state:t});let p=await o(this,T).animation;if(l(this,T,void 0),p.result==="cancelled")return p}return c===!1&&o(this,m).updateState(this.facade,{visible:c}),{result:"completed"}}destroy(){this.cancelAnimation(),o(this,k).floors.forEach(t=>{o(this,m).updateState(t,{visible:t.id===o(this,m).currentFloor.id})}),this.has(o(this,m).currentFloor)||o(this,m).updateState(this.facade,{visible:!0,opacity:1})}};k=new WeakMap,X=new WeakMap,V=new WeakMap,y=new WeakMap,m=new WeakMap,E=new WeakMap,T=new WeakMap,j=new WeakMap,G=new WeakMap,n(Ft,"Building");var lt=Ft;var Ot=rt("@mappedin/mappedin-js");function _t(a,t,e){return a>=t?"in-range":a<=e?"out-of-range":"transition"}n(_t,"getZoomState");function dt(a,t,e){if(a.__type==="outdoors"&&"floor"in a)return a.floor;let i=a;if(i.excluded)return i.activeFloor;switch(t){case"lock-elevation":return i.getFloorByElevation(e);case"nearest-elevation":return i.getNearestFloorByElevation(e);default:break}return i.isIndoor?i.activeFloor:i.defaultFloor}n(dt,"getFloorToShow");function Qt(a,t){return{facade:a,state:{type:"facade",visible:!t,opacity:t?0:1}}}n(Qt,"getFacadeState");function Jt(a,t,e){return{outdoorOpacity:1,floorStates:a.floors.map(i=>{let s=i.id===t.id&&e;return{floor:i,state:{type:"floor",visible:s,geometry:{visible:s},labels:{enabled:s},markers:{enabled:s},footprint:{visible:!1},occlusion:{enabled:!1}}}})}}n(Jt,"getSingleBuildingState");function Mt(a,t){if(!t.includes("lock-elevation"))return 1;for(let e of a)if(e.outdoorOpacity>=0&&e.outdoorOpacity<=1)return e.outdoorOpacity;return 1}n(Mt,"getOutdoorOpacity");function Nt(a,t,e){switch(e){case"in-range":return a[0]?.canSetFloor?a[0]:void 0;case"out-of-range":return t;case"transition":default:return}}n(Nt,"getSetFloorTargetFromZoomState");function Bt(a,t,e,i,s,c,p,h,b){let N=new Map;for(let f of a){let R=t.has(f.id),Q=gt(f,e,i==="in-range",R,s,c,h.includes(f.id)),B=e.floorStack.id===f.id?f.activeFloor:dt(f,s,c),st=b.enabled?(0,Ot.getMultiFloorState)(f.floors,B||f.activeFloor,b.floorGap,p):Jt(f.floorStack,B||f.activeFloor,Q),ft=Qt(f.facade,Q);N.set(f.id,{building:f,showIndoor:Q,floorStackState:st,facadeState:ft,inFocus:R,floorToShow:B})}return N}n(Bt,"getBuildingStates");function Lt(a,t){return a.excluded?"none":t?"indoor":"outdoor"}n(Lt,"getBuildingAnimationType");function gt(a,t,e,i,s,c,p){if(a.id===t.floorStack.id)return!0;if(a.excluded)return!1;if(p===!0)return!0;if(!e)return!1;switch(s){case"lock-elevation":return a.getFloorByElevation(c)!=null;default:break}return!!i}n(gt,"shouldShowIndoor");function It(a,t,e){return a!==t&&!e}n(It,"shouldDeferSetFloor");var Ct=["default-floor","lock-elevation","nearest-elevation"];var ut=rt("@mappedin/mappedin-js");var Xt="[MappedinJS]";function St(a="",{prefix:t=Xt}={}){let e=\`\${t}\${a?\`-\${a}\`:""}\`,i=n((s,c)=>{if(typeof window<"u"&&window.rnDebug){let p=c.map(h=>h instanceof Error&&h.stack?\`\${h.message}
|
|
104
|
+
\${h.stack}\`:h);window.rnDebug(\`\${a} \${s}: \${p.join(" ")}\`)}},"rnDebug");return{logState:0,log(...s){this.logState<=0&&(console.log(e,...s),i("log",s))},warn(...s){this.logState<=1&&(console.warn(e,...s),i("warn",s))},error(...s){this.logState<=2&&(console.error(e,...s),i("error",s))},assert(...s){console.assert(...s)},time(s){console.time(s)},timeEnd(s){console.timeEnd(s)},setLevel(s){0<=s&&s<=3&&(this.logState=s)}}}n(St,"createLogger");var to=St();var Y=to;function Zt(a,t,e,i){return(a<t||a>e)&&Y.warn(i),Math.min(e,Math.max(t,a))}n(Zt,"clampWithWarning");function yt(a,t){return!ut.Floor.is(a)||a?.floorStack==null||!ut.FloorStack.is(t)?!1:a.floorStack.id!==t.id?(Y.warn(\`Floor (\${a.id}) does not belong to floor stack (\${t.id}).\`),!1):!0}n(yt,"validateFloorForStack");function Et(a,t,e,i){return Zt(a,t,e,\`\${i} must be between \${t} and \${e}.\`)}n(Et,"validateZoomThreshold");var \$=St("",{prefix:"[DynamicFocus]"});var w,H,tt,bt,vt=class vt{constructor(t,e){d(this,tt);S(this,"__type","outdoors");d(this,w);d(this,H);l(this,w,t),l(this,H,e)}get id(){return o(this,w).id}get floorStack(){return o(this,w)}get floor(){return o(this,w).defaultFloor}matchesFloorStack(t){return t===o(this,w)||t===o(this,w).id}show(){v(this,tt,bt).call(this,!0)}hide(){v(this,tt,bt).call(this,!1)}destroy(){this.matchesFloorStack(o(this,H).currentFloorStack)||this.hide()}};w=new WeakMap,H=new WeakMap,tt=new WeakSet,bt=n(function(t){for(let e of o(this,w).floors)o(this,H).updateState(e,{visible:t})},"#setVisible"),n(vt,"Outdoors");var ct=vt;function Pt(a,t){let e=a.find(s=>s.type?.toLowerCase()==="outdoor");if(e)return e;let i=a.find(s=>s.facade==null&&!t.has(s.id));return i||(\$.warn("No good candidate for the outdoor floor stack was found. Using the first floor stack."),a[0])}n(Pt,"getOutdoorFloorStack");var Rt={indoorZoomThreshold:18,outdoorZoomThreshold:17,setFloorOnFocus:!0,autoFocus:!1,indoorAnimationOptions:{duration:150},outdoorAnimationOptions:{duration:150},autoAdjustFacadeHeights:!1,mode:"default-floor",preloadFloors:!0};var D,r,L,u,I,C,F,O,Z,P,A,q,x,_,W,M,g,kt,Dt,ot,et,it,at,K,At,Gt,Ut,z,Tt=class Tt{constructor(t){d(this,g);d(this,D);d(this,r);d(this,L);d(this,u,Rt);d(this,I,[]);d(this,C,new Set);d(this,F,new Map);d(this,O);d(this,Z,!1);d(this,P,!1);d(this,A,0);d(this,q,0);d(this,x,"transition");d(this,_,"outdoor");d(this,W,[]);d(this,M,!1);S(this,"sceneUpdateQueue",Promise.resolve());S(this,"on",n((t,e)=>{o(this,D).on(t,e)},"on"));S(this,"off",n((t,e)=>{o(this,D).off(t,e)},"off"));d(this,ot,n(t=>{if(!this.isEnabled)return;let{facades:e}=t;if(!(Vt(e,this.focusedFacades)&&o(this,_)==="transition"&&!o(this,P))){if(l(this,P,!1),l(this,I,[]),o(this,C).clear(),e.length>0)for(let i of e){let s=o(this,F).get(i.floorStack.id);s&&(o(this,C).add(s.id),o(this,I).push(s))}o(this,u).autoFocus&&this.focus()}},"#handleFacadesInViewChange"));d(this,et,n(async t=>{if(!this.isEnabled)return;let{floor:e}=t,i=o(this,F).get(e.floorStack.id);i&&(i.activeFloor=e),i?.excluded===!1&&!o(this,O).matchesFloorStack(e.floorStack)&&l(this,A,e.elevation),o(this,r).manualFloorVisibility===!0&&(t.reason!=="dynamic-focus"&&(await o(this,z).call(this,e),o(this,D).publish("focus",{facades:this.focusedFacades})),o(this,r).options.multiFloorView!=null&&o(this,r).options.multiFloorView?.enabled&&o(this,r).options.multiFloorView?.updateCameraElevationOnFloorChange&&o(this,r).Camera.elevation!==o(this,A)*(o(this,r).options.multiFloorView.floorGap??0)&&o(this,r).Camera.animateElevation(o(this,A)*(o(this,r).options.multiFloorView.floorGap??0),{duration:750,easing:"ease-in-out"}))},"#handleFloorChangeStart"));d(this,it,n(()=>{l(this,Z,!0)},"#handleUserInteractionStart"));d(this,at,n(()=>{l(this,Z,!1)},"#handleUserInteractionEnd"));d(this,K,n(t=>{if(!this.isEnabled)return;let{zoomLevel:e}=t,i=_t(e,o(this,u).indoorZoomThreshold,o(this,u).outdoorZoomThreshold);o(this,x)!==i&&(l(this,x,i),i==="in-range"?this.setIndoor():i==="out-of-range"&&this.setOutdoor())},"#handleCameraChange"));d(this,z,n(async t=>{if(o(this,r).manualFloorVisibility!==!0||!this.isEnabled)return;let e=t||o(this,r).currentFloor,i=o(this,r).options.multiFloorView,s=o(this,r).Navigation?.floors?.map(h=>h.id)||[],c=o(this,r).Navigation?.floorStacks.map(h=>h.id)||[],p=new Promise(async h=>{await this.sceneUpdateQueue;let b=Bt(Array.from(o(this,F).values()),o(this,C),e,o(this,x),o(this,u).mode,o(this,A),s,c,i),N=[];await Promise.all(Array.from(b.values()).map(async f=>{let{building:R,showIndoor:Q,floorStackState:B,facadeState:st,inFocus:ft}=f;N.push(B);let wt=Lt(R,Q);if(wt==="indoor")return v(this,g,Gt).call(this,R,B,st,ft);if(wt==="outdoor")return v(this,g,Ut).call(this,R,B,st,c)})),o(this,r).Outdoor.setOpacity(Mt(N,o(this,u).mode)),l(this,W,o(this,I).filter(f=>b.get(f.id)?.showIndoor===!0).map(f=>f.facade)),o(this,D).publish("focus",{facades:o(this,W)}),h()});return this.sceneUpdateQueue=p,p},"#applyBuildingStates"));l(this,D,new nt),l(this,r,t),\$.setLevel(Y.logState),l(this,L,o(this,r).getMapData()),l(this,A,o(this,r).currentFloor.elevation),l(this,q,o(this,r).Camera.elevation);for(let e of o(this,L).getByType("facade"))o(this,F).set(e.floorStack.id,new lt(e.floorStack,o(this,r)));l(this,O,new ct(Pt(o(this,L).getByType("floor-stack"),o(this,F)),o(this,r))),o(this,r).on("floor-change-start",o(this,et)),o(this,r).on("camera-change",o(this,K)),o(this,r).on("facades-in-view-change",o(this,ot)),o(this,r).on("user-interaction-start",o(this,it)),o(this,r).on("user-interaction-end",o(this,at))}enable(t){if(o(this,M)){\$.warn("enable() called on an already enabled Dynamic Focus instance.");return}l(this,M,!0),o(this,r).manualFloorVisibility=!0,o(this,O).show(),this.updateState({...o(this,u),...t}),o(this,K).call(this,{zoomLevel:o(this,r).Camera.zoomLevel,center:o(this,r).Camera.center,bearing:o(this,r).Camera.bearing,pitch:o(this,r).Camera.pitch}),o(this,r).Camera.updateFacadesInView()}disable(){if(!o(this,M)){\$.warn("disable() called on an already disabled Dynamic Focus instance.");return}l(this,M,!1),o(this,r).manualFloorVisibility=!1}get isEnabled(){return o(this,M)}get isIndoor(){return o(this,_)==="indoor"}get isOutdoor(){return o(this,_)==="outdoor"}setIndoor(){l(this,_,"indoor"),l(this,x,"in-range"),v(this,g,kt).call(this)}setOutdoor(){l(this,_,"outdoor"),l(this,x,"out-of-range"),v(this,g,kt).call(this)}get focusedFacades(){return[...o(this,W)]}getState(){return{...o(this,u)}}updateState(t){t.indoorZoomThreshold!=null&&(t.indoorZoomThreshold=Et(t.indoorZoomThreshold,o(this,u).outdoorZoomThreshold,o(this,r).Camera.maxZoomLevel,"indoorZoomThreshold")),t.outdoorZoomThreshold!=null&&(t.outdoorZoomThreshold=Et(t.outdoorZoomThreshold,o(this,r).Camera.minZoomLevel,o(this,u).indoorZoomThreshold,"outdoorZoomThreshold")),t.mode!=null&&(t.mode=Ct.includes(t.mode)?t.mode:"default-floor"),t.autoAdjustFacadeHeights!=null&&(o(this,u).autoAdjustFacadeHeights=t.autoAdjustFacadeHeights,o(this,F).forEach(e=>{o(this,u).autoAdjustFacadeHeights?e.expandFacade():e.collapseFacade()})),l(this,u,Object.assign({},o(this,u),Object.fromEntries(Object.entries(t).filter(([,e])=>e!=null)))),t.mode!=null&&t.preloadFloors&&v(this,g,Dt).call(this,t.mode)}destroy(){this.disable(),o(this,r).off("facades-in-view-change",o(this,ot)),o(this,r).off("floor-change-start",o(this,et)),o(this,r).off("camera-change",o(this,K)),o(this,r).off("user-interaction-start",o(this,it)),o(this,r).off("user-interaction-end",o(this,at)),o(this,F).forEach(t=>t.destroy()),o(this,O).destroy(),o(this,D).destroy()}async focus(t=o(this,u).setFloorOnFocus){if(t)if(It(o(this,q),o(this,r).Camera.elevation,o(this,Z)))l(this,q,o(this,r).Camera.elevation),l(this,P,!0);else{let e=Nt(o(this,I),o(this,O),o(this,x)),i=e?dt(e,o(this,u).mode,o(this,A)):void 0;i&&i.id!==o(this,r).currentFloor.id&&o(this,r).setFloor(i,{context:"dynamic-focus"})}await o(this,z).call(this),o(this,D).publish("focus",{facades:this.focusedFacades})}preloadFloors(){v(this,g,Dt).call(this,o(this,u).mode)}setDefaultFloorForStack(t,e){if(!yt(e,t))return;let i=o(this,F).get(t.id);i&&(i.defaultFloor=e)}resetDefaultFloorForStack(t){let e=o(this,F).get(t.id);e&&(e.defaultFloor=t.defaultFloor)}getDefaultFloorForStack(t){return o(this,F).get(t.id)?.defaultFloor||t.defaultFloor||t.floors[0]}setCurrentFloorForStack(t,e){if(!yt(e,t))return;let i=o(this,F).get(t.id);i&&(i.activeFloor=e,o(this,z).call(this))}exclude(t){let e=Array.isArray(t)?t:[t];for(let i of e){let s=o(this,F).get(i.id);s&&(s.excluded=!0)}}include(t){let e=Array.isArray(t)?t:[t];for(let i of e){let s=o(this,F).get(i.id);s&&(s.excluded=!1)}}getCurrentFloorForStack(t){return o(this,F).get(t.id)?.activeFloor||this.getDefaultFloorForStack(t)}};D=new WeakMap,r=new WeakMap,L=new WeakMap,u=new WeakMap,I=new WeakMap,C=new WeakMap,F=new WeakMap,O=new WeakMap,Z=new WeakMap,P=new WeakMap,A=new WeakMap,q=new WeakMap,x=new WeakMap,_=new WeakMap,W=new WeakMap,M=new WeakMap,g=new WeakSet,kt=n(function(){o(this,u).autoFocus&&(o(this,Z)?this.focus():l(this,P,!0)),o(this,D).publish("state-change")},"#handleViewStateChange"),Dt=n(function(t){o(this,r).preloadFloors(o(this,L).getByType("facade").map(e=>dt(o(this,F).get(e.floorStack.id),t,o(this,A))).filter(e=>e!=null&&jt.Floor.is(e)))},"#preloadFloors"),ot=new WeakMap,et=new WeakMap,it=new WeakMap,at=new WeakMap,K=new WeakMap,At=n(function(t,e,i){t.forEach(s=>{if(s.floor)if(e){let c=i!==void 0?{...s.state,labels:{...s.state.labels,enabled:s.state.labels.enabled&&i},markers:{...s.state.markers,enabled:s.state.markers.enabled&&i},occlusion:{...s.state.occlusion,enabled:s.state.occlusion.enabled&&i}}:s.state;o(this,r).updateState(s.floor,c)}else o(this,r).updateState(s.floor,{visible:!1})})},"#updateFloorVisibility"),Gt=n(async function(t,e,i,s){return v(this,g,At).call(this,e.floorStates,!0,s),t.animateFacade(i.state,o(this,u).indoorAnimationOptions)},"#animateIndoorSequence"),Ut=n(async function(t,e,i,s){let c=await t.animateFacade(i.state,o(this,u).outdoorAnimationOptions);return c.result==="completed"&&v(this,g,At).call(this,e.floorStates,gt(t,o(this,r).currentFloor,o(this,x)==="in-range",o(this,C).has(t.id),o(this,u).mode,o(this,A),s.includes(t.floorStack.id))),c},"#animateOutdoorSequence"),z=new WeakMap,n(Tt,"DynamicFocus");var ht=Tt;return zt(oo);})();
|
|
105
|
+
//# sourceMappingURL=dynamic-focus.iife.js.map
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
// Register the extension
|
|
109
|
+
if (window.bridge && window.bridge.extensions) {
|
|
110
|
+
try {
|
|
111
|
+
window.bridge.extensions.register({ name: 'dynamic-focus', package: { Extension: DynamicFocus.DynamicFocus } });
|
|
112
|
+
if (window.rnDebug) window.rnDebug('Extension: dynamic-focus registered');
|
|
113
|
+
} catch(e) {
|
|
114
|
+
if (window.rnDebug) window.rnDebug('Failed to register dynamic-focus extension: ' + e.message);
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
if (window.rnDebug) window.rnDebug('Bridge or extensions not available for registration');
|
|
118
|
+
}`;
|
|
119
|
+
function getRegistrationScript() {
|
|
120
|
+
return EXTENSION_SOURCE_PLACEHOLDER;
|
|
121
|
+
}
|
|
122
|
+
__name(getRegistrationScript, "getRegistrationScript");
|
|
123
|
+
|
|
124
|
+
// src/rn/use-dynamic-focus-registration.ts
|
|
125
|
+
function useDynamicFocusRegistration() {
|
|
126
|
+
useRegisterExtension({
|
|
127
|
+
extensionName: "dynamic-focus",
|
|
128
|
+
getRegistrationScript,
|
|
129
|
+
logger: Logger2
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
__name(useDynamicFocusRegistration, "useDynamicFocusRegistration");
|
|
133
|
+
|
|
134
|
+
// src/rn/use-dynamic-focus-events.ts
|
|
135
|
+
function useDynamicFocusEvent(event, callback) {
|
|
136
|
+
if (typeof event !== "string" || event.length === 0) {
|
|
137
|
+
throw new Error("Event parameter must be a non-empty string");
|
|
138
|
+
}
|
|
139
|
+
if (typeof callback !== "function") {
|
|
140
|
+
throw new Error("Callback parameter must be a function");
|
|
141
|
+
}
|
|
142
|
+
const context = useContext(MappedinContext);
|
|
143
|
+
const { extensions, mapData } = context;
|
|
144
|
+
if (!mapData) {
|
|
145
|
+
throw new Error("Map data is not available");
|
|
146
|
+
}
|
|
147
|
+
useDynamicFocusRegistration();
|
|
148
|
+
const extensionRegistrationState = extensions?.["dynamic-focus"]?.registrationState || "unregistered";
|
|
149
|
+
const extensionIsReady = extensionRegistrationState === "registered";
|
|
150
|
+
const processedCallback = useCallback(
|
|
151
|
+
(payload) => {
|
|
152
|
+
try {
|
|
153
|
+
let processedPayload = payload;
|
|
154
|
+
if (event === "focus" && payload.facades) {
|
|
155
|
+
processedPayload = {
|
|
156
|
+
...payload,
|
|
157
|
+
facades: hydrateFacades(mapData, payload.facades)
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
callback(processedPayload);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
Logger2.error(`Error in dynamic focus event callback for ${String(event)}:`, error);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
[event, mapData, callback]
|
|
166
|
+
);
|
|
167
|
+
useEventCallback({
|
|
168
|
+
eventKey: `dynamic-focus:${String(event)}`,
|
|
169
|
+
callback: processedCallback,
|
|
170
|
+
setupScript: /* @__PURE__ */ __name(() => createEventSetupScript({
|
|
171
|
+
extensionName: "dynamic-focus",
|
|
172
|
+
eventName: String(event)
|
|
173
|
+
}), "setupScript"),
|
|
174
|
+
cleanupScript: /* @__PURE__ */ __name(() => createEventCleanupScript({
|
|
175
|
+
extensionName: "dynamic-focus",
|
|
176
|
+
eventName: String(event)
|
|
177
|
+
}), "cleanupScript"),
|
|
178
|
+
shouldInject: extensionIsReady
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
__name(useDynamicFocusEvent, "useDynamicFocusEvent");
|
|
182
|
+
|
|
183
|
+
// src/rn/use-dynamic-focus.ts
|
|
184
|
+
function useDynamicFocus() {
|
|
185
|
+
const context = useContext2(MappedinContext2);
|
|
186
|
+
const { bridge, extensions, updateExtensionState } = context;
|
|
187
|
+
useDynamicFocusRegistration();
|
|
188
|
+
const extensionState = extensions["dynamic-focus"];
|
|
189
|
+
const registrationState = extensionState?.registrationState || "unregistered";
|
|
190
|
+
const isReady = registrationState === "registered";
|
|
191
|
+
const isEnabled = extensionState?.isEnabled || false;
|
|
192
|
+
const [focusedFacades, setFocusedFacades] = useState(extensionState?.focusedFacades || []);
|
|
193
|
+
const extensionsRef = useRef(extensions);
|
|
194
|
+
extensionsRef.current = extensions;
|
|
195
|
+
useDynamicFocusEvent(
|
|
196
|
+
"focus",
|
|
197
|
+
useCallback2(
|
|
198
|
+
(event) => {
|
|
199
|
+
const newFocusedFacades = event.facades;
|
|
200
|
+
setFocusedFacades(newFocusedFacades);
|
|
201
|
+
updateExtensionState("dynamic-focus", (prev) => ({
|
|
202
|
+
...prev,
|
|
203
|
+
registrationState,
|
|
204
|
+
focusedFacades: newFocusedFacades
|
|
205
|
+
}));
|
|
206
|
+
},
|
|
207
|
+
[registrationState, updateExtensionState]
|
|
208
|
+
)
|
|
209
|
+
);
|
|
210
|
+
const callExtensionMethod = useCallback2(
|
|
211
|
+
async (method, args = []) => {
|
|
212
|
+
if (!bridge?.isReady) {
|
|
213
|
+
throw new Error("Bridge is not ready. Ensure the WebView is loaded.");
|
|
214
|
+
}
|
|
215
|
+
return await bridge.instruct({
|
|
216
|
+
type: "extension",
|
|
217
|
+
payload: {
|
|
218
|
+
name: "dynamic-focus",
|
|
219
|
+
method,
|
|
220
|
+
args
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
[bridge]
|
|
225
|
+
);
|
|
226
|
+
const enable = useCallback2(
|
|
227
|
+
async (options) => {
|
|
228
|
+
const currentExtensionState = extensionsRef.current?.["dynamic-focus"];
|
|
229
|
+
const currentIsEnabled = currentExtensionState?.isEnabled || false;
|
|
230
|
+
if (currentIsEnabled) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
await callExtensionMethod("enable", [options]);
|
|
234
|
+
updateExtensionState("dynamic-focus", (prev) => ({ ...prev, isEnabled: true }));
|
|
235
|
+
},
|
|
236
|
+
[callExtensionMethod, updateExtensionState]
|
|
237
|
+
);
|
|
238
|
+
const updateState = useCallback2(
|
|
239
|
+
async (state) => {
|
|
240
|
+
return await callExtensionMethod("updateState", [state]);
|
|
241
|
+
},
|
|
242
|
+
[callExtensionMethod]
|
|
243
|
+
);
|
|
244
|
+
const getState = useCallback2(async () => {
|
|
245
|
+
return await callExtensionMethod("getState");
|
|
246
|
+
}, [callExtensionMethod]);
|
|
247
|
+
const focus = useCallback2(
|
|
248
|
+
async (setFloor) => {
|
|
249
|
+
return await callExtensionMethod("focus", [setFloor]);
|
|
250
|
+
},
|
|
251
|
+
[callExtensionMethod]
|
|
252
|
+
);
|
|
253
|
+
const disable = useCallback2(async () => {
|
|
254
|
+
await callExtensionMethod("disable");
|
|
255
|
+
setFocusedFacades([]);
|
|
256
|
+
updateExtensionState("dynamic-focus", {
|
|
257
|
+
registrationState: "unregistered",
|
|
258
|
+
isEnabled: false,
|
|
259
|
+
focusedFacades: []
|
|
260
|
+
});
|
|
261
|
+
}, [callExtensionMethod, setFocusedFacades, updateExtensionState]);
|
|
262
|
+
return {
|
|
263
|
+
isReady,
|
|
264
|
+
isEnabled,
|
|
265
|
+
focusedFacades,
|
|
266
|
+
updateState,
|
|
267
|
+
getState,
|
|
268
|
+
focus,
|
|
269
|
+
disable,
|
|
270
|
+
enable
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
__name(useDynamicFocus, "useDynamicFocus");
|
|
274
|
+
export {
|
|
275
|
+
useDynamicFocus,
|
|
276
|
+
useDynamicFocusEvent
|
|
277
|
+
};
|
|
278
|
+
//# sourceMappingURL=index-rn.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/rn/use-dynamic-focus.ts", "../../src/rn/use-dynamic-focus-events.ts", "../../../packages/common/Mappedin.Logger.ts", "../../src/logger.ts", "../../src/rn/utils/facade-hydration.ts", "../../src/rn/use-dynamic-focus-registration.ts", "../../src/rn/extension-source.ts"],
|
|
4
|
+
"sourcesContent": ["import { useState, useCallback, useContext, useRef } from 'react';\nimport type { DynamicFocusState } from '../types';\nimport { MappedinContext } from '@mappedin/react-native-sdk';\nimport type { Facade } from '@mappedin/mappedin-js';\nimport { useDynamicFocusEvent } from './use-dynamic-focus-events';\nimport { useDynamicFocusRegistration } from './use-dynamic-focus-registration';\n\n/**\n * Extension state for Dynamic Focus that includes the current focused facades\n */\nexport interface DynamicFocusExtensionState {\n\t/**\n\t * Whether the extension is currently enabled\n\t */\n\tisEnabled: boolean;\n\t/**\n\t * The currently focused facades\n\t */\n\tfocusedFacades: Facade[];\n}\n\n// Dynamic Focus hook return type\nexport interface UseDynamicFocusResult {\n\t/**\n\t * Whether the extension is ready and initialized\n\t */\n\tisReady: boolean;\n\t/**\n\t * Whether the extension is currently enabled\n\t */\n\tisEnabled: boolean;\n\t/**\n\t * The currently focused facades\n\t */\n\tfocusedFacades: Facade[];\n\t/**\n\t * Updates the state of the Dynamic Focus controller.\n\t * @param state - The state to update\n\t * @returns Promise resolving to the updated complete state\n\t * @see {@link DynamicFocus.updateState}\n\t */\n\tupdateState(state: Partial<DynamicFocusState>): Promise<DynamicFocusState>;\n\n\t/**\n\t * Returns the current state of the Dynamic Focus controller.\n\t * @returns Promise resolving to the current state\n\t * @see {@link DynamicFocus.getState}\n\t */\n\tgetState(): Promise<DynamicFocusState>;\n\n\t/**\n\t * Perform a manual visual update of the focused facades, and optionally set the floor.\n\t * @param setFloor - Whether to set the floor. This will default to the current state of setFloorOnFocus.\n\t * @returns Promise that resolves when the focus operation is complete\n\t * @see {@link DynamicFocus.focus}\n\t */\n\tfocus(setFloor?: boolean): Promise<void>;\n\n\t/**\n\t * Disables the Dynamic Focus instance and unsubscribes all MapView event listeners.\n\t * @returns Promise that resolves when disabling is complete\n\t * @see {@link DynamicFocus.disable}\n\t */\n\tdisable(): Promise<void>;\n\n\t/**\n\t * Enables Dynamic Focus with the given options.\n\t * @param options - The options to enable Dynamic Focus with\n\t * @returns Promise that resolves when enabling is complete\n\t * @see {@link DynamicFocus.enable}\n\t */\n\tenable(options?: Partial<DynamicFocusState>): Promise<void>;\n}\n\n/**\n * React hook for using DynamicFocus extension in React Native WebView\n *\n * This hook provides an API for interacting with the DynamicFocus extension:\n * - Uses the shared state system for automatic extension registration\n * - Provides methods for controlling dynamic focus behavior\n * - Handles bridge communication and error management\n *\n * @returns Hook API object with Dynamic Focus methods\n *\n *\n * @example\n * ```tsx\n * import { MapView } from '@mappedin/react-native-sdk';\n * import { useDynamicFocus } from '@mappedin/dynamic-focus/rn';\n *\n * function MyComponent() {\n * const { updateState, isReady } = useDynamicFocus();\n *\n * // Use the Dynamic Focus API\n * React.useEffect(() => {\n * if (isReady) {\n * updateState({ autoFocus: true });\n * }\n * }, [isReady]);\n * }\n * ```\n */\nexport function useDynamicFocus(): UseDynamicFocusResult {\n\tconst context = useContext(MappedinContext);\n\tconst { bridge, extensions, updateExtensionState } = context;\n\n\t// Use registration hook to handle extension registration (no parameters needed)\n\tuseDynamicFocusRegistration();\n\n\t// Get extension state from context - properly typed now, no casting needed!\n\tconst extensionState = extensions['dynamic-focus'];\n\tconst registrationState = extensionState?.registrationState || 'unregistered';\n\tconst isReady = registrationState === 'registered'; // Backward compatibility\n\tconst isEnabled = extensionState?.isEnabled || false;\n\n\t// State for focused facades\n\tconst [focusedFacades, setFocusedFacades] = useState<Facade[]>(extensionState?.focusedFacades || []);\n\n\t// Use ref to maintain stable reference for extensions for memoization\n\tconst extensionsRef = useRef(extensions);\n\n\t// Update ref on each render\n\textensionsRef.current = extensions;\n\n\t// Extension registration is handled by useDynamicFocusRegistration\n\n\t// Listen for focus events to update focused facades\n\tuseDynamicFocusEvent(\n\t\t'focus',\n\t\tuseCallback(\n\t\t\tevent => {\n\t\t\t\tconst newFocusedFacades = event.facades;\n\t\t\t\tsetFocusedFacades(newFocusedFacades);\n\n\t\t\t\t// Update extension state to persist facades - use callback to merge\n\t\t\t\tupdateExtensionState('dynamic-focus', prev => ({\n\t\t\t\t\t...prev,\n\t\t\t\t\tregistrationState,\n\t\t\t\t\tfocusedFacades: newFocusedFacades,\n\t\t\t\t}));\n\t\t\t},\n\t\t\t[registrationState, updateExtensionState],\n\t\t),\n\t);\n\n\t/**\n\t * Helper function to make extension method calls through the bridge\n\t */\n\tconst callExtensionMethod = useCallback(\n\t\tasync <T = any>(method: string, args: any[] = []): Promise<T> => {\n\t\t\tif (!bridge?.isReady) {\n\t\t\t\tthrow new Error('Bridge is not ready. Ensure the WebView is loaded.');\n\t\t\t}\n\n\t\t\treturn (await bridge!.instruct({\n\t\t\t\ttype: 'extension',\n\t\t\t\tpayload: {\n\t\t\t\t\tname: 'dynamic-focus',\n\t\t\t\t\tmethod,\n\t\t\t\t\targs,\n\t\t\t\t},\n\t\t\t})) as T;\n\t\t},\n\t\t[bridge],\n\t);\n\n\t// Memoize the enable method to prevent unnecessary re-renders\n\tconst enable = useCallback(\n\t\tasync (options?: Partial<DynamicFocusState>): Promise<void> => {\n\t\t\t// Get current isEnabled state from context using ref to avoid dependency\n\t\t\tconst currentExtensionState = extensionsRef.current?.['dynamic-focus'];\n\t\t\tconst currentIsEnabled = currentExtensionState?.isEnabled || false;\n\n\t\t\t// Check if already enabled to avoid unnecessary calls\n\t\t\tif (currentIsEnabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait callExtensionMethod<void>('enable', [options]);\n\n\t\t\t// Update extension state to reflect enabled status - use callback to merge\n\t\t\tupdateExtensionState('dynamic-focus', prev => ({ ...prev, isEnabled: true }));\n\t\t},\n\t\t[callExtensionMethod, updateExtensionState],\n\t);\n\n\t// Memoize the updateState method to prevent unnecessary re-renders\n\tconst updateState = useCallback(\n\t\tasync (state: Partial<DynamicFocusState>): Promise<DynamicFocusState> => {\n\t\t\treturn await callExtensionMethod<DynamicFocusState>('updateState', [state]);\n\t\t},\n\t\t[callExtensionMethod],\n\t);\n\n\t// Memoize the getState method to prevent unnecessary re-renders\n\tconst getState = useCallback(async (): Promise<DynamicFocusState> => {\n\t\treturn await callExtensionMethod<DynamicFocusState>('getState');\n\t}, [callExtensionMethod]);\n\n\t// Memoize the focus method to prevent unnecessary re-renders\n\tconst focus = useCallback(\n\t\tasync (setFloor?: boolean): Promise<void> => {\n\t\t\treturn await callExtensionMethod<void>('focus', [setFloor]);\n\t\t},\n\t\t[callExtensionMethod],\n\t);\n\n\t// Memoize the disable method to prevent unnecessary re-renders\n\tconst disable = useCallback(async (): Promise<void> => {\n\t\tawait callExtensionMethod<void>('disable');\n\t\t// Set extension state to idle after disabling and clear facades\n\t\tsetFocusedFacades([]);\n\t\t// Reset all state after disabling - replace entire state in this case\n\t\tupdateExtensionState('dynamic-focus', {\n\t\t\tregistrationState: 'unregistered',\n\t\t\tisEnabled: false,\n\t\t\tfocusedFacades: [],\n\t\t});\n\t}, [callExtensionMethod, setFocusedFacades, updateExtensionState]);\n\n\t// Return hook API with Dynamic Focus methods\n\treturn {\n\t\tisReady,\n\t\tisEnabled,\n\t\tfocusedFacades,\n\t\tupdateState,\n\t\tgetState,\n\t\tfocus,\n\t\tdisable,\n\t\tenable,\n\t};\n}\n", "import { useCallback, useContext } from 'react';\nimport { Logger } from '../logger';\nimport type { DynamicFocusEvents, DynamicFocusEventPayload } from '../types';\nimport {\n\tMappedinContext,\n\tuseEventCallback,\n\tcreateEventSetupScript,\n\tcreateEventCleanupScript,\n} from '@mappedin/react-native-sdk';\nimport { hydrateFacades } from './utils/facade-hydration';\nimport { useDynamicFocusRegistration } from './use-dynamic-focus-registration';\n\n/**\n * React hook for listening to DynamicFocus extension events in React Native WebView\n * This hook also auto registers the dynamic focus extension if it is not already registered\n *\n * @param event - The dynamic focus event type to listen for\n * @param callback - The callback function to call when the event is triggered\n *\n * @example\n * ```tsx\n * import { useDynamicFocusEvent } from '@mappedin/dynamic-focus/rn';\n *\n * function MyComponent() {\n * useDynamicFocusEvent('focus', (event) => {\n * console.log('Focus event:', event);\n * });\n * }\n * ```\n */\nexport function useDynamicFocusEvent<T extends keyof DynamicFocusEvents>(\n\tevent: T,\n\tcallback: (payload: DynamicFocusEventPayload<T>) => void,\n): void {\n\t// Parameter validation\n\tif (typeof event !== 'string' || event.length === 0) {\n\t\tthrow new Error('Event parameter must be a non-empty string');\n\t}\n\n\tif (typeof callback !== 'function') {\n\t\tthrow new Error('Callback parameter must be a function');\n\t}\n\n\tconst context = useContext(MappedinContext);\n\tconst { extensions, mapData } = context;\n\tif (!mapData) {\n\t\tthrow new Error('Map data is not available');\n\t}\n\n\t// Use registration hook to ensure extension is registered\n\tuseDynamicFocusRegistration();\n\n\t// Get extension readiness from context only\n\tconst extensionRegistrationState = extensions?.['dynamic-focus']?.registrationState || 'unregistered';\n\tconst extensionIsReady = extensionRegistrationState === 'registered';\n\n\t// Create a callback wrapper that handles payload processing and facade hydration\n\tconst processedCallback = useCallback(\n\t\t(payload: DynamicFocusEventPayload<T>) => {\n\t\t\ttry {\n\t\t\t\t// Hydrate facades for focus events before calling user callback\n\t\t\t\tlet processedPayload = payload;\n\t\t\t\tif (event === 'focus' && (payload as any).facades) {\n\t\t\t\t\tprocessedPayload = {\n\t\t\t\t\t\t...payload,\n\t\t\t\t\t\tfacades: hydrateFacades(mapData, (payload as any).facades),\n\t\t\t\t\t} as DynamicFocusEventPayload<T>;\n\t\t\t\t}\n\n\t\t\t\tcallback(processedPayload);\n\t\t\t} catch (error) {\n\t\t\t\tLogger.error(`Error in dynamic focus event callback for ${String(event)}:`, error);\n\t\t\t}\n\t\t},\n\t\t[event, mapData, callback],\n\t);\n\n\t// Use the shared event callback hook with dynamic focus specific setup/cleanup\n\tuseEventCallback({\n\t\teventKey: `dynamic-focus:${String(event)}`,\n\t\tcallback: processedCallback,\n\t\tsetupScript: () =>\n\t\t\tcreateEventSetupScript({\n\t\t\t\textensionName: 'dynamic-focus',\n\t\t\t\teventName: String(event),\n\t\t\t}),\n\t\tcleanupScript: () =>\n\t\t\tcreateEventCleanupScript({\n\t\t\t\textensionName: 'dynamic-focus',\n\t\t\t\teventName: String(event),\n\t\t\t}),\n\t\tshouldInject: extensionIsReady,\n\t});\n}\n", "/* eslint-disable no-console*/\nexport const MI_DEBUG_KEY = 'mi-debug';\nexport const MI_ERROR_LABEL = '[MappedinJS]';\n\nexport enum E_SDK_LOG_LEVEL {\n\tLOG,\n\tWARN,\n\tERROR,\n\tSILENT,\n}\n\nexport function createLogger(name = '', { prefix = MI_ERROR_LABEL } = {}) {\n\tconst label = `${prefix}${name ? `-${name}` : ''}`;\n\n\tconst rnDebug = (type: 'log' | 'warn' | 'error', args: any[]) => {\n\t\tif (typeof window !== 'undefined' && (window as any).rnDebug) {\n\t\t\tconst processed = args.map(arg => {\n\t\t\t\tif (arg instanceof Error && arg.stack) {\n\t\t\t\t\treturn `${arg.message}\\n${arg.stack}`;\n\t\t\t\t}\n\n\t\t\t\treturn arg;\n\t\t\t});\n\t\t\t(window as any).rnDebug(`${name} ${type}: ${processed.join(' ')}`);\n\t\t}\n\t};\n\n\treturn {\n\t\tlogState: process.env.NODE_ENV === 'test' ? E_SDK_LOG_LEVEL.SILENT : E_SDK_LOG_LEVEL.LOG,\n\n\t\tlog(...args: any[]) {\n\t\t\tif (this.logState <= E_SDK_LOG_LEVEL.LOG) {\n\t\t\t\tconsole.log(label, ...args);\n\t\t\t\trnDebug('log', args);\n\t\t\t}\n\t\t},\n\n\t\twarn(...args: any[]) {\n\t\t\tif (this.logState <= E_SDK_LOG_LEVEL.WARN) {\n\t\t\t\tconsole.warn(label, ...args);\n\t\t\t\trnDebug('warn', args);\n\t\t\t}\n\t\t},\n\n\t\terror(...args: any[]) {\n\t\t\tif (this.logState <= E_SDK_LOG_LEVEL.ERROR) {\n\t\t\t\tconsole.error(label, ...args);\n\n\t\t\t\trnDebug('error', args);\n\t\t\t}\n\t\t},\n\n\t\t// It's a bit tricky to prepend [MappedinJs] to assert and time because of how the output is structured in the console, so it is left out for simplicity\n\t\tassert(...args: any[]) {\n\t\t\tconsole.assert(...args);\n\t\t},\n\n\t\ttime(label: string) {\n\t\t\tconsole.time(label);\n\t\t},\n\n\t\ttimeEnd(label: string) {\n\t\t\tconsole.timeEnd(label);\n\t\t},\n\t\tsetLevel(level: E_SDK_LOG_LEVEL) {\n\t\t\tif (E_SDK_LOG_LEVEL.LOG <= level && level <= E_SDK_LOG_LEVEL.SILENT) {\n\t\t\t\tthis.logState = level;\n\t\t\t}\n\t\t},\n\t};\n}\n\nconst Logger = createLogger();\nexport function setLoggerLevel(level: E_SDK_LOG_LEVEL) {\n\tif (E_SDK_LOG_LEVEL.LOG <= level && level <= E_SDK_LOG_LEVEL.SILENT) {\n\t\tLogger.logState = level;\n\t}\n}\n\nexport default Logger;\n", "import { createLogger } from '../../packages/common/Mappedin.Logger';\n\nexport const Logger = createLogger('', { prefix: '[DynamicFocus]' });\n", "import type { MapData, Facade } from '@mappedin/mappedin-js';\n\n/**\n * Hydrates facade data from JSON objects to actual Facade instances\n * @param mapData - The MapData instance containing facade data\n * @param facades - Array of facade data (JSON objects or Facade instances)\n * @returns Array of hydrated Facade instances\n */\nexport function hydrateFacades(mapData: MapData, facades: Facade['toJSON'][]): Facade[] {\n\treturn facades.map((facadeData: any) => {\n\t\t// Try to hydrate the facade using mapData\n\t\tif (facadeData && facadeData.id) {\n\t\t\tconst hydratedFacade = mapData.getById('facade', facadeData.id);\n\t\t\treturn hydratedFacade || facadeData; // Fallback to original data if hydration fails\n\t\t}\n\t\treturn facadeData;\n\t});\n}\n", "import { useRegisterExtension } from '@mappedin/react-native-sdk';\nimport { Logger } from '../logger';\nimport { getRegistrationScript } from './extension-source';\n\n/**\n * Registration state for extension registration lifecycle\n */\nexport type RegistrationState = 'unregistered' | 'registering' | 'registered';\n\n/**\n * React hook for registering the DynamicFocus extension with the WebView bridge\n *\n * This hook handles the complete extension registration lifecycle:\n * - Monitors bridge readiness state\n * - Checks if extension is already registered to avoid duplicates\n * - Injects registration script when needed\n * - Updates extension state in shared context\n * - Handles all registration errors gracefully\n *\n * Multiple hooks can use this safely - the registration logic is idempotent\n * and will only register the extension once per bridge lifecycle.\n *\n * @returns void - Hook performs registration as side effect only\n *\n * @example\n * ```tsx\n * function MyExtensionHook() {\n * useDynamicFocusRegistration(); // Handles registration automatically\n *\n * // Read isReady from context extensions\n * const { extensions } = useContext(MappedinContext);\n * const isReady = extensions?.['dynamic-focus']?.isReady || false;\n * }\n * ```\n */\nexport function useDynamicFocusRegistration(): void {\n\tuseRegisterExtension({\n\t\textensionName: 'dynamic-focus',\n\t\tgetRegistrationScript,\n\t\tlogger: Logger,\n\t});\n}\n", "// Extension source placeholder - replaced at build time\nconst EXTENSION_SOURCE_PLACEHOLDER = `INJECT_SCRIPT_HERE`;\n\n/**\n * Returns the extension registration script\n * This function's return value is replaced at build time with the actual IIFE content\n * @returns JavaScript code string for extension registration\n */\nexport function getRegistrationScript(): string {\n\treturn EXTENSION_SOURCE_PLACEHOLDER;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["useCallback", "useContext", "MappedinContext", "label", "Logger", "Logger", "Logger", "useContext", "MappedinContext", "useCallback"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const Logger: {
|
|
2
|
+
logState: import("../../packages/common/Mappedin.Logger").E_SDK_LOG_LEVEL;
|
|
3
|
+
log(...args: any[]): void;
|
|
4
|
+
warn(...args: any[]): void;
|
|
5
|
+
error(...args: any[]): void;
|
|
6
|
+
assert(...args: any[]): void;
|
|
7
|
+
time(label: string): void;
|
|
8
|
+
timeEnd(label: string): void;
|
|
9
|
+
setLevel(level: import("../../packages/common/Mappedin.Logger").E_SDK_LOG_LEVEL): void;
|
|
10
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BaseExtensionState } from '@mappedin/react-native-sdk';
|
|
2
|
+
import type { DynamicFocusExtensionState } from './use-dynamic-focus';
|
|
3
|
+
/**
|
|
4
|
+
* Module augmentation to register the dynamic-focus extension type
|
|
5
|
+
* with the React Native SDK's extension registry.
|
|
6
|
+
*
|
|
7
|
+
* This allows TypeScript to properly type the extension state
|
|
8
|
+
* when accessed from the MappedinContext without type casting.
|
|
9
|
+
*/
|
|
10
|
+
declare module '@mappedin/react-native-sdk' {
|
|
11
|
+
interface ExtensionRegistry {
|
|
12
|
+
'dynamic-focus': DynamicFocusExtensionState & BaseExtensionState;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DynamicFocusEvents, DynamicFocusEventPayload } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* React hook for listening to DynamicFocus extension events in React Native WebView
|
|
4
|
+
* This hook also auto registers the dynamic focus extension if it is not already registered
|
|
5
|
+
*
|
|
6
|
+
* @param event - The dynamic focus event type to listen for
|
|
7
|
+
* @param callback - The callback function to call when the event is triggered
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* import { useDynamicFocusEvent } from '@mappedin/dynamic-focus/rn';
|
|
12
|
+
*
|
|
13
|
+
* function MyComponent() {
|
|
14
|
+
* useDynamicFocusEvent('focus', (event) => {
|
|
15
|
+
* console.log('Focus event:', event);
|
|
16
|
+
* });
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function useDynamicFocusEvent<T extends keyof DynamicFocusEvents>(event: T, callback: (payload: DynamicFocusEventPayload<T>) => void): void;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registration state for extension registration lifecycle
|
|
3
|
+
*/
|
|
4
|
+
export type RegistrationState = 'unregistered' | 'registering' | 'registered';
|
|
5
|
+
/**
|
|
6
|
+
* React hook for registering the DynamicFocus extension with the WebView bridge
|
|
7
|
+
*
|
|
8
|
+
* This hook handles the complete extension registration lifecycle:
|
|
9
|
+
* - Monitors bridge readiness state
|
|
10
|
+
* - Checks if extension is already registered to avoid duplicates
|
|
11
|
+
* - Injects registration script when needed
|
|
12
|
+
* - Updates extension state in shared context
|
|
13
|
+
* - Handles all registration errors gracefully
|
|
14
|
+
*
|
|
15
|
+
* Multiple hooks can use this safely - the registration logic is idempotent
|
|
16
|
+
* and will only register the extension once per bridge lifecycle.
|
|
17
|
+
*
|
|
18
|
+
* @returns void - Hook performs registration as side effect only
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* function MyExtensionHook() {
|
|
23
|
+
* useDynamicFocusRegistration(); // Handles registration automatically
|
|
24
|
+
*
|
|
25
|
+
* // Read isReady from context extensions
|
|
26
|
+
* const { extensions } = useContext(MappedinContext);
|
|
27
|
+
* const isReady = extensions?.['dynamic-focus']?.isReady || false;
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function useDynamicFocusRegistration(): void;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { DynamicFocusState } from '../types';
|
|
2
|
+
import type { Facade } from '@mappedin/mappedin-js';
|
|
3
|
+
/**
|
|
4
|
+
* Extension state for Dynamic Focus that includes the current focused facades
|
|
5
|
+
*/
|
|
6
|
+
export interface DynamicFocusExtensionState {
|
|
7
|
+
/**
|
|
8
|
+
* Whether the extension is currently enabled
|
|
9
|
+
*/
|
|
10
|
+
isEnabled: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* The currently focused facades
|
|
13
|
+
*/
|
|
14
|
+
focusedFacades: Facade[];
|
|
15
|
+
}
|
|
16
|
+
export interface UseDynamicFocusResult {
|
|
17
|
+
/**
|
|
18
|
+
* Whether the extension is ready and initialized
|
|
19
|
+
*/
|
|
20
|
+
isReady: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Whether the extension is currently enabled
|
|
23
|
+
*/
|
|
24
|
+
isEnabled: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The currently focused facades
|
|
27
|
+
*/
|
|
28
|
+
focusedFacades: Facade[];
|
|
29
|
+
/**
|
|
30
|
+
* Updates the state of the Dynamic Focus controller.
|
|
31
|
+
* @param state - The state to update
|
|
32
|
+
* @returns Promise resolving to the updated complete state
|
|
33
|
+
* @see {@link DynamicFocus.updateState}
|
|
34
|
+
*/
|
|
35
|
+
updateState(state: Partial<DynamicFocusState>): Promise<DynamicFocusState>;
|
|
36
|
+
/**
|
|
37
|
+
* Returns the current state of the Dynamic Focus controller.
|
|
38
|
+
* @returns Promise resolving to the current state
|
|
39
|
+
* @see {@link DynamicFocus.getState}
|
|
40
|
+
*/
|
|
41
|
+
getState(): Promise<DynamicFocusState>;
|
|
42
|
+
/**
|
|
43
|
+
* Perform a manual visual update of the focused facades, and optionally set the floor.
|
|
44
|
+
* @param setFloor - Whether to set the floor. This will default to the current state of setFloorOnFocus.
|
|
45
|
+
* @returns Promise that resolves when the focus operation is complete
|
|
46
|
+
* @see {@link DynamicFocus.focus}
|
|
47
|
+
*/
|
|
48
|
+
focus(setFloor?: boolean): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Disables the Dynamic Focus instance and unsubscribes all MapView event listeners.
|
|
51
|
+
* @returns Promise that resolves when disabling is complete
|
|
52
|
+
* @see {@link DynamicFocus.disable}
|
|
53
|
+
*/
|
|
54
|
+
disable(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Enables Dynamic Focus with the given options.
|
|
57
|
+
* @param options - The options to enable Dynamic Focus with
|
|
58
|
+
* @returns Promise that resolves when enabling is complete
|
|
59
|
+
* @see {@link DynamicFocus.enable}
|
|
60
|
+
*/
|
|
61
|
+
enable(options?: Partial<DynamicFocusState>): Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* React hook for using DynamicFocus extension in React Native WebView
|
|
65
|
+
*
|
|
66
|
+
* This hook provides an API for interacting with the DynamicFocus extension:
|
|
67
|
+
* - Uses the shared state system for automatic extension registration
|
|
68
|
+
* - Provides methods for controlling dynamic focus behavior
|
|
69
|
+
* - Handles bridge communication and error management
|
|
70
|
+
*
|
|
71
|
+
* @returns Hook API object with Dynamic Focus methods
|
|
72
|
+
*
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```tsx
|
|
76
|
+
* import { MapView } from '@mappedin/react-native-sdk';
|
|
77
|
+
* import { useDynamicFocus } from '@mappedin/dynamic-focus/rn';
|
|
78
|
+
*
|
|
79
|
+
* function MyComponent() {
|
|
80
|
+
* const { updateState, isReady } = useDynamicFocus();
|
|
81
|
+
*
|
|
82
|
+
* // Use the Dynamic Focus API
|
|
83
|
+
* React.useEffect(() => {
|
|
84
|
+
* if (isReady) {
|
|
85
|
+
* updateState({ autoFocus: true });
|
|
86
|
+
* }
|
|
87
|
+
* }, [isReady]);
|
|
88
|
+
* }
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export declare function useDynamicFocus(): UseDynamicFocusResult;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { MapData, Facade } from '@mappedin/mappedin-js';
|
|
2
|
+
/**
|
|
3
|
+
* Hydrates facade data from JSON objects to actual Facade instances
|
|
4
|
+
* @param mapData - The MapData instance containing facade data
|
|
5
|
+
* @param facades - Array of facade data (JSON objects or Facade instances)
|
|
6
|
+
* @returns Array of hydrated Facade instances
|
|
7
|
+
*/
|
|
8
|
+
export declare function hydrateFacades(mapData: MapData, facades: Facade['toJSON'][]): Facade[];
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { Facade, TFacadeState, VisibilityState as BuildingFloorStackState } from '@mappedin/mappedin-js';
|
|
2
|
+
export type DynamicFocusAnimationOptions = {
|
|
3
|
+
/**
|
|
4
|
+
* The duration of the animation in milliseconds.
|
|
5
|
+
* @default 150
|
|
6
|
+
*/
|
|
7
|
+
duration: number;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Array of valid dynamic focus modes for runtime validation.
|
|
11
|
+
*/
|
|
12
|
+
export declare const DYNAMIC_FOCUS_MODES: readonly ["default-floor", "lock-elevation", "nearest-elevation"];
|
|
13
|
+
/**
|
|
14
|
+
* The mode which determines the indoor floor to reveal when the camera focuses on a facade.
|
|
15
|
+
* - 'default-floor' - Show the default floor of the floor stack.
|
|
16
|
+
* - 'lock-elevation' - Show the floor at the current elevation, if possible.
|
|
17
|
+
* When a floor stack does not have a floor at the current elevation, no indoor floor will be shown.
|
|
18
|
+
* - 'nearest-elevation' - Show the floor at the current elevation, if possible.
|
|
19
|
+
* When a floor stack does not have a floor at the current elevation, show the indoor floor at the nearest lower elevation.
|
|
20
|
+
*/
|
|
21
|
+
export type DynamicFocusMode = (typeof DYNAMIC_FOCUS_MODES)[number];
|
|
22
|
+
/**
|
|
23
|
+
* State of the Dynamic Focus controller.
|
|
24
|
+
*/
|
|
25
|
+
export type DynamicFocusState = {
|
|
26
|
+
/**
|
|
27
|
+
* Whether to automatically focus on the outdoors when the camera moves in range.
|
|
28
|
+
* @default true
|
|
29
|
+
*/
|
|
30
|
+
autoFocus: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* The zoom level at which the camera will fade out the facades and fade in the indoor floors.
|
|
33
|
+
* @default 18
|
|
34
|
+
*/
|
|
35
|
+
indoorZoomThreshold: number;
|
|
36
|
+
/**
|
|
37
|
+
* The zoom level at which the camera will fade in the facades and fade out the indoor floors.
|
|
38
|
+
* @default 17
|
|
39
|
+
*/
|
|
40
|
+
outdoorZoomThreshold: number;
|
|
41
|
+
/**
|
|
42
|
+
* Whether to set the floor to the outdoors when the camera moves in range.
|
|
43
|
+
* @default true
|
|
44
|
+
*/
|
|
45
|
+
setFloorOnFocus: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Options for the animation when fading out the facade to reveal the interior.
|
|
48
|
+
*/
|
|
49
|
+
indoorAnimationOptions: DynamicFocusAnimationOptions;
|
|
50
|
+
/**
|
|
51
|
+
* Options for the animation when fading in the facade to hide the interior.
|
|
52
|
+
*/
|
|
53
|
+
outdoorAnimationOptions: DynamicFocusAnimationOptions;
|
|
54
|
+
/**
|
|
55
|
+
* The mode of the Dynamic Focus controller.
|
|
56
|
+
* @default 'default-floor'
|
|
57
|
+
*/
|
|
58
|
+
mode: DynamicFocusMode;
|
|
59
|
+
/**
|
|
60
|
+
* Whether to automatically adjust facade heights to align with floor boundaries in multi-floor buildings.
|
|
61
|
+
* @default false
|
|
62
|
+
*/
|
|
63
|
+
autoAdjustFacadeHeights: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Whether to preload the geometry of the initial floors in each building. Improves performance when rendering the building for the first time.
|
|
66
|
+
* @default true
|
|
67
|
+
*/
|
|
68
|
+
preloadFloors: boolean;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Internal events emitted for updating React state.
|
|
72
|
+
*/
|
|
73
|
+
export type InternalDynamicFocusEvents = {
|
|
74
|
+
'state-change': undefined;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Events emitted by the Dynamic Focus controller.
|
|
78
|
+
*/
|
|
79
|
+
export type DynamicFocusEvents = {
|
|
80
|
+
/**
|
|
81
|
+
* Emitted when the Dynamic Focus controller triggers a focus update either from a camera change or manually calling `focus()`.
|
|
82
|
+
*/
|
|
83
|
+
focus: {
|
|
84
|
+
facades: Facade[];
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
export type DynamicFocusEventPayload<EventName extends keyof DynamicFocusEvents> = DynamicFocusEvents[EventName] extends {
|
|
88
|
+
data: null;
|
|
89
|
+
} ? DynamicFocusEvents[EventName]['data'] : DynamicFocusEvents[EventName];
|
|
90
|
+
export type BuildingFacadeState = {
|
|
91
|
+
facade: Facade;
|
|
92
|
+
state: TFacadeState;
|
|
93
|
+
};
|
|
94
|
+
export type BuildingState = BuildingFloorStackState & {
|
|
95
|
+
facadeState: BuildingFacadeState;
|
|
96
|
+
};
|
|
97
|
+
export type ZoomState = 'in-range' | 'out-of-range' | 'transition';
|
|
98
|
+
export type ViewState = 'indoor' | 'outdoor' | 'transition';
|
|
99
|
+
export type BuildingAnimation = 'indoor' | 'outdoor' | 'none';
|
|
100
|
+
export type { BuildingFloorStackState };
|