@gravity-ui/markdown-editor 13.4.1 → 13.5.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 (60) hide show
  1. package/build/cjs/bundle/Editor.d.ts +1 -2
  2. package/build/cjs/bundle/Editor.js +1 -0
  3. package/build/cjs/bundle/MarkdownEditorView.js +3 -1
  4. package/build/cjs/bundle/settings/MarkdownHints/MarkdownHints.js +1 -2
  5. package/build/cjs/extensions/markdown/Link/plugins/LinkTooltipPlugin/TooltipView.js +2 -2
  6. package/build/cjs/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/ImageForm/ImageForm.js +2 -2
  7. package/build/cjs/extensions/yfm/YfmTable/plugins/YfmTableControls/view.css +1 -0
  8. package/build/cjs/forms/FileForm.js +2 -2
  9. package/build/cjs/forms/ImageForm.js +2 -2
  10. package/build/cjs/forms/LinkForm.js +2 -2
  11. package/build/cjs/i18n/md-hints/en.json +2 -1
  12. package/build/cjs/i18n/md-hints/index.d.ts +2 -1
  13. package/build/cjs/i18n/md-hints/ru.json +2 -1
  14. package/build/cjs/i18n/search/en.json +5 -0
  15. package/build/cjs/i18n/search/index.d.ts +7 -0
  16. package/build/cjs/i18n/search/index.js +9 -0
  17. package/build/cjs/i18n/search/ru.json +5 -0
  18. package/build/cjs/markup/codemirror/create.d.ts +3 -0
  19. package/build/cjs/markup/codemirror/create.js +6 -1
  20. package/build/cjs/markup/codemirror/search-plugin/plugin.d.ts +42 -0
  21. package/build/cjs/markup/codemirror/search-plugin/plugin.js +100 -0
  22. package/build/cjs/markup/codemirror/search-plugin/view/SearchPopup.css +9 -0
  23. package/build/cjs/markup/codemirror/search-plugin/view/SearchPopup.d.ts +29 -0
  24. package/build/cjs/markup/codemirror/search-plugin/view/SearchPopup.js +85 -0
  25. package/build/cjs/utils/handlers.d.ts +4 -0
  26. package/build/cjs/utils/handlers.js +29 -0
  27. package/build/cjs/version.js +1 -1
  28. package/build/esm/bundle/Editor.d.ts +1 -2
  29. package/build/esm/bundle/Editor.js +1 -0
  30. package/build/esm/bundle/MarkdownEditorView.js +3 -1
  31. package/build/esm/bundle/settings/MarkdownHints/MarkdownHints.js +1 -2
  32. package/build/esm/extensions/markdown/Link/plugins/LinkTooltipPlugin/TooltipView.js +1 -1
  33. package/build/esm/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/ImageForm/ImageForm.js +1 -1
  34. package/build/esm/extensions/yfm/YfmTable/plugins/YfmTableControls/view.css +1 -0
  35. package/build/esm/forms/FileForm.js +1 -1
  36. package/build/esm/forms/ImageForm.js +1 -1
  37. package/build/esm/forms/LinkForm.js +1 -1
  38. package/build/esm/i18n/md-hints/en.json +2 -1
  39. package/build/esm/i18n/md-hints/index.d.ts +2 -1
  40. package/build/esm/i18n/md-hints/ru.json +2 -1
  41. package/build/esm/i18n/search/en.json +5 -0
  42. package/build/esm/i18n/search/index.d.ts +7 -0
  43. package/build/esm/i18n/search/index.js +5 -0
  44. package/build/esm/i18n/search/ru.json +5 -0
  45. package/build/esm/markup/codemirror/create.d.ts +3 -0
  46. package/build/esm/markup/codemirror/create.js +6 -1
  47. package/build/esm/markup/codemirror/search-plugin/plugin.d.ts +42 -0
  48. package/build/esm/markup/codemirror/search-plugin/plugin.js +96 -0
  49. package/build/esm/markup/codemirror/search-plugin/view/SearchPopup.css +9 -0
  50. package/build/esm/markup/codemirror/search-plugin/view/SearchPopup.d.ts +30 -0
  51. package/build/esm/markup/codemirror/search-plugin/view/SearchPopup.js +80 -0
  52. package/build/esm/utils/handlers.d.ts +4 -0
  53. package/build/esm/utils/handlers.js +23 -0
  54. package/build/esm/version.js +1 -1
  55. package/build/styles.css +10 -0
  56. package/package.json +4 -3
  57. package/build/cjs/forms/utils.d.ts +0 -2
  58. package/build/cjs/forms/utils.js +0 -11
  59. package/build/esm/forms/utils.d.ts +0 -2
  60. package/build/esm/forms/utils.js +0 -7
@@ -4,7 +4,6 @@ import { i18n } from '.././../../i18n/md-hints';
4
4
  import { cn } from '../../../classname';
5
5
  import './MarkdownHints.css';
6
6
  const b = cn('markdown-hints');
7
- const YFM_DOCS_HREF = 'https://ydocs.tech';
8
7
  export const MarkdownHints = React.memo(function MarkdownHints() {
9
8
  const hints = [
10
9
  { title: i18n('header_title'), hint: i18n('header_hint') },
@@ -22,6 +21,6 @@ export const MarkdownHints = React.memo(function MarkdownHints() {
22
21
  React.createElement("div", { className: b('grid') }, hints.map((hint, index) => (React.createElement(React.Fragment, { key: `md-hint-${index}` },
23
22
  React.createElement("span", { className: b('title') }, hint.title),
24
23
  React.createElement("span", { className: b('hint') }, hint.hint))))),
25
- React.createElement(Link, { href: YFM_DOCS_HREF, target: "_blank", className: b('docs-link') }, i18n('documentation'))));
24
+ React.createElement(Link, { href: i18n('documentation_link'), target: "_blank", className: b('docs-link') }, i18n('documentation'))));
26
25
  });
27
26
  MarkdownHints.displayName = 'MarkdownHints';
@@ -2,8 +2,8 @@ import React from 'react';
2
2
  import { TextInputFixed } from '../../../../../forms/TextInput';
3
3
  import { UrlInputRow } from '../../../../../forms/UrlInputRow';
4
4
  import Form from '../../../../../forms/base';
5
- import { enterKeyHandler } from '../../../../../forms/utils';
6
5
  import { i18n } from '../../../../../i18n/forms';
6
+ import { enterKeyHandler } from '../../../../../utils/handlers';
7
7
  export const LinkForm = React.memo(function LinkForm({ href, autoFocus, onChange, onCancel, }) {
8
8
  const [url, setUrl] = React.useState(href);
9
9
  const handleSubmit = () => {
@@ -4,9 +4,9 @@ import isNumber from 'is-number';
4
4
  import { cn } from '../../../../../../classname';
5
5
  import Form from '../../../../../../forms/base';
6
6
  import { NumberInput } from '../../../../../../forms/components';
7
- import { enterKeyHandler } from '../../../../../../forms/utils';
8
7
  import { i18n } from '../../../../../../i18n/forms';
9
8
  import { useAutoFocus } from '../../../../../../react-utils/useAutoFocus';
9
+ import { enterKeyHandler } from '../../../../../../utils/handlers';
10
10
  import { LinkAttr, linkType } from '../../../../../markdown';
11
11
  import { ImgSizeAttr } from '../../../../../specs';
12
12
  import './ImageForm.css';
@@ -78,5 +78,6 @@
78
78
 
79
79
  .g-md-table-wrapper {
80
80
  display: inline-block;
81
+ width: 100%;
81
82
  margin-right: 2px;
82
83
  }
@@ -3,10 +3,10 @@ import { Tabs, TextInput } from '@gravity-ui/uikit';
3
3
  import { cn } from '../classname';
4
4
  import { i18n } from '../i18n/forms';
5
5
  import { isFunction } from '../lodash';
6
+ import { enterKeyHandler } from '../utils/handlers';
6
7
  import { TextInputFixed } from './TextInput';
7
8
  import Form from './base';
8
9
  import { ButtonAttach } from './components';
9
- import { enterKeyHandler } from './utils';
10
10
  const b = cn('file-form');
11
11
  export const FileForm = ({ className, autoFocus, onCancel, onSubmit, onAttach, loading, }) => {
12
12
  const [tabId, setTabId] = React.useState(() => isFunction(onAttach) ? "attach" /* TabId.Attach */ : "link" /* TabId.Link */);
@@ -3,10 +3,10 @@ import { Tabs, TextInput } from '@gravity-ui/uikit';
3
3
  import { cn } from '../classname';
4
4
  import { i18n } from '../i18n/forms';
5
5
  import { isFunction } from '../lodash';
6
+ import { enterKeyHandler } from '../utils/handlers';
6
7
  import { TextInputFixed } from './TextInput';
7
8
  import Form from './base';
8
9
  import { ButtonAttach, NumberInput } from './components';
9
- import { enterKeyHandler } from './utils';
10
10
  import './ImageForm.css';
11
11
  const b = cn('image-form');
12
12
  export const ImageForm = ({ className, autoFocus, onCancel, onSubmit, onAttach, loading, }) => {
@@ -1,10 +1,10 @@
1
1
  import React from 'react';
2
2
  import { TextInput } from '@gravity-ui/uikit';
3
3
  import { i18n } from '../i18n/forms';
4
+ import { enterKeyHandler } from '../utils/handlers';
4
5
  import { TextInputFixed } from './TextInput';
5
6
  import { UrlInputRow } from './UrlInputRow';
6
7
  import Form from './base';
7
- import { enterKeyHandler } from './utils';
8
8
  export const LinkForm = React.memo(function LinkForm({ className, autoFocus, initialUrl, initialText, readOnlyText, onSubmit, onCancel, }) {
9
9
  const [url, setUrl] = React.useState(initialUrl !== null && initialUrl !== void 0 ? initialUrl : '');
10
10
  const [text, setText] = React.useState(initialText !== null && initialText !== void 0 ? initialText : '');
@@ -19,5 +19,6 @@
19
19
  "list_hint": "- Your text",
20
20
  "numbered-list_title": "Numbered list",
21
21
  "numbered-list_hint": "1. Your text",
22
- "documentation": "Documentation"
22
+ "documentation": "Documentation",
23
+ "documentation_link": " https://diplodoc.com/docs/en/syntax/"
23
24
  }
@@ -1,4 +1,4 @@
1
- export declare const i18n: <G extends "header_title" | "header_hint" | "italic_title" | "italic_hint" | "bold_title" | "bold_hint" | "strikethrough_title" | "strikethrough_hint" | "blockquote_title" | "blockquote_hint" | "code_title" | "code_hint" | "link_title" | "link_hint" | "image_title" | "image_hint" | "list_title" | "list_hint" | "numbered-list_title" | "numbered-list_hint" | "documentation", S extends string>(key: G | (string extends S ? S : never), params?: {
1
+ export declare const i18n: <G extends "header_title" | "header_hint" | "italic_title" | "italic_hint" | "bold_title" | "bold_hint" | "strikethrough_title" | "strikethrough_hint" | "blockquote_title" | "blockquote_hint" | "code_title" | "code_hint" | "link_title" | "link_hint" | "image_title" | "image_hint" | "list_title" | "list_hint" | "numbered-list_title" | "numbered-list_hint" | "documentation" | "documentation_link", S extends string>(key: G | (string extends S ? S : never), params?: {
2
2
  [key: string]: any;
3
3
  } | undefined) => S extends G ? {
4
4
  header_title: string;
@@ -22,4 +22,5 @@ export declare const i18n: <G extends "header_title" | "header_hint" | "italic_t
22
22
  "numbered-list_title": string;
23
23
  "numbered-list_hint": string;
24
24
  documentation: string;
25
+ documentation_link: string;
25
26
  }[G] : string;
@@ -19,5 +19,6 @@
19
19
  "list_hint": "- Ваш текст",
20
20
  "numbered-list_title": "Нумерованный список",
21
21
  "numbered-list_hint": "1. Ваш текст",
22
- "documentation": "Документация"
22
+ "documentation": "Документация",
23
+ "documentation_link": " https://diplodoc.com/docs/ru/syntax/"
23
24
  }
@@ -0,0 +1,5 @@
1
+ {
2
+ "label_case-sensitive": "Case sensitive",
3
+ "label_whole-word": "Whole word",
4
+ "title": "Search in code"
5
+ }
@@ -0,0 +1,7 @@
1
+ export declare const i18n: <G extends "title" | "label_case-sensitive" | "label_whole-word", S extends string>(key: G | (string extends S ? S : never), params?: {
2
+ [key: string]: any;
3
+ } | undefined) => S extends G ? {
4
+ "label_case-sensitive": string;
5
+ "label_whole-word": string;
6
+ title: string;
7
+ }[G] : string;
@@ -0,0 +1,5 @@
1
+ import { registerKeyset } from '../i18n';
2
+ import en from './en.json';
3
+ import ru from './ru.json';
4
+ const KEYSET = 'search';
5
+ export const i18n = registerKeyset(KEYSET, { en, ru });
@@ -0,0 +1,5 @@
1
+ {
2
+ "label_case-sensitive": "С учетом регистра",
3
+ "label_whole-word": "Слово целиком",
4
+ "title": "Найти в коде"
5
+ }
@@ -1,6 +1,8 @@
1
1
  import type { Extension, StateCommand } from '@codemirror/state';
2
2
  import { EditorView, EditorViewConfig } from '@codemirror/view';
3
+ import { EventMap } from '../../bundle/Editor';
3
4
  import { ReactRenderStorage } from '../../extensions';
5
+ import { Receiver } from '../../utils';
4
6
  import { FileUploadHandler } from './files-upload-facet';
5
7
  export declare type CreateCodemirrorParams = {
6
8
  doc: EditorViewConfig['doc'];
@@ -14,6 +16,7 @@ export declare type CreateCodemirrorParams = {
14
16
  uploadHandler?: FileUploadHandler;
15
17
  needImgDimms?: boolean;
16
18
  extraMarkupExtensions?: Extension[];
19
+ receiver?: Receiver<EventMap>;
17
20
  };
18
21
  export declare function createCodemirror(params: CreateCodemirrorParams): EditorView;
19
22
  export declare function withLogger(action: string, command: StateCommand): StateCommand;
@@ -10,9 +10,10 @@ import { FileUploadHandlerFacet } from './files-upload-facet';
10
10
  import { gravityHighlightStyle, gravityTheme } from './gravity';
11
11
  import { PairingCharactersExtension } from './pairing-chars';
12
12
  import { ReactRendererFacet } from './react-facet';
13
+ import { SearchPanelPlugin } from './search-plugin/plugin';
13
14
  import { yfmLang } from './yfm';
14
15
  export function createCodemirror(params) {
15
- const { doc, placeholderText, reactRenderer, onCancel, onScroll, onSubmit, onChange, onDocChange, extraMarkupExtensions, } = params;
16
+ const { doc, placeholderText, reactRenderer, onCancel, onScroll, onSubmit, onChange, onDocChange, extraMarkupExtensions, receiver, } = params;
16
17
  const extensions = [
17
18
  gravityTheme,
18
19
  placeholder(placeholderText),
@@ -66,6 +67,10 @@ export function createCodemirror(params) {
66
67
  onScroll(event);
67
68
  },
68
69
  }),
70
+ SearchPanelPlugin({
71
+ anchorSelector: '.g-md-search-anchor',
72
+ receiver,
73
+ }),
69
74
  ];
70
75
  if (params.uploadHandler) {
71
76
  extensions.push(FileUploadHandlerFacet.of({
@@ -0,0 +1,42 @@
1
+ import { EditorView, ViewPlugin, ViewUpdate } from '@codemirror/view';
2
+ import { EditorMode, EventMap } from '../../../bundle/Editor';
3
+ import type { RendererItem } from '../../../extensions';
4
+ import { Receiver } from '../../../utils';
5
+ interface SearchQueryParams {
6
+ search: string;
7
+ caseSensitive?: boolean;
8
+ literal?: boolean;
9
+ regexp?: boolean;
10
+ replace?: string;
11
+ valid?: boolean;
12
+ wholeWord?: boolean;
13
+ }
14
+ export interface SearchPanelPluginParams {
15
+ anchorSelector: string;
16
+ inputDelay?: number;
17
+ receiver?: Receiver<EventMap>;
18
+ }
19
+ export declare const SearchPanelPlugin: (params: SearchPanelPluginParams) => ViewPlugin<{
20
+ readonly view: EditorView;
21
+ readonly params: SearchPanelPluginParams;
22
+ anchor: HTMLElement | null;
23
+ renderer: RendererItem | null;
24
+ searchQuery: SearchQueryParams;
25
+ receiver: Receiver<EventMap> | undefined;
26
+ setViewSearchWithDelay: (config: Partial<SearchQueryParams>) => void;
27
+ update(update: ViewUpdate): void;
28
+ destroy(): void;
29
+ setViewSearch(config: Partial<SearchQueryParams>): void;
30
+ handleEditorModeChange({ mode }: {
31
+ mode: EditorMode;
32
+ }): void;
33
+ handleChange(search: string): void;
34
+ handleClose(): void;
35
+ handleSearchNext(): void;
36
+ handleSearchPrev(): void;
37
+ handleSearchConfigChange({ isCaseSensitive, isWholeWord, }: {
38
+ isCaseSensitive?: boolean | undefined;
39
+ isWholeWord?: boolean | undefined;
40
+ }): void;
41
+ }>;
42
+ export {};
@@ -0,0 +1,96 @@
1
+ import { SearchQuery, closeSearchPanel, findNext, findPrevious, search, searchKeymap, searchPanelOpen, setSearchQuery, } from '@codemirror/search';
2
+ import { ViewPlugin, keymap } from '@codemirror/view';
3
+ import { debounce } from '../../../lodash';
4
+ import { ReactRendererFacet } from '../react-facet';
5
+ import { renderSearchPopup } from './view/SearchPopup';
6
+ const INPUT_DELAY = 200;
7
+ export const SearchPanelPlugin = (params) => ViewPlugin.fromClass(class {
8
+ constructor(view) {
9
+ var _a, _b;
10
+ this.searchQuery = {
11
+ search: '',
12
+ caseSensitive: false,
13
+ wholeWord: false,
14
+ };
15
+ this.view = view;
16
+ this.anchor = null;
17
+ this.renderer = null;
18
+ this.params = params;
19
+ this.receiver = params.receiver;
20
+ this.handleClose = this.handleClose.bind(this);
21
+ this.handleChange = this.handleChange.bind(this);
22
+ this.handleSearchNext = this.handleSearchNext.bind(this);
23
+ this.handleSearchPrev = this.handleSearchPrev.bind(this);
24
+ this.handleSearchConfigChange = this.handleSearchConfigChange.bind(this);
25
+ this.handleEditorModeChange = this.handleEditorModeChange.bind(this);
26
+ this.setViewSearchWithDelay = debounce(this.setViewSearch, (_a = this.params.inputDelay) !== null && _a !== void 0 ? _a : INPUT_DELAY);
27
+ (_b = this.receiver) === null || _b === void 0 ? void 0 : _b.on('change-editor-mode', this.handleEditorModeChange);
28
+ }
29
+ update(update) {
30
+ var _a;
31
+ const isPanelOpen = searchPanelOpen(update.state);
32
+ if (isPanelOpen && !this.renderer) {
33
+ this.anchor = document.querySelector(this.params.anchorSelector);
34
+ this.renderer = this.view.state
35
+ .facet(ReactRendererFacet)
36
+ .createItem('cm-search', () => renderSearchPopup({
37
+ open: true,
38
+ anchor: this.anchor,
39
+ onChange: this.handleChange,
40
+ onClose: this.handleClose,
41
+ onSearchNext: this.handleSearchNext,
42
+ onSearchPrev: this.handleSearchPrev,
43
+ onConfigChange: this.handleSearchConfigChange,
44
+ }));
45
+ }
46
+ else if (!isPanelOpen && this.renderer) {
47
+ (_a = this.renderer) === null || _a === void 0 ? void 0 : _a.remove();
48
+ this.renderer = null;
49
+ }
50
+ }
51
+ destroy() {
52
+ var _a, _b;
53
+ (_a = this.renderer) === null || _a === void 0 ? void 0 : _a.remove();
54
+ this.renderer = null;
55
+ (_b = this.receiver) === null || _b === void 0 ? void 0 : _b.off('change-editor-mode', this.handleEditorModeChange);
56
+ }
57
+ setViewSearch(config) {
58
+ this.searchQuery = Object.assign(Object.assign({}, this.searchQuery), config);
59
+ const searchQuery = new SearchQuery(Object.assign({}, this.searchQuery));
60
+ this.view.dispatch({ effects: setSearchQuery.of(searchQuery) });
61
+ }
62
+ handleEditorModeChange({ mode }) {
63
+ if (mode === 'wysiwyg') {
64
+ closeSearchPanel(this.view);
65
+ }
66
+ }
67
+ handleChange(search) {
68
+ this.setViewSearchWithDelay({ search });
69
+ }
70
+ handleClose() {
71
+ this.setViewSearch({ search: '' });
72
+ closeSearchPanel(this.view);
73
+ }
74
+ handleSearchNext() {
75
+ findNext(this.view);
76
+ }
77
+ handleSearchPrev() {
78
+ findPrevious(this.view);
79
+ }
80
+ handleSearchConfigChange({ isCaseSensitive, isWholeWord, }) {
81
+ this.setViewSearch({
82
+ caseSensitive: isCaseSensitive,
83
+ wholeWord: isWholeWord,
84
+ });
85
+ }
86
+ }, {
87
+ provide: () => [
88
+ keymap.of(searchKeymap),
89
+ search({
90
+ createPanel: () => ({
91
+ // Create an empty search panel
92
+ dom: document.createElement('div'),
93
+ }),
94
+ }),
95
+ ],
96
+ });
@@ -0,0 +1,9 @@
1
+ .g-md-search-card {
2
+ padding: var(--g-spacing-2) var(--g-spacing-2) var(--g-spacing-3) var(--g-spacing-4);
3
+ }
4
+ .g-md-search-card__header {
5
+ display: flex;
6
+ justify-content: space-between;
7
+ align-items: center;
8
+ margin-bottom: var(--g-spacing-1);
9
+ }
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import './SearchPopup.css';
3
+ interface SearchConfig {
4
+ isCaseSensitive: boolean;
5
+ isWholeWord: boolean;
6
+ }
7
+ interface SearchCardProps {
8
+ onSearchKeyDown?: (query: string) => void;
9
+ onChange?: (query: string) => void;
10
+ onClose?: (query: string) => void;
11
+ onSearchPrev?: (query: string) => void;
12
+ onSearchNext?: (query: string) => void;
13
+ onConfigChange?: (config: SearchConfig) => void;
14
+ }
15
+ export declare const SearchCard: React.FC<SearchCardProps>;
16
+ export interface SearchPopupProps {
17
+ anchor: HTMLElement;
18
+ onChange: (query: string) => void;
19
+ onClose: () => void;
20
+ onSearchNext: () => void;
21
+ onSearchPrev: () => void;
22
+ onConfigChange: (config: SearchConfig) => void;
23
+ open: boolean;
24
+ }
25
+ export declare const SearchPopup: React.FC<SearchPopupProps>;
26
+ interface SearchPopupWithRefProps extends Omit<SearchPopupProps, 'anchor'> {
27
+ anchor: HTMLElement | null;
28
+ }
29
+ export declare function renderSearchPopup({ anchor, open, onClose, ...props }: SearchPopupWithRefProps): JSX.Element;
30
+ export {};
@@ -0,0 +1,80 @@
1
+ import { __rest } from "tslib";
2
+ import React, { useRef, useState } from 'react';
3
+ import { ChevronDown, ChevronUp, Xmark } from '@gravity-ui/icons';
4
+ import { Button, Card, Checkbox, Icon, Popup, TextInput, sp, } from '@gravity-ui/uikit';
5
+ import { cn } from '../../../../classname';
6
+ import { i18n } from '../../../../i18n/search';
7
+ import { enterKeyHandler } from '../../../../utils/handlers';
8
+ import './SearchPopup.css';
9
+ const b = cn('search-card');
10
+ const noop = () => { };
11
+ export const SearchCard = ({ onChange = noop, onClose = noop, onSearchPrev = noop, onSearchNext = noop, onConfigChange = noop, }) => {
12
+ const [query, setQuery] = useState('');
13
+ const [isCaseSensitive, setIsCaseSensitive] = useState(false);
14
+ const [isWholeWord, setIsWholeWord] = useState(false);
15
+ const textInputRef = useRef(null);
16
+ const setInputFocus = () => {
17
+ var _a;
18
+ (_a = textInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
19
+ };
20
+ const handleInputChange = (event) => {
21
+ const { target: { value }, } = event;
22
+ setQuery(value);
23
+ onChange(value);
24
+ };
25
+ const handleClose = () => {
26
+ setQuery('');
27
+ onClose(query);
28
+ setInputFocus();
29
+ };
30
+ const handlePrev = () => {
31
+ onSearchPrev(query);
32
+ setInputFocus();
33
+ };
34
+ const handleNext = () => {
35
+ onSearchNext(query);
36
+ setInputFocus();
37
+ };
38
+ const handleIsCaseSensitive = () => {
39
+ onConfigChange({
40
+ isCaseSensitive: !isCaseSensitive,
41
+ isWholeWord,
42
+ });
43
+ setIsCaseSensitive((isCaseSensitive) => !isCaseSensitive);
44
+ setInputFocus();
45
+ };
46
+ const handleIsWholeWord = () => {
47
+ onConfigChange({
48
+ isCaseSensitive,
49
+ isWholeWord: !isWholeWord,
50
+ });
51
+ setIsWholeWord((isWholeWord) => !isWholeWord);
52
+ setInputFocus();
53
+ };
54
+ const handleSearchKeyPress = enterKeyHandler(handleNext);
55
+ return (React.createElement(Card, { className: b() },
56
+ React.createElement("div", { className: b('header') },
57
+ React.createElement("span", { className: b('title') },
58
+ " ",
59
+ i18n('title')),
60
+ React.createElement(Button, { onClick: handleClose, size: "s", view: "flat" },
61
+ React.createElement(Icon, { data: Xmark, size: 14 }))),
62
+ React.createElement(TextInput, { controlRef: textInputRef, className: sp({ mb: 2 }), size: "s", autoFocus: true, onKeyPress: handleSearchKeyPress, onChange: handleInputChange, value: query, endContent: React.createElement(React.Fragment, null,
63
+ React.createElement(Button, { onClick: handlePrev },
64
+ React.createElement(Icon, { data: ChevronUp, size: 12 })),
65
+ React.createElement(Button, { onClick: handleNext },
66
+ React.createElement(Icon, { data: ChevronDown, size: 12 }))) }),
67
+ React.createElement(Checkbox, { size: "m", onUpdate: handleIsCaseSensitive, checked: isCaseSensitive, className: sp({ mr: 4 }) }, i18n('label_case-sensitive')),
68
+ React.createElement(Checkbox, { size: "m", onUpdate: handleIsWholeWord, checked: isWholeWord }, i18n('label_whole-word'))));
69
+ };
70
+ export const SearchPopup = (_a) => {
71
+ var { open, anchor, onClose } = _a, props = __rest(_a, ["open", "anchor", "onClose"]);
72
+ const anchorRef = useRef(anchor);
73
+ return (React.createElement(Popup, { onEscapeKeyDown: onClose, open: anchorRef.current && open, anchorRef: anchorRef, placement: "bottom-end" },
74
+ React.createElement(SearchCard, Object.assign({ onClose: onClose }, props))));
75
+ };
76
+ SearchPopup.displayName = 'SearchPopup';
77
+ export function renderSearchPopup(_a) {
78
+ var { anchor, open, onClose } = _a, props = __rest(_a, ["anchor", "open", "onClose"]);
79
+ return (React.createElement(React.Fragment, null, anchor && React.createElement(SearchPopup, Object.assign({ open: open, onClose: onClose, anchor: anchor }, props))));
80
+ }
@@ -0,0 +1,4 @@
1
+ /// <reference types="react" />
2
+ export declare function enterKeyHandler<T>(handler: React.KeyboardEventHandler<T>): React.KeyboardEventHandler<T>;
3
+ export declare function escapeKeyHandler<T>(handler: React.KeyboardEventHandler<T>): React.KeyboardEventHandler<T>;
4
+ export declare function combinedKeyHandler<T>(handlers: Record<string, React.KeyboardEventHandler<T>>): React.KeyboardEventHandler<T>;
@@ -0,0 +1,23 @@
1
+ import { Key } from '../shortcuts';
2
+ export function enterKeyHandler(handler) {
3
+ return (event) => {
4
+ if (event.key === Key.Enter) {
5
+ handler(event);
6
+ }
7
+ };
8
+ }
9
+ export function escapeKeyHandler(handler) {
10
+ return (event) => {
11
+ if (event.key === Key.Esc) {
12
+ handler(event);
13
+ }
14
+ };
15
+ }
16
+ export function combinedKeyHandler(handlers) {
17
+ return (event) => {
18
+ const handler = handlers[event.key];
19
+ if (handler) {
20
+ handler(event);
21
+ }
22
+ };
23
+ }
@@ -1,2 +1,2 @@
1
1
  /** During build process, the current version will be injected here */
2
- export const VERSION = typeof '13.4.1' !== 'undefined' ? '13.4.1' : 'unknown';
2
+ export const VERSION = typeof '13.5.0' !== 'undefined' ? '13.5.0' : 'unknown';
package/build/styles.css CHANGED
@@ -1290,6 +1290,15 @@ img.ProseMirror-separator {
1290
1290
  .g-root_theme_dark .g-md-yfm-html-block_editing .g-text-area__content {
1291
1291
  color: var(--g-color-text-primary);
1292
1292
  }
1293
+ .g-md-search-card {
1294
+ padding: var(--g-spacing-2) var(--g-spacing-2) var(--g-spacing-3) var(--g-spacing-4);
1295
+ }
1296
+ .g-md-search-card__header {
1297
+ display: flex;
1298
+ justify-content: space-between;
1299
+ align-items: center;
1300
+ margin-bottom: var(--g-spacing-1);
1301
+ }
1293
1302
  .g-md-code-block-toolbar {
1294
1303
  margin: 2px 8px;
1295
1304
  }
@@ -1477,6 +1486,7 @@ img.ProseMirror-separator {
1477
1486
 
1478
1487
  .g-md-table-wrapper {
1479
1488
  display: inline-block;
1489
+ width: 100%;
1480
1490
  margin-right: 2px;
1481
1491
  }
1482
1492
  .g-md-table-cell-view__left-button {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/markdown-editor",
3
- "version": "13.4.1",
3
+ "version": "13.5.0",
4
4
  "description": "Markdown wysiwyg and markup editor",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -163,6 +163,7 @@
163
163
  "@codemirror/commands": "6.5.0",
164
164
  "@codemirror/lang-markdown": "6.2.5",
165
165
  "@codemirror/language": "6.10.1",
166
+ "@codemirror/search": "6.5.6",
166
167
  "@codemirror/state": "6.4.1",
167
168
  "@codemirror/view": "6.26.3",
168
169
  "@gravity-ui/i18n": "^1.1.0",
@@ -198,9 +199,9 @@
198
199
  "tslib": "^2.3.1"
199
200
  },
200
201
  "devDependencies": {
202
+ "@diplodoc/html-extension": "1.2.7",
201
203
  "@diplodoc/latex-extension": "1.0.3",
202
204
  "@diplodoc/mermaid-extension": "1.2.1",
203
- "@diplodoc/html-extension": "1.2.7",
204
205
  "@diplodoc/transform": "4.5.0",
205
206
  "@gravity-ui/components": "3.0.0",
206
207
  "@gravity-ui/eslint-config": "3.1.1",
@@ -268,9 +269,9 @@
268
269
  }
269
270
  },
270
271
  "peerDependencies": {
272
+ "@diplodoc/html-extension": "^1.2.7",
271
273
  "@diplodoc/latex-extension": "^1.0.3",
272
274
  "@diplodoc/mermaid-extension": "^1.0.0",
273
- "@diplodoc/html-extension": "^1.2.7",
274
275
  "@diplodoc/transform": "^4.5.0",
275
276
  "@gravity-ui/components": "^3.0.0",
276
277
  "@gravity-ui/uikit": "^6.11.0",
@@ -1,2 +0,0 @@
1
- /// <reference types="react" />
2
- export declare function enterKeyHandler<T>(handler: React.KeyboardEventHandler<T>): React.KeyboardEventHandler<T>;
@@ -1,11 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.enterKeyHandler = void 0;
4
- function enterKeyHandler(handler) {
5
- return (event) => {
6
- if (event.key === 'Enter') {
7
- handler(event);
8
- }
9
- };
10
- }
11
- exports.enterKeyHandler = enterKeyHandler;
@@ -1,2 +0,0 @@
1
- /// <reference types="react" />
2
- export declare function enterKeyHandler<T>(handler: React.KeyboardEventHandler<T>): React.KeyboardEventHandler<T>;
@@ -1,7 +0,0 @@
1
- export function enterKeyHandler(handler) {
2
- return (event) => {
3
- if (event.key === 'Enter') {
4
- handler(event);
5
- }
6
- };
7
- }