diff --git a/common/services/ASC.Studio.Notify/Program.cs b/common/services/ASC.Studio.Notify/Program.cs
index 17aed4d97c..7e38916590 100644
--- a/common/services/ASC.Studio.Notify/Program.cs
+++ b/common/services/ASC.Studio.Notify/Program.cs
@@ -14,7 +14,7 @@ using ASC.Web.Studio.Core.Notify;
using Autofac;
using Autofac.Extensions.DependencyInjection;
-using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
diff --git a/packages/asc-web-components/submenu/README.md b/packages/asc-web-components/submenu/README.md
new file mode 100644
index 0000000000..8b1a60882d
--- /dev/null
+++ b/packages/asc-web-components/submenu/README.md
@@ -0,0 +1,71 @@
+# Submenu
+
+### Usage
+
+```js
+import Submenu from "@appserver/components/submenu";
+```
+
+```jsx
+
+ ),
+ },
+ {
+ id: "ToggleButton",
+ name: "Toggle Button",
+ content: (
+ {}}
+ />
+ ),
+ },
+ ]}
+ startSelect={1}
+/>
+```
+
+#### Data is an array of objects with following fields:
+
+- id - unique id
+- name - header in submenu
+- content - HTML object that will be rendered under submenu
+
+##### Example:
+
+```jsx
+{
+ id: "FileInput",
+ name: "File Input",
+ content: (
+
+ ),
+},
+```
+
+### Properties
+
+| Props | Type | Required | Values | Default | Description |
+| ------------- | :-------------: | :------: | :----: | :-----: | ----------------------------------------------------------------------------- |
+| `data` | `array` | ✅ | - | - | List of elements |
+| `startSelect` | `obj`, `number` | - | - | 0 | Object from data that will be chosen first **OR** Its index in **data** array |
diff --git a/packages/asc-web-components/submenu/autoOffset.js b/packages/asc-web-components/submenu/autoOffset.js
new file mode 100644
index 0000000000..595ace8950
--- /dev/null
+++ b/packages/asc-web-components/submenu/autoOffset.js
@@ -0,0 +1,126 @@
+import { tablet } from "../utils/device";
+import DomHelpers from "../utils/domHelpers";
+
+const paddingGap = 14;
+const flexGap = 4;
+const offset = 32;
+const wrapperPadding = DomHelpers.getViewport() <= tablet ? 16 : 20;
+
+export const countAutoOffset = (data, submenuItemsRef) => {
+ const [marker, itemsAndGaps, itemOnMarker] = countParams(
+ data,
+ submenuItemsRef
+ );
+
+ if (itemOnMarker === undefined) return 0;
+ if (
+ itemOnMarker.type === "gap" &&
+ itemOnMarker !== itemsAndGaps[itemsAndGaps.length - 1]
+ )
+ return itemOnMarker.end - marker + offset - wrapperPadding;
+ if (itemOnMarker.type === "item" && marker - itemOnMarker.start < 32) {
+ return -(marker - itemOnMarker.start - offset) - wrapperPadding;
+ }
+ if (
+ itemOnMarker.type === "item" &&
+ itemOnMarker.end - marker < 7.5 &&
+ itemOnMarker !== itemsAndGaps[itemsAndGaps.length - 2]
+ ) {
+ return itemOnMarker.end - marker + offset * 2 - wrapperPadding;
+ }
+ return 0;
+};
+
+export const countAutoFocus = (itemId, data, submenuItemsRef) => {
+ const [marker, itemsAndGaps, itemOnMarker] = countParams(
+ data,
+ submenuItemsRef
+ );
+
+ const [focusedItem] = itemsAndGaps.filter((obj) => obj.id === itemId);
+ const submenuWidth = submenuItemsRef.current.offsetWidth;
+
+ if (itemOnMarker.id && focusedItem.id === itemOnMarker.id)
+ return focusedItem.end - marker;
+ if (
+ focusedItem.start < marker - submenuWidth ||
+ focusedItem.start - offset < marker - submenuWidth
+ )
+ return focusedItem.start - marker + submenuWidth - wrapperPadding - offset;
+ return 0;
+};
+
+const countParams = (data, submenuItemsRef) => {
+ const refCurrent = submenuItemsRef.current;
+
+ const texts = data.map((d) => countText(d.name));
+ const itemsAndGaps = countItemsAndGaps(texts);
+
+ const submenuWidth = refCurrent.offsetWidth;
+ const marker = refCurrent.scrollLeft + submenuWidth - wrapperPadding;
+
+ const [itemOnMarker] = itemsAndGaps.filter(
+ (obj) => obj.start < marker && marker < obj.end
+ );
+
+ return [marker, itemsAndGaps, itemOnMarker];
+};
+
+const countText = (text) => {
+ const inputText = text;
+ const font = "600 13px open sans";
+ const canvas = document.createElement("canvas");
+ const context = canvas.getContext("2d");
+ context.font = font;
+ return { id: text, width: context.measureText(inputText).width };
+};
+
+const countItemsAndGaps = (texts) => {
+ const result = [];
+
+ texts.forEach(({ id, width }) => {
+ if (!result.length)
+ result.push(
+ {
+ type: "gap",
+ length: paddingGap,
+ start: 0,
+ end: paddingGap + wrapperPadding,
+ },
+ {
+ id: id,
+ type: "item",
+ length: width,
+ start: paddingGap,
+ end: paddingGap + width,
+ }
+ );
+ else {
+ const lastItem = result[result.length - 1];
+ result.push(
+ {
+ type: "gap",
+ length: paddingGap * 2 + flexGap,
+ start: lastItem.end,
+ end: lastItem.end + paddingGap * 2 + flexGap,
+ },
+ {
+ id: id,
+ type: "item",
+ length: width,
+ start: lastItem.end + paddingGap * 2 + flexGap,
+ end: lastItem.end + paddingGap * 2 + flexGap + width,
+ }
+ );
+ }
+ });
+
+ result.push({
+ type: "gap",
+ length: paddingGap,
+ start: result[result.length - 1].end,
+ end: result[result.length - 1].end + paddingGap + wrapperPadding,
+ });
+
+ return result;
+};
diff --git a/packages/asc-web-components/submenu/data.js b/packages/asc-web-components/submenu/data.js
new file mode 100644
index 0000000000..998027aeb9
--- /dev/null
+++ b/packages/asc-web-components/submenu/data.js
@@ -0,0 +1,89 @@
+import React from "react";
+import FileInput from "@appserver/components/file-input";
+import Row from "@appserver/components/row";
+import Textarea from "@appserver/components/textarea";
+import Text from "../text";
+
+export const data = [
+ {
+ id: "Overview",
+ name: "Overview",
+ content: (
+ {}}
+ placeholder="Input file"
+ />
+ ),
+ },
+ {
+ id: "Documents",
+ name: "Documents",
+ content: (
+