Codrops 원본
From Flat to Spatial: Creating a 3D Product Grid with React Three Fiber
섹션 · MIT
갤러리 안에서는 화면을 벗어나거나 움직임을 멈추면 예제도 닫힙니다. 다시 재생할 때는 처음부터 시작해요.
SOURCE FILES
원본 코드 읽기
수집한 원본 소스와 실행 안내를 함께 제공합니다.
backend/shoes.json
[
{
"title": "Nike Dunk Low 'Black White'",
"price": "$69",
"image_url": "/shoes/shoe-000.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-black-white-dd1391-100",
"brand": "Nike",
"primary_color": "dark_gray",
"primary_color_hex": "#434344"
},
{
"title": "Travis Scott x Playstation x Nike Dunk Low",
"price": null,
"image_url": "/shoes/shoe-001.png",
"product_url": "https://www.goat.com/sneakers/travis-scott-x-playstation-x-dunk-low-travis-ps-dunk",
"brand": "Nike",
"primary_color": "dark_gray",
"primary_color_hex": "#5b5451"
},
{
"title": "The Powerpuff Girls x Nike Dunk Low Pro SB QS PS 'Bubbles'",
"price": "$179",
"image_url": "/shoes/shoe-002.png",
"product_url": "https://www.goat.com/sneakers/the-powerpuff-girls-x-dunk-low-pro-sb-qs-ps-bubbles-fz8833-400",
"brand": "Nike",
"primary_color": "yellow",
"primary_color_hex": "#d3b61b"
},
{
"title": "Concepts x Nike Dunk Low SB 'Orange Lobster'",
"price": "$307",
"image_url": "/shoes/shoe-003.png",
"product_url": "https://www.goat.com/sneakers/concepts-x-dunk-low-sb-orange-lobster-bv1310-orange",
"brand": "Nike",
"primary_color": "orange",
"primary_color_hex": "#ec6f3f"
},
{
"title": "Undefeated x Nike Dunk Low 'Dunk vs AF1'",
"price": "$199",
"image_url": "/shoes/shoe-004.png",
"product_url": "https://www.goat.com/sneakers/undefeated-x-dunk-low-dunk-vs-af1-do9329-001",
"brand": "Nike",
"primary_color": "blue",
"primary_color_hex": "#2f3c5e"
},
{
"title": "Nike Dunk Low 'Grey Fog'",
"price": "$99",
"image_url": "/shoes/shoe-005.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-grey-fog-dd1391-103",
"brand": "Nike",
"primary_color": "light_gray",
"primary_color_hex": "#bcbcbc"
},
{
"title": "Nike Dunk Low LTD 'Wizard'",
"price": "$82",
"image_url": "/shoes/shoe-006.png",
"product_url": "https://www.goat.com/sneakers/nike-dunk-low-ltd-wizard-ib2267-001",
"brand": "Nike",
"primary_color": "purple",
"primary_color_hex": "#372946"
},
{
"title": "Stranger Things x Nike Dunk Low 'Phantom'",
"price": "$243",
"image_url": "/shoes/shoe-007.png",
"product_url": "https://www.goat.com/sneakers/stranger-things-x-dunk-low-phantom-ih6766-001",
"brand": "Nike",
"primary_color": "orange",
"primary_color_hex": "#c4a484"
},
{
"title": "Nike Dunk Low SE 'Triple Black'",
"price": "$116",
"image_url": "/shoes/shoe-008.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-se-triple-black-ib6651-001",
"brand": "Nike",
"primary_color": "gray",
"primary_color_hex": "#707579"
},
{
"title": "Ben & Jerry's x Nike Dunk Low SB 'Chunky Dunky'",
"price": "$1,188",
"image_url": "/shoes/shoe-009.png",
"product_url": "https://www.goat.com/sneakers/ben-jerry-s-x-dunk-low-sb-chunky-dunky-cu3244-100",
"brand": "Nike",
"primary_color": "orange",
"primary_color_hex": "#fbb807"
},
{
"title": "Cactus Plant Flea Market x Nike Dunk Low 'Swamp Sponge Pack - Psychic Purple'",
"price": "$227",
"image_url": "/shoes/shoe-010.png",
"product_url": "https://www.goat.com/sneakers/cactus-plant-flea-market-x-dunk-low-swamp-sponge-pack-psychic-purple-ih5094-500",
"brand": "Nike",
"primary_color": "orange",
"primary_color_hex": "#f4a105"
},
{
"title": "Nike Dunk Low 'Frankenstein'",
"price": "$109",
"image_url": "/shoes/shoe-011.png",
"product_url": "https://www.goat.com/sneakers/nike-dunk-low-frankenstein-hv4452-300",
"brand": "Nike",
"primary_color": "orange",
"primary_color_hex": "#5b4f30"
},
{
"title": "Nike Dunk Low Pro SB 'Freddy Krueger'",
"price": "$25,059",
"image_url": "/shoes/shoe-012.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-pro-sb-freddy-krueger-313170-202",
"brand": "Nike",
"primary_color": "orange",
"primary_color_hex": "#dcccae"
},
{
"title": "League of Legends x Nike Dunk Low",
"price": "$159",
"image_url": "/shoes/shoe-013.png",
"product_url": "https://www.goat.com/sneakers/league-of-legends-x-dunk-low-black-do2327-011",
"brand": "Nike",
"primary_color": "light_gray",
"primary_color_hex": "#acb8af"
},
{
"title": "Nike Dunk Low Premium 'Setsubun'",
"price": "$98",
"image_url": "/shoes/shoe-014.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-premium-setsubun-dq5009-268",
"brand": "Nike",
"primary_color": "orange",
"primary_color_hex": "#9c8566"
},
{
"title": "Futura Laboratories x Nike Dunk Low SB 'Bleached Aqua'",
"price": "$269",
"image_url": "/shoes/shoe-015.png",
"product_url": "https://www.goat.com/sneakers/futura-laboratories-x-dunk-low-sb-bleached-aqua-hf6061-400",
"brand": "Nike",
"primary_color": "light_gray",
"primary_color_hex": "#bcbcc3"
},
{
"title": "Nike Dunk Low 'Athletic Department - Deep Jungle'",
"price": "$66",
"image_url": "/shoes/shoe-016.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-athletic-department-deep-jungle-fq8080-133",
"brand": "Nike",
"primary_color": "dark_gray",
"primary_color_hex": "#384243"
},
{
"title": "Nike Dunk Low 'Midnight Navy Smoke Grey'",
"price": "$103",
"image_url": "/shoes/shoe-017.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-midnight-navy-smoke-grey-fd9749-400",
"brand": "Nike",
"primary_color": "blue",
"primary_color_hex": "#31405e"
},
{
"title": "Nike Dunk Low GS 'Year of the Dragon'",
"price": "$99",
"image_url": "/shoes/shoe-018.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-gs-year-of-the-dragon-fz5528-101",
"brand": "Nike",
"primary_color": "red",
"primary_color_hex": "#c93d3b"
},
{
"title": "Nike Dunk Low 'Silver Surfer' 2024",
"price": "$39",
"image_url": "/shoes/shoe-019.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-silver-surfer-2024-hf0391-001",
"brand": "Nike",
"primary_color": "gray",
"primary_color_hex": "#808388"
},
{
"title": "Nike Dunk Low 'Year of the Snake'",
"price": "$143",
"image_url": "/shoes/shoe-020.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-year-of-the-snake-hv5980-231",
"brand": "Nike",
"primary_color": "gray",
"primary_color_hex": "#797d6e"
},
{
"title": "Nike Dunk Low 'Reverse Panda'",
"price": "$109",
"image_url": "/shoes/shoe-021.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-reverse-panda-dj6188-101",
"brand": "Nike",
"primary_color": "gray",
"primary_color_hex": "#8a8888"
},
{
"title": "Off-White x Nike Rubber Dunk TD 'Green Strike'",
"price": "$145",
"image_url": "/shoes/shoe-022.png",
"product_url": "https://www.goat.com/sneakers/off-white-x-rubber-dunk-td-green-strike-ow-dunk-td-grn",
"brand": "Nike",
"primary_color": "green",
"primary_color_hex": "#7ae27e"
},
{
"title": "Nike Dunk Low Next Nature 'Baroque Brown'",
"price": "$138",
"image_url": "/shoes/shoe-023.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-next-nature-cacao-wow-hf4292-200",
"brand": "Nike",
"primary_color": "orange",
"primary_color_hex": "#d4ccbb"
},
{
"title": "The Powerpuff Girls x Nike Dunk Low Pro SB QS 'Buttercup'",
"price": "$274",
"image_url": "/shoes/shoe-024.png",
"product_url": "https://www.goat.com/sneakers/the-powerpuff-girls-x-dunk-low-pro-sb-qs-buttercup-fz8319-300",
"brand": "Nike",
"primary_color": "green",
"primary_color_hex": "#6fa94b"
},
{
"title": "Nike Dunk Low ‘What the Duck - University of Oregon Alternate’ PE",
"price": "$300",
"image_url": "/shoes/shoe-025.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-what-the-phk-university-of-oregon-pe-hv1470-001",
"brand": "Nike",
"primary_color": "dark_gray",
"primary_color_hex": "#3a3c42"
},
{
"title": "Cactus Plant Flea Market x Nike Dunk Low 'Swamp Sponge Pack - Photo Blue'",
"price": "$249",
"image_url": "/shoes/shoe-026.png",
"product_url": "https://www.goat.com/sneakers/cactus-plant-flea-market-x-dunk-low-swamp-sponge-pack-photo-blue-ih5094-400",
"brand": "Nike",
"primary_color": "blue",
"primary_color_hex": "#0472be"
},
{
"title": "Verdy x Nike Dunk Low SB 'Visty'",
"price": "$239",
"image_url": "/shoes/shoe-027.png",
"product_url": "https://www.goat.com/sneakers/verdy-x-dunk-low-sb-visty-fn6040-400",
"brand": "Nike",
"primary_color": "green",
"primary_color_hex": "#bad1c5"
},
{
"title": "Nike Dunk Low 'Kentucky' 2025",
"price": "$105",
"image_url": "/shoes/shoe-028.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-kentucky-2025-hf5441-112",
"brand": "Nike",
"primary_color": "blue",
"primary_color_hex": "#043182"
},
{
"title": "Union LA x Nike Dunk Low 'Passport Pack - Argon'",
"price": "$165",
"image_url": "/shoes/shoe-029.png",
"product_url": "https://www.goat.com/sneakers/union-la-x-dunk-low-dj9649-400",
"brand": "Nike",
"primary_color": "blue",
"primary_color_hex": "#6e829a"
},
{
"title": "Nike Dunk Low 'Valerian Blue'",
"price": "$79",
"image_url": "/shoes/shoe-030.png",
"product_url": "https://www.goat.com/sneakers/dunk-low-usa-dd1391-400",
"brand": "Nike",
"primary_color": "blue",
"primary_color_hex": "#2c3d4c"
},
{
"title": "Nike Wmns Dunk Low 'Cacao Wow'",
"price": "$86",
"image_url": "/shoes/shoe-031.png",
"product_url": "https://www.goat.com/sneakers/wmns-dunk-low-cacao-wow-dd1503-124",
"brand": "Nike",
"primary_color": "yellow",
"primary_color_hex": "#d4d4bc"
},
{
"title": "Limosine Skateboards x Nike Dunk Low SB 'Football'",
"price": "$87",
"image_url": "/shoes/shoe-032.png",
"product_url": "https://www.goat.com/sneakers/limosine-skateboards-x-dunk-low-sb-basketball-leather-hj4131-200",
"brand": "Nike",
"primary_color": "red",
"primary_color_hex": "#371718"
},
{
"title": "Supreme x Nike Dunk Low SB 'Ocean Fog'",
"price": "$316",
"image_url": "/shoes/shoe-033.png",
"product_url": "https://www.goat.com/sneakers/supreme-x-dunk-low-sb-ocean-fog-hq8487-400",
"brand": "Nike",
"primary_color": "blue",
"primary_color_hex": "#3c4c58"
},
{
"title": "Nike Dunk Low SB 'Sandy Bodecker'",
"price": "$142",
"image_url": "/shoes/shoe-034.png",
"product_url": "https://www.goat.com/sneakers/ebay-x-dunk-low-sb-sandy-bodecker-fd8777-100",
"brand": "Nike",
"primary_color": "yellow",
"primary_color_hex": "#e6b211"
},
{
"title": "Jarritos x Nike Dunk Low SB",
"price": "$404",
"image_url": "/shoes/shoe-035.png",
"product_url": "https://www.goat.com/sneakers/jarritos-x-dunk-low-sb-fd0860-001",
"brand": "Nike",
"primary_color": "teal",
"primary_color_hex": "#1f735d"
},
{
"title": "Undefeated x Air Jordan 4 Retro 2025",
"price": "$262",
"image_url": "/shoes/shoe-036.png",
"product_url": "https://www.goat.com/sneakers/undefeated-x-air-jordan-4-retro-ib1519-200",
"brand": "Nike",
"primary_color": "yellow",
"primary_color_hex": "#838367"
},
{
"title": "Air Jordan 4 Retro 'Rare Air - White Lettering'",
"price": "$193",
"image_url": "/shoes/shoe-037.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-rare-air-fv5029-003",
"brand": "Nike",
"primary_color": "blue",
"primary_color_hex": "#19346f"
},
{
"title": "Wmns Air Jordan 4 Retro TEX 'Worn Blue Denim'",
"price": "$153",
"image_url": "/shoes/shoe-038.png",
"product_url": "https://www.goat.com/sneakers/wmns-air-jordan-4-retro-tex-worn-blue-denim-ib6716-100",
"brand": "Nike",
"primary_color": "blue",
"primary_color_hex": "#315472"
},
{
"title": "Air Jordan 4 Retro 'Black Cat' 2025",
"price": "$277",
"image_url": "/shoes/shoe-039.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-black-cat-2025-fv5029-010",
"brand": "Nike",
"primary_color": "black",
"primary_color_hex": "#0c040b"
},
{
"title": "Air Jordan 4 Retro OG 'White Cement' 2025",
"price": "$232",
"image_url": "/shoes/shoe-040.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-og-white-cement-2025-fv5029-100",
"brand": "Nike",
"primary_color": "gray",
"primary_color_hex": "#8a898b"
},
{
"title": "Nike SB x Air Jordan 4 Retro SP 'Navy'",
"price": "$166",
"image_url": "/shoes/shoe-041.png",
"product_url": "https://www.goat.com/sneakers/nike-sb-x-air-jordan-4-retro-sp-navy-dr5415-100",
"brand": "Nike",
"primary_color": "orange",
"primary_color_hex": "#e3a97b"
},
{
"title": "Air Jordan 4 Retro 'Cave Stone'",
"price": "$179",
"image_url": "/shoes/shoe-042.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-cave-stone-fq8138-200",
"brand": "Nike",
"primary_color": "dark_gray",
"primary_color_hex": "#4c443f"
},
{
"title": "Nigel Sylvester x Air Jordan 4 Retro OG SP 'Brick By Brick'",
"price": "$304",
"image_url": "/shoes/shoe-043.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-og-sp-firewood-orange-hf4340-800",
"brand": "Nike",
"primary_color": "red",
"primary_color_hex": "#c53f3b"
},
{
"title": "Jan 17",
"price": "$243",
"image_url": "/shoes/shoe-044.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-flight-club-im4002-100",
"brand": "Nike",
"primary_color": "gray",
"primary_color_hex": "#858483"
},
{
"title": "Air Jordan 4 Retro 'White Thunder'",
"price": "$294",
"image_url": "/shoes/shoe-045.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-white-thunder-fq8138-001",
"brand": "Nike",
"primary_color": "dark_gray",
"primary_color_hex": "#414143"
},
{
"title": "Air Jordan 4 Retro 'Bred Reimagined'",
"price": "$244",
"image_url": "/shoes/shoe-046.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-bred-reimagined-fv5029-006",
"brand": "Nike",
"primary_color": "light_gray",
"primary_color_hex": "#b4b3bb"
},
{
"title": "Air Jordan 4 Retro 'Military Blue' 2024",
"price": "$214",
"image_url": "/shoes/shoe-047.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-military-blue-2024-fv5029-141",
"brand": "Nike",
"primary_color": "light_gray",
"primary_color_hex": "#bcbebf"
},
{
"title": "Air Jordan 4 Retro GS 'Black Cat' 2025",
"price": "$158",
"image_url": "/shoes/shoe-048.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-jordan-4-retro-gs-black-cat-2025-ib4171-010",
"brand": "Nike",
"primary_color": "dark_gray",
"primary_color_hex": "#444c4d"
},
{
"title": "A Ma Maniére x Air Jordan 4 Retro 'Dark Mocha'",
"price": "$269",
"image_url": "/shoes/shoe-049.png",
"product_url": "https://www.goat.com/sneakers/a-ma-maniere-x-air-jordan-4-retro-dark-mocha-if3102-200",
"brand": "Nike",
"primary_color": "orange",
"primary_color_hex": "#4c392f"
},
{
"title": "Air Jordan 4 Retro 'Red Thunder'",
"price": "$401",
"image_url": "/shoes/shoe-050.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-red-thunder-ct8527-016",
"brand": "Nike",
"primary_color": "red",
"primary_color_hex": "#e2262a"
},
{
"title": "Nike SB x Air Jordan 4 Retro SP 'Pine Green'",
"price": "$319",
"image_url": "/shoes/shoe-051.png",
"product_url": "https://www.goat.com/sneakers/nike-sb-x-air-jordan-4-retro-pine-green-dr5415-103",
"brand": "Nike",
"primary_color": "teal",
"primary_color_hex": "#07845c"
},
{
"title": "Air Jordan 4 Retro SE 'Wet Cement'",
"price": "$298",
"image_url": "/shoes/shoe-052.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-se-smoke-grey-fq7928-001",
"brand": "Nike",
"primary_color": "light_gray",
"primary_color_hex": "#b2b3b7"
},
{
"title": "Air Jordan 4 Retro OG 'Bred' 2019",
"price": "$389",
"image_url": "/shoes/shoe-053.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-bred-2019-308497-060",
"brand": "Nike",
"primary_color": "red",
"primary_color_hex": "#a95c60"
},
{
"title": "A Ma Maniére x Wmns Air Jordan 4 Retro 'While You Were Sleeping'",
"price": "$155",
"image_url": "/shoes/shoe-054.png",
"product_url": "https://www.goat.com/sneakers/a-ma-maniere-x-wmns-air-jordan-4-retro-fossil-stone-fz4810-200",
"brand": "Nike",
"primary_color": "orange",
"primary_color_hex": "#cdb9ac"
},
{
"title": "Air Jordan 4 Retro 'Fear' 2024",
"price": "$226",
"image_url": "/shoes/shoe-055.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-fear-2024-fq8138-002",
"brand": "Nike",
"primary_color": "gray",
"primary_color_hex": "#7b7a7a"
},
{
"title": "Air Jordan 4 Retro OG 'Fire Red' 2020",
"price": "$311",
"image_url": "/shoes/shoe-056.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-og-fire-red-2020-dc7770-160",
"brand": "Nike",
"primary_color": "red",
"primary_color_hex": "#b91817"
},
{
"title": "Wmns Air Jordan 4 Retro 'Orchid'",
"price": "$307",
"image_url": "/shoes/shoe-057.png",
"product_url": "https://www.goat.com/sneakers/wmns-air-jordan-4-retro-orchid-aq9129-501",
"brand": "Nike",
"primary_color": "pink",
"primary_color_hex": "#cca5bc"
},
{
"title": "Air Jordan 4 Retro 'Thunder' 2023",
"price": "$291",
"image_url": "/shoes/shoe-058.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-thunder-2023-dh6927-017",
"brand": "Nike",
"primary_color": "orange",
"primary_color_hex": "#494233"
},
{
"title": "Air Jordan 4 Retro SE 'Black Canvas'",
"price": "$333",
"image_url": "/shoes/shoe-059.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-black-canvas-dh7138-006",
"brand": "Nike",
"primary_color": "light_gray",
"primary_color_hex": "#bdc1c4"
},
{
"title": "Off-White x Wmns Air Jordan 4 Retro SP 'Sail'",
"price": "$743",
"image_url": "/shoes/shoe-060.png",
"product_url": "https://www.goat.com/sneakers/off-white-x-wmns-air-jordan-4-sp-sail-cv9388-100",
"brand": "Nike",
"primary_color": "orange",
"primary_color_hex": "#ccc4b4"
},
{
"title": "Air Jordan 4 Retro 'Toro Bravo' 2026",
"price": null,
"image_url": "/shoes/shoe-061.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-toro-bravo-2026-fq8138-600",
"brand": "Nike",
"primary_color": "red",
"primary_color_hex": "#a41d2b"
},
{
"title": "Air Jordan 4 Retro 'University Blue'",
"price": "$390",
"image_url": "/shoes/shoe-062.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-university-blue-ct8527-400",
"brand": "Nike",
"primary_color": "blue",
"primary_color_hex": "#557fa9"
},
{
"title": "Air Jordan 4 Retro 'Military Black'",
"price": "$335",
"image_url": "/shoes/shoe-063.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-military-black-dh6927-111",
"brand": "Nike",
"primary_color": "black",
"primary_color_hex": "#2e2c2c"
},
{
"title": "Air Jordan 4 Retro 'Lightning' 2021",
"price": "$250",
"image_url": "/shoes/shoe-064.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-lightning-2021-ct8527-700",
"brand": "Nike",
"primary_color": "yellow",
"primary_color_hex": "#e0b92e"
},
{
"title": "Air Jordan 4 Retro 'Red Cement'",
"price": "$218",
"image_url": "/shoes/shoe-065.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-red-cement-dh6927-161",
"brand": "Nike",
"primary_color": "red",
"primary_color_hex": "#af2530"
},
{
"title": "Air Jordan 4 Retro 'Midnight Navy'",
"price": "$281",
"image_url": "/shoes/shoe-066.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-midnight-navy-dh6927-140",
"brand": "Nike",
"primary_color": "blue",
"primary_color_hex": "#2e354c"
},
{
"title": "Air Jordan 4 Retro '25th Silver Anniversary'",
"price": "$135",
"image_url": "/shoes/shoe-067.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-silver-anniversary-408202-101",
"brand": "Nike",
"primary_color": "light_gray",
"primary_color_hex": "#c5c2ca"
},
{
"title": "A Ma Maniére x Air Jordan 4 Retro 'Violet Ore'",
"price": "$198",
"image_url": "/shoes/shoe-068.png",
"product_url": "https://www.goat.com/sneakers/a-ma-maniere-x-air-jordan-4-retro-violet-ore-dv6773-220",
"brand": "Nike",
"primary_color": "yellow",
"primary_color_hex": "#dbd1b3"
},
{
"title": "Air Jordan 4 Retro 'White Oreo'",
"price": "$382",
"image_url": "/shoes/shoe-069.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-oreo-ct8527-100",
"brand": "Nike",
"primary_color": "dark_gray",
"primary_color_hex": "#494948"
},
{
"title": "Air Jordan 4 Retro 'Taupe Haze'",
"price": "$352",
"image_url": "/shoes/shoe-070.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-taupe-haze-db0732-200",
"brand": "Nike",
"primary_color": "gray",
"primary_color_hex": "#83776e"
},
{
"title": "Air Jordan 4 Retro OG GS 'White Cement' 2025",
"price": "$149",
"image_url": "/shoes/shoe-071.png",
"product_url": "https://www.goat.com/sneakers/air-jordan-4-retro-og-gs-white-cement-2025-ib4171-100",
"brand": "Nike",
"primary_color": "light_gray",
"primary_color_hex": "#c8cccc"
},
{
"title": "New Balance 2002R 'Protection Pack - Rain Cloud'",
"price": "$118",
"image_url": "/shoes/shoe-072.png",
"product_url": "https://www.goat.com/sneakers/2002r-protection-pack-rain-cloud-m2002rda",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#c7baa4"
},
{
"title": "New Balance 2002R 'Protection Pack - Phantom'",
"price": "$138",
"image_url": "/shoes/shoe-073.png",
"product_url": "https://www.goat.com/sneakers/2002r-protection-pack-phantom-m2002rdb",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#434648"
},
{
"title": "New Balance 9060 'Black Cat'",
"price": "$165",
"image_url": "/shoes/shoe-074.png",
"product_url": "https://www.goat.com/sneakers/9060-black-grey-u9060zge",
"brand": "New Balance",
"primary_color": "blue",
"primary_color_hex": "#343c44"
},
{
"title": "New Balance 1906R 'Silver Metallic Black'",
"price": "$105",
"image_url": "/shoes/shoe-075.png",
"product_url": "https://www.goat.com/sneakers/1906r-silver-metallic-black-m1906rer",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#7b7978"
},
{
"title": "New Balance 740v2 'Black Silver Metallic'",
"price": "$99",
"image_url": "/shoes/shoe-076.png",
"product_url": "https://www.goat.com/sneakers/740-black-silver-u740bm2",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#3b3c44"
},
{
"title": "New Balance 9060 'Suede Pack - Sea Salt'",
"price": "$145",
"image_url": "/shoes/shoe-077.png",
"product_url": "https://www.goat.com/sneakers/9060-white-grey-u9060hsc",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#82807b"
},
{
"title": "New Balance 1906R 'Silver Metallic Cream'",
"price": "$75",
"image_url": "/shoes/shoe-078.png",
"product_url": "https://www.goat.com/sneakers/1906r-silver-metallic-cream-m1906ree",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#8a8983"
},
{
"title": "New Balance 1906A 'Black'",
"price": "$184",
"image_url": "/shoes/shoe-079.png",
"product_url": "https://www.goat.com/sneakers/1906-black-dark-silver-metallic-m1906af",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#424243"
},
{
"title": "New Balance 850 'Grey'",
"price": "$99",
"image_url": "/shoes/shoe-080.png",
"product_url": "https://www.goat.com/sneakers/850-grey-ml850cf",
"brand": "New Balance",
"primary_color": "light_gray",
"primary_color_hex": "#bdbaba"
},
{
"title": "New Balance 9060 'Triple Black Suede'",
"price": "$125",
"image_url": "/shoes/shoe-081.png",
"product_url": "https://www.goat.com/sneakers/9060-triple-black-u9060bpm",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#3e3f3f"
},
{
"title": "New Balance 530 'White Natural Indigo'",
"price": "$84",
"image_url": "/shoes/shoe-082.png",
"product_url": "https://www.goat.com/sneakers/mr530sg-white-mr530sg",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#403e41"
},
{
"title": "New Balance 1906R 'Metallic Silver Metallic Gold'",
"price": "$79",
"image_url": "/shoes/shoe-083.png",
"product_url": "https://www.goat.com/sneakers/1906r-white-gold-m1906ra",
"brand": "New Balance",
"primary_color": "light_gray",
"primary_color_hex": "#bcbcbe"
},
{
"title": "New Balance 2002R 'Protection Pack - Sea Salt'",
"price": "$125",
"image_url": "/shoes/shoe-084.png",
"product_url": "https://www.goat.com/sneakers/2002r-protection-pack-sea-salt-m2002rdc",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#c2baa7"
},
{
"title": "New Balance 1906R 'Pink Taffy'",
"price": "$79",
"image_url": "/shoes/shoe-085.png",
"product_url": "https://www.goat.com/sneakers/1906r-pink-taffy-u1906rcu",
"brand": "New Balance",
"primary_color": "red",
"primary_color_hex": "#925e6a"
},
{
"title": "New Balance 9060 'Black Castlerock'",
"price": "$118",
"image_url": "/shoes/shoe-086.png",
"product_url": "https://www.goat.com/sneakers/9060-black-castlerock-u9060blk",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#434444"
},
{
"title": "New Balance 1000 'Black Cat'",
"price": "$133",
"image_url": "/shoes/shoe-087.png",
"product_url": "https://www.goat.com/sneakers/1000-triple-black-m1000la",
"brand": "New Balance",
"primary_color": "black",
"primary_color_hex": "#04040c"
},
{
"title": "New Balance 9060 'Arctic Grey'",
"price": "$157",
"image_url": "/shoes/shoe-088.png",
"product_url": "https://www.goat.com/sneakers/9060-washed-blue-u9060ib",
"brand": "New Balance",
"primary_color": "blue",
"primary_color_hex": "#747e91"
},
{
"title": "New Balance 1906R 'Silver Metallic Deep Ocean'",
"price": "$59",
"image_url": "/shoes/shoe-089.png",
"product_url": "https://www.goat.com/sneakers/1906r-silver-metallic-deep-ocean-u1906rce",
"brand": "New Balance",
"primary_color": "light_gray",
"primary_color_hex": "#c1c7c8"
},
{
"title": "New Balance BAPE x 2002R 'Apes Together Strong - Black Camo'",
"price": "$431",
"image_url": "/shoes/shoe-090.png",
"product_url": "https://www.goat.com/sneakers/bape-x-2002r-apes-together-strong-camo-m2002r-bape-camo",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#7d7c74"
},
{
"title": "New Balance 2002R 'Black Cat'",
"price": "$134",
"image_url": "/shoes/shoe-091.png",
"product_url": "https://www.goat.com/sneakers/2002r-triple-black-suede-u2002rbl",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#414141"
},
{
"title": "New Balance 1906L 'Silver Shadow Grey'",
"price": "$136",
"image_url": "/shoes/shoe-092.png",
"product_url": "https://www.goat.com/sneakers/1906l-silver-shadow-grey-u1906lae",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#868280"
},
{
"title": "New Balance 1906D 'Protection Pack - Triple Black'",
"price": "$119",
"image_url": "/shoes/shoe-093.png",
"product_url": "https://www.goat.com/sneakers/1906d-protection-pack-triple-black-m1906df",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#3c3c3c"
},
{
"title": "New Balance 1000 'Mallard Green Sea Salt'",
"price": "$133",
"image_url": "/shoes/shoe-094.png",
"product_url": "https://www.goat.com/sneakers/1000-green-grey-m1000ma",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#7c8e7c"
},
{
"title": "New Balance 550 'White Timberwolf'",
"price": "$73",
"image_url": "/shoes/shoe-095.png",
"product_url": "https://www.goat.com/sneakers/550-white-timberwolf-bb550pwg",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#ccbda2"
},
{
"title": "New Balance 2002R 'Protection Pack - Lunar New Year'",
"price": "$176",
"image_url": "/shoes/shoe-096.png",
"product_url": "https://www.goat.com/sneakers/2002r-protection-pack-lunar-new-year-m2002rdy",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#888382"
},
{
"title": "New Balance 2002R 'Black Gunmetal'",
"price": "$109",
"image_url": "/shoes/shoe-097.png",
"product_url": "https://www.goat.com/sneakers/2002r-black-gunmetal-m2002rbk",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#434040"
},
{
"title": "New Balance 530 'Raincloud'",
"price": "$95",
"image_url": "/shoes/shoe-098.png",
"product_url": "https://www.goat.com/sneakers/530-raincloud-mr530ck",
"brand": "New Balance",
"primary_color": "light_gray",
"primary_color_hex": "#bbbcba"
},
{
"title": "New Balance 1906A 'Black Dragon Berry'",
"price": "$133",
"image_url": "/shoes/shoe-099.png",
"product_url": "https://www.goat.com/sneakers/1906a-black-pink-u1906ad",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#493c43"
},
{
"title": "New Balance 2002R 'Protection Pack - Eclipse'",
"price": "$99",
"image_url": "/shoes/shoe-100.png",
"product_url": "https://www.goat.com/sneakers/2002r-protection-pack-eclipse-m2002rdo",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#8f8c84"
},
{
"title": "New Balance 1906A 'Silver Gold Metallic'",
"price": "$149",
"image_url": "/shoes/shoe-101.png",
"product_url": "https://www.goat.com/sneakers/1906-silver-gold-metallic-m1906ad",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#cdc2b1"
},
{
"title": "New Balance 1906D 'Protection Pack - White Turtledove'",
"price": "$183",
"image_url": "/shoes/shoe-102.png",
"product_url": "https://www.goat.com/sneakers/1906d-protection-pack-triple-white-m1906de",
"brand": "New Balance",
"primary_color": "yellow",
"primary_color_hex": "#c7c4b4"
},
{
"title": "New Balance 2002R 'Protection Pack - Brown' size? Exclusive",
"price": "$207",
"image_url": "/shoes/shoe-103.png",
"product_url": "https://www.goat.com/sneakers/2002r-protection-pack-brown-size-exclusive-m2002rd6",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#957b6a"
},
{
"title": "New Balance 9060 'Quartz Grey'",
"price": "$139",
"image_url": "/shoes/shoe-104.png",
"product_url": "https://www.goat.com/sneakers/9060-quartz-grey-u9060hsa",
"brand": "New Balance",
"primary_color": "yellow",
"primary_color_hex": "#dcd7c6"
},
{
"title": "New Balance 1906R 'Black Silver'",
"price": "$173",
"image_url": "/shoes/shoe-105.png",
"product_url": "https://www.goat.com/sneakers/1906r-primaloft-black-silver-u1906ros",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#3c4142"
},
{
"title": "New Balance 740v2 'White Navy Shadow Grey'",
"price": "$102",
"image_url": "/shoes/shoe-106.png",
"product_url": "https://www.goat.com/sneakers/740v2-navy-white-shadow-grey-u740wn2",
"brand": "New Balance",
"primary_color": "light_gray",
"primary_color_hex": "#c1c1c1"
},
{
"title": "New Balance 9060 'Pink Overdye' ASOS Exclusive",
"price": "$197",
"image_url": "/shoes/shoe-107.png",
"product_url": "https://www.goat.com/sneakers/9060-pink-overdye-asos-exclusive-u9060app",
"brand": "New Balance",
"primary_color": "pink",
"primary_color_hex": "#d19eb3"
},
{
"title": "New Balance 990v4 Made in USA 'Grey Silver'",
"price": "$123",
"image_url": "/shoes/shoe-108.png",
"product_url": "https://www.goat.com/sneakers/990v4-made-in-usa-grey-silver-u990gr4",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#807f80"
},
{
"title": "New Balance 990v6 Made in USA 'Castlerock'",
"price": "$137",
"image_url": "/shoes/shoe-109.png",
"product_url": "https://www.goat.com/sneakers/990v6-made-in-usa-castlerock-m990gl6",
"brand": "New Balance",
"primary_color": "light_gray",
"primary_color_hex": "#bcbcbd"
},
{
"title": "New Balance 990v3 Made in USA 'Grey'",
"price": "$208",
"image_url": "/shoes/shoe-110.png",
"product_url": "https://www.goat.com/sneakers/990v3-made-in-usa-grey-m990gy3",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#828485"
},
{
"title": "Teddy Santis x New Balance 990v4 Made in USA 'Arctic Grey Black'",
"price": "$179",
"image_url": "/shoes/shoe-111.png",
"product_url": "https://www.goat.com/sneakers/990v4-made-in-usa-arctic-grey-black-u990bb4",
"brand": "New Balance",
"primary_color": "blue",
"primary_color_hex": "#738290"
},
{
"title": "New Balance 990v3 Made In USA 'Black'",
"price": "$159",
"image_url": "/shoes/shoe-112.png",
"product_url": "https://www.goat.com/sneakers/990v3-made-in-usa-black-m990bs3",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#444343"
},
{
"title": "Bodega x New Balance 990v3 Made In USA 'Anniversary'",
"price": "$175",
"image_url": "/shoes/shoe-113.png",
"product_url": "https://www.goat.com/sneakers/bodega-x-990v3-made-in-usa-15th-anniversary-bodega-990v3",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#cbc5bd"
},
{
"title": "Action Bronson x New Balance 990v6 Made in USA 'Amazõnia'",
"price": "$175",
"image_url": "/shoes/shoe-114.png",
"product_url": "https://www.goat.com/sneakers/action-bronson-x-990v6-made-in-usa-amazonia-00011-10000abx9mi",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#bcb4a5"
},
{
"title": "Action Bronson x New Balance 990v6 Made in USA 'Lapis Lazuli'",
"price": "$173",
"image_url": "/shoes/shoe-115.png",
"product_url": "https://www.goat.com/sneakers/action-bronson-x-990v6-made-in-usa-lapis-lazuli-m990ac6",
"brand": "New Balance",
"primary_color": "blue",
"primary_color_hex": "#687eb2"
},
{
"title": "New Balance 990v4 Made In USA 'Red Label - Grey'",
"price": "$268",
"image_url": "/shoes/shoe-116.png",
"product_url": "https://www.goat.com/sneakers/990v4-made-in-usa-red-label-grey-m990vs4",
"brand": "New Balance",
"primary_color": "light_gray",
"primary_color_hex": "#b6b8bc"
},
{
"title": "New Balance 990v4 Made in USA 'Black Silver' 2023",
"price": "$154",
"image_url": "/shoes/shoe-117.png",
"product_url": "https://www.goat.com/sneakers/990v4-made-in-usa-black-silver-2023-u990bl4",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#474646"
},
{
"title": "Action Bronson x New Balance 990v6 Made in USA 'Untitled'",
"price": "$229",
"image_url": "/shoes/shoe-118.png",
"product_url": "https://www.goat.com/sneakers/action-bronson-x-990v6-made-in-usa-multi-color-u990at6",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#d4c49e"
},
{
"title": "New Balance Teddy Santis x 990v4 Made in USA 'Grey Black'",
"price": "$132",
"image_url": "/shoes/shoe-119.png",
"product_url": "https://www.goat.com/sneakers/teddy-santis-x-990v4-made-in-usa-grey-cream-u990tg4",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#7f7c7a"
},
{
"title": "Teddy Santis x New Balance 990v3 Made in USA 'Tan Orange'",
"price": "$166",
"image_url": "/shoes/shoe-120.png",
"product_url": "https://www.goat.com/sneakers/teddy-santis-x-990v3-made-in-usa-khaki-orange-m990bt3",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#9f855e"
},
{
"title": "Teddy Santis x New Balance 990v4 Made in USA 'Plum Purple'",
"price": "$147",
"image_url": "/shoes/shoe-121.png",
"product_url": "https://www.goat.com/sneakers/teddy-santis-x-990v4-made-in-usa-purple-suede-u990tb4",
"brand": "New Balance",
"primary_color": "purple",
"primary_color_hex": "#3c2c5c"
},
{
"title": "New Balance 990v6 Made in USA 'Workwear'",
"price": "$69",
"image_url": "/shoes/shoe-122.png",
"product_url": "https://www.goat.com/sneakers/990v6-made-in-usa-workwear-grey-u990tn6",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#b98651"
},
{
"title": "Teddy Santis x New Balance 990v3 Made in USA 'Green Gold'",
"price": "$183",
"image_url": "/shoes/shoe-123.png",
"product_url": "https://www.goat.com/sneakers/teddy-santis-x-990v3-made-in-usa-green-yellow-m990gg3",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#cfc4a4"
},
{
"title": "Teddy Santis x New Balance 990v6 Made in USA 'Reflection Marblehead'",
"price": "$149",
"image_url": "/shoes/shoe-124.png",
"product_url": "https://www.goat.com/sneakers/990v6-made-in-usa-reflection-marblehead-u990nc6",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#787573"
},
{
"title": "Action Bronson x New Balance 990v6 Made in USA 'Baklava'",
"price": "$215",
"image_url": "/shoes/shoe-125.png",
"product_url": "https://www.goat.com/sneakers/action-bronson-x-990v6-made-in-usa-baklava-m990ab6",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#d0b587"
},
{
"title": "Teddy Santis x New Balance 990v6 Made in USA 'Paris'",
"price": "$723",
"image_url": "/shoes/shoe-126.png",
"product_url": "https://www.goat.com/sneakers/teddy-santis-x-990v6-made-in-usa-paris-u990pa6",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#544639"
},
{
"title": "Teddy Santis x New Balance 990v6 Made in USA 'Community Pack - Red'",
"price": "$98",
"image_url": "/shoes/shoe-127.png",
"product_url": "https://www.goat.com/sneakers/action-bronson-x-990v6-made-in-usa-community-red-u990rt6",
"brand": "New Balance",
"primary_color": "red",
"primary_color_hex": "#dd817f"
},
{
"title": "JJJJound x New Balance 990v3 Made in USA 'Olive'",
"price": "$308",
"image_url": "/shoes/shoe-128.png",
"product_url": "https://www.goat.com/sneakers/jjjjound-x-990v3-made-in-usa-olive-m990jd3",
"brand": "New Balance",
"primary_color": "green",
"primary_color_hex": "#8c9370"
},
{
"title": "New Balance 990v6 Made in USA 'Triple Black'",
"price": "$149",
"image_url": "/shoes/shoe-129.png",
"product_url": "https://www.goat.com/sneakers/990v6-made-in-usa-triple-black-u990bb6",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#34343c"
},
{
"title": "JJJJound x New Balance 990v3 Made in USA 'Brown'",
"price": "$284",
"image_url": "/shoes/shoe-130.png",
"product_url": "https://www.goat.com/sneakers/jjjjound-x-990v3-made-in-usa-brown-m990jj3",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#544430"
},
{
"title": "New Balance 990v6 Made in USA 'Salmon'",
"price": "$77",
"image_url": "/shoes/shoe-131.png",
"product_url": "https://www.goat.com/sneakers/990v6-made-in-usa-salmon-u990sr6",
"brand": "New Balance",
"primary_color": "red",
"primary_color_hex": "#f88984"
},
{
"title": "New Balance 990v3 Made In USA 'Navy'",
"price": "$139",
"image_url": "/shoes/shoe-132.png",
"product_url": "https://www.goat.com/sneakers/990-made-in-usa-navy-denim-m990nb3",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#767a79"
},
{
"title": "Teddy Santis x New Balance 990v6 Made in USA 'Light Mushroom Moonrock'",
"price": "$154",
"image_url": "/shoes/shoe-133.png",
"product_url": "https://www.goat.com/sneakers/990v6-made-in-usa-light-mushroom-moonrock-u990mm6",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#99826b"
},
{
"title": "Teddy Santis x New Balance 990v6 Made in USA 'Community Pack - Navy'",
"price": "$174",
"image_url": "/shoes/shoe-134.png",
"product_url": "https://www.goat.com/sneakers/teddy-santis-x-990v6-made-in-usa-community-pack-vintage-indigo-u990lt6",
"brand": "New Balance",
"primary_color": "blue",
"primary_color_hex": "#5c6f88"
},
{
"title": "Teddy Santis x New Balance 990v3 Made in USA 'Raw Amethyst'",
"price": "$275",
"image_url": "/shoes/shoe-135.png",
"product_url": "https://www.goat.com/sneakers/teddy-santis-x-990v3-made-in-usa-raw-amethyst-m990td3",
"brand": "New Balance",
"primary_color": "purple",
"primary_color_hex": "#bcacc4"
},
{
"title": "Teddy Santis x New Balance 990v3 Made in USA 'Green Purple'",
"price": "$156",
"image_url": "/shoes/shoe-136.png",
"product_url": "https://www.goat.com/sneakers/teddy-santis-x-990v3-made-in-usa-olive-burgundy-m990gp3",
"brand": "New Balance",
"primary_color": "red",
"primary_color_hex": "#5e4a44"
},
{
"title": "New Balance Teddy Santis x 990v3 Made in USA 'Scarlet Marblehead'",
"price": "$234",
"image_url": "/shoes/shoe-137.png",
"product_url": "https://www.goat.com/sneakers/990v3-made-in-usa-scarlet-marblehead-m990tf3",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#7c7978"
},
{
"title": "New Balance 990v5 Made in USA 'Castlerock'",
"price": "$192",
"image_url": "/shoes/shoe-138.png",
"product_url": "https://www.goat.com/sneakers/990v5-grey-m990gl5",
"brand": "New Balance",
"primary_color": "light_gray",
"primary_color_hex": "#c0bfbe"
},
{
"title": "Teddy Santis x New Balance 990v3 Made in USA 'Black Tan'",
"price": "$135",
"image_url": "/shoes/shoe-139.png",
"product_url": "https://www.goat.com/sneakers/990v3-made-in-usa-black-tan-m990bb3",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#524c3c"
},
{
"title": "Kith x New Balance 990v3 Made in USA 'Steel Blue'",
"price": "$239",
"image_url": "/shoes/shoe-140.png",
"product_url": "https://www.goat.com/sneakers/kith-x-990v3-made-in-usa-steel-blue-m990ks3",
"brand": "New Balance",
"primary_color": "dark_gray",
"primary_color_hex": "#443c3c"
},
{
"title": "New Balance 990v4 Made in USA 'Castlerock'",
"price": "$223",
"image_url": "/shoes/shoe-141.png",
"product_url": "https://www.goat.com/sneakers/990v4-made-in-usa-castlerock-m990gl4",
"brand": "New Balance",
"primary_color": "gray",
"primary_color_hex": "#808081"
},
{
"title": "New Balance Aimé Leon Dore x 990v4 Made in USA 'True Camo'",
"price": "$233",
"image_url": "/shoes/shoe-142.png",
"product_url": "https://www.goat.com/sneakers/aime-leon-dore-x-990v4-made-in-usa-true-camo-u990ct4",
"brand": "New Balance",
"primary_color": "orange",
"primary_color_hex": "#50452c"
},
{
"title": "Teddy Santis x New Balance 990v6 Made in USA 'Community Pack - Mint'",
"price": "$118",
"image_url": "/shoes/shoe-143.png",
"product_url": "https://www.goat.com/sneakers/teddy-santis-x-990v6-made-in-usa-clay-ash-u990gt6",
"brand": "New Balance",
"primary_color": "teal",
"primary_color_hex": "#245254"
}
]
eslint.config.mjs
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
const eslintConfig = defineConfig([
...nextVitals,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);
export default eslintConfig;
함께 쓰는 파일 27개 보기
next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: false,
devIndicators: false,
webpack: (config) => {
config.module.rules.push({
test: /\.(glsl|vs|fs|vert|frag)$/,
use: ['raw-loader', 'glslify-loader'],
});
return config;
},
};
export default nextConfig;
postcss.config.mjs
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;
src/components/CloseButton.jsx
import React, { useEffect, useState, useRef } from "react";
import { Html } from "@react-three/drei";
import { useThree } from "@react-three/fiber";
export function CloseButton({
isActive,
position,
onClose,
}) {
const { gl } = useThree();
const [shouldShow, setShouldShow] = useState(false);
const timerRef = useRef(null);
const canvasRef = useRef(null);
useEffect(() => {
canvasRef.current = gl.domElement;
}, [gl]);
// 250ms delay before showing
useEffect(() => {
// Clear any existing timer
if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
if (!isActive) {
// Use setTimeout(0) to avoid synchronous setState
timerRef.current = setTimeout(() => {
setShouldShow(false);
}, 0);
return () => {
if (timerRef.current)
clearTimeout(timerRef.current);
};
}
timerRef.current = setTimeout(() => {
setShouldShow(true);
}, 250);
return () => {
if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
};
}, [isActive]);
if (!isActive) return null;
// Convert 3D position to screen coordinates for Html
// Html centers by default, so we need to adjust
const [x, y, z] = position;
return (
<Html
position={[x, y, z]}
center
style={{
pointerEvents: "auto",
transform: "translate(-50%, -150%)",
}}
occlude
>
<style>
{`
@keyframes breathe {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.015); }
}
`}
</style>
<button
onClick={(e) => {
e.stopPropagation();
onClose();
}}
onMouseEnter={() => {
if (canvasRef.current) {
canvasRef.current.style.cursor = "pointer";
}
}}
onMouseLeave={() => {
if (canvasRef.current) {
canvasRef.current.style.cursor = "grab";
}
}}
style={{
width: "32px",
height: "32px",
border: "1px solid rgba(0, 0, 0, 1.2)",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
padding: 0,
opacity: shouldShow ? 0.6 : 0,
animation: shouldShow
? "breathe 3.14s ease-in-out infinite"
: "none",
transform: shouldShow ? "scale(1)" : "scale(0.8)",
transition: "opacity 0.2s ease",
background: "transparent",
}}
onMouseOver={(e) => {
e.currentTarget.style.opacity = "0.8";
}}
onMouseOut={(e) => {
if (shouldShow) {
e.currentTarget.style.opacity = "0.6";
}
}}
>
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="rgba(0, 0, 0, 0.8)"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
</Html>
);
}
src/components/GridUI.jsx
import React from "react";
import { motion, AnimatePresence } from "framer-motion";
import { CONFIG } from "./grid/gridConfig";
// 1. THE PHYSICS
// High stiffness, moderate damping = "Snappy but smooth" (Apple feel)
const islandTransition = {
type: "spring",
stiffness: 500,
damping: 30,
mass: 1,
};
export function UnifiedControlBar({
currentCollection,
onSwitch,
setZoomTrigger,
isZoomedIn,
hasActiveSelection,
nikeFilter,
onFilterChange,
}) {
const collections = [
"Nike",
"New Balance",
"Under $150",
];
const nikeFilters = [
{ id: "all", label: "All" },
{ id: "jordan", label: "Jordan" },
{ id: "dunk", label: "Dunk" },
];
return (
<div
className="control-bar-container"
style={{
position: "fixed",
bottom: "40px",
left: "0",
right: "0",
display: "flex",
justifyContent: "center",
alignItems: "flex-end",
zIndex: 100,
pointerEvents: "none",
}}
>
{/*
THE ISLAND CONTAINER
- layout: Animates width/height changes
- borderRadius: 32px is standard for pill shapes
*/}
<motion.div
className="control-bar-island"
layout
transition={islandTransition}
style={{
background:
"linear-gradient(135deg, rgba(255, 240, 235, 0.4) 0%, rgba(255, 255, 255, 0.3) 50%, rgba(245, 235, 255, 0.4) 100%)", // Very subtle orange to purple gradient for glassy depth
backdropFilter: "blur(40px) saturate(200%)", // Increased blur for more glassy feel
WebkitBackdropFilter: "blur(40px) saturate(200%)", // Safari support
borderRadius: "32px",
border: "1px solid rgba(255, 255, 255, 0.3)", // Subtle white border for glass edge
boxShadow:
"0 8px 32px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.5)", // Soft shadow + inner highlight
padding: "6px",
display: "flex",
alignItems: "center",
pointerEvents: "auto",
height: "56px",
overflow: "hidden", // Crucial for clipping content during resize
}}
>
{/*
mode="popLayout" is CRITICAL.
It lets the outgoing component "pop" out of the layout flow
so the container can instantly shrink/grow to the new content's size.
*/}
<AnimatePresence mode="popLayout" initial={false}>
{hasActiveSelection ? (
/* ---------------- STATE 1: BUY NOW (Active Selection) ---------------- */
<motion.button
key="buy-now-mode"
initial={{
opacity: 0,
scale: 0.9,
filter: "blur(4px)",
}}
animate={{
opacity: 1,
scale: 1,
filter: "blur(0px)",
}}
exit={{
opacity: 0,
scale: 0.9,
filter: "blur(4px)",
}}
transition={{
...islandTransition,
opacity: { duration: 0.2 },
}}
onClick={() => {
// Handle buy now action
}}
style={{
background: "#000",
color: "#fff",
border: "none",
borderRadius: "24px",
padding: "0 24px",
height: "44px",
fontSize: "14px",
fontWeight: "600",
cursor: "pointer",
display: "flex",
alignItems: "center",
whiteSpace: "nowrap",
}}
whileTap={{ scale: 0.95 }}
>
Buy Now
</motion.button>
) : isZoomedIn ? (
/* ---------------- STATE 2: COMPACT (Zoomed In) ---------------- */
<motion.div
key="compact-mode"
initial={{
opacity: 0,
scale: 0.5,
filter: "blur(4px)",
}}
animate={{
opacity: 1,
scale: 1,
filter: "blur(0px)",
}}
exit={{
opacity: 0,
scale: 0.5,
filter: "blur(4px)",
}}
transition={{
...islandTransition,
opacity: { duration: 0.2 },
}}
style={{ display: "flex" }}
>
<ControlButton
icon="remove"
onClick={() => setZoomTrigger("OUT")}
label="Zoom Out"
/>
</motion.div>
) : (
/* ---------------- STATE 2: EXPANDED (Zoomed Out) ---------------- */
<motion.div
key="expanded-mode"
initial={{
opacity: 0,
scale: 0.9,
filter: "blur(4px)",
}}
animate={{
opacity: 1,
scale: 1,
filter: "blur(0px)",
}}
exit={{
opacity: 0,
scale: 0.9,
filter: "blur(4px)",
}}
transition={{
...islandTransition,
opacity: { duration: 0.2 },
}}
style={{
display: "flex",
alignItems: "center",
gap: "8px",
}}
>
<ControlButton
icon="add"
onClick={() =>
setZoomTrigger(CONFIG.zoomIn)
}
label="Zoom In"
/>
{/* Vertical Divider */}
<motion.div
layout
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "24px" }}
transition={{ delay: 0.1 }}
style={{
width: "1px",
background: "rgba(0,0,0,0.08)",
margin: "0 2px",
boxShadow:
"0 0 1px rgba(255, 255, 255, 0.3)", // Subtle highlight for glassy feel
}}
/>
{/* Collection Tabs Group */}
<div style={{ display: "flex", gap: "2px" }}>
{collections.map((name, index) => {
const isActive =
currentCollection === index;
return (
<TabButton
key={name}
isActive={isActive}
onClick={() => onSwitch(index)}
>
{name}
</TabButton>
);
})}
</div>
{/* Nike Filters - only show when Nike collection is active (desktop only) */}
{currentCollection === 0 && (
<div className="desktop-filters">
{/* Vertical Divider */}
<motion.div
layout
initial={{ opacity: 0, scaleX: 0 }}
animate={{ opacity: 1, scaleX: 1 }}
transition={islandTransition}
style={{
width: "1px",
height: "24px",
background: "rgba(0,0,0,0.08)",
margin: "0 6px",
transformOrigin: "center",
}}
/>
{/* Filter Chips */}
<motion.div
layout
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={islandTransition}
style={{ display: "flex", gap: "4px" }}
>
{nikeFilters.map((filter) => (
<FilterChip
key={filter.id}
isActive={nikeFilter === filter.id}
onClick={() => onFilterChange(filter.id)}
layoutGroup="desktop"
>
{filter.label}
</FilterChip>
))}
</motion.div>
</div>
)}
</motion.div>
)}
</AnimatePresence>
</motion.div>
{/* Mobile Nike Filters - appears above main bar */}
<AnimatePresence>
{currentCollection === 0 && !isZoomedIn && !hasActiveSelection && (
<motion.div
className="mobile-filters"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
transition={islandTransition}
style={{
position: "absolute",
bottom: "70px",
left: 0,
right: 0,
display: "none", // Hidden by default, shown on mobile via CSS
justifyContent: "center",
pointerEvents: "none",
}}
>
<div
style={{
display: "flex",
justifyContent: "center",
background: "rgba(255, 255, 255, 0.85)",
backdropFilter: "blur(40px) saturate(200%)",
WebkitBackdropFilter: "blur(40px) saturate(200%)",
borderRadius: "20px",
border: "1px solid rgba(255, 255, 255, 0.3)",
boxShadow: "0 4px 20px rgba(0, 0, 0, 0.08)",
padding: "6px 8px",
gap: "4px",
pointerEvents: "auto",
}}
>
{nikeFilters.map((filter) => (
<FilterChip
key={`mobile-${filter.id}`}
isActive={nikeFilter === filter.id}
onClick={() => onFilterChange(filter.id)}
layoutGroup="mobile"
>
{filter.label}
</FilterChip>
))}
</div>
</motion.div>
)}
</AnimatePresence>
{/* Responsive scaling for shorter viewports (tablets) and mobile */}
<style>{`
.desktop-filters {
display: flex;
align-items: center;
}
.mobile-filters {
display: none !important;
}
@media (max-height: 800px) {
.control-bar-container {
bottom: 24px !important;
}
.control-bar-island {
height: 48px !important;
border-radius: 24px !important;
padding: 4px !important;
}
}
@media (max-height: 650px) {
.control-bar-container {
bottom: 16px !important;
}
.control-bar-island {
height: 44px !important;
}
}
@media (max-width: 768px) {
.control-bar-container {
bottom: 20px !important;
}
.control-bar-island {
height: 48px !important;
padding: 4px !important;
}
.desktop-filters {
display: none !important;
}
.mobile-filters {
display: flex !important;
}
}
@media (max-width: 480px) {
.control-bar-container {
bottom: 16px !important;
}
.control-bar-island {
height: 44px !important;
}
.control-button {
width: 36px !important;
height: 36px !important;
}
.tab-button {
padding: 6px 10px !important;
font-size: 12px !important;
}
.filter-chip {
padding: 4px 8px !important;
font-size: 11px !important;
}
.mobile-filters {
bottom: 60px !important;
}
}
`}</style>
</div>
);
}
// --- Sub-components for cleaner code & isolated animations ---
function ControlButton({ onClick, icon, label }) {
return (
<motion.button
layout="position" // Prevents icon distortion during resize
onClick={onClick}
className="control-button"
whileHover={{
scale: 1.05,
backgroundColor: "rgba(0,0,0,0.05)",
}}
whileTap={{ scale: 0.9 }}
transition={{ duration: 0.2 }}
style={{
width: "44px",
height: "44px",
borderRadius: "50%",
border: "none",
background: "transparent",
color: "#111",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
outline: "none",
}}
aria-label={label}
>
{/* Simple SVG Icons instead of text for a sharper look */}
{icon === "add" ? (
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
) : (
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
)}
</motion.button>
);
}
function TabButton({ children, isActive, onClick }) {
return (
<motion.button
layout
onClick={onClick}
className="tab-button"
style={{
position: "relative",
border: "none",
background: "transparent",
color: isActive ? "#000" : "#666",
padding: "8px 16px",
borderRadius: "20px",
fontSize: "14px",
fontWeight: "600",
cursor: "pointer",
whiteSpace: "nowrap", // Prevents text wrapping during resize
zIndex: 1,
transition: "color 0.2s ease",
}}
>
{children}
{isActive && (
<motion.div
layoutId="activeTabIndicator"
transition={islandTransition}
style={{
position: "absolute",
inset: 0,
background: "rgba(255, 255, 255, 0.6)", // Solid background for active state
backdropFilter: "blur(20px)",
WebkitBackdropFilter: "blur(20px)",
borderRadius: "20px",
border: "1px solid rgba(255, 255, 255, 0.4)",
boxShadow:
"0 2px 8px rgba(0,0,0,0.06), inset 0 1px 0 rgba(255, 255, 255, 0.6)",
zIndex: -1,
}}
/>
)}
</motion.button>
);
}
function FilterChip({ children, isActive, onClick, layoutGroup = "default" }) {
return (
<motion.button
layout
onClick={onClick}
className="filter-chip"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.95 }}
transition={islandTransition}
style={{
position: "relative",
border: "none",
background: "transparent",
color: isActive ? "#fff" : "#555",
padding: "6px 12px",
borderRadius: "14px",
fontSize: "12px",
fontWeight: "500",
cursor: "pointer",
whiteSpace: "nowrap",
zIndex: 1,
}}
>
{/* Animated background pill */}
{isActive && (
<motion.div
layoutId={`activeFilterIndicator-${layoutGroup}`}
transition={islandTransition}
style={{
position: "absolute",
inset: 0,
background: "rgba(0, 0, 0, 0.85)",
borderRadius: "14px",
zIndex: -1,
}}
/>
)}
{!isActive && (
<div
style={{
position: "absolute",
inset: 0,
background: "rgba(0, 0, 0, 0.05)",
borderRadius: "14px",
zIndex: -1,
}}
/>
)}
{children}
</motion.button>
);
}
src/components/Header.jsx
import React from "react";
export default function Header() {
return (
<header
className="site-header"
style={{
position: "fixed",
top: 0,
left: 0,
right: 0,
zIndex: 50,
padding: "20px 32px",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
pointerEvents: "none",
}}
>
{/* Logo */}
<div
style={{
display: "flex",
alignItems: "center",
gap: "12px",
pointerEvents: "auto",
}}
>
{/* Text logo */}
<div
style={{
display: "flex",
flexDirection: "column",
gap: "0px",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
gap: "6px",
}}
>
<span
style={{
fontFamily:
"'Helvetica Neue', Helvetica, Arial, sans-serif",
fontSize: "15px",
fontWeight: "700",
letterSpacing: "0.12em",
color: "#000",
textTransform: "uppercase",
}}
>
PERFECT
</span>
<span
style={{
fontFamily:
"'Helvetica Neue', Helvetica, Arial, sans-serif",
fontSize: "15px",
fontWeight: "300",
letterSpacing: "0.12em",
color: "#000",
textTransform: "uppercase",
}}
>
PAIR
</span>
</div>
<span
style={{
fontFamily:
"'Helvetica Neue', Helvetica, Arial, sans-serif",
fontSize: "8px",
fontWeight: "400",
letterSpacing: "0.2em",
color: "#999",
textTransform: "uppercase",
marginTop: "-1px",
}}
>
Est. 2026
</span>
</div>
</div>
{/* Right side - minimal nav hints */}
<div
className="header-nav"
style={{
display: "flex",
alignItems: "center",
gap: "24px",
pointerEvents: "auto",
}}
>
<NavItem label="BROWSE" number="01" />
<NavItem label="COLLECT" number="02" />
<Divider />
<QuoteText text="FOOTWEAR" />
</div>
{/* Mobile responsive styles */}
<style>{`
@media (max-width: 600px) {
.site-header {
padding: 16px 20px !important;
}
.header-nav {
display: none !important;
}
}
`}</style>
</header>
);
}
function NavItem({ label, number }) {
return (
<div
style={{
display: "flex",
alignItems: "baseline",
gap: "6px",
cursor: "pointer",
opacity: 0.6,
transition: "opacity 0.2s ease",
}}
onMouseEnter={(e) =>
(e.currentTarget.style.opacity = 1)
}
onMouseLeave={(e) =>
(e.currentTarget.style.opacity = 0.6)
}
>
<span
style={{
fontFamily:
"'Helvetica Neue', Helvetica, Arial, sans-serif",
fontSize: "9px",
fontWeight: "400",
color: "#999",
}}
>
{number}
</span>
<span
style={{
fontFamily:
"'Helvetica Neue', Helvetica, Arial, sans-serif",
fontSize: "11px",
fontWeight: "500",
letterSpacing: "0.08em",
color: "#000",
}}
>
{label}
</span>
</div>
);
}
function Divider() {
return (
<div
style={{
width: "1px",
height: "12px",
background: "rgba(0,0,0,0.15)",
}}
/>
);
}
function QuoteText({ text }) {
return (
<span
style={{
fontFamily:
"'Helvetica Neue', Helvetica, Arial, sans-serif",
fontSize: "11px",
fontWeight: "400",
letterSpacing: "0.05em",
color: "#000",
opacity: 0.4,
}}
>
"{text}"
</span>
);
}
src/components/HoloCardMaterial.js
import * as THREE from "three";
import { shaderMaterial } from "@react-three/drei";
import { extend } from "@react-three/fiber";
import vertexShader from "@/shaders/holocard.vert";
import fragmentShader from "@/shaders/holocard.frag";
// --- HOLOGRAPHIC CARD SHADER MATERIAL ---
const HoloCardMaterial = shaderMaterial(
{
uTime: 0,
uTexture: new THREE.Texture(),
uOpacity: 1,
uActive: 0, // 0 = normal, 1 = active/selected
},
vertexShader,
fragmentShader
);
// Extend so R3F recognizes <holoCardMaterial />
extend({ HoloCardMaterial });
export default HoloCardMaterial;
src/components/MiniMap.jsx
import React, { useRef, useEffect, useState } from "react";
export default function MiniMap({
gridDims,
rigState,
config,
totalItems,
isZoomedIn,
}) {
const containerRef = useRef();
const canvasRef = useRef();
const zoomRef = useRef(1);
const centerRef = useRef({ x: 0.5, y: 0.5 });
const opacityRef = useRef(0);
// Use relative sizing (percentage of viewport) - larger on mobile
const [mapWidthPercent, setMapWidthPercent] = useState(8);
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const aspectRatio = gridDims.width / gridDims.height;
const dpr =
typeof window !== "undefined"
? window.devicePixelRatio || 1
: 1;
// Calculate actual pixel dimensions from percentage, responsive sizing
useEffect(() => {
const updateDimensions = () => {
// Larger minimap on mobile
let widthPercent = 8; // Desktop default
if (window.innerWidth < 480) {
widthPercent = 20; // Phone
} else if (window.innerWidth < 768) {
widthPercent = 15; // Tablet
}
setMapWidthPercent(widthPercent);
const width = (window.innerWidth * widthPercent) / 100;
const height = width / aspectRatio;
setDimensions({ width, height });
};
updateDimensions();
window.addEventListener("resize", updateDimensions);
return () => window.removeEventListener("resize", updateDimensions);
}, [aspectRatio]);
const cols = config.gridCols;
const rows = Math.ceil(totalItems / cols);
useEffect(() => {
let rafId;
const draw = () => {
if (!containerRef.current || !canvasRef.current) {
rafId = requestAnimationFrame(draw);
return;
}
// Show when dragging OR when an item is active
// On mobile, only show when zoomed in
const isMobile = window.innerWidth < 768;
const isActive =
rigState.isDragging || rigState.activeId !== null;
const shouldShow = isMobile ? (isActive && isZoomedIn) : isActive;
const targetOp = shouldShow ? 1 : 0;
opacityRef.current +=
(targetOp - opacityRef.current) * 0.1;
containerRef.current.style.opacity =
opacityRef.current;
if (opacityRef.current < 0.02) {
rafId = requestAnimationFrame(draw);
return;
}
// Calculate zoom and center targets
const isFocused = rigState.activeId !== null;
const targetZoom = isFocused ? 2.5 : 1;
let targetCenterX = 0.5;
let targetCenterY = 0.5;
if (isFocused) {
const col = rigState.activeId % cols;
const row = Math.floor(rigState.activeId / cols);
targetCenterX = (col + 0.5) / cols;
targetCenterY = (row + 0.5) / rows;
}
// Smooth interpolation
zoomRef.current +=
(targetZoom - zoomRef.current) * 0.08;
centerRef.current.x +=
(targetCenterX - centerRef.current.x) * 0.08;
centerRef.current.y +=
(targetCenterY - centerRef.current.y) * 0.08;
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
ctx.clearRect(0, 0, w, h);
// Apply zoom transform
ctx.save();
const zoom = zoomRef.current;
const cx = centerRef.current.x * w;
const cy = centerRef.current.y * h;
ctx.translate(w / 2, h / 2);
ctx.scale(zoom, zoom);
ctx.translate(-cx, -cy);
// Draw dots - size relative to canvas
const baseDotSize = Math.max(w, h) * 0.015; // 1.5% of larger dimension
for (let i = 0; i < totalItems; i++) {
const c = i % cols;
const r = Math.floor(i / cols);
const nX = (c + 0.5) / cols;
const nY = (r + 0.5) / rows;
const isSelected = rigState.activeId === i;
const dotSize = isSelected ? baseDotSize * 2 : baseDotSize;
ctx.beginPath();
ctx.arc(nX * w, nY * h, dotSize, 0, Math.PI * 2);
ctx.fillStyle = isSelected
? "#FFB000"
: "rgba(255, 255, 255, 0.4)";
ctx.fill();
}
// Viewport Rect (only show when not focused)
if (!isFocused) {
const offPctX =
-rigState.current.x / gridDims.width;
const offPctY =
rigState.current.y / gridDims.height;
const vFov = (45 * Math.PI) / 180;
const viewHeight = 2 * Math.tan(vFov / 2) * 10;
const viewWidth =
viewHeight *
(window.innerWidth / window.innerHeight);
const rectW =
Math.min(viewWidth / gridDims.width, 1) * w;
const rectH =
Math.min(viewHeight / gridDims.height, 1) * h;
const rectX = (0.5 + offPctX) * w - rectW / 2;
const rectY = (0.5 + offPctY) * h - rectH / 2;
ctx.strokeStyle = "rgba(255, 255, 255, 0.8)";
ctx.lineWidth = 1.5 / zoom;
ctx.strokeRect(rectX, rectY, rectW, rectH);
}
ctx.restore();
rafId = requestAnimationFrame(draw);
};
rafId = requestAnimationFrame(draw);
return () => cancelAnimationFrame(rafId);
}, [gridDims, cols, rows, rigState, config, totalItems, isZoomedIn]);
return (
<div
ref={containerRef}
style={{
position: "fixed",
bottom: "2vh",
right: "2vw",
width: `${mapWidthPercent}vw`,
aspectRatio: aspectRatio,
background: "rgba(0,0,0,0.4)",
backdropFilter: "blur(10px)",
borderRadius: 0,
opacity: 0,
border: "1px solid rgba(255,255,255,0.2)",
overflow: "hidden",
pointerEvents: "none",
zIndex: 100,
}}
>
<canvas
ref={canvasRef}
width={dimensions.width * dpr}
height={dimensions.height * dpr}
style={{ width: "100%", height: "100%" }}
/>
</div>
);
}
src/components/TopologyBackground.jsx
import React, { useRef } from "react";
import { useFrame, extend } from "@react-three/fiber";
import { shaderMaterial, Plane } from "@react-three/drei";
import * as THREE from "three";
import { easing } from "maath";
import vertexShader from "@/shaders/topography.vert";
import fragmentShader from "@/shaders/topography.frag";
// --- 1. SHADER: FLUID TOPOGRAPHY ---
const TopographyMaterial = shaderMaterial(
{
uTime: 0,
uColor: new THREE.Color("#e0e0e0"),
uResolution: new THREE.Vector2(1, 1),
uOpacity: 1.0,
uLineOpacity: 0.4,
uScale: 3.0,
uLineThickness: 0.03,
},
vertexShader,
fragmentShader
);
extend({ TopographyMaterial });
export function TopologyBackground({
isZoomedIn = false,
quality = 1,
color = "#e0e0e0",
opacity = 0.4,
speed = 0.05,
scale = 3.0,
lineThickness = 0.03,
}) {
const materialRef = useRef();
// Fixed world-space dimensions (doesn't change with camera zoom)
const planeWidth = 90;
const planeHeight = 40;
useFrame((_, delta) => {
if (!materialRef.current) return;
materialRef.current.uTime += delta * (speed / 0.05); // Normalize speed
materialRef.current.uResolution.set(planeWidth, planeHeight);
materialRef.current.uColor.set(color);
materialRef.current.uLineOpacity = opacity;
materialRef.current.uScale = scale;
materialRef.current.uLineThickness = lineThickness;
// Tween opacity based on zoom state
const targetOpacity = isZoomedIn ? 0.25 : 1.0;
easing.damp(materialRef.current, "uOpacity", targetOpacity, 0.3, delta);
});
// Low quality mode: render simple solid color plane (no shader)
if (quality < 0.5) {
return (
<Plane args={[planeWidth, planeHeight]} position={[0, 0, -15]} renderOrder={-1}>
<meshBasicMaterial color="#e8e8e8" />
</Plane>
);
}
return (
// Pass the calculated Width/Height to the geometry
<Plane args={[planeWidth, planeHeight]} position={[0, 0, -15]} renderOrder={-1}>
<topographyMaterial
ref={materialRef}
transparent
depthWrite={false}
/>
</Plane>
);
}src/components/grid/GridCanvas.jsx
import { useMemo, useState } from "react";
import { useFrame } from "@react-three/fiber";
import { CONFIG } from "./gridConfig";
import { EMPTY_COLORS, matchesFilter, calculateGridDimensions } from "./gridState";
import { ShoeTile } from "./ShoeTile";
// --- OPTIMIZED COMPONENT: GRID CANVAS ---
// Renders a single set of items with Time-Sliced mounting
export function GridCanvas({
items,
gridVisible,
transitionStartTime,
interactive,
filter = "all",
colorFilter = EMPTY_COLORS,
}) {
// Calculate filtered items and their new positions
const { mappedItems, filteredGridDims } = useMemo(() => {
const spacing = CONFIG.itemSize + CONFIG.gap;
const filteredItems = items.filter((item) =>
matchesFilter(item, filter, colorFilter)
);
const filteredCount = filteredItems.length;
const filteredDims =
calculateGridDimensions(filteredCount);
const maxDelay = gridVisible
? CONFIG.enterStaggerDelay
: CONFIG.exitStaggerDelay;
let filteredIdx = 0;
const mapped = items.map((shoe, i) => {
const matches = matchesFilter(shoe, filter, colorFilter);
let targetPos;
if (matches) {
const col = filteredIdx % CONFIG.gridCols;
const row = Math.floor(
filteredIdx / CONFIG.gridCols
);
targetPos = {
x:
col * spacing -
filteredDims.width / 2 +
spacing / 2,
y:
-(row * spacing) +
filteredDims.height / 2 -
spacing / 2,
};
filteredIdx++;
} else {
const col = i % CONFIG.gridCols;
const row = Math.floor(i / CONFIG.gridCols);
const originalDims = calculateGridDimensions(
items.length
);
targetPos = {
x:
col * spacing -
originalDims.width / 2 +
spacing / 2,
y:
-(row * spacing) +
originalDims.height / 2 -
spacing / 2,
};
}
return {
...shoe,
index: i,
randomDelay: Math.random() * maxDelay,
basePos: targetPos,
matchesFilter: matches,
};
});
return {
mappedItems: mapped,
filteredGridDims: filteredDims,
};
}, [items, filter, colorFilter, gridVisible]);
// --- TIME-SLICED MOUNTING ---
// Start with 0 items rendered for entering grids.
// Every frame, add more items to prevent GPU texture upload spike.
// If EXITING, render everything immediately (no need to stagger out).
const [mountedCount, setMountedCount] = useState(
gridVisible ? 0 : items.length
);
useFrame(() => {
// 5 items per frame @ 60fps = ~12 frames (200ms) to load 60 items.
// Fast enough to be invisible, slow enough to fix the lag.
if (mountedCount < mappedItems.length) {
setMountedCount((prev) =>
Math.min(prev + 5, mappedItems.length)
);
}
});
return (
<>
{mappedItems.map((item, i) => {
// Only render if within the mounted count
if (i > mountedCount) return null;
return (
<ShoeTile
key={item.product_url || item.index}
data={item}
index={item.index}
basePos={item.basePos}
gridVisible={gridVisible}
transitionStartTime={transitionStartTime}
interactive={interactive && item.matchesFilter}
matchesFilter={item.matchesFilter}
gridHeight={filteredGridDims.height}
/>
);
})}
</>
);
}
src/components/grid/Rig.jsx
import { useRef, useEffect } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import * as THREE from "three";
import { easing } from "maath";
import { CONFIG } from "./gridConfig";
import { rigState } from "./gridState";
// --- COMPONENT: RIG ---
// Controls the camera. Moved OUT of GridCanvas so it is persistent.
export function Rig({ gridW, gridH }) {
const { camera, gl } = useThree();
const prevPos = useRef(new THREE.Vector3());
const hasSetInitialZoom = useRef(false);
useEffect(() => {
if (!hasSetInitialZoom.current && rigState.zoom) {
camera.position.z = rigState.zoom;
hasSetInitialZoom.current = true;
}
}, [camera]);
const getBounds = () => {
const dist = camera.position.z;
const vFov = (camera.fov * Math.PI) / 180;
const visibleHeight = 2 * Math.tan(vFov / 2) * dist;
const visibleWidth = visibleHeight * camera.aspect;
const xLimit = Math.max(
0,
(gridW - visibleWidth) / 2 + 2
);
const yLimit = Math.max(
0,
(gridH - visibleHeight) / 2 + 2
);
return { x: xLimit, y: yLimit, visibleHeight };
};
useEffect(() => {
const canvas = gl.domElement;
let isDown = false;
let startX = 0;
let startY = 0;
let initialRigX = 0;
let initialRigY = 0;
let maxDragDistance = 0; // Track max distance for click detection
const onDown = (e) => {
isDown = true;
startX = e.clientX;
startY = e.clientY;
initialRigX = rigState.target.x;
initialRigY = rigState.target.y;
maxDragDistance = 0;
rigState.isDragging = false; // Reset on new gesture
canvas.style.cursor = "grabbing";
};
const onMove = (e) => {
if (!isDown) return;
const dx = e.clientX - startX;
const dy = e.clientY - startY;
const distance = Math.sqrt(dx * dx + dy * dy);
maxDragDistance = Math.max(maxDragDistance, distance);
// Use higher threshold for mobile (touch is less precise)
const threshold =
"ontouchstart" in window
? 15
: CONFIG.clickThreshold;
if (maxDragDistance > threshold) {
rigState.isDragging = true;
rigState.activeId = null;
}
const { x: bx, y: by, visibleHeight } = getBounds();
const sensitivity =
(visibleHeight / window.innerHeight) *
CONFIG.dragSpeed;
let rawTargetX = initialRigX + dx * sensitivity;
let rawTargetY = initialRigY - dy * sensitivity;
// Apply resistance when dragging past bounds
if (rawTargetX > bx)
rawTargetX =
bx + (rawTargetX - bx) * CONFIG.dragResistance;
if (rawTargetX < -bx)
rawTargetX =
-bx + (rawTargetX + bx) * CONFIG.dragResistance;
if (rawTargetY > by)
rawTargetY =
by + (rawTargetY - by) * CONFIG.dragResistance;
if (rawTargetY < -by)
rawTargetY =
-by + (rawTargetY + by) * CONFIG.dragResistance;
// Hard limit on overshoot for snappier mobile feel
const maxOvershoot = 3;
rawTargetX = Math.max(
-bx - maxOvershoot,
Math.min(bx + maxOvershoot, rawTargetX)
);
rawTargetY = Math.max(
-by - maxOvershoot,
Math.min(by + maxOvershoot, rawTargetY)
);
rigState.target.set(rawTargetX, rawTargetY, 0);
};
const onUp = () => {
if (!isDown) return; // Ignore spurious events
isDown = false;
rigState.isDragging = false;
canvas.style.cursor = "grab";
if (rigState.activeId !== null) return;
const { x: bx, y: by } = getBounds();
// When zoomed out, snap to center. When zoomed in, allow panning.
const isZoomedOut =
camera.position.z > CONFIG.zoomIn + 2;
let snapX = isZoomedOut
? 0
: Math.max(-bx, Math.min(bx, rigState.target.x));
let snapY = isZoomedOut
? 2
: Math.max(-by, Math.min(by, rigState.target.y));
rigState.target.set(snapX, snapY, 0);
};
canvas.addEventListener("pointerdown", onDown);
window.addEventListener("pointermove", onMove);
window.addEventListener("pointerup", onUp);
window.addEventListener("pointercancel", onUp); // Handle interrupted touch
return () => {
canvas.removeEventListener("pointerdown", onDown);
window.removeEventListener("pointermove", onMove);
window.removeEventListener("pointerup", onUp);
window.removeEventListener("pointercancel", onUp);
};
}, [gl, camera, gridW, gridH]);
useFrame((state, delta) => {
easing.damp3(
rigState.current,
rigState.target,
CONFIG.dampFactor,
delta
);
easing.damp(
camera.position,
"z",
rigState.zoom,
CONFIG.zoomDamp,
delta
);
rigState.velocity
.copy(rigState.current)
.sub(prevPos.current);
prevPos.current.copy(rigState.current);
const zoomFactor = Math.min(
1,
CONFIG.zoomIn / rigState.zoom
);
const tiltX =
rigState.velocity.y * CONFIG.tiltFactor * zoomFactor;
const tiltY =
-rigState.velocity.x * CONFIG.tiltFactor * zoomFactor;
easing.damp(camera.rotation, "x", tiltX, 0.2, delta);
easing.damp(camera.rotation, "y", tiltY, 0.2, delta);
});
return null;
}
src/components/grid/ShoeGrid.jsx
import React, {
useMemo,
useState,
useEffect,
Suspense,
} from "react";
import { Canvas } from "@react-three/fiber";
import { useTexture } from "@react-three/drei";
import * as THREE from "three";
import { Leva } from "leva";
// --- REAL DATA IMPORT ---
import shoes from "../../../backend/shoes.json";
import MiniMap from "../MiniMap";
import { DEFAULT_CONFIG, CONFIG } from "./gridConfig";
import { rigState, calculateGridDimensions, EMPTY_COLORS, matchesFilter } from "./gridState";
import { useGridConfig } from "./useGridConfig";
import { Rig } from "./Rig";
import { GridCanvas } from "./GridCanvas";
import { UnifiedControlBar } from "../GridUI";
import Header from "../Header";
import { TopologyBackground } from "../TopologyBackground";
import "../HoloCardMaterial"; // Registers <holoCardMaterial /> with R3F
// --- PRELOAD ALL TEXTURES ---
// This ensures all shoe images are cached before switching collections
shoes.forEach((shoe) => {
useTexture.preload(shoe.image_url);
});
// --- MAIN EXPORT ---
export default function ShoeGrid() {
const [zoomTarget, setZoomTarget] = useState(null);
const [initialZoom] = useState(DEFAULT_CONFIG.zoomOut);
const [currentZoom, setCurrentZoom] = useState(
rigState.zoom
);
const controls = useGridConfig();
// Track zoom state for UI components
useEffect(() => {
const interval = setInterval(() => {
setCurrentZoom(rigState.zoom);
}, 50); // Update every 50ms
return () => clearInterval(interval);
}, []);
// Track active selection state
const [hasActiveSelection, setHasActiveSelection] =
useState(false);
useEffect(() => {
const interval = setInterval(() => {
setHasActiveSelection(rigState.activeId !== null);
}, 16); // Update every frame (60fps) for smoother updates
return () => clearInterval(interval);
}, []);
const isZoomedIn = currentZoom <= CONFIG.zoomIn + 0.5;
// Responsive zoom for mobile viewports
useEffect(() => {
const updateResponsiveZoom = () => {
const width = window.innerWidth;
let newZoomOut;
if (width < 480) {
newZoomOut = 48; // Phone
} else if (width < 768) {
newZoomOut = 38; // Tablet portrait
} else {
newZoomOut = DEFAULT_CONFIG.zoomOut; // Desktop default (31)
}
CONFIG.zoomOut = newZoomOut;
// Only update current zoom if we're in zoomed-out state
if (rigState.zoom > CONFIG.zoomIn + 2) {
rigState.zoom = newZoomOut;
setCurrentZoom(newZoomOut);
}
};
updateResponsiveZoom();
window.addEventListener("resize", updateResponsiveZoom);
return () => window.removeEventListener("resize", updateResponsiveZoom);
}, []);
// Filter state for Nike collection
const [nikeFilter, setNikeFilter] = useState("all"); // 'all' | 'jordan' | 'dunk'
const [colorFilter, setColorFilter] = useState(EMPTY_COLORS); // [] = all, ['blue','green'] = blue OR green
// Collections - Nike (all, unfiltered), New Balance, Under $150
const collectionsData = useMemo(() => {
// All Nike shoes (filtering happens in GridCanvas)
const nike = shoes.filter((s) => s.brand === "Nike");
// New Balance shoes - take half and double to make 30+ items
const newBalanceFull = shoes.filter(
(s) => s.brand === "New Balance"
);
const newBalanceHalf = newBalanceFull.slice(0, Math.ceil(newBalanceFull.length / 2));
const newBalance = [
...newBalanceHalf,
...newBalanceHalf.map((s, i) => ({
...s,
product_url: `${s.product_url}-dup-${i}`,
})),
];
// Under $150 (all brands)
const budget = shoes.filter((s) => {
const price = parseInt(
s.price?.replace(/[$,]/g, "") || "999"
);
return price < 150;
});
return [nike, newBalance, budget];
}, []);
// --- Grid Stack State ---
// Instead of one list of items, we keep a stack of "Rendered Layers".
// This allows us to have one layer exiting and one layer entering simultaneously.
// Initial grid uses Nike collection (index 0)
const [gridLayers, setGridLayers] = useState(() => [
{
id: "init",
items: shoes.filter((s) => s.brand === "Nike"),
mode: "enter", // 'enter' | 'exit'
startTime: 0,
},
]);
const [activeCollectionIdx, setActiveCollectionIdx] =
useState(0);
const handleCollectionSwitch = (index) => {
if (index === activeCollectionIdx) return;
const now = Date.now();
setGridLayers((prev) => {
// 1. Mark existing 'enter' layers as 'exit'
const exitingLayers = prev.map((layer) =>
layer.mode === "enter"
? { ...layer, mode: "exit", startTime: now }
: layer
);
// 2. Add new 'enter' layer
const newLayer = {
id: `grid-${index}-${now}`, // Unique ID for key
items: collectionsData[index],
mode: "enter",
startTime: now,
};
return [...exitingLayers, newLayer];
});
setActiveCollectionIdx(index);
// Clear Nike filters when leaving Nike collection
setNikeFilter("all");
setColorFilter(EMPTY_COLORS);
rigState.target.set(0, 2, 0);
rigState.activeId = null;
// 3. Cleanup old layers after transition time
setTimeout(() => {
setGridLayers((prev) =>
prev.filter((layer) => layer.mode === "enter")
);
}, CONFIG.cleanupTimeout);
};
// Handle filter change (for Nike collection) - just update filter state
// The grid will animate items in place
const handleFilterChange = (filter) => {
if (filter === nikeFilter) return;
setNikeFilter(filter);
rigState.activeId = null;
};
// Handle color filter change (accepts array of colors)
const handleColorFilterChange = (colors) => {
setColorFilter(colors.length > 0 ? colors : EMPTY_COLORS);
rigState.activeId = null;
};
useEffect(() => {
if (zoomTarget === "OUT") {
rigState.zoom = CONFIG.zoomOut;
setCurrentZoom(CONFIG.zoomOut);
rigState.target.set(0, 2, 0);
} else if (typeof zoomTarget === "number") {
rigState.zoom = zoomTarget;
setCurrentZoom(zoomTarget);
}
setZoomTarget(null);
}, [zoomTarget]);
// Determine active grid dimensions for the Rig
// We use the dimensions of the LAST layer (the incoming one)
const activeLayer = gridLayers[gridLayers.length - 1];
// Calculate filtered item count for Nike collection
const filteredItemCount = useMemo(() => {
if (activeCollectionIdx !== 0)
return activeLayer.items.length;
return activeLayer.items.filter((item) =>
matchesFilter(item, nikeFilter, colorFilter)
).length;
}, [activeLayer.items, activeCollectionIdx, nikeFilter, colorFilter]);
const activeDims = calculateGridDimensions(
filteredItemCount
);
return (
<div
style={{
width: "100vw",
height: "100vh",
backgroundColor: "#f0f0f0",
position: "relative",
overflow: "hidden",
touchAction: "none", // Prevent mobile browser touch gestures
}}
>
<Leva collapsed={true} hidden={false} />
<Header />
<Canvas
camera={{ position: [0, 0, initialZoom], fov: 45 }}
dpr={[1, 2]}
gl={{
antialias: true,
toneMapping: THREE.NoToneMapping,
}}
>
{/* Rig is now shared, based on the dimensions of the active grid */}
<Rig
gridW={activeDims.width}
gridH={activeDims.height}
/>
{/* Tech Background - geometric lines and crosshairs for CAD/architectural feel */}
<TopologyBackground
isZoomedIn={isZoomedIn}
color={CONFIG.bgColor}
opacity={CONFIG.bgOpacity}
speed={CONFIG.bgSpeed}
scale={CONFIG.bgScale}
lineThickness={CONFIG.bgLineThickness}
/>
<fog
attach="fog"
args={[
"#f0f0f0",
controls?.fogNear ?? DEFAULT_CONFIG.fogNear,
controls?.fogFar ?? DEFAULT_CONFIG.fogFar,
]}
/>
{/* Suspense boundary for texture loading */}
<Suspense fallback={null}>
{/* Render all active layers (Entering + Exiting) */}
{gridLayers.map((layer, layerIdx) => (
<GridCanvas
key={layer.id} // Essential for React to treat them as different trees
items={layer.items}
gridVisible={layer.mode === "enter"}
transitionStartTime={layer.startTime}
interactive={layer.mode === "enter"} // Only entering grid is clickable
filter={
activeCollectionIdx === 0
? nikeFilter
: "all"
}
colorFilter={
activeCollectionIdx === 0
? colorFilter
: EMPTY_COLORS
}
/>
))}
</Suspense>
</Canvas>
<MiniMap
gridDims={activeDims}
rigState={rigState}
config={CONFIG}
totalItems={filteredItemCount}
isZoomedIn={isZoomedIn}
/>
<UnifiedControlBar
currentCollection={activeCollectionIdx}
onSwitch={handleCollectionSwitch}
setZoomTrigger={setZoomTarget}
isZoomedIn={isZoomedIn}
hasActiveSelection={hasActiveSelection}
nikeFilter={nikeFilter}
onFilterChange={handleFilterChange}
/>
</div>
);
}
src/components/grid/ShoeTile.jsx
import React, {
useRef,
useMemo,
useState,
useLayoutEffect,
} from "react";
import { useFrame } from "@react-three/fiber";
import { useTexture, Text } from "@react-three/drei";
import * as THREE from "three";
import { easing } from "maath";
import { CONFIG } from "./gridConfig";
import { rigState } from "./gridState";
import { CloseButton } from "../CloseButton";
// --- OPTIMIZED COMPONENT: SHOE TILE ---
export function ShoeTile({
data,
index,
basePos,
gridVisible,
transitionStartTime,
interactive,
matchesFilter = true,
gridHeight,
}) {
const ref = useRef();
const imageRef = useRef();
const titleRef = useRef();
const priceRef = useRef();
const [hovered, setHovered] = useState(false);
const texture = useTexture(data.image_url);
// Animation Refs
const focusZ = useRef(0);
const rotationX = useRef(0);
const rotationY = useRef(0);
const curveZ = useRef(0);
const transitionZ = useRef(0);
const transitionY = useRef(0);
const breathScale = useRef(1);
// Animated position for filter transitions
const animatedPos = useRef({
x: basePos.x,
y: basePos.y,
});
const filterOpacity = useRef(1);
const filterScale = useRef(1);
// State to track if we should stop processing entirely (optimization)
const isSleep = useRef(false);
// Track if this item was dimmed due to focus mode (for fast recovery)
const wasDimmedByFocus = useRef(false);
// RESET ANIMATION STATE ON MOUNT
useLayoutEffect(() => {
const normalizedY =
gridHeight > 0 ? basePos.y / (gridHeight / 2) : 0;
if (gridVisible) {
transitionZ.current = CONFIG.enterStartZ;
transitionY.current =
normalizedY * CONFIG.enterSpreadY;
if (imageRef.current)
imageRef.current.material.uOpacity =
CONFIG.enterStartOpacity;
isSleep.current = false;
} else {
transitionZ.current = 0;
transitionY.current = 0;
if (imageRef.current)
imageRef.current.material.uOpacity = 1;
}
}, []);
const imageDims = useMemo(() => {
const maxSize = CONFIG.itemSize * 0.9;
if (!texture.image)
return { width: maxSize, height: maxSize };
const imgAspect =
texture.image.width / texture.image.height;
return imgAspect > 1
? { width: maxSize, height: maxSize / imgAspect }
: { width: maxSize * imgAspect, height: maxSize };
}, [texture]);
useFrame((state, delta) => {
// OPTIMIZATION 1: If sleeping or ref missing, stop immediately.
if (!ref.current || isSleep.current) return;
// --- 0. Filter Animation ---
easing.damp(
animatedPos.current,
"x",
basePos.x,
0.2,
delta
);
easing.damp(
animatedPos.current,
"y",
basePos.y,
0.2,
delta
);
const targetFilterOpacity = matchesFilter ? 1 : 0;
const targetFilterScale = matchesFilter ? 1 : CONFIG.filterScaleTarget;
easing.damp(
filterOpacity,
"current",
targetFilterOpacity,
CONFIG.filterOpacityDamp,
delta
);
easing.damp(
filterScale,
"current",
targetFilterScale,
CONFIG.filterOpacityDamp,
delta
);
// Sleep check: If filtered out and visually invisible, stop processing
// Check actual material opacity, not filterOpacity ref, to avoid popping
const actualOpacity = imageRef.current?.material?.uOpacity ?? 1;
if (actualOpacity < 0.01 && !matchesFilter) {
ref.current.visible = false;
return;
}
// --- 1. Stagger Logic ---
const now = Date.now();
const timeSinceTrigger = now - transitionStartTime;
const staggerDelay = data.randomDelay || 0;
const canTransition = timeSinceTrigger > staggerDelay;
// --- 2. Calculate Targets ---
let targetTransitionOpacity = 1.0;
let targetTransitionZ = 0;
const normalizedY =
gridHeight > 0 ? basePos.y / (gridHeight / 2) : 0;
let targetTransitionY = 0;
if (gridVisible) {
// ENTERING
if (canTransition) {
targetTransitionOpacity = 1.0;
targetTransitionZ = 0;
targetTransitionY = 0;
} else {
targetTransitionOpacity = CONFIG.enterStartOpacity;
targetTransitionZ = CONFIG.enterStartZ;
targetTransitionY =
normalizedY * CONFIG.enterSpreadY;
}
} else {
// EXITING
if (canTransition) {
targetTransitionOpacity = 0.0;
targetTransitionZ = CONFIG.exitEndZ;
targetTransitionY =
normalizedY * CONFIG.exitSpreadY;
} else {
targetTransitionOpacity = 1.0;
targetTransitionZ = 0;
targetTransitionY = 0;
}
}
// --- 3. Base Position ---
const x = animatedPos.current.x + rigState.current.x;
const y = animatedPos.current.y + rigState.current.y;
// --- 4. Dynamic Culling ---
const currentCull =
CONFIG.cullDistance * (rigState.zoom / 8);
const isPositionVisible =
Math.abs(x) < currentCull &&
Math.abs(y) < currentCull;
// OPTIMIZATION 2: Strict Visibility Culling
// If exiting and invisible, stop running this loop forever
if (
!gridVisible &&
targetTransitionOpacity < 0.01 &&
filterOpacity.current < 0.01
) {
ref.current.visible = false;
isSleep.current = true;
return;
}
// Standard view culling
if (
!isPositionVisible &&
!(!gridVisible && canTransition)
) {
ref.current.visible = false;
return;
}
// If opacity is effectively 0, hide mesh to save GPU rasterization
if (
imageRef.current?.material.uOpacity < 0.01 &&
targetTransitionOpacity < 0.01
) {
ref.current.visible = false;
return;
}
ref.current.visible = true;
// --- 5. Curvature & Zoom ---
const isZoomedIn = rigState.zoom <= CONFIG.zoomIn + 0.5;
const maxZoom = CONFIG.zoomOut || 50;
const zoomRatio = isZoomedIn
? 0
: THREE.MathUtils.clamp(
(rigState.zoom - CONFIG.zoomIn) /
(maxZoom - CONFIG.zoomIn),
0,
1
);
const smoothRatio = easing.cubic.inOut(zoomRatio);
const distSq = x * x + y * y;
const dist = Math.sqrt(distSq);
const targetCurveZ =
-distSq * CONFIG.curvatureStrength * smoothRatio;
// Optimization: Skip complex rotation math if fading out
let rotX = 0,
rotY = 0;
if (targetTransitionOpacity > 0.1) {
const rotationIntensity =
Math.min(dist * 0.4, 2.0) * smoothRatio;
rotX =
y *
CONFIG.curvatureStrength *
CONFIG.rotationStrength *
rotationIntensity;
rotY =
-x *
CONFIG.curvatureStrength *
CONFIG.rotationStrength *
rotationIntensity;
}
// --- 6. Interaction State ---
const isFocusMode = rigState.activeId !== null;
const isActive = rigState.activeId === index;
const isHovered = hovered && interactive;
let interactionScale = 1.0;
let interactionOpacity = 1.0;
let targetTextOpacity = 0;
let targetFocusZ = 0;
if (isFocusMode) {
if (isActive) {
interactionScale = CONFIG.focusScale;
interactionOpacity = 1.0;
targetTextOpacity = 1.0;
targetFocusZ = 2;
} else {
interactionScale = CONFIG.dimScale;
interactionOpacity = CONFIG.dimOpacity;
targetTextOpacity = 0;
targetFocusZ = -0.5;
// Track that this item was dimmed
wasDimmedByFocus.current = true;
}
} else {
interactionScale =
isHovered && !rigState.isDragging ? 1.05 : 1.0;
targetFocusZ =
isHovered && !rigState.isDragging ? 0.5 : 0;
}
const finalOpacity =
interactionOpacity *
targetTransitionOpacity *
filterOpacity.current;
const combinedScale =
interactionScale * filterScale.current;
// --- 7. Apply Animations ---
easing.damp(
ref.current.scale,
"x",
combinedScale,
0.15,
delta
);
easing.damp(
ref.current.scale,
"y",
combinedScale,
0.15,
delta
);
easing.damp(
focusZ,
"current",
targetFocusZ,
0.2,
delta
);
easing.damp(
curveZ,
"current",
targetCurveZ,
0.2,
delta
);
easing.damp(
transitionZ,
"current",
targetTransitionZ,
CONFIG.transitionZDamp,
delta
);
easing.damp(
transitionY,
"current",
targetTransitionY,
CONFIG.transitionYDamp,
delta
);
ref.current.position.set(
x,
y + transitionY.current,
curveZ.current + focusZ.current + transitionZ.current
);
easing.damp(rotationX, "current", rotX, 0.2, delta);
easing.damp(rotationY, "current", rotY, 0.2, delta);
ref.current.rotation.set(
rotationX.current,
rotationY.current,
0
);
if (imageRef.current) {
// Update shader uniforms
imageRef.current.material.uTime =
state.clock.elapsedTime;
// Smoothly animate active state for shader effects
const isActive = rigState.activeId === index;
const activeDamp = isActive ? 0.6 : 0.15; // Slow open, fast close
easing.damp(
imageRef.current.material,
"uActive",
isActive ? 1 : 0,
activeDamp,
delta
);
// Animate opacity via shader uniform
// Use faster damp for filter transitions and focus recovery, slower for grid enter/exit
let opacityDamp;
const isFilterTransition = !matchesFilter || filterOpacity.current < 0.99;
// Focus recovery: item was dimmed and is now recovering
const isFocusRecovery = !isFocusMode && wasDimmedByFocus.current;
if (isFilterTransition && gridVisible) {
// Filtering in or out - use filter damp for faster fade
opacityDamp = CONFIG.filterOpacityDamp;
} else if (isFocusRecovery && gridVisible) {
// Recovering from dimmed state after deselection - use faster damp
opacityDamp = CONFIG.filterOpacityDamp;
// Reset flag once opacity is recovered
if (imageRef.current.material.uOpacity > 0.95) {
wasDimmedByFocus.current = false;
}
} else if (gridVisible) {
opacityDamp = CONFIG.enterOpacityDamp;
} else {
opacityDamp = CONFIG.exitOpacityDamp;
}
easing.damp(
imageRef.current.material,
"uOpacity",
finalOpacity,
opacityDamp,
delta
);
}
// Only update text opacity if text is actually rendered
if (gridVisible) {
const textTarget =
targetTransitionOpacity < 0.8
? 0
: targetTextOpacity;
if (titleRef.current)
easing.damp(
titleRef.current,
"fillOpacity",
textTarget,
0.1,
delta
);
if (priceRef.current)
easing.damp(
priceRef.current,
"fillOpacity",
textTarget,
0.1,
delta
);
// Breathing animation for text when active
const isActiveItem = rigState.activeId === index;
const targetBreath = isActiveItem
? 1 +
Math.sin(state.clock.elapsedTime * 2.0) * 0.035
: 1;
easing.damp(
breathScale,
"current",
targetBreath,
0.1,
delta
);
// Apply breathing scale to text
if (titleRef.current) {
titleRef.current.scale.setScalar(
breathScale.current
);
}
if (priceRef.current) {
priceRef.current.scale.setScalar(
breathScale.current
);
}
}
});
const handleClick = (e) => {
if (!interactive) return;
if (rigState.isDragging) {
e.stopPropagation();
return;
}
e.stopPropagation();
if (rigState.activeId === index) {
rigState.activeId = null;
} else {
const isZoomedOut = rigState.zoom > CONFIG.zoomIn + 2;
rigState.target.set(-basePos.x, -basePos.y, 0);
if (isZoomedOut) {
rigState.activeId = index;
rigState.zoom = CONFIG.zoomIn;
} else {
rigState.activeId = index;
}
}
};
const textY = -(imageDims.height / 2) - 0.25;
const isActive = rigState.activeId === index;
return (
<group ref={ref}>
<mesh
onPointerOver={() => setHovered(true)}
onPointerOut={() => setHovered(false)}
onClick={handleClick}
>
<planeGeometry
args={[
imageDims.width * 1.1,
imageDims.height * 1.1,
]}
/>
<meshBasicMaterial visible={false} />
</mesh>
<mesh ref={imageRef}>
<planeGeometry
args={[imageDims.width, imageDims.height, 16, 16]}
/>
<holoCardMaterial
transparent={true}
uTexture={texture}
/>
</mesh>
{/* OPTIMIZATION 3: CONDITIONAL TEXT RENDERING */}
{/* Do NOT render text if the grid is exiting. Saves massive CPU overhead. */}
{gridVisible && (
<>
<Text
ref={titleRef}
position={[0, textY, 0.01]}
fontSize={0.1}
color="#000"
anchorY="top"
anchorX="center"
maxWidth={2.5}
fillOpacity={0}
>
{data.title}
</Text>
{data.price && (
<Text
ref={priceRef}
position={[0, textY - 0.22, 0.01]}
fontSize={0.09}
color="#555"
anchorY="top"
anchorX="center"
fillOpacity={0}
>
{data.price}
</Text>
)}
</>
)}
<CloseButton
isActive={isActive}
position={[
imageDims.width / 2 - 0.15,
imageDims.height / 2 - 0.15,
0.02,
]}
onClose={() => {
rigState.activeId = null;
}}
/>
</group>
);
}
src/components/grid/gridConfig.js
// --- CONFIGURATION ---
// Default values - will be overridden by controls
export const DEFAULT_CONFIG = {
gridCols: 8,
itemSize: 2.5,
gap: 0.4,
// Physics
dragSpeed: 2.2,
dampFactor: 0.2,
tiltFactor: 0.08,
clickThreshold: 5,
dragResistance: 0.25,
// Camera / Zoom
zoomIn: 12,
zoomOut: 31, // Starting zoom level (configurable via Leva)
zoomDamp: 0.25,
// Visuals
focusScale: 1.5,
dimScale: 0.5,
dimOpacity: 0.15,
// 3D Curvature Effect
curvatureStrength: 0.06, // How much it curves back
rotationStrength: 0, // How much tiles rotate to face center
// Culling
cullDistance: 14,
// Minimap
mapWidth: 120,
mapDotSize: 2,
// Fog
fogNear: 19,
fogFar: 100,
// Animation
enterStartOpacity: 0.0,
enterStartZ: -50,
exitEndZ: 20,
transitionZDamp: 0.25,
enterOpacityDamp: 0.85,
exitOpacityDamp: 0.15,
enterStaggerDelay: 400,
exitStaggerDelay: 300,
cleanupTimeout: 700,
exitSpreadY: 0.5, // How far top/bottom items spread vertically when exiting
enterSpreadY: 1, // How far items start spread when entering (then settle to 0)
transitionYDamp: 0.08, // Y spread animates faster than Z (lower = faster)
filterOpacityDamp: 0.06, // Filter fade out (lower = faster, used when filtering within collection)
filterScaleTarget: 0.5, // Scale target when filtered out (smaller = more dramatic shrink)
// Tech Background
bgColor: "#e0e0e0",
bgOpacity: 0.4,
bgSpeed: 0.05,
bgScale: 3.0,
bgLineThickness: 0.03,
};
// Create a ref to hold the current config so it can be updated
export let CONFIG = { ...DEFAULT_CONFIG };
// Initialize CONFIG with defaults
Object.assign(CONFIG, DEFAULT_CONFIG);
src/components/grid/gridState.js
import * as THREE from "three";
import { CONFIG } from "./gridConfig";
// --- GLOBAL STATE ---
export const rigState = {
target: new THREE.Vector3(0, 2, 0),
current: new THREE.Vector3(0, 2, 0),
velocity: new THREE.Vector3(0, 0, 0),
zoom: CONFIG.zoomOut,
isDragging: false,
activeId: null,
};
// --- HELPER: Grid Dimensions ---
export const calculateGridDimensions = (count) => {
const rows = Math.ceil(count / CONFIG.gridCols);
const spacing = CONFIG.itemSize + CONFIG.gap;
return {
width: CONFIG.gridCols * spacing,
height: rows * spacing,
};
};
// Stable empty array to avoid unnecessary re-renders
export const EMPTY_COLORS = [];
// --- HELPER: Check if item matches filter ---
export const matchesFilter = (item, filter, colorFilter = EMPTY_COLORS) => {
// Check type filter (jordan/dunk)
let matchesType = true;
if (filter !== "all") {
const title = item.title.toLowerCase();
if (filter === "jordan") matchesType = title.includes("jordan");
else if (filter === "dunk") matchesType = title.includes("dunk");
}
// Check color filter (array - OR logic across colors)
let matchesColor = true;
if (colorFilter.length > 0) {
const shoeColor = item.primary_color || "";
matchesColor = colorFilter.some((c) => {
// Match gray variants (gray, dark_gray, light_gray) when "gray" is selected
if (c === "gray") return shoeColor.includes("gray");
return shoeColor === c;
});
}
return matchesType && matchesColor;
};
src/components/grid/useGridConfig.js
import { useEffect } from "react";
import { useControls } from "leva";
import { DEFAULT_CONFIG, CONFIG } from "./gridConfig";
// Hook to set up Leva controls and sync them to CONFIG
export function useGridConfig() {
// Controls for main config values
const controls = useControls(
"Shoe Grid",
{
curvatureStrength: {
value: DEFAULT_CONFIG.curvatureStrength,
min: 0,
max: 0.2,
step: 0.001,
label: "Curvature Strength",
},
rotationStrength: {
value: DEFAULT_CONFIG.rotationStrength,
min: 0,
max: 5,
step: 0.1,
label: "Rotation Strength",
},
focusScale: {
value: DEFAULT_CONFIG.focusScale,
min: 1,
max: 3,
step: 0.1,
label: "Focus Scale",
},
dimScale: {
value: DEFAULT_CONFIG.dimScale,
min: 0,
max: 1,
step: 0.05,
label: "Dim Scale",
},
dimOpacity: {
value: DEFAULT_CONFIG.dimOpacity,
min: 0,
max: 1,
step: 0.05,
label: "Dim Opacity",
},
dragSpeed: {
value: DEFAULT_CONFIG.dragSpeed,
min: 0.1,
max: 3,
step: 0.1,
label: "Drag Speed",
},
dampFactor: {
value: DEFAULT_CONFIG.dampFactor,
min: 0.05,
max: 0.5,
step: 0.05,
label: "Damp Factor",
},
tiltFactor: {
value: DEFAULT_CONFIG.tiltFactor,
min: 0,
max: 0.2,
step: 0.01,
label: "Tilt Factor",
},
zoomIn: {
value: DEFAULT_CONFIG.zoomIn,
min: 5,
max: 30,
step: 1,
label: "Zoom In",
},
zoomDamp: {
value: DEFAULT_CONFIG.zoomDamp,
min: 0.05,
max: 0.5,
step: 0.05,
label: "Zoom Damp",
},
zoomOut: {
value: DEFAULT_CONFIG.zoomOut,
min: 10,
max: 100,
step: 1,
label: "Zoom Out",
},
},
{ collapsed: true, order: 0 }
);
// Animation controls
const animationControls = useControls(
"Transition",
{
enterStartOpacity: {
value: DEFAULT_CONFIG.enterStartOpacity,
min: 0,
max: 1,
step: 0.05,
label: "Enter Start Opacity",
},
enterStartZ: {
value: DEFAULT_CONFIG.enterStartZ,
min: -50,
max: 0,
step: 1,
label: "Enter Start Z",
},
exitEndZ: {
value: DEFAULT_CONFIG.exitEndZ,
min: 0,
max: 50,
step: 1,
label: "Exit End Z",
},
transitionZDamp: {
value: DEFAULT_CONFIG.transitionZDamp,
min: 0.05,
max: 1,
step: 0.05,
label: "Transition Z Damp",
},
enterOpacityDamp: {
value: DEFAULT_CONFIG.enterOpacityDamp,
min: 0.05,
max: 1,
step: 0.05,
label: "Enter Opacity Damp",
},
exitOpacityDamp: {
value: DEFAULT_CONFIG.exitOpacityDamp,
min: 0.05,
max: 1,
step: 0.05,
label: "Exit Opacity Damp",
},
enterStaggerDelay: {
value: DEFAULT_CONFIG.enterStaggerDelay,
min: 0,
max: 2000,
step: 50,
label: "Enter Stagger Delay (ms)",
},
exitStaggerDelay: {
value: DEFAULT_CONFIG.exitStaggerDelay,
min: 0,
max: 1000,
step: 50,
label: "Exit Stagger Delay (ms)",
},
cleanupTimeout: {
value: DEFAULT_CONFIG.cleanupTimeout,
min: 500,
max: 3000,
step: 100,
label: "Cleanup Timeout (ms)",
},
exitSpreadY: {
value: DEFAULT_CONFIG.exitSpreadY,
min: 0,
max: 10,
step: 0.5,
label: "Exit Spread Y",
},
enterSpreadY: {
value: DEFAULT_CONFIG.enterSpreadY,
min: 0,
max: 10,
step: 0.5,
label: "Enter Spread Y",
},
transitionYDamp: {
value: DEFAULT_CONFIG.transitionYDamp,
min: 0.01,
max: 0.5,
step: 0.01,
label: "Y Damp (lower=faster)",
},
filterOpacityDamp: {
value: DEFAULT_CONFIG.filterOpacityDamp,
min: 0.01,
max: 0.3,
step: 0.01,
label: "Filter Opacity Damp",
},
},
{ collapsed: true, order: 1 }
);
// TopologyBackground controls
const techBgControls = useControls(
"BG Shader",
{
bgColor: {
value: "#e0e0e0",
label: "Color",
},
bgOpacity: {
value: 0.4,
min: 0,
max: 1,
step: 0.05,
label: "Opacity",
},
bgSpeed: {
value: 0.05,
min: 0,
max: 0.2,
step: 0.01,
label: "Speed",
},
bgScale: {
value: 3.0,
min: 1,
max: 10,
step: 0.5,
label: "Scale",
},
bgLineThickness: {
value: 0.03,
min: 0.01,
max: 0.1,
step: 0.01,
label: "Thickness",
},
},
{ collapsed: true, order: 2 }
);
// Update CONFIG object when controls change
useEffect(() => {
if (controls) {
CONFIG.curvatureStrength =
controls.curvatureStrength ??
DEFAULT_CONFIG.curvatureStrength;
CONFIG.rotationStrength =
controls.rotationStrength ??
DEFAULT_CONFIG.rotationStrength;
CONFIG.fogNear =
controls.fogNear ?? DEFAULT_CONFIG.fogNear;
CONFIG.fogFar =
controls.fogFar ?? DEFAULT_CONFIG.fogFar;
CONFIG.focusScale =
controls.focusScale ?? DEFAULT_CONFIG.focusScale;
CONFIG.dimScale =
controls.dimScale ?? DEFAULT_CONFIG.dimScale;
CONFIG.dimOpacity =
controls.dimOpacity ?? DEFAULT_CONFIG.dimOpacity;
CONFIG.dragSpeed =
controls.dragSpeed ?? DEFAULT_CONFIG.dragSpeed;
CONFIG.dampFactor =
controls.dampFactor ?? DEFAULT_CONFIG.dampFactor;
CONFIG.tiltFactor =
controls.tiltFactor ?? DEFAULT_CONFIG.tiltFactor;
CONFIG.zoomIn =
controls.zoomIn ?? DEFAULT_CONFIG.zoomIn;
CONFIG.zoomDamp =
controls.zoomDamp ?? DEFAULT_CONFIG.zoomDamp;
CONFIG.zoomOut =
controls.zoomOut ?? DEFAULT_CONFIG.zoomOut;
}
if (animationControls) {
CONFIG.enterStartOpacity =
animationControls.enterStartOpacity ??
DEFAULT_CONFIG.enterStartOpacity;
CONFIG.enterStartZ =
animationControls.enterStartZ ??
DEFAULT_CONFIG.enterStartZ;
CONFIG.exitEndZ =
animationControls.exitEndZ ??
DEFAULT_CONFIG.exitEndZ;
CONFIG.transitionZDamp =
animationControls.transitionZDamp ??
DEFAULT_CONFIG.transitionZDamp;
CONFIG.enterOpacityDamp =
animationControls.enterOpacityDamp ??
DEFAULT_CONFIG.enterOpacityDamp;
CONFIG.exitOpacityDamp =
animationControls.exitOpacityDamp ??
DEFAULT_CONFIG.exitOpacityDamp;
CONFIG.enterStaggerDelay =
animationControls.enterStaggerDelay ??
DEFAULT_CONFIG.enterStaggerDelay;
CONFIG.exitStaggerDelay =
animationControls.exitStaggerDelay ??
DEFAULT_CONFIG.exitStaggerDelay;
CONFIG.cleanupTimeout =
animationControls.cleanupTimeout ??
DEFAULT_CONFIG.cleanupTimeout;
CONFIG.exitSpreadY =
animationControls.exitSpreadY ??
DEFAULT_CONFIG.exitSpreadY;
CONFIG.enterSpreadY =
animationControls.enterSpreadY ??
DEFAULT_CONFIG.enterSpreadY;
CONFIG.transitionYDamp =
animationControls.transitionYDamp ??
DEFAULT_CONFIG.transitionYDamp;
CONFIG.filterOpacityDamp =
animationControls.filterOpacityDamp ??
DEFAULT_CONFIG.filterOpacityDamp;
}
if (techBgControls) {
CONFIG.bgColor = techBgControls.bgColor ?? "#e0e0e0";
CONFIG.bgOpacity = techBgControls.bgOpacity ?? 0.4;
CONFIG.bgSpeed = techBgControls.bgSpeed ?? 0.05;
CONFIG.bgScale = techBgControls.bgScale ?? 3.0;
CONFIG.bgLineThickness =
techBgControls.bgLineThickness ?? 0.03;
}
}, [controls, animationControls, techBgControls]);
return controls;
}
src/pages/_app.js
import "@/styles/globals.css";
import { Inter } from "next/font/google";
const inter = Inter({ subsets: ["latin"] });
export default function App({ Component, pageProps }) {
return (
<main className={inter.className}>
<Component {...pageProps} />
</main>
);
}
src/pages/_document.js
import { Html, Head, Main, NextScript } from "next/document";
export default function Document() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
src/pages/index.js
import Head from "next/head";
import dynamic from "next/dynamic";
// Dynamic import to avoid SSR issues with R3F
const ShoeGrid = dynamic(() => import("@/components/grid/ShoeGrid"), {
ssr: false,
});
export default function HomePage() {
return (
<>
<Head>
<title>Shoe Finder</title>
<meta name="description" content="Explore our endless collection of sneakers" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<ShoeGrid />
</>
);
}
src/shaders/holocard.frag
uniform sampler2D uTexture;
uniform float uOpacity;
uniform float uActive;
varying vec2 vUv;
void main() {
vec4 texColor = texture2D(uTexture, vUv);
vec3 baseColor = texColor.rgb;
if (uActive < 0.01) {
gl_FragColor = vec4(baseColor, texColor.a * uOpacity);
return;
}
// Light band sweep - driven by uActive transition (0->1)
float diagonal = (vUv.x * 0.8) + vUv.y;
float sheenPos = uActive * 2.5;
float sheenWidth = 0.5;
float dist = abs(diagonal - sheenPos);
float intensity = 1.0 - smoothstep(0.0, sheenWidth, dist);
intensity = pow(intensity, 3.0);
// Fade out the sheen as uActive approaches 1 (sweep complete)
float sheenFade = 1.0 - smoothstep(0.7, 1.0, uActive);
vec3 sheenColor = vec3(0.85, 0.92, 1.0) * intensity * 0.9 * sheenFade;
vec3 finalColor = baseColor + sheenColor * texColor.a;
gl_FragColor = vec4(finalColor, texColor.a * uOpacity);
}
src/shaders/holocard.vert
varying vec2 vUv;
uniform float uTime;
uniform float uActive;
void main() {
vUv = uv;
vec3 pos = position;
// "Breathing" - only when active
float breath = sin(uTime * 2.0) * 0.015 * uActive;
float scale = 1.0 + breath;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos * scale, 1.0);
}
src/shaders/topography.frag
#pragma glslify: snoise = require('glsl-noise/simplex/2d')
uniform float uTime;
uniform vec3 uColor;
uniform vec2 uResolution;
uniform float uOpacity;
uniform float uLineOpacity;
uniform float uScale;
uniform float uLineThickness;
varying vec2 vUv;
void main() {
vec2 uv = vUv;
// Correct aspect ratio based on the plane dimensions
float aspect = uResolution.x / uResolution.y;
vec2 noiseUv = uv;
noiseUv.x *= aspect;
// Circle mask (aspect-corrected to stay perfectly round)
vec2 centeredUv = uv - 0.5;
centeredUv.x *= aspect;
float dist = length(centeredUv);
float radius = 0.6;
float mask = 1.0 - smoothstep(radius - 0.01, radius + 0.01, dist);
// Noise Generation
float n = snoise(noiseUv * uScale + uTime * 0.05);
// Isolines
float lines = fract(n * 5.0);
float pattern = smoothstep(0.5 - uLineThickness, 0.5, lines) - smoothstep(0.5, 0.5 + uLineThickness, lines);
// Opacity
float opacity = uLineOpacity;
// Grain
float grain = (fract(sin(dot(vUv, vec2(12.9898, 78.233) * 2.0)) * 43758.5453) - 0.5) * 0.15;
vec3 finalColor = uColor + grain;
gl_FragColor = vec4(finalColor, pattern * opacity * mask * uOpacity);
}
src/shaders/topography.vert
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
src/styles/globals.css
@import "tailwindcss";
@theme {
--animate-accordion-down: accordion-down 0.2s ease-out;
--animate-accordion-up: accordion-up 0.2s ease-out;
}
@keyframes accordion-down {
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
}
@keyframes accordion-up {
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
}
/* Dialog animations */
@keyframes dialog-overlay-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes dialog-content-in {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
}
.dialog-overlay {
animation: dialog-overlay-in 0.15s ease-out;
}
.dialog-content {
animation: dialog-content-in 0.15s ease-out;
}
:root {
--background: #ffffff;
--foreground: #171717;
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
html,
body {
max-width: 100vw;
overflow-x: hidden;
}
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
a {
color: inherit;
text-decoration: none;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
}
Original author attribution실행 안내·자료
From Flat to Spatial: Creating a 3D Product Grid with React Three Fiber
Original author: Matt Greenberg
Published by Codrops. Copyright (c) 2026 Codrops.
License: MIT, pursuant to the publisher's downloadable-demo grant.
Keep CODROPS-MIT-2026.txt and the original project notices with copies.
Third-party media exceptions and credits are recorded separately in the source inventory.
external/troika-default/sans-serif.normal.400.woff — OFL-1.1실행 안내·자료
큰 소스 파일과 이미지·모델 등의 자료는 파일을 저장해 확인할 수 있어요.
Bundled dependency licenses실행 안내·자료
react@19.2.3 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
scheduler@0.27.0 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
react-dom@19.2.3 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
three@0.182.0 — LICENSE
The MIT License
Copyright © 2010-2025 three.js authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
use-sync-external-store@1.7.0 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
zustand@5.0.15 — LICENSE
MIT License
Copyright (c) 2019 Paul Henschel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
suspend-react@0.1.3 — LICENSE
MIT License
Copyright (c) 2021 Paul Henschel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
its-fine@2.0.0 — LICENSE
MIT License
Copyright (c) 2022-2025 Poimandres
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
react-use-measure@2.1.7 — LICENSE
MIT License
Copyright (c) 2019-2025 Poimandres
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@babel/runtime@7.29.7 — LICENSE
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@react-three/drei@10.7.7 — LICENSE
MIT License
Copyright (c) 2020 react-spring
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@use-gesture/core@10.3.1 — LICENSE
Copyright (c) 2018-present Paul Henschel <drcmda@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@use-gesture/react@10.3.1 — LICENSE
Copyright (c) 2018-present Paul Henschel <drcmda@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
three-stdlib@2.36.1 — LICENSE
MIT License
Copyright (c) 2021-2023 Poimandres
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
potpack@1.0.2 — LICENSE
ISC License
Copyright (c) 2018, Mapbox
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
fflate@0.6.11 — LICENSE
MIT License
Copyright (c) 2020 Arjun Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
troika-worker-utils@0.52.0 — LICENSE
MIT License
Copyright (c) 2019 ProtectWise
Copyright (c) 2021 Jason Johnston
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
webgl-sdf-generator@1.1.1 — LICENSE.txt
Copyright (c) 2021 Jason Johnston
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
bidi-js@1.1.0 — LICENSE.txt
Copyright (c) 2021 Jason Johnston
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
troika-three-utils@0.52.5 — LICENSE
MIT License
Copyright (c) 2019 ProtectWise
Copyright (c) 2021 Jason Johnston
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
troika-three-text@0.52.5 — LICENSE
MIT License
Copyright (c) 2019 ProtectWise
Copyright (c) 2021 Jason Johnston
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
meshline@3.3.1 — LICENSE
MIT License
Copyright (c) 2016 Jaume Sanchez
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
camera-controls@3.1.2 — LICENSE
MIT License
Copyright (c) 2017 @yomotsu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
hls.js@1.7.3 — LICENSE
Copyright (c) 2017 Dailymotion (http://www.dailymotion.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
src/remux/mp4-generator.js and src/demux/exp-golomb.ts implementation in this project
are derived from the HLS library for video.js (https://github.com/videojs/videojs-contrib-hls)
That work is also covered by the Apache 2 License, following copyright:
Copyright (c) 2013-2015 Brightcove
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
stats.js@0.17.0 — LICENSE
The MIT License
Copyright (c) 2009-2016 stats.js authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
detect-gpu@5.0.70 — LICENSE
MIT License
Copyright (c) 2020 Tim van Scherpenzeel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
three-mesh-bvh@0.8.3 — LICENSE
MIT License
Copyright (c) 2018 Garrett Johnson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@monogrid/gainmap-js@3.4.0 — LICENSE
MIT License
Copyright (c) 2023 MONOGRID
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
zustand@4.5.7 — LICENSE
MIT License
Copyright (c) 2019 Paul Henschel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
tunnel-rat@0.1.2 — LICENSE
MIT License
Copyright (c) 2022 Poimandres
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-compose-refs@1.1.5 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-slot@1.3.3 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-primitive@2.1.10 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-use-layout-effect@1.1.4 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-portal@1.1.17 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
dequal@2.0.3 — license
The MIT License (MIT)
Copyright (c) Luke Edwards <luke.edwards05@gmail.com> (lukeed.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
zustand@3.7.2 — LICENSE
MIT License
Copyright (c) 2019 Paul Henschel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
v8n@1.5.1 — LICENSE
MIT License
Copyright (c) 2018 Bruno C. Couto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/primitive@1.1.7 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-context@1.2.2 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-use-callback-ref@1.1.4 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-dismissable-layer@1.1.19 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-id@1.1.4 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@floating-ui/utils@0.2.12 — LICENSE
MIT License
Copyright (c) 2021-present Floating UI contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@floating-ui/core@1.8.0 — LICENSE
MIT License
Copyright (c) 2021-present Floating UI contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@floating-ui/dom@1.8.0 — LICENSE
MIT License
Copyright (c) 2021-present Floating UI contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@floating-ui/react-dom@2.1.9 — LICENSE
MIT License
Copyright (c) 2021-present Floating UI contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@radix-ui/react-arrow@1.1.15 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-use-size@1.1.4 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-popper@1.3.7 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-presence@1.1.10 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-use-effect-event@0.0.5 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-use-controllable-state@1.2.6 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-visually-hidden@1.2.11 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@radix-ui/react-tooltip@1.2.16 — LICENSE
MIT License
Copyright (c) 2022 WorkOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
leva@0.10.1 — LICENSE
MIT License
Copyright (c) 2021 Poimandres
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
colord@2.10.0 — LICENSE.md
MIT License
Copyright (c) 2020 Vlad Shilov omgovich@ya.ru
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
react-colorful@5.8.1 — LICENSE
MIT License
Copyright (c) 2020-present Vlad Shilov <omgovich@ya.ru>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
prop-types@15.8.1 — LICENSE
MIT License
Copyright (c) 2013-present, Facebook, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
tslib@2.8.1 — LICENSE.txt
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
file-selector@0.5.0 — LICENSE
MIT License
Copyright (c) 2020 Roland Groza
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
attr-accept@2.2.5 — LICENSE
The MIT License (MIT)
Copyright (c) 2015 Andrey Okonetchnikov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
react-dropzone@12.1.0 — LICENSE
The MIT License (MIT)
Copyright (c) 2018 Param Aggarwal
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
isobject@3.0.1 — LICENSE
The MIT License (MIT)
Copyright (c) 2014-2017, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
is-plain-object@2.0.4 — LICENSE
The MIT License (MIT)
Copyright (c) 2014-2017, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
is-extendable@1.0.1 — LICENSE
The MIT License (MIT)
Copyright (c) 2015-2017, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
for-in@1.0.2 — LICENSE
The MIT License (MIT)
Copyright (c) 2014-2017, Jon Schlinkert
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
mixin-deep@1.3.2 — LICENSE
The MIT License (MIT)
Copyright (c) 2014-2015, 2017, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
get-value@2.0.6 — LICENSE
The MIT License (MIT)
Copyright (c) 2014-2016, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
assign-symbols@1.0.0 — LICENSE
The MIT License (MIT)
Copyright (c) 2015, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
extend-shallow@3.0.2 — LICENSE
The MIT License (MIT)
Copyright (c) 2014-2015, 2017, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
split-string@3.1.0 — LICENSE
The MIT License (MIT)
Copyright (c) 2015-2017, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
is-extendable@0.1.1 — LICENSE
The MIT License (MIT)
Copyright (c) 2015, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
extend-shallow@2.0.1 — LICENSE
The MIT License (MIT)
Copyright (c) 2014-2015, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
is-extendable@0.1.1 — LICENSE
The MIT License (MIT)
Copyright (c) 2015, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
set-value@2.0.1 — LICENSE
The MIT License (MIT)
Copyright (c) 2014-2017, Jon Schlinkert
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
merge-value@1.0.0 — LICENSE
The MIT License (MIT)
Copyright (c) 2015-2017, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
framer-motion@12.25.0 — LICENSE.md
The MIT License (MIT)
Copyright (c) 2018 Framer B.V.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
motion-utils@12.24.10 — LICENSE.md
The MIT License (MIT)
Copyright (c) 2024 [Motion](https://motion.dev) B.V.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
motion-dom@12.24.11 — LICENSE.md
The MIT License (MIT)
Copyright (c) 2024 [Motion](https://motion.dev) B.V.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
BEHIND THE EXAMPLE
이 장면을 만드는 원리
드래그 한계와 확대 상태에 따른 놓기 위치
드래그로 가능한 작업에 클릭·탭과 키보드의 별도 경로를 설계합니다.
- 이 예제에서는
- Rig는 최대 이동 거리로 클릭과 드래그를 구분하고 경계 밖 이동에 저항을 줍니다. onUp은 확대가 풀린 상태에서는 중심으로, 확대 상태에서는 범위 안으로 target을 되돌립니다.
