Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 89 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,109 @@
# Slots Component

This package provides flexible types for writing highly customziable components that accepts slots. it adds `slots` and `slotProps` to your component props with full TypeScript support.

Using this package, you can write components that can be used like the following:

```jsx
<List
rows={data}
slots={{
Container: (props: { children: ReactNode }) => <div>{props.children}</div>,
}}
slotProps={{
row: ({ name }) => ({ onClick: () => alert(name) }),
}}
/>
```

You can pass another slot to replace the default or extra props.

See the example below to see how to write such a component.

## Installation

`npm i slots-component`
You can install slots-components via npm:

`npm i -D slots-components`

Or using yarn:

`yarn add slots-components`

## How to use it?

```tsx
First you need to define your default slots

```jsx
import { SlotsProps, Slots, SlotsCreator } from 'slots-component';

const DEFAULT_SLOTS = {
List: 'ul',
Container: 'ul',
Row: 'li',
} satisfies Slots;
```

Then you need to define the config, if you want to pass extra values to your props, let's say we want to pass `item` to `Row` component:

```jsx
interface TItem {
id: string;
name: string;
}

type SlotsConfig = SlotsCreator<typeof DEFAULT_SLOTS, {
Row: [TItem];
Row: [TItem]
}>
```

Now define your component:

```jsx
interface Props {
rows: TItem[];
}

export const List = <
TSlots extends SlotsConfig["Slots"],
>({ rows, slots, slotProps }: Props & SlotsProps<SlotsConfig, TSlots>) => {
const combinedSlots = { ...DEFAULT_SLOTS, ...slots };

return (
<combinedSlots.Container {...slotProps?.container}>
{rows.map((entity) => (
<combinedSlots.Row
key={row.id}
{...(typeof slotProps?.row === 'function'
? slotProps?.row(entity)
: slotProps?.row)}
>
{row.name}
</combinedSlots.Row>
))}
</combinedSlots.Container>
);
};
```

### Full example

```jsx
import { SlotsProps, Slots, SlotsCreator } from 'slots-component';

const DEFAULT_SLOTS = {
Container: 'ul',
Row: 'li',
} satisfies Slots;

interface TItem {
id: string;
name: string;
}

type SlotsConfig = SlotsCreator<typeof DEFAULT_SLOTS, {
Row: [TItem];
}>

interface Props {
rows: TItem[];
}
Expand All @@ -36,18 +117,18 @@ export const List = <
};

return (
<combinedSlots.List {...slotProps?.list}>
{rows.map((row) => (
<combinedSlots.Container {...slotProps?.container}>
{rows.map((entity) => (
<combinedSlots.Row
key={row.id}
{...(typeof slotProps?.row === 'function'
? slotProps?.row(row)
? slotProps?.row(entity)
: slotProps?.row)}
>
{row.name}
</combinedSlots.Row>
))}
</combinedSlots.List>
</combinedSlots.Container>
);
};
```