@koslibs/components 1.11.1 → 1.13.0
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/CHANGELOG.md +20 -0
- package/dist/components/Button/index_module.css +4 -0
- package/dist/components/Collapse/collapse.d.ts +10 -0
- package/dist/components/Collapse/collapse.js +2 -1
- package/dist/components/Collapse/index_module.css +6 -5
- package/dist/components/Input/index_module.css +8 -7
- package/dist/components/Input/input.d.ts +1 -0
- package/dist/components/Input/input.js +2 -1
- package/dist/components/Input/types.d.ts +5 -0
- package/dist/components/Modal/components/modal-surface/index.js +1 -1
- package/dist/components/Select/components/select-dropdown.d.ts +2 -2
- package/dist/components/Select/components/select-dropdown.js +2 -1
- package/dist/components/Select/index_module.css +5 -4
- package/dist/components/Select/select.js +3 -1
- package/dist/components/Select/types.d.ts +1 -1
- package/dist/components/Sidepanel/components/content/index.d.ts +13 -0
- package/dist/components/Sidepanel/components/content/index.js +21 -0
- package/dist/components/Sidepanel/components/content/index.module.js +5 -0
- package/dist/components/Sidepanel/components/content/index_module.css +14 -0
- package/dist/components/Sidepanel/components/footer/index.d.ts +66 -0
- package/dist/components/Sidepanel/components/footer/index.js +25 -0
- package/dist/components/Sidepanel/components/footer/index.module.js +19 -0
- package/dist/components/Sidepanel/components/footer/index_module.css +57 -0
- package/dist/components/Sidepanel/components/header/index.d.ts +33 -0
- package/dist/components/Sidepanel/components/header/index.js +45 -0
- package/dist/components/Sidepanel/components/header/index.module.js +8 -0
- package/dist/components/Sidepanel/components/header/index_module.css +32 -0
- package/dist/components/Sidepanel/components/sidepanel-surface/index.d.ts +4 -0
- package/dist/components/Sidepanel/components/sidepanel-surface/index.js +92 -0
- package/dist/components/Sidepanel/context.d.ts +6 -0
- package/dist/components/Sidepanel/context.js +3 -0
- package/dist/components/Sidepanel/hooks/use-dialog-transition.d.ts +5 -0
- package/dist/components/Sidepanel/hooks/use-dialog-transition.js +83 -0
- package/dist/components/Sidepanel/index.d.ts +8 -0
- package/dist/components/Sidepanel/index.js +4 -0
- package/dist/components/Sidepanel/index.module.js +8 -0
- package/dist/components/Sidepanel/index_module.css +68 -0
- package/dist/components/Sidepanel/scrollbar.module.js +5 -0
- package/dist/components/Sidepanel/scrollbar_module.css +41 -0
- package/dist/components/Sidepanel/sidepanel.d.ts +9 -0
- package/dist/components/Sidepanel/sidepanel.js +28 -0
- package/dist/components/Sidepanel/types.d.ts +36 -0
- package/dist/components/Sidepanel/types.js +0 -0
- package/dist/components/Sidepanel/vars.css +12 -0
- package/dist/components/Tabs/index_module.css +1 -1
- package/dist/components/Textarea/index_module.css +3 -2
- package/dist/components/Textarea/textarea.d.ts +1 -0
- package/dist/components/Textarea/textarea.js +2 -1
- package/dist/components/Textarea/types.d.ts +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/utils/lock-scroll.d.ts +1 -0
- package/dist/utils/lock-scroll.js +49 -0
- package/package.json +1 -1
- package/dist/components/Modal/utils/lock-scroll.d.ts +0 -1
- package/dist/components/Modal/utils/lock-scroll.js +0 -31
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
.component-pnHhxm {
|
|
2
|
+
box-sizing: border-box;
|
|
3
|
+
justify-content: space-between;
|
|
4
|
+
align-items: center;
|
|
5
|
+
gap: var(--size-16);
|
|
6
|
+
min-height: var(--size-80);
|
|
7
|
+
padding: var(--sidepanel-padding);
|
|
8
|
+
flex-shrink: 0;
|
|
9
|
+
display: flex;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.title-nolsR9 {
|
|
13
|
+
overflow-wrap: anywhere;
|
|
14
|
+
flex: 1;
|
|
15
|
+
min-width: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.component-pnHhxm > .closer-PhFI6O {
|
|
19
|
+
width: var(--size-24);
|
|
20
|
+
min-width: var(--size-24);
|
|
21
|
+
height: var(--size-32);
|
|
22
|
+
min-height: var(--size-32);
|
|
23
|
+
padding: var(--size-0);
|
|
24
|
+
flex-shrink: 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.closerLabel-q4m2AK {
|
|
28
|
+
justify-content: center;
|
|
29
|
+
align-items: center;
|
|
30
|
+
display: flex;
|
|
31
|
+
}
|
|
32
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import classnames from "classnames";
|
|
3
|
+
import { forwardRef, useCallback, useLayoutEffect, useMemo, useRef } from "react";
|
|
4
|
+
import { lockScroll } from "../../../../utils/lock-scroll.js";
|
|
5
|
+
import { SidepanelContext } from "../../context.js";
|
|
6
|
+
import { useDialogTransition } from "../../hooks/use-dialog-transition.js";
|
|
7
|
+
import index_module from "../../index.module.js";
|
|
8
|
+
import scrollbar_module from "../../scrollbar.module.js";
|
|
9
|
+
const SidepanelSurface = /*#__PURE__*/ forwardRef(({ open, onClose, onExited, escapeKeyDown = true, backdropClick = true, lockScroll: shouldLockScroll = true, className, dataTestId, children, onPointerDown, onClick, onTransitionEnd, ...restProps }, ref)=>{
|
|
10
|
+
const dialogRef = useRef(null);
|
|
11
|
+
const backdropPointerDown = useRef(false);
|
|
12
|
+
const mounted = useRef(false);
|
|
13
|
+
useLayoutEffect(()=>{
|
|
14
|
+
mounted.current = true;
|
|
15
|
+
return ()=>{
|
|
16
|
+
mounted.current = false;
|
|
17
|
+
};
|
|
18
|
+
}, []);
|
|
19
|
+
const { active, finishClosing } = useDialogTransition(dialogRef, open, onExited);
|
|
20
|
+
const requestClose = useCallback(()=>{
|
|
21
|
+
if (open) onClose?.();
|
|
22
|
+
}, [
|
|
23
|
+
onClose,
|
|
24
|
+
open
|
|
25
|
+
]);
|
|
26
|
+
const mergedRef = useCallback((element)=>{
|
|
27
|
+
dialogRef.current = element;
|
|
28
|
+
if ('function' == typeof ref) ref(element);
|
|
29
|
+
else if (ref) ref.current = element;
|
|
30
|
+
}, [
|
|
31
|
+
ref
|
|
32
|
+
]);
|
|
33
|
+
const context = useMemo(()=>({
|
|
34
|
+
onClose: requestClose,
|
|
35
|
+
dataTestId
|
|
36
|
+
}), [
|
|
37
|
+
dataTestId,
|
|
38
|
+
requestClose
|
|
39
|
+
]);
|
|
40
|
+
useLayoutEffect(()=>{
|
|
41
|
+
const dialog = dialogRef.current;
|
|
42
|
+
if (active && shouldLockScroll && dialog) return lockScroll(dialog.ownerDocument, true);
|
|
43
|
+
}, [
|
|
44
|
+
active,
|
|
45
|
+
shouldLockScroll
|
|
46
|
+
]);
|
|
47
|
+
const isBackdrop = (event)=>{
|
|
48
|
+
const bounds = event.currentTarget.getBoundingClientRect();
|
|
49
|
+
return event.target === event.currentTarget && (event.clientX < bounds.left || event.clientX > bounds.right || event.clientY < bounds.top || event.clientY > bounds.bottom);
|
|
50
|
+
};
|
|
51
|
+
const handleCancel = (event)=>{
|
|
52
|
+
event.preventDefault();
|
|
53
|
+
event.stopPropagation();
|
|
54
|
+
if (escapeKeyDown) requestClose();
|
|
55
|
+
};
|
|
56
|
+
const handleClose = (event)=>{
|
|
57
|
+
event.stopPropagation();
|
|
58
|
+
if (mounted.current && !event.currentTarget.open) requestClose();
|
|
59
|
+
};
|
|
60
|
+
const handlePointerDown = (event)=>{
|
|
61
|
+
onPointerDown?.(event);
|
|
62
|
+
backdropPointerDown.current = !event.defaultPrevented && isBackdrop(event);
|
|
63
|
+
};
|
|
64
|
+
const handleClick = (event)=>{
|
|
65
|
+
onClick?.(event);
|
|
66
|
+
const close = backdropPointerDown.current && isBackdrop(event);
|
|
67
|
+
backdropPointerDown.current = false;
|
|
68
|
+
if (backdropClick && close && !event.defaultPrevented) requestClose();
|
|
69
|
+
};
|
|
70
|
+
const handleTransitionEnd = (event)=>{
|
|
71
|
+
onTransitionEnd?.(event);
|
|
72
|
+
if (event.target === event.currentTarget && 'transform' === event.propertyName) finishClosing();
|
|
73
|
+
};
|
|
74
|
+
return /*#__PURE__*/ jsx("dialog", {
|
|
75
|
+
...restProps,
|
|
76
|
+
ref: mergedRef,
|
|
77
|
+
className: classnames(index_module.component, scrollbar_module.scrollbar, className),
|
|
78
|
+
"data-test-id": dataTestId,
|
|
79
|
+
"data-state": "closed",
|
|
80
|
+
onCancel: handleCancel,
|
|
81
|
+
onClose: handleClose,
|
|
82
|
+
onPointerDown: handlePointerDown,
|
|
83
|
+
onClick: handleClick,
|
|
84
|
+
onTransitionEnd: handleTransitionEnd,
|
|
85
|
+
children: /*#__PURE__*/ jsx(SidepanelContext.Provider, {
|
|
86
|
+
value: context,
|
|
87
|
+
children: children
|
|
88
|
+
})
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
SidepanelSurface.displayName = 'SidepanelSurface';
|
|
92
|
+
export { SidepanelSurface };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useCallback, useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
function milliseconds(value) {
|
|
3
|
+
return (parseFloat(value) || 0) * (value.trim().endsWith('ms') ? 1 : 1000);
|
|
4
|
+
}
|
|
5
|
+
function transitionTime(dialog) {
|
|
6
|
+
const style = dialog.ownerDocument.defaultView?.getComputedStyle(dialog);
|
|
7
|
+
const durations = style?.transitionDuration.split(',').map(milliseconds) ?? [
|
|
8
|
+
0
|
|
9
|
+
];
|
|
10
|
+
const delays = style?.transitionDelay.split(',').map(milliseconds) ?? [
|
|
11
|
+
0
|
|
12
|
+
];
|
|
13
|
+
return Math.max(0, ...durations.map((duration, index)=>duration + delays[index % delays.length]));
|
|
14
|
+
}
|
|
15
|
+
function useDialogTransition(dialogRef, open, onExited) {
|
|
16
|
+
const [active, setActive] = useState(open);
|
|
17
|
+
const latest = useRef({
|
|
18
|
+
open,
|
|
19
|
+
onExited
|
|
20
|
+
});
|
|
21
|
+
useLayoutEffect(()=>{
|
|
22
|
+
latest.current = {
|
|
23
|
+
open,
|
|
24
|
+
onExited
|
|
25
|
+
};
|
|
26
|
+
}, [
|
|
27
|
+
open,
|
|
28
|
+
onExited
|
|
29
|
+
]);
|
|
30
|
+
const finishClosing = useCallback(()=>{
|
|
31
|
+
const dialog = dialogRef.current;
|
|
32
|
+
if (!dialog || latest.current.open) return;
|
|
33
|
+
if (dialog.open) dialog.close();
|
|
34
|
+
setActive(false);
|
|
35
|
+
latest.current.onExited();
|
|
36
|
+
}, [
|
|
37
|
+
dialogRef
|
|
38
|
+
]);
|
|
39
|
+
useLayoutEffect(()=>{
|
|
40
|
+
const dialog = dialogRef.current;
|
|
41
|
+
const view = dialog?.ownerDocument.defaultView;
|
|
42
|
+
if (!dialog || !view) return;
|
|
43
|
+
let frame;
|
|
44
|
+
let timeout;
|
|
45
|
+
if (open) {
|
|
46
|
+
if (!dialog.open) {
|
|
47
|
+
dialog.dataset.state = 'closed';
|
|
48
|
+
dialog.showModal();
|
|
49
|
+
dialog.getBoundingClientRect();
|
|
50
|
+
}
|
|
51
|
+
setActive(true);
|
|
52
|
+
frame = view.requestAnimationFrame(()=>{
|
|
53
|
+
dialog.dataset.state = 'open';
|
|
54
|
+
});
|
|
55
|
+
} else if (dialog.open && 'open' === dialog.dataset.state) {
|
|
56
|
+
dialog.dataset.state = 'closed';
|
|
57
|
+
const duration = transitionTime(dialog);
|
|
58
|
+
if (0 === duration) finishClosing();
|
|
59
|
+
else timeout = view.setTimeout(finishClosing, duration + 50);
|
|
60
|
+
} else finishClosing();
|
|
61
|
+
return ()=>{
|
|
62
|
+
if (void 0 !== frame) view.cancelAnimationFrame(frame);
|
|
63
|
+
if (void 0 !== timeout) view.clearTimeout(timeout);
|
|
64
|
+
};
|
|
65
|
+
}, [
|
|
66
|
+
dialogRef,
|
|
67
|
+
finishClosing,
|
|
68
|
+
open
|
|
69
|
+
]);
|
|
70
|
+
useLayoutEffect(()=>{
|
|
71
|
+
const dialog = dialogRef.current;
|
|
72
|
+
return ()=>{
|
|
73
|
+
if (dialog?.open) dialog.close();
|
|
74
|
+
};
|
|
75
|
+
}, [
|
|
76
|
+
dialogRef
|
|
77
|
+
]);
|
|
78
|
+
return {
|
|
79
|
+
active,
|
|
80
|
+
finishClosing
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export { useDialogTransition };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { Sidepanel } from './sidepanel.js';
|
|
2
|
+
export type { SidepanelProps } from './types.js';
|
|
3
|
+
export { Header } from './components/header/index.js';
|
|
4
|
+
export type { HeaderProps } from './components/header/index.js';
|
|
5
|
+
export { Content } from './components/content/index.js';
|
|
6
|
+
export type { ContentProps } from './components/content/index.js';
|
|
7
|
+
export { Footer } from './components/footer/index.js';
|
|
8
|
+
export type { FooterProps } from './components/footer/index.js';
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
@import "../../styles/theme.css";
|
|
2
|
+
@import "./vars.css";
|
|
3
|
+
|
|
4
|
+
.component-kIdcz9 {
|
|
5
|
+
box-sizing: border-box;
|
|
6
|
+
width: 500px;
|
|
7
|
+
min-width: 0;
|
|
8
|
+
max-width: 100vw;
|
|
9
|
+
height: 100dvh;
|
|
10
|
+
max-height: none;
|
|
11
|
+
margin: var(--size-0);
|
|
12
|
+
padding: var(--size-0);
|
|
13
|
+
border-radius: var(--border-radius-0);
|
|
14
|
+
background: var(--sidepanel-background-color);
|
|
15
|
+
color: var(--sidepanel-color);
|
|
16
|
+
font-family: var(--font-primary);
|
|
17
|
+
transition: transform var(--sidepanel-animation-duration) ease-in-out;
|
|
18
|
+
border: 0;
|
|
19
|
+
outline: none;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
position: fixed;
|
|
22
|
+
inset: 0 0 0 auto;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
transform: translateX(100%);
|
|
25
|
+
|
|
26
|
+
&[open] {
|
|
27
|
+
display: flex;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&:has( > .nonStickyFooter-rXnHkK) {
|
|
31
|
+
overscroll-behavior: contain;
|
|
32
|
+
overflow: auto;
|
|
33
|
+
|
|
34
|
+
& > .header-UlW3Pm {
|
|
35
|
+
z-index: 1;
|
|
36
|
+
background: var(--sidepanel-background-color);
|
|
37
|
+
position: sticky;
|
|
38
|
+
top: 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
& > .content-RhY7Xk {
|
|
42
|
+
flex: 1 0 auto;
|
|
43
|
+
overflow: visible;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
&[data-state="open"] {
|
|
48
|
+
transform: translateX(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&::backdrop {
|
|
52
|
+
background: var(--sidepanel-backdrop-color);
|
|
53
|
+
backdrop-filter: blur(var(--sidepanel-backdrop-blur));
|
|
54
|
+
opacity: 0;
|
|
55
|
+
transition: opacity var(--sidepanel-animation-duration) ease-in-out;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
&[data-state="open"]::backdrop {
|
|
59
|
+
opacity: 1;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@media (prefers-reduced-motion: reduce) {
|
|
64
|
+
.component-kIdcz9, .component-kIdcz9::backdrop {
|
|
65
|
+
transition-duration: 0s;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
.scrollbar-haYNj_ {
|
|
2
|
+
scrollbar-color: auto;
|
|
3
|
+
scrollbar-width: auto;
|
|
4
|
+
|
|
5
|
+
&::-webkit-scrollbar {
|
|
6
|
+
width: var(--size-6);
|
|
7
|
+
height: var(--size-6);
|
|
8
|
+
background: none;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
&::-webkit-scrollbar-button {
|
|
12
|
+
width: 0;
|
|
13
|
+
height: 0;
|
|
14
|
+
display: none;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
&::-webkit-scrollbar-track {
|
|
18
|
+
background: none;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&::-webkit-scrollbar-track-piece {
|
|
22
|
+
background: none;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
&::-webkit-scrollbar-corner {
|
|
26
|
+
background: none;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&::-webkit-scrollbar-thumb {
|
|
30
|
+
border-radius: var(--border-radius-pill);
|
|
31
|
+
background: var(--sidepanel-scrollbar-color);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@supports not selector(::-webkit-scrollbar) {
|
|
36
|
+
.scrollbar-haYNj_ {
|
|
37
|
+
scrollbar-width: thin;
|
|
38
|
+
scrollbar-color: var(--sidepanel-scrollbar-color) transparent;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const Sidepanel: import("react").ForwardRefExoticComponent<Omit<import("react").DialogHTMLAttributes<HTMLDialogElement>, "open" | "onClose" | "onCancel"> & {
|
|
2
|
+
open: boolean;
|
|
3
|
+
onClose?: VoidFunction;
|
|
4
|
+
escapeKeyDown?: boolean;
|
|
5
|
+
backdropClick?: boolean;
|
|
6
|
+
lockScroll?: boolean;
|
|
7
|
+
getPortalContainer?: import("../Portal/index.js").PortalProps["getPortalContainer"];
|
|
8
|
+
dataTestId?: string;
|
|
9
|
+
} & import("react").RefAttributes<HTMLDialogElement>>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useCallback, useLayoutEffect, useState } from "react";
|
|
3
|
+
import { Portal } from "../Portal/index.js";
|
|
4
|
+
import { SidepanelSurface } from "./components/sidepanel-surface/index.js";
|
|
5
|
+
const Sidepanel = /*#__PURE__*/ forwardRef(({ open, getPortalContainer, ...restProps }, ref)=>{
|
|
6
|
+
const [mounted, setMounted] = useState(open);
|
|
7
|
+
useLayoutEffect(()=>{
|
|
8
|
+
if (open) setMounted(true);
|
|
9
|
+
}, [
|
|
10
|
+
open
|
|
11
|
+
]);
|
|
12
|
+
const handleExited = useCallback(()=>{
|
|
13
|
+
if (!open) setMounted(false);
|
|
14
|
+
}, [
|
|
15
|
+
open
|
|
16
|
+
]);
|
|
17
|
+
return mounted ? /*#__PURE__*/ jsx(Portal, {
|
|
18
|
+
getPortalContainer: getPortalContainer,
|
|
19
|
+
children: /*#__PURE__*/ jsx(SidepanelSurface, {
|
|
20
|
+
...restProps,
|
|
21
|
+
ref: ref,
|
|
22
|
+
open: open,
|
|
23
|
+
onExited: handleExited
|
|
24
|
+
})
|
|
25
|
+
}) : null;
|
|
26
|
+
});
|
|
27
|
+
Sidepanel.displayName = 'Sidepanel';
|
|
28
|
+
export { Sidepanel };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { DialogHTMLAttributes } from 'react';
|
|
2
|
+
import type { PortalProps } from '../Portal/index.js';
|
|
3
|
+
export type SidepanelProps = Omit<DialogHTMLAttributes<HTMLDialogElement>, 'open' | 'onClose' | 'onCancel'> & {
|
|
4
|
+
/**
|
|
5
|
+
* Видимость панели. Для закрытия обновите open в обработчике onClose.
|
|
6
|
+
* Панель остаётся открытой до завершения анимации ухода вправо.
|
|
7
|
+
*/
|
|
8
|
+
open: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Запрос закрытия по крестику, Escape или бекдропу.
|
|
11
|
+
*/
|
|
12
|
+
onClose?: VoidFunction;
|
|
13
|
+
/**
|
|
14
|
+
* Разрешает запрос закрытия по Escape.
|
|
15
|
+
* @default true
|
|
16
|
+
*/
|
|
17
|
+
escapeKeyDown?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Разрешает запрос закрытия по бекдропу.
|
|
20
|
+
* @default true
|
|
21
|
+
*/
|
|
22
|
+
backdropClick?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Блокирует прокрутку страницы до завершения закрытия.
|
|
25
|
+
* @default true
|
|
26
|
+
*/
|
|
27
|
+
lockScroll?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Контейнер портала. По умолчанию используется body текущего документа.
|
|
30
|
+
*/
|
|
31
|
+
getPortalContainer?: PortalProps['getPortalContainer'];
|
|
32
|
+
/**
|
|
33
|
+
* Идентификатор панели. Части получают суффиксы -header, -closer, -content и -footer.
|
|
34
|
+
*/
|
|
35
|
+
dataTestId?: string;
|
|
36
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
@import "../Modal/vars.css";
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--sidepanel-background-color: var(--modal-background-color);
|
|
5
|
+
--sidepanel-color: var(--modal-color);
|
|
6
|
+
--sidepanel-backdrop-color: var(--modal-backdrop-color);
|
|
7
|
+
--sidepanel-backdrop-blur: var(--modal-backdrop-blur);
|
|
8
|
+
--sidepanel-padding: var(--size-24);
|
|
9
|
+
--sidepanel-scrollbar-color: #a49b89;
|
|
10
|
+
--sidepanel-animation-duration: .2s;
|
|
11
|
+
}
|
|
12
|
+
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
--field-label-color: var(--input-disabled-color);
|
|
43
43
|
--field-placeholder-color: var(--input-disabled-color);
|
|
44
44
|
--field-icon-color: var(--input-disabled-color);
|
|
45
|
+
cursor: not-allowed;
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
|
|
@@ -64,13 +65,13 @@
|
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
.component-K9LhZN:not([data-disabled]) .field-GADSqb:focus-within {
|
|
68
|
+
.component-K9LhZN[data-has-outline]:not([data-disabled]) .field-GADSqb:focus-within {
|
|
68
69
|
box-shadow: none;
|
|
69
70
|
outline: 2px solid var(--input-focus-border-color);
|
|
70
71
|
outline-offset: 0;
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
.component-K9LhZN[data-error]:not([data-disabled]) .field-GADSqb:focus-within {
|
|
74
|
+
.component-K9LhZN[data-has-outline][data-error]:not([data-disabled]) .field-GADSqb:focus-within {
|
|
74
75
|
outline-color: var(--input-error-border-color);
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -6,6 +6,7 @@ export declare const Textarea: import("react").ForwardRefExoticComponent<Omit<im
|
|
|
6
6
|
showValueLength?: boolean;
|
|
7
7
|
view?: "primary" | "secondary";
|
|
8
8
|
block?: boolean;
|
|
9
|
+
hasOutline?: boolean;
|
|
9
10
|
label?: import("react").ReactNode;
|
|
10
11
|
labelView?: "outer" | "inner";
|
|
11
12
|
hint?: import("react").ReactNode;
|
|
@@ -7,7 +7,7 @@ import { Button } from "../Button/index.js";
|
|
|
7
7
|
import { TypographyText } from "../Typography/text.js";
|
|
8
8
|
import { useAutosize } from "./hooks/use-autosize.js";
|
|
9
9
|
import index_module from "./index.module.js";
|
|
10
|
-
const Textarea = /*#__PURE__*/ forwardRef(({ view = 'primary', block = false, label, labelView = 'outer', hint, error, minRows = 3, maxRows, autosize = false, resize = false, clear = false, value, defaultValue, maxLength, showValueLength = false, onChange, onClear, disabled = false, readOnly = false, id, className, style, fieldClassName, textareaClassName, labelClassName, messageClassName, dataTestId, ...restProps }, ref)=>{
|
|
10
|
+
const Textarea = /*#__PURE__*/ forwardRef(({ view = 'primary', block = false, hasOutline = false, label, labelView = 'outer', hint, error, minRows = 3, maxRows, autosize = false, resize = false, clear = false, value, defaultValue, maxLength, showValueLength = false, onChange, onClear, disabled = false, readOnly = false, id, className, style, fieldClassName, textareaClassName, labelClassName, messageClassName, dataTestId, ...restProps }, ref)=>{
|
|
11
11
|
const generatedId = useId();
|
|
12
12
|
const textareaId = id ?? generatedId;
|
|
13
13
|
const textareaRef = useRef(null);
|
|
@@ -96,6 +96,7 @@ const Textarea = /*#__PURE__*/ forwardRef(({ view = 'primary', block = false, la
|
|
|
96
96
|
},
|
|
97
97
|
"data-view": view,
|
|
98
98
|
"data-block": block || void 0,
|
|
99
|
+
"data-has-outline": hasOutline || void 0,
|
|
99
100
|
"data-disabled": disabled || void 0,
|
|
100
101
|
"data-read-only": readOnly || void 0,
|
|
101
102
|
"data-error": Boolean(error) || void 0,
|
|
@@ -28,6 +28,11 @@ export type TextareaProps = Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'v
|
|
|
28
28
|
* @default false
|
|
29
29
|
*/
|
|
30
30
|
block?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Показывает outline поля при фокусе.
|
|
33
|
+
* @default false
|
|
34
|
+
*/
|
|
35
|
+
hasOutline?: boolean;
|
|
31
36
|
/**
|
|
32
37
|
* Название поля. Клик переводит фокус в textarea.
|
|
33
38
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -25,4 +25,6 @@ export type { CollapseProps } from './components/Collapse/index.js';
|
|
|
25
25
|
export { Modal, ModalComponent, ModalHeader, ModalContent, ModalFooter, ModalControls, } from './components/Modal/index.js';
|
|
26
26
|
export { Portal } from './components/Portal/index.js';
|
|
27
27
|
export type { PortalProps } from './components/Portal/index.js';
|
|
28
|
+
export { Sidepanel, Header as SidepanelHeader, Content as SidepanelContent, Footer as SidepanelFooter, } from './components/Sidepanel/index.js';
|
|
29
|
+
export type { SidepanelProps, HeaderProps as SidepanelHeaderProps, ContentProps as SidepanelContentProps, FooterProps as SidepanelFooterProps, } from './components/Sidepanel/index.js';
|
|
28
30
|
export type { ModalProps, HeaderProps as ModalHeaderProps, ContentProps as ModalContentProps, FooterProps as ModalFooterProps, ControlsProps as ModalControlsProps, } from './components/Modal/index.js';
|
package/dist/index.js
CHANGED
|
@@ -12,3 +12,4 @@ export { Popover } from "./components/Popover/index.js";
|
|
|
12
12
|
export { Collapse } from "./components/Collapse/index.js";
|
|
13
13
|
export { Modal, ModalComponent, ModalContent, ModalControls, ModalFooter, ModalHeader } from "./components/Modal/index.js";
|
|
14
14
|
export { Portal } from "./components/Portal/index.js";
|
|
15
|
+
export { Content as SidepanelContent, Footer as SidepanelFooter, Header as SidepanelHeader, Sidepanel } from "./components/Sidepanel/index.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function lockScroll(document: Document, removeScrollbarGutter?: boolean): () => void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const locks = new WeakMap();
|
|
2
|
+
function lockScroll(document, removeScrollbarGutter = false) {
|
|
3
|
+
const { body, documentElement, defaultView } = document;
|
|
4
|
+
const existing = locks.get(document);
|
|
5
|
+
if (existing) existing.count += 1;
|
|
6
|
+
else {
|
|
7
|
+
locks.set(document, {
|
|
8
|
+
count: 1,
|
|
9
|
+
overflow: body.style.overflow,
|
|
10
|
+
paddingRight: body.style.paddingRight,
|
|
11
|
+
paddingLeft: body.style.paddingLeft
|
|
12
|
+
});
|
|
13
|
+
const scrollbarWidth = (defaultView?.innerWidth ?? 0) - documentElement.clientWidth;
|
|
14
|
+
const hasStableGutter = defaultView?.getComputedStyle(documentElement).scrollbarGutter?.split(/\s+/).includes('stable');
|
|
15
|
+
if (scrollbarWidth > 0 && defaultView && !hasStableGutter) {
|
|
16
|
+
const paddingRight = parseFloat(defaultView.getComputedStyle(body).paddingRight) || 0;
|
|
17
|
+
body.style.paddingRight = `${paddingRight + scrollbarWidth}px`;
|
|
18
|
+
}
|
|
19
|
+
body.style.overflow = 'hidden';
|
|
20
|
+
}
|
|
21
|
+
const gutterLock = locks.get(document);
|
|
22
|
+
if (removeScrollbarGutter && !gutterLock.gutter && defaultView?.getComputedStyle(documentElement).scrollbarGutter?.includes('stable')) {
|
|
23
|
+
const before = documentElement.getBoundingClientRect();
|
|
24
|
+
const paddingLeft = parseFloat(defaultView.getComputedStyle(body).paddingLeft) || 0;
|
|
25
|
+
const paddingRight = parseFloat(defaultView.getComputedStyle(body).paddingRight) || 0;
|
|
26
|
+
gutterLock.gutter = {
|
|
27
|
+
value: documentElement.style.getPropertyValue('scrollbar-gutter'),
|
|
28
|
+
priority: documentElement.style.getPropertyPriority('scrollbar-gutter')
|
|
29
|
+
};
|
|
30
|
+
documentElement.style.setProperty('scrollbar-gutter', 'auto', 'important');
|
|
31
|
+
const after = documentElement.getBoundingClientRect();
|
|
32
|
+
body.style.paddingLeft = `${paddingLeft + Math.max(0, before.left - after.left)}px`;
|
|
33
|
+
body.style.paddingRight = `${paddingRight + Math.max(0, after.right - before.right)}px`;
|
|
34
|
+
}
|
|
35
|
+
return ()=>{
|
|
36
|
+
const lock = locks.get(document);
|
|
37
|
+
if (!lock) return;
|
|
38
|
+
lock.count -= 1;
|
|
39
|
+
if (0 === lock.count) {
|
|
40
|
+
body.style.overflow = lock.overflow;
|
|
41
|
+
body.style.paddingRight = lock.paddingRight;
|
|
42
|
+
body.style.paddingLeft = lock.paddingLeft;
|
|
43
|
+
if (lock.gutter) if (lock.gutter.value) documentElement.style.setProperty('scrollbar-gutter', lock.gutter.value, lock.gutter.priority);
|
|
44
|
+
else documentElement.style.removeProperty('scrollbar-gutter');
|
|
45
|
+
locks.delete(document);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export { lockScroll };
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function lockScroll(document: Document): () => void;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
const locks = new WeakMap();
|
|
2
|
-
function lockScroll(document) {
|
|
3
|
-
const { body, documentElement, defaultView } = document;
|
|
4
|
-
const existing = locks.get(document);
|
|
5
|
-
if (existing) existing.count += 1;
|
|
6
|
-
else {
|
|
7
|
-
locks.set(document, {
|
|
8
|
-
count: 1,
|
|
9
|
-
overflow: body.style.overflow,
|
|
10
|
-
paddingRight: body.style.paddingRight
|
|
11
|
-
});
|
|
12
|
-
const scrollbarWidth = (defaultView?.innerWidth ?? 0) - documentElement.clientWidth;
|
|
13
|
-
const hasStableGutter = defaultView?.getComputedStyle(documentElement).scrollbarGutter?.split(/\s+/).includes('stable');
|
|
14
|
-
if (scrollbarWidth > 0 && defaultView && !hasStableGutter) {
|
|
15
|
-
const paddingRight = parseFloat(defaultView.getComputedStyle(body).paddingRight) || 0;
|
|
16
|
-
body.style.paddingRight = `${paddingRight + scrollbarWidth}px`;
|
|
17
|
-
}
|
|
18
|
-
body.style.overflow = 'hidden';
|
|
19
|
-
}
|
|
20
|
-
return ()=>{
|
|
21
|
-
const lock = locks.get(document);
|
|
22
|
-
if (!lock) return;
|
|
23
|
-
lock.count -= 1;
|
|
24
|
-
if (0 === lock.count) {
|
|
25
|
-
body.style.overflow = lock.overflow;
|
|
26
|
-
body.style.paddingRight = lock.paddingRight;
|
|
27
|
-
locks.delete(document);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
export { lockScroll };
|