@snack-uikit/card 0.16.15-preview-31ae96b0.0 → 0.16.15

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 (31) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +3 -4
  3. package/dist/cjs/components/Card/Card.d.ts +8 -12
  4. package/dist/cjs/components/Card/Card.js +27 -31
  5. package/dist/cjs/components/Card/constants.d.ts +1 -0
  6. package/dist/cjs/components/Card/constants.js +7 -0
  7. package/dist/cjs/components/Card/styles.module.css +10 -3
  8. package/dist/cjs/components/Footer/components/Action/Action.js +0 -14
  9. package/dist/cjs/components/Footer/components/Promo/Promo.js +0 -7
  10. package/dist/cjs/components/FunctionBadge/FunctionBadge.js +0 -1
  11. package/dist/cjs/constants.d.ts +1 -0
  12. package/dist/cjs/constants.js +2 -1
  13. package/dist/esm/components/Card/Card.d.ts +8 -12
  14. package/dist/esm/components/Card/Card.js +19 -11
  15. package/dist/esm/components/Card/constants.d.ts +1 -0
  16. package/dist/esm/components/Card/constants.js +1 -0
  17. package/dist/esm/components/Card/styles.module.css +10 -3
  18. package/dist/esm/components/Footer/components/Action/Action.js +1 -13
  19. package/dist/esm/components/Footer/components/Promo/Promo.js +1 -7
  20. package/dist/esm/components/FunctionBadge/FunctionBadge.js +0 -1
  21. package/dist/esm/constants.d.ts +1 -0
  22. package/dist/esm/constants.js +1 -0
  23. package/package.json +5 -4
  24. package/src/components/Card/Card.tsx +44 -39
  25. package/src/components/Card/constants.ts +1 -0
  26. package/src/components/Card/styles.module.scss +12 -2
  27. package/src/components/Footer/components/Action/Action.tsx +2 -25
  28. package/src/components/Footer/components/Promo/Promo.tsx +1 -11
  29. package/src/components/FunctionBadge/FunctionBadge.tsx +0 -2
  30. package/src/components/FunctionBadge/styles.module.scss +1 -1
  31. package/src/constants.ts +1 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,15 @@
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.16.15 (2025-02-04)
7
+
8
+ ### Only dependencies have been changed
9
+ * [@snack-uikit/list@0.24.0](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/list/CHANGELOG.md)
10
+
11
+
12
+
13
+
14
+
6
15
  ## 0.16.14 (2025-01-23)
7
16
 
8
17
  ### Only dependencies have been changed
package/README.md CHANGED
@@ -32,6 +32,7 @@ import { Card, SearchPrivate } from "@snack-uikit/card";
32
32
  | checked | `boolean` | - | Управление состоянием выбран/не выбран |
33
33
  | outline | `boolean` | - | Управление состоянием наличия обводки |
34
34
  | multipleSelection | `boolean` | - | Отображение галочки для режима массового выделения карточек |
35
+ | onClick | `(e: MouseEvent<HTMLDivElement \| HTMLAnchorElement, MouseEvent>) => void` | - | Колбек на клик по карточке |
35
36
  | size | enum Size: `"s"`, `"m"`, `"l"` | - | Размер |
36
37
  | promoBadge | `string \| Pick<PromoTagProps, "text" \| "appearance">` | - | PromoBadge |
37
38
  | children | `ReactNode` | - | Вложенный контент |
@@ -41,10 +42,8 @@ import { Card, SearchPrivate } from "@snack-uikit/card";
41
42
  | functionBadge | `ReactNode` | - | Вложенный FunctionBadge |
42
43
  | className | `string` | - | CSS-класс для элемента с контентом |
43
44
  | href | `string` | - | Ссылка карточки |
44
- | target | `HTMLAttributeAnchorTarget` | - | Атрибут target для ссылки |
45
- | onClick | `(e: MouseEvent<HTMLAnchorElement \| HTMLDivElement, MouseEvent>) => void` | - | Колбек на клик по карточке |
46
- | onKeyDown | `KeyboardEventHandler<HTMLAnchorElement \| HTMLDivElement>` | - | Колбек нажатия клавиши клавиатуры |
47
- | ref | `LegacyRef<HTMLAnchorElement \| HTMLDivElement>` | - | Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref). @see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs} |
45
+ | onKeyDown | `KeyboardEventHandler<HTMLDivElement>` | - | Колбек нажатия клавиши клавиатуры |
46
+ | ref | `LegacyRef<HTMLDivElement>` | - | Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref). @see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs} |
48
47
  | key | `Key` | - | |
49
48
 
50
49
 
@@ -1,4 +1,4 @@
1
- import { HTMLAttributeAnchorTarget, KeyboardEventHandler, MouseEvent, ReactElement, ReactNode } from 'react';
1
+ import { KeyboardEventHandler, MouseEvent, ReactElement, ReactNode } from 'react';
2
2
  import { PromoTagProps } from '@snack-uikit/promo-tag';
3
3
  import { WithSupportProps } from '@snack-uikit/utils';
4
4
  import { Size } from '../../types';
@@ -12,6 +12,8 @@ export type CardProps = WithSupportProps<{
12
12
  outline?: boolean;
13
13
  /** Отображение галочки для режима массового выделения карточек */
14
14
  multipleSelection?: boolean;
15
+ /** Колбек на клик по карточке */
16
+ onClick?(e: MouseEvent<HTMLDivElement | HTMLAnchorElement>): void;
15
17
  /** Размер */
16
18
  size?: Size;
17
19
  /** PromoBadge */
@@ -30,12 +32,8 @@ export type CardProps = WithSupportProps<{
30
32
  className?: string;
31
33
  /** Ссылка карточки */
32
34
  href?: string;
33
- /** Атрибут target для ссылки */
34
- target?: HTMLAttributeAnchorTarget;
35
- /** Колбек на клик по карточке */
36
- onClick?(e: MouseEvent<HTMLAnchorElement | HTMLDivElement>): void;
37
35
  /** Колбек нажатия клавиши клавиатуры */
38
- onKeyDown?: KeyboardEventHandler<HTMLAnchorElement | HTMLDivElement>;
36
+ onKeyDown?: KeyboardEventHandler<HTMLDivElement>;
39
37
  }>;
40
38
  export declare const Card: import("react").ForwardRefExoticComponent<{
41
39
  'data-test-id'?: string;
@@ -48,6 +46,8 @@ export declare const Card: import("react").ForwardRefExoticComponent<{
48
46
  outline?: boolean;
49
47
  /** Отображение галочки для режима массового выделения карточек */
50
48
  multipleSelection?: boolean;
49
+ /** Колбек на клик по карточке */
50
+ onClick?(e: MouseEvent<HTMLDivElement | HTMLAnchorElement>): void;
51
51
  /** Размер */
52
52
  size?: Size;
53
53
  /** PromoBadge */
@@ -66,10 +66,6 @@ export declare const Card: import("react").ForwardRefExoticComponent<{
66
66
  className?: string;
67
67
  /** Ссылка карточки */
68
68
  href?: string;
69
- /** Атрибут target для ссылки */
70
- target?: HTMLAttributeAnchorTarget;
71
- /** Колбек на клик по карточке */
72
- onClick?(e: MouseEvent<HTMLAnchorElement | HTMLDivElement>): void;
73
69
  /** Колбек нажатия клавиши клавиатуры */
74
- onKeyDown?: KeyboardEventHandler<HTMLAnchorElement | HTMLDivElement>;
75
- } & import("react").RefAttributes<HTMLDivElement | HTMLAnchorElement>>;
70
+ onKeyDown?: KeyboardEventHandler<HTMLDivElement>;
71
+ } & import("react").RefAttributes<HTMLDivElement>>;
@@ -19,34 +19,15 @@ Object.defineProperty(exports, "__esModule", {
19
19
  exports.Card = void 0;
20
20
  const jsx_runtime_1 = require("react/jsx-runtime");
21
21
  const classnames_1 = __importDefault(require("classnames"));
22
+ const merge_refs_1 = __importDefault(require("merge-refs"));
22
23
  const react_1 = require("react");
23
24
  const typography_1 = require("@snack-uikit/typography");
24
25
  const utils_1 = require("@snack-uikit/utils");
25
26
  const constants_1 = require("../../constants");
26
27
  const context_1 = require("../../context");
27
28
  const helperComponents_1 = require("../../helperComponents");
29
+ const constants_2 = require("./constants");
28
30
  const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
29
- const CardTag = (0, react_1.forwardRef)((_a, ref) => {
30
- var {
31
- children,
32
- href,
33
- target,
34
- disabled
35
- } = _a,
36
- props = __rest(_a, ["children", "href", "target", "disabled"]);
37
- if (href && !disabled) {
38
- return (0, jsx_runtime_1.jsx)("a", Object.assign({}, props, {
39
- href: href,
40
- target: target,
41
- ref: ref,
42
- children: children
43
- }));
44
- }
45
- return (0, jsx_runtime_1.jsx)("div", Object.assign({}, props, {
46
- ref: ref,
47
- children: children
48
- }));
49
- });
50
31
  exports.Card = (0, react_1.forwardRef)((_a, ref) => {
51
32
  var {
52
33
  onClick,
@@ -63,18 +44,28 @@ exports.Card = (0, react_1.forwardRef)((_a, ref) => {
63
44
  image,
64
45
  className,
65
46
  href,
66
- onKeyDown,
67
- target
47
+ onKeyDown: onKeyDownProp
68
48
  } = _a,
69
- rest = __rest(_a, ["onClick", "disabled", "checked", "outline", "multipleSelection", "size", "children", "header", "footer", "functionBadge", "promoBadge", "image", "className", "href", "onKeyDown", "target"]);
49
+ rest = __rest(_a, ["onClick", "disabled", "checked", "outline", "multipleSelection", "size", "children", "header", "footer", "functionBadge", "promoBadge", "image", "className", "href", "onKeyDown"]);
50
+ const localDivRef = (0, react_1.useRef)(null);
51
+ const localAnchorRef = (0, react_1.useRef)(null);
52
+ const onKeyDown = (0, react_1.useCallback)(e => {
53
+ var _a, _b;
54
+ if (e.target === localDivRef.current) {
55
+ if (constants_2.TRIGGER_CLICK_KEY_CODES.includes(e.code) || e.key === ' ') {
56
+ href ? (_a = localAnchorRef.current) === null || _a === void 0 ? void 0 : _a.click() : (_b = localDivRef.current) === null || _b === void 0 ? void 0 : _b.click();
57
+ }
58
+ onKeyDownProp === null || onKeyDownProp === void 0 ? void 0 : onKeyDownProp(e);
59
+ }
60
+ }, [href, onKeyDownProp]);
70
61
  const supportProps = (0, utils_1.extractSupportProps)(rest);
71
62
  return (0, jsx_runtime_1.jsx)(context_1.CardContext.Provider, {
72
63
  value: {
73
64
  size,
74
65
  disabled
75
66
  },
76
- children: (0, jsx_runtime_1.jsxs)(CardTag, Object.assign({
77
- ref: ref,
67
+ children: (0, jsx_runtime_1.jsxs)("div", Object.assign({
68
+ ref: (0, merge_refs_1.default)(ref, localDivRef),
78
69
  className: (0, classnames_1.default)(styles_module_scss_1.default.card, className)
79
70
  }, supportProps, {
80
71
  onClick: onClick,
@@ -82,15 +73,20 @@ exports.Card = (0, react_1.forwardRef)((_a, ref) => {
82
73
  "data-checked": checked || undefined,
83
74
  "data-outline": outline || undefined,
84
75
  "data-pointer": onClick ? true : undefined,
85
- disabled: disabled,
86
- href: href,
87
- target: target,
76
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
88
77
  tabIndex: 0,
89
78
  onKeyDown: onKeyDown,
90
79
  children: [image, (0, jsx_runtime_1.jsxs)("div", {
91
80
  className: styles_module_scss_1.default.composition,
92
81
  tabIndex: -1,
93
- children: [!disabled && functionBadge && (0, jsx_runtime_1.jsx)(helperComponents_1.FunctionBadgeWrapper, {
82
+ children: [href && (0, jsx_runtime_1.jsx)("a", {
83
+ ref: localAnchorRef,
84
+ "data-test-id": constants_1.TEST_IDS.anchor,
85
+ tabIndex: -1,
86
+ href: href,
87
+ className: styles_module_scss_1.default.anchor,
88
+ "aria-label": supportProps['aria-label']
89
+ }), !disabled && functionBadge && (0, jsx_runtime_1.jsx)(helperComponents_1.FunctionBadgeWrapper, {
94
90
  className: styles_module_scss_1.default.functionBadgeWrapper,
95
91
  children: functionBadge
96
92
  }), (0, jsx_runtime_1.jsx)("div", {
@@ -98,7 +94,7 @@ exports.Card = (0, react_1.forwardRef)((_a, ref) => {
98
94
  children: (0, jsx_runtime_1.jsxs)("div", {
99
95
  className: styles_module_scss_1.default.content,
100
96
  "data-size": size,
101
- children: [header, children && (0, jsx_runtime_1.jsx)(typography_1.Typography, {
97
+ children: [header || null, children && (0, jsx_runtime_1.jsx)(typography_1.Typography, {
102
98
  family: 'sans',
103
99
  size: size,
104
100
  purpose: 'body',
@@ -0,0 +1 @@
1
+ export declare const TRIGGER_CLICK_KEY_CODES: string[];
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.TRIGGER_CLICK_KEY_CODES = void 0;
7
+ exports.TRIGGER_CLICK_KEY_CODES = ['Enter', 'Space'];
@@ -32,9 +32,6 @@
32
32
  margin:0;
33
33
  padding:0;
34
34
  text-align:left;
35
- text-decoration:none;
36
- -webkit-text-decoration-skip-ink:none;
37
- text-decoration-skip-ink:none;
38
35
  background-color:var(--sys-neutral-background1-level, #fdfdfd);
39
36
  border:0;
40
37
  outline-color:transparent;
@@ -140,6 +137,16 @@
140
137
  cursor:not-allowed;
141
138
  }
142
139
 
140
+ .anchor{
141
+ position:absolute;
142
+ z-index:1;
143
+ top:0;
144
+ right:0;
145
+ bottom:0;
146
+ left:0;
147
+ border-radius:var(--radius-card-container, 20px);
148
+ }
149
+
143
150
  .body{
144
151
  display:block;
145
152
  flex-grow:1;
@@ -33,28 +33,14 @@ function FooterAction(_a) {
33
33
  const {
34
34
  disabled
35
35
  } = (0, context_1.useCardContext)();
36
- const handleButtonClick = e => {
37
- var _a;
38
- e.stopPropagation();
39
- e.preventDefault();
40
- (_a = button === null || button === void 0 ? void 0 : button.onClick) === null || _a === void 0 ? void 0 : _a.call(button, e);
41
- };
42
- const handleSecondaryButtonClick = e => {
43
- var _a;
44
- e.stopPropagation();
45
- e.preventDefault();
46
- (_a = button === null || button === void 0 ? void 0 : button.onClick) === null || _a === void 0 ? void 0 : _a.call(button, e);
47
- };
48
36
  return (0, jsx_runtime_1.jsxs)("div", Object.assign({
49
37
  className: (0, classnames_1.default)(styles_module_scss_1.default.action, className)
50
38
  }, (0, utils_1.excludeSupportProps)(rest), {
51
39
  children: [(0, jsx_runtime_1.jsx)(button_1.ButtonFilled, Object.assign({}, button, {
52
- onClick: handleButtonClick,
53
40
  appearance: 'primary',
54
41
  size: 'm',
55
42
  disabled: disabled
56
43
  })), secondaryButton && (0, jsx_runtime_1.jsx)(button_1.ButtonTonal, Object.assign({}, secondaryButton, {
57
- onClick: handleSecondaryButtonClick,
58
44
  appearance: 'neutral',
59
45
  size: 'm',
60
46
  disabled: disabled
@@ -35,17 +35,10 @@ function FooterPromo(_a) {
35
35
  const {
36
36
  disabled
37
37
  } = (0, context_1.useCardContext)();
38
- const handleButtonClick = e => {
39
- var _a;
40
- e.stopPropagation();
41
- e.preventDefault();
42
- (_a = button === null || button === void 0 ? void 0 : button.onClick) === null || _a === void 0 ? void 0 : _a.call(button, e);
43
- };
44
38
  return (0, jsx_runtime_1.jsxs)("div", Object.assign({
45
39
  className: (0, classnames_1.default)(styles_module_scss_1.default.promo, className)
46
40
  }, (0, utils_1.excludeSupportProps)(rest), {
47
41
  children: [button && (0, jsx_runtime_1.jsx)(button_1.ButtonFilled, Object.assign({}, button, {
48
- onClick: handleButtonClick,
49
42
  appearance: 'primary',
50
43
  size: 'm',
51
44
  disabled: disabled
@@ -41,7 +41,6 @@ function FunctionBadge(_ref) {
41
41
  }, [isOpen, setVisible]);
42
42
  const onClick = (0, react_1.useCallback)(e => {
43
43
  e.stopPropagation();
44
- e.preventDefault();
45
44
  setIsOpen(isOpen => !isOpen);
46
45
  }, []);
47
46
  return (0, jsx_runtime_1.jsx)("span", {
@@ -14,4 +14,5 @@ export declare const TEST_IDS: {
14
14
  title: string;
15
15
  description: string;
16
16
  metadata: string;
17
+ anchor: string;
17
18
  };
@@ -19,5 +19,6 @@ exports.TEST_IDS = {
19
19
  emblemIcon: 'card__header__emblem-icon',
20
20
  title: 'card__header__title',
21
21
  description: 'card__header__description',
22
- metadata: 'card__header__metadata'
22
+ metadata: 'card__header__metadata',
23
+ anchor: 'card__anchor'
23
24
  };
@@ -1,4 +1,4 @@
1
- import { HTMLAttributeAnchorTarget, KeyboardEventHandler, MouseEvent, ReactElement, ReactNode } from 'react';
1
+ import { KeyboardEventHandler, MouseEvent, ReactElement, ReactNode } from 'react';
2
2
  import { PromoTagProps } from '@snack-uikit/promo-tag';
3
3
  import { WithSupportProps } from '@snack-uikit/utils';
4
4
  import { Size } from '../../types';
@@ -12,6 +12,8 @@ export type CardProps = WithSupportProps<{
12
12
  outline?: boolean;
13
13
  /** Отображение галочки для режима массового выделения карточек */
14
14
  multipleSelection?: boolean;
15
+ /** Колбек на клик по карточке */
16
+ onClick?(e: MouseEvent<HTMLDivElement | HTMLAnchorElement>): void;
15
17
  /** Размер */
16
18
  size?: Size;
17
19
  /** PromoBadge */
@@ -30,12 +32,8 @@ export type CardProps = WithSupportProps<{
30
32
  className?: string;
31
33
  /** Ссылка карточки */
32
34
  href?: string;
33
- /** Атрибут target для ссылки */
34
- target?: HTMLAttributeAnchorTarget;
35
- /** Колбек на клик по карточке */
36
- onClick?(e: MouseEvent<HTMLAnchorElement | HTMLDivElement>): void;
37
35
  /** Колбек нажатия клавиши клавиатуры */
38
- onKeyDown?: KeyboardEventHandler<HTMLAnchorElement | HTMLDivElement>;
36
+ onKeyDown?: KeyboardEventHandler<HTMLDivElement>;
39
37
  }>;
40
38
  export declare const Card: import("react").ForwardRefExoticComponent<{
41
39
  'data-test-id'?: string;
@@ -48,6 +46,8 @@ export declare const Card: import("react").ForwardRefExoticComponent<{
48
46
  outline?: boolean;
49
47
  /** Отображение галочки для режима массового выделения карточек */
50
48
  multipleSelection?: boolean;
49
+ /** Колбек на клик по карточке */
50
+ onClick?(e: MouseEvent<HTMLDivElement | HTMLAnchorElement>): void;
51
51
  /** Размер */
52
52
  size?: Size;
53
53
  /** PromoBadge */
@@ -66,10 +66,6 @@ export declare const Card: import("react").ForwardRefExoticComponent<{
66
66
  className?: string;
67
67
  /** Ссылка карточки */
68
68
  href?: string;
69
- /** Атрибут target для ссылки */
70
- target?: HTMLAttributeAnchorTarget;
71
- /** Колбек на клик по карточке */
72
- onClick?(e: MouseEvent<HTMLAnchorElement | HTMLDivElement>): void;
73
69
  /** Колбек нажатия клавиши клавиатуры */
74
- onKeyDown?: KeyboardEventHandler<HTMLAnchorElement | HTMLDivElement>;
75
- } & import("react").RefAttributes<HTMLDivElement | HTMLAnchorElement>>;
70
+ onKeyDown?: KeyboardEventHandler<HTMLDivElement>;
71
+ } & import("react").RefAttributes<HTMLDivElement>>;
@@ -11,22 +11,30 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  import cn from 'classnames';
14
- import { forwardRef, } from 'react';
14
+ import mergeRefs from 'merge-refs';
15
+ import { forwardRef, useCallback, useRef, } from 'react';
15
16
  import { Typography } from '@snack-uikit/typography';
16
17
  import { extractSupportProps } from '@snack-uikit/utils';
17
- import { SIZE } from '../../constants';
18
+ import { SIZE, TEST_IDS } from '../../constants';
18
19
  import { CardContext } from '../../context';
19
20
  import { Check, FunctionBadgeWrapper, PromoBadge } from '../../helperComponents';
21
+ import { TRIGGER_CLICK_KEY_CODES } from './constants';
20
22
  import styles from './styles.module.css';
21
- const CardTag = forwardRef((_a, ref) => {
22
- var { children, href, target, disabled } = _a, props = __rest(_a, ["children", "href", "target", "disabled"]);
23
- if (href && !disabled) {
24
- return (_jsx("a", Object.assign({}, props, { href: href, target: target, ref: ref, children: children })));
25
- }
26
- return (_jsx("div", Object.assign({}, props, { ref: ref, children: children })));
27
- });
28
23
  export const Card = forwardRef((_a, ref) => {
29
- var { onClick, disabled = false, checked, outline, multipleSelection = false, size = SIZE.M, children, header, footer, functionBadge, promoBadge, image, className, href, onKeyDown, target } = _a, rest = __rest(_a, ["onClick", "disabled", "checked", "outline", "multipleSelection", "size", "children", "header", "footer", "functionBadge", "promoBadge", "image", "className", "href", "onKeyDown", "target"]);
24
+ var { onClick, disabled = false, checked, outline, multipleSelection = false, size = SIZE.M, children, header, footer, functionBadge, promoBadge, image, className, href, onKeyDown: onKeyDownProp } = _a, rest = __rest(_a, ["onClick", "disabled", "checked", "outline", "multipleSelection", "size", "children", "header", "footer", "functionBadge", "promoBadge", "image", "className", "href", "onKeyDown"]);
25
+ const localDivRef = useRef(null);
26
+ const localAnchorRef = useRef(null);
27
+ const onKeyDown = useCallback((e) => {
28
+ var _a, _b;
29
+ if (e.target === localDivRef.current) {
30
+ if (TRIGGER_CLICK_KEY_CODES.includes(e.code) || e.key === ' ') {
31
+ href ? (_a = localAnchorRef.current) === null || _a === void 0 ? void 0 : _a.click() : (_b = localDivRef.current) === null || _b === void 0 ? void 0 : _b.click();
32
+ }
33
+ onKeyDownProp === null || onKeyDownProp === void 0 ? void 0 : onKeyDownProp(e);
34
+ }
35
+ }, [href, onKeyDownProp]);
30
36
  const supportProps = extractSupportProps(rest);
31
- return (_jsx(CardContext.Provider, { value: { size, disabled }, children: _jsxs(CardTag, Object.assign({ ref: ref, className: cn(styles.card, className) }, supportProps, { onClick: onClick, "data-disabled": disabled || undefined, "data-checked": checked || undefined, "data-outline": outline || undefined, "data-pointer": onClick ? true : undefined, disabled: disabled, href: href, target: target, tabIndex: 0, onKeyDown: onKeyDown, children: [image, _jsxs("div", { className: styles.composition, tabIndex: -1, children: [!disabled && functionBadge && (_jsx(FunctionBadgeWrapper, { className: styles.functionBadgeWrapper, children: functionBadge })), _jsx("div", { className: styles.contentWrapper, children: _jsxs("div", { className: styles.content, "data-size": size, children: [header, children && (_jsx(Typography, { family: 'sans', size: size, purpose: 'body', className: styles.body, tag: 'div', children: children })), footer && _jsx("div", { className: styles.footer, children: footer })] }) }), promoBadge && _jsx(PromoBadge, Object.assign({}, (typeof promoBadge === 'string' ? { text: promoBadge } : promoBadge)))] }), checked && multipleSelection && _jsx(Check, { className: styles.check })] })) }));
37
+ return (_jsx(CardContext.Provider, { value: { size, disabled }, children: _jsxs("div", Object.assign({ ref: mergeRefs(ref, localDivRef), className: cn(styles.card, className) }, supportProps, { onClick: onClick, "data-disabled": disabled || undefined, "data-checked": checked || undefined, "data-outline": outline || undefined, "data-pointer": onClick ? true : undefined,
38
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
39
+ tabIndex: 0, onKeyDown: onKeyDown, children: [image, _jsxs("div", { className: styles.composition, tabIndex: -1, children: [href && (_jsx("a", { ref: localAnchorRef, "data-test-id": TEST_IDS.anchor, tabIndex: -1, href: href, className: styles.anchor, "aria-label": supportProps['aria-label'] })), !disabled && functionBadge && (_jsx(FunctionBadgeWrapper, { className: styles.functionBadgeWrapper, children: functionBadge })), _jsx("div", { className: styles.contentWrapper, children: _jsxs("div", { className: styles.content, "data-size": size, children: [header || null, children && (_jsx(Typography, { family: 'sans', size: size, purpose: 'body', className: styles.body, tag: 'div', children: children })), footer && _jsx("div", { className: styles.footer, children: footer })] }) }), promoBadge && _jsx(PromoBadge, Object.assign({}, (typeof promoBadge === 'string' ? { text: promoBadge } : promoBadge)))] }), checked && multipleSelection && _jsx(Check, { className: styles.check })] })) }));
32
40
  });
@@ -0,0 +1 @@
1
+ export declare const TRIGGER_CLICK_KEY_CODES: string[];
@@ -0,0 +1 @@
1
+ export const TRIGGER_CLICK_KEY_CODES = ['Enter', 'Space'];
@@ -32,9 +32,6 @@
32
32
  margin:0;
33
33
  padding:0;
34
34
  text-align:left;
35
- text-decoration:none;
36
- -webkit-text-decoration-skip-ink:none;
37
- text-decoration-skip-ink:none;
38
35
  background-color:var(--sys-neutral-background1-level, #fdfdfd);
39
36
  border:0;
40
37
  outline-color:transparent;
@@ -140,6 +137,16 @@
140
137
  cursor:not-allowed;
141
138
  }
142
139
 
140
+ .anchor{
141
+ position:absolute;
142
+ z-index:1;
143
+ top:0;
144
+ right:0;
145
+ bottom:0;
146
+ left:0;
147
+ border-radius:var(--radius-card-container, 20px);
148
+ }
149
+
143
150
  .body{
144
151
  display:block;
145
152
  flex-grow:1;
@@ -18,17 +18,5 @@ import styles from './styles.module.css';
18
18
  export function FooterAction(_a) {
19
19
  var { button, secondaryButton, className } = _a, rest = __rest(_a, ["button", "secondaryButton", "className"]);
20
20
  const { disabled } = useCardContext();
21
- const handleButtonClick = e => {
22
- var _a;
23
- e.stopPropagation();
24
- e.preventDefault();
25
- (_a = button === null || button === void 0 ? void 0 : button.onClick) === null || _a === void 0 ? void 0 : _a.call(button, e);
26
- };
27
- const handleSecondaryButtonClick = e => {
28
- var _a;
29
- e.stopPropagation();
30
- e.preventDefault();
31
- (_a = button === null || button === void 0 ? void 0 : button.onClick) === null || _a === void 0 ? void 0 : _a.call(button, e);
32
- };
33
- return (_jsxs("div", Object.assign({ className: cn(styles.action, className) }, excludeSupportProps(rest), { children: [_jsx(ButtonFilled, Object.assign({}, button, { onClick: handleButtonClick, appearance: 'primary', size: 'm', disabled: disabled })), secondaryButton && (_jsx(ButtonTonal, Object.assign({}, secondaryButton, { onClick: handleSecondaryButtonClick, appearance: 'neutral', size: 'm', disabled: disabled })))] })));
21
+ return (_jsxs("div", Object.assign({ className: cn(styles.action, className) }, excludeSupportProps(rest), { children: [_jsx(ButtonFilled, Object.assign({}, button, { appearance: 'primary', size: 'm', disabled: disabled })), secondaryButton && _jsx(ButtonTonal, Object.assign({}, secondaryButton, { appearance: 'neutral', size: 'm', disabled: disabled }))] })));
34
22
  }
@@ -19,11 +19,5 @@ import styles from './styles.module.css';
19
19
  export function FooterPromo(_a) {
20
20
  var { volume, button, className, size } = _a, rest = __rest(_a, ["volume", "button", "className", "size"]);
21
21
  const { disabled } = useCardContext();
22
- const handleButtonClick = e => {
23
- var _a;
24
- e.stopPropagation();
25
- e.preventDefault();
26
- (_a = button === null || button === void 0 ? void 0 : button.onClick) === null || _a === void 0 ? void 0 : _a.call(button, e);
27
- };
28
- return (_jsxs("div", Object.assign({ className: cn(styles.promo, className) }, excludeSupportProps(rest), { children: [button && (_jsx(ButtonFilled, Object.assign({}, button, { onClick: handleButtonClick, appearance: 'primary', size: 'm', disabled: disabled }))), volume && _jsx(Dimension, Object.assign({}, volume, { size: size }))] })));
22
+ return (_jsxs("div", Object.assign({ className: cn(styles.promo, className) }, excludeSupportProps(rest), { children: [button && _jsx(ButtonFilled, Object.assign({}, button, { appearance: 'primary', size: 'm', disabled: disabled })), volume && _jsx(Dimension, Object.assign({}, volume, { size: size }))] })));
29
23
  }
@@ -27,7 +27,6 @@ export function FunctionBadge({ icon, options }) {
27
27
  }, [isOpen, setVisible]);
28
28
  const onClick = useCallback((e) => {
29
29
  e.stopPropagation();
30
- e.preventDefault();
31
30
  setIsOpen(isOpen => !isOpen);
32
31
  }, []);
33
32
  return (_jsx("span", { className: styles.wrapper, children: _jsx(Droplist, { trigger: 'clickAndFocusVisible', open: isOpen, onOpenChange: setIsOpen, widthStrategy: 'gte', scroll: true, "data-test-id": TEST_IDS.droplist, placement: 'bottom-end', triggerElemRef: buttonRef, items: options.map((_a) => {
@@ -14,4 +14,5 @@ export declare const TEST_IDS: {
14
14
  title: string;
15
15
  description: string;
16
16
  metadata: string;
17
+ anchor: string;
17
18
  };
@@ -14,4 +14,5 @@ export const TEST_IDS = {
14
14
  title: 'card__header__title',
15
15
  description: 'card__header__description',
16
16
  metadata: 'card__header__metadata',
17
+ anchor: 'card__anchor',
17
18
  };
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "title": "Card",
7
- "version": "0.16.15-preview-31ae96b0.0",
7
+ "version": "0.16.15",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -39,13 +39,14 @@
39
39
  "@snack-uikit/button": "0.19.6",
40
40
  "@snack-uikit/icon-predefined": "0.7.3",
41
41
  "@snack-uikit/icons": "0.24.2",
42
- "@snack-uikit/list": "0.23.1",
42
+ "@snack-uikit/list": "0.24.0",
43
43
  "@snack-uikit/promo-tag": "0.7.1",
44
44
  "@snack-uikit/tag": "0.12.2",
45
45
  "@snack-uikit/truncate-string": "0.6.6",
46
46
  "@snack-uikit/typography": "0.8.4",
47
47
  "@snack-uikit/utils": "3.7.0",
48
- "classnames": "2.5.1"
48
+ "classnames": "2.5.1",
49
+ "merge-refs": "1.3.0"
49
50
  },
50
- "gitHead": "88a3023d9740bca7c55a403e7590d9fb21083dad"
51
+ "gitHead": "cb674e100e5e2b92b7c07d703fcc50789ec87606"
51
52
  }
@@ -1,23 +1,26 @@
1
1
  import cn from 'classnames';
2
+ import mergeRefs from 'merge-refs';
2
3
  import {
3
- ForwardedRef,
4
4
  forwardRef,
5
- HTMLAttributeAnchorTarget,
5
+ KeyboardEvent,
6
6
  KeyboardEventHandler,
7
7
  MouseEvent,
8
8
  ReactElement,
9
9
  ReactNode,
10
+ useCallback,
11
+ useRef,
10
12
  } from 'react';
11
13
 
12
14
  import { PromoTagProps } from '@snack-uikit/promo-tag';
13
15
  import { Typography } from '@snack-uikit/typography';
14
16
  import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
15
17
 
16
- import { SIZE } from '../../constants';
18
+ import { SIZE, TEST_IDS } from '../../constants';
17
19
  import { CardContext } from '../../context';
18
20
  import { Check, FunctionBadgeWrapper, PromoBadge } from '../../helperComponents';
19
21
  import { Size } from '../../types';
20
22
  import { HeaderProps } from '../Header';
23
+ import { TRIGGER_CLICK_KEY_CODES } from './constants';
21
24
  import styles from './styles.module.scss';
22
25
 
23
26
  export type CardProps = WithSupportProps<{
@@ -29,6 +32,8 @@ export type CardProps = WithSupportProps<{
29
32
  outline?: boolean;
30
33
  /** Отображение галочки для режима массового выделения карточек */
31
34
  multipleSelection?: boolean;
35
+ /** Колбек на клик по карточке */
36
+ onClick?(e: MouseEvent<HTMLDivElement | HTMLAnchorElement>): void;
32
37
  /** Размер */
33
38
  size?: Size;
34
39
  /** PromoBadge */
@@ -47,36 +52,11 @@ export type CardProps = WithSupportProps<{
47
52
  className?: string;
48
53
  /** Ссылка карточки */
49
54
  href?: string;
50
- /** Атрибут target для ссылки */
51
- target?: HTMLAttributeAnchorTarget;
52
- /** Колбек на клик по карточке */
53
- onClick?(e: MouseEvent<HTMLAnchorElement | HTMLDivElement>): void;
54
55
  /** Колбек нажатия клавиши клавиатуры */
55
- onKeyDown?: KeyboardEventHandler<HTMLAnchorElement | HTMLDivElement>;
56
+ onKeyDown?: KeyboardEventHandler<HTMLDivElement>;
56
57
  }>;
57
58
 
58
- const CardTag = forwardRef<
59
- HTMLAnchorElement | HTMLDivElement,
60
- CardProps & {
61
- tabIndex?: number;
62
- }
63
- >(({ children, href, target, disabled, ...props }, ref) => {
64
- if (href && !disabled) {
65
- return (
66
- <a {...props} href={href} target={target} ref={ref as ForwardedRef<HTMLAnchorElement>}>
67
- {children}
68
- </a>
69
- );
70
- }
71
-
72
- return (
73
- <div {...props} ref={ref as ForwardedRef<HTMLDivElement>}>
74
- {children}
75
- </div>
76
- );
77
- });
78
-
79
- export const Card = forwardRef<HTMLAnchorElement | HTMLDivElement, CardProps>(
59
+ export const Card = forwardRef<HTMLDivElement, CardProps>(
80
60
  (
81
61
  {
82
62
  onClick,
@@ -93,18 +73,34 @@ export const Card = forwardRef<HTMLAnchorElement | HTMLDivElement, CardProps>(
93
73
  image,
94
74
  className,
95
75
  href,
96
- onKeyDown,
97
- target,
76
+ onKeyDown: onKeyDownProp,
98
77
  ...rest
99
78
  },
100
79
  ref,
101
80
  ) => {
81
+ const localDivRef = useRef<HTMLDivElement>(null);
82
+ const localAnchorRef = useRef<HTMLAnchorElement>(null);
83
+
84
+ const onKeyDown = useCallback(
85
+ (e: KeyboardEvent<HTMLDivElement>) => {
86
+ if (e.target === localDivRef.current) {
87
+ if (TRIGGER_CLICK_KEY_CODES.includes(e.code) || e.key === ' ') {
88
+ href ? localAnchorRef.current?.click() : localDivRef.current?.click();
89
+ }
90
+
91
+ onKeyDownProp?.(e);
92
+ }
93
+ },
94
+ [href, onKeyDownProp],
95
+ );
96
+
102
97
  const supportProps = extractSupportProps(rest);
103
98
 
104
99
  return (
105
100
  <CardContext.Provider value={{ size, disabled }}>
106
- <CardTag
107
- ref={ref}
101
+ {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
102
+ <div
103
+ ref={mergeRefs(ref, localDivRef)}
108
104
  className={cn(styles.card, className)}
109
105
  {...supportProps}
110
106
  onClick={onClick}
@@ -112,22 +108,31 @@ export const Card = forwardRef<HTMLAnchorElement | HTMLDivElement, CardProps>(
112
108
  data-checked={checked || undefined}
113
109
  data-outline={outline || undefined}
114
110
  data-pointer={onClick ? true : undefined}
115
- disabled={disabled}
116
- href={href}
117
- target={target}
111
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
118
112
  tabIndex={0}
119
113
  onKeyDown={onKeyDown}
120
114
  >
121
115
  {image}
122
116
 
123
117
  <div className={styles.composition} tabIndex={-1}>
118
+ {href && (
119
+ <a
120
+ ref={localAnchorRef}
121
+ data-test-id={TEST_IDS.anchor}
122
+ tabIndex={-1}
123
+ href={href}
124
+ className={styles.anchor}
125
+ aria-label={supportProps['aria-label'] as string}
126
+ />
127
+ )}
128
+
124
129
  {!disabled && functionBadge && (
125
130
  <FunctionBadgeWrapper className={styles.functionBadgeWrapper}>{functionBadge}</FunctionBadgeWrapper>
126
131
  )}
127
132
 
128
133
  <div className={styles.contentWrapper}>
129
134
  <div className={styles.content} data-size={size}>
130
- {header}
135
+ {header || null}
131
136
 
132
137
  {children && (
133
138
  <Typography family='sans' size={size} purpose='body' className={styles.body} tag='div'>
@@ -143,7 +148,7 @@ export const Card = forwardRef<HTMLAnchorElement | HTMLDivElement, CardProps>(
143
148
  </div>
144
149
 
145
150
  {checked && multipleSelection && <Check className={styles.check} />}
146
- </CardTag>
151
+ </div>
147
152
  </CardContext.Provider>
148
153
  );
149
154
  },
@@ -0,0 +1 @@
1
+ export const TRIGGER_CLICK_KEY_CODES = ['Enter', 'Space'];
@@ -45,8 +45,6 @@ $sizes: 's', 'm', 'l';
45
45
  padding: 0;
46
46
 
47
47
  text-align: left;
48
- text-decoration: none;
49
- text-decoration-skip-ink: none;
50
48
 
51
49
  background-color: styles-tokens-card.$sys-neutral-background1-level;
52
50
  border: 0;
@@ -189,6 +187,18 @@ $sizes: 's', 'm', 'l';
189
187
  }
190
188
  }
191
189
 
190
+ .anchor {
191
+ position: absolute;
192
+ /* stylelint-disable-next-line declaration-property-value-allowed-list */
193
+ z-index: 1;
194
+ top: 0;
195
+ right: 0;
196
+ bottom: 0;
197
+ left: 0;
198
+
199
+ border-radius: styles-tokens-card.$radius-card-container;
200
+ }
201
+
192
202
  .body {
193
203
  display: block;
194
204
  flex-grow: 1;
@@ -1,5 +1,4 @@
1
1
  import cn from 'classnames';
2
- import { MouseEventHandler } from 'react';
3
2
 
4
3
  import { ButtonFilled, ButtonFilledProps, ButtonTonal, ButtonTonalProps } from '@snack-uikit/button';
5
4
  import { excludeSupportProps, WithSupportProps } from '@snack-uikit/utils';
@@ -19,32 +18,10 @@ export type FooterActionProps = WithSupportProps<{
19
18
  export function FooterAction({ button, secondaryButton, className, ...rest }: FooterActionProps) {
20
19
  const { disabled } = useCardContext();
21
20
 
22
- const handleButtonClick: MouseEventHandler<HTMLButtonElement> = e => {
23
- e.stopPropagation();
24
- e.preventDefault();
25
-
26
- button?.onClick?.(e);
27
- };
28
-
29
- const handleSecondaryButtonClick: MouseEventHandler<HTMLButtonElement> = e => {
30
- e.stopPropagation();
31
- e.preventDefault();
32
-
33
- button?.onClick?.(e);
34
- };
35
-
36
21
  return (
37
22
  <div className={cn(styles.action, className)} {...excludeSupportProps(rest)}>
38
- <ButtonFilled {...button} onClick={handleButtonClick} appearance='primary' size='m' disabled={disabled} />
39
- {secondaryButton && (
40
- <ButtonTonal
41
- {...secondaryButton}
42
- onClick={handleSecondaryButtonClick}
43
- appearance='neutral'
44
- size='m'
45
- disabled={disabled}
46
- />
47
- )}
23
+ <ButtonFilled {...button} appearance='primary' size='m' disabled={disabled} />
24
+ {secondaryButton && <ButtonTonal {...secondaryButton} appearance='neutral' size='m' disabled={disabled} />}
48
25
  </div>
49
26
  );
50
27
  }
@@ -1,5 +1,4 @@
1
1
  import cn from 'classnames';
2
- import { MouseEventHandler } from 'react';
3
2
 
4
3
  import { ButtonFilled, ButtonFilledProps } from '@snack-uikit/button';
5
4
  import { excludeSupportProps, WithSupportProps } from '@snack-uikit/utils';
@@ -23,18 +22,9 @@ export type FooterPromoProps = WithSupportProps<{
23
22
  export function FooterPromo({ volume, button, className, size, ...rest }: FooterPromoProps) {
24
23
  const { disabled } = useCardContext();
25
24
 
26
- const handleButtonClick: MouseEventHandler<HTMLButtonElement> = e => {
27
- e.stopPropagation();
28
- e.preventDefault();
29
-
30
- button?.onClick?.(e);
31
- };
32
-
33
25
  return (
34
26
  <div className={cn(styles.promo, className)} {...excludeSupportProps(rest)}>
35
- {button && (
36
- <ButtonFilled {...button} onClick={handleButtonClick} appearance='primary' size='m' disabled={disabled} />
37
- )}
27
+ {button && <ButtonFilled {...button} appearance='primary' size='m' disabled={disabled} />}
38
28
  {volume && <Dimension {...volume} size={size} />}
39
29
  </div>
40
30
  );
@@ -33,8 +33,6 @@ export function FunctionBadge({ icon, options }: FunctionBadgeProps) {
33
33
 
34
34
  const onClick = useCallback((e: MouseEvent<HTMLButtonElement>) => {
35
35
  e.stopPropagation();
36
- e.preventDefault();
37
-
38
36
  setIsOpen(isOpen => !isOpen);
39
37
  }, []);
40
38
 
@@ -49,4 +49,4 @@
49
49
 
50
50
  li.item {
51
51
  width: max-content;
52
- }
52
+ }
package/src/constants.ts CHANGED
@@ -15,4 +15,5 @@ export const TEST_IDS = {
15
15
  title: 'card__header__title',
16
16
  description: 'card__header__description',
17
17
  metadata: 'card__header__metadata',
18
+ anchor: 'card__anchor',
18
19
  };