diff --git a/packages/storyblok/src/components/withFormBuilderData.tsx b/packages/storyblok/src/components/withFormBuilderData.tsx index 5bd52ec..19f6ce9 100644 --- a/packages/storyblok/src/components/withFormBuilderData.tsx +++ b/packages/storyblok/src/components/withFormBuilderData.tsx @@ -1,16 +1,10 @@ import "server-only" -import type { ConfigStoryblok } from "@gotpop/system" +import type { ConfigStoryblok, FormBuilderStoryblok } from "@gotpop/system" import { StoryblokServerComponent } from "@storyblok/react/rsc" import type { ReactNode } from "react" import { getConfig } from "../config/runtime-config" -interface FormBuilderBlok { - // biome-ignore lint/suspicious/noExplicitAny: Replace with proper type once forms are full integrated - inputs?: any[] - [key: string]: unknown -} - interface FormState { errors: Record message: string @@ -18,8 +12,7 @@ interface FormState { } interface WithFormBuilderDataProps { - // biome-ignore lint/suspicious/noExplicitAny: Replace with proper type once forms are full integrated - blok: any + blok: FormBuilderStoryblok content: ReactNode config: ConfigStoryblok | null onSubmit?: (formData: FormData) => Promise @@ -34,10 +27,9 @@ export function withFormBuilderData( blok, config: providedConfig, }: { - blok: FormBuilderBlok + blok: FormBuilderStoryblok config?: ConfigStoryblok | null }) => { - // Use provided config or fetch from cache const config = providedConfig ?? (await getConfig()) const { inputs } = blok diff --git a/packages/system/package.json b/packages/system/package.json index 75f7fa4..34db797 100644 --- a/packages/system/package.json +++ b/packages/system/package.json @@ -1,6 +1,6 @@ { "name": "@gotpop/system", - "version": "0.1.295", + "version": "0.1.301", "description": "React design system components for gotpop", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/system/src/components/storyblok/NavDefault/NavDefault.tsx b/packages/system/src/components/storyblok/NavDefault/NavDefault.tsx index ae1648f..9a5de34 100644 --- a/packages/system/src/components/storyblok/NavDefault/NavDefault.tsx +++ b/packages/system/src/components/storyblok/NavDefault/NavDefault.tsx @@ -1,3 +1,5 @@ +"use client" + import { useId } from "react" import type { ConfigStoryblok, @@ -5,6 +7,7 @@ import type { } from "../../../types/storyblok-components" import { ButtonToggleMenu } from "../../ui/ButtonToggleMenu" import "./NavDefault.css" +import { useHamburgerState } from "../../../hooks/useHamburgerState" import { CustomElement } from "../../ui/CustomElement" interface NavDefaultProps { @@ -16,6 +19,7 @@ interface NavDefaultProps { export function NavDefault({ blok: _blok, blocks }: NavDefaultProps) { const navId = useId() + useHamburgerState() return ( <> diff --git a/packages/system/src/hooks/useHamburgerState.ts b/packages/system/src/hooks/useHamburgerState.ts new file mode 100644 index 0000000..110dec1 --- /dev/null +++ b/packages/system/src/hooks/useHamburgerState.ts @@ -0,0 +1,71 @@ +import { useEffect } from "react" + +/** + * Custom hook that initializes hamburger icon state tracking + * Tracks popover open/closed state and toggles class on icon-hamburger + */ +export function useHamburgerState() { + useEffect(() => { + const header = document.querySelector("header") + const hamburgerIcon = document.querySelector("icon-hamburger") + const popover = document.querySelector("[popover]") as HTMLElement & { + hidePopover?: () => void + } + + if (!header || !hamburgerIcon || !popover) { + console.warn("Hamburger state: Required elements not found") + return + } + + const handleToggle = (event: Event) => { + const toggleEvent = event as ToggleEvent + + if (toggleEvent.newState === "open") { + hamburgerIcon.classList.add("is-active") + } else { + hamburgerIcon.classList.remove("is-active") + } + } + + const checkPopoverState = (): void => { + const isOpen = popover.matches(":popover-open") + + hamburgerIcon.classList.toggle("is-active", isOpen) + } + + // Safari bug fix: reset popover state when crossing desktop breakpoint + const mediaQuery = window.matchMedia("(width >= 1480px)") + + const handleBreakpointChange = () => { + if (popover.hidePopover) { + popover.hidePopover() + hamburgerIcon.classList.remove("is-active") + } + } + + checkPopoverState() + + if (!mediaQuery.matches && popover.hidePopover) { + popover.hidePopover() + hamburgerIcon.classList.remove("is-active") + } + + popover.addEventListener("toggle", handleToggle) + mediaQuery.addEventListener("change", handleBreakpointChange) + + const observer = new MutationObserver(() => { + checkPopoverState() + }) + + observer.observe(popover, { + attributes: true, + attributeFilter: ["popover"], + }) + + return () => { + popover.removeEventListener("toggle", handleToggle) + mediaQuery.removeEventListener("change", handleBreakpointChange) + observer.disconnect() + } + }, []) +} diff --git a/packages/system/src/styles/icons/hamburger.css b/packages/system/src/styles/icons/hamburger.css index 071ff65..88b5b23 100644 --- a/packages/system/src/styles/icons/hamburger.css +++ b/packages/system/src/styles/icons/hamburger.css @@ -50,8 +50,8 @@ icon-hamburger { --y: var(--after-y); } - :has(+ [popover]:popover-open) &, - :has(~ [popover]:popover-open) & { + /* Active state when popover is open */ + &.is-active { --after-bottom: calc(50% - var(--line)); --after-rotate: -45deg; --after-y: calc(-var(--spacing-md) / 2 + var(--line)); diff --git a/yarn.lock b/yarn.lock index ca542d3..23226f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -938,7 +938,7 @@ "@storyblok/react" "5.4.20" "@gotpop/system@*", "@gotpop/system@file:/Users/minivan/web/sites/platform/system/packages/system": - version "0.1.295" + version "0.1.301" resolved "file:packages/system" dependencies: "@icons-pack/react-simple-icons" "^13.8.0"