From bab93392490fc79ca754b9c6c87c3eeec3c86a9f Mon Sep 17 00:00:00 2001 From: SrJuggernaut Date: Mon, 20 Oct 2025 18:07:32 -0600 Subject: [PATCH] feat: add menu recipe --- src/index.ts | 2 + src/recipes/menu.ts | 178 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 src/recipes/menu.ts diff --git a/src/index.ts b/src/index.ts index a2703f5..307170c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,7 @@ import detailsRecipe from '@/recipes/details' import iconButtonRecipe from '@/recipes/iconButton' import inputRecipe from '@/recipes/input' import markRecipe from '@/recipes/mark' +import menuRecipe from '@/recipes/menu' import progressBarRecipe from '@/recipes/progressBar' import selectRecipe from '@/recipes/select' import { type BrandColor, type Color, type ColorVariation, color, type NeutralColor } from '@/types' @@ -51,6 +52,7 @@ const srJuggernautPandaPreset = (config?: ThemeConfig) => { chip: chipRecipe({ semanticColorNames: semanticColorKeysArray }), details: detailsRecipe({ semanticColorNames: semanticColorKeysArray }), mark: markRecipe({ semanticColorNames: semanticColorKeysArray }), + menu: menuRecipe({ semanticColorNames: semanticColorKeysArray }), input: inputRecipe({ semanticColorNames: semanticColorKeysArray }), iconButton: iconButtonRecipe({ semanticColorNames: semanticColorKeysArray }), progressBar: progressBarRecipe({ semanticColorNames: semanticColorKeysArray }), diff --git a/src/recipes/menu.ts b/src/recipes/menu.ts new file mode 100644 index 0000000..28ac97d --- /dev/null +++ b/src/recipes/menu.ts @@ -0,0 +1,178 @@ +import { defineSlotRecipe, type SystemStyleObject } from '@pandacss/dev' + +export const menuSlots = ['container', 'item', 'label', 'group', 'separator'] as const + +export interface MenuRecipeArg { + semanticColorNames: string[] +} + +const menuRecipe = ({ semanticColorNames }: MenuRecipeArg) => { + const colors = ['neutral', ...semanticColorNames] + return defineSlotRecipe({ + className: 'menu', + slots: menuSlots, + base: { + container: { + '--menu-background-color-disabled': `color-mix(in srgb, {colors.neutral.3} 40%, transparent)`, + '--menu-background-color': `{colors.neutral.3}`, + '--menu-background-color-hover': `{colors.neutral.4}`, + '--menu-background-color-active': `{colors.neutral.5}`, + '--menu-border-color': `{colors.neutral.6}`, + '--menu-border-color-hover': `{colors.neutral.7}`, + '--menu-border-color-active': `{colors.neutral.8}`, + '--menu-text-color-disabled': `color-mix(in srgb, {colors.neutral.12} 40%, transparent)`, + '--menu-text-color': `{colors.neutral.12}`, + width: 'fit-content', + border: '1px solid', + borderColor: 'var(--menu-border-color)', + borderRadius: 'sm', + backgroundColor: 'var(--menu-background-color)', + color: 'var(--menu-text-color)', + overflow: 'hidden' + }, + item: { + all: 'unset', + display: 'flex', + alignItems: 'center', + gap: 'sm', + paddingBlock: 'xs', + paddingInline: 'sm', + cursor: 'pointer', + transitionProperty: 'color, background-color', + color: 'var(--menu-item-text-color, var(--menu-group-text-color ,var(--menu-text-color)))', + transitionDuration: 'normal', + transitionTimingFunction: 'easeOut', + _hover: { + backgroundColor: + 'var(--menu-item-background-color-hover, var(--menu-group-background-color-hover ,var(--menu-background-color-hover)))' + }, + _active: { + backgroundColor: + 'var(--menu-item-background-color-active, var(--menu-group-background-color-active ,var(--menu-background-color-active)))' + }, + _checked: { + backgroundColor: + 'var(--menu-item-background-color-active, var(--menu-group-background-color-active ,var(--menu-background-color-active)))' + }, + _selected: { + backgroundColor: + 'var(--menu-item-background-color-active, var(--menu-group-background-color-active ,var(--menu-background-color-active)))' + }, + _disabled: { + cursor: 'not-allowed', + color: + 'var(--menu-item-text-color-disabled, var(--menu-group-text-color-disabled, var(--menu-text-color-disabled)))', + backgroundColor: + 'var(--menu-item-background-color-disabled, var(--menu-group-background-color-disabled, var(--menu-background-color-disabled)))', + _hover: { + backgroundColor: + 'var(--menu-item-background-color-disabled, var(--menu-group-background-color-disabled, var(--menu-background-color-disabled)))' + }, + _active: { + backgroundColor: + 'var(--menu-item-background-color-disabled, var(--menu-group-background-color-disabled, var(--menu-background-color-disabled)))' + }, + _checked: { + backgroundColor: + 'var(--menu-item-background-color-disabled, var(--menu-group-background-color-disabled, var(--menu-background-color-disabled)))' + }, + _selected: { + backgroundColor: + 'var(--menu-item-background-color-disabled, var(--menu-group-background-color-disabled, var(--menu-background-color-disabled)))' + } + } + }, + label: { + fontSize: 'sm', + color: 'var(--menu-label-text-color, var(--menu-text-color))', + fontWeight: 'bold' + }, + group: { + borderBlock: '1px solid', + borderColor: 'var(--menu-group-border-color, var(--menu-border-color))', + backgroundColor: 'var(--menu-group-background-color, var(--menu-background-color))', + color: 'var(--menu-group-text-color, var(--menu-text-color))' + }, + separator: { + height: '1px', + backgroundColor: 'var(--menu-separator-border-color, var(--menu-group-border-color, var(--menu-border-color)))' + } + }, + variants: { + color: { + ...Object.fromEntries( + colors.map((color) => [ + color, + Object.fromEntries( + menuSlots.map((slot) => { + switch (slot) { + case 'container': + return [ + slot, + { + '--menu-background-color-disabled': `color-mix(in srgb, {colors.${color}.3} 40%, transparent)`, + '--menu-background-color': `{colors.${color}.3}`, + '--menu-background-color-hover': `{colors.${color}.4}`, + '--menu-background-color-active': `{colors.${color}.5}`, + '--menu-border-color': `{colors.${color}.6}`, + '--menu-border-color-hover': `{colors.${color}.7}`, + '--menu-border-color-active': `{colors.${color}.8}`, + '--menu-text-color-disabled': `color-mix(in srgb, {colors.${color}.12} 40%, transparent)`, + '--menu-text-color': `{colors.${color}.12}` + } as SystemStyleObject + ] + case 'item': + return [ + slot, + { + '--menu-item-background-color-disabled': `color-mix(in srgb, {colors.${color}.3} 40%, transparent)`, + '--menu-item-background-color': `{colors.${color}.3}`, + '--menu-item-background-color-hover': `{colors.${color}.4}`, + '--menu-item-background-color-active': `{colors.${color}.5}`, + '--menu-item-text-color-disabled': `color-mix(in srgb, {colors.${color}.12} 40%, transparent)`, + '--menu-item-text-color': `{colors.${color}.12}` + } as SystemStyleObject + ] + case 'label': + return [ + slot, + { + '--menu-label-text-color': `{colors.${color}.12}` + } as SystemStyleObject + ] + case 'group': + return [ + slot, + { + '--menu-group-background-color-disabled': `color-mix(in srgb, {colors.${color}.3} 40%, transparent)`, + '--menu-group-background-color': `{colors.${color}.3}`, + '--menu-group-background-color-hover': `{colors.${color}.4}`, + '--menu-group-background-color-active': `{colors.${color}.5}`, + '--menu-group-border-color': `{colors.${color}.6}`, + '--menu-group-border-color-hover': `{colors.${color}.7}`, + '--menu-group-border-color-active': `{colors.${color}.8}`, + '--menu-group-text-color-disabled': `color-mix(in srgb, {colors.${color}.12} 40%, transparent)`, + '--menu-group-text-color': `{colors.${color}.12}` + } as SystemStyleObject + ] + case 'separator': + return [ + slot, + { + '--menu-separator-border-color': `{colors.${color}.6}` + } as SystemStyleObject + ] + default: + return [slot, {} as SystemStyleObject] + } + }) + ) + ]) + ) + } + }, + defaultVariants: {} + }) +} + +export default menuRecipe