Skip to content

Commit

Permalink
Merge pull request #188 from ynput/develop
Browse files Browse the repository at this point in the history
Release: EnumDropdown
  • Loading branch information
Innders authored Oct 2, 2024
2 parents f248402 + 7d23224 commit 8e90c28
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 16 deletions.
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ This is an open source project. We welcome contributions.
🙋 Need some support or want something custom? Take a look at our services.

## Get Started with Storybook

Run locally: `yarn storybook`

build: `yarn build-storybook`

build-package: `yarn build`
After cloning the repo, you'll need to run `yarn install`. Then, you can:
- **Run locally:** `yarn storybook`
- **Build:** `yarn build-storybook`
- **Build package:** `yarn build`
124 changes: 124 additions & 0 deletions src/Dropdowns/EnumDropdown/EnumDropdown.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import type { Meta, StoryObj } from '@storybook/react'
import { EnumDropdown, EnumDropdownProps } from '.'
import { useState } from 'react'

const meta: Meta<typeof EnumDropdown> = {
component: EnumDropdown,
tags: ['autodocs'],
}

export default meta

type Story = StoryObj<typeof EnumDropdown>

const Template = ({ value: initValue, ...props }: EnumDropdownProps) => {
const [value, setValue] = useState(initValue)

return <EnumDropdown value={value} {...props} onChange={(value) => setValue(value)} />
}

const priorities: EnumDropdownProps['options'] = [
{
value: 'low',
label: 'Low',
icon: 'keyboard_double_arrow_down',
color: '#9FA7B1',
},
{
value: 'normal',
label: 'Normal',
color: '#9AC0E7',
icon: 'check_indeterminate_small',
},
{
value: 'high',
label: 'High',
color: '#FFAD66',
icon: 'keyboard_arrow_up',
},
{
value: 'urgent',
label: 'Urgent',
color: '#FF8585',
icon: 'keyboard_double_arrow_up',
},
]

const initArgs: Story['args'] = {
onClear: undefined,
onClearNull: undefined,
}

const priorityValue = 'low'

export const Priority: Story = {
args: {
...initArgs,
value: [priorityValue],
options: priorities,
},
render: Template,
}
export const InverseColors: Story = {
args: {
...initArgs,
value: [priorityValue],
options: priorities,
colorInverse: true,
},
render: Template,
}
export const OnlyColors: Story = {
args: {
...initArgs,
value: [priorityValue],
options: priorities.map(({ color, value, label }) => ({ color, value, label })),
},
render: Template,
}
export const OnlyIcons: Story = {
args: {
...initArgs,
value: [priorityValue],
options: priorities.map(({ icon, value, label }) => ({ icon, value, label })),
},
render: Template,
}
export const OnlyLabels: Story = {
args: {
...initArgs,
value: [priorityValue],
options: priorities.map(({ value, label }) => ({ value, label })),
},
render: Template,
}
export const MixedIconsAndColors: Story = {
args: {
...initArgs,
value: [priorityValue],
// some options have icons, some have colors, some have both, some have neither
options: [
{
value: 'low',
label: 'Low',
icon: 'keyboard_double_arrow_down',
color: '#9FA7B1',
},
{
value: 'normal',
label: 'Normal',
color: '#9AC0E7',
},
{
value: 'high',
label: 'High',
icon: 'keyboard_arrow_up',
},
{
value: 'urgent',
label: 'Urgent',
},
],
},
render: Template,
}
68 changes: 68 additions & 0 deletions src/Dropdowns/EnumDropdown/EnumDropdown.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import styled, { css } from 'styled-components'
import { DefaultValueTemplate } from '../Dropdown'

interface IconStyledProps {
$color?: string
}

const getSelectedBg = ({ $color }: IconStyledProps) => {
if ($color)
return css`
background: ${$color};
&:hover {
filter: brightness(1.1);
}
.value-label,
.icon {
color: var(--md-sys-color-inverse-on-surface);
}
`
else
return css`
background: var(--md-sys-color-primary-container);
&:hover {
background: var(--md-sys-color-primary-container-hover);
}
.value-label,
.icon {
color: var(--md-sys-color-on-primary-container);
}
`
}

export const StyledDefaultValueTemplate = styled(DefaultValueTemplate)<IconStyledProps>`
padding-left: 0;
&.inverse {
${({ $color }) => getSelectedBg({ $color })}
border-color: ${({ $color }) => $color && 'transparent'};
}
`

export const Option = styled.div<IconStyledProps>`
display: flex;
align-items: center;
/* justify-content: center; */
gap: 8px;
padding-left: 0.5rem;
height: 32px;
span:last-child {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
width: 100%;
.value-label,
.icon {
color: ${({ $color }) => ($color ? $color : `var(--md-sys-color-on-surface)`)};
}
&.selected {
${({ $color }) => getSelectedBg({ $color })}
}
`
60 changes: 60 additions & 0 deletions src/Dropdowns/EnumDropdown/EnumDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { forwardRef, useMemo } from 'react'
import * as Styled from './EnumDropdown.styled'
import { DefaultValueTemplate, Dropdown, DropdownProps, DropdownRef } from '../Dropdown'
import { Icon, IconType } from '../../Icon'
import clsx from 'clsx'

export interface EnumTemplateProps {
option: EnumDropdownOption | null | undefined
isSelected?: boolean
}

const EnumTemplate = ({ option, isSelected }: EnumTemplateProps) => {
const { value, label, icon, color } = option || {}
return (
<Styled.Option className={clsx({ selected: isSelected })} id={value} $color={color}>
{icon && <Icon icon={icon} />}
<span className="value-label">{label}</span>
</Styled.Option>
)
}

export type EnumDropdownOption = {
value: string
label: string
icon?: IconType
color?: string
}

export interface EnumDropdownProps
extends Omit<DropdownProps, 'options' | 'valueTemplate' | 'itemTemplate' | 'ref'> {
options: EnumDropdownOption[]
colorInverse?: boolean
}

export const EnumDropdown = forwardRef<DropdownRef, EnumDropdownProps>(
({ colorInverse, ...props }, ref) => {
return (
<Dropdown
ref={ref}
valueTemplate={(v, s, o) => {
const option = props.options.find((op) => op.value === v[0])
return (
<Styled.StyledDefaultValueTemplate
isOpen={o}
{...props}
$color={option?.color}
className={clsx({ inverse: colorInverse })}
>
<EnumTemplate option={option} />
</Styled.StyledDefaultValueTemplate>
)
}}
itemTemplate={(option, isSelected) => (
<EnumTemplate option={option} isSelected={isSelected} />
)}
{...props}
/>
)
},
)
1 change: 1 addition & 0 deletions src/Dropdowns/EnumDropdown/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './EnumDropdown'
1 change: 1 addition & 0 deletions src/Dropdowns/IconSelect/IconSelect.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const Template = ({ value: initValue, featured, multiSelect, featuredOnly }: Ico
<IconSelect
{...{ value, featured, multiSelect, featuredOnly }}
onChange={(value) => setValue(value)}
widthExpand
/>
)
}
Expand Down
4 changes: 1 addition & 3 deletions src/Dropdowns/IconSelect/IconSelect.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ export const Icon = styled.div<IconStyledProps>`
gap: 8px;
padding-left: 0.5rem;
height: 30px;
height: 32px;
span:last-child {
overflow: hidden;
white-space: nowrap;
Expand Down
20 changes: 13 additions & 7 deletions src/Dropdowns/IconSelect/IconSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { forwardRef, useMemo } from 'react'
import * as Styled from './IconSelect.styled'
import { Dropdown, DropdownProps, DropdownRef } from '../Dropdown'
import { DefaultValueTemplate, Dropdown, DropdownProps, DropdownRef } from '../Dropdown'
import { Icon, IconType, iconSet } from '../../Icon'

export interface IconTemplateProps {
value: IconSelectProps['value']
valueTemplate?: boolean
isActive?: boolean
isSelected?: boolean
}

const IconTemplate = ({ value, valueTemplate, isActive, isSelected }: IconTemplateProps) => {
const IconTemplate = ({ value, isSelected }: IconTemplateProps) => {
return (
<Styled.Icon $valueTemplate={valueTemplate} $isActive={isSelected}>
<Styled.Icon $isActive={isSelected}>
{value?.map((icon) => (
<Icon key={icon} icon={icon as IconType} />
))}
Expand All @@ -31,7 +30,10 @@ export interface IconSelectProps
}

export const IconSelect = forwardRef<DropdownRef, IconSelectProps>(
({ value, onChange, featured = [], multiSelect, featuredOnly, ...props }, ref) => {
(
{ value, onChange, featured = [], multiSelect, featuredOnly, widthExpand = false, ...props },
ref,
) => {
const dropdownOptions = useMemo(() => {
const dropdownOptions = []

Expand All @@ -57,7 +59,11 @@ export const IconSelect = forwardRef<DropdownRef, IconSelectProps>(
<Dropdown
value={value}
multiSelect={multiSelect}
valueTemplate={(v, s, o) => <IconTemplate value={(o ? s : v) || []} valueTemplate />}
valueTemplate={(v, s, o) => (
<DefaultValueTemplate value={(o ? s : v) || []} isOpen={o}>
<IconTemplate value={(o ? s : v) || []} />
</DefaultValueTemplate>
)}
options={dropdownOptions}
itemTemplate={({ value }, isActive, isSelected) => (
<IconTemplate value={[value]} isActive={isActive} isSelected={isSelected} />
Expand All @@ -70,7 +76,7 @@ export const IconSelect = forwardRef<DropdownRef, IconSelectProps>(
{...props}
maxOptionsShown={Math.max(props.maxOptionsShown || 25, featured.length)}
minSelected={1}
widthExpand={false}
widthExpand={widthExpand}
/>
)
},
Expand Down
3 changes: 3 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export type { TagsSelectProps } from './Dropdowns/TagsSelect'
// iconSelect
export { IconSelect } from './Dropdowns/IconSelect'
export type { IconSelectProps } from './Dropdowns/IconSelect'
// enumDropdown
export { EnumDropdown } from './Dropdowns/EnumDropdown'
export type { EnumDropdownProps } from './Dropdowns/EnumDropdown'
// versionSelect
export { VersionSelect } from './Dropdowns/VersionSelect'
export type { VersionSelectProps } from './Dropdowns/VersionSelect'
Expand Down

0 comments on commit 8e90c28

Please sign in to comment.