Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@namelessdev/slots

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@namelessdev/slots

Эта библиотека для слотов в ReactJS

npmnpm
Version
0.0.5
Version published
Weekly downloads
19
26.67%
Maintainers
1
Weekly downloads
 
Created
Source

Slots

Эта библиотека для слотов в ReactJS

Определение слотов в компоненте

Для определение слотов в компоненте используется функция

import * as S from "@namelessdev/slots";
const { SlotOne, SlotTwo, Default } = S.retrive(children, "slot_one", {
	name: "slot_two", // Название слота
	filter: ..., // Фильтр для того какие элементы могут попасть в слот
	overreding: ..., // Перезапись props элементов
})

Она принимает первым аргументом children компонента и неограниченное количество аргументов, которые являются настройкой слотов. Возвращает объект с компонентами слотов с теми же названиями, что и в настройках слотов, только в PascalCase slot_name → SlotName и так же слот Default, это элементы, которые не попали ни в один слот. Название слотов должно быть строго в snake_case. Если вы если вы напишете настройки для слота default, то они будут примениться для элементов, которые не попали ни в один слот.

Настройки слотов могут быть двумя типами.

  • string - название слота

  • SlotInfo - настройки слота

    type SlotInfo<N extends string> = {
      name: N;
      filter?: FilterSlot;
      overreding?: OverredingSlot;
    };
    type FilterSlot = (children: React.ReactNode) => boolean;
    type OverredingSlot = (
      props: Record<string, unknown>,
    ) => Record<string, unknown>;
    

    Фильтр слота принимает элемент ReactNode и возвращает boolean, если значение равно false, то этот элемент не попадёт в слот в компоненте. В этой функции вы можете всё что угодно, даже можно фильтровать по пропсам при помощи zod. Есть некоторые функции которые упростят фильтрацию, к примеру S.filters.type("h1") пропустить только теги h1

    Overreding используется для перезаписи props. Эта функция принимает props элемента и возвращает изменённые props. В библиотеке так же присутствуют функции облегчающие это, к примеру S.overreding.assign(), которая принимает либо объект, либо функцию, которая принимает и возвращает объект, помогает правильно объединить старые и новые props. Так же есть функция S.overreding.fun(), которая принимает функцию из старых props, новую функцию и isSecond?: boolean и помогает правильно объединить функции, чтобы одна функция выполнялась после другой

Создание настроек слотов

Есть функция create, которая помогает создать настройки слотов, она принимает название слота и объект с настройками

interface ConfigSlot {
  filter?: FilterSlot,
  overreding?: OverredingSlot,
}

export const create = <const N extends string>(name: N, config?: ConfigSlot): SlotInfo<N> => ({
  name,
  filter: config && config.filter,
  overreding: config && config.overreding
})

const SlotImage = create("image", {
	filter: ...
	overreding: ...
})
const SlotTitle = create("title")

Использование слотов

После того как вы определили слоты, вы можете использовать их в своей разметке

return (
	<div>
		<SlotOne />
		<section>
			<SlotTwo>
			 Этот текст будет отображатся если этот слот был не передан
			</SlotTwo>
		</section>
		<Default />
	<div>
)

Передача слотов

Передавать слоты в компонент можно тремя способами

1 способ

Вы можете использовать атрибут slot чтобы написать название слота в который нужно положить этот элемент

<ComponentWithSlot>
	<h1 slot="slot_one">Hello</h1>
	<p slot="slot_two">World</p>
	Element in Default slot
<ComponentWithSlot>

2 способ

Вы можете обернуть элементы которые хотите делать частью слота тегом <slot>и дать ему нужное имя. Это полезно, когда частью слота является обычный текст.

<ComponentWithSlot>
	<slot name="slot_one">
		<h1>Hello</h1>
		<p>World</p>
	</slot>
	<slot name="slot_two">This is Primitive</slot>
	Element in Default slot
<ComponentWithSlot>

3 способ

Вы можете создать компоненты которые будут являться слотами. Для этого используются две функции S.slot и S.wrapper

S.slot создаёт простой компонент который говорит, что дочерние элементы являются элементами слота. Она принимает название слота

import type { Slot } from "@namelessdev/slots";

type ComponentWithSlot = FC<{
  children: ReactNode;
}> & {
  SlotOne: Slot<"slot_one", /* можно указать тип который должен принимать этот слот */>;
};

ComponentWithSlot.SlotOne = S.slot("slot_one")

<ComponentWithSlot>
	<ComponentWithSlot.SlotOne>
		Hello
	<ComponentWithSlot.SlotOne>
<ComponentWithSlot>

S.wrapper создаёт компонент обёртку. Если в слоте вам нужна только картинка, то вы можете создать компонент обёртку которая не только скажет, что это слот, но и будет принимать все props компонента Image или любово другова, который вы ему передадите

import type { Wrapper } from "@namelessdev/slots";
type ComponentWithSlot = FC<{
  children: ReactNode;
}> & {
  Image: Wrapper<"image", typeof LazyLoadImage,  /* можно указать тип который должен принимать этот слот */>;
};

ComponentWithSlot.Image = S.wrapper("image", LazyLoadImage);

<ComponentWithSlot.Image src="link/to/image.png" alt="" />

Вспомогательные функции

Фильтрация

const element =
  (filter: (node: ReactElement) => boolean): FilterSlot =>
  (child) =>
    isValidElement(child) && filter(child);

const type =
  (type: string | FC): FilterSlot =>
  (child) =>
    // @ts-ignore
    isValidElement(child) && (child.type === type | child.type.element === type);

const primitive: FilterSlot = (child) => !isValidElement(child);

const props =
  (props: string): FilterSlot =>
  (child) =>
    isValidElement(child) && child.props.hasOwnProperty(props);

const hasChildren: FilterSlot = (child) =>
  isValidElement(child) && Boolean(React.Children.count(child.props.children));

const hasEvent = (eventName: string) => (child: ReactNode) =>
  isValidElement(child) && typeof child.props[eventName] === "function";

const and =
  (...filters: FilterSlot[]): FilterSlot =>
  (child) =>
    filters.reduce((result, filter) => {
      if (!result) return result;
      return result && filter(child);
    }, true);

const or =
  (...filters: FilterSlot[]): FilterSlot =>
  (child) =>
    filters.reduce((result, filter) => {
      return result || filter(child);
    }, true);

Перезапись props

const string = (props: unknown, newprops: string) =>
  typeof props === "string" ? `${props} ${newprops}` : newprops;

const fun = (props: unknown, newprops: Function, isSecond?: boolean) =>
  typeof props === "function"
    ? () => {
      if (isSecond) {
        props && props();
        newprops();
      } else {
        newprops();
        props && props();
      }
    }
    : newprops;

const assign =
  <T extends Record<string, unknown>>(args: (props: T) => T | T) =>
    (props: T) =>
      Object.assign({}, props, typeof args === "function" ? args(props) : args);

Пример использования

import * as S from "@namelessdev/slots";
import type { Slot, Wrapper } from "@namelessdev/slots";

export const SlotImage = S.create("image", {
  filter: F.type(LazyLoadImage),
  overreding: O.assign(props => ({
    className: O.string(props.className, "bject-cover w-full rounded-t-lg h-96 md:h-auto md:w-48 md:rounded-none md:rounded-s-lg")
  })),
})

export const SlotDefault = S.create("default", {
  overreding: O.assign(props => ({
    className: O.string(props.className, "mb-3 font-normal text-gray-700 dark:text-gray-400")
  })),
})

export const SlotTitle = S.create("title")

type ButtonComponent = FC<{
  children: ReactNode;
}> & {
  Title: Slot<"title">;
  Image: Wrapper<"image", typeof LazyLoadImage>;
};

export const Card: ButtonComponent = ({ children }) => {
  const { Default, Title, Image } = S.retrive(
    children,
    SlotTitle,
    SlotImage,
    SlotDefault,
  );

  return (
    <a
      href="#"
      className="flex flex-col items-center bg-white border border-gray-200 rounded-lg shadow md:flex-row md:max-w-xl hover:bg-gray-100 dark:border-gray-700 dark:bg-gray-800 dark:hover:bg-gray-700"
    >
      <Image />
      <div className="flex flex-col justify-between p-4 leading-normal">
        <h5 className="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
          <Title />
        </h5>
        <Default />
      </div>
    </a>
  );
};

Card.Title = S.slot("title");
Card.Image = S.wrapper("image", LazyLoadImage);

FAQs

Package last updated on 24 Mar 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts