190 display ctrl instead of for windows users (#9617)
Closes https://github.com/twentyhq/core-team-issues/issues/190 <img width="226" alt="Capture d’écran 2025-01-15 à 12 07 12" src="https://github.com/user-attachments/assets/b9a13746-2629-477a-9795-cda03c63f8f6" /> To test, update the user agent in your browser dev tools: <img width="459" alt="Capture d’écran 2025-01-15 à 12 14 29" src="https://github.com/user-attachments/assets/4371d5fc-fd3c-403d-beaa-7ba58019d3c9" />
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
import { Button } from 'twenty-ui';
|
import { RightDrawerHotkeyScope } from '@/ui/layout/right-drawer/types/RightDrawerHotkeyScope';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
import { RightDrawerHotkeyScope } from '@/ui/layout/right-drawer/types/RightDrawerHotkeyScope';
|
import { Button, getOsControlSymbol } from 'twenty-ui';
|
||||||
|
|
||||||
export const CmdEnterActionButton = ({
|
export const CmdEnterActionButton = ({
|
||||||
title,
|
title,
|
||||||
@ -24,7 +24,7 @@ export const CmdEnterActionButton = ({
|
|||||||
accent="blue"
|
accent="blue"
|
||||||
size="medium"
|
size="medium"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
shortcut={'⌘⏎'}
|
hotkeys={[getOsControlSymbol(), '⏎']}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { IconLayoutSidebarRightExpand } from 'twenty-ui';
|
import { IconLayoutSidebarRightExpand, getOsControlSymbol } from 'twenty-ui';
|
||||||
|
|
||||||
const StyledButton = styled.div`
|
const StyledButton = styled.div`
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
@ -46,7 +46,7 @@ export const RecordIndexActionMenuBarAllActionsButton = () => {
|
|||||||
<IconLayoutSidebarRightExpand size={theme.icon.size.md} />
|
<IconLayoutSidebarRightExpand size={theme.icon.size.md} />
|
||||||
<StyledButtonLabel>All Actions</StyledButtonLabel>
|
<StyledButtonLabel>All Actions</StyledButtonLabel>
|
||||||
<StyledSeparator size="sm" />
|
<StyledSeparator size="sm" />
|
||||||
<StyledShortcutLabel>⌘K</StyledShortcutLabel>
|
<StyledShortcutLabel>{getOsControlSymbol()}K</StyledShortcutLabel>
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/
|
|||||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
import { Button, MenuItem } from 'twenty-ui';
|
import { Button, MenuItem, getOsControlSymbol } from 'twenty-ui';
|
||||||
import { FeatureFlagKey } from '~/generated/graphql';
|
import { FeatureFlagKey } from '~/generated/graphql';
|
||||||
|
|
||||||
export const RightDrawerActionMenuDropdown = () => {
|
export const RightDrawerActionMenuDropdown = () => {
|
||||||
@ -68,7 +68,9 @@ export const RightDrawerActionMenuDropdown = () => {
|
|||||||
RightDrawerActionMenuDropdownHotkeyScope.RightDrawerActionMenuDropdown,
|
RightDrawerActionMenuDropdownHotkeyScope.RightDrawerActionMenuDropdown,
|
||||||
}}
|
}}
|
||||||
data-select-disable
|
data-select-disable
|
||||||
clickableComponent={<Button title="Actions" shortcut="⌘O" />}
|
clickableComponent={
|
||||||
|
<Button title="Actions" hotkeys={[getOsControlSymbol(), 'O']} />
|
||||||
|
}
|
||||||
dropdownPlacement="top-end"
|
dropdownPlacement="top-end"
|
||||||
dropdownOffset={{
|
dropdownOffset={{
|
||||||
y: parseInt(theme.spacing(2), 10),
|
y: parseInt(theme.spacing(2), 10),
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
|
import { getOsControlSymbol } from 'twenty-ui';
|
||||||
import { Shortcut, ShortcutType } from '../types/Shortcut';
|
import { Shortcut, ShortcutType } from '../types/Shortcut';
|
||||||
|
|
||||||
export const KEYBOARD_SHORTCUTS_GENERAL: Shortcut[] = [
|
export const KEYBOARD_SHORTCUTS_GENERAL: Shortcut[] = [
|
||||||
{
|
{
|
||||||
label: 'Open search',
|
label: 'Open search',
|
||||||
type: ShortcutType.General,
|
type: ShortcutType.General,
|
||||||
firstHotKey: '⌘',
|
firstHotKey: getOsControlSymbol(),
|
||||||
secondHotKey: 'K',
|
secondHotKey: 'K',
|
||||||
areSimultaneous: false,
|
areSimultaneous: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||||
import { IconSearch, IconSettings } from 'twenty-ui';
|
import { IconSearch, IconSettings, getOsControlSymbol } from 'twenty-ui';
|
||||||
|
|
||||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||||
import { CurrentWorkspaceMemberFavoritesFolders } from '@/favorites/components/CurrentWorkspaceMemberFavoritesFolders';
|
import { CurrentWorkspaceMemberFavoritesFolders } from '@/favorites/components/CurrentWorkspaceMemberFavoritesFolders';
|
||||||
@ -45,7 +45,7 @@ export const MainNavigationDrawerItems = () => {
|
|||||||
label="Search"
|
label="Search"
|
||||||
Icon={IconSearch}
|
Icon={IconSearch}
|
||||||
onClick={toggleCommandMenu}
|
onClick={toggleCommandMenu}
|
||||||
keyboard={['⌘', 'K']}
|
keyboard={[getOsControlSymbol(), 'K']}
|
||||||
/>
|
/>
|
||||||
<NavigationDrawerItem
|
<NavigationDrawerItem
|
||||||
label="Settings"
|
label="Settings"
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/compo
|
|||||||
import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo';
|
import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo';
|
||||||
import { DEFAULT_WORKSPACE_NAME } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceName';
|
import { DEFAULT_WORKSPACE_NAME } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceName';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
import { getOsControlSymbol } from '@ui/utilities/device/getOsControlSymbol';
|
||||||
import { IconSearch, IconSettings, useIsMobile } from 'twenty-ui';
|
import { IconSearch, IconSettings, useIsMobile } from 'twenty-ui';
|
||||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ export const SignInAppNavigationDrawerMock = ({
|
|||||||
label="Search"
|
label="Search"
|
||||||
Icon={IconSearch}
|
Icon={IconSearch}
|
||||||
onClick={() => {}}
|
onClick={() => {}}
|
||||||
keyboard={['⌘', 'K']}
|
keyboard={[getOsControlSymbol(), 'K']}
|
||||||
/>
|
/>
|
||||||
<NavigationDrawerItem
|
<NavigationDrawerItem
|
||||||
label="Settings"
|
label="Settings"
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
import { Button, IconButton, IconDotsVertical, useIsMobile } from 'twenty-ui';
|
import {
|
||||||
|
Button,
|
||||||
|
IconButton,
|
||||||
|
IconDotsVertical,
|
||||||
|
getOsControlSymbol,
|
||||||
|
useIsMobile,
|
||||||
|
} from 'twenty-ui';
|
||||||
|
|
||||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||||
@ -22,7 +28,7 @@ export const PageHeaderOpenCommandMenuButton = () => {
|
|||||||
size={isMobile ? 'medium' : 'small'}
|
size={isMobile ? 'medium' : 'small'}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
accent="default"
|
accent="default"
|
||||||
shortcut={isMobile ? '' : '⌘K'}
|
hotkeys={[getOsControlSymbol(), 'K']}
|
||||||
ariaLabel="Open command menu"
|
ariaLabel="Open command menu"
|
||||||
onClick={openCommandMenu}
|
onClick={openCommandMenu}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import {
|
|||||||
IconUser,
|
IconUser,
|
||||||
IconUserCircle,
|
IconUserCircle,
|
||||||
IconUsers,
|
IconUsers,
|
||||||
|
getOsControlSymbol,
|
||||||
} from 'twenty-ui';
|
} from 'twenty-ui';
|
||||||
|
|
||||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||||
@ -88,7 +89,7 @@ export const Default: Story = {
|
|||||||
<NavigationDrawerItem
|
<NavigationDrawerItem
|
||||||
label="Search"
|
label="Search"
|
||||||
Icon={IconSearch}
|
Icon={IconSearch}
|
||||||
keyboard={['⌘', 'K']}
|
keyboard={[`${getOsControlSymbol()}`, 'K']}
|
||||||
/>
|
/>
|
||||||
<NavigationDrawerItem
|
<NavigationDrawerItem
|
||||||
label="Settings"
|
label="Settings"
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { CatalogDecorator, CatalogStory, IconSearch } from 'twenty-ui';
|
|||||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||||
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
|
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
|
||||||
|
|
||||||
|
import { getOsControlSymbol } from '@ui/utilities/device/getOsControlSymbol';
|
||||||
import { NavigationDrawerItem } from '../NavigationDrawerItem';
|
import { NavigationDrawerItem } from '../NavigationDrawerItem';
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
@ -188,7 +189,7 @@ export const Catalog: CatalogStory<Story, typeof NavigationDrawerItem> = {
|
|||||||
: adornmentName === 'Count'
|
: adornmentName === 'Count'
|
||||||
? { count: 3 }
|
? { count: 3 }
|
||||||
: adornmentName === 'Keyboard Keys'
|
: adornmentName === 'Keyboard Keys'
|
||||||
? { keyboard: ['⌘', 'K'] }
|
? { keyboard: [getOsControlSymbol(), 'K'] }
|
||||||
: {},
|
: {},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -3,6 +3,8 @@ import { css, useTheme } from '@emotion/react';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Pill } from '@ui/components/Pill/Pill';
|
import { Pill } from '@ui/components/Pill/Pill';
|
||||||
import { IconComponent } from '@ui/display/icon/types/IconComponent';
|
import { IconComponent } from '@ui/display/icon/types/IconComponent';
|
||||||
|
import { useIsMobile } from '@ui/utilities';
|
||||||
|
import { getOsShortcutSeparator } from '@ui/utilities/device/getOsShortcutSeparator';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
@ -29,7 +31,7 @@ export type ButtonProps = {
|
|||||||
to?: string;
|
to?: string;
|
||||||
target?: string;
|
target?: string;
|
||||||
dataTestId?: string;
|
dataTestId?: string;
|
||||||
shortcut?: string;
|
hotkeys?: string[];
|
||||||
ariaLabel?: string;
|
ariaLabel?: string;
|
||||||
} & React.ComponentProps<'button'>;
|
} & React.ComponentProps<'button'>;
|
||||||
|
|
||||||
@ -417,11 +419,13 @@ export const Button = ({
|
|||||||
to,
|
to,
|
||||||
target,
|
target,
|
||||||
dataTestId,
|
dataTestId,
|
||||||
shortcut,
|
hotkeys,
|
||||||
ariaLabel,
|
ariaLabel,
|
||||||
}: ButtonProps) => {
|
}: ButtonProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledButton
|
<StyledButton
|
||||||
fullWidth={fullWidth}
|
fullWidth={fullWidth}
|
||||||
@ -443,11 +447,11 @@ export const Button = ({
|
|||||||
>
|
>
|
||||||
{Icon && <Icon size={theme.icon.size.sm} />}
|
{Icon && <Icon size={theme.icon.size.sm} />}
|
||||||
{title}
|
{title}
|
||||||
{shortcut && (
|
{hotkeys && !isMobile && (
|
||||||
<>
|
<>
|
||||||
<StyledSeparator buttonSize={size} accent={accent} />
|
<StyledSeparator buttonSize={size} accent={accent} />
|
||||||
<StyledShortcutLabel variant={variant} accent={accent}>
|
<StyledShortcutLabel variant={variant} accent={accent}>
|
||||||
{shortcut}
|
{hotkeys.join(getOsShortcutSeparator())}
|
||||||
</StyledShortcutLabel>
|
</StyledShortcutLabel>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ type Story = StoryObj<typeof Button>;
|
|||||||
|
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
argTypes: {
|
argTypes: {
|
||||||
shortcut: { control: false },
|
hotkeys: { control: false },
|
||||||
Icon: { control: false },
|
Icon: { control: false },
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
@ -44,7 +44,7 @@ export const Default: Story = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Catalog: CatalogStory<Story, typeof Button> = {
|
export const Catalog: CatalogStory<Story, typeof Button> = {
|
||||||
args: { title: 'Filter', Icon: IconSearch, shortcut: '' },
|
args: { title: 'Filter', Icon: IconSearch, hotkeys: ['⌘', 'O'] },
|
||||||
argTypes: {
|
argTypes: {
|
||||||
size: { control: false },
|
size: { control: false },
|
||||||
variant: { control: false },
|
variant: { control: false },
|
||||||
@ -127,7 +127,7 @@ export const SoonCatalog: CatalogStory<Story, typeof Button> = {
|
|||||||
soon: { control: false },
|
soon: { control: false },
|
||||||
position: { control: false },
|
position: { control: false },
|
||||||
className: { control: false },
|
className: { control: false },
|
||||||
shortcut: { control: false },
|
hotkeys: { control: false },
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
|
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
|
||||||
@ -199,7 +199,7 @@ export const PositionCatalog: CatalogStory<Story, typeof Button> = {
|
|||||||
fullWidth: { control: false },
|
fullWidth: { control: false },
|
||||||
soon: { control: false },
|
soon: { control: false },
|
||||||
position: { control: false },
|
position: { control: false },
|
||||||
shortcut: { control: false },
|
hotkeys: { control: false },
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
|
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
|
||||||
@ -266,7 +266,7 @@ export const PositionCatalog: CatalogStory<Story, typeof Button> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ShortcutCatalog: CatalogStory<Story, typeof Button> = {
|
export const ShortcutCatalog: CatalogStory<Story, typeof Button> = {
|
||||||
args: { title: 'Actions', shortcut: '⌘O' },
|
args: { title: 'Actions', hotkeys: ['⌘', 'O'] },
|
||||||
argTypes: {
|
argTypes: {
|
||||||
size: { control: false },
|
size: { control: false },
|
||||||
variant: { control: false },
|
variant: { control: false },
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
import { getOsControlSymbol } from '../getOsControlSymbol';
|
||||||
|
|
||||||
|
describe('getOsControlSymbol', () => {
|
||||||
|
let userAgentSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
userAgentSpy = jest.spyOn(window.navigator, 'userAgent', 'get');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
userAgentSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return Ctrl for Windows', () => {
|
||||||
|
userAgentSpy.mockReturnValue(
|
||||||
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0',
|
||||||
|
);
|
||||||
|
expect(getOsControlSymbol()).toBe('Ctrl');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return ⌘ for Mac', () => {
|
||||||
|
userAgentSpy.mockReturnValue(
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
||||||
|
);
|
||||||
|
expect(getOsControlSymbol()).toBe('⌘');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
import { getOsShortcutSeparator } from '../getOsShortcutSeparator';
|
||||||
|
|
||||||
|
describe('getOsShortcutSeparator', () => {
|
||||||
|
let userAgentSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
userAgentSpy = jest.spyOn(window.navigator, 'userAgent', 'get');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
userAgentSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return space for Windows', () => {
|
||||||
|
userAgentSpy.mockReturnValue(
|
||||||
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0',
|
||||||
|
);
|
||||||
|
expect(getOsShortcutSeparator()).toBe(' ');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return empty string for Mac', () => {
|
||||||
|
userAgentSpy.mockReturnValue(
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
||||||
|
);
|
||||||
|
expect(getOsShortcutSeparator()).toBe('');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { getUserDevice } from '@ui/utilities/device/getUserDevice';
|
||||||
|
|
||||||
|
export const getOsControlSymbol = () => {
|
||||||
|
const device = getUserDevice();
|
||||||
|
|
||||||
|
return device === 'mac' ? '⌘' : 'Ctrl';
|
||||||
|
};
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { getUserDevice } from '@ui/utilities/device/getUserDevice';
|
||||||
|
|
||||||
|
export const getOsShortcutSeparator = () => {
|
||||||
|
const device = getUserDevice();
|
||||||
|
return device === 'mac' ? '' : ' ';
|
||||||
|
};
|
||||||
26
packages/twenty-ui/src/utilities/device/getUserDevice.ts
Normal file
26
packages/twenty-ui/src/utilities/device/getUserDevice.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
export const getUserDevice = () => {
|
||||||
|
const userAgent = navigator.userAgent.toLowerCase();
|
||||||
|
|
||||||
|
if (userAgent.includes('mac os x') || userAgent.includes('macos')) {
|
||||||
|
return 'mac';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userAgent.includes('windows')) {
|
||||||
|
return 'windows';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userAgent.includes('linux')) {
|
||||||
|
return 'linux';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userAgent.includes('android')) return 'android';
|
||||||
|
|
||||||
|
if (
|
||||||
|
userAgent.includes('ios') ||
|
||||||
|
userAgent.includes('iphone') ||
|
||||||
|
userAgent.includes('ipad')
|
||||||
|
)
|
||||||
|
return 'ios';
|
||||||
|
|
||||||
|
return 'unknown';
|
||||||
|
};
|
||||||
@ -5,6 +5,9 @@ export * from './animation/components/AnimatedFadeOut';
|
|||||||
export * from './animation/components/AnimatedTextWord';
|
export * from './animation/components/AnimatedTextWord';
|
||||||
export * from './animation/components/AnimatedTranslation';
|
export * from './animation/components/AnimatedTranslation';
|
||||||
export * from './color/utils/stringToHslColor';
|
export * from './color/utils/stringToHslColor';
|
||||||
|
export * from './device/getOsControlSymbol';
|
||||||
|
export * from './device/getOsShortcutSeparator';
|
||||||
|
export * from './device/getUserDevice';
|
||||||
export * from './dimensions/components/ComputeNodeDimensions';
|
export * from './dimensions/components/ComputeNodeDimensions';
|
||||||
export * from './isDefined';
|
export * from './isDefined';
|
||||||
export * from './responsive/hooks/useIsMobile';
|
export * from './responsive/hooks/useIsMobile';
|
||||||
|
|||||||
Reference in New Issue
Block a user