Skip to content

Commit

Permalink
chore: add Slot and SlotProps (#30)
Browse files Browse the repository at this point in the history
* chore: add `Slot` and `SlotProps`

* chore: add `AsAnchor` story in ui-button

* chore: improve `SlotWithAsChild` type
  • Loading branch information
halvaradop authored Oct 3, 2024
1 parent adc935a commit 84d849b
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 6 deletions.
8 changes: 8 additions & 0 deletions packages/ui-button/src/button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,12 @@ export const Large: Story = {
},
}

export const AsAnchor: Story = {
render: () => (
<Button variant="link" asChild>
<a href="">Link</a>
</Button>
),
}

export default meta
13 changes: 7 additions & 6 deletions packages/ui-button/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { forwardRef, ComponentProps } from "react"
import { forwardRef } from "react"
import type { ArgsFunction } from "@halvaradop/ts-utility-types"
import { cva, type VariantProps } from "class-variance-authority"
import { merge } from "@halvaradop/ui-core"
import { merge, Slot, SlotProps } from "@halvaradop/ui-core"

export type ButtonProps<T extends ArgsFunction> = Omit<ComponentProps<"button">, "size"> & VariantProps<T>
export type ButtonProps<T extends ArgsFunction> = SlotProps<"button"> & VariantProps<T>

export const buttonVariants = cva("flex items-center justify-center font-semibold border transition hover:border-transparent hover:bg-opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2", {
variants: {
Expand Down Expand Up @@ -41,11 +41,12 @@ export const buttonVariants = cva("flex items-center justify-center font-semibol
* The Button component is a versatile and customizable button element.
* It supports various variants, sizes, and additional props to enhance its appearance and functionality.
*/
export const Button = forwardRef<HTMLButtonElement, ButtonProps<typeof buttonVariants>>(({ className, variant, size, fullWidth, fullRounded, children, ...props }, ref) => {
export const Button = forwardRef<HTMLButtonElement, ButtonProps<typeof buttonVariants>>(({ className, variant, size, fullWidth, fullRounded, asChild, children, ...props }, ref) => {
const SlotComponent = asChild ? Slot : "button"
return (
<button className={merge(buttonVariants({ className, variant, size, fullWidth, fullRounded }))} ref={ref} role="button" {...props}>
<SlotComponent className={merge(buttonVariants({ className, variant, size, fullWidth, fullRounded }))} ref={ref} role="button" {...props}>
{children}
</button>
</SlotComponent>
)
})

Expand Down
5 changes: 5 additions & 0 deletions packages/ui-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
"types": "./dist/tsup.config.base.d.ts",
"import": "./dist/tsup.config.base.js",
"require": "./dist/tsup.config.base.cjs"
},
"./slot": {
"types": "./dist/slot.d.ts",
"import": "./dist/slot.js",
"require": "./dist/slot.cjs"
}
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/ui-core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./utils.js"
export * from "./tsup.config.base.js"
export * from "./slot.js"
18 changes: 18 additions & 0 deletions packages/ui-core/src/slot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { type ReactNode, Children, isValidElement, cloneElement, ComponentProps } from "react"

export const Slot = ({ children, ...props }: { children: React.ReactNode }) => {
if (isValidElement(children)) {
return cloneElement(children, { ...props, ...children.props })
}
if (Children.count(children) > 1) {
Children.only(null)
}
return null
}

export type SlotWithAsChild<
Component extends keyof React.JSX.IntrinsicElements | React.JSXElementConstructor<unknown>,
> = ({ asChild?: false } & ComponentProps<Component>) | { asChild: true; children: ReactNode }

export type SlotProps<Component extends keyof React.JSX.IntrinsicElements | React.JSXElementConstructor<unknown>> =
SlotWithAsChild<Component> & { className?: string }

0 comments on commit 84d849b

Please sign in to comment.