@oscarrc/crust 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Oscar Rey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ var G=Symbol.for("crust.store"),W=globalThis,n=W[G]??={toasts:[],queue:[],listeners:new Set,timers:new Map,expandTimers:new Map,maxVisible:5,seq:0};n.expandTimers??=new Map;var E=()=>{for(let e of n.listeners)e(n.toasts)},A=e=>{Number.isFinite(e.duration)&&n.timers.set(e.id,{timeoutId:setTimeout(()=>h(e.id),e.duration),deadline:Date.now()+e.duration,remaining:e.duration})},q=e=>{let t=n.timers.get(e);t?.timeoutId!=null&&clearTimeout(t.timeoutId),n.timers.delete(e)},P=e=>{let t=n.expandTimers.get(e);t!=null&&clearTimeout(t),n.expandTimers.delete(e)},R=e=>{!e.title||e.expandAfter===void 0||Number.isFinite(e.expandAfter)&&(P(e.id),n.expandTimers.set(e.id,setTimeout(()=>{n.expandTimers.delete(e.id),M(e.id,{expanded:!0})},e.expandAfter)))},_=()=>{for(;n.queue.length>0&&n.toasts.length<n.maxVisible;){let e=n.queue.shift();n.toasts=[...n.toasts,e],A(e),R(e)}},V=e=>e===void 0?4e3:e===0?1/0:e,v=(e,t)=>{let r=`crust-${++n.seq}`,l=V(t?.duration),u={id:r,message:e,title:t?.title,type:t?.type??"info",duration:l,icon:t?.icon,expanded:t?.expanded,expandAfter:t?.expandAfter};return n.toasts.length<n.maxVisible?(n.toasts=[...n.toasts,u],A(u),R(u),E()):n.queue.push(u),r},h=e=>{q(e),P(e);let t=n.queue.findIndex(r=>r.id===e);if(t!==-1){n.queue.splice(t,1);return}n.toasts.some(r=>r.id===e)&&(n.toasts=n.toasts.filter(r=>r.id!==e),_(),E())},Y=()=>{for(let e of[...n.timers.keys()])q(e);for(let e of[...n.expandTimers.keys()])P(e);n.queue.length=0,n.toasts.length!==0&&(n.toasts=[],E())},M=(e,t)=>{let r=c=>({...c,...t,...t.duration!==void 0?{duration:V(t.duration)}:{}}),l=n.queue.findIndex(c=>c.id===e);if(l!==-1){n.queue[l]=r(n.queue[l]);return}let u=n.toasts.find(c=>c.id===e);if(!u)return;let p=r(u);if(n.toasts=n.toasts.map(c=>c.id===e?p:c),t.duration!==void 0||t.expanded===!0){let c=n.timers.get(e),T=c!==void 0&&c.timeoutId==null;q(e),T&&Number.isFinite(p.duration)?n.timers.set(e,{timeoutId:null,deadline:0,remaining:p.duration}):A(p)}E()},w=e=>{let t=n.timers.get(e);!t||t.timeoutId==null||(clearTimeout(t.timeoutId),t.timeoutId=null,t.remaining=Math.max(0,t.deadline-Date.now()))},L=e=>{let t=n.timers.get(e);!t||t.timeoutId!=null||(t.deadline=Date.now()+t.remaining,t.timeoutId=setTimeout(()=>h(e),t.remaining))},F=e=>{e.maxVisible!==void 0&&(n.maxVisible=Math.max(1,e.maxVisible),_(),E())},B={subscribe:e=>(n.listeners.add(e),()=>{n.listeners.delete(e)}),getSnapshot:()=>n.toasts,add:v,remove:h,update:M,pause:w,resume:L,configure:F},N=e=>typeof e=="string"?{message:e}:e,D=(e,t)=>typeof e=="function"?e(t):e,C=e=>(t,r)=>v(t,{...r,type:e}),te=Object.assign((e,t)=>v(e,t),{success:C("success"),error:C("error"),info:C("info"),warning:C("warning"),loading:(e,t)=>v(e,{duration:1/0,...t,type:"loading"}),update:(e,t)=>M(e,t),promise:(e,t,r)=>{let{expandOnSettle:l,...u}=r??{},p=v(N(t.loading).message,{...u,title:N(t.loading).title??u.title,type:"loading",duration:1/0}),c=(T,x)=>M(p,{title:void 0,...N(x),type:T,duration:V(u.duration),...l?{expanded:!0}:{}});return e.then(T=>c("success",D(t.success,T))).catch(T=>c("error",D(t.error,T))),p},dismiss:e=>e===void 0?Y():h(e)}),z={success:'<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path class="crust-draw" d="M4.5 12.5l5 5L19.5 7" pathLength="1"/></svg>',error:'<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path class="crust-draw" d="M6.5 6.5l11 11M17.5 6.5l-11 11" pathLength="1"/></svg>',info:'<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle class="crust-draw" cx="12" cy="12" r="9" pathLength="1"/><path class="crust-draw" d="M12 11v5" pathLength="1"/><path class="crust-draw" d="M12 8v.01" pathLength="1"/></svg>',warning:'<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path class="crust-draw" d="M12 3.5 21.5 20h-19Z" pathLength="1"/><path class="crust-draw" d="M12 10v4" pathLength="1"/><path class="crust-draw" d="M12 17v.01" pathLength="1"/></svg>',loading:'<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path class="crust-spin" d="M12 3a9 9 0 1 1-8.6 6.3"/></svg>'},U='<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 7l10 10M17 7L7 17"/></svg>',X=450,Z=320,J=50,Q=e=>{let t=document.createElement("template");return t.innerHTML=e.trim(),t.content.firstElementChild},ee=(e,t)=>{let r=e.icon!==void 0?e.icon:t&&t[e.type]!==void 0?t[e.type]:z[e.type];return r==null?null:typeof r=="string"?Q(r):typeof r=="function"?r():r.cloneNode(!0)},y=null,ne=(e={})=>{if(typeof document>"u")throw new Error("[crust] mountToaster needs a DOM \u2014 call it from client-side code only.");if(y)return y;let t=e.position??"bottom-right",r=t.startsWith("top");e.maxVisible!==void 0&&F({maxVisible:e.maxVisible});let l=document.createElement("section");l.className=`crust-region crust-pos-${t}`,l.setAttribute("role","status"),l.setAttribute("aria-live","polite"),l.setAttribute("aria-label","Notifications"),document.body.appendChild(l);let u=()=>{l.isConnected||document.body.appendChild(l)};document.addEventListener("astro:after-swap",u);let p=new Map,c=i=>{let a=!!i.title,s=document.createElement("div");s.className=`crust-toast crust-${i.type}${a?" crust-expandable":""}`,s.dataset.id=i.id;let o=document.createElement("div");o.className="crust-capsule";let f=ee(i,e.icons);if(f){let g=document.createElement("span");g.className="crust-icon",g.appendChild(f),o.appendChild(g)}let d=document.createElement("span");d.className="crust-title",d.textContent=i.title??i.message,o.appendChild(d);let m=document.createElement("button");if(m.type="button",m.className="crust-dismiss",m.setAttribute("aria-label","Dismiss notification"),m.innerHTML=U,m.addEventListener("click",g=>{g.stopPropagation(),h(i.id)}),o.appendChild(m),s.appendChild(o),a){let g=document.createElement("div");g.className="crust-body";let S=document.createElement("div");S.className="crust-body-inner";let O=document.createElement("p");O.className="crust-msg",O.textContent=i.message,S.appendChild(O),g.appendChild(S),s.appendChild(g)}let b=()=>a&&s.classList.add("crust-expanded"),k=()=>{s.dataset.pinned||s.classList.remove("crust-expanded")};return s.addEventListener("mouseenter",()=>{w(i.id),b()}),s.addEventListener("mouseleave",()=>{s.dataset.pinned||(k(),L(i.id))}),s.addEventListener("focusin",()=>{w(i.id),b()}),s.addEventListener("focusout",g=>{s.contains(g.relatedTarget)||s.dataset.pinned||(k(),L(i.id))}),s.addEventListener("click",()=>{if(!a){h(i.id);return}s.dataset.pinned?(delete s.dataset.pinned,k(),L(i.id)):(s.dataset.pinned="1",w(i.id),b())}),s},T=i=>{let a=document.createElement("div");a.className="crust-cell";let s=document.createElement("div");s.className="crust-cell-inner";let o=c(i);return s.appendChild(o),a.appendChild(s),{cell:a,inner:s,el:o,item:i}},x=i=>{i.classList.contains("crust-expandable")&&(i.offsetHeight,i.classList.add("crust-expanded"),i.dataset.pinned="1")},$=(i,a)=>{let s=i.item,o=a.expanded===!0&&s.expanded!==!0;if(!(a.message!==s.message||a.title!==s.title||a.type!==s.type||a.icon!==s.icon)){i.item=a,o&&x(i.el);return}let d=c(a);d.classList.contains("crust-expandable")&&(d.classList.toggle("crust-expanded",i.el.classList.contains("crust-expanded")),i.el.dataset.pinned&&(d.dataset.pinned=i.el.dataset.pinned)),i.el.replaceWith(d),i.el=d,i.item=a,o&&x(d)},j=(i,a)=>{let{cell:s}=a;if(s.dataset.leaving)return;s.dataset.leaving="1";let o=()=>{a.el.classList.add("crust-leaving"),s.classList.remove("crust-shown");let f=!1,d=()=>{f||(f=!0,s.remove(),p.delete(i))},m=setTimeout(d,X);s.addEventListener("transitionend",b=>{b.target===s&&(clearTimeout(m),d())})};a.el.classList.remove("crust-expanded"),delete a.el.dataset.pinned,o()},H=i=>{let a=new Set(i.map(o=>o.id));for(let[o,f]of p)a.has(o)||j(o,f);let s=0;for(let o of i){let f=p.get(o.id);if(f){f.item!==o&&$(f,o);continue}let d=T(o);p.set(o.id,d),r?l.prepend(d.cell):l.append(d.cell);let m=s*J;m>0&&d.cell.style.setProperty("--crust-stagger",`${m}ms`),d.cell.offsetHeight,d.cell.classList.add("crust-shown"),o.expanded===!0&&x(d.el),m>0&&setTimeout(()=>d.cell.style.removeProperty("--crust-stagger"),Z+m),s+=1}},K=B.subscribe(H);H(B.getSnapshot());let I={unmount:()=>{K(),document.removeEventListener("astro:after-swap",u),l.remove(),p.clear(),y===I&&(y=null)}};return y=I,I};export{B as a,te as b,ne as c};
@@ -0,0 +1,17 @@
1
+ import { ToasterOptions, Toast } from './vanilla.js';
2
+
3
+ /**
4
+ * Concurrent-safe read of the active toasts (e.g. for badge counts).
5
+ * Rendering is handled by `<Toaster />` / `mountToaster` — not this hook.
6
+ */
7
+ declare function useToasts(): readonly Toast[];
8
+ interface ToasterProps extends ToasterOptions {
9
+ }
10
+ /**
11
+ * Mounts the vanilla toaster for the lifetime of the component.
12
+ * Options are read once at mount — Crust is opinionated, not reactive,
13
+ * about its own configuration.
14
+ */
15
+ declare function Toaster(props: ToasterProps): null;
16
+
17
+ export { Toaster, type ToasterProps, useToasts };
package/dist/react.js ADDED
@@ -0,0 +1 @@
1
+ import{a as t,c as o}from"./chunk-JF4E32JE.js";import{useEffect as r,useSyncExternalStore as n}from"react";var s=[],a=()=>s;function i(){return n(t.subscribe,t.getSnapshot,a)}function c(e){return r(()=>o(e).unmount,[]),null}export{c as Toaster,i as useToasts};
@@ -0,0 +1 @@
1
+ .crust-region{--crust-surface: oklch(.98 .005 75);--crust-ink: oklch(.28 .012 75);--crust-ink-muted: oklch(.5 .012 75);--crust-border: oklch(.89 .01 75);--crust-shadow: 0 1px 2px oklch(.2 .01 75 / .05), 0 6px 16px -4px oklch(.2 .01 75 / .1);--crust-success: oklch(.55 .12 150);--crust-error: oklch(.55 .15 30);--crust-info: oklch(.55 .1 250);--crust-warning: oklch(.62 .13 80);--crust-radius: 10px;--crust-width: 360px;--crust-offset: 16px;--crust-z: 9999;--crust-ease: cubic-bezier(.22, 1, .36, 1);--crust-ease-exit: cubic-bezier(.4, 0, 1, 1);--crust-dur-in: .32s;--crust-dur-morph: .28s;--crust-dur-out: .2s;position:fixed;z-index:var(--crust-z);display:flex;flex-direction:column;max-width:calc(100vw - 2 * var(--crust-offset));pointer-events:none}@media(prefers-color-scheme:dark){.crust-region{--crust-surface: oklch(.24 .008 75);--crust-ink: oklch(.92 .008 75);--crust-ink-muted: oklch(.68 .01 75);--crust-border: oklch(.33 .01 75);--crust-shadow: 0 1px 2px oklch(0 0 0 / .3), 0 6px 16px -4px oklch(0 0 0 / .45);--crust-success: oklch(.72 .13 150);--crust-error: oklch(.7 .16 30);--crust-info: oklch(.72 .11 250);--crust-warning: oklch(.75 .13 80)}}.crust-pos-bottom-right{bottom:var(--crust-offset);right:var(--crust-offset);align-items:flex-end}.crust-pos-bottom-left{bottom:var(--crust-offset);left:var(--crust-offset);align-items:flex-start}.crust-pos-bottom-center{bottom:var(--crust-offset);left:50%;transform:translate(-50%);align-items:center}.crust-pos-top-right{top:var(--crust-offset);right:var(--crust-offset);align-items:flex-end}.crust-pos-top-left{top:var(--crust-offset);left:var(--crust-offset);align-items:flex-start}.crust-pos-top-center{top:var(--crust-offset);left:50%;transform:translate(-50%);align-items:center}.crust-cell{display:grid;grid-template-rows:0fr;transition:grid-template-rows var(--crust-dur-in) var(--crust-ease);transition-delay:var(--crust-stagger, 0ms)}.crust-cell.crust-shown{grid-template-rows:1fr}.crust-cell[data-leaving]{transition-duration:var(--crust-dur-out);transition-timing-function:var(--crust-ease-exit)}.crust-cell-inner{min-height:0;overflow:hidden;display:flex;padding:8px 12px;margin:-4px -12px}.crust-pos-bottom-right .crust-cell-inner,.crust-pos-top-right .crust-cell-inner{justify-content:flex-end}.crust-pos-bottom-center .crust-cell-inner,.crust-pos-top-center .crust-cell-inner{justify-content:center}.crust-toast{pointer-events:auto;box-sizing:border-box;width:fit-content;max-width:min(var(--crust-width),calc(100vw - 2 * var(--crust-offset)));interpolate-size:allow-keywords;background:var(--crust-surface);color:var(--crust-ink);border:1px solid var(--crust-border);border-radius:var(--crust-radius);box-shadow:var(--crust-shadow);font:inherit;margin:4px 0;opacity:0;transform:translateY(14px) scale(.97);transition:transform var(--crust-dur-in) var(--crust-ease),opacity var(--crust-dur-in) var(--crust-ease),width var(--crust-dur-morph) var(--crust-ease);transition-delay:var(--crust-stagger, 0ms)}.crust-pos-top-right .crust-toast,.crust-pos-top-left .crust-toast,.crust-pos-top-center .crust-toast{transform:translateY(-14px) scale(.97)}.crust-shown .crust-toast{opacity:1;transform:none}.crust-toast.crust-expandable{cursor:pointer}.crust-toast.crust-expanded{width:var(--crust-width)}.crust-shown .crust-toast.crust-leaving{opacity:0;transform:translateY(6px) scale(.97);transition:transform var(--crust-dur-out) var(--crust-ease-exit),opacity var(--crust-dur-out) var(--crust-ease-exit),width var(--crust-dur-out) var(--crust-ease-exit)}.crust-leaving .crust-body{transition:grid-template-rows var(--crust-dur-out) var(--crust-ease-exit)}.crust-pos-top-right .crust-shown .crust-toast.crust-leaving,.crust-pos-top-left .crust-shown .crust-toast.crust-leaving,.crust-pos-top-center .crust-shown .crust-toast.crust-leaving{transform:translateY(-6px) scale(.97)}.crust-capsule{display:flex;align-items:center;gap:10px;padding:10px 12px 10px 14px}.crust-icon{display:inline-flex;flex:none}.crust-success .crust-icon{color:var(--crust-success)}.crust-error .crust-icon{color:var(--crust-error)}.crust-info .crust-icon{color:var(--crust-info)}.crust-warning .crust-icon{color:var(--crust-warning)}.crust-loading .crust-icon{color:var(--crust-ink-muted)}.crust-loading .crust-icon svg{animation:crust-rotate .9s linear infinite}@keyframes crust-rotate{to{transform:rotate(360deg)}}.crust-draw{stroke-dasharray:1;stroke-dashoffset:1;animation:crust-draw .24s var(--crust-ease) 80ms forwards}@keyframes crust-draw{to{stroke-dashoffset:0}}.crust-title{font-size:.875rem;font-weight:600;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.crust-dismiss{flex:none;display:inline-flex;align-items:center;justify-content:center;align-self:flex-start;width:24px;height:24px;margin:-4px -4px -4px auto;padding:0;border:0;border-radius:6px;background:transparent;color:var(--crust-ink-muted);cursor:pointer;opacity:0;transition:opacity .15s var(--crust-ease),background-color .15s var(--crust-ease)}.crust-toast:hover .crust-dismiss,.crust-toast.crust-expanded .crust-dismiss,.crust-dismiss:focus-visible{opacity:1}.crust-dismiss:hover{background:oklch(from var(--crust-ink) l c h / .08)}.crust-dismiss:focus-visible{outline:2px solid var(--crust-info);outline-offset:1px}.crust-body{display:grid;grid-template-rows:0fr;transition:grid-template-rows var(--crust-dur-morph) var(--crust-ease)}.crust-expanded .crust-body{grid-template-rows:1fr}.crust-body-inner{min-height:0;overflow:hidden;width:0;min-width:100%}.crust-msg{margin:0;padding:0 14px 12px;font-size:.8125rem;line-height:1.5;color:var(--crust-ink-muted);max-width:65ch}@media(prefers-reduced-motion:reduce){.crust-cell,.crust-toast,.crust-body{transition-duration:1ms}.crust-toast,.crust-pos-top-right .crust-toast,.crust-pos-top-left .crust-toast,.crust-pos-top-center .crust-toast,.crust-shown .crust-toast.crust-leaving{transform:none;transition:opacity .15s linear}.crust-draw{animation:none;stroke-dashoffset:0}}
@@ -0,0 +1,88 @@
1
+ type ToastType = 'success' | 'error' | 'info' | 'warning' | 'loading';
2
+ /**
3
+ * Anything the renderer can turn into a DOM node:
4
+ * raw SVG markup, an Element (cloned per render), or a factory.
5
+ * `null` hides the icon for that toast.
6
+ */
7
+ type CrustIcon = string | Element | (() => Element);
8
+ interface ToastOptions {
9
+ title?: string;
10
+ type?: ToastType;
11
+ /** ms before auto-dismiss. Default 4000. `Infinity` (or `0` as alias) never dismisses. */
12
+ duration?: number;
13
+ icon?: CrustIcon | null;
14
+ /** Arrive with the message panel open (pinned). */
15
+ expanded?: boolean;
16
+ /** ms after becoming visible until the toast auto-expands. Needs a `title`. */
17
+ expandAfter?: number;
18
+ }
19
+ interface Toast {
20
+ id: string;
21
+ message: string;
22
+ title?: string;
23
+ type: ToastType;
24
+ duration: number;
25
+ icon?: CrustIcon | null;
26
+ expanded?: boolean;
27
+ expandAfter?: number;
28
+ }
29
+ type ToastPatch = Partial<Omit<Toast, 'id'>>;
30
+ /** Low-level store. Most apps only need `toast` and a mounted toaster. */
31
+ declare const toastStore: {
32
+ subscribe: (listener: (toasts: readonly Toast[]) => void) => () => void;
33
+ getSnapshot: () => readonly Toast[];
34
+ add: (message: string, options?: ToastOptions) => string;
35
+ remove: (id: string) => void;
36
+ update: (id: string, patch: ToastPatch) => void;
37
+ pause: (id: string) => void;
38
+ resume: (id: string) => void;
39
+ configure: (options: {
40
+ maxVisible?: number;
41
+ }) => void;
42
+ };
43
+ type ToastContent = string | {
44
+ message: string;
45
+ title?: string;
46
+ };
47
+ interface PromiseMessages<T> {
48
+ loading: ToastContent;
49
+ success: ToastContent | ((value: T) => ToastContent);
50
+ error: ToastContent | ((reason: unknown) => ToastContent);
51
+ }
52
+ type Shorthand = (message: string, options?: Omit<ToastOptions, 'type'>) => string;
53
+ declare const toast: ((message: string, options?: ToastOptions) => string) & {
54
+ success: Shorthand;
55
+ error: Shorthand;
56
+ info: Shorthand;
57
+ warning: Shorthand;
58
+ /** Loading toasts persist until updated or dismissed. */
59
+ loading: (message: string, options?: Omit<ToastOptions, "type">) => string;
60
+ /** Patch a live (or queued) toast. A new `duration` restarts its timer. */
61
+ update: (id: string, patch: ToastPatch) => void;
62
+ /**
63
+ * Show a loading toast that morphs into success/error when the
64
+ * promise settles. Returns the toast id.
65
+ */
66
+ promise: <T>(promise: Promise<T>, messages: PromiseMessages<T>, options?: Omit<ToastOptions, "type" | "duration"> & {
67
+ duration?: number;
68
+ /** Open the outcome's message panel when the promise settles. */
69
+ expandOnSettle?: boolean;
70
+ }) => string;
71
+ /** Dismiss one toast by id, or every toast (and the queue) with no argument. */
72
+ dismiss: (id?: string) => void;
73
+ };
74
+ type ToasterPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
75
+ interface ToasterOptions {
76
+ /** Corner/edge the stack anchors to. Default `'bottom-right'`. */
77
+ position?: ToasterPosition;
78
+ /** Replace the built-in state icons. `null` hides a type's icon. */
79
+ icons?: Partial<Record<ToastType, CrustIcon | null>>;
80
+ /** Max toasts on screen at once; the rest queue. Default 5. */
81
+ maxVisible?: number;
82
+ }
83
+ interface ToasterHandle {
84
+ unmount: () => void;
85
+ }
86
+ declare const mountToaster: (options?: ToasterOptions) => ToasterHandle;
87
+
88
+ export { type CrustIcon, type PromiseMessages, type Toast, type ToastOptions, type ToastPatch, type ToastType, type ToasterHandle, type ToasterOptions, type ToasterPosition, mountToaster, toast, toastStore };
@@ -0,0 +1 @@
1
+ import{a,b,c}from"./chunk-JF4E32JE.js";export{c as mountToaster,b as toast,a as toastStore};
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@oscarrc/crust",
3
+ "version": "0.1.0",
4
+ "description": "Don't throw the crust. An opinionated, crisp toast library built natively for Astro and React.",
5
+ "license": "MIT",
6
+ "author": "Oscar Rey",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/oscarrc/crust.git"
10
+ },
11
+ "homepage": "https://oscarrc.github.io/crust",
12
+ "keywords": [
13
+ "toast",
14
+ "notifications",
15
+ "astro",
16
+ "react",
17
+ "vanilla"
18
+ ],
19
+ "type": "module",
20
+ "sideEffects": [
21
+ "*.css"
22
+ ],
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "exports": {
27
+ "./vanilla": {
28
+ "types": "./dist/vanilla.d.ts",
29
+ "import": "./dist/vanilla.js"
30
+ },
31
+ "./react": {
32
+ "types": "./dist/react.d.ts",
33
+ "import": "./dist/react.js"
34
+ },
35
+ "./styles.css": "./dist/styles.css"
36
+ },
37
+ "peerDependencies": {
38
+ "react": ">=18"
39
+ },
40
+ "peerDependenciesMeta": {
41
+ "react": {
42
+ "optional": true
43
+ }
44
+ },
45
+ "devDependencies": {
46
+ "@arethetypeswrong/cli": "^0.18.3",
47
+ "@testing-library/react": "^16.3.2",
48
+ "@types/react": "^19.2.16",
49
+ "happy-dom": "^20.10.1",
50
+ "publint": "^0.3.21",
51
+ "react": "^19.2.7",
52
+ "react-dom": "^19.2.7",
53
+ "tsup": "^8.5.1",
54
+ "typescript": "^6.0.3",
55
+ "vitest": "^4.1.8"
56
+ },
57
+ "scripts": {
58
+ "build": "tsup",
59
+ "dev": "tsup --watch",
60
+ "test": "vitest run",
61
+ "check:pkg": "publint && attw --pack . --profile esm-only --entrypoints ./vanilla ./react"
62
+ }
63
+ }