@dismissible/react-client 1.0.0 → 2.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/CHANGELOG.md +30 -0
- package/README.md +55 -195
- package/dist/config/api.config.d.ts +0 -14
- package/dist/dismissible-client.es.js +205 -208
- package/dist/dismissible-client.umd.js +1 -1
- package/dist/hooks/useDismissibleItem.d.ts +16 -14
- package/dist/root.d.ts +0 -5
- package/package.json +17 -16
|
@@ -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 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"})}));
|
|
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"})}));
|
|
@@ -11,23 +11,25 @@ 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
|
+
}
|
|
14
28
|
/**
|
|
15
29
|
* Hook for managing dismissible items
|
|
16
30
|
* @param id - The ID of the dismissible item
|
|
17
31
|
* @param options - Configuration options for the hook
|
|
18
|
-
* @returns Object with
|
|
32
|
+
* @returns Object with dismissedAt, dismiss and restore functions
|
|
19
33
|
*/
|
|
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
|
-
};
|
|
34
|
+
export declare const useDismissibleItem: (itemId: string, options?: UseDismissibleItemOptions) => UseDismissibleItemResponse;
|
|
33
35
|
export {};
|
package/dist/root.d.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dismissible React Component Library
|
|
3
|
-
*
|
|
4
|
-
* This library provides React components for implementing dismissible UI elements
|
|
5
|
-
*/
|
|
6
1
|
export * from './components/Dismissible';
|
|
7
2
|
export * from './hooks/useDismissibleItem';
|
|
8
3
|
export * from './contexts/DismissibleContext';
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dismissible/react-client",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
7
|
-
"
|
|
7
|
+
"LICENSE",
|
|
8
|
+
"README.md",
|
|
9
|
+
"CHANGELOG.md"
|
|
8
10
|
],
|
|
9
11
|
"main": "./dist/dismissible-client.umd.js",
|
|
10
12
|
"module": "./dist/dismissible-client.es.js",
|
|
@@ -20,22 +22,26 @@
|
|
|
20
22
|
"access": "public"
|
|
21
23
|
},
|
|
22
24
|
"scripts": {
|
|
23
|
-
"dev": "vite",
|
|
24
25
|
"build": "vite build",
|
|
26
|
+
"dev": "vite",
|
|
25
27
|
"commit": "cz",
|
|
26
28
|
"commitlint": "commitlint --from HEAD~1 --to HEAD --verbose",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
+
"format": "npm run prettier:fix && npm run lint:fix",
|
|
30
|
+
"generate:clean": "rm -rf ./src/generated/contract/*",
|
|
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}\"",
|
|
29
36
|
"preview": "vite preview",
|
|
30
37
|
"release": "semantic-release",
|
|
31
|
-
"
|
|
32
|
-
"test:watch": "vitest --config vitest.config.ts",
|
|
38
|
+
"script": "ts-node -r tsconfig-paths/register --project tsconfig.json",
|
|
33
39
|
"storybook": "storybook dev -p 6006",
|
|
34
40
|
"storybook:staging": "MODE=staging storybook dev -p 6006",
|
|
35
|
-
"build
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
41
|
+
"storybook:build": "storybook build",
|
|
42
|
+
"test": "vitest run --config vitest.config.ts",
|
|
43
|
+
"test:watch": "vitest --config vitest.config.ts",
|
|
44
|
+
"validate": "npm run prettier && npm run lint && npm run test"
|
|
39
45
|
},
|
|
40
46
|
"dependencies": {
|
|
41
47
|
"openapi-fetch": "^0.15.0"
|
|
@@ -92,11 +98,6 @@
|
|
|
92
98
|
"path": "cz-conventional-changelog"
|
|
93
99
|
}
|
|
94
100
|
},
|
|
95
|
-
"eslintConfig": {
|
|
96
|
-
"extends": [
|
|
97
|
-
"plugin:storybook/recommended"
|
|
98
|
-
]
|
|
99
|
-
},
|
|
100
101
|
"msw": {
|
|
101
102
|
"workerDirectory": [
|
|
102
103
|
"public"
|