@snack-uikit/breadcrumbs 0.5.2 → 0.6.1
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/README.md +2 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +6 -6
- package/dist/components/Breadcrumbs/Breadcrumbs.js +8 -10
- package/dist/components/Collapse/Collapse.js +5 -5
- package/dist/components/Crumb/Crumb.d.ts +1 -2
- package/dist/components/Crumb/Crumb.js +6 -6
- package/dist/components/CrumbsTypography/CrumbsTypography.d.ts +1 -1
- package/dist/components/CrumbsTypography/CrumbsTypography.js +2 -2
- package/dist/components/HiddenChain/HiddenChain.d.ts +1 -2
- package/dist/components/HiddenChain/HiddenChain.js +1 -1
- package/dist/components/HiddenChain/hooks.js +5 -5
- package/dist/components/Separator/Separator.js +2 -2
- package/dist/components/Wrapper/Wrapper.d.ts +1 -1
- package/dist/components/Wrapper/Wrapper.js +1 -1
- package/dist/constants.d.ts +15 -15
- package/dist/constants.js +15 -18
- package/dist/context.d.ts +1 -1
- package/dist/context.js +2 -2
- package/dist/types.d.ts +12 -8
- package/dist/types.js +1 -1
- package/dist/utils/buildBreadcrumbsConfigs.js +25 -21
- package/dist/utils/buildSizeMap.js +5 -5
- package/package.json +5 -5
- package/src/components/Breadcrumbs/Breadcrumbs.tsx +9 -15
- package/src/components/Collapse/Collapse.tsx +5 -5
- package/src/components/Crumb/Crumb.tsx +7 -7
- package/src/components/CrumbsTypography/CrumbsTypography.tsx +3 -8
- package/src/components/HiddenChain/HiddenChain.tsx +1 -2
- package/src/components/HiddenChain/hooks.tsx +6 -6
- package/src/components/Separator/Separator.tsx +2 -2
- package/src/components/Wrapper/Wrapper.tsx +1 -1
- package/src/constants.ts +15 -15
- package/src/context.ts +3 -2
- package/src/types.ts +16 -8
- package/src/utils/buildBreadcrumbsConfigs.ts +26 -22
- package/src/utils/buildSizeMap.ts +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,26 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## 0.6.1 (2023-12-14)
|
|
7
|
+
|
|
8
|
+
### Only dependencies have been changed
|
|
9
|
+
* [@snack-uikit/popover@0.6.1](https://git.sbercloud.tech/sbercloud-ui/tokens-design-system/snack-uikit/-/blob/master/packages/popover/CHANGELOG.md)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# 0.6.0 (2023-12-14)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### BREAKING CHANGES
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
* **FF-3729:** replace enum with unions ([910db4a](https://github.com/cloud-ru-tech/snack-uikit/commit/910db4aa8231ccbc58e538e5c5c1f461b1dec275))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
## 0.5.2 (2023-12-07)
|
|
7
27
|
|
|
8
28
|
### Only dependencies have been changed
|
package/README.md
CHANGED
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
[//]: DOCUMENTATION_SECTION_START
|
|
10
10
|
[//]: THIS_SECTION_IS_AUTOGENERATED_PLEASE_DONT_EDIT_IT
|
|
11
11
|
## Breadcrumbs
|
|
12
|
+
Компонент хлебных крошек
|
|
12
13
|
### Props
|
|
13
14
|
| name | type | default value | description |
|
|
14
15
|
|------|------|---------------|-------------|
|
|
15
16
|
| items* | `Item[]` | - | Массив айтемов |
|
|
16
17
|
| className | `string` | - | CSS-класс |
|
|
17
18
|
| separator | `string` | "›" | Разделитель |
|
|
18
|
-
| size | enum Size: `"xs"`, `"s"` |
|
|
19
|
+
| size | enum Size: `"xs"`, `"s"` | s | Размер |
|
|
19
20
|
| firstItemIconOnly | `boolean` | false | Использовать иконку без лейбла в первом айтеме |
|
|
20
21
|
| inactiveLastItem | `boolean` | false | Делает некликабельным последний элемент, даже если для него переданы `href` или `onClick` |
|
|
21
22
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
3
|
-
import { Size } from '../../
|
|
4
|
-
import { Item } from '../../types';
|
|
3
|
+
import { Item, Size } from '../../types';
|
|
5
4
|
export type BreadcrumbsProps = WithSupportProps<{
|
|
6
5
|
/** Массив айтемов */
|
|
7
6
|
items: Item[];
|
|
@@ -14,7 +13,7 @@ export type BreadcrumbsProps = WithSupportProps<{
|
|
|
14
13
|
separator?: string;
|
|
15
14
|
/**
|
|
16
15
|
* Размер
|
|
17
|
-
* @default
|
|
16
|
+
* @default s
|
|
18
17
|
*/
|
|
19
18
|
size?: Size;
|
|
20
19
|
/**
|
|
@@ -28,6 +27,7 @@ export type BreadcrumbsProps = WithSupportProps<{
|
|
|
28
27
|
*/
|
|
29
28
|
inactiveLastItem?: boolean;
|
|
30
29
|
}>;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Компонент хлебных крошек
|
|
32
|
+
*/
|
|
33
|
+
export declare const Breadcrumbs: import("react").NamedExoticComponent<BreadcrumbsProps>;
|
|
@@ -11,7 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import { memo, useMemo, useRef } from 'react';
|
|
14
|
-
import {
|
|
14
|
+
import { ELEMENT_TYPE, ITEM_RENDER_MODE, SEPARATOR, SIZE } from '../../constants';
|
|
15
15
|
import { useBreadcrumbsLayout } from '../../hooks';
|
|
16
16
|
import { Collapse } from '../Collapse';
|
|
17
17
|
import { Crumb } from '../Crumb';
|
|
@@ -21,23 +21,23 @@ import { Wrapper } from '../Wrapper';
|
|
|
21
21
|
/**
|
|
22
22
|
* Компонент хлебных крошек
|
|
23
23
|
*/
|
|
24
|
-
const
|
|
25
|
-
var { items: itemsProps, size =
|
|
24
|
+
export const Breadcrumbs = memo(function Breadcrumbs(_a) {
|
|
25
|
+
var { items: itemsProps, size = SIZE.S, separator = SEPARATOR, className, firstItemIconOnly = false, inactiveLastItem = false } = _a, rest = __rest(_a, ["items", "size", "separator", "className", "firstItemIconOnly", "inactiveLastItem"]);
|
|
26
26
|
const containerRef = useRef(null);
|
|
27
27
|
const { setConfigs, currentConfig } = useBreadcrumbsLayout(containerRef);
|
|
28
28
|
const items = useMemo(() => inactiveLastItem
|
|
29
29
|
? itemsProps.map((item, index) => index === itemsProps.length - 1 ? Object.assign(Object.assign({}, item), { onClick: undefined, href: undefined }) : item)
|
|
30
30
|
: itemsProps, [inactiveLastItem, itemsProps]);
|
|
31
|
-
return (_jsxs(_Fragment, { children: [_jsx(HiddenChain, { items: items, size: size, separator: separator, onConfigsBuilt: setConfigs, firstItemIconOnly: firstItemIconOnly }), _jsx(Wrapper, Object.assign({}, rest, { ref: containerRef, hidden: false, size: size, separator: separator, className: className
|
|
31
|
+
return (_jsxs(_Fragment, { children: [_jsx(HiddenChain, { items: items, size: size, separator: separator, onConfigsBuilt: setConfigs, firstItemIconOnly: firstItemIconOnly }), _jsx(Wrapper, Object.assign({}, rest, { ref: containerRef, hidden: false, size: size, separator: separator, className: className, children: currentConfig === null || currentConfig === void 0 ? void 0 : currentConfig.chain.map((block, index, array) => {
|
|
32
32
|
const isLastElement = index === array.length - 1;
|
|
33
33
|
switch (block.element) {
|
|
34
|
-
case
|
|
34
|
+
case ELEMENT_TYPE.Separator:
|
|
35
35
|
return _jsx(Separator, {}, index);
|
|
36
|
-
case
|
|
36
|
+
case ELEMENT_TYPE.Collapse:
|
|
37
37
|
return _jsx(Collapse, { currentConfig: currentConfig.chain }, index);
|
|
38
|
-
case
|
|
38
|
+
case ELEMENT_TYPE.Item:
|
|
39
39
|
const { renderMode, id } = block.item;
|
|
40
|
-
if (renderMode !==
|
|
40
|
+
if (renderMode !== ITEM_RENDER_MODE.Collapsed) {
|
|
41
41
|
return (_jsx(Crumb, { useIconOnly: !index && firstItemIconOnly, current: isLastElement, renderMode: renderMode, minWidth: block.width, item: block.item }, id));
|
|
42
42
|
}
|
|
43
43
|
default:
|
|
@@ -45,5 +45,3 @@ const BreadcrumbsComponent = memo(function Breadcrumbs(_a) {
|
|
|
45
45
|
}
|
|
46
46
|
}) }))] }));
|
|
47
47
|
});
|
|
48
|
-
export const Breadcrumbs = BreadcrumbsComponent;
|
|
49
|
-
Breadcrumbs.sizes = Size;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useContext } from 'react';
|
|
3
3
|
import { Popover } from '@snack-uikit/popover';
|
|
4
|
-
import {
|
|
4
|
+
import { ELEMENT_TYPE, ITEM_RENDER_MODE } from '../../constants';
|
|
5
5
|
import { BreadcrumbsContext } from '../../context';
|
|
6
6
|
import { getTestId } from '../../utils';
|
|
7
7
|
import { Crumb } from '../Crumb';
|
|
@@ -12,12 +12,12 @@ export function Collapse({ currentConfig, className }) {
|
|
|
12
12
|
const ctx = useContext(BreadcrumbsContext);
|
|
13
13
|
const { hidden, size, testId } = ctx;
|
|
14
14
|
const collapsedItems = currentConfig.map(node => {
|
|
15
|
-
if (node.element ===
|
|
15
|
+
if (node.element === ELEMENT_TYPE.Item && node.item.renderMode === ITEM_RENDER_MODE.Collapsed) {
|
|
16
16
|
const { id } = node.item;
|
|
17
|
-
return (_jsxs("div",
|
|
17
|
+
return (_jsxs("div", { className: styles.collapsedRow, children: [_jsx(Crumb, { item: node.item, renderMode: ITEM_RENDER_MODE.Full }), _jsx(Separator, {})] }, id));
|
|
18
18
|
}
|
|
19
19
|
return null;
|
|
20
20
|
});
|
|
21
|
-
const tip = (_jsx(BreadcrumbsContext.Provider,
|
|
22
|
-
return (_jsx("div",
|
|
21
|
+
const tip = (_jsx(BreadcrumbsContext.Provider, { value: Object.assign(Object.assign({}, ctx), { testId: `${testId}-collapsed` }), children: collapsedItems }));
|
|
22
|
+
return (_jsx("div", { className: className, "data-test-id": getTestId('collapse', testId), "data-element-type": ELEMENT_TYPE.Collapse, children: _jsx(Popover, { tip: tip, trigger: 'hoverAndFocusVisible', placement: 'top', children: _jsx("button", { className: styles.collapse, tabIndex: hidden ? -1 : 0, children: _jsx(CrumbsTypography, { size: size, children: "..." }) }) }) }));
|
|
23
23
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
3
|
-
import { ItemRenderMode } from '../../
|
|
4
|
-
import { Item } from '../../types';
|
|
3
|
+
import { Item, ItemRenderMode } from '../../types';
|
|
5
4
|
export type CrumbProps = WithSupportProps<{
|
|
6
5
|
renderMode: ItemRenderMode;
|
|
7
6
|
className?: string;
|
|
@@ -13,7 +13,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
13
13
|
import cn from 'classnames';
|
|
14
14
|
import { memo, useContext } from 'react';
|
|
15
15
|
import { extractSupportProps } from '@snack-uikit/utils';
|
|
16
|
-
import {
|
|
16
|
+
import { ELEMENT_TYPE, ITEM_RENDER_MODE } from '../../constants';
|
|
17
17
|
import { BreadcrumbsContext } from '../../context';
|
|
18
18
|
import { getTestId } from '../../utils';
|
|
19
19
|
import { CrumbsTypography } from '../CrumbsTypography';
|
|
@@ -23,9 +23,9 @@ export const Crumb = memo(function Crumb(_a) {
|
|
|
23
23
|
var { minWidth: minWidthProp = 0, current = false, renderMode, className, item: { label: labelProp, shortLabel, icon: Icon, id, onClick, href }, useIconOnly: useIconOnlyProp } = _a, rest = __rest(_a, ["minWidth", "current", "renderMode", "className", "item", "useIconOnly"]);
|
|
24
24
|
const { hidden, size, testId } = useContext(BreadcrumbsContext);
|
|
25
25
|
const isLabelShouldBeEllipse = hidden && labelProp.length > ELLIPSIS_LABEL_LENGTH;
|
|
26
|
-
const label = isLabelShouldBeEllipse && renderMode ===
|
|
26
|
+
const label = isLabelShouldBeEllipse && renderMode === ITEM_RENDER_MODE.Ellipsis ? `${labelProp.substring(0, 4)}...` : labelProp;
|
|
27
27
|
const useIconOnly = Boolean(Icon && useIconOnlyProp);
|
|
28
|
-
const minWidth = minWidthProp && renderMode ===
|
|
28
|
+
const minWidth = minWidthProp && renderMode === ITEM_RENDER_MODE.Ellipsis ? minWidthProp : 'auto';
|
|
29
29
|
const dataAttributes = {
|
|
30
30
|
'data-render-mode': renderMode,
|
|
31
31
|
'data-current': current,
|
|
@@ -33,9 +33,9 @@ export const Crumb = memo(function Crumb(_a) {
|
|
|
33
33
|
'data-size': size,
|
|
34
34
|
'data-id': id,
|
|
35
35
|
};
|
|
36
|
-
const content = (_jsxs(_Fragment, { children: [Icon && (_jsx("div",
|
|
36
|
+
const content = (_jsxs(_Fragment, { children: [Icon && (_jsx("div", { className: styles.icon, children: _jsx(Icon, { size: 24 }) })), !useIconOnly && (_jsx(CrumbsTypography, { size: size, className: styles.label, children: renderMode === ITEM_RENDER_MODE.ShortLabel ? shortLabel : label }))] }));
|
|
37
37
|
const tabIndex = hidden ? { tabIndex: -1 } : {};
|
|
38
|
-
const title = renderMode ===
|
|
38
|
+
const title = renderMode === ITEM_RENDER_MODE.Ellipsis ? label : '';
|
|
39
39
|
let crumb = null;
|
|
40
40
|
if (href) {
|
|
41
41
|
crumb = (_jsx("a", Object.assign({ className: styles.crumb, onClick: onClick, href: href, "aria-label": useIconOnly ? label : undefined, "data-clickable": true }, dataAttributes, tabIndex, { children: content })));
|
|
@@ -46,5 +46,5 @@ export const Crumb = memo(function Crumb(_a) {
|
|
|
46
46
|
else {
|
|
47
47
|
crumb = (_jsx("div", Object.assign({ className: styles.crumb }, dataAttributes, { children: content })));
|
|
48
48
|
}
|
|
49
|
-
return (_jsx("li", Object.assign({}, extractSupportProps(rest), dataAttributes, { "data-test-id": getTestId('crumb', testId), className: cn(styles.wrapper, className), "data-element-type":
|
|
49
|
+
return (_jsx("li", Object.assign({}, extractSupportProps(rest), dataAttributes, { "data-test-id": getTestId('crumb', testId), className: cn(styles.wrapper, className), "data-element-type": ELEMENT_TYPE.Item, style: { minWidth }, title: title, children: crumb })));
|
|
50
50
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Typography } from '@snack-uikit/typography';
|
|
3
|
-
import {
|
|
3
|
+
import { SIZE } from '../../constants';
|
|
4
4
|
export function CrumbsTypography({ size, children, className }) {
|
|
5
|
-
return (_jsx(Typography,
|
|
5
|
+
return (_jsx(Typography, { className: className, size: size === SIZE.S ? 'l' : 'm', tag: 'div', family: 'sans', purpose: 'body', children: children }));
|
|
6
6
|
}
|
|
@@ -23,5 +23,5 @@ export const HiddenChain = memo(function HiddenChain({ size, separator, items, o
|
|
|
23
23
|
}
|
|
24
24
|
}, [items, onConfigsBuilt]);
|
|
25
25
|
const renderItemModes = useItemModesRender({ firstItemIconOnly });
|
|
26
|
-
return (_jsxs(Wrapper,
|
|
26
|
+
return (_jsxs(Wrapper, { size: size, hidden: true, ref: containerRef, separator: separator, "data-test-id": 'hidden-wrapper', children: [_jsx(Separator, {}), _jsx(Collapse, { currentConfig: [] }), items.map(renderItemModes)] }));
|
|
27
27
|
});
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { ITEM_RENDER_MODE } from '../../constants';
|
|
4
4
|
import { Crumb } from '../Crumb';
|
|
5
5
|
/**
|
|
6
6
|
* Варианты отображения для обычного айтема
|
|
7
7
|
*/
|
|
8
8
|
const NORMAL_MODES = [
|
|
9
|
-
[
|
|
10
|
-
[
|
|
11
|
-
[
|
|
9
|
+
[ITEM_RENDER_MODE.ShortLabel, 'shortLabel'],
|
|
10
|
+
[ITEM_RENDER_MODE.Ellipsis, 'label'],
|
|
11
|
+
[ITEM_RENDER_MODE.Full, 'label'],
|
|
12
12
|
];
|
|
13
13
|
/**
|
|
14
14
|
* Варианты отображения айтемов состоящих из одной иконки
|
|
15
15
|
*/
|
|
16
|
-
const ICON_ONLY_MODES = [[
|
|
16
|
+
const ICON_ONLY_MODES = [[ITEM_RENDER_MODE.Full, 'label']];
|
|
17
17
|
const getItemModesRenderer = (firstItemIconOnly) => (item, index) => {
|
|
18
18
|
const isIconOnlyMode = !index && firstItemIconOnly;
|
|
19
19
|
const modes = isIconOnlyMode ? ICON_ONLY_MODES : NORMAL_MODES;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useContext } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { ELEMENT_TYPE } from '../../constants';
|
|
4
4
|
import { BreadcrumbsContext } from '../../context';
|
|
5
5
|
import { getTestId } from '../../utils';
|
|
6
6
|
import { CrumbsTypography } from '../CrumbsTypography';
|
|
7
7
|
import styles from './styles.module.css';
|
|
8
8
|
export function Separator() {
|
|
9
9
|
const { size, separator, testId } = useContext(BreadcrumbsContext);
|
|
10
|
-
return (_jsx("div",
|
|
10
|
+
return (_jsx("div", { "aria-hidden": true, "data-size": size, className: styles.separator, "data-test-id": getTestId('separator', testId), "data-element-type": ELEMENT_TYPE.Separator, children: _jsx(CrumbsTypography, { size: size, children: separator }) }));
|
|
11
11
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PropsWithChildren } from 'react';
|
|
2
2
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
3
|
-
import { Size } from '../../
|
|
3
|
+
import { Size } from '../../types';
|
|
4
4
|
export type WrapperProps = PropsWithChildren<WithSupportProps<{
|
|
5
5
|
hidden: boolean;
|
|
6
6
|
className?: string;
|
|
@@ -17,5 +17,5 @@ import { BreadcrumbsContext } from '../../context';
|
|
|
17
17
|
import styles from './styles.module.css';
|
|
18
18
|
export const Wrapper = forwardRef(function HiddenWrapper(_a, ref) {
|
|
19
19
|
var { children, className, size, hidden, separator, 'data-test-id': testId } = _a, rest = __rest(_a, ["children", "className", "size", "hidden", "separator", 'data-test-id']);
|
|
20
|
-
return (_jsx("nav", Object.assign({}, extractSupportProps(rest), { className: cn(styles.wrapper, className), "data-test-id": testId, "data-hidden": hidden, "aria-hidden": hidden
|
|
20
|
+
return (_jsx("nav", Object.assign({}, extractSupportProps(rest), { className: cn(styles.wrapper, className), "data-test-id": testId, "data-hidden": hidden, "aria-hidden": hidden, children: _jsx(BreadcrumbsContext.Provider, { value: { hidden, size, separator, testId }, children: _jsx("ul", { className: styles.row, ref: ref, "data-hidden": hidden, "data-size": size, children: children }) }) })));
|
|
21
21
|
});
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
export declare const SEPARATOR = "\u203A";
|
|
2
|
-
export declare
|
|
3
|
-
Full
|
|
4
|
-
ShortLabel
|
|
5
|
-
Ellipsis
|
|
6
|
-
Collapsed
|
|
7
|
-
}
|
|
8
|
-
export declare
|
|
9
|
-
Separator
|
|
10
|
-
Collapse
|
|
11
|
-
Item
|
|
12
|
-
}
|
|
13
|
-
export declare
|
|
14
|
-
Xs
|
|
15
|
-
S
|
|
16
|
-
}
|
|
2
|
+
export declare const ITEM_RENDER_MODE: {
|
|
3
|
+
readonly Full: "full";
|
|
4
|
+
readonly ShortLabel: "shortLabel";
|
|
5
|
+
readonly Ellipsis: "ellipsis";
|
|
6
|
+
readonly Collapsed: "collapsed";
|
|
7
|
+
};
|
|
8
|
+
export declare const ELEMENT_TYPE: {
|
|
9
|
+
readonly Separator: "separator";
|
|
10
|
+
readonly Collapse: "collapse";
|
|
11
|
+
readonly Item: "item";
|
|
12
|
+
};
|
|
13
|
+
export declare const SIZE: {
|
|
14
|
+
readonly Xs: "xs";
|
|
15
|
+
readonly S: "s";
|
|
16
|
+
};
|
package/dist/constants.js
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
export const SEPARATOR = '›';
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Size["Xs"] = "xs";
|
|
18
|
-
Size["S"] = "s";
|
|
19
|
-
})(Size || (Size = {}));
|
|
2
|
+
export const ITEM_RENDER_MODE = {
|
|
3
|
+
Full: 'full',
|
|
4
|
+
ShortLabel: 'shortLabel',
|
|
5
|
+
Ellipsis: 'ellipsis',
|
|
6
|
+
Collapsed: 'collapsed',
|
|
7
|
+
};
|
|
8
|
+
export const ELEMENT_TYPE = {
|
|
9
|
+
Separator: 'separator',
|
|
10
|
+
Collapse: 'collapse',
|
|
11
|
+
Item: 'item',
|
|
12
|
+
};
|
|
13
|
+
export const SIZE = {
|
|
14
|
+
Xs: 'xs',
|
|
15
|
+
S: 's',
|
|
16
|
+
};
|
package/dist/context.d.ts
CHANGED
package/dist/context.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createContext } from 'react';
|
|
2
|
-
import { SEPARATOR,
|
|
2
|
+
import { SEPARATOR, SIZE } from './constants';
|
|
3
3
|
export const BreadcrumbsContext = createContext({
|
|
4
4
|
hidden: false,
|
|
5
|
-
size:
|
|
5
|
+
size: SIZE.S,
|
|
6
6
|
separator: SEPARATOR,
|
|
7
7
|
});
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { JSXElementConstructor, MouseEventHandler } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { ValueOf } from '@snack-uikit/utils';
|
|
3
|
+
import { ELEMENT_TYPE, ITEM_RENDER_MODE, SIZE } from './constants';
|
|
4
|
+
export type Size = ValueOf<typeof SIZE>;
|
|
5
|
+
export type ElementType = ValueOf<typeof ELEMENT_TYPE>;
|
|
6
|
+
export type ItemRenderMode = ValueOf<typeof ITEM_RENDER_MODE>;
|
|
3
7
|
export type Item = {
|
|
4
8
|
id: string;
|
|
5
9
|
label: string;
|
|
@@ -14,10 +18,10 @@ export type InnerItem = Item & {
|
|
|
14
18
|
renderMode: ItemRenderMode;
|
|
15
19
|
};
|
|
16
20
|
export type ItemSizeMap = {
|
|
17
|
-
[
|
|
18
|
-
[
|
|
19
|
-
[
|
|
20
|
-
[
|
|
21
|
+
[ITEM_RENDER_MODE.ShortLabel]: number;
|
|
22
|
+
[ITEM_RENDER_MODE.Collapsed]: number;
|
|
23
|
+
[ITEM_RENDER_MODE.Ellipsis]: number;
|
|
24
|
+
[ITEM_RENDER_MODE.Full]: number;
|
|
21
25
|
};
|
|
22
26
|
export type SizeMap = {
|
|
23
27
|
separator: number;
|
|
@@ -27,13 +31,13 @@ export type SizeMap = {
|
|
|
27
31
|
};
|
|
28
32
|
};
|
|
29
33
|
export type BreadcrumbsConfigChain = Array<{
|
|
30
|
-
element:
|
|
34
|
+
element: typeof ELEMENT_TYPE.Collapse;
|
|
31
35
|
width: number;
|
|
32
36
|
} | {
|
|
33
|
-
element:
|
|
37
|
+
element: typeof ELEMENT_TYPE.Separator;
|
|
34
38
|
width: number;
|
|
35
39
|
} | {
|
|
36
|
-
element:
|
|
40
|
+
element: typeof ELEMENT_TYPE.Item;
|
|
37
41
|
width: number;
|
|
38
42
|
item: InnerItem;
|
|
39
43
|
}>;
|
package/dist/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ITEM_RENDER_MODE } from './constants';
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ELEMENT_TYPE, ITEM_RENDER_MODE } from '../constants';
|
|
2
2
|
const RENDER_MODE_WEIGHT = {
|
|
3
|
-
[
|
|
4
|
-
[
|
|
5
|
-
[
|
|
6
|
-
[
|
|
3
|
+
[ITEM_RENDER_MODE.Full]: 0,
|
|
4
|
+
[ITEM_RENDER_MODE.ShortLabel]: 1,
|
|
5
|
+
[ITEM_RENDER_MODE.Ellipsis]: 100,
|
|
6
|
+
[ITEM_RENDER_MODE.Collapsed]: 10000,
|
|
7
7
|
};
|
|
8
|
-
const RENDER_MODE_WITH_WIDTH = [
|
|
8
|
+
const RENDER_MODE_WITH_WIDTH = [
|
|
9
|
+
ITEM_RENDER_MODE.Full,
|
|
10
|
+
ITEM_RENDER_MODE.ShortLabel,
|
|
11
|
+
ITEM_RENDER_MODE.Ellipsis,
|
|
12
|
+
];
|
|
9
13
|
const startsWith = (renderMode) => (item, tail) => [Object.assign(Object.assign({}, item), { renderMode }), ...tail];
|
|
10
|
-
const startsWithFull = startsWith(
|
|
11
|
-
const startsWithShortLabel = startsWith(
|
|
12
|
-
const startsWithEllipsis = startsWith(
|
|
13
|
-
const startsWithCollapsed = startsWith(
|
|
14
|
+
const startsWithFull = startsWith(ITEM_RENDER_MODE.Full);
|
|
15
|
+
const startsWithShortLabel = startsWith(ITEM_RENDER_MODE.ShortLabel);
|
|
16
|
+
const startsWithEllipsis = startsWith(ITEM_RENDER_MODE.Ellipsis);
|
|
17
|
+
const startsWithCollapsed = startsWith(ITEM_RENDER_MODE.Collapsed);
|
|
14
18
|
function buildSubChain([current, ...rest], { useCollapse = true, useEllipse = true }) {
|
|
15
19
|
const result = [];
|
|
16
20
|
if (!current) {
|
|
@@ -39,39 +43,39 @@ function buildSubChain([current, ...rest], { useCollapse = true, useEllipse = tr
|
|
|
39
43
|
}
|
|
40
44
|
const collapseAllRest = (lastElementRenderMode, rest) => rest.map((element, index, array) => {
|
|
41
45
|
const lastItem = index === array.length - 1;
|
|
42
|
-
return Object.assign(Object.assign({}, element), { renderMode: lastItem ? lastElementRenderMode :
|
|
46
|
+
return Object.assign(Object.assign({}, element), { renderMode: lastItem ? lastElementRenderMode : ITEM_RENDER_MODE.Collapsed });
|
|
43
47
|
});
|
|
44
48
|
export function buildBreadcrumbsConfigs(items, sizeMap) {
|
|
45
49
|
const chains = [];
|
|
46
|
-
const [first, ...rest] = items.map(item => (Object.assign(Object.assign({}, item), { renderMode:
|
|
50
|
+
const [first, ...rest] = items.map(item => (Object.assign(Object.assign({}, item), { renderMode: ITEM_RENDER_MODE.Full })));
|
|
47
51
|
buildSubChain(rest, { useCollapse: true, useEllipse: true }).forEach(subset => {
|
|
48
52
|
chains.push(startsWithFull(first, subset));
|
|
49
53
|
first.shortLabel && chains.push(startsWithShortLabel(first, subset));
|
|
50
54
|
});
|
|
51
55
|
/** Первый элемент можно схлопывать/сокращать только когда уже сокращено все что можно */
|
|
52
|
-
chains.push(startsWithEllipsis(first, collapseAllRest(
|
|
53
|
-
chains.push(startsWithEllipsis(first, collapseAllRest(
|
|
54
|
-
chains.push(startsWithCollapsed(first, collapseAllRest(
|
|
55
|
-
chains.push(startsWithCollapsed(first, collapseAllRest(
|
|
56
|
+
chains.push(startsWithEllipsis(first, collapseAllRest(ITEM_RENDER_MODE.Full, rest)));
|
|
57
|
+
chains.push(startsWithEllipsis(first, collapseAllRest(ITEM_RENDER_MODE.Ellipsis, rest)));
|
|
58
|
+
chains.push(startsWithCollapsed(first, collapseAllRest(ITEM_RENDER_MODE.Full, rest)));
|
|
59
|
+
chains.push(startsWithCollapsed(first, collapseAllRest(ITEM_RENDER_MODE.Ellipsis, rest)));
|
|
56
60
|
return chains.map(chain => chain.reduce((acc, item, index) => {
|
|
57
61
|
const { renderMode } = item;
|
|
58
62
|
if (index && RENDER_MODE_WITH_WIDTH.includes(renderMode)) {
|
|
59
63
|
acc.width += sizeMap.separator;
|
|
60
|
-
acc.chain.push({ element:
|
|
64
|
+
acc.chain.push({ element: ELEMENT_TYPE.Separator, width: sizeMap.separator });
|
|
61
65
|
}
|
|
62
|
-
if (!acc.hasCollapsed && renderMode ===
|
|
66
|
+
if (!acc.hasCollapsed && renderMode === ITEM_RENDER_MODE.Collapsed) {
|
|
63
67
|
if (index) {
|
|
64
68
|
acc.width += sizeMap.separator;
|
|
65
|
-
acc.chain.push({ element:
|
|
69
|
+
acc.chain.push({ element: ELEMENT_TYPE.Separator, width: sizeMap.separator });
|
|
66
70
|
}
|
|
67
71
|
acc.hasCollapsed = true;
|
|
68
72
|
acc.width += sizeMap.collapse;
|
|
69
|
-
acc.chain.push({ element:
|
|
73
|
+
acc.chain.push({ element: ELEMENT_TYPE.Collapse, width: sizeMap.collapse });
|
|
70
74
|
}
|
|
71
75
|
const width = sizeMap.items[item.id][renderMode];
|
|
72
76
|
acc.weight += RENDER_MODE_WEIGHT[item.renderMode];
|
|
73
77
|
acc.width += width;
|
|
74
|
-
acc.chain.push({ element:
|
|
78
|
+
acc.chain.push({ element: ELEMENT_TYPE.Item, item, width });
|
|
75
79
|
return acc;
|
|
76
80
|
}, { chain: [], weight: 0, width: 0, hasCollapsed: false }));
|
|
77
81
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ITEM_RENDER_MODE } from '../constants';
|
|
2
2
|
const getEmptyItemSizeMap = () => ({
|
|
3
|
-
[
|
|
4
|
-
[
|
|
5
|
-
[
|
|
6
|
-
[
|
|
3
|
+
[ITEM_RENDER_MODE.ShortLabel]: 0,
|
|
4
|
+
[ITEM_RENDER_MODE.Collapsed]: 0,
|
|
5
|
+
[ITEM_RENDER_MODE.Ellipsis]: 0,
|
|
6
|
+
[ITEM_RENDER_MODE.Full]: 0,
|
|
7
7
|
});
|
|
8
8
|
const getElementWidth = (element) => element.getBoundingClientRect().width;
|
|
9
9
|
export function buildSizeMap(container) {
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
6
|
"title": "Breadcrumbs",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.6.1",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"*.css",
|
|
10
10
|
"*.woff",
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"license": "Apache-2.0",
|
|
33
33
|
"scripts": {},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@snack-uikit/popover": "0.
|
|
36
|
-
"@snack-uikit/typography": "0.
|
|
37
|
-
"@snack-uikit/utils": "3.
|
|
35
|
+
"@snack-uikit/popover": "0.6.1",
|
|
36
|
+
"@snack-uikit/typography": "0.6.0",
|
|
37
|
+
"@snack-uikit/utils": "3.2.0",
|
|
38
38
|
"classnames": "2.3.2",
|
|
39
39
|
"lodash.debounce": "4.0.8"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "62f43bf0cbfd611d0b746117400f67096b1790f6"
|
|
42
42
|
}
|
|
@@ -2,9 +2,9 @@ import { memo, useMemo, useRef } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { ELEMENT_TYPE, ITEM_RENDER_MODE, SEPARATOR, SIZE } from '../../constants';
|
|
6
6
|
import { useBreadcrumbsLayout } from '../../hooks';
|
|
7
|
-
import { Item } from '../../types';
|
|
7
|
+
import { Item, Size } from '../../types';
|
|
8
8
|
import { Collapse } from '../Collapse';
|
|
9
9
|
import { Crumb } from '../Crumb';
|
|
10
10
|
import { HiddenChain } from '../HiddenChain';
|
|
@@ -23,7 +23,7 @@ export type BreadcrumbsProps = WithSupportProps<{
|
|
|
23
23
|
separator?: string;
|
|
24
24
|
/**
|
|
25
25
|
* Размер
|
|
26
|
-
* @default
|
|
26
|
+
* @default s
|
|
27
27
|
*/
|
|
28
28
|
size?: Size;
|
|
29
29
|
/**
|
|
@@ -41,9 +41,9 @@ export type BreadcrumbsProps = WithSupportProps<{
|
|
|
41
41
|
/**
|
|
42
42
|
* Компонент хлебных крошек
|
|
43
43
|
*/
|
|
44
|
-
const
|
|
44
|
+
export const Breadcrumbs = memo(function Breadcrumbs({
|
|
45
45
|
items: itemsProps,
|
|
46
|
-
size =
|
|
46
|
+
size = SIZE.S,
|
|
47
47
|
separator = SEPARATOR,
|
|
48
48
|
className,
|
|
49
49
|
firstItemIconOnly = false,
|
|
@@ -78,13 +78,13 @@ const BreadcrumbsComponent = memo(function Breadcrumbs({
|
|
|
78
78
|
const isLastElement = index === array.length - 1;
|
|
79
79
|
|
|
80
80
|
switch (block.element) {
|
|
81
|
-
case
|
|
81
|
+
case ELEMENT_TYPE.Separator:
|
|
82
82
|
return <Separator key={index} />;
|
|
83
|
-
case
|
|
83
|
+
case ELEMENT_TYPE.Collapse:
|
|
84
84
|
return <Collapse key={index} currentConfig={currentConfig.chain} />;
|
|
85
|
-
case
|
|
85
|
+
case ELEMENT_TYPE.Item:
|
|
86
86
|
const { renderMode, id } = block.item;
|
|
87
|
-
if (renderMode !==
|
|
87
|
+
if (renderMode !== ITEM_RENDER_MODE.Collapsed) {
|
|
88
88
|
return (
|
|
89
89
|
<Crumb
|
|
90
90
|
useIconOnly={!index && firstItemIconOnly}
|
|
@@ -104,9 +104,3 @@ const BreadcrumbsComponent = memo(function Breadcrumbs({
|
|
|
104
104
|
</>
|
|
105
105
|
);
|
|
106
106
|
});
|
|
107
|
-
|
|
108
|
-
export const Breadcrumbs = BreadcrumbsComponent as typeof BreadcrumbsComponent & {
|
|
109
|
-
sizes: typeof Size;
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
Breadcrumbs.sizes = Size;
|
|
@@ -2,7 +2,7 @@ import { useContext } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { Popover } from '@snack-uikit/popover';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { ELEMENT_TYPE, ITEM_RENDER_MODE } from '../../constants';
|
|
6
6
|
import { BreadcrumbsContext } from '../../context';
|
|
7
7
|
import { BreadcrumbsConfigChain } from '../../types';
|
|
8
8
|
import { getTestId } from '../../utils';
|
|
@@ -21,12 +21,12 @@ export function Collapse({ currentConfig, className }: CollapseProps) {
|
|
|
21
21
|
const { hidden, size, testId } = ctx;
|
|
22
22
|
|
|
23
23
|
const collapsedItems = currentConfig.map(node => {
|
|
24
|
-
if (node.element ===
|
|
24
|
+
if (node.element === ELEMENT_TYPE.Item && node.item.renderMode === ITEM_RENDER_MODE.Collapsed) {
|
|
25
25
|
const { id } = node.item;
|
|
26
26
|
|
|
27
27
|
return (
|
|
28
28
|
<div key={id} className={styles.collapsedRow}>
|
|
29
|
-
<Crumb item={node.item} renderMode={
|
|
29
|
+
<Crumb item={node.item} renderMode={ITEM_RENDER_MODE.Full} />
|
|
30
30
|
<Separator />
|
|
31
31
|
</div>
|
|
32
32
|
);
|
|
@@ -41,8 +41,8 @@ export function Collapse({ currentConfig, className }: CollapseProps) {
|
|
|
41
41
|
);
|
|
42
42
|
|
|
43
43
|
return (
|
|
44
|
-
<div className={className} data-test-id={getTestId('collapse', testId)} data-element-type={
|
|
45
|
-
<Popover tip={tip} trigger=
|
|
44
|
+
<div className={className} data-test-id={getTestId('collapse', testId)} data-element-type={ELEMENT_TYPE.Collapse}>
|
|
45
|
+
<Popover tip={tip} trigger='hoverAndFocusVisible' placement='top'>
|
|
46
46
|
<button className={styles.collapse} tabIndex={hidden ? -1 : 0}>
|
|
47
47
|
<CrumbsTypography size={size}>...</CrumbsTypography>
|
|
48
48
|
</button>
|
|
@@ -3,9 +3,9 @@ import { memo, useContext } from 'react';
|
|
|
3
3
|
|
|
4
4
|
import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { ELEMENT_TYPE, ITEM_RENDER_MODE } from '../../constants';
|
|
7
7
|
import { BreadcrumbsContext } from '../../context';
|
|
8
|
-
import { Item } from '../../types';
|
|
8
|
+
import { Item, ItemRenderMode } from '../../types';
|
|
9
9
|
import { getTestId } from '../../utils';
|
|
10
10
|
import { CrumbsTypography } from '../CrumbsTypography';
|
|
11
11
|
import styles from './styles.module.scss';
|
|
@@ -33,10 +33,10 @@ export const Crumb = memo(function Crumb({
|
|
|
33
33
|
const { hidden, size, testId } = useContext(BreadcrumbsContext);
|
|
34
34
|
const isLabelShouldBeEllipse = hidden && labelProp.length > ELLIPSIS_LABEL_LENGTH;
|
|
35
35
|
const label =
|
|
36
|
-
isLabelShouldBeEllipse && renderMode ===
|
|
36
|
+
isLabelShouldBeEllipse && renderMode === ITEM_RENDER_MODE.Ellipsis ? `${labelProp.substring(0, 4)}...` : labelProp;
|
|
37
37
|
const useIconOnly = Boolean(Icon && useIconOnlyProp);
|
|
38
38
|
|
|
39
|
-
const minWidth = minWidthProp && renderMode ===
|
|
39
|
+
const minWidth = minWidthProp && renderMode === ITEM_RENDER_MODE.Ellipsis ? minWidthProp : 'auto';
|
|
40
40
|
|
|
41
41
|
const dataAttributes = {
|
|
42
42
|
'data-render-mode': renderMode,
|
|
@@ -55,7 +55,7 @@ export const Crumb = memo(function Crumb({
|
|
|
55
55
|
)}
|
|
56
56
|
{!useIconOnly && (
|
|
57
57
|
<CrumbsTypography size={size} className={styles.label}>
|
|
58
|
-
{renderMode ===
|
|
58
|
+
{renderMode === ITEM_RENDER_MODE.ShortLabel ? shortLabel : label}
|
|
59
59
|
</CrumbsTypography>
|
|
60
60
|
)}
|
|
61
61
|
</>
|
|
@@ -63,7 +63,7 @@ export const Crumb = memo(function Crumb({
|
|
|
63
63
|
|
|
64
64
|
const tabIndex = hidden ? { tabIndex: -1 } : {};
|
|
65
65
|
|
|
66
|
-
const title = renderMode ===
|
|
66
|
+
const title = renderMode === ITEM_RENDER_MODE.Ellipsis ? label : '';
|
|
67
67
|
|
|
68
68
|
let crumb = null;
|
|
69
69
|
if (href) {
|
|
@@ -108,7 +108,7 @@ export const Crumb = memo(function Crumb({
|
|
|
108
108
|
{...dataAttributes}
|
|
109
109
|
data-test-id={getTestId('crumb', testId)}
|
|
110
110
|
className={cn(styles.wrapper, className)}
|
|
111
|
-
data-element-type={
|
|
111
|
+
data-element-type={ELEMENT_TYPE.Item}
|
|
112
112
|
style={{ minWidth }}
|
|
113
113
|
title={title}
|
|
114
114
|
>
|
|
@@ -2,7 +2,8 @@ import { PropsWithChildren } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { Typography } from '@snack-uikit/typography';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { SIZE } from '../../constants';
|
|
6
|
+
import { Size } from '../../types';
|
|
6
7
|
|
|
7
8
|
export type CrumbsTypographyProps = PropsWithChildren<{
|
|
8
9
|
size: Size;
|
|
@@ -11,13 +12,7 @@ export type CrumbsTypographyProps = PropsWithChildren<{
|
|
|
11
12
|
|
|
12
13
|
export function CrumbsTypography({ size, children, className }: CrumbsTypographyProps) {
|
|
13
14
|
return (
|
|
14
|
-
<Typography
|
|
15
|
-
className={className}
|
|
16
|
-
size={size === Size.S ? Typography.sizes.L : Typography.sizes.M}
|
|
17
|
-
tag={Typography.tags.div}
|
|
18
|
-
family={Typography.families.Sans}
|
|
19
|
-
role={Typography.roles.Body}
|
|
20
|
-
>
|
|
15
|
+
<Typography className={className} size={size === SIZE.S ? 'l' : 'm'} tag='div' family='sans' purpose='body'>
|
|
21
16
|
{children}
|
|
22
17
|
</Typography>
|
|
23
18
|
);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { memo, useEffect, useRef } from 'react';
|
|
2
2
|
|
|
3
|
-
import { Size } from '../../
|
|
4
|
-
import { BreadcrumbsConfig, Item } from '../../types';
|
|
3
|
+
import { BreadcrumbsConfig, Item, Size } from '../../types';
|
|
5
4
|
import { buildBreadcrumbsConfigs, buildSizeMap } from '../../utils';
|
|
6
5
|
import { Collapse } from '../Collapse';
|
|
7
6
|
import { Separator } from '../Separator';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { Item } from '../../types';
|
|
3
|
+
import { ITEM_RENDER_MODE } from '../../constants';
|
|
4
|
+
import { Item, ItemRenderMode } from '../../types';
|
|
5
5
|
import { Crumb } from '../Crumb';
|
|
6
6
|
|
|
7
7
|
type RenderModes = [ItemRenderMode, 'label' | 'shortLabel'][];
|
|
@@ -12,15 +12,15 @@ type UseItemModesRenderProps = { firstItemIconOnly: boolean };
|
|
|
12
12
|
* Варианты отображения для обычного айтема
|
|
13
13
|
*/
|
|
14
14
|
const NORMAL_MODES: RenderModes = [
|
|
15
|
-
[
|
|
16
|
-
[
|
|
17
|
-
[
|
|
15
|
+
[ITEM_RENDER_MODE.ShortLabel, 'shortLabel'],
|
|
16
|
+
[ITEM_RENDER_MODE.Ellipsis, 'label'],
|
|
17
|
+
[ITEM_RENDER_MODE.Full, 'label'],
|
|
18
18
|
];
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Варианты отображения айтемов состоящих из одной иконки
|
|
22
22
|
*/
|
|
23
|
-
const ICON_ONLY_MODES: RenderModes = [[
|
|
23
|
+
const ICON_ONLY_MODES: RenderModes = [[ITEM_RENDER_MODE.Full, 'label']];
|
|
24
24
|
|
|
25
25
|
const getItemModesRenderer = (firstItemIconOnly: boolean) => (item: Item, index: number) => {
|
|
26
26
|
const isIconOnlyMode = !index && firstItemIconOnly;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useContext } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { ELEMENT_TYPE } from '../../constants';
|
|
4
4
|
import { BreadcrumbsContext } from '../../context';
|
|
5
5
|
import { getTestId } from '../../utils';
|
|
6
6
|
import { CrumbsTypography } from '../CrumbsTypography';
|
|
@@ -15,7 +15,7 @@ export function Separator() {
|
|
|
15
15
|
data-size={size}
|
|
16
16
|
className={styles.separator}
|
|
17
17
|
data-test-id={getTestId('separator', testId)}
|
|
18
|
-
data-element-type={
|
|
18
|
+
data-element-type={ELEMENT_TYPE.Separator}
|
|
19
19
|
>
|
|
20
20
|
<CrumbsTypography size={size}>{separator}</CrumbsTypography>
|
|
21
21
|
</div>
|
|
@@ -3,8 +3,8 @@ import { forwardRef, PropsWithChildren } from 'react';
|
|
|
3
3
|
|
|
4
4
|
import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
|
|
5
5
|
|
|
6
|
-
import { Size } from '../../constants';
|
|
7
6
|
import { BreadcrumbsContext } from '../../context';
|
|
7
|
+
import { Size } from '../../types';
|
|
8
8
|
import styles from './styles.module.scss';
|
|
9
9
|
|
|
10
10
|
export type WrapperProps = PropsWithChildren<
|
package/src/constants.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
export const SEPARATOR = '›';
|
|
2
2
|
|
|
3
|
-
export
|
|
4
|
-
Full
|
|
5
|
-
ShortLabel
|
|
6
|
-
Ellipsis
|
|
7
|
-
Collapsed
|
|
8
|
-
}
|
|
3
|
+
export const ITEM_RENDER_MODE = {
|
|
4
|
+
Full: 'full',
|
|
5
|
+
ShortLabel: 'shortLabel',
|
|
6
|
+
Ellipsis: 'ellipsis',
|
|
7
|
+
Collapsed: 'collapsed',
|
|
8
|
+
} as const;
|
|
9
9
|
|
|
10
|
-
export
|
|
11
|
-
Separator
|
|
12
|
-
Collapse
|
|
13
|
-
Item
|
|
14
|
-
}
|
|
10
|
+
export const ELEMENT_TYPE = {
|
|
11
|
+
Separator: 'separator',
|
|
12
|
+
Collapse: 'collapse',
|
|
13
|
+
Item: 'item',
|
|
14
|
+
} as const;
|
|
15
15
|
|
|
16
|
-
export
|
|
17
|
-
Xs
|
|
18
|
-
S
|
|
19
|
-
}
|
|
16
|
+
export const SIZE = {
|
|
17
|
+
Xs: 'xs',
|
|
18
|
+
S: 's',
|
|
19
|
+
} as const;
|
package/src/context.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createContext } from 'react';
|
|
2
2
|
|
|
3
|
-
import { SEPARATOR,
|
|
3
|
+
import { SEPARATOR, SIZE } from './constants';
|
|
4
|
+
import { Size } from './types';
|
|
4
5
|
|
|
5
6
|
type BreadcrumbsContextValue = {
|
|
6
7
|
hidden: boolean;
|
|
@@ -11,6 +12,6 @@ type BreadcrumbsContextValue = {
|
|
|
11
12
|
|
|
12
13
|
export const BreadcrumbsContext = createContext<BreadcrumbsContextValue>({
|
|
13
14
|
hidden: false,
|
|
14
|
-
size:
|
|
15
|
+
size: SIZE.S,
|
|
15
16
|
separator: SEPARATOR,
|
|
16
17
|
});
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { JSXElementConstructor, MouseEventHandler } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { ValueOf } from '@snack-uikit/utils';
|
|
4
|
+
|
|
5
|
+
import { ELEMENT_TYPE, ITEM_RENDER_MODE, SIZE } from './constants';
|
|
6
|
+
|
|
7
|
+
export type Size = ValueOf<typeof SIZE>;
|
|
8
|
+
|
|
9
|
+
export type ElementType = ValueOf<typeof ELEMENT_TYPE>;
|
|
10
|
+
|
|
11
|
+
export type ItemRenderMode = ValueOf<typeof ITEM_RENDER_MODE>;
|
|
4
12
|
|
|
5
13
|
export type Item = {
|
|
6
14
|
id: string;
|
|
@@ -16,10 +24,10 @@ export type InnerItem = Item & {
|
|
|
16
24
|
};
|
|
17
25
|
|
|
18
26
|
export type ItemSizeMap = {
|
|
19
|
-
[
|
|
20
|
-
[
|
|
21
|
-
[
|
|
22
|
-
[
|
|
27
|
+
[ITEM_RENDER_MODE.ShortLabel]: number;
|
|
28
|
+
[ITEM_RENDER_MODE.Collapsed]: number;
|
|
29
|
+
[ITEM_RENDER_MODE.Ellipsis]: number;
|
|
30
|
+
[ITEM_RENDER_MODE.Full]: number;
|
|
23
31
|
};
|
|
24
32
|
|
|
25
33
|
export type SizeMap = {
|
|
@@ -32,15 +40,15 @@ export type SizeMap = {
|
|
|
32
40
|
|
|
33
41
|
export type BreadcrumbsConfigChain = Array<
|
|
34
42
|
| {
|
|
35
|
-
element:
|
|
43
|
+
element: typeof ELEMENT_TYPE.Collapse;
|
|
36
44
|
width: number;
|
|
37
45
|
}
|
|
38
46
|
| {
|
|
39
|
-
element:
|
|
47
|
+
element: typeof ELEMENT_TYPE.Separator;
|
|
40
48
|
width: number;
|
|
41
49
|
}
|
|
42
50
|
| {
|
|
43
|
-
element:
|
|
51
|
+
element: typeof ELEMENT_TYPE.Item;
|
|
44
52
|
width: number;
|
|
45
53
|
item: InnerItem;
|
|
46
54
|
}
|
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BreadcrumbsConfig, InnerItem, Item, SizeMap } from '../types';
|
|
1
|
+
import { ELEMENT_TYPE, ITEM_RENDER_MODE } from '../constants';
|
|
2
|
+
import { BreadcrumbsConfig, InnerItem, Item, ItemRenderMode, SizeMap } from '../types';
|
|
3
3
|
|
|
4
4
|
type Chain = InnerItem[];
|
|
5
5
|
|
|
6
6
|
const RENDER_MODE_WEIGHT = {
|
|
7
|
-
[
|
|
8
|
-
[
|
|
9
|
-
[
|
|
10
|
-
[
|
|
7
|
+
[ITEM_RENDER_MODE.Full]: 0,
|
|
8
|
+
[ITEM_RENDER_MODE.ShortLabel]: 1,
|
|
9
|
+
[ITEM_RENDER_MODE.Ellipsis]: 100,
|
|
10
|
+
[ITEM_RENDER_MODE.Collapsed]: 10000,
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
const RENDER_MODE_WITH_WIDTH = [
|
|
13
|
+
const RENDER_MODE_WITH_WIDTH = [
|
|
14
|
+
ITEM_RENDER_MODE.Full,
|
|
15
|
+
ITEM_RENDER_MODE.ShortLabel,
|
|
16
|
+
ITEM_RENDER_MODE.Ellipsis,
|
|
17
|
+
] as ItemRenderMode[];
|
|
14
18
|
|
|
15
19
|
const startsWith =
|
|
16
20
|
(renderMode: ItemRenderMode) =>
|
|
17
21
|
(item: InnerItem, tail: Chain): Chain =>
|
|
18
22
|
[{ ...item, renderMode }, ...tail];
|
|
19
|
-
const startsWithFull = startsWith(
|
|
20
|
-
const startsWithShortLabel = startsWith(
|
|
21
|
-
const startsWithEllipsis = startsWith(
|
|
22
|
-
const startsWithCollapsed = startsWith(
|
|
23
|
+
const startsWithFull = startsWith(ITEM_RENDER_MODE.Full);
|
|
24
|
+
const startsWithShortLabel = startsWith(ITEM_RENDER_MODE.ShortLabel);
|
|
25
|
+
const startsWithEllipsis = startsWith(ITEM_RENDER_MODE.Ellipsis);
|
|
26
|
+
const startsWithCollapsed = startsWith(ITEM_RENDER_MODE.Collapsed);
|
|
23
27
|
|
|
24
28
|
type BuildSubChainOptions = {
|
|
25
29
|
useCollapse: boolean;
|
|
@@ -65,12 +69,12 @@ function buildSubChain(
|
|
|
65
69
|
const collapseAllRest = (lastElementRenderMode: ItemRenderMode, rest: InnerItem[]) =>
|
|
66
70
|
rest.map((element, index, array) => {
|
|
67
71
|
const lastItem = index === array.length - 1;
|
|
68
|
-
return { ...element, renderMode: lastItem ? lastElementRenderMode :
|
|
72
|
+
return { ...element, renderMode: lastItem ? lastElementRenderMode : ITEM_RENDER_MODE.Collapsed };
|
|
69
73
|
});
|
|
70
74
|
|
|
71
75
|
export function buildBreadcrumbsConfigs(items: Item[], sizeMap: SizeMap): BreadcrumbsConfig[] {
|
|
72
76
|
const chains: InnerItem[][] = [];
|
|
73
|
-
const [first, ...rest] = items.map(item => ({ ...item, renderMode:
|
|
77
|
+
const [first, ...rest] = items.map(item => ({ ...item, renderMode: ITEM_RENDER_MODE.Full }));
|
|
74
78
|
|
|
75
79
|
buildSubChain(rest, { useCollapse: true, useEllipse: true }).forEach(subset => {
|
|
76
80
|
chains.push(startsWithFull(first, subset));
|
|
@@ -78,10 +82,10 @@ export function buildBreadcrumbsConfigs(items: Item[], sizeMap: SizeMap): Breadc
|
|
|
78
82
|
});
|
|
79
83
|
|
|
80
84
|
/** Первый элемент можно схлопывать/сокращать только когда уже сокращено все что можно */
|
|
81
|
-
chains.push(startsWithEllipsis(first, collapseAllRest(
|
|
82
|
-
chains.push(startsWithEllipsis(first, collapseAllRest(
|
|
83
|
-
chains.push(startsWithCollapsed(first, collapseAllRest(
|
|
84
|
-
chains.push(startsWithCollapsed(first, collapseAllRest(
|
|
85
|
+
chains.push(startsWithEllipsis(first, collapseAllRest(ITEM_RENDER_MODE.Full, rest)));
|
|
86
|
+
chains.push(startsWithEllipsis(first, collapseAllRest(ITEM_RENDER_MODE.Ellipsis, rest)));
|
|
87
|
+
chains.push(startsWithCollapsed(first, collapseAllRest(ITEM_RENDER_MODE.Full, rest)));
|
|
88
|
+
chains.push(startsWithCollapsed(first, collapseAllRest(ITEM_RENDER_MODE.Ellipsis, rest)));
|
|
85
89
|
|
|
86
90
|
return chains.map(chain =>
|
|
87
91
|
chain.reduce<BreadcrumbsConfig>(
|
|
@@ -90,23 +94,23 @@ export function buildBreadcrumbsConfigs(items: Item[], sizeMap: SizeMap): Breadc
|
|
|
90
94
|
|
|
91
95
|
if (index && RENDER_MODE_WITH_WIDTH.includes(renderMode)) {
|
|
92
96
|
acc.width += sizeMap.separator;
|
|
93
|
-
acc.chain.push({ element:
|
|
97
|
+
acc.chain.push({ element: ELEMENT_TYPE.Separator, width: sizeMap.separator });
|
|
94
98
|
}
|
|
95
99
|
|
|
96
|
-
if (!acc.hasCollapsed && renderMode ===
|
|
100
|
+
if (!acc.hasCollapsed && renderMode === ITEM_RENDER_MODE.Collapsed) {
|
|
97
101
|
if (index) {
|
|
98
102
|
acc.width += sizeMap.separator;
|
|
99
|
-
acc.chain.push({ element:
|
|
103
|
+
acc.chain.push({ element: ELEMENT_TYPE.Separator, width: sizeMap.separator });
|
|
100
104
|
}
|
|
101
105
|
acc.hasCollapsed = true;
|
|
102
106
|
acc.width += sizeMap.collapse;
|
|
103
|
-
acc.chain.push({ element:
|
|
107
|
+
acc.chain.push({ element: ELEMENT_TYPE.Collapse, width: sizeMap.collapse });
|
|
104
108
|
}
|
|
105
109
|
|
|
106
110
|
const width = sizeMap.items[item.id][renderMode];
|
|
107
111
|
acc.weight += RENDER_MODE_WEIGHT[item.renderMode];
|
|
108
112
|
acc.width += width;
|
|
109
|
-
acc.chain.push({ element:
|
|
113
|
+
acc.chain.push({ element: ELEMENT_TYPE.Item, item, width });
|
|
110
114
|
|
|
111
115
|
return acc;
|
|
112
116
|
},
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ItemSizeMap, SizeMap } from '../types';
|
|
1
|
+
import { ITEM_RENDER_MODE } from '../constants';
|
|
2
|
+
import { ItemRenderMode, ItemSizeMap, SizeMap } from '../types';
|
|
3
3
|
|
|
4
4
|
const getEmptyItemSizeMap = (): ItemSizeMap => ({
|
|
5
|
-
[
|
|
6
|
-
[
|
|
7
|
-
[
|
|
8
|
-
[
|
|
5
|
+
[ITEM_RENDER_MODE.ShortLabel]: 0,
|
|
6
|
+
[ITEM_RENDER_MODE.Collapsed]: 0,
|
|
7
|
+
[ITEM_RENDER_MODE.Ellipsis]: 0,
|
|
8
|
+
[ITEM_RENDER_MODE.Full]: 0,
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
const getElementWidth = (element: HTMLElement): number => element.getBoundingClientRect().width;
|