loading-games 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/svelte/index.ts"],"sourcesContent":["/**\r\n * Svelte wrapper for loading-games\r\n *\r\n * Provides a Svelte action and a factory function for programmatic control.\r\n * Svelte handles custom elements natively, so the <loading-game> element\r\n * can also be used directly in templates after importing this module.\r\n *\r\n * @example Using the Svelte action\r\n * <script>\r\n * import { loadingGame } from 'loading-games/svelte'\r\n * let active = false\r\n * </script>\r\n * <div use:loadingGame={{ game: 'snake', active }}>\r\n *\r\n * @example Direct web component usage\r\n * <script>\r\n * import 'loading-games/svelte'\r\n * let active = false\r\n * </script>\r\n * <loading-game game=\"snake\" active={active ? 'true' : undefined} />\r\n */\r\n\r\n// Register web component\r\nimport '../index.js'\r\n\r\nimport type {\r\n GameName,\r\n GameSize,\r\n ExitAnimation,\r\n ThemeObject,\r\n Score,\r\n GameResult,\r\n} from '../types.js'\r\n\r\n// Re-export types for convenience\r\nexport type {\r\n GameName,\r\n GameSize,\r\n ExitAnimation,\r\n ThemeObject,\r\n LoadingGameOptions,\r\n Score,\r\n GameResult,\r\n} from '../types.js'\r\n\r\n// ─── Action Options ──────────────────────────────────────────────────────────\r\n\r\nexport interface LoadingGameActionOptions {\r\n /** Which game to render. @default 'random' */\r\n game?: GameName\r\n /** Controls whether the game is shown. @default false */\r\n active?: boolean\r\n /** Container size preset. @default 'md' */\r\n size?: GameSize\r\n /** Milliseconds before showing the game. @default 800 */\r\n delay?: number\r\n /** Minimum display time once game appears. @default 0 */\r\n minDisplay?: number\r\n /** Exit animation style. @default 'fade' */\r\n exitAnimation?: ExitAnimation\r\n /** Persist scores to localStorage. @default true */\r\n saveScores?: boolean\r\n /** Score storage namespace. */\r\n namespace?: string\r\n /** Theme overrides. */\r\n theme?: ThemeObject\r\n /** Fires on every score change. */\r\n onScore?: (score: Score) => void\r\n /** Fires when a game round ends. */\r\n onGameOver?: (result: GameResult) => void\r\n /** Fires when loading completes and game exits. */\r\n onComplete?: () => void\r\n /** Fires on error. */\r\n onError?: (err: Error) => void\r\n}\r\n\r\n// ─── Svelte Action ───────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Svelte action that creates and manages a <loading-game> element.\r\n *\r\n * @example\r\n * <div use:loadingGame={{ game: 'snake', active: isLoading }} />\r\n */\r\nexport function loadingGame(node: HTMLElement, options: LoadingGameActionOptions = {}) {\r\n const el = document.createElement('loading-game')\r\n syncAttributes(el, options)\r\n\r\n if (options.theme) {\r\n (el as unknown as { theme: ThemeObject }).theme = options.theme\r\n }\r\n\r\n const handlers = attachListeners(el, options)\r\n node.appendChild(el)\r\n\r\n return {\r\n update(newOptions: LoadingGameActionOptions) {\r\n syncAttributes(el, newOptions)\r\n\r\n if (newOptions.theme) {\r\n (el as unknown as { theme: ThemeObject }).theme = newOptions.theme\r\n }\r\n\r\n // Reattach listeners if callbacks changed\r\n removeListeners(el, handlers)\r\n const newHandlers = attachListeners(el, newOptions)\r\n Object.assign(handlers, newHandlers)\r\n options = newOptions\r\n },\r\n destroy() {\r\n removeListeners(el, handlers)\r\n el.remove()\r\n },\r\n }\r\n}\r\n\r\n// ─── Programmatic Factory ────────────────────────────────────────────────────\r\n\r\nexport interface LoadingGameInstance {\r\n /** Start the game (set active=true). */\r\n start: () => void\r\n /** Stop the game (set active=false). */\r\n stop: () => void\r\n /** Update the theme at runtime. */\r\n setTheme: (theme: ThemeObject) => void\r\n /** Remove the element and clean up. */\r\n destroy: () => void\r\n /** The underlying <loading-game> element. */\r\n element: HTMLElement\r\n}\r\n\r\n/**\r\n * Programmatically create a loading-game instance inside a container.\r\n *\r\n * @example\r\n * const game = createLoadingGame(containerEl, { game: 'flappy' })\r\n * game.start()\r\n * // later...\r\n * game.stop()\r\n * game.destroy()\r\n */\r\nexport function createLoadingGame(\r\n container: HTMLElement,\r\n options: LoadingGameActionOptions = {},\r\n): LoadingGameInstance {\r\n const el = document.createElement('loading-game')\r\n syncAttributes(el, options)\r\n\r\n if (options.theme) {\r\n (el as unknown as { theme: ThemeObject }).theme = options.theme\r\n }\r\n\r\n const handlers = attachListeners(el, options)\r\n container.appendChild(el)\r\n\r\n return {\r\n start() {\r\n el.setAttribute('active', 'true')\r\n },\r\n stop() {\r\n el.removeAttribute('active')\r\n },\r\n setTheme(theme: ThemeObject) {\r\n (el as unknown as { theme: ThemeObject }).theme = theme\r\n },\r\n destroy() {\r\n removeListeners(el, handlers)\r\n el.remove()\r\n },\r\n element: el,\r\n }\r\n}\r\n\r\n// ─── Internal Helpers ────────────────────────────────────────────────────────\r\n\r\ninterface ListenerMap {\r\n score: (e: Event) => void\r\n gameover: (e: Event) => void\r\n complete: () => void\r\n error: (e: Event) => void\r\n}\r\n\r\nfunction syncAttributes(el: HTMLElement, options: LoadingGameActionOptions): void {\r\n el.setAttribute('game', options.game ?? 'random')\r\n el.setAttribute('size', options.size ?? 'md')\r\n el.setAttribute('delay', String(options.delay ?? 800))\r\n el.setAttribute('min-display', String(options.minDisplay ?? 0))\r\n el.setAttribute('exit-animation', options.exitAnimation ?? 'fade')\r\n el.setAttribute('save-scores', (options.saveScores ?? true) ? 'true' : 'false')\r\n\r\n if (options.namespace) {\r\n el.setAttribute('namespace', options.namespace)\r\n } else {\r\n el.removeAttribute('namespace')\r\n }\r\n\r\n if (options.active) {\r\n el.setAttribute('active', 'true')\r\n } else {\r\n el.removeAttribute('active')\r\n }\r\n}\r\n\r\nfunction attachListeners(el: HTMLElement, options: LoadingGameActionOptions): ListenerMap {\r\n const handlers: ListenerMap = {\r\n score: (e: Event) => options.onScore?.((e as CustomEvent<Score>).detail),\r\n gameover: (e: Event) => options.onGameOver?.((e as CustomEvent<GameResult>).detail),\r\n complete: () => options.onComplete?.(),\r\n error: (e: Event) => options.onError?.((e as CustomEvent<Error>).detail),\r\n }\r\n\r\n el.addEventListener('lg:score', handlers.score)\r\n el.addEventListener('lg:gameover', handlers.gameover)\r\n el.addEventListener('lg:complete', handlers.complete)\r\n el.addEventListener('lg:error', handlers.error)\r\n\r\n return handlers\r\n}\r\n\r\nfunction removeListeners(el: HTMLElement, handlers: ListenerMap): void {\r\n el.removeEventListener('lg:score', handlers.score)\r\n el.removeEventListener('lg:gameover', handlers.gameover)\r\n el.removeEventListener('lg:complete', handlers.complete)\r\n el.removeEventListener('lg:error', handlers.error)\r\n}\r\n"],"mappings":"AAuBA,MAAO,cA6DA,SAASA,EAAYC,EAAmBC,EAAoC,CAAC,EAAG,CACrF,IAAMC,EAAK,SAAS,cAAc,cAAc,EAChDC,EAAeD,EAAID,CAAO,EAEtBA,EAAQ,QACTC,EAAyC,MAAQD,EAAQ,OAG5D,IAAMG,EAAWC,EAAgBH,EAAID,CAAO,EAC5C,OAAAD,EAAK,YAAYE,CAAE,EAEZ,CACL,OAAOI,EAAsC,CAC3CH,EAAeD,EAAII,CAAU,EAEzBA,EAAW,QACZJ,EAAyC,MAAQI,EAAW,OAI/DC,EAAgBL,EAAIE,CAAQ,EAC5B,IAAMI,EAAcH,EAAgBH,EAAII,CAAU,EAClD,OAAO,OAAOF,EAAUI,CAAW,EACnCP,EAAUK,CACZ,EACA,SAAU,CACRC,EAAgBL,EAAIE,CAAQ,EAC5BF,EAAG,OAAO,CACZ,CACF,CACF,CA2BO,SAASO,EACdC,EACAT,EAAoC,CAAC,EAChB,CACrB,IAAMC,EAAK,SAAS,cAAc,cAAc,EAChDC,EAAeD,EAAID,CAAO,EAEtBA,EAAQ,QACTC,EAAyC,MAAQD,EAAQ,OAG5D,IAAMG,EAAWC,EAAgBH,EAAID,CAAO,EAC5C,OAAAS,EAAU,YAAYR,CAAE,EAEjB,CACL,OAAQ,CACNA,EAAG,aAAa,SAAU,MAAM,CAClC,EACA,MAAO,CACLA,EAAG,gBAAgB,QAAQ,CAC7B,EACA,SAASS,EAAoB,CAC1BT,EAAyC,MAAQS,CACpD,EACA,SAAU,CACRJ,EAAgBL,EAAIE,CAAQ,EAC5BF,EAAG,OAAO,CACZ,EACA,QAASA,CACX,CACF,CAWA,SAASC,EAAeD,EAAiBD,EAAyC,CAChFC,EAAG,aAAa,OAAQD,EAAQ,MAAQ,QAAQ,EAChDC,EAAG,aAAa,OAAQD,EAAQ,MAAQ,IAAI,EAC5CC,EAAG,aAAa,QAAS,OAAOD,EAAQ,OAAS,GAAG,CAAC,EACrDC,EAAG,aAAa,cAAe,OAAOD,EAAQ,YAAc,CAAC,CAAC,EAC9DC,EAAG,aAAa,iBAAkBD,EAAQ,eAAiB,MAAM,EACjEC,EAAG,aAAa,cAAgBD,EAAQ,YAAc,GAAQ,OAAS,OAAO,EAE1EA,EAAQ,UACVC,EAAG,aAAa,YAAaD,EAAQ,SAAS,EAE9CC,EAAG,gBAAgB,WAAW,EAG5BD,EAAQ,OACVC,EAAG,aAAa,SAAU,MAAM,EAEhCA,EAAG,gBAAgB,QAAQ,CAE/B,CAEA,SAASG,EAAgBH,EAAiBD,EAAgD,CACxF,IAAMG,EAAwB,CAC5B,MAAQQ,GAAaX,EAAQ,UAAWW,EAAyB,MAAM,EACvE,SAAWA,GAAaX,EAAQ,aAAcW,EAA8B,MAAM,EAClF,SAAU,IAAMX,EAAQ,aAAa,EACrC,MAAQW,GAAaX,EAAQ,UAAWW,EAAyB,MAAM,CACzE,EAEA,OAAAV,EAAG,iBAAiB,WAAYE,EAAS,KAAK,EAC9CF,EAAG,iBAAiB,cAAeE,EAAS,QAAQ,EACpDF,EAAG,iBAAiB,cAAeE,EAAS,QAAQ,EACpDF,EAAG,iBAAiB,WAAYE,EAAS,KAAK,EAEvCA,CACT,CAEA,SAASG,EAAgBL,EAAiBE,EAA6B,CACrEF,EAAG,oBAAoB,WAAYE,EAAS,KAAK,EACjDF,EAAG,oBAAoB,cAAeE,EAAS,QAAQ,EACvDF,EAAG,oBAAoB,cAAeE,EAAS,QAAQ,EACvDF,EAAG,oBAAoB,WAAYE,EAAS,KAAK,CACnD","names":["loadingGame","node","options","el","syncAttributes","handlers","attachListeners","newOptions","removeListeners","newHandlers","createLoadingGame","container","theme","e"]}
@@ -0,0 +1,2 @@
1
+ "use strict";var u=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var O=Object.prototype.hasOwnProperty;var x=(e,a)=>{for(var r in a)u(e,r,{get:a[r],enumerable:!0})},T=(e,a,r,m)=>{if(a&&typeof a=="object"||typeof a=="function")for(let t of S(a))!O.call(e,t)&&t!==r&&u(e,t,{get:()=>a[t],enumerable:!(m=b(a,t))||m.enumerable});return e};var j=e=>T(u({},"__esModule",{value:!0}),e);var A={};x(A,{LoadingGame:()=>E,default:()=>R,useLoadingGame:()=>g});module.exports=j(A);var o=require("vue"),U=require("../index.js");var l=require("vue"),z=require("../index.js");function g(e={}){let a=(0,l.ref)(null),r=(0,l.ref)(null),m=(0,l.ref)(!1),t=null;function d(){return{game:e.game??"random",size:e.size??"md",delay:String(e.delay??800),"min-display":String(e.minDisplay??0),"exit-animation":e.exitAnimation??"fade","save-scores":e.saveScores??!0?"true":"false",namespace:e.namespace}}function s(i){e.onScore?.(i.detail)}function n(i){e.onGameOver?.(i.detail)}function c(){m.value=!1,e.onComplete?.()}function v(i){m.value=!1,e.onError?.(i.detail)}(0,l.onMounted)(()=>{let i=a.value;if(!i)return;t=document.createElement("loading-game");let y=d();for(let[G,f]of Object.entries(y))f!==void 0&&t.setAttribute(G,f);e.theme&&(t.theme=e.theme),t.addEventListener("lg:score",s),t.addEventListener("lg:gameover",n),t.addEventListener("lg:complete",c),t.addEventListener("lg:error",v),i.appendChild(t),r.value=t}),(0,l.onUnmounted)(()=>{t&&(t.removeEventListener("lg:score",s),t.removeEventListener("lg:gameover",n),t.removeEventListener("lg:complete",c),t.removeEventListener("lg:error",v),t.remove(),t=null,r.value=null)});function p(){m.value=!0,t?.setAttribute("active","true")}function h(){m.value=!1,t?.removeAttribute("active")}function L(i){t&&(t.theme=i)}return{containerRef:a,isActive:m,start:p,stop:h,setTheme:L,element:r}}var E=(0,o.defineComponent)({name:"LoadingGame",props:{game:{type:String,default:"random"},active:{type:Boolean,default:!1},theme:{type:Object,default:void 0},size:{type:String,default:"md"},delay:{type:Number,default:800},minDisplay:{type:Number,default:0},exitAnimation:{type:String,default:"fade"},saveScores:{type:Boolean,default:!0},namespace:{type:String,default:void 0}},emits:["score","gameover","complete","error"],setup(e,{emit:a}){let r=(0,o.ref)(null),m=n=>a("score",n.detail),t=n=>a("gameover",n.detail),d=()=>a("complete"),s=n=>a("error",n.detail);return(0,o.onMounted)(()=>{let n=r.value;n&&(n.addEventListener("lg:score",m),n.addEventListener("lg:gameover",t),n.addEventListener("lg:complete",d),n.addEventListener("lg:error",s))}),(0,o.onUnmounted)(()=>{let n=r.value;n&&(n.removeEventListener("lg:score",m),n.removeEventListener("lg:gameover",t),n.removeEventListener("lg:complete",d),n.removeEventListener("lg:error",s))}),(0,o.watch)(()=>e.theme,n=>{r.value&&n&&(r.value.theme=n)},{deep:!0}),()=>(0,o.h)("loading-game",{ref:r,game:e.game,active:e.active?"true":void 0,size:e.size,delay:String(e.delay),"min-display":String(e.minDisplay),"exit-animation":e.exitAnimation,"save-scores":e.saveScores?"true":"false",namespace:e.namespace})}}),R=E;0&&(module.exports={LoadingGame,useLoadingGame});
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/vue/index.ts","../../src/vue/composable.ts"],"sourcesContent":["/**\r\n * Vue 3 wrapper for loading-games\r\n *\r\n * @example\r\n * import { LoadingGame } from 'loading-games/vue'\r\n *\r\n * <LoadingGame game=\"snake\" :active=\"isLoading\" />\r\n */\r\n\r\nimport { defineComponent, ref, onMounted, onUnmounted, watch, h } from 'vue'\r\nimport type { ThemeObject } from '../types.js'\r\n\r\n// Register web component\r\nimport '../index.js'\r\n\r\n// Composable for programmatic control\r\nexport { useLoadingGame } from './composable.js'\r\nexport type { UseLoadingGameOptions, UseLoadingGameReturn } from './composable.js'\r\n\r\n// Re-export types for convenience\r\nexport type {\r\n GameName,\r\n GameSize,\r\n ExitAnimation,\r\n ThemeObject,\r\n LoadingGameOptions,\r\n Score,\r\n GameResult,\r\n} from '../types.js'\r\n\r\nexport const LoadingGame = defineComponent({\r\n name: 'LoadingGame',\r\n props: {\r\n game: { type: String, default: 'random' },\r\n active: { type: Boolean, default: false },\r\n theme: { type: Object as () => ThemeObject, default: undefined },\r\n size: { type: String, default: 'md' },\r\n delay: { type: Number, default: 800 },\r\n minDisplay: { type: Number, default: 0 },\r\n exitAnimation: { type: String, default: 'fade' },\r\n saveScores: { type: Boolean, default: true },\r\n namespace: { type: String, default: undefined },\r\n },\r\n emits: ['score', 'gameover', 'complete', 'error'],\r\n setup(props, { emit }) {\r\n const el = ref<HTMLElement | null>(null)\r\n\r\n const handleScore = (e: Event) => emit('score', (e as CustomEvent).detail)\r\n const handleGameOver = (e: Event) => emit('gameover', (e as CustomEvent).detail)\r\n const handleComplete = () => emit('complete')\r\n const handleError = (e: Event) => emit('error', (e as CustomEvent).detail)\r\n\r\n onMounted(() => {\r\n const node = el.value\r\n if (!node) return\r\n node.addEventListener('lg:score', handleScore)\r\n node.addEventListener('lg:gameover', handleGameOver)\r\n node.addEventListener('lg:complete', handleComplete)\r\n node.addEventListener('lg:error', handleError)\r\n })\r\n\r\n onUnmounted(() => {\r\n const node = el.value\r\n if (!node) return\r\n node.removeEventListener('lg:score', handleScore)\r\n node.removeEventListener('lg:gameover', handleGameOver)\r\n node.removeEventListener('lg:complete', handleComplete)\r\n node.removeEventListener('lg:error', handleError)\r\n })\r\n\r\n // Sync theme as property\r\n watch(() => props.theme, (newTheme) => {\r\n if (el.value && newTheme) {\r\n (el.value as unknown as { theme: ThemeObject }).theme = newTheme\r\n }\r\n }, { deep: true })\r\n\r\n return () =>\r\n h('loading-game', {\r\n ref: el,\r\n game: props.game,\r\n active: props.active ? 'true' : undefined,\r\n size: props.size,\r\n delay: String(props.delay),\r\n 'min-display': String(props.minDisplay),\r\n 'exit-animation': props.exitAnimation,\r\n 'save-scores': props.saveScores ? 'true' : 'false',\r\n namespace: props.namespace,\r\n })\r\n },\r\n})\r\n\r\nexport default LoadingGame\r\n","/**\n * useLoadingGame — Vue 3 composable for programmatic control\n *\n * Provides reactive state and methods to control a loading-game\n * element without using the <LoadingGame> component directly.\n *\n * @example\n * const { el, start, stop, setTheme, isActive } = useLoadingGame({\n * game: 'snake',\n * onScore: (s) => console.log(s),\n * })\n *\n * // In template: <div ref=\"el\" />\n * // Or mount imperatively into any container\n */\n\nimport { ref, onMounted, onUnmounted, type Ref } from 'vue'\nimport type {\n GameName,\n GameSize,\n ExitAnimation,\n ThemeObject,\n Score,\n GameResult,\n} from '../types.js'\n\n// Ensure web component is registered\nimport '../index.js'\n\nexport interface UseLoadingGameOptions {\n /** Which game to render. @default 'random' */\n game?: GameName\n /** Container size preset. @default 'md' */\n size?: GameSize\n /** Milliseconds before showing the game. @default 800 */\n delay?: number\n /** Minimum display time once game appears. @default 0 */\n minDisplay?: number\n /** Exit animation style. @default 'fade' */\n exitAnimation?: ExitAnimation\n /** Persist scores to localStorage. @default true */\n saveScores?: boolean\n /** Score storage namespace. */\n namespace?: string\n /** Theme overrides. */\n theme?: ThemeObject\n /** Fires on every score change. */\n onScore?: (score: Score) => void\n /** Fires when a game round ends. */\n onGameOver?: (result: GameResult) => void\n /** Fires when loading completes and game exits. */\n onComplete?: () => void\n /** Fires on error. */\n onError?: (err: Error) => void\n}\n\nexport interface UseLoadingGameReturn {\n /** Ref to bind to a container element. The web component will be created inside it. */\n containerRef: Ref<HTMLElement | null>\n /** Reactive active state. */\n isActive: Ref<boolean>\n /** Start the game (set active=true). */\n start: () => void\n /** Stop the game (set active=false). */\n stop: () => void\n /** Update the theme at runtime. */\n setTheme: (theme: ThemeObject) => void\n /** The underlying <loading-game> element, if mounted. */\n element: Ref<HTMLElement | null>\n}\n\nexport function useLoadingGame(options: UseLoadingGameOptions = {}): UseLoadingGameReturn {\n const containerRef = ref<HTMLElement | null>(null)\n const element = ref<HTMLElement | null>(null)\n const isActive = ref(false)\n\n let lgElement: HTMLElement | null = null\n\n function createAttributes(): Record<string, string | undefined> {\n return {\n game: options.game ?? 'random',\n size: options.size ?? 'md',\n delay: String(options.delay ?? 800),\n 'min-display': String(options.minDisplay ?? 0),\n 'exit-animation': options.exitAnimation ?? 'fade',\n 'save-scores': (options.saveScores ?? true) ? 'true' : 'false',\n namespace: options.namespace,\n }\n }\n\n function handleScore(e: Event) {\n options.onScore?.((e as CustomEvent<Score>).detail)\n }\n function handleGameOver(e: Event) {\n options.onGameOver?.((e as CustomEvent<GameResult>).detail)\n }\n function handleComplete() {\n isActive.value = false\n options.onComplete?.()\n }\n function handleError(e: Event) {\n isActive.value = false\n options.onError?.((e as CustomEvent<Error>).detail)\n }\n\n onMounted(() => {\n const container = containerRef.value\n if (!container) return\n\n lgElement = document.createElement('loading-game')\n\n // Set attributes\n const attrs = createAttributes()\n for (const [key, value] of Object.entries(attrs)) {\n if (value !== undefined) {\n lgElement.setAttribute(key, value)\n }\n }\n\n // Set theme as property\n if (options.theme) {\n (lgElement as unknown as { theme: ThemeObject }).theme = options.theme\n }\n\n // Attach event listeners\n lgElement.addEventListener('lg:score', handleScore)\n lgElement.addEventListener('lg:gameover', handleGameOver)\n lgElement.addEventListener('lg:complete', handleComplete)\n lgElement.addEventListener('lg:error', handleError)\n\n container.appendChild(lgElement)\n element.value = lgElement\n })\n\n onUnmounted(() => {\n if (lgElement) {\n lgElement.removeEventListener('lg:score', handleScore)\n lgElement.removeEventListener('lg:gameover', handleGameOver)\n lgElement.removeEventListener('lg:complete', handleComplete)\n lgElement.removeEventListener('lg:error', handleError)\n lgElement.remove()\n lgElement = null\n element.value = null\n }\n })\n\n function start() {\n isActive.value = true\n lgElement?.setAttribute('active', 'true')\n }\n\n function stop() {\n isActive.value = false\n lgElement?.removeAttribute('active')\n }\n\n function setTheme(theme: ThemeObject) {\n if (lgElement) {\n (lgElement as unknown as { theme: ThemeObject }).theme = theme\n }\n }\n\n return {\n containerRef,\n isActive,\n start,\n stop,\n setTheme,\n element,\n }\n}\n"],"mappings":"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","names":["vue_exports","__export","LoadingGame","vue_default","useLoadingGame","__toCommonJS","import_vue","import__","import_vue","import__","useLoadingGame","options","containerRef","element","isActive","lgElement","createAttributes","handleScore","e","handleGameOver","handleComplete","handleError","container","attrs","key","value","start","stop","setTheme","theme","LoadingGame","props","emit","el","handleScore","e","handleGameOver","handleComplete","handleError","node","newTheme","vue_default"]}
@@ -0,0 +1,157 @@
1
+ import * as vue from 'vue';
2
+ import { Ref } from 'vue';
3
+ import { GameName, GameSize, ExitAnimation, ThemeObject, Score, GameResult } from '../../types.js';
4
+ export { ExitAnimation, GameName, GameResult, GameSize, LoadingGameOptions, Score, ThemeObject } from '../../types.js';
5
+
6
+ /**
7
+ * useLoadingGame — Vue 3 composable for programmatic control
8
+ *
9
+ * Provides reactive state and methods to control a loading-game
10
+ * element without using the <LoadingGame> component directly.
11
+ *
12
+ * @example
13
+ * const { el, start, stop, setTheme, isActive } = useLoadingGame({
14
+ * game: 'snake',
15
+ * onScore: (s) => console.log(s),
16
+ * })
17
+ *
18
+ * // In template: <div ref="el" />
19
+ * // Or mount imperatively into any container
20
+ */
21
+
22
+ interface UseLoadingGameOptions {
23
+ /** Which game to render. @default 'random' */
24
+ game?: GameName;
25
+ /** Container size preset. @default 'md' */
26
+ size?: GameSize;
27
+ /** Milliseconds before showing the game. @default 800 */
28
+ delay?: number;
29
+ /** Minimum display time once game appears. @default 0 */
30
+ minDisplay?: number;
31
+ /** Exit animation style. @default 'fade' */
32
+ exitAnimation?: ExitAnimation;
33
+ /** Persist scores to localStorage. @default true */
34
+ saveScores?: boolean;
35
+ /** Score storage namespace. */
36
+ namespace?: string;
37
+ /** Theme overrides. */
38
+ theme?: ThemeObject;
39
+ /** Fires on every score change. */
40
+ onScore?: (score: Score) => void;
41
+ /** Fires when a game round ends. */
42
+ onGameOver?: (result: GameResult) => void;
43
+ /** Fires when loading completes and game exits. */
44
+ onComplete?: () => void;
45
+ /** Fires on error. */
46
+ onError?: (err: Error) => void;
47
+ }
48
+ interface UseLoadingGameReturn {
49
+ /** Ref to bind to a container element. The web component will be created inside it. */
50
+ containerRef: Ref<HTMLElement | null>;
51
+ /** Reactive active state. */
52
+ isActive: Ref<boolean>;
53
+ /** Start the game (set active=true). */
54
+ start: () => void;
55
+ /** Stop the game (set active=false). */
56
+ stop: () => void;
57
+ /** Update the theme at runtime. */
58
+ setTheme: (theme: ThemeObject) => void;
59
+ /** The underlying <loading-game> element, if mounted. */
60
+ element: Ref<HTMLElement | null>;
61
+ }
62
+ declare function useLoadingGame(options?: UseLoadingGameOptions): UseLoadingGameReturn;
63
+
64
+ declare const LoadingGame: vue.DefineComponent<vue.ExtractPropTypes<{
65
+ game: {
66
+ type: StringConstructor;
67
+ default: string;
68
+ };
69
+ active: {
70
+ type: BooleanConstructor;
71
+ default: boolean;
72
+ };
73
+ theme: {
74
+ type: () => ThemeObject;
75
+ default: undefined;
76
+ };
77
+ size: {
78
+ type: StringConstructor;
79
+ default: string;
80
+ };
81
+ delay: {
82
+ type: NumberConstructor;
83
+ default: number;
84
+ };
85
+ minDisplay: {
86
+ type: NumberConstructor;
87
+ default: number;
88
+ };
89
+ exitAnimation: {
90
+ type: StringConstructor;
91
+ default: string;
92
+ };
93
+ saveScores: {
94
+ type: BooleanConstructor;
95
+ default: boolean;
96
+ };
97
+ namespace: {
98
+ type: StringConstructor;
99
+ default: undefined;
100
+ };
101
+ }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
102
+ [key: string]: any;
103
+ }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, ("score" | "gameover" | "complete" | "error")[], "score" | "gameover" | "complete" | "error", vue.PublicProps, Readonly<vue.ExtractPropTypes<{
104
+ game: {
105
+ type: StringConstructor;
106
+ default: string;
107
+ };
108
+ active: {
109
+ type: BooleanConstructor;
110
+ default: boolean;
111
+ };
112
+ theme: {
113
+ type: () => ThemeObject;
114
+ default: undefined;
115
+ };
116
+ size: {
117
+ type: StringConstructor;
118
+ default: string;
119
+ };
120
+ delay: {
121
+ type: NumberConstructor;
122
+ default: number;
123
+ };
124
+ minDisplay: {
125
+ type: NumberConstructor;
126
+ default: number;
127
+ };
128
+ exitAnimation: {
129
+ type: StringConstructor;
130
+ default: string;
131
+ };
132
+ saveScores: {
133
+ type: BooleanConstructor;
134
+ default: boolean;
135
+ };
136
+ namespace: {
137
+ type: StringConstructor;
138
+ default: undefined;
139
+ };
140
+ }>> & Readonly<{
141
+ onScore?: ((...args: any[]) => any) | undefined;
142
+ onGameover?: ((...args: any[]) => any) | undefined;
143
+ onComplete?: ((...args: any[]) => any) | undefined;
144
+ onError?: ((...args: any[]) => any) | undefined;
145
+ }>, {
146
+ game: string;
147
+ active: boolean;
148
+ theme: ThemeObject;
149
+ size: string;
150
+ delay: number;
151
+ minDisplay: number;
152
+ exitAnimation: string;
153
+ saveScores: boolean;
154
+ namespace: string;
155
+ }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
156
+
157
+ export { LoadingGame, type UseLoadingGameOptions, type UseLoadingGameReturn, LoadingGame as default, useLoadingGame };
@@ -0,0 +1,157 @@
1
+ import * as vue from 'vue';
2
+ import { Ref } from 'vue';
3
+ import { GameName, GameSize, ExitAnimation, ThemeObject, Score, GameResult } from '../../types.js';
4
+ export { ExitAnimation, GameName, GameResult, GameSize, LoadingGameOptions, Score, ThemeObject } from '../../types.js';
5
+
6
+ /**
7
+ * useLoadingGame — Vue 3 composable for programmatic control
8
+ *
9
+ * Provides reactive state and methods to control a loading-game
10
+ * element without using the <LoadingGame> component directly.
11
+ *
12
+ * @example
13
+ * const { el, start, stop, setTheme, isActive } = useLoadingGame({
14
+ * game: 'snake',
15
+ * onScore: (s) => console.log(s),
16
+ * })
17
+ *
18
+ * // In template: <div ref="el" />
19
+ * // Or mount imperatively into any container
20
+ */
21
+
22
+ interface UseLoadingGameOptions {
23
+ /** Which game to render. @default 'random' */
24
+ game?: GameName;
25
+ /** Container size preset. @default 'md' */
26
+ size?: GameSize;
27
+ /** Milliseconds before showing the game. @default 800 */
28
+ delay?: number;
29
+ /** Minimum display time once game appears. @default 0 */
30
+ minDisplay?: number;
31
+ /** Exit animation style. @default 'fade' */
32
+ exitAnimation?: ExitAnimation;
33
+ /** Persist scores to localStorage. @default true */
34
+ saveScores?: boolean;
35
+ /** Score storage namespace. */
36
+ namespace?: string;
37
+ /** Theme overrides. */
38
+ theme?: ThemeObject;
39
+ /** Fires on every score change. */
40
+ onScore?: (score: Score) => void;
41
+ /** Fires when a game round ends. */
42
+ onGameOver?: (result: GameResult) => void;
43
+ /** Fires when loading completes and game exits. */
44
+ onComplete?: () => void;
45
+ /** Fires on error. */
46
+ onError?: (err: Error) => void;
47
+ }
48
+ interface UseLoadingGameReturn {
49
+ /** Ref to bind to a container element. The web component will be created inside it. */
50
+ containerRef: Ref<HTMLElement | null>;
51
+ /** Reactive active state. */
52
+ isActive: Ref<boolean>;
53
+ /** Start the game (set active=true). */
54
+ start: () => void;
55
+ /** Stop the game (set active=false). */
56
+ stop: () => void;
57
+ /** Update the theme at runtime. */
58
+ setTheme: (theme: ThemeObject) => void;
59
+ /** The underlying <loading-game> element, if mounted. */
60
+ element: Ref<HTMLElement | null>;
61
+ }
62
+ declare function useLoadingGame(options?: UseLoadingGameOptions): UseLoadingGameReturn;
63
+
64
+ declare const LoadingGame: vue.DefineComponent<vue.ExtractPropTypes<{
65
+ game: {
66
+ type: StringConstructor;
67
+ default: string;
68
+ };
69
+ active: {
70
+ type: BooleanConstructor;
71
+ default: boolean;
72
+ };
73
+ theme: {
74
+ type: () => ThemeObject;
75
+ default: undefined;
76
+ };
77
+ size: {
78
+ type: StringConstructor;
79
+ default: string;
80
+ };
81
+ delay: {
82
+ type: NumberConstructor;
83
+ default: number;
84
+ };
85
+ minDisplay: {
86
+ type: NumberConstructor;
87
+ default: number;
88
+ };
89
+ exitAnimation: {
90
+ type: StringConstructor;
91
+ default: string;
92
+ };
93
+ saveScores: {
94
+ type: BooleanConstructor;
95
+ default: boolean;
96
+ };
97
+ namespace: {
98
+ type: StringConstructor;
99
+ default: undefined;
100
+ };
101
+ }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
102
+ [key: string]: any;
103
+ }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, ("score" | "gameover" | "complete" | "error")[], "score" | "gameover" | "complete" | "error", vue.PublicProps, Readonly<vue.ExtractPropTypes<{
104
+ game: {
105
+ type: StringConstructor;
106
+ default: string;
107
+ };
108
+ active: {
109
+ type: BooleanConstructor;
110
+ default: boolean;
111
+ };
112
+ theme: {
113
+ type: () => ThemeObject;
114
+ default: undefined;
115
+ };
116
+ size: {
117
+ type: StringConstructor;
118
+ default: string;
119
+ };
120
+ delay: {
121
+ type: NumberConstructor;
122
+ default: number;
123
+ };
124
+ minDisplay: {
125
+ type: NumberConstructor;
126
+ default: number;
127
+ };
128
+ exitAnimation: {
129
+ type: StringConstructor;
130
+ default: string;
131
+ };
132
+ saveScores: {
133
+ type: BooleanConstructor;
134
+ default: boolean;
135
+ };
136
+ namespace: {
137
+ type: StringConstructor;
138
+ default: undefined;
139
+ };
140
+ }>> & Readonly<{
141
+ onScore?: ((...args: any[]) => any) | undefined;
142
+ onGameover?: ((...args: any[]) => any) | undefined;
143
+ onComplete?: ((...args: any[]) => any) | undefined;
144
+ onError?: ((...args: any[]) => any) | undefined;
145
+ }>, {
146
+ game: string;
147
+ active: boolean;
148
+ theme: ThemeObject;
149
+ size: string;
150
+ delay: number;
151
+ minDisplay: number;
152
+ exitAnimation: string;
153
+ saveScores: boolean;
154
+ namespace: string;
155
+ }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
156
+
157
+ export { LoadingGame, type UseLoadingGameOptions, type UseLoadingGameReturn, LoadingGame as default, useLoadingGame };
@@ -0,0 +1,2 @@
1
+ import{defineComponent as G,ref as b,onMounted as S,onUnmounted as O,watch as x,h as T}from"vue";import"../index.js";import{ref as s,onMounted as h,onUnmounted as L}from"vue";import"../index.js";function y(e={}){let m=s(null),r=s(null),o=s(!1),t=null;function l(){return{game:e.game??"random",size:e.size??"md",delay:String(e.delay??800),"min-display":String(e.minDisplay??0),"exit-animation":e.exitAnimation??"fade","save-scores":e.saveScores??!0?"true":"false",namespace:e.namespace}}function i(a){e.onScore?.(a.detail)}function n(a){e.onGameOver?.(a.detail)}function d(){o.value=!1,e.onComplete?.()}function u(a){o.value=!1,e.onError?.(a.detail)}h(()=>{let a=m.value;if(!a)return;t=document.createElement("loading-game");let E=l();for(let[p,c]of Object.entries(E))c!==void 0&&t.setAttribute(p,c);e.theme&&(t.theme=e.theme),t.addEventListener("lg:score",i),t.addEventListener("lg:gameover",n),t.addEventListener("lg:complete",d),t.addEventListener("lg:error",u),a.appendChild(t),r.value=t}),L(()=>{t&&(t.removeEventListener("lg:score",i),t.removeEventListener("lg:gameover",n),t.removeEventListener("lg:complete",d),t.removeEventListener("lg:error",u),t.remove(),t=null,r.value=null)});function v(){o.value=!0,t?.setAttribute("active","true")}function f(){o.value=!1,t?.removeAttribute("active")}function g(a){t&&(t.theme=a)}return{containerRef:m,isActive:o,start:v,stop:f,setTheme:g,element:r}}var j=G({name:"LoadingGame",props:{game:{type:String,default:"random"},active:{type:Boolean,default:!1},theme:{type:Object,default:void 0},size:{type:String,default:"md"},delay:{type:Number,default:800},minDisplay:{type:Number,default:0},exitAnimation:{type:String,default:"fade"},saveScores:{type:Boolean,default:!0},namespace:{type:String,default:void 0}},emits:["score","gameover","complete","error"],setup(e,{emit:m}){let r=b(null),o=n=>m("score",n.detail),t=n=>m("gameover",n.detail),l=()=>m("complete"),i=n=>m("error",n.detail);return S(()=>{let n=r.value;n&&(n.addEventListener("lg:score",o),n.addEventListener("lg:gameover",t),n.addEventListener("lg:complete",l),n.addEventListener("lg:error",i))}),O(()=>{let n=r.value;n&&(n.removeEventListener("lg:score",o),n.removeEventListener("lg:gameover",t),n.removeEventListener("lg:complete",l),n.removeEventListener("lg:error",i))}),x(()=>e.theme,n=>{r.value&&n&&(r.value.theme=n)},{deep:!0}),()=>T("loading-game",{ref:r,game:e.game,active:e.active?"true":void 0,size:e.size,delay:String(e.delay),"min-display":String(e.minDisplay),"exit-animation":e.exitAnimation,"save-scores":e.saveScores?"true":"false",namespace:e.namespace})}}),U=j;export{j as LoadingGame,U as default,y as useLoadingGame};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/vue/index.ts","../../src/vue/composable.ts"],"sourcesContent":["/**\r\n * Vue 3 wrapper for loading-games\r\n *\r\n * @example\r\n * import { LoadingGame } from 'loading-games/vue'\r\n *\r\n * <LoadingGame game=\"snake\" :active=\"isLoading\" />\r\n */\r\n\r\nimport { defineComponent, ref, onMounted, onUnmounted, watch, h } from 'vue'\r\nimport type { ThemeObject } from '../types.js'\r\n\r\n// Register web component\r\nimport '../index.js'\r\n\r\n// Composable for programmatic control\r\nexport { useLoadingGame } from './composable.js'\r\nexport type { UseLoadingGameOptions, UseLoadingGameReturn } from './composable.js'\r\n\r\n// Re-export types for convenience\r\nexport type {\r\n GameName,\r\n GameSize,\r\n ExitAnimation,\r\n ThemeObject,\r\n LoadingGameOptions,\r\n Score,\r\n GameResult,\r\n} from '../types.js'\r\n\r\nexport const LoadingGame = defineComponent({\r\n name: 'LoadingGame',\r\n props: {\r\n game: { type: String, default: 'random' },\r\n active: { type: Boolean, default: false },\r\n theme: { type: Object as () => ThemeObject, default: undefined },\r\n size: { type: String, default: 'md' },\r\n delay: { type: Number, default: 800 },\r\n minDisplay: { type: Number, default: 0 },\r\n exitAnimation: { type: String, default: 'fade' },\r\n saveScores: { type: Boolean, default: true },\r\n namespace: { type: String, default: undefined },\r\n },\r\n emits: ['score', 'gameover', 'complete', 'error'],\r\n setup(props, { emit }) {\r\n const el = ref<HTMLElement | null>(null)\r\n\r\n const handleScore = (e: Event) => emit('score', (e as CustomEvent).detail)\r\n const handleGameOver = (e: Event) => emit('gameover', (e as CustomEvent).detail)\r\n const handleComplete = () => emit('complete')\r\n const handleError = (e: Event) => emit('error', (e as CustomEvent).detail)\r\n\r\n onMounted(() => {\r\n const node = el.value\r\n if (!node) return\r\n node.addEventListener('lg:score', handleScore)\r\n node.addEventListener('lg:gameover', handleGameOver)\r\n node.addEventListener('lg:complete', handleComplete)\r\n node.addEventListener('lg:error', handleError)\r\n })\r\n\r\n onUnmounted(() => {\r\n const node = el.value\r\n if (!node) return\r\n node.removeEventListener('lg:score', handleScore)\r\n node.removeEventListener('lg:gameover', handleGameOver)\r\n node.removeEventListener('lg:complete', handleComplete)\r\n node.removeEventListener('lg:error', handleError)\r\n })\r\n\r\n // Sync theme as property\r\n watch(() => props.theme, (newTheme) => {\r\n if (el.value && newTheme) {\r\n (el.value as unknown as { theme: ThemeObject }).theme = newTheme\r\n }\r\n }, { deep: true })\r\n\r\n return () =>\r\n h('loading-game', {\r\n ref: el,\r\n game: props.game,\r\n active: props.active ? 'true' : undefined,\r\n size: props.size,\r\n delay: String(props.delay),\r\n 'min-display': String(props.minDisplay),\r\n 'exit-animation': props.exitAnimation,\r\n 'save-scores': props.saveScores ? 'true' : 'false',\r\n namespace: props.namespace,\r\n })\r\n },\r\n})\r\n\r\nexport default LoadingGame\r\n","/**\n * useLoadingGame — Vue 3 composable for programmatic control\n *\n * Provides reactive state and methods to control a loading-game\n * element without using the <LoadingGame> component directly.\n *\n * @example\n * const { el, start, stop, setTheme, isActive } = useLoadingGame({\n * game: 'snake',\n * onScore: (s) => console.log(s),\n * })\n *\n * // In template: <div ref=\"el\" />\n * // Or mount imperatively into any container\n */\n\nimport { ref, onMounted, onUnmounted, type Ref } from 'vue'\nimport type {\n GameName,\n GameSize,\n ExitAnimation,\n ThemeObject,\n Score,\n GameResult,\n} from '../types.js'\n\n// Ensure web component is registered\nimport '../index.js'\n\nexport interface UseLoadingGameOptions {\n /** Which game to render. @default 'random' */\n game?: GameName\n /** Container size preset. @default 'md' */\n size?: GameSize\n /** Milliseconds before showing the game. @default 800 */\n delay?: number\n /** Minimum display time once game appears. @default 0 */\n minDisplay?: number\n /** Exit animation style. @default 'fade' */\n exitAnimation?: ExitAnimation\n /** Persist scores to localStorage. @default true */\n saveScores?: boolean\n /** Score storage namespace. */\n namespace?: string\n /** Theme overrides. */\n theme?: ThemeObject\n /** Fires on every score change. */\n onScore?: (score: Score) => void\n /** Fires when a game round ends. */\n onGameOver?: (result: GameResult) => void\n /** Fires when loading completes and game exits. */\n onComplete?: () => void\n /** Fires on error. */\n onError?: (err: Error) => void\n}\n\nexport interface UseLoadingGameReturn {\n /** Ref to bind to a container element. The web component will be created inside it. */\n containerRef: Ref<HTMLElement | null>\n /** Reactive active state. */\n isActive: Ref<boolean>\n /** Start the game (set active=true). */\n start: () => void\n /** Stop the game (set active=false). */\n stop: () => void\n /** Update the theme at runtime. */\n setTheme: (theme: ThemeObject) => void\n /** The underlying <loading-game> element, if mounted. */\n element: Ref<HTMLElement | null>\n}\n\nexport function useLoadingGame(options: UseLoadingGameOptions = {}): UseLoadingGameReturn {\n const containerRef = ref<HTMLElement | null>(null)\n const element = ref<HTMLElement | null>(null)\n const isActive = ref(false)\n\n let lgElement: HTMLElement | null = null\n\n function createAttributes(): Record<string, string | undefined> {\n return {\n game: options.game ?? 'random',\n size: options.size ?? 'md',\n delay: String(options.delay ?? 800),\n 'min-display': String(options.minDisplay ?? 0),\n 'exit-animation': options.exitAnimation ?? 'fade',\n 'save-scores': (options.saveScores ?? true) ? 'true' : 'false',\n namespace: options.namespace,\n }\n }\n\n function handleScore(e: Event) {\n options.onScore?.((e as CustomEvent<Score>).detail)\n }\n function handleGameOver(e: Event) {\n options.onGameOver?.((e as CustomEvent<GameResult>).detail)\n }\n function handleComplete() {\n isActive.value = false\n options.onComplete?.()\n }\n function handleError(e: Event) {\n isActive.value = false\n options.onError?.((e as CustomEvent<Error>).detail)\n }\n\n onMounted(() => {\n const container = containerRef.value\n if (!container) return\n\n lgElement = document.createElement('loading-game')\n\n // Set attributes\n const attrs = createAttributes()\n for (const [key, value] of Object.entries(attrs)) {\n if (value !== undefined) {\n lgElement.setAttribute(key, value)\n }\n }\n\n // Set theme as property\n if (options.theme) {\n (lgElement as unknown as { theme: ThemeObject }).theme = options.theme\n }\n\n // Attach event listeners\n lgElement.addEventListener('lg:score', handleScore)\n lgElement.addEventListener('lg:gameover', handleGameOver)\n lgElement.addEventListener('lg:complete', handleComplete)\n lgElement.addEventListener('lg:error', handleError)\n\n container.appendChild(lgElement)\n element.value = lgElement\n })\n\n onUnmounted(() => {\n if (lgElement) {\n lgElement.removeEventListener('lg:score', handleScore)\n lgElement.removeEventListener('lg:gameover', handleGameOver)\n lgElement.removeEventListener('lg:complete', handleComplete)\n lgElement.removeEventListener('lg:error', handleError)\n lgElement.remove()\n lgElement = null\n element.value = null\n }\n })\n\n function start() {\n isActive.value = true\n lgElement?.setAttribute('active', 'true')\n }\n\n function stop() {\n isActive.value = false\n lgElement?.removeAttribute('active')\n }\n\n function setTheme(theme: ThemeObject) {\n if (lgElement) {\n (lgElement as unknown as { theme: ThemeObject }).theme = theme\n }\n }\n\n return {\n containerRef,\n isActive,\n start,\n stop,\n setTheme,\n element,\n }\n}\n"],"mappings":"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","names":["defineComponent","ref","onMounted","onUnmounted","watch","h","ref","onMounted","onUnmounted","useLoadingGame","options","containerRef","element","isActive","lgElement","createAttributes","handleScore","e","handleGameOver","handleComplete","handleError","container","attrs","key","value","start","stop","setTheme","theme","LoadingGame","defineComponent","props","emit","el","ref","handleScore","e","handleGameOver","handleComplete","handleError","onMounted","node","onUnmounted","watch","newTheme","h","vue_default"]}
package/package.json ADDED
@@ -0,0 +1,105 @@
1
+ {
2
+ "name": "loading-games",
3
+ "version": "1.0.0",
4
+ "description": "Playable mini-games that replace loading spinners",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "require": {
17
+ "types": "./dist/index.d.cts",
18
+ "default": "./dist/index.cjs"
19
+ }
20
+ },
21
+ "./react": {
22
+ "import": {
23
+ "types": "./dist/react/index.d.ts",
24
+ "default": "./dist/react/index.js"
25
+ },
26
+ "require": {
27
+ "types": "./dist/react/index.d.cts",
28
+ "default": "./dist/react/index.cjs"
29
+ }
30
+ },
31
+ "./vue": {
32
+ "import": {
33
+ "types": "./dist/vue/index.d.ts",
34
+ "default": "./dist/vue/index.js"
35
+ },
36
+ "require": {
37
+ "types": "./dist/vue/index.d.cts",
38
+ "default": "./dist/vue/index.cjs"
39
+ }
40
+ },
41
+ "./svelte": {
42
+ "import": {
43
+ "types": "./dist/svelte/index.d.ts",
44
+ "default": "./dist/svelte/index.js"
45
+ }
46
+ }
47
+ },
48
+ "files": [
49
+ "dist"
50
+ ],
51
+ "sideEffects": [
52
+ "src/index.ts",
53
+ "dist/index.js",
54
+ "dist/index.cjs"
55
+ ],
56
+ "scripts": {
57
+ "build": "tsup",
58
+ "dev": "tsup --watch",
59
+ "typecheck": "tsc --noEmit",
60
+ "test": "vitest run",
61
+ "test:watch": "vitest",
62
+ "test:e2e": "playwright test"
63
+ },
64
+ "devDependencies": {
65
+ "@playwright/test": "^1.58.2",
66
+ "@types/react": "^18.0.0",
67
+ "jsdom": "^24.0.0",
68
+ "tsup": "^8.0.0",
69
+ "typescript": "^5.4.0",
70
+ "vitest": "^1.6.0"
71
+ },
72
+ "peerDependencies": {
73
+ "react": ">=17.0.0",
74
+ "svelte": ">=4.0.0",
75
+ "vue": ">=3.3.0"
76
+ },
77
+ "peerDependenciesMeta": {
78
+ "react": {
79
+ "optional": true
80
+ },
81
+ "vue": {
82
+ "optional": true
83
+ },
84
+ "svelte": {
85
+ "optional": true
86
+ }
87
+ },
88
+ "keywords": [
89
+ "loading",
90
+ "games",
91
+ "spinner",
92
+ "web-component",
93
+ "react",
94
+ "vue",
95
+ "svelte",
96
+ "snake",
97
+ "flappy",
98
+ "mini-games",
99
+ "ux"
100
+ ],
101
+ "repository": {
102
+ "type": "git",
103
+ "url": "https://github.com/Danishkhan1811/LoadingGame"
104
+ }
105
+ }