@cloud-ru/ds-accordion 1.0.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE +201 -0
  3. package/README.md +484 -0
  4. package/dist/cjs/Accordion.d.ts +160 -0
  5. package/dist/cjs/Accordion.js +21 -0
  6. package/dist/cjs/components/CollapseBlock/CollapseBlock.d.ts +45 -0
  7. package/dist/cjs/components/CollapseBlock/CollapseBlock.js +47 -0
  8. package/dist/cjs/components/CollapseBlock/hooks.d.ts +12 -0
  9. package/dist/cjs/components/CollapseBlock/hooks.js +43 -0
  10. package/dist/cjs/components/CollapseBlock/index.d.ts +1 -0
  11. package/dist/cjs/components/CollapseBlock/index.js +17 -0
  12. package/dist/cjs/components/CollapseBlock/styles.module.css +480 -0
  13. package/dist/cjs/components/index.d.ts +1 -0
  14. package/dist/cjs/components/index.js +17 -0
  15. package/dist/cjs/constants.d.ts +22 -0
  16. package/dist/cjs/constants.js +25 -0
  17. package/dist/cjs/index.d.ts +4 -0
  18. package/dist/cjs/index.js +20 -0
  19. package/dist/cjs/package.json +3 -0
  20. package/dist/cjs/style.css +480 -0
  21. package/dist/cjs/types.d.ts +5 -0
  22. package/dist/cjs/types.js +2 -0
  23. package/dist/esm/Accordion.d.ts +160 -0
  24. package/dist/esm/Accordion.js +18 -0
  25. package/dist/esm/components/CollapseBlock/CollapseBlock.d.ts +45 -0
  26. package/dist/esm/components/CollapseBlock/CollapseBlock.js +40 -0
  27. package/dist/esm/components/CollapseBlock/hooks.d.ts +12 -0
  28. package/dist/esm/components/CollapseBlock/hooks.js +40 -0
  29. package/dist/esm/components/CollapseBlock/index.d.ts +1 -0
  30. package/dist/esm/components/CollapseBlock/index.js +1 -0
  31. package/dist/esm/components/CollapseBlock/styles.module.css +480 -0
  32. package/dist/esm/components/index.d.ts +1 -0
  33. package/dist/esm/components/index.js +1 -0
  34. package/dist/esm/constants.d.ts +22 -0
  35. package/dist/esm/constants.js +22 -0
  36. package/dist/esm/index.d.ts +4 -0
  37. package/dist/esm/index.js +4 -0
  38. package/dist/esm/style.css +480 -0
  39. package/dist/esm/types.d.ts +5 -0
  40. package/dist/esm/types.js +1 -0
  41. package/dist/tsconfig.cjs.tsbuildinfo +1 -0
  42. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  43. package/package.json +49 -0
  44. package/src/Accordion.tsx +51 -0
  45. package/src/components/CollapseBlock/CollapseBlock.tsx +194 -0
  46. package/src/components/CollapseBlock/hooks.ts +49 -0
  47. package/src/components/CollapseBlock/index.ts +1 -0
  48. package/src/components/CollapseBlock/styles.module.scss +207 -0
  49. package/src/components/index.ts +1 -0
  50. package/src/constants.ts +26 -0
  51. package/src/index.ts +4 -0
  52. package/src/types.ts +9 -0
@@ -0,0 +1,160 @@
1
+ import { SELECTION_MODE } from '@cloud-ru/ds-toggles';
2
+ import { PropsWithChildren } from 'react';
3
+ export type AccordionProps = PropsWithChildren<{
4
+ /** Начальное состояние */
5
+ expandedDefault?: string;
6
+ /** Controlled состояние */
7
+ expanded?: string;
8
+ /** Controlled обработчик измения состояния */
9
+ onExpandedChange?(value: (string | undefined) | string): void;
10
+ /** Режим работы аккордиона */
11
+ selectionMode?: typeof SELECTION_MODE.Single;
12
+ } | {
13
+ /** Начальное состояние */
14
+ expandedDefault?: string[];
15
+ /** Controlled состояние */
16
+ expanded?: string[];
17
+ /** Controlled обработчик измения состояния */
18
+ onExpandedChange?(value: (string[] | undefined) | string[]): void;
19
+ /** Режим работы аккордиона */
20
+ selectionMode: typeof SELECTION_MODE.Multiple;
21
+ }>;
22
+ declare function AccordionBase({ selectionMode, onExpandedChange, expandedDefault, expanded, children, }: AccordionProps): import("react/jsx-runtime").JSX.Element;
23
+ export declare const Accordion: typeof AccordionBase & {
24
+ CollapseBlockPrimary: (props: {
25
+ id: string;
26
+ title?: string | undefined;
27
+ afterTitle?: import("react").ReactNode;
28
+ subTitle?: import("react").ReactNode;
29
+ children?: import("react").ReactNode;
30
+ className?: string | undefined;
31
+ view?: import("./types").View | undefined;
32
+ chevronPosition?: import("./types").ChevronPosition | undefined;
33
+ backgroundPredefined?: import("@cloud-ru/ds-materials").BackgroundPredefinedFill | undefined;
34
+ keepMounted?: boolean | undefined;
35
+ showChevron?: boolean | undefined;
36
+ 'data-test-id'?: string | undefined;
37
+ "aria-activedescendant"?: string | undefined | undefined;
38
+ "aria-atomic"?: (boolean | "true" | "false") | undefined;
39
+ "aria-autocomplete"?: "none" | "inline" | "list" | "both" | undefined | undefined;
40
+ "aria-braillelabel"?: string | undefined | undefined;
41
+ "aria-brailleroledescription"?: string | undefined | undefined;
42
+ "aria-busy"?: (boolean | "true" | "false") | undefined;
43
+ "aria-checked"?: boolean | "false" | "mixed" | "true" | undefined | undefined;
44
+ "aria-colcount"?: number | undefined | undefined;
45
+ "aria-colindex"?: number | undefined | undefined;
46
+ "aria-colindextext"?: string | undefined | undefined;
47
+ "aria-colspan"?: number | undefined | undefined;
48
+ "aria-controls"?: string | undefined | undefined;
49
+ "aria-current"?: boolean | "false" | "true" | "page" | "step" | "location" | "date" | "time" | undefined | undefined;
50
+ "aria-describedby"?: string | undefined | undefined;
51
+ "aria-description"?: string | undefined | undefined;
52
+ "aria-details"?: string | undefined | undefined;
53
+ "aria-disabled"?: (boolean | "true" | "false") | undefined;
54
+ "aria-dropeffect"?: "none" | "copy" | "execute" | "link" | "move" | "popup" | undefined | undefined;
55
+ "aria-errormessage"?: string | undefined | undefined;
56
+ "aria-expanded"?: (boolean | "true" | "false") | undefined;
57
+ "aria-flowto"?: string | undefined | undefined;
58
+ "aria-grabbed"?: (boolean | "true" | "false") | undefined;
59
+ "aria-haspopup"?: boolean | "false" | "true" | "menu" | "listbox" | "tree" | "grid" | "dialog" | undefined | undefined;
60
+ "aria-hidden"?: (boolean | "true" | "false") | undefined;
61
+ "aria-invalid"?: boolean | "false" | "true" | "grammar" | "spelling" | undefined | undefined;
62
+ "aria-keyshortcuts"?: string | undefined | undefined;
63
+ "aria-label"?: string | undefined | undefined;
64
+ "aria-labelledby"?: string | undefined | undefined;
65
+ "aria-level"?: number | undefined | undefined;
66
+ "aria-live"?: "off" | "assertive" | "polite" | undefined | undefined;
67
+ "aria-modal"?: (boolean | "true" | "false") | undefined;
68
+ "aria-multiline"?: (boolean | "true" | "false") | undefined;
69
+ "aria-multiselectable"?: (boolean | "true" | "false") | undefined;
70
+ "aria-orientation"?: "horizontal" | "vertical" | undefined | undefined;
71
+ "aria-owns"?: string | undefined | undefined;
72
+ "aria-placeholder"?: string | undefined | undefined;
73
+ "aria-posinset"?: number | undefined | undefined;
74
+ "aria-pressed"?: boolean | "false" | "mixed" | "true" | undefined | undefined;
75
+ "aria-readonly"?: (boolean | "true" | "false") | undefined;
76
+ "aria-relevant"?: "additions" | "additions removals" | "additions text" | "all" | "removals" | "removals additions" | "removals text" | "text" | "text additions" | "text removals" | undefined | undefined;
77
+ "aria-required"?: (boolean | "true" | "false") | undefined;
78
+ "aria-roledescription"?: string | undefined | undefined;
79
+ "aria-rowcount"?: number | undefined | undefined;
80
+ "aria-rowindex"?: number | undefined | undefined;
81
+ "aria-rowindextext"?: string | undefined | undefined;
82
+ "aria-rowspan"?: number | undefined | undefined;
83
+ "aria-selected"?: (boolean | "true" | "false") | undefined;
84
+ "aria-setsize"?: number | undefined | undefined;
85
+ "aria-sort"?: "none" | "ascending" | "descending" | "other" | undefined | undefined;
86
+ "aria-valuemax"?: number | undefined | undefined;
87
+ "aria-valuemin"?: number | undefined | undefined;
88
+ "aria-valuenow"?: number | undefined | undefined;
89
+ "aria-valuetext"?: string | undefined | undefined;
90
+ }) => import("react/jsx-runtime").JSX.Element;
91
+ CollapseBlockSecondary: (props: {
92
+ id: string;
93
+ title?: string | undefined;
94
+ afterTitle?: import("react").ReactNode;
95
+ subTitle?: import("react").ReactNode;
96
+ children?: import("react").ReactNode;
97
+ className?: string | undefined;
98
+ view?: import("./types").View | undefined;
99
+ chevronPosition?: import("./types").ChevronPosition | undefined;
100
+ backgroundPredefined?: import("@cloud-ru/ds-materials").BackgroundPredefinedFill | undefined;
101
+ keepMounted?: boolean | undefined;
102
+ showChevron?: boolean | undefined;
103
+ 'data-test-id'?: string | undefined;
104
+ "aria-activedescendant"?: string | undefined | undefined;
105
+ "aria-atomic"?: (boolean | "true" | "false") | undefined;
106
+ "aria-autocomplete"?: "none" | "inline" | "list" | "both" | undefined | undefined;
107
+ "aria-braillelabel"?: string | undefined | undefined;
108
+ "aria-brailleroledescription"?: string | undefined | undefined;
109
+ "aria-busy"?: (boolean | "true" | "false") | undefined;
110
+ "aria-checked"?: boolean | "false" | "mixed" | "true" | undefined | undefined;
111
+ "aria-colcount"?: number | undefined | undefined;
112
+ "aria-colindex"?: number | undefined | undefined;
113
+ "aria-colindextext"?: string | undefined | undefined;
114
+ "aria-colspan"?: number | undefined | undefined;
115
+ "aria-controls"?: string | undefined | undefined;
116
+ "aria-current"?: boolean | "false" | "true" | "page" | "step" | "location" | "date" | "time" | undefined | undefined;
117
+ "aria-describedby"?: string | undefined | undefined;
118
+ "aria-description"?: string | undefined | undefined;
119
+ "aria-details"?: string | undefined | undefined;
120
+ "aria-disabled"?: (boolean | "true" | "false") | undefined;
121
+ "aria-dropeffect"?: "none" | "copy" | "execute" | "link" | "move" | "popup" | undefined | undefined;
122
+ "aria-errormessage"?: string | undefined | undefined;
123
+ "aria-expanded"?: (boolean | "true" | "false") | undefined;
124
+ "aria-flowto"?: string | undefined | undefined;
125
+ "aria-grabbed"?: (boolean | "true" | "false") | undefined;
126
+ "aria-haspopup"?: boolean | "false" | "true" | "menu" | "listbox" | "tree" | "grid" | "dialog" | undefined | undefined;
127
+ "aria-hidden"?: (boolean | "true" | "false") | undefined;
128
+ "aria-invalid"?: boolean | "false" | "true" | "grammar" | "spelling" | undefined | undefined;
129
+ "aria-keyshortcuts"?: string | undefined | undefined;
130
+ "aria-label"?: string | undefined | undefined;
131
+ "aria-labelledby"?: string | undefined | undefined;
132
+ "aria-level"?: number | undefined | undefined;
133
+ "aria-live"?: "off" | "assertive" | "polite" | undefined | undefined;
134
+ "aria-modal"?: (boolean | "true" | "false") | undefined;
135
+ "aria-multiline"?: (boolean | "true" | "false") | undefined;
136
+ "aria-multiselectable"?: (boolean | "true" | "false") | undefined;
137
+ "aria-orientation"?: "horizontal" | "vertical" | undefined | undefined;
138
+ "aria-owns"?: string | undefined | undefined;
139
+ "aria-placeholder"?: string | undefined | undefined;
140
+ "aria-posinset"?: number | undefined | undefined;
141
+ "aria-pressed"?: boolean | "false" | "mixed" | "true" | undefined | undefined;
142
+ "aria-readonly"?: (boolean | "true" | "false") | undefined;
143
+ "aria-relevant"?: "additions" | "additions removals" | "additions text" | "all" | "removals" | "removals additions" | "removals text" | "text" | "text additions" | "text removals" | undefined | undefined;
144
+ "aria-required"?: (boolean | "true" | "false") | undefined;
145
+ "aria-roledescription"?: string | undefined | undefined;
146
+ "aria-rowcount"?: number | undefined | undefined;
147
+ "aria-rowindex"?: number | undefined | undefined;
148
+ "aria-rowindextext"?: string | undefined | undefined;
149
+ "aria-rowspan"?: number | undefined | undefined;
150
+ "aria-selected"?: (boolean | "true" | "false") | undefined;
151
+ "aria-setsize"?: number | undefined | undefined;
152
+ "aria-sort"?: "none" | "ascending" | "descending" | "other" | undefined | undefined;
153
+ "aria-valuemax"?: number | undefined | undefined;
154
+ "aria-valuemin"?: number | undefined | undefined;
155
+ "aria-valuenow"?: number | undefined | undefined;
156
+ "aria-valuetext"?: string | undefined | undefined;
157
+ }) => import("react/jsx-runtime").JSX.Element;
158
+ CollapseBlockTertiary: (props: import("./components").CollapseBlockTertiaryProps) => import("react/jsx-runtime").JSX.Element;
159
+ };
160
+ export {};
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { SELECTION_MODE, ToggleGroup } from '@cloud-ru/ds-toggles';
3
+ import { CollapseBlockPrimary, CollapseBlockSecondary, CollapseBlockTertiary } from './components/CollapseBlock/index.js';
4
+ function AccordionBase({ selectionMode = SELECTION_MODE.Single, onExpandedChange, expandedDefault, expanded, children, }) {
5
+ const toggleGroupProps = {
6
+ selectionMode,
7
+ defaultValue: expandedDefault,
8
+ value: expanded,
9
+ onChange: onExpandedChange,
10
+ children,
11
+ };
12
+ return _jsx(ToggleGroup, { ...toggleGroupProps });
13
+ }
14
+ export const Accordion = Object.assign(AccordionBase, {
15
+ CollapseBlockPrimary,
16
+ CollapseBlockSecondary,
17
+ CollapseBlockTertiary,
18
+ });
@@ -0,0 +1,45 @@
1
+ import { type BackgroundPredefinedFill } from '@cloud-ru/ds-materials';
2
+ import { WithSupportProps } from '@cloud-ru/ds-utils';
3
+ import { PropsWithChildren, ReactNode } from 'react';
4
+ import { ChevronPosition, View } from '../../types.js';
5
+ type Component = 'accordionPrimary' | 'accordionSecondary' | 'accordionTertiary';
6
+ export type CollapseBlockProps = PropsWithChildren<WithSupportProps<{
7
+ /** Уникальный идентификатор блока в группе переключателей */
8
+ id: string;
9
+ /** Начальное состояние раскрытия (uncontrolled) */
10
+ title?: string;
11
+ /** Контент справа от заголовка */
12
+ afterTitle?: ReactNode;
13
+ /** Подзаголовок под строкой заголовка */
14
+ subTitle?: ReactNode;
15
+ /** Дополнительный класс корневого элемента */
16
+ className?: string;
17
+ /** Визуальный вариант обложки (`simple`, `outline`, `elevated`) */
18
+ view?: View;
19
+ /** Положение шеврона-раскрытия относительно текста (`before` | `after`) */
20
+ chevronPosition?: ChevronPosition;
21
+ /** Показывать ли шеврон-раскрытия рядом с заголовком (по умолчанию `true`) */
22
+ showChevron?: boolean;
23
+ /**
24
+ * Слой backgroundPredefined + acrylic (см. `BACKGROUND_PREDEFINED_FILL` в `@cloud-ru/ds-materials`).
25
+ * По умолчанию `material/neutralBackground1Level`.
26
+ */
27
+ backgroundPredefined?: BackgroundPredefinedFill;
28
+ /** Уровень аккордеона: размер типографики и отступы */
29
+ component: Component;
30
+ /** Оставлять ли контент в DOM при сворачивании */
31
+ keepMounted?: boolean;
32
+ }>>;
33
+ /**
34
+ * Элемент аккордеона: заголовок, шеврон и раскрываемая область с контентом.
35
+ */
36
+ export declare function CollapseBlock({ id, title, afterTitle, subTitle, children, className, view, chevronPosition, backgroundPredefined, keepMounted, component, showChevron, 'data-test-id': dataTestId, ...rest }: CollapseBlockProps): import("react/jsx-runtime").JSX.Element;
37
+ export type CollapseBlockComponent = CollapseBlockProps['component'];
38
+ type CollapseBlockPropsWithoutComponent = Omit<CollapseBlockProps, 'component'>;
39
+ export type CollapseBlockPrimaryProps = CollapseBlockPropsWithoutComponent;
40
+ export declare const CollapseBlockPrimary: (props: CollapseBlockPropsWithoutComponent) => import("react/jsx-runtime").JSX.Element;
41
+ export type CollapseBlockSecondaryProps = CollapseBlockPropsWithoutComponent;
42
+ export declare const CollapseBlockSecondary: (props: CollapseBlockPropsWithoutComponent) => import("react/jsx-runtime").JSX.Element;
43
+ export type CollapseBlockTertiaryProps = Omit<CollapseBlockPropsWithoutComponent, 'view' | 'backgroundPredefined'>;
44
+ export declare const CollapseBlockTertiary: (props: CollapseBlockTertiaryProps) => import("react/jsx-runtime").JSX.Element;
45
+ export {};
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from '@cloud-ru/ds-button';
3
+ import { ChevronDownSVG, ChevronUpSVG } from '@cloud-ru/ds-icons/interface/system';
4
+ import { BACKGROUND_PREDEFINED_FILL, backgroundPredefinedFillToAcrylic, } from '@cloud-ru/ds-materials';
5
+ import { useThemeClassnames } from '@cloud-ru/ds-theme';
6
+ import { TruncateString } from '@cloud-ru/ds-truncate-string';
7
+ import { Typography } from '@cloud-ru/ds-typography';
8
+ import { extractSupportProps } from '@cloud-ru/ds-utils';
9
+ import cn from 'classnames';
10
+ import { ANIMATION_DURATION, CHEVRON_POSITION, TEST_IDS, VIEW } from '../../constants.js';
11
+ import { useCollapseState } from './hooks.js';
12
+ import styles from './styles.module.css';
13
+ const MAP_COMPONENT_TO_TILE_SIZE = {
14
+ accordionPrimary: 'l',
15
+ accordionSecondary: 'm',
16
+ accordionTertiary: 's',
17
+ };
18
+ /**
19
+ * Элемент аккордеона: заголовок, шеврон и раскрываемая область с контентом.
20
+ */
21
+ export function CollapseBlock({ id, title, afterTitle, subTitle, children, className, view = VIEW.Simple, chevronPosition = CHEVRON_POSITION.After, backgroundPredefined = BACKGROUND_PREDEFINED_FILL.NeutralBackground1Level, keepMounted = false, component, showChevron = true, 'data-test-id': dataTestId, ...rest }) {
22
+ const { isOpen, isMounted, toggleOpen, isCompletelyOpen, isCompletelyClose } = useCollapseState({
23
+ id,
24
+ keepMounted,
25
+ });
26
+ const { appearance, level } = backgroundPredefinedFillToAcrylic(backgroundPredefined);
27
+ // Фиксируем density, остальные оси (colorScheme/brand/…) наследуем из контекста темы.
28
+ const themeClassName = useThemeClassnames({ density: 'compact' });
29
+ return (_jsxs("div", { ...extractSupportProps(rest), className: cn(styles.wrapper, className), "data-acrylic-appearance": appearance, "data-acrylic-level": level, "data-expanded": isOpen, "data-completely-open": isCompletelyOpen || undefined, "data-view": view, "data-component": component, "data-test-id": dataTestId ?? TEST_IDS.collapseBlock, style: {
30
+ '--sn-collapse-block-animation-duration': `${ANIMATION_DURATION}ms`,
31
+ }, children: [component !== 'accordionTertiary' && (_jsxs(_Fragment, { children: [_jsx("div", { className: styles.acrylic }), view === VIEW.Outline && _jsx("div", { className: styles.stateLayer, "data-state": 'borderOnBackground' })] })), _jsxs("div", { className: styles.titleContent, "data-expanded": isOpen || undefined, "data-completely-close": isCompletelyClose || undefined, "data-chevron-position": chevronPosition, onClick: toggleOpen, children: [_jsxs("div", { className: styles.titleWrapper, children: [_jsxs("div", { className: styles.titleLine, children: [title && (_jsx(Typography, { size: MAP_COMPONENT_TO_TILE_SIZE[component], variant: 'title', className: styles.title, "data-test-id": TEST_IDS.title, children: _jsx(TruncateString, { text: title, variant: 'end' }) })), afterTitle && (_jsx("div", { "data-test-id": TEST_IDS.afterTitle, className: styles.afterTitle, children: afterTitle }))] }), subTitle && (_jsx("div", { className: styles.subTitle, "data-test-id": TEST_IDS.subTitle, children: subTitle }))] }), showChevron && (_jsx("div", { className: cn(styles.chevronWrapper, themeClassName), children: _jsx(Button, { view: 'function', size: 'm', icon: isOpen ? _jsx(ChevronUpSVG, {}) : _jsx(ChevronDownSVG, {}), "data-test-id": TEST_IDS.chevron, appearance: 'neutral' }) }))] }), _jsx("div", { className: styles.collapse, "data-expanded": isOpen || undefined, "aria-hidden": !isOpen, children: _jsxs("div", { className: cn(styles.container, isCompletelyOpen && styles.containerCompletelyOpen), "data-completely-close": isCompletelyClose || undefined, "data-expanded": isOpen || undefined, children: [_jsx("div", {}), isMounted ? (_jsx("div", { "data-test-id": TEST_IDS.content, className: styles.content, children: children })) : null] }) })] }));
32
+ }
33
+ function getCollapseBlock(component) {
34
+ return function CollapseBlockWithComponent(props) {
35
+ return _jsx(CollapseBlock, { ...props, component: component });
36
+ };
37
+ }
38
+ export const CollapseBlockPrimary = getCollapseBlock('accordionPrimary');
39
+ export const CollapseBlockSecondary = getCollapseBlock('accordionSecondary');
40
+ export const CollapseBlockTertiary = getCollapseBlock('accordionTertiary');
@@ -0,0 +1,12 @@
1
+ type UseCollapseStateProps = {
2
+ id: string;
3
+ keepMounted?: boolean;
4
+ };
5
+ export declare function useCollapseState({ id, keepMounted }: UseCollapseStateProps): {
6
+ isOpen: boolean;
7
+ isMounted: boolean;
8
+ toggleOpen: () => void;
9
+ isCompletelyOpen: boolean;
10
+ isCompletelyClose: boolean;
11
+ };
12
+ export {};
@@ -0,0 +1,40 @@
1
+ import { useToggleGroup } from '@cloud-ru/ds-toggles';
2
+ import { useEffect, useRef, useState } from 'react';
3
+ import { ANIMATION_DURATION } from '../../constants.js';
4
+ export function useCollapseState({ id, keepMounted = false }) {
5
+ const { isChecked: isOpen, handleClick: toggleOpen } = useToggleGroup({ value: id });
6
+ // Контент доживает до конца закрывающей анимации, но при открытии появляется синхронно с
7
+ // `isOpen` — если монтировать его эффектом, первый кадр раскрытия считается по пустому телу
8
+ // и высота стартует рывком.
9
+ const [keepMountedWhileClosing, setKeepMountedWhileClosing] = useState(false);
10
+ const isMounted = isOpen || keepMounted || keepMountedWhileClosing;
11
+ const [isCompletelyOpen, setIsCompletelyOpen] = useState(isOpen);
12
+ const [isCompletelyClose, setIsCompletelyClose] = useState(!isOpen);
13
+ const timeoutRef = useRef(null);
14
+ useEffect(() => {
15
+ if (isOpen) {
16
+ setKeepMountedWhileClosing(true);
17
+ setIsCompletelyClose(false);
18
+ timeoutRef.current = setTimeout(() => {
19
+ setIsCompletelyOpen(true);
20
+ }, ANIMATION_DURATION);
21
+ }
22
+ else {
23
+ setIsCompletelyOpen(false);
24
+ timeoutRef.current = setTimeout(() => {
25
+ setIsCompletelyClose(true);
26
+ setKeepMountedWhileClosing(false);
27
+ }, ANIMATION_DURATION);
28
+ }
29
+ return () => {
30
+ timeoutRef.current && clearTimeout(timeoutRef.current);
31
+ };
32
+ }, [isOpen, keepMounted]);
33
+ return {
34
+ isOpen,
35
+ isMounted,
36
+ toggleOpen,
37
+ isCompletelyOpen,
38
+ isCompletelyClose,
39
+ };
40
+ }
@@ -0,0 +1 @@
1
+ export * from './CollapseBlock.js';
@@ -0,0 +1 @@
1
+ export * from './CollapseBlock.js';