@gravity-ui/page-constructor 5.14.4-alpha.1 → 5.14.4-alpha.2

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 (29) hide show
  1. package/build/cjs/editor-v2/components/BigOverlay/BigOverlay.js +6 -3
  2. package/build/cjs/editor-v2/components/MiddleScreen/MiddleScreen.css +5 -3
  3. package/build/cjs/editor-v2/components/MiddleScreen/MiddleScreen.js +16 -11
  4. package/build/cjs/editor-v2/components/SourceCode/SourceCode.css +18 -6
  5. package/build/cjs/editor-v2/components/SourceCode/SourceCode.js +26 -5
  6. package/build/cjs/editor-v2/components/TopBar/TopBar.d.ts +4 -0
  7. package/build/cjs/editor-v2/components/TopBar/TopBar.js +2 -2
  8. package/build/cjs/editor-v2/components/ViewSwitches/ViewSwitches.css +7 -0
  9. package/build/cjs/editor-v2/components/ViewSwitches/ViewSwitches.d.ts +4 -2
  10. package/build/cjs/editor-v2/components/ViewSwitches/ViewSwitches.js +11 -14
  11. package/build/cjs/editor-v2/constants.d.ts +1 -0
  12. package/build/cjs/editor-v2/constants.js +4 -0
  13. package/build/cjs/editor-v2/context/iframeContext/store.d.ts +4 -0
  14. package/build/cjs/editor-v2/context/iframeContext/store.js +27 -1
  15. package/build/esm/editor-v2/components/BigOverlay/BigOverlay.js +7 -4
  16. package/build/esm/editor-v2/components/MiddleScreen/MiddleScreen.css +5 -3
  17. package/build/esm/editor-v2/components/MiddleScreen/MiddleScreen.js +16 -11
  18. package/build/esm/editor-v2/components/SourceCode/SourceCode.css +18 -6
  19. package/build/esm/editor-v2/components/SourceCode/SourceCode.js +27 -6
  20. package/build/esm/editor-v2/components/TopBar/TopBar.d.ts +4 -0
  21. package/build/esm/editor-v2/components/TopBar/TopBar.js +2 -2
  22. package/build/esm/editor-v2/components/ViewSwitches/ViewSwitches.css +7 -0
  23. package/build/esm/editor-v2/components/ViewSwitches/ViewSwitches.d.ts +4 -2
  24. package/build/esm/editor-v2/components/ViewSwitches/ViewSwitches.js +13 -15
  25. package/build/esm/editor-v2/constants.d.ts +1 -0
  26. package/build/esm/editor-v2/constants.js +1 -0
  27. package/build/esm/editor-v2/context/iframeContext/store.d.ts +4 -0
  28. package/build/esm/editor-v2/context/iframeContext/store.js +27 -1
  29. package/package.json +1 -1
@@ -9,6 +9,7 @@ const iframeContext_1 = require("../../context/iframeContext");
9
9
  const messagesContext_1 = require("../../context/messagesContext");
10
10
  const b = (0, utils_1.block)('big-overlay');
11
11
  const BigOverlay = ({ className }) => {
12
+ const { zoom } = (0, iframeContext_1.useIframeStore)();
12
13
  const { iframeElement } = (0, react_1.useContext)(iframeContext_1.IframeContext);
13
14
  const [mousePosition, setMousePosition] = (0, react_1.useState)(undefined);
14
15
  (0, messagesContext_1.useMessageObserver)(types_1.ActionTypes.OverlayModeOnMove, (payload, meta) => {
@@ -16,12 +17,14 @@ const BigOverlay = ({ className }) => {
16
17
  const { x, y } = payload.cursor;
17
18
  const iframeRect = iframeElement === null || iframeElement === void 0 ? void 0 : iframeElement.getClientRects().item(0);
18
19
  if (iframeRect) {
19
- const newX = meta.source === 'editor' ? x : x + iframeRect.x;
20
- const newY = meta.source === 'editor' ? y : y + iframeRect.y;
20
+ const zoomedX = (x * zoom) / 100;
21
+ const zoomedY = (y * zoom) / 100;
22
+ const newX = meta.source === 'editor' ? x : zoomedX + iframeRect.x;
23
+ const newY = meta.source === 'editor' ? y : zoomedY + iframeRect.y;
21
24
  setMousePosition({ x: newX, y: newY });
22
25
  }
23
26
  }
24
- }, [iframeElement]);
27
+ }, [iframeElement, zoom]);
25
28
  (0, messagesContext_1.useMessageObserver)(types_1.ActionTypes.InsertModeDisable, () => {
26
29
  setMousePosition(undefined);
27
30
  });
@@ -17,11 +17,13 @@ body {
17
17
  flex-direction: column;
18
18
  }
19
19
  .pc-middle-screen__topbar {
20
- height: 50px;
20
+ height: 40px;
21
21
  }
22
- .pc-middle-screen__canvas {
22
+ .pc-middle-screen__wrapper {
23
23
  height: 100%;
24
- width: 100%;
24
+ }
25
+ .pc-middle-screen__canvas {
26
+ transform-origin: left top;
25
27
  overflow-y: scroll;
26
28
  position: relative;
27
29
  }
@@ -10,20 +10,25 @@ const Overlay_1 = tslib_1.__importDefault(require("../Overlay/Overlay"));
10
10
  const TopBar_1 = tslib_1.__importDefault(require("../TopBar/TopBar"));
11
11
  const b = (0, utils_1.block)('middle-screen');
12
12
  const MiddleScreen = ({ className }) => {
13
- const { url, height } = (0, iframeContext_1.useIframeStore)();
13
+ const { url, height, zoom, decreaseZoom, increaseZoom, setZoom } = (0, iframeContext_1.useIframeStore)();
14
14
  const { initialized } = (0, editorContext_1.useEditorStore)();
15
15
  const { setIframeElement } = (0, react_1.useContext)(iframeContext_1.IframeContext);
16
16
  return (react_1.default.createElement("div", { className: b(null, className) },
17
17
  react_1.default.createElement("div", { className: b('topbar') },
18
- react_1.default.createElement(TopBar_1.default, null)),
19
- react_1.default.createElement("div", { className: b('canvas', { hidden: !initialized }) },
20
- react_1.default.createElement("iframe", { ref: (element) => {
21
- if (element) {
22
- setIframeElement(element);
23
- }
24
- }, className: b('iframe'), src: url, height: `${height}px`, width: "100%", frameBorder: "0" }),
25
- react_1.default.createElement(Overlay_1.default, { className: b('overlay') }),
26
- !initialized && (react_1.default.createElement("div", { className: b('loading') },
27
- react_1.default.createElement(uikit_1.Loader, { size: 'l' }))))));
18
+ react_1.default.createElement(TopBar_1.default, { onZoomUpdate: setZoom, zoom: zoom, onDecreaseZoom: decreaseZoom, onIncreaseZoom: increaseZoom })),
19
+ react_1.default.createElement("div", { className: b('wrapper') },
20
+ react_1.default.createElement("div", { className: b('canvas', { hidden: !initialized }), style: {
21
+ transform: `scale(${zoom}%)`,
22
+ height: `${(100 / zoom) * 100}%`,
23
+ width: `${(100 / zoom) * 100}%`,
24
+ } },
25
+ react_1.default.createElement("iframe", { ref: (element) => {
26
+ if (element) {
27
+ setIframeElement(element);
28
+ }
29
+ }, className: b('iframe'), src: url, height: `${height}px`, width: "100%", frameBorder: "0" }),
30
+ react_1.default.createElement(Overlay_1.default, { className: b('overlay') }),
31
+ !initialized && (react_1.default.createElement("div", { className: b('loading') },
32
+ react_1.default.createElement(uikit_1.Loader, { size: 'l' })))))));
28
33
  };
29
34
  exports.default = MiddleScreen;
@@ -11,22 +11,34 @@ body {
11
11
  }
12
12
 
13
13
  .pc-source-code {
14
- padding: 10px;
14
+ height: 100%;
15
+ box-sizing: border-box;
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: 10px;
19
+ }
20
+ .pc-source-code__code {
15
21
  white-space: pre;
16
22
  font-family: var(--g-font-family-monospace);
17
23
  font-size: var(--g-text-code-inline-1-font-size);
18
24
  line-height: var(--g-text-code-inline-1-line-height);
19
25
  font-weight: var(--g-text-code-font-weight);
20
- display: flex;
21
- flex-direction: column;
22
- gap: 10px;
26
+ padding: 0 10px 10px;
27
+ overflow: auto;
23
28
  }
24
- .pc-source-code__code {
25
- user-select: all;
29
+ .pc-source-code__head {
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: flex-start;
33
+ gap: 8px;
34
+ padding: 10px 10px 0;
26
35
  }
27
36
  .pc-source-code__textarea textarea {
28
37
  font-family: var(--g-font-family-monospace);
29
38
  font-size: var(--g-text-code-inline-1-font-size);
30
39
  line-height: var(--g-text-code-inline-1-line-height);
31
40
  font-weight: var(--g-text-code-font-weight);
41
+ }
42
+ .pc-source-code__alert {
43
+ margin-bottom: 8px;
32
44
  }
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const react_1 = tslib_1.__importStar(require("react"));
5
+ const icons_1 = require("@gravity-ui/icons");
5
6
  const uikit_1 = require("@gravity-ui/uikit");
7
+ const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
6
8
  const utils_1 = require("../../../utils");
7
9
  const useContentConfigStore_1 = require("../../context/contentConfig/hooks/useContentConfigStore");
8
10
  const b = (0, utils_1.block)('source-code');
@@ -10,25 +12,44 @@ const SourceCode = ({ className }) => {
10
12
  const { config, setConfig } = (0, useContentConfigStore_1.useContentConfigStore)();
11
13
  const [isOpen, setIsOpen] = (0, react_1.useState)(false);
12
14
  const [tempConfig, setTempConfig] = (0, react_1.useState)('');
15
+ const [format, setFormat] = (0, react_1.useState)('yaml');
13
16
  const onUpdate = () => {
14
17
  let object;
15
18
  try {
16
- object = JSON.parse(tempConfig);
19
+ if (tempConfig.trim().startsWith('{') && tempConfig.trim().endsWith('}')) {
20
+ object = JSON.parse(tempConfig);
21
+ }
22
+ else {
23
+ object = js_yaml_1.default.load(tempConfig);
24
+ }
17
25
  }
18
26
  catch (_a) {
19
27
  // eslint-disable-next-line no-console
20
28
  console.error('JSON.parse failed');
21
29
  }
22
- setConfig(object);
30
+ if (object) {
31
+ setConfig(object);
32
+ }
23
33
  setIsOpen(false);
24
34
  };
35
+ const text = format === 'yaml' ? js_yaml_1.default.dump(config) : JSON.stringify(config, null, 2);
25
36
  return (react_1.default.createElement("div", { className: b(null, className) },
26
- react_1.default.createElement(uikit_1.Button, { onClick: () => setIsOpen(true) }, "Update"),
27
- react_1.default.createElement("div", { className: b('code') }, JSON.stringify(config, null, 2)),
37
+ react_1.default.createElement("div", { className: b('head') },
38
+ react_1.default.createElement(uikit_1.RadioButton, { value: format, onUpdate: setFormat },
39
+ react_1.default.createElement(uikit_1.RadioButton.Option, { value: 'yaml', content: 'YAML' }),
40
+ react_1.default.createElement(uikit_1.RadioButton.Option, { value: 'json', content: 'JSON' })),
41
+ react_1.default.createElement(uikit_1.CopyToClipboard, { text: text, timeout: 1000 }, (status) => (react_1.default.createElement(uikit_1.Button, null,
42
+ status === 'pending' ? (react_1.default.createElement(uikit_1.Icon, { data: icons_1.Copy })) : (react_1.default.createElement(uikit_1.Icon, { data: icons_1.CopyCheck })),
43
+ "Copy"))),
44
+ react_1.default.createElement(uikit_1.Button, { onClick: () => setIsOpen(true) },
45
+ react_1.default.createElement(uikit_1.Icon, { data: icons_1.ArrowDownToSquare }),
46
+ "Import")),
47
+ react_1.default.createElement("div", { className: b('code') }, text),
28
48
  react_1.default.createElement(uikit_1.Dialog, { onClose: () => setIsOpen(false), open: isOpen, size: 'l' },
29
49
  react_1.default.createElement(uikit_1.Dialog.Header, { caption: "New configuration" }),
30
50
  react_1.default.createElement(uikit_1.Dialog.Body, null,
31
- react_1.default.createElement(uikit_1.TextArea, { className: b('textarea'), value: tempConfig, onUpdate: setTempConfig, rows: 30 })),
51
+ react_1.default.createElement(uikit_1.Alert, { className: b('alert'), theme: 'info', title: 'You can use YAML or JSON', message: 'The editor will automatically understand which format is needed.' }),
52
+ react_1.default.createElement(uikit_1.TextArea, { className: b('textarea'), value: tempConfig, onUpdate: setTempConfig, rows: 25 })),
32
53
  react_1.default.createElement(uikit_1.Dialog.Footer, { showError: false, listenKeyEnter: true, preset: 'default', textButtonApply: 'Apply', textButtonCancel: 'Cancel', onClickButtonApply: onUpdate, onClickButtonCancel: () => setIsOpen(false) }))));
33
54
  };
34
55
  exports.default = SourceCode;
@@ -1,6 +1,10 @@
1
1
  import React from 'react';
2
2
  import { ClassNameProps } from '../../../models';
3
3
  interface TopBarProps extends ClassNameProps {
4
+ onZoomUpdate: (zoom: number) => void;
5
+ onDecreaseZoom: () => void;
6
+ onIncreaseZoom: () => void;
7
+ zoom: number;
4
8
  }
5
9
  declare const TopBar: React.FC<TopBarProps>;
6
10
  export default TopBar;
@@ -6,10 +6,10 @@ const utils_1 = require("../../../utils");
6
6
  const Source_1 = tslib_1.__importDefault(require("../Source/Source"));
7
7
  const ViewSwitches_1 = tslib_1.__importDefault(require("../ViewSwitches/ViewSwitches"));
8
8
  const b = (0, utils_1.block)('topbar');
9
- const TopBar = ({ className }) => {
9
+ const TopBar = ({ zoom, onDecreaseZoom, onIncreaseZoom, onZoomUpdate, className, }) => {
10
10
  return (react_1.default.createElement("div", { className: b(null, className) },
11
11
  react_1.default.createElement("div", { className: b('switches') },
12
- react_1.default.createElement(ViewSwitches_1.default, null)),
12
+ react_1.default.createElement(ViewSwitches_1.default, { onZoomUpdate: onZoomUpdate, onDecreaseZoom: onDecreaseZoom, onIncreaseZoom: onIncreaseZoom, zoom: zoom })),
13
13
  react_1.default.createElement(Source_1.default, { className: b('source') })));
14
14
  };
15
15
  exports.default = TopBar;
@@ -13,4 +13,11 @@ body {
13
13
  .pc-view-switches {
14
14
  display: inline-flex;
15
15
  gap: 12px;
16
+ }
17
+ .pc-view-switches__zoom {
18
+ display: flex;
19
+ gap: 4px;
20
+ }
21
+ .pc-view-switches__zoom-input {
22
+ width: 40px;
16
23
  }
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
- import { RadioButtonOption } from '@gravity-ui/uikit';
3
2
  import { ClassNameProps } from '../../../models';
4
3
  interface ViewSwitchesProps extends ClassNameProps {
4
+ onZoomUpdate: (zoom: number) => void;
5
+ onDecreaseZoom: () => void;
6
+ onIncreaseZoom: () => void;
7
+ zoom: number;
5
8
  }
6
- export declare const options2: RadioButtonOption[];
7
9
  declare const ViewSwitches: React.FC<ViewSwitchesProps>;
8
10
  export default ViewSwitches;
@@ -1,25 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.options2 = void 0;
4
3
  const tslib_1 = require("tslib");
5
4
  const react_1 = tslib_1.__importDefault(require("react"));
6
5
  const icons_1 = require("@gravity-ui/icons");
7
6
  const uikit_1 = require("@gravity-ui/uikit");
8
7
  const utils_1 = require("../../../utils");
8
+ const constants_1 = require("../../constants");
9
9
  const b = (0, utils_1.block)('view-switches');
10
- const options1 = [
11
- { value: 'edit-mode', content: react_1.default.createElement(uikit_1.Icon, { data: icons_1.SquareDashedText }) },
12
- { value: 'preview-mode', content: react_1.default.createElement(uikit_1.Icon, { data: icons_1.Eye }) },
13
- ];
14
- exports.options2 = [
15
- { value: 'desktop-mode', content: react_1.default.createElement(uikit_1.Icon, { data: icons_1.Display }) },
16
- { value: 'tablet-mode', content: react_1.default.createElement(uikit_1.Icon, { data: icons_1.Square }) },
17
- { value: 'phone-mode', content: react_1.default.createElement(uikit_1.Icon, { data: icons_1.Smartphone }) },
18
- ];
19
- // TODO: To be done
20
- const ViewSwitches = ({ className }) => {
10
+ const ViewSwitches = ({ zoom, onIncreaseZoom, onDecreaseZoom, onZoomUpdate, className, }) => {
21
11
  return (react_1.default.createElement("div", { className: b(null, className) },
22
- react_1.default.createElement(uikit_1.RadioButton, { className: b('switch'), name: "group1", defaultValue: options1[0].value, options: options1, size: "m", disabled: true }),
23
- react_1.default.createElement(uikit_1.RadioButton, { className: b('switch'), name: "group2", defaultValue: exports.options2[0].value, options: exports.options2, size: "m", disabled: true })));
12
+ react_1.default.createElement("div", { className: b('zoom') },
13
+ react_1.default.createElement(uikit_1.Button, { view: 'flat', onClick: onDecreaseZoom },
14
+ react_1.default.createElement(uikit_1.Icon, { data: icons_1.Minus })),
15
+ react_1.default.createElement(uikit_1.Select, { multiple: false, value: [String(zoom)], options: constants_1.ZOOM_STEPS.map((step) => ({
16
+ value: String(step),
17
+ content: `${String(step)}%`,
18
+ })), onUpdate: (value) => onZoomUpdate(Number(value)) }),
19
+ react_1.default.createElement(uikit_1.Button, { view: 'flat', onClick: onIncreaseZoom },
20
+ react_1.default.createElement(uikit_1.Icon, { data: icons_1.Plus })))));
24
21
  };
25
22
  exports.default = ViewSwitches;
@@ -0,0 +1 @@
1
+ export declare const ZOOM_STEPS: number[];
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ZOOM_STEPS = void 0;
4
+ exports.ZOOM_STEPS = [25, 33, 50, 75, 100, 125, 150, 200, 250, 300];
@@ -2,9 +2,13 @@ import { WithStoreReducer } from '../../../common/types';
2
2
  export interface IframeState {
3
3
  url: string;
4
4
  height?: number;
5
+ zoom: number;
5
6
  }
6
7
  export interface IframeMethods extends WithStoreReducer {
7
8
  setUrl: (url: string) => void;
9
+ setZoom: (zoom: number) => void;
10
+ increaseZoom: () => void;
11
+ decreaseZoom: () => void;
8
12
  }
9
13
  export type IframeStore = IframeState & IframeMethods;
10
14
  export declare const createIframeStore: (overrideInitialState?: Partial<IframeState> | undefined) => import("zustand").UseBoundStore<Omit<Omit<import("zustand").StoreApi<IframeState & IframeMethods>, "subscribe"> & {
@@ -2,11 +2,37 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createIframeStore = void 0;
4
4
  const types_1 = require("../../../common/types");
5
+ const constants_1 = require("../../constants");
5
6
  const store_1 = require("../../utils/store");
6
7
  exports.createIframeStore = (0, store_1.initializeStore)({
7
8
  url: '',
8
9
  height: 400,
9
- }, (set) => ({
10
+ zoom: 100,
11
+ }, (set, get) => ({
12
+ setZoom: function (zoom) {
13
+ if (zoom > 0) {
14
+ set((state) => (Object.assign(Object.assign({}, state), { zoom })));
15
+ }
16
+ },
17
+ increaseZoom: function () {
18
+ const currentZoom = get().zoom;
19
+ for (const step of constants_1.ZOOM_STEPS) {
20
+ if (currentZoom < step) {
21
+ get().setZoom(step);
22
+ break;
23
+ }
24
+ }
25
+ },
26
+ decreaseZoom: function () {
27
+ const currentZoom = get().zoom;
28
+ const reverseSteps = constants_1.ZOOM_STEPS.slice().reverse();
29
+ for (const step of reverseSteps) {
30
+ if (currentZoom > step) {
31
+ get().setZoom(step);
32
+ break;
33
+ }
34
+ }
35
+ },
10
36
  setUrl: (url) => set((state) => (Object.assign(Object.assign({}, state), { url }))),
11
37
  reducer: (action) => {
12
38
  switch (action.type) {
@@ -2,11 +2,12 @@ import React, { useContext, useState } from 'react';
2
2
  import { Stop } from '@gravity-ui/icons';
3
3
  import { ActionTypes, } from '../../../common/types';
4
4
  import { block } from '../../../utils';
5
- import { IframeContext } from '../../context/iframeContext';
5
+ import { IframeContext, useIframeStore } from '../../context/iframeContext';
6
6
  import { useMessageObserver } from '../../context/messagesContext';
7
7
  import './BigOverlay.css';
8
8
  const b = block('big-overlay');
9
9
  const BigOverlay = ({ className }) => {
10
+ const { zoom } = useIframeStore();
10
11
  const { iframeElement } = useContext(IframeContext);
11
12
  const [mousePosition, setMousePosition] = useState(undefined);
12
13
  useMessageObserver(ActionTypes.OverlayModeOnMove, (payload, meta) => {
@@ -14,12 +15,14 @@ const BigOverlay = ({ className }) => {
14
15
  const { x, y } = payload.cursor;
15
16
  const iframeRect = iframeElement === null || iframeElement === void 0 ? void 0 : iframeElement.getClientRects().item(0);
16
17
  if (iframeRect) {
17
- const newX = meta.source === 'editor' ? x : x + iframeRect.x;
18
- const newY = meta.source === 'editor' ? y : y + iframeRect.y;
18
+ const zoomedX = (x * zoom) / 100;
19
+ const zoomedY = (y * zoom) / 100;
20
+ const newX = meta.source === 'editor' ? x : zoomedX + iframeRect.x;
21
+ const newY = meta.source === 'editor' ? y : zoomedY + iframeRect.y;
19
22
  setMousePosition({ x: newX, y: newY });
20
23
  }
21
24
  }
22
- }, [iframeElement]);
25
+ }, [iframeElement, zoom]);
23
26
  useMessageObserver(ActionTypes.InsertModeDisable, () => {
24
27
  setMousePosition(undefined);
25
28
  });
@@ -17,11 +17,13 @@ body {
17
17
  flex-direction: column;
18
18
  }
19
19
  .pc-middle-screen__topbar {
20
- height: 50px;
20
+ height: 40px;
21
21
  }
22
- .pc-middle-screen__canvas {
22
+ .pc-middle-screen__wrapper {
23
23
  height: 100%;
24
- width: 100%;
24
+ }
25
+ .pc-middle-screen__canvas {
26
+ transform-origin: left top;
25
27
  overflow-y: scroll;
26
28
  position: relative;
27
29
  }
@@ -8,20 +8,25 @@ import TopBar from '../TopBar/TopBar';
8
8
  import './MiddleScreen.css';
9
9
  const b = block('middle-screen');
10
10
  const MiddleScreen = ({ className }) => {
11
- const { url, height } = useIframeStore();
11
+ const { url, height, zoom, decreaseZoom, increaseZoom, setZoom } = useIframeStore();
12
12
  const { initialized } = useEditorStore();
13
13
  const { setIframeElement } = useContext(IframeContext);
14
14
  return (React.createElement("div", { className: b(null, className) },
15
15
  React.createElement("div", { className: b('topbar') },
16
- React.createElement(TopBar, null)),
17
- React.createElement("div", { className: b('canvas', { hidden: !initialized }) },
18
- React.createElement("iframe", { ref: (element) => {
19
- if (element) {
20
- setIframeElement(element);
21
- }
22
- }, className: b('iframe'), src: url, height: `${height}px`, width: "100%", frameBorder: "0" }),
23
- React.createElement(Overlay, { className: b('overlay') }),
24
- !initialized && (React.createElement("div", { className: b('loading') },
25
- React.createElement(Loader, { size: 'l' }))))));
16
+ React.createElement(TopBar, { onZoomUpdate: setZoom, zoom: zoom, onDecreaseZoom: decreaseZoom, onIncreaseZoom: increaseZoom })),
17
+ React.createElement("div", { className: b('wrapper') },
18
+ React.createElement("div", { className: b('canvas', { hidden: !initialized }), style: {
19
+ transform: `scale(${zoom}%)`,
20
+ height: `${(100 / zoom) * 100}%`,
21
+ width: `${(100 / zoom) * 100}%`,
22
+ } },
23
+ React.createElement("iframe", { ref: (element) => {
24
+ if (element) {
25
+ setIframeElement(element);
26
+ }
27
+ }, className: b('iframe'), src: url, height: `${height}px`, width: "100%", frameBorder: "0" }),
28
+ React.createElement(Overlay, { className: b('overlay') }),
29
+ !initialized && (React.createElement("div", { className: b('loading') },
30
+ React.createElement(Loader, { size: 'l' })))))));
26
31
  };
27
32
  export default MiddleScreen;
@@ -11,22 +11,34 @@ body {
11
11
  }
12
12
 
13
13
  .pc-source-code {
14
- padding: 10px;
14
+ height: 100%;
15
+ box-sizing: border-box;
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: 10px;
19
+ }
20
+ .pc-source-code__code {
15
21
  white-space: pre;
16
22
  font-family: var(--g-font-family-monospace);
17
23
  font-size: var(--g-text-code-inline-1-font-size);
18
24
  line-height: var(--g-text-code-inline-1-line-height);
19
25
  font-weight: var(--g-text-code-font-weight);
20
- display: flex;
21
- flex-direction: column;
22
- gap: 10px;
26
+ padding: 0 10px 10px;
27
+ overflow: auto;
23
28
  }
24
- .pc-source-code__code {
25
- user-select: all;
29
+ .pc-source-code__head {
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: flex-start;
33
+ gap: 8px;
34
+ padding: 10px 10px 0;
26
35
  }
27
36
  .pc-source-code__textarea textarea {
28
37
  font-family: var(--g-font-family-monospace);
29
38
  font-size: var(--g-text-code-inline-1-font-size);
30
39
  line-height: var(--g-text-code-inline-1-line-height);
31
40
  font-weight: var(--g-text-code-font-weight);
41
+ }
42
+ .pc-source-code__alert {
43
+ margin-bottom: 8px;
32
44
  }
@@ -1,5 +1,7 @@
1
1
  import React, { useState } from 'react';
2
- import { Button, Dialog, TextArea } from '@gravity-ui/uikit';
2
+ import { ArrowDownToSquare, Copy, CopyCheck } from '@gravity-ui/icons';
3
+ import { Alert, Button, CopyToClipboard, Dialog, Icon, RadioButton, TextArea, } from '@gravity-ui/uikit';
4
+ import yaml from 'js-yaml';
3
5
  import { block } from '../../../utils';
4
6
  import { useContentConfigStore } from '../../context/contentConfig/hooks/useContentConfigStore';
5
7
  import './SourceCode.css';
@@ -8,25 +10,44 @@ const SourceCode = ({ className }) => {
8
10
  const { config, setConfig } = useContentConfigStore();
9
11
  const [isOpen, setIsOpen] = useState(false);
10
12
  const [tempConfig, setTempConfig] = useState('');
13
+ const [format, setFormat] = useState('yaml');
11
14
  const onUpdate = () => {
12
15
  let object;
13
16
  try {
14
- object = JSON.parse(tempConfig);
17
+ if (tempConfig.trim().startsWith('{') && tempConfig.trim().endsWith('}')) {
18
+ object = JSON.parse(tempConfig);
19
+ }
20
+ else {
21
+ object = yaml.load(tempConfig);
22
+ }
15
23
  }
16
24
  catch (_a) {
17
25
  // eslint-disable-next-line no-console
18
26
  console.error('JSON.parse failed');
19
27
  }
20
- setConfig(object);
28
+ if (object) {
29
+ setConfig(object);
30
+ }
21
31
  setIsOpen(false);
22
32
  };
33
+ const text = format === 'yaml' ? yaml.dump(config) : JSON.stringify(config, null, 2);
23
34
  return (React.createElement("div", { className: b(null, className) },
24
- React.createElement(Button, { onClick: () => setIsOpen(true) }, "Update"),
25
- React.createElement("div", { className: b('code') }, JSON.stringify(config, null, 2)),
35
+ React.createElement("div", { className: b('head') },
36
+ React.createElement(RadioButton, { value: format, onUpdate: setFormat },
37
+ React.createElement(RadioButton.Option, { value: 'yaml', content: 'YAML' }),
38
+ React.createElement(RadioButton.Option, { value: 'json', content: 'JSON' })),
39
+ React.createElement(CopyToClipboard, { text: text, timeout: 1000 }, (status) => (React.createElement(Button, null,
40
+ status === 'pending' ? (React.createElement(Icon, { data: Copy })) : (React.createElement(Icon, { data: CopyCheck })),
41
+ "Copy"))),
42
+ React.createElement(Button, { onClick: () => setIsOpen(true) },
43
+ React.createElement(Icon, { data: ArrowDownToSquare }),
44
+ "Import")),
45
+ React.createElement("div", { className: b('code') }, text),
26
46
  React.createElement(Dialog, { onClose: () => setIsOpen(false), open: isOpen, size: 'l' },
27
47
  React.createElement(Dialog.Header, { caption: "New configuration" }),
28
48
  React.createElement(Dialog.Body, null,
29
- React.createElement(TextArea, { className: b('textarea'), value: tempConfig, onUpdate: setTempConfig, rows: 30 })),
49
+ React.createElement(Alert, { className: b('alert'), theme: 'info', title: 'You can use YAML or JSON', message: 'The editor will automatically understand which format is needed.' }),
50
+ React.createElement(TextArea, { className: b('textarea'), value: tempConfig, onUpdate: setTempConfig, rows: 25 })),
30
51
  React.createElement(Dialog.Footer, { showError: false, listenKeyEnter: true, preset: 'default', textButtonApply: 'Apply', textButtonCancel: 'Cancel', onClickButtonApply: onUpdate, onClickButtonCancel: () => setIsOpen(false) }))));
31
52
  };
32
53
  export default SourceCode;
@@ -2,6 +2,10 @@ import React from 'react';
2
2
  import { ClassNameProps } from '../../../models';
3
3
  import './TopBar.css';
4
4
  interface TopBarProps extends ClassNameProps {
5
+ onZoomUpdate: (zoom: number) => void;
6
+ onDecreaseZoom: () => void;
7
+ onIncreaseZoom: () => void;
8
+ zoom: number;
5
9
  }
6
10
  declare const TopBar: React.FC<TopBarProps>;
7
11
  export default TopBar;
@@ -4,10 +4,10 @@ import Source from '../Source/Source';
4
4
  import ViewSwitches from '../ViewSwitches/ViewSwitches';
5
5
  import './TopBar.css';
6
6
  const b = block('topbar');
7
- const TopBar = ({ className }) => {
7
+ const TopBar = ({ zoom, onDecreaseZoom, onIncreaseZoom, onZoomUpdate, className, }) => {
8
8
  return (React.createElement("div", { className: b(null, className) },
9
9
  React.createElement("div", { className: b('switches') },
10
- React.createElement(ViewSwitches, null)),
10
+ React.createElement(ViewSwitches, { onZoomUpdate: onZoomUpdate, onDecreaseZoom: onDecreaseZoom, onIncreaseZoom: onIncreaseZoom, zoom: zoom })),
11
11
  React.createElement(Source, { className: b('source') })));
12
12
  };
13
13
  export default TopBar;
@@ -13,4 +13,11 @@ body {
13
13
  .pc-view-switches {
14
14
  display: inline-flex;
15
15
  gap: 12px;
16
+ }
17
+ .pc-view-switches__zoom {
18
+ display: flex;
19
+ gap: 4px;
20
+ }
21
+ .pc-view-switches__zoom-input {
22
+ width: 40px;
16
23
  }
@@ -1,9 +1,11 @@
1
1
  import React from 'react';
2
- import { RadioButtonOption } from '@gravity-ui/uikit';
3
2
  import { ClassNameProps } from '../../../models';
4
3
  import './ViewSwitches.css';
5
4
  interface ViewSwitchesProps extends ClassNameProps {
5
+ onZoomUpdate: (zoom: number) => void;
6
+ onDecreaseZoom: () => void;
7
+ onIncreaseZoom: () => void;
8
+ zoom: number;
6
9
  }
7
- export declare const options2: RadioButtonOption[];
8
10
  declare const ViewSwitches: React.FC<ViewSwitchesProps>;
9
11
  export default ViewSwitches;
@@ -1,22 +1,20 @@
1
1
  import React from 'react';
2
- import { Display, Eye, Smartphone, Square, SquareDashedText } from '@gravity-ui/icons';
3
- import { Icon, RadioButton } from '@gravity-ui/uikit';
2
+ import { Minus, Plus } from '@gravity-ui/icons';
3
+ import { Button, Icon, Select } from '@gravity-ui/uikit';
4
4
  import { block } from '../../../utils';
5
+ import { ZOOM_STEPS } from '../../constants';
5
6
  import './ViewSwitches.css';
6
7
  const b = block('view-switches');
7
- const options1 = [
8
- { value: 'edit-mode', content: React.createElement(Icon, { data: SquareDashedText }) },
9
- { value: 'preview-mode', content: React.createElement(Icon, { data: Eye }) },
10
- ];
11
- export const options2 = [
12
- { value: 'desktop-mode', content: React.createElement(Icon, { data: Display }) },
13
- { value: 'tablet-mode', content: React.createElement(Icon, { data: Square }) },
14
- { value: 'phone-mode', content: React.createElement(Icon, { data: Smartphone }) },
15
- ];
16
- // TODO: To be done
17
- const ViewSwitches = ({ className }) => {
8
+ const ViewSwitches = ({ zoom, onIncreaseZoom, onDecreaseZoom, onZoomUpdate, className, }) => {
18
9
  return (React.createElement("div", { className: b(null, className) },
19
- React.createElement(RadioButton, { className: b('switch'), name: "group1", defaultValue: options1[0].value, options: options1, size: "m", disabled: true }),
20
- React.createElement(RadioButton, { className: b('switch'), name: "group2", defaultValue: options2[0].value, options: options2, size: "m", disabled: true })));
10
+ React.createElement("div", { className: b('zoom') },
11
+ React.createElement(Button, { view: 'flat', onClick: onDecreaseZoom },
12
+ React.createElement(Icon, { data: Minus })),
13
+ React.createElement(Select, { multiple: false, value: [String(zoom)], options: ZOOM_STEPS.map((step) => ({
14
+ value: String(step),
15
+ content: `${String(step)}%`,
16
+ })), onUpdate: (value) => onZoomUpdate(Number(value)) }),
17
+ React.createElement(Button, { view: 'flat', onClick: onIncreaseZoom },
18
+ React.createElement(Icon, { data: Plus })))));
21
19
  };
22
20
  export default ViewSwitches;
@@ -0,0 +1 @@
1
+ export declare const ZOOM_STEPS: number[];
@@ -0,0 +1 @@
1
+ export const ZOOM_STEPS = [25, 33, 50, 75, 100, 125, 150, 200, 250, 300];
@@ -2,9 +2,13 @@ import { WithStoreReducer } from '../../../common/types';
2
2
  export interface IframeState {
3
3
  url: string;
4
4
  height?: number;
5
+ zoom: number;
5
6
  }
6
7
  export interface IframeMethods extends WithStoreReducer {
7
8
  setUrl: (url: string) => void;
9
+ setZoom: (zoom: number) => void;
10
+ increaseZoom: () => void;
11
+ decreaseZoom: () => void;
8
12
  }
9
13
  export type IframeStore = IframeState & IframeMethods;
10
14
  export declare const createIframeStore: (overrideInitialState?: Partial<IframeState> | undefined) => import("zustand").UseBoundStore<Omit<Omit<import("zustand").StoreApi<IframeState & IframeMethods>, "subscribe"> & {
@@ -1,9 +1,35 @@
1
1
  import { ActionTypes } from '../../../common/types';
2
+ import { ZOOM_STEPS } from '../../constants';
2
3
  import { initializeStore } from '../../utils/store';
3
4
  export const createIframeStore = initializeStore({
4
5
  url: '',
5
6
  height: 400,
6
- }, (set) => ({
7
+ zoom: 100,
8
+ }, (set, get) => ({
9
+ setZoom: function (zoom) {
10
+ if (zoom > 0) {
11
+ set((state) => (Object.assign(Object.assign({}, state), { zoom })));
12
+ }
13
+ },
14
+ increaseZoom: function () {
15
+ const currentZoom = get().zoom;
16
+ for (const step of ZOOM_STEPS) {
17
+ if (currentZoom < step) {
18
+ get().setZoom(step);
19
+ break;
20
+ }
21
+ }
22
+ },
23
+ decreaseZoom: function () {
24
+ const currentZoom = get().zoom;
25
+ const reverseSteps = ZOOM_STEPS.slice().reverse();
26
+ for (const step of reverseSteps) {
27
+ if (currentZoom > step) {
28
+ get().setZoom(step);
29
+ break;
30
+ }
31
+ }
32
+ },
7
33
  setUrl: (url) => set((state) => (Object.assign(Object.assign({}, state), { url }))),
8
34
  reducer: (action) => {
9
35
  switch (action.type) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/page-constructor",
3
- "version": "5.14.4-alpha.1",
3
+ "version": "5.14.4-alpha.2",
4
4
  "description": "Gravity UI Page Constructor",
5
5
  "license": "MIT",
6
6
  "repository": {