feat: add select component recipe with semantic color variants
This commit is contained in:
@@ -14,6 +14,7 @@ import alertRecipe from './recipes/alert.js'
|
||||
import chipRecipe from './recipes/chip.js'
|
||||
import iconButtonRecipe from './recipes/iconButton.js'
|
||||
import markRecipe from './recipes/mark.js'
|
||||
import selectRecipe from './recipes/select.js'
|
||||
|
||||
export type ThemeConfig = {
|
||||
neutral?: NeutralColor
|
||||
@@ -48,7 +49,8 @@ const srJuggernautPandaPreset = (config?: ThemeConfig) => {
|
||||
details: detailsRecipe({ semanticColorNames: semanticColorKeysArray }),
|
||||
mark: markRecipe({ semanticColorNames: semanticColorKeysArray }),
|
||||
input: inputRecipe({ semanticColorNames: semanticColorKeysArray }),
|
||||
iconButton: iconButtonRecipe({ semanticColorNames: semanticColorKeysArray })
|
||||
iconButton: iconButtonRecipe({ semanticColorNames: semanticColorKeysArray }),
|
||||
select: selectRecipe({ semanticColorNames: semanticColorKeysArray })
|
||||
},
|
||||
tokens: {
|
||||
animations: {},
|
||||
@@ -186,6 +188,11 @@ const srJuggernautPandaPreset = (config?: ThemeConfig) => {
|
||||
}
|
||||
}
|
||||
},
|
||||
conditions: {
|
||||
extend: {
|
||||
active: '&[data-state="open"]'
|
||||
}
|
||||
},
|
||||
globalCss: {
|
||||
'*, *:before, *:after': {
|
||||
boxSizing: 'border-box',
|
||||
|
||||
234
src/recipes/select.ts
Normal file
234
src/recipes/select.ts
Normal file
@@ -0,0 +1,234 @@
|
||||
import { defineSlotRecipe, type SystemStyleObject } from '@pandacss/dev'
|
||||
|
||||
const selectSlots = [
|
||||
'trigger',
|
||||
'content',
|
||||
'viewport',
|
||||
'scrollButton',
|
||||
'item',
|
||||
'itemIndicator',
|
||||
'group',
|
||||
'groupLabel',
|
||||
'separator'
|
||||
] as const
|
||||
|
||||
export interface SelectRecipeArg {
|
||||
semanticColorNames: string[]
|
||||
}
|
||||
|
||||
const selectRecipe = ({ semanticColorNames }: SelectRecipeArg) => {
|
||||
const colorVariants = ['neutral', ...semanticColorNames]
|
||||
return defineSlotRecipe({
|
||||
className: 'select',
|
||||
slots: selectSlots,
|
||||
base: {
|
||||
trigger: {
|
||||
all: 'unset',
|
||||
boxSizing: 'border-box',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: 'xs',
|
||||
minWidth: 'fit-content',
|
||||
borderRadius: 'sm',
|
||||
border: '1px solid',
|
||||
cursor: 'pointer',
|
||||
transitionProperty: 'color, background-color, border-color',
|
||||
transitionDuration: 'normal',
|
||||
transitionTimingFunction: 'easeOut'
|
||||
},
|
||||
content: {
|
||||
overflow: 'hidden',
|
||||
maxHeight: 'var(--radix-select-content-available-height, 90dvh)',
|
||||
width: 'var(--radix-select-trigger-width, min(max-content, 90dvw))',
|
||||
backgroundColor: 'var(--select-background-color)',
|
||||
color: 'var(--select-color)',
|
||||
borderRadius: 'sm',
|
||||
_active: {
|
||||
animationName: 'fade',
|
||||
animationDuration: 'normal'
|
||||
},
|
||||
'&[data-state=closed]': {
|
||||
animationName: 'fade',
|
||||
animationDuration: 'normal',
|
||||
animationDirection: 'reverse'
|
||||
}
|
||||
},
|
||||
viewport: {
|
||||
padding: 'xs'
|
||||
},
|
||||
scrollButton: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: 'var(--select-color-subtle)',
|
||||
cursor: 'pointer',
|
||||
transitionProperty: 'color, background-color',
|
||||
transitionDuration: 'normal',
|
||||
transitionTimingFunction: 'easeOut',
|
||||
'&:hover': {
|
||||
color: 'var(--select-color)'
|
||||
}
|
||||
},
|
||||
item: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
gap: 'xs',
|
||||
padding: 'xs',
|
||||
borderRadius: 'sm',
|
||||
cursor: 'pointer',
|
||||
transitionProperty: 'color, background-color',
|
||||
transitionDuration: 'normal',
|
||||
transitionTimingFunction: 'easeOut',
|
||||
_highlighted: {
|
||||
backgroundColor: 'var(--select-background-color-hover)'
|
||||
},
|
||||
'&[data-state=checked]': {
|
||||
backgroundColor: 'var(--select-background-color-active)'
|
||||
},
|
||||
_disabled: {
|
||||
color: 'var(--select-color-disabled)',
|
||||
backgroundColor: 'var(--select-background-color-disabled)',
|
||||
cursor: 'not-allowed',
|
||||
_highlighted: {
|
||||
backgroundColor: 'var(--select-background-color-disabled)',
|
||||
color: 'var(--select-color-disabled)'
|
||||
}
|
||||
}
|
||||
},
|
||||
itemIndicator: {
|
||||
color: 'var(--select-color-subtle)'
|
||||
},
|
||||
group: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 'xs',
|
||||
marginInline: '-xs',
|
||||
borderBlock: '1px solid var(--select-border-color)',
|
||||
paddingInline: 'xs'
|
||||
},
|
||||
groupLabel: {
|
||||
color: 'var(--select-color-subtle)',
|
||||
fontSize: 'sm',
|
||||
fontWeight: 'semibold',
|
||||
lineHeight: 'tight',
|
||||
userSelect: 'none'
|
||||
},
|
||||
separator: {
|
||||
height: '1px',
|
||||
backgroundColor: 'var(--select-border-color)',
|
||||
marginBlock: 'xs',
|
||||
marginInline: '-xs'
|
||||
}
|
||||
},
|
||||
variants: {
|
||||
size: {
|
||||
small: {
|
||||
trigger: {
|
||||
paddingBlock: 'calc({spacing.xs} * 0.5)',
|
||||
paddingInline: 'calc({spacing.xs} * 1)',
|
||||
fontSize: 'sm'
|
||||
}
|
||||
},
|
||||
medium: {
|
||||
trigger: {
|
||||
paddingBlock: 'calc({spacing.xs} * 0.75)',
|
||||
paddingInline: 'calc({spacing.xs} * 1.25)',
|
||||
fontSize: 'base'
|
||||
}
|
||||
},
|
||||
large: {
|
||||
trigger: {
|
||||
paddingBlock: 'calc({spacing.xs} * 1)',
|
||||
paddingInline: 'calc({spacing.xs} * 1.5)',
|
||||
fontSize: 'lg'
|
||||
}
|
||||
}
|
||||
},
|
||||
color: {
|
||||
...Object.fromEntries(
|
||||
colorVariants.map((color) => [
|
||||
color,
|
||||
Object.fromEntries(
|
||||
selectSlots.map((slot) => {
|
||||
switch (slot) {
|
||||
case 'trigger':
|
||||
// Trigger is outside the content, so it needs to be set independently
|
||||
return [
|
||||
slot,
|
||||
{
|
||||
backgroundColor: `${color}.2`,
|
||||
color: `${color}.12`,
|
||||
borderColor: `${color}.6`,
|
||||
_placeholder: {
|
||||
color: `${color}.11`
|
||||
},
|
||||
_hover: {
|
||||
backgroundColor: `${color}.3`
|
||||
},
|
||||
_active: {
|
||||
backgroundColor: `${color}.4`
|
||||
},
|
||||
|
||||
_focus: {
|
||||
backgroundColor: `${color}.3`,
|
||||
borderColor: `${color}.7`
|
||||
},
|
||||
_disabled: {
|
||||
cursor: 'not-allowed',
|
||||
backgroundColor: `color-mix(in srgb, {colors.${color}.2} 40%, transparent)`,
|
||||
color: `color-mix(in srgb, {colors.${color}.11} 40%, transparent)`,
|
||||
_placeholder: {
|
||||
color: `color-mix(in srgb, {colors.${color}.11} 40%, transparent)`
|
||||
},
|
||||
_hover: {
|
||||
backgroundColor: `color-mix(in srgb, {colors.${color}.2} 40%, transparent)`,
|
||||
color: `color-mix(in srgb, {colors.${color}.11} 40%, transparent)`
|
||||
},
|
||||
_active: {
|
||||
backgroundColor: `color-mix(in srgb, {colors.${color}.2} 40%, transparent)`,
|
||||
color: `color-mix(in srgb, {colors.${color}.11} 40%, transparent)`
|
||||
}
|
||||
}
|
||||
} as SystemStyleObject
|
||||
]
|
||||
case 'content':
|
||||
// Items, Group, ScrollButtons are inside the content, so we can set them together
|
||||
return [
|
||||
slot,
|
||||
{
|
||||
'--select-background-color': `{colors.${color}.2}`,
|
||||
'--select-background-color-hover': `{colors.${color}.4}`,
|
||||
'--select-background-color-active': `{colors.${color}.5}`,
|
||||
'--select-background-color-disabled': `color-mix(in srgb, {colors.${color}.2} 40%, transparent)`,
|
||||
'--select-border-color': `{colors.${color}.6}`,
|
||||
'--select-color-subtle': `{colors.${color}.11}`,
|
||||
'--select-color': `{colors.${color}.12}`,
|
||||
'--select-color-disabled': `color-mix(in srgb, {colors.${color}.11} 40%, transparent)`
|
||||
} as SystemStyleObject
|
||||
]
|
||||
default:
|
||||
return [slot, {}]
|
||||
}
|
||||
})
|
||||
)
|
||||
])
|
||||
)
|
||||
},
|
||||
fullWidth: {
|
||||
true: {
|
||||
trigger: {
|
||||
width: '100%'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
color: 'neutral',
|
||||
size: 'medium'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default selectRecipe
|
||||
Reference in New Issue
Block a user