@koslibs/components 1.2.0 → 1.3.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/Collapse/collapse.d.ts +91 -0
- package/dist/components/Collapse/collapse.js +103 -0
- package/dist/components/Collapse/index.d.ts +2 -0
- package/dist/components/Collapse/index.js +1 -0
- package/dist/components/Collapse/index.module.js +12 -0
- package/dist/components/Collapse/index_module.css +82 -0
- package/dist/components/Collapse/vars.css +6 -0
- package/dist/components/Popover/types.d.ts +3 -1
- package/dist/components/Typography/shared.d.ts +9 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @koslibs/components
|
|
2
2
|
|
|
3
|
+
## 1.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#4](https://github.com/koslibs/components/pull/4) [`b37f3b0`](https://github.com/koslibs/components/commit/b37f3b008be8ffea80908557c13ea77d6d55c792) Thanks [@holypower777](https://github.com/holypower777)! - Перенесён Collapse из core-components GullEye с отдельным импортом
|
|
8
|
+
`@koslibs/components/Collapse`, внутренним и внешним управлением, сохранением
|
|
9
|
+
содержимого, анимацией и поддержкой reduced motion. Проп `colors` убран:
|
|
10
|
+
оформление кнопки настраивается через CSS-переменные. Добавлены документация
|
|
11
|
+
с тремя вкладками, Playground и история компонента. Стрелка подключается
|
|
12
|
+
отдельным импортом из `@koslibs/icons`.
|
|
13
|
+
|
|
14
|
+
Подписи `collapsedLabel` и `expandedLabel` не имеют дефолтных значений.
|
|
15
|
+
Добавлены `showRightChevron` (по умолчанию `true`) и `showLeftChevron`
|
|
16
|
+
(по умолчанию `false`): можно независимо показывать шевроны или скрыть оба.
|
|
17
|
+
Левый шеврон смотрит вправо в закрытом состоянии и вниз в раскрытом,
|
|
18
|
+
правый сохраняет поворот вниз → вверх.
|
|
19
|
+
|
|
20
|
+
Комментарии публичных пропсов приведены к единому многострочному формату JSDoc
|
|
21
|
+
с отдельной строкой для `@default`.
|
|
22
|
+
|
|
3
23
|
## 1.2.0
|
|
4
24
|
|
|
5
25
|
### Minor Changes
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from 'react';
|
|
2
|
+
export type CollapseProps = Omit<HTMLAttributes<HTMLDivElement>, 'onTransitionEnd'> & {
|
|
3
|
+
/**
|
|
4
|
+
* Состояние controlled-компонента.
|
|
5
|
+
*/
|
|
6
|
+
expanded?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Начальное состояние uncontrolled-компонента.
|
|
9
|
+
* @default false
|
|
10
|
+
*/
|
|
11
|
+
defaultExpanded?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Подпись кнопки, когда содержимое скрыто.
|
|
14
|
+
*/
|
|
15
|
+
collapsedLabel?: ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* Подпись кнопки, когда содержимое раскрыто.
|
|
18
|
+
*/
|
|
19
|
+
expandedLabel?: ReactNode;
|
|
20
|
+
/**
|
|
21
|
+
* Показывать шеврон справа от подписи: вниз при закрытии, вверх при раскрытии.
|
|
22
|
+
* @default true
|
|
23
|
+
*/
|
|
24
|
+
showRightChevron?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Показывать шеврон слева от подписи: вправо при закрытии, вниз при раскрытии.
|
|
27
|
+
* @default false
|
|
28
|
+
*/
|
|
29
|
+
showLeftChevron?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Запрос изменения состояния. В controlled-режиме обновите expanded.
|
|
32
|
+
*/
|
|
33
|
+
onExpandedChange?: (expanded: boolean) => void;
|
|
34
|
+
/**
|
|
35
|
+
* Завершение раскрытия/сворачивания; без анимации вызывается сразу.
|
|
36
|
+
*/
|
|
37
|
+
onTransitionEnd?: (expanded: boolean) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Дополнительный класс внутреннего контейнера содержимого.
|
|
40
|
+
*/
|
|
41
|
+
expandedContentClassName?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Идентификатор корневого элемента для автоматизированных тестов.
|
|
44
|
+
*/
|
|
45
|
+
dataTestId?: string;
|
|
46
|
+
};
|
|
47
|
+
export declare const Collapse: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "onTransitionEnd"> & {
|
|
48
|
+
/**
|
|
49
|
+
* Состояние controlled-компонента.
|
|
50
|
+
*/
|
|
51
|
+
expanded?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Начальное состояние uncontrolled-компонента.
|
|
54
|
+
* @default false
|
|
55
|
+
*/
|
|
56
|
+
defaultExpanded?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Подпись кнопки, когда содержимое скрыто.
|
|
59
|
+
*/
|
|
60
|
+
collapsedLabel?: ReactNode;
|
|
61
|
+
/**
|
|
62
|
+
* Подпись кнопки, когда содержимое раскрыто.
|
|
63
|
+
*/
|
|
64
|
+
expandedLabel?: ReactNode;
|
|
65
|
+
/**
|
|
66
|
+
* Показывать шеврон справа от подписи: вниз при закрытии, вверх при раскрытии.
|
|
67
|
+
* @default true
|
|
68
|
+
*/
|
|
69
|
+
showRightChevron?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Показывать шеврон слева от подписи: вправо при закрытии, вниз при раскрытии.
|
|
72
|
+
* @default false
|
|
73
|
+
*/
|
|
74
|
+
showLeftChevron?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Запрос изменения состояния. В controlled-режиме обновите expanded.
|
|
77
|
+
*/
|
|
78
|
+
onExpandedChange?: (expanded: boolean) => void;
|
|
79
|
+
/**
|
|
80
|
+
* Завершение раскрытия/сворачивания; без анимации вызывается сразу.
|
|
81
|
+
*/
|
|
82
|
+
onTransitionEnd?: (expanded: boolean) => void;
|
|
83
|
+
/**
|
|
84
|
+
* Дополнительный класс внутреннего контейнера содержимого.
|
|
85
|
+
*/
|
|
86
|
+
expandedContentClassName?: string;
|
|
87
|
+
/**
|
|
88
|
+
* Идентификатор корневого элемента для автоматизированных тестов.
|
|
89
|
+
*/
|
|
90
|
+
dataTestId?: string;
|
|
91
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronDownIcon } from "@koslibs/icons/ChevronDownIcon";
|
|
3
|
+
import classnames from "classnames";
|
|
4
|
+
import { forwardRef, useId, useLayoutEffect, useRef, useState } from "react";
|
|
5
|
+
import index_module from "./index.module.js";
|
|
6
|
+
const Collapse = /*#__PURE__*/ forwardRef(({ expanded, defaultExpanded = false, collapsedLabel, expandedLabel, showRightChevron = true, showLeftChevron = false, onExpandedChange, onTransitionEnd, expandedContentClassName, dataTestId, className, children, ...restProps }, ref)=>{
|
|
7
|
+
const [expandedState, setExpandedState] = useState(defaultExpanded);
|
|
8
|
+
const isExpanded = expanded ?? expandedState;
|
|
9
|
+
const contentId = `collapse-${useId()}`;
|
|
10
|
+
const contentRef = useRef(null);
|
|
11
|
+
const innerRef = useRef(null);
|
|
12
|
+
const toggleRef = useRef(null);
|
|
13
|
+
const previousExpanded = useRef(isExpanded);
|
|
14
|
+
const pendingTransition = useRef(false);
|
|
15
|
+
const transitionCallback = useRef(onTransitionEnd);
|
|
16
|
+
const label = isExpanded ? expandedLabel || collapsedLabel : collapsedLabel || expandedLabel;
|
|
17
|
+
useLayoutEffect(()=>{
|
|
18
|
+
transitionCallback.current = onTransitionEnd;
|
|
19
|
+
}, [
|
|
20
|
+
onTransitionEnd
|
|
21
|
+
]);
|
|
22
|
+
useLayoutEffect(()=>{
|
|
23
|
+
const content = contentRef.current;
|
|
24
|
+
if (!content) return;
|
|
25
|
+
if (!isExpanded && content.contains(content.ownerDocument.activeElement)) toggleRef.current?.focus();
|
|
26
|
+
content.inert = !isExpanded;
|
|
27
|
+
if (previousExpanded.current === isExpanded) return;
|
|
28
|
+
previousExpanded.current = isExpanded;
|
|
29
|
+
pendingTransition.current = true;
|
|
30
|
+
const transitionDuration = content.ownerDocument.defaultView?.getComputedStyle(content).transitionDuration;
|
|
31
|
+
if (innerRef.current?.scrollHeight === 0 || 0 === parseFloat(transitionDuration ?? '0')) {
|
|
32
|
+
pendingTransition.current = false;
|
|
33
|
+
transitionCallback.current?.(isExpanded);
|
|
34
|
+
}
|
|
35
|
+
}, [
|
|
36
|
+
isExpanded
|
|
37
|
+
]);
|
|
38
|
+
const handleToggle = ()=>{
|
|
39
|
+
const nextExpanded = !isExpanded;
|
|
40
|
+
if (void 0 === expanded) setExpandedState(nextExpanded);
|
|
41
|
+
onExpandedChange?.(nextExpanded);
|
|
42
|
+
};
|
|
43
|
+
const handleTransitionEnd = (event)=>{
|
|
44
|
+
if (event.target === event.currentTarget && 'grid-template-rows' === event.propertyName && pendingTransition.current) {
|
|
45
|
+
pendingTransition.current = false;
|
|
46
|
+
transitionCallback.current?.(isExpanded);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
50
|
+
...restProps,
|
|
51
|
+
ref: ref,
|
|
52
|
+
className: classnames(index_module.component, className),
|
|
53
|
+
"data-test-id": dataTestId,
|
|
54
|
+
children: [
|
|
55
|
+
label && /*#__PURE__*/ jsxs("button", {
|
|
56
|
+
ref: toggleRef,
|
|
57
|
+
type: "button",
|
|
58
|
+
className: index_module.toggle,
|
|
59
|
+
"aria-expanded": isExpanded,
|
|
60
|
+
"aria-controls": contentId,
|
|
61
|
+
onClick: handleToggle,
|
|
62
|
+
"data-test-id": dataTestId ? `${dataTestId}-toggle` : void 0,
|
|
63
|
+
children: [
|
|
64
|
+
showLeftChevron && /*#__PURE__*/ jsx(ChevronDownIcon, {
|
|
65
|
+
className: classnames(index_module.icon, index_module.leftIcon, {
|
|
66
|
+
[index_module.rotated]: isExpanded
|
|
67
|
+
}),
|
|
68
|
+
"aria-hidden": "true"
|
|
69
|
+
}),
|
|
70
|
+
/*#__PURE__*/ jsx("span", {
|
|
71
|
+
children: label
|
|
72
|
+
}),
|
|
73
|
+
showRightChevron && /*#__PURE__*/ jsx(ChevronDownIcon, {
|
|
74
|
+
className: classnames(index_module.icon, {
|
|
75
|
+
[index_module.rotated]: isExpanded
|
|
76
|
+
}),
|
|
77
|
+
"aria-hidden": "true"
|
|
78
|
+
})
|
|
79
|
+
]
|
|
80
|
+
}),
|
|
81
|
+
/*#__PURE__*/ jsx("div", {
|
|
82
|
+
ref: contentRef,
|
|
83
|
+
id: contentId,
|
|
84
|
+
className: classnames(index_module.content, {
|
|
85
|
+
[index_module.expanded]: isExpanded
|
|
86
|
+
}),
|
|
87
|
+
"aria-hidden": !isExpanded,
|
|
88
|
+
"data-test-id": dataTestId ? `${dataTestId}-content` : void 0,
|
|
89
|
+
onTransitionEnd: handleTransitionEnd,
|
|
90
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
91
|
+
className: index_module.clip,
|
|
92
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
93
|
+
ref: innerRef,
|
|
94
|
+
className: expandedContentClassName,
|
|
95
|
+
children: children
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
]
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
Collapse.displayName = 'Collapse';
|
|
103
|
+
export { Collapse };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Collapse } from "./collapse.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import "./index_module.css";
|
|
2
|
+
const index_module = {
|
|
3
|
+
component: "component-tPiInR",
|
|
4
|
+
content: "content-D8E_z9",
|
|
5
|
+
expanded: "expanded-rqGMOG",
|
|
6
|
+
clip: "clip-D3ydtQ",
|
|
7
|
+
toggle: "toggle-YA8uJB",
|
|
8
|
+
icon: "icon-wFeaQN",
|
|
9
|
+
rotated: "rotated-sRw5l2",
|
|
10
|
+
leftIcon: "leftIcon-PGZrVM"
|
|
11
|
+
};
|
|
12
|
+
export default index_module;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
@import "../../styles/theme.css";
|
|
2
|
+
@import "./vars.css";
|
|
3
|
+
|
|
4
|
+
.component-tPiInR {
|
|
5
|
+
min-width: 0;
|
|
6
|
+
font-family: var(--font-primary);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.content-D8E_z9 {
|
|
10
|
+
visibility: hidden;
|
|
11
|
+
grid-template-rows: 0fr;
|
|
12
|
+
transition: grid-template-rows .2s, visibility 0s linear .2s;
|
|
13
|
+
display: grid;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.expanded-rqGMOG {
|
|
17
|
+
visibility: visible;
|
|
18
|
+
grid-template-rows: 1fr;
|
|
19
|
+
transition-delay: 0s;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.clip-D3ydtQ {
|
|
23
|
+
min-height: 0;
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.toggle-YA8uJB {
|
|
28
|
+
align-items: center;
|
|
29
|
+
gap: var(--size-4, 4px);
|
|
30
|
+
max-width: 100%;
|
|
31
|
+
padding: var(--size-8, 8px) 0;
|
|
32
|
+
color: var(--collapse-color);
|
|
33
|
+
font: inherit;
|
|
34
|
+
text-align: left;
|
|
35
|
+
overflow-wrap: anywhere;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
background: none;
|
|
38
|
+
border: 0;
|
|
39
|
+
margin: 0;
|
|
40
|
+
font-size: 13px;
|
|
41
|
+
line-height: 20px;
|
|
42
|
+
display: inline-flex;
|
|
43
|
+
|
|
44
|
+
&:hover {
|
|
45
|
+
color: var(--collapse-hover-color);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
&:hover span {
|
|
49
|
+
text-underline-offset: 3px;
|
|
50
|
+
text-decoration: underline;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&:focus-visible {
|
|
54
|
+
outline: 2px solid var(--collapse-focus-color);
|
|
55
|
+
outline-offset: 2px;
|
|
56
|
+
border-radius: var(--border-radius-4, 4px);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.icon-wFeaQN {
|
|
61
|
+
flex-shrink: 0;
|
|
62
|
+
transition: transform .2s;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.rotated-sRw5l2 {
|
|
66
|
+
transform: rotate(180deg);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.leftIcon-PGZrVM {
|
|
70
|
+
transform: rotate(-90deg);
|
|
71
|
+
|
|
72
|
+
&.rotated-sRw5l2 {
|
|
73
|
+
transform: rotate(0);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@media (prefers-reduced-motion: reduce) {
|
|
78
|
+
.content-D8E_z9, .icon-wFeaQN {
|
|
79
|
+
transition: none;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
@@ -2,7 +2,9 @@ import type { Placement } from '@popperjs/core';
|
|
|
2
2
|
import type { CSSProperties, HTMLAttributes, ReactNode } from 'react';
|
|
3
3
|
export type PopoverPosition = Exclude<Placement, 'auto' | 'auto-start' | 'auto-end'>;
|
|
4
4
|
export type PopoverProps = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'className' | 'style'> & {
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Содержимое поповера. Оформление и прокрутка задаются вызывающим компонентом.
|
|
7
|
+
*/
|
|
6
8
|
children?: ReactNode;
|
|
7
9
|
/**
|
|
8
10
|
* Дополнительный класс внешнего контейнера.
|
|
@@ -2,11 +2,17 @@ import type { CSSProperties, HTMLAttributes, ReactNode } from 'react';
|
|
|
2
2
|
export type TypographyWeight = 'regular' | 'medium' | 'semibold' | 'bold';
|
|
3
3
|
export type TypographyColor = 'inherit' | 'primary' | 'secondary' | 'inverse' | 'positive' | 'negative';
|
|
4
4
|
export type BaseProps = Omit<HTMLAttributes<HTMLElement>, 'color' | 'children' | 'className' | 'style'> & {
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Содержимое текста или заголовка.
|
|
7
|
+
*/
|
|
6
8
|
children?: ReactNode;
|
|
7
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Дополнительный CSS-класс корневого элемента.
|
|
11
|
+
*/
|
|
8
12
|
className?: string;
|
|
9
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Инлайн-стили корневого элемента.
|
|
15
|
+
*/
|
|
10
16
|
style?: CSSProperties;
|
|
11
17
|
/**
|
|
12
18
|
* Цвет текста. По умолчанию наследуется от родителя.
|
package/dist/index.d.ts
CHANGED
|
@@ -6,3 +6,5 @@ export { Typography, TypographyText, TypographyTitle, Text, Title } from './comp
|
|
|
6
6
|
export type { TextProps, TitleProps, TypographyColor, TypographyWeight, } from './components/Typography/index.js';
|
|
7
7
|
export { Popover } from './components/Popover/index.js';
|
|
8
8
|
export type { PopoverProps, PopoverPosition } from './components/Popover/index.js';
|
|
9
|
+
export { Collapse } from './components/Collapse/index.js';
|
|
10
|
+
export type { CollapseProps } from './components/Collapse/index.js';
|
package/dist/index.js
CHANGED
|
@@ -2,3 +2,4 @@ export { Button } from "./components/Button/index.js";
|
|
|
2
2
|
export { Spinner } from "./components/Spinner/index.js";
|
|
3
3
|
export { Text, Title, Typography, TypographyText, TypographyTitle } from "./components/Typography/index.js";
|
|
4
4
|
export { Popover } from "./components/Popover/index.js";
|
|
5
|
+
export { Collapse } from "./components/Collapse/index.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koslibs/components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Shared React components for koslibs projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -80,6 +80,7 @@
|
|
|
80
80
|
"react-dom": "^18.3.1 || ^19.0.0"
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
|
+
"@koslibs/icons": "1.0.1",
|
|
83
84
|
"@popperjs/core": "2.11.8",
|
|
84
85
|
"classnames": "2.5.1"
|
|
85
86
|
}
|