- Replace .eslintrc.cjs with eslint.config.mjs (ESLint 9 flat config)
using direct eslint-plugin-solid + @typescript-eslint/parser approach
- Add @typescript-eslint/parser to root devDependencies
- Add main/module/types top-level fields to packages/core/package.json
- Add resolve.conditions to packages/core/vite.config.ts
- Create packages/core/tsconfig.test.json for test type-checking
- Remove empty paths:{} from packages/core/tsconfig.json
112 lines
5.4 KiB
Markdown
112 lines
5.4 KiB
Markdown
# Dynamic Components
|
||
|
||
All primitive components that render a DOM element are dynamic, which means that you can modify the element or the component they should render as.
|
||
|
||
## Native elements [Section titled Native elements](https://corvu.dev/docs/dynamic-components/\#native-elements)
|
||
|
||
In most cases, you shouldn’t need to change the DOM element that the primitive component renders. corvu has sensible defaults for all components. But there are cases where it makes sense to change them. An example would be the `Tooltip` trigger which renders as a `button` element. You may want to render a tooltip on a link (`a` tag) instead. To do this, you have to specify the `as` property on the trigger component:
|
||
|
||
```
|
||
<Tooltip.Trigger as="a" href="https://corvu.dev">
|
||
corvu.dev
|
||
</Tooltip.Trigger>
|
||
```
|
||
|
||
## Solid components [Section titled Solid components](https://corvu.dev/docs/dynamic-components/\#solid-components)
|
||
|
||
A much more common use case is to render a primitive component as a custom Solid component. This is useful to apply default styling or to add additional functionality.
|
||
|
||
For example, you might have your own, custom-styled button component and want to use it as a trigger for a dialog:
|
||
|
||
```
|
||
import {
|
||
ComponentProps,
|
||
splitProps,
|
||
} from 'solid-js'
|
||
import Dialog from '@corvu/dialog'
|
||
|
||
const CustomButton = (
|
||
props: ComponentProps<'button'> & { variant: 'fill' | 'outline' },
|
||
) => {
|
||
const [local, rest] = splitProps(props, ['variant'])
|
||
// Apply your custom styling here
|
||
return <button class={local.variant} {...rest} />
|
||
}
|
||
|
||
const DialogTrigger = () => (
|
||
<Dialog.Trigger as={CustomButton} variant="outline">
|
||
Open
|
||
</Dialog.Trigger>
|
||
)
|
||
```
|
||
|
||
Props not belonging to the primitive component will be passed through to your custom component. In this case, the `variant` prop is passed to the `CustomButton` component.
|
||
|
||
> ❗ To ensure functionality and accessibility, your component needs to spread the received props onto your element. Otherwise, corvu can’t define props on the element and things will break.
|
||
|
||
## Component types [Section titled Component types](https://corvu.dev/docs/dynamic-components/\#component-types)
|
||
|
||
corvu’s dynamic components have a flexible type system. This allows library developers or users who want to create their own components based on corvu’s primitives to have a great developer experience.
|
||
|
||
Every dynamic component exposes 4 types.
|
||
|
||
For example, the `<Dialog.Trigger>` component exports the types `DialogTriggerCorvuProps`, `DialogTriggerSharedElementProps<T>`, `DialogTriggerElementProps` and `DialogTriggerProps<T>`.
|
||
|
||
Lets have a look at the types:
|
||
|
||
### CorvuProps [Section titled CorvuProps](https://corvu.dev/docs/dynamic-components/\#corvuprops)
|
||
|
||
`CorvuProps` contains all props that are specific to the primitive component and are not passed through to the rendered element. They are consumed by corvu. For example, the [`<Resizable.Handle />`](https://corvu.dev/docs/primitives/resizable/#Panel) has props like `minSize` or `collapsible`.
|
||
|
||
### SharedElementProps<T extends ValidComponent> [Section titled SharedElementProps<T extends ValidComponent>](https://corvu.dev/docs/dynamic-components/\#sharedelementpropst-extends-validcomponent)
|
||
|
||
`SharedElementProps` includes all props that get defined by corvu on the rendered element **but** can be overridden by the user. This usually includes the `ref` or `style` properties. The generic is used to properly type `ref` and event listeners.
|
||
|
||
### ElementProps [Section titled ElementProps](https://corvu.dev/docs/dynamic-components/\#elementprops)
|
||
|
||
`ElementProps` element props inherits all `SharedElementProps` and additionally includes all props that are set by corvu and can’t be overridden by the user. This includes for example accessibility props like `aria-*`, `role` and `data-*`.
|
||
|
||
### Props<T extends ValidComponent> [Section titled Props<T extends ValidComponent>](https://corvu.dev/docs/dynamic-components/\#propst-extends-validcomponent)
|
||
|
||
This is the type that defines what props that corvu expects from the user. It’s equal to `CorvuProps & Partial<SharedElementProps>`.
|
||
|
||
### DynamicProps [Section titled DynamicProps](https://corvu.dev/docs/dynamic-components/\#dynamicprops)
|
||
|
||
`DynamicProps` is a helper type that allows you to expose the dynamic aspect of corvu components from your custom component. Let’s look at an example:
|
||
|
||
```
|
||
import {
|
||
type ValidComponent,
|
||
ComponentProps,
|
||
splitProps,
|
||
} from 'solid-js'
|
||
import Dialog, { type TriggerProps, type DynamicProps } from '@corvu/dialog'
|
||
|
||
// Define your custom props, including `TriggerProps` from corvu
|
||
export type CustomDisclosureTriggerProps<T extends ValidComponent = 'button'> = TriggerProps<T> & {
|
||
variant: 'fill' | 'outline'
|
||
}
|
||
|
||
// The generic `T` allows the user to specify
|
||
// the element this component should render as
|
||
const CustomDialogTrigger = <T extends ValidComponent = 'button'>(
|
||
props: DynamicProps<T, CustomDisclosureTriggerProps<T>>,
|
||
) => {
|
||
const [local, rest] = splitProps(props as CustomDisclosureTriggerProps, [\
|
||
'variant',\
|
||
])
|
||
// Define the default dynamic type, in this case 'button'.
|
||
// This can be overridden by the user.
|
||
return <Dialog.Trigger as="button" class={local.variant} {...rest} />
|
||
}
|
||
```
|
||
|
||
If you don’t want to expose the dynamic aspect of corvu’s components, you can define the generic explicitly:
|
||
|
||
```
|
||
const CustomDialogTrigger = (
|
||
props: DynamicProps<'button', CustomDisclosureTriggerProps<'button'>>,
|
||
) => {
|
||
```
|
||
|
||
Developed and designed by [Jasmin](https://github.com/GiyoMoon/) |