Skip to content

Commit

Permalink
refactor: collection and related machines
Browse files Browse the repository at this point in the history
  • Loading branch information
segunadebayo committed Sep 6, 2023
1 parent 2c79cf9 commit e4d78be
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 48 deletions.
8 changes: 8 additions & 0 deletions .changeset/clean-jobs-sell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@zag-js/collection": patch
"@zag-js/combobox": patch
"@zag-js/select": patch
---

- Loosen the collection item types to allow string item
- Add generic to select and combobox context and api
4 changes: 2 additions & 2 deletions .xstate/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const fetchMachine = createMachine({
on: {
"TRIGGER.CLICK": {
target: "open",
actions: ["invokeOnOpen"]
actions: ["invokeOnOpen", "highlightFirstSelectedItem"]
},
"TRIGGER.FOCUS": {
target: "focused"
Expand All @@ -88,7 +88,7 @@ const fetchMachine = createMachine({
},
"TRIGGER.CLICK": {
target: "open",
actions: ["invokeOnOpen"]
actions: ["invokeOnOpen", "highlightFirstSelectedItem"]
},
"TRIGGER.ENTER": [{
cond: "hasSelectedItems",
Expand Down
4 changes: 2 additions & 2 deletions packages/machines/combobox/src/combobox.collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ export const collection = <T extends CollectionItem>(options: CollectionOptions<
return ref(new Collection(options))
}

collection.empty = (): Collection<any> => {
return ref(new Collection<any>({ items: [] }))
collection.empty = (): Collection<CollectionItem> => {
return ref(new Collection<CollectionItem>({ items: [] }))
}
39 changes: 29 additions & 10 deletions packages/machines/combobox/src/combobox.types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { Collection, CollectionItem } from "@zag-js/collection"
import type { Collection, CollectionItem, CollectionOptions } from "@zag-js/collection"
import type { StateMachine as S } from "@zag-js/core"
import type { FocusOutsideEvent, InteractOutsideEvent, PointerDownOutsideEvent } from "@zag-js/interact-outside"
import type { Placement, PositioningOptions } from "@zag-js/popper"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"

export type { CollectionOptions }

type IntlTranslations = {
triggerLabel?: string
clearTriggerLabel?: string
Expand All @@ -23,7 +25,21 @@ type ElementIds = Partial<{
itemGroupLabel(id: string | number): string
}>

type PublicContext = DirectionProperty &
export type ValueChangeDetails<T extends CollectionItem = CollectionItem> = {
value: string[]
items: T[]
}

export type HighlightChangeDetails<T extends CollectionItem = CollectionItem> = {
value: string | null
item: T | null
}

type InputValueChangeDetails = {
value: string
}

type PublicContext<T extends CollectionItem = CollectionItem> = DirectionProperty &
CommonProperties & {
/**
* The ids of the elements in the combobox. Useful for composition.
Expand Down Expand Up @@ -108,16 +124,16 @@ type PublicContext = DirectionProperty &
/**
* Function called when the input's value changes
*/
onInputChange?: (details: { value: string }) => void
onInputChange?: (details: InputValueChangeDetails) => void
/**
* Function called when a new item is selected
*/
onChange?: (details: { value: string[]; items: CollectionItem[] }) => void
onChange?: (details: ValueChangeDetails<T>) => void
/**
* Function called when an item is highlighted using the pointer
* or keyboard navigation.
*/
onHighlight?: (details: { value: string | null; item: CollectionItem | null }) => void
onHighlight?: (details: HighlightChangeDetails<T>) => void
/**
* Function called when the popup is opened
*/
Expand Down Expand Up @@ -159,7 +175,10 @@ type PublicContext = DirectionProperty &
/**
* This is the actual context exposed to the user.
*/
export type UserDefinedContext = RequiredBy<PublicContext, "id" | "collection">
export type UserDefinedContext<T extends CollectionItem = CollectionItem> = RequiredBy<
PublicContext<T>,
"id" | "collection"
>

type ComputedContext = Readonly<{
/**
Expand Down Expand Up @@ -246,7 +265,7 @@ export type ItemGroupLabelProps = {

export type { InteractOutsideEvent, Placement, PositioningOptions }

export type MachineApi<T extends PropTypes = PropTypes> = {
export type MachineApi<T extends PropTypes = PropTypes, V extends CollectionItem = CollectionItem> = {
/**
* Whether the combobox is focused
*/
Expand All @@ -270,15 +289,15 @@ export type MachineApi<T extends PropTypes = PropTypes> = {
/**
* The highlighted item
*/
highlightedItem: CollectionItem | null
highlightedItem: V | null
/**
* The value of the combobox input
*/
highlightValue(value: string): void
/**
* The selected items
*/
selectedItems: CollectionItem[]
selectedItems: V[]
/**
* Whether there's a selected item
*/
Expand Down Expand Up @@ -326,7 +345,7 @@ export type MachineApi<T extends PropTypes = PropTypes> = {
/**
* Function to set the collection of items
*/
setCollection(collection: Collection<any>): void
setCollection(collection: Collection<V>): void

rootProps: T["element"]
labelProps: T["label"]
Expand Down
1 change: 1 addition & 0 deletions packages/machines/combobox/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export type {
Placement,
PositioningOptions,
MachineApi as Api,
CollectionOptions,
} from "./combobox.types"
1 change: 1 addition & 0 deletions packages/machines/select/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export type {
ItemGroupProps,
ItemProps,
MachineApi as Api,
CollectionOptions,
} from "./select.types"
4 changes: 2 additions & 2 deletions packages/machines/select/src/select.collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ export const collection = <T extends CollectionItem>(options: CollectionOptions<
return ref(new Collection(options))
}

collection.empty = (): Collection<any> => {
return ref(new Collection<any>({ items: [] }))
collection.empty = (): Collection<CollectionItem> => {
return ref(new Collection<CollectionItem>({ items: [] }))
}
4 changes: 2 additions & 2 deletions packages/machines/select/src/select.machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export function machine(userContext: UserDefinedContext) {
on: {
"TRIGGER.CLICK": {
target: "open",
actions: ["invokeOnOpen"],
actions: ["invokeOnOpen", "highlightFirstSelectedItem"],
},
"TRIGGER.FOCUS": {
target: "focused",
Expand All @@ -106,7 +106,7 @@ export function machine(userContext: UserDefinedContext) {
},
"TRIGGER.CLICK": {
target: "open",
actions: ["invokeOnOpen"],
actions: ["invokeOnOpen", "highlightFirstSelectedItem"],
},
"TRIGGER.ENTER": [
{
Expand Down
39 changes: 22 additions & 17 deletions packages/machines/select/src/select.types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { Collection, CollectionItem, CollectionItem as Item } from "@zag-js/collection"
import type { Collection, CollectionItem, CollectionOptions } from "@zag-js/collection"
import type { StateMachine as S } from "@zag-js/core"
import type { InteractOutsideHandlers } from "@zag-js/dismissable"
import type { TypeaheadState } from "@zag-js/dom-query"
import type { Placement, PositioningOptions } from "@zag-js/popper"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"

export type { CollectionOptions }

type ElementIds = Partial<{
content: string
trigger: string
Expand All @@ -17,17 +19,17 @@ type ElementIds = Partial<{
itemGroupLabel(id: string | number): string
}>

export type ValueChangeDetails = {
export type ValueChangeDetails<T extends CollectionItem = CollectionItem> = {
value: string[]
items: Item[]
items: T[]
}

export type HighlightChangeDetails = {
export type HighlightChangeDetails<T extends CollectionItem = CollectionItem> = {
value: string | null
item: Item | null
item: T | null
}

type PublicContext = DirectionProperty &
type PublicContext<T extends CollectionItem = any> = DirectionProperty &
CommonProperties &
InteractOutsideHandlers & {
/**
Expand Down Expand Up @@ -70,11 +72,11 @@ type PublicContext = DirectionProperty &
/**
* The callback fired when the highlighted item changes.
*/
onHighlight?: (details: HighlightChangeDetails) => void
onHighlight?: (details: HighlightChangeDetails<T>) => void
/**
* The callback fired when the selected item changes.
*/
onChange?: (details: ValueChangeDetails) => void
onChange?: (details: ValueChangeDetails<T>) => void
/**
* Function called when the popup is opened
*/
Expand Down Expand Up @@ -151,25 +153,28 @@ type ComputedContext = Readonly<{
/**
* The highlighted item
*/
highlightedItem: Item | null
highlightedItem: CollectionItem | null
/**
* @computed
* The selected items
*/
selectedItems: Item[]
selectedItems: CollectionItem[]
/**
* @computed
* The display value of the select (based on the selected items)
*/
valueAsString: string
}>

export type UserDefinedContext = RequiredBy<PublicContext, "id" | "collection">
export type UserDefinedContext<T extends CollectionItem = CollectionItem> = RequiredBy<
PublicContext<T>,
"id" | "collection"
>

export type MachineContext = PublicContext & PrivateContext & ComputedContext

export type ItemProps = {
item: Item
export type ItemProps<T extends CollectionItem = CollectionItem> = {
item: T
}

export type ItemState = {
Expand All @@ -195,7 +200,7 @@ export type ItemGroupLabelProps = {
htmlFor: string
}

export type MachineApi<T extends PropTypes = PropTypes> = {
export type MachineApi<T extends PropTypes = PropTypes, V extends CollectionItem = CollectionItem> = {
/**
* Whether the select is focused
*/
Expand All @@ -211,15 +216,15 @@ export type MachineApi<T extends PropTypes = PropTypes> = {
/**
* The highlighted item
*/
highlightedItem: CollectionItem | null
highlightedItem: V | null
/**
* The value of the combobox input
*/
highlightValue(value: string): void
/**
* The selected items
*/
selectedItems: CollectionItem[]
selectedItems: V[]
/**
* Whether there's a selected option
*/
Expand Down Expand Up @@ -263,7 +268,7 @@ export type MachineApi<T extends PropTypes = PropTypes> = {
/**
* Function to set the collection of items
*/
setCollection(collection: Collection<any>): void
setCollection(collection: Collection<V>): void

labelProps: T["label"]
triggerProps: T["button"]
Expand Down
Loading

4 comments on commit e4d78be

@vercel
Copy link

@vercel vercel bot commented on e4d78be Sep 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on e4d78be Sep 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

zag-vue – ./examples/vue-ts

zag-vue.vercel.app
zag-vue-chakra-ui.vercel.app
zag-vue-git-main-chakra-ui.vercel.app

@vercel
Copy link

@vercel vercel bot commented on e4d78be Sep 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

zag-nextjs – ./examples/next-ts

zag-two.vercel.app
zag-nextjs-git-main-chakra-ui.vercel.app
zag-nextjs-chakra-ui.vercel.app

@vercel
Copy link

@vercel vercel bot commented on e4d78be Sep 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

zag-solid – ./examples/solid-ts

zag-solid.vercel.app
zag-solid-chakra-ui.vercel.app
zag-solid-git-main-chakra-ui.vercel.app

Please sign in to comment.