fancy-ui-svelte 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +198 -0
- package/dist/fancy-ui/_template/Component.svelte +110 -0
- package/dist/fancy-ui/_template/Component.svelte.d.ts +16 -0
- package/dist/fancy-ui/_template/README.md +279 -0
- package/dist/fancy-ui/_template/index.d.ts +13 -0
- package/dist/fancy-ui/_template/index.js +27 -0
- package/dist/fancy-ui/_template/types.d.ts +53 -0
- package/dist/fancy-ui/_template/types.js +12 -0
- package/dist/fancy-ui/animated-beam/AnimatedBeam.svelte +166 -0
- package/dist/fancy-ui/animated-beam/AnimatedBeam.svelte.d.ts +4 -0
- package/dist/fancy-ui/animated-beam/AnimatedBeam.test.d.ts +1 -0
- package/dist/fancy-ui/animated-beam/AnimatedBeam.test.js +49 -0
- package/dist/fancy-ui/animated-beam/README.md +38 -0
- package/dist/fancy-ui/animated-beam/index.d.ts +2 -0
- package/dist/fancy-ui/animated-beam/index.js +1 -0
- package/dist/fancy-ui/animated-beam/types.d.ts +19 -0
- package/dist/fancy-ui/animated-beam/types.js +1 -0
- package/dist/fancy-ui/animated-testimonials/AnimatedTestimonials.svelte +196 -0
- package/dist/fancy-ui/animated-testimonials/AnimatedTestimonials.svelte.d.ts +29 -0
- package/dist/fancy-ui/animated-testimonials/AnimatedTestimonials.test.d.ts +1 -0
- package/dist/fancy-ui/animated-testimonials/AnimatedTestimonials.test.js +92 -0
- package/dist/fancy-ui/animated-testimonials/index.d.ts +2 -0
- package/dist/fancy-ui/animated-testimonials/index.js +1 -0
- package/dist/fancy-ui/animated-tooltip/AnimatedTooltip.svelte +97 -0
- package/dist/fancy-ui/animated-tooltip/AnimatedTooltip.svelte.d.ts +15 -0
- package/dist/fancy-ui/animated-tooltip/AnimatedTooltip.test.d.ts +1 -0
- package/dist/fancy-ui/animated-tooltip/AnimatedTooltip.test.js +67 -0
- package/dist/fancy-ui/animated-tooltip/index.d.ts +2 -0
- package/dist/fancy-ui/animated-tooltip/index.js +1 -0
- package/dist/fancy-ui/apple-card-carousel/AppleCard.svelte +226 -0
- package/dist/fancy-ui/apple-card-carousel/AppleCard.svelte.d.ts +27 -0
- package/dist/fancy-ui/apple-card-carousel/AppleCardCarousel.svelte +49 -0
- package/dist/fancy-ui/apple-card-carousel/AppleCardCarousel.svelte.d.ts +11 -0
- package/dist/fancy-ui/apple-card-carousel/AppleCardCarousel.test.d.ts +1 -0
- package/dist/fancy-ui/apple-card-carousel/AppleCardCarousel.test.js +85 -0
- package/dist/fancy-ui/apple-card-carousel/index.d.ts +3 -0
- package/dist/fancy-ui/apple-card-carousel/index.js +2 -0
- package/dist/fancy-ui/bento-grid/BentoGrid.svelte +21 -0
- package/dist/fancy-ui/bento-grid/BentoGrid.svelte.d.ts +7 -0
- package/dist/fancy-ui/bento-grid/BentoGridCard.svelte +70 -0
- package/dist/fancy-ui/bento-grid/BentoGridCard.svelte.d.ts +12 -0
- package/dist/fancy-ui/bento-grid/BentoGridItem.svelte +39 -0
- package/dist/fancy-ui/bento-grid/BentoGridItem.svelte.d.ts +10 -0
- package/dist/fancy-ui/bento-grid/README.md +51 -0
- package/dist/fancy-ui/bento-grid/index.d.ts +3 -0
- package/dist/fancy-ui/bento-grid/index.js +3 -0
- package/dist/fancy-ui/bg-falling-stars/FallingStarsBg.svelte +163 -0
- package/dist/fancy-ui/bg-falling-stars/FallingStarsBg.svelte.d.ts +11 -0
- package/dist/fancy-ui/bg-falling-stars/FallingStarsBg.test.d.ts +1 -0
- package/dist/fancy-ui/bg-falling-stars/FallingStarsBg.test.js +50 -0
- package/dist/fancy-ui/bg-falling-stars/README.md +22 -0
- package/dist/fancy-ui/bg-falling-stars/index.d.ts +2 -0
- package/dist/fancy-ui/bg-falling-stars/index.js +1 -0
- package/dist/fancy-ui/bg-stars/README.md +41 -0
- package/dist/fancy-ui/bg-stars/StarsBackground.svelte +205 -0
- package/dist/fancy-ui/bg-stars/StarsBackground.svelte.d.ts +33 -0
- package/dist/fancy-ui/bg-stars/StarsBackground.test.d.ts +1 -0
- package/dist/fancy-ui/bg-stars/StarsBackground.test.js +46 -0
- package/dist/fancy-ui/bg-stars/index.d.ts +1 -0
- package/dist/fancy-ui/bg-stars/index.js +1 -0
- package/dist/fancy-ui/blur-reveal/BlurReveal.svelte +126 -0
- package/dist/fancy-ui/blur-reveal/BlurReveal.svelte.d.ts +18 -0
- package/dist/fancy-ui/blur-reveal/BlurReveal.test.d.ts +1 -0
- package/dist/fancy-ui/blur-reveal/BlurReveal.test.js +47 -0
- package/dist/fancy-ui/blur-reveal/README.md +41 -0
- package/dist/fancy-ui/blur-reveal/index.d.ts +2 -0
- package/dist/fancy-ui/blur-reveal/index.js +2 -0
- package/dist/fancy-ui/book/Book.svelte +89 -0
- package/dist/fancy-ui/book/Book.svelte.d.ts +15 -0
- package/dist/fancy-ui/book/Book.test.d.ts +1 -0
- package/dist/fancy-ui/book/Book.test.js +47 -0
- package/dist/fancy-ui/book/BookDescription.svelte +17 -0
- package/dist/fancy-ui/book/BookDescription.svelte.d.ts +8 -0
- package/dist/fancy-ui/book/BookHeader.svelte +17 -0
- package/dist/fancy-ui/book/BookHeader.svelte.d.ts +8 -0
- package/dist/fancy-ui/book/BookTitle.svelte +17 -0
- package/dist/fancy-ui/book/BookTitle.svelte.d.ts +8 -0
- package/dist/fancy-ui/book/README.md +34 -0
- package/dist/fancy-ui/book/index.d.ts +128 -0
- package/dist/fancy-ui/book/index.js +46 -0
- package/dist/fancy-ui/border-beam/BorderBeam.svelte +85 -0
- package/dist/fancy-ui/border-beam/BorderBeam.svelte.d.ts +27 -0
- package/dist/fancy-ui/border-beam/BorderBeam.test.d.ts +1 -0
- package/dist/fancy-ui/border-beam/BorderBeam.test.js +64 -0
- package/dist/fancy-ui/border-beam/index.d.ts +2 -0
- package/dist/fancy-ui/border-beam/index.js +2 -0
- package/dist/fancy-ui/box-reveal/BoxReveal.svelte +73 -0
- package/dist/fancy-ui/box-reveal/BoxReveal.svelte.d.ts +24 -0
- package/dist/fancy-ui/box-reveal/BoxReveal.test.d.ts +1 -0
- package/dist/fancy-ui/box-reveal/BoxReveal.test.js +66 -0
- package/dist/fancy-ui/box-reveal/README.md +24 -0
- package/dist/fancy-ui/box-reveal/index.d.ts +2 -0
- package/dist/fancy-ui/box-reveal/index.js +2 -0
- package/dist/fancy-ui/card-3d/CardBody.svelte +16 -0
- package/dist/fancy-ui/card-3d/CardBody.svelte.d.ts +7 -0
- package/dist/fancy-ui/card-3d/CardContainer.svelte +53 -0
- package/dist/fancy-ui/card-3d/CardContainer.svelte.d.ts +8 -0
- package/dist/fancy-ui/card-3d/CardItem.svelte +49 -0
- package/dist/fancy-ui/card-3d/CardItem.svelte.d.ts +14 -0
- package/dist/fancy-ui/card-3d/README.md +52 -0
- package/dist/fancy-ui/card-3d/index.d.ts +3 -0
- package/dist/fancy-ui/card-3d/index.js +3 -0
- package/dist/fancy-ui/card-spotlight/CardSpotlight.svelte +59 -0
- package/dist/fancy-ui/card-spotlight/CardSpotlight.svelte.d.ts +11 -0
- package/dist/fancy-ui/card-spotlight/README.md +24 -0
- package/dist/fancy-ui/card-spotlight/index.d.ts +1 -0
- package/dist/fancy-ui/card-spotlight/index.js +1 -0
- package/dist/fancy-ui/colourful-text/ColourfulText.svelte +78 -0
- package/dist/fancy-ui/colourful-text/ColourfulText.svelte.d.ts +22 -0
- package/dist/fancy-ui/colourful-text/ColourfulText.test.d.ts +1 -0
- package/dist/fancy-ui/colourful-text/ColourfulText.test.js +53 -0
- package/dist/fancy-ui/colourful-text/README.md +21 -0
- package/dist/fancy-ui/colourful-text/index.d.ts +2 -0
- package/dist/fancy-ui/colourful-text/index.js +2 -0
- package/dist/fancy-ui/compare/Compare.svelte +304 -0
- package/dist/fancy-ui/compare/Compare.svelte.d.ts +25 -0
- package/dist/fancy-ui/compare/Compare.test.d.ts +1 -0
- package/dist/fancy-ui/compare/Compare.test.js +57 -0
- package/dist/fancy-ui/compare/README.md +97 -0
- package/dist/fancy-ui/compare/StarField.svelte +108 -0
- package/dist/fancy-ui/compare/StarField.svelte.d.ts +7 -0
- package/dist/fancy-ui/compare/index.d.ts +2 -0
- package/dist/fancy-ui/compare/index.js +2 -0
- package/dist/fancy-ui/confetti/Confetti.svelte +60 -0
- package/dist/fancy-ui/confetti/Confetti.svelte.d.ts +14 -0
- package/dist/fancy-ui/confetti/Confetti.test.d.ts +1 -0
- package/dist/fancy-ui/confetti/Confetti.test.js +39 -0
- package/dist/fancy-ui/confetti/ConfettiButton.svelte +41 -0
- package/dist/fancy-ui/confetti/ConfettiButton.svelte.d.ts +9 -0
- package/dist/fancy-ui/confetti/README.md +22 -0
- package/dist/fancy-ui/confetti/index.d.ts +2 -0
- package/dist/fancy-ui/confetti/index.js +2 -0
- package/dist/fancy-ui/container-scroll/ContainerScroll.svelte +82 -0
- package/dist/fancy-ui/container-scroll/ContainerScroll.svelte.d.ts +9 -0
- package/dist/fancy-ui/container-scroll/ContainerScroll.test.d.ts +1 -0
- package/dist/fancy-ui/container-scroll/ContainerScroll.test.js +34 -0
- package/dist/fancy-ui/container-scroll/README.md +28 -0
- package/dist/fancy-ui/container-scroll/index.d.ts +1 -0
- package/dist/fancy-ui/container-scroll/index.js +1 -0
- package/dist/fancy-ui/container-text-flip/ContainerTextFlip.svelte +78 -0
- package/dist/fancy-ui/container-text-flip/ContainerTextFlip.svelte.d.ts +10 -0
- package/dist/fancy-ui/container-text-flip/ContainerTextFlip.test.d.ts +1 -0
- package/dist/fancy-ui/container-text-flip/ContainerTextFlip.test.js +46 -0
- package/dist/fancy-ui/container-text-flip/README.md +22 -0
- package/dist/fancy-ui/container-text-flip/index.d.ts +1 -0
- package/dist/fancy-ui/container-text-flip/index.js +1 -0
- package/dist/fancy-ui/direction-aware-hover/DirectionAwareHover.svelte +232 -0
- package/dist/fancy-ui/direction-aware-hover/DirectionAwareHover.svelte.d.ts +11 -0
- package/dist/fancy-ui/direction-aware-hover/DirectionAwareHover.test.d.ts +1 -0
- package/dist/fancy-ui/direction-aware-hover/DirectionAwareHover.test.js +78 -0
- package/dist/fancy-ui/direction-aware-hover/README.md +47 -0
- package/dist/fancy-ui/direction-aware-hover/index.d.ts +1 -0
- package/dist/fancy-ui/direction-aware-hover/index.js +1 -0
- package/dist/fancy-ui/displacement-text/DisplacementText.svelte +224 -0
- package/dist/fancy-ui/displacement-text/DisplacementText.svelte.d.ts +19 -0
- package/dist/fancy-ui/displacement-text/DisplacementText.test.d.ts +1 -0
- package/dist/fancy-ui/displacement-text/DisplacementText.test.js +144 -0
- package/dist/fancy-ui/displacement-text/index.d.ts +2 -0
- package/dist/fancy-ui/displacement-text/index.js +1 -0
- package/dist/fancy-ui/dock/Dock.svelte +87 -0
- package/dist/fancy-ui/dock/Dock.svelte.d.ts +12 -0
- package/dist/fancy-ui/dock/Dock.test.d.ts +1 -0
- package/dist/fancy-ui/dock/Dock.test.js +51 -0
- package/dist/fancy-ui/dock/DockIcon.svelte +50 -0
- package/dist/fancy-ui/dock/DockIcon.svelte.d.ts +7 -0
- package/dist/fancy-ui/dock/DockSeparator.svelte +21 -0
- package/dist/fancy-ui/dock/DockSeparator.svelte.d.ts +6 -0
- package/dist/fancy-ui/dock/README.md +57 -0
- package/dist/fancy-ui/dock/index.d.ts +4 -0
- package/dist/fancy-ui/dock/index.js +4 -0
- package/dist/fancy-ui/dock/types.d.ts +14 -0
- package/dist/fancy-ui/dock/types.js +1 -0
- package/dist/fancy-ui/flickering-grid/FlickeringGrid.svelte +154 -0
- package/dist/fancy-ui/flickering-grid/FlickeringGrid.svelte.d.ts +27 -0
- package/dist/fancy-ui/flickering-grid/FlickeringGrid.test.d.ts +1 -0
- package/dist/fancy-ui/flickering-grid/FlickeringGrid.test.js +34 -0
- package/dist/fancy-ui/flickering-grid/README.md +24 -0
- package/dist/fancy-ui/flickering-grid/index.d.ts +2 -0
- package/dist/fancy-ui/flickering-grid/index.js +2 -0
- package/dist/fancy-ui/flip-card/FlipCard.svelte +48 -0
- package/dist/fancy-ui/flip-card/FlipCard.svelte.d.ts +10 -0
- package/dist/fancy-ui/flip-card/FlipCard.test.d.ts +1 -0
- package/dist/fancy-ui/flip-card/FlipCard.test.js +36 -0
- package/dist/fancy-ui/flip-card/README.md +26 -0
- package/dist/fancy-ui/flip-card/index.d.ts +1 -0
- package/dist/fancy-ui/flip-card/index.js +1 -0
- package/dist/fancy-ui/flip-words/FlipWords.svelte +135 -0
- package/dist/fancy-ui/flip-words/FlipWords.svelte.d.ts +18 -0
- package/dist/fancy-ui/flip-words/FlipWords.test.d.ts +1 -0
- package/dist/fancy-ui/flip-words/FlipWords.test.js +50 -0
- package/dist/fancy-ui/flip-words/README.md +17 -0
- package/dist/fancy-ui/flip-words/index.d.ts +2 -0
- package/dist/fancy-ui/flip-words/index.js +2 -0
- package/dist/fancy-ui/fluid-cursor/FluidCursor.svelte +1378 -0
- package/dist/fancy-ui/fluid-cursor/FluidCursor.svelte.d.ts +25 -0
- package/dist/fancy-ui/fluid-cursor/FluidCursor.test.d.ts +1 -0
- package/dist/fancy-ui/fluid-cursor/FluidCursor.test.js +41 -0
- package/dist/fancy-ui/fluid-cursor/README.md +34 -0
- package/dist/fancy-ui/fluid-cursor/index.d.ts +1 -0
- package/dist/fancy-ui/fluid-cursor/index.js +1 -0
- package/dist/fancy-ui/focus/Focus.svelte +177 -0
- package/dist/fancy-ui/focus/Focus.svelte.d.ts +12 -0
- package/dist/fancy-ui/focus/Focus.test.d.ts +1 -0
- package/dist/fancy-ui/focus/Focus.test.js +48 -0
- package/dist/fancy-ui/focus/README.md +21 -0
- package/dist/fancy-ui/focus/index.d.ts +1 -0
- package/dist/fancy-ui/focus/index.js +1 -0
- package/dist/fancy-ui/glare-card/GlareCard.svelte +133 -0
- package/dist/fancy-ui/glare-card/GlareCard.svelte.d.ts +8 -0
- package/dist/fancy-ui/glare-card/GlareCard.test.d.ts +1 -0
- package/dist/fancy-ui/glare-card/GlareCard.test.js +40 -0
- package/dist/fancy-ui/glare-card/README.md +24 -0
- package/dist/fancy-ui/glare-card/index.d.ts +1 -0
- package/dist/fancy-ui/glare-card/index.js +1 -0
- package/dist/fancy-ui/glow-border/GlowBorder.svelte +67 -0
- package/dist/fancy-ui/glow-border/GlowBorder.svelte.d.ts +10 -0
- package/dist/fancy-ui/glow-border/GlowBorder.test.d.ts +1 -0
- package/dist/fancy-ui/glow-border/GlowBorder.test.js +57 -0
- package/dist/fancy-ui/glow-border/README.md +54 -0
- package/dist/fancy-ui/glow-border/index.d.ts +1 -0
- package/dist/fancy-ui/glow-border/index.js +1 -0
- package/dist/fancy-ui/glowing-effect/GlowingEffect.svelte +182 -0
- package/dist/fancy-ui/glowing-effect/GlowingEffect.svelte.d.ts +15 -0
- package/dist/fancy-ui/glowing-effect/GlowingEffect.test.d.ts +1 -0
- package/dist/fancy-ui/glowing-effect/GlowingEffect.test.js +68 -0
- package/dist/fancy-ui/glowing-effect/README.md +29 -0
- package/dist/fancy-ui/glowing-effect/index.d.ts +1 -0
- package/dist/fancy-ui/glowing-effect/index.js +1 -0
- package/dist/fancy-ui/gradient-button/GradientButton.svelte +103 -0
- package/dist/fancy-ui/gradient-button/GradientButton.svelte.d.ts +24 -0
- package/dist/fancy-ui/gradient-button/GradientButton.test.d.ts +1 -0
- package/dist/fancy-ui/gradient-button/GradientButton.test.js +65 -0
- package/dist/fancy-ui/gradient-button/README.md +10 -0
- package/dist/fancy-ui/gradient-button/index.d.ts +2 -0
- package/dist/fancy-ui/gradient-button/index.js +2 -0
- package/dist/fancy-ui/hyper-text/HyperText.svelte +112 -0
- package/dist/fancy-ui/hyper-text/HyperText.svelte.d.ts +20 -0
- package/dist/fancy-ui/hyper-text/HyperText.test.d.ts +1 -0
- package/dist/fancy-ui/hyper-text/HyperText.test.js +39 -0
- package/dist/fancy-ui/hyper-text/README.md +19 -0
- package/dist/fancy-ui/hyper-text/index.d.ts +2 -0
- package/dist/fancy-ui/hyper-text/index.js +2 -0
- package/dist/fancy-ui/image-trail-cursor/ImageTrailCursor.svelte +89 -0
- package/dist/fancy-ui/image-trail-cursor/ImageTrailCursor.svelte.d.ts +9 -0
- package/dist/fancy-ui/image-trail-cursor/ImageTrailCursor.test.d.ts +1 -0
- package/dist/fancy-ui/image-trail-cursor/ImageTrailCursor.test.js +60 -0
- package/dist/fancy-ui/image-trail-cursor/README.md +36 -0
- package/dist/fancy-ui/image-trail-cursor/index.d.ts +3 -0
- package/dist/fancy-ui/image-trail-cursor/index.js +1 -0
- package/dist/fancy-ui/image-trail-cursor/trail-variants.d.ts +107 -0
- package/dist/fancy-ui/image-trail-cursor/trail-variants.js +703 -0
- package/dist/fancy-ui/index.d.ts +52 -0
- package/dist/fancy-ui/index.js +64 -0
- package/dist/fancy-ui/interactive-grid-pattern/InteractiveGridPattern.svelte +71 -0
- package/dist/fancy-ui/interactive-grid-pattern/InteractiveGridPattern.svelte.d.ts +15 -0
- package/dist/fancy-ui/interactive-grid-pattern/InteractiveGridPattern.test.d.ts +1 -0
- package/dist/fancy-ui/interactive-grid-pattern/InteractiveGridPattern.test.js +47 -0
- package/dist/fancy-ui/interactive-grid-pattern/README.md +30 -0
- package/dist/fancy-ui/interactive-grid-pattern/index.d.ts +2 -0
- package/dist/fancy-ui/interactive-grid-pattern/index.js +2 -0
- package/dist/fancy-ui/interactive-hover-button/InteractiveHoverButton.svelte +75 -0
- package/dist/fancy-ui/interactive-hover-button/InteractiveHoverButton.svelte.d.ts +14 -0
- package/dist/fancy-ui/interactive-hover-button/InteractiveHoverButton.test.d.ts +1 -0
- package/dist/fancy-ui/interactive-hover-button/InteractiveHoverButton.test.js +64 -0
- package/dist/fancy-ui/interactive-hover-button/README.md +28 -0
- package/dist/fancy-ui/interactive-hover-button/index.d.ts +2 -0
- package/dist/fancy-ui/interactive-hover-button/index.js +2 -0
- package/dist/fancy-ui/letter-pullup/LetterPullup.svelte +58 -0
- package/dist/fancy-ui/letter-pullup/LetterPullup.svelte.d.ts +17 -0
- package/dist/fancy-ui/letter-pullup/LetterPullup.test.d.ts +1 -0
- package/dist/fancy-ui/letter-pullup/LetterPullup.test.js +49 -0
- package/dist/fancy-ui/letter-pullup/README.md +18 -0
- package/dist/fancy-ui/letter-pullup/index.d.ts +2 -0
- package/dist/fancy-ui/letter-pullup/index.js +2 -0
- package/dist/fancy-ui/line-hover-link/LineHoverLink.svelte +450 -0
- package/dist/fancy-ui/line-hover-link/LineHoverLink.svelte.d.ts +35 -0
- package/dist/fancy-ui/line-hover-link/LineHoverLink.test.d.ts +1 -0
- package/dist/fancy-ui/line-hover-link/LineHoverLink.test.js +55 -0
- package/dist/fancy-ui/line-hover-link/index.d.ts +2 -0
- package/dist/fancy-ui/line-hover-link/index.js +1 -0
- package/dist/fancy-ui/line-shadow-text/LineShadowText.svelte +63 -0
- package/dist/fancy-ui/line-shadow-text/LineShadowText.svelte.d.ts +9 -0
- package/dist/fancy-ui/line-shadow-text/LineShadowText.test.d.ts +1 -0
- package/dist/fancy-ui/line-shadow-text/LineShadowText.test.js +59 -0
- package/dist/fancy-ui/line-shadow-text/README.md +12 -0
- package/dist/fancy-ui/line-shadow-text/index.d.ts +2 -0
- package/dist/fancy-ui/line-shadow-text/index.js +1 -0
- package/dist/fancy-ui/liquid-glass/LiquidGlass.svelte +204 -0
- package/dist/fancy-ui/liquid-glass/LiquidGlass.svelte.d.ts +23 -0
- package/dist/fancy-ui/liquid-glass/LiquidGlass.test.d.ts +1 -0
- package/dist/fancy-ui/liquid-glass/LiquidGlass.test.js +48 -0
- package/dist/fancy-ui/liquid-glass/README.md +34 -0
- package/dist/fancy-ui/liquid-glass/index.d.ts +1 -0
- package/dist/fancy-ui/liquid-glass/index.js +1 -0
- package/dist/fancy-ui/logo-cloud/AnimatedLogoCloud.svelte +61 -0
- package/dist/fancy-ui/logo-cloud/AnimatedLogoCloud.svelte.d.ts +15 -0
- package/dist/fancy-ui/logo-cloud/AnimatedLogoCloud.test.d.ts +1 -0
- package/dist/fancy-ui/logo-cloud/AnimatedLogoCloud.test.js +59 -0
- package/dist/fancy-ui/logo-cloud/IconLogoCloud.svelte +31 -0
- package/dist/fancy-ui/logo-cloud/IconLogoCloud.svelte.d.ts +11 -0
- package/dist/fancy-ui/logo-cloud/README.md +41 -0
- package/dist/fancy-ui/logo-cloud/StaticLogoCloud.svelte +31 -0
- package/dist/fancy-ui/logo-cloud/StaticLogoCloud.svelte.d.ts +11 -0
- package/dist/fancy-ui/logo-cloud/index.d.ts +4 -0
- package/dist/fancy-ui/logo-cloud/index.js +4 -0
- package/dist/fancy-ui/marquee/Marquee.svelte +70 -0
- package/dist/fancy-ui/marquee/Marquee.svelte.d.ts +11 -0
- package/dist/fancy-ui/marquee/Marquee.test.d.ts +1 -0
- package/dist/fancy-ui/marquee/Marquee.test.js +46 -0
- package/dist/fancy-ui/marquee/ReviewCard.svelte +25 -0
- package/dist/fancy-ui/marquee/ReviewCard.svelte.d.ts +9 -0
- package/dist/fancy-ui/marquee/index.d.ts +2 -0
- package/dist/fancy-ui/marquee/index.js +2 -0
- package/dist/fancy-ui/meteors/Meteors.svelte +59 -0
- package/dist/fancy-ui/meteors/Meteors.svelte.d.ts +15 -0
- package/dist/fancy-ui/meteors/Meteors.test.d.ts +1 -0
- package/dist/fancy-ui/meteors/Meteors.test.js +48 -0
- package/dist/fancy-ui/meteors/README.md +18 -0
- package/dist/fancy-ui/meteors/index.d.ts +2 -0
- package/dist/fancy-ui/meteors/index.js +2 -0
- package/dist/fancy-ui/neon-border/NeonBorder.svelte +135 -0
- package/dist/fancy-ui/neon-border/NeonBorder.svelte.d.ts +24 -0
- package/dist/fancy-ui/neon-border/NeonBorder.test.d.ts +1 -0
- package/dist/fancy-ui/neon-border/NeonBorder.test.js +80 -0
- package/dist/fancy-ui/neon-border/README.md +27 -0
- package/dist/fancy-ui/neon-border/index.d.ts +2 -0
- package/dist/fancy-ui/neon-border/index.js +2 -0
- package/dist/fancy-ui/number-ticker/NumberTicker.svelte +126 -0
- package/dist/fancy-ui/number-ticker/NumberTicker.svelte.d.ts +24 -0
- package/dist/fancy-ui/number-ticker/NumberTicker.test.d.ts +1 -0
- package/dist/fancy-ui/number-ticker/NumberTicker.test.js +64 -0
- package/dist/fancy-ui/number-ticker/README.md +22 -0
- package/dist/fancy-ui/number-ticker/index.d.ts +2 -0
- package/dist/fancy-ui/number-ticker/index.js +2 -0
- package/dist/fancy-ui/rainbow-button/RainbowButton.svelte +96 -0
- package/dist/fancy-ui/rainbow-button/RainbowButton.svelte.d.ts +18 -0
- package/dist/fancy-ui/rainbow-button/RainbowButton.test.d.ts +1 -0
- package/dist/fancy-ui/rainbow-button/RainbowButton.test.js +51 -0
- package/dist/fancy-ui/rainbow-button/index.d.ts +2 -0
- package/dist/fancy-ui/rainbow-button/index.js +2 -0
- package/dist/fancy-ui/registry.d.ts +72 -0
- package/dist/fancy-ui/registry.js +643 -0
- package/dist/fancy-ui/ripple/README.md +16 -0
- package/dist/fancy-ui/ripple/Ripple.svelte +64 -0
- package/dist/fancy-ui/ripple/Ripple.svelte.d.ts +13 -0
- package/dist/fancy-ui/ripple/Ripple.test.d.ts +1 -0
- package/dist/fancy-ui/ripple/Ripple.test.js +81 -0
- package/dist/fancy-ui/ripple/index.d.ts +1 -0
- package/dist/fancy-ui/ripple/index.js +1 -0
- package/dist/fancy-ui/ripple-button/RippleButton.svelte +111 -0
- package/dist/fancy-ui/ripple-button/RippleButton.svelte.d.ts +15 -0
- package/dist/fancy-ui/ripple-button/RippleButton.test.d.ts +1 -0
- package/dist/fancy-ui/ripple-button/RippleButton.test.js +44 -0
- package/dist/fancy-ui/ripple-button/index.d.ts +2 -0
- package/dist/fancy-ui/ripple-button/index.js +2 -0
- package/dist/fancy-ui/shimmer-button/README.md +10 -0
- package/dist/fancy-ui/shimmer-button/ShimmerButton.svelte +115 -0
- package/dist/fancy-ui/shimmer-button/ShimmerButton.svelte.d.ts +22 -0
- package/dist/fancy-ui/shimmer-button/ShimmerButton.test.d.ts +1 -0
- package/dist/fancy-ui/shimmer-button/ShimmerButton.test.js +69 -0
- package/dist/fancy-ui/shimmer-button/index.d.ts +2 -0
- package/dist/fancy-ui/shimmer-button/index.js +2 -0
- package/dist/fancy-ui/smooth-cursor/README.md +23 -0
- package/dist/fancy-ui/smooth-cursor/SmoothCursor.svelte +224 -0
- package/dist/fancy-ui/smooth-cursor/SmoothCursor.svelte.d.ts +20 -0
- package/dist/fancy-ui/smooth-cursor/index.d.ts +2 -0
- package/dist/fancy-ui/smooth-cursor/index.js +1 -0
- package/dist/fancy-ui/sparkles/README.md +15 -0
- package/dist/fancy-ui/sparkles/Sparkles.svelte +123 -0
- package/dist/fancy-ui/sparkles/Sparkles.svelte.d.ts +12 -0
- package/dist/fancy-ui/sparkles/Sparkles.test.d.ts +1 -0
- package/dist/fancy-ui/sparkles/Sparkles.test.js +66 -0
- package/dist/fancy-ui/sparkles/index.d.ts +1 -0
- package/dist/fancy-ui/sparkles/index.js +1 -0
- package/dist/fancy-ui/sparkles-text/README.md +19 -0
- package/dist/fancy-ui/sparkles-text/SparklesText.svelte +112 -0
- package/dist/fancy-ui/sparkles-text/SparklesText.svelte.d.ts +22 -0
- package/dist/fancy-ui/sparkles-text/SparklesText.test.d.ts +1 -0
- package/dist/fancy-ui/sparkles-text/SparklesText.test.js +54 -0
- package/dist/fancy-ui/sparkles-text/index.d.ts +2 -0
- package/dist/fancy-ui/sparkles-text/index.js +2 -0
- package/dist/fancy-ui/tailwind.css +3 -0
- package/dist/fancy-ui/text-generate-effect/README.md +14 -0
- package/dist/fancy-ui/text-generate-effect/TextGenerateEffect.svelte +66 -0
- package/dist/fancy-ui/text-generate-effect/TextGenerateEffect.svelte.d.ts +11 -0
- package/dist/fancy-ui/text-generate-effect/TextGenerateEffect.test.d.ts +1 -0
- package/dist/fancy-ui/text-generate-effect/TextGenerateEffect.test.js +73 -0
- package/dist/fancy-ui/text-generate-effect/index.d.ts +2 -0
- package/dist/fancy-ui/text-generate-effect/index.js +1 -0
- package/dist/fancy-ui/text-reveal-card/README.md +36 -0
- package/dist/fancy-ui/text-reveal-card/TextRevealCard.svelte +112 -0
- package/dist/fancy-ui/text-reveal-card/TextRevealCard.svelte.d.ts +12 -0
- package/dist/fancy-ui/text-reveal-card/TextRevealCard.test.d.ts +1 -0
- package/dist/fancy-ui/text-reveal-card/TextRevealCard.test.js +34 -0
- package/dist/fancy-ui/text-reveal-card/TextRevealStars.svelte +76 -0
- package/dist/fancy-ui/text-reveal-card/TextRevealStars.svelte.d.ts +7 -0
- package/dist/fancy-ui/text-reveal-card/index.d.ts +2 -0
- package/dist/fancy-ui/text-reveal-card/index.js +2 -0
- package/dist/fancy-ui/timeline/README.md +56 -0
- package/dist/fancy-ui/timeline/Timeline.svelte +141 -0
- package/dist/fancy-ui/timeline/Timeline.svelte.d.ts +20 -0
- package/dist/fancy-ui/timeline/Timeline.test.d.ts +1 -0
- package/dist/fancy-ui/timeline/Timeline.test.js +63 -0
- package/dist/fancy-ui/timeline/index.d.ts +2 -0
- package/dist/fancy-ui/timeline/index.js +2 -0
- package/dist/fancy-ui/tracing-beam/README.md +24 -0
- package/dist/fancy-ui/tracing-beam/TracingBeam.svelte +156 -0
- package/dist/fancy-ui/tracing-beam/TracingBeam.svelte.d.ts +8 -0
- package/dist/fancy-ui/tracing-beam/TracingBeam.test.d.ts +1 -0
- package/dist/fancy-ui/tracing-beam/TracingBeam.test.js +61 -0
- package/dist/fancy-ui/tracing-beam/index.d.ts +1 -0
- package/dist/fancy-ui/tracing-beam/index.js +1 -0
- package/dist/types.d.ts +87 -0
- package/dist/types.js +6 -0
- package/dist/utils.d.ts +12 -0
- package/dist/utils.js +5 -0
- package/package.json +105 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as AnimatedTestimonials } from "./AnimatedTestimonials.svelte";
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
|
|
4
|
+
export interface TooltipItem {
|
|
5
|
+
id: number | string;
|
|
6
|
+
name: string;
|
|
7
|
+
designation: string;
|
|
8
|
+
image: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AnimatedTooltipProps {
|
|
12
|
+
/** Array of items to display */
|
|
13
|
+
items: TooltipItem[];
|
|
14
|
+
/** Additional CSS classes for the container */
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<script lang="ts">
|
|
20
|
+
import { cn } from "../../utils.js";
|
|
21
|
+
import { scale } from "svelte/transition";
|
|
22
|
+
|
|
23
|
+
let { items, class: className }: AnimatedTooltipProps = $props();
|
|
24
|
+
|
|
25
|
+
let hoveredIndex = $state<number | string | null>(null);
|
|
26
|
+
let mouseX = $state(0);
|
|
27
|
+
|
|
28
|
+
// Calculate rotation and translation based on mouse position
|
|
29
|
+
let rotation = $derived((mouseX / 100) * 50);
|
|
30
|
+
let translation = $derived((mouseX / 100) * 50);
|
|
31
|
+
|
|
32
|
+
function handleMouseEnter(event: MouseEvent, itemId: number | string) {
|
|
33
|
+
// Reset mouseX first to prevent offset from previous item
|
|
34
|
+
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
|
|
35
|
+
const halfWidth = rect.width / 2;
|
|
36
|
+
mouseX = event.clientX - rect.left - halfWidth;
|
|
37
|
+
hoveredIndex = itemId;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function handleMouseMove(event: MouseEvent) {
|
|
41
|
+
if (hoveredIndex === null) return;
|
|
42
|
+
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
|
|
43
|
+
const halfWidth = rect.width / 2;
|
|
44
|
+
mouseX = event.clientX - rect.left - halfWidth;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function handleMouseLeave() {
|
|
48
|
+
hoveredIndex = null;
|
|
49
|
+
mouseX = 0;
|
|
50
|
+
}
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<div class={cn("flex flex-row items-center", className)}>
|
|
54
|
+
{#each items as item (item.id)}
|
|
55
|
+
<div
|
|
56
|
+
class="group relative -mr-4"
|
|
57
|
+
onmouseenter={(e) => handleMouseEnter(e, item.id)}
|
|
58
|
+
onmouseleave={handleMouseLeave}
|
|
59
|
+
onmousemove={handleMouseMove}
|
|
60
|
+
role="button"
|
|
61
|
+
tabindex="0"
|
|
62
|
+
>
|
|
63
|
+
<!-- Tooltip -->
|
|
64
|
+
{#if hoveredIndex === item.id}
|
|
65
|
+
<div
|
|
66
|
+
class="pointer-events-none absolute -top-16 left-1/2 z-50 flex flex-col items-center justify-center rounded-md bg-black px-4 py-2 text-xs whitespace-nowrap shadow-xl"
|
|
67
|
+
style="transform: translateX(calc(-50% + {translation}px)) rotate({rotation}deg);"
|
|
68
|
+
transition:scale={{
|
|
69
|
+
duration: 200,
|
|
70
|
+
start: 0.6,
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
<!-- Gradient lines -->
|
|
74
|
+
<div
|
|
75
|
+
class="absolute right-1/2 -bottom-px z-30 me-1 h-px w-2/5 translate-x-1/2 bg-gradient-to-r from-transparent via-emerald-500 to-transparent"
|
|
76
|
+
></div>
|
|
77
|
+
<div
|
|
78
|
+
class="absolute -bottom-px left-1/2 z-30 ms-1 h-px w-2/5 -translate-x-1/2 bg-gradient-to-r from-transparent via-sky-500 to-transparent"
|
|
79
|
+
></div>
|
|
80
|
+
|
|
81
|
+
<!-- Content -->
|
|
82
|
+
<div class="relative z-30 text-base font-bold text-white">
|
|
83
|
+
{item.name}
|
|
84
|
+
</div>
|
|
85
|
+
<div class="text-xs text-white">{item.designation}</div>
|
|
86
|
+
</div>
|
|
87
|
+
{/if}
|
|
88
|
+
|
|
89
|
+
<!-- Avatar Image -->
|
|
90
|
+
<img
|
|
91
|
+
src={item.image}
|
|
92
|
+
alt={item.name}
|
|
93
|
+
class="relative !m-0 size-14 rounded-full border-2 border-white object-cover object-top !p-0 transition duration-500 group-hover:z-30 group-hover:scale-105"
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
{/each}
|
|
97
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface TooltipItem {
|
|
2
|
+
id: number | string;
|
|
3
|
+
name: string;
|
|
4
|
+
designation: string;
|
|
5
|
+
image: string;
|
|
6
|
+
}
|
|
7
|
+
export interface AnimatedTooltipProps {
|
|
8
|
+
/** Array of items to display */
|
|
9
|
+
items: TooltipItem[];
|
|
10
|
+
/** Additional CSS classes for the container */
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
declare const AnimatedTooltip: import("svelte").Component<AnimatedTooltipProps, {}, "">;
|
|
14
|
+
type AnimatedTooltip = ReturnType<typeof AnimatedTooltip>;
|
|
15
|
+
export default AnimatedTooltip;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { render, cleanup } from "@testing-library/svelte";
|
|
2
|
+
import { afterEach, describe, it, expect } from "vitest";
|
|
3
|
+
import AnimatedTooltip from "./AnimatedTooltip.svelte";
|
|
4
|
+
const mockItems = [
|
|
5
|
+
{ id: 1, name: "Alice", designation: "Engineer", image: "/alice.jpg" },
|
|
6
|
+
{ id: 2, name: "Bob", designation: "Designer", image: "/bob.jpg" },
|
|
7
|
+
];
|
|
8
|
+
describe("AnimatedTooltip", () => {
|
|
9
|
+
afterEach(cleanup);
|
|
10
|
+
it("renders one avatar image per item", () => {
|
|
11
|
+
const { container } = render(AnimatedTooltip, {
|
|
12
|
+
props: { items: mockItems },
|
|
13
|
+
});
|
|
14
|
+
const imgs = container.querySelectorAll("img");
|
|
15
|
+
expect(imgs.length).toBe(2);
|
|
16
|
+
});
|
|
17
|
+
it("sets correct alt text on images", () => {
|
|
18
|
+
const { container } = render(AnimatedTooltip, {
|
|
19
|
+
props: { items: mockItems },
|
|
20
|
+
});
|
|
21
|
+
const imgs = container.querySelectorAll("img");
|
|
22
|
+
expect(imgs[0]).toHaveAttribute("alt", "Alice");
|
|
23
|
+
expect(imgs[1]).toHaveAttribute("alt", "Bob");
|
|
24
|
+
});
|
|
25
|
+
it("sets correct src on images", () => {
|
|
26
|
+
const { container } = render(AnimatedTooltip, {
|
|
27
|
+
props: { items: mockItems },
|
|
28
|
+
});
|
|
29
|
+
const imgs = container.querySelectorAll("img");
|
|
30
|
+
expect(imgs[0]).toHaveAttribute("src", "/alice.jpg");
|
|
31
|
+
expect(imgs[1]).toHaveAttribute("src", "/bob.jpg");
|
|
32
|
+
});
|
|
33
|
+
it("renders a flex container", () => {
|
|
34
|
+
const { container } = render(AnimatedTooltip, {
|
|
35
|
+
props: { items: mockItems },
|
|
36
|
+
});
|
|
37
|
+
const wrapper = container.firstElementChild;
|
|
38
|
+
expect(wrapper?.className).toContain("flex");
|
|
39
|
+
});
|
|
40
|
+
it("applies custom class names", () => {
|
|
41
|
+
const { container } = render(AnimatedTooltip, {
|
|
42
|
+
props: { items: mockItems, class: "custom" },
|
|
43
|
+
});
|
|
44
|
+
const wrapper = container.firstElementChild;
|
|
45
|
+
expect(wrapper?.className).toContain("custom");
|
|
46
|
+
});
|
|
47
|
+
it("renders empty when items is empty", () => {
|
|
48
|
+
const { container } = render(AnimatedTooltip, { props: { items: [] } });
|
|
49
|
+
const imgs = container.querySelectorAll("img");
|
|
50
|
+
expect(imgs.length).toBe(0);
|
|
51
|
+
});
|
|
52
|
+
it("each item wrapper has group class", () => {
|
|
53
|
+
const { container } = render(AnimatedTooltip, {
|
|
54
|
+
props: { items: mockItems },
|
|
55
|
+
});
|
|
56
|
+
const groups = container.querySelectorAll(".group");
|
|
57
|
+
expect(groups.length).toBe(2);
|
|
58
|
+
});
|
|
59
|
+
it("images have rounded-full class", () => {
|
|
60
|
+
const { container } = render(AnimatedTooltip, {
|
|
61
|
+
props: { items: mockItems },
|
|
62
|
+
});
|
|
63
|
+
const imgs = container.querySelectorAll("img");
|
|
64
|
+
expect(imgs[0]?.className).toContain("rounded-full");
|
|
65
|
+
expect(imgs[1]?.className).toContain("rounded-full");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as AnimatedTooltip } from "./AnimatedTooltip.svelte";
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
|
|
4
|
+
export interface AppleCardData {
|
|
5
|
+
/** Category label shown above the title */
|
|
6
|
+
category: string;
|
|
7
|
+
/** Main card title */
|
|
8
|
+
title: string;
|
|
9
|
+
/** URL of the card background image */
|
|
10
|
+
src: string;
|
|
11
|
+
/** Short description shown in the expanded view */
|
|
12
|
+
description?: string;
|
|
13
|
+
/** Rich content shown in the expanded view (takes precedence over description) */
|
|
14
|
+
content?: Snippet;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Shared transition duration — must stay in sync with the CSS transition below */
|
|
18
|
+
export const TRANSITION_MS = 400;
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<script lang="ts">
|
|
22
|
+
import { tick } from "svelte";
|
|
23
|
+
import { cn } from "../../utils.js";
|
|
24
|
+
|
|
25
|
+
interface Props {
|
|
26
|
+
card: AppleCardData;
|
|
27
|
+
index: number;
|
|
28
|
+
expandedIndex: number;
|
|
29
|
+
reducedMotion: boolean;
|
|
30
|
+
onExpand: (index: number) => void;
|
|
31
|
+
onCollapse: () => void;
|
|
32
|
+
class?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let {
|
|
36
|
+
card,
|
|
37
|
+
index,
|
|
38
|
+
expandedIndex,
|
|
39
|
+
reducedMotion,
|
|
40
|
+
onExpand,
|
|
41
|
+
onCollapse,
|
|
42
|
+
class: className = "",
|
|
43
|
+
}: Props = $props();
|
|
44
|
+
|
|
45
|
+
let cardEl: HTMLDivElement;
|
|
46
|
+
let dialogEl: HTMLDivElement;
|
|
47
|
+
let closeBtn: HTMLButtonElement;
|
|
48
|
+
let rect = $state<{ top: number; left: number; width: number; height: number } | null>(null);
|
|
49
|
+
let overlayVisible = $state(false);
|
|
50
|
+
let fullyExpanded = $state(false);
|
|
51
|
+
let previousFocus: HTMLElement | null = null;
|
|
52
|
+
|
|
53
|
+
async function handleExpand() {
|
|
54
|
+
if (expandedIndex !== -1) return;
|
|
55
|
+
previousFocus = document.activeElement as HTMLElement;
|
|
56
|
+
const r = cardEl.getBoundingClientRect();
|
|
57
|
+
rect = { top: r.top, left: r.left, width: r.width, height: r.height };
|
|
58
|
+
overlayVisible = true;
|
|
59
|
+
onExpand(index);
|
|
60
|
+
|
|
61
|
+
// Wait for the overlay to be in the DOM before focusing
|
|
62
|
+
await tick();
|
|
63
|
+
closeBtn?.focus();
|
|
64
|
+
|
|
65
|
+
if (reducedMotion) {
|
|
66
|
+
fullyExpanded = true;
|
|
67
|
+
} else {
|
|
68
|
+
requestAnimationFrame(() => {
|
|
69
|
+
requestAnimationFrame(() => {
|
|
70
|
+
fullyExpanded = true;
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function handleCollapse() {
|
|
77
|
+
fullyExpanded = false;
|
|
78
|
+
const delay = reducedMotion ? 0 : TRANSITION_MS;
|
|
79
|
+
setTimeout(() => {
|
|
80
|
+
overlayVisible = false;
|
|
81
|
+
onCollapse();
|
|
82
|
+
rect = null;
|
|
83
|
+
previousFocus?.focus();
|
|
84
|
+
previousFocus = null;
|
|
85
|
+
}, delay);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function handleCardKeydown(e: KeyboardEvent) {
|
|
89
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
90
|
+
e.preventDefault();
|
|
91
|
+
handleExpand();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function handleOverlayKeydown(e: KeyboardEvent) {
|
|
96
|
+
if (e.key === "Escape") {
|
|
97
|
+
handleCollapse();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// Simple focus trap: cycle focus within the dialog on Tab
|
|
101
|
+
if (e.key === "Tab" && dialogEl) {
|
|
102
|
+
const focusable = Array.from(
|
|
103
|
+
dialogEl.querySelectorAll<HTMLElement>(
|
|
104
|
+
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
105
|
+
)
|
|
106
|
+
);
|
|
107
|
+
if (focusable.length === 0) return;
|
|
108
|
+
const first = focusable[0];
|
|
109
|
+
const last = focusable[focusable.length - 1];
|
|
110
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
last.focus();
|
|
113
|
+
} else if (!e.shiftKey && document.activeElement === last) {
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
first.focus();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
<!-- Collapsed card in the carousel -->
|
|
122
|
+
<div
|
|
123
|
+
bind:this={cardEl}
|
|
124
|
+
class={cn(
|
|
125
|
+
"relative h-80 w-56 shrink-0 cursor-pointer snap-start overflow-hidden rounded-3xl md:h-96 md:w-72",
|
|
126
|
+
className
|
|
127
|
+
)}
|
|
128
|
+
role="button"
|
|
129
|
+
tabindex="0"
|
|
130
|
+
aria-label="Open {card.title}"
|
|
131
|
+
onclick={handleExpand}
|
|
132
|
+
onkeydown={handleCardKeydown}
|
|
133
|
+
>
|
|
134
|
+
<img
|
|
135
|
+
src={card.src}
|
|
136
|
+
alt={card.title}
|
|
137
|
+
class="absolute inset-0 h-full w-full object-cover transition-transform duration-500 hover:scale-105"
|
|
138
|
+
draggable="false"
|
|
139
|
+
/>
|
|
140
|
+
<div class="absolute inset-0 bg-gradient-to-t from-black/60 via-black/10 to-transparent"></div>
|
|
141
|
+
<div class="absolute bottom-0 left-0 p-5">
|
|
142
|
+
<p class="mb-1 text-xs font-semibold tracking-widest text-white/70 uppercase">
|
|
143
|
+
{card.category}
|
|
144
|
+
</p>
|
|
145
|
+
<h3 class="text-base font-semibold text-white md:text-lg">{card.title}</h3>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<!-- Expanded overlay -->
|
|
150
|
+
{#if overlayVisible && rect !== null}
|
|
151
|
+
<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
|
|
152
|
+
<div
|
|
153
|
+
class="fixed inset-0 z-40 bg-black/80"
|
|
154
|
+
aria-hidden="true"
|
|
155
|
+
style="opacity: {fullyExpanded ? 1 : 0}; transition: opacity {reducedMotion
|
|
156
|
+
? 0
|
|
157
|
+
: TRANSITION_MS}ms ease;"
|
|
158
|
+
onclick={handleCollapse}
|
|
159
|
+
></div>
|
|
160
|
+
|
|
161
|
+
<div
|
|
162
|
+
bind:this={dialogEl}
|
|
163
|
+
role="dialog"
|
|
164
|
+
aria-modal="true"
|
|
165
|
+
aria-label={card.title}
|
|
166
|
+
tabindex="-1"
|
|
167
|
+
class="fixed z-50 overflow-y-auto bg-white dark:bg-neutral-900"
|
|
168
|
+
style="
|
|
169
|
+
top: {fullyExpanded ? '0px' : rect.top + 'px'};
|
|
170
|
+
left: {fullyExpanded ? '0px' : rect.left + 'px'};
|
|
171
|
+
width: {fullyExpanded ? '100vw' : rect.width + 'px'};
|
|
172
|
+
height: {fullyExpanded ? '100vh' : rect.height + 'px'};
|
|
173
|
+
border-radius: {fullyExpanded ? '0px' : '1.5rem'};
|
|
174
|
+
transition: {reducedMotion
|
|
175
|
+
? 'none'
|
|
176
|
+
: `top ${TRANSITION_MS}ms cubic-bezier(0.32,0.72,0,1), left ${TRANSITION_MS}ms cubic-bezier(0.32,0.72,0,1), width ${TRANSITION_MS}ms cubic-bezier(0.32,0.72,0,1), height ${TRANSITION_MS}ms cubic-bezier(0.32,0.72,0,1), border-radius ${TRANSITION_MS}ms cubic-bezier(0.32,0.72,0,1)`};
|
|
177
|
+
"
|
|
178
|
+
onkeydown={handleOverlayKeydown}
|
|
179
|
+
>
|
|
180
|
+
<!-- Close button -->
|
|
181
|
+
<button
|
|
182
|
+
bind:this={closeBtn}
|
|
183
|
+
class="absolute top-4 right-4 z-10 flex h-10 w-10 items-center justify-center rounded-full bg-black/20 backdrop-blur-sm transition-colors hover:bg-black/30"
|
|
184
|
+
aria-label="Close"
|
|
185
|
+
onclick={handleCollapse}
|
|
186
|
+
>
|
|
187
|
+
<svg
|
|
188
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
189
|
+
width="18"
|
|
190
|
+
height="18"
|
|
191
|
+
viewBox="0 0 24 24"
|
|
192
|
+
fill="none"
|
|
193
|
+
stroke="white"
|
|
194
|
+
stroke-width="2.5"
|
|
195
|
+
stroke-linecap="round"
|
|
196
|
+
stroke-linejoin="round"
|
|
197
|
+
>
|
|
198
|
+
<path d="M18 6 6 18" />
|
|
199
|
+
<path d="m6 6 12 12" />
|
|
200
|
+
</svg>
|
|
201
|
+
</button>
|
|
202
|
+
|
|
203
|
+
<!-- Hero image -->
|
|
204
|
+
<div class="relative h-72 w-full shrink-0 md:h-96">
|
|
205
|
+
<img src={card.src} alt={card.title} class="h-full w-full object-cover" draggable="false" />
|
|
206
|
+
<div
|
|
207
|
+
class="absolute inset-0 bg-gradient-to-t from-black/60 via-black/10 to-transparent"
|
|
208
|
+
></div>
|
|
209
|
+
<div class="absolute bottom-0 left-0 p-6 md:p-8">
|
|
210
|
+
<p class="mb-2 text-xs font-semibold tracking-widest text-white/70 uppercase">
|
|
211
|
+
{card.category}
|
|
212
|
+
</p>
|
|
213
|
+
<h2 class="text-2xl font-bold text-white md:text-3xl">{card.title}</h2>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
<!-- Content area -->
|
|
218
|
+
<div class="p-6 md:p-8">
|
|
219
|
+
{#if card.content}
|
|
220
|
+
{@render card.content()}
|
|
221
|
+
{:else if card.description}
|
|
222
|
+
<p class="leading-relaxed text-gray-600 dark:text-neutral-400">{card.description}</p>
|
|
223
|
+
{/if}
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
{/if}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
export interface AppleCardData {
|
|
3
|
+
/** Category label shown above the title */
|
|
4
|
+
category: string;
|
|
5
|
+
/** Main card title */
|
|
6
|
+
title: string;
|
|
7
|
+
/** URL of the card background image */
|
|
8
|
+
src: string;
|
|
9
|
+
/** Short description shown in the expanded view */
|
|
10
|
+
description?: string;
|
|
11
|
+
/** Rich content shown in the expanded view (takes precedence over description) */
|
|
12
|
+
content?: Snippet;
|
|
13
|
+
}
|
|
14
|
+
/** Shared transition duration — must stay in sync with the CSS transition below */
|
|
15
|
+
export declare const TRANSITION_MS = 400;
|
|
16
|
+
interface Props {
|
|
17
|
+
card: AppleCardData;
|
|
18
|
+
index: number;
|
|
19
|
+
expandedIndex: number;
|
|
20
|
+
reducedMotion: boolean;
|
|
21
|
+
onExpand: (index: number) => void;
|
|
22
|
+
onCollapse: () => void;
|
|
23
|
+
class?: string;
|
|
24
|
+
}
|
|
25
|
+
declare const AppleCard: import("svelte").Component<Props, {}, "">;
|
|
26
|
+
type AppleCard = ReturnType<typeof AppleCard>;
|
|
27
|
+
export default AppleCard;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { AppleCardData } from "./AppleCard.svelte";
|
|
3
|
+
export type { AppleCardData };
|
|
4
|
+
|
|
5
|
+
export interface AppleCardCarouselProps {
|
|
6
|
+
/** Cards to display in the carousel */
|
|
7
|
+
cards: AppleCardData[];
|
|
8
|
+
/** Additional CSS classes */
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<script lang="ts">
|
|
14
|
+
import { onMount } from "svelte";
|
|
15
|
+
import { cn } from "../../utils.js";
|
|
16
|
+
import AppleCard from "./AppleCard.svelte";
|
|
17
|
+
|
|
18
|
+
let { cards, class: className = "" }: AppleCardCarouselProps = $props();
|
|
19
|
+
|
|
20
|
+
let expandedIndex = $state(-1);
|
|
21
|
+
let reducedMotion = $state(false);
|
|
22
|
+
|
|
23
|
+
onMount(() => {
|
|
24
|
+
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
25
|
+
reducedMotion = mq.matches;
|
|
26
|
+
const handler = (e: MediaQueryListEvent) => {
|
|
27
|
+
reducedMotion = e.matches;
|
|
28
|
+
};
|
|
29
|
+
mq.addEventListener("change", handler);
|
|
30
|
+
return () => mq.removeEventListener("change", handler);
|
|
31
|
+
});
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<div class={cn("relative w-full", className)}>
|
|
35
|
+
<div
|
|
36
|
+
class="flex snap-x snap-mandatory gap-4 overflow-x-auto px-4 pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
|
37
|
+
>
|
|
38
|
+
{#each cards as card, i}
|
|
39
|
+
<AppleCard
|
|
40
|
+
{card}
|
|
41
|
+
index={i}
|
|
42
|
+
{expandedIndex}
|
|
43
|
+
{reducedMotion}
|
|
44
|
+
onExpand={(idx) => (expandedIndex = idx)}
|
|
45
|
+
onCollapse={() => (expandedIndex = -1)}
|
|
46
|
+
/>
|
|
47
|
+
{/each}
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AppleCardData } from "./AppleCard.svelte";
|
|
2
|
+
export type { AppleCardData };
|
|
3
|
+
export interface AppleCardCarouselProps {
|
|
4
|
+
/** Cards to display in the carousel */
|
|
5
|
+
cards: AppleCardData[];
|
|
6
|
+
/** Additional CSS classes */
|
|
7
|
+
class?: string;
|
|
8
|
+
}
|
|
9
|
+
declare const AppleCardCarousel: import("svelte").Component<AppleCardCarouselProps, {}, "">;
|
|
10
|
+
type AppleCardCarousel = ReturnType<typeof AppleCardCarousel>;
|
|
11
|
+
export default AppleCardCarousel;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { render, cleanup, fireEvent } from "@testing-library/svelte";
|
|
2
|
+
import { afterEach, beforeEach, describe, it, expect, vi } from "vitest";
|
|
3
|
+
import { flushSync } from "svelte";
|
|
4
|
+
import AppleCardCarousel from "./AppleCardCarousel.svelte";
|
|
5
|
+
const cards = [
|
|
6
|
+
{ category: "Nature", title: "Mountains", src: "mountains.jpg", description: "High peaks." },
|
|
7
|
+
{ category: "City", title: "Skyline", src: "skyline.jpg", description: "Urban lights." },
|
|
8
|
+
{ category: "Ocean", title: "Waves", src: "waves.jpg", description: "Deep blue." },
|
|
9
|
+
];
|
|
10
|
+
describe("AppleCardCarousel", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
vi.useFakeTimers();
|
|
13
|
+
});
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
vi.useRealTimers();
|
|
16
|
+
cleanup();
|
|
17
|
+
});
|
|
18
|
+
it("renders all cards", () => {
|
|
19
|
+
const { getByText } = render(AppleCardCarousel, { props: { cards } });
|
|
20
|
+
expect(getByText("Mountains")).toBeTruthy();
|
|
21
|
+
expect(getByText("Skyline")).toBeTruthy();
|
|
22
|
+
expect(getByText("Waves")).toBeTruthy();
|
|
23
|
+
});
|
|
24
|
+
it("renders gracefully with no cards", () => {
|
|
25
|
+
const { container } = render(AppleCardCarousel, { props: { cards: [] } });
|
|
26
|
+
expect(container.querySelector("[role='button']")).toBeNull();
|
|
27
|
+
});
|
|
28
|
+
it("clicking a card shows the expanded dialog", () => {
|
|
29
|
+
const { getByLabelText } = render(AppleCardCarousel, { props: { cards } });
|
|
30
|
+
fireEvent.click(getByLabelText("Open Mountains"));
|
|
31
|
+
flushSync();
|
|
32
|
+
expect(getByLabelText("Mountains")).toBeTruthy();
|
|
33
|
+
});
|
|
34
|
+
it("expanded dialog has correct ARIA attributes", () => {
|
|
35
|
+
const { getByLabelText, getByRole } = render(AppleCardCarousel, { props: { cards } });
|
|
36
|
+
fireEvent.click(getByLabelText("Open Mountains"));
|
|
37
|
+
flushSync();
|
|
38
|
+
const dialog = getByRole("dialog");
|
|
39
|
+
expect(dialog).toBeTruthy();
|
|
40
|
+
expect(dialog.getAttribute("aria-modal")).toBe("true");
|
|
41
|
+
});
|
|
42
|
+
it("clicking the close button collapses the card", () => {
|
|
43
|
+
const { getByLabelText, queryByRole } = render(AppleCardCarousel, { props: { cards } });
|
|
44
|
+
fireEvent.click(getByLabelText("Open Mountains"));
|
|
45
|
+
flushSync();
|
|
46
|
+
fireEvent.click(getByLabelText("Close"));
|
|
47
|
+
flushSync(() => vi.advanceTimersByTime(400));
|
|
48
|
+
expect(queryByRole("dialog")).toBeNull();
|
|
49
|
+
});
|
|
50
|
+
it("pressing Escape collapses the card", () => {
|
|
51
|
+
const { getByLabelText, queryByRole } = render(AppleCardCarousel, { props: { cards } });
|
|
52
|
+
fireEvent.click(getByLabelText("Open Mountains"));
|
|
53
|
+
flushSync();
|
|
54
|
+
const dialog = getByLabelText("Mountains");
|
|
55
|
+
fireEvent.keyDown(dialog, { key: "Escape" });
|
|
56
|
+
flushSync(() => vi.advanceTimersByTime(400));
|
|
57
|
+
expect(queryByRole("dialog")).toBeNull();
|
|
58
|
+
});
|
|
59
|
+
it("only one card can be expanded at a time", () => {
|
|
60
|
+
const { getByLabelText, getAllByRole } = render(AppleCardCarousel, { props: { cards } });
|
|
61
|
+
fireEvent.click(getByLabelText("Open Mountains"));
|
|
62
|
+
flushSync();
|
|
63
|
+
expect(getAllByRole("dialog")).toHaveLength(1);
|
|
64
|
+
// Clicking another card while one is open should be a no-op
|
|
65
|
+
fireEvent.click(getByLabelText("Open Skyline"));
|
|
66
|
+
flushSync();
|
|
67
|
+
expect(getAllByRole("dialog")).toHaveLength(1);
|
|
68
|
+
});
|
|
69
|
+
it("clicking the backdrop collapses the card", () => {
|
|
70
|
+
const { getByLabelText, queryByRole } = render(AppleCardCarousel, { props: { cards } });
|
|
71
|
+
fireEvent.click(getByLabelText("Open Mountains"));
|
|
72
|
+
flushSync();
|
|
73
|
+
// The backdrop has aria-hidden="true"; target it via its class
|
|
74
|
+
const backdrop = document.querySelector(".fixed.z-40");
|
|
75
|
+
fireEvent.click(backdrop);
|
|
76
|
+
flushSync(() => vi.advanceTimersByTime(400));
|
|
77
|
+
expect(queryByRole("dialog")).toBeNull();
|
|
78
|
+
});
|
|
79
|
+
it("shows description text in the expanded view", () => {
|
|
80
|
+
const { getByLabelText, getByText } = render(AppleCardCarousel, { props: { cards } });
|
|
81
|
+
fireEvent.click(getByLabelText("Open Mountains"));
|
|
82
|
+
flushSync();
|
|
83
|
+
expect(getByText("High peaks.")).toBeTruthy();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from "../../utils";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
class?: string;
|
|
6
|
+
children?: import("svelte").Snippet;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let { class: className = "", children }: Props = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<div
|
|
13
|
+
class={cn(
|
|
14
|
+
"mx-auto grid max-w-7xl grid-cols-1 gap-4 md:auto-rows-[18rem] md:grid-cols-3",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
>
|
|
18
|
+
{#if children}
|
|
19
|
+
{@render children()}
|
|
20
|
+
{/if}
|
|
21
|
+
</div>
|