Skip to content

Latest commit

 

History

History
322 lines (280 loc) · 7.63 KB

README.md

File metadata and controls

322 lines (280 loc) · 7.63 KB

@pandabox/define-recipe

Extend, pick, omit and merge config Recipes/Slots Recipes to easily compose them together.

Config Recipe

The defineRecipe method will now return a RecipeBuilder object instead of a RecipeConfig object. The RecipeBuilder object has the following methods:

  • extend: add additional variants to or override variants of a recipe.
const button = defineRecipe({
  className: 'btn',
  variants: {
    variant: { primary: { color: 'red' } },
  },
}).extend({
  variant: {
    primary: { px: 2 },
    secondary: { color: 'blue' },
  },
})

resulting in:

{
  "className": "btn",
  "variants": {
    "variant": {
      "primary": { "color": "red", "px": 2 },
      "secondary": { "color": "blue" }
    }
  }
}
  • merge: deep merge a recipe with another recipe. It takes a partial RecipeConfig object as an argument, which can include new (or existing) variants, compound variants, and default variants.
const button = defineRecipe({
  className: 'btn',
  variants: {
    variant: { primary: { color: 'red' } },
  },
}).merge({
  className: 'custom-btn',
  variants: {
    secondary: { color: 'blue' },
  },
  defaultVariants: {
    variant: 'secondary',
  },
})

resulting in:

{
  "className": "custom-btn",
  "variants": {
    "variant": {
      "primary": { "color": "red" },
      "secondary": { "color": "blue" }
    }
  },
  "defaultVariants": {
    "variant": "secondary"
  }
}
  • pick: pick only specified variants from a recipe. It takes a list of variant keys as arguments and returns a new RecipeBuilder object with only the specified variants. This will also filter out any compound variants that include any of the omitted variants.
const button = defineRecipe({
  className: 'btn',
  variants: {
    variant: { primary: { color: 'red' } },
    size: { small: { px: 2 }, large: { px: 4 } },
  },
}).pick('size')

resulting in:

{
  "className": "btn",
  "variants": {
    "variant": {
      "size": {
        "small": { "px": 2 },
        "large": { "px": 4 }
      }
    }
  }
}
  • omit: omit specified variants from a recipe. It takes a list of variant keys as arguments and returns a new RecipeBuilder object without the specified variants. This will also filter out any compound variants that include any of the omitted variants.
const button = defineRecipe({
  className: 'btn',
  variants: {
    variant: { primary: { color: 'red' } },
    size: { small: { px: 2 }, large: { px: 4 } },
  },
}).omit('size')

resulting in:

{
  "className": "btn",
  "variants": {
    "variant": {
      "primary": { "color": "red" }
    }
  }
}
  • cast: make the recipe generic to simplify the typings. It returns a new RecipeConfig object with the final computed variants, without the RecipeBuilder methods.

Each of these methods return a new RecipeBuilder object, so they can be chained together.

Config Slot Recipe

The defineSlotRecipe method will now return a SlotRecipeBuilder object instead of a SlotRecipeConfig object. The SlotRecipeBuilder object has the same following methods as the RecipeBuilder object: extend, merge, pick, and omit.

In addition, the SlotRecipeBuilder object has an object property called slots that is a SlotRecipeBuilder, which has the following methods:

  • add: add additional slots to a slot recipe. It takes a list of slot names as arguments and returns a new SlotRecipeBuilder object with the updated slots.
const card = defineSlotRecipe({
  className: 'card',
  slots: ['root', 'input', 'icon'],
  variants: {
    variant: {
      subtle: { root: { color: 'blue.100' } },
      solid: { root: { color: 'blue.100' } },
    },
    size: {
      sm: { root: { fontSize: 'sm' } },
      md: { root: { fontSize: 'md' } },
    },
  },
}).slot.add('label')

resulting in:

{
  "className": "card",
  "slots": ["root", "input", "icon", "label"],
  "variants": {
    "variant": {
      "subtle": { "root": { "color": "blue.100" } },
      "solid": { "root": { "color": "blue.100" } }
    },
    "size": {
      "sm": { "root": { "fontSize": "sm" } },
      "md": { "root": { "fontSize": "md" } }
    }
  }
}
  • pick: pick only specified slots from a slot recipe. It takes a list of slot keys as arguments and returns a new SlotRecipeBuilder object with only the specified slots. This will also filter out any styles defined in a slot that is not picked, as well as any compound variants that include any of the omitted slots.
const card = defineSlotRecipe({
  className: 'card',
  slots: ['root', 'input', 'icon'],
  variants: {
    variant: {
      subtle: { root: { color: 'blue.100' } },
      solid: { input: { color: 'blue.100' } },
    },
    size: {
      sm: { root: { fontSize: 'sm' } },
      md: { input: { fontSize: 'md' } },
    },
  },
}).slot.pick('input')

resulting in:

{
  "className": "card",
  "slots": ["input"],
  "variants": {
    "variant": {
      "solid": { "input": { "color": "blue.100" } }
    },
    "size": {
      "md": { "input": { "fontSize": "md" } }
    }
  }
}
  • omit: omit specified slots from a slot recipe. It takes a list of slot keys as arguments and returns a new SlotRecipeBuilder object without the specified slots. This will also filter out any styles defined in a slot that is not picked, as well as any compound variants that include any of the omitted slots.
const card = defineSlotRecipe({
  className: 'card',
  slots: ['root', 'input', 'icon'],
  variants: {
    variant: {
      subtle: { root: { color: 'blue.100' } },
      solid: { input: { color: 'blue.100' } },
    },
    size: {
      sm: { root: { fontSize: 'sm' } },
      md: { input: { fontSize: 'md' } },
    },
  },
}).slot.omit('input')

resulting in:

{
  "className": "card",
  "slots": ["root", "icon"],
  "variants": {
    "variant": {
      "subtle": { "root": { "color": "blue.100" } }
    },
    "size": {
      "sm": { "root": { "fontSize": "sm" } }
    }
  }
}
  • assignTo: assign a simple (without slots) recipe to a slot of the current slot recipe. It takes a slot name and a recipe config as arguments and returns a new SlotRecipeBuilder object with the updated slot recipe. If a slot name already has styles defined in a matching (both defined in the simple recipe to assign from and the current slot recipe being assigned to) variant, the styles will be merged with the existing slot recipe, with priority given to the styles defined in the simple recipe to assign from.
const button = defineRecipe({
  className: 'btn',
  variants: {
    variant: {
      outline: { color: 'green.100' },
      empty: { border: 'none' },
    },
    size: {
      lg: { fontSize: 'xl', h: '10' },
    },
  },
})

const card = defineSlotRecipe({
  className: 'card',
  slots: ['root', 'input', 'icon'],
  variants: {
    variant: {
      subtle: { root: { color: 'blue.100' } },
      solid: { input: { color: 'blue.100' } },
      outline: { input: { mx: 2 } },
      empty: { input: {} },
    },
    size: {
      sm: { root: { fontSize: 'sm' } },
      md: { input: { fontSize: 'md' } },
    },
  },
}).slot.assignTo('input', button)

resulting in:

{
  "className": "card",
  "slots": ["root", "input", "icon"],
  "variants": {
    "variant": {
      "subtle": { "root": { "color": "blue.100" } },
      "solid": { "input": { "color": "blue.100" } },
      "outline": { "input": { "mx": 2, "color": "green.100" } },
      "empty": { "input": {} }
    },
    "size": {
      "sm": { "root": { "fontSize": "sm" } },
      "md": { "input": { "fontSize": "md" } }
    }
  }
}

Originally implemented here