Before using ListBox:
store/index.ts
import { action, makeObservable, observable } from 'mobx'
import type { IFrameItStore, TrafficSignal } from '@/types/index'
export class FrameItStore implements IFrameItStore {
trafficSignal: TrafficSignal = {
shape: 'circle',
}
constructor() {
makeObservable(this, {
trafficSignal: observable,
updateTrafficSignal: action.bound,
})
}
updateTrafficSignal({ shape }: TrafficSignal) {
if (shape) this.trafficSignal.shape = shape
}
}
Shape.tsx
import { observer } from 'mobx-react'
import * as React from 'react'
import { useFrameItStore } from '@/store/index'
import type { TrafficSignalShape } from '@/types/index'
export const Shape = observer(() => {
const frameItStore = useFrameItStore()
return (
<>
<label htmlFor="shape" className="mb-1 text-sm font-medium text-blue-gray-500">
Shape
</label>
<select
id="shape"
className="block w-full px-3 py-2 mb-2 bg-white border border-gray-300 rounded-md shadow-sm text-blue-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
value={frameItStore.trafficSignal.shape}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
const shape = e.target.value as TrafficSignalShape
frameItStore.updateTrafficSignal({ shape })
}}
>
<option value="circle">Circle</option>
<option value="square">Square</option>
</select>
</>
)
})
App.tsx
<Shape />
After using ListBox:
Select.tsx
import * as React from 'react'
import { Listbox, Transition } from '@headlessui/react'
import clsx from 'clsx'
import { Selector, Check } from '@/components/icons/index'
type Option = {
id: string
name: string
img: string
}
interface IProps {
label?: string
options: Array<Option>
}
export const Select = ({ label, options }: IProps) => {
const [selectedOption, setSelectedOption] = React.useState<Option>(options[0])
return (
<Listbox value={selectedOption} onChange={setSelectedOption}>
{({ open }) => (
<>
<Listbox.Label className="mb-1 text-sm font-medium text-blue-gray-500">
{label}
</Listbox.Label>
<div className="relative mt-1">
<Listbox.Button className="relative w-full py-2 pl-3 pr-10 text-left bg-white border border-gray-300 rounded-md shadow-sm cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
<span className="flex items-center">
<img
src={selectedOption.img}
alt={selectedOption.name}
className="flex-shrink-0 w-6 h-6 rounded-full"
/>
<span className="block ml-3 truncate">{selectedOption.name}</span>
</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 ml-3 pointer-events-none">
<Selector />
</span>
</Listbox.Button>
<div className="absolute w-full mt-1 bg-white rounded-md shadow-lg">
<Transition
show={open}
leave="transition duration-100 ease-in"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options
static
className="py-1 overflow-auto text-base rounded-md max-h-56 ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
>
{options.map((option) => (
<Listbox.Option as={React.Fragment} key={option.id} value={option}>
{({ active, selected }) => (
<li
className={clsx('relative py-2 pl-3 cursor-default select-none pr-9', {
'text-white bg-indigo-600': active,
'text-gray-900': !active,
})}
>
<div className="flex items-center">
<img
src={option.img}
alt={option.name}
className="flex-shrink-0 w-6 h-6 rounded-full"
/>
<span
className={clsx('ml-3 block truncate', {
'font-semibold': selected,
'font-normal': !selected,
})}
>
{option.name}
</span>
</div>
{selected && (
<span
className={clsx('absolute inset-y-0 right-0 flex items-center pr-4', {
'text-white': active,
'text-indigo-600': !active,
})}
>
<Check />
</span>
)}
</li>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</div>
</>
)}
</Listbox>
)
}
App.tsx
const shapes = [
{
id: '1',
name: 'Circle',
img:
'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
},
{
id: '2',
name: 'Square',
img:
'https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
},
]
<Select label="Shape" options={shapes} />
How do I convert the After part to use MobX like the Before part?
I tried passing value
& onChange
as it is in the Before part to Select like:
App.tsx
<Select
label="Shape"
options={shapes}
value={frameItStore.trafficSignal.shape}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
const shape = e.target.value as TrafficSignalShape
frameItStore.updateTrafficSignal({ shape })
}}
/>
Select.tsx
interface IProps {
label?: string
value: any
onChange: (value: any) => void
options: Array<Option>
}
export const Select = ({ label, options, value, onChange }: IProps) => {
const [selectedOption, setSelectedOption] = React.useState<Option>(options[0])
return (
<Listbox value={value} onChange={onChange}>
.
.
.
</Listbox>
)
}
But it doesn't select anything & I don't know what to do of selectedOption
?
from Use `ListBox` from `@headlessui/react` with Mobx?
No comments:
Post a Comment