@quikturn/logos-react 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.
package/dist/index.mjs ADDED
@@ -0,0 +1,665 @@
1
+ import React, { createContext, useRef, useState, useMemo, useEffect, useCallback, useContext } from 'react';
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { logoUrl, BASE_URL } from '@quikturn/logos';
4
+
5
+ // src/context.tsx
6
+ var QuikturnContext = createContext(null);
7
+ function useQuikturnContext() {
8
+ return useContext(QuikturnContext);
9
+ }
10
+ function QuikturnProvider({
11
+ token,
12
+ baseUrl,
13
+ children
14
+ }) {
15
+ return /* @__PURE__ */ jsx(QuikturnContext.Provider, { value: { token, baseUrl }, children });
16
+ }
17
+ function useLogoUrl(domain, options) {
18
+ const ctx = useQuikturnContext();
19
+ const token = options?.token ?? ctx?.token;
20
+ const baseUrl = options?.baseUrl ?? ctx?.baseUrl;
21
+ return useMemo(
22
+ () => logoUrl(domain, {
23
+ token,
24
+ baseUrl,
25
+ size: options?.size,
26
+ format: options?.format,
27
+ greyscale: options?.greyscale,
28
+ theme: options?.theme
29
+ }),
30
+ [domain, token, baseUrl, options?.size, options?.format, options?.greyscale, options?.theme]
31
+ );
32
+ }
33
+ var fired = /* @__PURE__ */ new Set();
34
+ function fireBeacon(token) {
35
+ if (typeof window === "undefined") return;
36
+ if (!token || token.startsWith("sk_")) return;
37
+ if (fired.has(token)) return;
38
+ fired.add(token);
39
+ const img = new Image();
40
+ img.src = `${BASE_URL}/_beacon?token=${token}&page=${encodeURIComponent(location.href)}`;
41
+ }
42
+ function QuikturnLogo({
43
+ domain,
44
+ token,
45
+ baseUrl,
46
+ size,
47
+ format,
48
+ greyscale,
49
+ theme,
50
+ alt,
51
+ href,
52
+ className,
53
+ style,
54
+ loading = "lazy",
55
+ onError,
56
+ onLoad
57
+ }) {
58
+ const ctx = useQuikturnContext();
59
+ const effectiveToken = token ?? ctx?.token ?? "";
60
+ const effectiveBaseUrl = baseUrl ?? ctx?.baseUrl;
61
+ const src = useMemo(
62
+ () => logoUrl(domain, {
63
+ token: effectiveToken || void 0,
64
+ size,
65
+ format,
66
+ greyscale,
67
+ theme,
68
+ baseUrl: effectiveBaseUrl
69
+ }),
70
+ [domain, effectiveToken, effectiveBaseUrl, size, format, greyscale, theme]
71
+ );
72
+ useEffect(() => {
73
+ if (effectiveToken) fireBeacon(effectiveToken);
74
+ }, [effectiveToken]);
75
+ const imgEl = /* @__PURE__ */ jsx(
76
+ "img",
77
+ {
78
+ src,
79
+ alt: alt ?? `${domain} logo`,
80
+ loading,
81
+ onError,
82
+ onLoad
83
+ }
84
+ );
85
+ if (href) {
86
+ return /* @__PURE__ */ jsx(
87
+ "a",
88
+ {
89
+ href,
90
+ target: "_blank",
91
+ rel: "noopener noreferrer",
92
+ className,
93
+ style,
94
+ children: imgEl
95
+ }
96
+ );
97
+ }
98
+ if (className || style) {
99
+ return /* @__PURE__ */ jsx("span", { className, style, children: imgEl });
100
+ }
101
+ return imgEl;
102
+ }
103
+ var ANIMATION_CONFIG = {
104
+ SMOOTH_TAU: 0.25,
105
+ MIN_COPIES: 2,
106
+ COPY_HEADROOM: 2
107
+ };
108
+ function useResizeObserver(callback, refs, deps) {
109
+ useEffect(() => {
110
+ if (typeof window === "undefined") return;
111
+ if (!window.ResizeObserver) {
112
+ const handle = () => callback();
113
+ window.addEventListener("resize", handle);
114
+ callback();
115
+ return () => window.removeEventListener("resize", handle);
116
+ }
117
+ const observers = refs.map((ref) => {
118
+ if (!ref.current) return null;
119
+ const observer = new ResizeObserver(callback);
120
+ observer.observe(ref.current);
121
+ return observer;
122
+ });
123
+ callback();
124
+ return () => {
125
+ observers.forEach((o) => o?.disconnect());
126
+ };
127
+ }, deps);
128
+ }
129
+ function useImageLoader(listRef, onLoad, deps) {
130
+ useEffect(() => {
131
+ const images = listRef.current?.querySelectorAll("img") ?? [];
132
+ if (images.length === 0) {
133
+ onLoad();
134
+ return;
135
+ }
136
+ let remaining = images.length;
137
+ const done = () => {
138
+ remaining -= 1;
139
+ if (remaining === 0) onLoad();
140
+ };
141
+ images.forEach((img) => {
142
+ const el = img;
143
+ if (el.complete) {
144
+ done();
145
+ } else {
146
+ el.addEventListener("load", done, { once: true });
147
+ el.addEventListener("error", done, { once: true });
148
+ }
149
+ });
150
+ return () => {
151
+ images.forEach((img) => {
152
+ img.removeEventListener("load", done);
153
+ img.removeEventListener("error", done);
154
+ });
155
+ };
156
+ }, deps);
157
+ }
158
+ function useAnimationLoop(trackRef, targetVelocity, seqWidth, seqHeight, isHovered, hoverSpeed, isVertical) {
159
+ const rafRef = useRef(null);
160
+ const lastTsRef = useRef(null);
161
+ const offsetRef = useRef(0);
162
+ const velocityRef = useRef(0);
163
+ useEffect(() => {
164
+ const track = trackRef.current;
165
+ if (!track) return;
166
+ const prefersReduced = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
167
+ const seqSize = isVertical ? seqHeight : seqWidth;
168
+ if (seqSize > 0) {
169
+ offsetRef.current = (offsetRef.current % seqSize + seqSize) % seqSize;
170
+ track.style.transform = isVertical ? `translate3d(0, ${-offsetRef.current}px, 0)` : `translate3d(${-offsetRef.current}px, 0, 0)`;
171
+ }
172
+ if (prefersReduced) {
173
+ track.style.transform = "translate3d(0, 0, 0)";
174
+ return () => {
175
+ lastTsRef.current = null;
176
+ };
177
+ }
178
+ const animate = (ts) => {
179
+ if (lastTsRef.current === null) lastTsRef.current = ts;
180
+ const dt = Math.max(0, ts - lastTsRef.current) / 1e3;
181
+ lastTsRef.current = ts;
182
+ const target = isHovered && hoverSpeed !== void 0 ? hoverSpeed : targetVelocity;
183
+ const ease = 1 - Math.exp(-dt / ANIMATION_CONFIG.SMOOTH_TAU);
184
+ velocityRef.current += (target - velocityRef.current) * ease;
185
+ if (seqSize > 0) {
186
+ let next = offsetRef.current + velocityRef.current * dt;
187
+ next = (next % seqSize + seqSize) % seqSize;
188
+ offsetRef.current = next;
189
+ track.style.transform = isVertical ? `translate3d(0, ${-offsetRef.current}px, 0)` : `translate3d(${-offsetRef.current}px, 0, 0)`;
190
+ }
191
+ rafRef.current = requestAnimationFrame(animate);
192
+ };
193
+ rafRef.current = requestAnimationFrame(animate);
194
+ return () => {
195
+ if (rafRef.current !== null) {
196
+ cancelAnimationFrame(rafRef.current);
197
+ rafRef.current = null;
198
+ }
199
+ lastTsRef.current = null;
200
+ };
201
+ }, [targetVelocity, seqWidth, seqHeight, isHovered, hoverSpeed, isVertical]);
202
+ }
203
+ function toCssLength(value) {
204
+ return typeof value === "number" ? `${value}px` : value ?? void 0;
205
+ }
206
+ function FadeOverlays({
207
+ isVertical,
208
+ fadeColor = "#ffffff"
209
+ }) {
210
+ const base = {
211
+ position: "absolute",
212
+ pointerEvents: "none",
213
+ zIndex: 1
214
+ };
215
+ if (isVertical) {
216
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
217
+ /* @__PURE__ */ jsx(
218
+ "div",
219
+ {
220
+ "aria-hidden": "true",
221
+ "data-testid": "fade-overlay",
222
+ style: {
223
+ ...base,
224
+ top: 0,
225
+ left: 0,
226
+ right: 0,
227
+ height: "clamp(24px, 8%, 120px)",
228
+ background: `linear-gradient(to bottom, ${fadeColor} 0%, transparent 100%)`
229
+ }
230
+ }
231
+ ),
232
+ /* @__PURE__ */ jsx(
233
+ "div",
234
+ {
235
+ "aria-hidden": "true",
236
+ "data-testid": "fade-overlay",
237
+ style: {
238
+ ...base,
239
+ bottom: 0,
240
+ left: 0,
241
+ right: 0,
242
+ height: "clamp(24px, 8%, 120px)",
243
+ background: `linear-gradient(to top, ${fadeColor} 0%, transparent 100%)`
244
+ }
245
+ }
246
+ )
247
+ ] });
248
+ }
249
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
250
+ /* @__PURE__ */ jsx(
251
+ "div",
252
+ {
253
+ "aria-hidden": "true",
254
+ "data-testid": "fade-overlay",
255
+ style: {
256
+ ...base,
257
+ top: 0,
258
+ bottom: 0,
259
+ left: 0,
260
+ width: "clamp(24px, 8%, 120px)",
261
+ background: `linear-gradient(to right, ${fadeColor} 0%, transparent 100%)`
262
+ }
263
+ }
264
+ ),
265
+ /* @__PURE__ */ jsx(
266
+ "div",
267
+ {
268
+ "aria-hidden": "true",
269
+ "data-testid": "fade-overlay",
270
+ style: {
271
+ ...base,
272
+ top: 0,
273
+ bottom: 0,
274
+ right: 0,
275
+ width: "clamp(24px, 8%, 120px)",
276
+ background: `linear-gradient(to left, ${fadeColor} 0%, transparent 100%)`
277
+ }
278
+ }
279
+ )
280
+ ] });
281
+ }
282
+ function DefaultLogoItem({
283
+ logo,
284
+ logoHeight,
285
+ scaleOnHover
286
+ }) {
287
+ const [hovered, setHovered] = useState(false);
288
+ const imgStyle = {
289
+ height: `${logoHeight}px`,
290
+ width: "auto",
291
+ display: "block",
292
+ objectFit: "contain",
293
+ userSelect: "none",
294
+ pointerEvents: "none",
295
+ transform: scaleOnHover && hovered ? "scale(1.2)" : void 0,
296
+ transition: scaleOnHover ? "transform 300ms cubic-bezier(0.4, 0, 0.2, 1)" : void 0
297
+ };
298
+ const handlers = scaleOnHover ? {
299
+ onMouseEnter: () => setHovered(true),
300
+ onMouseLeave: () => setHovered(false)
301
+ } : {};
302
+ const img = /* @__PURE__ */ jsx(
303
+ "img",
304
+ {
305
+ src: logo.url,
306
+ alt: logo.alt,
307
+ loading: "lazy",
308
+ decoding: "async",
309
+ draggable: false,
310
+ style: imgStyle
311
+ }
312
+ );
313
+ if (logo.href) {
314
+ return /* @__PURE__ */ jsx(
315
+ "a",
316
+ {
317
+ href: logo.href,
318
+ target: "_blank",
319
+ rel: "noopener noreferrer",
320
+ "aria-label": logo.alt,
321
+ style: {
322
+ display: "inline-flex",
323
+ alignItems: "center",
324
+ textDecoration: "none"
325
+ },
326
+ ...handlers,
327
+ children: img
328
+ }
329
+ );
330
+ }
331
+ return /* @__PURE__ */ jsx(
332
+ "span",
333
+ {
334
+ style: { display: "inline-flex", alignItems: "center" },
335
+ ...handlers,
336
+ children: img
337
+ }
338
+ );
339
+ }
340
+ var QuikturnLogoCarousel = React.memo(
341
+ function QuikturnLogoCarousel2({
342
+ domains,
343
+ logos,
344
+ token,
345
+ baseUrl,
346
+ speed = 120,
347
+ direction = "left",
348
+ pauseOnHover,
349
+ hoverSpeed,
350
+ logoHeight = 28,
351
+ gap = 32,
352
+ width = "100%",
353
+ fadeOut = false,
354
+ fadeOutColor,
355
+ scaleOnHover = false,
356
+ logoSize,
357
+ logoFormat,
358
+ logoGreyscale,
359
+ logoTheme,
360
+ renderItem,
361
+ className,
362
+ style,
363
+ ariaLabel = "Company logos"
364
+ }) {
365
+ const ctx = useQuikturnContext();
366
+ const effectiveToken = token ?? ctx?.token ?? "";
367
+ const effectiveBaseUrl = baseUrl ?? ctx?.baseUrl;
368
+ const containerRef = useRef(null);
369
+ const trackRef = useRef(null);
370
+ const seqRef = useRef(null);
371
+ const [seqWidth, setSeqWidth] = useState(0);
372
+ const [seqHeight, setSeqHeight] = useState(0);
373
+ const [copyCount, setCopyCount] = useState(ANIMATION_CONFIG.MIN_COPIES);
374
+ const [isHovered, setIsHovered] = useState(false);
375
+ const isVertical = direction === "up" || direction === "down";
376
+ const resolvedLogos = useMemo(() => {
377
+ const items = logos ?? (domains ?? []).map((d) => ({ domain: d }));
378
+ return items.map((item) => ({
379
+ domain: item.domain,
380
+ alt: item.alt ?? `${item.domain} logo`,
381
+ href: item.href,
382
+ url: logoUrl(item.domain, {
383
+ token: effectiveToken || void 0,
384
+ size: item.size ?? logoSize,
385
+ format: item.format ?? logoFormat,
386
+ greyscale: item.greyscale ?? logoGreyscale,
387
+ theme: item.theme ?? logoTheme,
388
+ baseUrl: effectiveBaseUrl
389
+ })
390
+ }));
391
+ }, [
392
+ domains,
393
+ logos,
394
+ effectiveToken,
395
+ effectiveBaseUrl,
396
+ logoSize,
397
+ logoFormat,
398
+ logoGreyscale,
399
+ logoTheme
400
+ ]);
401
+ useEffect(() => {
402
+ if (effectiveToken) fireBeacon(effectiveToken);
403
+ }, [effectiveToken]);
404
+ const effectiveHoverSpeed = useMemo(() => {
405
+ if (hoverSpeed !== void 0) return hoverSpeed;
406
+ if (pauseOnHover === true) return 0;
407
+ return void 0;
408
+ }, [hoverSpeed, pauseOnHover]);
409
+ const targetVelocity = useMemo(() => {
410
+ const mag = Math.abs(speed);
411
+ const dirMul = isVertical ? direction === "up" ? 1 : -1 : direction === "left" ? 1 : -1;
412
+ const signMul = speed < 0 ? -1 : 1;
413
+ return mag * dirMul * signMul;
414
+ }, [speed, direction, isVertical]);
415
+ const updateDimensions = useCallback(() => {
416
+ const containerEl = containerRef.current;
417
+ const seqRect = seqRef.current?.getBoundingClientRect();
418
+ const sw = seqRect?.width ?? 0;
419
+ const sh = seqRect?.height ?? 0;
420
+ if (isVertical) {
421
+ const parentH = containerEl?.parentElement?.clientHeight ?? 0;
422
+ if (containerEl && parentH > 0) {
423
+ containerEl.style.height = `${Math.ceil(parentH)}px`;
424
+ }
425
+ if (sh > 0) {
426
+ setSeqHeight(Math.ceil(sh));
427
+ const viewport = containerEl?.clientHeight ?? parentH ?? sh;
428
+ const copies = Math.ceil(viewport / sh) + ANIMATION_CONFIG.COPY_HEADROOM;
429
+ setCopyCount(Math.max(ANIMATION_CONFIG.MIN_COPIES, copies));
430
+ }
431
+ } else if (sw > 0) {
432
+ setSeqWidth(Math.ceil(sw));
433
+ const containerW = containerEl?.clientWidth ?? 0;
434
+ const copies = Math.ceil(containerW / sw) + ANIMATION_CONFIG.COPY_HEADROOM;
435
+ setCopyCount(Math.max(ANIMATION_CONFIG.MIN_COPIES, copies));
436
+ }
437
+ }, [isVertical]);
438
+ useResizeObserver(updateDimensions, [containerRef, seqRef], [
439
+ resolvedLogos,
440
+ gap,
441
+ logoHeight,
442
+ isVertical
443
+ ]);
444
+ useImageLoader(seqRef, updateDimensions, [
445
+ resolvedLogos,
446
+ gap,
447
+ logoHeight,
448
+ isVertical
449
+ ]);
450
+ useAnimationLoop(
451
+ trackRef,
452
+ targetVelocity,
453
+ seqWidth,
454
+ seqHeight,
455
+ isHovered,
456
+ effectiveHoverSpeed,
457
+ isVertical
458
+ );
459
+ const handleMouseEnter = useCallback(() => {
460
+ if (effectiveHoverSpeed !== void 0) setIsHovered(true);
461
+ }, [effectiveHoverSpeed]);
462
+ const handleMouseLeave = useCallback(() => {
463
+ if (effectiveHoverSpeed !== void 0) setIsHovered(false);
464
+ }, [effectiveHoverSpeed]);
465
+ const logoLists = useMemo(
466
+ () => Array.from({ length: copyCount }, (_, ci) => /* @__PURE__ */ jsx(
467
+ "ul",
468
+ {
469
+ ref: ci === 0 ? seqRef : void 0,
470
+ role: "list",
471
+ "aria-hidden": ci > 0 ? true : void 0,
472
+ style: {
473
+ display: "flex",
474
+ flexDirection: isVertical ? "column" : "row",
475
+ alignItems: "center",
476
+ listStyle: "none",
477
+ margin: 0,
478
+ padding: 0
479
+ },
480
+ children: resolvedLogos.map((logo, i) => /* @__PURE__ */ jsx(
481
+ "li",
482
+ {
483
+ role: "listitem",
484
+ style: {
485
+ flex: "none",
486
+ ...isVertical ? { marginBottom: `${gap}px` } : { marginRight: `${gap}px` }
487
+ },
488
+ children: renderItem ? renderItem(logo, i) : /* @__PURE__ */ jsx(
489
+ DefaultLogoItem,
490
+ {
491
+ logo,
492
+ logoHeight,
493
+ scaleOnHover
494
+ }
495
+ )
496
+ },
497
+ `${ci}-${i}`
498
+ ))
499
+ },
500
+ ci
501
+ )),
502
+ [
503
+ copyCount,
504
+ resolvedLogos,
505
+ isVertical,
506
+ gap,
507
+ logoHeight,
508
+ scaleOnHover,
509
+ renderItem
510
+ ]
511
+ );
512
+ const cssWidth = toCssLength(width);
513
+ return /* @__PURE__ */ jsxs(
514
+ "div",
515
+ {
516
+ ref: containerRef,
517
+ role: "region",
518
+ "aria-label": ariaLabel,
519
+ className,
520
+ style: {
521
+ position: "relative",
522
+ overflow: "hidden",
523
+ width: isVertical ? void 0 : cssWidth ?? "100%",
524
+ height: isVertical ? "100%" : void 0,
525
+ display: isVertical ? "inline-block" : void 0,
526
+ ...style
527
+ },
528
+ children: [
529
+ fadeOut && /* @__PURE__ */ jsx(FadeOverlays, { isVertical, fadeColor: fadeOutColor }),
530
+ /* @__PURE__ */ jsx(
531
+ "div",
532
+ {
533
+ ref: trackRef,
534
+ style: {
535
+ display: "flex",
536
+ flexDirection: isVertical ? "column" : "row",
537
+ width: isVertical ? "100%" : "max-content",
538
+ willChange: "transform",
539
+ userSelect: "none",
540
+ position: "relative",
541
+ zIndex: 0
542
+ },
543
+ onMouseEnter: handleMouseEnter,
544
+ onMouseLeave: handleMouseLeave,
545
+ children: logoLists
546
+ }
547
+ )
548
+ ]
549
+ }
550
+ );
551
+ }
552
+ );
553
+ function QuikturnLogoGrid({
554
+ domains,
555
+ logos,
556
+ token,
557
+ baseUrl,
558
+ columns = 4,
559
+ gap = 24,
560
+ logoSize,
561
+ logoFormat,
562
+ logoGreyscale,
563
+ logoTheme,
564
+ renderItem,
565
+ className,
566
+ style,
567
+ ariaLabel = "Company logos"
568
+ }) {
569
+ const ctx = useQuikturnContext();
570
+ const effectiveToken = token ?? ctx?.token ?? "";
571
+ const effectiveBaseUrl = baseUrl ?? ctx?.baseUrl;
572
+ const resolvedLogos = useMemo(() => {
573
+ const items = logos ?? (domains ?? []).map((d) => ({ domain: d }));
574
+ return items.map((item) => ({
575
+ domain: item.domain,
576
+ alt: item.alt ?? `${item.domain} logo`,
577
+ href: item.href,
578
+ url: logoUrl(item.domain, {
579
+ token: effectiveToken || void 0,
580
+ size: item.size ?? logoSize,
581
+ format: item.format ?? logoFormat,
582
+ greyscale: item.greyscale ?? logoGreyscale,
583
+ theme: item.theme ?? logoTheme,
584
+ baseUrl: effectiveBaseUrl
585
+ })
586
+ }));
587
+ }, [
588
+ domains,
589
+ logos,
590
+ effectiveToken,
591
+ effectiveBaseUrl,
592
+ logoSize,
593
+ logoFormat,
594
+ logoGreyscale,
595
+ logoTheme
596
+ ]);
597
+ useEffect(() => {
598
+ if (effectiveToken) fireBeacon(effectiveToken);
599
+ }, [effectiveToken]);
600
+ return /* @__PURE__ */ jsx(
601
+ "div",
602
+ {
603
+ role: "region",
604
+ "aria-label": ariaLabel,
605
+ className,
606
+ style: {
607
+ display: "grid",
608
+ gridTemplateColumns: `repeat(${columns}, 1fr)`,
609
+ gap: `${gap}px`,
610
+ alignItems: "center",
611
+ justifyItems: "center",
612
+ ...style
613
+ },
614
+ children: resolvedLogos.map(
615
+ (logo, i) => renderItem ? /* @__PURE__ */ jsx(React.Fragment, { children: renderItem(logo, i) }, logo.domain) : /* @__PURE__ */ jsx(
616
+ "div",
617
+ {
618
+ style: {
619
+ display: "flex",
620
+ alignItems: "center",
621
+ justifyContent: "center"
622
+ },
623
+ children: logo.href ? /* @__PURE__ */ jsx(
624
+ "a",
625
+ {
626
+ href: logo.href,
627
+ target: "_blank",
628
+ rel: "noopener noreferrer",
629
+ "aria-label": logo.alt,
630
+ children: /* @__PURE__ */ jsx(
631
+ "img",
632
+ {
633
+ src: logo.url,
634
+ alt: logo.alt,
635
+ loading: "lazy",
636
+ style: {
637
+ maxWidth: "100%",
638
+ height: "auto",
639
+ display: "block"
640
+ }
641
+ }
642
+ )
643
+ }
644
+ ) : /* @__PURE__ */ jsx(
645
+ "img",
646
+ {
647
+ src: logo.url,
648
+ alt: logo.alt,
649
+ loading: "lazy",
650
+ style: {
651
+ maxWidth: "100%",
652
+ height: "auto",
653
+ display: "block"
654
+ }
655
+ }
656
+ )
657
+ },
658
+ logo.domain
659
+ )
660
+ )
661
+ }
662
+ );
663
+ }
664
+
665
+ export { QuikturnLogo, QuikturnLogoCarousel, QuikturnLogoGrid, QuikturnProvider, useLogoUrl };
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@quikturn/logos-react",
3
+ "version": "1.0.0",
4
+ "description": "React components for the Quikturn Logos API — QuikturnLogo, LogoCarousel, LogoGrid",
5
+ "license": "MIT",
6
+ "author": "Quikturn",
7
+ "type": "module",
8
+ "sideEffects": false,
9
+ "exports": {
10
+ ".": {
11
+ "types": {
12
+ "import": "./dist/index.d.ts",
13
+ "require": "./dist/index.d.cts"
14
+ },
15
+ "import": "./dist/index.mjs",
16
+ "require": "./dist/index.cjs"
17
+ }
18
+ },
19
+ "main": "./dist/index.cjs",
20
+ "module": "./dist/index.mjs",
21
+ "types": "./dist/index.d.ts",
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "package.json"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsup",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest",
33
+ "typecheck": "tsc --noEmit"
34
+ },
35
+ "peerDependencies": {
36
+ "@quikturn/logos": ">=0.1.0",
37
+ "react": ">=18",
38
+ "react-dom": ">=18"
39
+ },
40
+ "devDependencies": {
41
+ "@quikturn/logos": "workspace:*",
42
+ "@semantic-release/changelog": "^6.0.3",
43
+ "@semantic-release/git": "^10.0.1",
44
+ "@testing-library/jest-dom": "^6.6.3",
45
+ "@testing-library/react": "^16.3.0",
46
+ "@types/react": "^19.0.0",
47
+ "@types/react-dom": "^19.0.0",
48
+ "jsdom": "^28.0.0",
49
+ "react": "^19.0.0",
50
+ "react-dom": "^19.0.0",
51
+ "semantic-release": "^25.0.3",
52
+ "tsup": "^8.5.1",
53
+ "typescript": "^5.9.3",
54
+ "vitest": "^4.0.18"
55
+ }
56
+ }