Merge branch 'develop' into refactoring/global-colors
This commit is contained in:
commit
3d089d9752
@ -32,7 +32,7 @@ import { withTranslation } from "react-i18next";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
|
||||
import { Submenu } from "@docspace/shared/components/submenu";
|
||||
import { Tabs } from "@docspace/shared/components/tabs";
|
||||
import {
|
||||
isDesktop as isDesktopUtils,
|
||||
isMobile as isMobileUtils,
|
||||
@ -75,7 +75,7 @@ const InfoPanelHeaderContent = (props) => {
|
||||
selection?.isFolder && selection?.id === selection?.rootFolderId;
|
||||
const isSeveralItems = selection && Array.isArray(selection);
|
||||
|
||||
const withSubmenu =
|
||||
const withTabs =
|
||||
!isRoot && !isSeveralItems && !isGallery && !isAccounts && !isTrash;
|
||||
|
||||
useEffect(() => {
|
||||
@ -102,7 +102,7 @@ const InfoPanelHeaderContent = (props) => {
|
||||
|
||||
//const isArchiveRoot = rootFolderType === FolderType.Archive;
|
||||
|
||||
const submenuData = [
|
||||
const tabsData = [
|
||||
{
|
||||
id: "info_members",
|
||||
name: t("Common:Members"),
|
||||
@ -123,12 +123,12 @@ const InfoPanelHeaderContent = (props) => {
|
||||
},
|
||||
];
|
||||
|
||||
const roomsSubmenu = [...submenuData];
|
||||
const roomsTabs = [...tabsData];
|
||||
|
||||
const personalSubmenu = [submenuData[1], submenuData[2]];
|
||||
const personalTabs = [tabsData[1], tabsData[2]];
|
||||
|
||||
if (selection?.canShare) {
|
||||
personalSubmenu.unshift({
|
||||
personalTabs.unshift({
|
||||
id: "info_share",
|
||||
name: t("Common:Share"),
|
||||
onClick: setShare,
|
||||
@ -148,7 +148,7 @@ const InfoPanelHeaderContent = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const submenuItem = {
|
||||
const tabsItem = {
|
||||
id: `info_plugin-${item.key}`,
|
||||
name: item.value.subMenu.name,
|
||||
onClick,
|
||||
@ -156,14 +156,14 @@ const InfoPanelHeaderContent = (props) => {
|
||||
};
|
||||
|
||||
if (!item.value.filesType) {
|
||||
roomsSubmenu.push(submenuItem);
|
||||
personalSubmenu.push(submenuItem);
|
||||
roomsTabs.push(tabsItem);
|
||||
personalTabs.push(tabsItem);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRoom && item.value.filesType.includes(PluginFileType.Rooms)) {
|
||||
roomsSubmenu.push(submenuItem);
|
||||
personalSubmenu.push(submenuItem);
|
||||
roomsTabs.push(tabsItem);
|
||||
personalTabs.push(tabsItem);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -175,14 +175,14 @@ const InfoPanelHeaderContent = (props) => {
|
||||
return;
|
||||
}
|
||||
|
||||
roomsSubmenu.push(submenuItem);
|
||||
personalSubmenu.push(submenuItem);
|
||||
roomsTabs.push(tabsItem);
|
||||
personalTabs.push(tabsItem);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.value.filesType.includes(PluginFileType.Folders)) {
|
||||
roomsSubmenu.push(submenuItem);
|
||||
personalSubmenu.push(submenuItem);
|
||||
roomsTabs.push(tabsItem);
|
||||
personalTabs.push(tabsItem);
|
||||
return;
|
||||
}
|
||||
});
|
||||
@ -193,7 +193,7 @@ const InfoPanelHeaderContent = (props) => {
|
||||
selection?.rootFolderType === FolderType.Archive;
|
||||
|
||||
return (
|
||||
<StyledInfoPanelHeader isTablet={isTablet} withSubmenu={withSubmenu}>
|
||||
<StyledInfoPanelHeader isTablet={isTablet} withTabs={withTabs}>
|
||||
<div className="main">
|
||||
<Text className="header-text" fontSize="21px" fontWeight="700">
|
||||
{t("Common:Info")}
|
||||
@ -214,19 +214,19 @@ const InfoPanelHeaderContent = (props) => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{withSubmenu && (
|
||||
<div className="submenu">
|
||||
{withTabs && (
|
||||
<div className="tabs">
|
||||
{isRoomsType ? (
|
||||
<Submenu
|
||||
<Tabs
|
||||
style={{ width: "100%" }}
|
||||
data={roomsSubmenu}
|
||||
forsedActiveItemId={roomsView}
|
||||
items={roomsTabs}
|
||||
selectedItemId={roomsView}
|
||||
/>
|
||||
) : (
|
||||
<Submenu
|
||||
<Tabs
|
||||
style={{ width: "100%" }}
|
||||
data={personalSubmenu}
|
||||
forsedActiveItemId={fileView}
|
||||
items={personalTabs}
|
||||
selectedItemId={fileView}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -28,9 +28,9 @@ import styled, { css } from "styled-components";
|
||||
import { Base } from "@docspace/shared/themes";
|
||||
import { tablet } from "@docspace/shared/utils";
|
||||
|
||||
const getHeaderHeight = ({ withSubmenu, isTablet }) => {
|
||||
const getHeaderHeight = ({ withTabs, isTablet }) => {
|
||||
let res = isTablet ? 53 : 69;
|
||||
if (withSubmenu) res += 32;
|
||||
if (withTabs) res += 32;
|
||||
return `${res}px`;
|
||||
};
|
||||
|
||||
@ -53,9 +53,7 @@ const StyledInfoPanelHeader = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-bottom: ${(props) =>
|
||||
props.withSubmenu
|
||||
? "none"
|
||||
: `1px solid ${props.theme.infoPanel.borderColor}`};
|
||||
props.withTabs ? "none" : `1px solid ${props.theme.infoPanel.borderColor}`};
|
||||
.main {
|
||||
height: ${(props) => getMainHeight(props)};
|
||||
min-height: ${(props) => getMainHeight(props)};
|
||||
@ -91,16 +89,13 @@ const StyledInfoPanelHeader = styled.div`
|
||||
`}
|
||||
}
|
||||
|
||||
.submenu {
|
||||
.tabs {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
.sticky {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
.bottom-line {
|
||||
background-color: ${(props) => props.theme.infoPanel.borderColor};
|
||||
.scroll-body > div {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,11 +49,11 @@ class FilesTableHeader extends React.Component {
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
isPublicRoom,
|
||||
isFrame,
|
||||
isRecentTab,
|
||||
isDefaultRoomsQuotaSet,
|
||||
showStorageInfo,
|
||||
isArchiveFolder,
|
||||
tableStorageName,
|
||||
} = this.props;
|
||||
|
||||
const defaultColumns = [];
|
||||
@ -358,7 +358,7 @@ class FilesTableHeader extends React.Component {
|
||||
}
|
||||
|
||||
let columns = getColumns(defaultColumns);
|
||||
const storageColumns = localStorage.getItem(this.props.tableStorageName);
|
||||
const storageColumns = localStorage.getItem(tableStorageName);
|
||||
const splitColumns = storageColumns && storageColumns.split(",");
|
||||
const resetColumnsSize =
|
||||
(splitColumns && splitColumns.length !== columns.length) || !splitColumns;
|
||||
@ -370,6 +370,7 @@ class FilesTableHeader extends React.Component {
|
||||
this.setState({
|
||||
columns,
|
||||
resetColumnsSize,
|
||||
tableStorageName,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
});
|
||||
@ -377,6 +378,7 @@ class FilesTableHeader extends React.Component {
|
||||
this.state = {
|
||||
columns,
|
||||
resetColumnsSize,
|
||||
tableStorageName,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
};
|
||||
@ -541,12 +543,12 @@ class FilesTableHeader extends React.Component {
|
||||
setHideColumns,
|
||||
isFrame,
|
||||
showSettings,
|
||||
tableStorageName,
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
columns,
|
||||
resetColumnsSize,
|
||||
tableStorageName,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
} = this.state;
|
||||
|
@ -32,7 +32,6 @@ import Error520 from "../../../../components/Error520Wrapper";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { combineUrl } from "@docspace/shared/utils/combineUrl";
|
||||
import config from "PACKAGE_FILE";
|
||||
import { Submenu } from "@docspace/shared/components/submenu";
|
||||
import PersonalSettings from "./CommonSettings";
|
||||
import GeneralSettings from "./AdminSettings";
|
||||
import { tablet } from "@docspace/shared/utils";
|
||||
|
@ -30,7 +30,7 @@ import { inject, observer } from "mobx-react";
|
||||
import { SectionSubmenuSkeleton } from "@docspace/shared/skeletons/sections";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const AccountsSubmenu = ({
|
||||
const AccountsTabs = ({
|
||||
showBodyLoader,
|
||||
setPeopleSelection,
|
||||
setGroupsSelection,
|
||||
@ -62,10 +62,10 @@ const AccountsSubmenu = ({
|
||||
if (showBodyLoader) return <SectionSubmenuSkeleton />;
|
||||
|
||||
return (
|
||||
<Styled.AccountsSubmenu
|
||||
<Styled.AccountsTabs
|
||||
className="accounts-tabs"
|
||||
forsedActiveItemId={isPeople ? "people" : "groups"}
|
||||
data={[
|
||||
selectedItemId={isPeople ? "people" : "groups"}
|
||||
items={[
|
||||
{
|
||||
id: "people",
|
||||
name: t("Common:People"),
|
||||
@ -89,4 +89,4 @@ export default inject(({ peopleStore, clientLoadingStore }) => ({
|
||||
setPeopleBufferSelection: peopleStore.selectionStore.setBufferSelection,
|
||||
setGroupsSelection: peopleStore.groupsStore.setSelection,
|
||||
setGroupsBufferSelection: peopleStore.groupsStore.setBufferSelection,
|
||||
}))(observer(AccountsSubmenu));
|
||||
}))(observer(AccountsTabs));
|
@ -24,11 +24,11 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { Submenu } from "@docspace/shared/components/submenu";
|
||||
import { Tabs } from "@docspace/shared/components/tabs";
|
||||
import { tablet } from "@docspace/shared/utils/device";
|
||||
import styled from "styled-components";
|
||||
|
||||
export const AccountsSubmenu = styled(Submenu)`
|
||||
export const AccountsTabs = styled(Tabs)`
|
||||
/* width: 100%;
|
||||
|
||||
@media ${tablet} {
|
@ -27,12 +27,12 @@
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Submenu } from "@docspace/shared/components/submenu";
|
||||
import { Tabs } from "@docspace/shared/components/tabs";
|
||||
import { SectionSubmenuSkeleton } from "@docspace/shared/skeletons/sections";
|
||||
import FilesFilter from "@docspace/shared/api/files/filter";
|
||||
import { getObjectByLocation } from "@docspace/shared/utils/common";
|
||||
|
||||
const MyDocumentsSubmenu = ({
|
||||
const MyDocumentsTabs = ({
|
||||
isPersonalRoom,
|
||||
isRecentTab,
|
||||
setFilter,
|
||||
@ -41,7 +41,7 @@ const MyDocumentsSubmenu = ({
|
||||
}) => {
|
||||
const { t } = useTranslation(["Common", "Files"]);
|
||||
|
||||
const submenu = [
|
||||
const tabs = [
|
||||
{
|
||||
id: "my",
|
||||
name: t("Common:MyDocuments"),
|
||||
@ -68,14 +68,17 @@ const MyDocumentsSubmenu = ({
|
||||
window.DocSpace.navigate(`${url}?${filter.toUrlParams()}`);
|
||||
};
|
||||
|
||||
const showSubmenu = (isPersonalRoom || isRecentTab) && isRoot;
|
||||
const startSelect =
|
||||
getObjectByLocation(window.DocSpace.location)?.folder === "recent" ? 1 : 0;
|
||||
const showTabs = (isPersonalRoom || isRecentTab) && isRoot;
|
||||
const startSelectId =
|
||||
tabs.length &&
|
||||
getObjectByLocation(window.DocSpace.location)?.folder === "recent"
|
||||
? tabs[1].id
|
||||
: tabs[0].id;
|
||||
|
||||
if (showSubmenu && showBodyLoader) return <SectionSubmenuSkeleton />;
|
||||
if (showTabs && showBodyLoader) return <SectionSubmenuSkeleton />;
|
||||
|
||||
return showSubmenu ? (
|
||||
<Submenu data={submenu} startSelect={startSelect} onSelect={onSelect} />
|
||||
return showTabs ? (
|
||||
<Tabs items={tabs} selectedItemId={startSelectId} onSelect={onSelect} />
|
||||
) : null;
|
||||
};
|
||||
|
||||
@ -93,4 +96,4 @@ export default inject(
|
||||
isRoot,
|
||||
};
|
||||
},
|
||||
)(observer(MyDocumentsSubmenu));
|
||||
)(observer(MyDocumentsTabs));
|
@ -24,8 +24,8 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import AccountsSubmenu from "./AccountsSubmenu";
|
||||
import MyDocumentsSubmenu from "./MyDocumentsSubmenu";
|
||||
import AccountsTabs from "./AccountsTabs";
|
||||
import MyDocumentsTabs from "./MyDocumentsTabs";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
|
||||
@ -33,8 +33,8 @@ const SectionSubmenuContent = ({ isPersonalRoom, isRecentTab }) => {
|
||||
const location = useLocation();
|
||||
const isAccounts = location.pathname.includes("/accounts");
|
||||
|
||||
if (isPersonalRoom || isRecentTab) return <MyDocumentsSubmenu />;
|
||||
if (isAccounts) return <AccountsSubmenu />;
|
||||
if (isPersonalRoom || isRecentTab) return <MyDocumentsTabs />;
|
||||
if (isAccounts) return <AccountsTabs />;
|
||||
return null;
|
||||
};
|
||||
|
@ -31,4 +31,4 @@ export { default as SettingsSectionBodyContent } from "./SettingsBody";
|
||||
export { default as SectionFilterContent } from "./Filter";
|
||||
export { default as SectionPagingContent } from "./Paging";
|
||||
export { default as SectionWarningContent } from "./Warning";
|
||||
export { default as SectionSubmenuContent } from "./Submenu";
|
||||
export { default as SectionSubmenuContent } from "./Tabs";
|
||||
|
@ -32,7 +32,7 @@ import { inject, observer } from "mobx-react";
|
||||
import { Button } from "@docspace/shared/components/button";
|
||||
import { Tooltip } from "@docspace/shared/components/tooltip";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { TabsContainer } from "@docspace/shared/components/tabs-container";
|
||||
import { Tabs, TabsTypes } from "@docspace/shared/components/tabs";
|
||||
import Preview from "./Appearance/preview";
|
||||
import { saveToSessionStorage, getFromSessionStorage } from "../../utils";
|
||||
import ColorSchemeDialog from "./sub-components/colorSchemeDialog";
|
||||
@ -138,12 +138,11 @@ const Appearance = (props) => {
|
||||
|
||||
const [theme, setTheme] = useState(appearanceTheme);
|
||||
|
||||
const array_items = useMemo(
|
||||
const arrayItems = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: "light-theme",
|
||||
key: "0",
|
||||
title: t("Profile:LightTheme"),
|
||||
name: t("Profile:LightTheme"),
|
||||
content: (
|
||||
<Preview
|
||||
appliedColorAccent={appliedColorAccent}
|
||||
@ -156,8 +155,7 @@ const Appearance = (props) => {
|
||||
},
|
||||
{
|
||||
id: "dark-theme",
|
||||
key: "1",
|
||||
title: t("Profile:DarkTheme"),
|
||||
name: t("Profile:DarkTheme"),
|
||||
content: (
|
||||
<Preview
|
||||
appliedColorAccent={appliedColorAccent}
|
||||
@ -799,7 +797,7 @@ const Appearance = (props) => {
|
||||
onSaveColorSchemeDialog={onSaveColorSchemeDialog}
|
||||
/>
|
||||
<div className="header preview-header">{t("Common:Preview")}</div>
|
||||
<TabsContainer elements={array_items} />
|
||||
<Tabs items={arrayItems} type={TabsTypes.Secondary} />
|
||||
|
||||
<div className="buttons-container">
|
||||
<Button
|
||||
|
@ -25,7 +25,7 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import React, { useEffect } from "react";
|
||||
import { Submenu } from "@docspace/shared/components/submenu";
|
||||
import { Tabs } from "@docspace/shared/components/tabs";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { combineUrl } from "@docspace/shared/utils/combineUrl";
|
||||
@ -35,15 +35,14 @@ import Customization from "./customization";
|
||||
import Branding from "./branding";
|
||||
import Appearance from "./appearance";
|
||||
import withLoading from "SRC_DIR/HOCs/withLoading";
|
||||
import LoaderSubmenu from "./sub-components/loaderSubmenu";
|
||||
import LoaderTabs from "./sub-components/loaderTabs";
|
||||
import { resetSessionStorage } from "../../utils";
|
||||
import { DeviceType } from "@docspace/shared/enums";
|
||||
import { SECTION_HEADER_HEIGHT } from "@docspace/shared/components/section/Section.constants";
|
||||
|
||||
const SubmenuCommon = (props) => {
|
||||
const TabsCommon = (props) => {
|
||||
const {
|
||||
t,
|
||||
|
||||
tReady,
|
||||
setIsLoadedSubmenu,
|
||||
loadBaseInfo,
|
||||
@ -103,22 +102,22 @@ const SubmenuCommon = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const getCurrentTab = () => {
|
||||
const getCurrentTabId = () => {
|
||||
const path = location.pathname;
|
||||
const currentTab = data.findIndex((item) => path.includes(item.id));
|
||||
return currentTab !== -1 ? currentTab : 0;
|
||||
const currentTab = data.find((item) => path.includes(item.id));
|
||||
return currentTab !== -1 && data.length ? currentTab.id : data[0].id;
|
||||
};
|
||||
|
||||
const currentTab = getCurrentTab();
|
||||
const currentTabId = getCurrentTabId();
|
||||
|
||||
if (!isLoadedSubmenu) return <LoaderSubmenu />;
|
||||
if (!isLoadedSubmenu) return <LoaderTabs />;
|
||||
|
||||
return (
|
||||
<Submenu
|
||||
data={data}
|
||||
startSelect={currentTab}
|
||||
<Tabs
|
||||
items={data}
|
||||
selectedItemId={currentTabId}
|
||||
onSelect={(e) => onSelect(e)}
|
||||
topProps={SECTION_HEADER_HEIGHT[currentDeviceType]}
|
||||
stickyTop={SECTION_HEADER_HEIGHT[currentDeviceType]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -146,4 +145,4 @@ export default inject(({ settingsStore, common }) => {
|
||||
currentDeviceType,
|
||||
isMobileView,
|
||||
};
|
||||
})(withLoading(withTranslation("Settings")(observer(SubmenuCommon))));
|
||||
})(withLoading(withTranslation("Settings")(observer(TabsCommon))));
|
||||
|
@ -47,7 +47,7 @@ const StyledLoader = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const LoaderSubmenu = () => {
|
||||
const LoaderTabs = () => {
|
||||
return (
|
||||
<StyledLoader>
|
||||
<RectangleSkeleton width="100px" height="28px" className="loader" />
|
||||
@ -56,4 +56,4 @@ const LoaderSubmenu = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default LoaderSubmenu;
|
||||
export default LoaderTabs;
|
@ -31,7 +31,7 @@ import { combineUrl } from "@docspace/shared/utils/combineUrl";
|
||||
import { DeviceType } from "@docspace/shared/enums";
|
||||
import ManualBackup from "./manual-backup";
|
||||
import AutoBackup from "./auto-backup";
|
||||
import { Submenu } from "@docspace/shared/components/submenu";
|
||||
import { Tabs } from "@docspace/shared/components/tabs";
|
||||
import config from "PACKAGE_FILE";
|
||||
|
||||
const Backup = ({ t, buttonSize, isNotPaidPeriod }) => {
|
||||
@ -63,7 +63,11 @@ const Backup = ({ t, buttonSize, isNotPaidPeriod }) => {
|
||||
return isNotPaidPeriod ? (
|
||||
<ManualBackup buttonSize={buttonSize} />
|
||||
) : (
|
||||
<Submenu data={data} startSelect={data[0]} onSelect={(e) => onSelect(e)} />
|
||||
<Tabs
|
||||
items={data}
|
||||
selectedItemId={data[0].id}
|
||||
onSelect={(e) => onSelect(e)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -25,14 +25,14 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import { withTranslation, Trans } from "react-i18next";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTheme } from "styled-components";
|
||||
|
||||
import HelpReactSvgUrl from "PUBLIC_DIR/images/help.react.svg?url";
|
||||
|
||||
import { Submenu } from "@docspace/shared/components/submenu";
|
||||
import { Tabs } from "@docspace/shared/components/tabs";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
@ -58,8 +58,9 @@ const DataManagementWrapper = (props) => {
|
||||
} = props;
|
||||
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const [currentTab, setCurrentTab] = useState(0);
|
||||
const [currentTabId, setCurrentTabId] = useState();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const { interfaceDirection } = useTheme();
|
||||
@ -120,18 +121,18 @@ const DataManagementWrapper = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
const path = location.pathname;
|
||||
|
||||
const currentTab = data.findIndex((item) => path.includes(item.id));
|
||||
if (currentTab !== -1) setCurrentTab(currentTab);
|
||||
const currentTab = data.find((item) => path.includes(item.id));
|
||||
if (currentTab !== -1 && data.length) setCurrentTabId(currentTab.id);
|
||||
|
||||
setIsLoading(true);
|
||||
}, []);
|
||||
}, [location.pathname]);
|
||||
|
||||
const onSelect = (e) => {
|
||||
const url = isManagement()
|
||||
? `/backup/${e.id}`
|
||||
: `/portal-settings/backup/${e.id}`;
|
||||
navigate(combineUrl(window.ClientConfig?.proxy?.url, config.homepage, url));
|
||||
setCurrentTabId(e.id);
|
||||
};
|
||||
|
||||
if (!isLoading) return <AppLoader />;
|
||||
@ -139,11 +140,11 @@ const DataManagementWrapper = (props) => {
|
||||
return isNotPaidPeriod ? (
|
||||
<ManualBackup buttonSize={buttonSize} renderTooltip={renderTooltip} />
|
||||
) : (
|
||||
<Submenu
|
||||
data={data}
|
||||
startSelect={currentTab}
|
||||
<Tabs
|
||||
items={data}
|
||||
selectedItemId={currentTabId}
|
||||
onSelect={(e) => onSelect(e)}
|
||||
topProps={SECTION_HEADER_HEIGHT[currentDeviceType]}
|
||||
stickyTop={SECTION_HEADER_HEIGHT[currentDeviceType]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -26,8 +26,8 @@
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Submenu } from "@docspace/shared/components/submenu";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import { Tabs } from "@docspace/shared/components/tabs";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import PortalDeactivationSection from "./portalDeactivation";
|
||||
import PortalDeletionSection from "./portalDeletion";
|
||||
@ -40,8 +40,9 @@ const DeleteData = (props) => {
|
||||
const { t, isNotPaidPeriod, tReady } = props;
|
||||
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const [currentTab, setCurrentTab] = useState(0);
|
||||
const [currentTabId, setCurrentTabId] = useState();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const data = [
|
||||
@ -59,10 +60,11 @@ const DeleteData = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
const path = location.pathname;
|
||||
const currentTab = data.findIndex((item) => path.includes(item.id));
|
||||
if (currentTab !== -1) setCurrentTab(currentTab);
|
||||
const currentTab = data.find((item) => path.includes(item.id));
|
||||
if (currentTab !== -1 && data.length) setCurrentTabId(currentTab.id);
|
||||
|
||||
setIsLoading(true);
|
||||
}, []);
|
||||
}, [location.pathname]);
|
||||
|
||||
const onSelect = (e) => {
|
||||
navigate(
|
||||
@ -72,15 +74,16 @@ const DeleteData = (props) => {
|
||||
`/portal-settings/delete-data/${e.id}`,
|
||||
),
|
||||
);
|
||||
setCurrentTabId(e.id);
|
||||
};
|
||||
|
||||
if (!isLoading || !tReady) return <DeleteDataLoader />;
|
||||
return isNotPaidPeriod ? (
|
||||
<PortalDeletionSection />
|
||||
) : (
|
||||
<Submenu
|
||||
data={data}
|
||||
startSelect={currentTab}
|
||||
<Tabs
|
||||
items={data}
|
||||
selectedItemId={currentTabId}
|
||||
onSelect={(e) => onSelect(e)}
|
||||
/>
|
||||
);
|
||||
|
@ -146,7 +146,7 @@ const PortalIntegration = (props) => {
|
||||
title: PRODUCT_NAME,
|
||||
description: t("PortalDescription", { productName: PRODUCT_NAME }),
|
||||
image: theme.isBase ? PortalImg : PortalImgDark,
|
||||
handleOnClick: navigateToDocspace,
|
||||
handleOnClick: navigateToPortal,
|
||||
},
|
||||
{
|
||||
title: t("Common:PublicRoom"),
|
||||
|
@ -39,8 +39,9 @@ export const SDKContainer = styled(Box)`
|
||||
width: 100%;
|
||||
`}
|
||||
|
||||
.tabs_body {
|
||||
.tabs-body {
|
||||
height: calc(100lvh - 260px);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.linkHelp {
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { objectToGetParams } from "@docspace/shared/utils/common";
|
||||
import { TabsContainer } from "@docspace/shared/components/tabs-container";
|
||||
import { Tabs, TabsTypes } from "@docspace/shared/components/tabs";
|
||||
|
||||
import { CodeToInsert } from "./CodeToInsert";
|
||||
import { GetCodeBlock } from "./GetCodeBlock";
|
||||
@ -56,13 +56,13 @@ export const PreviewBlock = ({
|
||||
);
|
||||
const dataTabs = [
|
||||
{
|
||||
key: "preview",
|
||||
title: t("Common:Preview"),
|
||||
id: "preview",
|
||||
name: t("Common:Preview"),
|
||||
content: preview,
|
||||
},
|
||||
{
|
||||
key: "code",
|
||||
title: t("Code"),
|
||||
id: "code",
|
||||
name: t("Code"),
|
||||
content: code,
|
||||
},
|
||||
];
|
||||
@ -82,9 +82,10 @@ export const PreviewBlock = ({
|
||||
|
||||
return showPreview ? (
|
||||
<Preview>
|
||||
<TabsContainer
|
||||
<Tabs
|
||||
type={TabsTypes.Secondary}
|
||||
onSelect={loadCurrentFrame}
|
||||
elements={dataTabs}
|
||||
items={dataTabs}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
</Preview>
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { Submenu } from "@docspace/shared/components/submenu";
|
||||
import { Tabs } from "@docspace/shared/components/tabs";
|
||||
|
||||
import RequestDetails from "./RequestDetails";
|
||||
import ResponseDetails from "./ResponseDetails";
|
||||
@ -34,7 +34,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { isMobile } from "@docspace/shared/utils";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
const SubmenuWrapper = styled.div`
|
||||
const TabsWrapper = styled.div`
|
||||
.sticky {
|
||||
z-index: 3;
|
||||
|
||||
@ -61,9 +61,9 @@ const MessagesDetails = ({ eventDetails }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<SubmenuWrapper>
|
||||
<Submenu data={menuData} startSelect={0} />
|
||||
</SubmenuWrapper>
|
||||
<TabsWrapper>
|
||||
<Tabs items={menuData} />
|
||||
</TabsWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -26,14 +26,14 @@
|
||||
|
||||
import React, { useEffect, useState, useTransition, Suspense } from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import { Submenu } from "@docspace/shared/components/submenu";
|
||||
import { Tabs } from "@docspace/shared/components/tabs";
|
||||
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { combineUrl } from "@docspace/shared/utils/combineUrl";
|
||||
import config from "PACKAGE_FILE";
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import JavascriptSDK from "./JavascriptSDK";
|
||||
import Webhooks from "./Webhooks";
|
||||
|
||||
@ -48,17 +48,13 @@ import PluginSDK from "./PluginSDK";
|
||||
import { Badge } from "@docspace/shared/components/badge";
|
||||
import { SECTION_HEADER_HEIGHT } from "@docspace/shared/components/section/Section.constants";
|
||||
|
||||
const StyledSubmenu = styled(Submenu)`
|
||||
.sticky {
|
||||
z-index: 201;
|
||||
}
|
||||
`;
|
||||
|
||||
const DeveloperToolsWrapper = (props) => {
|
||||
const { loadBaseInfo, currentDeviceType } = props;
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [currentTabId, setCurrentTabId] = useState();
|
||||
|
||||
const { t, ready } = useTranslation([
|
||||
"JavascriptSdk",
|
||||
@ -113,21 +109,19 @@ const DeveloperToolsWrapper = (props) => {
|
||||
},
|
||||
];
|
||||
|
||||
const [currentTab, setCurrentTab] = useState(
|
||||
data.findIndex((item) => location.pathname.includes(item.id)),
|
||||
);
|
||||
|
||||
const load = async () => {
|
||||
//await loadBaseInfo();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const path = location.pathname;
|
||||
const currentTab = data.findIndex((item) => path.includes(item.id));
|
||||
if (currentTab !== -1) {
|
||||
setCurrentTab(currentTab);
|
||||
const currentTab = data.find((item) => path.includes(item.id));
|
||||
if (currentTab !== -1 && data.length) {
|
||||
setCurrentTabId(currentTab.id);
|
||||
}
|
||||
}, []);
|
||||
|
||||
setIsLoading(true);
|
||||
}, [location.pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
ready && startTransition(load);
|
||||
@ -141,19 +135,18 @@ const DeveloperToolsWrapper = (props) => {
|
||||
`/portal-settings/developer-tools/${e.id}`,
|
||||
),
|
||||
);
|
||||
setCurrentTabId(e.id);
|
||||
};
|
||||
|
||||
const loaders = [<SSOLoader />, <AppLoader />];
|
||||
if (!isLoading) return <SSOLoader />;
|
||||
|
||||
return (
|
||||
<Suspense fallback={loaders[currentTab] || <AppLoader />}>
|
||||
<StyledSubmenu
|
||||
data={data}
|
||||
startSelect={currentTab}
|
||||
onSelect={onSelect}
|
||||
topProps={SECTION_HEADER_HEIGHT[currentDeviceType]}
|
||||
/>
|
||||
</Suspense>
|
||||
<Tabs
|
||||
items={data}
|
||||
selectedItemId={currentTabId}
|
||||
onSelect={onSelect}
|
||||
stickyTop={SECTION_HEADER_HEIGHT[currentDeviceType]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Submenu } from "@docspace/shared/components/submenu";
|
||||
import { Tabs } from "@docspace/shared/components/tabs";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { inject, observer } from "mobx-react";
|
||||
@ -113,13 +113,13 @@ const IntegrationWrapper = (props) => {
|
||||
});
|
||||
}
|
||||
|
||||
const getCurrentTab = () => {
|
||||
const getCurrentTabId = () => {
|
||||
const path = location.pathname;
|
||||
const currentTab = data.findIndex((item) => path.includes(item.id));
|
||||
return currentTab !== -1 ? currentTab : 0;
|
||||
const currentTab = data.find((item) => path.includes(item.id));
|
||||
return currentTab !== -1 && data.length ? currentTab.id : data[0].id;
|
||||
};
|
||||
|
||||
const currentTab = getCurrentTab();
|
||||
const currentTabId = getCurrentTabId();
|
||||
|
||||
const onSelect = (e) => {
|
||||
navigate(
|
||||
@ -132,9 +132,9 @@ const IntegrationWrapper = (props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Submenu
|
||||
data={data}
|
||||
startSelect={currentTab}
|
||||
<Tabs
|
||||
items={data}
|
||||
selectedItemId={currentTabId}
|
||||
onSelect={onSelect}
|
||||
topProps={SECTION_HEADER_HEIGHT[currentDeviceType]}
|
||||
/>
|
||||
|
@ -25,8 +25,8 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Submenu } from "@docspace/shared/components/submenu";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Tabs } from "@docspace/shared/components/tabs";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { combineUrl } from "@docspace/shared/utils/combineUrl";
|
||||
@ -45,9 +45,10 @@ import { PRODUCT_NAME } from "@docspace/shared/constants";
|
||||
|
||||
const SecurityWrapper = (props) => {
|
||||
const { t, loadBaseInfo, resetIsInit, currentDeviceType } = props;
|
||||
const [currentTab, setCurrentTab] = useState(0);
|
||||
const [currentTabId, setCurrentTabId] = useState();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const data = [
|
||||
{
|
||||
@ -81,11 +82,11 @@ const SecurityWrapper = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
const path = location.pathname;
|
||||
const currentTab = data.findIndex((item) => path.includes(item.id));
|
||||
if (currentTab !== -1) setCurrentTab(currentTab);
|
||||
const currentTab = data.find((item) => path.includes(item.id));
|
||||
if (currentTab !== -1 && data.length) setCurrentTabId(currentTab.id);
|
||||
|
||||
load();
|
||||
}, []);
|
||||
}, [location.pathname]);
|
||||
|
||||
const onSelect = (e) => {
|
||||
navigate(
|
||||
@ -95,10 +96,11 @@ const SecurityWrapper = (props) => {
|
||||
`/portal-settings/security/${e.id}`,
|
||||
),
|
||||
);
|
||||
setCurrentTabId(e.id);
|
||||
};
|
||||
|
||||
if (!isLoading)
|
||||
return currentTab === 0 ? (
|
||||
if (!isLoading && data.length)
|
||||
return currentTabId === data[0].id ? (
|
||||
currentDeviceType !== DeviceType.desktop ? (
|
||||
<MobileSecurityLoader />
|
||||
) : (
|
||||
@ -107,12 +109,13 @@ const SecurityWrapper = (props) => {
|
||||
) : (
|
||||
<AccessLoader />
|
||||
);
|
||||
|
||||
return (
|
||||
<Submenu
|
||||
data={data}
|
||||
startSelect={currentTab}
|
||||
<Tabs
|
||||
items={data}
|
||||
selectedItemId={currentTabId}
|
||||
onSelect={(e) => onSelect(e)}
|
||||
topProps={SECTION_HEADER_HEIGHT[currentDeviceType]}
|
||||
stickyTop={SECTION_HEADER_HEIGHT[currentDeviceType]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -32,7 +32,7 @@ import { inject, observer } from "mobx-react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { ProfileViewLoader } from "@docspace/shared/skeletons/profile";
|
||||
import { Submenu } from "@docspace/shared/components/submenu";
|
||||
import { Tabs } from "@docspace/shared/components/tabs";
|
||||
|
||||
import MainProfile from "./sub-components/main-profile";
|
||||
import LoginContent from "./sub-components/LoginContent";
|
||||
@ -54,7 +54,7 @@ const Wrapper = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledSubMenu = styled(Submenu)`
|
||||
const StyledTabs = styled(Tabs)`
|
||||
> .sticky {
|
||||
z-index: 201;
|
||||
margin-inline-end: -17px;
|
||||
@ -91,13 +91,13 @@ const SectionBodyContent = (props) => {
|
||||
content: <FileManagement />,
|
||||
});
|
||||
|
||||
const getCurrentTab = () => {
|
||||
const getCurrentTabId = () => {
|
||||
const path = location.pathname;
|
||||
const currentTab = data.findIndex((item) => path.includes(item.id));
|
||||
return currentTab !== -1 ? currentTab : 0;
|
||||
const currentTab = data.find((item) => path.includes(item.id));
|
||||
return currentTab !== -1 && data.length ? currentTab.id : data[0].id;
|
||||
};
|
||||
|
||||
const currentTab = getCurrentTab();
|
||||
const currentTabId = getCurrentTabId();
|
||||
|
||||
const onSelect = (e) => {
|
||||
const arrayPaths = location.pathname.split("/");
|
||||
@ -110,11 +110,11 @@ const SectionBodyContent = (props) => {
|
||||
return (
|
||||
<Wrapper>
|
||||
<MainProfile />
|
||||
<StyledSubMenu
|
||||
data={data}
|
||||
startSelect={currentTab}
|
||||
<StyledTabs
|
||||
items={data}
|
||||
selectedItemId={currentTabId}
|
||||
onSelect={onSelect}
|
||||
topProps={SECTION_HEADER_HEIGHT[currentDeviceType]}
|
||||
stickyTop={SECTION_HEADER_HEIGHT[currentDeviceType]}
|
||||
/>
|
||||
</Wrapper>
|
||||
);
|
||||
|
@ -36,6 +36,7 @@ import {
|
||||
RoomsProviderType,
|
||||
ShareAccessRights,
|
||||
Events,
|
||||
FilterKeys,
|
||||
} from "@docspace/shared/enums";
|
||||
|
||||
import { RoomsTypes } from "@docspace/shared/utils";
|
||||
@ -50,6 +51,7 @@ import { getDaysRemaining } from "@docspace/shared/utils/common";
|
||||
import {
|
||||
MEDIA_VIEW_URL,
|
||||
PDF_FORM_DIALOG_KEY,
|
||||
ROOMS_PROVIDER_TYPE_NAME,
|
||||
} from "@docspace/shared/constants";
|
||||
|
||||
import {
|
||||
@ -1438,6 +1440,19 @@ class FilesStore {
|
||||
filterData.pageCount = 100;
|
||||
}
|
||||
|
||||
const defaultFilter = FilesFilter.getDefault();
|
||||
|
||||
const { filterType, searchInContent } = filterData;
|
||||
|
||||
if (typeof filterData.withSubfolders !== "boolean")
|
||||
filterData.withSubfolders = defaultFilter.withSubfolders;
|
||||
|
||||
if (typeof searchInContent !== "boolean")
|
||||
filterData.searchInContent = defaultFilter.searchInContent;
|
||||
|
||||
if (!Object.keys(FilterType).find((key) => FilterType[key] === filterType))
|
||||
filterData.filterType = defaultFilter.filterType;
|
||||
|
||||
setSelectedNode([folderId + ""]);
|
||||
|
||||
return api.files
|
||||
@ -1733,6 +1748,22 @@ class FilesStore {
|
||||
|
||||
if (folderId) setSelectedNode([folderId + ""]);
|
||||
|
||||
const defaultFilter = RoomsFilter.getDefault();
|
||||
|
||||
const { provider, quotaFilter, type } = filterData;
|
||||
|
||||
if (!ROOMS_PROVIDER_TYPE_NAME[provider])
|
||||
filterData.provider = defaultFilter.provider;
|
||||
|
||||
if (
|
||||
quotaFilter &&
|
||||
quotaFilter !== FilterKeys.customQuota &&
|
||||
quotaFilter !== FilterKeys.defaultQuota
|
||||
)
|
||||
filterData.quotaFilter = defaultFilter.quotaFilter;
|
||||
|
||||
if (type && !RoomsType[type]) filterData.type = defaultFilter.type;
|
||||
|
||||
const request = () =>
|
||||
api.rooms
|
||||
.getRooms(filterData, this.roomsController.signal)
|
||||
|
@ -25,12 +25,11 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import type { Metadata } from "next";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
import AppLoader from "@docspace/shared/components/app-loader";
|
||||
import { BRAND_NAME } from "@docspace/shared/constants";
|
||||
|
||||
import { getData } from "@/utils/actions";
|
||||
import { RootPageProps } from "@/types";
|
||||
import Root from "@/components/Root";
|
||||
|
||||
const initialSearchParams: RootPageProps["searchParams"] = {
|
||||
fileId: undefined,
|
||||
@ -42,13 +41,8 @@ const initialSearchParams: RootPageProps["searchParams"] = {
|
||||
editorType: undefined,
|
||||
};
|
||||
|
||||
const Root = dynamic(() => import("@/components/Root"), {
|
||||
ssr: false,
|
||||
loading: () => <AppLoader />,
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "ONLYOFFICE DocEditor page",
|
||||
title: `${BRAND_NAME} DocEditor page`,
|
||||
|
||||
description: "",
|
||||
};
|
||||
|
@ -36,9 +36,10 @@ import IConfig from "@onlyoffice/document-editor-react/dist/esm/types/model/conf
|
||||
|
||||
import { FolderType, ThemeKeys } from "@docspace/shared/enums";
|
||||
import { getEditorTheme } from "@docspace/shared/utils";
|
||||
import { EDITOR_ID } from "@docspace/shared/constants";
|
||||
|
||||
import { getBackUrl } from "@/utils";
|
||||
import { IS_DESKTOP_EDITOR, IS_ZOOM } from "@/utils/constants";
|
||||
import { IS_DESKTOP_EDITOR, IS_ZOOM, SHOW_CLOSE } from "@/utils/constants";
|
||||
import { EditorProps, TGoBack } from "@/types";
|
||||
import {
|
||||
onSDKRequestHistoryClose,
|
||||
@ -47,11 +48,11 @@ import {
|
||||
onSDKWarning,
|
||||
onSDKError,
|
||||
onSDKRequestRename,
|
||||
onOutdatedVersion,
|
||||
} from "@/utils/events";
|
||||
import useInit from "@/hooks/useInit";
|
||||
import useEditorEvents from "@/hooks/useEditorEvents";
|
||||
import useFilesSettings from "@/hooks/useFilesSettings";
|
||||
import { EDITOR_ID } from "@docspace/shared/constants";
|
||||
|
||||
type IConfigType = IConfig & {
|
||||
events?: {
|
||||
@ -193,14 +194,13 @@ const Editor = ({
|
||||
>["customization"] = JSON.parse(customization || "{}");
|
||||
|
||||
const theme = sdkCustomization?.uiTheme || user?.theme;
|
||||
const showClose = document.referrer !== "" && window.history.length > 1;
|
||||
|
||||
if (newConfig.editorConfig)
|
||||
newConfig.editorConfig.customization = {
|
||||
...newConfig.editorConfig.customization,
|
||||
...sdkCustomization,
|
||||
goback: { ...goBack },
|
||||
close: { visible: showClose, text: t("Common:CloseButton") },
|
||||
close: { visible: SHOW_CLOSE, text: t("Common:CloseButton") },
|
||||
uiTheme: getEditorTheme(theme as ThemeKeys),
|
||||
};
|
||||
|
||||
@ -232,6 +232,7 @@ const Editor = ({
|
||||
onDocumentStateChange,
|
||||
onMetaChange,
|
||||
onMakeActionLink,
|
||||
onOutdatedVersion,
|
||||
};
|
||||
|
||||
if (successAuth) {
|
||||
@ -288,7 +289,7 @@ const Editor = ({
|
||||
if (
|
||||
(typeof window !== "undefined" &&
|
||||
window.ClientConfig?.editor?.requestClose) ||
|
||||
showClose ||
|
||||
SHOW_CLOSE ||
|
||||
IS_ZOOM
|
||||
) {
|
||||
newConfig.events.onRequestClose = onSDKRequestClose;
|
||||
@ -299,7 +300,11 @@ const Editor = ({
|
||||
}
|
||||
|
||||
newConfig.events.onSubmit = () => {
|
||||
router.push(`/completed-form?${searchParams.toString()}`);
|
||||
const origin = window.location.origin;
|
||||
|
||||
window.location.replace(
|
||||
`${origin}/doceditor/completed-form?${searchParams.toString()}`,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -30,7 +30,6 @@ import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import ErrorContainer from "@docspace/shared/components/error-container/ErrorContainer";
|
||||
import AppLoader from "@docspace/shared/components/app-loader";
|
||||
|
||||
import { TResponse } from "@/types";
|
||||
import useError from "@/hooks/useError";
|
||||
@ -83,7 +82,8 @@ const Root = ({
|
||||
|
||||
useEffect(() => {
|
||||
console.log("editor timer: ", timer);
|
||||
}, [timer]);
|
||||
console.log("openEdit timer: ", config?.timer);
|
||||
}, [config?.timer, timer]);
|
||||
|
||||
useRootInit({
|
||||
documentType: config?.documentType,
|
||||
|
@ -380,6 +380,7 @@ export async function openEdit(
|
||||
searchParams: string,
|
||||
share?: string,
|
||||
) {
|
||||
const startDate = new Date();
|
||||
const hdrs = headers();
|
||||
const cookie = hdrs.get("cookie");
|
||||
|
||||
@ -397,10 +398,12 @@ export async function openEdit(
|
||||
const config = await res.json();
|
||||
|
||||
if (res.ok) {
|
||||
const timer = new Date().getTime() - startDate.getTime();
|
||||
|
||||
config.response.editorUrl = (
|
||||
config.response as IInitialConfig
|
||||
).editorUrl.replace(REPLACED_URL_PATH, "");
|
||||
return config.response as IInitialConfig;
|
||||
return { ...config.response, timer } as IInitialConfig;
|
||||
}
|
||||
|
||||
const editorUrl =
|
||||
|
@ -38,3 +38,8 @@ export const IS_VIEW =
|
||||
: false;
|
||||
|
||||
export const REPLACED_URL_PATH = "/web-apps/apps/api/documents/api.js";
|
||||
|
||||
export const SHOW_CLOSE =
|
||||
typeof document !== "undefined" &&
|
||||
document.referrer !== "" &&
|
||||
window.history.length > 1;
|
||||
|
@ -119,3 +119,5 @@ export const onSDKRequestRename = async (
|
||||
const title = (event as TRenameEvent).data;
|
||||
await updateFile(id, title);
|
||||
};
|
||||
|
||||
export const onOutdatedVersion = () => window.location.reload();
|
||||
|
@ -56,26 +56,26 @@ import { Link, LinkType } from "../link";
|
||||
import { Loader, LoaderTypes } from "../loader";
|
||||
import { Row } from "../row";
|
||||
import { Scrollbar } from "../scrollbar";
|
||||
import { TabsContainer } from "../tabs-container";
|
||||
import { Tabs, TabsTypes } from "../tabs";
|
||||
import { Text } from "../text";
|
||||
import { Toast, toastr } from "../toast";
|
||||
import { ToggleContent } from "../toggle-content";
|
||||
import { Tooltip } from "../tooltip";
|
||||
|
||||
const ArrayItems = [
|
||||
const arrayItems = [
|
||||
{
|
||||
key: "0",
|
||||
title: "Tab 1",
|
||||
id: "0",
|
||||
name: "Tab 1",
|
||||
content: <div>Tab 1 content</div>,
|
||||
},
|
||||
{
|
||||
key: "1",
|
||||
title: "Tab 2",
|
||||
id: "1",
|
||||
name: "Tab 2",
|
||||
content: <div>Tab 2 content</div>,
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
title: "Tab 3",
|
||||
id: "2",
|
||||
name: "Tab 3",
|
||||
content: <div>Tab 3 content</div>,
|
||||
},
|
||||
];
|
||||
@ -570,11 +570,11 @@ const Template = () => (
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ padding: "8px 0" }}>
|
||||
<TabsContainer
|
||||
elements={ArrayItems}
|
||||
isDisabled={false}
|
||||
<Tabs
|
||||
items={arrayItems}
|
||||
type={TabsTypes.Secondary}
|
||||
onSelect={() => {}}
|
||||
selectedItem={0}
|
||||
selectedItemId={arrayItems[0].id}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ padding: "16px 0" }}>
|
||||
|
@ -28,12 +28,13 @@ import styled, { css } from "styled-components";
|
||||
|
||||
import ArrowRightSvg from "PUBLIC_DIR/images/arrow.right.react.svg";
|
||||
|
||||
import { Tabs } from "../tabs";
|
||||
import { Base } from "../../themes";
|
||||
import { mobile } from "../../utils/device";
|
||||
|
||||
import { ComboBox } from "../combobox";
|
||||
import { Text } from "../text";
|
||||
import { Submenu } from "../submenu";
|
||||
|
||||
import { AccessRightSelect } from "../access-right-select";
|
||||
|
||||
const accessComboboxStyles = css`
|
||||
@ -251,7 +252,7 @@ const StyledItem = styled.div<{
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #a3a9ae;
|
||||
color: ${props.theme.selector.item.disableTextColor};
|
||||
}
|
||||
|
||||
.disabled-text {
|
||||
@ -468,7 +469,7 @@ const StyledAccessSelector = styled(AccessRightSelect)`
|
||||
${accessComboboxStyles}
|
||||
`;
|
||||
|
||||
const StyledTabs = styled(Submenu)`
|
||||
const StyledTabs = styled(Tabs)`
|
||||
padding: 0 16px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
|
@ -32,7 +32,7 @@ import { TFileSecurity, TFolderSecurity } from "../../api/files/types";
|
||||
import { TRoomSecurity } from "../../api/rooms/types";
|
||||
|
||||
import { AvatarRole } from "../avatar";
|
||||
import { TSubmenuItem } from "../submenu";
|
||||
import { TTabItem } from "../tabs";
|
||||
|
||||
import { SelectorAccessRightsMode } from "./Selector.enums";
|
||||
|
||||
@ -106,7 +106,7 @@ export type TSelectorBreadCrumbs =
|
||||
|
||||
// tabs
|
||||
export type TWithTabs =
|
||||
| { withTabs: true; tabsData: TSubmenuItem[]; activeTabId: string }
|
||||
| { withTabs: true; tabsData: TTabItem[]; activeTabId: string }
|
||||
| { withTabs?: undefined; tabsData?: undefined; activeTabId?: undefined };
|
||||
|
||||
// select all
|
||||
|
@ -226,11 +226,7 @@ const Body = ({
|
||||
) : null}
|
||||
|
||||
{withTabs && tabsData && (
|
||||
<StyledTabs
|
||||
startSelect={0}
|
||||
data={tabsData}
|
||||
forsedActiveItemId={activeTabId}
|
||||
/>
|
||||
<StyledTabs items={tabsData} selectedItemId={activeTabId} />
|
||||
)}
|
||||
|
||||
{isSearchLoading || isBreadCrumbsLoading ? (
|
||||
|
@ -1,71 +0,0 @@
|
||||
# Submenu
|
||||
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import { Submenu } from "@docspace/shared/components/submenu";
|
||||
```
|
||||
|
||||
```jsx
|
||||
<Submenu
|
||||
data={[
|
||||
{
|
||||
id: "FileInput",
|
||||
name: "File Input",
|
||||
content: (
|
||||
<FileInput
|
||||
accept={[".doc", ".docx"]}
|
||||
id="file-input-id"
|
||||
name="demoFileInputName"
|
||||
onInput={function noRefCheck() {}}
|
||||
placeholder="Input file"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "ToggleButton",
|
||||
name: "Toggle Button",
|
||||
content: (
|
||||
<ToggleButton
|
||||
className="toggle className"
|
||||
id="toggle id"
|
||||
label="label text"
|
||||
onChange={() => {}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
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: (
|
||||
<FileInput
|
||||
accept={[".doc", ".docx"]}
|
||||
id="file-input-id"
|
||||
name="demoFileInputName"
|
||||
onInput={function noRefCheck() {}}
|
||||
placeholder="Input file"
|
||||
/>
|
||||
),
|
||||
},
|
||||
```
|
||||
|
||||
### 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 |
|
@ -1,182 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import styled, { css } from "styled-components";
|
||||
import { Base, TColorScheme } from "../../themes";
|
||||
|
||||
export const StyledSubmenu = styled.div<{ topProps?: string }>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.scrollbar {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.text {
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.sticky {
|
||||
position: sticky;
|
||||
top: ${(props) => (props.topProps ? props.topProps : 0)};
|
||||
background: ${(props) => props.theme.submenu.backgroundColor};
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.sticky-indent {
|
||||
height: 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledSubmenu.defaultProps = { theme: Base };
|
||||
|
||||
export const StyledSubmenuBottomLine = styled.div`
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
margin-top: -1px;
|
||||
background: ${(props) => props.theme.submenu.lineColor};
|
||||
`;
|
||||
|
||||
StyledSubmenuBottomLine.defaultProps = { theme: Base };
|
||||
|
||||
export const StyledSubmenuContentWrapper = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
export const StyledSubmenuItems = styled.div`
|
||||
overflow: scroll;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 4px;
|
||||
|
||||
width: max-content;
|
||||
overflow: hidden;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
export const StyledSubmenuItem = styled.div.attrs((props) => ({
|
||||
id: props.id,
|
||||
}))`
|
||||
scroll-behavior: smooth;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
flex-direction: column;
|
||||
padding-top: 4px;
|
||||
line-height: 20px;
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: 17px;
|
||||
`
|
||||
: css`
|
||||
&:not(:last-child) {
|
||||
margin-right: 17px;
|
||||
}
|
||||
`}
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
`;
|
||||
|
||||
export const StyledSubmenuItemText = styled.div<{ isActive?: boolean }>`
|
||||
width: max-content;
|
||||
display: flex;
|
||||
|
||||
.item-text {
|
||||
color: ${(props) =>
|
||||
props.isActive
|
||||
? props.theme.submenu.activeTextColor
|
||||
: props.theme.submenu.textColor};
|
||||
font-weight: 600;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledSubmenuItemText.defaultProps = { theme: Base };
|
||||
|
||||
export const StyledSubmenuItemLabel = styled.div<{ isActive?: boolean }>`
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
bottom: 0px;
|
||||
border-radius: 4px 4px 0 0;
|
||||
background-color: ${(props) =>
|
||||
props.isActive ? props.theme.submenu.bottomLineColor : ""};
|
||||
`;
|
||||
|
||||
StyledSubmenuItemLabel.defaultProps = { theme: Base };
|
||||
|
||||
export const SubmenuScroller = styled.div`
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
flex: 1 1 auto;
|
||||
white-space: nowrap;
|
||||
scrollbar-width: none; // Firefox
|
||||
&::-webkit-scrollbar {
|
||||
display: none; // Safari + Chrome
|
||||
}
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
`;
|
||||
|
||||
export const SubmenuRoot = styled.div`
|
||||
overflow: hidden;
|
||||
min-height: 32px;
|
||||
// Add iOS momentum scrolling for iOS < 13.0
|
||||
-webkit-overflow-scrolling: touch;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const SubmenuScrollbarSize = styled.div`
|
||||
height: 32;
|
||||
position: absolute;
|
||||
top: -9999;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
// Hide dimensionless scrollbar on macOS
|
||||
scrollbar-width: none; // Firefox
|
||||
&::-webkit-scrollbar {
|
||||
display: none; // Safari + Chrome
|
||||
}
|
||||
`;
|
||||
|
||||
export const StyledItemLabelTheme = styled(StyledSubmenuItemLabel)<{
|
||||
$currentColorScheme?: TColorScheme;
|
||||
}>`
|
||||
background-color: ${(props) =>
|
||||
props.isActive ? props.$currentColorScheme?.main?.accent : "none"};
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props) =>
|
||||
props.isActive && props.$currentColorScheme?.main?.accent};
|
||||
}
|
||||
`;
|
@ -1,203 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import React, { useEffect, useRef, useState, useCallback } from "react";
|
||||
import { useTheme } from "styled-components";
|
||||
import { countAutoFocus, countAutoOffset } from "./Submenu.utils";
|
||||
import {
|
||||
StyledSubmenu,
|
||||
StyledSubmenuBottomLine,
|
||||
StyledSubmenuContentWrapper,
|
||||
StyledSubmenuItems,
|
||||
StyledSubmenuItemText,
|
||||
SubmenuScroller,
|
||||
SubmenuRoot,
|
||||
SubmenuScrollbarSize,
|
||||
StyledItemLabelTheme,
|
||||
StyledSubmenuItem,
|
||||
} from "./Submenu.styled";
|
||||
|
||||
import { ColorTheme, ThemeId } from "../color-theme";
|
||||
import { SubmenuProps, TSubmenuItem } from "./Submenu.types";
|
||||
|
||||
const Submenu = (props: SubmenuProps) => {
|
||||
const {
|
||||
data,
|
||||
startSelect = 0,
|
||||
forsedActiveItemId,
|
||||
onSelect,
|
||||
topProps,
|
||||
...rest
|
||||
} = props;
|
||||
const submenuItemsRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const [currentItem, setCurrentItem] = useState<null | TSubmenuItem>(null);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof startSelect === "number")
|
||||
return setCurrentItem(data[startSelect]);
|
||||
|
||||
setCurrentItem(startSelect);
|
||||
}, [data, startSelect]);
|
||||
|
||||
// if (!data) return null;
|
||||
|
||||
const onCheckCurrentItem = useCallback(() => {
|
||||
const isSelect = data.find((item: TSubmenuItem) =>
|
||||
window.location.pathname.endsWith(item.id),
|
||||
);
|
||||
|
||||
if (isSelect) setCurrentItem(isSelect);
|
||||
}, [data, setCurrentItem]);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("popstate", onCheckCurrentItem);
|
||||
onCheckCurrentItem();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("popstate", onCheckCurrentItem);
|
||||
};
|
||||
}, [onCheckCurrentItem]);
|
||||
|
||||
const selectSubmenuItem = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (!forsedActiveItemId) {
|
||||
const item = data.find((el) => el.id === e.currentTarget.id);
|
||||
if (!item) return;
|
||||
setCurrentItem(item);
|
||||
const offset = countAutoFocus(item.name, data, submenuItemsRef.current);
|
||||
if (submenuItemsRef.current && offset)
|
||||
submenuItemsRef.current.scrollLeft += offset;
|
||||
onSelect?.(item);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!submenuItemsRef.current) return;
|
||||
let isDown = false;
|
||||
let startX: number;
|
||||
let scrollLeft: number;
|
||||
|
||||
const mouseDown = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
isDown = true;
|
||||
if (submenuItemsRef.current) {
|
||||
startX = e.pageX - submenuItemsRef.current.offsetLeft;
|
||||
scrollLeft = submenuItemsRef.current.scrollLeft;
|
||||
}
|
||||
};
|
||||
|
||||
const mouseMove = (e: MouseEvent) => {
|
||||
if (!isDown) return;
|
||||
e.preventDefault();
|
||||
if (submenuItemsRef.current) {
|
||||
const x = e.pageX - submenuItemsRef.current.offsetLeft;
|
||||
const walk = x - startX;
|
||||
submenuItemsRef.current.scrollLeft = scrollLeft - walk;
|
||||
}
|
||||
};
|
||||
|
||||
const mouseUp = () => {
|
||||
const offset = countAutoOffset(data, submenuItemsRef.current);
|
||||
if (submenuItemsRef.current && offset)
|
||||
submenuItemsRef.current.scrollLeft += offset;
|
||||
isDown = false;
|
||||
};
|
||||
|
||||
const mouseLeave = () => (isDown = false);
|
||||
|
||||
const ref = submenuItemsRef.current;
|
||||
|
||||
ref.addEventListener("mousedown", mouseDown);
|
||||
ref.addEventListener("mousemove", mouseMove);
|
||||
ref.addEventListener("mouseup", mouseUp);
|
||||
ref.addEventListener("mouseleave", mouseLeave);
|
||||
|
||||
return () => {
|
||||
ref?.removeEventListener("mousedown", mouseDown);
|
||||
ref?.removeEventListener("mousemove", mouseMove);
|
||||
ref?.removeEventListener("mouseup", mouseUp);
|
||||
ref?.removeEventListener("mouseleave", mouseLeave);
|
||||
};
|
||||
}, [data, submenuItemsRef]);
|
||||
|
||||
return (
|
||||
<StyledSubmenu {...rest} topProps={topProps}>
|
||||
<div className="sticky">
|
||||
<SubmenuRoot>
|
||||
<SubmenuScrollbarSize />
|
||||
<SubmenuScroller>
|
||||
<StyledSubmenuItems ref={submenuItemsRef} role="list">
|
||||
{data.map((d) => {
|
||||
const isActive =
|
||||
d.id === (forsedActiveItemId || currentItem?.id);
|
||||
|
||||
return (
|
||||
<StyledSubmenuItem
|
||||
key={d.id}
|
||||
id={d.id}
|
||||
onClick={(e) => {
|
||||
d.onClick?.();
|
||||
selectSubmenuItem(e);
|
||||
}}
|
||||
>
|
||||
<StyledSubmenuItemText isActive={isActive}>
|
||||
<ColorTheme
|
||||
{...props}
|
||||
as="div"
|
||||
themeId={ThemeId.SubmenuText}
|
||||
className="item-text"
|
||||
fontSize="13px"
|
||||
fontWeight="600"
|
||||
truncate={false}
|
||||
isActive={isActive}
|
||||
>
|
||||
{d.name}
|
||||
</ColorTheme>
|
||||
</StyledSubmenuItemText>
|
||||
<StyledItemLabelTheme
|
||||
isActive={isActive}
|
||||
$currentColorScheme={theme.currentColorScheme}
|
||||
/>
|
||||
</StyledSubmenuItem>
|
||||
);
|
||||
})}
|
||||
</StyledSubmenuItems>
|
||||
</SubmenuScroller>
|
||||
</SubmenuRoot>
|
||||
<StyledSubmenuBottomLine className="bottom-line" />
|
||||
</div>
|
||||
<div className="sticky-indent" />
|
||||
|
||||
<StyledSubmenuContentWrapper>
|
||||
{currentItem?.content}
|
||||
</StyledSubmenuContentWrapper>
|
||||
</StyledSubmenu>
|
||||
);
|
||||
};
|
||||
|
||||
export { Submenu };
|
@ -1,193 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { DomHelpers, size } from "../../utils";
|
||||
import { TSubmenuItem } from "./Submenu.types";
|
||||
|
||||
const paddingGap = 14;
|
||||
const flexGap = 4;
|
||||
const offset = 32;
|
||||
const wrapperPadding = DomHelpers.getViewport().width <= size.desktop ? 16 : 20;
|
||||
|
||||
const countText = (text: string) => {
|
||||
const inputText = text;
|
||||
const font = "600 13px open sans";
|
||||
const canvas = document.createElement("canvas");
|
||||
const context = canvas.getContext("2d");
|
||||
if (context) context.font = font;
|
||||
return { id: text, width: context?.measureText(inputText).width };
|
||||
};
|
||||
|
||||
const countItemsAndGaps = (
|
||||
texts: {
|
||||
id: string;
|
||||
width: number | undefined;
|
||||
}[],
|
||||
) => {
|
||||
const result: {
|
||||
type: string;
|
||||
length?: number;
|
||||
start: number;
|
||||
end: number;
|
||||
id?: string;
|
||||
}[] = [];
|
||||
|
||||
texts.forEach(({ id, width }) => {
|
||||
if (!result.length)
|
||||
result.push(
|
||||
{
|
||||
type: "gap",
|
||||
length: paddingGap,
|
||||
start: 0,
|
||||
end: paddingGap + wrapperPadding,
|
||||
},
|
||||
{
|
||||
id,
|
||||
type: "item",
|
||||
length: width,
|
||||
start: paddingGap,
|
||||
end: width ? paddingGap + width : paddingGap,
|
||||
},
|
||||
);
|
||||
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,
|
||||
type: "item",
|
||||
length: width,
|
||||
start: lastItem.end + paddingGap * 2 + flexGap,
|
||||
end: width
|
||||
? lastItem.end + paddingGap * 2 + flexGap + width
|
||||
: lastItem.end + paddingGap * 2 + flexGap,
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
result.push({
|
||||
type: "gap",
|
||||
length: paddingGap,
|
||||
start: result[result.length - 1].end,
|
||||
end: result[result.length - 1].end + paddingGap + wrapperPadding,
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const countParams = (data: TSubmenuItem[], submenuItemsRef: HTMLDivElement) => {
|
||||
const refCurrent = submenuItemsRef;
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
export const countAutoOffset = (
|
||||
data: TSubmenuItem[],
|
||||
submenuItemsRef: HTMLDivElement | null,
|
||||
) => {
|
||||
if (submenuItemsRef) {
|
||||
const [marker, itemsAndGaps, itemOnMarker] = countParams(
|
||||
data,
|
||||
submenuItemsRef,
|
||||
);
|
||||
|
||||
if (itemOnMarker === undefined) return 0;
|
||||
|
||||
if (
|
||||
!Array.isArray(itemOnMarker) &&
|
||||
Array.isArray(itemsAndGaps) &&
|
||||
typeof marker === "number" &&
|
||||
typeof itemOnMarker !== "number"
|
||||
) {
|
||||
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: string,
|
||||
data: TSubmenuItem[],
|
||||
submenuItemsRef: HTMLDivElement | null,
|
||||
) => {
|
||||
if (submenuItemsRef) {
|
||||
const [marker, itemsAndGaps, itemOnMarker] = countParams(
|
||||
data,
|
||||
submenuItemsRef,
|
||||
);
|
||||
|
||||
if (
|
||||
!Array.isArray(itemOnMarker) &&
|
||||
Array.isArray(itemsAndGaps) &&
|
||||
typeof marker === "number" &&
|
||||
typeof itemOnMarker !== "number"
|
||||
) {
|
||||
const [focusedItem] = itemsAndGaps.filter((obj) => obj.id === itemId);
|
||||
const submenuWidth = submenuItemsRef.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;
|
||||
}
|
||||
}
|
||||
};
|
@ -1,90 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { Canvas, Meta, Story } from "@storybook/blocks";
|
||||
|
||||
import Submenu from "./";
|
||||
import * as stories from "./submenu.stories.js";
|
||||
|
||||
<Meta
|
||||
title="Components/Submenu"
|
||||
component={Submenu}
|
||||
argTypes={{
|
||||
data: { required: true },
|
||||
}}
|
||||
/>
|
||||
|
||||
# Submenu
|
||||
|
||||
<Canvas>
|
||||
<Story story={stories.Default} name="Default" />
|
||||
</Canvas>
|
||||
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import {Submenu } from "@docspace/shared/components";
|
||||
```
|
||||
|
||||
```jsx
|
||||
<Submenu
|
||||
data={[
|
||||
{
|
||||
id: "FileInput",
|
||||
name: "File Input",
|
||||
content: (
|
||||
<FileInput
|
||||
accept={[".doc, .docx"]}
|
||||
id="file-input-id"
|
||||
name="demoFileInputName"
|
||||
onInput={function noRefCheck() {}}
|
||||
placeholder="Input file"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "ToggleButton",
|
||||
name: "Toggle Button",
|
||||
content: (
|
||||
<ToggleButton
|
||||
className="toggle className"
|
||||
id="toggle id"
|
||||
label="label text"
|
||||
onChange={() => {}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
startSelect={1}
|
||||
/>
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------- | :-------------: | :------: | :----: | :-----: | ----------------------------------------------------------------------------------- |
|
||||
| `data` | `array` | ✅ | - | - | List of elements |
|
||||
| `startSelect` | `obj`, `number` | - | - | 0 | Object from **`data`** **OR** Its index in **`data`** that will be a default select |
|
@ -1,80 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
// // @ts-expect-error TS(7016): Could not find a declaration file for module 'enzy... Remove this comment to see the full error message
|
||||
// import { mount, shallow } from "enzyme";
|
||||
// import React from "react";
|
||||
|
||||
// import Submenu from "./";
|
||||
// import { testData, testStartSelect } from "./data";
|
||||
|
||||
// const props = {
|
||||
// data: testData,
|
||||
// startSelect: testStartSelect,
|
||||
// };
|
||||
|
||||
// const onlyData = {
|
||||
// data: testData,
|
||||
// };
|
||||
|
||||
describe("<Submenu />", () => {
|
||||
it("renders without error", () => {
|
||||
// const wrapper = mount(<Submenu {...props} />);
|
||||
// // @ts-expect-error TS(2304): Cannot find name 'expect'.
|
||||
// expect(wrapper).toExist(true);
|
||||
});
|
||||
|
||||
// // @ts-expect-error TS(2582): Cannot find name 'it'. Do you need to install type... Remove this comment to see the full error message
|
||||
// it("gets data prop", () => {
|
||||
// const wrapper = mount(<Submenu {...onlyData} />);
|
||||
// // @ts-expect-error TS(2304): Cannot find name 'expect'.
|
||||
// expect(wrapper.prop("data")).toEqual(testData);
|
||||
// });
|
||||
|
||||
// // @ts-expect-error TS(2582): Cannot find name 'it'. Do you need to install type... Remove this comment to see the full error message
|
||||
// it("doesnt render without data prop", () => {
|
||||
// const wrapper = mount(<Submenu {...props} />);
|
||||
// // @ts-expect-error TS(2304): Cannot find name 'expect'.
|
||||
// expect(wrapper).toExist(false);
|
||||
// });
|
||||
|
||||
// // @ts-expect-error TS(2582): Cannot find name 'it'. Do you need to install type... Remove this comment to see the full error message
|
||||
// it("gets startSelect prop", () => {
|
||||
// const wrapper = mount(<Submenu {...props} />);
|
||||
// // @ts-expect-error TS(2304): Cannot find name 'expect'.
|
||||
// expect(wrapper.prop("startSelect")).toEqual(testStartSelect);
|
||||
// });
|
||||
|
||||
// // @ts-expect-error TS(2582): Cannot find name 'it'. Do you need to install type... Remove this comment to see the full error message
|
||||
// it("selects first data item as currentItem without startSelect prop", () => {
|
||||
// const wrapper = shallow(<Submenu {...onlyData} />)
|
||||
// .find("styled-submenu__StyledSubmenuContentWrapper")
|
||||
// .childAt(0);
|
||||
// const currentItemWrapper = shallow(testData[0].content);
|
||||
// // @ts-expect-error TS(2304): Cannot find name 'expect'.
|
||||
// expect(wrapper.debug()).toEqual(currentItemWrapper.debug());
|
||||
// });
|
||||
});
|
@ -478,10 +478,10 @@ const StyledTableCell = styled.div<{ hasAccess?: boolean; checked?: boolean }>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
${({ theme }) =>
|
||||
/* ${({ theme }) =>
|
||||
theme.interfaceDirection === "rtl"
|
||||
? `padding-left: 30px;`
|
||||
: `padding-right: 30px;`}
|
||||
: `padding-right: 30px;`} */
|
||||
|
||||
.react-svg-icon svg {
|
||||
margin-top: 2px;
|
||||
|
@ -1,85 +0,0 @@
|
||||
# TabContainer
|
||||
|
||||
Custom Tabs menu
|
||||
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import { TabContainer } from "@docspace/shared/components";
|
||||
```
|
||||
|
||||
```js
|
||||
const array_items = [
|
||||
{
|
||||
key: "0",
|
||||
title: "Title1",
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
<button>BUTTON</button>
|
||||
</div>
|
||||
<div>
|
||||
<button>BUTTON</button>
|
||||
</div>
|
||||
<div>
|
||||
<button>BUTTON</button>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "1",
|
||||
title: "Title2",
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
<label>LABEL</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>LABEL</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>LABEL</label>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
title: "Title3",
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
<input></input>
|
||||
</div>
|
||||
<div>
|
||||
<input></input>
|
||||
</div>
|
||||
<div>
|
||||
<input></input>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
```jsx
|
||||
<TabContainer>{array_items}</TabContainer>
|
||||
```
|
||||
|
||||
### TabContainer Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| -------------- | :-------: | :------: | :----: | :-----: | ---------------------------------- |
|
||||
| `isDisabled` | `boolean` | - | - | - | Disable the TabContainer |
|
||||
| `selectedItem` | `number` | - | - | `0` | Selected title of tabs container |
|
||||
| `onSelect` | `func` | - | - | - | Triggered when a title is selected |
|
||||
|
||||
### Array Items Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| --------- | :------: | :------: | :----: | :-----: | --------------------- |
|
||||
| `id` | `string` | ✅ | - | - | Index of object array |
|
||||
| `title` | `string` | ✅ | - | - | Tabs title |
|
||||
| `content` | `object` | ✅ | - | - | Content in Tab |
|
@ -1,120 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { Story, ArgsTable, Canvas, Meta } from "@storybook/blocks";
|
||||
import * as stories from "./TabsContainer.stories.tsx";
|
||||
import { TabContainer } from "./TabsContainer.tsx";
|
||||
|
||||
<Meta
|
||||
title="Components/TabContainer"
|
||||
component={TabContainer}
|
||||
parameters={{
|
||||
source: {
|
||||
code: stories.basic,
|
||||
},
|
||||
}}
|
||||
argTypes={{
|
||||
onSelect: { action: "onSelect. Selected items: " },
|
||||
}}
|
||||
/>
|
||||
|
||||
# TabContainer
|
||||
|
||||
Custom Tabs menu
|
||||
|
||||
<Canvas>
|
||||
<Story story={stories.basic} name="Default" />
|
||||
</Canvas>
|
||||
|
||||
<ArgsTable story="Default" />
|
||||
|
||||
### Array Items Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| --------- | :------: | :------: | :----: | :-----: | --------------------- |
|
||||
| `id` | `string` | ✅ | - | - | Index of object array |
|
||||
| `title` | `string` | ✅ | - | - | Tabs title |
|
||||
| `content` | `object` | ✅ | - | - | Content in Tab |
|
||||
|
||||
```js
|
||||
const array_items = [
|
||||
{
|
||||
key: "0",
|
||||
title: "Title1",
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
<button>BUTTON</button>
|
||||
</div>
|
||||
<div>
|
||||
<button>BUTTON</button>
|
||||
</div>
|
||||
<div>
|
||||
<button>BUTTON</button>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "1",
|
||||
title: "Title2",
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
<label>LABEL</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>LABEL</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>LABEL</label>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
title: "Title3",
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
<input></input>
|
||||
</div>
|
||||
<div>
|
||||
<input></input>
|
||||
</div>
|
||||
<div>
|
||||
<input></input>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
```jsx
|
||||
<TabContainer elements={array_items} />
|
||||
```
|
@ -1,493 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { TabsContainer } from "./TabsContainer";
|
||||
import { TElement, TabsContainerProps } from "./TabsContainer.types";
|
||||
|
||||
const meta = {
|
||||
title: "Components/TabsContainer",
|
||||
component: TabsContainer,
|
||||
parameters: {
|
||||
design: {
|
||||
type: "figma",
|
||||
url: "https://www.figma.com/file/ZiW5KSwb4t7Tj6Nz5TducC/UI-Kit-DocSpace-1.0.0?type=design&node-id=638-4439&mode=design&t=TBNCKMQKQMxr44IZ-0",
|
||||
},
|
||||
},
|
||||
} satisfies Meta<typeof TabsContainer>;
|
||||
type Story = StoryObj<typeof TabsContainer>;
|
||||
|
||||
export default meta;
|
||||
|
||||
const arrayItems = [
|
||||
{
|
||||
key: "tab0",
|
||||
title: "Title1",
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
<button type="button">BUTTON</button>
|
||||
<button type="button">BUTTON</button>
|
||||
<button type="button">BUTTON</button>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button">BUTTON</button>
|
||||
<button type="button">BUTTON</button>
|
||||
<button type="button">BUTTON</button>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button">BUTTON</button>
|
||||
<button type="button">BUTTON</button>
|
||||
<button type="button">BUTTON</button>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab1",
|
||||
title: "Title2",
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
<label>LABEL</label> <label>LABEL</label> <label>LABEL</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>LABEL</label> <label>LABEL</label> <label>LABEL</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>LABEL</label> <label>LABEL</label> <label>LABEL</label>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab2",
|
||||
title: "Title3",
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
<input /> <input /> <input />
|
||||
</div>
|
||||
<div>
|
||||
<input /> <input /> <input />
|
||||
</div>
|
||||
<div>
|
||||
<input /> <input /> <input />
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab3",
|
||||
title: "Title4",
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
<button type="button">BUTTON</button>
|
||||
<button type="button">BUTTON</button>
|
||||
<button type="button">BUTTON</button>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button">BUTTON</button>
|
||||
<button type="button">BUTTON</button>
|
||||
<button type="button">BUTTON</button>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button">BUTTON</button>
|
||||
<button type="button">BUTTON</button>
|
||||
<button type="button">BUTTON</button>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab4",
|
||||
title: "Title5",
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
<label>LABEL</label> <label>LABEL</label> <label>LABEL</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>LABEL</label> <label>LABEL</label> <label>LABEL</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>LABEL</label> <label>LABEL</label> <label>LABEL</label>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const scrollArrayItems = [
|
||||
{
|
||||
key: "tab0",
|
||||
title: "First long tab container",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_0 Tab_0 Tab_0</label>
|
||||
<br />
|
||||
<label>Tab_0 Tab_0 Tab_0</label>
|
||||
<br />
|
||||
<label>Tab_0 Tab_0 Tab_0</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab1",
|
||||
title: "Short",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_1 Tab_1 Tab_1</label>
|
||||
<br />
|
||||
<label>Tab_1 Tab_1 Tab_1</label>
|
||||
<br />
|
||||
<label>Tab_1 Tab_1 Tab_1</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab2",
|
||||
title: "Second long tab container",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_2 Tab_2 Tab_2</label>
|
||||
<br />
|
||||
<label>Tab_2 Tab_2 Tab_2</label>
|
||||
<br />
|
||||
<label>Tab_2 Tab_2 Tab_2</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab3",
|
||||
title: "Short2",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_3 Tab_3 Tab_3</label>
|
||||
<br />
|
||||
<label>Tab_3 Tab_3 Tab_3</label>
|
||||
<br />
|
||||
<label>Tab_3 Tab_3 Tab_3</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab4",
|
||||
title: "Third long tab container header",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_4 Tab_4 Tab_4</label>
|
||||
<br />
|
||||
<label>Tab_4 Tab_4 Tab_4</label>
|
||||
<br />
|
||||
<label>Tab_4 Tab_4 Tab_4</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab5",
|
||||
title: "Short3",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_5 Tab_5 Tab_5</label>
|
||||
<br />
|
||||
<label>Tab_5 Tab_5 Tab_5</label>
|
||||
<br />
|
||||
<label>Tab_5 Tab_5 Tab_5</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab6",
|
||||
title: "tab container",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_6 Tab_6 Tab_6</label>
|
||||
<br />
|
||||
<label>Tab_6 Tab_6 Tab_6</label>
|
||||
<br />
|
||||
<label>Tab_6 Tab_6 Tab_6</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab7",
|
||||
title: "Very long tabs-container field",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_7 Tab_7 Tab_7</label>
|
||||
<br />
|
||||
<label>Tab_7 Tab_7 Tab_7</label>
|
||||
<br />
|
||||
<label>Tab_7 Tab_7 Tab_7</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab8",
|
||||
title: "tab container",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_8 Tab_8 Tab_8</label>
|
||||
<br />
|
||||
<label>Tab_8 Tab_8 Tab_8</label>
|
||||
<br />
|
||||
<label>Tab_8 Tab_8 Tab_8</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab9",
|
||||
title: "Short_04",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_9 Tab_9 Tab_9</label>
|
||||
<br />
|
||||
<label>Tab_9 Tab_9 Tab_9</label>
|
||||
<br />
|
||||
<label>Tab_9 Tab_9 Tab_9</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab10",
|
||||
title: "Short__05",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_10 Tab_10 Tab_10</label>
|
||||
<br />
|
||||
<label>Tab_10 Tab_10 Tab_10</label>
|
||||
<br />
|
||||
<label>Tab_10 Tab_10 Tab_10</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab11",
|
||||
title: "TabsContainer",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_11 Tab_11 Tab_11</label>
|
||||
<br />
|
||||
<label>Tab_11 Tab_11 Tab_11</label>
|
||||
<br />
|
||||
<label>Tab_11 Tab_11 Tab_11</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const tabsItems = [
|
||||
{
|
||||
key: "tab0",
|
||||
title: "Title00000000",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_0 Tab_0 Tab_0</label>
|
||||
<br />
|
||||
<label>Tab_0 Tab_0 Tab_0</label>
|
||||
<br />
|
||||
<label>Tab_0 Tab_0 Tab_0</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab1",
|
||||
title: "Title00000001",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_1 Tab_1 Tab_1</label>
|
||||
<br />
|
||||
<label>Tab_1 Tab_1 Tab_1</label>
|
||||
<br />
|
||||
<label>Tab_1 Tab_1 Tab_1</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab2",
|
||||
title: "Title00000002",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_2 Tab_2 Tab_2</label>
|
||||
<br />
|
||||
<label>Tab_2 Tab_2 Tab_2</label>
|
||||
<br />
|
||||
<label>Tab_2 Tab_2 Tab_2</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab3",
|
||||
title: "Title00000003",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_3 Tab_3 Tab_3</label>
|
||||
<br />
|
||||
<label>Tab_3 Tab_3 Tab_3</label>
|
||||
<br />
|
||||
<label>Tab_3 Tab_3 Tab_3</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab4",
|
||||
title: "Title00000004",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_4 Tab_4 Tab_4</label>
|
||||
<br />
|
||||
<label>Tab_4 Tab_4 Tab_4</label>
|
||||
<br />
|
||||
<label>Tab_4 Tab_4 Tab_4</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab5",
|
||||
title: "Title00000005",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_5 Tab_5 Tab_5</label>
|
||||
<br />
|
||||
<label>Tab_5 Tab_5 Tab_5</label>
|
||||
<br />
|
||||
<label>Tab_5 Tab_5 Tab_5</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab6",
|
||||
title: "Title00000006",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_6 Tab_6 Tab_6</label>
|
||||
<br />
|
||||
<label>Tab_6 Tab_6 Tab_6</label>
|
||||
<br />
|
||||
<label>Tab_6 Tab_6 Tab_6</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab7",
|
||||
title: "Title00000007",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_7 Tab_7 Tab_7</label>
|
||||
<br />
|
||||
<label>Tab_7 Tab_7 Tab_7</label>
|
||||
<br />
|
||||
<label>Tab_7 Tab_7 Tab_7</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab8",
|
||||
title: "Title00000008",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_8 Tab_8 Tab_8</label>
|
||||
<br />
|
||||
<label>Tab_8 Tab_8 Tab_8</label>
|
||||
<br />
|
||||
<label>Tab_8 Tab_8 Tab_8</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tab9",
|
||||
title: "Title00000009",
|
||||
content: (
|
||||
<>
|
||||
<label>Tab_9 Tab_9 Tab_9</label>
|
||||
<br />
|
||||
<label>Tab_9 Tab_9 Tab_9</label>
|
||||
<br />
|
||||
<label>Tab_9 Tab_9 Tab_9</label>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const StyledTitle = styled.h5.attrs({ dir: "auto" })`
|
||||
text-align: ${({ theme }) =>
|
||||
theme.interfaceDirection === "rtl" ? `right` : `left`};
|
||||
`;
|
||||
|
||||
const Template = ({ onSelect, ...args }: TabsContainerProps) => {
|
||||
return (
|
||||
<div>
|
||||
<StyledTitle style={{ marginBottom: 20 }}>
|
||||
Base TabsContainer:
|
||||
</StyledTitle>
|
||||
<TabsContainer
|
||||
{...args}
|
||||
onSelect={(index: TElement) => onSelect(index)}
|
||||
selectedItem={arrayItems.indexOf(arrayItems[0])}
|
||||
elements={arrayItems}
|
||||
/>
|
||||
|
||||
<div style={{ marginTop: 32, maxWidth: 430 }}>
|
||||
<StyledTitle style={{ marginTop: 100, marginBottom: 20 }}>
|
||||
Autoscrolling with different tab widths:
|
||||
</StyledTitle>
|
||||
<TabsContainer
|
||||
{...args}
|
||||
selectedItem={3}
|
||||
elements={scrollArrayItems}
|
||||
onSelect={(index: TElement) => onSelect(index)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 32, maxWidth: 430 }}>
|
||||
<StyledTitle style={{ marginTop: 100, marginBottom: 20 }}>
|
||||
Autoscrolling with the same tabs width:
|
||||
</StyledTitle>
|
||||
<TabsContainer
|
||||
{...args}
|
||||
selectedItem={5}
|
||||
elements={tabsItems}
|
||||
onSelect={(index: TElement) => onSelect(index)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const basic: Story = {
|
||||
render: (args) => <Template {...args} />,
|
||||
args: {
|
||||
elements: tabsItems,
|
||||
isDisabled: false,
|
||||
selectedItem: 0,
|
||||
onSelect: () => {},
|
||||
},
|
||||
};
|
@ -1,130 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import styled, { css } from "styled-components";
|
||||
import { NoUserSelect } from "../../utils";
|
||||
import { Base, TColorScheme } from "../../themes";
|
||||
|
||||
import { Scrollbar } from "../scrollbar";
|
||||
|
||||
const StyledScrollbar = styled(Scrollbar)`
|
||||
width: ${(props) => props.theme.tabsContainer.scrollbar.width} !important;
|
||||
height: ${(props) => props.theme.tabsContainer.scrollbar.height} !important;
|
||||
`;
|
||||
|
||||
StyledScrollbar.defaultProps = { theme: Base };
|
||||
|
||||
const NavItem = styled.div`
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
`;
|
||||
|
||||
NavItem.defaultProps = { theme: Base };
|
||||
|
||||
const Label = styled.div<{ isDisabled?: boolean; selected?: boolean }>`
|
||||
height: ${(props) => props.theme.tabsContainer.label.height};
|
||||
border-radius: ${(props) => props.theme.tabsContainer.label.borderRadius};
|
||||
min-width: ${(props) => props.theme.tabsContainer.label.minWidth};
|
||||
width: ${(props) => props.theme.tabsContainer.label.width};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.title_style {
|
||||
text-align: center;
|
||||
padding: ${(props) => props.theme.tabsContainer.label.title.padding};
|
||||
overflow: ${(props) =>
|
||||
props.theme.interfaceDirection === "rtl" ? "visible" : "hidden"};
|
||||
${NoUserSelect};
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.isDisabled &&
|
||||
css`
|
||||
pointer-events: none;
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.selected
|
||||
? css`
|
||||
border: 1px solid transparent;
|
||||
cursor: default;
|
||||
background-color: ${props.theme.tabsContainer.label.backgroundColor};
|
||||
.title_style {
|
||||
color: ${props.theme.tabsContainer.label.title.color};
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: ${props.theme.tabsContainer.label
|
||||
.activeSelectedBackgroundColor};
|
||||
}
|
||||
`
|
||||
: css`
|
||||
border: ${props.theme.tabsContainer.label.border};
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: ${props.theme.tabsContainer.label
|
||||
.hoverBackgroundColor};
|
||||
.title_style {
|
||||
color: ${props.theme.tabsContainer.label.title.hoverColor};
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: ${props.theme.tabsContainer.label
|
||||
.activeBackgroundColor};
|
||||
}
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.isDisabled &&
|
||||
css`
|
||||
${props.selected && `opacity: 0.6;`}
|
||||
.title_style {
|
||||
color: ${props.theme.tabsContainer.label.title.disableColor};
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
Label.defaultProps = { theme: Base };
|
||||
|
||||
const StyledLabelTheme = styled(Label)<{ $currentColorScheme?: TColorScheme }>`
|
||||
background-color: ${(props) =>
|
||||
props.selected && props.$currentColorScheme?.main?.accent} !important;
|
||||
|
||||
.title_style {
|
||||
color: ${(props) =>
|
||||
props.selected && props.$currentColorScheme?.text?.accent};
|
||||
}
|
||||
`;
|
||||
|
||||
export { NavItem, Label, StyledScrollbar, StyledLabelTheme };
|
@ -1,204 +0,0 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import React from "react";
|
||||
|
||||
import { Text } from "../text";
|
||||
|
||||
import {
|
||||
NavItem,
|
||||
StyledLabelTheme,
|
||||
StyledScrollbar,
|
||||
} from "./TabsContainer.styled";
|
||||
import { TElement, TabsContainerProps } from "./TabsContainer.types";
|
||||
|
||||
const TabsContainer = ({
|
||||
elements,
|
||||
isDisabled,
|
||||
onSelect,
|
||||
selectedItem = 0,
|
||||
}: TabsContainerProps) => {
|
||||
const arrayRefs = React.useRef<HTMLDivElement[]>([]);
|
||||
const scrollRef = React.useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const [state, setState] = React.useState({
|
||||
activeTab: selectedItem,
|
||||
onScrollHide: true,
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
const countElements = elements.length;
|
||||
let item = countElements;
|
||||
while (item !== 0) {
|
||||
arrayRefs.current.push();
|
||||
item -= 1;
|
||||
}
|
||||
}, [elements]);
|
||||
|
||||
const getWidthElements = () => {
|
||||
const arrayWidths = [];
|
||||
const length = arrayRefs.current.length - 1;
|
||||
let widthItem = 0;
|
||||
while (length + 1 !== widthItem) {
|
||||
arrayWidths.push(arrayRefs.current[widthItem].offsetWidth);
|
||||
widthItem += 1;
|
||||
}
|
||||
|
||||
return arrayWidths;
|
||||
};
|
||||
|
||||
const setTabPosition = (index: number, currentRef: HTMLDivElement) => {
|
||||
const scrollElement = scrollRef.current;
|
||||
if (!scrollElement) return;
|
||||
|
||||
const arrayOfWidths = getWidthElements(); // get tabs widths
|
||||
const scrollLeft = scrollElement.scrollLeft; // get scroll position relative to left side
|
||||
const staticScroll = scrollElement.scrollWidth; // get static scroll width
|
||||
const containerWidth = scrollElement.clientWidth; // get main container width
|
||||
const currentTabWidth = currentRef.offsetWidth;
|
||||
const marginRight = 8;
|
||||
|
||||
// get tabs of left side
|
||||
let leftTabs = 0;
|
||||
let leftFullWidth = 0;
|
||||
while (leftTabs !== index) {
|
||||
leftTabs += 1;
|
||||
leftFullWidth += arrayOfWidths[leftTabs] + marginRight;
|
||||
}
|
||||
leftFullWidth += arrayOfWidths[0] + marginRight;
|
||||
|
||||
// get tabs of right side
|
||||
let rightTabs = arrayRefs.current.length - 1;
|
||||
let rightFullWidth = 0;
|
||||
while (rightTabs !== index - 1) {
|
||||
rightFullWidth += arrayOfWidths[rightTabs] + marginRight;
|
||||
rightTabs -= 1;
|
||||
}
|
||||
|
||||
// Out of range of left side
|
||||
if (leftFullWidth > containerWidth + scrollLeft) {
|
||||
let prevIndex = index - 1;
|
||||
let widthBlocksInContainer = 0;
|
||||
while (prevIndex !== -1) {
|
||||
widthBlocksInContainer += arrayOfWidths[prevIndex] + marginRight;
|
||||
prevIndex -= 1;
|
||||
}
|
||||
|
||||
const difference = containerWidth - widthBlocksInContainer;
|
||||
const currentContainerWidth = currentTabWidth;
|
||||
|
||||
scrollElement.scrollTo({
|
||||
left: difference * -1 + currentContainerWidth + marginRight,
|
||||
});
|
||||
}
|
||||
// Out of range of left side
|
||||
else if (rightFullWidth > staticScroll - scrollLeft) {
|
||||
scrollElement.scrollTo({ left: staticScroll - rightFullWidth });
|
||||
}
|
||||
};
|
||||
|
||||
const titleClick = (index: number, item: TElement, ref: HTMLDivElement) => {
|
||||
if (state.activeTab !== index) {
|
||||
setState((s) => ({ ...s, activeTab: index }));
|
||||
const newItem = { ...item };
|
||||
delete newItem.content;
|
||||
onSelect?.(newItem);
|
||||
|
||||
setTabPosition(index, ref);
|
||||
}
|
||||
};
|
||||
|
||||
const onClick = (index: number, item: TElement) => {
|
||||
titleClick(index, item, arrayRefs.current[index]);
|
||||
};
|
||||
|
||||
const setPrimaryTabPosition = React.useCallback((index: number) => {
|
||||
const scrollElement = scrollRef.current;
|
||||
if (!scrollElement) return;
|
||||
|
||||
const arrayOfWidths = getWidthElements(); // get tabs widths
|
||||
const marginRight = 8;
|
||||
let rightTabs = arrayRefs.current.length - 1;
|
||||
let rightFullWidth = 0;
|
||||
while (rightTabs !== index - 1) {
|
||||
rightFullWidth += arrayOfWidths[rightTabs] + marginRight;
|
||||
rightTabs -= 1;
|
||||
}
|
||||
rightFullWidth -= marginRight;
|
||||
scrollElement.scrollTo({
|
||||
left: scrollElement.scrollWidth - rightFullWidth,
|
||||
});
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (state.activeTab !== 0 && arrayRefs.current[state.activeTab] !== null) {
|
||||
setPrimaryTabPosition(state.activeTab);
|
||||
}
|
||||
}, [setPrimaryTabPosition, state.activeTab]);
|
||||
|
||||
const onMouseEnter = () => {
|
||||
setState((s) => ({ ...s, onScrollHide: false }));
|
||||
};
|
||||
|
||||
const onMouseLeave = () => {
|
||||
setState((s) => ({ ...s, onScrollHide: true }));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledScrollbar
|
||||
autoHide={state.onScrollHide}
|
||||
className="scrollbar"
|
||||
// @ts-expect-error error from custom scrollbar
|
||||
ref={scrollRef}
|
||||
>
|
||||
<NavItem className="className_items">
|
||||
{elements.map((item, index) => (
|
||||
<StyledLabelTheme
|
||||
id={item.id}
|
||||
onMouseMove={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
ref={(ref) => {
|
||||
if (ref) arrayRefs.current.push(ref);
|
||||
}}
|
||||
onClick={() => onClick(index, item)}
|
||||
key={item.key}
|
||||
selected={state.activeTab === index}
|
||||
isDisabled={isDisabled}
|
||||
>
|
||||
<Text fontWeight={600} className="title_style" fontSize="13px">
|
||||
{item.title}
|
||||
</Text>
|
||||
</StyledLabelTheme>
|
||||
))}
|
||||
</NavItem>
|
||||
</StyledScrollbar>
|
||||
<div className="tabs_body">{elements[state.activeTab].content}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export { TabsContainer };
|
88
packages/shared/components/tabs/README.md
Normal file
88
packages/shared/components/tabs/README.md
Normal file
@ -0,0 +1,88 @@
|
||||
# Tabs
|
||||
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import { Tabs } from "@docspace/shared/components/tabs";
|
||||
```
|
||||
|
||||
```js
|
||||
const array_items = [
|
||||
{
|
||||
id: "0",
|
||||
name: "Title1",
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
<button>BUTTON</button>
|
||||
</div>
|
||||
<div>
|
||||
<button>BUTTON</button>
|
||||
</div>
|
||||
<div>
|
||||
<button>BUTTON</button>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "1",
|
||||
name: "Title2",
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
<label>LABEL</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>LABEL</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>LABEL</label>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "Title3",
|
||||
isDisabled: true;
|
||||
content: (
|
||||
<div>
|
||||
<div>
|
||||
<input></input>
|
||||
</div>
|
||||
<div>
|
||||
<input></input>
|
||||
</div>
|
||||
<div>
|
||||
<input></input>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
```jsx
|
||||
<Tabs items={array_items} />
|
||||
```
|
||||
|
||||
### Tabs Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ---------------- | :--------------------: | :------: | :----: | :-------: | ------------------------------------------------------------------- |
|
||||
| `items` | `array` | ✅ | - | - | Child elements |
|
||||
| `selectedItemId` | `number`, `string` | - | - | - | Selected item id of tabs |
|
||||
| `theme` | `primary`, `secondary` | - | - | `primary` | Theme for displaying tabs |
|
||||
| `stickyTop` | `string` | - | - | - | Tab indentation for sticky positioning |
|
||||
| `onSelect` | `func` | - | - | - | Sets a callback function that is triggered when the tab is selected |
|
||||
|
||||
### Array Items Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------ | :-------: | :------: | :----: | :-----: | ------------------------------------------------------------------------ |
|
||||
| `id` | `string` | ✅ | - | - | Index of object array |
|
||||
| `name` | `string` | ✅ | - | - | Tab text |
|
||||
| `content` | `node` | ✅ | - | - | Content in Tab |
|
||||
| `isDisabled` | `boolean` | - | - | - | State of tab inclusion. State only works for tabs with a secondary theme |
|
||||
| `onClick` | `func` | - | - | - | Triggered when a title is selected |
|
@ -24,6 +24,7 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
export { TabsContainer } from "./TabsContainer";
|
||||
export const INDEX_NOT_FOUND = -1;
|
||||
|
||||
export type { TElement } from "./TabsContainer.types";
|
||||
export const OFFSET_RIGHT = 48;
|
||||
export const OFFSET_LEFT = 48;
|
@ -24,9 +24,7 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { Submenu } from "./Submenu";
|
||||
|
||||
import { TSubmenuItem } from "./Submenu.types";
|
||||
|
||||
export type { TSubmenuItem };
|
||||
export { Submenu };
|
||||
export enum TabsTypes {
|
||||
Primary = "primary",
|
||||
Secondary = "secondary",
|
||||
}
|
@ -25,15 +25,16 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { Meta, StoryObj } from "@storybook/react";
|
||||
import { Submenu } from "./Submenu";
|
||||
import { Tabs } from "./Tabs";
|
||||
|
||||
import { data, startSelect } from "./data";
|
||||
import { SubmenuProps } from "./Submenu.types";
|
||||
import { data } from "./data";
|
||||
import { TabsProps } from "./Tabs.types";
|
||||
import { TabsTypes } from "./Tabs.enums";
|
||||
|
||||
const meta = {
|
||||
title: "Components/Submenu",
|
||||
component: Submenu,
|
||||
} satisfies Meta<typeof Submenu>;
|
||||
title: "Components/Tabs",
|
||||
component: Tabs,
|
||||
} satisfies Meta<typeof Tabs>;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export default meta;
|
||||
@ -48,16 +49,27 @@ const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
</div>
|
||||
);
|
||||
|
||||
const Template = (args: SubmenuProps) => (
|
||||
const Template = (args: TabsProps) => (
|
||||
<Wrapper>
|
||||
<Submenu {...args} />
|
||||
<Tabs {...args} />
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
export const Default: Story = {
|
||||
render: (args) => <Template {...args} />,
|
||||
args: {
|
||||
data,
|
||||
startSelect,
|
||||
items: data,
|
||||
selectedItemId: data[0].id,
|
||||
onSelect: () => {},
|
||||
},
|
||||
};
|
||||
|
||||
export const Secondary: Story = {
|
||||
render: (args) => <Template {...args} />,
|
||||
args: {
|
||||
items: data,
|
||||
type: TabsTypes.Secondary,
|
||||
selectedItemId: data[0].id,
|
||||
onSelect: () => {},
|
||||
},
|
||||
};
|
250
packages/shared/components/tabs/Tabs.styled.ts
Normal file
250
packages/shared/components/tabs/Tabs.styled.ts
Normal file
@ -0,0 +1,250 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import styled, { css } from "styled-components";
|
||||
import { Scrollbar } from "../scrollbar";
|
||||
import { Base } from "../../themes";
|
||||
import { TabsTypes } from "./Tabs.enums";
|
||||
|
||||
export const StyledTabs = styled.div<{
|
||||
stickyTop?: string;
|
||||
}>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.sticky {
|
||||
height: 33px;
|
||||
position: sticky;
|
||||
position: -webkit-sticky;
|
||||
top: ${(props) => (props.stickyTop ? props.stickyTop : 0)};
|
||||
background: ${(props) => props.theme.tabs.backgroundColorPrimary};
|
||||
z-index: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.sticky-indent {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.blur-ahead {
|
||||
position: absolute;
|
||||
height: 32px;
|
||||
width: 60px;
|
||||
pointer-events: none;
|
||||
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(255, 255, 255, 0) 20.48%,
|
||||
${(props) => props.theme.tabs.gradientColor} 100%
|
||||
);
|
||||
transform: matrix(-1, 0, 0, 1, 0, 0);
|
||||
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.blur-back {
|
||||
position: absolute;
|
||||
height: 32px;
|
||||
width: 60px;
|
||||
right: 0;
|
||||
pointer-events: none;
|
||||
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(255, 255, 255, 0) 20.48%,
|
||||
${(props) => props.theme.tabs.gradientColor} 100%
|
||||
);
|
||||
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tabs-body {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledTabs.defaultProps = { theme: Base };
|
||||
|
||||
export const ScrollbarTabs = styled(Scrollbar)<{
|
||||
$type?: TabsTypes;
|
||||
}>`
|
||||
.scroller {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.scroll-body {
|
||||
position: absolute;
|
||||
padding-inline-end: 0 !important;
|
||||
}
|
||||
|
||||
.track {
|
||||
z-index: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.track > .thumb-horizontal {
|
||||
height: 1px !important;
|
||||
}
|
||||
|
||||
.thumb {
|
||||
display: ${(props) =>
|
||||
props.$type === TabsTypes.Primary ? "block" : "none"};
|
||||
background-color: rgba(100, 104, 112, 0.2) !important;
|
||||
}
|
||||
|
||||
.thumb:active,
|
||||
.thumb.dragging {
|
||||
background-color: rgba(6, 22, 38, 0.3) !important;
|
||||
}
|
||||
`;
|
||||
|
||||
export const TabList = styled.div<{
|
||||
$type?: TabsTypes;
|
||||
}>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: inherit;
|
||||
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
|
||||
gap: ${(props) => (props.$type === TabsTypes.Primary ? "20px" : "8px")};
|
||||
|
||||
border-bottom: ${(props) =>
|
||||
props.$type === TabsTypes.Primary &&
|
||||
css`1px solid ${props.theme.tabs.lineColor}`};
|
||||
`;
|
||||
|
||||
TabList.defaultProps = { theme: Base };
|
||||
|
||||
export const Tab = styled.div<{
|
||||
isActive: boolean;
|
||||
isDisabled?: boolean;
|
||||
$type?: TabsTypes;
|
||||
}>`
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
width: max-content;
|
||||
height: inhert;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
cursor: pointer;
|
||||
opacity: ${(props) => (props.isDisabled && props.$type === TabsTypes.Secondary ? 0.6 : 1)};
|
||||
pointer-events: ${(props) => props.isDisabled && props.$type === TabsTypes.Secondary && "none"};
|
||||
user-select: none;
|
||||
|
||||
padding: ${(props) =>
|
||||
props.$type === TabsTypes.Primary ? "4px 0 0 0" : "4px 16px"};
|
||||
|
||||
|
||||
color: ${(props) =>
|
||||
props.$type === TabsTypes.Primary
|
||||
? css`
|
||||
${props.isActive
|
||||
? props.theme.tabs.activeTextColorPrimary ||
|
||||
props.theme.currentColorScheme?.main?.accent
|
||||
: props.theme.tabs.textColorPrimary}
|
||||
`
|
||||
: css`
|
||||
${props.isActive
|
||||
? props.theme.tabs.activeTextColorSecondary
|
||||
: props.theme.tabs.textColorSecondary}
|
||||
`};
|
||||
|
||||
background-color: ${(props) =>
|
||||
props.$type === TabsTypes.Secondary &&
|
||||
css`
|
||||
${props.isActive
|
||||
? props.theme.tabs.activeBackgroundColorSecondary
|
||||
: props.theme.tabs.backgroundColorSecondary}
|
||||
`};
|
||||
|
||||
border: ${(props) =>
|
||||
props.$type === TabsTypes.Secondary &&
|
||||
css`1px solid ${props.theme.tabs.lineColor}`};}
|
||||
|
||||
border-radius: ${(props) => props.$type === TabsTypes.Secondary && "16px"};
|
||||
|
||||
&:hover {
|
||||
color: ${(props) =>
|
||||
props.$type === TabsTypes.Primary &&
|
||||
!props.isActive &&
|
||||
props.theme.tabs.hoverTextColorPrimary};
|
||||
|
||||
opacity: ${(props) =>
|
||||
props.$type === TabsTypes.Primary && props.isActive && 0.85};
|
||||
|
||||
background-color: ${(props) =>
|
||||
props.$type === TabsTypes.Secondary &&
|
||||
!props.isActive &&
|
||||
props.theme.tabs.hoverBackgroundColorSecondary};
|
||||
};
|
||||
|
||||
&:active {
|
||||
color: ${(props) =>
|
||||
props.$type === TabsTypes.Primary &&
|
||||
!props.isActive &&
|
||||
props.theme.tabs.pressedTextColorPrimary};
|
||||
|
||||
opacity: ${(props) =>
|
||||
props.$type === TabsTypes.Primary && props.isActive && 1};
|
||||
|
||||
background-color: ${(props) =>
|
||||
props.$type === TabsTypes.Secondary &&
|
||||
!props.isActive &&
|
||||
props.theme.tabs.pressedBackgroundColorSecondary};
|
||||
};
|
||||
`;
|
||||
|
||||
Tab.defaultProps = { theme: Base };
|
||||
|
||||
export const TabSubLine = styled.div<{
|
||||
isActive?: boolean;
|
||||
$type?: TabsTypes;
|
||||
}>`
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
bottom: 0px;
|
||||
border-radius: 4px 4px 0 0;
|
||||
border-radius: 4px 4px 0 0;
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
display: ${(props) => props.$type === TabsTypes.Secondary && "none"};
|
||||
|
||||
background-color: ${(props) =>
|
||||
props.$type === TabsTypes.Primary && props.isActive
|
||||
? props.theme.currentColorScheme?.main?.accent
|
||||
: "transparent"};
|
||||
`;
|
@ -24,17 +24,16 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
// import React from "react";
|
||||
/* import { screen, render } from "@testing-library/react";
|
||||
import "@testing-library/jest-dom";
|
||||
|
||||
// import { screen, render } from "@testing-library/react";
|
||||
// import "@testing-library/jest-dom";
|
||||
import { Tabs } from "./Tabs";
|
||||
import { TTabItem } from "./Tabs.types"; */
|
||||
|
||||
// import { TabsContainer } from "./TabsContainer";
|
||||
|
||||
// const ArrayItems = [
|
||||
// const arrayItems: TTabItem[] = [
|
||||
// {
|
||||
// key: "tab0",
|
||||
// title: "Title1",
|
||||
// id: "tab0",
|
||||
// name: "Title1",
|
||||
// content: (
|
||||
// <div>
|
||||
// <button>BUTTON</button>
|
||||
@ -44,8 +43,8 @@
|
||||
// ),
|
||||
// },
|
||||
// {
|
||||
// key: "tab1",
|
||||
// title: "Title2",
|
||||
// id: "tab1",
|
||||
// name: "Title2",
|
||||
// content: (
|
||||
// <div>
|
||||
// <label>LABEL</label>
|
||||
@ -55,19 +54,20 @@
|
||||
// ),
|
||||
// },
|
||||
// {
|
||||
// key: "tab2",
|
||||
// title: "Title3",
|
||||
// id: "tab2",
|
||||
// name: "Title3",
|
||||
// content: (
|
||||
// <div>
|
||||
// <input></input>
|
||||
// <input></input>
|
||||
// <input></input>
|
||||
// <input />
|
||||
// <input />
|
||||
// <input />
|
||||
// </div>
|
||||
// ),
|
||||
// },
|
||||
// {
|
||||
// key: "tab3",
|
||||
// title: "Title4",
|
||||
// id: "tab3",
|
||||
// name: "Title4",
|
||||
// isDisabled: true,
|
||||
// content: (
|
||||
// <div>
|
||||
// <button>BUTTON</button>
|
||||
@ -77,8 +77,8 @@
|
||||
// ),
|
||||
// },
|
||||
// {
|
||||
// key: "tab4",
|
||||
// title: "Title5",
|
||||
// id: "tab4",
|
||||
// name: "Title5",
|
||||
// content: (
|
||||
// <div>
|
||||
// <label>LABEL</label>
|
||||
@ -89,49 +89,28 @@
|
||||
// },
|
||||
// ];
|
||||
|
||||
describe("<TabContainer />", () => {
|
||||
describe("<Tabs />", () => {
|
||||
it("renders without error", () => {
|
||||
// render(
|
||||
// <TabContainer
|
||||
// elements={[
|
||||
// {
|
||||
// key: "0",
|
||||
// title: "Title1",
|
||||
// content: (
|
||||
// <div>
|
||||
// <button>BUTTON</button>
|
||||
// <button>BUTTON</button>
|
||||
// <button>BUTTON</button>
|
||||
// </div>
|
||||
// ),
|
||||
// },
|
||||
// ]}
|
||||
// />,
|
||||
// );
|
||||
// render(<Tabs items={arrayItems} />);
|
||||
// expect(wrapper).toExist();
|
||||
});
|
||||
|
||||
// // @ts-expect-error TS(2582): Cannot find name 'it'. Do you need to install type... Remove this comment to see the full error message
|
||||
// it("TabsContainer not re-render test", () => {
|
||||
// // @ts-expect-error TS(2322): Type '{ elements: { key: string; title: string; co... Remove this comment to see the full error message
|
||||
// const wrapper = mount(<TabContainer elements={array_items} />).instance();
|
||||
// const shouldUpdate = wrapper.shouldComponentUpdate(
|
||||
// wrapper.props,
|
||||
// wrapper.state,
|
||||
// );
|
||||
// // @ts-expect-error TS(2304): Cannot find name 'expect'.
|
||||
// expect(shouldUpdate).toBe(false);
|
||||
// });
|
||||
// it("Tabs not re-render test", () => {
|
||||
// const wrapper = mount(<Tabs items={arrayItems} />).instance();
|
||||
// const shouldUpdate = wrapper.shouldComponentUpdate(
|
||||
// wrapper.props,
|
||||
// wrapper.state,
|
||||
// );
|
||||
|
||||
// // @ts-expect-error TS(2582): Cannot find name 'it'. Do you need to install type... Remove this comment to see the full error message
|
||||
// it("TabsContainer not re-render test", () => {
|
||||
// // @ts-expect-error TS(2322): Type '{ elements: { key: string; title: string; co... Remove this comment to see the full error message
|
||||
// const wrapper = mount(<TabContainer elements={array_items} />).instance();
|
||||
// const shouldUpdate = wrapper.shouldComponentUpdate(wrapper.props, {
|
||||
// ...wrapper.state,
|
||||
// activeTab: 3,
|
||||
// expect(shouldUpdate).toBe(false);
|
||||
// });
|
||||
|
||||
// it("Tabs not re-render test", () => {
|
||||
// const wrapper = mount(<Tabs items={arrayItems} />).instance();
|
||||
// const shouldUpdate = wrapper.shouldComponentUpdate(wrapper.props, {
|
||||
// ...wrapper.state,
|
||||
// activeTab: 3,
|
||||
// });
|
||||
// expect(shouldUpdate).toBe(true);
|
||||
// });
|
||||
// // @ts-expect-error TS(2304): Cannot find name 'expect'.
|
||||
// expect(shouldUpdate).toBe(true);
|
||||
// });
|
||||
});
|
139
packages/shared/components/tabs/Tabs.tsx
Normal file
139
packages/shared/components/tabs/Tabs.tsx
Normal file
@ -0,0 +1,139 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
|
||||
import { Scrollbar as ScrollbarType } from "../scrollbar/custom-scrollbar";
|
||||
|
||||
import { useViewTab } from "./hooks/useViewTab";
|
||||
|
||||
import {
|
||||
ScrollbarTabs,
|
||||
StyledTabs,
|
||||
Tab,
|
||||
TabList,
|
||||
TabSubLine,
|
||||
} from "./Tabs.styled";
|
||||
import { TabsProps, TTabItem } from "./Tabs.types";
|
||||
import { TabsTypes } from "./Tabs.enums";
|
||||
import { OFFSET_RIGHT, OFFSET_LEFT, INDEX_NOT_FOUND } from "./Tabs.constants";
|
||||
|
||||
const Tabs = (props: TabsProps) => {
|
||||
const {
|
||||
items,
|
||||
selectedItemId,
|
||||
type = TabsTypes.Primary,
|
||||
stickyTop,
|
||||
onSelect,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
let selectedItemIndex = items.findIndex((item) => item.id === selectedItemId);
|
||||
if (selectedItemIndex === INDEX_NOT_FOUND) {
|
||||
selectedItemIndex = 0;
|
||||
}
|
||||
|
||||
const [currentItem, setCurrentItem] = useState<TTabItem>(
|
||||
items[selectedItemIndex],
|
||||
);
|
||||
|
||||
const tabsRef = useRef<HTMLDivElement>(null);
|
||||
const scrollRef = useRef<ScrollbarType>(null);
|
||||
|
||||
const isViewFirstTab = useViewTab(scrollRef, tabsRef, 0);
|
||||
const isViewLastTab = useViewTab(scrollRef, tabsRef, items.length - 1);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentItem(items[selectedItemIndex]);
|
||||
}, [selectedItemIndex, items]);
|
||||
|
||||
const scrollToTab = (index: number): void => {
|
||||
if (!scrollRef.current || !tabsRef.current) return;
|
||||
|
||||
const containerElement = scrollRef.current.scrollerElement;
|
||||
const tabElement = tabsRef.current.children[index] as HTMLDivElement;
|
||||
|
||||
if (!containerElement || !tabElement) return;
|
||||
|
||||
const containerWidth = containerElement.offsetWidth;
|
||||
const tabWidth = tabElement?.offsetWidth;
|
||||
const tabOffsetLeft = tabElement.offsetLeft;
|
||||
|
||||
if (tabOffsetLeft - OFFSET_LEFT < containerElement.scrollLeft) {
|
||||
scrollRef.current.scrollTo(tabOffsetLeft - OFFSET_LEFT);
|
||||
} else if (
|
||||
tabOffsetLeft + tabWidth >
|
||||
containerElement.scrollLeft + containerWidth
|
||||
) {
|
||||
scrollRef.current.scrollTo(
|
||||
tabOffsetLeft - containerWidth + tabWidth + OFFSET_RIGHT,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const setSelectedItem = (selectedTabItem: TTabItem, index: number): void => {
|
||||
setCurrentItem(selectedTabItem);
|
||||
scrollToTab(index);
|
||||
onSelect?.(selectedTabItem);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledTabs {...rest} stickyTop={stickyTop}>
|
||||
<div className="sticky">
|
||||
{!isViewFirstTab && <div className="blur-ahead" />}
|
||||
<ScrollbarTabs ref={scrollRef} autoHide={false} noScrollY $type={type}>
|
||||
<TabList ref={tabsRef} $type={type}>
|
||||
{items.map((item, index) => {
|
||||
const isActive = item.id === currentItem.id;
|
||||
return (
|
||||
<Tab
|
||||
key={item.id}
|
||||
isActive={isActive}
|
||||
isDisabled={item?.isDisabled}
|
||||
$type={type}
|
||||
onClick={() => {
|
||||
item.onClick?.();
|
||||
setSelectedItem(item, index);
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
<TabSubLine isActive={isActive} $type={type} />
|
||||
</Tab>
|
||||
);
|
||||
})}
|
||||
</TabList>
|
||||
</ScrollbarTabs>
|
||||
{!isViewLastTab && <div className="blur-back" />}
|
||||
</div>
|
||||
|
||||
<div className="sticky-indent" />
|
||||
|
||||
<div className="tabs-body">{currentItem?.content}</div>
|
||||
</StyledTabs>
|
||||
);
|
||||
};
|
||||
|
||||
export { Tabs };
|
@ -24,21 +24,30 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
export type TSubmenuItem = {
|
||||
import { TabsTypes } from "./Tabs.enums";
|
||||
|
||||
export type TTabItem = {
|
||||
/** Element id. */
|
||||
id: string;
|
||||
/** Tab text. */
|
||||
name: string;
|
||||
/** Content that is shown when you click on the tab. */
|
||||
content: React.ReactNode;
|
||||
/** State of tab inclusion. State only works for tabs with a secondary theme. */
|
||||
isDisabled?: boolean;
|
||||
/** Sets a callback function that is triggered when the tab is selected */
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export interface SubmenuProps {
|
||||
/** List of the elements */
|
||||
data: TSubmenuItem[];
|
||||
/** Specifies the first item or the item's index to be displayed in the submenu. */
|
||||
startSelect: number | TSubmenuItem;
|
||||
/** Property that allows explicitly selecting content passed through an external operation */
|
||||
forsedActiveItemId?: number | string;
|
||||
/** Sets a callback function that is triggered when the submenu item is selected */
|
||||
onSelect?: (item: TSubmenuItem) => void;
|
||||
topProps?: string;
|
||||
export interface TabsProps {
|
||||
/** Child elements. */
|
||||
items: TTabItem[];
|
||||
/** Selected item of tabs. */
|
||||
selectedItemId?: number | string;
|
||||
/** Theme for displaying tabs. */
|
||||
type?: TabsTypes;
|
||||
/** Tab indentation for sticky positioning. */
|
||||
stickyTop?: string;
|
||||
/** Sets a callback function that is triggered when the tab is selected. */
|
||||
onSelect?: (element: TTabItem) => void;
|
||||
}
|
@ -24,14 +24,13 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import React from "react";
|
||||
|
||||
import { TTabItem } from "./Tabs.types";
|
||||
import { FileInput } from "../file-input";
|
||||
import { Row } from "../row";
|
||||
import { Text } from "../text";
|
||||
import { InputSize } from "../text-input";
|
||||
|
||||
export const data = [
|
||||
export const data: TTabItem[] = [
|
||||
{
|
||||
id: "Overview",
|
||||
name: "Overview",
|
||||
@ -86,13 +85,14 @@ export const data = [
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "Time tracking",
|
||||
name: "Time tracking",
|
||||
id: "Time",
|
||||
name: "Time",
|
||||
content: <p>Time tracking</p>,
|
||||
},
|
||||
{
|
||||
id: "Contacts",
|
||||
name: "Contacts",
|
||||
isDisabled: true,
|
||||
content: <p>Contacts</p>,
|
||||
},
|
||||
{
|
||||
@ -101,20 +101,3 @@ export const data = [
|
||||
content: <p>Team</p>,
|
||||
},
|
||||
];
|
||||
|
||||
export const startSelect = data[2];
|
||||
|
||||
export const testData = [
|
||||
{
|
||||
id: "Tab1",
|
||||
name: "Tab1",
|
||||
content: <p>1</p>,
|
||||
},
|
||||
{
|
||||
id: "Tab2",
|
||||
name: "Tab2",
|
||||
content: <p>2</p>,
|
||||
},
|
||||
];
|
||||
|
||||
export const testStartSelect = testData[1];
|
64
packages/shared/components/tabs/hooks/useViewTab.ts
Normal file
64
packages/shared/components/tabs/hooks/useViewTab.ts
Normal file
@ -0,0 +1,64 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.s
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { useEffect, useState, useRef, RefObject } from "react";
|
||||
|
||||
import { Scrollbar as ScrollbarType } from "../../scrollbar/custom-scrollbar";
|
||||
|
||||
export const useViewTab = (
|
||||
containerRef: RefObject<ScrollbarType>,
|
||||
tabRef: RefObject<HTMLDivElement>,
|
||||
index: number,
|
||||
) => {
|
||||
const [isViewTab, setIsViewTab] = useState<boolean>(true);
|
||||
const observerRef = useRef<IntersectionObserver>();
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current?.scrollerElement;
|
||||
const trackedElement = tabRef.current?.children[index];
|
||||
if (!container || !trackedElement) return;
|
||||
|
||||
const observerCallback: IntersectionObserverCallback = ([entry]) => {
|
||||
setIsViewTab(entry.isIntersecting);
|
||||
};
|
||||
|
||||
observerRef.current = new IntersectionObserver(observerCallback, {
|
||||
root: container,
|
||||
rootMargin: "4px",
|
||||
threshold: 1,
|
||||
});
|
||||
|
||||
observerRef.current.observe(trackedElement);
|
||||
|
||||
return () => {
|
||||
if (observerRef.current) {
|
||||
observerRef.current.unobserve(trackedElement);
|
||||
}
|
||||
};
|
||||
}, [containerRef, index, tabRef]);
|
||||
|
||||
return isViewTab;
|
||||
};
|
@ -24,20 +24,10 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
export type TElement = {
|
||||
id?: string;
|
||||
key: string;
|
||||
title: string;
|
||||
content: React.ReactNode;
|
||||
};
|
||||
import { Tabs } from "./Tabs";
|
||||
import { TabsTypes } from "./Tabs.enums";
|
||||
import { type TTabItem, type TabsProps } from "./Tabs.types";
|
||||
|
||||
export interface TabsContainerProps {
|
||||
/** Child elements */
|
||||
elements: TElement[];
|
||||
/** Disables the TabContainer */
|
||||
isDisabled: boolean;
|
||||
/** Sets a callback function that is triggered when the title is selected */
|
||||
onSelect: (element: TElement) => void;
|
||||
/** Selected title of tabs container */
|
||||
selectedItem: number;
|
||||
}
|
||||
export { Tabs };
|
||||
export { TabsTypes };
|
||||
export { TTabItem, TabsProps };
|
@ -976,36 +976,6 @@ export const getBaseTheme = () => {
|
||||
},
|
||||
},
|
||||
|
||||
tabsContainer: {
|
||||
scrollbar: {
|
||||
width: "100%",
|
||||
height: "44px",
|
||||
},
|
||||
|
||||
label: {
|
||||
height: "30px",
|
||||
border: `1px solid ${grayLightMid}`,
|
||||
borderRadius: "16px",
|
||||
minWidth: "fit-content",
|
||||
marginRight: "8px",
|
||||
width: "fit-content",
|
||||
|
||||
backgroundColor: blueLightMid,
|
||||
hoverBackgroundColor: lightGrayHover,
|
||||
disableBackgroundColor: grayLightMid,
|
||||
activeBackgroundColor: grayLightMid,
|
||||
activeSelectedBackgroundColor: `linear-gradient(0deg, ${blueLightMid}, ${blueLightMid}), linear-gradient(0deg, ${onWhiteColor}, ${onWhiteColor})`,
|
||||
|
||||
title: {
|
||||
padding: "4px 16px",
|
||||
overflow: "hidden",
|
||||
color: white,
|
||||
hoverColor: black,
|
||||
disableColor: grayStrong,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
fieldContainer: {
|
||||
horizontal: {
|
||||
margin: "0 0 16px 0",
|
||||
@ -2148,8 +2118,10 @@ export const getBaseTheme = () => {
|
||||
hoverBackground: grayLight,
|
||||
selectedBackground: lightGrayHover,
|
||||
|
||||
inputButtonBorder: grayStrong,
|
||||
inputButtonBorderHover: lightGrayDark,
|
||||
inputButtonBorder: "#D0D5DA",
|
||||
inputButtonBorderHover: grayMain,
|
||||
|
||||
disableTextColor: "#A3A9AE",
|
||||
},
|
||||
|
||||
emptyScreen: {
|
||||
@ -2964,12 +2936,23 @@ export const getBaseTheme = () => {
|
||||
background: white,
|
||||
},
|
||||
|
||||
submenu: {
|
||||
lineColor: grayLightMid,
|
||||
backgroundColor: white,
|
||||
textColor: lightGrayDark,
|
||||
activeTextColor: link,
|
||||
bottomLineColor: link,
|
||||
tabs: {
|
||||
gradientColor: white,
|
||||
lineColor: "#eceef1",
|
||||
|
||||
textColorPrimary: "#657077",
|
||||
activeTextColorPrimary: "",
|
||||
hoverTextColorPrimary: "#A3A9AE",
|
||||
pressedTextColorPrimary: "#555F65",
|
||||
backgroundColorPrimary: white,
|
||||
|
||||
textColorSecondary: "#333333",
|
||||
activeTextColorSecondary: white,
|
||||
|
||||
backgroundColorSecondary: white,
|
||||
hoverBackgroundColorSecondary: "#F3F4F4",
|
||||
pressedBackgroundColorSecondary: "#ECEEF1",
|
||||
activeBackgroundColorSecondary: "#265A8F",
|
||||
},
|
||||
|
||||
hotkeys: {
|
||||
|
@ -960,36 +960,6 @@ const Dark: TTheme = {
|
||||
},
|
||||
},
|
||||
|
||||
tabsContainer: {
|
||||
scrollbar: {
|
||||
width: "100%",
|
||||
height: "44px",
|
||||
},
|
||||
|
||||
label: {
|
||||
height: " 30px",
|
||||
border: `1px solid ${grayDarkStrong}`,
|
||||
borderRadius: "16px",
|
||||
minWidth: "fit-content",
|
||||
marginRight: "8px",
|
||||
width: "fit-content",
|
||||
|
||||
backgroundColor: white,
|
||||
hoverBackgroundColor: grayDarkStrong,
|
||||
disableBackgroundColor: darkGrayLight,
|
||||
activeBackgroundColor: darkGrayLight,
|
||||
activeSelectedBackgroundColor: `linear-gradient(0deg, ${white}, ${white}), linear-gradient(0deg, rgba(0, 0, 0, 0.18), rgba(0, 0, 0, 0.18))`,
|
||||
|
||||
title: {
|
||||
padding: "4px 16px",
|
||||
overflow: "hidden",
|
||||
color: black,
|
||||
hoverColor: white,
|
||||
disableColor: grayDarkStrong,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
fieldContainer: {
|
||||
horizontal: {
|
||||
margin: "0 0 16px 0",
|
||||
@ -2118,8 +2088,8 @@ const Dark: TTheme = {
|
||||
border: `1px solid ${grayDarkStrong}`,
|
||||
|
||||
breadCrumbs: {
|
||||
prevItemColor: darkGrayDark,
|
||||
arrowRightColor: darkGrayDark,
|
||||
prevItemColor: "#CCCCCC",
|
||||
arrowRightColor: "#ADADAD",
|
||||
},
|
||||
|
||||
info: {
|
||||
@ -2133,8 +2103,10 @@ const Dark: TTheme = {
|
||||
hoverBackground: lightDarkGrayHover,
|
||||
selectedBackground: lightDarkGrayHover,
|
||||
|
||||
inputButtonBorder: grayDarkStrong,
|
||||
inputButtonBorderHover: white,
|
||||
inputButtonBorder: "#474747",
|
||||
inputButtonBorderHover: grayMaxLight,
|
||||
|
||||
disableTextColor: "#858585",
|
||||
},
|
||||
|
||||
emptyScreen: {
|
||||
@ -2946,12 +2918,23 @@ const Dark: TTheme = {
|
||||
background: black,
|
||||
},
|
||||
|
||||
submenu: {
|
||||
lineColor: grayDarkStrong,
|
||||
backgroundColor: black,
|
||||
activeTextColor: white,
|
||||
textColor: darkGrayDark,
|
||||
bottomLineColor: darkLink,
|
||||
tabs: {
|
||||
gradientColor: black,
|
||||
lineColor: "#474747",
|
||||
|
||||
textColorPrimary: "#657077",
|
||||
activeTextColorPrimary: white,
|
||||
hoverTextColorPrimary: white,
|
||||
pressedTextColorPrimary: "#CCCCCC",
|
||||
backgroundColorPrimary: "#333",
|
||||
|
||||
textColorSecondary: "#FFFFFF",
|
||||
activeTextColorSecondary: "#333333",
|
||||
|
||||
backgroundColorSecondary: "#333",
|
||||
hoverBackgroundColorSecondary: "#474747",
|
||||
pressedBackgroundColorSecondary: "#282828",
|
||||
activeBackgroundColorSecondary: "#FFFFFF",
|
||||
},
|
||||
|
||||
hotkeys: {
|
||||
|
@ -1054,25 +1054,41 @@ export const mapCulturesToArray = (
|
||||
isBetaBadge: boolean = true,
|
||||
i18nArg?: I18n,
|
||||
) => {
|
||||
let t = null;
|
||||
|
||||
if (i18nArg) {
|
||||
const t = i18nArg.getFixedT(null, "Common");
|
||||
return culturesArg.map((culture, index) => {
|
||||
return {
|
||||
key: culture,
|
||||
label: t(`Culture_${culture}`),
|
||||
icon: flagsIcons?.get(`${culture}.react.svg`),
|
||||
...(isBetaBadge && { isBeta: isBetaLanguage(culture) }),
|
||||
index,
|
||||
};
|
||||
});
|
||||
t = i18nArg.getFixedT(null, "Common");
|
||||
}
|
||||
|
||||
return culturesArg.map((culture, index) => {
|
||||
return {
|
||||
key: culture,
|
||||
icon: flagsIcons?.get(`${culture}.react.svg`),
|
||||
index,
|
||||
};
|
||||
let iconName = culture;
|
||||
|
||||
switch (culture) {
|
||||
case "sr-Cyrl-RS":
|
||||
case "sr-Latn-RS":
|
||||
iconName = "sr";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const icon = flagsIcons?.get(`${iconName}.react.svg`);
|
||||
|
||||
const cultureObj = t
|
||||
? {
|
||||
key: culture,
|
||||
label: t(`Culture_${culture}`),
|
||||
icon,
|
||||
...(isBetaBadge && { isBeta: isBetaLanguage(culture) }),
|
||||
index,
|
||||
}
|
||||
: {
|
||||
key: culture,
|
||||
icon,
|
||||
index,
|
||||
};
|
||||
|
||||
return cultureObj;
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -53,7 +53,7 @@ import RuReactSvgUrl from "PUBLIC_DIR/images/flags/ru.react.svg?url";
|
||||
import SkReactSvgUrl from "PUBLIC_DIR/images/flags/sk.react.svg?url";
|
||||
import SlReactSvgUrl from "PUBLIC_DIR/images/flags/sl.react.svg?url";
|
||||
import SiReactSvgUrl from "PUBLIC_DIR/images/flags/si.react.svg?url";
|
||||
import SrLatnRSReactSvgUrl from "PUBLIC_DIR/images/flags/sr-Latn-RS.react.svg?url";
|
||||
import SrReactSvgUrl from "PUBLIC_DIR/images/flags/sr.react.svg?url";
|
||||
import TrReactSvgUrl from "PUBLIC_DIR/images/flags/tr.react.svg?url";
|
||||
import UkUAReactSvgUrl from "PUBLIC_DIR/images/flags/uk-UA.react.svg?url";
|
||||
import ViReactSvgUrl from "PUBLIC_DIR/images/flags/vi.react.svg?url";
|
||||
@ -89,7 +89,7 @@ export const flagsIcons = new Map([
|
||||
["sk.react.svg", SkReactSvgUrl],
|
||||
["sl.react.svg", SlReactSvgUrl],
|
||||
["si.react.svg", SiReactSvgUrl],
|
||||
["sr-Latn-RS.react.svg", SrLatnRSReactSvgUrl],
|
||||
["sr.react.svg", SrReactSvgUrl],
|
||||
["tr.react.svg", TrReactSvgUrl],
|
||||
["uk-UA.react.svg", UkUAReactSvgUrl],
|
||||
["vi.react.svg", ViReactSvgUrl],
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Loading…
Reference in New Issue
Block a user