Mixins / foldkit-mixins-ui

Menu

namespace · packages/mixins-ui/src/menu.ts:1

export * as Menu from './menu.js'

In the guides

Members

Menu.fluid

const · packages/mixins-ui/src/menu.ts:173

const fluid: readonly [Mixin<never>]

What moves the open panel's highlight: on its rows, one highlight follows the pointer to the nearest row (--fk-popup-hover-*), and to the row the keys mark (data-active) while the pointer is away. Pass them beside the style; until they run, each row marks itself.

const · packages/mixins-ui/src/menu.ts:24

const MenuSlots: Slots.Contract<{ readonly wrapper: Slot.UnnamedSlot<Capability.Capability<"Container", readonly [Capability.Capability<"Interactive", readonly [Capability.Capability<"Base", readonly []>]>]>, readonly Slot.EventName[], readonly Slot.AttributeName[], readonly Slot.RequirementName[], boolean>; readonly button: Slot.UnnamedSlot<Capability.Capability<"Interactive", readonly [Capability.Capability<"Base", readonly []>]>, readonly [Event.EventToken<"click">, Event.EventToken<"pointerdown">, Event.EventToken<"keydown">], readonly [Attr.AttrToken<"role">, Attr.AttrToken<"aria-expanded">, Attr.AttrToken<"disabled">, Attr.AttrToken<"aria-disabled">], readonly Slot.RequirementName[], boolean>; readonly backdrop: Slot.UnnamedSlot<Capability.Capability<"Container", readonly [Capability.Capability<"Interactive", readonly [Capability.Capability<"Base", readonly []>]>]>, readonly [Event.EventToken<"click">], readonly Slot.AttributeName[], readonly Slot.RequirementName[], boolean>; readonly items: Slot.UnnamedSlot<Capability.Capability<"Container", readonly [Capability.Capability<"Interactive", readonly [Capability.Capability<"Base", readonly []>]>]>, readonly [Event.EventToken<"keydown">, Event.EventToken<"pointerup">, Event.EventToken<"blur">], readonly [Attr.AttrToken<"role">], readonly Slot.RequirementName[], boolean>; readonly scroll: Slot.UnnamedSlot<Capability.Capability<"Container", readonly [Capability.Capability<"Interactive", readonly [Capability.Capability<"Base", readonly []>]>]> …;

Menu is a nested Submodel: one button/backdrop/items group plus an item per entry (and group/heading/separator where the caller groups), so the resolver runs once per bundle and each base attribute passes through by identity.

The button owns click (and pointerdown/keydown while enabled); an item owns click while interactive. A Behavior adding its own handler to one of those is a conflict, not a second silent owner. Disabled buttons and items publish no click, so the contract still lists it as allowed.

Menu.resolve

function · packages/mixins-ui/src/menu.ts:89

function resolve<Input, Message>(
  render: MenuRenderInfo,
  mixins: MixinList<Message>,
  context: ResolveContext<Input, Message>,
): ResolvedMenu<Message>

Resolves the menu's bundles (and every item's); pass-throughs stay intact.

Menu.ResolvedMenu

type · packages/mixins-ui/src/menu.ts:68

type ResolvedMenu<Message> = Omit<
  MenuRenderInfo,
  'wrapper' | 'button' | 'backdrop' | 'items' | 'scroll' | 'groups'
> & {
  readonly wrapper: SlotAttributes<Message>
  readonly button: SlotAttributes<Message>
  readonly buttonContent: Html
  readonly backdrop:
    | {
        key: string
        attributes: SlotAttributes<Message>
      }
    | undefined
  readonly items:
    | {
        key: string
        attributes: SlotAttributes<Message>
      }
    | undefined
  readonly scroll: SlotAttributes<Message> | undefined
  readonly rows: SlotAttributes<Message> | undefined
  readonly highlight: SlotAttributes<Message> | undefined
  readonly groups: ReadonlyArray<ResolvedMenuGroup<Message>>
}

Menu.ResolvedMenuGroup

type · packages/mixins-ui/src/menu.ts:58

type ResolvedMenuGroup<Message> = Omit<
  MenuGroupRender,
  'heading' | 'group' | 'separator' | 'items'
> & {
  readonly heading: ResolvedMenuHeading<Message> | undefined
  readonly group:
    | {
        key: string
        attributes: SlotAttributes<Message>
      }
    | undefined
  readonly separator:
    | {
        key: string
        attributes: SlotAttributes<Message>
      }
    | undefined
  readonly items: ReadonlyArray<ResolvedMenuItem<Message>>
}

Menu.ResolvedMenuHeading

type · packages/mixins-ui/src/menu.ts:54

type ResolvedMenuHeading<Message> = Omit<MenuHeadingRender, 'attributes'> & {
  readonly attributes: SlotAttributes<Message>
}

Menu.ResolvedMenuItem

type · packages/mixins-ui/src/menu.ts:50

type ResolvedMenuItem<Message> = Omit<MenuItemRender, 'attributes'> & {
  readonly attributes: SlotAttributes<Message>
}

Menu.toView

function · packages/mixins-ui/src/menu.ts:158

function toView<Message>(
  mixins: MixinList<Message>,
  context: ResolveContext<unknown, Message>,
  draw?: (resolved: ResolvedMenu<Message>) => Html,
): (render: MenuRenderInfo) => Html

The menu's toView: draw receives its bundles with mixins applied. Without one, it draws upstream's markup from them.