@vrixton/pixel-pet 0.2.0 → 0.3.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 CHANGED
@@ -101,25 +101,23 @@ const danko: PixelPetProps = {
101
101
 
102
102
  ## Translating the messages
103
103
 
104
- Each message accepts any React node, so you can translate it and keep the colored words:
104
+ Messages can be plain strings with two markers, so they fit in translation files as they are:
105
105
 
106
106
  ```tsx
107
107
  <PixelPet
108
108
  petName="Danko"
109
+ label="{name}, the pet"
109
110
  messages={{
110
- happy: (
111
- <>
112
- <span className="pixel-pet-highlight-pet">Danko</span> está{' '}
113
- <span className="pixel-pet-highlight-happy">feliz</span>.
114
- </>
115
- ),
116
- angry: 'Danko está abrumado.',
117
- curious: 'Danko tiene mucha curiosidad.',
111
+ happy: '{name} está **feliz**.',
112
+ angry: '{name} está **abrumado**.',
113
+ curious: '{name} tiene mucha **curiosidad**.',
118
114
  }}
119
115
  />
120
116
  ```
121
117
 
122
- The highlight classes are `pixel-pet-highlight-pet`, `pixel-pet-highlight-happy`, `pixel-pet-highlight-angry`, and `pixel-pet-highlight-curious`. Any emotion you leave out keeps its English message.
118
+ `{name}` becomes the pet's name in its color, `__word__` shows any word in the pet's color, and `**word**` shows that word in the color of the emotion. If your translation system already fills in `{name}` itself, wrap it as `__{name}__`, so the pet receives `__Bit__` and still paints the name. The markers also work in `say()`: there, `**word**` takes the color of the requested emotion, or the pet's color when there is none. In `label`, which screen readers read aloud, only `{name}` is replaced. Any emotion you leave out keeps its English message.
119
+
120
+ Messages can also be any React node when you need more control. The highlight classes are `pixel-pet-highlight-pet`, `pixel-pet-highlight-happy`, `pixel-pet-highlight-angry`, and `pixel-pet-highlight-curious`.
123
121
 
124
122
  ## Styling the bubble
125
123
 
@@ -131,7 +129,7 @@ The highlight classes are `pixel-pet-highlight-pet`, `pixel-pet-highlight-happy`
131
129
  | `textColor` | `'#ffffff'` |
132
130
  | `borderColor` | Depends on the emotion |
133
131
  | `borderWidth` | `'1px'` |
134
- | `borderRadius` | `'5px'` |
132
+ | `borderRadius` | `'0'` |
135
133
  | `fontFamily` | `'monospace'` |
136
134
 
137
135
  A field set in `emotionDialogStyles` wins over the same field in `dialogStyle`, which wins over the default. Messages from `say()` without an emotion use `dialogStyle`.
@@ -144,7 +142,7 @@ A sheet passed through `spriteSrc` has to follow the same layout as the bundled
144
142
 
145
143
  ## How it behaves
146
144
 
147
- The pet wanders on its own, sometimes walking and sometimes standing, and turns around at the edges of the container. Every 30 seconds it may switch to a random mood for 5 seconds; hovering over it or focusing it with the keyboard shows what it's feeling. Clicking it shows its last mood, and clicking it more than five times in ten seconds makes it angry. After two minutes without mouse, touch, keyboard, or scroll wheel activity on the page, it falls asleep, and any activity wakes it up.
145
+ The pet wanders on its own, sometimes walking and sometimes standing, and turns around at the edges of the container. Every 30 seconds it may switch to a random mood for 5 seconds; hovering over it or focusing it with the keyboard shows what it's feeling. Clicking it shows its last mood for three seconds, and clicking it more than five times in ten seconds makes it angry. After a click, the bubble stays hidden while the pointer rests on the pet; moving the pointer away and back shows it again. After two minutes without mouse, touch, keyboard, or scroll wheel activity on the page, it falls asleep, and any activity wakes it up.
148
146
 
149
147
  The pet is a real button, so it can be reached with the keyboard, has a visible focus outline, and is announced with its name. When the user prefers reduced motion, it stays still, its animation pauses, and the bubble appears without its pop-in effect.
150
148
 
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  'use client';
2
- "use strict";var G=Object.defineProperty;var ve=Object.getOwnPropertyDescriptor;var we=Object.getOwnPropertyNames;var Me=Object.prototype.hasOwnProperty;var Ce=(s,a)=>{for(var u in a)G(s,u,{get:a[u],enumerable:!0})},Se=(s,a,u,b)=>{if(a&&typeof a=="object"||typeof a=="function")for(let d of we(a))!Me.call(s,d)&&d!==u&&G(s,d,{get:()=>a[d],enumerable:!(b=ve(a,d))||b.enumerable});return s};var De=s=>Se(G({},"__esModule",{value:!0}),s);var $e={};Ce($e,{PixelPet:()=>Oe});module.exports=De($e);var t=require("react");var r=require("react/jsx-runtime"),Z=["pointermove","pointerdown","keydown","wheel"],ne="(prefers-reduced-motion: reduce)",V=4,Ie=240,ee=5e3,te=65,oe=100,Ae=.04,Le=100,Ne=s=>{if(typeof window.matchMedia!="function")return()=>{};let a=window.matchMedia(ne);return a.addEventListener("change",s),()=>a.removeEventListener("change",s)},Fe=()=>typeof window.matchMedia=="function"&&window.matchMedia(ne).matches,We=()=>(0,t.useSyncExternalStore)(Ne,Fe,()=>!1),_e={happy:{backgroundColor:"#1e1c063d",borderColor:"#ffeb3b"},angry:{backgroundColor:"#21090833",borderColor:"#F04037"},curious:{backgroundColor:"#1f341d3a",borderColor:"#8BE383"}},He={backgroundColor:"#05080cd9",borderColor:"var(--pixel-pet-color, #FDDBA0)"},Oe=({petName:s="Static",variant:a="cat",spriteSrc:u,color:b,label:d,walk:re=!0,messages:A,dialogStyle:g={},emotionDialogStyles:ie={},onClick:se,ref:ae})=>{let[l,c]=(0,t.useState)("idle"),le=We(),y=re&&!le,L=(0,t.useRef)(null),M=(0,t.useRef)(null),Y=(0,t.useRef)(null),[N,pe]=(0,t.useState)(null),[E,C]=(0,t.useState)(null),[ce,q]=(0,t.useState)(!1),[ue,Q]=(0,t.useState)(!1),[de,fe]=(0,t.useState)(!1),[x,me]=(0,t.useState)(null),k=(0,t.useRef)(oe),[F,ge]=(0,t.useState)(0),[W,xe]=(0,t.useState)("right"),he={happy:A?.happy??(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("span",{className:"pixel-pet-highlight-pet",children:s})," is feeling"," ",(0,r.jsx)("span",{className:"pixel-pet-highlight-happy",children:"happy"}),"!"]}),angry:A?.angry??(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("span",{className:"pixel-pet-highlight-pet",children:s})," is feeling"," ",(0,r.jsx)("span",{className:"pixel-pet-highlight-angry",children:"overwhelmed"}),"!"]}),curious:A?.curious??(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("span",{className:"pixel-pet-highlight-pet",children:s})," is very"," ",(0,r.jsx)("span",{className:"pixel-pet-highlight-curious",children:"curious"})]})},K=(0,t.useRef)(l),S=(0,t.useRef)(null),P=(0,t.useRef)(!1),_=(0,t.useRef)(!1),p=(0,t.useRef)(null),j=(0,t.useRef)("happy"),H=(0,t.useRef)([]),T=(0,t.useRef)(!1),R=(0,t.useRef)(null),O=(0,t.useRef)(ee),$=(e,n=!1)=>{l==="sleep"&&c("idle"),j.current=e,C(e),fe(n),p.current&&clearTimeout(p.current),p.current=setTimeout(()=>{_.current||C(null)},5e3)},v=e=>{T.current=e!==null,me(e)},D=()=>{R.current&&clearTimeout(R.current)},J=e=>{D(),e!==1/0&&(R.current=setTimeout(()=>v(null),e))};(0,t.useImperativeHandle)(ae,()=>({say:(e,n={})=>{c(o=>o==="sleep"?"idle":o),v({message:e,emotion:n.emotion??null}),O.current=n.duration??ee,J(O.current)},hush:()=>{D(),v(null)}}),[]),(0,t.useEffect)(()=>{let e,n=()=>{c(o=>o==="sleep"?"idle":o),clearTimeout(e),e=setTimeout(()=>{C(null),v(null),c("sleep")},12e4)};return Z.forEach(o=>{window.addEventListener(o,n,{passive:!0})}),n(),()=>{clearTimeout(e),Z.forEach(o=>{window.removeEventListener(o,n)})}},[]),(0,t.useEffect)(()=>{K.current=l},[l]),(0,t.useEffect)(()=>{let e=setInterval(()=>{if(K.current==="sleep"||T.current)return;let n=["happy","angry","curious"],o=n[Math.floor(Math.random()*n.length)];$(o)},3e4);return()=>{clearInterval(e)}},[]),(0,t.useEffect)(()=>()=>{p.current&&clearTimeout(p.current),R.current&&clearTimeout(R.current)},[]),(0,t.useEffect)(()=>{if(!(l==="sleep"||!y)){if(l==="walk"){let e=Math.floor(Math.random()*175001)+5e3;S.current=setTimeout(()=>{c("idle"),P.current=!0,setTimeout(()=>{P.current=!1},3e4)},e)}else{let e=()=>{if(!P.current&&Math.random()>.4){c("walk");return}S.current=setTimeout(e,5e3)};e()}return()=>{S.current&&clearTimeout(S.current)}}},[l,y]);let B=e=>{k.current=e,M.current&&(M.current.style.transform=`translateX(${e}px)`)};(0,t.useEffect)(()=>{if(l!=="walk"||E!==null||x!==null||!y)return;let e=0,n=F-te;if(n<=e)return;let o=0,i=null,m=w=>{let X=i===null?0:Math.min(w-i,Le);i=w;let h=k.current+(W==="right"?1:-1)*Ae*X;if(h<=e||h>=n){if(Math.random()>.5){c("idle"),P.current=!0,setTimeout(()=>{P.current=!1},3e4);return}B(h<=e?e+2:n-2),xe(z=>z==="right"?"left":"right");return}B(h),o=requestAnimationFrame(m)};return o=requestAnimationFrame(m),()=>cancelAnimationFrame(o)},[l,E,x,W,y,F]),(0,t.useEffect)(()=>{let e=L.current;if(!e)return;let n=i=>{ge(i),B(Math.max(Math.min(k.current,i-te),0))};if(n(e.clientWidth),typeof ResizeObserver>"u")return;let o=new ResizeObserver(([i])=>{i&&n(i.contentRect.width)});return o.observe(e),()=>o.disconnect()},[]);let be=e=>{if(se?.(e),e.defaultPrevented)return;let n=Date.now();l==="sleep"&&c("idle"),T.current&&(D(),v(null));let o=[...H.current,n].filter(i=>n-i<=1e4);H.current=o,o.length>5?(H.current=[],$("angry",!0)):$(j.current,!0)},ye=()=>{q(!0),T.current&&D(),E&&(_.current=!0,p.current&&clearTimeout(p.current))},Ee=()=>{q(!1),T.current&&J(O.current),E&&(_.current=!1,p.current&&clearTimeout(p.current),p.current=setTimeout(()=>{C(null)},5e3))},I=x!==null&&l!=="sleep",f=l==="sleep"?null:I?x.emotion:E,U=I||f!==null&&(ce||ue||de);(0,t.useLayoutEffect)(()=>{let e=L.current,n=M.current,o=Y.current;if(!U||!e||!n||!o)return;let i=e.clientWidth,m=Math.max(Math.min(Ie,i-V*2),0),w=Math.min(o.offsetWidth,m),X=k.current+n.offsetWidth/2,h=V,z=i-V-w,Te=X-w/2,Re=Math.max(Math.min(Te,z),h);pe({left:Re-k.current,maxWidth:m})},[U,f,x,F]);let ke=()=>f?`pixel-pet-state-${f}`:(!y||I)&&l==="walk"?"pixel-pet-state-idle":`pixel-pet-state-${l}`,Pe=e=>{let n=e?_e[e]:He,o=e&&ie[e]||{},i=o.backgroundColor||g.backgroundColor||n.backgroundColor,m=o.borderColor||g.borderColor||n.borderColor;return{backgroundColor:i,color:o.textColor||g.textColor||"#ffffff",border:`${o.borderWidth||g.borderWidth||"1px"} solid ${m}`,borderRadius:o.borderRadius||g.borderRadius||"5px",fontFamily:o.fontFamily||g.fontFamily||"monospace"}};return(0,r.jsx)("div",{className:"pixel-pet",ref:L,style:b?{"--pixel-pet-color":b}:void 0,children:(0,r.jsxs)("div",{ref:M,className:"pixel-pet-container",style:{transform:`translateX(${oe}px)`},onMouseEnter:ye,onMouseLeave:Ee,children:[(0,r.jsx)("div",{className:"pixel-pet-status",role:"status",children:U&&(0,r.jsx)("div",{ref:Y,className:"pixel-pet-dialog",style:{...Pe(f),...N&&{left:N.left,maxWidth:N.maxWidth}},children:I?x.message:f&&he[f]})}),(0,r.jsx)("button",{type:"button","aria-label":d??s,className:`pixel-pet-sprite pixel-pet-${a} ${ke()} ${W==="left"?"pixel-pet-flip-x":""}`,style:u?{backgroundImage:`url("${u}")`}:void 0,onClick:be,onFocus:()=>Q(!0),onBlur:()=>Q(!1)})]})})};0&&(module.exports={PixelPet});
2
+ "use strict";var Y=Object.defineProperty;var De=Object.getOwnPropertyDescriptor;var _e=Object.getOwnPropertyNames;var Ae=Object.prototype.hasOwnProperty;var Ie=(i,s)=>{for(var u in s)Y(i,u,{get:s[u],enumerable:!0})},Le=(i,s,u,b)=>{if(s&&typeof s=="object"||typeof s=="function")for(let d of _e(s))!Ae.call(i,d)&&d!==u&&Y(i,d,{get:()=>s[d],enumerable:!(b=De(s,d))||b.enumerable});return i};var Fe=i=>Le(Y({},"__esModule",{value:!0}),i);var Ye={};Ie(Ye,{PixelPet:()=>Ve});module.exports=Fe(Ye);var t=require("react");var c=require("react/jsx-runtime"),re=["pointermove","pointerdown","keydown","wheel"],le="(prefers-reduced-motion: reduce)",K=4,Ne=240,ie=5e3,We=3e3,He=/(\{name\}|\*\*[^*]+\*\*|__[^_]+__)/,q={happy:"{name} is feeling **happy**!",angry:"{name} is feeling **overwhelmed**!",curious:"{name} is very **curious**"},se=65,ae=100,Oe=.04,Be=100,Ue=i=>{if(typeof window.matchMedia!="function")return()=>{};let s=window.matchMedia(le);return s.addEventListener("change",i),()=>s.removeEventListener("change",i)},$e=()=>typeof window.matchMedia=="function"&&window.matchMedia(le).matches,Xe=()=>(0,t.useSyncExternalStore)(Ue,$e,()=>!1),Ge={happy:{backgroundColor:"#1e1c06eb",borderColor:"#ffeb3b"},angry:{backgroundColor:"#210908eb",borderColor:"#F04037"},curious:{backgroundColor:"#1f341deb",borderColor:"#8BE383"}},ze={backgroundColor:"#05080ceb",borderColor:"var(--pixel-pet-color, #FDDBA0)"},Ve=({petName:i="Static",variant:s="cat",spriteSrc:u,color:b,label:d,walk:ce=!0,messages:F,dialogStyle:x={},emotionDialogStyles:pe={},onClick:ue,ref:de})=>{let[a,p]=(0,t.useState)("idle"),fe=Xe(),y=ce&&!fe,N=(0,t.useRef)(null),C=(0,t.useRef)(null),Q=(0,t.useRef)(null),[W,me]=(0,t.useState)(null),[E,S]=(0,t.useState)(null),[ge,j]=(0,t.useState)(!1),[xe,J]=(0,t.useState)(!1),[he,Z]=(0,t.useState)(!1),[be,ee]=(0,t.useState)(!1),[f,ye]=(0,t.useState)(null),R=(0,t.useRef)(ae),[H,Ee]=(0,t.useState)(0),[O,Re]=(0,t.useState)("right"),D=(e,n)=>typeof e!="string"?e:e.split(He).map((o,r)=>r%2===0?o:o==="{name}"||o.startsWith("__")?(0,c.jsx)("span",{className:"pixel-pet-highlight-pet",children:o==="{name}"?i:o.slice(2,-2)},r):(0,c.jsx)("span",{className:`pixel-pet-highlight-${n??"pet"}`,children:o.slice(2,-2)},r)),Te={happy:D(F?.happy??q.happy,"happy"),angry:D(F?.angry??q.angry,"angry"),curious:D(F?.curious??q.curious,"curious")},te=(0,t.useRef)(a),_=(0,t.useRef)(null),T=(0,t.useRef)(!1),A=(0,t.useRef)(!1),l=(0,t.useRef)(null),oe=(0,t.useRef)("happy"),B=(0,t.useRef)([]),k=(0,t.useRef)(null),P=(0,t.useRef)(!1),v=(0,t.useRef)(null),U=(0,t.useRef)(ie),$=(e,n=!1)=>{a==="sleep"&&p("idle"),oe.current=e,S(e),Z(n),l.current&&clearTimeout(l.current),l.current=setTimeout(()=>{A.current||S(null)},5e3)},M=e=>{P.current=e!==null,ye(e)},I=()=>{v.current&&clearTimeout(v.current)},ne=e=>{I(),e!==1/0&&(v.current=setTimeout(()=>M(null),e))};(0,t.useImperativeHandle)(de,()=>({say:(e,n={})=>{p(o=>o==="sleep"?"idle":o),M({message:e,emotion:n.emotion??null}),U.current=n.duration??ie,ne(U.current)},hush:()=>{I(),M(null)}}),[]),(0,t.useEffect)(()=>{let e,n=()=>{p(o=>o==="sleep"?"idle":o),clearTimeout(e),e=setTimeout(()=>{S(null),M(null),p("sleep")},12e4)};return re.forEach(o=>{window.addEventListener(o,n,{passive:!0})}),n(),()=>{clearTimeout(e),re.forEach(o=>{window.removeEventListener(o,n)})}},[]),(0,t.useEffect)(()=>{te.current=a},[a]),(0,t.useEffect)(()=>{let e=setInterval(()=>{if(te.current==="sleep"||P.current)return;let n=["happy","angry","curious"],o=n[Math.floor(Math.random()*n.length)];$(o)},3e4);return()=>{clearInterval(e)}},[]),(0,t.useEffect)(()=>()=>{l.current&&clearTimeout(l.current),v.current&&clearTimeout(v.current),k.current&&clearTimeout(k.current)},[]),(0,t.useEffect)(()=>{if(!(a==="sleep"||!y)){if(a==="walk"){let e=Math.floor(Math.random()*175001)+5e3;_.current=setTimeout(()=>{p("idle"),T.current=!0,setTimeout(()=>{T.current=!1},3e4)},e)}else{let e=()=>{if(!T.current&&Math.random()>.4){p("walk");return}_.current=setTimeout(e,5e3)};e()}return()=>{_.current&&clearTimeout(_.current)}}},[a,y]);let X=e=>{R.current=e,C.current&&(C.current.style.transform=`translateX(${e}px)`)};(0,t.useEffect)(()=>{if(a!=="walk"||E!==null||f!==null||!y)return;let e=0,n=H-se;if(n<=e)return;let o=0,r=null,g=w=>{let z=r===null?0:Math.min(w-r,Be);r=w;let h=R.current+(O==="right"?1:-1)*Oe*z;if(h<=e||h>=n){if(Math.random()>.5){p("idle"),T.current=!0,setTimeout(()=>{T.current=!1},3e4);return}X(h<=e?e+2:n-2),Re(V=>V==="right"?"left":"right");return}X(h),o=requestAnimationFrame(g)};return o=requestAnimationFrame(g),()=>cancelAnimationFrame(o)},[a,E,f,O,y,H]),(0,t.useEffect)(()=>{let e=N.current;if(!e)return;let n=r=>{Ee(r),X(Math.max(Math.min(R.current,r-se),0))};if(n(e.clientWidth),typeof ResizeObserver>"u")return;let o=new ResizeObserver(([r])=>{r&&n(r.contentRect.width)});return o.observe(e),()=>o.disconnect()},[]);let ke=e=>{if(ue?.(e),e.defaultPrevented)return;let n=Date.now();a==="sleep"&&p("idle"),P.current&&(I(),M(null)),A.current=!1,ee(!0),k.current&&clearTimeout(k.current),k.current=setTimeout(()=>Z(!1),We);let o=[...B.current,n].filter(r=>n-r<=1e4);B.current=o,o.length>5?(B.current=[],$("angry",!0)):$(oe.current,!0)},Pe=()=>{j(!0),P.current&&I(),E&&(A.current=!0,l.current&&clearTimeout(l.current))},ve=()=>{j(!1),ee(!1),P.current&&ne(U.current),E&&(A.current=!1,l.current&&clearTimeout(l.current),l.current=setTimeout(()=>{S(null)},5e3))},L=f!==null&&a!=="sleep",m=a==="sleep"?null:L?f.emotion:E,G=L||m!==null&&(ge&&!be||xe||he);(0,t.useLayoutEffect)(()=>{let e=N.current,n=C.current,o=Q.current;if(!G||!e||!n||!o)return;let r=e.clientWidth,g=Math.max(Math.min(Ne,r-K*2),0),w=Math.min(o.offsetWidth,g),z=R.current+n.offsetWidth/2,h=K,V=r-K-w,Ce=z-w/2,Se=Math.max(Math.min(Ce,V),h);me({left:Se-R.current,maxWidth:g})},[G,m,f,H]);let Me=()=>m?`pixel-pet-state-${m}`:(!y||L)&&a==="walk"?"pixel-pet-state-idle":`pixel-pet-state-${a}`,we=e=>{let n=e?Ge[e]:ze,o=e&&pe[e]||{},r=o.backgroundColor||x.backgroundColor||n.backgroundColor,g=o.borderColor||x.borderColor||n.borderColor;return{backgroundColor:r,color:o.textColor||x.textColor||"#ffffff",border:`${o.borderWidth||x.borderWidth||"1px"} solid ${g}`,borderRadius:o.borderRadius||x.borderRadius||"0",fontFamily:o.fontFamily||x.fontFamily||"monospace"}};return(0,c.jsx)("div",{className:"pixel-pet",ref:N,style:b?{"--pixel-pet-color":b}:void 0,children:(0,c.jsxs)("div",{ref:C,className:"pixel-pet-container",style:{transform:`translateX(${ae}px)`},onMouseEnter:Pe,onMouseLeave:ve,children:[(0,c.jsx)("div",{className:"pixel-pet-status",role:"status",children:G&&(0,c.jsx)("div",{ref:Q,className:"pixel-pet-dialog",style:{...we(m),...W&&{left:W.left,maxWidth:W.maxWidth}},children:L?D(f.message,f.emotion):m&&Te[m]})}),(0,c.jsx)("button",{type:"button","aria-label":(d??i).split("{name}").join(i),className:`pixel-pet-sprite pixel-pet-${s} ${Me()} ${O==="left"?"pixel-pet-flip-x":""}`,style:u?{backgroundImage:`url("${u}")`}:void 0,onClick:ke,onFocus:e=>J(e.currentTarget.matches(":focus-visible")),onBlur:()=>J(!1)})]})})};0&&(module.exports={PixelPet});
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
1
  'use client';
2
- import{useState as l,useEffect as f,useImperativeHandle as Ce,useLayoutEffect as Se,useRef as i,useSyncExternalStore as De}from"react";import{Fragment as V,jsx as a,jsxs as I}from"react/jsx-runtime";var te=["pointermove","pointerdown","keydown","wheel"],ie="(prefers-reduced-motion: reduce)",G=4,Ie=240,oe=5e3,ne=65,re=100,Ae=.04,Le=100,Ne=c=>{if(typeof window.matchMedia!="function")return()=>{};let v=window.matchMedia(ie);return v.addEventListener("change",c),()=>v.removeEventListener("change",c)},Fe=()=>typeof window.matchMedia=="function"&&window.matchMedia(ie).matches,We=()=>De(Ne,Fe,()=>!1),_e={happy:{backgroundColor:"#1e1c063d",borderColor:"#ffeb3b"},angry:{backgroundColor:"#21090833",borderColor:"#F04037"},curious:{backgroundColor:"#1f341d3a",borderColor:"#8BE383"}},He={backgroundColor:"#05080cd9",borderColor:"var(--pixel-pet-color, #FDDBA0)"},Ge=({petName:c="Static",variant:v="cat",spriteSrc:Y,color:q,label:se,walk:ae=!0,messages:A,dialogStyle:m={},emotionDialogStyles:le={},onClick:pe,ref:ce})=>{let[r,p]=l("idle"),ue=We(),h=ae&&!ue,L=i(null),w=i(null),Q=i(null),[N,de]=l(null),[b,M]=l(null),[fe,K]=l(!1),[me,j]=l(!1),[ge,xe]=l(!1),[g,he]=l(null),y=i(re),[F,be]=l(0),[W,ye]=l("right"),Ee={happy:A?.happy??I(V,{children:[a("span",{className:"pixel-pet-highlight-pet",children:c})," is feeling"," ",a("span",{className:"pixel-pet-highlight-happy",children:"happy"}),"!"]}),angry:A?.angry??I(V,{children:[a("span",{className:"pixel-pet-highlight-pet",children:c})," is feeling"," ",a("span",{className:"pixel-pet-highlight-angry",children:"overwhelmed"}),"!"]}),curious:A?.curious??I(V,{children:[a("span",{className:"pixel-pet-highlight-pet",children:c})," is very"," ",a("span",{className:"pixel-pet-highlight-curious",children:"curious"})]})},J=i(r),C=i(null),E=i(!1),_=i(!1),s=i(null),Z=i("happy"),H=i([]),k=i(!1),P=i(null),O=i(oe),$=(e,o=!1)=>{r==="sleep"&&p("idle"),Z.current=e,M(e),xe(o),s.current&&clearTimeout(s.current),s.current=setTimeout(()=>{_.current||M(null)},5e3)},T=e=>{k.current=e!==null,he(e)},S=()=>{P.current&&clearTimeout(P.current)},ee=e=>{S(),e!==1/0&&(P.current=setTimeout(()=>T(null),e))};Ce(ce,()=>({say:(e,o={})=>{p(t=>t==="sleep"?"idle":t),T({message:e,emotion:o.emotion??null}),O.current=o.duration??oe,ee(O.current)},hush:()=>{S(),T(null)}}),[]),f(()=>{let e,o=()=>{p(t=>t==="sleep"?"idle":t),clearTimeout(e),e=setTimeout(()=>{M(null),T(null),p("sleep")},12e4)};return te.forEach(t=>{window.addEventListener(t,o,{passive:!0})}),o(),()=>{clearTimeout(e),te.forEach(t=>{window.removeEventListener(t,o)})}},[]),f(()=>{J.current=r},[r]),f(()=>{let e=setInterval(()=>{if(J.current==="sleep"||k.current)return;let o=["happy","angry","curious"],t=o[Math.floor(Math.random()*o.length)];$(t)},3e4);return()=>{clearInterval(e)}},[]),f(()=>()=>{s.current&&clearTimeout(s.current),P.current&&clearTimeout(P.current)},[]),f(()=>{if(!(r==="sleep"||!h)){if(r==="walk"){let e=Math.floor(Math.random()*175001)+5e3;C.current=setTimeout(()=>{p("idle"),E.current=!0,setTimeout(()=>{E.current=!1},3e4)},e)}else{let e=()=>{if(!E.current&&Math.random()>.4){p("walk");return}C.current=setTimeout(e,5e3)};e()}return()=>{C.current&&clearTimeout(C.current)}}},[r,h]);let B=e=>{y.current=e,w.current&&(w.current.style.transform=`translateX(${e}px)`)};f(()=>{if(r!=="walk"||b!==null||g!==null||!h)return;let e=0,o=F-ne;if(o<=e)return;let t=0,n=null,d=R=>{let X=n===null?0:Math.min(R-n,Le);n=R;let x=y.current+(W==="right"?1:-1)*Ae*X;if(x<=e||x>=o){if(Math.random()>.5){p("idle"),E.current=!0,setTimeout(()=>{E.current=!1},3e4);return}B(x<=e?e+2:o-2),ye(z=>z==="right"?"left":"right");return}B(x),t=requestAnimationFrame(d)};return t=requestAnimationFrame(d),()=>cancelAnimationFrame(t)},[r,b,g,W,h,F]),f(()=>{let e=L.current;if(!e)return;let o=n=>{be(n),B(Math.max(Math.min(y.current,n-ne),0))};if(o(e.clientWidth),typeof ResizeObserver>"u")return;let t=new ResizeObserver(([n])=>{n&&o(n.contentRect.width)});return t.observe(e),()=>t.disconnect()},[]);let ke=e=>{if(pe?.(e),e.defaultPrevented)return;let o=Date.now();r==="sleep"&&p("idle"),k.current&&(S(),T(null));let t=[...H.current,o].filter(n=>o-n<=1e4);H.current=t,t.length>5?(H.current=[],$("angry",!0)):$(Z.current,!0)},Pe=()=>{K(!0),k.current&&S(),b&&(_.current=!0,s.current&&clearTimeout(s.current))},Te=()=>{K(!1),k.current&&ee(O.current),b&&(_.current=!1,s.current&&clearTimeout(s.current),s.current=setTimeout(()=>{M(null)},5e3))},D=g!==null&&r!=="sleep",u=r==="sleep"?null:D?g.emotion:b,U=D||u!==null&&(fe||me||ge);Se(()=>{let e=L.current,o=w.current,t=Q.current;if(!U||!e||!o||!t)return;let n=e.clientWidth,d=Math.max(Math.min(Ie,n-G*2),0),R=Math.min(t.offsetWidth,d),X=y.current+o.offsetWidth/2,x=G,z=n-G-R,we=X-R/2,Me=Math.max(Math.min(we,z),x);de({left:Me-y.current,maxWidth:d})},[U,u,g,F]);let Re=()=>u?`pixel-pet-state-${u}`:(!h||D)&&r==="walk"?"pixel-pet-state-idle":`pixel-pet-state-${r}`,ve=e=>{let o=e?_e[e]:He,t=e&&le[e]||{},n=t.backgroundColor||m.backgroundColor||o.backgroundColor,d=t.borderColor||m.borderColor||o.borderColor;return{backgroundColor:n,color:t.textColor||m.textColor||"#ffffff",border:`${t.borderWidth||m.borderWidth||"1px"} solid ${d}`,borderRadius:t.borderRadius||m.borderRadius||"5px",fontFamily:t.fontFamily||m.fontFamily||"monospace"}};return a("div",{className:"pixel-pet",ref:L,style:q?{"--pixel-pet-color":q}:void 0,children:I("div",{ref:w,className:"pixel-pet-container",style:{transform:`translateX(${re}px)`},onMouseEnter:Pe,onMouseLeave:Te,children:[a("div",{className:"pixel-pet-status",role:"status",children:U&&a("div",{ref:Q,className:"pixel-pet-dialog",style:{...ve(u),...N&&{left:N.left,maxWidth:N.maxWidth}},children:D?g.message:u&&Ee[u]})}),a("button",{type:"button","aria-label":se??c,className:`pixel-pet-sprite pixel-pet-${v} ${Re()} ${W==="left"?"pixel-pet-flip-x":""}`,style:Y?{backgroundImage:`url("${Y}")`}:void 0,onClick:ke,onFocus:()=>j(!0),onBlur:()=>j(!1)})]})})};export{Ge as PixelPet};
2
+ import{useState as a,useEffect as d,useImperativeHandle as _e,useLayoutEffect as Ae,useRef as r,useSyncExternalStore as Ie}from"react";import{jsx as x,jsxs as Ge}from"react/jsx-runtime";var re=["pointermove","pointerdown","keydown","wheel"],le="(prefers-reduced-motion: reduce)",V=4,Le=240,ie=5e3,Fe=3e3,Ne=/(\{name\}|\*\*[^*]+\*\*|__[^_]+__)/,Y={happy:"{name} is feeling **happy**!",angry:"{name} is feeling **overwhelmed**!",curious:"{name} is very **curious**"},se=65,ae=100,We=.04,He=100,Oe=f=>{if(typeof window.matchMedia!="function")return()=>{};let M=window.matchMedia(le);return M.addEventListener("change",f),()=>M.removeEventListener("change",f)},Be=()=>typeof window.matchMedia=="function"&&window.matchMedia(le).matches,Ue=()=>Ie(Oe,Be,()=>!1),$e={happy:{backgroundColor:"#1e1c06eb",borderColor:"#ffeb3b"},angry:{backgroundColor:"#210908eb",borderColor:"#F04037"},curious:{backgroundColor:"#1f341deb",borderColor:"#8BE383"}},Xe={backgroundColor:"#05080ceb",borderColor:"var(--pixel-pet-color, #FDDBA0)"},je=({petName:f="Static",variant:M="cat",spriteSrc:K,color:q,label:ce,walk:pe=!0,messages:L,dialogStyle:m={},emotionDialogStyles:ue={},onClick:de,ref:fe})=>{let[i,l]=a("idle"),me=Ue(),h=pe&&!me,F=r(null),w=r(null),Q=r(null),[N,ge]=a(null),[b,C]=a(null),[xe,j]=a(!1),[he,J]=a(!1),[be,Z]=a(!1),[ye,ee]=a(!1),[c,Ee]=a(null),y=r(ae),[W,Re]=a(0),[H,Te]=a("right"),S=(e,o)=>typeof e!="string"?e:e.split(Ne).map((t,n)=>n%2===0?t:t==="{name}"||t.startsWith("__")?x("span",{className:"pixel-pet-highlight-pet",children:t==="{name}"?f:t.slice(2,-2)},n):x("span",{className:`pixel-pet-highlight-${o??"pet"}`,children:t.slice(2,-2)},n)),ke={happy:S(L?.happy??Y.happy,"happy"),angry:S(L?.angry??Y.angry,"angry"),curious:S(L?.curious??Y.curious,"curious")},te=r(i),D=r(null),E=r(!1),_=r(!1),s=r(null),oe=r("happy"),O=r([]),R=r(null),T=r(!1),k=r(null),B=r(ie),U=(e,o=!1)=>{i==="sleep"&&l("idle"),oe.current=e,C(e),Z(o),s.current&&clearTimeout(s.current),s.current=setTimeout(()=>{_.current||C(null)},5e3)},P=e=>{T.current=e!==null,Ee(e)},A=()=>{k.current&&clearTimeout(k.current)},ne=e=>{A(),e!==1/0&&(k.current=setTimeout(()=>P(null),e))};_e(fe,()=>({say:(e,o={})=>{l(t=>t==="sleep"?"idle":t),P({message:e,emotion:o.emotion??null}),B.current=o.duration??ie,ne(B.current)},hush:()=>{A(),P(null)}}),[]),d(()=>{let e,o=()=>{l(t=>t==="sleep"?"idle":t),clearTimeout(e),e=setTimeout(()=>{C(null),P(null),l("sleep")},12e4)};return re.forEach(t=>{window.addEventListener(t,o,{passive:!0})}),o(),()=>{clearTimeout(e),re.forEach(t=>{window.removeEventListener(t,o)})}},[]),d(()=>{te.current=i},[i]),d(()=>{let e=setInterval(()=>{if(te.current==="sleep"||T.current)return;let o=["happy","angry","curious"],t=o[Math.floor(Math.random()*o.length)];U(t)},3e4);return()=>{clearInterval(e)}},[]),d(()=>()=>{s.current&&clearTimeout(s.current),k.current&&clearTimeout(k.current),R.current&&clearTimeout(R.current)},[]),d(()=>{if(!(i==="sleep"||!h)){if(i==="walk"){let e=Math.floor(Math.random()*175001)+5e3;D.current=setTimeout(()=>{l("idle"),E.current=!0,setTimeout(()=>{E.current=!1},3e4)},e)}else{let e=()=>{if(!E.current&&Math.random()>.4){l("walk");return}D.current=setTimeout(e,5e3)};e()}return()=>{D.current&&clearTimeout(D.current)}}},[i,h]);let $=e=>{y.current=e,w.current&&(w.current.style.transform=`translateX(${e}px)`)};d(()=>{if(i!=="walk"||b!==null||c!==null||!h)return;let e=0,o=W-se;if(o<=e)return;let t=0,n=null,u=v=>{let G=n===null?0:Math.min(v-n,He);n=v;let g=y.current+(H==="right"?1:-1)*We*G;if(g<=e||g>=o){if(Math.random()>.5){l("idle"),E.current=!0,setTimeout(()=>{E.current=!1},3e4);return}$(g<=e?e+2:o-2),Te(z=>z==="right"?"left":"right");return}$(g),t=requestAnimationFrame(u)};return t=requestAnimationFrame(u),()=>cancelAnimationFrame(t)},[i,b,c,H,h,W]),d(()=>{let e=F.current;if(!e)return;let o=n=>{Re(n),$(Math.max(Math.min(y.current,n-se),0))};if(o(e.clientWidth),typeof ResizeObserver>"u")return;let t=new ResizeObserver(([n])=>{n&&o(n.contentRect.width)});return t.observe(e),()=>t.disconnect()},[]);let Pe=e=>{if(de?.(e),e.defaultPrevented)return;let o=Date.now();i==="sleep"&&l("idle"),T.current&&(A(),P(null)),_.current=!1,ee(!0),R.current&&clearTimeout(R.current),R.current=setTimeout(()=>Z(!1),Fe);let t=[...O.current,o].filter(n=>o-n<=1e4);O.current=t,t.length>5?(O.current=[],U("angry",!0)):U(oe.current,!0)},ve=()=>{j(!0),T.current&&A(),b&&(_.current=!0,s.current&&clearTimeout(s.current))},Me=()=>{j(!1),ee(!1),T.current&&ne(B.current),b&&(_.current=!1,s.current&&clearTimeout(s.current),s.current=setTimeout(()=>{C(null)},5e3))},I=c!==null&&i!=="sleep",p=i==="sleep"?null:I?c.emotion:b,X=I||p!==null&&(xe&&!ye||he||be);Ae(()=>{let e=F.current,o=w.current,t=Q.current;if(!X||!e||!o||!t)return;let n=e.clientWidth,u=Math.max(Math.min(Le,n-V*2),0),v=Math.min(t.offsetWidth,u),G=y.current+o.offsetWidth/2,g=V,z=n-V-v,Se=G-v/2,De=Math.max(Math.min(Se,z),g);ge({left:De-y.current,maxWidth:u})},[X,p,c,W]);let we=()=>p?`pixel-pet-state-${p}`:(!h||I)&&i==="walk"?"pixel-pet-state-idle":`pixel-pet-state-${i}`,Ce=e=>{let o=e?$e[e]:Xe,t=e&&ue[e]||{},n=t.backgroundColor||m.backgroundColor||o.backgroundColor,u=t.borderColor||m.borderColor||o.borderColor;return{backgroundColor:n,color:t.textColor||m.textColor||"#ffffff",border:`${t.borderWidth||m.borderWidth||"1px"} solid ${u}`,borderRadius:t.borderRadius||m.borderRadius||"0",fontFamily:t.fontFamily||m.fontFamily||"monospace"}};return x("div",{className:"pixel-pet",ref:F,style:q?{"--pixel-pet-color":q}:void 0,children:Ge("div",{ref:w,className:"pixel-pet-container",style:{transform:`translateX(${ae}px)`},onMouseEnter:ve,onMouseLeave:Me,children:[x("div",{className:"pixel-pet-status",role:"status",children:X&&x("div",{ref:Q,className:"pixel-pet-dialog",style:{...Ce(p),...N&&{left:N.left,maxWidth:N.maxWidth}},children:I?S(c.message,c.emotion):p&&ke[p]})}),x("button",{type:"button","aria-label":(ce??f).split("{name}").join(f),className:`pixel-pet-sprite pixel-pet-${M} ${we()} ${H==="left"?"pixel-pet-flip-x":""}`,style:K?{backgroundImage:`url("${K}")`}:void 0,onClick:Pe,onFocus:e=>J(e.currentTarget.matches(":focus-visible")),onBlur:()=>J(!1)})]})})};export{je as PixelPet};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vrixton/pixel-pet",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A pixel art pet for React that walks, sleeps, reacts to clicks, and speaks on demand.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",