@snack-uikit/code-editor 0.6.4-preview-845a4e8b.0 → 0.6.5-preview-580e8472.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,16 @@
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.6.4 (2025-05-16)
7
+
8
+ ### Only dependencies have been changed
9
+ * [@snack-uikit/loaders@0.9.5](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/loaders/CHANGELOG.md)
10
+ * [@snack-uikit/utils@3.8.2](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/utils/CHANGELOG.md)
11
+
12
+
13
+
14
+
15
+
6
16
  ## 0.6.3 (2025-05-12)
7
17
 
8
18
  ### Only dependencies have been changed
package/README.md CHANGED
@@ -15,6 +15,7 @@
15
15
  | jsonSchema* | `JsonSchema` | - | Схема для валидации |
16
16
  | themeName | `string` | - | Название текущей темы. Значение не важно, важно что смена значения запускает пересчет стилей. |
17
17
  | hasBackground | `boolean` | true | Включение/отключение псевдобекграунда |
18
+ | onCopyClick | `MouseEventHandler<HTMLElement>` | - | Клик по кнопке копирования |
18
19
  | defaultValue | `string` | - | Default value of the current model |
19
20
  | defaultLanguage | `string` | - | Default language of the current model |
20
21
  | defaultPath | `string` | - | Default path of the current model Will be passed as the third argument to `.createModel` method `monaco.editor.createModel(..., ..., monaco.Uri.parse(defaultPath))` |
@@ -1,3 +1,4 @@
1
+ import { ButtonFunctionProps } from '@snack-uikit/button';
1
2
  import { WithSupportProps } from '@snack-uikit/utils';
2
3
  import { EditorBaseProps, EditorWithJsonSchemaProps } from './types';
3
4
  export type CodeEditorProps = WithSupportProps<{
@@ -9,5 +10,9 @@ export type CodeEditorProps = WithSupportProps<{
9
10
  * Включение/отключение псевдобекграунда
10
11
  */
11
12
  hasBackground?: boolean;
13
+ /**
14
+ * Клик по кнопке копирования
15
+ */
16
+ onCopyClick?: ButtonFunctionProps['onClick'];
12
17
  }> & (EditorBaseProps | EditorWithJsonSchemaProps);
13
- export declare function CodeEditor({ themeName, className, theme, options, loading, hasBackground, onMount, language, ...props }: CodeEditorProps): import("react/jsx-runtime").JSX.Element;
18
+ export declare function CodeEditor({ themeName, className, theme, options, loading, hasBackground, onMount, language, onCopyClick, ...props }: CodeEditorProps): import("react/jsx-runtime").JSX.Element;
@@ -22,8 +22,11 @@ const react_1 = require("@monaco-editor/react");
22
22
  const classnames_1 = __importDefault(require("classnames"));
23
23
  const react_2 = require("react");
24
24
  const loaders_1 = require("@snack-uikit/loaders");
25
+ const locale_1 = require("@snack-uikit/locale");
26
+ const typography_1 = require("@snack-uikit/typography");
25
27
  const utils_1 = require("@snack-uikit/utils");
26
28
  const constants_1 = require("./constants");
29
+ const CopyButton_1 = require("./CopyButton");
27
30
  const hooks_1 = require("./hooks");
28
31
  const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
29
32
  const utils_2 = require("./utils");
@@ -36,11 +39,15 @@ function CodeEditor(_a) {
36
39
  loading,
37
40
  hasBackground = true,
38
41
  onMount,
39
- language
42
+ language,
43
+ onCopyClick
40
44
  } = _a,
41
- props = __rest(_a, ["themeName", "className", "theme", "options", "loading", "hasBackground", "onMount", "language"]);
45
+ props = __rest(_a, ["themeName", "className", "theme", "options", "loading", "hasBackground", "onMount", "language", "onCopyClick"]);
42
46
  const monaco = (0, react_1.useMonaco)();
43
47
  const [wrapperElement, setWrapperElement] = (0, react_2.useState)(null);
48
+ const {
49
+ t
50
+ } = (0, locale_1.useLocale)();
44
51
  const {
45
52
  jsonSchemaProps,
46
53
  jsonSchemaOptions
@@ -111,11 +118,20 @@ function CodeEditor(_a) {
111
118
  fontWeight: themeValues.mono.body.s['font-weight'],
112
119
  fontFamily: themeValues.mono.body.s['font-family']
113
120
  } : constants_1.DEFAULT_THEME_OPTIONS.mono.s), options), [options, themeValues === null || themeValues === void 0 ? void 0 : themeValues.mono, jsonSchemaOptions]);
114
- return (0, jsx_runtime_1.jsx)("div", Object.assign({
121
+ return (0, jsx_runtime_1.jsxs)("div", Object.assign({
115
122
  className: className
116
123
  }, (0, utils_1.extractSupportProps)(props), {
117
124
  ref: setWrapperElement,
118
- children: (0, jsx_runtime_1.jsx)(react_1.Editor, Object.assign({}, jsonSchemaProps, props, {
125
+ children: [(0, jsx_runtime_1.jsxs)("div", {
126
+ className: styles_module_scss_1.default.headline,
127
+ children: [(0, jsx_runtime_1.jsx)(typography_1.Typography.SansLabelS, {
128
+ children: t('Markdown.codeEditorTitle')
129
+ }), (0, jsx_runtime_1.jsx)(CopyButton_1.CopyButton, {
130
+ valueToCopy: props.value || '',
131
+ size: 's',
132
+ onClick: onCopyClick
133
+ })]
134
+ }), (0, jsx_runtime_1.jsx)(react_1.Editor, Object.assign({}, jsonSchemaProps, props, {
119
135
  theme: theme !== null && theme !== void 0 ? theme : dark ? 'snackDark' : 'snack',
120
136
  className: (0, classnames_1.default)({
121
137
  [styles_module_scss_1.default.editor]: hasBackground
@@ -124,6 +140,6 @@ function CodeEditor(_a) {
124
140
  options: mergedOptions,
125
141
  onMount: onEditorMount,
126
142
  language: language
127
- }))
143
+ }))]
128
144
  }));
129
145
  }
@@ -0,0 +1,9 @@
1
+ import { ButtonFunctionProps } from '@snack-uikit/button';
2
+ import { WithSupportProps } from '@snack-uikit/utils';
3
+ export type CopyButtonProps = WithSupportProps<{
4
+ valueToCopy: string | number;
5
+ size?: ButtonFunctionProps['size'];
6
+ className?: string;
7
+ onClick?: ButtonFunctionProps['onClick'];
8
+ }>;
9
+ export declare function CopyButton({ valueToCopy, size, className, onClick, ...rest }: CopyButtonProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+
3
+ var __rest = void 0 && (void 0).__rest || function (s, e) {
4
+ var t = {};
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
8
+ }
9
+ return t;
10
+ };
11
+ var __importDefault = void 0 && (void 0).__importDefault || function (mod) {
12
+ return mod && mod.__esModule ? mod : {
13
+ "default": mod
14
+ };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", {
17
+ value: true
18
+ });
19
+ exports.CopyButton = CopyButton;
20
+ const jsx_runtime_1 = require("react/jsx-runtime");
21
+ const react_1 = require("react");
22
+ const frontend_tools_copy_to_clipboard_1 = require("@cloud-ru/frontend-tools-copy-to-clipboard");
23
+ const button_1 = require("@snack-uikit/button");
24
+ const icons_1 = require("@snack-uikit/icons");
25
+ const locale_1 = require("@snack-uikit/locale");
26
+ const tooltip_1 = require("@snack-uikit/tooltip");
27
+ const utils_1 = require("@snack-uikit/utils");
28
+ const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
29
+ function CopyButton(_a) {
30
+ var {
31
+ valueToCopy,
32
+ size = 's',
33
+ className,
34
+ onClick
35
+ } = _a,
36
+ rest = __rest(_a, ["valueToCopy", "size", "className", "onClick"]);
37
+ const {
38
+ t
39
+ } = (0, locale_1.useLocale)();
40
+ const [isChecked, setIsCheckedOpen] = (0, react_1.useState)(false);
41
+ const timerId = (0, react_1.useRef)();
42
+ const openChecked = () => setIsCheckedOpen(true);
43
+ const closeChecked = () => setIsCheckedOpen(false);
44
+ const handleClick = event => {
45
+ event.stopPropagation();
46
+ valueToCopy && (0, frontend_tools_copy_to_clipboard_1.copyToClipboard)(String(valueToCopy));
47
+ openChecked();
48
+ clearTimeout(timerId.current);
49
+ timerId.current = setTimeout(closeChecked, 1000);
50
+ onClick === null || onClick === void 0 ? void 0 : onClick(event);
51
+ };
52
+ (0, react_1.useEffect)(() => () => {
53
+ closeChecked();
54
+ clearTimeout(timerId.current);
55
+ }, []);
56
+ return (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, {
57
+ tip: t('Markdown.copyButtonToolTip'),
58
+ children: (0, jsx_runtime_1.jsx)(button_1.ButtonFunction, Object.assign({}, (0, utils_1.extractSupportProps)(rest), {
59
+ onClick: handleClick,
60
+ className: className,
61
+ "data-test-id": 'button-copy-value',
62
+ type: 'button',
63
+ icon: isChecked ? (0, jsx_runtime_1.jsx)(icons_1.CheckSVG, {
64
+ className: styles_module_scss_1.default.checkIcon
65
+ }) : (0, jsx_runtime_1.jsx)(icons_1.CopySVG, {}),
66
+ size: size
67
+ }))
68
+ });
69
+ }
@@ -0,0 +1 @@
1
+ export * from './CopyButton';
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ var __createBinding = void 0 && (void 0).__createBinding || (Object.create ? function (o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = {
8
+ enumerable: true,
9
+ get: function () {
10
+ return m[k];
11
+ }
12
+ };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ } : function (o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ });
19
+ var __exportStar = void 0 && (void 0).__exportStar || function (m, exports) {
20
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
21
+ };
22
+ Object.defineProperty(exports, "__esModule", {
23
+ value: true
24
+ });
25
+ __exportStar(require("./CopyButton"), exports);
@@ -0,0 +1,3 @@
1
+ .checkIcon{
2
+ fill:var(--sys-green-accent-default, #57b762);
3
+ }
@@ -10,4 +10,15 @@
10
10
  width:100%;
11
11
  opacity:var(--opacity-a004, 0.04);
12
12
  background-color:var(--sys-neutral-accent-default, #787b8a);
13
+ }
14
+
15
+ .headline{
16
+ min-height:var(--size-markdown-code-editor-headline-min-height, 36px);
17
+ padding-left:var(--space-markdown-code-editor-headline-horizontal-padding, 12px);
18
+ padding-right:var(--space-markdown-code-editor-headline-horizontal-padding, 12px);
19
+ display:flex;
20
+ align-items:center;
21
+ justify-content:space-between;
22
+ background-color:var(--sys-neutral-background, #eeeff3);
23
+ border-color:var(--sys-neutral-decor-default, #dde0ea);
13
24
  }
@@ -1,3 +1,4 @@
1
+ import { ButtonFunctionProps } from '@snack-uikit/button';
1
2
  import { WithSupportProps } from '@snack-uikit/utils';
2
3
  import { EditorBaseProps, EditorWithJsonSchemaProps } from './types';
3
4
  export type CodeEditorProps = WithSupportProps<{
@@ -9,5 +10,9 @@ export type CodeEditorProps = WithSupportProps<{
9
10
  * Включение/отключение псевдобекграунда
10
11
  */
11
12
  hasBackground?: boolean;
13
+ /**
14
+ * Клик по кнопке копирования
15
+ */
16
+ onCopyClick?: ButtonFunctionProps['onClick'];
12
17
  }> & (EditorBaseProps | EditorWithJsonSchemaProps);
13
- export declare function CodeEditor({ themeName, className, theme, options, loading, hasBackground, onMount, language, ...props }: CodeEditorProps): import("react/jsx-runtime").JSX.Element;
18
+ export declare function CodeEditor({ themeName, className, theme, options, loading, hasBackground, onMount, language, onCopyClick, ...props }: CodeEditorProps): import("react/jsx-runtime").JSX.Element;
@@ -9,20 +9,24 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- import { jsx as _jsx } from "react/jsx-runtime";
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  import { Editor, useMonaco } from '@monaco-editor/react';
14
14
  import cn from 'classnames';
15
15
  import { useCallback, useMemo, useState } from 'react';
16
16
  import { Spinner } from '@snack-uikit/loaders';
17
+ import { useLocale } from '@snack-uikit/locale';
18
+ import { Typography } from '@snack-uikit/typography';
17
19
  import { extractSupportProps, isBrowser } from '@snack-uikit/utils';
18
20
  import { CODE_EDITOR_OPTIONS, DEFAULT_THEME_OPTIONS, DEFAULT_THEME_VALUES } from './constants';
21
+ import { CopyButton } from './CopyButton';
19
22
  import { useApplyJsonSchema, useCalculatedThemeValues } from './hooks';
20
23
  import styles from './styles.module.css';
21
24
  import { isDark } from './utils';
22
25
  export function CodeEditor(_a) {
23
- var { themeName, className, theme, options, loading, hasBackground = true, onMount, language } = _a, props = __rest(_a, ["themeName", "className", "theme", "options", "loading", "hasBackground", "onMount", "language"]);
26
+ var { themeName, className, theme, options, loading, hasBackground = true, onMount, language, onCopyClick } = _a, props = __rest(_a, ["themeName", "className", "theme", "options", "loading", "hasBackground", "onMount", "language", "onCopyClick"]);
24
27
  const monaco = useMonaco();
25
28
  const [wrapperElement, setWrapperElement] = useState(null);
29
+ const { t } = useLocale();
26
30
  const { jsonSchemaProps, jsonSchemaOptions } = useApplyJsonSchema(language, 'jsonSchema' in props ? props.jsonSchema : undefined);
27
31
  const onEditorMount = useCallback((editor, monaco) => {
28
32
  var _a;
@@ -87,5 +91,5 @@ export function CodeEditor(_a) {
87
91
  fontFamily: themeValues.mono.body.s['font-family'],
88
92
  }
89
93
  : DEFAULT_THEME_OPTIONS.mono.s)), options)), [options, themeValues === null || themeValues === void 0 ? void 0 : themeValues.mono, jsonSchemaOptions]);
90
- return (_jsx("div", Object.assign({ className: className }, extractSupportProps(props), { ref: setWrapperElement, children: _jsx(Editor, Object.assign({}, jsonSchemaProps, props, { theme: theme !== null && theme !== void 0 ? theme : (dark ? 'snackDark' : 'snack'), className: cn({ [styles.editor]: hasBackground }), loading: loading !== null && loading !== void 0 ? loading : _jsx(Spinner, {}), options: mergedOptions, onMount: onEditorMount, language: language })) })));
94
+ return (_jsxs("div", Object.assign({ className: className }, extractSupportProps(props), { ref: setWrapperElement, children: [_jsxs("div", { className: styles.headline, children: [_jsx(Typography.SansLabelS, { children: t('Markdown.codeEditorTitle') }), _jsx(CopyButton, { valueToCopy: props.value || '', size: 's', onClick: onCopyClick })] }), _jsx(Editor, Object.assign({}, jsonSchemaProps, props, { theme: theme !== null && theme !== void 0 ? theme : (dark ? 'snackDark' : 'snack'), className: cn({ [styles.editor]: hasBackground }), loading: loading !== null && loading !== void 0 ? loading : _jsx(Spinner, {}), options: mergedOptions, onMount: onEditorMount, language: language }))] })));
91
95
  }
@@ -0,0 +1,9 @@
1
+ import { ButtonFunctionProps } from '@snack-uikit/button';
2
+ import { WithSupportProps } from '@snack-uikit/utils';
3
+ export type CopyButtonProps = WithSupportProps<{
4
+ valueToCopy: string | number;
5
+ size?: ButtonFunctionProps['size'];
6
+ className?: string;
7
+ onClick?: ButtonFunctionProps['onClick'];
8
+ }>;
9
+ export declare function CopyButton({ valueToCopy, size, className, onClick, ...rest }: CopyButtonProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,41 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { jsx as _jsx } from "react/jsx-runtime";
13
+ import { useEffect, useRef, useState } from 'react';
14
+ import { copyToClipboard } from '@cloud-ru/frontend-tools-copy-to-clipboard';
15
+ import { ButtonFunction } from '@snack-uikit/button';
16
+ import { CheckSVG, CopySVG } from '@snack-uikit/icons';
17
+ import { useLocale } from '@snack-uikit/locale';
18
+ import { Tooltip } from '@snack-uikit/tooltip';
19
+ import { extractSupportProps } from '@snack-uikit/utils';
20
+ import styles from './styles.module.css';
21
+ export function CopyButton(_a) {
22
+ var { valueToCopy, size = 's', className, onClick } = _a, rest = __rest(_a, ["valueToCopy", "size", "className", "onClick"]);
23
+ const { t } = useLocale();
24
+ const [isChecked, setIsCheckedOpen] = useState(false);
25
+ const timerId = useRef();
26
+ const openChecked = () => setIsCheckedOpen(true);
27
+ const closeChecked = () => setIsCheckedOpen(false);
28
+ const handleClick = event => {
29
+ event.stopPropagation();
30
+ valueToCopy && copyToClipboard(String(valueToCopy));
31
+ openChecked();
32
+ clearTimeout(timerId.current);
33
+ timerId.current = setTimeout(closeChecked, 1000);
34
+ onClick === null || onClick === void 0 ? void 0 : onClick(event);
35
+ };
36
+ useEffect(() => () => {
37
+ closeChecked();
38
+ clearTimeout(timerId.current);
39
+ }, []);
40
+ return (_jsx(Tooltip, { tip: t('Markdown.copyButtonToolTip'), children: _jsx(ButtonFunction, Object.assign({}, extractSupportProps(rest), { onClick: handleClick, className: className, "data-test-id": 'button-copy-value', type: 'button', icon: isChecked ? _jsx(CheckSVG, { className: styles.checkIcon }) : _jsx(CopySVG, {}), size: size })) }));
41
+ }
@@ -0,0 +1 @@
1
+ export * from './CopyButton';
@@ -0,0 +1 @@
1
+ export * from './CopyButton';
@@ -0,0 +1,3 @@
1
+ .checkIcon{
2
+ fill:var(--sys-green-accent-default, #57b762);
3
+ }
@@ -10,4 +10,15 @@
10
10
  width:100%;
11
11
  opacity:var(--opacity-a004, 0.04);
12
12
  background-color:var(--sys-neutral-accent-default, #787b8a);
13
+ }
14
+
15
+ .headline{
16
+ min-height:var(--size-markdown-code-editor-headline-min-height, 36px);
17
+ padding-left:var(--space-markdown-code-editor-headline-horizontal-padding, 12px);
18
+ padding-right:var(--space-markdown-code-editor-headline-horizontal-padding, 12px);
19
+ display:flex;
20
+ align-items:center;
21
+ justify-content:space-between;
22
+ background-color:var(--sys-neutral-background, #eeeff3);
23
+ border-color:var(--sys-neutral-decor-default, #dde0ea);
13
24
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.6.4-preview-845a4e8b.0",
7
+ "version": "0.6.5-preview-580e8472.0",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -36,9 +36,15 @@
36
36
  "license": "Apache-2.0",
37
37
  "scripts": {},
38
38
  "dependencies": {
39
+ "@cloud-ru/frontend-tools-copy-to-clipboard": "0.0.1-preview-8188049.0",
39
40
  "@monaco-editor/react": "4.6.0",
40
- "@snack-uikit/loaders": "0.9.5-preview-845a4e8b.0",
41
- "@snack-uikit/utils": "3.8.2-preview-845a4e8b.0",
41
+ "@snack-uikit/button": "0.19.11",
42
+ "@snack-uikit/icons": "0.26.3",
43
+ "@snack-uikit/loaders": "0.9.5",
44
+ "@snack-uikit/locale": "0.14.1-preview-580e8472.0",
45
+ "@snack-uikit/tooltip": "0.17.2",
46
+ "@snack-uikit/typography": "0.8.7",
47
+ "@snack-uikit/utils": "3.8.2",
42
48
  "chroma-js": "3.1.2",
43
49
  "classnames": "2.5.1",
44
50
  "monaco-yaml": "5.2.3"
@@ -46,5 +52,5 @@
46
52
  "devDependencies": {
47
53
  "@types/chroma-js": "2.4.4"
48
54
  },
49
- "gitHead": "84992c2beb4db1a6bddcb3209c0ffbd26df23185"
55
+ "gitHead": "83474111057d185ee911d5bb4489288c89f1490f"
50
56
  }
@@ -2,10 +2,14 @@ import { Editor, OnMount, useMonaco } from '@monaco-editor/react';
2
2
  import cn from 'classnames';
3
3
  import { useCallback, useMemo, useState } from 'react';
4
4
 
5
+ import { ButtonFunctionProps } from '@snack-uikit/button';
5
6
  import { Spinner } from '@snack-uikit/loaders';
7
+ import { useLocale } from '@snack-uikit/locale';
8
+ import { Typography } from '@snack-uikit/typography';
6
9
  import { extractSupportProps, isBrowser, WithSupportProps } from '@snack-uikit/utils';
7
10
 
8
11
  import { CODE_EDITOR_OPTIONS, DEFAULT_THEME_OPTIONS, DEFAULT_THEME_VALUES } from './constants';
12
+ import { CopyButton } from './CopyButton';
9
13
  import { useApplyJsonSchema, useCalculatedThemeValues } from './hooks';
10
14
  import styles from './styles.module.scss';
11
15
  import { EditorBaseProps, EditorWithJsonSchemaProps } from './types';
@@ -20,6 +24,10 @@ export type CodeEditorProps = WithSupportProps<{
20
24
  * Включение/отключение псевдобекграунда
21
25
  */
22
26
  hasBackground?: boolean;
27
+ /**
28
+ * Клик по кнопке копирования
29
+ */
30
+ onCopyClick?: ButtonFunctionProps['onClick'];
23
31
  }> &
24
32
  (EditorBaseProps | EditorWithJsonSchemaProps);
25
33
 
@@ -32,11 +40,14 @@ export function CodeEditor({
32
40
  hasBackground = true,
33
41
  onMount,
34
42
  language,
43
+ onCopyClick,
35
44
  ...props
36
45
  }: CodeEditorProps) {
37
46
  const monaco = useMonaco();
38
47
  const [wrapperElement, setWrapperElement] = useState<HTMLDivElement | null>(null);
39
48
 
49
+ const { t } = useLocale();
50
+
40
51
  const { jsonSchemaProps, jsonSchemaOptions } = useApplyJsonSchema(
41
52
  language,
42
53
  'jsonSchema' in props ? props.jsonSchema : undefined,
@@ -132,6 +143,10 @@ export function CodeEditor({
132
143
 
133
144
  return (
134
145
  <div className={className} {...extractSupportProps(props)} ref={setWrapperElement}>
146
+ <div className={styles.headline}>
147
+ <Typography.SansLabelS>{t('Markdown.codeEditorTitle')}</Typography.SansLabelS>
148
+ <CopyButton valueToCopy={props.value || ''} size={'s'} onClick={onCopyClick} />
149
+ </div>
135
150
  <Editor
136
151
  {...jsonSchemaProps}
137
152
  {...props}
@@ -0,0 +1,56 @@
1
+ import { MouseEventHandler, useEffect, useRef, useState } from 'react';
2
+
3
+ import { copyToClipboard } from '@cloud-ru/frontend-tools-copy-to-clipboard';
4
+ import { ButtonFunction, ButtonFunctionProps } from '@snack-uikit/button';
5
+ import { CheckSVG, CopySVG } from '@snack-uikit/icons';
6
+ import { useLocale } from '@snack-uikit/locale';
7
+ import { Tooltip } from '@snack-uikit/tooltip';
8
+ import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
9
+
10
+ import styles from './styles.module.scss';
11
+
12
+ export type CopyButtonProps = WithSupportProps<{
13
+ valueToCopy: string | number;
14
+ size?: ButtonFunctionProps['size'];
15
+ className?: string;
16
+ onClick?: ButtonFunctionProps['onClick'];
17
+ }>;
18
+
19
+ export function CopyButton({ valueToCopy, size = 's', className, onClick, ...rest }: CopyButtonProps) {
20
+ const { t } = useLocale();
21
+ const [isChecked, setIsCheckedOpen] = useState(false);
22
+ const timerId = useRef<NodeJS.Timeout>();
23
+ const openChecked = () => setIsCheckedOpen(true);
24
+ const closeChecked = () => setIsCheckedOpen(false);
25
+
26
+ const handleClick: MouseEventHandler<HTMLButtonElement> = event => {
27
+ event.stopPropagation();
28
+ valueToCopy && copyToClipboard(String(valueToCopy));
29
+ openChecked();
30
+ clearTimeout(timerId.current);
31
+ timerId.current = setTimeout(closeChecked, 1000);
32
+ onClick?.(event);
33
+ };
34
+
35
+ useEffect(
36
+ () => () => {
37
+ closeChecked();
38
+ clearTimeout(timerId.current);
39
+ },
40
+ [],
41
+ );
42
+
43
+ return (
44
+ <Tooltip tip={t('Markdown.copyButtonToolTip')}>
45
+ <ButtonFunction
46
+ {...extractSupportProps(rest)}
47
+ onClick={handleClick}
48
+ className={className}
49
+ data-test-id='button-copy-value'
50
+ type='button'
51
+ icon={isChecked ? <CheckSVG className={styles.checkIcon} /> : <CopySVG />}
52
+ size={size}
53
+ />
54
+ </Tooltip>
55
+ );
56
+ }
@@ -0,0 +1 @@
1
+ export * from './CopyButton';
@@ -0,0 +1,6 @@
1
+ @use '@snack-uikit/figma-tokens/build/scss/styles-theme-variables';
2
+
3
+
4
+ .checkIcon {
5
+ fill: styles-theme-variables.$sys-green-accent-default
6
+ }
@@ -1,4 +1,6 @@
1
1
  @use '@snack-uikit/figma-tokens/build/scss/styles-theme-variables';
2
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-markdown';
3
+
2
4
 
3
5
  .editor {
4
6
  background-color: inherit;
@@ -17,3 +19,15 @@
17
19
  background-color: styles-theme-variables.$sys-neutral-accent-default;
18
20
  }
19
21
  }
22
+
23
+ .headline {
24
+ @include styles-tokens-markdown.composite-var(styles-tokens-markdown.$markdown-syntax-code-editor-headline);
25
+
26
+ display: flex;
27
+ align-items: center;
28
+ justify-content: space-between;
29
+
30
+ background-color: styles-tokens-markdown.$sys-neutral-background;
31
+ border-color: styles-tokens-markdown.$sys-neutral-decor-default;
32
+
33
+ }