@cloud-ru/ds-markdown 1.0.21-preview-a0540d7f.0 → 1.0.22

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 CHANGED
@@ -3,6 +3,17 @@
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
+ ## 1.0.22 (2026-09-14)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **FF-9049:** nest markdown table customize item into a divider group ([5a2e0db](https://github.com/cloud-ru-tech/snack-v2/commit/5a2e0db45034eb9e05b85153c679dea0d3c5a72e))
11
+ - **FF-9049:** open markdown toolbar More menu on mobile, pick table size with a single tap ([d51e277](https://github.com/cloud-ru-tech/snack-v2/commit/d51e27726ce3dec480e48047440f5a866991418b))
12
+
13
+ ## 1.0.21 (2026-09-14)
14
+
15
+ **Note:** Version bump only for package @ds/markdown
16
+
6
17
  ## 1.0.20 (2026-09-11)
7
18
 
8
19
  ### Bug Fixes
@@ -8,7 +8,6 @@ const jsx_runtime_1 = require("react/jsx-runtime");
8
8
  const system_1 = require("@cloud-ru/ds-icons/interface/system");
9
9
  const list_1 = require("@cloud-ru/ds-list");
10
10
  const portal_context_1 = require("@cloud-ru/ds-portal-context");
11
- const tooltip_1 = require("@cloud-ru/ds-tooltip");
12
11
  const react_1 = require("react");
13
12
  const constants_1 = require("../../constants");
14
13
  const helperComponents_1 = require("../../helperComponents");
@@ -65,15 +64,20 @@ function Toolbar({ api, items }) {
65
64
  } })),
66
65
  inactive: true,
67
66
  },
68
- { type: 'group', content: '', divider: true },
69
67
  {
70
- content: {
71
- label: t('table.customize'),
72
- className: styles_module_scss_1.default.tableCustomizeOption,
73
- },
74
- onClick: openCustomize,
75
- afterContent: (0, jsx_runtime_1.jsx)(system_1.ChevronRightSVG, {}),
76
- 'data-test-id': constants_1.TEST_IDS.tableCustomize,
68
+ type: 'group',
69
+ divider: true,
70
+ items: [
71
+ {
72
+ content: {
73
+ label: t('table.customize'),
74
+ className: styles_module_scss_1.default.tableCustomizeOption,
75
+ },
76
+ onClick: openCustomize,
77
+ afterContent: (0, jsx_runtime_1.jsx)(system_1.ChevronRightSVG, {}),
78
+ 'data-test-id': constants_1.TEST_IDS.tableCustomize,
79
+ },
80
+ ],
77
81
  },
78
82
  ], [api, t, openCustomize]);
79
83
  const renderItem = (id) => {
@@ -169,5 +173,5 @@ function Toolbar({ api, items }) {
169
173
  },
170
174
  ];
171
175
  });
172
- return ((0, jsx_runtime_1.jsxs)("div", { ref: rootRef, className: styles_module_scss_1.default.root, children: [(0, jsx_runtime_1.jsxs)(helperComponents_1.Buttons, { children: [presentGroups.map((group, gi) => ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [gi > 0 && (0, jsx_runtime_1.jsx)(helperComponents_1.ButtonsDivider, {}), (0, jsx_runtime_1.jsx)(helperComponents_1.ButtonsGroup, { children: group.map(renderItem) })] }, `g-${gi}`))), overflowItems.length > 0 && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(helperComponents_1.ButtonsDivider, {}), (0, jsx_runtime_1.jsx)(list_1.Droplist, { size: 'm', container: portalContext, open: moreOpen, onOpenChange: setMoreOpen, placement: 'bottom-end', marker: false, items: moreItems, triggerElemRef: moreButtonRef, triggerClassName: styles_module_scss_1.default.tooltipTrigger, "data-test-id": constants_1.TEST_IDS.toolbarMoreList, children: (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { tip: t('toolbar.more'), triggerClassName: styles_module_scss_1.default.tooltipTrigger, children: (0, jsx_runtime_1.jsx)(helperComponents_1.PrivateButton, { icon: (0, jsx_runtime_1.jsx)(system_1.MoreSVG, {}), "data-test-id": constants_1.TEST_IDS.toolbarMore, "aria-label": t('toolbar.more'), "aria-haspopup": 'menu', "aria-expanded": moreOpen }) }) })] }))] }), (0, jsx_runtime_1.jsx)(helperComponents_1.CustomizeTableModal, { open: toolbarItemModal === 'table', onClose: closeToolbarItemModal, onSubmit: handleSubmitCustomizeTable }), (0, jsx_runtime_1.jsx)(helperComponents_1.LinkModal, { open: toolbarItemModal === 'link', initial: linkInitial, onClose: closeToolbarItemModal, onSubmit: handleLinkSubmit }), (0, jsx_runtime_1.jsx)(helperComponents_1.ImageModal, { open: toolbarItemModal === 'image', onClose: closeToolbarItemModal, onSubmit: handleImageSubmit })] }));
176
+ return ((0, jsx_runtime_1.jsxs)("div", { ref: rootRef, className: styles_module_scss_1.default.root, children: [(0, jsx_runtime_1.jsxs)(helperComponents_1.Buttons, { children: [presentGroups.map((group, gi) => ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [gi > 0 && (0, jsx_runtime_1.jsx)(helperComponents_1.ButtonsDivider, {}), (0, jsx_runtime_1.jsx)(helperComponents_1.ButtonsGroup, { children: group.map(renderItem) })] }, `g-${gi}`))), overflowItems.length > 0 && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(helperComponents_1.ButtonsDivider, {}), (0, jsx_runtime_1.jsx)(list_1.Droplist, { size: 'm', container: portalContext, open: moreOpen, onOpenChange: setMoreOpen, placement: 'bottom-end', marker: false, items: moreItems, triggerElemRef: moreButtonRef, triggerClassName: styles_module_scss_1.default.tooltipTrigger, "data-test-id": constants_1.TEST_IDS.toolbarMoreList, children: (0, jsx_runtime_1.jsx)(helperComponents_1.PrivateButton, { icon: (0, jsx_runtime_1.jsx)(system_1.MoreSVG, {}), tip: { title: t('toolbar.more') }, "data-test-id": constants_1.TEST_IDS.toolbarMore, "aria-label": t('toolbar.more'), "aria-haspopup": 'menu', "aria-expanded": moreOpen }) })] }))] }), (0, jsx_runtime_1.jsx)(helperComponents_1.CustomizeTableModal, { open: toolbarItemModal === 'table', onClose: closeToolbarItemModal, onSubmit: handleSubmitCustomizeTable }), (0, jsx_runtime_1.jsx)(helperComponents_1.LinkModal, { open: toolbarItemModal === 'link', initial: linkInitial, onClose: closeToolbarItemModal, onSubmit: handleLinkSubmit }), (0, jsx_runtime_1.jsx)(helperComponents_1.ImageModal, { open: toolbarItemModal === 'image', onClose: closeToolbarItemModal, onSubmit: handleImageSubmit })] }));
173
177
  }
@@ -1,11 +1,12 @@
1
+ import { PointerEvent } from 'react';
1
2
  export type TableSelectItemProps = {
2
- /** Hover-состояние (управляется родителем по mouseEnter). */
3
+ /** Hover-состояние (управляется родителем по pointerEnter). */
3
4
  hovered?: boolean;
4
5
  /** Будет ли ячейка попадать в выбранный диапазон. */
5
6
  checked?: boolean;
6
- onMouseEnter?(): void;
7
+ onPointerEnter?(event: PointerEvent<HTMLButtonElement>): void;
7
8
  onClick?(): void;
8
9
  'aria-label'?: string;
9
10
  'data-test-id'?: string;
10
11
  };
11
- export declare function TableSelectItem({ hovered, checked, onMouseEnter, onClick, 'aria-label': ariaLabel, 'data-test-id': dataTestId, }: TableSelectItemProps): import("react/jsx-runtime").JSX.Element;
12
+ export declare function TableSelectItem({ hovered, checked, onPointerEnter, onClick, 'aria-label': ariaLabel, 'data-test-id': dataTestId, }: TableSelectItemProps): import("react/jsx-runtime").JSX.Element;
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.TableSelectItem = TableSelectItem;
7
7
  const jsx_runtime_1 = require("react/jsx-runtime");
8
8
  const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
9
- function TableSelectItem({ hovered = false, checked = false, onMouseEnter, onClick, 'aria-label': ariaLabel, 'data-test-id': dataTestId, }) {
9
+ function TableSelectItem({ hovered = false, checked = false, onPointerEnter, onClick, 'aria-label': ariaLabel, 'data-test-id': dataTestId, }) {
10
10
  const state = hovered ? 'hovered' : 'default';
11
- return ((0, jsx_runtime_1.jsx)("button", { type: 'button', className: styles_module_scss_1.default.root, "data-state": state, "data-checked": checked || undefined, onMouseEnter: onMouseEnter, onClick: onClick, "aria-label": ariaLabel, "data-test-id": dataTestId, children: (0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_1.default.stateLayer, "data-state": checked ? 'activatedOnBackground' : 'emptyNeutralOnBackground', "aria-hidden": true }) }));
11
+ return ((0, jsx_runtime_1.jsx)("button", { type: 'button', className: styles_module_scss_1.default.root, "data-state": state, "data-checked": checked || undefined, onPointerEnter: onPointerEnter, onClick: onClick, "aria-label": ariaLabel, "data-test-id": dataTestId, children: (0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_1.default.stateLayer, "data-state": checked ? 'activatedOnBackground' : 'emptyNeutralOnBackground', "aria-hidden": true }) }));
12
12
  }
@@ -14,14 +14,19 @@ const MAX = 8;
14
14
  function TableSelectSettingsGrid({ onPick }) {
15
15
  const { t } = locale_1.markdownLocale.useTranslations();
16
16
  const [hover, setHover] = (0, react_1.useState)(null);
17
+ const handleCellPointerEnter = (event, cell) => {
18
+ // iOS Safari не отправляет click по тапу, если hover меняет разметку.
19
+ if (event.pointerType === 'mouse')
20
+ setHover(cell);
21
+ };
17
22
  const cells = [];
18
23
  for (let r = 1; r <= MAX; r += 1) {
19
24
  for (let c = 1; c <= MAX; c += 1) {
20
25
  cells.push({ r, c });
21
26
  }
22
27
  }
23
- return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.root, "data-test-id": constants_1.TEST_IDS.tableGridPicker, children: (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.container, children: [(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.grid, onMouseLeave: () => setHover(null), children: cells.map(({ r, c }) => {
28
+ return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.root, "data-test-id": constants_1.TEST_IDS.tableGridPicker, children: (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.container, children: [(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.grid, onPointerLeave: () => setHover(null), children: cells.map(({ r, c }) => {
24
29
  const checked = hover !== null && r <= hover.r && c <= hover.c;
25
- return ((0, jsx_runtime_1.jsx)(TableSelectItem_1.TableSelectItem, { hovered: hover?.r === r && hover?.c === c, checked: checked, onMouseEnter: () => setHover({ r, c }), onClick: () => onPick(r, c), "aria-label": t('table.insert', { rows: r, cols: c }), "data-test-id": (0, constants_1.tableCellTestId)(r, c) }, `${r}-${c}`));
30
+ return ((0, jsx_runtime_1.jsx)(TableSelectItem_1.TableSelectItem, { hovered: hover?.r === r && hover?.c === c, checked: checked, onPointerEnter: event => handleCellPointerEnter(event, { r, c }), onClick: () => onPick(r, c), "aria-label": t('table.insert', { rows: r, cols: c }), "data-test-id": (0, constants_1.tableCellTestId)(r, c) }, `${r}-${c}`));
26
31
  }) }), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.label, children: hover ? `${hover.r} × ${hover.c}` : t('table.pickSize') })] }) }));
27
32
  }
@@ -2,7 +2,6 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { ChevronRightSVG, MoreSVG } from '@cloud-ru/ds-icons/interface/system';
3
3
  import { Droplist } from '@cloud-ru/ds-list';
4
4
  import { usePortalContext } from '@cloud-ru/ds-portal-context';
5
- import { Tooltip } from '@cloud-ru/ds-tooltip';
6
5
  import { Fragment, useCallback, useEffect, useMemo, useState } from 'react';
7
6
  import { TEST_IDS, TOOLBAR_ITEM, toolbarButtonTestId } from '../../constants.js';
8
7
  import { ButtonHeading, Buttons, ButtonsDivider, ButtonsGroup, CustomizeTableModal, ImageModal, LinkModal, PrivateButton, TableSelectSettingsGrid, } from '../../helperComponents/index.js';
@@ -59,15 +58,20 @@ export function Toolbar({ api, items }) {
59
58
  } })),
60
59
  inactive: true,
61
60
  },
62
- { type: 'group', content: '', divider: true },
63
61
  {
64
- content: {
65
- label: t('table.customize'),
66
- className: styles.tableCustomizeOption,
67
- },
68
- onClick: openCustomize,
69
- afterContent: _jsx(ChevronRightSVG, {}),
70
- 'data-test-id': TEST_IDS.tableCustomize,
62
+ type: 'group',
63
+ divider: true,
64
+ items: [
65
+ {
66
+ content: {
67
+ label: t('table.customize'),
68
+ className: styles.tableCustomizeOption,
69
+ },
70
+ onClick: openCustomize,
71
+ afterContent: _jsx(ChevronRightSVG, {}),
72
+ 'data-test-id': TEST_IDS.tableCustomize,
73
+ },
74
+ ],
71
75
  },
72
76
  ], [api, t, openCustomize]);
73
77
  const renderItem = (id) => {
@@ -163,5 +167,5 @@ export function Toolbar({ api, items }) {
163
167
  },
164
168
  ];
165
169
  });
166
- return (_jsxs("div", { ref: rootRef, className: styles.root, children: [_jsxs(Buttons, { children: [presentGroups.map((group, gi) => (_jsxs(Fragment, { children: [gi > 0 && _jsx(ButtonsDivider, {}), _jsx(ButtonsGroup, { children: group.map(renderItem) })] }, `g-${gi}`))), overflowItems.length > 0 && (_jsxs(_Fragment, { children: [_jsx(ButtonsDivider, {}), _jsx(Droplist, { size: 'm', container: portalContext, open: moreOpen, onOpenChange: setMoreOpen, placement: 'bottom-end', marker: false, items: moreItems, triggerElemRef: moreButtonRef, triggerClassName: styles.tooltipTrigger, "data-test-id": TEST_IDS.toolbarMoreList, children: _jsx(Tooltip, { tip: t('toolbar.more'), triggerClassName: styles.tooltipTrigger, children: _jsx(PrivateButton, { icon: _jsx(MoreSVG, {}), "data-test-id": TEST_IDS.toolbarMore, "aria-label": t('toolbar.more'), "aria-haspopup": 'menu', "aria-expanded": moreOpen }) }) })] }))] }), _jsx(CustomizeTableModal, { open: toolbarItemModal === 'table', onClose: closeToolbarItemModal, onSubmit: handleSubmitCustomizeTable }), _jsx(LinkModal, { open: toolbarItemModal === 'link', initial: linkInitial, onClose: closeToolbarItemModal, onSubmit: handleLinkSubmit }), _jsx(ImageModal, { open: toolbarItemModal === 'image', onClose: closeToolbarItemModal, onSubmit: handleImageSubmit })] }));
170
+ return (_jsxs("div", { ref: rootRef, className: styles.root, children: [_jsxs(Buttons, { children: [presentGroups.map((group, gi) => (_jsxs(Fragment, { children: [gi > 0 && _jsx(ButtonsDivider, {}), _jsx(ButtonsGroup, { children: group.map(renderItem) })] }, `g-${gi}`))), overflowItems.length > 0 && (_jsxs(_Fragment, { children: [_jsx(ButtonsDivider, {}), _jsx(Droplist, { size: 'm', container: portalContext, open: moreOpen, onOpenChange: setMoreOpen, placement: 'bottom-end', marker: false, items: moreItems, triggerElemRef: moreButtonRef, triggerClassName: styles.tooltipTrigger, "data-test-id": TEST_IDS.toolbarMoreList, children: _jsx(PrivateButton, { icon: _jsx(MoreSVG, {}), tip: { title: t('toolbar.more') }, "data-test-id": TEST_IDS.toolbarMore, "aria-label": t('toolbar.more'), "aria-haspopup": 'menu', "aria-expanded": moreOpen }) })] }))] }), _jsx(CustomizeTableModal, { open: toolbarItemModal === 'table', onClose: closeToolbarItemModal, onSubmit: handleSubmitCustomizeTable }), _jsx(LinkModal, { open: toolbarItemModal === 'link', initial: linkInitial, onClose: closeToolbarItemModal, onSubmit: handleLinkSubmit }), _jsx(ImageModal, { open: toolbarItemModal === 'image', onClose: closeToolbarItemModal, onSubmit: handleImageSubmit })] }));
167
171
  }
@@ -1,11 +1,12 @@
1
+ import { PointerEvent } from 'react';
1
2
  export type TableSelectItemProps = {
2
- /** Hover-состояние (управляется родителем по mouseEnter). */
3
+ /** Hover-состояние (управляется родителем по pointerEnter). */
3
4
  hovered?: boolean;
4
5
  /** Будет ли ячейка попадать в выбранный диапазон. */
5
6
  checked?: boolean;
6
- onMouseEnter?(): void;
7
+ onPointerEnter?(event: PointerEvent<HTMLButtonElement>): void;
7
8
  onClick?(): void;
8
9
  'aria-label'?: string;
9
10
  'data-test-id'?: string;
10
11
  };
11
- export declare function TableSelectItem({ hovered, checked, onMouseEnter, onClick, 'aria-label': ariaLabel, 'data-test-id': dataTestId, }: TableSelectItemProps): import("react/jsx-runtime").JSX.Element;
12
+ export declare function TableSelectItem({ hovered, checked, onPointerEnter, onClick, 'aria-label': ariaLabel, 'data-test-id': dataTestId, }: TableSelectItemProps): import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import styles from './styles.module.css';
3
- export function TableSelectItem({ hovered = false, checked = false, onMouseEnter, onClick, 'aria-label': ariaLabel, 'data-test-id': dataTestId, }) {
3
+ export function TableSelectItem({ hovered = false, checked = false, onPointerEnter, onClick, 'aria-label': ariaLabel, 'data-test-id': dataTestId, }) {
4
4
  const state = hovered ? 'hovered' : 'default';
5
- return (_jsx("button", { type: 'button', className: styles.root, "data-state": state, "data-checked": checked || undefined, onMouseEnter: onMouseEnter, onClick: onClick, "aria-label": ariaLabel, "data-test-id": dataTestId, children: _jsx("span", { className: styles.stateLayer, "data-state": checked ? 'activatedOnBackground' : 'emptyNeutralOnBackground', "aria-hidden": true }) }));
5
+ return (_jsx("button", { type: 'button', className: styles.root, "data-state": state, "data-checked": checked || undefined, onPointerEnter: onPointerEnter, onClick: onClick, "aria-label": ariaLabel, "data-test-id": dataTestId, children: _jsx("span", { className: styles.stateLayer, "data-state": checked ? 'activatedOnBackground' : 'emptyNeutralOnBackground', "aria-hidden": true }) }));
6
6
  }
@@ -8,14 +8,19 @@ const MAX = 8;
8
8
  export function TableSelectSettingsGrid({ onPick }) {
9
9
  const { t } = markdownLocale.useTranslations();
10
10
  const [hover, setHover] = useState(null);
11
+ const handleCellPointerEnter = (event, cell) => {
12
+ // iOS Safari не отправляет click по тапу, если hover меняет разметку.
13
+ if (event.pointerType === 'mouse')
14
+ setHover(cell);
15
+ };
11
16
  const cells = [];
12
17
  for (let r = 1; r <= MAX; r += 1) {
13
18
  for (let c = 1; c <= MAX; c += 1) {
14
19
  cells.push({ r, c });
15
20
  }
16
21
  }
17
- return (_jsx("div", { className: styles.root, "data-test-id": TEST_IDS.tableGridPicker, children: _jsxs("div", { className: styles.container, children: [_jsx("div", { className: styles.grid, onMouseLeave: () => setHover(null), children: cells.map(({ r, c }) => {
22
+ return (_jsx("div", { className: styles.root, "data-test-id": TEST_IDS.tableGridPicker, children: _jsxs("div", { className: styles.container, children: [_jsx("div", { className: styles.grid, onPointerLeave: () => setHover(null), children: cells.map(({ r, c }) => {
18
23
  const checked = hover !== null && r <= hover.r && c <= hover.c;
19
- return (_jsx(TableSelectItem, { hovered: hover?.r === r && hover?.c === c, checked: checked, onMouseEnter: () => setHover({ r, c }), onClick: () => onPick(r, c), "aria-label": t('table.insert', { rows: r, cols: c }), "data-test-id": tableCellTestId(r, c) }, `${r}-${c}`));
24
+ return (_jsx(TableSelectItem, { hovered: hover?.r === r && hover?.c === c, checked: checked, onPointerEnter: event => handleCellPointerEnter(event, { r, c }), onClick: () => onPick(r, c), "aria-label": t('table.insert', { rows: r, cols: c }), "data-test-id": tableCellTestId(r, c) }, `${r}-${c}`));
20
25
  }) }), _jsx("div", { className: styles.label, children: hover ? `${hover.r} × ${hover.c}` : t('table.pickSize') })] }) }));
21
26
  }