-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement React Slots RFC (#10)
* WIP: add rfc * add tests * Host -> HostSlots * add createFill in test * add isSlot util * update after rebase * reexport
- Loading branch information
Showing
22 changed files
with
1,394 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import React, { useId } from 'react' | ||
import { createHost, createSlot, getSlot } from 'create-slots/list' | ||
|
||
type ItemProps = Omit<React.ComponentPropsWithoutRef<'li'>, 'value'> & { | ||
value: string | ||
} | ||
|
||
const ItemTitle = createSlot<'h4'>() | ||
const ItemDescription = createSlot<'div'>() | ||
|
||
export const Item = (props: ItemProps) => { | ||
const id = useId() | ||
|
||
return createHost(props.children, (slots) => { | ||
const titleSlot = getSlot(slots, ItemTitle) | ||
const descriptionSlot = getSlot(slots, ItemDescription) | ||
const titleId = titleSlot ? `${id}-title` : undefined | ||
const descId = descriptionSlot ? `${id}-desc` : undefined | ||
|
||
return ( | ||
<li aria-describedby={descId} aria-label={titleId} {...props}> | ||
{titleSlot && ( | ||
<h4 id={titleId} ref={titleSlot.ref} {...titleSlot.props} /> | ||
)} | ||
{descriptionSlot && ( | ||
<div | ||
id={descId} | ||
ref={descriptionSlot.ref} | ||
{...descriptionSlot.props} | ||
/> | ||
)} | ||
</li> | ||
) | ||
}) | ||
} | ||
|
||
Item.Title = ItemTitle | ||
Item.Description = ItemDescription |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import React, { useRef, useState } from 'react' | ||
import { createHost, createSlot, getSlotProps, isSlot } from 'create-slots/list' | ||
|
||
import { Item } from './RFCItem' | ||
|
||
const SelectItem = createSlot<typeof Item>() | ||
const SelectDivider = createSlot('hr') | ||
|
||
export const Select = (props: React.ComponentProps<'ul'>) => { | ||
const [selected, setSelected] = useState<React.ReactNode>(null) | ||
const indexRef = useRef(0) | ||
|
||
return ( | ||
<div> | ||
<div>Selected: {selected}</div> | ||
{createHost(props.children, (slots) => { | ||
indexRef.current = 0 | ||
return ( | ||
<ul {...props}> | ||
{slots.map((slot) => { | ||
if (isSlot(slot, SelectItem)) { | ||
const itemProps = getSlotProps(slot) | ||
|
||
return ( | ||
<Item | ||
key={slot.key} | ||
{...itemProps} | ||
role="button" | ||
tabIndex={0} | ||
data-index={indexRef.current++} | ||
aria-selected={itemProps.value === selected} | ||
onClick={() => setSelected(itemProps.value)} | ||
onKeyDown={(event) => { | ||
if (event.key === 'Enter' || event.key === ' ') { | ||
setSelected(itemProps.value) | ||
} | ||
}} | ||
/> | ||
) | ||
} | ||
|
||
return slot | ||
})} | ||
</ul> | ||
) | ||
})} | ||
</div> | ||
) | ||
} | ||
|
||
Select.Item = SelectItem | ||
Select.Divider = SelectDivider | ||
|
||
Select.Item.Title = Item.Title | ||
Select.Item.Description = Item.Description |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import React, { useId, useState } from 'react' | ||
import { createHost, createSlot } from 'create-slots' | ||
|
||
const Description = (props: React.ComponentPropsWithoutRef<'div'>) => ( | ||
<div | ||
{...props} | ||
style={{ borderLeft: '4px solid lightgray', paddingLeft: 4 }} | ||
/> | ||
) | ||
|
||
const FieldLabel = createSlot('label') | ||
const FieldInput = createSlot('input') | ||
const FieldDescription = createSlot(Description) | ||
|
||
const StyledLabel = (props: React.ComponentPropsWithoutRef<'label'>) => ( | ||
<FieldLabel {...props} style={{ color: 'red' }} /> | ||
) | ||
|
||
export const Field = (props: React.ComponentPropsWithoutRef<'div'>) => { | ||
const id = useId() | ||
const [value, setValue] = useState('') | ||
|
||
if (value === 'a') return null | ||
|
||
return ( | ||
<div {...props}> | ||
{createHost(props.children, (Slots) => { | ||
const labelProps = Slots.getProps(FieldLabel) | ||
const inputProps = Slots.getProps(FieldInput) | ||
const descriptionIdProps = Slots.getProps(FieldDescription) | ||
|
||
const inputId = inputProps?.id || `${id}-label` | ||
const descriptionId = descriptionIdProps ? `${id}-desc` : undefined | ||
|
||
return ( | ||
<> | ||
{labelProps && <label {...labelProps} htmlFor={inputId} />} | ||
{inputProps && ( | ||
<input | ||
value={value} | ||
onChange={(e) => setValue(e.target.value)} | ||
id={inputId} | ||
aria-describedby={descriptionId} | ||
{...inputProps} | ||
/> | ||
)} | ||
{descriptionIdProps && ( | ||
<Description id={descriptionId} {...descriptionIdProps} /> | ||
)} | ||
</> | ||
) | ||
})} | ||
</div> | ||
) | ||
} | ||
|
||
Field.Label = FieldLabel | ||
Field.Input = FieldInput | ||
Field.Description = FieldDescription | ||
Field.StyledLabel = StyledLabel |
Oops, something went wrong.
19ddfe0
There was a problem hiding this comment.
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:
create-slots – ./
create-slots.vercel.app
create-slots-neonie.vercel.app
create-slots-git-master-neonie.vercel.app