feat: tv component definition w/ config overrides #242
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
I use
tailwind-variants
to help write a top-level component library that will be used to implement a number of apps across my company. One requirement is that a given component may have different styling depending on what app it is a part of, but the core functionality should not change. This means that I need to be able to override class definitions w/ a prop.Currently, my only option to extend/override the style is to use a "class" prop to supply alternate styling via the
class
prop intv
. To help illustrate what I mean, imagine we have a simple button component:This is great because my button's
tv
config needs to support a base & icon/label slots, but as this method relies ontailwind-merge
we will always face the possibility of inheriting the classes in our originaltv
component config. What I really need is the ability to define an override that has the same structure as the original config but replaces the underlying class definitions.This PR introduces that capability through the new
defineTV
function. I extracted the options config type for thetv
&createTV
functions into it's own type & added thedefineTV
function which accepts the same initial arguments that would be passed if we were defining atv
object & returns another function that accepts an optional override argument along w/ the props argument to set variant types & so on.What this does is let us define our
tv
template in our component, accept an optional override prop from the downstream application, & instantiate atv
object at runtime that runs a merged config:Now when I use this button in an app, I can include a
tv
configuration that is tightly coupled to the structure of the underlying component that allows for total customization of the applied classes!Solution
The first step was extracting the options type that is accepted by the
tv
function into a separate type. I then defined aTVOverride
type that accepts all of the same generic arguments as thetv
function but the value of the type is a structure that directly reflects the root configuration.To facilitate simpler merges, I introduced the
defu
dependency which performs recursive assignment but w/ a flexible API. I useddefu
to refactor themergeObjects
function as well as define a merger for thetv
options config.The new
defineTV
function accepts the same argstv
does, but instead of returning the TV return type we return a function that can override the config before passing the TV return type. The result is a fully customizable component structure for layered environments!Notes
I have had this working to great effect in my project for a while, so I wanted to adapt it to be a part of this package so that it can be used by the wider community.
If you have any questions or need me to make changes, please let me know!
What is the purpose of this pull request?
Before submitting the PR, please make sure you do the following
fixes #123
).