From 911af01aeabe3ac3080712e09b985a7e195afb4b Mon Sep 17 00:00:00 2001 From: daff Date: Fri, 4 Oct 2024 14:45:19 +0300 Subject: [PATCH 001/118] feat: experimental Editor functionality --- .gitignore | 5 + .prettierignore | 6 +- README.md | 32 +- package-lock.json | 448 +++++++++-- package.json | 16 +- playground/.gitignore | 28 + playground/next.config.mjs | 4 + playground/package-lock.json | 478 ++++++++++++ playground/package.json | 23 + playground/src/app/content.json | 335 ++++++++ playground/src/app/favicon.ico | Bin 0 -> 1732 bytes playground/src/app/layout.tsx | 17 + playground/src/app/page.scss | 5 + playground/src/app/page.tsx | 36 + playground/src/app/pc/page.tsx | 13 + playground/src/constants.ts | 4 + playground/src/styles/globals.scss | 6 + playground/tsconfig.json | 32 + src/blocks/Banner/Banner.tsx | 5 +- src/blocks/Banner/index.tsx | 25 + src/blocks/CardLayout/CardLayout.scss | 1 + src/blocks/CardLayout/CardLayout.tsx | 2 + src/blocks/CardLayout/index.ts | 170 ++++ src/blocks/Companies/Companies.tsx | 17 +- src/blocks/Companies/index.ts | 23 + src/blocks/ContentLayout/ContentLayout.tsx | 68 +- src/blocks/ContentLayout/index.ts | 21 + .../ExtendedFeatures/ExtendedFeatures.tsx | 46 +- src/blocks/ExtendedFeatures/index.ts | 22 + src/blocks/ExtendedFeatures/schema.ts | 1 + src/blocks/FilterBlock/FilterBlock.tsx | 62 +- src/blocks/FilterBlock/index.ts | 19 + src/blocks/Form/index.ts | 20 + src/blocks/Header/dynamic-form.ts | 732 ++++++++++++++++++ src/blocks/Header/index.ts | 16 + src/blocks/HeaderSlider/HeaderSlider.tsx | 19 +- src/blocks/HeaderSlider/index.ts | 44 ++ src/blocks/Icons/Icons.tsx | 64 +- src/blocks/Icons/index.ts | 22 + src/blocks/Info/index.ts | 43 + src/blocks/Map/index.ts | 19 + src/blocks/Media/index.ts | 23 + .../PromoFeaturesBlock/PromoFeaturesBlock.tsx | 78 +- src/blocks/PromoFeaturesBlock/index.ts | 22 + src/blocks/Questions/Questions.tsx | 35 +- src/blocks/Questions/index.ts | 51 ++ src/blocks/Share/Share.tsx | 47 +- src/blocks/Share/index.ts | 20 + src/blocks/Slider/__tests__/Slider.test.tsx | 1 + src/blocks/Slider/dynamic-form.ts | 226 ++++++ src/blocks/Slider/index.ts | 16 + src/blocks/Table/index.ts | 16 + src/blocks/Table/schema.ts | 1 + src/blocks/Tabs/Tabs.tsx | 74 +- src/blocks/Tabs/index.ts | 31 + .../TestEditorBlock/TestEditorBlock.tsx | 13 + src/blocks/TestEditorBlock/form.ts | 114 +++ src/blocks/TestEditorBlock/index.ts | 16 + src/common/hooks/usePostMessage.tsx | 134 ++++ src/common/types/actions/codes.ts | 27 + src/common/types/actions/index.ts | 31 + src/common/types/actions/initial.ts | 29 + src/common/types/actions/insert.ts | 24 + src/common/types/actions/other.ts | 21 + src/common/types/actions/overlay.ts | 16 + src/common/types/actions/reorder.ts | 27 + src/common/types/actions/select.ts | 20 + src/common/types/common.ts | 17 + src/common/types/forms.ts | 110 +++ src/common/types/index.ts | 4 + src/common/types/messages.ts | 22 + src/components/BlockBase/BlockBase.scss | 8 +- src/components/Image/dynamic-form.ts | 87 +++ .../editor/ChildrenWrap/ChildrenWrap.scss | 8 + .../editor/ChildrenWrap/ChildrenWrap.tsx | 27 + src/components/editor/ItemWrap/ItemWrap.scss | 12 + src/components/editor/ItemWrap/ItemWrap.tsx | 27 + src/constructor-items.ts | 84 ++ .../PageConstructor/PageConstructor.tsx | 65 +- src/containers/PageConstructor/Provider.tsx | 4 + .../ConstructorBlock/ConstructorBlock.scss | 4 + .../ConstructorBlock/ConstructorBlock.tsx | 16 +- .../hooks/useEditorBlockMouseEvents.tsx | 130 ++++ .../ConstructorBlocks/ConstructorBlocks.tsx | 16 +- .../ConstructorItem/ConstructorItem.tsx | 4 +- .../ConstructorLoadable.tsx | 2 +- .../ConstructorRow/ConstructorRow.tsx | 7 +- src/context/blockIdContext/blockIdContext.ts | 4 +- src/context/editorContext/editorContext.ts | 18 + src/context/editorContext/editorProvider.tsx | 27 + .../editorContext/hooks/useEditorStore.ts | 17 + src/context/editorContext/index.ts | 4 + src/context/editorContext/store.ts | 68 ++ .../hooks/useMessageObserver.tsx | 23 + .../hooks/useMessageSender.tsx | 10 + .../hooks/useMessagesStore.tsx | 17 + src/context/messagesContext/index.ts | 5 + .../messagesContext/messagesContext.tsx | 21 + .../messagesContext/messagesProvider.tsx | 38 + src/context/messagesContext/store.ts | 44 ++ .../components/BigOverlay/BigOverlay.scss | 33 + .../components/BigOverlay/BigOverlay.tsx | 69 ++ .../components/BlockConfig/BlockConfig.scss | 28 + .../components/BlockConfig/BlockConfig.tsx | 53 ++ .../components/BlocksList/BlocksList.scss | 42 + .../components/BlocksList/BlocksList.tsx | 43 + .../components/DynamicForm/DynamicForm.scss | 8 + .../components/DynamicForm/DynamicForm.tsx | 219 ++++++ .../DynamicForm/FieldBase/FieldBase.scss | 72 ++ .../DynamicForm/FieldBase/FieldBase.tsx | 73 ++ .../DynamicForm/Fields/Array/Array.scss | 51 ++ .../DynamicForm/Fields/Array/Array.tsx | 158 ++++ .../Fields/Array/ItemButton/ItemButton.tsx | 77 ++ .../DynamicForm/Fields/Boolean/Boolean.tsx | 26 + .../DynamicForm/Fields/Number/Number.tsx | 30 + .../DynamicForm/Fields/Object/Object.scss | 10 + .../DynamicForm/Fields/Object/Object.tsx | 38 + .../DynamicForm/Fields/OneOf/OneOf.scss | 14 + .../DynamicForm/Fields/OneOf/OneOf.tsx | 78 ++ .../DynamicForm/Fields/Select/Select.tsx | 42 + .../DynamicForm/Fields/Text/Text.tsx | 26 + .../DynamicForm/Fields/TextArea/TextArea.tsx | 26 + src/editor-v2/components/DynamicForm/utils.ts | 23 + .../components/GlobalConfig/GlobalConfig.scss | 17 + .../components/GlobalConfig/GlobalConfig.tsx | 29 + .../components/MiddleScreen/MiddleScreen.scss | 52 ++ .../components/MiddleScreen/MiddleScreen.tsx | 52 ++ src/editor-v2/components/Overlay/Overlay.scss | 68 ++ src/editor-v2/components/Overlay/Overlay.tsx | 133 ++++ src/editor-v2/components/Sidebar/Sidebar.scss | 86 ++ src/editor-v2/components/Sidebar/Sidebar.tsx | 110 +++ src/editor-v2/components/Source/Source.scss | 18 + src/editor-v2/components/Source/Source.tsx | 53 ++ .../components/SourceCode/SourceCode.scss | 26 + .../components/SourceCode/SourceCode.tsx | 63 ++ src/editor-v2/components/TopBar/TopBar.scss | 34 + src/editor-v2/components/TopBar/TopBar.tsx | 25 + src/editor-v2/components/Tree/Tree.scss | 38 + src/editor-v2/components/Tree/Tree.tsx | 54 ++ .../components/ViewSwitches/ViewSwitches.scss | 12 + .../components/ViewSwitches/ViewSwitches.tsx | 50 ++ src/editor-v2/containers/Editor/Editor.scss | 57 ++ src/editor-v2/containers/Editor/Editor.tsx | 107 +++ .../Editor/hooks/useAdminInitialize.tsx | 39 + .../containers/__stories__/Editor.stories.tsx | 29 + .../containers/__stories__/data.json | 335 ++++++++ src/editor-v2/containers/__stories__/utils.ts | 22 + .../contentConfig/contentConfigContext.tsx | 15 + .../contentConfig/contentConfigProvider.tsx | 34 + .../hooks/useContentConfigStore.tsx | 15 + src/editor-v2/context/contentConfig/index.ts | 4 + src/editor-v2/context/contentConfig/store.ts | 202 +++++ .../context/editorContext/editorContext.tsx | 16 + .../context/editorContext/editorProvider.tsx | 20 + .../editorContext/hooks/useEditorStore.tsx | 17 + src/editor-v2/context/editorContext/index.ts | 4 + src/editor-v2/context/editorContext/store.ts | 101 +++ .../iframeContext/hooks/useIframeStore.tsx | 15 + .../context/iframeContext/iframeContext.tsx | 18 + .../context/iframeContext/iframeProvider.tsx | 36 + src/editor-v2/context/iframeContext/index.ts | 4 + src/editor-v2/context/iframeContext/store.ts | 34 + .../hooks/useMessageObserver.tsx | 23 + .../hooks/useMessageSender.tsx | 10 + .../hooks/useMessagesStore.tsx | 17 + .../context/messagesContext/index.ts | 5 + .../messagesContext/messagesContext.tsx | 21 + .../messagesContext/messagesProvider.tsx | 46 ++ .../context/messagesContext/store.ts | 44 ++ src/editor-v2/icons/Tablet.tsx | 23 + src/editor-v2/index.ts | 2 + src/editor-v2/styles/mixins.scss | 12 + src/editor-v2/styles/root.scss | 9 + src/editor-v2/styles/variables.scss | 5 + src/editor-v2/utils/code.ts | 12 + src/editor-v2/utils/index.ts | 168 ++++ src/editor-v2/utils/store.ts | 24 + .../data/templates/test-editor-block.json | 18 + src/grid/Grid/Grid.scss | 8 +- src/hooks/useAnalytics.ts | 2 +- src/hooks/useEditorInitialize.ts | 117 +++ .../NavigationItem/NavigationItem.tsx | 4 +- .../NavigationButton/NavigationButton.tsx | 4 +- src/navigation/containers/Layout/Layout.scss | 3 +- src/schema/index.ts | 17 +- src/sub-blocks/BackgroundCard/dynamic-form.ts | 81 ++ src/sub-blocks/BackgroundCard/index.tsx | 24 + src/sub-blocks/BannerCard/BannerCard.tsx | 4 +- src/sub-blocks/BannerCard/index.tsx | 25 + src/sub-blocks/BasicCard/index.tsx | 21 + src/sub-blocks/Content/index.tsx | 21 + src/sub-blocks/Content/schema.ts | 1 + src/sub-blocks/Divider/index.tsx | 18 + src/sub-blocks/ImageCard/ImageCard.tsx | 14 +- src/sub-blocks/ImageCard/index.tsx | 21 + src/sub-blocks/LayoutItem/LayoutItem.tsx | 2 +- src/sub-blocks/LayoutItem/form.ts | 16 + src/sub-blocks/LayoutItem/index.tsx | 23 + src/sub-blocks/LayoutItem/schema.ts | 2 + src/sub-blocks/MediaCard/index.tsx | 23 + src/sub-blocks/PriceCard/index.tsx | 34 + .../PriceDetailed/PriceDetailed.tsx | 2 +- src/sub-blocks/PriceDetailed/index.tsx | 38 + src/sub-blocks/Quote/index.tsx | 24 + src/utils/editor.ts | 20 + src/utils/form-generator.ts | 145 ++++ src/utils/store.ts | 22 + styles/mixins.scss | 18 +- 208 files changed, 9097 insertions(+), 391 deletions(-) create mode 100644 playground/.gitignore create mode 100644 playground/next.config.mjs create mode 100644 playground/package-lock.json create mode 100644 playground/package.json create mode 100644 playground/src/app/content.json create mode 100644 playground/src/app/favicon.ico create mode 100644 playground/src/app/layout.tsx create mode 100644 playground/src/app/page.scss create mode 100644 playground/src/app/page.tsx create mode 100644 playground/src/app/pc/page.tsx create mode 100644 playground/src/constants.ts create mode 100644 playground/src/styles/globals.scss create mode 100644 playground/tsconfig.json create mode 100644 src/blocks/Banner/index.tsx create mode 100644 src/blocks/CardLayout/index.ts create mode 100644 src/blocks/Companies/index.ts create mode 100644 src/blocks/ContentLayout/index.ts create mode 100644 src/blocks/ExtendedFeatures/index.ts create mode 100644 src/blocks/FilterBlock/index.ts create mode 100644 src/blocks/Form/index.ts create mode 100644 src/blocks/Header/dynamic-form.ts create mode 100644 src/blocks/Header/index.ts create mode 100644 src/blocks/HeaderSlider/index.ts create mode 100644 src/blocks/Icons/index.ts create mode 100644 src/blocks/Info/index.ts create mode 100644 src/blocks/Map/index.ts create mode 100644 src/blocks/Media/index.ts create mode 100644 src/blocks/PromoFeaturesBlock/index.ts create mode 100644 src/blocks/Questions/index.ts create mode 100644 src/blocks/Share/index.ts create mode 100644 src/blocks/Slider/dynamic-form.ts create mode 100644 src/blocks/Slider/index.ts create mode 100644 src/blocks/Table/index.ts create mode 100644 src/blocks/Tabs/index.ts create mode 100644 src/blocks/TestEditorBlock/TestEditorBlock.tsx create mode 100644 src/blocks/TestEditorBlock/form.ts create mode 100644 src/blocks/TestEditorBlock/index.ts create mode 100644 src/common/hooks/usePostMessage.tsx create mode 100644 src/common/types/actions/codes.ts create mode 100644 src/common/types/actions/index.ts create mode 100644 src/common/types/actions/initial.ts create mode 100644 src/common/types/actions/insert.ts create mode 100644 src/common/types/actions/other.ts create mode 100644 src/common/types/actions/overlay.ts create mode 100644 src/common/types/actions/reorder.ts create mode 100644 src/common/types/actions/select.ts create mode 100644 src/common/types/common.ts create mode 100644 src/common/types/forms.ts create mode 100644 src/common/types/index.ts create mode 100644 src/common/types/messages.ts create mode 100644 src/components/Image/dynamic-form.ts create mode 100644 src/components/editor/ChildrenWrap/ChildrenWrap.scss create mode 100644 src/components/editor/ChildrenWrap/ChildrenWrap.tsx create mode 100644 src/components/editor/ItemWrap/ItemWrap.scss create mode 100644 src/components/editor/ItemWrap/ItemWrap.tsx create mode 100644 src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx create mode 100644 src/context/editorContext/editorContext.ts create mode 100644 src/context/editorContext/editorProvider.tsx create mode 100644 src/context/editorContext/hooks/useEditorStore.ts create mode 100644 src/context/editorContext/index.ts create mode 100644 src/context/editorContext/store.ts create mode 100644 src/context/messagesContext/hooks/useMessageObserver.tsx create mode 100644 src/context/messagesContext/hooks/useMessageSender.tsx create mode 100644 src/context/messagesContext/hooks/useMessagesStore.tsx create mode 100644 src/context/messagesContext/index.ts create mode 100644 src/context/messagesContext/messagesContext.tsx create mode 100644 src/context/messagesContext/messagesProvider.tsx create mode 100644 src/context/messagesContext/store.ts create mode 100644 src/editor-v2/components/BigOverlay/BigOverlay.scss create mode 100644 src/editor-v2/components/BigOverlay/BigOverlay.tsx create mode 100644 src/editor-v2/components/BlockConfig/BlockConfig.scss create mode 100644 src/editor-v2/components/BlockConfig/BlockConfig.tsx create mode 100644 src/editor-v2/components/BlocksList/BlocksList.scss create mode 100644 src/editor-v2/components/BlocksList/BlocksList.tsx create mode 100644 src/editor-v2/components/DynamicForm/DynamicForm.scss create mode 100644 src/editor-v2/components/DynamicForm/DynamicForm.tsx create mode 100644 src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss create mode 100644 src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx create mode 100644 src/editor-v2/components/DynamicForm/Fields/Array/Array.scss create mode 100644 src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx create mode 100644 src/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx create mode 100644 src/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx create mode 100644 src/editor-v2/components/DynamicForm/Fields/Number/Number.tsx create mode 100644 src/editor-v2/components/DynamicForm/Fields/Object/Object.scss create mode 100644 src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx create mode 100644 src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss create mode 100644 src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx create mode 100644 src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx create mode 100644 src/editor-v2/components/DynamicForm/Fields/Text/Text.tsx create mode 100644 src/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx create mode 100644 src/editor-v2/components/DynamicForm/utils.ts create mode 100644 src/editor-v2/components/GlobalConfig/GlobalConfig.scss create mode 100644 src/editor-v2/components/GlobalConfig/GlobalConfig.tsx create mode 100644 src/editor-v2/components/MiddleScreen/MiddleScreen.scss create mode 100644 src/editor-v2/components/MiddleScreen/MiddleScreen.tsx create mode 100644 src/editor-v2/components/Overlay/Overlay.scss create mode 100644 src/editor-v2/components/Overlay/Overlay.tsx create mode 100644 src/editor-v2/components/Sidebar/Sidebar.scss create mode 100644 src/editor-v2/components/Sidebar/Sidebar.tsx create mode 100644 src/editor-v2/components/Source/Source.scss create mode 100644 src/editor-v2/components/Source/Source.tsx create mode 100644 src/editor-v2/components/SourceCode/SourceCode.scss create mode 100644 src/editor-v2/components/SourceCode/SourceCode.tsx create mode 100644 src/editor-v2/components/TopBar/TopBar.scss create mode 100644 src/editor-v2/components/TopBar/TopBar.tsx create mode 100644 src/editor-v2/components/Tree/Tree.scss create mode 100644 src/editor-v2/components/Tree/Tree.tsx create mode 100644 src/editor-v2/components/ViewSwitches/ViewSwitches.scss create mode 100644 src/editor-v2/components/ViewSwitches/ViewSwitches.tsx create mode 100644 src/editor-v2/containers/Editor/Editor.scss create mode 100644 src/editor-v2/containers/Editor/Editor.tsx create mode 100644 src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx create mode 100644 src/editor-v2/containers/__stories__/Editor.stories.tsx create mode 100644 src/editor-v2/containers/__stories__/data.json create mode 100644 src/editor-v2/containers/__stories__/utils.ts create mode 100644 src/editor-v2/context/contentConfig/contentConfigContext.tsx create mode 100644 src/editor-v2/context/contentConfig/contentConfigProvider.tsx create mode 100644 src/editor-v2/context/contentConfig/hooks/useContentConfigStore.tsx create mode 100644 src/editor-v2/context/contentConfig/index.ts create mode 100644 src/editor-v2/context/contentConfig/store.ts create mode 100644 src/editor-v2/context/editorContext/editorContext.tsx create mode 100644 src/editor-v2/context/editorContext/editorProvider.tsx create mode 100644 src/editor-v2/context/editorContext/hooks/useEditorStore.tsx create mode 100644 src/editor-v2/context/editorContext/index.ts create mode 100644 src/editor-v2/context/editorContext/store.ts create mode 100644 src/editor-v2/context/iframeContext/hooks/useIframeStore.tsx create mode 100644 src/editor-v2/context/iframeContext/iframeContext.tsx create mode 100644 src/editor-v2/context/iframeContext/iframeProvider.tsx create mode 100644 src/editor-v2/context/iframeContext/index.ts create mode 100644 src/editor-v2/context/iframeContext/store.ts create mode 100644 src/editor-v2/context/messagesContext/hooks/useMessageObserver.tsx create mode 100644 src/editor-v2/context/messagesContext/hooks/useMessageSender.tsx create mode 100644 src/editor-v2/context/messagesContext/hooks/useMessagesStore.tsx create mode 100644 src/editor-v2/context/messagesContext/index.ts create mode 100644 src/editor-v2/context/messagesContext/messagesContext.tsx create mode 100644 src/editor-v2/context/messagesContext/messagesProvider.tsx create mode 100644 src/editor-v2/context/messagesContext/store.ts create mode 100644 src/editor-v2/icons/Tablet.tsx create mode 100644 src/editor-v2/index.ts create mode 100644 src/editor-v2/styles/mixins.scss create mode 100644 src/editor-v2/styles/root.scss create mode 100644 src/editor-v2/styles/variables.scss create mode 100644 src/editor-v2/utils/code.ts create mode 100644 src/editor-v2/utils/index.ts create mode 100644 src/editor-v2/utils/store.ts create mode 100644 src/editor/data/templates/test-editor-block.json create mode 100644 src/hooks/useEditorInitialize.ts create mode 100644 src/sub-blocks/BackgroundCard/dynamic-form.ts create mode 100644 src/sub-blocks/BackgroundCard/index.tsx create mode 100644 src/sub-blocks/BannerCard/index.tsx create mode 100644 src/sub-blocks/BasicCard/index.tsx create mode 100644 src/sub-blocks/Content/index.tsx create mode 100644 src/sub-blocks/Divider/index.tsx create mode 100644 src/sub-blocks/ImageCard/index.tsx create mode 100644 src/sub-blocks/LayoutItem/form.ts create mode 100644 src/sub-blocks/LayoutItem/index.tsx create mode 100644 src/sub-blocks/MediaCard/index.tsx create mode 100644 src/sub-blocks/PriceCard/index.tsx create mode 100644 src/sub-blocks/PriceDetailed/index.tsx create mode 100644 src/sub-blocks/Quote/index.tsx create mode 100644 src/utils/editor.ts create mode 100644 src/utils/form-generator.ts create mode 100644 src/utils/store.ts diff --git a/.gitignore b/.gitignore index ada2d4bd1d..d8ccc7731d 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,8 @@ node_modules playwright-report* /blob-report/ .cache* + +# Playground +/playground/node_modules +/playground/.next +/playground/.env diff --git a/.prettierignore b/.prettierignore index c31cfb90ac..c837294137 100644 --- a/.prettierignore +++ b/.prettierignore @@ -15,8 +15,12 @@ CONTRIBUTING.md /widget /schema + /playwright/playwright/.cache/ playwright # npm files -package.json \ No newline at end of file +package.json + +# Playground +/playground/.next diff --git a/README.md b/README.md index 4bf2342cbb..9562604a63 100644 --- a/README.md +++ b/README.md @@ -540,7 +540,7 @@ If you want to release a new version in previous major after commit it to the ma 7. Check your changes in CHANGELOG.md and approve robot's PR. 8. Squash and merge PR. You can see release process on [the Actions tab](https://github.com/gravity-ui/page-constructor/actions). -## Page constructor editor +## Page Constructor Editor v1 Editor provides user interface for page content management with realtime preview. @@ -593,6 +593,36 @@ When working with AI agents on this project, the Memory Bank serves as a compreh AI agents can read these files to quickly get up to speed with the project context and make more informed decisions about code changes and implementations. +## Page Constructor Editor v2 + +Editor provides user interface for page content management with realtime preview. + +Based on Iframe postMessage communication. + +How to use: + +```tsx +import {Editor} from '@gravity-ui/page-constructor/editor-v2'; + +export const MyAppEditor = ({initialContent, onUpdate, disableUrlField}: MyAppEditorProps) => ( + +); +``` + +### How to develop + +```shell +npm run deps:install +npm run dev:playground +``` + +Directory `/playground` contains NextJS service with integrated PC and Editor for development purposes. + ## Tests Comprehensive documentation is available at the provided [link](./test-utils/docs/README.md). diff --git a/package-lock.json b/package-lock.json index a7a274cde3..856ca931ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,12 +18,14 @@ "ajv-keywords": "^5.1.0", "final-form": "^4.20.9", "github-buttons": "2.23.0", + "immutable": "^4.3.7", "js-yaml-source-map": "^0.2.2", "lodash": "^4.17.21", "monaco-editor": "^0.52.2", "react-final-form": "^6.5.9", "react-monaco-editor": "^0.53.0", "react-player": "^2.9.0", + "react-resizable-panels": "^2.1.3", "react-slick": "^0.29.0", "react-transition-group": "^4.4.2", "react-waypoint": "^10.1.0", @@ -32,7 +34,8 @@ "swiper": "^10.2.0", "typograf": "^7.4.1", "utility-types": "^3.10.0", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "zustand": "^4.5.2" }, "devDependencies": { "@babel/core": "^7.22.8", @@ -104,6 +107,7 @@ "lint-staged": "^11.2.6", "monaco-editor-webpack-plugin": "^7.1.0", "move-file-cli": "^3.0.0", + "next": "^14.2.3", "npm-run-all": "^4.1.5", "postcss": "^8.4.16", "postcss-loader": "^4.3.0", @@ -5709,6 +5713,156 @@ "@tybys/wasm-util": "^0.9.0" } }, + "node_modules/@next/env": { + "version": "14.2.24", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.24.tgz", + "integrity": "sha512-LAm0Is2KHTNT6IT16lxT+suD0u+VVfYNQqM+EJTKuFRRuY2z+zj01kueWXPCxbMBDt0B5vONYzabHGUNbZYAhA==", + "dev": true + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.24", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.24.tgz", + "integrity": "sha512-7Tdi13aojnAZGpapVU6meVSpNzgrFwZ8joDcNS8cJVNuP3zqqrLqeory9Xec5TJZR/stsGJdfwo8KeyloT3+rQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.24", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.24.tgz", + "integrity": "sha512-lXR2WQqUtu69l5JMdTwSvQUkdqAhEWOqJEYUQ21QczQsAlNOW2kWZCucA6b3EXmPbcvmHB1kSZDua/713d52xg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.24", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.24.tgz", + "integrity": "sha512-nxvJgWOpSNmzidYvvGDfXwxkijb6hL9+cjZx1PVG6urr2h2jUqBALkKjT7kpfurRWicK6hFOvarmaWsINT1hnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.24", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.24.tgz", + "integrity": "sha512-PaBgOPhqa4Abxa3y/P92F3kklNPsiFjcjldQGT7kFmiY5nuFn8ClBEoX8GIpqU1ODP2y8P6hio6vTomx2Vy0UQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.24", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.24.tgz", + "integrity": "sha512-vEbyadiRI7GOr94hd2AB15LFVgcJZQWu7Cdi9cWjCMeCiUsHWA0U5BkGPuoYRnTxTn0HacuMb9NeAmStfBCLoQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.24", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.24.tgz", + "integrity": "sha512-df0FC9ptaYsd8nQCINCzFtDWtko8PNRTAU0/+d7hy47E0oC17tI54U/0NdGk7l/76jz1J377dvRjmt6IUdkpzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.24", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.24.tgz", + "integrity": "sha512-ZEntbLjeYAJ286eAqbxpZHhDFYpYjArotQ+/TW9j7UROh0DUmX7wYDGtsTPpfCV8V+UoqHBPU7q9D4nDNH014Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.24", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.24.tgz", + "integrity": "sha512-9KuS+XUXM3T6v7leeWU0erpJ6NsFIwiTFD5nzNg8J5uo/DMIPvCp3L1Ao5HjbHX0gkWPB1VrKoo/Il4F0cGK2Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.24", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.24.tgz", + "integrity": "sha512-cXcJ2+x0fXQ2CntaE00d7uUH+u1Bfp/E0HsNQH79YiLaZE5Rbm7dZzyAYccn3uICM7mw+DxoMqEfGXZtF4Fgaw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -7392,6 +7546,22 @@ "@svgr/core": "*" } }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "dev": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, "node_modules/@tanstack/react-virtual": { "version": "3.13.9", "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.9.tgz", @@ -10264,6 +10434,18 @@ "dev": true, "license": "MIT" }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dev": true, + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -10660,6 +10842,62 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-truncate/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cli-truncate/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-truncate/node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "dev": true + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -16343,8 +16581,7 @@ "node_modules/immutable": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", - "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "dev": true + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==" }, "node_modules/import-fresh": { "version": "3.3.1", @@ -21671,6 +21908,56 @@ "dev": true, "license": "MIT" }, + "node_modules/next": { + "version": "14.2.24", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.24.tgz", + "integrity": "sha512-En8VEexSJ0Py2FfVnRRh8gtERwDRaJGNvsvad47ShkC2Yi8AXQPXEA2vKoDJlGFSj5WE5SyF21zNi4M5gyi+SQ==", + "dev": true, + "dependencies": { + "@next/env": "14.2.24", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.24", + "@next/swc-darwin-x64": "14.2.24", + "@next/swc-linux-arm64-gnu": "14.2.24", + "@next/swc-linux-arm64-musl": "14.2.24", + "@next/swc-linux-x64-gnu": "14.2.24", + "@next/swc-linux-x64-musl": "14.2.24", + "@next/swc-win32-arm64-msvc": "14.2.24", + "@next/swc-win32-ia32-msvc": "14.2.24", + "@next/swc-win32-x64-msvc": "14.2.24" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, "node_modules/next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -21678,6 +21965,34 @@ "dev": true, "license": "MIT" }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -24481,6 +24796,15 @@ "node": ">=0.10.0" } }, + "node_modules/react-resizable-panels": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.1.7.tgz", + "integrity": "sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA==", + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/react-select": { "version": "5.10.1", "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.1.tgz", @@ -26289,57 +26613,6 @@ "node": ">=8" } }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/slugify": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", @@ -26580,6 +26853,15 @@ "dev": true, "license": "MIT" }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/streamx": { "version": "2.22.0", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", @@ -27084,6 +27366,29 @@ "dev": true, "license": "ISC" }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dev": true, + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/stylelint": { "version": "15.11.0", "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.11.0.tgz", @@ -28940,6 +29245,14 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -30393,6 +30706,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zustand": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz", + "integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index 6f900647b0..e789b008d5 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,11 @@ "require": "./build/cjs/editor/index.js", "import": "./build/esm/editor/index.js" }, + "./editor-v2": { + "types": "./build/esm/editor-v2/index.d.ts", + "require": "./build/cjs/editor-v2/index.js", + "import": "./build/esm/editor-v2/index.js" + }, "./server": { "types": "./server/index.d.ts", "require": "./server/index.js", @@ -61,7 +66,9 @@ "*.scss" ], "scripts": { - "deps:install": "npm ci", + "deps:install:playground": "cd playground && npm ci", + "deps:install:package": "npm ci", + "deps:install": "run-p deps:install:playground deps:install:package", "deps:truncate": "npm prune --production", "lint:fix": "run-s lint:js:fix lint:styles:fix lint:prettier:fix typecheck", "lint:js": "eslint '**/*.{js,jsx,ts,tsx}' --max-warnings=0", @@ -73,6 +80,7 @@ "lint": "run-p lint:js lint:styles lint:prettier typecheck", "typecheck": "tsc --noEmit", "dev": "npm run storybook:start", + "dev:website": "next dev playground", "storybook:start": "storybook dev -p 7009", "storybook:build": "storybook build -c .storybook -o storybook-static", "start": "node dist", @@ -105,12 +113,14 @@ "ajv-keywords": "^5.1.0", "final-form": "^4.20.9", "github-buttons": "2.23.0", + "immutable": "^4.3.7", "js-yaml-source-map": "^0.2.2", "lodash": "^4.17.21", "monaco-editor": "^0.52.2", "react-final-form": "^6.5.9", "react-monaco-editor": "^0.53.0", "react-player": "^2.9.0", + "react-resizable-panels": "^2.1.3", "react-slick": "^0.29.0", "react-transition-group": "^4.4.2", "react-waypoint": "^10.1.0", @@ -119,7 +129,8 @@ "swiper": "^10.2.0", "typograf": "^7.4.1", "utility-types": "^3.10.0", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "zustand": "^4.5.2" }, "peerDependencies": { "@diplodoc/transform": "^4.28.2", @@ -196,6 +207,7 @@ "lint-staged": "^11.2.6", "monaco-editor-webpack-plugin": "^7.1.0", "move-file-cli": "^3.0.0", + "next": "^14.2.3", "npm-run-all": "^4.1.5", "postcss": "^8.4.16", "postcss-loader": "^4.3.0", diff --git a/playground/.gitignore b/playground/.gitignore new file mode 100644 index 0000000000..583394ff25 --- /dev/null +++ b/playground/.gitignore @@ -0,0 +1,28 @@ +# dependencies +/node_modules + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/playground/next.config.mjs b/playground/next.config.mjs new file mode 100644 index 0000000000..4678774e6d --- /dev/null +++ b/playground/next.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; diff --git a/playground/package-lock.json b/playground/package-lock.json new file mode 100644 index 0000000000..afdffac322 --- /dev/null +++ b/playground/package-lock.json @@ -0,0 +1,478 @@ +{ + "name": "page-constructor-example", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "page-constructor-example", + "version": "1.0.0", + "dependencies": { + "bem-cn-lite": "^4.1.0", + "next": "14.2.3", + "react": "^18", + "react-dom": "^18" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "typescript": "^5" + } + }, + "node_modules/@next/env": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz", + "integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz", + "integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz", + "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz", + "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz", + "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz", + "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz", + "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz", + "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz", + "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz", + "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@types/node": { + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.3.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.2.tgz", + "integrity": "sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/bem-cn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bem-cn/-/bem-cn-3.0.1.tgz", + "integrity": "sha512-kWC76a09vSk6cJXDYsH1erjxdBf856HTxl0IHOvYItSmBC6wQCsRCf9bmKR0hmeUDcUP5XPMr8MNXDgKbKJi0A==" + }, + "node_modules/bem-cn-lite": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bem-cn-lite/-/bem-cn-lite-4.1.0.tgz", + "integrity": "sha512-0IEVRYK2MQKQO00P3sY3hNv7vH8P+Z8mR46qFcaiwsQAWp0MuMWpLSuUUhZEjKD2HzTGXMqMsFysWEeeJa1drQ==", + "dependencies": { + "bem-cn": "^3.0.1" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001618", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001618.tgz", + "integrity": "sha512-p407+D1tIkDvsEAPS22lJxLQQaG8OTBEqo0KhzfABGk0TU4juBNDSfH0hyAp/HRyx+M8L17z/ltyhxh27FTfQg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz", + "integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==", + "dependencies": { + "@next/env": "14.2.3", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.3", + "@next/swc-darwin-x64": "14.2.3", + "@next/swc-linux-arm64-gnu": "14.2.3", + "@next/swc-linux-arm64-musl": "14.2.3", + "@next/swc-linux-x64-gnu": "14.2.3", + "@next/swc-linux-x64-musl": "14.2.3", + "@next/swc-win32-arm64-msvc": "14.2.3", + "@next/swc-win32-ia32-msvc": "14.2.3", + "@next/swc-win32-x64-msvc": "14.2.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + } + } +} diff --git a/playground/package.json b/playground/package.json new file mode 100644 index 0000000000..2ce85b873d --- /dev/null +++ b/playground/package.json @@ -0,0 +1,23 @@ +{ + "name": "page-constructor-playground", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "bem-cn-lite": "^4.1.0", + "next": "14.2.3", + "react": "^18", + "react-dom": "^18" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "typescript": "^5" + } +} diff --git a/playground/src/app/content.json b/playground/src/app/content.json new file mode 100644 index 0000000000..396e3394e0 --- /dev/null +++ b/playground/src/app/content.json @@ -0,0 +1,335 @@ +{ + "blocks": [ + { + "type": "header-block", + "title": "Yandex Open Source", + "description": "

Мы в Яндексе верим, что вклад в опенсорс — это вклад в технологическую эволюцию: без открытости, совместной работы и поддержки развитие IT‑индустрии сильно затруднено. Уже много лет мы используем в своих продуктах сторонние открытые технологии, а также делимся собственными и активно вовлекаем в их развитие разработчиков по всему миру.

", + "width": "s", + "verticalOffset": "l", + "offset": "default", + "resetPaddings": true, + "background": { + "image": { + "mobile": "https://storage.yandexcloud.net/yandex-opensource/pages/index/yos-index-cover-m.png", + "desktop": "https://storage.yandexcloud.net/yandex-opensource/pages/index/yos-index-cover.png" + }, + "color": "#EFF2F8", + "fullWidth": false, + "fullWidthMedia": true + } + }, + { + "type": "extended-features-block", + "title": { + "text": "Почему мы выкладываем наши технологии в открытый доступ?" + }, + "items": [ + { + "title": "Ответственность", + "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-01.svg", + "text": "

Мы верим, что вкладываться в развитие опенсорс‑технологий — это ответственность каждого технологического лидера на рынке. Без опенсорс‑решений не появились бы многие продукты и сервисы не только Яндекса, но и других крупных компаний, и мы хотим отдавать обратно, делиться теми нашими решениями, которые, как мы считаем, принесут реальную пользу.

" + }, + { + "title": "Польза для сообщества", + "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-02.svg", + "text": "

Технологии, которые мы разрабатываем, ежедневно помогают нам эффективно решать огромное количество самых разных задач в наших сервисах. Мы знаем, что разработчики вне Яндекса часто сталкиваются с теми же самыми задачами — и верим, что наши технологии могут быть полезны и им.

" + }, + { + "title": "Качество сервисов", + "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-03.svg", + "text": "

Для нас важно разрабатывать и использовать только качественные технологические решения. В особенности это касается опенсорса: зная, что наши решения увидят и будут использовать другие, мы уделяем их качеству особое внимание. А уже в открытом доступе у технологии больше шансов развиваться и улучшаться — в том числе, при участии сообщества разработчиков.

" + }, + { + "title": "Бизнес‑потенциал", + "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-04.svg", + "text": "

Мы верим, что при условии роста популярности наших решений и спроса на них со стороны сообщества, то, что мы выкладываем в опенсорс, может далее стать для нас бизнесом. То, что мы выкладываем в опенсорс, можно использовать и во внешних коммерческих проектах.

" + }, + { + "title": "Поиск талантов", + "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-05.svg", + "text": "

Мы ценим каждого, кто вкладывается в сторонние опенсорс‑решения или делится с миром своими. Контрибьюторы в наши продукты нам особенно важны: среди них мы ищем и находим тех, кто сможет развивать технологии уже будучи частью команды Яндекса.

" + } + ] + }, + { + "type": "card-layout-block", + "animated": false, + "title": "Краткая история опенсорса в Яндексе", + "description": "

С начала истории развития опенсорса в Яндексе мы успели выложить в открытый доступ десятки собственных проектов, использовать в разработке наших продуктов внешние технологии, а также внесли существенный вклад в их развитие.

", + "colSizes": { + "all": 12, + "lg": 3, + "md": 4, + "sm": 6 + }, + "anchor": { + "url": "history", + "text": "history" + }, + "children": [ + { + "type": "layout-item", + "content": { + "title": "2010", + "text": "

Методология веб‑разработки БЭМ (Блок‑Элемент‑Модификатор) выходит в оперсорс

" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-01.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2012", + "text": "

Запуск Яндекс Браузера на базе Blink (Chromium)

" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-02.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2013", + "text": "

Яндекс начинает контрибьютить в ядро Linux

" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-03.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2016", + "text": "

Выход в опенсорс ClickHouse

\\n

Выход в опенсорс Hermione (с 2024 года — Testplane)

" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-2016.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2017", + "text": "

Выход в опенсорс CatBoost
\\nЯндекс начинает контрибьютить в PostgreSQL

" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-05.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2018", + "text": "

Выход в опенсорс Одиссея

\\n

Яндекс — топ‑контрибьютор в WAL‑G

" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-06.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2019", + "text": "

В Яндексе появляется команда разработки СУБД с открытым исходным кодом

\\n

Яндекс — спонсор разработки PostgreSQL

" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-07.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2020", + "text": "

Выход в опенсорс Testsuite

" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-2020.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2022", + "text": "

Яндекс — один из основных спонсоров разработки PostgreSQL

\\n

Выход в опенсорс YDB, userver, YaLM 100B, DivKit, Yatagan

\\n

Старт программы «Код для всех»

" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-09.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2023", + "text": "

Выход в опенсорс YTsaurus, Gravity UI, AppMetrica, Diplodoc, DataLens и счётчика Метрики

\\n

Старт Программы грантов Yandex Open Source

" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-2023.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2024", + "text": "

Первый Yandex Open Source Jam

\\n

Выход в опенсорс YaFSDP

" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-2024.png" + } + } + ] + }, + { + "type": "card-layout-block", + "title": "Наши проекты", + "description": "

В Яндексе мы разрабатываем и развиваем технологические решения самых разных сфер применения, размеров и сложности. Поэтому и в открытый доступ попадают самые разные проекты — главное, чтобы они приносили пользу не только нам, но и другим.

", + "colSizes": { + "all": 12, + "lg": 3, + "md": 4, + "sm": 6 + }, + "anchor": { + "url": "projects", + "text": "projects" + }, + "children": [ + { + "type": "background-card", + "title": "YDB", + "text": "

Отказоустойчивая распределённая SQL база данных

", + "backgroundColor": "#2399FF", + "background": { + "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-ydb.png", + "alt": "card-background" + }, + "paddingBottom": "m", + "theme": "dark" + }, + { + "type": "background-card", + "title": "YTsaurus", + "text": "

Платформа для хранения и обработки больших данных

", + "backgroundColor": "#FFB23E", + "background": { + "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-yt.png", + "alt": "card-background" + }, + "paddingBottom": "m", + "theme": "light" + }, + { + "type": "background-card", + "title": "GravityUI", + "text": "

Библиотеки для создания интерфейсов

", + "backgroundColor": "#262626", + "background": { + "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-gui.png", + "alt": "card-background" + }, + "paddingBottom": "m", + "theme": "dark" + }, + { + "type": "background-card", + "title": "DivKit", + "text": "

Фреймворк для server‑driven интерфейсов

", + "backgroundColor": "#F1F1F1", + "background": { + "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-dk.png", + "alt": "card-background" + }, + "paddingBottom": "m", + "theme": "light" + }, + { + "type": "background-card", + "title": "Diplodoc", + "text": "

Платформа для написания документации в концепции Docs as Code

", + "backgroundColor": "#79F985", + "background": { + "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-dd.png", + "alt": "card-background" + }, + "paddingBottom": "m", + "theme": "light" + }, + { + "type": "background-card", + "title": "userver", + "text": "

Фреймворк для создания высоконагруженных приложений

", + "backgroundColor": "#FF9D73", + "background": { + "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-u.png", + "alt": "card-background" + }, + "paddingBottom": "m", + "theme": "light" + }, + { + "type": "background-card", + "title": "DataLens", + "text": "

BI-платформа для анализа и визуализации данных

", + "backgroundColor": "#FF7132", + "background": { + "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-dl.png", + "alt": "card-background" + }, + "paddingBottom": "m", + "theme": "dark" + }, + { + "type": "basic-card", + "title": "И это ещё не всё", + "text": "

Узнать про наши опенсорс‑проекты больше вы можете на другой странице

", + "border": "none", + "buttons": [ + { + "text": "Все проекты", + "primary": true, + "theme": "monochrome", + "size": "promo", + "url": "/projects" + } + ] + } + ] + }, + { + "largeMedia": true, + "mediaOnly": false, + "size": "l", + "type": "media-block", + "direction": "content-media", + "anchor": { + "url": "1", + "text": "Фильм" + }, + "title": "

Смотрите фильм с YaC 22

", + "description": "

Руководители опенсорс‑проектов рассказывают про историю и культуру открытого кода в Яндексе.

", + "media": { + "youtube": "https://www.youtube.com/watch?v=G7G286S8ntc", + "previewImg": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/video-cover.png" + }, + "disableShadow": true + }, + { + "type": "content-layout-block", + "size": "l", + "centered": true, + "textContent": { + "title": "", + "text": "", + "additionalInfo": "", + "buttons": [ + { + "text": "Сделано на GravityUI", + "theme": "monochrome", + "img": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/open-source-gravity-ui-button.svg", + "url": "https://gravity-ui.com/" + } + ] + } + } + ] +} diff --git a/playground/src/app/favicon.ico b/playground/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..74cc5d2cdbf7a60dc986d8bcdf7a7080882dcc19 GIT binary patch literal 1732 zcmV;#20QtQP)5>=%wn{M|7ezpf!Ke|u1Y*L)2P0}k6zV0Cfarq>7^v~V zAP;CH+6WP13}QkGFDRmIK}2kWUDz&cVcTvmyPfXr?D0P{+tO~QbcO!O$?lw)bN=sq z|Mf8BC_WO!aIFC3#f+hfa|9Soi+3D|(Oi(`8tL(bF2|3=NOIYijjgyeprqtY^yRt% zg}DfU+OO-z2Qe)Y#O(kuPn`B_V;q}s2f`Wl+~^L!iT?vYoLV*HI{F*H_((9R8%yoj zkg_NIo+(x1tXxPyRpdIcclL{64CHe1g(Zb}!0$!XgnSe_9p>Dno<5wo+KFG=x)IfM z+(Je$!EP$|`BcNxFq-mb10~pa?^Mk2I{`HXC~4v^ZS6pYhQW~RXE@ZHW3idrb0}qvZfIK>#I|(>50AvAx z{5>rIkTsLUNWTN&Xc~=IH7xS_;WgYqLdDT=6zk8QG4~wmm~063E!oOTTq?d@R*8zK zv~fW{;1PoWxCn@10FJcvp#Y%ErgHQtG*lo?qnFr<)96)kxFw30&o>P2O{SP8Oko;P zG34zwtfOY(bW3@mMx&2FXlU;0!-2Xu>ikEu;|!t*-K*N3)3XU&{x=^Ji%)XIv-A%w#WOtoxqNKoy zy+?~m>=!UzB~cs%MgS-+v}hEbPUAwWKBjU99rob+`9uK$&@APc83CiHDKBu78k?-o zSwOO@aFQ^Lu%Yl~iuJY_9}_^$t9dBPbDN>pEeKoL2=3Lu&2Tl~lU2KAKFMyD3nL1^ z%=#_!-@0mVdESZV=6Fnx?};kddb-67U30nc17?yiNi-ALlNUPUHyw$_-06-{ zmA|vfh3Y4~KxZEGb{E!u69!6qEHh|y>V5@ni6m0?;S&>17|fn+i1u0@oJk}vEq9?H zZ#WBXC&RwYd3fvb0xRCT+<4_+1gEaVQT=wW3CPL=PSnS`?9nqGlo(5&&7MCV?nHNj zTUm_qdm-6x#~SzG`vo%{_;E)*o~j__oo;ARH#XEp@WaI}YA& zXvZ~s0Gu^CIHVJFwsk}2yOJqyQ<=adHJZAteTdoDpxFT?{JznUvr=p zzl7r0{B94bU+)M1hr%vY1cZIK5|&UKYq8f){iB(RDI2zAM?;}1__? zM5TNF(3kxNM};pRGmFWVqH#(|R<+YIRgMtLs~hm-+D<&SfT6@7x-a>G<`NSCYo3T* z4I%7pZ^9_DzrFiLt?Ayf!`PEH^k!13l8J;g{a1;NifiD_ek;tV-kT2B zMm`ZH>VtP-*SX8s+4k3H${|t5EPl;hBe6)(pFg#RF+K?5k&+20V|1!S%A#D()PCWZ z#T2H0PPR-U!1Xz->PJo&3dz`fu^uOTBcm#(h^&cZ+Ooj_*By@Ss*!w}xDANs`#nWg zV_{hJ{8UE=_FfEQxvLzN#f7LW%m*VE7OhxYLdWT73`edv;ivA_(PDl;eNcBKwhufF z3+__tjM9cwCqR_D^00g6EL=AM2 z5y!loT5On2>pJ8pW`X1QQcf9rBDZ@v%@*r=?pZ+#mT=KUvEkQqv#2ridVZ3w=ZAB> ap8p42@17$8jUv) { + return ( + + {children} + + ); +} diff --git a/playground/src/app/page.scss b/playground/src/app/page.scss new file mode 100644 index 0000000000..213d4b128a --- /dev/null +++ b/playground/src/app/page.scss @@ -0,0 +1,5 @@ +$block: '.home'; + +#{$block} { + height: 100vh; +} diff --git a/playground/src/app/page.tsx b/playground/src/app/page.tsx new file mode 100644 index 0000000000..e641a8a5fd --- /dev/null +++ b/playground/src/app/page.tsx @@ -0,0 +1,36 @@ +'use client'; +import {ThemeProvider} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; +import React, {useEffect, useState} from 'react'; + +import {Editor} from '../../../src/editor-v2'; + +import content from './content.json'; +import './page.scss'; + +const b = block('home'); + +export default function Home() { + const [initialUrl, setInitialUrl] = useState(''); + + useEffect(() => { + if (typeof window !== 'undefined') { + setInitialUrl(window.location.origin + '/pc'); + } + }, []); + + return ( + +
+ {initialUrl && ( + {}} + /> + )} +
+
+ ); +} diff --git a/playground/src/app/pc/page.tsx b/playground/src/app/pc/page.tsx new file mode 100644 index 0000000000..e208d76c6b --- /dev/null +++ b/playground/src/app/pc/page.tsx @@ -0,0 +1,13 @@ +'use client'; +import React from 'react'; + +import {PageConstructor, PageConstructorProvider} from '../../../../src'; +import content from '../content.json'; + +export default function Home() { + return ( + + + + ); +} diff --git a/playground/src/constants.ts b/playground/src/constants.ts new file mode 100644 index 0000000000..428aee7cb7 --- /dev/null +++ b/playground/src/constants.ts @@ -0,0 +1,4 @@ +import {Theme} from '@gravity-ui/uikit'; + +export const DEFAULT_THEME: Theme = 'light'; +export const DEFAULT_BODY_CLASSNAME = `g-root g-root_theme_${DEFAULT_THEME}`; diff --git a/playground/src/styles/globals.scss b/playground/src/styles/globals.scss new file mode 100644 index 0000000000..9fb6a01e75 --- /dev/null +++ b/playground/src/styles/globals.scss @@ -0,0 +1,6 @@ +@import '../../../styles/styles.scss'; + +html, +body { + margin: 0; +} diff --git a/playground/tsconfig.json b/playground/tsconfig.json new file mode 100644 index 0000000000..5912c19745 --- /dev/null +++ b/playground/tsconfig.json @@ -0,0 +1,32 @@ +{ + "extends": "@gravity-ui/tsconfig/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "outDir": "build/esm", + "module": "esnext", + "jsx": "preserve", + "baseUrl": ".", + "target": "ES2020", + "resolveJsonModule": true, + "importHelpers": true, + "lib": ["dom", "dom.iterable", "esnext"], + "noEmit": true, + "incremental": true, + "isolatedModules": true, + "plugins": [ + { + "name": "next" + } + ] + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + "src/typings/*.d.ts", + ".next/types/**/*.ts", + "../src", + "../styles" + ], + "exclude": ["node_modules"] +} diff --git a/src/blocks/Banner/Banner.tsx b/src/blocks/Banner/Banner.tsx index 89a7906fc7..53b3a13162 100644 --- a/src/blocks/Banner/Banner.tsx +++ b/src/blocks/Banner/Banner.tsx @@ -1,4 +1,5 @@ import AnimateBlock from '../../components/AnimateBlock/AnimateBlock'; +import {Grid} from '../../grid'; import {BannerBlockProps} from '../../models'; import {BannerCard} from '../../sub-blocks'; import {block} from '../../utils'; @@ -12,7 +13,9 @@ export const BannerBlock = (props: BannerBlockProps) => { return ( - + + + ); }; diff --git a/src/blocks/Banner/index.tsx b/src/blocks/Banner/index.tsx new file mode 100644 index 0000000000..928df06605 --- /dev/null +++ b/src/blocks/Banner/index.tsx @@ -0,0 +1,25 @@ +import {JSONSchemaType} from 'ajv'; + +import {BlockData} from '../../constructor-items'; +import {generateFromAJV} from '../../utils/form-generator'; + +import BannerBlock from './Banner'; +import {BannerCardProps} from './schema'; + +const BannerBlockConfig: BlockData = { + component: BannerBlock, + schema: { + name: 'Banner Block', + inputs: generateFromAJV(BannerCardProps as unknown as JSONSchemaType<{}>), + default: { + color: 'rgba(54, 151, 241, 0.4)', + title: 'Banner Block', + subtitle: 'Some sort of description.', + button: { + text: 'Read more', + }, + }, + }, +}; + +export default BannerBlockConfig; diff --git a/src/blocks/CardLayout/CardLayout.scss b/src/blocks/CardLayout/CardLayout.scss index 404d6d22bf..8b46ad619a 100644 --- a/src/blocks/CardLayout/CardLayout.scss +++ b/src/blocks/CardLayout/CardLayout.scss @@ -35,6 +35,7 @@ $largeBorderRadius: 32px; width: 100%; height: 100%; border-radius: $largeBorderRadius; + pointer-events: none; img { object-fit: cover; diff --git a/src/blocks/CardLayout/CardLayout.tsx b/src/blocks/CardLayout/CardLayout.tsx index ad6294dfc2..dac709f3b6 100644 --- a/src/blocks/CardLayout/CardLayout.tsx +++ b/src/blocks/CardLayout/CardLayout.tsx @@ -3,6 +3,8 @@ import * as React from 'react'; import isEmpty from 'lodash/isEmpty'; import {AnimateBlock, BackgroundImage, Title} from '../../components'; +import ChildrenWrap from '../../components/editor/ChildrenWrap/ChildrenWrap'; +import ItemWrap from '../../components/editor/ItemWrap/ItemWrap'; import {useTheme} from '../../context/theme'; import {Col, GridColumnSizesType, GridJustifyContent, Row} from '../../grid'; import {CardLayoutBlockProps as CardLayoutBlockParams, ClassNameProps} from '../../models'; diff --git a/src/blocks/CardLayout/index.ts b/src/blocks/CardLayout/index.ts new file mode 100644 index 0000000000..b600e79dd5 --- /dev/null +++ b/src/blocks/CardLayout/index.ts @@ -0,0 +1,170 @@ +import {ConfigInput} from '../../common/types'; +import {BlockData} from '../../constructor-items'; +import {sliderSizesArray, textSize} from '../../schema/validators/common'; + +import CardLayout from './CardLayout'; + +const textSizeEnum = textSize.map((size) => ({value: size, content: size})); + +export const blockConfig: ConfigInput[] = [ + { + type: 'oneOf', + name: 'title', + title: 'Title Object', + options: [ + { + title: 'Simple', + value: 'simple', + properties: [ + { + type: 'text', + name: '', + title: 'Title', + }, + ], + }, + { + title: 'Complex', + value: 'complex', + properties: [ + { + type: 'text', + name: 'text', + title: 'Title', + }, + { + type: 'select', + name: 'textSize', + title: 'Text Size', + enum: textSizeEnum, + view: 'select', + mode: 'single', + }, + { + type: 'text', + name: 'url', + title: 'Url', + }, + { + type: 'text', + name: 'urlTitle', + title: 'Url', + }, + { + type: 'boolean', + name: 'resetMargin', + title: 'Reset Margin', + }, + ], + }, + ], + }, + { + type: 'textarea', + name: 'description', + title: 'Description', + }, + { + type: 'boolean', + name: 'dots', + title: 'With dots', + }, + { + type: 'boolean', + name: 'arrows', + title: 'With Arrows', + }, + { + type: 'boolean', + name: 'randomOrder', + title: 'Random Order', + }, + { + type: 'number', + name: 'autoplay', + title: 'Autoplay', + }, + { + type: 'object', + name: 'disclaimer', + title: 'Disclaimer', + properties: [ + { + type: 'text', + name: 'text', + title: 'Text', + }, + { + type: 'select', + name: 'size', + title: 'Size', + enum: textSizeEnum, + view: 'select', + mode: 'single', + }, + ], + }, + { + type: 'oneOf', + name: 'slidesToShow', + title: 'Slides to Show', + options: [ + { + title: 'Simple', + value: 'simple', + properties: [ + { + type: 'number', + name: '', + title: 'Slides', + }, + ], + }, + { + title: 'Complex', + value: 'complex', + properties: sliderSizesArray.reduce((acc, size) => { + acc.push({type: 'number', name: size, title: size}); + return acc; + }, [] as Array), + }, + ], + }, + { + type: 'array', + name: 'array', + title: 'Array Properties', + buttonText: 'Add new', + arrayType: 'object', + properties: [ + { + type: 'text', + name: 'text', + title: 'Property', + }, + { + type: 'boolean', + name: 'boolean', + title: 'Property', + }, + ], + }, +]; + +const CardLayoutBlockConfig: BlockData = { + component: CardLayout, + schema: { + name: 'Card Layout Block', + inputs: blockConfig, + // inputs: generateFromAJV(CardLayoutProps as unknown as JSONSchemaType<{}>), + default: { + type: 'card-layout-block', + children: [], + title: 'Card Layout Block', + description: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + }, +}; + +export default CardLayoutBlockConfig; diff --git a/src/blocks/Companies/Companies.tsx b/src/blocks/Companies/Companies.tsx index d16d78a05e..c200e45281 100644 --- a/src/blocks/Companies/Companies.tsx +++ b/src/blocks/Companies/Companies.tsx @@ -1,6 +1,7 @@ import {Image, Title} from '../../components'; import AnimateBlock from '../../components/AnimateBlock/AnimateBlock'; import {useTheme} from '../../context/theme'; +import {Grid} from '../../grid'; import {CompaniesBlockProps} from '../../models'; import {block, getThemedValue} from '../../utils'; @@ -14,12 +15,18 @@ export const CompaniesBlock = ({title, description, images, animated}: Companies return ( -
- -
- + +
+ +
+ +
-
+ ); }; diff --git a/src/blocks/Companies/index.ts b/src/blocks/Companies/index.ts new file mode 100644 index 0000000000..dab19df95e --- /dev/null +++ b/src/blocks/Companies/index.ts @@ -0,0 +1,23 @@ +import {JSONSchemaType} from 'ajv'; + +import {BlockData} from '../../constructor-items'; +import {generateFromAJV} from '../../utils/form-generator'; + +import CompaniesBlock from './Companies'; +import {CompaniesBlock as CompaniesBlockSchema} from './schema'; + +const CompaniesBlockConfig: BlockData = { + component: CompaniesBlock, + schema: { + name: 'Companies Block', + inputs: generateFromAJV( + CompaniesBlockSchema['companies-block'] as unknown as JSONSchemaType<{}>, + ), + default: { + title: 'Companies Block', + description: 'Here is the list', + }, + }, +}; + +export default CompaniesBlockConfig; diff --git a/src/blocks/ContentLayout/ContentLayout.tsx b/src/blocks/ContentLayout/ContentLayout.tsx index 1154f77170..6bc7029a0b 100644 --- a/src/blocks/ContentLayout/ContentLayout.tsx +++ b/src/blocks/ContentLayout/ContentLayout.tsx @@ -4,7 +4,7 @@ import {BackgroundImage, FileLink} from '../../components'; import {BREAKPOINTS} from '../../constants'; import {useTheme} from '../../context/theme'; import {useWindowWidth} from '../../context/windowWidthContext'; -import {Col} from '../../grid'; +import {Col, Grid} from '../../grid'; import {ContentLayoutBlockProps, ContentSize, ContentTextSize} from '../../models'; import {Content} from '../../sub-blocks'; import {block, getThemedValue} from '../../utils'; @@ -54,39 +54,41 @@ export const ContentLayoutBlock = (props: ContentLayoutBlockProps) => { const themedBackground = getThemedValue(background, globalTheme); return ( -
- - {fileContent && ( - - {fileContent.map((file) => ( - +
+ + {fileContent && ( + + {fileContent.map((file) => ( + + ))} + + )} + {background && ( +
+ - ))} - - )} - {background && ( -
- -
- )} -
+
+ )} +
+ ); }; export default ContentLayoutBlock; diff --git a/src/blocks/ContentLayout/index.ts b/src/blocks/ContentLayout/index.ts new file mode 100644 index 0000000000..59dac4d754 --- /dev/null +++ b/src/blocks/ContentLayout/index.ts @@ -0,0 +1,21 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import ContentLayoutBlock from './ContentLayout'; +import {ContentLayoutBlock as ContentLayoutBlockSchema} from './schema'; + +const ContentLayoutBlockConfig = { + component: ContentLayoutBlock, + schema: { + name: 'Content Layout Block', + inputs: generateFromAJV( + ContentLayoutBlockSchema['content-layout-block'] as unknown as JSONSchemaType<{}>, + ), + default: { + title: 'Content Layout Block', + }, + }, +}; + +export default ContentLayoutBlockConfig; diff --git a/src/blocks/ExtendedFeatures/ExtendedFeatures.tsx b/src/blocks/ExtendedFeatures/ExtendedFeatures.tsx index bc5e0b8fd8..686e821394 100644 --- a/src/blocks/ExtendedFeatures/ExtendedFeatures.tsx +++ b/src/blocks/ExtendedFeatures/ExtendedFeatures.tsx @@ -2,7 +2,7 @@ import {AnimateBlock, Title, YFMWrapper} from '../../components/'; import Image from '../../components/Image/Image'; import {getMediaImage} from '../../components/Media/Image/utils'; import {useTheme} from '../../context/theme'; -import {Col, Row} from '../../grid'; +import {Col, Grid, Row} from '../../grid'; import {ExtendedFeaturesProps} from '../../models'; import {Content} from '../../sub-blocks'; import {block, getThemedValue} from '../../utils'; @@ -29,29 +29,31 @@ export const ExtendedFeaturesBlock = ({ return ( - - <div className={b('items')}> - <Row> - {items.map( - ({ - title: itemTitle, - text, - list, - link, - links, - label, - icon, - buttons, - additionalInfo, - }) => { - const itemLinks = links || []; + <Grid> + <Title title={title} subtitle={description} className={b('header')} /> + <div className={b('items')}> + <Row> + {items && + items.map( + ({ + title: itemTitle, + text, + list, + link, + links, + label, + icon, + buttons, + additionalInfo, + }) => { + const itemLinks = links || []; - const iconThemed = icon && getThemedValue(icon, theme); - const iconData = iconThemed && getMediaImage(iconThemed); + const iconThemed = icon && getThemedValue(icon, theme); + const iconData = iconThemed && getMediaImage(iconThemed); - if (link) { - itemLinks.push(link); - } + if (link) { + itemLinks.push(link); + } return ( <Col className={b('item')} key={text || itemTitle} sizes={colSizes}> diff --git a/src/blocks/ExtendedFeatures/index.ts b/src/blocks/ExtendedFeatures/index.ts new file mode 100644 index 0000000000..e6b0a209d0 --- /dev/null +++ b/src/blocks/ExtendedFeatures/index.ts @@ -0,0 +1,22 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import ExtendedFeaturesBlock from './ExtendedFeatures'; +import {ExtendedFeaturesBlock as ExtendedFeaturesBlockSchema} from './schema'; + +const ExtendedFeaturesBlockConfig = { + component: ExtendedFeaturesBlock, + schema: { + name: 'Extended Features Block', + inputs: generateFromAJV( + ExtendedFeaturesBlockSchema['extended-features-block'] as unknown as JSONSchemaType<{}>, + ), + default: { + title: 'Extended Features Block', + items: [{}], + }, + }, +}; + +export default ExtendedFeaturesBlockConfig; diff --git a/src/blocks/ExtendedFeatures/schema.ts b/src/blocks/ExtendedFeatures/schema.ts index a7705dfc17..eae9ea2e52 100644 --- a/src/blocks/ExtendedFeatures/schema.ts +++ b/src/blocks/ExtendedFeatures/schema.ts @@ -20,6 +20,7 @@ export const ExtendedFeaturesItem = { text: { type: 'string', contentType: 'yfm', + inputType: 'textarea', }, label: { type: 'string', diff --git a/src/blocks/FilterBlock/FilterBlock.tsx b/src/blocks/FilterBlock/FilterBlock.tsx index cc5d78d074..e161eaffe7 100644 --- a/src/blocks/FilterBlock/FilterBlock.tsx +++ b/src/blocks/FilterBlock/FilterBlock.tsx @@ -4,7 +4,7 @@ import {CardLayoutBlock} from '..'; import {AnimateBlock, Title} from '../../components'; import ButtonTabs, {ButtonTabsItemProps} from '../../components/ButtonTabs/ButtonTabs'; import {ConstructorItem} from '../../containers/PageConstructor/components/ConstructorItem'; -import {Col, Row} from '../../grid'; +import {Col, Grid, Row} from '../../grid'; import {FilterBlockProps, FilterItem} from '../../models'; import {block, getBlockKey} from '../../utils'; @@ -20,7 +20,7 @@ const FilterBlock = ({ tags, tagButtonSize, allTag, - items, + items = [], colSizes, centered, animated, @@ -54,35 +54,37 @@ const FilterBlock = ({ return ( <AnimateBlock className={b()} animate={animated}> - {title && ( - <Title - className={b('title', {centered: centered})} - title={title} - subtitle={description} - /> - )} - {tabButtons.length && ( - <Row> - <Col> - <ButtonTabs - className={b('tabs', {centered: centered})} - items={tabButtons} - activeTab={selectedTag} - onSelectTab={setSelectedTag} - tabSize={tagButtonSize} - /> - </Col> - </Row> - )} - <Row className={b('block-container')}> - <CardLayoutBlock title="" colSizes={colSizes} className={b('cards-container')}> - {cards.map((card, index) => { - const key = getBlockKey(card, index); + <Grid> + {title && ( + <Title + className={b('title', {centered: centered})} + title={title} + subtitle={description} + /> + )} + {tabButtons.length && ( + <Row> + <Col> + <ButtonTabs + className={b('tabs', {centered: centered})} + items={tabButtons} + activeTab={selectedTag} + onSelectTab={setSelectedTag} + tabSize={tagButtonSize} + /> + </Col> + </Row> + )} + <Row className={b('block-container')}> + <CardLayoutBlock title="" colSizes={colSizes} className={b('cards-container')}> + {cards.map((card, index) => { + const key = getBlockKey(card, index); - return <ConstructorItem data={card} blockKey={key} key={key} />; - })} - </CardLayoutBlock> - </Row> + return <ConstructorItem data={card} blockKey={index} key={key} />; + })} + </CardLayoutBlock> + </Row> + </Grid> </AnimateBlock> ); }; diff --git a/src/blocks/FilterBlock/index.ts b/src/blocks/FilterBlock/index.ts new file mode 100644 index 0000000000..15a4a56b1a --- /dev/null +++ b/src/blocks/FilterBlock/index.ts @@ -0,0 +1,19 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import FilterBlock from './FilterBlock'; +import {FilterProps} from './schema'; + +const FilterBlockConfig = { + component: FilterBlock, + schema: { + name: 'Filter Block', + inputs: generateFromAJV(FilterProps as unknown as JSONSchemaType<{}>), + default: { + title: 'Filter Block', + }, + }, +}; + +export default FilterBlockConfig; diff --git a/src/blocks/Form/index.ts b/src/blocks/Form/index.ts new file mode 100644 index 0000000000..b80b5c3716 --- /dev/null +++ b/src/blocks/Form/index.ts @@ -0,0 +1,20 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import FormBlock from './Form'; +import {FormBlock as FormBlockSchema} from './schema'; + +const FormBlockConfig = { + component: FormBlock, + schema: { + name: 'Form Block', + inputs: generateFromAJV(FormBlockSchema['form-block'] as unknown as JSONSchemaType<{}>), + default: { + title: 'Form Block', + formData: {}, + }, + }, +}; + +export default FormBlockConfig; diff --git a/src/blocks/Header/dynamic-form.ts b/src/blocks/Header/dynamic-form.ts new file mode 100644 index 0000000000..d3f174aba0 --- /dev/null +++ b/src/blocks/Header/dynamic-form.ts @@ -0,0 +1,732 @@ +import {BlockConfig, ConfigInput} from '../../common/types'; +import {imageInputs} from '../../components/Image/dynamic-form'; +import {Theme} from '../../models'; + +const mediaInputs: ConfigInput[] = [ + { + type: 'oneOf', + name: '', + key: 'mediaType', + title: 'Media Type', + options: [ + { + title: 'Empty', + value: 'emptyMedia', + properties: [ + { + type: 'text', + name: 'color', + title: 'Color', + }, + ], + }, + { + title: 'Image', + value: 'image', + properties: [ + { + type: 'oneOf', + name: 'image', + key: 'imagesCount', + title: 'Images Count', + options: [ + {title: 'Single Image', value: 'singleImage', properties: imageInputs}, + { + title: 'Multiple Images', + value: 'multipleImages', + properties: [ + { + type: 'array', + name: '', + arrayType: 'object', + title: 'Images', + properties: imageInputs, + buttonText: 'Add Image', + }, + ], + }, + ], + }, + ], + }, + { + title: 'Youtube', + value: 'youtube', + properties: [ + { + type: 'text', + name: 'youtube', + title: 'Youtube', + }, + { + type: 'text', + name: 'previewImg', + title: 'Preview Image', + }, + ], + }, + { + title: 'Datalens', + value: 'datalens', + properties: [], + }, + { + title: 'Iframe', + value: 'iframe', + properties: [], + }, + { + title: 'Video', + value: 'video', + properties: [], + }, + ], + }, + { + type: 'boolean', + name: 'disableImageSliderForArrayInput', + title: 'Disable Image Slider For Array Input', + }, + { + type: 'boolean', + name: 'parallax', + title: 'Parallax', + }, + { + type: 'number', + name: 'height', + title: 'Height', + }, + { + type: 'boolean', + name: 'fullscreen', + title: 'Fullscreen', + }, + { + type: 'number', + name: 'ratio', + title: 'Ratio', + }, + { + type: 'boolean', + name: 'margins', + title: 'Margins', + }, +]; + +const headerBackgroundInputs: ConfigInput[] = [ + { + type: 'boolean', + name: 'fullWidthMedia', + title: 'Full width Media', + }, + { + type: 'boolean', + name: 'fullWidth', + title: 'Full width', + }, +]; + +const backgroundProperty = (properties: ConfigInput[]): ConfigInput => ({ + type: 'oneOf', + name: 'background', + title: 'Background', + options: [ + { + value: 'noThemes', + title: 'No Themes', + properties: properties, + }, + { + value: 'withThemes', + title: 'With Themes', + properties: Object.values(Theme).map((theme) => ({ + type: 'object', + name: theme, + title: theme, + properties: properties, + })), + }, + ], +}); + +export const blockConfig: BlockConfig = { + name: 'Header Block', + inputs: [ + { + type: 'text', + name: 'title', + title: 'Title', + }, + { + type: 'text', + name: 'overtitle', + title: 'Overtitle', + }, + { + type: 'textarea', + name: 'description', + title: 'Description', + }, + backgroundProperty([...mediaInputs, ...headerBackgroundInputs]), + /* { + type: 'text', + name: 'when', + title: 'when', + }, + { + type: 'object', + name: 'anchor', + title: 'anchor', + properties: [ + { + type: 'text', + name: 'text', + title: 'text', + }, + { + type: 'text', + name: 'url', + title: 'url', + }, + { + type: 'text', + name: 'urlTitle', + title: 'urlTitle', + }, + ], + }, + { + type: 'select', + name: 'visible', + title: 'visible', + enum: [ + { + content: 'sm', + value: 'sm', + }, + { + content: 'md', + value: 'md', + }, + { + content: 'lg', + value: 'lg', + }, + { + content: 'xl', + value: 'xl', + }, + { + content: 'all', + value: 'all', + }, + ], + }, + { + type: 'text', + name: 'context', + title: 'context', + }, + { + type: 'object', + name: 'indent', + title: 'indent', + properties: [], + }, + { + type: 'select', + name: 'width', + title: 'width', + enum: [ + { + content: 's', + value: 's', + }, + { + content: 'm', + value: 'm', + }, + { + content: 'l', + value: 'l', + }, + ], + }, + { + type: 'array', + name: 'buttons', + title: 'buttons', + properties: [ + { + type: 'text', + name: 'when', + title: 'when', + }, + { + type: 'text', + name: 'text', + title: 'text', + }, + { + type: 'text', + name: 'url', + title: 'url', + }, + { + type: 'text', + name: 'urlTitle', + title: 'urlTitle', + }, + { + type: 'select', + name: 'size', + title: 'size', + enum: [ + { + content: 'xs', + value: 'xs', + }, + { + content: 'ns', + value: 'ns', + }, + { + content: 's', + value: 's', + }, + { + content: 'n', + value: 'n', + }, + { + content: 'm', + value: 'm', + }, + { + content: 'l', + value: 'l', + }, + { + content: 'xl', + value: 'xl', + }, + { + content: 'head', + value: 'head', + }, + { + content: 'promo', + value: 'promo', + }, + ], + }, + { + type: 'select', + name: 'theme', + title: 'theme', + enum: [ + { + content: 'normal', + value: 'normal', + }, + { + content: 'action', + value: 'action', + }, + { + content: 'outlined', + value: 'outlined', + }, + { + content: 'outlined-info', + value: 'outlined-info', + }, + { + content: 'outlined-danger', + value: 'outlined-danger', + }, + { + content: 'raised', + value: 'raised', + }, + { + content: 'flat', + value: 'flat', + }, + { + content: 'flat-info', + value: 'flat-info', + }, + { + content: 'flat-danger', + value: 'flat-danger', + }, + { + content: 'flat-secondary', + value: 'flat-secondary', + }, + { + content: 'clear', + value: 'clear', + }, + { + content: 'normal-contrast', + value: 'normal-contrast', + }, + { + content: 'outlined-contrast', + value: 'outlined-contrast', + }, + { + content: 'flat-contrast', + value: 'flat-contrast', + }, + { + content: 'link', + value: 'link', + }, + { + content: 'pseudo', + value: 'pseudo', + }, + { + content: 'pseudo-special', + value: 'pseudo-special', + }, + { + content: 'websearch', + value: 'websearch', + }, + { + content: 'normal-dark', + value: 'normal-dark', + }, + { + content: 'normal-special', + value: 'normal-special', + }, + { + content: 'accent', + value: 'accent', + }, + { + content: 'dark-grey', + value: 'dark-grey', + }, + { + content: 'app-store', + value: 'app-store', + }, + { + content: 'google-play', + value: 'google-play', + }, + { + content: 'scale', + value: 'scale', + }, + { + content: 'github', + value: 'github', + }, + { + content: 'monochrome', + value: 'monochrome', + }, + ], + }, + { + type: 'oneOf', + name: 'img', + title: 'img', + options: [ + { + value: 'url', + title: 'url', + properties: [], + }, + { + value: 'options', + title: 'options', + properties: [], + }, + ], + }, + { + type: 'oneOf', + name: 'analyticsEvents', + title: 'analyticsEvents', + options: [ + { + value: 'single', + title: 'single', + properties: [ + { + type: 'text', + name: 'additionalProperties', + title: 'additionalProperties', + }, + ], + }, + { + value: 'list', + title: 'list', + properties: [ + { + type: 'object', + name: 'items', + title: 'items', + properties: [ + { + type: 'text', + name: 'name', + title: 'name', + }, + { + type: 'text', + name: 'type', + title: 'type', + }, + { + type: 'object', + name: 'counters', + title: 'counters', + properties: [], + }, + { + type: 'text', + name: 'context', + title: 'context', + }, + ], + }, + ], + }, + ], + }, + { + type: 'select', + name: 'target', + title: 'target', + enum: [ + { + content: '_self', + value: '_self', + }, + { + content: '_blank', + value: '_blank', + }, + { + content: '_parent', + value: '_parent', + }, + { + content: '_top', + value: '_top', + }, + ], + }, + { + type: 'select', + name: 'width', + title: 'width', + enum: [ + { + content: 'auto', + value: 'auto', + }, + { + content: 'max', + value: 'max', + }, + ], + }, + ], + }, + { + type: 'select', + name: 'offset', + title: 'offset', + enum: [ + { + content: 'default', + value: 'default', + }, + { + content: 'large', + value: 'large', + }, + ], + }, + { + type: 'oneOf', + name: 'image', + title: 'image', + options: [ + { + value: 'no theme', + title: 'no theme', + properties: [], + }, + { + value: 'themes', + title: 'themes', + properties: [], + }, + ], + }, + { + type: 'oneOf', + name: 'video', + title: 'video', + options: [ + { + value: 'no theme', + title: 'no theme', + properties: [], + }, + { + value: 'themes', + title: 'themes', + properties: [], + }, + ], + }, + { + type: 'select', + name: 'mediaView', + title: 'mediaView', + enum: [ + { + content: 'fit', + value: 'fit', + }, + { + content: 'full', + value: 'full', + }, + ], + }, + { + type: 'object', + name: 'backLink', + title: 'backLink', + properties: [ + { + type: 'text', + name: 'url', + title: 'url', + }, + { + type: 'text', + name: 'title', + title: 'title', + }, + ], + }, + { + type: 'select', + name: 'imageSize', + title: 'imageSize', + enum: [ + { + content: 's', + value: 's', + }, + { + content: 'm', + value: 'm', + }, + ], + }, + { + type: 'select', + name: 'verticalOffset', + title: 'verticalOffset', + enum: [ + { + content: '0', + value: '0', + }, + { + content: 's', + value: 's', + }, + { + content: 'm', + value: 'm', + }, + { + content: 'l', + value: 'l', + }, + { + content: 'xl', + value: 'xl', + }, + ], + }, + + { + type: 'select', + name: 'theme', + title: 'theme', + enum: [ + { + content: 'default', + value: 'default', + }, + { + content: 'dark', + value: 'dark', + }, + ], + }, + { + type: 'object', + name: 'breadcrumbs', + title: 'breadcrumbs', + properties: [ + { + type: 'array', + name: 'items', + title: 'items', + properties: [ + { + type: 'text', + name: 'url', + title: 'url', + }, + { + type: 'text', + name: 'text', + title: 'text', + }, + ], + }, + { + type: 'select', + name: 'theme', + title: 'theme', + enum: [ + { + content: 'light', + value: 'light', + }, + { + content: 'dark', + value: 'dark', + }, + ], + }, + ], + }, + { + type: 'text', + name: 'status', + title: 'status', + },*/ + ], +}; diff --git a/src/blocks/Header/index.ts b/src/blocks/Header/index.ts new file mode 100644 index 0000000000..1f432122ed --- /dev/null +++ b/src/blocks/Header/index.ts @@ -0,0 +1,16 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import HeaderBlock from './Header'; +import {HeaderBlock as HeaderBlockSchema} from './schema'; + +const HeaderBlockConfig = { + component: HeaderBlock, + schema: { + name: 'Header Block', + inputs: generateFromAJV(HeaderBlockSchema['header-block'] as unknown as JSONSchemaType<{}>), + }, +}; + +export default HeaderBlockConfig; diff --git a/src/blocks/HeaderSlider/HeaderSlider.tsx b/src/blocks/HeaderSlider/HeaderSlider.tsx index 443f76dc97..5e0e359a4c 100644 --- a/src/blocks/HeaderSlider/HeaderSlider.tsx +++ b/src/blocks/HeaderSlider/HeaderSlider.tsx @@ -25,15 +25,16 @@ export const HeaderSliderBlock = ({items, arrows, ...props}: HeaderSliderBlockPr blockClassName={b()} arrowSize={20} > - {items.map((item, index) => ( - <div - key={index} - className={b('item')} - data-qa={`header-slider-item-${index + 1}`} - > - <Header {...item} className={b('item-content')} /> - </div> - ))} + {items && + items.map((item, index) => ( + <div + key={index} + className={b('item')} + data-qa={`header-slider-item-${index + 1}`} + > + <Header {...item} className={b('item-content')} /> + </div> + ))} </SliderBlock> </div> ); diff --git a/src/blocks/HeaderSlider/index.ts b/src/blocks/HeaderSlider/index.ts new file mode 100644 index 0000000000..4828875431 --- /dev/null +++ b/src/blocks/HeaderSlider/index.ts @@ -0,0 +1,44 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import HeaderSliderBlock from './HeaderSlider'; +import {HeaderSliderBlock as HeaderSliderBlockSchema} from './schema'; + +const HeaderSliderBlockConfig = { + component: HeaderSliderBlock, + schema: { + name: 'Header Slider Block', + inputs: generateFromAJV( + HeaderSliderBlockSchema['header-slider-block'] as unknown as JSONSchemaType<{}>, + ), + default: { + type: 'header-slider-block', + items: [ + { + title: 'Header Slide 1', + overtitle: 'Header Slider Block presents', + description: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + { + image: 'https://storage.yandexcloud.net/cloud-www-assets/constructor/storybook/images/header-bg-video_light.png', + mediaView: 'fit', + title: 'Header Slide 2', + overtitle: 'Header Slider Block presents', + description: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + width: 'm', + buttons: [ + { + text: 'Button', + theme: 'action', + }, + ], + }, + ], + }, + }, +}; + +export default HeaderSliderBlockConfig; diff --git a/src/blocks/Icons/Icons.tsx b/src/blocks/Icons/Icons.tsx index e6ce8c667d..93935febb8 100644 --- a/src/blocks/Icons/Icons.tsx +++ b/src/blocks/Icons/Icons.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import {Image, Title} from '../../components'; import {LocationContext} from '../../context/locationContext'; +import {Grid} from '../../grid'; import {useTheme} from '../../context/theme'; import {useAnalytics} from '../../hooks'; import {IconsBlockItemProps, IconsBlockProps} from '../../models'; @@ -32,36 +33,39 @@ const Icons = ({title, description, size = 's', colSizes = {all: 12}, items}: Ic return ( <div className={b({size})}> - {(title || description) && ( - <Title - className={b('header')} - title={title} - subtitle={description} - colSizes={colSizes} - /> - )} - {items.map((item) => { - const themedSrc = getThemedValue(item.src, theme); - const itemContent = getItemContent({...item, src: themedSrc}); - const {url, text} = item; - return url ? ( - <a - className={b('item')} - key={url} - href={url} - aria-label={text} - title={text} - {...getLinkProps(url, hostname)} - onClick={() => onClick(item)} - > - {itemContent} - </a> - ) : ( - <div className={b('item')} key={text}> - {itemContent} - </div> - ); - })} + <Grid> + {(title || description) && ( + <Title + className={b('header')} + title={title} + subtitle={description} + colSizes={colSizes} + /> + )} + {items && + items.map((item) => { + const themedSrc = getThemedValue(item.src, theme); + const itemContent = getItemContent({...item, src: themedSrc}); + const {url, text} = item; + return url ? ( + <a + className={b('item')} + key={url} + href={url} + aria-label={text} + title={text} + {...getLinkProps(url, hostname)} + onClick={() => onClick(item)} + > + {itemContent} + </a> + ) : ( + <div className={b('item')} key={text}> + {itemContent} + </div> + ); + })} + </Grid> </div> ); }; diff --git a/src/blocks/Icons/index.ts b/src/blocks/Icons/index.ts new file mode 100644 index 0000000000..21251e103e --- /dev/null +++ b/src/blocks/Icons/index.ts @@ -0,0 +1,22 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import IconsBlock from './Icons'; +import {IconsProps} from './schema'; + +const IconsBlockConfig = { + component: IconsBlock, + schema: { + name: 'Icons Block', + inputs: generateFromAJV(IconsProps as unknown as JSONSchemaType<{}>), + default: { + type: 'icons-block', + title: 'Icons Block', + description: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + }, +}; + +export default IconsBlockConfig; diff --git a/src/blocks/Info/index.ts b/src/blocks/Info/index.ts new file mode 100644 index 0000000000..1bd367f981 --- /dev/null +++ b/src/blocks/Info/index.ts @@ -0,0 +1,43 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import InfoBlock from './Info'; +import {InfoBlock as InfoBlockSchema} from './schema'; + +const InfoBlockConfig = { + component: InfoBlock, + schema: { + name: 'Info Block', + inputs: generateFromAJV(InfoBlockSchema['info-block'] as unknown as JSONSchemaType<{}>), + default: { + type: 'info-block', + title: 'Info Block', + backgroundColor: '#1c1c1c', + sectionsTitle: 'Other links', + links: [ + { + text: 'Link 1', + }, + { + text: 'Link 2', + }, + { + text: 'Link 3', + }, + ], + buttons: [ + { + text: 'Read more', + theme: 'outlined-contrast', + }, + { + text: 'Go back', + theme: 'action', + }, + ], + }, + }, +}; + +export default InfoBlockConfig; diff --git a/src/blocks/Map/index.ts b/src/blocks/Map/index.ts new file mode 100644 index 0000000000..88c48ce778 --- /dev/null +++ b/src/blocks/Map/index.ts @@ -0,0 +1,19 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import MapBlock from './Map'; +import {MapBlock as MapBlockSchema} from './schema'; + +const MapBlockConfig = { + component: MapBlock, + schema: { + name: 'Map Block', + inputs: generateFromAJV(MapBlockSchema['map-block'] as unknown as JSONSchemaType<{}>), + default: { + title: 'Map Block', + }, + }, +}; + +export default MapBlockConfig; diff --git a/src/blocks/Media/index.ts b/src/blocks/Media/index.ts new file mode 100644 index 0000000000..b3f113ad60 --- /dev/null +++ b/src/blocks/Media/index.ts @@ -0,0 +1,23 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import MediaBlock from './Media'; +import {MediaBlock as MediaBlockSchema} from './schema'; + +const MediaBlockConfig = { + component: MediaBlock, + schema: { + name: 'Media Block', + inputs: generateFromAJV(MediaBlockSchema['media-block'] as unknown as JSONSchemaType<{}>), + default: { + type: 'media-block', + title: 'Media Block', + additionalInfo: 'Additional info', + description: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + }, +}; + +export default MediaBlockConfig; diff --git a/src/blocks/PromoFeaturesBlock/PromoFeaturesBlock.tsx b/src/blocks/PromoFeaturesBlock/PromoFeaturesBlock.tsx index 4c34e6a958..ac77be32ff 100644 --- a/src/blocks/PromoFeaturesBlock/PromoFeaturesBlock.tsx +++ b/src/blocks/PromoFeaturesBlock/PromoFeaturesBlock.tsx @@ -5,6 +5,7 @@ import Media from '../../components/Media/Media'; import Title from '../../components/Title/Title'; import YFMWrapper from '../../components/YFMWrapper/YFMWrapper'; import {BREAKPOINTS} from '../../constants'; +import {Grid} from '../../grid'; import {useTheme} from '../../context/theme'; import {PromoFeaturesProps} from '../../models'; import {block, getThemedValue} from '../../utils'; @@ -26,43 +27,50 @@ const PromoFeaturesBlock = (props: PromoFeaturesProps) => { const globalTheme = useTheme(); return ( - <AnimateBlock className={b({[backgroundTheme]: true})} animate={animated}> - <FullWidthBackground className={b('background', {[backgroundTheme]: true})} /> - <Title title={title} subtitle={description} className={b('header')} /> - <BalancedMasonry - breakpointCols={breakpointColumns} - className={b('card-container')} - columnClassName={b('card-container-column')} - > - {items.map(({title: cardTitle, text, media, theme: cardTheme}, index) => { - const blockModifier = backgroundTheme === 'default' ? 'default' : 'light'; - const themeMod = cardTheme || blockModifier || ''; - const themedMedia = getThemedValue(media, globalTheme); - const allProps = mergeVideoMicrodata(themedMedia, { - name: cardTitle, - description: text, - }); + <Grid> + <AnimateBlock className={b({[backgroundTheme]: true})} animate={animated}> + <FullWidthBackground className={b('background', {[backgroundTheme]: true})} /> + <Title title={title} subtitle={description} className={b('header')} /> + <BalancedMasonry + breakpointCols={breakpointColumns} + className={b('card-container')} + columnClassName={b('card-container-column')} + > + {items && + items.map(({title: cardTitle, text, media, theme: cardTheme}, index) => { + const blockModifier = + backgroundTheme === 'default' ? 'default' : 'light'; + const themeMod = cardTheme || blockModifier || ''; + const themedMedia = getThemedValue(media, globalTheme); + const allProps = mergeVideoMicrodata(themedMedia, { + name: cardTitle, + description: text, + }); - return ( - <div - key={index} - className={b('card', { - 'no-media': !media, - [themeMod]: Boolean(themeMod), - })} - > - <div className={b('card-info')}> - <h3 className={b('card-title')}>{cardTitle}</h3> - <div className={b('card-text')}> - <YFMWrapper content={text} modifiers={{constructor: true}} /> + return ( + <div + key={index} + className={b('card', { + 'no-media': !media, + [themeMod]: Boolean(themeMod), + })} + > + <div className={b('card-info')}> + <h3 className={b('card-title')}>{cardTitle}</h3> + <div className={b('card-text')}> + <YFMWrapper + content={text} + modifiers={{constructor: true}} + /> + </div> + </div> + {media && <Media className={b('card-media')} {...allProps} />} </div> - </div> - {media && <Media className={b('card-media')} {...allProps} />} - </div> - ); - })} - </BalancedMasonry> - </AnimateBlock> + ); + })} + </BalancedMasonry> + </AnimateBlock> + </Grid> ); }; diff --git a/src/blocks/PromoFeaturesBlock/index.ts b/src/blocks/PromoFeaturesBlock/index.ts new file mode 100644 index 0000000000..2c3eac449a --- /dev/null +++ b/src/blocks/PromoFeaturesBlock/index.ts @@ -0,0 +1,22 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import PromoFeaturesBlock from './PromoFeaturesBlock'; +import {PromoFeaturesBlock as PromoFeaturesBlockSchema} from './schema'; + +const PromoFeaturesBlockConfig = { + component: PromoFeaturesBlock, + schema: { + name: 'Promo Features Block', + inputs: generateFromAJV( + PromoFeaturesBlockSchema['promo-features-block'] as unknown as JSONSchemaType<{}>, + ), + default: { + title: 'Promo Features Block', + items: [{}], + }, + }, +}; + +export default PromoFeaturesBlockConfig; diff --git a/src/blocks/Questions/Questions.tsx b/src/blocks/Questions/Questions.tsx index a0160a5df0..e562a429fd 100644 --- a/src/blocks/Questions/Questions.tsx +++ b/src/blocks/Questions/Questions.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import {Col, Row} from '../../grid'; +import {Col, Grid, Row} from '../../grid'; import {QuestionsProps} from '../../models'; import {Content} from '../../sub-blocks'; import {block} from '../../utils'; @@ -89,22 +89,23 @@ const QuestionsBlock = (props: QuestionsProps) => { const isOpened = opened.includes(index); const onClick = () => toggleItem(index, itemOnClick); - return ( - <QuestionBlockItem - key={itemTitle} - title={itemTitle} - text={itemText} - link={link} - listStyle={listStyle} - isOpened={isOpened} - onClick={onClick} - /> - ); - }, - )} - </Col> - </Row> - </div> + return ( + <QuestionBlockItem + key={itemTitle} + title={itemTitle} + text={itemText} + link={link} + listStyle={listStyle} + isOpened={isOpened} + onClick={onClick} + /> + ); + }, + )} + </Col> + </Row> + </div> + </Grid> ); }; diff --git a/src/blocks/Questions/index.ts b/src/blocks/Questions/index.ts new file mode 100644 index 0000000000..43253bf7e5 --- /dev/null +++ b/src/blocks/Questions/index.ts @@ -0,0 +1,51 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import QuestionsBlock from './Questions'; +import {QuestionsBlock as QuestionsBlockSchema} from './schema'; + +const QuestionsBlockConfig = { + component: QuestionsBlock, + schema: { + name: 'Questions Block', + inputs: generateFromAJV( + QuestionsBlockSchema['questions-block'] as unknown as JSONSchemaType<{}>, + ), + default: { + type: 'questions-block', + title: 'Questions Block', + text: 'Here you can find answers.', + links: [ + { + text: 'Report for 2024', + url: 'file.doc', + }, + ], + buttons: [ + { + text: 'Get more', + theme: 'outlined-info', + }, + ], + list: [ + { + title: 'Report for 2024', + text: 'Some advice here', + }, + ], + items: [ + { + title: 'Question 1', + text: 'Answer for question 1', + }, + { + title: 'Question 2', + text: 'Answer for question 2', + }, + ], + }, + }, +}; + +export default QuestionsBlockConfig; diff --git a/src/blocks/Share/Share.tsx b/src/blocks/Share/Share.tsx index 0c17d7dd88..89619fa150 100644 --- a/src/blocks/Share/Share.tsx +++ b/src/blocks/Share/Share.tsx @@ -52,29 +52,32 @@ const Share = ({items, title}: ShareBlockProps) => { <h5 className={b('title')}>{i18n('constructor-share')}</h5> )} <div className={b('items')}> - {items.map((type) => { - const url = getAbsolutePath(hostname, pathname); - const socialUrl = getShareLink(url, type); - const icon = icons[type]; - const urlTitle = i18n(`${type}-title`); - const buttonLabel = i18n(`${type}-label`); + {items && + items.map((type) => { + const url = getAbsolutePath(hostname, pathname); + const socialUrl = getShareLink(url, type); + const icon = icons[type]; + const urlTitle = i18n(`${type}-title`); + const buttonLabel = i18n(`${type}-label`); - return ( - <Button - key={type} - view="flat" - size="l" - target="_blank" - href={socialUrl} - className={b('item', {type: type.toLowerCase()})} - onClick={handleButtonClick} - title={urlTitle} - aria-label={buttonLabel} - > - {icon && <Icon data={icon} size={24} className={b('icon', {type})} />} - </Button> - ); - })} + return ( + <Button + key={type} + view="flat" + size="l" + target="_blank" + href={socialUrl} + className={b('item', {type: type.toLowerCase()})} + onClick={handleButtonClick} + title={urlTitle} + aria-label={buttonLabel} + > + {icon && ( + <Icon data={icon} size={24} className={b('icon', {type})} /> + )} + </Button> + ); + })} </div> </div> ); diff --git a/src/blocks/Share/index.ts b/src/blocks/Share/index.ts new file mode 100644 index 0000000000..e44d6b623b --- /dev/null +++ b/src/blocks/Share/index.ts @@ -0,0 +1,20 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import ShareBlock from './Share'; +import {ShareBlock as ShareBlockSchema} from './schema'; + +const ShareBlockConfig = { + component: ShareBlock, + schema: { + name: 'Share Block', + inputs: generateFromAJV(ShareBlockSchema['share-block'] as unknown as JSONSchemaType<{}>), + default: { + items: ['vk', 'telegram', 'facebook'], + title: 'Share Block', + }, + }, +}; + +export default ShareBlockConfig; diff --git a/src/blocks/Slider/__tests__/Slider.test.tsx b/src/blocks/Slider/__tests__/Slider.test.tsx index fb39e9685c..20e108f2aa 100644 --- a/src/blocks/Slider/__tests__/Slider.test.tsx +++ b/src/blocks/Slider/__tests__/Slider.test.tsx @@ -1,5 +1,6 @@ import {render} from '@testing-library/react'; +import {PageConstructorProvider} from '../../../containers/PageConstructor'; import {BasicCard} from '../../../sub-blocks'; import Slider from '../Slider'; diff --git a/src/blocks/Slider/dynamic-form.ts b/src/blocks/Slider/dynamic-form.ts new file mode 100644 index 0000000000..ea7468997c --- /dev/null +++ b/src/blocks/Slider/dynamic-form.ts @@ -0,0 +1,226 @@ +import {BlockConfig, ConfigInput} from '../../common/types'; +import {sliderSizesArray, textSize} from '../../schema/validators/common'; + +const textSizeEnum = textSize.map((size) => ({value: size, content: size})); + +export const blockConfig: BlockConfig = { + name: 'Slider Block', + inputs: [ + { + type: 'oneOf', + name: 'title', + title: 'Title Object', + options: [ + { + title: 'Simple', + value: 'simple', + properties: [ + { + type: 'text', + name: '', + title: 'Title', + }, + ], + }, + { + title: 'Complex', + value: 'complex', + properties: [ + { + type: 'text', + name: 'text', + title: 'Title', + }, + { + type: 'select', + name: 'textSize', + title: 'Text Size', + enum: textSizeEnum, + view: 'select', + mode: 'single', + }, + { + type: 'text', + name: 'url', + title: 'Url', + }, + { + type: 'text', + name: 'urlTitle', + title: 'Url', + }, + { + type: 'boolean', + name: 'resetMargin', + title: 'Reset Margin', + }, + ], + }, + ], + }, + { + type: 'textarea', + name: 'description', + title: 'Description', + }, + { + type: 'boolean', + name: 'dots', + title: 'With dots', + }, + { + type: 'boolean', + name: 'arrows', + title: 'With Arrows', + }, + { + type: 'boolean', + name: 'randomOrder', + title: 'Random Order', + }, + { + type: 'number', + name: 'autoplay', + title: 'Autoplay', + }, + { + type: 'object', + name: 'disclaimer', + title: 'Disclaimer', + properties: [ + { + type: 'text', + name: 'text', + title: 'Text', + }, + { + type: 'select', + name: 'size', + title: 'Size', + enum: textSizeEnum, + view: 'select', + mode: 'single', + }, + ], + }, + { + type: 'oneOf', + name: 'slidesToShow', + title: 'Slides to Show', + options: [ + { + title: 'Simple', + value: 'simple', + properties: [ + { + type: 'number', + name: '', + title: 'Slides', + }, + ], + }, + { + title: 'Complex', + value: 'complex', + properties: sliderSizesArray.reduce((acc, size) => { + acc.push({type: 'number', name: size, title: size}); + return acc; + }, [] as Array<ConfigInput>), + }, + ], + }, + { + type: 'array', + name: 'array', + title: 'Array Properties', + buttonText: 'Add new', + arrayType: 'object', + properties: [ + { + type: 'text', + name: 'text', + title: 'Property', + }, + { + type: 'boolean', + name: 'boolean', + title: 'Property', + }, + ], + }, + ], +}; + +export const exampleConfig: BlockConfig = { + name: 'Slider Block', + inputs: [ + { + type: 'text', + name: 'title', + title: 'Text Property', + }, + { + type: 'boolean', + name: '<ID>', + title: 'Boolean Property', + }, + { + type: 'number', + name: '<ID>', + title: 'Number Property', + }, + { + type: 'textarea', + name: '<ID>', + title: 'TextArea Property', + showIf: `block.description === 'Test'`, + }, + { + type: 'select', + name: 'select', + title: 'Select Property', + enum: [ + {content: 'Option 1', value: 'option-1'}, + {content: 'Option 2', value: 'option-2'}, + ], + mode: 'single', + view: 'select', + }, + { + type: 'object', + name: 'object_data', + title: 'Object Property', + properties: [ + { + type: 'text', + name: 'text', + title: 'Property', + }, + { + type: 'boolean', + name: 'boolean', + title: 'Property', + }, + ], + }, + { + type: 'array', + name: 'array', + title: 'Array Properties', + buttonText: 'Add new', + arrayType: 'object', + properties: [ + { + type: 'text', + name: 'text', + title: 'Property', + }, + { + type: 'boolean', + name: 'boolean', + title: 'Property', + }, + ], + }, + ], +}; diff --git a/src/blocks/Slider/index.ts b/src/blocks/Slider/index.ts new file mode 100644 index 0000000000..106ad3afe3 --- /dev/null +++ b/src/blocks/Slider/index.ts @@ -0,0 +1,16 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import SliderBlock from './Slider'; +import {SliderBlock as SliderBlockSchema} from './schema'; + +const SliderBlockConfig = { + component: SliderBlock, + schema: { + name: 'Slider Block', + inputs: generateFromAJV(SliderBlockSchema['slider-block'] as unknown as JSONSchemaType<{}>), + }, +}; + +export default SliderBlockConfig; diff --git a/src/blocks/Table/index.ts b/src/blocks/Table/index.ts new file mode 100644 index 0000000000..a7f5878114 --- /dev/null +++ b/src/blocks/Table/index.ts @@ -0,0 +1,16 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import TableBlock from './Table'; +import {TableBlock as TableBlockSchema} from './schema'; + +const TableBlockConfig = { + component: TableBlock, + schema: { + name: 'Table Block', + inputs: generateFromAJV(TableBlockSchema['table-block'] as unknown as JSONSchemaType<{}>), + }, +}; + +export default TableBlockConfig; diff --git a/src/blocks/Table/schema.ts b/src/blocks/Table/schema.ts index a6b1ecd2ea..30fc6765ec 100644 --- a/src/blocks/Table/schema.ts +++ b/src/blocks/Table/schema.ts @@ -11,6 +11,7 @@ export const TableBlock = { contentType: 'text', }, table: { + type: 'object', additionalProperties: false, required: ['content'], properties: { diff --git a/src/blocks/Tabs/Tabs.tsx b/src/blocks/Tabs/Tabs.tsx index 24fcb535e4..503ccf2963 100644 --- a/src/blocks/Tabs/Tabs.tsx +++ b/src/blocks/Tabs/Tabs.tsx @@ -5,7 +5,7 @@ import {getUniqId} from '@gravity-ui/uikit'; import AnimateBlock from '../../components/AnimateBlock/AnimateBlock'; import ButtonTabs, {ButtonTabsItemProps} from '../../components/ButtonTabs/ButtonTabs'; import Title from '../../components/Title/Title'; -import {Col, GridJustifyContent, Row} from '../../grid'; +import {Col, Grid, GridJustifyContent, Row} from '../../grid'; import {TabsBlockProps} from '../../models'; import {block} from '../../utils'; @@ -56,42 +56,44 @@ export const TabsBlock = ({ ); return ( - <AnimateBlock className={b()} onScroll={() => setPlay(true)} animate={animated}> - <Title - title={title} - subtitle={description} - className={b('title', {centered: centered})} - /> - <Row justifyContent={centered ? GridJustifyContent.Center : undefined}> - <Col sizes={tabsColSizes}> - <ButtonTabs - items={tabs} - onSelectTab={onSelectTab} - activeTab={activeTab} - className={b('tabs', {centered: centered})} - getTabElementId={getTabElementId} - getTabContentElementId={getTabContentElementId} - /> - </Col> - </Row> - {items.map((tabData) => { - const {tabName} = tabData; + <Grid> + <AnimateBlock className={b()} onScroll={() => setPlay(true)} animate={animated}> + <Title + title={title} + subtitle={description} + className={b('title', {centered: centered})} + /> + <Row justifyContent={centered ? GridJustifyContent.Center : undefined}> + <Col sizes={tabsColSizes}> + <ButtonTabs + items={tabs} + onSelectTab={onSelectTab} + activeTab={activeTab} + className={b('tabs', {centered: centered})} + getTabElementId={getTabElementId} + getTabContentElementId={getTabContentElementId} + /> + </Col> + </Row> + {items.map((tabData) => { + const {tabName} = tabData; - return ( - <TabContent - key={tabName} - tabData={tabData} - isActive={tabName === activeTab} - isReverse={isReverse} - contentSize={contentSize} - centered={centered} - play={play} - getTabElementId={getTabElementId} - getTabContentElementId={getTabContentElementId} - /> - ); - })} - </AnimateBlock> + return ( + <TabContent + key={tabName} + tabData={tabData} + isActive={tabName === activeTab} + isReverse={isReverse} + contentSize={contentSize} + centered={centered} + play={play} + getTabElementId={getTabElementId} + getTabContentElementId={getTabContentElementId} + /> + ); + })} + </AnimateBlock> + </Grid> ); }; diff --git a/src/blocks/Tabs/index.ts b/src/blocks/Tabs/index.ts new file mode 100644 index 0000000000..4d7be63d1d --- /dev/null +++ b/src/blocks/Tabs/index.ts @@ -0,0 +1,31 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import TabsBlock from './Tabs'; +import {TabsBlock as TabsBlockSchema} from './schema'; + +const TabsBlockConfig = { + component: TabsBlock, + schema: { + name: 'Tabs Block', + inputs: generateFromAJV(TabsBlockSchema['tabs-block'] as unknown as JSONSchemaType<{}>), + default: { + title: 'Tabs Block', + items: [ + { + tabName: 'First Tab', + text: 'First Tab Content', + title: 'First Tab Title', + }, + { + text: 'Second Tab Content', + title: 'Second Tab Title', + tabName: 'Second Tab', + }, + ], + }, + }, +}; + +export default TabsBlockConfig; diff --git a/src/blocks/TestEditorBlock/TestEditorBlock.tsx b/src/blocks/TestEditorBlock/TestEditorBlock.tsx new file mode 100644 index 0000000000..7888c5cc1e --- /dev/null +++ b/src/blocks/TestEditorBlock/TestEditorBlock.tsx @@ -0,0 +1,13 @@ +import React, {PropsWithChildren} from 'react'; + +import {block} from '../../utils'; + +const b = block('test-editor-block'); + +export interface TestEditorBlockProps extends PropsWithChildren {} + +export const TestEditorBlock = (props: TestEditorBlockProps) => { + return <div className={b()}>{JSON.stringify(props, null, 2)}</div>; +}; + +export default TestEditorBlock; diff --git a/src/blocks/TestEditorBlock/form.ts b/src/blocks/TestEditorBlock/form.ts new file mode 100644 index 0000000000..838eacb06e --- /dev/null +++ b/src/blocks/TestEditorBlock/form.ts @@ -0,0 +1,114 @@ +import { + ArrayObjectInput, + ArrayTextInput, + BooleanInput, + NumberInput, + ObjectInput, + OneOfInput, + SelectMultipleInput, + SelectSingleInput, + TextAreaInput, + TextInput, +} from '../../common/types'; + +const textInput: TextInput = { + type: 'text', + name: 'text', + title: 'Text Input', +}; + +const textAreaInput: TextAreaInput = { + type: 'textarea', + name: 'textarea', + title: 'TextArea Input', +}; + +const booleanInput: BooleanInput = { + type: 'boolean', + name: 'boolean', + title: 'Boolean Input', +}; + +const numberInput: NumberInput = { + type: 'number', + name: 'number', + title: 'Number Input', +}; + +const selectInput: SelectSingleInput = { + type: 'select', + name: 'selectSingle', + title: 'Select Single Input', + mode: 'single', + view: 'select', + enum: [ + {value: 'id_1', content: 'Option 1'}, + {value: 'id_2', content: 'Option 2'}, + ], +}; + +const radioButtonsViewSingleInput: SelectSingleInput = { + ...selectInput, + name: 'radioButtons', + title: 'Radio Button Input', + + view: 'radiobutton', +}; + +// @ts-ignore +const selectMultipleModeInput: SelectMultipleInput = { + ...selectInput, + name: 'selectMultiple', + title: 'Select Multiple Input', + + mode: 'multiple', +}; + +const objectInput: ObjectInput = { + type: 'object', + name: 'object', + title: 'Object Input', + properties: [textInput, textAreaInput, selectInput], +}; + +const arrayTextInput: ArrayTextInput = { + type: 'array', + name: 'arrayText', + title: 'Array Text Input', + buttonText: 'Add Array Item', + arrayType: 'text', +}; + +const arrayObjectInput: ArrayObjectInput = { + type: 'array', + name: 'arrayObject', + title: 'Array Object Input', + buttonText: 'Add Array Item', + arrayType: 'object', + properties: [textInput, textAreaInput, selectInput], +}; + +const oneOfInput: OneOfInput = { + type: 'oneOf', + name: 'oneOf', + key: 'oneOfKey', + title: 'Array Text Input', + options: [ + {value: 'text', title: 'Text', properties: [textInput]}, + {value: 'textarea', title: 'TextArea', properties: [textAreaInput]}, + ], +}; + +export default [ + textInput, + textAreaInput, + booleanInput, + numberInput, + selectInput, + radioButtonsViewSingleInput, + selectMultipleModeInput, + objectInput, + arrayTextInput, + arrayObjectInput, + oneOfInput, +]; diff --git a/src/blocks/TestEditorBlock/index.ts b/src/blocks/TestEditorBlock/index.ts new file mode 100644 index 0000000000..b999fdf7e7 --- /dev/null +++ b/src/blocks/TestEditorBlock/index.ts @@ -0,0 +1,16 @@ +import TestEditorBlock from './TestEditorBlock'; +import testEditorBlockInputs from './form'; + +const TestEditorBlockConfig = { + component: TestEditorBlock, + schema: { + name: 'Test Editor Block', + inputs: testEditorBlockInputs, + }, +}; + +export const TestEditorBlockSchema = { + ['test-editor-block']: {}, +}; + +export default TestEditorBlockConfig; diff --git a/src/common/hooks/usePostMessage.tsx b/src/common/hooks/usePostMessage.tsx new file mode 100644 index 0000000000..8b3d032590 --- /dev/null +++ b/src/common/hooks/usePostMessage.tsx @@ -0,0 +1,134 @@ +import {useCallback, useEffect} from 'react'; + +import { + Action, + Meta, + MetaSource, + PostMessageArgs, + SendOptions, + Subscriber, + WithStoreReducer, +} from '../../common/types'; + +interface UsePostMessageProps { + subscribers: Subscriber[]; + storesWithReducer: WithStoreReducer[]; + targetIframeElement?: HTMLIFrameElement; + urlOrigin?: string; +} + +// TODO: enable/disable via env variables +const DEBUG_MODE = false; + +export const usePostMessage = (props: UsePostMessageProps) => { + const {subscribers, storesWithReducer, targetIframeElement, urlOrigin} = props; + + const metaSource: MetaSource = targetIframeElement ? 'editor' : 'pc'; + const receiveMetaSource: MetaSource = targetIframeElement ? 'pc' : 'editor'; + + const notifySubscribers = useCallback( + (action: Action, source: MetaSource) => { + const meta: Meta = {source}; + + const {type, payload} = action; + + if (!type) { + return; + } + + if (subscribers) { + const foundedSubscribers = subscribers.filter( + (subscriber) => subscriber.action === type, + ); + + for (const storeWithReducer of storesWithReducer) { + storeWithReducer.reducer(action, meta); + } + + if (foundedSubscribers.length) { + for (const subscriber of foundedSubscribers) { + subscriber.handler(payload, meta); + } + } + } + }, + [storesWithReducer, subscribers], + ); + + const sendPostMessage = useCallback( + (args: PostMessageArgs) => { + if (targetIframeElement && targetIframeElement.contentWindow) { + if (targetIframeElement.contentWindow) { + targetIframeElement.contentWindow.postMessage(args, '*'); + } else { + // eslint-disable-next-line no-console + console.error('No Iframe element in Editor'); + } + } else { + window.parent.postMessage(args, '*'); + } + }, + [targetIframeElement], + ); + + const sendMessage = useCallback( + (action: Action, {debug = DEBUG_MODE, direction = 'both'}: SendOptions = {}) => { + if (debug) { + if (metaSource === 'editor') { + // eslint-disable-next-line no-console + console.log(`🔵 Editor ➡️ ${action.type}`, action.payload); + } + + if (metaSource === 'pc') { + // eslint-disable-next-line no-console + console.log(`🟢 Website ➡️ ${action.type}`, action.payload); + } + } + + if (direction === 'both') { + sendPostMessage({action, debug}); + notifySubscribers(action, metaSource); + } else if (direction === metaSource) { + notifySubscribers(action, metaSource); + } else { + sendPostMessage({action, debug}); + } + }, + [metaSource, notifySubscribers, sendPostMessage], + ); + + useEffect(() => { + const onMessage = (e: MessageEvent) => { + const {action, debug}: PostMessageArgs = e.data; + + if (urlOrigin && e.origin !== urlOrigin) { + return; + } + + if (action && action.type) { + if (debug) { + if (metaSource === 'editor') { + // eslint-disable-next-line no-console + console.log(`🔵 ➡️ Editor ${action.type}`, action.payload); + } + + if (metaSource === 'pc') { + // eslint-disable-next-line no-console + console.log(`🟢 ➡️ Website ${action.type}`, action.payload); + } + } + notifySubscribers(action, receiveMetaSource); + } + }; + + window.addEventListener('message', onMessage); + + return () => { + window.removeEventListener('message', onMessage); + }; + }, [metaSource, notifySubscribers, receiveMetaSource, targetIframeElement, urlOrigin]); + + return { + sendMessage, + }; +}; diff --git a/src/common/types/actions/codes.ts b/src/common/types/actions/codes.ts new file mode 100644 index 0000000000..3ec1a59350 --- /dev/null +++ b/src/common/types/actions/codes.ts @@ -0,0 +1,27 @@ +export enum ActionTypes { + // Other Events + UpdateConfigs = 'UPDATE_CONFIGS', + SetHeight = 'SET_HEIGHT', + + // Initial Events + IframeReady = 'IFRAME_READY', + EditorReady = 'EDITOR_READY', + BlocksConfigs = 'BLOCKS_CONFIGS', + + // Insert Events + InsertBlock = 'INSERT_BLOCK', + InsertModeEnable = 'INSERT_MODE_ENABLE', + InsertModeDisable = 'INSERT_MODE_DISABLE', + + // Reorder Events + ReorderBlocks = 'REORDER_BLOCKS', + ReorderModeEnable = 'REORDER_MODE_ENABLE', + ReorderModeDisable = 'REORDER_MODE_DISABLE', + + // Overlay Mode + OverlayModeOnMove = 'OVERLAY_MODE_ON_MOVE', + + // Select Events + SelectBlock = 'SELECT_BLOCK', + UpdateSelectedBlockRect = 'UPDATE_SELECTED_BLOCK_RECT', +} diff --git a/src/common/types/actions/index.ts b/src/common/types/actions/index.ts new file mode 100644 index 0000000000..2c7da200ad --- /dev/null +++ b/src/common/types/actions/index.ts @@ -0,0 +1,31 @@ +import {InitialActions} from './initial'; +import {InsertActions} from './insert'; +import {OtherActions} from './other'; +import {OverlayActions} from './overlay'; +import {ReorderActions} from './reorder'; +import {SelectActions} from './select'; + +export type BaseAction<T extends string = string> = { + type: T; +}; + +export interface UnknownAction extends BaseAction { + // Allows any extra properties to be defined in an action. + [extraProps: string]: unknown; +} + +export type Action = + | InitialActions + | InsertActions + | OverlayActions + | ReorderActions + | SelectActions + | OtherActions; + +export * from './codes'; +export * from './initial'; +export * from './insert'; +export * from './other'; +export * from './overlay'; +export * from './reorder'; +export * from './select'; diff --git a/src/common/types/actions/initial.ts b/src/common/types/actions/initial.ts new file mode 100644 index 0000000000..b313c3a110 --- /dev/null +++ b/src/common/types/actions/initial.ts @@ -0,0 +1,29 @@ +import {ConfigInput} from '../../../common/types'; +import {ItemConfig} from '../index'; + +import {ActionTypes} from './codes'; + +import {UnknownAction} from './index'; + +export interface IframeReadyAction extends UnknownAction { + type: ActionTypes.IframeReady; + payload: { + height: number; + }; +} + +export interface EditorReadyAction extends UnknownAction { + type: ActionTypes.EditorReady; + payload: unknown; +} + +export interface BlocksConfigsAction extends UnknownAction { + type: ActionTypes.BlocksConfigs; + payload: { + blocks: ItemConfig[]; + subBlocks: ItemConfig[]; + global: ConfigInput[]; + }; +} + +export type InitialActions = BlocksConfigsAction | IframeReadyAction | EditorReadyAction; diff --git a/src/common/types/actions/insert.ts b/src/common/types/actions/insert.ts new file mode 100644 index 0000000000..de88f33aaf --- /dev/null +++ b/src/common/types/actions/insert.ts @@ -0,0 +1,24 @@ +import {ActionTypes} from './codes'; + +import {UnknownAction} from './index'; + +export interface InsertBlockAction extends UnknownAction { + type: ActionTypes.InsertBlock; + payload: { + path: number[]; + }; +} + +export interface InsertModeEnableAction extends UnknownAction { + type: ActionTypes.InsertModeEnable; + payload: { + blockType: string; + }; +} + +export interface InsertModeDisableAction extends UnknownAction { + type: ActionTypes.InsertModeDisable; + payload: undefined; +} + +export type InsertActions = InsertBlockAction | InsertModeEnableAction | InsertModeDisableAction; diff --git a/src/common/types/actions/other.ts b/src/common/types/actions/other.ts new file mode 100644 index 0000000000..1f10e9217e --- /dev/null +++ b/src/common/types/actions/other.ts @@ -0,0 +1,21 @@ +import {PageContent} from '../../../models'; + +import {ActionTypes} from './codes'; + +import {UnknownAction} from './index'; + +export interface UpdateConfigsAction extends UnknownAction { + type: ActionTypes.UpdateConfigs; + payload: { + content: PageContent; + }; +} + +export interface SetHeightAction extends UnknownAction { + type: ActionTypes.SetHeight; + payload: { + height: number; + }; +} + +export type OtherActions = UpdateConfigsAction | SetHeightAction; diff --git a/src/common/types/actions/overlay.ts b/src/common/types/actions/overlay.ts new file mode 100644 index 0000000000..dd5340dd27 --- /dev/null +++ b/src/common/types/actions/overlay.ts @@ -0,0 +1,16 @@ +import {ActionTypes} from './codes'; + +import {UnknownAction} from './index'; + +export interface OverlayModeOnMoveAction extends UnknownAction { + type: ActionTypes.OverlayModeOnMove; + payload: { + block?: { + rect: DOMRect; + cursorPosition: 'top' | 'bottom' | 'left' | 'right'; + }; + cursor: {x: number; y: number}; + }; +} + +export type OverlayActions = OverlayModeOnMoveAction; diff --git a/src/common/types/actions/reorder.ts b/src/common/types/actions/reorder.ts new file mode 100644 index 0000000000..e49cc2c043 --- /dev/null +++ b/src/common/types/actions/reorder.ts @@ -0,0 +1,27 @@ +import {ActionTypes} from './codes'; + +import {UnknownAction} from './index'; + +export interface ReorderModeEnableAction extends UnknownAction { + type: ActionTypes.ReorderModeEnable; + payload: { + path: number[]; + }; +} + +export interface ReorderModeDisableAction extends UnknownAction { + type: ActionTypes.ReorderModeDisable; + payload: undefined; +} + +export interface ReorderBlocksAction extends UnknownAction { + type: ActionTypes.ReorderBlocks; + payload: { + path: number[]; + }; +} + +export type ReorderActions = + | ReorderBlocksAction + | ReorderModeEnableAction + | ReorderModeDisableAction; diff --git a/src/common/types/actions/select.ts b/src/common/types/actions/select.ts new file mode 100644 index 0000000000..cf451ab841 --- /dev/null +++ b/src/common/types/actions/select.ts @@ -0,0 +1,20 @@ +import {ActionTypes} from './codes'; + +import {UnknownAction} from './index'; + +export interface SelectBlockAction extends UnknownAction { + type: ActionTypes.SelectBlock; + payload: { + path: number[]; + rect: DOMRect; + }; +} + +export interface UpdateSelectedBlockRectAction extends UnknownAction { + type: ActionTypes.UpdateSelectedBlockRect; + payload: { + rect: DOMRect; + }; +} + +export type SelectActions = SelectBlockAction | UpdateSelectedBlockRectAction; diff --git a/src/common/types/common.ts b/src/common/types/common.ts new file mode 100644 index 0000000000..ce344e43fe --- /dev/null +++ b/src/common/types/common.ts @@ -0,0 +1,17 @@ +import {Action} from './actions'; +import {BlockConfig} from './forms'; +import {Meta} from './messages'; + +export interface ItemConfig { + type: string; + schema: BlockConfig; +} + +export interface WithStoreReducer { + reducer: (action: Action, meta: Meta) => void; +} + +export type Subscriber<A extends Action = Action> = { + action: A['type']; + handler: (payload: A['payload'], meta: Meta) => void; +}; diff --git a/src/common/types/forms.ts b/src/common/types/forms.ts new file mode 100644 index 0000000000..8ade3e18a1 --- /dev/null +++ b/src/common/types/forms.ts @@ -0,0 +1,110 @@ +export interface BlockConfig { + name: string; + inputs: Array<ConfigInput>; + default?: object; +} + +export interface TextInput { + type: 'text'; + name: string; + title: string; +} + +export interface BooleanInput { + type: 'boolean'; + name: string; + title: string; +} + +export interface NumberInput { + type: 'number'; + name: string; + title: string; +} + +export interface TextAreaInput { + type: 'textarea'; + name: string; + title: string; +} + +export interface SelectBaseInput { + type: 'select'; + name: string; + title: string; + view: 'select' | 'radiobutton'; + mode: 'single' | 'multiple'; + enum: Array<{content: string; value: string}>; +} + +export interface SelectSingleInput extends SelectBaseInput { + type: 'select'; + name: string; + title: string; + view: 'select' | 'radiobutton'; + mode: 'single'; + enum: Array<{content: string; value: string}>; +} + +export interface SelectMultipleInput extends SelectBaseInput { + type: 'select'; + name: string; + title: string; + view: 'select'; + mode: 'multiple'; + enum: Array<{content: string; value: string}>; +} + +export interface ObjectInput { + type: 'object'; + name: string; + title: string; + properties: Array<ConfigInput>; +} + +export interface ArrayBaseInput { + type: 'array'; + arrayType: 'object' | 'text'; + name: string; + title: string; + buttonText: string; +} + +export interface ArrayTextInput extends ArrayBaseInput { + arrayType: 'text'; +} + +export interface ArrayObjectInput extends ArrayBaseInput { + arrayType: 'object'; + properties: Array<ConfigInput>; +} + +export interface OneOfInput { + type: 'oneOf'; + name: string; + key?: string; + title: string; + options: { + value: string; + title: string; + properties: Array<ConfigInput>; + }[]; +} + +export interface GeneralProps { + showIf?: string; +} + +export type ConfigInput = ( + | TextInput + | BooleanInput + | NumberInput + | TextAreaInput + | SelectSingleInput + | SelectMultipleInput + | ObjectInput + | ArrayTextInput + | ArrayObjectInput + | OneOfInput +) & + GeneralProps; diff --git a/src/common/types/index.ts b/src/common/types/index.ts new file mode 100644 index 0000000000..31c2745fd6 --- /dev/null +++ b/src/common/types/index.ts @@ -0,0 +1,4 @@ +export * from './actions'; +export * from './common'; +export * from './forms'; +export * from './messages'; diff --git a/src/common/types/messages.ts b/src/common/types/messages.ts new file mode 100644 index 0000000000..b2a905b8e3 --- /dev/null +++ b/src/common/types/messages.ts @@ -0,0 +1,22 @@ +import {Action} from './actions'; + +export interface SendOptions { + direction?: MetaSource | 'both'; + debug?: boolean; +} + +export interface PostMessageArgs { + action: Action; + debug: boolean; +} + +export type SubscriptionFunc = <A extends Action>( + type: A['type'], + payloadCallback: (payload: A['payload'], meta: Meta) => void, +) => void; + +export type MetaSource = 'pc' | 'editor'; + +export interface Meta { + source: MetaSource; +} diff --git a/src/components/BlockBase/BlockBase.scss b/src/components/BlockBase/BlockBase.scss index 01221dfc34..88c2fc6566 100644 --- a/src/components/BlockBase/BlockBase.scss +++ b/src/components/BlockBase/BlockBase.scss @@ -14,12 +14,12 @@ $block: '.#{$ns}block-base'; @include add-specificity(&) { @media only screen and (max-width: map-get($gridBreakpoints, 'sm')) { - margin-top: $indentM; + padding-top: $indentM; padding-bottom: $indentM; - &:first-child { - margin-top: var(--pc-first-block-mobile-indent, #{$indentXL}); - } + //&:first-child { + // padding-top: var(--pc-first-block-mobile-indent, #{$indentXL}); + //} } } diff --git a/src/components/Image/dynamic-form.ts b/src/components/Image/dynamic-form.ts new file mode 100644 index 0000000000..f5f8de747f --- /dev/null +++ b/src/components/Image/dynamic-form.ts @@ -0,0 +1,87 @@ +import _ from 'lodash'; + +import {ConfigInput} from '../../common/types'; + +const devices = ['desktop', 'tablet', 'mobile']; + +const imageBaseInputs: ConfigInput[] = [ + { + type: 'text', + name: 'alt', + title: 'Alternative', + }, + { + type: 'boolean', + name: 'disableCompress', + title: 'Disable Compress', + }, +]; + +const imageStyleInputs: ConfigInput[] = [ + { + type: 'text', + name: 'style.backgroundColor', + title: 'Background Color', + }, + { + type: 'text', + name: 'style.height', + title: 'Height', + }, + { + type: 'text', + name: 'style.width', + title: 'Width', + }, + { + type: 'text', + name: 'style.color', + title: 'Color', + }, +]; + +const devicesInputs: ConfigInput[] = devices.map((device) => ({ + type: 'text', + title: _.capitalize(device), + name: `${device}`, +})); + +export const imageInputs: ConfigInput[] = [ + { + type: 'oneOf', + name: '', + key: 'imageType', + title: 'Image Type', + options: [ + { + title: 'Simple', + value: 'simple', + properties: [ + { + type: 'text', + name: '', // image props + title: 'Image URL', + }, + ], + }, + { + title: 'Complex', + value: 'complex', + properties: [ + { + type: 'text', + name: 'src', + title: 'Source', + }, + ...imageStyleInputs, + ...imageBaseInputs, + ], + }, + { + title: 'Device Based', + value: 'deviseBased', + properties: [...devicesInputs, ...imageBaseInputs], + }, + ], + }, +]; diff --git a/src/components/editor/ChildrenWrap/ChildrenWrap.scss b/src/components/editor/ChildrenWrap/ChildrenWrap.scss new file mode 100644 index 0000000000..c583e86a84 --- /dev/null +++ b/src/components/editor/ChildrenWrap/ChildrenWrap.scss @@ -0,0 +1,8 @@ +@import '../../../../styles/mixins.scss'; +@import '../../../../styles/variables.scss'; + +$block: '.#{$ns}children-wrap'; + +#{$block} { + min-height: 50px; +} diff --git a/src/components/editor/ChildrenWrap/ChildrenWrap.tsx b/src/components/editor/ChildrenWrap/ChildrenWrap.tsx new file mode 100644 index 0000000000..1eb77dca3e --- /dev/null +++ b/src/components/editor/ChildrenWrap/ChildrenWrap.tsx @@ -0,0 +1,27 @@ +import React, {PropsWithChildren, ReactNode, useContext} from 'react'; + +import useEditorBlockMouseEvents from '../../../containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents'; +import {BlockIdContext} from '../../../context/blockIdContext'; +import {block} from '../../../utils'; + +import './ChildrenWrap.scss'; + +const b = block('children-wrap'); + +export interface ChildrenWrapProps extends PropsWithChildren { + checkChildren?: ReactNode; +} + +const ChildrenWrap = (props: ChildrenWrapProps) => { + const {children} = props; + const parentBlockId = useContext(BlockIdContext); + const {onMouseUp, onMouseMove} = useEditorBlockMouseEvents([parentBlockId, 0]); + + return ( + <div className={b()} onMouseMove={onMouseMove} onMouseUp={onMouseUp}> + {children} + </div> + ); +}; + +export default ChildrenWrap; diff --git a/src/components/editor/ItemWrap/ItemWrap.scss b/src/components/editor/ItemWrap/ItemWrap.scss new file mode 100644 index 0000000000..40bbfad312 --- /dev/null +++ b/src/components/editor/ItemWrap/ItemWrap.scss @@ -0,0 +1,12 @@ +@import '../../../../styles/mixins.scss'; +@import '../../../../styles/variables.scss'; + +$block: '.#{$ns}item-wrap'; + +#{$block} { + height: 100%; +} + +.pc-page-constructor_with-editor #{$block} { + cursor: pointer; +} diff --git a/src/components/editor/ItemWrap/ItemWrap.tsx b/src/components/editor/ItemWrap/ItemWrap.tsx new file mode 100644 index 0000000000..4c78420025 --- /dev/null +++ b/src/components/editor/ItemWrap/ItemWrap.tsx @@ -0,0 +1,27 @@ +import React, {PropsWithChildren, useContext} from 'react'; + +import useEditorBlockMouseEvents from '../../../containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents'; +import {BlockIdContext} from '../../../context/blockIdContext'; +import {block} from '../../../utils'; + +import './ItemWrap.scss'; + +const b = block('item-wrap'); + +export interface ItemWrapProps extends PropsWithChildren { + index: number; +} + +const ItemWrap = (props: ItemWrapProps) => { + const {children, index} = props; + const parentBlockId = useContext(BlockIdContext); + const adminBlockMouseEvents = useEditorBlockMouseEvents([parentBlockId, index]); + + return ( + <div className={b()} {...adminBlockMouseEvents}> + {children} + </div> + ); +}; + +export default ItemWrap; diff --git a/src/constructor-items.ts b/src/constructor-items.ts index ea8899796e..61a086d81c 100644 --- a/src/constructor-items.ts +++ b/src/constructor-items.ts @@ -1,3 +1,5 @@ +import React from 'react'; + import { BannerBlock, CardLayoutBlock, @@ -21,6 +23,26 @@ import { TableBlock, TabsBlock, } from './blocks'; +import BannerBlockConfig from './blocks/Banner'; +import CardLayoutBlockConfig from './blocks/CardLayout'; +import CompaniesBlockConfig from './blocks/Companies'; +import ContentLayoutBlockConfig from './blocks/ContentLayout'; +import ExtendedFeaturesBlockConfig from './blocks/ExtendedFeatures'; +import FilterBlockConfig from './blocks/FilterBlock'; +import FormBlockConfig from './blocks/Form'; +import HeaderBlockConfig from './blocks/Header'; +import HeaderSliderBlockConfig from './blocks/HeaderSlider'; +import IconsBlockConfig from './blocks/Icons'; +import InfoBlockConfig from './blocks/Info'; +import MapBlockConfig from './blocks/Map'; +import MediaBlockConfig from './blocks/Media'; +import PromoFeaturesBlockConfig from './blocks/PromoFeaturesBlock'; +import QuestionsBlockConfig from './blocks/Questions'; +import ShareBlockConfig from './blocks/Share'; +import SliderBlockConfig from './blocks/Slider'; +import TableBlockConfig from './blocks/Table'; +import TabsBlockConfig from './blocks/Tabs'; +import {ConfigInput} from './common/types'; import {BlockType, NavigationItemType, SubBlockType} from './models'; import { GithubButton, @@ -42,7 +64,21 @@ import { PriceDetailed, Quote, } from './sub-blocks'; +import BackgroundCardConfig from './sub-blocks/BackgroundCard'; +import BannerCardConfig from './sub-blocks/BannerCard'; +import BasicCardConfig from './sub-blocks/BasicCard'; +import ContentConfig from './sub-blocks/Content'; +import DividerConfig from './sub-blocks/Divider'; +import ImageCardConfig from './sub-blocks/ImageCard'; +import LayoutItemConfig from './sub-blocks/LayoutItem/form'; +import MediaCardConfig from './sub-blocks/MediaCard'; +import PriceCardConfig from './sub-blocks/PriceCard'; +import PriceDetailedConfig from './sub-blocks/PriceDetailed'; +import QuoteConfig from './sub-blocks/Quote'; +/** + * @deprecated use blockDataMap + **/ export const blockMap = { [BlockType.SliderOldBlock]: SliderOldBlock, [BlockType.ExtendedFeaturesBlock]: ExtendedFeaturesBlock, @@ -67,6 +103,9 @@ export const blockMap = { [BlockType.SliderBlock]: SliderBlock, }; +/** + * @deprecated use blockDataMap + **/ export const subBlockMap = { [SubBlockType.Divider]: Divider, [SubBlockType.PriceDetailed]: PriceDetailed, @@ -88,3 +127,48 @@ export const navItemMap = { [NavigationItemType.Link]: NavigationLink, [NavigationItemType.GithubButton]: GithubButton, }; + +export interface BlockData { + // TODO: remove any + // eslint-disable-next-line @typescript-eslint/no-explicit-any + component: React.ComponentType<any>; + schema: { + name: string; + inputs: ConfigInput[]; + default?: object; + }; +} + +export const blockDataMap: Record<string, BlockData> = { + [BlockType.ExtendedFeaturesBlock]: ExtendedFeaturesBlockConfig, + [BlockType.PromoFeaturesBlock]: PromoFeaturesBlockConfig, + [BlockType.QuestionsBlock]: QuestionsBlockConfig, + [BlockType.BannerBlock]: BannerBlockConfig, + [BlockType.CompaniesBlock]: CompaniesBlockConfig, + [BlockType.MediaBlock]: MediaBlockConfig, + [BlockType.InfoBlock]: InfoBlockConfig, + [BlockType.TableBlock]: TableBlockConfig, + [BlockType.TabsBlock]: TabsBlockConfig, + [BlockType.HeaderBlock]: HeaderBlockConfig, + [BlockType.IconsBlock]: IconsBlockConfig, + [BlockType.HeaderSliderBlock]: HeaderSliderBlockConfig, + [BlockType.CardLayoutBlock]: CardLayoutBlockConfig, + [BlockType.ContentLayoutBlock]: ContentLayoutBlockConfig, + [BlockType.ShareBlock]: ShareBlockConfig, + [BlockType.MapBlock]: MapBlockConfig, + [BlockType.FilterBlock]: FilterBlockConfig, + [BlockType.FormBlock]: FormBlockConfig, + [BlockType.SliderBlock]: SliderBlockConfig, + + [SubBlockType.Divider]: DividerConfig, + [SubBlockType.PriceDetailed]: PriceDetailedConfig, + [SubBlockType.MediaCard]: MediaCardConfig, + [SubBlockType.BannerCard]: BannerCardConfig, + [SubBlockType.LayoutItem]: LayoutItemConfig, + [SubBlockType.BackgroundCard]: BackgroundCardConfig, + [SubBlockType.BasicCard]: BasicCardConfig, + [SubBlockType.Content]: ContentConfig, + [SubBlockType.Quote]: QuoteConfig, + [SubBlockType.PriceCard]: PriceCardConfig, + [SubBlockType.ImageCard]: ImageCardConfig, +}; diff --git a/src/containers/PageConstructor/PageConstructor.tsx b/src/containers/PageConstructor/PageConstructor.tsx index ccb8a8a240..801c6e5547 100644 --- a/src/containers/PageConstructor/PageConstructor.tsx +++ b/src/containers/PageConstructor/PageConstructor.tsx @@ -1,18 +1,19 @@ -import * as React from 'react'; +import {ReactNode, useContext, useMemo, useState} from 'react'; -import '@diplodoc/transform/dist/js/yfm.js'; +import '@diplodoc/transform/dist/js/yfm'; +import {ThemeProvider} from '@gravity-ui/uikit'; import BackgroundMedia from '../../components/BackgroundMedia/BackgroundMedia'; import BrandFooter from '../../components/BrandFooter/BrandFooter'; import RootCn from '../../components/RootCn'; import {blockMap, navItemMap, subBlockMap} from '../../constructor-items'; import {AnimateContext} from '../../context/animateContext'; +import {useEditorStore} from '../../context/editorContext'; import {InnerContext} from '../../context/innerContext'; import {ProjectSettingsContext} from '../../context/projectSettingsContext'; import {useTheme} from '../../context/theme'; -import {Grid} from '../../grid'; +import useEditorInitialize from '../../hooks/useEditorInitialize'; import { - BlockType, BlockTypes, CustomConfig, CustomItems, @@ -24,17 +25,9 @@ import { SubBlockTypes, } from '../../models'; import Layout from '../../navigation/containers/Layout/Layout'; -import { - block as cnBlock, - getCustomItems, - getCustomTypes, - getHeaderBlock, - getOrderedBlocks, - getThemedValue, -} from '../../utils'; - -import {ConstructorBlocks} from './components/ConstructorBlocks'; -import {ConstructorHeader} from './components/ConstructorItem'; +import {block as cnBlock, getCustomItems, getCustomTypes, getThemedValue} from '../../utils'; + +import {ConstructorBlocks} from './components'; import {ConstructorRow} from './components/ConstructorRow'; import './PageConstructor.scss'; @@ -49,7 +42,7 @@ export interface PageConstructorProps { content?: PageContent; shouldRenderBlock?: ShouldRenderBlock; custom?: CustomConfig; - renderMenu?: () => React.ReactNode; + renderMenu?: () => ReactNode; navigation?: NavigationData; isBranded?: boolean; microdata?: { @@ -68,7 +61,13 @@ export const Constructor = (props: PageConstructorProps) => { microdata, } = props; - const {context} = React.useMemo( + const [content, setContent] = useState<PageContent>({blocks, background}); + const theme = useTheme(); + + const {initialized} = useEditorStore(); + useEditorInitialize({content, setContent}); + + const {context} = useMemo( () => ({ context: { blockTypes: [...BlockTypes, ...getCustomTypes(['blocks', 'headers'], custom)], @@ -98,31 +97,23 @@ export const Constructor = (props: PageConstructorProps) => { [custom, shouldRenderBlock, microdata], ); - const theme = useTheme(); - - const header = getHeaderBlock(blocks, context.headerBlockTypes); - const restBlocks = getOrderedBlocks(blocks, context.headerBlockTypes); - const themedBackground = getThemedValue(background, theme); + const restBlocks = content.blocks; + const themedBackground = getThemedValue(content.background, theme); return ( <InnerContext.Provider value={context}> - <RootCn className={b()}> + <RootCn className={b('', {['with-editor']: initialized})}> <div className={b('wrapper')}> {themedBackground && ( <BackgroundMedia {...themedBackground} className={b('background')} /> )} <Layout navigation={navigation}> {renderMenu && renderMenu()} - {header && ( - <ConstructorHeader data={header} blockKey={BlockType.HeaderBlock} /> + {restBlocks && ( + <ConstructorRow> + <ConstructorBlocks items={restBlocks} /> + </ConstructorRow> )} - <Grid> - {restBlocks && ( - <ConstructorRow> - <ConstructorBlocks items={restBlocks} /> - </ConstructorRow> - )} - </Grid> </Layout> {isBranded && <BrandFooter />} </div> @@ -132,12 +123,14 @@ export const Constructor = (props: PageConstructorProps) => { }; export const PageConstructor = (props: PageConstructorProps) => { - const {isAnimationEnabled = true} = React.useContext(ProjectSettingsContext); + const {isAnimationEnabled = true} = useContext(ProjectSettingsContext); const {content: {animated = isAnimationEnabled} = {}, ...rest} = props; return ( - <AnimateContext.Provider value={{animated}}> - <Constructor content={props.content} {...rest} /> - </AnimateContext.Provider> + <ThemeProvider> + <AnimateContext.Provider value={{animated}}> + <Constructor content={props.content} {...rest} /> + </AnimateContext.Provider> + </ThemeProvider> ); }; diff --git a/src/containers/PageConstructor/Provider.tsx b/src/containers/PageConstructor/Provider.tsx index 7e3203039a..12cc5535af 100644 --- a/src/containers/PageConstructor/Provider.tsx +++ b/src/containers/PageConstructor/Provider.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import {DEFAULT_THEME} from '../../components/constants'; import {AnalyticsContext, AnalyticsContextProps} from '../../context/analyticsContext'; +import {EditorProvider} from '../../context/editorContext'; import { DEFAULT_FORMS_CONTEXT_VALUE, FormsContext, @@ -11,6 +12,7 @@ import {ImageContext, ImageContextProps} from '../../context/imageContext'; import {LocaleContext, LocaleContextProps} from '../../context/localeContext'; import {LocationContext, LocationContextProps} from '../../context/locationContext'; import {MapsContext, MapsContextType, initialMapValue} from '../../context/mapsContext/mapsContext'; +import {PostMessageProvider} from '../../context/messagesContext'; import {MobileContext} from '../../context/mobileContext'; import { ProjectSettingsContext, @@ -64,6 +66,8 @@ export const PageConstructorProvider = ( <FormsContext.Provider value={forms} />, <SSRContext.Provider value={{isServer: ssrConfig?.isServer}} />, <WindowWidthProvider />, + <EditorProvider />, + <PostMessageProvider />, ].reduceRight((prev, provider) => React.cloneElement(provider, {}, prev), children); /* eslint-enable react/jsx-key */ diff --git a/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.scss b/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.scss index b6c7cbc86b..3f49e00862 100644 --- a/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.scss +++ b/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.scss @@ -6,3 +6,7 @@ $block: '.#{$ns}constructor-block'; #{$block} { @include indents(&); } + +.pc-page-constructor_with-editor #{$block} { + cursor: pointer; +} diff --git a/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx b/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx index b41b5bc182..6f66977543 100644 --- a/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx +++ b/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx @@ -7,6 +7,8 @@ import {BlockDecoration} from '../../../../customization/BlockDecoration'; import {BlockDecorationProps, ConstructorBlock as ConstructorBlockType} from '../../../../models'; import {block} from '../../../../utils'; +import useEditorBlockMouseEvents from './hooks/useEditorBlockMouseEvents'; + import './ConstructorBlock.scss'; interface ConstructorBlockProps extends Pick<BlockDecorationProps, 'index'> { @@ -20,6 +22,8 @@ export const ConstructorBlock = ({ data, children, }: React.PropsWithChildren<ConstructorBlockProps>) => { + const adminBlockMouseEvents = useEditorBlockMouseEvents([index]); + const {type} = data; const blockBaseProps = React.useMemo( () => pick(data, ['anchor', 'visible', 'resetPaddings', 'indent']), @@ -27,10 +31,12 @@ export const ConstructorBlock = ({ ); return ( - <BlockDecoration type={type} index={index} {...blockBaseProps}> - <BlockBase className={b({type})} {...blockBaseProps}> - {children} - </BlockBase> - </BlockDecoration> + <div {...adminBlockMouseEvents}> + <BlockDecoration type={type} index={index} {...blockBaseProps}> + <BlockBase className={b({type})} {...blockBaseProps}> + {children} + </BlockBase> + </BlockDecoration> + </div> ); }; diff --git a/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx b/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx new file mode 100644 index 0000000000..b0c7787712 --- /dev/null +++ b/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx @@ -0,0 +1,130 @@ +import React, {useCallback, useContext} from 'react'; + +import {ActionTypes} from '../../../../../common/types/actions'; +import {EditorContext, useEditorStore} from '../../../../../context/editorContext'; +import {useMessageSender} from '../../../../../context/messagesContext/hooks/useMessageSender'; +import {getCursorPositionOverElement} from '../../../../../utils/editor'; + +const useEditorBlockMouseEvents = (arrayIndex: number[]) => { + const sendMessage = useMessageSender(); + const {manipulateOverlayMode} = useEditorStore(); + const {setActiveElement} = useContext(EditorContext); + + const onMouseUp = useCallback( + (event: React.MouseEvent) => { + event.preventDefault(); + event.stopPropagation(); + if (manipulateOverlayMode) { + const rect = event.currentTarget.getClientRects().item(0); + if (rect) { + const position = getCursorPositionOverElement(rect, event); + + let newLastIndex = arrayIndex[arrayIndex.length - 1]; + + if (position === 'right' || position === 'bottom') { + newLastIndex += 1; + } + + const newArrayIndex = [ + ...arrayIndex.slice(0, arrayIndex.length - 1), + newLastIndex, + ]; + + if (manipulateOverlayMode === 'insert') { + sendMessage({ + type: ActionTypes.InsertBlock, + payload: { + path: newArrayIndex, + }, + }); + } else if (manipulateOverlayMode === 'reorder') { + sendMessage({ + type: ActionTypes.ReorderBlocks, + payload: { + path: newArrayIndex, + }, + }); + } + } + } + }, + [arrayIndex, manipulateOverlayMode, sendMessage], + ); + + const onMouseMove = useCallback( + (event: React.MouseEvent) => { + event.preventDefault(); + event.stopPropagation(); + if (manipulateOverlayMode) { + const rect = event.currentTarget.getClientRects().item(0); + if (rect) { + const cursorPosition = getCursorPositionOverElement(rect, event); + sendMessage({ + type: ActionTypes.OverlayModeOnMove, + payload: { + block: {rect, cursorPosition}, + cursor: { + x: event.clientX, + y: event.clientY, + }, + }, + }); + } + } + }, + [manipulateOverlayMode, sendMessage], + ); + + const onMouseLeave = useCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + if (manipulateOverlayMode) { + const rect = e.currentTarget.getClientRects().item(0); + if (rect) { + sendMessage({ + type: ActionTypes.OverlayModeOnMove, + payload: { + cursor: { + x: e.clientX, + y: e.clientY, + }, + }, + }); + } + } else { + } + }, + [manipulateOverlayMode, sendMessage], + ); + + const onClick = useCallback( + (e: React.MouseEvent<HTMLDivElement>) => { + e.stopPropagation(); + if (!manipulateOverlayMode) { + const element = e.currentTarget; + const rect = element.getClientRects().item(0); + if (rect) { + setActiveElement(element); + + sendMessage({ + type: ActionTypes.SelectBlock, + payload: { + path: arrayIndex, + rect: rect, + }, + }); + } + } + }, + [arrayIndex, manipulateOverlayMode, sendMessage, setActiveElement], + ); + + return { + onClick, + onMouseMove, + onMouseUp, + onMouseLeave, + }; +}; + +export default useEditorBlockMouseEvents; diff --git a/src/containers/PageConstructor/components/ConstructorBlocks/ConstructorBlocks.tsx b/src/containers/PageConstructor/components/ConstructorBlocks/ConstructorBlocks.tsx index 5ce480b4b2..2c9f432043 100644 --- a/src/containers/PageConstructor/components/ConstructorBlocks/ConstructorBlocks.tsx +++ b/src/containers/PageConstructor/components/ConstructorBlocks/ConstructorBlocks.tsx @@ -19,11 +19,15 @@ export interface ConstructorBlocksProps { items: ConstructorBlockType[]; } -export const ConstructorBlocks = ({items}: ConstructorBlocksProps) => { - const {blockTypes, loadables, itemMap, shouldRenderBlock} = React.useContext(InnerContext); +export const ConstructorBlocks: React.FC<ConstructorBlocksProps> = ({items}) => { + const {blockTypes, subBlockTypes, loadables, itemMap, shouldRenderBlock} = + React.useContext(InnerContext); + + const allBlocks = [...blockTypes, ...subBlockTypes]; const renderer = ( parentId = '', + withoutConstructorBlockWrapper = false, item: ConstructorBlockType, index: number, ): React.ReactElement | null => { @@ -63,17 +67,17 @@ export const ConstructorBlocks = ({items}: ConstructorBlocksProps) => { } else { let children; if ('children' in item && item.children) { - children = (item.children as SubBlock[]).map(renderer.bind(null, blockId)); + children = (item.children as SubBlock[]).map(renderer.bind(null, blockId, true)); } itemElement = ( - <ConstructorItem data={item} key={blockId} blockKey={blockId}> + <ConstructorItem data={item} key={blockId} blockKey={index}> {children} </ConstructorItem> ); } - return blockTypes.includes(item.type) ? ( + return allBlocks.includes(item.type) && !withoutConstructorBlockWrapper ? ( //TODO: replace ConstructorBlock (and delete it) with BlockBase when all // components relying on constructor inner structure like Slider or blog-constructor will be refactored <ConstructorBlock data={item} key={blockId} index={index}> @@ -84,5 +88,5 @@ export const ConstructorBlocks = ({items}: ConstructorBlocksProps) => { ); }; - return <React.Fragment>{items.map(renderer.bind(null, ''))}</React.Fragment>; + return <React.Fragment>{items.map(renderer.bind(null, '', false))}</React.Fragment>; }; diff --git a/src/containers/PageConstructor/components/ConstructorItem/ConstructorItem.tsx b/src/containers/PageConstructor/components/ConstructorItem/ConstructorItem.tsx index 182f206410..6524dbd10c 100644 --- a/src/containers/PageConstructor/components/ConstructorItem/ConstructorItem.tsx +++ b/src/containers/PageConstructor/components/ConstructorItem/ConstructorItem.tsx @@ -7,7 +7,7 @@ import {BlockType, ConstructorBlock} from '../../../../models'; export interface ConstructorItemProps { data: ConstructorBlock; - blockKey: string; + blockKey: number; } export const ConstructorItem = ({ @@ -23,7 +23,7 @@ export const ConstructorItem = ({ >; return ( - <BlockIdContext.Provider value={blockKey}> + <BlockIdContext.Provider value={blockKey} key={blockKey}> <Component {...rest}>{children}</Component> </BlockIdContext.Provider> ); diff --git a/src/containers/PageConstructor/components/ConstructorLoadable/ConstructorLoadable.tsx b/src/containers/PageConstructor/components/ConstructorLoadable/ConstructorLoadable.tsx index 4488dd6f2c..8d29d4f045 100644 --- a/src/containers/PageConstructor/components/ConstructorLoadable/ConstructorLoadable.tsx +++ b/src/containers/PageConstructor/components/ConstructorLoadable/ConstructorLoadable.tsx @@ -20,7 +20,7 @@ export const ConstructorLoadable = (props: ConstructorLoadableProps) => { >; return ( - <BlockIdContext.Provider value={blockKey} key={blockKey}> + <BlockIdContext.Provider value={Number(blockKey)} key={blockKey}> <Loadable key={blockKey} block={block} diff --git a/src/containers/PageConstructor/components/ConstructorRow/ConstructorRow.tsx b/src/containers/PageConstructor/components/ConstructorRow/ConstructorRow.tsx index d3b7077ec6..5173b89ad5 100644 --- a/src/containers/PageConstructor/components/ConstructorRow/ConstructorRow.tsx +++ b/src/containers/PageConstructor/components/ConstructorRow/ConstructorRow.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; -import {Col, Row} from '../../../../grid'; import {block} from '../../../../utils'; import './ConstructorRow.scss'; @@ -8,8 +7,4 @@ import './ConstructorRow.scss'; const b = block('constructor-row'); export const ConstructorRow = ({children}: React.PropsWithChildren<{}>) => - children ? ( - <Row className={b()}> - <Col>{children}</Col> - </Row> - ) : null; + children ? <div className={b()}>{children}</div> : null; diff --git a/src/context/blockIdContext/blockIdContext.ts b/src/context/blockIdContext/blockIdContext.ts index 289ed964df..b621d734c2 100644 --- a/src/context/blockIdContext/blockIdContext.ts +++ b/src/context/blockIdContext/blockIdContext.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -export type BlockIdContextProp = string; +export type BlockIdContextProp = number; -export const BlockIdContext = React.createContext<BlockIdContextProp>(''); +export const BlockIdContext = React.createContext<BlockIdContextProp>(NaN); diff --git a/src/context/editorContext/editorContext.ts b/src/context/editorContext/editorContext.ts new file mode 100644 index 0000000000..b8ef739ef9 --- /dev/null +++ b/src/context/editorContext/editorContext.ts @@ -0,0 +1,18 @@ +/** + * Context for managing internal Editor states + * Same exist in Editor + **/ + +import React from 'react'; + +import {StoreApi} from 'zustand'; + +import {EditorStore} from './store'; + +export interface EditorContextProps { + state?: StoreApi<EditorStore>; + activeElement?: HTMLElement; + setActiveElement: (element: HTMLElement) => void; +} + +export const EditorContext = React.createContext<EditorContextProps>({setActiveElement: () => {}}); diff --git a/src/context/editorContext/editorProvider.tsx b/src/context/editorContext/editorProvider.tsx new file mode 100644 index 0000000000..4812ee416c --- /dev/null +++ b/src/context/editorContext/editorProvider.tsx @@ -0,0 +1,27 @@ +import React, {PropsWithChildren, useRef, useState} from 'react'; + +import {StoreApi} from 'zustand'; + +import {EditorContext} from './editorContext'; +import {EditorStore, createEditorStore} from './store'; + +export const EditorProvider: React.FC<PropsWithChildren> = ({children}) => { + const storeRef = useRef<StoreApi<EditorStore>>(); + const [activeElement, setActiveElement] = useState<HTMLElement | undefined>(); + + if (!storeRef.current) { + storeRef.current = createEditorStore(); + } + + return ( + <EditorContext.Provider + value={{ + state: storeRef.current, + activeElement: activeElement, + setActiveElement: setActiveElement, + }} + > + {children} + </EditorContext.Provider> + ); +}; diff --git a/src/context/editorContext/hooks/useEditorStore.ts b/src/context/editorContext/hooks/useEditorStore.ts new file mode 100644 index 0000000000..4f2a346cde --- /dev/null +++ b/src/context/editorContext/hooks/useEditorStore.ts @@ -0,0 +1,17 @@ +import {useContext} from 'react'; + +import {useStore} from 'zustand'; + +import {EditorContext} from '../editorContext'; + +export const useEditorStore = () => { + const {state} = useContext(EditorContext); + + if (!state) { + throw new Error('Missing EditorContext'); + } + + return useStore(state); +}; + +export default useEditorStore; diff --git a/src/context/editorContext/index.ts b/src/context/editorContext/index.ts new file mode 100644 index 0000000000..c7b944eea3 --- /dev/null +++ b/src/context/editorContext/index.ts @@ -0,0 +1,4 @@ +export * from './editorContext'; +export * from './editorProvider'; +export * from './hooks/useEditorStore'; +export * from './store'; diff --git a/src/context/editorContext/store.ts b/src/context/editorContext/store.ts new file mode 100644 index 0000000000..c8f65aabc4 --- /dev/null +++ b/src/context/editorContext/store.ts @@ -0,0 +1,68 @@ +import {ActionTypes, WithStoreReducer} from '../../common/types'; +import {initializeStore} from '../../utils/store'; + +export interface EditorState { + manipulateOverlayMode: 'insert' | 'reorder' | false; + isSelectActive: boolean; + initialized: boolean; +} + +export interface EditorMethods extends WithStoreReducer {} + +export type EditorStore = EditorState & EditorMethods; + +export const createEditorStore = initializeStore<EditorState, EditorMethods>( + { + manipulateOverlayMode: false, + isSelectActive: false, + initialized: false, + }, + (set, _get) => ({ + reducer: (action) => { + switch (action.type) { + case ActionTypes.EditorReady: { + set((state) => ({ + ...state, + initialized: true, + })); + break; + } + case ActionTypes.ReorderModeEnable: { + set((state) => ({ + ...state, + manipulateOverlayMode: 'reorder', + })); + break; + } + case ActionTypes.ReorderModeDisable: { + set((state) => ({ + ...state, + manipulateOverlayMode: false, + })); + break; + } + case ActionTypes.InsertModeEnable: { + set((state) => ({ + ...state, + manipulateOverlayMode: 'insert', + })); + break; + } + case ActionTypes.InsertModeDisable: { + set((state) => ({ + ...state, + manipulateOverlayMode: false, + })); + break; + } + case ActionTypes.SelectBlock: { + set((state) => ({ + ...state, + isSelectActive: true, + })); + break; + } + } + }, + }), +); diff --git a/src/context/messagesContext/hooks/useMessageObserver.tsx b/src/context/messagesContext/hooks/useMessageObserver.tsx new file mode 100644 index 0000000000..296ca9deae --- /dev/null +++ b/src/context/messagesContext/hooks/useMessageObserver.tsx @@ -0,0 +1,23 @@ +import {DependencyList, useEffect} from 'react'; + +import {Action} from '../../../common/types'; + +import {useMessagesStore} from './useMessagesStore'; + +export const useMessageObserver = <A extends Action>( + type: A['type'], + callback: (payload: A['payload']) => void, + deps: DependencyList = [], +) => { + const {subscribe, unsubscribe} = useMessagesStore(); + + useEffect(() => { + subscribe(type, callback); + + return () => { + unsubscribe(type, callback); + }; + }, deps); // eslint-disable-line react-hooks/exhaustive-deps +}; + +export default useMessageObserver; diff --git a/src/context/messagesContext/hooks/useMessageSender.tsx b/src/context/messagesContext/hooks/useMessageSender.tsx new file mode 100644 index 0000000000..57f25eef0e --- /dev/null +++ b/src/context/messagesContext/hooks/useMessageSender.tsx @@ -0,0 +1,10 @@ +import {useContext} from 'react'; + +import {PostMessageContext} from '../messagesContext'; + +export const useMessageSender = () => { + const {sendMessage} = useContext(PostMessageContext); + return sendMessage; +}; + +export default useMessageSender; diff --git a/src/context/messagesContext/hooks/useMessagesStore.tsx b/src/context/messagesContext/hooks/useMessagesStore.tsx new file mode 100644 index 0000000000..1805cd32a6 --- /dev/null +++ b/src/context/messagesContext/hooks/useMessagesStore.tsx @@ -0,0 +1,17 @@ +import {useContext} from 'react'; + +import {useStore} from 'zustand'; + +import {PostMessageContext} from '../messagesContext'; + +export const useMessagesStore = () => { + const {state} = useContext(PostMessageContext); + + if (!state) { + throw new Error('Missing PostMessageContext'); + } + + return useStore(state); +}; + +export default useMessagesStore; diff --git a/src/context/messagesContext/index.ts b/src/context/messagesContext/index.ts new file mode 100644 index 0000000000..eda561f123 --- /dev/null +++ b/src/context/messagesContext/index.ts @@ -0,0 +1,5 @@ +export * from './messagesContext'; +export * from './messagesProvider'; +export * from './hooks/useMessageSender'; +export * from './hooks/useMessageObserver'; +export * from './hooks/useMessagesStore'; diff --git a/src/context/messagesContext/messagesContext.tsx b/src/context/messagesContext/messagesContext.tsx new file mode 100644 index 0000000000..502b6a2c87 --- /dev/null +++ b/src/context/messagesContext/messagesContext.tsx @@ -0,0 +1,21 @@ +/** + * Context for sending messages between Editor and App + * Same exist in Editor + **/ + +import React from 'react'; + +import {StoreApi} from 'zustand'; + +import {Action, SendOptions} from '../../common/types'; + +import {MessagesStore} from './store'; + +export interface PostMessageContextProps { + state?: StoreApi<MessagesStore>; + sendMessage: (action: Action, options?: SendOptions) => void; +} + +export const PostMessageContext = React.createContext<PostMessageContextProps>({ + sendMessage: () => {}, +}); diff --git a/src/context/messagesContext/messagesProvider.tsx b/src/context/messagesContext/messagesProvider.tsx new file mode 100644 index 0000000000..86515bd344 --- /dev/null +++ b/src/context/messagesContext/messagesProvider.tsx @@ -0,0 +1,38 @@ +import React, {PropsWithChildren, useRef} from 'react'; + +import {StoreApi} from 'zustand'; + +import {usePostMessage} from '../../common/hooks/usePostMessage'; +import {WithStoreReducer} from '../../common/types'; +import {useEditorStore} from '../editorContext'; + +import {PostMessageContext} from './messagesContext'; +import {MessagesStore, createMessagesStore} from './store'; + +export const PostMessageProvider = ({children}: PropsWithChildren) => { + const storeRef = useRef<StoreApi<MessagesStore>>(); + + if (!storeRef.current) { + storeRef.current = createMessagesStore(); + } + + const editorStore = useEditorStore(); + + const storesWithReducer: WithStoreReducer[] = [editorStore]; + + const {sendMessage} = usePostMessage({ + subscribers: storeRef.current?.getState().subscribers, + storesWithReducer, + }); + + return ( + <PostMessageContext.Provider + value={{ + state: storeRef.current, + sendMessage: sendMessage, + }} + > + {children} + </PostMessageContext.Provider> + ); +}; diff --git a/src/context/messagesContext/store.ts b/src/context/messagesContext/store.ts new file mode 100644 index 0000000000..ccfa312282 --- /dev/null +++ b/src/context/messagesContext/store.ts @@ -0,0 +1,44 @@ +import {Subscriber, SubscriptionFunc} from '../../common/types'; +import {initializeStore} from '../../utils/store'; + +export interface MessagesState { + subscribers: Subscriber[]; +} + +export interface MessagesMethods { + subscribe: SubscriptionFunc; + unsubscribe: SubscriptionFunc; +} + +export type MessagesStore = MessagesState & MessagesMethods; + +export const createMessagesStore = initializeStore<MessagesState, MessagesMethods>( + { + subscribers: [], + }, + (set, _get) => ({ + subscribe: (type, payloadCallback) => { + set((state) => ({ + ...state, + subscribers: [...state.subscribers, {action: type, handler: payloadCallback}], + })); + + return () => { + set((state) => ({ + ...state, + subscribers: state.subscribers.filter( + ({handler, action}) => payloadCallback !== handler || type !== action, + ), + })); + }; + }, + unsubscribe: (type, payloadCallback) => { + set((state) => ({ + ...state, + subscribers: state.subscribers.filter( + ({handler, action}) => payloadCallback !== handler || type !== action, + ), + })); + }, + }), +); diff --git a/src/editor-v2/components/BigOverlay/BigOverlay.scss b/src/editor-v2/components/BigOverlay/BigOverlay.scss new file mode 100644 index 0000000000..e9d6abbf9e --- /dev/null +++ b/src/editor-v2/components/BigOverlay/BigOverlay.scss @@ -0,0 +1,33 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}big-overlay'; + +#{$block} { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + pointer-events: none; + + $border: 3px var(--g-color-line-brand) solid; + + &__border { + pointer-events: none; + position: absolute; + width: 40px; + height: 40px; + margin-left: -20px; + margin-top: -20px; + background-color: var(--g-color-base-brand); + display: flex; + align-items: center; + justify-content: center; + border-radius: 10px; + //transition: top .1s ease, left .1s ease, height .1s ease, width .1s ease; + } +} diff --git a/src/editor-v2/components/BigOverlay/BigOverlay.tsx b/src/editor-v2/components/BigOverlay/BigOverlay.tsx new file mode 100644 index 0000000000..02fa4679a9 --- /dev/null +++ b/src/editor-v2/components/BigOverlay/BigOverlay.tsx @@ -0,0 +1,69 @@ +import React, {useContext, useState} from 'react'; + +import {Stop} from '@gravity-ui/icons'; + +import { + ActionTypes, + InsertModeDisableAction, + OverlayModeOnMoveAction, + ReorderModeDisableAction, +} from '../../../common/types'; +import {ClassNameProps} from '../../../models'; +import {block} from '../../../utils'; +import {IframeContext} from '../../context/iframeContext'; +import {useMessageObserver} from '../../context/messagesContext'; + +import './BigOverlay.scss'; + +const b = block('big-overlay'); + +interface BigOverlayProps extends ClassNameProps {} + +const BigOverlay: React.FC<BigOverlayProps> = ({className}) => { + const {iframeElement} = useContext(IframeContext); + const [mousePosition, setMousePosition] = useState<{x: number; y: number} | undefined>( + undefined, + ); + + useMessageObserver<OverlayModeOnMoveAction>( + ActionTypes.OverlayModeOnMove, + (payload, meta) => { + if (payload && payload.cursor) { + const {x, y} = payload.cursor; + const iframeRect = iframeElement?.getClientRects().item(0); + if (iframeRect) { + const newX = meta.source === 'editor' ? x : x + iframeRect.x; + const newY = meta.source === 'editor' ? y : y + iframeRect.y; + setMousePosition({x: newX, y: newY}); + } + } + }, + [iframeElement], + ); + + useMessageObserver<InsertModeDisableAction>(ActionTypes.InsertModeDisable, () => { + setMousePosition(undefined); + }); + + useMessageObserver<ReorderModeDisableAction>(ActionTypes.ReorderModeDisable, () => { + setMousePosition(undefined); + }); + + return ( + <div className={b(null, className)}> + {mousePosition ? ( + <div + className={b('border')} + style={{ + top: mousePosition.y, + left: mousePosition.x, + }} + > + <Stop height={20} width={20} /> + </div> + ) : null} + </div> + ); +}; + +export default BigOverlay; diff --git a/src/editor-v2/components/BlockConfig/BlockConfig.scss b/src/editor-v2/components/BlockConfig/BlockConfig.scss new file mode 100644 index 0000000000..6af7fb8a77 --- /dev/null +++ b/src/editor-v2/components/BlockConfig/BlockConfig.scss @@ -0,0 +1,28 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}block-config'; + +#{$block} { + padding: 12px; + + &__title { + margin-bottom: 16px; + margin-top: 8px; + @include text-subheader-3; + } + + &__empty { + display: flex; + justify-content: center; + align-items: center; + text-align: center; + height: 100%; + width: 100%; + + @include text-body-2; + } +} diff --git a/src/editor-v2/components/BlockConfig/BlockConfig.tsx b/src/editor-v2/components/BlockConfig/BlockConfig.tsx new file mode 100644 index 0000000000..fcb71593d6 --- /dev/null +++ b/src/editor-v2/components/BlockConfig/BlockConfig.tsx @@ -0,0 +1,53 @@ +import React from 'react'; + +import _ from 'lodash'; + +import {ClassNameProps} from '../../../models'; +import {block} from '../../../utils'; +import {useContentConfigStore} from '../../context/contentConfig'; +import {useEditorStore} from '../../context/editorContext'; +import {generateChildrenPathFromArray} from '../../utils'; +import DynamicForm, {DynamicFormValue} from '../DynamicForm/DynamicForm'; + +import './BlockConfig.scss'; + +const b = block('block-config'); + +interface BlockConfigProps extends ClassNameProps {} + +const BlockConfig: React.FC<BlockConfigProps> = ({className}) => { + const {selectedBlock} = useEditorStore(); + const {config, blocks, subBlocks, updateField} = useContentConfigStore(); + + const currentBlockPath = selectedBlock?.path + ? generateChildrenPathFromArray(selectedBlock?.path) + : '[]'; + + const currentConfig = _.get(config.blocks, currentBlockPath || ''); + const currentSchema = [...blocks, ...subBlocks].find(({type}) => type === currentConfig?.type); + + const onUpdate = (key: string, value: DynamicFormValue) => { + updateField('blocks' + currentBlockPath + '.' + key, value); + }; + + if (!currentConfig) { + return <div className={b('empty')}>Select block for start</div>; + } + + if (!currentSchema) { + return <div className={b('empty')}>Not supported: {currentConfig.type}</div>; + } + + return ( + <div className={b(null, className)}> + <div className={b('title')}>{currentSchema.schema.name}</div> + <DynamicForm + contentConfig={currentConfig} + blockConfig={currentSchema.schema.inputs} + onUpdate={onUpdate} + /> + </div> + ); +}; + +export default BlockConfig; diff --git a/src/editor-v2/components/BlocksList/BlocksList.scss b/src/editor-v2/components/BlocksList/BlocksList.scss new file mode 100644 index 0000000000..e2b9c301ed --- /dev/null +++ b/src/editor-v2/components/BlocksList/BlocksList.scss @@ -0,0 +1,42 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}blocks-list'; + +#{$block} { + padding: 12px; + display: flex; + flex-direction: column; + gap: 8px; + + &__card { + padding: 8px; + cursor: pointer; + user-select: none; + margin-bottom: 8px; + + &:last-child { + margin-bottom: 0; + } + + &:active { + background-color: var(--g-color-base-generic-hover); + } + } + + &__title { + @include text-subheader-3; + margin-bottom: 10px; + } + + &__section { + margin-bottom: 20px; + + &:last-child { + margin-bottom: 0; + } + } +} diff --git a/src/editor-v2/components/BlocksList/BlocksList.tsx b/src/editor-v2/components/BlocksList/BlocksList.tsx new file mode 100644 index 0000000000..f2af642112 --- /dev/null +++ b/src/editor-v2/components/BlocksList/BlocksList.tsx @@ -0,0 +1,43 @@ +import React, {PropsWithChildren, useCallback} from 'react'; + +import {Card} from '@gravity-ui/uikit'; + +import {ActionTypes, ItemConfig} from '../../../common/types'; +import {block} from '../../../utils'; +import {useContentConfigStore} from '../../context/contentConfig'; +import {useMessageSender} from '../../context/messagesContext'; + +import './BlocksList.scss'; + +const b = block('blocks-list'); + +export interface BlocksListProps { + blocks: ItemConfig[]; +} + +const BlocksList = (_p: PropsWithChildren<BlocksListProps>) => { + const {blocks} = useContentConfigStore(); + const sendMessage = useMessageSender(); + + const onMouseDown = useCallback( + (blockType: string) => { + sendMessage({type: ActionTypes.InsertModeEnable, payload: {blockType}}); + }, + [sendMessage], + ); + + return ( + <div className={b()}> + <div className={b('section')}> + <div className={b('title')}>Blocks</div> + {blocks.map(({type, schema: {name}}) => ( + <Card key={type} className={b('card')} onMouseDown={() => onMouseDown(type)}> + {name} + </Card> + ))} + </div> + </div> + ); +}; + +export default BlocksList; diff --git a/src/editor-v2/components/DynamicForm/DynamicForm.scss b/src/editor-v2/components/DynamicForm/DynamicForm.scss new file mode 100644 index 0000000000..4628d70ab2 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/DynamicForm.scss @@ -0,0 +1,8 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; + +$block: '.#{$ns}dynamic-form'; + +#{$block} { + //padding-left: 16px; +} diff --git a/src/editor-v2/components/DynamicForm/DynamicForm.tsx b/src/editor-v2/components/DynamicForm/DynamicForm.tsx new file mode 100644 index 0000000000..8bef3e94dc --- /dev/null +++ b/src/editor-v2/components/DynamicForm/DynamicForm.tsx @@ -0,0 +1,219 @@ +import React, {useCallback} from 'react'; + +import _ from 'lodash'; + +import {ConfigInput} from '../../../common/types'; +import {ClassNameProps, PageContent} from '../../../models'; +import {block} from '../../../utils'; + +import ArrayDynamicField from './Fields/Array/Array'; +import BooleanDynamicField from './Fields/Boolean/Boolean'; +import NumberDynamicField from './Fields/Number/Number'; +import ObjectDynamicField from './Fields/Object/Object'; +import OneOfDynamicField from './Fields/OneOf/OneOf'; +import SelectDynamicField from './Fields/Select/Select'; +import TextDynamicField from './Fields/Text/Text'; +import TextAreaDynamicField from './Fields/TextArea/TextArea'; +import {getContent, getFullPath} from './utils'; + +import './DynamicForm.scss'; + +const b = block('dynamic-form'); + +export type DynamicFormValue = string | number | [] | object | boolean | PageContent | undefined; + +interface DynamicFormProps extends ClassNameProps { + blockConfig: Array<ConfigInput>; + contentConfig: PageContent; + onUpdate: (key: string, value: DynamicFormValue) => void; +} + +const DynamicForm: React.FC<DynamicFormProps> = (props) => { + const {blockConfig, onUpdate, contentConfig} = props; + const inputs = blockConfig; + + const getData = useCallback( + (variable: string) => { + if (variable.startsWith('block.')) { + const purePath = variable.replace('block.', ''); + return _.get(contentConfig, purePath); + } + + if ( + (variable.startsWith(`'`) && variable.endsWith(`'`)) || + (variable.startsWith(`"`) && variable.endsWith(`"`)) + ) { + // @ts-ignore TODO: replaceAll types + return variable.replaceAll(`'`, '').replaceAll(`"`, ''); + } + + return undefined; + }, + [contentConfig], + ); + + const decide = useCallback( + (showIf: string): boolean => { + const parts = showIf.split(' '); + + if (!(parts.length === 3)) { + // eslint-disable-next-line no-console + console.log('Something bad happened in showIf, ignored'); + return true; + } + + const [firstVariable, equals, secondVariable] = parts; + + const data1 = getData(firstVariable); + const data2 = getData(secondVariable); + + if (equals === '===') { + return data1 === data2; + } else { + return data1 !== data2; + } + }, + [getData], + ); + + const renderInput = useCallback( + (input: ConfigInput) => { + const fieldPath = input.name; + const fieldValue = getContent(contentConfig, input.name); + + if (input.showIf) { + const decision = decide(input.showIf); + + if (!decision) { + return <div>Hidden Field: {input.name}</div>; + } + } + + // Text, Select, Boolean and etc + const onSimpleDynamicFieldUpdate = (value: DynamicFormValue) => { + onUpdate(fieldPath, value); + }; + + // Array and Objects + const onComplexDynamicFieldUpdate = (key: string, value: DynamicFormValue) => { + onUpdate(getFullPath(fieldPath, key), value); + }; + + switch (input.type) { + case 'text': { + return ( + <TextDynamicField + onRefresh={(value) => onUpdate(fieldPath, value)} + title={input.title} + value={fieldValue} + onUpdate={onSimpleDynamicFieldUpdate} + /> + ); + } + case 'boolean': { + return ( + <BooleanDynamicField + onRefresh={(value) => onUpdate(fieldPath, value)} + title={input.title} + value={fieldValue} + onUpdate={onSimpleDynamicFieldUpdate} + /> + ); + } + case 'textarea': { + return ( + <TextAreaDynamicField + onRefresh={(value) => onUpdate(fieldPath, value)} + title={input.title} + value={fieldValue} + onUpdate={onSimpleDynamicFieldUpdate} + /> + ); + } + case 'select': { + return ( + <SelectDynamicField + onRefresh={(value) => onUpdate(fieldPath, value)} + input={input} + value={fieldValue} + onUpdate={onSimpleDynamicFieldUpdate} + /> + ); + } + case 'number': { + return ( + <NumberDynamicField + onRefresh={(value) => onUpdate(fieldPath, value)} + title={input.title} + value={fieldValue} + onUpdate={onSimpleDynamicFieldUpdate} + /> + ); + } + case 'object': { + if (!input || !('properties' in input)) { + return null; + } + + return ( + <ObjectDynamicField + onRefresh={(value) => onUpdate(fieldPath, value)} + blockConfig={input.properties} + title={input.title} + value={fieldValue} + onUpdate={onComplexDynamicFieldUpdate} + /> + ); + } + case 'array': { + return ( + <ArrayDynamicField + blockConfig={input} + title={input.title} + values={fieldValue} + onUpdate={onComplexDynamicFieldUpdate} + /> + ); + } + case 'oneOf': { + if (!input || !('options' in input)) { + return null; + } + + return ( + <OneOfDynamicField + inputConfig={input} + contentConfig={contentConfig} + onUpdate={onComplexDynamicFieldUpdate} + /> + ); + } + default: { + return <div>Ignore {JSON.stringify(input)}</div>; + } + } + }, + [contentConfig, decide, onUpdate], + ); + + const sortedInputs = inputs.sort((x, y) => { + const nestingFieldTypes = ['object', 'array', 'oneOf']; + if (nestingFieldTypes.includes(x.type)) { + return 1; + } + if (nestingFieldTypes.includes(y.type)) { + return -1; + } + return 0; + }); + + return ( + <div className={b()}> + {sortedInputs.map((input, index) => ( + <React.Fragment key={index}>{renderInput(input)}</React.Fragment> + ))} + </div> + ); +}; + +export default DynamicForm; diff --git a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss new file mode 100644 index 0000000000..c739699967 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss @@ -0,0 +1,72 @@ +@import '../../../../../styles/variables.scss'; +@import '../../../../../styles/mixins.scss'; +@import '../../../styles/root.scss'; +@import '../../../styles/variables.scss'; +@import '../../../styles/mixins.scss'; + +$block: '.#{$ns}field-base'; + +#{$block} { + $class: &; + + position: relative; + + &:hover { + & > #{$class}__top > #{$class}__button { + opacity: 1; + } + } + + &__top { + display: flex; + align-items: center; + gap: 4px; + margin-bottom: 8px; + } + + &__foldable { + display: flex; + align-items: center; + cursor: pointer; + } + + &__non-foldable { + &:before { + //content: '•'; + position: absolute; + right: calc(100% + 5px); + } + } + + &__button { + transition: opacity 0.3s ease; + opacity: 0; + } + + &__title { + @include text-body-2; + + &_size { + &_s { + @include text-body-1; + } + &_m { + @include text-body-2; + } + &_l { + @include text-body-3; + } + } + } + + margin-top: 20px; + + &:first-child { + margin-top: 0; + } + + &__arrow-toggle { + //position: absolute; + //right: calc(100%); + } +} diff --git a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx new file mode 100644 index 0000000000..8b5860ace6 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx @@ -0,0 +1,73 @@ +import React, {PropsWithChildren, useState} from 'react'; + +import {ArrowRotateLeft} from '@gravity-ui/icons'; +import {ArrowToggle, Button, Icon} from '@gravity-ui/uikit'; +import _ from 'lodash'; + +import {ClassNameProps} from '../../../../models'; +import {block} from '../../../../utils'; + +import './FieldBase.scss'; + +const b = block('field-base'); + +export interface FieldBaseParams { + title?: string; + textSize?: 's' | 'm' | 'l'; + onRefresh?: (value: undefined) => void; + expandable?: boolean; +} + +export interface FieldBaseProps extends ClassNameProps, PropsWithChildren, FieldBaseParams {} + +const FieldBase: React.FC<FieldBaseProps> = (props) => { + const {className, title, textSize, children, onRefresh, expandable = false} = props; + const [showChildren, setShowChildren] = useState(!expandable); + + const titleComponent = React.useMemo(() => { + if (title) { + const defaultTitle = ( + <div className={b('title', {size: textSize})}>{_.capitalize(title)}</div> + ); + + if (expandable) { + return ( + <div className={b('foldable')} onClick={() => setShowChildren(!showChildren)}> + <ArrowToggle + direction={showChildren ? 'bottom' : 'right'} + className={b('arrow-toggle')} + /> + {defaultTitle} + </div> + ); + } + + return <div className={b('non-foldable')}>{defaultTitle}</div>; + } + + return null; + }, [expandable, showChildren, textSize, title]); + + return ( + <div className={b(null, className)}> + {title && ( + <div className={b('top')}> + {titleComponent} + {onRefresh && ( + <Button + className={b('button')} + onClick={() => onRefresh(undefined)} + view={'flat'} + size={'xs'} + > + <Icon data={ArrowRotateLeft} size={14} /> + </Button> + )} + </div> + )} + {(!title || showChildren) && <div className={b('children')}>{children}</div>} + </div> + ); +}; + +export default FieldBase; diff --git a/src/editor-v2/components/DynamicForm/Fields/Array/Array.scss b/src/editor-v2/components/DynamicForm/Fields/Array/Array.scss new file mode 100644 index 0000000000..973665582e --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/Array/Array.scss @@ -0,0 +1,51 @@ +@import '../../../../../../styles/variables.scss'; +@import '../../../../../../styles/mixins.scss'; + +$block: '.#{$ns}array-dynamic-field'; + +#{$block} { + &__card { + padding: 12px; + margin-top: 12px; + + &:first-child { + margin-bottom: 0; + } + } + + &__row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + + margin-top: 10px; + + &:first-child { + margin-top: 0; + } + } + + &__card-head { + margin-bottom: 8px; + } + + &__row-title { + @include text-subheader-3; + } + + &__row-title, + &__row-field { + flex: 1; + } + + &__empty { + padding: 10px; + display: flex; + justify-content: center; + } + + &__add-button { + margin-top: 12px; + } +} diff --git a/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx b/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx new file mode 100644 index 0000000000..823665bbf5 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx @@ -0,0 +1,158 @@ +import React, {useCallback} from 'react'; + +import {Plus} from '@gravity-ui/icons'; +import {Button, Card, Icon} from '@gravity-ui/uikit'; + +import {ArrayObjectInput, ArrayTextInput} from '../../../../../common/types'; +import {ClassNameProps, PageContent} from '../../../../../models'; +import {block} from '../../../../../utils'; +import {removeFromArray, swapArrayItems} from '../../../../utils'; +import DynamicForm, {DynamicFormValue} from '../../DynamicForm'; +import FieldBase from '../../FieldBase/FieldBase'; +import Text from '../Text/Text'; + +import ItemButton from './ItemButton/ItemButton'; + +import './Array.scss'; + +const b = block('array-dynamic-field'); + +type ArrayInput = ArrayTextInput | ArrayObjectInput; + +interface ArrayFieldProps extends ClassNameProps { + title: string; + values: Array<DynamicFormValue>; + onUpdate: (key: string, value: DynamicFormValue) => void; + blockConfig: ArrayInput; +} + +const ArrayDynamicField: React.FC<ArrayFieldProps> = (props) => { + const {title, values, onUpdate, className, blockConfig} = props; + + const haveItems = values && Array.isArray(values) && values.length; + + const onAddItem = useCallback(() => { + if (blockConfig.arrayType === 'text') { + onUpdate('', haveItems ? [...values, ''] : ['']); + } else if (blockConfig.arrayType === 'object') { + onUpdate('', haveItems ? [...values, {}] : [{}]); + } + }, [blockConfig.arrayType, haveItems, onUpdate, values]); + + const onDeleteItem = useCallback( + (index: number) => { + if (Array.isArray(values)) { + const newArray = removeFromArray(values, index); + onUpdate('', newArray); + } + }, + [onUpdate, values], + ); + + const onReorderItem = useCallback( + (index: number, placement: 'up' | 'down') => { + if (Array.isArray(values)) { + const newArray = swapArrayItems( + values, + index, + placement === 'up' ? index - 1 : index + 1, + ); + onUpdate('', newArray); + } + }, + [onUpdate, values], + ); + + const renderInput = useCallback( + (value: DynamicFormValue, index: number) => { + const arrayItemButton = ( + <ItemButton + onRemove={() => onDeleteItem(index)} + onReorderUp={() => onReorderItem(index, 'up')} + onReorderDown={() => onReorderItem(index, 'down')} + disableReorderUp={index === 0} + disableReorderDown={Boolean(haveItems) && values.length === index + 1} + /> + ); + + switch (blockConfig.arrayType) { + case 'text': { + return ( + <div className={b('row')}> + <Text + className={b('row-field')} + value={String(value)} + onUpdate={(updateValue) => onUpdate(`[${index}]`, updateValue)} + onRefresh={(updatedValue) => onUpdate('', updatedValue)} + /> + {arrayItemButton} + </div> + ); + } + case 'object': { + if (!blockConfig.properties) { + return null; + } + return ( + <Card key={index} className={b('card')}> + <div className={`${b('row')} ${b('card-head')}`}> + <div className={b('row-title')}>#{index}</div> + {arrayItemButton} + </div> + <DynamicForm + contentConfig={value as PageContent} + blockConfig={blockConfig.properties} + onUpdate={(key, updateValue) => + onUpdate(`[${index}].${key}`, updateValue) + } + /> + </Card> + ); + } + default: { + return null; + } + } + }, + [blockConfig, haveItems, onDeleteItem, onReorderItem, onUpdate, values], + ); + + const renderInputs = useCallback(() => { + if (haveItems) { + const renderItems = values + .map(renderInput) + .filter(Boolean) as unknown as React.ReactNode[]; + return ( + <React.Fragment> + {renderItems} + <Button className={b('add-button')} onClick={onAddItem}> + <Icon data={Plus} /> + {blockConfig.buttonText} + </Button> + </React.Fragment> + ); + } else { + return ( + <div className={b('empty')}> + <Button className={b('add-button')} onClick={onAddItem}> + <Icon data={Plus} /> + Please, add new item + </Button> + </div> + ); + } + }, [blockConfig.buttonText, haveItems, onAddItem, renderInput, values]); + + return ( + <FieldBase + title={title} + className={b(null, className)} + onRefresh={(value) => onUpdate('', value)} + expandable + > + <Card className={b('card')}>{renderInputs()}</Card> + </FieldBase> + ); +}; + +export default ArrayDynamicField; diff --git a/src/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx b/src/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx new file mode 100644 index 0000000000..d566f49e55 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx @@ -0,0 +1,77 @@ +import React, {Fragment, useCallback, useRef, useState} from 'react'; + +import {ArrowDown, ArrowUp, EllipsisVertical, TrashBin} from '@gravity-ui/icons'; +import {Button, Icon, Menu, Popup} from '@gravity-ui/uikit'; + +import {ClassNameProps} from '../../../../../../models'; +import {block} from '../../../../../../utils'; + +const b = block('array-item-button'); + +interface ItemButtonProps extends ClassNameProps { + onRemove: () => void; + onReorderUp: () => void; + disableReorderUp?: boolean; + onReorderDown: () => void; + disableReorderDown?: boolean; +} + +const ItemButton: React.FC<ItemButtonProps> = (props) => { + const { + className, + onRemove, + onReorderUp, + onReorderDown, + disableReorderUp = false, + disableReorderDown = false, + } = props; + const buttonRef = useRef(null); + const [isOpen, setIsOpen] = useState(false); + + const onMenuItemClickWrapper = useCallback((callback: () => void) => { + return () => { + setIsOpen(false); + callback(); + }; + }, []); + + return ( + <Fragment> + <Button className={b(null, className)} ref={buttonRef} onClick={() => setIsOpen(true)}> + <Icon data={EllipsisVertical} /> + </Button> + <Popup + placement={'bottom-end'} + anchorRef={buttonRef} + open={isOpen} + onOutsideClick={() => setIsOpen(false)} + > + <Menu> + <Menu.Item + theme={'danger'} + onClick={onMenuItemClickWrapper(onRemove)} + iconStart={<Icon data={TrashBin} />} + > + Remove + </Menu.Item> + <Menu.Item + disabled={disableReorderUp} + onClick={onMenuItemClickWrapper(onReorderUp)} + iconStart={<Icon data={ArrowUp} />} + > + Reorder Up + </Menu.Item> + <Menu.Item + disabled={disableReorderDown} + onClick={onMenuItemClickWrapper(onReorderDown)} + iconStart={<Icon data={ArrowDown} />} + > + Reorder Down + </Menu.Item> + </Menu> + </Popup> + </Fragment> + ); +}; + +export default ItemButton; diff --git a/src/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx b/src/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx new file mode 100644 index 0000000000..2ac37c4ef6 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +import {Switch} from '@gravity-ui/uikit'; + +import {ClassNameProps} from '../../../../../models'; +import {block} from '../../../../../utils'; +import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; + +const b = block('boolean-dynamic-field'); + +interface BooleanProps extends ClassNameProps, FieldBaseParams { + value: string; + onUpdate: (value: boolean | undefined) => void; +} + +const BooleanDynamicField: React.FC<BooleanProps> = (props) => { + const {title, value, onUpdate, className} = props; + + return ( + <FieldBase title={title} className={b(null, className)} onRefresh={onUpdate}> + <Switch checked={Boolean(value)} onUpdate={onUpdate} /> + </FieldBase> + ); +}; + +export default BooleanDynamicField; diff --git a/src/editor-v2/components/DynamicForm/Fields/Number/Number.tsx b/src/editor-v2/components/DynamicForm/Fields/Number/Number.tsx new file mode 100644 index 0000000000..2b01e67989 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/Number/Number.tsx @@ -0,0 +1,30 @@ +import React from 'react'; + +import {TextInput} from '@gravity-ui/uikit'; + +import {ClassNameProps} from '../../../../../models'; +import {block} from '../../../../../utils'; +import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; + +const b = block('number-dynamic-field'); + +interface NumberDynamicFieldProps extends ClassNameProps, FieldBaseParams { + value: string; + onUpdate: (value: number | undefined) => void; +} + +const NumberDynamicField: React.FC<NumberDynamicFieldProps> = (props) => { + const {title, value, onUpdate, className} = props; + + const onUpdateFunc = (updateValue: string) => { + onUpdate(Number(updateValue)); + }; + + return ( + <FieldBase title={title} className={b(null, className)} onRefresh={onUpdate}> + <TextInput value={value || ''} onUpdate={onUpdateFunc} /> + </FieldBase> + ); +}; + +export default NumberDynamicField; diff --git a/src/editor-v2/components/DynamicForm/Fields/Object/Object.scss b/src/editor-v2/components/DynamicForm/Fields/Object/Object.scss new file mode 100644 index 0000000000..b3aa2610b6 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/Object/Object.scss @@ -0,0 +1,10 @@ +@import '../../../../../../styles/variables.scss'; +@import '../../../../../../styles/mixins.scss'; + +$block: '.#{$ns}object-dynamic-field'; + +#{$block} { + &__card { + padding: 12px; + } +} diff --git a/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx b/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx new file mode 100644 index 0000000000..ff8bf177c5 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx @@ -0,0 +1,38 @@ +import React from 'react'; + +import {Card} from '@gravity-ui/uikit'; + +import {ConfigInput} from '../../../../../common/types'; +import {ClassNameProps, PageContent} from '../../../../../models'; +import {block} from '../../../../../utils'; +import DynamicForm, {DynamicFormValue} from '../../DynamicForm'; +import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; + +import './Object.scss'; + +const b = block('object-dynamic-field'); + +interface ObjectDynamicFieldProps extends ClassNameProps, FieldBaseParams { + value: PageContent; + onUpdate: (key: string, value: DynamicFormValue) => void; + blockConfig: Array<ConfigInput>; +} + +const ObjectDynamicField: React.FC<ObjectDynamicFieldProps> = (props) => { + const {title, value, onUpdate, className, blockConfig} = props; + + return ( + <FieldBase + title={title} + className={b(null, className)} + onRefresh={(updatedValue) => onUpdate('', updatedValue)} + expandable + > + <Card className={b('card')}> + <DynamicForm contentConfig={value} blockConfig={blockConfig} onUpdate={onUpdate} /> + </Card> + </FieldBase> + ); +}; + +export default ObjectDynamicField; diff --git a/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss b/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss new file mode 100644 index 0000000000..1e5ec1c1a9 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss @@ -0,0 +1,14 @@ +@import '../../../../../../styles/variables.scss'; +@import '../../../../../../styles/mixins.scss'; + +$block: '.#{$ns}oneof-dynamic-field'; + +#{$block} { + &__card { + padding: 12px; + } + + &__radio { + margin-bottom: 12px; + } +} diff --git a/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx b/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx new file mode 100644 index 0000000000..4dafbe0793 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx @@ -0,0 +1,78 @@ +import React, {useCallback, useMemo, useState} from 'react'; + +import {Card, RadioButton} from '@gravity-ui/uikit'; + +import {OneOfInput} from '../../../../../common/types'; +import {ClassNameProps, PageContent} from '../../../../../models'; +import {block} from '../../../../../utils'; +import DynamicForm, {DynamicFormValue} from '../../DynamicForm'; +import FieldBase from '../../FieldBase/FieldBase'; + +import './OneOf.scss'; + +const b = block('oneof-dynamic-field'); + +interface OneOfDynamicFieldProps extends ClassNameProps { + contentConfig: PageContent; + onUpdate: (key: string, value: DynamicFormValue) => void; + inputConfig: OneOfInput; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const getOneOfContentConfig = (contentConfig: any, name: string) => { + if (name) { + return contentConfig ? contentConfig[name] : {}; + } + return contentConfig; +}; + +const OneOfDynamicField: React.FC<OneOfDynamicFieldProps> = (props) => { + const {contentConfig, onUpdate, className, inputConfig} = props; + + const defaultValue = inputConfig.options[0].value; + + const [oneOfMetaValue, setOneOfMetaValue] = useState(defaultValue); + + const oneOfContentConfig = getOneOfContentConfig(contentConfig, inputConfig.name); + const oneOfChosenOption = useMemo( + () => + inputConfig.options.find( + ({value: foundOneOfValue}) => foundOneOfValue === oneOfMetaValue, + ), + [inputConfig.options, oneOfMetaValue], + ); + + const onUpdateOneOf = useCallback((value: string) => { + setOneOfMetaValue(value); + }, []); + + return ( + <FieldBase + title={inputConfig.title} + className={b(null, className)} + onRefresh={(value) => onUpdate('', value)} + expandable + > + <Card className={b('card')}> + <RadioButton + className={b('radio')} + options={inputConfig.options.map((option) => ({ + content: option.title, + value: option.value, + }))} + value={oneOfMetaValue} + onUpdate={onUpdateOneOf} + /> + {oneOfChosenOption && ( + <DynamicForm + blockConfig={oneOfChosenOption.properties} + contentConfig={oneOfContentConfig} + onUpdate={onUpdate} + /> + )} + </Card> + </FieldBase> + ); +}; + +export default OneOfDynamicField; diff --git a/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx b/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx new file mode 100644 index 0000000000..06eff057a5 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx @@ -0,0 +1,42 @@ +import React from 'react'; + +import {RadioButton, Select} from '@gravity-ui/uikit'; + +import {SelectMultipleInput, SelectSingleInput} from '../../../../../common/types'; +import {ClassNameProps} from '../../../../../models'; +import {block} from '../../../../../utils'; +import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; + +const b = block('select-field'); + +type SelectInput = SelectSingleInput | SelectMultipleInput; + +interface SelectDynamicFieldProps extends ClassNameProps, FieldBaseParams { + input: SelectInput; + value: string; + onUpdate: (value: string | undefined) => void; +} + +const SelectDynamicField: React.FC<SelectDynamicFieldProps> = (props) => { + const {input, value, onUpdate, className} = props; + + const inputView = input.view || 'radiobutton'; + + return ( + <FieldBase title={input.title} className={b(null, className)} onRefresh={onUpdate}> + {inputView === 'select' && ( + <Select + placeholder={'Value'} + value={value ? [value] : []} + onUpdate={([selectValue]) => onUpdate(selectValue)} + options={input.enum} + /> + )} + {inputView === 'radiobutton' && ( + <RadioButton options={input.enum} value={value} onUpdate={onUpdate} /> + )} + </FieldBase> + ); +}; + +export default SelectDynamicField; diff --git a/src/editor-v2/components/DynamicForm/Fields/Text/Text.tsx b/src/editor-v2/components/DynamicForm/Fields/Text/Text.tsx new file mode 100644 index 0000000000..b16101e86b --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/Text/Text.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +import {TextInput} from '@gravity-ui/uikit'; + +import {ClassNameProps} from '../../../../../models'; +import {block} from '../../../../../utils'; +import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; + +const b = block('text-dynamic-field'); + +interface TextDynamicFieldProps extends ClassNameProps, FieldBaseParams { + value: string; + onUpdate: (value: string | undefined) => void; +} + +const TextDynamicField: React.FC<TextDynamicFieldProps> = (props) => { + const {title, value, onUpdate, className} = props; + + return ( + <FieldBase title={title} className={b(null, className)} onRefresh={onUpdate}> + <TextInput value={value || ''} onUpdate={onUpdate} /> + </FieldBase> + ); +}; + +export default TextDynamicField; diff --git a/src/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx b/src/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx new file mode 100644 index 0000000000..4ca783696b --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +import {TextArea} from '@gravity-ui/uikit'; + +import {ClassNameProps} from '../../../../../models'; +import {block} from '../../../../../utils'; +import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; + +const b = block('textarea-dynamic-field'); + +interface TextAreaDynamicFieldProps extends ClassNameProps, FieldBaseParams { + value: string; + onUpdate: (value: string | undefined) => void; +} + +const TextAreaDynamicField: React.FC<TextAreaDynamicFieldProps> = (props) => { + const {title, value, onUpdate, className} = props; + + return ( + <FieldBase title={title} className={b(null, className)} onRefresh={onUpdate}> + <TextArea minRows={5} maxRows={20} value={value || ''} onUpdate={onUpdate} /> + </FieldBase> + ); +}; + +export default TextAreaDynamicField; diff --git a/src/editor-v2/components/DynamicForm/utils.ts b/src/editor-v2/components/DynamicForm/utils.ts new file mode 100644 index 0000000000..b52c3381a9 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/utils.ts @@ -0,0 +1,23 @@ +import _ from 'lodash'; + +import {PageContent} from '../../../models'; + +export const getFullPath = (path: string, name: string) => { + if (!path && !name) { + return ''; + } + + if (!path) { + return name; + } + + if (!name) { + return path; + } + + return path + '.' + name; +}; + +export const getContent = (contentConfig: PageContent, path: string) => { + return path ? _.get(contentConfig, path) : contentConfig; +}; diff --git a/src/editor-v2/components/GlobalConfig/GlobalConfig.scss b/src/editor-v2/components/GlobalConfig/GlobalConfig.scss new file mode 100644 index 0000000000..d54900eb40 --- /dev/null +++ b/src/editor-v2/components/GlobalConfig/GlobalConfig.scss @@ -0,0 +1,17 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}global-config'; + +#{$block} { + padding: 12px; + + &__title { + margin-bottom: 16px; + margin-top: 8px; + @include text-subheader-3; + } +} diff --git a/src/editor-v2/components/GlobalConfig/GlobalConfig.tsx b/src/editor-v2/components/GlobalConfig/GlobalConfig.tsx new file mode 100644 index 0000000000..e203439d21 --- /dev/null +++ b/src/editor-v2/components/GlobalConfig/GlobalConfig.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +import {ClassNameProps} from '../../../models'; +import {block} from '../../../utils'; +import {useContentConfigStore} from '../../context/contentConfig'; +import DynamicForm, {DynamicFormValue} from '../DynamicForm/DynamicForm'; + +import './GlobalConfig.scss'; + +const b = block('global-config'); + +interface GlobalConfigProps extends ClassNameProps {} + +const GlobalConfig: React.FC<GlobalConfigProps> = ({className}) => { + const {global, updateField, config} = useContentConfigStore(); + + const onUpdate = (key: string, value: DynamicFormValue) => { + updateField(key, value); + }; + + return ( + <div className={b(null, className)}> + <div className={b('title')}>Global Config</div> + <DynamicForm contentConfig={config} blockConfig={global} onUpdate={onUpdate} /> + </div> + ); +}; + +export default GlobalConfig; diff --git a/src/editor-v2/components/MiddleScreen/MiddleScreen.scss b/src/editor-v2/components/MiddleScreen/MiddleScreen.scss new file mode 100644 index 0000000000..bb24c8168a --- /dev/null +++ b/src/editor-v2/components/MiddleScreen/MiddleScreen.scss @@ -0,0 +1,52 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}middle-screen'; + +#{$block} { + height: 100%; + width: 100%; + display: flex; + flex-direction: column; + + &__topbar { + height: 50px; + } + + &__canvas { + height: 100%; + width: 100%; + overflow-y: scroll; + position: relative; + } + + &__overlay { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + pointer-events: none; + } + + &__border { + position: absolute; + border: 3px var(--g-color-line-brand) solid; + border-radius: 10px; + } + + &__loading { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + background: var(--g-color-base-background); + display: flex; + align-items: center; + justify-content: center; + } +} diff --git a/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx b/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx new file mode 100644 index 0000000000..eebe7469c0 --- /dev/null +++ b/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx @@ -0,0 +1,52 @@ +import React, {useContext} from 'react'; + +import {Loader} from '@gravity-ui/uikit'; + +import {ClassNameProps} from '../../../models'; +import {block} from '../../../utils'; +import {useEditorStore} from '../../context/editorContext'; +import {IframeContext, useIframeStore} from '../../context/iframeContext'; +import Overlay from '../Overlay/Overlay'; +import TopBar from '../TopBar/TopBar'; + +import './MiddleScreen.scss'; + +const b = block('middle-screen'); + +interface MiddleScreenProps extends ClassNameProps {} + +const MiddleScreen: React.FC<MiddleScreenProps> = ({className}) => { + const {url, height} = useIframeStore(); + const {initialized} = useEditorStore(); + const {setIframeElement} = useContext(IframeContext); + + return ( + <div className={b(null, className)}> + <div className={b('topbar')}> + <TopBar /> + </div> + <div className={b('canvas', {hidden: !initialized})}> + <iframe + ref={(element) => { + if (element) { + setIframeElement(element); + } + }} + className={b('iframe')} + src={url} + height={`${height}px`} + width="100%" + frameBorder="0" + /> + <Overlay className={b('overlay')} /> + {!initialized && ( + <div className={b('loading')}> + <Loader size={'l'} /> + </div> + )} + </div> + </div> + ); +}; + +export default MiddleScreen; diff --git a/src/editor-v2/components/Overlay/Overlay.scss b/src/editor-v2/components/Overlay/Overlay.scss new file mode 100644 index 0000000000..61e1b68a86 --- /dev/null +++ b/src/editor-v2/components/Overlay/Overlay.scss @@ -0,0 +1,68 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}overlay'; + +#{$block} { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + pointer-events: none; + + $border: 5px var(--g-color-line-brand) solid; + $border-2: 3px var(--g-color-base-selection) solid; + + &__border { + position: absolute; + border: 3px var(--g-color-line-brand) solid; + box-sizing: border-box; + box-shadow: 4px 4px 8px 0 var(--g-color-sfx-shadow); + } + + &__line { + position: absolute; + border: $border-2; + + &_position { + &_top { + border-top: $border; + } + + &_bottom { + border-bottom: $border; + } + + &_left { + border-left: $border; + } + + &_right { + border-right: $border; + } + } + } + + &__actions { + pointer-events: auto; + position: absolute; + bottom: -40px; + left: 0; + + display: flex; + align-items: center; + background: transparent; + box-shadow: 4px 4px 8px 0 var(--g-color-sfx-shadow); + border-radius: 20px; + } + + &__action-button { + &_grip { + cursor: grab; + } + } +} diff --git a/src/editor-v2/components/Overlay/Overlay.tsx b/src/editor-v2/components/Overlay/Overlay.tsx new file mode 100644 index 0000000000..ff231cc9f9 --- /dev/null +++ b/src/editor-v2/components/Overlay/Overlay.tsx @@ -0,0 +1,133 @@ +import React, {useCallback, useState} from 'react'; + +import {Copy, Grip, TrashBin} from '@gravity-ui/icons'; +import {Button, Icon} from '@gravity-ui/uikit'; + +import { + ActionTypes, + InsertModeDisableAction, + OverlayModeOnMoveAction, + ReorderModeDisableAction, +} from '../../../common/types'; +import {ClassNameProps} from '../../../models'; +import {block} from '../../../utils'; +import {useContentConfigStore} from '../../context/contentConfig'; +import {useEditorStore} from '../../context/editorContext'; +import {useIframeStore} from '../../context/iframeContext'; +import {useMessageObserver, useMessageSender} from '../../context/messagesContext'; + +import './Overlay.scss'; + +const b = block('overlay'); + +interface OverlayProps extends ClassNameProps {} + +interface InsertLineProps { + top: number; + left: number; + height: number; + width: number; + position: string; +} + +const Overlay: React.FC<OverlayProps> = ({className}) => { + const {selectedBlock} = useEditorStore(); + const [insertLineBox, setInsertLineBox] = useState<InsertLineProps | undefined>(undefined); + const {height} = useIframeStore(); + const {deleteBlock, duplicateBlock} = useContentConfigStore(); + const sendMessage = useMessageSender(); + + const margin = 0; + + useMessageObserver<OverlayModeOnMoveAction>(ActionTypes.OverlayModeOnMove, (payload) => { + if (payload && payload.block) { + const {rect, cursorPosition} = payload.block; + setInsertLineBox({ + left: rect.x, + top: rect.y, + height: rect.height, + width: rect.width, + position: cursorPosition, + }); + } + }); + + useMessageObserver<InsertModeDisableAction>(ActionTypes.InsertModeDisable, () => { + setInsertLineBox(undefined); + }); + + useMessageObserver<ReorderModeDisableAction>(ActionTypes.ReorderModeDisable, () => { + setInsertLineBox(undefined); + }); + + const onMouseDown = useCallback(() => { + if (selectedBlock) { + sendMessage({ + type: ActionTypes.ReorderModeEnable, + payload: {path: selectedBlock.path}, + }); + } + }, [selectedBlock, sendMessage]); + + return ( + <div className={b(null, className)} style={{height: `${height}px`}}> + {selectedBlock ? ( + <div + className={b('border')} + style={{ + top: selectedBlock.rect.top - margin, + left: selectedBlock.rect.left - margin, + width: selectedBlock.rect.width + margin * 2, + height: selectedBlock.rect.height + margin * 2, + }} + > + <div className={b('actions')}> + <div onMouseDown={onMouseDown}> + <Button + pin={'round-clear'} + className={`${b('action-button', {grip: true})}`} + size={'m'} + view={'action'} + > + <Icon data={Grip} size={18} /> + </Button> + </div> + <Button + pin={'clear-clear'} + className={b('action-button')} + size={'m'} + view={'action'} + onClick={() => selectedBlock && deleteBlock(selectedBlock.path)} + > + <Icon data={TrashBin} size={18} /> + </Button> + <Button + pin={'clear-round'} + className={b('action-button')} + size={'m'} + view={'action'} + onClick={() => selectedBlock && duplicateBlock(selectedBlock.path)} + > + <Icon data={Copy} size={18} /> + </Button> + </div> + </div> + ) : null} + {insertLineBox ? ( + <div + className={b('line', { + position: insertLineBox.position, + })} + style={{ + top: insertLineBox.top, + left: insertLineBox.left, + width: insertLineBox.width, + height: insertLineBox.height, + }} + /> + ) : null} + </div> + ); +}; + +export default Overlay; diff --git a/src/editor-v2/components/Sidebar/Sidebar.scss b/src/editor-v2/components/Sidebar/Sidebar.scss new file mode 100644 index 0000000000..842996be65 --- /dev/null +++ b/src/editor-v2/components/Sidebar/Sidebar.scss @@ -0,0 +1,86 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}sidebar'; + +#{$block} { + $class: &; + + position: relative; + height: 100%; + width: 100%; + display: flex; + + &_position_left { + flex-direction: row; + } + + &_position_right { + flex-direction: row-reverse; + } + + &__buttons-wrapper { + border-right: 1px solid var(--g-color-line-generic); + } + + &__buttons { + display: inline-flex; + flex-direction: column; + padding: 8px; + gap: 8px; + + &:hover #{$class}__label-wrapper { + opacity: 1; + } + } + + &__body { + flex: 1; + border-right: 1px solid var(--g-color-line-generic); + overflow-y: auto; + } + + &__button-wrapper { + position: relative; + } + + &_position_right { + #{$class}__button-wrapper:hover { + #{$class}__label-wrapper { + right: 40px; + } + } + + #{$class}__label-wrapper { + right: 50px; + } + } + + &_position_left { + #{$class}__button-wrapper:hover { + #{$class}__label-wrapper { + left: 40px; + } + } + + #{$class}__label-wrapper { + left: 50px; + } + } + + &__label-wrapper { + pointer-events: none; + opacity: 0; + + background-color: var(--g-color-base-background); + position: absolute; + top: 50%; + transform: translateY(-50%); + z-index: 100; + + transition: left 0.1s ease, right 0.1s ease, opacity 0.1s ease; + } +} diff --git a/src/editor-v2/components/Sidebar/Sidebar.tsx b/src/editor-v2/components/Sidebar/Sidebar.tsx new file mode 100644 index 0000000000..09311f20e3 --- /dev/null +++ b/src/editor-v2/components/Sidebar/Sidebar.tsx @@ -0,0 +1,110 @@ +import React, {useMemo, useState} from 'react'; + +import {Code, FolderTree, Gear, Rectangles4, Square} from '@gravity-ui/icons'; +import {Button, Icon, Label} from '@gravity-ui/uikit'; +import {IconData} from '@gravity-ui/uikit/build/esm/components/Icon/Icon'; + +import {ClassNameProps} from '../../../models'; +import {block} from '../../../utils'; +import BlockConfig from '../BlockConfig/BlockConfig'; +import BlocksList from '../BlocksList/BlocksList'; +import GlobalConfig from '../GlobalConfig/GlobalConfig'; +import SourceCode from '../SourceCode/SourceCode'; +import Tree from '../Tree/Tree'; + +import './Sidebar.scss'; + +const b = block('sidebar'); + +interface SidebarProps extends ClassNameProps { + position: 'left' | 'right'; + startMenu?: string; +} + +interface TabConfig { + id: string; + name: string; + icon: IconData; + component: React.ElementType; + position: 'left' | 'right' | 'both'; +} + +export const Sidebar = ({className, position, startMenu}: SidebarProps) => { + const [currentTab, setCurrentTab] = useState(startMenu || 'blocks-list'); + + const defaultTabs: TabConfig[] = useMemo( + () => [ + { + id: 'blocks-list', + name: 'Блоки', + icon: Rectangles4, + component: BlocksList, + position: 'left', + }, + { + id: 'global-config', + name: 'Глобальная конфигурация', + icon: Gear, + component: GlobalConfig, + position: 'left', + }, + { + id: 'tree', + name: 'Дерево', + icon: FolderTree, + component: Tree, + position: 'left', + }, + { + id: 'block-config', + name: 'Конфигурация блока', + icon: Square, + component: BlockConfig, + position: 'right', + }, + { + id: 'source-code', + name: 'Исходный код', + icon: Code, + component: SourceCode, + position: 'both', + }, + ], + [], + ); + + const filteredTabs = defaultTabs.filter( + ({position: tabPosition}) => tabPosition === 'both' || tabPosition === position, + ); + + const TabComponent = useMemo(() => { + const findTab = defaultTabs.find(({id}) => id === currentTab); + return findTab?.component; + }, [currentTab, defaultTabs]); + + return ( + <div className={`${b(null, className)} ${b('', {position})}`}> + <div className={b('buttons-wrapper')}> + <div className={b('buttons')}> + {filteredTabs.map(({id, icon, name}) => ( + <div className={b('button-wrapper')} key={id}> + <Button + view={currentTab === id ? 'action' : 'flat'} + size="m" + onClick={() => setCurrentTab(id)} + > + <Icon data={icon} size={18} /> + </Button> + <div className={b('label-wrapper')}> + <Label theme={'normal'} size={'m'}> + {name} + </Label> + </div> + </div> + ))} + </div> + </div> + <div className={b('body')}>{TabComponent && <TabComponent />}</div> + </div> + ); +}; diff --git a/src/editor-v2/components/Source/Source.scss b/src/editor-v2/components/Source/Source.scss new file mode 100644 index 0000000000..d85ac31e61 --- /dev/null +++ b/src/editor-v2/components/Source/Source.scss @@ -0,0 +1,18 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}source'; + +#{$block} { + display: inline-flex; + align-items: center; + padding: 0 8px; + gap: 8px; + + &__text { + flex: 1; + } +} diff --git a/src/editor-v2/components/Source/Source.tsx b/src/editor-v2/components/Source/Source.tsx new file mode 100644 index 0000000000..a9e065370a --- /dev/null +++ b/src/editor-v2/components/Source/Source.tsx @@ -0,0 +1,53 @@ +import React, {useCallback, useContext} from 'react'; + +import {ArrowRotateRight} from '@gravity-ui/icons'; +import {Button, Icon, TextInput} from '@gravity-ui/uikit'; + +import {ClassNameProps} from '../../../models'; +import {block} from '../../../utils'; +import {useEditorStore} from '../../context/editorContext'; +import {IframeContext, useIframeStore} from '../../context/iframeContext'; + +import './Source.scss'; + +const b = block('source'); + +interface SourceProps extends ClassNameProps {} + +const Source: React.FC<SourceProps> = ({className}) => { + const {url, setUrl} = useIframeStore(); + const {resetInitialize} = useEditorStore(); + const {disableUrlField} = useContext(IframeContext); + + const onUpdateUrl = useCallback( + (value: string) => { + setUrl(value); + resetInitialize(); + }, + [resetInitialize, setUrl], + ); + + const reloadIframe = () => { + setUrl(''); + setTimeout(() => { + setUrl(url); + }, 0); + }; + + return ( + <div className={b(null, className)}> + <Button view="flat" size="m" onClick={reloadIframe}> + <Icon data={ArrowRotateRight} size={18} /> + </Button> + <TextInput + disabled={disableUrlField} + className={b('text')} + size="s" + value={url} + onUpdate={onUpdateUrl} + /> + </div> + ); +}; + +export default Source; diff --git a/src/editor-v2/components/SourceCode/SourceCode.scss b/src/editor-v2/components/SourceCode/SourceCode.scss new file mode 100644 index 0000000000..c63709db20 --- /dev/null +++ b/src/editor-v2/components/SourceCode/SourceCode.scss @@ -0,0 +1,26 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}source-code'; + +#{$block} { + padding: 10px; + white-space: pre; + + @include text-code-inline-1; + + display: flex; + flex-direction: column; + gap: 10px; + + &__code { + user-select: all; + } + + &__textarea textarea { + @include text-code-inline-1; + } +} diff --git a/src/editor-v2/components/SourceCode/SourceCode.tsx b/src/editor-v2/components/SourceCode/SourceCode.tsx new file mode 100644 index 0000000000..02b52d1628 --- /dev/null +++ b/src/editor-v2/components/SourceCode/SourceCode.tsx @@ -0,0 +1,63 @@ +import React, {useState} from 'react'; + +import {Button, Dialog, TextArea} from '@gravity-ui/uikit'; + +import {ClassNameProps, PageContent} from '../../../models'; +import {block} from '../../../utils'; +import {useContentConfigStore} from '../../context/contentConfig/hooks/useContentConfigStore'; + +import './SourceCode.scss'; + +const b = block('source-code'); + +interface SourceCodeProps extends ClassNameProps {} + +const SourceCode: React.FC<SourceCodeProps> = ({className}) => { + const {config, setConfig} = useContentConfigStore(); + const [isOpen, setIsOpen] = useState(false); + const [tempConfig, setTempConfig] = useState(''); + + const onUpdate = () => { + let object; + + try { + object = JSON.parse(tempConfig); + } catch { + // eslint-disable-next-line no-console + console.error('JSON.parse failed'); + } + + setConfig(object as PageContent); + setIsOpen(false); + }; + + return ( + <div className={b(null, className)}> + <Button onClick={() => setIsOpen(true)}>Update</Button> + <div className={b('code')}>{JSON.stringify(config, null, 2)}</div> + + <Dialog onClose={() => setIsOpen(false)} open={isOpen} size={'l'}> + <Dialog.Header caption="New configuration" /> + <Dialog.Body> + <TextArea + className={b('textarea')} + value={tempConfig} + onUpdate={setTempConfig} + rows={30} + /> + </Dialog.Body> + <Dialog.Footer + showError={false} + listenKeyEnter={true} + preset={'default'} + textButtonApply={'Apply'} + textButtonCancel={'Cancel'} + onClickButtonApply={onUpdate} + onClickButtonCancel={() => setIsOpen(false)} + /> + </Dialog> + </div> + ); +}; + +export default SourceCode; diff --git a/src/editor-v2/components/TopBar/TopBar.scss b/src/editor-v2/components/TopBar/TopBar.scss new file mode 100644 index 0000000000..506b6ae875 --- /dev/null +++ b/src/editor-v2/components/TopBar/TopBar.scss @@ -0,0 +1,34 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}topbar'; + +#{$block} { + display: flex; + justify-content: start; + align-items: center; + width: 100%; + height: 40px; + + border-bottom: 1px solid var(--g-color-line-generic); + + &__switches { + display: flex; + align-items: center; + width: auto; + height: 100%; + padding: 0 12px; + gap: 12px; + + border-right: 1px solid var(--g-color-line-generic); + } + + &__source { + flex: 1; + width: 100%; + box-sizing: border-box; + } +} diff --git a/src/editor-v2/components/TopBar/TopBar.tsx b/src/editor-v2/components/TopBar/TopBar.tsx new file mode 100644 index 0000000000..af8566fcf3 --- /dev/null +++ b/src/editor-v2/components/TopBar/TopBar.tsx @@ -0,0 +1,25 @@ +import React from 'react'; + +import {ClassNameProps} from '../../../models'; +import {block} from '../../../utils'; +import Source from '../Source/Source'; +import ViewSwitches from '../ViewSwitches/ViewSwitches'; + +import './TopBar.scss'; + +const b = block('topbar'); + +interface TopBarProps extends ClassNameProps {} + +const TopBar: React.FC<TopBarProps> = ({className}) => { + return ( + <div className={b(null, className)}> + <div className={b('switches')}> + <ViewSwitches /> + </div> + <Source className={b('source')} /> + </div> + ); +}; + +export default TopBar; diff --git a/src/editor-v2/components/Tree/Tree.scss b/src/editor-v2/components/Tree/Tree.scss new file mode 100644 index 0000000000..628987a448 --- /dev/null +++ b/src/editor-v2/components/Tree/Tree.scss @@ -0,0 +1,38 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}tree'; + +#{$block} { + padding: 12px; + + &__item { + padding: 8px; + margin-bottom: 8px; + + &:last-child { + margin-bottom: 0; + } + + @for $i from 1 through 9 { + &_deep_#{$i} { + position: relative; + margin-left: 16px * $i; + width: calc(100% - 16px * $i); + + &:before { + content: ''; + position: absolute; + right: calc(100% + 12px); + height: 100%; + width: 2px; + background-color: var(--g-color-line-generic-accent); + top: 0; + } + } + } + } +} diff --git a/src/editor-v2/components/Tree/Tree.tsx b/src/editor-v2/components/Tree/Tree.tsx new file mode 100644 index 0000000000..e3c2dc5fe2 --- /dev/null +++ b/src/editor-v2/components/Tree/Tree.tsx @@ -0,0 +1,54 @@ +import React, {PropsWithChildren} from 'react'; + +import {Card} from '@gravity-ui/uikit'; + +import {ItemConfig} from '../../../common/types'; +import {block} from '../../../utils'; +import {useContentConfigStore} from '../../context/contentConfig'; + +import './Tree.scss'; + +const b = block('tree'); + +export interface TreeProps { + blocks: ItemConfig[]; +} + +type TreeItem = { + type: string; + children?: TreeItem[]; +}; + +const generateTree = (items: TreeItem[]): TreeItem[] => { + return items.map((item) => { + let children; + + if ('children' in item && item.children?.length) { + children = generateTree(item.children); + } + + return { + type: item.type, + children, + }; + }); +}; + +const Tree = (_p: PropsWithChildren<TreeProps>) => { + const {config} = useContentConfigStore(); + + const blockTree = generateTree(config.blocks); + + const renderTree = (items: TreeItem[], deepLevel = 0) => { + return items.map(({type, children}, index) => ( + <React.Fragment key={index}> + <Card className={b('item', {deep: deepLevel})}>{type}</Card> + {children && renderTree(children, deepLevel + 1)} + </React.Fragment> + )); + }; + + return <div className={b()}>{renderTree(blockTree)}</div>; +}; + +export default Tree; diff --git a/src/editor-v2/components/ViewSwitches/ViewSwitches.scss b/src/editor-v2/components/ViewSwitches/ViewSwitches.scss new file mode 100644 index 0000000000..ca2c5f7be9 --- /dev/null +++ b/src/editor-v2/components/ViewSwitches/ViewSwitches.scss @@ -0,0 +1,12 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}view-switches'; + +#{$block} { + display: inline-flex; + gap: 12px; +} diff --git a/src/editor-v2/components/ViewSwitches/ViewSwitches.tsx b/src/editor-v2/components/ViewSwitches/ViewSwitches.tsx new file mode 100644 index 0000000000..b96b73fe03 --- /dev/null +++ b/src/editor-v2/components/ViewSwitches/ViewSwitches.tsx @@ -0,0 +1,50 @@ +import React from 'react'; + +import {Display, Eye, Smartphone, Square, SquareDashedText} from '@gravity-ui/icons'; +import {Icon, RadioButton, RadioButtonOption} from '@gravity-ui/uikit'; + +import {ClassNameProps} from '../../../models'; +import {block} from '../../../utils'; + +import './ViewSwitches.scss'; + +const b = block('view-switches'); + +interface ViewSwitchesProps extends ClassNameProps {} + +const options1: RadioButtonOption[] = [ + {value: 'edit-mode', content: <Icon data={SquareDashedText} />}, + {value: 'preview-mode', content: <Icon data={Eye} />}, +]; + +export const options2: RadioButtonOption[] = [ + {value: 'desktop-mode', content: <Icon data={Display} />}, + {value: 'tablet-mode', content: <Icon data={Square} />}, + {value: 'phone-mode', content: <Icon data={Smartphone} />}, +]; + +// TODO: To be done +const ViewSwitches: React.FC<ViewSwitchesProps> = ({className}) => { + return ( + <div className={b(null, className)}> + <RadioButton + className={b('switch')} + name="group1" + defaultValue={options1[0].value} + options={options1} + size="m" + disabled + /> + <RadioButton + className={b('switch')} + name="group2" + defaultValue={options2[0].value} + options={options2} + size="m" + disabled + /> + </div> + ); +}; + +export default ViewSwitches; diff --git a/src/editor-v2/containers/Editor/Editor.scss b/src/editor-v2/containers/Editor/Editor.scss new file mode 100644 index 0000000000..b6dea3b886 --- /dev/null +++ b/src/editor-v2/containers/Editor/Editor.scss @@ -0,0 +1,57 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}editor'; + +#{$block} { + margin: 0; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + height: 100%; + width: 100%; + position: relative; + + &__header { + height: $headerHeight; + width: 100%; + border-bottom: 1px solid var(--g-color-line-generic); + } + + &__body { + display: flex; + align-items: center; + justify-content: center; + flex: 1; + width: 100%; + max-height: calc(100% - $headerHeight); + } + + &__canvas { + flex: 1; + } + + &__draggable { + width: 12px; + height: 100%; + cursor: col-resize; + background-color: var(--g-color-line-generic); + + display: flex; + align-items: center; + justify-content: center; + + color: var(--g-color-base-background); + } + + &__overlay { + position: absolute; + height: 100%; + width: 100%; + z-index: 100; + } +} diff --git a/src/editor-v2/containers/Editor/Editor.tsx b/src/editor-v2/containers/Editor/Editor.tsx new file mode 100644 index 0000000000..d0fb5b641a --- /dev/null +++ b/src/editor-v2/containers/Editor/Editor.tsx @@ -0,0 +1,107 @@ +import React, {useCallback} from 'react'; + +import {Grip} from '@gravity-ui/icons'; +import {Panel, PanelGroup, PanelResizeHandle} from 'react-resizable-panels'; + +import {ActionTypes} from '../../../common/types'; +import {PageContent} from '../../../models'; +import {block} from '../../../utils'; +import BigOverlay from '../../components/BigOverlay/BigOverlay'; +import MiddleScreen from '../../components/MiddleScreen/MiddleScreen'; +import {Sidebar} from '../../components/Sidebar/Sidebar'; +import {ContentConfigProvider} from '../../context/contentConfig'; +import {EditorProvider, useEditorStore} from '../../context/editorContext'; +import {IframeProvider} from '../../context/iframeContext'; +import {PostMessageProvider} from '../../context/messagesContext'; +import {useMessageSender} from '../../context/messagesContext/hooks/useMessageSender'; + +import useAdminInitialize from './hooks/useAdminInitialize'; + +import './Editor.scss'; + +const b = block('editor'); + +interface EditorViewProps { + content: PageContent; + onUpdate?: (pageContent: PageContent) => void; + initialUrl: string; + disableUrlField?: boolean; +} + +const EditorView = (_props: EditorViewProps) => { + const {manipulateOverlayMode} = useEditorStore(); + const sendMessage = useMessageSender(); + + useAdminInitialize(); + + // Disable insert mode on any MouseUp event + // Maybe should be attached to body + const onMouseUp = useCallback( + (e: React.MouseEvent) => { + if (manipulateOverlayMode === 'insert') { + e.preventDefault(); + sendMessage({type: ActionTypes.InsertModeDisable, payload: undefined}); + } + if (manipulateOverlayMode === 'reorder') { + e.preventDefault(); + sendMessage({type: ActionTypes.ReorderModeDisable, payload: undefined}); + } + }, + [manipulateOverlayMode, sendMessage], + ); + + const onMouseMove = useCallback( + (e: React.MouseEvent) => { + if (manipulateOverlayMode) { + sendMessage({ + type: ActionTypes.OverlayModeOnMove, + payload: {cursor: {x: e.clientX, y: e.clientY}}, + }); + } + }, + [manipulateOverlayMode, sendMessage], + ); + + return ( + <div className={b()} onMouseUp={onMouseUp} onMouseMove={onMouseMove}> + <div className={b('body')}> + <PanelGroup + className={b('panel')} + autoSaveId="page-constructor-editor" + direction="horizontal" + > + <Panel collapsible defaultSize={25} minSize={15}> + <Sidebar position={'left'} /> + </Panel> + <PanelResizeHandle className={b('draggable')}> + <Grip className={b('grip')} /> + </PanelResizeHandle> + <Panel minSize={20}> + <MiddleScreen /> + </Panel> + <PanelResizeHandle className={b('draggable')}> + <Grip className={b('grip')} /> + </PanelResizeHandle> + <Panel collapsible minSize={15} defaultSize={25}> + <Sidebar position={'right'} startMenu="block-config" /> + </Panel> + </PanelGroup> + </div> + <BigOverlay className={b('overlay')} /> + </div> + ); +}; + +export const Editor = (props: EditorViewProps) => { + return ( + <EditorProvider> + <IframeProvider initialUrl={props.initialUrl} disableUrlField={props.disableUrlField}> + <ContentConfigProvider onUpdate={props.onUpdate} content={props.content}> + <PostMessageProvider> + <EditorView {...props} /> + </PostMessageProvider> + </ContentConfigProvider> + </IframeProvider> + </EditorProvider> + ); +}; diff --git a/src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx b/src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx new file mode 100644 index 0000000000..9bc69e9f4c --- /dev/null +++ b/src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx @@ -0,0 +1,39 @@ +import {useContext, useEffect} from 'react'; + +import {ActionTypes, IframeReadyAction} from '../../../../common/types'; +import {ContentConfigContext, useContentConfigStore} from '../../../context/contentConfig'; +import {useEditorStore} from '../../../context/editorContext'; +import {useMessageObserver, useMessageSender} from '../../../context/messagesContext'; + +const useAdminInitialize = () => { + const {state: contentConfigState} = useContext(ContentConfigContext); + const {config} = useContentConfigStore(); + const {initialized} = useEditorStore(); + const sendMessage = useMessageSender(); + + useMessageObserver<IframeReadyAction>( + ActionTypes.IframeReady, + () => { + if (sendMessage && !initialized) { + sendMessage({type: ActionTypes.EditorReady, payload: undefined}); + sendMessage( + {type: ActionTypes.UpdateConfigs, payload: {content: config}}, + {direction: 'pc'}, + ); + } + }, + [sendMessage], + ); + + // Update configs for both instances on any changes + useEffect( + () => + contentConfigState && + contentConfigState.subscribe((state) => { + sendMessage({type: ActionTypes.UpdateConfigs, payload: {content: state.config}}); + }), + [sendMessage, contentConfigState], + ); +}; + +export default useAdminInitialize; diff --git a/src/editor-v2/containers/__stories__/Editor.stories.tsx b/src/editor-v2/containers/__stories__/Editor.stories.tsx new file mode 100644 index 0000000000..3ade46fb64 --- /dev/null +++ b/src/editor-v2/containers/__stories__/Editor.stories.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +import {Meta, StoryFn} from '@storybook/react'; + +import {PageContent} from '../../../models'; +import {Editor} from '../Editor/Editor'; + +import data from './data.json'; + +export default { + title: 'Editor/Main 2.0', + component: Editor, +} as Meta; + +const DefaultTemplate: StoryFn = (args) => { + return ( + <div style={{height: '100vh', width: '100vw'}}> + <Editor + {...args} + content={data as unknown as PageContent} + initialUrl={'http://localhost:3000'} + /> + </div> + ); +}; + +export const Default = DefaultTemplate.bind({}); + +// Default.args = data.default; diff --git a/src/editor-v2/containers/__stories__/data.json b/src/editor-v2/containers/__stories__/data.json new file mode 100644 index 0000000000..396e3394e0 --- /dev/null +++ b/src/editor-v2/containers/__stories__/data.json @@ -0,0 +1,335 @@ +{ + "blocks": [ + { + "type": "header-block", + "title": "Yandex Open Source", + "description": "<p>Мы в Яндексе верим, что вклад в опенсорс — это вклад в технологическую эволюцию: без открытости, совместной работы и поддержки развитие IT‑индустрии сильно затруднено. Уже много лет мы используем в своих продуктах сторонние открытые технологии, а также делимся собственными и активно вовлекаем в их развитие разработчиков по всему миру.</p>", + "width": "s", + "verticalOffset": "l", + "offset": "default", + "resetPaddings": true, + "background": { + "image": { + "mobile": "https://storage.yandexcloud.net/yandex-opensource/pages/index/yos-index-cover-m.png", + "desktop": "https://storage.yandexcloud.net/yandex-opensource/pages/index/yos-index-cover.png" + }, + "color": "#EFF2F8", + "fullWidth": false, + "fullWidthMedia": true + } + }, + { + "type": "extended-features-block", + "title": { + "text": "Почему мы выкладываем наши технологии в открытый доступ?" + }, + "items": [ + { + "title": "Ответственность", + "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-01.svg", + "text": "<p>Мы верим, что вкладываться в развитие опенсорс‑технологий — это ответственность каждого технологического лидера на рынке. Без опенсорс‑решений не появились бы многие продукты и сервисы не только Яндекса, но и других крупных компаний, и мы хотим отдавать обратно, делиться теми нашими решениями, которые, как мы считаем, принесут реальную пользу.</p>" + }, + { + "title": "Польза для сообщества", + "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-02.svg", + "text": "<p>Технологии, которые мы разрабатываем, ежедневно помогают нам эффективно решать огромное количество самых разных задач в наших сервисах. Мы знаем, что разработчики вне Яндекса часто сталкиваются с теми же самыми задачами — и верим, что наши технологии могут быть полезны и им.</p>" + }, + { + "title": "Качество сервисов", + "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-03.svg", + "text": "<p>Для нас важно разрабатывать и использовать только качественные технологические решения. В особенности это касается опенсорса: зная, что наши решения увидят и будут использовать другие, мы уделяем их качеству особое внимание. А уже в открытом доступе у технологии больше шансов развиваться и улучшаться — в том числе, при участии сообщества разработчиков.</p>" + }, + { + "title": "Бизнес‑потенциал", + "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-04.svg", + "text": "<p>Мы верим, что при условии роста популярности наших решений и спроса на них со стороны сообщества, то, что мы выкладываем в опенсорс, может далее стать для нас бизнесом. То, что мы выкладываем в опенсорс, можно использовать и во внешних коммерческих проектах.</p>" + }, + { + "title": "Поиск талантов", + "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-05.svg", + "text": "<p>Мы ценим каждого, кто вкладывается в сторонние опенсорс‑решения или делится с миром своими. Контрибьюторы в наши продукты нам особенно важны: среди них мы ищем и находим тех, кто сможет развивать технологии уже будучи частью команды Яндекса.</p>" + } + ] + }, + { + "type": "card-layout-block", + "animated": false, + "title": "Краткая история опенсорса в Яндексе", + "description": "<p>С начала истории развития опенсорса в Яндексе мы успели выложить в открытый доступ десятки собственных проектов, использовать в разработке наших продуктов внешние технологии, а также внесли существенный вклад в их развитие.</p>", + "colSizes": { + "all": 12, + "lg": 3, + "md": 4, + "sm": 6 + }, + "anchor": { + "url": "history", + "text": "history" + }, + "children": [ + { + "type": "layout-item", + "content": { + "title": "2010", + "text": "<p>Методология веб‑разработки БЭМ (Блок‑Элемент‑Модификатор) выходит в оперсорс</p>" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-01.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2012", + "text": "<p>Запуск Яндекс Браузера на базе Blink (Chromium)</p>" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-02.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2013", + "text": "<p>Яндекс начинает контрибьютить в ядро Linux</p>" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-03.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2016", + "text": "<p>Выход в опенсорс ClickHouse</p>\\n<p>Выход в опенсорс Hermione (с 2024 года — Testplane)</p>" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-2016.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2017", + "text": "<p>Выход в опенсорс CatBoost<br />\\nЯндекс начинает контрибьютить в PostgreSQL</p>" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-05.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2018", + "text": "<p>Выход в опенсорс Одиссея</p>\\n<p>Яндекс — топ‑контрибьютор в WAL‑G</p>" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-06.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2019", + "text": "<p>В Яндексе появляется команда разработки СУБД с открытым исходным кодом</p>\\n<p>Яндекс — спонсор разработки PostgreSQL</p>" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-07.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2020", + "text": "<p>Выход в опенсорс Testsuite</p>" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-2020.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2022", + "text": "<p>Яндекс — один из основных спонсоров разработки PostgreSQL</p>\\n<p>Выход в опенсорс YDB, userver, YaLM 100B, DivKit, Yatagan</p>\\n<p>Старт программы «Код для всех»</p>" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-09.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2023", + "text": "<p>Выход в опенсорс YTsaurus, Gravity UI, AppMetrica, Diplodoc, DataLens и счётчика Метрики</p>\\n<p>Старт Программы грантов Yandex Open Source</p>" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-2023.png" + } + }, + { + "type": "layout-item", + "content": { + "title": "2024", + "text": "<p>Первый Yandex Open Source Jam</p>\\n<p>Выход в опенсорс YaFSDP</p>" + }, + "media": { + "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-2024.png" + } + } + ] + }, + { + "type": "card-layout-block", + "title": "Наши проекты", + "description": "<p>В Яндексе мы разрабатываем и развиваем технологические решения самых разных сфер применения, размеров и сложности. Поэтому и в открытый доступ попадают самые разные проекты — главное, чтобы они приносили пользу не только нам, но и другим.</p>", + "colSizes": { + "all": 12, + "lg": 3, + "md": 4, + "sm": 6 + }, + "anchor": { + "url": "projects", + "text": "projects" + }, + "children": [ + { + "type": "background-card", + "title": "YDB", + "text": "<p>Отказоустойчивая распределённая SQL база данных</p>", + "backgroundColor": "#2399FF", + "background": { + "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-ydb.png", + "alt": "card-background" + }, + "paddingBottom": "m", + "theme": "dark" + }, + { + "type": "background-card", + "title": "YTsaurus", + "text": "<p>Платформа для хранения и обработки больших данных</p>", + "backgroundColor": "#FFB23E", + "background": { + "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-yt.png", + "alt": "card-background" + }, + "paddingBottom": "m", + "theme": "light" + }, + { + "type": "background-card", + "title": "GravityUI", + "text": "<p>Библиотеки для создания интерфейсов</p>", + "backgroundColor": "#262626", + "background": { + "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-gui.png", + "alt": "card-background" + }, + "paddingBottom": "m", + "theme": "dark" + }, + { + "type": "background-card", + "title": "DivKit", + "text": "<p>Фреймворк для server‑driven интерфейсов</p>", + "backgroundColor": "#F1F1F1", + "background": { + "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-dk.png", + "alt": "card-background" + }, + "paddingBottom": "m", + "theme": "light" + }, + { + "type": "background-card", + "title": "Diplodoc", + "text": "<p>Платформа для написания документации в концепции Docs as Code</p>", + "backgroundColor": "#79F985", + "background": { + "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-dd.png", + "alt": "card-background" + }, + "paddingBottom": "m", + "theme": "light" + }, + { + "type": "background-card", + "title": "userver", + "text": "<p>Фреймворк для создания высоконагруженных приложений</p>", + "backgroundColor": "#FF9D73", + "background": { + "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-u.png", + "alt": "card-background" + }, + "paddingBottom": "m", + "theme": "light" + }, + { + "type": "background-card", + "title": "DataLens", + "text": "<p>BI-платформа для анализа и визуализации данных</p>", + "backgroundColor": "#FF7132", + "background": { + "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-dl.png", + "alt": "card-background" + }, + "paddingBottom": "m", + "theme": "dark" + }, + { + "type": "basic-card", + "title": "И это ещё не всё", + "text": "<p>Узнать про наши опенсорс‑проекты больше вы можете на другой странице</p>", + "border": "none", + "buttons": [ + { + "text": "Все проекты", + "primary": true, + "theme": "monochrome", + "size": "promo", + "url": "/projects" + } + ] + } + ] + }, + { + "largeMedia": true, + "mediaOnly": false, + "size": "l", + "type": "media-block", + "direction": "content-media", + "anchor": { + "url": "1", + "text": "Фильм" + }, + "title": "<p>Смотрите фильм с YaC 22</p>", + "description": "<p>Руководители опенсорс‑проектов рассказывают про историю и культуру открытого кода в Яндексе.</p>", + "media": { + "youtube": "https://www.youtube.com/watch?v=G7G286S8ntc", + "previewImg": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/video-cover.png" + }, + "disableShadow": true + }, + { + "type": "content-layout-block", + "size": "l", + "centered": true, + "textContent": { + "title": "", + "text": "", + "additionalInfo": "", + "buttons": [ + { + "text": "Сделано на GravityUI", + "theme": "monochrome", + "img": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/open-source-gravity-ui-button.svg", + "url": "https://gravity-ui.com/" + } + ] + } + } + ] +} diff --git a/src/editor-v2/containers/__stories__/utils.ts b/src/editor-v2/containers/__stories__/utils.ts new file mode 100644 index 0000000000..7d9be48821 --- /dev/null +++ b/src/editor-v2/containers/__stories__/utils.ts @@ -0,0 +1,22 @@ +import isEqual from 'lodash/isEqual'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const memoizeLast = (fn: (...args: any[]) => any) => { + let cacheKey: Parameters<typeof fn>; + let cacheResult: ReturnType<typeof fn>; + + return (...params: Parameters<typeof fn>) => { + if (!isEqual(params, cacheKey)) { + try { + const result = fn(...params); + + cacheResult = result; + cacheKey = params; + } catch { + return cacheResult; + } + } + + return cacheResult; + }; +}; diff --git a/src/editor-v2/context/contentConfig/contentConfigContext.tsx b/src/editor-v2/context/contentConfig/contentConfigContext.tsx new file mode 100644 index 0000000000..9ae9e2791b --- /dev/null +++ b/src/editor-v2/context/contentConfig/contentConfigContext.tsx @@ -0,0 +1,15 @@ +/** + * Context for managing PC configuration (aka Yaml Configs) + **/ + +import React from 'react'; + +import {StoreApi} from 'zustand'; + +import {ContentConfigStore} from './store'; + +export interface ContentConfigContextProps { + state?: StoreApi<ContentConfigStore>; +} + +export const ContentConfigContext = React.createContext<ContentConfigContextProps>({}); diff --git a/src/editor-v2/context/contentConfig/contentConfigProvider.tsx b/src/editor-v2/context/contentConfig/contentConfigProvider.tsx new file mode 100644 index 0000000000..18a7f6276d --- /dev/null +++ b/src/editor-v2/context/contentConfig/contentConfigProvider.tsx @@ -0,0 +1,34 @@ +import React, {PropsWithChildren, useRef} from 'react'; + +import {StoreApi} from 'zustand'; + +import {PageContent} from '../../../models'; + +import {ContentConfigContext} from './contentConfigContext'; +import {ContentConfigStore, createContentConfigStore} from './store'; + +interface ContentConfigProviderProps extends PropsWithChildren { + content: PageContent; + onUpdate?: (pageContent: PageContent) => void; +} + +export const ContentConfigProvider = ({ + content, + onUpdate, + children, +}: ContentConfigProviderProps) => { + const storeRef = useRef<StoreApi<ContentConfigStore>>(); + + if (!storeRef.current) { + storeRef.current = createContentConfigStore({config: content}); + if (onUpdate) { + storeRef.current?.subscribe((state) => onUpdate(state.config)); + } + } + + return ( + <ContentConfigContext.Provider value={{state: storeRef.current}}> + {children} + </ContentConfigContext.Provider> + ); +}; diff --git a/src/editor-v2/context/contentConfig/hooks/useContentConfigStore.tsx b/src/editor-v2/context/contentConfig/hooks/useContentConfigStore.tsx new file mode 100644 index 0000000000..40d57fd1ab --- /dev/null +++ b/src/editor-v2/context/contentConfig/hooks/useContentConfigStore.tsx @@ -0,0 +1,15 @@ +import {useContext} from 'react'; + +import {useStore} from 'zustand'; + +import {ContentConfigContext} from '../contentConfigContext'; + +export const useContentConfigStore = () => { + const {state} = useContext(ContentConfigContext); + if (!state) { + throw new Error('Missing ContentConfigContext'); + } + return useStore(state); +}; + +export default useContentConfigStore; diff --git a/src/editor-v2/context/contentConfig/index.ts b/src/editor-v2/context/contentConfig/index.ts new file mode 100644 index 0000000000..4772ca2998 --- /dev/null +++ b/src/editor-v2/context/contentConfig/index.ts @@ -0,0 +1,4 @@ +export * from './contentConfigContext'; +export * from './contentConfigProvider'; +export * from './hooks/useContentConfigStore'; +export * from './store'; diff --git a/src/editor-v2/context/contentConfig/store.ts b/src/editor-v2/context/contentConfig/store.ts new file mode 100644 index 0000000000..819362418d --- /dev/null +++ b/src/editor-v2/context/contentConfig/store.ts @@ -0,0 +1,202 @@ +import _ from 'lodash'; + +import {ActionTypes, ConfigInput, ItemConfig, WithStoreReducer} from '../../../common/types'; +import {ConstructorBlock, PageContent} from '../../../models'; +import {DynamicFormValue} from '../../components/DynamicForm/DynamicForm'; +import { + duplicateArrayItem, + generateChildrenPathFromArray, + getDestinationShiftBeforeReorder, + insert, + isItemsNeighbours, + modifyObjectByPath, + removeFromArray, + reorderArrayItems, +} from '../../utils'; +import {initializeStore} from '../../utils/store'; + +export interface ContentConfigState { + config: PageContent; + blocks: Array<ItemConfig>; + subBlocks: Array<ItemConfig>; + global: Array<ConfigInput>; + + preInsertBlockType: string | null; + preReorderBlockPath: number[] | null; +} + +export interface ContentConfigMethods extends WithStoreReducer { + setConfig: (config: PageContent) => void; + setBlocks: (blocks: ItemConfig[]) => void; + setSubBlocks: (subBlocks: ItemConfig[]) => void; + insertBlock: (position: number[], blockType: string) => void; + deleteBlock: (path: number[]) => void; + duplicateBlock: (path: number[]) => void; + reorderBlock: (path: number[], destination: number[]) => void; + updateField: (path: string, value: DynamicFormValue) => void; +} + +export type ContentConfigStore = ContentConfigState & ContentConfigMethods; + +export const createContentConfigStore = initializeStore<ContentConfigState, ContentConfigMethods>( + { + config: {blocks: []}, + blocks: [], + subBlocks: [], + global: [], + preInsertBlockType: null, + preReorderBlockPath: null, + }, + (set, get) => ({ + setConfig: (config) => set((state) => ({...state, config})), + setBlocks: (blocks) => set((state) => ({...state, blocks})), + setSubBlocks: (subBlocks) => set((state) => ({...state, subBlocks})), + insertBlock: (arrayPath, blockType) => { + const blocksConfig = get().config.blocks; + const blocksData = get().blocks; + + const foundBlock = blocksData.find(({type}) => type === blockType); + const defaultValue = + foundBlock && foundBlock.schema.default + ? {...foundBlock.schema.default, type: blockType} + : {type: blockType}; + + const newBlocksConfig = modifyObjectByPath( + blocksConfig, + arrayPath, + (parentBlocks, index) => + insert(parentBlocks, index, defaultValue as ConstructorBlock), + ); + + set((state) => ({ + ...state, + config: {...state.config, blocks: newBlocksConfig}, + })); + }, + deleteBlock: (arrayPath) => { + const blocksConfig = get().config.blocks; + + const newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, removeFromArray); + + set((state) => ({ + ...state, + config: {...state.config, blocks: newBlocksConfig}, + })); + }, + duplicateBlock: (arrayPath) => { + const blocksConfig = get().config.blocks; + + const newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, duplicateArrayItem); + + set((state) => ({ + ...state, + config: {...state.config, blocks: newBlocksConfig}, + })); + }, + updateField: (path, value) => { + set((state) => { + const newConfig = _.set(state.config, path, value); + return { + ...state, + config: newConfig, + }; + }); + }, + reorderBlock: (arrayPath, destination) => { + let newBlocksConfig: ConstructorBlock[]; + const blocksConfig = get().config.blocks; + // Copy + const copiedBlock = _.get(blocksConfig, generateChildrenPathFromArray(arrayPath)); + + if (isItemsNeighbours(arrayPath, destination)) { + newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, (parentBlocks) => { + return reorderArrayItems( + parentBlocks, + arrayPath[arrayPath.length - 1], + destination[destination.length - 1], + ); + }); + } else { + const arrayDest = getDestinationShiftBeforeReorder(arrayPath, destination); + + // Delete + const blocksConfigWithoutBlock = modifyObjectByPath( + blocksConfig, + arrayPath, + removeFromArray, + ); + // Paste + newBlocksConfig = modifyObjectByPath( + blocksConfigWithoutBlock, + arrayDest, + (parentBlocks, index) => insert(parentBlocks, index, copiedBlock), + ); + } + + set((state) => ({ + ...state, + config: {...state.config, blocks: newBlocksConfig}, + })); + }, + reducer: function (action) { + switch (action.type) { + // Insert Actions + case ActionTypes.InsertModeEnable: { + set((state) => ({ + ...state, + preInsertBlockType: action.payload.blockType, + })); + break; + } + case ActionTypes.InsertModeDisable: { + set((state) => ({ + ...state, + preInsertBlockType: null, + })); + break; + } + + case ActionTypes.InsertBlock: { + const {preInsertBlockType} = get(); + if (preInsertBlockType) { + const {path} = action.payload; + this.insertBlock(path, preInsertBlockType); + } + break; + } + // Reorder Actions + case ActionTypes.ReorderModeEnable: { + set((state) => ({ + ...state, + preReorderBlockPath: action.payload.path, + })); + break; + } + case ActionTypes.ReorderModeDisable: { + set((state) => ({ + ...state, + preReorderBlockPath: null, + })); + break; + } + case ActionTypes.ReorderBlocks: { + const {preReorderBlockPath} = get(); + if (preReorderBlockPath) { + const {path: destinationPath} = action.payload; + this.reorderBlock(preReorderBlockPath, destinationPath); + } + break; + } + case ActionTypes.BlocksConfigs: { + set((state) => ({ + ...state, + blocks: action.payload.blocks, + subBlocks: action.payload.subBlocks, + global: action.payload.global, + })); + break; + } + } + }, + }), +); diff --git a/src/editor-v2/context/editorContext/editorContext.tsx b/src/editor-v2/context/editorContext/editorContext.tsx new file mode 100644 index 0000000000..23a9cce22f --- /dev/null +++ b/src/editor-v2/context/editorContext/editorContext.tsx @@ -0,0 +1,16 @@ +/** + * Context for managing internal Editor states + * Same exist in PC + **/ + +import React from 'react'; + +import {StoreApi} from 'zustand'; + +import {EditorStore} from './store'; + +export interface EditorContextProps { + state?: StoreApi<EditorStore>; +} + +export const EditorContext = React.createContext<EditorContextProps>({}); diff --git a/src/editor-v2/context/editorContext/editorProvider.tsx b/src/editor-v2/context/editorContext/editorProvider.tsx new file mode 100644 index 0000000000..64dcb447b4 --- /dev/null +++ b/src/editor-v2/context/editorContext/editorProvider.tsx @@ -0,0 +1,20 @@ +import React, {PropsWithChildren, useRef} from 'react'; + +import {StoreApi} from 'zustand'; + +import {EditorContext} from './editorContext'; +import {EditorStore, createEditorStore} from './store'; + +export const EditorProvider = ({children}: PropsWithChildren) => { + const storeRef = useRef<StoreApi<EditorStore>>(); + + if (!storeRef.current) { + storeRef.current = createEditorStore(); + } + + return ( + <EditorContext.Provider value={{state: storeRef.current}}> + {children} + </EditorContext.Provider> + ); +}; diff --git a/src/editor-v2/context/editorContext/hooks/useEditorStore.tsx b/src/editor-v2/context/editorContext/hooks/useEditorStore.tsx new file mode 100644 index 0000000000..4f2a346cde --- /dev/null +++ b/src/editor-v2/context/editorContext/hooks/useEditorStore.tsx @@ -0,0 +1,17 @@ +import {useContext} from 'react'; + +import {useStore} from 'zustand'; + +import {EditorContext} from '../editorContext'; + +export const useEditorStore = () => { + const {state} = useContext(EditorContext); + + if (!state) { + throw new Error('Missing EditorContext'); + } + + return useStore(state); +}; + +export default useEditorStore; diff --git a/src/editor-v2/context/editorContext/index.ts b/src/editor-v2/context/editorContext/index.ts new file mode 100644 index 0000000000..c7b944eea3 --- /dev/null +++ b/src/editor-v2/context/editorContext/index.ts @@ -0,0 +1,4 @@ +export * from './editorContext'; +export * from './editorProvider'; +export * from './hooks/useEditorStore'; +export * from './store'; diff --git a/src/editor-v2/context/editorContext/store.ts b/src/editor-v2/context/editorContext/store.ts new file mode 100644 index 0000000000..9c6085f756 --- /dev/null +++ b/src/editor-v2/context/editorContext/store.ts @@ -0,0 +1,101 @@ +import {Action, ActionTypes, WithStoreReducer} from '../../../common/types'; +import {initializeStore} from '../../utils/store'; + +export interface EditorState { + manipulateOverlayMode: 'insert' | 'reorder' | false; + selectedBlock?: { + path: number[]; + rect: DOMRect; + }; + initialized: boolean; +} + +export interface EditorMethods extends WithStoreReducer { + resetInitialize: () => void; +} + +export type EditorStore = EditorState & EditorMethods; + +export const createEditorStore = initializeStore<EditorState, EditorMethods>( + { + manipulateOverlayMode: false, + initialized: false, + }, + (set, get) => ({ + resetInitialize: () => { + set((state) => ({ + ...state, + initialized: false, + })); + }, + reducer: (action: Action) => { + switch (action.type) { + case ActionTypes.IframeReady: { + set((state) => ({ + ...state, + initialized: true, + })); + break; + } + case ActionTypes.ReorderModeEnable: { + set((state) => ({ + ...state, + manipulateOverlayMode: 'reorder', + })); + break; + } + case ActionTypes.ReorderModeDisable: { + set((state) => ({ + ...state, + manipulateOverlayMode: false, + })); + break; + } + case ActionTypes.InsertModeEnable: { + set((state) => ({ + ...state, + manipulateOverlayMode: 'insert', + })); + break; + } + case ActionTypes.InsertModeDisable: { + set((state) => ({ + ...state, + manipulateOverlayMode: false, + })); + break; + } + case ActionTypes.SelectBlock: { + set((state) => ({ + ...state, + selectedBlock: { + path: action.payload.path, + rect: action.payload.rect, + }, + })); + break; + } + case ActionTypes.UpdateSelectedBlockRect: { + const {selectedBlock} = get(); + if (selectedBlock) { + set((state) => ({ + ...state, + selectedBlock: { + ...selectedBlock, + rect: action.payload.rect, + }, + })); + } + break; + } + case ActionTypes.ReorderBlocks: { + set((state) => ({ + ...state, + selectedBlock: undefined, + })); + break; + } + } + }, + }), +); diff --git a/src/editor-v2/context/iframeContext/hooks/useIframeStore.tsx b/src/editor-v2/context/iframeContext/hooks/useIframeStore.tsx new file mode 100644 index 0000000000..87369816cc --- /dev/null +++ b/src/editor-v2/context/iframeContext/hooks/useIframeStore.tsx @@ -0,0 +1,15 @@ +import {useContext} from 'react'; + +import {useStore} from 'zustand'; + +import {IframeContext} from '../iframeContext'; + +export const useIframeStore = () => { + const {state} = useContext(IframeContext); + if (!state) { + throw new Error('Missing IframeContext'); + } + return useStore(state); +}; + +export default useIframeStore; diff --git a/src/editor-v2/context/iframeContext/iframeContext.tsx b/src/editor-v2/context/iframeContext/iframeContext.tsx new file mode 100644 index 0000000000..5404b61a75 --- /dev/null +++ b/src/editor-v2/context/iframeContext/iframeContext.tsx @@ -0,0 +1,18 @@ +/** + * Context for iframe window + **/ + +import React from 'react'; + +import {StoreApi} from 'zustand'; + +import {IframeStore} from './store'; + +export interface IframeContextProps { + state?: StoreApi<IframeStore>; + iframeElement?: HTMLIFrameElement; + setIframeElement: (element: HTMLIFrameElement) => void; + disableUrlField?: boolean; +} + +export const IframeContext = React.createContext<IframeContextProps>({setIframeElement: () => {}}); diff --git a/src/editor-v2/context/iframeContext/iframeProvider.tsx b/src/editor-v2/context/iframeContext/iframeProvider.tsx new file mode 100644 index 0000000000..a8b7f56fbf --- /dev/null +++ b/src/editor-v2/context/iframeContext/iframeProvider.tsx @@ -0,0 +1,36 @@ +import React, {PropsWithChildren, useRef, useState} from 'react'; + +import {StoreApi} from 'zustand'; + +import {IframeContext} from './iframeContext'; +import {IframeStore, createIframeStore} from './store'; + +interface IframeProviderProps extends PropsWithChildren { + initialUrl?: string; + disableUrlField?: boolean; +} + +export const IframeProvider = (props: IframeProviderProps) => { + const {children, initialUrl, disableUrlField} = props; + const storeRef = useRef<StoreApi<IframeStore>>(); + const [iframeElement, setIframeElement] = useState<HTMLIFrameElement>(); + + const setIframeElementFunc = (element: HTMLIFrameElement) => setIframeElement(element); + + if (!storeRef.current) { + storeRef.current = createIframeStore({url: initialUrl}); + } + + return ( + <IframeContext.Provider + value={{ + state: storeRef.current, + iframeElement, + setIframeElement: setIframeElementFunc, + disableUrlField, + }} + > + {children} + </IframeContext.Provider> + ); +}; diff --git a/src/editor-v2/context/iframeContext/index.ts b/src/editor-v2/context/iframeContext/index.ts new file mode 100644 index 0000000000..7d73463acd --- /dev/null +++ b/src/editor-v2/context/iframeContext/index.ts @@ -0,0 +1,4 @@ +export * from './iframeContext'; +export * from './iframeProvider'; +export * from './hooks/useIframeStore'; +export * from './store'; diff --git a/src/editor-v2/context/iframeContext/store.ts b/src/editor-v2/context/iframeContext/store.ts new file mode 100644 index 0000000000..756ba17fd8 --- /dev/null +++ b/src/editor-v2/context/iframeContext/store.ts @@ -0,0 +1,34 @@ +import {ActionTypes, WithStoreReducer} from '../../../common/types'; +import {initializeStore} from '../../utils/store'; + +export interface IframeState { + url: string; + height?: number; +} + +export interface IframeMethods extends WithStoreReducer { + setUrl: (url: string) => void; +} + +export type IframeStore = IframeState & IframeMethods; + +export const createIframeStore = initializeStore<IframeState, IframeMethods>( + { + url: '', + height: 400, + }, + (set) => ({ + setUrl: (url) => set((state) => ({...state, url})), + reducer: (action) => { + switch (action.type) { + case ActionTypes.SetHeight: { + set((state) => ({ + ...state, + height: action.payload.height + 200, + })); + break; + } + } + }, + }), +); diff --git a/src/editor-v2/context/messagesContext/hooks/useMessageObserver.tsx b/src/editor-v2/context/messagesContext/hooks/useMessageObserver.tsx new file mode 100644 index 0000000000..7414cad432 --- /dev/null +++ b/src/editor-v2/context/messagesContext/hooks/useMessageObserver.tsx @@ -0,0 +1,23 @@ +import {DependencyList, useEffect} from 'react'; + +import {Action, Meta} from '../../../../common/types'; + +import useMessagesStore from './useMessagesStore'; + +export const useMessageObserver = <A extends Action>( + type: A['type'], + callback: (payload: A['payload'], meta: Meta) => void, + deps: DependencyList = [], +) => { + const {subscribe, unsubscribe} = useMessagesStore(); + + useEffect(() => { + subscribe(type, callback); + + return () => { + unsubscribe(type, callback); + }; + }, deps); // eslint-disable-line react-hooks/exhaustive-deps +}; + +export default useMessageObserver; diff --git a/src/editor-v2/context/messagesContext/hooks/useMessageSender.tsx b/src/editor-v2/context/messagesContext/hooks/useMessageSender.tsx new file mode 100644 index 0000000000..57f25eef0e --- /dev/null +++ b/src/editor-v2/context/messagesContext/hooks/useMessageSender.tsx @@ -0,0 +1,10 @@ +import {useContext} from 'react'; + +import {PostMessageContext} from '../messagesContext'; + +export const useMessageSender = () => { + const {sendMessage} = useContext(PostMessageContext); + return sendMessage; +}; + +export default useMessageSender; diff --git a/src/editor-v2/context/messagesContext/hooks/useMessagesStore.tsx b/src/editor-v2/context/messagesContext/hooks/useMessagesStore.tsx new file mode 100644 index 0000000000..1805cd32a6 --- /dev/null +++ b/src/editor-v2/context/messagesContext/hooks/useMessagesStore.tsx @@ -0,0 +1,17 @@ +import {useContext} from 'react'; + +import {useStore} from 'zustand'; + +import {PostMessageContext} from '../messagesContext'; + +export const useMessagesStore = () => { + const {state} = useContext(PostMessageContext); + + if (!state) { + throw new Error('Missing PostMessageContext'); + } + + return useStore(state); +}; + +export default useMessagesStore; diff --git a/src/editor-v2/context/messagesContext/index.ts b/src/editor-v2/context/messagesContext/index.ts new file mode 100644 index 0000000000..eda561f123 --- /dev/null +++ b/src/editor-v2/context/messagesContext/index.ts @@ -0,0 +1,5 @@ +export * from './messagesContext'; +export * from './messagesProvider'; +export * from './hooks/useMessageSender'; +export * from './hooks/useMessageObserver'; +export * from './hooks/useMessagesStore'; diff --git a/src/editor-v2/context/messagesContext/messagesContext.tsx b/src/editor-v2/context/messagesContext/messagesContext.tsx new file mode 100644 index 0000000000..ba01f35522 --- /dev/null +++ b/src/editor-v2/context/messagesContext/messagesContext.tsx @@ -0,0 +1,21 @@ +/** + * Context for sending messages between Editor and App + * Same exist in PC + **/ + +import React from 'react'; + +import {StoreApi} from 'zustand'; + +import {Action, SendOptions} from '../../../common/types'; + +import {MessagesStore} from './store'; + +export interface PostMessageContextProps { + state?: StoreApi<MessagesStore>; + sendMessage: (action: Action, options?: SendOptions) => void; +} + +export const PostMessageContext = React.createContext<PostMessageContextProps>({ + sendMessage: () => {}, +}); diff --git a/src/editor-v2/context/messagesContext/messagesProvider.tsx b/src/editor-v2/context/messagesContext/messagesProvider.tsx new file mode 100644 index 0000000000..3fe7d60662 --- /dev/null +++ b/src/editor-v2/context/messagesContext/messagesProvider.tsx @@ -0,0 +1,46 @@ +import React, {PropsWithChildren, useContext, useRef} from 'react'; + +import {StoreApi} from 'zustand'; + +import {usePostMessage} from '../../../common/hooks/usePostMessage'; +import {WithStoreReducer} from '../../../common/types'; +import {getUrlOrigin} from '../../utils'; +import {useContentConfigStore} from '../contentConfig/hooks/useContentConfigStore'; +import {useEditorStore} from '../editorContext/hooks/useEditorStore'; +import {IframeContext, useIframeStore} from '../iframeContext'; + +import {PostMessageContext} from './messagesContext'; +import {MessagesStore, createMessagesStore} from './store'; + +export const PostMessageProvider = ({children}: PropsWithChildren) => { + const storeRef = useRef<StoreApi<MessagesStore>>(); + const {iframeElement} = useContext(IframeContext); + + const contentConfigStore = useContentConfigStore(); + const editorStore = useEditorStore(); + const iframeStore = useIframeStore(); + + if (!storeRef.current) { + storeRef.current = createMessagesStore(); + } + + const storesWithReducer: WithStoreReducer[] = [contentConfigStore, editorStore, iframeStore]; + + const {sendMessage} = usePostMessage({ + subscribers: storeRef.current?.getState().subscribers, + storesWithReducer, + targetIframeElement: iframeElement, + urlOrigin: getUrlOrigin(iframeStore.url), + }); + + return ( + <PostMessageContext.Provider + value={{ + state: storeRef.current, + sendMessage: sendMessage, + }} + > + {children} + </PostMessageContext.Provider> + ); +}; diff --git a/src/editor-v2/context/messagesContext/store.ts b/src/editor-v2/context/messagesContext/store.ts new file mode 100644 index 0000000000..3c76dfe9d2 --- /dev/null +++ b/src/editor-v2/context/messagesContext/store.ts @@ -0,0 +1,44 @@ +import {Subscriber, SubscriptionFunc} from '../../../common/types'; +import {initializeStore} from '../../utils/store'; + +export interface MessagesState { + subscribers: Subscriber[]; +} + +export interface MessagesMethods { + subscribe: SubscriptionFunc; + unsubscribe: SubscriptionFunc; +} + +export type MessagesStore = MessagesState & MessagesMethods; + +export const createMessagesStore = initializeStore<MessagesState, MessagesMethods>( + { + subscribers: [], + }, + (set, _get) => ({ + subscribe: (type, payloadCallback) => { + set((state) => ({ + ...state, + subscribers: [...state.subscribers, {action: type, handler: payloadCallback}], + })); + + return () => { + set((state) => ({ + ...state, + subscribers: state.subscribers.filter( + ({handler, action}) => payloadCallback !== handler || type !== action, + ), + })); + }; + }, + unsubscribe: (type, payloadCallback) => { + set((state) => ({ + ...state, + subscribers: state.subscribers.filter( + ({handler, action}) => payloadCallback !== handler || type !== action, + ), + })); + }, + }), +); diff --git a/src/editor-v2/icons/Tablet.tsx b/src/editor-v2/icons/Tablet.tsx new file mode 100644 index 0000000000..9ece669007 --- /dev/null +++ b/src/editor-v2/icons/Tablet.tsx @@ -0,0 +1,23 @@ +import React from 'react'; + +import {a11yHiddenSvgProps} from '../../utils/svg'; + +export const Tablet: React.FC<React.SVGProps<SVGSVGElement>> = (props) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="12" + height="14" + viewBox="0 0 12 14" + fill="none" + {...a11yHiddenSvgProps} + {...props} + > + <path + fillRule="evenodd" + clipRule="evenodd" + d="M10.5 3L10.5 11C10.5 11.8284 9.82843 12.5 9 12.5L3 12.5C2.17157 12.5 1.5 11.8284 1.5 11L1.5 3C1.5 2.17157 2.17157 1.5 3 1.5L9 1.5C9.82843 1.5 10.5 2.17157 10.5 3ZM9 -1.31134e-07C10.6569 -5.87108e-08 12 1.34315 12 3L12 11C12 12.6569 10.6569 14 9 14L3 14C1.34315 14 4.00426e-07 12.6569 4.72849e-07 11L8.2254e-07 3C8.94964e-07 1.34315 1.34315 -4.65826e-07 3 -3.93403e-07L9 -1.31134e-07ZM3.75 9.5C3.33579 9.5 3 9.83579 3 10.25C3 10.6642 3.33579 11 3.75 11L8.25 11C8.66421 11 9 10.6642 9 10.25C9 9.83579 8.66421 9.5 8.25 9.5L3.75 9.5Z" + fill="currentColor" + fillOpacity="0.85" + /> + </svg> +); diff --git a/src/editor-v2/index.ts b/src/editor-v2/index.ts new file mode 100644 index 0000000000..4697a13c9f --- /dev/null +++ b/src/editor-v2/index.ts @@ -0,0 +1,2 @@ +export {Editor} from './containers/Editor/Editor'; +export * from '../common/types'; diff --git a/src/editor-v2/styles/mixins.scss b/src/editor-v2/styles/mixins.scss new file mode 100644 index 0000000000..6144f64253 --- /dev/null +++ b/src/editor-v2/styles/mixins.scss @@ -0,0 +1,12 @@ +@import './variables.scss'; + +@mixin control($hoverScale: 1.05) { + display: flex; + justify-content: center; + align-items: center; + transition: transform $editorTransitionTime; + + &:hover { + transform: scale($hoverScale); + } +} ; diff --git a/src/editor-v2/styles/root.scss b/src/editor-v2/styles/root.scss new file mode 100644 index 0000000000..d69df66c26 --- /dev/null +++ b/src/editor-v2/styles/root.scss @@ -0,0 +1,9 @@ +body { + --pc-editor-header-height: 48px; + --pc-editor-code-header-height: 36px; + --pc-editor-divider-width: 12px; + --pc-editor-left-column-width: calc(400px + var(--pc-editor-divider-width)); + --pc-editor-base-color: var(--g-color-base-brand); + --pc-editor-control-color: var(--g-color-base-brand); + --pc-editor-control-icon-color: var(--g-color-text-dark-primary); +} diff --git a/src/editor-v2/styles/variables.scss b/src/editor-v2/styles/variables.scss new file mode 100644 index 0000000000..c776f820a1 --- /dev/null +++ b/src/editor-v2/styles/variables.scss @@ -0,0 +1,5 @@ +$editorTransitionTime: 0.2s; +$editorShadow: 0px 2px 8px rgba(0, 0, 0, 0.06), 0px 4px 24px rgba(0, 0, 0, 0.06); +$editorControlBorderRadius: 8px; + +$headerHeight: 0px; diff --git a/src/editor-v2/utils/code.ts b/src/editor-v2/utils/code.ts new file mode 100644 index 0000000000..3b46632677 --- /dev/null +++ b/src/editor-v2/utils/code.ts @@ -0,0 +1,12 @@ +import yaml from 'js-yaml'; + +import {PageContent} from '../../models'; + +export function parseCode(code: string) { + const pageContent = yaml.load(code) as PageContent; + + return { + ...pageContent, + blocks: pageContent.blocks?.filter(Boolean), + }; +} diff --git a/src/editor-v2/utils/index.ts b/src/editor-v2/utils/index.ts new file mode 100644 index 0000000000..dba1995558 --- /dev/null +++ b/src/editor-v2/utils/index.ts @@ -0,0 +1,168 @@ +import _ from 'lodash'; + +import {ConstructorBlock} from '../../models'; + +export function insert<T>(arr: Array<T>, index: number, newItem: T) { + return [...arr.slice(0, index), newItem, ...arr.slice(index)]; +} + +export function removeFromArray<T>(array: Array<T>, index: number) { + return [...array.slice(0, index), ...array.slice(index + 1)]; +} + +export function swapArrayItems<T>(array: Array<T>, firstIndex: number, secondIndex: number) { + const results = array.slice(); + const firstItem = array[firstIndex]; + results[firstIndex] = array[secondIndex]; + results[secondIndex] = firstItem; + return results; +} + +export function reorderArrayItems<T>(array: Array<T>, index: number, destination: number) { + const min = Math.min(index, destination); + const max = Math.max(index, destination); + const firstOperationRemove = index < destination; + const result = []; + result.push(...array.slice(0, min)); + if (!firstOperationRemove) { + result.push(array[index]); + } + result.push(...array.slice(firstOperationRemove ? min + 1 : min, max)); + if (firstOperationRemove) { + result.push(array[index]); + } + result.push(...array.slice(firstOperationRemove ? max : max + 1, array.length)); + return result; +} + +export function duplicateArrayItem<T>(array: Array<T>, index: number) { + const duplicatedItem = _.cloneDeep(array[index]); + return [...array.slice(0, index), duplicatedItem, ...array.slice(index)]; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function insertByPath<T extends object>(object: T, path: string, value: any) { + if (!path) { + return value as T; + } + + return _.setWith(_.clone(object), path, value, _.clone); +} + +/* + * path: string; + * Example: + * 1. blocks[0] => {path: blocks, index: 0} + * 2. blocks[2].children[10] => {path: blocks[2].children, index: 10} + **/ +export function splitPathAndIndex(path: string) { + // Match blocks[3], blocks[0].children[12], blocks[0], blocks[999999] + const bracketsRegExp = /(.*)\[(\d+)]$/g; + const regexpMatches = [...path.matchAll(bracketsRegExp)]; + if (regexpMatches.length) { + return { + // blocks, blocks[0].children + path: regexpMatches[0][1], + // 3, 12, 0, 9999 + index: Number(regexpMatches[0][2]), + }; + } + + // eslint-disable-next-line no-console + console.error('Non correct path for splitting'); + return undefined; +} + +/* + * [0, 4, 3] => [0].children[4].children[3] + * */ +export function generateChildrenPathFromArray(indexes: number[]) { + if (!indexes.length) { + return ''; + } + + let resultPath = `[${indexes[0]}]`; + + if (indexes.length > 1) { + for (let i = 1; i < indexes.length; i++) { + resultPath += `.children[${indexes[i]}]`; + } + } + + return resultPath; +} + +export function modifyObjectByPath( + blocks: ConstructorBlock[], + arrayPath: number[], + modifyCallback: (parentBlocks: ConstructorBlock[], index: number) => ConstructorBlock[], +) { + // [1] + // [4].children[3] + const insertPath = generateChildrenPathFromArray(arrayPath); + // path: '' index: 1 + // path: '[4].children' index: 3 + const splitPath = splitPathAndIndex(insertPath); + + if (splitPath) { + const {path: parentPath, index} = splitPath; + // Get Array that lies on path + const parentArray = parentPath ? _.get(blocks, parentPath) : blocks; + + const value = Array.isArray(parentArray) ? parentArray : []; + // Modify Array + const newModifiedArray = modifyCallback(value, index); + + // Return it back + return insertByPath(blocks, parentPath, newModifiedArray); + } + + return blocks; +} + +export function isItemsNeighbours(arrayA: number[], arrayB: number[]) { + if (arrayA.length !== arrayB.length) { + return false; + } + + for (let i = 0; i < arrayA.length - 1; i++) { + if (arrayA[i] !== arrayB[i]) { + return false; + } + } + + return true; +} + +export function getDestinationShiftBeforeReorder(arrayInit: number[], arrayDest: number[]) { + if (arrayInit.length === arrayDest.length || arrayInit.length > arrayDest.length) { + return arrayDest; + } + + for (let i = 0; i < arrayInit.length; i++) { + if (arrayInit[i] < arrayDest[i]) { + return prepareShift(arrayInit, arrayDest); + } + } + + return arrayDest; +} + +export function prepareShift(arrayInit: number[], arrayDest: number[]) { + if (arrayInit.length === arrayDest.length || arrayInit.length > arrayDest.length) { + return arrayDest; + } + + return arrayDest.map((pathIndex, index) => + index === arrayInit.length - 1 ? pathIndex - 1 : pathIndex, + ); +} + +export const getUrlOrigin = (url: string) => { + try { + const urlObject = new URL(url); + return urlObject.origin; + } catch { + return undefined; + } +}; diff --git a/src/editor-v2/utils/store.ts b/src/editor-v2/utils/store.ts new file mode 100644 index 0000000000..6806f7ca8c --- /dev/null +++ b/src/editor-v2/utils/store.ts @@ -0,0 +1,24 @@ +import {StoreApi, create} from 'zustand'; +import {devtools, subscribeWithSelector} from 'zustand/middleware'; + +export function initializeStore<State, Methods>( + initialState: State, + methods: ( + set: StoreApi<State & Methods>['setState'], + get: StoreApi<State & Methods>['getState'], + ) => Methods, +) { + return (overrideInitialState?: Partial<State>) => + create< + State & Methods, + [['zustand/subscribeWithSelector', never], ['zustand/devtools', never]] + >( + subscribeWithSelector( + devtools((set, get) => ({ + ...initialState, + ...overrideInitialState, + ...methods(set, get), + })), + ), + ); +} diff --git a/src/editor/data/templates/test-editor-block.json b/src/editor/data/templates/test-editor-block.json new file mode 100644 index 0000000000..43c1534b1f --- /dev/null +++ b/src/editor/data/templates/test-editor-block.json @@ -0,0 +1,18 @@ +{ + "template": { + "type": "test-editor-block", + "title": "Lorem ipsum dolor sit amet", + "table": { + "content": [ + ["Lorem", "ipsum 1", "dolor 2", "sit 3"], + ["Lorem 1", "0", "0", "0"], + ["Lorem 2", "0", "0", "1"], + ["Lorem 3", "0", "0", "1"], + ["Lorem 4", "0", "1", "1"], + ["Lorem 5", "1", "1", "1"] + ], + "legend": ["ipsum 1", "ipsum 2"], + "justify": ["start", "center", "center", "center"] + } + } +} diff --git a/src/grid/Grid/Grid.scss b/src/grid/Grid/Grid.scss index 5c172d42f8..6097bcc481 100644 --- a/src/grid/Grid/Grid.scss +++ b/src/grid/Grid/Grid.scss @@ -15,8 +15,8 @@ $block: '.#{$ns}Grid'; } .row { - margin-right: 0; - margin-left: 0; + //margin-right: 0; + //margin-left: 0; } #{$block} { @@ -26,7 +26,7 @@ $block: '.#{$ns}Grid'; } } - .row .row { + .row { margin: 0 -#{$gutter}; } @@ -58,7 +58,7 @@ $block: '.#{$ns}Grid'; } } - .row .row { + .row { margin: 0 -#{$gutterMobile}; } } diff --git a/src/hooks/useAnalytics.ts b/src/hooks/useAnalytics.ts index 5eb1768d9d..7212ef646e 100644 --- a/src/hooks/useAnalytics.ts +++ b/src/hooks/useAnalytics.ts @@ -12,7 +12,7 @@ export const useAnalytics = (name = '', target?: string) => { name ? { name, - context, + context: String(context), type: PredefinedEventTypes.Default, target: target, } diff --git a/src/hooks/useEditorInitialize.ts b/src/hooks/useEditorInitialize.ts new file mode 100644 index 0000000000..4f3cede1b3 --- /dev/null +++ b/src/hooks/useEditorInitialize.ts @@ -0,0 +1,117 @@ +import {useCallback, useContext, useEffect} from 'react'; + +import {JSONSchemaType} from 'ajv'; +import _ from 'lodash'; + +import {ActionTypes, ItemConfig, UpdateConfigsAction} from '../common/types'; +import {blockDataMap} from '../constructor-items'; +import {EditorContext, useEditorStore} from '../context/editorContext'; +import {useMessageObserver, useMessageSender} from '../context/messagesContext'; +import {PageContent} from '../models'; +import {defaultComponentsConfigurationSchema} from '../schema'; +import {generateFromAJV} from '../utils/form-generator'; + +interface UseEditorInitializeProps { + content: PageContent; + setContent: (content: PageContent) => void; +} + +const useEditorInitialize = ({setContent, content}: UseEditorInitializeProps) => { + const {manipulateOverlayMode, isSelectActive} = useEditorStore(); + const sendMessage = useMessageSender(); + const {activeElement} = useContext(EditorContext); + + const onResize = useCallback(() => { + const height = document.documentElement.getBoundingClientRect().height; + + sendMessage({ + type: ActionTypes.SetHeight, + payload: {height}, + }); + + if (isSelectActive && activeElement) { + const rect = activeElement.getClientRects().item(0); + + if (rect) { + sendMessage({ + type: ActionTypes.UpdateSelectedBlockRect, + payload: { + rect: rect, + }, + }); + } + } + }, [activeElement, isSelectActive, sendMessage]); + + useMessageObserver<UpdateConfigsAction>(ActionTypes.UpdateConfigs, ({content: newContent}) => { + setContent(newContent); + }); + + useEffect(() => { + onResize(); + }, [content, onResize]); + + // Fires only once + useEffect(() => { + const height = document.documentElement.getBoundingClientRect().height; + + sendMessage( + { + type: ActionTypes.IframeReady, + payload: {height}, + }, + {direction: 'editor'}, + ); + + sendMessage({ + type: ActionTypes.BlocksConfigs, + payload: { + blocks: Object.entries(blockDataMap).reduce((acc, [key, value]) => { + acc.push({type: key, schema: value.schema}); + return acc; + }, [] as ItemConfig[]), + subBlocks: [], + global: generateFromAJV( + defaultComponentsConfigurationSchema as unknown as JSONSchemaType<{}>, + ), + }, + }); + }, [sendMessage]); + + useEffect(() => { + const onMouseUp = (e: MouseEvent) => { + if (manipulateOverlayMode === 'insert') { + e.preventDefault(); + sendMessage({type: ActionTypes.InsertModeDisable, payload: undefined}); + } + if (manipulateOverlayMode === 'reorder') { + e.preventDefault(); + sendMessage({type: ActionTypes.ReorderModeDisable, payload: undefined}); + } + }; + + const onMouseMove = (event: MouseEvent) => { + if (manipulateOverlayMode) { + sendMessage({ + type: ActionTypes.OverlayModeOnMove, + payload: {cursor: {x: event.clientX, y: event.clientY}}, + }); + } + }; + + const throttleOnMouseMove = _.throttle(onMouseMove, 1); + const throttleOnMouseUp = _.throttle(onMouseUp, 1); + + document.addEventListener('mousemove', throttleOnMouseMove); + document.addEventListener('mouseup', throttleOnMouseUp); + window.addEventListener('resize', onResize); + + return () => { + document.removeEventListener('mousemove', throttleOnMouseMove); + document.removeEventListener('mouseup', throttleOnMouseUp); + window.removeEventListener('resize', onResize); + }; + }, [activeElement, manipulateOverlayMode, onResize, sendMessage]); +}; + +export default useEditorInitialize; diff --git a/src/navigation/components/NavigationItem/NavigationItem.tsx b/src/navigation/components/NavigationItem/NavigationItem.tsx index fb6db07fe1..d2fb34d19d 100644 --- a/src/navigation/components/NavigationItem/NavigationItem.tsx +++ b/src/navigation/components/NavigationItem/NavigationItem.tsx @@ -43,7 +43,9 @@ export const NavigationItem = ({data, className, menuLayout, ...props}: Navigati }, [data, props, type, menuLayout]); return ( - <BlockIdContext.Provider value={ANALYTICS_ID}> + // TODO: fix any + // eslint-disable-next-line @typescript-eslint/no-explicit-any + <BlockIdContext.Provider value={ANALYTICS_ID as any}> <li className={b({'menu-layout': menuLayout}, className)}> <Component {...componentProps} className={b('content', {type})} /> </li> diff --git a/src/navigation/components/NavigationItem/components/NavigationButton/NavigationButton.tsx b/src/navigation/components/NavigationItem/components/NavigationButton/NavigationButton.tsx index 0eded43742..a09501ac12 100644 --- a/src/navigation/components/NavigationItem/components/NavigationButton/NavigationButton.tsx +++ b/src/navigation/components/NavigationItem/components/NavigationButton/NavigationButton.tsx @@ -16,7 +16,9 @@ export const NavigationButton = (props: NavigationButtonProps) => { const {url, target, className} = props; const classes = b(null, className); return ( - <BlockIdContext.Provider value={ANALYTICS_ID}> + // TODO: fix any + // eslint-disable-next-line @typescript-eslint/no-explicit-any + <BlockIdContext.Provider value={ANALYTICS_ID as any}> {target ? ( <Button className={classes} {...props} url={url} /> ) : ( diff --git a/src/navigation/containers/Layout/Layout.scss b/src/navigation/containers/Layout/Layout.scss index 7d2a45b721..1cc4f89561 100644 --- a/src/navigation/containers/Layout/Layout.scss +++ b/src/navigation/containers/Layout/Layout.scss @@ -6,7 +6,8 @@ $block: '.#{$ns}layout'; display: flex; flex-direction: column; - min-height: 100vh; + // TODO: It seems not okay + //min-height: 100vh; &__content { display: flex; diff --git a/src/schema/index.ts b/src/schema/index.ts index c7f90d8b9a..42f16a0b76 100644 --- a/src/schema/index.ts +++ b/src/schema/index.ts @@ -33,6 +33,17 @@ export const getBlocksCases = (blocks: Schema) => { ); }; +export const defaultComponentsConfigurationSchema = { + type: 'object', + properties: { + ...AnimatableProps, + logo: withTheme(LogoProps), + header: NavigationHeaderProps, + menu: MenuProps, + background: withTheme(BackgroundProps), + }, +}; + export function generateDefaultSchema(config?: SchemaCustomConfig) { const {cards = {}, blocks = {}, extensions = {}} = config ?? {}; @@ -82,17 +93,13 @@ export function generateDefaultSchema(config?: SchemaCustomConfig) { additionalProperties: false, required: ['blocks'], properties: { - ...AnimatableProps, - logo: withTheme(LogoProps), - header: NavigationHeaderProps, + ...defaultComponentsConfigurationSchema.properties, blocks: { type: 'array', items: { $ref: '#/definitions/children', }, }, - menu: MenuProps, - background: withTheme(BackgroundProps), ...extensions, }, } as Schema; diff --git a/src/sub-blocks/BackgroundCard/dynamic-form.ts b/src/sub-blocks/BackgroundCard/dynamic-form.ts new file mode 100644 index 0000000000..817ff35e28 --- /dev/null +++ b/src/sub-blocks/BackgroundCard/dynamic-form.ts @@ -0,0 +1,81 @@ +import {BlockConfig} from '../../common/types'; +import {contentThemes, textSize} from '../../schema/validators/common'; + +const textSizeEnum = textSize.map((size) => ({value: size, content: size})); +const contentThemesEnum = contentThemes.map((size) => ({value: size, content: size})); + +export const blockConfig: BlockConfig = { + name: 'Background Card', + inputs: [ + { + type: 'oneOf', + name: 'title', + title: 'Title Object', + options: [ + { + title: 'Simple', + value: 'simple', + properties: [ + { + type: 'text', + name: '', + title: 'Title', + }, + ], + }, + { + title: 'Complex', + value: 'complex', + properties: [ + { + type: 'text', + name: 'text', + title: 'Title', + }, + { + type: 'select', + name: 'textSize', + title: 'Text Size', + enum: textSizeEnum, + view: 'select', + mode: 'single', + }, + { + type: 'text', + name: 'url', + title: 'Url', + }, + { + type: 'text', + name: 'urlTitle', + title: 'Url', + }, + { + type: 'boolean', + name: 'resetMargin', + title: 'Reset Margin', + }, + ], + }, + ], + }, + { + type: 'textarea', + name: 'text', + title: 'Description', + }, + { + type: 'textarea', + name: 'additionalInfo', + title: 'Additional Info', + }, + { + type: 'select', + name: 'size', + title: 'Size', + enum: contentThemesEnum, + view: 'select', + mode: 'single', + }, + ], +}; diff --git a/src/sub-blocks/BackgroundCard/index.tsx b/src/sub-blocks/BackgroundCard/index.tsx new file mode 100644 index 0000000000..17b5255d5f --- /dev/null +++ b/src/sub-blocks/BackgroundCard/index.tsx @@ -0,0 +1,24 @@ +import {JSONSchemaType} from 'ajv'; + +import {BlockData} from '../../constructor-items'; +import {generateFromAJV} from '../../utils/form-generator'; + +import BackgroundCard from './BackgroundCard'; +import {BackgroundCard as BackgroundCardSchema} from './schema'; + +const BackgroundCardConfig: BlockData = { + component: BackgroundCard, + schema: { + name: 'Background Card', + inputs: generateFromAJV( + BackgroundCardSchema['background-card'] as unknown as JSONSchemaType<{}>, + ), + default: { + title: 'Background Card', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + additionalInfo: 'Additional info', + }, + }, +}; + +export default BackgroundCardConfig; diff --git a/src/sub-blocks/BannerCard/BannerCard.tsx b/src/sub-blocks/BannerCard/BannerCard.tsx index 75da986611..52e5190695 100644 --- a/src/sub-blocks/BannerCard/BannerCard.tsx +++ b/src/sub-blocks/BannerCard/BannerCard.tsx @@ -11,7 +11,7 @@ export const BannerCard = (props: BannerCardProps) => { const { title, subtitle, - button: {url, text, target, theme: buttonTheme = 'raised'} = {}, + button, color, theme: textTheme = 'light', image, @@ -21,6 +21,8 @@ export const BannerCard = (props: BannerCardProps) => { const theme = useTheme(); const contentStyle: Record<string, string> = {}; + const {url, text, target, theme: buttonTheme = 'raised'} = button || {}; + if (color) { contentStyle.backgroundColor = getThemedValue(color, theme); } diff --git a/src/sub-blocks/BannerCard/index.tsx b/src/sub-blocks/BannerCard/index.tsx new file mode 100644 index 0000000000..8976cea40a --- /dev/null +++ b/src/sub-blocks/BannerCard/index.tsx @@ -0,0 +1,25 @@ +import {JSONSchemaType} from 'ajv'; + +import {BannerCardProps} from '../../blocks/Banner/schema'; +import {BlockData} from '../../constructor-items'; +import {generateFromAJV} from '../../utils/form-generator'; + +import BannerCard from './BannerCard'; + +const BannerCardConfig: BlockData = { + component: BannerCard, + schema: { + name: 'Banner Card', + inputs: generateFromAJV(BannerCardProps as unknown as JSONSchemaType<{}>), + default: { + color: 'rgba(54, 151, 241, 0.4)', + title: 'Banner Card', + subtitle: 'Some sort of description.', + button: { + text: 'Read more', + }, + }, + }, +}; + +export default BannerCardConfig; diff --git a/src/sub-blocks/BasicCard/index.tsx b/src/sub-blocks/BasicCard/index.tsx new file mode 100644 index 0000000000..23a4a85cf9 --- /dev/null +++ b/src/sub-blocks/BasicCard/index.tsx @@ -0,0 +1,21 @@ +import {JSONSchemaType} from 'ajv'; + +import {BlockData} from '../../constructor-items'; +import {generateFromAJV} from '../../utils/form-generator'; + +import BasicCard from './BasicCard'; +import {BasicCard as BasicCardSchema} from './schema'; + +const BasicCardConfig: BlockData = { + component: BasicCard, + schema: { + name: 'Basic Card', + inputs: generateFromAJV(BasicCardSchema['basic-card'] as unknown as JSONSchemaType<{}>), + default: { + title: 'Basic Card', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + }, +}; + +export default BasicCardConfig; diff --git a/src/sub-blocks/Content/index.tsx b/src/sub-blocks/Content/index.tsx new file mode 100644 index 0000000000..55eedd9c75 --- /dev/null +++ b/src/sub-blocks/Content/index.tsx @@ -0,0 +1,21 @@ +import {JSONSchemaType} from 'ajv'; + +import {BlockData} from '../../constructor-items'; +import {generateFromAJV} from '../../utils/form-generator'; + +import Content from './Content'; +import {ContentBlock} from './schema'; + +const ContentConfig: BlockData = { + component: Content, + schema: { + name: 'Content', + inputs: generateFromAJV(ContentBlock['content'] as unknown as JSONSchemaType<{}>), + default: { + title: 'Content', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + }, +}; + +export default ContentConfig; diff --git a/src/sub-blocks/Content/schema.ts b/src/sub-blocks/Content/schema.ts index ea3bd247c9..03dfb68fe4 100644 --- a/src/sub-blocks/Content/schema.ts +++ b/src/sub-blocks/Content/schema.ts @@ -92,6 +92,7 @@ export const ContentBase = { export const ContentBlock = { content: { + type: 'object', additionalProperties: false, properties: { ...ContentBase, diff --git a/src/sub-blocks/Divider/index.tsx b/src/sub-blocks/Divider/index.tsx new file mode 100644 index 0000000000..8256c4fbc8 --- /dev/null +++ b/src/sub-blocks/Divider/index.tsx @@ -0,0 +1,18 @@ +import {JSONSchemaType} from 'ajv'; + +import {BlockData} from '../../constructor-items'; +import {generateFromAJV} from '../../utils/form-generator'; + +import Divider from './Divider'; +import {Divider as DividerSchema} from './schema'; + +const DividerConfig: BlockData = { + component: Divider, + schema: { + name: 'Divider', + inputs: generateFromAJV(DividerSchema['divider'] as unknown as JSONSchemaType<{}>), + default: {}, + }, +}; + +export default DividerConfig; diff --git a/src/sub-blocks/ImageCard/ImageCard.tsx b/src/sub-blocks/ImageCard/ImageCard.tsx index 735105306f..0c503362c3 100644 --- a/src/sub-blocks/ImageCard/ImageCard.tsx +++ b/src/sub-blocks/ImageCard/ImageCard.tsx @@ -47,12 +47,14 @@ const ImageCard = (props: ImageCardProps) => { const cardContent = ( <React.Fragment> - <div className={b('image', {margins})}> - <Image - className={b('image_inner', {radius: enableImageBorderRadius})} - {...imageProps} - /> - </div> + {image && ( + <div className={b('image', {margins})}> + <Image + className={b('image_inner', {radius: enableImageBorderRadius})} + {...imageProps} + /> + </div> + )} {hasContent && ( <div className={b('content')}> <Content diff --git a/src/sub-blocks/ImageCard/index.tsx b/src/sub-blocks/ImageCard/index.tsx new file mode 100644 index 0000000000..121e1a304c --- /dev/null +++ b/src/sub-blocks/ImageCard/index.tsx @@ -0,0 +1,21 @@ +import {JSONSchemaType} from 'ajv'; + +import {BlockData} from '../../constructor-items'; +import {generateFromAJV} from '../../utils/form-generator'; + +import ImageCard from './ImageCard'; +import {ImageCard as ImageCardSchema} from './schema'; + +const ImageCardConfig: BlockData = { + component: ImageCard, + schema: { + name: 'Image Card', + inputs: generateFromAJV(ImageCardSchema['image-card'] as unknown as JSONSchemaType<{}>), + default: { + title: 'Image Card', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + }, +}; + +export default ImageCardConfig; diff --git a/src/sub-blocks/LayoutItem/LayoutItem.tsx b/src/sub-blocks/LayoutItem/LayoutItem.tsx index 8bd78976a7..77a49b1ca6 100644 --- a/src/sub-blocks/LayoutItem/LayoutItem.tsx +++ b/src/sub-blocks/LayoutItem/LayoutItem.tsx @@ -16,7 +16,7 @@ import './LayoutItem.scss'; const b = block('layout-item'); const LayoutItem = ({ - content: {links, ...content}, + content: {links = [], ...content} = {}, contentMargin = 'm', metaInfo, media, diff --git a/src/sub-blocks/LayoutItem/form.ts b/src/sub-blocks/LayoutItem/form.ts new file mode 100644 index 0000000000..301899c2a5 --- /dev/null +++ b/src/sub-blocks/LayoutItem/form.ts @@ -0,0 +1,16 @@ +import {JSONSchemaType} from 'ajv'; + +import {generateFromAJV} from '../../utils/form-generator'; + +import LayoutItem from './LayoutItem'; +import {LayoutItem as LayoutItemSchema} from './schema'; + +const LayoutItemConfig = { + component: LayoutItem, + schema: { + name: 'Layout Item', + inputs: generateFromAJV(LayoutItemSchema as unknown as JSONSchemaType<{}>), + }, +}; + +export default LayoutItemConfig; diff --git a/src/sub-blocks/LayoutItem/index.tsx b/src/sub-blocks/LayoutItem/index.tsx new file mode 100644 index 0000000000..7113f14992 --- /dev/null +++ b/src/sub-blocks/LayoutItem/index.tsx @@ -0,0 +1,23 @@ +import {JSONSchemaType} from 'ajv'; + +import {BlockData} from '../../constructor-items'; +import {generateFromAJV} from '../../utils/form-generator'; + +import LayoutItem from './LayoutItem'; +import {LayoutItem as LayoutItemSchema} from './schema'; + +const LayoutItemConfig: BlockData = { + component: LayoutItem, + schema: { + name: 'Layout Item', + inputs: generateFromAJV(LayoutItemSchema as unknown as JSONSchemaType<{}>), + default: { + content: { + title: 'Layout Item', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + }, + }, +}; + +export default LayoutItemConfig; diff --git a/src/sub-blocks/LayoutItem/schema.ts b/src/sub-blocks/LayoutItem/schema.ts index 4ae79c4f2c..5722b1539f 100644 --- a/src/sub-blocks/LayoutItem/schema.ts +++ b/src/sub-blocks/LayoutItem/schema.ts @@ -1,5 +1,6 @@ import omit from 'lodash/omit'; +import {Media} from '../../blocks/Media/schema'; import metaInfo from '../../components/MetaInfo/schema'; import {BaseProps, CardLayoutProps, MediaProps} from '../../schema/validators/common'; import {AnalyticsEventSchema} from '../../schema/validators/event'; @@ -13,6 +14,7 @@ export const LayoutItem = { ...BaseProps, ...CardLayoutProps, media: MediaProps, + media: Media, content: omit(ContentBase, ['colSize', 'size', 'centered']), contentMargin: { type: 'string', diff --git a/src/sub-blocks/MediaCard/index.tsx b/src/sub-blocks/MediaCard/index.tsx new file mode 100644 index 0000000000..22f6c9b7ed --- /dev/null +++ b/src/sub-blocks/MediaCard/index.tsx @@ -0,0 +1,23 @@ +import {JSONSchemaType} from 'ajv'; + +import {BlockData} from '../../constructor-items'; +import {generateFromAJV} from '../../utils/form-generator'; + +import MediaCard from './MediaCard'; +import {MediaCardBlock as MediaCardSchema} from './schema'; + +const MediaCardConfig: BlockData = { + component: MediaCard, + schema: { + name: 'Media Card', + inputs: generateFromAJV(MediaCardSchema['media-card'] as unknown as JSONSchemaType<{}>), + default: { + content: { + title: 'Media Card', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + }, + }, +}; + +export default MediaCardConfig; diff --git a/src/sub-blocks/PriceCard/index.tsx b/src/sub-blocks/PriceCard/index.tsx new file mode 100644 index 0000000000..604934389d --- /dev/null +++ b/src/sub-blocks/PriceCard/index.tsx @@ -0,0 +1,34 @@ +import {JSONSchemaType} from 'ajv'; + +import {BlockData} from '../../constructor-items'; +import {generateFromAJV} from '../../utils/form-generator'; + +import PriceCard from './PriceCard'; +import {PriceCardBlock as PriceCardSchema} from './schema'; + +const PriceCardConfig: BlockData = { + component: PriceCard, + schema: { + name: 'Price Card', + inputs: generateFromAJV(PriceCardSchema['price-card'] as unknown as JSONSchemaType<{}>), + default: { + type: 'price-card', + border: 'line', + title: 'Basic', + price: '100 $', + pricePeriod: 'month', + priceDetails: '+ 5% from check', + description: 'For any purposes', + buttons: [ + { + url: '/', + text: 'Read More', + width: 'max', + theme: 'action', + }, + ], + }, + }, +}; + +export default PriceCardConfig; diff --git a/src/sub-blocks/PriceDetailed/PriceDetailed.tsx b/src/sub-blocks/PriceDetailed/PriceDetailed.tsx index e1fb017cbf..e94bb6c7fc 100644 --- a/src/sub-blocks/PriceDetailed/PriceDetailed.tsx +++ b/src/sub-blocks/PriceDetailed/PriceDetailed.tsx @@ -18,7 +18,7 @@ import SeparatePriceDetailed from './SeparatePriceDetailed/SeparatePriceDetailed const PriceDetailed = (props: PriceDetailedProps) => { const { priceType = PriceDetailsType.SETTINGS, - items, + items = [], numberGroupItems = 1, description, details, diff --git a/src/sub-blocks/PriceDetailed/index.tsx b/src/sub-blocks/PriceDetailed/index.tsx new file mode 100644 index 0000000000..f031b87d9f --- /dev/null +++ b/src/sub-blocks/PriceDetailed/index.tsx @@ -0,0 +1,38 @@ +import {JSONSchemaType} from 'ajv'; + +import {BlockData} from '../../constructor-items'; +import {generateFromAJV} from '../../utils/form-generator'; + +import PriceDetailed from './PriceDetailed'; +import {PriceDetailedBlock as PriceDetailedSchema} from './schema'; + +/** @deprecated */ +const PriceDetailedConfig: BlockData = { + component: PriceDetailed, + schema: { + name: 'Price Detailed', + inputs: generateFromAJV( + PriceDetailedSchema['price-detailed'] as unknown as JSONSchemaType<{}>, + ), + default: { + priceType: 'marked-list', + items: [ + { + title: '100$', + description: 'Basic edition', + detailedTitle: 'per year', + items: [ + { + text: 'First item', + }, + { + text: 'Second item', + }, + ], + }, + ], + }, + }, +}; + +export default PriceDetailedConfig; diff --git a/src/sub-blocks/Quote/index.tsx b/src/sub-blocks/Quote/index.tsx new file mode 100644 index 0000000000..47f45c716b --- /dev/null +++ b/src/sub-blocks/Quote/index.tsx @@ -0,0 +1,24 @@ +import {JSONSchemaType} from 'ajv'; + +import {BlockData} from '../../constructor-items'; +import {generateFromAJV} from '../../utils/form-generator'; + +import Quote from './Quote'; +import {Quote as QuoteSchema} from './schema'; + +const QuoteConfig: BlockData = { + component: Quote, + schema: { + name: 'Quote', + inputs: generateFromAJV(QuoteSchema['quote'] as unknown as JSONSchemaType<{}>), + default: { + text: 'A good decision is based on knowledge and not on numbers.', + author: { + firstName: ' Plato', + description: 'Greek philosopher', + }, + }, + }, +}; + +export default QuoteConfig; diff --git a/src/utils/editor.ts b/src/utils/editor.ts new file mode 100644 index 0000000000..58cd311cef --- /dev/null +++ b/src/utils/editor.ts @@ -0,0 +1,20 @@ +import {MouseEvent} from 'react'; + +export const getCursorPositionOverElement = (elementRect: DOMRect, mouseEvent: MouseEvent) => { + const cursorPositionY = elementRect.height - (mouseEvent.clientY - elementRect.y); + const cursorPositionX = elementRect.width - (mouseEvent.clientX - elementRect.x); + const cursorRatioY = elementRect.height / 2 / cursorPositionY; + const cursorRatioX = elementRect.width / 2 / cursorPositionX; + + if (cursorRatioY > cursorRatioX) { + if (cursorRatioY >= 1) { + return 'bottom'; + } else { + return 'left'; + } + } else if (cursorRatioX >= 1) { + return 'right'; + } else { + return 'top'; + } +}; diff --git a/src/utils/form-generator.ts b/src/utils/form-generator.ts new file mode 100644 index 0000000000..9c8af8ca01 --- /dev/null +++ b/src/utils/form-generator.ts @@ -0,0 +1,145 @@ +import {JSONSchemaType} from 'ajv'; + +import { + ArrayBaseInput, + BooleanInput, + ConfigInput, + NumberInput, + ObjectInput, + SelectBaseInput, + TextAreaInput, + TextInput, +} from '../common/types'; + +export const generateFromAJV = (schema: JSONSchemaType<{}>): ConfigInput[] => { + if (schema && schema.properties) { + const obj = Object.entries(schema.properties).map(([key, value]) => { + const innerSchema = value as JSONSchemaType<{}>; + // eslint-disable-next-line @typescript-eslint/no-use-before-define + return generateSingleEntity(key, innerSchema); + }); + + return obj.filter(Boolean) as ConfigInput[]; + } + return []; +}; + +export const generateSingleEntity = (key: string, schema: JSONSchemaType<{}>) => { + const type = schema.type; + + if (!type && schema.enum) { + return { + type: 'select', + view: 'select', + name: key, + title: key, + enum: schema.enum.map((enumValue: string) => ({ + content: enumValue, + value: enumValue, + })), + } as SelectBaseInput; + } + + if (schema.oneOf) { + return { + type: 'oneOf', + name: key, + title: key, + options: schema.oneOf.map((item: JSONSchemaType<{}>) => { + let properties; + if (item.properties) { + properties = generateFromAJV(item); + } else { + properties = [ + generateSingleEntity('', { + ...item, + name: '', + title: item.optionName, + }), + ]; + } + + return { + value: item.optionName, + title: item.optionName, + properties: properties, + }; + }), + }; + } + + switch (type) { + case 'string': { + if (schema.inputType === 'textarea') { + return { + type: 'textarea', + name: key, + title: key, + } as TextAreaInput; + } + if (schema.enum) { + return { + type: 'select', + view: 'select', + name: key, + title: key, + enum: schema.enum.map((enumValue: string) => ({ + content: enumValue, + value: enumValue, + })), + } as SelectBaseInput; + } + return { + type: 'text', + name: key, + title: key, + } as TextInput; + } + case 'number': { + return { + type: 'number', + name: key, + title: key, + } as NumberInput; + } + case 'object': { + return { + type: 'object', + name: key, + title: key, + properties: generateFromAJV(schema), + } as ObjectInput; + } + case 'boolean': { + return { + type: 'boolean', + name: key, + title: key, + properties: generateFromAJV(schema), + } as BooleanInput; + } + case 'array': { + if (schema.items.type === 'string') { + return { + type: 'array', + name: key, + title: key, + properties: generateFromAJV(schema.items), + buttonText: 'Add', + arrayType: 'text', + } as ArrayBaseInput; + } + + return { + type: 'array', + name: key, + title: key, + properties: generateFromAJV(schema.items), + buttonText: 'Add', + arrayType: 'object', + } as ArrayBaseInput; + } + } + + return undefined; +}; diff --git a/src/utils/store.ts b/src/utils/store.ts new file mode 100644 index 0000000000..9bf940a34e --- /dev/null +++ b/src/utils/store.ts @@ -0,0 +1,22 @@ +import {StoreApi, create} from 'zustand'; +import {devtools} from 'zustand/middleware'; + +export function initializeStore<State, Methods>( + initialState: State, + methods: ( + set: StoreApi<State & Methods>['setState'], + get: StoreApi<State & Methods>['getState'], + ) => Methods, +) { + return (overrideInitialState?: Partial<State>) => + create< + State & Methods, + [['zustand/devtools', never], ['zustand/persist', State & Methods]] + >( + devtools((set, get) => ({ + ...initialState, + ...overrideInitialState, + ...methods(set, get), + })), + ); +} diff --git a/styles/mixins.scss b/styles/mixins.scss index dedcd9f60b..458279156c 100644 --- a/styles/mixins.scss +++ b/styles/mixins.scss @@ -177,12 +177,12 @@ @mixin block { @include add-specificity(&) { - margin-top: $indentL; - padding: 0 0 $indentL; + //margin-top: $indentL; + padding: $indentL 0; &:first-child { // @deprecated - margin-top: var(--pc-first-block-indent, #{$indentXXL}); + //margin-top: var(--pc-first-block-indent, #{$indentXXL}); } } } @@ -601,27 +601,27 @@ unpredictable css rules order in build */ @include add-specificity(&) { &_indentTop { &_0 { - margin-top: 0; + padding-top: 0; } &_xs { - margin-top: $indentXS; + padding-top: $indentXS; } &_s { - margin-top: $indentSM; + padding-top: $indentSM; } &_m { - margin-top: $indentM; + padding-top: $indentM; } &_l { - margin-top: $indentL; + padding-top: $indentL; } &_xl { - margin-top: $indentXL; + padding-top: $indentXL; } } From d316c6c2be51db58ba683268f0893aa127fe5f7d Mon Sep 17 00:00:00 2001 From: daff <daff@yandex-team.ru> Date: Sat, 12 Oct 2024 15:47:28 +0300 Subject: [PATCH 002/118] feat: add yaml support for editor v2 --- .../components/SourceCode/SourceCode.scss | 24 ++++++-- .../components/SourceCode/SourceCode.tsx | 60 +++++++++++++++++-- 2 files changed, 72 insertions(+), 12 deletions(-) diff --git a/src/editor-v2/components/SourceCode/SourceCode.scss b/src/editor-v2/components/SourceCode/SourceCode.scss index c63709db20..205c407b97 100644 --- a/src/editor-v2/components/SourceCode/SourceCode.scss +++ b/src/editor-v2/components/SourceCode/SourceCode.scss @@ -7,20 +7,32 @@ $block: '.#{$ns}source-code'; #{$block} { - padding: 10px; - white-space: pre; - - @include text-code-inline-1; - + height: 100%; + box-sizing: border-box; display: flex; flex-direction: column; gap: 10px; &__code { - user-select: all; + white-space: pre; + @include text-code-inline-1; + padding: 0 10px 10px; + overflow: auto; + } + + &__head { + display: flex; + align-items: center; + justify-content: flex-start; + gap: 8px; + padding: 10px 10px 0; } &__textarea textarea { @include text-code-inline-1; } + + &__alert { + margin-bottom: 8px; + } } diff --git a/src/editor-v2/components/SourceCode/SourceCode.tsx b/src/editor-v2/components/SourceCode/SourceCode.tsx index 02b52d1628..e31c840723 100644 --- a/src/editor-v2/components/SourceCode/SourceCode.tsx +++ b/src/editor-v2/components/SourceCode/SourceCode.tsx @@ -1,6 +1,16 @@ import React, {useState} from 'react'; -import {Button, Dialog, TextArea} from '@gravity-ui/uikit'; +import {ArrowDownToSquare, Copy, CopyCheck} from '@gravity-ui/icons'; +import { + Alert, + Button, + CopyToClipboard, + Dialog, + Icon, + RadioButton, + TextArea, +} from '@gravity-ui/uikit'; +import yaml from 'js-yaml'; import {ClassNameProps, PageContent} from '../../../models'; import {block} from '../../../utils'; @@ -16,34 +26,72 @@ const SourceCode: React.FC<SourceCodeProps> = ({className}) => { const {config, setConfig} = useContentConfigStore(); const [isOpen, setIsOpen] = useState(false); const [tempConfig, setTempConfig] = useState(''); + const [format, setFormat] = useState<'yaml' | 'json'>('yaml'); const onUpdate = () => { let object; try { - object = JSON.parse(tempConfig); + if (tempConfig.trim().startsWith('{') && tempConfig.trim().endsWith('}')) { + object = JSON.parse(tempConfig); + } else { + object = yaml.load(tempConfig); + } } catch { // eslint-disable-next-line no-console console.error('JSON.parse failed'); } - setConfig(object as PageContent); + if (object) { + setConfig(object as PageContent); + } + setIsOpen(false); }; + const text = format === 'yaml' ? yaml.dump(config) : JSON.stringify(config, null, 2); + return ( <div className={b(null, className)}> - <Button onClick={() => setIsOpen(true)}>Update</Button> - <div className={b('code')}>{JSON.stringify(config, null, 2)}</div> + <div className={b('head')}> + <RadioButton value={format} onUpdate={setFormat}> + <RadioButton.Option value={'yaml'} content={'YAML'} /> + <RadioButton.Option value={'json'} content={'JSON'} /> + </RadioButton> + <CopyToClipboard text={text} timeout={1000}> + {(status) => ( + <Button> + {status === 'pending' ? ( + <Icon data={Copy} /> + ) : ( + <Icon data={CopyCheck} /> + )} + Copy + </Button> + )} + </CopyToClipboard> + <Button onClick={() => setIsOpen(true)}> + <Icon data={ArrowDownToSquare} /> + Import + </Button> + </div> + + <div className={b('code')}>{text}</div> <Dialog onClose={() => setIsOpen(false)} open={isOpen} size={'l'}> <Dialog.Header caption="New configuration" /> <Dialog.Body> + <Alert + className={b('alert')} + theme={'info'} + title={'You can use YAML or JSON'} + message={'The editor will automatically understand which format is needed.'} + ></Alert> <TextArea className={b('textarea')} value={tempConfig} onUpdate={setTempConfig} - rows={30} + rows={25} /> </Dialog.Body> <Dialog.Footer From 5ea3e09c813a9fa3c141ad18317020363dc09869 Mon Sep 17 00:00:00 2001 From: daff <daff@yandex-team.ru> Date: Tue, 15 Oct 2024 16:57:05 +0300 Subject: [PATCH 003/118] feat: add zoom support for editor v2 --- .../components/BigOverlay/BigOverlay.tsx | 11 +-- .../components/MiddleScreen/MiddleScreen.scss | 9 ++- .../components/MiddleScreen/MiddleScreen.tsx | 54 +++++++++------ src/editor-v2/components/TopBar/TopBar.tsx | 22 +++++- .../components/ViewSwitches/ViewSwitches.scss | 9 +++ .../components/ViewSwitches/ViewSwitches.tsx | 67 ++++++++++--------- src/editor-v2/constants.ts | 1 + src/editor-v2/context/iframeContext/store.ts | 36 +++++++++- 8 files changed, 145 insertions(+), 64 deletions(-) create mode 100644 src/editor-v2/constants.ts diff --git a/src/editor-v2/components/BigOverlay/BigOverlay.tsx b/src/editor-v2/components/BigOverlay/BigOverlay.tsx index 02fa4679a9..345e18ad5d 100644 --- a/src/editor-v2/components/BigOverlay/BigOverlay.tsx +++ b/src/editor-v2/components/BigOverlay/BigOverlay.tsx @@ -10,7 +10,7 @@ import { } from '../../../common/types'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; -import {IframeContext} from '../../context/iframeContext'; +import {IframeContext, useIframeStore} from '../../context/iframeContext'; import {useMessageObserver} from '../../context/messagesContext'; import './BigOverlay.scss'; @@ -20,6 +20,7 @@ const b = block('big-overlay'); interface BigOverlayProps extends ClassNameProps {} const BigOverlay: React.FC<BigOverlayProps> = ({className}) => { + const {zoom} = useIframeStore(); const {iframeElement} = useContext(IframeContext); const [mousePosition, setMousePosition] = useState<{x: number; y: number} | undefined>( undefined, @@ -32,13 +33,15 @@ const BigOverlay: React.FC<BigOverlayProps> = ({className}) => { const {x, y} = payload.cursor; const iframeRect = iframeElement?.getClientRects().item(0); if (iframeRect) { - const newX = meta.source === 'editor' ? x : x + iframeRect.x; - const newY = meta.source === 'editor' ? y : y + iframeRect.y; + const zoomedX = (x * zoom) / 100; + const zoomedY = (y * zoom) / 100; + const newX = meta.source === 'editor' ? x : zoomedX + iframeRect.x; + const newY = meta.source === 'editor' ? y : zoomedY + iframeRect.y; setMousePosition({x: newX, y: newY}); } } }, - [iframeElement], + [iframeElement, zoom], ); useMessageObserver<InsertModeDisableAction>(ActionTypes.InsertModeDisable, () => { diff --git a/src/editor-v2/components/MiddleScreen/MiddleScreen.scss b/src/editor-v2/components/MiddleScreen/MiddleScreen.scss index bb24c8168a..16c869fa1c 100644 --- a/src/editor-v2/components/MiddleScreen/MiddleScreen.scss +++ b/src/editor-v2/components/MiddleScreen/MiddleScreen.scss @@ -13,12 +13,15 @@ $block: '.#{$ns}middle-screen'; flex-direction: column; &__topbar { - height: 50px; + height: 40px; } - &__canvas { + &__wrapper { height: 100%; - width: 100%; + } + + &__canvas { + transform-origin: left top; overflow-y: scroll; position: relative; } diff --git a/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx b/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx index eebe7469c0..e48ed564a6 100644 --- a/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx +++ b/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx @@ -16,34 +16,48 @@ const b = block('middle-screen'); interface MiddleScreenProps extends ClassNameProps {} const MiddleScreen: React.FC<MiddleScreenProps> = ({className}) => { - const {url, height} = useIframeStore(); + const {url, height, zoom, decreaseZoom, increaseZoom, setZoom} = useIframeStore(); const {initialized} = useEditorStore(); const {setIframeElement} = useContext(IframeContext); return ( <div className={b(null, className)}> <div className={b('topbar')}> - <TopBar /> + <TopBar + onZoomUpdate={setZoom} + zoom={zoom} + onDecreaseZoom={decreaseZoom} + onIncreaseZoom={increaseZoom} + /> </div> - <div className={b('canvas', {hidden: !initialized})}> - <iframe - ref={(element) => { - if (element) { - setIframeElement(element); - } + <div className={b('wrapper')}> + <div + className={b('canvas', {hidden: !initialized})} + style={{ + transform: `scale(${zoom}%)`, + height: `${(100 / zoom) * 100}%`, + width: `${(100 / zoom) * 100}%`, }} - className={b('iframe')} - src={url} - height={`${height}px`} - width="100%" - frameBorder="0" - /> - <Overlay className={b('overlay')} /> - {!initialized && ( - <div className={b('loading')}> - <Loader size={'l'} /> - </div> - )} + > + <iframe + ref={(element) => { + if (element) { + setIframeElement(element); + } + }} + className={b('iframe')} + src={url} + height={`${height}px`} + width="100%" + frameBorder="0" + /> + <Overlay className={b('overlay')} /> + {!initialized && ( + <div className={b('loading')}> + <Loader size={'l'} /> + </div> + )} + </div> </div> </div> ); diff --git a/src/editor-v2/components/TopBar/TopBar.tsx b/src/editor-v2/components/TopBar/TopBar.tsx index af8566fcf3..bf9c0b365d 100644 --- a/src/editor-v2/components/TopBar/TopBar.tsx +++ b/src/editor-v2/components/TopBar/TopBar.tsx @@ -9,13 +9,29 @@ import './TopBar.scss'; const b = block('topbar'); -interface TopBarProps extends ClassNameProps {} +interface TopBarProps extends ClassNameProps { + onZoomUpdate: (zoom: number) => void; + onDecreaseZoom: () => void; + onIncreaseZoom: () => void; + zoom: number; +} -const TopBar: React.FC<TopBarProps> = ({className}) => { +const TopBar: React.FC<TopBarProps> = ({ + zoom, + onDecreaseZoom, + onIncreaseZoom, + onZoomUpdate, + className, +}) => { return ( <div className={b(null, className)}> <div className={b('switches')}> - <ViewSwitches /> + <ViewSwitches + onZoomUpdate={onZoomUpdate} + onDecreaseZoom={onDecreaseZoom} + onIncreaseZoom={onIncreaseZoom} + zoom={zoom} + /> </div> <Source className={b('source')} /> </div> diff --git a/src/editor-v2/components/ViewSwitches/ViewSwitches.scss b/src/editor-v2/components/ViewSwitches/ViewSwitches.scss index ca2c5f7be9..45751b5989 100644 --- a/src/editor-v2/components/ViewSwitches/ViewSwitches.scss +++ b/src/editor-v2/components/ViewSwitches/ViewSwitches.scss @@ -9,4 +9,13 @@ $block: '.#{$ns}view-switches'; #{$block} { display: inline-flex; gap: 12px; + + &__zoom { + display: flex; + gap: 4px; + } + + &__zoom-input { + width: 40px; + } } diff --git a/src/editor-v2/components/ViewSwitches/ViewSwitches.tsx b/src/editor-v2/components/ViewSwitches/ViewSwitches.tsx index b96b73fe03..cb9cd65786 100644 --- a/src/editor-v2/components/ViewSwitches/ViewSwitches.tsx +++ b/src/editor-v2/components/ViewSwitches/ViewSwitches.tsx @@ -1,48 +1,49 @@ import React from 'react'; -import {Display, Eye, Smartphone, Square, SquareDashedText} from '@gravity-ui/icons'; -import {Icon, RadioButton, RadioButtonOption} from '@gravity-ui/uikit'; +import {Minus, Plus} from '@gravity-ui/icons'; +import {Button, Icon, Select} from '@gravity-ui/uikit'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; +import {ZOOM_STEPS} from '../../constants'; import './ViewSwitches.scss'; const b = block('view-switches'); -interface ViewSwitchesProps extends ClassNameProps {} - -const options1: RadioButtonOption[] = [ - {value: 'edit-mode', content: <Icon data={SquareDashedText} />}, - {value: 'preview-mode', content: <Icon data={Eye} />}, -]; - -export const options2: RadioButtonOption[] = [ - {value: 'desktop-mode', content: <Icon data={Display} />}, - {value: 'tablet-mode', content: <Icon data={Square} />}, - {value: 'phone-mode', content: <Icon data={Smartphone} />}, -]; - -// TODO: To be done -const ViewSwitches: React.FC<ViewSwitchesProps> = ({className}) => { +interface ViewSwitchesProps extends ClassNameProps { + onZoomUpdate: (zoom: number) => void; + onDecreaseZoom: () => void; + onIncreaseZoom: () => void; + zoom: number; +} + +const ViewSwitches: React.FC<ViewSwitchesProps> = ({ + zoom, + onIncreaseZoom, + onDecreaseZoom, + onZoomUpdate, + className, +}) => { return ( <div className={b(null, className)}> - <RadioButton - className={b('switch')} - name="group1" - defaultValue={options1[0].value} - options={options1} - size="m" - disabled - /> - <RadioButton - className={b('switch')} - name="group2" - defaultValue={options2[0].value} - options={options2} - size="m" - disabled - /> + <div className={b('zoom')}> + <Button view={'flat'} onClick={onDecreaseZoom}> + <Icon data={Minus} /> + </Button> + <Select + multiple={false} + value={[String(zoom)]} + options={ZOOM_STEPS.map((step) => ({ + value: String(step), + content: `${String(step)}%`, + }))} + onUpdate={(value) => onZoomUpdate(Number(value))} + /> + <Button view={'flat'} onClick={onIncreaseZoom}> + <Icon data={Plus} /> + </Button> + </div> </div> ); }; diff --git a/src/editor-v2/constants.ts b/src/editor-v2/constants.ts new file mode 100644 index 0000000000..22c3e53f27 --- /dev/null +++ b/src/editor-v2/constants.ts @@ -0,0 +1 @@ +export const ZOOM_STEPS = [25, 33, 50, 75, 100, 125, 150, 200, 250, 300]; diff --git a/src/editor-v2/context/iframeContext/store.ts b/src/editor-v2/context/iframeContext/store.ts index 756ba17fd8..c5902b9027 100644 --- a/src/editor-v2/context/iframeContext/store.ts +++ b/src/editor-v2/context/iframeContext/store.ts @@ -1,13 +1,19 @@ import {ActionTypes, WithStoreReducer} from '../../../common/types'; +import {ZOOM_STEPS} from '../../constants'; import {initializeStore} from '../../utils/store'; export interface IframeState { url: string; height?: number; + // In percents + zoom: number; } export interface IframeMethods extends WithStoreReducer { setUrl: (url: string) => void; + setZoom: (zoom: number) => void; + increaseZoom: () => void; + decreaseZoom: () => void; } export type IframeStore = IframeState & IframeMethods; @@ -16,8 +22,36 @@ export const createIframeStore = initializeStore<IframeState, IframeMethods>( { url: '', height: 400, + zoom: 100, }, - (set) => ({ + (set, get) => ({ + setZoom: function (zoom) { + if (zoom > 0) { + set((state) => ({...state, zoom})); + } + }, + increaseZoom: function () { + const currentZoom = get().zoom; + + for (const step of ZOOM_STEPS) { + if (currentZoom < step) { + get().setZoom(step); + break; + } + } + }, + decreaseZoom: function () { + const currentZoom = get().zoom; + const reverseSteps = ZOOM_STEPS.slice().reverse(); + + for (const step of reverseSteps) { + if (currentZoom > step) { + get().setZoom(step); + break; + } + } + }, + setUrl: (url) => set((state) => ({...state, url})), reducer: (action) => { switch (action.type) { From f94f3428f98a82b8e289b957179399192dfac953 Mon Sep 17 00:00:00 2001 From: daff <daff@yandex-team.ru> Date: Sat, 2 Nov 2024 14:26:31 +0300 Subject: [PATCH 004/118] feat: add groups, block buttons and panel open button --- src/blocks/Banner/index.tsx | 1 + src/blocks/CardLayout/index.ts | 26 +++++- src/blocks/Companies/index.ts | 1 + src/blocks/ContentLayout/index.ts | 66 ++++++++++++++- src/blocks/ExtendedFeatures/index.ts | 64 +++++++++++++- src/blocks/FilterBlock/FilterBlock.tsx | 1 - src/blocks/FilterBlock/index.ts | 83 ++++++++++++++++++- src/blocks/Form/index.ts | 1 + src/blocks/Header/index.ts | 19 +++++ src/blocks/HeaderSlider/index.ts | 1 + src/blocks/Icons/Icons.scss | 1 + src/blocks/Icons/Icons.tsx | 8 +- src/blocks/Icons/index.ts | 44 ++++++++++ src/blocks/Info/index.ts | 1 + src/blocks/Map/index.ts | 1 + src/blocks/Media/index.ts | 44 +++++++++- src/blocks/PromoFeaturesBlock/index.ts | 32 ++++++- src/blocks/Questions/index.ts | 1 + src/blocks/Share/index.ts | 1 + src/blocks/Slider/index.ts | 31 +++++++ src/blocks/Table/index.ts | 17 ++++ src/blocks/Tabs/index.ts | 1 + src/common/types/forms.ts | 1 + src/constructor-items.ts | 10 +-- .../components/BlocksList/BlocksList.scss | 31 ++++++- .../components/BlocksList/BlocksList.tsx | 62 ++++++++++++-- src/editor-v2/components/Panels/Panels.scss | 40 +++++++++ src/editor-v2/components/Panels/Panels.tsx | 79 ++++++++++++++++++ src/editor-v2/components/Tree/Tree.scss | 11 +++ src/editor-v2/components/Tree/Tree.tsx | 21 ++++- src/editor-v2/containers/Editor/Editor.scss | 13 --- src/editor-v2/containers/Editor/Editor.tsx | 30 ++----- src/editor-v2/context/contentConfig/store.ts | 7 ++ src/sub-blocks/BackgroundCard/index.tsx | 2 + src/sub-blocks/BannerCard/index.tsx | 1 + src/sub-blocks/BasicCard/index.tsx | 1 + src/sub-blocks/Content/index.tsx | 1 + src/sub-blocks/ImageCard/index.tsx | 1 + src/sub-blocks/LayoutItem/form.ts | 16 ---- src/sub-blocks/LayoutItem/index.tsx | 9 +- src/sub-blocks/MediaCard/index.tsx | 2 + src/sub-blocks/PriceCard/index.tsx | 1 + src/sub-blocks/PriceDetailed/index.tsx | 1 + src/sub-blocks/Quote/index.tsx | 1 + 44 files changed, 697 insertions(+), 89 deletions(-) create mode 100644 src/editor-v2/components/Panels/Panels.scss create mode 100644 src/editor-v2/components/Panels/Panels.tsx delete mode 100644 src/sub-blocks/LayoutItem/form.ts diff --git a/src/blocks/Banner/index.tsx b/src/blocks/Banner/index.tsx index 928df06605..58c9e84acb 100644 --- a/src/blocks/Banner/index.tsx +++ b/src/blocks/Banner/index.tsx @@ -10,6 +10,7 @@ const BannerBlockConfig: BlockData = { component: BannerBlock, schema: { name: 'Banner Block', + group: 'block', inputs: generateFromAJV(BannerCardProps as unknown as JSONSchemaType<{}>), default: { color: 'rgba(54, 151, 241, 0.4)', diff --git a/src/blocks/CardLayout/index.ts b/src/blocks/CardLayout/index.ts index b600e79dd5..2056f833c1 100644 --- a/src/blocks/CardLayout/index.ts +++ b/src/blocks/CardLayout/index.ts @@ -1,8 +1,12 @@ +import {JSONSchemaType} from 'ajv'; + import {ConfigInput} from '../../common/types'; import {BlockData} from '../../constructor-items'; import {sliderSizesArray, textSize} from '../../schema/validators/common'; +import {generateFromAJV} from '../../utils/form-generator'; import CardLayout from './CardLayout'; +import {CardLayoutProps} from './schema'; const textSizeEnum = textSize.map((size) => ({value: size, content: size})); @@ -155,11 +159,27 @@ const CardLayoutBlockConfig: BlockData = { component: CardLayout, schema: { name: 'Card Layout Block', - inputs: blockConfig, - // inputs: generateFromAJV(CardLayoutProps as unknown as JSONSchemaType<{}>), + group: 'card-containers', + inputs: generateFromAJV(CardLayoutProps as unknown as JSONSchemaType<{}>), default: { type: 'card-layout-block', - children: [], + children: [ + { + type: 'background-card', + title: 'Tell a story and build a narrative', + text: 'We are all storytellers. Stories are a powerful way to communicate ideas and share information. The right story can lead to a better understanding of a situation, make us laugh, or even inspire us to do something in the future.', + }, + { + type: 'background-card', + title: 'Tell a story and build a narrative', + text: 'We are all storytellers. Stories are a powerful way to communicate ideas and share information. The right story can lead to a better understanding of a situation, make us laugh, or even inspire us to do something in the future.', + }, + { + type: 'background-card', + title: 'Tell a story and build a narrative', + text: 'We are all storytellers. Stories are a powerful way to communicate ideas and share information. The right story can lead to a better understanding of a situation, make us laugh, or even inspire us to do something in the future.', + }, + ], title: 'Card Layout Block', description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', diff --git a/src/blocks/Companies/index.ts b/src/blocks/Companies/index.ts index dab19df95e..a1ee333c8c 100644 --- a/src/blocks/Companies/index.ts +++ b/src/blocks/Companies/index.ts @@ -10,6 +10,7 @@ const CompaniesBlockConfig: BlockData = { component: CompaniesBlock, schema: { name: 'Companies Block', + group: 'block', inputs: generateFromAJV( CompaniesBlockSchema['companies-block'] as unknown as JSONSchemaType<{}>, ), diff --git a/src/blocks/ContentLayout/index.ts b/src/blocks/ContentLayout/index.ts index 59dac4d754..50ce74ad07 100644 --- a/src/blocks/ContentLayout/index.ts +++ b/src/blocks/ContentLayout/index.ts @@ -9,11 +9,75 @@ const ContentLayoutBlockConfig = { component: ContentLayoutBlock, schema: { name: 'Content Layout Block', + group: 'block', inputs: generateFromAJV( ContentLayoutBlockSchema['content-layout-block'] as unknown as JSONSchemaType<{}>, ), default: { - title: 'Content Layout Block', + textContent: { + title: 'Lorem ipsum dolor sit amet', + text: 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + }, + additionalInfo: + 'Duis aute irure dolor in reprehenderit n voluptate velit esse cillum dolore eu fugiat nulla pariatur.', + buttons: [ + { + text: 'Button', + theme: 'action', + url: 'https://example.com', + }, + { + text: 'Button', + theme: 'outlined', + url: 'https://example.com', + }, + ], + links: [ + { + url: 'https://example.com', + text: 'Link', + theme: 'normal', + arrow: true, + }, + ], + fileContent: [ + { + href: 'https://example.xls', + text: 'File xls', + }, + { + href: 'https://example.fig', + text: 'File PNG, JPG, and SVG format', + }, + { + href: 'https://example.pdf', + text: 'Pdf file', + }, + { + href: 'https://example.zip', + text: 'Archive file', + }, + { + href: 'https://example.doc', + text: 'Microsoft Word document', + }, + { + href: 'https://example.ppt', + text: 'PPT file', + }, + ], + list: [ + { + title: 'Lorem ipsum', + text: 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + }, + { + text: 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + }, + { + title: 'Lorem ipsum ipsum', + }, + ], }, }, }; diff --git a/src/blocks/ExtendedFeatures/index.ts b/src/blocks/ExtendedFeatures/index.ts index e6b0a209d0..9876764de7 100644 --- a/src/blocks/ExtendedFeatures/index.ts +++ b/src/blocks/ExtendedFeatures/index.ts @@ -9,12 +9,72 @@ const ExtendedFeaturesBlockConfig = { component: ExtendedFeaturesBlock, schema: { name: 'Extended Features Block', + group: 'block', inputs: generateFromAJV( ExtendedFeaturesBlockSchema['extended-features-block'] as unknown as JSONSchemaType<{}>, ), default: { - title: 'Extended Features Block', - items: [{}], + type: 'extended-features-block', + title: { + text: 'Lorem ipsum dolor sit amet', + textSize: 'm', + }, + description: + 'Three cards in a row on the desktop, two cards in a row on a tablet, one card in a row on a mobile phone.', + items: [ + { + title: 'Sed do eiusmod tempor incididunt', + text: 'Ut enim ad minim veniam quis nostrud ullamco laboris nisi ut aliquip ex ea commodo consequat.', + additionalInfo: + 'Duis aute irure dolor in reprehenderit n voluptate velit esse cillum dolore eu fugiat nulla pariatur.', + }, + { + title: 'Sed do eiusmod tempor', + text: 'Ut enim ad minim veniam quis nostrud ullamco laboris nisi ut aliquip ex ea commodo consequat.', + buttons: [ + { + text: 'Button', + theme: 'action', + url: 'https://example.com', + }, + { + text: 'Button', + theme: 'outlined', + url: 'https://example.com', + }, + ], + }, + { + title: 'Sed do eiusmod tempor incididunt', + text: 'Ut enim ad minim veniam quis nostrud ullamco laboris nisi ut aliquip ex ea commodo consequat.', + links: [ + { + text: 'Go', + url: '#', + arrow: true, + theme: 'normal', + }, + ], + }, + { + title: 'Sed do eiusmod tempor incididunt', + text: 'Ut enim ad minim veniam quis nostrud ullamco laboris nisi ut aliquip ex ea commodo consequat.', + list: [ + { + title: 'Lorem ipsum', + text: 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + }, + { + title: 'Lorem ipsum ipsum', + text: 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + }, + { + title: 'Lorem ipsum', + text: 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + }, + ], + }, + ], }, }, }; diff --git a/src/blocks/FilterBlock/FilterBlock.tsx b/src/blocks/FilterBlock/FilterBlock.tsx index e161eaffe7..245f275749 100644 --- a/src/blocks/FilterBlock/FilterBlock.tsx +++ b/src/blocks/FilterBlock/FilterBlock.tsx @@ -79,7 +79,6 @@ const FilterBlock = ({ <CardLayoutBlock title="" colSizes={colSizes} className={b('cards-container')}> {cards.map((card, index) => { const key = getBlockKey(card, index); - return <ConstructorItem data={card} blockKey={index} key={key} />; })} </CardLayoutBlock> diff --git a/src/blocks/FilterBlock/index.ts b/src/blocks/FilterBlock/index.ts index 15a4a56b1a..f969c403bc 100644 --- a/src/blocks/FilterBlock/index.ts +++ b/src/blocks/FilterBlock/index.ts @@ -9,9 +9,90 @@ const FilterBlockConfig = { component: FilterBlock, schema: { name: 'Filter Block', + group: 'block', inputs: generateFromAJV(FilterProps as unknown as JSONSchemaType<{}>), default: { - title: 'Filter Block', + allTag: true, + description: + 'Three cards in a row on the desktop, two cards in a row on a tablet, one card in a row on a mobile phone.', + items: [ + { + card: { + content: { + title: 'Layout Item 1', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + type: 'layout-item', + }, + tags: ['one'], + }, + { + card: { + content: { + title: 'Layout Item 2', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + type: 'layout-item', + }, + tags: ['two'], + }, + { + card: { + content: { + title: 'Layout Item 3', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + type: 'layout-item', + }, + tags: ['three'], + }, + { + card: { + content: { + title: 'Layout Item 4', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + type: 'layout-item', + }, + tags: ['one'], + }, + { + card: { + content: { + title: 'Layout Item 5', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + type: 'layout-item', + }, + tags: ['two'], + }, + { + card: { + content: { + title: 'Layout Item 6', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + type: 'layout-item', + }, + tags: ['three'], + }, + ], + tags: [ + { + id: 'one', + label: 'First very long label', + }, + { + id: 'two', + label: 'Second very long label', + }, + { + id: 'three', + label: 'Third very long label', + }, + ], + title: 'Card Layout', + type: 'filter-block', }, }, }; diff --git a/src/blocks/Form/index.ts b/src/blocks/Form/index.ts index b80b5c3716..b28fc2c66a 100644 --- a/src/blocks/Form/index.ts +++ b/src/blocks/Form/index.ts @@ -9,6 +9,7 @@ const FormBlockConfig = { component: FormBlock, schema: { name: 'Form Block', + group: 'block', inputs: generateFromAJV(FormBlockSchema['form-block'] as unknown as JSONSchemaType<{}>), default: { title: 'Form Block', diff --git a/src/blocks/Header/index.ts b/src/blocks/Header/index.ts index 1f432122ed..7b9e77099f 100644 --- a/src/blocks/Header/index.ts +++ b/src/blocks/Header/index.ts @@ -9,7 +9,26 @@ const HeaderBlockConfig = { component: HeaderBlock, schema: { name: 'Header Block', + group: 'block', inputs: generateFromAJV(HeaderBlockSchema['header-block'] as unknown as JSONSchemaType<{}>), + default: { + type: 'header-block', + title: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit', + description: + 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.', + buttons: [ + { + text: 'Button\r', + theme: 'action', + url: 'https://example.com', + }, + { + text: 'Button', + theme: 'outlined', + url: 'https://example.com', + }, + ], + }, }, }; diff --git a/src/blocks/HeaderSlider/index.ts b/src/blocks/HeaderSlider/index.ts index 4828875431..6660668d5e 100644 --- a/src/blocks/HeaderSlider/index.ts +++ b/src/blocks/HeaderSlider/index.ts @@ -9,6 +9,7 @@ const HeaderSliderBlockConfig = { component: HeaderSliderBlock, schema: { name: 'Header Slider Block', + group: 'card-containers', inputs: generateFromAJV( HeaderSliderBlockSchema['header-slider-block'] as unknown as JSONSchemaType<{}>, ), diff --git a/src/blocks/Icons/Icons.scss b/src/blocks/Icons/Icons.scss index d098159d66..bd0cc7d3e4 100644 --- a/src/blocks/Icons/Icons.scss +++ b/src/blocks/Icons/Icons.scss @@ -48,6 +48,7 @@ $block: '.#{$ns}icons-block'; @include reset-link-style(); margin: 0 $indentXXXS $indentSM; } + a#{$block}__item { @include focusable(); border-radius: var(--g-focus-border-radius); diff --git a/src/blocks/Icons/Icons.tsx b/src/blocks/Icons/Icons.tsx index 93935febb8..c7338587e0 100644 --- a/src/blocks/Icons/Icons.tsx +++ b/src/blocks/Icons/Icons.tsx @@ -32,8 +32,8 @@ const Icons = ({title, description, size = 's', colSizes = {all: 12}, items}: Ic ); return ( - <div className={b({size})}> - <Grid> + <Grid> + <div className={b({size})}> {(title || description) && ( <Title className={b('header')} @@ -65,8 +65,8 @@ const Icons = ({title, description, size = 's', colSizes = {all: 12}, items}: Ic </div> ); })} - </Grid> - </div> + </div> + </Grid> ); }; diff --git a/src/blocks/Icons/index.ts b/src/blocks/Icons/index.ts index 21251e103e..6a1c394067 100644 --- a/src/blocks/Icons/index.ts +++ b/src/blocks/Icons/index.ts @@ -9,12 +9,56 @@ const IconsBlockConfig = { component: IconsBlock, schema: { name: 'Icons Block', + group: 'block', inputs: generateFromAJV(IconsProps as unknown as JSONSchemaType<{}>), default: { type: 'icons-block', title: 'Icons Block', description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + size: 'm', + items: [ + { + url: '/security/standards/software-registry', + text: 'Государственные реестры РФ', + src: 'https://storage.yandexcloud.net/cloud-www-assets/security-new/main-shield/security-software-registry.svg', + }, + { + url: '/security/standards/gdpr', + text: 'Общий регламент защиты данных (GDPR)', + src: 'https://storage.yandexcloud.net/cloud-www-assets/security-new/main-shield/security-gdpr.svg', + }, + { + url: '/security/standards/cloud-security-alliance', + text: 'Cloud Security Alliance', + src: 'https://storage.yandexcloud.net/cloud-www-assets/security-new/main-shield/security-csa.svg', + }, + { + url: '/security/standards/iso-standards', + text: 'Международная организация по стандартизации (ISO)', + src: 'https://storage.yandexcloud.net/cloud-www-assets/security-new/main-shield/security-iso.svg', + }, + { + url: '/security/standards/152-fz', + text: '№152-ФЗ «О персональных данных»', + src: 'https://storage.yandexcloud.net/cloud-www-assets/security-new/main-shield/security-152-fz.svg', + }, + { + url: '/security/standards/gost-p-57580', + text: 'ГОСТ Р 57580', + src: 'https://storage.yandexcloud.net/cloud-www-assets/security-new/main-shield/security-gost.svg', + }, + { + url: '/security/standards/pci', + text: 'Payment Card Industry Data Security Standard', + src: 'https://storage.yandexcloud.net/cloud-www-assets/security-new/main-shield/security-pci.svg', + }, + { + url: '/docs/security/standard/all', + text: 'Стандарт по защите облачной инфраструктуры', + src: 'https://storage.yandexcloud.net/cloud-www-assets/security-new/icons/security_yc.svg', + }, + ], }, }, }; diff --git a/src/blocks/Info/index.ts b/src/blocks/Info/index.ts index 1bd367f981..841e85d0a6 100644 --- a/src/blocks/Info/index.ts +++ b/src/blocks/Info/index.ts @@ -9,6 +9,7 @@ const InfoBlockConfig = { component: InfoBlock, schema: { name: 'Info Block', + group: 'block', inputs: generateFromAJV(InfoBlockSchema['info-block'] as unknown as JSONSchemaType<{}>), default: { type: 'info-block', diff --git a/src/blocks/Map/index.ts b/src/blocks/Map/index.ts index 88c48ce778..f34a5041c5 100644 --- a/src/blocks/Map/index.ts +++ b/src/blocks/Map/index.ts @@ -9,6 +9,7 @@ const MapBlockConfig = { component: MapBlock, schema: { name: 'Map Block', + group: 'block', inputs: generateFromAJV(MapBlockSchema['map-block'] as unknown as JSONSchemaType<{}>), default: { title: 'Map Block', diff --git a/src/blocks/Media/index.ts b/src/blocks/Media/index.ts index b3f113ad60..f9c0abb94f 100644 --- a/src/blocks/Media/index.ts +++ b/src/blocks/Media/index.ts @@ -9,13 +9,49 @@ const MediaBlockConfig = { component: MediaBlock, schema: { name: 'Media Block', + group: 'block', inputs: generateFromAJV(MediaBlockSchema['media-block'] as unknown as JSONSchemaType<{}>), default: { - type: 'media-block', - title: 'Media Block', - additionalInfo: 'Additional info', + title: 'Lorem ipsum dolor sit', description: - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.', + additionalInfo: + 'Duis aute irure dolor in reprehenderit n voluptate velit esse cillum dolore eu fugiat nulla pariatur.', + links: [ + { + url: '#', + text: 'Learn more', + theme: 'normal', + arrow: true, + }, + ], + buttons: [ + { + text: 'Button', + theme: 'action', + url: 'https://example.com', + }, + { + text: 'Button', + theme: 'outlined', + url: '#', + }, + ], + list: [ + { + title: 'Lorem ipsum', + text: 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + }, + { + text: 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + }, + { + title: 'Lorem ipsum ipsum', + }, + ], + media: { + image: 'https://storage.yandexcloud.net/cloud-www-assets/constructor/main/new/media-01-01.jpg', + }, }, }, }; diff --git a/src/blocks/PromoFeaturesBlock/index.ts b/src/blocks/PromoFeaturesBlock/index.ts index 2c3eac449a..07a01b7b22 100644 --- a/src/blocks/PromoFeaturesBlock/index.ts +++ b/src/blocks/PromoFeaturesBlock/index.ts @@ -9,12 +9,42 @@ const PromoFeaturesBlockConfig = { component: PromoFeaturesBlock, schema: { name: 'Promo Features Block', + group: 'block', inputs: generateFromAJV( PromoFeaturesBlockSchema['promo-features-block'] as unknown as JSONSchemaType<{}>, ), default: { title: 'Promo Features Block', - items: [{}], + theme: 'default', + items: [ + { + title: 'Lorem ipsum dolor sit amet', + text: 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + }, + { + title: 'Lorem ipsum dolor sit amet', + text: 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + }, + { + title: 'Lorem ipsum dolor sit amet', + text: 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + }, + { + title: 'Lorem ipsum dolor sit amet', + text: 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + theme: 'accent', + }, + { + title: 'Lorem ipsum dolor sit amet', + text: 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + theme: 'accent-light', + }, + { + title: 'Lorem ipsum dolor sit amet', + text: 'Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + theme: 'primary', + }, + ], }, }, }; diff --git a/src/blocks/Questions/index.ts b/src/blocks/Questions/index.ts index 43253bf7e5..8a8c30adcc 100644 --- a/src/blocks/Questions/index.ts +++ b/src/blocks/Questions/index.ts @@ -9,6 +9,7 @@ const QuestionsBlockConfig = { component: QuestionsBlock, schema: { name: 'Questions Block', + group: 'block', inputs: generateFromAJV( QuestionsBlockSchema['questions-block'] as unknown as JSONSchemaType<{}>, ), diff --git a/src/blocks/Share/index.ts b/src/blocks/Share/index.ts index e44d6b623b..4b27ce6897 100644 --- a/src/blocks/Share/index.ts +++ b/src/blocks/Share/index.ts @@ -9,6 +9,7 @@ const ShareBlockConfig = { component: ShareBlock, schema: { name: 'Share Block', + group: 'block', inputs: generateFromAJV(ShareBlockSchema['share-block'] as unknown as JSONSchemaType<{}>), default: { items: ['vk', 'telegram', 'facebook'], diff --git a/src/blocks/Slider/index.ts b/src/blocks/Slider/index.ts index 106ad3afe3..a809a55f7a 100644 --- a/src/blocks/Slider/index.ts +++ b/src/blocks/Slider/index.ts @@ -9,7 +9,38 @@ const SliderBlockConfig = { component: SliderBlock, schema: { name: 'Slider Block', + group: 'card-containers', inputs: generateFromAJV(SliderBlockSchema['slider-block'] as unknown as JSONSchemaType<{}>), + default: { + dots: true, + type: 'slider-block', + title: 'Slider Block with Quote Cards', + description: 'You can insert any card inside block', + slidesToShow: 1, + arrows: true, + children: [ + { + type: 'quote', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + url: 'https://example.com', + author: { + firstName: 'Lorem', + secondName: 'ipsum', + description: 'Lorem ipsum', + }, + }, + { + type: 'quote', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + url: 'https://example.com', + author: { + firstName: 'Lorem', + secondName: 'ipsum', + description: 'Lorem ipsum', + }, + }, + ], + }, }, }; diff --git a/src/blocks/Table/index.ts b/src/blocks/Table/index.ts index a7f5878114..8ccf96f3d7 100644 --- a/src/blocks/Table/index.ts +++ b/src/blocks/Table/index.ts @@ -9,7 +9,24 @@ const TableBlockConfig = { component: TableBlock, schema: { name: 'Table Block', + group: 'block', inputs: generateFromAJV(TableBlockSchema['table-block'] as unknown as JSONSchemaType<{}>), + default: { + type: 'table-block', + title: 'Lorem ipsum dolor sit amet', + table: { + content: [ + ['Lorem', 'ipsum 1', 'dolor 2', 'sit 3'], + ['Lorem 1', '0', '0', '0'], + ['Lorem 2', '0', '0', '1'], + ['Lorem 3', '0', '0', '1'], + ['Lorem 4', '0', '1', '1'], + ['Lorem 5', '1', '1', '1'], + ], + legend: ['ipsum 1', 'ipsum 2'], + justify: ['start', 'center', 'center', 'center'], + }, + }, }, }; diff --git a/src/blocks/Tabs/index.ts b/src/blocks/Tabs/index.ts index 4d7be63d1d..d0e63357db 100644 --- a/src/blocks/Tabs/index.ts +++ b/src/blocks/Tabs/index.ts @@ -9,6 +9,7 @@ const TabsBlockConfig = { component: TabsBlock, schema: { name: 'Tabs Block', + group: 'block', inputs: generateFromAJV(TabsBlockSchema['tabs-block'] as unknown as JSONSchemaType<{}>), default: { title: 'Tabs Block', diff --git a/src/common/types/forms.ts b/src/common/types/forms.ts index 8ade3e18a1..285bf89bb5 100644 --- a/src/common/types/forms.ts +++ b/src/common/types/forms.ts @@ -1,6 +1,7 @@ export interface BlockConfig { name: string; inputs: Array<ConfigInput>; + group?: string; default?: object; } diff --git a/src/constructor-items.ts b/src/constructor-items.ts index 61a086d81c..5a0b12fdc4 100644 --- a/src/constructor-items.ts +++ b/src/constructor-items.ts @@ -42,7 +42,7 @@ import ShareBlockConfig from './blocks/Share'; import SliderBlockConfig from './blocks/Slider'; import TableBlockConfig from './blocks/Table'; import TabsBlockConfig from './blocks/Tabs'; -import {ConfigInput} from './common/types'; +import {BlockConfig} from './common/types'; import {BlockType, NavigationItemType, SubBlockType} from './models'; import { GithubButton, @@ -70,7 +70,7 @@ import BasicCardConfig from './sub-blocks/BasicCard'; import ContentConfig from './sub-blocks/Content'; import DividerConfig from './sub-blocks/Divider'; import ImageCardConfig from './sub-blocks/ImageCard'; -import LayoutItemConfig from './sub-blocks/LayoutItem/form'; +import LayoutItemConfig from './sub-blocks/LayoutItem'; import MediaCardConfig from './sub-blocks/MediaCard'; import PriceCardConfig from './sub-blocks/PriceCard'; import PriceDetailedConfig from './sub-blocks/PriceDetailed'; @@ -132,11 +132,7 @@ export interface BlockData { // TODO: remove any // eslint-disable-next-line @typescript-eslint/no-explicit-any component: React.ComponentType<any>; - schema: { - name: string; - inputs: ConfigInput[]; - default?: object; - }; + schema: BlockConfig; } export const blockDataMap: Record<string, BlockData> = { diff --git a/src/editor-v2/components/BlocksList/BlocksList.scss b/src/editor-v2/components/BlocksList/BlocksList.scss index e2b9c301ed..e335c07ca0 100644 --- a/src/editor-v2/components/BlocksList/BlocksList.scss +++ b/src/editor-v2/components/BlocksList/BlocksList.scss @@ -14,22 +14,51 @@ $block: '.#{$ns}blocks-list'; &__card { padding: 8px; - cursor: pointer; + cursor: grab; user-select: none; margin-bottom: 8px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 4px; + &:last-child { margin-bottom: 0; } &:active { background-color: var(--g-color-base-generic-hover); + cursor: grabbing; } } + &__name { + flex: 1; + } + &__title { @include text-subheader-3; margin-bottom: 10px; + display: flex; + align-items: center; + justify-content: flex-start; + } + + &__subtitle { + @include text-subheader-2; + margin-bottom: 10px; + display: flex; + align-items: center; + justify-content: flex-start; + } + + &__group { + margin-bottom: 16px; + + &:last-child { + margin-bottom: 0; + } } &__section { diff --git a/src/editor-v2/components/BlocksList/BlocksList.tsx b/src/editor-v2/components/BlocksList/BlocksList.tsx index f2af642112..9f9f08d304 100644 --- a/src/editor-v2/components/BlocksList/BlocksList.tsx +++ b/src/editor-v2/components/BlocksList/BlocksList.tsx @@ -1,10 +1,13 @@ import React, {PropsWithChildren, useCallback} from 'react'; -import {Card} from '@gravity-ui/uikit'; +import {Grip, Plus} from '@gravity-ui/icons'; +import {Button, Card, Icon} from '@gravity-ui/uikit'; +import _ from 'lodash'; import {ActionTypes, ItemConfig} from '../../../common/types'; import {block} from '../../../utils'; import {useContentConfigStore} from '../../context/contentConfig'; +import {useEditorStore} from '../../context/editorContext'; import {useMessageSender} from '../../context/messagesContext'; import './BlocksList.scss'; @@ -15,8 +18,13 @@ export interface BlocksListProps { blocks: ItemConfig[]; } +interface BlockGroups { + [key: string]: ItemConfig[]; +} + const BlocksList = (_p: PropsWithChildren<BlocksListProps>) => { - const {blocks} = useContentConfigStore(); + const {blocks, insertBlock} = useContentConfigStore(); + const {selectedBlock} = useEditorStore(); const sendMessage = useMessageSender(); const onMouseDown = useCallback( @@ -26,14 +34,56 @@ const BlocksList = (_p: PropsWithChildren<BlocksListProps>) => { [sendMessage], ); + const onAddClick = useCallback( + (type: string) => { + const path = selectedBlock ? selectedBlock.path : [0]; + return insertBlock(path, type); + }, + [insertBlock, selectedBlock], + ); + + const groups = blocks.reduce<BlockGroups>((acc, currentBlock) => { + const group = currentBlock.schema.group; + if (group) { + if (!acc[group]) { + /* eslint-disable no-param-reassign */ + acc[group] = []; + } + acc[group].push(currentBlock); + } else { + if (!acc['other']) { + /* eslint-disable no-param-reassign */ + acc['other'] = []; + } + acc['other'].push(currentBlock); + } + + return acc; + }, {}); + return ( <div className={b()}> <div className={b('section')}> <div className={b('title')}>Blocks</div> - {blocks.map(({type, schema: {name}}) => ( - <Card key={type} className={b('card')} onMouseDown={() => onMouseDown(type)}> - {name} - </Card> + {Object.entries(groups).map(([key, groupBlocks]) => ( + <div className={b('group')} key={key}> + <div className={b('subtitle')}>{_.capitalize(key)}</div> + <div> + {groupBlocks.map(({type, schema: {name}}) => ( + <Card + key={type} + className={b('card')} + onMouseDown={() => onMouseDown(type)} + > + <Icon data={Grip} /> + <div className={b('name')}>{name}</div> + <Button onClick={() => onAddClick(type)}> + <Icon data={Plus} /> + </Button> + </Card> + ))} + </div> + </div> ))} </div> </div> diff --git a/src/editor-v2/components/Panels/Panels.scss b/src/editor-v2/components/Panels/Panels.scss new file mode 100644 index 0000000000..e22ef97642 --- /dev/null +++ b/src/editor-v2/components/Panels/Panels.scss @@ -0,0 +1,40 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}panels'; + +#{$block} { + &__button-wrap { + --pc-editor-panels-button-gap: 8px; + position: absolute; + z-index: 1000; + + &_left { + bottom: var(--pc-editor-panels-button-gap); + left: calc(100% + var(--pc-editor-panels-button-gap)); + } + + &_right { + bottom: var(--pc-editor-panels-button-gap); + right: calc(100% + var(--pc-editor-panels-button-gap)); + } + } + + &__draggable { + position: relative; + + width: 12px; + height: 100%; + cursor: col-resize; + background-color: var(--g-color-line-generic); + + display: flex; + align-items: center; + justify-content: center; + + color: var(--g-color-base-background); + } +} diff --git a/src/editor-v2/components/Panels/Panels.tsx b/src/editor-v2/components/Panels/Panels.tsx new file mode 100644 index 0000000000..8505f72097 --- /dev/null +++ b/src/editor-v2/components/Panels/Panels.tsx @@ -0,0 +1,79 @@ +import React, {ReactElement, useRef} from 'react'; + +import {ArrowLeftFromLine, ArrowRightFromLine, Grip} from '@gravity-ui/icons'; +import {Button, Icon} from '@gravity-ui/uikit'; +import {ImperativePanelHandle, Panel, PanelGroup, PanelResizeHandle} from 'react-resizable-panels'; + +import {block} from '../../../utils'; + +import './Panels.scss'; + +const b = block('panels'); + +interface PanelsProps { + left: ReactElement; + middle: ReactElement; + right: ReactElement; +} + +export const Panels = (props: PanelsProps) => { + const {left, right, middle} = props; + const leftPanel = useRef<ImperativePanelHandle>(null); + const rightPanel = useRef<ImperativePanelHandle>(null); + + const expandPanel = (reference: React.RefObject<ImperativePanelHandle>) => { + const panel = reference.current; + if (panel) { + panel.expand(); + } + }; + + const isCollapsed = { + left: leftPanel.current?.isCollapsed() || false, + right: rightPanel.current?.isCollapsed() || false, + }; + + return ( + <PanelGroup + className={b('panel')} + autoSaveId="page-constructor-editor" + direction="horizontal" + > + <Panel ref={leftPanel} collapsible defaultSize={25} minSize={15}> + {left} + </Panel> + <PanelResizeHandle className={b('draggable')}> + <Grip className={b('grip')} /> + {isCollapsed.left && ( + <div className={b('button-wrap', {left: true})}> + <Button + className={b('button')} + view="action" + onClick={() => expandPanel(leftPanel)} + > + <Icon data={ArrowRightFromLine} /> + </Button> + </div> + )} + </PanelResizeHandle> + <Panel minSize={20}>{middle}</Panel> + <PanelResizeHandle className={b('draggable')}> + <Grip className={b('grip')} /> + {isCollapsed.right && ( + <div className={b('button-wrap', {right: true})}> + <Button + className={b('button')} + view="action" + onClick={() => expandPanel(rightPanel)} + > + <Icon data={ArrowLeftFromLine} /> + </Button> + </div> + )} + </PanelResizeHandle> + <Panel ref={rightPanel} collapsible minSize={15} defaultSize={25}> + {right} + </Panel> + </PanelGroup> + ); +}; diff --git a/src/editor-v2/components/Tree/Tree.scss b/src/editor-v2/components/Tree/Tree.scss index 628987a448..1c197a67fe 100644 --- a/src/editor-v2/components/Tree/Tree.scss +++ b/src/editor-v2/components/Tree/Tree.scss @@ -9,6 +9,17 @@ $block: '.#{$ns}tree'; #{$block} { padding: 12px; + &__head { + margin-bottom: 10px; + display: flex; + align-items: center; + justify-content: space-between; + } + + &__title { + @include text-subheader-3; + } + &__item { padding: 8px; margin-bottom: 8px; diff --git a/src/editor-v2/components/Tree/Tree.tsx b/src/editor-v2/components/Tree/Tree.tsx index e3c2dc5fe2..68a38be011 100644 --- a/src/editor-v2/components/Tree/Tree.tsx +++ b/src/editor-v2/components/Tree/Tree.tsx @@ -1,6 +1,7 @@ import React, {PropsWithChildren} from 'react'; -import {Card} from '@gravity-ui/uikit'; +import {TrashBin} from '@gravity-ui/icons'; +import {Button, Card, Icon} from '@gravity-ui/uikit'; import {ItemConfig} from '../../../common/types'; import {block} from '../../../utils'; @@ -35,7 +36,7 @@ const generateTree = (items: TreeItem[]): TreeItem[] => { }; const Tree = (_p: PropsWithChildren<TreeProps>) => { - const {config} = useContentConfigStore(); + const {config, resetBlocks} = useContentConfigStore(); const blockTree = generateTree(config.blocks); @@ -48,7 +49,21 @@ const Tree = (_p: PropsWithChildren<TreeProps>) => { )); }; - return <div className={b()}>{renderTree(blockTree)}</div>; + return ( + <div className={b()}> + <div className={b('head')}> + <div className={b('title')}>Tree</div> + <div className={b('actions')}> + <Button view="outlined-danger" onClick={() => resetBlocks()}> + <Icon data={TrashBin} /> + Clear all + </Button> + </div> + </div> + + <div className={b('cards')}>{renderTree(blockTree)}</div> + </div> + ); }; export default Tree; diff --git a/src/editor-v2/containers/Editor/Editor.scss b/src/editor-v2/containers/Editor/Editor.scss index b6dea3b886..18030f4fc5 100644 --- a/src/editor-v2/containers/Editor/Editor.scss +++ b/src/editor-v2/containers/Editor/Editor.scss @@ -35,19 +35,6 @@ $block: '.#{$ns}editor'; flex: 1; } - &__draggable { - width: 12px; - height: 100%; - cursor: col-resize; - background-color: var(--g-color-line-generic); - - display: flex; - align-items: center; - justify-content: center; - - color: var(--g-color-base-background); - } - &__overlay { position: absolute; height: 100%; diff --git a/src/editor-v2/containers/Editor/Editor.tsx b/src/editor-v2/containers/Editor/Editor.tsx index d0fb5b641a..406d758842 100644 --- a/src/editor-v2/containers/Editor/Editor.tsx +++ b/src/editor-v2/containers/Editor/Editor.tsx @@ -1,13 +1,11 @@ import React, {useCallback} from 'react'; -import {Grip} from '@gravity-ui/icons'; -import {Panel, PanelGroup, PanelResizeHandle} from 'react-resizable-panels'; - import {ActionTypes} from '../../../common/types'; import {PageContent} from '../../../models'; import {block} from '../../../utils'; import BigOverlay from '../../components/BigOverlay/BigOverlay'; import MiddleScreen from '../../components/MiddleScreen/MiddleScreen'; +import {Panels} from '../../components/Panels/Panels'; import {Sidebar} from '../../components/Sidebar/Sidebar'; import {ContentConfigProvider} from '../../context/contentConfig'; import {EditorProvider, useEditorStore} from '../../context/editorContext'; @@ -65,27 +63,11 @@ const EditorView = (_props: EditorViewProps) => { return ( <div className={b()} onMouseUp={onMouseUp} onMouseMove={onMouseMove}> <div className={b('body')}> - <PanelGroup - className={b('panel')} - autoSaveId="page-constructor-editor" - direction="horizontal" - > - <Panel collapsible defaultSize={25} minSize={15}> - <Sidebar position={'left'} /> - </Panel> - <PanelResizeHandle className={b('draggable')}> - <Grip className={b('grip')} /> - </PanelResizeHandle> - <Panel minSize={20}> - <MiddleScreen /> - </Panel> - <PanelResizeHandle className={b('draggable')}> - <Grip className={b('grip')} /> - </PanelResizeHandle> - <Panel collapsible minSize={15} defaultSize={25}> - <Sidebar position={'right'} startMenu="block-config" /> - </Panel> - </PanelGroup> + <Panels + left={<Sidebar position={'left'} />} + right={<Sidebar position={'right'} startMenu="block-config" />} + middle={<MiddleScreen />} + /> </div> <BigOverlay className={b('overlay')} /> </div> diff --git a/src/editor-v2/context/contentConfig/store.ts b/src/editor-v2/context/contentConfig/store.ts index 819362418d..8443cbcb2e 100644 --- a/src/editor-v2/context/contentConfig/store.ts +++ b/src/editor-v2/context/contentConfig/store.ts @@ -34,6 +34,7 @@ export interface ContentConfigMethods extends WithStoreReducer { duplicateBlock: (path: number[]) => void; reorderBlock: (path: number[], destination: number[]) => void; updateField: (path: string, value: DynamicFormValue) => void; + resetBlocks: () => void; } export type ContentConfigStore = ContentConfigState & ContentConfigMethods; @@ -138,6 +139,12 @@ export const createContentConfigStore = initializeStore<ContentConfigState, Cont config: {...state.config, blocks: newBlocksConfig}, })); }, + resetBlocks: () => { + set((state) => ({ + ...state, + config: {...state.config, blocks: []}, + })); + }, reducer: function (action) { switch (action.type) { // Insert Actions diff --git a/src/sub-blocks/BackgroundCard/index.tsx b/src/sub-blocks/BackgroundCard/index.tsx index 17b5255d5f..abdfa7f0c4 100644 --- a/src/sub-blocks/BackgroundCard/index.tsx +++ b/src/sub-blocks/BackgroundCard/index.tsx @@ -13,10 +13,12 @@ const BackgroundCardConfig: BlockData = { inputs: generateFromAJV( BackgroundCardSchema['background-card'] as unknown as JSONSchemaType<{}>, ), + group: 'cards', default: { title: 'Background Card', text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', additionalInfo: 'Additional info', + backgroundColor: '#F0F0F0', }, }, }; diff --git a/src/sub-blocks/BannerCard/index.tsx b/src/sub-blocks/BannerCard/index.tsx index 8976cea40a..e311d3e5e8 100644 --- a/src/sub-blocks/BannerCard/index.tsx +++ b/src/sub-blocks/BannerCard/index.tsx @@ -10,6 +10,7 @@ const BannerCardConfig: BlockData = { component: BannerCard, schema: { name: 'Banner Card', + group: 'cards', inputs: generateFromAJV(BannerCardProps as unknown as JSONSchemaType<{}>), default: { color: 'rgba(54, 151, 241, 0.4)', diff --git a/src/sub-blocks/BasicCard/index.tsx b/src/sub-blocks/BasicCard/index.tsx index 23a4a85cf9..546df5946e 100644 --- a/src/sub-blocks/BasicCard/index.tsx +++ b/src/sub-blocks/BasicCard/index.tsx @@ -10,6 +10,7 @@ const BasicCardConfig: BlockData = { component: BasicCard, schema: { name: 'Basic Card', + group: 'cards', inputs: generateFromAJV(BasicCardSchema['basic-card'] as unknown as JSONSchemaType<{}>), default: { title: 'Basic Card', diff --git a/src/sub-blocks/Content/index.tsx b/src/sub-blocks/Content/index.tsx index 55eedd9c75..48a055979a 100644 --- a/src/sub-blocks/Content/index.tsx +++ b/src/sub-blocks/Content/index.tsx @@ -10,6 +10,7 @@ const ContentConfig: BlockData = { component: Content, schema: { name: 'Content', + group: 'cards', inputs: generateFromAJV(ContentBlock['content'] as unknown as JSONSchemaType<{}>), default: { title: 'Content', diff --git a/src/sub-blocks/ImageCard/index.tsx b/src/sub-blocks/ImageCard/index.tsx index 121e1a304c..a4d09b1b8d 100644 --- a/src/sub-blocks/ImageCard/index.tsx +++ b/src/sub-blocks/ImageCard/index.tsx @@ -10,6 +10,7 @@ const ImageCardConfig: BlockData = { component: ImageCard, schema: { name: 'Image Card', + group: 'cards', inputs: generateFromAJV(ImageCardSchema['image-card'] as unknown as JSONSchemaType<{}>), default: { title: 'Image Card', diff --git a/src/sub-blocks/LayoutItem/form.ts b/src/sub-blocks/LayoutItem/form.ts deleted file mode 100644 index 301899c2a5..0000000000 --- a/src/sub-blocks/LayoutItem/form.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {JSONSchemaType} from 'ajv'; - -import {generateFromAJV} from '../../utils/form-generator'; - -import LayoutItem from './LayoutItem'; -import {LayoutItem as LayoutItemSchema} from './schema'; - -const LayoutItemConfig = { - component: LayoutItem, - schema: { - name: 'Layout Item', - inputs: generateFromAJV(LayoutItemSchema as unknown as JSONSchemaType<{}>), - }, -}; - -export default LayoutItemConfig; diff --git a/src/sub-blocks/LayoutItem/index.tsx b/src/sub-blocks/LayoutItem/index.tsx index 7113f14992..e55f88d11f 100644 --- a/src/sub-blocks/LayoutItem/index.tsx +++ b/src/sub-blocks/LayoutItem/index.tsx @@ -10,11 +10,16 @@ const LayoutItemConfig: BlockData = { component: LayoutItem, schema: { name: 'Layout Item', + group: 'cards', inputs: generateFromAJV(LayoutItemSchema as unknown as JSONSchemaType<{}>), default: { + type: 'layout-item', content: { - title: 'Layout Item', - text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + title: 'Lorem ipsum', + text: 'Dolor sit amet', + }, + media: { + image: 'https://storage.yandexcloud.net/yc-www-community-images/event_ecaf1ef1-bc3a-40fa-adef-827b0959e6c3.jpg', }, }, }, diff --git a/src/sub-blocks/MediaCard/index.tsx b/src/sub-blocks/MediaCard/index.tsx index 22f6c9b7ed..e13c9bad13 100644 --- a/src/sub-blocks/MediaCard/index.tsx +++ b/src/sub-blocks/MediaCard/index.tsx @@ -10,12 +10,14 @@ const MediaCardConfig: BlockData = { component: MediaCard, schema: { name: 'Media Card', + group: 'cards', inputs: generateFromAJV(MediaCardSchema['media-card'] as unknown as JSONSchemaType<{}>), default: { content: { title: 'Media Card', text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', }, + image: 'https://storage.yandexcloud.net/yc-www-community-images/event_ecaf1ef1-bc3a-40fa-adef-827b0959e6c3.jpg', }, }, }; diff --git a/src/sub-blocks/PriceCard/index.tsx b/src/sub-blocks/PriceCard/index.tsx index 604934389d..e23bf5cdfc 100644 --- a/src/sub-blocks/PriceCard/index.tsx +++ b/src/sub-blocks/PriceCard/index.tsx @@ -10,6 +10,7 @@ const PriceCardConfig: BlockData = { component: PriceCard, schema: { name: 'Price Card', + group: 'cards', inputs: generateFromAJV(PriceCardSchema['price-card'] as unknown as JSONSchemaType<{}>), default: { type: 'price-card', diff --git a/src/sub-blocks/PriceDetailed/index.tsx b/src/sub-blocks/PriceDetailed/index.tsx index f031b87d9f..5907f86551 100644 --- a/src/sub-blocks/PriceDetailed/index.tsx +++ b/src/sub-blocks/PriceDetailed/index.tsx @@ -11,6 +11,7 @@ const PriceDetailedConfig: BlockData = { component: PriceDetailed, schema: { name: 'Price Detailed', + group: 'cards', inputs: generateFromAJV( PriceDetailedSchema['price-detailed'] as unknown as JSONSchemaType<{}>, ), diff --git a/src/sub-blocks/Quote/index.tsx b/src/sub-blocks/Quote/index.tsx index 47f45c716b..756458ff3e 100644 --- a/src/sub-blocks/Quote/index.tsx +++ b/src/sub-blocks/Quote/index.tsx @@ -10,6 +10,7 @@ const QuoteConfig: BlockData = { component: Quote, schema: { name: 'Quote', + group: 'cards', inputs: generateFromAJV(QuoteSchema['quote'] as unknown as JSONSchemaType<{}>), default: { text: 'A good decision is based on knowledge and not on numbers.', From 9d2f5d505317aa11265f89a755fccb349c29a836 Mon Sep 17 00:00:00 2001 From: daff <daff@yandex-team.ru> Date: Mon, 3 Feb 2025 16:07:17 +0300 Subject: [PATCH 005/118] feat: add PostMessage API and Synchronized store --- package-lock.json | 10966 +++++++--------- package.json | 2 + playground/src/app/page.tsx | 8 +- src/common/hooks/usePostMessage.tsx | 134 - src/common/postMessage.ts | 42 + src/common/store.ts | 263 + src/common/types/actions.ts | 22 + src/common/types/actions/codes.ts | 27 - src/common/types/actions/index.ts | 31 - src/common/types/actions/initial.ts | 29 - src/common/types/actions/insert.ts | 24 - src/common/types/actions/other.ts | 21 - src/common/types/actions/overlay.ts | 16 - src/common/types/actions/reorder.ts | 27 - src/common/types/actions/select.ts | 20 - src/common/types/common.ts | 11 - src/common/types/forms.ts | 4 + src/common/types/messages.ts | 28 +- .../editor/ChildrenWrap/ChildrenWrap.tsx | 12 +- src/components/editor/ItemWrap/ItemWrap.tsx | 12 +- .../PageConstructor/PageConstructor.tsx | 10 +- src/containers/PageConstructor/Provider.tsx | 6 +- .../ConstructorBlock/ConstructorBlock.tsx | 14 +- .../hooks/useEditorBlockMouseEvents.tsx | 141 +- src/context/editorContext/editorContext.ts | 18 - src/context/editorContext/editorProvider.tsx | 27 - .../editorContext/hooks/useEditorStore.ts | 17 - src/context/editorContext/index.ts | 4 - src/context/editorContext/store.ts | 68 - .../PCEditorStoreContext.tsx | 13 + .../PCEditorStoreProvider.tsx | 47 + .../hooks/usePCEditorStore.ts | 10 + src/context/editorStoreContext/index.ts | 3 + .../hooks/useMessageObserver.tsx | 23 - .../hooks/useMessageSender.tsx | 10 - .../hooks/useMessagesStore.tsx | 17 - src/context/messagesContext/index.ts | 5 - .../messagesContext/messagesContext.tsx | 21 - .../messagesContext/messagesProvider.tsx | 38 - src/context/messagesContext/store.ts | 44 - .../components/BigOverlay/BigOverlay.scss | 1 + .../components/BigOverlay/BigOverlay.tsx | 85 +- .../components/BlockConfig/BlockConfig.tsx | 15 +- .../components/BlocksList/BlocksList.tsx | 16 +- .../components/DynamicForm/DynamicForm.tsx | 4 +- .../DynamicForm/Fields/Array/Array.tsx | 4 +- .../DynamicForm/Fields/Object/Object.tsx | 4 +- .../DynamicForm/Fields/OneOf/OneOf.tsx | 4 +- .../components/GlobalConfig/GlobalConfig.tsx | 9 +- .../components/MiddleScreen/MiddleScreen.tsx | 34 +- src/editor-v2/components/Overlay/Overlay.scss | 7 +- src/editor-v2/components/Overlay/Overlay.tsx | 86 +- src/editor-v2/components/Source/Source.tsx | 9 +- .../components/SourceCode/SourceCode.tsx | 8 +- .../components/StoreViewer/StoreViewer.scss | 39 + .../components/StoreViewer/StoreViewer.tsx | 40 + src/editor-v2/components/Tree/Tree.tsx | 6 +- src/editor-v2/containers/Editor/Editor.scss | 7 + src/editor-v2/containers/Editor/Editor.tsx | 51 +- .../Editor/hooks/useAdminInitialize.tsx | 79 +- .../containers/__stories__/Editor.stories.tsx | 9 +- .../containers/__stories__/data.json | 335 - .../contentConfig/contentConfigContext.tsx | 15 - .../contentConfig/contentConfigProvider.tsx | 34 - .../hooks/useContentConfigStore.tsx | 15 - src/editor-v2/context/contentConfig/index.ts | 4 - src/editor-v2/context/contentConfig/store.ts | 209 - .../context/editorContext/editorContext.tsx | 16 - .../context/editorContext/editorProvider.tsx | 20 - .../editorContext/hooks/useEditorStore.tsx | 17 - src/editor-v2/context/editorContext/index.ts | 4 - src/editor-v2/context/editorContext/store.ts | 101 - .../editorStore/MainEditorStoreContext.tsx | 13 + .../editorStore/MainEditorStoreProvider.tsx | 53 + .../editorStore/hooks/useMainEditorStore.ts | 10 + src/editor-v2/context/editorStore/index.ts | 3 + .../iframeContext/hooks/useIframeStore.tsx | 15 - .../context/iframeContext/iframeContext.tsx | 13 +- .../context/iframeContext/iframeProvider.tsx | 16 +- src/editor-v2/context/iframeContext/index.ts | 2 - src/editor-v2/context/iframeContext/store.ts | 68 - .../hooks/useMessageObserver.tsx | 23 - .../hooks/useMessageSender.tsx | 10 - .../hooks/useMessagesStore.tsx | 17 - .../context/messagesContext/index.ts | 5 - .../messagesContext/messagesContext.tsx | 21 - .../messagesContext/messagesProvider.tsx | 46 - .../context/messagesContext/store.ts | 44 - src/editor-v2/hooks/usePostMessageEvents.ts | 26 + src/hooks/useEditorInitialize.ts | 124 +- src/hooks/usePostMessageAPI.ts | 42 + src/index.ts | 1 + src/utils/store.ts | 29 +- 93 files changed, 5917 insertions(+), 8186 deletions(-) delete mode 100644 src/common/hooks/usePostMessage.tsx create mode 100644 src/common/postMessage.ts create mode 100644 src/common/store.ts create mode 100644 src/common/types/actions.ts delete mode 100644 src/common/types/actions/codes.ts delete mode 100644 src/common/types/actions/index.ts delete mode 100644 src/common/types/actions/initial.ts delete mode 100644 src/common/types/actions/insert.ts delete mode 100644 src/common/types/actions/other.ts delete mode 100644 src/common/types/actions/overlay.ts delete mode 100644 src/common/types/actions/reorder.ts delete mode 100644 src/common/types/actions/select.ts delete mode 100644 src/context/editorContext/editorContext.ts delete mode 100644 src/context/editorContext/editorProvider.tsx delete mode 100644 src/context/editorContext/hooks/useEditorStore.ts delete mode 100644 src/context/editorContext/index.ts delete mode 100644 src/context/editorContext/store.ts create mode 100644 src/context/editorStoreContext/PCEditorStoreContext.tsx create mode 100644 src/context/editorStoreContext/PCEditorStoreProvider.tsx create mode 100644 src/context/editorStoreContext/hooks/usePCEditorStore.ts create mode 100644 src/context/editorStoreContext/index.ts delete mode 100644 src/context/messagesContext/hooks/useMessageObserver.tsx delete mode 100644 src/context/messagesContext/hooks/useMessageSender.tsx delete mode 100644 src/context/messagesContext/hooks/useMessagesStore.tsx delete mode 100644 src/context/messagesContext/index.ts delete mode 100644 src/context/messagesContext/messagesContext.tsx delete mode 100644 src/context/messagesContext/messagesProvider.tsx delete mode 100644 src/context/messagesContext/store.ts create mode 100644 src/editor-v2/components/StoreViewer/StoreViewer.scss create mode 100644 src/editor-v2/components/StoreViewer/StoreViewer.tsx delete mode 100644 src/editor-v2/containers/__stories__/data.json delete mode 100644 src/editor-v2/context/contentConfig/contentConfigContext.tsx delete mode 100644 src/editor-v2/context/contentConfig/contentConfigProvider.tsx delete mode 100644 src/editor-v2/context/contentConfig/hooks/useContentConfigStore.tsx delete mode 100644 src/editor-v2/context/contentConfig/index.ts delete mode 100644 src/editor-v2/context/contentConfig/store.ts delete mode 100644 src/editor-v2/context/editorContext/editorContext.tsx delete mode 100644 src/editor-v2/context/editorContext/editorProvider.tsx delete mode 100644 src/editor-v2/context/editorContext/hooks/useEditorStore.tsx delete mode 100644 src/editor-v2/context/editorContext/index.ts delete mode 100644 src/editor-v2/context/editorContext/store.ts create mode 100644 src/editor-v2/context/editorStore/MainEditorStoreContext.tsx create mode 100644 src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx create mode 100644 src/editor-v2/context/editorStore/hooks/useMainEditorStore.ts create mode 100644 src/editor-v2/context/editorStore/index.ts delete mode 100644 src/editor-v2/context/iframeContext/hooks/useIframeStore.tsx delete mode 100644 src/editor-v2/context/iframeContext/store.ts delete mode 100644 src/editor-v2/context/messagesContext/hooks/useMessageObserver.tsx delete mode 100644 src/editor-v2/context/messagesContext/hooks/useMessageSender.tsx delete mode 100644 src/editor-v2/context/messagesContext/hooks/useMessagesStore.tsx delete mode 100644 src/editor-v2/context/messagesContext/index.ts delete mode 100644 src/editor-v2/context/messagesContext/messagesContext.tsx delete mode 100644 src/editor-v2/context/messagesContext/messagesProvider.tsx delete mode 100644 src/editor-v2/context/messagesContext/store.ts create mode 100644 src/editor-v2/hooks/usePostMessageEvents.ts create mode 100644 src/hooks/usePostMessageAPI.ts diff --git a/package-lock.json b/package-lock.json index 856ca931ee..ea4b015a74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@react-spring/web": "^9.7.3", "ajv": "^8.12.0", "ajv-keywords": "^5.1.0", + "deep-object-diff": "^1.1.9", "final-form": "^4.20.9", "github-buttons": "2.23.0", "immutable": "^4.3.7", @@ -23,6 +24,7 @@ "lodash": "^4.17.21", "monaco-editor": "^0.52.2", "react-final-form": "^6.5.9", + "react-json-view": "^1.21.3", "react-monaco-editor": "^0.53.0", "react-player": "^2.9.0", "react-resizable-panels": "^2.1.3", @@ -3594,40 +3596,6 @@ "node": ">=14.17.0" } }, - "node_modules/@emnapi/core": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", - "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz", - "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emotion/babel-plugin": { "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", @@ -3770,589 +3738,189 @@ "dev": true, "license": "MIT" }, - "node_modules/@esbuild/aix-ppc64": { + "node_modules/@esbuild/darwin-arm64": { "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", - "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", + "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", "cpu": [ - "ppc64" + "arm64" ], "dev": true, "optional": true, "os": [ - "aix" + "darwin" ], "engines": { "node": ">=18" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz", - "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", - "cpu": [ - "arm" - ], + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": ">=18" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", - "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz", - "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", - "cpu": [ - "x64" - ], + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "license": "MIT", "engines": { - "node": ">=18" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", - "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=18" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", - "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", - "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", - "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": ">=18" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", - "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", - "cpu": [ - "arm" - ], + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", "engines": { - "node": ">=18" + "node": ">= 4" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", - "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } + "license": "MIT" }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", - "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", - "cpu": [ - "ia32" - ], + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=18" + "node": "*" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", - "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", - "cpu": [ - "loong64" - ], + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=18" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", - "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", - "cpu": [ - "mips64el" - ], + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", "engines": { - "node": ">=18" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", - "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", - "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", - "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", - "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", - "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", - "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", - "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", - "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", - "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", - "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", - "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", - "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", - "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz", - "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==", + "node_modules/@floating-ui/core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz", + "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==", "dev": true, "license": "MIT", "dependencies": { @@ -5679,185 +5247,44 @@ "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@mdx-js/react": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", - "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mdx": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=16", - "react": ">=16" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.10.tgz", - "integrity": "sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.9.0" - } - }, - "node_modules/@next/env": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.24.tgz", - "integrity": "sha512-LAm0Is2KHTNT6IT16lxT+suD0u+VVfYNQqM+EJTKuFRRuY2z+zj01kueWXPCxbMBDt0B5vONYzabHGUNbZYAhA==", - "dev": true - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.24.tgz", - "integrity": "sha512-7Tdi13aojnAZGpapVU6meVSpNzgrFwZ8joDcNS8cJVNuP3zqqrLqeory9Xec5TJZR/stsGJdfwo8KeyloT3+rQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.24.tgz", - "integrity": "sha512-lXR2WQqUtu69l5JMdTwSvQUkdqAhEWOqJEYUQ21QczQsAlNOW2kWZCucA6b3EXmPbcvmHB1kSZDua/713d52xg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.24.tgz", - "integrity": "sha512-nxvJgWOpSNmzidYvvGDfXwxkijb6hL9+cjZx1PVG6urr2h2jUqBALkKjT7kpfurRWicK6hFOvarmaWsINT1hnA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.24.tgz", - "integrity": "sha512-PaBgOPhqa4Abxa3y/P92F3kklNPsiFjcjldQGT7kFmiY5nuFn8ClBEoX8GIpqU1ODP2y8P6hio6vTomx2Vy0UQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.24.tgz", - "integrity": "sha512-vEbyadiRI7GOr94hd2AB15LFVgcJZQWu7Cdi9cWjCMeCiUsHWA0U5BkGPuoYRnTxTn0HacuMb9NeAmStfBCLoQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.24.tgz", - "integrity": "sha512-df0FC9ptaYsd8nQCINCzFtDWtko8PNRTAU0/+d7hy47E0oC17tI54U/0NdGk7l/76jz1J377dvRjmt6IUdkpzQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.24.tgz", - "integrity": "sha512-ZEntbLjeYAJ286eAqbxpZHhDFYpYjArotQ+/TW9j7UROh0DUmX7wYDGtsTPpfCV8V+UoqHBPU7q9D4nDNH014Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.24.tgz", - "integrity": "sha512-9KuS+XUXM3T6v7leeWU0erpJ6NsFIwiTFD5nzNg8J5uo/DMIPvCp3L1Ao5HjbHX0gkWPB1VrKoo/Il4F0cGK2Q==", - "cpu": [ - "ia32" - ], + "node_modules/@mdx-js/react": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", + "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" + "license": "MIT", + "dependencies": { + "@types/mdx": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" } }, - "node_modules/@next/swc-win32-x64-msvc": { + "node_modules/@next/env": { + "version": "14.2.24", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.24.tgz", + "integrity": "sha512-LAm0Is2KHTNT6IT16lxT+suD0u+VVfYNQqM+EJTKuFRRuY2z+zj01kueWXPCxbMBDt0B5vONYzabHGUNbZYAhA==", + "dev": true + }, + "node_modules/@next/swc-darwin-arm64": { "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.24.tgz", - "integrity": "sha512-cXcJ2+x0fXQ2CntaE00d7uUH+u1Bfp/E0HsNQH79YiLaZE5Rbm7dZzyAYccn3uICM7mw+DxoMqEfGXZtF4Fgaw==", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.24.tgz", + "integrity": "sha512-7Tdi13aojnAZGpapVU6meVSpNzgrFwZ8joDcNS8cJVNuP3zqqrLqeory9Xec5TJZR/stsGJdfwo8KeyloT3+rQ==", "cpu": [ - "x64" + "arm64" ], "dev": true, "optional": true, "os": [ - "win32" + "darwin" ], "engines": { "node": ">= 10" @@ -6018,356 +5445,90 @@ "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.5.tgz", "integrity": "sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw==", "license": "MIT" - }, - "node_modules/@react-spring/shared": { - "version": "9.7.5", - "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.5.tgz", - "integrity": "sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw==", - "license": "MIT", - "dependencies": { - "@react-spring/rafz": "~9.7.5", - "@react-spring/types": "~9.7.5" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@react-spring/types": { - "version": "9.7.5", - "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.5.tgz", - "integrity": "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==", - "license": "MIT" - }, - "node_modules/@react-spring/web": { - "version": "9.7.5", - "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.5.tgz", - "integrity": "sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ==", - "license": "MIT", - "dependencies": { - "@react-spring/animated": "~9.7.5", - "@react-spring/core": "~9.7.5", - "@react-spring/shared": "~9.7.5", - "@react-spring/types": "~9.7.5" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", - "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.0.tgz", - "integrity": "sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.0.tgz", - "integrity": "sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.0.tgz", - "integrity": "sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.0.tgz", - "integrity": "sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.0.tgz", - "integrity": "sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.0.tgz", - "integrity": "sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.0.tgz", - "integrity": "sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.0.tgz", - "integrity": "sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.0.tgz", - "integrity": "sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.0.tgz", - "integrity": "sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.0.tgz", - "integrity": "sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.0.tgz", - "integrity": "sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.0.tgz", - "integrity": "sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.0.tgz", - "integrity": "sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.0.tgz", - "integrity": "sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.0.tgz", - "integrity": "sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==", - "cpu": [ - "x64" - ], - "dev": true, + }, + "node_modules/@react-spring/shared": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.5.tgz", + "integrity": "sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@react-spring/rafz": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.0.tgz", - "integrity": "sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@react-spring/types": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.5.tgz", + "integrity": "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==", + "license": "MIT" + }, + "node_modules/@react-spring/web": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.5.tgz", + "integrity": "sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@react-spring/animated": "~9.7.5", + "@react-spring/core": "~9.7.5", + "@react-spring/shared": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.0.tgz", - "integrity": "sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==", - "cpu": [ - "arm64" - ], + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.0.tgz", - "integrity": "sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==", - "cpu": [ - "ia32" - ], + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { + "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.0.tgz", - "integrity": "sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.0.tgz", + "integrity": "sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" + "darwin" ] }, "node_modules/@rtsao/scc": { @@ -7799,17 +6960,6 @@ "node": ">=10.13.0" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", - "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -8502,248 +7652,21 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC" - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.7.2.tgz", - "integrity": "sha512-vxtBno4xvowwNmO/ASL0Y45TpHqmNkAaDtz4Jqb+clmcVSSl8XCG/PNFFkGsXXXS6AMjP+ja/TtNCFFa1QwLRg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.7.2.tgz", - "integrity": "sha512-qhVa8ozu92C23Hsmv0BF4+5Dyyd5STT1FolV4whNgbY6mj3kA0qsrGPe35zNR3wAN7eFict3s4Rc2dDTPBTuFQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.7.2.tgz", - "integrity": "sha512-zKKdm2uMXqLFX6Ac7K5ElnnG5VIXbDlFWzg4WJ8CGUedJryM5A3cTgHuGMw1+P5ziV8CRhnSEgOnurTI4vpHpg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.7.2.tgz", - "integrity": "sha512-8N1z1TbPnHH+iDS/42GJ0bMPLiGK+cUqOhNbMKtWJ4oFGzqSJk/zoXFzcQkgtI63qMcUI7wW1tq2usZQSb2jxw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.7.2.tgz", - "integrity": "sha512-tjYzI9LcAXR9MYd9rO45m1s0B/6bJNuZ6jeOxo1pq1K6OBuRMMmfyvJYval3s9FPPGmrldYA3mi4gWDlWuTFGA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.7.2.tgz", - "integrity": "sha512-jon9M7DKRLGZ9VYSkFMflvNqu9hDtOCEnO2QAryFWgT6o6AXU8du56V7YqnaLKr6rAbZBWYsYpikF226v423QA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.7.2.tgz", - "integrity": "sha512-c8Cg4/h+kQ63pL43wBNaVMmOjXI/X62wQmru51qjfTvI7kmCy5uHTJvK/9LrF0G8Jdx8r34d019P1DVJmhXQpA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.7.2.tgz", - "integrity": "sha512-A+lcwRFyrjeJmv3JJvhz5NbcCkLQL6Mk16kHTNm6/aGNc4FwPHPE4DR9DwuCvCnVHvF5IAd9U4VIs/VvVir5lg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.7.2.tgz", - "integrity": "sha512-hQQ4TJQrSQW8JlPm7tRpXN8OCNP9ez7PajJNjRD1ZTHQAy685OYqPrKjfaMw/8LiHCt8AZ74rfUVHP9vn0N69Q==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.7.2.tgz", - "integrity": "sha512-NoAGbiqrxtY8kVooZ24i70CjLDlUFI7nDj3I9y54U94p+3kPxwd2L692YsdLa+cqQ0VoqMWoehDFp21PKRUoIQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.7.2.tgz", - "integrity": "sha512-KaZByo8xuQZbUhhreBTW+yUnOIHUsv04P8lKjQ5otiGoSJ17ISGYArc+4vKdLEpGaLbemGzr4ZeUbYQQsLWFjA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.7.2.tgz", - "integrity": "sha512-dEidzJDubxxhUCBJ/SHSMJD/9q7JkyfBMT77Px1npl4xpg9t0POLvnWywSk66BgZS/b2Hy9Y1yFaoMTFJUe9yg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.7.2.tgz", - "integrity": "sha512-RvP+Ux3wDjmnZDT4XWFfNBRVG0fMsc+yVzNFUqOflnDfZ9OYujv6nkh+GOr+watwrW4wdp6ASfG/e7bkDradsw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.7.2.tgz", - "integrity": "sha512-y797JBmO9IsvXVRCKDXOxjyAE4+CcZpla2GSoBQ33TVb3ILXuFnMrbR/QQZoauBYeOFuu4w3ifWLw52sdHGz6g==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.9" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.7.2.tgz", - "integrity": "sha512-gtYTh4/VREVSLA+gHrfbWxaMO/00y+34htY7XpioBTy56YN2eBjkPrY1ML1Zys89X3RJDKVaogzwxlM1qU7egg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.7.2.tgz", - "integrity": "sha512-Ywv20XHvHTDRQs12jd3MY8X5C8KLjDbg/jyaal/QLKx3fAShhJyD4blEANInsjxW3P7isHx1Blt56iUDDJO3jg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dev": true, + "license": "ISC" }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "node_modules/@unrs/resolver-binding-darwin-arm64": { "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.7.2.tgz", - "integrity": "sha512-friS8NEQfHaDbkThxopGk+LuE5v3iY0StruifjQEt7SLbA46OnfgMO15sOTkbpJkol6RB+1l1TYPXh0sCddpvA==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.7.2.tgz", + "integrity": "sha512-vxtBno4xvowwNmO/ASL0Y45TpHqmNkAaDtz4Jqb+clmcVSSl8XCG/PNFFkGsXXXS6AMjP+ja/TtNCFFa1QwLRg==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" + "darwin" ] }, "node_modules/@vitejs/plugin-react": { @@ -9478,6 +8401,12 @@ "node": ">=0.10.0" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, "node_modules/assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -10193,6 +9122,12 @@ "license": "Apache-2.0", "optional": true }, + "node_modules/base16": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", + "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==", + "license": "MIT" + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -11355,6 +10290,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cross-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -12037,6 +10981,12 @@ "dev": true, "license": "MIT" }, + "node_modules/deep-object-diff": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.9.tgz", + "integrity": "sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==", + "license": "MIT" + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -14143,6 +13093,36 @@ "bser": "2.1.1" } }, + "node_modules/fbemitter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", + "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", + "license": "BSD-3-Clause", + "dependencies": { + "fbjs": "^3.0.0" + } + }, + "node_modules/fbjs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", + "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", + "license": "MIT", + "dependencies": { + "cross-fetch": "^3.1.5", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^1.0.35" + } + }, + "node_modules/fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", + "license": "MIT" + }, "node_modules/fdir": { "version": "6.4.4", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", @@ -14369,6 +13349,19 @@ "readable-stream": "^2.3.6" } }, + "node_modules/flux": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.4.tgz", + "integrity": "sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==", + "license": "BSD-3-Clause", + "dependencies": { + "fbemitter": "^3.0.0", + "fbjs": "^3.0.1" + }, + "peerDependencies": { + "react": "^15.0.2 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -20119,12 +19112,24 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.curry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", + "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==", + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "license": "MIT" }, + "node_modules/lodash.flow": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", + "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==", + "license": "MIT" + }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -22017,6 +21022,48 @@ "dev": true, "license": "MIT" }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -22858,652 +21905,302 @@ "engines": { "node": "20 || >=22" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", - "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", - "dev": true, - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", - "dev": true, - "license": "MIT", - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/playwright": { - "version": "1.45.3", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz", - "integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.45.3" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.45.3", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz", - "integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "license": "ISC", "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": "20 || >=22" } }, - "node_modules/please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "license": "MIT", "dependencies": { - "semver-compare": "^1.0.0" + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/plugin-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-2.0.1.tgz", - "integrity": "sha512-zMakqvIDyY40xHOvzXka0kUvf40nYIuwRE8dWhti2WtjQZ31xAgBZBhxsK7vK3QbRXS1Xms/LO7B5cuAsfB2Gg==", + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-colors": "^1.0.1" - }, "engines": { - "node": ">=10.13.0" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/plugin-error/node_modules/ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-wrap": "^0.1.0" + "bin": { + "pidtree": "bin/pidtree.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.10" } }, - "node_modules/polished": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", - "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.17.8" - }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">= 6" } }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "find-up": "^4.0.0" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=8" } }, - "node_modules/postcss-attribute-case-insensitive": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-6.0.3.tgz", - "integrity": "sha512-KHkmCILThWBRtg+Jn1owTnHPnFit4OkqS+eKiGEOPIGke54DCeYGJ6r0Fx/HjfE9M9kznApCLcU0DvnPchazMQ==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.13" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": ">=8" } }, - "node_modules/postcss-attribute-case-insensitive/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/postcss-clamp": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", - "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", "dependencies": { - "postcss-value-parser": "^4.2.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=7.6.0" + "node": ">=6" }, - "peerDependencies": { - "postcss": "^8.4.6" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/postcss-color-functional-notation": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-6.0.14.tgz", - "integrity": "sha512-dNUX+UH4dAozZ8uMHZ3CtCNYw8fyFAmqqdcyxMr7PEdM9jLXV19YscoYO0F25KqZYhmtWKQ+4tKrIZQrwzwg7A==", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", + "license": "MIT", "dependencies": { - "@csstools/css-color-parser": "^2.0.4", - "@csstools/css-parser-algorithms": "^2.7.1", - "@csstools/css-tokenizer": "^2.4.1", - "@csstools/postcss-progressive-custom-properties": "^3.3.0", - "@csstools/utilities": "^1.0.0" + "p-limit": "^2.2.0" }, "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": ">=8" } }, - "node_modules/postcss-color-hex-alpha": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-9.0.4.tgz", - "integrity": "sha512-XQZm4q4fNFqVCYMGPiBjcqDhuG7Ey2xrl99AnDJMyr5eDASsAGalndVgHZF8i97VFNy1GQeZc4q2ydagGmhelQ==", + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], "license": "MIT", - "dependencies": { - "@csstools/utilities": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": ">=8" } }, - "node_modules/postcss-color-rebeccapurple": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-9.0.3.tgz", - "integrity": "sha512-ruBqzEFDYHrcVq3FnW3XHgwRqVMrtEPLBtD7K2YmsLKVc2jbkxzzNEctJKsPCpDZ+LeMHLKRDoSShVefGc+CkQ==", + "node_modules/playwright": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz", + "integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/utilities": "^1.0.0", - "postcss-value-parser": "^4.2.0" + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.45.3" + }, + "bin": { + "playwright": "cli.js" }, "engines": { - "node": "^14 || ^16 || >=18" + "node": ">=18" }, - "peerDependencies": { - "postcss": "^8.4" + "optionalDependencies": { + "fsevents": "2.3.2" } }, - "node_modules/postcss-custom-media": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-10.0.8.tgz", - "integrity": "sha512-V1KgPcmvlGdxTel4/CyQtBJEFhMVpEmRGFrnVtgfGIHj5PJX9vO36eFBxKBeJn+aCDTed70cc+98Mz3J/uVdGQ==", + "node_modules/playwright-core": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz", + "integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/cascade-layer-name-parser": "^1.0.13", - "@csstools/css-parser-algorithms": "^2.7.1", - "@csstools/css-tokenizer": "^2.4.1", - "@csstools/media-query-list-parser": "^2.1.13" + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" }, "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": ">=18" } }, - "node_modules/postcss-custom-properties": { - "version": "13.3.12", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-13.3.12.tgz", - "integrity": "sha512-oPn/OVqONB2ZLNqN185LDyaVByELAA/u3l2CS2TS16x2j2XsmV4kd8U49+TMxmUsEU9d8fB/I10E6U7kB0L1BA==", + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "@csstools/cascade-layer-name-parser": "^1.0.13", - "@csstools/css-parser-algorithms": "^2.7.1", - "@csstools/css-tokenizer": "^2.4.1", - "@csstools/utilities": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/postcss-custom-selectors": { - "version": "7.1.12", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-7.1.12.tgz", - "integrity": "sha512-ctIoprBMJwByYMGjXG0F7IT2iMF2hnamQ+aWZETyBM0aAlyaYdVZTeUkk8RB+9h9wP+NdN3f01lfvKl2ZSqC0g==", + "node_modules/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], "license": "MIT", "dependencies": { - "@csstools/cascade-layer-name-parser": "^1.0.13", - "@csstools/css-parser-algorithms": "^2.7.1", - "@csstools/css-tokenizer": "^2.4.1", - "postcss-selector-parser": "^6.1.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" + "semver-compare": "^1.0.0" } }, - "node_modules/postcss-custom-selectors/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "node_modules/plugin-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-2.0.1.tgz", + "integrity": "sha512-zMakqvIDyY40xHOvzXka0kUvf40nYIuwRE8dWhti2WtjQZ31xAgBZBhxsK7vK3QbRXS1Xms/LO7B5cuAsfB2Gg==", "dev": true, "license": "MIT", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "ansi-colors": "^1.0.1" }, "engines": { - "node": ">=4" + "node": ">=10.13.0" } }, - "node_modules/postcss-dir-pseudo-class": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-8.0.1.tgz", - "integrity": "sha512-uULohfWBBVoFiZXgsQA24JV6FdKIidQ+ZqxOouhWwdE+qJlALbkS5ScB43ZTjPK+xUZZhlaO/NjfCt5h4IKUfw==", + "node_modules/plugin-error/node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.13" + "ansi-wrap": "^0.1.0" }, "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": ">=0.10.0" } }, - "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "node_modules/polished": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", + "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", "dev": true, "license": "MIT", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "@babel/runtime": "^7.17.8" }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/postcss-double-position-gradients": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-5.0.7.tgz", - "integrity": "sha512-1xEhjV9u1s4l3iP5lRt1zvMjI/ya8492o9l/ivcxHhkO3nOz16moC4JpMxDUGrOs4R3hX+KWT7gKoV842cwRgg==", + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^3.3.0", - "@csstools/utilities": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, + "license": "MIT", "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": ">= 0.4" } }, - "node_modules/postcss-focus-visible": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-9.0.1.tgz", - "integrity": "sha512-N2VQ5uPz3Z9ZcqI5tmeholn4d+1H14fKXszpjogZIrFbhaq0zNAtq8sAnw6VLiqGbL8YBzsnu7K9bBkTqaRimQ==", - "dev": true, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/csstools" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" }, { - "type": "opencollective", - "url": "https://opencollective.com/csstools" + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], - "license": "MIT-0", - "dependencies": { - "postcss-selector-parser": "^6.0.13" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-visible/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, "license": "MIT", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { - "node": ">=4" + "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-focus-within": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-8.0.1.tgz", - "integrity": "sha512-NFU3xcY/xwNaapVb+1uJ4n23XImoC86JNwkY/uduytSl2s9Ekc2EpzmRR63+ExitnW3Mab3Fba/wRPCT5oDILA==", + "node_modules/postcss-attribute-case-insensitive": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-6.0.3.tgz", + "integrity": "sha512-KHkmCILThWBRtg+Jn1owTnHPnFit4OkqS+eKiGEOPIGke54DCeYGJ6r0Fx/HjfE9M9kznApCLcU0DvnPchazMQ==", "dev": true, "funding": [ { @@ -23515,7 +22212,7 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", + "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.13" }, @@ -23526,7 +22223,7 @@ "postcss": "^8.4" } }, - "node_modules/postcss-focus-within/node_modules/postcss-selector-parser": { + "node_modules/postcss-attribute-case-insensitive/node_modules/postcss-selector-parser": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", @@ -23540,20 +22237,26 @@ "node": ">=4" } }, - "node_modules/postcss-font-variant": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "node_modules/postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", "dev": true, "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=7.6.0" + }, "peerDependencies": { - "postcss": "^8.1.0" + "postcss": "^8.4.6" } }, - "node_modules/postcss-gap-properties": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-5.0.1.tgz", - "integrity": "sha512-k2z9Cnngc24c0KF4MtMuDdToROYqGMMUQGcE6V0odwjHyOHtaDBlLeRBV70y9/vF7KIbShrTRZ70JjsI1BZyWw==", + "node_modules/postcss-color-functional-notation": { + "version": "6.0.14", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-6.0.14.tgz", + "integrity": "sha512-dNUX+UH4dAozZ8uMHZ3CtCNYw8fyFAmqqdcyxMr7PEdM9jLXV19YscoYO0F25KqZYhmtWKQ+4tKrIZQrwzwg7A==", "dev": true, "funding": [ { @@ -23566,6 +22269,13 @@ } ], "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^2.0.4", + "@csstools/css-parser-algorithms": "^2.7.1", + "@csstools/css-tokenizer": "^2.4.1", + "@csstools/postcss-progressive-custom-properties": "^3.3.0", + "@csstools/utilities": "^1.0.0" + }, "engines": { "node": "^14 || ^16 || >=18" }, @@ -23573,10 +22283,10 @@ "postcss": "^8.4" } }, - "node_modules/postcss-image-set-function": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-6.0.3.tgz", - "integrity": "sha512-i2bXrBYzfbRzFnm+pVuxVePSTCRiNmlfssGI4H0tJQvDue+yywXwUxe68VyzXs7cGtMaH6MCLY6IbCShrSroCw==", + "node_modules/postcss-color-hex-alpha": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-9.0.4.tgz", + "integrity": "sha512-XQZm4q4fNFqVCYMGPiBjcqDhuG7Ey2xrl99AnDJMyr5eDASsAGalndVgHZF8i97VFNy1GQeZc4q2ydagGmhelQ==", "dev": true, "funding": [ { @@ -23588,7 +22298,7 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", + "license": "MIT", "dependencies": { "@csstools/utilities": "^1.0.0", "postcss-value-parser": "^4.2.0" @@ -23600,10 +22310,10 @@ "postcss": "^8.4" } }, - "node_modules/postcss-lab-function": { - "version": "6.0.19", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-6.0.19.tgz", - "integrity": "sha512-vwln/mgvFrotJuGV8GFhpAOu9iGf3pvTBr6dLPDmUcqVD5OsQpEFyQMAFTxSxWXGEzBj6ld4pZ/9GDfEpXvo0g==", + "node_modules/postcss-color-rebeccapurple": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-9.0.3.tgz", + "integrity": "sha512-ruBqzEFDYHrcVq3FnW3XHgwRqVMrtEPLBtD7K2YmsLKVc2jbkxzzNEctJKsPCpDZ+LeMHLKRDoSShVefGc+CkQ==", "dev": true, "funding": [ { @@ -23617,11 +22327,8 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^2.0.4", - "@csstools/css-parser-algorithms": "^2.7.1", - "@csstools/css-tokenizer": "^2.4.1", - "@csstools/postcss-progressive-custom-properties": "^3.3.0", - "@csstools/utilities": "^1.0.0" + "@csstools/utilities": "^1.0.0", + "postcss-value-parser": "^4.2.0" }, "engines": { "node": "^14 || ^16 || >=18" @@ -23630,168 +22337,10 @@ "postcss": "^8.4" } }, - "node_modules/postcss-load-config": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", - "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^2.1.1" - }, - "engines": { - "node": ">= 14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.3.0.tgz", - "integrity": "sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.4", - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0", - "semver": "^7.3.4" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/postcss-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/postcss-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/postcss-loader/node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postcss-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-loader/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/postcss-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/postcss-loader/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postcss-loader/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/postcss-logical": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-7.0.1.tgz", - "integrity": "sha512-8GwUQZE0ri0K0HJHkDv87XOLC8DE0msc+HoWLeKdtjDZEwpZ5xuK3QdV6FhmHSQW40LPkg43QzvATRAI3LsRkg==", + "node_modules/postcss-custom-media": { + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-10.0.8.tgz", + "integrity": "sha512-V1KgPcmvlGdxTel4/CyQtBJEFhMVpEmRGFrnVtgfGIHj5PJX9vO36eFBxKBeJn+aCDTed70cc+98Mz3J/uVdGQ==", "dev": true, "funding": [ { @@ -23803,111 +22352,97 @@ "url": "https://opencollective.com/csstools" } ], - "license": "MIT-0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-media-query-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-modules": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-4.3.1.tgz", - "integrity": "sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==", - "dev": true, "license": "MIT", "dependencies": { - "generic-names": "^4.0.0", - "icss-replace-symbols": "^1.1.0", - "lodash.camelcase": "^4.3.0", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "string-hash": "^1.1.1" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", - "dev": true, - "license": "ISC", + "@csstools/cascade-layer-name-parser": "^1.0.13", + "@csstools/css-parser-algorithms": "^2.7.1", + "@csstools/css-tokenizer": "^2.4.1", + "@csstools/media-query-list-parser": "^2.1.13" + }, "engines": { - "node": "^10 || ^12 || >= 14" + "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "postcss": "^8.1.0" + "postcss": "^8.4" } }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", - "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "node_modules/postcss-custom-properties": { + "version": "13.3.12", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-13.3.12.tgz", + "integrity": "sha512-oPn/OVqONB2ZLNqN185LDyaVByELAA/u3l2CS2TS16x2j2XsmV4kd8U49+TMxmUsEU9d8fB/I10E6U7kB0L1BA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "license": "MIT", "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.1.0" + "@csstools/cascade-layer-name-parser": "^1.0.13", + "@csstools/css-parser-algorithms": "^2.7.1", + "@csstools/css-tokenizer": "^2.4.1", + "@csstools/utilities": "^1.0.0", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >= 14" + "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "postcss": "^8.1.0" + "postcss": "^8.4" } }, - "node_modules/postcss-modules-scope": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", - "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "node_modules/postcss-custom-selectors": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-7.1.12.tgz", + "integrity": "sha512-ctIoprBMJwByYMGjXG0F7IT2iMF2hnamQ+aWZETyBM0aAlyaYdVZTeUkk8RB+9h9wP+NdN3f01lfvKl2ZSqC0g==", "dev": true, - "license": "ISC", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^7.0.0" + "@csstools/cascade-layer-name-parser": "^1.0.13", + "@csstools/css-parser-algorithms": "^2.7.1", + "@csstools/css-tokenizer": "^2.4.1", + "postcss-selector-parser": "^6.1.0" }, "engines": { - "node": "^10 || ^12 || >= 14" + "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "postcss": "^8.1.0" + "postcss": "^8.4" } }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "node_modules/postcss-custom-selectors/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "icss-utils": "^5.0.0" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node": ">=4" } }, - "node_modules/postcss-nesting": { - "version": "12.1.5", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.5.tgz", - "integrity": "sha512-N1NgI1PDCiAGWPTYrwqm8wpjv0bgDmkYHH72pNsqTCv9CObxjxftdYu6AKtGN+pnJa7FQjMm3v4sp8QJbFsYdQ==", + "node_modules/postcss-dir-pseudo-class": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-8.0.1.tgz", + "integrity": "sha512-uULohfWBBVoFiZXgsQA24JV6FdKIidQ+ZqxOouhWwdE+qJlALbkS5ScB43ZTjPK+xUZZhlaO/NjfCt5h4IKUfw==", "dev": true, "funding": [ { @@ -23921,9 +22456,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/selector-resolve-nested": "^1.1.0", - "@csstools/selector-specificity": "^3.1.1", - "postcss-selector-parser": "^6.1.0" + "postcss-selector-parser": "^6.0.13" }, "engines": { "node": "^14 || ^16 || >=18" @@ -23932,7 +22465,7 @@ "postcss": "^8.4" } }, - "node_modules/postcss-nesting/node_modules/postcss-selector-parser": { + "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", @@ -23946,33 +22479,38 @@ "node": ">=4" } }, - "node_modules/postcss-opacity-percentage": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-2.0.0.tgz", - "integrity": "sha512-lyDrCOtntq5Y1JZpBFzIWm2wG9kbEdujpNt4NLannF+J9c8CgFIzPa80YQfdza+Y+yFfzbYj/rfoOsYsooUWTQ==", + "node_modules/postcss-double-position-gradients": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-5.0.7.tgz", + "integrity": "sha512-1xEhjV9u1s4l3iP5lRt1zvMjI/ya8492o9l/ivcxHhkO3nOz16moC4JpMxDUGrOs4R3hX+KWT7gKoV842cwRgg==", "dev": true, "funding": [ { - "type": "kofi", - "url": "https://ko-fi.com/mrcgrtz" + "type": "github", + "url": "https://github.com/sponsors/csstools" }, { - "type": "liberapay", - "url": "https://liberapay.com/mrcgrtz" + "type": "opencollective", + "url": "https://opencollective.com/csstools" } ], - "license": "MIT", + "license": "MIT-0", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^3.3.0", + "@csstools/utilities": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, "engines": { "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, - "node_modules/postcss-overflow-shorthand": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-5.0.1.tgz", - "integrity": "sha512-XzjBYKLd1t6vHsaokMV9URBt2EwC9a7nDhpQpjoPk2HRTSQfokPfyAS/Q7AOrzUu6q+vp/GnrDBGuj/FCaRqrQ==", + "node_modules/postcss-focus-visible": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-9.0.1.tgz", + "integrity": "sha512-N2VQ5uPz3Z9ZcqI5tmeholn4d+1H14fKXszpjogZIrFbhaq0zNAtq8sAnw6VLiqGbL8YBzsnu7K9bBkTqaRimQ==", "dev": true, "funding": [ { @@ -23986,7 +22524,7 @@ ], "license": "MIT-0", "dependencies": { - "postcss-value-parser": "^4.2.0" + "postcss-selector-parser": "^6.0.13" }, "engines": { "node": "^14 || ^16 || >=18" @@ -23995,20 +22533,24 @@ "postcss": "^8.4" } }, - "node_modules/postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "node_modules/postcss-focus-visible/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "license": "MIT", - "peerDependencies": { - "postcss": "^8" + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" } }, - "node_modules/postcss-place": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-9.0.1.tgz", - "integrity": "sha512-JfL+paQOgRQRMoYFc2f73pGuG/Aw3tt4vYMR6UA3cWVMxivviPTnMFnFTczUJOA4K2Zga6xgQVE+PcLs64WC8Q==", + "node_modules/postcss-focus-within": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-8.0.1.tgz", + "integrity": "sha512-NFU3xcY/xwNaapVb+1uJ4n23XImoC86JNwkY/uduytSl2s9Ekc2EpzmRR63+ExitnW3Mab3Fba/wRPCT5oDILA==", "dev": true, "funding": [ { @@ -24022,7 +22564,7 @@ ], "license": "MIT-0", "dependencies": { - "postcss-value-parser": "^4.2.0" + "postcss-selector-parser": "^6.0.13" }, "engines": { "node": "^14 || ^16 || >=18" @@ -24031,85 +22573,46 @@ "postcss": "^8.4" } }, - "node_modules/postcss-preset-env": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-9.6.0.tgz", - "integrity": "sha512-Lxfk4RYjUdwPCYkc321QMdgtdCP34AeI94z+/8kVmqnTIlD4bMRQeGcMZgwz8BxHrzQiFXYIR5d7k/9JMs2MEA==", + "node_modules/postcss-focus-within/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", + "license": "MIT", "dependencies": { - "@csstools/postcss-cascade-layers": "^4.0.6", - "@csstools/postcss-color-function": "^3.0.19", - "@csstools/postcss-color-mix-function": "^2.0.19", - "@csstools/postcss-content-alt-text": "^1.0.0", - "@csstools/postcss-exponential-functions": "^1.0.9", - "@csstools/postcss-font-format-keywords": "^3.0.2", - "@csstools/postcss-gamut-mapping": "^1.0.11", - "@csstools/postcss-gradients-interpolation-method": "^4.0.20", - "@csstools/postcss-hwb-function": "^3.0.18", - "@csstools/postcss-ic-unit": "^3.0.7", - "@csstools/postcss-initial": "^1.0.1", - "@csstools/postcss-is-pseudo-class": "^4.0.8", - "@csstools/postcss-light-dark-function": "^1.0.8", - "@csstools/postcss-logical-float-and-clear": "^2.0.1", - "@csstools/postcss-logical-overflow": "^1.0.1", - "@csstools/postcss-logical-overscroll-behavior": "^1.0.1", - "@csstools/postcss-logical-resize": "^2.0.1", - "@csstools/postcss-logical-viewport-units": "^2.0.11", - "@csstools/postcss-media-minmax": "^1.1.8", - "@csstools/postcss-media-queries-aspect-ratio-number-values": "^2.0.11", - "@csstools/postcss-nested-calc": "^3.0.2", - "@csstools/postcss-normalize-display-values": "^3.0.2", - "@csstools/postcss-oklab-function": "^3.0.19", - "@csstools/postcss-progressive-custom-properties": "^3.3.0", - "@csstools/postcss-relative-color-syntax": "^2.0.19", - "@csstools/postcss-scope-pseudo-class": "^3.0.1", - "@csstools/postcss-stepped-value-functions": "^3.0.10", - "@csstools/postcss-text-decoration-shorthand": "^3.0.7", - "@csstools/postcss-trigonometric-functions": "^3.0.10", - "@csstools/postcss-unset-value": "^3.0.1", - "autoprefixer": "^10.4.19", - "browserslist": "^4.23.1", - "css-blank-pseudo": "^6.0.2", - "css-has-pseudo": "^6.0.5", - "css-prefers-color-scheme": "^9.0.1", - "cssdb": "^8.1.0", - "postcss-attribute-case-insensitive": "^6.0.3", - "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^6.0.14", - "postcss-color-hex-alpha": "^9.0.4", - "postcss-color-rebeccapurple": "^9.0.3", - "postcss-custom-media": "^10.0.8", - "postcss-custom-properties": "^13.3.12", - "postcss-custom-selectors": "^7.1.12", - "postcss-dir-pseudo-class": "^8.0.1", - "postcss-double-position-gradients": "^5.0.7", - "postcss-focus-visible": "^9.0.1", - "postcss-focus-within": "^8.0.1", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^5.0.1", - "postcss-image-set-function": "^6.0.3", - "postcss-lab-function": "^6.0.19", - "postcss-logical": "^7.0.1", - "postcss-nesting": "^12.1.5", - "postcss-opacity-percentage": "^2.0.0", - "postcss-overflow-shorthand": "^5.0.1", - "postcss-page-break": "^3.0.4", - "postcss-place": "^9.0.1", - "postcss-pseudo-class-any-link": "^9.0.2", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^7.0.2" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-5.0.1.tgz", + "integrity": "sha512-k2z9Cnngc24c0KF4MtMuDdToROYqGMMUQGcE6V0odwjHyOHtaDBlLeRBV70y9/vF7KIbShrTRZ70JjsI1BZyWw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", "engines": { "node": "^14 || ^16 || >=18" }, @@ -24117,10 +22620,10 @@ "postcss": "^8.4" } }, - "node_modules/postcss-pseudo-class-any-link": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-9.0.2.tgz", - "integrity": "sha512-HFSsxIqQ9nA27ahyfH37cRWGk3SYyQLpk0LiWw/UGMV4VKT5YG2ONee4Pz/oFesnK0dn2AjcyequDbIjKJgB0g==", + "node_modules/postcss-image-set-function": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-6.0.3.tgz", + "integrity": "sha512-i2bXrBYzfbRzFnm+pVuxVePSTCRiNmlfssGI4H0tJQvDue+yywXwUxe68VyzXs7cGtMaH6MCLY6IbCShrSroCw==", "dev": true, "funding": [ { @@ -24134,7 +22637,8 @@ ], "license": "MIT-0", "dependencies": { - "postcss-selector-parser": "^6.0.13" + "@csstools/utilities": "^1.0.0", + "postcss-value-parser": "^4.2.0" }, "engines": { "node": "^14 || ^16 || >=18" @@ -24143,846 +22647,949 @@ "postcss": "^8.4" } }, - "node_modules/postcss-pseudo-class-any-link/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "node_modules/postcss-lab-function": { + "version": "6.0.19", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-6.0.19.tgz", + "integrity": "sha512-vwln/mgvFrotJuGV8GFhpAOu9iGf3pvTBr6dLPDmUcqVD5OsQpEFyQMAFTxSxWXGEzBj6ld4pZ/9GDfEpXvo0g==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "@csstools/css-color-parser": "^2.0.4", + "@csstools/css-parser-algorithms": "^2.7.1", + "@csstools/css-tokenizer": "^2.4.1", + "@csstools/postcss-progressive-custom-properties": "^3.3.0", + "@csstools/utilities": "^1.0.0" }, "engines": { - "node": ">=4" + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.4" } }, - "node_modules/postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "node_modules/postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", "dev": true, "license": "MIT", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + }, + "engines": { + "node": ">= 14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, "peerDependencies": { - "postcss": "^8.0.3" + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/postcss-resolve-nested-selector": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", - "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-safe-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", - "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "node_modules/postcss-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.3.0.tgz", + "integrity": "sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==", "dev": true, "license": "MIT", + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.4" + }, "engines": { - "node": ">=12.0" + "node": ">= 10.13.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "postcss": "^8.3.3" + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/postcss-scss": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", - "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", + "node_modules/postcss-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss-scss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", - "engines": { - "node": ">=12.0" + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/postcss-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", "peerDependencies": { - "postcss": "^8.4.29" + "ajv": "^6.9.1" } }, - "node_modules/postcss-selector-not": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-7.0.2.tgz", - "integrity": "sha512-/SSxf/90Obye49VZIfc0ls4H0P6i6V1iHv0pzZH8SdgvZOPFkF37ef1r5cyWcMflJSFJ5bfuoluTnFnBBFiuSA==", + "node_modules/postcss-loader/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.13" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" }, "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": ">=10" } }, - "node_modules/postcss-selector-not/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "node_modules/postcss-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-loader/node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "license": "MIT", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">=4" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "node_modules/postcss-loader/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-loader/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-logical": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-7.0.1.tgz", + "integrity": "sha512-8GwUQZE0ri0K0HJHkDv87XOLC8DE0msc+HoWLeKdtjDZEwpZ5xuK3QdV6FhmHSQW40LPkg43QzvATRAI3LsRkg==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-sorting": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-8.0.2.tgz", - "integrity": "sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==", - "dev": true, - "license": "MIT", + "node": "^14 || ^16 || >=18" + }, "peerDependencies": { - "postcss": "^8.4.20" + "postcss": "^8.4" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", "dev": true, "license": "MIT" }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/postcss-modules": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-4.3.1.tgz", + "integrity": "sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.8.0" + "dependencies": { + "generic-names": "^4.0.0", + "icss-replace-symbols": "^1.1.0", + "lodash.camelcase": "^4.3.0", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "string-hash": "^1.1.1" + }, + "peerDependencies": { + "postcss": "^8.0.0" } }, - "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, + "license": "ISC", "engines": { - "node": ">=14" + "node": "^10 || ^12 || >= 14" }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", "dev": true, "license": "MIT", "dependencies": { - "fast-diff": "^1.1.2" + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" }, "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" + "postcss-selector-parser": "^7.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, "engines": { - "node": ">=10" + "node": "^10 || ^12 || >= 14" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "node_modules/postcss-nesting": { + "version": "12.1.5", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.5.tgz", + "integrity": "sha512-N1NgI1PDCiAGWPTYrwqm8wpjv0bgDmkYHH72pNsqTCv9CObxjxftdYu6AKtGN+pnJa7FQjMm3v4sp8QJbFsYdQ==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/selector-resolve-nested": "^1.1.0", + "@csstools/selector-specificity": "^3.1.1", + "postcss-selector-parser": "^6.1.0" + }, "engines": { - "node": ">= 0.6.0" + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.4" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "node_modules/postcss-nesting/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "license": "MIT", "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" + "node": ">=4" } }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/psl": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", - "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "node_modules/postcss-opacity-percentage": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-2.0.0.tgz", + "integrity": "sha512-lyDrCOtntq5Y1JZpBFzIWm2wG9kbEdujpNt4NLannF+J9c8CgFIzPa80YQfdza+Y+yFfzbYj/rfoOsYsooUWTQ==", "dev": true, + "funding": [ + { + "type": "kofi", + "url": "https://ko-fi.com/mrcgrtz" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mrcgrtz" + } + ], "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" + "engines": { + "node": "^14 || ^16 || >=18" }, - "funding": { - "url": "https://github.com/sponsors/lupomontero" + "peerDependencies": { + "postcss": "^8.2" } }, - "node_modules/pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "node_modules/postcss-overflow-shorthand": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-5.0.1.tgz", + "integrity": "sha512-XzjBYKLd1t6vHsaokMV9URBt2EwC9a7nDhpQpjoPk2HRTSQfokPfyAS/Q7AOrzUu6q+vp/GnrDBGuj/FCaRqrQ==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.4" } }, - "node_modules/pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", "dev": true, "license": "MIT", - "dependencies": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" + "peerDependencies": { + "postcss": "^8" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/postcss-place": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-9.0.1.tgz", + "integrity": "sha512-JfL+paQOgRQRMoYFc2f73pGuG/Aw3tt4vYMR6UA3cWVMxivviPTnMFnFTczUJOA4K2Zga6xgQVE+PcLs64WC8Q==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, "engines": { - "node": ">=6" + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.4" } }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "node_modules/postcss-preset-env": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-9.6.0.tgz", + "integrity": "sha512-Lxfk4RYjUdwPCYkc321QMdgtdCP34AeI94z+/8kVmqnTIlD4bMRQeGcMZgwz8BxHrzQiFXYIR5d7k/9JMs2MEA==", "dev": true, "funding": [ { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" + "type": "github", + "url": "https://github.com/sponsors/csstools" }, { "type": "opencollective", - "url": "https://opencollective.com/fast-check" + "url": "https://opencollective.com/csstools" } ], - "license": "MIT" - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "dev": true, - "license": "BSD-3-Clause", + "license": "MIT-0", "dependencies": { - "side-channel": "^1.1.0" + "@csstools/postcss-cascade-layers": "^4.0.6", + "@csstools/postcss-color-function": "^3.0.19", + "@csstools/postcss-color-mix-function": "^2.0.19", + "@csstools/postcss-content-alt-text": "^1.0.0", + "@csstools/postcss-exponential-functions": "^1.0.9", + "@csstools/postcss-font-format-keywords": "^3.0.2", + "@csstools/postcss-gamut-mapping": "^1.0.11", + "@csstools/postcss-gradients-interpolation-method": "^4.0.20", + "@csstools/postcss-hwb-function": "^3.0.18", + "@csstools/postcss-ic-unit": "^3.0.7", + "@csstools/postcss-initial": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^4.0.8", + "@csstools/postcss-light-dark-function": "^1.0.8", + "@csstools/postcss-logical-float-and-clear": "^2.0.1", + "@csstools/postcss-logical-overflow": "^1.0.1", + "@csstools/postcss-logical-overscroll-behavior": "^1.0.1", + "@csstools/postcss-logical-resize": "^2.0.1", + "@csstools/postcss-logical-viewport-units": "^2.0.11", + "@csstools/postcss-media-minmax": "^1.1.8", + "@csstools/postcss-media-queries-aspect-ratio-number-values": "^2.0.11", + "@csstools/postcss-nested-calc": "^3.0.2", + "@csstools/postcss-normalize-display-values": "^3.0.2", + "@csstools/postcss-oklab-function": "^3.0.19", + "@csstools/postcss-progressive-custom-properties": "^3.3.0", + "@csstools/postcss-relative-color-syntax": "^2.0.19", + "@csstools/postcss-scope-pseudo-class": "^3.0.1", + "@csstools/postcss-stepped-value-functions": "^3.0.10", + "@csstools/postcss-text-decoration-shorthand": "^3.0.7", + "@csstools/postcss-trigonometric-functions": "^3.0.10", + "@csstools/postcss-unset-value": "^3.0.1", + "autoprefixer": "^10.4.19", + "browserslist": "^4.23.1", + "css-blank-pseudo": "^6.0.2", + "css-has-pseudo": "^6.0.5", + "css-prefers-color-scheme": "^9.0.1", + "cssdb": "^8.1.0", + "postcss-attribute-case-insensitive": "^6.0.3", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^6.0.14", + "postcss-color-hex-alpha": "^9.0.4", + "postcss-color-rebeccapurple": "^9.0.3", + "postcss-custom-media": "^10.0.8", + "postcss-custom-properties": "^13.3.12", + "postcss-custom-selectors": "^7.1.12", + "postcss-dir-pseudo-class": "^8.0.1", + "postcss-double-position-gradients": "^5.0.7", + "postcss-focus-visible": "^9.0.1", + "postcss-focus-within": "^8.0.1", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^5.0.1", + "postcss-image-set-function": "^6.0.3", + "postcss-lab-function": "^6.0.19", + "postcss-logical": "^7.0.1", + "postcss-nesting": "^12.1.5", + "postcss-opacity-percentage": "^2.0.0", + "postcss-overflow-shorthand": "^5.0.1", + "postcss-page-break": "^3.0.4", + "postcss-place": "^9.0.1", + "postcss-pseudo-class-any-link": "^9.0.2", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^7.0.2" }, "engines": { - "node": ">=0.6" + "node": "^14 || ^16 || >=18" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "postcss": "^8.4" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/postcss-pseudo-class-any-link": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-9.0.2.tgz", + "integrity": "sha512-HFSsxIqQ9nA27ahyfH37cRWGk3SYyQLpk0LiWw/UGMV4VKT5YG2ONee4Pz/oFesnK0dn2AjcyequDbIjKJgB0g==", "dev": true, "funding": [ { "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "url": "https://github.com/sponsors/csstools" }, { - "type": "consulting", - "url": "https://feross.org/support" + "type": "opencollective", + "url": "https://opencollective.com/csstools" } ], - "license": "MIT" - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true, - "license": "MIT", + "license": "MIT-0", + "dependencies": { + "postcss-selector-parser": "^6.0.13" + }, "engines": { - "node": ">=10" + "node": "^14 || ^16 || >=18" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "postcss": "^8.4" } }, - "node_modules/raf-schd": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", - "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==", + "node_modules/postcss-pseudo-class-any-link/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", "dev": true, "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" + "peerDependencies": { + "postcss": "^8.0.3" } }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", + "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" } }, - "node_modules/rc-slider": { - "version": "11.1.8", - "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.8.tgz", - "integrity": "sha512-2gg/72YFSpKP+Ja5AjC5DPL1YnV8DEITDQrcc1eASrUYjl0esptaBVJBh5nLTXCCp15eD8EuGjwezVGSHhs9tQ==", + "node_modules/postcss-scss": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.5", - "rc-util": "^5.36.0" - }, "engines": { - "node": ">=8.x" + "node": ">=12.0" }, "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" + "postcss": "^8.4.29" } }, - "node_modules/rc-util": { - "version": "5.44.4", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz", - "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==", + "node_modules/postcss-selector-not": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-7.0.2.tgz", + "integrity": "sha512-/SSxf/90Obye49VZIfc0ls4H0P6i6V1iHv0pzZH8SdgvZOPFkF37ef1r5cyWcMflJSFJ5bfuoluTnFnBBFiuSA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "license": "MIT", "dependencies": { - "@babel/runtime": "^7.18.3", - "react-is": "^18.2.0" + "postcss-selector-parser": "^6.0.13" + }, + "engines": { + "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" + "postcss": "^8.4" } }, - "node_modules/rc-util/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "node_modules/postcss-selector-not/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/react-beautiful-dnd": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz", - "integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==", - "deprecated": "react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672", + "node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.9.2", - "css-box-model": "^1.2.0", - "memoize-one": "^5.1.1", - "raf-schd": "^4.0.2", - "react-redux": "^7.2.0", - "redux": "^4.0.4", - "use-memo-one": "^1.1.1" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, - "peerDependencies": { - "react": "^16.8.5 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" + "engines": { + "node": ">=4" } }, - "node_modules/react-colorful": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", - "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", + "node_modules/postcss-sorting": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-8.0.2.tgz", + "integrity": "sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==", "dev": true, "license": "MIT", "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "postcss": "^8.4.20" } }, - "node_modules/react-docgen": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-7.1.1.tgz", - "integrity": "sha512-hlSJDQ2synMPKFZOsKo9Hi8WWZTC7POR8EmWvTSjow+VDgKzkmjQvFm2fk0tmRw+f0vTOIYKlarR0iL4996pdg==", + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.18.9", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9", - "@types/babel__core": "^7.18.0", - "@types/babel__traverse": "^7.18.0", - "@types/doctrine": "^0.0.9", - "@types/resolve": "^1.20.2", - "doctrine": "^3.0.0", - "resolve": "^1.22.1", - "strip-indent": "^4.0.0" - }, - "engines": { - "node": ">=16.14.0" - } + "license": "MIT" }, - "node_modules/react-docgen-typescript": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz", - "integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", - "peerDependencies": { - "typescript": ">= 4.3.x" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "bin": { + "prettier": "bin/prettier.cjs" }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/react-fast-compare": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", - "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", - "license": "MIT" - }, - "node_modules/react-final-form": { - "version": "6.5.9", - "resolved": "https://registry.npmjs.org/react-final-form/-/react-final-form-6.5.9.tgz", - "integrity": "sha512-x3XYvozolECp3nIjly+4QqxdjSSWfcnpGEL5K8OBT6xmGrq5kBqbA6+/tOqoom9NwqIPPbxPNsOViFlbKgowbA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.15.4" + "engines": { + "node": ">=14" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/final-form" - }, - "peerDependencies": { - "final-form": "^4.20.4", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "license": "MIT" - }, - "node_modules/react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, - "license": "MIT" - }, - "node_modules/react-monaco-editor": { - "version": "0.53.0", - "resolved": "https://registry.npmjs.org/react-monaco-editor/-/react-monaco-editor-0.53.0.tgz", - "integrity": "sha512-ZITzsauH4CsicCGddtpgjRACaKNTVEL2hnjYFx8QMuc8zmsgfgq7D2GcF8OQsa2URIdGm/Zl7YwY2fmWpvqs/g==", "license": "MIT", "dependencies": { - "prop-types": "^15.8.1" + "fast-diff": "^1.1.2" }, - "peerDependencies": { - "@types/react": ">=16 <= 18", - "monaco-editor": "^0.38.0", - "react": ">=16 <= 18" + "engines": { + "node": ">=6.0.0" } }, - "node_modules/react-player": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/react-player/-/react-player-2.16.0.tgz", - "integrity": "sha512-mAIPHfioD7yxO0GNYVFD1303QFtI3lyyQZLY229UEAp/a10cSW+hPcakg0Keq8uWJxT2OiT/4Gt+Lc9bD6bJmQ==", + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dev": true, "license": "MIT", "dependencies": { - "deepmerge": "^4.0.0", - "load-script": "^1.0.0", - "memoize-one": "^5.1.1", - "prop-types": "^15.7.2", - "react-fast-compare": "^3.0.1" - }, - "peerDependencies": { - "react": ">=16.6.0" + "lodash": "^4.17.20", + "renderkid": "^3.0.0" } }, - "node_modules/react-redux": { - "version": "7.2.9", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", - "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.15.4", - "@types/react-redux": "^7.1.20", - "hoist-non-react-statics": "^3.3.2", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-is": "^17.0.2" - }, - "peerDependencies": { - "react": "^16.8.3 || ^17 || ^18" + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, - "node_modules/react-refresh": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", - "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", - "dev": true, - "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/react-resizable-panels": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.1.7.tgz", - "integrity": "sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA==", - "peerDependencies": { - "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", - "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/react-select": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.1.tgz", - "integrity": "sha512-roPEZUL4aRZDx6DcsD+ZNreVl+fM8VsKn0Wtex1v4IazH60ILp5xhdlp464IsEAlJdXeD+BhDAFsBVMfvLQueA==", + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.0", - "@emotion/cache": "^11.4.0", - "@emotion/react": "^11.8.1", - "@floating-ui/dom": "^1.0.1", - "@types/react-transition-group": "^4.4.0", - "memoize-one": "^6.0.0", - "prop-types": "^15.6.0", - "react-transition-group": "^4.3.0", - "use-isomorphic-layout-effect": "^1.2.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "engines": { + "node": ">= 0.6.0" } }, - "node_modules/react-select/node_modules/memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true, "license": "MIT" }, - "node_modules/react-slick": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.29.0.tgz", - "integrity": "sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA==", + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "license": "MIT", "dependencies": { - "classnames": "^2.2.5", - "enquire.js": "^2.1.6", - "json2mq": "^0.2.0", - "lodash.debounce": "^4.0.8", - "resize-observer-polyfill": "^1.5.0" - }, - "peerDependencies": { - "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0" + "asap": "~2.0.3" } }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" + "engines": { + "node": ">= 6" } }, - "node_modules/react-virtualized-auto-sizer": { - "version": "1.0.26", - "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.26.tgz", - "integrity": "sha512-CblNyiNVw2o+hsa5/49NH2ogGxZ+t+3aweRvNSq7TVjDIlwk7ir4lencEg5HxHeSzwNarSkNkiu0qJSOXtxm5A==", - "dev": true, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", - "peerDependencies": { - "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0" + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" } }, - "node_modules/react-waypoint": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/react-waypoint/-/react-waypoint-10.3.0.tgz", - "integrity": "sha512-iF1y2c1BsoXuEGz08NoahaLFIGI9gTUAAOKip96HUmylRT6DUtpgoBPjk/Y8dfcFVmfVDvUzWjNXpZyKTOV0SQ==", + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.12.5", - "consolidated-events": "^1.1.0 || ^2.0.0", - "prop-types": "^15.0.0", - "react-is": "^17.0.1 || ^18.0.0" + "punycode": "^2.3.1" }, - "peerDependencies": { - "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + "funding": { + "url": "https://github.com/sponsors/lupomontero" } }, - "node_modules/react-window": { - "version": "1.8.11", - "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.11.tgz", - "integrity": "sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==", + "node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.0.0", - "memoize-one": ">=3.1.1 <6" - }, - "engines": { - "node": ">8.0.0" - }, - "peerDependencies": { - "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "dev": true, "license": "MIT", "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" } }, - "node_modules/read-pkg-up": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-8.0.0.tgz", - "integrity": "sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", - "dependencies": { - "find-up": "^5.0.0", - "read-pkg": "^6.0.0", - "type-fest": "^1.0.1" - }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/pure-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", + "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==", + "license": "MIT" + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "side-channel": "^1.1.0" }, "engines": { - "node": ">=10" + "node": ">=0.6" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/read-pkg-up/node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } + "license": "MIT" }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, "engines": { "node": ">=10" }, @@ -24990,1111 +23597,1123 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==", "dev": true, - "license": "ISC", + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "safe-buffer": "^5.1.0" } }, - "node_modules/read-pkg-up/node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.6" } }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/rc-slider": { + "version": "11.1.8", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.8.tgz", + "integrity": "sha512-2gg/72YFSpKP+Ja5AjC5DPL1YnV8DEITDQrcc1eASrUYjl0esptaBVJBh5nLTXCCp15eD8EuGjwezVGSHhs9tQ==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.36.0" }, "engines": { - "node": ">=10" + "node": ">=8.x" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/read-pkg-up/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/rc-util": { + "version": "5.44.4", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz", + "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@babel/runtime": "^7.18.3", + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/read-pkg-up/node_modules/read-pkg": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-6.0.0.tgz", - "integrity": "sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==", + "node_modules/rc-util/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^3.0.2", - "parse-json": "^5.2.0", - "type-fest": "^1.0.1" + "loose-envify": "^1.1.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/read-pkg-up/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "node_modules/react-base16-styling": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", + "integrity": "sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==", + "license": "MIT", + "dependencies": { + "base16": "^1.0.0", + "lodash.curry": "^4.0.1", + "lodash.flow": "^3.3.0", + "pure-color": "^1.2.0" } }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "node_modules/react-beautiful-dnd": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz", + "integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==", + "deprecated": "react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672", "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.9.2", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.2.0", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "react": "^16.8.5 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" } }, - "node_modules/read-pkg-up/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "node_modules/react-colorful": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", + "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", "dev": true, - "license": "ISC" + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/react-docgen": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-7.1.1.tgz", + "integrity": "sha512-hlSJDQ2synMPKFZOsKo9Hi8WWZTC7POR8EmWvTSjow+VDgKzkmjQvFm2fk0tmRw+f0vTOIYKlarR0iL4996pdg==", "dev": true, "license": "MIT", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "@babel/core": "^7.18.9", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9", + "@types/babel__core": "^7.18.0", + "@types/babel__traverse": "^7.18.0", + "@types/doctrine": "^0.0.9", + "@types/resolve": "^1.20.2", + "doctrine": "^3.0.0", + "resolve": "^1.22.1", + "strip-indent": "^4.0.0" + }, + "engines": { + "node": ">=16.14.0" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/react-docgen-typescript": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz", + "integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">= 4.3.x" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dev": true, "license": "MIT", "dependencies": { - "picomatch": "^2.2.1" + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" }, - "engines": { - "node": ">=8.10.0" + "peerDependencies": { + "react": "^18.3.1" } }, - "node_modules/recast": { - "version": "0.23.11", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", - "integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==", - "dev": true, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" + }, + "node_modules/react-final-form": { + "version": "6.5.9", + "resolved": "https://registry.npmjs.org/react-final-form/-/react-final-form-6.5.9.tgz", + "integrity": "sha512-x3XYvozolECp3nIjly+4QqxdjSSWfcnpGEL5K8OBT6xmGrq5kBqbA6+/tOqoom9NwqIPPbxPNsOViFlbKgowbA==", "license": "MIT", "dependencies": { - "ast-types": "^0.16.1", - "esprima": "~4.0.0", - "source-map": "~0.6.1", - "tiny-invariant": "^1.3.3", - "tslib": "^2.0.1" + "@babel/runtime": "^7.15.4" }, - "engines": { - "node": ">= 4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/final-form" + }, + "peerDependencies": { + "final-form": "^4.20.4", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dev": true, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "license": "MIT" + }, + "node_modules/react-json-view": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", + "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", "license": "MIT", "dependencies": { - "resolve": "^1.20.0" + "flux": "^4.0.1", + "react-base16-styling": "^0.6.0", + "react-lifecycles-compat": "^3.0.4", + "react-textarea-autosize": "^8.3.2" }, - "engines": { - "node": ">= 10.13.0" + "peerDependencies": { + "react": "^17.0.0 || ^16.3.0 || ^15.5.4", + "react-dom": "^17.0.0 || ^16.3.0 || ^15.5.4" } }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "license": "MIT" + }, + "node_modules/react-monaco-editor": { + "version": "0.53.0", + "resolved": "https://registry.npmjs.org/react-monaco-editor/-/react-monaco-editor-0.53.0.tgz", + "integrity": "sha512-ZITzsauH4CsicCGddtpgjRACaKNTVEL2hnjYFx8QMuc8zmsgfgq7D2GcF8OQsa2URIdGm/Zl7YwY2fmWpvqs/g==", "license": "MIT", "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "prop-types": "^15.8.1" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "@types/react": ">=16 <= 18", + "monaco-editor": "^0.38.0", + "react": ">=16 <= 18" } }, - "node_modules/redent/node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, + "node_modules/react-player": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/react-player/-/react-player-2.16.0.tgz", + "integrity": "sha512-mAIPHfioD7yxO0GNYVFD1303QFtI3lyyQZLY229UEAp/a10cSW+hPcakg0Keq8uWJxT2OiT/4Gt+Lc9bD6bJmQ==", "license": "MIT", "dependencies": { - "min-indent": "^1.0.0" + "deepmerge": "^4.0.0", + "load-script": "^1.0.0", + "memoize-one": "^5.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.0.1" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "react": ">=16.6.0" } }, - "node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "node_modules/react-redux": { + "version": "7.2.9", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", + "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.9.2" + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "peerDependencies": { + "react": "^16.8.3 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } } }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true, - "license": "MIT" + "node_modules/react-resizable-panels": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.1.7.tgz", + "integrity": "sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA==", + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "node_modules/react-select": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.1.tgz", + "integrity": "sha512-roPEZUL4aRZDx6DcsD+ZNreVl+fM8VsKn0Wtex1v4IazH60ILp5xhdlp464IsEAlJdXeD+BhDAFsBVMfvLQueA==", "dev": true, "license": "MIT", "dependencies": { - "regenerate": "^1.4.2" + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.2.0" }, - "engines": { - "node": ">=4" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/regex-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.1.tgz", - "integrity": "sha512-yXLRqatcCuKtVHsWrNg0JL3l1zGfdXeEvDa0bdu4tCDQw0RpMDZsqbkyRTUnKMR0tXF627V2oEWjBEaEdqTwtQ==", + "node_modules/react-select/node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", "dev": true, "license": "MIT" }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", - "dev": true, - "license": "MIT", - "bin": { - "regexp-tree": "bin/regexp-tree" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "dev": true, + "node_modules/react-slick": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.29.0.tgz", + "integrity": "sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" + "classnames": "^2.2.5", + "enquire.js": "^2.1.6", + "json2mq": "^0.2.0", + "lodash.debounce": "^4.0.8", + "resize-observer-polyfill": "^1.5.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/regexpu-core": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", - "dev": true, + "node_modules/react-textarea-autosize": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", + "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", "license": "MIT", "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", "dependencies": { - "jsesc": "~3.0.2" + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" }, - "bin": { - "regjsparser": "bin/parser" + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "node_modules/react-virtualized-auto-sizer": { + "version": "1.0.26", + "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.26.tgz", + "integrity": "sha512-CblNyiNVw2o+hsa5/49NH2ogGxZ+t+3aweRvNSq7TVjDIlwk7ir4lencEg5HxHeSzwNarSkNkiu0qJSOXtxm5A==", "dev": true, "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" + "peerDependencies": { + "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "dev": true, + "node_modules/react-waypoint": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/react-waypoint/-/react-waypoint-10.3.0.tgz", + "integrity": "sha512-iF1y2c1BsoXuEGz08NoahaLFIGI9gTUAAOKip96HUmylRT6DUtpgoBPjk/Y8dfcFVmfVDvUzWjNXpZyKTOV0SQ==", "license": "MIT", - "engines": { - "node": ">= 0.10" + "dependencies": { + "@babel/runtime": "^7.12.5", + "consolidated-events": "^1.1.0 || ^2.0.0", + "prop-types": "^15.0.0", + "react-is": "^17.0.1 || ^18.0.0" + }, + "peerDependencies": { + "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "node_modules/react-window": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.11.tgz", + "integrity": "sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">8.0.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "dev": true, "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=4" } }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "node_modules/read-pkg-up": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-8.0.0.tgz", + "integrity": "sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" + "find-up": "^5.0.0", + "read-pkg": "^6.0.0", + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/remove-bom-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", - "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { - "is-buffer": "^1.1.5", - "is-utf8": "^0.2.1" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/remove-bom-stream": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", - "integrity": "sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==", + "node_modules/read-pkg-up/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "remove-bom-buffer": "^3.0.0", - "safe-buffer": "^5.1.0", - "through2": "^2.0.3" + "lru-cache": "^6.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">=10" } }, - "node_modules/remove-bom-stream/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", - "dev": true, - "license": "ISC" - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "node_modules/read-pkg-up/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/renderkid/node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "node_modules/read-pkg-up/node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "engines": { + "node": ">=10" } }, - "node_modules/renderkid/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/renderkid/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, + "license": "MIT", "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "node": ">=8" } }, - "node_modules/renderkid/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "node_modules/read-pkg-up/node_modules/read-pkg": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-6.0.0.tgz", + "integrity": "sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/replace-ext": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", - "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "node_modules/read-pkg-up/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, - "license": "MIT", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">= 10" + "node": ">=10" } }, - "node_modules/replace-homedir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-2.0.0.tgz", - "integrity": "sha512-bgEuQQ/BHW0XkkJtawzrfzHFSN70f/3cNOiHa2QsYxqrjaC30X1k74FJ6xswVBP0sr0SpGIdVFuPwfrYziVeyw==", + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, - "license": "MIT", + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">= 10.13.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/replacestream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz", - "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==", + "node_modules/read-pkg-up/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "escape-string-regexp": "^1.0.3", - "object-assign": "^4.0.1", - "readable-stream": "^2.0.2" - } + "license": "ISC" }, - "node_modules/replacestream/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.8.0" + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "node_modules/recast": { + "version": "0.23.11", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", + "integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==", + "dev": true, "license": "MIT", + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 4" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/resize-observer-polyfill": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "resolve": "^1.20.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 10.13.0" } }, - "node_modules/resolve-cwd": { + "node_modules/redent": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", "dev": true, "license": "MIT", "dependencies": { - "resolve-from": "^5.0.0" + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "node_modules/redent/node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", "dev": true, "license": "MIT", "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "min-indent": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@babel/runtime": "^7.9.2" } }, - "node_modules/resolve-options": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-2.0.0.tgz", - "integrity": "sha512-/FopbmmFOQCfsCx77BRFdKOniglTiHumLgwvd6IDPihy1GKkadZbgQJBcTb2lMzSR1pndzd96b1nZrreZ7+9/A==", + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, "license": "MIT", "dependencies": { - "value-or-function": "^4.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" }, "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", + "node": ">= 0.4" + }, "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true, "license": "MIT" }, - "node_modules/resolve-url-loader": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.5.tgz", - "integrity": "sha512-mgFMCmrV/tA4738EsFmPFE5/MaqSgUMe8LK971kVEKA/RrNVb7+VqFsg/qmKyythf34eyq476qIobP/gfFBGSQ==", + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "dev": true, "license": "MIT", "dependencies": { - "adjust-sourcemap-loader": "3.0.0", - "camelcase": "5.3.1", - "compose-function": "3.0.3", - "convert-source-map": "1.7.0", - "es6-iterator": "2.0.3", - "loader-utils": "^1.2.3", - "postcss": "7.0.36", - "rework": "1.0.1", - "rework-visit": "1.0.0", - "source-map": "0.6.1" + "regenerate": "^1.4.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=4" } }, - "node_modules/resolve-url-loader/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/regex-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.1.tgz", + "integrity": "sha512-yXLRqatcCuKtVHsWrNg0JL3l1zGfdXeEvDa0bdu4tCDQw0RpMDZsqbkyRTUnKMR0tXF627V2oEWjBEaEdqTwtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true, + "license": "MIT", + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" }, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-url-loader/node_modules/chalk/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" }, "engines": { "node": ">=4" } }, - "node_modules/resolve-url-loader/node_modules/convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "safe-buffer": "~5.1.1" + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" } }, - "node_modules/resolve-url-loader/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, "engines": { - "node": ">=0.8.0" + "node": ">=6" } }, - "node_modules/resolve-url-loader/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.10" } }, - "node_modules/resolve-url-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", "dev": true, "license": "MIT", "dependencies": { - "minimist": "^1.2.0" + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" }, - "bin": { - "json5": "lib/cli.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/resolve-url-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", "dev": true, "license": "MIT", "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" }, - "engines": { - "node": ">=4.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/resolve-url-loader/node_modules/postcss": { - "version": "7.0.36", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", - "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "url": "https://opencollective.com/unified" } }, - "node_modules/resolve-url-loader/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "node_modules/remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" }, "engines": { - "node": ">=6" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "node_modules/remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==", "dev": true, "license": "MIT", "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" }, "engines": { - "node": ">=8" + "node": ">= 0.10" } }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "node_modules/remove-bom-stream/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rework": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", - "integrity": "sha512-eEjL8FdkdsxApd0yWVZgBGzfCQiT8yqSc2H1p4jpZpQdtz7ohETiDMoje5PlM8I9WgkqkreVxFUKYOiJdVWDXw==", - "dev": true, "dependencies": { - "convert-source-map": "^0.3.3", - "css": "^2.0.0" + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" } }, - "node_modules/rework-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz", - "integrity": "sha512-W6V2fix7nCLUYX1v6eGPrBOZlc03/faqzP4sUxMAJMBMOPYhfV/RyLegTufn5gJKaOITyi+gvf0LXDZ9NzkHnQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/rework/node_modules/convert-source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", - "integrity": "sha512-+4nRk0k3oEpwUB7/CalD7xE2z4VmtEnnq0GO2IPTkrooTrAhEsWvuLF5iWP1dXrwluki/azwXV1ve7gtYuPldg==", + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/rework/node_modules/css": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "source-map": "^0.6.1", - "source-map-resolve": "^0.5.2", - "urix": "^0.1.0" + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" } }, - "node_modules/rework/node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "node_modules/renderkid/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/rfdc": { + "node_modules/renderkid/node_modules/dom-serializer": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/rimraf": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", - "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "glob": "^11.0.0", - "package-json-from-dist": "^1.0.0" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz", - "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==", + "node_modules/renderkid/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "dev": true, - "license": "ISC", + "license": "BSD-2-Clause", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "domelementtype": "^2.2.0" }, "engines": { - "node": "20 || >=22" + "node": ">= 4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "node_modules/renderkid/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "dev": true, - "license": "ISC", + "license": "BSD-2-Clause", "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": "20 || >=22" + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/rollup": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.0.tgz", - "integrity": "sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==", + "node_modules/renderkid/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", "dev": true, "license": "MIT", - "dependencies": { - "@types/estree": "1.0.7" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.41.0", - "@rollup/rollup-android-arm64": "4.41.0", - "@rollup/rollup-darwin-arm64": "4.41.0", - "@rollup/rollup-darwin-x64": "4.41.0", - "@rollup/rollup-freebsd-arm64": "4.41.0", - "@rollup/rollup-freebsd-x64": "4.41.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.41.0", - "@rollup/rollup-linux-arm-musleabihf": "4.41.0", - "@rollup/rollup-linux-arm64-gnu": "4.41.0", - "@rollup/rollup-linux-arm64-musl": "4.41.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.41.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.41.0", - "@rollup/rollup-linux-riscv64-gnu": "4.41.0", - "@rollup/rollup-linux-riscv64-musl": "4.41.0", - "@rollup/rollup-linux-s390x-gnu": "4.41.0", - "@rollup/rollup-linux-x64-gnu": "4.41.0", - "@rollup/rollup-linux-x64-musl": "4.41.0", - "@rollup/rollup-win32-arm64-msvc": "4.41.0", - "@rollup/rollup-win32-ia32-msvc": "4.41.0", - "@rollup/rollup-win32-x64-msvc": "4.41.0", - "fsevents": "~2.3.2" + "node": ">= 10" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/replace-homedir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-2.0.0.tgz", + "integrity": "sha512-bgEuQQ/BHW0XkkJtawzrfzHFSN70f/3cNOiHa2QsYxqrjaC30X1k74FJ6xswVBP0sr0SpGIdVFuPwfrYziVeyw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" + "engines": { + "node": ">= 10.13.0" } }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "node_modules/replacestream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz", + "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==", "dev": true, - "license": "Apache-2.0", + "license": "BSD-3-Clause", "dependencies": { - "tslib": "^2.1.0" + "escape-string-regexp": "^1.0.3", + "object-assign": "^4.0.1", + "readable-stream": "^2.0.2" } }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "node_modules/replacestream/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.8.0" } }, - "node_modules/safe-array-concat/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true, "license": "MIT" }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "engines": { "node": ">= 0.4" @@ -26103,445 +24722,485 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-push-apply/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/safe-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", - "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", "dev": true, "license": "MIT", "dependencies": { - "regexp-tree": "~0.1.1" + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-options": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-2.0.0.tgz", + "integrity": "sha512-/FopbmmFOQCfsCx77BRFdKOniglTiHumLgwvd6IDPihy1GKkadZbgQJBcTb2lMzSR1pndzd96b1nZrreZ7+9/A==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" + "value-or-function": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, + "node": ">= 10.13.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", "dev": true, "license": "MIT" }, - "node_modules/sanitize-html": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.12.1.tgz", - "integrity": "sha512-Plh+JAn0UVDpBRP/xEjsk+xDCoOvMBwQUf/K+/cBAVuTbtX8bj2VB7S1sL1dssVpykqp0/KPSesHrqXtokVBpA==", - "license": "MIT", - "dependencies": { - "deepmerge": "^4.2.2", - "escape-string-regexp": "^4.0.0", - "htmlparser2": "^8.0.0", - "is-plain-object": "^5.0.0", - "parse-srcset": "^1.0.2", - "postcss": "^8.3.11" - } - }, - "node_modules/sanitize-html/node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], + "node_modules/resolve-url-loader": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.5.tgz", + "integrity": "sha512-mgFMCmrV/tA4738EsFmPFE5/MaqSgUMe8LK971kVEKA/RrNVb7+VqFsg/qmKyythf34eyq476qIobP/gfFBGSQ==", + "dev": true, "license": "MIT", "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "adjust-sourcemap-loader": "3.0.0", + "camelcase": "5.3.1", + "compose-function": "3.0.3", + "convert-source-map": "1.7.0", + "es6-iterator": "2.0.3", + "loader-utils": "^1.2.3", + "postcss": "7.0.36", + "rework": "1.0.1", + "rework-visit": "1.0.0", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/sass": { - "version": "1.63.6", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.63.6.tgz", - "integrity": "sha512-MJuxGMHzaOW7ipp+1KdELtqKbfAWbH7OLIdoSMnVe3EXPMTmxTmlaZDCTsgIpPCs3w99lLo9/zDKkOrJuT5byw==", + "node_modules/resolve-url-loader/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=4" } }, - "node_modules/sass-loader": { - "version": "10.5.2", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.5.2.tgz", - "integrity": "sha512-vMUoSNOUKJILHpcNCCyD23X34gve1TS7Rjd9uXHeKqhvBG39x6XbswFDtpbTElj6XdMFezoWhkh5vtKudf2cgQ==", + "node_modules/resolve-url-loader/node_modules/chalk/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "license": "MIT", "dependencies": { - "klona": "^2.0.4", - "loader-utils": "^2.0.0", - "neo-async": "^2.6.2", - "schema-utils": "^3.0.0", - "semver": "^7.3.2" + "has-flag": "^3.0.0" }, "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", - "sass": "^1.3.0", - "webpack": "^4.36.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - } + "node": ">=4" } }, - "node_modules/sass-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/resolve-url-loader/node_modules/convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "safe-buffer": "~5.1.1" } }, - "node_modules/sass-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/resolve-url-loader/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" + "engines": { + "node": ">=0.8.0" } }, - "node_modules/sass-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "node_modules/resolve-url-loader/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "node_modules/sass-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "node_modules/resolve-url-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" + "minimist": "^1.2.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "bin": { + "json5": "lib/cli.js" } }, - "node_modules/sass-loader/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" }, "engines": { - "node": ">=10" + "node": ">=4.0.0" } }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "node_modules/resolve-url-loader/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "xmlchars": "^2.2.0" + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" }, "engines": { - "node": ">=v12.22.7" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "node_modules/resolve-url-loader/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0" + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - }, "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=10" } }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=8" } }, - "node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "node_modules/schema-utils/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/rework": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", + "integrity": "sha512-eEjL8FdkdsxApd0yWVZgBGzfCQiT8yqSc2H1p4jpZpQdtz7ohETiDMoje5PlM8I9WgkqkreVxFUKYOiJdVWDXw==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "convert-source-map": "^0.3.3", + "css": "^2.0.0" } }, - "node_modules/semver-compare": { + "node_modules/rework-visit": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz", + "integrity": "sha512-W6V2fix7nCLUYX1v6eGPrBOZlc03/faqzP4sUxMAJMBMOPYhfV/RyLegTufn5gJKaOITyi+gvf0LXDZ9NzkHnQ==", "dev": true, "license": "MIT" }, - "node_modules/semver-greatest-satisfied-range": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-2.0.0.tgz", - "integrity": "sha512-lH3f6kMbwyANB7HuOWRMlLCa2itaCrZJ+SAqqkSZrZKO/cAsk2EOyaKHUtNkVLFyFW9pct22SFesFp3Z7zpA0g==", + "node_modules/rework/node_modules/convert-source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", + "integrity": "sha512-+4nRk0k3oEpwUB7/CalD7xE2z4VmtEnnq0GO2IPTkrooTrAhEsWvuLF5iWP1dXrwluki/azwXV1ve7gtYuPldg==", "dev": true, - "license": "MIT", - "dependencies": { - "sver": "^1.8.3" - }, - "engines": { - "node": ">= 10.13.0" - } + "license": "MIT" }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "node_modules/rework/node_modules/css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "randombytes": "^2.1.0" + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" } }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "node_modules/rework/node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", "dev": true, "license": "MIT", "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" } }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "license": "ISC", "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": ">= 0.4" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "node_modules/rimraf/node_modules/glob": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz", + "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">= 0.4" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "kind-of": "^6.0.2" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=8" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/rollup": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.0.tgz", + "integrity": "sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==", "dev": true, "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=8" + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.41.0", + "@rollup/rollup-android-arm64": "4.41.0", + "@rollup/rollup-darwin-arm64": "4.41.0", + "@rollup/rollup-darwin-x64": "4.41.0", + "@rollup/rollup-freebsd-arm64": "4.41.0", + "@rollup/rollup-freebsd-x64": "4.41.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.41.0", + "@rollup/rollup-linux-arm-musleabihf": "4.41.0", + "@rollup/rollup-linux-arm64-gnu": "4.41.0", + "@rollup/rollup-linux-arm64-musl": "4.41.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.41.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.41.0", + "@rollup/rollup-linux-riscv64-gnu": "4.41.0", + "@rollup/rollup-linux-riscv64-musl": "4.41.0", + "@rollup/rollup-linux-s390x-gnu": "4.41.0", + "@rollup/rollup-linux-x64-gnu": "4.41.0", + "@rollup/rollup-linux-x64-musl": "4.41.0", + "@rollup/rollup-win32-arm64-msvc": "4.41.0", + "@rollup/rollup-win32-ia32-msvc": "4.41.0", + "@rollup/rollup-win32-x64-msvc": "4.41.0", + "fsevents": "~2.3.2" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "queue-microtask": "^1.2.2" } }, - "node_modules/shell-quote": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", - "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" } }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" }, "engines": { - "node": ">= 0.4" + "node": ">=0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/side-channel-list": { + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-push-apply": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "isarray": "^2.0.5" }, "engines": { "node": ">= 0.4" @@ -26550,37 +25209,33 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", + "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "regexp-tree": "~0.1.1" } }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -26589,724 +25244,812 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "license": "MIT" }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, + "node_modules/sanitize-html": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.12.1.tgz", + "integrity": "sha512-Plh+JAn0UVDpBRP/xEjsk+xDCoOvMBwQUf/K+/cBAVuTbtX8bj2VB7S1sL1dssVpykqp0/KPSesHrqXtokVBpA==", "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^8.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" } }, - "node_modules/slugify": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", - "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", - "dev": true, + "node_modules/sanitize-html/node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/sass": { + "version": "1.63.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.63.6.tgz", + "integrity": "sha512-MJuxGMHzaOW7ipp+1KdELtqKbfAWbH7OLIdoSMnVe3EXPMTmxTmlaZDCTsgIpPCs3w99lLo9/zDKkOrJuT5byw==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, "engines": { - "node": ">=8.0.0" + "node": ">=14.0.0" } }, - "node_modules/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "node_modules/sass-loader": { + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.5.2.tgz", + "integrity": "sha512-vMUoSNOUKJILHpcNCCyD23X34gve1TS7Rjd9uXHeKqhvBG39x6XbswFDtpbTElj6XdMFezoWhkh5vtKudf2cgQ==", + "dev": true, "license": "MIT", "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "webpack": "^4.36.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } } }, - "node_modules/snakecase-keys": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-5.5.0.tgz", - "integrity": "sha512-r3kRtnoPu3FxGJ3fny6PKNnU3pteb29o6qAa0ugzhSseKNWRkw1dw8nIjXMyyKaU9vQxxVIE62Mb3bKbdrgpiw==", + "node_modules/sass-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "license": "MIT", "dependencies": { - "map-obj": "^4.1.0", - "snake-case": "^3.0.4", - "type-fest": "^3.12.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=12" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/snakecase-keys/node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", - "license": "(MIT OR CC0-1.0)", + "node_modules/sass-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/sass-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, "engines": { - "node": ">=14.16" + "node": ">= 10.13.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/sass-loader/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" } }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dev": true, "license": "MIT", "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "loose-envify": "^1.1.0" } }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "deprecated": "See https://github.com/lydell/source-map-url#deprecated", - "dev": true, - "license": "MIT" - }, - "node_modules/sparkles": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-2.1.0.tgz", - "integrity": "sha512-r7iW1bDw8R/cFifrD3JnQJX0K1jqT0kprL48BiBpLZLJPmAm34zsVBsK5lc7HirZYZqMW65dOXZgbAGt/I6frg==", + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", "dev": true, "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, "engines": { - "node": ">= 10.13.0" + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true, - "license": "CC-BY-3.0" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "peerDependencies": { + "ajv": "^6.9.1" } }, - "node_modules/spdx-license-ids": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", - "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "CC0-1.0" + "license": "MIT" }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", - "engines": { - "node": ">= 10.x" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stable-hash": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", - "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", "dev": true, "license": "MIT" }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "node_modules/semver-greatest-satisfied-range": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-2.0.0.tgz", + "integrity": "sha512-lH3f6kMbwyANB7HuOWRMlLCa2itaCrZJ+SAqqkSZrZKO/cAsk2EOyaKHUtNkVLFyFW9pct22SFesFp3Z7zpA0g==", "dev": true, "license": "MIT", "dependencies": { - "escape-string-regexp": "^2.0.0" + "sver": "^1.8.3" }, "engines": { - "node": ">=10" + "node": ">= 10.13.0" } }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" } }, - "node_modules/storybook": { - "version": "8.6.14", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.6.14.tgz", - "integrity": "sha512-sVKbCj/OTx67jhmauhxc2dcr1P+yOgz/x3h0krwjyMgdc5Oubvxyg4NYDZmzAw+ym36g/lzH8N0Ccp4dwtdfxw==", + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core": "8.6.14" - }, - "bin": { - "getstorybook": "bin/index.cjs", - "sb": "bin/index.cjs", - "storybook": "bin/index.cjs" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "prettier": "^2 || ^3" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" }, - "peerDependenciesMeta": { - "prettier": { - "optional": true - } + "engines": { + "node": ">= 0.4" } }, - "node_modules/stream-composer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-composer/-/stream-composer-1.0.2.tgz", - "integrity": "sha512-bnBselmwfX5K10AH6L4c8+S5lgZMWI7ZYrz2rvYjCPB2DIMC4Ig8OpxGpNJSxRZ58oti7y1IcNvjBAz9vW5m4w==", + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "license": "MIT", "dependencies": { - "streamx": "^2.13.2" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/stream-exhaust": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", - "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", - "dev": true, - "license": "MIT" - }, - "node_modules/stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": ">=10.0.0" + "node": ">= 0.4" } }, - "node_modules/streamx": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", - "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, "license": "MIT", "dependencies": { - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" + "kind-of": "^6.0.2" }, - "optionalDependencies": { - "bare-events": "^2.2.0" + "engines": { + "node": ">=8" } }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.0" + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.6.19" + "node": ">=8" } }, - "node_modules/string-convert": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", - "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", - "license": "MIT" - }, - "node_modules/string-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", - "integrity": "sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==", + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", "dev": true, - "license": "CC0-1.0" + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "license": "MIT", "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string-replace-loader": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-replace-loader/-/string-replace-loader-3.1.0.tgz", - "integrity": "sha512-5AOMUZeX5HE/ylKDnEa/KKBqvlnFmRZudSOjVJHxhoJg9QYTwl1rECx7SLR8BBH7tfxb4Rp7EM2XVfQFxIhsbQ==", + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dev": true, "license": "MIT", "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" }, - "peerDependencies": { - "webpack": "^5" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string-replace-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string-replace-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string-replace-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true, "license": "MIT" }, - "node_modules/string-replace-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slugify": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", + "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", "dev": true, "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/snakecase-keys": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-5.5.0.tgz", + "integrity": "sha512-r3kRtnoPu3FxGJ3fny6PKNnU3pteb29o6qAa0ugzhSseKNWRkw1dw8nIjXMyyKaU9vQxxVIE62Mb3bKbdrgpiw==", + "license": "MIT", + "dependencies": { + "map-obj": "^4.1.0", + "snake-case": "^3.0.4", + "type-fest": "^3.12.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">=12" + } + }, + "node_modules/snakecase-keys/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=14.16" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" } }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } }, - "node_modules/string-width/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", "dev": true, "license": "MIT" }, - "node_modules/string.prototype.includes": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", - "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "node_modules/sparkles": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-2.1.0.tgz", + "integrity": "sha512-r7iW1bDw8R/cFifrD3JnQJX0K1jqT0kprL48BiBpLZLJPmAm34zsVBsK5lc7HirZYZqMW65dOXZgbAGt/I6frg==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3" - }, "engines": { - "node": ">= 0.4" + "node": ">= 10.13.0" } }, - "node_modules/string.prototype.matchall": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", - "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "regexp.prototype.flags": "^1.5.3", - "set-function-name": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/string.prototype.padend": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", - "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "license": "CC0-1.0" + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" + "escape-string-regexp": "^2.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "node_modules/storybook": { + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.6.14.tgz", + "integrity": "sha512-sVKbCj/OTx67jhmauhxc2dcr1P+yOgz/x3h0krwjyMgdc5Oubvxyg4NYDZmzAw+ym36g/lzH8N0Ccp4dwtdfxw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "@storybook/core": "8.6.14" }, - "engines": { - "node": ">= 0.4" + "bin": { + "getstorybook": "bin/index.cjs", + "sb": "bin/index.cjs", + "storybook": "bin/index.cjs" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "prettier": "^2 || ^3" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } } }, - "node_modules/stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "node_modules/stream-composer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-composer/-/stream-composer-1.0.2.tgz", + "integrity": "sha512-bnBselmwfX5K10AH6L4c8+S5lgZMWI7ZYrz2rvYjCPB2DIMC4Ig8OpxGpNJSxRZ58oti7y1IcNvjBAz9vW5m4w==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - }, - "engines": { - "node": ">=4" + "streamx": "^2.13.2" } }, - "node_modules/stringify-object/node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "node_modules/stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { - "node": ">=8" + "node": ">=10.0.0" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/streamx": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", + "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" }, - "engines": { - "node": ">=8" + "optionalDependencies": { + "bare-events": "^2.2.0" } }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "safe-buffer": "~5.1.0" } }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=0.6.19" } }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", + "license": "MIT" + }, + "node_modules/string-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", + "integrity": "sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } + "license": "CC0-1.0" }, - "node_modules/strip-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", - "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "license": "MIT", "dependencies": { - "min-indent": "^1.0.1" - }, - "engines": { - "node": ">=12" + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10" } }, - "node_modules/style-inject": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/style-inject/-/style-inject-0.3.0.tgz", - "integrity": "sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==", - "dev": true, - "license": "MIT" - }, - "node_modules/style-loader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", - "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", + "node_modules/string-replace-loader": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-replace-loader/-/string-replace-loader-3.1.0.tgz", + "integrity": "sha512-5AOMUZeX5HE/ylKDnEa/KKBqvlnFmRZudSOjVJHxhoJg9QYTwl1rECx7SLR8BBH7tfxb4Rp7EM2XVfQFxIhsbQ==", "dev": true, "license": "MIT", "dependencies": { "loader-utils": "^2.0.0", "schema-utils": "^3.0.0" }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" + "webpack": "^5" } }, - "node_modules/style-loader/node_modules/ajv": { + "node_modules/string-replace-loader/node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", @@ -27323,7 +26066,7 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/style-loader/node_modules/ajv-keywords": { + "node_modules/string-replace-loader/node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", @@ -27333,14 +26076,14 @@ "ajv": "^6.9.1" } }, - "node_modules/style-loader/node_modules/json-schema-traverse": { + "node_modules/string-replace-loader/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, - "node_modules/style-loader/node_modules/schema-utils": { + "node_modules/string-replace-loader/node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", @@ -27359,364 +26102,273 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/style-search": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", - "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", - "dev": true, - "license": "ISC" - }, - "node_modules/styled-jsx": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", - "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", - "dev": true, - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/stylelint": { - "version": "15.11.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.11.0.tgz", - "integrity": "sha512-78O4c6IswZ9TzpcIiQJIN49K3qNoXTM8zEJzhaTE/xRTCZswaovSEVIa/uwbOltZrk16X4jAxjaOhzz/hTm1Kw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@csstools/css-parser-algorithms": "^2.3.1", - "@csstools/css-tokenizer": "^2.2.0", - "@csstools/media-query-list-parser": "^2.1.4", - "@csstools/selector-specificity": "^3.0.0", - "balanced-match": "^2.0.0", - "colord": "^2.9.3", - "cosmiconfig": "^8.2.0", - "css-functions-list": "^3.2.1", - "css-tree": "^2.3.1", - "debug": "^4.3.4", - "fast-glob": "^3.3.1", - "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^7.0.0", - "global-modules": "^2.0.0", - "globby": "^11.1.0", - "globjoin": "^0.1.4", - "html-tags": "^3.3.1", - "ignore": "^5.2.4", - "import-lazy": "^4.0.0", - "imurmurhash": "^0.1.4", - "is-plain-object": "^5.0.0", - "known-css-properties": "^0.29.0", - "mathml-tag-names": "^2.1.3", - "meow": "^10.1.5", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.28", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.13", - "postcss-value-parser": "^4.2.0", - "resolve-from": "^5.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "style-search": "^0.1.0", - "supports-hyperlinks": "^3.0.0", - "svg-tags": "^1.0.0", - "table": "^6.8.1", - "write-file-atomic": "^5.0.1" - }, - "bin": { - "stylelint": "bin/stylelint.mjs" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/stylelint" - } - }, - "node_modules/stylelint-order": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-6.0.4.tgz", - "integrity": "sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss": "^8.4.32", - "postcss-sorting": "^8.0.2" - }, - "peerDependencies": { - "stylelint": "^14.0.0 || ^15.0.0 || ^16.0.1" - } - }, - "node_modules/stylelint-prettier": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/stylelint-prettier/-/stylelint-prettier-4.1.0.tgz", - "integrity": "sha512-dd653q/d1IfvsSQshz1uAMe+XDm6hfM/7XiFH0htYY8Lse/s5ERTg7SURQehZPwVvm/rs7AsFhda9EQ2E9TS0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": "^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "prettier": ">=3.0.0", - "stylelint": ">=15.8.0" - } - }, - "node_modules/stylelint-scss": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-5.3.2.tgz", - "integrity": "sha512-4LzLaayFhFyneJwLo0IUa8knuIvj+zF0vBFueQs4e3tEaAMIQX8q5th8ziKkgOavr6y/y9yoBe+RXN/edwLzsQ==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { - "known-css-properties": "^0.29.0", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-selector-parser": "^6.0.13", - "postcss-value-parser": "^4.2.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, - "peerDependencies": { - "stylelint": "^14.5.1 || ^15.0.0" + "engines": { + "node": ">=8" } }, - "node_modules/stylelint-scss/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/stylelint/node_modules/balanced-match": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", - "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "license": "MIT" }, - "node_modules/stylelint/node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", "dev": true, "license": "MIT", "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">= 0.4" } }, - "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-7.0.2.tgz", - "integrity": "sha512-TfW7/1iI4Cy7Y8L6iqNdZQVvdXn0f8B4QcIXmkIbtTIe/Okm/nSlHb4IwGzRVOd3WfSieCgvf5cMzEfySAIl0g==", + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^3.2.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { - "node": ">=12.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/stylelint/node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "node_modules/string.prototype.padend": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", "dev": true, "license": "MIT", "dependencies": { - "global-prefix": "^3.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/stylelint/node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, "license": "MIT", "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" } }, - "node_modules/stylelint/node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/stylelint/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": ">= 4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/stylelint/node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/stylelint/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC" - }, - "node_modules/stylelint/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", "dev": true, - "license": "ISC", + "license": "BSD-2-Clause", "dependencies": { - "yallist": "^4.0.0" + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/stylelint/node_modules/meow": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.5.tgz", - "integrity": "sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==", + "node_modules/stringify-object/node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", "dev": true, "license": "MIT", - "dependencies": { - "@types/minimist": "^1.2.2", - "camelcase-keys": "^7.0.0", - "decamelize": "^5.0.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.2", - "read-pkg-up": "^8.0.0", - "redent": "^4.0.0", - "trim-newlines": "^4.0.2", - "type-fest": "^1.2.2", - "yargs-parser": "^20.2.9" - }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/stylelint/node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/stylelint/node_modules/path-type": { + "node_modules/strip-bom": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/stylelint/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", "dev": true, "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/stylelint/node_modules/redent": { + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", - "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", + "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", "dev": true, "license": "MIT", "dependencies": { - "indent-string": "^5.0.0", - "strip-indent": "^4.0.0" + "min-indent": "^1.0.1" }, "engines": { "node": ">=12" @@ -27725,2241 +26377,2383 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stylelint/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stylelint/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "license": "ISC", + "license": "MIT", "engines": { - "node": ">=14" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stylelint/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "node_modules/style-inject": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/style-inject/-/style-inject-0.3.0.tgz", + "integrity": "sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==", "dev": true, - "license": "(MIT OR CC0-1.0)", + "license": "MIT" + }, + "node_modules/style-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", + "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, "engines": { - "node": ">=10" + "node": ">= 10.13.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/stylelint/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/style-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "bin": { - "which": "bin/which" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/stylelint/node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "node_modules/style-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" } }, - "node_modules/stylelint/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "node_modules/style-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/stylelint/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "node_modules/style-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, "engines": { - "node": ">=10" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "node_modules/style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", "dev": true, - "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "client-only": "0.0.1" }, "engines": { - "node": ">=8" + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } } }, - "node_modules/supports-hyperlinks": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", - "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "node_modules/stylelint": { + "version": "15.11.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.11.0.tgz", + "integrity": "sha512-78O4c6IswZ9TzpcIiQJIN49K3qNoXTM8zEJzhaTE/xRTCZswaovSEVIa/uwbOltZrk16X4jAxjaOhzz/hTm1Kw==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" + "@csstools/css-parser-algorithms": "^2.3.1", + "@csstools/css-tokenizer": "^2.2.0", + "@csstools/media-query-list-parser": "^2.1.4", + "@csstools/selector-specificity": "^3.0.0", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^8.2.0", + "css-functions-list": "^3.2.1", + "css-tree": "^2.3.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.1", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^7.0.0", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.3.1", + "ignore": "^5.2.4", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.29.0", + "mathml-tag-names": "^2.1.3", + "meow": "^10.1.5", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.28", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.0.13", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "style-search": "^0.1.0", + "supports-hyperlinks": "^3.0.0", + "svg-tags": "^1.0.0", + "table": "^6.8.1", + "write-file-atomic": "^5.0.1" }, - "engines": { - "node": ">=14.18" + "bin": { + "stylelint": "bin/stylelint.mjs" }, - "funding": { - "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", "engines": { - "node": ">= 0.4" + "node": "^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/stylelint" } }, - "node_modules/sver": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/sver/-/sver-1.8.4.tgz", - "integrity": "sha512-71o1zfzyawLfIWBOmw8brleKyvnbn73oVHNCsu51uPMz/HWiKkkXsI31JjHW5zqXEqnPYkIiHd8ZmL7FCimLEA==", + "node_modules/stylelint-order": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-6.0.4.tgz", + "integrity": "sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA==", "dev": true, "license": "MIT", - "optionalDependencies": { - "semver": "^6.3.0" + "dependencies": { + "postcss": "^8.4.32", + "postcss-sorting": "^8.0.2" + }, + "peerDependencies": { + "stylelint": "^14.0.0 || ^15.0.0 || ^16.0.1" } }, - "node_modules/svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/svg-tags": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", - "dev": true - }, - "node_modules/svgo": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", - "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "node_modules/stylelint-prettier": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/stylelint-prettier/-/stylelint-prettier-4.1.0.tgz", + "integrity": "sha512-dd653q/d1IfvsSQshz1uAMe+XDm6hfM/7XiFH0htYY8Lse/s5ERTg7SURQehZPwVvm/rs7AsFhda9EQ2E9TS0g==", "dev": true, "license": "MIT", "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.3.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.0.0" - }, - "bin": { - "svgo": "bin/svgo" + "prettier-linter-helpers": "^1.0.0" }, "engines": { - "node": ">=14.0.0" + "node": "^14.17.0 || >=16.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" + "peerDependencies": { + "prettier": ">=3.0.0", + "stylelint": ">=15.8.0" } }, - "node_modules/svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "node_modules/stylelint-scss": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-5.3.2.tgz", + "integrity": "sha512-4LzLaayFhFyneJwLo0IUa8knuIvj+zF0vBFueQs4e3tEaAMIQX8q5th8ziKkgOavr6y/y9yoBe+RXN/edwLzsQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 10" + "dependencies": { + "known-css-properties": "^0.29.0", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-selector-parser": "^6.0.13", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "stylelint": "^14.5.1 || ^15.0.0" } }, - "node_modules/swiper": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/swiper/-/swiper-10.3.1.tgz", - "integrity": "sha512-24Wk3YUdZHxjc9faID97GTu6xnLNia+adMt6qMTZG/HgdSUt4fS0REsGUXJOgpTED0Amh/j+gRGQxsLayJUlBQ==", - "funding": [ - { - "type": "patreon", - "url": "https://www.patreon.com/swiperjs" - }, - { - "type": "open_collective", - "url": "http://opencollective.com/swiper" - } - ], + "node_modules/stylelint-scss/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, "engines": { - "node": ">= 4.7.0" + "node": ">=4" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", "dev": true, "license": "MIT" }, - "node_modules/synckit": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.6.tgz", - "integrity": "sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==", + "node_modules/stylelint/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, "license": "MIT", "dependencies": { - "@pkgr/core": "^0.2.4" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": ">=14" }, "funding": { - "url": "https://opencollective.com/synckit" + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/tabbable": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", - "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", - "dev": true, - "license": "MIT" - }, - "node_modules/table": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", - "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-7.0.2.tgz", + "integrity": "sha512-TfW7/1iI4Cy7Y8L6iqNdZQVvdXn0f8B4QcIXmkIbtTIe/Okm/nSlHb4IwGzRVOd3WfSieCgvf5cMzEfySAIl0g==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" + "flat-cache": "^3.2.0" }, "engines": { - "node": ">=10.0.0" + "node": ">=12.0.0" } }, - "node_modules/table/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/stylelint/node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "global-prefix": "^3.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=6" } }, - "node_modules/table/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/stylelint/node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=6" } }, - "node_modules/table/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/table/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "node_modules/stylelint/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "lru-cache": "^6.0.0" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "node_modules/stylelint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 4" } }, - "node_modules/teex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", - "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "node_modules/stylelint/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true, "license": "MIT", - "dependencies": { - "streamx": "^2.12.5" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", - "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", + "node_modules/stylelint/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true, - "license": "BSD-2-Clause", + "license": "ISC" + }, + "node_modules/stylelint/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", "dependencies": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - }, - "bin": { - "terser": "bin/terser" + "yallist": "^4.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=10" } }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "node_modules/stylelint/node_modules/meow": { + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.5.tgz", + "integrity": "sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" + "@types/minimist": "^1.2.2", + "camelcase-keys": "^7.0.0", + "decamelize": "^5.0.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.2", + "read-pkg-up": "^8.0.0", + "redent": "^4.0.0", + "trim-newlines": "^4.0.2", + "type-fest": "^1.2.2", + "yargs-parser": "^20.2.9" }, "engines": { - "node": ">= 10.13.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser-webpack-plugin/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/terser-webpack-plugin/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "node_modules/stylelint/node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" }, "engines": { - "node": ">= 10.13.0" + "node": ">=10" } }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "node_modules/stylelint/node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=8" } }, - "node_modules/terser-webpack-plugin/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/stylelint/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "license": "MIT", "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" } }, - "node_modules/terser-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/stylelint/node_modules/redent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", + "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "indent-string": "^5.0.0", + "strip-indent": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser-webpack-plugin/node_modules/terser": { - "version": "5.39.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.2.tgz", - "integrity": "sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==", + "node_modules/stylelint/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.14.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, + "license": "ISC", "bin": { - "terser": "bin/terser" + "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "node_modules/stylelint/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "engines": { + "node": ">=14" }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/stylelint/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/stylelint/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/stylelint/node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" }, "engines": { - "node": "*" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "node_modules/stylelint/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } + "license": "ISC" }, - "node_modules/text-extensions": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", - "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "node_modules/stylelint/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, - "license": "MIT", + "license": "ISC", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", "dev": true, "license": "MIT" }, - "node_modules/textextensions": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-3.3.0.tgz", - "integrity": "sha512-mk82dS8eRABNbeVJrEiN5/UMSCliINAuz8mkUwH4SwslkNP//gbEzlWNS5au0z5Dpx40SQxzqZevZkn+WYJ9Dw==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://bevry.me/fund" } }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", "dev": true, "license": "MIT", "dependencies": { - "readable-stream": "3" + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" } }, - "node_modules/through2-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", - "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "license": "MIT", - "dependencies": { - "through2": "~2.0.0", - "xtend": "~4.0.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/through2-filter/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "node_modules/sver": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/sver/-/sver-1.8.4.tgz", + "integrity": "sha512-71o1zfzyawLfIWBOmw8brleKyvnbn73oVHNCsu51uPMz/HWiKkkXsI31JjHW5zqXEqnPYkIiHd8ZmL7FCimLEA==", "dev": true, "license": "MIT", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "optionalDependencies": { + "semver": "^6.3.0" } }, - "node_modules/through2/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/svgo": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" }, "engines": { - "node": ">= 6" + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" } }, - "node_modules/timers-ext": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", - "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.64", - "next-tick": "^1.1.0" - }, + "license": "MIT", "engines": { - "node": ">=0.12" + "node": ">= 10" } }, - "node_modules/timers-ext/node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "dev": true, - "hasInstallScript": true, - "license": "ISC", - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, + "node_modules/swiper": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-10.3.1.tgz", + "integrity": "sha512-24Wk3YUdZHxjc9faID97GTu6xnLNia+adMt6qMTZG/HgdSUt4fS0REsGUXJOgpTED0Amh/j+gRGQxsLayJUlBQ==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/swiperjs" + }, + { + "type": "open_collective", + "url": "http://opencollective.com/swiper" + } + ], "engines": { - "node": ">=0.10" + "node": ">= 4.7.0" } }, - "node_modules/timers-ext/node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", - "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true, "license": "MIT" }, - "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "node_modules/synckit": { + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.6.tgz", + "integrity": "sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" + "@pkgr/core": "^0.2.4" }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", "engines": { - "node": ">=12" + "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://opencollective.com/synckit" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", "dev": true, - "license": "BSD-3-Clause" + "license": "MIT" }, - "node_modules/to-absolute-glob": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", - "integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==", + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "is-absolute": "^1.0.0", - "is-negated-glob": "^1.0.0" + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.0.0" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/table/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { - "is-number": "^7.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=8.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/to-through": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/to-through/-/to-through-3.0.0.tgz", - "integrity": "sha512-y8MN937s/HVhEoBU1SxfHC+wxCHkV1a9gW8eAdTadYh/bGyesZIVcbjI+mSpFbSVwQici/XjBjuUyri1dnXwBw==", + "node_modules/table/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { - "streamx": "^2.12.5" + "color-name": "~1.1.4" }, "engines": { - "node": ">=10.13.0" + "node": ">=7.0.0" } }, - "node_modules/toggle-selection": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", - "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "node_modules/table/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, - "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "node_modules/table/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">=6" } }, - "node_modules/tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", "dev": true, "license": "MIT", "dependencies": { - "punycode": "^2.1.1" + "streamx": "^2.12.5" + } + }, + "node_modules/terser": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", + "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "bin": { + "terser": "bin/terser" }, "engines": { - "node": ">=12" + "node": ">=6.0.0" } }, - "node_modules/trim-newlines": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", - "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==", + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", "dev": true, "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, "engines": { - "node": ">=12" + "node": ">= 10.13.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } } }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "node_modules/terser-webpack-plugin/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" } }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", "dev": true, "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, "engines": { - "node": ">=18.12" + "node": ">= 10.13.0" }, - "peerDependencies": { - "typescript": ">=4.8.4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/ts-dedent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", - "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "node_modules/terser-webpack-plugin/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6.10" + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/ts-jest": { - "version": "29.3.4", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.4.tgz", - "integrity": "sha512-Iqbrm8IXOmV+ggWHOTEbjwyCf2xZlUMv5npExksXohL+tk8va4Fjhb+X2+Rt9NBmgO7bJ8WpnMLOwih/DnMlFA==", + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", "dependencies": { - "bs-logger": "^0.2.6", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "^2.1.0", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.2", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" + "has-flag": "^4.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + "node": ">=10" }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/terser": { + "version": "5.39.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.2.tgz", + "integrity": "sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.14.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" } }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=6" + "node": "*" } }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "node_modules/textextensions": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-3.3.0.tgz", + "integrity": "sha512-mk82dS8eRABNbeVJrEiN5/UMSCliINAuz8mkUwH4SwslkNP//gbEzlWNS5au0z5Dpx40SQxzqZevZkn+WYJ9Dw==", "dev": true, "license": "MIT", - "dependencies": { - "tslib": "^1.8.1" - }, "engines": { - "node": ">= 6" + "node": ">=8" }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + "funding": { + "url": "https://bevry.me/fund" } }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true, - "license": "0BSD" + "license": "MIT" }, - "node_modules/type": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", - "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "dev": true, "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" + "through2": "~2.0.0", + "xtend": "~4.0.0" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "node_modules/through2-filter/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=4" + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" } }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "node_modules/through2/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 6" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "node_modules/timers-ext": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", + "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" + "es5-ext": "^0.10.64", + "next-tick": "^1.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=0.12" } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "node_modules/timers-ext/node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "dev": true, - "license": "MIT", + "hasInstallScript": true, + "license": "ISC", "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10" } }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "node_modules/timers-ext/node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", + "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", "dev": true, "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" + "fdir": "^6.4.4", + "picomatch": "^4.0.2" }, "engines": { - "node": ">= 0.4" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" }, "engines": { - "node": ">=14.17" + "node": ">=8.0" } }, - "node_modules/typograf": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/typograf/-/typograf-7.4.4.tgz", - "integrity": "sha512-/DonkfuPyTMAZMweHfBdB8S8B4tSjc7gXiADs47lloPS2GHVlN+IJypyOqvRkpszfwCjnWv+3q59MUY6BhVcHA==", + "node_modules/to-through": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-3.0.0.tgz", + "integrity": "sha512-y8MN937s/HVhEoBU1SxfHC+wxCHkV1a9gW8eAdTadYh/bGyesZIVcbjI+mSpFbSVwQici/XjBjuUyri1dnXwBw==", + "dev": true, "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + }, "engines": { - "node": ">= 4" + "node": ">=10.13.0" } }, - "node_modules/uc.micro": { + "node_modules/toggle-selection": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", "dev": true, "license": "MIT" }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "node_modules/unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 4.0.0" } }, - "node_modules/undertaker": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-2.0.0.tgz", - "integrity": "sha512-tO/bf30wBbTsJ7go80j0RzA2rcwX6o7XPBpeFcb+jzoeb4pfMM2zUeSDIkY1AWqeZabWxaQZ/h8N9t35QKDLPQ==", + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", "dev": true, "license": "MIT", "dependencies": { - "bach": "^2.0.1", - "fast-levenshtein": "^3.0.0", - "last-run": "^2.0.0", - "undertaker-registry": "^2.0.0" + "punycode": "^2.1.1" }, "engines": { - "node": ">=10.13.0" + "node": ">=12" } }, - "node_modules/undertaker-registry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-2.0.0.tgz", - "integrity": "sha512-+hhVICbnp+rlzZMgxXenpvTxpuvA67Bfgtt+O9WOE5jo7w/dyiF1VmoZVIHvP2EkUjsyKyTwYKlLhA+j47m1Ew==", + "node_modules/trim-newlines": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", + "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 10.13.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/undertaker/node_modules/fast-levenshtein": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", - "integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==", + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", "dev": true, "license": "MIT", - "dependencies": { - "fastest-levenshtein": "^1.0.7" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/undici": { - "version": "6.21.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", - "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=18.17" + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" } }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=6.10" + } }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "node_modules/ts-jest": { + "version": "29.3.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.4.tgz", + "integrity": "sha512-Iqbrm8IXOmV+ggWHOTEbjwyCf2xZlUMv5npExksXohL+tk8va4Fjhb+X2+Rt9NBmgO7bJ8WpnMLOwih/DnMlFA==", "dev": true, "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.2", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } } }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "node_modules/ts-jest/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, "license": "MIT", "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, - "node_modules/unicorn-magic": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", - "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, "engines": { - "node": ">=18" + "node": ">= 6" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" + "prelude-ls": "^1.2.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/unique-stream": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", - "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, "license": "MIT", - "dependencies": { - "json-stable-stringify-without-jsonify": "^1.0.1", - "through2-filter": "^3.0.0" + "engines": { + "node": ">=4" } }, - "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, "license": "MIT", "dependencies": { - "@types/unist": "^3.0.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">= 0.4" } }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, "license": "MIT", "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/universal-cookie": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.2.2.tgz", - "integrity": "sha512-fMiOcS3TmzP2x5QV26pIH3mvhexLIT0HmPa3V7Q7knRfT9HG6kTwq02HZGLPw0sAOXrAmotElGRvTLCMbJsvxQ==", - "license": "MIT", - "dependencies": { - "@types/cookie": "^0.6.0", - "cookie": "^0.7.2" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, "engines": { - "node": ">= 10.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unplugin": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz", - "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==", + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.14.0", - "webpack-virtual-modules": "^0.6.2" + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.0.0" + "node": ">=14.17" } }, - "node_modules/unrs-resolver": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.7.2.tgz", - "integrity": "sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "napi-postinstall": "^0.2.2" - }, - "funding": { - "url": "https://github.com/sponsors/JounQin" - }, - "optionalDependencies": { - "@unrs/resolver-binding-darwin-arm64": "1.7.2", - "@unrs/resolver-binding-darwin-x64": "1.7.2", - "@unrs/resolver-binding-freebsd-x64": "1.7.2", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.7.2", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.7.2", - "@unrs/resolver-binding-linux-arm64-gnu": "1.7.2", - "@unrs/resolver-binding-linux-arm64-musl": "1.7.2", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.7.2", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.7.2", - "@unrs/resolver-binding-linux-riscv64-musl": "1.7.2", - "@unrs/resolver-binding-linux-s390x-gnu": "1.7.2", - "@unrs/resolver-binding-linux-x64-gnu": "1.7.2", - "@unrs/resolver-binding-linux-x64-musl": "1.7.2", - "@unrs/resolver-binding-wasm32-wasi": "1.7.2", - "@unrs/resolver-binding-win32-arm64-msvc": "1.7.2", - "@unrs/resolver-binding-win32-ia32-msvc": "1.7.2", - "@unrs/resolver-binding-win32-x64-msvc": "1.7.2" + "node_modules/typograf": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/typograf/-/typograf-7.4.4.tgz", + "integrity": "sha512-/DonkfuPyTMAZMweHfBdB8S8B4tSjc7gXiADs47lloPS2GHVlN+IJypyOqvRkpszfwCjnWv+3q59MUY6BhVcHA==", + "license": "MIT", + "engines": { + "node": ">= 4" } }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, + "node_modules/ua-parser-js": { + "version": "1.0.41", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz", + "integrity": "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==", "funding": [ { "type": "opencollective", - "url": "https://opencollective.com/browserslist" + "url": "https://opencollective.com/ua-parser-js" }, { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" + "type": "paypal", + "url": "https://paypal.me/faisalman" }, { "type": "github", - "url": "https://github.com/sponsors/ai" + "url": "https://github.com/sponsors/faisalman" } ], "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, "bin": { - "update-browserslist-db": "cli.js" + "ua-parser-js": "script/cli.js" }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" + "engines": { + "node": "*" } }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", - "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "dev": true, "license": "MIT" }, - "node_modules/url": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", - "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "license": "MIT", "dependencies": { - "punycode": "^1.4.1", - "qs": "^6.12.3" + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "node_modules/unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", "dev": true, "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/url/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/use-isomorphic-layout-effect": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", - "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", + "node_modules/undertaker": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-2.0.0.tgz", + "integrity": "sha512-tO/bf30wBbTsJ7go80j0RzA2rcwX6o7XPBpeFcb+jzoeb4pfMM2zUeSDIkY1AWqeZabWxaQZ/h8N9t35QKDLPQ==", "dev": true, "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "dependencies": { + "bach": "^2.0.1", + "fast-levenshtein": "^3.0.0", + "last-run": "^2.0.0", + "undertaker-registry": "^2.0.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">=10.13.0" } }, - "node_modules/use-memo-one": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", - "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", + "node_modules/undertaker-registry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-2.0.0.tgz", + "integrity": "sha512-+hhVICbnp+rlzZMgxXenpvTxpuvA67Bfgtt+O9WOE5jo7w/dyiF1VmoZVIHvP2EkUjsyKyTwYKlLhA+j47m1Ew==", "dev": true, "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "engines": { + "node": ">= 10.13.0" } }, - "node_modules/use-sync-external-store": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", - "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "node_modules/undertaker/node_modules/fast-levenshtein": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", + "integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fastest-levenshtein": "^1.0.7" } }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "node_modules/undici": { + "version": "6.21.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", + "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", "dev": true, "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" + "engines": { + "node": ">=18.17" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "node_modules/utility-types": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", - "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, "engines": { - "node": ">= 4" + "node": ">=4" } }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "engines": { + "node": ">=4" } }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, + "license": "MIT", "engines": { - "node": ">=10.12.0" + "node": ">=4" } }, - "node_modules/v8flags": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-4.0.1.tgz", - "integrity": "sha512-fcRLaS4H/hrZk9hYwbdRM35D0U8IYMfEClhXxCivOojl+yTRAZH3Zy2sSy6qVCiGbV9YAtPssP6jaChqC9vPCg==", + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 10.13.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/value-or-function": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-4.0.0.tgz", - "integrity": "sha512-aeVK81SIuT6aMJfNo9Vte8Dw0/FZINGBV8BfCraGtqVxIeLAEhJyoWs8SmvRVmXfGss2PmmOwZCuBPbZR+IYWg==", + "node_modules/unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 10.13.0" + "dependencies": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" } }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", "dev": true, "license": "MIT", "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" + "@types/unist": "^3.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/vfile-message": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", - "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" + "@types/unist": "^3.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/vinyl": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.0.tgz", - "integrity": "sha512-rC2VRfAVVCGEgjnxHUnpIVh3AGuk62rP3tqVrn+yab0YH7UULisC085+NYH+mnqf3Wx4SpSi1RQMwudL89N03g==", + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", "dev": true, "license": "MIT", "dependencies": { - "clone": "^2.1.2", - "clone-stats": "^1.0.0", - "remove-trailing-separator": "^1.1.0", - "replace-ext": "^2.0.0", - "teex": "^1.0.1" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" }, - "engines": { - "node": ">=10.13.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/vinyl-contents": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/vinyl-contents/-/vinyl-contents-2.0.0.tgz", - "integrity": "sha512-cHq6NnGyi2pZ7xwdHSW1v4Jfnho4TEGtxZHw01cmnc8+i7jgR6bRnED/LbrKan/Q7CvVLbnvA5OepnhbpjBZ5Q==", + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", "dev": true, "license": "MIT", "dependencies": { - "bl": "^5.0.0", - "vinyl": "^3.0.0" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" }, - "engines": { - "node": ">=10.13.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/vinyl-fs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.0.tgz", - "integrity": "sha512-7GbgBnYfaquMk3Qu9g22x000vbYkOex32930rBnc3qByw6HfMEAoELjCjoJv4HuEQxHAurT+nvMHm6MnJllFLw==", - "dev": true, + "node_modules/universal-cookie": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.2.2.tgz", + "integrity": "sha512-fMiOcS3TmzP2x5QV26pIH3mvhexLIT0HmPa3V7Q7knRfT9HG6kTwq02HZGLPw0sAOXrAmotElGRvTLCMbJsvxQ==", "license": "MIT", "dependencies": { - "fs-mkdirp-stream": "^2.0.1", - "glob-stream": "^8.0.0", - "graceful-fs": "^4.2.11", - "iconv-lite": "^0.6.3", - "is-valid-glob": "^1.0.0", - "lead": "^4.0.0", - "normalize-path": "3.0.0", - "resolve-options": "^2.0.0", - "stream-composer": "^1.0.2", - "streamx": "^2.14.0", - "to-through": "^3.0.0", - "value-or-function": "^4.0.0", - "vinyl": "^3.0.0", - "vinyl-sourcemap": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" + "@types/cookie": "^0.6.0", + "cookie": "^0.7.2" } }, - "node_modules/vinyl-sourcemap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-2.0.0.tgz", - "integrity": "sha512-BAEvWxbBUXvlNoFQVFVHpybBbjW1r03WhohJzJDSfgrrK5xVYIDTan6xN14DlyImShgDRv2gl9qhM6irVMsV0Q==", + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", - "dependencies": { - "convert-source-map": "^2.0.0", - "graceful-fs": "^4.2.10", - "now-and-later": "^3.0.0", - "streamx": "^2.12.5", - "vinyl": "^3.0.0", - "vinyl-contents": "^2.0.0" - }, "engines": { - "node": ">=10.13.0" + "node": ">= 10.0.0" } }, - "node_modules/vinyl-sourcemaps-apply": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", - "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", + "node_modules/unplugin": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz", + "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "source-map": "^0.5.1" - } - }, - "node_modules/vinyl-sourcemaps-apply/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true, - "license": "BSD-3-Clause", + "acorn": "^8.14.0", + "webpack-virtual-modules": "^0.6.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=14.0.0" } }, - "node_modules/vite": { - "version": "5.4.19", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", - "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "node_modules/unrs-resolver": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.7.2.tgz", + "integrity": "sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==", "dev": true, + "hasInstallScript": true, "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" + "napi-postinstall": "^0.2.2" }, "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" + "url": "https://github.com/sponsors/JounQin" }, "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true + "@unrs/resolver-binding-darwin-arm64": "1.7.2", + "@unrs/resolver-binding-darwin-x64": "1.7.2", + "@unrs/resolver-binding-freebsd-x64": "1.7.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.7.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.7.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.7.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.7.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.7.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-x64-musl": "1.7.2", + "@unrs/resolver-binding-wasm32-wasi": "1.7.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.7.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.7.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.7.2" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" }, - "sugarss": { - "optional": true + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" }, - "terser": { - "optional": true + { + "type": "github", + "url": "https://github.com/sponsors/ai" } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/vite-plugin-commonjs": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/vite-plugin-commonjs/-/vite-plugin-commonjs-0.10.4.tgz", - "integrity": "sha512-eWQuvQKCcx0QYB5e5xfxBNjQKyrjEWZIR9UOkOV6JAgxVhtbZvCOF+FNC2ZijBJ3U3Px04ZMMyyMyFBVWIJ5+g==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true, + "license": "MIT" + }, + "node_modules/url": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.12.1", - "magic-string": "^0.30.11", - "vite-plugin-dynamic-import": "^1.6.0" + "punycode": "^1.4.1", + "qs": "^6.12.3" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/vite-plugin-dynamic-import": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vite-plugin-dynamic-import/-/vite-plugin-dynamic-import-1.6.0.tgz", - "integrity": "sha512-TM0sz70wfzTIo9YCxVFwS8OA9lNREsh+0vMHGSkWDTZ7bgd1Yjs5RV8EgB634l/91IsXJReg0xtmuQqP0mf+rg==", + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.12.1", - "es-module-lexer": "^1.5.4", - "fast-glob": "^3.3.2", - "magic-string": "^0.30.11" + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" } }, - "node_modules/vite-plugin-svgr": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.3.0.tgz", - "integrity": "sha512-Jy9qLB2/PyWklpYy0xk0UU3TlU0t2UMpJXZvf+hWII1lAmRHrOUKi11Uw8N3rxoNk7atZNYO3pR3vI1f7oi+6w==", + "node_modules/url/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", "dev": true, + "license": "MIT" + }, + "node_modules/use-composed-ref": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz", + "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==", "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.1.3", - "@svgr/core": "^8.1.0", - "@svgr/plugin-jsx": "^8.1.0" - }, "peerDependencies": { - "vite": ">=2.6.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", + "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, + "node_modules/use-latest": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz", + "integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==", "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" + "dependencies": { + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], + "node_modules/use-memo-one": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", + "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], + "node_modules/use-sync-external-store": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" } }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", "dev": true, + "license": "MIT" + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">=12" + "node": ">= 4" } }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" ], - "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, "engines": { - "node": ">=12" + "node": ">=10.12.0" } }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], + "node_modules/v8flags": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-4.0.1.tgz", + "integrity": "sha512-fcRLaS4H/hrZk9hYwbdRM35D0U8IYMfEClhXxCivOojl+yTRAZH3Zy2sSy6qVCiGbV9YAtPssP6jaChqC9vPCg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], "engines": { - "node": ">=12" + "node": ">= 10.13.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], + "node_modules/value-or-function": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-4.0.0.tgz", + "integrity": "sha512-aeVK81SIuT6aMJfNo9Vte8Dw0/FZINGBV8BfCraGtqVxIeLAEhJyoWs8SmvRVmXfGss2PmmOwZCuBPbZR+IYWg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=12" + "node": ">= 10.13.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], + "node_modules/vinyl": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.0.tgz", + "integrity": "sha512-rC2VRfAVVCGEgjnxHUnpIVh3AGuk62rP3tqVrn+yab0YH7UULisC085+NYH+mnqf3Wx4SpSi1RQMwudL89N03g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "clone": "^2.1.2", + "clone-stats": "^1.0.0", + "remove-trailing-separator": "^1.1.0", + "replace-ext": "^2.0.0", + "teex": "^1.0.1" + }, "engines": { - "node": ">=12" + "node": ">=10.13.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], + "node_modules/vinyl-contents": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-contents/-/vinyl-contents-2.0.0.tgz", + "integrity": "sha512-cHq6NnGyi2pZ7xwdHSW1v4Jfnho4TEGtxZHw01cmnc8+i7jgR6bRnED/LbrKan/Q7CvVLbnvA5OepnhbpjBZ5Q==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "bl": "^5.0.0", + "vinyl": "^3.0.0" + }, "engines": { - "node": ">=12" + "node": ">=10.13.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], + "node_modules/vinyl-fs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.0.tgz", + "integrity": "sha512-7GbgBnYfaquMk3Qu9g22x000vbYkOex32930rBnc3qByw6HfMEAoELjCjoJv4HuEQxHAurT+nvMHm6MnJllFLw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "fs-mkdirp-stream": "^2.0.1", + "glob-stream": "^8.0.0", + "graceful-fs": "^4.2.11", + "iconv-lite": "^0.6.3", + "is-valid-glob": "^1.0.0", + "lead": "^4.0.0", + "normalize-path": "3.0.0", + "resolve-options": "^2.0.0", + "stream-composer": "^1.0.2", + "streamx": "^2.14.0", + "to-through": "^3.0.0", + "value-or-function": "^4.0.0", + "vinyl": "^3.0.0", + "vinyl-sourcemap": "^2.0.0" + }, "engines": { - "node": ">=12" + "node": ">=10.13.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], + "node_modules/vinyl-sourcemap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-2.0.0.tgz", + "integrity": "sha512-BAEvWxbBUXvlNoFQVFVHpybBbjW1r03WhohJzJDSfgrrK5xVYIDTan6xN14DlyImShgDRv2gl9qhM6irVMsV0Q==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "convert-source-map": "^2.0.0", + "graceful-fs": "^4.2.10", + "now-and-later": "^3.0.0", + "streamx": "^2.12.5", + "vinyl": "^3.0.0", + "vinyl-contents": "^2.0.0" + }, "engines": { - "node": ">=12" + "node": ">=10.13.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], + "node_modules/vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "license": "ISC", + "dependencies": { + "source-map": "^0.5.1" } }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], + "node_modules/vinyl-sourcemaps-apply/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "license": "BSD-3-Clause", "engines": { - "node": ">=12" + "node": ">=0.10.0" } }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], + "node_modules/vite": { + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, "engines": { - "node": ">=12" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } } }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], + "node_modules/vite-plugin-commonjs": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/vite-plugin-commonjs/-/vite-plugin-commonjs-0.10.4.tgz", + "integrity": "sha512-eWQuvQKCcx0QYB5e5xfxBNjQKyrjEWZIR9UOkOV6JAgxVhtbZvCOF+FNC2ZijBJ3U3Px04ZMMyyMyFBVWIJ5+g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" + "dependencies": { + "acorn": "^8.12.1", + "magic-string": "^0.30.11", + "vite-plugin-dynamic-import": "^1.6.0" } }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], + "node_modules/vite-plugin-dynamic-import": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-plugin-dynamic-import/-/vite-plugin-dynamic-import-1.6.0.tgz", + "integrity": "sha512-TM0sz70wfzTIo9YCxVFwS8OA9lNREsh+0vMHGSkWDTZ7bgd1Yjs5RV8EgB634l/91IsXJReg0xtmuQqP0mf+rg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "dependencies": { + "acorn": "^8.12.1", + "es-module-lexer": "^1.5.4", + "fast-glob": "^3.3.2", + "magic-string": "^0.30.11" } }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], + "node_modules/vite-plugin-svgr": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.3.0.tgz", + "integrity": "sha512-Jy9qLB2/PyWklpYy0xk0UU3TlU0t2UMpJXZvf+hWII1lAmRHrOUKi11Uw8N3rxoNk7atZNYO3pR3vI1f7oi+6w==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@rollup/pluginutils": "^5.1.3", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" + }, + "peerDependencies": { + "vite": ">=2.6.0" } }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" + "darwin" ], "engines": { "node": ">=12" diff --git a/package.json b/package.json index e789b008d5..51c9513bae 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "@react-spring/web": "^9.7.3", "ajv": "^8.12.0", "ajv-keywords": "^5.1.0", + "deep-object-diff": "^1.1.9", "final-form": "^4.20.9", "github-buttons": "2.23.0", "immutable": "^4.3.7", @@ -118,6 +119,7 @@ "lodash": "^4.17.21", "monaco-editor": "^0.52.2", "react-final-form": "^6.5.9", + "react-json-view": "^1.21.3", "react-monaco-editor": "^0.53.0", "react-player": "^2.9.0", "react-resizable-panels": "^2.1.3", diff --git a/playground/src/app/page.tsx b/playground/src/app/page.tsx index e641a8a5fd..061338f96c 100644 --- a/playground/src/app/page.tsx +++ b/playground/src/app/page.tsx @@ -5,7 +5,6 @@ import React, {useEffect, useState} from 'react'; import {Editor} from '../../../src/editor-v2'; -import content from './content.json'; import './page.scss'; const b = block('home'); @@ -23,12 +22,7 @@ export default function Home() { <ThemeProvider theme={'light'}> <div className={b()}> {initialUrl && ( - <Editor - initialUrl={initialUrl} - content={content} - disableUrlField={false} - onUpdate={() => {}} - /> + <Editor initialUrl={initialUrl} disableUrlField={false} onUpdate={() => {}} /> )} </div> </ThemeProvider> diff --git a/src/common/hooks/usePostMessage.tsx b/src/common/hooks/usePostMessage.tsx deleted file mode 100644 index 8b3d032590..0000000000 --- a/src/common/hooks/usePostMessage.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import {useCallback, useEffect} from 'react'; - -import { - Action, - Meta, - MetaSource, - PostMessageArgs, - SendOptions, - Subscriber, - WithStoreReducer, -} from '../../common/types'; - -interface UsePostMessageProps { - subscribers: Subscriber[]; - storesWithReducer: WithStoreReducer[]; - targetIframeElement?: HTMLIFrameElement; - urlOrigin?: string; -} - -// TODO: enable/disable via env variables -const DEBUG_MODE = false; - -export const usePostMessage = (props: UsePostMessageProps) => { - const {subscribers, storesWithReducer, targetIframeElement, urlOrigin} = props; - - const metaSource: MetaSource = targetIframeElement ? 'editor' : 'pc'; - const receiveMetaSource: MetaSource = targetIframeElement ? 'pc' : 'editor'; - - const notifySubscribers = useCallback( - (action: Action, source: MetaSource) => { - const meta: Meta = {source}; - - const {type, payload} = action; - - if (!type) { - return; - } - - if (subscribers) { - const foundedSubscribers = subscribers.filter( - (subscriber) => subscriber.action === type, - ); - - for (const storeWithReducer of storesWithReducer) { - storeWithReducer.reducer(action, meta); - } - - if (foundedSubscribers.length) { - for (const subscriber of foundedSubscribers) { - subscriber.handler(payload, meta); - } - } - } - }, - [storesWithReducer, subscribers], - ); - - const sendPostMessage = useCallback( - (args: PostMessageArgs) => { - if (targetIframeElement && targetIframeElement.contentWindow) { - if (targetIframeElement.contentWindow) { - targetIframeElement.contentWindow.postMessage(args, '*'); - } else { - // eslint-disable-next-line no-console - console.error('No Iframe element in Editor'); - } - } else { - window.parent.postMessage(args, '*'); - } - }, - [targetIframeElement], - ); - - const sendMessage = useCallback( - (action: Action, {debug = DEBUG_MODE, direction = 'both'}: SendOptions = {}) => { - if (debug) { - if (metaSource === 'editor') { - // eslint-disable-next-line no-console - console.log(`🔵 Editor ➡️ ${action.type}`, action.payload); - } - - if (metaSource === 'pc') { - // eslint-disable-next-line no-console - console.log(`🟢 Website ➡️ ${action.type}`, action.payload); - } - } - - if (direction === 'both') { - sendPostMessage({action, debug}); - notifySubscribers(action, metaSource); - } else if (direction === metaSource) { - notifySubscribers(action, metaSource); - } else { - sendPostMessage({action, debug}); - } - }, - [metaSource, notifySubscribers, sendPostMessage], - ); - - useEffect(() => { - const onMessage = (e: MessageEvent) => { - const {action, debug}: PostMessageArgs = e.data; - - if (urlOrigin && e.origin !== urlOrigin) { - return; - } - - if (action && action.type) { - if (debug) { - if (metaSource === 'editor') { - // eslint-disable-next-line no-console - console.log(`🔵 ➡️ Editor ${action.type}`, action.payload); - } - - if (metaSource === 'pc') { - // eslint-disable-next-line no-console - console.log(`🟢 ➡️ Website ${action.type}`, action.payload); - } - } - notifySubscribers(action, receiveMetaSource); - } - }; - - window.addEventListener('message', onMessage); - - return () => { - window.removeEventListener('message', onMessage); - }; - }, [metaSource, notifySubscribers, receiveMetaSource, targetIframeElement, urlOrigin]); - - return { - sendMessage, - }; -}; diff --git a/src/common/postMessage.ts b/src/common/postMessage.ts new file mode 100644 index 0000000000..47695356f6 --- /dev/null +++ b/src/common/postMessage.ts @@ -0,0 +1,42 @@ +import {useEffect} from 'react'; + +import {ActionMessageTypes, EventMessageTypes, PostMessageAPIMessage} from './types'; + +export function requestActionPostMessage<K extends keyof ActionMessageTypes>( + action: K, + data: ActionMessageTypes[K], + destinationElement: Window, +) { + const message = {action, data} as PostMessageAPIMessage<K>; + destinationElement.postMessage(message); +} + +export function listenPostMessageEvents<K extends keyof EventMessageTypes>( + action: K, + callback: (data: EventMessageTypes[K]) => void, +) { + const onMessage = (e: MessageEvent) => { + const message = e.data as PostMessageAPIMessage<K>; + + if ('action' in message && message.action === action) { + return callback(message.data); + } + + return undefined; + }; + + window.addEventListener('message', onMessage); + + return () => { + window.removeEventListener('message', onMessage); + }; +} + +export function usePostMessageAPIListener<K extends keyof EventMessageTypes>( + action: K, + callback: (data: EventMessageTypes[K]) => void, +) { + useEffect(() => { + return listenPostMessageEvents(action, callback); + }, [action, callback]); +} diff --git a/src/common/store.ts b/src/common/store.ts new file mode 100644 index 0000000000..37a246003b --- /dev/null +++ b/src/common/store.ts @@ -0,0 +1,263 @@ +import _ from 'lodash'; + +// TODO: remove imports from editor-v2 +import {ZOOM_STEPS} from '../editor-v2/constants'; +import { + duplicateArrayItem, + generateChildrenPathFromArray, + getDestinationShiftBeforeReorder, + insert, + isItemsNeighbours, + modifyObjectByPath, + removeFromArray, + reorderArrayItems, +} from '../editor-v2/utils'; +import {ConstructorBlock, PageContent} from '../models'; +import {initializeStore} from '../utils/store'; + +import {ConfigInput, DynamicFormValue, ItemConfig} from './types'; + +export interface EditorState { + height?: number; + zoom: number; + + manipulateOverlayMode: 'insert' | 'reorder' | false; + selectedBlock?: number[]; + initialized: boolean; + + content: PageContent; + blocks: Array<ItemConfig>; + subBlocks: Array<ItemConfig>; + global: Array<ConfigInput>; + + preInsertBlockType: string | null; + preReorderBlockPath: number[] | null; +} + +export interface EditorMethods { + initialize(): void; + setSelectedBlock(path: number[]): void; + setHeight(height: number): void; + setZoom(zoom: number): void; + increaseZoom(): void; + decreaseZoom(): void; + setConfig(data: Pick<EditorState, 'blocks' | 'subBlocks' | 'global'>): void; + setContent(data: PageContent): void; + insertBlock(path: number[], blockType: string, position?: 'prepend' | 'append'): void; + enableInsertMode(blockType: string): void; + enableReorderMode(path: number[]): void; + disableMode(): void; + updateField(path: string, value: DynamicFormValue): void; + deleteBlock(path: number[]): void; + duplicateBlock(path: number[]): void; + reorderBlock(path: number[], destination: number[], position?: 'prepend' | 'append'): void; + resetInitialize(): void; + resetBlocks(): void; +} + +export type EditorStore = EditorState & EditorMethods; + +// TODO: split methods and state +export const createEditorStore = initializeStore<EditorState, EditorMethods>( + { + height: 100, + zoom: 100, + manipulateOverlayMode: false, + selectedBlock: undefined, + initialized: false, + content: {blocks: []}, + blocks: [], + subBlocks: [], + global: [], + preInsertBlockType: null, + preReorderBlockPath: null, + }, + (set, get) => ({ + setHeight(height: number) { + // We have to add 200-500px, because of bottom padding or margin of last element + // which is not taken into calculation of final height + const newHeight = height + 500; + set((state) => ({...state, height: newHeight})); + }, + setZoom(zoom) { + if (zoom > 0) { + set((state) => ({...state, zoom})); + } + }, + increaseZoom() { + const currentZoom = get().zoom; + + for (const step of ZOOM_STEPS) { + if (currentZoom < step) { + get().setZoom(step); + break; + } + } + }, + decreaseZoom() { + const currentZoom = get().zoom; + const reverseSteps = ZOOM_STEPS.slice().reverse(); + + for (const step of reverseSteps) { + if (currentZoom > step) { + get().setZoom(step); + break; + } + } + }, + setConfig(data) { + set((state) => ({...state, ...data})); + }, + insertBlock: (arrayPath, blockType, position = 'append') => { + if (position === 'append') { + // TODO: fix + // eslint-disable-next-line no-not-accumulator-reassign/no-not-accumulator-reassign, no-param-reassign + arrayPath[arrayPath.length - 1] = arrayPath[arrayPath.length - 1] + 1; + } + + const blocksConfig = get().content.blocks; + const blocksData = get().blocks; + + const foundBlock = blocksData.find(({type}) => type === blockType); + const defaultValue = + foundBlock && foundBlock.schema.default + ? {...foundBlock.schema.default, type: blockType} + : {type: blockType}; + + const newBlocksConfig = modifyObjectByPath( + blocksConfig, + arrayPath, + (parentBlocks, index) => + insert(parentBlocks, index, defaultValue as ConstructorBlock), + ); + + set((state) => ({ + ...state, + content: {...state.content, blocks: newBlocksConfig}, + })); + }, + enableInsertMode(blockType: string) { + set((state) => ({ + ...state, + manipulateOverlayMode: 'insert', + preInsertBlockType: blockType, + })); + }, + disableMode() { + set((state) => ({ + ...state, + manipulateOverlayMode: false, + preInsertBlockType: undefined, + preReorderBlockPath: undefined, + })); + }, + enableReorderMode(path) { + set((state) => ({ + ...state, + manipulateOverlayMode: 'reorder', + preReorderBlockPath: path, + })); + }, + setContent(content) { + set((state) => ({ + ...state, + content: content, + })); + }, + initialize() { + set((state) => ({ + ...state, + initialized: true, + })); + }, + setSelectedBlock(path) { + set((state) => ({ + ...state, + selectedBlock: path, + })); + }, + updateField(path, value) { + set((state) => { + const newConfig = _.set(state.content, path, value); + return { + ...state, + content: newConfig, + }; + }); + }, + deleteBlock: (arrayPath) => { + const blocksConfig = get().content.blocks; + + const newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, removeFromArray); + + set((state) => ({ + ...state, + content: {...state.content, blocks: newBlocksConfig}, + })); + }, + duplicateBlock: (arrayPath) => { + const blocksConfig = get().content.blocks; + + const newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, duplicateArrayItem); + + set((state) => ({ + ...state, + content: {...state.content, blocks: newBlocksConfig}, + })); + }, + reorderBlock: (arrayPath, destination, position = 'append') => { + if (position === 'append') { + // TODO: fix + // eslint-disable-next-line no-not-accumulator-reassign/no-not-accumulator-reassign, no-param-reassign + destination[destination.length - 1] = destination[destination.length - 1] + 1; + } + + let newBlocksConfig: ConstructorBlock[]; + const blocksConfig = get().content.blocks; + // Copy + const copiedBlock = _.get(blocksConfig, generateChildrenPathFromArray(arrayPath)); + + if (isItemsNeighbours(arrayPath, destination)) { + newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, (parentBlocks) => { + return reorderArrayItems( + parentBlocks, + arrayPath[arrayPath.length - 1], + destination[destination.length - 1], + ); + }); + } else { + const arrayDest = getDestinationShiftBeforeReorder(arrayPath, destination); + + // Delete + const blocksConfigWithoutBlock = modifyObjectByPath( + blocksConfig, + arrayPath, + removeFromArray, + ); + // Paste + newBlocksConfig = modifyObjectByPath( + blocksConfigWithoutBlock, + arrayDest, + (parentBlocks, index) => insert(parentBlocks, index, copiedBlock), + ); + } + + set((state) => ({ + ...state, + content: {...state.content, blocks: newBlocksConfig}, + })); + }, + resetInitialize: () => { + set((state) => ({ + ...state, + initialized: false, + })); + }, + resetBlocks: () => { + set((state) => ({ + ...state, + content: {...state.content, blocks: []}, + })); + }, + }), +); diff --git a/src/common/types/actions.ts b/src/common/types/actions.ts new file mode 100644 index 0000000000..64c7465d53 --- /dev/null +++ b/src/common/types/actions.ts @@ -0,0 +1,22 @@ +import {PageContent} from '../../models'; +import {EditorState} from '../store'; + +export type MessageTypes = EventMessageTypes & ActionMessageTypes; + +export type EventMessageTypes = { + ON_INIT: {height: number}; + ON_RESIZE: {height: number}; + ON_MOUSE_UP: {path?: number[]; rect?: DOMRect; position?: string}; + ON_MOUSE_MOVE: {x: number; y: number}; + ON_HOVER_BLOCK: {rect?: DOMRect; position?: string}; + ON_CLICK_BLOCK: {path: number[]; rect: DOMRect}; + ON_RESIZE_BLOCK: {rect: DOMRect}; + ON_SUPPORTED_BLOCKS: Pick<EditorState, 'blocks' | 'subBlocks' | 'global'>; + ON_INITIAL_CONTENT: PageContent; +}; + +export type ActionMessageTypes = { + GET_SUPPORTED_BLOCKS: {}; + GET_CURRENT_CONTENT: {}; + SET_CONTENT: PageContent; +}; diff --git a/src/common/types/actions/codes.ts b/src/common/types/actions/codes.ts deleted file mode 100644 index 3ec1a59350..0000000000 --- a/src/common/types/actions/codes.ts +++ /dev/null @@ -1,27 +0,0 @@ -export enum ActionTypes { - // Other Events - UpdateConfigs = 'UPDATE_CONFIGS', - SetHeight = 'SET_HEIGHT', - - // Initial Events - IframeReady = 'IFRAME_READY', - EditorReady = 'EDITOR_READY', - BlocksConfigs = 'BLOCKS_CONFIGS', - - // Insert Events - InsertBlock = 'INSERT_BLOCK', - InsertModeEnable = 'INSERT_MODE_ENABLE', - InsertModeDisable = 'INSERT_MODE_DISABLE', - - // Reorder Events - ReorderBlocks = 'REORDER_BLOCKS', - ReorderModeEnable = 'REORDER_MODE_ENABLE', - ReorderModeDisable = 'REORDER_MODE_DISABLE', - - // Overlay Mode - OverlayModeOnMove = 'OVERLAY_MODE_ON_MOVE', - - // Select Events - SelectBlock = 'SELECT_BLOCK', - UpdateSelectedBlockRect = 'UPDATE_SELECTED_BLOCK_RECT', -} diff --git a/src/common/types/actions/index.ts b/src/common/types/actions/index.ts deleted file mode 100644 index 2c7da200ad..0000000000 --- a/src/common/types/actions/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {InitialActions} from './initial'; -import {InsertActions} from './insert'; -import {OtherActions} from './other'; -import {OverlayActions} from './overlay'; -import {ReorderActions} from './reorder'; -import {SelectActions} from './select'; - -export type BaseAction<T extends string = string> = { - type: T; -}; - -export interface UnknownAction extends BaseAction { - // Allows any extra properties to be defined in an action. - [extraProps: string]: unknown; -} - -export type Action = - | InitialActions - | InsertActions - | OverlayActions - | ReorderActions - | SelectActions - | OtherActions; - -export * from './codes'; -export * from './initial'; -export * from './insert'; -export * from './other'; -export * from './overlay'; -export * from './reorder'; -export * from './select'; diff --git a/src/common/types/actions/initial.ts b/src/common/types/actions/initial.ts deleted file mode 100644 index b313c3a110..0000000000 --- a/src/common/types/actions/initial.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {ConfigInput} from '../../../common/types'; -import {ItemConfig} from '../index'; - -import {ActionTypes} from './codes'; - -import {UnknownAction} from './index'; - -export interface IframeReadyAction extends UnknownAction { - type: ActionTypes.IframeReady; - payload: { - height: number; - }; -} - -export interface EditorReadyAction extends UnknownAction { - type: ActionTypes.EditorReady; - payload: unknown; -} - -export interface BlocksConfigsAction extends UnknownAction { - type: ActionTypes.BlocksConfigs; - payload: { - blocks: ItemConfig[]; - subBlocks: ItemConfig[]; - global: ConfigInput[]; - }; -} - -export type InitialActions = BlocksConfigsAction | IframeReadyAction | EditorReadyAction; diff --git a/src/common/types/actions/insert.ts b/src/common/types/actions/insert.ts deleted file mode 100644 index de88f33aaf..0000000000 --- a/src/common/types/actions/insert.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {ActionTypes} from './codes'; - -import {UnknownAction} from './index'; - -export interface InsertBlockAction extends UnknownAction { - type: ActionTypes.InsertBlock; - payload: { - path: number[]; - }; -} - -export interface InsertModeEnableAction extends UnknownAction { - type: ActionTypes.InsertModeEnable; - payload: { - blockType: string; - }; -} - -export interface InsertModeDisableAction extends UnknownAction { - type: ActionTypes.InsertModeDisable; - payload: undefined; -} - -export type InsertActions = InsertBlockAction | InsertModeEnableAction | InsertModeDisableAction; diff --git a/src/common/types/actions/other.ts b/src/common/types/actions/other.ts deleted file mode 100644 index 1f10e9217e..0000000000 --- a/src/common/types/actions/other.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {PageContent} from '../../../models'; - -import {ActionTypes} from './codes'; - -import {UnknownAction} from './index'; - -export interface UpdateConfigsAction extends UnknownAction { - type: ActionTypes.UpdateConfigs; - payload: { - content: PageContent; - }; -} - -export interface SetHeightAction extends UnknownAction { - type: ActionTypes.SetHeight; - payload: { - height: number; - }; -} - -export type OtherActions = UpdateConfigsAction | SetHeightAction; diff --git a/src/common/types/actions/overlay.ts b/src/common/types/actions/overlay.ts deleted file mode 100644 index dd5340dd27..0000000000 --- a/src/common/types/actions/overlay.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {ActionTypes} from './codes'; - -import {UnknownAction} from './index'; - -export interface OverlayModeOnMoveAction extends UnknownAction { - type: ActionTypes.OverlayModeOnMove; - payload: { - block?: { - rect: DOMRect; - cursorPosition: 'top' | 'bottom' | 'left' | 'right'; - }; - cursor: {x: number; y: number}; - }; -} - -export type OverlayActions = OverlayModeOnMoveAction; diff --git a/src/common/types/actions/reorder.ts b/src/common/types/actions/reorder.ts deleted file mode 100644 index e49cc2c043..0000000000 --- a/src/common/types/actions/reorder.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {ActionTypes} from './codes'; - -import {UnknownAction} from './index'; - -export interface ReorderModeEnableAction extends UnknownAction { - type: ActionTypes.ReorderModeEnable; - payload: { - path: number[]; - }; -} - -export interface ReorderModeDisableAction extends UnknownAction { - type: ActionTypes.ReorderModeDisable; - payload: undefined; -} - -export interface ReorderBlocksAction extends UnknownAction { - type: ActionTypes.ReorderBlocks; - payload: { - path: number[]; - }; -} - -export type ReorderActions = - | ReorderBlocksAction - | ReorderModeEnableAction - | ReorderModeDisableAction; diff --git a/src/common/types/actions/select.ts b/src/common/types/actions/select.ts deleted file mode 100644 index cf451ab841..0000000000 --- a/src/common/types/actions/select.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {ActionTypes} from './codes'; - -import {UnknownAction} from './index'; - -export interface SelectBlockAction extends UnknownAction { - type: ActionTypes.SelectBlock; - payload: { - path: number[]; - rect: DOMRect; - }; -} - -export interface UpdateSelectedBlockRectAction extends UnknownAction { - type: ActionTypes.UpdateSelectedBlockRect; - payload: { - rect: DOMRect; - }; -} - -export type SelectActions = SelectBlockAction | UpdateSelectedBlockRectAction; diff --git a/src/common/types/common.ts b/src/common/types/common.ts index ce344e43fe..2732c46edf 100644 --- a/src/common/types/common.ts +++ b/src/common/types/common.ts @@ -1,17 +1,6 @@ -import {Action} from './actions'; import {BlockConfig} from './forms'; -import {Meta} from './messages'; export interface ItemConfig { type: string; schema: BlockConfig; } - -export interface WithStoreReducer { - reducer: (action: Action, meta: Meta) => void; -} - -export type Subscriber<A extends Action = Action> = { - action: A['type']; - handler: (payload: A['payload'], meta: Meta) => void; -}; diff --git a/src/common/types/forms.ts b/src/common/types/forms.ts index 285bf89bb5..6597b12e53 100644 --- a/src/common/types/forms.ts +++ b/src/common/types/forms.ts @@ -1,3 +1,7 @@ +import {PageContent} from '../../models'; + +export type DynamicFormValue = string | number | [] | object | boolean | PageContent | undefined; + export interface BlockConfig { name: string; inputs: Array<ConfigInput>; diff --git a/src/common/types/messages.ts b/src/common/types/messages.ts index b2a905b8e3..aa9ec2a629 100644 --- a/src/common/types/messages.ts +++ b/src/common/types/messages.ts @@ -1,22 +1,12 @@ -import {Action} from './actions'; +import {EditorState} from '../store'; -export interface SendOptions { - direction?: MetaSource | 'both'; - debug?: boolean; -} +import {MessageTypes} from './actions'; -export interface PostMessageArgs { - action: Action; - debug: boolean; -} +export type PostMessageAPIMessage<K extends keyof MessageTypes> = { + action: K; + data: MessageTypes[K]; +}; -export type SubscriptionFunc = <A extends Action>( - type: A['type'], - payloadCallback: (payload: A['payload'], meta: Meta) => void, -) => void; - -export type MetaSource = 'pc' | 'editor'; - -export interface Meta { - source: MetaSource; -} +export type StoreSyncMessage = { + state: EditorState; +}; diff --git a/src/components/editor/ChildrenWrap/ChildrenWrap.tsx b/src/components/editor/ChildrenWrap/ChildrenWrap.tsx index 1eb77dca3e..78287b18f1 100644 --- a/src/components/editor/ChildrenWrap/ChildrenWrap.tsx +++ b/src/components/editor/ChildrenWrap/ChildrenWrap.tsx @@ -1,4 +1,4 @@ -import React, {PropsWithChildren, ReactNode, useContext} from 'react'; +import React, {PropsWithChildren, ReactNode, useCallback, useContext, useState} from 'react'; import useEditorBlockMouseEvents from '../../../containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents'; import {BlockIdContext} from '../../../context/blockIdContext'; @@ -14,11 +14,17 @@ export interface ChildrenWrapProps extends PropsWithChildren { const ChildrenWrap = (props: ChildrenWrapProps) => { const {children} = props; + const [element, setElement] = useState<HTMLElement | undefined>(); + const blockRef = useCallback((node: HTMLElement | null) => { + if (node !== null) { + setElement(node); + } + }, []); const parentBlockId = useContext(BlockIdContext); - const {onMouseUp, onMouseMove} = useEditorBlockMouseEvents([parentBlockId, 0]); + const {onMouseUp, onMouseMove} = useEditorBlockMouseEvents([parentBlockId, 0], element); return ( - <div className={b()} onMouseMove={onMouseMove} onMouseUp={onMouseUp}> + <div ref={blockRef} className={b()} onMouseMove={onMouseMove} onMouseUp={onMouseUp}> {children} </div> ); diff --git a/src/components/editor/ItemWrap/ItemWrap.tsx b/src/components/editor/ItemWrap/ItemWrap.tsx index 4c78420025..cf8fbad926 100644 --- a/src/components/editor/ItemWrap/ItemWrap.tsx +++ b/src/components/editor/ItemWrap/ItemWrap.tsx @@ -1,4 +1,4 @@ -import React, {PropsWithChildren, useContext} from 'react'; +import React, {PropsWithChildren, useCallback, useContext, useState} from 'react'; import useEditorBlockMouseEvents from '../../../containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents'; import {BlockIdContext} from '../../../context/blockIdContext'; @@ -13,12 +13,18 @@ export interface ItemWrapProps extends PropsWithChildren { } const ItemWrap = (props: ItemWrapProps) => { + const [element, setElement] = useState<HTMLElement | undefined>(); + const blockRef = useCallback((node: HTMLElement | null) => { + if (node !== null) { + setElement(node); + } + }, []); const {children, index} = props; const parentBlockId = useContext(BlockIdContext); - const adminBlockMouseEvents = useEditorBlockMouseEvents([parentBlockId, index]); + const adminBlockMouseEvents = useEditorBlockMouseEvents([parentBlockId, index], element); return ( - <div className={b()} {...adminBlockMouseEvents}> + <div ref={blockRef} className={b()} {...adminBlockMouseEvents}> {children} </div> ); diff --git a/src/containers/PageConstructor/PageConstructor.tsx b/src/containers/PageConstructor/PageConstructor.tsx index 801c6e5547..c3e49b23fd 100644 --- a/src/containers/PageConstructor/PageConstructor.tsx +++ b/src/containers/PageConstructor/PageConstructor.tsx @@ -8,11 +8,11 @@ import BrandFooter from '../../components/BrandFooter/BrandFooter'; import RootCn from '../../components/RootCn'; import {blockMap, navItemMap, subBlockMap} from '../../constructor-items'; import {AnimateContext} from '../../context/animateContext'; -import {useEditorStore} from '../../context/editorContext'; +import {usePCEditorStore} from '../../context/editorStoreContext'; import {InnerContext} from '../../context/innerContext'; import {ProjectSettingsContext} from '../../context/projectSettingsContext'; import {useTheme} from '../../context/theme'; -import useEditorInitialize from '../../hooks/useEditorInitialize'; +import {useInitializeEditorEvents} from '../../hooks/useEditorInitialize'; import { BlockTypes, CustomConfig, @@ -64,8 +64,10 @@ export const Constructor = (props: PageConstructorProps) => { const [content, setContent] = useState<PageContent>({blocks, background}); const theme = useTheme(); - const {initialized} = useEditorStore(); - useEditorInitialize({content, setContent}); + const store = usePCEditorStore(); + const {initialized} = store; + + useInitializeEditorEvents({initialContent: {blocks, background}, setContent}); const {context} = useMemo( () => ({ diff --git a/src/containers/PageConstructor/Provider.tsx b/src/containers/PageConstructor/Provider.tsx index 12cc5535af..ec6f3f7b58 100644 --- a/src/containers/PageConstructor/Provider.tsx +++ b/src/containers/PageConstructor/Provider.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import {DEFAULT_THEME} from '../../components/constants'; import {AnalyticsContext, AnalyticsContextProps} from '../../context/analyticsContext'; -import {EditorProvider} from '../../context/editorContext'; +import {PCEditorStoreProvider} from '../../context/editorStoreContext'; import { DEFAULT_FORMS_CONTEXT_VALUE, FormsContext, @@ -12,7 +12,6 @@ import {ImageContext, ImageContextProps} from '../../context/imageContext'; import {LocaleContext, LocaleContextProps} from '../../context/localeContext'; import {LocationContext, LocationContextProps} from '../../context/locationContext'; import {MapsContext, MapsContextType, initialMapValue} from '../../context/mapsContext/mapsContext'; -import {PostMessageProvider} from '../../context/messagesContext'; import {MobileContext} from '../../context/mobileContext'; import { ProjectSettingsContext, @@ -66,8 +65,7 @@ export const PageConstructorProvider = ( <FormsContext.Provider value={forms} />, <SSRContext.Provider value={{isServer: ssrConfig?.isServer}} />, <WindowWidthProvider />, - <EditorProvider />, - <PostMessageProvider />, + <PCEditorStoreProvider />, ].reduceRight((prev, provider) => React.cloneElement(provider, {}, prev), children); /* eslint-enable react/jsx-key */ diff --git a/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx b/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx index 6f66977543..cd9c6b4850 100644 --- a/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx +++ b/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import React, {useCallback, useMemo, useState} from 'react'; import pick from 'lodash/pick'; @@ -22,16 +22,22 @@ export const ConstructorBlock = ({ data, children, }: React.PropsWithChildren<ConstructorBlockProps>) => { - const adminBlockMouseEvents = useEditorBlockMouseEvents([index]); + const [element, setElement] = useState<HTMLElement | undefined>(); + const blockRef = useCallback((node: HTMLElement | null) => { + if (node !== null) { + setElement(node); + } + }, []); + const adminBlockMouseEvents = useEditorBlockMouseEvents([index], element); const {type} = data; - const blockBaseProps = React.useMemo( + const blockBaseProps = useMemo( () => pick(data, ['anchor', 'visible', 'resetPaddings', 'indent']), [data], ); return ( - <div {...adminBlockMouseEvents}> + <div ref={blockRef} {...adminBlockMouseEvents}> <BlockDecoration type={type} index={index} {...blockBaseProps}> <BlockBase className={b({type})} {...blockBaseProps}> {children} diff --git a/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx b/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx index b0c7787712..b339857163 100644 --- a/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx +++ b/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx @@ -1,124 +1,81 @@ -import React, {useCallback, useContext} from 'react'; +import React, {useCallback, useEffect} from 'react'; -import {ActionTypes} from '../../../../../common/types/actions'; -import {EditorContext, useEditorStore} from '../../../../../context/editorContext'; -import {useMessageSender} from '../../../../../context/messagesContext/hooks/useMessageSender'; +import _ from 'lodash'; + +import {usePCEditorStore} from '../../../../../context/editorStoreContext'; +import {sendEventPostMessage} from '../../../../../hooks/usePostMessageAPI'; import {getCursorPositionOverElement} from '../../../../../utils/editor'; -const useEditorBlockMouseEvents = (arrayIndex: number[]) => { - const sendMessage = useMessageSender(); - const {manipulateOverlayMode} = useEditorStore(); - const {setActiveElement} = useContext(EditorContext); +const useEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement) => { + const {selectedBlock} = usePCEditorStore(); const onMouseUp = useCallback( - (event: React.MouseEvent) => { - event.preventDefault(); - event.stopPropagation(); - if (manipulateOverlayMode) { - const rect = event.currentTarget.getClientRects().item(0); + (e: React.MouseEvent) => { + e.stopPropagation(); + if (element) { + const rect = element.getClientRects().item(0); if (rect) { - const position = getCursorPositionOverElement(rect, event); - - let newLastIndex = arrayIndex[arrayIndex.length - 1]; - - if (position === 'right' || position === 'bottom') { - newLastIndex += 1; - } - - const newArrayIndex = [ - ...arrayIndex.slice(0, arrayIndex.length - 1), - newLastIndex, - ]; - - if (manipulateOverlayMode === 'insert') { - sendMessage({ - type: ActionTypes.InsertBlock, - payload: { - path: newArrayIndex, - }, - }); - } else if (manipulateOverlayMode === 'reorder') { - sendMessage({ - type: ActionTypes.ReorderBlocks, - payload: { - path: newArrayIndex, - }, - }); - } + const position = getCursorPositionOverElement(rect, e); + sendEventPostMessage('ON_MOUSE_UP', {path: arrayIndex, rect, position}); } } }, - [arrayIndex, manipulateOverlayMode, sendMessage], + [arrayIndex, element], ); const onMouseMove = useCallback( - (event: React.MouseEvent) => { - event.preventDefault(); - event.stopPropagation(); - if (manipulateOverlayMode) { - const rect = event.currentTarget.getClientRects().item(0); + (e: React.MouseEvent) => { + e.stopPropagation(); + if (element) { + const rect = element.getClientRects().item(0); if (rect) { - const cursorPosition = getCursorPositionOverElement(rect, event); - sendMessage({ - type: ActionTypes.OverlayModeOnMove, - payload: { - block: {rect, cursorPosition}, - cursor: { - x: event.clientX, - y: event.clientY, - }, - }, - }); + const position = getCursorPositionOverElement(rect, e); + sendEventPostMessage('ON_HOVER_BLOCK', {rect, position}); } } }, - [manipulateOverlayMode, sendMessage], + [element], ); - const onMouseLeave = useCallback( - (e: React.MouseEvent) => { - e.preventDefault(); - if (manipulateOverlayMode) { - const rect = e.currentTarget.getClientRects().item(0); - if (rect) { - sendMessage({ - type: ActionTypes.OverlayModeOnMove, - payload: { - cursor: { - x: e.clientX, - y: e.clientY, - }, - }, - }); - } - } else { + const onMouseLeave = useCallback(() => { + if (element) { + const rect = element.getClientRects().item(0); + if (rect) { + sendEventPostMessage('ON_HOVER_BLOCK', {}); } - }, - [manipulateOverlayMode, sendMessage], - ); + } + }, [element]); const onClick = useCallback( - (e: React.MouseEvent<HTMLDivElement>) => { + (e: React.MouseEvent) => { e.stopPropagation(); - if (!manipulateOverlayMode) { - const element = e.currentTarget; + if (element) { const rect = element.getClientRects().item(0); if (rect) { - setActiveElement(element); - - sendMessage({ - type: ActionTypes.SelectBlock, - payload: { - path: arrayIndex, - rect: rect, - }, - }); + sendEventPostMessage('ON_CLICK_BLOCK', {rect, path: arrayIndex}); } } }, - [arrayIndex, manipulateOverlayMode, sendMessage, setActiveElement], + [arrayIndex, element], ); + const onResize = useCallback(() => { + if (element && _.isEqual(selectedBlock, arrayIndex)) { + const rect = element.getClientRects().item(0); + if (rect) { + sendEventPostMessage('ON_RESIZE_BLOCK', {rect}); + } + } + }, [arrayIndex, element, selectedBlock]); + + useEffect(() => { + window.addEventListener('resize', onResize); + + return () => { + window.removeEventListener('resize', onResize); + }; + }, [element, onResize]); + return { onClick, onMouseMove, diff --git a/src/context/editorContext/editorContext.ts b/src/context/editorContext/editorContext.ts deleted file mode 100644 index b8ef739ef9..0000000000 --- a/src/context/editorContext/editorContext.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Context for managing internal Editor states - * Same exist in Editor - **/ - -import React from 'react'; - -import {StoreApi} from 'zustand'; - -import {EditorStore} from './store'; - -export interface EditorContextProps { - state?: StoreApi<EditorStore>; - activeElement?: HTMLElement; - setActiveElement: (element: HTMLElement) => void; -} - -export const EditorContext = React.createContext<EditorContextProps>({setActiveElement: () => {}}); diff --git a/src/context/editorContext/editorProvider.tsx b/src/context/editorContext/editorProvider.tsx deleted file mode 100644 index 4812ee416c..0000000000 --- a/src/context/editorContext/editorProvider.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React, {PropsWithChildren, useRef, useState} from 'react'; - -import {StoreApi} from 'zustand'; - -import {EditorContext} from './editorContext'; -import {EditorStore, createEditorStore} from './store'; - -export const EditorProvider: React.FC<PropsWithChildren> = ({children}) => { - const storeRef = useRef<StoreApi<EditorStore>>(); - const [activeElement, setActiveElement] = useState<HTMLElement | undefined>(); - - if (!storeRef.current) { - storeRef.current = createEditorStore(); - } - - return ( - <EditorContext.Provider - value={{ - state: storeRef.current, - activeElement: activeElement, - setActiveElement: setActiveElement, - }} - > - {children} - </EditorContext.Provider> - ); -}; diff --git a/src/context/editorContext/hooks/useEditorStore.ts b/src/context/editorContext/hooks/useEditorStore.ts deleted file mode 100644 index 4f2a346cde..0000000000 --- a/src/context/editorContext/hooks/useEditorStore.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {useContext} from 'react'; - -import {useStore} from 'zustand'; - -import {EditorContext} from '../editorContext'; - -export const useEditorStore = () => { - const {state} = useContext(EditorContext); - - if (!state) { - throw new Error('Missing EditorContext'); - } - - return useStore(state); -}; - -export default useEditorStore; diff --git a/src/context/editorContext/index.ts b/src/context/editorContext/index.ts deleted file mode 100644 index c7b944eea3..0000000000 --- a/src/context/editorContext/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './editorContext'; -export * from './editorProvider'; -export * from './hooks/useEditorStore'; -export * from './store'; diff --git a/src/context/editorContext/store.ts b/src/context/editorContext/store.ts deleted file mode 100644 index c8f65aabc4..0000000000 --- a/src/context/editorContext/store.ts +++ /dev/null @@ -1,68 +0,0 @@ -import {ActionTypes, WithStoreReducer} from '../../common/types'; -import {initializeStore} from '../../utils/store'; - -export interface EditorState { - manipulateOverlayMode: 'insert' | 'reorder' | false; - isSelectActive: boolean; - initialized: boolean; -} - -export interface EditorMethods extends WithStoreReducer {} - -export type EditorStore = EditorState & EditorMethods; - -export const createEditorStore = initializeStore<EditorState, EditorMethods>( - { - manipulateOverlayMode: false, - isSelectActive: false, - initialized: false, - }, - (set, _get) => ({ - reducer: (action) => { - switch (action.type) { - case ActionTypes.EditorReady: { - set((state) => ({ - ...state, - initialized: true, - })); - break; - } - case ActionTypes.ReorderModeEnable: { - set((state) => ({ - ...state, - manipulateOverlayMode: 'reorder', - })); - break; - } - case ActionTypes.ReorderModeDisable: { - set((state) => ({ - ...state, - manipulateOverlayMode: false, - })); - break; - } - case ActionTypes.InsertModeEnable: { - set((state) => ({ - ...state, - manipulateOverlayMode: 'insert', - })); - break; - } - case ActionTypes.InsertModeDisable: { - set((state) => ({ - ...state, - manipulateOverlayMode: false, - })); - break; - } - case ActionTypes.SelectBlock: { - set((state) => ({ - ...state, - isSelectActive: true, - })); - break; - } - } - }, - }), -); diff --git a/src/context/editorStoreContext/PCEditorStoreContext.tsx b/src/context/editorStoreContext/PCEditorStoreContext.tsx new file mode 100644 index 0000000000..7eebc64a70 --- /dev/null +++ b/src/context/editorStoreContext/PCEditorStoreContext.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +import {StoreApi} from 'zustand'; + +import {EditorStore, createEditorStore} from '../../common/store'; + +export interface PCEditorStoreContextProps { + state: StoreApi<EditorStore>; +} + +export const PCEditorStoreContext = React.createContext<PCEditorStoreContextProps>({ + state: createEditorStore(), +}); diff --git a/src/context/editorStoreContext/PCEditorStoreProvider.tsx b/src/context/editorStoreContext/PCEditorStoreProvider.tsx new file mode 100644 index 0000000000..9a305ca55d --- /dev/null +++ b/src/context/editorStoreContext/PCEditorStoreProvider.tsx @@ -0,0 +1,47 @@ +import React, {PropsWithChildren, useCallback, useEffect, useRef} from 'react'; + +import {StoreApi} from 'zustand'; + +import {EditorStore, createEditorStore} from '../../common/store'; +import {StoreSyncMessage} from '../../common/types'; + +import {PCEditorStoreContext} from './PCEditorStoreContext'; + +interface PCEditorStoreProviderProps extends PropsWithChildren {} + +export const PCEditorStoreProvider = ({children}: PCEditorStoreProviderProps) => { + const storeRef = useRef<StoreApi<EditorStore>>(); + + const syncStore = useCallback((message: StoreSyncMessage) => { + if (storeRef.current && message.state) { + storeRef.current.setState(message.state); + } + }, []); + + useEffect(() => { + const onMessage = (e: MessageEvent) => { + const message = e.data as StoreSyncMessage; + syncStore(message); + }; + + window.addEventListener('message', onMessage); + + return () => { + window.removeEventListener('message', onMessage); + }; + }, [syncStore]); + + if (!storeRef.current) { + storeRef.current = createEditorStore(); + } + + return ( + <PCEditorStoreContext.Provider + value={{ + state: storeRef.current, + }} + > + {children} + </PCEditorStoreContext.Provider> + ); +}; diff --git a/src/context/editorStoreContext/hooks/usePCEditorStore.ts b/src/context/editorStoreContext/hooks/usePCEditorStore.ts new file mode 100644 index 0000000000..31e9226aa0 --- /dev/null +++ b/src/context/editorStoreContext/hooks/usePCEditorStore.ts @@ -0,0 +1,10 @@ +import {useContext} from 'react'; + +import {useStore} from 'zustand'; + +import {PCEditorStoreContext} from '../PCEditorStoreContext'; + +export const usePCEditorStore = () => { + const {state} = useContext(PCEditorStoreContext); + return useStore(state); +}; diff --git a/src/context/editorStoreContext/index.ts b/src/context/editorStoreContext/index.ts new file mode 100644 index 0000000000..7fa6e621ac --- /dev/null +++ b/src/context/editorStoreContext/index.ts @@ -0,0 +1,3 @@ +export * from './hooks/usePCEditorStore'; +export * from './PCEditorStoreContext'; +export * from './PCEditorStoreProvider'; diff --git a/src/context/messagesContext/hooks/useMessageObserver.tsx b/src/context/messagesContext/hooks/useMessageObserver.tsx deleted file mode 100644 index 296ca9deae..0000000000 --- a/src/context/messagesContext/hooks/useMessageObserver.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import {DependencyList, useEffect} from 'react'; - -import {Action} from '../../../common/types'; - -import {useMessagesStore} from './useMessagesStore'; - -export const useMessageObserver = <A extends Action>( - type: A['type'], - callback: (payload: A['payload']) => void, - deps: DependencyList = [], -) => { - const {subscribe, unsubscribe} = useMessagesStore(); - - useEffect(() => { - subscribe(type, callback); - - return () => { - unsubscribe(type, callback); - }; - }, deps); // eslint-disable-line react-hooks/exhaustive-deps -}; - -export default useMessageObserver; diff --git a/src/context/messagesContext/hooks/useMessageSender.tsx b/src/context/messagesContext/hooks/useMessageSender.tsx deleted file mode 100644 index 57f25eef0e..0000000000 --- a/src/context/messagesContext/hooks/useMessageSender.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import {useContext} from 'react'; - -import {PostMessageContext} from '../messagesContext'; - -export const useMessageSender = () => { - const {sendMessage} = useContext(PostMessageContext); - return sendMessage; -}; - -export default useMessageSender; diff --git a/src/context/messagesContext/hooks/useMessagesStore.tsx b/src/context/messagesContext/hooks/useMessagesStore.tsx deleted file mode 100644 index 1805cd32a6..0000000000 --- a/src/context/messagesContext/hooks/useMessagesStore.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import {useContext} from 'react'; - -import {useStore} from 'zustand'; - -import {PostMessageContext} from '../messagesContext'; - -export const useMessagesStore = () => { - const {state} = useContext(PostMessageContext); - - if (!state) { - throw new Error('Missing PostMessageContext'); - } - - return useStore(state); -}; - -export default useMessagesStore; diff --git a/src/context/messagesContext/index.ts b/src/context/messagesContext/index.ts deleted file mode 100644 index eda561f123..0000000000 --- a/src/context/messagesContext/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './messagesContext'; -export * from './messagesProvider'; -export * from './hooks/useMessageSender'; -export * from './hooks/useMessageObserver'; -export * from './hooks/useMessagesStore'; diff --git a/src/context/messagesContext/messagesContext.tsx b/src/context/messagesContext/messagesContext.tsx deleted file mode 100644 index 502b6a2c87..0000000000 --- a/src/context/messagesContext/messagesContext.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Context for sending messages between Editor and App - * Same exist in Editor - **/ - -import React from 'react'; - -import {StoreApi} from 'zustand'; - -import {Action, SendOptions} from '../../common/types'; - -import {MessagesStore} from './store'; - -export interface PostMessageContextProps { - state?: StoreApi<MessagesStore>; - sendMessage: (action: Action, options?: SendOptions) => void; -} - -export const PostMessageContext = React.createContext<PostMessageContextProps>({ - sendMessage: () => {}, -}); diff --git a/src/context/messagesContext/messagesProvider.tsx b/src/context/messagesContext/messagesProvider.tsx deleted file mode 100644 index 86515bd344..0000000000 --- a/src/context/messagesContext/messagesProvider.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React, {PropsWithChildren, useRef} from 'react'; - -import {StoreApi} from 'zustand'; - -import {usePostMessage} from '../../common/hooks/usePostMessage'; -import {WithStoreReducer} from '../../common/types'; -import {useEditorStore} from '../editorContext'; - -import {PostMessageContext} from './messagesContext'; -import {MessagesStore, createMessagesStore} from './store'; - -export const PostMessageProvider = ({children}: PropsWithChildren) => { - const storeRef = useRef<StoreApi<MessagesStore>>(); - - if (!storeRef.current) { - storeRef.current = createMessagesStore(); - } - - const editorStore = useEditorStore(); - - const storesWithReducer: WithStoreReducer[] = [editorStore]; - - const {sendMessage} = usePostMessage({ - subscribers: storeRef.current?.getState().subscribers, - storesWithReducer, - }); - - return ( - <PostMessageContext.Provider - value={{ - state: storeRef.current, - sendMessage: sendMessage, - }} - > - {children} - </PostMessageContext.Provider> - ); -}; diff --git a/src/context/messagesContext/store.ts b/src/context/messagesContext/store.ts deleted file mode 100644 index ccfa312282..0000000000 --- a/src/context/messagesContext/store.ts +++ /dev/null @@ -1,44 +0,0 @@ -import {Subscriber, SubscriptionFunc} from '../../common/types'; -import {initializeStore} from '../../utils/store'; - -export interface MessagesState { - subscribers: Subscriber[]; -} - -export interface MessagesMethods { - subscribe: SubscriptionFunc; - unsubscribe: SubscriptionFunc; -} - -export type MessagesStore = MessagesState & MessagesMethods; - -export const createMessagesStore = initializeStore<MessagesState, MessagesMethods>( - { - subscribers: [], - }, - (set, _get) => ({ - subscribe: (type, payloadCallback) => { - set((state) => ({ - ...state, - subscribers: [...state.subscribers, {action: type, handler: payloadCallback}], - })); - - return () => { - set((state) => ({ - ...state, - subscribers: state.subscribers.filter( - ({handler, action}) => payloadCallback !== handler || type !== action, - ), - })); - }; - }, - unsubscribe: (type, payloadCallback) => { - set((state) => ({ - ...state, - subscribers: state.subscribers.filter( - ({handler, action}) => payloadCallback !== handler || type !== action, - ), - })); - }, - }), -); diff --git a/src/editor-v2/components/BigOverlay/BigOverlay.scss b/src/editor-v2/components/BigOverlay/BigOverlay.scss index e9d6abbf9e..b1f87ba97b 100644 --- a/src/editor-v2/components/BigOverlay/BigOverlay.scss +++ b/src/editor-v2/components/BigOverlay/BigOverlay.scss @@ -13,6 +13,7 @@ $block: '.#{$ns}big-overlay'; width: 100%; height: 100%; pointer-events: none; + overflow: hidden; $border: 3px var(--g-color-line-brand) solid; diff --git a/src/editor-v2/components/BigOverlay/BigOverlay.tsx b/src/editor-v2/components/BigOverlay/BigOverlay.tsx index 345e18ad5d..1401916855 100644 --- a/src/editor-v2/components/BigOverlay/BigOverlay.tsx +++ b/src/editor-v2/components/BigOverlay/BigOverlay.tsx @@ -1,17 +1,12 @@ -import React, {useContext, useState} from 'react'; +import React, {useCallback, useContext, useEffect, useMemo, useState} from 'react'; import {Stop} from '@gravity-ui/icons'; -import { - ActionTypes, - InsertModeDisableAction, - OverlayModeOnMoveAction, - ReorderModeDisableAction, -} from '../../../common/types'; +import {usePostMessageAPIListener} from '../../../common/postMessage'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; -import {IframeContext, useIframeStore} from '../../context/iframeContext'; -import {useMessageObserver} from '../../context/messagesContext'; +import {useMainEditorStore} from '../../context/editorStore'; +import {IframeContext} from '../../context/iframeContext'; import './BigOverlay.scss'; @@ -20,46 +15,64 @@ const b = block('big-overlay'); interface BigOverlayProps extends ClassNameProps {} const BigOverlay: React.FC<BigOverlayProps> = ({className}) => { - const {zoom} = useIframeStore(); + const {zoom, manipulateOverlayMode} = useMainEditorStore(); const {iframeElement} = useContext(IframeContext); const [mousePosition, setMousePosition] = useState<{x: number; y: number} | undefined>( undefined, ); + const [source, setSource] = useState<'main' | 'iframe'>('main'); - useMessageObserver<OverlayModeOnMoveAction>( - ActionTypes.OverlayModeOnMove, - (payload, meta) => { - if (payload && payload.cursor) { - const {x, y} = payload.cursor; - const iframeRect = iframeElement?.getClientRects().item(0); - if (iframeRect) { - const zoomedX = (x * zoom) / 100; - const zoomedY = (y * zoom) / 100; - const newX = meta.source === 'editor' ? x : zoomedX + iframeRect.x; - const newY = meta.source === 'editor' ? y : zoomedY + iframeRect.y; - setMousePosition({x: newX, y: newY}); - } - } - }, - [iframeElement, zoom], - ); - - useMessageObserver<InsertModeDisableAction>(ActionTypes.InsertModeDisable, () => { + const onMouseUp = useCallback(() => { setMousePosition(undefined); - }); + }, []); - useMessageObserver<ReorderModeDisableAction>(ActionTypes.ReorderModeDisable, () => { - setMousePosition(undefined); - }); + const onIframeMouseEvent = useCallback((position: {x: number; y: number}) => { + setMousePosition(position); + setSource('iframe'); + }, []); + + usePostMessageAPIListener('ON_MOUSE_UP', onMouseUp); + usePostMessageAPIListener('ON_MOUSE_MOVE', onIframeMouseEvent); + + useEffect(() => { + const onEditorMouseEvent = (event: MouseEvent) => { + setMousePosition({x: event.clientX, y: event.clientY}); + setSource('main'); + }; + + document.addEventListener('mousemove', onEditorMouseEvent); + document.addEventListener('mousedown', onEditorMouseEvent); + + return () => { + document.removeEventListener('mousemove', onEditorMouseEvent); + document.removeEventListener('mousedown', onEditorMouseEvent); + }; + }, []); + + const realPositions = useMemo(() => { + if (mousePosition) { + const {x, y} = mousePosition; + const iframeRect = iframeElement?.getClientRects().item(0); + if (iframeRect) { + const zoomedX = (x * zoom) / 100; + const zoomedY = (y * zoom) / 100; + const newX = source === 'main' ? x : zoomedX + iframeRect.x; + const newY = source === 'main' ? y : zoomedY + iframeRect.y; + return {x: newX, y: newY}; + } + } + + return undefined; + }, [mousePosition, source, iframeElement, zoom]); return ( <div className={b(null, className)}> - {mousePosition ? ( + {realPositions && manipulateOverlayMode ? ( <div className={b('border')} style={{ - top: mousePosition.y, - left: mousePosition.x, + top: realPositions.y, + left: realPositions.x, }} > <Stop height={20} width={20} /> diff --git a/src/editor-v2/components/BlockConfig/BlockConfig.tsx b/src/editor-v2/components/BlockConfig/BlockConfig.tsx index fcb71593d6..cc2b93a28f 100644 --- a/src/editor-v2/components/BlockConfig/BlockConfig.tsx +++ b/src/editor-v2/components/BlockConfig/BlockConfig.tsx @@ -2,12 +2,12 @@ import React from 'react'; import _ from 'lodash'; +import {DynamicFormValue} from '../../../common/types'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; -import {useContentConfigStore} from '../../context/contentConfig'; -import {useEditorStore} from '../../context/editorContext'; +import {useMainEditorStore} from '../../context/editorStore'; import {generateChildrenPathFromArray} from '../../utils'; -import DynamicForm, {DynamicFormValue} from '../DynamicForm/DynamicForm'; +import DynamicForm from '../DynamicForm/DynamicForm'; import './BlockConfig.scss'; @@ -16,14 +16,11 @@ const b = block('block-config'); interface BlockConfigProps extends ClassNameProps {} const BlockConfig: React.FC<BlockConfigProps> = ({className}) => { - const {selectedBlock} = useEditorStore(); - const {config, blocks, subBlocks, updateField} = useContentConfigStore(); + const {selectedBlock, content, blocks, subBlocks, updateField} = useMainEditorStore(); - const currentBlockPath = selectedBlock?.path - ? generateChildrenPathFromArray(selectedBlock?.path) - : '[]'; + const currentBlockPath = selectedBlock ? generateChildrenPathFromArray(selectedBlock) : '[]'; - const currentConfig = _.get(config.blocks, currentBlockPath || ''); + const currentConfig = _.get(content.blocks, currentBlockPath || ''); const currentSchema = [...blocks, ...subBlocks].find(({type}) => type === currentConfig?.type); const onUpdate = (key: string, value: DynamicFormValue) => { diff --git a/src/editor-v2/components/BlocksList/BlocksList.tsx b/src/editor-v2/components/BlocksList/BlocksList.tsx index 9f9f08d304..a6f6dcce5d 100644 --- a/src/editor-v2/components/BlocksList/BlocksList.tsx +++ b/src/editor-v2/components/BlocksList/BlocksList.tsx @@ -4,11 +4,9 @@ import {Grip, Plus} from '@gravity-ui/icons'; import {Button, Card, Icon} from '@gravity-ui/uikit'; import _ from 'lodash'; -import {ActionTypes, ItemConfig} from '../../../common/types'; +import {ItemConfig} from '../../../common/types'; import {block} from '../../../utils'; -import {useContentConfigStore} from '../../context/contentConfig'; -import {useEditorStore} from '../../context/editorContext'; -import {useMessageSender} from '../../context/messagesContext'; +import {useMainEditorStore} from '../../context/editorStore'; import './BlocksList.scss'; @@ -23,20 +21,18 @@ interface BlockGroups { } const BlocksList = (_p: PropsWithChildren<BlocksListProps>) => { - const {blocks, insertBlock} = useContentConfigStore(); - const {selectedBlock} = useEditorStore(); - const sendMessage = useMessageSender(); + const {blocks, selectedBlock, insertBlock, enableInsertMode} = useMainEditorStore(); const onMouseDown = useCallback( (blockType: string) => { - sendMessage({type: ActionTypes.InsertModeEnable, payload: {blockType}}); + enableInsertMode(blockType); }, - [sendMessage], + [enableInsertMode], ); const onAddClick = useCallback( (type: string) => { - const path = selectedBlock ? selectedBlock.path : [0]; + const path = selectedBlock || [0]; return insertBlock(path, type); }, [insertBlock, selectedBlock], diff --git a/src/editor-v2/components/DynamicForm/DynamicForm.tsx b/src/editor-v2/components/DynamicForm/DynamicForm.tsx index 8bef3e94dc..722aa84118 100644 --- a/src/editor-v2/components/DynamicForm/DynamicForm.tsx +++ b/src/editor-v2/components/DynamicForm/DynamicForm.tsx @@ -2,7 +2,7 @@ import React, {useCallback} from 'react'; import _ from 'lodash'; -import {ConfigInput} from '../../../common/types'; +import {ConfigInput, DynamicFormValue} from '../../../common/types'; import {ClassNameProps, PageContent} from '../../../models'; import {block} from '../../../utils'; @@ -20,8 +20,6 @@ import './DynamicForm.scss'; const b = block('dynamic-form'); -export type DynamicFormValue = string | number | [] | object | boolean | PageContent | undefined; - interface DynamicFormProps extends ClassNameProps { blockConfig: Array<ConfigInput>; contentConfig: PageContent; diff --git a/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx b/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx index 823665bbf5..185de27d5f 100644 --- a/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx +++ b/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx @@ -3,11 +3,11 @@ import React, {useCallback} from 'react'; import {Plus} from '@gravity-ui/icons'; import {Button, Card, Icon} from '@gravity-ui/uikit'; -import {ArrayObjectInput, ArrayTextInput} from '../../../../../common/types'; +import {ArrayObjectInput, ArrayTextInput, DynamicFormValue} from '../../../../../common/types'; import {ClassNameProps, PageContent} from '../../../../../models'; import {block} from '../../../../../utils'; import {removeFromArray, swapArrayItems} from '../../../../utils'; -import DynamicForm, {DynamicFormValue} from '../../DynamicForm'; +import DynamicForm from '../../DynamicForm'; import FieldBase from '../../FieldBase/FieldBase'; import Text from '../Text/Text'; diff --git a/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx b/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx index ff8bf177c5..1d012eb2d9 100644 --- a/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx +++ b/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx @@ -2,10 +2,10 @@ import React from 'react'; import {Card} from '@gravity-ui/uikit'; -import {ConfigInput} from '../../../../../common/types'; +import {ConfigInput, DynamicFormValue} from '../../../../../common/types'; import {ClassNameProps, PageContent} from '../../../../../models'; import {block} from '../../../../../utils'; -import DynamicForm, {DynamicFormValue} from '../../DynamicForm'; +import DynamicForm from '../../DynamicForm'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; import './Object.scss'; diff --git a/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx b/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx index 4dafbe0793..5db8644b96 100644 --- a/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx +++ b/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx @@ -2,10 +2,10 @@ import React, {useCallback, useMemo, useState} from 'react'; import {Card, RadioButton} from '@gravity-ui/uikit'; -import {OneOfInput} from '../../../../../common/types'; +import {DynamicFormValue, OneOfInput} from '../../../../../common/types'; import {ClassNameProps, PageContent} from '../../../../../models'; import {block} from '../../../../../utils'; -import DynamicForm, {DynamicFormValue} from '../../DynamicForm'; +import DynamicForm from '../../DynamicForm'; import FieldBase from '../../FieldBase/FieldBase'; import './OneOf.scss'; diff --git a/src/editor-v2/components/GlobalConfig/GlobalConfig.tsx b/src/editor-v2/components/GlobalConfig/GlobalConfig.tsx index e203439d21..3fb4e55007 100644 --- a/src/editor-v2/components/GlobalConfig/GlobalConfig.tsx +++ b/src/editor-v2/components/GlobalConfig/GlobalConfig.tsx @@ -1,9 +1,10 @@ import React from 'react'; +import {DynamicFormValue} from '../../../common/types'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; -import {useContentConfigStore} from '../../context/contentConfig'; -import DynamicForm, {DynamicFormValue} from '../DynamicForm/DynamicForm'; +import {useMainEditorStore} from '../../context/editorStore'; +import DynamicForm from '../DynamicForm/DynamicForm'; import './GlobalConfig.scss'; @@ -12,7 +13,7 @@ const b = block('global-config'); interface GlobalConfigProps extends ClassNameProps {} const GlobalConfig: React.FC<GlobalConfigProps> = ({className}) => { - const {global, updateField, config} = useContentConfigStore(); + const {global, content, updateField} = useMainEditorStore(); const onUpdate = (key: string, value: DynamicFormValue) => { updateField(key, value); @@ -21,7 +22,7 @@ const GlobalConfig: React.FC<GlobalConfigProps> = ({className}) => { return ( <div className={b(null, className)}> <div className={b('title')}>Global Config</div> - <DynamicForm contentConfig={config} blockConfig={global} onUpdate={onUpdate} /> + <DynamicForm contentConfig={content} blockConfig={global} onUpdate={onUpdate} /> </div> ); }; diff --git a/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx b/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx index e48ed564a6..6bd08ebda1 100644 --- a/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx +++ b/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx @@ -1,11 +1,12 @@ -import React, {useContext} from 'react'; +import React, {useCallback, useContext, useState} from 'react'; import {Loader} from '@gravity-ui/uikit'; +import {usePostMessageAPIListener} from '../../../common/postMessage'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; -import {useEditorStore} from '../../context/editorContext'; -import {IframeContext, useIframeStore} from '../../context/iframeContext'; +import {useMainEditorStore} from '../../context/editorStore'; +import {IframeContext} from '../../context/iframeContext'; import Overlay from '../Overlay/Overlay'; import TopBar from '../TopBar/TopBar'; @@ -16,9 +17,24 @@ const b = block('middle-screen'); interface MiddleScreenProps extends ClassNameProps {} const MiddleScreen: React.FC<MiddleScreenProps> = ({className}) => { - const {url, height, zoom, decreaseZoom, increaseZoom, setZoom} = useIframeStore(); - const {initialized} = useEditorStore(); - const {setIframeElement} = useContext(IframeContext); + const {zoom, initialized, setZoom, decreaseZoom, increaseZoom} = useMainEditorStore(); + const {url, setIframeElement} = useContext(IframeContext); + const [height, setHeight] = useState(0); + + const onResize = useCallback( + (newHeight: number) => { + setHeight(newHeight + 500); + }, + [setHeight], + ); + + usePostMessageAPIListener('ON_RESIZE', ({height: newHeight}) => { + onResize(newHeight); + }); + + usePostMessageAPIListener('ON_INIT', ({height: newHeight}) => { + onResize(newHeight); + }); return ( <div className={b(null, className)}> @@ -40,11 +56,7 @@ const MiddleScreen: React.FC<MiddleScreenProps> = ({className}) => { }} > <iframe - ref={(element) => { - if (element) { - setIframeElement(element); - } - }} + ref={(instance) => instance && setIframeElement(instance)} className={b('iframe')} src={url} height={`${height}px`} diff --git a/src/editor-v2/components/Overlay/Overlay.scss b/src/editor-v2/components/Overlay/Overlay.scss index 61e1b68a86..7d946ec711 100644 --- a/src/editor-v2/components/Overlay/Overlay.scss +++ b/src/editor-v2/components/Overlay/Overlay.scss @@ -22,11 +22,16 @@ $block: '.#{$ns}overlay'; border: 3px var(--g-color-line-brand) solid; box-sizing: border-box; box-shadow: 4px 4px 8px 0 var(--g-color-sfx-shadow); + + &_hover { + opacity: 0.5; + } } &__line { position: absolute; - border: $border-2; + //border: $border-2; + box-sizing: border-box; &_position { &_top { diff --git a/src/editor-v2/components/Overlay/Overlay.tsx b/src/editor-v2/components/Overlay/Overlay.tsx index ff231cc9f9..51722ef3a9 100644 --- a/src/editor-v2/components/Overlay/Overlay.tsx +++ b/src/editor-v2/components/Overlay/Overlay.tsx @@ -3,18 +3,10 @@ import React, {useCallback, useState} from 'react'; import {Copy, Grip, TrashBin} from '@gravity-ui/icons'; import {Button, Icon} from '@gravity-ui/uikit'; -import { - ActionTypes, - InsertModeDisableAction, - OverlayModeOnMoveAction, - ReorderModeDisableAction, -} from '../../../common/types'; +import {usePostMessageAPIListener} from '../../../common/postMessage'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; -import {useContentConfigStore} from '../../context/contentConfig'; -import {useEditorStore} from '../../context/editorContext'; -import {useIframeStore} from '../../context/iframeContext'; -import {useMessageObserver, useMessageSender} from '../../context/messagesContext'; +import {useMainEditorStore} from '../../context/editorStore'; import './Overlay.scss'; @@ -31,54 +23,59 @@ interface InsertLineProps { } const Overlay: React.FC<OverlayProps> = ({className}) => { - const {selectedBlock} = useEditorStore(); + const { + height, + selectedBlock, + setSelectedBlock, + deleteBlock, + duplicateBlock, + manipulateOverlayMode, + enableReorderMode, + } = useMainEditorStore(); const [insertLineBox, setInsertLineBox] = useState<InsertLineProps | undefined>(undefined); - const {height} = useIframeStore(); - const {deleteBlock, duplicateBlock} = useContentConfigStore(); - const sendMessage = useMessageSender(); + const [hoverBorders, setHoverBorders] = useState<DOMRect | null>(null); + const [blockBorders, setBlockBorders] = useState<DOMRect | null>(null); const margin = 0; - useMessageObserver<OverlayModeOnMoveAction>(ActionTypes.OverlayModeOnMove, (payload) => { - if (payload && payload.block) { - const {rect, cursorPosition} = payload.block; + usePostMessageAPIListener('ON_HOVER_BLOCK', ({rect, position}) => { + setHoverBorders(rect || null); + if (rect && position) { setInsertLineBox({ left: rect.x, top: rect.y, height: rect.height, width: rect.width, - position: cursorPosition, + position: position, }); } }); - useMessageObserver<InsertModeDisableAction>(ActionTypes.InsertModeDisable, () => { - setInsertLineBox(undefined); + usePostMessageAPIListener('ON_CLICK_BLOCK', ({rect, path}) => { + setSelectedBlock(path); + setBlockBorders(rect || null); }); - useMessageObserver<ReorderModeDisableAction>(ActionTypes.ReorderModeDisable, () => { - setInsertLineBox(undefined); + usePostMessageAPIListener('ON_RESIZE_BLOCK', ({rect}) => { + setBlockBorders(rect || null); }); const onMouseDown = useCallback(() => { if (selectedBlock) { - sendMessage({ - type: ActionTypes.ReorderModeEnable, - payload: {path: selectedBlock.path}, - }); + enableReorderMode(selectedBlock); } - }, [selectedBlock, sendMessage]); + }, [enableReorderMode, selectedBlock]); return ( <div className={b(null, className)} style={{height: `${height}px`}}> - {selectedBlock ? ( + {blockBorders ? ( <div className={b('border')} style={{ - top: selectedBlock.rect.top - margin, - left: selectedBlock.rect.left - margin, - width: selectedBlock.rect.width + margin * 2, - height: selectedBlock.rect.height + margin * 2, + top: blockBorders.top - margin, + left: blockBorders.left - margin, + width: blockBorders.width + margin * 2, + height: blockBorders.height + margin * 2, }} > <div className={b('actions')}> @@ -97,7 +94,7 @@ const Overlay: React.FC<OverlayProps> = ({className}) => { className={b('action-button')} size={'m'} view={'action'} - onClick={() => selectedBlock && deleteBlock(selectedBlock.path)} + onClick={() => selectedBlock && deleteBlock(selectedBlock)} > <Icon data={TrashBin} size={18} /> </Button> @@ -106,23 +103,34 @@ const Overlay: React.FC<OverlayProps> = ({className}) => { className={b('action-button')} size={'m'} view={'action'} - onClick={() => selectedBlock && duplicateBlock(selectedBlock.path)} + onClick={() => selectedBlock && duplicateBlock(selectedBlock)} > <Icon data={Copy} size={18} /> </Button> </div> </div> ) : null} - {insertLineBox ? ( + {hoverBorders ? ( + <div + className={b('border', {hover: true})} + style={{ + top: hoverBorders.top - margin, + left: hoverBorders.left - margin, + width: hoverBorders.width + margin * 2, + height: hoverBorders.height + margin * 2, + }} + ></div> + ) : null} + {manipulateOverlayMode && hoverBorders && insertLineBox ? ( <div className={b('line', { position: insertLineBox.position, })} style={{ - top: insertLineBox.top, - left: insertLineBox.left, - width: insertLineBox.width, - height: insertLineBox.height, + top: hoverBorders.top, + left: hoverBorders.left, + width: hoverBorders.width, + height: hoverBorders.height, }} /> ) : null} diff --git a/src/editor-v2/components/Source/Source.tsx b/src/editor-v2/components/Source/Source.tsx index a9e065370a..5e56661de9 100644 --- a/src/editor-v2/components/Source/Source.tsx +++ b/src/editor-v2/components/Source/Source.tsx @@ -5,8 +5,8 @@ import {Button, Icon, TextInput} from '@gravity-ui/uikit'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; -import {useEditorStore} from '../../context/editorContext'; -import {IframeContext, useIframeStore} from '../../context/iframeContext'; +import {useMainEditorStore} from '../../context/editorStore'; +import {IframeContext} from '../../context/iframeContext'; import './Source.scss'; @@ -15,9 +15,8 @@ const b = block('source'); interface SourceProps extends ClassNameProps {} const Source: React.FC<SourceProps> = ({className}) => { - const {url, setUrl} = useIframeStore(); - const {resetInitialize} = useEditorStore(); - const {disableUrlField} = useContext(IframeContext); + const {resetInitialize} = useMainEditorStore(); + const {disableUrlField, url, setUrl} = useContext(IframeContext); const onUpdateUrl = useCallback( (value: string) => { diff --git a/src/editor-v2/components/SourceCode/SourceCode.tsx b/src/editor-v2/components/SourceCode/SourceCode.tsx index e31c840723..94e84a2c37 100644 --- a/src/editor-v2/components/SourceCode/SourceCode.tsx +++ b/src/editor-v2/components/SourceCode/SourceCode.tsx @@ -14,7 +14,7 @@ import yaml from 'js-yaml'; import {ClassNameProps, PageContent} from '../../../models'; import {block} from '../../../utils'; -import {useContentConfigStore} from '../../context/contentConfig/hooks/useContentConfigStore'; +import {useMainEditorStore} from '../../context/editorStore'; import './SourceCode.scss'; @@ -23,7 +23,7 @@ const b = block('source-code'); interface SourceCodeProps extends ClassNameProps {} const SourceCode: React.FC<SourceCodeProps> = ({className}) => { - const {config, setConfig} = useContentConfigStore(); + const {content, setContent} = useMainEditorStore(); const [isOpen, setIsOpen] = useState(false); const [tempConfig, setTempConfig] = useState(''); const [format, setFormat] = useState<'yaml' | 'json'>('yaml'); @@ -43,13 +43,13 @@ const SourceCode: React.FC<SourceCodeProps> = ({className}) => { } if (object) { - setConfig(object as PageContent); + setContent(object as PageContent); } setIsOpen(false); }; - const text = format === 'yaml' ? yaml.dump(config) : JSON.stringify(config, null, 2); + const text = format === 'yaml' ? yaml.dump(content) : JSON.stringify(content, null, 2); return ( <div className={b(null, className)}> diff --git a/src/editor-v2/components/StoreViewer/StoreViewer.scss b/src/editor-v2/components/StoreViewer/StoreViewer.scss new file mode 100644 index 0000000000..93a126da99 --- /dev/null +++ b/src/editor-v2/components/StoreViewer/StoreViewer.scss @@ -0,0 +1,39 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; + +$block: '.#{$ns}store-viewer'; + +#{$block} { + background-color: var(--g-color-base-float-heavy); + border-radius: 12px; + border: 1px solid var(--g-color-line-generic); + overflow: hidden; + opacity: 0.3; + + &:hover { + opacity: 1; + } + + &_open { + opacity: 1; + + #{$block}__box { + display: block; + } + } + + &__head { + padding: 12px; + } + + &__box { + max-height: 80vh; + overflow: scroll; + display: none; + min-width: 400px; + max-width: 80vw; + padding: 12px; + background-color: var(--g-color-base-float); + white-space: pre; + } +} diff --git a/src/editor-v2/components/StoreViewer/StoreViewer.tsx b/src/editor-v2/components/StoreViewer/StoreViewer.tsx new file mode 100644 index 0000000000..c45f091e31 --- /dev/null +++ b/src/editor-v2/components/StoreViewer/StoreViewer.tsx @@ -0,0 +1,40 @@ +import React, {useState} from 'react'; + +import {Code} from '@gravity-ui/icons'; +import {Button, Icon} from '@gravity-ui/uikit'; +import ReactJson from 'react-json-view'; + +import {ClassNameProps} from '../../../models'; +import {block} from '../../../utils'; +import {removeFn} from '../../../utils/store'; + +import './StoreViewer.scss'; + +const b = block('store-viewer'); + +interface StoreViewerProps extends ClassNameProps { + store: object; +} + +const StoreViewer: React.FC<StoreViewerProps> = ({store, className}) => { + const [open, setOpen] = useState(false); + return ( + <div className={b({open}, className)}> + <div className={b('head')}> + <Button view="action" onClick={() => setOpen(!open)}> + <Icon data={Code} /> + </Button> + </div> + <div className={b('box')}> + <ReactJson + style={{backgroundColor: 'transparent'}} + src={removeFn(store)} + collapsed + theme="bright:inverted" + /> + </div> + </div> + ); +}; + +export default StoreViewer; diff --git a/src/editor-v2/components/Tree/Tree.tsx b/src/editor-v2/components/Tree/Tree.tsx index 68a38be011..a8874e8c28 100644 --- a/src/editor-v2/components/Tree/Tree.tsx +++ b/src/editor-v2/components/Tree/Tree.tsx @@ -5,7 +5,7 @@ import {Button, Card, Icon} from '@gravity-ui/uikit'; import {ItemConfig} from '../../../common/types'; import {block} from '../../../utils'; -import {useContentConfigStore} from '../../context/contentConfig'; +import {useMainEditorStore} from '../../context/editorStore'; import './Tree.scss'; @@ -36,9 +36,9 @@ const generateTree = (items: TreeItem[]): TreeItem[] => { }; const Tree = (_p: PropsWithChildren<TreeProps>) => { - const {config, resetBlocks} = useContentConfigStore(); + const {content, resetBlocks} = useMainEditorStore(); - const blockTree = generateTree(config.blocks); + const blockTree = generateTree(content.blocks); const renderTree = (items: TreeItem[], deepLevel = 0) => { return items.map(({type, children}, index) => ( diff --git a/src/editor-v2/containers/Editor/Editor.scss b/src/editor-v2/containers/Editor/Editor.scss index 18030f4fc5..58ed10c8e2 100644 --- a/src/editor-v2/containers/Editor/Editor.scss +++ b/src/editor-v2/containers/Editor/Editor.scss @@ -41,4 +41,11 @@ $block: '.#{$ns}editor'; width: 100%; z-index: 100; } + + &__debug-store { + position: fixed; + right: 8px; + bottom: 8px; + z-index: 1000; + } } diff --git a/src/editor-v2/containers/Editor/Editor.tsx b/src/editor-v2/containers/Editor/Editor.tsx index 406d758842..e7fd9b9ac5 100644 --- a/src/editor-v2/containers/Editor/Editor.tsx +++ b/src/editor-v2/containers/Editor/Editor.tsx @@ -1,17 +1,14 @@ import React, {useCallback} from 'react'; -import {ActionTypes} from '../../../common/types'; import {PageContent} from '../../../models'; import {block} from '../../../utils'; import BigOverlay from '../../components/BigOverlay/BigOverlay'; import MiddleScreen from '../../components/MiddleScreen/MiddleScreen'; import {Panels} from '../../components/Panels/Panels'; import {Sidebar} from '../../components/Sidebar/Sidebar'; -import {ContentConfigProvider} from '../../context/contentConfig'; -import {EditorProvider, useEditorStore} from '../../context/editorContext'; +import StoreViewer from '../../components/StoreViewer/StoreViewer'; +import {MainEditorStoreProvider, useMainEditorStore} from '../../context/editorStore'; import {IframeProvider} from '../../context/iframeContext'; -import {PostMessageProvider} from '../../context/messagesContext'; -import {useMessageSender} from '../../context/messagesContext/hooks/useMessageSender'; import useAdminInitialize from './hooks/useAdminInitialize'; @@ -20,48 +17,31 @@ import './Editor.scss'; const b = block('editor'); interface EditorViewProps { - content: PageContent; onUpdate?: (pageContent: PageContent) => void; initialUrl: string; disableUrlField?: boolean; } const EditorView = (_props: EditorViewProps) => { - const {manipulateOverlayMode} = useEditorStore(); - const sendMessage = useMessageSender(); + const store = useMainEditorStore(); + const {manipulateOverlayMode, disableMode} = store; useAdminInitialize(); // Disable insert mode on any MouseUp event // Maybe should be attached to body const onMouseUp = useCallback( - (e: React.MouseEvent) => { - if (manipulateOverlayMode === 'insert') { - e.preventDefault(); - sendMessage({type: ActionTypes.InsertModeDisable, payload: undefined}); - } - if (manipulateOverlayMode === 'reorder') { - e.preventDefault(); - sendMessage({type: ActionTypes.ReorderModeDisable, payload: undefined}); - } - }, - [manipulateOverlayMode, sendMessage], - ); - - const onMouseMove = useCallback( (e: React.MouseEvent) => { if (manipulateOverlayMode) { - sendMessage({ - type: ActionTypes.OverlayModeOnMove, - payload: {cursor: {x: e.clientX, y: e.clientY}}, - }); + e.preventDefault(); + disableMode(); } }, - [manipulateOverlayMode, sendMessage], + [disableMode, manipulateOverlayMode], ); return ( - <div className={b()} onMouseUp={onMouseUp} onMouseMove={onMouseMove}> + <div className={b()} onMouseUp={onMouseUp}> <div className={b('body')}> <Panels left={<Sidebar position={'left'} />} @@ -70,20 +50,17 @@ const EditorView = (_props: EditorViewProps) => { /> </div> <BigOverlay className={b('overlay')} /> + <StoreViewer className={b('debug-store')} store={store} /> </div> ); }; export const Editor = (props: EditorViewProps) => { return ( - <EditorProvider> - <IframeProvider initialUrl={props.initialUrl} disableUrlField={props.disableUrlField}> - <ContentConfigProvider onUpdate={props.onUpdate} content={props.content}> - <PostMessageProvider> - <EditorView {...props} /> - </PostMessageProvider> - </ContentConfigProvider> - </IframeProvider> - </EditorProvider> + <IframeProvider initialUrl={props.initialUrl} disableUrlField={props.disableUrlField}> + <MainEditorStoreProvider> + <EditorView {...props} /> + </MainEditorStoreProvider> + </IframeProvider> ); }; diff --git a/src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx b/src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx index 9bc69e9f4c..bdf1e929db 100644 --- a/src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx +++ b/src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx @@ -1,39 +1,54 @@ -import {useContext, useEffect} from 'react'; - -import {ActionTypes, IframeReadyAction} from '../../../../common/types'; -import {ContentConfigContext, useContentConfigStore} from '../../../context/contentConfig'; -import {useEditorStore} from '../../../context/editorContext'; -import {useMessageObserver, useMessageSender} from '../../../context/messagesContext'; +import {usePostMessageAPIListener} from '../../../../common/postMessage'; +import {useMainEditorStore} from '../../../context/editorStore'; +import {usePostMessageEvents} from '../../../hooks/usePostMessageEvents'; const useAdminInitialize = () => { - const {state: contentConfigState} = useContext(ContentConfigContext); - const {config} = useContentConfigStore(); - const {initialized} = useEditorStore(); - const sendMessage = useMessageSender(); + const {requestPostMessage} = usePostMessageEvents(); + const { + initialize, + setConfig, + setContent, + manipulateOverlayMode, + disableMode, + insertBlock, + reorderBlock, + setSelectedBlock, + preInsertBlockType, + preReorderBlockPath, + } = useMainEditorStore(); + + usePostMessageAPIListener('ON_INIT', () => { + initialize(); + requestPostMessage('GET_SUPPORTED_BLOCKS', {}); + requestPostMessage('GET_CURRENT_CONTENT', {}); + }); + + usePostMessageAPIListener('ON_INITIAL_CONTENT', (data) => { + setContent(data); + }); - useMessageObserver<IframeReadyAction>( - ActionTypes.IframeReady, - () => { - if (sendMessage && !initialized) { - sendMessage({type: ActionTypes.EditorReady, payload: undefined}); - sendMessage( - {type: ActionTypes.UpdateConfigs, payload: {content: config}}, - {direction: 'pc'}, - ); - } - }, - [sendMessage], - ); + usePostMessageAPIListener('ON_SUPPORTED_BLOCKS', (data) => { + setConfig(data); + }); - // Update configs for both instances on any changes - useEffect( - () => - contentConfigState && - contentConfigState.subscribe((state) => { - sendMessage({type: ActionTypes.UpdateConfigs, payload: {content: state.config}}); - }), - [sendMessage, contentConfigState], - ); + usePostMessageAPIListener('ON_MOUSE_UP', ({path, position}) => { + if (manipulateOverlayMode === 'insert' && path && position && preInsertBlockType) { + insertBlock( + path, + preInsertBlockType, + ['left', 'top'].includes(position) ? 'prepend' : 'append', + ); + } + if (manipulateOverlayMode === 'reorder' && path && position && preReorderBlockPath) { + reorderBlock( + preReorderBlockPath, + path, + ['left', 'top'].includes(position) ? 'prepend' : 'append', + ); + setSelectedBlock(path); + } + disableMode(); + }); }; export default useAdminInitialize; diff --git a/src/editor-v2/containers/__stories__/Editor.stories.tsx b/src/editor-v2/containers/__stories__/Editor.stories.tsx index 3ade46fb64..9d5ee69592 100644 --- a/src/editor-v2/containers/__stories__/Editor.stories.tsx +++ b/src/editor-v2/containers/__stories__/Editor.stories.tsx @@ -2,11 +2,8 @@ import React from 'react'; import {Meta, StoryFn} from '@storybook/react'; -import {PageContent} from '../../../models'; import {Editor} from '../Editor/Editor'; -import data from './data.json'; - export default { title: 'Editor/Main 2.0', component: Editor, @@ -15,11 +12,7 @@ export default { const DefaultTemplate: StoryFn = (args) => { return ( <div style={{height: '100vh', width: '100vw'}}> - <Editor - {...args} - content={data as unknown as PageContent} - initialUrl={'http://localhost:3000'} - /> + <Editor {...args} initialUrl={'http://localhost:3000'} /> </div> ); }; diff --git a/src/editor-v2/containers/__stories__/data.json b/src/editor-v2/containers/__stories__/data.json deleted file mode 100644 index 396e3394e0..0000000000 --- a/src/editor-v2/containers/__stories__/data.json +++ /dev/null @@ -1,335 +0,0 @@ -{ - "blocks": [ - { - "type": "header-block", - "title": "Yandex Open Source", - "description": "<p>Мы в Яндексе верим, что вклад в опенсорс — это вклад в технологическую эволюцию: без открытости, совместной работы и поддержки развитие IT‑индустрии сильно затруднено. Уже много лет мы используем в своих продуктах сторонние открытые технологии, а также делимся собственными и активно вовлекаем в их развитие разработчиков по всему миру.</p>", - "width": "s", - "verticalOffset": "l", - "offset": "default", - "resetPaddings": true, - "background": { - "image": { - "mobile": "https://storage.yandexcloud.net/yandex-opensource/pages/index/yos-index-cover-m.png", - "desktop": "https://storage.yandexcloud.net/yandex-opensource/pages/index/yos-index-cover.png" - }, - "color": "#EFF2F8", - "fullWidth": false, - "fullWidthMedia": true - } - }, - { - "type": "extended-features-block", - "title": { - "text": "Почему мы выкладываем наши технологии в открытый доступ?" - }, - "items": [ - { - "title": "Ответственность", - "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-01.svg", - "text": "<p>Мы верим, что вкладываться в развитие опенсорс‑технологий — это ответственность каждого технологического лидера на рынке. Без опенсорс‑решений не появились бы многие продукты и сервисы не только Яндекса, но и других крупных компаний, и мы хотим отдавать обратно, делиться теми нашими решениями, которые, как мы считаем, принесут реальную пользу.</p>" - }, - { - "title": "Польза для сообщества", - "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-02.svg", - "text": "<p>Технологии, которые мы разрабатываем, ежедневно помогают нам эффективно решать огромное количество самых разных задач в наших сервисах. Мы знаем, что разработчики вне Яндекса часто сталкиваются с теми же самыми задачами — и верим, что наши технологии могут быть полезны и им.</p>" - }, - { - "title": "Качество сервисов", - "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-03.svg", - "text": "<p>Для нас важно разрабатывать и использовать только качественные технологические решения. В особенности это касается опенсорса: зная, что наши решения увидят и будут использовать другие, мы уделяем их качеству особое внимание. А уже в открытом доступе у технологии больше шансов развиваться и улучшаться — в том числе, при участии сообщества разработчиков.</p>" - }, - { - "title": "Бизнес‑потенциал", - "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-04.svg", - "text": "<p>Мы верим, что при условии роста популярности наших решений и спроса на них со стороны сообщества, то, что мы выкладываем в опенсорс, может далее стать для нас бизнесом. То, что мы выкладываем в опенсорс, можно использовать и во внешних коммерческих проектах.</p>" - }, - { - "title": "Поиск талантов", - "icon": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/os-index-icon-05.svg", - "text": "<p>Мы ценим каждого, кто вкладывается в сторонние опенсорс‑решения или делится с миром своими. Контрибьюторы в наши продукты нам особенно важны: среди них мы ищем и находим тех, кто сможет развивать технологии уже будучи частью команды Яндекса.</p>" - } - ] - }, - { - "type": "card-layout-block", - "animated": false, - "title": "Краткая история опенсорса в Яндексе", - "description": "<p>С начала истории развития опенсорса в Яндексе мы успели выложить в открытый доступ десятки собственных проектов, использовать в разработке наших продуктов внешние технологии, а также внесли существенный вклад в их развитие.</p>", - "colSizes": { - "all": 12, - "lg": 3, - "md": 4, - "sm": 6 - }, - "anchor": { - "url": "history", - "text": "history" - }, - "children": [ - { - "type": "layout-item", - "content": { - "title": "2010", - "text": "<p>Методология веб‑разработки БЭМ (Блок‑Элемент‑Модификатор) выходит в оперсорс</p>" - }, - "media": { - "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-01.png" - } - }, - { - "type": "layout-item", - "content": { - "title": "2012", - "text": "<p>Запуск Яндекс Браузера на базе Blink (Chromium)</p>" - }, - "media": { - "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-02.png" - } - }, - { - "type": "layout-item", - "content": { - "title": "2013", - "text": "<p>Яндекс начинает контрибьютить в ядро Linux</p>" - }, - "media": { - "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-03.png" - } - }, - { - "type": "layout-item", - "content": { - "title": "2016", - "text": "<p>Выход в опенсорс ClickHouse</p>\\n<p>Выход в опенсорс Hermione (с 2024 года — Testplane)</p>" - }, - "media": { - "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-2016.png" - } - }, - { - "type": "layout-item", - "content": { - "title": "2017", - "text": "<p>Выход в опенсорс CatBoost<br />\\nЯндекс начинает контрибьютить в PostgreSQL</p>" - }, - "media": { - "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-05.png" - } - }, - { - "type": "layout-item", - "content": { - "title": "2018", - "text": "<p>Выход в опенсорс Одиссея</p>\\n<p>Яндекс — топ‑контрибьютор в WAL‑G</p>" - }, - "media": { - "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-06.png" - } - }, - { - "type": "layout-item", - "content": { - "title": "2019", - "text": "<p>В Яндексе появляется команда разработки СУБД с открытым исходным кодом</p>\\n<p>Яндекс — спонсор разработки PostgreSQL</p>" - }, - "media": { - "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-07.png" - } - }, - { - "type": "layout-item", - "content": { - "title": "2020", - "text": "<p>Выход в опенсорс Testsuite</p>" - }, - "media": { - "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-2020.png" - } - }, - { - "type": "layout-item", - "content": { - "title": "2022", - "text": "<p>Яндекс — один из основных спонсоров разработки PostgreSQL</p>\\n<p>Выход в опенсорс YDB, userver, YaLM 100B, DivKit, Yatagan</p>\\n<p>Старт программы «Код для всех»</p>" - }, - "media": { - "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-09.png" - } - }, - { - "type": "layout-item", - "content": { - "title": "2023", - "text": "<p>Выход в опенсорс YTsaurus, Gravity UI, AppMetrica, Diplodoc, DataLens и счётчика Метрики</p>\\n<p>Старт Программы грантов Yandex Open Source</p>" - }, - "media": { - "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-2023.png" - } - }, - { - "type": "layout-item", - "content": { - "title": "2024", - "text": "<p>Первый Yandex Open Source Jam</p>\\n<p>Выход в опенсорс YaFSDP</p>" - }, - "media": { - "image": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-timeline-2024.png" - } - } - ] - }, - { - "type": "card-layout-block", - "title": "Наши проекты", - "description": "<p>В Яндексе мы разрабатываем и развиваем технологические решения самых разных сфер применения, размеров и сложности. Поэтому и в открытый доступ попадают самые разные проекты — главное, чтобы они приносили пользу не только нам, но и другим.</p>", - "colSizes": { - "all": 12, - "lg": 3, - "md": 4, - "sm": 6 - }, - "anchor": { - "url": "projects", - "text": "projects" - }, - "children": [ - { - "type": "background-card", - "title": "YDB", - "text": "<p>Отказоустойчивая распределённая SQL база данных</p>", - "backgroundColor": "#2399FF", - "background": { - "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-ydb.png", - "alt": "card-background" - }, - "paddingBottom": "m", - "theme": "dark" - }, - { - "type": "background-card", - "title": "YTsaurus", - "text": "<p>Платформа для хранения и обработки больших данных</p>", - "backgroundColor": "#FFB23E", - "background": { - "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-yt.png", - "alt": "card-background" - }, - "paddingBottom": "m", - "theme": "light" - }, - { - "type": "background-card", - "title": "GravityUI", - "text": "<p>Библиотеки для создания интерфейсов</p>", - "backgroundColor": "#262626", - "background": { - "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-gui.png", - "alt": "card-background" - }, - "paddingBottom": "m", - "theme": "dark" - }, - { - "type": "background-card", - "title": "DivKit", - "text": "<p>Фреймворк для server‑driven интерфейсов</p>", - "backgroundColor": "#F1F1F1", - "background": { - "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-dk.png", - "alt": "card-background" - }, - "paddingBottom": "m", - "theme": "light" - }, - { - "type": "background-card", - "title": "Diplodoc", - "text": "<p>Платформа для написания документации в концепции Docs as Code</p>", - "backgroundColor": "#79F985", - "background": { - "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-dd.png", - "alt": "card-background" - }, - "paddingBottom": "m", - "theme": "light" - }, - { - "type": "background-card", - "title": "userver", - "text": "<p>Фреймворк для создания высоконагруженных приложений</p>", - "backgroundColor": "#FF9D73", - "background": { - "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-u.png", - "alt": "card-background" - }, - "paddingBottom": "m", - "theme": "light" - }, - { - "type": "background-card", - "title": "DataLens", - "text": "<p>BI-платформа для анализа и визуализации данных</p>", - "backgroundColor": "#FF7132", - "background": { - "src": "https://storage.yandexcloud.net/yandex-opensource/pages/index/os-index-card-dl.png", - "alt": "card-background" - }, - "paddingBottom": "m", - "theme": "dark" - }, - { - "type": "basic-card", - "title": "И это ещё не всё", - "text": "<p>Узнать про наши опенсорс‑проекты больше вы можете на другой странице</p>", - "border": "none", - "buttons": [ - { - "text": "Все проекты", - "primary": true, - "theme": "monochrome", - "size": "promo", - "url": "/projects" - } - ] - } - ] - }, - { - "largeMedia": true, - "mediaOnly": false, - "size": "l", - "type": "media-block", - "direction": "content-media", - "anchor": { - "url": "1", - "text": "Фильм" - }, - "title": "<p>Смотрите фильм с YaC 22</p>", - "description": "<p>Руководители опенсорс‑проектов рассказывают про историю и культуру открытого кода в Яндексе.</p>", - "media": { - "youtube": "https://www.youtube.com/watch?v=G7G286S8ntc", - "previewImg": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/video-cover.png" - }, - "disableShadow": true - }, - { - "type": "content-layout-block", - "size": "l", - "centered": true, - "textContent": { - "title": "", - "text": "", - "additionalInfo": "", - "buttons": [ - { - "text": "Сделано на GravityUI", - "theme": "monochrome", - "img": "https://storage.yandexcloud.net/cloud-www-assets/pages/open-source/open-source-gravity-ui-button.svg", - "url": "https://gravity-ui.com/" - } - ] - } - } - ] -} diff --git a/src/editor-v2/context/contentConfig/contentConfigContext.tsx b/src/editor-v2/context/contentConfig/contentConfigContext.tsx deleted file mode 100644 index 9ae9e2791b..0000000000 --- a/src/editor-v2/context/contentConfig/contentConfigContext.tsx +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Context for managing PC configuration (aka Yaml Configs) - **/ - -import React from 'react'; - -import {StoreApi} from 'zustand'; - -import {ContentConfigStore} from './store'; - -export interface ContentConfigContextProps { - state?: StoreApi<ContentConfigStore>; -} - -export const ContentConfigContext = React.createContext<ContentConfigContextProps>({}); diff --git a/src/editor-v2/context/contentConfig/contentConfigProvider.tsx b/src/editor-v2/context/contentConfig/contentConfigProvider.tsx deleted file mode 100644 index 18a7f6276d..0000000000 --- a/src/editor-v2/context/contentConfig/contentConfigProvider.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, {PropsWithChildren, useRef} from 'react'; - -import {StoreApi} from 'zustand'; - -import {PageContent} from '../../../models'; - -import {ContentConfigContext} from './contentConfigContext'; -import {ContentConfigStore, createContentConfigStore} from './store'; - -interface ContentConfigProviderProps extends PropsWithChildren { - content: PageContent; - onUpdate?: (pageContent: PageContent) => void; -} - -export const ContentConfigProvider = ({ - content, - onUpdate, - children, -}: ContentConfigProviderProps) => { - const storeRef = useRef<StoreApi<ContentConfigStore>>(); - - if (!storeRef.current) { - storeRef.current = createContentConfigStore({config: content}); - if (onUpdate) { - storeRef.current?.subscribe((state) => onUpdate(state.config)); - } - } - - return ( - <ContentConfigContext.Provider value={{state: storeRef.current}}> - {children} - </ContentConfigContext.Provider> - ); -}; diff --git a/src/editor-v2/context/contentConfig/hooks/useContentConfigStore.tsx b/src/editor-v2/context/contentConfig/hooks/useContentConfigStore.tsx deleted file mode 100644 index 40d57fd1ab..0000000000 --- a/src/editor-v2/context/contentConfig/hooks/useContentConfigStore.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import {useContext} from 'react'; - -import {useStore} from 'zustand'; - -import {ContentConfigContext} from '../contentConfigContext'; - -export const useContentConfigStore = () => { - const {state} = useContext(ContentConfigContext); - if (!state) { - throw new Error('Missing ContentConfigContext'); - } - return useStore(state); -}; - -export default useContentConfigStore; diff --git a/src/editor-v2/context/contentConfig/index.ts b/src/editor-v2/context/contentConfig/index.ts deleted file mode 100644 index 4772ca2998..0000000000 --- a/src/editor-v2/context/contentConfig/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './contentConfigContext'; -export * from './contentConfigProvider'; -export * from './hooks/useContentConfigStore'; -export * from './store'; diff --git a/src/editor-v2/context/contentConfig/store.ts b/src/editor-v2/context/contentConfig/store.ts deleted file mode 100644 index 8443cbcb2e..0000000000 --- a/src/editor-v2/context/contentConfig/store.ts +++ /dev/null @@ -1,209 +0,0 @@ -import _ from 'lodash'; - -import {ActionTypes, ConfigInput, ItemConfig, WithStoreReducer} from '../../../common/types'; -import {ConstructorBlock, PageContent} from '../../../models'; -import {DynamicFormValue} from '../../components/DynamicForm/DynamicForm'; -import { - duplicateArrayItem, - generateChildrenPathFromArray, - getDestinationShiftBeforeReorder, - insert, - isItemsNeighbours, - modifyObjectByPath, - removeFromArray, - reorderArrayItems, -} from '../../utils'; -import {initializeStore} from '../../utils/store'; - -export interface ContentConfigState { - config: PageContent; - blocks: Array<ItemConfig>; - subBlocks: Array<ItemConfig>; - global: Array<ConfigInput>; - - preInsertBlockType: string | null; - preReorderBlockPath: number[] | null; -} - -export interface ContentConfigMethods extends WithStoreReducer { - setConfig: (config: PageContent) => void; - setBlocks: (blocks: ItemConfig[]) => void; - setSubBlocks: (subBlocks: ItemConfig[]) => void; - insertBlock: (position: number[], blockType: string) => void; - deleteBlock: (path: number[]) => void; - duplicateBlock: (path: number[]) => void; - reorderBlock: (path: number[], destination: number[]) => void; - updateField: (path: string, value: DynamicFormValue) => void; - resetBlocks: () => void; -} - -export type ContentConfigStore = ContentConfigState & ContentConfigMethods; - -export const createContentConfigStore = initializeStore<ContentConfigState, ContentConfigMethods>( - { - config: {blocks: []}, - blocks: [], - subBlocks: [], - global: [], - preInsertBlockType: null, - preReorderBlockPath: null, - }, - (set, get) => ({ - setConfig: (config) => set((state) => ({...state, config})), - setBlocks: (blocks) => set((state) => ({...state, blocks})), - setSubBlocks: (subBlocks) => set((state) => ({...state, subBlocks})), - insertBlock: (arrayPath, blockType) => { - const blocksConfig = get().config.blocks; - const blocksData = get().blocks; - - const foundBlock = blocksData.find(({type}) => type === blockType); - const defaultValue = - foundBlock && foundBlock.schema.default - ? {...foundBlock.schema.default, type: blockType} - : {type: blockType}; - - const newBlocksConfig = modifyObjectByPath( - blocksConfig, - arrayPath, - (parentBlocks, index) => - insert(parentBlocks, index, defaultValue as ConstructorBlock), - ); - - set((state) => ({ - ...state, - config: {...state.config, blocks: newBlocksConfig}, - })); - }, - deleteBlock: (arrayPath) => { - const blocksConfig = get().config.blocks; - - const newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, removeFromArray); - - set((state) => ({ - ...state, - config: {...state.config, blocks: newBlocksConfig}, - })); - }, - duplicateBlock: (arrayPath) => { - const blocksConfig = get().config.blocks; - - const newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, duplicateArrayItem); - - set((state) => ({ - ...state, - config: {...state.config, blocks: newBlocksConfig}, - })); - }, - updateField: (path, value) => { - set((state) => { - const newConfig = _.set(state.config, path, value); - return { - ...state, - config: newConfig, - }; - }); - }, - reorderBlock: (arrayPath, destination) => { - let newBlocksConfig: ConstructorBlock[]; - const blocksConfig = get().config.blocks; - // Copy - const copiedBlock = _.get(blocksConfig, generateChildrenPathFromArray(arrayPath)); - - if (isItemsNeighbours(arrayPath, destination)) { - newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, (parentBlocks) => { - return reorderArrayItems( - parentBlocks, - arrayPath[arrayPath.length - 1], - destination[destination.length - 1], - ); - }); - } else { - const arrayDest = getDestinationShiftBeforeReorder(arrayPath, destination); - - // Delete - const blocksConfigWithoutBlock = modifyObjectByPath( - blocksConfig, - arrayPath, - removeFromArray, - ); - // Paste - newBlocksConfig = modifyObjectByPath( - blocksConfigWithoutBlock, - arrayDest, - (parentBlocks, index) => insert(parentBlocks, index, copiedBlock), - ); - } - - set((state) => ({ - ...state, - config: {...state.config, blocks: newBlocksConfig}, - })); - }, - resetBlocks: () => { - set((state) => ({ - ...state, - config: {...state.config, blocks: []}, - })); - }, - reducer: function (action) { - switch (action.type) { - // Insert Actions - case ActionTypes.InsertModeEnable: { - set((state) => ({ - ...state, - preInsertBlockType: action.payload.blockType, - })); - break; - } - case ActionTypes.InsertModeDisable: { - set((state) => ({ - ...state, - preInsertBlockType: null, - })); - break; - } - - case ActionTypes.InsertBlock: { - const {preInsertBlockType} = get(); - if (preInsertBlockType) { - const {path} = action.payload; - this.insertBlock(path, preInsertBlockType); - } - break; - } - // Reorder Actions - case ActionTypes.ReorderModeEnable: { - set((state) => ({ - ...state, - preReorderBlockPath: action.payload.path, - })); - break; - } - case ActionTypes.ReorderModeDisable: { - set((state) => ({ - ...state, - preReorderBlockPath: null, - })); - break; - } - case ActionTypes.ReorderBlocks: { - const {preReorderBlockPath} = get(); - if (preReorderBlockPath) { - const {path: destinationPath} = action.payload; - this.reorderBlock(preReorderBlockPath, destinationPath); - } - break; - } - case ActionTypes.BlocksConfigs: { - set((state) => ({ - ...state, - blocks: action.payload.blocks, - subBlocks: action.payload.subBlocks, - global: action.payload.global, - })); - break; - } - } - }, - }), -); diff --git a/src/editor-v2/context/editorContext/editorContext.tsx b/src/editor-v2/context/editorContext/editorContext.tsx deleted file mode 100644 index 23a9cce22f..0000000000 --- a/src/editor-v2/context/editorContext/editorContext.tsx +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Context for managing internal Editor states - * Same exist in PC - **/ - -import React from 'react'; - -import {StoreApi} from 'zustand'; - -import {EditorStore} from './store'; - -export interface EditorContextProps { - state?: StoreApi<EditorStore>; -} - -export const EditorContext = React.createContext<EditorContextProps>({}); diff --git a/src/editor-v2/context/editorContext/editorProvider.tsx b/src/editor-v2/context/editorContext/editorProvider.tsx deleted file mode 100644 index 64dcb447b4..0000000000 --- a/src/editor-v2/context/editorContext/editorProvider.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React, {PropsWithChildren, useRef} from 'react'; - -import {StoreApi} from 'zustand'; - -import {EditorContext} from './editorContext'; -import {EditorStore, createEditorStore} from './store'; - -export const EditorProvider = ({children}: PropsWithChildren) => { - const storeRef = useRef<StoreApi<EditorStore>>(); - - if (!storeRef.current) { - storeRef.current = createEditorStore(); - } - - return ( - <EditorContext.Provider value={{state: storeRef.current}}> - {children} - </EditorContext.Provider> - ); -}; diff --git a/src/editor-v2/context/editorContext/hooks/useEditorStore.tsx b/src/editor-v2/context/editorContext/hooks/useEditorStore.tsx deleted file mode 100644 index 4f2a346cde..0000000000 --- a/src/editor-v2/context/editorContext/hooks/useEditorStore.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import {useContext} from 'react'; - -import {useStore} from 'zustand'; - -import {EditorContext} from '../editorContext'; - -export const useEditorStore = () => { - const {state} = useContext(EditorContext); - - if (!state) { - throw new Error('Missing EditorContext'); - } - - return useStore(state); -}; - -export default useEditorStore; diff --git a/src/editor-v2/context/editorContext/index.ts b/src/editor-v2/context/editorContext/index.ts deleted file mode 100644 index c7b944eea3..0000000000 --- a/src/editor-v2/context/editorContext/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './editorContext'; -export * from './editorProvider'; -export * from './hooks/useEditorStore'; -export * from './store'; diff --git a/src/editor-v2/context/editorContext/store.ts b/src/editor-v2/context/editorContext/store.ts deleted file mode 100644 index 9c6085f756..0000000000 --- a/src/editor-v2/context/editorContext/store.ts +++ /dev/null @@ -1,101 +0,0 @@ -import {Action, ActionTypes, WithStoreReducer} from '../../../common/types'; -import {initializeStore} from '../../utils/store'; - -export interface EditorState { - manipulateOverlayMode: 'insert' | 'reorder' | false; - selectedBlock?: { - path: number[]; - rect: DOMRect; - }; - initialized: boolean; -} - -export interface EditorMethods extends WithStoreReducer { - resetInitialize: () => void; -} - -export type EditorStore = EditorState & EditorMethods; - -export const createEditorStore = initializeStore<EditorState, EditorMethods>( - { - manipulateOverlayMode: false, - initialized: false, - }, - (set, get) => ({ - resetInitialize: () => { - set((state) => ({ - ...state, - initialized: false, - })); - }, - reducer: (action: Action) => { - switch (action.type) { - case ActionTypes.IframeReady: { - set((state) => ({ - ...state, - initialized: true, - })); - break; - } - case ActionTypes.ReorderModeEnable: { - set((state) => ({ - ...state, - manipulateOverlayMode: 'reorder', - })); - break; - } - case ActionTypes.ReorderModeDisable: { - set((state) => ({ - ...state, - manipulateOverlayMode: false, - })); - break; - } - case ActionTypes.InsertModeEnable: { - set((state) => ({ - ...state, - manipulateOverlayMode: 'insert', - })); - break; - } - case ActionTypes.InsertModeDisable: { - set((state) => ({ - ...state, - manipulateOverlayMode: false, - })); - break; - } - case ActionTypes.SelectBlock: { - set((state) => ({ - ...state, - selectedBlock: { - path: action.payload.path, - rect: action.payload.rect, - }, - })); - break; - } - case ActionTypes.UpdateSelectedBlockRect: { - const {selectedBlock} = get(); - if (selectedBlock) { - set((state) => ({ - ...state, - selectedBlock: { - ...selectedBlock, - rect: action.payload.rect, - }, - })); - } - break; - } - case ActionTypes.ReorderBlocks: { - set((state) => ({ - ...state, - selectedBlock: undefined, - })); - break; - } - } - }, - }), -); diff --git a/src/editor-v2/context/editorStore/MainEditorStoreContext.tsx b/src/editor-v2/context/editorStore/MainEditorStoreContext.tsx new file mode 100644 index 0000000000..b14610b740 --- /dev/null +++ b/src/editor-v2/context/editorStore/MainEditorStoreContext.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +import {StoreApi} from 'zustand'; + +import {EditorStore, createEditorStore} from '../../../common/store'; + +export interface MainEditorStoreContextProps { + state: StoreApi<EditorStore>; +} + +export const MainEditorStoreContext = React.createContext<MainEditorStoreContextProps>({ + state: createEditorStore(), +}); diff --git a/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx b/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx new file mode 100644 index 0000000000..68b8e9908f --- /dev/null +++ b/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx @@ -0,0 +1,53 @@ +import React, {PropsWithChildren, useCallback, useContext, useEffect, useRef} from 'react'; + +import {StoreApi} from 'zustand'; + +import {EditorState, EditorStore, createEditorStore} from '../../../common/store'; +import {StoreSyncMessage} from '../../../common/types'; +import {removeFn} from '../../../utils/store'; +import {IframeContext} from '../iframeContext'; + +import {MainEditorStoreContext} from './MainEditorStoreContext'; + +interface MainEditorProviderProps extends PropsWithChildren {} + +export const MainEditorStoreProvider = ({children}: MainEditorProviderProps) => { + const {iframeElement} = useContext(IframeContext); + const storeRef = useRef<StoreApi<EditorStore>>(); + + const sendPostMessage = useCallback( + (data: EditorState) => { + const message: StoreSyncMessage = { + state: data, + }; + + if (iframeElement && iframeElement.contentWindow) { + iframeElement.contentWindow.postMessage(message, '*'); + } else { + // eslint-disable-next-line no-console + console.error('No Iframe element in Editor'); + } + }, + [iframeElement], + ); + + if (!storeRef.current) { + storeRef.current = createEditorStore(); + } + + useEffect(() => { + storeRef.current?.subscribe((state) => { + sendPostMessage(removeFn(state)); + }); + }, [sendPostMessage]); + + return ( + <MainEditorStoreContext.Provider + value={{ + state: storeRef.current, + }} + > + {children} + </MainEditorStoreContext.Provider> + ); +}; diff --git a/src/editor-v2/context/editorStore/hooks/useMainEditorStore.ts b/src/editor-v2/context/editorStore/hooks/useMainEditorStore.ts new file mode 100644 index 0000000000..7cd4000059 --- /dev/null +++ b/src/editor-v2/context/editorStore/hooks/useMainEditorStore.ts @@ -0,0 +1,10 @@ +import {useContext} from 'react'; + +import {useStore} from 'zustand'; + +import {MainEditorStoreContext} from '../MainEditorStoreContext'; + +export const useMainEditorStore = () => { + const {state} = useContext(MainEditorStoreContext); + return useStore(state); +}; diff --git a/src/editor-v2/context/editorStore/index.ts b/src/editor-v2/context/editorStore/index.ts new file mode 100644 index 0000000000..ced0a0fcd8 --- /dev/null +++ b/src/editor-v2/context/editorStore/index.ts @@ -0,0 +1,3 @@ +export * from './hooks/useMainEditorStore'; +export * from './MainEditorStoreContext'; +export * from './MainEditorStoreProvider'; diff --git a/src/editor-v2/context/iframeContext/hooks/useIframeStore.tsx b/src/editor-v2/context/iframeContext/hooks/useIframeStore.tsx deleted file mode 100644 index 87369816cc..0000000000 --- a/src/editor-v2/context/iframeContext/hooks/useIframeStore.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import {useContext} from 'react'; - -import {useStore} from 'zustand'; - -import {IframeContext} from '../iframeContext'; - -export const useIframeStore = () => { - const {state} = useContext(IframeContext); - if (!state) { - throw new Error('Missing IframeContext'); - } - return useStore(state); -}; - -export default useIframeStore; diff --git a/src/editor-v2/context/iframeContext/iframeContext.tsx b/src/editor-v2/context/iframeContext/iframeContext.tsx index 5404b61a75..95c498befe 100644 --- a/src/editor-v2/context/iframeContext/iframeContext.tsx +++ b/src/editor-v2/context/iframeContext/iframeContext.tsx @@ -4,15 +4,16 @@ import React from 'react'; -import {StoreApi} from 'zustand'; - -import {IframeStore} from './store'; - export interface IframeContextProps { - state?: StoreApi<IframeStore>; iframeElement?: HTMLIFrameElement; setIframeElement: (element: HTMLIFrameElement) => void; + url: string; + setUrl: (url: string) => void; disableUrlField?: boolean; } -export const IframeContext = React.createContext<IframeContextProps>({setIframeElement: () => {}}); +export const IframeContext = React.createContext<IframeContextProps>({ + setIframeElement: () => {}, + setUrl: () => {}, + url: '', +}); diff --git a/src/editor-v2/context/iframeContext/iframeProvider.tsx b/src/editor-v2/context/iframeContext/iframeProvider.tsx index a8b7f56fbf..f6261ac550 100644 --- a/src/editor-v2/context/iframeContext/iframeProvider.tsx +++ b/src/editor-v2/context/iframeContext/iframeProvider.tsx @@ -1,9 +1,6 @@ -import React, {PropsWithChildren, useRef, useState} from 'react'; - -import {StoreApi} from 'zustand'; +import React, {PropsWithChildren, useState} from 'react'; import {IframeContext} from './iframeContext'; -import {IframeStore, createIframeStore} from './store'; interface IframeProviderProps extends PropsWithChildren { initialUrl?: string; @@ -11,20 +8,17 @@ interface IframeProviderProps extends PropsWithChildren { } export const IframeProvider = (props: IframeProviderProps) => { - const {children, initialUrl, disableUrlField} = props; - const storeRef = useRef<StoreApi<IframeStore>>(); + const {children, initialUrl = '', disableUrlField} = props; const [iframeElement, setIframeElement] = useState<HTMLIFrameElement>(); + const [url, setUrl] = useState(initialUrl); const setIframeElementFunc = (element: HTMLIFrameElement) => setIframeElement(element); - if (!storeRef.current) { - storeRef.current = createIframeStore({url: initialUrl}); - } - return ( <IframeContext.Provider value={{ - state: storeRef.current, + url, + setUrl, iframeElement, setIframeElement: setIframeElementFunc, disableUrlField, diff --git a/src/editor-v2/context/iframeContext/index.ts b/src/editor-v2/context/iframeContext/index.ts index 7d73463acd..7da30b4e20 100644 --- a/src/editor-v2/context/iframeContext/index.ts +++ b/src/editor-v2/context/iframeContext/index.ts @@ -1,4 +1,2 @@ export * from './iframeContext'; export * from './iframeProvider'; -export * from './hooks/useIframeStore'; -export * from './store'; diff --git a/src/editor-v2/context/iframeContext/store.ts b/src/editor-v2/context/iframeContext/store.ts deleted file mode 100644 index c5902b9027..0000000000 --- a/src/editor-v2/context/iframeContext/store.ts +++ /dev/null @@ -1,68 +0,0 @@ -import {ActionTypes, WithStoreReducer} from '../../../common/types'; -import {ZOOM_STEPS} from '../../constants'; -import {initializeStore} from '../../utils/store'; - -export interface IframeState { - url: string; - height?: number; - // In percents - zoom: number; -} - -export interface IframeMethods extends WithStoreReducer { - setUrl: (url: string) => void; - setZoom: (zoom: number) => void; - increaseZoom: () => void; - decreaseZoom: () => void; -} - -export type IframeStore = IframeState & IframeMethods; - -export const createIframeStore = initializeStore<IframeState, IframeMethods>( - { - url: '', - height: 400, - zoom: 100, - }, - (set, get) => ({ - setZoom: function (zoom) { - if (zoom > 0) { - set((state) => ({...state, zoom})); - } - }, - increaseZoom: function () { - const currentZoom = get().zoom; - - for (const step of ZOOM_STEPS) { - if (currentZoom < step) { - get().setZoom(step); - break; - } - } - }, - decreaseZoom: function () { - const currentZoom = get().zoom; - const reverseSteps = ZOOM_STEPS.slice().reverse(); - - for (const step of reverseSteps) { - if (currentZoom > step) { - get().setZoom(step); - break; - } - } - }, - - setUrl: (url) => set((state) => ({...state, url})), - reducer: (action) => { - switch (action.type) { - case ActionTypes.SetHeight: { - set((state) => ({ - ...state, - height: action.payload.height + 200, - })); - break; - } - } - }, - }), -); diff --git a/src/editor-v2/context/messagesContext/hooks/useMessageObserver.tsx b/src/editor-v2/context/messagesContext/hooks/useMessageObserver.tsx deleted file mode 100644 index 7414cad432..0000000000 --- a/src/editor-v2/context/messagesContext/hooks/useMessageObserver.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import {DependencyList, useEffect} from 'react'; - -import {Action, Meta} from '../../../../common/types'; - -import useMessagesStore from './useMessagesStore'; - -export const useMessageObserver = <A extends Action>( - type: A['type'], - callback: (payload: A['payload'], meta: Meta) => void, - deps: DependencyList = [], -) => { - const {subscribe, unsubscribe} = useMessagesStore(); - - useEffect(() => { - subscribe(type, callback); - - return () => { - unsubscribe(type, callback); - }; - }, deps); // eslint-disable-line react-hooks/exhaustive-deps -}; - -export default useMessageObserver; diff --git a/src/editor-v2/context/messagesContext/hooks/useMessageSender.tsx b/src/editor-v2/context/messagesContext/hooks/useMessageSender.tsx deleted file mode 100644 index 57f25eef0e..0000000000 --- a/src/editor-v2/context/messagesContext/hooks/useMessageSender.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import {useContext} from 'react'; - -import {PostMessageContext} from '../messagesContext'; - -export const useMessageSender = () => { - const {sendMessage} = useContext(PostMessageContext); - return sendMessage; -}; - -export default useMessageSender; diff --git a/src/editor-v2/context/messagesContext/hooks/useMessagesStore.tsx b/src/editor-v2/context/messagesContext/hooks/useMessagesStore.tsx deleted file mode 100644 index 1805cd32a6..0000000000 --- a/src/editor-v2/context/messagesContext/hooks/useMessagesStore.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import {useContext} from 'react'; - -import {useStore} from 'zustand'; - -import {PostMessageContext} from '../messagesContext'; - -export const useMessagesStore = () => { - const {state} = useContext(PostMessageContext); - - if (!state) { - throw new Error('Missing PostMessageContext'); - } - - return useStore(state); -}; - -export default useMessagesStore; diff --git a/src/editor-v2/context/messagesContext/index.ts b/src/editor-v2/context/messagesContext/index.ts deleted file mode 100644 index eda561f123..0000000000 --- a/src/editor-v2/context/messagesContext/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './messagesContext'; -export * from './messagesProvider'; -export * from './hooks/useMessageSender'; -export * from './hooks/useMessageObserver'; -export * from './hooks/useMessagesStore'; diff --git a/src/editor-v2/context/messagesContext/messagesContext.tsx b/src/editor-v2/context/messagesContext/messagesContext.tsx deleted file mode 100644 index ba01f35522..0000000000 --- a/src/editor-v2/context/messagesContext/messagesContext.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Context for sending messages between Editor and App - * Same exist in PC - **/ - -import React from 'react'; - -import {StoreApi} from 'zustand'; - -import {Action, SendOptions} from '../../../common/types'; - -import {MessagesStore} from './store'; - -export interface PostMessageContextProps { - state?: StoreApi<MessagesStore>; - sendMessage: (action: Action, options?: SendOptions) => void; -} - -export const PostMessageContext = React.createContext<PostMessageContextProps>({ - sendMessage: () => {}, -}); diff --git a/src/editor-v2/context/messagesContext/messagesProvider.tsx b/src/editor-v2/context/messagesContext/messagesProvider.tsx deleted file mode 100644 index 3fe7d60662..0000000000 --- a/src/editor-v2/context/messagesContext/messagesProvider.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, {PropsWithChildren, useContext, useRef} from 'react'; - -import {StoreApi} from 'zustand'; - -import {usePostMessage} from '../../../common/hooks/usePostMessage'; -import {WithStoreReducer} from '../../../common/types'; -import {getUrlOrigin} from '../../utils'; -import {useContentConfigStore} from '../contentConfig/hooks/useContentConfigStore'; -import {useEditorStore} from '../editorContext/hooks/useEditorStore'; -import {IframeContext, useIframeStore} from '../iframeContext'; - -import {PostMessageContext} from './messagesContext'; -import {MessagesStore, createMessagesStore} from './store'; - -export const PostMessageProvider = ({children}: PropsWithChildren) => { - const storeRef = useRef<StoreApi<MessagesStore>>(); - const {iframeElement} = useContext(IframeContext); - - const contentConfigStore = useContentConfigStore(); - const editorStore = useEditorStore(); - const iframeStore = useIframeStore(); - - if (!storeRef.current) { - storeRef.current = createMessagesStore(); - } - - const storesWithReducer: WithStoreReducer[] = [contentConfigStore, editorStore, iframeStore]; - - const {sendMessage} = usePostMessage({ - subscribers: storeRef.current?.getState().subscribers, - storesWithReducer, - targetIframeElement: iframeElement, - urlOrigin: getUrlOrigin(iframeStore.url), - }); - - return ( - <PostMessageContext.Provider - value={{ - state: storeRef.current, - sendMessage: sendMessage, - }} - > - {children} - </PostMessageContext.Provider> - ); -}; diff --git a/src/editor-v2/context/messagesContext/store.ts b/src/editor-v2/context/messagesContext/store.ts deleted file mode 100644 index 3c76dfe9d2..0000000000 --- a/src/editor-v2/context/messagesContext/store.ts +++ /dev/null @@ -1,44 +0,0 @@ -import {Subscriber, SubscriptionFunc} from '../../../common/types'; -import {initializeStore} from '../../utils/store'; - -export interface MessagesState { - subscribers: Subscriber[]; -} - -export interface MessagesMethods { - subscribe: SubscriptionFunc; - unsubscribe: SubscriptionFunc; -} - -export type MessagesStore = MessagesState & MessagesMethods; - -export const createMessagesStore = initializeStore<MessagesState, MessagesMethods>( - { - subscribers: [], - }, - (set, _get) => ({ - subscribe: (type, payloadCallback) => { - set((state) => ({ - ...state, - subscribers: [...state.subscribers, {action: type, handler: payloadCallback}], - })); - - return () => { - set((state) => ({ - ...state, - subscribers: state.subscribers.filter( - ({handler, action}) => payloadCallback !== handler || type !== action, - ), - })); - }; - }, - unsubscribe: (type, payloadCallback) => { - set((state) => ({ - ...state, - subscribers: state.subscribers.filter( - ({handler, action}) => payloadCallback !== handler || type !== action, - ), - })); - }, - }), -); diff --git a/src/editor-v2/hooks/usePostMessageEvents.ts b/src/editor-v2/hooks/usePostMessageEvents.ts new file mode 100644 index 0000000000..3bb5473ffb --- /dev/null +++ b/src/editor-v2/hooks/usePostMessageEvents.ts @@ -0,0 +1,26 @@ +import {useContext} from 'react'; + +import {requestActionPostMessage} from '../../common/postMessage'; +import {ActionMessageTypes} from '../../common/types/actions'; +import {IframeContext} from '../context/iframeContext'; + +interface UsePostMessageRequestReturn { + requestPostMessage: <K extends keyof ActionMessageTypes>( + action: K, + data: ActionMessageTypes[K], + ) => void; +} + +export function usePostMessageEvents(): UsePostMessageRequestReturn { + const {iframeElement} = useContext(IframeContext); + + return { + requestPostMessage: (action, data) => { + if (iframeElement && iframeElement.contentWindow) { + return requestActionPostMessage(action, data, iframeElement.contentWindow); + } + + return undefined; + }, + }; +} diff --git a/src/hooks/useEditorInitialize.ts b/src/hooks/useEditorInitialize.ts index 4f3cede1b3..8d721821ca 100644 --- a/src/hooks/useEditorInitialize.ts +++ b/src/hooks/useEditorInitialize.ts @@ -1,106 +1,71 @@ -import {useCallback, useContext, useEffect} from 'react'; +import {useCallback, useEffect} from 'react'; import {JSONSchemaType} from 'ajv'; import _ from 'lodash'; -import {ActionTypes, ItemConfig, UpdateConfigsAction} from '../common/types'; +import {ItemConfig} from '../common/types'; import {blockDataMap} from '../constructor-items'; -import {EditorContext, useEditorStore} from '../context/editorContext'; -import {useMessageObserver, useMessageSender} from '../context/messagesContext'; +import {usePCEditorStore} from '../context/editorStoreContext'; import {PageContent} from '../models'; import {defaultComponentsConfigurationSchema} from '../schema'; import {generateFromAJV} from '../utils/form-generator'; +import {sendEventPostMessage, useInternalPostMessageAPIListener} from './usePostMessageAPI'; + interface UseEditorInitializeProps { - content: PageContent; + initialContent: PageContent; setContent: (content: PageContent) => void; } -const useEditorInitialize = ({setContent, content}: UseEditorInitializeProps) => { - const {manipulateOverlayMode, isSelectActive} = useEditorStore(); - const sendMessage = useMessageSender(); - const {activeElement} = useContext(EditorContext); +export const useInitializeEditorEvents = ({ + initialContent, + setContent, +}: UseEditorInitializeProps) => { + const {manipulateOverlayMode, disableMode, initialized, content} = usePCEditorStore(); - const onResize = useCallback(() => { - const height = document.documentElement.getBoundingClientRect().height; - - sendMessage({ - type: ActionTypes.SetHeight, - payload: {height}, - }); - - if (isSelectActive && activeElement) { - const rect = activeElement.getClientRects().item(0); - - if (rect) { - sendMessage({ - type: ActionTypes.UpdateSelectedBlockRect, - payload: { - rect: rect, - }, - }); - } + useEffect(() => { + if (initialized) { + setContent(content); } - }, [activeElement, isSelectActive, sendMessage]); + }, [content, initialized, setContent]); - useMessageObserver<UpdateConfigsAction>(ActionTypes.UpdateConfigs, ({content: newContent}) => { - setContent(newContent); + useInternalPostMessageAPIListener('GET_CURRENT_CONTENT', () => { + sendEventPostMessage('ON_INITIAL_CONTENT', initialContent); }); - useEffect(() => { - onResize(); - }, [content, onResize]); + useInternalPostMessageAPIListener('GET_SUPPORTED_BLOCKS', () => { + sendEventPostMessage('ON_SUPPORTED_BLOCKS', { + blocks: Object.entries(blockDataMap).reduce((acc, [key, value]) => { + acc.push({type: key, schema: value.schema}); + return acc; + }, [] as ItemConfig[]), + subBlocks: [], + global: generateFromAJV( + defaultComponentsConfigurationSchema as unknown as JSONSchemaType<{}>, + ), + }); + }); - // Fires only once - useEffect(() => { + const onResize = useCallback(() => { const height = document.documentElement.getBoundingClientRect().height; + sendEventPostMessage('ON_RESIZE', {height}); + }, []); - sendMessage( - { - type: ActionTypes.IframeReady, - payload: {height}, - }, - {direction: 'editor'}, - ); - - sendMessage({ - type: ActionTypes.BlocksConfigs, - payload: { - blocks: Object.entries(blockDataMap).reduce((acc, [key, value]) => { - acc.push({type: key, schema: value.schema}); - return acc; - }, [] as ItemConfig[]), - subBlocks: [], - global: generateFromAJV( - defaultComponentsConfigurationSchema as unknown as JSONSchemaType<{}>, - ), - }, - }); - }, [sendMessage]); + new ResizeObserver(onResize).observe(document.body); useEffect(() => { - const onMouseUp = (e: MouseEvent) => { - if (manipulateOverlayMode === 'insert') { - e.preventDefault(); - sendMessage({type: ActionTypes.InsertModeDisable, payload: undefined}); - } - if (manipulateOverlayMode === 'reorder') { - e.preventDefault(); - sendMessage({type: ActionTypes.ReorderModeDisable, payload: undefined}); - } + const onMouseUp = () => { + sendEventPostMessage('ON_MOUSE_UP', {}); }; const onMouseMove = (event: MouseEvent) => { - if (manipulateOverlayMode) { - sendMessage({ - type: ActionTypes.OverlayModeOnMove, - payload: {cursor: {x: event.clientX, y: event.clientY}}, - }); - } + event.preventDefault(); + event.stopPropagation(); + sendEventPostMessage('ON_MOUSE_MOVE', {x: event.clientX, y: event.clientY}); }; - const throttleOnMouseMove = _.throttle(onMouseMove, 1); - const throttleOnMouseUp = _.throttle(onMouseUp, 1); + const throttleOnMouseMove = _.throttle(onMouseMove, 10); + const throttleOnMouseUp = _.throttle(onMouseUp, 10); document.addEventListener('mousemove', throttleOnMouseMove); document.addEventListener('mouseup', throttleOnMouseUp); @@ -111,7 +76,10 @@ const useEditorInitialize = ({setContent, content}: UseEditorInitializeProps) => document.removeEventListener('mouseup', throttleOnMouseUp); window.removeEventListener('resize', onResize); }; - }, [activeElement, manipulateOverlayMode, onResize, sendMessage]); -}; + }, [disableMode, manipulateOverlayMode, onResize]); -export default useEditorInitialize; + useEffect(() => { + const height = document.documentElement.getBoundingClientRect().height; + sendEventPostMessage('ON_INIT', {height}); + }, []); +}; diff --git a/src/hooks/usePostMessageAPI.ts b/src/hooks/usePostMessageAPI.ts new file mode 100644 index 0000000000..6cf8c4cdaa --- /dev/null +++ b/src/hooks/usePostMessageAPI.ts @@ -0,0 +1,42 @@ +import {useEffect} from 'react'; + +import {PostMessageAPIMessage} from '../common/types'; +import {ActionMessageTypes, EventMessageTypes} from '../common/types/actions'; + +export function sendEventPostMessage<K extends keyof EventMessageTypes>( + action: K, + data: EventMessageTypes[K], +) { + const message = {action, data} as PostMessageAPIMessage<K>; + window.parent.postMessage(message); +} + +export function listenPostMessageActions<K extends keyof ActionMessageTypes>( + action: K, + callback: (data: ActionMessageTypes[K]) => void, +) { + const onMessage = (e: MessageEvent) => { + const message = e.data as PostMessageAPIMessage<K>; + + if ('action' in message && message.action === action) { + return callback(message.data); + } + + return undefined; + }; + + window.addEventListener('message', onMessage); + + return () => { + window.removeEventListener('message', onMessage); + }; +} + +export function useInternalPostMessageAPIListener<K extends keyof ActionMessageTypes>( + action: K, + callback: (data: ActionMessageTypes[K]) => void, +) { + useEffect(() => { + return listenPostMessageActions(action, callback); + }, [action, callback]); +} diff --git a/src/index.ts b/src/index.ts index 10becb250d..05106270fd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,5 +12,6 @@ export * from './utils'; export * from './schema'; export * from './hooks'; export * from './navigation'; +export * from './common/postMessage'; export {BREAKPOINTS} from './constants'; diff --git a/src/utils/store.ts b/src/utils/store.ts index 9bf940a34e..a50614c5da 100644 --- a/src/utils/store.ts +++ b/src/utils/store.ts @@ -1,22 +1,35 @@ import {StoreApi, create} from 'zustand'; -import {devtools} from 'zustand/middleware'; +import {devtools, subscribeWithSelector} from 'zustand/middleware'; export function initializeStore<State, Methods>( initialState: State, methods: ( set: StoreApi<State & Methods>['setState'], get: StoreApi<State & Methods>['getState'], + onStateUpdated?: () => void, ) => Methods, ) { - return (overrideInitialState?: Partial<State>) => + return (overrideInitialState?: Partial<State>, onStateUpdated?: () => void) => create< State & Methods, - [['zustand/devtools', never], ['zustand/persist', State & Methods]] + [ + ['zustand/subscribeWithSelector', State & Methods], + ['zustand/devtools', never], + ['zustand/persist', State & Methods], + ] >( - devtools((set, get) => ({ - ...initialState, - ...overrideInitialState, - ...methods(set, get), - })), + subscribeWithSelector( + devtools((set, get) => { + return { + ...initialState, + ...overrideInitialState, + ...methods(set, get, onStateUpdated), + }; + }), + ), ); } + +export const removeFn = (object: object) => { + return JSON.parse(JSON.stringify(object)); +}; From 4f999540ded6b35a84a6ed865ba1f258bf7d4a97 Mon Sep 17 00:00:00 2001 From: DaffPunks <sawavas2@gmail.com> Date: Mon, 3 Feb 2025 17:26:54 +0300 Subject: [PATCH 006/118] feat: add PostMessage API and Synchronized store --- playground/package-lock.json | 680 ++++++++++++++---- playground/package.json | 10 +- src/blocks/CardLayout/CardLayout.tsx | 2 - .../ExtendedFeatures/ExtendedFeatures.tsx | 108 +-- src/blocks/Icons/Icons.tsx | 2 +- .../PromoFeaturesBlock/PromoFeaturesBlock.tsx | 2 +- .../editor/ChildrenWrap/ChildrenWrap.tsx | 1 + .../DynamicForm/FieldBase/FieldBase.tsx | 1 + .../components/MiddleScreen/MiddleScreen.tsx | 1 + src/editor-v2/components/Overlay/Overlay.tsx | 1 + src/editor-v2/containers/Editor/Editor.tsx | 1 + .../editorStore/MainEditorStoreProvider.tsx | 3 - .../components/CodeEditor/CodeEditor.tsx | 106 ++- src/editor/data/index.ts | 31 +- src/sub-blocks/LayoutItem/schema.ts | 3 +- 15 files changed, 686 insertions(+), 266 deletions(-) diff --git a/playground/package-lock.json b/playground/package-lock.json index afdffac322..4352df2721 100644 --- a/playground/package-lock.json +++ b/playground/package-lock.json @@ -1,34 +1,385 @@ { - "name": "page-constructor-example", + "name": "page-constructor-playground", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "page-constructor-example", + "name": "page-constructor-playground", "version": "1.0.0", "dependencies": { "bem-cn-lite": "^4.1.0", - "next": "14.2.3", - "react": "^18", - "react-dom": "^18" + "next": "15.1.2", + "react": "^19", + "react-dom": "^19" }, "devDependencies": { "@types/node": "^20", - "@types/react": "^18", - "@types/react-dom": "^18", + "@types/react": "^19", + "@types/react-dom": "^19", "typescript": "^5" } }, + "node_modules/@emnapi/runtime": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", + "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@next/env": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz", - "integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==" + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.2.tgz", + "integrity": "sha512-Hm3jIGsoUl6RLB1vzY+dZeqb+/kWPZ+h34yiWxW0dV87l8Im/eMOwpOA+a0L78U0HM04syEjXuRlCozqpwuojQ==" }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz", - "integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.2.tgz", + "integrity": "sha512-b9TN7q+j5/7+rGLhFAVZiKJGIASuo8tWvInGfAd8wsULjB1uNGRCj1z1WZwwPWzVQbIKWFYqc+9L7W09qwt52w==", "cpu": [ "arm64" ], @@ -41,9 +392,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz", - "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.2.tgz", + "integrity": "sha512-caR62jNDUCU+qobStO6YJ05p9E+LR0EoXh1EEmyU69cYydsAy7drMcOlUlRtQihM6K6QfvNwJuLhsHcCzNpqtA==", "cpu": [ "x64" ], @@ -56,9 +407,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz", - "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.2.tgz", + "integrity": "sha512-fHHXBusURjBmN6VBUtu6/5s7cCeEkuGAb/ZZiGHBLVBXMBy4D5QpM8P33Or8JD1nlOjm/ZT9sEE5HouQ0F+hUA==", "cpu": [ "arm64" ], @@ -71,9 +422,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz", - "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.2.tgz", + "integrity": "sha512-9CF1Pnivij7+M3G74lxr+e9h6o2YNIe7QtExWq1KUK4hsOLTBv6FJikEwCaC3NeYTflzrm69E5UfwEAbV2U9/g==", "cpu": [ "arm64" ], @@ -86,9 +437,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz", - "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.2.tgz", + "integrity": "sha512-tINV7WmcTUf4oM/eN3Yuu/f8jQ5C6AkueZPKeALs/qfdfX57eNv4Ij7rt0SA6iZ8+fMobVfcFVv664Op0caCCg==", "cpu": [ "x64" ], @@ -101,9 +452,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz", - "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.2.tgz", + "integrity": "sha512-jf2IseC4WRsGkzeUw/cK3wci9pxR53GlLAt30+y+B+2qAQxMw6WAC3QrANIKxkcoPU3JFh/10uFfmoMDF9JXKg==", "cpu": [ "x64" ], @@ -116,9 +467,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz", - "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.2.tgz", + "integrity": "sha512-wvg7MlfnaociP7k8lxLX4s2iBJm4BrNiNFhVUY+Yur5yhAJHfkS8qPPeDEUH8rQiY0PX3u/P7Q/wcg6Mv6GSAA==", "cpu": [ "arm64" ], @@ -130,25 +481,10 @@ "node": ">= 10" } }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz", - "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz", - "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.2.tgz", + "integrity": "sha512-D3cNA8NoT3aWISWmo7HF5Eyko/0OdOO+VagkoJuiTk7pyX3P/b+n8XA/MYvyR+xSVcbKn68B1rY9fgqjNISqzQ==", "cpu": [ "x64" ], @@ -166,12 +502,11 @@ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" }, "node_modules/@swc/helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", - "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", "dependencies": { - "@swc/counter": "^0.1.3", - "tslib": "^2.4.0" + "tslib": "^2.8.0" } }, "node_modules/@types/node": { @@ -183,29 +518,22 @@ "undici-types": "~5.26.4" } }, - "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "dev": true - }, "node_modules/@types/react": { - "version": "18.3.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.2.tgz", - "integrity": "sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==", + "version": "19.0.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.8.tgz", + "integrity": "sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==", "dev": true, "dependencies": { - "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", - "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.3.tgz", + "integrity": "sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==", "dev": true, - "dependencies": { - "@types/react": "*" + "peerDependencies": { + "@types/react": "^19.0.0" } }, "node_modules/bem-cn": { @@ -256,33 +584,68 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "optional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "optional": true + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "dev": true }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "optional": true, + "engines": { + "node": ">=8" } }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "optional": true + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -301,40 +664,41 @@ } }, "node_modules/next": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz", - "integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/next/-/next-15.1.2.tgz", + "integrity": "sha512-nLJDV7peNy+0oHlmY2JZjzMfJ8Aj0/dd3jCwSZS8ZiO5nkQfcZRqDrRN3U5rJtqVTQneIOGZzb6LCNrk7trMCQ==", "dependencies": { - "@next/env": "14.2.3", - "@swc/helpers": "0.5.5", + "@next/env": "15.1.2", + "@swc/counter": "0.1.3", + "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", - "graceful-fs": "^4.2.11", "postcss": "8.4.31", - "styled-jsx": "5.1.1" + "styled-jsx": "5.1.6" }, "bin": { "next": "dist/bin/next" }, "engines": { - "node": ">=18.17.0" + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.3", - "@next/swc-darwin-x64": "14.2.3", - "@next/swc-linux-arm64-gnu": "14.2.3", - "@next/swc-linux-arm64-musl": "14.2.3", - "@next/swc-linux-x64-gnu": "14.2.3", - "@next/swc-linux-x64-musl": "14.2.3", - "@next/swc-win32-arm64-msvc": "14.2.3", - "@next/swc-win32-ia32-msvc": "14.2.3", - "@next/swc-win32-x64-msvc": "14.2.3" + "@next/swc-darwin-arm64": "15.1.2", + "@next/swc-darwin-x64": "15.1.2", + "@next/swc-linux-arm64-gnu": "15.1.2", + "@next/swc-linux-arm64-musl": "15.1.2", + "@next/swc-linux-x64-gnu": "15.1.2", + "@next/swc-linux-x64-musl": "15.1.2", + "@next/swc-win32-arm64-msvc": "15.1.2", + "@next/swc-win32-x64-msvc": "15.1.2", + "sharp": "^0.33.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "peerDependenciesMeta": { @@ -344,6 +708,9 @@ "@playwright/test": { "optional": true }, + "babel-plugin-react-compiler": { + "optional": true + }, "sass": { "optional": true } @@ -382,34 +749,87 @@ } }, "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.25.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.0.0" } }, "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==" + }, + "node_modules/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "optional": true, "dependencies": { - "loose-envify": "^1.1.0" + "is-arrayish": "^0.3.1" } }, "node_modules/source-map-js": { @@ -429,9 +849,9 @@ } }, "node_modules/styled-jsx": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", - "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", "dependencies": { "client-only": "0.0.1" }, @@ -439,7 +859,7 @@ "node": ">= 12.0.0" }, "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" }, "peerDependenciesMeta": { "@babel/core": { @@ -451,9 +871,9 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/typescript": { "version": "5.4.5", diff --git a/playground/package.json b/playground/package.json index 2ce85b873d..76783faa5e 100644 --- a/playground/package.json +++ b/playground/package.json @@ -10,14 +10,14 @@ }, "dependencies": { "bem-cn-lite": "^4.1.0", - "next": "14.2.3", - "react": "^18", - "react-dom": "^18" + "next": "15.1.2", + "react": "^19", + "react-dom": "^19" }, "devDependencies": { "@types/node": "^20", - "@types/react": "^18", - "@types/react-dom": "^18", + "@types/react": "^19", + "@types/react-dom": "^19", "typescript": "^5" } } diff --git a/src/blocks/CardLayout/CardLayout.tsx b/src/blocks/CardLayout/CardLayout.tsx index dac709f3b6..ad6294dfc2 100644 --- a/src/blocks/CardLayout/CardLayout.tsx +++ b/src/blocks/CardLayout/CardLayout.tsx @@ -3,8 +3,6 @@ import * as React from 'react'; import isEmpty from 'lodash/isEmpty'; import {AnimateBlock, BackgroundImage, Title} from '../../components'; -import ChildrenWrap from '../../components/editor/ChildrenWrap/ChildrenWrap'; -import ItemWrap from '../../components/editor/ItemWrap/ItemWrap'; import {useTheme} from '../../context/theme'; import {Col, GridColumnSizesType, GridJustifyContent, Row} from '../../grid'; import {CardLayoutBlockProps as CardLayoutBlockParams, ClassNameProps} from '../../models'; diff --git a/src/blocks/ExtendedFeatures/ExtendedFeatures.tsx b/src/blocks/ExtendedFeatures/ExtendedFeatures.tsx index 686e821394..7f96c86427 100644 --- a/src/blocks/ExtendedFeatures/ExtendedFeatures.tsx +++ b/src/blocks/ExtendedFeatures/ExtendedFeatures.tsx @@ -35,17 +35,20 @@ export const ExtendedFeaturesBlock = ({ <Row> {items && items.map( - ({ - title: itemTitle, - text, - list, - link, - links, - label, - icon, - buttons, - additionalInfo, - }) => { + ( + { + title: itemTitle, + text, + list, + link, + links, + label, + icon, + buttons, + additionalInfo, + }, + index, + ) => { const itemLinks = links || []; const iconThemed = icon && getThemedValue(icon, theme); @@ -55,45 +58,52 @@ export const ExtendedFeaturesBlock = ({ itemLinks.push(link); } - return ( - <Col className={b('item')} key={text || itemTitle} sizes={colSizes}> - {iconData && ( - <div className={b('icon-wrap')} aria-hidden> - <Image {...iconData} className={b('icon')} /> - </div> - )} - <div className={b('container')}> - {itemTitle && ( - <YFMWrapper - tagName={itemTitleHeadingTag} - content={itemTitle} - className={b('item-title-container')} - contentClassName={b('item-title')} - modifiers={{ - constructor: true, - }} - > - {label && ( - <span className={b('item-label')}>{label}</span> + return ( + <Col + className={b('item')} + key={text || itemTitle} + sizes={colSizes} + > + {iconData && ( + <div className={b('icon-wrap')} aria-hidden> + <Image {...iconData} className={b('icon')} /> + </div> + )} + <div className={b('container')}> + {itemTitle && ( + <YFMWrapper + tagName={itemTitleHeadingTag} + content={itemTitle} + className={b('item-title-container')} + contentClassName={b('item-title')} + modifiers={{ + constructor: true, + }} + > + {label && ( + <span className={b('item-label')}> + {label} + </span> + )} + </YFMWrapper> )} - </YFMWrapper> - )} - <Content - text={text} - links={itemLinks} - size="s" - list={list} - colSizes={{all: 12, md: 12}} - buttons={buttons} - additionalInfo={additionalInfo} - /> - </div> - </Col> - ); - }, - )} - </Row> - </div> + <Content + text={text} + links={itemLinks} + size="s" + list={list} + colSizes={{all: 12, md: 12}} + buttons={buttons} + additionalInfo={additionalInfo} + /> + </div> + </Col> + ); + }, + )} + </Row> + </div> + </Grid> </AnimateBlock> ); }; diff --git a/src/blocks/Icons/Icons.tsx b/src/blocks/Icons/Icons.tsx index c7338587e0..8d966d4aa6 100644 --- a/src/blocks/Icons/Icons.tsx +++ b/src/blocks/Icons/Icons.tsx @@ -2,8 +2,8 @@ import * as React from 'react'; import {Image, Title} from '../../components'; import {LocationContext} from '../../context/locationContext'; -import {Grid} from '../../grid'; import {useTheme} from '../../context/theme'; +import {Grid} from '../../grid'; import {useAnalytics} from '../../hooks'; import {IconsBlockItemProps, IconsBlockProps} from '../../models'; import {block, getLinkProps, getThemedValue} from '../../utils'; diff --git a/src/blocks/PromoFeaturesBlock/PromoFeaturesBlock.tsx b/src/blocks/PromoFeaturesBlock/PromoFeaturesBlock.tsx index ac77be32ff..227a9d331f 100644 --- a/src/blocks/PromoFeaturesBlock/PromoFeaturesBlock.tsx +++ b/src/blocks/PromoFeaturesBlock/PromoFeaturesBlock.tsx @@ -5,8 +5,8 @@ import Media from '../../components/Media/Media'; import Title from '../../components/Title/Title'; import YFMWrapper from '../../components/YFMWrapper/YFMWrapper'; import {BREAKPOINTS} from '../../constants'; -import {Grid} from '../../grid'; import {useTheme} from '../../context/theme'; +import {Grid} from '../../grid'; import {PromoFeaturesProps} from '../../models'; import {block, getThemedValue} from '../../utils'; import {mergeVideoMicrodata} from '../../utils/microdata'; diff --git a/src/components/editor/ChildrenWrap/ChildrenWrap.tsx b/src/components/editor/ChildrenWrap/ChildrenWrap.tsx index 78287b18f1..f6129ae0c4 100644 --- a/src/components/editor/ChildrenWrap/ChildrenWrap.tsx +++ b/src/components/editor/ChildrenWrap/ChildrenWrap.tsx @@ -24,6 +24,7 @@ const ChildrenWrap = (props: ChildrenWrapProps) => { const {onMouseUp, onMouseMove} = useEditorBlockMouseEvents([parentBlockId, 0], element); return ( + // eslint-disable-next-line jsx-a11y/no-static-element-interactions <div ref={blockRef} className={b()} onMouseMove={onMouseMove} onMouseUp={onMouseUp}> {children} </div> diff --git a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx index 8b5860ace6..ce1bf25879 100644 --- a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx +++ b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx @@ -32,6 +32,7 @@ const FieldBase: React.FC<FieldBaseProps> = (props) => { if (expandable) { return ( + // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions <div className={b('foldable')} onClick={() => setShowChildren(!showChildren)}> <ArrowToggle direction={showChildren ? 'bottom' : 'right'} diff --git a/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx b/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx index 6bd08ebda1..e0a1bac099 100644 --- a/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx +++ b/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx @@ -62,6 +62,7 @@ const MiddleScreen: React.FC<MiddleScreenProps> = ({className}) => { height={`${height}px`} width="100%" frameBorder="0" + title="Page Constructor Iframe" /> <Overlay className={b('overlay')} /> {!initialized && ( diff --git a/src/editor-v2/components/Overlay/Overlay.tsx b/src/editor-v2/components/Overlay/Overlay.tsx index 51722ef3a9..f42078f903 100644 --- a/src/editor-v2/components/Overlay/Overlay.tsx +++ b/src/editor-v2/components/Overlay/Overlay.tsx @@ -79,6 +79,7 @@ const Overlay: React.FC<OverlayProps> = ({className}) => { }} > <div className={b('actions')}> + {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */} <div onMouseDown={onMouseDown}> <Button pin={'round-clear'} diff --git a/src/editor-v2/containers/Editor/Editor.tsx b/src/editor-v2/containers/Editor/Editor.tsx index e7fd9b9ac5..85eb3c3bdf 100644 --- a/src/editor-v2/containers/Editor/Editor.tsx +++ b/src/editor-v2/containers/Editor/Editor.tsx @@ -41,6 +41,7 @@ const EditorView = (_props: EditorViewProps) => { ); return ( + // eslint-disable-next-line jsx-a11y/no-static-element-interactions <div className={b()} onMouseUp={onMouseUp}> <div className={b('body')}> <Panels diff --git a/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx b/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx index 68b8e9908f..c91e56a4c5 100644 --- a/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx +++ b/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx @@ -23,9 +23,6 @@ export const MainEditorStoreProvider = ({children}: MainEditorProviderProps) => if (iframeElement && iframeElement.contentWindow) { iframeElement.contentWindow.postMessage(message, '*'); - } else { - // eslint-disable-next-line no-console - console.error('No Iframe element in Editor'); } }, [iframeElement], diff --git a/src/editor/components/CodeEditor/CodeEditor.tsx b/src/editor/components/CodeEditor/CodeEditor.tsx index de0978a5c4..168bdce525 100644 --- a/src/editor/components/CodeEditor/CodeEditor.tsx +++ b/src/editor/components/CodeEditor/CodeEditor.tsx @@ -1,5 +1,4 @@ -import * as React from 'react'; - +import {useCallback, useContext, useState} from 'react'; import {ChevronsCollapseUpRight, ChevronsExpandUpRight} from '@gravity-ui/icons'; import {Button, Icon} from '@gravity-ui/uikit'; import debounce from 'lodash/debounce'; @@ -17,8 +16,6 @@ import './CodeEditor.scss'; const b = block('code-editor'); -const ON_CHANGE_DEBOUNCE_TIMEOUT = 300; - interface CodeEditorProps { code: string; fullscreenModeOn: boolean; @@ -28,60 +25,57 @@ interface CodeEditorProps { message?: CodeEditorMessageProps; } -export const CodeEditor = React.memo( - ({onChange, validator, fullscreenModeOn, onFullscreenModeOnUpdate, code}: CodeEditorProps) => { - const [message, setMessage] = React.useState(() => validator(code)); - const {theme = Theme.Light} = React.useContext(EditorContext); +export const CodeEditor = ({ + onChange, + validator, + fullscreenModeOn, + onFullscreenModeOnUpdate, + code, +}: CodeEditorProps) => { + const [message, setMessage] = useState(() => validator(code)); + const {theme = Theme.Light} = useContext(EditorContext); - // eslint-disable-next-line react-hooks/exhaustive-deps - const onChangeWithValidation = React.useCallback( - debounce((newCode: string) => { - const validationResult = validator(newCode); + // eslint-disable-next-line react-hooks/exhaustive-deps + const onChangeWithValidation = useCallback( + debounce((newCode: string) => { + const validationResult = validator(newCode); - setMessage(validationResult); - onChange(parseCode(newCode)); - }, ON_CHANGE_DEBOUNCE_TIMEOUT), - [onChange, validator], - ); + setMessage(validationResult); + onChange(parseCode(newCode)); + }, 200), + [onChange, validator], + ); - return ( - <div className={b({fullscreen: fullscreenModeOn})}> - <div className={b('header')}> - <Button - view="flat-secondary" - onClick={() => onFullscreenModeOnUpdate(!fullscreenModeOn)} - > - <Icon - data={ - fullscreenModeOn ? ChevronsCollapseUpRight : ChevronsExpandUpRight - } - size={16} - /> - </Button> - </div> - <div className={b('code')}> - <MonacoEditor - key={String(fullscreenModeOn)} - defaultValue={code} - value={code} - language="yaml" - options={options} - onChange={onChangeWithValidation} - theme={theme === Theme.Dark ? 'vs-dark' : 'vs'} + return ( + <div className={b({fullscreen: fullscreenModeOn})}> + <div className={b('header')}> + <Button + view="flat-secondary" + onClick={() => onFullscreenModeOnUpdate(!fullscreenModeOn)} + > + <Icon + data={fullscreenModeOn ? ChevronsCollapseUpRight : ChevronsExpandUpRight} + size={16} /> - </div> - <div className={b('footer')}> - {message && ( - <div className={b('message-container')}> - <div className={b('message', {status: message.status})}> - {message.text} - </div> - </div> - )} - </div> + </Button> </div> - ); - }, -); - -CodeEditor.displayName = 'CodeEditor'; + <div className={b('code')}> + <MonacoEditor + key={String(fullscreenModeOn)} + value={code} + language="yaml" + options={options} + onChange={onChangeWithValidation} + theme={theme === Theme.Dark ? 'vs-dark' : 'vs'} + /> + </div> + <div className={b('footer')}> + {message && ( + <div className={b('message-container')}> + <div className={b('message', {status: message.status})}>{message.text}</div> + </div> + )} + </div> + </div> + ); +}; diff --git a/src/editor/data/index.ts b/src/editor/data/index.ts index 52d4808b72..c3d8f6322b 100644 --- a/src/editor/data/index.ts +++ b/src/editor/data/index.ts @@ -15,10 +15,10 @@ export interface EdiorBlockData { }; } -const getBlockTemplate = (blockType: BlockType): Promise<Omit<EdiorBlockData, 'preview'>> => - import(`./templates/${blockType}.json`).then((data) => data.default); +const getBlockTemplate = (blockType: BlockType) => + require(`./templates/${blockType}.json`) as Omit<EdiorBlockData, 'preview'>; -const getBlockPreview = (blockType: BlockType): PreviewComponent => { +const getBlockPreview = (blockType: BlockType) => { try { if (blockType === BlockType.HeaderBlock) { return HeaderBlock; @@ -32,26 +32,23 @@ const getBlockPreview = (blockType: BlockType): PreviewComponent => { } }; -type EditorBlocksData = Partial<Record<BlockType, EdiorBlockData>>; - -async function getEditorBlocksData(): Promise<EditorBlocksData> { - const EdiorBlockData: EditorBlocksData = {}; - - for (const blockType of Object.values(BlockType)) { - const template = await getBlockTemplate(blockType as BlockType); - +const EditorBlocksData = Object.values(BlockType).reduce( + (previewData, blockType) => { + const template = getBlockTemplate(blockType); const preview = getBlockPreview(blockType); template.meta = template.meta || {}; template.meta.title = template.meta.title || formatBlockName(blockType); - EdiorBlockData[blockType] = { + /* eslint-disable no-param-reassign */ + previewData[blockType] = { ...template, preview, - }; - } + } as EdiorBlockData; - return EdiorBlockData; -} + return previewData; + }, + {} as Record<BlockType, EdiorBlockData>, +); -export {EditorBlocksData, getEditorBlocksData}; +export default EditorBlocksData; diff --git a/src/sub-blocks/LayoutItem/schema.ts b/src/sub-blocks/LayoutItem/schema.ts index 5722b1539f..9f47d54e1f 100644 --- a/src/sub-blocks/LayoutItem/schema.ts +++ b/src/sub-blocks/LayoutItem/schema.ts @@ -2,7 +2,7 @@ import omit from 'lodash/omit'; import {Media} from '../../blocks/Media/schema'; import metaInfo from '../../components/MetaInfo/schema'; -import {BaseProps, CardLayoutProps, MediaProps} from '../../schema/validators/common'; +import {BaseProps, CardLayoutProps} from '../../schema/validators/common'; import {AnalyticsEventSchema} from '../../schema/validators/event'; import {ContentBase} from '../../sub-blocks/Content/schema'; @@ -13,7 +13,6 @@ export const LayoutItem = { properties: { ...BaseProps, ...CardLayoutProps, - media: MediaProps, media: Media, content: omit(ContentBase, ['colSize', 'size', 'centered']), contentMargin: { From b548f2172a69a9c09ac1dd66363370eae017b850 Mon Sep 17 00:00:00 2001 From: DaffPunks <sawavas2@gmail.com> Date: Thu, 13 Feb 2025 15:37:14 +0300 Subject: [PATCH 007/118] fix: rework state, fix listeners --- src/common/postMessage.ts | 4 ++- src/common/store.ts | 31 ++++++++++--------- src/common/types/actions.ts | 3 +- .../PCEditorStoreContext.tsx | 6 ++-- .../PCEditorStoreProvider.tsx | 6 ++-- .../Editor/hooks/useAdminInitialize.tsx | 14 ++++++--- src/editor-v2/hooks/usePostMessageEvents.ts | 2 +- src/hooks/useEditorInitialize.ts | 6 ++-- src/utils/store.ts | 10 +++--- 9 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/common/postMessage.ts b/src/common/postMessage.ts index 47695356f6..65481b3297 100644 --- a/src/common/postMessage.ts +++ b/src/common/postMessage.ts @@ -35,8 +35,10 @@ export function listenPostMessageEvents<K extends keyof EventMessageTypes>( export function usePostMessageAPIListener<K extends keyof EventMessageTypes>( action: K, callback: (data: EventMessageTypes[K]) => void, + deps: unknown[] = [], ) { useEffect(() => { return listenPostMessageEvents(action, callback); - }, [action, callback]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [...deps]); } diff --git a/src/common/store.ts b/src/common/store.ts index 37a246003b..7e21101215 100644 --- a/src/common/store.ts +++ b/src/common/store.ts @@ -57,21 +57,24 @@ export interface EditorMethods { export type EditorStore = EditorState & EditorMethods; -// TODO: split methods and state +const initialStore: EditorState = { + height: 100, + zoom: 100, + manipulateOverlayMode: false, + selectedBlock: undefined, + initialized: false, + content: {blocks: []}, + blocks: [], + subBlocks: [], + global: [], + preInsertBlockType: null, + preReorderBlockPath: null, +}; + +export const createPCEditorStore = initializeStore<EditorState>(initialStore, () => ({})); + export const createEditorStore = initializeStore<EditorState, EditorMethods>( - { - height: 100, - zoom: 100, - manipulateOverlayMode: false, - selectedBlock: undefined, - initialized: false, - content: {blocks: []}, - blocks: [], - subBlocks: [], - global: [], - preInsertBlockType: null, - preReorderBlockPath: null, - }, + initialStore, (set, get) => ({ setHeight(height: number) { // We have to add 200-500px, because of bottom padding or margin of last element diff --git a/src/common/types/actions.ts b/src/common/types/actions.ts index 64c7465d53..ffb86cb764 100644 --- a/src/common/types/actions.ts +++ b/src/common/types/actions.ts @@ -17,6 +17,5 @@ export type EventMessageTypes = { export type ActionMessageTypes = { GET_SUPPORTED_BLOCKS: {}; - GET_CURRENT_CONTENT: {}; - SET_CONTENT: PageContent; + GET_INITIAL_CONTENT: {}; }; diff --git a/src/context/editorStoreContext/PCEditorStoreContext.tsx b/src/context/editorStoreContext/PCEditorStoreContext.tsx index 7eebc64a70..acceef310e 100644 --- a/src/context/editorStoreContext/PCEditorStoreContext.tsx +++ b/src/context/editorStoreContext/PCEditorStoreContext.tsx @@ -2,12 +2,12 @@ import React from 'react'; import {StoreApi} from 'zustand'; -import {EditorStore, createEditorStore} from '../../common/store'; +import {EditorState, createPCEditorStore} from '../../common/store'; export interface PCEditorStoreContextProps { - state: StoreApi<EditorStore>; + state: StoreApi<EditorState>; } export const PCEditorStoreContext = React.createContext<PCEditorStoreContextProps>({ - state: createEditorStore(), + state: createPCEditorStore(), }); diff --git a/src/context/editorStoreContext/PCEditorStoreProvider.tsx b/src/context/editorStoreContext/PCEditorStoreProvider.tsx index 9a305ca55d..22938c5079 100644 --- a/src/context/editorStoreContext/PCEditorStoreProvider.tsx +++ b/src/context/editorStoreContext/PCEditorStoreProvider.tsx @@ -2,7 +2,7 @@ import React, {PropsWithChildren, useCallback, useEffect, useRef} from 'react'; import {StoreApi} from 'zustand'; -import {EditorStore, createEditorStore} from '../../common/store'; +import {EditorState, createPCEditorStore} from '../../common/store'; import {StoreSyncMessage} from '../../common/types'; import {PCEditorStoreContext} from './PCEditorStoreContext'; @@ -10,7 +10,7 @@ import {PCEditorStoreContext} from './PCEditorStoreContext'; interface PCEditorStoreProviderProps extends PropsWithChildren {} export const PCEditorStoreProvider = ({children}: PCEditorStoreProviderProps) => { - const storeRef = useRef<StoreApi<EditorStore>>(); + const storeRef = useRef<StoreApi<EditorState>>(); const syncStore = useCallback((message: StoreSyncMessage) => { if (storeRef.current && message.state) { @@ -32,7 +32,7 @@ export const PCEditorStoreProvider = ({children}: PCEditorStoreProviderProps) => }, [syncStore]); if (!storeRef.current) { - storeRef.current = createEditorStore(); + storeRef.current = createPCEditorStore(); } return ( diff --git a/src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx b/src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx index bdf1e929db..88bb3e815e 100644 --- a/src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx +++ b/src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx @@ -17,11 +17,15 @@ const useAdminInitialize = () => { preReorderBlockPath, } = useMainEditorStore(); - usePostMessageAPIListener('ON_INIT', () => { - initialize(); - requestPostMessage('GET_SUPPORTED_BLOCKS', {}); - requestPostMessage('GET_CURRENT_CONTENT', {}); - }); + usePostMessageAPIListener( + 'ON_INIT', + () => { + initialize(); + requestPostMessage('GET_SUPPORTED_BLOCKS', {}); + requestPostMessage('GET_INITIAL_CONTENT', {}); + }, + [requestPostMessage], + ); usePostMessageAPIListener('ON_INITIAL_CONTENT', (data) => { setContent(data); diff --git a/src/editor-v2/hooks/usePostMessageEvents.ts b/src/editor-v2/hooks/usePostMessageEvents.ts index 3bb5473ffb..1b73e0cd86 100644 --- a/src/editor-v2/hooks/usePostMessageEvents.ts +++ b/src/editor-v2/hooks/usePostMessageEvents.ts @@ -1,7 +1,7 @@ import {useContext} from 'react'; import {requestActionPostMessage} from '../../common/postMessage'; -import {ActionMessageTypes} from '../../common/types/actions'; +import {ActionMessageTypes} from '../../common/types'; import {IframeContext} from '../context/iframeContext'; interface UsePostMessageRequestReturn { diff --git a/src/hooks/useEditorInitialize.ts b/src/hooks/useEditorInitialize.ts index 8d721821ca..f47e4dde14 100644 --- a/src/hooks/useEditorInitialize.ts +++ b/src/hooks/useEditorInitialize.ts @@ -21,7 +21,7 @@ export const useInitializeEditorEvents = ({ initialContent, setContent, }: UseEditorInitializeProps) => { - const {manipulateOverlayMode, disableMode, initialized, content} = usePCEditorStore(); + const {manipulateOverlayMode, initialized, content} = usePCEditorStore(); useEffect(() => { if (initialized) { @@ -29,7 +29,7 @@ export const useInitializeEditorEvents = ({ } }, [content, initialized, setContent]); - useInternalPostMessageAPIListener('GET_CURRENT_CONTENT', () => { + useInternalPostMessageAPIListener('GET_INITIAL_CONTENT', () => { sendEventPostMessage('ON_INITIAL_CONTENT', initialContent); }); @@ -76,7 +76,7 @@ export const useInitializeEditorEvents = ({ document.removeEventListener('mouseup', throttleOnMouseUp); window.removeEventListener('resize', onResize); }; - }, [disableMode, manipulateOverlayMode, onResize]); + }, [manipulateOverlayMode, onResize]); useEffect(() => { const height = document.documentElement.getBoundingClientRect().height; diff --git a/src/utils/store.ts b/src/utils/store.ts index a50614c5da..666e535732 100644 --- a/src/utils/store.ts +++ b/src/utils/store.ts @@ -1,16 +1,15 @@ import {StoreApi, create} from 'zustand'; import {devtools, subscribeWithSelector} from 'zustand/middleware'; -export function initializeStore<State, Methods>( +export function initializeStore<State extends {}, Methods extends {} = {}>( initialState: State, methods: ( set: StoreApi<State & Methods>['setState'], get: StoreApi<State & Methods>['getState'], - onStateUpdated?: () => void, ) => Methods, ) { - return (overrideInitialState?: Partial<State>, onStateUpdated?: () => void) => - create< + return (overrideInitialState?: Partial<State>) => { + return create< State & Methods, [ ['zustand/subscribeWithSelector', State & Methods], @@ -23,11 +22,12 @@ export function initializeStore<State, Methods>( return { ...initialState, ...overrideInitialState, - ...methods(set, get, onStateUpdated), + ...methods(set, get), }; }), ), ); + }; } export const removeFn = (object: object) => { From 3b3c2190cfb4b50ed57a02e7e4f2b0f0e16f7b81 Mon Sep 17 00:00:00 2001 From: DaffPunks <sawavas2@gmail.com> Date: Fri, 21 Feb 2025 16:03:36 +0300 Subject: [PATCH 008/118] fix: refactor common, store and hooks --- package-lock.json | 167 -------- package.json | 2 - playground/src/app/pc-2/content.json | 402 ++++++++++++++++++ playground/src/app/pc-2/page.tsx | 15 + playground/src/app/pc-2/styles.scss | 116 +++++ playground/src/app/{ => pc}/content.json | 0 playground/src/app/pc/page.tsx | 3 +- src/common/store.ts | 235 +--------- src/{utils/store.ts => common/utils.ts} | 0 .../editor/ChildrenWrap/ChildrenWrap.tsx | 4 +- src/components/editor/ItemWrap/ItemWrap.tsx | 4 +- src/constructor-items.ts | 9 +- .../PageConstructor/PageConstructor.tsx | 6 +- .../ConstructorBlock/ConstructorBlock.tsx | 5 +- src/context/editorStoreContext/index.ts | 1 - .../components/BigOverlay/BigOverlay.tsx | 2 +- .../components/BlockConfig/BlockConfig.tsx | 2 +- .../components/BlocksList/BlocksList.tsx | 2 +- .../components/GlobalConfig/GlobalConfig.tsx | 2 +- .../components/MiddleScreen/MiddleScreen.tsx | 2 +- src/editor-v2/components/Overlay/Overlay.tsx | 2 +- src/editor-v2/components/Source/Source.tsx | 2 +- .../components/SourceCode/SourceCode.tsx | 2 +- .../components/StoreViewer/StoreViewer.tsx | 2 +- src/editor-v2/components/Tree/Tree.tsx | 2 +- src/editor-v2/containers/Editor/Editor.tsx | 8 +- .../Editor/hooks/useAdminInitialize.tsx | 58 --- .../editorStore/MainEditorStoreContext.tsx | 2 +- .../editorStore/MainEditorStoreProvider.tsx | 5 +- src/editor-v2/context/editorStore/index.ts | 1 - .../{iframeContext.tsx => IframeContext.tsx} | 0 ...{iframeProvider.tsx => IframeProvider.tsx} | 2 +- src/editor-v2/context/iframeContext/index.ts | 4 +- .../hooks/useMainEditorInitialize.ts | 63 +++ .../hooks/useMainEditorStore.ts | 2 +- src/editor-v2/store.ts | 233 ++++++++++ src/editor/data/index.ts | 30 +- .../usePCEditorBlockMouseEvents.ts} | 11 +- ...lize.ts => usePCEditorInitializeEvents.ts} | 4 +- .../hooks/usePCEditorStore.ts | 2 +- 40 files changed, 894 insertions(+), 520 deletions(-) create mode 100644 playground/src/app/pc-2/content.json create mode 100644 playground/src/app/pc-2/page.tsx create mode 100644 playground/src/app/pc-2/styles.scss rename playground/src/app/{ => pc}/content.json (100%) rename src/{utils/store.ts => common/utils.ts} (100%) delete mode 100644 src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx rename src/editor-v2/context/iframeContext/{iframeContext.tsx => IframeContext.tsx} (100%) rename src/editor-v2/context/iframeContext/{iframeProvider.tsx => IframeProvider.tsx} (94%) create mode 100644 src/editor-v2/hooks/useMainEditorInitialize.ts rename src/editor-v2/{context/editorStore => }/hooks/useMainEditorStore.ts (75%) create mode 100644 src/editor-v2/store.ts rename src/{containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx => hooks/usePCEditorBlockMouseEvents.ts} (86%) rename src/hooks/{useEditorInitialize.ts => usePCEditorInitializeEvents.ts} (96%) rename src/{context/editorStoreContext => }/hooks/usePCEditorStore.ts (74%) diff --git a/package-lock.json b/package-lock.json index ea4b015a74..c5fda3a85e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -109,7 +109,6 @@ "lint-staged": "^11.2.6", "monaco-editor-webpack-plugin": "^7.1.0", "move-file-cli": "^3.0.0", - "next": "^14.2.3", "npm-run-all": "^4.1.5", "postcss": "^8.4.16", "postcss-loader": "^4.3.0", @@ -5268,28 +5267,6 @@ "react": ">=16" } }, - "node_modules/@next/env": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.24.tgz", - "integrity": "sha512-LAm0Is2KHTNT6IT16lxT+suD0u+VVfYNQqM+EJTKuFRRuY2z+zj01kueWXPCxbMBDt0B5vONYzabHGUNbZYAhA==", - "dev": true - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.24.tgz", - "integrity": "sha512-7Tdi13aojnAZGpapVU6meVSpNzgrFwZ8joDcNS8cJVNuP3zqqrLqeory9Xec5TJZR/stsGJdfwo8KeyloT3+rQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -6707,22 +6684,6 @@ "@svgr/core": "*" } }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true - }, - "node_modules/@swc/helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", - "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", - "dev": true, - "dependencies": { - "@swc/counter": "^0.1.3", - "tslib": "^2.4.0" - } - }, "node_modules/@tanstack/react-virtual": { "version": "3.13.9", "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.9.tgz", @@ -9369,18 +9330,6 @@ "dev": true, "license": "MIT" }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dev": true, - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -9827,12 +9776,6 @@ "node": ">=8" } }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "dev": true - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -20913,56 +20856,6 @@ "dev": true, "license": "MIT" }, - "node_modules/next": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.24.tgz", - "integrity": "sha512-En8VEexSJ0Py2FfVnRRh8gtERwDRaJGNvsvad47ShkC2Yi8AXQPXEA2vKoDJlGFSj5WE5SyF21zNi4M5gyi+SQ==", - "dev": true, - "dependencies": { - "@next/env": "14.2.24", - "@swc/helpers": "0.5.5", - "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001579", - "graceful-fs": "^4.2.11", - "postcss": "8.4.31", - "styled-jsx": "5.1.1" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=18.17.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.24", - "@next/swc-darwin-x64": "14.2.24", - "@next/swc-linux-arm64-gnu": "14.2.24", - "@next/swc-linux-arm64-musl": "14.2.24", - "@next/swc-linux-x64-gnu": "14.2.24", - "@next/swc-linux-x64-musl": "14.2.24", - "@next/swc-win32-arm64-msvc": "14.2.24", - "@next/swc-win32-ia32-msvc": "14.2.24", - "@next/swc-win32-x64-msvc": "14.2.24" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.41.2", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, "node_modules/next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -20970,34 +20863,6 @@ "dev": true, "license": "MIT" }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -25965,15 +25830,6 @@ "dev": true, "license": "MIT" }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/streamx": { "version": "2.22.0", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", @@ -26478,29 +26334,6 @@ "dev": true, "license": "ISC" }, - "node_modules/styled-jsx": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", - "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", - "dev": true, - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, "node_modules/stylelint": { "version": "15.11.0", "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.11.0.tgz", diff --git a/package.json b/package.json index 51c9513bae..94ad3bce08 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,6 @@ "lint": "run-p lint:js lint:styles lint:prettier typecheck", "typecheck": "tsc --noEmit", "dev": "npm run storybook:start", - "dev:website": "next dev playground", "storybook:start": "storybook dev -p 7009", "storybook:build": "storybook build -c .storybook -o storybook-static", "start": "node dist", @@ -209,7 +208,6 @@ "lint-staged": "^11.2.6", "monaco-editor-webpack-plugin": "^7.1.0", "move-file-cli": "^3.0.0", - "next": "^14.2.3", "npm-run-all": "^4.1.5", "postcss": "^8.4.16", "postcss-loader": "^4.3.0", diff --git a/playground/src/app/pc-2/content.json b/playground/src/app/pc-2/content.json new file mode 100644 index 0000000000..568c1b8d2d --- /dev/null +++ b/playground/src/app/pc-2/content.json @@ -0,0 +1,402 @@ +{ + "meta": { + "title": "YDB — распределённая SQL база данных с открытым исходным кодом", + "description": "YDB — это открытая распределённая SQL база данных, которая сочетает высокую доступность и масштабируемость со строгой согласованностью и ACID транзакциями.", + "sharing": { + "image": "https://storage.yandexcloud.net/ydb-site-assets/share-ydb-eng.png" + } + }, + "blocks": [ + { + "type": "header-block", + "title": "YDB", + "description": "YDB — это распределённая отказоустойчивая Distributed SQL база данных с открытым исходным кодом, которая сочетает в себе высокую доступность и масштабируемость со строгой согласованностью и транзакциями ACID. Она поддерживает одновременное выполнение транзакционных (OLTP), аналитических (OLAP) и потоковых нагрузок.", + "width": "s", + "verticalOffset": "m", + "imageSize": "m", + "background": { + "image": { + "src": "https://storage.yandexcloud.net/ydb-site-assets/cover.png", + "disableCompress": true, + "alt": "Фон YDB" + }, + "color": "#e2eaff", + "fullWidth": false + }, + "buttons": [ + { + "text": "Быстрый старт", + "theme": "accent", + "url": "/../docs/ru/quickstart" + }, + { + "text": "Документация", + "theme": "outlined", + "url": "/../docs/ru/" + } + ] + }, + { + "type": "card-layout-block", + "title": "Что я могу делать с YDB?", + "animated": false, + "colSizes": { + "all": 12, + "sm": 6, + "md": 4 + }, + "anchor": { + "url": "whatcanido", + "text": "Что я могу делать с YDB?" + }, + "description": "", + "children": [ + { + "type": "background-card", + "backgroundColor": "#CCE7FF", + "title": "Транзакционные нагрузки (OLTP)", + "text": "Вы можете использовать YDB для хранения состояния вашего приложения, независимо от объема данных или частоты их изменения. Обрабатывать петабайты при миллионах транзакций в секунду — не проблема.", + "controlPosition": "footer", + "buttons": [ + { + "text": "Узнать больше", + "theme": "outlined", + "url": "/docs/ru/concepts/" + } + ] + }, + { + "type": "background-card", + "backgroundColor": "rgba(107,132,153,.12)", + "title": "Аналитические нагрузки (OLAP)", + "text": "Вы можете создавать аналитические отчёты на основе хранимых в YDB данных с производительностью, сопоставимой со специализированными аналитическими СУБД. При этом никаких компромиссов по согласованности и доступности не потребуется.", + "controlPosition": "footer", + "buttons": [ + { + "text": "Узнать больше", + "theme": "outlined", + "url": "/docs/ru/concepts/datamodel/table#column-oriented-tables" + } + ] + }, + { + "type": "background-card", + "backgroundColor": "rgba(107,132,153,.12)", + "title": "Потоковые нагрузки", + "text": "Вы можете использовать функциональность YDB-топиков для надёжной отправки данных между вашими приложениями или отслеживания изменений в таблицах YDB. Можно выбрать как семантику доставки сообщений ровно один раз (exactly once), так и не менее одного раза (at least once).", + "controlPosition": "footer", + "buttons": [ + { + "text": "Узнать больше", + "theme": "outlined", + "url": "/docs/ru/concepts/topic" + } + ] + } + ] + }, + { + "type": "extended-features-block", + "title": "Почему YDB?", + "animated": false, + "colSizes": { + "all": 12, + "sm": 6, + "md": 4 + }, + "items": [ + { + "title": "Эластичность и масштабируемость", + "text": "Добавляйте или удаляйте узлы на лету, чтобы легко масштабировать кластер по мере необходимости. YDB имеет отдельные слои вычисления и хранения, что позволяет независимо добавлять дисковую ёмкость или вычислительные ресурсы в зависимости от того, чего не хватает при текущей нагрузке.", + "icon": "https://storage.yandexcloud.net/ydb-site-assets/features-icons/icon_01.svg" + }, + { + "title": "Отказоустойчивость", + "text": "YDB спроектирована для работы в трёх зонах доступности и обеспечивает работоспособность даже в случае выхода из строя одной из них. Она автоматически восстанавливается после сбоя диска, сервера или датацентра с минимальной задержкой для приложений.", + "icon": "https://storage.yandexcloud.net/ydb-site-assets/features-icons/icon_03.svg" + }, + { + "title": "Простота в использовании", + "text": "Работа с кластером YDB ощущается как работа с одноузловой СУБД с безграничными ресурсами благодаря строгой согласованности, ACID-транзакциям, высокопроизводительным запросам, возможности загрузки больших объёмов данных, а также поддержке знакомого диалекта SQL и JSON API.", + "icon": "https://storage.yandexcloud.net/ydb-site-assets/features-icons/icon_02.svg" + }, + { + "title": "Универсальность", + "text": "Благодаря поддержке различных видов нагрузок в одной системе YDB может заменить несколько систем хранения и обработки данных или всю корпоративную экосистему данных в компании.", + "icon": "https://storage.yandexcloud.net/ydb-site-assets/features-icons/icon_06.svg" + }, + { + "title": "Открытый исходный код", + "text": "[Исходный код YDB](https://github.com/ydb-platform/ydb) опубликован под лицензией Apache 2.0, накладывающей минимум ограничений на использование. Таким образом, нет рисков, связанных с привязкой к конкретному поставщику или провайдеру облачных услуг.", + "icon": "https://storage.yandexcloud.net/ydb-site-assets/features-icons/icon_05.svg" + }, + { + "title": "Совместимость с любым окружением", + "text": "YDB можно развернуть в [Kubernetes](https://github.com/ydb-platform/ydb-kubernetes-operator), в любом облачном окружении или в корпоративных ЦОД. Либо можно использовать YDB как [управляемый сервис в Yandex Cloud](https://cloud.yandex.ru/services/ydb). Также возможны локальные эксперименты на любом компьютере.", + "icon": "https://storage.yandexcloud.net/ydb-site-assets/features-icons/icon_04.svg" + } + ] + }, + { + "type": "banner-block", + "animated": false, + "title": "Оценка производительности PostgreSQL vs Distributed DBMS", + "subtitle": "TPC-C является наиболее известным набором тестов производительности для OLTP. Мы подготовили исследование производительности, сравнивающее PostgreSQL, YDB и CockroachDB в отказоустойчивых конфигурациях.", + "image": "https://storage.yandexcloud.net/ydb-site-assets/banner-block-pg.png", + "color": "#CCE7FF", + "button": { + "text": "Читать дальше", + "theme": "raised", + "url": "https://habr.com/ru/companies/ydb/articles/801587/" + } + }, + { + "type": "card-layout-block", + "title": "Кто использует YDB?", + "animated": false, + "colSizes": { + "all": 12, + "sm": 6, + "md": 4 + }, + "anchor": { + "url": "whouses", + "text": "Кто использует YDB?" + }, + "description": "", + "children": [ + { + "type": "layout-item", + "media": { + "image": { + "src": "https://storage.yandexcloud.net/ydb-site-assets/clients/metrika.png", + "disableCompress": true + } + }, + "content": { + "title": "Метрика", + "text": "[Метрика](https://metrika.yandex.ru/) - одна из крупнейших в мире платформ мобильной и веб-аналитики. Она полагается на YDB для создания пользовательских сессий на лету.\n\nПереход на YDB позволил Метрике расширить объём хранимых данных и бесконечно наращивать обрабатываемую нагрузку. Теперь одна из баз данных Метрики в YDB содержит более 400 ТБ данных и выдерживает нагрузку более 1 000 000 RPS." + }, + "fullScreen": false, + "border": true + }, + { + "type": "layout-item", + "media": { + "image": { + "src": "https://storage.yandexcloud.net/ydb-site-assets/clients/yandex-cloud-new.png", + "disableCompress": true + } + }, + "content": { + "title": "Yandex Cloud", + "text": "YDB отвечает за слой хранения для сетевых дисков [Yandex Cloud](https://cloud.yandex.ru), используется в качестве СУБД для хранения данных и метаданных облачной инфраструктуры и сервисов платформы, а также в качестве базы данных для облачного Control Plane.\n\nИнфраструктурным и платформенным сервисам Yandex Cloud необходима высокая доступность и масштабируемость, поэтому платформа выбрала YDB в качестве ключевого компонента." + }, + "fullScreen": false, + "border": true + }, + { + "type": "layout-item", + "media": { + "image": { + "src": "https://storage.yandexcloud.net/ydb-site-assets/clients/praktikum.jpg", + "disableCompress": true + } + }, + "content": { + "title": "Практикум", + "text": "[Практикум](https://practicum.yandex.ru/) - это онлайн-ориентированная образовательная платформа. Она использует YDB в качестве гибкого хранилища состояний для своих микросервисов.\n\nНативная поддержка многоарендности в YDB позволяет им избежать создания и управления выделенными базами данных для каждого компонента сервиса." + }, + "fullScreen": false, + "border": true + }, + { + "type": "layout-item", + "media": { + "image": { + "src": "https://storage.yandexcloud.net/ydb-site-assets/clients/market.png", + "disableCompress": true + } + }, + "content": { + "title": "Яндекс Маркет", + "text": "[Яндекс Маркет](https://market.yandex.ru) - один из крупнейших сервисов электронной коммерции в СНГ. Многие ключевые функции сервиса, такие как корзина, скидки, и оформление заказа, используют YDB для хранения своего состояния.\n\nВыбор YDB в качестве базы данных позволил Яндекс Маркету выдержать стократное увеличение нагрузки на корзину при соблюдении строгих гарантий времени отклика." + }, + "fullScreen": false, + "border": true + }, + { + "type": "layout-item", + "media": { + "image": { + "src": "https://storage.yandexcloud.net/ydb-site-assets/clients/jaeger.png", + "disableCompress": true + } + }, + "content": { + "title": "Auto.ru", + "text": "[Auto.ru](https://auto.ru) снизила потребление CPU для трассировочной базы данных [Jaeger](https://www.jaegertracing.io/) в три раза после перехода на YDB, что позволило записывать 500 000 трассировок в секунду без семплирования.\n\nУспешная реализация YDB в качестве хранилища трассировок доказала применимость и ключевые свойства, такие как масштабируемость, отказоустойчивость и строгая согласованность. В результате Auto.ru выбрала YDB в качестве реляционной базы данных для некоторых своих микросервисов." + }, + "fullScreen": false, + "border": true + }, + { + "type": "layout-item", + "media": { + "image": { + "src": "https://storage.yandexcloud.net/ydb-site-assets/clients/alice-new-speakers.png", + "disableCompress": "true\"" + } + }, + "content": { + "title": "Алиса", + "text": "[Алиса](https://yandex.ru/alice) - это голосовой помощник и экосистема умного дома. После перехода на YDB команда Алисы решила проблемы синхронизации между дата‑центрами, простоев при переключении основных серверов, снизила нагрузку на команду DevOps, увеличила объём хранимых данных до сотен терабайт и нагрузку до сотен тысяч запросов в секунду.\n\nПереход на YDB позволил отказаться от ручного шардинга данных и добиться строгой согласованности в кросс-датацентровом кластере. Сейчас команда Алисы использует YDB как реляционную базу данных и как базу для хранения логов и трейсов." + }, + "fullScreen": false, + "border": true + } + ] + }, + { + "type": "slider-block", + "animated": false, + "anchor": { + "url": "scenarios", + "text": "scenarios" + }, + "title": { + "text": "В каких типовых сценариях стоит использовать YDB?", + "textSize": "m" + }, + "children": [ + { + "type": "basic-card", + "title": "Работа с внезапным ростом нагрузки", + "text": "Эластичность YDB позволяет быстро изменять количество ресурсов, выделенных базе данных, чтобы обеспечить необходимую пропускную способность в соответствии с нагрузкой. Вы можете легко увеличить или уменьшить количество вычислительных ресурсов в зависимости от предстоящих нагрузок, например, на «Чёрную пятницу» или для маркетинговых кампаний." + }, + { + "type": "basic-card", + "title": "Хранение Observability данных", + "text": "[Колоночные таблицы YDB](/docs/ru/concepts/column-table) отлично подходят для хранения логов и метрик с простым доступом к ним через SQL интерфейс. Также, низкое потребление вычислительных ресурсов и масштабируемость YDB делают запись [Jaeger трейсов](https://www.jaegertracing.io/) выгодной по себестоимости и лёгкой в использовании." + }, + { + "type": "basic-card", + "title": "Документоориентированная СУБД", + "text": "Не смотря на строгую систему типов данных YDB, поддержка [типа данных JSON и связанных с ним функций](/docs/ru/yql/reference/builtins/json), расширяет возможности YDB в роли системы хранения неструктурированных документов." + }, + { + "type": "basic-card", + "title": "Кеш с SQL-интерфейсом", + "text": "Быстрый отклик и масштабируемость пропускной способности позволяют использовать YDB одновременно в качестве онлайн-базы данных и предварительно посчитанного кеша. Возможности SQL значительно повышают удобство использования и позволяют проводить оперативную аналитику данных в кеше. Например, сайты туроператоров и агрегаторов путешествий могут использовать базу данных для кеширования результатов поиска авиабилетов или туров, а также для пересчёта цен и проверки сезонной доступности." + }, + { + "type": "basic-card", + "title": "Централизованная система учёта запасов", + "text": "YDB обеспечивает строгую целостность транзакций. Это позволяет предоставлять консистентные данные о запасах на всех складах и торговых объектах, и делает YDB подходящим решением для электронной коммерции, приложений складской или транспортной логистики." + }, + { + "type": "basic-card", + "title": "Система хранения данных для Internet of Things (IoT) экосистемы", + "text": "Поддержка автоматического шардирования YDB позволяет обрабатывать потоки данных от большого количества устройств — профиль нагрузки, который встречается в проектах интернета вещей." + } + ] + }, + { + "type": "media-block", + "animated": false, + "direction": "content-media", + "title": "Можете вкратце объяснить, что такое YDB, в видеоформате?", + "description": "Конечно, смотрите →", + "anchor": { + "url": "video", + "text": "Video" + }, + "largeMedia": true, + "media": { + "youtube": "https://youtu.be/QVmhR__wAJg" + } + }, + { + "type": "banner-block", + "animated": false, + "title": "Учебный курс по YDB от участника сообщества пользователей", + "subtitle": "_Сторонняя разработка - Contribution_\n\nНачальный [учебный курс по YDB](https://stepik.org/course/186264/promo), разработанный одним из наших пользователей - Владом Бурмистровым, позволяет ознакомиться с ключевыми свойствами, возможностями и архитектурой YDB, а также научиться основным приемам по работе с YDB.", + "color": "#CCE7FF", + "image": "https://storage.yandexcloud.net/ydb-site-assets/careers/img_4.png", + "button": { + "text": "Описание и программа курса", + "theme": "raised", + "url": "https://stepik.org/course/186264/promo" + } + }, + { + "type": "tabs-block", + "title": { + "text": "Как начать?" + }, + "anchor": { + "url": "howtostart", + "text": "Как начать?" + }, + "items": [ + { + "tabName": "Docker", + "title": "Docker", + "text": " Создайте рабочий каталог и запустите локальный контейнер YDB из этого каталога:\n```bash mkdir ydb-local && cd ydb-local \ndocker run -d --rm --name ydb-local -h localhost \\\n--platform linux/amd64 \\\n-p 2135:2135 -p 2136:2136 -p 8765:8765 \\\n-v $(pwd)/ydb_certs:/ydb_certs -v $(pwd)/ydb_data:/ydb_data \\\n-e GRPC_TLS_PORT=2135 -e GRPC_PORT=2136 -e MON_PORT=8765 \\\nydbplatform/local-ydb:latest\n```\nПерейдите в раздел [«Быстрый старт»](/docs/ru/quickstart) в документации YDB, чтобы получить дополнительную информацию. " + }, + { + "tabName": "Minikube", + "title": "Minikube", + "text": " Установите Kubernetes CLI [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl) и менеджер пакетов [Helm 3](https://helm.sh/docs/intro/install/).\nУстановите и запустите [Minikube](https://kubernetes.io/ru/docs/tasks/tools/install-minikube/).\nСклонируйте репозиторий с [YDB Kubernetes Operator](https://github.com/ydb-platform/ydb-kubernetes-operator).\n```\ngit clone https://github.com/ydb-platform/ydb-kubernetes-operator && cd ydb-kubernetes-operator\n```\nУстановите контроллер YDB на кластер.\n```\nhelm upgrade --install ydb-operator deploy/ydb-operator --set metrics.enabled=false\n```\nПримените манифест для создания кластера YDB.\n```\nkubectl apply -f samples/minikube/storage.yaml\n```\nДождитесь, пока `kubectl get storages.ydb.tech` не станет `Ready`.\nПримените манифест для создания базы данных.\n```\nkubectl apply -f samples/minikube/database.yaml\n```\nДождитесь, пока `kubectl get databases.ydb.tech` не станет `Ready`.\n\nПосле обработки манифеста будет создан объект StatefulSet, описывающий набор динамических узлов. Созданная база данных будет доступна изнутри кластера Kubernetes по DNS имени `database-minikube-sample` на порту 2135.\n\nПерейдите в раздел [«Быстрый старт»](/docs/ru/quickstart) в документации YDB, чтобы получить дополнительную информацию. " + }, + { + "tabName": "Установка вручную", + "title": "Установка вручную (только для Linux x86_64)", + "text": " Создайте рабочий каталог, запустите скрипт установки из него, а затем запустите одноузловую базу данных YDB с включенным режимом работы в оперативной памяти:\n``` mkdir ydb-local && cd ydb-local\ncurl https://install.ydb.tech | bash\n./start.sh ram\n```\nПерейдите в раздел [«Быстрый старт»](/docs/ru/quickstart) в документации YDB, чтобы получить дополнительную информацию. " + } + ] + }, + { + "type": "icons-block", + "size": "s", + "title": "Как оставаться на связи?", + "items": [ + { + "url": "https://github.com/ydb-platform/ydb", + "text": "GitHub", + "src": "https://storage.yandexcloud.net/ydb-site-assets/community/git.svg" + }, + { + "url": "https://t.me/ydb_ru", + "text": "Telegram", + "src": "https://storage.yandexcloud.net/ydb-site-assets/community/telegram.svg" + }, + { + "url": "https://habr.com/ru/companies/ydb/articles/", + "text": "Habr", + "src": "https://storage.yandexcloud.net/ydb-site-assets/community/habr.svg" + }, + { + "url": "https://blog.ydb.tech", + "text": "Medium", + "src": "https://storage.yandexcloud.net/ydb-site-assets/community/medium.svg" + }, + { + "url": "https://twitter.com/YDBPlatform", + "text": "Twitter", + "src": "https://storage.yandexcloud.net/ydb-site-assets/community/twitter.svg" + }, + { + "url": "https://www.linkedin.com/company/ydb-platform/", + "text": "LinkedIn", + "src": "https://storage.yandexcloud.net/ydb-site-assets/community/linkedin.svg" + }, + { + "url": "https://www.youtube.com/c/YDBPlatform", + "text": "YouTube", + "src": "https://storage.yandexcloud.net/ydb-site-assets/community/youtube.svg" + } + ] + } + ] +} diff --git a/playground/src/app/pc-2/page.tsx b/playground/src/app/pc-2/page.tsx new file mode 100644 index 0000000000..5b54e6796e --- /dev/null +++ b/playground/src/app/pc-2/page.tsx @@ -0,0 +1,15 @@ +'use client'; +import React from 'react'; + +import {PageConstructor, PageConstructorProvider} from '../../../../src'; + +import content from './content.json'; +import './styles.scss'; + +export default function Home() { + return ( + <PageConstructorProvider projectSettings={{disableCompress: true}}> + <PageConstructor content={content} /> + </PageConstructorProvider> + ); +} diff --git a/playground/src/app/pc-2/styles.scss b/playground/src/app/pc-2/styles.scss new file mode 100644 index 0000000000..158d942167 --- /dev/null +++ b/playground/src/app/pc-2/styles.scss @@ -0,0 +1,116 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap'); + +.g-root_theme_light { + --g-border-radius-xs: 0px; + --g-border-radius-s: 0px; + --g-border-radius-m: 0px; + --g-border-radius-l: 0px; + --g-border-radius-xl: 0px; + + --g-font-family-sans: 'Inter', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif; + --g-font-family-monospace: 'Roboto Mono', 'Menlo', 'Monaco', 'Consolas', 'Ubuntu Mono', + 'Liberation Mono', 'DejaVu Sans Mono', 'Courier New', 'Courier', monospace; + + --g-color-private-brand-50: rgba(203, 255, 92, 0.1); + --g-color-private-brand-100: rgba(203, 255, 92, 0.15); + --g-color-private-brand-150: rgba(203, 255, 92, 0.2); + --g-color-private-brand-200: rgba(203, 255, 92, 0.3); + --g-color-private-brand-250: rgba(203, 255, 92, 0.4); + --g-color-private-brand-300: rgba(203, 255, 92, 0.5); + --g-color-private-brand-350: rgba(203, 255, 92, 0.6); + --g-color-private-brand-400: rgba(203, 255, 92, 0.7); + --g-color-private-brand-450: rgba(203, 255, 92, 0.8); + --g-color-private-brand-500: rgba(203, 255, 92, 0.9); + --g-color-private-brand-550-solid: rgb(203, 255, 92); + --g-color-private-brand-1000-solid: rgb(59, 63, 43); + --g-color-private-brand-950-solid: rgb(68, 74, 46); + --g-color-private-brand-900-solid: rgb(85, 97, 51); + --g-color-private-brand-850-solid: rgb(102, 119, 57); + --g-color-private-brand-800-solid: rgb(119, 142, 63); + --g-color-private-brand-750-solid: rgb(135, 165, 69); + --g-color-private-brand-700-solid: rgb(152, 187, 75); + --g-color-private-brand-650-solid: rgb(169, 210, 80); + --g-color-private-brand-600-solid: rgb(186, 232, 86); + --g-color-private-brand-500-solid: rgb(208, 255, 108); + --g-color-private-brand-450-solid: rgb(213, 255, 125); + --g-color-private-brand-400-solid: rgb(219, 255, 141); + --g-color-private-brand-350-solid: rgb(224, 255, 157); + --g-color-private-brand-300-solid: rgb(229, 255, 174); + --g-color-private-brand-250-solid: rgb(234, 255, 190); + --g-color-private-brand-200-solid: rgb(239, 255, 206); + --g-color-private-brand-150-solid: rgb(245, 255, 222); + --g-color-private-brand-100-solid: rgb(247, 255, 231); + --g-color-private-brand-50-solid: rgb(250, 255, 239); + + --g-color-base-brand: rgb(203, 255, 92); + --g-color-base-background: rgb(255, 255, 255); + --g-color-base-brand-hover: var(--g-color-private-brand-600-solid); + --g-color-base-selection: var(--g-color-private-brand-200); + --g-color-base-selection-hover: var(--g-color-private-brand-300); + --g-color-line-brand: var(--g-color-private-brand-600-solid); + --g-color-text-brand: var(--g-color-private-brand-700-solid); + --g-color-text-brand-heavy: var(--g-color-private-brand-700-solid); + --g-color-text-brand-contrast: rgba(0, 0, 0, 0.85); + --g-color-text-link: var(--g-color-private-brand-600-solid); + --g-color-text-link-hover: var(--g-color-private-brand-800-solid); + --g-color-text-link-visited: var(--g-color-private-purple-550-solid); + --g-color-text-link-visited-hover: var(--g-color-private-purple-800-solid); +} + +.g-root_theme_dark { + --g-border-radius-xs: 0px; + --g-border-radius-s: 0px; + --g-border-radius-m: 0px; + --g-border-radius-l: 0px; + --g-border-radius-xl: 0px; + + --g-font-family-sans: 'Inter', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif; + --g-font-family-monospace: 'Roboto Mono', 'Menlo', 'Monaco', 'Consolas', 'Ubuntu Mono', + 'Liberation Mono', 'DejaVu Sans Mono', 'Courier New', 'Courier', monospace; + + --g-color-private-brand-50: rgba(203, 255, 92, 0.1); + --g-color-private-brand-100: rgba(203, 255, 92, 0.15); + --g-color-private-brand-150: rgba(203, 255, 92, 0.2); + --g-color-private-brand-200: rgba(203, 255, 92, 0.3); + --g-color-private-brand-250: rgba(203, 255, 92, 0.4); + --g-color-private-brand-300: rgba(203, 255, 92, 0.5); + --g-color-private-brand-350: rgba(203, 255, 92, 0.6); + --g-color-private-brand-400: rgba(203, 255, 92, 0.7); + --g-color-private-brand-450: rgba(203, 255, 92, 0.8); + --g-color-private-brand-500: rgba(203, 255, 92, 0.9); + --g-color-private-brand-550-solid: rgb(203, 255, 92); + --g-color-private-brand-1000-solid: rgb(247, 255, 231); + --g-color-private-brand-950-solid: rgb(245, 255, 222); + --g-color-private-brand-900-solid: rgb(239, 255, 206); + --g-color-private-brand-850-solid: rgb(234, 255, 190); + --g-color-private-brand-800-solid: rgb(229, 255, 174); + --g-color-private-brand-750-solid: rgb(224, 255, 157); + --g-color-private-brand-700-solid: rgb(219, 255, 141); + --g-color-private-brand-650-solid: rgb(213, 255, 125); + --g-color-private-brand-600-solid: rgb(208, 255, 108); + --g-color-private-brand-500-solid: rgb(186, 232, 86); + --g-color-private-brand-450-solid: rgb(169, 210, 80); + --g-color-private-brand-400-solid: rgb(152, 187, 75); + --g-color-private-brand-350-solid: rgb(135, 165, 69); + --g-color-private-brand-300-solid: rgb(119, 142, 63); + --g-color-private-brand-250-solid: rgb(102, 119, 57); + --g-color-private-brand-200-solid: rgb(85, 97, 51); + --g-color-private-brand-150-solid: rgb(68, 74, 46); + --g-color-private-brand-100-solid: rgb(59, 63, 43); + --g-color-private-brand-50-solid: rgb(51, 52, 40); + + --g-color-base-brand: rgb(203, 255, 92); + --g-color-base-background: rgb(34, 29, 34); + --g-color-base-brand-hover: var(--g-color-private-brand-650-solid); + --g-color-base-selection: var(--g-color-private-brand-150); + --g-color-base-selection-hover: var(--g-color-private-brand-200); + --g-color-line-brand: var(--g-color-private-brand-600-solid); + --g-color-text-brand: var(--g-color-private-brand-600-solid); + --g-color-text-brand-heavy: var(--g-color-private-brand-700-solid); + --g-color-text-brand-contrast: rgba(0, 0, 0, 0.9); + --g-color-text-link: var(--g-color-private-brand-550-solid); + --g-color-text-link-hover: var(--g-color-private-brand-700-solid); + --g-color-text-link-visited: var(--g-color-private-purple-700-solid); + --g-color-text-link-visited-hover: var(--g-color-private-purple-850-solid); +} diff --git a/playground/src/app/content.json b/playground/src/app/pc/content.json similarity index 100% rename from playground/src/app/content.json rename to playground/src/app/pc/content.json diff --git a/playground/src/app/pc/page.tsx b/playground/src/app/pc/page.tsx index e208d76c6b..88c94a9f2e 100644 --- a/playground/src/app/pc/page.tsx +++ b/playground/src/app/pc/page.tsx @@ -2,7 +2,8 @@ import React from 'react'; import {PageConstructor, PageConstructorProvider} from '../../../../src'; -import content from '../content.json'; + +import content from './content.json'; export default function Home() { return ( diff --git a/src/common/store.ts b/src/common/store.ts index 7e21101215..41db0f6706 100644 --- a/src/common/store.ts +++ b/src/common/store.ts @@ -1,21 +1,9 @@ import _ from 'lodash'; -// TODO: remove imports from editor-v2 -import {ZOOM_STEPS} from '../editor-v2/constants'; -import { - duplicateArrayItem, - generateChildrenPathFromArray, - getDestinationShiftBeforeReorder, - insert, - isItemsNeighbours, - modifyObjectByPath, - removeFromArray, - reorderArrayItems, -} from '../editor-v2/utils'; -import {ConstructorBlock, PageContent} from '../models'; -import {initializeStore} from '../utils/store'; +import {PageContent} from '../models'; -import {ConfigInput, DynamicFormValue, ItemConfig} from './types'; +import {ConfigInput, ItemConfig} from './types'; +import {initializeStore} from './utils'; export interface EditorState { height?: number; @@ -34,30 +22,7 @@ export interface EditorState { preReorderBlockPath: number[] | null; } -export interface EditorMethods { - initialize(): void; - setSelectedBlock(path: number[]): void; - setHeight(height: number): void; - setZoom(zoom: number): void; - increaseZoom(): void; - decreaseZoom(): void; - setConfig(data: Pick<EditorState, 'blocks' | 'subBlocks' | 'global'>): void; - setContent(data: PageContent): void; - insertBlock(path: number[], blockType: string, position?: 'prepend' | 'append'): void; - enableInsertMode(blockType: string): void; - enableReorderMode(path: number[]): void; - disableMode(): void; - updateField(path: string, value: DynamicFormValue): void; - deleteBlock(path: number[]): void; - duplicateBlock(path: number[]): void; - reorderBlock(path: number[], destination: number[], position?: 'prepend' | 'append'): void; - resetInitialize(): void; - resetBlocks(): void; -} - -export type EditorStore = EditorState & EditorMethods; - -const initialStore: EditorState = { +export const initialStore: EditorState = { height: 100, zoom: 100, manipulateOverlayMode: false, @@ -72,195 +37,3 @@ const initialStore: EditorState = { }; export const createPCEditorStore = initializeStore<EditorState>(initialStore, () => ({})); - -export const createEditorStore = initializeStore<EditorState, EditorMethods>( - initialStore, - (set, get) => ({ - setHeight(height: number) { - // We have to add 200-500px, because of bottom padding or margin of last element - // which is not taken into calculation of final height - const newHeight = height + 500; - set((state) => ({...state, height: newHeight})); - }, - setZoom(zoom) { - if (zoom > 0) { - set((state) => ({...state, zoom})); - } - }, - increaseZoom() { - const currentZoom = get().zoom; - - for (const step of ZOOM_STEPS) { - if (currentZoom < step) { - get().setZoom(step); - break; - } - } - }, - decreaseZoom() { - const currentZoom = get().zoom; - const reverseSteps = ZOOM_STEPS.slice().reverse(); - - for (const step of reverseSteps) { - if (currentZoom > step) { - get().setZoom(step); - break; - } - } - }, - setConfig(data) { - set((state) => ({...state, ...data})); - }, - insertBlock: (arrayPath, blockType, position = 'append') => { - if (position === 'append') { - // TODO: fix - // eslint-disable-next-line no-not-accumulator-reassign/no-not-accumulator-reassign, no-param-reassign - arrayPath[arrayPath.length - 1] = arrayPath[arrayPath.length - 1] + 1; - } - - const blocksConfig = get().content.blocks; - const blocksData = get().blocks; - - const foundBlock = blocksData.find(({type}) => type === blockType); - const defaultValue = - foundBlock && foundBlock.schema.default - ? {...foundBlock.schema.default, type: blockType} - : {type: blockType}; - - const newBlocksConfig = modifyObjectByPath( - blocksConfig, - arrayPath, - (parentBlocks, index) => - insert(parentBlocks, index, defaultValue as ConstructorBlock), - ); - - set((state) => ({ - ...state, - content: {...state.content, blocks: newBlocksConfig}, - })); - }, - enableInsertMode(blockType: string) { - set((state) => ({ - ...state, - manipulateOverlayMode: 'insert', - preInsertBlockType: blockType, - })); - }, - disableMode() { - set((state) => ({ - ...state, - manipulateOverlayMode: false, - preInsertBlockType: undefined, - preReorderBlockPath: undefined, - })); - }, - enableReorderMode(path) { - set((state) => ({ - ...state, - manipulateOverlayMode: 'reorder', - preReorderBlockPath: path, - })); - }, - setContent(content) { - set((state) => ({ - ...state, - content: content, - })); - }, - initialize() { - set((state) => ({ - ...state, - initialized: true, - })); - }, - setSelectedBlock(path) { - set((state) => ({ - ...state, - selectedBlock: path, - })); - }, - updateField(path, value) { - set((state) => { - const newConfig = _.set(state.content, path, value); - return { - ...state, - content: newConfig, - }; - }); - }, - deleteBlock: (arrayPath) => { - const blocksConfig = get().content.blocks; - - const newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, removeFromArray); - - set((state) => ({ - ...state, - content: {...state.content, blocks: newBlocksConfig}, - })); - }, - duplicateBlock: (arrayPath) => { - const blocksConfig = get().content.blocks; - - const newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, duplicateArrayItem); - - set((state) => ({ - ...state, - content: {...state.content, blocks: newBlocksConfig}, - })); - }, - reorderBlock: (arrayPath, destination, position = 'append') => { - if (position === 'append') { - // TODO: fix - // eslint-disable-next-line no-not-accumulator-reassign/no-not-accumulator-reassign, no-param-reassign - destination[destination.length - 1] = destination[destination.length - 1] + 1; - } - - let newBlocksConfig: ConstructorBlock[]; - const blocksConfig = get().content.blocks; - // Copy - const copiedBlock = _.get(blocksConfig, generateChildrenPathFromArray(arrayPath)); - - if (isItemsNeighbours(arrayPath, destination)) { - newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, (parentBlocks) => { - return reorderArrayItems( - parentBlocks, - arrayPath[arrayPath.length - 1], - destination[destination.length - 1], - ); - }); - } else { - const arrayDest = getDestinationShiftBeforeReorder(arrayPath, destination); - - // Delete - const blocksConfigWithoutBlock = modifyObjectByPath( - blocksConfig, - arrayPath, - removeFromArray, - ); - // Paste - newBlocksConfig = modifyObjectByPath( - blocksConfigWithoutBlock, - arrayDest, - (parentBlocks, index) => insert(parentBlocks, index, copiedBlock), - ); - } - - set((state) => ({ - ...state, - content: {...state.content, blocks: newBlocksConfig}, - })); - }, - resetInitialize: () => { - set((state) => ({ - ...state, - initialized: false, - })); - }, - resetBlocks: () => { - set((state) => ({ - ...state, - content: {...state.content, blocks: []}, - })); - }, - }), -); diff --git a/src/utils/store.ts b/src/common/utils.ts similarity index 100% rename from src/utils/store.ts rename to src/common/utils.ts diff --git a/src/components/editor/ChildrenWrap/ChildrenWrap.tsx b/src/components/editor/ChildrenWrap/ChildrenWrap.tsx index f6129ae0c4..7ddbce1d1b 100644 --- a/src/components/editor/ChildrenWrap/ChildrenWrap.tsx +++ b/src/components/editor/ChildrenWrap/ChildrenWrap.tsx @@ -1,7 +1,7 @@ import React, {PropsWithChildren, ReactNode, useCallback, useContext, useState} from 'react'; -import useEditorBlockMouseEvents from '../../../containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents'; import {BlockIdContext} from '../../../context/blockIdContext'; +import usePCEditorBlockMouseEvents from '../../../hooks/usePCEditorBlockMouseEvents'; import {block} from '../../../utils'; import './ChildrenWrap.scss'; @@ -21,7 +21,7 @@ const ChildrenWrap = (props: ChildrenWrapProps) => { } }, []); const parentBlockId = useContext(BlockIdContext); - const {onMouseUp, onMouseMove} = useEditorBlockMouseEvents([parentBlockId, 0], element); + const {onMouseUp, onMouseMove} = usePCEditorBlockMouseEvents([parentBlockId, 0], element); return ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions diff --git a/src/components/editor/ItemWrap/ItemWrap.tsx b/src/components/editor/ItemWrap/ItemWrap.tsx index cf8fbad926..c002f418dd 100644 --- a/src/components/editor/ItemWrap/ItemWrap.tsx +++ b/src/components/editor/ItemWrap/ItemWrap.tsx @@ -1,7 +1,7 @@ import React, {PropsWithChildren, useCallback, useContext, useState} from 'react'; -import useEditorBlockMouseEvents from '../../../containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents'; import {BlockIdContext} from '../../../context/blockIdContext'; +import usePCEditorBlockMouseEvents from '../../../hooks/usePCEditorBlockMouseEvents'; import {block} from '../../../utils'; import './ItemWrap.scss'; @@ -21,7 +21,7 @@ const ItemWrap = (props: ItemWrapProps) => { }, []); const {children, index} = props; const parentBlockId = useContext(BlockIdContext); - const adminBlockMouseEvents = useEditorBlockMouseEvents([parentBlockId, index], element); + const adminBlockMouseEvents = usePCEditorBlockMouseEvents([parentBlockId, index], element); return ( <div ref={blockRef} className={b()} {...adminBlockMouseEvents}> diff --git a/src/constructor-items.ts b/src/constructor-items.ts index 5a0b12fdc4..cea5121b14 100644 --- a/src/constructor-items.ts +++ b/src/constructor-items.ts @@ -44,12 +44,7 @@ import TableBlockConfig from './blocks/Table'; import TabsBlockConfig from './blocks/Tabs'; import {BlockConfig} from './common/types'; import {BlockType, NavigationItemType, SubBlockType} from './models'; -import { - GithubButton, - NavigationButton, - NavigationDropdown, - NavigationLink, -} from './navigation/components/NavigationItem'; +import {GithubButton, NavigationButton, NavigationDropdown, NavigationLink} from './navigation'; import SocialIcon from './navigation/components/SocialIcon/SocialIcon'; import { BackgroundCard, @@ -77,6 +72,7 @@ import PriceDetailedConfig from './sub-blocks/PriceDetailed'; import QuoteConfig from './sub-blocks/Quote'; /** + * TODO: remove it * @deprecated use blockDataMap **/ export const blockMap = { @@ -104,6 +100,7 @@ export const blockMap = { }; /** + * TODO: remove it * @deprecated use blockDataMap **/ export const subBlockMap = { diff --git a/src/containers/PageConstructor/PageConstructor.tsx b/src/containers/PageConstructor/PageConstructor.tsx index c3e49b23fd..e0442e2da8 100644 --- a/src/containers/PageConstructor/PageConstructor.tsx +++ b/src/containers/PageConstructor/PageConstructor.tsx @@ -8,11 +8,11 @@ import BrandFooter from '../../components/BrandFooter/BrandFooter'; import RootCn from '../../components/RootCn'; import {blockMap, navItemMap, subBlockMap} from '../../constructor-items'; import {AnimateContext} from '../../context/animateContext'; -import {usePCEditorStore} from '../../context/editorStoreContext'; import {InnerContext} from '../../context/innerContext'; import {ProjectSettingsContext} from '../../context/projectSettingsContext'; import {useTheme} from '../../context/theme'; -import {useInitializeEditorEvents} from '../../hooks/useEditorInitialize'; +import {usePCEditorInitializeEvents} from '../../hooks/usePCEditorInitializeEvents'; +import {usePCEditorStore} from '../../hooks/usePCEditorStore'; import { BlockTypes, CustomConfig, @@ -67,7 +67,7 @@ export const Constructor = (props: PageConstructorProps) => { const store = usePCEditorStore(); const {initialized} = store; - useInitializeEditorEvents({initialContent: {blocks, background}, setContent}); + usePCEditorInitializeEvents({initialContent: {blocks, background}, setContent}); const {context} = useMemo( () => ({ diff --git a/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx b/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx index cd9c6b4850..9d593cf901 100644 --- a/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx +++ b/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx @@ -4,11 +4,10 @@ import pick from 'lodash/pick'; import BlockBase from '../../../../components/BlockBase/BlockBase'; import {BlockDecoration} from '../../../../customization/BlockDecoration'; +import usePCEditorBlockMouseEvents from '../../../../hooks/usePCEditorBlockMouseEvents'; import {BlockDecorationProps, ConstructorBlock as ConstructorBlockType} from '../../../../models'; import {block} from '../../../../utils'; -import useEditorBlockMouseEvents from './hooks/useEditorBlockMouseEvents'; - import './ConstructorBlock.scss'; interface ConstructorBlockProps extends Pick<BlockDecorationProps, 'index'> { @@ -28,7 +27,7 @@ export const ConstructorBlock = ({ setElement(node); } }, []); - const adminBlockMouseEvents = useEditorBlockMouseEvents([index], element); + const adminBlockMouseEvents = usePCEditorBlockMouseEvents([index], element); const {type} = data; const blockBaseProps = useMemo( diff --git a/src/context/editorStoreContext/index.ts b/src/context/editorStoreContext/index.ts index 7fa6e621ac..2cb38452e5 100644 --- a/src/context/editorStoreContext/index.ts +++ b/src/context/editorStoreContext/index.ts @@ -1,3 +1,2 @@ -export * from './hooks/usePCEditorStore'; export * from './PCEditorStoreContext'; export * from './PCEditorStoreProvider'; diff --git a/src/editor-v2/components/BigOverlay/BigOverlay.tsx b/src/editor-v2/components/BigOverlay/BigOverlay.tsx index 1401916855..8a33f9bead 100644 --- a/src/editor-v2/components/BigOverlay/BigOverlay.tsx +++ b/src/editor-v2/components/BigOverlay/BigOverlay.tsx @@ -5,8 +5,8 @@ import {Stop} from '@gravity-ui/icons'; import {usePostMessageAPIListener} from '../../../common/postMessage'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; -import {useMainEditorStore} from '../../context/editorStore'; import {IframeContext} from '../../context/iframeContext'; +import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import './BigOverlay.scss'; diff --git a/src/editor-v2/components/BlockConfig/BlockConfig.tsx b/src/editor-v2/components/BlockConfig/BlockConfig.tsx index cc2b93a28f..852739b701 100644 --- a/src/editor-v2/components/BlockConfig/BlockConfig.tsx +++ b/src/editor-v2/components/BlockConfig/BlockConfig.tsx @@ -5,7 +5,7 @@ import _ from 'lodash'; import {DynamicFormValue} from '../../../common/types'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; -import {useMainEditorStore} from '../../context/editorStore'; +import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {generateChildrenPathFromArray} from '../../utils'; import DynamicForm from '../DynamicForm/DynamicForm'; diff --git a/src/editor-v2/components/BlocksList/BlocksList.tsx b/src/editor-v2/components/BlocksList/BlocksList.tsx index a6f6dcce5d..e608b8c1f1 100644 --- a/src/editor-v2/components/BlocksList/BlocksList.tsx +++ b/src/editor-v2/components/BlocksList/BlocksList.tsx @@ -6,7 +6,7 @@ import _ from 'lodash'; import {ItemConfig} from '../../../common/types'; import {block} from '../../../utils'; -import {useMainEditorStore} from '../../context/editorStore'; +import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import './BlocksList.scss'; diff --git a/src/editor-v2/components/GlobalConfig/GlobalConfig.tsx b/src/editor-v2/components/GlobalConfig/GlobalConfig.tsx index 3fb4e55007..57594bca37 100644 --- a/src/editor-v2/components/GlobalConfig/GlobalConfig.tsx +++ b/src/editor-v2/components/GlobalConfig/GlobalConfig.tsx @@ -3,7 +3,7 @@ import React from 'react'; import {DynamicFormValue} from '../../../common/types'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; -import {useMainEditorStore} from '../../context/editorStore'; +import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import DynamicForm from '../DynamicForm/DynamicForm'; import './GlobalConfig.scss'; diff --git a/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx b/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx index e0a1bac099..efa1499b66 100644 --- a/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx +++ b/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx @@ -5,8 +5,8 @@ import {Loader} from '@gravity-ui/uikit'; import {usePostMessageAPIListener} from '../../../common/postMessage'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; -import {useMainEditorStore} from '../../context/editorStore'; import {IframeContext} from '../../context/iframeContext'; +import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import Overlay from '../Overlay/Overlay'; import TopBar from '../TopBar/TopBar'; diff --git a/src/editor-v2/components/Overlay/Overlay.tsx b/src/editor-v2/components/Overlay/Overlay.tsx index f42078f903..b900ed4b8c 100644 --- a/src/editor-v2/components/Overlay/Overlay.tsx +++ b/src/editor-v2/components/Overlay/Overlay.tsx @@ -6,7 +6,7 @@ import {Button, Icon} from '@gravity-ui/uikit'; import {usePostMessageAPIListener} from '../../../common/postMessage'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; -import {useMainEditorStore} from '../../context/editorStore'; +import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import './Overlay.scss'; diff --git a/src/editor-v2/components/Source/Source.tsx b/src/editor-v2/components/Source/Source.tsx index 5e56661de9..9c3ed18f60 100644 --- a/src/editor-v2/components/Source/Source.tsx +++ b/src/editor-v2/components/Source/Source.tsx @@ -5,8 +5,8 @@ import {Button, Icon, TextInput} from '@gravity-ui/uikit'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; -import {useMainEditorStore} from '../../context/editorStore'; import {IframeContext} from '../../context/iframeContext'; +import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import './Source.scss'; diff --git a/src/editor-v2/components/SourceCode/SourceCode.tsx b/src/editor-v2/components/SourceCode/SourceCode.tsx index 94e84a2c37..42d3570893 100644 --- a/src/editor-v2/components/SourceCode/SourceCode.tsx +++ b/src/editor-v2/components/SourceCode/SourceCode.tsx @@ -14,7 +14,7 @@ import yaml from 'js-yaml'; import {ClassNameProps, PageContent} from '../../../models'; import {block} from '../../../utils'; -import {useMainEditorStore} from '../../context/editorStore'; +import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import './SourceCode.scss'; diff --git a/src/editor-v2/components/StoreViewer/StoreViewer.tsx b/src/editor-v2/components/StoreViewer/StoreViewer.tsx index c45f091e31..b0bd87d9de 100644 --- a/src/editor-v2/components/StoreViewer/StoreViewer.tsx +++ b/src/editor-v2/components/StoreViewer/StoreViewer.tsx @@ -4,9 +4,9 @@ import {Code} from '@gravity-ui/icons'; import {Button, Icon} from '@gravity-ui/uikit'; import ReactJson from 'react-json-view'; +import {removeFn} from '../../../common/utils'; import {ClassNameProps} from '../../../models'; import {block} from '../../../utils'; -import {removeFn} from '../../../utils/store'; import './StoreViewer.scss'; diff --git a/src/editor-v2/components/Tree/Tree.tsx b/src/editor-v2/components/Tree/Tree.tsx index a8874e8c28..26b6d85064 100644 --- a/src/editor-v2/components/Tree/Tree.tsx +++ b/src/editor-v2/components/Tree/Tree.tsx @@ -5,7 +5,7 @@ import {Button, Card, Icon} from '@gravity-ui/uikit'; import {ItemConfig} from '../../../common/types'; import {block} from '../../../utils'; -import {useMainEditorStore} from '../../context/editorStore'; +import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import './Tree.scss'; diff --git a/src/editor-v2/containers/Editor/Editor.tsx b/src/editor-v2/containers/Editor/Editor.tsx index 85eb3c3bdf..0ec1cf3210 100644 --- a/src/editor-v2/containers/Editor/Editor.tsx +++ b/src/editor-v2/containers/Editor/Editor.tsx @@ -7,10 +7,10 @@ import MiddleScreen from '../../components/MiddleScreen/MiddleScreen'; import {Panels} from '../../components/Panels/Panels'; import {Sidebar} from '../../components/Sidebar/Sidebar'; import StoreViewer from '../../components/StoreViewer/StoreViewer'; -import {MainEditorStoreProvider, useMainEditorStore} from '../../context/editorStore'; +import {MainEditorStoreProvider} from '../../context/editorStore'; import {IframeProvider} from '../../context/iframeContext'; - -import useAdminInitialize from './hooks/useAdminInitialize'; +import useMainEditorInitialize from '../../hooks/useMainEditorInitialize'; +import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import './Editor.scss'; @@ -26,7 +26,7 @@ const EditorView = (_props: EditorViewProps) => { const store = useMainEditorStore(); const {manipulateOverlayMode, disableMode} = store; - useAdminInitialize(); + useMainEditorInitialize(); // Disable insert mode on any MouseUp event // Maybe should be attached to body diff --git a/src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx b/src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx deleted file mode 100644 index 88bb3e815e..0000000000 --- a/src/editor-v2/containers/Editor/hooks/useAdminInitialize.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import {usePostMessageAPIListener} from '../../../../common/postMessage'; -import {useMainEditorStore} from '../../../context/editorStore'; -import {usePostMessageEvents} from '../../../hooks/usePostMessageEvents'; - -const useAdminInitialize = () => { - const {requestPostMessage} = usePostMessageEvents(); - const { - initialize, - setConfig, - setContent, - manipulateOverlayMode, - disableMode, - insertBlock, - reorderBlock, - setSelectedBlock, - preInsertBlockType, - preReorderBlockPath, - } = useMainEditorStore(); - - usePostMessageAPIListener( - 'ON_INIT', - () => { - initialize(); - requestPostMessage('GET_SUPPORTED_BLOCKS', {}); - requestPostMessage('GET_INITIAL_CONTENT', {}); - }, - [requestPostMessage], - ); - - usePostMessageAPIListener('ON_INITIAL_CONTENT', (data) => { - setContent(data); - }); - - usePostMessageAPIListener('ON_SUPPORTED_BLOCKS', (data) => { - setConfig(data); - }); - - usePostMessageAPIListener('ON_MOUSE_UP', ({path, position}) => { - if (manipulateOverlayMode === 'insert' && path && position && preInsertBlockType) { - insertBlock( - path, - preInsertBlockType, - ['left', 'top'].includes(position) ? 'prepend' : 'append', - ); - } - if (manipulateOverlayMode === 'reorder' && path && position && preReorderBlockPath) { - reorderBlock( - preReorderBlockPath, - path, - ['left', 'top'].includes(position) ? 'prepend' : 'append', - ); - setSelectedBlock(path); - } - disableMode(); - }); -}; - -export default useAdminInitialize; diff --git a/src/editor-v2/context/editorStore/MainEditorStoreContext.tsx b/src/editor-v2/context/editorStore/MainEditorStoreContext.tsx index b14610b740..07a9eb77ef 100644 --- a/src/editor-v2/context/editorStore/MainEditorStoreContext.tsx +++ b/src/editor-v2/context/editorStore/MainEditorStoreContext.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {StoreApi} from 'zustand'; -import {EditorStore, createEditorStore} from '../../../common/store'; +import {EditorStore, createEditorStore} from '../../store'; export interface MainEditorStoreContextProps { state: StoreApi<EditorStore>; diff --git a/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx b/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx index c91e56a4c5..43bafff897 100644 --- a/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx +++ b/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx @@ -2,9 +2,10 @@ import React, {PropsWithChildren, useCallback, useContext, useEffect, useRef} fr import {StoreApi} from 'zustand'; -import {EditorState, EditorStore, createEditorStore} from '../../../common/store'; +import {EditorState} from '../../../common/store'; import {StoreSyncMessage} from '../../../common/types'; -import {removeFn} from '../../../utils/store'; +import {removeFn} from '../../../common/utils'; +import {EditorStore, createEditorStore} from '../../store'; import {IframeContext} from '../iframeContext'; import {MainEditorStoreContext} from './MainEditorStoreContext'; diff --git a/src/editor-v2/context/editorStore/index.ts b/src/editor-v2/context/editorStore/index.ts index ced0a0fcd8..7804dc9a12 100644 --- a/src/editor-v2/context/editorStore/index.ts +++ b/src/editor-v2/context/editorStore/index.ts @@ -1,3 +1,2 @@ -export * from './hooks/useMainEditorStore'; export * from './MainEditorStoreContext'; export * from './MainEditorStoreProvider'; diff --git a/src/editor-v2/context/iframeContext/iframeContext.tsx b/src/editor-v2/context/iframeContext/IframeContext.tsx similarity index 100% rename from src/editor-v2/context/iframeContext/iframeContext.tsx rename to src/editor-v2/context/iframeContext/IframeContext.tsx diff --git a/src/editor-v2/context/iframeContext/iframeProvider.tsx b/src/editor-v2/context/iframeContext/IframeProvider.tsx similarity index 94% rename from src/editor-v2/context/iframeContext/iframeProvider.tsx rename to src/editor-v2/context/iframeContext/IframeProvider.tsx index f6261ac550..d6b3454c06 100644 --- a/src/editor-v2/context/iframeContext/iframeProvider.tsx +++ b/src/editor-v2/context/iframeContext/IframeProvider.tsx @@ -1,6 +1,6 @@ import React, {PropsWithChildren, useState} from 'react'; -import {IframeContext} from './iframeContext'; +import {IframeContext} from './IframeContext'; interface IframeProviderProps extends PropsWithChildren { initialUrl?: string; diff --git a/src/editor-v2/context/iframeContext/index.ts b/src/editor-v2/context/iframeContext/index.ts index 7da30b4e20..dc2795f0f3 100644 --- a/src/editor-v2/context/iframeContext/index.ts +++ b/src/editor-v2/context/iframeContext/index.ts @@ -1,2 +1,2 @@ -export * from './iframeContext'; -export * from './iframeProvider'; +export * from './IframeContext'; +export * from './IframeProvider'; diff --git a/src/editor-v2/hooks/useMainEditorInitialize.ts b/src/editor-v2/hooks/useMainEditorInitialize.ts new file mode 100644 index 0000000000..b1ba595600 --- /dev/null +++ b/src/editor-v2/hooks/useMainEditorInitialize.ts @@ -0,0 +1,63 @@ +import {usePostMessageAPIListener} from '../../common/postMessage'; + +import {useMainEditorStore} from './useMainEditorStore'; +import {usePostMessageEvents} from './usePostMessageEvents'; + +const useMainEditorInitialize = () => { + const {requestPostMessage} = usePostMessageEvents(); + const { + initialize, + setConfig, + setContent, + manipulateOverlayMode, + disableMode, + insertBlock, + reorderBlock, + setSelectedBlock, + preInsertBlockType, + preReorderBlockPath, + } = useMainEditorStore(); + + usePostMessageAPIListener( + 'ON_INIT', + () => { + initialize(); + requestPostMessage('GET_SUPPORTED_BLOCKS', {}); + requestPostMessage('GET_INITIAL_CONTENT', {}); + }, + [requestPostMessage], + ); + + usePostMessageAPIListener('ON_INITIAL_CONTENT', (data) => { + setContent(data); + }); + + usePostMessageAPIListener('ON_SUPPORTED_BLOCKS', (data) => { + setConfig(data); + }); + + usePostMessageAPIListener( + 'ON_MOUSE_UP', + ({path, position}) => { + if (manipulateOverlayMode === 'insert' && path && position && preInsertBlockType) { + insertBlock( + path, + preInsertBlockType, + ['left', 'top'].includes(position) ? 'prepend' : 'append', + ); + } + if (manipulateOverlayMode === 'reorder' && path && position && preReorderBlockPath) { + reorderBlock( + preReorderBlockPath, + path, + ['left', 'top'].includes(position) ? 'prepend' : 'append', + ); + setSelectedBlock(path); + } + disableMode(); + }, + [preInsertBlockType, preReorderBlockPath], + ); +}; + +export default useMainEditorInitialize; diff --git a/src/editor-v2/context/editorStore/hooks/useMainEditorStore.ts b/src/editor-v2/hooks/useMainEditorStore.ts similarity index 75% rename from src/editor-v2/context/editorStore/hooks/useMainEditorStore.ts rename to src/editor-v2/hooks/useMainEditorStore.ts index 7cd4000059..49c3496a90 100644 --- a/src/editor-v2/context/editorStore/hooks/useMainEditorStore.ts +++ b/src/editor-v2/hooks/useMainEditorStore.ts @@ -2,7 +2,7 @@ import {useContext} from 'react'; import {useStore} from 'zustand'; -import {MainEditorStoreContext} from '../MainEditorStoreContext'; +import {MainEditorStoreContext} from '../context/editorStore'; export const useMainEditorStore = () => { const {state} = useContext(MainEditorStoreContext); diff --git a/src/editor-v2/store.ts b/src/editor-v2/store.ts new file mode 100644 index 0000000000..a75e1262b3 --- /dev/null +++ b/src/editor-v2/store.ts @@ -0,0 +1,233 @@ +import _ from 'lodash'; + +import {EditorState, initialStore} from '../common/store'; +import {DynamicFormValue} from '../common/types'; +import {initializeStore} from '../common/utils'; +import {ConstructorBlock, PageContent} from '../models'; + +import {ZOOM_STEPS} from './constants'; +import { + duplicateArrayItem, + generateChildrenPathFromArray, + getDestinationShiftBeforeReorder, + insert, + isItemsNeighbours, + modifyObjectByPath, + removeFromArray, + reorderArrayItems, +} from './utils'; + +export interface EditorMethods { + initialize(): void; + setSelectedBlock(path: number[]): void; + setHeight(height: number): void; + setZoom(zoom: number): void; + increaseZoom(): void; + decreaseZoom(): void; + setConfig(data: Pick<EditorState, 'blocks' | 'subBlocks' | 'global'>): void; + setContent(data: PageContent): void; + insertBlock(path: number[], blockType: string, position?: 'prepend' | 'append'): void; + enableInsertMode(blockType: string): void; + enableReorderMode(path: number[]): void; + disableMode(): void; + updateField(path: string, value: DynamicFormValue): void; + deleteBlock(path: number[]): void; + duplicateBlock(path: number[]): void; + reorderBlock(path: number[], destination: number[], position?: 'prepend' | 'append'): void; + resetInitialize(): void; + resetBlocks(): void; +} + +export type EditorStore = EditorState & EditorMethods; + +export const createEditorStore = initializeStore<EditorState, EditorMethods>( + initialStore, + (set, get) => ({ + setHeight(height: number) { + // We have to add 200-500px, because of bottom padding or margin of last element + // which is not taken into calculation of final height + const newHeight = height + 500; + set((state) => ({...state, height: newHeight})); + }, + setZoom(zoom) { + if (zoom > 0) { + set((state) => ({...state, zoom})); + } + }, + increaseZoom() { + const currentZoom = get().zoom; + + for (const step of ZOOM_STEPS) { + if (currentZoom < step) { + get().setZoom(step); + break; + } + } + }, + decreaseZoom() { + const currentZoom = get().zoom; + const reverseSteps = ZOOM_STEPS.slice().reverse(); + + for (const step of reverseSteps) { + if (currentZoom > step) { + get().setZoom(step); + break; + } + } + }, + setConfig(data) { + set((state) => ({...state, ...data})); + }, + insertBlock: (arrayPath, blockType, position = 'append') => { + if (position === 'append') { + // TODO: fix + // eslint-disable-next-line no-not-accumulator-reassign/no-not-accumulator-reassign, no-param-reassign + arrayPath[arrayPath.length - 1] = arrayPath[arrayPath.length - 1] + 1; + } + + const blocksConfig = get().content.blocks; + const blocksData = get().blocks; + + const foundBlock = blocksData.find(({type}) => type === blockType); + const defaultValue = + foundBlock && foundBlock.schema.default + ? {...foundBlock.schema.default, type: blockType} + : {type: blockType}; + + const newBlocksConfig = modifyObjectByPath( + blocksConfig, + arrayPath, + (parentBlocks, index) => + insert(parentBlocks, index, defaultValue as ConstructorBlock), + ); + + set((state) => ({ + ...state, + content: {...state.content, blocks: newBlocksConfig}, + })); + }, + enableInsertMode(blockType: string) { + set((state) => ({ + ...state, + manipulateOverlayMode: 'insert', + preInsertBlockType: blockType, + })); + }, + disableMode() { + set((state) => ({ + ...state, + manipulateOverlayMode: false, + preInsertBlockType: undefined, + preReorderBlockPath: undefined, + })); + }, + enableReorderMode(path) { + set((state) => ({ + ...state, + manipulateOverlayMode: 'reorder', + preReorderBlockPath: path, + })); + }, + setContent(content) { + set((state) => ({ + ...state, + content: content, + })); + }, + initialize() { + set((state) => ({ + ...state, + initialized: true, + })); + }, + setSelectedBlock(path) { + set((state) => ({ + ...state, + selectedBlock: path, + })); + }, + updateField(path, value) { + set((state) => { + const newConfig = _.set(state.content, path, value); + return { + ...state, + content: newConfig, + }; + }); + }, + deleteBlock: (arrayPath) => { + const blocksConfig = get().content.blocks; + + const newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, removeFromArray); + + set((state) => ({ + ...state, + content: {...state.content, blocks: newBlocksConfig}, + })); + }, + duplicateBlock: (arrayPath) => { + const blocksConfig = get().content.blocks; + + const newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, duplicateArrayItem); + + set((state) => ({ + ...state, + content: {...state.content, blocks: newBlocksConfig}, + })); + }, + reorderBlock: (arrayPath, destination, position = 'append') => { + if (position === 'append') { + // TODO: fix + // eslint-disable-next-line no-not-accumulator-reassign/no-not-accumulator-reassign, no-param-reassign + destination[destination.length - 1] = destination[destination.length - 1] + 1; + } + + let newBlocksConfig: ConstructorBlock[]; + const blocksConfig = get().content.blocks; + // Copy + const copiedBlock = _.get(blocksConfig, generateChildrenPathFromArray(arrayPath)); + + if (isItemsNeighbours(arrayPath, destination)) { + newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, (parentBlocks) => { + return reorderArrayItems( + parentBlocks, + arrayPath[arrayPath.length - 1], + destination[destination.length - 1], + ); + }); + } else { + const arrayDest = getDestinationShiftBeforeReorder(arrayPath, destination); + + // Delete + const blocksConfigWithoutBlock = modifyObjectByPath( + blocksConfig, + arrayPath, + removeFromArray, + ); + // Paste + newBlocksConfig = modifyObjectByPath( + blocksConfigWithoutBlock, + arrayDest, + (parentBlocks, index) => insert(parentBlocks, index, copiedBlock), + ); + } + + set((state) => ({ + ...state, + content: {...state.content, blocks: newBlocksConfig}, + })); + }, + resetInitialize: () => { + set((state) => ({ + ...state, + initialized: false, + })); + }, + resetBlocks: () => { + set((state) => ({ + ...state, + content: {...state.content, blocks: []}, + })); + }, + }), +); diff --git a/src/editor/data/index.ts b/src/editor/data/index.ts index c3d8f6322b..4ec434f5a4 100644 --- a/src/editor/data/index.ts +++ b/src/editor/data/index.ts @@ -15,10 +15,10 @@ export interface EdiorBlockData { }; } -const getBlockTemplate = (blockType: BlockType) => - require(`./templates/${blockType}.json`) as Omit<EdiorBlockData, 'preview'>; +const getBlockTemplate = (blockType: BlockType): Promise<Omit<EdiorBlockData, 'preview'>> => + import(`./templates/${blockType}.json`).then((data) => data.default); -const getBlockPreview = (blockType: BlockType) => { +const getBlockPreview = (blockType: BlockType): PreviewComponent => { try { if (blockType === BlockType.HeaderBlock) { return HeaderBlock; @@ -31,24 +31,26 @@ const getBlockPreview = (blockType: BlockType) => { return DefaultPreview; } }; +type EditorBlocksData = Partial<Record<BlockType, EdiorBlockData>>; + +async function getEditorBlocksData(): Promise<EditorBlocksData> { + const EdiorBlockData: EditorBlocksData = {}; + + for (const blockType of Object.values(BlockType)) { + const template = await getBlockTemplate(blockType as BlockType); -const EditorBlocksData = Object.values(BlockType).reduce( - (previewData, blockType) => { - const template = getBlockTemplate(blockType); const preview = getBlockPreview(blockType); template.meta = template.meta || {}; template.meta.title = template.meta.title || formatBlockName(blockType); - /* eslint-disable no-param-reassign */ - previewData[blockType] = { + EdiorBlockData[blockType] = { ...template, preview, - } as EdiorBlockData; + }; + } - return previewData; - }, - {} as Record<BlockType, EdiorBlockData>, -); + return EdiorBlockData; +} -export default EditorBlocksData; +export {EditorBlocksData, getEditorBlocksData}; diff --git a/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx b/src/hooks/usePCEditorBlockMouseEvents.ts similarity index 86% rename from src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx rename to src/hooks/usePCEditorBlockMouseEvents.ts index b339857163..d908f584d1 100644 --- a/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx +++ b/src/hooks/usePCEditorBlockMouseEvents.ts @@ -2,11 +2,12 @@ import React, {useCallback, useEffect} from 'react'; import _ from 'lodash'; -import {usePCEditorStore} from '../../../../../context/editorStoreContext'; -import {sendEventPostMessage} from '../../../../../hooks/usePostMessageAPI'; -import {getCursorPositionOverElement} from '../../../../../utils/editor'; +import {getCursorPositionOverElement} from '../utils/editor'; -const useEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement) => { +import {usePCEditorStore} from './usePCEditorStore'; +import {sendEventPostMessage} from './usePostMessageAPI'; + +const usePCEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement) => { const {selectedBlock} = usePCEditorStore(); const onMouseUp = useCallback( @@ -84,4 +85,4 @@ const useEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement) }; }; -export default useEditorBlockMouseEvents; +export default usePCEditorBlockMouseEvents; diff --git a/src/hooks/useEditorInitialize.ts b/src/hooks/usePCEditorInitializeEvents.ts similarity index 96% rename from src/hooks/useEditorInitialize.ts rename to src/hooks/usePCEditorInitializeEvents.ts index f47e4dde14..fa4a61b81a 100644 --- a/src/hooks/useEditorInitialize.ts +++ b/src/hooks/usePCEditorInitializeEvents.ts @@ -5,11 +5,11 @@ import _ from 'lodash'; import {ItemConfig} from '../common/types'; import {blockDataMap} from '../constructor-items'; -import {usePCEditorStore} from '../context/editorStoreContext'; import {PageContent} from '../models'; import {defaultComponentsConfigurationSchema} from '../schema'; import {generateFromAJV} from '../utils/form-generator'; +import {usePCEditorStore} from './usePCEditorStore'; import {sendEventPostMessage, useInternalPostMessageAPIListener} from './usePostMessageAPI'; interface UseEditorInitializeProps { @@ -17,7 +17,7 @@ interface UseEditorInitializeProps { setContent: (content: PageContent) => void; } -export const useInitializeEditorEvents = ({ +export const usePCEditorInitializeEvents = ({ initialContent, setContent, }: UseEditorInitializeProps) => { diff --git a/src/context/editorStoreContext/hooks/usePCEditorStore.ts b/src/hooks/usePCEditorStore.ts similarity index 74% rename from src/context/editorStoreContext/hooks/usePCEditorStore.ts rename to src/hooks/usePCEditorStore.ts index 31e9226aa0..3c9cdf851e 100644 --- a/src/context/editorStoreContext/hooks/usePCEditorStore.ts +++ b/src/hooks/usePCEditorStore.ts @@ -2,7 +2,7 @@ import {useContext} from 'react'; import {useStore} from 'zustand'; -import {PCEditorStoreContext} from '../PCEditorStoreContext'; +import {PCEditorStoreContext} from '../context/editorStoreContext'; export const usePCEditorStore = () => { const {state} = useContext(PCEditorStoreContext); From 7df0a1b1cc833c4249d247b1e8aa72f0bcc5b2e3 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Tue, 4 Mar 2025 18:23:56 +0300 Subject: [PATCH 009/118] refactor: distinct editor v2, add components config --- {src/common => common}/postMessage.ts | 1 - {src/common => common}/store.ts | 2 +- {src/common => common}/types/actions.ts | 0 {src/common => common}/types/common.ts | 0 {src/common => common}/types/forms.ts | 0 {src/common => common}/types/index.ts | 0 {src/common => common}/types/messages.ts | 0 {src/common => common}/utils.ts | 0 .../components/BigOverlay/BigOverlay.scss | 4 +- .../components/BigOverlay/BigOverlay.tsx | 12 +- .../components/DynamicForm/DynamicForm.scss | 8 ++ .../components/DynamicForm/DynamicForm.tsx | 18 ++- .../DynamicForm/FieldBase/FieldBase.scss | 2 - .../DynamicForm/FieldBase/FieldBase.tsx | 22 ++-- .../DynamicForm/Fields/Array/Array.scss | 4 +- .../DynamicForm/Fields/Array/Array.tsx | 19 ++- .../Fields/Array/ItemButton/ItemButton.tsx | 28 +++-- .../DynamicForm/Fields/Boolean/Boolean.tsx | 15 +-- .../DynamicForm/Fields/Number/Number.tsx | 15 +-- .../DynamicForm/Fields/Object/Object.scss | 6 +- .../DynamicForm/Fields/Object/Object.tsx | 22 ++-- .../DynamicForm/Fields/OneOf/OneOf.scss | 4 +- .../DynamicForm/Fields/OneOf/OneOf.tsx | 21 ++-- .../DynamicForm/Fields/Select/Select.tsx | 15 +-- .../DynamicForm/Fields/Text/Text.tsx | 15 +-- .../DynamicForm/Fields/TextArea/TextArea.tsx | 15 +-- .../components/DynamicForm/utils.ts | 2 +- .../components/MiddleScreen/MiddleScreen.scss | 9 +- .../components/MiddleScreen/MiddleScreen.tsx | 31 +++-- .../components/Overlay/Overlay.scss | 2 - .../components/Overlay/Overlay.tsx | 15 ++- .../components/Panels/Panels.scss | 12 +- .../components/Panels/Panels.tsx | 10 +- editor-v2/components/Sidebar/Sidebar.scss | 18 +++ editor-v2/components/Sidebar/Sidebar.tsx | 28 +++++ .../components/StoreViewer/StoreViewer.scss | 4 +- .../components/StoreViewer/StoreViewer.tsx | 13 +-- editor-v2/components/Tabs/Tabs.scss | 35 ++++++ editor-v2/components/Tabs/Tabs.tsx | 72 ++++++++++++ {src/editor-v2 => editor-v2}/constants.ts | 0 .../containers}/BlockConfig/BlockConfig.scss | 5 - .../containers}/BlockConfig/BlockConfig.tsx | 16 +-- .../containers}/BlocksList/BlocksList.scss | 2 - .../containers}/BlocksList/BlocksList.tsx | 9 +- .../containers/Editor/Editor.scss | 2 - .../containers/Editor/Editor.tsx | 36 ++++-- .../GlobalConfig/GlobalConfig.scss | 2 - .../containers}/GlobalConfig/GlobalConfig.tsx | 13 ++- .../containers}/Source/Source.scss | 12 +- .../containers}/Source/Source.tsx | 16 +-- .../containers}/SourceCode/SourceCode.scss | 2 - .../containers}/SourceCode/SourceCode.tsx | 15 +-- .../containers}/Tree/Tree.scss | 2 - .../containers}/Tree/Tree.tsx | 7 +- .../ViewSwitches/ViewSwitches.scss | 4 +- .../containers}/ViewSwitches/ViewSwitches.tsx | 34 ++---- .../containers/__stories__/Editor.stories.tsx | 3 +- .../containers/__stories__/utils.ts | 0 .../editorStore/MainEditorStoreContext.tsx | 1 - .../editorStore/MainEditorStoreProvider.tsx | 1 - .../context/editorStore/index.ts | 0 .../context/iframeContext/IframeContext.tsx | 0 .../context/iframeContext/IframeProvider.tsx | 7 +- .../context/iframeContext/index.ts | 0 editor-v2/hooks/useEditorTabs.tsx | 64 ++++++++++ .../hooks/useMainEditorInitialize.ts | 0 .../hooks/useMainEditorStore.ts | 1 - .../hooks/usePostMessageEvents.ts | 0 {src/editor-v2 => editor-v2}/index.ts | 0 {src/editor-v2 => editor-v2}/store.ts | 2 +- .../styles/mixins.scss | 1 + editor-v2/styles/root.scss | 2 + .../styles/variables.scss | 1 + editor-v2/utils/cn.ts | 5 + {src/editor-v2 => editor-v2}/utils/code.ts | 2 +- {src/editor-v2 => editor-v2}/utils/index.ts | 2 +- editor-v2/utils/store.ts | 0 playground/src/app/page.scss | 4 + playground/src/app/page.tsx | 23 +++- playground/src/app/pc-2/navigation.json | 48 ++++++++ playground/src/app/pc-2/page.tsx | 3 +- src/blocks/CardLayout/index.ts | 2 +- src/blocks/Header/dynamic-form.ts | 2 +- src/blocks/Slider/dynamic-form.ts | 2 +- src/blocks/TestEditorBlock/form.ts | 2 +- src/components/Image/dynamic-form.ts | 2 +- .../PCEditorStoreContext.tsx | 2 +- .../PCEditorStoreProvider.tsx | 4 +- .../DynamicForm/Fields/Object/Object.scss | 10 -- src/editor-v2/components/Sidebar/Sidebar.scss | 86 -------------- src/editor-v2/components/Sidebar/Sidebar.tsx | 110 ------------------ src/editor-v2/components/TopBar/TopBar.scss | 34 ------ src/editor-v2/components/TopBar/TopBar.tsx | 41 ------- src/editor-v2/icons/Tablet.tsx | 23 ---- src/editor-v2/styles/root.scss | 9 -- src/editor-v2/utils/store.ts | 24 ---- src/hooks/usePCEditorInitializeEvents.ts | 2 +- src/hooks/usePostMessageAPI.ts | 4 +- src/index.ts | 1 - 99 files changed, 570 insertions(+), 636 deletions(-) rename {src/common => common}/postMessage.ts (94%) rename {src/common => common}/store.ts (95%) rename {src/common => common}/types/actions.ts (100%) rename {src/common => common}/types/common.ts (100%) rename {src/common => common}/types/forms.ts (100%) rename {src/common => common}/types/index.ts (100%) rename {src/common => common}/types/messages.ts (100%) rename {src/common => common}/utils.ts (100%) rename {src/editor-v2 => editor-v2}/components/BigOverlay/BigOverlay.scss (89%) rename {src/editor-v2 => editor-v2}/components/BigOverlay/BigOverlay.tsx (91%) create mode 100644 editor-v2/components/DynamicForm/DynamicForm.scss rename {src/editor-v2 => editor-v2}/components/DynamicForm/DynamicForm.tsx (95%) rename {src/editor-v2 => editor-v2}/components/DynamicForm/FieldBase/FieldBase.scss (92%) rename {src/editor-v2 => editor-v2}/components/DynamicForm/FieldBase/FieldBase.tsx (85%) rename {src/editor-v2 => editor-v2}/components/DynamicForm/Fields/Array/Array.scss (88%) rename {src/editor-v2 => editor-v2}/components/DynamicForm/Fields/Array/Array.tsx (94%) rename {src/editor-v2 => editor-v2}/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx (82%) rename {src/editor-v2 => editor-v2}/components/DynamicForm/Fields/Boolean/Boolean.tsx (57%) rename {src/editor-v2 => editor-v2}/components/DynamicForm/Fields/Number/Number.tsx (61%) rename src/editor-v2/components/DynamicForm/DynamicForm.scss => editor-v2/components/DynamicForm/Fields/Object/Object.scss (55%) rename {src/editor-v2 => editor-v2}/components/DynamicForm/Fields/Object/Object.tsx (69%) rename {src/editor-v2 => editor-v2}/components/DynamicForm/Fields/OneOf/OneOf.scss (59%) rename {src/editor-v2 => editor-v2}/components/DynamicForm/Fields/OneOf/OneOf.tsx (86%) rename {src/editor-v2 => editor-v2}/components/DynamicForm/Fields/Select/Select.tsx (75%) rename {src/editor-v2 => editor-v2}/components/DynamicForm/Fields/Text/Text.tsx (56%) rename {src/editor-v2 => editor-v2}/components/DynamicForm/Fields/TextArea/TextArea.tsx (57%) rename {src/editor-v2 => editor-v2}/components/DynamicForm/utils.ts (89%) rename {src/editor-v2 => editor-v2}/components/MiddleScreen/MiddleScreen.scss (87%) rename {src/editor-v2 => editor-v2}/components/MiddleScreen/MiddleScreen.tsx (73%) rename {src/editor-v2 => editor-v2}/components/Overlay/Overlay.scss (94%) rename {src/editor-v2 => editor-v2}/components/Overlay/Overlay.tsx (95%) rename {src/editor-v2 => editor-v2}/components/Panels/Panels.scss (61%) rename {src/editor-v2 => editor-v2}/components/Panels/Panels.tsx (94%) create mode 100644 editor-v2/components/Sidebar/Sidebar.scss create mode 100644 editor-v2/components/Sidebar/Sidebar.tsx rename {src/editor-v2 => editor-v2}/components/StoreViewer/StoreViewer.scss (88%) rename {src/editor-v2 => editor-v2}/components/StoreViewer/StoreViewer.tsx (78%) create mode 100644 editor-v2/components/Tabs/Tabs.scss create mode 100644 editor-v2/components/Tabs/Tabs.tsx rename {src/editor-v2 => editor-v2}/constants.ts (100%) rename {src/editor-v2/components => editor-v2/containers}/BlockConfig/BlockConfig.scss (73%) rename {src/editor-v2/components => editor-v2/containers}/BlockConfig/BlockConfig.tsx (82%) rename {src/editor-v2/components => editor-v2/containers}/BlocksList/BlocksList.scss (93%) rename {src/editor-v2/components => editor-v2/containers}/BlocksList/BlocksList.tsx (92%) rename {src/editor-v2 => editor-v2}/containers/Editor/Editor.scss (91%) rename {src/editor-v2 => editor-v2}/containers/Editor/Editor.tsx (64%) rename {src/editor-v2/components => editor-v2/containers}/GlobalConfig/GlobalConfig.scss (76%) rename {src/editor-v2/components => editor-v2/containers}/GlobalConfig/GlobalConfig.tsx (67%) rename {src/editor-v2/components => editor-v2/containers}/Source/Source.scss (63%) rename {src/editor-v2/components => editor-v2/containers}/Source/Source.tsx (76%) rename {src/editor-v2/components => editor-v2/containers}/SourceCode/SourceCode.scss (88%) rename {src/editor-v2/components => editor-v2/containers}/SourceCode/SourceCode.tsx (93%) rename {src/editor-v2/components => editor-v2/containers}/Tree/Tree.scss (92%) rename {src/editor-v2/components => editor-v2/containers}/Tree/Tree.tsx (96%) rename {src/editor-v2/components => editor-v2/containers}/ViewSwitches/ViewSwitches.scss (77%) rename {src/editor-v2/components => editor-v2/containers}/ViewSwitches/ViewSwitches.tsx (52%) rename {src/editor-v2 => editor-v2}/containers/__stories__/Editor.stories.tsx (99%) rename {src/editor-v2 => editor-v2}/containers/__stories__/utils.ts (100%) rename {src/editor-v2 => editor-v2}/context/editorStore/MainEditorStoreContext.tsx (99%) rename {src/editor-v2 => editor-v2}/context/editorStore/MainEditorStoreProvider.tsx (99%) rename {src/editor-v2 => editor-v2}/context/editorStore/index.ts (100%) rename {src/editor-v2 => editor-v2}/context/iframeContext/IframeContext.tsx (100%) rename {src/editor-v2 => editor-v2}/context/iframeContext/IframeProvider.tsx (85%) rename {src/editor-v2 => editor-v2}/context/iframeContext/index.ts (100%) create mode 100644 editor-v2/hooks/useEditorTabs.tsx rename {src/editor-v2 => editor-v2}/hooks/useMainEditorInitialize.ts (100%) rename {src/editor-v2 => editor-v2}/hooks/useMainEditorStore.ts (99%) rename {src/editor-v2 => editor-v2}/hooks/usePostMessageEvents.ts (100%) rename {src/editor-v2 => editor-v2}/index.ts (100%) rename {src/editor-v2 => editor-v2}/store.ts (99%) rename {src/editor-v2 => editor-v2}/styles/mixins.scss (83%) create mode 100644 editor-v2/styles/root.scss rename {src/editor-v2 => editor-v2}/styles/variables.scss (90%) create mode 100644 editor-v2/utils/cn.ts rename {src/editor-v2 => editor-v2}/utils/code.ts (83%) rename {src/editor-v2 => editor-v2}/utils/index.ts (98%) create mode 100644 editor-v2/utils/store.ts create mode 100644 playground/src/app/pc-2/navigation.json delete mode 100644 src/editor-v2/components/DynamicForm/Fields/Object/Object.scss delete mode 100644 src/editor-v2/components/Sidebar/Sidebar.scss delete mode 100644 src/editor-v2/components/Sidebar/Sidebar.tsx delete mode 100644 src/editor-v2/components/TopBar/TopBar.scss delete mode 100644 src/editor-v2/components/TopBar/TopBar.tsx delete mode 100644 src/editor-v2/icons/Tablet.tsx delete mode 100644 src/editor-v2/styles/root.scss delete mode 100644 src/editor-v2/utils/store.ts diff --git a/src/common/postMessage.ts b/common/postMessage.ts similarity index 94% rename from src/common/postMessage.ts rename to common/postMessage.ts index 65481b3297..d96c84b76f 100644 --- a/src/common/postMessage.ts +++ b/common/postMessage.ts @@ -39,6 +39,5 @@ export function usePostMessageAPIListener<K extends keyof EventMessageTypes>( ) { useEffect(() => { return listenPostMessageEvents(action, callback); - // eslint-disable-next-line react-hooks/exhaustive-deps }, [...deps]); } diff --git a/src/common/store.ts b/common/store.ts similarity index 95% rename from src/common/store.ts rename to common/store.ts index 41db0f6706..a011799227 100644 --- a/src/common/store.ts +++ b/common/store.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {PageContent} from '../models'; +import {PageContent} from '../src/models'; import {ConfigInput, ItemConfig} from './types'; import {initializeStore} from './utils'; diff --git a/src/common/types/actions.ts b/common/types/actions.ts similarity index 100% rename from src/common/types/actions.ts rename to common/types/actions.ts diff --git a/src/common/types/common.ts b/common/types/common.ts similarity index 100% rename from src/common/types/common.ts rename to common/types/common.ts diff --git a/src/common/types/forms.ts b/common/types/forms.ts similarity index 100% rename from src/common/types/forms.ts rename to common/types/forms.ts diff --git a/src/common/types/index.ts b/common/types/index.ts similarity index 100% rename from src/common/types/index.ts rename to common/types/index.ts diff --git a/src/common/types/messages.ts b/common/types/messages.ts similarity index 100% rename from src/common/types/messages.ts rename to common/types/messages.ts diff --git a/src/common/utils.ts b/common/utils.ts similarity index 100% rename from src/common/utils.ts rename to common/utils.ts diff --git a/src/editor-v2/components/BigOverlay/BigOverlay.scss b/editor-v2/components/BigOverlay/BigOverlay.scss similarity index 89% rename from src/editor-v2/components/BigOverlay/BigOverlay.scss rename to editor-v2/components/BigOverlay/BigOverlay.scss index b1f87ba97b..1ae968b872 100644 --- a/src/editor-v2/components/BigOverlay/BigOverlay.scss +++ b/editor-v2/components/BigOverlay/BigOverlay.scss @@ -1,5 +1,5 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; +@import '../../../styles/variables.scss'; +@import '../../../styles/mixins.scss'; @import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/components/BigOverlay/BigOverlay.tsx b/editor-v2/components/BigOverlay/BigOverlay.tsx similarity index 91% rename from src/editor-v2/components/BigOverlay/BigOverlay.tsx rename to editor-v2/components/BigOverlay/BigOverlay.tsx index 8a33f9bead..e88d786762 100644 --- a/src/editor-v2/components/BigOverlay/BigOverlay.tsx +++ b/editor-v2/components/BigOverlay/BigOverlay.tsx @@ -1,20 +1,16 @@ -import React, {useCallback, useContext, useEffect, useMemo, useState} from 'react'; - import {Stop} from '@gravity-ui/icons'; +import React, {useCallback, useContext, useEffect, useMemo, useState} from 'react'; import {usePostMessageAPIListener} from '../../../common/postMessage'; -import {ClassNameProps} from '../../../models'; -import {block} from '../../../utils'; import {IframeContext} from '../../context/iframeContext'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; +import {editorCn} from '../../utils/cn'; import './BigOverlay.scss'; -const b = block('big-overlay'); - -interface BigOverlayProps extends ClassNameProps {} +const b = editorCn('big-overlay'); -const BigOverlay: React.FC<BigOverlayProps> = ({className}) => { +const BigOverlay = ({className}: {className?: string}) => { const {zoom, manipulateOverlayMode} = useMainEditorStore(); const {iframeElement} = useContext(IframeContext); const [mousePosition, setMousePosition] = useState<{x: number; y: number} | undefined>( diff --git a/editor-v2/components/DynamicForm/DynamicForm.scss b/editor-v2/components/DynamicForm/DynamicForm.scss new file mode 100644 index 0000000000..d7bc2c688a --- /dev/null +++ b/editor-v2/components/DynamicForm/DynamicForm.scss @@ -0,0 +1,8 @@ +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}dynamic-form'; + +#{$block} { + //padding-left: 16px; +} diff --git a/src/editor-v2/components/DynamicForm/DynamicForm.tsx b/editor-v2/components/DynamicForm/DynamicForm.tsx similarity index 95% rename from src/editor-v2/components/DynamicForm/DynamicForm.tsx rename to editor-v2/components/DynamicForm/DynamicForm.tsx index 722aa84118..2051840ed6 100644 --- a/src/editor-v2/components/DynamicForm/DynamicForm.tsx +++ b/editor-v2/components/DynamicForm/DynamicForm.tsx @@ -1,11 +1,11 @@ -import React, {useCallback} from 'react'; - import _ from 'lodash'; +import React, {useCallback} from 'react'; import {ConfigInput, DynamicFormValue} from '../../../common/types'; -import {ClassNameProps, PageContent} from '../../../models'; -import {block} from '../../../utils'; +import {PageContent} from '../../../src/models'; +import {editorCn} from '../../utils/cn'; +import './DynamicForm.scss'; import ArrayDynamicField from './Fields/Array/Array'; import BooleanDynamicField from './Fields/Boolean/Boolean'; import NumberDynamicField from './Fields/Number/Number'; @@ -16,18 +16,16 @@ import TextDynamicField from './Fields/Text/Text'; import TextAreaDynamicField from './Fields/TextArea/TextArea'; import {getContent, getFullPath} from './utils'; -import './DynamicForm.scss'; - -const b = block('dynamic-form'); +const b = editorCn('dynamic-form'); -interface DynamicFormProps extends ClassNameProps { +interface DynamicFormProps { blockConfig: Array<ConfigInput>; contentConfig: PageContent; onUpdate: (key: string, value: DynamicFormValue) => void; + className?: string; } -const DynamicForm: React.FC<DynamicFormProps> = (props) => { - const {blockConfig, onUpdate, contentConfig} = props; +const DynamicForm = ({blockConfig, onUpdate, contentConfig}: DynamicFormProps) => { const inputs = blockConfig; const getData = useCallback( diff --git a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss b/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss similarity index 92% rename from src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss rename to editor-v2/components/DynamicForm/FieldBase/FieldBase.scss index c739699967..b029dd1fa7 100644 --- a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss +++ b/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss @@ -1,5 +1,3 @@ -@import '../../../../../styles/variables.scss'; -@import '../../../../../styles/mixins.scss'; @import '../../../styles/root.scss'; @import '../../../styles/variables.scss'; @import '../../../styles/mixins.scss'; diff --git a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx b/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx similarity index 85% rename from src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx rename to editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx index ce1bf25879..2def6c0d06 100644 --- a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx +++ b/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx @@ -1,15 +1,13 @@ -import React, {PropsWithChildren, useState} from 'react'; - import {ArrowRotateLeft} from '@gravity-ui/icons'; import {ArrowToggle, Button, Icon} from '@gravity-ui/uikit'; import _ from 'lodash'; +import React, {PropsWithChildren, useState} from 'react'; -import {ClassNameProps} from '../../../../models'; -import {block} from '../../../../utils'; +import {editorCn} from '../../../utils/cn'; import './FieldBase.scss'; -const b = block('field-base'); +const b = editorCn('field-base'); export interface FieldBaseParams { title?: string; @@ -18,10 +16,18 @@ export interface FieldBaseParams { expandable?: boolean; } -export interface FieldBaseProps extends ClassNameProps, PropsWithChildren, FieldBaseParams {} +export interface FieldBaseProps extends PropsWithChildren, FieldBaseParams { + className?: string; +} -const FieldBase: React.FC<FieldBaseProps> = (props) => { - const {className, title, textSize, children, onRefresh, expandable = false} = props; +const FieldBase: React.FC<FieldBaseProps> = ({ + className, + title, + textSize, + children, + onRefresh, + expandable = false, +}) => { const [showChildren, setShowChildren] = useState(!expandable); const titleComponent = React.useMemo(() => { diff --git a/src/editor-v2/components/DynamicForm/Fields/Array/Array.scss b/editor-v2/components/DynamicForm/Fields/Array/Array.scss similarity index 88% rename from src/editor-v2/components/DynamicForm/Fields/Array/Array.scss rename to editor-v2/components/DynamicForm/Fields/Array/Array.scss index 973665582e..8a060c7ccc 100644 --- a/src/editor-v2/components/DynamicForm/Fields/Array/Array.scss +++ b/editor-v2/components/DynamicForm/Fields/Array/Array.scss @@ -1,5 +1,5 @@ -@import '../../../../../../styles/variables.scss'; -@import '../../../../../../styles/mixins.scss'; +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; $block: '.#{$ns}array-dynamic-field'; diff --git a/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx b/editor-v2/components/DynamicForm/Fields/Array/Array.tsx similarity index 94% rename from src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx rename to editor-v2/components/DynamicForm/Fields/Array/Array.tsx index 185de27d5f..3342ab3692 100644 --- a/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx +++ b/editor-v2/components/DynamicForm/Fields/Array/Array.tsx @@ -1,34 +1,31 @@ -import React, {useCallback} from 'react'; - import {Plus} from '@gravity-ui/icons'; import {Button, Card, Icon} from '@gravity-ui/uikit'; +import React, {useCallback} from 'react'; import {ArrayObjectInput, ArrayTextInput, DynamicFormValue} from '../../../../../common/types'; -import {ClassNameProps, PageContent} from '../../../../../models'; -import {block} from '../../../../../utils'; +import {PageContent} from '../../../../../src/models'; import {removeFromArray, swapArrayItems} from '../../../../utils'; +import {editorCn} from '../../../../utils/cn'; import DynamicForm from '../../DynamicForm'; import FieldBase from '../../FieldBase/FieldBase'; import Text from '../Text/Text'; -import ItemButton from './ItemButton/ItemButton'; - import './Array.scss'; +import ItemButton from './ItemButton/ItemButton'; -const b = block('array-dynamic-field'); +const b = editorCn('array-dynamic-field'); type ArrayInput = ArrayTextInput | ArrayObjectInput; -interface ArrayFieldProps extends ClassNameProps { +interface ArrayFieldProps { title: string; values: Array<DynamicFormValue>; onUpdate: (key: string, value: DynamicFormValue) => void; blockConfig: ArrayInput; + className?: string; } -const ArrayDynamicField: React.FC<ArrayFieldProps> = (props) => { - const {title, values, onUpdate, className, blockConfig} = props; - +const ArrayDynamicField = ({title, values, onUpdate, className, blockConfig}: ArrayFieldProps) => { const haveItems = values && Array.isArray(values) && values.length; const onAddItem = useCallback(() => { diff --git a/src/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx b/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx similarity index 82% rename from src/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx rename to editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx index d566f49e55..05128a5b03 100644 --- a/src/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx +++ b/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx @@ -1,30 +1,28 @@ -import React, {Fragment, useCallback, useRef, useState} from 'react'; - import {ArrowDown, ArrowUp, EllipsisVertical, TrashBin} from '@gravity-ui/icons'; import {Button, Icon, Menu, Popup} from '@gravity-ui/uikit'; +import React, {Fragment, useCallback, useRef, useState} from 'react'; -import {ClassNameProps} from '../../../../../../models'; -import {block} from '../../../../../../utils'; +import {editorCn} from '../../../../../utils/cn'; -const b = block('array-item-button'); +const b = editorCn('array-item-button'); -interface ItemButtonProps extends ClassNameProps { +interface ItemButtonProps { onRemove: () => void; onReorderUp: () => void; disableReorderUp?: boolean; onReorderDown: () => void; disableReorderDown?: boolean; + className?: string; } -const ItemButton: React.FC<ItemButtonProps> = (props) => { - const { - className, - onRemove, - onReorderUp, - onReorderDown, - disableReorderUp = false, - disableReorderDown = false, - } = props; +const ItemButton = ({ + className, + onRemove, + onReorderUp, + onReorderDown, + disableReorderUp = false, + disableReorderDown = false, +}: ItemButtonProps) => { const buttonRef = useRef(null); const [isOpen, setIsOpen] = useState(false); diff --git a/src/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx b/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx similarity index 57% rename from src/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx rename to editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx index 2ac37c4ef6..1ce0f5af22 100644 --- a/src/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx +++ b/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx @@ -1,21 +1,18 @@ -import React from 'react'; - import {Switch} from '@gravity-ui/uikit'; +import React from 'react'; -import {ClassNameProps} from '../../../../../models'; -import {block} from '../../../../../utils'; +import {editorCn} from '../../../../utils/cn'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; -const b = block('boolean-dynamic-field'); +const b = editorCn('boolean-dynamic-field'); -interface BooleanProps extends ClassNameProps, FieldBaseParams { +interface BooleanProps extends FieldBaseParams { value: string; onUpdate: (value: boolean | undefined) => void; + className?: string; } -const BooleanDynamicField: React.FC<BooleanProps> = (props) => { - const {title, value, onUpdate, className} = props; - +const BooleanDynamicField = ({title, value, onUpdate, className}: BooleanProps) => { return ( <FieldBase title={title} className={b(null, className)} onRefresh={onUpdate}> <Switch checked={Boolean(value)} onUpdate={onUpdate} /> diff --git a/src/editor-v2/components/DynamicForm/Fields/Number/Number.tsx b/editor-v2/components/DynamicForm/Fields/Number/Number.tsx similarity index 61% rename from src/editor-v2/components/DynamicForm/Fields/Number/Number.tsx rename to editor-v2/components/DynamicForm/Fields/Number/Number.tsx index 2b01e67989..659ea5c088 100644 --- a/src/editor-v2/components/DynamicForm/Fields/Number/Number.tsx +++ b/editor-v2/components/DynamicForm/Fields/Number/Number.tsx @@ -1,21 +1,18 @@ -import React from 'react'; - import {TextInput} from '@gravity-ui/uikit'; +import React from 'react'; -import {ClassNameProps} from '../../../../../models'; -import {block} from '../../../../../utils'; +import {editorCn} from '../../../../utils/cn'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; -const b = block('number-dynamic-field'); +const b = editorCn('number-dynamic-field'); -interface NumberDynamicFieldProps extends ClassNameProps, FieldBaseParams { +interface NumberDynamicFieldProps extends FieldBaseParams { value: string; onUpdate: (value: number | undefined) => void; + className?: string; } -const NumberDynamicField: React.FC<NumberDynamicFieldProps> = (props) => { - const {title, value, onUpdate, className} = props; - +const NumberDynamicField = ({title, value, onUpdate, className}: NumberDynamicFieldProps) => { const onUpdateFunc = (updateValue: string) => { onUpdate(Number(updateValue)); }; diff --git a/src/editor-v2/components/DynamicForm/DynamicForm.scss b/editor-v2/components/DynamicForm/Fields/Object/Object.scss similarity index 55% rename from src/editor-v2/components/DynamicForm/DynamicForm.scss rename to editor-v2/components/DynamicForm/Fields/Object/Object.scss index 4628d70ab2..7092b9aff9 100644 --- a/src/editor-v2/components/DynamicForm/DynamicForm.scss +++ b/editor-v2/components/DynamicForm/Fields/Object/Object.scss @@ -1,8 +1,10 @@ @import '../../../../styles/variables.scss'; @import '../../../../styles/mixins.scss'; -$block: '.#{$ns}dynamic-form'; +$block: '.#{$ns}object-dynamic-field'; #{$block} { - //padding-left: 16px; + &__card { + padding: 12px; + } } diff --git a/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx b/editor-v2/components/DynamicForm/Fields/Object/Object.tsx similarity index 69% rename from src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx rename to editor-v2/components/DynamicForm/Fields/Object/Object.tsx index 1d012eb2d9..b8b67f7806 100644 --- a/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx +++ b/editor-v2/components/DynamicForm/Fields/Object/Object.tsx @@ -1,26 +1,30 @@ -import React from 'react'; - import {Card} from '@gravity-ui/uikit'; +import React from 'react'; import {ConfigInput, DynamicFormValue} from '../../../../../common/types'; -import {ClassNameProps, PageContent} from '../../../../../models'; -import {block} from '../../../../../utils'; +import {PageContent} from '../../../../../src/models'; +import {editorCn} from '../../../../utils/cn'; import DynamicForm from '../../DynamicForm'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; import './Object.scss'; -const b = block('object-dynamic-field'); +const b = editorCn('object-dynamic-field'); -interface ObjectDynamicFieldProps extends ClassNameProps, FieldBaseParams { +interface ObjectDynamicFieldProps extends FieldBaseParams { value: PageContent; onUpdate: (key: string, value: DynamicFormValue) => void; blockConfig: Array<ConfigInput>; + className?: string; } -const ObjectDynamicField: React.FC<ObjectDynamicFieldProps> = (props) => { - const {title, value, onUpdate, className, blockConfig} = props; - +const ObjectDynamicField = ({ + title, + value, + onUpdate, + className, + blockConfig, +}: ObjectDynamicFieldProps) => { return ( <FieldBase title={title} diff --git a/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss b/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss similarity index 59% rename from src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss rename to editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss index 1e5ec1c1a9..b7660714b8 100644 --- a/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss +++ b/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss @@ -1,5 +1,5 @@ -@import '../../../../../../styles/variables.scss'; -@import '../../../../../../styles/mixins.scss'; +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; $block: '.#{$ns}oneof-dynamic-field'; diff --git a/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx b/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx similarity index 86% rename from src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx rename to editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx index 5db8644b96..eda4874f68 100644 --- a/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx +++ b/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx @@ -1,21 +1,21 @@ -import React, {useCallback, useMemo, useState} from 'react'; - import {Card, RadioButton} from '@gravity-ui/uikit'; +import React, {useCallback, useMemo, useState} from 'react'; import {DynamicFormValue, OneOfInput} from '../../../../../common/types'; -import {ClassNameProps, PageContent} from '../../../../../models'; -import {block} from '../../../../../utils'; +import {PageContent} from '../../../../../src/models'; +import {editorCn} from '../../../../utils/cn'; import DynamicForm from '../../DynamicForm'; import FieldBase from '../../FieldBase/FieldBase'; import './OneOf.scss'; -const b = block('oneof-dynamic-field'); +const b = editorCn('oneof-dynamic-field'); -interface OneOfDynamicFieldProps extends ClassNameProps { +interface OneOfDynamicFieldProps { contentConfig: PageContent; onUpdate: (key: string, value: DynamicFormValue) => void; inputConfig: OneOfInput; + className?: string; } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -26,9 +26,12 @@ const getOneOfContentConfig = (contentConfig: any, name: string) => { return contentConfig; }; -const OneOfDynamicField: React.FC<OneOfDynamicFieldProps> = (props) => { - const {contentConfig, onUpdate, className, inputConfig} = props; - +const OneOfDynamicField = ({ + contentConfig, + onUpdate, + className, + inputConfig, +}: OneOfDynamicFieldProps) => { const defaultValue = inputConfig.options[0].value; const [oneOfMetaValue, setOneOfMetaValue] = useState(defaultValue); diff --git a/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx b/editor-v2/components/DynamicForm/Fields/Select/Select.tsx similarity index 75% rename from src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx rename to editor-v2/components/DynamicForm/Fields/Select/Select.tsx index 06eff057a5..ae91fca9de 100644 --- a/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx +++ b/editor-v2/components/DynamicForm/Fields/Select/Select.tsx @@ -1,25 +1,22 @@ -import React from 'react'; - import {RadioButton, Select} from '@gravity-ui/uikit'; +import React from 'react'; import {SelectMultipleInput, SelectSingleInput} from '../../../../../common/types'; -import {ClassNameProps} from '../../../../../models'; -import {block} from '../../../../../utils'; +import {editorCn} from '../../../../utils/cn'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; -const b = block('select-field'); +const b = editorCn('select-field'); type SelectInput = SelectSingleInput | SelectMultipleInput; -interface SelectDynamicFieldProps extends ClassNameProps, FieldBaseParams { +interface SelectDynamicFieldProps extends FieldBaseParams { input: SelectInput; value: string; onUpdate: (value: string | undefined) => void; + className?: string; } -const SelectDynamicField: React.FC<SelectDynamicFieldProps> = (props) => { - const {input, value, onUpdate, className} = props; - +const SelectDynamicField = ({input, value, onUpdate, className}: SelectDynamicFieldProps) => { const inputView = input.view || 'radiobutton'; return ( diff --git a/src/editor-v2/components/DynamicForm/Fields/Text/Text.tsx b/editor-v2/components/DynamicForm/Fields/Text/Text.tsx similarity index 56% rename from src/editor-v2/components/DynamicForm/Fields/Text/Text.tsx rename to editor-v2/components/DynamicForm/Fields/Text/Text.tsx index b16101e86b..96ad2ab90e 100644 --- a/src/editor-v2/components/DynamicForm/Fields/Text/Text.tsx +++ b/editor-v2/components/DynamicForm/Fields/Text/Text.tsx @@ -1,21 +1,18 @@ -import React from 'react'; - import {TextInput} from '@gravity-ui/uikit'; +import React from 'react'; -import {ClassNameProps} from '../../../../../models'; -import {block} from '../../../../../utils'; +import {editorCn} from '../../../../utils/cn'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; -const b = block('text-dynamic-field'); +const b = editorCn('text-dynamic-field'); -interface TextDynamicFieldProps extends ClassNameProps, FieldBaseParams { +interface TextDynamicFieldProps extends FieldBaseParams { value: string; onUpdate: (value: string | undefined) => void; + className?: string; } -const TextDynamicField: React.FC<TextDynamicFieldProps> = (props) => { - const {title, value, onUpdate, className} = props; - +const TextDynamicField = ({title, value, onUpdate, className}: TextDynamicFieldProps) => { return ( <FieldBase title={title} className={b(null, className)} onRefresh={onUpdate}> <TextInput value={value || ''} onUpdate={onUpdate} /> diff --git a/src/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx b/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx similarity index 57% rename from src/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx rename to editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx index 4ca783696b..49d399c668 100644 --- a/src/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx +++ b/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx @@ -1,21 +1,18 @@ -import React from 'react'; - import {TextArea} from '@gravity-ui/uikit'; +import React from 'react'; -import {ClassNameProps} from '../../../../../models'; -import {block} from '../../../../../utils'; +import {editorCn} from '../../../../utils/cn'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; -const b = block('textarea-dynamic-field'); +const b = editorCn('textarea-dynamic-field'); -interface TextAreaDynamicFieldProps extends ClassNameProps, FieldBaseParams { +interface TextAreaDynamicFieldProps extends FieldBaseParams { value: string; onUpdate: (value: string | undefined) => void; + className?: string; } -const TextAreaDynamicField: React.FC<TextAreaDynamicFieldProps> = (props) => { - const {title, value, onUpdate, className} = props; - +const TextAreaDynamicField = ({title, value, onUpdate, className}: TextAreaDynamicFieldProps) => { return ( <FieldBase title={title} className={b(null, className)} onRefresh={onUpdate}> <TextArea minRows={5} maxRows={20} value={value || ''} onUpdate={onUpdate} /> diff --git a/src/editor-v2/components/DynamicForm/utils.ts b/editor-v2/components/DynamicForm/utils.ts similarity index 89% rename from src/editor-v2/components/DynamicForm/utils.ts rename to editor-v2/components/DynamicForm/utils.ts index b52c3381a9..8a83dc1cb3 100644 --- a/src/editor-v2/components/DynamicForm/utils.ts +++ b/editor-v2/components/DynamicForm/utils.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {PageContent} from '../../../models'; +import {PageContent} from '../../../src/models'; export const getFullPath = (path: string, name: string) => { if (!path && !name) { diff --git a/src/editor-v2/components/MiddleScreen/MiddleScreen.scss b/editor-v2/components/MiddleScreen/MiddleScreen.scss similarity index 87% rename from src/editor-v2/components/MiddleScreen/MiddleScreen.scss rename to editor-v2/components/MiddleScreen/MiddleScreen.scss index 16c869fa1c..0b869dbdd8 100644 --- a/src/editor-v2/components/MiddleScreen/MiddleScreen.scss +++ b/editor-v2/components/MiddleScreen/MiddleScreen.scss @@ -1,5 +1,3 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; @import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; @@ -13,11 +11,14 @@ $block: '.#{$ns}middle-screen'; flex-direction: column; &__topbar { - height: 40px; + flex: 0 0 auto; + // min-height: 40px; } &__wrapper { - height: 100%; + flex: 1 1 auto; + overflow-y: auto; + // height: 100%; } &__canvas { diff --git a/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx b/editor-v2/components/MiddleScreen/MiddleScreen.tsx similarity index 73% rename from src/editor-v2/components/MiddleScreen/MiddleScreen.tsx rename to editor-v2/components/MiddleScreen/MiddleScreen.tsx index efa1499b66..5f7e0b8d1e 100644 --- a/src/editor-v2/components/MiddleScreen/MiddleScreen.tsx +++ b/editor-v2/components/MiddleScreen/MiddleScreen.tsx @@ -1,23 +1,23 @@ -import React, {useCallback, useContext, useState} from 'react'; - import {Loader} from '@gravity-ui/uikit'; +import React, {ElementType, useCallback, useContext, useState} from 'react'; import {usePostMessageAPIListener} from '../../../common/postMessage'; -import {ClassNameProps} from '../../../models'; -import {block} from '../../../utils'; import {IframeContext} from '../../context/iframeContext'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; +import {editorCn} from '../../utils/cn'; import Overlay from '../Overlay/Overlay'; -import TopBar from '../TopBar/TopBar'; import './MiddleScreen.scss'; -const b = block('middle-screen'); +const b = editorCn('middle-screen'); -interface MiddleScreenProps extends ClassNameProps {} +interface MiddleScreenProps { + className?: string; + CustomTop?: ElementType; +} -const MiddleScreen: React.FC<MiddleScreenProps> = ({className}) => { - const {zoom, initialized, setZoom, decreaseZoom, increaseZoom} = useMainEditorStore(); +const MiddleScreen = ({className, CustomTop}: MiddleScreenProps) => { + const {zoom, initialized} = useMainEditorStore(); const {url, setIframeElement} = useContext(IframeContext); const [height, setHeight] = useState(0); @@ -38,14 +38,11 @@ const MiddleScreen: React.FC<MiddleScreenProps> = ({className}) => { return ( <div className={b(null, className)}> - <div className={b('topbar')}> - <TopBar - onZoomUpdate={setZoom} - zoom={zoom} - onDecreaseZoom={decreaseZoom} - onIncreaseZoom={increaseZoom} - /> - </div> + {CustomTop ? ( + <div className={b('topbar')}> + <CustomTop /> + </div> + ) : null} <div className={b('wrapper')}> <div className={b('canvas', {hidden: !initialized})} diff --git a/src/editor-v2/components/Overlay/Overlay.scss b/editor-v2/components/Overlay/Overlay.scss similarity index 94% rename from src/editor-v2/components/Overlay/Overlay.scss rename to editor-v2/components/Overlay/Overlay.scss index 7d946ec711..bde825744f 100644 --- a/src/editor-v2/components/Overlay/Overlay.scss +++ b/editor-v2/components/Overlay/Overlay.scss @@ -1,5 +1,3 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; @import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/components/Overlay/Overlay.tsx b/editor-v2/components/Overlay/Overlay.tsx similarity index 95% rename from src/editor-v2/components/Overlay/Overlay.tsx rename to editor-v2/components/Overlay/Overlay.tsx index b900ed4b8c..b9d74b7dc9 100644 --- a/src/editor-v2/components/Overlay/Overlay.tsx +++ b/editor-v2/components/Overlay/Overlay.tsx @@ -1,19 +1,18 @@ -import React, {useCallback, useState} from 'react'; - import {Copy, Grip, TrashBin} from '@gravity-ui/icons'; import {Button, Icon} from '@gravity-ui/uikit'; +import React, {useCallback, useState} from 'react'; import {usePostMessageAPIListener} from '../../../common/postMessage'; -import {ClassNameProps} from '../../../models'; -import {block} from '../../../utils'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; +import {editorCn} from '../../utils/cn'; import './Overlay.scss'; -const b = block('overlay'); - -interface OverlayProps extends ClassNameProps {} +const b = editorCn('overlay'); +interface OverlayProps { + className?: string; +} interface InsertLineProps { top: number; left: number; @@ -22,7 +21,7 @@ interface InsertLineProps { position: string; } -const Overlay: React.FC<OverlayProps> = ({className}) => { +const Overlay = ({className}: OverlayProps) => { const { height, selectedBlock, diff --git a/src/editor-v2/components/Panels/Panels.scss b/editor-v2/components/Panels/Panels.scss similarity index 61% rename from src/editor-v2/components/Panels/Panels.scss rename to editor-v2/components/Panels/Panels.scss index e22ef97642..e7865c13f7 100644 --- a/src/editor-v2/components/Panels/Panels.scss +++ b/editor-v2/components/Panels/Panels.scss @@ -1,5 +1,3 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; @import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; @@ -8,18 +6,18 @@ $block: '.#{$ns}panels'; #{$block} { &__button-wrap { - --pc-editor-panels-button-gap: 8px; + --pceditor-panels-button-gap: 8px; position: absolute; z-index: 1000; &_left { - bottom: var(--pc-editor-panels-button-gap); - left: calc(100% + var(--pc-editor-panels-button-gap)); + bottom: var(--pceditor-panels-button-gap); + left: calc(100% + var(--pceditor-panels-button-gap)); } &_right { - bottom: var(--pc-editor-panels-button-gap); - right: calc(100% + var(--pc-editor-panels-button-gap)); + bottom: var(--pceditor-panels-button-gap); + right: calc(100% + var(--pceditor-panels-button-gap)); } } diff --git a/src/editor-v2/components/Panels/Panels.tsx b/editor-v2/components/Panels/Panels.tsx similarity index 94% rename from src/editor-v2/components/Panels/Panels.tsx rename to editor-v2/components/Panels/Panels.tsx index 8505f72097..b0f2f6c87e 100644 --- a/src/editor-v2/components/Panels/Panels.tsx +++ b/editor-v2/components/Panels/Panels.tsx @@ -1,14 +1,13 @@ -import React, {ReactElement, useRef} from 'react'; - import {ArrowLeftFromLine, ArrowRightFromLine, Grip} from '@gravity-ui/icons'; import {Button, Icon} from '@gravity-ui/uikit'; +import React, {ReactElement, useRef} from 'react'; import {ImperativePanelHandle, Panel, PanelGroup, PanelResizeHandle} from 'react-resizable-panels'; -import {block} from '../../../utils'; +import {editorCn} from '../../utils/cn'; import './Panels.scss'; -const b = block('panels'); +const b = editorCn('panels'); interface PanelsProps { left: ReactElement; @@ -16,8 +15,7 @@ interface PanelsProps { right: ReactElement; } -export const Panels = (props: PanelsProps) => { - const {left, right, middle} = props; +export const Panels = ({left, right, middle}: PanelsProps) => { const leftPanel = useRef<ImperativePanelHandle>(null); const rightPanel = useRef<ImperativePanelHandle>(null); diff --git a/editor-v2/components/Sidebar/Sidebar.scss b/editor-v2/components/Sidebar/Sidebar.scss new file mode 100644 index 0000000000..fca4b373d6 --- /dev/null +++ b/editor-v2/components/Sidebar/Sidebar.scss @@ -0,0 +1,18 @@ +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}sidebar'; + +#{$block} { + position: relative; + height: 100%; + width: 100%; + display: flex; + flex-direction: column; + + &__block { + width: 100%; + border-bottom: 1px solid var(--g-color-line-generic); + } +} diff --git a/editor-v2/components/Sidebar/Sidebar.tsx b/editor-v2/components/Sidebar/Sidebar.tsx new file mode 100644 index 0000000000..1ef6cfe7ce --- /dev/null +++ b/editor-v2/components/Sidebar/Sidebar.tsx @@ -0,0 +1,28 @@ +import React, {ElementType} from 'react'; + +import {editorCn} from '../../utils/cn'; +import Tabs, {TabsItemProps} from '../Tabs/Tabs'; + +import './Sidebar.scss'; + +const b = editorCn('sidebar'); + +interface SidebarProps { + tabs: TabsItemProps[]; + defaultTab?: string; + top?: ElementType[]; + className?: string; +} + +export const Sidebar = ({className, tabs, top = []}: SidebarProps) => { + return ( + <div className={b(null, className)}> + {top.map((TopComponent, idx) => ( + <div key={idx} className={b('block')}> + <TopComponent /> + </div> + ))} + <Tabs items={tabs} /> + </div> + ); +}; diff --git a/src/editor-v2/components/StoreViewer/StoreViewer.scss b/editor-v2/components/StoreViewer/StoreViewer.scss similarity index 88% rename from src/editor-v2/components/StoreViewer/StoreViewer.scss rename to editor-v2/components/StoreViewer/StoreViewer.scss index 93a126da99..6fd241894a 100644 --- a/src/editor-v2/components/StoreViewer/StoreViewer.scss +++ b/editor-v2/components/StoreViewer/StoreViewer.scss @@ -1,5 +1,5 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; $block: '.#{$ns}store-viewer'; diff --git a/src/editor-v2/components/StoreViewer/StoreViewer.tsx b/editor-v2/components/StoreViewer/StoreViewer.tsx similarity index 78% rename from src/editor-v2/components/StoreViewer/StoreViewer.tsx rename to editor-v2/components/StoreViewer/StoreViewer.tsx index b0bd87d9de..59afb58eee 100644 --- a/src/editor-v2/components/StoreViewer/StoreViewer.tsx +++ b/editor-v2/components/StoreViewer/StoreViewer.tsx @@ -1,22 +1,21 @@ -import React, {useState} from 'react'; - import {Code} from '@gravity-ui/icons'; import {Button, Icon} from '@gravity-ui/uikit'; +import React, {useState} from 'react'; import ReactJson from 'react-json-view'; import {removeFn} from '../../../common/utils'; -import {ClassNameProps} from '../../../models'; -import {block} from '../../../utils'; +import {editorCn} from '../../utils/cn'; import './StoreViewer.scss'; -const b = block('store-viewer'); +const b = editorCn('store-viewer'); -interface StoreViewerProps extends ClassNameProps { +interface StoreViewerProps { + className?: string; store: object; } -const StoreViewer: React.FC<StoreViewerProps> = ({store, className}) => { +const StoreViewer = ({store, className}: StoreViewerProps) => { const [open, setOpen] = useState(false); return ( <div className={b({open}, className)}> diff --git a/editor-v2/components/Tabs/Tabs.scss b/editor-v2/components/Tabs/Tabs.scss new file mode 100644 index 0000000000..ecd849e296 --- /dev/null +++ b/editor-v2/components/Tabs/Tabs.scss @@ -0,0 +1,35 @@ +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}tabs'; + +#{$block} { + overflow: auto hidden; + width: 100%; + + &__item { + color: var(--g-color-private-black-300); + font-weight: 500; + letter-spacing: 0.5px; + cursor: pointer; + + &_active { + pointer-events: none; + color: var(--g-color-text-primary); + } + } + + &__tabs-wrapper { + width: 100%; + border-bottom: 1px solid var(--g-color-line-generic); + display: inline-flex; + flex-direction: row; + padding: 16px 4px; + } + + &__body { + flex: 1; + border-right: 1px solid var(--g-color-line-generic); + overflow-y: auto; + } +} diff --git a/editor-v2/components/Tabs/Tabs.tsx b/editor-v2/components/Tabs/Tabs.tsx new file mode 100644 index 0000000000..7c7ee710ee --- /dev/null +++ b/editor-v2/components/Tabs/Tabs.tsx @@ -0,0 +1,72 @@ +import {Button} from '@gravity-ui/uikit'; +import React, {ElementType, useCallback, useMemo, useState} from 'react'; + +import {editorCn} from '../../utils/cn'; + +import './Tabs.scss'; + +const b = editorCn('tabs'); + +export interface TabsItemProps { + id: string; + title: string; + component: ElementType; +} + +export interface TabsProps { + className?: string; + items: TabsItemProps[]; + defaultTab?: string | null; +} + +const Tabs = ({className, items, defaultTab}: TabsProps) => { + const [currentTab, setCurrentTab] = useState(defaultTab); + + const activeTabId: string | null = useMemo(() => { + if (currentTab) { + return currentTab; + } + + return items[0].id; + }, [currentTab, items]); + + const handleClick = useCallback( + (tabId: string) => () => { + setCurrentTab(tabId); + }, + [], + ); + + const TabComponent = useMemo(() => { + const findTab = items.find(({id}) => id === activeTabId); + return findTab?.component; + }, [activeTabId, items]); + + return ( + <div className={b(null, className)}> + <div className={b('tabs-wrapper')} role="tablist"> + {items.map(({id, title}) => { + const isActive = id === activeTabId; + + return ( + <Button + view="flat" + size="s" + className={b('item', {active: isActive})} + key={title} + extraProps={{ + role: 'tab', + }} + onClick={handleClick(id)} + > + {title} + </Button> + ); + })} + </div> + <div className={b('body')}>{TabComponent && <TabComponent />}</div> + </div> + ); +}; + +export default Tabs; diff --git a/src/editor-v2/constants.ts b/editor-v2/constants.ts similarity index 100% rename from src/editor-v2/constants.ts rename to editor-v2/constants.ts diff --git a/src/editor-v2/components/BlockConfig/BlockConfig.scss b/editor-v2/containers/BlockConfig/BlockConfig.scss similarity index 73% rename from src/editor-v2/components/BlockConfig/BlockConfig.scss rename to editor-v2/containers/BlockConfig/BlockConfig.scss index 6af7fb8a77..0411ca8ad0 100644 --- a/src/editor-v2/components/BlockConfig/BlockConfig.scss +++ b/editor-v2/containers/BlockConfig/BlockConfig.scss @@ -1,5 +1,3 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; @import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; @@ -12,7 +10,6 @@ $block: '.#{$ns}block-config'; &__title { margin-bottom: 16px; margin-top: 8px; - @include text-subheader-3; } &__empty { @@ -22,7 +19,5 @@ $block: '.#{$ns}block-config'; text-align: center; height: 100%; width: 100%; - - @include text-body-2; } } diff --git a/src/editor-v2/components/BlockConfig/BlockConfig.tsx b/editor-v2/containers/BlockConfig/BlockConfig.tsx similarity index 82% rename from src/editor-v2/components/BlockConfig/BlockConfig.tsx rename to editor-v2/containers/BlockConfig/BlockConfig.tsx index 852739b701..1ccc59952c 100644 --- a/src/editor-v2/components/BlockConfig/BlockConfig.tsx +++ b/editor-v2/containers/BlockConfig/BlockConfig.tsx @@ -1,21 +1,21 @@ -import React from 'react'; - import _ from 'lodash'; +import React from 'react'; import {DynamicFormValue} from '../../../common/types'; -import {ClassNameProps} from '../../../models'; -import {block} from '../../../utils'; +import DynamicForm from '../../components/DynamicForm/DynamicForm'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {generateChildrenPathFromArray} from '../../utils'; -import DynamicForm from '../DynamicForm/DynamicForm'; +import {editorCn} from '../../utils/cn'; import './BlockConfig.scss'; -const b = block('block-config'); +const b = editorCn('block-config'); -interface BlockConfigProps extends ClassNameProps {} +interface BlockConfigProps { + className?: string; +} -const BlockConfig: React.FC<BlockConfigProps> = ({className}) => { +const BlockConfig = ({className}: BlockConfigProps) => { const {selectedBlock, content, blocks, subBlocks, updateField} = useMainEditorStore(); const currentBlockPath = selectedBlock ? generateChildrenPathFromArray(selectedBlock) : '[]'; diff --git a/src/editor-v2/components/BlocksList/BlocksList.scss b/editor-v2/containers/BlocksList/BlocksList.scss similarity index 93% rename from src/editor-v2/components/BlocksList/BlocksList.scss rename to editor-v2/containers/BlocksList/BlocksList.scss index e335c07ca0..c2a8cfe100 100644 --- a/src/editor-v2/components/BlocksList/BlocksList.scss +++ b/editor-v2/containers/BlocksList/BlocksList.scss @@ -1,5 +1,3 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; @import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/components/BlocksList/BlocksList.tsx b/editor-v2/containers/BlocksList/BlocksList.tsx similarity index 92% rename from src/editor-v2/components/BlocksList/BlocksList.tsx rename to editor-v2/containers/BlocksList/BlocksList.tsx index e608b8c1f1..b2aa9b2618 100644 --- a/src/editor-v2/components/BlocksList/BlocksList.tsx +++ b/editor-v2/containers/BlocksList/BlocksList.tsx @@ -1,16 +1,15 @@ -import React, {PropsWithChildren, useCallback} from 'react'; - import {Grip, Plus} from '@gravity-ui/icons'; import {Button, Card, Icon} from '@gravity-ui/uikit'; import _ from 'lodash'; +import React, {PropsWithChildren, useCallback} from 'react'; import {ItemConfig} from '../../../common/types'; -import {block} from '../../../utils'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; +import {editorCn} from '../../utils/cn'; import './BlocksList.scss'; -const b = block('blocks-list'); +const b = editorCn('blocks-list'); export interface BlocksListProps { blocks: ItemConfig[]; @@ -60,10 +59,8 @@ const BlocksList = (_p: PropsWithChildren<BlocksListProps>) => { return ( <div className={b()}> <div className={b('section')}> - <div className={b('title')}>Blocks</div> {Object.entries(groups).map(([key, groupBlocks]) => ( <div className={b('group')} key={key}> - <div className={b('subtitle')}>{_.capitalize(key)}</div> <div> {groupBlocks.map(({type, schema: {name}}) => ( <Card diff --git a/src/editor-v2/containers/Editor/Editor.scss b/editor-v2/containers/Editor/Editor.scss similarity index 91% rename from src/editor-v2/containers/Editor/Editor.scss rename to editor-v2/containers/Editor/Editor.scss index 58ed10c8e2..cdb4c1f6d1 100644 --- a/src/editor-v2/containers/Editor/Editor.scss +++ b/editor-v2/containers/Editor/Editor.scss @@ -1,5 +1,3 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; @import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/containers/Editor/Editor.tsx b/editor-v2/containers/Editor/Editor.tsx similarity index 64% rename from src/editor-v2/containers/Editor/Editor.tsx rename to editor-v2/containers/Editor/Editor.tsx index 0ec1cf3210..83eeee2217 100644 --- a/src/editor-v2/containers/Editor/Editor.tsx +++ b/editor-v2/containers/Editor/Editor.tsx @@ -1,7 +1,6 @@ -import React, {useCallback} from 'react'; +import React, {ElementType, useCallback} from 'react'; -import {PageContent} from '../../../models'; -import {block} from '../../../utils'; +import {PageContent} from '../../../src/models'; import BigOverlay from '../../components/BigOverlay/BigOverlay'; import MiddleScreen from '../../components/MiddleScreen/MiddleScreen'; import {Panels} from '../../components/Panels/Panels'; @@ -9,20 +8,34 @@ import {Sidebar} from '../../components/Sidebar/Sidebar'; import StoreViewer from '../../components/StoreViewer/StoreViewer'; import {MainEditorStoreProvider} from '../../context/editorStore'; import {IframeProvider} from '../../context/iframeContext'; +import {useEditorTabs} from '../../hooks/useEditorTabs'; import useMainEditorInitialize from '../../hooks/useMainEditorInitialize'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; +import {editorCn} from '../../utils/cn'; import './Editor.scss'; -const b = block('editor'); +const b = editorCn('editor'); +interface SidebarTabComponent { + id: string; + title: string; + component: ElementType; +} interface EditorViewProps { onUpdate?: (pageContent: PageContent) => void; initialUrl: string; disableUrlField?: boolean; + componentsConfig?: { + middleTop?: ElementType; + leftTop?: ElementType[]; + rightTop?: ElementType[]; + leftTabs?: SidebarTabComponent[]; + rightTabs?: SidebarTabComponent[]; + }; } -const EditorView = (_props: EditorViewProps) => { +const EditorView = ({componentsConfig = {}}: EditorViewProps) => { const store = useMainEditorStore(); const {manipulateOverlayMode, disableMode} = store; @@ -39,15 +52,22 @@ const EditorView = (_props: EditorViewProps) => { }, [disableMode, manipulateOverlayMode], ); + const {left, right} = useEditorTabs(componentsConfig); return ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions <div className={b()} onMouseUp={onMouseUp}> <div className={b('body')}> <Panels - left={<Sidebar position={'left'} />} - right={<Sidebar position={'right'} startMenu="block-config" />} - middle={<MiddleScreen />} + left={<Sidebar tabs={left} />} + right={ + <Sidebar + tabs={right} + top={componentsConfig.rightTop} + defaultTab="block-config" + /> + } + middle={<MiddleScreen CustomTop={componentsConfig.middleTop} />} /> </div> <BigOverlay className={b('overlay')} /> diff --git a/src/editor-v2/components/GlobalConfig/GlobalConfig.scss b/editor-v2/containers/GlobalConfig/GlobalConfig.scss similarity index 76% rename from src/editor-v2/components/GlobalConfig/GlobalConfig.scss rename to editor-v2/containers/GlobalConfig/GlobalConfig.scss index d54900eb40..8210171157 100644 --- a/src/editor-v2/components/GlobalConfig/GlobalConfig.scss +++ b/editor-v2/containers/GlobalConfig/GlobalConfig.scss @@ -1,5 +1,3 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; @import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/components/GlobalConfig/GlobalConfig.tsx b/editor-v2/containers/GlobalConfig/GlobalConfig.tsx similarity index 67% rename from src/editor-v2/components/GlobalConfig/GlobalConfig.tsx rename to editor-v2/containers/GlobalConfig/GlobalConfig.tsx index 57594bca37..0a3d2eb484 100644 --- a/src/editor-v2/components/GlobalConfig/GlobalConfig.tsx +++ b/editor-v2/containers/GlobalConfig/GlobalConfig.tsx @@ -1,18 +1,19 @@ import React from 'react'; import {DynamicFormValue} from '../../../common/types'; -import {ClassNameProps} from '../../../models'; -import {block} from '../../../utils'; +import DynamicForm from '../../components/DynamicForm/DynamicForm'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; -import DynamicForm from '../DynamicForm/DynamicForm'; +import {editorCn} from '../../utils/cn'; import './GlobalConfig.scss'; -const b = block('global-config'); +const b = editorCn('global-config'); -interface GlobalConfigProps extends ClassNameProps {} +interface GlobalConfigProps { + className?: string; +} -const GlobalConfig: React.FC<GlobalConfigProps> = ({className}) => { +const GlobalConfig = ({className}: GlobalConfigProps) => { const {global, content, updateField} = useMainEditorStore(); const onUpdate = (key: string, value: DynamicFormValue) => { diff --git a/src/editor-v2/components/Source/Source.scss b/editor-v2/containers/Source/Source.scss similarity index 63% rename from src/editor-v2/components/Source/Source.scss rename to editor-v2/containers/Source/Source.scss index d85ac31e61..04512580ea 100644 --- a/src/editor-v2/components/Source/Source.scss +++ b/editor-v2/containers/Source/Source.scss @@ -1,5 +1,3 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; @import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; @@ -7,10 +5,16 @@ $block: '.#{$ns}source'; #{$block} { + width: 100%; display: inline-flex; + box-sizing: border-box; align-items: center; - padding: 0 8px; - gap: 8px; + flex-direction: row; + padding: 16px 12px; + + &__icon { + flex: 0 0 auto; + } &__text { flex: 1; diff --git a/src/editor-v2/components/Source/Source.tsx b/editor-v2/containers/Source/Source.tsx similarity index 76% rename from src/editor-v2/components/Source/Source.tsx rename to editor-v2/containers/Source/Source.tsx index 9c3ed18f60..176e819e25 100644 --- a/src/editor-v2/components/Source/Source.tsx +++ b/editor-v2/containers/Source/Source.tsx @@ -1,20 +1,16 @@ -import React, {useCallback, useContext} from 'react'; - import {ArrowRotateRight} from '@gravity-ui/icons'; import {Button, Icon, TextInput} from '@gravity-ui/uikit'; +import React, {useCallback, useContext} from 'react'; -import {ClassNameProps} from '../../../models'; -import {block} from '../../../utils'; import {IframeContext} from '../../context/iframeContext'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; +import {editorCn} from '../../utils/cn'; import './Source.scss'; -const b = block('source'); - -interface SourceProps extends ClassNameProps {} +const b = editorCn('source'); -const Source: React.FC<SourceProps> = ({className}) => { +const Source = () => { const {resetInitialize} = useMainEditorStore(); const {disableUrlField, url, setUrl} = useContext(IframeContext); @@ -34,8 +30,8 @@ const Source: React.FC<SourceProps> = ({className}) => { }; return ( - <div className={b(null, className)}> - <Button view="flat" size="m" onClick={reloadIframe}> + <div className={b()}> + <Button className={b('icon')} view="flat" size="m" onClick={reloadIframe}> <Icon data={ArrowRotateRight} size={18} /> </Button> <TextInput diff --git a/src/editor-v2/components/SourceCode/SourceCode.scss b/editor-v2/containers/SourceCode/SourceCode.scss similarity index 88% rename from src/editor-v2/components/SourceCode/SourceCode.scss rename to editor-v2/containers/SourceCode/SourceCode.scss index 205c407b97..df1cda2af8 100644 --- a/src/editor-v2/components/SourceCode/SourceCode.scss +++ b/editor-v2/containers/SourceCode/SourceCode.scss @@ -1,5 +1,3 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; @import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/components/SourceCode/SourceCode.tsx b/editor-v2/containers/SourceCode/SourceCode.tsx similarity index 93% rename from src/editor-v2/components/SourceCode/SourceCode.tsx rename to editor-v2/containers/SourceCode/SourceCode.tsx index 42d3570893..98d707081a 100644 --- a/src/editor-v2/components/SourceCode/SourceCode.tsx +++ b/editor-v2/containers/SourceCode/SourceCode.tsx @@ -1,5 +1,3 @@ -import React, {useState} from 'react'; - import {ArrowDownToSquare, Copy, CopyCheck} from '@gravity-ui/icons'; import { Alert, @@ -11,18 +9,21 @@ import { TextArea, } from '@gravity-ui/uikit'; import yaml from 'js-yaml'; +import React, {useState} from 'react'; -import {ClassNameProps, PageContent} from '../../../models'; -import {block} from '../../../utils'; +import {PageContent} from '../../../src/models'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; +import {editorCn} from '../../utils/cn'; import './SourceCode.scss'; -const b = block('source-code'); +const b = editorCn('source-code'); -interface SourceCodeProps extends ClassNameProps {} +interface SourceCodeProps { + className?: string; +} -const SourceCode: React.FC<SourceCodeProps> = ({className}) => { +const SourceCode = ({className}: SourceCodeProps) => { const {content, setContent} = useMainEditorStore(); const [isOpen, setIsOpen] = useState(false); const [tempConfig, setTempConfig] = useState(''); diff --git a/src/editor-v2/components/Tree/Tree.scss b/editor-v2/containers/Tree/Tree.scss similarity index 92% rename from src/editor-v2/components/Tree/Tree.scss rename to editor-v2/containers/Tree/Tree.scss index 1c197a67fe..ca1b5f5385 100644 --- a/src/editor-v2/components/Tree/Tree.scss +++ b/editor-v2/containers/Tree/Tree.scss @@ -1,5 +1,3 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; @import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/components/Tree/Tree.tsx b/editor-v2/containers/Tree/Tree.tsx similarity index 96% rename from src/editor-v2/components/Tree/Tree.tsx rename to editor-v2/containers/Tree/Tree.tsx index 26b6d85064..b789161104 100644 --- a/src/editor-v2/components/Tree/Tree.tsx +++ b/editor-v2/containers/Tree/Tree.tsx @@ -1,15 +1,14 @@ -import React, {PropsWithChildren} from 'react'; - import {TrashBin} from '@gravity-ui/icons'; import {Button, Card, Icon} from '@gravity-ui/uikit'; +import React, {PropsWithChildren} from 'react'; import {ItemConfig} from '../../../common/types'; -import {block} from '../../../utils'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; +import {editorCn} from '../../utils/cn'; import './Tree.scss'; -const b = block('tree'); +const b = editorCn('tree'); export interface TreeProps { blocks: ItemConfig[]; diff --git a/src/editor-v2/components/ViewSwitches/ViewSwitches.scss b/editor-v2/containers/ViewSwitches/ViewSwitches.scss similarity index 77% rename from src/editor-v2/components/ViewSwitches/ViewSwitches.scss rename to editor-v2/containers/ViewSwitches/ViewSwitches.scss index 45751b5989..7e0cee91bb 100644 --- a/src/editor-v2/components/ViewSwitches/ViewSwitches.scss +++ b/editor-v2/containers/ViewSwitches/ViewSwitches.scss @@ -1,5 +1,3 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; @import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; @@ -7,7 +5,9 @@ $block: '.#{$ns}view-switches'; #{$block} { + padding: 12px; display: inline-flex; + align-items: center; gap: 12px; &__zoom { diff --git a/src/editor-v2/components/ViewSwitches/ViewSwitches.tsx b/editor-v2/containers/ViewSwitches/ViewSwitches.tsx similarity index 52% rename from src/editor-v2/components/ViewSwitches/ViewSwitches.tsx rename to editor-v2/containers/ViewSwitches/ViewSwitches.tsx index cb9cd65786..fd01be7e09 100644 --- a/src/editor-v2/components/ViewSwitches/ViewSwitches.tsx +++ b/editor-v2/containers/ViewSwitches/ViewSwitches.tsx @@ -1,34 +1,22 @@ -import React from 'react'; - import {Minus, Plus} from '@gravity-ui/icons'; import {Button, Icon, Select} from '@gravity-ui/uikit'; +import React from 'react'; -import {ClassNameProps} from '../../../models'; -import {block} from '../../../utils'; import {ZOOM_STEPS} from '../../constants'; +import {useMainEditorStore} from '../../hooks/useMainEditorStore'; +import {editorCn} from '../../utils/cn'; import './ViewSwitches.scss'; -const b = block('view-switches'); - -interface ViewSwitchesProps extends ClassNameProps { - onZoomUpdate: (zoom: number) => void; - onDecreaseZoom: () => void; - onIncreaseZoom: () => void; - zoom: number; -} +const b = editorCn('view-switches'); -const ViewSwitches: React.FC<ViewSwitchesProps> = ({ - zoom, - onIncreaseZoom, - onDecreaseZoom, - onZoomUpdate, - className, -}) => { +const ViewSwitches = () => { + const {zoom, setZoom, decreaseZoom, increaseZoom} = useMainEditorStore(); return ( - <div className={b(null, className)}> + <div className={b()}> + <div>TBD screen sizes</div> <div className={b('zoom')}> - <Button view={'flat'} onClick={onDecreaseZoom}> + <Button view={'flat'} onClick={decreaseZoom}> <Icon data={Minus} /> </Button> <Select @@ -38,9 +26,9 @@ const ViewSwitches: React.FC<ViewSwitchesProps> = ({ value: String(step), content: `${String(step)}%`, }))} - onUpdate={(value) => onZoomUpdate(Number(value))} + onUpdate={(value) => setZoom(Number(value))} /> - <Button view={'flat'} onClick={onIncreaseZoom}> + <Button view={'flat'} onClick={increaseZoom}> <Icon data={Plus} /> </Button> </div> diff --git a/src/editor-v2/containers/__stories__/Editor.stories.tsx b/editor-v2/containers/__stories__/Editor.stories.tsx similarity index 99% rename from src/editor-v2/containers/__stories__/Editor.stories.tsx rename to editor-v2/containers/__stories__/Editor.stories.tsx index 9d5ee69592..c118f076b2 100644 --- a/src/editor-v2/containers/__stories__/Editor.stories.tsx +++ b/editor-v2/containers/__stories__/Editor.stories.tsx @@ -1,6 +1,5 @@ -import React from 'react'; - import {Meta, StoryFn} from '@storybook/react'; +import React from 'react'; import {Editor} from '../Editor/Editor'; diff --git a/src/editor-v2/containers/__stories__/utils.ts b/editor-v2/containers/__stories__/utils.ts similarity index 100% rename from src/editor-v2/containers/__stories__/utils.ts rename to editor-v2/containers/__stories__/utils.ts diff --git a/src/editor-v2/context/editorStore/MainEditorStoreContext.tsx b/editor-v2/context/editorStore/MainEditorStoreContext.tsx similarity index 99% rename from src/editor-v2/context/editorStore/MainEditorStoreContext.tsx rename to editor-v2/context/editorStore/MainEditorStoreContext.tsx index 07a9eb77ef..7f48605d49 100644 --- a/src/editor-v2/context/editorStore/MainEditorStoreContext.tsx +++ b/editor-v2/context/editorStore/MainEditorStoreContext.tsx @@ -1,5 +1,4 @@ import React from 'react'; - import {StoreApi} from 'zustand'; import {EditorStore, createEditorStore} from '../../store'; diff --git a/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx b/editor-v2/context/editorStore/MainEditorStoreProvider.tsx similarity index 99% rename from src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx rename to editor-v2/context/editorStore/MainEditorStoreProvider.tsx index 43bafff897..5d3b57306a 100644 --- a/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx +++ b/editor-v2/context/editorStore/MainEditorStoreProvider.tsx @@ -1,5 +1,4 @@ import React, {PropsWithChildren, useCallback, useContext, useEffect, useRef} from 'react'; - import {StoreApi} from 'zustand'; import {EditorState} from '../../../common/store'; diff --git a/src/editor-v2/context/editorStore/index.ts b/editor-v2/context/editorStore/index.ts similarity index 100% rename from src/editor-v2/context/editorStore/index.ts rename to editor-v2/context/editorStore/index.ts diff --git a/src/editor-v2/context/iframeContext/IframeContext.tsx b/editor-v2/context/iframeContext/IframeContext.tsx similarity index 100% rename from src/editor-v2/context/iframeContext/IframeContext.tsx rename to editor-v2/context/iframeContext/IframeContext.tsx diff --git a/src/editor-v2/context/iframeContext/IframeProvider.tsx b/editor-v2/context/iframeContext/IframeProvider.tsx similarity index 85% rename from src/editor-v2/context/iframeContext/IframeProvider.tsx rename to editor-v2/context/iframeContext/IframeProvider.tsx index d6b3454c06..c5a0295cee 100644 --- a/src/editor-v2/context/iframeContext/IframeProvider.tsx +++ b/editor-v2/context/iframeContext/IframeProvider.tsx @@ -7,8 +7,11 @@ interface IframeProviderProps extends PropsWithChildren { disableUrlField?: boolean; } -export const IframeProvider = (props: IframeProviderProps) => { - const {children, initialUrl = '', disableUrlField} = props; +export const IframeProvider = ({ + children, + initialUrl = '', + disableUrlField, +}: IframeProviderProps) => { const [iframeElement, setIframeElement] = useState<HTMLIFrameElement>(); const [url, setUrl] = useState(initialUrl); diff --git a/src/editor-v2/context/iframeContext/index.ts b/editor-v2/context/iframeContext/index.ts similarity index 100% rename from src/editor-v2/context/iframeContext/index.ts rename to editor-v2/context/iframeContext/index.ts diff --git a/editor-v2/hooks/useEditorTabs.tsx b/editor-v2/hooks/useEditorTabs.tsx new file mode 100644 index 0000000000..aaa6baf982 --- /dev/null +++ b/editor-v2/hooks/useEditorTabs.tsx @@ -0,0 +1,64 @@ +import Tabs, {TabsItemProps} from '../components/Tabs/Tabs'; +import BlockConfig from '../containers/BlockConfig/BlockConfig'; +import BlocksList from '../containers/BlocksList/BlocksList'; +import GlobalConfig from '../containers/GlobalConfig/GlobalConfig'; +import SourceCode from '../containers/SourceCode/SourceCode'; +import Tree from '../containers/Tree/Tree'; + +export const useEditorTabs = ({ + leftTabs = [], + rightTabs = [], +}: { + leftTabs?: TabsItemProps[]; + rightTabs?: TabsItemProps[]; +}) => { + return { + left: [ + { + id: 'page', + title: 'Page', + component: () => ( + <Tabs + items={[ + { + id: 'blocks-list', + title: 'Blocks', + component: BlocksList, + }, + { + id: 'tree', + title: 'Tree', + component: Tree, + }, + { + id: 'source-code', + title: 'SourceCode', + component: SourceCode, + }, + ]} + /> + ), + }, + { + id: 'global-config', + title: 'GlobalConfig', + component: GlobalConfig, + }, + ...leftTabs, + ], + right: [ + { + id: 'block-config', + title: 'BlockConfig', + component: BlockConfig, + position: 'right', + }, + { + id: 'source-code', + title: 'SourceCode', + component: SourceCode, + }, + ...rightTabs, + ], + }; +}; diff --git a/src/editor-v2/hooks/useMainEditorInitialize.ts b/editor-v2/hooks/useMainEditorInitialize.ts similarity index 100% rename from src/editor-v2/hooks/useMainEditorInitialize.ts rename to editor-v2/hooks/useMainEditorInitialize.ts diff --git a/src/editor-v2/hooks/useMainEditorStore.ts b/editor-v2/hooks/useMainEditorStore.ts similarity index 99% rename from src/editor-v2/hooks/useMainEditorStore.ts rename to editor-v2/hooks/useMainEditorStore.ts index 49c3496a90..5ff0baf4fe 100644 --- a/src/editor-v2/hooks/useMainEditorStore.ts +++ b/editor-v2/hooks/useMainEditorStore.ts @@ -1,5 +1,4 @@ import {useContext} from 'react'; - import {useStore} from 'zustand'; import {MainEditorStoreContext} from '../context/editorStore'; diff --git a/src/editor-v2/hooks/usePostMessageEvents.ts b/editor-v2/hooks/usePostMessageEvents.ts similarity index 100% rename from src/editor-v2/hooks/usePostMessageEvents.ts rename to editor-v2/hooks/usePostMessageEvents.ts diff --git a/src/editor-v2/index.ts b/editor-v2/index.ts similarity index 100% rename from src/editor-v2/index.ts rename to editor-v2/index.ts diff --git a/src/editor-v2/store.ts b/editor-v2/store.ts similarity index 99% rename from src/editor-v2/store.ts rename to editor-v2/store.ts index a75e1262b3..18a882223d 100644 --- a/src/editor-v2/store.ts +++ b/editor-v2/store.ts @@ -3,7 +3,7 @@ import _ from 'lodash'; import {EditorState, initialStore} from '../common/store'; import {DynamicFormValue} from '../common/types'; import {initializeStore} from '../common/utils'; -import {ConstructorBlock, PageContent} from '../models'; +import {ConstructorBlock, PageContent} from '../src/models'; import {ZOOM_STEPS} from './constants'; import { diff --git a/src/editor-v2/styles/mixins.scss b/editor-v2/styles/mixins.scss similarity index 83% rename from src/editor-v2/styles/mixins.scss rename to editor-v2/styles/mixins.scss index 6144f64253..63223ed648 100644 --- a/src/editor-v2/styles/mixins.scss +++ b/editor-v2/styles/mixins.scss @@ -1,3 +1,4 @@ +@import '@gravity-ui/uikit/styles/mixins.scss'; @import './variables.scss'; @mixin control($hoverScale: 1.05) { diff --git a/editor-v2/styles/root.scss b/editor-v2/styles/root.scss new file mode 100644 index 0000000000..25fa51c5a8 --- /dev/null +++ b/editor-v2/styles/root.scss @@ -0,0 +1,2 @@ +body { +} diff --git a/src/editor-v2/styles/variables.scss b/editor-v2/styles/variables.scss similarity index 90% rename from src/editor-v2/styles/variables.scss rename to editor-v2/styles/variables.scss index c776f820a1..e8cd79a738 100644 --- a/src/editor-v2/styles/variables.scss +++ b/editor-v2/styles/variables.scss @@ -1,3 +1,4 @@ +$ns: 'pceditor-'; $editorTransitionTime: 0.2s; $editorShadow: 0px 2px 8px rgba(0, 0, 0, 0.06), 0px 4px 24px rgba(0, 0, 0, 0.06); $editorControlBorderRadius: 8px; diff --git a/editor-v2/utils/cn.ts b/editor-v2/utils/cn.ts new file mode 100644 index 0000000000..a43775d67e --- /dev/null +++ b/editor-v2/utils/cn.ts @@ -0,0 +1,5 @@ +import {withNaming} from '@bem-react/classname'; + +export const EDITOR_NAMESPACE = 'pceditor-'; + +export const editorCn = withNaming({n: EDITOR_NAMESPACE, e: '__', m: '_'}); diff --git a/src/editor-v2/utils/code.ts b/editor-v2/utils/code.ts similarity index 83% rename from src/editor-v2/utils/code.ts rename to editor-v2/utils/code.ts index 3b46632677..5fe3297231 100644 --- a/src/editor-v2/utils/code.ts +++ b/editor-v2/utils/code.ts @@ -1,6 +1,6 @@ import yaml from 'js-yaml'; -import {PageContent} from '../../models'; +import {PageContent} from '../../src/models'; export function parseCode(code: string) { const pageContent = yaml.load(code) as PageContent; diff --git a/src/editor-v2/utils/index.ts b/editor-v2/utils/index.ts similarity index 98% rename from src/editor-v2/utils/index.ts rename to editor-v2/utils/index.ts index dba1995558..8bb7e96fd1 100644 --- a/src/editor-v2/utils/index.ts +++ b/editor-v2/utils/index.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {ConstructorBlock} from '../../models'; +import {ConstructorBlock} from '../../src/models'; export function insert<T>(arr: Array<T>, index: number, newItem: T) { return [...arr.slice(0, index), newItem, ...arr.slice(index)]; diff --git a/editor-v2/utils/store.ts b/editor-v2/utils/store.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/playground/src/app/page.scss b/playground/src/app/page.scss index 213d4b128a..e4fa88e636 100644 --- a/playground/src/app/page.scss +++ b/playground/src/app/page.scss @@ -2,4 +2,8 @@ $block: '.home'; #{$block} { height: 100vh; + + &__test { + padding: 16px; + } } diff --git a/playground/src/app/page.tsx b/playground/src/app/page.tsx index 061338f96c..b817c38933 100644 --- a/playground/src/app/page.tsx +++ b/playground/src/app/page.tsx @@ -3,12 +3,16 @@ import {ThemeProvider} from '@gravity-ui/uikit'; import block from 'bem-cn-lite'; import React, {useEffect, useState} from 'react'; -import {Editor} from '../../../src/editor-v2'; +import {Editor} from '../../../editor-v2'; +import Source from '../../../editor-v2/containers/Source/Source'; +import ViewSwitches from '../../../editor-v2/containers/ViewSwitches/ViewSwitches'; import './page.scss'; const b = block('home'); +const Test = () => <div className={b('test')}>custom test</div>; + export default function Home() { const [initialUrl, setInitialUrl] = useState<string>(''); @@ -22,7 +26,22 @@ export default function Home() { <ThemeProvider theme={'light'}> <div className={b()}> {initialUrl && ( - <Editor initialUrl={initialUrl} disableUrlField={false} onUpdate={() => {}} /> + <Editor + componentsConfig={{ + middleTop: Test, + leftTabs: [ + { + id: 'test', + title: 'TEST', + component: Test, + }, + ], + rightTop: [Source, ViewSwitches], + }} + initialUrl={initialUrl} + disableUrlField={false} + onUpdate={() => {}} + /> )} </div> </ThemeProvider> diff --git a/playground/src/app/pc-2/navigation.json b/playground/src/app/pc-2/navigation.json new file mode 100644 index 0000000000..8df0e9cccd --- /dev/null +++ b/playground/src/app/pc-2/navigation.json @@ -0,0 +1,48 @@ +{ + "logo": { + "text": "", + "icon": "https://storage.yandexcloud.net/ydb-site-assets/ydb_icon.svg" + }, + "header": { + "leftItems": [ + { + "type": "link", + "text": "Документация", + "url": "/../docs/ru", + "target": "_self" + }, + { + "type": "link", + "text": "Команда", + "url": "/careers/" + }, + { + "type": "link", + "text": "Студентам", + "url": "/students/" + }, + { + "type": "link", + "text": "Поддержка", + "url": "/support/" + }, + { + "type": "link", + "text": "Блог", + "url": "/blog/" + } + ], + "rightItems": [ + { + "type": "github-button", + "text": "Star", + "label": "Star ydb-platform/ydb on GitHub", + "url": "https://github.com/ydb-platform/ydb" + } + ] + }, + "meta": { + "title": "", + "description": "" + } +} diff --git a/playground/src/app/pc-2/page.tsx b/playground/src/app/pc-2/page.tsx index 5b54e6796e..9765c3c46e 100644 --- a/playground/src/app/pc-2/page.tsx +++ b/playground/src/app/pc-2/page.tsx @@ -4,12 +4,13 @@ import React from 'react'; import {PageConstructor, PageConstructorProvider} from '../../../../src'; import content from './content.json'; +import navigation from './navigation.json'; import './styles.scss'; export default function Home() { return ( <PageConstructorProvider projectSettings={{disableCompress: true}}> - <PageConstructor content={content} /> + <PageConstructor content={content} navigation={navigation} /> </PageConstructorProvider> ); } diff --git a/src/blocks/CardLayout/index.ts b/src/blocks/CardLayout/index.ts index 2056f833c1..13ce0f2db0 100644 --- a/src/blocks/CardLayout/index.ts +++ b/src/blocks/CardLayout/index.ts @@ -1,6 +1,6 @@ import {JSONSchemaType} from 'ajv'; -import {ConfigInput} from '../../common/types'; +import {ConfigInput} from '../../../common/types'; import {BlockData} from '../../constructor-items'; import {sliderSizesArray, textSize} from '../../schema/validators/common'; import {generateFromAJV} from '../../utils/form-generator'; diff --git a/src/blocks/Header/dynamic-form.ts b/src/blocks/Header/dynamic-form.ts index d3f174aba0..33c4b9b932 100644 --- a/src/blocks/Header/dynamic-form.ts +++ b/src/blocks/Header/dynamic-form.ts @@ -1,4 +1,4 @@ -import {BlockConfig, ConfigInput} from '../../common/types'; +import {BlockConfig, ConfigInput} from '../../../common/types'; import {imageInputs} from '../../components/Image/dynamic-form'; import {Theme} from '../../models'; diff --git a/src/blocks/Slider/dynamic-form.ts b/src/blocks/Slider/dynamic-form.ts index ea7468997c..5bf5520fc3 100644 --- a/src/blocks/Slider/dynamic-form.ts +++ b/src/blocks/Slider/dynamic-form.ts @@ -1,4 +1,4 @@ -import {BlockConfig, ConfigInput} from '../../common/types'; +import {BlockConfig, ConfigInput} from '../../../common/types'; import {sliderSizesArray, textSize} from '../../schema/validators/common'; const textSizeEnum = textSize.map((size) => ({value: size, content: size})); diff --git a/src/blocks/TestEditorBlock/form.ts b/src/blocks/TestEditorBlock/form.ts index 838eacb06e..849cb6d307 100644 --- a/src/blocks/TestEditorBlock/form.ts +++ b/src/blocks/TestEditorBlock/form.ts @@ -9,7 +9,7 @@ import { SelectSingleInput, TextAreaInput, TextInput, -} from '../../common/types'; +} from '../../../common/types'; const textInput: TextInput = { type: 'text', diff --git a/src/components/Image/dynamic-form.ts b/src/components/Image/dynamic-form.ts index f5f8de747f..23b3f44cfa 100644 --- a/src/components/Image/dynamic-form.ts +++ b/src/components/Image/dynamic-form.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {ConfigInput} from '../../common/types'; +import {ConfigInput} from '../../../common/types'; const devices = ['desktop', 'tablet', 'mobile']; diff --git a/src/context/editorStoreContext/PCEditorStoreContext.tsx b/src/context/editorStoreContext/PCEditorStoreContext.tsx index acceef310e..e957bd6f99 100644 --- a/src/context/editorStoreContext/PCEditorStoreContext.tsx +++ b/src/context/editorStoreContext/PCEditorStoreContext.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {StoreApi} from 'zustand'; -import {EditorState, createPCEditorStore} from '../../common/store'; +import {EditorState, createPCEditorStore} from '../../../common/store'; export interface PCEditorStoreContextProps { state: StoreApi<EditorState>; diff --git a/src/context/editorStoreContext/PCEditorStoreProvider.tsx b/src/context/editorStoreContext/PCEditorStoreProvider.tsx index 22938c5079..a6f0add576 100644 --- a/src/context/editorStoreContext/PCEditorStoreProvider.tsx +++ b/src/context/editorStoreContext/PCEditorStoreProvider.tsx @@ -2,8 +2,8 @@ import React, {PropsWithChildren, useCallback, useEffect, useRef} from 'react'; import {StoreApi} from 'zustand'; -import {EditorState, createPCEditorStore} from '../../common/store'; -import {StoreSyncMessage} from '../../common/types'; +import {EditorState, createPCEditorStore} from '../../../common/store'; +import {StoreSyncMessage} from '../../../common/types'; import {PCEditorStoreContext} from './PCEditorStoreContext'; diff --git a/src/editor-v2/components/DynamicForm/Fields/Object/Object.scss b/src/editor-v2/components/DynamicForm/Fields/Object/Object.scss deleted file mode 100644 index b3aa2610b6..0000000000 --- a/src/editor-v2/components/DynamicForm/Fields/Object/Object.scss +++ /dev/null @@ -1,10 +0,0 @@ -@import '../../../../../../styles/variables.scss'; -@import '../../../../../../styles/mixins.scss'; - -$block: '.#{$ns}object-dynamic-field'; - -#{$block} { - &__card { - padding: 12px; - } -} diff --git a/src/editor-v2/components/Sidebar/Sidebar.scss b/src/editor-v2/components/Sidebar/Sidebar.scss deleted file mode 100644 index 842996be65..0000000000 --- a/src/editor-v2/components/Sidebar/Sidebar.scss +++ /dev/null @@ -1,86 +0,0 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; -@import '../../styles/root.scss'; -@import '../../styles/variables.scss'; -@import '../../styles/mixins.scss'; - -$block: '.#{$ns}sidebar'; - -#{$block} { - $class: &; - - position: relative; - height: 100%; - width: 100%; - display: flex; - - &_position_left { - flex-direction: row; - } - - &_position_right { - flex-direction: row-reverse; - } - - &__buttons-wrapper { - border-right: 1px solid var(--g-color-line-generic); - } - - &__buttons { - display: inline-flex; - flex-direction: column; - padding: 8px; - gap: 8px; - - &:hover #{$class}__label-wrapper { - opacity: 1; - } - } - - &__body { - flex: 1; - border-right: 1px solid var(--g-color-line-generic); - overflow-y: auto; - } - - &__button-wrapper { - position: relative; - } - - &_position_right { - #{$class}__button-wrapper:hover { - #{$class}__label-wrapper { - right: 40px; - } - } - - #{$class}__label-wrapper { - right: 50px; - } - } - - &_position_left { - #{$class}__button-wrapper:hover { - #{$class}__label-wrapper { - left: 40px; - } - } - - #{$class}__label-wrapper { - left: 50px; - } - } - - &__label-wrapper { - pointer-events: none; - opacity: 0; - - background-color: var(--g-color-base-background); - position: absolute; - top: 50%; - transform: translateY(-50%); - z-index: 100; - - transition: left 0.1s ease, right 0.1s ease, opacity 0.1s ease; - } -} diff --git a/src/editor-v2/components/Sidebar/Sidebar.tsx b/src/editor-v2/components/Sidebar/Sidebar.tsx deleted file mode 100644 index 09311f20e3..0000000000 --- a/src/editor-v2/components/Sidebar/Sidebar.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import React, {useMemo, useState} from 'react'; - -import {Code, FolderTree, Gear, Rectangles4, Square} from '@gravity-ui/icons'; -import {Button, Icon, Label} from '@gravity-ui/uikit'; -import {IconData} from '@gravity-ui/uikit/build/esm/components/Icon/Icon'; - -import {ClassNameProps} from '../../../models'; -import {block} from '../../../utils'; -import BlockConfig from '../BlockConfig/BlockConfig'; -import BlocksList from '../BlocksList/BlocksList'; -import GlobalConfig from '../GlobalConfig/GlobalConfig'; -import SourceCode from '../SourceCode/SourceCode'; -import Tree from '../Tree/Tree'; - -import './Sidebar.scss'; - -const b = block('sidebar'); - -interface SidebarProps extends ClassNameProps { - position: 'left' | 'right'; - startMenu?: string; -} - -interface TabConfig { - id: string; - name: string; - icon: IconData; - component: React.ElementType; - position: 'left' | 'right' | 'both'; -} - -export const Sidebar = ({className, position, startMenu}: SidebarProps) => { - const [currentTab, setCurrentTab] = useState(startMenu || 'blocks-list'); - - const defaultTabs: TabConfig[] = useMemo( - () => [ - { - id: 'blocks-list', - name: 'Блоки', - icon: Rectangles4, - component: BlocksList, - position: 'left', - }, - { - id: 'global-config', - name: 'Глобальная конфигурация', - icon: Gear, - component: GlobalConfig, - position: 'left', - }, - { - id: 'tree', - name: 'Дерево', - icon: FolderTree, - component: Tree, - position: 'left', - }, - { - id: 'block-config', - name: 'Конфигурация блока', - icon: Square, - component: BlockConfig, - position: 'right', - }, - { - id: 'source-code', - name: 'Исходный код', - icon: Code, - component: SourceCode, - position: 'both', - }, - ], - [], - ); - - const filteredTabs = defaultTabs.filter( - ({position: tabPosition}) => tabPosition === 'both' || tabPosition === position, - ); - - const TabComponent = useMemo(() => { - const findTab = defaultTabs.find(({id}) => id === currentTab); - return findTab?.component; - }, [currentTab, defaultTabs]); - - return ( - <div className={`${b(null, className)} ${b('', {position})}`}> - <div className={b('buttons-wrapper')}> - <div className={b('buttons')}> - {filteredTabs.map(({id, icon, name}) => ( - <div className={b('button-wrapper')} key={id}> - <Button - view={currentTab === id ? 'action' : 'flat'} - size="m" - onClick={() => setCurrentTab(id)} - > - <Icon data={icon} size={18} /> - </Button> - <div className={b('label-wrapper')}> - <Label theme={'normal'} size={'m'}> - {name} - </Label> - </div> - </div> - ))} - </div> - </div> - <div className={b('body')}>{TabComponent && <TabComponent />}</div> - </div> - ); -}; diff --git a/src/editor-v2/components/TopBar/TopBar.scss b/src/editor-v2/components/TopBar/TopBar.scss deleted file mode 100644 index 506b6ae875..0000000000 --- a/src/editor-v2/components/TopBar/TopBar.scss +++ /dev/null @@ -1,34 +0,0 @@ -@import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; -@import '../../styles/root.scss'; -@import '../../styles/variables.scss'; -@import '../../styles/mixins.scss'; - -$block: '.#{$ns}topbar'; - -#{$block} { - display: flex; - justify-content: start; - align-items: center; - width: 100%; - height: 40px; - - border-bottom: 1px solid var(--g-color-line-generic); - - &__switches { - display: flex; - align-items: center; - width: auto; - height: 100%; - padding: 0 12px; - gap: 12px; - - border-right: 1px solid var(--g-color-line-generic); - } - - &__source { - flex: 1; - width: 100%; - box-sizing: border-box; - } -} diff --git a/src/editor-v2/components/TopBar/TopBar.tsx b/src/editor-v2/components/TopBar/TopBar.tsx deleted file mode 100644 index bf9c0b365d..0000000000 --- a/src/editor-v2/components/TopBar/TopBar.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; - -import {ClassNameProps} from '../../../models'; -import {block} from '../../../utils'; -import Source from '../Source/Source'; -import ViewSwitches from '../ViewSwitches/ViewSwitches'; - -import './TopBar.scss'; - -const b = block('topbar'); - -interface TopBarProps extends ClassNameProps { - onZoomUpdate: (zoom: number) => void; - onDecreaseZoom: () => void; - onIncreaseZoom: () => void; - zoom: number; -} - -const TopBar: React.FC<TopBarProps> = ({ - zoom, - onDecreaseZoom, - onIncreaseZoom, - onZoomUpdate, - className, -}) => { - return ( - <div className={b(null, className)}> - <div className={b('switches')}> - <ViewSwitches - onZoomUpdate={onZoomUpdate} - onDecreaseZoom={onDecreaseZoom} - onIncreaseZoom={onIncreaseZoom} - zoom={zoom} - /> - </div> - <Source className={b('source')} /> - </div> - ); -}; - -export default TopBar; diff --git a/src/editor-v2/icons/Tablet.tsx b/src/editor-v2/icons/Tablet.tsx deleted file mode 100644 index 9ece669007..0000000000 --- a/src/editor-v2/icons/Tablet.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; - -import {a11yHiddenSvgProps} from '../../utils/svg'; - -export const Tablet: React.FC<React.SVGProps<SVGSVGElement>> = (props) => ( - <svg - xmlns="http://www.w3.org/2000/svg" - width="12" - height="14" - viewBox="0 0 12 14" - fill="none" - {...a11yHiddenSvgProps} - {...props} - > - <path - fillRule="evenodd" - clipRule="evenodd" - d="M10.5 3L10.5 11C10.5 11.8284 9.82843 12.5 9 12.5L3 12.5C2.17157 12.5 1.5 11.8284 1.5 11L1.5 3C1.5 2.17157 2.17157 1.5 3 1.5L9 1.5C9.82843 1.5 10.5 2.17157 10.5 3ZM9 -1.31134e-07C10.6569 -5.87108e-08 12 1.34315 12 3L12 11C12 12.6569 10.6569 14 9 14L3 14C1.34315 14 4.00426e-07 12.6569 4.72849e-07 11L8.2254e-07 3C8.94964e-07 1.34315 1.34315 -4.65826e-07 3 -3.93403e-07L9 -1.31134e-07ZM3.75 9.5C3.33579 9.5 3 9.83579 3 10.25C3 10.6642 3.33579 11 3.75 11L8.25 11C8.66421 11 9 10.6642 9 10.25C9 9.83579 8.66421 9.5 8.25 9.5L3.75 9.5Z" - fill="currentColor" - fillOpacity="0.85" - /> - </svg> -); diff --git a/src/editor-v2/styles/root.scss b/src/editor-v2/styles/root.scss deleted file mode 100644 index d69df66c26..0000000000 --- a/src/editor-v2/styles/root.scss +++ /dev/null @@ -1,9 +0,0 @@ -body { - --pc-editor-header-height: 48px; - --pc-editor-code-header-height: 36px; - --pc-editor-divider-width: 12px; - --pc-editor-left-column-width: calc(400px + var(--pc-editor-divider-width)); - --pc-editor-base-color: var(--g-color-base-brand); - --pc-editor-control-color: var(--g-color-base-brand); - --pc-editor-control-icon-color: var(--g-color-text-dark-primary); -} diff --git a/src/editor-v2/utils/store.ts b/src/editor-v2/utils/store.ts deleted file mode 100644 index 6806f7ca8c..0000000000 --- a/src/editor-v2/utils/store.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {StoreApi, create} from 'zustand'; -import {devtools, subscribeWithSelector} from 'zustand/middleware'; - -export function initializeStore<State, Methods>( - initialState: State, - methods: ( - set: StoreApi<State & Methods>['setState'], - get: StoreApi<State & Methods>['getState'], - ) => Methods, -) { - return (overrideInitialState?: Partial<State>) => - create< - State & Methods, - [['zustand/subscribeWithSelector', never], ['zustand/devtools', never]] - >( - subscribeWithSelector( - devtools((set, get) => ({ - ...initialState, - ...overrideInitialState, - ...methods(set, get), - })), - ), - ); -} diff --git a/src/hooks/usePCEditorInitializeEvents.ts b/src/hooks/usePCEditorInitializeEvents.ts index fa4a61b81a..3053b23317 100644 --- a/src/hooks/usePCEditorInitializeEvents.ts +++ b/src/hooks/usePCEditorInitializeEvents.ts @@ -3,7 +3,7 @@ import {useCallback, useEffect} from 'react'; import {JSONSchemaType} from 'ajv'; import _ from 'lodash'; -import {ItemConfig} from '../common/types'; +import {ItemConfig} from '../../common/types'; import {blockDataMap} from '../constructor-items'; import {PageContent} from '../models'; import {defaultComponentsConfigurationSchema} from '../schema'; diff --git a/src/hooks/usePostMessageAPI.ts b/src/hooks/usePostMessageAPI.ts index 6cf8c4cdaa..9c1953c2a9 100644 --- a/src/hooks/usePostMessageAPI.ts +++ b/src/hooks/usePostMessageAPI.ts @@ -1,7 +1,7 @@ import {useEffect} from 'react'; -import {PostMessageAPIMessage} from '../common/types'; -import {ActionMessageTypes, EventMessageTypes} from '../common/types/actions'; +import {PostMessageAPIMessage} from '../../common/types'; +import {ActionMessageTypes, EventMessageTypes} from '../../common/types/actions'; export function sendEventPostMessage<K extends keyof EventMessageTypes>( action: K, diff --git a/src/index.ts b/src/index.ts index 05106270fd..10becb250d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,5 @@ export * from './utils'; export * from './schema'; export * from './hooks'; export * from './navigation'; -export * from './common/postMessage'; export {BREAKPOINTS} from './constants'; From 91fa72e3d09578e19afeda00284462640d2a5f0c Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Sun, 9 Mar 2025 23:47:48 +0300 Subject: [PATCH 010/118] fix: common imports --- common/types/actions.ts | 2 +- common/types/forms.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/types/actions.ts b/common/types/actions.ts index ffb86cb764..c0add108ac 100644 --- a/common/types/actions.ts +++ b/common/types/actions.ts @@ -1,4 +1,4 @@ -import {PageContent} from '../../models'; +import {PageContent} from '../../src/models'; import {EditorState} from '../store'; export type MessageTypes = EventMessageTypes & ActionMessageTypes; diff --git a/common/types/forms.ts b/common/types/forms.ts index 6597b12e53..f4e21a7571 100644 --- a/common/types/forms.ts +++ b/common/types/forms.ts @@ -1,4 +1,4 @@ -import {PageContent} from '../../models'; +import {PageContent} from '../../src/models'; export type DynamicFormValue = string | number | [] | object | boolean | PageContent | undefined; From afdd41138fbabd95b710467d08bd2a216c4e0fc7 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Sun, 9 Mar 2025 23:49:32 +0300 Subject: [PATCH 011/118] fix: overlay css --- editor-v2/components/Overlay/Overlay.scss | 8 +- editor-v2/components/Overlay/Overlay.tsx | 55 ++++++----- editor-v2/components/Tabs/Tabs.scss | 2 +- .../containers/SourceCode/SourceCode.scss | 30 +++--- .../containers/SourceCode/SourceCode.tsx | 93 +------------------ .../containers/ViewSwitches/ViewSwitches.tsx | 2 +- editor-v2/hooks/useEditorTabs.tsx | 22 +++-- editor-v2/store.ts | 2 +- editor-v2/styles/root.scss | 7 +- 9 files changed, 77 insertions(+), 144 deletions(-) diff --git a/editor-v2/components/Overlay/Overlay.scss b/editor-v2/components/Overlay/Overlay.scss index bde825744f..975ae27caa 100644 --- a/editor-v2/components/Overlay/Overlay.scss +++ b/editor-v2/components/Overlay/Overlay.scss @@ -18,6 +18,7 @@ $block: '.#{$ns}overlay'; &__border { position: absolute; border: 3px var(--g-color-line-brand) solid; + border-radius: 24px; box-sizing: border-box; box-shadow: 4px 4px 8px 0 var(--g-color-sfx-shadow); @@ -53,14 +54,13 @@ $block: '.#{$ns}overlay'; &__actions { pointer-events: auto; position: absolute; - bottom: -40px; - left: 0; + bottom: 0; + left: 50%; + transform: translate(-50%, 100%); display: flex; align-items: center; background: transparent; - box-shadow: 4px 4px 8px 0 var(--g-color-sfx-shadow); - border-radius: 20px; } &__action-button { diff --git a/editor-v2/components/Overlay/Overlay.tsx b/editor-v2/components/Overlay/Overlay.tsx index b9d74b7dc9..c38643dfcc 100644 --- a/editor-v2/components/Overlay/Overlay.tsx +++ b/editor-v2/components/Overlay/Overlay.tsx @@ -1,6 +1,6 @@ -import {Copy, Grip, TrashBin} from '@gravity-ui/icons'; +import {ChevronDown, ChevronUp, Copy, TrashBin} from '@gravity-ui/icons'; import {Button, Icon} from '@gravity-ui/uikit'; -import React, {useCallback, useState} from 'react'; +import React, {useState} from 'react'; import {usePostMessageAPIListener} from '../../../common/postMessage'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; @@ -29,7 +29,7 @@ const Overlay = ({className}: OverlayProps) => { deleteBlock, duplicateBlock, manipulateOverlayMode, - enableReorderMode, + reorderBlock, } = useMainEditorStore(); const [insertLineBox, setInsertLineBox] = useState<InsertLineProps | undefined>(undefined); const [hoverBorders, setHoverBorders] = useState<DOMRect | null>(null); @@ -59,11 +59,23 @@ const Overlay = ({className}: OverlayProps) => { setBlockBorders(rect || null); }); - const onMouseDown = useCallback(() => { - if (selectedBlock) { - enableReorderMode(selectedBlock); - } - }, [enableReorderMode, selectedBlock]); + const handleMoveUp = () => { + if (!selectedBlock) return; + const destination = [...selectedBlock]; + destination[destination.length - 1] = destination[destination.length - 1] - 1; + reorderBlock(selectedBlock, destination, 'prepend'); + setSelectedBlock(undefined); + setBlockBorders(null); + }; + + const handleMoveDown = () => { + if (!selectedBlock) return; + const destination = [...selectedBlock]; + destination[destination.length - 1] = destination[destination.length - 1] + 1; + reorderBlock(selectedBlock, destination, 'append'); + setSelectedBlock(undefined); + setBlockBorders(null); + }; return ( <div className={b(null, className)} style={{height: `${height}px`}}> @@ -78,34 +90,29 @@ const Overlay = ({className}: OverlayProps) => { }} > <div className={b('actions')}> - {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */} - <div onMouseDown={onMouseDown}> - <Button - pin={'round-clear'} - className={`${b('action-button', {grip: true})}`} - size={'m'} - view={'action'} - > - <Icon data={Grip} size={18} /> - </Button> - </div> + <Button view="flat" size={'m'} onClick={handleMoveUp}> + <Icon data={ChevronUp} size={18} /> + </Button> <Button - pin={'clear-clear'} + pin={'round-clear'} className={b('action-button')} size={'m'} view={'action'} - onClick={() => selectedBlock && deleteBlock(selectedBlock)} + onClick={() => selectedBlock && duplicateBlock(selectedBlock)} > - <Icon data={TrashBin} size={18} /> + <Icon data={Copy} size={18} /> </Button> <Button pin={'clear-round'} className={b('action-button')} size={'m'} view={'action'} - onClick={() => selectedBlock && duplicateBlock(selectedBlock)} + onClick={() => selectedBlock && deleteBlock(selectedBlock)} > - <Icon data={Copy} size={18} /> + <Icon data={TrashBin} size={18} /> + </Button> + <Button view="flat" size={'m'} onClick={handleMoveDown}> + <Icon data={ChevronDown} size={18} /> </Button> </div> </div> diff --git a/editor-v2/components/Tabs/Tabs.scss b/editor-v2/components/Tabs/Tabs.scss index ecd849e296..c4f2e727d9 100644 --- a/editor-v2/components/Tabs/Tabs.scss +++ b/editor-v2/components/Tabs/Tabs.scss @@ -4,7 +4,7 @@ $block: '.#{$ns}tabs'; #{$block} { - overflow: auto hidden; + overflow: auto; width: 100%; &__item { diff --git a/editor-v2/containers/SourceCode/SourceCode.scss b/editor-v2/containers/SourceCode/SourceCode.scss index df1cda2af8..30f7272a79 100644 --- a/editor-v2/containers/SourceCode/SourceCode.scss +++ b/editor-v2/containers/SourceCode/SourceCode.scss @@ -6,31 +6,25 @@ $block: '.#{$ns}source-code'; #{$block} { height: 100%; + padding: 16px 12px; box-sizing: border-box; display: flex; flex-direction: column; - gap: 10px; + gap: 12px; + + &__title { + font-weight: 500; + font-size: 13px; + line-height: 18px; + color: var(--g-color-private-black-300); + } &__code { white-space: pre; @include text-code-inline-1; - padding: 0 10px 10px; + padding: 6px 2px 8px; overflow: auto; - } - - &__head { - display: flex; - align-items: center; - justify-content: flex-start; - gap: 8px; - padding: 10px 10px 0; - } - - &__textarea textarea { - @include text-code-inline-1; - } - - &__alert { - margin-bottom: 8px; + border: 1px solid var(--g-color-line-generic); + border-radius: 6px; } } diff --git a/editor-v2/containers/SourceCode/SourceCode.tsx b/editor-v2/containers/SourceCode/SourceCode.tsx index 98d707081a..864d2f3100 100644 --- a/editor-v2/containers/SourceCode/SourceCode.tsx +++ b/editor-v2/containers/SourceCode/SourceCode.tsx @@ -1,17 +1,6 @@ -import {ArrowDownToSquare, Copy, CopyCheck} from '@gravity-ui/icons'; -import { - Alert, - Button, - CopyToClipboard, - Dialog, - Icon, - RadioButton, - TextArea, -} from '@gravity-ui/uikit'; import yaml from 'js-yaml'; -import React, {useState} from 'react'; +import React from 'react'; -import {PageContent} from '../../../src/models'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; @@ -21,90 +10,18 @@ const b = editorCn('source-code'); interface SourceCodeProps { className?: string; + format: 'yaml' | 'json'; } -const SourceCode = ({className}: SourceCodeProps) => { - const {content, setContent} = useMainEditorStore(); - const [isOpen, setIsOpen] = useState(false); - const [tempConfig, setTempConfig] = useState(''); - const [format, setFormat] = useState<'yaml' | 'json'>('yaml'); - - const onUpdate = () => { - let object; - - try { - if (tempConfig.trim().startsWith('{') && tempConfig.trim().endsWith('}')) { - object = JSON.parse(tempConfig); - } else { - object = yaml.load(tempConfig); - } - } catch { - // eslint-disable-next-line no-console - console.error('JSON.parse failed'); - } - - if (object) { - setContent(object as PageContent); - } - - setIsOpen(false); - }; +const SourceCode = ({className, format}: SourceCodeProps) => { + const {content} = useMainEditorStore(); const text = format === 'yaml' ? yaml.dump(content) : JSON.stringify(content, null, 2); return ( <div className={b(null, className)}> - <div className={b('head')}> - <RadioButton value={format} onUpdate={setFormat}> - <RadioButton.Option value={'yaml'} content={'YAML'} /> - <RadioButton.Option value={'json'} content={'JSON'} /> - </RadioButton> - <CopyToClipboard text={text} timeout={1000}> - {(status) => ( - <Button> - {status === 'pending' ? ( - <Icon data={Copy} /> - ) : ( - <Icon data={CopyCheck} /> - )} - Copy - </Button> - )} - </CopyToClipboard> - <Button onClick={() => setIsOpen(true)}> - <Icon data={ArrowDownToSquare} /> - Import - </Button> - </div> - + <div className={b('title')}>{format}</div> <div className={b('code')}>{text}</div> - - <Dialog onClose={() => setIsOpen(false)} open={isOpen} size={'l'}> - <Dialog.Header caption="New configuration" /> - <Dialog.Body> - <Alert - className={b('alert')} - theme={'info'} - title={'You can use YAML or JSON'} - message={'The editor will automatically understand which format is needed.'} - ></Alert> - <TextArea - className={b('textarea')} - value={tempConfig} - onUpdate={setTempConfig} - rows={25} - /> - </Dialog.Body> - <Dialog.Footer - showError={false} - listenKeyEnter={true} - preset={'default'} - textButtonApply={'Apply'} - textButtonCancel={'Cancel'} - onClickButtonApply={onUpdate} - onClickButtonCancel={() => setIsOpen(false)} - /> - </Dialog> </div> ); }; diff --git a/editor-v2/containers/ViewSwitches/ViewSwitches.tsx b/editor-v2/containers/ViewSwitches/ViewSwitches.tsx index fd01be7e09..f04ce59d8c 100644 --- a/editor-v2/containers/ViewSwitches/ViewSwitches.tsx +++ b/editor-v2/containers/ViewSwitches/ViewSwitches.tsx @@ -14,7 +14,7 @@ const ViewSwitches = () => { const {zoom, setZoom, decreaseZoom, increaseZoom} = useMainEditorStore(); return ( <div className={b()}> - <div>TBD screen sizes</div> + <div>TBD</div> <div className={b('zoom')}> <Button view={'flat'} onClick={decreaseZoom}> <Icon data={Minus} /> diff --git a/editor-v2/hooks/useEditorTabs.tsx b/editor-v2/hooks/useEditorTabs.tsx index aaa6baf982..6dbc5b65b1 100644 --- a/editor-v2/hooks/useEditorTabs.tsx +++ b/editor-v2/hooks/useEditorTabs.tsx @@ -31,9 +31,14 @@ export const useEditorTabs = ({ component: Tree, }, { - id: 'source-code', - title: 'SourceCode', - component: SourceCode, + id: 'source-code-yaml', + title: 'YAML', + component: () => <SourceCode format="yaml" />, + }, + { + id: 'source-code-json', + title: 'JSON', + component: () => <SourceCode format="json" />, }, ]} /> @@ -54,9 +59,14 @@ export const useEditorTabs = ({ position: 'right', }, { - id: 'source-code', - title: 'SourceCode', - component: SourceCode, + id: 'source-code-yaml', + title: 'YAML', + component: () => <SourceCode format="yaml" />, + }, + { + id: 'source-code-json', + title: 'JSON', + component: () => <SourceCode format="json" />, }, ...rightTabs, ], diff --git a/editor-v2/store.ts b/editor-v2/store.ts index 18a882223d..5e5ef91963 100644 --- a/editor-v2/store.ts +++ b/editor-v2/store.ts @@ -19,7 +19,7 @@ import { export interface EditorMethods { initialize(): void; - setSelectedBlock(path: number[]): void; + setSelectedBlock(path?: number[]): void; setHeight(height: number): void; setZoom(zoom: number): void; increaseZoom(): void; diff --git a/editor-v2/styles/root.scss b/editor-v2/styles/root.scss index 25fa51c5a8..93d3ef75a4 100644 --- a/editor-v2/styles/root.scss +++ b/editor-v2/styles/root.scss @@ -1,2 +1,7 @@ -body { +.g-root { + --g-color-base-brand: var(--g-color-text-primary); + --g-color-base-brand-hover: var(--g-color-text-complementary); + --g-color-text-brand-contrast: var(--g-color-text-light-primary); + --g-color-line-brand: var(--g-color-text-primary); + --g-color-text-brand: var(--g-color-private-brand-700-solid); } From 336a7d5350db30971043491c3939d8e6d2aea3f9 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 10 Mar 2025 00:16:29 +0300 Subject: [PATCH 012/118] feat: tree selected block style --- editor-v2/containers/Tree/Tree.scss | 4 + editor-v2/containers/Tree/Tree.tsx | 45 ++++++++-- editor-v2/hooks/useEditorTabs.tsx | 125 +++++++++++++++------------- playground/src/app/page.tsx | 24 +++--- 4 files changed, 119 insertions(+), 79 deletions(-) diff --git a/editor-v2/containers/Tree/Tree.scss b/editor-v2/containers/Tree/Tree.scss index ca1b5f5385..d4b28e8dae 100644 --- a/editor-v2/containers/Tree/Tree.scss +++ b/editor-v2/containers/Tree/Tree.scss @@ -22,6 +22,10 @@ $block: '.#{$ns}tree'; padding: 8px; margin-bottom: 8px; + &_selected { + border: 1.5px var(--g-color-line-brand) solid; + } + &:last-child { margin-bottom: 0; } diff --git a/editor-v2/containers/Tree/Tree.tsx b/editor-v2/containers/Tree/Tree.tsx index b789161104..8e192d0681 100644 --- a/editor-v2/containers/Tree/Tree.tsx +++ b/editor-v2/containers/Tree/Tree.tsx @@ -1,9 +1,10 @@ import {TrashBin} from '@gravity-ui/icons'; import {Button, Card, Icon} from '@gravity-ui/uikit'; -import React, {PropsWithChildren} from 'react'; +import React, {PropsWithChildren, useMemo} from 'react'; import {ItemConfig} from '../../../common/types'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; +import {generateChildrenPathFromArray} from '../../utils'; import {editorCn} from '../../utils/cn'; import './Tree.scss'; @@ -19,6 +20,11 @@ type TreeItem = { children?: TreeItem[]; }; +interface ItemOptions { + deepLevel?: number; + parentIndex?: number; +} + const generateTree = (items: TreeItem[]): TreeItem[] => { return items.map((item) => { let children; @@ -35,17 +41,38 @@ const generateTree = (items: TreeItem[]): TreeItem[] => { }; const Tree = (_p: PropsWithChildren<TreeProps>) => { - const {content, resetBlocks} = useMainEditorStore(); + const {content, resetBlocks, selectedBlock} = useMainEditorStore(); + + const selectedBlockPath = useMemo(() => { + return generateChildrenPathFromArray(selectedBlock || []); + }, [selectedBlock]); const blockTree = generateTree(content.blocks); - const renderTree = (items: TreeItem[], deepLevel = 0) => { - return items.map(({type, children}, index) => ( - <React.Fragment key={index}> - <Card className={b('item', {deep: deepLevel})}>{type}</Card> - {children && renderTree(children, deepLevel + 1)} - </React.Fragment> - )); + const renderTree = (items: TreeItem[], {deepLevel = 0, parentIndex}: ItemOptions = {}) => { + return items.map(({type, children}, index) => { + let blockPathArray; + if (parentIndex) { + blockPathArray = [parentIndex, index]; + } else { + blockPathArray = [index]; + } + const blockPath = generateChildrenPathFromArray(blockPathArray); + return ( + <React.Fragment key={index}> + <Card + className={b('item', { + deep: deepLevel, + selected: blockPath === selectedBlockPath, + })} + > + {type} + </Card> + {children && + renderTree(children, {deepLevel: deepLevel + 1, parentIndex: index})} + </React.Fragment> + ); + }); }; return ( diff --git a/editor-v2/hooks/useEditorTabs.tsx b/editor-v2/hooks/useEditorTabs.tsx index 6dbc5b65b1..ee861e99a8 100644 --- a/editor-v2/hooks/useEditorTabs.tsx +++ b/editor-v2/hooks/useEditorTabs.tsx @@ -1,3 +1,5 @@ +import {useMemo} from 'react'; + import Tabs, {TabsItemProps} from '../components/Tabs/Tabs'; import BlockConfig from '../containers/BlockConfig/BlockConfig'; import BlocksList from '../containers/BlocksList/BlocksList'; @@ -12,63 +14,68 @@ export const useEditorTabs = ({ leftTabs?: TabsItemProps[]; rightTabs?: TabsItemProps[]; }) => { - return { - left: [ - { - id: 'page', - title: 'Page', - component: () => ( - <Tabs - items={[ - { - id: 'blocks-list', - title: 'Blocks', - component: BlocksList, - }, - { - id: 'tree', - title: 'Tree', - component: Tree, - }, - { - id: 'source-code-yaml', - title: 'YAML', - component: () => <SourceCode format="yaml" />, - }, - { - id: 'source-code-json', - title: 'JSON', - component: () => <SourceCode format="json" />, - }, - ]} - /> - ), - }, - { - id: 'global-config', - title: 'GlobalConfig', - component: GlobalConfig, - }, - ...leftTabs, - ], - right: [ - { - id: 'block-config', - title: 'BlockConfig', - component: BlockConfig, - position: 'right', - }, - { - id: 'source-code-yaml', - title: 'YAML', - component: () => <SourceCode format="yaml" />, - }, - { - id: 'source-code-json', - title: 'JSON', - component: () => <SourceCode format="json" />, - }, - ...rightTabs, - ], - }; + const tabs = useMemo( + () => ({ + left: [ + { + id: 'page', + title: 'Page', + component: () => ( + <Tabs + items={[ + { + id: 'blocks-list', + title: 'Blocks', + component: BlocksList, + }, + { + id: 'tree', + title: 'Tree', + component: Tree, + }, + { + id: 'source-code-yaml', + title: 'YAML', + component: () => <SourceCode format="yaml" />, + }, + { + id: 'source-code-json', + title: 'JSON', + component: () => <SourceCode format="json" />, + }, + ]} + /> + ), + }, + { + id: 'global-config', + title: 'GlobalConfig', + component: GlobalConfig, + }, + ...leftTabs, + ], + right: [ + { + id: 'block-config', + title: 'BlockConfig', + component: BlockConfig, + position: 'right', + }, + { + id: 'source-code-yaml', + title: 'YAML', + component: () => <SourceCode format="yaml" />, + }, + { + id: 'source-code-json', + title: 'JSON', + component: () => <SourceCode format="json" />, + }, + ...rightTabs, + ], + }), + [], + ); + + return tabs; }; diff --git a/playground/src/app/page.tsx b/playground/src/app/page.tsx index b817c38933..8886fbcdd1 100644 --- a/playground/src/app/page.tsx +++ b/playground/src/app/page.tsx @@ -13,6 +13,18 @@ const b = block('home'); const Test = () => <div className={b('test')}>custom test</div>; +export const COMPONENTS_CONFIG = { + middleTop: Test, + leftTabs: [ + { + id: 'test', + title: 'TEST', + component: Test, + }, + ], + rightTop: [Source, ViewSwitches], +}; + export default function Home() { const [initialUrl, setInitialUrl] = useState<string>(''); @@ -27,17 +39,7 @@ export default function Home() { <div className={b()}> {initialUrl && ( <Editor - componentsConfig={{ - middleTop: Test, - leftTabs: [ - { - id: 'test', - title: 'TEST', - component: Test, - }, - ], - rightTop: [Source, ViewSwitches], - }} + componentsConfig={COMPONENTS_CONFIG} initialUrl={initialUrl} disableUrlField={false} onUpdate={() => {}} From c60d470e652c97644998441320250a24cdd0a9e7 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 10 Mar 2025 15:39:40 +0300 Subject: [PATCH 013/118] feat: editor global config navigation --- common/store.ts | 4 ++-- common/types/actions.ts | 4 ++-- common/types/forms.ts | 4 ++-- editor-v2/components/DynamicForm/DynamicForm.tsx | 3 +-- .../components/DynamicForm/Fields/Array/Array.tsx | 3 +-- .../components/DynamicForm/Fields/Object/Object.tsx | 3 +-- .../components/DynamicForm/Fields/OneOf/OneOf.tsx | 3 +-- editor-v2/components/DynamicForm/utils.ts | 4 ++-- editor-v2/containers/Editor/Editor.tsx | 4 ++-- editor-v2/containers/GlobalConfig/GlobalConfig.tsx | 8 ++++++-- editor-v2/store.ts | 4 ++-- src/containers/PageConstructor/PageConstructor.tsx | 8 +++++--- src/hooks/usePCEditorInitializeEvents.ts | 6 +++--- src/models/constructor.ts | 12 +++++++++++- src/models/navigation.ts | 4 ++-- .../DesktopNavigation/DesktopNavigation.tsx | 4 ++-- src/navigation/components/Navigation/Navigation.tsx | 4 ++-- src/navigation/containers/Layout/Layout.tsx | 3 ++- src/navigation/models.ts | 2 +- src/utils/form-generator.ts | 2 +- src/utils/index.ts | 1 + src/utils/navigation.ts | 12 ++++++++++++ 22 files changed, 64 insertions(+), 38 deletions(-) create mode 100644 src/utils/navigation.ts diff --git a/common/store.ts b/common/store.ts index a011799227..820ee28115 100644 --- a/common/store.ts +++ b/common/store.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {PageContent} from '../src/models'; +import {Content} from '../src/models'; import {ConfigInput, ItemConfig} from './types'; import {initializeStore} from './utils'; @@ -13,7 +13,7 @@ export interface EditorState { selectedBlock?: number[]; initialized: boolean; - content: PageContent; + content: Content; blocks: Array<ItemConfig>; subBlocks: Array<ItemConfig>; global: Array<ConfigInput>; diff --git a/common/types/actions.ts b/common/types/actions.ts index c0add108ac..16114a740b 100644 --- a/common/types/actions.ts +++ b/common/types/actions.ts @@ -1,4 +1,4 @@ -import {PageContent} from '../../src/models'; +import {Content} from '../../src/models'; import {EditorState} from '../store'; export type MessageTypes = EventMessageTypes & ActionMessageTypes; @@ -12,7 +12,7 @@ export type EventMessageTypes = { ON_CLICK_BLOCK: {path: number[]; rect: DOMRect}; ON_RESIZE_BLOCK: {rect: DOMRect}; ON_SUPPORTED_BLOCKS: Pick<EditorState, 'blocks' | 'subBlocks' | 'global'>; - ON_INITIAL_CONTENT: PageContent; + ON_INITIAL_CONTENT: Content; }; export type ActionMessageTypes = { diff --git a/common/types/forms.ts b/common/types/forms.ts index f4e21a7571..d6377b3564 100644 --- a/common/types/forms.ts +++ b/common/types/forms.ts @@ -1,6 +1,6 @@ -import {PageContent} from '../../src/models'; +import {Content} from '../../src/models'; -export type DynamicFormValue = string | number | [] | object | boolean | PageContent | undefined; +export type DynamicFormValue = string | number | [] | object | boolean | Content | undefined; export interface BlockConfig { name: string; diff --git a/editor-v2/components/DynamicForm/DynamicForm.tsx b/editor-v2/components/DynamicForm/DynamicForm.tsx index 2051840ed6..3ed3cf233a 100644 --- a/editor-v2/components/DynamicForm/DynamicForm.tsx +++ b/editor-v2/components/DynamicForm/DynamicForm.tsx @@ -2,7 +2,6 @@ import _ from 'lodash'; import React, {useCallback} from 'react'; import {ConfigInput, DynamicFormValue} from '../../../common/types'; -import {PageContent} from '../../../src/models'; import {editorCn} from '../../utils/cn'; import './DynamicForm.scss'; @@ -20,7 +19,7 @@ const b = editorCn('dynamic-form'); interface DynamicFormProps { blockConfig: Array<ConfigInput>; - contentConfig: PageContent; + contentConfig?: DynamicFormValue; onUpdate: (key: string, value: DynamicFormValue) => void; className?: string; } diff --git a/editor-v2/components/DynamicForm/Fields/Array/Array.tsx b/editor-v2/components/DynamicForm/Fields/Array/Array.tsx index 3342ab3692..25dba84c7f 100644 --- a/editor-v2/components/DynamicForm/Fields/Array/Array.tsx +++ b/editor-v2/components/DynamicForm/Fields/Array/Array.tsx @@ -3,7 +3,6 @@ import {Button, Card, Icon} from '@gravity-ui/uikit'; import React, {useCallback} from 'react'; import {ArrayObjectInput, ArrayTextInput, DynamicFormValue} from '../../../../../common/types'; -import {PageContent} from '../../../../../src/models'; import {removeFromArray, swapArrayItems} from '../../../../utils'; import {editorCn} from '../../../../utils/cn'; import DynamicForm from '../../DynamicForm'; @@ -97,7 +96,7 @@ const ArrayDynamicField = ({title, values, onUpdate, className, blockConfig}: Ar {arrayItemButton} </div> <DynamicForm - contentConfig={value as PageContent} + contentConfig={value} blockConfig={blockConfig.properties} onUpdate={(key, updateValue) => onUpdate(`[${index}].${key}`, updateValue) diff --git a/editor-v2/components/DynamicForm/Fields/Object/Object.tsx b/editor-v2/components/DynamicForm/Fields/Object/Object.tsx index b8b67f7806..c5df74f106 100644 --- a/editor-v2/components/DynamicForm/Fields/Object/Object.tsx +++ b/editor-v2/components/DynamicForm/Fields/Object/Object.tsx @@ -2,7 +2,6 @@ import {Card} from '@gravity-ui/uikit'; import React from 'react'; import {ConfigInput, DynamicFormValue} from '../../../../../common/types'; -import {PageContent} from '../../../../../src/models'; import {editorCn} from '../../../../utils/cn'; import DynamicForm from '../../DynamicForm'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; @@ -12,7 +11,7 @@ import './Object.scss'; const b = editorCn('object-dynamic-field'); interface ObjectDynamicFieldProps extends FieldBaseParams { - value: PageContent; + value: DynamicFormValue; onUpdate: (key: string, value: DynamicFormValue) => void; blockConfig: Array<ConfigInput>; className?: string; diff --git a/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx b/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx index eda4874f68..48f34e68ee 100644 --- a/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx +++ b/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx @@ -2,7 +2,6 @@ import {Card, RadioButton} from '@gravity-ui/uikit'; import React, {useCallback, useMemo, useState} from 'react'; import {DynamicFormValue, OneOfInput} from '../../../../../common/types'; -import {PageContent} from '../../../../../src/models'; import {editorCn} from '../../../../utils/cn'; import DynamicForm from '../../DynamicForm'; import FieldBase from '../../FieldBase/FieldBase'; @@ -12,7 +11,7 @@ import './OneOf.scss'; const b = editorCn('oneof-dynamic-field'); interface OneOfDynamicFieldProps { - contentConfig: PageContent; + contentConfig: DynamicFormValue; onUpdate: (key: string, value: DynamicFormValue) => void; inputConfig: OneOfInput; className?: string; diff --git a/editor-v2/components/DynamicForm/utils.ts b/editor-v2/components/DynamicForm/utils.ts index 8a83dc1cb3..5ad3f80b7c 100644 --- a/editor-v2/components/DynamicForm/utils.ts +++ b/editor-v2/components/DynamicForm/utils.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {PageContent} from '../../../src/models'; +import {DynamicFormValue} from '../../../common/types'; export const getFullPath = (path: string, name: string) => { if (!path && !name) { @@ -18,6 +18,6 @@ export const getFullPath = (path: string, name: string) => { return path + '.' + name; }; -export const getContent = (contentConfig: PageContent, path: string) => { +export const getContent = (contentConfig: DynamicFormValue, path: string) => { return path ? _.get(contentConfig, path) : contentConfig; }; diff --git a/editor-v2/containers/Editor/Editor.tsx b/editor-v2/containers/Editor/Editor.tsx index 83eeee2217..7bc9611cc7 100644 --- a/editor-v2/containers/Editor/Editor.tsx +++ b/editor-v2/containers/Editor/Editor.tsx @@ -1,6 +1,6 @@ import React, {ElementType, useCallback} from 'react'; -import {PageContent} from '../../../src/models'; +import {Content} from '../../../src/models'; import BigOverlay from '../../components/BigOverlay/BigOverlay'; import MiddleScreen from '../../components/MiddleScreen/MiddleScreen'; import {Panels} from '../../components/Panels/Panels'; @@ -23,7 +23,7 @@ interface SidebarTabComponent { component: ElementType; } interface EditorViewProps { - onUpdate?: (pageContent: PageContent) => void; + onUpdate?: (pageContent: Content) => void; initialUrl: string; disableUrlField?: boolean; componentsConfig?: { diff --git a/editor-v2/containers/GlobalConfig/GlobalConfig.tsx b/editor-v2/containers/GlobalConfig/GlobalConfig.tsx index 0a3d2eb484..f77241a438 100644 --- a/editor-v2/containers/GlobalConfig/GlobalConfig.tsx +++ b/editor-v2/containers/GlobalConfig/GlobalConfig.tsx @@ -17,13 +17,17 @@ const GlobalConfig = ({className}: GlobalConfigProps) => { const {global, content, updateField} = useMainEditorStore(); const onUpdate = (key: string, value: DynamicFormValue) => { - updateField(key, value); + updateField('navigation.' + key, value); }; return ( <div className={b(null, className)}> <div className={b('title')}>Global Config</div> - <DynamicForm contentConfig={content} blockConfig={global} onUpdate={onUpdate} /> + <DynamicForm + contentConfig={content.navigation} + blockConfig={global} + onUpdate={onUpdate} + /> </div> ); }; diff --git a/editor-v2/store.ts b/editor-v2/store.ts index 5e5ef91963..48c3aaf91d 100644 --- a/editor-v2/store.ts +++ b/editor-v2/store.ts @@ -3,7 +3,7 @@ import _ from 'lodash'; import {EditorState, initialStore} from '../common/store'; import {DynamicFormValue} from '../common/types'; import {initializeStore} from '../common/utils'; -import {ConstructorBlock, PageContent} from '../src/models'; +import {ConstructorBlock, Content} from '../src/models'; import {ZOOM_STEPS} from './constants'; import { @@ -25,7 +25,7 @@ export interface EditorMethods { increaseZoom(): void; decreaseZoom(): void; setConfig(data: Pick<EditorState, 'blocks' | 'subBlocks' | 'global'>): void; - setContent(data: PageContent): void; + setContent(data: Content): void; insertBlock(path: number[], blockType: string, position?: 'prepend' | 'append'): void; enableInsertMode(blockType: string): void; enableReorderMode(path: number[]): void; diff --git a/src/containers/PageConstructor/PageConstructor.tsx b/src/containers/PageConstructor/PageConstructor.tsx index e0442e2da8..58412bec76 100644 --- a/src/containers/PageConstructor/PageConstructor.tsx +++ b/src/containers/PageConstructor/PageConstructor.tsx @@ -15,6 +15,7 @@ import {usePCEditorInitializeEvents} from '../../hooks/usePCEditorInitializeEven import {usePCEditorStore} from '../../hooks/usePCEditorStore'; import { BlockTypes, + Content, CustomConfig, CustomItems, HeaderBlockTypes, @@ -61,13 +62,14 @@ export const Constructor = (props: PageConstructorProps) => { microdata, } = props; - const [content, setContent] = useState<PageContent>({blocks, background}); + const [content, setContent] = useState<Content>({blocks, background, navigation}); + const theme = useTheme(); const store = usePCEditorStore(); const {initialized} = store; - usePCEditorInitializeEvents({initialContent: {blocks, background}, setContent}); + usePCEditorInitializeEvents({initialContent: {blocks, background, navigation}, setContent}); const {context} = useMemo( () => ({ @@ -109,7 +111,7 @@ export const Constructor = (props: PageConstructorProps) => { {themedBackground && ( <BackgroundMedia {...themedBackground} className={b('background')} /> )} - <Layout navigation={navigation}> + <Layout navigation={content.navigation}> {renderMenu && renderMenu()} {restBlocks && ( <ConstructorRow> diff --git a/src/hooks/usePCEditorInitializeEvents.ts b/src/hooks/usePCEditorInitializeEvents.ts index 3053b23317..9dec24459c 100644 --- a/src/hooks/usePCEditorInitializeEvents.ts +++ b/src/hooks/usePCEditorInitializeEvents.ts @@ -5,7 +5,7 @@ import _ from 'lodash'; import {ItemConfig} from '../../common/types'; import {blockDataMap} from '../constructor-items'; -import {PageContent} from '../models'; +import {Content} from '../models'; import {defaultComponentsConfigurationSchema} from '../schema'; import {generateFromAJV} from '../utils/form-generator'; @@ -13,8 +13,8 @@ import {usePCEditorStore} from './usePCEditorStore'; import {sendEventPostMessage, useInternalPostMessageAPIListener} from './usePostMessageAPI'; interface UseEditorInitializeProps { - initialContent: PageContent; - setContent: (content: PageContent) => void; + initialContent: Content; + setContent: (content: Content) => void; } export const usePCEditorInitializeEvents = ({ diff --git a/src/models/constructor.ts b/src/models/constructor.ts index 7861007276..ed7b0068c9 100644 --- a/src/models/constructor.ts +++ b/src/models/constructor.ts @@ -1,6 +1,12 @@ import * as React from 'react'; -import {Animatable, BlockDecorationProps, ConstructorItem, ThemedMediaProps} from './'; +import { + Animatable, + BlockDecorationProps, + ConstructorItem, + NavigationData, + ThemedMediaProps, +} from './'; export interface PageData { content: PageContent; @@ -18,6 +24,10 @@ export interface PageContent extends Animatable { background?: ThemedMediaProps; } +export interface Content extends PageContent { + navigation?: NavigationData; +} + export interface InitConstrucorState { hasMenu: boolean; } diff --git a/src/models/navigation.ts b/src/models/navigation.ts index 72f51da19b..613855bf71 100644 --- a/src/models/navigation.ts +++ b/src/models/navigation.ts @@ -121,8 +121,8 @@ export interface FooterData { } export interface NavigationData { - logo: ThemedNavigationLogoData; - header: HeaderData; + logo?: ThemedNavigationLogoData; + header?: HeaderData; footer?: FooterData; renderNavigation?: () => React.ReactNode; } diff --git a/src/navigation/components/DesktopNavigation/DesktopNavigation.tsx b/src/navigation/components/DesktopNavigation/DesktopNavigation.tsx index ccc88837d3..1c2c040f86 100644 --- a/src/navigation/components/DesktopNavigation/DesktopNavigation.tsx +++ b/src/navigation/components/DesktopNavigation/DesktopNavigation.tsx @@ -1,5 +1,5 @@ import OverflowScroller from '../../../components/OverflowScroller/OverflowScroller'; -import {block} from '../../../utils'; +import {block, isLogoSet} from '../../../utils'; import {DesktopNavigationProps, ItemColumnName, NavigationLayout} from '../../models'; import Logo from '../Logo/Logo'; import {MobileMenuButton} from '../MobileMenuButton/MobileMenuButton'; @@ -20,7 +20,7 @@ export const DesktopNavigation = ({ activeItemId, }: DesktopNavigationProps) => ( <div className={b('wrapper')}> - {logo && ( + {isLogoSet(logo) && ( <div className={b('left')}> <Logo {...logo} className={b('logo')} /> </div> diff --git a/src/navigation/components/Navigation/Navigation.tsx b/src/navigation/components/Navigation/Navigation.tsx index 6116f80ac5..4fadc7c227 100644 --- a/src/navigation/components/Navigation/Navigation.tsx +++ b/src/navigation/components/Navigation/Navigation.tsx @@ -27,13 +27,13 @@ export const Navigation = ({ onSidebarChange, }: NavigationComponentProps) => { const { - leftItems, + leftItems = [], rightItems, customMobileHeaderItems, iconSize = 20, withBorder = false, withBorderOnScroll = true, - } = data; + } = data || {}; const [isSidebarOpened, setIsSidebarOpened] = React.useState(false); const [showBorder] = useShowBorder(withBorder, withBorderOnScroll); diff --git a/src/navigation/containers/Layout/Layout.tsx b/src/navigation/containers/Layout/Layout.tsx index 03a7c19fdc..a120723e3c 100644 --- a/src/navigation/containers/Layout/Layout.tsx +++ b/src/navigation/containers/Layout/Layout.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import {NavigationData} from '../../../models'; -import {block} from '../../../utils'; +import {block, isHeaderSet, isLogoSet} from '../../../utils'; import Navigation from '../../components/Navigation/Navigation'; import './Layout.scss'; @@ -16,6 +16,7 @@ export interface LayoutProps { const Layout = ({children, navigation}: React.PropsWithChildren<LayoutProps>) => ( <div className={b()}> {navigation && + (isLogoSet(navigation.logo) || isHeaderSet(navigation.header)) && (navigation.renderNavigation ? ( navigation.renderNavigation() ) : ( diff --git a/src/navigation/models.ts b/src/navigation/models.ts index 010db51bee..b6cc530bdc 100644 --- a/src/navigation/models.ts +++ b/src/navigation/models.ts @@ -63,7 +63,7 @@ export interface ItemsWrapperProps ClassNameProps {} export interface DesktopNavigationProps extends MobileMenuButtonProps, ActiveItemProps { - logo: ThemedNavigationLogoData; + logo?: ThemedNavigationLogoData; leftItemsWithIconSize: NavigationItemModel[]; rightItemsWithIconSize?: NavigationItemModel[]; customMobileHeaderItems?: NavigationItemModel[]; diff --git a/src/utils/form-generator.ts b/src/utils/form-generator.ts index 9c8af8ca01..03d4ee6c7b 100644 --- a/src/utils/form-generator.ts +++ b/src/utils/form-generator.ts @@ -9,7 +9,7 @@ import { SelectBaseInput, TextAreaInput, TextInput, -} from '../common/types'; +} from '../../common/types'; export const generateFromAJV = (schema: JSONSchemaType<{}>): ConfigInput[] => { if (schema && schema.properties) { diff --git a/src/utils/index.ts b/src/utils/index.ts index 13dca7be8b..8d4963cbad 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -7,6 +7,7 @@ export * from './cn'; export * from './url'; export * from './theme'; export * from './icons'; +export * from './navigation'; export type {HubspotEventData, HubspotEventHandlers, HubspotEventName} from './hubspot'; export {isHubspotEventData} from './hubspot'; diff --git a/src/utils/navigation.ts b/src/utils/navigation.ts new file mode 100644 index 0000000000..080f950738 --- /dev/null +++ b/src/utils/navigation.ts @@ -0,0 +1,12 @@ +import _get from 'lodash/get'; +import _isEmpty from 'lodash/isEmpty'; + +import {HeaderData, ThemedNavigationLogoData} from '../models'; + +export function isLogoSet(logo?: ThemedNavigationLogoData): logo is ThemedNavigationLogoData { + return Boolean(_get(logo, 'icon') || _get(logo, 'light.icon')); +} + +export function isHeaderSet(header?: HeaderData): header is HeaderData { + return !_isEmpty(header?.leftItems); +} From e47cf3062fc7bbbcd46e0cb27ebfa504707f7a15 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 10 Mar 2025 16:42:01 +0300 Subject: [PATCH 014/118] feat: editor tree tab --- editor-v2/containers/Tree/Tree.scss | 26 ++++++++------------------ editor-v2/containers/Tree/Tree.tsx | 22 +++++++++++++--------- editor-v2/hooks/useEditorTabs.tsx | 14 +++++++------- editor-v2/utils/index.ts | 9 +++++++++ 4 files changed, 37 insertions(+), 34 deletions(-) diff --git a/editor-v2/containers/Tree/Tree.scss b/editor-v2/containers/Tree/Tree.scss index d4b28e8dae..40ba330649 100644 --- a/editor-v2/containers/Tree/Tree.scss +++ b/editor-v2/containers/Tree/Tree.scss @@ -14,8 +14,12 @@ $block: '.#{$ns}tree'; justify-content: space-between; } + &__type { + color: var(--g-color-text-dark-secondary); + } + &__title { - @include text-subheader-3; + @include overflow-ellipsis(); } &__item { @@ -29,23 +33,9 @@ $block: '.#{$ns}tree'; &:last-child { margin-bottom: 0; } + } - @for $i from 1 through 9 { - &_deep_#{$i} { - position: relative; - margin-left: 16px * $i; - width: calc(100% - 16px * $i); - - &:before { - content: ''; - position: absolute; - right: calc(100% + 12px); - height: 100%; - width: 2px; - background-color: var(--g-color-line-generic-accent); - top: 0; - } - } - } + &__children { + margin-top: 8px; } } diff --git a/editor-v2/containers/Tree/Tree.tsx b/editor-v2/containers/Tree/Tree.tsx index 8e192d0681..3f7eb7341d 100644 --- a/editor-v2/containers/Tree/Tree.tsx +++ b/editor-v2/containers/Tree/Tree.tsx @@ -4,7 +4,7 @@ import React, {PropsWithChildren, useMemo} from 'react'; import {ItemConfig} from '../../../common/types'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; -import {generateChildrenPathFromArray} from '../../utils'; +import {generateChildrenPathFromArray, getItemTitle} from '../../utils'; import {editorCn} from '../../utils/cn'; import './Tree.scss'; @@ -18,6 +18,7 @@ export interface TreeProps { type TreeItem = { type: string; children?: TreeItem[]; + treeTitle?: string; }; interface ItemOptions { @@ -36,6 +37,7 @@ const generateTree = (items: TreeItem[]): TreeItem[] => { return { type: item.type, children, + treeTitle: getItemTitle(item), }; }); }; @@ -49,9 +51,9 @@ const Tree = (_p: PropsWithChildren<TreeProps>) => { const blockTree = generateTree(content.blocks); - const renderTree = (items: TreeItem[], {deepLevel = 0, parentIndex}: ItemOptions = {}) => { - return items.map(({type, children}, index) => { - let blockPathArray; + const renderTree = (items: TreeItem[], {parentIndex}: ItemOptions = {}) => { + return items.map(({type, children, treeTitle}, index) => { + let blockPathArray: number[]; if (parentIndex) { blockPathArray = [parentIndex, index]; } else { @@ -62,14 +64,17 @@ const Tree = (_p: PropsWithChildren<TreeProps>) => { <React.Fragment key={index}> <Card className={b('item', { - deep: deepLevel, selected: blockPath === selectedBlockPath, })} > - {type} + <div className={b('type')}>{type}</div> + <div className={b('title')}>{treeTitle}</div> + {children && ( + <div className={b('children')}> + {renderTree(children, {parentIndex: index})} + </div> + )} </Card> - {children && - renderTree(children, {deepLevel: deepLevel + 1, parentIndex: index})} </React.Fragment> ); }); @@ -78,7 +83,6 @@ const Tree = (_p: PropsWithChildren<TreeProps>) => { return ( <div className={b()}> <div className={b('head')}> - <div className={b('title')}>Tree</div> <div className={b('actions')}> <Button view="outlined-danger" onClick={() => resetBlocks()}> <Icon data={TrashBin} /> diff --git a/editor-v2/hooks/useEditorTabs.tsx b/editor-v2/hooks/useEditorTabs.tsx index ee861e99a8..2716fa93a7 100644 --- a/editor-v2/hooks/useEditorTabs.tsx +++ b/editor-v2/hooks/useEditorTabs.tsx @@ -19,18 +19,18 @@ export const useEditorTabs = ({ left: [ { id: 'page', - title: 'Page', + title: 'PAGE', component: () => ( <Tabs items={[ { id: 'blocks-list', - title: 'Blocks', + title: 'BLOCKS', component: BlocksList, }, { - id: 'tree', - title: 'Tree', + id: 'layers', + title: 'LAYERS', component: Tree, }, { @@ -48,8 +48,8 @@ export const useEditorTabs = ({ ), }, { - id: 'global-config', - title: 'GlobalConfig', + id: 'navigation', + title: 'NAVIGATION', component: GlobalConfig, }, ...leftTabs, @@ -57,7 +57,7 @@ export const useEditorTabs = ({ right: [ { id: 'block-config', - title: 'BlockConfig', + title: 'INPUTS', component: BlockConfig, position: 'right', }, diff --git a/editor-v2/utils/index.ts b/editor-v2/utils/index.ts index 8bb7e96fd1..9b416b484f 100644 --- a/editor-v2/utils/index.ts +++ b/editor-v2/utils/index.ts @@ -166,3 +166,12 @@ export const getUrlOrigin = (url: string) => { return undefined; } }; + +export const getItemTitle = (item: object): string | undefined => { + return ( + _.get(item, 'title.text') || + _.get(item, 'title') || + _.get(item, 'textContent.title') || + _.get(item, 'content.title') + ); +}; From b9910d252159468a205f247dec877d50721cdc97 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 10 Mar 2025 19:05:07 +0300 Subject: [PATCH 015/118] fix: editor blocks previews --- common/types/forms.ts | 1 + .../containers/BlocksList/BlocksList.scss | 42 +++++++------- .../containers/BlocksList/BlocksList.tsx | 56 +++++++++---------- src/blocks/CardLayout/index.ts | 1 + src/blocks/Header/index.ts | 1 + src/blocks/Media/index.ts | 1 + src/blocks/Slider/index.ts | 1 + src/constructor-items.ts | 2 +- 8 files changed, 50 insertions(+), 55 deletions(-) diff --git a/common/types/forms.ts b/common/types/forms.ts index d6377b3564..230d29f3b7 100644 --- a/common/types/forms.ts +++ b/common/types/forms.ts @@ -7,6 +7,7 @@ export interface BlockConfig { inputs: Array<ConfigInput>; group?: string; default?: object; + previewImg?: string; } export interface TextInput { diff --git a/editor-v2/containers/BlocksList/BlocksList.scss b/editor-v2/containers/BlocksList/BlocksList.scss index c2a8cfe100..6204e9b466 100644 --- a/editor-v2/containers/BlocksList/BlocksList.scss +++ b/editor-v2/containers/BlocksList/BlocksList.scss @@ -17,14 +17,12 @@ $block: '.#{$ns}blocks-list'; margin-bottom: 8px; display: flex; + flex-direction: column; align-items: center; justify-content: space-between; + text-align: center; gap: 4px; - &:last-child { - margin-bottom: 0; - } - &:active { background-color: var(--g-color-base-generic-hover); cursor: grabbing; @@ -32,38 +30,36 @@ $block: '.#{$ns}blocks-list'; } &__name { - flex: 1; + justify-self: flex-end; + color: var(--g-color-text-dark-secondary); } &__title { - @include text-subheader-3; + color: var(--g-color-text-dark-secondary); margin-bottom: 10px; - display: flex; - align-items: center; - justify-content: flex-start; } - &__subtitle { - @include text-subheader-2; - margin-bottom: 10px; - display: flex; - align-items: center; - justify-content: flex-start; + &__icon { + color: var(--g-color-text-dark-complementary); } &__group { - margin-bottom: 16px; + padding-bottom: 16px; + margin-top: 16px; + border-bottom: 1px solid var(--g-color-line-generic); - &:last-child { - margin-bottom: 0; + &:first-child { + margin-top: 0; } - } - - &__section { - margin-bottom: 20px; &:last-child { - margin-bottom: 0; + border-bottom: none; + } + + &-items { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; } } } diff --git a/editor-v2/containers/BlocksList/BlocksList.tsx b/editor-v2/containers/BlocksList/BlocksList.tsx index b2aa9b2618..fe6fad1099 100644 --- a/editor-v2/containers/BlocksList/BlocksList.tsx +++ b/editor-v2/containers/BlocksList/BlocksList.tsx @@ -1,5 +1,5 @@ -import {Grip, Plus} from '@gravity-ui/icons'; -import {Button, Card, Icon} from '@gravity-ui/uikit'; +import {SquareBars} from '@gravity-ui/icons'; +import {Card, Icon} from '@gravity-ui/uikit'; import _ from 'lodash'; import React, {PropsWithChildren, useCallback} from 'react'; @@ -20,7 +20,7 @@ interface BlockGroups { } const BlocksList = (_p: PropsWithChildren<BlocksListProps>) => { - const {blocks, selectedBlock, insertBlock, enableInsertMode} = useMainEditorStore(); + const {blocks, enableInsertMode} = useMainEditorStore(); const onMouseDown = useCallback( (blockType: string) => { @@ -29,14 +29,6 @@ const BlocksList = (_p: PropsWithChildren<BlocksListProps>) => { [enableInsertMode], ); - const onAddClick = useCallback( - (type: string) => { - const path = selectedBlock || [0]; - return insertBlock(path, type); - }, - [insertBlock, selectedBlock], - ); - const groups = blocks.reduce<BlockGroups>((acc, currentBlock) => { const group = currentBlock.schema.group; if (group) { @@ -58,27 +50,29 @@ const BlocksList = (_p: PropsWithChildren<BlocksListProps>) => { return ( <div className={b()}> - <div className={b('section')}> - {Object.entries(groups).map(([key, groupBlocks]) => ( - <div className={b('group')} key={key}> - <div> - {groupBlocks.map(({type, schema: {name}}) => ( - <Card - key={type} - className={b('card')} - onMouseDown={() => onMouseDown(type)} - > - <Icon data={Grip} /> - <div className={b('name')}>{name}</div> - <Button onClick={() => onAddClick(type)}> - <Icon data={Plus} /> - </Button> - </Card> - ))} - </div> + {Object.entries(groups).map(([key, groupBlocks]) => ( + <div className={b('group')} key={key}> + <div className={b('title')}>{key}</div> + <div className={b('group-items')}> + {groupBlocks.map(({type, schema: {name, previewImg}}) => ( + <Card + key={type} + className={b('card')} + onMouseDown={() => onMouseDown(type)} + > + <div> + {previewImg ? ( + <img src={previewImg} alt="" /> + ) : ( + <Icon className={b('icon')} size={45} data={SquareBars} /> + )} + </div> + <div className={b('name')}>{name}</div> + </Card> + ))} </div> - ))} - </div> + </div> + ))} </div> ); }; diff --git a/src/blocks/CardLayout/index.ts b/src/blocks/CardLayout/index.ts index 13ce0f2db0..70f9abfcab 100644 --- a/src/blocks/CardLayout/index.ts +++ b/src/blocks/CardLayout/index.ts @@ -184,6 +184,7 @@ const CardLayoutBlockConfig: BlockData = { description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', }, + previewImg: 'https://storage.cloud-preprod.yandex.net/qradle-test/card-layout-block.svg', }, }; diff --git a/src/blocks/Header/index.ts b/src/blocks/Header/index.ts index 7b9e77099f..b303ebedf8 100644 --- a/src/blocks/Header/index.ts +++ b/src/blocks/Header/index.ts @@ -29,6 +29,7 @@ const HeaderBlockConfig = { }, ], }, + previewImg: 'https://storage.cloud-preprod.yandex.net/qradle-test/header-block.svg', }, }; diff --git a/src/blocks/Media/index.ts b/src/blocks/Media/index.ts index f9c0abb94f..2b74b12368 100644 --- a/src/blocks/Media/index.ts +++ b/src/blocks/Media/index.ts @@ -53,6 +53,7 @@ const MediaBlockConfig = { image: 'https://storage.yandexcloud.net/cloud-www-assets/constructor/main/new/media-01-01.jpg', }, }, + previewImg: 'https://storage.cloud-preprod.yandex.net/qradle-test/media-block.svg', }, }; diff --git a/src/blocks/Slider/index.ts b/src/blocks/Slider/index.ts index a809a55f7a..8dad37425c 100644 --- a/src/blocks/Slider/index.ts +++ b/src/blocks/Slider/index.ts @@ -41,6 +41,7 @@ const SliderBlockConfig = { }, ], }, + previewImg: 'https://storage.cloud-preprod.yandex.net/qradle-test/card-layout-block.svg', }, }; diff --git a/src/constructor-items.ts b/src/constructor-items.ts index cea5121b14..588118c1b3 100644 --- a/src/constructor-items.ts +++ b/src/constructor-items.ts @@ -42,7 +42,7 @@ import ShareBlockConfig from './blocks/Share'; import SliderBlockConfig from './blocks/Slider'; import TableBlockConfig from './blocks/Table'; import TabsBlockConfig from './blocks/Tabs'; -import {BlockConfig} from './common/types'; +import {BlockConfig} from '../common/types'; import {BlockType, NavigationItemType, SubBlockType} from './models'; import {GithubButton, NavigationButton, NavigationDropdown, NavigationLink} from './navigation'; import SocialIcon from './navigation/components/SocialIcon/SocialIcon'; From 7a93fb279b73967cded7cde77953d1b0ac58a006 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Tue, 11 Mar 2025 15:37:35 +0300 Subject: [PATCH 016/118] feat: editor tree controls --- editor-v2/containers/Tree/Tree.scss | 40 +++++++++--- editor-v2/containers/Tree/Tree.tsx | 96 +++++++++++++++++++++-------- 2 files changed, 102 insertions(+), 34 deletions(-) diff --git a/editor-v2/containers/Tree/Tree.scss b/editor-v2/containers/Tree/Tree.scss index 40ba330649..666e4f0d2f 100644 --- a/editor-v2/containers/Tree/Tree.scss +++ b/editor-v2/containers/Tree/Tree.scss @@ -14,14 +14,6 @@ $block: '.#{$ns}tree'; justify-content: space-between; } - &__type { - color: var(--g-color-text-dark-secondary); - } - - &__title { - @include overflow-ellipsis(); - } - &__item { padding: 8px; margin-bottom: 8px; @@ -35,6 +27,38 @@ $block: '.#{$ns}tree'; } } + &__main { + display: flex; + flex-direction: row; + align-items: center; + + &:hover { + #{$block}__buttons { + display: flex; + } + } + } + + &__text { + flex: 1 1 auto; + min-width: 1px; + } + + &__buttons { + flex: 0 0 auto; + flex-direction: row; + gap: 4px; + display: none; + } + + &__type { + color: var(--g-color-text-dark-secondary); + } + + &__title { + @include overflow-ellipsis(); + } + &__children { margin-top: 8px; } diff --git a/editor-v2/containers/Tree/Tree.tsx b/editor-v2/containers/Tree/Tree.tsx index 3f7eb7341d..aa3c48f38b 100644 --- a/editor-v2/containers/Tree/Tree.tsx +++ b/editor-v2/containers/Tree/Tree.tsx @@ -1,6 +1,6 @@ -import {TrashBin} from '@gravity-ui/icons'; +import {Copy, TrashBin} from '@gravity-ui/icons'; import {Button, Card, Icon} from '@gravity-ui/uikit'; -import React, {PropsWithChildren, useMemo} from 'react'; +import React, {PropsWithChildren, useCallback, useMemo} from 'react'; import {ItemConfig} from '../../../common/types'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; @@ -21,9 +21,13 @@ type TreeItem = { treeTitle?: string; }; -interface ItemOptions { - deepLevel?: number; - parentIndex?: number; +interface ItemProps { + type: string; + treeTitle?: string; + path: number[]; + selected: boolean; + onCopy(path: number[]): void; + onDelete(path: number[]): void; } const generateTree = (items: TreeItem[]): TreeItem[] => { @@ -42,8 +46,50 @@ const generateTree = (items: TreeItem[]): TreeItem[] => { }); }; +const Item = ({ + type, + children, + treeTitle, + path, + selected, + onCopy, + onDelete, +}: PropsWithChildren<ItemProps>) => { + const handleCopy = useCallback(() => { + onCopy(path); + }, [onCopy, path]); + + const handleDelete = useCallback(() => { + onDelete(path); + }, [onDelete, path]); + + return ( + <Card + className={b('item', { + selected, + })} + > + <div className={b('main')}> + <div className={b('text')}> + <div className={b('type')}>{type}</div> + <div className={b('title')}>{treeTitle}</div> + </div> + <div className={b('buttons')}> + <Button view="flat" size="xs" onClick={handleCopy}> + <Icon size={12} data={Copy} /> + </Button> + <Button view="flat" size="xs" onClick={handleDelete}> + <Icon size={12} data={TrashBin} /> + </Button> + </div> + </div> + {children} + </Card> + ); +}; + const Tree = (_p: PropsWithChildren<TreeProps>) => { - const {content, resetBlocks, selectedBlock} = useMainEditorStore(); + const {content, resetBlocks, selectedBlock, duplicateBlock, deleteBlock} = useMainEditorStore(); const selectedBlockPath = useMemo(() => { return generateChildrenPathFromArray(selectedBlock || []); @@ -51,31 +97,30 @@ const Tree = (_p: PropsWithChildren<TreeProps>) => { const blockTree = generateTree(content.blocks); - const renderTree = (items: TreeItem[], {parentIndex}: ItemOptions = {}) => { - return items.map(({type, children, treeTitle}, index) => { + const renderTree = (items: TreeItem[], parentPathArray?: number[]) => { + return items.map(({type, treeTitle, children}, index) => { let blockPathArray: number[]; - if (parentIndex) { - blockPathArray = [parentIndex, index]; + if (parentPathArray) { + blockPathArray = [...parentPathArray, index]; } else { blockPathArray = [index]; } const blockPath = generateChildrenPathFromArray(blockPathArray); + return ( - <React.Fragment key={index}> - <Card - className={b('item', { - selected: blockPath === selectedBlockPath, - })} - > - <div className={b('type')}>{type}</div> - <div className={b('title')}>{treeTitle}</div> - {children && ( - <div className={b('children')}> - {renderTree(children, {parentIndex: index})} - </div> - )} - </Card> - </React.Fragment> + <Item + type={type} + treeTitle={treeTitle} + onCopy={duplicateBlock} + onDelete={deleteBlock} + key={index} + path={blockPathArray} + selected={selectedBlockPath === blockPath} + > + {children && ( + <div className={b('children')}>{renderTree(children, blockPathArray)}</div> + )} + </Item> ); }); }; @@ -90,7 +135,6 @@ const Tree = (_p: PropsWithChildren<TreeProps>) => { </Button> </div> </div> - <div className={b('cards')}>{renderTree(blockTree)}</div> </div> ); From 11a4f157222207c554c4b47e97971e7fccdc63cf Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Tue, 11 Mar 2025 18:19:58 +0300 Subject: [PATCH 017/118] feat: editor source yaml json controls --- .../containers/SourceCode/SourceCode.scss | 16 +++++++ .../containers/SourceCode/SourceCode.tsx | 45 +++++++++++++++++-- .../SourceCode/UpdateModal/UpdateModal.scss | 10 +++++ .../SourceCode/UpdateModal/UpdateModal.tsx | 38 ++++++++++++++++ 4 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 editor-v2/containers/SourceCode/UpdateModal/UpdateModal.scss create mode 100644 editor-v2/containers/SourceCode/UpdateModal/UpdateModal.tsx diff --git a/editor-v2/containers/SourceCode/SourceCode.scss b/editor-v2/containers/SourceCode/SourceCode.scss index 30f7272a79..625207caf1 100644 --- a/editor-v2/containers/SourceCode/SourceCode.scss +++ b/editor-v2/containers/SourceCode/SourceCode.scss @@ -26,5 +26,21 @@ $block: '.#{$ns}source-code'; overflow: auto; border: 1px solid var(--g-color-line-generic); border-radius: 6px; + position: relative; + + &:hover { + #{$block}__controls { + display: flex; + flex-direction: row; + gap: 8px; + } + } + } + + &__controls { + display: none; + position: absolute; + top: 10px; + right: 10px; } } diff --git a/editor-v2/containers/SourceCode/SourceCode.tsx b/editor-v2/containers/SourceCode/SourceCode.tsx index 864d2f3100..b0b9ec368e 100644 --- a/editor-v2/containers/SourceCode/SourceCode.tsx +++ b/editor-v2/containers/SourceCode/SourceCode.tsx @@ -1,10 +1,14 @@ +import {ArrowDownToSquare} from '@gravity-ui/icons'; +import {Button, ClipboardButton, Icon} from '@gravity-ui/uikit'; import yaml from 'js-yaml'; -import React from 'react'; +import React, {useMemo, useState} from 'react'; +import {Content} from '../../../src/models'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; import './SourceCode.scss'; +import {UpdateModal} from './UpdateModal/UpdateModal'; const b = editorCn('source-code'); @@ -14,14 +18,47 @@ interface SourceCodeProps { } const SourceCode = ({className, format}: SourceCodeProps) => { - const {content} = useMainEditorStore(); + const {content, setContent} = useMainEditorStore(); + const [isOpen, setIsOpen] = useState(false); - const text = format === 'yaml' ? yaml.dump(content) : JSON.stringify(content, null, 2); + const handleUpdate = (tempConfig: string) => { + let object: Content | undefined; + + try { + if (tempConfig.trim().startsWith('{') && tempConfig.trim().endsWith('}')) { + object = JSON.parse(tempConfig); + } else { + object = yaml.load(tempConfig) as Content; + } + } catch { + // eslint-disable-next-line no-console + console.error('JSON.parse failed'); + } + + if (object) { + setContent(object); + } + + setIsOpen(false); + }; + + const textContent = useMemo(() => { + return format === 'yaml' ? yaml.dump(content) : JSON.stringify(content, null, 2); + }, [format, content]); return ( <div className={b(null, className)}> <div className={b('title')}>{format}</div> - <div className={b('code')}>{text}</div> + <div className={b('code')}> + <div className={b('content')}>{textContent}</div> + <div className={b('controls')}> + <ClipboardButton view="flat" size="xs" text={textContent} /> + <Button view="flat" size="xs" onClick={() => setIsOpen(true)}> + <Icon size={14} data={ArrowDownToSquare} /> + </Button> + </div> + </div> + <UpdateModal onApply={handleUpdate} onClose={() => setIsOpen(false)} isOpen={isOpen} /> </div> ); }; diff --git a/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.scss b/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.scss new file mode 100644 index 0000000000..3a4980b95a --- /dev/null +++ b/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.scss @@ -0,0 +1,10 @@ +@import '../../../styles/variables.scss'; +@import '../../../styles/mixins.scss'; + +$block: '.#{$ns}source-code-update-modal'; + +#{$block} { + &__alert { + margin-bottom: 8px; + } +} diff --git a/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.tsx b/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.tsx new file mode 100644 index 0000000000..4f5ba2cd1f --- /dev/null +++ b/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.tsx @@ -0,0 +1,38 @@ +import {Alert, Dialog, TextArea} from '@gravity-ui/uikit'; +import React, {useState} from 'react'; + +import {editorCn} from '../../../utils/cn'; + +import './UpdateModal.scss'; + +const b = editorCn('source-code-update-modal'); + +export const UpdateModal = ({onClose, onApply, isOpen}) => { + const [tempConfig, setTempConfig] = useState(''); + const handleApply = () => { + onApply(tempConfig); + }; + return ( + <Dialog onClose={onClose} open={isOpen} size={'l'} className={b()}> + <Dialog.Header caption="New configuration" /> + <Dialog.Body> + <Alert + theme={'info'} + title={'You can use YAML or JSON'} + message={'The editor will automatically understand which format is needed.'} + className={b('alert')} + ></Alert> + <TextArea value={tempConfig} onUpdate={setTempConfig} rows={25} /> + </Dialog.Body> + <Dialog.Footer + showError={false} + listenKeyEnter={true} + preset={'default'} + textButtonApply={'Apply'} + textButtonCancel={'Cancel'} + onClickButtonApply={handleApply} + onClickButtonCancel={onClose} + /> + </Dialog> + ); +}; From f02154e58f719bb52ed5dfb156c158257a7c1a77 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Tue, 11 Mar 2025 18:37:58 +0300 Subject: [PATCH 018/118] fix: editor right tabs lvl --- editor-v2/hooks/useEditorTabs.tsx | 38 +++++++++++++++++++------------ 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/editor-v2/hooks/useEditorTabs.tsx b/editor-v2/hooks/useEditorTabs.tsx index 2716fa93a7..0fa30e8ae2 100644 --- a/editor-v2/hooks/useEditorTabs.tsx +++ b/editor-v2/hooks/useEditorTabs.tsx @@ -56,20 +56,30 @@ export const useEditorTabs = ({ ], right: [ { - id: 'block-config', - title: 'INPUTS', - component: BlockConfig, - position: 'right', - }, - { - id: 'source-code-yaml', - title: 'YAML', - component: () => <SourceCode format="yaml" />, - }, - { - id: 'source-code-json', - title: 'JSON', - component: () => <SourceCode format="json" />, + id: 'edit', + title: 'EDIT', + component: () => ( + <Tabs + items={[ + { + id: 'block-config', + title: 'INPUTS', + component: BlockConfig, + position: 'right', + }, + { + id: 'source-code-yaml', + title: 'YAML', + component: () => <SourceCode format="yaml" />, + }, + { + id: 'source-code-json', + title: 'JSON', + component: () => <SourceCode format="json" />, + }, + ]} + /> + ), }, ...rightTabs, ], From 8ec78f6d0f0fbf9e086d54b8f56b1f60c008ed5c Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Wed, 12 Mar 2025 12:28:56 +0300 Subject: [PATCH 019/118] feat: editor blocks search input --- .../containers/BlocksList/BlocksList.scss | 12 +++- .../containers/BlocksList/BlocksList.tsx | 58 +++++++++++-------- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/editor-v2/containers/BlocksList/BlocksList.scss b/editor-v2/containers/BlocksList/BlocksList.scss index 6204e9b466..b6b793ff3b 100644 --- a/editor-v2/containers/BlocksList/BlocksList.scss +++ b/editor-v2/containers/BlocksList/BlocksList.scss @@ -5,7 +5,7 @@ $block: '.#{$ns}blocks-list'; #{$block} { - padding: 12px; + padding: 16px 12px; display: flex; flex-direction: column; gap: 8px; @@ -44,8 +44,8 @@ $block: '.#{$ns}blocks-list'; } &__group { - padding-bottom: 16px; - margin-top: 16px; + padding-bottom: 12px; + margin-top: 12px; border-bottom: 1px solid var(--g-color-line-generic); &:first-child { @@ -62,4 +62,10 @@ $block: '.#{$ns}blocks-list'; gap: 8px; } } + + &__search { + &-icon { + margin-left: 8px; + } + } } diff --git a/editor-v2/containers/BlocksList/BlocksList.tsx b/editor-v2/containers/BlocksList/BlocksList.tsx index fe6fad1099..9461f3154d 100644 --- a/editor-v2/containers/BlocksList/BlocksList.tsx +++ b/editor-v2/containers/BlocksList/BlocksList.tsx @@ -1,7 +1,6 @@ -import {SquareBars} from '@gravity-ui/icons'; -import {Card, Icon} from '@gravity-ui/uikit'; -import _ from 'lodash'; -import React, {PropsWithChildren, useCallback} from 'react'; +import {Magnifier, SquareBars} from '@gravity-ui/icons'; +import {Card, Icon, TextInput} from '@gravity-ui/uikit'; +import React, {useCallback, useMemo, useState} from 'react'; import {ItemConfig} from '../../../common/types'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; @@ -11,16 +10,13 @@ import './BlocksList.scss'; const b = editorCn('blocks-list'); -export interface BlocksListProps { - blocks: ItemConfig[]; -} - interface BlockGroups { [key: string]: ItemConfig[]; } -const BlocksList = (_p: PropsWithChildren<BlocksListProps>) => { +const BlocksList = () => { const {blocks, enableInsertMode} = useMainEditorStore(); + const [search, setSearch] = useState(''); const onMouseDown = useCallback( (blockType: string) => { @@ -29,27 +25,41 @@ const BlocksList = (_p: PropsWithChildren<BlocksListProps>) => { [enableInsertMode], ); - const groups = blocks.reduce<BlockGroups>((acc, currentBlock) => { - const group = currentBlock.schema.group; - if (group) { - if (!acc[group]) { - /* eslint-disable no-param-reassign */ - acc[group] = []; + const groups = useMemo(() => { + return blocks.reduce<BlockGroups>((acc, currentBlock) => { + const group = currentBlock.schema.group; + if (search && currentBlock.type.indexOf(search) === -1) { + return acc; } - acc[group].push(currentBlock); - } else { - if (!acc['other']) { - /* eslint-disable no-param-reassign */ - acc['other'] = []; + if (group) { + if (!acc[group]) { + /* eslint-disable no-param-reassign */ + acc[group] = []; + } + acc[group].push(currentBlock); + } else { + if (!acc['other']) { + /* eslint-disable no-param-reassign */ + acc['other'] = []; + } + acc['other'].push(currentBlock); } - acc['other'].push(currentBlock); - } - return acc; - }, {}); + return acc; + }, {}); + }, [blocks, search]); return ( <div className={b()}> + <div className={b('search')}> + <TextInput + hasClear + placeholder="Search block" + onUpdate={setSearch} + value={search} + startContent={<Icon className={b('search-icon')} data={Magnifier} />} + /> + </div> {Object.entries(groups).map(([key, groupBlocks]) => ( <div className={b('group')} key={key}> <div className={b('title')}>{key}</div> From 563b9c41902424c1bae51681747d7096a805861f Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Wed, 12 Mar 2025 12:40:36 +0300 Subject: [PATCH 020/118] fix: editor containers imports --- .../{components => containers}/BigOverlay/BigOverlay.scss | 0 .../{components => containers}/BigOverlay/BigOverlay.tsx | 0 editor-v2/containers/Editor/Editor.tsx | 4 ++-- .../{components => containers}/MiddleScreen/MiddleScreen.scss | 0 .../{components => containers}/MiddleScreen/MiddleScreen.tsx | 0 editor-v2/{components => containers}/Overlay/Overlay.scss | 0 editor-v2/{components => containers}/Overlay/Overlay.tsx | 0 7 files changed, 2 insertions(+), 2 deletions(-) rename editor-v2/{components => containers}/BigOverlay/BigOverlay.scss (100%) rename editor-v2/{components => containers}/BigOverlay/BigOverlay.tsx (100%) rename editor-v2/{components => containers}/MiddleScreen/MiddleScreen.scss (100%) rename editor-v2/{components => containers}/MiddleScreen/MiddleScreen.tsx (100%) rename editor-v2/{components => containers}/Overlay/Overlay.scss (100%) rename editor-v2/{components => containers}/Overlay/Overlay.tsx (100%) diff --git a/editor-v2/components/BigOverlay/BigOverlay.scss b/editor-v2/containers/BigOverlay/BigOverlay.scss similarity index 100% rename from editor-v2/components/BigOverlay/BigOverlay.scss rename to editor-v2/containers/BigOverlay/BigOverlay.scss diff --git a/editor-v2/components/BigOverlay/BigOverlay.tsx b/editor-v2/containers/BigOverlay/BigOverlay.tsx similarity index 100% rename from editor-v2/components/BigOverlay/BigOverlay.tsx rename to editor-v2/containers/BigOverlay/BigOverlay.tsx diff --git a/editor-v2/containers/Editor/Editor.tsx b/editor-v2/containers/Editor/Editor.tsx index 7bc9611cc7..6753648109 100644 --- a/editor-v2/containers/Editor/Editor.tsx +++ b/editor-v2/containers/Editor/Editor.tsx @@ -1,11 +1,11 @@ import React, {ElementType, useCallback} from 'react'; import {Content} from '../../../src/models'; -import BigOverlay from '../../components/BigOverlay/BigOverlay'; -import MiddleScreen from '../../components/MiddleScreen/MiddleScreen'; import {Panels} from '../../components/Panels/Panels'; import {Sidebar} from '../../components/Sidebar/Sidebar'; import StoreViewer from '../../components/StoreViewer/StoreViewer'; +import BigOverlay from '../../containers/BigOverlay/BigOverlay'; +import MiddleScreen from '../../containers/MiddleScreen/MiddleScreen'; import {MainEditorStoreProvider} from '../../context/editorStore'; import {IframeProvider} from '../../context/iframeContext'; import {useEditorTabs} from '../../hooks/useEditorTabs'; diff --git a/editor-v2/components/MiddleScreen/MiddleScreen.scss b/editor-v2/containers/MiddleScreen/MiddleScreen.scss similarity index 100% rename from editor-v2/components/MiddleScreen/MiddleScreen.scss rename to editor-v2/containers/MiddleScreen/MiddleScreen.scss diff --git a/editor-v2/components/MiddleScreen/MiddleScreen.tsx b/editor-v2/containers/MiddleScreen/MiddleScreen.tsx similarity index 100% rename from editor-v2/components/MiddleScreen/MiddleScreen.tsx rename to editor-v2/containers/MiddleScreen/MiddleScreen.tsx diff --git a/editor-v2/components/Overlay/Overlay.scss b/editor-v2/containers/Overlay/Overlay.scss similarity index 100% rename from editor-v2/components/Overlay/Overlay.scss rename to editor-v2/containers/Overlay/Overlay.scss diff --git a/editor-v2/components/Overlay/Overlay.tsx b/editor-v2/containers/Overlay/Overlay.tsx similarity index 100% rename from editor-v2/components/Overlay/Overlay.tsx rename to editor-v2/containers/Overlay/Overlay.tsx From d3fb219e41121fd5b52237c5c14dda96a4bf0e89 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Wed, 12 Mar 2025 16:56:42 +0300 Subject: [PATCH 021/118] fix: pc item path id array for editor --- .../editor/ChildrenWrap/ChildrenWrap.tsx | 20 +++++++------------ src/components/editor/ItemWrap/ItemWrap.tsx | 18 ++++------------- .../ConstructorBlock/ConstructorBlock.tsx | 12 +++-------- .../ConstructorItem/ConstructorItem.tsx | 3 ++- .../ConstructorLoadable.tsx | 3 ++- src/context/blockIdContext/blockIdContext.ts | 4 ++-- src/hooks/usePCEditorItemWrap.ts | 20 +++++++++++++++++++ 7 files changed, 40 insertions(+), 40 deletions(-) create mode 100644 src/hooks/usePCEditorItemWrap.ts diff --git a/src/components/editor/ChildrenWrap/ChildrenWrap.tsx b/src/components/editor/ChildrenWrap/ChildrenWrap.tsx index 7ddbce1d1b..f4f212cc85 100644 --- a/src/components/editor/ChildrenWrap/ChildrenWrap.tsx +++ b/src/components/editor/ChildrenWrap/ChildrenWrap.tsx @@ -1,7 +1,6 @@ -import React, {PropsWithChildren, ReactNode, useCallback, useContext, useState} from 'react'; +import React, {PropsWithChildren, ReactNode} from 'react'; -import {BlockIdContext} from '../../../context/blockIdContext'; -import usePCEditorBlockMouseEvents from '../../../hooks/usePCEditorBlockMouseEvents'; +import {usePCEditorItemWrap} from '../../../hooks/usePCEditorItemWrap'; import {block} from '../../../utils'; import './ChildrenWrap.scss'; @@ -12,16 +11,11 @@ export interface ChildrenWrapProps extends PropsWithChildren { checkChildren?: ReactNode; } -const ChildrenWrap = (props: ChildrenWrapProps) => { - const {children} = props; - const [element, setElement] = useState<HTMLElement | undefined>(); - const blockRef = useCallback((node: HTMLElement | null) => { - if (node !== null) { - setElement(node); - } - }, []); - const parentBlockId = useContext(BlockIdContext); - const {onMouseUp, onMouseMove} = usePCEditorBlockMouseEvents([parentBlockId, 0], element); +const ChildrenWrap = ({children}: ChildrenWrapProps) => { + const { + blockRef, + adminBlockMouseEvents: {onMouseMove, onMouseUp}, + } = usePCEditorItemWrap(); return ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions diff --git a/src/components/editor/ItemWrap/ItemWrap.tsx b/src/components/editor/ItemWrap/ItemWrap.tsx index c002f418dd..90e98ca0b4 100644 --- a/src/components/editor/ItemWrap/ItemWrap.tsx +++ b/src/components/editor/ItemWrap/ItemWrap.tsx @@ -1,7 +1,6 @@ -import React, {PropsWithChildren, useCallback, useContext, useState} from 'react'; +import React, {PropsWithChildren} from 'react'; -import {BlockIdContext} from '../../../context/blockIdContext'; -import usePCEditorBlockMouseEvents from '../../../hooks/usePCEditorBlockMouseEvents'; +import {usePCEditorItemWrap} from '../../../hooks/usePCEditorItemWrap'; import {block} from '../../../utils'; import './ItemWrap.scss'; @@ -12,17 +11,8 @@ export interface ItemWrapProps extends PropsWithChildren { index: number; } -const ItemWrap = (props: ItemWrapProps) => { - const [element, setElement] = useState<HTMLElement | undefined>(); - const blockRef = useCallback((node: HTMLElement | null) => { - if (node !== null) { - setElement(node); - } - }, []); - const {children, index} = props; - const parentBlockId = useContext(BlockIdContext); - const adminBlockMouseEvents = usePCEditorBlockMouseEvents([parentBlockId, index], element); - +const ItemWrap = ({index, children}: ItemWrapProps) => { + const {blockRef, adminBlockMouseEvents} = usePCEditorItemWrap(index); return ( <div ref={blockRef} className={b()} {...adminBlockMouseEvents}> {children} diff --git a/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx b/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx index 9d593cf901..27f8a214ac 100644 --- a/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx +++ b/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx @@ -1,10 +1,10 @@ -import React, {useCallback, useMemo, useState} from 'react'; +import React, {useMemo} from 'react'; import pick from 'lodash/pick'; import BlockBase from '../../../../components/BlockBase/BlockBase'; import {BlockDecoration} from '../../../../customization/BlockDecoration'; -import usePCEditorBlockMouseEvents from '../../../../hooks/usePCEditorBlockMouseEvents'; +import {usePCEditorItemWrap} from '../../../../hooks/usePCEditorItemWrap'; import {BlockDecorationProps, ConstructorBlock as ConstructorBlockType} from '../../../../models'; import {block} from '../../../../utils'; @@ -21,13 +21,7 @@ export const ConstructorBlock = ({ data, children, }: React.PropsWithChildren<ConstructorBlockProps>) => { - const [element, setElement] = useState<HTMLElement | undefined>(); - const blockRef = useCallback((node: HTMLElement | null) => { - if (node !== null) { - setElement(node); - } - }, []); - const adminBlockMouseEvents = usePCEditorBlockMouseEvents([index], element); + const {blockRef, adminBlockMouseEvents} = usePCEditorItemWrap(index); const {type} = data; const blockBaseProps = useMemo( diff --git a/src/containers/PageConstructor/components/ConstructorItem/ConstructorItem.tsx b/src/containers/PageConstructor/components/ConstructorItem/ConstructorItem.tsx index 6524dbd10c..7b80cda6dd 100644 --- a/src/containers/PageConstructor/components/ConstructorItem/ConstructorItem.tsx +++ b/src/containers/PageConstructor/components/ConstructorItem/ConstructorItem.tsx @@ -16,6 +16,7 @@ export const ConstructorItem = ({ children, }: React.PropsWithChildren<ConstructorItemProps>) => { const {itemMap} = React.useContext(InnerContext); + const parentId = React.useContext(BlockIdContext); const {type, ...rest} = data; const Component = itemMap[type] as React.ComponentType< @@ -23,7 +24,7 @@ export const ConstructorItem = ({ >; return ( - <BlockIdContext.Provider value={blockKey} key={blockKey}> + <BlockIdContext.Provider value={[...parentId, blockKey]} key={blockKey}> <Component {...rest}>{children}</Component> </BlockIdContext.Provider> ); diff --git a/src/containers/PageConstructor/components/ConstructorLoadable/ConstructorLoadable.tsx b/src/containers/PageConstructor/components/ConstructorLoadable/ConstructorLoadable.tsx index 8d29d4f045..e2daf4d305 100644 --- a/src/containers/PageConstructor/components/ConstructorLoadable/ConstructorLoadable.tsx +++ b/src/containers/PageConstructor/components/ConstructorLoadable/ConstructorLoadable.tsx @@ -18,9 +18,10 @@ export const ConstructorLoadable = (props: ConstructorLoadableProps) => { const Component = itemMap[type] as React.Component< React.ComponentProps<(typeof itemMap)[typeof type]> >; + const parentId = useContext(BlockIdContext); return ( - <BlockIdContext.Provider value={Number(blockKey)} key={blockKey}> + <BlockIdContext.Provider value={[...parentId, Number(blockKey)]} key={blockKey}> <Loadable key={blockKey} block={block} diff --git a/src/context/blockIdContext/blockIdContext.ts b/src/context/blockIdContext/blockIdContext.ts index b621d734c2..bc82a8b723 100644 --- a/src/context/blockIdContext/blockIdContext.ts +++ b/src/context/blockIdContext/blockIdContext.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -export type BlockIdContextProp = number; +export type BlockIdContextProp = number[]; -export const BlockIdContext = React.createContext<BlockIdContextProp>(NaN); +export const BlockIdContext = React.createContext<BlockIdContextProp>([]); diff --git a/src/hooks/usePCEditorItemWrap.ts b/src/hooks/usePCEditorItemWrap.ts new file mode 100644 index 0000000000..c05568445c --- /dev/null +++ b/src/hooks/usePCEditorItemWrap.ts @@ -0,0 +1,20 @@ +import {useCallback, useContext, useState} from 'react'; + +import {BlockIdContext} from '../context/blockIdContext'; + +import usePCEditorBlockMouseEvents from './usePCEditorBlockMouseEvents'; + +export function usePCEditorItemWrap(index = 0) { + const [element, setElement] = useState<HTMLElement | undefined>(); + + const blockRef = useCallback((node: HTMLElement | null) => { + if (node !== null) { + setElement(node); + } + }, []); + + const parentBlockId = useContext(BlockIdContext); + const adminBlockMouseEvents = usePCEditorBlockMouseEvents([...parentBlockId, index], element); + + return {adminBlockMouseEvents, blockRef}; +} From 2fc2e45061b87e3f74b9fa3304e71be6dd9cf103 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Fri, 14 Mar 2025 16:23:26 +0300 Subject: [PATCH 022/118] fix: prettier --- editor-v2/styles/mixins.scss | 2 +- editor-v2/styles/variables.scss | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/editor-v2/styles/mixins.scss b/editor-v2/styles/mixins.scss index 63223ed648..ae42847302 100644 --- a/editor-v2/styles/mixins.scss +++ b/editor-v2/styles/mixins.scss @@ -10,4 +10,4 @@ &:hover { transform: scale($hoverScale); } -} ; +} diff --git a/editor-v2/styles/variables.scss b/editor-v2/styles/variables.scss index e8cd79a738..d44cdee401 100644 --- a/editor-v2/styles/variables.scss +++ b/editor-v2/styles/variables.scss @@ -1,6 +1,8 @@ $ns: 'pceditor-'; $editorTransitionTime: 0.2s; -$editorShadow: 0px 2px 8px rgba(0, 0, 0, 0.06), 0px 4px 24px rgba(0, 0, 0, 0.06); +$editorShadow: + 0px 2px 8px rgba(0, 0, 0, 0.06), + 0px 4px 24px rgba(0, 0, 0, 0.06); $editorControlBorderRadius: 8px; $headerHeight: 0px; From 6910d3352c9c35f9f5415246a101a8b5ab0e5b81 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Fri, 14 Mar 2025 23:38:05 +0300 Subject: [PATCH 023/118] fix: update react imports for linter --- common/postMessage.ts | 4 +- .../components/DynamicForm/DynamicForm.tsx | 8 +- .../DynamicForm/FieldBase/FieldBase.tsx | 6 +- .../DynamicForm/Fields/Array/Array.tsx | 12 +- .../Fields/Array/ItemButton/ItemButton.tsx | 12 +- .../DynamicForm/Fields/Boolean/Boolean.tsx | 1 - .../DynamicForm/Fields/Number/Number.tsx | 1 - .../DynamicForm/Fields/Object/Object.tsx | 1 - .../DynamicForm/Fields/OneOf/OneOf.tsx | 12 +- .../DynamicForm/Fields/Select/Select.tsx | 5 +- .../DynamicForm/Fields/Text/Text.tsx | 1 - .../DynamicForm/Fields/TextArea/TextArea.tsx | 1 - editor-v2/components/Panels/Panels.tsx | 12 +- editor-v2/components/Sidebar/Sidebar.tsx | 4 +- .../components/StoreViewer/StoreViewer.tsx | 4 +- editor-v2/components/Tabs/Tabs.tsx | 12 +- .../containers/BigOverlay/BigOverlay.tsx | 16 +- .../containers/BlockConfig/BlockConfig.tsx | 1 - .../containers/BlocksList/BlocksList.tsx | 10 +- editor-v2/containers/Editor/Editor.tsx | 12 +- .../containers/GlobalConfig/GlobalConfig.tsx | 2 - .../containers/MiddleScreen/MiddleScreen.tsx | 10 +- editor-v2/containers/Overlay/Overlay.tsx | 10 +- editor-v2/containers/Source/Source.tsx | 6 +- .../containers/SourceCode/SourceCode.tsx | 6 +- .../SourceCode/UpdateModal/UpdateModal.tsx | 4 +- editor-v2/containers/Tree/Tree.tsx | 12 +- .../containers/ViewSwitches/ViewSwitches.tsx | 1 - .../containers/__stories__/Editor.stories.tsx | 1 - .../editorStore/MainEditorStoreContext.tsx | 2 +- .../editorStore/MainEditorStoreProvider.tsx | 12 +- .../context/iframeContext/IframeContext.tsx | 2 +- .../context/iframeContext/IframeProvider.tsx | 8 +- editor-v2/hooks/useEditorTabs.tsx | 5 +- editor-v2/hooks/useMainEditorStore.ts | 4 +- editor-v2/hooks/usePostMessageEvents.ts | 4 +- editor-v2/styles/root.scss | 5 + playground/src/app/page.tsx | 6 +- playground/src/app/pc-2/page.tsx | 5 +- playground/src/app/pc/page.tsx | 1 - .../TestEditorBlock/TestEditorBlock.tsx | 4 +- .../editor/ChildrenWrap/ChildrenWrap.tsx | 6 +- src/components/editor/ItemWrap/ItemWrap.tsx | 4 +- src/constructor-items.ts | 2 +- .../PageConstructor/PageConstructor.tsx | 10 +- .../ConstructorBlock/ConstructorBlock.tsx | 4 +- .../hooks/useEditorBlockMouseEvents.tsx | 87 ++++++++++ .../PCEditorStoreContext.tsx | 2 +- .../PCEditorStoreProvider.tsx | 10 +- .../components/BigOverlay/BigOverlay.tsx | 85 ++++++++++ .../DynamicForm/Fields/Array/Array.tsx | 158 ++++++++++++++++++ .../components/CodeEditor/CodeEditor.tsx | 8 +- src/hooks/usePCEditorBlockMouseEvents.ts | 14 +- src/hooks/usePCEditorInitializeEvents.ts | 10 +- src/hooks/usePCEditorItemWrap.ts | 8 +- src/hooks/usePCEditorStore.ts | 4 +- src/hooks/usePostMessageAPI.ts | 4 +- src/utils/editor.ts | 7 +- 58 files changed, 497 insertions(+), 171 deletions(-) create mode 100644 src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx create mode 100644 src/editor-v2/components/BigOverlay/BigOverlay.tsx create mode 100644 src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx diff --git a/common/postMessage.ts b/common/postMessage.ts index d96c84b76f..8faadd6fd4 100644 --- a/common/postMessage.ts +++ b/common/postMessage.ts @@ -1,4 +1,4 @@ -import {useEffect} from 'react'; +import * as React from 'react'; import {ActionMessageTypes, EventMessageTypes, PostMessageAPIMessage} from './types'; @@ -37,7 +37,7 @@ export function usePostMessageAPIListener<K extends keyof EventMessageTypes>( callback: (data: EventMessageTypes[K]) => void, deps: unknown[] = [], ) { - useEffect(() => { + React.useEffect(() => { return listenPostMessageEvents(action, callback); }, [...deps]); } diff --git a/editor-v2/components/DynamicForm/DynamicForm.tsx b/editor-v2/components/DynamicForm/DynamicForm.tsx index 3ed3cf233a..9531c21815 100644 --- a/editor-v2/components/DynamicForm/DynamicForm.tsx +++ b/editor-v2/components/DynamicForm/DynamicForm.tsx @@ -1,5 +1,5 @@ import _ from 'lodash'; -import React, {useCallback} from 'react'; +import * as React from 'react'; import {ConfigInput, DynamicFormValue} from '../../../common/types'; import {editorCn} from '../../utils/cn'; @@ -27,7 +27,7 @@ interface DynamicFormProps { const DynamicForm = ({blockConfig, onUpdate, contentConfig}: DynamicFormProps) => { const inputs = blockConfig; - const getData = useCallback( + const getData = React.useCallback( (variable: string) => { if (variable.startsWith('block.')) { const purePath = variable.replace('block.', ''); @@ -47,7 +47,7 @@ const DynamicForm = ({blockConfig, onUpdate, contentConfig}: DynamicFormProps) = [contentConfig], ); - const decide = useCallback( + const decide = React.useCallback( (showIf: string): boolean => { const parts = showIf.split(' '); @@ -71,7 +71,7 @@ const DynamicForm = ({blockConfig, onUpdate, contentConfig}: DynamicFormProps) = [getData], ); - const renderInput = useCallback( + const renderInput = React.useCallback( (input: ConfigInput) => { const fieldPath = input.name; const fieldValue = getContent(contentConfig, input.name); diff --git a/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx b/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx index 2def6c0d06..3b4dc5a551 100644 --- a/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx +++ b/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx @@ -1,7 +1,7 @@ import {ArrowRotateLeft} from '@gravity-ui/icons'; import {ArrowToggle, Button, Icon} from '@gravity-ui/uikit'; import _ from 'lodash'; -import React, {PropsWithChildren, useState} from 'react'; +import * as React from 'react'; import {editorCn} from '../../../utils/cn'; @@ -16,7 +16,7 @@ export interface FieldBaseParams { expandable?: boolean; } -export interface FieldBaseProps extends PropsWithChildren, FieldBaseParams { +export interface FieldBaseProps extends React.PropsWithChildren, FieldBaseParams { className?: string; } @@ -28,7 +28,7 @@ const FieldBase: React.FC<FieldBaseProps> = ({ onRefresh, expandable = false, }) => { - const [showChildren, setShowChildren] = useState(!expandable); + const [showChildren, setShowChildren] = React.useState(!expandable); const titleComponent = React.useMemo(() => { if (title) { diff --git a/editor-v2/components/DynamicForm/Fields/Array/Array.tsx b/editor-v2/components/DynamicForm/Fields/Array/Array.tsx index 25dba84c7f..295225c737 100644 --- a/editor-v2/components/DynamicForm/Fields/Array/Array.tsx +++ b/editor-v2/components/DynamicForm/Fields/Array/Array.tsx @@ -1,6 +1,6 @@ import {Plus} from '@gravity-ui/icons'; import {Button, Card, Icon} from '@gravity-ui/uikit'; -import React, {useCallback} from 'react'; +import * as React from 'react'; import {ArrayObjectInput, ArrayTextInput, DynamicFormValue} from '../../../../../common/types'; import {removeFromArray, swapArrayItems} from '../../../../utils'; @@ -27,7 +27,7 @@ interface ArrayFieldProps { const ArrayDynamicField = ({title, values, onUpdate, className, blockConfig}: ArrayFieldProps) => { const haveItems = values && Array.isArray(values) && values.length; - const onAddItem = useCallback(() => { + const onAddItem = React.useCallback(() => { if (blockConfig.arrayType === 'text') { onUpdate('', haveItems ? [...values, ''] : ['']); } else if (blockConfig.arrayType === 'object') { @@ -35,7 +35,7 @@ const ArrayDynamicField = ({title, values, onUpdate, className, blockConfig}: Ar } }, [blockConfig.arrayType, haveItems, onUpdate, values]); - const onDeleteItem = useCallback( + const onDeleteItem = React.useCallback( (index: number) => { if (Array.isArray(values)) { const newArray = removeFromArray(values, index); @@ -45,7 +45,7 @@ const ArrayDynamicField = ({title, values, onUpdate, className, blockConfig}: Ar [onUpdate, values], ); - const onReorderItem = useCallback( + const onReorderItem = React.useCallback( (index: number, placement: 'up' | 'down') => { if (Array.isArray(values)) { const newArray = swapArrayItems( @@ -59,7 +59,7 @@ const ArrayDynamicField = ({title, values, onUpdate, className, blockConfig}: Ar [onUpdate, values], ); - const renderInput = useCallback( + const renderInput = React.useCallback( (value: DynamicFormValue, index: number) => { const arrayItemButton = ( <ItemButton @@ -113,7 +113,7 @@ const ArrayDynamicField = ({title, values, onUpdate, className, blockConfig}: Ar [blockConfig, haveItems, onDeleteItem, onReorderItem, onUpdate, values], ); - const renderInputs = useCallback(() => { + const renderInputs = React.useCallback(() => { if (haveItems) { const renderItems = values .map(renderInput) diff --git a/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx b/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx index 05128a5b03..95b22af945 100644 --- a/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx +++ b/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx @@ -1,6 +1,6 @@ import {ArrowDown, ArrowUp, EllipsisVertical, TrashBin} from '@gravity-ui/icons'; import {Button, Icon, Menu, Popup} from '@gravity-ui/uikit'; -import React, {Fragment, useCallback, useRef, useState} from 'react'; +import * as React from 'react'; import {editorCn} from '../../../../../utils/cn'; @@ -23,10 +23,10 @@ const ItemButton = ({ disableReorderUp = false, disableReorderDown = false, }: ItemButtonProps) => { - const buttonRef = useRef(null); - const [isOpen, setIsOpen] = useState(false); + const buttonRef = React.useRef(null); + const [isOpen, setIsOpen] = React.useState(false); - const onMenuItemClickWrapper = useCallback((callback: () => void) => { + const onMenuItemClickWrapper = React.useCallback((callback: () => void) => { return () => { setIsOpen(false); callback(); @@ -34,7 +34,7 @@ const ItemButton = ({ }, []); return ( - <Fragment> + <React.Fragment> <Button className={b(null, className)} ref={buttonRef} onClick={() => setIsOpen(true)}> <Icon data={EllipsisVertical} /> </Button> @@ -68,7 +68,7 @@ const ItemButton = ({ </Menu.Item> </Menu> </Popup> - </Fragment> + </React.Fragment> ); }; diff --git a/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx b/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx index 1ce0f5af22..1bacfdc8f2 100644 --- a/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx +++ b/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx @@ -1,5 +1,4 @@ import {Switch} from '@gravity-ui/uikit'; -import React from 'react'; import {editorCn} from '../../../../utils/cn'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; diff --git a/editor-v2/components/DynamicForm/Fields/Number/Number.tsx b/editor-v2/components/DynamicForm/Fields/Number/Number.tsx index 659ea5c088..6a42d71d91 100644 --- a/editor-v2/components/DynamicForm/Fields/Number/Number.tsx +++ b/editor-v2/components/DynamicForm/Fields/Number/Number.tsx @@ -1,5 +1,4 @@ import {TextInput} from '@gravity-ui/uikit'; -import React from 'react'; import {editorCn} from '../../../../utils/cn'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; diff --git a/editor-v2/components/DynamicForm/Fields/Object/Object.tsx b/editor-v2/components/DynamicForm/Fields/Object/Object.tsx index c5df74f106..72ad24941c 100644 --- a/editor-v2/components/DynamicForm/Fields/Object/Object.tsx +++ b/editor-v2/components/DynamicForm/Fields/Object/Object.tsx @@ -1,5 +1,4 @@ import {Card} from '@gravity-ui/uikit'; -import React from 'react'; import {ConfigInput, DynamicFormValue} from '../../../../../common/types'; import {editorCn} from '../../../../utils/cn'; diff --git a/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx b/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx index 48f34e68ee..941f5e070d 100644 --- a/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx +++ b/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx @@ -1,5 +1,5 @@ -import {Card, RadioButton} from '@gravity-ui/uikit'; -import React, {useCallback, useMemo, useState} from 'react'; +import {Card, SegmentedRadioGroup} from '@gravity-ui/uikit'; +import * as React from 'react'; import {DynamicFormValue, OneOfInput} from '../../../../../common/types'; import {editorCn} from '../../../../utils/cn'; @@ -33,10 +33,10 @@ const OneOfDynamicField = ({ }: OneOfDynamicFieldProps) => { const defaultValue = inputConfig.options[0].value; - const [oneOfMetaValue, setOneOfMetaValue] = useState(defaultValue); + const [oneOfMetaValue, setOneOfMetaValue] = React.useState(defaultValue); const oneOfContentConfig = getOneOfContentConfig(contentConfig, inputConfig.name); - const oneOfChosenOption = useMemo( + const oneOfChosenOption = React.useMemo( () => inputConfig.options.find( ({value: foundOneOfValue}) => foundOneOfValue === oneOfMetaValue, @@ -44,7 +44,7 @@ const OneOfDynamicField = ({ [inputConfig.options, oneOfMetaValue], ); - const onUpdateOneOf = useCallback((value: string) => { + const onUpdateOneOf = React.useCallback((value: string) => { setOneOfMetaValue(value); }, []); @@ -56,7 +56,7 @@ const OneOfDynamicField = ({ expandable > <Card className={b('card')}> - <RadioButton + <SegmentedRadioGroup className={b('radio')} options={inputConfig.options.map((option) => ({ content: option.title, diff --git a/editor-v2/components/DynamicForm/Fields/Select/Select.tsx b/editor-v2/components/DynamicForm/Fields/Select/Select.tsx index ae91fca9de..a61e4e28dc 100644 --- a/editor-v2/components/DynamicForm/Fields/Select/Select.tsx +++ b/editor-v2/components/DynamicForm/Fields/Select/Select.tsx @@ -1,5 +1,4 @@ -import {RadioButton, Select} from '@gravity-ui/uikit'; -import React from 'react'; +import {SegmentedRadioGroup, Select} from '@gravity-ui/uikit'; import {SelectMultipleInput, SelectSingleInput} from '../../../../../common/types'; import {editorCn} from '../../../../utils/cn'; @@ -30,7 +29,7 @@ const SelectDynamicField = ({input, value, onUpdate, className}: SelectDynamicFi /> )} {inputView === 'radiobutton' && ( - <RadioButton options={input.enum} value={value} onUpdate={onUpdate} /> + <SegmentedRadioGroup options={input.enum} value={value} onUpdate={onUpdate} /> )} </FieldBase> ); diff --git a/editor-v2/components/DynamicForm/Fields/Text/Text.tsx b/editor-v2/components/DynamicForm/Fields/Text/Text.tsx index 96ad2ab90e..ec16c5233c 100644 --- a/editor-v2/components/DynamicForm/Fields/Text/Text.tsx +++ b/editor-v2/components/DynamicForm/Fields/Text/Text.tsx @@ -1,5 +1,4 @@ import {TextInput} from '@gravity-ui/uikit'; -import React from 'react'; import {editorCn} from '../../../../utils/cn'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; diff --git a/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx b/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx index 49d399c668..acd6b81eb0 100644 --- a/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx +++ b/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx @@ -1,5 +1,4 @@ import {TextArea} from '@gravity-ui/uikit'; -import React from 'react'; import {editorCn} from '../../../../utils/cn'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; diff --git a/editor-v2/components/Panels/Panels.tsx b/editor-v2/components/Panels/Panels.tsx index b0f2f6c87e..0c128a44b5 100644 --- a/editor-v2/components/Panels/Panels.tsx +++ b/editor-v2/components/Panels/Panels.tsx @@ -1,6 +1,6 @@ import {ArrowLeftFromLine, ArrowRightFromLine, Grip} from '@gravity-ui/icons'; import {Button, Icon} from '@gravity-ui/uikit'; -import React, {ReactElement, useRef} from 'react'; +import * as React from 'react'; import {ImperativePanelHandle, Panel, PanelGroup, PanelResizeHandle} from 'react-resizable-panels'; import {editorCn} from '../../utils/cn'; @@ -10,14 +10,14 @@ import './Panels.scss'; const b = editorCn('panels'); interface PanelsProps { - left: ReactElement; - middle: ReactElement; - right: ReactElement; + left: React.ReactElement; + middle: React.ReactElement; + right: React.ReactElement; } export const Panels = ({left, right, middle}: PanelsProps) => { - const leftPanel = useRef<ImperativePanelHandle>(null); - const rightPanel = useRef<ImperativePanelHandle>(null); + const leftPanel = React.useRef<ImperativePanelHandle>(null); + const rightPanel = React.useRef<ImperativePanelHandle>(null); const expandPanel = (reference: React.RefObject<ImperativePanelHandle>) => { const panel = reference.current; diff --git a/editor-v2/components/Sidebar/Sidebar.tsx b/editor-v2/components/Sidebar/Sidebar.tsx index 1ef6cfe7ce..f77cd854cc 100644 --- a/editor-v2/components/Sidebar/Sidebar.tsx +++ b/editor-v2/components/Sidebar/Sidebar.tsx @@ -1,4 +1,4 @@ -import React, {ElementType} from 'react'; +import * as React from 'react'; import {editorCn} from '../../utils/cn'; import Tabs, {TabsItemProps} from '../Tabs/Tabs'; @@ -10,7 +10,7 @@ const b = editorCn('sidebar'); interface SidebarProps { tabs: TabsItemProps[]; defaultTab?: string; - top?: ElementType[]; + top?: React.ElementType[]; className?: string; } diff --git a/editor-v2/components/StoreViewer/StoreViewer.tsx b/editor-v2/components/StoreViewer/StoreViewer.tsx index 59afb58eee..753842df29 100644 --- a/editor-v2/components/StoreViewer/StoreViewer.tsx +++ b/editor-v2/components/StoreViewer/StoreViewer.tsx @@ -1,6 +1,6 @@ import {Code} from '@gravity-ui/icons'; import {Button, Icon} from '@gravity-ui/uikit'; -import React, {useState} from 'react'; +import * as React from 'react'; import ReactJson from 'react-json-view'; import {removeFn} from '../../../common/utils'; @@ -16,7 +16,7 @@ interface StoreViewerProps { } const StoreViewer = ({store, className}: StoreViewerProps) => { - const [open, setOpen] = useState(false); + const [open, setOpen] = React.useState(false); return ( <div className={b({open}, className)}> <div className={b('head')}> diff --git a/editor-v2/components/Tabs/Tabs.tsx b/editor-v2/components/Tabs/Tabs.tsx index 7c7ee710ee..7c8dbd6c50 100644 --- a/editor-v2/components/Tabs/Tabs.tsx +++ b/editor-v2/components/Tabs/Tabs.tsx @@ -1,5 +1,5 @@ import {Button} from '@gravity-ui/uikit'; -import React, {ElementType, useCallback, useMemo, useState} from 'react'; +import * as React from 'react'; import {editorCn} from '../../utils/cn'; @@ -10,7 +10,7 @@ const b = editorCn('tabs'); export interface TabsItemProps { id: string; title: string; - component: ElementType; + component: React.ElementType; } export interface TabsProps { @@ -20,9 +20,9 @@ export interface TabsProps { } const Tabs = ({className, items, defaultTab}: TabsProps) => { - const [currentTab, setCurrentTab] = useState(defaultTab); + const [currentTab, setCurrentTab] = React.useState(defaultTab); - const activeTabId: string | null = useMemo(() => { + const activeTabId: string | null = React.useMemo(() => { if (currentTab) { return currentTab; } @@ -30,14 +30,14 @@ const Tabs = ({className, items, defaultTab}: TabsProps) => { return items[0].id; }, [currentTab, items]); - const handleClick = useCallback( + const handleClick = React.useCallback( (tabId: string) => () => { setCurrentTab(tabId); }, [], ); - const TabComponent = useMemo(() => { + const TabComponent = React.useMemo(() => { const findTab = items.find(({id}) => id === activeTabId); return findTab?.component; }, [activeTabId, items]); diff --git a/editor-v2/containers/BigOverlay/BigOverlay.tsx b/editor-v2/containers/BigOverlay/BigOverlay.tsx index e88d786762..34d50fefc7 100644 --- a/editor-v2/containers/BigOverlay/BigOverlay.tsx +++ b/editor-v2/containers/BigOverlay/BigOverlay.tsx @@ -1,5 +1,5 @@ import {Stop} from '@gravity-ui/icons'; -import React, {useCallback, useContext, useEffect, useMemo, useState} from 'react'; +import * as React from 'react'; import {usePostMessageAPIListener} from '../../../common/postMessage'; import {IframeContext} from '../../context/iframeContext'; @@ -12,17 +12,17 @@ const b = editorCn('big-overlay'); const BigOverlay = ({className}: {className?: string}) => { const {zoom, manipulateOverlayMode} = useMainEditorStore(); - const {iframeElement} = useContext(IframeContext); - const [mousePosition, setMousePosition] = useState<{x: number; y: number} | undefined>( + const {iframeElement} = React.useContext(IframeContext); + const [mousePosition, setMousePosition] = React.useState<{x: number; y: number} | undefined>( undefined, ); - const [source, setSource] = useState<'main' | 'iframe'>('main'); + const [source, setSource] = React.useState<'main' | 'iframe'>('main'); - const onMouseUp = useCallback(() => { + const onMouseUp = React.useCallback(() => { setMousePosition(undefined); }, []); - const onIframeMouseEvent = useCallback((position: {x: number; y: number}) => { + const onIframeMouseEvent = React.useCallback((position: {x: number; y: number}) => { setMousePosition(position); setSource('iframe'); }, []); @@ -30,7 +30,7 @@ const BigOverlay = ({className}: {className?: string}) => { usePostMessageAPIListener('ON_MOUSE_UP', onMouseUp); usePostMessageAPIListener('ON_MOUSE_MOVE', onIframeMouseEvent); - useEffect(() => { + React.useEffect(() => { const onEditorMouseEvent = (event: MouseEvent) => { setMousePosition({x: event.clientX, y: event.clientY}); setSource('main'); @@ -45,7 +45,7 @@ const BigOverlay = ({className}: {className?: string}) => { }; }, []); - const realPositions = useMemo(() => { + const realPositions = React.useMemo(() => { if (mousePosition) { const {x, y} = mousePosition; const iframeRect = iframeElement?.getClientRects().item(0); diff --git a/editor-v2/containers/BlockConfig/BlockConfig.tsx b/editor-v2/containers/BlockConfig/BlockConfig.tsx index 1ccc59952c..e8f1263563 100644 --- a/editor-v2/containers/BlockConfig/BlockConfig.tsx +++ b/editor-v2/containers/BlockConfig/BlockConfig.tsx @@ -1,5 +1,4 @@ import _ from 'lodash'; -import React from 'react'; import {DynamicFormValue} from '../../../common/types'; import DynamicForm from '../../components/DynamicForm/DynamicForm'; diff --git a/editor-v2/containers/BlocksList/BlocksList.tsx b/editor-v2/containers/BlocksList/BlocksList.tsx index 9461f3154d..b8652f238d 100644 --- a/editor-v2/containers/BlocksList/BlocksList.tsx +++ b/editor-v2/containers/BlocksList/BlocksList.tsx @@ -1,6 +1,6 @@ import {Magnifier, SquareBars} from '@gravity-ui/icons'; import {Card, Icon, TextInput} from '@gravity-ui/uikit'; -import React, {useCallback, useMemo, useState} from 'react'; +import * as React from 'react'; import {ItemConfig} from '../../../common/types'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; @@ -16,16 +16,16 @@ interface BlockGroups { const BlocksList = () => { const {blocks, enableInsertMode} = useMainEditorStore(); - const [search, setSearch] = useState(''); + const [search, setSearch] = React.useState(''); - const onMouseDown = useCallback( + const onMouseDown = React.useCallback( (blockType: string) => { enableInsertMode(blockType); }, [enableInsertMode], ); - const groups = useMemo(() => { + const groups = React.useMemo(() => { return blocks.reduce<BlockGroups>((acc, currentBlock) => { const group = currentBlock.schema.group; if (search && currentBlock.type.indexOf(search) === -1) { @@ -46,7 +46,7 @@ const BlocksList = () => { } return acc; - }, {}); + }, {} as BlockGroups); }, [blocks, search]); return ( diff --git a/editor-v2/containers/Editor/Editor.tsx b/editor-v2/containers/Editor/Editor.tsx index 6753648109..c151506282 100644 --- a/editor-v2/containers/Editor/Editor.tsx +++ b/editor-v2/containers/Editor/Editor.tsx @@ -1,4 +1,4 @@ -import React, {ElementType, useCallback} from 'react'; +import * as React from 'react'; import {Content} from '../../../src/models'; import {Panels} from '../../components/Panels/Panels'; @@ -20,16 +20,16 @@ const b = editorCn('editor'); interface SidebarTabComponent { id: string; title: string; - component: ElementType; + component: React.ElementType; } interface EditorViewProps { onUpdate?: (pageContent: Content) => void; initialUrl: string; disableUrlField?: boolean; componentsConfig?: { - middleTop?: ElementType; - leftTop?: ElementType[]; - rightTop?: ElementType[]; + middleTop?: React.ElementType; + leftTop?: React.ElementType[]; + rightTop?: React.ElementType[]; leftTabs?: SidebarTabComponent[]; rightTabs?: SidebarTabComponent[]; }; @@ -43,7 +43,7 @@ const EditorView = ({componentsConfig = {}}: EditorViewProps) => { // Disable insert mode on any MouseUp event // Maybe should be attached to body - const onMouseUp = useCallback( + const onMouseUp = React.useCallback( (e: React.MouseEvent) => { if (manipulateOverlayMode) { e.preventDefault(); diff --git a/editor-v2/containers/GlobalConfig/GlobalConfig.tsx b/editor-v2/containers/GlobalConfig/GlobalConfig.tsx index f77241a438..f360555308 100644 --- a/editor-v2/containers/GlobalConfig/GlobalConfig.tsx +++ b/editor-v2/containers/GlobalConfig/GlobalConfig.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {DynamicFormValue} from '../../../common/types'; import DynamicForm from '../../components/DynamicForm/DynamicForm'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; diff --git a/editor-v2/containers/MiddleScreen/MiddleScreen.tsx b/editor-v2/containers/MiddleScreen/MiddleScreen.tsx index 5f7e0b8d1e..7ce7b974fa 100644 --- a/editor-v2/containers/MiddleScreen/MiddleScreen.tsx +++ b/editor-v2/containers/MiddleScreen/MiddleScreen.tsx @@ -1,5 +1,5 @@ import {Loader} from '@gravity-ui/uikit'; -import React, {ElementType, useCallback, useContext, useState} from 'react'; +import * as React from 'react'; import {usePostMessageAPIListener} from '../../../common/postMessage'; import {IframeContext} from '../../context/iframeContext'; @@ -13,15 +13,15 @@ const b = editorCn('middle-screen'); interface MiddleScreenProps { className?: string; - CustomTop?: ElementType; + CustomTop?: React.ElementType; } const MiddleScreen = ({className, CustomTop}: MiddleScreenProps) => { const {zoom, initialized} = useMainEditorStore(); - const {url, setIframeElement} = useContext(IframeContext); - const [height, setHeight] = useState(0); + const {url, setIframeElement} = React.useContext(IframeContext); + const [height, setHeight] = React.useState(0); - const onResize = useCallback( + const onResize = React.useCallback( (newHeight: number) => { setHeight(newHeight + 500); }, diff --git a/editor-v2/containers/Overlay/Overlay.tsx b/editor-v2/containers/Overlay/Overlay.tsx index c38643dfcc..759d9fbb0a 100644 --- a/editor-v2/containers/Overlay/Overlay.tsx +++ b/editor-v2/containers/Overlay/Overlay.tsx @@ -1,6 +1,6 @@ import {ChevronDown, ChevronUp, Copy, TrashBin} from '@gravity-ui/icons'; import {Button, Icon} from '@gravity-ui/uikit'; -import React, {useState} from 'react'; +import * as React from 'react'; import {usePostMessageAPIListener} from '../../../common/postMessage'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; @@ -31,9 +31,11 @@ const Overlay = ({className}: OverlayProps) => { manipulateOverlayMode, reorderBlock, } = useMainEditorStore(); - const [insertLineBox, setInsertLineBox] = useState<InsertLineProps | undefined>(undefined); - const [hoverBorders, setHoverBorders] = useState<DOMRect | null>(null); - const [blockBorders, setBlockBorders] = useState<DOMRect | null>(null); + const [insertLineBox, setInsertLineBox] = React.useState<InsertLineProps | undefined>( + undefined, + ); + const [hoverBorders, setHoverBorders] = React.useState<DOMRect | null>(null); + const [blockBorders, setBlockBorders] = React.useState<DOMRect | null>(null); const margin = 0; diff --git a/editor-v2/containers/Source/Source.tsx b/editor-v2/containers/Source/Source.tsx index 176e819e25..ead0aa6c97 100644 --- a/editor-v2/containers/Source/Source.tsx +++ b/editor-v2/containers/Source/Source.tsx @@ -1,6 +1,6 @@ import {ArrowRotateRight} from '@gravity-ui/icons'; import {Button, Icon, TextInput} from '@gravity-ui/uikit'; -import React, {useCallback, useContext} from 'react'; +import * as React from 'react'; import {IframeContext} from '../../context/iframeContext'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; @@ -12,9 +12,9 @@ const b = editorCn('source'); const Source = () => { const {resetInitialize} = useMainEditorStore(); - const {disableUrlField, url, setUrl} = useContext(IframeContext); + const {disableUrlField, url, setUrl} = React.useContext(IframeContext); - const onUpdateUrl = useCallback( + const onUpdateUrl = React.useCallback( (value: string) => { setUrl(value); resetInitialize(); diff --git a/editor-v2/containers/SourceCode/SourceCode.tsx b/editor-v2/containers/SourceCode/SourceCode.tsx index b0b9ec368e..4e8b0de7fb 100644 --- a/editor-v2/containers/SourceCode/SourceCode.tsx +++ b/editor-v2/containers/SourceCode/SourceCode.tsx @@ -1,7 +1,7 @@ import {ArrowDownToSquare} from '@gravity-ui/icons'; import {Button, ClipboardButton, Icon} from '@gravity-ui/uikit'; import yaml from 'js-yaml'; -import React, {useMemo, useState} from 'react'; +import * as React from 'react'; import {Content} from '../../../src/models'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; @@ -19,7 +19,7 @@ interface SourceCodeProps { const SourceCode = ({className, format}: SourceCodeProps) => { const {content, setContent} = useMainEditorStore(); - const [isOpen, setIsOpen] = useState(false); + const [isOpen, setIsOpen] = React.useState(false); const handleUpdate = (tempConfig: string) => { let object: Content | undefined; @@ -42,7 +42,7 @@ const SourceCode = ({className, format}: SourceCodeProps) => { setIsOpen(false); }; - const textContent = useMemo(() => { + const textContent = React.useMemo(() => { return format === 'yaml' ? yaml.dump(content) : JSON.stringify(content, null, 2); }, [format, content]); diff --git a/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.tsx b/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.tsx index 4f5ba2cd1f..4a3224adb9 100644 --- a/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.tsx +++ b/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.tsx @@ -1,5 +1,5 @@ import {Alert, Dialog, TextArea} from '@gravity-ui/uikit'; -import React, {useState} from 'react'; +import * as React from 'react'; import {editorCn} from '../../../utils/cn'; @@ -8,7 +8,7 @@ import './UpdateModal.scss'; const b = editorCn('source-code-update-modal'); export const UpdateModal = ({onClose, onApply, isOpen}) => { - const [tempConfig, setTempConfig] = useState(''); + const [tempConfig, setTempConfig] = React.useState(''); const handleApply = () => { onApply(tempConfig); }; diff --git a/editor-v2/containers/Tree/Tree.tsx b/editor-v2/containers/Tree/Tree.tsx index aa3c48f38b..4e698f7a13 100644 --- a/editor-v2/containers/Tree/Tree.tsx +++ b/editor-v2/containers/Tree/Tree.tsx @@ -1,6 +1,6 @@ import {Copy, TrashBin} from '@gravity-ui/icons'; import {Button, Card, Icon} from '@gravity-ui/uikit'; -import React, {PropsWithChildren, useCallback, useMemo} from 'react'; +import * as React from 'react'; import {ItemConfig} from '../../../common/types'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; @@ -54,12 +54,12 @@ const Item = ({ selected, onCopy, onDelete, -}: PropsWithChildren<ItemProps>) => { - const handleCopy = useCallback(() => { +}: React.PropsWithChildren<ItemProps>) => { + const handleCopy = React.useCallback(() => { onCopy(path); }, [onCopy, path]); - const handleDelete = useCallback(() => { + const handleDelete = React.useCallback(() => { onDelete(path); }, [onDelete, path]); @@ -88,10 +88,10 @@ const Item = ({ ); }; -const Tree = (_p: PropsWithChildren<TreeProps>) => { +const Tree = (_p: React.PropsWithChildren<TreeProps>) => { const {content, resetBlocks, selectedBlock, duplicateBlock, deleteBlock} = useMainEditorStore(); - const selectedBlockPath = useMemo(() => { + const selectedBlockPath = React.useMemo(() => { return generateChildrenPathFromArray(selectedBlock || []); }, [selectedBlock]); diff --git a/editor-v2/containers/ViewSwitches/ViewSwitches.tsx b/editor-v2/containers/ViewSwitches/ViewSwitches.tsx index f04ce59d8c..9ad12ac3e0 100644 --- a/editor-v2/containers/ViewSwitches/ViewSwitches.tsx +++ b/editor-v2/containers/ViewSwitches/ViewSwitches.tsx @@ -1,6 +1,5 @@ import {Minus, Plus} from '@gravity-ui/icons'; import {Button, Icon, Select} from '@gravity-ui/uikit'; -import React from 'react'; import {ZOOM_STEPS} from '../../constants'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; diff --git a/editor-v2/containers/__stories__/Editor.stories.tsx b/editor-v2/containers/__stories__/Editor.stories.tsx index c118f076b2..f06c3746be 100644 --- a/editor-v2/containers/__stories__/Editor.stories.tsx +++ b/editor-v2/containers/__stories__/Editor.stories.tsx @@ -1,5 +1,4 @@ import {Meta, StoryFn} from '@storybook/react'; -import React from 'react'; import {Editor} from '../Editor/Editor'; diff --git a/editor-v2/context/editorStore/MainEditorStoreContext.tsx b/editor-v2/context/editorStore/MainEditorStoreContext.tsx index 7f48605d49..1332029b7b 100644 --- a/editor-v2/context/editorStore/MainEditorStoreContext.tsx +++ b/editor-v2/context/editorStore/MainEditorStoreContext.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {StoreApi} from 'zustand'; import {EditorStore, createEditorStore} from '../../store'; diff --git a/editor-v2/context/editorStore/MainEditorStoreProvider.tsx b/editor-v2/context/editorStore/MainEditorStoreProvider.tsx index 5d3b57306a..e290b9880f 100644 --- a/editor-v2/context/editorStore/MainEditorStoreProvider.tsx +++ b/editor-v2/context/editorStore/MainEditorStoreProvider.tsx @@ -1,4 +1,4 @@ -import React, {PropsWithChildren, useCallback, useContext, useEffect, useRef} from 'react'; +import * as React from 'react'; import {StoreApi} from 'zustand'; import {EditorState} from '../../../common/store'; @@ -9,13 +9,13 @@ import {IframeContext} from '../iframeContext'; import {MainEditorStoreContext} from './MainEditorStoreContext'; -interface MainEditorProviderProps extends PropsWithChildren {} +interface MainEditorProviderProps extends React.PropsWithChildren {} export const MainEditorStoreProvider = ({children}: MainEditorProviderProps) => { - const {iframeElement} = useContext(IframeContext); - const storeRef = useRef<StoreApi<EditorStore>>(); + const {iframeElement} = React.useContext(IframeContext); + const storeRef = React.useRef<StoreApi<EditorStore>>(); - const sendPostMessage = useCallback( + const sendPostMessage = React.useCallback( (data: EditorState) => { const message: StoreSyncMessage = { state: data, @@ -32,7 +32,7 @@ export const MainEditorStoreProvider = ({children}: MainEditorProviderProps) => storeRef.current = createEditorStore(); } - useEffect(() => { + React.useEffect(() => { storeRef.current?.subscribe((state) => { sendPostMessage(removeFn(state)); }); diff --git a/editor-v2/context/iframeContext/IframeContext.tsx b/editor-v2/context/iframeContext/IframeContext.tsx index 95c498befe..c8962762f8 100644 --- a/editor-v2/context/iframeContext/IframeContext.tsx +++ b/editor-v2/context/iframeContext/IframeContext.tsx @@ -2,7 +2,7 @@ * Context for iframe window **/ -import React from 'react'; +import * as React from 'react'; export interface IframeContextProps { iframeElement?: HTMLIFrameElement; diff --git a/editor-v2/context/iframeContext/IframeProvider.tsx b/editor-v2/context/iframeContext/IframeProvider.tsx index c5a0295cee..499850d216 100644 --- a/editor-v2/context/iframeContext/IframeProvider.tsx +++ b/editor-v2/context/iframeContext/IframeProvider.tsx @@ -1,8 +1,8 @@ -import React, {PropsWithChildren, useState} from 'react'; +import * as React from 'react'; import {IframeContext} from './IframeContext'; -interface IframeProviderProps extends PropsWithChildren { +interface IframeProviderProps extends React.PropsWithChildren { initialUrl?: string; disableUrlField?: boolean; } @@ -12,8 +12,8 @@ export const IframeProvider = ({ initialUrl = '', disableUrlField, }: IframeProviderProps) => { - const [iframeElement, setIframeElement] = useState<HTMLIFrameElement>(); - const [url, setUrl] = useState(initialUrl); + const [iframeElement, setIframeElement] = React.useState<HTMLIFrameElement>(); + const [url, setUrl] = React.useState(initialUrl); const setIframeElementFunc = (element: HTMLIFrameElement) => setIframeElement(element); diff --git a/editor-v2/hooks/useEditorTabs.tsx b/editor-v2/hooks/useEditorTabs.tsx index 0fa30e8ae2..fb2199017a 100644 --- a/editor-v2/hooks/useEditorTabs.tsx +++ b/editor-v2/hooks/useEditorTabs.tsx @@ -1,4 +1,4 @@ -import {useMemo} from 'react'; +import * as React from 'react'; import Tabs, {TabsItemProps} from '../components/Tabs/Tabs'; import BlockConfig from '../containers/BlockConfig/BlockConfig'; @@ -14,7 +14,7 @@ export const useEditorTabs = ({ leftTabs?: TabsItemProps[]; rightTabs?: TabsItemProps[]; }) => { - const tabs = useMemo( + const tabs = React.useMemo( () => ({ left: [ { @@ -65,7 +65,6 @@ export const useEditorTabs = ({ id: 'block-config', title: 'INPUTS', component: BlockConfig, - position: 'right', }, { id: 'source-code-yaml', diff --git a/editor-v2/hooks/useMainEditorStore.ts b/editor-v2/hooks/useMainEditorStore.ts index 5ff0baf4fe..541ff6540b 100644 --- a/editor-v2/hooks/useMainEditorStore.ts +++ b/editor-v2/hooks/useMainEditorStore.ts @@ -1,9 +1,9 @@ -import {useContext} from 'react'; +import * as React from 'react'; import {useStore} from 'zustand'; import {MainEditorStoreContext} from '../context/editorStore'; export const useMainEditorStore = () => { - const {state} = useContext(MainEditorStoreContext); + const {state} = React.useContext(MainEditorStoreContext); return useStore(state); }; diff --git a/editor-v2/hooks/usePostMessageEvents.ts b/editor-v2/hooks/usePostMessageEvents.ts index 1b73e0cd86..cc80008433 100644 --- a/editor-v2/hooks/usePostMessageEvents.ts +++ b/editor-v2/hooks/usePostMessageEvents.ts @@ -1,4 +1,4 @@ -import {useContext} from 'react'; +import * as React from 'react'; import {requestActionPostMessage} from '../../common/postMessage'; import {ActionMessageTypes} from '../../common/types'; @@ -12,7 +12,7 @@ interface UsePostMessageRequestReturn { } export function usePostMessageEvents(): UsePostMessageRequestReturn { - const {iframeElement} = useContext(IframeContext); + const {iframeElement} = React.useContext(IframeContext); return { requestPostMessage: (action, data) => { diff --git a/editor-v2/styles/root.scss b/editor-v2/styles/root.scss index 93d3ef75a4..0e2334f3aa 100644 --- a/editor-v2/styles/root.scss +++ b/editor-v2/styles/root.scss @@ -1,7 +1,12 @@ .g-root { + --g-color-base-brand: var(--g-color-private-black-550-solid); + --g-color-base-brand-hover: var(--g-color-private-black-600-solid); + --g-color-base-selection: var(--g-color-private-black-200); + --g-color-base-selection-hover: var(--g-color-private-black-300); --g-color-base-brand: var(--g-color-text-primary); --g-color-base-brand-hover: var(--g-color-text-complementary); --g-color-text-brand-contrast: var(--g-color-text-light-primary); --g-color-line-brand: var(--g-color-text-primary); --g-color-text-brand: var(--g-color-private-brand-700-solid); + --g-color-text-brand-heavy: var(--g-color-private-black-700-solid); } diff --git a/playground/src/app/page.tsx b/playground/src/app/page.tsx index 8886fbcdd1..ef1feacef5 100644 --- a/playground/src/app/page.tsx +++ b/playground/src/app/page.tsx @@ -1,7 +1,7 @@ 'use client'; import {ThemeProvider} from '@gravity-ui/uikit'; import block from 'bem-cn-lite'; -import React, {useEffect, useState} from 'react'; +import * as React from 'react'; import {Editor} from '../../../editor-v2'; import Source from '../../../editor-v2/containers/Source/Source'; @@ -26,9 +26,9 @@ export const COMPONENTS_CONFIG = { }; export default function Home() { - const [initialUrl, setInitialUrl] = useState<string>(''); + const [initialUrl, setInitialUrl] = React.useState<string>(''); - useEffect(() => { + React.useEffect(() => { if (typeof window !== 'undefined') { setInitialUrl(window.location.origin + '/pc'); } diff --git a/playground/src/app/pc-2/page.tsx b/playground/src/app/pc-2/page.tsx index 9765c3c46e..319e2bcf8c 100644 --- a/playground/src/app/pc-2/page.tsx +++ b/playground/src/app/pc-2/page.tsx @@ -1,7 +1,6 @@ 'use client'; -import React from 'react'; -import {PageConstructor, PageConstructorProvider} from '../../../../src'; +import {NavigationData, PageConstructor, PageConstructorProvider} from '../../../../src'; import content from './content.json'; import navigation from './navigation.json'; @@ -10,7 +9,7 @@ import './styles.scss'; export default function Home() { return ( <PageConstructorProvider projectSettings={{disableCompress: true}}> - <PageConstructor content={content} navigation={navigation} /> + <PageConstructor content={content} navigation={navigation as NavigationData} /> </PageConstructorProvider> ); } diff --git a/playground/src/app/pc/page.tsx b/playground/src/app/pc/page.tsx index 88c94a9f2e..5a7dbab8e4 100644 --- a/playground/src/app/pc/page.tsx +++ b/playground/src/app/pc/page.tsx @@ -1,5 +1,4 @@ 'use client'; -import React from 'react'; import {PageConstructor, PageConstructorProvider} from '../../../../src'; diff --git a/src/blocks/TestEditorBlock/TestEditorBlock.tsx b/src/blocks/TestEditorBlock/TestEditorBlock.tsx index 7888c5cc1e..a9922bd37c 100644 --- a/src/blocks/TestEditorBlock/TestEditorBlock.tsx +++ b/src/blocks/TestEditorBlock/TestEditorBlock.tsx @@ -1,10 +1,10 @@ -import React, {PropsWithChildren} from 'react'; +import * as React from 'react'; import {block} from '../../utils'; const b = block('test-editor-block'); -export interface TestEditorBlockProps extends PropsWithChildren {} +export interface TestEditorBlockProps extends React.PropsWithChildren {} export const TestEditorBlock = (props: TestEditorBlockProps) => { return <div className={b()}>{JSON.stringify(props, null, 2)}</div>; diff --git a/src/components/editor/ChildrenWrap/ChildrenWrap.tsx b/src/components/editor/ChildrenWrap/ChildrenWrap.tsx index f4f212cc85..068ece91b4 100644 --- a/src/components/editor/ChildrenWrap/ChildrenWrap.tsx +++ b/src/components/editor/ChildrenWrap/ChildrenWrap.tsx @@ -1,4 +1,4 @@ -import React, {PropsWithChildren, ReactNode} from 'react'; +import * as React from 'react'; import {usePCEditorItemWrap} from '../../../hooks/usePCEditorItemWrap'; import {block} from '../../../utils'; @@ -7,8 +7,8 @@ import './ChildrenWrap.scss'; const b = block('children-wrap'); -export interface ChildrenWrapProps extends PropsWithChildren { - checkChildren?: ReactNode; +export interface ChildrenWrapProps extends React.PropsWithChildren { + checkChildren?: React.ReactNode; } const ChildrenWrap = ({children}: ChildrenWrapProps) => { diff --git a/src/components/editor/ItemWrap/ItemWrap.tsx b/src/components/editor/ItemWrap/ItemWrap.tsx index 90e98ca0b4..cca5b199cd 100644 --- a/src/components/editor/ItemWrap/ItemWrap.tsx +++ b/src/components/editor/ItemWrap/ItemWrap.tsx @@ -1,4 +1,4 @@ -import React, {PropsWithChildren} from 'react'; +import * as React from 'react'; import {usePCEditorItemWrap} from '../../../hooks/usePCEditorItemWrap'; import {block} from '../../../utils'; @@ -7,7 +7,7 @@ import './ItemWrap.scss'; const b = block('item-wrap'); -export interface ItemWrapProps extends PropsWithChildren { +export interface ItemWrapProps extends React.PropsWithChildren { index: number; } diff --git a/src/constructor-items.ts b/src/constructor-items.ts index 588118c1b3..d4930a5b78 100644 --- a/src/constructor-items.ts +++ b/src/constructor-items.ts @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import { BannerBlock, diff --git a/src/containers/PageConstructor/PageConstructor.tsx b/src/containers/PageConstructor/PageConstructor.tsx index 58412bec76..b0eb1fa589 100644 --- a/src/containers/PageConstructor/PageConstructor.tsx +++ b/src/containers/PageConstructor/PageConstructor.tsx @@ -1,4 +1,4 @@ -import {ReactNode, useContext, useMemo, useState} from 'react'; +import * as React from 'react'; import '@diplodoc/transform/dist/js/yfm'; import {ThemeProvider} from '@gravity-ui/uikit'; @@ -43,7 +43,7 @@ export interface PageConstructorProps { content?: PageContent; shouldRenderBlock?: ShouldRenderBlock; custom?: CustomConfig; - renderMenu?: () => ReactNode; + renderMenu?: () => React.ReactNode; navigation?: NavigationData; isBranded?: boolean; microdata?: { @@ -62,7 +62,7 @@ export const Constructor = (props: PageConstructorProps) => { microdata, } = props; - const [content, setContent] = useState<Content>({blocks, background, navigation}); + const [content, setContent] = React.useState<Content>({blocks, background, navigation}); const theme = useTheme(); @@ -71,7 +71,7 @@ export const Constructor = (props: PageConstructorProps) => { usePCEditorInitializeEvents({initialContent: {blocks, background, navigation}, setContent}); - const {context} = useMemo( + const {context} = React.useMemo( () => ({ context: { blockTypes: [...BlockTypes, ...getCustomTypes(['blocks', 'headers'], custom)], @@ -127,7 +127,7 @@ export const Constructor = (props: PageConstructorProps) => { }; export const PageConstructor = (props: PageConstructorProps) => { - const {isAnimationEnabled = true} = useContext(ProjectSettingsContext); + const {isAnimationEnabled = true} = React.useContext(ProjectSettingsContext); const {content: {animated = isAnimationEnabled} = {}, ...rest} = props; return ( diff --git a/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx b/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx index 27f8a214ac..469f8ab48b 100644 --- a/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx +++ b/src/containers/PageConstructor/components/ConstructorBlock/ConstructorBlock.tsx @@ -1,4 +1,4 @@ -import React, {useMemo} from 'react'; +import * as React from 'react'; import pick from 'lodash/pick'; @@ -24,7 +24,7 @@ export const ConstructorBlock = ({ const {blockRef, adminBlockMouseEvents} = usePCEditorItemWrap(index); const {type} = data; - const blockBaseProps = useMemo( + const blockBaseProps = React.useMemo( () => pick(data, ['anchor', 'visible', 'resetPaddings', 'indent']), [data], ); diff --git a/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx b/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx new file mode 100644 index 0000000000..5d7de0742b --- /dev/null +++ b/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx @@ -0,0 +1,87 @@ +import * as React from 'react'; + +import _ from 'lodash'; + +import {usePCEditorStore} from '../../../../../context/editorStoreContext'; +import {sendEventPostMessage} from '../../../../../hooks/usePostMessageAPI'; +import {getCursorPositionOverElement} from '../../../../../utils/editor'; + +const useEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement) => { + const {selectedBlock} = usePCEditorStore(); + + const onMouseUp = React.useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + if (element) { + const rect = element.getClientRects().item(0); + if (rect) { + const position = getCursorPositionOverElement(rect, e); + sendEventPostMessage('ON_MOUSE_UP', {path: arrayIndex, rect, position}); + } + } + }, + [arrayIndex, element], + ); + + const onMouseMove = React.useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + if (element) { + const rect = element.getClientRects().item(0); + if (rect) { + const position = getCursorPositionOverElement(rect, e); + sendEventPostMessage('ON_HOVER_BLOCK', {rect, position}); + } + } + }, + [element], + ); + + const onMouseLeave = React.useCallback(() => { + if (element) { + const rect = element.getClientRects().item(0); + if (rect) { + sendEventPostMessage('ON_HOVER_BLOCK', {}); + } + } + }, [element]); + + const onClick = React.useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + if (element) { + const rect = element.getClientRects().item(0); + if (rect) { + sendEventPostMessage('ON_CLICK_BLOCK', {rect, path: arrayIndex}); + } + } + }, + [arrayIndex, element], + ); + + const onResize = React.useCallback(() => { + if (element && _.isEqual(selectedBlock, arrayIndex)) { + const rect = element.getClientRects().item(0); + if (rect) { + sendEventPostMessage('ON_RESIZE_BLOCK', {rect}); + } + } + }, [arrayIndex, element, selectedBlock]); + + React.useEffect(() => { + window.addEventListener('resize', onResize); + + return () => { + window.removeEventListener('resize', onResize); + }; + }, [element, onResize]); + + return { + onClick, + onMouseMove, + onMouseUp, + onMouseLeave, + }; +}; + +export default useEditorBlockMouseEvents; diff --git a/src/context/editorStoreContext/PCEditorStoreContext.tsx b/src/context/editorStoreContext/PCEditorStoreContext.tsx index e957bd6f99..64e0b60516 100644 --- a/src/context/editorStoreContext/PCEditorStoreContext.tsx +++ b/src/context/editorStoreContext/PCEditorStoreContext.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {StoreApi} from 'zustand'; diff --git a/src/context/editorStoreContext/PCEditorStoreProvider.tsx b/src/context/editorStoreContext/PCEditorStoreProvider.tsx index a6f0add576..9de324e5a7 100644 --- a/src/context/editorStoreContext/PCEditorStoreProvider.tsx +++ b/src/context/editorStoreContext/PCEditorStoreProvider.tsx @@ -1,4 +1,4 @@ -import React, {PropsWithChildren, useCallback, useEffect, useRef} from 'react'; +import * as React from 'react'; import {StoreApi} from 'zustand'; @@ -7,18 +7,18 @@ import {StoreSyncMessage} from '../../../common/types'; import {PCEditorStoreContext} from './PCEditorStoreContext'; -interface PCEditorStoreProviderProps extends PropsWithChildren {} +interface PCEditorStoreProviderProps extends React.PropsWithChildren {} export const PCEditorStoreProvider = ({children}: PCEditorStoreProviderProps) => { - const storeRef = useRef<StoreApi<EditorState>>(); + const storeRef = React.useRef<StoreApi<EditorState>>(); - const syncStore = useCallback((message: StoreSyncMessage) => { + const syncStore = React.useCallback((message: StoreSyncMessage) => { if (storeRef.current && message.state) { storeRef.current.setState(message.state); } }, []); - useEffect(() => { + React.useEffect(() => { const onMessage = (e: MessageEvent) => { const message = e.data as StoreSyncMessage; syncStore(message); diff --git a/src/editor-v2/components/BigOverlay/BigOverlay.tsx b/src/editor-v2/components/BigOverlay/BigOverlay.tsx new file mode 100644 index 0000000000..25eba68bc3 --- /dev/null +++ b/src/editor-v2/components/BigOverlay/BigOverlay.tsx @@ -0,0 +1,85 @@ +import * as React from 'react'; + +import {Stop} from '@gravity-ui/icons'; + +import {usePostMessageAPIListener} from '../../../common/postMessage'; +import {ClassNameProps} from '../../../models'; +import {block} from '../../../utils'; +import {IframeContext} from '../../context/iframeContext'; +import {useMainEditorStore} from '../../hooks/useMainEditorStore'; + +import './BigOverlay.scss'; + +const b = block('big-overlay'); + +interface BigOverlayProps extends ClassNameProps {} + +const BigOverlay: React.FC<BigOverlayProps> = ({className}) => { + const {zoom, manipulateOverlayMode} = useMainEditorStore(); + const {iframeElement} = useContext(IframeContext); + const [mousePosition, setMousePosition] = useState<{x: number; y: number} | undefined>( + undefined, + ); + const [source, setSource] = useState<'main' | 'iframe'>('main'); + + const onMouseUp = useCallback(() => { + setMousePosition(undefined); + }, []); + + const onIframeMouseEvent = useCallback((position: {x: number; y: number}) => { + setMousePosition(position); + setSource('iframe'); + }, []); + + usePostMessageAPIListener('ON_MOUSE_UP', onMouseUp); + usePostMessageAPIListener('ON_MOUSE_MOVE', onIframeMouseEvent); + + useEffect(() => { + const onEditorMouseEvent = (event: MouseEvent) => { + setMousePosition({x: event.clientX, y: event.clientY}); + setSource('main'); + }; + + document.addEventListener('mousemove', onEditorMouseEvent); + document.addEventListener('mousedown', onEditorMouseEvent); + + return () => { + document.removeEventListener('mousemove', onEditorMouseEvent); + document.removeEventListener('mousedown', onEditorMouseEvent); + }; + }, []); + + const realPositions = useMemo(() => { + if (mousePosition) { + const {x, y} = mousePosition; + const iframeRect = iframeElement?.getClientRects().item(0); + if (iframeRect) { + const zoomedX = (x * zoom) / 100; + const zoomedY = (y * zoom) / 100; + const newX = source === 'main' ? x : zoomedX + iframeRect.x; + const newY = source === 'main' ? y : zoomedY + iframeRect.y; + return {x: newX, y: newY}; + } + } + + return undefined; + }, [mousePosition, source, iframeElement, zoom]); + + return ( + <div className={b(null, className)}> + {realPositions && manipulateOverlayMode ? ( + <div + className={b('border')} + style={{ + top: realPositions.y, + left: realPositions.x, + }} + > + <Stop height={20} width={20} /> + </div> + ) : null} + </div> + ); +}; + +export default BigOverlay; diff --git a/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx b/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx new file mode 100644 index 0000000000..994d825890 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx @@ -0,0 +1,158 @@ +import * as React from 'react'; + +import {Plus} from '@gravity-ui/icons'; +import {Button, Card, Icon} from '@gravity-ui/uikit'; + +import {ArrayObjectInput, ArrayTextInput, DynamicFormValue} from '../../../../../common/types'; +import {ClassNameProps, PageContent} from '../../../../../models'; +import {block} from '../../../../../utils'; +import {removeFromArray, swapArrayItems} from '../../../../utils'; +import DynamicForm from '../../DynamicForm'; +import FieldBase from '../../FieldBase/FieldBase'; +import Text from '../Text/Text'; + +import ItemButton from './ItemButton/ItemButton'; + +import './Array.scss'; + +const b = block('array-dynamic-field'); + +type ArrayInput = ArrayTextInput | ArrayObjectInput; + +interface ArrayFieldProps extends ClassNameProps { + title: string; + values: Array<DynamicFormValue>; + onUpdate: (key: string, value: DynamicFormValue) => void; + blockConfig: ArrayInput; +} + +const ArrayDynamicField: React.FC<ArrayFieldProps> = (props) => { + const {title, values, onUpdate, className, blockConfig} = props; + + const haveItems = values && Array.isArray(values) && values.length; + + const onAddItem = useCallback(() => { + if (blockConfig.arrayType === 'text') { + onUpdate('', haveItems ? [...values, ''] : ['']); + } else if (blockConfig.arrayType === 'object') { + onUpdate('', haveItems ? [...values, {}] : [{}]); + } + }, [blockConfig.arrayType, haveItems, onUpdate, values]); + + const onDeleteItem = useCallback( + (index: number) => { + if (Array.isArray(values)) { + const newArray = removeFromArray(values, index); + onUpdate('', newArray); + } + }, + [onUpdate, values], + ); + + const onReorderItem = useCallback( + (index: number, placement: 'up' | 'down') => { + if (Array.isArray(values)) { + const newArray = swapArrayItems( + values, + index, + placement === 'up' ? index - 1 : index + 1, + ); + onUpdate('', newArray); + } + }, + [onUpdate, values], + ); + + const renderInput = useCallback( + (value: DynamicFormValue, index: number) => { + const arrayItemButton = ( + <ItemButton + onRemove={() => onDeleteItem(index)} + onReorderUp={() => onReorderItem(index, 'up')} + onReorderDown={() => onReorderItem(index, 'down')} + disableReorderUp={index === 0} + disableReorderDown={Boolean(haveItems) && values.length === index + 1} + /> + ); + + switch (blockConfig.arrayType) { + case 'text': { + return ( + <div className={b('row')}> + <Text + className={b('row-field')} + value={String(value)} + onUpdate={(updateValue) => onUpdate(`[${index}]`, updateValue)} + onRefresh={(updatedValue) => onUpdate('', updatedValue)} + /> + {arrayItemButton} + </div> + ); + } + case 'object': { + if (!blockConfig.properties) { + return null; + } + return ( + <Card key={index} className={b('card')}> + <div className={`${b('row')} ${b('card-head')}`}> + <div className={b('row-title')}>#{index}</div> + {arrayItemButton} + </div> + <DynamicForm + contentConfig={value as PageContent} + blockConfig={blockConfig.properties} + onUpdate={(key, updateValue) => + onUpdate(`[${index}].${key}`, updateValue) + } + /> + </Card> + ); + } + default: { + return null; + } + } + }, + [blockConfig, haveItems, onDeleteItem, onReorderItem, onUpdate, values], + ); + + const renderInputs = useCallback(() => { + if (haveItems) { + const renderItems = values + .map(renderInput) + .filter(Boolean) as unknown as React.ReactNode[]; + return ( + <React.Fragment> + {renderItems} + <Button className={b('add-button')} onClick={onAddItem}> + <Icon data={Plus} /> + {blockConfig.buttonText} + </Button> + </React.Fragment> + ); + } else { + return ( + <div className={b('empty')}> + <Button className={b('add-button')} onClick={onAddItem}> + <Icon data={Plus} /> + Please, add new item + </Button> + </div> + ); + } + }, [blockConfig.buttonText, haveItems, onAddItem, renderInput, values]); + + return ( + <FieldBase + title={title} + className={b(null, className)} + onRefresh={(value) => onUpdate('', value)} + expandable + > + <Card className={b('card')}>{renderInputs()}</Card> + </FieldBase> + ); +}; + +export default ArrayDynamicField; diff --git a/src/editor/components/CodeEditor/CodeEditor.tsx b/src/editor/components/CodeEditor/CodeEditor.tsx index 168bdce525..d39f84822f 100644 --- a/src/editor/components/CodeEditor/CodeEditor.tsx +++ b/src/editor/components/CodeEditor/CodeEditor.tsx @@ -1,4 +1,4 @@ -import {useCallback, useContext, useState} from 'react'; +import * as React from 'react'; import {ChevronsCollapseUpRight, ChevronsExpandUpRight} from '@gravity-ui/icons'; import {Button, Icon} from '@gravity-ui/uikit'; import debounce from 'lodash/debounce'; @@ -32,11 +32,11 @@ export const CodeEditor = ({ onFullscreenModeOnUpdate, code, }: CodeEditorProps) => { - const [message, setMessage] = useState(() => validator(code)); - const {theme = Theme.Light} = useContext(EditorContext); + const [message, setMessage] = React.useState(() => validator(code)); + const {theme = Theme.Light} = React.useContext(EditorContext); // eslint-disable-next-line react-hooks/exhaustive-deps - const onChangeWithValidation = useCallback( + const onChangeWithValidation = React.useCallback( debounce((newCode: string) => { const validationResult = validator(newCode); diff --git a/src/hooks/usePCEditorBlockMouseEvents.ts b/src/hooks/usePCEditorBlockMouseEvents.ts index d908f584d1..9424223718 100644 --- a/src/hooks/usePCEditorBlockMouseEvents.ts +++ b/src/hooks/usePCEditorBlockMouseEvents.ts @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect} from 'react'; +import * as React from 'react'; import _ from 'lodash'; @@ -10,7 +10,7 @@ import {sendEventPostMessage} from './usePostMessageAPI'; const usePCEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement) => { const {selectedBlock} = usePCEditorStore(); - const onMouseUp = useCallback( + const onMouseUp = React.useCallback( (e: React.MouseEvent) => { e.stopPropagation(); if (element) { @@ -24,7 +24,7 @@ const usePCEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement [arrayIndex, element], ); - const onMouseMove = useCallback( + const onMouseMove = React.useCallback( (e: React.MouseEvent) => { e.stopPropagation(); if (element) { @@ -38,7 +38,7 @@ const usePCEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement [element], ); - const onMouseLeave = useCallback(() => { + const onMouseLeave = React.useCallback(() => { if (element) { const rect = element.getClientRects().item(0); if (rect) { @@ -47,7 +47,7 @@ const usePCEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement } }, [element]); - const onClick = useCallback( + const onClick = React.useCallback( (e: React.MouseEvent) => { e.stopPropagation(); if (element) { @@ -60,7 +60,7 @@ const usePCEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement [arrayIndex, element], ); - const onResize = useCallback(() => { + const onResize = React.useCallback(() => { if (element && _.isEqual(selectedBlock, arrayIndex)) { const rect = element.getClientRects().item(0); if (rect) { @@ -69,7 +69,7 @@ const usePCEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement } }, [arrayIndex, element, selectedBlock]); - useEffect(() => { + React.useEffect(() => { window.addEventListener('resize', onResize); return () => { diff --git a/src/hooks/usePCEditorInitializeEvents.ts b/src/hooks/usePCEditorInitializeEvents.ts index 9dec24459c..69ec825770 100644 --- a/src/hooks/usePCEditorInitializeEvents.ts +++ b/src/hooks/usePCEditorInitializeEvents.ts @@ -1,4 +1,4 @@ -import {useCallback, useEffect} from 'react'; +import * as React from 'react'; import {JSONSchemaType} from 'ajv'; import _ from 'lodash'; @@ -23,7 +23,7 @@ export const usePCEditorInitializeEvents = ({ }: UseEditorInitializeProps) => { const {manipulateOverlayMode, initialized, content} = usePCEditorStore(); - useEffect(() => { + React.useEffect(() => { if (initialized) { setContent(content); } @@ -46,14 +46,14 @@ export const usePCEditorInitializeEvents = ({ }); }); - const onResize = useCallback(() => { + const onResize = React.useCallback(() => { const height = document.documentElement.getBoundingClientRect().height; sendEventPostMessage('ON_RESIZE', {height}); }, []); new ResizeObserver(onResize).observe(document.body); - useEffect(() => { + React.useEffect(() => { const onMouseUp = () => { sendEventPostMessage('ON_MOUSE_UP', {}); }; @@ -78,7 +78,7 @@ export const usePCEditorInitializeEvents = ({ }; }, [manipulateOverlayMode, onResize]); - useEffect(() => { + React.useEffect(() => { const height = document.documentElement.getBoundingClientRect().height; sendEventPostMessage('ON_INIT', {height}); }, []); diff --git a/src/hooks/usePCEditorItemWrap.ts b/src/hooks/usePCEditorItemWrap.ts index c05568445c..6ab498a313 100644 --- a/src/hooks/usePCEditorItemWrap.ts +++ b/src/hooks/usePCEditorItemWrap.ts @@ -1,19 +1,19 @@ -import {useCallback, useContext, useState} from 'react'; +import * as React from 'react'; import {BlockIdContext} from '../context/blockIdContext'; import usePCEditorBlockMouseEvents from './usePCEditorBlockMouseEvents'; export function usePCEditorItemWrap(index = 0) { - const [element, setElement] = useState<HTMLElement | undefined>(); + const [element, setElement] = React.useState<HTMLElement | undefined>(); - const blockRef = useCallback((node: HTMLElement | null) => { + const blockRef = React.useCallback((node: HTMLElement | null) => { if (node !== null) { setElement(node); } }, []); - const parentBlockId = useContext(BlockIdContext); + const parentBlockId = React.useContext(BlockIdContext); const adminBlockMouseEvents = usePCEditorBlockMouseEvents([...parentBlockId, index], element); return {adminBlockMouseEvents, blockRef}; diff --git a/src/hooks/usePCEditorStore.ts b/src/hooks/usePCEditorStore.ts index 3c9cdf851e..6e70148c94 100644 --- a/src/hooks/usePCEditorStore.ts +++ b/src/hooks/usePCEditorStore.ts @@ -1,10 +1,10 @@ -import {useContext} from 'react'; +import * as React from 'react'; import {useStore} from 'zustand'; import {PCEditorStoreContext} from '../context/editorStoreContext'; export const usePCEditorStore = () => { - const {state} = useContext(PCEditorStoreContext); + const {state} = React.useContext(PCEditorStoreContext); return useStore(state); }; diff --git a/src/hooks/usePostMessageAPI.ts b/src/hooks/usePostMessageAPI.ts index 9c1953c2a9..7d5c33c695 100644 --- a/src/hooks/usePostMessageAPI.ts +++ b/src/hooks/usePostMessageAPI.ts @@ -1,4 +1,4 @@ -import {useEffect} from 'react'; +import * as React from 'react'; import {PostMessageAPIMessage} from '../../common/types'; import {ActionMessageTypes, EventMessageTypes} from '../../common/types/actions'; @@ -36,7 +36,7 @@ export function useInternalPostMessageAPIListener<K extends keyof ActionMessageT action: K, callback: (data: ActionMessageTypes[K]) => void, ) { - useEffect(() => { + React.useEffect(() => { return listenPostMessageActions(action, callback); }, [action, callback]); } diff --git a/src/utils/editor.ts b/src/utils/editor.ts index 58cd311cef..b0cd861c5b 100644 --- a/src/utils/editor.ts +++ b/src/utils/editor.ts @@ -1,6 +1,9 @@ -import {MouseEvent} from 'react'; +import * as React from 'react'; -export const getCursorPositionOverElement = (elementRect: DOMRect, mouseEvent: MouseEvent) => { +export const getCursorPositionOverElement = ( + elementRect: DOMRect, + mouseEvent: React.MouseEvent, +) => { const cursorPositionY = elementRect.height - (mouseEvent.clientY - elementRect.y); const cursorPositionX = elementRect.width - (mouseEvent.clientX - elementRect.x); const cursorRatioY = elementRect.height / 2 / cursorPositionY; From c5c738be44a46c7cfd6a115ea3fe083ee7d7a73f Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Sat, 15 Mar 2025 10:22:27 +0300 Subject: [PATCH 024/118] fix: lint errors --- common/store.ts | 4 +- common/types/actions.ts | 4 +- common/types/forms.ts | 13 +- editor-v2/containers/Editor/Editor.tsx | 4 +- .../containers/SourceCode/SourceCode.tsx | 6 +- editor-v2/store.ts | 4 +- .../PageConstructor/PageConstructor.tsx | 8 +- .../hooks/useEditorBlockMouseEvents.tsx | 2 +- .../ConstructorLoadable.tsx | 2 +- .../components/BigOverlay/BigOverlay.tsx | 85 ---------- .../DynamicForm/Fields/Array/Array.tsx | 158 ------------------ src/hooks/usePCEditorInitializeEvents.ts | 6 +- src/models/constructor.ts | 2 +- .../components/Navigation/Navigation.tsx | 4 +- src/sub-blocks/BackgroundCard/dynamic-form.ts | 2 +- 15 files changed, 36 insertions(+), 268 deletions(-) delete mode 100644 src/editor-v2/components/BigOverlay/BigOverlay.tsx delete mode 100644 src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx diff --git a/common/store.ts b/common/store.ts index 820ee28115..48befec430 100644 --- a/common/store.ts +++ b/common/store.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {Content} from '../src/models'; +import {PageContentWithNavigation} from '../src/models'; import {ConfigInput, ItemConfig} from './types'; import {initializeStore} from './utils'; @@ -13,7 +13,7 @@ export interface EditorState { selectedBlock?: number[]; initialized: boolean; - content: Content; + content: PageContentWithNavigation; blocks: Array<ItemConfig>; subBlocks: Array<ItemConfig>; global: Array<ConfigInput>; diff --git a/common/types/actions.ts b/common/types/actions.ts index 16114a740b..2bde27f542 100644 --- a/common/types/actions.ts +++ b/common/types/actions.ts @@ -1,4 +1,4 @@ -import {Content} from '../../src/models'; +import {PageContentWithNavigation} from '../../src/models'; import {EditorState} from '../store'; export type MessageTypes = EventMessageTypes & ActionMessageTypes; @@ -12,7 +12,7 @@ export type EventMessageTypes = { ON_CLICK_BLOCK: {path: number[]; rect: DOMRect}; ON_RESIZE_BLOCK: {rect: DOMRect}; ON_SUPPORTED_BLOCKS: Pick<EditorState, 'blocks' | 'subBlocks' | 'global'>; - ON_INITIAL_CONTENT: Content; + ON_INITIAL_CONTENT: PageContentWithNavigation; }; export type ActionMessageTypes = { diff --git a/common/types/forms.ts b/common/types/forms.ts index 230d29f3b7..190d349d47 100644 --- a/common/types/forms.ts +++ b/common/types/forms.ts @@ -1,6 +1,13 @@ -import {Content} from '../../src/models'; - -export type DynamicFormValue = string | number | [] | object | boolean | Content | undefined; +import {PageContentWithNavigation} from '../../src/models'; + +export type DynamicFormValue = + | string + | number + | [] + | object + | boolean + | PageContentWithNavigation + | undefined; export interface BlockConfig { name: string; diff --git a/editor-v2/containers/Editor/Editor.tsx b/editor-v2/containers/Editor/Editor.tsx index c151506282..ae3ac6820d 100644 --- a/editor-v2/containers/Editor/Editor.tsx +++ b/editor-v2/containers/Editor/Editor.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import {Content} from '../../../src/models'; +import {PageContentWithNavigation} from '../../../src/models'; import {Panels} from '../../components/Panels/Panels'; import {Sidebar} from '../../components/Sidebar/Sidebar'; import StoreViewer from '../../components/StoreViewer/StoreViewer'; @@ -23,7 +23,7 @@ interface SidebarTabComponent { component: React.ElementType; } interface EditorViewProps { - onUpdate?: (pageContent: Content) => void; + onUpdate?: (pageContent: PageContentWithNavigation) => void; initialUrl: string; disableUrlField?: boolean; componentsConfig?: { diff --git a/editor-v2/containers/SourceCode/SourceCode.tsx b/editor-v2/containers/SourceCode/SourceCode.tsx index 4e8b0de7fb..bc64851358 100644 --- a/editor-v2/containers/SourceCode/SourceCode.tsx +++ b/editor-v2/containers/SourceCode/SourceCode.tsx @@ -3,7 +3,7 @@ import {Button, ClipboardButton, Icon} from '@gravity-ui/uikit'; import yaml from 'js-yaml'; import * as React from 'react'; -import {Content} from '../../../src/models'; +import {PageContentWithNavigation} from '../../../src/models'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; @@ -22,13 +22,13 @@ const SourceCode = ({className, format}: SourceCodeProps) => { const [isOpen, setIsOpen] = React.useState(false); const handleUpdate = (tempConfig: string) => { - let object: Content | undefined; + let object: PageContentWithNavigation | undefined; try { if (tempConfig.trim().startsWith('{') && tempConfig.trim().endsWith('}')) { object = JSON.parse(tempConfig); } else { - object = yaml.load(tempConfig) as Content; + object = yaml.load(tempConfig) as PageContentWithNavigation; } } catch { // eslint-disable-next-line no-console diff --git a/editor-v2/store.ts b/editor-v2/store.ts index 48c3aaf91d..46fa4e17da 100644 --- a/editor-v2/store.ts +++ b/editor-v2/store.ts @@ -3,7 +3,7 @@ import _ from 'lodash'; import {EditorState, initialStore} from '../common/store'; import {DynamicFormValue} from '../common/types'; import {initializeStore} from '../common/utils'; -import {ConstructorBlock, Content} from '../src/models'; +import {ConstructorBlock, PageContentWithNavigation} from '../src/models'; import {ZOOM_STEPS} from './constants'; import { @@ -25,7 +25,7 @@ export interface EditorMethods { increaseZoom(): void; decreaseZoom(): void; setConfig(data: Pick<EditorState, 'blocks' | 'subBlocks' | 'global'>): void; - setContent(data: Content): void; + setContent(data: PageContentWithNavigation): void; insertBlock(path: number[], blockType: string, position?: 'prepend' | 'append'): void; enableInsertMode(blockType: string): void; enableReorderMode(path: number[]): void; diff --git a/src/containers/PageConstructor/PageConstructor.tsx b/src/containers/PageConstructor/PageConstructor.tsx index b0eb1fa589..6d308eae71 100644 --- a/src/containers/PageConstructor/PageConstructor.tsx +++ b/src/containers/PageConstructor/PageConstructor.tsx @@ -15,13 +15,13 @@ import {usePCEditorInitializeEvents} from '../../hooks/usePCEditorInitializeEven import {usePCEditorStore} from '../../hooks/usePCEditorStore'; import { BlockTypes, - Content, CustomConfig, CustomItems, HeaderBlockTypes, NavigationData, NavigationItemTypes, PageContent, + PageContentWithNavigation, ShouldRenderBlock, SubBlockTypes, } from '../../models'; @@ -62,7 +62,11 @@ export const Constructor = (props: PageConstructorProps) => { microdata, } = props; - const [content, setContent] = React.useState<Content>({blocks, background, navigation}); + const [content, setContent] = React.useState<PageContentWithNavigation>({ + blocks, + background, + navigation, + }); const theme = useTheme(); diff --git a/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx b/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx index 5d7de0742b..f42540ea11 100644 --- a/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx +++ b/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import _ from 'lodash'; -import {usePCEditorStore} from '../../../../../context/editorStoreContext'; +import {usePCEditorStore} from '../../../../../hooks/usePCEditorStore'; import {sendEventPostMessage} from '../../../../../hooks/usePostMessageAPI'; import {getCursorPositionOverElement} from '../../../../../utils/editor'; diff --git a/src/containers/PageConstructor/components/ConstructorLoadable/ConstructorLoadable.tsx b/src/containers/PageConstructor/components/ConstructorLoadable/ConstructorLoadable.tsx index e2daf4d305..21ca4b2344 100644 --- a/src/containers/PageConstructor/components/ConstructorLoadable/ConstructorLoadable.tsx +++ b/src/containers/PageConstructor/components/ConstructorLoadable/ConstructorLoadable.tsx @@ -18,7 +18,7 @@ export const ConstructorLoadable = (props: ConstructorLoadableProps) => { const Component = itemMap[type] as React.Component< React.ComponentProps<(typeof itemMap)[typeof type]> >; - const parentId = useContext(BlockIdContext); + const parentId = React.useContext(BlockIdContext); return ( <BlockIdContext.Provider value={[...parentId, Number(blockKey)]} key={blockKey}> diff --git a/src/editor-v2/components/BigOverlay/BigOverlay.tsx b/src/editor-v2/components/BigOverlay/BigOverlay.tsx deleted file mode 100644 index 25eba68bc3..0000000000 --- a/src/editor-v2/components/BigOverlay/BigOverlay.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import * as React from 'react'; - -import {Stop} from '@gravity-ui/icons'; - -import {usePostMessageAPIListener} from '../../../common/postMessage'; -import {ClassNameProps} from '../../../models'; -import {block} from '../../../utils'; -import {IframeContext} from '../../context/iframeContext'; -import {useMainEditorStore} from '../../hooks/useMainEditorStore'; - -import './BigOverlay.scss'; - -const b = block('big-overlay'); - -interface BigOverlayProps extends ClassNameProps {} - -const BigOverlay: React.FC<BigOverlayProps> = ({className}) => { - const {zoom, manipulateOverlayMode} = useMainEditorStore(); - const {iframeElement} = useContext(IframeContext); - const [mousePosition, setMousePosition] = useState<{x: number; y: number} | undefined>( - undefined, - ); - const [source, setSource] = useState<'main' | 'iframe'>('main'); - - const onMouseUp = useCallback(() => { - setMousePosition(undefined); - }, []); - - const onIframeMouseEvent = useCallback((position: {x: number; y: number}) => { - setMousePosition(position); - setSource('iframe'); - }, []); - - usePostMessageAPIListener('ON_MOUSE_UP', onMouseUp); - usePostMessageAPIListener('ON_MOUSE_MOVE', onIframeMouseEvent); - - useEffect(() => { - const onEditorMouseEvent = (event: MouseEvent) => { - setMousePosition({x: event.clientX, y: event.clientY}); - setSource('main'); - }; - - document.addEventListener('mousemove', onEditorMouseEvent); - document.addEventListener('mousedown', onEditorMouseEvent); - - return () => { - document.removeEventListener('mousemove', onEditorMouseEvent); - document.removeEventListener('mousedown', onEditorMouseEvent); - }; - }, []); - - const realPositions = useMemo(() => { - if (mousePosition) { - const {x, y} = mousePosition; - const iframeRect = iframeElement?.getClientRects().item(0); - if (iframeRect) { - const zoomedX = (x * zoom) / 100; - const zoomedY = (y * zoom) / 100; - const newX = source === 'main' ? x : zoomedX + iframeRect.x; - const newY = source === 'main' ? y : zoomedY + iframeRect.y; - return {x: newX, y: newY}; - } - } - - return undefined; - }, [mousePosition, source, iframeElement, zoom]); - - return ( - <div className={b(null, className)}> - {realPositions && manipulateOverlayMode ? ( - <div - className={b('border')} - style={{ - top: realPositions.y, - left: realPositions.x, - }} - > - <Stop height={20} width={20} /> - </div> - ) : null} - </div> - ); -}; - -export default BigOverlay; diff --git a/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx b/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx deleted file mode 100644 index 994d825890..0000000000 --- a/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import * as React from 'react'; - -import {Plus} from '@gravity-ui/icons'; -import {Button, Card, Icon} from '@gravity-ui/uikit'; - -import {ArrayObjectInput, ArrayTextInput, DynamicFormValue} from '../../../../../common/types'; -import {ClassNameProps, PageContent} from '../../../../../models'; -import {block} from '../../../../../utils'; -import {removeFromArray, swapArrayItems} from '../../../../utils'; -import DynamicForm from '../../DynamicForm'; -import FieldBase from '../../FieldBase/FieldBase'; -import Text from '../Text/Text'; - -import ItemButton from './ItemButton/ItemButton'; - -import './Array.scss'; - -const b = block('array-dynamic-field'); - -type ArrayInput = ArrayTextInput | ArrayObjectInput; - -interface ArrayFieldProps extends ClassNameProps { - title: string; - values: Array<DynamicFormValue>; - onUpdate: (key: string, value: DynamicFormValue) => void; - blockConfig: ArrayInput; -} - -const ArrayDynamicField: React.FC<ArrayFieldProps> = (props) => { - const {title, values, onUpdate, className, blockConfig} = props; - - const haveItems = values && Array.isArray(values) && values.length; - - const onAddItem = useCallback(() => { - if (blockConfig.arrayType === 'text') { - onUpdate('', haveItems ? [...values, ''] : ['']); - } else if (blockConfig.arrayType === 'object') { - onUpdate('', haveItems ? [...values, {}] : [{}]); - } - }, [blockConfig.arrayType, haveItems, onUpdate, values]); - - const onDeleteItem = useCallback( - (index: number) => { - if (Array.isArray(values)) { - const newArray = removeFromArray(values, index); - onUpdate('', newArray); - } - }, - [onUpdate, values], - ); - - const onReorderItem = useCallback( - (index: number, placement: 'up' | 'down') => { - if (Array.isArray(values)) { - const newArray = swapArrayItems( - values, - index, - placement === 'up' ? index - 1 : index + 1, - ); - onUpdate('', newArray); - } - }, - [onUpdate, values], - ); - - const renderInput = useCallback( - (value: DynamicFormValue, index: number) => { - const arrayItemButton = ( - <ItemButton - onRemove={() => onDeleteItem(index)} - onReorderUp={() => onReorderItem(index, 'up')} - onReorderDown={() => onReorderItem(index, 'down')} - disableReorderUp={index === 0} - disableReorderDown={Boolean(haveItems) && values.length === index + 1} - /> - ); - - switch (blockConfig.arrayType) { - case 'text': { - return ( - <div className={b('row')}> - <Text - className={b('row-field')} - value={String(value)} - onUpdate={(updateValue) => onUpdate(`[${index}]`, updateValue)} - onRefresh={(updatedValue) => onUpdate('', updatedValue)} - /> - {arrayItemButton} - </div> - ); - } - case 'object': { - if (!blockConfig.properties) { - return null; - } - return ( - <Card key={index} className={b('card')}> - <div className={`${b('row')} ${b('card-head')}`}> - <div className={b('row-title')}>#{index}</div> - {arrayItemButton} - </div> - <DynamicForm - contentConfig={value as PageContent} - blockConfig={blockConfig.properties} - onUpdate={(key, updateValue) => - onUpdate(`[${index}].${key}`, updateValue) - } - /> - </Card> - ); - } - default: { - return null; - } - } - }, - [blockConfig, haveItems, onDeleteItem, onReorderItem, onUpdate, values], - ); - - const renderInputs = useCallback(() => { - if (haveItems) { - const renderItems = values - .map(renderInput) - .filter(Boolean) as unknown as React.ReactNode[]; - return ( - <React.Fragment> - {renderItems} - <Button className={b('add-button')} onClick={onAddItem}> - <Icon data={Plus} /> - {blockConfig.buttonText} - </Button> - </React.Fragment> - ); - } else { - return ( - <div className={b('empty')}> - <Button className={b('add-button')} onClick={onAddItem}> - <Icon data={Plus} /> - Please, add new item - </Button> - </div> - ); - } - }, [blockConfig.buttonText, haveItems, onAddItem, renderInput, values]); - - return ( - <FieldBase - title={title} - className={b(null, className)} - onRefresh={(value) => onUpdate('', value)} - expandable - > - <Card className={b('card')}>{renderInputs()}</Card> - </FieldBase> - ); -}; - -export default ArrayDynamicField; diff --git a/src/hooks/usePCEditorInitializeEvents.ts b/src/hooks/usePCEditorInitializeEvents.ts index 69ec825770..f72ea957c5 100644 --- a/src/hooks/usePCEditorInitializeEvents.ts +++ b/src/hooks/usePCEditorInitializeEvents.ts @@ -5,7 +5,7 @@ import _ from 'lodash'; import {ItemConfig} from '../../common/types'; import {blockDataMap} from '../constructor-items'; -import {Content} from '../models'; +import {PageContentWithNavigation} from '../models'; import {defaultComponentsConfigurationSchema} from '../schema'; import {generateFromAJV} from '../utils/form-generator'; @@ -13,8 +13,8 @@ import {usePCEditorStore} from './usePCEditorStore'; import {sendEventPostMessage, useInternalPostMessageAPIListener} from './usePostMessageAPI'; interface UseEditorInitializeProps { - initialContent: Content; - setContent: (content: Content) => void; + initialContent: PageContentWithNavigation; + setContent: (content: PageContentWithNavigation) => void; } export const usePCEditorInitializeEvents = ({ diff --git a/src/models/constructor.ts b/src/models/constructor.ts index ed7b0068c9..477086ac31 100644 --- a/src/models/constructor.ts +++ b/src/models/constructor.ts @@ -24,7 +24,7 @@ export interface PageContent extends Animatable { background?: ThemedMediaProps; } -export interface Content extends PageContent { +export interface PageContentWithNavigation extends PageContent { navigation?: NavigationData; } diff --git a/src/navigation/components/Navigation/Navigation.tsx b/src/navigation/components/Navigation/Navigation.tsx index 4fadc7c227..e25b2bf118 100644 --- a/src/navigation/components/Navigation/Navigation.tsx +++ b/src/navigation/components/Navigation/Navigation.tsx @@ -13,8 +13,8 @@ import './Navigation.scss'; const b = block('navigation'); export interface NavigationComponentProps extends ClassNameProps { - logo: ThemedNavigationLogoData; - data: HeaderData; + logo?: ThemedNavigationLogoData; + data?: HeaderData; mobilePortalContainer?: React.RefObject<HTMLElement>; onSidebarChange?: (isOpen: boolean) => void; } diff --git a/src/sub-blocks/BackgroundCard/dynamic-form.ts b/src/sub-blocks/BackgroundCard/dynamic-form.ts index 817ff35e28..1ab9100a14 100644 --- a/src/sub-blocks/BackgroundCard/dynamic-form.ts +++ b/src/sub-blocks/BackgroundCard/dynamic-form.ts @@ -1,4 +1,4 @@ -import {BlockConfig} from '../../common/types'; +import {BlockConfig} from '../../../common/types'; import {contentThemes, textSize} from '../../schema/validators/common'; const textSizeEnum = textSize.map((size) => ({value: size, content: size})); From a9696c507c48515cd6e318338d172925e8f90fd6 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Fri, 28 Mar 2025 18:58:47 +0300 Subject: [PATCH 025/118] feat: add device switcher --- common/store.ts | 2 + .../containers/MiddleScreen/MiddleScreen.scss | 11 +++- .../containers/MiddleScreen/MiddleScreen.tsx | 52 ++++++++++--------- .../containers/ViewSwitches/ViewSwitches.tsx | 20 +++++-- editor-v2/store.ts | 4 ++ 5 files changed, 58 insertions(+), 31 deletions(-) diff --git a/common/store.ts b/common/store.ts index 48befec430..796c844f4d 100644 --- a/common/store.ts +++ b/common/store.ts @@ -7,6 +7,7 @@ import {initializeStore} from './utils'; export interface EditorState { height?: number; + deviceWidth?: string; zoom: number; manipulateOverlayMode: 'insert' | 'reorder' | false; @@ -24,6 +25,7 @@ export interface EditorState { export const initialStore: EditorState = { height: 100, + deviceWidth: '100%', zoom: 100, manipulateOverlayMode: false, selectedBlock: undefined, diff --git a/editor-v2/containers/MiddleScreen/MiddleScreen.scss b/editor-v2/containers/MiddleScreen/MiddleScreen.scss index 0b869dbdd8..e2a530dd50 100644 --- a/editor-v2/containers/MiddleScreen/MiddleScreen.scss +++ b/editor-v2/containers/MiddleScreen/MiddleScreen.scss @@ -12,19 +12,26 @@ $block: '.#{$ns}middle-screen'; &__topbar { flex: 0 0 auto; - // min-height: 40px; } - &__wrapper { + &__content { flex: 1 1 auto; overflow-y: auto; // height: 100%; + background-color: var(--g-color-text-dark-secondary); + } + + &__wrapper { + width: 100%; + height: 100%; + margin: auto; } &__canvas { transform-origin: left top; overflow-y: scroll; position: relative; + height: 100%; } &__overlay { diff --git a/editor-v2/containers/MiddleScreen/MiddleScreen.tsx b/editor-v2/containers/MiddleScreen/MiddleScreen.tsx index 7ce7b974fa..6ccf9dbc3d 100644 --- a/editor-v2/containers/MiddleScreen/MiddleScreen.tsx +++ b/editor-v2/containers/MiddleScreen/MiddleScreen.tsx @@ -17,7 +17,7 @@ interface MiddleScreenProps { } const MiddleScreen = ({className, CustomTop}: MiddleScreenProps) => { - const {zoom, initialized} = useMainEditorStore(); + const {zoom, initialized, deviceWidth} = useMainEditorStore(); const {url, setIframeElement} = React.useContext(IframeContext); const [height, setHeight] = React.useState(0); @@ -43,30 +43,32 @@ const MiddleScreen = ({className, CustomTop}: MiddleScreenProps) => { <CustomTop /> </div> ) : null} - <div className={b('wrapper')}> - <div - className={b('canvas', {hidden: !initialized})} - style={{ - transform: `scale(${zoom}%)`, - height: `${(100 / zoom) * 100}%`, - width: `${(100 / zoom) * 100}%`, - }} - > - <iframe - ref={(instance) => instance && setIframeElement(instance)} - className={b('iframe')} - src={url} - height={`${height}px`} - width="100%" - frameBorder="0" - title="Page Constructor Iframe" - /> - <Overlay className={b('overlay')} /> - {!initialized && ( - <div className={b('loading')}> - <Loader size={'l'} /> - </div> - )} + <div className={b('content')}> + <div className={b('wrapper')} style={{width: deviceWidth}}> + <div + className={b('canvas', {hidden: !initialized})} + style={{ + transform: `scale(${zoom}%)`, + height: `${(100 / zoom) * 100}%`, + width: `${(100 / zoom) * 100}%`, + }} + > + <iframe + ref={(instance) => instance && setIframeElement(instance)} + className={b('iframe')} + src={url} + height={`${height}px`} + width="100%" + frameBorder="0" + title="Page Constructor Iframe" + /> + <Overlay className={b('overlay')} /> + {!initialized && ( + <div className={b('loading')}> + <Loader size={'l'} /> + </div> + )} + </div> </div> </div> </div> diff --git a/editor-v2/containers/ViewSwitches/ViewSwitches.tsx b/editor-v2/containers/ViewSwitches/ViewSwitches.tsx index 9ad12ac3e0..b811ca6a99 100644 --- a/editor-v2/containers/ViewSwitches/ViewSwitches.tsx +++ b/editor-v2/containers/ViewSwitches/ViewSwitches.tsx @@ -1,5 +1,5 @@ -import {Minus, Plus} from '@gravity-ui/icons'; -import {Button, Icon, Select} from '@gravity-ui/uikit'; +import {Display, Minus, Plus, Smartphone} from '@gravity-ui/icons'; +import {Button, Icon, SegmentedRadioGroup, Select} from '@gravity-ui/uikit'; import {ZOOM_STEPS} from '../../constants'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; @@ -9,11 +9,23 @@ import './ViewSwitches.scss'; const b = editorCn('view-switches'); +const DEVICE_OPTIONS = [ + {label: <Icon data={Display} />, value: '100%'}, + {label: <Icon data={Smartphone} />, value: '768px'}, + {label: <Icon width={14} data={Smartphone} />, value: '576px'}, +]; + const ViewSwitches = () => { - const {zoom, setZoom, decreaseZoom, increaseZoom} = useMainEditorStore(); + const {zoom, setZoom, decreaseZoom, increaseZoom, deviceWidth, setDeviceWidth} = + useMainEditorStore(); + return ( <div className={b()}> - <div>TBD</div> + <SegmentedRadioGroup value={deviceWidth} onUpdate={setDeviceWidth}> + {DEVICE_OPTIONS.map(({value, label}) => ( + <SegmentedRadioGroup.Option value={value}>{label}</SegmentedRadioGroup.Option> + ))} + </SegmentedRadioGroup> <div className={b('zoom')}> <Button view={'flat'} onClick={decreaseZoom}> <Icon data={Minus} /> diff --git a/editor-v2/store.ts b/editor-v2/store.ts index 46fa4e17da..5760689458 100644 --- a/editor-v2/store.ts +++ b/editor-v2/store.ts @@ -21,6 +21,7 @@ export interface EditorMethods { initialize(): void; setSelectedBlock(path?: number[]): void; setHeight(height: number): void; + setDeviceWidth(deviceWidth: string): void; setZoom(zoom: number): void; increaseZoom(): void; decreaseZoom(): void; @@ -49,6 +50,9 @@ export const createEditorStore = initializeStore<EditorState, EditorMethods>( const newHeight = height + 500; set((state) => ({...state, height: newHeight})); }, + setDeviceWidth(deviceWidth: string) { + set((state) => ({...state, deviceWidth})); + }, setZoom(zoom) { if (zoom > 0) { set((state) => ({...state, zoom})); From 92806a7ab49eebf64ba6e0dadf67511ca8a8492d Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 31 Mar 2025 12:47:05 +0300 Subject: [PATCH 026/118] fix: ai refactor viewswitches --- .../containers/ViewSwitches/ViewSwitches.scss | 4 - .../containers/ViewSwitches/ViewSwitches.tsx | 118 +++++++++++++++--- 2 files changed, 99 insertions(+), 23 deletions(-) diff --git a/editor-v2/containers/ViewSwitches/ViewSwitches.scss b/editor-v2/containers/ViewSwitches/ViewSwitches.scss index 7e0cee91bb..852d29aab9 100644 --- a/editor-v2/containers/ViewSwitches/ViewSwitches.scss +++ b/editor-v2/containers/ViewSwitches/ViewSwitches.scss @@ -14,8 +14,4 @@ $block: '.#{$ns}view-switches'; display: flex; gap: 4px; } - - &__zoom-input { - width: 40px; - } } diff --git a/editor-v2/containers/ViewSwitches/ViewSwitches.tsx b/editor-v2/containers/ViewSwitches/ViewSwitches.tsx index b811ca6a99..fbebdcfb86 100644 --- a/editor-v2/containers/ViewSwitches/ViewSwitches.tsx +++ b/editor-v2/containers/ViewSwitches/ViewSwitches.tsx @@ -1,3 +1,4 @@ +import * as React from 'react'; import {Display, Minus, Plus, Smartphone} from '@gravity-ui/icons'; import {Button, Icon, SegmentedRadioGroup, Select} from '@gravity-ui/uikit'; @@ -9,37 +10,116 @@ import './ViewSwitches.scss'; const b = editorCn('view-switches'); -const DEVICE_OPTIONS = [ - {label: <Icon data={Display} />, value: '100%'}, - {label: <Icon data={Smartphone} />, value: '768px'}, - {label: <Icon width={14} data={Smartphone} />, value: '576px'}, +/** + * Device option type definition + */ +interface DeviceOption { + /** React node to display as the option label */ + label: React.ReactNode; + /** Device width value (e.g., '100%', '768px') */ + value: string; + /** Descriptive name for accessibility */ + ariaLabel: string; +} + +/** + * Available device viewport options + * - Desktop: 100% width + * - Tablet: 768px width + * - Mobile: 576px width + */ +const DEVICE_OPTIONS: DeviceOption[] = [ + { + label: <Icon data={Display} />, + value: '100%', + ariaLabel: 'Desktop view', + }, + { + label: <Icon data={Smartphone} />, + value: '768px', + ariaLabel: 'Tablet view', + }, + { + label: <Icon width={14} data={Smartphone} />, + value: '576px', + ariaLabel: 'Mobile view', + }, ]; -const ViewSwitches = () => { +/** + * ViewSwitches component + * + * Provides UI controls for: + * 1. Switching between device viewport widths (desktop, tablet, mobile) + * 2. Adjusting zoom level with increment/decrement buttons and dropdown + */ +const ViewSwitches: React.FC = () => { const {zoom, setZoom, decreaseZoom, increaseZoom, deviceWidth, setDeviceWidth} = useMainEditorStore(); + // Memoize zoom options to prevent unnecessary recalculations + const zoomOptions = React.useMemo( + () => + ZOOM_STEPS.map((step) => ({ + value: String(step), + content: `${step}%`, + })), + [], + ); + + // Memoize current zoom value for Select component + const currentZoomValue = React.useMemo(() => [String(zoom)], [zoom]); + + // Create stable callback for zoom updates + const handleZoomUpdate = React.useCallback( + (value: string | string[]) => { + const newZoom = Number(Array.isArray(value) ? value[0] : value); + if (!isNaN(newZoom) && ZOOM_STEPS.includes(newZoom)) { + setZoom(newZoom); + } + }, + [setZoom], + ); + return ( - <div className={b()}> - <SegmentedRadioGroup value={deviceWidth} onUpdate={setDeviceWidth}> - {DEVICE_OPTIONS.map(({value, label}) => ( - <SegmentedRadioGroup.Option value={value}>{label}</SegmentedRadioGroup.Option> + <div className={b()} role="toolbar" aria-label="View controls"> + <SegmentedRadioGroup + value={deviceWidth} + onUpdate={setDeviceWidth} + aria-label="Device viewport selector" + > + {DEVICE_OPTIONS.map(({value, label, ariaLabel}) => ( + <SegmentedRadioGroup.Option key={value} value={value} aria-label={ariaLabel}> + {label} + </SegmentedRadioGroup.Option> ))} </SegmentedRadioGroup> - <div className={b('zoom')}> - <Button view={'flat'} onClick={decreaseZoom}> + + <div className={b('zoom')} role="group" aria-label="Zoom controls"> + <Button + view="flat" + onClick={decreaseZoom} + aria-label="Decrease zoom" + disabled={zoom <= Math.min(...ZOOM_STEPS)} + > <Icon data={Minus} /> </Button> + <Select multiple={false} - value={[String(zoom)]} - options={ZOOM_STEPS.map((step) => ({ - value: String(step), - content: `${String(step)}%`, - }))} - onUpdate={(value) => setZoom(Number(value))} + value={currentZoomValue} + options={zoomOptions} + onUpdate={handleZoomUpdate} + aria-label="Select zoom level" + width="max" /> - <Button view={'flat'} onClick={increaseZoom}> + + <Button + view="flat" + onClick={increaseZoom} + aria-label="Increase zoom" + disabled={zoom >= Math.max(...ZOOM_STEPS)} + > <Icon data={Plus} /> </Button> </div> @@ -47,4 +127,4 @@ const ViewSwitches = () => { ); }; -export default ViewSwitches; +export default React.memo(ViewSwitches); From 37b3d1681b85e0910d8f7c6b97024e4d05d90aee Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 31 Mar 2025 13:56:11 +0300 Subject: [PATCH 027/118] feat: add c9r top component --- editor-v2/containers/Editor/Editor.tsx | 2 +- .../src/app/components/C9RComponent.scss | 30 +++++++++++++++++++ .../src/app/components/C9RComponent.tsx | 22 ++++++++++++++ playground/src/app/page.tsx | 2 ++ 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 playground/src/app/components/C9RComponent.scss create mode 100644 playground/src/app/components/C9RComponent.tsx diff --git a/editor-v2/containers/Editor/Editor.tsx b/editor-v2/containers/Editor/Editor.tsx index ae3ac6820d..8b98d5d509 100644 --- a/editor-v2/containers/Editor/Editor.tsx +++ b/editor-v2/containers/Editor/Editor.tsx @@ -59,7 +59,7 @@ const EditorView = ({componentsConfig = {}}: EditorViewProps) => { <div className={b()} onMouseUp={onMouseUp}> <div className={b('body')}> <Panels - left={<Sidebar tabs={left} />} + left={<Sidebar tabs={left} top={componentsConfig.leftTop} />} right={ <Sidebar tabs={right} diff --git a/playground/src/app/components/C9RComponent.scss b/playground/src/app/components/C9RComponent.scss new file mode 100644 index 0000000000..fb9a04fa48 --- /dev/null +++ b/playground/src/app/components/C9RComponent.scss @@ -0,0 +1,30 @@ +$block: '.c9r-component'; + +#{$block} { + width: 100%; + padding: 16px 12px; + + &__header { + display: flex; + justify-content: space-between; + align-items: flex-start; + } + + &__title { + font-size: 30px; + line-height: 28px; + font-weight: 900; + } + + &__info-bar { + margin-top: 16px; + display: flex; + justify-content: space-between; + align-items: center; + } + + &__editor-info { + font-size: 14px; + color: var(--g-color-private-black-300); + } +} diff --git a/playground/src/app/components/C9RComponent.tsx b/playground/src/app/components/C9RComponent.tsx new file mode 100644 index 0000000000..1f4d2e7919 --- /dev/null +++ b/playground/src/app/components/C9RComponent.tsx @@ -0,0 +1,22 @@ +'use client'; + +import block from 'bem-cn-lite'; + +import './C9RComponent.scss'; + +const b = block('c9r-component'); + +export const C9RComponent = () => { + return ( + <div className={b()}> + <div className={b('header')}> + <div className={b('title')}>C9R</div> + </div> + <div className={b('info-bar')}> + <div className={b('editor-info')}>Editor alpha 1.0.0</div> + </div> + </div> + ); +}; + +export default C9RComponent; diff --git a/playground/src/app/page.tsx b/playground/src/app/page.tsx index ef1feacef5..ddae138bf8 100644 --- a/playground/src/app/page.tsx +++ b/playground/src/app/page.tsx @@ -6,6 +6,7 @@ import * as React from 'react'; import {Editor} from '../../../editor-v2'; import Source from '../../../editor-v2/containers/Source/Source'; import ViewSwitches from '../../../editor-v2/containers/ViewSwitches/ViewSwitches'; +import C9RComponent from './components/C9RComponent'; import './page.scss'; @@ -15,6 +16,7 @@ const Test = () => <div className={b('test')}>custom test</div>; export const COMPONENTS_CONFIG = { middleTop: Test, + leftTop: [C9RComponent], leftTabs: [ { id: 'test', From 8128703695c2f85a75eaefcc5f23e452f7ee756f Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 31 Mar 2025 14:52:36 +0300 Subject: [PATCH 028/118] feat: editor tree selection --- editor-v2/containers/Tree/Tree.tsx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/editor-v2/containers/Tree/Tree.tsx b/editor-v2/containers/Tree/Tree.tsx index 4e698f7a13..7423dfbb1a 100644 --- a/editor-v2/containers/Tree/Tree.tsx +++ b/editor-v2/containers/Tree/Tree.tsx @@ -28,6 +28,7 @@ interface ItemProps { selected: boolean; onCopy(path: number[]): void; onDelete(path: number[]): void; + onSelect(path: number[]): void; } const generateTree = (items: TreeItem[]): TreeItem[] => { @@ -54,6 +55,7 @@ const Item = ({ selected, onCopy, onDelete, + onSelect, }: React.PropsWithChildren<ItemProps>) => { const handleCopy = React.useCallback(() => { onCopy(path); @@ -63,12 +65,16 @@ const Item = ({ onDelete(path); }, [onDelete, path]); + const handleSelect = React.useCallback( + (e: React.MouseEvent<HTMLDivElement>) => { + e.stopPropagation(); + onSelect(path); + }, + [onSelect, path], + ); + return ( - <Card - className={b('item', { - selected, - })} - > + <Card className={b('item')} selected={selected} onClick={handleSelect} type="selection"> <div className={b('main')}> <div className={b('text')}> <div className={b('type')}>{type}</div> @@ -89,7 +95,8 @@ const Item = ({ }; const Tree = (_p: React.PropsWithChildren<TreeProps>) => { - const {content, resetBlocks, selectedBlock, duplicateBlock, deleteBlock} = useMainEditorStore(); + const {content, resetBlocks, selectedBlock, duplicateBlock, deleteBlock, setSelectedBlock} = + useMainEditorStore(); const selectedBlockPath = React.useMemo(() => { return generateChildrenPathFromArray(selectedBlock || []); @@ -113,6 +120,7 @@ const Tree = (_p: React.PropsWithChildren<TreeProps>) => { treeTitle={treeTitle} onCopy={duplicateBlock} onDelete={deleteBlock} + onSelect={setSelectedBlock} key={index} path={blockPathArray} selected={selectedBlockPath === blockPath} From 23c8c245710cc7d819b06d6bdcb4ce5f841abcb5 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 31 Mar 2025 19:29:46 +0300 Subject: [PATCH 029/118] feat: editor tree dnd --- editor-v2/containers/Tree/DragContext.scss | 32 +++ editor-v2/containers/Tree/DragContext.tsx | 70 +++++++ editor-v2/containers/Tree/Tree.scss | 50 +---- editor-v2/containers/Tree/Tree.tsx | 139 +++---------- editor-v2/containers/Tree/TreeContent.scss | 24 +++ editor-v2/containers/Tree/TreeContent.tsx | 121 +++++++++++ editor-v2/containers/Tree/TreeItem.scss | 71 +++++++ editor-v2/containers/Tree/TreeItem.tsx | 226 +++++++++++++++++++++ editor-v2/containers/Tree/index.ts | 4 + editor-v2/hooks/useEditorTabs.tsx | 2 +- 10 files changed, 579 insertions(+), 160 deletions(-) create mode 100644 editor-v2/containers/Tree/DragContext.scss create mode 100644 editor-v2/containers/Tree/DragContext.tsx create mode 100644 editor-v2/containers/Tree/TreeContent.scss create mode 100644 editor-v2/containers/Tree/TreeContent.tsx create mode 100644 editor-v2/containers/Tree/TreeItem.scss create mode 100644 editor-v2/containers/Tree/TreeItem.tsx create mode 100644 editor-v2/containers/Tree/index.ts diff --git a/editor-v2/containers/Tree/DragContext.scss b/editor-v2/containers/Tree/DragContext.scss new file mode 100644 index 0000000000..ae1097e5ed --- /dev/null +++ b/editor-v2/containers/Tree/DragContext.scss @@ -0,0 +1,32 @@ +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}preview'; + +#{$block} { + position: absolute; + height: auto; + background-color: var(--g-color-base-brand-light); + border: 1px dashed var(--g-color-line-brand); + border-radius: 4px; + padding: 8px; + z-index: 1000; + pointer-events: none; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); + + &-content { + display: flex; + flex-direction: column; + } + + &-type { + color: var(--g-color-text-dark-secondary); + font-size: 12px; + } + + &-title { + @include overflow-ellipsis(); + font-size: 14px; + } +} diff --git a/editor-v2/containers/Tree/DragContext.tsx b/editor-v2/containers/Tree/DragContext.tsx new file mode 100644 index 0000000000..268793903e --- /dev/null +++ b/editor-v2/containers/Tree/DragContext.tsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import {editorCn} from '../../utils/cn'; + +import './DragContext.scss'; + +const b = editorCn('preview'); + +export interface DragItem { + path: number[]; + type: string; + treeTitle?: string; +} + +// Context for drag and drop operations +export const DragContext = React.createContext<{ + draggedItem: DragItem | null; + setDraggedItem: React.Dispatch<React.SetStateAction<DragItem | null>>; + previewRef: React.RefObject<HTMLDivElement>; + showPreview: (rect: DOMRect, item: DragItem) => void; + hidePreview: () => void; +}>({ + draggedItem: null, + setDraggedItem: () => {}, + previewRef: {current: null}, + showPreview: () => {}, + hidePreview: () => {}, +}); + +// Component for the drag preview +const DragPreview = React.forwardRef<HTMLDivElement>((_, ref) => { + return <div ref={ref} className={b()} style={{display: 'none'}} />; +}); + +// Provider component for drag context +export const DragContextProvider: React.FC<React.PropsWithChildren<{}>> = ({children}) => { + const [draggedItem, setDraggedItem] = React.useState<DragItem | null>(null); + const previewRef = React.useRef<HTMLDivElement>(null); + + const showPreview = React.useCallback((rect: DOMRect, item: DragItem) => { + if (!previewRef.current) return; + + const previewEl = previewRef.current; + previewEl.style.display = 'block'; + previewEl.style.width = `${rect.width}px`; + previewEl.style.top = `${rect.bottom}px`; + previewEl.style.left = `${rect.left}px`; + + previewEl.innerHTML = ` + <div class="${b('content')}"> + <div class="${b('type')}">${item.type}</div> + ${item.treeTitle ? `<div class="${b('title')}">${item.treeTitle}</div>` : ''} + </div> + `; + }, []); + + const hidePreview = React.useCallback(() => { + if (previewRef.current) { + previewRef.current.style.display = 'none'; + } + }, []); + + return ( + <DragContext.Provider + value={{draggedItem, setDraggedItem, previewRef, showPreview, hidePreview}} + > + {children} + <DragPreview ref={previewRef} /> + </DragContext.Provider> + ); +}; diff --git a/editor-v2/containers/Tree/Tree.scss b/editor-v2/containers/Tree/Tree.scss index 666e4f0d2f..f2daed6b1d 100644 --- a/editor-v2/containers/Tree/Tree.scss +++ b/editor-v2/containers/Tree/Tree.scss @@ -6,6 +6,7 @@ $block: '.#{$ns}tree'; #{$block} { padding: 12px; + position: relative; &__head { margin-bottom: 10px; @@ -13,53 +14,4 @@ $block: '.#{$ns}tree'; align-items: center; justify-content: space-between; } - - &__item { - padding: 8px; - margin-bottom: 8px; - - &_selected { - border: 1.5px var(--g-color-line-brand) solid; - } - - &:last-child { - margin-bottom: 0; - } - } - - &__main { - display: flex; - flex-direction: row; - align-items: center; - - &:hover { - #{$block}__buttons { - display: flex; - } - } - } - - &__text { - flex: 1 1 auto; - min-width: 1px; - } - - &__buttons { - flex: 0 0 auto; - flex-direction: row; - gap: 4px; - display: none; - } - - &__type { - color: var(--g-color-text-dark-secondary); - } - - &__title { - @include overflow-ellipsis(); - } - - &__children { - margin-top: 8px; - } } diff --git a/editor-v2/containers/Tree/Tree.tsx b/editor-v2/containers/Tree/Tree.tsx index 7423dfbb1a..58053b44db 100644 --- a/editor-v2/containers/Tree/Tree.tsx +++ b/editor-v2/containers/Tree/Tree.tsx @@ -1,36 +1,18 @@ -import {Copy, TrashBin} from '@gravity-ui/icons'; -import {Button, Card, Icon} from '@gravity-ui/uikit'; import * as React from 'react'; -import {ItemConfig} from '../../../common/types'; +import {editorCn} from '../../utils/cn'; +import {DragContextProvider} from './DragContext'; +import {TreeContent, TreeItem} from './TreeContent'; + import {useMainEditorStore} from '../../hooks/useMainEditorStore'; +import {Button, Icon} from '@gravity-ui/uikit'; +import {TrashBin} from '@gravity-ui/icons'; import {generateChildrenPathFromArray, getItemTitle} from '../../utils'; -import {editorCn} from '../../utils/cn'; import './Tree.scss'; const b = editorCn('tree'); -export interface TreeProps { - blocks: ItemConfig[]; -} - -type TreeItem = { - type: string; - children?: TreeItem[]; - treeTitle?: string; -}; - -interface ItemProps { - type: string; - treeTitle?: string; - path: number[]; - selected: boolean; - onCopy(path: number[]): void; - onDelete(path: number[]): void; - onSelect(path: number[]): void; -} - const generateTree = (items: TreeItem[]): TreeItem[] => { return items.map((item) => { let children; @@ -47,104 +29,41 @@ const generateTree = (items: TreeItem[]): TreeItem[] => { }); }; -const Item = ({ - type, - children, - treeTitle, - path, - selected, - onCopy, - onDelete, - onSelect, -}: React.PropsWithChildren<ItemProps>) => { - const handleCopy = React.useCallback(() => { - onCopy(path); - }, [onCopy, path]); - - const handleDelete = React.useCallback(() => { - onDelete(path); - }, [onDelete, path]); - - const handleSelect = React.useCallback( - (e: React.MouseEvent<HTMLDivElement>) => { - e.stopPropagation(); - onSelect(path); - }, - [onSelect, path], - ); - - return ( - <Card className={b('item')} selected={selected} onClick={handleSelect} type="selection"> - <div className={b('main')}> - <div className={b('text')}> - <div className={b('type')}>{type}</div> - <div className={b('title')}>{treeTitle}</div> - </div> - <div className={b('buttons')}> - <Button view="flat" size="xs" onClick={handleCopy}> - <Icon size={12} data={Copy} /> - </Button> - <Button view="flat" size="xs" onClick={handleDelete}> - <Icon size={12} data={TrashBin} /> - </Button> - </div> - </div> - {children} - </Card> - ); -}; - -const Tree = (_p: React.PropsWithChildren<TreeProps>) => { - const {content, resetBlocks, selectedBlock, duplicateBlock, deleteBlock, setSelectedBlock} = - useMainEditorStore(); +// Main Tree component with context provider +const Tree = () => { + const { + content, + resetBlocks, + selectedBlock, + duplicateBlock, + deleteBlock, + setSelectedBlock, + reorderBlock, + } = useMainEditorStore(); const selectedBlockPath = React.useMemo(() => { return generateChildrenPathFromArray(selectedBlock || []); }, [selectedBlock]); - const blockTree = generateTree(content.blocks); - - const renderTree = (items: TreeItem[], parentPathArray?: number[]) => { - return items.map(({type, treeTitle, children}, index) => { - let blockPathArray: number[]; - if (parentPathArray) { - blockPathArray = [...parentPathArray, index]; - } else { - blockPathArray = [index]; - } - const blockPath = generateChildrenPathFromArray(blockPathArray); - - return ( - <Item - type={type} - treeTitle={treeTitle} - onCopy={duplicateBlock} - onDelete={deleteBlock} - onSelect={setSelectedBlock} - key={index} - path={blockPathArray} - selected={selectedBlockPath === blockPath} - > - {children && ( - <div className={b('children')}>{renderTree(children, blockPathArray)}</div> - )} - </Item> - ); - }); - }; - return ( - <div className={b()}> - <div className={b('head')}> - <div className={b('actions')}> + <DragContextProvider> + <div className={b()}> + <div className={b('head')}> <Button view="outlined-danger" onClick={() => resetBlocks()}> <Icon data={TrashBin} /> Clear all </Button> </div> + <TreeContent + reorderBlock={reorderBlock} + onCopy={duplicateBlock} + onDelete={deleteBlock} + onSelect={setSelectedBlock} + blockTree={generateTree(content.blocks)} + selectedBlockPath={selectedBlockPath} + /> </div> - <div className={b('cards')}>{renderTree(blockTree)}</div> - </div> + </DragContextProvider> ); }; diff --git a/editor-v2/containers/Tree/TreeContent.scss b/editor-v2/containers/Tree/TreeContent.scss new file mode 100644 index 0000000000..2f58420217 --- /dev/null +++ b/editor-v2/containers/Tree/TreeContent.scss @@ -0,0 +1,24 @@ +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}tree-content'; + +#{$block} { + position: relative; + + &__drop-zone { + height: 8px; + margin-bottom: 8px; + border-radius: 4px; + transition: background-color 0.2s; + + &:hover { + background-color: var(--g-color-text-light-secondary); + } + } + + &__children { + margin-top: 8px; + } +} diff --git a/editor-v2/containers/Tree/TreeContent.tsx b/editor-v2/containers/Tree/TreeContent.tsx new file mode 100644 index 0000000000..bc2e252340 --- /dev/null +++ b/editor-v2/containers/Tree/TreeContent.tsx @@ -0,0 +1,121 @@ +import * as React from 'react'; + +import {generateChildrenPathFromArray} from '../../utils'; +import {DragContext, DragItem} from './DragContext'; +import {Item} from './TreeItem'; +import {editorCn} from '../../utils/cn'; + +import './TreeContent.scss'; + +const b = editorCn('tree-content'); + +interface TreeContentProps { + blockTree: TreeItem[]; + selectedBlockPath: string; + reorderBlock( + selectedBlock: number[], + destination: number[], + position?: 'prepend' | 'append', + ): void; + onCopy(path: number[]): void; + onDelete(path: number[]): void; + onSelect(path: number[]): void; +} + +export type TreeItem = { + type: string; + children?: TreeItem[]; + treeTitle?: string; +}; + +export const TreeContent = ({ + blockTree, + selectedBlockPath, + reorderBlock, + onCopy, + onDelete, + onSelect, +}: React.PropsWithChildren<TreeContentProps>) => { + const {draggedItem, setDraggedItem, hidePreview, showPreview} = React.useContext(DragContext); + + const handleFirstPositionDrop = React.useCallback( + (e: any) => { + e.preventDefault(); + e.stopPropagation(); + + try { + const data = e.dataTransfer.getData('application/json'); + if (data) { + const dragItem: DragItem = JSON.parse(data); + // Reorder to the first position by using [0] as destination + // and 'prepend' as position + reorderBlock(dragItem.path, [0], 'prepend'); + + // Reset drag context + setDraggedItem(null); + hidePreview(); + } + } catch (error) { + console.error('Error parsing drag data:', error); + } + }, + [reorderBlock, hidePreview, setDraggedItem], + ); + + const handleDragOver = React.useCallback( + (e: any) => { + e.preventDefault(); + e.stopPropagation(); + e.dataTransfer.dropEffect = 'move'; + + // Show preview element at this position + const rect = e.currentTarget.getBoundingClientRect(); + if (draggedItem) { + showPreview(rect, draggedItem); + } + }, + [draggedItem, showPreview], + ); + + const renderTree = (items: TreeItem[], parentPathArray?: number[]) => { + return items.map(({type, treeTitle, children}, index) => { + let blockPathArray: number[]; + if (parentPathArray) { + blockPathArray = [...parentPathArray, index]; + } else { + blockPathArray = [index]; + } + const blockPath = generateChildrenPathFromArray(blockPathArray); + + return ( + <Item + type={type} + treeTitle={treeTitle} + onCopy={onCopy} + onDelete={onDelete} + onSelect={onSelect} + onReorder={reorderBlock} + key={index} + path={blockPathArray} + selected={selectedBlockPath === blockPath} + > + {children && ( + <div className={b('children')}>{renderTree(children, blockPathArray)}</div> + )} + </Item> + ); + }); + }; + + return ( + <div className={b()}> + <div + className={b('drop-zone')} + onDragOver={handleDragOver} + onDragLeave={hidePreview} + onDrop={handleFirstPositionDrop} + ></div> + <div>{renderTree(blockTree)}</div> + </div> + ); +}; diff --git a/editor-v2/containers/Tree/TreeItem.scss b/editor-v2/containers/Tree/TreeItem.scss new file mode 100644 index 0000000000..ae821fcfe7 --- /dev/null +++ b/editor-v2/containers/Tree/TreeItem.scss @@ -0,0 +1,71 @@ +@import '../../styles/root.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/mixins.scss'; + +$block: '.#{$ns}tree-item'; + +#{$block} { + padding: 8px; + margin-bottom: 8px; + cursor: pointer; + + &_selected { + border: 1.5px var(--g-color-line-brand) solid; + } + + &_dragging { + opacity: 0.5; + } + + &_drag-over { + border: 1.5px var(--g-color-line-generic) dashed; + } + + &:last-child { + margin-bottom: 0; + } + + &__main { + display: flex; + flex-direction: row; + align-items: center; + + &:hover { + #{$block}__buttons { + display: flex; + } + } + } + + &__text { + flex: 1 1 auto; + min-width: 1px; + } + + &__buttons { + flex: 0 0 auto; + flex-direction: row; + gap: 4px; + display: none; + } + + &__type { + color: var(--g-color-text-dark-secondary); + } + + &__title { + @include overflow-ellipsis(); + } + + &__children-drop-zone { + height: 8px; + margin-top: 8px; + margin-bottom: 0; + border-radius: 4px; + transition: background-color 0.2s; + + &:hover { + background-color: var(--g-color-text-light-secondary); + } + } +} diff --git a/editor-v2/containers/Tree/TreeItem.tsx b/editor-v2/containers/Tree/TreeItem.tsx new file mode 100644 index 0000000000..96fbf51e05 --- /dev/null +++ b/editor-v2/containers/Tree/TreeItem.tsx @@ -0,0 +1,226 @@ +import {Copy, TrashBin} from '@gravity-ui/icons'; +import {Button, Card, Icon} from '@gravity-ui/uikit'; +import * as React from 'react'; + +import {editorCn} from '../../utils/cn'; +import {DragContext, DragItem} from './DragContext'; + +import './TreeItem.scss'; + +const b = editorCn('tree-item'); + +export interface ItemProps { + type: string; + treeTitle?: string; + path: number[]; + selected: boolean; + onCopy(path: number[]): void; + onDelete(path: number[]): void; + onSelect(path: number[]): void; + onReorder( + sourcePath: number[], + destinationPath: number[], + position?: 'prepend' | 'append', + ): void; + children?: React.ReactNode; +} + +export const Item = ({ + type, + children, + treeTitle, + path, + selected, + onCopy, + onDelete, + onSelect, + onReorder, +}: ItemProps) => { + const {draggedItem, setDraggedItem, showPreview, hidePreview} = React.useContext(DragContext); + const [isDragging, setIsDragging] = React.useState(false); + const [isDragOver, setIsDragOver] = React.useState(false); + const [mouseDownPos, setMouseDownPos] = React.useState<{x: number; y: number} | null>(null); + + const handleCopy = React.useCallback(() => { + onCopy(path); + }, [onCopy, path]); + + const handleDelete = React.useCallback(() => { + onDelete(path); + }, [onDelete, path]); + + const handleMouseDown = React.useCallback((e: any) => { + setMouseDownPos({x: e.clientX, y: e.clientY}); + }, []); + + const handleMouseUp = React.useCallback( + (e: any) => { + if (mouseDownPos) { + // Check if the mouse has moved significantly (dragging) or just a click + const dx = Math.abs(e.clientX - mouseDownPos.x); + const dy = Math.abs(e.clientY - mouseDownPos.y); + + // If the mouse hasn't moved much, consider it a click for selection + if (dx < 5 && dy < 5) { + e.stopPropagation(); + onSelect(path); + } + } + setMouseDownPos(null); + }, + [mouseDownPos, onSelect, path], + ); + + const handleDragStart = React.useCallback( + (e: any) => { + e.stopPropagation(); + const dragData: DragItem = { + path, + type, + treeTitle, + }; + e.dataTransfer.setData('application/json', JSON.stringify(dragData)); + e.dataTransfer.effectAllowed = 'move'; + setIsDragging(true); + + // Update drag context + setDraggedItem(dragData); + }, + [path, type, treeTitle, setDraggedItem], + ); + + const handleDragEnd = React.useCallback(() => { + setIsDragging(false); + + // Reset drag context + setDraggedItem(null); + hidePreview(); + }, [setDraggedItem, hidePreview, path]); + + const handleDragOver = React.useCallback( + (e: any) => { + e.preventDefault(); + e.stopPropagation(); + e.dataTransfer.dropEffect = 'move'; + setIsDragOver(true); + + // Show preview element at this position + const rect = e.currentTarget.getBoundingClientRect(); + if (draggedItem) { + showPreview(rect, draggedItem); + } + }, + [showPreview, draggedItem], + ); + + const handleDragLeave = React.useCallback(() => { + setIsDragOver(false); + hidePreview(); + }, [hidePreview]); + + const handleDrop = React.useCallback( + (e: any) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragOver(false); + + try { + const data = e.dataTransfer.getData('application/json'); + if (data) { + const dragItem: DragItem = JSON.parse(data); + if (dragItem.path.join(',') !== path.join(',')) { + onReorder(dragItem.path, path); + + // Reset drag context + setDraggedItem(null); + hidePreview(); + } + } + } catch (error) { + console.error('Error parsing drag data:', error); + } + }, + [onReorder, path, setDraggedItem, hidePreview], + ); + + const handleChildrenFirstPositionDrop = React.useCallback( + (e: any) => { + e.preventDefault(); + e.stopPropagation(); + + try { + const data = e.dataTransfer.getData('application/json'); + if (data) { + const dragItem: DragItem = JSON.parse(data); + // Create a path for the first child position + const firstChildPath = [...path, 0]; + // Reorder to the first position within children + onReorder(dragItem.path, firstChildPath, 'prepend'); + + // Reset drag context + setDraggedItem(null); + hidePreview(); + } + } catch (error) { + console.error('Error parsing drag data:', error); + } + }, + [onReorder, path, setDraggedItem, hidePreview], + ); + + const handleDropZoneDragOver = (e: any) => { + e.preventDefault(); + e.stopPropagation(); + e.dataTransfer.dropEffect = 'move'; + + // Show preview element at this position + const rect = e.currentTarget.getBoundingClientRect(); + if (draggedItem) { + showPreview(rect, draggedItem); + } + }; + + return ( + <Card + className={b({ + selected, + dragging: isDragging, + 'drag-over': isDragOver, + })} + onMouseDown={handleMouseDown} + onMouseUp={handleMouseUp} + draggable + onDragStart={handleDragStart} + onDragEnd={handleDragEnd} + onDragOver={handleDragOver} + onDragLeave={handleDragLeave} + onDrop={handleDrop} + > + <div className={b('main')}> + <div className={b('text')}> + <div className={b('type')}>{type}</div> + <div className={b('title')}>{treeTitle}</div> + </div> + <div className={b('buttons')}> + <Button view="flat" size="xs" onClick={handleCopy}> + <Icon size={12} data={Copy} /> + </Button> + <Button view="flat" size="xs" onClick={handleDelete}> + <Icon size={12} data={TrashBin} /> + </Button> + </div> + </div> + {children && ( + <React.Fragment> + <div + className={b('children-drop-zone')} + onDragOver={handleDropZoneDragOver} + onDragLeave={hidePreview} + onDrop={handleChildrenFirstPositionDrop} + ></div> + {children} + </React.Fragment> + )} + </Card> + ); +}; diff --git a/editor-v2/containers/Tree/index.ts b/editor-v2/containers/Tree/index.ts new file mode 100644 index 0000000000..f5143b3bdd --- /dev/null +++ b/editor-v2/containers/Tree/index.ts @@ -0,0 +1,4 @@ +export {default} from './Tree'; +export * from './DragContext'; +export * from './TreeItem'; +export * from './TreeContent'; diff --git a/editor-v2/hooks/useEditorTabs.tsx b/editor-v2/hooks/useEditorTabs.tsx index fb2199017a..1f0e544fc5 100644 --- a/editor-v2/hooks/useEditorTabs.tsx +++ b/editor-v2/hooks/useEditorTabs.tsx @@ -5,7 +5,7 @@ import BlockConfig from '../containers/BlockConfig/BlockConfig'; import BlocksList from '../containers/BlocksList/BlocksList'; import GlobalConfig from '../containers/GlobalConfig/GlobalConfig'; import SourceCode from '../containers/SourceCode/SourceCode'; -import Tree from '../containers/Tree/Tree'; +import Tree from '../containers/Tree'; export const useEditorTabs = ({ leftTabs = [], From 2954c673d8a159c2291ab3e6ad39da4c2908d902 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Tue, 1 Apr 2025 21:02:15 +0300 Subject: [PATCH 030/118] fix: update editor block config styles --- .../DynamicForm/FieldBase/FieldBase.scss | 42 ++++++++++++++----- .../DynamicForm/FieldBase/FieldBase.tsx | 26 +++--------- .../DynamicForm/Fields/Boolean/Boolean.scss | 10 +++++ .../DynamicForm/Fields/Boolean/Boolean.tsx | 4 +- .../DynamicForm/Fields/Object/Object.tsx | 9 +--- .../DynamicForm/Fields/OneOf/OneOf.scss | 8 ++-- .../DynamicForm/Fields/OneOf/OneOf.tsx | 34 ++++++++++----- .../Object.scss => Select/Select.scss} | 6 +-- .../DynamicForm/Fields/Select/Select.tsx | 5 ++- .../containers/BlockConfig/BlockConfig.scss | 12 ++++-- .../containers/BlockConfig/BlockConfig.tsx | 12 +++--- 11 files changed, 101 insertions(+), 67 deletions(-) create mode 100644 editor-v2/components/DynamicForm/Fields/Boolean/Boolean.scss rename editor-v2/components/DynamicForm/Fields/{Object/Object.scss => Select/Select.scss} (57%) diff --git a/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss b/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss index b029dd1fa7..92209faa58 100644 --- a/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss +++ b/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss @@ -6,8 +6,14 @@ $block: '.#{$ns}field-base'; #{$block} { $class: &; + padding: 16px 12px; + border-bottom: 1px solid var(--g-color-line-generic); position: relative; + display: flex; + flex-direction: row; + align-items: flex-start; + gap: 8px; &:hover { & > #{$class}__top > #{$class}__button { @@ -15,16 +21,36 @@ $block: '.#{$ns}field-base'; } } + &_expandable { + flex-direction: column; + gap: 16px; + + & > #{$block}__top { + width: 100%; + flex-basis: initial; + } + & > #{$block}__children { + width: 100%; + } + } + &__top { display: flex; align-items: center; gap: 4px; - margin-bottom: 8px; + flex: 0 0 64px; + word-break: break-all; + } + + &__children { + flex: 2 1 auto; } &__foldable { + width: 100%; display: flex; align-items: center; + justify-content: space-between; cursor: pointer; } @@ -43,28 +69,24 @@ $block: '.#{$ns}field-base'; &__title { @include text-body-2; + margin: 5px 0; &_size { &_s { @include text-body-1; } + &_m { @include text-body-2; } + &_l { @include text-body-3; } } } - margin-top: 20px; - - &:first-child { - margin-top: 0; - } - - &__arrow-toggle { - //position: absolute; - //right: calc(100%); + &:last-child { + border-bottom: none; } } diff --git a/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx b/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx index 3b4dc5a551..edfb58f079 100644 --- a/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx +++ b/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx @@ -1,5 +1,4 @@ -import {ArrowRotateLeft} from '@gravity-ui/icons'; -import {ArrowToggle, Button, Icon} from '@gravity-ui/uikit'; +import {ArrowToggle} from '@gravity-ui/uikit'; import _ from 'lodash'; import * as React from 'react'; @@ -23,9 +22,8 @@ export interface FieldBaseProps extends React.PropsWithChildren, FieldBaseParams const FieldBase: React.FC<FieldBaseProps> = ({ className, title, - textSize, + textSize = 's', children, - onRefresh, expandable = false, }) => { const [showChildren, setShowChildren] = React.useState(!expandable); @@ -40,11 +38,11 @@ const FieldBase: React.FC<FieldBaseProps> = ({ return ( // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions <div className={b('foldable')} onClick={() => setShowChildren(!showChildren)}> + {defaultTitle} <ArrowToggle direction={showChildren ? 'bottom' : 'right'} className={b('arrow-toggle')} /> - {defaultTitle} </div> ); } @@ -56,22 +54,8 @@ const FieldBase: React.FC<FieldBaseProps> = ({ }, [expandable, showChildren, textSize, title]); return ( - <div className={b(null, className)}> - {title && ( - <div className={b('top')}> - {titleComponent} - {onRefresh && ( - <Button - className={b('button')} - onClick={() => onRefresh(undefined)} - view={'flat'} - size={'xs'} - > - <Icon data={ArrowRotateLeft} size={14} /> - </Button> - )} - </div> - )} + <div className={b({expandable}, className)}> + {title && <div className={b('top')}>{titleComponent}</div>} {(!title || showChildren) && <div className={b('children')}>{children}</div>} </div> ); diff --git a/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.scss b/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.scss new file mode 100644 index 0000000000..a3342d9651 --- /dev/null +++ b/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.scss @@ -0,0 +1,10 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; + +$block: '.#{$ns}boolean-dynamic-field'; + +#{$block} { + &__switch { + margin-top: 5px; + } +} diff --git a/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx b/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx index 1bacfdc8f2..21f3238db5 100644 --- a/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx +++ b/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx @@ -3,6 +3,8 @@ import {Switch} from '@gravity-ui/uikit'; import {editorCn} from '../../../../utils/cn'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; +import './Boolean.scss'; + const b = editorCn('boolean-dynamic-field'); interface BooleanProps extends FieldBaseParams { @@ -14,7 +16,7 @@ interface BooleanProps extends FieldBaseParams { const BooleanDynamicField = ({title, value, onUpdate, className}: BooleanProps) => { return ( <FieldBase title={title} className={b(null, className)} onRefresh={onUpdate}> - <Switch checked={Boolean(value)} onUpdate={onUpdate} /> + <Switch className={b('switch')} checked={Boolean(value)} onUpdate={onUpdate} /> </FieldBase> ); }; diff --git a/editor-v2/components/DynamicForm/Fields/Object/Object.tsx b/editor-v2/components/DynamicForm/Fields/Object/Object.tsx index 72ad24941c..3711df5be3 100644 --- a/editor-v2/components/DynamicForm/Fields/Object/Object.tsx +++ b/editor-v2/components/DynamicForm/Fields/Object/Object.tsx @@ -1,14 +1,9 @@ import {Card} from '@gravity-ui/uikit'; import {ConfigInput, DynamicFormValue} from '../../../../../common/types'; -import {editorCn} from '../../../../utils/cn'; import DynamicForm from '../../DynamicForm'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; -import './Object.scss'; - -const b = editorCn('object-dynamic-field'); - interface ObjectDynamicFieldProps extends FieldBaseParams { value: DynamicFormValue; onUpdate: (key: string, value: DynamicFormValue) => void; @@ -26,11 +21,11 @@ const ObjectDynamicField = ({ return ( <FieldBase title={title} - className={b(null, className)} + className={className} onRefresh={(updatedValue) => onUpdate('', updatedValue)} expandable > - <Card className={b('card')}> + <Card> <DynamicForm contentConfig={value} blockConfig={blockConfig} onUpdate={onUpdate} /> </Card> </FieldBase> diff --git a/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss b/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss index b7660714b8..35de5b6db5 100644 --- a/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss +++ b/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss @@ -4,11 +4,11 @@ $block: '.#{$ns}oneof-dynamic-field'; #{$block} { - &__card { - padding: 12px; - } - &__radio { margin-bottom: 12px; } + + &__select { + min-width: 80px; + } } diff --git a/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx b/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx index 941f5e070d..31ed85ff3f 100644 --- a/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx +++ b/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx @@ -1,4 +1,4 @@ -import {Card, SegmentedRadioGroup} from '@gravity-ui/uikit'; +import {Card, SegmentedRadioGroup, Select} from '@gravity-ui/uikit'; import * as React from 'react'; import {DynamicFormValue, OneOfInput} from '../../../../../common/types'; @@ -55,16 +55,28 @@ const OneOfDynamicField = ({ onRefresh={(value) => onUpdate('', value)} expandable > - <Card className={b('card')}> - <SegmentedRadioGroup - className={b('radio')} - options={inputConfig.options.map((option) => ({ - content: option.title, - value: option.value, - }))} - value={oneOfMetaValue} - onUpdate={onUpdateOneOf} - /> + <Card> + {inputConfig.options.length < 4 ? ( + <SegmentedRadioGroup + className={b('radio')} + options={inputConfig.options.map((option) => ({ + content: option.title, + value: option.value, + }))} + value={oneOfMetaValue} + onUpdate={onUpdateOneOf} + /> + ) : ( + <Select + options={inputConfig.options.map((option) => ({ + content: option.title, + value: option.value, + }))} + value={oneOfMetaValue ? [oneOfMetaValue] : []} + onUpdate={([selectValue]) => onUpdateOneOf(selectValue)} + className={b('select')} + /> + )} {oneOfChosenOption && ( <DynamicForm blockConfig={oneOfChosenOption.properties} diff --git a/editor-v2/components/DynamicForm/Fields/Object/Object.scss b/editor-v2/components/DynamicForm/Fields/Select/Select.scss similarity index 57% rename from editor-v2/components/DynamicForm/Fields/Object/Object.scss rename to editor-v2/components/DynamicForm/Fields/Select/Select.scss index 7092b9aff9..dbc0748bc9 100644 --- a/editor-v2/components/DynamicForm/Fields/Object/Object.scss +++ b/editor-v2/components/DynamicForm/Fields/Select/Select.scss @@ -1,10 +1,10 @@ @import '../../../../styles/variables.scss'; @import '../../../../styles/mixins.scss'; -$block: '.#{$ns}object-dynamic-field'; +$block: '.#{$ns}editor-select-field'; #{$block} { - &__card { - padding: 12px; + &__select { + min-width: 80px; } } diff --git a/editor-v2/components/DynamicForm/Fields/Select/Select.tsx b/editor-v2/components/DynamicForm/Fields/Select/Select.tsx index a61e4e28dc..13bcbe0dcc 100644 --- a/editor-v2/components/DynamicForm/Fields/Select/Select.tsx +++ b/editor-v2/components/DynamicForm/Fields/Select/Select.tsx @@ -4,7 +4,9 @@ import {SelectMultipleInput, SelectSingleInput} from '../../../../../common/type import {editorCn} from '../../../../utils/cn'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; -const b = editorCn('select-field'); +import './Select.scss'; + +const b = editorCn('editor-select-field'); type SelectInput = SelectSingleInput | SelectMultipleInput; @@ -26,6 +28,7 @@ const SelectDynamicField = ({input, value, onUpdate, className}: SelectDynamicFi value={value ? [value] : []} onUpdate={([selectValue]) => onUpdate(selectValue)} options={input.enum} + className={b('select')} /> )} {inputView === 'radiobutton' && ( diff --git a/editor-v2/containers/BlockConfig/BlockConfig.scss b/editor-v2/containers/BlockConfig/BlockConfig.scss index 0411ca8ad0..14921baad8 100644 --- a/editor-v2/containers/BlockConfig/BlockConfig.scss +++ b/editor-v2/containers/BlockConfig/BlockConfig.scss @@ -5,14 +5,18 @@ $block: '.#{$ns}block-config'; #{$block} { - padding: 12px; - &__title { - margin-bottom: 16px; - margin-top: 8px; + @include text-subheader-3; + padding: 12px 16px; + border-bottom: 1px solid var(--g-color-line-generic); + } + + &__form { + padding: 12px 0; } &__empty { + padding: 12px 16px; display: flex; justify-content: center; align-items: center; diff --git a/editor-v2/containers/BlockConfig/BlockConfig.tsx b/editor-v2/containers/BlockConfig/BlockConfig.tsx index e8f1263563..c0b753d07e 100644 --- a/editor-v2/containers/BlockConfig/BlockConfig.tsx +++ b/editor-v2/containers/BlockConfig/BlockConfig.tsx @@ -37,11 +37,13 @@ const BlockConfig = ({className}: BlockConfigProps) => { return ( <div className={b(null, className)}> <div className={b('title')}>{currentSchema.schema.name}</div> - <DynamicForm - contentConfig={currentConfig} - blockConfig={currentSchema.schema.inputs} - onUpdate={onUpdate} - /> + <div className={b('form')}> + <DynamicForm + contentConfig={currentConfig} + blockConfig={currentSchema.schema.inputs} + onUpdate={onUpdate} + /> + </div> </div> ); }; From b32261b0a3ea66f163708b527f3162586a36d3dc Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Wed, 2 Apr 2025 20:56:06 +0300 Subject: [PATCH 031/118] fix: update selected block borders --- common/types/actions.ts | 2 ++ editor-v2/containers/Overlay/Overlay.tsx | 26 ++++++++++++++++++++++-- src/hooks/usePCEditorBlockMouseEvents.ts | 20 +++++++++++++++++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/common/types/actions.ts b/common/types/actions.ts index 2bde27f542..da55bd75f9 100644 --- a/common/types/actions.ts +++ b/common/types/actions.ts @@ -11,6 +11,7 @@ export type EventMessageTypes = { ON_HOVER_BLOCK: {rect?: DOMRect; position?: string}; ON_CLICK_BLOCK: {path: number[]; rect: DOMRect}; ON_RESIZE_BLOCK: {rect: DOMRect}; + ON_UPDATE_SELECTED_BLOCK: {path: number[]; rect: DOMRect}; ON_SUPPORTED_BLOCKS: Pick<EditorState, 'blocks' | 'subBlocks' | 'global'>; ON_INITIAL_CONTENT: PageContentWithNavigation; }; @@ -18,4 +19,5 @@ export type EventMessageTypes = { export type ActionMessageTypes = { GET_SUPPORTED_BLOCKS: {}; GET_INITIAL_CONTENT: {}; + UPDATE_SELECTED_BLOCK: {path: number[]}; }; diff --git a/editor-v2/containers/Overlay/Overlay.tsx b/editor-v2/containers/Overlay/Overlay.tsx index 759d9fbb0a..ea66cd5c5e 100644 --- a/editor-v2/containers/Overlay/Overlay.tsx +++ b/editor-v2/containers/Overlay/Overlay.tsx @@ -4,6 +4,7 @@ import * as React from 'react'; import {usePostMessageAPIListener} from '../../../common/postMessage'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; +import {usePostMessageEvents} from '../../hooks/usePostMessageEvents'; import {editorCn} from '../../utils/cn'; import './Overlay.scss'; @@ -22,6 +23,7 @@ interface InsertLineProps { } const Overlay = ({className}: OverlayProps) => { + const {requestPostMessage} = usePostMessageEvents(); const { height, selectedBlock, @@ -61,13 +63,33 @@ const Overlay = ({className}: OverlayProps) => { setBlockBorders(rect || null); }); + usePostMessageAPIListener( + 'ON_UPDATE_SELECTED_BLOCK', + ({rect, path}) => { + if (selectedBlock && JSON.stringify(selectedBlock) === JSON.stringify(path)) { + setBlockBorders(rect || null); + } + }, + [selectedBlock], + ); + + // Update blockBorders when selectedBlock changes + React.useEffect(() => { + if (!selectedBlock) { + setBlockBorders(null); + } else { + // If a block is selected, trigger the UPDATE_SELECTED_BLOCK action to update blockBorders + requestPostMessage('UPDATE_SELECTED_BLOCK', {path: selectedBlock}); + } + }, [selectedBlock]); + const handleMoveUp = () => { if (!selectedBlock) return; const destination = [...selectedBlock]; destination[destination.length - 1] = destination[destination.length - 1] - 1; reorderBlock(selectedBlock, destination, 'prepend'); setSelectedBlock(undefined); - setBlockBorders(null); + // blockBorders will be set to null by the useEffect hook }; const handleMoveDown = () => { @@ -76,7 +98,7 @@ const Overlay = ({className}: OverlayProps) => { destination[destination.length - 1] = destination[destination.length - 1] + 1; reorderBlock(selectedBlock, destination, 'append'); setSelectedBlock(undefined); - setBlockBorders(null); + // blockBorders will be set to null by the useEffect hook }; return ( diff --git a/src/hooks/usePCEditorBlockMouseEvents.ts b/src/hooks/usePCEditorBlockMouseEvents.ts index 9424223718..5027e2326f 100644 --- a/src/hooks/usePCEditorBlockMouseEvents.ts +++ b/src/hooks/usePCEditorBlockMouseEvents.ts @@ -5,7 +5,7 @@ import _ from 'lodash'; import {getCursorPositionOverElement} from '../utils/editor'; import {usePCEditorStore} from './usePCEditorStore'; -import {sendEventPostMessage} from './usePostMessageAPI'; +import {sendEventPostMessage, useInternalPostMessageAPIListener} from './usePostMessageAPI'; const usePCEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement) => { const {selectedBlock} = usePCEditorStore(); @@ -77,6 +77,24 @@ const usePCEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement }; }, [element, onResize]); + // Listen for the UPDATE_SELECTED_BLOCK action and handle it directly + const handleUpdateSelectedBlock = React.useCallback( + (data: {path?: number[]}) => { + if (data && data.path && Array.isArray(data.path) && _.isEqual(data.path, arrayIndex)) { + // Only proceed if the path matches arrayIndex + if (element) { + const rect = element.getClientRects().item(0); + if (rect) { + sendEventPostMessage('ON_UPDATE_SELECTED_BLOCK', {rect, path: arrayIndex}); + } + } + } + }, + [arrayIndex, element], + ); + + useInternalPostMessageAPIListener('UPDATE_SELECTED_BLOCK', handleUpdateSelectedBlock); + return { onClick, onMouseMove, From 09ba739e5a9c1213a216e5fc962d61e927ca26ce Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Wed, 2 Apr 2025 21:02:22 +0300 Subject: [PATCH 032/118] fix: remove unused hook --- .../hooks/useEditorBlockMouseEvents.tsx | 87 ------------------- 1 file changed, 87 deletions(-) delete mode 100644 src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx diff --git a/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx b/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx deleted file mode 100644 index f42540ea11..0000000000 --- a/src/containers/PageConstructor/components/ConstructorBlock/hooks/useEditorBlockMouseEvents.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import * as React from 'react'; - -import _ from 'lodash'; - -import {usePCEditorStore} from '../../../../../hooks/usePCEditorStore'; -import {sendEventPostMessage} from '../../../../../hooks/usePostMessageAPI'; -import {getCursorPositionOverElement} from '../../../../../utils/editor'; - -const useEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement) => { - const {selectedBlock} = usePCEditorStore(); - - const onMouseUp = React.useCallback( - (e: React.MouseEvent) => { - e.stopPropagation(); - if (element) { - const rect = element.getClientRects().item(0); - if (rect) { - const position = getCursorPositionOverElement(rect, e); - sendEventPostMessage('ON_MOUSE_UP', {path: arrayIndex, rect, position}); - } - } - }, - [arrayIndex, element], - ); - - const onMouseMove = React.useCallback( - (e: React.MouseEvent) => { - e.stopPropagation(); - if (element) { - const rect = element.getClientRects().item(0); - if (rect) { - const position = getCursorPositionOverElement(rect, e); - sendEventPostMessage('ON_HOVER_BLOCK', {rect, position}); - } - } - }, - [element], - ); - - const onMouseLeave = React.useCallback(() => { - if (element) { - const rect = element.getClientRects().item(0); - if (rect) { - sendEventPostMessage('ON_HOVER_BLOCK', {}); - } - } - }, [element]); - - const onClick = React.useCallback( - (e: React.MouseEvent) => { - e.stopPropagation(); - if (element) { - const rect = element.getClientRects().item(0); - if (rect) { - sendEventPostMessage('ON_CLICK_BLOCK', {rect, path: arrayIndex}); - } - } - }, - [arrayIndex, element], - ); - - const onResize = React.useCallback(() => { - if (element && _.isEqual(selectedBlock, arrayIndex)) { - const rect = element.getClientRects().item(0); - if (rect) { - sendEventPostMessage('ON_RESIZE_BLOCK', {rect}); - } - } - }, [arrayIndex, element, selectedBlock]); - - React.useEffect(() => { - window.addEventListener('resize', onResize); - - return () => { - window.removeEventListener('resize', onResize); - }; - }, [element, onResize]); - - return { - onClick, - onMouseMove, - onMouseUp, - onMouseLeave, - }; -}; - -export default useEditorBlockMouseEvents; From dc8a47048af7ad4068dddb7189dd13cb2dce11ad Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Wed, 2 Apr 2025 21:09:40 +0300 Subject: [PATCH 033/118] fix: remove block selection from click event --- common/types/actions.ts | 2 +- editor-v2/containers/Overlay/Overlay.tsx | 3 +-- src/hooks/usePCEditorBlockMouseEvents.ts | 5 +---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/common/types/actions.ts b/common/types/actions.ts index da55bd75f9..6751c228bc 100644 --- a/common/types/actions.ts +++ b/common/types/actions.ts @@ -9,7 +9,7 @@ export type EventMessageTypes = { ON_MOUSE_UP: {path?: number[]; rect?: DOMRect; position?: string}; ON_MOUSE_MOVE: {x: number; y: number}; ON_HOVER_BLOCK: {rect?: DOMRect; position?: string}; - ON_CLICK_BLOCK: {path: number[]; rect: DOMRect}; + ON_CLICK_BLOCK: {path: number[]}; ON_RESIZE_BLOCK: {rect: DOMRect}; ON_UPDATE_SELECTED_BLOCK: {path: number[]; rect: DOMRect}; ON_SUPPORTED_BLOCKS: Pick<EditorState, 'blocks' | 'subBlocks' | 'global'>; diff --git a/editor-v2/containers/Overlay/Overlay.tsx b/editor-v2/containers/Overlay/Overlay.tsx index ea66cd5c5e..1a97cf9a16 100644 --- a/editor-v2/containers/Overlay/Overlay.tsx +++ b/editor-v2/containers/Overlay/Overlay.tsx @@ -54,9 +54,8 @@ const Overlay = ({className}: OverlayProps) => { } }); - usePostMessageAPIListener('ON_CLICK_BLOCK', ({rect, path}) => { + usePostMessageAPIListener('ON_CLICK_BLOCK', ({path}) => { setSelectedBlock(path); - setBlockBorders(rect || null); }); usePostMessageAPIListener('ON_RESIZE_BLOCK', ({rect}) => { diff --git a/src/hooks/usePCEditorBlockMouseEvents.ts b/src/hooks/usePCEditorBlockMouseEvents.ts index 5027e2326f..c758d5c223 100644 --- a/src/hooks/usePCEditorBlockMouseEvents.ts +++ b/src/hooks/usePCEditorBlockMouseEvents.ts @@ -51,10 +51,7 @@ const usePCEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement (e: React.MouseEvent) => { e.stopPropagation(); if (element) { - const rect = element.getClientRects().item(0); - if (rect) { - sendEventPostMessage('ON_CLICK_BLOCK', {rect, path: arrayIndex}); - } + sendEventPostMessage('ON_CLICK_BLOCK', {path: arrayIndex}); } }, [arrayIndex, element], From de7fb474467dc6e43754c691e972c13721a68af3 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Thu, 3 Apr 2025 12:57:37 +0300 Subject: [PATCH 034/118] feat: add scroll to selected block --- .../containers/MiddleScreen/MiddleScreen.tsx | 4 +- editor-v2/containers/Overlay/Overlay.tsx | 36 +++---------- editor-v2/hooks/useSelectedBlockBorders.ts | 54 +++++++++++++++++++ .../editor/ChildrenWrap/ChildrenWrap.scss | 8 --- .../editor/ChildrenWrap/ChildrenWrap.tsx | 28 ---------- 5 files changed, 63 insertions(+), 67 deletions(-) create mode 100644 editor-v2/hooks/useSelectedBlockBorders.ts delete mode 100644 src/components/editor/ChildrenWrap/ChildrenWrap.scss delete mode 100644 src/components/editor/ChildrenWrap/ChildrenWrap.tsx diff --git a/editor-v2/containers/MiddleScreen/MiddleScreen.tsx b/editor-v2/containers/MiddleScreen/MiddleScreen.tsx index 6ccf9dbc3d..198c759ef8 100644 --- a/editor-v2/containers/MiddleScreen/MiddleScreen.tsx +++ b/editor-v2/containers/MiddleScreen/MiddleScreen.tsx @@ -19,6 +19,7 @@ interface MiddleScreenProps { const MiddleScreen = ({className, CustomTop}: MiddleScreenProps) => { const {zoom, initialized, deviceWidth} = useMainEditorStore(); const {url, setIframeElement} = React.useContext(IframeContext); + const [canvasRef, setCanvasRef] = React.useState<HTMLDivElement | null>(null); const [height, setHeight] = React.useState(0); const onResize = React.useCallback( @@ -46,6 +47,7 @@ const MiddleScreen = ({className, CustomTop}: MiddleScreenProps) => { <div className={b('content')}> <div className={b('wrapper')} style={{width: deviceWidth}}> <div + ref={setCanvasRef} className={b('canvas', {hidden: !initialized})} style={{ transform: `scale(${zoom}%)`, @@ -62,7 +64,7 @@ const MiddleScreen = ({className, CustomTop}: MiddleScreenProps) => { frameBorder="0" title="Page Constructor Iframe" /> - <Overlay className={b('overlay')} /> + <Overlay className={b('overlay')} canvasElement={canvasRef} /> {!initialized && ( <div className={b('loading')}> <Loader size={'l'} /> diff --git a/editor-v2/containers/Overlay/Overlay.tsx b/editor-v2/containers/Overlay/Overlay.tsx index 1a97cf9a16..4c6c22e054 100644 --- a/editor-v2/containers/Overlay/Overlay.tsx +++ b/editor-v2/containers/Overlay/Overlay.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import {usePostMessageAPIListener} from '../../../common/postMessage'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; -import {usePostMessageEvents} from '../../hooks/usePostMessageEvents'; +import {useSelectedBlockBorders} from '../../hooks/useSelectedBlockBorders'; import {editorCn} from '../../utils/cn'; import './Overlay.scss'; @@ -13,6 +13,7 @@ const b = editorCn('overlay'); interface OverlayProps { className?: string; + canvasElement?: HTMLDivElement | null; } interface InsertLineProps { top: number; @@ -22,8 +23,7 @@ interface InsertLineProps { position: string; } -const Overlay = ({className}: OverlayProps) => { - const {requestPostMessage} = usePostMessageEvents(); +const Overlay = ({className, canvasElement}: OverlayProps) => { const { height, selectedBlock, @@ -37,7 +37,9 @@ const Overlay = ({className}: OverlayProps) => { undefined, ); const [hoverBorders, setHoverBorders] = React.useState<DOMRect | null>(null); - const [blockBorders, setBlockBorders] = React.useState<DOMRect | null>(null); + + // Use the hook to get blockBorders and handle auto-scrolling + const blockBorders = useSelectedBlockBorders(selectedBlock, canvasElement); const margin = 0; @@ -58,37 +60,12 @@ const Overlay = ({className}: OverlayProps) => { setSelectedBlock(path); }); - usePostMessageAPIListener('ON_RESIZE_BLOCK', ({rect}) => { - setBlockBorders(rect || null); - }); - - usePostMessageAPIListener( - 'ON_UPDATE_SELECTED_BLOCK', - ({rect, path}) => { - if (selectedBlock && JSON.stringify(selectedBlock) === JSON.stringify(path)) { - setBlockBorders(rect || null); - } - }, - [selectedBlock], - ); - - // Update blockBorders when selectedBlock changes - React.useEffect(() => { - if (!selectedBlock) { - setBlockBorders(null); - } else { - // If a block is selected, trigger the UPDATE_SELECTED_BLOCK action to update blockBorders - requestPostMessage('UPDATE_SELECTED_BLOCK', {path: selectedBlock}); - } - }, [selectedBlock]); - const handleMoveUp = () => { if (!selectedBlock) return; const destination = [...selectedBlock]; destination[destination.length - 1] = destination[destination.length - 1] - 1; reorderBlock(selectedBlock, destination, 'prepend'); setSelectedBlock(undefined); - // blockBorders will be set to null by the useEffect hook }; const handleMoveDown = () => { @@ -97,7 +74,6 @@ const Overlay = ({className}: OverlayProps) => { destination[destination.length - 1] = destination[destination.length - 1] + 1; reorderBlock(selectedBlock, destination, 'append'); setSelectedBlock(undefined); - // blockBorders will be set to null by the useEffect hook }; return ( diff --git a/editor-v2/hooks/useSelectedBlockBorders.ts b/editor-v2/hooks/useSelectedBlockBorders.ts new file mode 100644 index 0000000000..41cff4f7d7 --- /dev/null +++ b/editor-v2/hooks/useSelectedBlockBorders.ts @@ -0,0 +1,54 @@ +import * as React from 'react'; + +import {usePostMessageAPIListener} from '../../common/postMessage'; +import {usePostMessageEvents} from './usePostMessageEvents'; + +/** + * Hook to track selected block borders and auto-scroll to the selected block + * @param selectedBlock The currently selected block path + * @param canvasElement The canvas element reference that contains the iframe + * @returns The borders of the selected block + */ +export const useSelectedBlockBorders = ( + selectedBlock: any, + canvasElement?: HTMLDivElement | null, +) => { + const [blockBorders, setBlockBorders] = React.useState<DOMRect | null>(null); + const {requestPostMessage} = usePostMessageEvents(); + + // Listen for updates to the selected block's position + usePostMessageAPIListener('ON_UPDATE_SELECTED_BLOCK', ({rect}) => { + setBlockBorders(rect || null); + }); + + // Update blockBorders when selectedBlock changes + React.useEffect(() => { + console.log(321, selectedBlock); + if (!selectedBlock) { + setBlockBorders(null); + } else { + // Only request an update if we haven't already done so for this block + requestPostMessage('UPDATE_SELECTED_BLOCK', {path: selectedBlock}); + } + }, [selectedBlock]); + + // Auto scroll to the selected block when blockBorders changes + React.useEffect(() => { + if (blockBorders && canvasElement) { + // Calculate the scroll position to center the block in the viewport + const canvasHeight = canvasElement.clientHeight; + const scrollPosition = blockBorders.top - canvasHeight / 2 + blockBorders.height / 2; + + // Scroll the canvas element to the calculated position with smooth behavior + canvasElement.scrollTo({ + top: Math.max(0, scrollPosition), + behavior: 'smooth', + }); + } + }, [blockBorders, canvasElement]); + + React.useEffect(() => { + console.log('blockBorders'); + }, [blockBorders]); + return blockBorders; +}; diff --git a/src/components/editor/ChildrenWrap/ChildrenWrap.scss b/src/components/editor/ChildrenWrap/ChildrenWrap.scss deleted file mode 100644 index c583e86a84..0000000000 --- a/src/components/editor/ChildrenWrap/ChildrenWrap.scss +++ /dev/null @@ -1,8 +0,0 @@ -@import '../../../../styles/mixins.scss'; -@import '../../../../styles/variables.scss'; - -$block: '.#{$ns}children-wrap'; - -#{$block} { - min-height: 50px; -} diff --git a/src/components/editor/ChildrenWrap/ChildrenWrap.tsx b/src/components/editor/ChildrenWrap/ChildrenWrap.tsx deleted file mode 100644 index 068ece91b4..0000000000 --- a/src/components/editor/ChildrenWrap/ChildrenWrap.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import * as React from 'react'; - -import {usePCEditorItemWrap} from '../../../hooks/usePCEditorItemWrap'; -import {block} from '../../../utils'; - -import './ChildrenWrap.scss'; - -const b = block('children-wrap'); - -export interface ChildrenWrapProps extends React.PropsWithChildren { - checkChildren?: React.ReactNode; -} - -const ChildrenWrap = ({children}: ChildrenWrapProps) => { - const { - blockRef, - adminBlockMouseEvents: {onMouseMove, onMouseUp}, - } = usePCEditorItemWrap(); - - return ( - // eslint-disable-next-line jsx-a11y/no-static-element-interactions - <div ref={blockRef} className={b()} onMouseMove={onMouseMove} onMouseUp={onMouseUp}> - {children} - </div> - ); -}; - -export default ChildrenWrap; From da9795bf2fdaf632cb33eb66afd8f39e6f2348e5 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Thu, 3 Apr 2025 13:01:46 +0300 Subject: [PATCH 035/118] feat: add scroll to selected block in the Tree component --- editor-v2/containers/Tree/TreeItem.tsx | 12 ++++++++++++ editor-v2/hooks/useSelectedBlockBorders.ts | 4 ---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/editor-v2/containers/Tree/TreeItem.tsx b/editor-v2/containers/Tree/TreeItem.tsx index 96fbf51e05..dc80e83a25 100644 --- a/editor-v2/containers/Tree/TreeItem.tsx +++ b/editor-v2/containers/Tree/TreeItem.tsx @@ -40,6 +40,17 @@ export const Item = ({ const [isDragging, setIsDragging] = React.useState(false); const [isDragOver, setIsDragOver] = React.useState(false); const [mouseDownPos, setMouseDownPos] = React.useState<{x: number; y: number} | null>(null); + const itemRef = React.useRef<HTMLDivElement>(null); + + // Scroll into view when selected + React.useEffect(() => { + if (selected && itemRef.current) { + itemRef.current.scrollIntoView({ + behavior: 'smooth', + block: 'nearest', + }); + } + }, [selected]); const handleCopy = React.useCallback(() => { onCopy(path); @@ -182,6 +193,7 @@ export const Item = ({ return ( <Card + ref={itemRef} className={b({ selected, dragging: isDragging, diff --git a/editor-v2/hooks/useSelectedBlockBorders.ts b/editor-v2/hooks/useSelectedBlockBorders.ts index 41cff4f7d7..087979809f 100644 --- a/editor-v2/hooks/useSelectedBlockBorders.ts +++ b/editor-v2/hooks/useSelectedBlockBorders.ts @@ -23,7 +23,6 @@ export const useSelectedBlockBorders = ( // Update blockBorders when selectedBlock changes React.useEffect(() => { - console.log(321, selectedBlock); if (!selectedBlock) { setBlockBorders(null); } else { @@ -47,8 +46,5 @@ export const useSelectedBlockBorders = ( } }, [blockBorders, canvasElement]); - React.useEffect(() => { - console.log('blockBorders'); - }, [blockBorders]); return blockBorders; }; From 4e615f2869e971f4d09a3ae1c2bdba0dd69b9208 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Thu, 3 Apr 2025 16:19:43 +0300 Subject: [PATCH 036/118] fix: selected block after blocks reorder --- editor-v2/containers/Overlay/Overlay.tsx | 2 -- editor-v2/hooks/useMainEditorInitialize.ts | 2 -- editor-v2/store.ts | 17 +++++++++++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/editor-v2/containers/Overlay/Overlay.tsx b/editor-v2/containers/Overlay/Overlay.tsx index 4c6c22e054..8e4c2f3393 100644 --- a/editor-v2/containers/Overlay/Overlay.tsx +++ b/editor-v2/containers/Overlay/Overlay.tsx @@ -65,7 +65,6 @@ const Overlay = ({className, canvasElement}: OverlayProps) => { const destination = [...selectedBlock]; destination[destination.length - 1] = destination[destination.length - 1] - 1; reorderBlock(selectedBlock, destination, 'prepend'); - setSelectedBlock(undefined); }; const handleMoveDown = () => { @@ -73,7 +72,6 @@ const Overlay = ({className, canvasElement}: OverlayProps) => { const destination = [...selectedBlock]; destination[destination.length - 1] = destination[destination.length - 1] + 1; reorderBlock(selectedBlock, destination, 'append'); - setSelectedBlock(undefined); }; return ( diff --git a/editor-v2/hooks/useMainEditorInitialize.ts b/editor-v2/hooks/useMainEditorInitialize.ts index b1ba595600..2326b95ec9 100644 --- a/editor-v2/hooks/useMainEditorInitialize.ts +++ b/editor-v2/hooks/useMainEditorInitialize.ts @@ -13,7 +13,6 @@ const useMainEditorInitialize = () => { disableMode, insertBlock, reorderBlock, - setSelectedBlock, preInsertBlockType, preReorderBlockPath, } = useMainEditorStore(); @@ -52,7 +51,6 @@ const useMainEditorInitialize = () => { path, ['left', 'top'].includes(position) ? 'prepend' : 'append', ); - setSelectedBlock(path); } disableMode(); }, diff --git a/editor-v2/store.ts b/editor-v2/store.ts index 5760689458..9fefcf93e9 100644 --- a/editor-v2/store.ts +++ b/editor-v2/store.ts @@ -109,6 +109,9 @@ export const createEditorStore = initializeStore<EditorState, EditorMethods>( ...state, content: {...state.content, blocks: newBlocksConfig}, })); + + // Set the inserted block as selected + get().setSelectedBlock(arrayPath); }, enableInsertMode(blockType: string) { set((state) => ({ @@ -180,6 +183,9 @@ export const createEditorStore = initializeStore<EditorState, EditorMethods>( })); }, reorderBlock: (arrayPath, destination, position = 'append') => { + // Create a copy of the destination array before any modifications + let finalDestinationPath: number[] = _.cloneDeep(destination); + if (position === 'append') { // TODO: fix // eslint-disable-next-line no-not-accumulator-reassign/no-not-accumulator-reassign, no-param-reassign @@ -199,8 +205,17 @@ export const createEditorStore = initializeStore<EditorState, EditorMethods>( destination[destination.length - 1], ); }); + + if ( + position === 'append' && + destination[destination.length - 1] < arrayPath[arrayPath.length - 1] + ) { + finalDestinationPath[finalDestinationPath.length - 1] = + finalDestinationPath[finalDestinationPath.length - 1] + 1; + } } else { const arrayDest = getDestinationShiftBeforeReorder(arrayPath, destination); + finalDestinationPath = _.cloneDeep(arrayDest); // Delete const blocksConfigWithoutBlock = modifyObjectByPath( @@ -220,6 +235,8 @@ export const createEditorStore = initializeStore<EditorState, EditorMethods>( ...state, content: {...state.content, blocks: newBlocksConfig}, })); + + get().setSelectedBlock(finalDestinationPath); }, resetInitialize: () => { set((state) => ({ From 13ab234406733ea47a429183b25c7b5cea7e8e29 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Thu, 3 Apr 2025 18:05:48 +0300 Subject: [PATCH 037/118] fix: refactor pc editor events --- common/types/actions.ts | 4 +- editor-v2/containers/Overlay/Overlay.tsx | 30 +++++++++++-- editor-v2/hooks/useSelectedBlockBorders.ts | 50 ---------------------- src/hooks/usePCEditorBlockMouseEvents.ts | 40 +---------------- src/hooks/usePCEditorBlockSelection.ts | 32 ++++++++++++++ src/hooks/usePCEditorItemWrap.ts | 2 + 6 files changed, 63 insertions(+), 95 deletions(-) delete mode 100644 editor-v2/hooks/useSelectedBlockBorders.ts create mode 100644 src/hooks/usePCEditorBlockSelection.ts diff --git a/common/types/actions.ts b/common/types/actions.ts index 6751c228bc..ecb2bcd9f4 100644 --- a/common/types/actions.ts +++ b/common/types/actions.ts @@ -10,8 +10,7 @@ export type EventMessageTypes = { ON_MOUSE_MOVE: {x: number; y: number}; ON_HOVER_BLOCK: {rect?: DOMRect; position?: string}; ON_CLICK_BLOCK: {path: number[]}; - ON_RESIZE_BLOCK: {rect: DOMRect}; - ON_UPDATE_SELECTED_BLOCK: {path: number[]; rect: DOMRect}; + ON_UPDATE_BLOCK_SELECTION: {rect: DOMRect}; ON_SUPPORTED_BLOCKS: Pick<EditorState, 'blocks' | 'subBlocks' | 'global'>; ON_INITIAL_CONTENT: PageContentWithNavigation; }; @@ -19,5 +18,4 @@ export type EventMessageTypes = { export type ActionMessageTypes = { GET_SUPPORTED_BLOCKS: {}; GET_INITIAL_CONTENT: {}; - UPDATE_SELECTED_BLOCK: {path: number[]}; }; diff --git a/editor-v2/containers/Overlay/Overlay.tsx b/editor-v2/containers/Overlay/Overlay.tsx index 8e4c2f3393..6d8933909e 100644 --- a/editor-v2/containers/Overlay/Overlay.tsx +++ b/editor-v2/containers/Overlay/Overlay.tsx @@ -4,7 +4,6 @@ import * as React from 'react'; import {usePostMessageAPIListener} from '../../../common/postMessage'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; -import {useSelectedBlockBorders} from '../../hooks/useSelectedBlockBorders'; import {editorCn} from '../../utils/cn'; import './Overlay.scss'; @@ -37,9 +36,34 @@ const Overlay = ({className, canvasElement}: OverlayProps) => { undefined, ); const [hoverBorders, setHoverBorders] = React.useState<DOMRect | null>(null); + const [blockBorders, setBlockBorders] = React.useState<DOMRect | null>(null); - // Use the hook to get blockBorders and handle auto-scrolling - const blockBorders = useSelectedBlockBorders(selectedBlock, canvasElement); + // Listen for updates to the selected block's position + usePostMessageAPIListener('ON_UPDATE_BLOCK_SELECTION', ({rect}) => { + setBlockBorders(rect || null); + }); + + // Update blockBorders when selectedBlock changes + React.useEffect(() => { + if (!selectedBlock) { + setBlockBorders(null); + } + }, [selectedBlock]); + + // Auto scroll to the selected block when blockBorders changes + React.useEffect(() => { + if (blockBorders && canvasElement) { + // Calculate the scroll position to center the block in the viewport + const canvasHeight = canvasElement.clientHeight; + const scrollPosition = blockBorders.top - canvasHeight / 2 + blockBorders.height / 2; + + // Scroll the canvas element to the calculated position with smooth behavior + canvasElement.scrollTo({ + top: Math.max(0, scrollPosition), + behavior: 'smooth', + }); + } + }, [blockBorders, canvasElement]); const margin = 0; diff --git a/editor-v2/hooks/useSelectedBlockBorders.ts b/editor-v2/hooks/useSelectedBlockBorders.ts deleted file mode 100644 index 087979809f..0000000000 --- a/editor-v2/hooks/useSelectedBlockBorders.ts +++ /dev/null @@ -1,50 +0,0 @@ -import * as React from 'react'; - -import {usePostMessageAPIListener} from '../../common/postMessage'; -import {usePostMessageEvents} from './usePostMessageEvents'; - -/** - * Hook to track selected block borders and auto-scroll to the selected block - * @param selectedBlock The currently selected block path - * @param canvasElement The canvas element reference that contains the iframe - * @returns The borders of the selected block - */ -export const useSelectedBlockBorders = ( - selectedBlock: any, - canvasElement?: HTMLDivElement | null, -) => { - const [blockBorders, setBlockBorders] = React.useState<DOMRect | null>(null); - const {requestPostMessage} = usePostMessageEvents(); - - // Listen for updates to the selected block's position - usePostMessageAPIListener('ON_UPDATE_SELECTED_BLOCK', ({rect}) => { - setBlockBorders(rect || null); - }); - - // Update blockBorders when selectedBlock changes - React.useEffect(() => { - if (!selectedBlock) { - setBlockBorders(null); - } else { - // Only request an update if we haven't already done so for this block - requestPostMessage('UPDATE_SELECTED_BLOCK', {path: selectedBlock}); - } - }, [selectedBlock]); - - // Auto scroll to the selected block when blockBorders changes - React.useEffect(() => { - if (blockBorders && canvasElement) { - // Calculate the scroll position to center the block in the viewport - const canvasHeight = canvasElement.clientHeight; - const scrollPosition = blockBorders.top - canvasHeight / 2 + blockBorders.height / 2; - - // Scroll the canvas element to the calculated position with smooth behavior - canvasElement.scrollTo({ - top: Math.max(0, scrollPosition), - behavior: 'smooth', - }); - } - }, [blockBorders, canvasElement]); - - return blockBorders; -}; diff --git a/src/hooks/usePCEditorBlockMouseEvents.ts b/src/hooks/usePCEditorBlockMouseEvents.ts index c758d5c223..8dea23f82e 100644 --- a/src/hooks/usePCEditorBlockMouseEvents.ts +++ b/src/hooks/usePCEditorBlockMouseEvents.ts @@ -4,12 +4,9 @@ import _ from 'lodash'; import {getCursorPositionOverElement} from '../utils/editor'; -import {usePCEditorStore} from './usePCEditorStore'; -import {sendEventPostMessage, useInternalPostMessageAPIListener} from './usePostMessageAPI'; +import {sendEventPostMessage} from './usePostMessageAPI'; const usePCEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement) => { - const {selectedBlock} = usePCEditorStore(); - const onMouseUp = React.useCallback( (e: React.MouseEvent) => { e.stopPropagation(); @@ -57,41 +54,6 @@ const usePCEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement [arrayIndex, element], ); - const onResize = React.useCallback(() => { - if (element && _.isEqual(selectedBlock, arrayIndex)) { - const rect = element.getClientRects().item(0); - if (rect) { - sendEventPostMessage('ON_RESIZE_BLOCK', {rect}); - } - } - }, [arrayIndex, element, selectedBlock]); - - React.useEffect(() => { - window.addEventListener('resize', onResize); - - return () => { - window.removeEventListener('resize', onResize); - }; - }, [element, onResize]); - - // Listen for the UPDATE_SELECTED_BLOCK action and handle it directly - const handleUpdateSelectedBlock = React.useCallback( - (data: {path?: number[]}) => { - if (data && data.path && Array.isArray(data.path) && _.isEqual(data.path, arrayIndex)) { - // Only proceed if the path matches arrayIndex - if (element) { - const rect = element.getClientRects().item(0); - if (rect) { - sendEventPostMessage('ON_UPDATE_SELECTED_BLOCK', {rect, path: arrayIndex}); - } - } - } - }, - [arrayIndex, element], - ); - - useInternalPostMessageAPIListener('UPDATE_SELECTED_BLOCK', handleUpdateSelectedBlock); - return { onClick, onMouseMove, diff --git a/src/hooks/usePCEditorBlockSelection.ts b/src/hooks/usePCEditorBlockSelection.ts new file mode 100644 index 0000000000..41bb4f8b5f --- /dev/null +++ b/src/hooks/usePCEditorBlockSelection.ts @@ -0,0 +1,32 @@ +import * as React from 'react'; +import _ from 'lodash'; + +import {usePCEditorStore} from './usePCEditorStore'; +import {sendEventPostMessage} from './usePostMessageAPI'; + +const usePCEditorBlockSelection = (arrayIndex: number[], element?: HTMLElement) => { + const {selectedBlock} = usePCEditorStore(); + + const onResize = React.useCallback(() => { + if (element && _.isEqual(selectedBlock, arrayIndex)) { + const rect = element.getClientRects().item(0); + if (rect) { + sendEventPostMessage('ON_UPDATE_BLOCK_SELECTION', {rect}); + } + } + }, [JSON.stringify(arrayIndex), element, JSON.stringify(selectedBlock)]); + + React.useEffect(() => { + window.addEventListener('resize', onResize); + + return () => { + window.removeEventListener('resize', onResize); + }; + }, [element, onResize]); + + React.useEffect(() => { + onResize(); + }, [onResize]); +}; + +export default usePCEditorBlockSelection; diff --git a/src/hooks/usePCEditorItemWrap.ts b/src/hooks/usePCEditorItemWrap.ts index 6ab498a313..eb7eb55a86 100644 --- a/src/hooks/usePCEditorItemWrap.ts +++ b/src/hooks/usePCEditorItemWrap.ts @@ -3,6 +3,7 @@ import * as React from 'react'; import {BlockIdContext} from '../context/blockIdContext'; import usePCEditorBlockMouseEvents from './usePCEditorBlockMouseEvents'; +import usePCEditorBlockSelection from './usePCEditorBlockSelection'; export function usePCEditorItemWrap(index = 0) { const [element, setElement] = React.useState<HTMLElement | undefined>(); @@ -15,6 +16,7 @@ export function usePCEditorItemWrap(index = 0) { const parentBlockId = React.useContext(BlockIdContext); const adminBlockMouseEvents = usePCEditorBlockMouseEvents([...parentBlockId, index], element); + usePCEditorBlockSelection([...parentBlockId, index], element); return {adminBlockMouseEvents, blockRef}; } From 26c5ba6fa6412638d92fb9415d7bc84598365d9c Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Thu, 3 Apr 2025 19:32:58 +0300 Subject: [PATCH 038/118] feat: unselect block on deletion --- common/store.ts | 4 ++-- editor-v2/store.ts | 11 ++++------- src/hooks/usePCEditorBlockSelection.ts | 3 ++- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/common/store.ts b/common/store.ts index 796c844f4d..668684374d 100644 --- a/common/store.ts +++ b/common/store.ts @@ -11,7 +11,7 @@ export interface EditorState { zoom: number; manipulateOverlayMode: 'insert' | 'reorder' | false; - selectedBlock?: number[]; + selectedBlock: number[] | null; initialized: boolean; content: PageContentWithNavigation; @@ -28,7 +28,7 @@ export const initialStore: EditorState = { deviceWidth: '100%', zoom: 100, manipulateOverlayMode: false, - selectedBlock: undefined, + selectedBlock: null, initialized: false, content: {blocks: []}, blocks: [], diff --git a/editor-v2/store.ts b/editor-v2/store.ts index 9fefcf93e9..17c50cb65c 100644 --- a/editor-v2/store.ts +++ b/editor-v2/store.ts @@ -19,7 +19,7 @@ import { export interface EditorMethods { initialize(): void; - setSelectedBlock(path?: number[]): void; + setSelectedBlock(path: number[] | null): void; setHeight(height: number): void; setDeviceWidth(deviceWidth: string): void; setZoom(zoom: number): void; @@ -108,10 +108,8 @@ export const createEditorStore = initializeStore<EditorState, EditorMethods>( set((state) => ({ ...state, content: {...state.content, blocks: newBlocksConfig}, + selectedBlock: arrayPath, })); - - // Set the inserted block as selected - get().setSelectedBlock(arrayPath); }, enableInsertMode(blockType: string) { set((state) => ({ @@ -166,10 +164,10 @@ export const createEditorStore = initializeStore<EditorState, EditorMethods>( const blocksConfig = get().content.blocks; const newBlocksConfig = modifyObjectByPath(blocksConfig, arrayPath, removeFromArray); - set((state) => ({ ...state, content: {...state.content, blocks: newBlocksConfig}, + selectedBlock: null, })); }, duplicateBlock: (arrayPath) => { @@ -234,9 +232,8 @@ export const createEditorStore = initializeStore<EditorState, EditorMethods>( set((state) => ({ ...state, content: {...state.content, blocks: newBlocksConfig}, + selectedBlock: finalDestinationPath, })); - - get().setSelectedBlock(finalDestinationPath); }, resetInitialize: () => { set((state) => ({ diff --git a/src/hooks/usePCEditorBlockSelection.ts b/src/hooks/usePCEditorBlockSelection.ts index 41bb4f8b5f..eb934b92c2 100644 --- a/src/hooks/usePCEditorBlockSelection.ts +++ b/src/hooks/usePCEditorBlockSelection.ts @@ -14,7 +14,7 @@ const usePCEditorBlockSelection = (arrayIndex: number[], element?: HTMLElement) sendEventPostMessage('ON_UPDATE_BLOCK_SELECTION', {rect}); } } - }, [JSON.stringify(arrayIndex), element, JSON.stringify(selectedBlock)]); + }, [arrayIndex, element, selectedBlock]); React.useEffect(() => { window.addEventListener('resize', onResize); @@ -24,6 +24,7 @@ const usePCEditorBlockSelection = (arrayIndex: number[], element?: HTMLElement) }; }, [element, onResize]); + // Update blockBorders when selectedBlock changes React.useEffect(() => { onResize(); }, [onResize]); From 048e1ba851d0f858ea589294c3c6692ac9305d9e Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Fri, 4 Apr 2025 12:42:24 +0300 Subject: [PATCH 039/118] fix: eslint --- editor-v2/containers/Tree/TreeContent.tsx | 6 ++-- editor-v2/containers/Tree/TreeItem.tsx | 30 +++++++++++-------- .../containers/ViewSwitches/ViewSwitches.tsx | 7 ----- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/editor-v2/containers/Tree/TreeContent.tsx b/editor-v2/containers/Tree/TreeContent.tsx index bc2e252340..afa07066fb 100644 --- a/editor-v2/containers/Tree/TreeContent.tsx +++ b/editor-v2/containers/Tree/TreeContent.tsx @@ -39,7 +39,7 @@ export const TreeContent = ({ const {draggedItem, setDraggedItem, hidePreview, showPreview} = React.useContext(DragContext); const handleFirstPositionDrop = React.useCallback( - (e: any) => { + (e: React.DragEvent<HTMLDivElement>) => { e.preventDefault(); e.stopPropagation(); @@ -56,6 +56,7 @@ export const TreeContent = ({ hidePreview(); } } catch (error) { + // eslint-disable-next-line no-console console.error('Error parsing drag data:', error); } }, @@ -63,9 +64,10 @@ export const TreeContent = ({ ); const handleDragOver = React.useCallback( - (e: any) => { + (e: React.DragEvent<HTMLDivElement>) => { e.preventDefault(); e.stopPropagation(); + // eslint-disable-next-line no-not-accumulator-reassign/no-not-accumulator-reassign, no-param-reassign e.dataTransfer.dropEffect = 'move'; // Show preview element at this position diff --git a/editor-v2/containers/Tree/TreeItem.tsx b/editor-v2/containers/Tree/TreeItem.tsx index dc80e83a25..80da0af574 100644 --- a/editor-v2/containers/Tree/TreeItem.tsx +++ b/editor-v2/containers/Tree/TreeItem.tsx @@ -60,12 +60,12 @@ export const Item = ({ onDelete(path); }, [onDelete, path]); - const handleMouseDown = React.useCallback((e: any) => { + const handleMouseDown = React.useCallback((e: React.MouseEvent<HTMLDivElement>) => { setMouseDownPos({x: e.clientX, y: e.clientY}); }, []); const handleMouseUp = React.useCallback( - (e: any) => { + (e: React.MouseEvent<HTMLDivElement>) => { if (mouseDownPos) { // Check if the mouse has moved significantly (dragging) or just a click const dx = Math.abs(e.clientX - mouseDownPos.x); @@ -83,7 +83,7 @@ export const Item = ({ ); const handleDragStart = React.useCallback( - (e: any) => { + (e: React.DragEvent<HTMLDivElement>) => { e.stopPropagation(); const dragData: DragItem = { path, @@ -91,6 +91,7 @@ export const Item = ({ treeTitle, }; e.dataTransfer.setData('application/json', JSON.stringify(dragData)); + // eslint-disable-next-line no-not-accumulator-reassign/no-not-accumulator-reassign, no-param-reassign e.dataTransfer.effectAllowed = 'move'; setIsDragging(true); @@ -109,13 +110,13 @@ export const Item = ({ }, [setDraggedItem, hidePreview, path]); const handleDragOver = React.useCallback( - (e: any) => { + (e: React.DragEvent<HTMLDivElement>) => { e.preventDefault(); e.stopPropagation(); + // eslint-disable-next-line no-not-accumulator-reassign/no-not-accumulator-reassign, no-param-reassign e.dataTransfer.dropEffect = 'move'; setIsDragOver(true); - // Show preview element at this position const rect = e.currentTarget.getBoundingClientRect(); if (draggedItem) { showPreview(rect, draggedItem); @@ -130,7 +131,7 @@ export const Item = ({ }, [hidePreview]); const handleDrop = React.useCallback( - (e: any) => { + (e: React.DragEvent<HTMLDivElement>) => { e.preventDefault(); e.stopPropagation(); setIsDragOver(false); @@ -148,6 +149,7 @@ export const Item = ({ } } } catch (error) { + // eslint-disable-next-line no-console console.error('Error parsing drag data:', error); } }, @@ -155,7 +157,7 @@ export const Item = ({ ); const handleChildrenFirstPositionDrop = React.useCallback( - (e: any) => { + (e: React.DragEvent<HTMLDivElement>) => { e.preventDefault(); e.stopPropagation(); @@ -173,15 +175,17 @@ export const Item = ({ hidePreview(); } } catch (error) { + // eslint-disable-next-line no-console console.error('Error parsing drag data:', error); } }, [onReorder, path, setDraggedItem, hidePreview], ); - const handleDropZoneDragOver = (e: any) => { + const handleDropZoneDragOver = (e: React.DragEvent<HTMLDivElement>) => { e.preventDefault(); e.stopPropagation(); + // eslint-disable-next-line no-not-accumulator-reassign/no-not-accumulator-reassign, no-param-reassign e.dataTransfer.dropEffect = 'move'; // Show preview element at this position @@ -199,14 +203,14 @@ export const Item = ({ dragging: isDragging, 'drag-over': isDragOver, })} - onMouseDown={handleMouseDown} - onMouseUp={handleMouseUp} + onMouseDown={handleMouseDown as unknown as React.MouseEventHandler<'div'>} + onMouseUp={handleMouseUp as unknown as React.MouseEventHandler<'div'>} draggable - onDragStart={handleDragStart} + onDragStart={handleDragStart as unknown as React.DragEventHandler<'div'>} onDragEnd={handleDragEnd} - onDragOver={handleDragOver} + onDragOver={handleDragOver as unknown as React.DragEventHandler<'div'>} onDragLeave={handleDragLeave} - onDrop={handleDrop} + onDrop={handleDrop as unknown as React.DragEventHandler<'div'>} > <div className={b('main')}> <div className={b('text')}> diff --git a/editor-v2/containers/ViewSwitches/ViewSwitches.tsx b/editor-v2/containers/ViewSwitches/ViewSwitches.tsx index fbebdcfb86..78139a36a4 100644 --- a/editor-v2/containers/ViewSwitches/ViewSwitches.tsx +++ b/editor-v2/containers/ViewSwitches/ViewSwitches.tsx @@ -46,13 +46,6 @@ const DEVICE_OPTIONS: DeviceOption[] = [ }, ]; -/** - * ViewSwitches component - * - * Provides UI controls for: - * 1. Switching between device viewport widths (desktop, tablet, mobile) - * 2. Adjusting zoom level with increment/decrement buttons and dropdown - */ const ViewSwitches: React.FC = () => { const {zoom, setZoom, decreaseZoom, increaseZoom, deviceWidth, setDeviceWidth} = useMainEditorStore(); From f90dc9b926419306d6dee8eeabcc614d4273cf27 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 7 Apr 2025 12:04:38 +0300 Subject: [PATCH 040/118] fix: return editor-v2 back to src --- editor-v2/utils/store.ts | 0 playground/src/app/page.tsx | 6 +++--- .../editor-v2}/components/DynamicForm/DynamicForm.scss | 0 .../editor-v2}/components/DynamicForm/DynamicForm.tsx | 2 +- .../components/DynamicForm/FieldBase/FieldBase.scss | 0 .../components/DynamicForm/FieldBase/FieldBase.tsx | 0 .../components/DynamicForm/Fields/Array/Array.scss | 0 .../components/DynamicForm/Fields/Array/Array.tsx | 2 +- .../DynamicForm/Fields/Array/ItemButton/ItemButton.tsx | 0 .../components/DynamicForm/Fields/Boolean/Boolean.scss | 0 .../components/DynamicForm/Fields/Boolean/Boolean.tsx | 0 .../components/DynamicForm/Fields/Number/Number.tsx | 0 .../components/DynamicForm/Fields/Object/Object.tsx | 2 +- .../components/DynamicForm/Fields/OneOf/OneOf.scss | 0 .../components/DynamicForm/Fields/OneOf/OneOf.tsx | 2 +- .../components/DynamicForm/Fields/Select/Select.scss | 0 .../components/DynamicForm/Fields/Select/Select.tsx | 2 +- .../components/DynamicForm/Fields/Text/Text.tsx | 0 .../components/DynamicForm/Fields/TextArea/TextArea.tsx | 0 .../editor-v2}/components/DynamicForm/utils.ts | 2 +- .../editor-v2}/components/Panels/Panels.scss | 0 .../editor-v2}/components/Panels/Panels.tsx | 0 .../editor-v2}/components/Sidebar/Sidebar.scss | 0 .../editor-v2}/components/Sidebar/Sidebar.tsx | 0 .../editor-v2}/components/StoreViewer/StoreViewer.scss | 0 .../editor-v2}/components/StoreViewer/StoreViewer.tsx | 2 +- {editor-v2 => src/editor-v2}/components/Tabs/Tabs.scss | 0 {editor-v2 => src/editor-v2}/components/Tabs/Tabs.tsx | 0 {editor-v2 => src/editor-v2}/constants.ts | 0 .../editor-v2}/containers/BigOverlay/BigOverlay.scss | 2 -- .../editor-v2}/containers/BigOverlay/BigOverlay.tsx | 2 +- .../editor-v2}/containers/BlockConfig/BlockConfig.scss | 0 .../editor-v2}/containers/BlockConfig/BlockConfig.tsx | 2 +- .../editor-v2}/containers/BlocksList/BlocksList.scss | 0 .../editor-v2}/containers/BlocksList/BlocksList.tsx | 2 +- .../editor-v2}/containers/Editor/Editor.scss | 0 .../editor-v2}/containers/Editor/Editor.tsx | 2 +- .../editor-v2}/containers/GlobalConfig/GlobalConfig.scss | 0 .../editor-v2}/containers/GlobalConfig/GlobalConfig.tsx | 4 ++-- .../editor-v2}/containers/MiddleScreen/MiddleScreen.scss | 0 .../editor-v2}/containers/MiddleScreen/MiddleScreen.tsx | 2 +- .../editor-v2}/containers/Overlay/Overlay.scss | 0 .../editor-v2}/containers/Overlay/Overlay.tsx | 2 +- .../editor-v2}/containers/Source/Source.scss | 0 .../editor-v2}/containers/Source/Source.tsx | 0 .../editor-v2}/containers/SourceCode/SourceCode.scss | 0 .../editor-v2}/containers/SourceCode/SourceCode.tsx | 2 +- .../containers/SourceCode/UpdateModal/UpdateModal.scss | 0 .../containers/SourceCode/UpdateModal/UpdateModal.tsx | 9 +++++++-- .../editor-v2}/containers/Tree/DragContext.scss | 0 .../editor-v2}/containers/Tree/DragContext.tsx | 2 ++ {editor-v2 => src/editor-v2}/containers/Tree/Tree.scss | 0 {editor-v2 => src/editor-v2}/containers/Tree/Tree.tsx | 0 .../editor-v2}/containers/Tree/TreeContent.scss | 0 .../editor-v2}/containers/Tree/TreeContent.tsx | 0 .../editor-v2}/containers/Tree/TreeItem.scss | 0 .../editor-v2}/containers/Tree/TreeItem.tsx | 0 {editor-v2 => src/editor-v2}/containers/Tree/index.ts | 0 .../editor-v2}/containers/ViewSwitches/ViewSwitches.scss | 0 .../editor-v2}/containers/ViewSwitches/ViewSwitches.tsx | 0 .../editor-v2}/containers/__stories__/Editor.stories.tsx | 0 .../editor-v2}/containers/__stories__/utils.ts | 0 .../context/editorStore/MainEditorStoreContext.tsx | 0 .../context/editorStore/MainEditorStoreProvider.tsx | 6 +++--- .../editor-v2}/context/editorStore/index.ts | 0 .../editor-v2}/context/iframeContext/IframeContext.tsx | 0 .../editor-v2}/context/iframeContext/IframeProvider.tsx | 0 .../editor-v2}/context/iframeContext/index.ts | 0 {editor-v2 => src/editor-v2}/hooks/useEditorTabs.tsx | 0 .../editor-v2}/hooks/useMainEditorInitialize.ts | 2 +- {editor-v2 => src/editor-v2}/hooks/useMainEditorStore.ts | 0 .../editor-v2}/hooks/usePostMessageEvents.ts | 4 ++-- {editor-v2 => src/editor-v2}/index.ts | 2 +- {editor-v2 => src/editor-v2}/store.ts | 8 ++++---- {editor-v2 => src/editor-v2}/styles/mixins.scss | 0 {editor-v2 => src/editor-v2}/styles/root.scss | 0 {editor-v2 => src/editor-v2}/styles/variables.scss | 0 {editor-v2 => src/editor-v2}/utils/cn.ts | 0 {editor-v2 => src/editor-v2}/utils/code.ts | 2 +- {editor-v2 => src/editor-v2}/utils/index.ts | 2 +- 80 files changed, 41 insertions(+), 36 deletions(-) delete mode 100644 editor-v2/utils/store.ts rename {editor-v2 => src/editor-v2}/components/DynamicForm/DynamicForm.scss (100%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/DynamicForm.tsx (99%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/FieldBase/FieldBase.scss (100%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/FieldBase/FieldBase.tsx (100%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/Fields/Array/Array.scss (100%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/Fields/Array/Array.tsx (99%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx (100%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/Fields/Boolean/Boolean.scss (100%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/Fields/Boolean/Boolean.tsx (100%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/Fields/Number/Number.tsx (100%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/Fields/Object/Object.tsx (92%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/Fields/OneOf/OneOf.scss (100%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/Fields/OneOf/OneOf.tsx (97%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/Fields/Select/Select.scss (100%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/Fields/Select/Select.tsx (98%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/Fields/Text/Text.tsx (100%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/Fields/TextArea/TextArea.tsx (100%) rename {editor-v2 => src/editor-v2}/components/DynamicForm/utils.ts (87%) rename {editor-v2 => src/editor-v2}/components/Panels/Panels.scss (100%) rename {editor-v2 => src/editor-v2}/components/Panels/Panels.tsx (100%) rename {editor-v2 => src/editor-v2}/components/Sidebar/Sidebar.scss (100%) rename {editor-v2 => src/editor-v2}/components/Sidebar/Sidebar.tsx (100%) rename {editor-v2 => src/editor-v2}/components/StoreViewer/StoreViewer.scss (100%) rename {editor-v2 => src/editor-v2}/components/StoreViewer/StoreViewer.tsx (95%) rename {editor-v2 => src/editor-v2}/components/Tabs/Tabs.scss (100%) rename {editor-v2 => src/editor-v2}/components/Tabs/Tabs.tsx (100%) rename {editor-v2 => src/editor-v2}/constants.ts (100%) rename {editor-v2 => src/editor-v2}/containers/BigOverlay/BigOverlay.scss (90%) rename {editor-v2 => src/editor-v2}/containers/BigOverlay/BigOverlay.tsx (97%) rename {editor-v2 => src/editor-v2}/containers/BlockConfig/BlockConfig.scss (100%) rename {editor-v2 => src/editor-v2}/containers/BlockConfig/BlockConfig.tsx (96%) rename {editor-v2 => src/editor-v2}/containers/BlocksList/BlocksList.scss (100%) rename {editor-v2 => src/editor-v2}/containers/BlocksList/BlocksList.tsx (98%) rename {editor-v2 => src/editor-v2}/containers/Editor/Editor.scss (100%) rename {editor-v2 => src/editor-v2}/containers/Editor/Editor.tsx (97%) rename {editor-v2 => src/editor-v2}/containers/GlobalConfig/GlobalConfig.scss (100%) rename {editor-v2 => src/editor-v2}/containers/GlobalConfig/GlobalConfig.tsx (89%) rename {editor-v2 => src/editor-v2}/containers/MiddleScreen/MiddleScreen.scss (100%) rename {editor-v2 => src/editor-v2}/containers/MiddleScreen/MiddleScreen.tsx (97%) rename {editor-v2 => src/editor-v2}/containers/Overlay/Overlay.scss (100%) rename {editor-v2 => src/editor-v2}/containers/Overlay/Overlay.tsx (98%) rename {editor-v2 => src/editor-v2}/containers/Source/Source.scss (100%) rename {editor-v2 => src/editor-v2}/containers/Source/Source.tsx (100%) rename {editor-v2 => src/editor-v2}/containers/SourceCode/SourceCode.scss (100%) rename {editor-v2 => src/editor-v2}/containers/SourceCode/SourceCode.tsx (97%) rename {editor-v2 => src/editor-v2}/containers/SourceCode/UpdateModal/UpdateModal.scss (100%) rename {editor-v2 => src/editor-v2}/containers/SourceCode/UpdateModal/UpdateModal.tsx (86%) rename {editor-v2 => src/editor-v2}/containers/Tree/DragContext.scss (100%) rename {editor-v2 => src/editor-v2}/containers/Tree/DragContext.tsx (98%) rename {editor-v2 => src/editor-v2}/containers/Tree/Tree.scss (100%) rename {editor-v2 => src/editor-v2}/containers/Tree/Tree.tsx (100%) rename {editor-v2 => src/editor-v2}/containers/Tree/TreeContent.scss (100%) rename {editor-v2 => src/editor-v2}/containers/Tree/TreeContent.tsx (100%) rename {editor-v2 => src/editor-v2}/containers/Tree/TreeItem.scss (100%) rename {editor-v2 => src/editor-v2}/containers/Tree/TreeItem.tsx (100%) rename {editor-v2 => src/editor-v2}/containers/Tree/index.ts (100%) rename {editor-v2 => src/editor-v2}/containers/ViewSwitches/ViewSwitches.scss (100%) rename {editor-v2 => src/editor-v2}/containers/ViewSwitches/ViewSwitches.tsx (100%) rename {editor-v2 => src/editor-v2}/containers/__stories__/Editor.stories.tsx (100%) rename {editor-v2 => src/editor-v2}/containers/__stories__/utils.ts (100%) rename {editor-v2 => src/editor-v2}/context/editorStore/MainEditorStoreContext.tsx (100%) rename {editor-v2 => src/editor-v2}/context/editorStore/MainEditorStoreProvider.tsx (89%) rename {editor-v2 => src/editor-v2}/context/editorStore/index.ts (100%) rename {editor-v2 => src/editor-v2}/context/iframeContext/IframeContext.tsx (100%) rename {editor-v2 => src/editor-v2}/context/iframeContext/IframeProvider.tsx (100%) rename {editor-v2 => src/editor-v2}/context/iframeContext/index.ts (100%) rename {editor-v2 => src/editor-v2}/hooks/useEditorTabs.tsx (100%) rename {editor-v2 => src/editor-v2}/hooks/useMainEditorInitialize.ts (96%) rename {editor-v2 => src/editor-v2}/hooks/useMainEditorStore.ts (100%) rename {editor-v2 => src/editor-v2}/hooks/usePostMessageEvents.ts (84%) rename {editor-v2 => src/editor-v2}/index.ts (58%) rename {editor-v2 => src/editor-v2}/store.ts (97%) rename {editor-v2 => src/editor-v2}/styles/mixins.scss (100%) rename {editor-v2 => src/editor-v2}/styles/root.scss (100%) rename {editor-v2 => src/editor-v2}/styles/variables.scss (100%) rename {editor-v2 => src/editor-v2}/utils/cn.ts (100%) rename {editor-v2 => src/editor-v2}/utils/code.ts (83%) rename {editor-v2 => src/editor-v2}/utils/index.ts (99%) diff --git a/editor-v2/utils/store.ts b/editor-v2/utils/store.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/playground/src/app/page.tsx b/playground/src/app/page.tsx index ddae138bf8..2051474097 100644 --- a/playground/src/app/page.tsx +++ b/playground/src/app/page.tsx @@ -3,9 +3,9 @@ import {ThemeProvider} from '@gravity-ui/uikit'; import block from 'bem-cn-lite'; import * as React from 'react'; -import {Editor} from '../../../editor-v2'; -import Source from '../../../editor-v2/containers/Source/Source'; -import ViewSwitches from '../../../editor-v2/containers/ViewSwitches/ViewSwitches'; +import {Editor} from '../../../src/editor-v2'; +import Source from '../../../src/editor-v2/containers/Source/Source'; +import ViewSwitches from '../../../src/editor-v2/containers/ViewSwitches/ViewSwitches'; import C9RComponent from './components/C9RComponent'; import './page.scss'; diff --git a/editor-v2/components/DynamicForm/DynamicForm.scss b/src/editor-v2/components/DynamicForm/DynamicForm.scss similarity index 100% rename from editor-v2/components/DynamicForm/DynamicForm.scss rename to src/editor-v2/components/DynamicForm/DynamicForm.scss diff --git a/editor-v2/components/DynamicForm/DynamicForm.tsx b/src/editor-v2/components/DynamicForm/DynamicForm.tsx similarity index 99% rename from editor-v2/components/DynamicForm/DynamicForm.tsx rename to src/editor-v2/components/DynamicForm/DynamicForm.tsx index 9531c21815..74a80edc17 100644 --- a/editor-v2/components/DynamicForm/DynamicForm.tsx +++ b/src/editor-v2/components/DynamicForm/DynamicForm.tsx @@ -1,7 +1,7 @@ import _ from 'lodash'; import * as React from 'react'; -import {ConfigInput, DynamicFormValue} from '../../../common/types'; +import {ConfigInput, DynamicFormValue} from '../../../../common/types'; import {editorCn} from '../../utils/cn'; import './DynamicForm.scss'; diff --git a/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss similarity index 100% rename from editor-v2/components/DynamicForm/FieldBase/FieldBase.scss rename to src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss diff --git a/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx similarity index 100% rename from editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx rename to src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx diff --git a/editor-v2/components/DynamicForm/Fields/Array/Array.scss b/src/editor-v2/components/DynamicForm/Fields/Array/Array.scss similarity index 100% rename from editor-v2/components/DynamicForm/Fields/Array/Array.scss rename to src/editor-v2/components/DynamicForm/Fields/Array/Array.scss diff --git a/editor-v2/components/DynamicForm/Fields/Array/Array.tsx b/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx similarity index 99% rename from editor-v2/components/DynamicForm/Fields/Array/Array.tsx rename to src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx index 295225c737..31381279fe 100644 --- a/editor-v2/components/DynamicForm/Fields/Array/Array.tsx +++ b/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx @@ -2,7 +2,7 @@ import {Plus} from '@gravity-ui/icons'; import {Button, Card, Icon} from '@gravity-ui/uikit'; import * as React from 'react'; -import {ArrayObjectInput, ArrayTextInput, DynamicFormValue} from '../../../../../common/types'; +import {ArrayObjectInput, ArrayTextInput, DynamicFormValue} from '../../../../../../common/types'; import {removeFromArray, swapArrayItems} from '../../../../utils'; import {editorCn} from '../../../../utils/cn'; import DynamicForm from '../../DynamicForm'; diff --git a/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx b/src/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx similarity index 100% rename from editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx rename to src/editor-v2/components/DynamicForm/Fields/Array/ItemButton/ItemButton.tsx diff --git a/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.scss b/src/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.scss similarity index 100% rename from editor-v2/components/DynamicForm/Fields/Boolean/Boolean.scss rename to src/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.scss diff --git a/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx b/src/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx similarity index 100% rename from editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx rename to src/editor-v2/components/DynamicForm/Fields/Boolean/Boolean.tsx diff --git a/editor-v2/components/DynamicForm/Fields/Number/Number.tsx b/src/editor-v2/components/DynamicForm/Fields/Number/Number.tsx similarity index 100% rename from editor-v2/components/DynamicForm/Fields/Number/Number.tsx rename to src/editor-v2/components/DynamicForm/Fields/Number/Number.tsx diff --git a/editor-v2/components/DynamicForm/Fields/Object/Object.tsx b/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx similarity index 92% rename from editor-v2/components/DynamicForm/Fields/Object/Object.tsx rename to src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx index 3711df5be3..e6839cba84 100644 --- a/editor-v2/components/DynamicForm/Fields/Object/Object.tsx +++ b/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx @@ -1,6 +1,6 @@ import {Card} from '@gravity-ui/uikit'; -import {ConfigInput, DynamicFormValue} from '../../../../../common/types'; +import {ConfigInput, DynamicFormValue} from '../../../../../../common/types'; import DynamicForm from '../../DynamicForm'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; diff --git a/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss b/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss similarity index 100% rename from editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss rename to src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss diff --git a/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx b/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx similarity index 97% rename from editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx rename to src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx index 31ed85ff3f..77559d1e7e 100644 --- a/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx +++ b/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx @@ -1,7 +1,7 @@ import {Card, SegmentedRadioGroup, Select} from '@gravity-ui/uikit'; import * as React from 'react'; -import {DynamicFormValue, OneOfInput} from '../../../../../common/types'; +import {DynamicFormValue, OneOfInput} from '../../../../../../common/types'; import {editorCn} from '../../../../utils/cn'; import DynamicForm from '../../DynamicForm'; import FieldBase from '../../FieldBase/FieldBase'; diff --git a/editor-v2/components/DynamicForm/Fields/Select/Select.scss b/src/editor-v2/components/DynamicForm/Fields/Select/Select.scss similarity index 100% rename from editor-v2/components/DynamicForm/Fields/Select/Select.scss rename to src/editor-v2/components/DynamicForm/Fields/Select/Select.scss diff --git a/editor-v2/components/DynamicForm/Fields/Select/Select.tsx b/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx similarity index 98% rename from editor-v2/components/DynamicForm/Fields/Select/Select.tsx rename to src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx index 13bcbe0dcc..6d3eaabfef 100644 --- a/editor-v2/components/DynamicForm/Fields/Select/Select.tsx +++ b/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx @@ -1,6 +1,6 @@ import {SegmentedRadioGroup, Select} from '@gravity-ui/uikit'; -import {SelectMultipleInput, SelectSingleInput} from '../../../../../common/types'; +import {SelectMultipleInput, SelectSingleInput} from '../../../../../../common/types'; import {editorCn} from '../../../../utils/cn'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; diff --git a/editor-v2/components/DynamicForm/Fields/Text/Text.tsx b/src/editor-v2/components/DynamicForm/Fields/Text/Text.tsx similarity index 100% rename from editor-v2/components/DynamicForm/Fields/Text/Text.tsx rename to src/editor-v2/components/DynamicForm/Fields/Text/Text.tsx diff --git a/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx b/src/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx similarity index 100% rename from editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx rename to src/editor-v2/components/DynamicForm/Fields/TextArea/TextArea.tsx diff --git a/editor-v2/components/DynamicForm/utils.ts b/src/editor-v2/components/DynamicForm/utils.ts similarity index 87% rename from editor-v2/components/DynamicForm/utils.ts rename to src/editor-v2/components/DynamicForm/utils.ts index 5ad3f80b7c..2449db5329 100644 --- a/editor-v2/components/DynamicForm/utils.ts +++ b/src/editor-v2/components/DynamicForm/utils.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {DynamicFormValue} from '../../../common/types'; +import {DynamicFormValue} from '../../../../common/types'; export const getFullPath = (path: string, name: string) => { if (!path && !name) { diff --git a/editor-v2/components/Panels/Panels.scss b/src/editor-v2/components/Panels/Panels.scss similarity index 100% rename from editor-v2/components/Panels/Panels.scss rename to src/editor-v2/components/Panels/Panels.scss diff --git a/editor-v2/components/Panels/Panels.tsx b/src/editor-v2/components/Panels/Panels.tsx similarity index 100% rename from editor-v2/components/Panels/Panels.tsx rename to src/editor-v2/components/Panels/Panels.tsx diff --git a/editor-v2/components/Sidebar/Sidebar.scss b/src/editor-v2/components/Sidebar/Sidebar.scss similarity index 100% rename from editor-v2/components/Sidebar/Sidebar.scss rename to src/editor-v2/components/Sidebar/Sidebar.scss diff --git a/editor-v2/components/Sidebar/Sidebar.tsx b/src/editor-v2/components/Sidebar/Sidebar.tsx similarity index 100% rename from editor-v2/components/Sidebar/Sidebar.tsx rename to src/editor-v2/components/Sidebar/Sidebar.tsx diff --git a/editor-v2/components/StoreViewer/StoreViewer.scss b/src/editor-v2/components/StoreViewer/StoreViewer.scss similarity index 100% rename from editor-v2/components/StoreViewer/StoreViewer.scss rename to src/editor-v2/components/StoreViewer/StoreViewer.scss diff --git a/editor-v2/components/StoreViewer/StoreViewer.tsx b/src/editor-v2/components/StoreViewer/StoreViewer.tsx similarity index 95% rename from editor-v2/components/StoreViewer/StoreViewer.tsx rename to src/editor-v2/components/StoreViewer/StoreViewer.tsx index 753842df29..b931bd0845 100644 --- a/editor-v2/components/StoreViewer/StoreViewer.tsx +++ b/src/editor-v2/components/StoreViewer/StoreViewer.tsx @@ -3,7 +3,7 @@ import {Button, Icon} from '@gravity-ui/uikit'; import * as React from 'react'; import ReactJson from 'react-json-view'; -import {removeFn} from '../../../common/utils'; +import {removeFn} from '../../../../common/utils'; import {editorCn} from '../../utils/cn'; import './StoreViewer.scss'; diff --git a/editor-v2/components/Tabs/Tabs.scss b/src/editor-v2/components/Tabs/Tabs.scss similarity index 100% rename from editor-v2/components/Tabs/Tabs.scss rename to src/editor-v2/components/Tabs/Tabs.scss diff --git a/editor-v2/components/Tabs/Tabs.tsx b/src/editor-v2/components/Tabs/Tabs.tsx similarity index 100% rename from editor-v2/components/Tabs/Tabs.tsx rename to src/editor-v2/components/Tabs/Tabs.tsx diff --git a/editor-v2/constants.ts b/src/editor-v2/constants.ts similarity index 100% rename from editor-v2/constants.ts rename to src/editor-v2/constants.ts diff --git a/editor-v2/containers/BigOverlay/BigOverlay.scss b/src/editor-v2/containers/BigOverlay/BigOverlay.scss similarity index 90% rename from editor-v2/containers/BigOverlay/BigOverlay.scss rename to src/editor-v2/containers/BigOverlay/BigOverlay.scss index 1ae968b872..3d79aed84a 100644 --- a/editor-v2/containers/BigOverlay/BigOverlay.scss +++ b/src/editor-v2/containers/BigOverlay/BigOverlay.scss @@ -1,5 +1,3 @@ -@import '../../../styles/variables.scss'; -@import '../../../styles/mixins.scss'; @import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/editor-v2/containers/BigOverlay/BigOverlay.tsx b/src/editor-v2/containers/BigOverlay/BigOverlay.tsx similarity index 97% rename from editor-v2/containers/BigOverlay/BigOverlay.tsx rename to src/editor-v2/containers/BigOverlay/BigOverlay.tsx index 34d50fefc7..eb21962bd3 100644 --- a/editor-v2/containers/BigOverlay/BigOverlay.tsx +++ b/src/editor-v2/containers/BigOverlay/BigOverlay.tsx @@ -1,7 +1,7 @@ import {Stop} from '@gravity-ui/icons'; import * as React from 'react'; -import {usePostMessageAPIListener} from '../../../common/postMessage'; +import {usePostMessageAPIListener} from '../../../../common/postMessage'; import {IframeContext} from '../../context/iframeContext'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; diff --git a/editor-v2/containers/BlockConfig/BlockConfig.scss b/src/editor-v2/containers/BlockConfig/BlockConfig.scss similarity index 100% rename from editor-v2/containers/BlockConfig/BlockConfig.scss rename to src/editor-v2/containers/BlockConfig/BlockConfig.scss diff --git a/editor-v2/containers/BlockConfig/BlockConfig.tsx b/src/editor-v2/containers/BlockConfig/BlockConfig.tsx similarity index 96% rename from editor-v2/containers/BlockConfig/BlockConfig.tsx rename to src/editor-v2/containers/BlockConfig/BlockConfig.tsx index c0b753d07e..39a67238dd 100644 --- a/editor-v2/containers/BlockConfig/BlockConfig.tsx +++ b/src/editor-v2/containers/BlockConfig/BlockConfig.tsx @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {DynamicFormValue} from '../../../common/types'; +import {DynamicFormValue} from '../../../../common/types'; import DynamicForm from '../../components/DynamicForm/DynamicForm'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {generateChildrenPathFromArray} from '../../utils'; diff --git a/editor-v2/containers/BlocksList/BlocksList.scss b/src/editor-v2/containers/BlocksList/BlocksList.scss similarity index 100% rename from editor-v2/containers/BlocksList/BlocksList.scss rename to src/editor-v2/containers/BlocksList/BlocksList.scss diff --git a/editor-v2/containers/BlocksList/BlocksList.tsx b/src/editor-v2/containers/BlocksList/BlocksList.tsx similarity index 98% rename from editor-v2/containers/BlocksList/BlocksList.tsx rename to src/editor-v2/containers/BlocksList/BlocksList.tsx index b8652f238d..2f7dd25e32 100644 --- a/editor-v2/containers/BlocksList/BlocksList.tsx +++ b/src/editor-v2/containers/BlocksList/BlocksList.tsx @@ -2,7 +2,7 @@ import {Magnifier, SquareBars} from '@gravity-ui/icons'; import {Card, Icon, TextInput} from '@gravity-ui/uikit'; import * as React from 'react'; -import {ItemConfig} from '../../../common/types'; +import {ItemConfig} from '../../../../common/types'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; diff --git a/editor-v2/containers/Editor/Editor.scss b/src/editor-v2/containers/Editor/Editor.scss similarity index 100% rename from editor-v2/containers/Editor/Editor.scss rename to src/editor-v2/containers/Editor/Editor.scss diff --git a/editor-v2/containers/Editor/Editor.tsx b/src/editor-v2/containers/Editor/Editor.tsx similarity index 97% rename from editor-v2/containers/Editor/Editor.tsx rename to src/editor-v2/containers/Editor/Editor.tsx index 8b98d5d509..fd4dbe3974 100644 --- a/editor-v2/containers/Editor/Editor.tsx +++ b/src/editor-v2/containers/Editor/Editor.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import {PageContentWithNavigation} from '../../../src/models'; +import {PageContentWithNavigation} from '../../../models'; import {Panels} from '../../components/Panels/Panels'; import {Sidebar} from '../../components/Sidebar/Sidebar'; import StoreViewer from '../../components/StoreViewer/StoreViewer'; diff --git a/editor-v2/containers/GlobalConfig/GlobalConfig.scss b/src/editor-v2/containers/GlobalConfig/GlobalConfig.scss similarity index 100% rename from editor-v2/containers/GlobalConfig/GlobalConfig.scss rename to src/editor-v2/containers/GlobalConfig/GlobalConfig.scss diff --git a/editor-v2/containers/GlobalConfig/GlobalConfig.tsx b/src/editor-v2/containers/GlobalConfig/GlobalConfig.tsx similarity index 89% rename from editor-v2/containers/GlobalConfig/GlobalConfig.tsx rename to src/editor-v2/containers/GlobalConfig/GlobalConfig.tsx index f360555308..9299a3150f 100644 --- a/editor-v2/containers/GlobalConfig/GlobalConfig.tsx +++ b/src/editor-v2/containers/GlobalConfig/GlobalConfig.tsx @@ -1,4 +1,4 @@ -import {DynamicFormValue} from '../../../common/types'; +import {DynamicFormValue} from '../../../../common/types'; import DynamicForm from '../../components/DynamicForm/DynamicForm'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; @@ -7,7 +7,7 @@ import './GlobalConfig.scss'; const b = editorCn('global-config'); -interface GlobalConfigProps { +export interface GlobalConfigProps { className?: string; } diff --git a/editor-v2/containers/MiddleScreen/MiddleScreen.scss b/src/editor-v2/containers/MiddleScreen/MiddleScreen.scss similarity index 100% rename from editor-v2/containers/MiddleScreen/MiddleScreen.scss rename to src/editor-v2/containers/MiddleScreen/MiddleScreen.scss diff --git a/editor-v2/containers/MiddleScreen/MiddleScreen.tsx b/src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx similarity index 97% rename from editor-v2/containers/MiddleScreen/MiddleScreen.tsx rename to src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx index 198c759ef8..9ab96a5dae 100644 --- a/editor-v2/containers/MiddleScreen/MiddleScreen.tsx +++ b/src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx @@ -1,7 +1,7 @@ import {Loader} from '@gravity-ui/uikit'; import * as React from 'react'; -import {usePostMessageAPIListener} from '../../../common/postMessage'; +import {usePostMessageAPIListener} from '../../../../common/postMessage'; import {IframeContext} from '../../context/iframeContext'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; diff --git a/editor-v2/containers/Overlay/Overlay.scss b/src/editor-v2/containers/Overlay/Overlay.scss similarity index 100% rename from editor-v2/containers/Overlay/Overlay.scss rename to src/editor-v2/containers/Overlay/Overlay.scss diff --git a/editor-v2/containers/Overlay/Overlay.tsx b/src/editor-v2/containers/Overlay/Overlay.tsx similarity index 98% rename from editor-v2/containers/Overlay/Overlay.tsx rename to src/editor-v2/containers/Overlay/Overlay.tsx index 6d8933909e..ac77598816 100644 --- a/editor-v2/containers/Overlay/Overlay.tsx +++ b/src/editor-v2/containers/Overlay/Overlay.tsx @@ -2,7 +2,7 @@ import {ChevronDown, ChevronUp, Copy, TrashBin} from '@gravity-ui/icons'; import {Button, Icon} from '@gravity-ui/uikit'; import * as React from 'react'; -import {usePostMessageAPIListener} from '../../../common/postMessage'; +import {usePostMessageAPIListener} from '../../../../common/postMessage'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; diff --git a/editor-v2/containers/Source/Source.scss b/src/editor-v2/containers/Source/Source.scss similarity index 100% rename from editor-v2/containers/Source/Source.scss rename to src/editor-v2/containers/Source/Source.scss diff --git a/editor-v2/containers/Source/Source.tsx b/src/editor-v2/containers/Source/Source.tsx similarity index 100% rename from editor-v2/containers/Source/Source.tsx rename to src/editor-v2/containers/Source/Source.tsx diff --git a/editor-v2/containers/SourceCode/SourceCode.scss b/src/editor-v2/containers/SourceCode/SourceCode.scss similarity index 100% rename from editor-v2/containers/SourceCode/SourceCode.scss rename to src/editor-v2/containers/SourceCode/SourceCode.scss diff --git a/editor-v2/containers/SourceCode/SourceCode.tsx b/src/editor-v2/containers/SourceCode/SourceCode.tsx similarity index 97% rename from editor-v2/containers/SourceCode/SourceCode.tsx rename to src/editor-v2/containers/SourceCode/SourceCode.tsx index bc64851358..538c2ba2e2 100644 --- a/editor-v2/containers/SourceCode/SourceCode.tsx +++ b/src/editor-v2/containers/SourceCode/SourceCode.tsx @@ -3,7 +3,7 @@ import {Button, ClipboardButton, Icon} from '@gravity-ui/uikit'; import yaml from 'js-yaml'; import * as React from 'react'; -import {PageContentWithNavigation} from '../../../src/models'; +import {PageContentWithNavigation} from '../../../models'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; diff --git a/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.scss b/src/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.scss similarity index 100% rename from editor-v2/containers/SourceCode/UpdateModal/UpdateModal.scss rename to src/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.scss diff --git a/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.tsx b/src/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.tsx similarity index 86% rename from editor-v2/containers/SourceCode/UpdateModal/UpdateModal.tsx rename to src/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.tsx index 4a3224adb9..75a007e96c 100644 --- a/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.tsx +++ b/src/editor-v2/containers/SourceCode/UpdateModal/UpdateModal.tsx @@ -7,7 +7,13 @@ import './UpdateModal.scss'; const b = editorCn('source-code-update-modal'); -export const UpdateModal = ({onClose, onApply, isOpen}) => { +interface UpdateModalProps { + onClose(): void; + onApply(tempConfig?: string): void; + isOpen: boolean; +} + +export const UpdateModal = ({onClose, onApply, isOpen}: UpdateModalProps) => { const [tempConfig, setTempConfig] = React.useState(''); const handleApply = () => { onApply(tempConfig); @@ -26,7 +32,6 @@ export const UpdateModal = ({onClose, onApply, isOpen}) => { </Dialog.Body> <Dialog.Footer showError={false} - listenKeyEnter={true} preset={'default'} textButtonApply={'Apply'} textButtonCancel={'Cancel'} diff --git a/editor-v2/containers/Tree/DragContext.scss b/src/editor-v2/containers/Tree/DragContext.scss similarity index 100% rename from editor-v2/containers/Tree/DragContext.scss rename to src/editor-v2/containers/Tree/DragContext.scss diff --git a/editor-v2/containers/Tree/DragContext.tsx b/src/editor-v2/containers/Tree/DragContext.tsx similarity index 98% rename from editor-v2/containers/Tree/DragContext.tsx rename to src/editor-v2/containers/Tree/DragContext.tsx index 268793903e..703126f312 100644 --- a/editor-v2/containers/Tree/DragContext.tsx +++ b/src/editor-v2/containers/Tree/DragContext.tsx @@ -31,6 +31,8 @@ const DragPreview = React.forwardRef<HTMLDivElement>((_, ref) => { return <div ref={ref} className={b()} style={{display: 'none'}} />; }); +DragPreview.displayName = 'DragPreview'; + // Provider component for drag context export const DragContextProvider: React.FC<React.PropsWithChildren<{}>> = ({children}) => { const [draggedItem, setDraggedItem] = React.useState<DragItem | null>(null); diff --git a/editor-v2/containers/Tree/Tree.scss b/src/editor-v2/containers/Tree/Tree.scss similarity index 100% rename from editor-v2/containers/Tree/Tree.scss rename to src/editor-v2/containers/Tree/Tree.scss diff --git a/editor-v2/containers/Tree/Tree.tsx b/src/editor-v2/containers/Tree/Tree.tsx similarity index 100% rename from editor-v2/containers/Tree/Tree.tsx rename to src/editor-v2/containers/Tree/Tree.tsx diff --git a/editor-v2/containers/Tree/TreeContent.scss b/src/editor-v2/containers/Tree/TreeContent.scss similarity index 100% rename from editor-v2/containers/Tree/TreeContent.scss rename to src/editor-v2/containers/Tree/TreeContent.scss diff --git a/editor-v2/containers/Tree/TreeContent.tsx b/src/editor-v2/containers/Tree/TreeContent.tsx similarity index 100% rename from editor-v2/containers/Tree/TreeContent.tsx rename to src/editor-v2/containers/Tree/TreeContent.tsx diff --git a/editor-v2/containers/Tree/TreeItem.scss b/src/editor-v2/containers/Tree/TreeItem.scss similarity index 100% rename from editor-v2/containers/Tree/TreeItem.scss rename to src/editor-v2/containers/Tree/TreeItem.scss diff --git a/editor-v2/containers/Tree/TreeItem.tsx b/src/editor-v2/containers/Tree/TreeItem.tsx similarity index 100% rename from editor-v2/containers/Tree/TreeItem.tsx rename to src/editor-v2/containers/Tree/TreeItem.tsx diff --git a/editor-v2/containers/Tree/index.ts b/src/editor-v2/containers/Tree/index.ts similarity index 100% rename from editor-v2/containers/Tree/index.ts rename to src/editor-v2/containers/Tree/index.ts diff --git a/editor-v2/containers/ViewSwitches/ViewSwitches.scss b/src/editor-v2/containers/ViewSwitches/ViewSwitches.scss similarity index 100% rename from editor-v2/containers/ViewSwitches/ViewSwitches.scss rename to src/editor-v2/containers/ViewSwitches/ViewSwitches.scss diff --git a/editor-v2/containers/ViewSwitches/ViewSwitches.tsx b/src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx similarity index 100% rename from editor-v2/containers/ViewSwitches/ViewSwitches.tsx rename to src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx diff --git a/editor-v2/containers/__stories__/Editor.stories.tsx b/src/editor-v2/containers/__stories__/Editor.stories.tsx similarity index 100% rename from editor-v2/containers/__stories__/Editor.stories.tsx rename to src/editor-v2/containers/__stories__/Editor.stories.tsx diff --git a/editor-v2/containers/__stories__/utils.ts b/src/editor-v2/containers/__stories__/utils.ts similarity index 100% rename from editor-v2/containers/__stories__/utils.ts rename to src/editor-v2/containers/__stories__/utils.ts diff --git a/editor-v2/context/editorStore/MainEditorStoreContext.tsx b/src/editor-v2/context/editorStore/MainEditorStoreContext.tsx similarity index 100% rename from editor-v2/context/editorStore/MainEditorStoreContext.tsx rename to src/editor-v2/context/editorStore/MainEditorStoreContext.tsx diff --git a/editor-v2/context/editorStore/MainEditorStoreProvider.tsx b/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx similarity index 89% rename from editor-v2/context/editorStore/MainEditorStoreProvider.tsx rename to src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx index e290b9880f..b20b7d6855 100644 --- a/editor-v2/context/editorStore/MainEditorStoreProvider.tsx +++ b/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; import {StoreApi} from 'zustand'; -import {EditorState} from '../../../common/store'; -import {StoreSyncMessage} from '../../../common/types'; -import {removeFn} from '../../../common/utils'; +import {EditorState} from '../../../../common/store'; +import {StoreSyncMessage} from '../../../../common/types'; +import {removeFn} from '../../../../common/utils'; import {EditorStore, createEditorStore} from '../../store'; import {IframeContext} from '../iframeContext'; diff --git a/editor-v2/context/editorStore/index.ts b/src/editor-v2/context/editorStore/index.ts similarity index 100% rename from editor-v2/context/editorStore/index.ts rename to src/editor-v2/context/editorStore/index.ts diff --git a/editor-v2/context/iframeContext/IframeContext.tsx b/src/editor-v2/context/iframeContext/IframeContext.tsx similarity index 100% rename from editor-v2/context/iframeContext/IframeContext.tsx rename to src/editor-v2/context/iframeContext/IframeContext.tsx diff --git a/editor-v2/context/iframeContext/IframeProvider.tsx b/src/editor-v2/context/iframeContext/IframeProvider.tsx similarity index 100% rename from editor-v2/context/iframeContext/IframeProvider.tsx rename to src/editor-v2/context/iframeContext/IframeProvider.tsx diff --git a/editor-v2/context/iframeContext/index.ts b/src/editor-v2/context/iframeContext/index.ts similarity index 100% rename from editor-v2/context/iframeContext/index.ts rename to src/editor-v2/context/iframeContext/index.ts diff --git a/editor-v2/hooks/useEditorTabs.tsx b/src/editor-v2/hooks/useEditorTabs.tsx similarity index 100% rename from editor-v2/hooks/useEditorTabs.tsx rename to src/editor-v2/hooks/useEditorTabs.tsx diff --git a/editor-v2/hooks/useMainEditorInitialize.ts b/src/editor-v2/hooks/useMainEditorInitialize.ts similarity index 96% rename from editor-v2/hooks/useMainEditorInitialize.ts rename to src/editor-v2/hooks/useMainEditorInitialize.ts index 2326b95ec9..0670fab2d4 100644 --- a/editor-v2/hooks/useMainEditorInitialize.ts +++ b/src/editor-v2/hooks/useMainEditorInitialize.ts @@ -1,4 +1,4 @@ -import {usePostMessageAPIListener} from '../../common/postMessage'; +import {usePostMessageAPIListener} from '../../../common/postMessage'; import {useMainEditorStore} from './useMainEditorStore'; import {usePostMessageEvents} from './usePostMessageEvents'; diff --git a/editor-v2/hooks/useMainEditorStore.ts b/src/editor-v2/hooks/useMainEditorStore.ts similarity index 100% rename from editor-v2/hooks/useMainEditorStore.ts rename to src/editor-v2/hooks/useMainEditorStore.ts diff --git a/editor-v2/hooks/usePostMessageEvents.ts b/src/editor-v2/hooks/usePostMessageEvents.ts similarity index 84% rename from editor-v2/hooks/usePostMessageEvents.ts rename to src/editor-v2/hooks/usePostMessageEvents.ts index cc80008433..2eaf52d112 100644 --- a/editor-v2/hooks/usePostMessageEvents.ts +++ b/src/editor-v2/hooks/usePostMessageEvents.ts @@ -1,7 +1,7 @@ import * as React from 'react'; -import {requestActionPostMessage} from '../../common/postMessage'; -import {ActionMessageTypes} from '../../common/types'; +import {requestActionPostMessage} from '../../../common/postMessage'; +import {ActionMessageTypes} from '../../../common/types'; import {IframeContext} from '../context/iframeContext'; interface UsePostMessageRequestReturn { diff --git a/editor-v2/index.ts b/src/editor-v2/index.ts similarity index 58% rename from editor-v2/index.ts rename to src/editor-v2/index.ts index 4697a13c9f..a42d4a4300 100644 --- a/editor-v2/index.ts +++ b/src/editor-v2/index.ts @@ -1,2 +1,2 @@ export {Editor} from './containers/Editor/Editor'; -export * from '../common/types'; +export * from '../../common/types'; diff --git a/editor-v2/store.ts b/src/editor-v2/store.ts similarity index 97% rename from editor-v2/store.ts rename to src/editor-v2/store.ts index 17c50cb65c..9fea7fcba2 100644 --- a/editor-v2/store.ts +++ b/src/editor-v2/store.ts @@ -1,9 +1,9 @@ import _ from 'lodash'; -import {EditorState, initialStore} from '../common/store'; -import {DynamicFormValue} from '../common/types'; -import {initializeStore} from '../common/utils'; -import {ConstructorBlock, PageContentWithNavigation} from '../src/models'; +import {EditorState, initialStore} from '../../common/store'; +import {DynamicFormValue} from '../../common/types'; +import {initializeStore} from '../../common/utils'; +import {ConstructorBlock, PageContentWithNavigation} from '../models'; import {ZOOM_STEPS} from './constants'; import { diff --git a/editor-v2/styles/mixins.scss b/src/editor-v2/styles/mixins.scss similarity index 100% rename from editor-v2/styles/mixins.scss rename to src/editor-v2/styles/mixins.scss diff --git a/editor-v2/styles/root.scss b/src/editor-v2/styles/root.scss similarity index 100% rename from editor-v2/styles/root.scss rename to src/editor-v2/styles/root.scss diff --git a/editor-v2/styles/variables.scss b/src/editor-v2/styles/variables.scss similarity index 100% rename from editor-v2/styles/variables.scss rename to src/editor-v2/styles/variables.scss diff --git a/editor-v2/utils/cn.ts b/src/editor-v2/utils/cn.ts similarity index 100% rename from editor-v2/utils/cn.ts rename to src/editor-v2/utils/cn.ts diff --git a/editor-v2/utils/code.ts b/src/editor-v2/utils/code.ts similarity index 83% rename from editor-v2/utils/code.ts rename to src/editor-v2/utils/code.ts index 5fe3297231..3b46632677 100644 --- a/editor-v2/utils/code.ts +++ b/src/editor-v2/utils/code.ts @@ -1,6 +1,6 @@ import yaml from 'js-yaml'; -import {PageContent} from '../../src/models'; +import {PageContent} from '../../models'; export function parseCode(code: string) { const pageContent = yaml.load(code) as PageContent; diff --git a/editor-v2/utils/index.ts b/src/editor-v2/utils/index.ts similarity index 99% rename from editor-v2/utils/index.ts rename to src/editor-v2/utils/index.ts index 9b416b484f..8de140f3ba 100644 --- a/editor-v2/utils/index.ts +++ b/src/editor-v2/utils/index.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {ConstructorBlock} from '../../src/models'; +import {ConstructorBlock} from '../../models'; export function insert<T>(arr: Array<T>, index: number, newItem: T) { return [...arr.slice(0, index), newItem, ...arr.slice(index)]; From 5df335da24d85c84455468bd5cfd9241fcd78162 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 7 Apr 2025 13:06:37 +0300 Subject: [PATCH 041/118] fix: eslint --- src/editor-v2/containers/Tree/TreeItem.tsx | 2 +- src/editor-v2/hooks/useEditorTabs.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/editor-v2/containers/Tree/TreeItem.tsx b/src/editor-v2/containers/Tree/TreeItem.tsx index 80da0af574..0aafed007c 100644 --- a/src/editor-v2/containers/Tree/TreeItem.tsx +++ b/src/editor-v2/containers/Tree/TreeItem.tsx @@ -107,7 +107,7 @@ export const Item = ({ // Reset drag context setDraggedItem(null); hidePreview(); - }, [setDraggedItem, hidePreview, path]); + }, [setDraggedItem, hidePreview]); const handleDragOver = React.useCallback( (e: React.DragEvent<HTMLDivElement>) => { diff --git a/src/editor-v2/hooks/useEditorTabs.tsx b/src/editor-v2/hooks/useEditorTabs.tsx index 1f0e544fc5..5db43263b5 100644 --- a/src/editor-v2/hooks/useEditorTabs.tsx +++ b/src/editor-v2/hooks/useEditorTabs.tsx @@ -8,8 +8,8 @@ import SourceCode from '../containers/SourceCode/SourceCode'; import Tree from '../containers/Tree'; export const useEditorTabs = ({ - leftTabs = [], - rightTabs = [], + leftTabs, + rightTabs, }: { leftTabs?: TabsItemProps[]; rightTabs?: TabsItemProps[]; @@ -52,7 +52,7 @@ export const useEditorTabs = ({ title: 'NAVIGATION', component: GlobalConfig, }, - ...leftTabs, + ...(leftTabs || []), ], right: [ { @@ -80,10 +80,10 @@ export const useEditorTabs = ({ /> ), }, - ...rightTabs, + ...(rightTabs || []), ], }), - [], + [leftTabs, rightTabs], ); return tabs; From 3ec3406395ead99692820be46e166672777c4100 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 7 Apr 2025 15:46:13 +0300 Subject: [PATCH 042/118] fix: editor for radiobutton --- src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss b/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss index 35de5b6db5..76b3b96a24 100644 --- a/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss +++ b/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.scss @@ -5,7 +5,7 @@ $block: '.#{$ns}oneof-dynamic-field'; #{$block} { &__radio { - margin-bottom: 12px; + margin: 16px 12px; } &__select { From 3c6521faa57a438e2994edb2d88bab476aea4270 Mon Sep 17 00:00:00 2001 From: DaffPunks <sawavas2@gmail.com> Date: Tue, 8 Apr 2025 12:06:21 +0300 Subject: [PATCH 043/118] feat: update styles for Overlay --- src/editor-v2/containers/Overlay/Overlay.scss | 48 ++++++++++++----- src/editor-v2/containers/Overlay/Overlay.tsx | 52 ++++++++++--------- src/editor-v2/styles/root.scss | 4 +- 3 files changed, 64 insertions(+), 40 deletions(-) diff --git a/src/editor-v2/containers/Overlay/Overlay.scss b/src/editor-v2/containers/Overlay/Overlay.scss index 975ae27caa..d478f7526f 100644 --- a/src/editor-v2/containers/Overlay/Overlay.scss +++ b/src/editor-v2/containers/Overlay/Overlay.scss @@ -5,6 +5,11 @@ $block: '.#{$ns}overlay'; #{$block} { + $border-width: 3px; + $border-select: 5px var(--g-color-line-brand) solid; + $border: $border-width var(--g-color-line-brand) solid; + $border-radius: 24px; + position: absolute; left: 0; top: 0; @@ -12,18 +17,17 @@ $block: '.#{$ns}overlay'; height: 100%; pointer-events: none; - $border: 5px var(--g-color-line-brand) solid; - $border-2: 3px var(--g-color-base-selection) solid; - &__border { position: absolute; - border: 3px var(--g-color-line-brand) solid; - border-radius: 24px; + border: $border; + border-radius: $border-radius; box-sizing: border-box; box-shadow: 4px 4px 8px 0 var(--g-color-sfx-shadow); + z-index: 100; &_hover { opacity: 0.5; + z-index: 99; } } @@ -31,22 +35,28 @@ $block: '.#{$ns}overlay'; position: absolute; //border: $border-2; box-sizing: border-box; + border-radius: $border-radius; + z-index: 10; &_position { &_top { - border-top: $border; + border-top: $border-select; + border-left: $border-select; } &_bottom { - border-bottom: $border; + border-right: $border-select; + border-bottom: $border-select; } &_left { - border-left: $border; + border-top: $border-select; + border-left: $border-select; } &_right { - border-right: $border; + border-right: $border-select; + border-bottom: $border-select; } } } @@ -54,18 +64,28 @@ $block: '.#{$ns}overlay'; &__actions { pointer-events: auto; position: absolute; - bottom: 0; + bottom: -$border-width; left: 50%; transform: translate(-50%, 100%); + z-index: 1000; display: flex; - align-items: center; + align-items: flex-start; background: transparent; } - &__action-button { - &_grip { - cursor: grab; + &__actions-box { + &_main { + display: flex; + gap: 2px; + align-items: center; + padding: 1px 8px 4px; + background-color: var(--g-color-base-brand); + border-radius: 0px 0px 8px 8px; + } + + &_reorder { + padding: 3px 4px; } } } diff --git a/src/editor-v2/containers/Overlay/Overlay.tsx b/src/editor-v2/containers/Overlay/Overlay.tsx index ac77598816..481e47458c 100644 --- a/src/editor-v2/containers/Overlay/Overlay.tsx +++ b/src/editor-v2/containers/Overlay/Overlay.tsx @@ -111,30 +111,34 @@ const Overlay = ({className, canvasElement}: OverlayProps) => { }} > <div className={b('actions')}> - <Button view="flat" size={'m'} onClick={handleMoveUp}> - <Icon data={ChevronUp} size={18} /> - </Button> - <Button - pin={'round-clear'} - className={b('action-button')} - size={'m'} - view={'action'} - onClick={() => selectedBlock && duplicateBlock(selectedBlock)} - > - <Icon data={Copy} size={18} /> - </Button> - <Button - pin={'clear-round'} - className={b('action-button')} - size={'m'} - view={'action'} - onClick={() => selectedBlock && deleteBlock(selectedBlock)} - > - <Icon data={TrashBin} size={18} /> - </Button> - <Button view="flat" size={'m'} onClick={handleMoveDown}> - <Icon data={ChevronDown} size={18} /> - </Button> + <div className={b('actions-box', {reorder: true})}> + <Button view="flat" size={'m'} onClick={handleMoveUp}> + <Icon data={ChevronUp} size={18} /> + </Button> + </div> + <div className={b('actions-box', {main: true})}> + <Button + className={b('action-button')} + size={'m'} + view={'action'} + onClick={() => selectedBlock && duplicateBlock(selectedBlock)} + > + <Icon data={Copy} size={18} /> + </Button> + <Button + className={b('action-button')} + size={'m'} + view={'action'} + onClick={() => selectedBlock && deleteBlock(selectedBlock)} + > + <Icon data={TrashBin} size={18} /> + </Button> + </div> + <div className={b('actions-box', {reorder: true})}> + <Button view="flat" size={'m'} onClick={handleMoveDown}> + <Icon data={ChevronDown} size={18} /> + </Button> + </div> </div> </div> ) : null} diff --git a/src/editor-v2/styles/root.scss b/src/editor-v2/styles/root.scss index 0e2334f3aa..d74f9a58f4 100644 --- a/src/editor-v2/styles/root.scss +++ b/src/editor-v2/styles/root.scss @@ -3,8 +3,8 @@ --g-color-base-brand-hover: var(--g-color-private-black-600-solid); --g-color-base-selection: var(--g-color-private-black-200); --g-color-base-selection-hover: var(--g-color-private-black-300); - --g-color-base-brand: var(--g-color-text-primary); - --g-color-base-brand-hover: var(--g-color-text-complementary); + --g-color-base-brand: var(--g-color-private-black-850-solid); + --g-color-base-brand-hover: var(--g-color-private-black-700-solid); --g-color-text-brand-contrast: var(--g-color-text-light-primary); --g-color-line-brand: var(--g-color-text-primary); --g-color-text-brand: var(--g-color-private-brand-700-solid); From b65d68f15d375f6f779345664175635729e96d9c Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 7 Apr 2025 18:27:54 +0300 Subject: [PATCH 044/118] fix: build --- package-lock.json | 244 +----------------- package.json | 1 - playground/src/app/page.tsx | 2 +- src/blocks/CardLayout/index.ts | 2 +- src/blocks/Header/dynamic-form.ts | 2 +- src/blocks/Slider/dynamic-form.ts | 2 +- src/blocks/TestEditorBlock/form.ts | 2 +- {common => src/common}/postMessage.ts | 0 {common => src/common}/store.ts | 2 +- {common => src/common}/types/actions.ts | 2 +- {common => src/common}/types/common.ts | 0 {common => src/common}/types/forms.ts | 2 +- {common => src/common}/types/index.ts | 0 {common => src/common}/types/messages.ts | 0 {common => src/common}/utils.ts | 0 src/components/Image/dynamic-form.ts | 2 +- src/constructor-items.ts | 2 +- .../PCEditorStoreContext.tsx | 2 +- .../PCEditorStoreProvider.tsx | 4 +- .../components/DynamicForm/DynamicForm.tsx | 2 +- .../DynamicForm/Fields/Array/Array.tsx | 2 +- .../DynamicForm/Fields/Object/Object.tsx | 2 +- .../DynamicForm/Fields/OneOf/OneOf.tsx | 2 +- .../DynamicForm/Fields/Select/Select.tsx | 2 +- src/editor-v2/components/DynamicForm/utils.ts | 2 +- .../components/StoreViewer/StoreViewer.scss | 39 --- .../components/StoreViewer/StoreViewer.tsx | 39 --- .../containers/BigOverlay/BigOverlay.tsx | 2 +- .../containers/BlockConfig/BlockConfig.tsx | 2 +- .../containers/BlocksList/BlocksList.tsx | 2 +- src/editor-v2/containers/Editor/Editor.tsx | 2 - .../containers/GlobalConfig/GlobalConfig.tsx | 2 +- .../containers/MiddleScreen/MiddleScreen.tsx | 2 +- src/editor-v2/containers/Overlay/Overlay.tsx | 2 +- .../editorStore/MainEditorStoreProvider.tsx | 6 +- .../hooks/useMainEditorInitialize.ts | 2 +- src/editor-v2/hooks/usePostMessageEvents.ts | 4 +- src/editor-v2/index.ts | 2 +- src/editor-v2/store.ts | 6 +- src/hooks/usePCEditorInitializeEvents.ts | 2 +- src/hooks/usePostMessageAPI.ts | 4 +- src/sub-blocks/BackgroundCard/dynamic-form.ts | 2 +- src/utils/form-generator.ts | 2 +- 43 files changed, 42 insertions(+), 363 deletions(-) rename {common => src/common}/postMessage.ts (100%) rename {common => src/common}/store.ts (94%) rename {common => src/common}/types/actions.ts (92%) rename {common => src/common}/types/common.ts (100%) rename {common => src/common}/types/forms.ts (97%) rename {common => src/common}/types/index.ts (100%) rename {common => src/common}/types/messages.ts (100%) rename {common => src/common}/utils.ts (100%) delete mode 100644 src/editor-v2/components/StoreViewer/StoreViewer.scss delete mode 100644 src/editor-v2/components/StoreViewer/StoreViewer.tsx diff --git a/package-lock.json b/package-lock.json index c5fda3a85e..56b15a1a85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,6 @@ "lodash": "^4.17.21", "monaco-editor": "^0.52.2", "react-final-form": "^6.5.9", - "react-json-view": "^1.21.3", "react-monaco-editor": "^0.53.0", "react-player": "^2.9.0", "react-resizable-panels": "^2.1.3", @@ -8362,12 +8361,6 @@ "node": ">=0.10.0" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "license": "MIT" - }, "node_modules/assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -9083,12 +9076,6 @@ "license": "Apache-2.0", "optional": true }, - "node_modules/base16": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", - "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==", - "license": "MIT" - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -10233,15 +10220,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cross-fetch": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", - "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -13036,36 +13014,6 @@ "bser": "2.1.1" } }, - "node_modules/fbemitter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", - "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", - "license": "BSD-3-Clause", - "dependencies": { - "fbjs": "^3.0.0" - } - }, - "node_modules/fbjs": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", - "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", - "license": "MIT", - "dependencies": { - "cross-fetch": "^3.1.5", - "fbjs-css-vars": "^1.0.0", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^1.0.35" - } - }, - "node_modules/fbjs-css-vars": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", - "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", - "license": "MIT" - }, "node_modules/fdir": { "version": "6.4.4", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", @@ -13292,19 +13240,6 @@ "readable-stream": "^2.3.6" } }, - "node_modules/flux": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.4.tgz", - "integrity": "sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==", - "license": "BSD-3-Clause", - "dependencies": { - "fbemitter": "^3.0.0", - "fbjs": "^3.0.1" - }, - "peerDependencies": { - "react": "^15.0.2 || ^16.0.0 || ^17.0.0" - } - }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -19055,24 +18990,12 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.curry": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", - "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==", - "license": "MIT" - }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "license": "MIT" }, - "node_modules/lodash.flow": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", - "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==", - "license": "MIT" - }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -20887,48 +20810,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -23296,15 +23177,6 @@ "dev": true, "license": "MIT" }, - "node_modules/promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "license": "MIT", - "dependencies": { - "asap": "~2.0.3" - } - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -23382,12 +23254,6 @@ "node": ">=6" } }, - "node_modules/pure-color": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", - "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==", - "license": "MIT" - }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -23543,18 +23409,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-base16-styling": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", - "integrity": "sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==", - "license": "MIT", - "dependencies": { - "base16": "^1.0.0", - "lodash.curry": "^4.0.1", - "lodash.flow": "^3.3.0", - "pure-color": "^1.2.0" - } - }, "node_modules/react-beautiful-dnd": { "version": "13.1.1", "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz", @@ -23662,26 +23516,11 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "license": "MIT" }, - "node_modules/react-json-view": { - "version": "1.21.3", - "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", - "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", - "license": "MIT", - "dependencies": { - "flux": "^4.0.1", - "react-base16-styling": "^0.6.0", - "react-lifecycles-compat": "^3.0.4", - "react-textarea-autosize": "^8.3.2" - }, - "peerDependencies": { - "react": "^17.0.0 || ^16.3.0 || ^15.5.4", - "react-dom": "^17.0.0 || ^16.3.0 || ^15.5.4" - } - }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "dev": true, "license": "MIT" }, "node_modules/react-monaco-editor": { @@ -23805,23 +23644,6 @@ "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/react-textarea-autosize": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", - "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.20.13", - "use-composed-ref": "^1.3.0", - "use-latest": "^1.2.1" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -25435,12 +25257,6 @@ "node": ">= 0.4" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -27781,32 +27597,6 @@ "node": ">= 4" } }, - "node_modules/ua-parser-js": { - "version": "1.0.41", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz", - "integrity": "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "license": "MIT", - "bin": { - "ua-parser-js": "script/cli.js" - }, - "engines": { - "node": "*" - } - }, "node_modules/uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", @@ -28191,24 +27981,11 @@ "dev": true, "license": "MIT" }, - "node_modules/use-composed-ref": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz", - "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/use-isomorphic-layout-effect": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", + "dev": true, "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -28219,23 +27996,6 @@ } } }, - "node_modules/use-latest": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz", - "integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==", - "license": "MIT", - "dependencies": { - "use-isomorphic-layout-effect": "^1.1.1" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/use-memo-one": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", diff --git a/package.json b/package.json index 94ad3bce08..6eef5a332d 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,6 @@ "lodash": "^4.17.21", "monaco-editor": "^0.52.2", "react-final-form": "^6.5.9", - "react-json-view": "^1.21.3", "react-monaco-editor": "^0.53.0", "react-player": "^2.9.0", "react-resizable-panels": "^2.1.3", diff --git a/playground/src/app/page.tsx b/playground/src/app/page.tsx index 2051474097..854c352b7d 100644 --- a/playground/src/app/page.tsx +++ b/playground/src/app/page.tsx @@ -14,7 +14,7 @@ const b = block('home'); const Test = () => <div className={b('test')}>custom test</div>; -export const COMPONENTS_CONFIG = { +const COMPONENTS_CONFIG = { middleTop: Test, leftTop: [C9RComponent], leftTabs: [ diff --git a/src/blocks/CardLayout/index.ts b/src/blocks/CardLayout/index.ts index 70f9abfcab..5a4ce280dd 100644 --- a/src/blocks/CardLayout/index.ts +++ b/src/blocks/CardLayout/index.ts @@ -1,6 +1,6 @@ import {JSONSchemaType} from 'ajv'; -import {ConfigInput} from '../../../common/types'; +import {ConfigInput} from '../../common/types'; import {BlockData} from '../../constructor-items'; import {sliderSizesArray, textSize} from '../../schema/validators/common'; import {generateFromAJV} from '../../utils/form-generator'; diff --git a/src/blocks/Header/dynamic-form.ts b/src/blocks/Header/dynamic-form.ts index 33c4b9b932..d3f174aba0 100644 --- a/src/blocks/Header/dynamic-form.ts +++ b/src/blocks/Header/dynamic-form.ts @@ -1,4 +1,4 @@ -import {BlockConfig, ConfigInput} from '../../../common/types'; +import {BlockConfig, ConfigInput} from '../../common/types'; import {imageInputs} from '../../components/Image/dynamic-form'; import {Theme} from '../../models'; diff --git a/src/blocks/Slider/dynamic-form.ts b/src/blocks/Slider/dynamic-form.ts index 5bf5520fc3..ea7468997c 100644 --- a/src/blocks/Slider/dynamic-form.ts +++ b/src/blocks/Slider/dynamic-form.ts @@ -1,4 +1,4 @@ -import {BlockConfig, ConfigInput} from '../../../common/types'; +import {BlockConfig, ConfigInput} from '../../common/types'; import {sliderSizesArray, textSize} from '../../schema/validators/common'; const textSizeEnum = textSize.map((size) => ({value: size, content: size})); diff --git a/src/blocks/TestEditorBlock/form.ts b/src/blocks/TestEditorBlock/form.ts index 849cb6d307..838eacb06e 100644 --- a/src/blocks/TestEditorBlock/form.ts +++ b/src/blocks/TestEditorBlock/form.ts @@ -9,7 +9,7 @@ import { SelectSingleInput, TextAreaInput, TextInput, -} from '../../../common/types'; +} from '../../common/types'; const textInput: TextInput = { type: 'text', diff --git a/common/postMessage.ts b/src/common/postMessage.ts similarity index 100% rename from common/postMessage.ts rename to src/common/postMessage.ts diff --git a/common/store.ts b/src/common/store.ts similarity index 94% rename from common/store.ts rename to src/common/store.ts index 668684374d..1b8e37a709 100644 --- a/common/store.ts +++ b/src/common/store.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {PageContentWithNavigation} from '../src/models'; +import {PageContentWithNavigation} from '../models'; import {ConfigInput, ItemConfig} from './types'; import {initializeStore} from './utils'; diff --git a/common/types/actions.ts b/src/common/types/actions.ts similarity index 92% rename from common/types/actions.ts rename to src/common/types/actions.ts index ecb2bcd9f4..1fbf0fdc55 100644 --- a/common/types/actions.ts +++ b/src/common/types/actions.ts @@ -1,4 +1,4 @@ -import {PageContentWithNavigation} from '../../src/models'; +import {PageContentWithNavigation} from '../../models'; import {EditorState} from '../store'; export type MessageTypes = EventMessageTypes & ActionMessageTypes; diff --git a/common/types/common.ts b/src/common/types/common.ts similarity index 100% rename from common/types/common.ts rename to src/common/types/common.ts diff --git a/common/types/forms.ts b/src/common/types/forms.ts similarity index 97% rename from common/types/forms.ts rename to src/common/types/forms.ts index 190d349d47..f5a53c37dd 100644 --- a/common/types/forms.ts +++ b/src/common/types/forms.ts @@ -1,4 +1,4 @@ -import {PageContentWithNavigation} from '../../src/models'; +import {PageContentWithNavigation} from '../../models'; export type DynamicFormValue = | string diff --git a/common/types/index.ts b/src/common/types/index.ts similarity index 100% rename from common/types/index.ts rename to src/common/types/index.ts diff --git a/common/types/messages.ts b/src/common/types/messages.ts similarity index 100% rename from common/types/messages.ts rename to src/common/types/messages.ts diff --git a/common/utils.ts b/src/common/utils.ts similarity index 100% rename from common/utils.ts rename to src/common/utils.ts diff --git a/src/components/Image/dynamic-form.ts b/src/components/Image/dynamic-form.ts index 23b3f44cfa..f5f8de747f 100644 --- a/src/components/Image/dynamic-form.ts +++ b/src/components/Image/dynamic-form.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {ConfigInput} from '../../../common/types'; +import {ConfigInput} from '../../common/types'; const devices = ['desktop', 'tablet', 'mobile']; diff --git a/src/constructor-items.ts b/src/constructor-items.ts index d4930a5b78..68946845e6 100644 --- a/src/constructor-items.ts +++ b/src/constructor-items.ts @@ -42,7 +42,7 @@ import ShareBlockConfig from './blocks/Share'; import SliderBlockConfig from './blocks/Slider'; import TableBlockConfig from './blocks/Table'; import TabsBlockConfig from './blocks/Tabs'; -import {BlockConfig} from '../common/types'; +import {BlockConfig} from './common/types'; import {BlockType, NavigationItemType, SubBlockType} from './models'; import {GithubButton, NavigationButton, NavigationDropdown, NavigationLink} from './navigation'; import SocialIcon from './navigation/components/SocialIcon/SocialIcon'; diff --git a/src/context/editorStoreContext/PCEditorStoreContext.tsx b/src/context/editorStoreContext/PCEditorStoreContext.tsx index 64e0b60516..1b9e53899c 100644 --- a/src/context/editorStoreContext/PCEditorStoreContext.tsx +++ b/src/context/editorStoreContext/PCEditorStoreContext.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import {StoreApi} from 'zustand'; -import {EditorState, createPCEditorStore} from '../../../common/store'; +import {EditorState, createPCEditorStore} from '../../common/store'; export interface PCEditorStoreContextProps { state: StoreApi<EditorState>; diff --git a/src/context/editorStoreContext/PCEditorStoreProvider.tsx b/src/context/editorStoreContext/PCEditorStoreProvider.tsx index 9de324e5a7..b3d851565a 100644 --- a/src/context/editorStoreContext/PCEditorStoreProvider.tsx +++ b/src/context/editorStoreContext/PCEditorStoreProvider.tsx @@ -2,8 +2,8 @@ import * as React from 'react'; import {StoreApi} from 'zustand'; -import {EditorState, createPCEditorStore} from '../../../common/store'; -import {StoreSyncMessage} from '../../../common/types'; +import {EditorState, createPCEditorStore} from '../../common/store'; +import {StoreSyncMessage} from '../../common/types'; import {PCEditorStoreContext} from './PCEditorStoreContext'; diff --git a/src/editor-v2/components/DynamicForm/DynamicForm.tsx b/src/editor-v2/components/DynamicForm/DynamicForm.tsx index 74a80edc17..9531c21815 100644 --- a/src/editor-v2/components/DynamicForm/DynamicForm.tsx +++ b/src/editor-v2/components/DynamicForm/DynamicForm.tsx @@ -1,7 +1,7 @@ import _ from 'lodash'; import * as React from 'react'; -import {ConfigInput, DynamicFormValue} from '../../../../common/types'; +import {ConfigInput, DynamicFormValue} from '../../../common/types'; import {editorCn} from '../../utils/cn'; import './DynamicForm.scss'; diff --git a/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx b/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx index 31381279fe..295225c737 100644 --- a/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx +++ b/src/editor-v2/components/DynamicForm/Fields/Array/Array.tsx @@ -2,7 +2,7 @@ import {Plus} from '@gravity-ui/icons'; import {Button, Card, Icon} from '@gravity-ui/uikit'; import * as React from 'react'; -import {ArrayObjectInput, ArrayTextInput, DynamicFormValue} from '../../../../../../common/types'; +import {ArrayObjectInput, ArrayTextInput, DynamicFormValue} from '../../../../../common/types'; import {removeFromArray, swapArrayItems} from '../../../../utils'; import {editorCn} from '../../../../utils/cn'; import DynamicForm from '../../DynamicForm'; diff --git a/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx b/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx index e6839cba84..3711df5be3 100644 --- a/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx +++ b/src/editor-v2/components/DynamicForm/Fields/Object/Object.tsx @@ -1,6 +1,6 @@ import {Card} from '@gravity-ui/uikit'; -import {ConfigInput, DynamicFormValue} from '../../../../../../common/types'; +import {ConfigInput, DynamicFormValue} from '../../../../../common/types'; import DynamicForm from '../../DynamicForm'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; diff --git a/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx b/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx index 77559d1e7e..31ed85ff3f 100644 --- a/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx +++ b/src/editor-v2/components/DynamicForm/Fields/OneOf/OneOf.tsx @@ -1,7 +1,7 @@ import {Card, SegmentedRadioGroup, Select} from '@gravity-ui/uikit'; import * as React from 'react'; -import {DynamicFormValue, OneOfInput} from '../../../../../../common/types'; +import {DynamicFormValue, OneOfInput} from '../../../../../common/types'; import {editorCn} from '../../../../utils/cn'; import DynamicForm from '../../DynamicForm'; import FieldBase from '../../FieldBase/FieldBase'; diff --git a/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx b/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx index 6d3eaabfef..13bcbe0dcc 100644 --- a/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx +++ b/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx @@ -1,6 +1,6 @@ import {SegmentedRadioGroup, Select} from '@gravity-ui/uikit'; -import {SelectMultipleInput, SelectSingleInput} from '../../../../../../common/types'; +import {SelectMultipleInput, SelectSingleInput} from '../../../../../common/types'; import {editorCn} from '../../../../utils/cn'; import FieldBase, {FieldBaseParams} from '../../FieldBase/FieldBase'; diff --git a/src/editor-v2/components/DynamicForm/utils.ts b/src/editor-v2/components/DynamicForm/utils.ts index 2449db5329..5ad3f80b7c 100644 --- a/src/editor-v2/components/DynamicForm/utils.ts +++ b/src/editor-v2/components/DynamicForm/utils.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {DynamicFormValue} from '../../../../common/types'; +import {DynamicFormValue} from '../../../common/types'; export const getFullPath = (path: string, name: string) => { if (!path && !name) { diff --git a/src/editor-v2/components/StoreViewer/StoreViewer.scss b/src/editor-v2/components/StoreViewer/StoreViewer.scss deleted file mode 100644 index 6fd241894a..0000000000 --- a/src/editor-v2/components/StoreViewer/StoreViewer.scss +++ /dev/null @@ -1,39 +0,0 @@ -@import '../../styles/variables.scss'; -@import '../../styles/mixins.scss'; - -$block: '.#{$ns}store-viewer'; - -#{$block} { - background-color: var(--g-color-base-float-heavy); - border-radius: 12px; - border: 1px solid var(--g-color-line-generic); - overflow: hidden; - opacity: 0.3; - - &:hover { - opacity: 1; - } - - &_open { - opacity: 1; - - #{$block}__box { - display: block; - } - } - - &__head { - padding: 12px; - } - - &__box { - max-height: 80vh; - overflow: scroll; - display: none; - min-width: 400px; - max-width: 80vw; - padding: 12px; - background-color: var(--g-color-base-float); - white-space: pre; - } -} diff --git a/src/editor-v2/components/StoreViewer/StoreViewer.tsx b/src/editor-v2/components/StoreViewer/StoreViewer.tsx deleted file mode 100644 index b931bd0845..0000000000 --- a/src/editor-v2/components/StoreViewer/StoreViewer.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import {Code} from '@gravity-ui/icons'; -import {Button, Icon} from '@gravity-ui/uikit'; -import * as React from 'react'; -import ReactJson from 'react-json-view'; - -import {removeFn} from '../../../../common/utils'; -import {editorCn} from '../../utils/cn'; - -import './StoreViewer.scss'; - -const b = editorCn('store-viewer'); - -interface StoreViewerProps { - className?: string; - store: object; -} - -const StoreViewer = ({store, className}: StoreViewerProps) => { - const [open, setOpen] = React.useState(false); - return ( - <div className={b({open}, className)}> - <div className={b('head')}> - <Button view="action" onClick={() => setOpen(!open)}> - <Icon data={Code} /> - </Button> - </div> - <div className={b('box')}> - <ReactJson - style={{backgroundColor: 'transparent'}} - src={removeFn(store)} - collapsed - theme="bright:inverted" - /> - </div> - </div> - ); -}; - -export default StoreViewer; diff --git a/src/editor-v2/containers/BigOverlay/BigOverlay.tsx b/src/editor-v2/containers/BigOverlay/BigOverlay.tsx index eb21962bd3..34d50fefc7 100644 --- a/src/editor-v2/containers/BigOverlay/BigOverlay.tsx +++ b/src/editor-v2/containers/BigOverlay/BigOverlay.tsx @@ -1,7 +1,7 @@ import {Stop} from '@gravity-ui/icons'; import * as React from 'react'; -import {usePostMessageAPIListener} from '../../../../common/postMessage'; +import {usePostMessageAPIListener} from '../../../common/postMessage'; import {IframeContext} from '../../context/iframeContext'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; diff --git a/src/editor-v2/containers/BlockConfig/BlockConfig.tsx b/src/editor-v2/containers/BlockConfig/BlockConfig.tsx index 39a67238dd..c0b753d07e 100644 --- a/src/editor-v2/containers/BlockConfig/BlockConfig.tsx +++ b/src/editor-v2/containers/BlockConfig/BlockConfig.tsx @@ -1,6 +1,6 @@ import _ from 'lodash'; -import {DynamicFormValue} from '../../../../common/types'; +import {DynamicFormValue} from '../../../common/types'; import DynamicForm from '../../components/DynamicForm/DynamicForm'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {generateChildrenPathFromArray} from '../../utils'; diff --git a/src/editor-v2/containers/BlocksList/BlocksList.tsx b/src/editor-v2/containers/BlocksList/BlocksList.tsx index 2f7dd25e32..b8652f238d 100644 --- a/src/editor-v2/containers/BlocksList/BlocksList.tsx +++ b/src/editor-v2/containers/BlocksList/BlocksList.tsx @@ -2,7 +2,7 @@ import {Magnifier, SquareBars} from '@gravity-ui/icons'; import {Card, Icon, TextInput} from '@gravity-ui/uikit'; import * as React from 'react'; -import {ItemConfig} from '../../../../common/types'; +import {ItemConfig} from '../../../common/types'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; diff --git a/src/editor-v2/containers/Editor/Editor.tsx b/src/editor-v2/containers/Editor/Editor.tsx index fd4dbe3974..e8bef32f82 100644 --- a/src/editor-v2/containers/Editor/Editor.tsx +++ b/src/editor-v2/containers/Editor/Editor.tsx @@ -3,7 +3,6 @@ import * as React from 'react'; import {PageContentWithNavigation} from '../../../models'; import {Panels} from '../../components/Panels/Panels'; import {Sidebar} from '../../components/Sidebar/Sidebar'; -import StoreViewer from '../../components/StoreViewer/StoreViewer'; import BigOverlay from '../../containers/BigOverlay/BigOverlay'; import MiddleScreen from '../../containers/MiddleScreen/MiddleScreen'; import {MainEditorStoreProvider} from '../../context/editorStore'; @@ -71,7 +70,6 @@ const EditorView = ({componentsConfig = {}}: EditorViewProps) => { /> </div> <BigOverlay className={b('overlay')} /> - <StoreViewer className={b('debug-store')} store={store} /> </div> ); }; diff --git a/src/editor-v2/containers/GlobalConfig/GlobalConfig.tsx b/src/editor-v2/containers/GlobalConfig/GlobalConfig.tsx index 9299a3150f..9defca1403 100644 --- a/src/editor-v2/containers/GlobalConfig/GlobalConfig.tsx +++ b/src/editor-v2/containers/GlobalConfig/GlobalConfig.tsx @@ -1,4 +1,4 @@ -import {DynamicFormValue} from '../../../../common/types'; +import {DynamicFormValue} from '../../../common/types'; import DynamicForm from '../../components/DynamicForm/DynamicForm'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; diff --git a/src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx b/src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx index 9ab96a5dae..198c759ef8 100644 --- a/src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx +++ b/src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx @@ -1,7 +1,7 @@ import {Loader} from '@gravity-ui/uikit'; import * as React from 'react'; -import {usePostMessageAPIListener} from '../../../../common/postMessage'; +import {usePostMessageAPIListener} from '../../../common/postMessage'; import {IframeContext} from '../../context/iframeContext'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; diff --git a/src/editor-v2/containers/Overlay/Overlay.tsx b/src/editor-v2/containers/Overlay/Overlay.tsx index 481e47458c..86cbc6bef5 100644 --- a/src/editor-v2/containers/Overlay/Overlay.tsx +++ b/src/editor-v2/containers/Overlay/Overlay.tsx @@ -2,7 +2,7 @@ import {ChevronDown, ChevronUp, Copy, TrashBin} from '@gravity-ui/icons'; import {Button, Icon} from '@gravity-ui/uikit'; import * as React from 'react'; -import {usePostMessageAPIListener} from '../../../../common/postMessage'; +import {usePostMessageAPIListener} from '../../../common/postMessage'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; diff --git a/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx b/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx index b20b7d6855..e290b9880f 100644 --- a/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx +++ b/src/editor-v2/context/editorStore/MainEditorStoreProvider.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; import {StoreApi} from 'zustand'; -import {EditorState} from '../../../../common/store'; -import {StoreSyncMessage} from '../../../../common/types'; -import {removeFn} from '../../../../common/utils'; +import {EditorState} from '../../../common/store'; +import {StoreSyncMessage} from '../../../common/types'; +import {removeFn} from '../../../common/utils'; import {EditorStore, createEditorStore} from '../../store'; import {IframeContext} from '../iframeContext'; diff --git a/src/editor-v2/hooks/useMainEditorInitialize.ts b/src/editor-v2/hooks/useMainEditorInitialize.ts index 0670fab2d4..2326b95ec9 100644 --- a/src/editor-v2/hooks/useMainEditorInitialize.ts +++ b/src/editor-v2/hooks/useMainEditorInitialize.ts @@ -1,4 +1,4 @@ -import {usePostMessageAPIListener} from '../../../common/postMessage'; +import {usePostMessageAPIListener} from '../../common/postMessage'; import {useMainEditorStore} from './useMainEditorStore'; import {usePostMessageEvents} from './usePostMessageEvents'; diff --git a/src/editor-v2/hooks/usePostMessageEvents.ts b/src/editor-v2/hooks/usePostMessageEvents.ts index 2eaf52d112..cc80008433 100644 --- a/src/editor-v2/hooks/usePostMessageEvents.ts +++ b/src/editor-v2/hooks/usePostMessageEvents.ts @@ -1,7 +1,7 @@ import * as React from 'react'; -import {requestActionPostMessage} from '../../../common/postMessage'; -import {ActionMessageTypes} from '../../../common/types'; +import {requestActionPostMessage} from '../../common/postMessage'; +import {ActionMessageTypes} from '../../common/types'; import {IframeContext} from '../context/iframeContext'; interface UsePostMessageRequestReturn { diff --git a/src/editor-v2/index.ts b/src/editor-v2/index.ts index a42d4a4300..4697a13c9f 100644 --- a/src/editor-v2/index.ts +++ b/src/editor-v2/index.ts @@ -1,2 +1,2 @@ export {Editor} from './containers/Editor/Editor'; -export * from '../../common/types'; +export * from '../common/types'; diff --git a/src/editor-v2/store.ts b/src/editor-v2/store.ts index 9fea7fcba2..54edef1dd3 100644 --- a/src/editor-v2/store.ts +++ b/src/editor-v2/store.ts @@ -1,8 +1,8 @@ import _ from 'lodash'; -import {EditorState, initialStore} from '../../common/store'; -import {DynamicFormValue} from '../../common/types'; -import {initializeStore} from '../../common/utils'; +import {EditorState, initialStore} from '../common/store'; +import {DynamicFormValue} from '../common/types'; +import {initializeStore} from '../common/utils'; import {ConstructorBlock, PageContentWithNavigation} from '../models'; import {ZOOM_STEPS} from './constants'; diff --git a/src/hooks/usePCEditorInitializeEvents.ts b/src/hooks/usePCEditorInitializeEvents.ts index f72ea957c5..0b6990d6f6 100644 --- a/src/hooks/usePCEditorInitializeEvents.ts +++ b/src/hooks/usePCEditorInitializeEvents.ts @@ -3,7 +3,7 @@ import * as React from 'react'; import {JSONSchemaType} from 'ajv'; import _ from 'lodash'; -import {ItemConfig} from '../../common/types'; +import {ItemConfig} from '../common/types'; import {blockDataMap} from '../constructor-items'; import {PageContentWithNavigation} from '../models'; import {defaultComponentsConfigurationSchema} from '../schema'; diff --git a/src/hooks/usePostMessageAPI.ts b/src/hooks/usePostMessageAPI.ts index 7d5c33c695..346828b6e4 100644 --- a/src/hooks/usePostMessageAPI.ts +++ b/src/hooks/usePostMessageAPI.ts @@ -1,7 +1,7 @@ import * as React from 'react'; -import {PostMessageAPIMessage} from '../../common/types'; -import {ActionMessageTypes, EventMessageTypes} from '../../common/types/actions'; +import {PostMessageAPIMessage} from '../common/types'; +import {ActionMessageTypes, EventMessageTypes} from '../common/types/actions'; export function sendEventPostMessage<K extends keyof EventMessageTypes>( action: K, diff --git a/src/sub-blocks/BackgroundCard/dynamic-form.ts b/src/sub-blocks/BackgroundCard/dynamic-form.ts index 1ab9100a14..817ff35e28 100644 --- a/src/sub-blocks/BackgroundCard/dynamic-form.ts +++ b/src/sub-blocks/BackgroundCard/dynamic-form.ts @@ -1,4 +1,4 @@ -import {BlockConfig} from '../../../common/types'; +import {BlockConfig} from '../../common/types'; import {contentThemes, textSize} from '../../schema/validators/common'; const textSizeEnum = textSize.map((size) => ({value: size, content: size})); diff --git a/src/utils/form-generator.ts b/src/utils/form-generator.ts index 03d4ee6c7b..9c8af8ca01 100644 --- a/src/utils/form-generator.ts +++ b/src/utils/form-generator.ts @@ -9,7 +9,7 @@ import { SelectBaseInput, TextAreaInput, TextInput, -} from '../../common/types'; +} from '../common/types'; export const generateFromAJV = (schema: JSONSchemaType<{}>): ConfigInput[] => { if (schema && schema.properties) { From ba4307cb745500f86e14c678dc6b24077378656a Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 7 Apr 2025 20:00:59 +0300 Subject: [PATCH 045/118] fix: styles for dark theme --- playground/src/app/components/C9RComponent.scss | 2 +- playground/src/styles/globals.scss | 1 + .../components/DynamicForm/FieldBase/FieldBase.scss | 1 - src/editor-v2/components/Panels/Panels.scss | 1 - src/editor-v2/components/Sidebar/Sidebar.scss | 1 - src/editor-v2/components/Tabs/Tabs.scss | 2 +- src/editor-v2/containers/BigOverlay/BigOverlay.scss | 1 - src/editor-v2/containers/BlockConfig/BlockConfig.scss | 1 - src/editor-v2/containers/BlocksList/BlocksList.scss | 7 +++---- src/editor-v2/containers/Editor/Editor.scss | 1 - src/editor-v2/containers/GlobalConfig/GlobalConfig.scss | 1 - src/editor-v2/containers/MiddleScreen/MiddleScreen.scss | 3 +-- src/editor-v2/containers/Overlay/Overlay.scss | 1 - src/editor-v2/containers/Source/Source.scss | 1 - src/editor-v2/containers/SourceCode/SourceCode.scss | 1 - src/editor-v2/containers/Tree/DragContext.scss | 5 ++--- src/editor-v2/containers/Tree/Tree.scss | 1 - src/editor-v2/containers/Tree/TreeContent.scss | 5 ----- src/editor-v2/containers/Tree/TreeItem.scss | 7 +------ src/editor-v2/containers/ViewSwitches/ViewSwitches.scss | 1 - src/editor-v2/styles/root.scss | 7 +++---- 21 files changed, 13 insertions(+), 38 deletions(-) diff --git a/playground/src/app/components/C9RComponent.scss b/playground/src/app/components/C9RComponent.scss index fb9a04fa48..6203e962ed 100644 --- a/playground/src/app/components/C9RComponent.scss +++ b/playground/src/app/components/C9RComponent.scss @@ -25,6 +25,6 @@ $block: '.c9r-component'; &__editor-info { font-size: 14px; - color: var(--g-color-private-black-300); + color: var(--g-color-text-secondary); } } diff --git a/playground/src/styles/globals.scss b/playground/src/styles/globals.scss index 9fb6a01e75..bfbf1af4e2 100644 --- a/playground/src/styles/globals.scss +++ b/playground/src/styles/globals.scss @@ -1,4 +1,5 @@ @import '../../../styles/styles.scss'; +@import '../../../src/editor-v2/styles/root.scss'; html, body { diff --git a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss index 92209faa58..50418c0c11 100644 --- a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss +++ b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss @@ -1,4 +1,3 @@ -@import '../../../styles/root.scss'; @import '../../../styles/variables.scss'; @import '../../../styles/mixins.scss'; diff --git a/src/editor-v2/components/Panels/Panels.scss b/src/editor-v2/components/Panels/Panels.scss index e7865c13f7..bdad68a03a 100644 --- a/src/editor-v2/components/Panels/Panels.scss +++ b/src/editor-v2/components/Panels/Panels.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/components/Sidebar/Sidebar.scss b/src/editor-v2/components/Sidebar/Sidebar.scss index fca4b373d6..04cb06e721 100644 --- a/src/editor-v2/components/Sidebar/Sidebar.scss +++ b/src/editor-v2/components/Sidebar/Sidebar.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/components/Tabs/Tabs.scss b/src/editor-v2/components/Tabs/Tabs.scss index c4f2e727d9..a67b9899f1 100644 --- a/src/editor-v2/components/Tabs/Tabs.scss +++ b/src/editor-v2/components/Tabs/Tabs.scss @@ -8,7 +8,7 @@ $block: '.#{$ns}tabs'; width: 100%; &__item { - color: var(--g-color-private-black-300); + color: var(--g-color-text-hint); font-weight: 500; letter-spacing: 0.5px; cursor: pointer; diff --git a/src/editor-v2/containers/BigOverlay/BigOverlay.scss b/src/editor-v2/containers/BigOverlay/BigOverlay.scss index 3d79aed84a..bbb1e76e1d 100644 --- a/src/editor-v2/containers/BigOverlay/BigOverlay.scss +++ b/src/editor-v2/containers/BigOverlay/BigOverlay.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/containers/BlockConfig/BlockConfig.scss b/src/editor-v2/containers/BlockConfig/BlockConfig.scss index 14921baad8..306bf79ee2 100644 --- a/src/editor-v2/containers/BlockConfig/BlockConfig.scss +++ b/src/editor-v2/containers/BlockConfig/BlockConfig.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/containers/BlocksList/BlocksList.scss b/src/editor-v2/containers/BlocksList/BlocksList.scss index b6b793ff3b..90bffb0cd5 100644 --- a/src/editor-v2/containers/BlocksList/BlocksList.scss +++ b/src/editor-v2/containers/BlocksList/BlocksList.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; @@ -31,16 +30,16 @@ $block: '.#{$ns}blocks-list'; &__name { justify-self: flex-end; - color: var(--g-color-text-dark-secondary); + color: var(--g-color-text-secondary); } &__title { - color: var(--g-color-text-dark-secondary); + color: var(--g-color-text-secondary); margin-bottom: 10px; } &__icon { - color: var(--g-color-text-dark-complementary); + color: var(--g-color-text-complementary); } &__group { diff --git a/src/editor-v2/containers/Editor/Editor.scss b/src/editor-v2/containers/Editor/Editor.scss index cdb4c1f6d1..da304c330d 100644 --- a/src/editor-v2/containers/Editor/Editor.scss +++ b/src/editor-v2/containers/Editor/Editor.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/containers/GlobalConfig/GlobalConfig.scss b/src/editor-v2/containers/GlobalConfig/GlobalConfig.scss index 8210171157..45849ce92c 100644 --- a/src/editor-v2/containers/GlobalConfig/GlobalConfig.scss +++ b/src/editor-v2/containers/GlobalConfig/GlobalConfig.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/containers/MiddleScreen/MiddleScreen.scss b/src/editor-v2/containers/MiddleScreen/MiddleScreen.scss index e2a530dd50..2761a12d7f 100644 --- a/src/editor-v2/containers/MiddleScreen/MiddleScreen.scss +++ b/src/editor-v2/containers/MiddleScreen/MiddleScreen.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; @@ -18,7 +17,7 @@ $block: '.#{$ns}middle-screen'; flex: 1 1 auto; overflow-y: auto; // height: 100%; - background-color: var(--g-color-text-dark-secondary); + background-color: var(--g-color-text-secondary); } &__wrapper { diff --git a/src/editor-v2/containers/Overlay/Overlay.scss b/src/editor-v2/containers/Overlay/Overlay.scss index d478f7526f..e32b9da484 100644 --- a/src/editor-v2/containers/Overlay/Overlay.scss +++ b/src/editor-v2/containers/Overlay/Overlay.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/containers/Source/Source.scss b/src/editor-v2/containers/Source/Source.scss index 04512580ea..f2da4ce7ca 100644 --- a/src/editor-v2/containers/Source/Source.scss +++ b/src/editor-v2/containers/Source/Source.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/containers/SourceCode/SourceCode.scss b/src/editor-v2/containers/SourceCode/SourceCode.scss index 625207caf1..421536a9f9 100644 --- a/src/editor-v2/containers/SourceCode/SourceCode.scss +++ b/src/editor-v2/containers/SourceCode/SourceCode.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/containers/Tree/DragContext.scss b/src/editor-v2/containers/Tree/DragContext.scss index ae1097e5ed..19f0786503 100644 --- a/src/editor-v2/containers/Tree/DragContext.scss +++ b/src/editor-v2/containers/Tree/DragContext.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; @@ -7,8 +6,8 @@ $block: '.#{$ns}preview'; #{$block} { position: absolute; height: auto; - background-color: var(--g-color-base-brand-light); - border: 1px dashed var(--g-color-line-brand); + background-color: var(--g-color-text-hint); + border: 1px dashed var(--g-color-text-primary); border-radius: 4px; padding: 8px; z-index: 1000; diff --git a/src/editor-v2/containers/Tree/Tree.scss b/src/editor-v2/containers/Tree/Tree.scss index f2daed6b1d..547de744bd 100644 --- a/src/editor-v2/containers/Tree/Tree.scss +++ b/src/editor-v2/containers/Tree/Tree.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/containers/Tree/TreeContent.scss b/src/editor-v2/containers/Tree/TreeContent.scss index 2f58420217..5b724500a0 100644 --- a/src/editor-v2/containers/Tree/TreeContent.scss +++ b/src/editor-v2/containers/Tree/TreeContent.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; @@ -12,10 +11,6 @@ $block: '.#{$ns}tree-content'; margin-bottom: 8px; border-radius: 4px; transition: background-color 0.2s; - - &:hover { - background-color: var(--g-color-text-light-secondary); - } } &__children { diff --git a/src/editor-v2/containers/Tree/TreeItem.scss b/src/editor-v2/containers/Tree/TreeItem.scss index ae821fcfe7..da2f2d6db8 100644 --- a/src/editor-v2/containers/Tree/TreeItem.scss +++ b/src/editor-v2/containers/Tree/TreeItem.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; @@ -50,7 +49,7 @@ $block: '.#{$ns}tree-item'; } &__type { - color: var(--g-color-text-dark-secondary); + color: var(--g-color-text-secondary); } &__title { @@ -63,9 +62,5 @@ $block: '.#{$ns}tree-item'; margin-bottom: 0; border-radius: 4px; transition: background-color 0.2s; - - &:hover { - background-color: var(--g-color-text-light-secondary); - } } } diff --git a/src/editor-v2/containers/ViewSwitches/ViewSwitches.scss b/src/editor-v2/containers/ViewSwitches/ViewSwitches.scss index 852d29aab9..4844fad8fa 100644 --- a/src/editor-v2/containers/ViewSwitches/ViewSwitches.scss +++ b/src/editor-v2/containers/ViewSwitches/ViewSwitches.scss @@ -1,4 +1,3 @@ -@import '../../styles/root.scss'; @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; diff --git a/src/editor-v2/styles/root.scss b/src/editor-v2/styles/root.scss index d74f9a58f4..8334b15f52 100644 --- a/src/editor-v2/styles/root.scss +++ b/src/editor-v2/styles/root.scss @@ -1,12 +1,11 @@ .g-root { - --g-color-base-brand: var(--g-color-private-black-550-solid); - --g-color-base-brand-hover: var(--g-color-private-black-600-solid); + --g-color-base-brand: var(--g-color-text-primary); + --g-color-base-brand-hover: var(--g-color-text-complementary); --g-color-base-selection: var(--g-color-private-black-200); --g-color-base-selection-hover: var(--g-color-private-black-300); --g-color-base-brand: var(--g-color-private-black-850-solid); --g-color-base-brand-hover: var(--g-color-private-black-700-solid); --g-color-text-brand-contrast: var(--g-color-text-light-primary); - --g-color-line-brand: var(--g-color-text-primary); - --g-color-text-brand: var(--g-color-private-brand-700-solid); --g-color-text-brand-heavy: var(--g-color-private-black-700-solid); + --g-color-line-brand: var(--g-color-text-primary); } From 897b7688afc8255c92dec812ed75477fcca6bf75 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 7 Apr 2025 20:24:49 +0300 Subject: [PATCH 046/118] fix: update editor-v2 exports --- playground/src/app/page.tsx | 3 --- src/common/postMessage.ts | 1 + .../BlockConfigForm.scss} | 2 +- .../BlockConfigForm.tsx} | 10 +++++----- src/editor-v2/containers/Editor/Editor.tsx | 4 +++- src/editor-v2/containers/index.ts | 7 +++++++ src/editor-v2/hooks/index.ts | 2 ++ src/editor-v2/hooks/useEditorTabs.tsx | 4 ++-- src/editor-v2/index.ts | 5 ++++- src/editor-v2/utils/index.ts | 2 ++ 10 files changed, 27 insertions(+), 13 deletions(-) rename src/editor-v2/containers/{BlockConfig/BlockConfig.scss => BlockConfigForm/BlockConfigForm.scss} (93%) rename src/editor-v2/containers/{BlockConfig/BlockConfig.tsx => BlockConfigForm/BlockConfigForm.tsx} (87%) create mode 100644 src/editor-v2/containers/index.ts create mode 100644 src/editor-v2/hooks/index.ts diff --git a/playground/src/app/page.tsx b/playground/src/app/page.tsx index 854c352b7d..d3ff81a737 100644 --- a/playground/src/app/page.tsx +++ b/playground/src/app/page.tsx @@ -4,8 +4,6 @@ import block from 'bem-cn-lite'; import * as React from 'react'; import {Editor} from '../../../src/editor-v2'; -import Source from '../../../src/editor-v2/containers/Source/Source'; -import ViewSwitches from '../../../src/editor-v2/containers/ViewSwitches/ViewSwitches'; import C9RComponent from './components/C9RComponent'; import './page.scss'; @@ -24,7 +22,6 @@ const COMPONENTS_CONFIG = { component: Test, }, ], - rightTop: [Source, ViewSwitches], }; export default function Home() { diff --git a/src/common/postMessage.ts b/src/common/postMessage.ts index 8faadd6fd4..54fe90f937 100644 --- a/src/common/postMessage.ts +++ b/src/common/postMessage.ts @@ -39,5 +39,6 @@ export function usePostMessageAPIListener<K extends keyof EventMessageTypes>( ) { React.useEffect(() => { return listenPostMessageEvents(action, callback); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [...deps]); } diff --git a/src/editor-v2/containers/BlockConfig/BlockConfig.scss b/src/editor-v2/containers/BlockConfigForm/BlockConfigForm.scss similarity index 93% rename from src/editor-v2/containers/BlockConfig/BlockConfig.scss rename to src/editor-v2/containers/BlockConfigForm/BlockConfigForm.scss index 306bf79ee2..316de2fb22 100644 --- a/src/editor-v2/containers/BlockConfig/BlockConfig.scss +++ b/src/editor-v2/containers/BlockConfigForm/BlockConfigForm.scss @@ -1,7 +1,7 @@ @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; -$block: '.#{$ns}block-config'; +$block: '.#{$ns}block-config-form'; #{$block} { &__title { diff --git a/src/editor-v2/containers/BlockConfig/BlockConfig.tsx b/src/editor-v2/containers/BlockConfigForm/BlockConfigForm.tsx similarity index 87% rename from src/editor-v2/containers/BlockConfig/BlockConfig.tsx rename to src/editor-v2/containers/BlockConfigForm/BlockConfigForm.tsx index c0b753d07e..18e14fdd20 100644 --- a/src/editor-v2/containers/BlockConfig/BlockConfig.tsx +++ b/src/editor-v2/containers/BlockConfigForm/BlockConfigForm.tsx @@ -6,15 +6,15 @@ import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {generateChildrenPathFromArray} from '../../utils'; import {editorCn} from '../../utils/cn'; -import './BlockConfig.scss'; +import './BlockConfigForm.scss'; -const b = editorCn('block-config'); +const b = editorCn('block-config-form'); -interface BlockConfigProps { +interface BlockConfigFormProps { className?: string; } -const BlockConfig = ({className}: BlockConfigProps) => { +const BlockConfigForm = ({className}: BlockConfigFormProps) => { const {selectedBlock, content, blocks, subBlocks, updateField} = useMainEditorStore(); const currentBlockPath = selectedBlock ? generateChildrenPathFromArray(selectedBlock) : '[]'; @@ -48,4 +48,4 @@ const BlockConfig = ({className}: BlockConfigProps) => { ); }; -export default BlockConfig; +export default BlockConfigForm; diff --git a/src/editor-v2/containers/Editor/Editor.tsx b/src/editor-v2/containers/Editor/Editor.tsx index e8bef32f82..cc1d658eab 100644 --- a/src/editor-v2/containers/Editor/Editor.tsx +++ b/src/editor-v2/containers/Editor/Editor.tsx @@ -11,6 +11,8 @@ import {useEditorTabs} from '../../hooks/useEditorTabs'; import useMainEditorInitialize from '../../hooks/useMainEditorInitialize'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; import {editorCn} from '../../utils/cn'; +import Source from '../Source/Source'; +import ViewSwitches from '../ViewSwitches/ViewSwitches'; import './Editor.scss'; @@ -62,7 +64,7 @@ const EditorView = ({componentsConfig = {}}: EditorViewProps) => { right={ <Sidebar tabs={right} - top={componentsConfig.rightTop} + top={[...(componentsConfig.rightTop || []), Source, ViewSwitches]} defaultTab="block-config" /> } diff --git a/src/editor-v2/containers/index.ts b/src/editor-v2/containers/index.ts new file mode 100644 index 0000000000..fc0241a724 --- /dev/null +++ b/src/editor-v2/containers/index.ts @@ -0,0 +1,7 @@ +export {Editor} from './Editor/Editor'; +export {default as BlockConfigForm} from './BlockConfigForm/BlockConfigForm'; +export {default as BlocksList} from './BlocksList/BlocksList'; +export {default as Source} from './Source/Source'; +export {default as SourceCode} from './SourceCode/SourceCode'; +export {default as Tree} from './Tree/Tree'; +export {default as ViewSwitches} from './ViewSwitches/ViewSwitches'; diff --git a/src/editor-v2/hooks/index.ts b/src/editor-v2/hooks/index.ts new file mode 100644 index 0000000000..3b3541883c --- /dev/null +++ b/src/editor-v2/hooks/index.ts @@ -0,0 +1,2 @@ +export {usePostMessageEvents} from './usePostMessageEvents'; +export {useMainEditorStore} from './useMainEditorStore'; diff --git a/src/editor-v2/hooks/useEditorTabs.tsx b/src/editor-v2/hooks/useEditorTabs.tsx index 5db43263b5..e1257a2c46 100644 --- a/src/editor-v2/hooks/useEditorTabs.tsx +++ b/src/editor-v2/hooks/useEditorTabs.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import Tabs, {TabsItemProps} from '../components/Tabs/Tabs'; -import BlockConfig from '../containers/BlockConfig/BlockConfig'; +import BlockConfigForm from '../containers/BlockConfigForm/BlockConfigForm'; import BlocksList from '../containers/BlocksList/BlocksList'; import GlobalConfig from '../containers/GlobalConfig/GlobalConfig'; import SourceCode from '../containers/SourceCode/SourceCode'; @@ -64,7 +64,7 @@ export const useEditorTabs = ({ { id: 'block-config', title: 'INPUTS', - component: BlockConfig, + component: BlockConfigForm, }, { id: 'source-code-yaml', diff --git a/src/editor-v2/index.ts b/src/editor-v2/index.ts index 4697a13c9f..346f948557 100644 --- a/src/editor-v2/index.ts +++ b/src/editor-v2/index.ts @@ -1,2 +1,5 @@ -export {Editor} from './containers/Editor/Editor'; export * from '../common/types'; +export * from './containers'; +export * from './hooks'; +export * from './utils'; +export * from './constants'; diff --git a/src/editor-v2/utils/index.ts b/src/editor-v2/utils/index.ts index 8de140f3ba..8573f80f81 100644 --- a/src/editor-v2/utils/index.ts +++ b/src/editor-v2/utils/index.ts @@ -1,6 +1,8 @@ import _ from 'lodash'; import {ConstructorBlock} from '../../models'; +export * from './code'; +export * from './cn'; export function insert<T>(arr: Array<T>, index: number, newItem: T) { return [...arr.slice(0, index), newItem, ...arr.slice(index)]; From f2159fb4203abfff87adc30a1c4e3339192252ef Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Tue, 8 Apr 2025 11:52:53 +0300 Subject: [PATCH 047/118] fix: editor blocks styles --- src/editor-v2/components/Sidebar/Sidebar.scss | 3 +++ src/editor-v2/components/Tabs/Tabs.scss | 7 ++++++- src/editor-v2/containers/GlobalConfig/GlobalConfig.scss | 5 ++--- src/editor-v2/containers/ViewSwitches/ViewSwitches.scss | 4 ++++ src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx | 1 + 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/editor-v2/components/Sidebar/Sidebar.scss b/src/editor-v2/components/Sidebar/Sidebar.scss index 04cb06e721..df6eff3df0 100644 --- a/src/editor-v2/components/Sidebar/Sidebar.scss +++ b/src/editor-v2/components/Sidebar/Sidebar.scss @@ -13,5 +13,8 @@ $block: '.#{$ns}sidebar'; &__block { width: 100%; border-bottom: 1px solid var(--g-color-line-generic); + overflow-x: scroll; + box-sizing: border-box; + flex: 0 0 auto; } } diff --git a/src/editor-v2/components/Tabs/Tabs.scss b/src/editor-v2/components/Tabs/Tabs.scss index a67b9899f1..c9b6b3dd12 100644 --- a/src/editor-v2/components/Tabs/Tabs.scss +++ b/src/editor-v2/components/Tabs/Tabs.scss @@ -6,6 +6,8 @@ $block: '.#{$ns}tabs'; #{$block} { overflow: auto; width: 100%; + display: flex; + flex-direction: column; &__item { color: var(--g-color-text-hint); @@ -25,11 +27,14 @@ $block: '.#{$ns}tabs'; display: inline-flex; flex-direction: row; padding: 16px 4px; + flex: 0 0 auto; + box-sizing: border-box; + overflow-x: auto; } &__body { flex: 1; - border-right: 1px solid var(--g-color-line-generic); + min-height: 1px; overflow-y: auto; } } diff --git a/src/editor-v2/containers/GlobalConfig/GlobalConfig.scss b/src/editor-v2/containers/GlobalConfig/GlobalConfig.scss index 45849ce92c..a51239af78 100644 --- a/src/editor-v2/containers/GlobalConfig/GlobalConfig.scss +++ b/src/editor-v2/containers/GlobalConfig/GlobalConfig.scss @@ -4,11 +4,10 @@ $block: '.#{$ns}global-config'; #{$block} { - padding: 12px; - &__title { - margin-bottom: 16px; + padding: 12px; margin-top: 8px; @include text-subheader-3; + border-bottom: 1px solid var(--g-color-line-generic); } } diff --git a/src/editor-v2/containers/ViewSwitches/ViewSwitches.scss b/src/editor-v2/containers/ViewSwitches/ViewSwitches.scss index 4844fad8fa..a6e4d7a65a 100644 --- a/src/editor-v2/containers/ViewSwitches/ViewSwitches.scss +++ b/src/editor-v2/containers/ViewSwitches/ViewSwitches.scss @@ -12,5 +12,9 @@ $block: '.#{$ns}view-switches'; &__zoom { display: flex; gap: 4px; + + &-select { + min-width: 80px; + } } } diff --git a/src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx b/src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx index 78139a36a4..51ce4471d5 100644 --- a/src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx +++ b/src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx @@ -99,6 +99,7 @@ const ViewSwitches: React.FC = () => { </Button> <Select + className={b('zoom-select')} multiple={false} value={currentZoomValue} options={zoomOptions} From d3f85328d9d2b277ebf278a7cca454de02f8553d Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Tue, 8 Apr 2025 12:36:12 +0300 Subject: [PATCH 048/118] fix: title style editor source code --- src/editor-v2/containers/SourceCode/SourceCode.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/editor-v2/containers/SourceCode/SourceCode.scss b/src/editor-v2/containers/SourceCode/SourceCode.scss index 421536a9f9..fe73fd4281 100644 --- a/src/editor-v2/containers/SourceCode/SourceCode.scss +++ b/src/editor-v2/containers/SourceCode/SourceCode.scss @@ -15,7 +15,7 @@ $block: '.#{$ns}source-code'; font-weight: 500; font-size: 13px; line-height: 18px; - color: var(--g-color-private-black-300); + color: var(--g-color-text-secondary); } &__code { From 3fce38588241d2ca89521aeca090634f6b014e8c Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Tue, 8 Apr 2025 12:40:55 +0300 Subject: [PATCH 049/118] fix: editor root styles --- src/editor-v2/styles/root.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/editor-v2/styles/root.scss b/src/editor-v2/styles/root.scss index 8334b15f52..ff241f9a01 100644 --- a/src/editor-v2/styles/root.scss +++ b/src/editor-v2/styles/root.scss @@ -1,6 +1,4 @@ .g-root { - --g-color-base-brand: var(--g-color-text-primary); - --g-color-base-brand-hover: var(--g-color-text-complementary); --g-color-base-selection: var(--g-color-private-black-200); --g-color-base-selection-hover: var(--g-color-private-black-300); --g-color-base-brand: var(--g-color-private-black-850-solid); From e161a0120de561d28e7e5d9d2e916d38d0647149 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Wed, 9 Apr 2025 15:43:30 +0300 Subject: [PATCH 050/118] fix: brand colors --- src/editor-v2/containers/BigOverlay/BigOverlay.scss | 2 +- src/editor-v2/containers/Overlay/Overlay.scss | 8 ++++++-- src/editor-v2/containers/Overlay/Overlay.tsx | 4 ++-- src/editor-v2/styles/root.scss | 10 +++++++--- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/editor-v2/containers/BigOverlay/BigOverlay.scss b/src/editor-v2/containers/BigOverlay/BigOverlay.scss index bbb1e76e1d..1d09dfdb1b 100644 --- a/src/editor-v2/containers/BigOverlay/BigOverlay.scss +++ b/src/editor-v2/containers/BigOverlay/BigOverlay.scss @@ -12,7 +12,7 @@ $block: '.#{$ns}big-overlay'; pointer-events: none; overflow: hidden; - $border: 3px var(--g-color-line-brand) solid; + $border: 3px var(--g-color-base-brand) solid; &__border { pointer-events: none; diff --git a/src/editor-v2/containers/Overlay/Overlay.scss b/src/editor-v2/containers/Overlay/Overlay.scss index e32b9da484..ce09269329 100644 --- a/src/editor-v2/containers/Overlay/Overlay.scss +++ b/src/editor-v2/containers/Overlay/Overlay.scss @@ -5,8 +5,8 @@ $block: '.#{$ns}overlay'; #{$block} { $border-width: 3px; - $border-select: 5px var(--g-color-line-brand) solid; - $border: $border-width var(--g-color-line-brand) solid; + $border-select: 5px var(--g-color-base-brand) solid; + $border: $border-width var(--g-color-base-brand) solid; $border-radius: 24px; position: absolute; @@ -87,4 +87,8 @@ $block: '.#{$ns}overlay'; padding: 3px 4px; } } + + &__reorder-icon { + color: var(--g-color-base-brand); + } } diff --git a/src/editor-v2/containers/Overlay/Overlay.tsx b/src/editor-v2/containers/Overlay/Overlay.tsx index 86cbc6bef5..f639d0e738 100644 --- a/src/editor-v2/containers/Overlay/Overlay.tsx +++ b/src/editor-v2/containers/Overlay/Overlay.tsx @@ -113,7 +113,7 @@ const Overlay = ({className, canvasElement}: OverlayProps) => { <div className={b('actions')}> <div className={b('actions-box', {reorder: true})}> <Button view="flat" size={'m'} onClick={handleMoveUp}> - <Icon data={ChevronUp} size={18} /> + <Icon className={b('reorder-icon')} data={ChevronUp} size={18} /> </Button> </div> <div className={b('actions-box', {main: true})}> @@ -136,7 +136,7 @@ const Overlay = ({className, canvasElement}: OverlayProps) => { </div> <div className={b('actions-box', {reorder: true})}> <Button view="flat" size={'m'} onClick={handleMoveDown}> - <Icon data={ChevronDown} size={18} /> + <Icon className={b('reorder-icon')} data={ChevronDown} size={18} /> </Button> </div> </div> diff --git a/src/editor-v2/styles/root.scss b/src/editor-v2/styles/root.scss index ff241f9a01..4eeff88b03 100644 --- a/src/editor-v2/styles/root.scss +++ b/src/editor-v2/styles/root.scss @@ -1,9 +1,13 @@ .g-root { --g-color-base-selection: var(--g-color-private-black-200); --g-color-base-selection-hover: var(--g-color-private-black-300); - --g-color-base-brand: var(--g-color-private-black-850-solid); - --g-color-base-brand-hover: var(--g-color-private-black-700-solid); + --g-color-base-brand: rgb(38, 38, 38); // --g-color-private-black-850-solid only light theme + --g-color-base-brand-hover: rgb( + 76, + 76, + 76 + ); // --g-color-private-black-700-solid only light theme --g-color-text-brand-contrast: var(--g-color-text-light-primary); - --g-color-text-brand-heavy: var(--g-color-private-black-700-solid); + --g-color-text-brand-heavy: rgb(76, 76, 76); --g-color-line-brand: var(--g-color-text-primary); } From e17c42802e1e29fd16012e07807a1637020c6f3a Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Wed, 9 Apr 2025 17:18:23 +0300 Subject: [PATCH 051/118] fix: editor zoom device centering --- .../containers/MiddleScreen/MiddleScreen.scss | 12 +++++++++++- .../containers/MiddleScreen/MiddleScreen.tsx | 7 ++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/editor-v2/containers/MiddleScreen/MiddleScreen.scss b/src/editor-v2/containers/MiddleScreen/MiddleScreen.scss index 2761a12d7f..0944756acc 100644 --- a/src/editor-v2/containers/MiddleScreen/MiddleScreen.scss +++ b/src/editor-v2/containers/MiddleScreen/MiddleScreen.scss @@ -24,13 +24,23 @@ $block: '.#{$ns}middle-screen'; width: 100%; height: 100%; margin: auto; + display: flex; + justify-content: center; + align-items: flex-start; + overflow: hidden; } &__canvas { - transform-origin: left top; + transform-origin: center top; overflow-y: scroll; position: relative; height: 100%; + max-width: 100%; + } + + &__iframe { + display: block; + margin: 0 auto; } &__overlay { diff --git a/src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx b/src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx index 198c759ef8..5e6c8be06c 100644 --- a/src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx +++ b/src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx @@ -45,14 +45,15 @@ const MiddleScreen = ({className, CustomTop}: MiddleScreenProps) => { </div> ) : null} <div className={b('content')}> - <div className={b('wrapper')} style={{width: deviceWidth}}> + <div className={b('wrapper')}> <div ref={setCanvasRef} className={b('canvas', {hidden: !initialized})} style={{ transform: `scale(${zoom}%)`, height: `${(100 / zoom) * 100}%`, - width: `${(100 / zoom) * 100}%`, + width: deviceWidth, + maxWidth: `${(100 / zoom) * 100}%`, }} > <iframe @@ -60,7 +61,7 @@ const MiddleScreen = ({className, CustomTop}: MiddleScreenProps) => { className={b('iframe')} src={url} height={`${height}px`} - width="100%" + width={deviceWidth} frameBorder="0" title="Page Constructor Iframe" /> From 61ec8337d3fb5951377a2308df32e680c711705d Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Wed, 9 Apr 2025 18:04:20 +0300 Subject: [PATCH 052/118] fix: restore editor refresh fields --- .../DynamicForm/FieldBase/FieldBase.scss | 15 +++++++----- .../DynamicForm/FieldBase/FieldBase.tsx | 23 ++++++++++++++++--- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss index 50418c0c11..df3effd9de 100644 --- a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss +++ b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss @@ -5,17 +5,16 @@ $block: '.#{$ns}field-base'; #{$block} { $class: &; - padding: 16px 12px; + padding: 16px 12px 16px 0; border-bottom: 1px solid var(--g-color-line-generic); position: relative; display: flex; flex-direction: row; align-items: flex-start; - gap: 8px; &:hover { - & > #{$class}__top > #{$class}__button { + & > #{$class}__top #{$class}__title > #{$class}__button { opacity: 1; } } @@ -28,6 +27,7 @@ $block: '.#{$ns}field-base'; width: 100%; flex-basis: initial; } + & > #{$block}__children { width: 100%; } @@ -37,12 +37,13 @@ $block: '.#{$ns}field-base'; display: flex; align-items: center; gap: 4px; - flex: 0 0 64px; - word-break: break-all; + flex: 0 0 84px; + word-break: break-word; } &__children { flex: 2 1 auto; + margin-left: 12px; } &__foldable { @@ -68,7 +69,9 @@ $block: '.#{$ns}field-base'; &__title { @include text-body-2; - margin: 5px 0; + margin: 5px 16px 5px 0; + display: flex; + flex-direction: row; &_size { &_s { diff --git a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx index edfb58f079..7426361176 100644 --- a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx +++ b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.tsx @@ -1,4 +1,5 @@ -import {ArrowToggle} from '@gravity-ui/uikit'; +import {ArrowToggle, Button, Icon} from '@gravity-ui/uikit'; +import {ArrowRotateLeft} from '@gravity-ui/icons'; import _ from 'lodash'; import * as React from 'react'; @@ -25,13 +26,29 @@ const FieldBase: React.FC<FieldBaseProps> = ({ textSize = 's', children, expandable = false, + onRefresh, }) => { const [showChildren, setShowChildren] = React.useState(!expandable); const titleComponent = React.useMemo(() => { if (title) { const defaultTitle = ( - <div className={b('title', {size: textSize})}>{_.capitalize(title)}</div> + <div className={b('title', {size: textSize})}> + {onRefresh && ( + <Button + className={b('button')} + onClick={(e: React.MouseEvent<HTMLButtonElement>) => { + e.stopPropagation(); + onRefresh(undefined); + }} + view={'flat'} + size={'xs'} + > + <Icon data={ArrowRotateLeft} size={14} /> + </Button> + )} + <span>{_.capitalize(title)}</span> + </div> ); if (expandable) { @@ -51,7 +68,7 @@ const FieldBase: React.FC<FieldBaseProps> = ({ } return null; - }, [expandable, showChildren, textSize, title]); + }, [expandable, showChildren, textSize, title, onRefresh]); return ( <div className={b({expandable}, className)}> From 706c9cc3bb51b2ef1ef8bc497d07c59c2325ffee Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Wed, 9 Apr 2025 18:05:00 +0300 Subject: [PATCH 053/118] fix: paddings --- .../containers/BlockConfigForm/BlockConfigForm.scss | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/editor-v2/containers/BlockConfigForm/BlockConfigForm.scss b/src/editor-v2/containers/BlockConfigForm/BlockConfigForm.scss index 316de2fb22..c21fc9318b 100644 --- a/src/editor-v2/containers/BlockConfigForm/BlockConfigForm.scss +++ b/src/editor-v2/containers/BlockConfigForm/BlockConfigForm.scss @@ -3,19 +3,21 @@ $block: '.#{$ns}block-config-form'; +$padding: 12px; + #{$block} { &__title { @include text-subheader-3; - padding: 12px 16px; + padding: $padding; border-bottom: 1px solid var(--g-color-line-generic); } &__form { - padding: 12px 0; + padding: $padding 0; } &__empty { - padding: 12px 16px; + padding: $padding; display: flex; justify-content: center; align-items: center; From e84c13580b48afff866e94dd5a3098456619a428 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Wed, 9 Apr 2025 20:22:52 +0300 Subject: [PATCH 054/118] fix: root css --- src/editor-v2/styles/root.scss | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/editor-v2/styles/root.scss b/src/editor-v2/styles/root.scss index 4eeff88b03..c25a7298a9 100644 --- a/src/editor-v2/styles/root.scss +++ b/src/editor-v2/styles/root.scss @@ -1,12 +1,8 @@ .g-root { --g-color-base-selection: var(--g-color-private-black-200); --g-color-base-selection-hover: var(--g-color-private-black-300); - --g-color-base-brand: rgb(38, 38, 38); // --g-color-private-black-850-solid only light theme - --g-color-base-brand-hover: rgb( - 76, - 76, - 76 - ); // --g-color-private-black-700-solid only light theme + --g-color-base-brand: rgb(38, 38, 38); // --g-color-private-black-850-solid light theme only + --g-color-base-brand-hover: rgb(76, 76, 76); // --g-color-private-black-700-solid --g-color-text-brand-contrast: var(--g-color-text-light-primary); --g-color-text-brand-heavy: rgb(76, 76, 76); --g-color-line-brand: var(--g-color-text-primary); From eec4375f76a6d104ad7a00eec1bd4fe7acc4d54b Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Tue, 15 Apr 2025 21:58:28 +0300 Subject: [PATCH 055/118] fix: editor add initialContent --- src/editor-v2/containers/Editor/Editor.tsx | 5 +++-- src/editor-v2/hooks/useMainEditorInitialize.ts | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/editor-v2/containers/Editor/Editor.tsx b/src/editor-v2/containers/Editor/Editor.tsx index cc1d658eab..edc2c39ece 100644 --- a/src/editor-v2/containers/Editor/Editor.tsx +++ b/src/editor-v2/containers/Editor/Editor.tsx @@ -26,6 +26,7 @@ interface SidebarTabComponent { interface EditorViewProps { onUpdate?: (pageContent: PageContentWithNavigation) => void; initialUrl: string; + initialContent?: PageContentWithNavigation; disableUrlField?: boolean; componentsConfig?: { middleTop?: React.ElementType; @@ -36,11 +37,11 @@ interface EditorViewProps { }; } -const EditorView = ({componentsConfig = {}}: EditorViewProps) => { +const EditorView = ({componentsConfig = {}, initialContent}: EditorViewProps) => { const store = useMainEditorStore(); const {manipulateOverlayMode, disableMode} = store; - useMainEditorInitialize(); + useMainEditorInitialize(initialContent); // Disable insert mode on any MouseUp event // Maybe should be attached to body diff --git a/src/editor-v2/hooks/useMainEditorInitialize.ts b/src/editor-v2/hooks/useMainEditorInitialize.ts index 2326b95ec9..197ed7dd4e 100644 --- a/src/editor-v2/hooks/useMainEditorInitialize.ts +++ b/src/editor-v2/hooks/useMainEditorInitialize.ts @@ -1,9 +1,11 @@ +import * as React from 'react'; import {usePostMessageAPIListener} from '../../common/postMessage'; +import {PageContentWithNavigation} from '../../models'; import {useMainEditorStore} from './useMainEditorStore'; import {usePostMessageEvents} from './usePostMessageEvents'; -const useMainEditorInitialize = () => { +const useMainEditorInitialize = (initialContent?: PageContentWithNavigation) => { const {requestPostMessage} = usePostMessageEvents(); const { initialize, @@ -22,11 +24,20 @@ const useMainEditorInitialize = () => { () => { initialize(); requestPostMessage('GET_SUPPORTED_BLOCKS', {}); - requestPostMessage('GET_INITIAL_CONTENT', {}); + + if (!initialContent) { + requestPostMessage('GET_INITIAL_CONTENT', {}); + } }, [requestPostMessage], ); + React.useEffect(() => { + if (initialContent) { + setContent(initialContent); + } + }, [initialContent]); + usePostMessageAPIListener('ON_INITIAL_CONTENT', (data) => { setContent(data); }); From e2e2991c9788ebca4cf501d14650c315a0937b9c Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Thu, 17 Apr 2025 01:33:23 +0300 Subject: [PATCH 056/118] fix: eslint disable --- src/editor-v2/hooks/useMainEditorInitialize.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/editor-v2/hooks/useMainEditorInitialize.ts b/src/editor-v2/hooks/useMainEditorInitialize.ts index 197ed7dd4e..ae00f01234 100644 --- a/src/editor-v2/hooks/useMainEditorInitialize.ts +++ b/src/editor-v2/hooks/useMainEditorInitialize.ts @@ -36,6 +36,7 @@ const useMainEditorInitialize = (initialContent?: PageContentWithNavigation) => if (initialContent) { setContent(initialContent); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [initialContent]); usePostMessageAPIListener('ON_INITIAL_CONTENT', (data) => { From 2e58203b4643449710f7b304cd9b0e9fac55ffb5 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 28 Apr 2025 14:10:25 +0300 Subject: [PATCH 057/118] fix: json/yaml tab for selected block --- .../containers/SourceCode/SourceCode.tsx | 38 +++++++++++++++---- src/editor-v2/hooks/useEditorTabs.tsx | 8 +++- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/editor-v2/containers/SourceCode/SourceCode.tsx b/src/editor-v2/containers/SourceCode/SourceCode.tsx index 538c2ba2e2..aaa4d22684 100644 --- a/src/editor-v2/containers/SourceCode/SourceCode.tsx +++ b/src/editor-v2/containers/SourceCode/SourceCode.tsx @@ -1,10 +1,12 @@ import {ArrowDownToSquare} from '@gravity-ui/icons'; import {Button, ClipboardButton, Icon} from '@gravity-ui/uikit'; import yaml from 'js-yaml'; +import _ from 'lodash'; import * as React from 'react'; import {PageContentWithNavigation} from '../../../models'; import {useMainEditorStore} from '../../hooks/useMainEditorStore'; +import {generateChildrenPathFromArray} from '../../utils'; import {editorCn} from '../../utils/cn'; import './SourceCode.scss'; @@ -15,36 +17,58 @@ const b = editorCn('source-code'); interface SourceCodeProps { className?: string; format: 'yaml' | 'json'; + showSelectedBlockOnly?: boolean; } -const SourceCode = ({className, format}: SourceCodeProps) => { - const {content, setContent} = useMainEditorStore(); +const SourceCode = ({className, format, showSelectedBlockOnly = false}: SourceCodeProps) => { + const {content, setContent, selectedBlock} = useMainEditorStore(); const [isOpen, setIsOpen] = React.useState(false); const handleUpdate = (tempConfig: string) => { - let object: PageContentWithNavigation | undefined; + let object: any; try { if (tempConfig.trim().startsWith('{') && tempConfig.trim().endsWith('}')) { object = JSON.parse(tempConfig); } else { - object = yaml.load(tempConfig) as PageContentWithNavigation; + object = yaml.load(tempConfig); } } catch { // eslint-disable-next-line no-console console.error('JSON.parse failed'); + return; } - if (object) { - setContent(object); + if (showSelectedBlockOnly && selectedBlock) { + // Update only the selected block + const currentBlockPath = generateChildrenPathFromArray(selectedBlock); + const newContent = _.cloneDeep(content); + _.set(newContent.blocks, currentBlockPath, object); + setContent(newContent); + } else if (object) { + // Update the entire content + setContent(object as PageContentWithNavigation); } setIsOpen(false); }; const textContent = React.useMemo(() => { + if (showSelectedBlockOnly && selectedBlock) { + const currentBlockPath = generateChildrenPathFromArray(selectedBlock); + const currentConfig = _.get(content.blocks, currentBlockPath || ''); + + if (currentConfig) { + return format === 'yaml' + ? yaml.dump(currentConfig) + : JSON.stringify(currentConfig, null, 2); + } + + return 'No block selected'; + } + return format === 'yaml' ? yaml.dump(content) : JSON.stringify(content, null, 2); - }, [format, content]); + }, [format, content, showSelectedBlockOnly, selectedBlock]); return ( <div className={b(null, className)}> diff --git a/src/editor-v2/hooks/useEditorTabs.tsx b/src/editor-v2/hooks/useEditorTabs.tsx index e1257a2c46..7e5d1acbd0 100644 --- a/src/editor-v2/hooks/useEditorTabs.tsx +++ b/src/editor-v2/hooks/useEditorTabs.tsx @@ -69,12 +69,16 @@ export const useEditorTabs = ({ { id: 'source-code-yaml', title: 'YAML', - component: () => <SourceCode format="yaml" />, + component: () => ( + <SourceCode format="yaml" showSelectedBlockOnly={true} /> + ), }, { id: 'source-code-json', title: 'JSON', - component: () => <SourceCode format="json" />, + component: () => ( + <SourceCode format="json" showSelectedBlockOnly={true} /> + ), }, ]} /> From 7b7479ccb96227cda2d08f93b2b8173d312837ef Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 28 Apr 2025 20:19:55 +0300 Subject: [PATCH 058/118] fix: add anyof case for editorv2 --- src/common/types/forms.ts | 13 +++ .../components/DynamicForm/DynamicForm.tsx | 16 +++- .../DynamicForm/Fields/AnyOf/AnyOf.scss | 14 +++ .../DynamicForm/Fields/AnyOf/AnyOf.tsx | 92 +++++++++++++++++++ src/utils/form-generator.ts | 28 ++++++ 5 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 src/editor-v2/components/DynamicForm/Fields/AnyOf/AnyOf.scss create mode 100644 src/editor-v2/components/DynamicForm/Fields/AnyOf/AnyOf.tsx diff --git a/src/common/types/forms.ts b/src/common/types/forms.ts index f5a53c37dd..31716225c0 100644 --- a/src/common/types/forms.ts +++ b/src/common/types/forms.ts @@ -104,6 +104,18 @@ export interface OneOfInput { }[]; } +export interface AnyOfInput { + type: 'anyOf'; + name: string; + key?: string; + title: string; + options: { + value: string; + title: string; + properties: Array<ConfigInput>; + }[]; +} + export interface GeneralProps { showIf?: string; } @@ -119,5 +131,6 @@ export type ConfigInput = ( | ArrayTextInput | ArrayObjectInput | OneOfInput + | AnyOfInput ) & GeneralProps; diff --git a/src/editor-v2/components/DynamicForm/DynamicForm.tsx b/src/editor-v2/components/DynamicForm/DynamicForm.tsx index 9531c21815..853e6986e7 100644 --- a/src/editor-v2/components/DynamicForm/DynamicForm.tsx +++ b/src/editor-v2/components/DynamicForm/DynamicForm.tsx @@ -9,6 +9,7 @@ import ArrayDynamicField from './Fields/Array/Array'; import BooleanDynamicField from './Fields/Boolean/Boolean'; import NumberDynamicField from './Fields/Number/Number'; import ObjectDynamicField from './Fields/Object/Object'; +import AnyOfDynamicField from './Fields/AnyOf/AnyOf'; import OneOfDynamicField from './Fields/OneOf/OneOf'; import SelectDynamicField from './Fields/Select/Select'; import TextDynamicField from './Fields/Text/Text'; @@ -183,6 +184,19 @@ const DynamicForm = ({blockConfig, onUpdate, contentConfig}: DynamicFormProps) = /> ); } + case 'anyOf': { + if (!input || !('options' in input)) { + return null; + } + + return ( + <AnyOfDynamicField + inputConfig={input} + contentConfig={contentConfig} + onUpdate={onComplexDynamicFieldUpdate} + /> + ); + } default: { return <div>Ignore {JSON.stringify(input)}</div>; } @@ -192,7 +206,7 @@ const DynamicForm = ({blockConfig, onUpdate, contentConfig}: DynamicFormProps) = ); const sortedInputs = inputs.sort((x, y) => { - const nestingFieldTypes = ['object', 'array', 'oneOf']; + const nestingFieldTypes = ['object', 'array', 'oneOf', 'anyOf']; if (nestingFieldTypes.includes(x.type)) { return 1; } diff --git a/src/editor-v2/components/DynamicForm/Fields/AnyOf/AnyOf.scss b/src/editor-v2/components/DynamicForm/Fields/AnyOf/AnyOf.scss new file mode 100644 index 0000000000..af2528c796 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/AnyOf/AnyOf.scss @@ -0,0 +1,14 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; + +$block: '.#{$ns}anyof-dynamic-field'; + +#{$block} { + &__radio { + margin: 16px 12px; + } + + &__select { + min-width: 80px; + } +} diff --git a/src/editor-v2/components/DynamicForm/Fields/AnyOf/AnyOf.tsx b/src/editor-v2/components/DynamicForm/Fields/AnyOf/AnyOf.tsx new file mode 100644 index 0000000000..7ef0e2d992 --- /dev/null +++ b/src/editor-v2/components/DynamicForm/Fields/AnyOf/AnyOf.tsx @@ -0,0 +1,92 @@ +import {Card, SegmentedRadioGroup, Select} from '@gravity-ui/uikit'; +import * as React from 'react'; + +import {AnyOfInput, DynamicFormValue} from '../../../../../common/types'; +import {editorCn} from '../../../../utils/cn'; +import DynamicForm from '../../DynamicForm'; +import FieldBase from '../../FieldBase/FieldBase'; + +import './AnyOf.scss'; + +const b = editorCn('anyof-dynamic-field'); + +interface AnyOfDynamicFieldProps { + contentConfig: DynamicFormValue; + onUpdate: (key: string, value: DynamicFormValue) => void; + inputConfig: AnyOfInput; + className?: string; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const getAnyOfContentConfig = (contentConfig: any, name: string) => { + if (name) { + return contentConfig ? contentConfig[name] : {}; + } + return contentConfig; +}; + +const AnyOfDynamicField = ({ + contentConfig, + onUpdate, + className, + inputConfig, +}: AnyOfDynamicFieldProps) => { + const defaultValue = inputConfig.options[0].value; + + const [anyOfMetaValue, setAnyOfMetaValue] = React.useState(defaultValue); + + const anyOfContentConfig = getAnyOfContentConfig(contentConfig, inputConfig.name); + const anyOfChosenOption = React.useMemo( + () => + inputConfig.options.find( + ({value: foundAnyOfValue}) => foundAnyOfValue === anyOfMetaValue, + ), + [inputConfig.options, anyOfMetaValue], + ); + + const onUpdateAnyOf = React.useCallback((value: string) => { + setAnyOfMetaValue(value); + }, []); + + return ( + <FieldBase + title={inputConfig.title} + className={b(null, className)} + onRefresh={(value) => onUpdate('', value)} + expandable + > + <Card> + {inputConfig.options.length < 4 ? ( + <SegmentedRadioGroup + className={b('radio')} + options={inputConfig.options.map((option) => ({ + content: option.title, + value: option.value, + }))} + value={anyOfMetaValue} + onUpdate={onUpdateAnyOf} + /> + ) : ( + <Select + options={inputConfig.options.map((option) => ({ + content: option.title, + value: option.value, + }))} + value={anyOfMetaValue ? [anyOfMetaValue] : []} + onUpdate={([selectValue]) => onUpdateAnyOf(selectValue)} + className={b('select')} + /> + )} + {anyOfChosenOption && ( + <DynamicForm + blockConfig={anyOfChosenOption.properties} + contentConfig={anyOfContentConfig} + onUpdate={onUpdate} + /> + )} + </Card> + </FieldBase> + ); +}; + +export default AnyOfDynamicField; diff --git a/src/utils/form-generator.ts b/src/utils/form-generator.ts index 9c8af8ca01..c34993f8b3 100644 --- a/src/utils/form-generator.ts +++ b/src/utils/form-generator.ts @@ -68,6 +68,34 @@ export const generateSingleEntity = (key: string, schema: JSONSchemaType<{}>) => }; } + if (schema.anyOf) { + return { + type: 'anyOf', + name: key, + title: key, + options: schema.anyOf.map((item: JSONSchemaType<{}>) => { + let properties; + if (item.properties) { + properties = generateFromAJV(item); + } else { + properties = [ + generateSingleEntity('', { + ...item, + name: '', + title: item.optionName, + }), + ]; + } + + return { + value: item.optionName, + title: item.optionName, + properties: properties, + }; + }), + }; + } + switch (type) { case 'string': { if (schema.inputType === 'textarea') { From a8bc1816dd173f7ed7472fcbe6a2093f16d6be88 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 28 Apr 2025 20:38:59 +0300 Subject: [PATCH 059/118] fix: navigation item props --- src/navigation/schema.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/navigation/schema.ts b/src/navigation/schema.ts index 5c809d633e..7c2b80f1be 100644 --- a/src/navigation/schema.ts +++ b/src/navigation/schema.ts @@ -86,9 +86,18 @@ const NavigationDropdownItemProps = { const NavigationItemProps = { oneOf: [ - filteredArray(NavigationLinkItemProps), - filteredArray(NavigationButtonItemProps), - filteredArray(NavigationDropdownItemProps), + { + optionName: 'link', + ...filteredArray(NavigationLinkItemProps), + }, + { + optionName: 'button', + ...filteredArray(NavigationButtonItemProps), + }, + { + optionName: 'dropdown', + ...filteredArray(NavigationDropdownItemProps), + }, ], }; From 1519e0986ef206f8bd7ca29cd1d0c050d7ced06c Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Tue, 29 Apr 2025 19:19:55 +0300 Subject: [PATCH 060/118] feat: add previewMode for editor --- src/common/store.ts | 2 + .../PageConstructor/PageConstructor.tsx | 17 ++++++- .../containers/MiddleScreen/MiddleScreen.scss | 34 +++++++++++++ .../containers/MiddleScreen/MiddleScreen.tsx | 50 +++++++++++++------ .../containers/ViewSwitches/ViewSwitches.tsx | 22 ++++++-- src/editor-v2/store.ts | 4 ++ src/hooks/usePCEditorBlockMouseEvents.ts | 1 + 7 files changed, 111 insertions(+), 19 deletions(-) diff --git a/src/common/store.ts b/src/common/store.ts index 1b8e37a709..68e978a5d5 100644 --- a/src/common/store.ts +++ b/src/common/store.ts @@ -13,6 +13,7 @@ export interface EditorState { manipulateOverlayMode: 'insert' | 'reorder' | false; selectedBlock: number[] | null; initialized: boolean; + isPreviewMode: boolean; content: PageContentWithNavigation; blocks: Array<ItemConfig>; @@ -30,6 +31,7 @@ export const initialStore: EditorState = { manipulateOverlayMode: false, selectedBlock: null, initialized: false, + isPreviewMode: false, content: {blocks: []}, blocks: [], subBlocks: [], diff --git a/src/containers/PageConstructor/PageConstructor.tsx b/src/containers/PageConstructor/PageConstructor.tsx index 6d308eae71..89c8d9fd68 100644 --- a/src/containers/PageConstructor/PageConstructor.tsx +++ b/src/containers/PageConstructor/PageConstructor.tsx @@ -71,7 +71,7 @@ export const Constructor = (props: PageConstructorProps) => { const theme = useTheme(); const store = usePCEditorStore(); - const {initialized} = store; + const {initialized, isPreviewMode} = store; usePCEditorInitializeEvents({initialContent: {blocks, background, navigation}, setContent}); @@ -108,6 +108,21 @@ export const Constructor = (props: PageConstructorProps) => { const restBlocks = content.blocks; const themedBackground = getThemedValue(content.background, theme); + // disable click events + React.useEffect(() => { + if (initialized && !isPreviewMode) { + const handler: React.EventHandler<any> = (e) => { + e?.preventDefault(); + const blockElement = e.target.closest('[data-editor-item]'); + blockElement.click(e); + }; + document.body.addEventListener('click', handler); + return () => { + document.body.removeEventListener('click', handler); + }; + } + }, [initialized, isPreviewMode]); + return ( <InnerContext.Provider value={context}> <RootCn className={b('', {['with-editor']: initialized})}> diff --git a/src/editor-v2/containers/MiddleScreen/MiddleScreen.scss b/src/editor-v2/containers/MiddleScreen/MiddleScreen.scss index 0944756acc..55a03886f2 100644 --- a/src/editor-v2/containers/MiddleScreen/MiddleScreen.scss +++ b/src/editor-v2/containers/MiddleScreen/MiddleScreen.scss @@ -18,6 +18,16 @@ $block: '.#{$ns}middle-screen'; overflow-y: auto; // height: 100%; background-color: var(--g-color-text-secondary); + + &_fullscreen { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: 1000; + background-color: #000; + } } &__wrapper { @@ -36,11 +46,35 @@ $block: '.#{$ns}middle-screen'; position: relative; height: 100%; max-width: 100%; + + &_fullscreen { + overflow: hidden; + width: 100%; + height: 100%; + max-width: 100%; + } } &__iframe { display: block; margin: 0 auto; + + &_fullscreen { + width: 100%; + height: 100%; + } + } + + &__exit-preview { + position: fixed; + opacity: 0.5; + top: 16px; + right: 16px; + z-index: 1001; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; } &__overlay { diff --git a/src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx b/src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx index 5e6c8be06c..75f7a08b08 100644 --- a/src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx +++ b/src/editor-v2/containers/MiddleScreen/MiddleScreen.tsx @@ -1,4 +1,5 @@ -import {Loader} from '@gravity-ui/uikit'; +import {Xmark} from '@gravity-ui/icons'; +import {Button, Icon, Loader} from '@gravity-ui/uikit'; import * as React from 'react'; import {usePostMessageAPIListener} from '../../../common/postMessage'; @@ -17,11 +18,21 @@ interface MiddleScreenProps { } const MiddleScreen = ({className, CustomTop}: MiddleScreenProps) => { - const {zoom, initialized, deviceWidth} = useMainEditorStore(); + const {zoom, initialized, deviceWidth, isPreviewMode, togglePreviewMode} = useMainEditorStore(); const {url, setIframeElement} = React.useContext(IframeContext); const [canvasRef, setCanvasRef] = React.useState<HTMLDivElement | null>(null); const [height, setHeight] = React.useState(0); + const canvasStyle = React.useMemo( + () => ({ + transform: isPreviewMode ? 'none' : `scale(${zoom}%)`, + height: isPreviewMode ? '100%' : `${(100 / zoom) * 100}%`, + width: isPreviewMode ? '100%' : deviceWidth, + maxWidth: isPreviewMode ? '100%' : `${(100 / zoom) * 100}%`, + }), + [isPreviewMode, zoom, deviceWidth], + ); + const onResize = React.useCallback( (newHeight: number) => { setHeight(newHeight + 500); @@ -39,33 +50,42 @@ const MiddleScreen = ({className, CustomTop}: MiddleScreenProps) => { return ( <div className={b(null, className)}> - {CustomTop ? ( + {CustomTop && !isPreviewMode ? ( <div className={b('topbar')}> <CustomTop /> </div> ) : null} - <div className={b('content')}> + <div className={b('content', {fullscreen: isPreviewMode})}> <div className={b('wrapper')}> <div ref={setCanvasRef} - className={b('canvas', {hidden: !initialized})} - style={{ - transform: `scale(${zoom}%)`, - height: `${(100 / zoom) * 100}%`, - width: deviceWidth, - maxWidth: `${(100 / zoom) * 100}%`, - }} + className={b('canvas', {hidden: !initialized, fullscreen: isPreviewMode})} + style={canvasStyle} > <iframe ref={(instance) => instance && setIframeElement(instance)} - className={b('iframe')} + className={b('iframe', {fullscreen: isPreviewMode})} src={url} - height={`${height}px`} - width={deviceWidth} + height={isPreviewMode ? '100%' : `${height}px`} + width={isPreviewMode ? '100%' : deviceWidth} frameBorder="0" title="Page Constructor Iframe" /> - <Overlay className={b('overlay')} canvasElement={canvasRef} /> + {!isPreviewMode && ( + <Overlay className={b('overlay')} canvasElement={canvasRef} /> + )} + {isPreviewMode && ( + <Button + view="action" + className={b('exit-preview')} + onClick={togglePreviewMode} + aria-label="Exit preview mode" + title="Exit preview mode" + size="l" + > + <Icon size={24} data={Xmark} /> + </Button> + )} {!initialized && ( <div className={b('loading')}> <Loader size={'l'} /> diff --git a/src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx b/src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx index 51ce4471d5..a1fb322e41 100644 --- a/src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx +++ b/src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import {Display, Minus, Plus, Smartphone} from '@gravity-ui/icons'; +import {Display, Eye, Minus, Plus, Smartphone} from '@gravity-ui/icons'; import {Button, Icon, SegmentedRadioGroup, Select} from '@gravity-ui/uikit'; import {ZOOM_STEPS} from '../../constants'; @@ -47,8 +47,15 @@ const DEVICE_OPTIONS: DeviceOption[] = [ ]; const ViewSwitches: React.FC = () => { - const {zoom, setZoom, decreaseZoom, increaseZoom, deviceWidth, setDeviceWidth} = - useMainEditorStore(); + const { + zoom, + setZoom, + decreaseZoom, + increaseZoom, + deviceWidth, + setDeviceWidth, + togglePreviewMode, + } = useMainEditorStore(); // Memoize zoom options to prevent unnecessary recalculations const zoomOptions = React.useMemo( @@ -88,6 +95,15 @@ const ViewSwitches: React.FC = () => { ))} </SegmentedRadioGroup> + <Button + view="flat" + onClick={togglePreviewMode} + aria-label="Switch to preview mode" + title="Switch to preview mode" + > + <Icon data={Eye} /> + </Button> + <div className={b('zoom')} role="group" aria-label="Zoom controls"> <Button view="flat" diff --git a/src/editor-v2/store.ts b/src/editor-v2/store.ts index 54edef1dd3..eef9f2ba1c 100644 --- a/src/editor-v2/store.ts +++ b/src/editor-v2/store.ts @@ -25,6 +25,7 @@ export interface EditorMethods { setZoom(zoom: number): void; increaseZoom(): void; decreaseZoom(): void; + togglePreviewMode(): void; setConfig(data: Pick<EditorState, 'blocks' | 'subBlocks' | 'global'>): void; setContent(data: PageContentWithNavigation): void; insertBlock(path: number[], blockType: string, position?: 'prepend' | 'append'): void; @@ -79,6 +80,9 @@ export const createEditorStore = initializeStore<EditorState, EditorMethods>( } } }, + togglePreviewMode() { + set((state) => ({...state, isPreviewMode: !state.isPreviewMode})); + }, setConfig(data) { set((state) => ({...state, ...data})); }, diff --git a/src/hooks/usePCEditorBlockMouseEvents.ts b/src/hooks/usePCEditorBlockMouseEvents.ts index 8dea23f82e..1376145a46 100644 --- a/src/hooks/usePCEditorBlockMouseEvents.ts +++ b/src/hooks/usePCEditorBlockMouseEvents.ts @@ -59,6 +59,7 @@ const usePCEditorBlockMouseEvents = (arrayIndex: number[], element?: HTMLElement onMouseMove, onMouseUp, onMouseLeave, + 'data-editor-item': true, }; }; From 8dcbbd86790e9cb77aca3a9d0f35b9a7969d8055 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Tue, 29 Apr 2025 19:40:30 +0300 Subject: [PATCH 061/118] fix: linter --- src/containers/PageConstructor/PageConstructor.tsx | 1 + src/editor-v2/containers/SourceCode/SourceCode.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/containers/PageConstructor/PageConstructor.tsx b/src/containers/PageConstructor/PageConstructor.tsx index 89c8d9fd68..533af08178 100644 --- a/src/containers/PageConstructor/PageConstructor.tsx +++ b/src/containers/PageConstructor/PageConstructor.tsx @@ -121,6 +121,7 @@ export const Constructor = (props: PageConstructorProps) => { document.body.removeEventListener('click', handler); }; } + return; }, [initialized, isPreviewMode]); return ( diff --git a/src/editor-v2/containers/SourceCode/SourceCode.tsx b/src/editor-v2/containers/SourceCode/SourceCode.tsx index aaa4d22684..4abb101731 100644 --- a/src/editor-v2/containers/SourceCode/SourceCode.tsx +++ b/src/editor-v2/containers/SourceCode/SourceCode.tsx @@ -25,6 +25,7 @@ const SourceCode = ({className, format, showSelectedBlockOnly = false}: SourceCo const [isOpen, setIsOpen] = React.useState(false); const handleUpdate = (tempConfig: string) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any let object: any; try { From 4ed9bb978722eba8fc6dcc37983d86115733059d Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Tue, 29 Apr 2025 20:02:00 +0300 Subject: [PATCH 062/118] fix: linter2 --- .../PageConstructor/PageConstructor.tsx | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/containers/PageConstructor/PageConstructor.tsx b/src/containers/PageConstructor/PageConstructor.tsx index 533af08178..988fd5754c 100644 --- a/src/containers/PageConstructor/PageConstructor.tsx +++ b/src/containers/PageConstructor/PageConstructor.tsx @@ -110,18 +110,21 @@ export const Constructor = (props: PageConstructorProps) => { // disable click events React.useEffect(() => { - if (initialized && !isPreviewMode) { - const handler: React.EventHandler<any> = (e) => { - e?.preventDefault(); - const blockElement = e.target.closest('[data-editor-item]'); - blockElement.click(e); - }; - document.body.addEventListener('click', handler); - return () => { - document.body.removeEventListener('click', handler); - }; + if (!initialized || isPreviewMode) { + return; } - return; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const handler: React.EventHandler<any> = (e) => { + e?.preventDefault(); + const blockElement = e.target.closest('[data-editor-item]'); + blockElement.click(e); + }; + document.body.addEventListener('click', handler); + + // eslint-disable-next-line consistent-return + return () => { + document.body.removeEventListener('click', handler); + }; }, [initialized, isPreviewMode]); return ( From e59a37bc22ab2223100f06201a313194e5f452c4 Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Mon, 5 May 2025 10:46:39 +0300 Subject: [PATCH 063/118] fix: filter editor blocks --- src/constructor-items.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/constructor-items.ts b/src/constructor-items.ts index 68946845e6..97f32f5a61 100644 --- a/src/constructor-items.ts +++ b/src/constructor-items.ts @@ -28,7 +28,7 @@ import CardLayoutBlockConfig from './blocks/CardLayout'; import CompaniesBlockConfig from './blocks/Companies'; import ContentLayoutBlockConfig from './blocks/ContentLayout'; import ExtendedFeaturesBlockConfig from './blocks/ExtendedFeatures'; -import FilterBlockConfig from './blocks/FilterBlock'; +// import FilterBlockConfig from './blocks/FilterBlock'; import FormBlockConfig from './blocks/Form'; import HeaderBlockConfig from './blocks/Header'; import HeaderSliderBlockConfig from './blocks/HeaderSlider'; @@ -68,7 +68,6 @@ import ImageCardConfig from './sub-blocks/ImageCard'; import LayoutItemConfig from './sub-blocks/LayoutItem'; import MediaCardConfig from './sub-blocks/MediaCard'; import PriceCardConfig from './sub-blocks/PriceCard'; -import PriceDetailedConfig from './sub-blocks/PriceDetailed'; import QuoteConfig from './sub-blocks/Quote'; /** @@ -149,12 +148,12 @@ export const blockDataMap: Record<string, BlockData> = { [BlockType.ContentLayoutBlock]: ContentLayoutBlockConfig, [BlockType.ShareBlock]: ShareBlockConfig, [BlockType.MapBlock]: MapBlockConfig, - [BlockType.FilterBlock]: FilterBlockConfig, + // TODO: fix items prop for editor compatibility + // [BlockType.FilterBlock]: FilterBlockConfig, [BlockType.FormBlock]: FormBlockConfig, [BlockType.SliderBlock]: SliderBlockConfig, [SubBlockType.Divider]: DividerConfig, - [SubBlockType.PriceDetailed]: PriceDetailedConfig, [SubBlockType.MediaCard]: MediaCardConfig, [SubBlockType.BannerCard]: BannerCardConfig, [SubBlockType.LayoutItem]: LayoutItemConfig, From d826e1f42df75744ced09dcc1862eb620e483cff Mon Sep 17 00:00:00 2001 From: qradle <qradle@yandex-team.ru> Date: Wed, 14 May 2025 10:55:31 +0300 Subject: [PATCH 064/118] fix: ui review fixes --- .../src/app/components/C9RComponent.scss | 1 + .../DynamicForm/FieldBase/FieldBase.scss | 1 - src/editor-v2/components/Tabs/Tabs.scss | 27 +++++++++++++++++-- .../BlockConfigForm/BlockConfigForm.scss | 1 - .../containers/BlocksList/BlocksList.scss | 3 ++- .../containers/GlobalConfig/GlobalConfig.scss | 1 - .../containers/ViewSwitches/ViewSwitches.tsx | 8 +++--- 7 files changed, 32 insertions(+), 10 deletions(-) diff --git a/playground/src/app/components/C9RComponent.scss b/playground/src/app/components/C9RComponent.scss index 6203e962ed..4beb537a6d 100644 --- a/playground/src/app/components/C9RComponent.scss +++ b/playground/src/app/components/C9RComponent.scss @@ -3,6 +3,7 @@ $block: '.c9r-component'; #{$block} { width: 100%; padding: 16px 12px; + box-sizing: border-box; &__header { display: flex; diff --git a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss index df3effd9de..9e7ed039d0 100644 --- a/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss +++ b/src/editor-v2/components/DynamicForm/FieldBase/FieldBase.scss @@ -6,7 +6,6 @@ $block: '.#{$ns}field-base'; #{$block} { $class: &; padding: 16px 12px 16px 0; - border-bottom: 1px solid var(--g-color-line-generic); position: relative; display: flex; diff --git a/src/editor-v2/components/Tabs/Tabs.scss b/src/editor-v2/components/Tabs/Tabs.scss index c9b6b3dd12..0029e5f67f 100644 --- a/src/editor-v2/components/Tabs/Tabs.scss +++ b/src/editor-v2/components/Tabs/Tabs.scss @@ -14,6 +14,9 @@ $block: '.#{$ns}tabs'; font-weight: 500; letter-spacing: 0.5px; cursor: pointer; + font-size: var(--g-text-code-inline-1-font-size); + line-height: var(--g-text-code-inline-2-line-height); + align-items: center; &_active { pointer-events: none; @@ -26,15 +29,35 @@ $block: '.#{$ns}tabs'; border-bottom: 1px solid var(--g-color-line-generic); display: inline-flex; flex-direction: row; - padding: 16px 4px; + padding: 12px 4px; flex: 0 0 auto; box-sizing: border-box; overflow-x: auto; + position: sticky; + top: 0; + background-color: var(--g-color-base-background); + /* This ensures the background is visible when sticky */ + z-index: 2; + /* Higher z-index for parent tabs-wrapper */ + } + + /* Make sure any nested tabs component also has proper overflow behavior */ + #{$block} { + overflow: visible; + + /* Ensure nested tabs-wrapper elements are also sticky but positioned below parent tabs-wrapper */ + /* This selector specifically targets tabs-wrapper elements inside a nested tabs component */ + #{$block} &__tabs-wrapper { + position: sticky; + top: 48.5px; + /* Position below parent tabs-wrapper */ + z-index: 1; + background-color: var(--g-color-base-background); + } } &__body { flex: 1; min-height: 1px; - overflow-y: auto; } } diff --git a/src/editor-v2/containers/BlockConfigForm/BlockConfigForm.scss b/src/editor-v2/containers/BlockConfigForm/BlockConfigForm.scss index c21fc9318b..31dab48f74 100644 --- a/src/editor-v2/containers/BlockConfigForm/BlockConfigForm.scss +++ b/src/editor-v2/containers/BlockConfigForm/BlockConfigForm.scss @@ -9,7 +9,6 @@ $padding: 12px; &__title { @include text-subheader-3; padding: $padding; - border-bottom: 1px solid var(--g-color-line-generic); } &__form { diff --git a/src/editor-v2/containers/BlocksList/BlocksList.scss b/src/editor-v2/containers/BlocksList/BlocksList.scss index 90bffb0cd5..711caa281e 100644 --- a/src/editor-v2/containers/BlocksList/BlocksList.scss +++ b/src/editor-v2/containers/BlocksList/BlocksList.scss @@ -21,6 +21,7 @@ $block: '.#{$ns}blocks-list'; justify-content: space-between; text-align: center; gap: 4px; + max-width: 192px; &:active { background-color: var(--g-color-base-generic-hover); @@ -57,7 +58,7 @@ $block: '.#{$ns}blocks-list'; &-items { display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: repeat(auto-fill, minmax(98px, 1fr)); gap: 8px; } } diff --git a/src/editor-v2/containers/GlobalConfig/GlobalConfig.scss b/src/editor-v2/containers/GlobalConfig/GlobalConfig.scss index a51239af78..d747c94685 100644 --- a/src/editor-v2/containers/GlobalConfig/GlobalConfig.scss +++ b/src/editor-v2/containers/GlobalConfig/GlobalConfig.scss @@ -8,6 +8,5 @@ $block: '.#{$ns}global-config'; padding: 12px; margin-top: 8px; @include text-subheader-3; - border-bottom: 1px solid var(--g-color-line-generic); } } diff --git a/src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx b/src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx index a1fb322e41..a5751b6427 100644 --- a/src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx +++ b/src/editor-v2/containers/ViewSwitches/ViewSwitches.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import {Display, Eye, Minus, Plus, Smartphone} from '@gravity-ui/icons'; +import {Display, Minus, Plus, Smartphone, SquareDashed} from '@gravity-ui/icons'; import {Button, Icon, SegmentedRadioGroup, Select} from '@gravity-ui/uikit'; import {ZOOM_STEPS} from '../../constants'; @@ -26,7 +26,7 @@ interface DeviceOption { * Available device viewport options * - Desktop: 100% width * - Tablet: 768px width - * - Mobile: 576px width + * - Mobile: 375px width */ const DEVICE_OPTIONS: DeviceOption[] = [ { @@ -41,7 +41,7 @@ const DEVICE_OPTIONS: DeviceOption[] = [ }, { label: <Icon width={14} data={Smartphone} />, - value: '576px', + value: '375px', ariaLabel: 'Mobile view', }, ]; @@ -101,7 +101,7 @@ const ViewSwitches: React.FC = () => { aria-label="Switch to preview mode" title="Switch to preview mode" > - <Icon data={Eye} /> + <Icon data={SquareDashed} /> </Button> <div className={b('zoom')} role="group" aria-label="Zoom controls"> From ff468286f1dc4017d95a361b9f6dd1515116a093 Mon Sep 17 00:00:00 2001 From: DaffPunks <sawavas2@gmail.com> Date: Mon, 9 Jun 2025 10:03:59 +0300 Subject: [PATCH 065/118] feat: update playground, add form builder --- package-lock.json | 1051 ++++++++++++++++- package.json | 12 +- playground/.eslintignore | 3 + playground/.gitignore | 14 +- playground/.prettierignore | 3 + playground/index.html | 20 + playground/next.config.mjs | 4 - playground/package-lock.json | 898 -------------- playground/package.json | 23 - playground/{src/app => public}/favicon.ico | Bin playground/public/manifest.json | 13 + playground/public/vite.svg | 1 + .../src/app/components/C9RComponent.scss | 31 - .../src/app/components/C9RComponent.tsx | 22 - playground/src/app/layout.tsx | 17 - playground/src/app/page.scss | 9 - playground/src/app/pc-2/page.tsx | 15 - playground/src/app/pc/page.tsx | 13 - playground/src/constants.ts | 4 - playground/src/main.tsx | 17 + playground/src/pages/editor/editor.scss | 13 + .../{app/page.tsx => pages/editor/editor.tsx} | 36 +- .../AddPropertyButton/AddPropertyButton.scss | 3 + .../AddPropertyButton/AddPropertyButton.tsx | 36 + .../ArrayFieldRenderer.scss | 29 + .../ArrayFieldRenderer/ArrayFieldRenderer.tsx | 83 ++ .../form/components/ConfigRow/ConfigRow.scss | 14 + .../form/components/ConfigRow/ConfigRow.tsx | 33 + .../form/components/FieldCard/FieldCard.scss | 17 + .../form/components/FieldCard/FieldCard.tsx | 164 +++ .../components/FieldHeader/FieldHeader.scss | 6 + .../components/FieldHeader/FieldHeader.tsx | 28 + .../components/FormBuilder/FormBuilder.scss | 110 ++ .../components/FormBuilder/FormBuilder.tsx | 94 ++ .../components/FormOutput/FormOutput.scss | 13 + .../form/components/FormOutput/FormOutput.tsx | 25 + .../ObjectFieldRenderer.scss | 8 + .../ObjectFieldRenderer.tsx | 47 + .../components/OptionHeader/OptionHeader.scss | 6 + .../components/OptionHeader/OptionHeader.tsx | 17 + .../OptionsRenderer/OptionsRenderer.scss | 38 + .../OptionsRenderer/OptionsRenderer.tsx | 110 ++ .../SectionHeader/SectionHeader.scss | 3 + .../SectionHeader/SectionHeader.tsx | 18 + .../SelectFieldRenderer.scss | 45 + .../SelectFieldRenderer.tsx | 145 +++ playground/src/pages/form/form.scss | 56 + playground/src/pages/form/form.tsx | 105 ++ .../src/pages/form/hooks/FormContext.tsx | 25 + playground/src/pages/form/hooks/types.ts | 81 ++ .../src/pages/form/hooks/useFormFields.ts | 965 +++++++++++++++ .../pc => pages/pc/example-1}/content.json | 0 .../pc-2 => pages/pc/example-2}/content.json | 0 .../pc/example-2}/navigation.json | 0 .../pc-2 => pages/pc/example-2}/styles.scss | 0 playground/src/pages/pc/pc.tsx | 43 + playground/src/router.tsx | 21 + playground/src/styles/globals.scss | 8 +- playground/src/vite-env.d.ts | 2 + playground/tsconfig.json | 40 +- playground/vite.config.mts | 26 + .../DynamicForm/Fields/Select/Select.tsx | 23 +- .../containers/MiddleScreen/MiddleScreen.tsx | 6 +- src/editor-v2/utils/index.ts | 2 +- styles/yfm.scss | 2 +- 65 files changed, 3564 insertions(+), 1152 deletions(-) create mode 100644 playground/.eslintignore create mode 100644 playground/.prettierignore create mode 100644 playground/index.html delete mode 100644 playground/next.config.mjs delete mode 100644 playground/package-lock.json delete mode 100644 playground/package.json rename playground/{src/app => public}/favicon.ico (100%) create mode 100644 playground/public/manifest.json create mode 100644 playground/public/vite.svg delete mode 100644 playground/src/app/components/C9RComponent.scss delete mode 100644 playground/src/app/components/C9RComponent.tsx delete mode 100644 playground/src/app/layout.tsx delete mode 100644 playground/src/app/page.scss delete mode 100644 playground/src/app/pc-2/page.tsx delete mode 100644 playground/src/app/pc/page.tsx delete mode 100644 playground/src/constants.ts create mode 100644 playground/src/main.tsx create mode 100644 playground/src/pages/editor/editor.scss rename playground/src/{app/page.tsx => pages/editor/editor.tsx} (52%) create mode 100644 playground/src/pages/form/components/AddPropertyButton/AddPropertyButton.scss create mode 100644 playground/src/pages/form/components/AddPropertyButton/AddPropertyButton.tsx create mode 100644 playground/src/pages/form/components/ArrayFieldRenderer/ArrayFieldRenderer.scss create mode 100644 playground/src/pages/form/components/ArrayFieldRenderer/ArrayFieldRenderer.tsx create mode 100644 playground/src/pages/form/components/ConfigRow/ConfigRow.scss create mode 100644 playground/src/pages/form/components/ConfigRow/ConfigRow.tsx create mode 100644 playground/src/pages/form/components/FieldCard/FieldCard.scss create mode 100644 playground/src/pages/form/components/FieldCard/FieldCard.tsx create mode 100644 playground/src/pages/form/components/FieldHeader/FieldHeader.scss create mode 100644 playground/src/pages/form/components/FieldHeader/FieldHeader.tsx create mode 100644 playground/src/pages/form/components/FormBuilder/FormBuilder.scss create mode 100644 playground/src/pages/form/components/FormBuilder/FormBuilder.tsx create mode 100644 playground/src/pages/form/components/FormOutput/FormOutput.scss create mode 100644 playground/src/pages/form/components/FormOutput/FormOutput.tsx create mode 100644 playground/src/pages/form/components/ObjectFieldRenderer/ObjectFieldRenderer.scss create mode 100644 playground/src/pages/form/components/ObjectFieldRenderer/ObjectFieldRenderer.tsx create mode 100644 playground/src/pages/form/components/OptionHeader/OptionHeader.scss create mode 100644 playground/src/pages/form/components/OptionHeader/OptionHeader.tsx create mode 100644 playground/src/pages/form/components/OptionsRenderer/OptionsRenderer.scss create mode 100644 playground/src/pages/form/components/OptionsRenderer/OptionsRenderer.tsx create mode 100644 playground/src/pages/form/components/SectionHeader/SectionHeader.scss create mode 100644 playground/src/pages/form/components/SectionHeader/SectionHeader.tsx create mode 100644 playground/src/pages/form/components/SelectFieldRenderer/SelectFieldRenderer.scss create mode 100644 playground/src/pages/form/components/SelectFieldRenderer/SelectFieldRenderer.tsx create mode 100644 playground/src/pages/form/form.scss create mode 100644 playground/src/pages/form/form.tsx create mode 100644 playground/src/pages/form/hooks/FormContext.tsx create mode 100644 playground/src/pages/form/hooks/types.ts create mode 100644 playground/src/pages/form/hooks/useFormFields.ts rename playground/src/{app/pc => pages/pc/example-1}/content.json (100%) rename playground/src/{app/pc-2 => pages/pc/example-2}/content.json (100%) rename playground/src/{app/pc-2 => pages/pc/example-2}/navigation.json (100%) rename playground/src/{app/pc-2 => pages/pc/example-2}/styles.scss (100%) create mode 100644 playground/src/pages/pc/pc.tsx create mode 100644 playground/src/router.tsx create mode 100644 playground/src/vite-env.d.ts create mode 100644 playground/vite.config.mts diff --git a/package-lock.json b/package-lock.json index 56b15a1a85..e679d8ef75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -81,9 +81,11 @@ "@types/uuid": "^9.0.0", "@types/webpack-env": "^1.18.1", "@types/youtube-player": "^5.5.11", + "@vitejs/plugin-react": "^4.5.0", "autoprefixer": "^10.4.14", "babel-jest": "^30.2.0", "babel-loader": "^8.3.0", + "bem-cn-lite": "^4.1.0", "css-loader": "^5.2.7", "es5-ext": "0.10.53", "esbuild": "^0.25.11", @@ -117,6 +119,7 @@ "react": "^18.3.1", "react-docgen-typescript": "^2.2.2", "react-dom": "^18.3.1", + "react-router": "^7.6.1", "resolve-url-loader": "^3.1.5", "rimraf": "^6.0.1", "sass": "^1.63.6", @@ -129,8 +132,9 @@ "ts-jest": "^29.2.5", "tslib": "^2.4.0", "typescript": "^5.7.3", + "vite": "^6.3.5", "vite-plugin-commonjs": "^0.10.1", - "vite-plugin-svgr": "^4.2.0", + "vite-plugin-svgr": "^4.3.0", "webpack": "^5.98.0", "webpack-cli": "^6.0.1", "webpack-shell-plugin-next": "^2.3.1" @@ -178,6 +182,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -227,6 +232,7 @@ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", @@ -367,6 +373,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", @@ -467,6 +474,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -501,6 +509,7 @@ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" @@ -514,6 +523,7 @@ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.28.5" }, @@ -2020,6 +2030,7 @@ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -2038,6 +2049,7 @@ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" @@ -5282,74 +5294,872 @@ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz", + "integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@playwright/experimental-ct-core": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/@playwright/experimental-ct-core/-/experimental-ct-core-1.45.3.tgz", + "integrity": "sha512-uYcWBxRPu2G2Mj2e+XUxRBzRNnG/Yz0A5DVWFewiG3qEfC92MaGYGxmzKeFeU9NcMA2fWwaqB3XWHXjn9qSM5Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.45.3", + "playwright-core": "1.45.3", + "vite": "^5.2.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 8" + "node": ">=12" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 8" + "node": ">=12" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@playwright/experimental-ct-core/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 8" + "node": ">=12" } }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "node_modules/@playwright/experimental-ct-core/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, + "hasInstallScript": true, "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, "engines": { - "node": ">=12.4.0" + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, - "node_modules/@pkgr/core": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz", - "integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==", + "node_modules/@playwright/experimental-ct-core/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/@playwright/experimental-ct-core": { - "version": "1.45.3", - "resolved": "https://registry.npmjs.org/@playwright/experimental-ct-core/-/experimental-ct-core-1.45.3.tgz", - "integrity": "sha512-uYcWBxRPu2G2Mj2e+XUxRBzRNnG/Yz0A5DVWFewiG3qEfC92MaGYGxmzKeFeU9NcMA2fWwaqB3XWHXjn9qSM5Q==", + "node_modules/@playwright/experimental-ct-core/node_modules/vite": { + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "playwright": "1.45.3", - "playwright-core": "1.45.3", - "vite": "^5.2.8" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" }, "engines": { - "node": ">=18" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } } }, "node_modules/@playwright/experimental-ct-react": { @@ -9097,6 +9907,23 @@ ], "license": "MIT" }, + "node_modules/bem-cn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bem-cn/-/bem-cn-3.0.1.tgz", + "integrity": "sha512-kWC76a09vSk6cJXDYsH1erjxdBf856HTxl0IHOvYItSmBC6wQCsRCf9bmKR0hmeUDcUP5XPMr8MNXDgKbKJi0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/bem-cn-lite": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bem-cn-lite/-/bem-cn-lite-4.1.0.tgz", + "integrity": "sha512-0IEVRYK2MQKQO00P3sY3hNv7vH8P+Z8mR46qFcaiwsQAWp0MuMWpLSuUUhZEjKD2HzTGXMqMsFysWEeeJa1drQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bem-cn": "^3.0.1" + } + }, "node_modules/better-opn": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", @@ -10057,6 +10884,16 @@ "node": ">= 10.13.0" } }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/copy-to-clipboard": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", @@ -10993,6 +11830,20 @@ "node": ">=0.10.0" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-newline": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", @@ -20810,6 +21661,14 @@ "dev": true, "license": "MIT" }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -23598,6 +24457,29 @@ "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, + "node_modules/react-router": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.1.tgz", + "integrity": "sha512-hPJXXxHJZEsPFNVbtATH7+MMX43UDeOauz+EAU4cgqTn7ojdI9qQORqS8Z0qmDlL1TclO/6jLRYUEtbWidtdHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, "node_modules/react-select": { "version": "5.10.1", "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.1.tgz", @@ -24879,6 +25761,13 @@ "dev": true, "license": "MIT" }, + "node_modules/safe-identifier": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz", + "integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==", + "dev": true, + "license": "ISC" + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -24972,13 +25861,14 @@ } }, "node_modules/sass": { - "version": "1.63.6", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.63.6.tgz", - "integrity": "sha512-MJuxGMHzaOW7ipp+1KdELtqKbfAWbH7OLIdoSMnVe3EXPMTmxTmlaZDCTsgIpPCs3w99lLo9/zDKkOrJuT5byw==", + "version": "1.89.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.1.tgz", + "integrity": "sha512-eMLLkl+qz7tx/0cJ9wI+w09GQ2zodTkcE/aVfywwdlRcI3EO19xGnbmJwg/JMIm+5MxVJ6outddLZ4Von4E++Q==", "dev": true, + "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", + "chokidar": "^4.0.0", + "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -24986,6 +25876,9 @@ }, "engines": { "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" } }, "node_modules/sass-loader": { @@ -25208,6 +26101,13 @@ "randombytes": "^2.1.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "dev": true, + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -27183,6 +28083,36 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -28242,15 +29172,18 @@ "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -28259,19 +29192,25 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", - "terser": "^5.4.0" + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, + "jiti": { + "optional": true + }, "less": { "optional": true }, @@ -28292,6 +29231,12 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, diff --git a/package.json b/package.json index 6eef5a332d..b68720c909 100644 --- a/package.json +++ b/package.json @@ -66,9 +66,7 @@ "*.scss" ], "scripts": { - "deps:install:playground": "cd playground && npm ci", - "deps:install:package": "npm ci", - "deps:install": "run-p deps:install:playground deps:install:package", + "deps:install": "npm ci", "deps:truncate": "npm prune --production", "lint:fix": "run-s lint:js:fix lint:styles:fix lint:prettier:fix typecheck", "lint:js": "eslint '**/*.{js,jsx,ts,tsx}' --max-warnings=0", @@ -80,6 +78,7 @@ "lint": "run-p lint:js lint:styles lint:prettier typecheck", "typecheck": "tsc --noEmit", "dev": "npm run storybook:start", + "dev:playground": "cd playground && vite -c vite.config.mts", "storybook:start": "storybook dev -p 7009", "storybook:build": "storybook build -c .storybook -o storybook-static", "start": "node dist", @@ -89,6 +88,7 @@ "build:widget": "webpack --config widget.webpack.js", "build:schema": "webpack --config schema.webpack.js", "build": "run-p build:client build:server build:widget build:schema", + "build:playground": "cd playground && tsc && vite build -c vite.config.mts", "prepublishOnly": "npm run lint && npm run build", "prepare": "husky install", "test": "jest", @@ -180,9 +180,11 @@ "@types/uuid": "^9.0.0", "@types/webpack-env": "^1.18.1", "@types/youtube-player": "^5.5.11", + "@vitejs/plugin-react": "^4.5.0", "autoprefixer": "^10.4.14", "babel-jest": "^30.2.0", "babel-loader": "^8.3.0", + "bem-cn-lite": "^4.1.0", "css-loader": "^5.2.7", "es5-ext": "0.10.53", "esbuild": "^0.25.11", @@ -216,6 +218,7 @@ "react": "^18.3.1", "react-docgen-typescript": "^2.2.2", "react-dom": "^18.3.1", + "react-router": "^7.6.1", "resolve-url-loader": "^3.1.5", "rimraf": "^6.0.1", "sass": "^1.63.6", @@ -228,8 +231,9 @@ "ts-jest": "^29.2.5", "tslib": "^2.4.0", "typescript": "^5.7.3", + "vite": "^6.3.5", "vite-plugin-commonjs": "^0.10.1", - "vite-plugin-svgr": "^4.2.0", + "vite-plugin-svgr": "^4.3.0", "webpack": "^5.98.0", "webpack-cli": "^6.0.1", "webpack-shell-plugin-next": "^2.3.1" diff --git a/playground/.eslintignore b/playground/.eslintignore new file mode 100644 index 0000000000..dffb6a9edf --- /dev/null +++ b/playground/.eslintignore @@ -0,0 +1,3 @@ +build +dist +public diff --git a/playground/.gitignore b/playground/.gitignore index 583394ff25..cf74115ddb 100644 --- a/playground/.gitignore +++ b/playground/.gitignore @@ -1,12 +1,16 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + # dependencies /node_modules +/.pnp +.pnp.js -# next.js -/.next/ -/out/ +# testing +/coverage # production /build +/dist # misc .DS_Store @@ -20,9 +24,5 @@ yarn-error.log* # local env files .env*.local -# vercel -.vercel - # typescript *.tsbuildinfo -next-env.d.ts diff --git a/playground/.prettierignore b/playground/.prettierignore new file mode 100644 index 0000000000..dffb6a9edf --- /dev/null +++ b/playground/.prettierignore @@ -0,0 +1,3 @@ +build +dist +public diff --git a/playground/index.html b/playground/index.html new file mode 100644 index 0000000000..be673cfe69 --- /dev/null +++ b/playground/index.html @@ -0,0 +1,20 @@ +<!doctype html> +<html lang="en"> + <head> + <meta charset="utf-8" /> + <link rel="icon" type="image/png" href="/favicon.ico" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <meta name="description" content="Web site created using vite" /> + <!-- + manifest.json provides metadata used when your web app is installed on a + user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ + --> + <link rel="manifest" href="/manifest.json" /> + <title>Page Constructor Playground + + + +
+ + + diff --git a/playground/next.config.mjs b/playground/next.config.mjs deleted file mode 100644 index 4678774e6d..0000000000 --- a/playground/next.config.mjs +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = {}; - -export default nextConfig; diff --git a/playground/package-lock.json b/playground/package-lock.json deleted file mode 100644 index 4352df2721..0000000000 --- a/playground/package-lock.json +++ /dev/null @@ -1,898 +0,0 @@ -{ - "name": "page-constructor-playground", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "page-constructor-playground", - "version": "1.0.0", - "dependencies": { - "bem-cn-lite": "^4.1.0", - "next": "15.1.2", - "react": "^19", - "react-dom": "^19" - }, - "devDependencies": { - "@types/node": "^20", - "@types/react": "^19", - "@types/react-dom": "^19", - "typescript": "^5" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", - "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", - "cpu": [ - "wasm32" - ], - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.2.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@next/env": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.2.tgz", - "integrity": "sha512-Hm3jIGsoUl6RLB1vzY+dZeqb+/kWPZ+h34yiWxW0dV87l8Im/eMOwpOA+a0L78U0HM04syEjXuRlCozqpwuojQ==" - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.2.tgz", - "integrity": "sha512-b9TN7q+j5/7+rGLhFAVZiKJGIASuo8tWvInGfAd8wsULjB1uNGRCj1z1WZwwPWzVQbIKWFYqc+9L7W09qwt52w==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.2.tgz", - "integrity": "sha512-caR62jNDUCU+qobStO6YJ05p9E+LR0EoXh1EEmyU69cYydsAy7drMcOlUlRtQihM6K6QfvNwJuLhsHcCzNpqtA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.2.tgz", - "integrity": "sha512-fHHXBusURjBmN6VBUtu6/5s7cCeEkuGAb/ZZiGHBLVBXMBy4D5QpM8P33Or8JD1nlOjm/ZT9sEE5HouQ0F+hUA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.2.tgz", - "integrity": "sha512-9CF1Pnivij7+M3G74lxr+e9h6o2YNIe7QtExWq1KUK4hsOLTBv6FJikEwCaC3NeYTflzrm69E5UfwEAbV2U9/g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.2.tgz", - "integrity": "sha512-tINV7WmcTUf4oM/eN3Yuu/f8jQ5C6AkueZPKeALs/qfdfX57eNv4Ij7rt0SA6iZ8+fMobVfcFVv664Op0caCCg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.2.tgz", - "integrity": "sha512-jf2IseC4WRsGkzeUw/cK3wci9pxR53GlLAt30+y+B+2qAQxMw6WAC3QrANIKxkcoPU3JFh/10uFfmoMDF9JXKg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.2.tgz", - "integrity": "sha512-wvg7MlfnaociP7k8lxLX4s2iBJm4BrNiNFhVUY+Yur5yhAJHfkS8qPPeDEUH8rQiY0PX3u/P7Q/wcg6Mv6GSAA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.2.tgz", - "integrity": "sha512-D3cNA8NoT3aWISWmo7HF5Eyko/0OdOO+VagkoJuiTk7pyX3P/b+n8XA/MYvyR+xSVcbKn68B1rY9fgqjNISqzQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" - }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@types/node": { - "version": "20.12.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", - "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/react": { - "version": "19.0.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.8.tgz", - "integrity": "sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==", - "dev": true, - "dependencies": { - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.3.tgz", - "integrity": "sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==", - "dev": true, - "peerDependencies": { - "@types/react": "^19.0.0" - } - }, - "node_modules/bem-cn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/bem-cn/-/bem-cn-3.0.1.tgz", - "integrity": "sha512-kWC76a09vSk6cJXDYsH1erjxdBf856HTxl0IHOvYItSmBC6wQCsRCf9bmKR0hmeUDcUP5XPMr8MNXDgKbKJi0A==" - }, - "node_modules/bem-cn-lite": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bem-cn-lite/-/bem-cn-lite-4.1.0.tgz", - "integrity": "sha512-0IEVRYK2MQKQO00P3sY3hNv7vH8P+Z8mR46qFcaiwsQAWp0MuMWpLSuUUhZEjKD2HzTGXMqMsFysWEeeJa1drQ==", - "dependencies": { - "bem-cn": "^3.0.1" - } - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001618", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001618.tgz", - "integrity": "sha512-p407+D1tIkDvsEAPS22lJxLQQaG8OTBEqo0KhzfABGk0TU4juBNDSfH0hyAp/HRyx+M8L17z/ltyhxh27FTfQg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "optional": true, - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "optional": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "optional": true - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "optional": true, - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true - }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "optional": true - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/next": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/next/-/next-15.1.2.tgz", - "integrity": "sha512-nLJDV7peNy+0oHlmY2JZjzMfJ8Aj0/dd3jCwSZS8ZiO5nkQfcZRqDrRN3U5rJtqVTQneIOGZzb6LCNrk7trMCQ==", - "dependencies": { - "@next/env": "15.1.2", - "@swc/counter": "0.1.3", - "@swc/helpers": "0.5.15", - "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", - "styled-jsx": "5.1.6" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "15.1.2", - "@next/swc-darwin-x64": "15.1.2", - "@next/swc-linux-arm64-gnu": "15.1.2", - "@next/swc-linux-arm64-musl": "15.1.2", - "@next/swc-linux-x64-gnu": "15.1.2", - "@next/swc-linux-x64-musl": "15.1.2", - "@next/swc-win32-arm64-msvc": "15.1.2", - "@next/swc-win32-x64-msvc": "15.1.2", - "sharp": "^0.33.5" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.41.2", - "babel-plugin-react-compiler": "*", - "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" - }, - "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/react": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", - "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", - "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", - "dependencies": { - "scheduler": "^0.25.0" - }, - "peerDependencies": { - "react": "^19.0.0" - } - }, - "node_modules/scheduler": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", - "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==" - }, - "node_modules/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", - "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.6.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.5", - "@img/sharp-darwin-x64": "0.33.5", - "@img/sharp-libvips-darwin-arm64": "1.0.4", - "@img/sharp-libvips-darwin-x64": "1.0.4", - "@img/sharp-libvips-linux-arm": "1.0.5", - "@img/sharp-libvips-linux-arm64": "1.0.4", - "@img/sharp-libvips-linux-s390x": "1.0.4", - "@img/sharp-libvips-linux-x64": "1.0.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", - "@img/sharp-libvips-linuxmusl-x64": "1.0.4", - "@img/sharp-linux-arm": "0.33.5", - "@img/sharp-linux-arm64": "0.33.5", - "@img/sharp-linux-s390x": "0.33.5", - "@img/sharp-linux-x64": "0.33.5", - "@img/sharp-linuxmusl-arm64": "0.33.5", - "@img/sharp-linuxmusl-x64": "0.33.5", - "@img/sharp-wasm32": "0.33.5", - "@img/sharp-win32-ia32": "0.33.5", - "@img/sharp-win32-x64": "0.33.5" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "optional": true, - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/styled-jsx": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", - "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, - "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - } - } -} diff --git a/playground/package.json b/playground/package.json deleted file mode 100644 index 76783faa5e..0000000000 --- a/playground/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "page-constructor-playground", - "version": "1.0.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint" - }, - "dependencies": { - "bem-cn-lite": "^4.1.0", - "next": "15.1.2", - "react": "^19", - "react-dom": "^19" - }, - "devDependencies": { - "@types/node": "^20", - "@types/react": "^19", - "@types/react-dom": "^19", - "typescript": "^5" - } -} diff --git a/playground/src/app/favicon.ico b/playground/public/favicon.ico similarity index 100% rename from playground/src/app/favicon.ico rename to playground/public/favicon.ico diff --git a/playground/public/manifest.json b/playground/public/manifest.json new file mode 100644 index 0000000000..c5e41e11a5 --- /dev/null +++ b/playground/public/manifest.json @@ -0,0 +1,13 @@ +{ + "short_name": "React App", + "name": "Gravity UI – Vite Example", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone" +} diff --git a/playground/public/vite.svg b/playground/public/vite.svg new file mode 100644 index 0000000000..e7b8dfb1b2 --- /dev/null +++ b/playground/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/playground/src/app/components/C9RComponent.scss b/playground/src/app/components/C9RComponent.scss deleted file mode 100644 index 4beb537a6d..0000000000 --- a/playground/src/app/components/C9RComponent.scss +++ /dev/null @@ -1,31 +0,0 @@ -$block: '.c9r-component'; - -#{$block} { - width: 100%; - padding: 16px 12px; - box-sizing: border-box; - - &__header { - display: flex; - justify-content: space-between; - align-items: flex-start; - } - - &__title { - font-size: 30px; - line-height: 28px; - font-weight: 900; - } - - &__info-bar { - margin-top: 16px; - display: flex; - justify-content: space-between; - align-items: center; - } - - &__editor-info { - font-size: 14px; - color: var(--g-color-text-secondary); - } -} diff --git a/playground/src/app/components/C9RComponent.tsx b/playground/src/app/components/C9RComponent.tsx deleted file mode 100644 index 1f4d2e7919..0000000000 --- a/playground/src/app/components/C9RComponent.tsx +++ /dev/null @@ -1,22 +0,0 @@ -'use client'; - -import block from 'bem-cn-lite'; - -import './C9RComponent.scss'; - -const b = block('c9r-component'); - -export const C9RComponent = () => { - return ( -
-
-
C9R
-
-
-
Editor alpha 1.0.0
-
-
- ); -}; - -export default C9RComponent; diff --git a/playground/src/app/layout.tsx b/playground/src/app/layout.tsx deleted file mode 100644 index 9efb03f02f..0000000000 --- a/playground/src/app/layout.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import '@gravity-ui/uikit/styles/fonts.css'; -import '@gravity-ui/uikit/styles/styles.css'; - -import {DEFAULT_BODY_CLASSNAME} from '../constants'; -import '../styles/globals.scss'; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - {children} - - ); -} diff --git a/playground/src/app/page.scss b/playground/src/app/page.scss deleted file mode 100644 index e4fa88e636..0000000000 --- a/playground/src/app/page.scss +++ /dev/null @@ -1,9 +0,0 @@ -$block: '.home'; - -#{$block} { - height: 100vh; - - &__test { - padding: 16px; - } -} diff --git a/playground/src/app/pc-2/page.tsx b/playground/src/app/pc-2/page.tsx deleted file mode 100644 index 319e2bcf8c..0000000000 --- a/playground/src/app/pc-2/page.tsx +++ /dev/null @@ -1,15 +0,0 @@ -'use client'; - -import {NavigationData, PageConstructor, PageConstructorProvider} from '../../../../src'; - -import content from './content.json'; -import navigation from './navigation.json'; -import './styles.scss'; - -export default function Home() { - return ( - - - - ); -} diff --git a/playground/src/app/pc/page.tsx b/playground/src/app/pc/page.tsx deleted file mode 100644 index 5a7dbab8e4..0000000000 --- a/playground/src/app/pc/page.tsx +++ /dev/null @@ -1,13 +0,0 @@ -'use client'; - -import {PageConstructor, PageConstructorProvider} from '../../../../src'; - -import content from './content.json'; - -export default function Home() { - return ( - - - - ); -} diff --git a/playground/src/constants.ts b/playground/src/constants.ts deleted file mode 100644 index 428aee7cb7..0000000000 --- a/playground/src/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ -import {Theme} from '@gravity-ui/uikit'; - -export const DEFAULT_THEME: Theme = 'light'; -export const DEFAULT_BODY_CLASSNAME = `g-root g-root_theme_${DEFAULT_THEME}`; diff --git a/playground/src/main.tsx b/playground/src/main.tsx new file mode 100644 index 0000000000..b87d84bd4b --- /dev/null +++ b/playground/src/main.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import ReactDOM from 'react-dom/client'; +import {BrowserRouter} from 'react-router'; +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import './styles/globals.scss'; + +import Router from './router'; + +const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); +root.render( + + + + + , +); diff --git a/playground/src/pages/editor/editor.scss b/playground/src/pages/editor/editor.scss new file mode 100644 index 0000000000..69b14306be --- /dev/null +++ b/playground/src/pages/editor/editor.scss @@ -0,0 +1,13 @@ +$block: '.editor'; + +#{$block} { + height: 100vh; + + &__other { + padding: 16px; + display: flex; + flex-direction: column; + gap: 8px; + align-items: flex-start; + } +} diff --git a/playground/src/app/page.tsx b/playground/src/pages/editor/editor.tsx similarity index 52% rename from playground/src/app/page.tsx rename to playground/src/pages/editor/editor.tsx index d3ff81a737..69196b779f 100644 --- a/playground/src/app/page.tsx +++ b/playground/src/pages/editor/editor.tsx @@ -1,35 +1,41 @@ -'use client'; -import {ThemeProvider} from '@gravity-ui/uikit'; -import block from 'bem-cn-lite'; import * as React from 'react'; +import block from 'bem-cn-lite'; +import {useNavigate} from 'react-router'; +import {Button, Text, ThemeProvider} from '@gravity-ui/uikit'; + +import {Editor} from '../../../../src/editor-v2'; -import {Editor} from '../../../src/editor-v2'; -import C9RComponent from './components/C9RComponent'; +import './editor.scss'; -import './page.scss'; +const b = block('editor'); -const b = block('home'); +const Other = () => { + const navigate = useNavigate(); -const Test = () =>
custom test
; + return ( +
+ Form Editor + +
+ ); +}; const COMPONENTS_CONFIG = { - middleTop: Test, - leftTop: [C9RComponent], leftTabs: [ { - id: 'test', - title: 'TEST', - component: Test, + id: 'other', + title: 'OTHER', + component: Other, }, ], }; -export default function Home() { +export default function EditorPage() { const [initialUrl, setInitialUrl] = React.useState(''); React.useEffect(() => { if (typeof window !== 'undefined') { - setInitialUrl(window.location.origin + '/pc'); + setInitialUrl(window.location.origin + '/?page=pc&id=1'); } }, []); diff --git a/playground/src/pages/form/components/AddPropertyButton/AddPropertyButton.scss b/playground/src/pages/form/components/AddPropertyButton/AddPropertyButton.scss new file mode 100644 index 0000000000..dfc01cd7bc --- /dev/null +++ b/playground/src/pages/form/components/AddPropertyButton/AddPropertyButton.scss @@ -0,0 +1,3 @@ +.add-property-button { + margin-top: 8px; +} diff --git a/playground/src/pages/form/components/AddPropertyButton/AddPropertyButton.tsx b/playground/src/pages/form/components/AddPropertyButton/AddPropertyButton.tsx new file mode 100644 index 0000000000..42630570fd --- /dev/null +++ b/playground/src/pages/form/components/AddPropertyButton/AddPropertyButton.tsx @@ -0,0 +1,36 @@ +import * as React from 'react'; +import {Button, DropdownMenu} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; +import {ConfigInput} from '../../../../../../src/editor-v2'; + +import './AddPropertyButton.scss'; + +const b = block('add-property-button'); + +interface AddPropertyButtonProps { + inputTypeMenuItems: Array<{action: () => void; text: string; type: string}>; + onAdd: (type: ConfigInput['type']) => void; + buttonText?: string; +} + +export const AddPropertyButton: React.FC = ({ + inputTypeMenuItems, + onAdd, + buttonText = '+ Add Property', +}) => ( +
+ ({ + ...item, + action: () => { + onAdd(item.type as ConfigInput['type']); + }, + }))} + renderSwitcher={(props) => ( + + )} + /> +
+); diff --git a/playground/src/pages/form/components/ArrayFieldRenderer/ArrayFieldRenderer.scss b/playground/src/pages/form/components/ArrayFieldRenderer/ArrayFieldRenderer.scss new file mode 100644 index 0000000000..e36cc0a332 --- /dev/null +++ b/playground/src/pages/form/components/ArrayFieldRenderer/ArrayFieldRenderer.scss @@ -0,0 +1,29 @@ +.array-field-renderer { + &__config { + display: flex; + flex-direction: column; + gap: 8px; + } + + &__config-row { + display: flex; + align-items: center; + gap: 8px; + + & > span { + min-width: 90px; + font-weight: 500; + } + + & > div { + flex: 1; + } + } + + &__nested-fields { + margin-top: 12px; + margin-left: 16px; + padding-left: 12px; + border-left: 2px solid var(--g-color-line-generic); + } +} diff --git a/playground/src/pages/form/components/ArrayFieldRenderer/ArrayFieldRenderer.tsx b/playground/src/pages/form/components/ArrayFieldRenderer/ArrayFieldRenderer.tsx new file mode 100644 index 0000000000..8da5e9011a --- /dev/null +++ b/playground/src/pages/form/components/ArrayFieldRenderer/ArrayFieldRenderer.tsx @@ -0,0 +1,83 @@ +import * as React from 'react'; +import {Button, DropdownMenu, Text} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; +import {ArrayObjectInput, ConfigInput} from '../../../../../../src/editor-v2'; +import {ConfigRow} from '../ConfigRow/ConfigRow'; +import {AddPropertyButton} from '../AddPropertyButton/AddPropertyButton'; +import {SectionHeader} from '../SectionHeader/SectionHeader'; +import {useFormContext} from '../../hooks/FormContext'; +import {FormArrayField, InputTypeMenuItem} from '../../hooks/types'; + +import './ArrayFieldRenderer.scss'; + +const b = block('array-field-renderer'); + +interface ArrayFieldRendererProps { + field: FormArrayField; + fieldName: string; + inputTypeMenuItems: InputTypeMenuItem[]; + renderNestedField: ( + field: ConfigInput, + index: number, + parentId: string, + optionIndex?: number, + ) => React.ReactNode; +} + +export const ArrayFieldRenderer: React.FC = ({ + field, + fieldName: _fieldName, + inputTypeMenuItems, + renderNestedField, +}) => { + const {addObjectProperty, updateField} = useFormContext(); + + return ( +
+ {/* Array Type Selection */} +
+ Array Type: + updateField(field.id, {arrayType: 'text'}), + text: 'Text', + }, + { + action: () => updateField(field.id, {arrayType: 'object'}), + text: 'Object', + }, + ]} + renderSwitcher={(props) => ( + + )} + /> +
+ + {/* Button Text Configuration */} + updateField(field.id, {buttonText: value})} + /> + + {/* Properties for Object Array Type */} + {field.arrayType === 'object' && ( +
+ + + {(field as ArrayObjectInput)?.properties?.map((property, index) => + renderNestedField(property, index, field.id), + )} + + addObjectProperty(field.id, type)} + /> +
+ )} +
+ ); +}; diff --git a/playground/src/pages/form/components/ConfigRow/ConfigRow.scss b/playground/src/pages/form/components/ConfigRow/ConfigRow.scss new file mode 100644 index 0000000000..320669a5bf --- /dev/null +++ b/playground/src/pages/form/components/ConfigRow/ConfigRow.scss @@ -0,0 +1,14 @@ +.config-row { + display: flex; + align-items: center; + gap: 8px; + + & > span { + min-width: 90px; + font-weight: 500; + } + + & > div { + flex: 1; + } +} diff --git a/playground/src/pages/form/components/ConfigRow/ConfigRow.tsx b/playground/src/pages/form/components/ConfigRow/ConfigRow.tsx new file mode 100644 index 0000000000..ec728dad80 --- /dev/null +++ b/playground/src/pages/form/components/ConfigRow/ConfigRow.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import {Text, TextInput} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; + +import './ConfigRow.scss'; + +const b = block('config-row'); + +interface ConfigRowProps { + label: string; + value: string; + onUpdate: (value: string) => void; +} + +const ConfigRowComponent: React.FC = ({label, value, onUpdate}) => { + // Memoize the onUpdate callback to prevent unnecessary rerenders + const handleUpdate = React.useCallback( + (newValue: string) => { + onUpdate(newValue); + }, + [onUpdate], + ); + + return ( +
+ {label}: + +
+ ); +}; + +// Use React.memo to prevent unnecessary rerenders +export const ConfigRow = React.memo(ConfigRowComponent); diff --git a/playground/src/pages/form/components/FieldCard/FieldCard.scss b/playground/src/pages/form/components/FieldCard/FieldCard.scss new file mode 100644 index 0000000000..c2a456ea77 --- /dev/null +++ b/playground/src/pages/form/components/FieldCard/FieldCard.scss @@ -0,0 +1,17 @@ +.field-card { + padding: 16px; + + &__config { + display: flex; + flex-direction: column; + gap: 8px; + } + + &__nested-field { + margin-top: 8px; + padding: 12px; + background-color: var(--g-color-base-generic); + border-radius: 6px; + border: 1px solid var(--g-color-line-generic); + } +} diff --git a/playground/src/pages/form/components/FieldCard/FieldCard.tsx b/playground/src/pages/form/components/FieldCard/FieldCard.tsx new file mode 100644 index 0000000000..1a6f33e4a1 --- /dev/null +++ b/playground/src/pages/form/components/FieldCard/FieldCard.tsx @@ -0,0 +1,164 @@ +import * as React from 'react'; +import {Card} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; +import {ConfigInput} from '../../../../../../src/editor-v2'; +import {ConfigRow} from '../ConfigRow/ConfigRow'; +import {FieldHeader} from '../FieldHeader/FieldHeader'; +import {ArrayFieldRenderer} from '../ArrayFieldRenderer/ArrayFieldRenderer'; +import {ObjectFieldRenderer} from '../ObjectFieldRenderer/ObjectFieldRenderer'; +import {OptionsRenderer} from '../OptionsRenderer/OptionsRenderer'; +import {SelectFieldRenderer} from '../SelectFieldRenderer/SelectFieldRenderer'; +import {useFormContext} from '../../hooks/FormContext'; +import { + FormAnyOfField, + FormArrayField, + FormField, + FormObjectField, + FormOneOfField, + InputTypeMenuItem, +} from '../../hooks/types'; + +import './FieldCard.scss'; + +const b = block('field-card'); + +interface FieldCardProps { + field: FormField; + inputTypeMenuItems: InputTypeMenuItem[]; +} + +export const FieldCard: React.FC = ({field, inputTypeMenuItems}) => { + const { + removeField, + updateField, + removeObjectProperty, + removeOptionProperty, + updateObjectProperty, + updateOptionProperty, + } = useFormContext(); + + // Render a nested field (for object properties and oneOf/anyOf options) + const renderNestedField = ( + nestedField: ConfigInput, + index: number, + parentId: string, + optionIndex?: number, + ) => { + const isInOption = optionIndex !== undefined; + + const handleRemove = () => { + if (isInOption) { + removeOptionProperty(parentId, optionIndex, index); + } else { + removeObjectProperty(parentId, index); + } + }; + + const handleUpdate = (updates: Partial) => { + if (isInOption) { + updateOptionProperty(parentId, optionIndex, index, updates); + } else { + updateObjectProperty(parentId, index, updates); + } + }; + + return ( +
+ + +
+ handleUpdate({name: value})} + /> + handleUpdate({title: value})} + /> + + {renderFieldTypeSpecificContent(nestedField, parentId)} +
+
+ ); + }; + + // Render field-type specific content using switch/case + const renderFieldTypeSpecificContent = (fieldConfig: ConfigInput, parentId: string) => { + switch (fieldConfig.type) { + case 'object': + return ( + + ); + + case 'oneOf': + return ( + + ); + + case 'anyOf': + return ( + + ); + + case 'select': + return ; + + case 'array': + return ( + + ); + + default: + return null; + } + }; + + return ( + + removeField(field.id)} /> + +
+ updateField(field.id, {name: value})} + /> + updateField(field.id, {title: value})} + /> + + {/* Render type-specific configuration */} + {renderFieldTypeSpecificContent(field, field.id)} +
+
+ ); +}; diff --git a/playground/src/pages/form/components/FieldHeader/FieldHeader.scss b/playground/src/pages/form/components/FieldHeader/FieldHeader.scss new file mode 100644 index 0000000000..91ce8b6469 --- /dev/null +++ b/playground/src/pages/form/components/FieldHeader/FieldHeader.scss @@ -0,0 +1,6 @@ +.field-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 12px; +} diff --git a/playground/src/pages/form/components/FieldHeader/FieldHeader.tsx b/playground/src/pages/form/components/FieldHeader/FieldHeader.tsx new file mode 100644 index 0000000000..0d74977757 --- /dev/null +++ b/playground/src/pages/form/components/FieldHeader/FieldHeader.tsx @@ -0,0 +1,28 @@ +import * as React from 'react'; +import {Button, Text} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; + +import './FieldHeader.scss'; + +const b = block('field-header'); + +interface FieldHeaderProps { + title: string; + onRemove: () => void; + variant?: 'subheader-2' | 'subheader-3'; + buttonSize?: 's' | 'xs'; +} + +export const FieldHeader: React.FC = ({ + title, + onRemove, + variant = 'subheader-2', + buttonSize = 's', +}) => ( +
+ {title} + +
+); diff --git a/playground/src/pages/form/components/FormBuilder/FormBuilder.scss b/playground/src/pages/form/components/FormBuilder/FormBuilder.scss new file mode 100644 index 0000000000..ef47824afe --- /dev/null +++ b/playground/src/pages/form/components/FormBuilder/FormBuilder.scss @@ -0,0 +1,110 @@ +.form-builder { + width: 100%; + + &__field { + margin-bottom: 16px; + } + + &__fields-list { + margin-top: 20px; + display: flex; + flex-direction: column; + gap: 12px; + } + + &__field-card { + padding: 16px; + border: 1px solid #d1d5db; + border-radius: 8px; + background-color: #ffffff; + } + + &__field-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 12px; + } + + &__field-config { + display: flex; + flex-direction: column; + gap: 8px; + } + + &__config-row { + display: flex; + align-items: center; + gap: 8px; + + & > span { + min-width: 90px; + font-weight: 500; + } + + & > div { + flex: 1; + } + } + + &__nested-fields { + margin-top: 12px; + margin-left: 16px; + padding-left: 12px; + border-left: 2px solid #e5e7eb; + } + + &__nested-field { + margin-top: 8px; + padding: 12px; + background-color: #f9fafb; + border-radius: 6px; + border: 1px solid #e5e7eb; + } + + &__nested-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + } + + &__option { + margin-top: 12px; + padding: 12px; + background-color: #f9fafb; + border-radius: 6px; + border: 1px solid #e5e7eb; + } + + &__option-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + } + + &__add-field-button { + margin-top: 8px; + } + + &__enum-option { + margin-top: 8px; + padding: 12px; + background-color: #f3f4f6; + border-radius: 6px; + border: 1px solid #e5e7eb; + display: flex; + flex-direction: column; + gap: 8px; + + .forms__config-row { + margin-bottom: 4px; + } + + button { + align-self: flex-end; + margin-top: 4px; + } + } +} diff --git a/playground/src/pages/form/components/FormBuilder/FormBuilder.tsx b/playground/src/pages/form/components/FormBuilder/FormBuilder.tsx new file mode 100644 index 0000000000..fc0b661ec7 --- /dev/null +++ b/playground/src/pages/form/components/FormBuilder/FormBuilder.tsx @@ -0,0 +1,94 @@ +'use client'; + +import * as React from 'react'; +import {Button, DropdownMenu} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; +import {FieldCard} from '../FieldCard/FieldCard'; +import {useFormContext} from '../../hooks/FormContext'; +import {FormField, InputTypeMenuItem} from '../../hooks/types'; + +import './FormBuilder.scss'; + +const b = block('form-builder'); + +interface FormBuilderProps { + className?: string; + formFields: Array; +} + +export const FormBuilder: React.FC = ({className, formFields}) => { + const {addField} = useFormContext(); + + const inputTypeMenuItems: InputTypeMenuItem[] = [ + { + type: 'text', + action: () => addField('text'), + text: 'Text Input', + }, + { + type: 'number', + action: () => addField('number'), + text: 'Number Input', + }, + { + type: 'boolean', + action: () => addField('boolean'), + text: 'Boolean Input', + }, + { + type: 'textarea', + action: () => addField('textarea'), + text: 'Textarea Input', + }, + { + type: 'select', + action: () => addField('select'), + text: 'Select Input', + }, + { + type: 'object', + action: () => addField('object'), + text: 'Object Input', + }, + { + type: 'array', + action: () => addField('array'), + text: 'Array Input', + }, + { + type: 'oneOf', + action: () => addField('oneOf'), + text: 'OneOf Input', + }, + { + type: 'anyOf', + action: () => addField('anyOf'), + text: 'AnyOf Input', + }, + ]; + + return ( +
+
+ ( + + )} + /> +
+ +
+ {formFields.map((field) => ( + + ))} +
+
+ ); +}; diff --git a/playground/src/pages/form/components/FormOutput/FormOutput.scss b/playground/src/pages/form/components/FormOutput/FormOutput.scss new file mode 100644 index 0000000000..0fea24c8fd --- /dev/null +++ b/playground/src/pages/form/components/FormOutput/FormOutput.scss @@ -0,0 +1,13 @@ +.form-output { + margin-top: 24px; + + &__content { + background-color: var(--g-color-base-generic); + padding: 12px; + border-radius: 6px; + border: 1px solid var(--g-color-line-generic); + font-family: monospace; + font-size: 12px; + white-space: pre-wrap; + } +} diff --git a/playground/src/pages/form/components/FormOutput/FormOutput.tsx b/playground/src/pages/form/components/FormOutput/FormOutput.tsx new file mode 100644 index 0000000000..fc777d563b --- /dev/null +++ b/playground/src/pages/form/components/FormOutput/FormOutput.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import {Text} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; + +import './FormOutput.scss'; + +const b = block('form-output'); + +interface FormOutputProps { + title: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data: any; + className?: string; +} + +export const FormOutput: React.FC = ({title, data, className}) => { + return ( +
+ + {title} + +
{JSON.stringify(data, null, 2)}
+
+ ); +}; diff --git a/playground/src/pages/form/components/ObjectFieldRenderer/ObjectFieldRenderer.scss b/playground/src/pages/form/components/ObjectFieldRenderer/ObjectFieldRenderer.scss new file mode 100644 index 0000000000..bcf9aea3b4 --- /dev/null +++ b/playground/src/pages/form/components/ObjectFieldRenderer/ObjectFieldRenderer.scss @@ -0,0 +1,8 @@ +.object-field-renderer { + &__nested-fields { + margin-top: 12px; + margin-left: 16px; + padding-left: 12px; + border-left: 2px solid var(--g-color-line-generic); + } +} diff --git a/playground/src/pages/form/components/ObjectFieldRenderer/ObjectFieldRenderer.tsx b/playground/src/pages/form/components/ObjectFieldRenderer/ObjectFieldRenderer.tsx new file mode 100644 index 0000000000..3f8105c3be --- /dev/null +++ b/playground/src/pages/form/components/ObjectFieldRenderer/ObjectFieldRenderer.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; +import block from 'bem-cn-lite'; +import {ConfigInput} from '../../../../../../src/editor-v2'; +import {AddPropertyButton} from '../AddPropertyButton/AddPropertyButton'; +import {SectionHeader} from '../SectionHeader/SectionHeader'; +import {useFormContext} from '../../hooks/FormContext'; +import {FormObjectField, InputTypeMenuItem} from '../../hooks/types'; + +import './ObjectFieldRenderer.scss'; + +const b = block('object-field-renderer'); + +interface ObjectFieldRendererProps { + field: FormObjectField; + fieldName: string; + inputTypeMenuItems: InputTypeMenuItem[]; + renderNestedField: ( + field: ConfigInput, + index: number, + parentId: string, + optionIndex?: number, + ) => React.ReactNode; +} + +export const ObjectFieldRenderer: React.FC = ({ + field, + fieldName: _fieldName, + inputTypeMenuItems, + renderNestedField, +}) => { + const {addObjectProperty} = useFormContext(); + + return ( +
+ + + {field.properties.map((property, index) => + renderNestedField(property, index, field.id), + )} + + addObjectProperty(field.id, type)} + /> +
+ ); +}; diff --git a/playground/src/pages/form/components/OptionHeader/OptionHeader.scss b/playground/src/pages/form/components/OptionHeader/OptionHeader.scss new file mode 100644 index 0000000000..8a29db70c5 --- /dev/null +++ b/playground/src/pages/form/components/OptionHeader/OptionHeader.scss @@ -0,0 +1,6 @@ +.option-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; +} diff --git a/playground/src/pages/form/components/OptionHeader/OptionHeader.tsx b/playground/src/pages/form/components/OptionHeader/OptionHeader.tsx new file mode 100644 index 0000000000..0b4a444f4c --- /dev/null +++ b/playground/src/pages/form/components/OptionHeader/OptionHeader.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import {Text} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; + +import './OptionHeader.scss'; + +const b = block('option-header'); + +interface OptionHeaderProps { + title: string; +} + +export const OptionHeader: React.FC = ({title}) => ( +
+ {title} +
+); diff --git a/playground/src/pages/form/components/OptionsRenderer/OptionsRenderer.scss b/playground/src/pages/form/components/OptionsRenderer/OptionsRenderer.scss new file mode 100644 index 0000000000..c70d164619 --- /dev/null +++ b/playground/src/pages/form/components/OptionsRenderer/OptionsRenderer.scss @@ -0,0 +1,38 @@ +.options-renderer { + &__header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + } + + &__option-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + } + + &__add-option-button { + margin-left: 8px; + } + + &__remove-option-button { + margin-left: 8px; + } + + &__nested-fields { + margin-top: 12px; + margin-left: 16px; + padding-left: 12px; + border-left: 2px solid var(--g-color-line-generic); + } + + &__option { + margin-top: 12px; + padding: 12px; + background-color: var(--g-color-base-background); + border-radius: 6px; + border: 1px solid var(--g-color-line-generic); + } +} diff --git a/playground/src/pages/form/components/OptionsRenderer/OptionsRenderer.tsx b/playground/src/pages/form/components/OptionsRenderer/OptionsRenderer.tsx new file mode 100644 index 0000000000..2081aa4d27 --- /dev/null +++ b/playground/src/pages/form/components/OptionsRenderer/OptionsRenderer.tsx @@ -0,0 +1,110 @@ +import * as React from 'react'; +import block from 'bem-cn-lite'; +import {Button} from '@gravity-ui/uikit'; +import {ConfigInput} from '../../../../../../src/editor-v2'; +import {AddPropertyButton} from '../AddPropertyButton/AddPropertyButton'; +import {OptionHeader} from '../OptionHeader/OptionHeader'; +import {SectionHeader} from '../SectionHeader/SectionHeader'; +import {useFormContext} from '../../hooks/FormContext'; +import {FormOptionsField, InputTypeMenuItem} from '../../hooks/types'; + +import './OptionsRenderer.scss'; + +const b = block('options-renderer'); + +interface OptionsRendererProps { + field: FormOptionsField; + fieldName: string; + inputTypeMenuItems: InputTypeMenuItem[]; + renderNestedField: ( + field: ConfigInput, + index: number, + parentId: string, + optionIndex?: number, + ) => React.ReactNode; +} + +export const OptionsRenderer: React.FC = ({ + field, + fieldName: _fieldName, + inputTypeMenuItems, + renderNestedField, +}) => { + const {addOptionProperty, addOption, removeOption} = useFormContext(); + + // Отладочный вывод для проверки поля + console.log('OptionsRenderer rendering field:', field); + console.log('OptionsRenderer options:', field.options); + + return ( +
+
+ + +
+ + {field.options.map((option, optionIndex) => ( +
+
+ + {field.options.length > 1 && ( + + )} +
+ +
+ + + {option.properties.map((property, propIndex) => + renderNestedField(property, propIndex, field.id, optionIndex), + )} + + { + console.log('AddPropertyButton onAdd called with:', { + fieldId: field.id, + optionIndex, + isOneOf: field.type === 'oneOf', + type, + currentProperties: option.properties, + }); + addOptionProperty( + field.id, + optionIndex, + field.type === 'oneOf', + type, + ); + }} + /> +
+
+ ))} +
+ ); +}; diff --git a/playground/src/pages/form/components/SectionHeader/SectionHeader.scss b/playground/src/pages/form/components/SectionHeader/SectionHeader.scss new file mode 100644 index 0000000000..1e6fada2f3 --- /dev/null +++ b/playground/src/pages/form/components/SectionHeader/SectionHeader.scss @@ -0,0 +1,3 @@ +.section-header { + margin-bottom: 8px; +} diff --git a/playground/src/pages/form/components/SectionHeader/SectionHeader.tsx b/playground/src/pages/form/components/SectionHeader/SectionHeader.tsx new file mode 100644 index 0000000000..e90831a789 --- /dev/null +++ b/playground/src/pages/form/components/SectionHeader/SectionHeader.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import {Text} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; + +import './SectionHeader.scss'; + +const b = block('section-header'); + +interface SectionHeaderProps { + title: string; + variant?: 'body-1' | 'body-2'; +} + +export const SectionHeader: React.FC = ({title, variant = 'body-1'}) => ( +
+ {title} +
+); diff --git a/playground/src/pages/form/components/SelectFieldRenderer/SelectFieldRenderer.scss b/playground/src/pages/form/components/SelectFieldRenderer/SelectFieldRenderer.scss new file mode 100644 index 0000000000..c55dd6931f --- /dev/null +++ b/playground/src/pages/form/components/SelectFieldRenderer/SelectFieldRenderer.scss @@ -0,0 +1,45 @@ +.select-field-renderer { + &__config-row { + display: flex; + align-items: center; + gap: 8px; + + & > span { + min-width: 90px; + font-weight: 500; + } + + & > div { + flex: 1; + } + + &_vertical { + flex-direction: column; + align-items: flex-start; + } + } + + &__options-title { + margin-bottom: 8px; + } + + &__enum-option { + margin-top: 8px; + padding: 12px; + background-color: var(--g-color-base-generic-ultralight); + border-radius: 6px; + border: 1px solid var(--g-color-line-generic); + display: flex; + flex-direction: column; + gap: 8px; + } + + &__remove-button { + align-self: flex-end; + margin-top: 4px; + } + + &__add-button { + margin-top: 8px; + } +} diff --git a/playground/src/pages/form/components/SelectFieldRenderer/SelectFieldRenderer.tsx b/playground/src/pages/form/components/SelectFieldRenderer/SelectFieldRenderer.tsx new file mode 100644 index 0000000000..ec442b8195 --- /dev/null +++ b/playground/src/pages/form/components/SelectFieldRenderer/SelectFieldRenderer.tsx @@ -0,0 +1,145 @@ +import * as React from 'react'; +import {Button, DropdownMenu, Text, TextInput} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; +import {FormField} from '../../hooks/types'; +import {useFormContext} from '../../hooks/FormContext'; + +import './SelectFieldRenderer.scss'; + +const b = block('select-field-renderer'); + +interface SelectFieldRendererProps { + field: FormField; +} + +interface SelectFieldOption { + content: string; + value: string; +} + +export const SelectFieldRenderer: React.FC = ({field}) => { + const {updateField} = useFormContext(); + + // Проверяем, что поле имеет тип 'select' + if (field.type !== 'select') { + return null; + } + + // Безопасное приведение типа для доступа к специфичным полям + const view = field.view as 'select' | 'radiobutton'; + const mode = field.mode as 'single' | 'multiple'; + const enumValues = (field.enum || []) as SelectFieldOption[]; + + return ( + +
+ View: + updateField(field.id, {view: 'select'}), + text: 'Select', + }, + { + action: () => updateField(field.id, {view: 'radiobutton'}), + text: 'Radio Button', + }, + ]} + renderSwitcher={(props) => ( + + )} + /> +
+ +
+ Mode: + updateField(field.id, {mode: 'single'}), + text: 'Single', + }, + { + action: () => updateField(field.id, {mode: 'multiple'}), + text: 'Multiple', + }, + ]} + renderSwitcher={(props) => ( + + )} + /> +
+ +
+ + Options: + + {enumValues.map((option, index) => ( +
+
+ Label: + { + const newEnum = [...enumValues]; + newEnum[index] = { + ...newEnum[index], + content: value, + }; + updateField(field.id, {enum: newEnum}); + }} + size="s" + /> +
+
+ Value: + { + const newEnum = [...enumValues]; + newEnum[index] = { + ...newEnum[index], + value, + }; + updateField(field.id, {enum: newEnum}); + }} + size="s" + /> +
+ +
+ ))} + +
+
+ ); +}; diff --git a/playground/src/pages/form/form.scss b/playground/src/pages/form/form.scss new file mode 100644 index 0000000000..fe6834284f --- /dev/null +++ b/playground/src/pages/form/form.scss @@ -0,0 +1,56 @@ +$block: '.form'; + +#{$block} { + width: 100%; + height: 100vh; + overflow: hidden; + + &__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 4px; + margin-bottom: 16px; + } + + &__header-buttons { + display: flex; + gap: 8px; + } + + &__panel-group { + width: 100%; + height: 100%; + } + + &__panel-content { + padding: 20px; + height: 100%; + overflow: auto; + background-color: var(--g-color-base-background); + box-sizing: border-box; + } + + &__resize-handle { + background-color: var(--g-color-line-generic); + transition: background-color 0.2s; + + &:hover { + background-color: var(--g-color-line-generic-hover); + } + + &_horizontal { + height: 8px; + cursor: row-resize; + } + + &_vertical { + width: 8px; + cursor: col-resize; + } + } + + &__form { + margin-top: 20px; + } +} diff --git a/playground/src/pages/form/form.tsx b/playground/src/pages/form/form.tsx new file mode 100644 index 0000000000..265fa5d666 --- /dev/null +++ b/playground/src/pages/form/form.tsx @@ -0,0 +1,105 @@ +import {Button, Text, ThemeProvider} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; +import {Panel, PanelGroup, PanelResizeHandle} from 'react-resizable-panels'; +import {useNavigate} from 'react-router'; +import DynamicForm from '../../../../src/editor-v2/components/DynamicForm/DynamicForm'; +import {FormBuilder} from './components/FormBuilder/FormBuilder'; +import {FormOutput} from './components/FormOutput/FormOutput'; +import {FormProvider, useFormContext} from './hooks/FormContext'; + +import './form.scss'; + +const b = block('form'); + +// Компонент содержимого формы, использующий контекст +const FormContent = () => { + const {formFields, contentConfig, handleFormUpdate, resetForm} = useFormContext(); + + const navigate = useNavigate(); + + return ( +
+ + {/* Left Panel Group */} + + + {/* Form Builder Panel */} + +
+
+ Form Builder +
+ + +
+
+
+ +
+
+
+ + {/* Resize Handle */} + + + {/* Form Output Panel */} + +
+ + rest)} + /> +
+
+
+
+ + {/* Resize Handle */} + + + {/* Right Panel Group */} + + + {/* Form Preview Panel */} + +
+ Form Preview + +
+
+ + {/* Resize Handle */} + + + {/* Form Output Panel */} + +
+ +
+
+
+
+
+
+ ); +}; + +// Главный компонент формы, оборачивающий содержимое в провайдер +export default function FormPage() { + return ( + + + + + + ); +} diff --git a/playground/src/pages/form/hooks/FormContext.tsx b/playground/src/pages/form/hooks/FormContext.tsx new file mode 100644 index 0000000000..c71e93281e --- /dev/null +++ b/playground/src/pages/form/hooks/FormContext.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import {useFormFields} from './useFormFields'; +import {FormContextType} from './types'; + +// Создаем контекст с дефолтными значениями +export const FormContext = React.createContext({} as FormContextType); + +// Provider компонент, который будет оборачивать наше приложение +export const FormProvider: React.FC<{children: React.ReactNode}> = ({children}) => { + // Используем наш существующий хук + const formFieldsData = useFormFields(); + + return {children}; +}; + +// Кастомный хук для использования контекста +export const useFormContext = () => { + const context = React.useContext(FormContext); + + if (!context) { + throw new Error('useFormContext must be used within a FormProvider'); + } + + return context; +}; diff --git a/playground/src/pages/form/hooks/types.ts b/playground/src/pages/form/hooks/types.ts new file mode 100644 index 0000000000..24ea41838d --- /dev/null +++ b/playground/src/pages/form/hooks/types.ts @@ -0,0 +1,81 @@ +import { + AnyOfInput, + ArrayObjectInput, + ArrayTextInput, + ConfigInput, + ObjectInput, + OneOfInput, +} from '../../../../../src/editor-v2'; + +// Базовый тип для поля формы +export type FormField = ConfigInput & {id: string}; + +// Типизированные поля формы +export type FormObjectField = ObjectInput & {id: string}; +export type FormArrayField = (ArrayTextInput | ArrayObjectInput) & { + id: string; + properties?: FormField[]; +}; +export type FormOneOfField = OneOfInput & {id: string}; +export type FormAnyOfField = AnyOfInput & {id: string}; +export type FormOptionsField = (OneOfInput | AnyOfInput) & {id: string}; + +// Тип для элемента меню выбора типа поля +export interface InputTypeMenuItem { + action: () => void; + text: string; + type: string; +} + +// Тип для конфигурации контента +export interface ContentConfig { + [key: string]: unknown; +} + +// Интерфейс для методов работы с полями формы +export interface FormFieldsActions { + // Основные методы для работы с полями + createField: (type: ConfigInput['type'], name?: string) => FormField; + addField: (type: ConfigInput['type']) => void; + removeField: (fieldId: string) => void; + updateField: (fieldId: string, updates: Partial) => void; + + // Методы для работы с объектными полями + addObjectProperty: (objectId: string, type: ConfigInput['type']) => void; + removeObjectProperty: (fieldId: string, propertyIndex: number) => void; + updateObjectProperty: ( + fieldId: string, + propertyIndex: number, + updates: Partial, + ) => void; + + // Методы для работы с опциями + addOption: (fieldId: string) => void; + removeOption: (fieldId: string, optionIndex: number) => void; + addOptionProperty: ( + fieldId: string, + optionIndex: number, + isOneOf: boolean, + type?: ConfigInput['type'], + ) => void; + removeOptionProperty: (fieldId: string, optionIndex: number, propertyIndex: number) => void; + updateOptionProperty: ( + fieldId: string, + optionIndex: number, + propertyIndex: number, + updates: Partial, + ) => void; + + // Сброс формы + resetForm: () => void; +} + +// Интерфейс для данных формы +export interface FormData { + formFields: Array; + contentConfig: ContentConfig; + handleFormUpdate: (key: string, value: unknown) => void; +} + +// Полный интерфейс контекста формы +export interface FormContextType extends FormData, FormFieldsActions {} diff --git a/playground/src/pages/form/hooks/useFormFields.ts b/playground/src/pages/form/hooks/useFormFields.ts new file mode 100644 index 0000000000..59cb6e386b --- /dev/null +++ b/playground/src/pages/form/hooks/useFormFields.ts @@ -0,0 +1,965 @@ +import * as React from 'react'; +import {set} from 'lodash'; +import {AnyOfInput, ConfigInput, ObjectInput, OneOfInput} from '../../../../../src/editor-v2'; +import {ContentConfig, FormArrayField, FormField} from './types'; + +// Key for storing form data in localStorage +const FORM_STORAGE_KEY = 'formBuilder_fields'; +const FORM_ID_COUNTER_KEY = 'formBuilder_nextId'; + +export const useFormFields = () => { + // Initialize state from localStorage if available + const [formFields, setFormFields] = React.useState(() => { + try { + const savedFields = localStorage.getItem(FORM_STORAGE_KEY); + return savedFields ? JSON.parse(savedFields) : []; + } catch (error) { + console.error('Error loading form fields from localStorage:', error); + return []; + } + }); + + // Отслеживаем изменения formFields + React.useEffect(() => { + console.log('formFields updated:', formFields); + }, [formFields]); + + const [nextId, setNextId] = React.useState(() => { + try { + const savedNextId = localStorage.getItem(FORM_ID_COUNTER_KEY); + return savedNextId ? parseInt(savedNextId, 10) : 1; + } catch (error) { + console.error('Error loading next ID from localStorage:', error); + return 1; + } + }); + + const [contentConfig, setContentConfig] = React.useState({}); + + // Save form fields to localStorage whenever they change + React.useEffect(() => { + try { + localStorage.setItem(FORM_STORAGE_KEY, JSON.stringify(formFields)); + } catch (error) { + console.error('Error saving form fields to localStorage:', error); + } + }, [formFields]); + + // Save next ID to localStorage whenever it changes + React.useEffect(() => { + try { + localStorage.setItem(FORM_ID_COUNTER_KEY, nextId.toString()); + } catch (error) { + console.error('Error saving next ID to localStorage:', error); + } + }, [nextId]); + + // Helper function to set nested object properties using dot notation + /** + * Sets a value at a nested path within an object using dot notation. + * Uses lodash's set method for reliable deep property setting. + * + * @param obj - The source object to modify + * @param path - Dot-separated path to the target property + * @param value - The value to set at the specified path + * @returns The modified object + */ + const setNestedProperty = >( + obj: T, + path: string, + value: unknown, + ): T => { + return set({...obj}, path, value); + }; + + const handleFormUpdate = (key: string, value: unknown) => { + setContentConfig((prev) => { + const newConfig = JSON.parse(JSON.stringify(prev)); // Deep clone + return setNestedProperty(newConfig, key, value); + }); + }; + + // Generate a unique ID for new fields + const generateId = () => { + const id = `field_id_${nextId}`; + setNextId((prev) => prev + 1); + return id; + }; + + // Generate a unique name for new fields + const generateName = () => { + const name = `field_${nextId}`; + return name; + }; + + // Create a new field configuration based on type + const createField = (type: ConfigInput['type'], name = ''): FormField => { + console.log('createField called with:', {type, name}); + + const fieldName = name || generateName(); + const fieldId = generateId(); + let newField: FormField; + + switch (type) { + case 'text': + newField = { + type: 'text', + name: fieldName, + title: 'Text Input', + id: fieldId, + } as FormField; + break; + case 'number': + newField = { + type: 'number', + name: fieldName, + title: 'Number Input', + id: fieldId, + } as FormField; + break; + case 'boolean': + newField = { + type: 'boolean', + name: fieldName, + title: 'Boolean Input', + id: fieldId, + } as FormField; + break; + case 'textarea': + newField = { + type: 'textarea', + name: fieldName, + title: 'Textarea Input', + id: fieldId, + } as FormField; + break; + case 'select': + newField = { + type: 'select', + name: fieldName, + title: 'Select Input', + view: 'select', + mode: 'single', + enum: [ + {content: 'Option 1', value: 'option1'}, + {content: 'Option 2', value: 'option2'}, + ], + id: fieldId, + } as FormField; + break; + case 'object': + newField = { + type: 'object', + name: fieldName, + title: 'Object Input', + properties: [ + { + type: 'text', + name: 'property1', + title: 'Property 1', + id: generateId(), + } as FormField, + ], + id: fieldId, + } as FormField; + break; + case 'array': + newField = { + type: 'array', + name: fieldName, + title: 'Array Input', + buttonText: 'Add Item', + arrayType: 'text', + id: fieldId, + } as FormField; + break; + case 'oneOf': + newField = { + type: 'oneOf', + name: fieldName, + title: 'OneOf Input', + options: [ + { + title: 'Option A', + value: 'optionA', + properties: [ + { + type: 'text', + name: 'textField', + title: 'Text Field', + id: generateId(), + } as FormField, + ], + }, + { + title: 'Option B', + value: 'optionB', + properties: [ + { + type: 'number', + name: 'numberField', + title: 'Number Field', + id: generateId(), + } as FormField, + ], + }, + ], + id: fieldId, + } as FormField; + break; + case 'anyOf': + newField = { + type: 'anyOf', + name: fieldName, + title: 'AnyOf Input', + options: [ + { + title: 'Option A', + value: 'optionA', + properties: [ + { + type: 'text', + name: 'textField', + title: 'Text Field', + id: generateId(), + } as FormField, + ], + }, + { + title: 'Option B', + value: 'optionB', + properties: [ + { + type: 'boolean', + name: 'booleanField', + title: 'Boolean Field', + id: generateId(), + } as FormField, + ], + }, + ], + id: fieldId, + } as FormField; + break; + default: + return createField('text', fieldName); + } + + console.log('Created new field:', newField); + return newField; + }; + + // Helper function to update a field by ID in a nested structure + const updateFieldById = ( + fields: FormField[], + fieldId: string, + updates: Partial, + ): FormField[] => { + return fields.map((field) => { + // If this is the field we're looking for, update it + if (field.id === fieldId) { + return {...field, ...updates} as FormField; + } + + // Check if this field has properties (for object type) + if (field.type === 'object' && (field as ObjectInput).properties) { + const objectField = field as ObjectInput; + return { + ...field, + properties: updateFieldById( + objectField.properties as FormField[], + fieldId, + updates, + ), + } as FormField; + } + + // Check if this field has options (for oneOf/anyOf types) + if ( + (field.type === 'oneOf' || field.type === 'anyOf') && + (field as OneOfInput | AnyOfInput).options + ) { + const optionsField = field as OneOfInput | AnyOfInput; + return { + ...field, + options: optionsField.options.map((option) => ({ + ...option, + properties: updateFieldById( + option.properties as FormField[], + fieldId, + updates, + ), + })), + } as FormField; + } + + // Check if this field has array items with properties + if (field.type === 'array' && field.arrayType === 'object') { + const arrayField = field as FormArrayField; + return { + ...field, + properties: updateFieldById( + arrayField.properties as FormField[], + fieldId, + updates, + ), + } as FormField; + } + + return field; + }); + }; + + // Add a top-level field + const addField = (type: ConfigInput['type']) => { + const newField = createField(type); + setFormFields((prev) => [...prev, newField] as FormField[]); + }; + + // Remove a field + const removeField = (fieldId: string) => { + setFormFields((prev) => prev.filter((field) => field.id !== fieldId) as FormField[]); + }; + + // Update a field - now using the field's ID for stability and supporting nested fields + const updateField = (fieldId: string, updates: Partial) => { + setFormFields((prev) => updateFieldById(prev, fieldId, updates)); + }; + + // Helper function to add a property to an object field by ID + const addPropertyToObjectById = ( + fields: FormField[], + objectId: string, + type: ConfigInput['type'] = 'text', + ): FormField[] => { + return fields.map((field) => { + // If this is the object field we're looking for, add the property to it + if (field.id === objectId && field.type === 'object') { + const objectField = field as ObjectInput; + const propertyName = `property${(objectField.properties?.length || 0) + 1}`; + const newProperty = createField(type, propertyName); + + return { + ...field, + properties: [...(objectField.properties || []), newProperty], + } as FormField; + } + + // Check if this field has properties (for object type) + if (field.type === 'object' && (field as ObjectInput).properties) { + const objectField = field as ObjectInput; + return { + ...field, + properties: addPropertyToObjectById( + objectField.properties as FormField[], + objectId, + type, + ), + } as FormField; + } + + // Check if this field has options (for oneOf/anyOf types) + if ( + (field.type === 'oneOf' || field.type === 'anyOf') && + (field as OneOfInput | AnyOfInput).options + ) { + const optionsField = field as OneOfInput | AnyOfInput; + return { + ...field, + options: optionsField.options.map((option) => ({ + ...option, + properties: addPropertyToObjectById( + option.properties as FormField[], + objectId, + type, + ), + })), + } as FormField; + } + + // Check if this field has array items with properties + if (field.type === 'array' && field.arrayType === 'object') { + const arrayField = field as FormArrayField; + + // If this is the array field we're looking for, add the property to it + if (field.id === objectId) { + const propertyName = `property${(arrayField.properties?.length || 0) + 1}`; + const newProperty = createField(type, propertyName); + + return { + ...field, + properties: [...(arrayField.properties || []), newProperty], + } as FormField; + } + + // Otherwise, recursively search through existing properties if they exist + if (arrayField.properties && arrayField.properties.length > 0) { + return { + ...field, + properties: addPropertyToObjectById(arrayField.properties, objectId, type), + } as FormField; + } + } + + return field; + }); + }; + + // Add a field to an object's properties - now using the object's ID + const addObjectProperty = (objectId: string, type: ConfigInput['type'] = 'text') => { + setFormFields((prev) => addPropertyToObjectById(prev, objectId, type)); + }; + + // Add a field to a oneOf/anyOf option's properties + const addOptionProperty = ( + fieldId: string, + optionIndex: number, + isOneOf: boolean, + type: ConfigInput['type'] = 'text', + ) => { + console.log('addOptionProperty called with:', {fieldId, optionIndex, isOneOf, type}); + + // Функция для рекурсивного поиска и обновления поля + const addPropertyToOptionRecursive = ( + fields: FormField[], + targetFieldId: string, + targetOptionIndex: number, + ): FormField[] => { + return fields.map((field) => { + // Если это искомое поле, добавляем свойство к указанной опции + if ( + field.id === targetFieldId && + (field.type === 'oneOf' || field.type === 'anyOf') + ) { + const updatedField = JSON.parse(JSON.stringify(field)); + const options = updatedField.options; + + if ( + options && + Array.isArray(options) && + targetOptionIndex >= 0 && + targetOptionIndex < options.length && + options[targetOptionIndex].properties + ) { + // Создаем новое свойство + const propertyName = `property${options[targetOptionIndex].properties.length + 1}`; + const newProperty = createField(type, propertyName); + + // Добавляем свойство в опцию + options[targetOptionIndex].properties.push(newProperty); + } + + return updatedField; + } + + // Проверяем вложенные объекты + if (field.type === 'object' && (field as ObjectInput).properties) { + const objectField = field as ObjectInput; + return { + ...field, + properties: addPropertyToOptionRecursive( + objectField.properties as FormField[], + targetFieldId, + targetOptionIndex, + ), + } as FormField; + } + + // Проверяем вложенные oneOf/anyOf + if ( + (field.type === 'oneOf' || field.type === 'anyOf') && + (field as OneOfInput | AnyOfInput).options + ) { + const optionsField = field as OneOfInput | AnyOfInput; + return { + ...field, + options: optionsField.options.map((option) => ({ + ...option, + properties: addPropertyToOptionRecursive( + option.properties as FormField[], + targetFieldId, + targetOptionIndex, + ), + })), + } as FormField; + } + + // Проверяем массивы с объектными свойствами + if ( + field.type === 'array' && + field.arrayType === 'object' && + (field as FormArrayField).properties + ) { + const arrayField = field as FormArrayField; + return { + ...field, + properties: addPropertyToOptionRecursive( + arrayField.properties as FormField[], + targetFieldId, + targetOptionIndex, + ), + } as FormField; + } + + return field; + }); + }; + + // Применяем рекурсивную функцию ко всем полям формы + setFormFields((prev) => addPropertyToOptionRecursive(prev, fieldId, optionIndex)); + }; + + // Remove a property from an object + const removeObjectProperty = (fieldId: string, propertyIndex: number) => { + setFormFields((prev) => { + return prev.map((field) => { + if (field.id !== fieldId) return field; + + const updatedField = JSON.parse(JSON.stringify(field)); // Deep clone + const objectInput = updatedField as ObjectInput; + + // Remove the property at the specified index + objectInput.properties.splice(propertyIndex, 1); + + return updatedField; + }) as FormField[]; + }); + }; + + // Remove a property from a oneOf/anyOf option + const removeOptionProperty = (fieldId: string, optionIndex: number, propertyIndex: number) => { + // Функция для рекурсивного поиска и удаления свойства + const removePropertyFromOptionRecursive = ( + fields: FormField[], + targetFieldId: string, + targetOptionIndex: number, + targetPropertyIndex: number, + ): FormField[] => { + return fields.map((field) => { + // Если это искомое поле, удаляем свойство из указанной опции + if ( + field.id === targetFieldId && + (field.type === 'oneOf' || field.type === 'anyOf') + ) { + const updatedField = JSON.parse(JSON.stringify(field)); + const options = updatedField.options; + + if ( + options && + Array.isArray(options) && + targetOptionIndex >= 0 && + targetOptionIndex < options.length && + options[targetOptionIndex].properties && + targetPropertyIndex >= 0 && + targetPropertyIndex < options[targetOptionIndex].properties.length + ) { + // Удаляем свойство из опции + options[targetOptionIndex].properties.splice(targetPropertyIndex, 1); + } + + return updatedField; + } + + // Проверяем вложенные объекты + if (field.type === 'object' && (field as ObjectInput).properties) { + const objectField = field as ObjectInput; + return { + ...field, + properties: removePropertyFromOptionRecursive( + objectField.properties as FormField[], + targetFieldId, + targetOptionIndex, + targetPropertyIndex, + ), + } as FormField; + } + + // Проверяем вложенные oneOf/anyOf + if ( + (field.type === 'oneOf' || field.type === 'anyOf') && + (field as OneOfInput | AnyOfInput).options + ) { + const optionsField = field as OneOfInput | AnyOfInput; + return { + ...field, + options: optionsField.options.map((option) => ({ + ...option, + properties: removePropertyFromOptionRecursive( + option.properties as FormField[], + targetFieldId, + targetOptionIndex, + targetPropertyIndex, + ), + })), + } as FormField; + } + + // Проверяем массивы с объектными свойствами + if ( + field.type === 'array' && + field.arrayType === 'object' && + (field as FormArrayField).properties + ) { + const arrayField = field as FormArrayField; + return { + ...field, + properties: removePropertyFromOptionRecursive( + arrayField.properties as FormField[], + targetFieldId, + targetOptionIndex, + targetPropertyIndex, + ), + } as FormField; + } + + return field; + }); + }; + + // Применяем рекурсивную функцию ко всем полям формы + setFormFields((prev) => + removePropertyFromOptionRecursive(prev, fieldId, optionIndex, propertyIndex), + ); + }; + + // Update a property in an object + const updateObjectProperty = ( + fieldId: string, + propertyIndex: number, + updates: Partial, + ) => { + setFormFields((prev) => { + return prev.map((field) => { + if (field.id !== fieldId) return field; + + const updatedField = JSON.parse(JSON.stringify(field)); // Deep clone + const objectInput = updatedField as ObjectInput; + + // Update the property at the specified index + objectInput.properties[propertyIndex] = { + ...objectInput.properties[propertyIndex], + ...updates, + } as ConfigInput; + + return updatedField; + }) as FormField[]; + }); + }; + + // Update a property in a oneOf/anyOf option + const updateOptionProperty = ( + fieldId: string, + optionIndex: number, + propertyIndex: number, + updates: Partial, + ) => { + // Функция для рекурсивного поиска и обновления свойства + const updatePropertyInOptionRecursive = ( + fields: FormField[], + targetFieldId: string, + targetOptionIndex: number, + targetPropertyIndex: number, + propertyUpdates: Partial, + ): FormField[] => { + return fields.map((field) => { + // Если это искомое поле, обновляем свойство в указанной опции + if ( + field.id === targetFieldId && + (field.type === 'oneOf' || field.type === 'anyOf') + ) { + const updatedField = JSON.parse(JSON.stringify(field)); + const options = updatedField.options; + + if ( + options && + Array.isArray(options) && + targetOptionIndex >= 0 && + targetOptionIndex < options.length && + options[targetOptionIndex].properties && + targetPropertyIndex >= 0 && + targetPropertyIndex < options[targetOptionIndex].properties.length + ) { + // Обновляем свойство в опции + options[targetOptionIndex].properties[targetPropertyIndex] = { + ...options[targetOptionIndex].properties[targetPropertyIndex], + ...propertyUpdates, + } as ConfigInput; + } + + return updatedField; + } + + // Проверяем вложенные объекты + if (field.type === 'object' && (field as ObjectInput).properties) { + const objectField = field as ObjectInput; + return { + ...field, + properties: updatePropertyInOptionRecursive( + objectField.properties as FormField[], + targetFieldId, + targetOptionIndex, + targetPropertyIndex, + propertyUpdates, + ), + } as FormField; + } + + // Проверяем вложенные oneOf/anyOf + if ( + (field.type === 'oneOf' || field.type === 'anyOf') && + (field as OneOfInput | AnyOfInput).options + ) { + const optionsField = field as OneOfInput | AnyOfInput; + return { + ...field, + options: optionsField.options.map((option) => ({ + ...option, + properties: updatePropertyInOptionRecursive( + option.properties as FormField[], + targetFieldId, + targetOptionIndex, + targetPropertyIndex, + propertyUpdates, + ), + })), + } as FormField; + } + + // Проверяем массивы с объектными свойствами + if ( + field.type === 'array' && + field.arrayType === 'object' && + (field as FormArrayField).properties + ) { + const arrayField = field as FormArrayField; + return { + ...field, + properties: updatePropertyInOptionRecursive( + arrayField.properties as FormField[], + targetFieldId, + targetOptionIndex, + targetPropertyIndex, + propertyUpdates, + ), + } as FormField; + } + + return field; + }); + }; + + // Применяем рекурсивную функцию ко всем полям формы + setFormFields((prev) => + updatePropertyInOptionRecursive(prev, fieldId, optionIndex, propertyIndex, updates), + ); + }; + + // Add a new option to a oneOf/anyOf field + const addOption = (fieldId: string) => { + console.log('addOption called with fieldId:', fieldId); + + // Функция для рекурсивного поиска и добавления опции + const addOptionRecursive = (fields: FormField[], targetFieldId: string): FormField[] => { + return fields.map((field) => { + // Если это искомое поле, добавляем новую опцию + if ( + field.id === targetFieldId && + (field.type === 'oneOf' || field.type === 'anyOf') + ) { + const updatedField = JSON.parse(JSON.stringify(field)); + const options = updatedField.options; + + if (options && Array.isArray(options)) { + // Создаем новую опцию с дефолтным свойством + const optionLetter = String.fromCharCode(65 + options.length); // A, B, C, etc. + const newOption = { + title: `Option ${optionLetter}`, + value: `option${optionLetter.toLowerCase()}`, + properties: [ + { + type: 'text', + name: 'textField', + title: 'Text Field', + id: generateId(), + } as FormField, + ], + }; + + // Добавляем новую опцию + options.push(newOption); + } + + return updatedField; + } + + // Проверяем вложенные объекты + if (field.type === 'object' && (field as ObjectInput).properties) { + const objectField = field as ObjectInput; + return { + ...field, + properties: addOptionRecursive( + objectField.properties as FormField[], + targetFieldId, + ), + } as FormField; + } + + // Проверяем вложенные oneOf/anyOf + if ( + (field.type === 'oneOf' || field.type === 'anyOf') && + (field as OneOfInput | AnyOfInput).options + ) { + const optionsField = field as OneOfInput | AnyOfInput; + return { + ...field, + options: optionsField.options.map((option) => ({ + ...option, + properties: addOptionRecursive( + option.properties as FormField[], + targetFieldId, + ), + })), + } as FormField; + } + + // Проверяем массивы с объектными свойствами + if ( + field.type === 'array' && + field.arrayType === 'object' && + (field as FormArrayField).properties + ) { + const arrayField = field as FormArrayField; + return { + ...field, + properties: addOptionRecursive( + arrayField.properties as FormField[], + targetFieldId, + ), + } as FormField; + } + + return field; + }); + }; + + // Применяем рекурсивную функцию ко всем полям формы + setFormFields((prev) => addOptionRecursive(prev, fieldId)); + }; + + // Remove an option from a oneOf/anyOf field + const removeOption = (fieldId: string, optionIndex: number) => { + console.log('removeOption called with:', {fieldId, optionIndex}); + + // Функция для рекурсивного поиска и удаления опции + const removeOptionRecursive = ( + fields: FormField[], + targetFieldId: string, + targetOptionIndex: number, + ): FormField[] => { + return fields.map((field) => { + // Если это искомое поле, удаляем опцию + if ( + field.id === targetFieldId && + (field.type === 'oneOf' || field.type === 'anyOf') + ) { + const updatedField = JSON.parse(JSON.stringify(field)); + const options = updatedField.options; + + if ( + options && + Array.isArray(options) && + targetOptionIndex >= 0 && + targetOptionIndex < options.length + ) { + // Проверяем, что можно удалить опцию (должна остаться хотя бы одна) + if (options.length > 1) { + // Удаляем опцию по индексу + options.splice(targetOptionIndex, 1); + } + } + + return updatedField; + } + + // Проверяем вложенные объекты + if (field.type === 'object' && (field as ObjectInput).properties) { + const objectField = field as ObjectInput; + return { + ...field, + properties: removeOptionRecursive( + objectField.properties as FormField[], + targetFieldId, + targetOptionIndex, + ), + } as FormField; + } + + // Проверяем вложенные oneOf/anyOf + if ( + (field.type === 'oneOf' || field.type === 'anyOf') && + (field as OneOfInput | AnyOfInput).options + ) { + const optionsField = field as OneOfInput | AnyOfInput; + return { + ...field, + options: optionsField.options.map((option) => ({ + ...option, + properties: removeOptionRecursive( + option.properties as FormField[], + targetFieldId, + targetOptionIndex, + ), + })), + } as FormField; + } + + // Проверяем массивы с объектными свойствами + if ( + field.type === 'array' && + field.arrayType === 'object' && + (field as FormArrayField).properties + ) { + const arrayField = field as FormArrayField; + return { + ...field, + properties: removeOptionRecursive( + arrayField.properties as FormField[], + targetFieldId, + targetOptionIndex, + ), + } as FormField; + } + + return field; + }); + }; + + // Применяем рекурсивную функцию ко всем полям формы + setFormFields((prev) => removeOptionRecursive(prev, fieldId, optionIndex)); + }; + + // Reset the form + const resetForm = () => { + setFormFields([]); + setContentConfig({}); + }; + + return { + formFields, + contentConfig, + handleFormUpdate, + createField, + addField, + removeField, + updateField, + addObjectProperty, + addOptionProperty, + removeObjectProperty, + removeOptionProperty, + updateObjectProperty, + updateOptionProperty, + addOption, + removeOption, + resetForm, + }; +}; diff --git a/playground/src/app/pc/content.json b/playground/src/pages/pc/example-1/content.json similarity index 100% rename from playground/src/app/pc/content.json rename to playground/src/pages/pc/example-1/content.json diff --git a/playground/src/app/pc-2/content.json b/playground/src/pages/pc/example-2/content.json similarity index 100% rename from playground/src/app/pc-2/content.json rename to playground/src/pages/pc/example-2/content.json diff --git a/playground/src/app/pc-2/navigation.json b/playground/src/pages/pc/example-2/navigation.json similarity index 100% rename from playground/src/app/pc-2/navigation.json rename to playground/src/pages/pc/example-2/navigation.json diff --git a/playground/src/app/pc-2/styles.scss b/playground/src/pages/pc/example-2/styles.scss similarity index 100% rename from playground/src/app/pc-2/styles.scss rename to playground/src/pages/pc/example-2/styles.scss diff --git a/playground/src/pages/pc/pc.tsx b/playground/src/pages/pc/pc.tsx new file mode 100644 index 0000000000..2d46b8b2b5 --- /dev/null +++ b/playground/src/pages/pc/pc.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import {NavigationData, PageConstructor, PageConstructorProvider} from '../../../../src'; + +// Example 1 +import contentExample1 from './example-1/content.json'; + +// Example 2 +import contentExample2 from './example-2/content.json'; +import navigationExample2 from './example-2/navigation.json'; + +interface PCPageProps { + id?: string | null; +} + +export default function PCPage({id}: PCPageProps) { + const pageId = id || '1'; + + const page = React.useMemo(() => { + switch (Number(pageId)) { + case 2: + import('./example-2/styles.scss'); + return { + content: contentExample2, + navigation: navigationExample2, + }; + default: + case 1: + return { + content: contentExample1, + navigation: undefined, + }; + } + }, [pageId]); + + return ( + + + + ); +} diff --git a/playground/src/router.tsx b/playground/src/router.tsx new file mode 100644 index 0000000000..d79a7bd20e --- /dev/null +++ b/playground/src/router.tsx @@ -0,0 +1,21 @@ +import {useSearchParams} from 'react-router'; + +import EditorPage from './pages/editor/editor'; +import PCPage from './pages/pc/pc'; +import FormPage from './pages/form/form'; + +export default function Router() { + const [searchParams] = useSearchParams(); + const page = searchParams.get('page'); + const id = searchParams.get('id'); + + // Route based on query parameters + switch (page) { + case 'form': + return ; + case 'pc': + return ; + default: + return ; + } +} diff --git a/playground/src/styles/globals.scss b/playground/src/styles/globals.scss index bfbf1af4e2..5d12d09236 100644 --- a/playground/src/styles/globals.scss +++ b/playground/src/styles/globals.scss @@ -1,7 +1,7 @@ -@import '../../../styles/styles.scss'; -@import '../../../src/editor-v2/styles/root.scss'; - html, -body { +body, +#root { + height: 100%; + min-width: 320px; margin: 0; } diff --git a/playground/src/vite-env.d.ts b/playground/src/vite-env.d.ts new file mode 100644 index 0000000000..b1f45c7866 --- /dev/null +++ b/playground/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/playground/tsconfig.json b/playground/tsconfig.json index 5912c19745..8261a7284a 100644 --- a/playground/tsconfig.json +++ b/playground/tsconfig.json @@ -1,32 +1,26 @@ { - "extends": "@gravity-ui/tsconfig/tsconfig.json", + "extends": "@gravity-ui/tsconfig/tsconfig", "compilerOptions": { + "target": "es2022", + "lib": ["dom", "dom.iterable", "esnext"], + "types": ["vite-plugin-svgr/client"], "allowJs": true, - "outDir": "build/esm", + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, "module": "esnext", - "jsx": "preserve", - "baseUrl": ".", - "target": "ES2020", + "moduleResolution": "bundler", "resolveJsonModule": true, - "importHelpers": true, - "lib": ["dom", "dom.iterable", "esnext"], - "noEmit": true, - "incremental": true, "isolatedModules": true, - "plugins": [ - { - "name": "next" - } - ] + "jsx": "react-jsx", + "incremental": true, + "allowSyntheticDefaultImports": true, + "paths": { + "@/*": ["./src/*"], + "*": ["../src/internal-typings/*"] + } }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - "src/typings/*.d.ts", - ".next/types/**/*.ts", - "../src", - "../styles" - ], + "include": ["**/*.ts", "**/*.tsx", "vite.config.mts", "../src/internal-typings/*"], "exclude": ["node_modules"] } diff --git a/playground/vite.config.mts b/playground/vite.config.mts new file mode 100644 index 0000000000..bc93fe0aa5 --- /dev/null +++ b/playground/vite.config.mts @@ -0,0 +1,26 @@ +import {defineConfig} from 'vite'; +import react from '@vitejs/plugin-react'; +import svgr from 'vite-plugin-svgr'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [svgr(), react()], + optimizeDeps: { + //workaround for the problem https://github.com/vitejs/vite/issues/7719 + extensions: ['.css'], + esbuildOptions: { + plugins: [ + (await import('esbuild-sass-plugin')).sassPlugin({ + type: 'style', + }), + ], + }, + }, + css: { + preprocessorOptions: { + scss: { + silenceDeprecations: ['import', 'mixed-decls', 'global-builtin'], + }, + }, + }, +}); diff --git a/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx b/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx index 13bcbe0dcc..0f27cf0d57 100644 --- a/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx +++ b/src/editor-v2/components/DynamicForm/Fields/Select/Select.tsx @@ -12,27 +12,36 @@ type SelectInput = SelectSingleInput | SelectMultipleInput; interface SelectDynamicFieldProps extends FieldBaseParams { input: SelectInput; - value: string; - onUpdate: (value: string | undefined) => void; + value: string | string[]; + onUpdate: (value: string | string[] | undefined) => void; className?: string; } const SelectDynamicField = ({input, value, onUpdate, className}: SelectDynamicFieldProps) => { const inputView = input.view || 'radiobutton'; + const isMultiple = input.mode === 'multiple'; + const currentValue = Array.isArray(value) ? value : [value]; return ( - {inputView === 'select' && ( + {(inputView === 'select' || isMultiple) && ( ({ - content: option.title, - value: option.value, - }))} - value={oneOfMetaValue ? [oneOfMetaValue] : []} - onUpdate={([selectValue]) => onUpdateOneOf(selectValue)} - className={b('select')} - /> - )} - {oneOfChosenOption && ( - - )} - + {inputConfig.options.length < 4 ? ( + ({ + content: option.title, + value: option.value, + }))} + value={oneOfMetaValue} + onUpdate={onUpdateOneOf} + /> + ) : ( +