@django-bridge/react 0.5.0 → 0.6.0-rc.1

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/dist/index.d.ts CHANGED
@@ -1,14 +1,31 @@
1
- import React, { FunctionComponent, ReactNode, ReactElement, Context } from 'react';
2
- import Telepath from 'telepath-unpack';
1
+ import React, { ReactNode, ReactElement, FunctionComponent, Context } from 'react';
3
2
 
4
- declare class Config$1 {
5
- views: Map<string, FunctionComponent>;
3
+ interface PackedValuesById {
4
+ [id: string]: any;
5
+ }
6
+ interface ValuesById {
7
+ [id: string]: any;
8
+ }
9
+ interface ConstructorRegistry {
10
+ [name: string]: new (...args: any[]) => any;
11
+ }
12
+ declare class Telepath {
13
+ constructors: ConstructorRegistry;
14
+ constructor();
15
+ register(name: string, constructor: new (...args: any[]) => any): void;
16
+ unpack(objData: any): any;
17
+ scanForIds(objData: any, packedValuesById: PackedValuesById): void;
18
+ unpackWithRefs(objData: any, packedValuesById: PackedValuesById, valuesById: ValuesById): any;
19
+ }
20
+
21
+ declare class Config$1<V> {
22
+ views: Map<string, V>;
6
23
  telepathRegistry: Telepath;
7
24
  constructor();
8
- addView: <P>(name: string, component: FunctionComponent<P>) => Config$1;
25
+ addView: <P>(name: string, component: V) => Config$1<V>;
9
26
  addAdapter: <Cls>(name: string, ctor: {
10
27
  new (...args: any[]): Cls;
11
- }) => Config$1;
28
+ }) => Config$1<V>;
12
29
  unpack: (data: Record<string, unknown>) => Record<string, unknown>;
13
30
  }
14
31
 
@@ -54,6 +71,7 @@ interface NetworkErrorResponse {
54
71
  }
55
72
  type DjangoBridgeResponse = ReloadResponse | RedirectResponse | RenderResponse | CloseOverlayResponse | ServerErrorResponse | NetworkErrorResponse;
56
73
 
74
+ type ShouldReloadCallback = (newPath: string, newProps: Record<string, unknown>) => boolean;
57
75
  interface Frame<Props = Record<string, unknown>> {
58
76
  id: number;
59
77
  path: string;
@@ -61,7 +79,7 @@ interface Frame<Props = Record<string, unknown>> {
61
79
  view: string;
62
80
  props: Props;
63
81
  context: Record<string, unknown>;
64
- shouldReloadCallback?: (newPath: string, newProps: Props) => boolean;
82
+ shouldReloadCallback?: ShouldReloadCallback;
65
83
  }
66
84
 
67
85
  interface NavigateOptions {
@@ -91,6 +109,7 @@ interface Navigation {
91
109
  openOverlay: (path: string, render: (content: ReactNode) => ReactNode, options?: OpenOverlayOptions) => void;
92
110
  refreshProps: () => Promise<void>;
93
111
  isNavigating: boolean;
112
+ setShouldReloadCallback: (callback: ShouldReloadCallback) => void;
94
113
  }
95
114
  declare const NavigationContext: React.Context<Navigation>;
96
115
  declare const FormWidgetChangeNotificationContext: React.Context<() => void>;
@@ -110,7 +129,7 @@ declare const Link: React.ForwardRefExoticComponent<React.AnchorHTMLAttributes<H
110
129
  skipDirtyFormCheck?: boolean;
111
130
  } & React.RefAttributes<HTMLAnchorElement>>;
112
131
 
113
- declare class Config extends Config$1 {
132
+ declare class Config extends Config$1<FunctionComponent> {
114
133
  contextProviders: Map<string, Context<unknown>>;
115
134
  constructor();
116
135
  addContextProvider: <C>(name: string, context: Context<C>) => Config;
@@ -140,6 +159,9 @@ interface DirtyForm {
140
159
  declare const DirtyFormContext: React.Context<DirtyForm>;
141
160
  declare function DirtyFormMarker(): React.ReactElement;
142
161
 
162
+ declare function useAutoRefresh(enabled: boolean, interval: number): void;
163
+ declare function useShouldReloadCallback(callback: ShouldReloadCallback, deps: React.DependencyList): void;
164
+
143
165
  interface NavigationController {
144
166
  parent: NavigationController | null;
145
167
  currentFrame: Frame;
@@ -159,4 +181,4 @@ interface AppProps {
159
181
  }
160
182
  declare function App({ config, initialResponse }: AppProps): ReactElement;
161
183
 
162
- export { App, type AppProps, BuildLinkElement, Config, type DirtyForm, DirtyFormContext, DirtyFormMarker, Form, FormSubmissionStatus, FormWidgetChangeNotificationContext, type Frame, Link, type Message, MessagesContext, type Metadata, type Navigation, NavigationContext, type NavigationController, OverlayContext, RenderFrame, type DjangoBridgeResponse as Response, buildLinkElement };
184
+ export { App, type AppProps, BuildLinkElement, Config, type DirtyForm, DirtyFormContext, DirtyFormMarker, Form, FormSubmissionStatus, FormWidgetChangeNotificationContext, type Frame, Link, type Message, MessagesContext, type Metadata, type Navigation, NavigationContext, type NavigationController, OverlayContext, RenderFrame, type DjangoBridgeResponse as Response, buildLinkElement, useAutoRefresh, useShouldReloadCallback };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var we=Object.create;var V=Object.defineProperty;var Pe=Object.getOwnPropertyDescriptor;var be=Object.getOwnPropertyNames;var Fe=Object.getPrototypeOf,Me=Object.prototype.hasOwnProperty;var De=(e,o)=>{for(var t in o)V(e,t,{get:o[t],enumerable:!0})},fe=(e,o,t,n)=>{if(o&&typeof o=="object"||typeof o=="function")for(let r of be(o))!Me.call(e,r)&&r!==t&&V(e,r,{get:()=>o[r],enumerable:!(n=Pe(o,r))||n.enumerable});return e};var D=(e,o,t)=>(t=e!=null?we(Fe(e)):{},fe(o||!e||!e.__esModule?V(t,"default",{value:e,enumerable:!0}):t,e)),ke=e=>fe(V({},"__esModule",{value:!0}),e);var Be={};De(Be,{App:()=>Oe,BuildLinkElement:()=>se,Config:()=>X,DirtyFormContext:()=>F,DirtyFormMarker:()=>J,Form:()=>le,FormSubmissionStatus:()=>Q,FormWidgetChangeNotificationContext:()=>K,Link:()=>Ce,MessagesContext:()=>L,NavigationContext:()=>k,OverlayContext:()=>z,RenderFrame:()=>Z,buildLinkElement:()=>ie});module.exports=ke(Be);var M=D(require("react"));var ve=D(require("telepath-unpack")),q=class{views;telepathRegistry;constructor(){this.views=new Map,this.telepathRegistry=new ve.default,this.addAdapter("Date",Date)}addView=(o,t)=>(this.views.set(o,t),this);addAdapter=(o,t)=>(this.telepathRegistry.register(o,t),this);unpack=o=>this.telepathRegistry.unpack(o)};async function O(e,o){let t,n={"X-Requested-With":"DjangoBridge"};o&&(n["X-DjangoBridge-Overlay"]="true");try{t=await fetch(e,{headers:n})}catch{return{action:"network-error"}}return t.status===500?{action:"server-error"}:t.headers.get("X-DjangoBridge-Action")?t.json():{action:"reload"}}async function te(e,o,t){let n,r={"X-Requested-With":"DjangoBridge"};t&&(r["X-DjangoBridge-Overlay"]="true");try{n=await fetch(e,{method:"post",headers:r,body:o})}catch{return{action:"network-error"}}return n.status===500?{action:"server-error"}:n.headers.get("X-DjangoBridge-Action")?n.json():{action:"reload"}}var u=require("react");var Y=1;function $(e,o,t,n,r={}){Y+=1;let[i,p]=(0,u.useState)({id:Y,path:n,metadata:{title:"Loading"},view:"loading",props:{},context:{}}),[v,c]=(0,u.useState)(!1),g=(0,u.useCallback)((s,R,P,b,N,S,U=!0,ye=!0)=>{let de=i.id,me=P!==i.view||ye;if(me&&(Y+=1,de=Y),!e&&(document.title=R.title,U)){let pe=0,xe=window.scrollY,ge=window.history?.state;ge?.prevPath===s&&(pe=ge.prevScrollPosition??0),window.history.pushState({prevPath:window.location.pathname,prevScrollPosition:xe},"",s),window.scrollTo(0,pe)}let ue={id:de,path:s,metadata:R,view:P,props:b,context:N};p(ue),r.onNavigation&&r.onNavigation(ue,me,S)},[r,i.id,i.view,e]),[d,C]=(0,u.useState)(null),h=(0,u.useCallback)((s,R,P=!0,b=!1)=>{if(s.action==="reload")if(!e)window.location.href=R;else return e.handleResponse(s,R);else{if(s.action==="redirect")return C(s.path),Promise.resolve();if(s.action==="render"){if(e&&!s.overlay)return e.handleResponse(s,R);let N=o(s.props),S=o(s.context),U=!b;U&&s.view===i.view&&i.shouldReloadCallback&&(U=i.shouldReloadCallback(R,N)),g(R,s.metadata,s.view,N,S,s.messages,P,U)}else if(s.action==="close-overlay")r.onOverlayClose&&r.onOverlayClose(s.messages);else{if(s.action==="server-error")return r.onServerError&&r.onServerError("server"),Promise.reject();if(s.action==="network-error")return r.onServerError&&r.onServerError("network"),Promise.reject()}}return Promise.resolve()},[r,i,e,g,o]),f=(0,u.useRef)(1),l=(0,u.useRef)(1),y=(0,u.useRef)(!1),a=(0,u.useCallback)(async(s,R,P,b=!1)=>{f.current+=1;let N=f.current;y.current=!0;let S=await s();N<l.current||(l.current=N,S!==null&&(await h(S,R,P,b),y.current=!1))},[h]),m=(0,u.useCallback)((s,R=!0)=>{let P=s;if(!s.startsWith("/")){let b=new URL(s);if(b.origin!==window.location.origin)return window.location.href=s,Promise.resolve();P=b.pathname+b.search}return c(!0),a(()=>O(P,!!e),P,R).finally(()=>{c(!1)})},[a,e]),w=(0,u.useCallback)((s,R)=>{s===i.id&&(i.path=R,e||history.replaceState({},"",i.path))},[i,e]),oe=(0,u.useCallback)((s,R)=>a(()=>te(s,R,!!e),s,!0),[a,e]),Re=(0,u.useCallback)(()=>y.current?Promise.resolve():a(()=>O(i.path,!!e),i.path,!1,!0),[i.path,a,e]);return(0,u.useEffect)(()=>{h(t,n,!1)},[]),(0,u.useEffect)(()=>{d&&(C(null),m(d))},[m,d]),{parent:e,currentFrame:i,isLoading:i.view==="loading",handleResponse:h,navigate:m,replacePath:w,submitForm:oe,refreshProps:Re,isNavigating:v,setIsNavigating:c}}var x=D(require("react")),A=require("react/jsx-runtime"),re=x.default.createContext(()=>{}),F=x.default.createContext({isDirty:!1,requestUnload:()=>Promise.resolve(),unloadRequested:!1,unloadBlocked:!1,confirmUnload:()=>{},cancelUnload:()=>{},unloadConfirmed:!1});function ne({handleBrowserUnload:e=!1,children:o}){let[t,n]=x.default.useState(!1),[r,i]=x.default.useState({cb:()=>{}}),[p,v]=x.default.useState(!1),[c,g]=x.default.useState(!1),d=x.default.useRef(null),C=()=>d.current&&!!d.current.querySelector("div.dirty-form-marker")||!1;x.default.useEffect(()=>{if(e&&c){let y="This page has unsaved changes.",a=m=>C()?(m.returnValue=y,y):"";return window.addEventListener("beforeunload",a),()=>{window.removeEventListener("beforeunload",a)}}return()=>{}},[e,c]);let h=x.default.useContext(re),f=x.default.useCallback(()=>{g(C()),h()},[h]),l=x.default.useMemo(()=>({isDirty:c,requestUnload:()=>c&&C()?(n(!0),new Promise(y=>{i({cb:y})})):Promise.resolve(),unloadRequested:t,unloadBlocked:c&&t,confirmUnload:()=>{t&&(v(!0),r.cb(),n(!1),i({cb:()=>{}}))},cancelUnload:()=>{t&&(n(!1),i({cb:()=>{}}))},unloadConfirmed:p}),[c,r,p,t]);return(0,A.jsx)("div",{ref:d,children:(0,A.jsx)(re.Provider,{value:f,children:(0,A.jsx)(F.Provider,{value:l,children:o})})})}function J(){let e=x.default.useContext(re);return x.default.useEffect(e),(0,A.jsx)("div",{className:"dirty-form-marker",style:{display:"none"}})}var ae=D(require("react"));var B=D(require("react")),z=B.default.createContext({overlay:!1,closeRequested:!1,requestClose:()=>{console.error("OverlayContext.requestClose() called from outside an overlay")},onCloseCompleted:()=>{console.error("OverlayContext.onCloseCompleted() called from outside an overlay")}}),k=B.default.createContext({frameId:0,path:"/",props:{},context:{},navigate:()=>(console.error("navigate() called from outside a Django Bridge Browser"),Promise.resolve()),replacePath:()=>{console.error("replacePath() called from outside a Django Bridge Browser")},submitForm:()=>(console.error("submitForm() called from outside a Django Bridge Browser"),Promise.resolve()),openOverlay:()=>{throw console.error("openOverlay() called from outside a Django Bridge Browser"),new Error("Modal cannot be opened here")},refreshProps:()=>(console.error("refreshProps() called from outside a Django Bridge Browser"),Promise.resolve()),isNavigating:!1}),K=B.default.createContext(()=>{}),Q=B.default.createContext(!1),L=B.default.createContext({messages:[],pushMessage:()=>{}});var j=require("react/jsx-runtime");function Ne({config:e,frame:o}){let t=e.views.get(o.view);if(!t)return(0,j.jsxs)("p",{children:["Unknown view '",o.view,"'"]});let n=(0,j.jsx)(t,{...o.props});return e.contextProviders.forEach((r,i)=>{n=(0,j.jsx)(r.Provider,{value:o.context[i],children:n})}),(0,j.jsx)("div",{children:n},o.id)}var Z=Ne;var _=require("react/jsx-runtime");function Ee({config:e,navigationController:o,openOverlay:t}){let{currentFrame:n,navigate:r,replacePath:i,submitForm:p,refreshProps:v,isNavigating:c}=o,{isDirty:g,requestUnload:d,cancelUnload:C}=ae.default.useContext(F),h=ae.default.useMemo(()=>({frameId:n.id,path:n.path,props:n.props,context:n.context,navigate:(f,l={})=>!g||l.skipDirtyFormCheck===!0?(l.skipDirtyFormCheck===!0&&C(),r(f,l.pushState)):d().then(()=>r(f,l.pushState)),replacePath:i,submitForm:p,openOverlay:t,refreshProps:v,isNavigating:c}),[n,i,p,t,g,d,C,r,v,c]);return(0,_.jsx)(k.Provider,{value:h,children:(0,_.jsx)("div",{children:(0,_.jsx)(Z,{config:e,frame:n})},n.id)})}var ee=Ee;var W=D(require("react"));var he=require("react/jsx-runtime");function ie({children:e,href:o,skipDirtyFormCheck:t=!1,...n},{navigate:r},i){return(0,he.jsx)("a",{onClick:v=>{o&&(v.preventDefault(),r(o,{skipDirtyFormCheck:t}))},href:o||"#",ref:i,...n,children:e})}var se=W.default.createContext(ie),Se=W.default.forwardRef((e,o)=>{let t=W.default.useContext(k);return W.default.useContext(se)(e,t,o)}),Ce=Se;var X=class extends q{contextProviders;constructor(){super(),this.contextProviders=new Map}addContextProvider=(o,t)=>(this.contextProviders.set(o,t),this)};var T=D(require("react"));var I=require("react/jsx-runtime");function le({children:e,onSubmit:o,isDirty:t=!1,disableDirtyCheck:n=!1,...r}){let{submitForm:i,navigate:p}=T.default.useContext(k),[v,c]=T.default.useState(!1),[g,d]=T.default.useState(t),{cancelUnload:C}=T.default.useContext(F),h=l=>{if(g&&C(),!(o&&(o(l),l.defaultPrevented))&&l.target instanceof HTMLFormElement){if(v){l.preventDefault();return}let y=new FormData(l.target);if(l.nativeEvent instanceof SubmitEvent&&l.nativeEvent.submitter){let{submitter:a}=l.nativeEvent;(a instanceof HTMLButtonElement||a instanceof HTMLInputElement)&&a.name&&a.value&&y.set(a.name,a.value)}if(l.target.method==="post")l.preventDefault(),c(!0),i(l.target.action,y).catch(()=>c(!1));else if(l.target.method==="get"){l.preventDefault();let a=Array.from(y.entries()).map(w=>`${encodeURIComponent(w[0])}=${encodeURIComponent(w[1])}`).join("&"),m=l.target.action+(l.target.action.indexOf("?")===-1?"?":"&")+a;c(!0),p(m).catch(()=>c(!1))}}},f=T.default.useCallback(()=>{d(!0)},[]);return(0,I.jsxs)("form",{onSubmit:h,...r,children:[g&&!n&&(0,I.jsx)(J,{}),(0,I.jsx)(Q.Provider,{value:v,children:(0,I.jsx)(K.Provider,{value:f,children:e})})]})}var G=D(require("react"));var H=require("react/jsx-runtime");function ce({config:e,initialResponse:o,initialPath:t,parentNavigationContoller:n,render:r,requestClose:i,closeRequested:p,onCloseCompleted:v,onServerError:c}){let{pushMessage:g}=G.default.useContext(L),d=$(n,e.unpack,o,t,{onNavigation:(l,y,a)=>{a.forEach(g)},onOverlayClose:l=>{l.forEach(g),i()},onServerError:c}),C=G.default.useContext(F),h=G.default.useCallback(({skipDirtyFormCheck:l=!1}={})=>{!l&&C.isDirty?C.requestUnload().then(()=>i()):i()},[C,i]),f=G.default.useMemo(()=>({overlay:!0,closeRequested:p,requestClose:h,onCloseCompleted:v}),[p,v,h]);return d.isLoading?(0,H.jsx)(H.Fragment,{}):(0,H.jsx)(z.Provider,{value:f,children:r((0,H.jsx)(ee,{config:e,navigationController:d,openOverlay:()=>{}}))})}var E=require("react/jsx-runtime");function Oe({config:e,initialResponse:o}){let[t,n]=M.default.useReducer((a,m)=>{switch(m.action){case"push":return a.concat([m.message]);case"clear":return[];default:return a}},[]),r=M.default.useCallback(a=>{n({action:"push",message:a})},[n]),i=M.default.useCallback(a=>{a==="server"?r({level:"error",text:"A server error occurred. Please try again later."}):a==="network"&&r({level:"error",text:"A network error occurred. Please check your internet connection or try again later."})},[r]),[p,v]=M.default.useState(null),[c,g]=M.default.useState(!1),d=M.default.useRef(null),C=(a,m,w)=>{m&&(g(!0),d.current=null),m&&n({action:"clear"}),w.forEach(r)},h=window.location.pathname+window.location.search+window.location.hash,f=$(null,e.unpack,o,h,{onNavigation:C,onServerError:i});M.default.useEffect(()=>{let a=document.querySelector(".django-bridge-load");a instanceof HTMLElement&&(a.classList.add("django-bridge-load--hidden"),setTimeout(()=>{a.remove()},200));let m=()=>{f.navigate(document.location.pathname,!1)};return window.addEventListener("popstate",m),()=>{window.removeEventListener("popstate",m)}},[]);let l=async(a,m,{onClose:w}={})=>{f.setIsNavigating(!0);let oe=await O(a,!0);w&&(d.current=w),g(!1),v({render:m,initialResponse:oe,initialPath:a}),f.setIsNavigating(!1)},y=M.default.useMemo(()=>({messages:t,pushMessage:r}),[t,r]);return(0,E.jsx)(ne,{handleBrowserUnload:!0,children:(0,E.jsxs)(L.Provider,{value:y,children:[p&&(0,E.jsx)(ne,{children:(0,E.jsx)(ce,{config:e,initialResponse:p.initialResponse,initialPath:p.initialPath,parentNavigationContoller:f,render:a=>p.render(a),requestClose:()=>g(!0),closeRequested:c,onCloseCompleted:()=>{v(null),g(!1),d.current&&(d.current(),d.current=null)},onServerError:i})}),!f.isLoading&&(0,E.jsx)(ee,{config:e,navigationController:f,openOverlay:(a,m,w)=>void l(a,m,w)})]})})}0&&(module.exports={App,BuildLinkElement,Config,DirtyFormContext,DirtyFormMarker,Form,FormSubmissionStatus,FormWidgetChangeNotificationContext,Link,MessagesContext,NavigationContext,OverlayContext,RenderFrame,buildLinkElement});
1
+ "use strict";var Fe=Object.create;var Y=Object.defineProperty;var be=Object.getOwnPropertyDescriptor;var Me=Object.getOwnPropertyNames;var Ee=Object.getPrototypeOf,Ne=Object.prototype.hasOwnProperty;var Se=(n,e)=>{for(var t in e)Y(n,t,{get:e[t],enumerable:!0})},ye=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of Me(e))!Ne.call(n,o)&&o!==t&&Y(n,o,{get:()=>e[o],enumerable:!(r=be(e,o))||r.enumerable});return n};var N=(n,e,t)=>(t=n!=null?Fe(Ee(n)):{},ye(e||!n||!n.__esModule?Y(t,"default",{value:n,enumerable:!0}):t,n)),Oe=n=>ye(Y({},"__esModule",{value:!0}),n);var _e={};Se(_e,{App:()=>Te,BuildLinkElement:()=>ue,Config:()=>X,DirtyFormContext:()=>b,DirtyFormMarker:()=>z,Form:()=>fe,FormSubmissionStatus:()=>ee,FormWidgetChangeNotificationContext:()=>Z,Link:()=>Ce,MessagesContext:()=>T,NavigationContext:()=>P,OverlayContext:()=>Q,RenderFrame:()=>te,buildLinkElement:()=>de,useAutoRefresh:()=>xe,useShouldReloadCallback:()=>we});module.exports=Oe(_e);var E=N(require("react"));var I=class{constructors;constructor(){this.constructors={}}register(e,t){this.constructors[e]=t}unpack(e){let t={};this.scanForIds(e,t);let r={};return this.unpackWithRefs(e,t,r)}scanForIds(e,t){if(e===null||typeof e!="object")return;if(Array.isArray(e)){e.forEach(o=>this.scanForIds(o,t));return}let r=!1;if("_id"in e&&(r=!0,t[e._id]=e),("_type"in e||"_val"in e||"_ref"in e)&&(r=!0),"_list"in e&&(r=!0,e._list.forEach(o=>this.scanForIds(o,t))),"_args"in e&&(r=!0,e._args.forEach(o=>this.scanForIds(o,t))),"_dict"in e){r=!0;for(let[o,i]of Object.entries(e._dict))this.scanForIds(i,t)}if(!r)for(let[o,i]of Object.entries(e))this.scanForIds(i,t)}unpackWithRefs(e,t,r){if(e===null||typeof e!="object")return e;if(Array.isArray(e))return e.map(i=>this.unpackWithRefs(i,t,r));let o;if("_ref"in e)e._ref in r?o=r[e._ref]:o=this.unpackWithRefs(t[e._ref],t,r);else if("_val"in e)o=e._val;else if("_list"in e)o=e._list.map(i=>this.unpackWithRefs(i,t,r));else if("_dict"in e){o={};for(let[i,c]of Object.entries(e._dict))o[i]=this.unpackWithRefs(c,t,r)}else if("_type"in e){let i=e._type,c=this.constructors[i];if(typeof c!="function")throw new Error(`telepath encountered unknown object type ${i}`);let f=e._args.map(d=>this.unpackWithRefs(d,t,r));o=new c(...f)}else{if("_id"in e)throw new Error("telepath encountered object with _id but no type specified");o={};for(let[i,c]of Object.entries(e))o[i]=this.unpackWithRefs(c,t,r);return o}return"_id"in e&&(r[e._id]=o),o}};var W=class{views;telepathRegistry;constructor(){this.views=new Map,this.telepathRegistry=new I,this.addAdapter("Date",Date)}addView=(e,t)=>(this.views.set(e,t),this);addAdapter=(e,t)=>(this.telepathRegistry.register(e,t),this);unpack=e=>this.telepathRegistry.unpack(e)};async function L(n,e){let t,r={"X-Requested-With":"DjangoBridge"};e&&(r["X-DjangoBridge-Overlay"]="true");try{t=await fetch(n,{headers:r})}catch{return{action:"network-error"}}return t.status===500?{action:"server-error"}:t.headers.get("X-DjangoBridge-Action")?t.json():{action:"reload"}}async function se(n,e,t){let r,o={"X-Requested-With":"DjangoBridge"};t&&(o["X-DjangoBridge-Overlay"]="true");try{r=await fetch(n,{method:"post",headers:o,body:e})}catch{return{action:"network-error"}}return r.status===500?{action:"server-error"}:r.headers.get("X-DjangoBridge-Action")?r.json():{action:"reload"}}var g=require("react");var J=1;function K(n,e,t,r,o={}){J+=1;let[i,c]=(0,g.useState)({id:J,path:r,metadata:{title:"Loading"},view:"loading",props:{},context:{}}),[f,d]=(0,g.useState)(!1),v=(0,g.useCallback)((a,y,k,F,D,S,ie=!0,q=!0)=>{let $=i.id,pe=k!==i.view||q;if(pe&&(J+=1,$=J),!n&&(document.title=y.title,ie)){let ve=0,ke=window.scrollY,he=window.history?.state;he?.prevPath===a&&(ve=he.prevScrollPosition??0),window.history.pushState({prevPath:window.location.pathname,prevScrollPosition:ke},"",a),window.scrollTo(0,ve)}let ge={id:$,path:a,metadata:y,view:k,props:F,context:D};c(ge),o.onNavigation&&o.onNavigation(ge,pe,S)},[o,i.id,i.view,n]),[u,h]=(0,g.useState)(null),C=(0,g.useCallback)((a,y,k=!0,F=!1,D=!1)=>{if(a.action==="reload")if(!n)window.location.href=y;else return n.handleResponse(a,y);else{if(a.action==="redirect")return h(a.path),Promise.resolve();if(a.action==="render"){if(n&&!a.overlay){D&&console.warn(`openOverlay('${y}') returned a response that couldn't be rendered in an overlay.`);let $=n.handleResponse(a,y);return o.onEscalate&&o.onEscalate(),$}let S=e(a.props),ie=e(a.context),q=!F;q&&a.view===i.view&&i.shouldReloadCallback&&(q=i.shouldReloadCallback(y,S)),v(y,a.metadata,a.view,S,ie,a.messages,k,q)}else if(a.action==="close-overlay")o.onOverlayClose&&o.onOverlayClose(a.messages);else{if(a.action==="server-error")return o.onServerError&&o.onServerError("server"),Promise.reject();if(a.action==="network-error")return o.onServerError&&o.onServerError("network"),Promise.reject()}}return Promise.resolve()},[o,i,n,v,e]),m=(0,g.useRef)(1),l=(0,g.useRef)(1),R=(0,g.useRef)(!1),s=(0,g.useCallback)(async(a,y,k,F=!1)=>{m.current+=1;let D=m.current;R.current=!0;let S=await a();D<l.current||(l.current=D,S!==null&&(await C(S,y,k,F),R.current=!1))},[C]),p=(0,g.useCallback)((a,y=!0)=>{let k=a;if(!a.startsWith("/")){let F=new URL(a);if(F.origin!==window.location.origin)return window.location.href=a,Promise.resolve();k=F.pathname+F.search}return d(!0),s(()=>L(k,!!n),k,y).finally(()=>{d(!1)})},[s,n]),w=(0,g.useCallback)((a,y)=>{a===i.id&&(i.path=y,n||history.replaceState({},"",i.path))},[i,n]),ne=(0,g.useCallback)((a,y)=>s(()=>se(a,y,!!n),a,!0),[s,n]),Pe=(0,g.useCallback)(()=>R.current?Promise.resolve():s(()=>L(i.path,!!n),i.path,!1,!0),[i.path,s,n]);return(0,g.useEffect)(()=>{C(t,r,!1,!1,!0)},[]),(0,g.useEffect)(()=>{u&&(h(null),p(u))},[p,u]),{parent:n,currentFrame:i,isLoading:i.view==="loading",handleResponse:C,navigate:p,replacePath:w,submitForm:ne,refreshProps:Pe,isNavigating:f,setIsNavigating:d}}var x=N(require("react")),j=require("react/jsx-runtime"),ae=x.default.createContext(()=>{}),b=x.default.createContext({isDirty:!1,requestUnload:()=>Promise.resolve(),unloadRequested:!1,unloadBlocked:!1,confirmUnload:()=>{},cancelUnload:()=>{},unloadConfirmed:!1});function le({handleBrowserUnload:n=!1,children:e}){let[t,r]=x.default.useState(!1),[o,i]=x.default.useState({cb:()=>{}}),[c,f]=x.default.useState(!1),[d,v]=x.default.useState(!1),u=x.default.useRef(null),h=()=>u.current&&!!u.current.querySelector("div.dirty-form-marker")||!1;x.default.useEffect(()=>{if(n&&d){let R="This page has unsaved changes.",s=p=>h()?(p.returnValue=R,R):"";return window.addEventListener("beforeunload",s),()=>{window.removeEventListener("beforeunload",s)}}return()=>{}},[n,d]);let C=x.default.useContext(ae),m=x.default.useCallback(()=>{v(h()),C()},[C]),l=x.default.useMemo(()=>({isDirty:d,requestUnload:()=>d&&h()?(r(!0),new Promise(R=>{i({cb:R})})):Promise.resolve(),unloadRequested:t,unloadBlocked:d&&t,confirmUnload:()=>{t&&(f(!0),o.cb(),r(!1),i({cb:()=>{}}))},cancelUnload:()=>{t&&(r(!1),i({cb:()=>{}}))},unloadConfirmed:c}),[d,o,c,t]);return(0,j.jsx)("div",{ref:u,children:(0,j.jsx)(ae.Provider,{value:m,children:(0,j.jsx)(b.Provider,{value:l,children:e})})})}function z(){let n=x.default.useContext(ae);return x.default.useEffect(n),(0,j.jsx)("div",{className:"dirty-form-marker",style:{display:"none"}})}var ce=N(require("react"));var B=N(require("react")),Q=B.default.createContext({overlay:!1,closeRequested:!1,requestClose:()=>{console.error("OverlayContext.requestClose() called from outside an overlay")},onCloseCompleted:()=>{console.error("OverlayContext.onCloseCompleted() called from outside an overlay")}}),P=B.default.createContext({frameId:0,path:"/",props:{},context:{},navigate:()=>(console.error("navigate() called from outside a Django Bridge Browser"),Promise.resolve()),replacePath:()=>{console.error("replacePath() called from outside a Django Bridge Browser")},submitForm:()=>(console.error("submitForm() called from outside a Django Bridge Browser"),Promise.resolve()),openOverlay:()=>{throw console.error("openOverlay() called from outside a Django Bridge Browser"),new Error("Modal cannot be opened here")},refreshProps:()=>(console.error("refreshProps() called from outside a Django Bridge Browser"),Promise.resolve()),isNavigating:!1,setShouldReloadCallback:()=>{}}),Z=B.default.createContext(()=>{}),ee=B.default.createContext(!1),T=B.default.createContext({messages:[],pushMessage:()=>{}});var _=require("react/jsx-runtime");function De({config:n,frame:e}){let t=n.views.get(e.view);if(!t)return(0,_.jsxs)("p",{children:["Unknown view '",e.view,"'"]});let r=(0,_.jsx)(t,{...e.props});return n.contextProviders.forEach((o,i)=>{r=(0,_.jsx)(o.Provider,{value:e.context[i],children:r})}),(0,_.jsx)("div",{children:r},e.id)}var te=De;var oe=require("react/jsx-runtime");function Le({config:n,navigationController:e,openOverlay:t}){let{currentFrame:r,navigate:o,replacePath:i,submitForm:c,refreshProps:f,isNavigating:d}=e,{isDirty:v,requestUnload:u,cancelUnload:h}=ce.default.useContext(b),C=ce.default.useMemo(()=>({frameId:r.id,path:r.path,props:r.props,context:r.context,navigate:(m,l={})=>!v||l.skipDirtyFormCheck===!0?(l.skipDirtyFormCheck===!0&&h(),o(m,l.pushState)):u().then(()=>o(m,l.pushState)),replacePath:i,submitForm:c,openOverlay:t,refreshProps:f,isNavigating:d,setShouldReloadCallback:m=>{r.shouldReloadCallback=m}}),[r,i,c,t,v,u,h,o,f,d]);return(0,oe.jsx)(P.Provider,{value:C,children:(0,oe.jsx)("div",{children:(0,oe.jsx)(te,{config:n,frame:r})},r.id)})}var re=Le;var V=N(require("react"));var Re=require("react/jsx-runtime");function de({children:n,href:e,skipDirtyFormCheck:t=!1,...r},{navigate:o},i){return(0,Re.jsx)("a",{onClick:f=>{e&&(f.preventDefault(),o(e,{skipDirtyFormCheck:t}))},href:e||"#",ref:i,...r,children:n})}var ue=V.default.createContext(de),Be=V.default.forwardRef((n,e)=>{let t=V.default.useContext(P);return V.default.useContext(ue)(n,t,e)}),Ce=Be;var X=class extends W{contextProviders;constructor(){super(),this.contextProviders=new Map}addContextProvider=(e,t)=>(this.contextProviders.set(e,t),this)};var A=N(require("react"));var U=require("react/jsx-runtime");function fe({children:n,onSubmit:e,isDirty:t=!1,disableDirtyCheck:r=!1,...o}){let{submitForm:i,navigate:c}=A.default.useContext(P),[f,d]=A.default.useState(!1),[v,u]=A.default.useState(t),{cancelUnload:h}=A.default.useContext(b),C=l=>{if(v&&h(),!(e&&(e(l),l.defaultPrevented))&&l.target instanceof HTMLFormElement){if(f){l.preventDefault();return}let R=new FormData(l.target);if(l.nativeEvent instanceof SubmitEvent&&l.nativeEvent.submitter){let{submitter:s}=l.nativeEvent;(s instanceof HTMLButtonElement||s instanceof HTMLInputElement)&&s.name&&s.value&&R.set(s.name,s.value)}if(l.target.method==="post")l.preventDefault(),d(!0),i(l.target.action,R).catch(()=>d(!1));else if(l.target.method==="get"){l.preventDefault();let s=Array.from(R.entries()).map(w=>`${encodeURIComponent(w[0])}=${encodeURIComponent(w[1])}`).join("&"),p=l.target.action+(l.target.action.indexOf("?")===-1?"?":"&")+s;d(!0),c(p).catch(()=>d(!1))}}},m=A.default.useCallback(()=>{u(!0)},[]);return(0,U.jsxs)("form",{onSubmit:C,...o,children:[v&&!r&&(0,U.jsx)(z,{}),(0,U.jsx)(ee.Provider,{value:f,children:(0,U.jsx)(Z.Provider,{value:m,children:n})})]})}var G=N(require("react"));var H=require("react/jsx-runtime");function me({config:n,initialResponse:e,initialPath:t,parentNavigationController:r,render:o,requestClose:i,closeRequested:c,onCloseCompleted:f,onServerError:d}){let{pushMessage:v}=G.default.useContext(T),u=K(r,n.unpack,e,t,{onNavigation:(l,R,s)=>{s.forEach(v)},onEscalate:()=>{f()},onOverlayClose:l=>{l.forEach(v),i()},onServerError:d}),h=G.default.useContext(b),C=G.default.useCallback(({skipDirtyFormCheck:l=!1}={})=>{!l&&h.isDirty?h.requestUnload().then(()=>i()):i()},[h,i]),m=G.default.useMemo(()=>({overlay:!0,closeRequested:c,requestClose:C,onCloseCompleted:f}),[c,f,C]);return u.isLoading?(0,H.jsx)(H.Fragment,{}):(0,H.jsx)(Q.Provider,{value:m,children:o((0,H.jsx)(re,{config:n,navigationController:u,openOverlay:()=>{}}))})}var M=N(require("react"));function xe(n,e){let{refreshProps:t}=M.default.useContext(P);M.default.useEffect(()=>{if(!n)return()=>{};let r=null,o=()=>{r=setTimeout(()=>{t(),o()},e)};return o(),()=>{r&&clearTimeout(r)}},[n,e,t])}function we(n,e){let t=(0,M.useCallback)(n,e),{setShouldReloadCallback:r}=(0,M.useContext)(P);(0,M.useEffect)(()=>r(t),[t,r])}var O=require("react/jsx-runtime");function Te({config:n,initialResponse:e}){let[t,r]=E.default.useReducer((s,p)=>{switch(p.action){case"push":return s.concat([p.message]);case"clear":return[];default:return s}},[]),o=E.default.useCallback(s=>{r({action:"push",message:s})},[r]),i=E.default.useCallback(s=>{s==="server"?o({level:"error",text:"A server error occurred. Please try again later."}):s==="network"&&o({level:"error",text:"A network error occurred. Please check your internet connection or try again later."})},[o]),[c,f]=E.default.useState(null),[d,v]=E.default.useState(!1),u=E.default.useRef(null),h=(s,p,w)=>{p&&(v(!0),u.current=null),p&&r({action:"clear"}),w.forEach(o)},C=window.location.pathname+window.location.search+window.location.hash,m=K(null,n.unpack,e,C,{onNavigation:h,onServerError:i});E.default.useEffect(()=>{let s=document.querySelector(".django-bridge-load");s instanceof HTMLElement&&(s.classList.add("django-bridge-load--hidden"),setTimeout(()=>{s.remove()},200));let p=()=>{m.navigate(document.location.pathname,!1)};return window.addEventListener("popstate",p),()=>{window.removeEventListener("popstate",p)}},[]);let l=async(s,p,{onClose:w}={})=>{if(c){console.error("Unable to open overlay as an overlay is already open.");return}m.setIsNavigating(!0);let ne=await L(s,!0);w&&(u.current=w),v(!1),f({render:p,initialResponse:ne,initialPath:s}),m.setIsNavigating(!1)},R=E.default.useMemo(()=>({messages:t,pushMessage:o}),[t,o]);return(0,O.jsx)(le,{handleBrowserUnload:!0,children:(0,O.jsxs)(T.Provider,{value:R,children:[c&&(0,O.jsx)(le,{children:(0,O.jsx)(me,{config:n,initialResponse:c.initialResponse,initialPath:c.initialPath,parentNavigationController:m,render:s=>c.render(s),requestClose:()=>v(!0),closeRequested:d,onCloseCompleted:()=>{f(null),v(!1),u.current&&(u.current(),u.current=null)},onServerError:i})}),!m.isLoading&&(0,O.jsx)(re,{config:n,navigationController:m,openOverlay:(s,p,w)=>{l(s,p,w)}})]})})}0&&(module.exports={App,BuildLinkElement,Config,DirtyFormContext,DirtyFormMarker,Form,FormSubmissionStatus,FormWidgetChangeNotificationContext,Link,MessagesContext,NavigationContext,OverlayContext,RenderFrame,buildLinkElement,useAutoRefresh,useShouldReloadCallback});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@django-bridge/react",
3
- "version": "0.5.0",
3
+ "version": "0.6.0-rc.1",
4
4
  "description": "The simple way to build Django applications with modern React frontends",
5
5
  "repository": "https://github.com/django-bridge/django-bridge",
6
6
  "license": "BSD-3-Clause",
@@ -26,8 +26,7 @@
26
26
  ]
27
27
  },
28
28
  "dependencies": {
29
- "react": "^18.2.0",
30
- "telepath-unpack": "^0.0.4"
29
+ "react": "^18.2.0"
31
30
  },
32
31
  "devDependencies": {
33
32
  "@types/node": "^12.0.0",