Skip to content

Commit

Permalink
fix: ListBox now based on Radix Dropdown
Browse files Browse the repository at this point in the history
  • Loading branch information
MildTomato committed Feb 12, 2022
1 parent 3c8880f commit aae004f
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 176 deletions.
17 changes: 9 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,26 @@
"react-dom": "^17.0.1"
},
"dependencies": {
"@headlessui/react": "^1.0.0",
"@mertasan/tailwindcss-variables": "^2.0.1",
"@radix-ui/colors": "^0.1.8",
"@radix-ui/react-accordion": "^0.1.5",
"@radix-ui/react-collapsible": "^0.1.5",
"@radix-ui/react-context-menu": "^0.1.0",
"@radix-ui/react-dialog": "^0.1.5",
"@radix-ui/react-dropdown-menu": "^0.1.0",
"@radix-ui/react-dropdown-menu": "^0.1.4",
"@radix-ui/react-popover": "^0.1.0",
"@radix-ui/react-portal": "^0.1.3",
"@radix-ui/react-tabs": "^0.1.0",
"@mertasan/tailwindcss-variables": "^2.0.1",
"tailwindcss-radix": "^1.6.0",
"formik": "^2.2.9",
"@tailwindcss/forms": "^0.4.0",
"@tailwindcss/typography": "^0.5.0",
"@headlessui/react": "^1.0.0",
"tailwindcss": "^3.0.15",
"postcss": "^8.4.5",
"autoprefixer": "^10.4.2",
"formik": "^2.2.9",
"lodash": "^4.17.20",
"prop-types": "^15.7.2"
"postcss": "^8.4.5",
"prop-types": "^15.7.2",
"tailwindcss": "^3.0.15",
"tailwindcss-radix": "^1.6.0"
},
"devDependencies": {
"@babel/cli": "^7.11.6",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
/* This example requires Tailwind CSS v2.0+ */
import React, { Fragment, ReactNode, useEffect, useState } from 'react'
import { Listbox as HeadlessListbox, Transition } from '@headlessui/react'
import React, { useEffect, useRef, useState } from 'react'
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
import { FormLayout } from '../../lib/Layout/FormLayout'
// @ts-ignore
// import SelectStyles from './SelectStyled.module.css'

import InputIconContainer from '../../lib/Layout/InputIconContainer'
import InputErrorIcon from '../../lib/Layout/InputErrorIcon'
import { IconCheck } from '../Icon/icons/IconCheck'

import { useFormContext } from '../Form/FormContext'

import { flatten } from 'lodash'

import { SelectContext } from './SelectContext'

import styleHandler from '../../lib/theme/styleHandler'
import { Button } from '../Button'

function classNames(...classes: any) {
return classes.filter(Boolean).join(' ')
Expand Down Expand Up @@ -51,7 +51,7 @@ function Listbox({
label,
labelOptional,
layout,
value,
value = undefined,
onChange,
onFocus,
onBlur,
Expand All @@ -76,38 +76,48 @@ function Listbox({
fieldLevelValidation,
} = useFormContext()

if (values && !value) defaultValue = values[id || name]
if (values && !value) {
console.log('WRONG THING RAN 1')
defaultValue = values[id || name]
}
if (handleBlur) onBlur = handleBlur

if (!error) {
if (errors && !error) error = errors[id || name]
error = touched && touched[id || name] ? error : undefined
}

useEffect(() => {
if (defaultValue) {
setSelected(defaultValue)
}
}, [defaultValue])

useEffect(() => {
if (value) {
console.log('value useeffect')
setSelected(value)
}
}, [value])

useEffect(() => {
console.log(triggerRef?.current?.offsetWidth)
if (document) {
document.documentElement.style.setProperty('--width-listbox', `533px`)
}
}, [])

useEffect(() => {
const data: any = children
const content: any = flatten(data)

function findNode(value: any) {
return content.find((node: any) => node.props.value == value)
console.log('selected in useEffect', selected)

console.log('value has changed')

function findNode(_value: any) {
return content.find((node: any) => node.props.value === _value)
}

/*
* value prop overrides everything
*/
if (value) {
console.log('if value')
setSelected(value)
const node: any = findNode(value)
setSelectedNode(node?.props ? node.props : undefined)
Expand All @@ -118,15 +128,25 @@ function Listbox({
* if no value prop, then use selected state
*/
if (selected) {
console.log('if selected')
const node: any = findNode(selected)
setSelectedNode(node?.props ? node.props : undefined)
return
} else if (defaultValue) {
console.log('if defaultValue')
setSelected(defaultValue)
const node: any = findNode(selected)
setSelectedNode(node?.props ? node.props : undefined)
return
} else {
console.log('else')
/*
* if no selected value (including a `defaultvalue`), then use first child
*/
setSelectedNode(content[0].props)
return
}
}, [children, selected, value])
}, [selected])

function handleOnChange(value: any) {
// console.log('listbox onchange e', value)
Expand Down Expand Up @@ -156,7 +176,7 @@ function Listbox({
if (validation) fieldLevelValidation(id, validation(value))
}

let selectClasses = [__styles.base]
let selectClasses = [__styles.container, __styles.base]
if (error) selectClasses.push(__styles.variants.error)
if (!error) selectClasses.push(__styles.variants.standard)
// if (icon) selectClasses.push(SelectStyles['sbui-listbox--with-icon'])
Expand All @@ -166,6 +186,8 @@ function Listbox({
// if (borderless) selectClasses.push(SelectStyles['sbui-listbox--borderless'])
if (disabled) selectClasses.push(__styles.disabled)

const triggerRef = useRef(null)

return (
<FormLayout
label={label}
Expand All @@ -178,71 +200,58 @@ function Listbox({
style={style}
size={size}
>
<div className={__styles.container}>
<HeadlessListbox value={selected} onChange={handleOnChange}>
{({ open }) => {
return (
<div className="relative">
<HeadlessListbox.Button
className={selectClasses.join(' ')}
name={name}
id={id}
onBlur={onBlur}
onFocus={onFocus}
>
{icon && <InputIconContainer icon={icon} />}
<span className={__styles.addOnBefore}>
{selectedNode?.addOnBefore && <selectedNode.addOnBefore />}
<span className={__styles.label}>
{selectedNode?.label}
</span>
</span>
<span className={__styles.chevron_container}>
<svg
className={__styles.chevron}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fillRule="evenodd"
d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
</span>
{error && (
<div className={__styles.actions_container}>
{error && <InputErrorIcon size={size} />}
</div>
)}
</HeadlessListbox.Button>
{/* <Transition
show={open}
as={Fragment}
// leave={SelectStyles['sbui-listbox-transition--leave']}
// leaveFrom={
// SelectStyles['sbui-listbox-transition--leave-from']
// }
// leaveTo={SelectStyles['sbui-listbox-transition--leave-to']}
> */}
<div
data-state={open ? 'open' : 'closed'}
className={__styles.options_container_animate}
>
<HeadlessListbox.Options
className={__styles.options_container}
>
{children}
</HeadlessListbox.Options>
</div>
{/* </Transition> */}
<DropdownMenuPrimitive.Root>
<DropdownMenuPrimitive.Trigger ref={triggerRef} asChild>
<button
className={selectClasses.join(' ')}
onBlur={onBlur}
onFocus={onFocus}
name={name}
id={id}
>
{/* {icon && <InputIconContainer icon={icon} />} */}
<span className={__styles.addOnBefore}>
{selectedNode?.addOnBefore && <selectedNode.addOnBefore />}
<span className={__styles.label}>{selectedNode?.label}</span>
</span>
<span className={__styles.chevron_container}>
<svg
className={__styles.chevron}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fillRule="evenodd"
d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
</span>
{error && (
<div className={__styles.actions_container}>
{error && <InputErrorIcon size={size} />}
</div>
)
}}
</HeadlessListbox>
</div>
)}
</button>
</DropdownMenuPrimitive.Trigger>
<DropdownMenuPrimitive.Content
sideOffset={6}
loop={true}
side={'bottom'}
align="center"
className={__styles.options_container}
>
<div>
<SelectContext.Provider
value={{ onChange: handleOnChange, selected }}
>
{children}
</SelectContext.Provider>
</div>
</DropdownMenuPrimitive.Content>
</DropdownMenuPrimitive.Root>
</FormLayout>
)
}
Expand Down Expand Up @@ -275,20 +284,27 @@ function SelectOption({

const __styles = styleHandler('listbox')

// const active = false
// const selected = false

return (
<HeadlessListbox.Option key={id} value={value} disabled={disabled}>
{({ selected, active }) => {
// if (active) {
// console.log('selected', selected, 'active', active)
// console.log(label)
// }
<SelectContext.Consumer>
{({ onChange, selected }) => {
// console.log('selected inside SelectOption', selected)
// console.log('onChange inside SelectOption', onChange)

const active = selected === value ? true : false

console.log('active inside SelectOption', active, value)
return (
<div
<DropdownMenuPrimitive.Item
key={id}
className={classNames(
__styles.option,
active ? __styles.option_active : ' ',
disabled ? __styles.option_disabled : ' '
)}
onSelect={() => onChange(value)}
>
<div className={__styles.option_inner}>
{addOnBefore && addOnBefore({ active, selected })}
Expand All @@ -299,7 +315,7 @@ function SelectOption({
</span>
</div>

{selected ? (
{active ? (
<span
className={classNames(
__styles.option_check,
Expand All @@ -312,10 +328,10 @@ function SelectOption({
/>
</span>
) : null}
</div>
</DropdownMenuPrimitive.Item>
)
}}
</HeadlessListbox.Option>
</SelectContext.Consumer>
)
}

Expand Down
34 changes: 4 additions & 30 deletions src/components/Listbox/SelectContext.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,8 @@
import React, { createContext, useContext } from 'react'
import { createContext } from 'react'

interface ContextProps {
formContextOnChange: any
}
// Make sure the shape of the default value passed to
// createContext matches the shape that the consumers expect!
const SelectContext = createContext<ContextProps>({
formContextOnChange: null,
export const SelectContext = createContext({
onChange: (e: any) => {},
selected: undefined,
})

export const SelectContextProvider = (props: any) => {
const { formContextOnChange } = props

const value = {
formContextOnChange: formContextOnChange,
}
return (
<SelectContext.Provider value={value} {...props}>
{props.children}
</SelectContext.Provider>
)
}

// context helper to avoid using a consumer component
export const useSelectContext = () => {
const context = useContext(SelectContext)
if (context === undefined) {
throw new Error(
`useFormContextOnChange must be used within a FormContextProvider.`
)
}
return context
}
Loading

0 comments on commit aae004f

Please sign in to comment.