Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/shaggy-facts-serve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@zag-js/menu": patch
---

Delay prop normalization in menu getTriggerItemProps until after prop merging
22 changes: 16 additions & 6 deletions packages/machines/menu/src/menu.connect.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Service } from "@zag-js/core"
import { mergeProps } from "@zag-js/core"
import { createNormalizer } from "@zag-js/types"
import {
ariaAttr,
dataAttr,
Expand All @@ -22,6 +23,9 @@ import { parts } from "./menu.anatomy"
import * as dom from "./menu.dom"
import type { ItemProps, ItemState, MenuApi, MenuSchema, OptionItemProps, OptionItemState } from "./menu.types"

// We need to avoid mixing framework-agnostic logic with framework-specific prop handling
const identityProps = createNormalizer<PropTypes>((v) => v)

export function connect<T extends PropTypes>(service: Service<MenuSchema>, normalize: NormalizeProps<T>): MenuApi<T> {
const { context, send, state, computed, prop, scope } = service

Expand Down Expand Up @@ -61,11 +65,12 @@ export function connect<T extends PropTypes>(service: Service<MenuSchema>, norma
}
}

function getItemProps(props: ItemProps) {
function getItemProps(props: ItemProps, normalized = true) {
const { closeOnSelect, valueText, value } = props
const itemState = getItemState(props)
const id = dom.getItemId(scope, value)
return normalize.element({

const itemProps = identityProps.element({
...parts.item.attrs,
id,
role: "menuitem",
Expand Down Expand Up @@ -110,6 +115,8 @@ export function connect<T extends PropTypes>(service: Service<MenuSchema>, norma
send({ type: "ITEM_CLICK", target, id, closeOnSelect })
},
})

return normalized ? normalize.element(itemProps) : itemProps
}

return {
Expand Down Expand Up @@ -177,12 +184,13 @@ export function connect<T extends PropTypes>(service: Service<MenuSchema>, norma
},

getTriggerItemProps(childApi) {
const triggerProps = childApi.getTriggerProps()
return mergeProps(getItemProps({ value: triggerProps.id }), triggerProps) as T["element"]
const triggerProps = childApi.getTriggerProps(false)
const itemProps = getItemProps({ value: triggerProps.id }, false)
return normalize.element(mergeProps(itemProps, triggerProps))
},

getTriggerProps() {
return normalize.button({
getTriggerProps(normalized = true) {
const triggerProps = identityProps.button({
...(isSubmenu ? parts.triggerItem.attrs : parts.trigger.attrs),
"data-placement": context.get("currentPlacement"),
type: "button",
Expand Down Expand Up @@ -256,6 +264,8 @@ export function connect<T extends PropTypes>(service: Service<MenuSchema>, norma
}
},
})

return normalized ? normalize.button(triggerProps) : triggerProps
},

getIndicatorProps() {
Expand Down
8 changes: 4 additions & 4 deletions packages/machines/menu/src/menu.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@ export type ParentMenuService = Pick<MenuService, "prop" | "send" | "refs" | "co
* -----------------------------------------------------------------------------*/

export interface Api {
getItemProps: (props: ItemProps) => Record<string, any>
getTriggerProps: () => Record<string, any>
getItemProps: (props: ItemProps, normalized?: boolean) => Record<string, any>
getTriggerProps: (normalized?: boolean) => Record<string, any>
}

export interface ItemProps {
Expand Down Expand Up @@ -317,14 +317,14 @@ export interface MenuApi<T extends PropTypes = PropTypes> {

getContextTriggerProps: () => T["element"]
getTriggerItemProps: <A extends Api>(childApi: A) => T["element"]
getTriggerProps: () => T["button"]
getTriggerProps: (normalized?: boolean) => T["button"]
getIndicatorProps: () => T["element"]
getPositionerProps: () => T["element"]
getArrowProps: () => T["element"]
getArrowTipProps: () => T["element"]
getContentProps: () => T["element"]
getSeparatorProps: () => T["element"]
getItemProps: (options: ItemProps) => T["element"]
getItemProps: (options: ItemProps, normalized?: boolean) => T["element"]
getOptionItemProps: (option: OptionItemProps) => T["element"]
getItemIndicatorProps: (option: ItemBaseProps) => T["element"]
getItemTextProps: (option: ItemBaseProps) => T["element"]
Expand Down
1 change: 1 addition & 0 deletions packages/machines/tour/src/tour.connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export function connect<T extends PropTypes>(service: TourService, normalize: No
send({ type: "STEPS.SET", value: next, src: "removeStep" })
},
updateStep(id, stepOverrides) {
// Should mergeProps here merge the effect functions? It does not, it takes the last it finds.
const next = steps.map((step) => (step.id === id ? mergeProps(step, stepOverrides) : step))
send({ type: "STEPS.SET", value: next, src: "updateStep" })
},
Expand Down
Loading