@dismissible/react-client 1.0.0-canary.4.a9fb4a5 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +195 -55
- package/dist/config/api.config.d.ts +14 -0
- package/dist/dismissible-client.es.js +208 -205
- package/dist/dismissible-client.umd.js +1 -1
- package/dist/hooks/useDismissibleItem.d.ts +14 -16
- package/dist/root.d.ts +5 -0
- package/package.json +16 -17
- package/CHANGELOG.md +0 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(c,d){typeof exports=="object"&&typeof module!="undefined"?d(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],d):(c=typeof globalThis!="undefined"?globalThis:c||self,d(c.DismissibleClient={},c.React.jsxRuntime,c.React))})(this,(function(c,d,o){"use strict";var je=Object.defineProperty,$e=Object.defineProperties;var Te=Object.getOwnPropertyDescriptors;var Q=Object.getOwnPropertySymbols;var me=Object.prototype.hasOwnProperty,ye=Object.prototype.propertyIsEnumerable;var he=(c,d,o)=>d in c?je(c,d,{enumerable:!0,configurable:!0,writable:!0,value:o}):c[d]=o,g=(c,d)=>{for(var o in d||(d={}))me.call(d,o)&&he(c,o,d[o]);if(Q)for(var o of Q(d))ye.call(d,o)&&he(c,o,d[o]);return c},x=(c,d)=>$e(c,Te(d));var te=(c,d)=>{var o={};for(var R in c)me.call(c,R)&&d.indexOf(R)<0&&(o[R]=c[R]);if(c!=null&&Q)for(var R of Q(c))d.indexOf(R)<0&&ye.call(c,R)&&(o[R]=c[R]);return o};var q=(c,d,o)=>new Promise((R,J)=>{var V=j=>{try{H(o.next(j))}catch(F){J(F)}},X=j=>{try{H(o.throw(j))}catch(F){J(F)}},H=j=>j.done?R(j.value):Promise.resolve(j.value).then(V,X);H((o=o.apply(c,d)).next())});const R=/\{[^{}]+\}/g,J=()=>{var e,r;return typeof process=="object"&&Number.parseInt((r=(e=process==null?void 0:process.versions)==null?void 0:e.node)==null?void 0:r.substring(0,2))>=18&&process.versions.undici};function V(){return Math.random().toString(36).slice(2,11)}function X(e){let I=g({},e),{baseUrl:r="",Request:t=globalThis.Request,fetch:n=globalThis.fetch,querySerializer:i,bodySerializer:s,headers:a,requestInitExt:h=void 0}=I,b=te(I,["baseUrl","Request","fetch","querySerializer","bodySerializer","headers","requestInitExt"]);h=J()?h:void 0,r=ne(r);const m=[];function C(u,l){return q(this,null,function*(){var de;const fe=l||{},{baseUrl:A,fetch:$=n,Request:L=t,headers:D,params:S={},parseAs:U="json",querySerializer:T,bodySerializer:W=s!=null?s:we,body:_,middleware:f=[]}=fe,p=te(fe,["baseUrl","fetch","Request","headers","params","parseAs","querySerializer","bodySerializer","body","middleware"]);let y=r;A&&(y=(de=ne(A))!=null?de:r);let k=typeof i=="function"?i:re(i);T&&(k=typeof T=="function"?T:re(g(g({},typeof i=="object"?i:{}),T)));const N=_===void 0?void 0:W(_,se(a,D,S.header)),Ae=se(N===void 0||N instanceof FormData?{}:{"Content-Type":"application/json"},a,D,S.header),P=[...m,...f],De=x(g(g({redirect:"follow"},b),p),{body:N,headers:Ae});let B,G,O=new L(pe(u,{baseUrl:y,params:S,querySerializer:k}),De),w;for(const E in p)E in O||(O[E]=p[E]);if(P.length){B=V(),G=Object.freeze({baseUrl:y,fetch:$,parseAs:U,querySerializer:k,bodySerializer:W});for(const E of P)if(E&&typeof E=="object"&&typeof E.onRequest=="function"){const v=yield E.onRequest({request:O,schemaPath:u,params:S,options:G,id:B});if(v)if(v instanceof L)O=v;else if(v instanceof Response){w=v;break}else throw new Error("onRequest: must return new Request() or Response() when modifying the request")}}if(!w){try{w=yield $(O,h)}catch(E){let v=E;if(P.length)for(let z=P.length-1;z>=0;z--){const K=P[z];if(K&&typeof K=="object"&&typeof K.onError=="function"){const M=yield K.onError({request:O,error:v,schemaPath:u,params:S,options:G,id:B});if(M){if(M instanceof Response){v=void 0,w=M;break}if(M instanceof Error){v=M;continue}throw new Error("onError: must return new Response() or instance of Error")}}}if(v)throw v}if(P.length)for(let E=P.length-1;E>=0;E--){const v=P[E];if(v&&typeof v=="object"&&typeof v.onResponse=="function"){const z=yield v.onResponse({request:O,response:w,schemaPath:u,params:S,options:G,id:B});if(z){if(!(z instanceof Response))throw new Error("onResponse: must return new Response() when modifying the response");w=z}}}}if(w.status===204||O.method==="HEAD"||w.headers.get("Content-Length")==="0")return w.ok?{data:void 0,response:w}:{error:void 0,response:w};if(w.ok)return U==="stream"?{data:w.body,response:w}:{data:yield w[U](),response:w};let ee=yield w.text();try{ee=JSON.parse(ee)}catch(E){}return{error:ee,response:w}})}return{request(u,l,A){return C(l,x(g({},A),{method:u.toUpperCase()}))},GET(u,l){return C(u,x(g({},l),{method:"GET"}))},PUT(u,l){return C(u,x(g({},l),{method:"PUT"}))},POST(u,l){return C(u,x(g({},l),{method:"POST"}))},DELETE(u,l){return C(u,x(g({},l),{method:"DELETE"}))},OPTIONS(u,l){return C(u,x(g({},l),{method:"OPTIONS"}))},HEAD(u,l){return C(u,x(g({},l),{method:"HEAD"}))},PATCH(u,l){return C(u,x(g({},l),{method:"PATCH"}))},TRACE(u,l){return C(u,x(g({},l),{method:"TRACE"}))},use(...u){for(const l of u)if(l){if(typeof l!="object"||!("onRequest"in l||"onResponse"in l||"onError"in l))throw new Error("Middleware must be an object with one of `onRequest()`, `onResponse() or `onError()`");m.push(l)}},eject(...u){for(const l of u){const A=m.indexOf(l);A!==-1&&m.splice(A,1)}}}}function H(e,r,t){if(r==null)return"";if(typeof r=="object")throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");return`${e}=${(t==null?void 0:t.allowReserved)===!0?r:encodeURIComponent(r)}`}function j(e,r,t){if(!r||typeof r!="object")return"";const n=[],i={simple:",",label:".",matrix:";"}[t.style]||"&";if(t.style!=="deepObject"&&t.explode===!1){for(const h in r)n.push(h,t.allowReserved===!0?r[h]:encodeURIComponent(r[h]));const a=n.join(",");switch(t.style){case"form":return`${e}=${a}`;case"label":return`.${a}`;case"matrix":return`;${e}=${a}`;default:return a}}for(const a in r){const h=t.style==="deepObject"?`${e}[${a}]`:a;n.push(H(h,r[a],t))}const s=n.join(i);return t.style==="label"||t.style==="matrix"?`${i}${s}`:s}function F(e,r,t){if(!Array.isArray(r))return"";if(t.explode===!1){const s={form:",",spaceDelimited:"%20",pipeDelimited:"|"}[t.style]||",",a=(t.allowReserved===!0?r:r.map(h=>encodeURIComponent(h))).join(s);switch(t.style){case"simple":return a;case"label":return`.${a}`;case"matrix":return`;${e}=${a}`;default:return`${e}=${a}`}}const n={simple:",",label:".",matrix:";"}[t.style]||"&",i=[];for(const s of r)t.style==="simple"||t.style==="label"?i.push(t.allowReserved===!0?s:encodeURIComponent(s)):i.push(H(e,s,t));return t.style==="label"||t.style==="matrix"?`${n}${i.join(n)}`:i.join(n)}function re(e){return function(t){const n=[];if(t&&typeof t=="object")for(const i in t){const s=t[i];if(s!=null){if(Array.isArray(s)){if(s.length===0)continue;n.push(F(i,s,x(g({style:"form",explode:!0},e==null?void 0:e.array),{allowReserved:(e==null?void 0:e.allowReserved)||!1})));continue}if(typeof s=="object"){n.push(j(i,s,x(g({style:"deepObject",explode:!0},e==null?void 0:e.object),{allowReserved:(e==null?void 0:e.allowReserved)||!1})));continue}n.push(H(i,s,e))}}return n.join("&")}}function be(e,r){var n;let t=e;for(const i of(n=e.match(R))!=null?n:[]){let s=i.substring(1,i.length-1),a=!1,h="simple";if(s.endsWith("*")&&(a=!0,s=s.substring(0,s.length-1)),s.startsWith(".")?(h="label",s=s.substring(1)):s.startsWith(";")&&(h="matrix",s=s.substring(1)),!r||r[s]===void 0||r[s]===null)continue;const b=r[s];if(Array.isArray(b)){t=t.replace(i,F(s,b,{style:h,explode:a}));continue}if(typeof b=="object"){t=t.replace(i,j(s,b,{style:h,explode:a}));continue}if(h==="matrix"){t=t.replace(i,`;${H(s,b)}`);continue}t=t.replace(i,h==="label"?`.${encodeURIComponent(b)}`:encodeURIComponent(b))}return t}function we(e,r){var t,n;return e instanceof FormData?e:r&&(r.get instanceof Function?(t=r.get("Content-Type"))!=null?t:r.get("content-type"):(n=r["Content-Type"])!=null?n:r["content-type"])==="application/x-www-form-urlencoded"?new URLSearchParams(e).toString():JSON.stringify(e)}function pe(e,r){var i,s;let t=`${r.baseUrl}${e}`;(i=r.params)!=null&&i.path&&(t=be(t,r.params.path));let n=r.querySerializer((s=r.params.query)!=null?s:{});return n.startsWith("?")&&(n=n.substring(1)),n&&(t+=`?${n}`),t}function se(...e){const r=new Headers;for(const t of e){if(!t||typeof t!="object")continue;const n=t instanceof Headers?t.entries():Object.entries(t);for(const[i,s]of n)if(s===null)r.delete(i);else if(Array.isArray(s))for(const a of s)r.append(i,a);else s!==void 0&&r.set(i,s)}return r}function ne(e){return e.endsWith("/")?e.substring(0,e.length-1):e}const ie=(e,r,t)=>{try{const n=`${r}_${e}`,i=localStorage.getItem(n);if(!i)return null;const{data:s,timestamp:a}=JSON.parse(i);return t&&Date.now()-a>t?(localStorage.removeItem(n),null):s}catch(n){return null}},Y=(e,r,t)=>{try{const n=`${t}_${e}`,i={data:r,timestamp:Date.now()};localStorage.setItem(n,JSON.stringify(i))}catch(n){console.warn("Failed to cache dismissible item:",n)}},oe=(e,r)=>{try{const t=`${r}_${e}`;localStorage.removeItem(t)}catch(t){console.warn("Failed to remove cached dismissible item:",t)}},Z=o.createContext(null),ae=()=>{const e=o.useContext(Z);if(!e)throw new Error("useDismissibleContext must be used within a DismissibleProvider");return e},ge="dismissible",ce=(e,r={})=>{const{initialData:t,enableCache:n=!0,cachePrefix:i=ge,cacheExpiration:s}=r,a=ae(),{userId:h}=a,b=o.useMemo(()=>X({baseUrl:a.baseUrl,headers:{}}),[a.baseUrl]),m=o.useMemo(()=>`${h}-${e}`,[h,e]),C=o.useRef({enableCache:n,cachePrefix:i,cacheExpiration:s}),I=o.useRef(e),u=o.useRef(m),l=o.useRef(null),[A,$]=o.useState(!1),[L,D]=o.useState(),[S,U]=o.useState(()=>{if(t)return t;if(n){const f=ie(m,i,s);if(f)return f}}),T=o.useCallback(()=>q(null,null,function*(){var p;if(n){const y=ie(m,i,s);if(y!=null&&y.dismissedAt){U(y),$(!1);return}}(p=l.current)==null||p.abort();const f=new AbortController;l.current=f,$(!0),D(void 0);try{const y=yield a.getAuthHeaders(),{data:k,error:N}=yield b.GET("/v1/users/{userId}/items/{itemId}",{params:{path:{userId:h,itemId:e}},headers:y,signal:f.signal});if(N||!k)throw new Error("Failed to fetch dismissible item");U(k.data),n&&Y(m,k.data,i)}catch(y){if(y instanceof Error&&y.name==="AbortError")return;D(y instanceof Error?y:new Error("Unknown error occurred"))}finally{$(!1)}}),[e,h,m,n,i,s,b,a]);o.useEffect(()=>{const f=I.current!==e,p=u.current!==m;f||p?(I.current=e,u.current=m,T()):t||T()},[e,m,t,T]),o.useEffect(()=>()=>{var f;(f=l.current)==null||f.abort()},[]),o.useEffect(()=>{const f=C.current;(f.enableCache!==n||f.cachePrefix!==i||f.cacheExpiration!==s)&&(f.cachePrefix!==i&&oe(m,f.cachePrefix),!n&&f.enableCache&&oe(m,f.cachePrefix),C.current={enableCache:n,cachePrefix:i,cacheExpiration:s},T())},[n,i,s,m,T]);const W=o.useCallback(()=>q(null,null,function*(){D(void 0);try{const f=yield a.getAuthHeaders(),{data:p,error:y}=yield b.DELETE("/v1/users/{userId}/items/{itemId}",{params:{path:{userId:h,itemId:e}},headers:f});if(y||!p)throw new Error("Failed to dismiss item");U(p.data),n&&Y(m,p.data,i)}catch(f){throw D(f instanceof Error?f:new Error("Failed to dismiss item")),f}}),[e,h,m,n,i,b,a]),_=o.useCallback(()=>q(null,null,function*(){D(void 0);try{const f=yield a.getAuthHeaders(),{data:p,error:y}=yield b.POST("/v1/users/{userId}/items/{itemId}",{params:{path:{userId:h,itemId:e}},headers:f});if(y||!p)throw new Error("Failed to restore item");U(p.data),n&&Y(m,p.data,i)}catch(f){throw D(f instanceof Error?f:new Error("Failed to restore item")),f}}),[e,h,m,n,i,b,a]);return{dismissedAt:S==null?void 0:S.dismissedAt,dismiss:W,restore:_,isLoading:A,error:L,item:S}},ve=()=>d.jsx("div",{className:"dismissible-loading","aria-live":"polite",children:"Loading..."}),Ee=()=>d.jsx("div",{className:"dismissible-error",role:"alert",children:"Unable to load content. Please try again later."}),Ce=({onDismiss:e,ariaLabel:r})=>d.jsx("button",{className:"dismissible-button",onClick:e,"aria-label":r,type:"button",children:"×"}),Re=({itemId:e,children:r,onDismiss:t,LoadingComponent:n=ve,ErrorComponent:i=Ee,DismissButtonComponent:s=Ce,enableCache:a,cachePrefix:h,cacheExpiration:b,ignoreErrors:m=!1})=>{const{dismissedAt:C,isLoading:I,error:u,dismiss:l}=ce(e,{enableCache:a,cachePrefix:h,cacheExpiration:b}),[A,$]=o.useState(!1),[L,D]=o.useState(e);e!==L&&(D(e),$(!1));const S=()=>q(null,null,function*(){$(!0);try{yield l(),t==null||t()}catch(U){$(!1)}});return I&&n?d.jsx(n,{itemId:e}):I&&!n?null:u&&i&&!m?d.jsx(i,{itemId:e,error:u}):C||A?null:d.jsxs("div",{className:"dismissible-container",children:[d.jsx("div",{className:"dismissible-content",children:r}),s?d.jsx(s,{onDismiss:S,ariaLabel:`Dismiss ${e}`}):null]})},le=e=>q(null,null,function*(){if(typeof e=="function")try{const r=e();return yield Promise.resolve(r)}catch(r){console.warn("Failed to resolve JWT from function:",r);return}return e}),ue=e=>q(null,null,function*(){const r=yield le(e);return r?{Authorization:`Bearer ${r}`}:{}}),Se=e=>{try{const r=new URL(e),t=r.hostname==="localhost"||r.hostname==="127.0.0.1"||r.hostname==="[::1]",n=r.protocol==="https:";return{isSecure:n||t,isLocalhost:t,isHttps:n}}catch(r){return{isSecure:!1,isLocalhost:!1,isHttps:!1}}},xe=({userId:e,jwt:r,baseUrl:t,children:n})=>{const{isSecure:i}=Se(t);i||console.warn(`[dismissible] Insecure baseUrl "${t}". Use https:// in production (or localhost for development). JWT tokens may be exposed over insecure connections.`);const s=o.useMemo(()=>({userId:e,jwt:r,baseUrl:t,getAuthHeaders:()=>q(null,null,function*(){return yield ue(r)})}),[e,r,t]);return d.jsx(Z.Provider,{value:s,children:n})};c.Dismissible=Re,c.DismissibleContext=Z,c.DismissibleProvider=xe,c.getAuthHeaders=ue,c.resolveJwt=le,c.useDismissibleContext=ae,c.useDismissibleItem=ce,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})}));
|
|
1
|
+
(function(c,d){typeof exports=="object"&&typeof module!="undefined"?d(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],d):(c=typeof globalThis!="undefined"?globalThis:c||self,d(c.DismissibleClient={},c.React.jsxRuntime,c.React))})(this,(function(c,d,o){"use strict";var Ae=Object.defineProperty,$e=Object.defineProperties;var Te=Object.getOwnPropertyDescriptors;var Q=Object.getOwnPropertySymbols;var ye=Object.prototype.hasOwnProperty,be=Object.prototype.propertyIsEnumerable;var me=(c,d,o)=>d in c?Ae(c,d,{enumerable:!0,configurable:!0,writable:!0,value:o}):c[d]=o,g=(c,d)=>{for(var o in d||(d={}))ye.call(d,o)&&me(c,o,d[o]);if(Q)for(var o of Q(d))be.call(d,o)&&me(c,o,d[o]);return c},S=(c,d)=>$e(c,Te(d));var re=(c,d)=>{var o={};for(var R in c)ye.call(c,R)&&d.indexOf(R)<0&&(o[R]=c[R]);if(c!=null&&Q)for(var R of Q(c))d.indexOf(R)<0&&be.call(c,R)&&(o[R]=c[R]);return o};var U=(c,d,o)=>new Promise((R,M)=>{var V=j=>{try{q(o.next(j))}catch(F){M(F)}},X=j=>{try{q(o.throw(j))}catch(F){M(F)}},q=j=>j.done?R(j.value):Promise.resolve(j.value).then(V,X);q((o=o.apply(c,d)).next())});const R=/\{[^{}]+\}/g,M=()=>{var e,r;return typeof process=="object"&&Number.parseInt((r=(e=process==null?void 0:process.versions)==null?void 0:e.node)==null?void 0:r.substring(0,2))>=18&&process.versions.undici};function V(){return Math.random().toString(36).slice(2,11)}function X(e){let H=g({},e),{baseUrl:r="",Request:t=globalThis.Request,fetch:n=globalThis.fetch,querySerializer:i,bodySerializer:s,headers:a,requestInitExt:h=void 0}=H,b=re(H,["baseUrl","Request","fetch","querySerializer","bodySerializer","headers","requestInitExt"]);h=M()?h:void 0,r=ie(r);const m=[];function C(f,l){return U(this,null,function*(){var he;const de=l||{},{baseUrl:x,fetch:A=n,Request:L=t,headers:$,params:D={},parseAs:O="json",querySerializer:T,bodySerializer:J=s!=null?s:pe,body:W,middleware:_=[]}=de,u=re(de,["baseUrl","fetch","Request","headers","params","parseAs","querySerializer","bodySerializer","body","middleware"]);let p=r;x&&(p=(he=ie(x))!=null?he:r);let y=typeof i=="function"?i:se(i);T&&(y=typeof T=="function"?T:se(g(g({},typeof i=="object"?i:{}),T)));const k=W===void 0?void 0:J(W,ne(a,$,D.header)),ee=ne(k===void 0||k instanceof FormData?{}:{"Content-Type":"application/json"},a,$,D.header),P=[...m,..._],je=S(g(g({redirect:"follow"},b),u),{body:k,headers:ee});let B,G,I=new L(ge(f,{baseUrl:p,params:D,querySerializer:y}),je),w;for(const v in u)v in I||(I[v]=u[v]);if(P.length){B=V(),G=Object.freeze({baseUrl:p,fetch:A,parseAs:O,querySerializer:y,bodySerializer:J});for(const v of P)if(v&&typeof v=="object"&&typeof v.onRequest=="function"){const E=yield v.onRequest({request:I,schemaPath:f,params:D,options:G,id:B});if(E)if(E instanceof L)I=E;else if(E instanceof Response){w=E;break}else throw new Error("onRequest: must return new Request() or Response() when modifying the request")}}if(!w){try{w=yield A(I,h)}catch(v){let E=v;if(P.length)for(let z=P.length-1;z>=0;z--){const K=P[z];if(K&&typeof K=="object"&&typeof K.onError=="function"){const N=yield K.onError({request:I,error:E,schemaPath:f,params:D,options:G,id:B});if(N){if(N instanceof Response){E=void 0,w=N;break}if(N instanceof Error){E=N;continue}throw new Error("onError: must return new Response() or instance of Error")}}}if(E)throw E}if(P.length)for(let v=P.length-1;v>=0;v--){const E=P[v];if(E&&typeof E=="object"&&typeof E.onResponse=="function"){const z=yield E.onResponse({request:I,response:w,schemaPath:f,params:D,options:G,id:B});if(z){if(!(z instanceof Response))throw new Error("onResponse: must return new Response() when modifying the response");w=z}}}}if(w.status===204||I.method==="HEAD"||w.headers.get("Content-Length")==="0")return w.ok?{data:void 0,response:w}:{error:void 0,response:w};if(w.ok)return O==="stream"?{data:w.body,response:w}:{data:yield w[O](),response:w};let te=yield w.text();try{te=JSON.parse(te)}catch(v){}return{error:te,response:w}})}return{request(f,l,x){return C(l,S(g({},x),{method:f.toUpperCase()}))},GET(f,l){return C(f,S(g({},l),{method:"GET"}))},PUT(f,l){return C(f,S(g({},l),{method:"PUT"}))},POST(f,l){return C(f,S(g({},l),{method:"POST"}))},DELETE(f,l){return C(f,S(g({},l),{method:"DELETE"}))},OPTIONS(f,l){return C(f,S(g({},l),{method:"OPTIONS"}))},HEAD(f,l){return C(f,S(g({},l),{method:"HEAD"}))},PATCH(f,l){return C(f,S(g({},l),{method:"PATCH"}))},TRACE(f,l){return C(f,S(g({},l),{method:"TRACE"}))},use(...f){for(const l of f)if(l){if(typeof l!="object"||!("onRequest"in l||"onResponse"in l||"onError"in l))throw new Error("Middleware must be an object with one of `onRequest()`, `onResponse() or `onError()`");m.push(l)}},eject(...f){for(const l of f){const x=m.indexOf(l);x!==-1&&m.splice(x,1)}}}}function q(e,r,t){if(r==null)return"";if(typeof r=="object")throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");return`${e}=${(t==null?void 0:t.allowReserved)===!0?r:encodeURIComponent(r)}`}function j(e,r,t){if(!r||typeof r!="object")return"";const n=[],i={simple:",",label:".",matrix:";"}[t.style]||"&";if(t.style!=="deepObject"&&t.explode===!1){for(const h in r)n.push(h,t.allowReserved===!0?r[h]:encodeURIComponent(r[h]));const a=n.join(",");switch(t.style){case"form":return`${e}=${a}`;case"label":return`.${a}`;case"matrix":return`;${e}=${a}`;default:return a}}for(const a in r){const h=t.style==="deepObject"?`${e}[${a}]`:a;n.push(q(h,r[a],t))}const s=n.join(i);return t.style==="label"||t.style==="matrix"?`${i}${s}`:s}function F(e,r,t){if(!Array.isArray(r))return"";if(t.explode===!1){const s={form:",",spaceDelimited:"%20",pipeDelimited:"|"}[t.style]||",",a=(t.allowReserved===!0?r:r.map(h=>encodeURIComponent(h))).join(s);switch(t.style){case"simple":return a;case"label":return`.${a}`;case"matrix":return`;${e}=${a}`;default:return`${e}=${a}`}}const n={simple:",",label:".",matrix:";"}[t.style]||"&",i=[];for(const s of r)t.style==="simple"||t.style==="label"?i.push(t.allowReserved===!0?s:encodeURIComponent(s)):i.push(q(e,s,t));return t.style==="label"||t.style==="matrix"?`${n}${i.join(n)}`:i.join(n)}function se(e){return function(t){const n=[];if(t&&typeof t=="object")for(const i in t){const s=t[i];if(s!=null){if(Array.isArray(s)){if(s.length===0)continue;n.push(F(i,s,S(g({style:"form",explode:!0},e==null?void 0:e.array),{allowReserved:(e==null?void 0:e.allowReserved)||!1})));continue}if(typeof s=="object"){n.push(j(i,s,S(g({style:"deepObject",explode:!0},e==null?void 0:e.object),{allowReserved:(e==null?void 0:e.allowReserved)||!1})));continue}n.push(q(i,s,e))}}return n.join("&")}}function we(e,r){var n;let t=e;for(const i of(n=e.match(R))!=null?n:[]){let s=i.substring(1,i.length-1),a=!1,h="simple";if(s.endsWith("*")&&(a=!0,s=s.substring(0,s.length-1)),s.startsWith(".")?(h="label",s=s.substring(1)):s.startsWith(";")&&(h="matrix",s=s.substring(1)),!r||r[s]===void 0||r[s]===null)continue;const b=r[s];if(Array.isArray(b)){t=t.replace(i,F(s,b,{style:h,explode:a}));continue}if(typeof b=="object"){t=t.replace(i,j(s,b,{style:h,explode:a}));continue}if(h==="matrix"){t=t.replace(i,`;${q(s,b)}`);continue}t=t.replace(i,h==="label"?`.${encodeURIComponent(b)}`:encodeURIComponent(b))}return t}function pe(e,r){var t,n;return e instanceof FormData?e:r&&(r.get instanceof Function?(t=r.get("Content-Type"))!=null?t:r.get("content-type"):(n=r["Content-Type"])!=null?n:r["content-type"])==="application/x-www-form-urlencoded"?new URLSearchParams(e).toString():JSON.stringify(e)}function ge(e,r){var i,s;let t=`${r.baseUrl}${e}`;(i=r.params)!=null&&i.path&&(t=we(t,r.params.path));let n=r.querySerializer((s=r.params.query)!=null?s:{});return n.startsWith("?")&&(n=n.substring(1)),n&&(t+=`?${n}`),t}function ne(...e){const r=new Headers;for(const t of e){if(!t||typeof t!="object")continue;const n=t instanceof Headers?t.entries():Object.entries(t);for(const[i,s]of n)if(s===null)r.delete(i);else if(Array.isArray(s))for(const a of s)r.append(i,a);else s!==void 0&&r.set(i,s)}return r}function ie(e){return e.endsWith("/")?e.substring(0,e.length-1):e}const oe=(e,r,t)=>{try{const n=`${r}_${e}`,i=localStorage.getItem(n);if(!i)return null;const{data:s,timestamp:a}=JSON.parse(i);return t&&Date.now()-a>t?(localStorage.removeItem(n),null):s}catch(n){return null}},Y=(e,r,t)=>{try{const n=`${t}_${e}`,i={data:r,timestamp:Date.now()};localStorage.setItem(n,JSON.stringify(i))}catch(n){console.warn("Failed to cache dismissible item:",n)}},ae=(e,r)=>{try{const t=`${r}_${e}`;localStorage.removeItem(t)}catch(t){console.warn("Failed to remove cached dismissible item:",t)}},Z=o.createContext(null),ce=()=>{const e=o.useContext(Z);if(!e)throw new Error("useDismissibleContext must be used within a DismissibleProvider");return e},Ee="dismissible",le=(e,r={})=>{var _;const{initialData:t,enableCache:n=!0,cachePrefix:i=Ee,cacheExpiration:s}=r,a=ce(),{userId:h}=a,b=o.useMemo(()=>X({baseUrl:a.baseUrl,headers:{}}),[a.baseUrl]),m=o.useMemo(()=>`${h}-${e}`,[h,e]),C=o.useRef({enableCache:n,cachePrefix:i,cacheExpiration:s}),H=o.useRef(e),f=o.useRef(m),l=o.useRef(null),[x,A]=o.useState(!1),[L,$]=o.useState(null),[D,O]=o.useState(()=>{if(t)return t;if(n){const u=oe(m,i,s);if(u)return u}}),T=o.useCallback(()=>U(null,null,function*(){var p;if(n){const y=oe(m,i,s);if(y!=null&&y.dismissedAt){O(y),A(!1);return}}(p=l.current)==null||p.abort();const u=new AbortController;l.current=u,A(!0),$(null);try{const y=yield a.getAuthHeaders(),{data:k,error:ee}=yield b.GET("/v1/users/{userId}/items/{itemId}",{params:{path:{userId:h,itemId:e}},headers:y,signal:u.signal});if(ee||!k)throw new Error("Failed to fetch dismissible item");O(k.data),n&&Y(m,k.data,i)}catch(y){if(y instanceof Error&&y.name==="AbortError")return;$(y instanceof Error?y:new Error("Unknown error occurred"))}finally{A(!1)}}),[e,h,m,n,i,s,b,a]);o.useEffect(()=>{const u=H.current!==e,p=f.current!==m;u||p?(H.current=e,f.current=m,T()):t||T()},[e,m,t,T]),o.useEffect(()=>()=>{var u;(u=l.current)==null||u.abort()},[]),o.useEffect(()=>{const u=C.current;(u.enableCache!==n||u.cachePrefix!==i||u.cacheExpiration!==s)&&(u.cachePrefix!==i&&ae(m,u.cachePrefix),!n&&u.enableCache&&ae(m,u.cachePrefix),C.current={enableCache:n,cachePrefix:i,cacheExpiration:s},T())},[n,i,s,m,T]);const J=o.useCallback(()=>U(null,null,function*(){$(null);try{const u=yield a.getAuthHeaders(),{data:p,error:y}=yield b.DELETE("/v1/users/{userId}/items/{itemId}",{params:{path:{userId:h,itemId:e}},headers:u});if(y||!p)throw new Error("Failed to dismiss item");O(p.data),n&&Y(m,p.data,i)}catch(u){throw $(u instanceof Error?u:new Error("Failed to dismiss item")),u}}),[e,h,m,n,i,b,a]),W=o.useCallback(()=>U(null,null,function*(){$(null);try{const u=yield a.getAuthHeaders(),{data:p,error:y}=yield b.POST("/v1/users/{userId}/items/{itemId}",{params:{path:{userId:h,itemId:e}},headers:u});if(y||!p)throw new Error("Failed to restore item");O(p.data),n&&Y(m,p.data,i)}catch(u){throw $(u instanceof Error?u:new Error("Failed to restore item")),u}}),[e,h,m,n,i,b,a]);return{dismissedOn:(_=D==null?void 0:D.dismissedAt)!=null?_:null,dismiss:J,restore:W,isLoading:x,error:L,item:D}},ve=()=>d.jsx("div",{className:"dismissible-loading","aria-live":"polite",children:"Loading..."}),Ce=()=>d.jsx("div",{className:"dismissible-error",role:"alert",children:"Unable to load content. Please try again later."}),Re=({onDismiss:e,ariaLabel:r})=>d.jsx("button",{className:"dismissible-button",onClick:e,"aria-label":r,type:"button",children:"×"}),Se=({itemId:e,children:r,onDismiss:t,LoadingComponent:n=ve,ErrorComponent:i=Ce,DismissButtonComponent:s=Re,enableCache:a,cachePrefix:h,cacheExpiration:b,ignoreErrors:m=!1})=>{const{dismissedOn:C,isLoading:H,error:f,dismiss:l}=le(e,{enableCache:a,cachePrefix:h,cacheExpiration:b}),[x,A]=o.useState(!1);o.useEffect(()=>{A(!1)},[e]);const L=()=>U(null,null,function*(){A(!0);try{yield l(),t==null||t()}catch($){A(!1)}});return H&&n?d.jsx(n,{itemId:e}):H&&!n?null:f&&i&&!m?d.jsx(i,{itemId:e,error:f}):C||x?null:d.jsxs("div",{className:"dismissible-container",children:[d.jsx("div",{className:"dismissible-content",children:r}),s?d.jsx(s,{onDismiss:L,ariaLabel:`Dismiss ${e}`}):null]})},ue=e=>U(null,null,function*(){if(typeof e=="function")try{const r=e();return yield Promise.resolve(r)}catch(r){console.warn("Failed to resolve JWT from function:",r);return}return e}),fe=e=>U(null,null,function*(){const r=yield ue(e);return r?{Authorization:`Bearer ${r}`}:{}}),xe=e=>{try{const r=new URL(e),t=r.hostname==="localhost"||r.hostname==="127.0.0.1"||r.hostname==="[::1]",n=r.protocol==="https:";return{isSecure:n||t,isLocalhost:t,isHttps:n}}catch(r){return{isSecure:!1,isLocalhost:!1,isHttps:!1}}},De=({userId:e,jwt:r,baseUrl:t,children:n})=>{const{isSecure:i}=xe(t);i||console.warn(`[dismissible] Insecure baseUrl "${t}". Use https:// in production (or localhost for development). JWT tokens may be exposed over insecure connections.`);const s=o.useMemo(()=>({userId:e,jwt:r,baseUrl:t,getAuthHeaders:()=>U(null,null,function*(){return yield fe(r)})}),[e,r,t]);return d.jsx(Z.Provider,{value:s,children:n})};c.Dismissible=Se,c.DismissibleContext=Z,c.DismissibleProvider=De,c.getAuthHeaders=fe,c.resolveJwt=ue,c.useDismissibleContext=ce,c.useDismissibleItem=le,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})}));
|
|
@@ -11,25 +11,23 @@ export interface UseDismissibleItemOptions {
|
|
|
11
11
|
/** Cache expiration time in milliseconds (default: never expires) */
|
|
12
12
|
cacheExpiration?: number;
|
|
13
13
|
}
|
|
14
|
-
export interface UseDismissibleItemResponse {
|
|
15
|
-
/** The date when the item was dismissed, or undefined if not dismissed */
|
|
16
|
-
dismissedAt?: IDismissibleItem["dismissedAt"];
|
|
17
|
-
/** Function to dismiss the item */
|
|
18
|
-
dismiss: () => Promise<void>;
|
|
19
|
-
/** Function to restore the item */
|
|
20
|
-
restore: () => Promise<void>;
|
|
21
|
-
/** Loading state */
|
|
22
|
-
isLoading: boolean;
|
|
23
|
-
/** Error state */
|
|
24
|
-
error?: Error;
|
|
25
|
-
/** The dismissible item data */
|
|
26
|
-
item?: IDismissibleItem;
|
|
27
|
-
}
|
|
28
14
|
/**
|
|
29
15
|
* Hook for managing dismissible items
|
|
30
16
|
* @param id - The ID of the dismissible item
|
|
31
17
|
* @param options - Configuration options for the hook
|
|
32
|
-
* @returns Object with
|
|
18
|
+
* @returns Object with dismissedOn, dismiss and restore functions
|
|
33
19
|
*/
|
|
34
|
-
export declare const useDismissibleItem: (itemId: string, options?: UseDismissibleItemOptions) =>
|
|
20
|
+
export declare const useDismissibleItem: (itemId: string, options?: UseDismissibleItemOptions) => {
|
|
21
|
+
dismissedOn: string | null;
|
|
22
|
+
dismiss: () => Promise<void>;
|
|
23
|
+
restore: () => Promise<void>;
|
|
24
|
+
isLoading: boolean;
|
|
25
|
+
error: Error | null;
|
|
26
|
+
item: {
|
|
27
|
+
itemId: string;
|
|
28
|
+
userId: string;
|
|
29
|
+
createdAt: string;
|
|
30
|
+
dismissedAt?: string;
|
|
31
|
+
} | undefined;
|
|
32
|
+
};
|
|
35
33
|
export {};
|
package/dist/root.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dismissible React Component Library
|
|
3
|
+
*
|
|
4
|
+
* This library provides React components for implementing dismissible UI elements
|
|
5
|
+
*/
|
|
1
6
|
export * from './components/Dismissible';
|
|
2
7
|
export * from './hooks/useDismissibleItem';
|
|
3
8
|
export * from './contexts/DismissibleContext';
|
package/package.json
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dismissible/react-client",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
7
|
-
"
|
|
8
|
-
"README.md",
|
|
9
|
-
"CHANGELOG.md"
|
|
7
|
+
"README.md"
|
|
10
8
|
],
|
|
11
9
|
"main": "./dist/dismissible-client.umd.js",
|
|
12
10
|
"module": "./dist/dismissible-client.es.js",
|
|
@@ -22,26 +20,22 @@
|
|
|
22
20
|
"access": "public"
|
|
23
21
|
},
|
|
24
22
|
"scripts": {
|
|
25
|
-
"build": "vite build",
|
|
26
23
|
"dev": "vite",
|
|
24
|
+
"build": "vite build",
|
|
27
25
|
"commit": "cz",
|
|
28
26
|
"commitlint": "commitlint --from HEAD~1 --to HEAD --verbose",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"generate:api": "npm run generate:clean && npx openapi-typescript http://localhost:3001/docs-yaml -o ./src/generated/contract/index.d.ts",
|
|
32
|
-
"lint": "eslint \"{.storybook,src,test}/**/*.{ts,tsx}\"",
|
|
33
|
-
"lint:fix": "eslint \"{.storybook,src,test}/**/*.{ts,tsx}\" --fix",
|
|
34
|
-
"prettier": "prettier --check \"src/**/*.{ts,tsx}\" \".github/**/*.{yaml,yml}\"",
|
|
35
|
-
"prettier:fix": "prettier --write \"src/**/*.{ts,tsx}\" \".github/**/*.{yaml,yml}\"",
|
|
27
|
+
"lint": "eslint \"{.storybook,src,test}/**/*.{ts,tsx}\" --fix",
|
|
28
|
+
"format": "prettier --write \"src/**/*.{ts,tsx}\" \".github/**/*.{yaml,yml}\"",
|
|
36
29
|
"preview": "vite preview",
|
|
37
30
|
"release": "semantic-release",
|
|
38
|
-
"script": "ts-node -r tsconfig-paths/register --project tsconfig.json",
|
|
39
|
-
"storybook": "storybook dev -p 6006",
|
|
40
|
-
"storybook:staging": "MODE=staging storybook dev -p 6006",
|
|
41
|
-
"storybook:build": "storybook build",
|
|
42
31
|
"test": "vitest run --config vitest.config.ts",
|
|
43
32
|
"test:watch": "vitest --config vitest.config.ts",
|
|
44
|
-
"
|
|
33
|
+
"storybook": "storybook dev -p 6006",
|
|
34
|
+
"storybook:staging": "MODE=staging storybook dev -p 6006",
|
|
35
|
+
"build-storybook": "storybook build",
|
|
36
|
+
"script": "ts-node -r tsconfig-paths/register --project tsconfig.json",
|
|
37
|
+
"generate:clean": "rm -rf ./src/generated/contract/*",
|
|
38
|
+
"generate:api": "npm run generate:clean && npx openapi-typescript http://localhost:3001/docs-yaml -o ./src/generated/contract/index.d.ts"
|
|
45
39
|
},
|
|
46
40
|
"dependencies": {
|
|
47
41
|
"openapi-fetch": "^0.15.0"
|
|
@@ -98,6 +92,11 @@
|
|
|
98
92
|
"path": "cz-conventional-changelog"
|
|
99
93
|
}
|
|
100
94
|
},
|
|
95
|
+
"eslintConfig": {
|
|
96
|
+
"extends": [
|
|
97
|
+
"plugin:storybook/recommended"
|
|
98
|
+
]
|
|
99
|
+
},
|
|
101
100
|
"msw": {
|
|
102
101
|
"workerDirectory": [
|
|
103
102
|
"public"
|
package/CHANGELOG.md
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# 1.0.0 (2025-12-21)
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
### Features
|
|
5
|
-
|
|
6
|
-
* **react-client:** dismissible React Client ([#1](https://github.com/DismissibleIo/dismissible-react-client/issues/1)) ([a4e6dc7](https://github.com/DismissibleIo/dismissible-react-client/commit/a4e6dc7786a4fd1abb81f4a796e22d37fdc12e0b))
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
### BREAKING CHANGES
|
|
10
|
-
|
|
11
|
-
* **react-client:** This is the first public release which requires both the DismissibleProvider and
|
|
12
|
-
Dismissible components to be used to force a userId.
|