diff --git a/app/components/Cursors.tsx b/app/components/Cursors.tsx new file mode 100644 index 0000000..1e74e6d --- /dev/null +++ b/app/components/Cursors.tsx @@ -0,0 +1,102 @@ +import { + ArrowTopRightOnSquareIcon, + AtSymbolIcon, +} from '@heroicons/react/24/outline'; +import { type Variant, motion } from 'framer-motion'; +import React from 'react'; + +import { FadeIn, FadeInDirection } from '~/components/FadeIn'; + +export type CustomCursor = Variant & { isRounded?: boolean }; + +export function ContactUsVariant(): React.ReactNode { + return ( +
+
+ + + +
+
+ +

+ Contact Us +

+
+
+
+ ); +} + +export function GithubVariant(): React.ReactNode { + return ( +
+
+ + + + + +
+
+ +

+ Github{' '} + +

+
+
+
+ ); +} + +export function DribbbleVariant(): React.ReactNode { + return ( +
+
+ + + + + + +
+
+ +

+ Dribbble{' '} + +

+
+
+
+ ); +} diff --git a/app/context/CursorContext.tsx b/app/context/CursorContext.tsx new file mode 100644 index 0000000..f374316 --- /dev/null +++ b/app/context/CursorContext.tsx @@ -0,0 +1,323 @@ +import type { CursorState } from 'ahooks/lib/useMouse'; +import clsx from 'clsx'; +import { motion } from 'framer-motion'; +import { get } from 'lodash-es'; +import { createContext } from 'react'; +import * as React from 'react'; + +import type { CustomCursor } from '~/components/Cursors'; +import { + ContactUsVariant, + DribbbleVariant, + GithubVariant, +} from '~/components/Cursors'; +import { FramerMotionIcon } from '~/icons/FramerMotionIcon'; +import { NestJsIcon } from '~/icons/NestJsIcon'; +import { ReactIcon } from '~/icons/ReactIcon'; +import { TailwindIcon } from '~/icons/TailwindIcon'; +import { TypescriptIcon } from '~/icons/TypescriptIcon'; +import { VueIcon } from '~/icons/VueIcon'; +import type { GrayscaleImageProps } from '~/lib/GrayscaleImage'; + +export enum CursorVariants { + CONTACT = 'CONTACT', + CONTACT_CTA = 'CONTACT_CTA', + DEFAULT = 'DEFAULT', + DRIBBBLE = 'DRIBBBLE', + FRAMER_MOTION = 'FRAMER_MOTION', + GITHUB = 'GITHUB', + HIDDEN = 'HIDDEN', + IMG = 'IMG', + NEST_JS = 'NEST_JS', + REACT = 'REACT', + SHRUG = 'SHRUG', + TAILWIND = 'TAILWIND', + TYPESCRIPT = 'TYPESCRIPT', + VUE = 'VUE', +} + +type CursorContext = { + cursorVariant: CursorVariants | GrayscaleImageProps; + setCursorVariant: (cursorVariant: CursorVariants) => void; +}; +export const CursorContext = createContext({ + cursorVariant: CursorVariants.DEFAULT, + setCursorVariant: (cursorVariant) => { + /** + * as we need a default action to handle the param + */ + // eslint-disable-next-line no-console + console.debug({ cursorVariant }); + }, +}); + +const spring = { + type: 'spring', + stiffness: 500, + damping: 28, +}; + +export function CursorContextProvider({ + children, +}: { + children: React.ReactNode; +}): React.ReactNode { + const [cursorText, setCursorText] = React.useState(''); + + const [mouseCursorState, setMouseCursorState] = React.useState< + Partial + >({ + clientX: 0, + clientY: 0, + }); + + const [cursorVariant, setCursorVariant] = React.useState( + CursorVariants.DEFAULT, + ); + + React.useEffect(() => { + window.addEventListener('mousemove', setMouseCursorState); + + return () => { + window.removeEventListener('mousemove', setMouseCursorState); + }; + }, []); + + let mouseXPosition: number = + typeof window !== 'undefined' + ? Math.floor(get(window, ['innerWidth'], 0) / 2) + : 0; + + let mouseYPosition = 0; + + if (get(mouseCursorState, ['x']) !== null) { + mouseXPosition = get(mouseCursorState, ['clientX'], 0); + } + + if (get(mouseCursorState, ['y']) !== null) { + mouseYPosition = get(mouseCursorState, ['clientY'], 0); + } + + const isMouseAtStartPoint = [mouseXPosition, mouseYPosition].some( + (mousePosition) => mousePosition === 0, + ); + + const opacity: number = isMouseAtStartPoint ? 0 : 1; + + const commonInteractiveCursor: CustomCursor = { + alignItems: 'center', + backgroundColor: '#dbfd39', + color: '#000', + display: 'flex', + height: 164, + justifyContent: 'center', + opacity, + width: 164, + x: mouseXPosition - 82, + y: mouseYPosition - 82, + }; + + const commonSocialCustomCursor: CustomCursor = { + borderRadius: '100%', + color: '#fdfdfd', + fontSize: '60px', + height: 96, + textAlign: 'center', + width: 96, + x: mouseXPosition - 48, + y: mouseYPosition - 48, + }; + + const cursorVariantMap: { [K in CursorVariants]: CustomCursor } = { + [CursorVariants.HIDDEN]: { + height: 0, + width: 0, + x: mouseXPosition, + y: mouseYPosition, + }, + [CursorVariants.DEFAULT]: { + backgroundBlendMode: 'difference', + backgroundColor: 'rgba(245, 40, 145, 0.8)', + border: '1px solid papayawhip', + display: 'flex', + fontSize: '0', + height: 10, + mixBlendMode: 'difference', + opacity, + transition: { type: 'spring', mass: 0.6 }, + width: 10, + x: mouseXPosition - 14, + y: mouseYPosition - 14, + }, + [CursorVariants.CONTACT]: commonInteractiveCursor, + [CursorVariants.CONTACT_CTA]: { + ...commonInteractiveCursor, + height: 46, + justifyContent: 'center', + width: 160, + x: mouseXPosition - 160, + y: mouseYPosition - 20, + }, + [CursorVariants.SHRUG]: { + backgroundBlendMode: 'difference', + backgroundColor: 'rgb(9,40,64)', + borderRadius: '100%', + color: '#fff', + fontSize: '24px', + height: 120, + opacity, + textAlign: 'center', + width: 120, + x: mouseXPosition - 60, + y: mouseYPosition - 140, + }, + [CursorVariants.IMG]: { + backgroundColor: 'rgb(9,40,64)', + borderRadius: '28px', + display: 'flex', + height: 113, + opacity, + padding: '4px', + width: 200, + x: mouseXPosition - 50, + y: mouseYPosition - 100, + }, + [CursorVariants.DRIBBBLE]: commonInteractiveCursor, + [CursorVariants.GITHUB]: commonInteractiveCursor, + [CursorVariants.NEST_JS]: { + ...commonSocialCustomCursor, + backgroundColor: '#ed1543', + opacity, + }, + [CursorVariants.TAILWIND]: { + ...commonSocialCustomCursor, + backgroundColor: '#00b4b6', + opacity, + }, + [CursorVariants.REACT]: { + ...commonSocialCustomCursor, + backgroundColor: '#61DBFB', + opacity, + }, + [CursorVariants.FRAMER_MOTION]: { + ...commonSocialCustomCursor, + backgroundColor: 'rgb(0,0,0)', + opacity, + }, + [CursorVariants.TYPESCRIPT]: { + ...commonSocialCustomCursor, + backgroundColor: '#007ACC', + opacity, + }, + [CursorVariants.VUE]: { + ...commonSocialCustomCursor, + backgroundColor: '#42b883', + opacity, + }, + }; + + const cursorVariantHandlers: { [K in CursorVariants]: () => void } = + React.useMemo( + () => ({ + [CursorVariants.CONTACT]: () => { + setCursorText(); + setCursorVariant(CursorVariants.CONTACT); + }, + [CursorVariants.CONTACT_CTA]: () => { + setCursorText('Contact Us'); + setCursorVariant(CursorVariants.CONTACT_CTA); + }, + [CursorVariants.DEFAULT]: () => { + setCursorText(''); + setCursorVariant(CursorVariants.DEFAULT); + }, + [CursorVariants.DRIBBBLE]: () => { + setCursorText(); + setCursorVariant(CursorVariants.DRIBBBLE); + }, + [CursorVariants.GITHUB]: () => { + setCursorText(); + setCursorVariant(CursorVariants.GITHUB); + }, + [CursorVariants.HIDDEN]: () => { + setCursorText(''); + setCursorVariant(CursorVariants.HIDDEN); + }, + [CursorVariants.NEST_JS]: () => { + setCursorText(); + setCursorVariant(CursorVariants.NEST_JS); + }, + [CursorVariants.TAILWIND]: () => { + setCursorText(); + setCursorVariant(CursorVariants.TAILWIND); + }, + [CursorVariants.REACT]: () => { + setCursorText(); + setCursorVariant(CursorVariants.REACT); + }, + [CursorVariants.FRAMER_MOTION]: () => { + setCursorText(); + setCursorVariant(CursorVariants.FRAMER_MOTION); + }, + [CursorVariants.SHRUG]: () => { + setCursorText('¯\\_(ツ)_/¯'); + setCursorVariant(CursorVariants.SHRUG); + }, + [CursorVariants.TYPESCRIPT]: () => { + setCursorText(); + setCursorVariant(CursorVariants.TYPESCRIPT); + }, + [CursorVariants.VUE]: () => { + setCursorText(); + setCursorVariant(CursorVariants.VUE); + }, + [CursorVariants.IMG]: () => { + if (typeof cursorVariant === 'object') { + setCursorVariant(CursorVariants.IMG); + } + }, + }), + [cursorVariant], + ); + + const cursorContextProviderValue = React.useMemo( + () => ({ + cursorVariant, + setCursorVariant: ( + nextCursorVariant: CursorVariants | GrayscaleImageProps, + ) => { + if (typeof nextCursorVariant === 'object') { + setCursorText( + , + ); + cursorVariantHandlers[CursorVariants.IMG](); + } else { + get(cursorVariantHandlers, [nextCursorVariant], () => null)(); + } + }, + }), + [cursorVariant, cursorVariantHandlers], + ); + + return ( + + + {children} + + ); +} diff --git a/app/hooks/useCursorVariant.ts b/app/hooks/useCursorVariant.ts new file mode 100644 index 0000000..08442e3 --- /dev/null +++ b/app/hooks/useCursorVariant.ts @@ -0,0 +1,23 @@ +import type { Variant } from 'framer-motion'; + +export enum CursorVariants { + CONTACT = 'CONTACT', + CONTACT_CTA = 'CONTACT_CTA', + DEFAULT = 'DEFAULT', + DRIBBBLE = 'DRIBBBLE', + FRAMER_MOTION = 'FRAMER_MOTION', + GITHUB = 'GITHUB', + HIDDEN = 'HIDDEN', + IMG = 'IMG', + NEST_JS = 'NEST_JS', + REACT = 'REACT', + SHRUG = 'SHRUG', + TAILWIND = 'TAILWIND', + TYPESCRIPT = 'TYPESCRIPT', + VUE = 'VUE', +} + +export type UseCursorVariant = () => void; + +export type CustomCursor = Variant & { isRounded?: boolean }; +export const useCursorVariant: UseCursorVariant = () => {}; diff --git a/app/icons/FramerMotionIcon.tsx b/app/icons/FramerMotionIcon.tsx new file mode 100644 index 0000000..06c6784 --- /dev/null +++ b/app/icons/FramerMotionIcon.tsx @@ -0,0 +1,24 @@ +import get from 'lodash-es/get'; +import React from 'react'; + +import { BaseIcon } from '~/icons/Icon'; +import type { IconVariantProps } from '~/icons/Icon'; + +export function FramerMotionIcon(props: IconVariantProps): React.ReactNode { + return ( + + } + inverted={ + + } + plain={} + plainInverted={} + variant={get(props, ['variant'])} + /> + ); +} diff --git a/app/icons/Icon.tsx b/app/icons/Icon.tsx new file mode 100644 index 0000000..e177578 --- /dev/null +++ b/app/icons/Icon.tsx @@ -0,0 +1,53 @@ +import get from 'lodash-es/get'; +import { type ReactNode } from 'react'; +import type React from 'react'; + +import type { PropsWithoutRef } from '~/lib/types'; + +enum IconVariant { + REGULAR = 'REGULAR', + INVERTED = 'INVERTED', + PLAIN_REGULAR = 'PLAIN_REGULAR', + PLAIN_INVERTED = 'PLAIN_INVERTED', +} + +interface BaseIconProps { + regular: ReactNode; + inverted: ReactNode; + plain: ReactNode; + plainInverted: ReactNode; + variant?: IconVariant; +} + +type IconComponentProps = PropsWithoutRef<'i'>; + +export type IconVariantFC = React.FC< + IconComponentProps & { variant?: IconVariant } +>; + +export interface IconVariantProps extends IconComponentProps { + variant?: IconVariant; +} + +export const BaseIcon: React.FC = (props) => { + const UnknownIcon = null; + + const variant: IconVariant = get( + props, + ['variant'], + IconVariant.PLAIN_INVERTED, + ); + + switch (variant) { + case IconVariant.INVERTED: + return get(props, ['inverted'], UnknownIcon); + case IconVariant.PLAIN_INVERTED: + return get(props, ['plainInverted'], UnknownIcon); + case IconVariant.PLAIN_REGULAR: + return get(props, ['plain'], UnknownIcon); + case IconVariant.REGULAR: + return get(props, ['regular'], UnknownIcon); + default: + return UnknownIcon; + } +}; diff --git a/app/icons/NestJsIcon.tsx b/app/icons/NestJsIcon.tsx new file mode 100644 index 0000000..5a319d7 --- /dev/null +++ b/app/icons/NestJsIcon.tsx @@ -0,0 +1,18 @@ +import get from 'lodash-es/get'; +import React from 'react'; + +import { BaseIcon, type IconVariantProps } from '~/icons/Icon'; + +export function NestJsIcon(props: IconVariantProps): React.ReactNode { + return ( + + } + inverted={} + plain={} + plainInverted={} + variant={get(props, ['variant'])} + /> + ); +} diff --git a/app/icons/ReactIcon.tsx b/app/icons/ReactIcon.tsx new file mode 100644 index 0000000..a2cb4c6 --- /dev/null +++ b/app/icons/ReactIcon.tsx @@ -0,0 +1,36 @@ +import clsx from 'clsx'; +import get from 'lodash-es/get'; +import React from 'react'; + +import { BaseIcon, type IconVariantProps } from '~/icons/Icon'; + +export function ReactIcon(props: IconVariantProps): React.ReactNode { + const { className, ...rest } = props; + + return ( + + } + inverted={ + + } + plain={ + + } + plainInverted={ + + } + variant={get(props, ['variant'])} + /> + ); +} diff --git a/app/icons/TailwindIcon.tsx b/app/icons/TailwindIcon.tsx new file mode 100644 index 0000000..2641291 --- /dev/null +++ b/app/icons/TailwindIcon.tsx @@ -0,0 +1,18 @@ +import get from 'lodash-es/get'; +import React from 'react'; + +import { BaseIcon, type IconVariantProps } from '~/icons/Icon'; + +export function TailwindIcon(props: IconVariantProps): React.ReactNode { + return ( + + } + inverted={} + plain={} + plainInverted={} + variant={get(props, ['variant'])} + /> + ); +} diff --git a/app/icons/TypescriptIcon.tsx b/app/icons/TypescriptIcon.tsx new file mode 100644 index 0000000..c3219bf --- /dev/null +++ b/app/icons/TypescriptIcon.tsx @@ -0,0 +1,16 @@ +import get from 'lodash-es/get'; +import React from 'react'; + +import { BaseIcon, type IconVariantProps } from '~/icons/Icon'; + +export function TypescriptIcon(props: IconVariantProps): React.ReactNode { + return ( + } + inverted={} + plain={} + plainInverted={} + variant={get(props, ['variant'])} + /> + ); +} diff --git a/app/icons/VueIcon.tsx b/app/icons/VueIcon.tsx new file mode 100644 index 0000000..5719d97 --- /dev/null +++ b/app/icons/VueIcon.tsx @@ -0,0 +1,18 @@ +import get from 'lodash-es/get'; +import React from 'react'; + +import { BaseIcon, type IconVariantProps } from '~/icons/Icon'; + +export function VueIcon(props: IconVariantProps): React.ReactNode { + return ( + + } + inverted={} + plain={} + plainInverted={} + variant={get(props, ['variant'])} + /> + ); +} diff --git a/app/icons/types.ts b/app/icons/types.ts new file mode 100644 index 0000000..e69de29 diff --git a/app/lib/GrayscaleImage.tsx b/app/lib/GrayscaleImage.tsx new file mode 100644 index 0000000..dfa9e11 --- /dev/null +++ b/app/lib/GrayscaleImage.tsx @@ -0,0 +1,40 @@ +import { + motion, + useMotionTemplate, + useScroll, + useTransform, +} from 'framer-motion'; +import React, { useRef } from 'react'; + +export type FramerCursorAttributes = Pick< + Partial>, + 'onMouseEnter' | 'onMouseLeave' +>; + +export type GrayscaleImageProps = Partial< + Pick +> & { + alt?: string; +} & FramerCursorAttributes; + +export function GrayscaleImage(props: GrayscaleImageProps): React.ReactNode { + const ref = useRef>(null); + + const { scrollYProgress } = useScroll({ + target: ref, + offset: ['start 65%', 'end 35%'], + }); + + const grayscale = useTransform(scrollYProgress, [0, 0.5, 1], [1, 0, 1]); + const filter = useMotionTemplate`grayscale(${grayscale})`; + + return ( +
+ +
+ ); +} diff --git a/app/root.tsx b/app/root.tsx index 57eef1c..828da81 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -21,6 +21,7 @@ import { import React from 'react'; import { BodyHTMLTagColorProvider } from '~/context/ColorContext'; +import { CursorContextProvider } from '~/context/CursorContext'; import '~/tailwind.css'; export const links: LinksFunction = () => [ @@ -51,9 +52,11 @@ export function Layout({ Kurocado Studio - {children} - - + + {children} + + + ); diff --git a/package.json b/package.json index 527fab4..b67ef3b 100644 --- a/package.json +++ b/package.json @@ -20,37 +20,39 @@ "typecheck": "tsc" }, "dependencies": { - "@motionone/utils": "10.18.0", - "@remix-run/node": "2.9.2", - "@remix-run/react": "2.9.2", - "clsx": "2.1.1", - "framer-motion": "11.13.5", - "isbot": "5.1.1", - "lodash-es": "4.17.21", - "react": "18.2.0", - "react-dom": "18.2.0", - "tailwind-merge": "2.4.0", - "tailwindcss": "3.3.2" + "@heroicons/react": "^2.2.0", + "@motionone/utils": "10.18.0", + "@remix-run/node": "2.9.2", + "@remix-run/react": "2.9.2", + "ahooks": "^3.8.4", + "clsx": "2.1.1", + "framer-motion": "11.13.5", + "isbot": "5.1.1", + "lodash-es": "4.17.21", + "react": "18.2.0", + "react-dom": "18.2.0", + "tailwind-merge": "2.4.0", + "tailwindcss": "3.3.2" }, "devDependencies": { - "@flydotio/dockerfile": "0.5.9", - "@kurocado-studio/styleguide": "1.10.2", - "@remix-run/dev": "2.8.1", - "@remix-run/serve": "2.9.2", - "@types/react-dom": "18.3.1", - "autoprefixer": "10.4.18", - "commitlint": "19.6.0", - "eslint": "9.16.0", - "eslint-define-config": "2.1.0", - "husky": "9.0.11", - "lint-staged": "15.2.10", - "postcss": "8.4.35", - "prettier": "3.3.3", - "semantic-release": "23.0.2", - "typescript": "5.4.2", - "vite": "5.1.0", - "vite-tsconfig-paths": "4.2.1", - "vitest": "2.1.8" + "@flydotio/dockerfile": "0.5.9", + "@kurocado-studio/styleguide": "1.10.3", + "@remix-run/dev": "2.8.1", + "@remix-run/serve": "2.9.2", + "@types/react-dom": "18.3.1", + "autoprefixer": "10.4.18", + "commitlint": "19.6.0", + "eslint": "9.16.0", + "eslint-define-config": "2.1.0", + "husky": "9.0.11", + "lint-staged": "15.2.10", + "postcss": "8.4.35", + "prettier": "3.3.3", + "semantic-release": "23.0.2", + "typescript": "5.4.2", + "vite": "5.1.0", + "vite-tsconfig-paths": "4.2.1", + "vitest": "2.1.8" }, "engines": { "node": ">=20" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7975042..4c8d980 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@heroicons/react': + specifier: ^2.2.0 + version: 2.2.0(react@18.2.0) '@motionone/utils': specifier: 10.18.0 version: 10.18.0 @@ -17,6 +20,9 @@ importers: '@remix-run/react': specifier: 2.9.2 version: 2.9.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.4.2) + ahooks: + specifier: ^3.8.4 + version: 3.8.4(react@18.2.0) clsx: specifier: 2.1.1 version: 2.1.1 @@ -46,8 +52,8 @@ importers: specifier: 0.5.9 version: 0.5.9 '@kurocado-studio/styleguide': - specifier: 1.10.2 - version: 1.10.2(@commitlint/cli@19.6.1(@types/node@20.5.0)(typescript@5.4.2))(@commitlint/config-conventional@19.6.0)(@remix-run/serve@2.9.2(typescript@5.4.2))(@testing-library/jest-dom@6.6.3)(@testing-library/react@16.1.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.1)(@types/react@19.0.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0))(@vitejs/plugin-react@4.3.4(vite@5.1.0(@types/node@20.5.0)))(eslint@9.16.0(jiti@2.4.2))(husky@9.0.11)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@4.29.1)(semantic-release@23.0.2(typescript@5.4.2))(typescript@5.4.2)(vite@5.1.0(@types/node@20.5.0))(vitest-axe@0.1.0(vitest@2.1.8(@types/node@20.5.0)(jsdom@25.0.1)))(vitest@2.1.8(@types/node@20.5.0)(jsdom@25.0.1)) + specifier: 1.10.3 + version: 1.10.3(@commitlint/cli@19.6.1(@types/node@20.5.0)(typescript@5.4.2))(@commitlint/config-conventional@19.6.0)(@remix-run/serve@2.9.2(typescript@5.4.2))(@testing-library/jest-dom@6.6.3)(@testing-library/react@16.1.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.1)(@types/react@19.0.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0))(@vitejs/plugin-react@4.3.4(vite@5.1.0(@types/node@20.5.0)))(eslint@9.16.0(jiti@2.4.2))(husky@9.0.11)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@4.29.1)(semantic-release@23.0.2(typescript@5.4.2))(typescript@5.4.2)(vite@5.1.0(@types/node@20.5.0))(vitest-axe@0.1.0(vitest@2.1.8(@types/node@20.5.0)(jsdom@25.0.1)))(vitest@2.1.8(@types/node@20.5.0)(jsdom@25.0.1)) '@remix-run/dev': specifier: 2.8.1 version: 2.8.1(@remix-run/serve@2.9.2(typescript@5.4.2))(@types/node@20.5.0)(typescript@5.4.2)(vite@5.1.0(@types/node@20.5.0)) @@ -689,6 +695,11 @@ packages: engines: {node: '>=16.0.0'} hasBin: true + '@heroicons/react@2.2.0': + resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==} + peerDependencies: + react: '>= 16 || ^19.0.0-rc' + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -750,8 +761,8 @@ packages: '@jspm/core@2.0.1': resolution: {integrity: sha512-Lg3PnLp0QXpxwLIAuuJboLeRaIhrgJjeuh797QADg3xz8wGLugQOS5DpsE8A6i6Adgzf+bacllkKZG3J0tGfDw==} - '@kurocado-studio/styleguide@1.10.2': - resolution: {integrity: sha512-9RktLKhHyIAaGa2dWNUnYfim902fQaNct5iyjG6hDjiwWkOK5jJaB/WU3AOU1Xu5A9epF4XsMPXzJK0JBXZVtw==} + '@kurocado-studio/styleguide@1.10.3': + resolution: {integrity: sha512-yh1TfvU+n12O8ufPehobvMp6PThOP70i23duKm+4FUl3nSmQd0HdqEZwDKzpOR/jbnGXGkh90dPDomn+7qJFuw==} engines: {node: '>=20'} peerDependencies: '@commitlint/cli': ^19.0.3 @@ -1548,8 +1559,8 @@ packages: '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} - '@types/lodash@4.17.13': - resolution: {integrity: sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==} + '@types/lodash@4.17.14': + resolution: {integrity: sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==} '@types/mdast@3.0.15': resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} @@ -1807,6 +1818,12 @@ packages: resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==} engines: {node: '>=18'} + ahooks@3.8.4: + resolution: {integrity: sha512-39wDEw2ZHvypaT14EpMMk4AzosHWt0z9bulY0BeDsvc9PqJEV+Kjh/4TZfftSsotBMq52iYIOFPd3PR56e0ZJg==} + engines: {node: '>=8.0.0'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -2383,6 +2400,9 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} @@ -3400,6 +3420,9 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + intersection-observer@0.12.2: + resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} + into-stream@7.0.0: resolution: {integrity: sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==} engines: {node: '>=12'} @@ -3688,6 +3711,10 @@ packages: js-base64@3.7.7: resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4940,6 +4967,9 @@ packages: peerDependencies: react: ^18.2.0 + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -5068,6 +5098,9 @@ packages: require-like@0.1.2: resolution: {integrity: sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==} + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -5157,6 +5190,10 @@ packages: scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + screenfull@5.2.0: + resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} + engines: {node: '>=0.10.0'} + semantic-release-github-pullrequest@1.3.0: resolution: {integrity: sha512-hkUaK+mLzmRg3XQB5OiE5Z5pee20zMqhaiUXgJsToiYVmrhmGMwXEEjTuOsB/A8f8S7XanfSAS8yK0baXvpKsA==} engines: {node: '>=14'} @@ -6697,6 +6734,10 @@ snapshots: shell-quote: 1.8.2 yargs: 17.7.2 + '@heroicons/react@2.2.0(react@18.2.0)': + dependencies: + react: 18.2.0 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -6757,7 +6798,7 @@ snapshots: '@jspm/core@2.0.1': {} - '@kurocado-studio/styleguide@1.10.2(@commitlint/cli@19.6.1(@types/node@20.5.0)(typescript@5.4.2))(@commitlint/config-conventional@19.6.0)(@remix-run/serve@2.9.2(typescript@5.4.2))(@testing-library/jest-dom@6.6.3)(@testing-library/react@16.1.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.1)(@types/react@19.0.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0))(@vitejs/plugin-react@4.3.4(vite@5.1.0(@types/node@20.5.0)))(eslint@9.16.0(jiti@2.4.2))(husky@9.0.11)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@4.29.1)(semantic-release@23.0.2(typescript@5.4.2))(typescript@5.4.2)(vite@5.1.0(@types/node@20.5.0))(vitest-axe@0.1.0(vitest@2.1.8(@types/node@20.5.0)(jsdom@25.0.1)))(vitest@2.1.8(@types/node@20.5.0)(jsdom@25.0.1))': + '@kurocado-studio/styleguide@1.10.3(@commitlint/cli@19.6.1(@types/node@20.5.0)(typescript@5.4.2))(@commitlint/config-conventional@19.6.0)(@remix-run/serve@2.9.2(typescript@5.4.2))(@testing-library/jest-dom@6.6.3)(@testing-library/react@16.1.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.1)(@types/react@19.0.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0))(@vitejs/plugin-react@4.3.4(vite@5.1.0(@types/node@20.5.0)))(eslint@9.16.0(jiti@2.4.2))(husky@9.0.11)(prettier@3.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@4.29.1)(semantic-release@23.0.2(typescript@5.4.2))(typescript@5.4.2)(vite@5.1.0(@types/node@20.5.0))(vitest-axe@0.1.0(vitest@2.1.8(@types/node@20.5.0)(jsdom@25.0.1)))(vitest@2.1.8(@types/node@20.5.0)(jsdom@25.0.1))': dependencies: '@babel/core': 7.26.0 '@babel/eslint-parser': 7.25.9(@babel/core@7.26.0)(eslint@9.16.0(jiti@2.4.2)) @@ -7952,9 +7993,9 @@ snapshots: '@types/lodash-es@4.17.12': dependencies: - '@types/lodash': 4.17.13 + '@types/lodash': 4.17.14 - '@types/lodash@4.17.13': {} + '@types/lodash@4.17.14': {} '@types/mdast@3.0.15': dependencies: @@ -8172,7 +8213,7 @@ snapshots: '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) '@vanilla-extract/babel-plugin-debug-ids': 1.2.0 '@vanilla-extract/css': 1.17.0 - esbuild: 0.17.6 + esbuild: 0.19.12 eval: 0.1.8 find-up: 5.0.0 javascript-stringify: 2.1.0 @@ -8327,6 +8368,19 @@ snapshots: clean-stack: 5.2.0 indent-string: 5.0.0 + ahooks@3.8.4(react@18.2.0): + dependencies: + '@babel/runtime': 7.26.0 + dayjs: 1.11.13 + intersection-observer: 0.12.2 + js-cookie: 3.0.5 + lodash: 4.17.21 + react: 18.2.0 + react-fast-compare: 3.2.2 + resize-observer-polyfill: 1.5.1 + screenfull: 5.2.0 + tslib: 2.8.1 + ajv-draft-04@1.0.0(ajv@8.13.0): optionalDependencies: ajv: 8.13.0 @@ -8938,6 +8992,8 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 + dayjs@1.11.13: {} + de-indent@1.0.2: {} debug@2.6.9: @@ -10129,6 +10185,8 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + intersection-observer@0.12.2: {} + into-stream@7.0.0: dependencies: from2: 2.3.0 @@ -10414,6 +10472,8 @@ snapshots: js-base64@3.7.7: {} + js-cookie@3.0.5: {} + js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -11681,6 +11741,8 @@ snapshots: react: 18.2.0 scheduler: 0.23.2 + react-fast-compare@3.2.2: {} + react-is@16.13.1: {} react-is@17.0.2: {} @@ -11847,6 +11909,8 @@ snapshots: require-like@0.1.2: {} + resize-observer-polyfill@1.5.1: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -11956,6 +12020,8 @@ snapshots: dependencies: loose-envify: 1.4.0 + screenfull@5.2.0: {} + semantic-release-github-pullrequest@1.3.0(semantic-release@23.0.2(typescript@5.4.2)): dependencies: '@octokit/rest': 18.12.0