Skip to content

Commit

Permalink
chore: more progress
Browse files Browse the repository at this point in the history
  • Loading branch information
segunadebayo committed Oct 10, 2024
1 parent 90e5b24 commit 11bb412
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 79 deletions.
126 changes: 92 additions & 34 deletions examples/next-ts/pages/navigation-menu-viewport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,42 +23,52 @@ export default function Page() {
return (
<>
<main className="navigation-menu">
<div {...api.getRootProps()}>
<div style={{ position: "relative" }}>
<div {...api.getListProps()}>
<div {...api.getItemProps({ value: "products" })}>
<button {...api.getTriggerProps({ value: "products" })}>
Products
<ChevronDown />
</button>
</div>
<Navbar>
<div {...api.getRootProps()}>
<div style={{ position: "relative" }}>
<div {...api.getListProps()}>
<div {...api.getItemProps({ value: "products" })}>
<button {...api.getTriggerProps({ value: "products" })}>
Products
<ChevronDown />
</button>
</div>

<div {...api.getItemProps({ value: "company" })}>
<button {...api.getTriggerProps({ value: "company" })}>
Company
<ChevronDown />
</button>
</div>
<div {...api.getItemProps({ value: "company" })}>
<button {...api.getTriggerProps({ value: "company" })}>
Company
<ChevronDown />
</button>
</div>

<div {...api.getItemProps({ value: "developers", disabled: true })}>
<button {...api.getTriggerProps({ value: "developers", disabled: true })}>
Developers
<ChevronDown />
</button>
</div>
<div {...api.getItemProps({ value: "developers", disabled: true })}>
<button {...api.getTriggerProps({ value: "developers", disabled: true })}>
Developers
<ChevronDown />
</button>
</div>

<div {...api.getItemProps({ value: "pricing" })}>
<a href="#" {...api.getLinkProps({ value: "pricing" })}>
Pricing
</a>
</div>
<div {...api.getItemProps({ value: "pricing" })}>
<a href="#" {...api.getLinkProps({ value: "pricing" })}>
Pricing
</a>
</div>

<div {...api.getViewportProps()}>
<div {...api.getArrowProps()}>
<Presence {...api.getArrowProps()}>
<div {...api.getArrowTipProps()} />
</div>
</Presence>
</div>
</div>

<Presence {...api.getContentProps({ value: "products" })}>
<div data-scope="navigation-menu" data-part="viewport-container">
<Presence {...api.getViewportProps()}>
<Presence
{...api.getContentProps({ value: "products" })}
style={{
gridTemplateColumns: "1fr 2fr",
width: 600,
}}
>
{renderLinks({
value: "products",
items: [
Expand All @@ -70,25 +80,51 @@ export default function Page() {
"Pellentesque",
],
})}

{renderLinks({
value: "products",
items: ["Fusce pellentesque", "Aliquam porttitor", "Pellentesque"],
})}
</Presence>

<Presence {...api.getContentProps({ value: "company" })}>
<Presence
{...api.getContentProps({ value: "company" })}
style={{
gridTemplateColumns: "1fr 1fr",
width: 450,
}}
>
{renderLinks({
value: "company",
items: ["Fusce pellentesque", "Aliquam porttitor", "Pellentesque", "Aliquam porttitor"],
})}

{renderLinks({
value: "company",
items: ["Fusce pellentesque", "Aliquam porttitor", "Pellentesque"],
})}
</Presence>

<Presence {...api.getContentProps({ value: "developers" })}>
<Presence
{...api.getContentProps({ value: "developers" })}
style={{
gridTemplateColumns: "1.6fr 1fr",
width: 650,
}}
>
{renderLinks({
value: "developers",
items: ["Donec quis dui", "Vestibulum", "Fusce pellentesque", "Aliquam porttitor"],
})}
{renderLinks({
value: "developers",
items: ["Fusce pellentesque", "Aliquam porttitor"],
})}
</Presence>
</div>
</Presence>
</div>
</div>
</div>
</Navbar>
</main>

<Toolbar>
Expand All @@ -97,3 +133,25 @@ export default function Page() {
</>
)
}

const Navbar = ({ children }: { children: React.ReactNode }) => {
return (
<div
style={{
position: "relative",
display: "flex",
boxSizing: "border-box",
alignItems: "center",
padding: "15px 20px",
justifyContent: "space-between",
width: "100%",
backgroundColor: "white",
boxShadow: "0 50px 100px -20px rgba(50,50,93,0.1),0 30px 60px -30px rgba(0,0,0,0.2)",
}}
>
<button>Logo</button>
{children}
<button>Login</button>
</div>
)
}
24 changes: 19 additions & 5 deletions packages/machines/navigation-menu/src/navigation-menu.connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
const open = Boolean(state.context.value)

const activeTriggerRect = state.context.activeTriggerRect
const viewportRect = state.context.viewportRect
const viewportSize = state.context.viewportSize

const value = state.context.value
const previousValue = state.context.previousValue
Expand Down Expand Up @@ -57,6 +57,7 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
...parts.item.attrs,
dir: state.context.dir,
"data-value": props.value,
"data-type": state.context.isViewportRendered ? "viewport" : "popover",
"data-state": itemState.open ? "open" : "closed",
"data-orientation": state.context.orientation,
"data-disabled": dataAttr(itemState.disabled),
Expand Down Expand Up @@ -222,13 +223,18 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize

getContentProps(props: ItemProps) {
const itemState = getItemState(props)

const activeContentValue = state.context.value ?? state.context.previousValue
const selected = state.context.isViewportRendered ? activeContentValue === props.value : itemState.selected

return normalize.element({
...parts.content.attrs,
id: dom.getContentId(state.context, props.value),
dir: state.context.dir,
hidden: !itemState.selected,
hidden: !selected,
"data-uid": state.context.id,
"data-state": itemState.selected ? "open" : "closed",
"data-state": selected ? "open" : "closed",
"data-type": state.context.isViewportRendered ? "viewport" : "popover",
"data-value": props.value,
onPointerEnter() {
send({ type: "CONTENT_ENTER", value: props.value })
Expand All @@ -249,9 +255,17 @@ export function connect<T extends PropTypes>(state: State, send: Send, normalize
hidden: !open,
"data-state": open ? "open" : "closed",
style: {
transition: state.context.value && !state.context.previousValue ? "none" : undefined,
pointerEvents: !open ? "none" : undefined,
"--viewport-width": viewportRect != null ? viewportRect.width + "px" : undefined,
"--viewport-height": viewportRect != null ? viewportRect.height + "px" : undefined,
"--viewport-width": viewportSize != null ? viewportSize.width + "px" : undefined,
"--viewport-height": viewportSize != null ? viewportSize.height + "px" : undefined,
},
onPointerEnter() {
send({ type: "CONTENT_ENTER", src: "viewport" })
},
onPointerLeave(event) {
if (event.pointerType !== "mouse") return
send({ type: "CONTENT_LEAVE", src: "viewport" })
},
})
},
Expand Down
40 changes: 24 additions & 16 deletions packages/machines/navigation-menu/src/navigation-menu.machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export function machine(userContext: UserDefinedContext) {
id: "navigation-menu",

context: {
viewportRect: null,
viewportSize: null,
isViewportRendered: false,
activeTriggerRect: null,
value: null,
Expand All @@ -29,7 +29,13 @@ export function machine(userContext: UserDefinedContext) {
},

watch: {
value: ["setActiveTriggerNode", "syncTriggerRectObserver", "setActiveContentNode", "syncMotionAttribute"],
value: [
"setActiveTriggerNode",
"syncTriggerRectObserver",
"setActiveContentNode",
"syncContentRectObserver",
"syncMotionAttribute",
],
},

initial: "closed",
Expand Down Expand Up @@ -170,25 +176,27 @@ export function machine(userContext: UserDefinedContext) {
syncTriggerRectObserver(ctx) {
const node = ctx.activeTriggerNode
if (!node) return

ctx.activeTriggerCleanup?.()

const exec = () => {
const rect = node.getBoundingClientRect()
const listEl = dom.getListEl(ctx)

const listRect = listEl?.getBoundingClientRect()
if (!listRect) return

const x = rect.x
const y = rect.y
ctx.activeTriggerRect = { x, y, width: node.offsetWidth, height: node.offsetHeight }
ctx.activeTriggerRect = {
x: node.offsetLeft,
y: node.offsetTop,
width: node.offsetWidth,
height: node.offsetHeight,
}
}

exec()

ctx.activeTriggerCleanup = trackResizeObserver(node, exec)
},
syncContentRectObserver(ctx) {
if (!ctx.isViewportRendered) return
const node = ctx.activeContentNode
if (!node) return
ctx.activeContentCleanup?.()
const exec = () => {
ctx.viewportSize = { width: node.offsetWidth, height: node.offsetHeight }
}
ctx.activeContentCleanup = trackResizeObserver(node, exec)
},
syncMotionAttribute(ctx) {
set.motionAttr(ctx)
},
Expand Down
11 changes: 6 additions & 5 deletions packages/machines/navigation-menu/src/navigation-menu.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ interface ValueChangeDetails {
value: string | null
}

interface Rect {
interface Size {
width: number
height: number
}

interface Rect extends Size {
y: number
x: number
}
Expand All @@ -21,7 +24,7 @@ interface PublicContext extends DirectionProperty, CommonProperties, Orientation
}

interface PrivateContext {
viewportRect: Rect | null
viewportSize: Size | null
isViewportRendered: boolean
wasClickCloseRef: string | null
hasPointerMoveOpenedRef: string | null
Expand All @@ -34,9 +37,7 @@ interface PrivateContext {
activeTriggerCleanup: VoidFunction | null
}

type ComputedContext = Readonly<{
activeContentValue: string | null
}>
type ComputedContext = Readonly<{}>

export type UserDefinedContext = RequiredBy<PublicContext, "id">

Expand Down
Loading

0 comments on commit 11bb412

Please sign in to comment.