@snack-uikit/toolbar 0.9.18 → 0.10.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 (67) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +2 -3
  3. package/dist/cjs/components/Toolbar/Toolbar.d.ts +1 -1
  4. package/dist/cjs/components/Toolbar/Toolbar.js +10 -10
  5. package/dist/cjs/components/Toolbar/helpers.d.ts +4 -4
  6. package/dist/cjs/components/Toolbar/helpers.js +7 -7
  7. package/dist/cjs/components/Toolbar/styles.module.css +11 -0
  8. package/dist/cjs/components/Toolbar/types.d.ts +7 -5
  9. package/dist/cjs/constants.d.ts +7 -2
  10. package/dist/cjs/constants.js +7 -2
  11. package/dist/cjs/helperComponents/BulkActions/BulkActions.d.ts +5 -0
  12. package/dist/cjs/helperComponents/BulkActions/BulkActions.js +131 -0
  13. package/dist/cjs/helperComponents/{DeleteAction → BulkActions}/index.d.ts +1 -1
  14. package/dist/cjs/helperComponents/{DeleteAction → BulkActions}/index.js +1 -1
  15. package/dist/{esm/helperComponents/DeleteAction → cjs/helperComponents/BulkActions}/styles.module.css +26 -0
  16. package/dist/cjs/helperComponents/BulkActions/types.d.ts +26 -0
  17. package/dist/cjs/helperComponents/Separator/Separator.js +5 -2
  18. package/dist/cjs/helperComponents/Separator/styles.module.css +7 -2
  19. package/dist/cjs/helperComponents/index.d.ts +1 -1
  20. package/dist/cjs/helperComponents/index.js +1 -1
  21. package/dist/esm/components/Toolbar/Toolbar.d.ts +1 -1
  22. package/dist/esm/components/Toolbar/Toolbar.js +8 -6
  23. package/dist/esm/components/Toolbar/helpers.d.ts +4 -4
  24. package/dist/esm/components/Toolbar/helpers.js +8 -10
  25. package/dist/esm/components/Toolbar/styles.module.css +11 -0
  26. package/dist/esm/components/Toolbar/types.d.ts +7 -5
  27. package/dist/esm/constants.d.ts +7 -2
  28. package/dist/esm/constants.js +7 -2
  29. package/dist/esm/helperComponents/BulkActions/BulkActions.d.ts +5 -0
  30. package/dist/esm/helperComponents/BulkActions/BulkActions.js +34 -0
  31. package/dist/esm/helperComponents/{DeleteAction → BulkActions}/index.d.ts +1 -1
  32. package/dist/esm/helperComponents/{DeleteAction → BulkActions}/index.js +1 -1
  33. package/dist/{cjs/helperComponents/DeleteAction → esm/helperComponents/BulkActions}/styles.module.css +26 -0
  34. package/dist/esm/helperComponents/BulkActions/types.d.ts +26 -0
  35. package/dist/esm/helperComponents/Separator/Separator.js +1 -1
  36. package/dist/esm/helperComponents/Separator/styles.module.css +7 -2
  37. package/dist/esm/helperComponents/index.d.ts +1 -1
  38. package/dist/esm/helperComponents/index.js +1 -1
  39. package/package.json +10 -9
  40. package/src/components/Toolbar/Toolbar.tsx +17 -14
  41. package/src/components/Toolbar/helpers.ts +11 -13
  42. package/src/components/Toolbar/styles.module.scss +18 -9
  43. package/src/components/Toolbar/types.ts +8 -5
  44. package/src/constants.ts +7 -2
  45. package/src/helperComponents/BulkActions/BulkActions.tsx +118 -0
  46. package/src/helperComponents/{DeleteAction → BulkActions}/index.ts +1 -1
  47. package/src/helperComponents/BulkActions/styles.module.scss +55 -0
  48. package/src/helperComponents/BulkActions/types.ts +29 -0
  49. package/src/helperComponents/Separator/Separator.tsx +5 -1
  50. package/src/helperComponents/Separator/styles.module.scss +10 -6
  51. package/src/helperComponents/index.ts +1 -1
  52. package/dist/cjs/helperComponents/DeleteAction/DeleteAction.d.ts +0 -14
  53. package/dist/cjs/helperComponents/DeleteAction/DeleteAction.js +0 -56
  54. package/dist/cjs/helperComponents/DeleteAction/types.d.ts +0 -3
  55. package/dist/esm/helperComponents/DeleteAction/DeleteAction.d.ts +0 -14
  56. package/dist/esm/helperComponents/DeleteAction/DeleteAction.js +0 -16
  57. package/dist/esm/helperComponents/DeleteAction/types.d.ts +0 -3
  58. package/src/helperComponents/DeleteAction/DeleteAction.tsx +0 -68
  59. package/src/helperComponents/DeleteAction/styles.module.scss +0 -24
  60. package/src/helperComponents/DeleteAction/types.ts +0 -5
  61. /package/dist/cjs/helperComponents/{DeleteAction → BulkActions}/constants.d.ts +0 -0
  62. /package/dist/cjs/helperComponents/{DeleteAction → BulkActions}/constants.js +0 -0
  63. /package/dist/cjs/helperComponents/{DeleteAction → BulkActions}/types.js +0 -0
  64. /package/dist/esm/helperComponents/{DeleteAction → BulkActions}/constants.d.ts +0 -0
  65. /package/dist/esm/helperComponents/{DeleteAction → BulkActions}/constants.js +0 -0
  66. /package/dist/esm/helperComponents/{DeleteAction → BulkActions}/types.js +0 -0
  67. /package/src/helperComponents/{DeleteAction → BulkActions}/constants.ts +0 -0
@@ -1,12 +1,17 @@
1
1
  export declare const TEST_IDS: {
2
2
  main: string;
3
3
  checkbox: string;
4
- deleteButton: string;
4
+ confirmAction: string;
5
+ rejectAction: string;
6
+ deleteAction: string;
7
+ deactivateAction: string;
8
+ disabledAction: string;
9
+ bulkActions: string;
10
+ moreBulkActionsButton: string;
5
11
  refreshButton: string;
6
12
  search: string;
7
13
  moreActionsButton: string;
8
14
  droplist: string;
9
15
  option: string;
10
- before: string;
11
16
  after: string;
12
17
  };
@@ -1,12 +1,17 @@
1
1
  export const TEST_IDS = {
2
2
  main: 'toolbar',
3
3
  checkbox: 'toolbar__checkbox',
4
- deleteButton: 'toolbar__delete-button',
4
+ confirmAction: 'toolbar__confirm-action',
5
+ rejectAction: 'toolbar__reject-action',
6
+ deleteAction: 'toolbar__delete-action',
7
+ deactivateAction: 'toolbar__deactivate-action',
8
+ disabledAction: 'toolbar__disabled-action',
9
+ bulkActions: 'toolbar__bulk-actions',
10
+ moreBulkActionsButton: 'toolbar__more-bulk-actions-button',
5
11
  refreshButton: 'toolbar__refresh-button',
6
12
  search: 'toolbar__search',
7
13
  moreActionsButton: 'toolbar__more-actions-button',
8
14
  droplist: 'toolbar__droplist',
9
15
  option: 'toolbar__droplist-option',
10
- before: 'toolbar__before',
11
16
  after: 'toolbar__after',
12
17
  };
@@ -0,0 +1,5 @@
1
+ import { RefObject } from 'react';
2
+ import { BulkActionsProps } from './types';
3
+ export declare function BulkActions({ actions, checked, onCheck, indeterminate, selectionMode, resizingContainerRef, }: BulkActionsProps & {
4
+ resizingContainerRef: RefObject<HTMLDivElement>;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useRef } from 'react';
3
+ import { ButtonFunction } from '@snack-uikit/button';
4
+ import { MoreSVG } from '@snack-uikit/icons';
5
+ import { Droplist } from '@snack-uikit/list';
6
+ import { Checkbox } from '@snack-uikit/toggles';
7
+ import { Tooltip } from '@snack-uikit/tooltip';
8
+ import { useDynamicList } from '@snack-uikit/utils';
9
+ import { TEST_IDS } from '../../constants';
10
+ import { SELECTION_MODE } from './constants';
11
+ import styles from './styles.module.css';
12
+ export function BulkActions({ actions, checked, onCheck, indeterminate, selectionMode = SELECTION_MODE.Multiple, resizingContainerRef, }) {
13
+ const handleKeyDown = useCallback((e) => {
14
+ if (e.key === ' ') {
15
+ onCheck === null || onCheck === void 0 ? void 0 : onCheck();
16
+ }
17
+ }, [onCheck]);
18
+ const parentContainerRef = useRef(null);
19
+ const { visibleItems, hiddenItems } = useDynamicList({
20
+ items: actions,
21
+ resizingContainerRef,
22
+ parentContainerRef,
23
+ maxVisibleItems: 3,
24
+ });
25
+ return (_jsxs(_Fragment, { children: [selectionMode === SELECTION_MODE.Multiple && (_jsx("div", { className: styles.checkboxWrapper, onClick: onCheck, tabIndex: 0, role: 'checkbox', "aria-checked": checked, onKeyDown: handleKeyDown, "data-test-id": TEST_IDS.checkbox, children: _jsx(Checkbox, { size: 's', checked: checked, indeterminate: indeterminate, tabIndex: -1 }) })), (checked || indeterminate) && (_jsxs("div", { className: styles.bulkActions, "data-test-id": TEST_IDS.bulkActions, ref: parentContainerRef, children: [visibleItems.map(({ label, icon: Icon, onClick, disabled, tooltip, 'data-test-id': testId }) => (_jsx(Tooltip, { tip: tooltip, open: tooltip ? undefined : false, placement: 'top', "data-test-id": `${testId}-tooltip`, children: _jsx(ButtonFunction, { className: styles.action, fullWidth: true, "data-test-id": testId, iconPosition: 'before', icon: _jsx(Icon, {}), label: label, size: 'm', onClick: onClick, disabled: disabled }) }, label))), hiddenItems.length > 0 && (_jsx(Droplist, { trigger: 'clickAndFocusVisible', closeDroplistOnItemClick: true, items: hiddenItems.map(({ label, icon: Icon, onClick, disabled, tooltip, 'data-test-id': testId }) => ({
26
+ id: label,
27
+ content: { option: label },
28
+ beforeContent: _jsx(Icon, {}),
29
+ onClick,
30
+ disabled,
31
+ itemWrapRender: item => (_jsx(Tooltip, { tip: tooltip, open: tooltip ? undefined : false, placement: 'right', "data-test-id": `${testId}-tooltip`, children: item })),
32
+ 'data-test-id': testId,
33
+ })), children: _jsx(ButtonFunction, { className: styles.moreActionButton, size: 'm', icon: _jsx(MoreSVG, {}), "data-test-id": TEST_IDS.moreBulkActionsButton }) }))] }))] }));
34
+ }
@@ -1,3 +1,3 @@
1
- export * from './DeleteAction';
1
+ export * from './BulkActions';
2
2
  export * from './constants';
3
3
  export * from './types';
@@ -1,3 +1,3 @@
1
- export * from './DeleteAction';
1
+ export * from './BulkActions';
2
2
  export * from './constants';
3
3
  export * from './types';
@@ -1,3 +1,17 @@
1
+ .bulkActions{
2
+ padding-right:var(--space-toolbar-bulk-action-padding-right, 12px);
3
+ gap:var(--space-toolbar-bulk-action-gap, 8px);
4
+ scrollbar-width:none;
5
+ display:flex;
6
+ flex-shrink:1;
7
+ justify-content:stretch;
8
+ min-width:0;
9
+ -ms-overflow-style:none;
10
+ }
11
+ .bulkActions::-webkit-scrollbar{
12
+ display:none;
13
+ }
14
+
1
15
  .checkboxWrapper{
2
16
  border-radius:var(--radius-toolbar-checkbox, 14px);
3
17
  width:var(--size-toolbar-checkbox, 40px);
@@ -15,4 +29,16 @@
15
29
  outline-style:var(--border-state-focus-s-border-style, solid);
16
30
  outline-color:var(--border-state-focus-s-border-color, );
17
31
  outline-color:var(--sys-available-complementary, #1c1c24);
32
+ }
33
+
34
+ .moreActionButton{
35
+ flex-shrink:0;
36
+ }
37
+
38
+ .action{
39
+ white-space:nowrap;
40
+ }
41
+ .action[data-full-width]:not([data-variant=icon-only]){
42
+ flex-shrink:0;
43
+ width:auto;
18
44
  }
@@ -0,0 +1,26 @@
1
+ import { JSXElementConstructor } from 'react';
2
+ import { TooltipProps } from '@snack-uikit/tooltip';
3
+ import { ValueOf, WithSupportProps } from '@snack-uikit/utils';
4
+ import { SELECTION_MODE } from './constants';
5
+ export type SelectionMode = ValueOf<typeof SELECTION_MODE>;
6
+ export type BulkAction = WithSupportProps<{
7
+ label: string;
8
+ icon: JSXElementConstructor<{
9
+ className?: string;
10
+ }>;
11
+ disabled?: boolean;
12
+ tooltip?: TooltipProps['tip'];
13
+ onClick?(): void;
14
+ }>;
15
+ export type BulkActionsProps = {
16
+ /** Список массовых действий */
17
+ actions: BulkAction[];
18
+ /** Колбек смены значения чекбокса */
19
+ onCheck?(): void;
20
+ /** Значение чекбокса */
21
+ checked?: boolean;
22
+ /** Состояние частичного выбора */
23
+ indeterminate?: boolean;
24
+ /** Режим выбора @default 'multiple'*/
25
+ selectionMode?: SelectionMode;
26
+ };
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import styles from './styles.module.css';
3
3
  export function Separator() {
4
- return _jsx("span", { className: styles.separator });
4
+ return (_jsx("div", { className: styles.separatorWrapper, children: _jsx("div", { className: styles.separator }) }));
5
5
  }
@@ -1,7 +1,12 @@
1
- .separator{
1
+ .separatorWrapper{
2
+ padding-top:var(--space-toolbar-separator-padding, 8px);
3
+ padding-bottom:var(--space-toolbar-separator-padding, 8px);
2
4
  flex-shrink:0;
5
+ }
6
+
7
+ .separator{
3
8
  box-sizing:border-box;
4
9
  width:var(--border-width-toolbar-separator, 1px);
5
- margin:var(--space-toolbar-separator-padding, 8px) 0;
10
+ height:100%;
6
11
  background-color:var(--sys-neutral-decor-default, #dde0ea);
7
12
  }
@@ -1,3 +1,3 @@
1
- export * from './DeleteAction';
1
+ export * from './BulkActions';
2
2
  export * from './Separator';
3
3
  export * from './MoreActions';
@@ -1,3 +1,3 @@
1
- export * from './DeleteAction';
1
+ export * from './BulkActions';
2
2
  export * from './Separator';
3
3
  export * from './MoreActions';
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "title": "Toolbar",
7
- "version": "0.9.18",
7
+ "version": "0.10.0",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -36,14 +36,15 @@
36
36
  "license": "Apache-2.0",
37
37
  "scripts": {},
38
38
  "dependencies": {
39
- "@snack-uikit/button": "0.19.5",
40
- "@snack-uikit/icons": "0.24.1",
41
- "@snack-uikit/list": "0.23.0",
42
- "@snack-uikit/search-private": "0.4.8",
43
- "@snack-uikit/tag": "0.12.1",
44
- "@snack-uikit/toggles": "0.13.4",
45
- "@snack-uikit/utils": "3.6.0",
39
+ "@snack-uikit/button": "0.19.6",
40
+ "@snack-uikit/icons": "0.24.2",
41
+ "@snack-uikit/list": "0.23.1",
42
+ "@snack-uikit/search-private": "0.4.9",
43
+ "@snack-uikit/tag": "0.12.2",
44
+ "@snack-uikit/toggles": "0.13.5",
45
+ "@snack-uikit/tooltip": "0.15.5",
46
+ "@snack-uikit/utils": "3.7.0",
46
47
  "classnames": "2.5.1"
47
48
  },
48
- "gitHead": "546ae486452e74e78f65fd9ce0044c13b058872c"
49
+ "gitHead": "3528e78aec23804dca44d311947c1dcd4ed3792b"
49
50
  }
@@ -1,4 +1,5 @@
1
1
  import cn from 'classnames';
2
+ import { useRef } from 'react';
2
3
 
3
4
  import { ButtonFunction } from '@snack-uikit/button';
4
5
  import { UpdateSVG } from '@snack-uikit/icons';
@@ -6,30 +7,32 @@ import { SearchPrivate } from '@snack-uikit/search-private';
6
7
  import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
7
8
 
8
9
  import { TEST_IDS } from '../../constants';
9
- import { DeleteAction, MoreActions, Separator } from '../../helperComponents';
10
- import { extractDeleteActionProps, isDeleteActionProps } from './helpers';
10
+ import { BulkActions, MoreActions, Separator } from '../../helperComponents';
11
+ import { extractBulkActionsProps, isBulkActionsProps } from './helpers';
11
12
  import styles from './styles.module.scss';
12
13
  import { CheckedToolbarProps, DefaultToolbarProps } from './types';
13
14
 
14
15
  export type ToolbarProps = WithSupportProps<DefaultToolbarProps | CheckedToolbarProps>;
15
16
 
16
- export function Toolbar({ className, before, after, outline, moreActions, onRefresh, search, ...rest }: ToolbarProps) {
17
- const needsDeleteAction = isDeleteActionProps(rest);
18
- const hasLeftSideElements = Boolean(needsDeleteAction || before || onRefresh);
17
+ export function Toolbar({ className, after, outline, moreActions, onRefresh, search, ...rest }: ToolbarProps) {
18
+ const needsBulkActions = isBulkActionsProps(rest);
19
+ const hasLeftSideElements = Boolean(needsBulkActions || onRefresh);
20
+ const resizingContainerRef = useRef<HTMLDivElement>(null);
19
21
 
20
22
  return (
21
- <div className={cn(styles.container, className)} {...extractSupportProps(rest)} data-outline={outline || undefined}>
23
+ <div
24
+ className={cn(styles.container, className)}
25
+ {...extractSupportProps(rest)}
26
+ data-outline={outline || undefined}
27
+ ref={resizingContainerRef}
28
+ >
22
29
  {hasLeftSideElements && (
23
- <div className={styles.flexRow}>
24
- {needsDeleteAction && <DeleteAction {...extractDeleteActionProps(rest)} />}
25
-
26
- {before && (
27
- <div data-test-id={TEST_IDS.before} className={styles.actions}>
28
- {before}
29
- </div>
30
+ <div className={styles.beforeSearch}>
31
+ {needsBulkActions && (
32
+ <BulkActions {...extractBulkActionsProps(rest)} resizingContainerRef={resizingContainerRef} />
30
33
  )}
31
34
 
32
- {(needsDeleteAction || before) && <Separator />}
35
+ {needsBulkActions && <Separator />}
33
36
 
34
37
  {onRefresh && (
35
38
  <>
@@ -1,24 +1,22 @@
1
1
  import { ToolbarProps } from './Toolbar';
2
- import { CheckedToolbarProps } from './types';
2
+ import { ToolbarBulkActionProps } from './types';
3
3
 
4
- export function extractDeleteActionProps({
4
+ export function extractBulkActionsProps({
5
5
  onCheck,
6
6
  checked,
7
7
  indeterminate,
8
- onDelete,
8
+ bulkActions = [],
9
9
  selectionMode,
10
- }: Omit<CheckedToolbarProps, 'onRefresh'>) {
11
- return { onCheck, checked, indeterminate, onDelete, selectionMode };
10
+ }: ToolbarBulkActionProps) {
11
+ return { onCheck, checked, indeterminate, actions: bulkActions, selectionMode };
12
12
  }
13
13
 
14
- export function isDeleteActionProps(props: Partial<ToolbarProps>): props is CheckedToolbarProps {
14
+ export function isBulkActionsProps(props: Partial<ToolbarProps>): props is ToolbarBulkActionProps {
15
15
  return (
16
- ('onDelete' in props && props.onDelete !== undefined) ||
17
- ('checked' in props &&
18
- props.checked !== undefined &&
19
- 'onCheck' in props &&
20
- props.onCheck !== undefined &&
21
- 'selectionMode' in props &&
22
- props.selectionMode === 'multiple')
16
+ 'bulkActions' in props &&
17
+ Array.isArray(props.bulkActions) &&
18
+ props.bulkActions.length > 0 &&
19
+ 'selectionMode' in props &&
20
+ props.selectionMode === 'multiple'
23
21
  );
24
22
  }
@@ -1,12 +1,12 @@
1
- @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
2
- @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-toolbar';
1
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element' as element;
2
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-toolbar' as toolbar;
3
3
 
4
4
  .toolbar {
5
5
  box-sizing: border-box;
6
6
  }
7
7
 
8
8
  .container {
9
- @include styles-tokens-element.composite-var(styles-tokens-toolbar.$toolbar-container);
9
+ @include element.composite-var(toolbar.$toolbar-container);
10
10
 
11
11
  position: relative;
12
12
 
@@ -15,11 +15,11 @@
15
15
 
16
16
  box-sizing: border-box;
17
17
 
18
- background-color: styles-tokens-element.$sys-neutral-background1-level;
18
+ background-color: element.$sys-neutral-background1-level;
19
19
 
20
20
  &[data-outline] {
21
21
  &::before {
22
- @include styles-tokens-element.composite-var(styles-tokens-toolbar.$toolbar-container-outline);
22
+ @include element.composite-var(toolbar.$toolbar-container-outline);
23
23
 
24
24
  pointer-events: none;
25
25
  content: '';
@@ -32,7 +32,7 @@
32
32
  width: 100%;
33
33
  height: 100%;
34
34
 
35
- border-color: styles-tokens-element.$sys-neutral-decor-default;
35
+ border-color: element.$sys-neutral-decor-default;
36
36
  border-style: solid;
37
37
  }
38
38
  }
@@ -40,17 +40,19 @@
40
40
  }
41
41
 
42
42
  .search {
43
- @include styles-tokens-element.composite-var(styles-tokens-toolbar.$toolbar-search);
43
+ @include element.composite-var(toolbar.$toolbar-search);
44
44
 
45
45
  &:focus-within {
46
- @include styles-tokens-element.outline-var(styles-tokens-element.$container-focused-s);
46
+ @include element.outline-var(element.$container-focused-s);
47
47
 
48
48
  position: relative;
49
- outline-color: styles-tokens-element.$sys-available-complementary;
49
+ outline-color: element.$sys-available-complementary;
50
50
  }
51
51
  }
52
52
 
53
53
  .updateButton {
54
+ flex-shrink: 0;
55
+
54
56
  svg {
55
57
  transform: rotate(360deg);
56
58
  transition: transform 0.5s;
@@ -64,6 +66,13 @@
64
66
  }
65
67
  }
66
68
 
69
+ .beforeSearch {
70
+ display: flex;
71
+ flex-shrink: 1;
72
+ box-sizing: border-box;
73
+ min-width: 0;
74
+ }
75
+
67
76
  .flexRow {
68
77
  display: flex;
69
78
  flex-shrink: 0;
@@ -1,13 +1,11 @@
1
1
  import { ReactNode } from 'react';
2
2
 
3
- import { DeleteActionProps, MoreActionsProps } from '../../helperComponents';
3
+ import { BulkActionsProps, MoreActionsProps } from '../../helperComponents';
4
4
  import { NeverOrUndefined, RequireAtLeastOne } from './typesUtils';
5
5
 
6
6
  type OptionalProps = {
7
7
  /** Колбек обновления */
8
8
  onRefresh?(): void;
9
- /** Дополнительный слот в начале Тулбара */
10
- before?: ReactNode;
11
9
  /** Дополнительный слот в конце тулбара */
12
10
  after?: ReactNode;
13
11
  /** Элементы выпадающего списка кнопки с действиями */
@@ -35,8 +33,13 @@ export type CommonToolbarProps = {
35
33
  outline?: boolean;
36
34
  };
37
35
 
38
- export type CheckedToolbarProps = CommonToolbarProps & DeleteActionProps & OptionalProps;
36
+ export type ToolbarBulkActionProps = Omit<BulkActionsProps, 'actions'> & {
37
+ /** Список массовых действий */
38
+ bulkActions?: BulkActionsProps['actions'];
39
+ };
40
+
41
+ export type CheckedToolbarProps = CommonToolbarProps & ToolbarBulkActionProps & OptionalProps;
39
42
 
40
43
  export type DefaultToolbarProps = CommonToolbarProps &
41
- NeverOrUndefined<DeleteActionProps> &
44
+ NeverOrUndefined<ToolbarBulkActionProps> &
42
45
  RequireAtLeastOne<OptionalProps>;
package/src/constants.ts CHANGED
@@ -1,12 +1,17 @@
1
1
  export const TEST_IDS = {
2
2
  main: 'toolbar',
3
3
  checkbox: 'toolbar__checkbox',
4
- deleteButton: 'toolbar__delete-button',
4
+ confirmAction: 'toolbar__confirm-action',
5
+ rejectAction: 'toolbar__reject-action',
6
+ deleteAction: 'toolbar__delete-action',
7
+ deactivateAction: 'toolbar__deactivate-action',
8
+ disabledAction: 'toolbar__disabled-action',
9
+ bulkActions: 'toolbar__bulk-actions',
10
+ moreBulkActionsButton: 'toolbar__more-bulk-actions-button',
5
11
  refreshButton: 'toolbar__refresh-button',
6
12
  search: 'toolbar__search',
7
13
  moreActionsButton: 'toolbar__more-actions-button',
8
14
  droplist: 'toolbar__droplist',
9
15
  option: 'toolbar__droplist-option',
10
- before: 'toolbar__before',
11
16
  after: 'toolbar__after',
12
17
  };
@@ -0,0 +1,118 @@
1
+ import { KeyboardEvent, RefObject, useCallback, useRef } from 'react';
2
+
3
+ import { ButtonFunction } from '@snack-uikit/button';
4
+ import { MoreSVG } from '@snack-uikit/icons';
5
+ import { Droplist } from '@snack-uikit/list';
6
+ import { Checkbox } from '@snack-uikit/toggles';
7
+ import { Tooltip } from '@snack-uikit/tooltip';
8
+ import { useDynamicList } from '@snack-uikit/utils';
9
+
10
+ import { TEST_IDS } from '../../constants';
11
+ import { SELECTION_MODE } from './constants';
12
+ import styles from './styles.module.scss';
13
+ import { BulkActionsProps } from './types';
14
+
15
+ export function BulkActions({
16
+ actions,
17
+ checked,
18
+ onCheck,
19
+ indeterminate,
20
+ selectionMode = SELECTION_MODE.Multiple,
21
+ resizingContainerRef,
22
+ }: BulkActionsProps & {
23
+ resizingContainerRef: RefObject<HTMLDivElement>;
24
+ }) {
25
+ const handleKeyDown = useCallback(
26
+ (e: KeyboardEvent<HTMLDivElement>) => {
27
+ if (e.key === ' ') {
28
+ onCheck?.();
29
+ }
30
+ },
31
+ [onCheck],
32
+ );
33
+
34
+ const parentContainerRef = useRef<HTMLDivElement>(null);
35
+
36
+ const { visibleItems, hiddenItems } = useDynamicList({
37
+ items: actions,
38
+ resizingContainerRef,
39
+ parentContainerRef,
40
+ maxVisibleItems: 3,
41
+ });
42
+
43
+ return (
44
+ <>
45
+ {selectionMode === SELECTION_MODE.Multiple && (
46
+ <div
47
+ className={styles.checkboxWrapper}
48
+ onClick={onCheck}
49
+ tabIndex={0}
50
+ role='checkbox'
51
+ aria-checked={checked}
52
+ onKeyDown={handleKeyDown}
53
+ data-test-id={TEST_IDS.checkbox}
54
+ >
55
+ <Checkbox size='s' checked={checked} indeterminate={indeterminate} tabIndex={-1} />
56
+ </div>
57
+ )}
58
+
59
+ {(checked || indeterminate) && (
60
+ <div className={styles.bulkActions} data-test-id={TEST_IDS.bulkActions} ref={parentContainerRef}>
61
+ {visibleItems.map(({ label, icon: Icon, onClick, disabled, tooltip, 'data-test-id': testId }) => (
62
+ <Tooltip
63
+ tip={tooltip}
64
+ key={label}
65
+ open={tooltip ? undefined : false}
66
+ placement='top'
67
+ data-test-id={`${testId}-tooltip`}
68
+ >
69
+ <ButtonFunction
70
+ className={styles.action}
71
+ fullWidth
72
+ data-test-id={testId}
73
+ iconPosition='before'
74
+ icon={<Icon />}
75
+ label={label}
76
+ size='m'
77
+ onClick={onClick}
78
+ disabled={disabled}
79
+ />
80
+ </Tooltip>
81
+ ))}
82
+
83
+ {hiddenItems.length > 0 && (
84
+ <Droplist
85
+ trigger='clickAndFocusVisible'
86
+ closeDroplistOnItemClick
87
+ items={hiddenItems.map(({ label, icon: Icon, onClick, disabled, tooltip, 'data-test-id': testId }) => ({
88
+ id: label,
89
+ content: { option: label },
90
+ beforeContent: <Icon />,
91
+ onClick,
92
+ disabled,
93
+ itemWrapRender: item => (
94
+ <Tooltip
95
+ tip={tooltip}
96
+ open={tooltip ? undefined : false}
97
+ placement='right'
98
+ data-test-id={`${testId}-tooltip`}
99
+ >
100
+ {item}
101
+ </Tooltip>
102
+ ),
103
+ 'data-test-id': testId,
104
+ }))}
105
+ >
106
+ <ButtonFunction
107
+ className={styles.moreActionButton}
108
+ size='m'
109
+ icon={<MoreSVG />}
110
+ data-test-id={TEST_IDS.moreBulkActionsButton}
111
+ />
112
+ </Droplist>
113
+ )}
114
+ </div>
115
+ )}
116
+ </>
117
+ );
118
+ }
@@ -1,3 +1,3 @@
1
- export * from './DeleteAction';
1
+ export * from './BulkActions';
2
2
  export * from './constants';
3
3
  export * from './types';
@@ -0,0 +1,55 @@
1
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-toolbar' as toolbar;
2
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element' as element;
3
+
4
+ .bulkActions {
5
+ @include toolbar.composite-var(toolbar.$toolbar-bulk-action);
6
+
7
+ scrollbar-width: none; /* Firefox */
8
+
9
+ display: flex;
10
+ flex-shrink: 1;
11
+ justify-content: stretch;
12
+
13
+ min-width: 0;
14
+
15
+ /* Hide scrollbar */
16
+ -ms-overflow-style: none; /* IE and Edge */
17
+
18
+ &::-webkit-scrollbar {
19
+ display: none; /* Chrome, Safari and Opera */
20
+ }
21
+ }
22
+
23
+ .checkboxWrapper {
24
+ @include toolbar.composite-var(toolbar.$toolbar-checkbox);
25
+
26
+ cursor: pointer;
27
+
28
+ position: relative;
29
+
30
+ display: flex;
31
+ flex-shrink: 0;
32
+ align-items: center;
33
+ justify-content: center;
34
+
35
+ box-sizing: border-box;
36
+
37
+ &:focus-visible {
38
+ @include element.outline-var(element.$container-focused-s);
39
+
40
+ outline-color: toolbar.$sys-available-complementary;
41
+ }
42
+ }
43
+
44
+ .moreActionButton {
45
+ flex-shrink: 0;
46
+ }
47
+
48
+ .action {
49
+ white-space: nowrap;
50
+
51
+ &[data-full-width]:not([data-variant="icon-only"]) {
52
+ flex-shrink: 0;
53
+ width: auto;
54
+ }
55
+ }
@@ -0,0 +1,29 @@
1
+ import { JSXElementConstructor } from 'react';
2
+
3
+ import { TooltipProps } from '@snack-uikit/tooltip';
4
+ import { ValueOf, WithSupportProps } from '@snack-uikit/utils';
5
+
6
+ import { SELECTION_MODE } from './constants';
7
+
8
+ export type SelectionMode = ValueOf<typeof SELECTION_MODE>;
9
+
10
+ export type BulkAction = WithSupportProps<{
11
+ label: string;
12
+ icon: JSXElementConstructor<{ className?: string }>;
13
+ disabled?: boolean;
14
+ tooltip?: TooltipProps['tip'];
15
+ onClick?(): void;
16
+ }>;
17
+
18
+ export type BulkActionsProps = {
19
+ /** Список массовых действий */
20
+ actions: BulkAction[];
21
+ /** Колбек смены значения чекбокса */
22
+ onCheck?(): void;
23
+ /** Значение чекбокса */
24
+ checked?: boolean;
25
+ /** Состояние частичного выбора */
26
+ indeterminate?: boolean;
27
+ /** Режим выбора @default 'multiple'*/
28
+ selectionMode?: SelectionMode;
29
+ };