@cloud-ru/ds-markdown 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE +201 -0
  3. package/README.md +273 -0
  4. package/dist/cjs/components/Markdown/CodeBlock.d.ts +9 -0
  5. package/dist/cjs/components/Markdown/CodeBlock.js +58 -0
  6. package/dist/cjs/components/Markdown/Markdown.d.ts +2 -0
  7. package/dist/cjs/components/Markdown/Markdown.js +25 -0
  8. package/dist/cjs/components/Markdown/index.d.ts +1 -0
  9. package/dist/cjs/components/Markdown/index.js +17 -0
  10. package/dist/cjs/components/Markdown/styles.module.css +205 -0
  11. package/dist/cjs/components/MarkdownEditor/MarkdownEditor.d.ts +2 -0
  12. package/dist/cjs/components/MarkdownEditor/MarkdownEditor.js +176 -0
  13. package/dist/cjs/components/MarkdownEditor/index.d.ts +1 -0
  14. package/dist/cjs/components/MarkdownEditor/index.js +17 -0
  15. package/dist/cjs/components/MarkdownEditor/styles.module.css +273 -0
  16. package/dist/cjs/components/Toolbar/Toolbar.d.ts +9 -0
  17. package/dist/cjs/components/Toolbar/Toolbar.js +173 -0
  18. package/dist/cjs/components/Toolbar/constants.d.ts +13 -0
  19. package/dist/cjs/components/Toolbar/constants.js +40 -0
  20. package/dist/cjs/components/Toolbar/hooks/index.d.ts +2 -0
  21. package/dist/cjs/components/Toolbar/hooks/index.js +18 -0
  22. package/dist/cjs/components/Toolbar/hooks/useToolbarHotkeys.d.ts +21 -0
  23. package/dist/cjs/components/Toolbar/hooks/useToolbarHotkeys.js +59 -0
  24. package/dist/cjs/components/Toolbar/hooks/useToolbarOverflow.d.ts +14 -0
  25. package/dist/cjs/components/Toolbar/hooks/useToolbarOverflow.js +87 -0
  26. package/dist/cjs/components/Toolbar/index.d.ts +2 -0
  27. package/dist/cjs/components/Toolbar/index.js +20 -0
  28. package/dist/cjs/components/Toolbar/styles.module.css +17 -0
  29. package/dist/cjs/components/index.d.ts +2 -0
  30. package/dist/cjs/components/index.js +20 -0
  31. package/dist/cjs/constants.d.ts +65 -0
  32. package/dist/cjs/constants.js +67 -0
  33. package/dist/cjs/helperComponents/ButtonHeading/ButtonHeading.d.ts +6 -0
  34. package/dist/cjs/helperComponents/ButtonHeading/ButtonHeading.js +61 -0
  35. package/dist/cjs/helperComponents/ButtonHeading/index.d.ts +1 -0
  36. package/dist/cjs/helperComponents/ButtonHeading/index.js +17 -0
  37. package/dist/cjs/helperComponents/ButtonHeading/styles.module.css +3 -0
  38. package/dist/cjs/helperComponents/Buttons/Buttons.d.ts +10 -0
  39. package/dist/cjs/helperComponents/Buttons/Buttons.js +21 -0
  40. package/dist/cjs/helperComponents/Buttons/index.d.ts +1 -0
  41. package/dist/cjs/helperComponents/Buttons/index.js +17 -0
  42. package/dist/cjs/helperComponents/Buttons/styles.module.css +17 -0
  43. package/dist/cjs/helperComponents/Modals/CustomizeTableModal.d.ts +8 -0
  44. package/dist/cjs/helperComponents/Modals/CustomizeTableModal.js +39 -0
  45. package/dist/cjs/helperComponents/Modals/ImageModal.d.ts +7 -0
  46. package/dist/cjs/helperComponents/Modals/ImageModal.js +44 -0
  47. package/dist/cjs/helperComponents/Modals/LinkModal.d.ts +13 -0
  48. package/dist/cjs/helperComponents/Modals/LinkModal.js +42 -0
  49. package/dist/cjs/helperComponents/Modals/index.d.ts +3 -0
  50. package/dist/cjs/helperComponents/Modals/index.js +19 -0
  51. package/dist/cjs/helperComponents/Modals/styles.module.css +17 -0
  52. package/dist/cjs/helperComponents/PrivateButton/PrivateButton.d.ts +17 -0
  53. package/dist/cjs/helperComponents/PrivateButton/PrivateButton.js +27 -0
  54. package/dist/cjs/helperComponents/PrivateButton/index.d.ts +1 -0
  55. package/dist/cjs/helperComponents/PrivateButton/index.js +17 -0
  56. package/dist/cjs/helperComponents/PrivateButton/styles.module.css +140 -0
  57. package/dist/cjs/helperComponents/TableSelectItem/TableSelectItem.d.ts +11 -0
  58. package/dist/cjs/helperComponents/TableSelectItem/TableSelectItem.js +12 -0
  59. package/dist/cjs/helperComponents/TableSelectItem/index.d.ts +1 -0
  60. package/dist/cjs/helperComponents/TableSelectItem/index.js +17 -0
  61. package/dist/cjs/helperComponents/TableSelectItem/styles.module.css +101 -0
  62. package/dist/cjs/helperComponents/TableSelectSettingsGrid/TableSelectSettingsGrid.d.ts +4 -0
  63. package/dist/cjs/helperComponents/TableSelectSettingsGrid/TableSelectSettingsGrid.js +27 -0
  64. package/dist/cjs/helperComponents/TableSelectSettingsGrid/index.d.ts +1 -0
  65. package/dist/cjs/helperComponents/TableSelectSettingsGrid/index.js +17 -0
  66. package/dist/cjs/helperComponents/TableSelectSettingsGrid/styles.module.css +54 -0
  67. package/dist/cjs/helperComponents/index.d.ts +6 -0
  68. package/dist/cjs/helperComponents/index.js +22 -0
  69. package/dist/cjs/index.d.ts +4 -0
  70. package/dist/cjs/index.js +20 -0
  71. package/dist/cjs/locale/index.d.ts +199 -0
  72. package/dist/cjs/locale/index.js +104 -0
  73. package/dist/cjs/lowlight.d.ts +14 -0
  74. package/dist/cjs/lowlight.js +35 -0
  75. package/dist/cjs/mixins.css +0 -0
  76. package/dist/cjs/package.json +3 -0
  77. package/dist/cjs/rawMarkdownCommands.d.ts +16 -0
  78. package/dist/cjs/rawMarkdownCommands.js +145 -0
  79. package/dist/cjs/style.css +875 -0
  80. package/dist/cjs/styles/syntax.module.css +47 -0
  81. package/dist/cjs/toolbarApi.d.ts +33 -0
  82. package/dist/cjs/toolbarApi.js +100 -0
  83. package/dist/cjs/types.d.ts +52 -0
  84. package/dist/cjs/types.js +2 -0
  85. package/dist/esm/components/Markdown/CodeBlock.d.ts +9 -0
  86. package/dist/esm/components/Markdown/CodeBlock.js +52 -0
  87. package/dist/esm/components/Markdown/Markdown.d.ts +2 -0
  88. package/dist/esm/components/Markdown/Markdown.js +19 -0
  89. package/dist/esm/components/Markdown/index.d.ts +1 -0
  90. package/dist/esm/components/Markdown/index.js +1 -0
  91. package/dist/esm/components/Markdown/styles.module.css +205 -0
  92. package/dist/esm/components/MarkdownEditor/MarkdownEditor.d.ts +2 -0
  93. package/dist/esm/components/MarkdownEditor/MarkdownEditor.js +170 -0
  94. package/dist/esm/components/MarkdownEditor/index.d.ts +1 -0
  95. package/dist/esm/components/MarkdownEditor/index.js +1 -0
  96. package/dist/esm/components/MarkdownEditor/styles.module.css +273 -0
  97. package/dist/esm/components/Toolbar/Toolbar.d.ts +9 -0
  98. package/dist/esm/components/Toolbar/Toolbar.js +167 -0
  99. package/dist/esm/components/Toolbar/constants.d.ts +13 -0
  100. package/dist/esm/components/Toolbar/constants.js +37 -0
  101. package/dist/esm/components/Toolbar/hooks/index.d.ts +2 -0
  102. package/dist/esm/components/Toolbar/hooks/index.js +2 -0
  103. package/dist/esm/components/Toolbar/hooks/useToolbarHotkeys.d.ts +21 -0
  104. package/dist/esm/components/Toolbar/hooks/useToolbarHotkeys.js +56 -0
  105. package/dist/esm/components/Toolbar/hooks/useToolbarOverflow.d.ts +14 -0
  106. package/dist/esm/components/Toolbar/hooks/useToolbarOverflow.js +84 -0
  107. package/dist/esm/components/Toolbar/index.d.ts +2 -0
  108. package/dist/esm/components/Toolbar/index.js +2 -0
  109. package/dist/esm/components/Toolbar/styles.module.css +17 -0
  110. package/dist/esm/components/index.d.ts +2 -0
  111. package/dist/esm/components/index.js +4 -0
  112. package/dist/esm/constants.d.ts +65 -0
  113. package/dist/esm/constants.js +61 -0
  114. package/dist/esm/helperComponents/ButtonHeading/ButtonHeading.d.ts +6 -0
  115. package/dist/esm/helperComponents/ButtonHeading/ButtonHeading.js +55 -0
  116. package/dist/esm/helperComponents/ButtonHeading/index.d.ts +1 -0
  117. package/dist/esm/helperComponents/ButtonHeading/index.js +1 -0
  118. package/dist/esm/helperComponents/ButtonHeading/styles.module.css +3 -0
  119. package/dist/esm/helperComponents/Buttons/Buttons.d.ts +10 -0
  120. package/dist/esm/helperComponents/Buttons/Buttons.js +13 -0
  121. package/dist/esm/helperComponents/Buttons/index.d.ts +1 -0
  122. package/dist/esm/helperComponents/Buttons/index.js +1 -0
  123. package/dist/esm/helperComponents/Buttons/styles.module.css +17 -0
  124. package/dist/esm/helperComponents/Modals/CustomizeTableModal.d.ts +8 -0
  125. package/dist/esm/helperComponents/Modals/CustomizeTableModal.js +33 -0
  126. package/dist/esm/helperComponents/Modals/ImageModal.d.ts +7 -0
  127. package/dist/esm/helperComponents/Modals/ImageModal.js +38 -0
  128. package/dist/esm/helperComponents/Modals/LinkModal.d.ts +13 -0
  129. package/dist/esm/helperComponents/Modals/LinkModal.js +36 -0
  130. package/dist/esm/helperComponents/Modals/index.d.ts +3 -0
  131. package/dist/esm/helperComponents/Modals/index.js +3 -0
  132. package/dist/esm/helperComponents/Modals/styles.module.css +17 -0
  133. package/dist/esm/helperComponents/PrivateButton/PrivateButton.d.ts +17 -0
  134. package/dist/esm/helperComponents/PrivateButton/PrivateButton.js +21 -0
  135. package/dist/esm/helperComponents/PrivateButton/index.d.ts +1 -0
  136. package/dist/esm/helperComponents/PrivateButton/index.js +1 -0
  137. package/dist/esm/helperComponents/PrivateButton/styles.module.css +140 -0
  138. package/dist/esm/helperComponents/TableSelectItem/TableSelectItem.d.ts +11 -0
  139. package/dist/esm/helperComponents/TableSelectItem/TableSelectItem.js +6 -0
  140. package/dist/esm/helperComponents/TableSelectItem/index.d.ts +1 -0
  141. package/dist/esm/helperComponents/TableSelectItem/index.js +1 -0
  142. package/dist/esm/helperComponents/TableSelectItem/styles.module.css +101 -0
  143. package/dist/esm/helperComponents/TableSelectSettingsGrid/TableSelectSettingsGrid.d.ts +4 -0
  144. package/dist/esm/helperComponents/TableSelectSettingsGrid/TableSelectSettingsGrid.js +21 -0
  145. package/dist/esm/helperComponents/TableSelectSettingsGrid/index.d.ts +1 -0
  146. package/dist/esm/helperComponents/TableSelectSettingsGrid/index.js +1 -0
  147. package/dist/esm/helperComponents/TableSelectSettingsGrid/styles.module.css +54 -0
  148. package/dist/esm/helperComponents/index.d.ts +6 -0
  149. package/dist/esm/helperComponents/index.js +6 -0
  150. package/dist/esm/index.d.ts +4 -0
  151. package/dist/esm/index.js +4 -0
  152. package/dist/esm/locale/index.d.ts +199 -0
  153. package/dist/esm/locale/index.js +101 -0
  154. package/dist/esm/lowlight.d.ts +14 -0
  155. package/dist/esm/lowlight.js +29 -0
  156. package/dist/esm/mixins.css +0 -0
  157. package/dist/esm/rawMarkdownCommands.d.ts +16 -0
  158. package/dist/esm/rawMarkdownCommands.js +135 -0
  159. package/dist/esm/style.css +875 -0
  160. package/dist/esm/styles/syntax.module.css +47 -0
  161. package/dist/esm/toolbarApi.d.ts +33 -0
  162. package/dist/esm/toolbarApi.js +96 -0
  163. package/dist/esm/types.d.ts +52 -0
  164. package/dist/esm/types.js +1 -0
  165. package/dist/tsconfig.cjs.tsbuildinfo +1 -0
  166. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  167. package/package.json +84 -0
  168. package/src/components/Markdown/CodeBlock.tsx +96 -0
  169. package/src/components/Markdown/Markdown.tsx +47 -0
  170. package/src/components/Markdown/index.ts +1 -0
  171. package/src/components/Markdown/styles.module.scss +105 -0
  172. package/src/components/MarkdownEditor/MarkdownEditor.tsx +322 -0
  173. package/src/components/MarkdownEditor/index.ts +1 -0
  174. package/src/components/MarkdownEditor/styles.module.scss +201 -0
  175. package/src/components/Toolbar/Toolbar.tsx +292 -0
  176. package/src/components/Toolbar/constants.tsx +63 -0
  177. package/src/components/Toolbar/hooks/index.ts +2 -0
  178. package/src/components/Toolbar/hooks/useToolbarHotkeys.ts +74 -0
  179. package/src/components/Toolbar/hooks/useToolbarOverflow.ts +102 -0
  180. package/src/components/Toolbar/index.ts +2 -0
  181. package/src/components/Toolbar/styles.module.scss +29 -0
  182. package/src/components/index.ts +4 -0
  183. package/src/constants.ts +67 -0
  184. package/src/helperComponents/ButtonHeading/ButtonHeading.tsx +100 -0
  185. package/src/helperComponents/ButtonHeading/index.ts +1 -0
  186. package/src/helperComponents/ButtonHeading/styles.module.scss +5 -0
  187. package/src/helperComponents/Buttons/Buttons.tsx +30 -0
  188. package/src/helperComponents/Buttons/index.ts +1 -0
  189. package/src/helperComponents/Buttons/styles.module.scss +26 -0
  190. package/src/helperComponents/Modals/CustomizeTableModal.tsx +82 -0
  191. package/src/helperComponents/Modals/ImageModal.tsx +84 -0
  192. package/src/helperComponents/Modals/LinkModal.tsx +92 -0
  193. package/src/helperComponents/Modals/index.ts +3 -0
  194. package/src/helperComponents/Modals/styles.module.scss +21 -0
  195. package/src/helperComponents/PrivateButton/PrivateButton.tsx +88 -0
  196. package/src/helperComponents/PrivateButton/index.ts +1 -0
  197. package/src/helperComponents/PrivateButton/styles.module.scss +77 -0
  198. package/src/helperComponents/TableSelectItem/TableSelectItem.tsx +43 -0
  199. package/src/helperComponents/TableSelectItem/index.ts +1 -0
  200. package/src/helperComponents/TableSelectItem/styles.module.scss +26 -0
  201. package/src/helperComponents/TableSelectSettingsGrid/TableSelectSettingsGrid.tsx +48 -0
  202. package/src/helperComponents/TableSelectSettingsGrid/index.ts +1 -0
  203. package/src/helperComponents/TableSelectSettingsGrid/styles.module.scss +65 -0
  204. package/src/helperComponents/index.ts +6 -0
  205. package/src/index.ts +4 -0
  206. package/src/locale/index.ts +105 -0
  207. package/src/lowlight.ts +30 -0
  208. package/src/mixins.scss +123 -0
  209. package/src/rawMarkdownCommands.ts +179 -0
  210. package/src/styles/syntax.module.scss +54 -0
  211. package/src/toolbarApi.ts +152 -0
  212. package/src/types.ts +56 -0
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { BlockCodeSVG, BlockQuoteSVG, BoldSVG, BulletListSVG, HeadingSVG, ImageSVG, InlineCodeSVG, ItalicSVG, LinkSVG, OrderedListSVG, StrikeSVG, TableSVG, } from '@cloud-ru/ds-icons/interface/system';
3
+ import { TOOLBAR_ITEM } from '../../constants.js';
4
+ // Группировка: text-style | inline | lists | blocks | media.
5
+ export const GROUPS = [
6
+ [TOOLBAR_ITEM.Heading, TOOLBAR_ITEM.Bold, TOOLBAR_ITEM.Italic, TOOLBAR_ITEM.Strikethrough],
7
+ [TOOLBAR_ITEM.Link, TOOLBAR_ITEM.InlineCode, TOOLBAR_ITEM.BulletList, TOOLBAR_ITEM.OrderedList],
8
+ [TOOLBAR_ITEM.BlockQuote, TOOLBAR_ITEM.BlockCode, TOOLBAR_ITEM.Table, TOOLBAR_ITEM.Image],
9
+ ];
10
+ export const BUTTONS = {
11
+ [TOOLBAR_ITEM.Heading]: { icon: _jsx(HeadingSVG, {}), nameKey: 'toolbar.heading' },
12
+ [TOOLBAR_ITEM.Bold]: { icon: _jsx(BoldSVG, {}), nameKey: 'toolbar.bold', hotkey: 'Ctrl+B' },
13
+ [TOOLBAR_ITEM.Italic]: { icon: _jsx(ItalicSVG, {}), nameKey: 'toolbar.italic', hotkey: 'Ctrl+I' },
14
+ [TOOLBAR_ITEM.Strikethrough]: { icon: _jsx(StrikeSVG, {}), nameKey: 'toolbar.strikethrough', hotkey: 'Ctrl+Shift+X' },
15
+ [TOOLBAR_ITEM.Link]: { icon: _jsx(LinkSVG, {}), nameKey: 'toolbar.link', hotkey: 'Ctrl+K' },
16
+ [TOOLBAR_ITEM.Image]: { icon: _jsx(ImageSVG, {}), nameKey: 'toolbar.image', hotkey: 'Ctrl+Shift+I' },
17
+ [TOOLBAR_ITEM.Table]: { icon: _jsx(TableSVG, {}), nameKey: 'toolbar.table' },
18
+ [TOOLBAR_ITEM.InlineCode]: { icon: _jsx(InlineCodeSVG, {}), nameKey: 'toolbar.inlineCode', hotkey: 'Ctrl+E' },
19
+ [TOOLBAR_ITEM.BulletList]: { icon: _jsx(BulletListSVG, {}), nameKey: 'toolbar.bulletList', hotkey: 'Ctrl+Shift+8' },
20
+ [TOOLBAR_ITEM.OrderedList]: { icon: _jsx(OrderedListSVG, {}), nameKey: 'toolbar.orderedList', hotkey: 'Ctrl+Shift+7' },
21
+ [TOOLBAR_ITEM.BlockQuote]: { icon: _jsx(BlockQuoteSVG, {}), nameKey: 'toolbar.blockQuote', hotkey: 'Ctrl+Shift+B' },
22
+ [TOOLBAR_ITEM.BlockCode]: { icon: _jsx(BlockCodeSVG, {}), nameKey: 'toolbar.blockCode', hotkey: 'Ctrl+Alt+C' },
23
+ };
24
+ export const DEFAULT_TOOLBAR = [
25
+ TOOLBAR_ITEM.Heading,
26
+ TOOLBAR_ITEM.Bold,
27
+ TOOLBAR_ITEM.Italic,
28
+ TOOLBAR_ITEM.Strikethrough,
29
+ TOOLBAR_ITEM.Link,
30
+ TOOLBAR_ITEM.InlineCode,
31
+ TOOLBAR_ITEM.BulletList,
32
+ TOOLBAR_ITEM.OrderedList,
33
+ TOOLBAR_ITEM.BlockQuote,
34
+ TOOLBAR_ITEM.BlockCode,
35
+ TOOLBAR_ITEM.Table,
36
+ TOOLBAR_ITEM.Image,
37
+ ];
@@ -0,0 +1,2 @@
1
+ export * from './useToolbarHotkeys.js';
2
+ export * from './useToolbarOverflow.js';
@@ -0,0 +1,2 @@
1
+ export * from './useToolbarHotkeys.js';
2
+ export * from './useToolbarOverflow.js';
@@ -0,0 +1,21 @@
1
+ import { RefObject } from 'react';
2
+ import { ToolbarApi } from '../../../toolbarApi.js';
3
+ import { ToolbarItemId } from '../../../types.js';
4
+ type ToolbarHotkeysParams = {
5
+ /** Корень тулбара (из useToolbarOverflow) — от его родителя ищем scope-предка для listener'а. */
6
+ rootRef: RefObject<HTMLDivElement | null>;
7
+ items: ToolbarItemId[];
8
+ api: ToolbarApi;
9
+ /** Действие кнопки Link (открыть модалку или вставить сырой markdown). */
10
+ onLink(): void;
11
+ /** Действие кнопки Image (открыть модалку). */
12
+ onImage(): void;
13
+ };
14
+ /**
15
+ * Вешает хоткеи тулбара на корень редактора (scope), а не на конкретный input:
16
+ * - Link/Image открывают UI (модалку/пикер), а не команды TipTap → биндим всегда;
17
+ * - остальные кнопки (bold/italic/…) в WYSIWYG обрабатывает сам keymap TipTap, а в raw-режиме
18
+ * textarea их не знает, поэтому в raw биндим все hotkey'и на api-команды вручную.
19
+ */
20
+ export declare function useToolbarHotkeys({ rootRef, items, api, onLink, onImage }: ToolbarHotkeysParams): void;
21
+ export {};
@@ -0,0 +1,56 @@
1
+ import { useEffect } from 'react';
2
+ import { TOOLBAR_ITEM } from '../../../constants.js';
3
+ import { BUTTONS } from '../constants.js';
4
+ // Сопоставляет KeyboardEvent с хоткеем вида 'Ctrl+Shift+I' (Cmd подменяет Ctrl на macOS).
5
+ const matchesHotkey = (event, hotkey) => {
6
+ const parts = hotkey.toLowerCase().split('+');
7
+ const last = parts[parts.length - 1];
8
+ // С Shift цифры дают символ (Shift+8 → '*'), поэтому помимо event.key матчим и по event.code
9
+ // (Digit8 / KeyB) — иначе Ctrl+Shift+8/7 не распознались бы.
10
+ const code = event.code.toLowerCase();
11
+ const keyMatches = event.key.toLowerCase() === last || code === `key${last}` || code === `digit${last}`;
12
+ return ((event.ctrlKey || event.metaKey) === parts.includes('ctrl') &&
13
+ event.shiftKey === parts.includes('shift') &&
14
+ event.altKey === parts.includes('alt') &&
15
+ keyMatches);
16
+ };
17
+ /**
18
+ * Вешает хоткеи тулбара на корень редактора (scope), а не на конкретный input:
19
+ * - Link/Image открывают UI (модалку/пикер), а не команды TipTap → биндим всегда;
20
+ * - остальные кнопки (bold/italic/…) в WYSIWYG обрабатывает сам keymap TipTap, а в raw-режиме
21
+ * textarea их не знает, поэтому в raw биндим все hotkey'и на api-команды вручную.
22
+ */
23
+ export function useToolbarHotkeys({ rootRef, items, api, onLink, onImage }) {
24
+ useEffect(() => {
25
+ // Ищем scope от РОДИТЕЛЯ тулбара: у самого toolbar-root есть data-test-id, и closest вернул бы
26
+ // его же — а input (textarea/ProseMirror) лежит рядом, не внутри тулбара. Нужен общий предок
27
+ // (оболочка поля), который содержит и тулбар, и input, чтобы keydown из input всплывал к нему.
28
+ const scope = rootRef.current?.parentElement?.closest('[data-test-id]');
29
+ if (!scope)
30
+ return;
31
+ const bindings = [];
32
+ for (const id of items) {
33
+ const hotkey = BUTTONS[id]?.hotkey;
34
+ if (!hotkey)
35
+ continue;
36
+ if (id === TOOLBAR_ITEM.Link) {
37
+ bindings.push({ hotkey, action: onLink });
38
+ }
39
+ else if (id === TOOLBAR_ITEM.Image) {
40
+ bindings.push({ hotkey, action: onImage });
41
+ }
42
+ else if (api.mode === 'raw') {
43
+ bindings.push({ hotkey, action: () => api.toggle(id) });
44
+ }
45
+ }
46
+ const handleKeyDown = (event) => {
47
+ const hit = bindings.find(binding => matchesHotkey(event, binding.hotkey));
48
+ if (hit) {
49
+ event.preventDefault();
50
+ hit.action();
51
+ }
52
+ };
53
+ scope.addEventListener('keydown', handleKeyDown);
54
+ return () => scope.removeEventListener('keydown', handleKeyDown);
55
+ }, [items, api, onLink, onImage, rootRef]);
56
+ }
@@ -0,0 +1,14 @@
1
+ import { ToolbarItemId } from '../../../types.js';
2
+ /**
3
+ * Считает, сколько кнопок тулбара помещается по ширине, остальные — в overflow («Ещё»).
4
+ * Возвращает `rootRef` (корень тулбара, под ResizeObserver), `moreButtonRef` (для измерения
5
+ * «веса» overflow-группы) и `visibleCount` (число видимых кнопок).
6
+ *
7
+ * Края слотов кэшируются в полной раскладке (все кнопки, без More) и переиспользуются при
8
+ * пересчёте — поэтому не нужно арифметически приближать гэпы/дивайдеры.
9
+ */
10
+ export declare function useToolbarOverflow(items: ToolbarItemId[]): {
11
+ rootRef: import("react").MutableRefObject<HTMLDivElement | null>;
12
+ moreButtonRef: import("react").RefObject<HTMLElement>;
13
+ visibleCount: number;
14
+ };
@@ -0,0 +1,84 @@
1
+ import { useLayoutEffect } from '@cloud-ru/ds-utils';
2
+ import { useRef, useState } from 'react';
3
+ /**
4
+ * Считает, сколько кнопок тулбара помещается по ширине, остальные — в overflow («Ещё»).
5
+ * Возвращает `rootRef` (корень тулбара, под ResizeObserver), `moreButtonRef` (для измерения
6
+ * «веса» overflow-группы) и `visibleCount` (число видимых кнопок).
7
+ *
8
+ * Края слотов кэшируются в полной раскладке (все кнопки, без More) и переиспользуются при
9
+ * пересчёте — поэтому не нужно арифметически приближать гэпы/дивайдеры.
10
+ */
11
+ export function useToolbarOverflow(items) {
12
+ const rootRef = useRef(null);
13
+ const moreButtonRef = useRef(null);
14
+ // Правый край каждого слота (отн. левого края тулбара), измеренный в ПОЛНОЙ раскладке.
15
+ const slotEdgesRef = useRef(new Map());
16
+ // Реальная ширина группы «дивайдер + More» — измеряется, когда More присутствует в DOM.
17
+ const moreReserveRef = useRef(48);
18
+ const [visibleCount, setVisibleCount] = useState(items.length);
19
+ useLayoutEffect(() => {
20
+ const root = rootRef.current;
21
+ if (!root) {
22
+ return;
23
+ }
24
+ const edges = slotEdgesRef.current;
25
+ edges.clear();
26
+ let rafId = 0;
27
+ const measure = () => {
28
+ const rootRect = root.getBoundingClientRect();
29
+ const padRight = parseFloat(getComputedStyle(root).paddingRight) || 0;
30
+ // Правая граница контентной области тулбара (отн. левого края root).
31
+ const limit = root.clientWidth - padRight;
32
+ const slots = Array.from(root.querySelectorAll('[data-toolbar-slot]'));
33
+ // Кэшируем края только в полной раскладке (все items, без More) — иначе край
34
+ // зависел бы от текущего visibleCount/набора дивайдеров и был бы неверным для пересчёта.
35
+ if (slots.length === items.length) {
36
+ for (const slot of slots) {
37
+ const id = slot.dataset.toolbarItem;
38
+ if (id) {
39
+ edges.set(id, slot.getBoundingClientRect().right - rootRect.left);
40
+ }
41
+ }
42
+ }
43
+ // Реальный «вес» More-группы (дивайдер + гэпы + кнопка): от правого края последнего
44
+ // видимого слота до правого края More-кнопки. Уточняется, как только More появляется.
45
+ const moreEl = moreButtonRef.current;
46
+ if (moreEl && slots.length > 0) {
47
+ const lastRight = slots[slots.length - 1].getBoundingClientRect().right - rootRect.left - moreReserveRef.current;
48
+ moreReserveRef.current = moreEl.getBoundingClientRect().right - rootRect.left - lastRight;
49
+ }
50
+ // Края ещё не собраны (нет полного рендера, напр. items только что изменились) —
51
+ // показываем всё и перезамеряем на следующем кадре, когда все слоты окажутся в DOM.
52
+ if (!items.every(id => edges.has(id))) {
53
+ setVisibleCount(items.length);
54
+ rafId = requestAnimationFrame(measure);
55
+ return;
56
+ }
57
+ // Всё помещается без overflow — показываем все кнопки, без More.
58
+ const fullRight = edges.get(items[items.length - 1]) ?? 0;
59
+ if (fullRight <= limit) {
60
+ setVisibleCount(items.length);
61
+ return;
62
+ }
63
+ // Иначе — максимум k кнопок, после которых ещё влезает группа «│ More».
64
+ const reserve = moreReserveRef.current;
65
+ let fits = 0;
66
+ for (const id of items) {
67
+ if ((edges.get(id) ?? 0) + reserve > limit) {
68
+ break;
69
+ }
70
+ fits += 1;
71
+ }
72
+ setVisibleCount(Math.max(fits, 1));
73
+ };
74
+ measure();
75
+ const observer = new ResizeObserver(measure);
76
+ observer.observe(root);
77
+ return () => {
78
+ observer.disconnect();
79
+ if (rafId)
80
+ cancelAnimationFrame(rafId);
81
+ };
82
+ }, [items]);
83
+ return { rootRef, moreButtonRef, visibleCount };
84
+ }
@@ -0,0 +1,2 @@
1
+ export * from './Toolbar.js';
2
+ export { DEFAULT_TOOLBAR } from './constants.js';
@@ -0,0 +1,2 @@
1
+ export * from './Toolbar.js';
2
+ export { DEFAULT_TOOLBAR } from './constants.js';
@@ -0,0 +1,17 @@
1
+ .root{
2
+ overflow:hidden;
3
+ display:flex;
4
+ flex-shrink:0;
5
+ align-items:center;
6
+ box-sizing:border-box;
7
+ padding:var(--sn-markdown-anatomy-size-buttons-paddingTop, var(--sn-primitive-dimension-8, 8px)) var(--sn-markdown-anatomy-size-buttons-paddingHorizontal, var(--sn-primitive-dimension-8, 8px)) var(--sn-markdown-anatomy-size-buttons-paddingBottom, var(--sn-primitive-dimension-6, 6px));
8
+ }
9
+
10
+ .tooltipTrigger{
11
+ display:inline-flex;
12
+ }
13
+
14
+ .tableCustomizeOption{
15
+ align-items:flex-start;
16
+ align-self:center;
17
+ }
@@ -0,0 +1,2 @@
1
+ export * from './Markdown/index.js';
2
+ export * from './MarkdownEditor/index.js';
@@ -0,0 +1,4 @@
1
+ // Публичные компоненты пакета. Toolbar — внутренний,
2
+ // импортируется напрямую по относительным путям и наружу не экспортируется.
3
+ export * from './Markdown/index.js';
4
+ export * from './MarkdownEditor/index.js';
@@ -0,0 +1,65 @@
1
+ export declare const TOOLBAR_ITEM: {
2
+ readonly Heading: "heading";
3
+ readonly Bold: "bold";
4
+ readonly Italic: "italic";
5
+ readonly Strikethrough: "strikethrough";
6
+ readonly Link: "link";
7
+ readonly InlineCode: "inline-code";
8
+ readonly BulletList: "bullet-list";
9
+ readonly OrderedList: "ordered-list";
10
+ readonly BlockQuote: "block-quote";
11
+ readonly BlockCode: "block-code";
12
+ readonly Table: "table";
13
+ readonly Image: "image";
14
+ };
15
+ export declare const HEADING_LEVEL: {
16
+ readonly H1: 1;
17
+ readonly H2: 2;
18
+ readonly H3: 3;
19
+ readonly H4: 4;
20
+ readonly H5: 5;
21
+ };
22
+ export declare const ON_CHANGE_DEBOUNCE_MS = 150;
23
+ export declare const TEST_IDS: {
24
+ readonly viewer: "markdown";
25
+ readonly viewerCodeCopy: "markdown__code-copy";
26
+ readonly editor: "markdown-editor";
27
+ readonly editorHeader: "markdown-editor__header";
28
+ readonly editorPreviewToggle: "markdown-editor__preview-toggle";
29
+ readonly editorLabel: "markdown-editor__label";
30
+ readonly editorClear: "markdown-editor__clear";
31
+ readonly editorContent: "markdown-editor__content";
32
+ readonly editorField: "markdown-editor__field";
33
+ readonly editorRawInput: "markdown-editor__raw-input";
34
+ readonly toolbar: "markdown-editor__toolbar";
35
+ readonly toolbarButton: "markdown-editor__toolbar-button";
36
+ readonly toolbarMore: "markdown-editor__toolbar-more";
37
+ readonly toolbarMoreList: "markdown-editor__toolbar-more-list";
38
+ readonly toolbarHeading: "markdown-editor__toolbar-heading";
39
+ readonly headingDropdown: "markdown-editor__heading-dropdown";
40
+ readonly headingOption: "markdown-editor__heading-option";
41
+ readonly tableGridPicker: "markdown-editor__table-picker";
42
+ readonly tablePickerCell: "markdown-editor__table-cell";
43
+ readonly tableCustomize: "markdown-editor__table-customize";
44
+ readonly customizeModal: "markdown-editor__customize-table";
45
+ readonly customizeColumns: "markdown-editor__customize-table-columns";
46
+ readonly customizeRows: "markdown-editor__customize-table-rows";
47
+ readonly customizeCancel: "markdown-editor__customize-table-cancel";
48
+ readonly customizeAdd: "markdown-editor__customize-table-add";
49
+ readonly linkModal: "markdown-editor__link-modal";
50
+ readonly linkModalUrl: "markdown-editor__link-modal-url";
51
+ readonly linkModalTitle: "markdown-editor__link-modal-title";
52
+ readonly linkModalCancel: "markdown-editor__link-modal-cancel";
53
+ readonly linkModalAdd: "markdown-editor__link-modal-add";
54
+ readonly imageModal: "markdown-editor__image-modal";
55
+ readonly imageModalUrl: "markdown-editor__image-modal-url";
56
+ readonly imageModalAlt: "markdown-editor__image-modal-alt";
57
+ readonly imageModalCancel: "markdown-editor__image-modal-cancel";
58
+ readonly imageModalAdd: "markdown-editor__image-modal-add";
59
+ };
60
+ /** Стабильный data-test-id конкретной кнопки тулбара (`markdown-editor__toolbar-button--bold`). */
61
+ export declare const toolbarButtonTestId: (item: string) => string;
62
+ /** data-test-id пункта heading-дропдауна (`…__heading-option--paragraph` | `…--2`). */
63
+ export declare const headingOptionTestId: (level: number | "paragraph") => string;
64
+ /** data-test-id ячейки table-picker'а (`…__table-cell--2x3`). */
65
+ export declare const tableCellTestId: (rows: number, cols: number) => string;
@@ -0,0 +1,61 @@
1
+ export const TOOLBAR_ITEM = {
2
+ Heading: 'heading',
3
+ Bold: 'bold',
4
+ Italic: 'italic',
5
+ Strikethrough: 'strikethrough',
6
+ Link: 'link',
7
+ InlineCode: 'inline-code',
8
+ BulletList: 'bullet-list',
9
+ OrderedList: 'ordered-list',
10
+ BlockQuote: 'block-quote',
11
+ BlockCode: 'block-code',
12
+ Table: 'table',
13
+ Image: 'image',
14
+ };
15
+ export const HEADING_LEVEL = { H1: 1, H2: 2, H3: 3, H4: 4, H5: 5 };
16
+ export const ON_CHANGE_DEBOUNCE_MS = 150;
17
+ export const TEST_IDS = {
18
+ viewer: 'markdown',
19
+ viewerCodeCopy: 'markdown__code-copy',
20
+ editor: 'markdown-editor',
21
+ editorHeader: 'markdown-editor__header',
22
+ editorPreviewToggle: 'markdown-editor__preview-toggle',
23
+ editorLabel: 'markdown-editor__label',
24
+ editorClear: 'markdown-editor__clear',
25
+ editorContent: 'markdown-editor__content',
26
+ // Оболочка поля (FieldShell из @cloud-ru/ds-fields) — общая для raw и preview.
27
+ editorField: 'markdown-editor__field',
28
+ // Нативная textarea в raw-режиме.
29
+ editorRawInput: 'markdown-editor__raw-input',
30
+ toolbar: 'markdown-editor__toolbar',
31
+ toolbarButton: 'markdown-editor__toolbar-button',
32
+ toolbarMore: 'markdown-editor__toolbar-more',
33
+ toolbarMoreList: 'markdown-editor__toolbar-more-list',
34
+ toolbarHeading: 'markdown-editor__toolbar-heading',
35
+ headingDropdown: 'markdown-editor__heading-dropdown',
36
+ headingOption: 'markdown-editor__heading-option',
37
+ tableGridPicker: 'markdown-editor__table-picker',
38
+ tablePickerCell: 'markdown-editor__table-cell',
39
+ tableCustomize: 'markdown-editor__table-customize',
40
+ customizeModal: 'markdown-editor__customize-table',
41
+ customizeColumns: 'markdown-editor__customize-table-columns',
42
+ customizeRows: 'markdown-editor__customize-table-rows',
43
+ customizeCancel: 'markdown-editor__customize-table-cancel',
44
+ customizeAdd: 'markdown-editor__customize-table-add',
45
+ linkModal: 'markdown-editor__link-modal',
46
+ linkModalUrl: 'markdown-editor__link-modal-url',
47
+ linkModalTitle: 'markdown-editor__link-modal-title',
48
+ linkModalCancel: 'markdown-editor__link-modal-cancel',
49
+ linkModalAdd: 'markdown-editor__link-modal-add',
50
+ imageModal: 'markdown-editor__image-modal',
51
+ imageModalUrl: 'markdown-editor__image-modal-url',
52
+ imageModalAlt: 'markdown-editor__image-modal-alt',
53
+ imageModalCancel: 'markdown-editor__image-modal-cancel',
54
+ imageModalAdd: 'markdown-editor__image-modal-add',
55
+ };
56
+ /** Стабильный data-test-id конкретной кнопки тулбара (`markdown-editor__toolbar-button--bold`). */
57
+ export const toolbarButtonTestId = (item) => `${TEST_IDS.toolbarButton}--${item}`;
58
+ /** data-test-id пункта heading-дропдауна (`…__heading-option--paragraph` | `…--2`). */
59
+ export const headingOptionTestId = (level) => `${TEST_IDS.headingOption}--${level}`;
60
+ /** data-test-id ячейки table-picker'а (`…__table-cell--2x3`). */
61
+ export const tableCellTestId = (rows, cols) => `${TEST_IDS.tablePickerCell}--${rows}x${cols}`;
@@ -0,0 +1,6 @@
1
+ import { ToolbarApi } from '../../toolbarApi.js';
2
+ export type ButtonHeadingProps = {
3
+ /** Бэкенд команд тулбара (WYSIWYG либо raw markdown). */
4
+ api: ToolbarApi;
5
+ };
6
+ export declare function ButtonHeading({ api }: ButtonHeadingProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Heading1SVG, Heading2SVG, Heading3SVG, Heading4SVG, Heading5SVG, HeadingSVG, } from '@cloud-ru/ds-icons/interface/system';
3
+ import { Droplist } from '@cloud-ru/ds-list';
4
+ import { usePortalContext } from '@cloud-ru/ds-portal-context';
5
+ import { useState } from 'react';
6
+ import { headingOptionTestId, TEST_IDS, TOOLBAR_ITEM } from '../../constants.js';
7
+ import { markdownLocale } from '../../locale/index.js';
8
+ import { PrivateButton } from '../PrivateButton/index.js';
9
+ import styles from './styles.module.css';
10
+ // Кнопка-триггер выбора уровня заголовка: иконка текущего уровня. Открывает Droplist со списком уровней.
11
+ const HEADING_LEVELS = [1, 2, 3, 4, 5];
12
+ const ICON_FOR = {
13
+ 1: Heading1SVG,
14
+ 2: Heading2SVG,
15
+ 3: Heading3SVG,
16
+ 4: Heading4SVG,
17
+ 5: Heading5SVG,
18
+ };
19
+ export function ButtonHeading({ api }) {
20
+ const portalContext = usePortalContext();
21
+ const { t } = markdownLocale.useTranslations();
22
+ const [open, setOpen] = useState(false);
23
+ const activeLevel = HEADING_LEVELS.find(lvl => api.isHeadingActive(lvl));
24
+ const TriggerIcon = activeLevel != null ? ICON_FOR[activeLevel] : HeadingSVG;
25
+ const selectLevel = (lvl) => {
26
+ if (lvl == null) {
27
+ api.setParagraph();
28
+ }
29
+ else {
30
+ api.toggleHeading(lvl);
31
+ }
32
+ setOpen(false);
33
+ };
34
+ const items = [
35
+ {
36
+ id: 'paragraph',
37
+ content: { label: 'Paragraph' },
38
+ checked: activeLevel == null,
39
+ onClick: () => selectLevel(null),
40
+ 'data-test-id': headingOptionTestId('paragraph'),
41
+ },
42
+ ...HEADING_LEVELS.map((lvl) => {
43
+ const Icon = ICON_FOR[lvl];
44
+ return {
45
+ id: `heading-${lvl}`,
46
+ beforeContent: _jsx(Icon, {}),
47
+ content: { label: `Heading ${lvl}` },
48
+ checked: activeLevel === lvl,
49
+ onClick: () => selectLevel(lvl),
50
+ 'data-test-id': headingOptionTestId(lvl),
51
+ };
52
+ }),
53
+ ];
54
+ return (_jsx(Droplist, { size: 'm', selection: { mode: 'single', value: activeLevel }, container: portalContext, open: open, onOpenChange: setOpen, placement: 'bottom-start', items: items, triggerClassName: styles.trigger, "data-test-id": TEST_IDS.headingDropdown, children: _jsx(PrivateButton, { icon: _jsx(TriggerIcon, {}), tip: { title: t('toolbar.heading') }, withChevron: true, checked: activeLevel != null, "aria-label": t('toolbar.heading'), "data-toolbar-slot": true, "data-toolbar-item": TOOLBAR_ITEM.Heading, "data-test-id": TEST_IDS.toolbarHeading, "aria-expanded": open }) }));
55
+ }
@@ -0,0 +1 @@
1
+ export * from './ButtonHeading.js';
@@ -0,0 +1 @@
1
+ export * from './ButtonHeading.js';
@@ -0,0 +1,3 @@
1
+ .trigger{
2
+ display:inline-flex;
3
+ }
@@ -0,0 +1,10 @@
1
+ import { ReactNode } from 'react';
2
+ export type ButtonsProps = {
3
+ children: ReactNode;
4
+ };
5
+ export declare function Buttons({ children }: ButtonsProps): import("react/jsx-runtime").JSX.Element;
6
+ export type ButtonsGroupProps = {
7
+ children: ReactNode;
8
+ };
9
+ export declare function ButtonsGroup({ children }: ButtonsGroupProps): import("react/jsx-runtime").JSX.Element;
10
+ export declare function ButtonsDivider(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Divider } from '@cloud-ru/ds-divider';
3
+ import { TEST_IDS } from '../../constants.js';
4
+ import styles from './styles.module.css';
5
+ export function Buttons({ children }) {
6
+ return (_jsx("div", { className: styles.root, "data-test-id": TEST_IDS.toolbar, role: 'toolbar', children: children }));
7
+ }
8
+ export function ButtonsGroup({ children }) {
9
+ return _jsx("div", { className: styles.group, children: children });
10
+ }
11
+ export function ButtonsDivider() {
12
+ return _jsx(Divider, { orientation: 'vertical', className: styles.divider });
13
+ }
@@ -0,0 +1 @@
1
+ export * from './Buttons.js';
@@ -0,0 +1 @@
1
+ export * from './Buttons.js';
@@ -0,0 +1,17 @@
1
+ .root{
2
+ overflow:hidden;
3
+ display:flex;
4
+ flex:0 0 auto;
5
+ gap:var(--sn-markdown-anatomy-size-buttons-gap, var(--sn-primitive-dimension-8, 8px));
6
+ box-sizing:border-box;
7
+ }
8
+
9
+ .group{
10
+ display:flex;
11
+ gap:var(--sn-primitive-dimension-4, 4px);
12
+ align-items:center;
13
+ }
14
+
15
+ .divider[data-orientation=vertical]{
16
+ height:auto;
17
+ }
@@ -0,0 +1,8 @@
1
+ type CustomizeTableModalProps = {
2
+ open: boolean;
3
+ onClose(): void;
4
+ /** Вставить таблицу заданного размера. */
5
+ onSubmit(rows: number, cols: number): void;
6
+ };
7
+ export declare function CustomizeTableModal({ open, onClose, onSubmit }: CustomizeTableModalProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ButtonGroup } from '@cloud-ru/ds-button';
3
+ import { FieldStepper } from '@cloud-ru/ds-fields';
4
+ import { Modal } from '@cloud-ru/ds-modal';
5
+ import { usePortalContext } from '@cloud-ru/ds-portal-context';
6
+ import { useEffect, useState } from 'react';
7
+ import { TEST_IDS } from '../../constants.js';
8
+ import { markdownLocale } from '../../locale/index.js';
9
+ import styles from './styles.module.css';
10
+ const DEFAULT_SIZE = 1;
11
+ export function CustomizeTableModal({ open, onClose, onSubmit }) {
12
+ const { t } = markdownLocale.useTranslations();
13
+ const portalContext = usePortalContext();
14
+ const [columns, setColumns] = useState(DEFAULT_SIZE);
15
+ const [rows, setRows] = useState(DEFAULT_SIZE);
16
+ // Сбрасываем значения при каждом открытии, чтобы модалка не помнила прошлый ввод.
17
+ useEffect(() => {
18
+ if (open) {
19
+ setColumns(DEFAULT_SIZE);
20
+ setRows(DEFAULT_SIZE);
21
+ }
22
+ }, [open]);
23
+ const handleAdd = () => onSubmit(rows, columns);
24
+ return (
25
+ // TODO: заменить на адаптив
26
+ _jsx(Modal, { open: open, onClose: onClose, container: portalContext.current ?? undefined, title: t('table.customize'), "data-test-id": TEST_IDS.customizeModal, content: _jsxs("div", { className: styles.fields, children: [_jsx(FieldStepper, { size: 'm', className: styles.field, label: t('table.columns'), min: DEFAULT_SIZE, value: columns, onChange: setColumns, "data-test-id": TEST_IDS.customizeColumns }), _jsx(FieldStepper, { size: 'm', className: styles.field, label: t('table.rows'), min: DEFAULT_SIZE, value: rows, onChange: setRows, "data-test-id": TEST_IDS.customizeRows })] }), footer: _jsx(ButtonGroup, { size: 'm', primaryAction: { label: t('add'), onClick: handleAdd, 'data-test-id': TEST_IDS.customizeAdd }, secondaryAction: {
27
+ appearance: 'neutral',
28
+ view: 'outline',
29
+ label: t('cancel'),
30
+ onClick: onClose,
31
+ 'data-test-id': TEST_IDS.customizeCancel,
32
+ }, className: styles.buttons }) }));
33
+ }
@@ -0,0 +1,7 @@
1
+ type ImageModalProps = {
2
+ open: boolean;
3
+ onClose(): void;
4
+ onSubmit(url: string, alt: string): void;
5
+ };
6
+ export declare function ImageModal({ open, onClose, onSubmit }: ImageModalProps): import("react/jsx-runtime").JSX.Element;
7
+ export {};
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ButtonGroup } from '@cloud-ru/ds-button';
3
+ import { FieldCombo } from '@cloud-ru/ds-fields';
4
+ import { Modal } from '@cloud-ru/ds-modal';
5
+ import { usePortalContext } from '@cloud-ru/ds-portal-context';
6
+ import { useEffect, useState } from 'react';
7
+ import { TEST_IDS } from '../../constants.js';
8
+ import { markdownLocale } from '../../locale/index.js';
9
+ import styles from './styles.module.css';
10
+ export function ImageModal({ open, onClose, onSubmit }) {
11
+ const { t } = markdownLocale.useTranslations();
12
+ const portalContext = usePortalContext();
13
+ const [url, setUrl] = useState('');
14
+ const [alt, setAlt] = useState('');
15
+ // Сбрасываем поля при каждом открытии.
16
+ useEffect(() => {
17
+ if (open) {
18
+ setUrl('');
19
+ setAlt('');
20
+ }
21
+ }, [open]);
22
+ const trimmedUrl = url.trim();
23
+ const handleAdd = () => onSubmit(trimmedUrl, alt.trim() || 'image');
24
+ return (
25
+ // TODO: заменить на адаптив
26
+ _jsx(Modal, { open: open, onClose: onClose, container: portalContext.current ?? undefined, title: t('image.title'), "data-test-id": TEST_IDS.imageModal, content: _jsxs("div", { className: styles.fields, "data-column": true, children: [_jsx(FieldCombo, { size: 'm', label: t('image.url'), placeholder: t('image.placeholder'), value: url, onChange: setUrl, autoFocus: true, "data-test-id": TEST_IDS.imageModalUrl }), _jsx(FieldCombo, { size: 'm', label: t('image.alt'), value: alt, onChange: setAlt, "data-test-id": TEST_IDS.imageModalAlt })] }), footer: _jsx(ButtonGroup, { size: 'm', primaryAction: {
27
+ label: t('add'),
28
+ onClick: handleAdd,
29
+ disabled: trimmedUrl === '',
30
+ 'data-test-id': TEST_IDS.imageModalAdd,
31
+ }, secondaryAction: {
32
+ appearance: 'neutral',
33
+ view: 'outline',
34
+ label: t('cancel'),
35
+ onClick: onClose,
36
+ 'data-test-id': TEST_IDS.imageModalCancel,
37
+ }, className: styles.buttons }) }));
38
+ }
@@ -0,0 +1,13 @@
1
+ export type LinkProps = {
2
+ href?: string;
3
+ title?: string;
4
+ };
5
+ type LinkModalProps = {
6
+ open: boolean;
7
+ /** Текущий href (для редактирования существующей ссылки). */
8
+ initial?: LinkProps;
9
+ onClose(): void;
10
+ onSubmit(props: LinkProps): void;
11
+ };
12
+ export declare function LinkModal({ open, initial, onClose, onSubmit }: LinkModalProps): import("react/jsx-runtime").JSX.Element;
13
+ export {};