vyrn 1.8.5 → 1.8.6
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/components/ClientToastProvider.d.ts +1 -0
- package/dist/hooks/useToast.d.ts +8 -6
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +3 -0
- package/package.json +1 -1
- package/src/components/ClientToastProvider.tsx +3 -2
- package/src/components/Toast.tsx +10 -1
- package/src/components/ToastContainer.tsx +16 -3
- package/src/components/ToastProvider.tsx +10 -6
- package/src/hooks/useToast.ts +11 -7
- package/src/styles/toast.css +38 -11
- package/src/types/index.ts +3 -0
package/dist/hooks/useToast.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { ToastType } from '../types';
|
|
1
|
+
import { ToastType, ToastProps } from '../types';
|
|
2
2
|
export declare const useToast: () => {
|
|
3
|
-
toast: (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
toast: (options: Omit<ToastProps, 'id' | 'type'> & {
|
|
4
|
+
type?: ToastType;
|
|
5
|
+
}) => void;
|
|
6
|
+
info: (message: string, options?: Omit<ToastProps, 'id' | 'type' | 'message'>) => void;
|
|
7
|
+
success: (message: string, options?: Omit<ToastProps, 'id' | 'type' | 'message'>) => void;
|
|
8
|
+
warning: (message: string, options?: Omit<ToastProps, 'id' | 'type' | 'message'>) => void;
|
|
9
|
+
error: (message: string, options?: Omit<ToastProps, 'id' | 'type' | 'message'>) => void;
|
|
8
10
|
position: import("../types").ToastPosition;
|
|
9
11
|
};
|
package/dist/index.esm.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import t,{useEffect as e,useState as r,useCallback as n}from"react";import{X as o,AlertTriangle as a,Info as
|
|
1
|
+
import t,{useEffect as e,useState as r,useCallback as n}from"react";import{X as o,AlertTriangle as a,Info as s,AlertCircle as i,CheckCircle as c}from"lucide-react";var l=function(){return l=Object.assign||function(t){for(var e,r=1,n=arguments.length;r<n;r++)for(var o in e=arguments[r])Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=e[o]);return t},l.apply(this,arguments)};function m(t,e,r){if(r||2===arguments.length)for(var n,o=0,a=e.length;o<a;o++)!n&&o in e||(n||(n=Array.prototype.slice.call(e,0,o)),n[o]=e[o]);return t.concat(n||Array.prototype.slice.call(e))}"function"==typeof SuppressedError&&SuppressedError;var u=t.createContext(void 0),d=function(){var e=t.useContext(u);if(void 0===e)throw new Error("useToastContext must be used within a ToastProvider");return e},f=function(r){var n=r.id,l=r.message,m=r.type,u=r.duration,f=void 0===u?3e3:u,p=r.title,v=r.description,y=d().removeToast;e((function(){var t=setTimeout((function(){y(n)}),f);return function(){return clearTimeout(t)}}),[n,f,y]);return t.createElement("div",{className:"vyrn-toast vyrn-toast-".concat(m),role:"alert"},function(){switch(m){case"success":return t.createElement(c,{className:"vyrn-toast-icon"});case"error":return t.createElement(i,{className:"vyrn-toast-icon"});case"info":return t.createElement(s,{className:"vyrn-toast-icon"});case"warning":return t.createElement(a,{className:"vyrn-toast-icon"});default:return null}}(),t.createElement("div",{className:"vyrn-toast-content"},p&&t.createElement("div",{className:"vyrn-toast-title"},p),t.createElement("div",{className:"vyrn-toast-message"},l),v&&t.createElement("div",{className:"vyrn-toast-description"},v)),t.createElement("button",{className:"vyrn-toast-close",onClick:function(){return y(n)},"aria-label":"Close"},t.createElement(o,{size:18})))},p=function(e){var n=e.toasts,o=e.position,a=r(null),s=a[0],i=a[1];return t.createElement("div",{className:"vyrn-toast-container vyrn-toast-".concat(o),"aria-live":"polite","aria-atomic":"true"},n.map((function(e,r){return t.createElement("div",{key:e.id,className:"vyrn-toast-wrapper ".concat(r===s?"vyrn-toast-hovered":""),style:{zIndex:n.length-r,transform:"translateY(".concat(r*(null!==s&&r>s?100:10),"%)")},onMouseEnter:function(){return i(r)},onMouseLeave:function(){return i(null)}},t.createElement(f,l({},e)))})))};!function(t,e){void 0===e&&(e={});var r=e.insertAt;if(t&&"undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],o=document.createElement("style");o.type="text/css","top"===r&&n.firstChild?n.insertBefore(o,n.firstChild):n.appendChild(o),o.styleSheet?o.styleSheet.cssText=t:o.appendChild(document.createTextNode(t))}}(".vyrn-toast-container{display:flex;flex-direction:column;gap:.5rem;max-width:380px;pointer-events:none;position:fixed;width:100%;z-index:9999}.vyrn-toast-top-right{right:1rem;top:1rem}.vyrn-toast-top-left{left:1rem;top:1rem}.vyrn-toast-bottom-right{bottom:1rem;right:1rem}.vyrn-toast-bottom-left{bottom:1rem;left:1rem}.vyrn-toast-top-center{left:50%;top:1rem;transform:translateX(-50%)}.vyrn-toast-bottom-center{bottom:1rem;left:50%;transform:translateX(-50%)}.vyrn-toast-wrapper{pointer-events:auto;transition:all .3s ease-out}.vyrn-toast{align-items:flex-start;animation:vyrnSlideIn .3s ease-out;background:#fff;border-left:4px solid;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.15);display:flex;max-width:100%;padding:1rem}.vyrn-toast-icon{flex-shrink:0;height:20px;margin-right:.75rem;width:20px}.vyrn-toast-content{flex-grow:1;margin-right:.75rem}.vyrn-toast-title{font-size:1rem;font-weight:600;margin-bottom:.25rem}.vyrn-toast-message{font-size:.875rem;line-height:1.25rem}.vyrn-toast-description{font-size:.75rem;margin-top:.25rem;opacity:.8}.vyrn-toast-close{align-items:center;background:none;border:none;color:inherit;cursor:pointer;display:flex;justify-content:center;opacity:.7;padding:0;transition:opacity .2s}.vyrn-toast-close:hover{opacity:1}.vyrn-toast-info{border-color:#3498db;color:#2980b9}.vyrn-toast-success{border-color:#2ecc71;color:#27ae60}.vyrn-toast-warning{border-color:#f39c12;color:#f39c12}.vyrn-toast-error{border-color:#e74c3c;color:#c0392b}.vyrn-toast-hovered{box-shadow:0 8px 16px rgba(0,0,0,.2);transform:scale(1.02)!important}@keyframes vyrnSlideIn{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes vyrnFadeOut{0%{opacity:1}to{opacity:0}}.vyrn-toast-exit{animation:vyrnFadeOut .3s ease-in forwards}",{insertAt:"top"});var v=function(o){var a=o.children,s=o.position,i=void 0===s?"top-right":s,c=o.maxToasts,d=void 0===c?5:c,f=r([]),v=f[0],y=f[1],g=r([]),h=g[0],x=g[1],b=n((function(t){var e=Math.random().toString(36).substr(2,9);v.length<d?y((function(r){return m(m([],r,!0),[l(l({},t),{id:e})],!1)})):x((function(e){return m(m([],e,!0),[t],!1)}))}),[v,d]),E=n((function(t){y((function(e){return e.filter((function(e){return e.id!==t}))}))}),[]);return e((function(){if(v.length<d&&h.length>0){var t=h[0],e=h.slice(1);b(t),x(e)}}),[v,h,b,d]),t.createElement(u.Provider,{value:{addToast:b,removeToast:E,position:i}},a,t.createElement(p,{toasts:v,position:i}))},y=function(){var t=d(),e=t.addToast,r=t.position,o=n((function(t){e(l(l({},t),{type:t.type||"info"}))}),[e]);return{toast:o,info:function(t,e){return o(l({message:t,type:"info"},e))},success:function(t,e){return o(l({message:t,type:"success"},e))},warning:function(t,e){return o(l({message:t,type:"warning"},e))},error:function(t,e){return o(l({message:t,type:"error"},e))},position:r}},g=function(e){var r=e.children,n=e.position,o=e.maxToasts;return t.createElement(v,{position:n,maxToasts:o},r)};export{g as ClientToastProvider,v as ToastProvider,y as useToast};
|
|
2
2
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":["css","ref","insertAt","document","head","getElementsByTagName","style","createElement","type","firstChild","insertBefore","appendChild","styleSheet","cssText","createTextNode"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":["css","ref","insertAt","document","head","getElementsByTagName","style","createElement","type","firstChild","insertBefore","appendChild","styleSheet","cssText","createTextNode"],"mappings":"ixEAAA,SAAqBA,EAAKC,QACX,IAARA,IAAiBA,EAAM,CAAA,GAC5B,IAAIC,EAAWD,EAAIC,SAEnB,GAAKF,GAA2B,oBAAbG,SAAnB,CAEA,IAAIC,EAAOD,SAASC,MAAQD,SAASE,qBAAqB,QAAQ,GAC9DC,EAAQH,SAASI,cAAc,SACnCD,EAAME,KAAO,WAEI,QAAbN,GACEE,EAAKK,WACPL,EAAKM,aAAaJ,EAAOF,EAAKK,YAKhCL,EAAKO,YAAYL,GAGfA,EAAMM,WACRN,EAAMM,WAAWC,QAAUb,EAE3BM,EAAMK,YAAYR,SAASW,eAAed,GAnBY,CAqB1D"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t=require("react"),e=require("lucide-react");function r(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var n=r(t),o=function(){return o=Object.assign||function(t){for(var e,r=1,n=arguments.length;r<n;r++)for(var o in e=arguments[r])Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=e[o]);return t},o.apply(this,arguments)};function a(t,e,r){if(r||2===arguments.length)for(var n,o=0,a=e.length;o<a;o++)!n&&o in e||(n||(n=Array.prototype.slice.call(e,0,o)),n[o]=e[o]);return t.concat(n||Array.prototype.slice.call(e))}"function"==typeof SuppressedError&&SuppressedError;var
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t=require("react"),e=require("lucide-react");function r(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var n=r(t),o=function(){return o=Object.assign||function(t){for(var e,r=1,n=arguments.length;r<n;r++)for(var o in e=arguments[r])Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=e[o]);return t},o.apply(this,arguments)};function a(t,e,r){if(r||2===arguments.length)for(var n,o=0,a=e.length;o<a;o++)!n&&o in e||(n||(n=Array.prototype.slice.call(e,0,o)),n[o]=e[o]);return t.concat(n||Array.prototype.slice.call(e))}"function"==typeof SuppressedError&&SuppressedError;var s=n.default.createContext(void 0),i=function(){var t=n.default.useContext(s);if(void 0===t)throw new Error("useToastContext must be used within a ToastProvider");return t},c=function(r){var o=r.id,a=r.message,s=r.type,c=r.duration,l=void 0===c?3e3:c,u=r.title,f=r.description,d=i().removeToast;t.useEffect((function(){var t=setTimeout((function(){d(o)}),l);return function(){return clearTimeout(t)}}),[o,l,d]);return n.default.createElement("div",{className:"vyrn-toast vyrn-toast-".concat(s),role:"alert"},function(){switch(s){case"success":return n.default.createElement(e.CheckCircle,{className:"vyrn-toast-icon"});case"error":return n.default.createElement(e.AlertCircle,{className:"vyrn-toast-icon"});case"info":return n.default.createElement(e.Info,{className:"vyrn-toast-icon"});case"warning":return n.default.createElement(e.AlertTriangle,{className:"vyrn-toast-icon"});default:return null}}(),n.default.createElement("div",{className:"vyrn-toast-content"},u&&n.default.createElement("div",{className:"vyrn-toast-title"},u),n.default.createElement("div",{className:"vyrn-toast-message"},a),f&&n.default.createElement("div",{className:"vyrn-toast-description"},f)),n.default.createElement("button",{className:"vyrn-toast-close",onClick:function(){return d(o)},"aria-label":"Close"},n.default.createElement(e.X,{size:18})))},l=function(e){var r=e.toasts,a=e.position,s=t.useState(null),i=s[0],l=s[1];return n.default.createElement("div",{className:"vyrn-toast-container vyrn-toast-".concat(a),"aria-live":"polite","aria-atomic":"true"},r.map((function(t,e){return n.default.createElement("div",{key:t.id,className:"vyrn-toast-wrapper ".concat(e===i?"vyrn-toast-hovered":""),style:{zIndex:r.length-e,transform:"translateY(".concat(e*(null!==i&&e>i?100:10),"%)")},onMouseEnter:function(){return l(e)},onMouseLeave:function(){return l(null)}},n.default.createElement(c,o({},t)))})))};!function(t,e){void 0===e&&(e={});var r=e.insertAt;if(t&&"undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],o=document.createElement("style");o.type="text/css","top"===r&&n.firstChild?n.insertBefore(o,n.firstChild):n.appendChild(o),o.styleSheet?o.styleSheet.cssText=t:o.appendChild(document.createTextNode(t))}}(".vyrn-toast-container{display:flex;flex-direction:column;gap:.5rem;max-width:380px;pointer-events:none;position:fixed;width:100%;z-index:9999}.vyrn-toast-top-right{right:1rem;top:1rem}.vyrn-toast-top-left{left:1rem;top:1rem}.vyrn-toast-bottom-right{bottom:1rem;right:1rem}.vyrn-toast-bottom-left{bottom:1rem;left:1rem}.vyrn-toast-top-center{left:50%;top:1rem;transform:translateX(-50%)}.vyrn-toast-bottom-center{bottom:1rem;left:50%;transform:translateX(-50%)}.vyrn-toast-wrapper{pointer-events:auto;transition:all .3s ease-out}.vyrn-toast{align-items:flex-start;animation:vyrnSlideIn .3s ease-out;background:#fff;border-left:4px solid;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.15);display:flex;max-width:100%;padding:1rem}.vyrn-toast-icon{flex-shrink:0;height:20px;margin-right:.75rem;width:20px}.vyrn-toast-content{flex-grow:1;margin-right:.75rem}.vyrn-toast-title{font-size:1rem;font-weight:600;margin-bottom:.25rem}.vyrn-toast-message{font-size:.875rem;line-height:1.25rem}.vyrn-toast-description{font-size:.75rem;margin-top:.25rem;opacity:.8}.vyrn-toast-close{align-items:center;background:none;border:none;color:inherit;cursor:pointer;display:flex;justify-content:center;opacity:.7;padding:0;transition:opacity .2s}.vyrn-toast-close:hover{opacity:1}.vyrn-toast-info{border-color:#3498db;color:#2980b9}.vyrn-toast-success{border-color:#2ecc71;color:#27ae60}.vyrn-toast-warning{border-color:#f39c12;color:#f39c12}.vyrn-toast-error{border-color:#e74c3c;color:#c0392b}.vyrn-toast-hovered{box-shadow:0 8px 16px rgba(0,0,0,.2);transform:scale(1.02)!important}@keyframes vyrnSlideIn{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes vyrnFadeOut{0%{opacity:1}to{opacity:0}}.vyrn-toast-exit{animation:vyrnFadeOut .3s ease-in forwards}",{insertAt:"top"});var u=function(e){var r=e.children,i=e.position,c=void 0===i?"top-right":i,u=e.maxToasts,f=void 0===u?5:u,d=t.useState([]),m=d[0],p=d[1],v=t.useState([]),y=v[0],g=v[1],h=t.useCallback((function(t){var e=Math.random().toString(36).substr(2,9);m.length<f?p((function(r){return a(a([],r,!0),[o(o({},t),{id:e})],!1)})):g((function(e){return a(a([],e,!0),[t],!1)}))}),[m,f]),x=t.useCallback((function(t){p((function(e){return e.filter((function(e){return e.id!==t}))}))}),[]);return t.useEffect((function(){if(m.length<f&&y.length>0){var t=y[0],e=y.slice(1);h(t),g(e)}}),[m,y,h,f]),n.default.createElement(s.Provider,{value:{addToast:h,removeToast:x,position:c}},r,n.default.createElement(l,{toasts:m,position:c}))};exports.ClientToastProvider=function(t){var e=t.children,r=t.position,o=t.maxToasts;return n.default.createElement(u,{position:r,maxToasts:o},e)},exports.ToastProvider=u,exports.useToast=function(){var e=i(),r=e.addToast,n=e.position,a=t.useCallback((function(t){r(o(o({},t),{type:t.type||"info"}))}),[r]);return{toast:a,info:function(t,e){return a(o({message:t,type:"info"},e))},success:function(t,e){return a(o({message:t,type:"success"},e))},warning:function(t,e){return a(o({message:t,type:"warning"},e))},error:function(t,e){return a(o({message:t,type:"error"},e))},position:n}};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":["css","ref","insertAt","document","head","getElementsByTagName","style","createElement","type","firstChild","insertBefore","appendChild","styleSheet","cssText","createTextNode"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":["css","ref","insertAt","document","head","getElementsByTagName","style","createElement","type","firstChild","insertBefore","appendChild","styleSheet","cssText","createTextNode"],"mappings":"i/EAAA,SAAqBA,EAAKC,QACX,IAARA,IAAiBA,EAAM,CAAA,GAC5B,IAAIC,EAAWD,EAAIC,SAEnB,GAAKF,GAA2B,oBAAbG,SAAnB,CAEA,IAAIC,EAAOD,SAASC,MAAQD,SAASE,qBAAqB,QAAQ,GAC9DC,EAAQH,SAASI,cAAc,SACnCD,EAAME,KAAO,WAEI,QAAbN,GACEE,EAAKK,WACPL,EAAKM,aAAaJ,EAAOF,EAAKK,YAKhCL,EAAKO,YAAYL,GAGfA,EAAMM,WACRN,EAAMM,WAAWC,QAAUb,EAE3BM,EAAMK,YAAYR,SAASW,eAAed,GAnBY,CAqB1D"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export interface ToastProps {
|
|
|
6
6
|
message: string;
|
|
7
7
|
type: ToastType;
|
|
8
8
|
duration?: number;
|
|
9
|
+
title?: string;
|
|
10
|
+
description?: string;
|
|
9
11
|
}
|
|
10
12
|
export interface ToastContextValue {
|
|
11
13
|
addToast: (toast: Omit<ToastProps, 'id'>) => void;
|
|
@@ -15,4 +17,5 @@ export interface ToastContextValue {
|
|
|
15
17
|
export interface ToastProviderProps {
|
|
16
18
|
children: React.ReactNode;
|
|
17
19
|
position?: ToastPosition;
|
|
20
|
+
maxToasts?: number;
|
|
18
21
|
}
|
package/package.json
CHANGED
|
@@ -7,9 +7,10 @@ import { ToastPosition } from '../types';
|
|
|
7
7
|
interface ClientToastProviderProps {
|
|
8
8
|
children: React.ReactNode;
|
|
9
9
|
position?: ToastPosition;
|
|
10
|
+
maxToasts?: number;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
export const ClientToastProvider: React.FC<ClientToastProviderProps> = ({ children, position }) => {
|
|
13
|
-
return <ToastProvider position={position}>{children}</ToastProvider>;
|
|
13
|
+
export const ClientToastProvider: React.FC<ClientToastProviderProps> = ({ children, position, maxToasts }) => {
|
|
14
|
+
return <ToastProvider position={position} maxToasts={maxToasts}>{children}</ToastProvider>;
|
|
14
15
|
};
|
|
15
16
|
|
package/src/components/Toast.tsx
CHANGED
|
@@ -6,7 +6,14 @@ import { useToastContext } from '../context/ToastContext';
|
|
|
6
6
|
import { DEFAULT_DURATION } from '../utils/constants';
|
|
7
7
|
import { X, CheckCircle, AlertCircle, Info, AlertTriangle } from 'lucide-react';
|
|
8
8
|
|
|
9
|
-
export const Toast: React.FC<ToastProps> = ({
|
|
9
|
+
export const Toast: React.FC<ToastProps> = ({
|
|
10
|
+
id,
|
|
11
|
+
message,
|
|
12
|
+
type,
|
|
13
|
+
duration = DEFAULT_DURATION,
|
|
14
|
+
title,
|
|
15
|
+
description
|
|
16
|
+
}) => {
|
|
10
17
|
const { removeToast } = useToastContext();
|
|
11
18
|
|
|
12
19
|
useEffect(() => {
|
|
@@ -36,7 +43,9 @@ export const Toast: React.FC<ToastProps> = ({ id, message, type, duration = DEFA
|
|
|
36
43
|
<div className={`vyrn-toast vyrn-toast-${type}`} role="alert">
|
|
37
44
|
{getIcon()}
|
|
38
45
|
<div className="vyrn-toast-content">
|
|
46
|
+
{title && <div className="vyrn-toast-title">{title}</div>}
|
|
39
47
|
<div className="vyrn-toast-message">{message}</div>
|
|
48
|
+
{description && <div className="vyrn-toast-description">{description}</div>}
|
|
40
49
|
</div>
|
|
41
50
|
<button className="vyrn-toast-close" onClick={() => removeToast(id)} aria-label="Close">
|
|
42
51
|
<X size={18} />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React from 'react';
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
4
|
import { Toast } from './Toast';
|
|
5
5
|
import { ToastProps, ToastPosition } from '../types';
|
|
6
6
|
|
|
@@ -10,10 +10,23 @@ interface ToastContainerProps {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export const ToastContainer: React.FC<ToastContainerProps> = ({ toasts, position }) => {
|
|
13
|
+
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
|
14
|
+
|
|
13
15
|
return (
|
|
14
16
|
<div className={`vyrn-toast-container vyrn-toast-${position}`} aria-live="polite" aria-atomic="true">
|
|
15
|
-
{toasts.map((toast) => (
|
|
16
|
-
<
|
|
17
|
+
{toasts.map((toast, index) => (
|
|
18
|
+
<div
|
|
19
|
+
key={toast.id}
|
|
20
|
+
className={`vyrn-toast-wrapper ${index === hoveredIndex ? 'vyrn-toast-hovered' : ''}`}
|
|
21
|
+
style={{
|
|
22
|
+
zIndex: toasts.length - index,
|
|
23
|
+
transform: `translateY(${index * (hoveredIndex !== null && index > hoveredIndex ? 100 : 10)}%)`,
|
|
24
|
+
}}
|
|
25
|
+
onMouseEnter={() => setHoveredIndex(index)}
|
|
26
|
+
onMouseLeave={() => setHoveredIndex(null)}
|
|
27
|
+
>
|
|
28
|
+
<Toast {...toast} />
|
|
29
|
+
</div>
|
|
17
30
|
))}
|
|
18
31
|
</div>
|
|
19
32
|
);
|
|
@@ -3,34 +3,38 @@
|
|
|
3
3
|
import React, { useState, useCallback, useEffect } from 'react';
|
|
4
4
|
import { ToastContext } from '../context/ToastContext';
|
|
5
5
|
import { ToastContainer } from './ToastContainer';
|
|
6
|
-
import { ToastProps, ToastProviderProps
|
|
6
|
+
import { ToastProps, ToastProviderProps } from '../types';
|
|
7
7
|
import { MAX_TOASTS } from '../utils/constants';
|
|
8
8
|
import '../styles/toast.css';
|
|
9
9
|
|
|
10
|
-
export const ToastProvider: React.FC<ToastProviderProps> = ({
|
|
10
|
+
export const ToastProvider: React.FC<ToastProviderProps> = ({
|
|
11
|
+
children,
|
|
12
|
+
position = 'top-right',
|
|
13
|
+
maxToasts = MAX_TOASTS
|
|
14
|
+
}) => {
|
|
11
15
|
const [toasts, setToasts] = useState<ToastProps[]>([]);
|
|
12
16
|
const [queue, setQueue] = useState<Omit<ToastProps, 'id'>[]>([]);
|
|
13
17
|
|
|
14
18
|
const addToast = useCallback((toast: Omit<ToastProps, 'id'>) => {
|
|
15
19
|
const id = Math.random().toString(36).substr(2, 9);
|
|
16
|
-
if (toasts.length <
|
|
20
|
+
if (toasts.length < maxToasts) {
|
|
17
21
|
setToasts((prevToasts) => [...prevToasts, { ...toast, id }]);
|
|
18
22
|
} else {
|
|
19
23
|
setQueue((prevQueue) => [...prevQueue, toast]);
|
|
20
24
|
}
|
|
21
|
-
}, [toasts]);
|
|
25
|
+
}, [toasts, maxToasts]);
|
|
22
26
|
|
|
23
27
|
const removeToast = useCallback((id: string) => {
|
|
24
28
|
setToasts((prevToasts) => prevToasts.filter((toast) => toast.id !== id));
|
|
25
29
|
}, []);
|
|
26
30
|
|
|
27
31
|
useEffect(() => {
|
|
28
|
-
if (toasts.length <
|
|
32
|
+
if (toasts.length < maxToasts && queue.length > 0) {
|
|
29
33
|
const [nextToast, ...restQueue] = queue;
|
|
30
34
|
addToast(nextToast);
|
|
31
35
|
setQueue(restQueue);
|
|
32
36
|
}
|
|
33
|
-
}, [toasts, queue, addToast]);
|
|
37
|
+
}, [toasts, queue, addToast, maxToasts]);
|
|
34
38
|
|
|
35
39
|
return (
|
|
36
40
|
<ToastContext.Provider value={{ addToast, removeToast, position }}>
|
package/src/hooks/useToast.ts
CHANGED
|
@@ -2,24 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback } from 'react';
|
|
4
4
|
import { useToastContext } from '../context/ToastContext';
|
|
5
|
-
import { ToastType } from '../types';
|
|
5
|
+
import { ToastType, ToastProps } from '../types';
|
|
6
6
|
|
|
7
7
|
export const useToast = () => {
|
|
8
8
|
const { addToast, position } = useToastContext();
|
|
9
9
|
|
|
10
10
|
const toast = useCallback(
|
|
11
|
-
(
|
|
12
|
-
addToast({
|
|
11
|
+
(options: Omit<ToastProps, 'id' | 'type'> & { type?: ToastType }) => {
|
|
12
|
+
addToast({ ...options, type: options.type || 'info' });
|
|
13
13
|
},
|
|
14
14
|
[addToast]
|
|
15
15
|
);
|
|
16
16
|
|
|
17
17
|
return {
|
|
18
18
|
toast,
|
|
19
|
-
info: (message: string,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
info: (message: string, options?: Omit<ToastProps, 'id' | 'type' | 'message'>) =>
|
|
20
|
+
toast({ message, type: 'info', ...options }),
|
|
21
|
+
success: (message: string, options?: Omit<ToastProps, 'id' | 'type' | 'message'>) =>
|
|
22
|
+
toast({ message, type: 'success', ...options }),
|
|
23
|
+
warning: (message: string, options?: Omit<ToastProps, 'id' | 'type' | 'message'>) =>
|
|
24
|
+
toast({ message, type: 'warning', ...options }),
|
|
25
|
+
error: (message: string, options?: Omit<ToastProps, 'id' | 'type' | 'message'>) =>
|
|
26
|
+
toast({ message, type: 'error', ...options }),
|
|
23
27
|
position,
|
|
24
28
|
};
|
|
25
29
|
};
|
package/src/styles/toast.css
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
display: flex;
|
|
5
5
|
flex-direction: column;
|
|
6
6
|
gap: 0.5rem;
|
|
7
|
-
max-width:
|
|
7
|
+
max-width: 380px;
|
|
8
8
|
width: 100%;
|
|
9
|
+
pointer-events: none;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
.vyrn-toast-top-right {
|
|
@@ -40,19 +41,28 @@
|
|
|
40
41
|
transform: translateX(-50%);
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
.vyrn-toast-wrapper {
|
|
45
|
+
transition: all 0.3s ease-out;
|
|
46
|
+
pointer-events: auto;
|
|
47
|
+
}
|
|
48
|
+
|
|
43
49
|
.vyrn-toast {
|
|
44
50
|
display: flex;
|
|
45
|
-
align-items:
|
|
46
|
-
padding:
|
|
51
|
+
align-items: flex-start;
|
|
52
|
+
padding: 1rem;
|
|
47
53
|
border-radius: 8px;
|
|
48
54
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
49
55
|
animation: vyrnSlideIn 0.3s ease-out;
|
|
50
56
|
max-width: 100%;
|
|
57
|
+
background: #fff;
|
|
58
|
+
border-left: 4px solid;
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
.vyrn-toast-icon {
|
|
54
62
|
flex-shrink: 0;
|
|
55
63
|
margin-right: 0.75rem;
|
|
64
|
+
width: 20px;
|
|
65
|
+
height: 20px;
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
.vyrn-toast-content {
|
|
@@ -60,11 +70,23 @@
|
|
|
60
70
|
margin-right: 0.75rem;
|
|
61
71
|
}
|
|
62
72
|
|
|
73
|
+
.vyrn-toast-title {
|
|
74
|
+
font-weight: 600;
|
|
75
|
+
font-size: 1rem;
|
|
76
|
+
margin-bottom: 0.25rem;
|
|
77
|
+
}
|
|
78
|
+
|
|
63
79
|
.vyrn-toast-message {
|
|
64
80
|
font-size: 0.875rem;
|
|
65
81
|
line-height: 1.25rem;
|
|
66
82
|
}
|
|
67
83
|
|
|
84
|
+
.vyrn-toast-description {
|
|
85
|
+
font-size: 0.75rem;
|
|
86
|
+
margin-top: 0.25rem;
|
|
87
|
+
opacity: 0.8;
|
|
88
|
+
}
|
|
89
|
+
|
|
68
90
|
.vyrn-toast-close {
|
|
69
91
|
background: none;
|
|
70
92
|
border: none;
|
|
@@ -83,23 +105,28 @@
|
|
|
83
105
|
}
|
|
84
106
|
|
|
85
107
|
.vyrn-toast-info {
|
|
86
|
-
|
|
87
|
-
color: #
|
|
108
|
+
border-color: #3498db;
|
|
109
|
+
color: #2980b9;
|
|
88
110
|
}
|
|
89
111
|
|
|
90
112
|
.vyrn-toast-success {
|
|
91
|
-
|
|
92
|
-
color: #
|
|
113
|
+
border-color: #2ecc71;
|
|
114
|
+
color: #27ae60;
|
|
93
115
|
}
|
|
94
116
|
|
|
95
117
|
.vyrn-toast-warning {
|
|
96
|
-
|
|
97
|
-
color: #
|
|
118
|
+
border-color: #f39c12;
|
|
119
|
+
color: #f39c12;
|
|
98
120
|
}
|
|
99
121
|
|
|
100
122
|
.vyrn-toast-error {
|
|
101
|
-
|
|
102
|
-
color: #
|
|
123
|
+
border-color: #e74c3c;
|
|
124
|
+
color: #c0392b;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.vyrn-toast-hovered {
|
|
128
|
+
transform: scale(1.02) !important;
|
|
129
|
+
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
|
|
103
130
|
}
|
|
104
131
|
|
|
105
132
|
@keyframes vyrnSlideIn {
|
package/src/types/index.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface ToastProps {
|
|
|
7
7
|
message: string;
|
|
8
8
|
type: ToastType;
|
|
9
9
|
duration?: number;
|
|
10
|
+
title?: string;
|
|
11
|
+
description?: string;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
export interface ToastContextValue {
|
|
@@ -18,5 +20,6 @@ export interface ToastContextValue {
|
|
|
18
20
|
export interface ToastProviderProps {
|
|
19
21
|
children: React.ReactNode;
|
|
20
22
|
position?: ToastPosition;
|
|
23
|
+
maxToasts?: number;
|
|
21
24
|
}
|
|
22
25
|
|