@de-kit/st-ui 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -10
- package/dist/components/button/button.cjs +26 -13
- package/dist/components/button/button.cjs.map +1 -1
- package/dist/components/button/button.d.cts +8 -7
- package/dist/components/button/button.d.ts +8 -7
- package/dist/components/button/button.js +29 -16
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/button/button.scss +22 -10
- package/dist/components/button/index.d.cts +2 -2
- package/dist/components/button/index.d.ts +2 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/lib/slot.cjs +100 -0
- package/dist/lib/slot.cjs.map +1 -0
- package/dist/lib/slot.js +100 -0
- package/dist/lib/slot.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -67,7 +67,9 @@ import { IconClose, LogoVisa } from '@de-kit/st-ui/icons';
|
|
|
67
67
|
<Button onClick={save}>Сохранить</Button>
|
|
68
68
|
<Button variant="outline">Отмена</Button>
|
|
69
69
|
<Button variant="link" disabled>Скрыть</Button>
|
|
70
|
-
<Button
|
|
70
|
+
<Button asChild>
|
|
71
|
+
<a href="/prices">Тарифы</a>
|
|
72
|
+
</Button>
|
|
71
73
|
<Button size="sm">Маленькая</Button>
|
|
72
74
|
<Button size="lg">Крупная</Button>
|
|
73
75
|
<Button fullWidth>Дальше</Button>
|
|
@@ -82,17 +84,17 @@ import { IconClose, LogoVisa } from '@de-kit/st-ui/icons';
|
|
|
82
84
|
<Button loading>Сохранение</Button>
|
|
83
85
|
```
|
|
84
86
|
|
|
85
|
-
Иконка — обычный `children`, слева или справа, какой набор нужен. `loading` оставляет подпись в потоке (`opacity: 0`) и кладёт спиннер поверх, поэтому ширина не прыгает. Ставит `aria-busy`. Пока `loading`, `<button>` получает `disabled
|
|
87
|
+
Иконка — обычный `children`, слева или справа, какой набор нужен. `loading` оставляет подпись в потоке (`opacity: 0`) и кладёт спиннер поверх, поэтому ширина не прыгает. Ставит `aria-busy`. Пока `loading`, `<button>` получает `disabled`. У `asChild` на не-кнопке клик отменяется и ставится `aria-disabled`.
|
|
86
88
|
|
|
87
|
-
| Prop | По умолчанию |
|
|
88
|
-
| ----------- | ------------ |
|
|
89
|
-
| `variant` | `default` | `default` заливка, `outline` обводка, `link` текст
|
|
90
|
-
| `size` | `md` | `sm` компактная, `md` обычная, `lg` крупная
|
|
91
|
-
| `fullWidth` | `false` | ширина 100%, не зависит от `size`
|
|
92
|
-
| `
|
|
93
|
-
| `loading` | `false` | `aria-busy`, клик блокируется, ширина как у подписи
|
|
89
|
+
| Prop | По умолчанию | |
|
|
90
|
+
| ----------- | ------------ | ------------------------------------------------------- |
|
|
91
|
+
| `variant` | `default` | `default` заливка, `outline` обводка, `link` текст |
|
|
92
|
+
| `size` | `md` | `sm` компактная, `md` обычная, `lg` крупная |
|
|
93
|
+
| `fullWidth` | `false` | ширина 100%, не зависит от `size` |
|
|
94
|
+
| `asChild` | `false` | классы и пропы на единственном ребёнке, без своего тега |
|
|
95
|
+
| `loading` | `false` | `aria-busy`, клик блокируется, ширина как у подписи |
|
|
94
96
|
|
|
95
|
-
`variant="link"` — это вид кнопки (`font-weight: 400`), не тип элемента. Ссылка —
|
|
97
|
+
`variant="link"` — это вид кнопки (`font-weight: 400`), не тип элемента. Ссылка — `asChild` вокруг своего `<a>` или `Link`: классы, `ref` и обработчики сливаются в единственного ребёнка, свой `<button>` не рисуется. Ребёнок должен принять `className` и `ref`.
|
|
96
98
|
|
|
97
99
|
## Остальные
|
|
98
100
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const require_icon_spinner = require("../../icons/icon-spinner.cjs");
|
|
2
2
|
const require_cn = require("../../lib/cn.cjs");
|
|
3
|
+
const require_slot = require("../../lib/slot.cjs");
|
|
3
4
|
let react = require("react");
|
|
4
5
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
5
6
|
require("./button.scss");
|
|
@@ -9,6 +10,7 @@ const styleKeys = [
|
|
|
9
10
|
"size",
|
|
10
11
|
"fullWidth",
|
|
11
12
|
"loading",
|
|
13
|
+
"asChild",
|
|
12
14
|
"className"
|
|
13
15
|
];
|
|
14
16
|
const getClassName = ({ variant = "default", size = "md", fullWidth = false, loading = false, className }) => require_cn.cn("button", className, require_cn.when(variant !== "default", `button--${variant}`), require_cn.when(size !== "md", `button--${size}`), require_cn.when(fullWidth, "button--full-width"), require_cn.when(loading, "button--loading"));
|
|
@@ -31,25 +33,36 @@ const renderContent = (children, loading) => {
|
|
|
31
33
|
const Button = (0, react.forwardRef)(function Button(props, ref) {
|
|
32
34
|
const { loading } = props;
|
|
33
35
|
const className = getClassName(props);
|
|
34
|
-
const { children,
|
|
36
|
+
const { children, ref: propsRef, ...domProps } = omitStyleProps(props);
|
|
35
37
|
const nodeRef = ref ?? propsRef;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
38
|
+
if (props.asChild) {
|
|
39
|
+
if (!(0, react.isValidElement)(children) || children.type === react.Fragment) throw new Error("Button с asChild ждёт ровно один React-элемент.");
|
|
40
|
+
const childProps = children.props;
|
|
41
|
+
const { disabled, onClick, type, ...childDomProps } = domProps;
|
|
42
|
+
const hostButton = children.type === "button";
|
|
43
|
+
const isBlocked = Boolean(disabled || loading);
|
|
39
44
|
const handleClick = (event) => {
|
|
40
|
-
|
|
45
|
+
if (isBlocked) {
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
childProps.onClick?.(event);
|
|
50
|
+
if (!event.defaultPrevented) onClick?.(event);
|
|
41
51
|
};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
52
|
+
const element = (0, react.cloneElement)(children, {}, renderContent(childProps.children, loading));
|
|
53
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_slot.Slot, {
|
|
54
|
+
...childDomProps,
|
|
45
55
|
className,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
ref: nodeRef,
|
|
57
|
+
onClick: handleClick,
|
|
58
|
+
...type !== void 0 || hostButton ? { type: type ?? "button" } : {},
|
|
59
|
+
...hostButton && isBlocked ? { disabled: true } : {},
|
|
60
|
+
...loading ? { "aria-busy": true } : {},
|
|
61
|
+
...!hostButton && isBlocked ? { "aria-disabled": true } : {},
|
|
62
|
+
children: element
|
|
51
63
|
});
|
|
52
64
|
}
|
|
65
|
+
const content = renderContent(children, loading);
|
|
53
66
|
const { type = "button", disabled, onClick, ...buttonProps } = domProps;
|
|
54
67
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
55
68
|
...buttonProps,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.cjs","names":["cn","when","IconSpinner","forwardRef"],"sources":["../../../src/components/button/button.tsx"],"sourcesContent":["import {\n forwardRef,\n
|
|
1
|
+
{"version":3,"file":"button.cjs","names":["cn","when","IconSpinner","forwardRef","isValidElement","Fragment","cloneElement","Slot"],"sources":["../../../src/components/button/button.tsx"],"sourcesContent":["import {\n cloneElement,\n Fragment,\n forwardRef,\n isValidElement,\n type ButtonHTMLAttributes,\n type ForwardedRef,\n type MouseEventHandler,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from 'react';\n\nimport { IconSpinner } from '@/icons/icon-spinner';\nimport { cn, when } from '@/lib/cn';\nimport { Slot } from '@/lib/slot';\nimport './button.scss';\n\nexport type ButtonVariant = 'default' | 'outline' | 'link' | 'image' | 'icon';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\n\ninterface ButtonStyleProps {\n variant?: ButtonVariant;\n size?: ButtonSize;\n fullWidth?: boolean;\n loading?: boolean;\n className?: string;\n children?: ReactNode;\n}\n\nexport interface ButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>, ButtonStyleProps {\n asChild?: false;\n ref?: Ref<HTMLButtonElement> | ((element: HTMLElement | null) => void);\n}\n\nexport interface ButtonAsChildProps\n extends\n Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'>,\n ButtonStyleProps {\n asChild: true;\n children: ReactElement;\n ref?: Ref<HTMLElement> | ((element: HTMLElement | null) => void);\n}\n\nexport type ButtonComponentProps = ButtonProps | ButtonAsChildProps;\n\ninterface ButtonComponent {\n (props: ButtonAsChildProps): ReactElement | null;\n (props: ButtonProps): ReactElement | null;\n}\n\ninterface ChildElementProps {\n children?: ReactNode;\n className?: string;\n onClick?: MouseEventHandler<HTMLElement>;\n}\n\nconst styleKeys = [\n 'variant',\n 'size',\n 'fullWidth',\n 'loading',\n 'asChild',\n 'className',\n] as const;\n\nconst getClassName = ({\n variant = 'default',\n size = 'md',\n fullWidth = false,\n loading = false,\n className,\n}: ButtonStyleProps) =>\n cn(\n 'button',\n className,\n when(variant !== 'default', `button--${variant}`),\n when(size !== 'md', `button--${size}`),\n when(fullWidth, 'button--full-width'),\n when(loading, 'button--loading'),\n );\n\nconst omitStyleProps = <T extends ButtonStyleProps>(\n props: T,\n): Omit<T, (typeof styleKeys)[number]> => {\n const next: Record<string, unknown> = {};\n const skipped = new Set<string>(styleKeys);\n\n for (const [key, value] of Object.entries(props)) {\n if (!skipped.has(key)) next[key] = value;\n }\n\n return next as Omit<T, (typeof styleKeys)[number]>;\n};\n\nconst renderContent = (children: ReactNode, loading?: boolean) => {\n if (!loading) return children;\n\n return (\n <>\n <span className=\"button__label\">{children}</span>\n <span className=\"button__spinner\">\n <IconSpinner className=\"button__icon--spin\" />\n </span>\n </>\n );\n};\n\n// forwardRef остаётся: в React 18 ref ещё не обычный проп, peer — ^18.3 || ^19.\nexport const Button = forwardRef(function Button(\n props: ButtonComponentProps,\n ref: ForwardedRef<HTMLElement>,\n) {\n const { loading } = props;\n const className = getClassName(props);\n const { children, ref: propsRef, ...domProps } = omitStyleProps(props);\n const nodeRef = ref ?? propsRef;\n\n if (props.asChild) {\n if (!isValidElement(children) || children.type === Fragment) {\n throw new Error('Button с asChild ждёт ровно один React-элемент.');\n }\n\n const childProps = children.props as ChildElementProps;\n const { disabled, onClick, type, ...childDomProps } =\n domProps as ButtonHTMLAttributes<HTMLButtonElement>;\n const hostButton = children.type === 'button';\n const isBlocked = Boolean(disabled || loading);\n const handleClick: MouseEventHandler<HTMLElement> = event => {\n if (isBlocked) {\n event.preventDefault();\n return;\n }\n\n childProps.onClick?.(event);\n if (!event.defaultPrevented) {\n (onClick as MouseEventHandler<HTMLElement> | undefined)?.(event);\n }\n };\n\n const element = cloneElement(\n children,\n {},\n renderContent(childProps.children, loading),\n );\n\n return (\n <Slot\n {...childDomProps}\n className={className}\n ref={nodeRef}\n onClick={handleClick}\n {...(type !== undefined || hostButton\n ? { type: type ?? 'button' }\n : {})}\n {...(hostButton && isBlocked ? { disabled: true } : {})}\n {...(loading ? { 'aria-busy': true } : {})}\n {...(!hostButton && isBlocked ? { 'aria-disabled': true } : {})}\n >\n {element}\n </Slot>\n );\n }\n\n const content = renderContent(children, loading);\n\n const {\n type = 'button',\n disabled,\n onClick,\n ...buttonProps\n } = domProps as ButtonHTMLAttributes<HTMLButtonElement>;\n\n return (\n <button\n {...buttonProps}\n ref={nodeRef as Ref<HTMLButtonElement>}\n className={className}\n type={type}\n disabled={disabled || loading}\n onClick={onClick}\n aria-busy={loading || undefined}\n >\n {content}\n </button>\n );\n}) as ButtonComponent;\n"],"mappings":";;;;;;;AA0DA,MAAM,YAAY;CAChB;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,gBAAgB,EACpB,UAAU,WACV,OAAO,MACP,YAAY,OACZ,UAAU,OACV,gBAEAA,WAAAA,GACE,UACA,WACAC,WAAAA,KAAK,YAAY,WAAW,WAAW,SAAS,GAChDA,WAAAA,KAAK,SAAS,MAAM,WAAW,MAAM,GACrCA,WAAAA,KAAK,WAAW,oBAAoB,GACpCA,WAAAA,KAAK,SAAS,iBAAiB,CACjC;AAEF,MAAM,kBACJ,UACwC;CACxC,MAAM,OAAgC,CAAC;CACvC,MAAM,UAAU,IAAI,IAAY,SAAS;CAEzC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAC7C,IAAI,CAAC,QAAQ,IAAI,GAAG,GAAG,KAAK,OAAO;CAGrC,OAAO;AACT;AAEA,MAAM,iBAAiB,UAAqB,YAAsB;CAChE,IAAI,CAAC,SAAS,OAAO;CAErB,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;EAAM,WAAU;EAAiB;CAAe,CAAA,GAChD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;EAAM,WAAU;EACd,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACC,qBAAAA,aAAD,EAAa,WAAU,qBAAsB,CAAA;CACzC,CAAA,CACN,EAAA,CAAA;AAEN;AAGA,MAAa,UAAA,GAASC,MAAAA,WAAAA,CAAW,SAAS,OACxC,OACA,KACA;CACA,MAAM,EAAE,YAAY;CACpB,MAAM,YAAY,aAAa,KAAK;CACpC,MAAM,EAAE,UAAU,KAAK,UAAU,GAAG,aAAa,eAAe,KAAK;CACrE,MAAM,UAAU,OAAO;CAEvB,IAAI,MAAM,SAAS;EACjB,IAAI,EAAA,GAACC,MAAAA,eAAAA,CAAe,QAAQ,KAAK,SAAS,SAASC,MAAAA,UACjD,MAAM,IAAI,MAAM,iDAAiD;EAGnE,MAAM,aAAa,SAAS;EAC5B,MAAM,EAAE,UAAU,SAAS,MAAM,GAAG,kBAClC;EACF,MAAM,aAAa,SAAS,SAAS;EACrC,MAAM,YAAY,QAAQ,YAAY,OAAO;EAC7C,MAAM,eAA8C,UAAS;GAC3D,IAAI,WAAW;IACb,MAAM,eAAe;IACrB;GACF;GAEA,WAAW,UAAU,KAAK;GAC1B,IAAI,CAAC,MAAM,kBACT,UAA0D,KAAK;EAEnE;EAEA,MAAM,WAAA,GAAUC,MAAAA,aAAAA,CACd,UACA,CAAC,GACD,cAAc,WAAW,UAAU,OAAO,CAC5C;EAEA,OACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,aAAAA,MAAD;GACE,GAAI;GACO;GACX,KAAK;GACL,SAAS;GACT,GAAK,SAAS,KAAA,KAAa,aACvB,EAAE,MAAM,QAAQ,SAAS,IACzB,CAAC;GACL,GAAK,cAAc,YAAY,EAAE,UAAU,KAAK,IAAI,CAAC;GACrD,GAAK,UAAU,EAAE,aAAa,KAAK,IAAI,CAAC;GACxC,GAAK,CAAC,cAAc,YAAY,EAAE,iBAAiB,KAAK,IAAI,CAAC;GAE5D,UAAA;EACG,CAAA;CAEV;CAEA,MAAM,UAAU,cAAc,UAAU,OAAO;CAE/C,MAAM,EACJ,OAAO,UACP,UACA,SACA,GAAG,gBACD;CAEJ,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;EACE,GAAI;EACJ,KAAK;EACM;EACL;EACN,UAAU,YAAY;EACb;EACT,aAAW,WAAW,KAAA;EAErB,UAAA;CACK,CAAA;AAEZ,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ButtonHTMLAttributes, ReactElement, ReactNode, Ref } from "react";
|
|
2
2
|
export type ButtonVariant = 'default' | 'outline' | 'link' | 'image' | 'icon';
|
|
3
3
|
export type ButtonSize = 'sm' | 'md' | 'lg';
|
|
4
4
|
interface ButtonStyleProps {
|
|
@@ -10,16 +10,17 @@ interface ButtonStyleProps {
|
|
|
10
10
|
children?: ReactNode;
|
|
11
11
|
}
|
|
12
12
|
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, ButtonStyleProps {
|
|
13
|
-
|
|
13
|
+
asChild?: false;
|
|
14
14
|
ref?: Ref<HTMLButtonElement> | ((element: HTMLElement | null) => void);
|
|
15
15
|
}
|
|
16
|
-
export interface
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
export interface ButtonAsChildProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'>, ButtonStyleProps {
|
|
17
|
+
asChild: true;
|
|
18
|
+
children: ReactElement;
|
|
19
|
+
ref?: Ref<HTMLElement> | ((element: HTMLElement | null) => void);
|
|
19
20
|
}
|
|
20
|
-
export type ButtonComponentProps = ButtonProps |
|
|
21
|
+
export type ButtonComponentProps = ButtonProps | ButtonAsChildProps;
|
|
21
22
|
interface ButtonComponent {
|
|
22
|
-
(props:
|
|
23
|
+
(props: ButtonAsChildProps): ReactElement | null;
|
|
23
24
|
(props: ButtonProps): ReactElement | null;
|
|
24
25
|
}
|
|
25
26
|
export declare const Button: ButtonComponent;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ButtonHTMLAttributes, ReactElement, ReactNode, Ref } from "react";
|
|
2
2
|
export type ButtonVariant = 'default' | 'outline' | 'link' | 'image' | 'icon';
|
|
3
3
|
export type ButtonSize = 'sm' | 'md' | 'lg';
|
|
4
4
|
interface ButtonStyleProps {
|
|
@@ -10,16 +10,17 @@ interface ButtonStyleProps {
|
|
|
10
10
|
children?: ReactNode;
|
|
11
11
|
}
|
|
12
12
|
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, ButtonStyleProps {
|
|
13
|
-
|
|
13
|
+
asChild?: false;
|
|
14
14
|
ref?: Ref<HTMLButtonElement> | ((element: HTMLElement | null) => void);
|
|
15
15
|
}
|
|
16
|
-
export interface
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
export interface ButtonAsChildProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'>, ButtonStyleProps {
|
|
17
|
+
asChild: true;
|
|
18
|
+
children: ReactElement;
|
|
19
|
+
ref?: Ref<HTMLElement> | ((element: HTMLElement | null) => void);
|
|
19
20
|
}
|
|
20
|
-
export type ButtonComponentProps = ButtonProps |
|
|
21
|
+
export type ButtonComponentProps = ButtonProps | ButtonAsChildProps;
|
|
21
22
|
interface ButtonComponent {
|
|
22
|
-
(props:
|
|
23
|
+
(props: ButtonAsChildProps): ReactElement | null;
|
|
23
24
|
(props: ButtonProps): ReactElement | null;
|
|
24
25
|
}
|
|
25
26
|
export declare const Button: ButtonComponent;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { IconSpinner } from "../../icons/icon-spinner.js";
|
|
2
2
|
import { cn, when } from "../../lib/cn.js";
|
|
3
|
-
import {
|
|
4
|
-
import { Fragment,
|
|
3
|
+
import { Slot } from "../../lib/slot.js";
|
|
4
|
+
import { Fragment, cloneElement, forwardRef, isValidElement } from "react";
|
|
5
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
6
|
import "./button.scss";
|
|
6
7
|
// src/components/button/button.tsx
|
|
7
8
|
const styleKeys = [
|
|
@@ -9,6 +10,7 @@ const styleKeys = [
|
|
|
9
10
|
"size",
|
|
10
11
|
"fullWidth",
|
|
11
12
|
"loading",
|
|
13
|
+
"asChild",
|
|
12
14
|
"className"
|
|
13
15
|
];
|
|
14
16
|
const getClassName = ({ variant = "default", size = "md", fullWidth = false, loading = false, className }) => cn("button", className, when(variant !== "default", `button--${variant}`), when(size !== "md", `button--${size}`), when(fullWidth, "button--full-width"), when(loading, "button--loading"));
|
|
@@ -20,7 +22,7 @@ const omitStyleProps = (props) => {
|
|
|
20
22
|
};
|
|
21
23
|
const renderContent = (children, loading) => {
|
|
22
24
|
if (!loading) return children;
|
|
23
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
25
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
24
26
|
className: "button__label",
|
|
25
27
|
children
|
|
26
28
|
}), /* @__PURE__ */ jsx("span", {
|
|
@@ -31,25 +33,36 @@ const renderContent = (children, loading) => {
|
|
|
31
33
|
const Button = forwardRef(function Button(props, ref) {
|
|
32
34
|
const { loading } = props;
|
|
33
35
|
const className = getClassName(props);
|
|
34
|
-
const { children,
|
|
36
|
+
const { children, ref: propsRef, ...domProps } = omitStyleProps(props);
|
|
35
37
|
const nodeRef = ref ?? propsRef;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
38
|
+
if (props.asChild) {
|
|
39
|
+
if (!isValidElement(children) || children.type === Fragment) throw new Error("Button с asChild ждёт ровно один React-элемент.");
|
|
40
|
+
const childProps = children.props;
|
|
41
|
+
const { disabled, onClick, type, ...childDomProps } = domProps;
|
|
42
|
+
const hostButton = children.type === "button";
|
|
43
|
+
const isBlocked = Boolean(disabled || loading);
|
|
39
44
|
const handleClick = (event) => {
|
|
40
|
-
|
|
45
|
+
if (isBlocked) {
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
childProps.onClick?.(event);
|
|
50
|
+
if (!event.defaultPrevented) onClick?.(event);
|
|
41
51
|
};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
52
|
+
const element = cloneElement(children, {}, renderContent(childProps.children, loading));
|
|
53
|
+
return /* @__PURE__ */ jsx(Slot, {
|
|
54
|
+
...childDomProps,
|
|
45
55
|
className,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
ref: nodeRef,
|
|
57
|
+
onClick: handleClick,
|
|
58
|
+
...type !== void 0 || hostButton ? { type: type ?? "button" } : {},
|
|
59
|
+
...hostButton && isBlocked ? { disabled: true } : {},
|
|
60
|
+
...loading ? { "aria-busy": true } : {},
|
|
61
|
+
...!hostButton && isBlocked ? { "aria-disabled": true } : {},
|
|
62
|
+
children: element
|
|
51
63
|
});
|
|
52
64
|
}
|
|
65
|
+
const content = renderContent(children, loading);
|
|
53
66
|
const { type = "button", disabled, onClick, ...buttonProps } = domProps;
|
|
54
67
|
return /* @__PURE__ */ jsx("button", {
|
|
55
68
|
...buttonProps,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.js","names":[],"sources":["../../../src/components/button/button.tsx"],"sourcesContent":["import {\n forwardRef,\n
|
|
1
|
+
{"version":3,"file":"button.js","names":[],"sources":["../../../src/components/button/button.tsx"],"sourcesContent":["import {\n cloneElement,\n Fragment,\n forwardRef,\n isValidElement,\n type ButtonHTMLAttributes,\n type ForwardedRef,\n type MouseEventHandler,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from 'react';\n\nimport { IconSpinner } from '@/icons/icon-spinner';\nimport { cn, when } from '@/lib/cn';\nimport { Slot } from '@/lib/slot';\nimport './button.scss';\n\nexport type ButtonVariant = 'default' | 'outline' | 'link' | 'image' | 'icon';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\n\ninterface ButtonStyleProps {\n variant?: ButtonVariant;\n size?: ButtonSize;\n fullWidth?: boolean;\n loading?: boolean;\n className?: string;\n children?: ReactNode;\n}\n\nexport interface ButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>, ButtonStyleProps {\n asChild?: false;\n ref?: Ref<HTMLButtonElement> | ((element: HTMLElement | null) => void);\n}\n\nexport interface ButtonAsChildProps\n extends\n Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'>,\n ButtonStyleProps {\n asChild: true;\n children: ReactElement;\n ref?: Ref<HTMLElement> | ((element: HTMLElement | null) => void);\n}\n\nexport type ButtonComponentProps = ButtonProps | ButtonAsChildProps;\n\ninterface ButtonComponent {\n (props: ButtonAsChildProps): ReactElement | null;\n (props: ButtonProps): ReactElement | null;\n}\n\ninterface ChildElementProps {\n children?: ReactNode;\n className?: string;\n onClick?: MouseEventHandler<HTMLElement>;\n}\n\nconst styleKeys = [\n 'variant',\n 'size',\n 'fullWidth',\n 'loading',\n 'asChild',\n 'className',\n] as const;\n\nconst getClassName = ({\n variant = 'default',\n size = 'md',\n fullWidth = false,\n loading = false,\n className,\n}: ButtonStyleProps) =>\n cn(\n 'button',\n className,\n when(variant !== 'default', `button--${variant}`),\n when(size !== 'md', `button--${size}`),\n when(fullWidth, 'button--full-width'),\n when(loading, 'button--loading'),\n );\n\nconst omitStyleProps = <T extends ButtonStyleProps>(\n props: T,\n): Omit<T, (typeof styleKeys)[number]> => {\n const next: Record<string, unknown> = {};\n const skipped = new Set<string>(styleKeys);\n\n for (const [key, value] of Object.entries(props)) {\n if (!skipped.has(key)) next[key] = value;\n }\n\n return next as Omit<T, (typeof styleKeys)[number]>;\n};\n\nconst renderContent = (children: ReactNode, loading?: boolean) => {\n if (!loading) return children;\n\n return (\n <>\n <span className=\"button__label\">{children}</span>\n <span className=\"button__spinner\">\n <IconSpinner className=\"button__icon--spin\" />\n </span>\n </>\n );\n};\n\n// forwardRef остаётся: в React 18 ref ещё не обычный проп, peer — ^18.3 || ^19.\nexport const Button = forwardRef(function Button(\n props: ButtonComponentProps,\n ref: ForwardedRef<HTMLElement>,\n) {\n const { loading } = props;\n const className = getClassName(props);\n const { children, ref: propsRef, ...domProps } = omitStyleProps(props);\n const nodeRef = ref ?? propsRef;\n\n if (props.asChild) {\n if (!isValidElement(children) || children.type === Fragment) {\n throw new Error('Button с asChild ждёт ровно один React-элемент.');\n }\n\n const childProps = children.props as ChildElementProps;\n const { disabled, onClick, type, ...childDomProps } =\n domProps as ButtonHTMLAttributes<HTMLButtonElement>;\n const hostButton = children.type === 'button';\n const isBlocked = Boolean(disabled || loading);\n const handleClick: MouseEventHandler<HTMLElement> = event => {\n if (isBlocked) {\n event.preventDefault();\n return;\n }\n\n childProps.onClick?.(event);\n if (!event.defaultPrevented) {\n (onClick as MouseEventHandler<HTMLElement> | undefined)?.(event);\n }\n };\n\n const element = cloneElement(\n children,\n {},\n renderContent(childProps.children, loading),\n );\n\n return (\n <Slot\n {...childDomProps}\n className={className}\n ref={nodeRef}\n onClick={handleClick}\n {...(type !== undefined || hostButton\n ? { type: type ?? 'button' }\n : {})}\n {...(hostButton && isBlocked ? { disabled: true } : {})}\n {...(loading ? { 'aria-busy': true } : {})}\n {...(!hostButton && isBlocked ? { 'aria-disabled': true } : {})}\n >\n {element}\n </Slot>\n );\n }\n\n const content = renderContent(children, loading);\n\n const {\n type = 'button',\n disabled,\n onClick,\n ...buttonProps\n } = domProps as ButtonHTMLAttributes<HTMLButtonElement>;\n\n return (\n <button\n {...buttonProps}\n ref={nodeRef as Ref<HTMLButtonElement>}\n className={className}\n type={type}\n disabled={disabled || loading}\n onClick={onClick}\n aria-busy={loading || undefined}\n >\n {content}\n </button>\n );\n}) as ButtonComponent;\n"],"mappings":";;;;;;;AA0DA,MAAM,YAAY;CAChB;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,gBAAgB,EACpB,UAAU,WACV,OAAO,MACP,YAAY,OACZ,UAAU,OACV,gBAEA,GACE,UACA,WACA,KAAK,YAAY,WAAW,WAAW,SAAS,GAChD,KAAK,SAAS,MAAM,WAAW,MAAM,GACrC,KAAK,WAAW,oBAAoB,GACpC,KAAK,SAAS,iBAAiB,CACjC;AAEF,MAAM,kBACJ,UACwC;CACxC,MAAM,OAAgC,CAAC;CACvC,MAAM,UAAU,IAAI,IAAY,SAAS;CAEzC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAC7C,IAAI,CAAC,QAAQ,IAAI,GAAG,GAAG,KAAK,OAAO;CAGrC,OAAO;AACT;AAEA,MAAM,iBAAiB,UAAqB,YAAsB;CAChE,IAAI,CAAC,SAAS,OAAO;CAErB,OACE,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,QAAD;EAAM,WAAU;EAAiB;CAAe,CAAA,GAChD,oBAAC,QAAD;EAAM,WAAU;EACd,UAAA,oBAAC,aAAD,EAAa,WAAU,qBAAsB,CAAA;CACzC,CAAA,CACN,EAAA,CAAA;AAEN;AAGA,MAAa,SAAS,WAAW,SAAS,OACxC,OACA,KACA;CACA,MAAM,EAAE,YAAY;CACpB,MAAM,YAAY,aAAa,KAAK;CACpC,MAAM,EAAE,UAAU,KAAK,UAAU,GAAG,aAAa,eAAe,KAAK;CACrE,MAAM,UAAU,OAAO;CAEvB,IAAI,MAAM,SAAS;EACjB,IAAI,CAAC,eAAe,QAAQ,KAAK,SAAS,SAAS,UACjD,MAAM,IAAI,MAAM,iDAAiD;EAGnE,MAAM,aAAa,SAAS;EAC5B,MAAM,EAAE,UAAU,SAAS,MAAM,GAAG,kBAClC;EACF,MAAM,aAAa,SAAS,SAAS;EACrC,MAAM,YAAY,QAAQ,YAAY,OAAO;EAC7C,MAAM,eAA8C,UAAS;GAC3D,IAAI,WAAW;IACb,MAAM,eAAe;IACrB;GACF;GAEA,WAAW,UAAU,KAAK;GAC1B,IAAI,CAAC,MAAM,kBACT,UAA0D,KAAK;EAEnE;EAEA,MAAM,UAAU,aACd,UACA,CAAC,GACD,cAAc,WAAW,UAAU,OAAO,CAC5C;EAEA,OACE,oBAAC,MAAD;GACE,GAAI;GACO;GACX,KAAK;GACL,SAAS;GACT,GAAK,SAAS,KAAA,KAAa,aACvB,EAAE,MAAM,QAAQ,SAAS,IACzB,CAAC;GACL,GAAK,cAAc,YAAY,EAAE,UAAU,KAAK,IAAI,CAAC;GACrD,GAAK,UAAU,EAAE,aAAa,KAAK,IAAI,CAAC;GACxC,GAAK,CAAC,cAAc,YAAY,EAAE,iBAAiB,KAAK,IAAI,CAAC;GAE5D,UAAA;EACG,CAAA;CAEV;CAEA,MAAM,UAAU,cAAc,UAAU,OAAO;CAE/C,MAAM,EACJ,OAAO,UACP,UACA,SACA,GAAG,gBACD;CAEJ,OACE,oBAAC,UAAD;EACE,GAAI;EACJ,KAAK;EACM;EACL;EACN,UAAU,YAAY;EACb;EACT,aAAW,WAAW,KAAA;EAErB,UAAA;CACK,CAAA;AAEZ,CAAC"}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
gap: 8px;
|
|
22
22
|
text-decoration: none;
|
|
23
23
|
|
|
24
|
-
&:hover:not(:active, :disabled) {
|
|
24
|
+
&:hover:not(:active, :disabled, [aria-disabled='true']) {
|
|
25
25
|
background-color: var(--color-primary-hover);
|
|
26
26
|
border-color: var(--color-primary-hover);
|
|
27
27
|
}
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
outline-offset: var(--focus-ring-offset);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
&:active {
|
|
38
|
+
&:active:not(:disabled, [aria-disabled='true']) {
|
|
39
39
|
background-color: var(--color-muted);
|
|
40
40
|
color: var(--color-muted-foreground);
|
|
41
41
|
fill: currentColor;
|
|
@@ -43,8 +43,11 @@
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// pointer-events не гасим: нативный disabled и так не пускает клик, а так
|
|
46
|
-
// на кнопку можно навести тултип «почему недоступна».
|
|
47
|
-
|
|
46
|
+
// на кнопку можно навести тултип «почему недоступна». aria-disabled — для
|
|
47
|
+
// asChild на ссылке: :disabled у <a> не срабатывает. loading уже aria-busy,
|
|
48
|
+
// его вид не приглушаем.
|
|
49
|
+
&:disabled,
|
|
50
|
+
&[aria-disabled='true']:not([aria-busy='true']) {
|
|
48
51
|
background-color: var(--color-muted);
|
|
49
52
|
color: var(--color-muted-foreground);
|
|
50
53
|
fill: currentColor;
|
|
@@ -59,14 +62,15 @@
|
|
|
59
62
|
fill: currentColor;
|
|
60
63
|
border: 2px solid var(--color-foreground);
|
|
61
64
|
|
|
62
|
-
&:hover:not(:active, :disabled) {
|
|
65
|
+
&:hover:not(:active, :disabled, [aria-disabled='true']) {
|
|
63
66
|
background-color: transparent;
|
|
64
67
|
border-color: var(--color-foreground);
|
|
65
68
|
color: var(--color-foreground);
|
|
66
69
|
fill: currentColor;
|
|
67
70
|
}
|
|
68
71
|
|
|
69
|
-
&:disabled
|
|
72
|
+
&:disabled,
|
|
73
|
+
&[aria-disabled='true']:not([aria-busy='true']) {
|
|
70
74
|
background-color: transparent;
|
|
71
75
|
}
|
|
72
76
|
}
|
|
@@ -79,12 +83,13 @@
|
|
|
79
83
|
border-radius: 0;
|
|
80
84
|
padding: 0;
|
|
81
85
|
|
|
82
|
-
&:hover:not(:active, :disabled) {
|
|
86
|
+
&:hover:not(:active, :disabled, [aria-disabled='true']) {
|
|
83
87
|
background-color: transparent;
|
|
84
88
|
}
|
|
85
89
|
|
|
86
|
-
&:active,
|
|
87
|
-
&:disabled
|
|
90
|
+
&:active:not([aria-disabled='true']),
|
|
91
|
+
&:disabled,
|
|
92
|
+
&[aria-disabled='true']:not([aria-busy='true']) {
|
|
88
93
|
background-color: transparent;
|
|
89
94
|
}
|
|
90
95
|
}
|
|
@@ -103,12 +108,13 @@
|
|
|
103
108
|
--size: 100px;
|
|
104
109
|
}
|
|
105
110
|
|
|
106
|
-
&:hover:not(:active, :disabled) {
|
|
111
|
+
&:hover:not(:active, :disabled, [aria-disabled='true']) {
|
|
107
112
|
background-color: var(--color-secondary);
|
|
108
113
|
}
|
|
109
114
|
|
|
110
115
|
&__icon {
|
|
111
116
|
--size-icon: 32px;
|
|
117
|
+
--size-svg: 16px;
|
|
112
118
|
position: absolute;
|
|
113
119
|
top: 50%;
|
|
114
120
|
left: 50%;
|
|
@@ -124,8 +130,14 @@
|
|
|
124
130
|
transform: translate(-50%, -50%);
|
|
125
131
|
pointer-events: none;
|
|
126
132
|
|
|
133
|
+
svg {
|
|
134
|
+
width: var(--size-svg);
|
|
135
|
+
height: var(--size-svg);
|
|
136
|
+
}
|
|
137
|
+
|
|
127
138
|
@include mixins.min-width(md) {
|
|
128
139
|
--size-icon: 40px;
|
|
140
|
+
--size-svg: 20px;
|
|
129
141
|
}
|
|
130
142
|
}
|
|
131
143
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Button,
|
|
2
|
-
export { Button, type
|
|
1
|
+
import { Button, ButtonAsChildProps, ButtonComponentProps, ButtonProps, ButtonSize, ButtonVariant } from "./button.cjs";
|
|
2
|
+
export { Button, type ButtonAsChildProps, type ButtonComponentProps, type ButtonProps, type ButtonSize, type ButtonVariant };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Button,
|
|
2
|
-
export { Button, type
|
|
1
|
+
import { Button, ButtonAsChildProps, ButtonComponentProps, ButtonProps, ButtonSize, ButtonVariant } from "./button.js";
|
|
2
|
+
export { Button, type ButtonAsChildProps, type ButtonComponentProps, type ButtonProps, type ButtonSize, type ButtonVariant };
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Button,
|
|
1
|
+
import { Button, ButtonAsChildProps, ButtonComponentProps, ButtonProps, ButtonSize, ButtonVariant } from "./components/button/button.cjs";
|
|
2
2
|
import "./components/button/index.cjs";
|
|
3
3
|
import { CheckboxInput, CheckboxInputProps } from "./components/checkbox/checkbox.cjs";
|
|
4
4
|
import "./components/checkbox/index.cjs";
|
|
@@ -12,4 +12,4 @@ import { Spinner, SpinnerPosition, SpinnerProps } from "./components/spinner/spi
|
|
|
12
12
|
import "./components/spinner/index.cjs";
|
|
13
13
|
import { TextArea, TextAreaProps, TextAreaSize } from "./components/textarea/textarea.cjs";
|
|
14
14
|
import "./components/textarea/index.cjs";
|
|
15
|
-
export { Button, type
|
|
15
|
+
export { Button, type ButtonAsChildProps, type ButtonComponentProps, type ButtonProps, type ButtonSize, type ButtonVariant, CheckboxInput, type CheckboxInputProps, Field, type FieldHintPlacement, type FieldProps, Input, type InputProps, type InputSize, RadioGroup, type RadioGroupProps, RadioInput, type RadioInputProps, Spinner, type SpinnerPosition, type SpinnerProps, TextArea, type TextAreaProps, type TextAreaSize };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Button,
|
|
1
|
+
import { Button, ButtonAsChildProps, ButtonComponentProps, ButtonProps, ButtonSize, ButtonVariant } from "./components/button/button.js";
|
|
2
2
|
import "./components/button/index.js";
|
|
3
3
|
import { CheckboxInput, CheckboxInputProps } from "./components/checkbox/checkbox.js";
|
|
4
4
|
import "./components/checkbox/index.js";
|
|
@@ -12,4 +12,4 @@ import { Spinner, SpinnerPosition, SpinnerProps } from "./components/spinner/spi
|
|
|
12
12
|
import "./components/spinner/index.js";
|
|
13
13
|
import { TextArea, TextAreaProps, TextAreaSize } from "./components/textarea/textarea.js";
|
|
14
14
|
import "./components/textarea/index.js";
|
|
15
|
-
export { Button, type
|
|
15
|
+
export { Button, type ButtonAsChildProps, type ButtonComponentProps, type ButtonProps, type ButtonSize, type ButtonVariant, CheckboxInput, type CheckboxInputProps, Field, type FieldHintPlacement, type FieldProps, Input, type InputProps, type InputSize, RadioGroup, type RadioGroupProps, RadioInput, type RadioInputProps, Spinner, type SpinnerPosition, type SpinnerProps, TextArea, type TextAreaProps, type TextAreaSize };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const require_cn = require("./cn.cjs");
|
|
2
|
+
let react = require("react");
|
|
3
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
4
|
+
// src/lib/slot.tsx
|
|
5
|
+
const isHandlerKey = (key) => /^on[A-Z]/.test(key);
|
|
6
|
+
const asHandler = (value) => {
|
|
7
|
+
if (typeof value !== "function") return void 0;
|
|
8
|
+
return value;
|
|
9
|
+
};
|
|
10
|
+
const asClassName = (value) => {
|
|
11
|
+
if (typeof value === "string" || typeof value === "number") return String(value);
|
|
12
|
+
};
|
|
13
|
+
const asStyle = (value) => {
|
|
14
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
15
|
+
return value;
|
|
16
|
+
};
|
|
17
|
+
const isPrevented = (event) => typeof event === "object" && event !== null && "defaultPrevented" in event && event.defaultPrevented === true;
|
|
18
|
+
const composeHandlers = (childHandler, slotHandler) => {
|
|
19
|
+
const child = asHandler(childHandler);
|
|
20
|
+
const slot = asHandler(slotHandler);
|
|
21
|
+
if (!child) return slot;
|
|
22
|
+
if (!slot) return child;
|
|
23
|
+
return (...args) => {
|
|
24
|
+
child(...args);
|
|
25
|
+
if (!isPrevented(args[0])) slot(...args);
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
const warnsOnRefRead = (getter) => {
|
|
29
|
+
if (!getter || !("isReactWarning" in getter)) return false;
|
|
30
|
+
return Boolean(getter.isReactWarning);
|
|
31
|
+
};
|
|
32
|
+
const readRef = (source) => {
|
|
33
|
+
if (!("ref" in source)) return void 0;
|
|
34
|
+
return source.ref ?? void 0;
|
|
35
|
+
};
|
|
36
|
+
const getElementRef = (element) => {
|
|
37
|
+
const props = "props" in element && element.props && typeof element.props === "object" ? element.props : {};
|
|
38
|
+
const propsGetter = Object.getOwnPropertyDescriptor(props, "ref")?.get;
|
|
39
|
+
if (warnsOnRefRead(propsGetter)) return readRef(element);
|
|
40
|
+
const elementGetter = Object.getOwnPropertyDescriptor(element, "ref")?.get;
|
|
41
|
+
if (warnsOnRefRead(elementGetter)) return readRef(props);
|
|
42
|
+
return readRef(props) ?? readRef(element);
|
|
43
|
+
};
|
|
44
|
+
const mergeProps = (childProps, slotProps) => {
|
|
45
|
+
const merged = { ...slotProps };
|
|
46
|
+
for (const key of Object.keys(childProps)) {
|
|
47
|
+
if (key === "ref") continue;
|
|
48
|
+
const childValue = childProps[key];
|
|
49
|
+
const slotValue = slotProps[key];
|
|
50
|
+
if (key === "className") continue;
|
|
51
|
+
if (key === "style") {
|
|
52
|
+
merged.style = {
|
|
53
|
+
...asStyle(slotValue),
|
|
54
|
+
...asStyle(childValue)
|
|
55
|
+
};
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (key === "onClick") continue;
|
|
59
|
+
if (isHandlerKey(key)) {
|
|
60
|
+
merged[key] = composeHandlers(childValue, slotValue);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (key === "disabled" && slotProps.disabled || key === "aria-busy" && slotProps["aria-busy"] || key === "aria-disabled" && slotProps["aria-disabled"]) continue;
|
|
64
|
+
merged[key] = childValue;
|
|
65
|
+
}
|
|
66
|
+
const className = require_cn.cn(asClassName(slotProps.className), asClassName(childProps.className));
|
|
67
|
+
if (className) merged.className = className;
|
|
68
|
+
if (slotProps.onClick) merged.onClick = slotProps.onClick;
|
|
69
|
+
return merged;
|
|
70
|
+
};
|
|
71
|
+
const attachRef = (ref, node) => {
|
|
72
|
+
if (typeof ref === "function") return ref(node);
|
|
73
|
+
if (ref) ref.current = node;
|
|
74
|
+
};
|
|
75
|
+
/** Сливает пропы в единственного ребёнка, без своего DOM-узла. Как Radix Slot. */
|
|
76
|
+
const Slot = (0, react.forwardRef)(function Slot(props, slotRef) {
|
|
77
|
+
const { children, ...slotProps } = props;
|
|
78
|
+
if (!(0, react.isValidElement)(children)) throw new Error("Button с asChild ждёт ровно один React-элемент.");
|
|
79
|
+
const childProps = children.props;
|
|
80
|
+
const childRef = getElementRef(children);
|
|
81
|
+
const Comp = children.type;
|
|
82
|
+
const merged = mergeProps(childProps, slotProps);
|
|
83
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Comp, {
|
|
84
|
+
...merged,
|
|
85
|
+
ref: (node) => {
|
|
86
|
+
const slotCleanup = attachRef(slotRef, node);
|
|
87
|
+
const childCleanup = attachRef(childRef, node);
|
|
88
|
+
if (typeof slotCleanup !== "function" && typeof childCleanup !== "function") return;
|
|
89
|
+
return () => {
|
|
90
|
+
if (typeof slotCleanup === "function") slotCleanup();
|
|
91
|
+
else attachRef(slotRef, null);
|
|
92
|
+
if (typeof childCleanup === "function") childCleanup();
|
|
93
|
+
else attachRef(childRef, null);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
exports.Slot = Slot;
|
|
99
|
+
|
|
100
|
+
//# sourceMappingURL=slot.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slot.cjs","names":["cn","forwardRef","isValidElement"],"sources":["../../src/lib/slot.tsx"],"sourcesContent":["import {\n forwardRef,\n isValidElement,\n type CSSProperties,\n type ElementType,\n type ForwardedRef,\n type MouseEventHandler,\n type ReactNode,\n type Ref,\n} from 'react';\n\nimport { cn } from './cn';\n\ninterface SlotProps {\n children?: ReactNode;\n className?: string;\n style?: CSSProperties;\n onClick?: MouseEventHandler<HTMLElement>;\n disabled?: boolean;\n [key: string]: unknown;\n}\n\ntype AnyHandler = (...args: unknown[]) => void;\n\nconst isHandlerKey = (key: string) => /^on[A-Z]/.test(key);\n\nconst asHandler = (value: unknown): AnyHandler | undefined => {\n if (typeof value !== 'function') return undefined;\n return value as AnyHandler;\n};\n\nconst asClassName = (value: unknown) => {\n if (typeof value === 'string' || typeof value === 'number')\n return String(value);\n return undefined;\n};\n\nconst asStyle = (value: unknown): CSSProperties | undefined => {\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return undefined;\n return value as CSSProperties;\n};\n\nconst isPrevented = (event: unknown) =>\n typeof event === 'object' &&\n event !== null &&\n 'defaultPrevented' in event &&\n event.defaultPrevented === true;\n\nconst composeHandlers = (childHandler: unknown, slotHandler: unknown) => {\n const child = asHandler(childHandler);\n const slot = asHandler(slotHandler);\n if (!child) return slot;\n if (!slot) return child;\n\n return (...args: unknown[]) => {\n child(...args);\n if (!isPrevented(args[0])) slot(...args);\n };\n};\n\nconst warnsOnRefRead = (getter: PropertyDescriptor['get']) => {\n if (!getter || !('isReactWarning' in getter)) return false;\n return Boolean((getter as { isReactWarning?: boolean }).isReactWarning);\n};\n\nconst readRef = (source: object): Ref<HTMLElement> | undefined => {\n if (!('ref' in source)) return undefined;\n const value = (source as { ref?: Ref<HTMLElement> | null }).ref;\n return value ?? undefined;\n};\n\n// React 18 кладёт ref на элемент, React 19 — в props. Геттер-заглушка\n// ругается при чтении «не той» стороны, поэтому берём ту, что без предупреждения.\nconst getElementRef = (element: object) => {\n const props =\n 'props' in element && element.props && typeof element.props === 'object'\n ? element.props\n : {};\n const propsGetter = Object.getOwnPropertyDescriptor(props, 'ref')?.get;\n if (warnsOnRefRead(propsGetter)) return readRef(element);\n\n const elementGetter = Object.getOwnPropertyDescriptor(element, 'ref')?.get;\n if (warnsOnRefRead(elementGetter)) return readRef(props);\n\n return readRef(props) ?? readRef(element);\n};\n\nconst mergeProps = (\n childProps: Record<string, unknown>,\n slotProps: SlotProps,\n) => {\n const merged: Record<string, unknown> = { ...slotProps };\n\n for (const key of Object.keys(childProps)) {\n if (key === 'ref') continue;\n\n const childValue = childProps[key];\n const slotValue = slotProps[key];\n\n if (key === 'className') continue;\n\n if (key === 'style') {\n merged.style = { ...asStyle(slotValue), ...asStyle(childValue) };\n continue;\n }\n\n if (key === 'onClick') continue;\n\n if (isHandlerKey(key)) {\n merged[key] = composeHandlers(childValue, slotValue);\n continue;\n }\n\n if (\n (key === 'disabled' && slotProps.disabled) ||\n (key === 'aria-busy' && slotProps['aria-busy']) ||\n (key === 'aria-disabled' && slotProps['aria-disabled'])\n ) {\n continue;\n }\n\n merged[key] = childValue;\n }\n\n const className = cn(\n asClassName(slotProps.className),\n asClassName(childProps.className),\n );\n if (className) merged.className = className;\n if (slotProps.onClick) merged.onClick = slotProps.onClick;\n\n return merged;\n};\n\nconst attachRef = (\n ref: ForwardedRef<HTMLElement> | Ref<HTMLElement> | undefined,\n node: HTMLElement | null,\n) => {\n if (typeof ref === 'function') return ref(node);\n if (ref) ref.current = node;\n return undefined;\n};\n\n/** Сливает пропы в единственного ребёнка, без своего DOM-узла. Как Radix Slot. */\nexport const Slot = forwardRef(function Slot(\n props: SlotProps,\n slotRef: ForwardedRef<HTMLElement>,\n) {\n const { children, ...slotProps } = props;\n\n if (!isValidElement(children)) {\n throw new Error('Button с asChild ждёт ровно один React-элемент.');\n }\n\n const childProps = children.props as Record<string, unknown>;\n const childRef = getElementRef(children);\n const Comp = children.type as ElementType;\n const merged = mergeProps(childProps, slotProps);\n\n return (\n <Comp\n {...merged}\n ref={(node: HTMLElement | null) => {\n const slotCleanup = attachRef(slotRef, node);\n const childCleanup = attachRef(childRef, node);\n if (\n typeof slotCleanup !== 'function' &&\n typeof childCleanup !== 'function'\n ) {\n return undefined;\n }\n\n return () => {\n if (typeof slotCleanup === 'function') slotCleanup();\n else attachRef(slotRef, null);\n if (typeof childCleanup === 'function') childCleanup();\n else attachRef(childRef, null);\n };\n }}\n />\n );\n});\n"],"mappings":"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"}
|
package/dist/lib/slot.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { cn } from "./cn.js";
|
|
2
|
+
import { forwardRef, isValidElement } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
// src/lib/slot.tsx
|
|
5
|
+
const isHandlerKey = (key) => /^on[A-Z]/.test(key);
|
|
6
|
+
const asHandler = (value) => {
|
|
7
|
+
if (typeof value !== "function") return void 0;
|
|
8
|
+
return value;
|
|
9
|
+
};
|
|
10
|
+
const asClassName = (value) => {
|
|
11
|
+
if (typeof value === "string" || typeof value === "number") return String(value);
|
|
12
|
+
};
|
|
13
|
+
const asStyle = (value) => {
|
|
14
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
15
|
+
return value;
|
|
16
|
+
};
|
|
17
|
+
const isPrevented = (event) => typeof event === "object" && event !== null && "defaultPrevented" in event && event.defaultPrevented === true;
|
|
18
|
+
const composeHandlers = (childHandler, slotHandler) => {
|
|
19
|
+
const child = asHandler(childHandler);
|
|
20
|
+
const slot = asHandler(slotHandler);
|
|
21
|
+
if (!child) return slot;
|
|
22
|
+
if (!slot) return child;
|
|
23
|
+
return (...args) => {
|
|
24
|
+
child(...args);
|
|
25
|
+
if (!isPrevented(args[0])) slot(...args);
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
const warnsOnRefRead = (getter) => {
|
|
29
|
+
if (!getter || !("isReactWarning" in getter)) return false;
|
|
30
|
+
return Boolean(getter.isReactWarning);
|
|
31
|
+
};
|
|
32
|
+
const readRef = (source) => {
|
|
33
|
+
if (!("ref" in source)) return void 0;
|
|
34
|
+
return source.ref ?? void 0;
|
|
35
|
+
};
|
|
36
|
+
const getElementRef = (element) => {
|
|
37
|
+
const props = "props" in element && element.props && typeof element.props === "object" ? element.props : {};
|
|
38
|
+
const propsGetter = Object.getOwnPropertyDescriptor(props, "ref")?.get;
|
|
39
|
+
if (warnsOnRefRead(propsGetter)) return readRef(element);
|
|
40
|
+
const elementGetter = Object.getOwnPropertyDescriptor(element, "ref")?.get;
|
|
41
|
+
if (warnsOnRefRead(elementGetter)) return readRef(props);
|
|
42
|
+
return readRef(props) ?? readRef(element);
|
|
43
|
+
};
|
|
44
|
+
const mergeProps = (childProps, slotProps) => {
|
|
45
|
+
const merged = { ...slotProps };
|
|
46
|
+
for (const key of Object.keys(childProps)) {
|
|
47
|
+
if (key === "ref") continue;
|
|
48
|
+
const childValue = childProps[key];
|
|
49
|
+
const slotValue = slotProps[key];
|
|
50
|
+
if (key === "className") continue;
|
|
51
|
+
if (key === "style") {
|
|
52
|
+
merged.style = {
|
|
53
|
+
...asStyle(slotValue),
|
|
54
|
+
...asStyle(childValue)
|
|
55
|
+
};
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (key === "onClick") continue;
|
|
59
|
+
if (isHandlerKey(key)) {
|
|
60
|
+
merged[key] = composeHandlers(childValue, slotValue);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (key === "disabled" && slotProps.disabled || key === "aria-busy" && slotProps["aria-busy"] || key === "aria-disabled" && slotProps["aria-disabled"]) continue;
|
|
64
|
+
merged[key] = childValue;
|
|
65
|
+
}
|
|
66
|
+
const className = cn(asClassName(slotProps.className), asClassName(childProps.className));
|
|
67
|
+
if (className) merged.className = className;
|
|
68
|
+
if (slotProps.onClick) merged.onClick = slotProps.onClick;
|
|
69
|
+
return merged;
|
|
70
|
+
};
|
|
71
|
+
const attachRef = (ref, node) => {
|
|
72
|
+
if (typeof ref === "function") return ref(node);
|
|
73
|
+
if (ref) ref.current = node;
|
|
74
|
+
};
|
|
75
|
+
/** Сливает пропы в единственного ребёнка, без своего DOM-узла. Как Radix Slot. */
|
|
76
|
+
const Slot = forwardRef(function Slot(props, slotRef) {
|
|
77
|
+
const { children, ...slotProps } = props;
|
|
78
|
+
if (!isValidElement(children)) throw new Error("Button с asChild ждёт ровно один React-элемент.");
|
|
79
|
+
const childProps = children.props;
|
|
80
|
+
const childRef = getElementRef(children);
|
|
81
|
+
const Comp = children.type;
|
|
82
|
+
const merged = mergeProps(childProps, slotProps);
|
|
83
|
+
return /* @__PURE__ */ jsx(Comp, {
|
|
84
|
+
...merged,
|
|
85
|
+
ref: (node) => {
|
|
86
|
+
const slotCleanup = attachRef(slotRef, node);
|
|
87
|
+
const childCleanup = attachRef(childRef, node);
|
|
88
|
+
if (typeof slotCleanup !== "function" && typeof childCleanup !== "function") return;
|
|
89
|
+
return () => {
|
|
90
|
+
if (typeof slotCleanup === "function") slotCleanup();
|
|
91
|
+
else attachRef(slotRef, null);
|
|
92
|
+
if (typeof childCleanup === "function") childCleanup();
|
|
93
|
+
else attachRef(childRef, null);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
export { Slot };
|
|
99
|
+
|
|
100
|
+
//# sourceMappingURL=slot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slot.js","names":[],"sources":["../../src/lib/slot.tsx"],"sourcesContent":["import {\n forwardRef,\n isValidElement,\n type CSSProperties,\n type ElementType,\n type ForwardedRef,\n type MouseEventHandler,\n type ReactNode,\n type Ref,\n} from 'react';\n\nimport { cn } from './cn';\n\ninterface SlotProps {\n children?: ReactNode;\n className?: string;\n style?: CSSProperties;\n onClick?: MouseEventHandler<HTMLElement>;\n disabled?: boolean;\n [key: string]: unknown;\n}\n\ntype AnyHandler = (...args: unknown[]) => void;\n\nconst isHandlerKey = (key: string) => /^on[A-Z]/.test(key);\n\nconst asHandler = (value: unknown): AnyHandler | undefined => {\n if (typeof value !== 'function') return undefined;\n return value as AnyHandler;\n};\n\nconst asClassName = (value: unknown) => {\n if (typeof value === 'string' || typeof value === 'number')\n return String(value);\n return undefined;\n};\n\nconst asStyle = (value: unknown): CSSProperties | undefined => {\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return undefined;\n return value as CSSProperties;\n};\n\nconst isPrevented = (event: unknown) =>\n typeof event === 'object' &&\n event !== null &&\n 'defaultPrevented' in event &&\n event.defaultPrevented === true;\n\nconst composeHandlers = (childHandler: unknown, slotHandler: unknown) => {\n const child = asHandler(childHandler);\n const slot = asHandler(slotHandler);\n if (!child) return slot;\n if (!slot) return child;\n\n return (...args: unknown[]) => {\n child(...args);\n if (!isPrevented(args[0])) slot(...args);\n };\n};\n\nconst warnsOnRefRead = (getter: PropertyDescriptor['get']) => {\n if (!getter || !('isReactWarning' in getter)) return false;\n return Boolean((getter as { isReactWarning?: boolean }).isReactWarning);\n};\n\nconst readRef = (source: object): Ref<HTMLElement> | undefined => {\n if (!('ref' in source)) return undefined;\n const value = (source as { ref?: Ref<HTMLElement> | null }).ref;\n return value ?? undefined;\n};\n\n// React 18 кладёт ref на элемент, React 19 — в props. Геттер-заглушка\n// ругается при чтении «не той» стороны, поэтому берём ту, что без предупреждения.\nconst getElementRef = (element: object) => {\n const props =\n 'props' in element && element.props && typeof element.props === 'object'\n ? element.props\n : {};\n const propsGetter = Object.getOwnPropertyDescriptor(props, 'ref')?.get;\n if (warnsOnRefRead(propsGetter)) return readRef(element);\n\n const elementGetter = Object.getOwnPropertyDescriptor(element, 'ref')?.get;\n if (warnsOnRefRead(elementGetter)) return readRef(props);\n\n return readRef(props) ?? readRef(element);\n};\n\nconst mergeProps = (\n childProps: Record<string, unknown>,\n slotProps: SlotProps,\n) => {\n const merged: Record<string, unknown> = { ...slotProps };\n\n for (const key of Object.keys(childProps)) {\n if (key === 'ref') continue;\n\n const childValue = childProps[key];\n const slotValue = slotProps[key];\n\n if (key === 'className') continue;\n\n if (key === 'style') {\n merged.style = { ...asStyle(slotValue), ...asStyle(childValue) };\n continue;\n }\n\n if (key === 'onClick') continue;\n\n if (isHandlerKey(key)) {\n merged[key] = composeHandlers(childValue, slotValue);\n continue;\n }\n\n if (\n (key === 'disabled' && slotProps.disabled) ||\n (key === 'aria-busy' && slotProps['aria-busy']) ||\n (key === 'aria-disabled' && slotProps['aria-disabled'])\n ) {\n continue;\n }\n\n merged[key] = childValue;\n }\n\n const className = cn(\n asClassName(slotProps.className),\n asClassName(childProps.className),\n );\n if (className) merged.className = className;\n if (slotProps.onClick) merged.onClick = slotProps.onClick;\n\n return merged;\n};\n\nconst attachRef = (\n ref: ForwardedRef<HTMLElement> | Ref<HTMLElement> | undefined,\n node: HTMLElement | null,\n) => {\n if (typeof ref === 'function') return ref(node);\n if (ref) ref.current = node;\n return undefined;\n};\n\n/** Сливает пропы в единственного ребёнка, без своего DOM-узла. Как Radix Slot. */\nexport const Slot = forwardRef(function Slot(\n props: SlotProps,\n slotRef: ForwardedRef<HTMLElement>,\n) {\n const { children, ...slotProps } = props;\n\n if (!isValidElement(children)) {\n throw new Error('Button с asChild ждёт ровно один React-элемент.');\n }\n\n const childProps = children.props as Record<string, unknown>;\n const childRef = getElementRef(children);\n const Comp = children.type as ElementType;\n const merged = mergeProps(childProps, slotProps);\n\n return (\n <Comp\n {...merged}\n ref={(node: HTMLElement | null) => {\n const slotCleanup = attachRef(slotRef, node);\n const childCleanup = attachRef(childRef, node);\n if (\n typeof slotCleanup !== 'function' &&\n typeof childCleanup !== 'function'\n ) {\n return undefined;\n }\n\n return () => {\n if (typeof slotCleanup === 'function') slotCleanup();\n else attachRef(slotRef, null);\n if (typeof childCleanup === 'function') childCleanup();\n else attachRef(childRef, null);\n };\n }}\n />\n );\n});\n"],"mappings":"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"}
|