Skip to content
Merged
Show file tree
Hide file tree
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
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Or using yarn:
First you need to define your default slots

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

const DEFAULT_SLOTS = {
Container: 'ul',
Expand Down Expand Up @@ -100,7 +100,7 @@ interface TItem {
name: string;
}

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

Expand Down Expand Up @@ -135,7 +135,7 @@ export const List = <

## Roadmap

- Add CD
- Write a simpler generic
- Better documentation
- Add example
- [ ] Add CD
- [ ] Write a simpler generic
- [ ] Better documentation
- [ ] Add example
4 changes: 2 additions & 2 deletions index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Equal, Expect } from '@type-challenges/utils'
import type { Slots, SlotsCreator, SlotsProps } from "./index"
import type { Slots, SlotsConfigCreator, SlotsProps } from "./index"
import type { ComponentProps, PropsWithChildren, JSX } from 'react'

const SLOTS = {
Expand All @@ -8,7 +8,7 @@ const SLOTS = {
Custom1: (props: PropsWithChildren) => <></>
} satisfies Slots

type SlotsConfig = SlotsCreator<typeof SLOTS, { Name1: [number], Custom: [{ id: number, name: string }] }>
type SlotsConfig = SlotsConfigCreator<typeof SLOTS, { Name1: [number], Custom: [{ id: number, name: string }] }>

type Props = SlotsProps<SlotsConfig, { Custom: (props: PropsWithChildren) => JSX.Element }>

Expand Down
61 changes: 23 additions & 38 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,37 @@
import { ComponentProps, JSXElementConstructor, JSX } from 'react';
import { CallbacksCreator, MaybeFunc, Merge, PartialProps, ReactComponent, Slots } from './utils';

type MaybeFunc<Value, Args extends Array<unknown>> =
| Value
| ((...args: Args) => Value);

type ReactComponent = keyof JSX.IntrinsicElements | JSXElementConstructor<any>;
type Slots = Record<string, ReactComponent>;

type CallbacksCreator<DefaultProps extends Slots> = {
[Key in keyof DefaultProps]?: Array<any>;
};

type SlotProps<
TSlots extends Slots,
SlotPropsWithCallback extends CallbacksCreator<TSlots>
> = {
[SlotKey in keyof TSlots as SlotKey extends string
? Lowercase<SlotKey>
: never]: TSlots[SlotKey] extends ReactComponent
? SlotPropsWithCallback[SlotKey] extends Array<any>
? MaybeFunc<
Partial<ComponentProps<TSlots[SlotKey]>>,
SlotPropsWithCallback[SlotKey]
>
: Partial<ComponentProps<TSlots[SlotKey]>>
: never;
};

type SlotsCreator<
type SlotsConfigCreator<
TDefaultSlots extends Slots,
TCallbacks extends CallbacksCreator<TDefaultSlots>
> = {
DefaultSlots: TDefaultSlots;
Slots: { [Key in keyof TDefaultSlots]?: ReactComponent };
Slots: Partial<Record<keyof TDefaultSlots, ReactComponent>>;
Callbacks: TCallbacks;
};

type SlotPropsCreator<
TSlotsConfig extends SlotsConfigCreator<any, any>,
TSlots extends Slots,
> = {
[SlotKey in keyof TSlots as Lowercase<string & SlotKey>]?:
// Get the props and rename it to Props
PartialProps<TSlots[SlotKey]> extends infer Props ?
// get the slot args for slotProps callback if avaliable, name it Args
TSlotsConfig["Callbacks"][SlotKey] extends infer Args extends Array<any>
? MaybeFunc<Props, Args> : Props
: never
};

type SlotsProps<
TSlotsConfig extends SlotsCreator<any, any>,
TSlots extends TSlotsConfig['Slots']
TSlotsConfig extends SlotsConfigCreator<any, any>,
TSlots extends TSlotsConfig['Slots'],
> = {
slots?: TSlots;
slotProps?: Partial<
SlotProps<
Omit<TSlotsConfig['DefaultSlots'], keyof TSlots> & TSlots,
TSlotsConfig['Callbacks']
slotProps?:
SlotPropsCreator<
TSlotsConfig,
Merge<TSlotsConfig['DefaultSlots'], TSlots>
>
>;
};

export type { SlotsProps, SlotsCreator, Slots };
export type { SlotsProps, SlotsConfigCreator, Slots, CallbacksCreator };
18 changes: 18 additions & 0 deletions utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { JSXElementConstructor, JSX, ComponentProps } from 'react';

type MaybeFunc<Value, Args extends Array<unknown>> =
| Value
| ((...args: Args) => Value);

type ReactComponent = keyof JSX.IntrinsicElements | JSXElementConstructor<any>;
type Slots = Record<string, ReactComponent>;

type PartialProps<T extends ReactComponent> = Partial<ComponentProps<T>>

type CallbacksCreator<DefaultProps extends Slots> = {
[Key in keyof DefaultProps]?: Array<any>;
};

type Merge<T, U> = Omit<T, keyof U> & U

export type { Merge, MaybeFunc, ReactComponent, Slots, CallbacksCreator, PartialProps }