@popover-kit/svelte 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.
@@ -0,0 +1,313 @@
1
+ import { Snippet } from 'svelte';
2
+
3
+ // =============================================================================
4
+ // Popover Micro-Plugin — Type Definitions (single source of truth)
5
+ // =============================================================================
6
+ // ─── Placement ────────────────────────────────────────────────────────────────
7
+ /**
8
+ * Where the panel appears relative to the trigger.
9
+ *
10
+ * Sides: top | bottom | left | right
11
+ * Alignment: -start | -end (no suffix = center)
12
+ * Special: autoVertical — prefers bottom, flips to top when viewport clips it
13
+ */
14
+ export type Side = "top" | "bottom" | "left" | "right";
15
+ export type Alignment = "start" | "end";
16
+ export type Placement = "top" | "top-start" | "top-end" | "bottom" | "bottom-start" | "bottom-end" | "left" | "left-start" | "left-end" | "right" | "right-start" | "right-end" | "autoVertical";
17
+ // ─── Computed position ────────────────────────────────────────────────────────
18
+ /**
19
+ * Output of the positioning engine.
20
+ * x / y are viewport-relative px values suitable for `position: fixed`.
21
+ */
22
+ export interface ResolvedPosition {
23
+ x: number;
24
+ y: number;
25
+ placement: Placement; // actual placement used (may differ from requested after flip)
26
+ }
27
+ // ─── State ────────────────────────────────────────────────────────────────────
28
+ /** Fine-grained lifecycle state — drives CSS enter/exit transitions. */
29
+ export type PopoverState = "closed" | "opening" | "open" | "closing";
30
+ // ─── Controller interface ─────────────────────────────────────────────────────
31
+ /**
32
+ * Public surface of PopoverController.
33
+ * All readonly getters are $state runes — Svelte templates track them.
34
+ */
35
+ export interface IPopoverController {
36
+ // Reactive getters
37
+ readonly isOpen: boolean;
38
+ readonly state: PopoverState;
39
+ readonly resolvedPlacement: Placement;
40
+ readonly position: ResolvedPosition | null;
41
+ /** True while the panel should stay mounted (open OR exit-animating). */
42
+ readonly isVisible: boolean;
43
+ readonly triggerId: string;
44
+ readonly contentId: string;
45
+ // Core actions
46
+ open(): void;
47
+ close(): void;
48
+ toggle(): void;
49
+ setPlacement(p: Placement): void;
50
+ // Element wiring (called by Popover.svelte $effects and headless consumers)
51
+ attachTrigger(el: HTMLElement): void;
52
+ attachPanel(el: HTMLElement): void;
53
+ detachTrigger(): void;
54
+ detachPanel(): void;
55
+ getSnapshot(): PopoverSnapshot;
56
+ subscribe(cb: SubscriberFn): UnsubscribeFn;
57
+ destroy(): void;
58
+ }
59
+ export interface PopoverSnapshot {
60
+ readonly isOpen: boolean;
61
+ readonly state: PopoverState;
62
+ readonly resolvedPlacement: Placement;
63
+ readonly triggerId: string;
64
+ readonly contentId: string;
65
+ }
66
+ export type SubscriberFn = (snap: PopoverSnapshot) => void;
67
+ export type UnsubscribeFn = () => void;
68
+ // ─── Controller options ───────────────────────────────────────────────────────
69
+ export interface ControllerOptions {
70
+ /** ID prefix for generated ARIA ids. Auto-generated if omitted. */
71
+ id?: string;
72
+ /** Preferred placement. Default: `bottom`. */
73
+ placement?: Placement;
74
+ /** Gap between trigger and panel in px. Default: 8. */
75
+ offset?: number;
76
+ trapFocus?: boolean;
77
+ closeOnOutsideClick?: boolean;
78
+ closeOnEsc?: boolean;
79
+ onOpen?: () => void;
80
+ onClose?: () => void;
81
+ onStateChange?: (state: PopoverState) => void;
82
+ }
83
+ // ─── Component props ──────────────────────────────────────────────────────────
84
+ export type PopoverAnimation = "scale" | "slide" | "fade" | "none";
85
+ export type TriggerInteraction = "click" | "hover" | "focus" | "manual";
86
+ export interface PopoverProps extends ControllerOptions {
87
+ animation?: PopoverAnimation;
88
+ showArrow?: boolean;
89
+ class?: string;
90
+ "aria-label"?: string;
91
+ triggerInteraction?: TriggerInteraction;
92
+ /** Delay (ms) before opening — useful for hover mode. */
93
+ openDelay?: number;
94
+ /** Delay (ms) before closing — useful for hover mode. */
95
+ closeDelay?: number;
96
+ /** External boolean for fully-controlled mode. */
97
+ open?: boolean;
98
+ /** Inject an existing controller; this component will not destroy it. */
99
+ controller?: IPopoverController;
100
+ }
101
+ // ─── Snippet contexts (Bits UI style — fully typed parameters) ────────────────
102
+ /** Passed into `{#snippet trigger(ctx)}`. Spread these onto your trigger element. */
103
+ export interface TriggerSnippetCtx {
104
+ id: string;
105
+ controls: string;
106
+ expanded: boolean;
107
+ onclick: () => void;
108
+ onkeydown: (e: KeyboardEvent) => void;
109
+ onmouseenter: () => void;
110
+ onmouseleave: () => void;
111
+ onfocus: () => void;
112
+ onblur: () => void;
113
+ ctrl: IPopoverController;
114
+ }
115
+ /** Passed into `{#snippet content(ctx)}`. */
116
+ export interface ContentSnippetCtx {
117
+ ctrl: IPopoverController;
118
+ resolvedPlacement: Placement;
119
+ state: PopoverState;
120
+ }
121
+ // ─── Accessibility handles ────────────────────────────────────────────────────
122
+ export interface FocusTrapHandle {
123
+ activate(): void;
124
+ deactivate(): void;
125
+ destroy(): void;
126
+ }
127
+ // ─── Plugin factory ───────────────────────────────────────────────────────────
128
+ export interface ThemeTokens {
129
+ borderRadius: string;
130
+ backdropBlur: string;
131
+ shadowColor: string;
132
+ lightBg: string;
133
+ darkBg: string;
134
+ lightBorder: string;
135
+ darkBorder: string;
136
+ lightText: string;
137
+ darkText: string;
138
+ accentColor: string;
139
+ }
140
+ export declare class PopoverController implements IPopoverController {
141
+ #private;
142
+ readonly triggerId: string;
143
+ readonly contentId: string;
144
+ constructor(opts?: ControllerOptions);
145
+ get isOpen(): boolean;
146
+ get state(): PopoverState;
147
+ get resolvedPlacement(): Placement;
148
+ get position(): ResolvedPosition | null;
149
+ get isVisible(): boolean;
150
+ getSnapshot(): PopoverSnapshot;
151
+ subscribe(cb: SubscriberFn): UnsubscribeFn;
152
+ attachTrigger(el: HTMLElement): void;
153
+ /**
154
+ * Called by Popover.svelte's $effect after the panel element mounts.
155
+ * If state is 'opening', we're in the open sequence → finish it now.
156
+ * If state is already 'open', controller was re-attached → just recompute.
157
+ */
158
+ attachPanel(el: HTMLElement): void;
159
+ detachTrigger(): void;
160
+ detachPanel(): void;
161
+ open(): void;
162
+ close(): void;
163
+ toggle(): void;
164
+ setPlacement(p: Placement): void;
165
+ destroy(): void;
166
+ }
167
+ export interface Props extends PopoverProps {
168
+ trigger?: Snippet<[
169
+ TriggerSnippetCtx
170
+ ]>;
171
+ content?: Snippet<[
172
+ ContentSnippetCtx
173
+ ]>;
174
+ children?: Snippet<[
175
+ IPopoverController
176
+ ]>;
177
+ }
178
+ export declare const Popover: import("svelte").Component<Props, {
179
+ open: () => void;
180
+ close: () => void;
181
+ toggle: () => void;
182
+ getController: () => PopoverController;
183
+ }, "">;
184
+ export type Popover = ReturnType<typeof Popover>;
185
+ interface Props$1 {
186
+ ctx: TriggerSnippetCtx;
187
+ disabled?: boolean;
188
+ class?: string;
189
+ children?: Snippet;
190
+ }
191
+ export declare const PopoverTrigger: import("svelte").Component<Props$1, {}, "">;
192
+ export type PopoverTrigger = ReturnType<typeof PopoverTrigger>;
193
+ interface Props$2 {
194
+ ctx: ContentSnippetCtx;
195
+ title?: string;
196
+ description?: string;
197
+ showClose?: boolean;
198
+ class?: string;
199
+ /** Primary body slot — receives ctx for controller access. */
200
+ body?: Snippet<[
201
+ ContentSnippetCtx
202
+ ]>;
203
+ /** Fallback when no body snippet is provided. */
204
+ children?: Snippet<[
205
+ ContentSnippetCtx
206
+ ]>;
207
+ }
208
+ export declare const PopoverContent: import("svelte").Component<Props$2, {}, "">;
209
+ export type PopoverContent = ReturnType<typeof PopoverContent>;
210
+ interface Props$3 {
211
+ id: string;
212
+ triggerId: string;
213
+ isOpen: boolean;
214
+ position: ResolvedPosition | null;
215
+ resolvedPlacement: Placement;
216
+ showArrow: boolean;
217
+ animation: PopoverAnimation;
218
+ role?: "dialog" | "listbox" | "tooltip" | "menu" | "tree" | "grid";
219
+ class?: string;
220
+ "aria-label"?: string;
221
+ /** $bindable — Popover.svelte reads this for the controller's attachPanel(). */
222
+ el?: HTMLElement | null;
223
+ children?: Snippet;
224
+ }
225
+ export declare const PopoverPanel: import("svelte").Component<Props$3, {}, "el">;
226
+ export type PopoverPanel = ReturnType<typeof PopoverPanel>;
227
+ interface Props$4 {
228
+ placement: Placement;
229
+ class?: string;
230
+ }
231
+ export declare const PopoverArrow: import("svelte").Component<Props$4, {}, "">;
232
+ export type PopoverArrow = ReturnType<typeof PopoverArrow>;
233
+ /** Split "bottom-start" → { side: 'bottom', alignment: 'start' } */
234
+ export declare function parsePlacement(p: Placement): {
235
+ side: Side;
236
+ alignment: Alignment | null;
237
+ };
238
+ /** Reads the resolved (computed) writing direction of an element. */
239
+ export declare function getDirection(el: HTMLElement): "ltr" | "rtl";
240
+ /**
241
+ * Compute the `position:fixed` coordinates for the popover panel.
242
+ *
243
+ * The panel element MUST already be in the DOM (even if visibility:hidden)
244
+ * so its dimensions can be read via getBoundingClientRect().
245
+ *
246
+ * @param triggerEl - The trigger / reference element.
247
+ * @param panelEl - The panel element to position.
248
+ * @param placement - Requested placement (may be overridden by auto-flip).
249
+ * @param offset - Gap between trigger edge and panel in px.
250
+ * @returns { x, y, placement } — coordinates and the actual placement used.
251
+ */
252
+ export declare function computePosition(triggerEl: HTMLElement, panelEl: HTMLElement, placement: Placement, offset: number): ResolvedPosition;
253
+ /**
254
+ * Keeps keyboard focus inside `container` while active.
255
+ * Activation is deferred so it can be timed to the enter animation.
256
+ *
257
+ * @param container - The panel element to trap focus within.
258
+ * @param returnTo - Element that receives focus when the trap deactivates.
259
+ */
260
+ export declare function createFocusTrap(container: HTMLElement, returnTo?: HTMLElement): FocusTrapHandle;
261
+ /** Announce a message to screen readers via the shared polite live region. */
262
+ export declare function announce(message: string, delayMs?: number): void;
263
+ /**
264
+ * Calls `callback` when a pointer-down event occurs outside all `elements`.
265
+ * Uses capture phase so stopPropagation inside the panel cannot suppress it.
266
+ * Returns a cleanup function.
267
+ */
268
+ export declare function onClickOutside(elements: HTMLElement[], callback: (e: PointerEvent) => void): () => void;
269
+ export declare const DEFAULT_OPTIONS: {
270
+ readonly placement: "bottom";
271
+ readonly offset: 8;
272
+ readonly trapFocus: true;
273
+ readonly closeOnOutsideClick: true;
274
+ readonly closeOnEsc: true;
275
+ };
276
+ /**
277
+ * Must match the longest `transition-duration` in PopoverPanel's CSS.
278
+ * The controller waits this long after close() before setting state → 'closed'
279
+ * so the exit animation completes before the panel is unmounted.
280
+ */
281
+ export declare const ANIMATION_DURATION_MS = 200;
282
+ export declare const DEFAULT_THEME_TOKENS: ThemeTokens;
283
+ /**
284
+ * Generates a stable, unique DOM id.
285
+ *
286
+ * Deliberately a pure monotonic counter — NOT `Math.random()`. With SSR
287
+ * enabled, ids are generated once on the server and again on the client
288
+ * during hydration; a random suffix would differ between the two passes
289
+ * and mismatch the DOM, breaking `aria-controls`/`aria-labelledby` wiring
290
+ * the moment hydration reconciles attributes. A counter produces the same
291
+ * sequence both times as long as components mount in the same order, which
292
+ * holds for a given render tree.
293
+ */
294
+ export declare function uid(prefix?: string): string;
295
+ export interface PluginConfig {
296
+ defaults?: Partial<ControllerOptions>;
297
+ theme?: Partial<ThemeTokens>;
298
+ }
299
+ /**
300
+ * createPopoverPlugin — returns a config bundle with merged theme tokens.
301
+ * Use to create module-specific variants with different defaults.
302
+ *
303
+ * @example
304
+ * const SearchPopover = createPopoverPlugin({
305
+ * defaults: { placement: 'bottom-start', trapFocus: false },
306
+ * });
307
+ */
308
+ export declare function createPopoverPlugin(cfg?: PluginConfig): {
309
+ defaults: Partial<ControllerOptions>;
310
+ tokens: ThemeTokens;
311
+ };
312
+
313
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{default as q}from"./components/Popover.svelte";import{default as V}from"./components/PopoverTrigger.svelte";import{default as W}from"./components/PopoverContent.svelte";import{default as j}from"./elements/PopoverPanel.svelte";import{default as z}from"./elements/PopoverArrow.svelte";import{PopoverController as Q}from"./controller.svelte";function x(t){if(t==="autoVertical")return{side:"bottom",alignment:null};let[o,e=null]=t.split("-");return{side:o,alignment:e}}function w(t,o,e,n,i,s){let a,c;switch(e){case"bottom":c=t.bottom+i;break;case"top":c=t.top-o.height-i;break;case"left":a=t.left-o.width-i;break;case"right":a=t.right+i;break}if(e==="top"||e==="bottom"){let r=s?t.right-o.width:t.left,d=s?t.left:t.right-o.width;n==="start"?a=r:n==="end"?a=d:a=t.left+(t.width-o.width)/2}else n==="start"?c=t.top:n==="end"?c=t.bottom-o.height:c=t.top+(t.height-o.height)/2;return{x:a,y:c}}function T(t){return getComputedStyle(t).direction==="rtl"?"rtl":"ltr"}function v(t,o,e,n=6){return Math.max(n,Math.min(e-o-n,t))}function O(t,o,e,n,i){let s=window.innerWidth,a=window.innerHeight;switch(t){case"bottom":return o.bottom+i+e<=a?"bottom":"top";case"top":return o.top-i-e>=0?"top":"bottom";case"right":return o.right+i+n<=s?"right":"left";case"left":return o.left-i-n>=0?"left":"right"}}function _(t,o,e,n){let i=t.getBoundingClientRect(),s=o.getBoundingClientRect(),a=window.innerWidth,c=window.innerHeight,r=T(t)==="rtl",{side:d,alignment:p}=x(e),u=O(d,i,s.height,s.width,n),f=e==="autoVertical"?u==="top"?"top":"bottom":u===d?e:p?`${u}-${p}`:u,{x:m,y:h}=w(i,s,u,p,n,r);return u==="top"||u==="bottom"?m=v(m,s.width,a):h=v(h,s.height,c),{x:m,y:h,placement:f}}var L={placement:"bottom",offset:8,trapFocus:!0,closeOnOutsideClick:!0,closeOnEsc:!0};var k=200,A=["a[href]","button:not([disabled])","input:not([disabled])","select:not([disabled])","textarea:not([disabled])",'[tabindex]:not([tabindex="-1"])','[contenteditable="true"]',"details > summary"].join(", "),E="pop-live-region",b={borderRadius:"0.75rem",backdropBlur:"12px",shadowColor:"rgba(0,0,0,0.15)",lightBg:"oklch(var(--color-base-100))",darkBg:"oklch(var(--color-base-200))",lightBorder:"oklch(var(--color-base-300))",darkBorder:"oklch(var(--color-base-300))",lightText:"oklch(var(--color-base-content))",darkText:"oklch(var(--color-base-content))",accentColor:"oklch(var(--color-primary))"},D=0;function S(t="pop"){return`${t}-${++D}`}function I(t,o){let e=!1,n=document.activeElement;function i(){return Array.from(t.querySelectorAll(A)).filter(r=>!r.hasAttribute("disabled")&&r.offsetParent!==null)}function s(r){if(!e||r.key!=="Tab")return;let d=i();if(!d.length){r.preventDefault();return}let p=d[0],u=d[d.length-1],f=document.activeElement;r.shiftKey?(f===p||!t.contains(f))&&(r.preventDefault(),u.focus()):(f===u||!t.contains(f))&&(r.preventDefault(),p.focus())}function a(){if(e)return;e=!0,n=document.activeElement,document.addEventListener("keydown",s,!0);let r=i()[0]??t;requestAnimationFrame(()=>r.focus())}function c(){if(!e)return;e=!1,document.removeEventListener("keydown",s,!0);let r=o??n;r&&typeof r.focus=="function"&&requestAnimationFrame(()=>r.focus())}return{activate:a,deactivate:c,destroy:c}}var l=null;function P(){return l||(l=document.getElementById(E),l||(l=document.createElement("div"),l.id=E,l.setAttribute("aria-live","polite"),l.setAttribute("aria-atomic","true"),Object.assign(l.style,{position:"absolute",width:"1px",height:"1px",overflow:"hidden",clip:"rect(0 0 0 0)",whiteSpace:"nowrap",border:"0",padding:"0",margin:"-1px"}),document.body.appendChild(l)),l)}function F(t,o=80){let e=P();e.textContent="",setTimeout(()=>{e.textContent=t},o)}function y(t,o){function e(n){let i=n.target;t.some(s=>s.contains(i))||o(n)}return document.addEventListener("pointerdown",e,!0),()=>document.removeEventListener("pointerdown",e,!0)}function M(t={}){return{defaults:t.defaults??{},tokens:{...b,...t.theme}}}export{k as ANIMATION_DURATION_MS,L as DEFAULT_OPTIONS,b as DEFAULT_THEME_TOKENS,q as Popover,z as PopoverArrow,W as PopoverContent,Q as PopoverController,j as PopoverPanel,V as PopoverTrigger,F as announce,_ as computePosition,I as createFocusTrap,M as createPopoverPlugin,T as getDirection,y as onClickOutside,x as parsePlacement,S as uid};
@@ -0,0 +1 @@
1
+ import{FOCUSABLE_SELECTOR as v,LIVE_REGION_ID as m}from"../constants/config.js";function y(o,r){let t=!1,i=document.activeElement;function u(){return Array.from(o.querySelectorAll(v)).filter(e=>!e.hasAttribute("disabled")&&e.offsetParent!==null)}function c(e){if(!t||e.key!=="Tab")return;const s=u();if(!s.length){e.preventDefault();return}const d=s[0],l=s[s.length-1],a=document.activeElement;e.shiftKey?(a===d||!o.contains(a))&&(e.preventDefault(),l.focus()):(a===l||!o.contains(a))&&(e.preventDefault(),d.focus())}function p(){if(t)return;t=!0,i=document.activeElement,document.addEventListener("keydown",c,!0);const e=u()[0]??o;requestAnimationFrame(()=>e.focus())}function f(){if(!t)return;t=!1,document.removeEventListener("keydown",c,!0);const e=r??i;e&&typeof e.focus=="function"&&requestAnimationFrame(()=>e.focus())}return{activate:p,deactivate:f,destroy:f}}let n=null;function g(){return n||(n=document.getElementById(m),n||(n=document.createElement("div"),n.id=m,n.setAttribute("aria-live","polite"),n.setAttribute("aria-atomic","true"),Object.assign(n.style,{position:"absolute",width:"1px",height:"1px",overflow:"hidden",clip:"rect(0 0 0 0)",whiteSpace:"nowrap",border:"0",padding:"0",margin:"-1px"}),document.body.appendChild(n)),n)}function h(o,r=80){const t=g();t.textContent="",setTimeout(()=>{t.textContent=o},r)}function b(o,r){function t(i){const u=i.target;o.some(c=>c.contains(u))||r(i)}return document.addEventListener("pointerdown",t,!0),()=>document.removeEventListener("pointerdown",t,!0)}export{h as announce,y as createFocusTrap,b as onClickOutside};
@@ -0,0 +1 @@
1
+ function p(t){if(t==="autoVertical")return{side:"bottom",alignment:null};const[o,n=null]=t.split("-");return{side:o,alignment:n}}const S={top:"bottom",bottom:"top",left:"right",right:"left"};function f(t,o,n,i,e,h){let l,s;switch(n){case"bottom":s=t.bottom+e;break;case"top":s=t.top-o.height-e;break;case"left":l=t.left-o.width-e;break;case"right":l=t.right+e;break}if(n==="top"||n==="bottom"){const d=h?t.right-o.width:t.left,r=h?t.left:t.right-o.width;i==="start"?l=d:i==="end"?l=r:l=t.left+(t.width-o.width)/2}else i==="start"?s=t.top:i==="end"?s=t.bottom-o.height:s=t.top+(t.height-o.height)/2;return{x:l,y:s}}function v(t){return getComputedStyle(t).direction==="rtl"?"rtl":"ltr"}function w(t,o,n,i=6){return Math.max(i,Math.min(n-o-i,t))}function x(t,o,n,i,e){const h=window.innerWidth,l=window.innerHeight;switch(t){case"bottom":return o.bottom+e+n<=l?"bottom":"top";case"top":return o.top-e-n>=0?"top":"bottom";case"right":return o.right+e+i<=h?"right":"left";case"left":return o.left-e-i>=0?"left":"right"}}function P(t,o,n,i){const e=t.getBoundingClientRect(),h=o.getBoundingClientRect(),l=window.innerWidth,s=window.innerHeight,d=v(t)==="rtl";let{side:r,alignment:c}=p(n);const u=x(r,e,h.height,h.width,i),b=n==="autoVertical"?u==="top"?"top":"bottom":u===r?n:c?`${u}-${c}`:u;let{x:m,y:a}=f(e,h,u,c,i,d);return u==="top"||u==="bottom"?m=w(m,h.width,l):a=w(a,h.height,s),{x:m,y:a,placement:b}}export{P as computePosition,v as getDirection,p as parsePlacement};
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@popover-kit/svelte",
3
+ "version": "0.1.0",
4
+ "description": "",
5
+ "keywords": [
6
+ "svelte",
7
+ "svelte5",
8
+ "popover",
9
+ "modal",
10
+ "drawer",
11
+ "daisyui",
12
+ "tailwindcss",
13
+ "rtl",
14
+ "a11y"
15
+ ],
16
+ "license": "MIT",
17
+ "type": "module",
18
+ "sideEffects": false,
19
+ "svelte": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "svelte": "./dist/index.js",
25
+ "default": "./dist/index.js"
26
+ },
27
+ "./package.json": "./package.json"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "README.md",
32
+ "LICENSE"
33
+ ],
34
+ "peerDependencies": {
35
+ "svelte": "^5.0.0"
36
+ },
37
+ "dependencies": {
38
+ "lucide-svelte": "^0.462.0"
39
+ },
40
+ "devDependencies": {
41
+ "@sveltejs/package": "^2.3.0",
42
+ "@sveltejs/vite-plugin-svelte": "^4.0.0",
43
+ "@testing-library/svelte": "^5.2.0",
44
+ "@testing-library/jest-dom": "^6.6.0",
45
+ "@testing-library/user-event": "^14.5.0",
46
+ "@types/node": "^22.0.0",
47
+ "dts-bundle-generator": "^9.5.1",
48
+ "esbuild": "^0.24.0",
49
+ "jsdom": "^25.0.0",
50
+ "svelte": "^5.0.0",
51
+ "svelte-check": "^4.0.0",
52
+ "typescript": "^5.6.0",
53
+ "vitest": "^2.1.0"
54
+ },
55
+ "engines": {
56
+ "node": ">=18"
57
+ },
58
+ "publishConfig": {
59
+ "access": "public"
60
+ },
61
+ "scripts": {
62
+ "build": "node scripts/build.mjs",
63
+ "typecheck": "svelte-check --tsconfig ./tsconfig.json",
64
+ "typecheck:test": "tsc -p tsconfig.test.json",
65
+ "test": "vitest run",
66
+ "test:watch": "vitest",
67
+ "prepublishOnlyx": "pnpm run typecheck && pnpm run test && pnpm run build"
68
+ }
69
+ }